thermferm/server.c

Mon, 19 May 2014 17:49:04 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Mon, 19 May 2014 17:49:04 +0200
changeset 44
f37d73940699
parent 43
24e731bb2e08
child 45
053c4657105f
permissions
-rw-r--r--

Server communication works

/*****************************************************************************
 * 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"


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


/*
 * 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 (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                *hostname, buf[SS_BUFSIZE], obuf[SS_BUFSIZE];
    int                 i, rc, rlen, timer;
    socklen_t           fromlen;
    struct pollfd       pfd[1];

    /*
     * 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);

			/*
			 * Process commands from the client
			 */
			if (strncmp(buf, "ack", 3) == 0) {
			    srv_send((char *)"ack");
			} else if (strncmp(buf, "lcd", 3) == 0) {
			    for (i = 0; i < 20; i++) {
				obuf[i]    = lcdbuf[lcdHandle][i][0];
				obuf[i+21] = lcdbuf[lcdHandle][i][1];
				obuf[i+42] = lcdbuf[lcdHandle][i][2];
				obuf[i+63] = lcdbuf[lcdHandle][i][3];
			    }
			    obuf[20] = obuf[41] = obuf[62] = ',';
			    obuf[83] = '\0';
			    srv_send(obuf);
			} else if (strncmp(buf, "getMode", 7) == 0) {
			    srv_send("b");
			} else if (strncmp(buf, "getControlSettings", 18) == 0) {
			    srv_send("mode='b', beerSet=20.0, fridgeSet=20.0, heatEstimator=0.2, coolEstimator=5");
			} 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");

    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;
	}

    }
}


mercurial