utils.c

/* This is free software.  See LICENSE for terms.
 * Copyright 2004 - 2015, Patricia Kirk.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <math.h>
#include <errno.h>
#include <netdb.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <ctype.h>

#if defined(__OpenBSD__)
#include <sys/socket.h>
#include <netinet/in_systm.h>
#endif

#if defined(__NetBSD__)
#include <errno.h>
#include <netinet/in_systm.h>
#endif

#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>

#if defined (__linux__)
#include <time.h>
#include <sys/time.h>
#endif

#include "proto.h"

extern struct global g;
extern struct lineopts *linelist;

extern char **cmdline;
extern char **cmdargs;

struct data fresh;
struct pingsent *pings=NULL;
static int ident=0;
unsigned long sequence;

char buffer[BUFPLUS];
char smalbuf[SMALBUF];

void setMinums() {

    pixAlloc(BACKGROUND);
    pixAlloc(FOREGROUND);
    pixAlloc(SHUTDOWN);
    STRDUP(g.cfgfile, CFGFILE);
    STRDUP(g.datadir, DATADIR);
    STRDUP(g.audiocmd, AUDIOCMD);
    STRDUP(g.audiodev, AUDIODEV);
    if (getenv("TMPDIR")) {
	STRDUP(g.tempdir, getenv("TMPDIR"));
    } else {
	STRDUP(g.tempdir, TEMPDIR);
    }
    g.linecount = 1;
    g.xmag = 2;
}

void setRequired() {
    char *ptr=NULL;

    if ((g.moongraphic || (g.pixmap > 0)) && !g.resize) {
	if (findExecutable(buffer, BUFSIZE, RESIZE)) {
	    STRDUP(g.resize, buffer);
	}
    } else if (g.pixmap < 0 && !g.tiler) {
	if (findExecutable(buffer, BUFSIZE, TILER)) {
	    STRDUP(g.tiler, buffer);
	}
    }
    if ((g.pixmap || g.moongraphic) && !g.unzip) {
	if (findExecutable(buffer, BUFSIZE, UNZIP)) {
	    STRDUP(g.unzip, buffer);
	}
    }
    getHostNet();
    g.localhost = 1;
    if (g.display && (ptr = strchr(g.display, ':')) &&
	    strncmp(g.hostname, g.display, ptr - g.display))
	g.localhost = 0;
    if (g.timehost && !strncmp(g.hostname, g.timehost, strlen(g.timehost)))
	g.timeserver = 1;
    if (g.moongraphic && g.pixmap)
	g.pixmap = 1;
}

int itsanInt(char *buffer) {
    char *ptr;

    errno = 0;
    strtol(buffer, &ptr, 10);
    if (errno || !ptr || (*ptr != '\0' && !isspace((int)*ptr)))
	return 0;
    return 1;
}

int parseStrtol(char *arg, int plus, char *from) {
    char *end=NULL;
    int i=0;

    errno = 0;
    if (arg)
	i = (int)strtol(arg, &end, 10);
    if (errno || !end || (*end != '\0' && !isspace((int)*end))) {
	snprintf(smalbuf, SMALBUF, "parseStrtol() from %s(), %s", from, arg);
	if (!errno)
	    errno = EINVAL;
	gdayMate(smalbuf, strerror(errno));
    }
    if (plus)
	return abs(i);
    return i;
}

/* Use the speech synthesizer to speak the contents
 * of cfgfile or cmdfile at the numeric label.
 * Called by setting sound=-label.
 */
void readAloud(int noise) {
    char **args=NULL;

    if (!g.talker)
	return;
    if (g.cmdfile)
	snprintf(smalbuf, SMALBUF, ":%d", abs(noise));
    else
	snprintf(smalbuf, SMALBUF, "~%d", abs(noise));
    args = readArgs(smalbuf, "return", 0);
    if (!args || parseStrtol(args[0], TRUE, "readAloud") != 2) {
	snprintf(buffer, BUFPLUS, "readAloud() invalid label \"%s\"", smalbuf);
	logMessage(buffer);
	FREE(args);
	return;
    }
    while (*args[1] == '-')
	args[1]++;
    snprintf(buffer, BUFPLUS, "%s \"%s\" &", g.talker, args[1]);
    execComm(buffer);
    FREE(args);
}

void makeNoise(int noise) {
    
    if (noise < 0) {
	readAloud(noise);
	return;
    }
    if (noise && g.audiocmd) {
	if (g.audiodev)
	    snprintf(buffer, BUFPLUS, "%s %s/%03d.au >%s &",
		    g.audiocmd, g.datadir, noise, g.audiodev);
	else
	    snprintf(buffer, BUFPLUS, "%s %s/%03d.au &",
		    g.audiocmd, g.datadir, noise);
	execComm(buffer);
    }
}

#if WITH_COMMANDS || WITH_STEPEXEC
/* Execute a command from a key or button press or an expired
 * timer/alarm.  This may read from a config file that was
 * not used to start salmon or has been changed since then.
 */
void execComm(char *command) {
    char *temp=NULL, **args=NULL;

#if WITH_STEPEXEC
    if (*command == '&') {
	execFunc((char *)command);
	return;
    }
#endif	/* WITH_STEPEXEC */
    STRDUP(temp, command);
    if (*temp == '~' || *temp == ':') {
	/* Read command from cmdfile or cfgfile. */
	args = readArgs(temp, "return", 0);
	if (!args || parseStrtol(args[0], TRUE, "execComm") != 2) {
	    snprintf(buffer, BUFPLUS, "execComm() invalid label \"%s\"", command);
	    logMessage(buffer);
	    goto jail;
	}
	while (*args[1] == '-')
	    args[1]++;
	snprintf(buffer, BUFPLUS, "%s", args[1]);
    } else {
	/* Execute command as is. */
	snprintf(buffer, BUFPLUS, "%s", temp);
    }
    if (strlen(buffer) && !fork())
	execl("/bin/sh", "/bin/sh", "-c", buffer, (char *)0);
jail:
    FREE(args);
    FREE(temp);
}
#endif	/* WITH_COMMANDS || WITH_STEPEXEC */

#if WITH_STEPEXEC
#if defined(__linux__)
#include <signal.h>
#endif	/* (__linux__) */

extern Window rootwindow;

void deadPipe() {

    gdayMate("execFunc() received SIGPIPE", strerror(errno));
}

/* Attempt to execute an afterstep internal function.
 * The user is responsible for providing a valid
 * function name and proper syntax.
 */
void execFunc(char *command) {
    static int init=1;
    int len;
    char *cp0;

    if (!g.localhost)
	return;
    if (init) {
	/* Initialize the afterstep connection. */
	len = (g.appname ? strlen(g.appname) : 6) + 10;
	snprintf(buffer, len, "SET_NAME %s", g.appname ? g.appname : "salmon");
	signal(SIGPIPE, deadPipe);
	init = 0;
    } else {
	cp0 = command + 1;
	len = strlen(cp0) + (strchr(cp0, ' ') ? 10 : 1);
	snprintf(buffer, len, "%s", cp0);
	if ((cp0 = strchr(cp0, ' '))) {
	    *strchr(buffer, ' ') = '\0';
	    stringcat(buffer, " \"dummy\"", len);
	    stringcat(buffer, cp0, len);
	}
    }
    len = strlen(buffer);
    write(g.stepsock, &rootwindow, sizeof(unsigned long));
    write(g.stepsock, &len, sizeof(size_t));
    write(g.stepsock, buffer, len);
    len = 1;
    write(g.stepsock, &len, sizeof(size_t));
}
#endif	/* WITH_STEPEXEC */

/* Copy src to dst, insure that dst is terminated
 * with a null byte and return the total length
 * including the null byte.  If truncation would
 * result, return -len, dst is unchanged.  Caller
 * is responsible for insuring that dst and src
 * have terminating null bytes.
 */
int stringcpy(char *dst, char *src, int maxlen) {
    int len;

    len = strlen(src) + 1;
    if (maxlen <= 0 || len > maxlen)
	return -len;
    strncpy(dst, src, maxlen);
    dst[len] = '\0';
    return len;
}

/* Append src to dst, insure that dst is terminated
 * with a null byte and return the total length
 * including the null byte.  If truncation would
 * result, return -len, dst is unchanged.  Caller
 * is responsible for insuring that dst and src
 * have terminating null bytes.
 */
int stringcat(char *dst, char *src, int maxlen) {
    int len;

    len = strlen(dst) + strlen(src) + 1;
    if (maxlen <= 0 || len > maxlen)
	return -len;
    strncat(dst, src, maxlen);
    dst[len] = '\0';
    return len;
}

/* Devide time in seconds into days, etc.
 * Used by timers and elapsed.
 */
void splitSeconds(int *days, int *hours, int *minutes, int *seconds, int timeval) {
    int value;

    value = timeval;
    if (seconds)
	*seconds = value % 60;
    value /= 60;
    if (minutes)
	*minutes = value % 60;
    value /= 60;
    if (hours)
	*hours = value % 24;
    value /= 24;
    if (days)
	*days = value;
}

/* Use the locate data base to find helper executables.
 * Used by NetBSD for envstat, OpenBSD for apm, FreeBSD
 * for swapinfo and all OSs for UNZIP, RESIZE, TILER
 * TALKER, and MIXER.  Call with a static char buffer
 * or STRDUP into struct global.
 */
int findExecutable(char *execbuf, int execlen, char *executable) {
    char *buffer, *ptr, *showbuf;
    struct stat sb;
    FILE *fd;
    int yes=0;

    if (*executable == '/')
	return 1;	/* User sent absolute path. */
    CALLOC(buffer, BUFSIZE, sizeof(char), "findExecutable");
    CALLOC(showbuf, SHOWBUF, sizeof(char), "findExecutable");
    bzero(execbuf, execlen);
    snprintf(buffer, BUFSIZE, "locate bin/%s", executable);
    ptr = strchr(buffer, '/');
    if ((ptr = strchr(ptr, ' '))) {
	/* Separate any args and save them. */
	*ptr++ = '\0';
	snprintf(showbuf, SHOWBUF, "%s", ptr);
    }
    if ((fd = popen(buffer, "r"))) {
	buffer[0] = '\0';
	while (fgets(buffer, BUFSIZE, fd)) {
	    if (strchr(buffer, '\n'))
		*strchr(buffer, '\n') = '\0';
	    if ((ptr = strrchr(buffer, '/')) && !strcmp(ptr + 1, executable))
		break;
	}
	pclose(fd);
	if (strlen(buffer)) {
	    if (stat(buffer, &sb) == -1)
		goto nyet;
	    if (!(sb.st_mode & S_IXOTH)) {
		errno = EACCES;
		goto nyet;
	    }
	    snprintf(execbuf, execlen, "%s ", buffer);
	    if (strlen(showbuf))
		stringcat(execbuf, showbuf, execlen);
	    yes = 1;
	} else {
	    errno = ENOENT;
nyet:
	    snprintf(buffer, BUFSIZE, "findExecutable() %s", executable);
	    gdayMate(buffer, strerror(errno));
	}
    }
    FREE(buffer);
    FREE(showbuf);
    return yes;
}

#if WITH_SUID
extern struct pixstack *iconpix, *mainpix;

int execShutdown(int time) {
    struct pixstack *pix, *next;
    int stopping=0;
    FILE *fd;

    if (g.localhost && !g.effectiveuid) {
	if (time >= 0) {
#if defined(__FreeBSD__)
	    snprintf(smalbuf, SMALBUF, "/sbin/shutdown -p +%d", time);
#elif defined(__linux__)
	    snprintf(smalbuf, SMALBUF, "/sbin/shutdown -h -P +%d &", time);
#else
	    snprintf(smalbuf, SMALBUF, "/sbin/shutdown -h -p +%d", time);
#endif	/* __FreeBSD__ */
	    logMessage("execute shutdown");
	} else {
	    snprintf(smalbuf, SMALBUF, "pkill shutdown");
	    logMessage("kill shutdown");
	}
	seteuid(g.effectiveuid);
	if ((fd = popen(smalbuf, "r"))) {
	    pclose(fd);
	    logMessage("success");
	    stopping = (time >= 0);
	    pix = iconpix->next;
	    next = iconpix->next->next;
	    pix->next = next->next;
	    next->next = pix;
	    iconpix->next = next;
	    iconpix->next->next = pix;
	    pix = mainpix->next;
	    next = mainpix->next->next;
	    pix->next = next->next;
	    next->next = pix;
	    mainpix->next = next;
	    mainpix->next->next = pix;
	} else {
	    logMessage("failure");
	}
	seteuid(g.realuid);
    }
    return stopping;
}
#endif	/* WITH_SUID */

/* Phase of the Moon.  Calculates the current phase of the moon.
 * Based on routines from `Practical Astronomy with Your Calculator',
 * by Duffett-Smith.  Comments give the section from the book that
 * particular piece of code was adapted from.
 *
 * -- Keith E. Brandt  VIII 1984
 *
 * Updated to the Third Edition of Duffett-Smith's book, Paul Janzen, IX 1998
 *
 * The EPOCH in the third edition of the book is 1990 Jan 0.0 TDT.
 * In this program, we do not bother to correct for the differences
 * between UTC (as shown by the UNIX clock) and TDT.  (TDT = TAI + 32.184s;
 * TAI-UTC = 32s in Jan 1999.)
 */
#define EPOCH_MINUS_1970	(20 * 365 + 5 - 1)	/* 20 years, 5 leaps, back 1 day to Jan 0 */
#define	EPSILONg		279.403303		/* solar ecliptic long at EPOCH */
#define	RHOg			282.768422		/* solar ecliptic long of perigee at EPOCH */
#define	ECCEN			0.01671022		/* solar orbit eccentricity */
#define	lzero			318.351648		/* lunar mean long at EPOCH */
#define	Pzero			36.340410		/* lunar mean long of perigee at EPOCH */
#define	Nzero			318.510107		/* lunar mean long of node at EPOCH */

#ifndef PI
#define	PI	  3.14159265358979323846
#endif	/* ifndef PI */

double dtor(double deg) {

    return (deg * PI / 180);
}

void adj360(double *deg) {

    for (;;)
	if (*deg < 0)
	    *deg += 360;
	else if (*deg > 360)
	    *deg -= 360;
	else
	    break;
}

float getMoon(float days) {
    double N, Msol, Ec, LambdaSol, l, Mm, Ev, Ac, A3, Mmprime;
    double A4, lprime, V, ldprime, D, Nm;

    N = 360 * days / 365.242191;			/* sec 46 #3 */
    adj360(&N);
    Msol = N + EPSILONg - RHOg;				/* sec 46 #4 */
    adj360(&Msol);
    Msol = dtor(Msol);
    Ec = 360 / PI * ECCEN * sin(Msol);			/* sec 46 #5 */
    LambdaSol = N + Ec + EPSILONg;			/* sec 46 #6 */
    adj360(&LambdaSol);
    l = 13.1763966 * days + lzero;			/* sec 65 #4 */
    adj360(&l);
    Mm = l - (0.1114041 * days) - Pzero;		/* sec 65 #5 */
    adj360(&Mm);
    Nm = Nzero - (0.0529539 * days);			/* sec 65 #6 */
    adj360(&Nm);
    Ev = 1.2739 * sin(dtor(2*(l - LambdaSol) - Mm));	/* sec 65 #7 */
    Ac = 0.1858 * sin(Msol);				/* sec 65 #8 */
    A3 = 0.37 * sin(Msol);
    Mmprime = Mm + Ev - Ac - A3;			/* sec 65 #9 */
    Ec = 6.2886 * sin(dtor(Mmprime));			/* sec 65 #10 */
    A4 = 0.214 * sin(dtor(2 * Mmprime));		/* sec 65 #11 */
    lprime = l + Ev + Ec - Ac + A4;			/* sec 65 #12 */
    V = 0.6583 * sin(dtor(2 * (lprime - LambdaSol)));	/* sec 65 #13 */
    ldprime = lprime + V;				/* sec 65 #14 */
    D = ldprime - LambdaSol;				/* sec 67 #2 */
    return (50 * (1 - cos(dtor(D))));
}

void getNext(time_t timenow, int next) {
    float days, now;

    days = (timenow - EPOCH_MINUS_1970 * 86400) / 86400.0;
    days += (0.1 * next);
    for (;;) {
	now = getMoon(days);
	/* The rate of change at 1st and 3rd quarters is fast
	 * enough that this range results in an accuracy of about
	 * plus or minus 1/2 - 1 minute.  At New and Full it's
	 * about plus or minus 2 - 3 minutes.  It produces results
	 * that are pretty close to "NASA's official moon phases page."
	 * at http://eclipse.gsfc.nasa.gov/phase/phase2001pst.html
	 * which has "Phases of the Moon: 2001 to 2025".
	 */
	if (now < 0.000005 || now > 99.999995 || (now > 49.99 && now < 50.01))
	    break;
	days += (0.0005 * next);
    }
    g.phasetime = (time_t)(days * 86400.0) + (EPOCH_MINUS_1970 * 86400);
}

void getPhase(time_t timenow, char *buffer) {
    float days, now, then;
    int i;

    days = (timenow - EPOCH_MINUS_1970 * 86400) / 86400.0;
    now = getMoon(days);
    fresh.phase = now;
    then = getMoon(days + 0.05);		/* about an hour and twelve minutes ahead */
    i = (now < then) ? ASCEN : DSCEN;
    if (g.moongraphic) {
	/* Return ascending or descending and percent full. */
	snprintf(buffer, SHOWBUF, "%02d %f", i, now);
    } else {
	/* Display quarters from a few hours before to a few hours after. */
	if (now <= 0.05) {
	    snprintf(buffer, SHOWBUF, "|New|Moon|");
	} else if (now >= 99.95) {
	    snprintf(buffer, SHOWBUF, "|Full|Moon|");
	} else if (now > 47.3 && now < 52.7) {
	    (i == ASCEN) ? snprintf(buffer, SHOWBUF, "1st|Quarter") : snprintf(buffer, SHOWBUF, "3rd|Quarter");
	} else {
	    snprintf(buffer, SHOWBUF, "Moon| |  . %%");
	    buffer[5] = i;
	    i = rint(now * 10);
	    buffer[10] = (i % 10) + AZERO;	/* AZERO is the offset of the digit into forexpm */
	    i /= 10;
	    buffer[8] = (i % 10) + AZERO;
	    i /= 10;
	    buffer[7] = i ? (i % 10) + AZERO : SPACE;
	}
    }
}

/* This code is derived from Net/OpenBSD code which is in turn derived
 * from public domain code that was written by Mike Muuss for the
 * U. S. Army Ballistic Research Laboratory in December, 1983.
 * Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
 * will be added on by the kernel.  The ID field is our UNIX process ID.
 */
#define DEFDATALEN 56
#define MAXIPLEN 60
#define MAXICMPLEN 76

int ping(struct lineopts *line) {
    struct hostent *hostent=NULL;
    struct icmp *icmp;
    struct sockaddr_in *to, whereto;
    struct pingsent *p, *pnew;
    time_t timenow=time(NULL);
    int nleft, cc, result=0, sum=0, pingcount, ttw;
    u_char outpackhdr[IP_MAXPACKET];
    u_char *outpack=outpackhdr + sizeof(struct ip);
    unsigned int datalen=DEFDATALEN;
    u_char *packet=outpack;
    u_short answer=0, *w;
    char showbuf[SHOWBUF];
    
    if (!ident)
	ident = getpid() & 0xFFFF;
    ttw = parseStrtol(line->status, TRUE, "ping");
    if ((pnew = calloc(1, sizeof(struct pingsent)))) {
	hostent = gethostbyname(line->argument);
	bzero(&whereto, sizeof(whereto));
	to = &whereto;
#if !defined(__linux__)
	to->sin_len = sizeof(struct sockaddr_in);
#endif	/* !(__linux__) */
	to->sin_family = hostent->h_addrtype;
	memcpy(&to->sin_addr, hostent->h_addr, (size_t)hostent->h_length);
	icmp = (struct icmp *)outpack;
	icmp->icmp_type = ICMP_ECHO;
	icmp->icmp_code = 0;
	icmp->icmp_cksum = 0;
	icmp->icmp_seq = htons(sequence);
	icmp->icmp_id = ident;
	cc = datalen + 8;
	w = (u_short *)icmp;
	nleft = cc;
	while (nleft > 1) {
	    sum += *w++;
	    nleft -= 2;
	}
	if (nleft == 1) {
	    *(u_char *)(&answer) = *(u_char *)w;
	    sum += answer;
	}
	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
	sum += (sum >> 16);			/* add carry */
	icmp->icmp_cksum = ~sum;		/* truncate to 16 bits */
	pnew->line = line;
	pnew->expire = timenow + ttw;
	pnew->sequence = sequence;
	pnew->address = (int)to->sin_addr.s_addr;
	sequence = (sequence + 1) % IP_MAXPACKET;
	if (!pings) {
	    /* This is a new pings stack. */
	    pings = pnew;
	} else {
	    /* Add to the end. */
	    p = pings;
	    while (p->next)
		p = p->next;
	    p->next = pnew;
	    pnew->prev = p;
	}
	pingcount = parseStrtol(line->value, TRUE, "ping");
	snprintf(showbuf, sizeof(showbuf), "%d", ++pingcount);
	STRDUP(line->value, showbuf);
	result = sendto(g.pingsock, packet, cc, 0,
			(struct sockaddr *)&whereto, sizeof(whereto));
    }
    return result;
}

int checkEcho() {
    struct sockaddr_in from;
    struct icmp *icmp;
    struct ip *ip;
    struct pingsent *p, *pnext;
    struct timeval tv;
    fd_set ready;
#if defined(__OpenBSD__)
    socklen_t fromlen, packlen=(DEFDATALEN + MAXIPLEN + MAXICMPLEN);
#else
    size_t fromlen, packlen=(DEFDATALEN + MAXIPLEN + MAXICMPLEN);
#endif	/* __OpenBSD__ */
    time_t timenow=time(NULL);
    int i, j, seq, address, result=0, pingcount;
    char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];

    if (g.pingsock > 0) {
	fromlen = sizeof(from);
	bzero(&tv, sizeof(tv));
	for (;;) {
	    FD_ZERO(&ready);
	    FD_SET(g.pingsock, &ready);
	    select(g.pingsock + 1, &ready, 0, 0, &tv);
	    if (FD_ISSET(g.pingsock, &ready)) {
		if ((i = recvfrom(g.pingsock, packet, packlen, MSG_DONTWAIT,
			(struct sockaddr *)&from, &fromlen)) > 0) {
		    ip = (struct ip *)packet;
		    j = ip->ip_hl << 2;
		    if (i >= j + ICMP_MINLEN) {
			icmp = (struct icmp *)(packet + j);
			if (icmp->icmp_type == ICMP_ECHOREPLY && icmp->icmp_id == ident) {
			    seq = ntohs(icmp->icmp_seq);
			    address = (int)from.sin_addr.s_addr;
			    for (p = pings; p; p = p->next) {
				if (p->sequence == seq && p->address == address) {
				    /* Host has responded, mark host as accessible
				     * and mark this pingsent as expired.
				     */
				    p->expire = 0;
				    result = 1;
				    break;
				}
			    }
			}
		    }
		}
		if (result)
		    break;
	    } else {
		break;
	    }
	}
	p = pings;
	while (p) {
	    pnext = p->next;
	    if (p->expire < timenow) {
		pingcount = parseStrtol(p->line->value, TRUE, "checkEcho");
		pingcount -= (pingcount > 0);
		snprintf(packet, sizeof(packet), "%d", pingcount);
		STRDUP(p->line->value, packet);
		if (p == pings)
		    pings = p->next;
		if (p->next)
		    p->next->prev = p->prev;
		if (p->prev)
		    p->prev->next = p->next;
		FREE(p);
	    }
	    p = pnext;
	}
    }
    return result;
}

int openSocket(char *host, int port) {
    struct addrinfo hints, *ai, *res=NULL;
    int opensock=-1;

    bzero(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_CANONNAME | AI_NUMERICSERV;
    snprintf(buffer, sizeof(buffer), "%d", port);
    if (!getaddrinfo(host, buffer, &hints, &res)) {
	for (ai = res; ai; ai = ai->ai_next) {
	    opensock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
	    if (opensock == -1)
		continue;
	    if (connect(opensock, ai->ai_addr, ai->ai_addrlen)) {
		close(opensock);
		opensock = -1;
		continue;
	    }
	    break;
	}
	freeaddrinfo(res);
    }
    return opensock;
}

#if WITH_TIMESYNC
#if defined(__linux__)
#include <sys/time.h>
#endif	/* (__linux__) */

const char *request="syncreq";

void deadTime() {

    shutdown(g.timesock, SHUT_RDWR);
    close(g.timesock);
    g.timesock = -1;
}

int getTimeDiff(struct timeval *res, struct timeval *serv, struct timeval *locl) {
    float servtm, locltm, dtime=0.0;
    int diff=0;

    /* First see if the diff is > one millisecond. */
    servtm = serv->tv_sec + (float)serv->tv_usec / 1000000;
    locltm = locl->tv_sec + (float)locl->tv_usec / 1000000;
    if (servtm > locltm)
	dtime = servtm - locltm;
    else if (locltm > servtm)
	dtime = locltm - servtm;
    if (dtime > 0.001) {
	res->tv_sec = serv->tv_sec - locl->tv_sec;
	res->tv_usec = serv->tv_usec - locl->tv_usec;
	if (res->tv_usec >= 1000000) {
	    res->tv_sec++;
	    res->tv_usec -= 1000000;
	}
	if (res->tv_usec < 0) {
	    res->tv_sec--;
	    res->tv_usec += 1000000;
	}
	diff = 1;
    }
    return diff;
}

/* Only used by client machines.
 * Waits for reply from server.
 * Must be run by super user.
 */
void setClock() {
    struct timeval servtime, adjust;
    struct timespec timeset;
    struct tm *timeptr=NULL;
    time_t timenow;
    int ssec=0, susec=0;
    fd_set ready;

    bzero(&adjust, sizeof(adjust));
    adjtime(&adjust, NULL);
    for (;;) {
	if ((g.timesock = openSocket(g.timehost, g.timeport)) > 0) {
	    signal(SIGPIPE, deadTime);
	    FD_ZERO(&ready);
	    FD_SET(g.timesock, &ready);
	    select(g.timesock + 1, 0, &ready, 0, &adjust);
	    if (FD_ISSET(g.timesock, &ready)) {
		bzero(&buffer, sizeof(buffer));
		write(g.timesock, request, strlen(request));
		recv(g.timesock, buffer, sizeof(buffer), 0);
		buffer[sizeof(buffer) - 1] = '\0';
		if (sscanf(buffer, "%d %d", &ssec, &susec) == 2) {
		    servtime.tv_sec = ssec;
		    servtime.tv_usec = susec;
		    timenow = time(NULL);
		    timeptr = gmtime(&timenow);
		    strftime(buffer, sizeof(buffer), "system wall clock %m/%d %H:%M:%S UTC", timeptr);
		    logMessage(buffer);
		    timeset.tv_sec = servtime.tv_sec;
		    timeset.tv_nsec = servtime.tv_usec * 1000;
		    if (!clock_settime(CLOCK_REALTIME, ×et)) {
			timenow = time(NULL);
			timeptr = gmtime(&timenow);
			strftime(buffer, sizeof(buffer), " reset wall clock %m/%d %H:%M:%S UTC", timeptr);
		    }
		    logMessage(buffer);
		}
	    }
	    shutdown(g.timesock, SHUT_RDWR);
	    close(g.timesock);
	    send(g.timesock, buffer, strlen(buffer), 0);
	    break;
	}
	sleep(1);
    }
}

/* Testing with timedc shows time diffs of < +-10
 * ms after sync.  The usual diff is <= 1 ms, the
 * resolution of timedc.  checkSync() is only
 * called by the client machines and only if
 * run by the super user.
 */
void checkSync() {
    static time_t lasttime=0;
    time_t timenow=time(NULL);
    struct timeval locltime, servtime, delta, adjust;
    int ssec=0, susec=0;
    fd_set ready;

    if (timenow >= lasttime + g.timepoll) {
	if (!g.timepoll)
	    g.timepoll = 60;
	if ((g.timesock = openSocket(g.timehost, g.timeport)) > 0) {
	    signal(SIGPIPE, deadTime);
	    FD_ZERO(&ready);
	    FD_SET(g.timesock, &ready);
	    bzero(&adjust, sizeof(adjust));
	    select(g.timesock + 1, 0, &ready, 0, &adjust);
	    if (FD_ISSET(g.timesock, &ready)) {
		adjtime(NULL, &adjust);
		if (adjust.tv_sec == 0 && adjust.tv_usec == 0) {
		    write(g.timesock, request, strlen(request));
		    bzero(&buffer, sizeof(buffer));
		    recv(g.timesock, buffer, sizeof(buffer), 0);
		    buffer[sizeof(buffer) - 1] = '\0';
		    if (sscanf(buffer, "%d %d", &ssec, &susec) == 2) {
			servtime.tv_sec = ssec;
			servtime.tv_usec = susec;
			gettimeofday(&locltime, NULL);
			if (getTimeDiff(&delta, &servtime, &locltime)) {
			    adjtime(&delta, NULL);
			    if (g.timelog) {
				snprintf(buffer, sizeof(buffer), "adjust time %d sec %06ld usec",
					(int)delta.tv_sec, delta.tv_usec);
				logMessage(buffer);
			    }
			}
		    }
		}
	    }
	    shutdown(g.timesock, SHUT_RDWR);
	    close(g.timesock);
	    send(g.timesock, buffer, strlen(buffer), 0);
	}
	lasttime = timenow;
    }
}
#endif	/* WITH_TIMESYNC */

#if WITH_CONTROL
void deadCtl() {

    shutdown(g.ctlsock, SHUT_RDWR);
    close(g.ctlsock);
    g.ctlsock = -1;
}

/* Allocate and fill an array of the options/arguments
 * used at startup or as reset using samcon.  cmdargs only
 * lists options set by the user, aargs includes options
 * automatically set as defaults.
 */
char **listStruct() {
    int i=0, j=1, count=1, maxlen=0;
    char **aargs;

    for (;; i++) {
	if (!findName(i))
	    break;
	if (switchOpt(i, NULL, 1, FALSE)) {
	    if (!strcmp(switchOpt(i, NULL, 1, FALSE), "0")) {
		continue;
	    }
	    if (strlen(switchOpt(i, NULL, 1, FALSE)) >= maxlen)
		maxlen = strlen(findName(i)) + strlen(switchOpt(i, NULL, 1, FALSE)) + 4;
	    count++;
	}
    }
    for (i = 0;; i++) {
	if (!cmdargs[i])
	    break;
	if (strlen(cmdargs[i]) >= maxlen)
	    maxlen = strlen(cmdargs[i]) + 1;
    }
    if (!(aargs = (char **)calloc(count * maxlen, sizeof(char))))
	allocFailed("checkCtl");
    aargs[0] = (char *)calloc(maxlen, sizeof(char));
    snprintf(aargs[0], maxlen, "%s", cmdargs[0]);
    for (i = 0;; i++) {
	if (!findName(i))
	    break;
	if (switchOpt(i, NULL, 1, FALSE)) {
	    if (!strcmp(switchOpt(i, NULL, 1, FALSE), "0")) {
		continue;
	    }
	    aargs[j] = (char *)calloc(maxlen, sizeof(char));
	    snprintf(aargs[j++], maxlen,
#if defined (__NetBSD__)
		    "--%s=%s",
#else
		    "-%s=%s",
#endif
		    findName(i), switchOpt(i, NULL, 1, FALSE));
	}
    }
    for (i = 0;; i++) {
	if (!cmdargs[i])
	    break;
	if (strstr(cmdargs[i], "line=")) {
	    aargs[j] = (char *)calloc(maxlen, sizeof(char));
	    snprintf(aargs[j++], maxlen, "%s", cmdargs[i]);
	}
    }
    return aargs;
}

/* Write a single or multi line list of the
 * options/arguments.  If this is called for
 * all current connections it will be a single
 * line, else it will be multi line to the
 * size of buffer.
 */
void listArgs(char **args, int full) {
    int i=0;

    if (full)
	stringcat(buffer, "\n  ", BUFPLUS);
    for (;; i++) {
	if (!args[i])
	    break;
	if (full && strstr(args[i], "line="))
	    continue;
	if (!full && (strlen(args[i]) + strlen(buffer)) > SMALBUF) {
	    stringcat(buffer, " ...", BUFPLUS);
	    break;
	}
	if (full && *args[i] == '-')
	    stringcat(buffer, "\n  ", BUFPLUS);
	else if (i)
	    stringcat(buffer, " ", BUFPLUS);
	if (strlen(buffer) + strlen(args[i]) > BUFPLUS) {
	    stringcat(buffer, "...", BUFPLUS);
	    break;
	}
	stringcat(buffer, args[i], BUFPLUS);
    }
    if (full) {
	for (i = 0;; i++) {
	    if (!args[i])
		break;
	    if (!strstr(args[i], "line="))
		continue;
	    if (*args[i] == '-')
		stringcat(buffer, "\n  ", BUFPLUS);
	    else if (i)
		stringcat(buffer, " ", BUFPLUS);
	    if (strlen(buffer) + strlen(args[i]) > BUFPLUS) {
		stringcat(buffer, "\t...", BUFPLUS);
		break;
	    }
	    stringcat(buffer, args[i], BUFPLUS);
	}
	stringcat(buffer, "\n", BUFPLUS);
    }
}

void checkCtl() {
    static time_t lasttime=0;
    time_t timenow=time(NULL);
    struct timeval wait;
    static int lasttype=0;
    static char *ppid=NULL, *cpid=NULL;
    char *ptr, *nval, **aargs;
    fd_set ready;

    if (timenow >= lasttime + g.refresh) {
	if (!lasttype) {
	    signal(SIGPIPE, deadCtl);
	    if (!cmdargs)
		cmdargs = cmdline;
	    snprintf(smalbuf, SMALBUF, "%05d", getpid());
	    STRDUP(ppid, smalbuf);
	    lasttype = findIndex("talker");
	}
	if (g.ctlsock > 0) {
	    FD_ZERO(&ready);
	    FD_SET(g.ctlsock, &ready);
	    bzero(&wait, sizeof(wait));
	    select(g.ctlsock + 1, &ready, 0, 0, &wait);
	    if (FD_ISSET(g.ctlsock, &ready)) {
		bzero(&smalbuf, SMALBUF);
		recv(g.ctlsock, smalbuf, SMALBUF, 0);
		smalbuf[SMALBUF - 1] = '\0';
sendargv:
		snprintf(buffer, sizeof(buffer), "%s ", ppid);
		if (cpid)
		    stringcat(buffer, cpid, sizeof(buffer));
		if (!strcmp(smalbuf, "argv")) {
		    listArgs(cmdline, FALSE);
		} else if (!strcmp(smalbuf, "args")) {
		    listArgs(cmdargs, FALSE);
		} else if (!strncmp(smalbuf, ppid, 5)) {
		    if ((ptr = strchr(smalbuf, ' '))) {
			while (*ptr == ' ')
			    ptr++;
			if (!strcmp(ptr, "stop")) {
			    shutdown(g.ctlsock, SHUT_RDWR);
			    close(g.ctlsock);
			    gdayMate(NULL, NULL);
			} else if (!strcmp(ptr, "close")) {
			    goto closeit;
			} else if (g.cpid > 0 && !strcmp(ptr, "kill")) {
			    snprintf(smalbuf, SMALBUF, "kill %d", g.cpid);
			    execComm(smalbuf);	/* uses buffer */
			} else if (!strncmp(ptr, "argv", 4)) {
			    listArgs(cmdline, TRUE);
			} else if (!strncmp(ptr, "args", 4)) {
			    listArgs(cmdargs, TRUE);
			} else if (!strncmp(ptr, "aargs", 5)) {
			    aargs = listStruct();
			    listArgs(aargs, TRUE);
			    FREE(aargs);
			} else if (!strncmp(ptr, "set ", 4)) {
			    ptr += 4;
			    if ((nval = strchr(ptr, ' '))) {
				*nval++ = '\0';
				if (findIndex(ptr) > lasttype)
				    goto whatsisname;
				while (*nval == ' ')
				    nval++;
				if (strlen(nval)) {
				    switchOpt(findIndex(ptr), nval, 1, TRUE);
				    goto showval;
				} else {
				    goto valreq;
				}
			    } else if (hasArg(findIndex(ptr)) != 1) {
				switchOpt(findIndex(ptr), NULL, 1, TRUE);
				goto showval;
			    } else {
valreq:
				stringcat(buffer, "set ", sizeof(buffer));
				stringcat(buffer, ptr, sizeof(buffer));
				stringcat(buffer, ", value required", sizeof(buffer));
			    }
			} else if (!strncmp(ptr, "get ", 4)) {
			    ptr += 4;
			    if (strchr(ptr, ' ') || findIndex(ptr) > lasttype)
				goto whatsisname;
showval:
			    stringcat(buffer, ptr, sizeof(buffer));
			    snprintf(smalbuf, SMALBUF, " \"%s\"",
				    switchOpt(findIndex(ptr), NULL, 1, FALSE));
			    stringcat(buffer, smalbuf, sizeof(buffer));
			} else {
			    goto whatsisname;
			}
		    } else {
			listArgs(cmdargs, TRUE);
		    }
		} else {
whatsisname:
		    stringcat(buffer, "invalid command", sizeof(buffer));
		}
		send(g.ctlsock, buffer, strlen(buffer), 0);
	    }
	} else if (!g.daemonize || getpid() == g.ppid) {
	    if ((g.ctlsock = openSocket(g.hostname, g.ctlport)) > 0) {
		if (g.daemonize) {
		    bzero(&wait, sizeof(wait));
		    wait.tv_sec = 5;
		    setsockopt(g.ctlsock, SOL_SOCKET, SO_RCVTIMEO, &wait, sizeof(wait));
		    snprintf(smalbuf, SMALBUF, "-> %05d ", g.cpid);
		    STRDUP(cpid, smalbuf);
		}
		bzero(&smalbuf, SMALBUF);
		recv(g.ctlsock, smalbuf, SMALBUF, 0);
		smalbuf[SMALBUF - 1] = '\0';
		if (strlen(smalbuf)) {
		    snprintf(buffer, sizeof(buffer), "%d", g.realuid);
		    if (!strcmp(buffer, smalbuf)) {
			snprintf(smalbuf, SMALBUF, "argv");
			goto sendargv;
		    }
		}
closeit:
		shutdown(g.ctlsock, SHUT_RDWR);
		close(g.ctlsock);
		g.ctlsock = -1;
		FREE(cpid);
	    }
	}
	lasttime = timenow;
    }
}
#endif	/* WITH_CONTROL */
* * o * *