1 /* service.c -- skeleton for Cyrus service; calls the real main
2 *
3 * Copyright (c) 1994-2008 Carnegie Mellon University. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * 3. The name "Carnegie Mellon University" must not be used to
18 * endorse or promote products derived from this software without
19 * prior written permission. For permission or any legal
20 * details, please contact
21 * Carnegie Mellon University
22 * Center for Technology Transfer and Enterprise Creation
23 * 4615 Forbes Avenue
24 * Suite 302
25 * Pittsburgh, PA 15213
26 * (412) 268-7393, fax: (412) 268-7395
27 * innovation@andrew.cmu.edu
28 *
29 * 4. Redistributions of any form whatsoever must retain the following
30 * acknowledgment:
31 * "This product includes software developed by Computing Services
32 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
33 *
34 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
35 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
36 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
37 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
38 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
39 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
40 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
41 */
42
43 #include <config.h>
44
45 #include <stdio.h>
46 #include <sys/time.h>
47 #include <sys/types.h>
48 #include <sys/wait.h>
49 #ifdef HAVE_UNISTD_H
50 #include <unistd.h>
51 #endif
52 #include <fcntl.h>
53 #include <signal.h>
54 #include <sys/param.h>
55 #include <sys/stat.h>
56 #include <syslog.h>
57 #include <netdb.h>
58 #include <sys/socket.h>
59 #include <netinet/in.h>
60 #include <netinet/tcp.h>
61 #include <arpa/inet.h>
62 #include <errno.h>
63 #include <stdlib.h>
64 #include <sysexits.h>
65 #include <string.h>
66 #include <limits.h>
67
68 #include "service.h"
69 #include "libconfig.h"
70 #include "xmalloc.h"
71 #include "xstrlcpy.h"
72 #include "strarray.h"
73 #include "signals.h"
74 #include "util.h"
75
76 #ifndef PATH_MAX
77 #define PATH_MAX 4096
78 #endif
79
80 extern int optind, opterr;
81 extern char *optarg;
82
83 /* number of times this service has been used */
84 static int use_count = 0;
85 static int verbose = 0;
86 static int lockfd = -1;
87 static int newfile = 0;
88
notify_master(int fd,int msg)89 void notify_master(int fd, int msg)
90 {
91 struct notify_message notifymsg;
92 if (verbose) syslog(LOG_DEBUG, "telling master %x", msg);
93 notifymsg.message = msg;
94 notifymsg.service_pid = getpid();
95 if (write(fd, ¬ifymsg, sizeof(notifymsg)) != sizeof(notifymsg)) {
96 syslog(LOG_ERR, "unable to tell master %x: %m", msg);
97 }
98 }
99
100 #ifdef HAVE_LIBWRAP
101 #include <tcpd.h>
102
103 int allow_severity = LOG_DEBUG;
104 int deny_severity = LOG_ERR;
105
libwrap_init(struct request_info * req,char * service)106 static void libwrap_init(struct request_info *req, char *service)
107 {
108 request_init(req, RQ_DAEMON, service, 0);
109 }
110
libwrap_ask(struct request_info * req,int fd)111 static int libwrap_ask(struct request_info *req, int fd)
112 {
113 struct sockaddr_storage sin_storage;
114 struct sockaddr *sin = (struct sockaddr *)&sin_storage;
115 socklen_t sinlen;
116 int a;
117
118 /* XXX: old FreeBSD didn't fill sockaddr correctly against AF_UNIX */
119 sin->sa_family = AF_UNIX;
120
121 /* is this a connection from the local host? */
122 sinlen = sizeof(struct sockaddr_storage);
123 if (getpeername(fd, sin, &sinlen) == 0) {
124 if (sin->sa_family == AF_UNIX) {
125 return 1;
126 }
127 }
128
129 /* i hope using the sock_* functions are legal; it certainly makes
130 this code very easy! */
131 request_set(req, RQ_FILE, fd, 0);
132 sock_host(req);
133
134 a = hosts_access(req);
135 if (!a) {
136 syslog(deny_severity, "refused connection from %s", eval_client(req));
137 }
138
139 return a;
140 }
141
142 #else
143 struct request_info { int x; };
144
libwrap_init(struct request_info * r,char * service)145 static void libwrap_init(struct request_info *r __attribute__((unused)),
146 char *service __attribute__((unused)))
147 {
148
149 }
150
libwrap_ask(struct request_info * r,int fd)151 static int libwrap_ask(struct request_info *r __attribute__((unused)),
152 int fd __attribute__((unused)))
153 {
154 return 1;
155 }
156
157 #endif
158
159 extern void cyrus_init(const char *, const char *, unsigned, int);
160
getlockfd(char * service,int id)161 static int getlockfd(char *service, int id)
162 {
163 char lockfile[1024];
164 int fd;
165
166 snprintf(lockfile, sizeof(lockfile), "%s/socket/%s-%d.lock",
167 config_dir, service, id);
168 fd = open(lockfile, O_CREAT | O_RDWR, 0600);
169 if (fd < 0) {
170 syslog(LOG_ERR,
171 "locking disabled: couldn't open socket lockfile %s: %m",
172 lockfile);
173 lockfd = -1;
174 return -1;
175 }
176
177 lockfd = fd;
178 return 0;
179 }
180
lockaccept(void)181 static int lockaccept(void)
182 {
183 struct flock alockinfo;
184 int rc;
185
186 /* setup the alockinfo structure */
187 alockinfo.l_start = 0;
188 alockinfo.l_len = 0;
189 alockinfo.l_whence = SEEK_SET;
190
191 if (lockfd != -1) {
192 alockinfo.l_type = F_WRLCK;
193 while ((rc = fcntl(lockfd, F_SETLKW, &alockinfo)) < 0 &&
194 errno == EINTR &&
195 !signals_poll())
196 /* noop */;
197
198 if (rc < 0 && signals_poll()) {
199 if (MESSAGE_MASTER_ON_EXIT)
200 notify_master(STATUS_FD, MASTER_SERVICE_UNAVAILABLE);
201 service_abort(0);
202 return -1;
203 }
204
205 if (rc < 0) {
206 syslog(LOG_ERR, "fcntl: F_SETLKW: error getting accept lock: %m");
207 if (MESSAGE_MASTER_ON_EXIT)
208 notify_master(STATUS_FD, MASTER_SERVICE_UNAVAILABLE);
209 service_abort(EX_OSERR);
210 return -1;
211 }
212 }
213
214 return 0;
215 }
216
unlockaccept(void)217 static int unlockaccept(void)
218 {
219 struct flock alockinfo;
220 int rc;
221
222 /* setup the alockinfo structure */
223 alockinfo.l_start = 0;
224 alockinfo.l_len = 0;
225 alockinfo.l_whence = SEEK_SET;
226
227 if (lockfd != -1) {
228 alockinfo.l_type = F_UNLCK;
229 while ((rc = fcntl(lockfd, F_SETLKW, &alockinfo)) < 0 &&
230 errno == EINTR && !signals_poll())
231 /* noop */;
232
233 if (rc < 0) {
234 syslog(LOG_ERR,
235 "fcntl: F_SETLKW: error releasing accept lock: %m");
236 if (MESSAGE_MASTER_ON_EXIT)
237 notify_master(STATUS_FD, MASTER_SERVICE_UNAVAILABLE);
238 service_abort(EX_OSERR);
239 return -1;
240 }
241 }
242
243 return 0;
244 }
245
safe_wait_readable(int fd)246 static int safe_wait_readable(int fd)
247 {
248 fd_set rfds;
249 int r;
250
251 FD_ZERO(&rfds);
252 FD_SET(fd, &rfds);
253
254 /* Waiting for incoming connection, we want to leave as soon as
255 * possible upon SIGHUP. Julien explains:
256 *
257 * The thing is SIGHUP handler is set as restartable, which is a good thing
258 * when we have received a connection and are processing client commands:
259 * we don't want to be interrupted by that signal.
260 *
261 * On the other hand, when we are waiting to receive a new connection, I
262 * needed a way to make the service instance holding the lock react faster.
263 * Without resetting SIGHUP as not restartable, the instance would just
264 * keep on waiting for a new connection (while the other instances -
265 * waiting for the lock - had received and processed the signal right
266 * away).
267 *
268 * Now that we have safe_wait_readable, Linux systems already react faster
269 * because there select/pselect always returns -1/EINTR even if SA_RESTART
270 * is set. But that may not be the case in other OSes (POSIX spec says it
271 * is implementation-defined whether it does restart or return -1/EINTR
272 * when SA_RESTART is set).
273 */
274 signals_reset_sighup_handler(0);
275
276 r = signals_select(fd+1, &rfds, NULL, NULL, NULL);
277
278 /* we don't want to be interrupted by SIGHUP anymore */
279 signals_reset_sighup_handler(1);
280
281 return r;
282 }
283
main(int argc,char ** argv,char ** envp)284 int main(int argc, char **argv, char **envp)
285 {
286 int fdflags;
287 int fd;
288 char *p = NULL, *service;
289 struct request_info request;
290 int opt;
291 char *alt_config = NULL;
292 int call_debugger = 0;
293 int debug_stdio = 0;
294 int max_use = MAX_USE;
295 int reuse_timeout = REUSE_TIMEOUT;
296 int soctype;
297 socklen_t typelen = sizeof(soctype);
298 struct sockaddr socname;
299 socklen_t addrlen = sizeof(struct sockaddr);
300 int id;
301 char path[PATH_MAX];
302 struct stat sbuf;
303 ino_t start_ino;
304 off_t start_size;
305 time_t start_mtime;
306
307 /*
308 * service_init and service_main need argv and argc, so they can process
309 * service-specific options. They need argv[0] to point into the real argv
310 * memory space, so that setproctitle can work its magic. But they also
311 * need the generic options handled here to be removed, because they don't
312 * know how to handle them.
313 *
314 * So, we create a strarray_t "service_argv", and populate it with the
315 * options that we aren't handling here, using strarray_appendm (which
316 * simply ptr-copies its argument), and pass that through, and everything
317 * is happy.
318 *
319 * Note that we don't need to strarray_free service_argv, because it
320 * doesn't contain any malloced memory.
321 */
322 strarray_t service_argv = STRARRAY_INITIALIZER;
323 strarray_appendm(&service_argv, argv[0]);
324
325 opterr = 0; /* disable error reporting,
326 since we don't know about service-specific options */
327 while ((opt = getopt(argc, argv, "C:U:T:DX")) != EOF) {
328 if (argv[optind-1][0] == '-' && strlen(argv[optind-1]) > 2) {
329 /* we have merged options */
330 syslog(LOG_ERR,
331 "options and arguments MUST be separated by whitespace");
332 exit(EX_USAGE);
333 }
334
335 switch (opt) {
336 case 'C': /* alt config file */
337 alt_config = optarg;
338 break;
339 case 'U': /* maximum uses */
340 max_use = atoi(optarg);
341 if (max_use < 0) max_use = 0;
342 break;
343 case 'T': /* reuse timeout */
344 reuse_timeout = atoi(optarg);
345 if (reuse_timeout < 0) reuse_timeout = 0;
346 break;
347 case 'D':
348 call_debugger = 1;
349 break;
350 case 'X':
351 debug_stdio = 1;
352 break;
353 default:
354 strarray_appendm(&service_argv, argv[optind-1]);
355
356 /* option has an argument */
357 if (optind < argc && argv[optind][0] != '-')
358 strarray_appendm(&service_argv, argv[optind++]);
359
360 break;
361 }
362 }
363 /* grab the remaining arguments */
364 for (; optind < argc; optind++)
365 strarray_appendm(&service_argv, argv[optind]);
366
367 opterr = 1; /* enable error reporting */
368 optind = 1; /* reset the option index for parsing by the service */
369
370 p = getenv("CYRUS_VERBOSE");
371 if (p) verbose = atoi(p) + 1;
372
373 if (verbose > 30) {
374 syslog(LOG_DEBUG, "waiting 15 seconds for debugger");
375 sleep(15);
376 }
377
378 p = getenv("CYRUS_SERVICE");
379 if (p == NULL) {
380 syslog(LOG_ERR, "could not getenv(CYRUS_SERVICE); exiting");
381 exit(EX_SOFTWARE);
382 }
383 service = xstrdup(p);
384
385 p = getenv("CYRUS_ID");
386 if (p == NULL) {
387 syslog(LOG_ERR, "could not getenv(CYRUS_ID); exiting");
388 exit(EX_SOFTWARE);
389 }
390 id = atoi(p);
391
392 srand(time(NULL) * getpid());
393
394 /* if timeout is enabled, pick a random timeout between reuse_timeout
395 * and 2*reuse_timeout to avoid massive IO overload if the network
396 * connection goes away */
397 if (reuse_timeout)
398 reuse_timeout = reuse_timeout + (rand() % reuse_timeout);
399
400 extern const int config_need_data;
401 cyrus_init(alt_config, service, 0, config_need_data);
402
403 if (call_debugger) {
404 char debugbuf[1024];
405 int ret;
406 const char *debugger = config_getstring(IMAPOPT_DEBUG_COMMAND);
407 if (debugger) {
408 #pragma GCC diagnostic push
409 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
410 #pragma GCC diagnostic ignored "-Wformat-security"
411 /* This is exactly the kind of usage that -Wformat is designed to
412 * complain about (using user-supplied string as format argument),
413 * but in this case the "user" is the server administrator, and
414 * they're about to attach a debugger, so worrying about leaking
415 * contents of memory here is a little silly! :)
416 */
417 snprintf(debugbuf, sizeof(debugbuf), debugger,
418 argv[0], getpid(), service);
419 #pragma GCC diagnostic pop
420 syslog(LOG_DEBUG, "running external debugger: %s", debugbuf);
421 ret = system(debugbuf); /* run debugger */
422 syslog(LOG_DEBUG, "debugger returned exit status: %d", ret);
423 }
424 }
425 syslog(LOG_DEBUG, "executed");
426
427 if (debug_stdio) {
428 if (service_init(service_argv.count, service_argv.data, envp) != 0) {
429 return 1;
430 }
431 }
432 else {
433 /* set close on exec */
434 fdflags = fcntl(LISTEN_FD, F_GETFD, 0);
435 if (fdflags != -1) fdflags = fcntl(LISTEN_FD, F_SETFD,
436 fdflags | FD_CLOEXEC);
437 if (fdflags == -1) {
438 syslog(LOG_ERR, "unable to set close on exec: %m");
439 if (MESSAGE_MASTER_ON_EXIT)
440 notify_master(STATUS_FD, MASTER_SERVICE_UNAVAILABLE);
441 return 1;
442 }
443 fdflags = fcntl(STATUS_FD, F_GETFD, 0);
444 if (fdflags != -1) fdflags = fcntl(STATUS_FD, F_SETFD,
445 fdflags | FD_CLOEXEC);
446 if (fdflags == -1) {
447 syslog(LOG_ERR, "unable to set close on exec: %m");
448 if (MESSAGE_MASTER_ON_EXIT)
449 notify_master(STATUS_FD, MASTER_SERVICE_UNAVAILABLE);
450 return 1;
451 }
452
453 /* figure out what sort of socket this is */
454 if (getsockopt(LISTEN_FD, SOL_SOCKET, SO_TYPE,
455 (char *) &soctype, &typelen) < 0) {
456 syslog(LOG_ERR, "getsockopt: SOL_SOCKET: failed to get type: %m");
457 if (MESSAGE_MASTER_ON_EXIT)
458 notify_master(STATUS_FD, MASTER_SERVICE_UNAVAILABLE);
459 return 1;
460 }
461 if (getsockname(LISTEN_FD, &socname, &addrlen) < 0) {
462 syslog(LOG_ERR, "getsockname: failed: %m");
463 if (MESSAGE_MASTER_ON_EXIT)
464 notify_master(STATUS_FD, MASTER_SERVICE_UNAVAILABLE);
465 return 1;
466 }
467
468 if (service_init(service_argv.count, service_argv.data, envp) != 0) {
469 if (MESSAGE_MASTER_ON_EXIT)
470 notify_master(STATUS_FD, MASTER_SERVICE_UNAVAILABLE);
471 return 1;
472 }
473 }
474
475 /* determine initial process file inode, size and mtime */
476 if (service_argv.data[0][0] == '/')
477 strlcpy(path, service_argv.data[0], sizeof(path));
478 else
479 snprintf(path, sizeof(path), "%s/%s", LIBEXEC_DIR, service_argv.data[0]);
480
481 stat(path, &sbuf);
482 start_ino= sbuf.st_ino;
483 start_size = sbuf.st_size;
484 start_mtime = sbuf.st_mtime;
485
486 getlockfd(service, id);
487
488 if (debug_stdio) {
489 service_main(service_argv.count, service_argv.data, envp);
490 service_abort(0);
491 return 0;
492 }
493
494 for (;;) {
495 /* ok, listen to this socket until someone talks to us */
496
497 /* (re)set signal handlers, including SIGALRM */
498 signals_add_handlers(SIGALRM);
499
500 if (use_count > 0) {
501 /* we want to time out after 60 seconds, set an alarm */
502 alarm(reuse_timeout);
503 }
504
505 /* lock */
506 lockaccept();
507
508 fd = -1;
509 while (fd < 0 && !signals_poll()) { /* loop until we succeed */
510 /* check current process file inode, size and mtime */
511 int r = stat(path, &sbuf);
512 if (r < 0) {
513 /* This might happen transiently during a package
514 * upgrade or permanently after package removal.
515 * In either case, it's time to die. */
516 syslog(LOG_INFO, "cannot stat process file: %m");
517 break;
518 }
519 if (sbuf.st_ino != start_ino || sbuf.st_size != start_size ||
520 sbuf.st_mtime != start_mtime) {
521 syslog(LOG_INFO, "process file has changed");
522 newfile = 1;
523 break;
524 }
525
526 if (soctype == SOCK_STREAM) {
527 /* Wait for the file descriptor to be connected to, in a
528 * signal-safe manner. This ensures the accept() does
529 * not block and we don't need to make it signal-safe. */
530 if (safe_wait_readable(LISTEN_FD) < 0)
531 continue;
532 fd = accept(LISTEN_FD, NULL, NULL);
533 if (fd < 0) {
534 switch (errno) {
535 case EINTR:
536 signals_poll();
537 case ENETDOWN:
538 #ifdef EPROTO
539 case EPROTO:
540 #endif
541 case ENOPROTOOPT:
542 case EHOSTDOWN:
543 #ifdef ENONET
544 case ENONET:
545 #endif
546 case EHOSTUNREACH:
547 case EOPNOTSUPP:
548 case ENETUNREACH:
549 case ECONNABORTED:
550 case EAGAIN:
551 break;
552
553 case EINVAL:
554 if (signals_poll() == SIGHUP) break;
555 GCC_FALLTHROUGH
556
557 default:
558 syslog(LOG_ERR, "accept failed: %m");
559 if (MESSAGE_MASTER_ON_EXIT)
560 notify_master(STATUS_FD, MASTER_SERVICE_UNAVAILABLE);
561 service_abort(EX_OSERR);
562 }
563 }
564 } else {
565 /* udp */
566 struct sockaddr_storage from;
567 socklen_t fromlen;
568 char ch;
569 int r;
570
571 if (safe_wait_readable(LISTEN_FD) < 0)
572 continue;
573 fromlen = sizeof(from);
574 r = recvfrom(LISTEN_FD, (void *) &ch, 1, MSG_PEEK,
575 (struct sockaddr *) &from, &fromlen);
576 if (r == -1) {
577 if (signals_poll() == SIGHUP) break;
578 syslog(LOG_ERR, "recvfrom failed: %m");
579 if (MESSAGE_MASTER_ON_EXIT)
580 notify_master(STATUS_FD, MASTER_SERVICE_UNAVAILABLE);
581 service_abort(EX_OSERR);
582 }
583 fd = LISTEN_FD;
584 }
585 }
586
587 /* unlock */
588 unlockaccept();
589
590 if (fd < 0 && (signals_poll() || newfile)) {
591 /* timed out (SIGALRM), SIGHUP, or new process file */
592 if (MESSAGE_MASTER_ON_EXIT)
593 notify_master(STATUS_FD, MASTER_SERVICE_UNAVAILABLE);
594 service_abort(0);
595 }
596 if (fd < 0) {
597 /* how did this happen? - we might have caught a signal. */
598 syslog(LOG_ERR, "accept() failed but we didn't catch it?");
599 if (MESSAGE_MASTER_ON_EXIT)
600 notify_master(STATUS_FD, MASTER_SERVICE_UNAVAILABLE);
601 service_abort(EX_SOFTWARE);
602 }
603
604 /* cancel the alarm */
605 alarm(0);
606
607 /* tcp only */
608 if(soctype == SOCK_STREAM && socname.sa_family != AF_UNIX) {
609 libwrap_init(&request, service);
610
611 if (!libwrap_ask(&request, fd)) {
612 /* connection denied! */
613 shutdown(fd, SHUT_RDWR);
614 close(fd);
615 continue;
616 }
617
618 tcp_enable_keepalive(fd);
619 }
620
621 notify_master(STATUS_FD, MASTER_SERVICE_UNAVAILABLE);
622 syslog(LOG_DEBUG, "accepted connection");
623
624 if (fd != STDIN_FILENO && dup2(fd, STDIN_FILENO) < 0) {
625 syslog(LOG_ERR, "can't duplicate accepted socket: %m");
626 service_abort(EX_OSERR);
627 }
628 if (fd != STDOUT_FILENO && dup2(fd, STDOUT_FILENO) < 0) {
629 syslog(LOG_ERR, "can't duplicate accepted socket: %m");
630 service_abort(EX_OSERR);
631 }
632 #if 0 /* XXX This appears to have no valid use (and breaks wire protocols).
633 We should look into capturing stderr and sending it to syslog. */
634 if (fd != STDERR_FILENO && dup2(fd, STDERR_FILENO) < 0) {
635 syslog(LOG_ERR, "can't duplicate accepted socket: %m");
636 service_abort(EX_OSERR);
637 }
638 #endif
639
640 /* tcp only */
641 if(soctype == SOCK_STREAM) {
642 if (fd > STDERR_FILENO) close(fd);
643 }
644
645 notify_master(STATUS_FD, MASTER_SERVICE_CONNECTION);
646 use_count++;
647 service_main(service_argv.count, service_argv.data, envp);
648 /* if we returned, we can service another client with this process */
649
650 if (signals_poll() || use_count >= max_use) {
651 /* caught SIGHUP or exceeded max use count */
652 break;
653 }
654
655 notify_master(STATUS_FD, MASTER_SERVICE_AVAILABLE);
656 }
657
658 service_abort(0);
659 return 0;
660 }
661