/* 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 <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/un.h>
#include <sys/time.h>
#include <signal.h>
#include <netinet/in.h>
#include <sys/resource.h>
#if defined(__linux__)
#include <time.h>
#endif
#include "proto.h"
struct global g;
char **cmdline=NULL;
extern struct lineopts *linelist;
char smalbuf[SMALBUF];
void freeLine(struct lineopts *line) {
if (line->prev)
line->prev->next = line->next;
if (line->next)
line->next->prev = line->prev;
if (line == linelist)
linelist = line->next;
FREE(line->type);
FREE(line->argument);
FREE(line->command);
FREE(line->status);
FREE(line->value);
FREE(line->buffer);
FREE(line->setting);
FREE(line);
}
void freeLines() {
struct lineopts *line, *next;
if ((line = linelist)) {
while (line) {
next = line->next;
freeLine(line);
line = next;
}
linelist = NULL;
}
}
/* Set count as minus whatever to
* force a specific refresh rate.
*/
void setRefresh(int count) {
if (!g.refresh) {
if (g.ctlport) {
g.refresh = 1; /* so samcon can find it */
} else if (count == g.linecount) { /* all display lines */
/* Only call Redraw() on XEvent. */
g.refresh = (g.pixmap == 1) ? 10 : FALSE;
setpriority(PRIO_PROCESS, 0, 20);
} else if (count == -1) { /* calendar */
g.refresh = (g.pixmap == 1) ? 10 : 60;
setpriority(PRIO_PROCESS, 0, 10);
} else if (g.moongraphic) {
g.refresh = (g.pixmap == 1) ? 10 : 300;
setpriority(PRIO_PROCESS, 0, 10);
} else {
g.refresh = 1;
}
}
}
/* Set lines and panels, return if four panel calendar.
* Insure that the lines are sequential in each panel.
* Set up lines for phasecalc, one panel calendar and
* volume control.
*/
void initLines() {
struct lineopts *line, *nline;
int calendar=0, panel=0, linenum=0, color=0;
int i, j=1, k=1, curpan=0, p[]={1, 3, 2, 4};
char *utc="";
if (g.linecount == 1) { /* No lines were defined. */
parseLine("format=%l:%M|%p|%Z_linenum=1");
parseLine("load1_linenum=2");
parseLine("memfree_linenum=3");
parseLine("swapfree_linenum=4");
parseLine("processes_linenum=5");
parseLine("updays_linenum=6");
} else {
for (line = linelist; line; line = line->next) {
if (line->index == getIndex("volume")) {
/* Only allow one volume line. Don`t
* allow user to change mixer def.
*/
if (g.mixer) {
freeLine(line);
continue;
}
if (findExecutable(smalbuf, SMALBUF, MIXER)) {
STRDUP(g.mixer, smalbuf);
}
STRDUP(line->status, "connect");
for (i = 0; i < 2; i++) {
CALLOC(nline, 1, sizeof(struct lineopts), "initLines");
STRDUP(nline->type, line->type);
nline->index = line->index;
nline->color = line->color;
nline->panel = line->panel;
if (line->linenum)
nline->linenum = line->linenum + 1;
snprintf(smalbuf, SMALBUF, "%d", i);
STRDUP(nline->value, smalbuf);
STRDUP(nline->status, "volume");
nline->next = line->next;
nline->prev = line;
if (line->next)
line->next->prev = nline;
line->next = nline;
line = nline;
}
}
}
}
if (g.linecount > 25)
gdayMate("initLines() too many lines defined", NULL);
if (g.linecount > 7)
g.panel = 1;
bzero(&g.lines, sizeof(g.lines));
for (line = linelist; line; line = line->next) {
if (line->index == getIndex("calendar")) {
if (!calendar) {
panel = (line->panel + !line->panel);
linenum = (line->linenum + !line->linenum);
color = line->color;
if (g.calendar == 2)
utc = "UTC";
if (line->setting && !strcmp(line->setting, "4")) {
freeLines();
linenum = 1 - (linenum + ((panel - 1) * 6));
for (j = 0; j < 4; j++) {
g.lines[j][0] = 6;
for (i = 1; i < 7; i++, linenum++) {
snprintf(smalbuf, SMALBUF,
"calendar_panel=%d_linenum=%d_setting=%d",
p[j], i, linenum);
line = parseLine(smalbuf);
snprintf(smalbuf, SMALBUF, "%s%%a|%%b|%%d", utc);
STRDUP(line->argument, smalbuf);
if (color && !linenum)
line->color = color;
}
}
setRefresh(-1); /* Mark this as four panel calendar. */
return;
}
calendar = 1;
}
freeLine(line);
continue;
}
if ((i = line->panel)) {
g.lines[i - 1][0]++;
if (g.lines[i - 1][0] > 6)
goto nope;
} else {
line->panel = curpan + 1;
if (line->index == getIndex("volume")) {
g.lines[curpan][0] += 3;
if (g.lines[curpan][0] > 6) {
nope:
snprintf(smalbuf, SMALBUF, "initLines() too many lines in panel #%d",
line->panel);
gdayMate(smalbuf, NULL);
}
line = line->next;
line->panel = curpan + 1;
line = line->next;
line->panel = curpan + 1;
} else {
g.lines[curpan][0]++;
}
}
if (g.lines[curpan][0] == 6)
curpan++;
if (line->linenum) {
i = line->panel - 1;
j = line->linenum - 1;
k = 1 << j;
if (g.lines[i][1] & k) {
snprintf(smalbuf, SMALBUF,
"initLines() second line numbered %d in panel %d, type \"%s\"",
line->linenum, line->panel, line->type);
gdayMate(smalbuf, NULL);
}
g.lines[i][1] |= k;
} else {
j = line->panel - 1;
for (i = 0; i < 6; i++) {
if (!(g.lines[j][1] & (1 << i))) {
line->linenum = i + 1;
g.lines[j][1] |= (1 << i);
break;
}
}
}
}
for (i = 0; i < 4; i++) {
if (g.lines[i][1] != (1 << (g.lines[i][0] + 1)) - 1) {
j = 1;
for (line = linelist; line; line = line->next) {
if (line->panel == i + 1)
line->linenum = j++;
}
}
}
if (calendar) {
if (g.panel) {
panel = 0;
for (i = 0; i < 4; i++) {
if (!g.lines[i][0]) {
panel = i + 1;
break;
}
}
if (!panel)
goto getout;
} else {
panel = 1;
i = 0;
}
g.lines[i][0] += 6;
if (g.lines[i][0] > 6)
goto getout;
linenum = 1 - linenum;
for (i = 1; i < 7; i++, linenum++) {
snprintf(smalbuf, SMALBUF, "calendar_panel=%d_linenum=%d_setting=%d",
panel, i, linenum);
line = parseLine(smalbuf);
snprintf(smalbuf, SMALBUF, "%s%%a|%%b|%%d", utc);
STRDUP(line->argument, smalbuf);
if (!linenum)
line->color = color;
}
}
if (g.phasecalc) {
if (g.panel) {
panel = 0;
for (i = 0; i < 4; i++) {
if (!g.lines[i][0]) {
panel = i + 1;
g.lines[i][0] = 5;
break;
}
}
if (!panel) {
getout:
snprintf(smalbuf, SMALBUF, "initLines() the %s function requires"
" an empty panel in four panel mode", line->type);
gdayMate(smalbuf, NULL);
}
} else {
panel = 1;
g.lines[0][0] = 5;
}
if (g.phasecalc == 2) /* UTC */
utc = "UTC";
else
utc = "";
snprintf(smalbuf, SMALBUF, "setting=|Current|_linenum=1_panel=%d", panel);
line = parseLine(smalbuf);
STRDUP(line->status, "phasecalc");
snprintf(smalbuf, SMALBUF, "moonphase_linenum=2_panel=%d", panel);
line = parseLine(smalbuf);
STRDUP(line->status, "phasecalc");
snprintf(smalbuf, SMALBUF, "format=%s%%l:%%M|%%p|%%Z_linenum=3_panel=%d", utc, panel);
line = parseLine(smalbuf);
STRDUP(line->status, "phasecalc");
snprintf(smalbuf, SMALBUF, "format=%s|%%A|_linenum=4_panel=%d", utc, panel);
line = parseLine(smalbuf);
STRDUP(line->status, "phasecalc");
snprintf(smalbuf, SMALBUF, "format=%s|%%b|%%e,|%%Y|_linenum=5_panel=%d", utc, panel);
line = parseLine(smalbuf);
STRDUP(line->status, "phasecalc");
}
}
/* Set upper, lower, left and right event
* limits. Set line->type specific values.
*/
void spaceLines() {
int i, j, k=0, index, display=0;
struct lineopts *line;
char **args, *ptr=NULL;
for (i = 0; i <= (g.panel * 3); i++) {
j = 60 - (g.lines[i][0] * CHARHEIGHT); /* j=number of unused one pixel rows */
k = j / (g.lines[i][0] + 1); /* k=height of one blank line */
j = ((k + (k != 4)) * g.xmag) + g.psize * (i > 1); /* j=location of top info line */
k = (CHARHEIGHT + 1 + k) * g.xmag;
for (line = linelist; line; line = line->next) {
if (line->panel == i + 1) {
line->upper = j + ((line->linenum - 1) * k); /* used for clicks and by lineToDraw */
line->lower = line->upper + ((CHARHEIGHT + 1) * g.xmag);
line->left = (i % 2) * g.psize;
line->right = line->left + g.psize;
}
}
}
for (line = linelist; line; line = line->next) {
index = line->index;
if (index == getIndex("display")) {
display++;
if (!line->setting) {
STRDUP(line->setting, "|Display|");
}
} else if (index == getIndex("powerstate")) {
if (!line->warn || !line->crit)
gdayMate("spaceLines() warning and critical levels must"
" be set for powerstate", strerror(EINVAL));
if (line->warn < line->crit) {
snprintf(smalbuf, SMALBUF, "spaceLines() warning level %d"
" < critical level %d for powerstate",
line->warn, line->crit);
logMessage(smalbuf);
}
goto putcolors;
} else if (index == getIndex("thermal")) {
if (!line->warn || !line->crit) {
snprintf(smalbuf, SMALBUF,
"spaceLines() warning and critical levels must be"
" set for thermal zone #%s",
line->argument);
gdayMate(smalbuf, strerror(EINVAL));
}
if (line->warn > line->crit) {
snprintf(smalbuf, SMALBUF,
"spaceLines() warning level %d > critical level %d"
" for thermal zone #%s",
line->warn, line->crit, line->argument);
logMessage(smalbuf);
}
putcolors:
if (!line->color1)
line->color1 = pixAlloc("#00ff00");
if (!line->color2)
line->color2 = pixAlloc("#ffff00");
if (!line->color3)
line->color3 = pixAlloc("#ff0000");
STRDUP(line->status, "0");
} else if (index == getIndex("format") && line->setting) {
/* Set up talking clock times. */
if (!g.talker) {
if (findExecutable(smalbuf, SMALBUF, TALKER)) {
STRDUP(g.talker, smalbuf);
}
}
if (*line->setting == '~' || *line->setting == ':') {
snprintf(smalbuf, SMALBUF, line->setting);
args = readArgs(smalbuf, "return", 0);
if (!args || parseStrtol(args[0], TRUE, "spaceLines") != 2)
gdayMate("spaceLines() talking clock label", strerror(EINVAL));
ptr = args[1];
while (*ptr == '-')
ptr++;
STRDUP(line->setting, ptr);
FREE(args);
} else {
k = parseStrtol(line->setting, TRUE, "spaceLines");
if (k != 1 && k != 5 && k != 10 && k != 15 && k != 30 && k != 60)
gdayMate("spaceLines() talking clock interval", strerror(EINVAL));
}
} else if (index == getIndex("timer")) {
if (line->setting) {
STRDUP(line->buffer, line->setting);
snprintf(smalbuf, SMALBUF, "%d", (int)getDuration(line));
STRDUP(line->setting, smalbuf);
FREE(line->buffer);
}
if (line->value) {
STRDUP(line->buffer, line->value);
k = (int)getDuration(line);
snprintf(smalbuf, SMALBUF, "%d", k);
STRDUP(line->value, smalbuf);
snprintf(smalbuf, SMALBUF, "%d", (int)(k + time(NULL)));
STRDUP(line->buffer, smalbuf);
STRDUP(line->status, "running");
}
} else if (index == getIndex("alarm") || index == getIndex("reset")) {
if (line->setting) {
if (index == getIndex("reset")) {
if (sscanf(line->setting, "%d:%d", &i, &j) != 2)
goto badset;
k = (i * 3600) + (j * 60);
if (!k) {
badset:
snprintf(smalbuf, SMALBUF,
"spaceLines() resetting alarm interval [HH:MM] %s",
line->setting);
gdayMate(smalbuf, strerror(EINVAL));
}
} else {
STRDUP(line->buffer, line->setting);
k = (int)getExpiration(line);
}
snprintf(smalbuf, SMALBUF, "%d", k);
STRDUP(line->setting, smalbuf);
FREE(line->buffer);
}
if (line->value) {
STRDUP(line->buffer, line->value);
snprintf(smalbuf, SMALBUF, "%d", (int)getExpiration(line));
STRDUP(line->value, smalbuf);
STRDUP(line->buffer, smalbuf);
STRDUP(line->status, "running");
}
} else if (line->argument && index >= getIndex("user") &&
index <= getIndex("idle")) {
i = parseStrtol(line->argument, TRUE, "initLines");
if (!i) {
FREE(line->argument);
} else if (i > g.ncpu) {
snprintf(smalbuf, SMALBUF, "initLines() cpu \"%d\" for line=\"%s\""
" is greater than #cpu's = \"%d\"", i, line->type, g.ncpu);
gdayMate(smalbuf, NULL);
} else {
g.split = 1;
}
}
}
setRefresh(display);
}
#if WITH_REMEXEC || WITH_TIMESYNC
void bindPort(int *port, int *sock, const char *portname) {
struct sockaddr_in sockaddr;
struct timeval tv;
char *buffer=NULL;
int i, temp=0;
CALLOC(buffer, BUFSIZE, sizeof(char), "bindPort");
if ((*sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
snprintf(buffer, BUFSIZE, "bindPort() failed to open %s socket", portname);
gdayMate(buffer, strerror(errno));
}
tv.tv_sec = 5;
tv.tv_usec = 0;
setsockopt(*sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
setsockopt(*sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
bzero(&sockaddr, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(port[0]);
sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
temp = port[0];
if (temp < 0) {
/* No number given, try to find a usable port. */
srandom(time(NULL));
i = 0;
for (;; i++) {
if (i > MAXTRIES) {
snprintf(buffer, BUFSIZE,
"bindPort() exceeded maximum tries to find usable %s port in range %d - %d",
portname, LODYNAMIC, HIDYNAMIC);
gdayMate(buffer, strerror(EAGAIN));
}
temp = random();
if (temp >= LODYNAMIC && temp <= HIDYNAMIC) {
sockaddr.sin_port = htons(temp);
if (bind(*sock, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) == -1) {
close(*sock);
continue;
}
snprintf(buffer, BUFSIZE, "%s started on port %d", portname, temp);
logMessage(buffer);
port[0] = temp;
break;
}
}
} else if (bind(*sock, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) == -1) {
snprintf(buffer, BUFSIZE, "bindPort() failed to bind %s socket #%d on port %d",
portname, *sock, temp);
gdayMate(buffer, strerror(errno));
}
if (listen(*sock, 1) == -1) {
snprintf(buffer, BUFSIZE, "bindPort() failed to set listen on %s socket #%d",
portname, *sock);
gdayMate(buffer, strerror(errno));
}
if (fcntl(*sock, F_SETFL, O_NONBLOCK) == -1) {
snprintf(buffer, BUFSIZE, "bindPort() failed to set non-block on %s socket #%d",
portname, *sock);
gdayMate(buffer, strerror(errno));
}
FREE(buffer);
}
#endif /* WITH_REMEXEC || WITH_TIMESYNC */
int main(int argc, char **argv) {
struct itimerval itv;
#if WITH_STEPEXEC
struct sockaddr_un sockaddr;
#endif /* WITH_STEPEXEC */
bzero(&g, sizeof(struct global));
cmdline = argv;
if (!(g.effectiveuid = geteuid()))
g.pingsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
g.realuid = getuid();
seteuid(g.realuid);
parseArgs(argc, argv);
g.running = 1;
g.ppid = getpid();
#if WITH_REMEXEC
if (g.execport)
bindPort(&g.execport, &g.execsock, "remote execute");
#endif /* WITH_REMEXEC */
#if WITH_TIMESYNC
if (g.timeserver)
/* Only do this on the master server. */
bindPort(&g.timeport, &g.timesock, "time server");
else if (g.settime)
setClock();
#endif /* WITH_TIMESYNC */
if (g.daemonize) {
if (daemon(1, 0))
/* Detach from the controlling terminal.
* Close stdin, stdout and stderr.
*/
gdayMate("main() daemon() failed", strerror(errno));
/* The parent process now has a new PID. */
g.ppid = getpid();
if (g.ctlport) {
bzero(&itv, sizeof(itv));
itv.it_interval.tv_sec = itv.it_value.tv_sec = 1;
setitimer(ITIMER_REAL, &itv, NULL);
signal(SIGALRM, checkCtl);
}
do {
if ((g.cpid = fork()) == -1)
gdayMate("main() fork() failed", strerror(errno));
waitpid(g.cpid, NULL, 0);
} while (getpid() == g.ppid);
if (g.ctlport) {
bzero(&itv, sizeof(itv));
setitimer(ITIMER_REAL, &itv, NULL);
}
}
initLines();
getOSVals();
initWindow(argc, argv);
spaceLines();
if (!g.moongraphic)
initPixlist();
#if WITH_STEPEXEC
if (g.stepfile) {
if ((g.stepsock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
gdayMate("main() unable to create afterstep socket", strerror(errno));
bzero(&sockaddr, sizeof(sockaddr));
sockaddr.sun_family = AF_UNIX;
if (stringcpy(sockaddr.sun_path, g.stepfile, sizeof(sockaddr.sun_path)) < 0)
gdayMate("main() afterstep socket path too long", strerror(errno));
if (connect(g.stepsock, (struct sockaddr *)&sockaddr, sizeof(struct sockaddr_un)))
gdayMate("main() unable to connect to afterstep socket", strerror(errno));
/* Initialize the connection. */
execFunc(NULL);
}
#endif /* WITH_STEPEXEC */
for (;;) {
/* Update ten times per second. */
Update();
usleep(100000);
}
}