mirror of
https://github.com/troglobit/finit.git
synced 2026-10-24 11:54:36 +00:00
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:
1 parent
5d2172eac8
commit
577622c945
7 files changed
+166
-107
No files matched your search
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 *));
|
||||
|
||||
Reference in new issue
Block a user