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