]> arthur.barton.de Git - netatalk.git/blob - etc/netatalk/netatalk.c
Fix SIGHUP config reloading
[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 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, *timer_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     sigset_t sigs;
88     struct timeval tv;
89
90     LOG(log_info, logtype_afpd, "Exiting on SIGTERM");
91
92     if (in_shutdown)
93         return;
94     in_shutdown = 1;
95
96     /* block any signal but SIGCHLD */
97     sigfillset(&sigs);
98     sigdelset(&sigs, SIGCHLD);
99     sigprocmask(SIG_SETMASK, &sigs, NULL);
100
101     /* add 10 sec timeout timer, remove all events but SIGCHLD */
102     tv.tv_sec = KILL_GRACETIME;
103     tv.tv_usec = 0;
104     event_base_loopexit(base, &tv);
105     event_del(sigterm_ev);
106     event_del(sigquit_ev);
107     event_del(timer_ev);
108
109     kill_childs(SIGTERM, &afpd_pid, &cnid_metad_pid, NULL);
110 }
111
112 /* SIGQUIT callback */
113 static void sigquit_cb(evutil_socket_t fd, short what, void *arg)
114 {
115     LOG(log_note, logtype_afpd, "Exiting on SIGQUIT");
116     kill_childs(SIGQUIT, &afpd_pid, &cnid_metad_pid, NULL);
117 }
118
119 /* SIGQUIT callback */
120 static void sighup_cb(evutil_socket_t fd, short what, void *arg)
121 {
122     LOG(log_note, logtype_afpd, "Received SIGHUP, sending all processes signal to reload config");
123     kill_childs(SIGHUP, &afpd_pid, &cnid_metad_pid, NULL);
124 }
125
126 /* SIGCHLD callback */
127 static void sigchld_cb(evutil_socket_t fd, short what, void *arg)
128 {
129     int status, i;
130     pid_t pid;
131
132     LOG(log_debug, logtype_afpd, "Got SIGCHLD event");
133   
134     while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
135         if (WIFEXITED(status)) {
136             if (WEXITSTATUS(status))
137                 LOG(log_info, logtype_afpd, "child[%d]: exited %d", pid, WEXITSTATUS(status));
138             else
139                 LOG(log_info, logtype_afpd, "child[%d]: done", pid);
140         } else {
141             if (WIFSIGNALED(status))
142                 LOG(log_info, logtype_afpd, "child[%d]: killed by signal %d", pid, WTERMSIG(status));
143             else
144                 LOG(log_info, logtype_afpd, "child[%d]: died", pid);
145         }
146
147         if (pid == afpd_pid)
148             afpd_pid = -1;
149         else if (pid = cnid_metad_pid)
150             cnid_metad_pid = -1;
151         else
152             LOG(log_error, logtype_afpd, "Bad pid: %d", pid);
153     }
154
155     if (in_shutdown && afpd_pid == -1 && cnid_metad_pid == -1)
156         event_base_loopbreak(base);
157 }
158
159 /* timer callback */
160 static void timer_cb(evutil_socket_t fd, short what, void *arg)
161 {
162     static int i = 0;
163
164     if (in_shutdown)
165         return;
166
167     if (afpd_pid == -1) {
168         afpd_restarts++;
169         LOG(log_note, logtype_afpd, "Restarting 'afpd' (restarts: %u)", afpd_restarts);
170         if ((afpd_pid = run_process(_PATH_AFPD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
171             LOG(log_error, logtype_afpd, "Error starting 'afpd'");
172         }
173     }
174
175     if (cnid_metad_pid == -1) {
176         cnid_metad_restarts++;
177         LOG(log_note, logtype_afpd, "Restarting 'cnid_metad' (restarts: %u)", cnid_metad_restarts);
178         if ((cnid_metad_pid = run_process(_PATH_CNID_METAD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
179             LOG(log_error, logtype_afpd, "Error starting 'cnid_metad'");
180         }
181     }
182 }
183
184 /******************************************************************
185  * helper functions
186  ******************************************************************/
187
188 /* kill processes passed as varargs of type "pid_t *", terminate list with NULL */
189 static void kill_childs(int sig, ...)
190 {
191     va_list args;
192     pid_t *pid;
193
194     va_start(args, sig);
195
196     while ((pid = va_arg(args, pid_t *)) != NULL) {
197         if (*pid == -1)
198             continue;
199         kill(*pid, sig);
200     }
201     va_end(args);
202 }
203
204 /* this get called when error conditions are met that require us to exit gracefully */
205 static void netatalk_exit(int ret)
206 {
207     server_unlock(_PATH_NETATALK_LOCK);
208     exit(ret);
209 }
210
211 /* this forks() and exec() "path" with varags as argc[] */
212 static pid_t run_process(const char *path, ...)
213 {
214     int ret, i = 0;
215     char *myargv[10];
216     va_list args;
217     pid_t pid;
218
219     if ((pid = fork()) < 0) {
220         LOG(log_error, logtype_cnid, "error in fork: %s", strerror(errno));
221         return -1;
222     }
223
224     if (pid == 0) {
225         myargv[i++] = (char *)path;
226         va_start(args, path);
227         while ((myargv[i++] = va_arg(args, char *)) != NULL)
228             ;
229         va_end(args);
230
231         ret = execv(path, myargv);
232
233         /* Yikes! We're still here, so exec failed... */
234         LOG(log_error, logtype_cnid, "Fatal error in exec: %s", strerror(errno));
235         exit(1);
236     }
237     return pid;
238 }
239
240 static void usage(void)
241 {
242     printf("usage: netatalk [-F configfile] \n");
243 }
244
245 int main(int argc, char **argv)
246 {
247     const char *configfile = NULL;
248     int c, ret, debug = 0;
249     sigset_t blocksigs;
250     struct timeval tv;
251
252     /* Log SIGBUS/SIGSEGV SBT */
253     fault_setup(NULL);
254
255     while ((c = getopt(argc, argv, ":dF:")) != -1) {
256         switch(c) {
257         case 'd':
258             debug = 1;
259             break;
260         case 'F':
261             obj.cmdlineconfigfile = strdup(optarg);
262             break;
263         default:
264             usage();
265             exit(EXIT_FAILURE);
266         }
267     }
268
269     if (check_lockfile("netatalk", _PATH_NETATALK_LOCK) != 0)
270         exit(EXITERR_SYS);
271
272     if (!debug && daemonize(0, 0) != 0)
273         exit(EXITERR_SYS);
274
275     if (create_lockfile("netatalk", _PATH_NETATALK_LOCK) != 0)
276         exit(EXITERR_SYS);
277
278     sigfillset(&blocksigs);
279     sigprocmask(SIG_SETMASK, &blocksigs, NULL);
280     
281     if (afp_config_parse(&obj, "netatalk") != 0)
282         netatalk_exit(EXITERR_CONF);
283
284     event_set_log_callback(libevent_logmsg_cb);
285     event_set_fatal_callback(netatalk_exit);
286
287     LOG(log_note, logtype_default, "Netatalk AFP server starting");
288
289     if ((afpd_pid = run_process(_PATH_AFPD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
290         LOG(log_error, logtype_afpd, "Error starting 'cnid_metad'");
291         netatalk_exit(EXITERR_CONF);
292     }
293
294     if ((cnid_metad_pid = run_process(_PATH_CNID_METAD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
295         LOG(log_error, logtype_afpd, "Error starting 'cnid_metad'");
296         netatalk_exit(EXITERR_CONF);
297     }
298
299     if ((base = event_base_new()) == NULL) {
300         LOG(log_error, logtype_afpd, "Error starting event loop");
301         netatalk_exit(EXITERR_CONF);
302     }
303
304     sigterm_ev = event_new(base, SIGTERM, EV_SIGNAL, sigterm_cb, NULL);
305     sigquit_ev = event_new(base, SIGQUIT, EV_SIGNAL | EV_PERSIST, sigquit_cb, NULL);
306     sigquit_ev = event_new(base, SIGHUP,  EV_SIGNAL | EV_PERSIST, sighup_cb, NULL);
307     sigchld_ev = event_new(base, SIGCHLD, EV_SIGNAL | EV_PERSIST, sigchld_cb, NULL);
308     timer_ev = event_new(base, -1, EV_PERSIST, timer_cb, NULL);
309
310     tv.tv_sec = 1;
311     tv.tv_usec = 0;
312
313     event_add(sigterm_ev, NULL);
314     event_add(sigquit_ev, NULL);
315     event_add(sigchld_ev, NULL);
316     event_add(timer_ev, &tv);
317
318     sigfillset(&blocksigs);
319     sigdelset(&blocksigs, SIGTERM);
320     sigdelset(&blocksigs, SIGQUIT);
321     sigdelset(&blocksigs, SIGCHLD);
322     sigdelset(&blocksigs, SIGHUP);
323     sigprocmask(SIG_SETMASK, &blocksigs, NULL);
324
325     /* run the event loop */
326     ret = event_base_dispatch(base);
327
328     if (afpd_pid != -1 || cnid_metad_pid != -1) {
329         if (afpd_pid != -1)
330             LOG(log_error, logtype_afpd, "AFP service did not shutdown, killing it");
331         if (cnid_metad_pid != -1)
332             LOG(log_error, logtype_afpd, "CNID database service did not shutdown, killing it");
333         kill_childs(SIGKILL, &afpd_pid, &cnid_metad_pid, NULL);
334     }
335
336     LOG(log_note, logtype_afpd, "Netatalk AFP server exiting");
337
338     netatalk_exit(ret);
339 }