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