Merge branch 'debian'
[hcoop/debian/courier-authlib.git] / liblog / logger.c
CommitLineData
d9898ee8 1/*
2** Copyright 1998 - 2006 Double Precision, Inc.
3** See COPYING for distribution information.
4*/
5
6#if HAVE_CONFIG_H
7#include "config.h"
8#endif
9#include <string.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <sys/types.h>
13#include <sys/wait.h>
14#include <signal.h>
15#include <errno.h>
16#include <fcntl.h>
17#include <pwd.h>
18#include <grp.h>
19#if HAVE_UNISTD_H
20#include <unistd.h>
21#endif
22#if HAVE_SYSLOG_H
23#include <syslog.h>
24#else
25#undef TESTMODE
26#define TESTMODE 1
27#endif
28#include "../liblock/config.h"
29#include "../liblock/liblock.h"
30#include "../numlib/numlib.h"
31
32static const char rcsid[]="$Id: logger.c,v 1.11 2006/06/01 10:47:33 mrsam Exp $";
33
34static int do_logger(const char *name, int facility, FILE *f)
35{
36char buf[512];
37char *p;
38int c;
39
40#if TESTMODE
41
42#else
43 openlog(name, 0
44
45#ifdef LOG_NDELAY
46 | LOG_NDELAY
47#else
48 | LOG_NOWAIT
49#endif
50
51 , facility);
52#endif
53 if (chdir("/") < 0)
54 {
55 perror("chdir(\"/\")");
56 exit(1);
57 }
58
59 while (fgets(buf, sizeof(buf), f))
60 {
61 if ((p=strchr(buf, '\n')) != 0) *p=0;
62 else
63 while ((c=getchar()) != EOF && c != '\n')
64 ;
65
66#if TESTMODE
67 fprintf(stderr, "%s: %s\n", name, buf);
68#else
69 c=LOG_INFO;
70 if (strncmp(buf, "ERR:", 4) == 0)
71 {
72 c=LOG_ERR;
73 p=buf+4;
74 }
75 else if (strncmp(buf, "WARN:", 5) == 0)
76 {
77 c=LOG_WARNING;
78 p=buf+5;
79 }
80 else if (strncmp(buf, "ALERT:", 6) == 0)
81 {
82 c=LOG_ALERT;
83 p=buf+6;
84 }
85 else if (strncmp(buf, "CRIT:", 5) == 0)
86 {
87 c=LOG_CRIT;
88 p=buf+5;
89 }
90 else if (strncmp(buf, "DEBUG:", 6) == 0)
91 {
92 c=LOG_DEBUG;
93 p=buf+6;
94 }
95 else if (strncmp(buf, "INFO:", 5) == 0)
96 p=buf+5;
97 else p=buf;
98
99 while (*p == ' ')
100 ++p;
101
102 syslog(c, "%s", p);
103#endif
104 }
105 return (0);
106}
107
108static const char *namearg=0;
109static const char *pidarg=0;
110static char *lockfilename=0;
111
112struct lognames
113{
114 char *name;
115 int value;
116};
117
118static struct lognames facilitynames[] =
119{
120#ifdef LOG_AUTH
121 { "auth", LOG_AUTH },
122#endif
123#ifdef LOG_AUTHPRIV
124 { "authpriv", LOG_AUTHPRIV },
125#endif
126#ifdef LOG_CONSOLE
127 { "console", LOG_CONSOLE },
128#endif
129#ifdef LOG_CRON
130 { "cron", LOG_CRON },
131#endif
132#ifdef LOG_DAEMON
133 { "daemon", LOG_DAEMON },
134#endif
135#ifdef LOG_FTP
136 { "ftp", LOG_FTP },
137#endif
138#ifdef LOG_KERN
139 { "kern", LOG_KERN },
140#endif
141#ifdef LOG_LPR
142 { "lpr", LOG_LPR },
143#endif
144#ifdef LOG_MAIL
145 { "mail", LOG_MAIL },
146#endif
147#ifdef LOG_AUTHPRIV
148 { "news", LOG_NEWS },
149#endif
150#ifdef LOG_SECURITY
151 { "security", LOG_SECURITY },
152#endif
153#ifdef LOG_USER
154 { "user", LOG_USER },
155#endif
156#ifdef LOG_UUCP
157 { "uucp", LOG_UUCP },
158#endif
159#ifdef LOG_LOCAL0
160 { "local0", LOG_LOCAL0 },
161#endif
162#ifdef LOG_LOCAL1
163 { "local1", LOG_LOCAL1 },
164#endif
165#ifdef LOG_LOCAL2
166 { "local2", LOG_LOCAL2 },
167#endif
168#ifdef LOG_LOCAL3
169 { "local3", LOG_LOCAL3 },
170#endif
171#ifdef LOG_LOCAL4
172 { "local4", LOG_LOCAL4 },
173#endif
174#ifdef LOG_LOCAL5
175 { "local5", LOG_LOCAL5 },
176#endif
177#ifdef LOG_LOCAL6
178 { "local6", LOG_LOCAL6 },
179#endif
180#ifdef LOG_LOCAL7
181 { "local7", LOG_LOCAL7 },
182#endif
183 { 0, 0 }
184};
185
186static int hup_restart = 0;
187static int respawn = 0;
188static int child_pid = -1;
189
190static RETSIGTYPE sighup(int n)
191{
192 if (child_pid > 0)
193 {
194 /* The child may respond to HUP by dying. If so it will
195 close its stderr and do_logger will terminate; at that
196 point we need to restart it */
197 hup_restart = 1;
198 kill (child_pid, SIGHUP);
199 }
200 signal(SIGHUP, sighup);
201#if RETSIGTYPE != void
202 return (1);
203#endif
204}
205
206static RETSIGTYPE sigalrm(int n)
207{
208 if (child_pid > 0) kill(child_pid, SIGKILL);
209#if RETSIGTYPE != void
210 return (1);
211#endif
212}
213
214static RETSIGTYPE sigterm(int n)
215{
216 if (child_pid > 0)
217 {
218 hup_restart = 0;
219 respawn = 0;
220 kill(child_pid, SIGTERM);
221 signal(SIGALRM, sigalrm); /* kill after 8 secs */
222 alarm(8);
223 }
224 else exit(0);
225#if RETSIGTYPE != void
226 return (1);
227#endif
228}
229
230static void checkfd(int actual, int exp)
231{
232 if (actual == exp) return;
233 fprintf(stderr, "Bad fd, got %d, expected %d\n", actual, exp);
234 if (actual < 0) perror("error");
235 exit(1);
236}
237
238static int isid(const char *p)
239{
240 while (*p)
241 {
242 if (*p < '0' || *p > '9') return (0);
243 ++p;
244 }
245 return (1);
246}
247
248static void setuidgid(const char *userarg,
249 const char *grouparg)
250{
251 if (grouparg)
252 {
253 gid_t gid = 0;
254 struct group *gr;
255
256 if (isid(grouparg))
257 gid=atoi(grouparg);
258 else if ((gr=getgrnam(grouparg)) == 0)
259 {
260 fprintf(stderr, "Group not found: %s\n", grouparg);
261 exit(1);
262 }
263 else gid=gr->gr_gid;
264
265 libmail_changegroup(gid);
266 }
267
268 if (userarg)
269 {
270 uid_t uid;
271
272 if (isid(userarg))
273 {
274 uid=atoi(userarg);
275 libmail_changeuidgid(uid, getgid());
276 }
277 else
278 {
279 gid_t g=getgid(), *gp=0;
280
281 if (grouparg) gp= &g;
282 libmail_changeusername(userarg, gp);
283 }
284 }
285}
286
287
288static void startchild(char **argv, const char *userarg, const char *grouparg)
289{
290pid_t p;
291int pipefd[2];
292int tmp;
293
294 signal(SIGTERM, sigterm);
295 signal(SIGHUP, sighup);
296
297 /* Make sure the pipefds are at least 3 and 4. If we have an open
298 stderr then keep it around for debugging purposes. */
299 close(0);
300 checkfd(open("/dev/null", O_RDWR), 0);
301 close(1);
302 checkfd(dup(0), 1);
303 if ((tmp = dup(0)) > 2) close(tmp);
304
305 if (pipe(pipefd) < 0)
306 {
307 perror("pipe");
308 exit(1);
309 }
310 p = fork();
311 if (p < 0)
312 {
313 perror("fork");
314 exit(1);
315 }
316 if (p == 0)
317 {
318
319 close(pipefd[0]);
320 close(2);
321 checkfd(dup(pipefd[1]), 2);
322 close(pipefd[1]);
323 setuidgid(userarg, grouparg);
324 execvp(argv[0], argv);
325 perror("exec");
326 exit(1);
327 }
328
329 // We can close stderr now
330
331 close(2);
332 checkfd(dup(0), 2);
333
334
335 close(pipefd[1]);
336 close(0);
337 checkfd(dup(pipefd[0]), 0);
338 close(pipefd[0]);
339 child_pid = p;
340}
341/*
342 * Note that we now support several modes of operation:
343 *
344 * (1) standalone logger, just collect messages on stdin and feed to syslog
345 * courierlogger foo
346 * courierlogger -name=foo
347 *
348 * (2) run a child process, collect its stderr messages and feed to syslog
349 * courierlogger [-name=foo] foo arg1 arg2
350 *
351 * (3) start a detached daemon with a child process
352 * courierlogger [-name=foo] -pid=/var/run/foo.pid -start foo arg1 arg2
353 *
354 * (4) stop or restart a detached daemon
355 * courierlogger -pid=/var/run/foo.pid -stop (or receive a SIGTERM)
356 * courierlogger -pid=/var/run/foo.pid -restart (or receive a SIGHUP)
357 */
358
359int main(int argc, char **argv)
360{
361int facility = LOG_DEST;
362int daemon = 0;
363int lockfd = -1;
364const char *userarg = 0;
365const char *grouparg = 0;
366int droproot=0;
367
368 if (argc == 2 && argv[1][0] != '-')
369 {
370 /* backwards-compatibility mode */
371 close(1);
372 close(2);
373 checkfd(open("/dev/null", O_WRONLY), 1);
374 checkfd(open("/dev/null", O_WRONLY), 2);
375 do_logger(argv[1], facility, stdin);
376 exit(0);
377 }
378
379 if (argc <= 1)
380 {
381 fprintf(stderr,
382
383 "Usage: courierlogger [-name=name] [-pid=pidfile] [-facility=type]\n"
384 " [-start|-stop|-restart] [cmd [args...]]\n"
385 );
386 exit(1);
387 }
388
389 argv++, argc--;
390 while (argc > 0 && argv[0][0]=='-')
391 {
392 if (strncmp(argv[0],"-pid=",5) == 0 && argv[0][5])
393 {
394 pidarg=&argv[0][5];
395 lockfilename=malloc(strlen(pidarg)+sizeof(".lock"));
396 if (!lockfilename)
397 {
398 perror("malloc");
399 exit(1);
400 }
401 strcat(strcpy(lockfilename, pidarg), ".lock");
402 }
403 else
404 if (strncmp(argv[0],"-name=",6) == 0 && argv[0][6])
405 namearg=&argv[0][6];
406 else if (strncmp(argv[0],"-user=",6) == 0)
407 userarg=argv[0]+6;
408 else if (strncmp(argv[0],"-group=",7) == 0)
409 grouparg=argv[0]+7;
410 else if (strcmp(argv[0], "-droproot") == 0)
411 droproot=1;
412 else
413 if (strncmp(argv[0],"-facility=",10) == 0)
414 {
415 struct lognames *p = facilitynames;
416
417 while (p->name && strcmp(p->name, &argv[0][10]))
418 p++;
419 if (p->name == 0)
420 {
421 fprintf(stderr, "Unknown facility name '%s'\n",
422 &argv[0][10]);
423 exit(1);
424 }
425 facility = p->value;
426 }
427 else
428 if (strcmp(argv[0],"-start") == 0)
429 daemon = 1;
430 else
431 if (strcmp(argv[0],"-stop") == 0)
432 daemon = 2;
433 else
434 if (strcmp(argv[0],"-restart") == 0)
435 daemon = 3;
436 else
437 if (strcmp(argv[0],"-respawn") == 0)
438 respawn = 1;
439 else
440 {
441 fprintf(stderr, "Unknown option '%s'\n", argv[0]);
442 exit(1);
443 }
444 argv++, argc--;
445 }
446
447 if (daemon && !pidarg)
448 {
449 fprintf(stderr, "-pid argument required\n");
450 exit(1);
451 }
452
453 if (!daemon && pidarg)
454 daemon = 1; /* -start implied */
455
456 if (!namearg && daemon != 2 && daemon != 3)
457 {
458 /* choose a default name based on the program we're running */
459 if (argc <= 0 || !argv[0] || !argv[0][0])
460 {
461 fprintf(stderr, "-name option required for standalone logger\n");
462 exit(1);
463 }
464 namearg = strrchr(argv[0],'/');
465 namearg = namearg ? namearg+1 : argv[0];
466 }
467
468 switch (daemon)
469 {
470 case 1: /* start */
471 if (argc <= 0 || !argv[0] || !argv[0][0])
472 {
473 fprintf(stderr, "-start must be followed by a command to execute\n");
474 exit(1);
475 }
476 lockfd=ll_daemon_start(lockfilename);
477 if (lockfd < 0)
478 {
479 perror("ll_daemon_start");
480 exit(1);
481 }
482 startchild(argv, droproot ? userarg:NULL,
483 droproot ? grouparg:NULL);
484 ll_daemon_started(pidarg, lockfd);
485 break;
486 case 2: /* stop */
487 exit(ll_daemon_stop(lockfilename, pidarg));
488 case 3: /* restart */
489 exit(ll_daemon_restart(lockfilename, pidarg));
490 default: /* run in foreground, with or without a child process */
491
492 if (argc > 0)
493 startchild(argv, droproot ? userarg:NULL,
494 droproot ? grouparg:NULL);
495 }
496
497 setuidgid(userarg, grouparg);
498
499 while (1)
500 {
501 int waitstat;
502 pid_t p2;
503 FILE *f = fdopen(0, "r");
504
505 do_logger(namearg, facility, f);
506 fclose(f);
507 if (child_pid < 0) break;
508 while ((p2=wait(&waitstat)) != child_pid &&
509 (p2 != -1 || errno != ECHILD))
510 ;
511 if (hup_restart)
512 hup_restart = 0;
513 else if (respawn)
514 sleep (5);
515 else
516 break;
517 startchild(argv, NULL, NULL);
518 }
519 exit(0);
520}