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