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