]> arthur.barton.de Git - netatalk.git/blob - etc/netatalk/netatalk.c
Reliable shutdown of started services
[netatalk.git] / etc / netatalk / netatalk.c
1 /*
2  * Copyright (c) 1990,1993 Regents of The University of Michigan.
3  * All Rights Reserved.  See COPYRIGHT.
4  */
5
6 #ifdef HAVE_CONFIG_H
7 #include "config.h"
8 #endif /* HAVE_CONFIG_H */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <signal.h>
14 #include <sys/param.h>
15 #include <sys/uio.h>
16 #include <sys/time.h>
17 #include <sys/socket.h>
18 #include <sys/poll.h>
19 #include <errno.h>
20 #include <sys/wait.h>
21 #include <sys/resource.h>
22
23 #include <atalk/logger.h>
24 #include <atalk/adouble.h>
25 #include <atalk/compat.h>
26 #include <atalk/dsi.h>
27 #include <atalk/afp.h>
28 #include <atalk/paths.h>
29 #include <atalk/util.h>
30 #include <atalk/server_child.h>
31 #include <atalk/server_ipc.h>
32 #include <atalk/errchk.h>
33 #include <atalk/globals.h>
34 #include <atalk/netatalk_conf.h>
35
36 #include <event2/event.h>
37
38 /* how many seconds we wait to shutdown from SIGTERM before we send SIGKILL */
39 #define KILL_GRACETIME 5
40
41 /* forward declaration */
42 static pid_t run_process(const char *path, ...);
43 static void kill_childs(int count, int sig, ...);
44
45 /* static variables */
46 static AFPObj obj;
47 static sig_atomic_t got_chldsig;
48 static pid_t afpd_pid = -1,  cnid_metad_pid = -1;
49 static uint afpd_restarts, cnid_metad_restarts;
50 static struct event_base *base;
51 struct event *sigterm_ev, *sigquit_ev, *sigchld_ev;
52 static int in_shutdown;
53
54 /******************************************************************
55  * libevent helper functions
56  ******************************************************************/
57
58 /* libevent logging callback */
59 static void libevent_logmsg_cb(int severity, const char *msg)
60 {
61     switch (severity) {
62     case _EVENT_LOG_DEBUG:
63         LOG(log_debug, logtype_default, "libevent: %s", msg);
64         break;
65     case _EVENT_LOG_MSG:
66         LOG(log_info, logtype_default, "libevent: %s", msg);
67         break;
68     case _EVENT_LOG_WARN:
69         LOG(log_warning, logtype_default, "libevent: %s", msg);
70         break;
71     case _EVENT_LOG_ERR:
72         LOG(log_error, logtype_default, "libevent: %s", msg);
73         break;
74     default:
75         LOG(log_error, logtype_default, "libevent: %s", msg);
76         break; /* never reached */
77     }
78 }
79
80 /******************************************************************
81  * libevent event callbacks
82  ******************************************************************/
83
84 /* SIGTERM callback */
85 static void sigterm_cb(evutil_socket_t fd, short what, void *arg)
86 {
87     LOG(log_note, logtype_afpd, "Exiting on SIGTERM");
88     in_shutdown = 1;
89     event_base_loopbreak(base);
90 }
91
92 /* SIGQUIT callback */
93 static void sigquit_cb(evutil_socket_t fd, short what, void *arg)
94 {
95     LOG(log_note, logtype_afpd, "Exiting on SIGQUIT");
96     in_shutdown = 1;
97     event_base_loopbreak(base);
98 }
99
100 /* SIGCHLD callback */
101 static void sigchld_cb(evutil_socket_t fd, short what, void *arg)
102 {
103     int status, i;
104     pid_t pid;
105
106     LOG(log_debug, logtype_afpd, "Got SIGCHLD event");
107   
108     while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
109         if (WIFEXITED(status)) {
110             if (WEXITSTATUS(status))
111                 LOG(log_info, logtype_afpd, "child[%d]: exited %d", pid, WEXITSTATUS(status));
112             else
113                 LOG(log_info, logtype_afpd, "child[%d]: done", pid);
114         } else {
115             if (WIFSIGNALED(status))
116                 LOG(log_info, logtype_afpd, "child[%d]: killed by signal %d", pid, WTERMSIG(status));
117             else
118                 LOG(log_info, logtype_afpd, "child[%d]: died", pid);
119         }
120
121         if (pid == afpd_pid) {
122             if (in_shutdown) {
123                 afpd_pid = -1;
124             } else {
125                 sleep(1);
126                 afpd_restarts++;
127                 LOG(log_note, logtype_afpd, "Restarting 'afpd' (restarts: %u)", afpd_restarts);
128                 if ((afpd_pid = run_process(_PATH_AFPD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
129                     LOG(log_error, logtype_afpd, "Error starting 'afpd'");
130                 }
131             }
132         } else if (pid = cnid_metad_pid) {
133             if (in_shutdown) {
134                 cnid_metad_pid = -1;
135             } else {
136                 sleep(1);
137                 cnid_metad_restarts++;
138                 LOG(log_note, logtype_afpd, "Restarting 'cnid_metad' (restarts: %u)", cnid_metad_restarts);
139                 if ((cnid_metad_pid = run_process(_PATH_CNID_METAD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
140                     LOG(log_error, logtype_afpd, "Error starting 'cnid_metad'");
141                 }
142             }
143         } else {
144             LOG(log_error, logtype_afpd, "Bad pid: %d", pid);
145         }
146     }
147
148     if (in_shutdown && afpd_pid == -1 && cnid_metad_pid == -1)
149         event_base_loopbreak(base);
150 }
151
152 /******************************************************************
153  * helper functions
154  ******************************************************************/
155
156 /* kill "count" processes passed as varargs of type "pid_t *" */
157 static void kill_childs(int count, int sig, ...)
158 {
159     va_list args;
160     pid_t *pid;
161
162     va_start(args, sig);
163
164     while (count--) {
165         pid = va_arg(args, pid_t *);
166         if (*pid == -1)
167             continue;
168         kill(*pid, sig);
169     }
170     va_end(args);
171 }
172
173 /* this get called when error conditions are met that require us to exit gracefully */
174 static void netatalk_exit(int ret)
175 {
176     server_unlock(_PATH_NETATALK_LOCK);
177     exit(ret);
178 }
179
180 /* this forks() and exec() "path" with varags as argc[] */
181 static pid_t run_process(const char *path, ...)
182 {
183     int ret, i = 0;
184     char *myargv[10];
185     va_list args;
186     pid_t pid;
187
188     if ((pid = fork()) < 0) {
189         LOG(log_error, logtype_cnid, "error in fork: %s", strerror(errno));
190         return -1;
191     }
192
193     if (pid == 0) {
194         myargv[i++] = (char *)path;
195         va_start(args, path);
196         while ((myargv[i++] = va_arg(args, char *)) != NULL)
197             ;
198         va_end(args);
199
200         ret = execv(path, myargv);
201
202         /* Yikes! We're still here, so exec failed... */
203         LOG(log_error, logtype_cnid, "Fatal error in exec: %s", strerror(errno));
204         exit(1);
205     }
206     return pid;
207 }
208
209 static void usage(void)
210 {
211     printf("usage: netatalk [-F configfile] \n");
212 }
213
214 int main(int argc, char **argv)
215 {
216     const char *configfile = NULL;
217     int c, ret, debug = 0;
218     sigset_t blocksigs;
219
220     /* Log SIGBUS/SIGSEGV SBT */
221     fault_setup(NULL);
222
223     while ((c = getopt(argc, argv, ":dF:")) != -1) {
224         switch(c) {
225         case 'd':
226             debug = 1;
227             break;
228         case 'F':
229             obj.cmdlineconfigfile = strdup(optarg);
230             break;
231         default:
232             usage();
233             exit(EXIT_FAILURE);
234         }
235     }
236
237     if (check_lockfile("netatalk", _PATH_NETATALK_LOCK) != 0)
238         exit(EXITERR_SYS);
239
240     if (!debug && daemonize(0, 0) != 0)
241         exit(EXITERR_SYS);
242
243     if (create_lockfile("netatalk", _PATH_NETATALK_LOCK) != 0)
244         exit(EXITERR_SYS);
245
246     sigfillset(&blocksigs);
247     sigprocmask(SIG_SETMASK, &blocksigs, NULL);
248     
249     if (afp_config_parse(&obj) != 0)
250         netatalk_exit(EXITERR_CONF);
251
252     set_processname("netatalk");
253     setuplog(obj.options.logconfig, obj.options.logfile);
254     event_set_log_callback(libevent_logmsg_cb);
255     event_set_fatal_callback(netatalk_exit);
256
257     LOG(log_note, logtype_default, "Netatalk AFP server starting");
258
259     if ((afpd_pid = run_process(_PATH_AFPD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
260         LOG(log_error, logtype_afpd, "Error starting 'cnid_metad'");
261         netatalk_exit(EXITERR_CONF);
262     }
263
264     if ((cnid_metad_pid = run_process(_PATH_CNID_METAD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
265         LOG(log_error, logtype_afpd, "Error starting 'cnid_metad'");
266         netatalk_exit(EXITERR_CONF);
267     }
268
269     if ((base = event_base_new()) == NULL) {
270         LOG(log_error, logtype_afpd, "Error starting event loop");
271         netatalk_exit(EXITERR_CONF);
272     }
273
274     sigterm_ev = event_new(base, SIGTERM, EV_SIGNAL, sigterm_cb, NULL);
275     sigquit_ev = event_new(base, SIGQUIT, EV_SIGNAL, sigquit_cb, NULL);
276     sigchld_ev = event_new(base, SIGCHLD, EV_SIGNAL | EV_PERSIST, sigchld_cb, NULL);
277
278     event_add(sigterm_ev, NULL);
279     event_add(sigquit_ev, NULL);
280     event_add(sigchld_ev, NULL);
281
282     sigfillset(&blocksigs);
283     sigdelset(&blocksigs, SIGTERM);
284     sigdelset(&blocksigs, SIGQUIT);
285     sigdelset(&blocksigs, SIGCHLD);
286     sigprocmask(SIG_SETMASK, &blocksigs, NULL);
287
288     /* run the event loop */
289     ret = event_base_dispatch(base);
290
291     /* got SIGTERM or similar, so we're going to shutdown */
292
293     /* block any signal but SIGCHLD */
294     sigfillset(&blocksigs);
295     sigdelset(&blocksigs, SIGCHLD);
296     sigprocmask(SIG_SETMASK, &blocksigs, NULL);
297
298     /* setup new events: remove SIGTERM and SIGQUIT cbs, add timeout */
299     struct timeval tv;
300     tv.tv_sec = KILL_GRACETIME;
301     tv.tv_usec = 0;
302     event_base_loopexit(base, &tv);
303     event_del(sigterm_ev);
304     event_del(sigquit_ev);
305
306     /* run the event loop again, waiting for child to exit on SIGTERM for KILL_GRACETIME seconds */
307     kill_childs(2, SIGTERM, &afpd_pid, &cnid_metad_pid);
308     ret = event_base_dispatch(base);
309
310     if (afpd_pid != -1 || cnid_metad_pid != -1) {
311         if (afpd_pid != -1)
312             LOG(log_error, logtype_afpd, "AFP service did not shutdown, killing it");
313         if (cnid_metad_pid != -1)
314             LOG(log_error, logtype_afpd, "CNID database service did not shutdown, killing it");
315         kill_childs(2, SIGKILL, &afpd_pid, &cnid_metad_pid);
316     }
317     netatalk_exit(ret);
318 }