2 * Copyright (c) 2014 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * Handle remote listen/connect and parsing operations.
40 pthread_mutex_t serial_mtx;
41 time_t LastStart; /* uptime */
42 time_t LastStop; /* uptime */
44 runstate_t RunState = RS_STOPPED;
49 execute_init(command_t *cmd)
53 pid_t stoppingpid = -1;
55 int lfd; /* unix domain listen socket */
56 int pfd; /* pid file */
62 if (cmd->label == NULL || cmd->ext_ac == 0) {
63 fprintf(cmd->fp, "init requires a label and command\n");
66 fprintf(cmd->fp, "initializing new service: %s\n", cmd->label);
69 fprintf(cmd->fp, "Unable to create pipe: %s\n",
73 if ((xfd = open("/dev/null", O_RDWR)) < 0) {
74 fprintf(cmd->fp, "Unable to open /dev/null: %s\n",
80 * Setup pidfile and unix domain listen socket and lock the
83 rc = setup_pid_and_socket(cmd, &lfd, &pfd);
93 if ((pid = fork()) != 0) {
99 fprintf(cmd->fp, "fork failed: %s\n", strerror(errno));
102 * Fill-in pfd before returning.
104 snprintf(buf, sizeof(buf), "%d\n", (int)pid);
105 write(pfd, buf, strlen(buf));
111 * Wait for child to completely detach from the tty
121 * Service demon is now running.
123 * Detach from terminal, setup logfile.
128 cmd->fp = fopen(cmd->logfile, "a");
130 cmd->fp = fdopen(dup(xfd), "a");
135 dup2(fileno(cmd->fp), 1);
136 dup2(fileno(cmd->fp), 2);
137 if ((xfd = open("/dev/tty", O_RDWR)) >= 0) {
138 ioctl(xfd, TIOCNOTTY, 0); /* no controlling tty */
141 setsid(); /* new session */
144 * Signal parent that we are completely detached now.
147 write(fds[1], &c, 1);
152 * Start accept thread for unix domain listen socket.
155 remote_listener(cmd, lfd);
156 pthread_mutex_lock(&serial_mtx);
160 * Become the reaper for all children recursively.
162 if (procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL) < 0) {
163 fprintf(cmd->fp, "svc is unable to become the "
164 "reaper for its children\n");
169 * Main loop is the reaper
172 union reaper_info info;
179 * If we are running just block doing normal reaping,
180 * if we are stopping we have to poll for reaping while
181 * we handle stopping.
183 if (RunState == RS_STARTED) {
184 pthread_mutex_unlock(&serial_mtx);
185 pid = wait3(&status, 0, NULL);
186 pthread_mutex_lock(&serial_mtx);
188 pid = wait3(&status, WNOHANG, NULL);
190 clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
193 if (pid == DirectPid) {
195 "svc %s: lost direct child %d\n",
199 if (cmd->restart_some) {
200 RunState = RS_STOPPED;
201 LastStop = ts.tv_sec;
202 } /* else still considered normal run state */
204 /* reap random disconnected child */
206 "svc %s: reap indirect child %d\n",
213 * Calculate the pid to potentially act on and/or
214 * determine if any children still exist.
216 if (DirectPid >= 0) {
218 } else if (procctl(P_PID, getpid(),
219 PROC_REAP_STATUS, &info) == 0) {
220 usepid = info.status.pid_head;
226 * If stoppingpid changes we have to reset the TERM->KILL
230 if (RunState != RS_STOPPED) {
231 RunState = RS_STOPPED;
232 LastStop = ts.tv_sec;
234 } else if (stoppingpid != usepid &&
235 (RunState == RS_STOPPING2 ||
236 RunState == RS_STOPPING3)) {
237 RunState = RS_STOPPING1;
239 stoppingpid = usepid;
247 RunState = RS_STOPPED;
248 LastStop = ts.tv_sec;
252 dt = (int)(ts.tv_sec - LastStop);
254 if (cmd->exit_mode) {
256 * Service demon was told to exit on service
257 * stop (-x passed to init).
260 "svc %s: service demon exiting\n",
263 } else if (cmd->manual_stop) {
265 * Service demon was told to stop via
266 * commanded (not automatic) action. We
267 * do not auto-restart the service in
270 pthread_mutex_unlock(&serial_mtx);
271 if (dt < 0 || dt > 60)
275 pthread_mutex_lock(&serial_mtx);
276 } else if (cmd->restart_some || cmd->restart_all) {
278 * Handle automatic restarts
280 if (dt > cmd->restart_timo) {
283 pthread_mutex_unlock(&serial_mtx);
285 pthread_mutex_lock(&serial_mtx);
289 * No automatic restart was configured,
290 * wait for commanded action.
292 pthread_mutex_unlock(&serial_mtx);
293 if (dt < 0 || dt > 60)
297 pthread_mutex_lock(&serial_mtx);
302 * Reset TERM->KILL timer
304 nextstop = ts.tv_sec;
305 RunState = RS_STOPPING2;
308 if (cmd->termkill_timo == 0) {
309 nextstop = ts.tv_sec - 1;
311 kill(stoppingpid, SIGTERM);
312 fprintf(cmd->fp, "svc %s: sigterm %d\n",
313 cmd->label, stoppingpid);
316 RunState = RS_STOPPING3;
319 dt = (int)(ts.tv_sec - nextstop);
320 if (dt > cmd->termkill_timo) {
321 fprintf(cmd->fp, "svc %s: sigkill %d\n",
322 cmd->label, stoppingpid);
323 kill(stoppingpid, SIGKILL);
329 pthread_mutex_unlock(&serial_mtx);
331 /* does not return */
335 execute_start(command_t *cmd)
340 while (RunState == RS_STOPPING1 ||
341 RunState == RS_STOPPING2 ||
342 RunState == RS_STOPPING3) {
344 "svc %s: Waiting for previous action to complete\n",
347 pthread_mutex_unlock(&serial_mtx);
349 pthread_mutex_lock(&serial_mtx);
350 if (--maxwait == 0) {
352 "svc %s: Giving up waiting for action\n",
358 if (RunState == RS_STARTED) {
359 fprintf(cmd->fp, "svc %s: Already started pid %d\n",
360 cmd->label, DirectPid);
365 clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
366 if ((DirectPid = fork()) == 0) {
368 execvp(InitCmd->ext_av[0], InitCmd->ext_av);
371 if (DirectPid >= 0) {
372 RunState = RS_STARTED;
373 LastStart = ts.tv_sec;
375 RunState = RS_STOPPED;
376 LastStop = ts.tv_sec;
378 InitCmd->manual_stop = 0;
379 fprintf(cmd->fp, "svc %s: Starting pid %d\n", cmd->label, DirectPid);
386 execute_restart(command_t *cmd)
390 rc = execute_stop(cmd) + execute_start(cmd);
395 execute_stop(command_t *cmd)
398 int save_restart_some;
399 int save_restart_all;
402 save_restart_some = InitCmd->restart_some;
403 save_restart_all = InitCmd->restart_all;
405 InitCmd->manual_stop = 1;
406 if (cmd->commanded && (cmd->restart_some || cmd->restart_all)) {
407 InitCmd->restart_some = cmd->restart_some;
408 InitCmd->restart_all = cmd->restart_all;
410 fprintf(cmd->fp, "svc %s: Stopping\n", cmd->label);
413 if (DirectPid >= 0) {
414 kill(DirectPid, SIGTERM);
417 clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
418 LastStop = ts.tv_sec;
419 RunState = RS_STOPPING1;
422 * If commanded (verses automatic), we are running remote in our
423 * own thread and we need to wait for the action to complete.
425 if (cmd->commanded) {
426 while (RunState == RS_STOPPING1 ||
427 RunState == RS_STOPPING2 ||
428 RunState == RS_STOPPING3) {
430 "svc %s: Waiting for service to stop\n",
433 pthread_mutex_unlock(&serial_mtx);
435 pthread_mutex_lock(&serial_mtx);
436 if (--maxwait == 0) {
438 "svc %s: Giving up waiting for stop\n",
444 if (cmd->restart_some || cmd->restart_all) {
445 InitCmd->restart_some = save_restart_some;
446 InitCmd->restart_all = save_restart_all;
454 execute_exit(command_t *cmd)
457 fprintf(cmd->fp, "svc %s: Exiting\n", cmd->label);
463 execute_list(command_t *cmd)
465 fprintf(cmd->fp, "%-16s\n", cmd->label);
471 execute_status(command_t *cmd)
477 if (InitCmd && InitCmd->manual_stop)
478 state = "stopped (manual)";
495 fprintf(cmd->fp, "%-16s %s\n", cmd->label, state);
501 execute_log(command_t *cmd __unused)
507 execute_logfile(command_t *cmd)
513 logfile = cmd->logfile;
514 if (cmd->ext_av && cmd->ext_av[0])
515 logfile = cmd->ext_av[0];
516 if (logfile == NULL && InitCmd)
517 logfile = InitCmd->logfile;
520 if (InitCmd && logfile) {
521 if (InitCmd->logfile &&
522 strcmp(InitCmd->logfile, logfile) == 0) {
523 fprintf(cmd->fp, "svc %s: Reopen logfile %s\n",
524 cmd->label, logfile);
526 fprintf(cmd->fp, "svc %s: Change logfile to %s\n",
527 cmd->label, logfile);
529 fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0640);
533 dup2(fd, fileno(stdout));
534 dup2(fd, fileno(stderr));
535 sreplace(&InitCmd->logfile, logfile);
539 "svc %s: Unable to open/create \"%s\": %s\n",
540 cmd->label, logfile, strerror(errno));