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