1 /* direvent - directory content watcher daemon
2    Copyright (C) 2012-2016 Sergey Poznyakoff
3 
4    Direvent is free software; you can redistribute it and/or modify it
5    under the terms of the GNU General Public License as published by the
6    Free Software Foundation; either version 3 of the License, or (at your
7    option) any later version.
8 
9    Direvent is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with direvent. If not, see <http://www.gnu.org/licenses/>. */
16 
17 #include "direvent.h"
18 #include <stdarg.h>
19 #include <fcntl.h>
20 #include <getopt.h>
21 #include <pwd.h>
22 #include <grp.h>
23 #include <signal.h>
24 #include <grecs.h>
25 #include <locale.h>
26 #include "wordsplit.h"
27 
28 #ifndef SYSCONFDIR
29 # define SYSCONFDIR "/etc"
30 #endif
31 #define DEFAULT_CONFFILE SYSCONFDIR "/direvent.conf"
32 
33 /* Configuration settings */
34 const char *program_name;         /* This program name */
35 const char *conffile = DEFAULT_CONFFILE;
36 int foreground;                   /* Remain in the foreground */
37 char *self_test_prog;
38 char *tag;                        /* Syslog tag */
39 int facility = -1;                /* Use this syslog facility for logging.
40 				     -1 means log to stderr */
41 int syslog_include_prio;
42 int debug_level;                  /* Debug verbosity level */
43 char *pidfile = NULL;             /* Store PID to this file */
44 char *user = NULL;                /* User to run as */
45 
46 int log_to_stderr = LOG_DEBUG;
47 
48 
49 /* Diagnostic functions */
50 const char *
severity(int prio)51 severity(int prio)
52 {
53 	switch (prio) {
54 	case LOG_EMERG:
55 		return "EMERG";
56 	case LOG_ALERT:
57 		return "ALERT";
58 	case LOG_CRIT:
59 		return "CRIT";
60 	case LOG_ERR:
61 		return "ERROR";
62 	case LOG_WARNING:
63 		return "WARNING";
64 	case LOG_NOTICE:
65 		return "NOTICE";
66 	case LOG_INFO:
67 		return "INFO";
68 	case LOG_DEBUG:
69 		return "DEBUG";
70 	}
71 	return NULL;
72 }
73 
74 void
vdiag(int prio,const char * fmt,va_list ap)75 vdiag(int prio, const char *fmt, va_list ap)
76 {
77 	const char *s;
78 	va_list tmp;
79 
80 	if (log_to_stderr >= prio) {
81 		fprintf(stderr, "%s: ", program_name);
82 		s = severity(prio);
83 		if (s)
84 			fprintf(stderr, "[%s] ", s);
85 		va_copy(tmp, ap);
86 		vfprintf(stderr, fmt, tmp);
87 		fputc('\n', stderr);
88 		va_end(tmp);
89 	}
90 
91 	if (facility > 0) {
92 		if (syslog_include_prio && (s = severity(prio)) != NULL) {
93 			static char *fmtbuf;
94 			static size_t fmtsize;
95 			size_t len = strlen(fmt) + strlen(s) + 4;
96 			char *p;
97 
98 			if (len > fmtsize) {
99 				fmtbuf = erealloc(fmtbuf, len);
100 				fmtsize = len;
101 			}
102 
103 			p = fmtbuf;
104 			*p++ = '[';
105 			while (*s)
106 				*p++ = *s++;
107 			*p++ = ']';
108 			*p++ = ' ';
109 			while (*p++ = *fmt++);
110 			vsyslog(prio, fmtbuf, ap);
111 		} else
112 			vsyslog(prio, fmt, ap);
113 	}
114 }
115 
116 void
diag(int prio,const char * fmt,...)117 diag(int prio, const char *fmt, ...)
118 {
119 	va_list ap;
120 
121 	va_start(ap, fmt);
122 	vdiag(prio, fmt, ap);
123 	va_end(ap);
124 }
125 
126 void
debugprt(const char * fmt,...)127 debugprt(const char *fmt, ...)
128 {
129 	va_list ap;
130 
131 	va_start(ap, fmt);
132 	vdiag(LOG_DEBUG, fmt, ap);
133 	va_end(ap);
134 }
135 
136 /* Memory allocation with error checking */
137 void *
emalloc(size_t size)138 emalloc(size_t size)
139 {
140 	void *p = malloc(size);
141 	if (!p) {
142 		diag(LOG_CRIT, _("not enough memory"));
143 		exit(2);
144 	}
145 	return p;
146 }
147 
148 void *
ecalloc(size_t nmemb,size_t size)149 ecalloc(size_t nmemb, size_t size)
150 {
151 	void *p = calloc(nmemb, size);
152 	if (!p) {
153 		diag(LOG_CRIT, "not enough memory");
154 		exit(2);
155 	}
156 	return p;
157 }
158 
159 void *
erealloc(void * ptr,size_t size)160 erealloc(void *ptr, size_t size)
161 {
162 	void *p = realloc(ptr, size);
163 	if (!p) {
164 		diag(LOG_CRIT, _("not enough memory"));
165 		exit(2);
166 	}
167 	return p;
168 }
169 
170 char *
estrdup(const char * str)171 estrdup(const char *str)
172 {
173 	size_t len = strlen(str);
174 	char *p = emalloc(len + 1);
175 	memcpy(p, str, len);
176 	p[len] = 0;
177 	return p;
178 }
179 
180 /* Create a full file name from directory and file name */
181 char *
mkfilename(const char * dir,const char * file)182 mkfilename(const char *dir, const char *file)
183 {
184 	char *tmp;
185 	size_t dirlen = strlen(dir);
186 	size_t fillen = strlen(file);
187 	size_t len;
188 
189 	if (!file || file[0] == 0)
190 		return strdup(dir);
191 	while (dirlen > 0 && dir[dirlen-1] == '/')
192 		dirlen--;
193 
194 	len = dirlen + (dir[0] ? 1 : 0) + fillen;
195 	tmp = malloc(len + 1);
196 	if (tmp) {
197 		memcpy(tmp, dir, dirlen);
198 		if (dir[0])
199 			tmp[dirlen++] = '/';
200 		memcpy(tmp + dirlen, file, fillen);
201 		tmp[len] = 0;
202 	}
203 	return tmp;
204 }
205 
206 int
trans_strtotok(struct transtab * tab,const char * str,int * ret)207 trans_strtotok(struct transtab *tab, const char *str, int *ret)
208 {
209 	for (; tab->name; tab++)
210 		if (strcmp(tab->name, str) == 0) {
211 			*ret = tab->tok;
212 			return 0;
213 		}
214 	return -1;
215 }
216 
217 char *
trans_toktostr(struct transtab * tab,int tok)218 trans_toktostr(struct transtab *tab, int tok)
219 {
220 	for (; tab->name; tab++)
221 		if (tab->tok == tok)
222 			return tab->name;
223 	return NULL;
224 }
225 
226 char *
trans_toknext(struct transtab * tab,int tok,int * next)227 trans_toknext(struct transtab *tab, int tok, int *next)
228 {
229 	int i;
230 
231 	for (i = *next; tab[i].name; i++)
232 		if (tab[i].tok & tok) {
233 			*next = i + 1;
234 			return tab[i].name;
235 		}
236 	*next = i;
237 	return NULL;
238 }
239 
240 char *
trans_tokfirst(struct transtab * tab,int tok,int * next)241 trans_tokfirst(struct transtab *tab, int tok, int *next)
242 {
243 	*next = 0;
244 	return trans_toknext(tab, tok, next);
245 }
246 
247 
248 /* Command line processing and auxiliary functions */
249 
250 static void
set_program_name(const char * arg)251 set_program_name(const char *arg)
252 {
253 	char *p = strrchr(arg, '/');
254 	if (p)
255 		program_name = p + 1;
256 	else
257 		program_name = arg;
258 }
259 
260 
261 void
signal_setup(void (* sf)(int))262 signal_setup(void (*sf) (int))
263 {
264 	static int sigv[] = { SIGTERM, SIGQUIT, SIGINT, SIGHUP, SIGALRM,
265 			      SIGUSR1, SIGUSR1, SIGCHLD };
266 	sigv_set_all(sf, NITEMS(sigv), sigv, NULL);
267 }
268 
269 void
storepid(const char * pidfile)270 storepid(const char *pidfile)
271 {
272 	FILE *fp = fopen(pidfile, "w");
273 	if (!fp) {
274 		diag(LOG_ERR, _("cannot open pidfile %s for writing: %s"),
275 		     pidfile, strerror(errno));
276 	} else {
277 		fprintf(fp, "%lu\n", (unsigned long) getpid());
278 		fclose(fp);
279 	}
280 }
281 
282 static int
membergid(gid_t gid,size_t gc,gid_t * gv)283 membergid(gid_t gid, size_t gc, gid_t *gv)
284 {
285 	int i;
286 	for (i = 0; i < gc; i++)
287 		if (gv[i] == gid)
288 			return 1;
289 	return 0;
290 }
291 
292 static void
get_user_groups(uid_t uid,size_t * pgidc,gid_t ** pgidv)293 get_user_groups(uid_t uid, size_t *pgidc, gid_t **pgidv)
294 {
295 	size_t gidc = 0, n = 0;
296 	gid_t *gidv = NULL;
297 	struct passwd *pw;
298 	struct group *gr;
299 
300 	pw = getpwuid(uid);
301 	if (!pw) {
302 		diag(LOG_ERR, 0, _("no user with UID %lu"),
303 		     (unsigned long)uid);
304 		exit(2);
305 	}
306 
307 	n = 32;
308 	gidv = ecalloc(n, sizeof(gidv[0]));
309 
310 	gidv[0] = pw->pw_gid;
311 	gidc = 1;
312 
313 	setgrent();
314 	while (gr = getgrent()) {
315 		char **p;
316 		for (p = gr->gr_mem; *p; p++)
317 			if (strcmp(*p, pw->pw_name) == 0) {
318 				if (n == gidc) {
319 					n += 32;
320 					gidv = erealloc(gidv,
321 							n * sizeof(gidv[0]));
322 				}
323 				if (!membergid(gr->gr_gid, gidc, gidv))
324 					gidv[gidc++] = gr->gr_gid;
325 			}
326 	}
327 	endgrent();
328 	*pgidc = gidc;
329 	*pgidv = gidv;
330 }
331 
332 void
setuser(const char * user)333 setuser(const char *user)
334 {
335 	struct passwd *pw;
336 	size_t gidc;
337 	gid_t *gidv;
338 
339 	pw = getpwnam(user);
340 	if (!pw) {
341 		diag(LOG_CRIT, "getpwnam(%s): %s", user, strerror(errno));
342 		exit(2);
343 	}
344 	if (pw->pw_uid == 0)
345 		return;
346 
347 	get_user_groups(pw->pw_uid, &gidc, &gidv);
348 	if (setgroups(gidc, gidv) < 0) {
349 		diag(LOG_CRIT, "setgroups: %s", strerror(errno));
350 		exit(2);
351 	}
352 	free(gidv);
353 
354 	if (setgid(pw->pw_gid)) {
355 		diag(LOG_CRIT, "setgid(%lu): %s", (unsigned long) pw->pw_gid,
356 		     strerror(errno));
357 		exit(2);
358 	}
359 	if (setuid(pw->pw_uid)) {
360 		diag(LOG_CRIT, "setuid(%lu): %s", (unsigned long) pw->pw_uid,
361 		     strerror(errno));
362 		exit(2);
363 	}
364 }
365 
366 void
ev_log(int flags,struct watchpoint * dp)367 ev_log(int flags, struct watchpoint *dp)
368 {
369 	int i;
370 	char *p;
371 
372 	if (debug_level > 0) {
373 		for (p = trans_tokfirst(sysev_transtab, flags, &i); p;
374 		     p = trans_toknext(sysev_transtab, flags, &i))
375 			debug(1, ("%s: %s", dp->dirname, p));
376 	}
377 }
378 
379 
380 /* Initialize generic event table */
381 void
genev_init()382 genev_init()
383 {
384 	int i;
385 
386 	for (i = 0; i < genev_xlat[i].gen_mask; i++)
387 		defevt(trans_toktostr(genev_transtab, genev_xlat[i].gen_mask),
388 		       &genev_xlat[i], 0);
389 }
390 
391 
392 int signo = 0;
393 int stop = 0;
394 
395 pid_t self_test_pid;
396 int exit_code = 0;
397 
398 void
sigmain(int sig)399 sigmain(int sig)
400 {
401 	signo = sig;
402 	switch (signo) {
403 	case SIGCHLD:
404 	case SIGALRM:
405 		break;
406 	default:
407 		stop = 1;
408 	}
409 }
410 
411 void
self_test()412 self_test()
413 {
414 	pid_t pid;
415 	char *args[4];
416 
417 	pid = fork();
418 	if (pid == (pid_t)-1) {
419 		diag(LOG_CRIT,
420 		     _("cannot run `%s': fork failed: %s"),
421 		     self_test_prog, strerror(errno));
422 		exit(2);
423 	}
424 
425 	if (pid != 0) {
426 		self_test_pid = pid;
427 		return;
428 	}
429 
430 	args[0] = "/bin/sh";
431 	args[1] = "-c";
432 	args[2] = self_test_prog;
433 	args[3] = NULL;
434 	execv(args[0], args);
435 
436 	diag(LOG_ERR, "execv: %s: %s", self_test_prog, strerror(errno));
437 	_exit(127);
438 }
439 
440 
441 #if USE_IFACE == IFACE_INOTIFY
442 # define INTERFACE "inotify"
443 #elif USE_IFACE == IFACE_KQUEUE
444 # define INTERFACE "kqueue"
445 #endif
446 
447 static int opt_debug_level = 0;
448 static int opt_foreground = 0;
449 static char *opt_pidfile = NULL;
450 static char *opt_user = NULL;
451 static int opt_facility = -1;
452 static int lint_only = 0;
453 
454 #include "cmdline.h"
455 
456 int
main(int argc,char ** argv)457 main(int argc, char **argv)
458 {
459 	int i;
460 
461 #ifdef ENABLE_NLS
462 	setlocale(LC_ALL, "");
463 	bindtextdomain(PACKAGE, LOCALEDIR);
464 	textdomain(PACKAGE);
465 #endif
466 
467 	set_program_name(argv[0]);
468 	tag = estrdup(program_name);
469 
470 	genev_init();
471 	config_init();
472 
473 	parse_options(argc, argv, &i);
474 
475 	argc -= i;
476 	argv += i;
477 
478 	switch (argc) {
479 	default:
480 		diag(LOG_CRIT, _("too many arguments"));
481 		exit(1);
482 	case 1:
483 		conffile = argv[0];
484 		break;
485 	case 0:
486 		break;
487 	}
488 
489 	config_parse(conffile);
490 	if (lint_only)
491 		return 0;
492 
493 	if (opt_debug_level)
494 		debug_level += opt_debug_level;
495 	if (opt_foreground)
496 		foreground = opt_foreground;
497 	if (opt_pidfile)
498 		pidfile = opt_pidfile;
499 	if (opt_facility != -1)
500 		facility = opt_facility;
501 	if (!foreground && facility <= 0)
502 		facility = LOG_DAEMON;
503 	if (opt_user)
504 		user = opt_user;
505 
506 	if (facility > 0) {
507 		openlog(tag, LOG_PID, facility);
508 		grecs_log_to_stderr = 0;
509 	}
510 
511 	if (foreground)
512 		setup_watchers();
513 	else {
514 		/* Become a daemon */
515 		if (detach(setup_watchers)) {
516 			diag(LOG_CRIT, "daemon: %s", strerror(errno));
517 			exit(1);
518 		}
519 		log_to_stderr = -1;
520 	}
521 
522 	diag(LOG_INFO, _("%s %s started"), program_name, VERSION);
523 
524 	/* Write pidfile */
525 	if (pidfile)
526 		storepid(pidfile);
527 
528 	/* Relinquish superuser privileges */
529 	if (user && getuid() == 0)
530 		setuser(user);
531 
532 	signal_setup(sigmain);
533 
534 	if (self_test_prog)
535 		self_test();
536 
537 	/* Main loop */
538 	while (!stop && sysev_select() == 0) {
539 		process_timeouts();
540 		process_cleanup(0);
541 		watchpoint_gc();
542 	}
543 
544 	shutdown_watchers();
545 
546 	diag(LOG_INFO, _("%s %s stopped"), program_name, VERSION);
547 
548 	if (pidfile)
549 		unlink(pidfile);
550 
551 	return exit_code;
552 }
553