#include #include #include #include #include #include #include #include #include #include #define NILL INT_MIN #define LOCKFILE "/var/local/dwmblocks/dwmblocks.pid" #define LENGTH(X) (sizeof X / sizeof X[0]) #define DELIMITERLENGTH (sizeof delimiter) #define STATUSLENGTH (LENGTH(blocks) * (CMDOUTLENGTH + DELIMITERLENGTH) + 1) #include "config.h" _Static_assert(INTERVALs >= 0, "INTERVALs must be greater than or equal to 0"); _Static_assert(INTERVALn >= 0 && INTERVALn <= 999999999, "INTERVALn must be between 0 and 999999999"); static void buttonhandler(int sig, siginfo_t *info, void *ucontext); static void cleanup(void); static void setupsignals(void); static void sighandler(int sig, siginfo_t *si, void *ucontext); static void statusloop(void); static void termhandler(int sig); static void updateblock(Block *block, int sigval); static void updatestatus(void); static void writepid(void); static Block *dirtyblock; static Display *dpy; static sigset_t blocksigmask; void buttonhandler(int sig, siginfo_t *info, void *ucontext) { sig = info->si_value.sival_int >> 8; for (Block *block = blocks; block->pathu; block++) if (block->signal == sig) switch (fork()) { case -1: perror("buttonhandler - fork"); break; case 0: { char button[] = { '0' + (info->si_value.sival_int & 0xff), '\0' }; char *arg[] = { block->pathc, button, NULL }; setsid(); execv(arg[0], arg); perror("buttonhandler - child - execv"); _exit(127); } } } void cleanup(void) { unlink(LOCKFILE); XStoreName(dpy, DefaultRootWindow(dpy), ""); XCloseDisplay(dpy); } void setupsignals(void) { struct sigaction sa; /* populate blocksigmask and check validity of signals */ sigemptyset(&blocksigmask); sigaddset(&blocksigmask, SIGHUP); sigaddset(&blocksigmask, SIGINT); sigaddset(&blocksigmask, SIGTERM); for (Block *block = blocks; block->pathu; block++) { if (block->signal <= 0) continue; if (block->signal > SIGRTMAX - SIGRTMIN) { fprintf(stderr, "Error: SIGRTMIN + %d exceeds SIGRTMAX.\n", block->signal); unlink(LOCKFILE); XCloseDisplay(dpy); exit(2); } sigaddset(&blocksigmask, SIGRTMIN + block->signal); } /* setup signal handlers */ /* to handle HUP, INT and TERM */ sa.sa_flags = SA_RESTART; sigemptyset(&sa.sa_mask); sa.sa_handler = termhandler; sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); /* to ignore unused realtime signals */ // sa.sa_flags = SA_RESTART; // sigemptyset(&sa.sa_mask); sa.sa_handler = SIG_IGN; for (int i = SIGRTMIN + 1; i <= SIGRTMAX; i++) sigaction(i, &sa, NULL); /* to prevent forked children from becoming zombies */ sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; // sigemptyset(&sa.sa_mask); sa.sa_handler = SIG_IGN; sigaction(SIGCHLD, &sa, NULL); /* to handle signals generated by dwm on click events */ sa.sa_flags = SA_RESTART | SA_SIGINFO; // sigemptyset(&sa.sa_mask); sa.sa_sigaction = buttonhandler; sigaction(SIGRTMIN, &sa, NULL); /* to handle update signals for individual blocks */ sa.sa_flags = SA_NODEFER | SA_RESTART | SA_SIGINFO; sa.sa_mask = blocksigmask; sa.sa_sigaction = sighandler; for (Block *block = blocks; block->pathu; block++) if (block->signal > 0) sigaction(SIGRTMIN + block->signal, &sa, NULL); } void sighandler(int sig, siginfo_t *info, void *ucontext) { sig -= SIGRTMIN; for (Block *block = blocks; block->pathu; block++) if (block->signal == sig) updateblock(block, info->si_value.sival_int); updatestatus(); } void statusloop(void) { int i; struct timespec t; sigprocmask(SIG_BLOCK, &blocksigmask, NULL); for (Block *block = blocks; block->pathu; block++) if (block->interval >= 0) updateblock(block, NILL); for (i = 1; ; i++) { updatestatus(); sigprocmask(SIG_UNBLOCK, &blocksigmask, NULL); t.tv_sec = INTERVALs, t.tv_nsec = INTERVALn; while (nanosleep(&t, &t) == -1); sigprocmask(SIG_BLOCK, &blocksigmask, NULL); for (Block *block = blocks; block->pathu; block++) if (block->interval > 0 && i % block->interval == 0) updateblock(block, NILL); } } void termhandler(int sig) { cleanup(); exit(0); } void updateblock(Block *block, int sigval) { int fd[2]; if (pipe(fd) == -1) { perror("updateblock - pipe"); cleanup(); exit(1); } switch (fork()) { case -1: perror("updateblock - fork"); close(fd[0]); close(fd[1]); cleanup(); exit(1); case 0: close(fd[0]); if (fd[1] != STDOUT_FILENO) { if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) { perror("updateblock - child - dup2"); close(fd[1]); exit(1); } close(fd[1]); } if (sigval == NILL) { char *arg[] = { block->pathu, NULL }; execv(arg[0], arg); } else { char buf[12]; char *arg[] = { block->pathu, buf, NULL }; snprintf(buf, sizeof buf, "%d", sigval); execv(arg[0], arg); } perror("updateblock - child - execv"); _exit(127); default: { size_t trd = 0; ssize_t rd; close(fd[1]); do rd = read(fd[0], block->curtext + trd, CMDOUTLENGTH - trd); while (rd > 0 && (trd += rd) < CMDOUTLENGTH); if (rd == -1) { perror("updateblock - read"); close(fd[0]); cleanup(); exit(1); } close(fd[0]); block->curtext[trd] = '\0'; if (memcmp(block->curtext, block->prvtext, trd + 1) != 0) { memcpy(block->prvtext, block->curtext, trd + 1); if (!dirtyblock || block < dirtyblock) dirtyblock = block; } if (trd == 0) block->length = 0; else { if (block->curtext[trd - 1] == '\n') trd--; if (block->pathc) block->curtext[trd++] = block->signal; memcpy(block->curtext + trd, delimiter, DELIMITERLENGTH); block->length = trd + DELIMITERLENGTH; } } } } void updatestatus(void) { static char statustext[STATUSLENGTH]; char *s = statustext; Block *block; if (!dirtyblock) return; for (block = blocks; block < dirtyblock; block++) s += block->length; for (; block->pathu; block++) { memcpy(s, block->curtext, block->length); s += block->length; } s[s == statustext ? 0 : -DELIMITERLENGTH] = '\0'; dirtyblock = NULL; XStoreName(dpy, DefaultRootWindow(dpy), statustext); XSync(dpy, False); } void writepid(void) { int fd; struct flock fl; if ((fd = open(LOCKFILE, O_RDWR | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) { perror("writepid - open"); exit(1); } fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; if (fcntl(fd, F_SETLK, &fl) == -1) { if (errno == EACCES || errno == EAGAIN) { fputs("Error: another instance of dwmblocks is already running.\n", stderr); exit(2); } perror("writepid - fcntl"); close(fd); exit(1); } if (ftruncate(fd, 0) == -1) { perror("writepid - ftruncate"); close(fd); exit(1); } if (dprintf(fd, "%ld", (long)getpid()) < 0) { perror("writepid - dprintf"); close(fd); exit(1); } } int main(int argc, char *argv[]) { int xfd; writepid(); if (!(dpy = XOpenDisplay(NULL))) { fputs("Error: could not open display.\n", stderr); unlink(LOCKFILE); return 1; } xfd = ConnectionNumber(dpy); fcntl(xfd, F_SETFD, fcntl(xfd, F_GETFD) | FD_CLOEXEC); setupsignals(); statusloop(); cleanup(); return 0; }