dsblocks.c (7755B)
1 #include <errno.h> 2 #include <fcntl.h> 3 #include <signal.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 #include <time.h> 8 #include <unistd.h> 9 #include <X11/Xlib.h> 10 11 #include "shared.h" 12 13 #define LOCKFILE "/tmp/dsblocks.pid" 14 15 #define DELIMITERLENGTH (sizeof delimiter) 16 #define STATUSLENGTH (LENGTH(blocks) * (BLOCKLENGTH + DELIMITERLENGTH) + 1) 17 18 #include "config.h" 19 20 _Static_assert(INTERVALs >= 0, "INTERVALs must be greater than or equal to 0"); 21 _Static_assert(INTERVALn >= 0 && INTERVALn <= 999999999, "INTERVALn must be between 0 and 999999999"); 22 23 void cleanup(); 24 25 static void buttonhandler(int sig, siginfo_t *info, void *ucontext); 26 static void setupsignals(); 27 static void sighandler(int sig, siginfo_t *info, void *ucontext); 28 static void statusloop(); 29 static void termhandler(int sig); 30 static void updateblock(Block *block, int sigval); 31 static void updatestatus(); 32 static void writepid(); 33 34 Display *dpy; 35 pid_t pid; 36 37 static Block *dirtyblock; 38 static sigset_t blocksigmask; 39 40 void 41 buttonhandler(int sig, siginfo_t *info, void *ucontext) 42 { 43 sig = info->si_value.sival_int >> 8; 44 for (Block *block = blocks; block->funcu; block++) 45 if (block->signal == sig) 46 switch (fork()) { 47 case -1: 48 perror("buttonhandler - fork"); 49 break; 50 case 0: 51 close(ConnectionNumber(dpy)); 52 block->funcc(info->si_value.sival_int & 0xff); 53 exit(0); 54 } 55 } 56 57 void 58 cleanup() 59 { 60 unlink(LOCKFILE); 61 XStoreName(dpy, DefaultRootWindow(dpy), ""); 62 XCloseDisplay(dpy); 63 } 64 65 void 66 setupsignals() 67 { 68 struct sigaction sa; 69 70 /* populate blocksigmask and check validity of signals */ 71 sigemptyset(&blocksigmask); 72 sigaddset(&blocksigmask, SIGHUP); 73 sigaddset(&blocksigmask, SIGINT); 74 sigaddset(&blocksigmask, SIGTERM); 75 for (Block *block = blocks; block->funcu; block++) { 76 if (block->signal <= 0) 77 continue; 78 if (block->signal > SIGRTMAX - SIGRTMIN) { 79 fprintf(stderr, "Error: SIGRTMIN + %d exceeds SIGRTMAX.\n", block->signal); 80 unlink(LOCKFILE); 81 XCloseDisplay(dpy); 82 exit(2); 83 } 84 sigaddset(&blocksigmask, SIGRTMIN + block->signal); 85 } 86 87 /* setup signal handlers */ 88 /* to handle HUP, INT and TERM */ 89 sa.sa_flags = SA_RESTART; 90 sigemptyset(&sa.sa_mask); 91 sa.sa_handler = termhandler; 92 sigaction(SIGHUP, &sa, NULL); 93 sigaction(SIGINT, &sa, NULL); 94 sigaction(SIGTERM, &sa, NULL); 95 96 /* to ignore unused realtime signals */ 97 // sa.sa_flags = SA_RESTART; 98 // sigemptyset(&sa.sa_mask); 99 sa.sa_handler = SIG_IGN; 100 for (int i = SIGRTMIN + 1; i <= SIGRTMAX; i++) 101 sigaction(i, &sa, NULL); 102 103 /* to prevent forked children from becoming zombies */ 104 sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; 105 // sigemptyset(&sa.sa_mask); 106 sa.sa_handler = SIG_DFL; 107 sigaction(SIGCHLD, &sa, NULL); 108 109 /* to handle signals generated by dwm on click events */ 110 sa.sa_flags = SA_RESTART | SA_SIGINFO; 111 // sigemptyset(&sa.sa_mask); 112 sa.sa_sigaction = buttonhandler; 113 sigaction(SIGRTMIN, &sa, NULL); 114 115 /* to handle update signals for individual blocks */ 116 sa.sa_flags = SA_NODEFER | SA_RESTART | SA_SIGINFO; 117 sa.sa_mask = blocksigmask; 118 sa.sa_sigaction = sighandler; 119 for (Block *block = blocks; block->funcu; block++) 120 if (block->signal > 0) 121 sigaction(SIGRTMIN + block->signal, &sa, NULL); 122 } 123 124 void 125 sighandler(int sig, siginfo_t *info, void *ucontext) 126 { 127 sig -= SIGRTMIN; 128 for (Block *block = blocks; block->funcu; block++) 129 if (block->signal == sig) 130 updateblock(block, info->si_value.sival_int); 131 updatestatus(); 132 } 133 134 void 135 statusloop() 136 { 137 int i; 138 struct timespec t; 139 140 sigprocmask(SIG_BLOCK, &blocksigmask, NULL); 141 for (Block *block = blocks; block->funcu; block++) 142 if (block->interval >= 0) 143 updateblock(block, NILL); 144 for (i = 1; ; i++) { 145 updatestatus(); 146 sigprocmask(SIG_UNBLOCK, &blocksigmask, NULL); 147 t.tv_sec = INTERVALs, t.tv_nsec = INTERVALn; 148 while (nanosleep(&t, &t) == -1); 149 sigprocmask(SIG_BLOCK, &blocksigmask, NULL); 150 for (Block *block = blocks; block->funcu; block++) 151 if (block->interval > 0 && i % block->interval == 0) 152 updateblock(block, NILL); 153 } 154 } 155 156 void 157 termhandler(int sig) 158 { 159 cleanup(); 160 exit(0); 161 } 162 163 void 164 updateblock(Block *block, int sigval) 165 { 166 size_t l; 167 168 if (!(l = block->funcu(block->curtext, sigval))) 169 return; 170 if (memcmp(block->curtext, block->prvtext, l) != 0) { 171 memcpy(block->prvtext, block->curtext, l); 172 if (!dirtyblock || block < dirtyblock) 173 dirtyblock = block; 174 } 175 if (l == 1) 176 block->length = 0; 177 else { 178 if (block->funcc) 179 block->curtext[l - 1] = block->signal; 180 else 181 l--; 182 memcpy(block->curtext + l, delimiter, DELIMITERLENGTH); 183 block->length = l + DELIMITERLENGTH; 184 } 185 } 186 187 void 188 updatestatus() 189 { 190 static char statustext[STATUSLENGTH]; 191 char *s = statustext; 192 Block *block; 193 194 if (!dirtyblock) 195 return; 196 for (block = blocks; block < dirtyblock; block++) 197 s += block->length; 198 for (; block->funcu; block++) { 199 memcpy(s, block->curtext, block->length); 200 s += block->length; 201 } 202 s[s == statustext ? 0 : -DELIMITERLENGTH] = '\0'; 203 dirtyblock = NULL; 204 205 XStoreName(dpy, DefaultRootWindow(dpy), statustext); 206 XSync(dpy, False); 207 } 208 209 void 210 writepid() 211 { 212 int fd; 213 struct flock fl; 214 215 if ((fd = open(LOCKFILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) { 216 perror("writepid - open"); 217 exit(1); 218 } 219 fl.l_type = F_WRLCK; 220 fl.l_whence = SEEK_SET; 221 fl.l_start = 0; 222 fl.l_len = 0; 223 if (fcntl(fd, F_SETLK, &fl) == -1) { 224 if (errno == EACCES || errno == EAGAIN) { 225 fputs("Error: another instance of dsblocks is already running.\n", stderr); 226 exit(2); 227 } 228 perror("writepid - fcntl"); 229 exit(1); 230 } 231 if (ftruncate(fd, 0) == -1) { 232 perror("writepid - ftruncate"); 233 exit(1); 234 } 235 if (dprintf(fd, "%ld", (long)pid) < 0) { 236 perror("writepid - dprintf"); 237 exit(1); 238 } 239 } 240 241 int 242 main(int argc, char *argv[]) 243 { 244 pid = getpid(); 245 writepid(); 246 if (!(dpy = XOpenDisplay(NULL))) { 247 fputs("Error: could not open display.\n", stderr); 248 unlink(LOCKFILE); 249 return 1; 250 } 251 setupsignals(); 252 statusloop(); 253 cleanup(); 254 return 0; 255 }