Tue, 20 May 2014 22:40:16 +0200
More client/server communications
/***************************************************************************** * Copyright (C) 2008-2014 * * Michiel Broek <mbroek at mbse dot eu> * * This file is part of the mbsePi-apps * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * mbsePi-apps is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with EC-65K; see the file COPYING. If not, write to the Free * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *****************************************************************************/ #include "../lib/mbselib.h" #include "server.h" #include "thermferm.h" extern bool my_shutdown; extern bool debug; extern int lcdHandle; extern unsigned char lcdbuf[MAX_LCDS][20][4]; extern sys_config Config; extern int clients; int s; /* connected socket */ int ls; /* listen socket */ struct sockaddr_in myaddr_in; /* for local socket address */ struct sockaddr_in peeraddr_in; /* for peer socket address */ struct hostent *hp; #define SS_BUFSIZE 1024 #define SS_TIMEOUT 300 void defaultControlsettings(void) { beer->cs_mode = 'o'; /* o = Off, f = fridge, b = beer, p = profile-run */ beer->cs_beerSet = 20.0; beer->cs_fridgeSet = 20.0; beer->cs_heatEstimator = 0.2; beer->cs_coolEstimator = 5; } void defaultControlConstants(void) { beer->cc_tempFormat = 'C'; beer->cc_tempSetMin = 1.0; beer->cc_tempSetMax = 30.0; beer->cc_idleRangeH = 1.000; beer->cc_idleRangeL = -1.000; } void defaultControlVariables(void) { beer->cv_beerDiff = 0.0; } /* * Send message to client */ int srv_send(const char *format, ...) { char out[SS_BUFSIZE]; va_list va_ptr; if (s == -1) return -1; va_start(va_ptr, format); vsnprintf(out, SS_BUFSIZE-1, format, va_ptr); va_end(va_ptr); syslog(LOG_NOTICE, "send: \"%s\"", out); if (debug) fprintf(stdout, "send: \"%s\"\n", out); if (send(s, out, strlen(out), 0) != strlen(out)) { syslog(LOG_NOTICE, "srv_send failed"); return -1; } if (send(s, (char *)"\r\n", 2, 0) != 2) { syslog(LOG_NOTICE, "srv_send failed"); return -1; } return 0; } void cmd_die(int onsig) { syslog(LOG_NOTICE, "Server process die on signal %d", onsig); close(s); exit(0); } void cmd_server(void) { char *inp, *hostname, buf[SS_BUFSIZE], obuf[SS_BUFSIZE]; int i, rc, rlen, timer; socklen_t fromlen; struct pollfd pfd[1]; float newtemp; /* * Close listen socket */ close(ls); /* * Install private signal handler. */ for (i = 0; i < NSIG; i++) { if ((i == SIGHUP) || (i == SIGPIPE) || (i == SIGBUS) || (i == SIGILL) || (i == SIGSEGV)) signal(i, (void (*))cmd_die); else signal(i, SIG_IGN); } hp = gethostbyaddr ((char *) &peeraddr_in.sin_addr, sizeof(struct in_addr), peeraddr_in.sin_family); if (hp == NULL) { hostname = inet_ntoa(peeraddr_in.sin_addr); } else { hostname = hp->h_name; } clients++; syslog(LOG_NOTICE, "Start new client connection (%d) from %s port %u", clients, hostname, ntohs(peeraddr_in.sin_port)); if (debug) fprintf(stdout, "Start new client connection (%d) from %s port %u\n", clients, hostname, ntohs(peeraddr_in.sin_port)); timer = SS_TIMEOUT * 4; /* * Receive loop */ for (;;) { /* * Poll socket until a define timeout of 0,25 second. */ pfd[0].fd = s; pfd[0].events = POLLIN; pfd[0].revents = 0; rc = poll(pfd, 1, 250); if (rc == -1) { /* * Poll can be interrupted by a finished child so that's not a real error. */ if (errno != EINTR) { syslog(LOG_NOTICE, "poll() rc=%d sock=%d events=%04x", rc, s, pfd[0].revents); } } else if (rc) { if (pfd[0].revents & POLLIN) { memset((char *)&buf, 0, SS_BUFSIZE); fromlen = sizeof(peeraddr_in); rlen = recvfrom(s, buf, sizeof(buf) -1, 0, (struct sockaddr *)&peeraddr_in, &fromlen); if (rlen == -1) { syslog(LOG_NOTICE, "recvfrom(): %s", strerror(errno)); } else { for (i = 0; i < strlen(buf); i++) { if (buf[i] == '\n') buf[i] = '\0'; if (buf[i] == '\r') buf[i] = '\0'; } timer = SS_TIMEOUT * 4; if (strlen(buf)) { syslog(LOG_NOTICE, "recv: \"%s\"", buf); if (debug) fprintf(stdout, "recv: \"%s\"\n", buf); /* * Process commands from the client */ if (strncmp(buf, "ack", 3) == 0) { srv_send((char *)"ack"); } else if (strncmp(buf, "lcd", 3) == 0) { sprintf(obuf, "[\" \", \" \", \" \", \" \"]"); for (i = 0; i < 20; i++) { obuf[i+2] = lcdbuf[lcdHandle][i][0]; obuf[i+26] = lcdbuf[lcdHandle][i][1]; obuf[i+50] = lcdbuf[lcdHandle][i][2]; obuf[i+74] = lcdbuf[lcdHandle][i][3]; } srv_send(obuf); } else if (strncmp(buf, "getMode", 7) == 0) { srv_send("%c", beer->cs_mode); } else if (strncmp(buf, "getFridge", 9) == 0) { srv_send("%.1f", beer->cs_fridgeSet); } else if (strncmp(buf, "getBeer", 7) == 0) { srv_send("%.1f", beer->cs_beerSet); } else if (strncmp(buf, "getControlConstants", 19) == 0) { srv_send("{ \"tempFormat\":\"%c\", \"tempSetMin\":%.1f, \"tempSetMax\":%.1f, \"idleRangeH\":%.3f, \"idleRangeL\":%.3f }", beer->cc_tempFormat, beer->cc_tempSetMin, beer->cc_tempSetMax, beer->cc_idleRangeH, beer->cc_idleRangeL ); } else if (strncmp(buf, "getControlSettings", 18) == 0) { srv_send("{ \"mode\":\"%c\", \"beerSet\":%.1f, \"fridgeSet\":%.1f, \"heatEstimator\":%.1f, \"coolEstimator\":%.1f }", beer->cs_mode, beer->cs_beerSet, beer->cs_fridgeSet, beer->cs_heatEstimator, beer->cs_coolEstimator); } else if (strncmp(buf, "getControlVariables", 19) == 0) { srv_send("{ \"beerDiff\":%.2f }", beer->cv_beerDiff); } else if (strncmp(buf, "setBeer=", 8) == 0) { inp = xstrcpy(buf+8); rc = sscanf(inp, "%f", &newtemp); if (debug) fprintf(stdout, "new temp from %s, %.1f, rc=%d\n", inp, newtemp, rc); if (rc == 1) { srv_send("ack"); beer->cs_mode = 'b'; beer->cs_beerSet = newtemp; } else { srv_send("err"); } } else if (strncmp(buf, "setFridge=", 10) == 0) { inp = xstrcpy(buf+10); rc = sscanf(inp, "%f", &newtemp); if (debug) fprintf(stdout, "new temp from %s, %.1f, rc=%d\n", inp, newtemp, rc); if (rc == 1) { srv_send("ack"); beer->cs_mode = 'f'; beer->cs_fridgeSet = newtemp; } else { srv_send("err"); } } else { if (debug) fprintf(stdout, "unknown command \"%s\"\n", buf); srv_send((char *)"ERR"); } break; } } } else { syslog(LOG_NOTICE, "poll other event"); } } else { /* * Poll timeout, do some housekeeping */ if (timer) { timer--; } else { /* Inactivity timeout */ break; } if (my_shutdown) { break; } } } if (clients) clients--; syslog(LOG_NOTICE, "End connection from %s port %u", hostname, ntohs(peeraddr_in.sin_port)); if (debug) fprintf(stdout, "End connection from %s port %u\n", hostname, ntohs(peeraddr_in.sin_port)); close(s); } PI_THREAD (my_server_loop) { socklen_t addrlen; int optval = 1; syslog(LOG_NOTICE, "Thread my_server_loop started"); if (debug) fprintf(stdout, "Thread my_server_loop started\n"); defaultControlsettings(); defaultControlConstants(); defaultControlVariables(); memset((char *)&myaddr_in, 0, sizeof(struct sockaddr_in)); memset((char *)&peeraddr_in, 0, sizeof(struct sockaddr_in)); myaddr_in.sin_family = AF_INET; myaddr_in.sin_addr.s_addr = INADDR_ANY; myaddr_in.sin_port = htons(Config.my_port); ls = socket(AF_INET, SOCK_STREAM, 0); if (ls == -1) { syslog(LOG_NOTICE, "Can't create listen socket: %s", strerror(errno)); fprintf(stderr, "Can't create listen socket: %s\n", strerror(errno)); return 0; } if (setsockopt(ls, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1) { syslog(LOG_NOTICE, "Can't setsockopt SO_KEEPALIVE socket: %s", strerror(errno)); close(ls); return 0; } if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) { syslog(LOG_NOTICE, "Can't setsockopt SO_REUSEADDR socket: %s", strerror(errno)); close(ls); return NULL; } if (bind(ls, (struct sockaddr *)&myaddr_in, sizeof(struct sockaddr_in)) == -1) { syslog(LOG_NOTICE, "Can't bind to listen socket: %s", strerror(errno)); close(ls); return NULL; } if (listen(ls, 5) == -1) { syslog(LOG_NOTICE, "Can't listen on listen socket: %s", strerror(errno)); close(ls); return NULL; } syslog(LOG_NOTICE, "listen socket created %d", ls); if (debug) fprintf(stdout, "listen socket created %d\n", ls); /* * Loop forever until the external shutdown variable is set. */ for (;;) { addrlen = sizeof(struct sockaddr_in); /* * This call will block until a new connection * arrives. Then it will return the address of * the connecting peer, and a new socket * descriptor, s, for that connection. */ s = accept(ls, (struct sockaddr *)&peeraddr_in, &addrlen); if (s == -1) { syslog(LOG_NOTICE, "my_server_loop accept failed %s", strerror(errno)); if (debug) fprintf(stdout, "my_server_loop accept failed %s\n", strerror(errno)); return 0; } switch (fork()) { case -1: /* * Can't fork, just continue. */ return 0; case 0: /* * Child process, the commandline server. */ cmd_server(); return 0; default: /* * Daemon process comes here. The daemon * needs to remember to close the new * accept socket after forking the child. */ close(s); } if (my_shutdown) { syslog(LOG_NOTICE, "Thread my_server_loop stopped"); if (debug) fprintf(stdout, "Thread my_server_loop stopped\n"); return 0; } } }