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