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