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 | |
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 | } |