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