2020-07-06 21:23:31 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2020-07-09 20:10:34 +00:00
|
|
|
#include <limits.h>
|
2020-07-06 21:23:31 +00:00
|
|
|
#include <signal.h>
|
2020-07-03 21:04:11 +00:00
|
|
|
#include <stdio.h>
|
2020-07-06 21:23:31 +00:00
|
|
|
#include <stdlib.h>
|
2020-07-03 21:04:11 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
|
2020-07-06 21:23:31 +00:00
|
|
|
#define CMDLENGTH 25
|
|
|
|
#define STTLENGTH 256
|
2020-07-03 21:04:11 +00:00
|
|
|
#define LOCKFILE "/tmp/dwmblocks.pid"
|
2020-07-09 20:10:34 +00:00
|
|
|
#define NILL INT_MIN
|
2020-07-03 21:04:11 +00:00
|
|
|
|
|
|
|
#define EMPTYCMDOUT(block) (block->cmdoutcur[0] == '\n' || block->cmdoutcur[0] == '\0')
|
|
|
|
#define NOTATCMDOUTEND(block, i) (i < CMDLENGTH && block->cmdoutcur[i] != '\n' && block->cmdoutcur[i] != '\0')
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
char *pathu;
|
|
|
|
char *pathc;
|
|
|
|
const int interval;
|
|
|
|
const int signal;
|
|
|
|
char cmdoutcur[CMDLENGTH];
|
|
|
|
char cmdoutprv[CMDLENGTH];
|
|
|
|
} Block;
|
|
|
|
|
2020-07-07 10:17:13 +00:00
|
|
|
#include "blocks.h"
|
|
|
|
|
2020-07-03 21:04:11 +00:00
|
|
|
static void buttonhandler(int signal, siginfo_t *si, void *ucontext);
|
2020-07-09 20:10:34 +00:00
|
|
|
static void getcmd(Block *block, int sigval);
|
2020-07-03 21:04:11 +00:00
|
|
|
static void setroot();
|
|
|
|
static void setupsignals();
|
2020-07-06 20:28:41 +00:00
|
|
|
static void sighandler(int signal, siginfo_t *si, void *ucontext);
|
2020-07-03 21:04:11 +00:00
|
|
|
static void statusloop();
|
|
|
|
static void termhandler(int signum);
|
|
|
|
static int updatestatus();
|
|
|
|
static void writepid();
|
|
|
|
|
|
|
|
static int statusContinue = 1;
|
|
|
|
static char statusstr[STTLENGTH];
|
|
|
|
static size_t delimlength;
|
|
|
|
static Display *dpy;
|
|
|
|
|
|
|
|
void
|
|
|
|
buttonhandler(int signal, siginfo_t *si, void *ucontext)
|
|
|
|
{
|
|
|
|
signal = si->si_value.sival_int >> 8;
|
|
|
|
switch (fork()) {
|
|
|
|
case -1:
|
|
|
|
perror("buttonhandler - fork");
|
|
|
|
exit(1);
|
|
|
|
case 0:
|
2020-07-06 20:01:38 +00:00
|
|
|
close(ConnectionNumber(dpy));
|
2020-07-03 21:04:11 +00:00
|
|
|
for (Block *current = blocks; current->pathu; current++) {
|
|
|
|
if (current->signal == signal) {
|
|
|
|
char button[] = { '0' + (si->si_value.sival_int & 0xff), '\0' };
|
|
|
|
char *arg[] = { current->pathc, button, NULL };
|
|
|
|
|
|
|
|
setsid();
|
|
|
|
execv(arg[0], arg);
|
2020-07-08 17:25:19 +00:00
|
|
|
perror("buttonhandler - child - execv");
|
2020-07-03 21:04:11 +00:00
|
|
|
_exit(127);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-07-09 20:10:34 +00:00
|
|
|
getcmd(Block *block, int sigval)
|
2020-07-03 21:04:11 +00:00
|
|
|
{
|
|
|
|
int fd[2];
|
|
|
|
|
|
|
|
if (pipe(fd) == -1) {
|
|
|
|
perror("getcmd - pipe");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
switch (fork()) {
|
|
|
|
case -1:
|
|
|
|
perror("getcmd - fork");
|
|
|
|
exit(1);
|
|
|
|
case 0:
|
2020-07-06 20:01:38 +00:00
|
|
|
close(ConnectionNumber(dpy));
|
2020-07-03 21:04:11 +00:00
|
|
|
close(fd[0]);
|
|
|
|
if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
|
2020-07-08 17:25:19 +00:00
|
|
|
perror("getcmd - child - dup2");
|
2020-07-03 21:04:11 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
close(fd[1]);
|
2020-07-09 20:10:34 +00:00
|
|
|
if (sigval == NILL) {
|
|
|
|
char *arg[] = { block->pathu, NULL };
|
2020-07-03 21:04:11 +00:00
|
|
|
|
|
|
|
execv(arg[0], arg);
|
|
|
|
} else {
|
2020-07-09 20:10:34 +00:00
|
|
|
char buf[12];
|
|
|
|
char *arg[] = { block->pathu, buf, NULL };
|
2020-07-03 21:04:11 +00:00
|
|
|
|
2020-07-09 20:10:34 +00:00
|
|
|
snprintf(buf, sizeof buf, "%d", sigval);
|
2020-07-03 21:04:11 +00:00
|
|
|
execv(arg[0], arg);
|
|
|
|
}
|
2020-07-08 17:25:19 +00:00
|
|
|
perror("getcmd - child - execv");
|
2020-07-03 21:04:11 +00:00
|
|
|
_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()) /* only set root if block outputs have changed */
|
|
|
|
return;
|
2020-07-06 20:28:41 +00:00
|
|
|
XStoreName(dpy, DefaultRootWindow(dpy), statusstr);
|
2020-07-03 21:04:11 +00:00
|
|
|
XFlush(dpy);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
setupsignals()
|
|
|
|
{
|
|
|
|
struct sigaction sa;
|
|
|
|
|
|
|
|
sigemptyset(&sa.sa_mask);
|
|
|
|
sa.sa_flags = SA_RESTART;
|
|
|
|
/* to handle INT, HUP and TERM */
|
|
|
|
sa.sa_handler = termhandler;
|
|
|
|
sigaction(SIGINT, &sa, NULL);
|
|
|
|
sigaction(SIGHUP, &sa, NULL);
|
|
|
|
sigaction(SIGTERM, &sa, NULL);
|
|
|
|
/* to ignore unused realtime signals */
|
|
|
|
sa.sa_handler = SIG_IGN;
|
|
|
|
for (int i = SIGRTMIN; i <= SIGRTMAX; i++)
|
|
|
|
sigaction(i, &sa, NULL);
|
|
|
|
/* to handle signals generated by dwm on click events */
|
|
|
|
sa.sa_flags = SA_RESTART | SA_SIGINFO;
|
|
|
|
sa.sa_sigaction = buttonhandler;
|
|
|
|
sigaction(SIGRTMIN, &sa, NULL);
|
|
|
|
/* to handle update signals for individual blocks */
|
|
|
|
sa.sa_sigaction = sighandler;
|
|
|
|
for (Block *current = blocks; current->pathu; current++)
|
|
|
|
if (current->signal > 0)
|
|
|
|
sigaction(SIGRTMIN + current->signal, &sa, NULL);
|
|
|
|
/* to prevent forked children from becoming zombies */
|
|
|
|
sa.sa_flags = SA_NOCLDWAIT | SA_RESTART;
|
|
|
|
sa.sa_handler = SIG_DFL;
|
|
|
|
sigaction(SIGCHLD, &sa, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-07-06 20:28:41 +00:00
|
|
|
sighandler(int signal, siginfo_t *si, void *ucontext)
|
2020-07-03 21:04:11 +00:00
|
|
|
{
|
2020-07-06 20:28:41 +00:00
|
|
|
signal -= SIGRTMIN;
|
2020-07-03 21:04:11 +00:00
|
|
|
for (Block *current = blocks; current->pathu; current++) {
|
2020-07-06 20:28:41 +00:00
|
|
|
if (current->signal == signal)
|
2020-07-09 20:10:34 +00:00
|
|
|
getcmd(current, si->si_value.sival_int);
|
2020-07-03 21:04:11 +00:00
|
|
|
}
|
|
|
|
setroot();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
statusloop()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
setupsignals();
|
|
|
|
for (Block *current = blocks; current->pathu; current++)
|
|
|
|
if (current->interval >= 0)
|
2020-07-09 20:10:34 +00:00
|
|
|
getcmd(current, NILL);
|
2020-07-03 21:04:11 +00:00
|
|
|
setroot();
|
|
|
|
sleep(SLEEPINTERVAL);
|
|
|
|
i = SLEEPINTERVAL;
|
|
|
|
while (statusContinue) {
|
|
|
|
for (Block *current = blocks; current->pathu; current++)
|
|
|
|
if (current->interval > 0 && i % current->interval == 0)
|
2020-07-09 20:10:34 +00:00
|
|
|
getcmd(current, NILL);
|
2020-07-03 21:04:11 +00:00
|
|
|
setroot();
|
|
|
|
sleep(SLEEPINTERVAL);
|
|
|
|
i += SLEEPINTERVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
termhandler(int signum)
|
|
|
|
{
|
|
|
|
statusContinue = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* returns whether block outputs have changed and updates statusstr if they have */
|
|
|
|
int
|
|
|
|
updatestatus()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char *str = statusstr;
|
|
|
|
Block *current;
|
|
|
|
|
|
|
|
for (current = blocks; current->pathu; current++) {
|
|
|
|
if (EMPTYCMDOUT(current)) {
|
|
|
|
if (current->cmdoutprv[0] != current->cmdoutcur[0])
|
|
|
|
goto updatestrstrt;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
i = 0;
|
|
|
|
do {
|
|
|
|
if (current->cmdoutcur[i] == current->cmdoutprv[i]) {
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
str += i;
|
|
|
|
goto updatestrmddl;
|
|
|
|
}
|
|
|
|
} while (NOTATCMDOUTEND(current, i));
|
|
|
|
str += i;
|
|
|
|
if (current->pathc && current->signal)
|
|
|
|
str++;
|
|
|
|
str += delimlength;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
updatestrstrt:
|
|
|
|
current->cmdoutprv[0] = current->cmdoutcur[0];
|
|
|
|
for (current++; current->pathu; current++) {
|
|
|
|
if (EMPTYCMDOUT(current)) {
|
|
|
|
current->cmdoutprv[0] = current->cmdoutcur[0];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
i = 0;
|
|
|
|
updatestrmddl:
|
|
|
|
do {
|
|
|
|
*(str++) = current->cmdoutcur[i];
|
|
|
|
current->cmdoutprv[i] = current->cmdoutcur[i];
|
|
|
|
i++;
|
|
|
|
} while (NOTATCMDOUTEND(current, i));
|
|
|
|
if (current->pathc && current->signal)
|
|
|
|
*(str++) = current->signal;
|
|
|
|
for (i = 0; delim[i]; i++)
|
|
|
|
*(str++) = delim[i];
|
|
|
|
*(str++) = '\n';
|
|
|
|
}
|
|
|
|
if (str != statusstr)
|
|
|
|
*(str - delimlength) = '\0';
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
writepid()
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
char buf[20];
|
|
|
|
struct flock fl;
|
|
|
|
ssize_t len;
|
|
|
|
|
|
|
|
fd = open(LOCKFILE, O_RDWR|O_CREAT, 0644);
|
|
|
|
if (fd == -1) {
|
|
|
|
perror("writepid - fd");
|
|
|
|
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("lockfile - fcntl");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
if (ftruncate(fd, 0) == -1) {
|
|
|
|
perror("writepid - ftruncate");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
snprintf(buf, sizeof buf, "%ld", (long)getpid());
|
|
|
|
len = strlen(buf);
|
|
|
|
if (write(fd, buf, len) != len) {
|
|
|
|
perror("writepid - write");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
writepid();
|
2020-07-07 10:14:42 +00:00
|
|
|
if (argc > 2)
|
2020-07-03 21:04:11 +00:00
|
|
|
if (strcmp("-d", argv[1]) == 0)
|
|
|
|
delim = argv[2];
|
|
|
|
delimlength = strlen(delim) + 1;
|
|
|
|
if (!(dpy = XOpenDisplay(NULL))) {
|
|
|
|
fputs("Error: could not open display.\n", stderr);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
statusloop();
|
|
|
|
unlink(LOCKFILE);
|
2020-07-06 20:28:41 +00:00
|
|
|
XStoreName(dpy, DefaultRootWindow(dpy), "");
|
2020-07-03 21:04:11 +00:00
|
|
|
XCloseDisplay(dpy);
|
|
|
|
return 0;
|
|
|
|
}
|