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