Imported upstream version 0.59.3
[hcoop/debian/courier-authlib.git] / liblog / logger.c
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
32 static const char rcsid[]="$Id: logger.c,v 1.11 2006/06/01 10:47:33 mrsam Exp $";
33
34 static int do_logger(const char *name, int facility, FILE *f)
35 {
36 char buf[512];
37 char *p;
38 int 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
108 static const char *namearg=0;
109 static const char *pidarg=0;
110 static char *lockfilename=0;
111
112 struct lognames
113 {
114 char *name;
115 int value;
116 };
117
118 static 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
186 static int hup_restart = 0;
187 static int respawn = 0;
188 static int child_pid = -1;
189
190 static 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
206 static 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
214 static 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
230 static 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
238 static 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
248 static 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
288 static void startchild(char **argv, const char *userarg, const char *grouparg)
289 {
290 pid_t p;
291 int pipefd[2];
292 int 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
359 int main(int argc, char **argv)
360 {
361 int facility = LOG_DEST;
362 int daemon = 0;
363 int lockfd = -1;
364 const char *userarg = 0;
365 const char *grouparg = 0;
366 int 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 }