You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

321 lines
11 KiB
C

#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <X11/Xlib.h>
#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;
}