mirror of
https://github.com/troglobit/finit.git
synced 2026-11-09 16:21:24 +00:00
Refactor: move advanced service methods from svc.c --> service.c
This patch refactors svc.c into two files: svc.c now as a low-level svc_t API and service.c for the more advanced rest. Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
1 parent
8e9a5ee678
commit
97a48ec5fb
12 files changed
+1024
-861
No files matched your search
@@ -36,7 +36,7 @@ ARCHIVEZ = ../$(ARCHIVE).xz
|
|||||||
EXEC = finit reboot
|
EXEC = finit reboot
|
||||||
HEADERS = plugin.h svc.h helpers.h queue.h
|
HEADERS = plugin.h svc.h helpers.h queue.h
|
||||||
DISTFILES = LICENSE README CHANGELOG finit.conf services
|
DISTFILES = LICENSE README CHANGELOG finit.conf services
|
||||||
OBJS = finit.o conf.o helpers.o sig.o svc.o plugin.o tty.o inetd.o
|
OBJS = finit.o conf.o helpers.o sig.o svc.o service.o plugin.o tty.o inetd.o
|
||||||
|
|
||||||
TOPDIR = $(shell pwd)
|
TOPDIR = $(shell pwd)
|
||||||
-include config.mk
|
-include config.mk
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "svc.h"
|
#include "service.h"
|
||||||
#include "tty.h"
|
#include "tty.h"
|
||||||
#include "libite/lite.h"
|
#include "libite/lite.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
@@ -165,7 +165,7 @@ static void parse_static(char *line)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (MATCH_CMD(line, "startx ", x)) {
|
if (MATCH_CMD(line, "startx ", x)) {
|
||||||
svc_register(SVC_TYPE_SERVICE, strip_line(x), 0, username);
|
service_register(SVC_TYPE_SERVICE, strip_line(x), 0, username);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,27 +216,27 @@ static void parse_dynamic(char *line, time_t mtime)
|
|||||||
* long as the (optional) service callback returns
|
* long as the (optional) service callback returns
|
||||||
* non-zero */
|
* non-zero */
|
||||||
if (MATCH_CMD(line, "service ", x)) {
|
if (MATCH_CMD(line, "service ", x)) {
|
||||||
svc_register(SVC_TYPE_SERVICE, x, mtime, NULL);
|
service_register(SVC_TYPE_SERVICE, x, mtime, NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* One-shot task, will not be respawned. Only runs if
|
/* One-shot task, will not be respawned. Only runs if
|
||||||
* the (optional) service callback returns true */
|
* the (optional) service callback returns true */
|
||||||
if (MATCH_CMD(line, "task ", x)) {
|
if (MATCH_CMD(line, "task ", x)) {
|
||||||
svc_register(SVC_TYPE_TASK, x, mtime, NULL);
|
service_register(SVC_TYPE_TASK, x, mtime, NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Like task but waits for completion, useful w/ [S] */
|
/* Like task but waits for completion, useful w/ [S] */
|
||||||
if (MATCH_CMD(line, "run ", x)) {
|
if (MATCH_CMD(line, "run ", x)) {
|
||||||
svc_register(SVC_TYPE_RUN, x, mtime, NULL);
|
service_register(SVC_TYPE_RUN, x, mtime, NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Classic inetd service */
|
/* Classic inetd service */
|
||||||
if (MATCH_CMD(line, "inetd ", x)) {
|
if (MATCH_CMD(line, "inetd ", x)) {
|
||||||
#ifndef INETD_DISABLED
|
#ifndef INETD_DISABLED
|
||||||
svc_register(SVC_TYPE_INETD, x, mtime, NULL);
|
service_register(SVC_TYPE_INETD, x, mtime, NULL);
|
||||||
#else
|
#else
|
||||||
_e("Finit built with inetd support disabled, cannot register service inetd %s!", x);
|
_e("Finit built with inetd support disabled, cannot register service inetd %s!", x);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
#include "svc.h"
|
#include "service.h"
|
||||||
#include "sig.h"
|
#include "sig.h"
|
||||||
#include "tty.h"
|
#include "tty.h"
|
||||||
#include "libite/lite.h"
|
#include "libite/lite.h"
|
||||||
@@ -245,7 +245,7 @@ int main(int argc, char* argv[])
|
|||||||
/*
|
/*
|
||||||
* Start all bootstrap tasks, no network available!
|
* Start all bootstrap tasks, no network available!
|
||||||
*/
|
*/
|
||||||
svc_bootstrap();
|
service_bootstrap();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Network stuff
|
* Network stuff
|
||||||
@@ -267,7 +267,7 @@ int main(int argc, char* argv[])
|
|||||||
* tasks/services in the configured runlevel
|
* tasks/services in the configured runlevel
|
||||||
*/
|
*/
|
||||||
parse_finit_d(rcsd);
|
parse_finit_d(rcsd);
|
||||||
svc_runlevel(cfglevel);
|
service_runlevel(cfglevel);
|
||||||
|
|
||||||
_d("Running svc up hooks ...");
|
_d("Running svc up hooks ...");
|
||||||
plugin_run_hooks(HOOK_SVC_UP);
|
plugin_run_hooks(HOOK_SVC_UP);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
#include "svc.h"
|
#include "service.h"
|
||||||
|
|
||||||
#define ENABLE_SOCKOPT(sd, level, opt) \
|
#define ENABLE_SOCKOPT(sd, level, opt) \
|
||||||
do { \
|
do { \
|
||||||
@@ -49,13 +49,13 @@ static void socket_cb(uev_ctx_t *UNUSED(ctx), uev_t *w, void *arg, int UNUSED(ev
|
|||||||
{
|
{
|
||||||
svc_t *svc = (svc_t *)arg;
|
svc_t *svc = (svc_t *)arg;
|
||||||
|
|
||||||
if (SVC_START != svc_enabled(svc, -1, NULL))
|
if (SVC_START != service_enabled(svc, -1, NULL))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!svc->inetd.forking)
|
if (!svc->inetd.forking)
|
||||||
uev_io_stop(w);
|
uev_io_stop(w);
|
||||||
|
|
||||||
svc_start(svc);
|
service_start(svc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Launch Inet socket for service.
|
/* Launch Inet socket for service.
|
||||||
@@ -174,12 +174,12 @@ int inetd_stream_peek(int sd, char *ifname)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Inetd monitor, called by svc_monitor() */
|
/* Inetd monitor, called by service_monitor() */
|
||||||
int inetd_respawn(pid_t pid)
|
int inetd_respawn(pid_t pid)
|
||||||
{
|
{
|
||||||
svc_t *svc = svc_find_by_pid(pid);
|
svc_t *svc = svc_find_by_pid(pid);
|
||||||
|
|
||||||
if (svc && SVC_TYPE_INETD == svc->type) {
|
if (svc_is_inetd(svc)) {
|
||||||
inetd_t *inetd = &svc->inetd;
|
inetd_t *inetd = &svc->inetd;
|
||||||
|
|
||||||
svc->pid = 0;
|
svc->pid = 0;
|
||||||
@@ -213,7 +213,7 @@ void inetd_stop(inetd_t *inetd)
|
|||||||
|
|
||||||
/* Stop any running service, not allowed anymore. */
|
/* Stop any running service, not allowed anymore. */
|
||||||
if (svc->pid)
|
if (svc->pid)
|
||||||
svc_stop(svc);
|
service_stop(svc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getent(char *service, char *proto, struct servent **sv, struct protoent **pv)
|
static int getent(char *service, char *proto, struct servent **sv, struct protoent **pv)
|
||||||
@@ -407,7 +407,7 @@ int inetd_match(inetd_t *inetd, char *service, char *proto, char *port)
|
|||||||
* before the specific (eth0:222), the new inetd service must find the
|
* before the specific (eth0:222), the new inetd service must find the
|
||||||
* (any!) previous rule and add its ifname to their deny list.
|
* (any!) previous rule and add its ifname to their deny list.
|
||||||
*
|
*
|
||||||
* If equivalent service exists already svc_register() will instead call
|
* If equivalent service exists already service_register() will instead call
|
||||||
* inetd_allow().
|
* inetd_allow().
|
||||||
*/
|
*/
|
||||||
int inetd_new(inetd_t *inetd, char *service, char *proto, int forking)
|
int inetd_new(inetd_t *inetd, char *service, char *proto, int forking)
|
||||||
@@ -477,13 +477,9 @@ int inetd_init(inetd_t *inetd, void *arg, char *ifname, char *port)
|
|||||||
result = inetd_allow(inetd, ifname);
|
result = inetd_allow(inetd, ifname);
|
||||||
|
|
||||||
/* For each similar service, on other port, add their ifnames as deny to ours. */
|
/* For each similar service, on other port, add their ifnames as deny to ours. */
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) {
|
||||||
inetd_filter_t *filter;
|
inetd_filter_t *filter;
|
||||||
|
|
||||||
/* Skip non-inetd services */
|
|
||||||
if (svc->type != SVC_TYPE_INETD)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Skip ourselves */
|
/* Skip ourselves */
|
||||||
if (&svc->inetd == inetd)
|
if (&svc->inetd == inetd)
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
+2
-1
@@ -33,6 +33,7 @@
|
|||||||
#include "../plugin.h"
|
#include "../plugin.h"
|
||||||
#include "../sig.h"
|
#include "../sig.h"
|
||||||
#include "../finit.h"
|
#include "../finit.h"
|
||||||
|
#include "../service.h"
|
||||||
|
|
||||||
static void parse(void *arg, int fd, int events);
|
static void parse(void *arg, int fd, int events);
|
||||||
|
|
||||||
@@ -102,7 +103,7 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
|
|||||||
case '1'...'5':
|
case '1'...'5':
|
||||||
case '7'...'9':
|
case '7'...'9':
|
||||||
_d("Setting new runlevel %c", rq.runlevel);
|
_d("Setting new runlevel %c", rq.runlevel);
|
||||||
svc_runlevel(rq.runlevel - '0');
|
service_runlevel(rq.runlevel - '0');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '6':
|
case '6':
|
||||||
|
|||||||
@@ -30,8 +30,8 @@
|
|||||||
|
|
||||||
uev_ctx_t *ctx; /* Main loop context */
|
uev_ctx_t *ctx; /* Main loop context */
|
||||||
|
|
||||||
void svc_bootstrap (void);
|
void service_bootstrap(void);
|
||||||
void svc_monitor (pid_t lost);
|
void service_monitor (pid_t lost);
|
||||||
|
|
||||||
void plugin_run_hooks (hook_point_t no);
|
void plugin_run_hooks (hook_point_t no);
|
||||||
int plugin_load_all (uev_ctx_t *ctx, char *path);
|
int plugin_load_all (uev_ctx_t *ctx, char *path);
|
||||||
|
|||||||
@@ -0,0 +1,821 @@
|
|||||||
|
/* Finit service monitor, task starter and generic API for managing svc_t
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
|
* Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <utmp.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "helpers.h"
|
||||||
|
#include "private.h"
|
||||||
|
#include "sig.h"
|
||||||
|
#include "tty.h"
|
||||||
|
#include "libite/lite.h"
|
||||||
|
#include "service.h"
|
||||||
|
#include "inetd.h"
|
||||||
|
|
||||||
|
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
|
||||||
|
|
||||||
|
static int is_norespawn (void);
|
||||||
|
static void restart_lost_procs (void);
|
||||||
|
static void svc_dance (svc_t *svc);
|
||||||
|
static void utmp_save (int pre, int now);
|
||||||
|
static svc_t *find_inetd_svc (char *path, char *service, char *proto, char *port);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* service_bootstrap - Start bootstrap services and tasks
|
||||||
|
*
|
||||||
|
* System startup, runlevel S, where only services, tasks and
|
||||||
|
* run commands absolutely essential to bootstrap are located.
|
||||||
|
*/
|
||||||
|
void service_bootstrap(void)
|
||||||
|
{
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
|
_d("Bootstrapping all services in runlevel S from %s", FINIT_CONF);
|
||||||
|
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||||
|
svc_cmd_t cmd;
|
||||||
|
|
||||||
|
/* Inetd services cannot be part of bootstrap currently. */
|
||||||
|
if (svc_is_inetd(svc))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cmd = service_enabled(svc, 0, NULL);
|
||||||
|
if (SVC_START == cmd || (SVC_RELOAD == cmd))
|
||||||
|
service_start(svc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* service_enabled - Should the service run?
|
||||||
|
* @svc: Pointer to &svc_t object
|
||||||
|
* @event: Dynamic event, opaque flag passed to callback
|
||||||
|
* @arg: Event argument, used only by external service plugins.
|
||||||
|
*
|
||||||
|
* This method calls an associated service callback, if registered by a
|
||||||
|
* plugin, and returns the &svc_cmd_t status. If no plugin is registered
|
||||||
|
* the service is statically enabled in /etc/finit.conf and the result
|
||||||
|
* will always be %SVC_START.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Either one of %SVC_START, %SVC_STOP, %SVC_RELOAD.
|
||||||
|
*/
|
||||||
|
svc_cmd_t service_enabled(svc_t *svc, int event, void *arg)
|
||||||
|
{
|
||||||
|
if (!svc) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return SVC_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ISSET(svc->runlevels, runlevel))
|
||||||
|
return SVC_STOP;
|
||||||
|
|
||||||
|
/* Is there a service plugin registered? */
|
||||||
|
if (svc->cb) {
|
||||||
|
int status;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
/* Let callback run in separate process so it doesn't crash PID 1 */
|
||||||
|
pid = fork();
|
||||||
|
if (-1 == pid) {
|
||||||
|
_pe("Failed in %s callback", svc->cmd);
|
||||||
|
return SVC_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pid) {
|
||||||
|
status = svc->cb(svc, event, arg);
|
||||||
|
exit(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waitpid(pid, &status, 0) == -1) {
|
||||||
|
_pe("Failed reading status from %s callback", svc->cmd);
|
||||||
|
return SVC_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback normally exits here. */
|
||||||
|
if (WIFEXITED(status))
|
||||||
|
return WEXITSTATUS(status);
|
||||||
|
|
||||||
|
/* Check for SEGFAULT or other error ... */
|
||||||
|
if (WCOREDUMP(status))
|
||||||
|
_e("Callback to %s crashed!\n", svc->cmd);
|
||||||
|
else
|
||||||
|
_e("Callback to %s did not exit normally!\n", svc->cmd);
|
||||||
|
|
||||||
|
return SVC_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No service plugin, default to start, since listed in finit.conf */
|
||||||
|
return SVC_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remember: service_enabled() must be called before calling service_start() */
|
||||||
|
int service_start(svc_t *svc)
|
||||||
|
{
|
||||||
|
int respawn, sd = 0;
|
||||||
|
pid_t pid;
|
||||||
|
sigset_t nmask, omask;
|
||||||
|
|
||||||
|
if (!svc)
|
||||||
|
return 0;
|
||||||
|
respawn = svc->pid != 0;
|
||||||
|
|
||||||
|
/* Don't try and start service if it doesn't exist. */
|
||||||
|
if (!fexist(svc->cmd) && !svc->inetd.cmd) {
|
||||||
|
char msg[80];
|
||||||
|
|
||||||
|
snprintf(msg, sizeof(msg), "Service %s does not exist!", svc->cmd);
|
||||||
|
print_desc("", msg);
|
||||||
|
print_result(1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ignore if finit is SIGSTOP'ed */
|
||||||
|
if (is_norespawn())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#ifndef INETD_DISABLED
|
||||||
|
if (svc_is_inetd(svc)) {
|
||||||
|
char ifname[IF_NAMESIZE] = "UNKNOWN";
|
||||||
|
|
||||||
|
sd = svc->inetd.watcher.fd;
|
||||||
|
|
||||||
|
if (svc->inetd.type == SOCK_STREAM) {
|
||||||
|
/* Open new client socket from server socket */
|
||||||
|
sd = accept(sd, NULL, NULL);
|
||||||
|
if (sd < 0) {
|
||||||
|
FLOG_PERROR("Failed accepting inetd service %d/tcp", svc->inetd.port);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_d("New client socket %d accepted for inetd service %d/tcp", sd, svc->inetd.port);
|
||||||
|
|
||||||
|
/* Find ifname by means of getsockname() and getifaddrs() */
|
||||||
|
inetd_stream_peek(sd, ifname);
|
||||||
|
} else { /* SOCK_DGRAM */
|
||||||
|
/* Find ifname by means of IP_PKTINFO sockopt --> ifindex + if_indextoname() */
|
||||||
|
inetd_dgram_peek(sd, ifname);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inetd_is_allowed(&svc->inetd, ifname)) {
|
||||||
|
FLOG_INFO("Service %s on port %d not allowed from interface %s.",
|
||||||
|
svc->inetd.name, svc->inetd.port, ifname);
|
||||||
|
if (svc->inetd.type == SOCK_STREAM)
|
||||||
|
close(sd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FLOG_INFO("Starting inetd service %s for requst from iface %s ...", svc->inetd.name, ifname);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
if (svc_is_daemon(svc))
|
||||||
|
print_desc("", svc->desc);
|
||||||
|
else if (!respawn)
|
||||||
|
print_desc("Starting ", svc->desc);
|
||||||
|
|
||||||
|
/* Block sigchild while forking. */
|
||||||
|
sigemptyset(&nmask);
|
||||||
|
sigaddset(&nmask, SIGCHLD);
|
||||||
|
sigprocmask(SIG_BLOCK, &nmask, &omask);
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||||
|
if (pid == 0) {
|
||||||
|
int i = 0;
|
||||||
|
int status;
|
||||||
|
#ifdef ENABLE_STATIC
|
||||||
|
int uid = 0; /* XXX: Fix better warning that dropprivs is disabled. */
|
||||||
|
#else
|
||||||
|
int uid = getuser(svc->username);
|
||||||
|
#endif
|
||||||
|
struct sigaction sa;
|
||||||
|
char *args[MAX_NUM_SVC_ARGS];
|
||||||
|
|
||||||
|
sigemptyset(&nmask);
|
||||||
|
sigaddset(&nmask, SIGCHLD);
|
||||||
|
sigprocmask(SIG_UNBLOCK, &nmask, NULL);
|
||||||
|
|
||||||
|
/* Reset signal handlers that were set by the parent process */
|
||||||
|
for (i = 1; i < NSIG; i++)
|
||||||
|
DFLSIG(sa, i, 0);
|
||||||
|
|
||||||
|
/* Set desired user */
|
||||||
|
if (uid >= 0)
|
||||||
|
setuid(uid);
|
||||||
|
|
||||||
|
/* Serve copy of args to process in case it modifies them. */
|
||||||
|
for (i = 0; i < (MAX_NUM_SVC_ARGS - 1) && svc->args[i][0] != 0; i++)
|
||||||
|
args[i] = svc->args[i];
|
||||||
|
args[i] = NULL;
|
||||||
|
|
||||||
|
/* Redirect inetd socket to stdin for service */
|
||||||
|
if (svc_is_inetd(svc)) {
|
||||||
|
/* sd set previously */
|
||||||
|
dup2(sd, STDIN_FILENO);
|
||||||
|
close(sd);
|
||||||
|
dup2(STDIN_FILENO, STDOUT_FILENO);
|
||||||
|
dup2(STDIN_FILENO, STDERR_FILENO);
|
||||||
|
} else if (debug) {
|
||||||
|
int fd;
|
||||||
|
char buf[CMD_SIZE] = "";
|
||||||
|
|
||||||
|
fd = open(CONSOLE, O_WRONLY | O_APPEND);
|
||||||
|
if (-1 != fd) {
|
||||||
|
dup2(fd, STDOUT_FILENO);
|
||||||
|
dup2(fd, STDERR_FILENO);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_NUM_SVC_ARGS && args[i]; i++) {
|
||||||
|
char arg[MAX_ARG_LEN];
|
||||||
|
|
||||||
|
snprintf(arg, sizeof(arg), "%s ", args[i]);
|
||||||
|
if (strlen(arg) < (sizeof(buf) - strlen(buf)))
|
||||||
|
strcat(buf, arg);
|
||||||
|
}
|
||||||
|
_e("%starting %s: %s", respawn ? "Res" : "S", svc->cmd, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (svc->inetd.cmd)
|
||||||
|
status = svc->inetd.cmd(svc->inetd.type);
|
||||||
|
else
|
||||||
|
status = execv(svc->cmd, args); /* XXX: Maybe use execve() to be able to launch scripts? */
|
||||||
|
|
||||||
|
if (svc_is_inetd(svc)) {
|
||||||
|
if (svc->inetd.type == SOCK_STREAM) {
|
||||||
|
close(STDIN_FILENO);
|
||||||
|
close(STDOUT_FILENO);
|
||||||
|
close(STDERR_FILENO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(status);
|
||||||
|
}
|
||||||
|
svc->pid = pid;
|
||||||
|
|
||||||
|
if (svc_is_inetd(svc)) {
|
||||||
|
if (svc->inetd.type == SOCK_STREAM)
|
||||||
|
close(sd);
|
||||||
|
}
|
||||||
|
else if (SVC_TYPE_RUN == svc->type)
|
||||||
|
print_result(WEXITSTATUS(complete(svc->cmd, pid)));
|
||||||
|
else if (!respawn)
|
||||||
|
print_result(svc->pid > 1 ? 0 : 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int service_stop(svc_t *svc)
|
||||||
|
{
|
||||||
|
int res = 1;
|
||||||
|
|
||||||
|
if (!svc) {
|
||||||
|
_e("Failed, no svc pointer.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (svc->pid <= 1) {
|
||||||
|
_d("Bad PID %d for %s, SIGTERM", svc->pid, svc->desc);
|
||||||
|
res = 1;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SVC_TYPE_SERVICE != svc->type)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (runlevel != 1)
|
||||||
|
print_desc("Stopping ", svc->desc);
|
||||||
|
|
||||||
|
_d("Sending SIGTERM to pid:%d name:%s", svc->pid, pid_get_name(svc->pid, NULL, 0));
|
||||||
|
res = kill(svc->pid, SIGTERM);
|
||||||
|
|
||||||
|
if (runlevel != 1)
|
||||||
|
print_result(res);
|
||||||
|
exit:
|
||||||
|
svc->pid = 0;
|
||||||
|
svc->restart_counter = 0;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* service_reload - Send SIGHUP to a service
|
||||||
|
* @svc: Service to reload
|
||||||
|
*
|
||||||
|
* This function does some basic checks of the runtime state of Finit
|
||||||
|
* and a sanity check of the @svc before sending %SIGHUP.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* POSIX OK(0) or non-zero on error.
|
||||||
|
*/
|
||||||
|
int service_reload(svc_t *svc)
|
||||||
|
{
|
||||||
|
/* Ignore if finit is SIGSTOP'ed */
|
||||||
|
if (is_norespawn())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!svc) {
|
||||||
|
_e("Failed, no svc pointer.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (svc->pid <= 1) {
|
||||||
|
_d("Bad PID %d for %s, SIGHUP", svc->pid, svc->cmd);
|
||||||
|
svc->pid = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_d("Sending SIGHUP to PID %d", svc->pid);
|
||||||
|
return kill(svc->pid, SIGHUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* service_reload_dynamic - Reload modifued dynamic services
|
||||||
|
*
|
||||||
|
* This function is called when Finit has recieved SIGHUP to reload
|
||||||
|
* .conf files in /etc/finit.d. It is responsible for starting,
|
||||||
|
* stopping and reloading (forwarding SIGHUP) to processes affected.
|
||||||
|
*/
|
||||||
|
void service_reload_dynamic(void)
|
||||||
|
{
|
||||||
|
int num = 0;
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
|
_d("Stopping disabled/removed services ...");
|
||||||
|
for (svc = svc_dynamic_iterator(1); svc; svc = svc_dynamic_iterator(0)) {
|
||||||
|
if (svc->dirty != 0) {
|
||||||
|
service_stop(svc);
|
||||||
|
num++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!num)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_d("All disabled/removed services have been stoppped, calling reconf hooks ...");
|
||||||
|
plugin_run_hooks(HOOK_SVC_RECONF); /* Reconfigure HW/VLANs/etc here */
|
||||||
|
|
||||||
|
_d("Starting enabled/added services ...");
|
||||||
|
for (svc = svc_dynamic_iterator(1); svc; svc = svc_dynamic_iterator(0)) {
|
||||||
|
if (svc->dirty > 0)
|
||||||
|
svc_dance(svc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* service_runlevel - Change to a new runlevel
|
||||||
|
* @newlevel: New runlevel to activate
|
||||||
|
*
|
||||||
|
* Stops all services not in @newlevel and starts, or lets continue to run,
|
||||||
|
* those in @newlevel. Also updates @prevlevel and active @runlevel.
|
||||||
|
*/
|
||||||
|
void service_runlevel(int newlevel)
|
||||||
|
{
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
|
if (runlevel == newlevel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (newlevel < 0 || newlevel > 9)
|
||||||
|
return;
|
||||||
|
|
||||||
|
prevlevel = runlevel;
|
||||||
|
runlevel = newlevel;
|
||||||
|
|
||||||
|
_d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel);
|
||||||
|
utmp_save(prevlevel, newlevel);
|
||||||
|
|
||||||
|
/* Make sure to reload all *.conf in /etc/finit.d/ */
|
||||||
|
svc_mark_dynamic();
|
||||||
|
parse_finit_d(rcsd);
|
||||||
|
|
||||||
|
_d("Stopping services services not allowed in new runlevel ...");
|
||||||
|
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||||
|
if (!ISSET(svc->runlevels, runlevel)) {
|
||||||
|
#ifndef INETD_DISABLED
|
||||||
|
if (svc_is_inetd(svc))
|
||||||
|
inetd_stop(&svc->inetd);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
service_stop(svc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ... or disabled/removed services from /etc/finit.d/ */
|
||||||
|
if (svc->mtime && svc->dirty != 0)
|
||||||
|
service_stop(svc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prev runlevel services stopped, call hooks before starting new runlevel ... */
|
||||||
|
_d("All services have been stoppped, calling runlevel change hooks ...");
|
||||||
|
plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */
|
||||||
|
|
||||||
|
_d("Starting services services new to this runlevel ...");
|
||||||
|
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||||
|
#ifndef INETD_DISABLED
|
||||||
|
/* Inetd services have slightly different semantics */
|
||||||
|
if (svc_is_inetd(svc)) {
|
||||||
|
if (ISSET(svc->runlevels, runlevel))
|
||||||
|
inetd_start(&svc->inetd);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* All other services consult their callback here */
|
||||||
|
svc_dance(svc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cleanup stale services */
|
||||||
|
svc_clean_dynamic(service_unregister);
|
||||||
|
|
||||||
|
if (0 == runlevel) {
|
||||||
|
do_shutdown(SIGUSR2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (6 == runlevel) {
|
||||||
|
do_shutdown(SIGUSR1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runlevel == 1)
|
||||||
|
touch("/etc/nologin"); /* Disable login in single-user mode */
|
||||||
|
else
|
||||||
|
erase("/etc/nologin");
|
||||||
|
|
||||||
|
if (0 != prevlevel)
|
||||||
|
tty_runlevel(runlevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* service_register - Register service, task or run commands
|
||||||
|
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
|
||||||
|
* @line: A complete command line with -- separated description text
|
||||||
|
* @mtime: The modification time if service is loaded from /etc/finit.d
|
||||||
|
* @username: Optional username to run service as, or %NULL to run as root
|
||||||
|
*
|
||||||
|
* This function is used to register commands to be run on different
|
||||||
|
* system runlevels with optional username. The @type argument details
|
||||||
|
* if it's service to bo monitored/respawned (daemon), a one-shot task
|
||||||
|
* or a command that must run in sequence and not in parallell, like
|
||||||
|
* service and task commands do.
|
||||||
|
*
|
||||||
|
* The @line can optionally start with a username, denoted by an @
|
||||||
|
* character. Like this:
|
||||||
|
*
|
||||||
|
* service @username [!0-6,S] /path/to/daemon arg -- Description
|
||||||
|
* task @username [!0-6,S] /path/to/task arg -- Description
|
||||||
|
* run @username [!0-6,S] /path/to/cmd arg -- Description
|
||||||
|
* inetd tcp/ssh nowait [2345] @root:root /sbin/sshd -i -- Description
|
||||||
|
*
|
||||||
|
* If the username is left out the command is started as root. The []
|
||||||
|
* brackets denote the allowed runlevels, if left out the default for a
|
||||||
|
* service is set to [2-5]. Allowed runlevels mimic that of SysV init
|
||||||
|
* with the addition of the 'S' runlevel, which is only run once at
|
||||||
|
* startup. It can be seen as the system bootstrap. If a task or run
|
||||||
|
* command is listed in more than the [S] runlevel they will be called
|
||||||
|
* when changing runlevel.
|
||||||
|
*
|
||||||
|
* For multiple instances of the same command, e.g. multiple DHCP
|
||||||
|
* clients, the user may enter an optional ID, using the #ID syntax.
|
||||||
|
*
|
||||||
|
* service #1 /sbin/udhcpc -i eth1
|
||||||
|
* service #2 /sbin/udhcpc -i eth2
|
||||||
|
*
|
||||||
|
* Without the #ID syntax Finit will overwrite the first service line
|
||||||
|
* with the contents of the second. The #ID must be [1,MAXINT].
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* POSIX OK(0) on success, or non-zero errno exit status on failure.
|
||||||
|
*/
|
||||||
|
int service_register(int type, char *line, time_t mtime, char *username)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
int id = 1; /* Default to ID:1 */
|
||||||
|
#ifndef INETD_DISABLED
|
||||||
|
int forking = 0;
|
||||||
|
#endif
|
||||||
|
char *service = NULL, *proto = NULL, *iface = NULL, *port = NULL;
|
||||||
|
char *cmd, *desc, *runlevels = NULL;
|
||||||
|
svc_t *svc;
|
||||||
|
plugin_t *plugin = NULL;
|
||||||
|
|
||||||
|
if (!line) {
|
||||||
|
_e("Invalid input argument.");
|
||||||
|
return errno = EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
desc = strstr(line, "-- ");
|
||||||
|
if (desc)
|
||||||
|
*desc = 0;
|
||||||
|
|
||||||
|
cmd = strtok(line, " ");
|
||||||
|
if (!cmd) {
|
||||||
|
incomplete:
|
||||||
|
_e("Incomplete service, cannot register.");
|
||||||
|
return errno = ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (cmd) {
|
||||||
|
if (cmd[0] != '/' && strchr(cmd, '/'))
|
||||||
|
service = cmd; /* inetd service/proto */
|
||||||
|
#ifndef INETD_DISABLED
|
||||||
|
else if (!strncasecmp(cmd, "nowait", 6))
|
||||||
|
forking = 1;
|
||||||
|
else if (!strncasecmp(cmd, "wait", 4))
|
||||||
|
forking = 0;
|
||||||
|
#endif
|
||||||
|
else if (cmd[0] == '@') /* @username[:group] */
|
||||||
|
username = &cmd[1];
|
||||||
|
else if (cmd[0] == '[') /* [runlevels] */
|
||||||
|
runlevels = &cmd[0];
|
||||||
|
else if (cmd[0] == '#') /* #ID */
|
||||||
|
id = atoi(&cmd[1]);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Check if valid command follows... */
|
||||||
|
cmd = strtok(NULL, " ");
|
||||||
|
if (!cmd)
|
||||||
|
goto incomplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Example: inetd ssh@eth0:222/tcp */
|
||||||
|
if (service) {
|
||||||
|
proto = strchr(service, '/');
|
||||||
|
if (!proto)
|
||||||
|
goto incomplete;
|
||||||
|
*proto++ = 0;
|
||||||
|
|
||||||
|
port = strchr(service, ':');
|
||||||
|
if (port)
|
||||||
|
*port++ = 0;
|
||||||
|
|
||||||
|
iface = strchr(service, '@');
|
||||||
|
if (iface)
|
||||||
|
*iface++ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef INETD_DISABLED
|
||||||
|
/* Find plugin that provides a callback for this inetd service */
|
||||||
|
if (type == SVC_TYPE_INETD && !strncasecmp(cmd, "internal", 8)) {
|
||||||
|
plugin = plugin_find(service);
|
||||||
|
if (!plugin || !plugin->inetd.cmd) {
|
||||||
|
_e("Inetd service %s has no internal plugin, skipping.", service);
|
||||||
|
return errno = ENOENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if known inetd, then add ifname for filtering only. */
|
||||||
|
svc = find_inetd_svc(cmd, service, proto, port);
|
||||||
|
if (svc)
|
||||||
|
return inetd_allow(&svc->inetd, iface);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
svc = svc_find(cmd, id);
|
||||||
|
if (!svc) {
|
||||||
|
_d("Creating new svc for %s id #%d", cmd, id);
|
||||||
|
svc = svc_new(id);
|
||||||
|
if (!svc) {
|
||||||
|
_e("Out of memory, cannot register service %s", cmd);
|
||||||
|
return errno = ENOMEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (svc->mtime != mtime)
|
||||||
|
svc->dirty = 1;
|
||||||
|
svc->mtime = mtime;
|
||||||
|
svc->type = type;
|
||||||
|
|
||||||
|
if (desc)
|
||||||
|
strlcpy(svc->desc, desc + 3, sizeof(svc->desc));
|
||||||
|
|
||||||
|
if (username) {
|
||||||
|
char *ptr = strchr(username, ':');
|
||||||
|
|
||||||
|
if (ptr) {
|
||||||
|
*ptr++ = 0;
|
||||||
|
strlcpy(svc->group, ptr, sizeof(svc->group));
|
||||||
|
}
|
||||||
|
strlcpy(svc->username, username, sizeof(svc->username));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef INETD_DISABLED
|
||||||
|
if (svc_is_inetd(svc)) {
|
||||||
|
int result;
|
||||||
|
|
||||||
|
result = inetd_new(&svc->inetd, service, proto, forking);
|
||||||
|
result += inetd_init(&svc->inetd, svc, iface, port);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
_e("Failed registering new inetd service %s.", service);
|
||||||
|
inetd_del(&svc->inetd);
|
||||||
|
return svc_del(svc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (plugin) {
|
||||||
|
/* Internal plugin provides this service */
|
||||||
|
svc->inetd.cmd = plugin->inetd.cmd;
|
||||||
|
} else {
|
||||||
|
/* External program to call */
|
||||||
|
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
||||||
|
|
||||||
|
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
||||||
|
while ((cmd = strtok(NULL, " ")))
|
||||||
|
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
||||||
|
svc->args[i][0] = 0;
|
||||||
|
|
||||||
|
plugin = plugin_find(svc->cmd);
|
||||||
|
if (plugin && plugin->svc.cb) {
|
||||||
|
svc->cb = plugin->svc.cb;
|
||||||
|
svc->dynamic = plugin->svc.dynamic;
|
||||||
|
svc->dynamic_stop = plugin->svc.dynamic_stop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svc->runlevels = parse_runlevels(runlevels);
|
||||||
|
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void service_unregister(svc_t *svc)
|
||||||
|
{
|
||||||
|
service_stop(svc);
|
||||||
|
svc_del(svc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void service_monitor(pid_t lost)
|
||||||
|
{
|
||||||
|
svc_t *svc;
|
||||||
|
static int was_stopped = 0;
|
||||||
|
|
||||||
|
if (was_stopped && !is_norespawn()) {
|
||||||
|
was_stopped = 0;
|
||||||
|
restart_lost_procs();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fexist(SYNC_SHUTDOWN) || lost <= 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Power user at the console, don't respawn tasks. */
|
||||||
|
if (is_norespawn()) {
|
||||||
|
was_stopped = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tty_respawn(lost))
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifndef INETD_DISABLED
|
||||||
|
if (inetd_respawn(lost))
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||||
|
if (lost != svc->pid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (SVC_TYPE_SERVICE != svc->type) {
|
||||||
|
svc->pid = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_d("Ouch, lost pid %d - %s(%d)", lost, basename(svc->cmd), svc->pid);
|
||||||
|
|
||||||
|
/* No longer running, update books. */
|
||||||
|
svc->pid = 0;
|
||||||
|
|
||||||
|
if (sig_stopped()) {
|
||||||
|
_e("Stopped, not respawning killed processes.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restarting lost service. */
|
||||||
|
if (service_enabled(svc, 0, NULL)) {
|
||||||
|
if (svc->restart_counter > RESPAWN_MAX) {
|
||||||
|
_e("Not restarting %s id %d, respawn MAX (%d) reached!",
|
||||||
|
svc->cmd, svc->id, RESPAWN_MAX);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
svc->restart_counter++;
|
||||||
|
service_start(svc);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_norespawn(void)
|
||||||
|
{
|
||||||
|
return sig_stopped() ||
|
||||||
|
fexist("/mnt/norespawn") ||
|
||||||
|
fexist("/tmp/norespawn");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void restart_lost_procs(void)
|
||||||
|
{
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
|
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||||
|
if (svc->pid > 0 && pid_alive(svc->pid))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Only restart lost daemons, not task/run/inetd services */
|
||||||
|
if (SVC_TYPE_SERVICE != svc->type) {
|
||||||
|
svc->pid = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
service_start(svc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Singing and dancing ... */
|
||||||
|
static void svc_dance(svc_t *svc)
|
||||||
|
{
|
||||||
|
svc_cmd_t cmd = service_enabled(svc, 0, NULL);
|
||||||
|
|
||||||
|
if (svc->pid) {
|
||||||
|
if (SVC_STOP == cmd)
|
||||||
|
service_stop(svc);
|
||||||
|
else if (SVC_RELOAD == cmd)
|
||||||
|
service_reload(svc);
|
||||||
|
} else {
|
||||||
|
if (SVC_START == cmd || SVC_RELOAD == cmd)
|
||||||
|
service_start(svc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int encode(int lvl)
|
||||||
|
{
|
||||||
|
if (!lvl) return 0;
|
||||||
|
return lvl + '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void utmp_save(int pre, int now)
|
||||||
|
{
|
||||||
|
struct utmp utent;
|
||||||
|
|
||||||
|
utent.ut_type = RUN_LVL;
|
||||||
|
utent.ut_pid = (encode(pre) << 8) | (encode(now) & 0xFF);
|
||||||
|
strlcpy(utent.ut_user, "runlevel", sizeof(utent.ut_user));
|
||||||
|
|
||||||
|
setutent();
|
||||||
|
pututline(&utent);
|
||||||
|
endutent();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef INETD_DISABLED
|
||||||
|
static svc_t *find_inetd_svc(char *path, char *service, char *proto, char *port)
|
||||||
|
{
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
|
for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) {
|
||||||
|
if (strncmp(path, svc->cmd, strlen(svc->cmd)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (inetd_match(&svc->inetd, service, proto, port)) {
|
||||||
|
_d("Found a matching svc for %s", path);
|
||||||
|
return svc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* version-control: t
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/* Finit service monitor, task starter and generic API for managing svc_t
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
|
* Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FINIT_SERVICE_H_
|
||||||
|
#define FINIT_SERVICE_H_
|
||||||
|
|
||||||
|
#include "svc.h"
|
||||||
|
|
||||||
|
void service_runlevel (int newlevel);
|
||||||
|
int service_register (int type, char *line, time_t mtime, char *username);
|
||||||
|
void service_unregister (svc_t *svc);
|
||||||
|
svc_cmd_t service_enabled (svc_t *svc, int event, void *arg);
|
||||||
|
|
||||||
|
int service_start (svc_t *svc);
|
||||||
|
int service_stop (svc_t *svc);
|
||||||
|
int service_reload (svc_t *svc);
|
||||||
|
void service_reload_dynamic (void);
|
||||||
|
|
||||||
|
void service_cleanup (void);
|
||||||
|
|
||||||
|
#endif /* FINIT_SERVICE_H_ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* version-control: t
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -46,6 +46,7 @@
|
|||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
#include "sig.h"
|
#include "sig.h"
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
static int stopped = 0;
|
static int stopped = 0;
|
||||||
static uev_t sighup_watcher, sigint_watcher, sigpwr_watcher;
|
static uev_t sighup_watcher, sigint_watcher, sigpwr_watcher;
|
||||||
@@ -99,10 +100,10 @@ static void sighup_cb(uev_ctx_t *UNUSED(ctx), uev_t *UNUSED(w), void *UNUSED(arg
|
|||||||
parse_finit_d(rcsd);
|
parse_finit_d(rcsd);
|
||||||
|
|
||||||
/* Reload dirty services */
|
/* Reload dirty services */
|
||||||
svc_reload_dynamic();
|
service_reload_dynamic();
|
||||||
|
|
||||||
/* Cleanup stale services */
|
/* Cleanup stale services */
|
||||||
svc_cleanup();
|
svc_clean_dynamic(service_unregister);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -125,7 +126,7 @@ static void sigchld_cb(uev_ctx_t *UNUSED(ctx), uev_t *UNUSED(w), void *UNUSED(ar
|
|||||||
pid = waitpid(-1, NULL, WNOHANG);
|
pid = waitpid(-1, NULL, WNOHANG);
|
||||||
if (pid > 0) {
|
if (pid > 0) {
|
||||||
_d("Collected child %d", pid);
|
_d("Collected child %d", pid);
|
||||||
svc_monitor(pid);
|
service_monitor(pid);
|
||||||
}
|
}
|
||||||
} while (pid > 0);
|
} while (pid > 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Finit service monitor, task starter and generic API for managing svc_t
|
/* Low-level service primitives and generic API for managing svc_t structures
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
* Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
|
* Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
|
||||||
@@ -22,41 +22,24 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include <stdlib.h>
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <utmp.h>
|
|
||||||
#include <net/if.h>
|
|
||||||
|
|
||||||
#include "finit.h"
|
|
||||||
#include "helpers.h"
|
|
||||||
#include "private.h"
|
|
||||||
#include "sig.h"
|
|
||||||
#include "tty.h"
|
|
||||||
#include "libite/lite.h"
|
|
||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
#include "inetd.h"
|
#include "helpers.h"
|
||||||
|
|
||||||
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
|
|
||||||
|
|
||||||
/* The registered services are kept in shared memory for easy read-only access
|
|
||||||
* by 3rd party APIs. Mostly because of the ability to, from the outside, query
|
|
||||||
* liveness state of all daemons that should run. */
|
|
||||||
static int was_stopped = 0;
|
|
||||||
static svc_t *services = NULL; /* List of registered services, in shared memory. */
|
|
||||||
|
|
||||||
|
|
||||||
/* Connect to/create shared memory area of registered servies */
|
static svc_t *__connect_shm(void)
|
||||||
static void __connect_shm(void)
|
|
||||||
{
|
{
|
||||||
if (!services) {
|
svc_t *list = finit_svc_connect();
|
||||||
services = finit_svc_connect();
|
|
||||||
if (!services) {
|
if (!list) {
|
||||||
/* This should never happen, but if it does we're probably
|
/* This should never happen, but if it does we're probably
|
||||||
* knee-deep in more serious problems already... */
|
* knee-deep in more serious problems already... */
|
||||||
_e("Failed allocating shared memory, error %d: %s", errno, strerror (errno));
|
_e("Failed connecting to shared memory, error %d: %s", errno, strerror(errno));
|
||||||
abort();
|
abort();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,10 +52,10 @@ static void __connect_shm(void)
|
|||||||
svc_t *svc_new(int id)
|
svc_t *svc_new(int id)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
svc_t *list = __connect_shm();
|
||||||
|
|
||||||
__connect_shm();
|
|
||||||
for (i = 0; i < MAX_NUM_SVC; i++) {
|
for (i = 0; i < MAX_NUM_SVC; i++) {
|
||||||
svc_t *svc = &services[i];
|
svc_t *svc = &list[i];
|
||||||
|
|
||||||
if (svc->type == SVC_TYPE_FREE) {
|
if (svc->type == SVC_TYPE_FREE) {
|
||||||
memset(svc, 0, sizeof(*svc));
|
memset(svc, 0, sizeof(*svc));
|
||||||
@@ -110,13 +93,13 @@ int svc_del(svc_t *svc)
|
|||||||
svc_t *svc_iterator(int first)
|
svc_t *svc_iterator(int first)
|
||||||
{
|
{
|
||||||
static int i = 0;
|
static int i = 0;
|
||||||
|
svc_t *list = __connect_shm();
|
||||||
|
|
||||||
__connect_shm();
|
|
||||||
if (first)
|
if (first)
|
||||||
i = 0;
|
i = 0;
|
||||||
|
|
||||||
while (i < MAX_NUM_SVC) {
|
while (i < MAX_NUM_SVC) {
|
||||||
svc_t *svc = &services[i++];
|
svc_t *svc = &list[i++];
|
||||||
|
|
||||||
if (svc->type != SVC_TYPE_FREE)
|
if (svc->type != SVC_TYPE_FREE)
|
||||||
return svc;
|
return svc;
|
||||||
@@ -125,6 +108,81 @@ svc_t *svc_iterator(int first)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* svc_inetd_iterator - Naive iterator over all registered inetd services.
|
||||||
|
* @first: Get first &svc_t object, or next until end.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* The first inetd &svc_t when @first is set, otherwise the next
|
||||||
|
* inetd &svc_t until the end when %NULL is returned.
|
||||||
|
*/
|
||||||
|
svc_t *svc_inetd_iterator(int first)
|
||||||
|
{
|
||||||
|
svc_t *svc = svc_iterator(first);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (svc_is_inetd(svc))
|
||||||
|
return svc;
|
||||||
|
} while ((svc = svc_iterator(0)));
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* svc_dynamic_iterator - Naive iterator over all registered dynamic services.
|
||||||
|
* @first: Get first &svc_t object, or next until end.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* The first dynamically loaded &svc_t when @first is set, otherwise the
|
||||||
|
* next dynamically loaded &svc_t until the end when %NULL is returned.
|
||||||
|
*/
|
||||||
|
svc_t *svc_dynamic_iterator(int first)
|
||||||
|
{
|
||||||
|
svc_t *svc = svc_iterator(first);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (svc->mtime)
|
||||||
|
return svc;
|
||||||
|
} while ((svc = svc_iterator(0)));
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* svc_foreach - Run a callback for each registered service
|
||||||
|
* @cb: Callback to run for each service
|
||||||
|
*/
|
||||||
|
void svc_foreach(void (*cb)(svc_t *))
|
||||||
|
{
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
|
if (!cb)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (svc = svc_iterator(1); svc; svc = svc_iterator(0))
|
||||||
|
cb(svc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* svc_foreach_dynamic - Run a callback for each registered dynamic service
|
||||||
|
* @cb: Callback to run for each dynamic service
|
||||||
|
*/
|
||||||
|
void svc_foreach_dynamic(void (*cb)(svc_t *))
|
||||||
|
{
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
|
if (!cb)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (svc = svc_dynamic_iterator(1); svc; svc = svc_dynamic_iterator(0))
|
||||||
|
cb(svc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* svc_find - Find a service object by its full path name
|
* svc_find - Find a service object by its full path name
|
||||||
* @path: Full path name, e.g., /sbin/syslogd
|
* @path: Full path name, e.g., /sbin/syslogd
|
||||||
@@ -165,811 +223,47 @@ svc_t *svc_find_by_pid(pid_t pid)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef INETD_DISABLED
|
|
||||||
svc_t *svc_find_inetd(char *path, char *service, char *proto, char *port)
|
|
||||||
{
|
|
||||||
svc_t *svc;
|
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
|
||||||
if (svc->type != SVC_TYPE_INETD)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (strncmp(path, svc->cmd, strlen(svc->cmd)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (inetd_match(&svc->inetd, service, proto, port)) {
|
|
||||||
_d("Found a matching svc for %s", path);
|
|
||||||
return svc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* svc_mark_dynamic - Mark dynamically loaded services for deletion.
|
* svc_mark_dynamic - Mark dynamically loaded services for deletion.
|
||||||
*
|
*
|
||||||
* This function traverses the list of known services, marking all that
|
* This function traverses the list of known services, marking all that
|
||||||
* have been loaded from /etc/finit.d/ for deletion. If a .conf file
|
* have been loaded from /etc/finit.d/ for deletion. If a .conf file
|
||||||
* has been removed svc_cleanup() will stop and delete the service.
|
* has been removed svc_cleanup_dynamic() will stop and delete the
|
||||||
|
* service.
|
||||||
*
|
*
|
||||||
* This function is called from sighup_cb().
|
* This function is called from sighup_cb().
|
||||||
*/
|
*/
|
||||||
void svc_mark_dynamic(void)
|
void svc_mark_dynamic(void)
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc = svc_dynamic_iterator(1);
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
while (svc) {
|
||||||
if (svc->mtime)
|
svc->dirty = -1;
|
||||||
svc->dirty = -1;
|
svc = svc_dynamic_iterator(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* svc_bootstrap - Start bootstrap services and tasks
|
* svc_clean_dynamic - Stop and cleanup stale services removed from /etc/finit.d
|
||||||
*
|
* @cb: Callback to run for each stale service
|
||||||
* System startup, runlevel S, where only services, tasks and
|
|
||||||
* run commands absolutely essential to bootstrap are located.
|
|
||||||
*/
|
|
||||||
void svc_bootstrap(void)
|
|
||||||
{
|
|
||||||
svc_t *svc;
|
|
||||||
|
|
||||||
_d("Bootstrapping all services in runlevel S from %s", FINIT_CONF);
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
|
||||||
svc_cmd_t cmd;
|
|
||||||
|
|
||||||
/* Inetd services cannot be part of bootstrap currently. */
|
|
||||||
if (svc->type == SVC_TYPE_INETD)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
cmd = svc_enabled(svc, 0, NULL);
|
|
||||||
if (SVC_START == cmd || (SVC_RELOAD == cmd))
|
|
||||||
svc_start(svc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int encode(int lvl)
|
|
||||||
{
|
|
||||||
if (!lvl)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return lvl + '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
static void utmp_save(int pre, int now)
|
|
||||||
{
|
|
||||||
struct utmp utent;
|
|
||||||
|
|
||||||
utent.ut_type = RUN_LVL;
|
|
||||||
utent.ut_pid = (encode(pre) << 8) | (encode(now) & 0xFF);
|
|
||||||
strlcpy(utent.ut_user, "runlevel", sizeof(utent.ut_user));
|
|
||||||
|
|
||||||
setutent();
|
|
||||||
pututline(&utent);
|
|
||||||
endutent();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Singing and dancing ... */
|
|
||||||
static void svc_dance(svc_t *svc)
|
|
||||||
{
|
|
||||||
svc_cmd_t cmd = svc_enabled(svc, 0, NULL);
|
|
||||||
|
|
||||||
if (svc->pid) {
|
|
||||||
if (SVC_STOP == cmd)
|
|
||||||
svc_stop(svc);
|
|
||||||
else if (SVC_RELOAD == cmd)
|
|
||||||
svc_reload(svc);
|
|
||||||
} else {
|
|
||||||
if (SVC_START == cmd || SVC_RELOAD == cmd)
|
|
||||||
svc_start(svc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* svc_runlevel - Change to a new runlevel
|
|
||||||
* @newlevel: New runlevel to activate
|
|
||||||
*
|
|
||||||
* Stops all services not in @newlevel and starts, or lets continue to run,
|
|
||||||
* those in @newlevel. Also updates @prevlevel and active @runlevel.
|
|
||||||
*/
|
|
||||||
void svc_runlevel(int newlevel)
|
|
||||||
{
|
|
||||||
svc_t *svc;
|
|
||||||
|
|
||||||
if (runlevel == newlevel)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (newlevel < 0 || newlevel > 9)
|
|
||||||
return;
|
|
||||||
|
|
||||||
prevlevel = runlevel;
|
|
||||||
runlevel = newlevel;
|
|
||||||
|
|
||||||
_d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel);
|
|
||||||
utmp_save(prevlevel, newlevel);
|
|
||||||
|
|
||||||
/* Make sure to reload all *.conf in /etc/finit.d/ */
|
|
||||||
svc_mark_dynamic();
|
|
||||||
parse_finit_d(rcsd);
|
|
||||||
|
|
||||||
_d("Stopping services services not allowed in new runlevel ...");
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
|
||||||
if (!ISSET(svc->runlevels, runlevel)) {
|
|
||||||
#ifndef INETD_DISABLED
|
|
||||||
if (svc->type == SVC_TYPE_INETD)
|
|
||||||
inetd_stop(&svc->inetd);
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
svc_stop(svc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ... or disabled/removed services from /etc/finit.d/ */
|
|
||||||
if (svc->mtime && svc->dirty != 0)
|
|
||||||
svc_stop(svc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prev runlevel services stopped, call hooks before starting new runlevel ... */
|
|
||||||
_d("All services have been stoppped, calling runlevel change hooks ...");
|
|
||||||
plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */
|
|
||||||
|
|
||||||
_d("Starting services services new to this runlevel ...");
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
|
||||||
#ifndef INETD_DISABLED
|
|
||||||
/* Inetd services have slightly different semantics */
|
|
||||||
if (svc->type == SVC_TYPE_INETD) {
|
|
||||||
if (ISSET(svc->runlevels, runlevel))
|
|
||||||
inetd_start(&svc->inetd);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* All other services consult their callback here */
|
|
||||||
svc_dance(svc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cleanup stale services */
|
|
||||||
svc_cleanup();
|
|
||||||
|
|
||||||
if (0 == runlevel) {
|
|
||||||
do_shutdown(SIGUSR2);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (6 == runlevel) {
|
|
||||||
do_shutdown(SIGUSR1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (runlevel == 1)
|
|
||||||
touch("/etc/nologin"); /* Disable login in single-user mode */
|
|
||||||
else
|
|
||||||
erase("/etc/nologin");
|
|
||||||
|
|
||||||
if (0 != prevlevel)
|
|
||||||
tty_runlevel(runlevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* svc_register - Register service, task or run commands
|
|
||||||
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
|
|
||||||
* @line: A complete command line with -- separated description text
|
|
||||||
* @mtime: The modification time if service is loaded from /etc/finit.d
|
|
||||||
* @username: Optional username to run service as, or %NULL to run as root
|
|
||||||
*
|
|
||||||
* This function is used to register commands to be run on different
|
|
||||||
* system runlevels with optional username. The @type argument details
|
|
||||||
* if it's service to bo monitored/respawned (daemon), a one-shot task
|
|
||||||
* or a command that must run in sequence and not in parallell, like
|
|
||||||
* service and task commands do.
|
|
||||||
*
|
|
||||||
* The @line can optionally start with a username, denoted by an @
|
|
||||||
* character. Like this:
|
|
||||||
*
|
|
||||||
* service @username [!0-6,S] /path/to/daemon arg -- Description
|
|
||||||
* task @username [!0-6,S] /path/to/task arg -- Description
|
|
||||||
* run @username [!0-6,S] /path/to/cmd arg -- Description
|
|
||||||
* inetd tcp/ssh nowait [2345] @root:root /sbin/sshd -i -- Description
|
|
||||||
*
|
|
||||||
* If the username is left out the command is started as root. The []
|
|
||||||
* brackets denote the allowed runlevels, if left out the default for a
|
|
||||||
* service is set to [2-5]. Allowed runlevels mimic that of SysV init
|
|
||||||
* with the addition of the 'S' runlevel, which is only run once at
|
|
||||||
* startup. It can be seen as the system bootstrap. If a task or run
|
|
||||||
* command is listed in more than the [S] runlevel they will be called
|
|
||||||
* when changing runlevel.
|
|
||||||
*
|
|
||||||
* For multiple instances of the same command, e.g. multiple DHCP
|
|
||||||
* clients, the user may enter an optional ID, using the #ID syntax.
|
|
||||||
*
|
|
||||||
* service #1 /sbin/udhcpc -i eth1
|
|
||||||
* service #2 /sbin/udhcpc -i eth2
|
|
||||||
*
|
|
||||||
* Without the #ID syntax Finit will overwrite the first service line
|
|
||||||
* with the contents of the second. The #ID must be [1,MAXINT].
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* POSIX OK(0) on success, or non-zero errno exit status on failure.
|
|
||||||
*/
|
|
||||||
int svc_register(int type, char *line, time_t mtime, char *username)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
int id = 1; /* Default to ID:1 */
|
|
||||||
#ifndef INETD_DISABLED
|
|
||||||
int forking = 0;
|
|
||||||
#endif
|
|
||||||
char *service = NULL, *proto = NULL, *iface = NULL, *port = NULL;
|
|
||||||
char *cmd, *desc, *runlevels = NULL;
|
|
||||||
svc_t *svc;
|
|
||||||
plugin_t *plugin = NULL;
|
|
||||||
|
|
||||||
if (!line) {
|
|
||||||
_e("Invalid input argument.");
|
|
||||||
return errno = EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
desc = strstr(line, "-- ");
|
|
||||||
if (desc)
|
|
||||||
*desc = 0;
|
|
||||||
|
|
||||||
cmd = strtok(line, " ");
|
|
||||||
if (!cmd) {
|
|
||||||
incomplete:
|
|
||||||
_e("Incomplete service, cannot register.");
|
|
||||||
return errno = ENOENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (cmd) {
|
|
||||||
if (cmd[0] != '/' && strchr(cmd, '/'))
|
|
||||||
service = cmd; /* inetd service/proto */
|
|
||||||
#ifndef INETD_DISABLED
|
|
||||||
else if (!strncasecmp(cmd, "nowait", 6))
|
|
||||||
forking = 1;
|
|
||||||
else if (!strncasecmp(cmd, "wait", 4))
|
|
||||||
forking = 0;
|
|
||||||
#endif
|
|
||||||
else if (cmd[0] == '@') /* @username[:group] */
|
|
||||||
username = &cmd[1];
|
|
||||||
else if (cmd[0] == '[') /* [runlevels] */
|
|
||||||
runlevels = &cmd[0];
|
|
||||||
else if (cmd[0] == '#') /* #ID */
|
|
||||||
id = atoi(&cmd[1]);
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Check if valid command follows... */
|
|
||||||
cmd = strtok(NULL, " ");
|
|
||||||
if (!cmd)
|
|
||||||
goto incomplete;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Example: inetd ssh@eth0:222/tcp */
|
|
||||||
if (service) {
|
|
||||||
proto = strchr(service, '/');
|
|
||||||
if (!proto)
|
|
||||||
goto incomplete;
|
|
||||||
*proto++ = 0;
|
|
||||||
|
|
||||||
port = strchr(service, ':');
|
|
||||||
if (port)
|
|
||||||
*port++ = 0;
|
|
||||||
|
|
||||||
iface = strchr(service, '@');
|
|
||||||
if (iface)
|
|
||||||
*iface++ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef INETD_DISABLED
|
|
||||||
/* Find plugin that provides a callback for this inetd service */
|
|
||||||
if (type == SVC_TYPE_INETD && !strncasecmp(cmd, "internal", 8)) {
|
|
||||||
plugin = plugin_find(service);
|
|
||||||
if (!plugin || !plugin->inetd.cmd) {
|
|
||||||
_e("Inetd service %s has no internal plugin, skipping.", service);
|
|
||||||
return errno = ENOENT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if known inetd, then add ifname for filtering only. */
|
|
||||||
svc = svc_find_inetd(cmd, service, proto, port);
|
|
||||||
if (svc)
|
|
||||||
return inetd_allow(&svc->inetd, iface);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
svc = svc_find(cmd, id);
|
|
||||||
if (!svc) {
|
|
||||||
_d("Creating new svc for %s id #%d", cmd, id);
|
|
||||||
svc = svc_new(id);
|
|
||||||
if (!svc) {
|
|
||||||
_e("Out of memory, cannot register service %s", cmd);
|
|
||||||
return errno = ENOMEM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (svc->mtime != mtime)
|
|
||||||
svc->dirty = 1;
|
|
||||||
svc->mtime = mtime;
|
|
||||||
svc->type = type;
|
|
||||||
|
|
||||||
if (desc)
|
|
||||||
strlcpy(svc->desc, desc + 3, sizeof(svc->desc));
|
|
||||||
|
|
||||||
if (username) {
|
|
||||||
char *ptr = strchr(username, ':');
|
|
||||||
|
|
||||||
if (ptr) {
|
|
||||||
*ptr++ = 0;
|
|
||||||
strlcpy(svc->group, ptr, sizeof(svc->group));
|
|
||||||
}
|
|
||||||
strlcpy(svc->username, username, sizeof(svc->username));
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef INETD_DISABLED
|
|
||||||
if (svc->type == SVC_TYPE_INETD) {
|
|
||||||
int result;
|
|
||||||
|
|
||||||
result = inetd_new(&svc->inetd, service, proto, forking);
|
|
||||||
result += inetd_init(&svc->inetd, svc, iface, port);
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
_e("Failed registering new inetd service %s.", service);
|
|
||||||
inetd_del(&svc->inetd);
|
|
||||||
return svc_del(svc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (plugin) {
|
|
||||||
/* Internal plugin provides this service */
|
|
||||||
svc->inetd.cmd = plugin->inetd.cmd;
|
|
||||||
} else {
|
|
||||||
/* External program to call */
|
|
||||||
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
|
||||||
|
|
||||||
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
|
||||||
while ((cmd = strtok(NULL, " ")))
|
|
||||||
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
|
||||||
svc->args[i][0] = 0;
|
|
||||||
|
|
||||||
plugin = plugin_find(svc->cmd);
|
|
||||||
if (plugin && plugin->svc.cb) {
|
|
||||||
svc->cb = plugin->svc.cb;
|
|
||||||
svc->dynamic = plugin->svc.dynamic;
|
|
||||||
svc->dynamic_stop = plugin->svc.dynamic_stop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
svc->runlevels = parse_runlevels(runlevels);
|
|
||||||
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* svc_enabled - Should the service run?
|
|
||||||
* @svc: Pointer to &svc_t object
|
|
||||||
* @event: Dynamic event, opaque flag passed to callback
|
|
||||||
* @arg: Event argument, used only by external service plugins.
|
|
||||||
*
|
|
||||||
* This method calls an associated service callback, if registered by a
|
|
||||||
* plugin, and returns the &svc_cmd_t status. If no plugin is registered
|
|
||||||
* the service is statically enabled in /etc/finit.conf and the result
|
|
||||||
* will always be %SVC_START.
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* Either one of %SVC_START, %SVC_STOP, %SVC_RELOAD.
|
|
||||||
*/
|
|
||||||
svc_cmd_t svc_enabled(svc_t *svc, int event, void *arg)
|
|
||||||
{
|
|
||||||
if (!svc) {
|
|
||||||
errno = EINVAL;
|
|
||||||
return SVC_STOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ISSET(svc->runlevels, runlevel))
|
|
||||||
return SVC_STOP;
|
|
||||||
|
|
||||||
/* Is there a service plugin registered? */
|
|
||||||
if (svc->cb) {
|
|
||||||
int status;
|
|
||||||
pid_t pid;
|
|
||||||
|
|
||||||
/* Let callback run in separate process so it doesn't crash PID 1 */
|
|
||||||
pid = fork();
|
|
||||||
if (-1 == pid) {
|
|
||||||
_pe("Failed in %s callback", svc->cmd);
|
|
||||||
return SVC_STOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pid) {
|
|
||||||
status = svc->cb(svc, event, arg);
|
|
||||||
exit(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (waitpid(pid, &status, 0) == -1) {
|
|
||||||
_pe("Failed reading status from %s callback", svc->cmd);
|
|
||||||
return SVC_STOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Callback normally exits here. */
|
|
||||||
if (WIFEXITED(status))
|
|
||||||
return WEXITSTATUS(status);
|
|
||||||
|
|
||||||
/* Check for SEGFAULT or other error ... */
|
|
||||||
if (WCOREDUMP(status))
|
|
||||||
_e("Callback to %s crashed!\n", svc->cmd);
|
|
||||||
else
|
|
||||||
_e("Callback to %s did not exit normally!\n", svc->cmd);
|
|
||||||
|
|
||||||
return SVC_STOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No service plugin, default to start, since listed in finit.conf */
|
|
||||||
return SVC_START;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int is_norespawn(void)
|
|
||||||
{
|
|
||||||
return sig_stopped() ||
|
|
||||||
fexist("/mnt/norespawn") ||
|
|
||||||
fexist("/tmp/norespawn");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void restart_any_lost_procs(void)
|
|
||||||
{
|
|
||||||
svc_t *svc;
|
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
|
||||||
if (svc->pid > 0 && pid_alive(svc->pid))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Only restart lost daemons, not task/run/inetd services */
|
|
||||||
if (SVC_TYPE_SERVICE != svc->type) {
|
|
||||||
svc->pid = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
svc_start(svc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void svc_monitor(pid_t lost)
|
|
||||||
{
|
|
||||||
svc_t *svc;
|
|
||||||
|
|
||||||
if (was_stopped && !is_norespawn()) {
|
|
||||||
was_stopped = 0;
|
|
||||||
restart_any_lost_procs();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fexist(SYNC_SHUTDOWN) || lost <= 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Power user at the console, don't respawn tasks. */
|
|
||||||
if (is_norespawn()) {
|
|
||||||
was_stopped = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tty_respawn(lost))
|
|
||||||
return;
|
|
||||||
|
|
||||||
#ifndef INETD_DISABLED
|
|
||||||
if (inetd_respawn(lost))
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
|
||||||
if (lost != svc->pid)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (SVC_TYPE_SERVICE != svc->type) {
|
|
||||||
svc->pid = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_d("Ouch, lost pid %d - %s(%d)", lost, basename(svc->cmd), svc->pid);
|
|
||||||
|
|
||||||
/* No longer running, update books. */
|
|
||||||
svc->pid = 0;
|
|
||||||
|
|
||||||
if (sig_stopped()) {
|
|
||||||
_e("Stopped, not respawning killed processes.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Restarting lost service. */
|
|
||||||
if (svc_enabled(svc, 0, NULL)) {
|
|
||||||
if (svc->restart_counter > RESPAWN_MAX) {
|
|
||||||
_e("Not restarting %s id %d, respawn MAX (%d) reached!",
|
|
||||||
svc->cmd, svc->id, RESPAWN_MAX);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
svc->restart_counter++;
|
|
||||||
svc_start(svc);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remember: svc_enabled() must be called before calling svc_start() */
|
|
||||||
int svc_start(svc_t *svc)
|
|
||||||
{
|
|
||||||
int respawn, sd = 0;
|
|
||||||
pid_t pid;
|
|
||||||
sigset_t nmask, omask;
|
|
||||||
|
|
||||||
if (!svc)
|
|
||||||
return 0;
|
|
||||||
respawn = svc->pid != 0;
|
|
||||||
|
|
||||||
/* Don't try and start service if it doesn't exist. */
|
|
||||||
if (!fexist(svc->cmd) && !svc->inetd.cmd) {
|
|
||||||
char msg[80];
|
|
||||||
|
|
||||||
snprintf(msg, sizeof(msg), "Service %s does not exist!", svc->cmd);
|
|
||||||
print_desc("", msg);
|
|
||||||
print_result(1);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ignore if finit is SIGSTOP'ed */
|
|
||||||
if (is_norespawn())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
#ifndef INETD_DISABLED
|
|
||||||
if (SVC_TYPE_INETD == svc->type) {
|
|
||||||
char ifname[IF_NAMESIZE] = "UNKNOWN";
|
|
||||||
|
|
||||||
sd = svc->inetd.watcher.fd;
|
|
||||||
|
|
||||||
if (svc->inetd.type == SOCK_STREAM) {
|
|
||||||
/* Open new client socket from server socket */
|
|
||||||
sd = accept(sd, NULL, NULL);
|
|
||||||
if (sd < 0) {
|
|
||||||
FLOG_PERROR("Failed accepting inetd service %d/tcp", svc->inetd.port);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
_d("New client socket %d accepted for inetd service %d/tcp", sd, svc->inetd.port);
|
|
||||||
|
|
||||||
/* Find ifname by means of getsockname() and getifaddrs() */
|
|
||||||
inetd_stream_peek(sd, ifname);
|
|
||||||
} else { /* SOCK_DGRAM */
|
|
||||||
/* Find ifname by means of IP_PKTINFO sockopt --> ifindex + if_indextoname() */
|
|
||||||
inetd_dgram_peek(sd, ifname);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!inetd_is_allowed(&svc->inetd, ifname)) {
|
|
||||||
FLOG_INFO("Service %s on port %d not allowed from interface %s.",
|
|
||||||
svc->inetd.name, svc->inetd.port, ifname);
|
|
||||||
if (svc->inetd.type == SOCK_STREAM)
|
|
||||||
close(sd);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FLOG_INFO("Starting inetd service %s for requst from iface %s ...", svc->inetd.name, ifname);
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
if (SVC_TYPE_SERVICE != svc->type)
|
|
||||||
print_desc("", svc->desc);
|
|
||||||
else if (!respawn)
|
|
||||||
print_desc("Starting ", svc->desc);
|
|
||||||
|
|
||||||
/* Block sigchild while forking. */
|
|
||||||
sigemptyset(&nmask);
|
|
||||||
sigaddset(&nmask, SIGCHLD);
|
|
||||||
sigprocmask(SIG_BLOCK, &nmask, &omask);
|
|
||||||
|
|
||||||
pid = fork();
|
|
||||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
|
||||||
if (pid == 0) {
|
|
||||||
int i = 0;
|
|
||||||
int status;
|
|
||||||
#ifdef ENABLE_STATIC
|
|
||||||
int uid = 0; /* XXX: Fix better warning that dropprivs is disabled. */
|
|
||||||
#else
|
|
||||||
int uid = getuser(svc->username);
|
|
||||||
#endif
|
|
||||||
struct sigaction sa;
|
|
||||||
char *args[MAX_NUM_SVC_ARGS];
|
|
||||||
|
|
||||||
sigemptyset(&nmask);
|
|
||||||
sigaddset(&nmask, SIGCHLD);
|
|
||||||
sigprocmask(SIG_UNBLOCK, &nmask, NULL);
|
|
||||||
|
|
||||||
/* Reset signal handlers that were set by the parent process */
|
|
||||||
for (i = 1; i < NSIG; i++)
|
|
||||||
DFLSIG(sa, i, 0);
|
|
||||||
|
|
||||||
/* Set desired user */
|
|
||||||
if (uid >= 0)
|
|
||||||
setuid(uid);
|
|
||||||
|
|
||||||
/* Serve copy of args to process in case it modifies them. */
|
|
||||||
for (i = 0; i < (MAX_NUM_SVC_ARGS - 1) && svc->args[i][0] != 0; i++)
|
|
||||||
args[i] = svc->args[i];
|
|
||||||
args[i] = NULL;
|
|
||||||
|
|
||||||
/* Redirect inetd socket to stdin for service */
|
|
||||||
if (SVC_TYPE_INETD == svc->type) {
|
|
||||||
/* sd set previously */
|
|
||||||
dup2(sd, STDIN_FILENO);
|
|
||||||
close(sd);
|
|
||||||
dup2(STDIN_FILENO, STDOUT_FILENO);
|
|
||||||
dup2(STDIN_FILENO, STDERR_FILENO);
|
|
||||||
} else if (debug) {
|
|
||||||
int fd;
|
|
||||||
char buf[CMD_SIZE] = "";
|
|
||||||
|
|
||||||
fd = open(CONSOLE, O_WRONLY | O_APPEND);
|
|
||||||
if (-1 != fd) {
|
|
||||||
dup2(fd, STDOUT_FILENO);
|
|
||||||
dup2(fd, STDERR_FILENO);
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_NUM_SVC_ARGS && args[i]; i++) {
|
|
||||||
char arg[MAX_ARG_LEN];
|
|
||||||
|
|
||||||
snprintf(arg, sizeof(arg), "%s ", args[i]);
|
|
||||||
if (strlen(arg) < (sizeof(buf) - strlen(buf)))
|
|
||||||
strcat(buf, arg);
|
|
||||||
}
|
|
||||||
_e("%starting %s: %s", respawn ? "Res" : "S", svc->cmd, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (svc->inetd.cmd)
|
|
||||||
status = svc->inetd.cmd(svc->inetd.type);
|
|
||||||
else
|
|
||||||
status = execv(svc->cmd, args); /* XXX: Maybe use execve() to be able to launch scripts? */
|
|
||||||
|
|
||||||
if (SVC_TYPE_INETD == svc->type) {
|
|
||||||
if (svc->inetd.type == SOCK_STREAM) {
|
|
||||||
close(STDIN_FILENO);
|
|
||||||
close(STDOUT_FILENO);
|
|
||||||
close(STDERR_FILENO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exit(status);
|
|
||||||
}
|
|
||||||
svc->pid = pid;
|
|
||||||
|
|
||||||
if (SVC_TYPE_INETD == svc->type) {
|
|
||||||
if (svc->inetd.type == SOCK_STREAM)
|
|
||||||
close(sd);
|
|
||||||
}
|
|
||||||
else if (SVC_TYPE_RUN == svc->type)
|
|
||||||
print_result(WEXITSTATUS(complete(svc->cmd, pid)));
|
|
||||||
else if (!respawn)
|
|
||||||
print_result(svc->pid > 1 ? 0 : 1);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int svc_stop(svc_t *svc)
|
|
||||||
{
|
|
||||||
int res = 1;
|
|
||||||
|
|
||||||
if (!svc) {
|
|
||||||
_e("Failed, no svc pointer.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (svc->pid <= 1) {
|
|
||||||
_d("Bad PID %d for %s, SIGTERM", svc->pid, svc->desc);
|
|
||||||
svc->pid = 0;
|
|
||||||
svc->restart_counter = 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SVC_TYPE_SERVICE != svc->type)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (runlevel != 1)
|
|
||||||
print_desc("Stopping ", svc->desc);
|
|
||||||
|
|
||||||
_d("Sending SIGTERM to pid:%d name:%s", svc->pid, pid_get_name(svc->pid, NULL, 0));
|
|
||||||
res = kill(svc->pid, SIGTERM);
|
|
||||||
|
|
||||||
if (runlevel != 1)
|
|
||||||
print_result(res);
|
|
||||||
|
|
||||||
svc->pid = 0;
|
|
||||||
svc->restart_counter = 0;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* svc_reload - Send SIGHUP to a service
|
|
||||||
* @svc: Service to reload
|
|
||||||
*
|
|
||||||
* This function does some basic checks of the runtime state of Finit
|
|
||||||
* and a sanity check of the @svc before sending %SIGHUP.
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* POSIX OK(0) or non-zero on error.
|
|
||||||
*/
|
|
||||||
int svc_reload(svc_t *svc)
|
|
||||||
{
|
|
||||||
/* Ignore if finit is SIGSTOP'ed */
|
|
||||||
if (is_norespawn())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!svc) {
|
|
||||||
_e("Failed, no svc pointer.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (svc->pid <= 1) {
|
|
||||||
_d("Bad PID %d for %s, SIGHUP", svc->pid, svc->cmd);
|
|
||||||
svc->pid = 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_d("Sending SIGHUP to PID %d", svc->pid);
|
|
||||||
return kill(svc->pid, SIGHUP);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* svc_reload_dynamic - Reload modifued dynamic services
|
|
||||||
*
|
|
||||||
* This function is called when Finit has recieved SIGHUP to reload
|
|
||||||
* .conf files in /etc/finit.d. It is responsible for starting,
|
|
||||||
* stopping and reloading (forwarding SIGHUP) to processes affected.
|
|
||||||
*/
|
|
||||||
void svc_reload_dynamic(void)
|
|
||||||
{
|
|
||||||
int num = 0;
|
|
||||||
svc_t *svc;
|
|
||||||
|
|
||||||
_d("Stopping disabled/removed services ...");
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
|
||||||
if (svc->mtime && svc->dirty != 0) {
|
|
||||||
svc_stop(svc);
|
|
||||||
num++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!num)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_d("All disabled/removed services have been stoppped, calling reconf hooks ...");
|
|
||||||
plugin_run_hooks(HOOK_SVC_RECONF); /* Reconfigure HW/VLANs/etc here */
|
|
||||||
|
|
||||||
_d("Starting enabled/added services ...");
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
|
||||||
if (svc->mtime && svc->dirty > 0)
|
|
||||||
svc_dance(svc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* svc_cleanup - Stop and cleanup stale services removed from /etc/finit.d
|
|
||||||
*
|
*
|
||||||
* This function traverses the list of known services to stop and clean
|
* This function traverses the list of known services to stop and clean
|
||||||
* up all non-existing services previously marked by svc_mark_dynamic().
|
* up all non-existing services previously marked by svc_mark_dynamic().
|
||||||
*/
|
*/
|
||||||
void svc_cleanup(void)
|
void svc_clean_dynamic(void (*cb)(svc_t *))
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc = svc_dynamic_iterator(1);
|
||||||
|
|
||||||
|
while (svc) {
|
||||||
|
if (svc->dirty == -1 && cb)
|
||||||
|
cb(svc);
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
|
||||||
if (svc->mtime && svc->dirty == -1) {
|
|
||||||
svc_stop(svc);
|
|
||||||
svc_del(svc);
|
|
||||||
}
|
|
||||||
svc->dirty = 0;
|
svc->dirty = 0;
|
||||||
|
svc = svc_dynamic_iterator(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local Variables:
|
* Local Variables:
|
||||||
* version-control: t
|
* version-control: t
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Finit service monitor and generic API for managing svc_t structures
|
/* Low-level service primitives and generic API for managing svc_t structures
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
* Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
|
* Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
|
||||||
@@ -33,7 +33,6 @@
|
|||||||
|
|
||||||
#include "inetd.h"
|
#include "inetd.h"
|
||||||
#include "queue.h" /* BSD sys/queue.h API */
|
#include "queue.h" /* BSD sys/queue.h API */
|
||||||
#include "libuev/uev.h"
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SVC_STOP = 0, /* Disabled */
|
SVC_STOP = 0, /* Disabled */
|
||||||
@@ -115,23 +114,24 @@ static inline svc_t *finit_svc_connect(void)
|
|||||||
return (svc_t *)ptr;
|
return (svc_t *)ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
svc_t *svc_new (int id);
|
svc_t *svc_new (int id);
|
||||||
int svc_del (svc_t *svc);
|
int svc_del (svc_t *svc);
|
||||||
svc_t *svc_find (char *path, int id);
|
|
||||||
svc_t *svc_find_by_pid (pid_t pid);
|
|
||||||
svc_t *svc_find_inetd (char *path, char *service, char *proto, char *port);
|
|
||||||
svc_t *svc_iterator (int first);
|
|
||||||
void svc_mark_dynamic (void);
|
|
||||||
void svc_runlevel (int newlevel);
|
|
||||||
|
|
||||||
int svc_register (int type, char *line, time_t mtime, char *username);
|
svc_t *svc_find (char *path, int id);
|
||||||
int svc_id_by_name (char *name);
|
svc_t *svc_find_by_pid (pid_t pid);
|
||||||
svc_cmd_t svc_enabled (svc_t *svc, int event, void *arg);
|
|
||||||
int svc_start (svc_t *svc);
|
svc_t *svc_iterator (int first);
|
||||||
int svc_stop (svc_t *svc);
|
svc_t *svc_inetd_iterator (int first);
|
||||||
int svc_reload (svc_t *svc);
|
svc_t *svc_dynamic_iterator (int first);
|
||||||
void svc_reload_dynamic(void);
|
|
||||||
void svc_cleanup (void);
|
void svc_foreach (void (*cb)(svc_t *));
|
||||||
|
void svc_foreach_dynamic (void (*cb)(svc_t *));
|
||||||
|
|
||||||
|
void svc_mark_dynamic (void);
|
||||||
|
void svc_clean_dynamic (void (*cb)(svc_t *));
|
||||||
|
|
||||||
|
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
|
||||||
|
static inline int svc_is_daemon(svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
|
||||||
|
|
||||||
#endif /* FINIT_SVC_H_ */
|
#endif /* FINIT_SVC_H_ */
|
||||||
|
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ int tty_enabled(finit_tty_t *tty, int runlevel)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TTY monitor, called by svc_monitor() */
|
/* TTY monitor, called by service_monitor() */
|
||||||
int tty_respawn(pid_t pid)
|
int tty_respawn(pid_t pid)
|
||||||
{
|
{
|
||||||
tty_node_t *entry = tty_find_by_pid(pid);
|
tty_node_t *entry = tty_find_by_pid(pid);
|
||||||
|
|||||||
Reference in new issue
Block a user