#include #include #include #include #include #include #include #include #include #include #define CMDLENGTH 50 #define STTLENGTH 256 #define NILL INT_MIN #define LOCKFILE "/tmp/dwmblocks.pid" typedef struct { char *const pathu; char *const pathc; const int interval; const int signal; char cmdoutcur[CMDLENGTH]; char cmdoutprv[CMDLENGTH]; } Block; #include "blocks.h" static void buttonhandler(int sig, siginfo_t *info, void *ucontext); static void cleanup(); static void getcmd(Block *block, int sigval); static void setroot(); static void setupsignals(); static void sighandler(int sig, siginfo_t *si, void *ucontext); static void statusloop(); static void termhandler(int sig); static int updatestatus(); static void writepid(); static char statustext[STTLENGTH]; static char *delim; static size_t delimlength; 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 }; close(ConnectionNumber(dpy)); setsid(); execv(arg[0], arg); perror("buttonhandler - child - execv"); _exit(127); } } } void cleanup() { unlink(LOCKFILE); XStoreName(dpy, DefaultRootWindow(dpy), ""); XCloseDisplay(dpy); } void getcmd(Block *block, int sigval) { int fd[2]; if (pipe(fd) == -1) { perror("getcmd - pipe"); exit(1); } switch (fork()) { case -1: perror("getcmd - fork"); exit(1); case 0: close(ConnectionNumber(dpy)); close(fd[0]); if (fd[1] != STDOUT_FILENO) { if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) { perror("getcmd - child - dup2"); 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("getcmd - child - execv"); _exit(127); default: close(fd[1]); if (read(fd[0], block->cmdoutcur, CMDLENGTH) == -1) { perror("getcmd - read"); exit(1); } close(fd[0]); } } void setroot() { if (updatestatus()) { XStoreName(dpy, DefaultRootWindow(dpy), statustext); XSync(dpy, False); } } void setupsignals() { struct sigaction sa; /* populate blocksigmask */ sigemptyset(&blocksigmask); sigaddset(&blocksigmask, SIGHUP); sigaddset(&blocksigmask, SIGINT); sigaddset(&blocksigmask, SIGTERM); for (Block *block = blocks; block->pathu; block++) if (block->signal > 0) 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_DFL; 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.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) getcmd(block, info->si_value.sival_int); setroot(); } void statusloop() { int i; struct timespec t; /* first run */ sigprocmask(SIG_BLOCK, &blocksigmask, NULL); for (Block *block = blocks; block->pathu; block++) if (block->interval >= 0) getcmd(block, NILL); setroot(); sigprocmask(SIG_UNBLOCK, &blocksigmask, NULL); /* main loop */ for (i = 1;; t.tv_sec = INTERVALs, t.tv_nsec = INTERVALn, i += 1) { while (nanosleep(&t, &t) == -1) if (errno != EINTR) { perror("statusloop - nanosleep"); exit(1); } sigprocmask(SIG_BLOCK, &blocksigmask, NULL); for (Block *block = blocks; block->pathu; block++) if (block->interval > 0 && i % block->interval == 0) getcmd(block, NILL); setroot(); sigprocmask(SIG_UNBLOCK, &blocksigmask, NULL); } } void termhandler(int sig) { cleanup(); exit(0); } /* returns whether block outputs have changed and updates statustext if they have */ int updatestatus() { char *s = statustext; char *c, *p; /* for cmdoutcur and cmdoutprv */ const char *d; /* for delimiter */ Block *block = blocks; /* checking half of the function */ /* find the first non-empty block */ for (;; block++) { /* all blocks are empty */ if (!block->pathu) return 0; /* contents of the block changed */ if (*block->cmdoutcur != *block->cmdoutprv) goto update0; /* skip delimiter handler for the first non-empty block */ if (*block->cmdoutcur != '\n' && *block->cmdoutcur != '\0') goto skipdelimc; } /* main loop */ for (; block->pathu; block++) { /* contents of the block changed */ if (*block->cmdoutcur != *block->cmdoutprv) goto update1; /* delimiter handler */ if (*block->cmdoutcur != '\n' && *block->cmdoutcur != '\0') s += delimlength; /* skip over empty blocks */ else continue; skipdelimc: /* checking for the first byte has been done */ c = block->cmdoutcur + 1, p = block->cmdoutprv + 1; for (; *c != '\n' && *c != '\0'; c++, p++) /* contents of the block changed */ if (*c != *p) { s += c - block->cmdoutcur; goto update2; } s += c - block->cmdoutcur; /* byte containing info about signal number for the block */ if (block->pathc && block->signal) s++; } return 0; /* updating half of the function */ /* find the first non-empty block */ for (;; block++) { /* all blocks are empty */ if (!block->pathu) return 1; update0: /* don't add delimiter before the first non-empty block */ if (*block->cmdoutcur != '\n' && *block->cmdoutcur != '\0') goto skipdelimu; *block->cmdoutprv = *block->cmdoutcur; } /* main loop */ for (; block->pathu; block++) { update1: /* delimiter handler */ if (*block->cmdoutcur != '\n' && *block->cmdoutcur != '\0') { d = delim; while (*d != '\0') *(s++) = *(d++); *(s++) = DELIMITERENDCHAR; /* skip over empty blocks */ } else { *block->cmdoutprv = *block->cmdoutcur; continue; } skipdelimu: c = block->cmdoutcur, p = block->cmdoutprv; update2: do { *(s++) = *c; *p = *c; c++, p++; } while (*c != '\n' && *c != '\0'); if (block->pathc && block->signal) *(s++) = block->signal; } *s = '\0'; return 1; } void writepid() { int fd; struct flock fl; fd = open(LOCKFILE, O_RDWR|O_CREAT, 0644); if (fd == -1) { perror("writepid - open"); exit(1); } fl.l_type = F_WRLCK; fl.l_start = 0; fl.l_whence = SEEK_SET; 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"); exit(1); } if (ftruncate(fd, 0) == -1) { perror("writepid - ftruncate"); exit(1); } if (dprintf(fd, "%ld", (long)getpid()) < 0) { perror("writepid - dprintf"); exit(1); } } int main(int argc, char *argv[]) { writepid(); if (argc == 3 && strcmp(argv[1], "-d") == 0) { delim = argv[2]; delimlength = strlen(delim) + 1; } else { delim = DELIMITER; delimlength = sizeof DELIMITER; } if (!(dpy = XOpenDisplay(NULL))) { fputs("Error: could not open display.\n", stderr); return 1; } setupsignals(); statusloop(); cleanup(); return 0; }