Add support for calling initctl with process name as well as JOBID

This patch makes it possible to communicate process name or JOB[:ID]
from initctl to Finit.  When the process name translates into a service
with multiple instances Finit applies the requested command to all
instances.  For example, two Dropbear SSH services running on two
different ports, calling `initctl stop dropbear` stops both services.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson committed 2015-06-21 15:54:47 +02:00
1 parent 5d2172eac8
commit 577622c945
7 files changed
+166 -107

No files matched your search

+37 -32
View File
@@ -41,45 +41,37 @@
uev_t api_watcher;
/* Make sure the job id is NUL terminated and on the form SVC:ID. */
static char *sanitize_jobid(char *jobid, size_t len)
/* Sanitize user input, make sure to NUL terminate. */
static char *sanitize(char *arg, size_t len)
{
size_t i = 0;
static char buf[42];
while (isdigit(jobid[i]) && i < len)
while ((isalnum(arg[i]) || isspace(arg[i])) && i < len)
i++;
if (jobid[i++] == ':') {
while (isdigit(jobid[i]) && i < len)
i++;
}
if (i + 1 < sizeof(buf)) {
memcpy(buf, jobid, i);
buf[i + 1] = 0;
return buf;
if (i + 1 < len) {
arg[i + 1] = 0;
return arg;
}
return NULL;
}
static svc_t *convert_jobid(int noid, char *jobid, size_t len)
static svc_t *convert(int noid, char *arg, size_t len)
{
int job, id = 1;
char *token, *ptr;
jobid = sanitize_jobid(jobid, len);
if (!jobid)
arg = sanitize(arg, len);
if (!arg)
return NULL;
if (noid && (ptr = strchr(jobid, ':')))
if (noid && (ptr = strchr(arg, ':')))
ptr = 0;
token = strtok(jobid, ":");
token = strtok(arg, ":");
if (!token) {
job = atoi(jobid);
job = atoi(arg);
} else {
job = atoi(token);
token = strtok(NULL, "");
@@ -91,31 +83,51 @@ static svc_t *convert_jobid(int noid, char *jobid, size_t len)
return svc_find_by_jobid(job, id);
}
static int call(int (*action)(svc_t *), char *buf, size_t len)
{
int result = 0;
char *job = strtok(sanitize(buf, len), " ");
while (job) {
svc_t *svc;
if (isdigit(job[0]))
result += action(convert(0, job, len));
else
for (svc = svc_named_iterator(1, job); svc; svc = svc_named_iterator(0, job))
result += action(svc);
job = strtok(NULL, " ");
}
return result;
}
static int do_start(char *buf, size_t len)
{
return service_start(convert_jobid(0, buf, len));
return call(service_start, buf, len);
}
static int do_stop(char *buf, size_t len)
{
return service_stop(convert_jobid(0, buf, len));
return call(service_stop, buf, len);
}
static int do_reload(char *buf, size_t len)
{
return service_reload(convert_jobid(0, buf, len));
return call(service_reload, buf, len);
}
static int do_restart(char *buf, size_t len)
{
return service_restart(convert_jobid(0, buf, len));
return call(service_restart, buf, len);
}
static int do_query_inetd(char *buf, size_t len)
{
svc_t *svc;
svc = convert_jobid(1, buf, len);
svc = convert(1, buf, len);
if (!svc || !svc_is_inetd(svc)) {
_e("Cannot %s svc %s ...", !svc ? "find" : "query, not an inetd", buf);
return 1;
@@ -152,8 +164,6 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
_e("Failed reading initctl request, error %d: %s", errno, strerror(errno));
}
_d("Nothing to do, bailing out.");
break;
}
@@ -237,11 +247,6 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
len = write(sd, &rq, sizeof(rq));
if (len != sizeof(rq))
_d("Failed sending ACK/NACK back to client.");
#if 0
else
sleep(1); /* Give client time to read FIFO */
_e("Ready for next station.");
#endif
}
leave:
+37
View File
@@ -32,6 +32,7 @@
#include <stdarg.h>
#include <string.h>
#include <sys/ioctl.h>
#include <utmp.h>
#include "finit.h"
#include "helpers.h"
@@ -122,6 +123,42 @@ int atonum(char *str)
return val;
}
static int encode(int lvl)
{
if (!lvl) return 0;
return lvl + '0';
}
void runlevel_set(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();
}
int runlevel_get(void)
{
int lvl = '?'; /* Non-existing runlevel */
struct utmp *utent;
setutent();
while ((utent = getutent())) {
if (utent->ut_type == RUN_LVL) {
lvl = utent->ut_pid & 0xFF;
break;
}
}
endutent();
return lvl - '0';
}
/**
* runlevel_string - Convert a bit encoded runlevel to .conf syntax
* @levels: Bit encoded runlevels
+2
View File
@@ -69,6 +69,8 @@ int makepath (char *path);
void ifconfig (char *ifname, char *addr, char *mask, int up);
int atonum (char *str);
void runlevel_set (int pre, int now);
int runlevel_get (void);
char *runlevel_string (int levels);
pid_t pidfile_read (char *pidfile);
+56 -49
View File
@@ -38,9 +38,9 @@ typedef struct {
int (*cb)(char *arg);
} command_t;
int debug = 0;
int verbose = 0;
int debug = 0;
int verbose = 0;
int runlevel = 0;
static int do_send(struct init_request *rq, ssize_t len)
{
@@ -93,14 +93,14 @@ static int set_runlevel(char *arg)
return do_send(&rq, sizeof(rq));
}
static int do_svc(int cmd, char *jobid)
static int do_svc(int cmd, char *arg)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = cmd,
};
if (!jobid) {
if (!arg) {
if (cmd == INIT_CMD_RELOAD_SVC) {
rq.cmd = INIT_CMD_RELOAD;
goto exit;
@@ -108,7 +108,7 @@ static int do_svc(int cmd, char *jobid)
return 1;
}
strlcpy(rq.data, jobid, sizeof(rq.data));
strlcpy(rq.data, arg, sizeof(rq.data));
exit:
return do_send(&rq, sizeof(rq));
@@ -132,57 +132,59 @@ static int show_status(char *UNUSED(arg))
{
svc_t *svc;
/* Fetch UTMP runlevel, needed for svc_status() call below */
runlevel = runlevel_get();
if (!verbose) {
printf("# Status PID Runlevels Service Description\n");
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];
char jobid[10], args[80] = "";
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_QUERY_INETD,
};
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("%-5s %7s %-6d %-9s ", jobid, svc_status(svc), svc->pid, runlevel_string(svc->runlevels));
if (!verbose) {
printf("%-20s %s\n", svc->cmd, svc->desc);
continue;
}
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;
}
}
if (inetd) {
char *info;
printf("%s %s\n", svc->cmd, info);
snprintf(rq.data, sizeof(rq.data), "%s", jobid);
if (do_send(&rq, sizeof(rq))) {
snprintf(args, sizeof(args), "Unknown inetd");
info = args;
} else {
int i;
for (i = 1; i < MAX_NUM_SVC_ARGS; i++) {
strlcat(args, svc->args[i], sizeof(args));
strlcat(args, " ", sizeof(args));
info = rq.data;
if (strcmp("internal", svc->cmd)) {
char *ptr = strchr(info, ' ');
if (ptr)
info = ptr + 1;
}
printf("%s %s\n", svc->cmd, args);
}
printf("%s %s\n", svc->cmd, info);
} else {
printf("%-6s %7s %-6d %-9s %-20s %s\n", jobid, svc_status(svc), svc->pid,
runlevel_string(svc->runlevels), svc->cmd, svc->desc);
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);
}
}
@@ -196,15 +198,15 @@ static int usage(int rc)
" -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 <0-9> Change runlevel: 0 halt, 6 reboot\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);
" debug Toggle Finit debug\n"
" reload Reload *.conf in /etc/finit.d/\n"
" runlevel <0-9> Change runlevel: 0 halt, 6 reboot\n"
" status Show status of services\n"
" start <JOB|NAME> Start stopped job or service\n"
" stop <JOB|NAME> Stop running job or service\n"
" restart <JOB|NAME> Restart (stop/start) job or service\n"
" reload <JOB|NAME> Reload (SIGHUP) job or service\n"
" version Show Finit version\n\n", __progname);
return rc;
}
@@ -244,7 +246,12 @@ int main(int argc, char *argv[])
if (optind < argc) {
char *cmd = argv[optind++];
char *arg = optind < argc ? argv[optind] : NULL;
char arg[120] = "";
while (optind < argc) {
strlcat(arg, argv[optind++], sizeof(arg));
strlcat(arg, " ", sizeof(arg));
}
for (c = 0; command[c].cmd; c++) {
if (!strcmp(command[c].cmd, cmd))
+2 -23
View File
@@ -24,7 +24,6 @@
#include <string.h>
#include <sys/wait.h>
#include <utmp.h>
#include <net/if.h>
#include "libite/lite.h"
@@ -38,10 +37,9 @@
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
static int is_norespawn (void);
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);
@@ -433,7 +431,7 @@ void service_runlevel(int newlevel)
runlevel = newlevel;
_d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel);
utmp_save(prevlevel, newlevel);
runlevel_set(prevlevel, newlevel);
/* Make sure to reload all *.conf in /etc/finit.d/ */
svc_mark_dynamic();
@@ -817,25 +815,6 @@ static void svc_dance(svc_t *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)
{
+31 -3
View File
@@ -170,6 +170,31 @@ svc_t *svc_dynamic_iterator(int first)
}
/**
* svc_named_iterator - Iterates over all instances of a service.
* @first: Get first &svc_t object, or next until end.
* @cmd: Service name to look for.
*
* Returns:
* The first matching &svc_t when @first is set, otherwise the next
* &svc_t instance with the same @cmd until the end when %NULL is
* returned.
*/
svc_t *svc_named_iterator(int first, char *cmd)
{
svc_t *svc;
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
char *name = basename(svc->cmd);
if (!strncmp(name, cmd, strlen(name)))
return svc;
}
return NULL;
}
/**
* svc_foreach - Run a callback for each registered service
* @cb: Callback to run for each service
@@ -307,10 +332,13 @@ char *svc_status(svc_t *svc)
if (svc->pid)
return "running";
if (svc_is_inetd(svc))
return "inetd";
if (!ISSET(svc->runlevels, runlevel))
return "halted";
return "waiting";
if (svc_is_inetd(svc))
return "waiting";
return "stopped";
}
/* Same base service, return unique ID */
+1
View File
@@ -121,6 +121,7 @@ svc_t *svc_find_by_jobid (int job, int id);
svc_t *svc_iterator (int first);
svc_t *svc_inetd_iterator (int first);
svc_t *svc_dynamic_iterator (int first);
svc_t *svc_named_iterator (int first, char *cmd);
void svc_foreach (void (*cb)(svc_t *));
void svc_foreach_dynamic (void (*cb)(svc_t *));