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 #include <atalk/bstrlib.h>
44 #include <atalk/bstradd.h>
45 #include "afp_zeroconf.h"
46
47 #include <event2/event.h>
48
49 /* how many seconds we wait to shutdown from SIGTERM before we send SIGKILL */
50 #define KILL_GRACETIME 5
51
52 /* defines that control whether services should run by default */
53 #define NETATALK_SRV_NEEDED -1
54 #define NETATALK_SRV_OPTIONAL 0
55 #define NETATALK_SRV_ERROR NETATALK_SRV_NEEDED
56
57 /* forward declaration */
58 static pid_t run_process(const char *path, ...);
59 static void kill_childs(int sig, ...);
60
61 /* static variables */
62 static AFPObj obj;
63 static pid_t afpd_pid = NETATALK_SRV_NEEDED;
64 static pid_t cnid_metad_pid = NETATALK_SRV_NEEDED;
65 static pid_t dbus_pid = NETATALK_SRV_OPTIONAL;
66 static uint afpd_restarts, cnid_metad_restarts, dbus_restarts;
67 static struct event_base *base;
68 struct event *sigterm_ev, *sigquit_ev, *sigchld_ev, *sighup_ev, *timer_ev;
69 static int in_shutdown;
70 static const char *dbus_path;
71
72 /******************************************************************
73 * Misc stuff
74 ******************************************************************/
75
service_running(pid_t pid)76 static bool service_running(pid_t pid)
77 {
78 if ((pid != NETATALK_SRV_NEEDED) && (pid != NETATALK_SRV_OPTIONAL))
79 return true;
80 return false;
81 }
82
83 /* Set Tracker Miners to index all our volumes */
set_sl_volumes(void)84 static int set_sl_volumes(void)
85 {
86 EC_INIT;
87 const struct vol *volumes, *vol;
88 struct bstrList *vollist = bstrListCreate();
89 bstring sep = bfromcstr(", ");
90 bstring volnamelist = NULL, cmd = NULL;
91
92 EC_NULL_LOG( volumes = getvolumes() );
93
94 for (vol = volumes; vol; vol = vol->v_next) {
95 if (vol->v_flags & AFPVOL_SPOTLIGHT) {
96 bstring volnamequot = bformat("'%s'", vol->v_path);
97 bstrListPush(vollist, volnamequot);
98 }
99 }
100
101 volnamelist = bjoin(vollist, sep);
102 cmd = bformat("gsettings set org.freedesktop.Tracker.Miner.Files index-recursive-directories \"[%s]\"",
103 bdata(volnamelist) ? bdata(volnamelist) : "");
104 LOG(log_debug, logtype_sl, "set_sl_volumes: %s", bdata(cmd));
105 system(bdata(cmd));
106
107 /* Disable default root user home indexing */
108 system("gsettings set org.freedesktop.Tracker.Miner.Files index-single-directories \"[]\"");
109
110 EC_CLEANUP:
111 if (cmd)
112 bdestroy(cmd);
113 if (sep)
114 bdestroy(sep);
115 if (vollist)
116 bstrListDestroy(vollist);
117 if (volnamelist)
118 bdestroy(volnamelist);
119 EC_EXIT;
120 }
121
122 /******************************************************************
123 * libevent helper functions
124 ******************************************************************/
125
126 /* libevent logging callback */
libevent_logmsg_cb(int severity,const char * msg)127 static void libevent_logmsg_cb(int severity, const char *msg)
128 {
129 switch (severity) {
130 case _EVENT_LOG_DEBUG:
131 LOG(log_debug, logtype_default, "libevent: %s", msg);
132 break;
133 case _EVENT_LOG_MSG:
134 LOG(log_info, logtype_default, "libevent: %s", msg);
135 break;
136 case _EVENT_LOG_WARN:
137 LOG(log_warning, logtype_default, "libevent: %s", msg);
138 break;
139 case _EVENT_LOG_ERR:
140 LOG(log_error, logtype_default, "libevent: %s", msg);
141 break;
142 default:
143 LOG(log_error, logtype_default, "libevent: %s", msg);
144 break; /* never reached */
145 }
146 }
147
148 /******************************************************************
149 * libevent event callbacks
150 ******************************************************************/
151
152 /* SIGTERM callback */
sigterm_cb(evutil_socket_t fd,short what,void * arg)153 static void sigterm_cb(evutil_socket_t fd, short what, void *arg)
154 {
155 sigset_t sigs;
156 struct timeval tv;
157
158 LOG(log_info, logtype_afpd, "Exiting on SIGTERM");
159
160 if (in_shutdown)
161 return;
162 in_shutdown = 1;
163
164 /* block any signal but SIGCHLD */
165 sigfillset(&sigs);
166 sigdelset(&sigs, SIGCHLD);
167 sigprocmask(SIG_SETMASK, &sigs, NULL);
168
169 /* add 10 sec timeout timer, remove all events but SIGCHLD */
170 tv.tv_sec = KILL_GRACETIME;
171 tv.tv_usec = 0;
172 event_base_loopexit(base, &tv);
173 event_del(sigterm_ev);
174 event_del(sigquit_ev);
175 event_del(sighup_ev);
176 event_del(timer_ev);
177
178 #ifdef HAVE_TRACKER
179 system(TRACKER_MANAGING_COMMAND " -t");
180 #endif
181 kill_childs(SIGTERM, &afpd_pid, &cnid_metad_pid, &dbus_pid, NULL);
182 }
183
184 /* SIGQUIT callback */
sigquit_cb(evutil_socket_t fd,short what,void * arg)185 static void sigquit_cb(evutil_socket_t fd, short what, void *arg)
186 {
187 LOG(log_note, logtype_afpd, "Exiting on SIGQUIT");
188 #ifdef HAVE_TRACKER
189 system(TRACKER_MANAGING_COMMAND " -t");
190 #endif
191 kill_childs(SIGQUIT, &afpd_pid, &cnid_metad_pid, &dbus_pid, NULL);
192 }
193
194 /* SIGHUP callback */
sighup_cb(evutil_socket_t fd,short what,void * arg)195 static void sighup_cb(evutil_socket_t fd, short what, void *arg)
196 {
197 LOG(log_note, logtype_afpd, "Received SIGHUP, sending all processes signal to reload config");
198
199 if (!(obj.options.flags & OPTION_NOZEROCONF)) {
200 zeroconf_deregister();
201 load_volumes(&obj, LV_ALL | LV_FORCE);
202 zeroconf_register(&obj);
203 LOG(log_note, logtype_default, "Re-registered with Zeroconf");
204 }
205
206 kill_childs(SIGHUP, &afpd_pid, &cnid_metad_pid, NULL);
207 }
208
209 /* SIGCHLD callback */
sigchld_cb(evutil_socket_t fd,short what,void * arg)210 static void sigchld_cb(evutil_socket_t fd, short what, void *arg)
211 {
212 int status;
213 pid_t pid;
214
215 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
216 if (WIFEXITED(status)) {
217 if (WEXITSTATUS(status))
218 LOG(log_info, logtype_default, "child[%d]: exited %d", pid, WEXITSTATUS(status));
219 else
220 LOG(log_info, logtype_default, "child[%d]: done", pid);
221 } else {
222 if (WIFSIGNALED(status))
223 LOG(log_info, logtype_default, "child[%d]: killed by signal %d", pid, WTERMSIG(status));
224 else
225 LOG(log_info, logtype_default, "child[%d]: died", pid);
226 }
227
228 if (pid == afpd_pid)
229 afpd_pid = NETATALK_SRV_ERROR;
230 else if (pid == cnid_metad_pid)
231 cnid_metad_pid = NETATALK_SRV_ERROR;
232 else if (pid == dbus_pid)
233 dbus_pid = NETATALK_SRV_ERROR;
234 else
235 LOG(log_error, logtype_afpd, "Bad pid: %d", pid);
236 }
237
238 if (in_shutdown
239 && !service_running(afpd_pid)
240 && !service_running(cnid_metad_pid)
241 && !service_running(dbus_pid)) {
242 event_base_loopbreak(base);
243 }
244 }
245
246 /* timer callback */
timer_cb(evutil_socket_t fd,short what,void * arg)247 static void timer_cb(evutil_socket_t fd, short what, void *arg)
248 {
249 if (in_shutdown)
250 return;
251
252 if (afpd_pid == NETATALK_SRV_NEEDED) {
253 afpd_restarts++;
254 LOG(log_note, logtype_afpd, "Restarting 'afpd' (restarts: %u)", afpd_restarts);
255 if ((afpd_pid = run_process(_PATH_AFPD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
256 LOG(log_error, logtype_default, "Error starting 'afpd'");
257 }
258 }
259
260 if (cnid_metad_pid == NETATALK_SRV_NEEDED) {
261 cnid_metad_restarts++;
262 LOG(log_note, logtype_afpd, "Restarting 'cnid_metad' (restarts: %u)", cnid_metad_restarts);
263 if ((cnid_metad_pid = run_process(_PATH_CNID_METAD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
264 LOG(log_error, logtype_default, "Error starting 'cnid_metad'");
265 }
266 }
267
268 #ifdef HAVE_TRACKER
269 if (dbus_pid == NETATALK_SRV_NEEDED) {
270 dbus_restarts++;
271 LOG(log_note, logtype_afpd, "Restarting 'dbus' (restarts: %u)", dbus_restarts);
272 if ((dbus_pid = run_process(dbus_path, "--config-file=" _PATH_CONFDIR "dbus-session.conf", NULL)) == -1) {
273 LOG(log_error, logtype_default, "Error starting '%s'", dbus_path);
274 }
275 }
276 #endif
277 }
278
279 /******************************************************************
280 * helper functions
281 ******************************************************************/
282
283 /* kill processes passed as varargs of type "pid_t *", terminate list with NULL */
kill_childs(int sig,...)284 static void kill_childs(int sig, ...)
285 {
286 va_list args;
287 pid_t *pid;
288
289 va_start(args, sig);
290
291 while ((pid = va_arg(args, pid_t *)) != NULL) {
292 if (*pid == NETATALK_SRV_ERROR || *pid == NETATALK_SRV_OPTIONAL)
293 continue;
294 kill(*pid, sig);
295 }
296 va_end(args);
297 }
298
299 /* this get called when error conditions are met that require us to exit gracefully */
netatalk_exit(int ret)300 static void netatalk_exit(int ret)
301 {
302 server_unlock(PATH_NETATALK_LOCK);
303 exit(ret);
304 }
305
306 /* this forks() and exec() "path" with varags as argc[] */
run_process(const char * path,...)307 static pid_t run_process(const char *path, ...)
308 {
309 int i = 0;
310 #define MYARVSIZE 64
311 char *myargv[MYARVSIZE];
312 va_list args;
313 pid_t pid;
314
315 if ((pid = fork()) < 0) {
316 LOG(log_error, logtype_cnid, "error in fork: %s", strerror(errno));
317 return -1;
318 }
319
320 if (pid == 0) {
321 myargv[i++] = (char *)path;
322 va_start(args, path);
323 while (i < MYARVSIZE) {
324 if ((myargv[i++] = va_arg(args, char *)) == NULL)
325 break;
326 }
327 va_end(args);
328
329 (void)execv(path, myargv);
330
331 /* Yikes! We're still here, so exec failed... */
332 LOG(log_error, logtype_cnid, "Fatal error in exec: %s", strerror(errno));
333 exit(1);
334 }
335 return pid;
336 }
337
show_netatalk_version(void)338 static void show_netatalk_version( void )
339 {
340 int num, i;
341
342 printf( "netatalk %s - Netatalk AFP server service controller daemon\n\n", VERSION );
343
344 puts( "This program is free software; you can redistribute it and/or modify it under" );
345 puts( "the terms of the GNU General Public License as published by the Free Software" );
346 puts( "Foundation; either version 2 of the License, or (at your option) any later" );
347 puts( "version. Please see the file COPYING for further information and details.\n" );
348
349 puts( "netatalk has been compiled with support for these features:\n" );
350
351 printf( " Zeroconf support:\t" );
352 #if defined (HAVE_MDNS)
353 puts( "mDNSResponder" );
354 #elif defined (HAVE_AVAHI)
355 puts( "Avahi" );
356 #else
357 puts( "No" );
358 #endif
359
360 printf( " Spotlight support:\t" );
361 #ifdef HAVE_TRACKER
362 puts( "Yes" );
363 #else
364 puts( "No" );
365 #endif
366
367 }
368
show_netatalk_paths(void)369 static void show_netatalk_paths( void )
370 {
371 printf( " afpd:\t%s\n", _PATH_AFPD);
372 printf( " cnid_metad:\t%s\n", _PATH_CNID_METAD);
373
374 #ifdef HAVE_TRACKER
375 printf( " tracker manager:\t%s\n", TRACKER_PREFIX "/bin/" TRACKER_MANAGING_COMMAND);
376 printf( " dbus-daemon:\t%s\n", DBUS_DAEMON_PATH);
377 #endif
378
379 printf( " afp.conf:\t%s\n", _PATH_CONFDIR "afp.conf");
380
381 #ifdef HAVE_TRACKER
382 printf( " dbus-session.conf:\t%s\n", _PATH_CONFDIR "dbus-session.conf");
383 #endif
384
385 #ifndef SOLARIS
386 printf( " netatalk lock file:\t%s\n", PATH_NETATALK_LOCK);
387 #endif
388
389 }
390
usage(void)391 static void usage(void)
392 {
393 printf("usage: netatalk [-F configfile] \n");
394 printf(" netatalk -v|-V \n");
395 }
396
main(int argc,char ** argv)397 int main(int argc, char **argv)
398 {
399 int c, ret, debug = 0;
400 sigset_t blocksigs;
401 struct timeval tv;
402
403 /* Log SIGBUS/SIGSEGV SBT */
404 fault_setup(NULL);
405
406 while ((c = getopt(argc, argv, ":dF:vV")) != -1) {
407 switch(c) {
408 case 'd':
409 debug = 1;
410 break;
411 case 'F':
412 obj.cmdlineconfigfile = strdup(optarg);
413 break;
414 case 'v': /* version */
415 case 'V': /* version */
416 show_netatalk_version( ); puts( "" );
417 show_netatalk_paths( ); puts( "" );
418 exit( 0 );
419 break;
420 default:
421 usage();
422 exit(EXIT_FAILURE);
423 }
424 }
425
426 if (check_lockfile("netatalk", PATH_NETATALK_LOCK) != 0)
427 exit(EXITERR_SYS);
428
429 if (!debug && daemonize(0, 0) != 0)
430 exit(EXITERR_SYS);
431
432 if (create_lockfile("netatalk", PATH_NETATALK_LOCK) != 0)
433 exit(EXITERR_SYS);
434
435 sigfillset(&blocksigs);
436 sigprocmask(SIG_SETMASK, &blocksigs, NULL);
437
438 if (afp_config_parse(&obj, "netatalk") != 0)
439 netatalk_exit(EXITERR_CONF);
440
441 load_volumes(&obj, LV_ALL);
442
443 event_set_log_callback(libevent_logmsg_cb);
444 event_set_fatal_callback(netatalk_exit);
445
446 LOG(log_note, logtype_default, "Netatalk AFP server starting");
447
448 if ((afpd_pid = run_process(_PATH_AFPD, "-d", "-F", obj.options.configfile, NULL)) == NETATALK_SRV_ERROR) {
449 LOG(log_error, logtype_afpd, "Error starting 'afpd'");
450 netatalk_exit(EXITERR_CONF);
451 }
452
453 if ((cnid_metad_pid = run_process(_PATH_CNID_METAD, "-d", "-F", obj.options.configfile, NULL)) == NETATALK_SRV_ERROR) {
454 LOG(log_error, logtype_afpd, "Error starting 'cnid_metad'");
455 netatalk_exit(EXITERR_CONF);
456 }
457
458 if ((base = event_base_new()) == NULL) {
459 LOG(log_error, logtype_afpd, "Error starting event loop");
460 netatalk_exit(EXITERR_CONF);
461 }
462
463 sigterm_ev = event_new(base, SIGTERM, EV_SIGNAL, sigterm_cb, NULL);
464 sigquit_ev = event_new(base, SIGQUIT, EV_SIGNAL | EV_PERSIST, sigquit_cb, NULL);
465 sighup_ev = event_new(base, SIGHUP, EV_SIGNAL | EV_PERSIST, sighup_cb, NULL);
466 sigchld_ev = event_new(base, SIGCHLD, EV_SIGNAL | EV_PERSIST, sigchld_cb, NULL);
467 timer_ev = event_new(base, -1, EV_PERSIST, timer_cb, NULL);
468
469 tv.tv_sec = 1;
470 tv.tv_usec = 0;
471
472 event_add(sigterm_ev, NULL);
473 event_add(sigquit_ev, NULL);
474 event_add(sigchld_ev, NULL);
475 event_add(sighup_ev, NULL);
476 event_add(timer_ev, &tv);
477
478 sigfillset(&blocksigs);
479 sigdelset(&blocksigs, SIGTERM);
480 sigdelset(&blocksigs, SIGQUIT);
481 sigdelset(&blocksigs, SIGCHLD);
482 sigdelset(&blocksigs, SIGHUP);
483 sigprocmask(SIG_SETMASK, &blocksigs, NULL);
484
485 #ifdef HAVE_TRACKER
486 if (obj.options.flags & OPTION_SPOTLIGHT) {
487 setenv("DBUS_SESSION_BUS_ADDRESS", "unix:path=" _PATH_STATEDIR "spotlight.ipc", 1);
488 setenv("XDG_DATA_HOME", _PATH_STATEDIR, 0);
489 setenv("XDG_CACHE_HOME", _PATH_STATEDIR, 0);
490 setenv("TRACKER_USE_LOG_FILES", "1", 0);
491
492 if (atalk_iniparser_getboolean(obj.iniconfig, INISEC_GLOBAL, "start dbus", 1)) {
493 dbus_path = atalk_iniparser_getstring(obj.iniconfig, INISEC_GLOBAL, "dbus daemon", DBUS_DAEMON_PATH);
494 LOG(log_note, logtype_default, "Starting dbus: %s", dbus_path);
495 if ((dbus_pid = run_process(dbus_path, "--config-file=" _PATH_CONFDIR "dbus-session.conf", NULL)) == NETATALK_SRV_ERROR) {
496 LOG(log_error, logtype_default, "Error starting '%s'", dbus_path);
497 netatalk_exit(EXITERR_CONF);
498 }
499
500 /* Allow dbus some time to start up */
501 sleep(1);
502 }
503
504 set_sl_volumes();
505
506 if (atalk_iniparser_getboolean(obj.iniconfig, INISEC_GLOBAL, "start tracker", 1)) {
507 LOG(log_note, logtype_default, "Starting Tracker: " TRACKER_PREFIX "/bin/" TRACKER_MANAGING_COMMAND " -s");
508 system(TRACKER_PREFIX "/bin/" TRACKER_MANAGING_COMMAND " -s");
509 }
510 }
511 #endif
512
513 /* Now register with zeroconf, we also need the volumes for that */
514 if (! (obj.options.flags & OPTION_NOZEROCONF)) {
515 zeroconf_register(&obj);
516 LOG(log_note, logtype_default, "Registered with Zeroconf");
517 }
518
519 /* run the event loop */
520 ret = event_base_dispatch(base);
521
522 if (service_running(afpd_pid) || service_running(cnid_metad_pid) || service_running(dbus_pid)) {
523 if (service_running(afpd_pid))
524 LOG(log_error, logtype_afpd, "AFP service did not shutdown, killing it");
525 if (service_running(cnid_metad_pid))
526 LOG(log_error, logtype_afpd, "CNID database service did not shutdown, killing it");
527 if (service_running(dbus_pid))
528 LOG(log_error, logtype_afpd, "DBUS session daemon still running, killing it");
529 kill_childs(SIGKILL, &afpd_pid, &cnid_metad_pid, &dbus_pid, NULL);
530 }
531
532 LOG(log_note, logtype_afpd, "Netatalk AFP server exiting");
533
534 netatalk_exit(ret);
535 }
536