ztatus

[discontinued] Status bar for dwm, and simple notification daemon.
git clone https://noxz.tech/git/ztatus.git
Log | Files | LICENSE

ztatus.c
1#include <dirent.h>
2#include <errno.h>
3#include <fcntl.h>
4#include <signal.h>
5#include <stdarg.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <sys/select.h>
10#include <sys/stat.h>
11#include <time.h>
12#include <unistd.h>
13
14#include "config.h"
15
16#define PNAME "ztatus"
17
18/* functions */
19static int  check_proc(struct dirent*);
20static int die(const char*, ...);
21static void dispatch(void);
22static int  get_int(const char*, int*);
23static int  get_pid(void);
24static void notify(const char*);
25static void render_time(char*);
26static int  run(void);
27static void set_status(const char*);
28static void sigint_handler(int);
29
30/* global variables */
31static char *last_line;
32static int delay_time = 0;
33static int running = 0;
34static int self_pid = 0;
35static int fifofd;
36
37void
38sigint_handler(int signum)
39{
40	running = 0;
41}
42
43int
44die(const char *format, ...)
45{
46	va_list args;
47
48	va_start(args, format);
49	vfprintf(stderr, format, args);
50	va_end(args);
51
52	return 1;
53}
54
55int
56run(void)
57{
58	int rv, count, pid;
59	fd_set rfds;
60	struct timeval tv;
61	char *status_line = NULL;
62
63	/* only allow one instance of ztatus */
64	if ((pid = get_pid()) != -1)
65		return die("error: an instance of %s is already running (PID: %d)\n",
66		           PNAME, pid);
67
68	unlink(FIFO_PATH);
69
70	/* setup, or fail */
71	if ((mknod(FIFO_PATH, S_IFIFO | 0600, 0)) < 0)
72		return die("error: cannot create fifo '%s'\n", FIFO_PATH);
73	if ((fifofd = open(FIFO_PATH, O_RDWR | O_NONBLOCK)) < 0)
74		return die("error: cannot open fifo '%s'\n", FIFO_PATH);
75	if ((status_line = malloc(STATUS_LENGTH)) == NULL)
76		return die("error: failed to allocate memory\n", FIFO_PATH);
77	if ((last_line = calloc(STATUS_LENGTH, sizeof(char))) == NULL)
78		return die("error: failed to allocate memory\n", FIFO_PATH);
79
80	/* handle interrupts and terminations */
81	signal(SIGINT, sigint_handler);
82	signal(SIGTERM, sigint_handler);
83
84	fprintf(stderr, "%s is running as PID: %d\n", PNAME, self_pid);
85
86	running = 1;
87	while (running) {
88		count++;
89
90		/* wait for 1 second or fifofd */
91		FD_ZERO(&rfds);
92		FD_SET(fifofd, &rfds);
93		tv.tv_sec = 1;
94		tv.tv_usec = 0;
95
96		/* select fifo descriptor, and break on failure */
97		if ((rv = select(fifofd + 1, &rfds, NULL, NULL, &tv)) < 0)
98			break;
99
100		/* dispatch on fifo read */
101		if (FD_ISSET(fifofd, &rfds))
102			dispatch();
103
104		/* prevent data printing if it's delayed.
105		 * this is so an external signal can borrow the status line
106		 * for a short time, determined by the delay_time */
107		if (delay_time > 0) {
108			delay_time--;
109			continue;
110		}
111
112		render_time(status_line);
113		set_status(status_line);
114	}
115
116	/* free up resources when done */
117	free(status_line);
118	free(last_line);
119	if (fifofd >= 0)
120		unlink(FIFO_PATH);
121
122	return 0;
123}
124
125int
126get_pid(void)
127{
128	const char *path = "/proc";
129	DIR *dir;
130	struct dirent *e;
131	int p = -1;
132
133	/* check all processes */
134	if ((dir = opendir(path)) != NULL) {
135		while ((e = readdir(dir)) != NULL && (p = check_proc(e)) == -1);
136		closedir(dir);
137	}
138
139	return p;
140}
141
142int
143get_int(const char *s, int *i)
144{
145	const char *c = s;
146
147	*i = 0;
148	while (*c) {
149		if (*c < 48 || *c >= 58)
150			return 0;
151		*i = *i * 10 - 48 + *c;
152		c++;
153	}
154
155	return 1;
156}
157
158int
159check_proc(struct dirent *e)
160{
161	char *base_p = NULL;
162	char *comm_p = NULL;
163	char *cmdline_p = NULL;
164	char comm[7] = {0};
165	char cmdline[64] = {0};
166	FILE *fptr = NULL;
167	struct stat sb;
168	int i, l;
169	int pid;
170	int state = 0;
171
172	if (get_int(e->d_name, &pid) == 0 || pid == self_pid)
173		state = -1;
174
175	if (state == 0) {
176		base_p = malloc(strlen(e->d_name) + 7);
177		strcpy(base_p, "/proc/");
178		strcat(base_p, e->d_name);
179		if (stat(base_p, &sb) != 0 || !S_ISDIR(sb.st_mode))
180			state = -1;
181	}
182
183	if (state == 0) {
184		comm_p = malloc(strlen(base_p) + 6);
185		strcpy(comm_p, base_p);
186		strcat(comm_p, "/comm");
187		if (stat(comm_p, &sb) != 0 || !S_ISREG(sb.st_mode))
188			state = -1;
189	}
190
191	if (state == 0) {
192		cmdline_p = malloc(strlen(base_p) + 9);
193		strcpy(cmdline_p, base_p);
194		strcat(cmdline_p, "/cmdline");
195		if (stat(cmdline_p, &sb) != 0 || !S_ISREG(sb.st_mode))
196			state = -1;
197	}
198
199	if (state == 0) {
200		fptr = fopen(comm_p, "rb");
201		for (i = 0, l = sizeof(comm); i < l; i++)
202			comm[i] = fgetc(fptr);
203		fclose(fptr);
204		if (comm[6] != '\n') {
205			state = -1;
206		} else {
207			comm[6] = '\0';
208			if (strcmp(comm, PNAME) != 0)
209				state = -1;
210		}
211	}
212
213	if (state == 0) {
214		fptr = fopen(cmdline_p, "rb");
215		fread(cmdline, sizeof(cmdline), 1, fptr);
216		l = ftell(fptr);
217		fclose(fptr);
218		if (strchr(cmdline, '/') != NULL)
219			memmove(cmdline,
220			        strrchr(cmdline, '/') + 1,
221			        strlen(strrchr(cmdline, '/') + 1)
222			);
223		if (strcmp(cmdline, PNAME) == 0)
224			return pid;
225	}
226
227	if (base_p != NULL)
228		free(base_p);
229	if (comm_p != NULL)
230		free(comm_p);
231	if (cmdline_p != NULL)
232		free(cmdline_p);
233
234	return -1;
235}
236
237void
238render_time(char *str)
239{
240	int z, y, era, time_val;
241	unsigned doe, yoe, doy, mp, d, m;
242	char *tmp = malloc(32);
243	time_t currentTime = time(NULL);
244	struct tm *localTime;
245
246	localTime = localtime(&currentTime);
247	time_val = currentTime + localTime->tm_gmtoff;
248
249	z = time_val / 86400 + 719468;
250	era = (z >= 0 ? z : z - 146096) / 146097;
251	doe = (unsigned)(z - era * 146097);
252	yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
253	y = (int)yoe + era * 400;
254	doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
255	mp = (5 * doy + 2) / 153;
256	d = doy - (153 * mp + 2) / 5 + 1;
257	m = mp + (mp < 10 ? 3 : -9);
258	y += (m <= 2);
259
260	snprintf(tmp, 32, FORMAT_TIME
261		,y ,m ,d
262		,(time_val / 3600) % 24
263		,(time_val / 60) % 60
264	);
265	strcpy(str, tmp);
266	free(tmp);
267}
268
269void
270set_status(const char *line) {
271	if (strcmp(line, last_line))
272		puts(line);
273	strcpy(last_line, line);
274}
275
276void
277dispatch(void)
278{
279	char buf[BUFSIZ];
280	char *line, *next;
281	ssize_t n;
282
283	if ((n = read(fifofd, buf, sizeof(buf) - 1)) == -1)
284		return;
285
286	buf[n] = '\0';
287	line = buf;
288
289	/* read each line as a single notification,
290	 * which means that only that last line will be seen */
291	while (line) {
292		next = strchr(line, '\n');
293		if (next)
294			*next = '\0';
295		notify(line);
296		if (next)
297			*next = '\n';
298		line = next ? next + 1 : NULL;
299	}
300
301	/* make sure fifo is empty */
302	while (errno != EWOULDBLOCK)
303		read(fifofd, buf, sizeof(buf) - 1);
304}
305
306void
307notify(const char* input)
308{
309	char *status_line = NULL;
310
311	if ((status_line = malloc(STATUS_LENGTH)) == NULL)
312		return;
313
314	delay_time = (int)(DELAY_INTERVAL);
315
316	snprintf(status_line, STATUS_LENGTH, FORMAT_NOTIFY, input);
317	set_status(status_line);
318	free(status_line);
319}
320
321int
322main(int argc, char *argv[])
323{
324	char *s;
325	self_pid = getpid();
326
327	if ((s = strrchr(argv[0], '/')) == NULL)
328		s = argv[0];
329	else
330		s++;
331
332	if (strcmp(s, PNAME) != 0)
333		return die("error: program must run as '%s'\n", PNAME);
334
335	return run();
336}