Refactor: introduce telinit and a new initctl tool w/ API

- Reduce size of `helpers.c`, for linking against new `initctl` tool,
  by moving out functions to `pid.c` and `exec.c`
- Add `AF_UNIX` API to Finit, to complement old `/dev/initctl` FIFO
- Let old FIFO API be used by init/telinit: `init <q | 1-9>`
- Move all advanced initctl code from `client.c` to `initctl.c`, yes
  its a bit confusing to call the *new* tool the same as the old FIFO
  but this is more in line with what, e.g Upstart does.
- Move all advanced server side code from `plugins/initctl.c` to `api.c`
- Update TODO with upcoming inetd syntax change and dynamic events.
- Temporarily fix display of inetd services from `initctl status -v`

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson
2015-06-13 12:26:16 +02:00
parent 66edb42e06
commit 1a1f24721f
15 changed files with 934 additions and 731 deletions
+7 -5
View File
@@ -33,11 +33,11 @@ DEV = $(NAME)-dev
ARCHTOOL = `which git-archive-all`
ARCHIVE = $(PKG).tar
ARCHIVEZ = ../$(ARCHIVE).xz
EXEC = finit reboot
EXEC = finit initctl reboot
HEADERS = plugin.h svc.h helpers.h queue.h
DISTFILES = LICENSE README CHANGELOG finit.conf services
OBJS = finit.o client.o conf.o helpers.o sig.o svc.o service.o \
plugin.o tty.o inetd.o
OBJS = finit.o api.o client.o conf.o exec.o helpers.o pid.o sig.o \
svc.o service.o plugin.o tty.o inetd.o
TOPDIR = $(shell pwd)
-include config.mk
@@ -74,6 +74,8 @@ config.h: configure
finit: $(OBJS)
initctl: initctl.o svc.o helpers.o
reboot: reboot.o $(DEPLIBS)
install-exec: all
@@ -83,8 +85,8 @@ install-exec: all
printf " INSTALL $(DESTDIR)$(sbindir)/$$file\n"; \
$(STRIPINST) $$file $(DESTDIR)$(sbindir)/$$file; \
done
printf " INSTALL $(DESTDIR)$(sbindir)/initctl\n"
@ln -sf finit $(DESTDIR)$(sbindir)/initctl
printf " INSTALL $(DESTDIR)$(sbindir)/telinit\n"
@ln -sf finit $(DESTDIR)$(sbindir)/telinit
$(MAKE) -C libite install-exec
$(MAKE) -C plugins install
+15
View File
@@ -27,6 +27,10 @@ General
* Add support for listening to netlink messages for GW, ADDR, IF UP/DOWN
* Add <!,...> to let Finit know that a service does not support reload
(SIGHUP), but must be restarted (stop+start)
* Allow custom reload for processes like libreSwan's pluto
service [2345] <!gw,if:eth0,reload:'starter reload'> /sbin/pluto
* Allow process callbacks to be scripts.
* Add support for a pipe/popen where we can listen for `STOP`, `START`
and `RELOAD` on `STDOUT` from a script ... call it for example
@@ -55,6 +59,17 @@ to achieve compatibility would be to add a plugin to finit which reads
Inetd
-----
wkz says we need deny rules and better syntax! we agrees
wkz --> inetd telnet/tcp @!eth0
wkz --> inetd 2323/tcp @eth0
wkz --> 77/udp wait [2345] @finit/time
jnn --> telnet@eth0,eth1/tcp
jnn --> 2323@eth2/tcp
jnn --> 3000/udp wait [2345] time/internal
Optimize interface filtering by using socket filter. The functions
`inet_*_peek()` and `inetd_is_allowed()` used for interace filtering
should be possible to rewrite as socket filters.
+24 -154
View File
@@ -1,4 +1,4 @@
/* Finit client for querying status and control services
/* Simple telinit client
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
@@ -27,22 +27,18 @@
#include <stdio.h>
#include "finit.h"
#include "helpers.h"
#include "service.h"
#include "libite/lite.h"
typedef struct {
char *cmd;
int (*cb)(char *arg);
} command_t;
static int do_send(struct init_request *rq, ssize_t len)
static int do_send(int cmd, int runlevel)
{
int fd;
int result = -1;
ssize_t num;
int result = 255;
ssize_t len;
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = cmd,
.runlevel = runlevel,
};
if (!fexist(FINIT_FIFO)) {
fprintf(stderr, "%s does not support %s!\n", __progname, FINIT_FIFO);
@@ -55,127 +51,22 @@ static int do_send(struct init_request *rq, ssize_t len)
return 1;
}
num = write(fd, rq, len);
if (num == len) {
num = read(fd, rq, len);
if (num == len && rq->cmd == INIT_CMD_ACK)
result = 0;
}
len = write(fd, &rq, sizeof(rq));
if (sizeof(rq) == len)
result = 0;
close(fd);
return result;
}
static int toggle_debug(char *UNUSED(arg))
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_DEBUG,
};
return do_send(&rq, sizeof(rq));
}
static int set_runlevel(char *arg)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_RUNLVL,
.runlevel = (int)arg[0],
};
return do_send(&rq, sizeof(rq));
}
static int do_svc(int cmd, char *jobid)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = cmd,
};
if (!jobid) {
if (cmd == INIT_CMD_RELOAD_SVC) {
rq.cmd = INIT_CMD_RELOAD;
goto exit;
}
return 1;
}
strlcpy(rq.data, jobid, sizeof(rq.data));
exit:
return do_send(&rq, sizeof(rq));
}
static int do_start (char *arg) { return do_svc(INIT_CMD_START_SVC, arg); }
static int do_stop (char *arg) { return do_svc(INIT_CMD_STOP_SVC, arg); }
static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD_SVC, arg); }
static int do_restart(char *arg) { return do_svc(INIT_CMD_RESTART_SVC, arg); }
static int show_version(char *UNUSED(arg))
{
puts("v" VERSION);
return 0;
}
/* In verbose mode we skip the header and each service description.
* This in favor of having all info on one line so a machine can more
* easily parse it. */
static int show_status(char *UNUSED(arg))
{
svc_t *svc;
if (!verbose) {
printf("# Status PID Runlevels Service Description\n");
printf("====================================================================================\n");
}
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
char jobid[10];
if (svc_is_unique(svc))
snprintf(jobid, sizeof(jobid), "%d", svc->job);
else
snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id);
if (verbose) {
int i;
char args[80] = "";
for (i = 1; i < MAX_NUM_SVC_ARGS; i++) {
strlcat(args, svc->args[i], sizeof(args));
strlcat(args, " ", sizeof(args));
}
printf("%-6s %7s %-6d %-9s %s %s\n", jobid, svc_status(svc), svc->pid,
runlevel_string(svc->runlevels), svc->cmd, args);
} else {
printf("%-6s %7s %-6d %-9s %-20s %s\n", jobid, svc_status(svc), svc->pid,
runlevel_string(svc->runlevels), svc->cmd, svc->desc);
}
}
return 0;
}
/* telinit q | telinit <1-6> */
static int usage(int rc)
{
fprintf(stderr, "Usage: %s [OPTIONS] [<COMMAND> | <q | 1-6>]\n\n"
fprintf(stderr, "Usage: %s <q | 1-6>\n\n"
"Options:\n"
" -d, --debug Toggle Finit debug\n"
" -v, --verbose Verbose output\n"
" -h, --help This help text\n\n"
"Commands:\n"
" debug Toggle Finit debug\n"
" q | reload Reload *.conf in /etc/finit.d/\n"
" runlevel <1-6> Set new runlevel\n"
" status Show status of services\n"
" start <JOB#> Start stopped service\n"
" stop <JOB#> Stop running service\n"
" restart <JOB#> Restart (stop/start) running service\n"
" reload <JOB#> Reload (SIGHUP) running service\n"
" version Show Finit version\n\n", __progname);
" -V, --version Show Finit version\n\n", __progname);
return rc;
}
@@ -183,54 +74,33 @@ static int usage(int rc)
int client(int argc, char *argv[])
{
int c;
command_t command[] = {
{ "debug", toggle_debug },
{ "q", do_reload }, /* SysV init compat. */
{ "reload", do_reload },
{ "runlevel", set_runlevel },
{ "status", show_status },
{ "start", do_start },
{ "stop", do_stop },
{ "restart", do_restart },
{ "version", show_version },
{ NULL, NULL }
};
struct option long_options[] = {
{"debug", 0, NULL, 'd'},
{"help", 0, NULL, 'h'},
{"verbose", 0, NULL, 'v'},
{"version", 0, NULL, 'v'},
{NULL, 0, NULL, 0}
};
verbose = 0;
while ((c = getopt_long(argc, argv, "h?dv", long_options, NULL)) != EOF) {
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
switch(c) {
case 'd':
toggle_debug(NULL);
break;
case 'v':
return puts("v" VERSION) == EOF;
case 'h':
case '?':
return usage(0);
case 'v':
verbose = 1;
break;
}
}
if (optind < argc) {
char *cmd = argv[optind++];
char *job = optind < argc ? argv[optind] : NULL;
int req = (int)argv[optind][0];
/* Compat: 'init <1-9>' */
if (strlen(cmd) == 1 && isdigit((int)cmd[0]))
return set_runlevel(cmd);
if (isdigit(req))
return do_send(INIT_CMD_RUNLVL, req);
for (c = 0; command[c].cmd; c++) {
if (!strcmp(command[c].cmd, cmd))
return command[c].cb(job);
}
if (req == 'q' || req == 'Q')
return do_send(INIT_CMD_RELOAD, 0);
}
return usage(1);
+356
View File
@@ -0,0 +1,356 @@
/* Functions for exec'ing processes
*
* 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 <ctype.h> /* isdigit() */
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "finit.h"
#include "sig.h"
#include "helpers.h"
#include "libite/lite.h"
#define NUM_ARGS 16
/* Wait for process completion, returns status of waitpid(2) syscall */
int complete(char *cmd, int pid)
{
int status = 0;
if (waitpid(pid, &status, 0) == -1) {
if (errno == EINTR)
_e("Caught unblocked signal waiting for %s, aborting.", cmd);
else if (errno == ECHILD)
_e("Caught SIGCHLD waiting for %s, aborting.", cmd);
else
_e("Failed starting %s, error %d: %s", cmd, errno, strerror (errno));
return -1;
}
return status;
}
int run(char *cmd)
{
int status, result, i = 0;
char *args[NUM_ARGS + 1], *arg, *backup;
pid_t pid;
/* We must create a copy that is possible to modify. */
backup = arg = strdup(cmd);
if (!arg)
return 1; /* Failed allocating a string to be modified. */
/* Split command line into tokens of an argv[] array. */
args[i++] = strsep(&arg, "\t ");
while (arg && i < NUM_ARGS) {
/* Handle run("su -c \"dbus-daemon --system\" messagebus");
* => "su", "-c", "\"dbus-daemon --system\"", "messagebus" */
if (*arg == '\'' || *arg == '"') {
char *p, delim[2] = " ";
delim[0] = arg[0];
args[i++] = arg++;
strsep(&arg, delim);
p = arg - 1;
*p = *delim;
*arg++ = 0;
} else {
args[i++] = strsep(&arg, "\t ");
}
}
args[i] = NULL;
#if 0
_e("Splitting: '%s' =>", cmd);
for (i = 0; args[i]; i++)
_e("\t%s", args[i]);
#endif
if (i == NUM_ARGS && args[i]) {
_e("Command too long: %s", cmd);
free(backup);
errno = EOVERFLOW;
return 1;
}
pid = fork();
if (0 == pid) {
int i;
FILE *fp;
struct sigaction sa;
/* Reset signal handlers that were set by the parent process */
for (i = 1; i < NSIG; i++)
DFLSIG(sa, i, 0);
/* Always redirect stdio for run() */
fp = fopen("/dev/null", "w");
if (fp) {
int fd = fileno(fp);
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
}
sig_unblock();
execvp(args[0], args);
_exit(1); /* Only if execv() fails. */
} else if (-1 == pid) {
_pe("%s", args[0]);
free(backup);
return -1;
}
status = complete(args[0], pid);
if (-1 == status) {
free(backup);
return 1;
}
result = WEXITSTATUS(status);
if (WIFEXITED(status)) {
_d("Started %s and ended OK: %d", args[0], result);
} else if (WIFSIGNALED(status)) {
_d("Process %s terminated by signal %d", args[0], WTERMSIG(status));
if (!result)
result = 1; /* Must alert callee that the command did complete successfully.
* This is necessary since not all programs trap signals and
* change their return code accordingly. --Jocke */
}
free(backup);
return result;
}
int run_interactive(char *cmd, char *fmt, ...)
{
int status, oldout = 1, olderr = 2;
char line[LINE_SIZE];
va_list ap;
FILE *fp;
if (!cmd) {
errno = EINVAL;
return 1;
}
if (verbose && fmt) {
va_start(ap, fmt);
vsnprintf(line, sizeof(line), fmt, ap);
va_end(ap);
print_desc("", line);
}
/* Redirect output from cmd to a tempfile */
fp = tmpfile();
if (fp && !debug) {
oldout = dup(STDOUT_FILENO);
olderr = dup(STDERR_FILENO);
dup2(fileno(fp), STDOUT_FILENO);
dup2(fileno(fp), STDERR_FILENO);
}
/* Run cmd ... */
status = run(cmd);
/* Restore stderr/stdout */
if (fp && !debug) {
if (oldout >= 0) {
dup2(oldout, STDOUT_FILENO);
close(oldout);
}
if (olderr >= 0) {
dup2(olderr, STDERR_FILENO);
close(olderr);
}
}
if (verbose && fmt)
print_result(status);
/* Dump any results of cmd on stderr after we've printed [ OK ] or [FAIL] */
if (fp && !debug) {
size_t len, written;
rewind(fp);
do {
len = fread(line, 1, sizeof(line), fp);
written = fwrite(line, len, sizeof(char), stderr);
} while (len > 0 && written == len);
}
if (fp)
fclose(fp);
return status;
}
pid_t run_getty(char *cmd, char *args[], int console)
{
pid_t pid = fork();
if (!pid) {
int fd;
char c;
if (console) {
/* Detach from initial controlling TTY */
vhangup();
close(STDERR_FILENO);
close(STDOUT_FILENO);
close(STDIN_FILENO);
/* Attach TTY to console */
fd = open(CONSOLE, O_RDWR);
if (fd != STDIN_FILENO)
exit(1);
dup2(0, STDIN_FILENO);
dup2(0, STDOUT_FILENO);
dup2(0, STDERR_FILENO);
prctl(PR_SET_NAME, "console", 0, 0, 0);
}
sig_unblock();
setsid();
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
_pe("Failed TIOCSCTTY");
while (!fexist(SYNC_SHUTDOWN)) {
static const char msg[] = "\nPlease press Enter to activate this console.";
if (fexist(SYNC_STOPPED)) {
sleep(1);
continue;
}
if (console) {
(void)write(STDERR_FILENO, msg, sizeof(msg));
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
continue;
}
if (fexist(SYNC_STOPPED))
continue;
execv(cmd, args);
}
if (console)
close(fd);
exit(0);
}
return pid;
}
int run_parts(char *dir, char *cmd)
{
struct dirent **e;
int i, num;
num = scandir(dir, &e, NULL, alphasort);
if (num < 0) {
_d("No files found in %s, skipping ...", dir);
return -1;
}
for (i = 0; i < num; i++) {
int j = 0;
pid_t pid = 0;
mode_t mode;
char *args[NUM_ARGS];
char *name = e[i]->d_name;
char path[CMD_SIZE];
snprintf(path, sizeof(path), "%s/%s", dir, name);
mode = fmode(path);
if (!S_ISEXEC(mode) || S_ISDIR(mode)) {
_d("Skipping %s ...", path);
continue;
}
/* Fill in args[], starting with full path to executable */
args[j++] = path;
/* If the callee didn't supply a run_parts() argument */
if (!cmd) {
/* Check if S<NUM>service or K<NUM>service notation is used */
_d("Checking if %s is a sysvinit startstop script ...", name);
if (name[0] == 'S' && isdigit(name[1])) {
args[j++] = "start";
} else if (name[0] == 'K' && isdigit(name[1])) {
args[j++] = "stop";
}
} else {
args[j++] = cmd;
}
args[j++] = NULL;
pid = fork();
if (!pid) {
_d("Calling %s ...", path);
sig_unblock();
execv(path, args);
exit(0);
}
complete(path, pid);
}
while (num--)
free(e[num]);
free(e);
return 0;
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+21
View File
@@ -53,11 +53,29 @@ char *console = NULL;
uev_ctx_t *ctx = NULL; /* Main loop context */
static int banner(void)
{
if (!verbose)
return 0;
delline();
#ifdef INIT_HEADING
echo("%s", INIT_HEADING);
#else
echo("Finit v" VERSION);
echo("===================================================================");
#endif
return 0;
}
int main(int argc, char* argv[])
{
uev_ctx_t loop;
/*
* finit/init/telinit client tool uses /dev/initctl pipe
* for compatibility but initctl client tool uses socket
*/
if (getpid() != 1)
return client(argc, argv);
@@ -190,6 +208,9 @@ int main(int argc, char* argv[])
if (quiet)
verbose = 0;
/* Start new initctl API responder */
api_init(&loop);
/*
* Enter main loop to monior /dev/initctl and services
*/
+4
View File
@@ -73,6 +73,9 @@
#define LINE_SIZE 1024
#define BUF_SIZE 4096
/* We reuse the INIT_CMD_ for the new initctl tool, but telinit only
* supports changing runlevel and reloading the configuration. */
#define INIT_SOCKET _PATH_VARRUN "finit.sock"
#define INIT_MAGIC 0x03091969
#define INIT_CMD_RUNLVL 1
#define INIT_CMD_DEBUG 2 /* Toggle Finit debug */
@@ -81,6 +84,7 @@
#define INIT_CMD_STOP_SVC 5
#define INIT_CMD_RELOAD_SVC 6 /* SIGHUP service */
#define INIT_CMD_RESTART_SVC 7 /* STOP + START service */
#define INIT_CMD_QUERY_INETD 8
#define INIT_CMD_NACK 254
#define INIT_CMD_ACK 255
+9 -486
View File
@@ -1,7 +1,7 @@
/* Misc. utility functions and C-library extensions for finit and its plugins
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2014 Joachim Nilsson <troglobit@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
@@ -22,36 +22,22 @@
* THE SOFTWARE.
*/
#include <ctype.h> /* isdigit() */
#include <string.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <stdarg.h>
#include <sys/wait.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <limits.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <string.h>
#include <sys/ioctl.h>
#include "finit.h"
#include "helpers.h"
#include "private.h"
#include "sig.h"
#include "libite/lite.h"
#define NUM_ARGS 16
#define NUM_SCRIPTS 128 /* ought to be enough for anyone */
/*
* Helpers to replace system() calls
@@ -162,164 +148,6 @@ char *runlevel_string(int levels)
return lvl;
}
/**
* banner - Show Finit version at boot or from cmdline
*
* This function prints the default Finit version, or a custom heading.
*
* Returns:
* This function is always successful, returning POSIX OK(0).
*/
int banner(void)
{
if (!verbose)
return 0;
delline();
#ifdef INIT_HEADING
echo("%s", INIT_HEADING);
#else
echo("Finit v" VERSION);
echo("===================================================================");
#endif
return 0;
}
/**
* pidfile_read - Reads a PID value from a pidfile.
* @pidfile: File containing PID, usually in /var/run/<PROC>.pid
*
* This function takes a @pidfile and returns the PID found therein.
*
* Returns:
* On invalid @pidfile -1 and @errno set to %EINVAL, when @pidfile does not exist -1
* and errno set to %ENOENT. When the pidfile is empty or when its contents cannot
* be translated this function returns zero (0), on success this function returns
* a PID value greater than one. PID 1 is reserved for the system init process.
*/
pid_t pidfile_read(char *pidfile)
{
pid_t pid = 0;
char buf[16];
FILE *fp;
if (!pidfile) {
errno = EINVAL;
return -1;
}
if (!fexist(pidfile))
return -1;
fp = fopen(pidfile, "r");
if (!fp)
return -1;
if (fgets(buf, sizeof(buf), fp)) {
errno = 0;
pid = (pid_t)strtol(buf, NULL, 10);
if (errno)
pid = 0; /* Failed conversion. */
}
fclose(fp);
return pid;
}
/**
* pidfile_poll - Poll for the existence of a pidfile and return PID
* @cmd: Process name, or command, called to expect a pidfile
* @path: Path to pidfile to poll for
*
* This function polls for the pidfile at @path for at most 5 seconds
* before timing out. If the file is created within that time span the
* file is read and its PID contents returned.
*
* Returns:
* The PID read from @path, or zero on timeout.
*/
pid_t pidfile_poll(char *cmd, char *path)
{
pid_t pid = 0;
int tries = 0;
/* Timeout = 100 * 50ms = 5s */
while (!fexist(path) && tries++ < 100) {
/* Wait 50ms between retries */
usleep(50000);
}
if (!fexist(path)) {
_e("Timeout! No PID found for %s, pidfile %s does not exist?", cmd, path);
pid = 0;
} else {
pid = pidfile_read(path);
}
return pid;
}
/**
* pid_alive - Check if a given process ID is running
* @pid: Process ID to check for.
*
* Returns:
* %TRUE(1) if pid is alive (/proc/pid exists), otherwise %FALSE(0)
*/
int pid_alive(pid_t pid)
{
char name[24]; /* Enough for max pid_t */
snprintf(name, sizeof(name), "/proc/%d", pid);
return fexist(name);
}
/**
* pid_get_name - Find name of a process
* @pid: PID of process to find.
* @name: Pointer to buffer where to return process name, may be %NULL
* @len: Length in bytes of @name buffer.
*
* If @name is %NULL, or @len is zero the function will return
* a static string. This may be useful to one-off calls.
*
* Returns:
* %NULL on error, otherwise a va
*/
char *pid_get_name(pid_t pid, char *name, size_t len)
{
int ret = 1;
FILE *fp;
char *pname, path[32];
static char line[64];
snprintf(path, sizeof(path), "/proc/%d/status", pid);
fp = fopen(path, "r");
if (fp) {
if (fgets(line, sizeof (line), fp)) {
pname = line + 6; /* Skip first part of line --> "Name:\t" */
chomp(pname);
if (name)
strlcpy(name, pname, len);
ret = 0; /* Found it! */
}
fclose(fp);
}
if (ret)
return NULL;
if (name)
return name;
return pname;
}
static int print_timestamp(void)
{
#if defined(CONFIG_PRINTK_TIME)
@@ -393,307 +221,6 @@ int print_result(int fail)
return fail;
}
/* Wait for process completion, returns status of waitpid(2) syscall */
int complete(char *cmd, int pid)
{
int status = 0;
if (waitpid(pid, &status, 0) == -1) {
if (errno == EINTR)
_e("Caught unblocked signal waiting for %s, aborting.", cmd);
else if (errno == ECHILD)
_e("Caught SIGCHLD waiting for %s, aborting.", cmd);
else
_e("Failed starting %s, error %d: %s", cmd, errno, strerror (errno));
return -1;
}
return status;
}
int run(char *cmd)
{
int status, result, i = 0;
char *args[NUM_ARGS + 1], *arg, *backup;
pid_t pid;
/* We must create a copy that is possible to modify. */
backup = arg = strdup(cmd);
if (!arg)
return 1; /* Failed allocating a string to be modified. */
/* Split command line into tokens of an argv[] array. */
args[i++] = strsep(&arg, "\t ");
while (arg && i < NUM_ARGS) {
/* Handle run("su -c \"dbus-daemon --system\" messagebus");
* => "su", "-c", "\"dbus-daemon --system\"", "messagebus" */
if (*arg == '\'' || *arg == '"') {
char *p, delim[2] = " ";
delim[0] = arg[0];
args[i++] = arg++;
strsep(&arg, delim);
p = arg - 1;
*p = *delim;
*arg++ = 0;
} else {
args[i++] = strsep(&arg, "\t ");
}
}
args[i] = NULL;
#if 0
_e("Splitting: '%s' =>", cmd);
for (i = 0; args[i]; i++)
_e("\t%s", args[i]);
#endif
if (i == NUM_ARGS && args[i]) {
_e("Command too long: %s", cmd);
free(backup);
errno = EOVERFLOW;
return 1;
}
pid = fork();
if (0 == pid) {
int i;
FILE *fp;
struct sigaction sa;
/* Reset signal handlers that were set by the parent process */
for (i = 1; i < NSIG; i++)
DFLSIG(sa, i, 0);
/* Always redirect stdio for run() */
fp = fopen("/dev/null", "w");
if (fp) {
int fd = fileno(fp);
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
}
sig_unblock();
execvp(args[0], args);
_exit(1); /* Only if execv() fails. */
} else if (-1 == pid) {
_pe("%s", args[0]);
free(backup);
return -1;
}
status = complete(args[0], pid);
if (-1 == status) {
free(backup);
return 1;
}
result = WEXITSTATUS(status);
if (WIFEXITED(status)) {
_d("Started %s and ended OK: %d", args[0], result);
} else if (WIFSIGNALED(status)) {
_d("Process %s terminated by signal %d", args[0], WTERMSIG(status));
if (!result)
result = 1; /* Must alert callee that the command did complete successfully.
* This is necessary since not all programs trap signals and
* change their return code accordingly. --Jocke */
}
free(backup);
return result;
}
int run_interactive(char *cmd, char *fmt, ...)
{
int status, oldout = 1, olderr = 2;
char line[LINE_SIZE];
va_list ap;
FILE *fp;
if (!cmd) {
errno = EINVAL;
return 1;
}
if (verbose && fmt) {
va_start(ap, fmt);
vsnprintf(line, sizeof(line), fmt, ap);
va_end(ap);
print_desc("", line);
}
/* Redirect output from cmd to a tempfile */
fp = tmpfile();
if (fp && !debug) {
oldout = dup(STDOUT_FILENO);
olderr = dup(STDERR_FILENO);
dup2(fileno(fp), STDOUT_FILENO);
dup2(fileno(fp), STDERR_FILENO);
}
/* Run cmd ... */
status = run(cmd);
/* Restore stderr/stdout */
if (fp && !debug) {
if (oldout >= 0) {
dup2(oldout, STDOUT_FILENO);
close(oldout);
}
if (olderr >= 0) {
dup2(olderr, STDERR_FILENO);
close(olderr);
}
}
if (verbose && fmt)
print_result(status);
/* Dump any results of cmd on stderr after we've printed [ OK ] or [FAIL] */
if (fp && !debug) {
size_t len, written;
rewind(fp);
do {
len = fread(line, 1, sizeof(line), fp);
written = fwrite(line, len, sizeof(char), stderr);
} while (len > 0 && written == len);
}
if (fp)
fclose(fp);
return status;
}
pid_t run_getty(char *cmd, char *args[], int console)
{
pid_t pid = fork();
if (!pid) {
int fd;
char c;
if (console) {
/* Detach from initial controlling TTY */
vhangup();
close(STDERR_FILENO);
close(STDOUT_FILENO);
close(STDIN_FILENO);
/* Attach TTY to console */
fd = open(CONSOLE, O_RDWR);
if (fd != STDIN_FILENO)
exit(1);
dup2(0, STDIN_FILENO);
dup2(0, STDOUT_FILENO);
dup2(0, STDERR_FILENO);
prctl(PR_SET_NAME, "console", 0, 0, 0);
}
sig_unblock();
setsid();
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
_pe("Failed TIOCSCTTY");
while (!fexist(SYNC_SHUTDOWN)) {
static const char msg[] = "\nPlease press Enter to activate this console.";
if (fexist(SYNC_STOPPED)) {
sleep(1);
continue;
}
if (console) {
(void)write(STDERR_FILENO, msg, sizeof(msg));
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
continue;
}
if (fexist(SYNC_STOPPED))
continue;
execv(cmd, args);
}
if (console)
close(fd);
exit(0);
}
return pid;
}
int run_parts(char *dir, char *cmd)
{
struct dirent **e;
int i, num;
num = scandir(dir, &e, NULL, alphasort);
if (num < 0) {
_d("No files found in %s, skipping ...", dir);
return -1;
}
for (i = 0; i < num; i++) {
int j = 0, status;
pid_t pid = 0;
mode_t mode;
char *args[NUM_ARGS];
char *name = e[i]->d_name;
char path[CMD_SIZE];
snprintf(path, sizeof(path), "%s/%s", dir, name);
mode = fmode(path);
if (!S_ISEXEC(mode) || S_ISDIR(mode)) {
_d("Skipping %s ...", path);
continue;
}
/* Fill in args[], starting with full path to executable */
args[j++] = path;
/* If the callee didn't supply a run_parts() argument */
if (!cmd) {
/* Check if S<NUM>service or K<NUM>service notation is used */
_d("Checking if %s is a sysvinit startstop script ...", name);
if (name[0] == 'S' && isdigit(name[1])) {
args[j++] = "start";
} else if (name[0] == 'K' && isdigit(name[1])) {
args[j++] = "stop";
}
} else {
args[j++] = cmd;
}
args[j++] = NULL;
pid = fork();
if (!pid) {
_d("Calling %s ...", path);
sig_unblock();
execv(path, args);
exit(0);
}
waitpid(pid, &status, 0);
}
while (num--)
free(e[num]);
free(e);
return 0;
}
#ifndef ENABLE_STATIC
int getuser(char *username)
{
@@ -716,10 +243,6 @@ int getgroup(char *group)
}
#endif
/*
* Other convenience functions
*/
void chomp(char *str)
{
char *x = strrchr(str, 0x0a);
-1
View File
@@ -93,7 +93,6 @@ void ifconfig (char *ifname, char *addr, char *mask, int up);
int atonum (char *str);
char *runlevel_string (int levels);
int banner (void);
pid_t pidfile_read (char *pidfile);
pid_t pidfile_poll (char *cmd, char *path);
+54
View File
@@ -390,6 +390,60 @@ int inetd_match(inetd_t *inetd, char *service, char *proto, char *port)
return 0;
}
/* Compose presentable string of inetd filters: !eth0,eth1,eth2,!eth3 */
int inetd_filter_str(inetd_t *inetd, char *str, size_t len)
{
int prev = 0;
char buf[42];
inetd_filter_t *filter;
if (!inetd || !str || len <= 0) {
_e("Dafuq?");
return 1;
}
snprintf(str, len, "%s allow ", inetd->name);
LIST_FOREACH(filter, &inetd->filters, link) {
char ifname[IFNAMSIZ];
if (filter->deny)
continue;
if (!strlen(filter->ifname))
snprintf(ifname, sizeof(ifname), "ANY");
else
strlcpy(ifname, filter->ifname, sizeof(ifname));
snprintf(buf, sizeof(buf), "%s%s:%d", prev ? "," : "",
ifname, inetd->port);
prev = 1;
strlcat(str, buf, len);
}
prev = 0;
LIST_FOREACH(filter, &inetd->filters, link) {
char ifname[IFNAMSIZ];
if (!filter->deny)
continue;
if (!prev) {
snprintf(buf, sizeof(buf), " deny ");
strlcat(str, buf, len);
}
if (!strlen(filter->ifname))
snprintf(ifname, sizeof(ifname), "ANY");
else
strlcpy(ifname, filter->ifname, sizeof(ifname));
snprintf(buf, sizeof(buf), "%s%s", prev ? "," : "", ifname);
prev = 1;
strlcat(str, buf, len);
}
return 0;
}
/*
* This function is called to add a new, unique, inetd service. When an
* ifname is given as argument this means *only* run service on this
+1
View File
@@ -65,6 +65,7 @@ int inetd_del (inetd_t *inetd);
int inetd_init (inetd_t *inetd, void *arg, char *ifname, char *port);
int inetd_match (inetd_t *inetd, char *service, char *proto, char *port);
int inetd_filter_str (inetd_t *inetd, char *str, size_t len);
int inetd_allow (inetd_t *inetd, char *ifname);
int inetd_deny (inetd_t *inetd, char *ifname);
+264
View File
@@ -0,0 +1,264 @@
/* New client tool, complements old /dev/initctl API and telinit tool
*
* Copyright (c) 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 <ctype.h>
#include <getopt.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "finit.h"
#include "helpers.h"
#include "service.h"
#include "libite/lite.h"
typedef struct {
char *cmd;
int (*cb)(char *arg);
} command_t;
int debug = 0;
int verbose = 0;
static int do_send(struct init_request *rq, ssize_t len)
{
int sd, result = 255;
struct sockaddr_un sun = {
.sun_family = AF_UNIX,
.sun_path = INIT_SOCKET,
};
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (-1 == sd)
return -1;
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
goto error;
if (write(sd, rq, len) != len)
goto error;
if (read(sd, rq, len) != len)
goto error;
result = 0;
goto exit;
error:
perror("Failed communicating with finit");
exit:
close(sd);
return result;
}
static int toggle_debug(char *UNUSED(arg))
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_DEBUG,
};
return do_send(&rq, sizeof(rq));
}
static int set_runlevel(char *arg)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_RUNLVL,
.runlevel = (int)arg[0],
};
return do_send(&rq, sizeof(rq));
}
static int do_svc(int cmd, char *jobid)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = cmd,
};
if (!jobid) {
if (cmd == INIT_CMD_RELOAD_SVC) {
rq.cmd = INIT_CMD_RELOAD;
goto exit;
}
return 1;
}
strlcpy(rq.data, jobid, sizeof(rq.data));
exit:
return do_send(&rq, sizeof(rq));
}
static int do_start (char *arg) { return do_svc(INIT_CMD_START_SVC, arg); }
static int do_stop (char *arg) { return do_svc(INIT_CMD_STOP_SVC, arg); }
static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD_SVC, arg); }
static int do_restart(char *arg) { return do_svc(INIT_CMD_RESTART_SVC, arg); }
static int show_version(char *UNUSED(arg))
{
puts("v" VERSION);
return 0;
}
/* In verbose mode we skip the header and each service description.
* This in favor of having all info on one line so a machine can more
* easily parse it. */
static int show_status(char *UNUSED(arg))
{
svc_t *svc;
if (!verbose) {
printf("# Status PID Runlevels Service Description\n");
printf("====================================================================================\n");
}
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
int inetd = svc_is_inetd(svc);
char jobid[10];
if (svc_is_unique(svc))
snprintf(jobid, sizeof(jobid), "%d", svc->job);
else
snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id);
if (verbose) {
int internal = !strcmp("internal", svc->cmd);
char *info, args[80] = "";
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_QUERY_INETD,
};
printf("%-6s %7s %-6d %-9s ", jobid, svc_status(svc), svc->pid, runlevel_string(svc->runlevels));
if (inetd) {
snprintf(rq.data, sizeof(rq.data), "%s", jobid);
if (do_send(&rq, sizeof(rq))) {
snprintf(args, sizeof(args), "Unknown inetd");
info = args;
} else {
info = rq.data;
if (!internal) {
char *ptr = strchr(info, ' ');
if (ptr)
info = ptr + 1;
}
}
printf("%s %s\n", svc->cmd, info);
} else {
int i;
for (i = 1; i < MAX_NUM_SVC_ARGS; i++) {
strlcat(args, svc->args[i], sizeof(args));
strlcat(args, " ", sizeof(args));
}
printf("%s %s\n", svc->cmd, args);
}
} else {
printf("%-6s %7s %-6d %-9s %-20s %s\n", jobid, svc_status(svc), svc->pid,
runlevel_string(svc->runlevels), svc->cmd, svc->desc);
}
}
return 0;
}
static int usage(int rc)
{
fprintf(stderr, "Usage: %s [OPTIONS] <COMMAND>\n\n"
"Options:\n"
" -v, --verbose Verbose output\n"
" -h, --help This help text\n\n"
"Commands:\n"
" debug Toggle Finit debug\n"
" reload Reload *.conf in /etc/finit.d/\n"
" runlevel <1-6> Set new runlevel\n"
" status Show status of services\n"
" start <JOB#> Start stopped service\n"
" stop <JOB#> Stop running service\n"
" restart <JOB#> Restart (stop/start) running service\n"
" reload <JOB#> Reload (SIGHUP) running service\n"
" version Show Finit version\n\n", __progname);
return rc;
}
int main(int argc, char *argv[])
{
int c;
command_t command[] = {
{ "debug", toggle_debug },
{ "reload", do_reload },
{ "runlevel", set_runlevel },
{ "status", show_status },
{ "start", do_start },
{ "stop", do_stop },
{ "restart", do_restart },
{ "version", show_version },
{ NULL, NULL }
};
struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"verbose", 0, NULL, 'v'},
{NULL, 0, NULL, 0}
};
verbose = 0;
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
switch(c) {
case 'h':
case '?':
return usage(0);
case 'v':
verbose = 1;
break;
}
}
if (optind < argc) {
char *cmd = argv[optind++];
char *arg = optind < argc ? argv[optind] : NULL;
for (c = 0; command[c].cmd; c++) {
if (!strcmp(command[c].cmd, cmd))
return command[c].cb(arg);
}
}
return usage(1);
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+174
View File
@@ -0,0 +1,174 @@
/* Misc. utility functions and C-library extensions for finit and its plugins
*
* 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 <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "helpers.h"
#include "libite/lite.h"
/**
* pidfile_read - Reads a PID value from a pidfile.
* @pidfile: File containing PID, usually in /var/run/<PROC>.pid
*
* This function takes a @pidfile and returns the PID found therein.
*
* Returns:
* On invalid @pidfile -1 and @errno set to %EINVAL, when @pidfile does not exist -1
* and errno set to %ENOENT. When the pidfile is empty or when its contents cannot
* be translated this function returns zero (0), on success this function returns
* a PID value greater than one. PID 1 is reserved for the system init process.
*/
pid_t pidfile_read(char *pidfile)
{
pid_t pid = 0;
char buf[16];
FILE *fp;
if (!pidfile) {
errno = EINVAL;
return -1;
}
if (!fexist(pidfile))
return -1;
fp = fopen(pidfile, "r");
if (!fp)
return -1;
if (fgets(buf, sizeof(buf), fp)) {
errno = 0;
pid = (pid_t)strtol(buf, NULL, 10);
if (errno)
pid = 0; /* Failed conversion. */
}
fclose(fp);
return pid;
}
/**
* pidfile_poll - Poll for the existence of a pidfile and return PID
* @cmd: Process name, or command, called to expect a pidfile
* @path: Path to pidfile to poll for
*
* This function polls for the pidfile at @path for at most 5 seconds
* before timing out. If the file is created within that time span the
* file is read and its PID contents returned.
*
* Returns:
* The PID read from @path, or zero on timeout.
*/
pid_t pidfile_poll(char *cmd, char *path)
{
pid_t pid = 0;
int tries = 0;
/* Timeout = 100 * 50ms = 5s */
while (!fexist(path) && tries++ < 100) {
/* Wait 50ms between retries */
usleep(50000);
}
if (!fexist(path)) {
_e("Timeout! No PID found for %s, pidfile %s does not exist?", cmd, path);
pid = 0;
} else {
pid = pidfile_read(path);
}
return pid;
}
/**
* pid_alive - Check if a given process ID is running
* @pid: Process ID to check for.
*
* Returns:
* %TRUE(1) if pid is alive (/proc/pid exists), otherwise %FALSE(0)
*/
int pid_alive(pid_t pid)
{
char name[24]; /* Enough for max pid_t */
snprintf(name, sizeof(name), "/proc/%d", pid);
return fexist(name);
}
/**
* pid_get_name - Find name of a process
* @pid: PID of process to find.
* @name: Pointer to buffer where to return process name, may be %NULL
* @len: Length in bytes of @name buffer.
*
* If @name is %NULL, or @len is zero the function will return
* a static string. This may be useful to one-off calls.
*
* Returns:
* %NULL on error, otherwise a va
*/
char *pid_get_name(pid_t pid, char *name, size_t len)
{
int ret = 1;
FILE *fp;
char *pname, path[32];
static char line[64];
snprintf(path, sizeof(path), "/proc/%d/status", pid);
fp = fopen(path, "r");
if (fp) {
if (fgets(line, sizeof (line), fp)) {
pname = line + 6; /* Skip first part of line --> "Name:\t" */
chomp(pname);
if (name)
strlcpy(name, pname, len);
ret = 0; /* Found it! */
}
fclose(fp);
}
if (ret)
return NULL;
if (name)
return name;
return pname;
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+2 -83
View File
@@ -1,7 +1,7 @@
/* Optional /dev/initctl FIFO monitor
/* Optional /dev/initctl FIFO monitor, for receiving commands from telinit
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2014 Joachim Nilsson <troglobit@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
@@ -53,48 +53,6 @@ static void fifo_open(void)
}
}
static svc_t *convert_jobid(char *jobid)
{
int job, id = 1;
char *token;
if (!jobid)
return NULL;
token = strtok(jobid, ":");
if (!token) {
job = atoi(jobid);
} else {
job = atoi(token);
token = strtok(NULL, "");
if (token)
id = atoi(token);
}
_d("Got job %d id %d ...", job, id);
return svc_find_by_jobid(job, id);
}
static int do_start(char *jobid)
{
return service_start(convert_jobid(jobid));
}
static int do_stop(char *jobid)
{
return service_stop(convert_jobid(jobid));
}
static int do_reload(char *jobid)
{
return service_reload(convert_jobid(jobid));
}
static int do_restart(char *jobid)
{
return service_restart(convert_jobid(jobid));
}
/* Standard reboot/shutdown utilities talk to init using /dev/initctl.
* We should check if the fifo was recreated and reopen it.
*/
@@ -102,9 +60,7 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
{
struct init_request rq;
_d("Receiving request on descriptor %d, from %s ...", fd, FINIT_FIFO);
while (1) {
int result = 0;
ssize_t len = read(fd, &rq, sizeof(rq));
if (len <= 0) {
@@ -119,7 +75,6 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
}
_d("Nothing to do, bailing out.");
break;
}
@@ -128,7 +83,6 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
break;
}
_d("Magic OK ...");
switch (rq.cmd) {
case INIT_CMD_RUNLVL:
switch (rq.runlevel) {
@@ -160,51 +114,16 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
}
break;
case INIT_CMD_DEBUG:
debug = !debug;
break;
case INIT_CMD_RELOAD:
reload_finit_d();
break;
case INIT_CMD_START_SVC:
result = do_start(rq.data);
break;
case INIT_CMD_STOP_SVC:
result = do_stop(rq.data);
break;
case INIT_CMD_RELOAD_SVC:
result = do_reload(rq.data);
break;
case INIT_CMD_RESTART_SVC:
result = do_restart(rq.data);
break;
case INIT_CMD_ACK:
_d("Client failed reading ACK.");
goto leave;
default:
_d("Unsupported cmd: %d", rq.cmd);
break;
}
if (result)
rq.cmd = INIT_CMD_NACK;
else
rq.cmd = INIT_CMD_ACK;
len = write(fd, &rq, sizeof(rq));
if (len != sizeof(rq))
_d("Failed sending ACK/NACK back to client.");
else
sleep(1); /* Give client time to read FIFO */
}
leave:
close(fd);
fifo_open();
}
+1
View File
@@ -30,6 +30,7 @@
uev_ctx_t *ctx; /* Main loop context */
int api_init (uev_ctx_t *ctx);
int client (int argc, char *argv[]);
void service_bootstrap(void);
+2 -2
View File
@@ -308,9 +308,9 @@ char *svc_status(svc_t *svc)
return "running";
if (svc_is_inetd(svc))
return "waiting";
return "inetd";
return "stopped";
return "waiting";
}
/* Same base service, return unique ID */