thermferm/server.c

Sat, 16 May 2015 17:39:30 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sat, 16 May 2015 17:39:30 +0200
changeset 362
c92651a54969
parent 358
8b7ef338bf49
child 364
2f9bbbcd2407
permissions
-rw-r--r--

Made the client-server protocol more robust. When a change to a unit is made using the web interface, the main process is stopped during the update. Splitted the PID in two PID's, one for heating and one for cooling. Adjusted the web edit scrreen for this, but there are still rough edges. Replaced the PID code, maybe this one works better for our purpose. The simulator air temperature changes on the simulator heater and cooler, but it is not realistic at all. This is a development version, do not use in production. The version is 0.3.0

/*****************************************************************************
 * Copyright (C) 2008-2015
 *   
 * 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 ThermFerm; see the file COPYING.  If not, write to the Free
 * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *****************************************************************************/

#include "rdconfig.h"
#include "thermferm.h"
#include "logger.h"
#include "devices.h"
#include "server.h"
#include "lcd-buffer.h"
#include "xutil.h"


extern int		my_shutdown;
extern int		debug;
extern int		run_pause;
extern int		run_hold;
extern sys_config       Config;
extern const char	UNITMODE[5][8];
extern const char	TEMPSTATE[3][8];
extern const char	DEVTYPE[8][6];
extern const char	DEVPRESENT[4][6];
extern const char	DEVDIR[7][11];
extern const char	PROFSTATE[5][6];


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

#define	MAX_INTERVALS	6
const int	GRAPH_INTERVAL[MAX_INTERVALS] = { 0, 1, 5, 15, 30, 60, };
const int	GRAPH_DATALINES[MAX_INTERVALS] = { 0, 800, 3200, 12000, 24000, 48000, };
const char	MONTH[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };


typedef struct _ls_list {
    struct _ls_list	*next;		/* Next record pointer			*/
    char		d_name[256];	/* File name				*/
    mode_t		mode;		/* File mode				*/
    off_t		size;		/* File size				*/
    time_t		mtime;		/* File modification time		*/
} ls_list;



void tidy_lslist(ls_list **);
void fill_list(ls_list **, char *, mode_t, off_t, time_t);
int  comp(ls_list **,ls_list **);
void sort_list(ls_list **);



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

    if (debug) {
	syslog(LOG_NOTICE, "send: \"%s\"", out);
	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;
}



/*
 * Argument is a buffer of size SS_BUFSIZE.
 * Return -1 if error, else the number of received
 * character. \n is line end, ignore \r.
 */
int srv_recv(char *buffer)
{
    int			bytesloaded = 0;
    ssize_t		ret;
    unsigned char	buf;
    socklen_t		fromlen;

    memset(buffer, 0, SS_BUFSIZE);

    while(1) {
	/*
	 * read a single byte
	 */
	fromlen = sizeof(peeraddr_in);
	ret = recvfrom(s, &buf, 1, 0, (struct sockaddr *)&peeraddr_in, &fromlen);
	if (ret < 1) {
	    syslog(LOG_NOTICE, "recvfrom(): %s", strerror(errno));
	    srv_send((char *)"518 recfrom(): %s", strerror(errno));
	    return -1;	/* error */
	}

	if (buf == '\n')
	    break;

	if (buf != '\r') {
	    buffer[bytesloaded] = buf; 
	    bytesloaded++;
	}
    } 

    if (debug) {
	syslog(LOG_NOTICE, "recv: %d `%s'", bytesloaded, buffer);
	fprintf(stdout, "recv: %d `%s'\n", bytesloaded, buffer);
    }
    return bytesloaded;
}



/*
 * Update the device inuse counter.
 */
void device_count(int plus, char *uuid)
{
    devices_list	*device;

    for (device = Config.devices; device; device = device->next) {
	if (strcmp(device->uuid, uuid) == 0) {
#ifdef HAVE_WIRINGPI_H
	    piLock(LOCK_DEVICES);
#endif
	    if (plus == TRUE) {
		device->inuse++;
	    } else {
		if (device->inuse)
		    device->inuse--;
	    }
#ifdef HAVE_WIRINGPI_H
	    piUnlock(LOCK_DEVICES);
#endif
	}
    }
}



int delete_Profile(char *uuid)
{
    profiles_list	*current = Config.profiles;
    profiles_list	*previous = NULL;
    prof_step		*step, *olds;

    while (current) {
	if (strcmp(current->uuid, uuid) == 0) {
	    if (previous == NULL) {
		Config.profiles = current->next;
		free(current->uuid);
		current->uuid = NULL;
		free(current->name);
		current->name = NULL;
		if (current->steps) {
		    for (step = current->steps; step; step = olds) {
			olds = step->next;
			free(step);
		    }
		    current->steps = NULL;
		}
		free(current);
		return 1;
	    } else {
		free(current->uuid);
		current->uuid = NULL;
		free(current->name);
		current->name = NULL;
		if (current->steps) {
		    for (step = current->steps; step; step = olds) {
			olds = step->next;
			free(step);
		    }
		    current->steps = NULL;
		}
		previous->next = current->next;
		free(current);
		current = previous->next;
		return 1;
	    }
	} else {
	    previous = current;
	    current = current->next;
	}
    }

    return 0;
}



void tidy_lslist(ls_list **lap)
{
    ls_list     *tmp, *old;

    for (tmp = *lap; tmp; tmp = old) {
	old = tmp->next;
	free(tmp);
    }
    *lap = NULL;
}



void fill_list(ls_list **lap, char *name, mode_t mode, off_t size, time_t mtime)
{
    ls_list     **tmp;

    for (tmp = lap; *tmp; tmp = &((*tmp)->next));

    *tmp = (ls_list *)malloc(sizeof(ls_list));
    (*tmp)->next = NULL;
    strncpy((*tmp)->d_name, name, 256);
    (*tmp)->mode = mode;
    (*tmp)->size = size;
    (*tmp)->mtime = mtime;
    tmp = &((*tmp)->next);
}



void sort_list(ls_list **lap)
{
    ls_list	*ta, **vector;
    size_t	n = 0, i;

    if (*lap == NULL)
	return;

    for (ta = *lap; ta; ta = ta->next) 
	n++;
    vector = (ls_list **)malloc(n * sizeof(ls_list *));
    i = 0;

    for (ta = *lap; ta; ta = ta->next) {
	vector[i++] = ta;
    }
    qsort(vector, n, sizeof(ls_list *), (int(*)(const void*, const void*))comp);

    (*lap) = vector[0];
	i = 1;

    for (ta = *lap; ta; ta = ta->next) {
	if (i < n) 
	    ta->next = vector[i++];
	else 
	    ta->next = NULL;
    }

    free(vector);
    return;
}



int comp(ls_list **lsp1, ls_list **lsp2)
{
    return strcmp((*lsp1)->d_name, (*lsp2)->d_name);
}



/*
 * ARCHIVE DIR
 * ARCHIVE GET filename
 * ARCHIVE LOG filename
 * ARCHIVE HELP
 */
int cmd_archive(char *buf)
{
    char		*opt, *param, *name = NULL, *filename = NULL, mbits[11], tstr[24];
    DIR			*dd;
    FILE		*fp;
    struct dirent	entry, *result;
    ls_list		*lsx = NULL, *tmp;
    struct stat		sbuf;
    struct tm		*tbuf;
    time_t		ftime;

    opt = strtok(buf, " \0");
    opt = strtok(NULL, " \0");

    if (opt == NULL) {
	srv_send((char *)"501 Subcommand missing");
	return 1;
    }
    param = strtok(NULL, "\0");

    if (strcmp(opt, (char *)"HELP") == 0) {
	srv_send((char *)"100 Help text follows:");
	srv_send((char *)"Recognized commands:");
	srv_send((char *)"ARCHIVE DIR                   Archived logfiles directory");
	srv_send((char *)"ARCHIVE GET filename          Archived logfile download");
	srv_send((char *)"ARCHIVE LOG filename          Archived logfile data in graphsteps");
	srv_send((char *)".");
	return 0;
    }

    if (strcmp(opt, (char *)"DIR") == 0) {

	if (getenv((char *)"USER") == NULL) {
	    name = xstrcpy((char *)"/root");
	} else {
	    name = xstrcpy(getenv((char *)"HOME"));
	}
	name = xstrcat(name, (char *)"/.thermferm/log/");

	if ((dd = opendir(name))) {
	    for (;;) {
		if ((readdir_r(dd, &entry, &result)) != 0) {
		    syslog(LOG_NOTICE, "readdir_r: error=%d", errno);
		    break;
		}
		if (result == NULL)	/* End of directory	*/
		    break;
		if (result->d_name[0] != '.') {
		    filename = xstrcpy(name);
		    filename = xstrcat(filename, result->d_name);
		    if ((stat(filename, &sbuf)) == 0) {
		        fill_list(&lsx, result->d_name, sbuf.st_mode, sbuf.st_size, sbuf.st_mtime);
		    }
		    free(filename);
		    filename = NULL;
		}
	    }
	    closedir(dd);
	} else {
	    syslog(LOG_NOTICE, "opendir: \"%s\" error=%d", name, errno);    
	}

	sort_list(&lsx);

	srv_send((char *)"212 Archive directory follows:");
	for (tmp = lsx; tmp; tmp = tmp->next) {
	    sprintf(mbits, "----------");
	    if (tmp->mode & S_IRUSR)
		mbits[1] = 'r';
	    if (tmp->mode & S_IWUSR)
		mbits[2] = 'w';
	    if (tmp->mode & S_IXUSR)
		mbits[3] = 'x';
	    if (tmp->mode & S_IRGRP)
		mbits[4] = 'r';
	    if (tmp->mode & S_IWGRP)
		mbits[5] = 'w';
	    if (tmp->mode & S_IXGRP)
		mbits[6] = 'x';
	    if (tmp->mode & S_IROTH)
		mbits[7] = 'r';
	    if (tmp->mode & S_IWOTH)
		mbits[8] = 'w';
	    if (tmp->mode & S_IXOTH)
		mbits[9] = 'x';
	    ftime = tmp->mtime;
	    tbuf = localtime(&ftime);
	    sprintf(tstr, "%02d %s %04d %02d:%02d", tbuf->tm_mday, MONTH[tbuf->tm_mon], tbuf->tm_year+1900, tbuf->tm_hour, tbuf->tm_min);
	    srv_send((char *)"%s,%s,%d,%s", tmp->d_name, mbits, tmp->size, tstr);
	}
	srv_send((char *)".");
	tidy_lslist(&lsx);

	free(name);
	name = NULL;
	return 1;
    }

    if (param == NULL) {
	srv_send((char *)"502 Parameter missing");
	return 1;
    }

    if (strcmp(opt, (char *)"GET") == 0) {
	if (getenv((char *)"USER") == NULL) {
	    name = xstrcpy((char *)"/root");
	} else {
	    name = xstrcpy(getenv((char *)"HOME"));
	}
	name = xstrcat(name, (char *)"/.thermferm/log/");
	name = xstrcat(name, param);

	if ((fp = fopen(name, "r"))) {
	    char	buffer[256];

	    srv_send((char *)"212 Archive file follows:");
	    while (fgets(buffer, sizeof(buffer)-1, fp) != NULL) {
		int	i;

		for (i = 0; i < strlen(buffer); i++) {
		    if (buffer[i] == '\n')
			buffer[i] = '\0';
		    if (buffer[i] == '\r')
			buffer[i] = '\0';
		}
		srv_send(buffer);
	    }
	    srv_send((char *)".");
	    fclose(fp);
	} else {
	    srv_send((char *)"440 No such file");
	}

	free(name);
	name = NULL;
	return 1;
    }

    if (strcmp(opt, (char *)"LOG") == 0) {
	if (getenv((char *)"USER") == NULL) {
	    name = xstrcpy((char *)"/root");
	} else {
	    name = xstrcpy(getenv((char *)"HOME"));
	}
	name = xstrcat(name, (char *)"/.thermferm/log/");
	name = xstrcat(name, param);

	if ((fp = fopen(name, "r"))) {
	    char        buffer[256], outbuf[256], q[5];
	    char	*date_n, *mode_n, *air_n, *beer_n, *target_n, *heater_n, *cooler_n, *room_n;
	    char	*heater_u, *cooler_u;
	    int		lines = 0, heater_l = 0, cooler_l = 0, h = 0, c = 0, heat_used = 0, cool_used = 0, graphstep = 0;
	    float	room_t = 0.0;

	    srv_send((char *)"212 Logfile list follows:");
	    while (fgets(buffer, sizeof(buffer)-1, fp) != NULL) {
		lines++;
	    }
	    fseek(fp, 0L, SEEK_SET);
	    /*
	     * We have counted the lines in the logfile including the header lines.
	     * The header lines should be ignored but there are so few of them, we
	     * just include them in the total.
	     * Now find a reasonable interval of lines to sent to the client.
	     */
	    for (graphstep = 1; graphstep <= MAX_INTERVALS; graphstep++) {
		if (lines < GRAPH_DATALINES[graphstep]) {
		    break;
		}
	    }
	    syslog(LOG_NOTICE, "ARCHIVE LOG %s: lines=%d, interval=%d, graphstep=%d", param, lines, GRAPH_INTERVAL[graphstep], graphstep);

	    while (fgets(buffer, sizeof(buffer)-1, fp) != NULL) {
		/*
		 * 2014-11-15 18:39,BEER,20.312,19.750,20.0,0,NA,NA,NA,78105,NA,NA,18.000
		 *       |           |     |      |     |   | |  |  |    |   |  |    |
		 *  date_n           |     |      |     |   | |  |  |    |   |  |    |
		 *  mode_n ----------+     |      |     |   | |  |  |    |   |  |    |
		 *  air_n -----------------+      |     |   | |  |  |    |   |  |    |
		 *  beer_n -----------------------+     |   | |  |  |    |   |  |    |
		 *  target_n ---------------------------+   | |  |  |    |   |  |    |
		 *  heater_n -------------------------------+ |  |  |    |   |  |    |
		 *  cooler_n ---------------------------------+  |  |    |   |  |    |
		 *  not used ------------------------------------+  |    |   |  |    |
		 *  not used ---------------------------------------+    |   |  |    |
		 *  heater_u --------------------------------------------+   |  |    |
		 *  cooler_u ------------------------------------------------+  |    |
		 *  not used ---------------------------------------------------+    |
		 *  room_n ----------------------------------------------------------+
		 */
		q[0] = buffer[11];
		q[1] = buffer[12];
		q[2] = buffer[14];
		q[3] = buffer[15];
		buffer[strlen(buffer) -1] = '\0';
		date_n = strtok(buffer, ",\0"); /* timestamp            */
		mode_n = strtok(NULL, ",\0");   /* unit mode            */
		air_n = strtok(NULL, ",\0");    /* air temp             */
		beer_n = strtok(NULL, ",\0");   /* beer temp            */
		target_n = strtok(NULL, ",\0"); /* target temp          */
		heater_n = strtok(NULL, ",\0"); /* current heater state */
		cooler_n = strtok(NULL, ",\0"); /* current cooler state */
		heater_u = strtok(NULL, ",\0"); /* current fan state    */
		heater_u = strtok(NULL, ",\0"); /* current door state   */
		heater_u = strtok(NULL, ",\0"); /* heater use counter   */
		cooler_u = strtok(NULL, ",\0"); /* cooler use counter   */
		room_n = strtok(NULL, ",\0");	/* fan use counter	*/
		room_n = strtok(NULL, ",\0");	/* room temperature	*/

		if (strncmp(mode_n, (char *)"Mode", 4)) {
		    /*
		     * Output a line at the right intervals
		     */
		    if (((graphstep == 1)) ||
			((graphstep == 2) && (q[3] == '0' || q[3] == '5')) ||
			((graphstep == 3) && ((q[2] == '0' && q[3] == '0') || (q[2] == '1' && q[3] == '5') || (q[2] == '3' && q[3] == '0') || (q[2] == '4' && q[3] == '5'))) ||
			((graphstep == 4) && ((q[2] == '0' && q[3] == '0') || (q[2] == '3' && q[3] == '0'))) ||
			((graphstep == 5) && (q[2] == '0' && q[3] == '0')) ) {
			heat_used = cool_used = 0;
			if (heater_u && strcmp(heater_u, "NA") && (sscanf(heater_u, "%d", &h) == 1)) {
			    if (h && heater_l) {
				heat_used = ((h - heater_l) * 100) / (GRAPH_INTERVAL[graphstep] * 60);
			    }
			}
			if (cooler_u && strcmp(cooler_u, "NA") && (sscanf(cooler_u, "%d", &c) == 1)) {
			    if (c && cooler_l) {
				cool_used = ((c - cooler_l) * 100) / (GRAPH_INTERVAL[graphstep] * 60);
			    }
			}
			if (room_n)
			    sscanf(room_n, "%f", &room_t);
			snprintf(outbuf, 255, "%s,%s,%s,%s,%s,%s,%s,%d,%d,%.1f", 
				date_n, mode_n, air_n, beer_n, target_n, heater_n, cooler_n, heat_used, cool_used, room_t);
			srv_send(outbuf);
			if (heater_u && h && strcmp(heater_u, "NA"))
			    heater_l = h;
			if (cooler_u && c & strcmp(cooler_u, "NA"))
			    cooler_l = c;
		    }
		}
	    }

	    srv_send((char *)".");
	    fclose(fp);
	} else {
	    srv_send((char *)"440 No such file");
	}

	free(name);
	name = NULL;
	return 1;
    }

    return 0;
}



int delete_Device(char *uuid)
{
    devices_list	*current = Config.devices;
    devices_list	*previous = NULL;

    while (current) {
	if (strcmp(current->uuid, uuid) == 0) {
	    if (previous == NULL) {
		Config.devices = current->next;
		free(current->uuid);
		current->uuid = NULL;
		free(current->address);
		current->address = NULL;
		free(current->description);
		current->description = NULL;
		free(current->comment);
		current->comment = NULL;
		free(current);
		return 1;
	    } else {
		free(current->uuid);
		current->uuid = NULL;
		free(current->address);
		current->address = NULL;
		free(current->description);
		current->description = NULL;
		free(current->comment);
		current->comment = NULL;
		previous->next = current->next;
		free(current);
		current = previous->next;
		return 1;
	    }
	} else {
	    previous = current;
	    current = current->next;
	}
    }
    return 0;
}



/*
 * DEVICE ADD type
 * DEVICE DEL uuid
 * DEVICE LIST
 * DEVICE GET uuid
 * DEVICE PUT uuid
 */
int cmd_device(char *buf)
{
    char		*opt, *param, *kwd, *val, ibuf[SS_BUFSIZE];
    devices_list	*device, *tmpd;
    int			i, rc, rlen, ival;
    uuid_t		uu;

    opt = strtok(buf, " \0");
    opt = strtok(NULL, " \0");

    if (opt == NULL) {
	srv_send((char *)"501 Subcommand missing");
	return 1;
    }
    param = strtok(NULL, "\0");

    if (strcmp(opt, (char *)"LIST") == 0) {
	srv_send((char *)"212 Devices list follows:");
	for (device = Config.devices; device; device = device->next) {
	    srv_send((char *)"%s,%s,%d,%d,%s,%s,%d", device->uuid, device->address, device->subdevice,
			    device->inuse, device->comment, DEVDIR[device->direction], device->value + device->offset);
	}
	srv_send((char *)".");
	return 1;
    }

    if (param == NULL) {
	srv_send((char *)"502 Parameter missing");
	return 1;
    }

    if (strcmp(opt, (char *)"ADD") == 0) {
	if ((strcmp(param, (char *)"RC433") == 0) || (strcmp(param, (char *)"DHT") == 0) ||
	    (strcmp(param, (char *)"I2C") == 0) || (strcmp(param, (char *)"SPI") == 0)) {

	    device = (devices_list *)malloc(sizeof(devices_list));
	    device->next = NULL;
	    device->version = 1;
	    device->uuid = malloc(37);
	    uuid_generate(uu);
	    uuid_unparse(uu, device->uuid);
	    for (i = 0; i < 8; i++) {
		if (strcmp(param, DEVTYPE[i]) == 0) {
		    device->type = i;
		    break;
		}
	    }
	    device->direction = DEVDIR_UNDEF;
	    device->value = device->offset = device->subdevice = device->inuse = 0;
	    device->present = DEVPRESENT_UNDEF;
	    device->address = xstrcpy((char *)"Enter address here");
	    device->gpiopin = -1;
	    device->description = xstrcpy((char *)"Describe me here");
	    device->comment = xstrcpy((char *)"Comment here");

#ifdef HAVE_WIRINGPI_H
	    piLock(LOCK_DEVICES);
#endif
	    if (Config.devices == NULL) {
		Config.devices = device;
	    } else {
	        for (tmpd = Config.devices; tmpd; tmpd = tmpd->next) {
	    	    if (tmpd->next == NULL) {
	    		tmpd->next = device;
    			break;
		    }
		}
	    }
#ifdef HAVE_WIRINGPI_H
	    piUnlock(LOCK_DEVICES);
#endif
	    syslog(LOG_NOTICE, "Device %s added", device->uuid);
	    srv_send((char *)"211 Device %s added", device->uuid);
	    return 0;

	} else {
	    srv_send((char *)"503 Parameter error");
	    return 1;
	}
    }

    if (strcmp(opt, (char *)"DEL") == 0) {
	
#ifdef HAVE_WIRINGPI_H
	piLock(LOCK_DEVICES);
#endif
	rc = delete_Device(param);
#ifdef HAVE_WIRINGPI_H
	piUnlock(LOCK_DEVICES);
#endif
	if (rc) {
	    syslog(LOG_NOTICE, "Device %s deleted", param);
	    srv_send((char *)"211 Device %s deleted", param);
	    return 0;
	} else {
	    srv_send((char *)"440 No such device");
	    return 1;
	}
    }

    if (strcmp(opt, (char *)"GET") == 0) {
	for (device = Config.devices; device; device = device->next) {
	    if (strcmp(device->uuid, param) == 0) {
#ifdef HAVE_WIRINGPI_H
		piLock(LOCK_DEVICES);
#endif
		int	my_value = device->value;
		int	my_timestamp = (int)device->timestamp;
#ifdef HAVE_WIRINGPI_H
		piUnlock(LOCK_DEVICES);
#endif
		srv_send((char *)"213 Device record follows:");
		srv_send((char *)"TYPE,%s", DEVTYPE[device->type]);
		srv_send((char *)"ADDRESS,%s", device->address);
		srv_send((char *)"DIRECTION,%s", DEVDIR[device->direction]);
		srv_send((char *)"VALUE,%d", my_value);
		srv_send((char *)"OFFSET,%d", device->offset);
		srv_send((char *)"PRESENT,%s", DEVPRESENT[device->present]);
		srv_send((char *)"SUBDEVICE,%d", device->subdevice);
		srv_send((char *)"GPIOPIN,%d", device->gpiopin);
		srv_send((char *)"DESCRIPTION,%s", device->description);
		srv_send((char *)"INUSE,%d", device->inuse);
		srv_send((char *)"COMMENT,%s", device->comment);
		srv_send((char *)"TIMESTAMP,%d", my_timestamp);
		srv_send((char *)".");
		return 1;
	    }
	}
	srv_send((char *)"440 No such device");
	return 1;
    }

    if (strcmp(opt, (char *)"PUT") == 0) {
	for (device = Config.devices; device; device = device->next) {
	    if (strcmp(device->uuid, param) == 0) {
		while (1) {
		    rlen = srv_recv(ibuf);
    		    if (rlen == -1) {
			return 1;
		    }
		    if (strlen(ibuf)) {
			if (strcmp(ibuf, (char *)".") == 0) {
			    srv_send((char *)"219 Accepted Device record");
			    return 0;
			}
			kwd = strtok(ibuf, ",\0");
			val = strtok(NULL, "\0");
			if (kwd && val) {
			    if (strcmp(kwd, (char *)"TYPE") == 0) {
				for (i = 0; i < 8; i++) {
				    if (strcmp(val, DEVTYPE[i]) == 0) {
#ifdef HAVE_WIRINGPI_H
					piLock(LOCK_DEVICES);
#endif
					if (device->type != i)
					    syslog(LOG_NOTICE, "Device %s changed type %s to %s", device->uuid, DEVTYPE[device->type], DEVTYPE[i]);
					device->type = i;
#ifdef HAVE_WIRINGPI_H
					piUnlock(LOCK_DEVICES);
#endif
					break;
				    }
				}

			    } else if (strcmp(kwd, (char *)"DIRECTION") == 0) {
				for (i = 0; i < 7; i++) {
				    if (strcmp(val, DEVDIR[i]) == 0) {
#ifdef HAVE_WIRINGPI_H
					piLock(LOCK_DEVICES);
#endif
					if (device->direction != i)
					    syslog(LOG_NOTICE, "Device %s changed direction %s to %s", device->uuid, DEVDIR[device->type], DEVDIR[i]);
					device->direction = i;
#ifdef HAVE_WIRINGPI_H
					piUnlock(LOCK_DEVICES);
#endif
					break;
				    }
				}

			    } else if (strcmp(kwd, (char *)"VALUE") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
#ifdef HAVE_WIRINGPI_H
				    piLock(LOCK_DEVICES);
#endif
    				    if (device->value != ival)
					syslog(LOG_NOTICE, "Device %s changed value %d to %d", device->uuid, device->value, ival);
				    device->value = ival;
#ifdef HAVE_WIRINGPI_H
				    piUnlock(LOCK_DEVICES);
#endif
				}

			    } else if (strcmp(kwd, (char *)"OFFSET") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
#ifdef HAVE_WIRINGPI_H
				    piLock(LOCK_DEVICES);
#endif
    				    if (device->offset != ival)
					syslog(LOG_NOTICE, "Device %s changed offset %d to %d", device->uuid, device->offset, ival);
				    device->offset = ival;
#ifdef HAVE_WIRINGPI_H
				    piUnlock(LOCK_DEVICES);
#endif
				}

			    } else if (strcmp(kwd, (char *)"PRESENT") == 0) {
				for (i = 0; i < 4; i++) {
				    if (strcmp(val, DEVPRESENT[i]) == 0) {
#ifdef HAVE_WIRINGPI_H
					piLock(LOCK_DEVICES);
#endif
					if (device->present != i)
					    syslog(LOG_NOTICE, "Device %s changed present %s to %s", device->uuid, DEVPRESENT[device->present], DEVPRESENT[i]);
					device->present = i;
#ifdef HAVE_WIRINGPI_H
					piUnlock(LOCK_DEVICES);
#endif
					break;
				    }
				}

			    } else if (strcmp(kwd, (char *)"ADDRESS") == 0) {
#ifdef HAVE_WIRINGPI_H
				piLock(LOCK_DEVICES);
#endif
				if (device->address) {
				    if (strcmp(device->address, val))
					syslog(LOG_NOTICE, "Device %s changed address `%s' to `%s'", device->uuid, device->address, val);
				    free(device->address);
				}
				device->address = xstrcpy(val);
#ifdef HAVE_WIRINGPI_H               
				piUnlock(LOCK_DEVICES);
#endif

			    } else if (strcmp(kwd, (char *)"SUBDEVICE") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
#ifdef HAVE_WIRINGPI_H
				    piLock(LOCK_DEVICES);
#endif
				    if (device->subdevice != ival)
					syslog(LOG_NOTICE, "Device %s changed subdevice %d to %d", device->uuid, device->subdevice, ival);
				    device->subdevice = ival;
#ifdef HAVE_WIRINGPI_H               
				    piUnlock(LOCK_DEVICES);
#endif
				}

			    } else if (strcmp(kwd, (char *)"GPIOPIN") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
#ifdef HAVE_WIRINGPI_H
				    piLock(LOCK_DEVICES);
#endif
				    if (device->gpiopin != ival)
					syslog(LOG_NOTICE, "Device %s changed gpiopin %d to %d", device->uuid, device->gpiopin, ival);
				    device->gpiopin = ival;
#ifdef HAVE_WIRINGPI_H               
				    piUnlock(LOCK_DEVICES);
#endif
				}

			    } else if (strcmp(kwd, (char *)"DESCRIPTION") == 0) {
#ifdef HAVE_WIRINGPI_H
				piLock(LOCK_DEVICES);
#endif
				if (device->description) {
				    if (strcmp(device->description, val))
					syslog(LOG_NOTICE, "Device %s changed description `%s' to `%s'", device->uuid, device->description, val);
				    free(device->description);
				}
				device->description = xstrcpy(val);
#ifdef HAVE_WIRINGPI_H               
				piUnlock(LOCK_DEVICES);
#endif

			    } else if (strcmp(kwd, (char *)"COMMENT") == 0) {
#ifdef HAVE_WIRINGPI_H
				piLock(LOCK_DEVICES);
#endif
				if (device->comment) {
				    if (strcmp(device->comment, val))
					syslog(LOG_NOTICE, "Device %s changed comment `%s' to `%s'", device->uuid, device->comment, val);
				    free(device->comment);
				}
				device->comment = xstrcpy(val);
#ifdef HAVE_WIRINGPI_H               
				piUnlock(LOCK_DEVICES);
#endif

			    }
			}
		    }
		}
	    }
	}
	srv_send((char *)"440 No such device");
	return 1;
    }

    srv_send((char *)"504 Subcommand error");
    return 1;
}



/*
 * GLOBAL GET
 * GLOBAL PUT
 */
int cmd_global(char *buf)
{
    char	*opt, *kwd, *val, ibuf[SS_BUFSIZE];
    int		ival, rlen;

    opt = strtok(buf, " \0");
    opt = strtok(NULL, "\0");

    if (opt == NULL) {
	srv_send((char *)"501 Subcommand missing");
	return 1;
    }

    if (strcmp(opt, (char *)"GET") == 0) {
	srv_send((char *)"213 Global Settings record follows:");
	srv_send((char *)"RELEASE,%s", VERSION);
	srv_send((char *)"NAME,%s", Config.name);
	srv_send((char *)"PORT,%d", Config.my_port);
	srv_send((char *)"TEMPFORMAT,%c", Config.tempFormat);
	srv_send((char *)"TEMP_ADDRESS,%s", Config.temp_address);
	srv_send((char *)"TEMP_STATE,%s", TEMPSTATE[Config.temp_state]);
	srv_send((char *)"TEMP_VALUE,%.1f", Config.temp_value / 1000.0);
	srv_send((char *)"HUM_ADDRESS,%s", Config.hum_address);
	srv_send((char *)"HUM_STATE,%s", TEMPSTATE[Config.hum_state]);
	srv_send((char *)"HUM_VALUE,%.0f", Config.hum_value / 1000.0);
#ifdef HAVE_WIRINGPI_H
	srv_send((char *)"LCD_COLS,%d", Config.lcd_cols);
	srv_send((char *)"LCD_ROWS,%d", Config.lcd_rows);
#endif
	srv_send((char *)".");
	return 1;
    }

    if (strcmp(opt, (char *)"PUT") == 0) {
	while (1) {
	    rlen = srv_recv(ibuf);
	    if (rlen == -1) {
		return 1;
	    }
	    if (strlen(ibuf)) {
		if (strcmp(ibuf, (char *)".") == 0) {
		    srv_send((char *)"219 Accepted Global record");
		    return 0;
		}
		kwd = strtok(ibuf, ",\0");
		val = strtok(NULL, "\0");
		if (kwd) {
		    if (strcmp(kwd, (char *)"NAME") == 0) {
			if (val && Config.name && strcmp(val, Config.name))
			    syslog(LOG_NOTICE, "Global name `%s' to `%s'", Config.name, val);
			if (Config.name)
			    free(Config.name);
			if (val)
			    Config.name = xstrcpy(val);
			else
			    Config.name = NULL;

		    } else if (val && (strcmp(kwd, (char *)"PORT") == 0)) {
			if (sscanf(val, "%d", &ival) == 1) {
			    if (Config.my_port != ival)
			    	syslog(LOG_NOTICE, "Global port %d to %d", Config.my_port, ival);
			    Config.my_port = ival;
			}

		    } else if (val && (strcmp(kwd, (char *)"TEMPFORMAT") == 0)) {
			if ((val[0] == 'C') || (val[0] == 'F')) {
			    if (Config.tempFormat != val[0])
				syslog(LOG_NOTICE, "Global port %c to %c", Config.tempFormat, val[0]);
			    Config.tempFormat = val[0];
			}

		    } else if (strcmp(kwd, (char *)"TEMP_ADDRESS") == 0) {
			if (val && Config.temp_address && (strcmp(val, Config.temp_address)))
			    syslog(LOG_NOTICE, "Global temperature address `%s' to `%s'", Config.temp_address, val);
		        if (Config.temp_address) {
			    device_count(FALSE, Config.temp_address);
			    free(Config.temp_address);
			}
			if (val) {
			    Config.temp_address = xstrcpy(val);
			    device_count(TRUE, Config.temp_address);
			} else
			    Config.temp_address = NULL;

		    } else if (strcmp(kwd, (char *)"HUM_ADDRESS") == 0) {
			if (val && Config.hum_address && (strcmp(val, Config.hum_address)))
			    syslog(LOG_NOTICE, "Global humidity address `%s' to `%s'", Config.hum_address, val);
			if (Config.hum_address) {
			    device_count(FALSE, Config.hum_address);
			    free(Config.hum_address);
			}
			if (val) {
			    Config.hum_address = xstrcpy(val);
			    device_count(TRUE, Config.hum_address);
			} else
			    Config.hum_address = NULL;

#ifdef HAVE_WIRINGPI_H
		    } else if (val && (strcmp(kwd, (char *)"LCD_COLS") == 0)) {
			if (sscanf(val, "%d", &ival) == 1) {
			    if (Config.lcd_cols != ival)
				syslog(LOG_NOTICE, "Global LCD columns %d to %d", Config.lcd_cols, ival);
			    Config.lcd_cols = ival;
			}

		    } else if (val && (strcmp(kwd, (char *)"LCD_ROWS") == 0)) {
			if (sscanf(val, "%d", &ival) == 1) {
			    if (Config.lcd_rows != ival)
				syslog(LOG_NOTICE, "Global LCD rows %d to %d", Config.lcd_rows, ival);
			    Config.lcd_rows = ival;
			}
#endif
		    }
		}
	    }
	}
    }

    srv_send((char *)"504 Subcommand error");
    return 1;
}



/*
 * LIST
 * LIST LOG
 */
int cmd_list(char *buf)
{
    char		*opt, *param, *filename, q[5], buffer[256], outbuf[256];
    char		*date_n, *mode_n, *air_n, *beer_n, *target_n, *heater_n, *cooler_n, *room_n;
    char		*heater_u, *cooler_u;
    int			heater_l = 0, cooler_l = 0, h = 0, c = 0, heat_used = 0, cool_used = 0, lines = 0, graphstep = 0;
    units_list		*unit;
    FILE		*fp;
    float		room_t;

    opt = strtok(buf, " \0");
    opt = strtok(NULL, " \0");

    if (opt == NULL) {
	/*
	 * Default, list available units
	 */
	srv_send((char *)"212 Fermenter list follows:");
	for (unit = Config.units; unit; unit = unit->next) {
	    srv_send((char *)"%s,%s,%s", unit->uuid, unit->name, UNITMODE[unit->mode]);
	}
	srv_send((char *)".");
	return 1;

    } else if (strcmp(opt, (char *)"LOG") == 0) {

	param = strtok(NULL, "\0");
	if (param == NULL) {
	    srv_send((char *)"502 Parameter missing");
	    return 1;
	}

	q[0] = q[1] = q[2] = q[3] = q[4] = 'a';
	for (unit = Config.units; unit; unit = unit->next) {
	    if (strcmp(param, unit->uuid) == 0)
		break;
	}

	srv_send((char *)"212 Logfile list follows:");
	if (getenv((char *)"USER") == NULL) {
	    filename = xstrcpy((char *)"/root");
	} else {
	    filename = xstrcpy(getenv((char *)"HOME"));
	}
	filename = xstrcat(filename, (char *)"/.thermferm/log/");
	filename = xstrcat(filename, unit->name);
	filename = xstrcat(filename, (char *)".log");
	if ((fp = fopen(filename, "r"))) {

	    /*
	     * Count the lines in the logfile
	     */
	    lines = 0;
	    while (fgets(buffer, sizeof(buffer)-1, fp) != NULL) {
		lines++;
	    }
	    fseek(fp, 0L, SEEK_SET);
	    /*
	     * We have counted the lines in the logfile including the header lines.
	     * The header lines should be ignored but there are so few of them, we
	     * just include them in the total.
	     * Now find a reasonable interval of lines to sent to the client.
	     */
	    for (graphstep = 1; graphstep <= MAX_INTERVALS; graphstep++) {
		if (lines < GRAPH_DATALINES[graphstep]) {
		    break;
		}
	    }
	    syslog(LOG_NOTICE, "LIST LOG %s: lines=%d, interval=%d, graphstep=%d", unit->name, lines, GRAPH_INTERVAL[graphstep], graphstep);

	    while (fgets(buffer, sizeof(buffer)-1, fp) != NULL) {
		/*
		 * 2014-11-15 18:39,BEER,20.312,19.750,20.0,0,NA,NA,NA,78105,NA,NA
		 */
		q[0] = buffer[11];
		q[1] = buffer[12];
		q[2] = buffer[14];
		q[3] = buffer[15];
		buffer[strlen(buffer) -1] = '\0';
		date_n = strtok(buffer, ",\0");	/* timestamp		*/
		mode_n = strtok(NULL, ",\0");	/* unit mode		*/
		air_n = strtok(NULL, ",\0");	/* air temp		*/
		beer_n = strtok(NULL, ",\0");	/* beer temp		*/
		target_n = strtok(NULL, ",\0"); /* target temp		*/
		heater_n = strtok(NULL, ",\0");	/* current heater state	*/
		cooler_n = strtok(NULL, ",\0");	/* current cooler state	*/
		heater_u = strtok(NULL, ",\0");	/* current fan state	*/
		heater_u = strtok(NULL, ",\0");	/* current door state	*/
		heater_u = strtok(NULL, ",\0");	/* heater use counter	*/
		cooler_u = strtok(NULL, ",\0");	/* cooler use counter	*/
		room_n = strtok(NULL, ",\0");	/* fan use counter	*/
		room_n = strtok(NULL, ",\0");	/* room temperature	*/

		if (strncmp(mode_n, (char *)"Mode", 4)) {
		    /*
		     * Output a line at the right intervals
		     */
		    if (((graphstep == 1)) ||
			((graphstep == 2) && (q[3] == '0' || q[3] == '5')) ||
			((graphstep == 3) && ((q[2] == '0' && q[3] == '0') || (q[2] == '1' && q[3] == '5') || (q[2] == '3' && q[3] == '0') || (q[2] == '4' && q[3] == '5'))) ||
			((graphstep == 4) && ((q[2] == '0' && q[3] == '0') || (q[2] == '3' && q[3] == '0'))) ||
			((graphstep == 5) && (q[2] == '0' && q[3] == '0')) ) {
			heat_used = cool_used = 0;
			if (strcmp(heater_u, "NA") && (sscanf(heater_u, "%d", &h) == 1)) {
			    if (h && heater_l) {
				heat_used = ((h - heater_l) * 100) / (GRAPH_INTERVAL[graphstep] * 60);
			    }
			}
			if (strcmp(cooler_u, "NA") && (sscanf(cooler_u, "%d", &c) == 1)) {
			    if (c && cooler_l) {
				cool_used = ((c - cooler_l) * 100) / (GRAPH_INTERVAL[graphstep] * 60);
			    }
			}
			if (room_n)
			    sscanf(room_n, "%f", &room_t);
			else
			    room_t = 0.0;
			snprintf(outbuf, 255, "%s,%s,%s,%s,%s,%s,%s,%d,%d,%.1f", 
					    date_n, mode_n, air_n, beer_n, target_n, heater_n, cooler_n, heat_used, cool_used, room_t);
		    	srv_send(outbuf);
			if (h && strcmp(heater_u, "NA"))
			    heater_l = h;
			if (c & strcmp(cooler_u, "NA"))
			    cooler_l = c;
		    }
	    	}
	    }
	}
	free(filename);
	filename = NULL;
	srv_send((char *)".");
	return 1;
    }

    srv_send((char *)"504 Subcommand error");
    return 1;
}



/*
 * PROFILE ADD name	Add a new profile
 * PROFILE DEL uuid	Delete profile with uuid
 * PROFILE LIST		List available profiles
 * PROFILE GET uuid     Get profile record
 * PROFILE PUT uuid     Put profile record
 * PROFILE GETS uuid	Get profile steps list
 * PROFILE PUTS uuid	Put profile steps list
 */
int cmd_profile(char *buf)
{
    char                ibuf[SS_BUFSIZE], *sstep, *rest, *targ, *param, *kwd, *val;
    int                 j, rlen, istep, irest;
    float		ftarg, fval;
    char		*opt;
    profiles_list	*profile, *tmpp;
    prof_step		*step, *olds;
    uuid_t		uu;

    opt = strtok(buf, " \0");
    opt = strtok(NULL, " \0");

    if (opt == NULL) {
	srv_send((char *)"501 Subcommand missing");
	return 1;
    }

    if (strcmp(opt, (char *)"LIST") == 0) {
	/*
	 * Fermenting profiles
	 */
	srv_send((char *)"212 Profiles list follows:");
	for (profile = Config.profiles; profile; profile = profile->next) {
	    j = 0;
	    for (step = profile->steps; step; step = step->next)
		j++;
	    srv_send((char *)"%s,%s,%d,%d", profile->uuid, profile->name, j, profile->busy);
	}
	srv_send((char *)".");
	return 1;
    }

    param = strtok(NULL, "\0");
    if (param == NULL) {
	srv_send((char *)"502 Parameter missing");
	return 1;
    }

    if (strcmp(opt, (char *)"ADD") == 0) {
	profile = (profiles_list *)malloc(sizeof(profiles_list));
	profile->next = NULL;
	profile->version = 1;
	profile->uuid = malloc(37);
	uuid_generate(uu);
	uuid_unparse(uu, profile->uuid);
	profile->name = xstrcpy(param);
	profile->busy = 0;
	profile->inittemp = 20.0;
	profile->steps = NULL;
	if (Config.profiles == NULL) {
	    Config.profiles = profile;
	} else {
	    for (tmpp = Config.profiles; tmpp; tmpp = tmpp->next) {
		if (tmpp->next == NULL) {
		    tmpp->next = profile;
		    break;
		}
	    }
	}

	syslog(LOG_NOTICE, "Profile %s added", profile->uuid);
	srv_send((char *)"211 Profile %s added", profile->uuid);
	return 0;


    } else if (strcmp(opt, (char *)"DEL") == 0) {
	if (delete_Profile(param)) {
	    syslog(LOG_NOTICE, "Profile %s deleted", param);
	    srv_send((char *)"211 Profile %s deleted", param);
	    return 0;
	} else {
	    srv_send((char *)"440 No such profile");
	    return 1;
	}
    
    } else if (strcmp(opt, (char *)"GET") == 0) {
	for (profile = Config.profiles; profile; profile = profile->next) {
	    if (strcmp(profile->uuid, param) == 0) {
		srv_send((char *)"213 Profile record follows:");
		srv_send((char *)"UUID,%s", profile->uuid);
		srv_send((char *)"NAME,%s", profile->name);
		srv_send((char *)"INITTEMP,%.1f", profile->inittemp);
		srv_send((char *)".");
		return 1;
	    }
	}
	srv_send((char *)"440 No such profile");
	return 1;

    } else if (strcmp(opt, (char *)"PUT") == 0) {
	for (profile = Config.profiles; profile; profile = profile->next) {
	    if (strcmp(profile->uuid, param) == 0) {
		while (1) {
		    rlen = srv_recv(ibuf);
		    if (rlen == -1) {
			return 1;
		    }
		    if (strlen(ibuf)) {
			if (strcmp(ibuf, (char *)".") == 0) {
			    srv_send((char *)"219 Accepted Profile record");
			    return 0;
			}
			kwd = strtok(ibuf, ",\0");
			val = strtok(NULL, "\0");
			if (kwd && val) {
			    if (strcmp(kwd, (char *)"NAME") == 0) {
				if (profile->name) {
				    if (strcmp(profile->name, val))
					syslog(LOG_NOTICE, "Profile %s name `%s' to `%s'", profile->uuid, profile->name, val);
				    free(profile->name);
				}
				profile->name = xstrcpy(val);
			    } else if (strcmp(kwd, (char *)"INITTEMP") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (profile->inittemp != fval)
					syslog(LOG_NOTICE, "Profile %s initial temperature %.1f to %.1f", profile->uuid, profile->inittemp, fval);
				    profile->inittemp = fval;
				}
			    }
			}
		    }
		}
	    }               
	}
	srv_send((char *)"440 No such profile");
	return 1;

    } else if (strcmp(opt, (char *)"GETS") == 0) {

	for (profile = Config.profiles; profile; profile = profile->next) {
	    if (strcmp(profile->uuid, param) == 0) {
		srv_send((char *)"215 Profile steps follow:");
		for (step = profile->steps; step; step = step->next) {
		    srv_send((char *)"%d,%d,%.1f", step->steptime, step->resttime, step->target);
		}
		srv_send((char *)".");
		return 1;
	    }
	}

	srv_send((char *)"440 No such profile");
	return 1;

    } else if (strcmp(opt, (char *)"PUTS") == 0) {

	for (profile = Config.profiles; profile; profile = profile->next) {
	    if (strcmp(profile->uuid, param) == 0) {

		if (profile->steps) {
		    syslog(LOG_NOTICE, "PROFILE PUTS %s erased all old steps", profile->uuid);
		    for (step = profile->steps; step; step = olds) {
			olds = step->next;
			free(step);
		    }
		    profile->steps = NULL;		    
		}

		j = 0;
            	while (1) {	
		    rlen = srv_recv(ibuf);
	    	    if (rlen == -1) {
		    	return 1;
	    	    } else {
	    	    	if (strlen(ibuf)) {
		    	    if (strcmp(ibuf, (char *)".") == 0) {

		    	    	srv_send((char *)"219 Accepted Profile steps");
		    	    	return 0;
		    	    }
			    sstep = strtok(ibuf, ",\0");
			    rest = strtok(NULL, ",\0");
			    targ = strtok(NULL, "\0");

			    if ((sscanf(sstep, "%d", &istep) == 1) &&
				(sscanf(rest, "%d", &irest) == 1) &&
				(sscanf(targ, "%f", &ftarg) == 1)) {

				j++;
				syslog(LOG_NOTICE, "PROFILE PUTS %s add step %d: steptime=%d resttime=%d target=%.1f", 
						profile->uuid, j, istep, irest, ftarg);
				step = (prof_step *)malloc(sizeof(prof_step));
				step->next = NULL;
				step->version = 1;
				step->steptime = istep;
				step->resttime = irest;
				step->target = ftarg;

				if (profile->steps == NULL) {
				    profile->steps = step;
				} else {
				    for (olds = profile->steps; olds; olds = olds->next) {
					if (olds->next == NULL) {
					    olds->next = step;
					    break;
					}
				    }
				}
			    }
		    	}
	    	    }
		}
	    }
	}

	srv_send((char *)"440 No such profile");
	return 1;
    }

    srv_send((char *)"504 Subcommand error");
    return 1;
}



#ifdef USE_SIMULATOR
int delete_Simulator(char *uuid)
{
    simulator_list	*current = Config.simulators;
    simulator_list	*previous = NULL;

    while (current) {
	if (strcmp(current->uuid, uuid) == 0) {
	    if (previous == NULL) {
		Config.simulators = current->next;
	    	free(current->uuid);
	    	current->uuid = NULL;
	    	free(current->name);
	    	current->name = NULL;
	    	free(current);
	    	return 1;
	    } else {
		free(current->uuid);
		current->uuid = NULL;
		free(current->name);
		current->name = NULL;
		previous->next = current->next;
		free(current);
		current = previous->next;
		return 1;
	    }
	} else {
	    previous = current;
	    current = current->next;
	}
    }
    return 0;
}



/*
 * SIMULATOR ADD name
 * SIMULATOR DEL uuid
 * SIMULATOR LIST
 * SIMULATOR GET uuid
 * SIMULATOR PUT uuid
 */
int cmd_simulator(char *buf)
{
    char		*opt, *param, *kwd, *val, ibuf[SS_BUFSIZE];
    simulator_list	*simulator, *tmps;
    int			rc, rlen, ival;
    float		fval;
    uuid_t		uu;

    opt = strtok(buf, " \0");
    opt = strtok(NULL, " \0");

    if (opt == NULL) {
	srv_send((char *)"501 Subcommand missing");
	return 1;
    }
    param = strtok(NULL, "\0");

    if (strcmp(opt, (char *)"LIST") == 0) {
	srv_send((char *)"212 Simulators list follows:");
	for (simulator = Config.simulators; simulator; simulator = simulator->next) {
	    srv_send((char *)"%s,%s", simulator->uuid, simulator->name);
	}
	srv_send((char *)".");
	return 1;
    }

    if (param == NULL) {
	srv_send((char *)"502 Parameter missing");
	return 1;
    }

    if (strcmp(opt, (char *)"ADD") == 0) {

	/*
	 * For now, only one simulator is allowed.
	 */
	if (Config.simulators) {
	    srv_send((char *)"441 Maximum simulators reached");
	    return 1;
	}

	simulator = (simulator_list *)malloc(sizeof(simulator_list));
	simulator->next = NULL;
	simulator->version = 1;
	simulator->uuid = malloc(37);
	uuid_generate(uu);
	uuid_unparse(uu, simulator->uuid);
	simulator->name = xstrcpy(param);
	simulator->volume_air = 150;
	simulator->volume_beer = 50;
	simulator->room_temperature = simulator->air_temperature = simulator->beer_temperature = simulator->s_cool_temp = simulator->s_heat_temp = 20.0;
	simulator->cooler_temp = -3.0;	/* Cooling temperature */
	simulator->cooler_time = 720;	/* About 12 minutes for the cooler plate */
	simulator->cooler_size = 0.8;	/* 0.8 square meter cooler plate */
	simulator->heater_temp = 150.0;	/* Heating temperature */
	simulator->heater_time = 3;	/* 3 seconds to heat-up	*/
	simulator->heater_size = 0.01;	/* 0.01 square meter heater plate */
	simulator->heater_state = simulator->cooler_state = 0;
	simulator->frigo_isolation = 0.002;
	simulator->s_yeast_heat = 0.0;
	simulator->s_yeast_started = simulator->s_cool_changed = simulator->s_heat_changed = (int)0;

	if (Config.simulators == NULL) {
	    Config.simulators = simulator;
	} else {
	    for (tmps = Config.simulators; tmps; tmps = tmps->next) {
		if (tmps->next == NULL) {
		    tmps->next = simulator;
		    break;
		}
	    }
	}

	syslog(LOG_NOTICE, "Simulator %s added", simulator->uuid);
	srv_send((char *)"211 Simulator %s added", simulator->uuid);
	return 0;
    }

    if (strcmp(opt, (char *)"DEL") == 0) {
	rc = delete_Simulator(param);
	if (rc) {
	    syslog(LOG_NOTICE, "Simulator %s deleted", param);
	    srv_send((char *)"211 Simulator %s deleted", param);
	    return 0;
	} else {
	    srv_send((char *)"440 No such simulator");
	    return 1;
	}
    }

    if (strcmp(opt, (char *)"GET") == 0) {
	for (simulator = Config.simulators; simulator; simulator = simulator->next) {
	    if (strcmp(simulator->uuid, param) == 0) {
		srv_send((char *)"213 Simulator record follows:");
		srv_send((char *)"NAME,%s", simulator->name);
		srv_send((char *)"VOLUME_AIR,%d", simulator->volume_air);
		srv_send((char *)"VOLUME_BEER,%d", simulator->volume_beer);
		srv_send((char *)"ROOM_TEMPERATURE,%.1f", simulator->room_temperature);
		srv_send((char *)"AIR_TEMPERATURE,%.3f", simulator->air_temperature);
		srv_send((char *)"BEER_TEMPERATURE,%.3f", simulator->beer_temperature);
		srv_send((char *)"COOLER_TEMP,%.1f", simulator->cooler_temp);
		srv_send((char *)"COOLER_TIME,%d", simulator->cooler_time);
		srv_send((char *)"COOLER_SIZE,%.3f", simulator->cooler_size);
		srv_send((char *)"HEATER_TEMP,%.1f", simulator->heater_temp);
		srv_send((char *)"HEATER_TIME,%d", simulator->heater_time);
		srv_send((char *)"HEATER_SIZE,%.3f", simulator->heater_size);
		srv_send((char *)"HEATER_STATE,%d", simulator->heater_state);
		srv_send((char *)"COOLER_STATE,%d", simulator->cooler_state);
		srv_send((char *)"FRIGO_ISOLATION,%.3f", simulator->frigo_isolation);
		srv_send((char *)".");
		return 1;
	    }
	}
	srv_send((char *)"440 No such simulator");
	return 1;
    }

    if (strcmp(opt, (char *)"PUT") == 0) {
	for (simulator = Config.simulators; simulator; simulator = simulator->next) {
	    if (strcmp(simulator->uuid, param) == 0) {
		while (1) {
		    rlen = srv_recv(ibuf);
		    if (rlen == -1) {
			return 1;
		    }
		    if (strlen(ibuf)) {
			if (strcmp(ibuf, (char *)".") == 0) {
			    srv_send((char *)"219 Accepted Simulator record");
			    return 0;
			}
			kwd = strtok(ibuf, ",\0");
			val = strtok(NULL, "\0");
			if (kwd && val) {

			    if (strcmp(kwd, (char *)"NAME") == 0) {
				if (simulator->name) {
				    if (strcmp(simulator->name, val))
					syslog(LOG_NOTICE, "Simulator %s name `%s' to `%s'", simulator->uuid, simulator->name, val);
				    free(simulator->name);
				}
				simulator->name = xstrcpy(val);

			    } else if (strcmp(kwd, (char *)"VOLUME_AIR") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (simulator->volume_air != ival)
					syslog(LOG_NOTICE, "Simulator %s volume air %d to %d", simulator->uuid, simulator->volume_air, ival);
				    simulator->volume_air = ival;
				}

			    } else if (strcmp(kwd, (char *)"VOLUME_BEER") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (simulator->volume_beer != ival)
					syslog(LOG_NOTICE, "Simulator %s volume beer %d to %d", simulator->uuid, simulator->volume_beer, ival);
				    simulator->volume_beer = ival;
				}

			    } else if (strcmp(kwd, (char *)"ROOM_TEMPERATURE") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->room_temperature != fval)
					syslog(LOG_NOTICE, "Simulator %s room temperature %.1f to %.1f", simulator->uuid, simulator->room_temperature, fval);
				    simulator->room_temperature = fval;
				}

			    } else if (strcmp(kwd, (char *)"AIR_TEMPERATURE") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->air_temperature != fval)
					syslog(LOG_NOTICE, "Simulator %s air temperature %.1f to %.1f", simulator->uuid, simulator->air_temperature, fval);
				    simulator->air_temperature = fval;
				}

			    } else if (strcmp(kwd, (char *)"BEER_TEMPERATURE") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->beer_temperature != fval)
					syslog(LOG_NOTICE, "Simulator %s beer temperature %.1f to %.1f", simulator->uuid, simulator->beer_temperature, fval);
				    simulator->beer_temperature = fval;
				}

			    } else if (strcmp(kwd, (char *)"COOLER_TEMP") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->cooler_temp != fval)
					syslog(LOG_NOTICE, "Simulator %s cooler temperature %.1f to %.1f", simulator->uuid, simulator->cooler_temp, fval);
				    simulator->cooler_temp = fval;
				}

			    } else if (strcmp(kwd, (char *)"COOLER_TIME") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (simulator->cooler_time != ival)
					syslog(LOG_NOTICE, "Simulator %s cooler time %d to %d", simulator->uuid, simulator->cooler_time, ival);
				    simulator->cooler_time = ival;
				}
	
			    } else if (strcmp(kwd, (char *)"COOLER_SIZE") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->cooler_size != fval)
					syslog(LOG_NOTICE, "Simulator %s cooler size %.1f to %.1f", simulator->uuid, simulator->cooler_size, fval);
				    simulator->cooler_size = fval;
				}

			    } else if (strcmp(kwd, (char *)"HEATER_TEMP") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->heater_temp != fval)
					syslog(LOG_NOTICE, "Simulator %s heater temperature %.1f to %.1f", simulator->uuid, simulator->heater_temp, fval);
				    simulator->heater_temp = fval;
				}

			    } else if (strcmp(kwd, (char *)"HEATER_TIME") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (simulator->heater_time != ival)
					syslog(LOG_NOTICE, "Simulator %s heater time %d to %d", simulator->uuid, simulator->heater_time, ival);
				    simulator->heater_time = ival;
				}

			    } else if (strcmp(kwd, (char *)"HEATER_SIZE") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->heater_size != fval)
					syslog(LOG_NOTICE, "Simulator %s heater size %.1f to %.1f", simulator->uuid, simulator->heater_size, fval);
				    simulator->heater_size = fval;
				}

			    } else if (strcmp(kwd, (char *)"HEATER_STATE") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (simulator->heater_state != ival)
					syslog(LOG_NOTICE, "Simulator %s heater state %d to %d", simulator->uuid, simulator->heater_state, ival);
				    simulator->heater_state = ival;
				}

			    } else if (strcmp(kwd, (char *)"COOLER_STATE") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (simulator->cooler_state != ival)
					syslog(LOG_NOTICE, "Simulator %s cooler state %d to %d", simulator->uuid, simulator->cooler_state, ival);
				    simulator->cooler_state = ival;
				}

			    } else if (strcmp(kwd, (char *)"FRIGO_ISOLATION") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->frigo_isolation != fval)
					syslog(LOG_NOTICE, "Simulator %s frigo isolation %.1f to %.1f", simulator->uuid, simulator->frigo_isolation, fval);
				    simulator->frigo_isolation = fval;
				}

			    }
			}
		    }
		}
	    }
	}
	srv_send((char *)"440 No such simulator");
	return 1;
    }

    srv_send((char *)"504 Subcommand error");
    return 1;
}
#endif



int delete_Unit(char *uuid)
{
    units_list  *current = Config.units;
    units_list	*previous = NULL;

    while (current) {
	if (strcmp(current->uuid, uuid) == 0) {
	    if (previous == NULL) {
		Config.units = current->next;
		free(current->uuid);
		current->uuid = NULL;
		free(current->name);
		current->name = NULL;
		if (current->air_address)
		    free(current->air_address);
		current->air_address = NULL;
		if (current->beer_address)
		    free(current->beer_address);
		current->beer_address = NULL;
		if (current->heater_address)
		    free(current->heater_address);
		current->heater_address = NULL;
		if (current->cooler_address)
		    free(current->cooler_address);
		current->cooler_address = NULL;
		if (current->fan_address)
		    free(current->fan_address);
		current->fan_address = NULL;
		if (current->light_address)
		    free(current->light_address);
		current->light_address = NULL;
		if (current->door_address)
		    free(current->door_address);
		current->door_address = NULL;
		if (current->psu_address)
		    free(current->psu_address);
		current->psu_address = NULL;
		if (current->profile)
		    free(current->profile);
		current->profile = NULL;
		if (current->PID_cool)
		    free(current->PID_cool);
		current->PID_cool = NULL;
		if (current->PID_heat)
		    free(current->PID_heat);
		current->PID_heat = NULL;
		free(current);
		return 1;
	    } else {
		free(current->uuid);
		current->uuid = NULL;
		free(current->name);
		current->name = NULL;
		if (current->air_address)
		    free(current->air_address);
		current->air_address = NULL;
		if (current->beer_address)
		    free(current->beer_address);
		current->beer_address = NULL;
		if (current->heater_address)
		    free(current->heater_address);
		current->heater_address = NULL;
		if (current->cooler_address)
		    free(current->cooler_address);
		current->cooler_address = NULL;
		if (current->fan_address)
		    free(current->fan_address);
		current->fan_address = NULL;
		if (current->door_address)
		    free(current->door_address);
		current->door_address = NULL;
		if (current->light_address)
		    free(current->light_address);
		current->light_address = NULL;
		if (current->psu_address)
		    free(current->psu_address);
		current->psu_address = NULL;
		if (current->profile)
		    free(current->profile);
		current->profile = NULL;
		if (current->PID_cool)
		    free(current->PID_cool);
		current->PID_cool = NULL;
		if (current->PID_heat)
		    free(current->PID_heat);
		current->PID_heat = NULL;
		previous->next = current->next;
		free(current);
		current = previous->next;
		return 1;
	    }
	} else {
	    previous = current;
	    current = current->next;
	}
    }
    return 0;
}



/*
 * UNIT ADD name
 * UNIT DEL uuid
 * UNIT LIST
 * UNIT GET uuid
 * UNIT PUT uuid
 */
int cmd_unit(char *buf)
{
    char                *opt, *param = NULL, *kwd, *val, ibuf[SS_BUFSIZE];
    units_list          *unit, *tmpu;
    uuid_t		uu;
    int			ival, i, rc, rlen;
    float		fval;

    opt = strtok(buf, " \0");
    opt = strtok(NULL, " \0");

    if (opt == NULL) {
	srv_send((char *)"501 Subcommand missing");
	return 1;
    }
    param = strtok(NULL, "\0");

    if ((strcmp(opt, (char *)"LIST") == 0) && (param == NULL)) {
	srv_send((char *)"212 Fermenter list follows:");
	for (unit = Config.units; unit; unit = unit->next) {
	    srv_send((char *)"%s,%s,%s", unit->uuid, unit->name, UNITMODE[unit->mode]);
	}
	srv_send((char *)".");
	return 1;
    }

    if (param == NULL) {
	srv_send((char *)"502 Parameter missing");
	return 1;
    }

    if (strcmp(opt, (char *)"ADD") == 0) {
	unit = (units_list *)malloc(sizeof(units_list));
	unit->next = NULL;
	unit->version = 1;
	unit->uuid = malloc(37);
	uuid_generate(uu);
	uuid_unparse(uu, unit->uuid);
	unit->name = xstrcpy(param);
	unit->air_address = unit->beer_address = unit->heater_address = unit->cooler_address = \
			    unit->fan_address = unit->door_address = unit->light_address = \
			    unit->psu_address = unit->profile = NULL;
	unit->volume = unit->prof_peak_abs = unit->prof_peak_rel = 0.0;
	unit->air_state = unit->beer_state = 1;
	unit->air_temperature = unit->beer_temperature = 20000;
	unit->beer_set = unit->fridge_set = 20.0;
	unit->heater_state = unit->cooler_state = unit->fan_state = unit->door_state = unit->mode = \
			     unit->light_state = unit->psu_state = unit->prof_state = 0;
	unit->heater_delay = unit->cooler_delay = unit->fan_delay = 20;	/* 5 minutes delay	*/
	unit->light_delay = 1;						/* 15 seconds delay	*/
	unit->heater_wait = unit->cooler_wait = unit->fan_wait = unit->light_wait = 0;
	unit->heater_usage = unit->cooler_usage = unit->fan_usage = unit->light_usage = 0;
	unit->temp_set_min = 1.0;
	unit->temp_set_max = 30.0;
	unit->prof_started = unit->prof_paused = unit->prof_primary_done = (time_t)0;
	unit->prof_percent = 0;
	unit->PID_cool = (pid_var *)malloc(sizeof(pid_var));
	unit->PID_heat = (pid_var *)malloc(sizeof(pid_var));
	InitPID(unit->PID_cool, PID_TYPE_COOL);
	InitPID(unit->PID_heat, PID_TYPE_HEAT);

	/*
	 * Block main process
	 */
	run_pause = TRUE;
	for (;;) {
	    usleep(100000);
	    if (run_hold)
		break;
	}

	if (Config.units == NULL) {
	    Config.units = unit;
	} else {
	    for (tmpu = Config.units; tmpu; tmpu = tmpu->next) {
		if (tmpu->next == NULL) {
		    tmpu->next = unit;
		    break;
		}
	    }
	}
#ifdef HAVE_WIRINGPI_H
	lcd_buf_reset();
#endif
	run_pause = FALSE;

	syslog(LOG_NOTICE, "Unit %s added", unit->uuid);
	srv_send((char *)"211 Unit %s added", unit->uuid);
	return 0;
    }

    if (strcmp(opt, (char *)"DEL") == 0) {
	/*
	 * Block main process.
	 */
	run_pause = TRUE;
	for (;;) {
	    usleep(100000);
	    if (run_hold)
		break;
	}

	rc = delete_Unit(param);
#ifdef HAVE_WIRINGPI_H
	lcd_buf_reset();
#endif
	run_pause = FALSE;

	if (rc) {
	    syslog(LOG_NOTICE, "Unit %s deleted", param);
	    srv_send((char *)"211 Unit %s deleted", param);
	    return 0;
	} else {
	    srv_send((char *)"440 No such unit");
	    return 1;
	}
    }

    if (strcmp(opt, (char *)"GET") == 0) {
	for (unit = Config.units; unit; unit = unit->next) {
	    if (strcmp(param, unit->uuid) == 0) {
		srv_send((char *)"213 Unit listing follows:");
		srv_send((char *)"NAME,%s", unit->name);
		srv_send((char *)"UUID,%s", unit->uuid);
		srv_send((char *)"VOLUME,%2f", unit->volume);
		srv_send((char *)"AIR_ADDRESS,%s", unit->air_address);
		srv_send((char *)"AIR_STATE,%s", TEMPSTATE[unit->air_state]);
		srv_send((char *)"AIR_TEMPERATURE,%.3f", unit->air_temperature / 1000.0);
		srv_send((char *)"BEER_ADDRESS,%s", MBSE_SS(unit->beer_address));
		srv_send((char *)"BEER_STATE,%s", TEMPSTATE[unit->beer_state]);
		srv_send((char *)"BEER_TEMPERATURE,%.3f", unit->beer_temperature / 1000.0);
		srv_send((char *)"HEATER_ADDRESS,%s", unit->heater_address);
		srv_send((char *)"HEATER_STATE,%d", unit->heater_state);
		srv_send((char *)"HEATER_DELAY,%d", unit->heater_delay);
		srv_send((char *)"HEATER_USAGE,%d", unit->heater_usage);
		if (unit->PID_heat) {
		    srv_send((char *)"PIDH_IMAX,%.1f", unit->PID_heat->iMax);
		    srv_send((char *)"PIDH_PGAIN,%.2f", unit->PID_heat->pGain);
		    srv_send((char *)"PIDH_IGAIN,%.2f", unit->PID_heat->iGain);
		    srv_send((char *)"PIDH_DGAIN,%.2f", unit->PID_heat->dGain);
		    srv_send((char *)"PIDH_IDLERANGE,%.2f", unit->PID_heat->idleRange);
		}
		srv_send((char *)"COOLER_ADDRESS,%s", unit->cooler_address);
		srv_send((char *)"COOLER_STATE,%d", unit->cooler_state);
		srv_send((char *)"COOLER_DELAY,%d", unit->cooler_delay);
		srv_send((char *)"COOLER_USAGE,%d", unit->cooler_usage);
		if (unit->PID_cool) {
		    srv_send((char *)"PIDC_IMAX,%.1f", unit->PID_cool->iMax);
		    srv_send((char *)"PIDC_PGAIN,%.2f", unit->PID_cool->pGain);
		    srv_send((char *)"PIDC_IGAIN,%.2f", unit->PID_cool->iGain);
		    srv_send((char *)"PIDC_DGAIN,%.2f", unit->PID_cool->dGain);
		    srv_send((char *)"PIDC_IDLERANGE,%.2f", unit->PID_cool->idleRange);
		}
		srv_send((char *)"FAN_ADDRESS,%s", unit->fan_address);
		srv_send((char *)"FAN_STATE,%d", unit->fan_state);
		srv_send((char *)"FAN_DELAY,%d", unit->fan_delay);
		srv_send((char *)"FAN_USAGE,%d", unit->fan_usage);
		srv_send((char *)"LIGHT_ADDRESS,%s", unit->light_address);
		srv_send((char *)"LIGHT_STATE,%d", unit->light_state);
		srv_send((char *)"LIGHT_DELAY,%d", unit->light_delay);
		srv_send((char *)"LIGHT_USAGE,%d", unit->light_usage);
		srv_send((char *)"DOOR_ADDRESS,%s", unit->door_address);
		srv_send((char *)"DOOR_STATE,%d", unit->door_state);
		srv_send((char *)"PSU_ADDRESS,%s", unit->psu_address);
		srv_send((char *)"PSU_STATE,%d", unit->psu_state);
		srv_send((char *)"MODE,%s", UNITMODE[unit->mode]);
		srv_send((char *)"FRIDGE_SET,%.1f", unit->fridge_set);
		srv_send((char *)"BEER_SET,%.1f", unit->beer_set);
		srv_send((char *)"PROFILE,%s", unit->profile);
		srv_send((char *)"PROF_STARTED,%d", (int)unit->prof_started);
		if (unit->prof_state == PROFILE_RUN) {
		    srv_send((char *)"PROF_STATE,%s %d%%", PROFSTATE[unit->prof_state], unit->prof_percent);
		} else {
		    srv_send((char *)"PROF_STATE,%s", PROFSTATE[unit->prof_state]);
		}
		srv_send((char *)"PROF_TARGET,%.3f", unit->prof_target);
		srv_send((char *)"PROF_PEAK_ABS,%.3f", unit->prof_peak_abs);
		srv_send((char *)"PROF_PEAK_REL,%.3f", unit->prof_peak_rel);
		srv_send((char *)"PROF_PRIMARY_DONE,%d", (int)unit->prof_primary_done);
		srv_send((char *)"TEMP_SET_MIN,%.1f", unit->temp_set_min);
		srv_send((char *)"TEMP_SET_MAX,%.1f", unit->temp_set_max);
		srv_send((char *)".");
		return 1;
	    }
	}
	srv_send((char *)"440 No such unit");
	return 1;
    }

    if (strcmp(opt, (char *)"PUT") == 0) {
	/*
	 * Block main process
	 */
	run_pause = TRUE;
	for (;;) {
	    usleep(100000);
	    if (run_hold)
		break;
	}

	for (unit = Config.units ; unit; unit = unit->next) {
	     if (strcmp(unit->uuid, param) == 0) {
		while (1) {
		    rlen = srv_recv(ibuf);
		    if (rlen == -1) {
			run_pause = FALSE;
			return 1;
		    }
		    if (strlen(ibuf)) {
			if (strcmp(ibuf, (char *)".") == 0) {
			    srv_send((char *)"219 Accepted Unit record");
			    run_pause = FALSE;
			    return 0;
			}
			kwd = strtok(ibuf, ",\0");
			val = strtok(NULL, "\0");
			if (kwd) {
			    /*
			     * Accept writable data. The client can sent just one line,
			     * but may also sent everything. Simply ignore things we
			     * don't understand.
			     */
			    if (val && (strcmp(kwd, (char *)"NAME") == 0)) {
				if (unit->name) {
				    if (strcmp(unit->name, val))
					syslog(LOG_NOTICE, "Fermenter unit %s name `%s' to `%s'", unit->uuid, unit->name, val);
				    free(unit->name);
				}
				unit->name = xstrcpy(val);

			    } else if (val && (strcmp(kwd, (char *)"VOLUME") == 0)) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (unit->volume != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s volume %.3f to %.3f", unit->uuid, unit->volume, fval);
				    unit->volume = fval;
				}

			    } else if (strcmp(kwd, (char *)"AIR_ADDRESS") == 0) {
				if (val && unit->air_address && (strcmp(val, unit->air_address)))
				    syslog(LOG_NOTICE, "Fermenter unit %s air address `%s' to `%s'", unit->uuid, unit->air_address, val);
				if (unit->air_address) {
				    device_count(FALSE, unit->air_address);
				    free(unit->air_address);
				}
				if (val) {
				    unit->air_address  = xstrcpy(val);
				    device_count(TRUE, unit->air_address);
				} else
				    unit->air_address  = NULL;

			    } else if (strcmp(kwd, (char *)"BEER_ADDRESS") == 0) {
				if (val && unit->beer_address && (strcmp(val, unit->beer_address)))
				    syslog(LOG_NOTICE, "Fermenter unit %s beer address `%s' to `%s'", unit->uuid, unit->beer_address, val);
				if (unit->beer_address) {
				    device_count(FALSE, unit->beer_address);
				    free(unit->beer_address);
				}
				if (val) {
				    unit->beer_address = xstrcpy(val);
				    device_count(TRUE, unit->beer_address);
				} else
				    unit->beer_address = NULL;

			    } else if (strcmp(kwd, (char *)"HEATER_ADDRESS") == 0) {
				if (val && unit->heater_address && (strcmp(val, unit->heater_address)))
				    syslog(LOG_NOTICE, "Fermenter unit %s heater address `%s' to `%s'", unit->uuid, unit->heater_address, val);
				if (unit->heater_address) {
				    device_count(FALSE, unit->heater_address);
				    free(unit->heater_address);
				}
				if (val) {
				    unit->heater_address = xstrcpy(val);
				    device_count(TRUE, unit->heater_address);
				} else
				    unit->heater_address = NULL;

			    } else if (val && (strcmp(kwd, (char *)"HEATER_STATE") == 0)) {
				if ((sscanf(val, "%d", &ival) == 1) && ((ival == 0) || (ival == 100))) {
				    if (unit->heater_state != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s heater state %d to %d", unit->uuid, unit->heater_state, ival);
				    unit->heater_state = ival;
				}

			    } else if (val && (strcmp(kwd, (char *)"HEATER_DELAY") == 0)) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (unit->heater_delay != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s heater delay %d to %d", unit->uuid, unit->heater_delay, ival);
				    unit->heater_delay = ival;
				}

			    } else if (strcmp(kwd, (char *)"COOLER_ADDRESS") == 0) {
				if (val && unit->cooler_address && (strcmp(val, unit->cooler_address)))
				    syslog(LOG_NOTICE, "Fermenter unit %s cooler address `%s' to `%s'", unit->uuid, unit->cooler_address, val);
				if (unit->cooler_address) {
				    device_count(FALSE, unit->cooler_address);
				    free(unit->cooler_address);
				}
				if (val) {
				    unit->cooler_address = xstrcpy(val);
				    device_count(TRUE, unit->cooler_address);
				} else
				    unit->cooler_address = NULL;

			    } else if (val && (strcmp(kwd, (char *)"COOLER_STATE") == 0)) {
				if ((sscanf(val, "%d", &ival) == 1) && ((ival == 0) || (ival == 100))) {
				    if (unit->cooler_state != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s cooler state %d to %d", unit->uuid, unit->cooler_state, ival);
				    unit->cooler_state = ival;
				}

			    } else if (val && (strcmp(kwd, (char *)"COOLER_DELAY") == 0)) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (unit->cooler_delay != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s cooler delay %d to %d", unit->uuid, unit->cooler_delay, ival);
				    unit->cooler_delay = ival;
				}

			    } else if (strcmp(kwd, (char *)"FAN_ADDRESS") == 0) {
				if (val && unit->fan_address && (strcmp(val, unit->fan_address)))
				    syslog(LOG_NOTICE, "Fermenter unit %s fan address `%s' to `%s'", unit->uuid, unit->fan_address, val);
				if (unit->fan_address) {
				    device_count(FALSE, unit->fan_address);
				    free(unit->fan_address);
				}
				if (val) {
				    unit->fan_address = xstrcpy(val);
				    device_count(TRUE, unit->fan_address);
				} else
				    unit->fan_address = NULL;
			   
			    } else if (val && (strcmp(kwd, (char *)"FAN_STATE") == 0)) {
				if ((sscanf(val, "%d", &ival) == 1) && ((ival == 0) || (ival == 100))) {
				    if (unit->fan_state != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s fan state %d to %d", unit->uuid, unit->fan_state, ival);    
				    unit->fan_state = ival;
				}
			    
			    } else if (val && (strcmp(kwd, (char *)"FAN_DELAY") == 0)) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (unit->fan_delay != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s fan delay %d to %d", unit->uuid, unit->fan_delay, ival);
				    unit->fan_delay = ival;
				}

			    } else if (strcmp(kwd, (char *)"LIGHT_ADDRESS") == 0) {
				if (val && unit->light_address && (strcmp(val, unit->light_address)))
				    syslog(LOG_NOTICE, "Fermenter unit %s light address `%s' to `%s'", unit->uuid, unit->light_address, val);
				if (unit->light_address) {
				    device_count(FALSE, unit->light_address);
				    free(unit->light_address);
				}
				if (val) {
				    unit->light_address = xstrcpy(val);
				    device_count(TRUE, unit->light_address);
				} else
				    unit->light_address = NULL;

			    } else if (val && (strcmp(kwd, (char *)"LIGHT_STATE") == 0)) {
				if ((sscanf(val, "%d", &ival) == 1) && ((ival == 0) || (ival == 100))) {
				    if (unit->light_state != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s light state %d to %d", unit->uuid, unit->light_state, ival);
				    unit->light_state = ival;
				}

			    } else if (val && (strcmp(kwd, (char *)"LIGHT_DELAY") == 0)) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (unit->light_delay != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s light delay %d to %d", unit->uuid, unit->light_delay, ival);
				    unit->light_delay = ival;
				}

			    } else if (strcmp(kwd, (char *)"DOOR_ADDRESS") == 0) {
				if (val && unit->door_address && (strcmp(val, unit->door_address)))
				    syslog(LOG_NOTICE, "Fermenter unit %s door address `%s' to `%s'", unit->uuid, unit->door_address, val);
				if (unit->door_address) {
				    device_count(FALSE, unit->door_address);
				    free(unit->door_address);
				}
				if (val) {
				    unit->door_address = xstrcpy(val);
				    device_count(TRUE, unit->door_address);
				} else
				    unit->door_address = NULL;

			    } else if (strcmp(kwd, (char *)"PSU_ADDRESS") == 0) {
				if (val && unit->psu_address && (strcmp(val, unit->psu_address)))
				    syslog(LOG_NOTICE, "Fermenter unit %s psu address `%s' to `%s'", unit->uuid, unit->psu_address, val);
				if (unit->psu_address) {
				    device_count(FALSE, unit->psu_address);
				    free(unit->psu_address);
				}
				if (val) {
				    unit->psu_address = xstrcpy(val);
				    device_count(TRUE, unit->psu_address);
				} else
				    unit->psu_address = NULL;

			    } else if (val && (strcmp(kwd, (char *)"MODE") == 0)) {
				for (i = 0; i < 5; i++) {
				    if (strcmp(val, UNITMODE[i]) == 0) {
					/* Initialize log if the unit is turned on */
					if ((unit->mode == UNITMODE_OFF) && (i != UNITMODE_OFF))
					    initlog(unit->name);
					syslog(LOG_NOTICE, "Fermenter unit %s mode %s to %s", unit->uuid, UNITMODE[unit->mode], UNITMODE[i]);
					unit->mode = i;
					/* Allways turn everything off after a mode change */
					unit->PID_cool->OutP = unit->PID_heat->OutP = 0.0;
					unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
					unit->heater_state = unit->cooler_state = unit->fan_state = unit->light_state = 0;
					unit->heater_wait = unit->cooler_wait = unit->fan_wait = unit->light_wait = 0;
					device_out(unit->heater_address, unit->heater_state);
					device_out(unit->cooler_address, unit->cooler_state);
					device_out(unit->fan_address, unit->fan_state);
					device_out(unit->light_address, unit->light_state);
					if (unit->mode == UNITMODE_PROFILE) {
					    /*
					     * Set a sane default until it will be overruled by the
					     * main processing loop.
					     */
					    unit->prof_target = 20.0;
					}
					break;
				    }
				}

			    } else if (val && (strcmp(kwd, (char *)"FRIDGE_SET") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= unit->temp_set_min) && (fval <= unit->temp_set_max)) {
				    if (unit->fridge_set != fval)
				    	syslog(LOG_NOTICE, "Fermenter unit %s fridge temperature %.1f to %.1f", unit->uuid, unit->fridge_set, fval);
				    unit->fridge_set = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"BEER_SET") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= unit->temp_set_min) && (fval <= unit->temp_set_max)) {
				    if (unit->beer_set != fval)
				    	syslog(LOG_NOTICE, "Fermenter unit %s beer temperature %.1f to %.1f", unit->uuid, unit->beer_set, fval);
				    unit->beer_set = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDC_IMAX") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_cool->iMax != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_cool iGain %.1f to %.1f", unit->uuid, unit->PID_cool->iMax, fval);
				    unit->PID_cool->iMax = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDC_PGAIN") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_cool->pGain != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_cool pGain %.2f to %.2f", unit->uuid, unit->PID_cool->pGain, fval);
				    unit->PID_cool->pGain = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDC_DGAIN") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_cool->dGain != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_cool dGain %.2f to %.2f", unit->uuid, unit->PID_cool->dGain, fval);
				    unit->PID_cool->dGain = fval;
				}
	
			    } else if (val && (strcmp(kwd, (char *)"PIDC_IGAIN") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_cool->iGain != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_cool iGain %.2f to %.2f", unit->uuid, unit->PID_cool->iGain, fval);
				    unit->PID_cool->iGain = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDC_IDLERANGE") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_cool->idleRange != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_cool idleRange %.2f to %.2f", unit->uuid, unit->PID_cool->idleRange, fval);
				    unit->PID_cool->idleRange = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDH_IMAX") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_heat->iMax != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_heat iGain %.1f to %.1f", unit->uuid, unit->PID_heat->iMax, fval);
				    unit->PID_heat->iMax = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDH_PGAIN") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_heat->pGain != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_heat pGain %.2f to %.2f", unit->uuid, unit->PID_heat->pGain, fval);
				    unit->PID_heat->pGain = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDH_DGAIN") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_heat->dGain != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_heat dGain %.2f to %.2f", unit->uuid, unit->PID_heat->dGain, fval);
				    unit->PID_heat->dGain = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDH_IGAIN") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_heat->iGain != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PIH_heat iGain %.2f to %.2f", unit->uuid, unit->PID_heat->iGain, fval);
				    unit->PID_heat->iGain = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDH_IDLERANGE") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_heat->idleRange != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_heat idleRange %.2f to %.2f", unit->uuid, unit->PID_heat->idleRange, fval);
				    unit->PID_heat->idleRange = fval;
				}

			    } else if (strcmp(kwd, (char *)"PROFILE") == 0) {
				if (unit->prof_state == PROFILE_OFF) {
				    /*
				     * Only change profile if it is not active, else drop this one.
				     */
				    if (unit->profile && val && strcmp(unit->profile, val))
					syslog(LOG_NOTICE, "Fermenter unit %s profile name `%s' to `%s'", unit->uuid, unit->profile, val);
				    if (unit->profile)
				    	free(unit->profile);

				    if (val)
				    	unit->profile = xstrcpy(val);
				    else
				    	unit->profile = NULL;
				    /*
				     * Reset all output devices
				     */
				    unit->PID_cool->OutP = unit->PID_heat->OutP = 0.0;
				    unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
				    unit->heater_state = unit->cooler_state = unit->fan_state = unit->light_state = 0;
				    unit->heater_wait = unit->cooler_wait = unit->fan_wait = unit->light_wait = 0;
				    device_out(unit->heater_address, unit->heater_state);
				    device_out(unit->cooler_address, unit->cooler_state);
				    device_out(unit->fan_address, unit->fan_state);
				    device_out(unit->light_address, unit->light_state);
				}

			    } else if (val && (strcmp(kwd, (char *)"PROF_STATE") == 0)) {
				for (i = 0; i < 5; i++) {
				    if (strcmp(val, PROFSTATE[i]) == 0) {
					switch (i) {
						case PROFILE_OFF:	if (unit->prof_state == PROFILE_DONE) {
									    unit->prof_state = PROFILE_OFF;
									    syslog(LOG_NOTICE, "Fermenter unit %s profile to OFF", unit->uuid);
									}
									break;
						case PROFILE_PAUSE:	if (unit->prof_state == PROFILE_RUN) {
									    unit->prof_state = PROFILE_PAUSE;
									    syslog(LOG_NOTICE, "Fermenter unit %s profile PAUSE", unit->uuid);
									} else if (unit->prof_state == PROFILE_PAUSE) {
									    unit->prof_state = PROFILE_RUN;
									    syslog(LOG_NOTICE, "Fermenter unit %s profile RESUME", unit->uuid);
									}
									break;
						case PROFILE_RUN:	if (unit->prof_state == PROFILE_OFF) {
									    unit->prof_state = PROFILE_RUN;
									    unit->prof_started = time(NULL);
									    unit->prof_paused = unit->prof_primary_done = 0;
									    unit->prof_peak_abs = unit->prof_peak_rel = 0.0;
									    syslog(LOG_NOTICE, "Fermenter unit %s profile to RUN", unit->uuid);
									}
									break;
						case PROFILE_DONE:	break;	/* Command is illegal */
						case PROFILE_ABORT:	if ((unit->prof_state == PROFILE_RUN) || (unit->prof_state == PROFILE_PAUSE)) {
									    unit->prof_state = PROFILE_OFF;
									    unit->prof_started = 0;
									    syslog(LOG_NOTICE, "Fermenter unit %s profile ABORT", unit->uuid);
									}
									break;
					}
					break;
				    }
				}

			    } else if (val && (strcmp(kwd, (char *)"TEMP_SET_MIN") == 0)) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (unit->temp_set_min != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s temperature set minimum %.1f to %.1f", unit->uuid, unit->temp_set_min, fval);
				    unit->temp_set_min = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"TEMP_SET_MAX") == 0)) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (unit->temp_set_max != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s temperature set maximum %.1f to %.1f", unit->uuid, unit->temp_set_max, fval);
				    unit->temp_set_max = fval;
				}

			    }
			}
		    }
		}
	    }
	}
	srv_send((char *)"440 No such unit");
	run_pause = FALSE;
	return 1;
    }

    srv_send((char *)"504 Subcommand error");
    return 1;
}



void cmd_server(void)
{
    char                buf[SS_BUFSIZE];
    int                 rlen;

    rlen = srv_recv(buf);
    if (rlen != -1) {
	if (strlen(buf)) {
	    /*
	     * Process commands from the client
	     */
	    if (strncmp(buf, "ARCHIVE", 7) == 0) {
		cmd_archive(buf);
	    } else if (strncmp(buf, "DEVICE", 6) == 0) {
		if (cmd_device(buf) == 0)
		    wrconfig();
	    } else if (strncmp(buf, "GLOBAL", 6) == 0) {
		if (cmd_global(buf) == 0)
		    wrconfig();
	    } else if (strncmp(buf, "HELP", 4) == 0) {
		srv_send((char *)"100 Help text follows");
		srv_send((char *)"Recognized commands:");
		srv_send((char *)"");
//                                12345678901234567890123456789012345678901234567890123456789012345678901234567890
		srv_send((char *)"ARCHIVE <CMD> [parameters]    Archive commands");
		srv_send((char *)"ARCHIVE HELP                  Archive help screen");
		srv_send((char *)"DEVICE ADD type               Add new Device type");
		srv_send((char *)"DEVICE DEL uuid               Delete Device by uuid");
		srv_send((char *)"DEVICE LIST                   List Devices");
		srv_send((char *)"DEVICE GET uuid               Get Device record by uuid");
		srv_send((char *)"DEVICE PUT uuid               Put Device record by uuid");
		srv_send((char *)"GLOBAL GET                    Get global settings");
		srv_send((char *)"GLOBAL PUT                    Put global settings");
		srv_send((char *)"LIST                          List all fermenter units");
		srv_send((char *)"LIST LOG uuid                 List logfile data graphsteps");
		srv_send((char *)"PING                          Check if server is alive");
		srv_send((char *)"PROFILE uuid,name             Profile rename");
		srv_send((char *)"PROFILE ADD name              Add new Profile with name");
		srv_send((char *)"PROFILE DEL uuid              Delete Profile by uuid");
		srv_send((char *)"PROFILE LIST                  List available profiles");
		srv_send((char *)"PROFILE GET uuid              Get Profile record by uuid");
		srv_send((char *)"PROFILE PUT uuid              Put Profile record by uuid");
		srv_send((char *)"PROFILE GETS uuid             Profile get steps list");
		srv_send((char *)"PROFILE PUTS uuid             Profile put steps list");
#ifdef USE_SIMULATOR
		srv_send((char *)"SIMULATOR ADD name            Add a new Simulator with name");
		srv_send((char *)"SIMULATOR DEL uuid            Delete Simulator by uuid");
		srv_send((char *)"SIMULATOR LIST                List all Simulators");
		srv_send((char *)"SIMULATOR GET uuid            Get Simulator record by uuid");
		srv_send((char *)"SIMULATOR PUT uuid            Put Simulator record by uuid");
#endif
		srv_send((char *)"UNIT ADD name                 Add a new Unit with name");
		srv_send((char *)"UNIT DEL uuid                 Delete Unit by uuid");
		srv_send((char *)"UNIT LIST                     List all Units");
		srv_send((char *)"UNIT GET uuid                 Get Unit record by uuid");
		srv_send((char *)"UNIT PUT uuid                 Put Unit record by uuid");
		srv_send((char *)".");
	    } else if (strncmp(buf, "LIST", 4) == 0) {
		cmd_list(buf);
	    } else if (strncmp(buf, "PING", 4) == 0) {
		srv_send((char *)"101 PONG");
	    } else if (strncmp(buf, "PROFILE", 7) == 0) {
		if (cmd_profile(buf) == 0)
		    wrconfig();
#ifdef USE_SIMULATOR
	    } else if (strncmp(buf, "SIMULATOR", 9) == 0) {
		if (cmd_simulator(buf) == 0)
		    wrconfig();
#endif
	    } else if (strncmp(buf, "UNIT", 4) == 0) {
		if (cmd_unit(buf) == 0)
		    wrconfig();
	    } else {
		srv_send((char *)"500 Unknown command");
	    }
	}
    }

    close(s);
}



#ifdef HAVE_WIRINGPI_H
PI_THREAD (my_server_loop)
#else
void *my_server_loop(void *threadid)
#endif
{
    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 0;
    }

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

    if (listen(ls, 5) == -1) {
	syslog(LOG_NOTICE, "Can't listen on listen socket: %s", strerror(errno));
	close(ls);
	return 0;
    }

    syslog(LOG_NOTICE, "listen socket created %d", 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;
	}

	cmd_server();

	if (my_shutdown) {
	    syslog(LOG_NOTICE, "Thread my_server_loop stopped");
	    if (debug)
		fprintf(stdout, "Thread my_server_loop stopped\n");
	    return 0;
	}

    }
}

mercurial