Refactor: Cleanup and expand client code to support <version|q|reload|debug>

Further refactoring of the client code.  Supported commands:

- debug:   Toggle PID 1 debug flag
- version: Show Finit version or custom header string
- reload:  Reload config files in /etc/finit.d/, same as SIGHUP

Also supported is the legacy 'init q' command, to reload config.

The 'status' command now supports an optiona '-v', either as an
argument to init itself, or as argument to the command.  When it
is given 'status' lists verbose information about all services.
Currently only the service arguments are listed.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson
2015-06-07 12:11:07 +02:00
parent 811bd75eab
commit 2026486fda
9 changed files with 233 additions and 145 deletions
-5
View File
@@ -28,14 +28,9 @@ General
service it monitors + `.sh`.
* Add support for `/etc/sysctl.d`
* Add compile-time supoprt for running `/bin/sh` instead of `getty`
* Add support for `init <q | reload-configuration>` and `SIGHUP` to
have finit reload its `finit.conf`, but also
* Add support for inotify to automatically reload `finit.conf`
* Implement `initctl stop|start|restart|reload|status <SVC>` and
`service <SVC> stop|start|restart|reload|status` on top
* Add PRE and POST hooks for when switching between runlevels
* Add support for `init -v,--version | version`
* Cleanup move sources from top-level directory to `src/` and `include/`
* Make sure to install `queue.h` to `$(PREFIX)/include/finit/queue.h`
* Move `finit.conf` "check" command to plugin which checks `/etc/fstab`
instead. This is the de-facto practice. But keep the check command
+141 -98
View File
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
#include <ctype.h>
#include <getopt.h>
#include <stdio.h>
@@ -31,107 +32,20 @@
#include "libite/lite.h"
typedef struct {
char *cmd;
int (*cb)(void);
} command_t;
static char *runlevel_string(svc_t *svc)
{
int i, pos = 1;;
static char lvl[10] = "[]";
for (i = 0; i < 10; i++) {
if (ISSET(svc->runlevels, i)) {
if (i == 0)
lvl[pos++] = 'S';
else
lvl[pos++] = '0' + i;
}
}
lvl[pos++] = ']';
lvl[pos] = 0;
return lvl;
}
static int status(void)
{
svc_t *svc;
printf("Status PID Runlevels Service Description\n");
printf("==============================================================================\n");
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
printf("%7s %-6d %-9s %-20s %s\n",
svc->pid ? "running" : "waiting", svc->pid,
runlevel_string(svc), svc->cmd, svc->desc);
}
return 0;
}
static int usage(int rc)
{
fprintf(stderr, "Usage: %s [OPTIONS] [status | <1-6>]\n\n"
"Options:\n"
" -d, --debug Enable/Disable debug\n"
" -h, --help This help text\n\n"
"Commands:\n"
" status Show status of services\n\n", __progname);
return rc;
}
int client(int argc, char *argv[])
static int do_send(struct init_request *rq, ssize_t len)
{
int fd;
int c;
int result = -1;
ssize_t len;
struct {
char *cmd;
int (*cb)(void);
} command[] = {
{ "status", status },
{ NULL, NULL }
};
struct option long_options[] = {
{"debug", 0, NULL, 'd'},
{"help", 0, NULL, 'h'},
{NULL, 0, NULL, 0}
};
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = 0
};
while ((c = getopt_long(argc, argv, "h?d", long_options, NULL)) != EOF) {
switch(c) {
case 'd':
rq.cmd = INIT_CMD_DEBUG;
break;
case 'h':
case '?':
return usage(0);
default:
return usage(1);
}
}
if (!rq.cmd) {
if (argc < 2) {
fprintf(stderr, "Missing argument.\n");
return usage(1);
}
for (c = 0; command[c].cmd; c++) {
if (!strcmp(command[c].cmd, argv[1]))
return command[c].cb();
}
rq.cmd = INIT_CMD_RUNLVL;
rq.runlevel = (int)argv[1][0];
}
ssize_t num;
if (!fexist(FINIT_FIFO)) {
fprintf(stderr, "/sbin/init does not support %s!\n", FINIT_FIFO);
fprintf(stderr, "%s does not support %s!\n", __progname, FINIT_FIFO);
return 1;
}
@@ -141,10 +55,10 @@ int client(int argc, char *argv[])
return 1;
}
len = write(fd, &rq, sizeof(rq));
if (len == sizeof(rq)) {
len = read(fd, &rq, sizeof(rq));
if (len == sizeof(rq) && rq.cmd == INIT_CMD_ACK)
num = write(fd, rq, len);
if (num == len) {
num = read(fd, rq, len);
if (num == len && rq->cmd == INIT_CMD_ACK)
result = 0;
}
@@ -153,6 +67,135 @@ int client(int argc, char *argv[])
return result;
}
static int toggle_debug(void)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_DEBUG,
};
return do_send(&rq, sizeof(rq));
}
static int reload_conf(void)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_RELOAD,
};
return do_send(&rq, sizeof(rq));
}
static int set_runlevel(int lvl)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_RUNLVL,
.runlevel = lvl,
};
return do_send(&rq, sizeof(rq));
}
int show_version(void)
{
return banner();
}
static int show_status(void)
{
svc_t *svc;
printf("Status PID Runlevels Service Description\n");
printf("==============================================================================\n");
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
printf("%7s %-6d %-9s %-20s %s\n",
svc->pid ? "running" : "waiting", svc->pid,
runlevel_string(svc->runlevels), svc->cmd, svc->desc);
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(" %s\n", args);
}
}
return 0;
}
static int usage(int rc)
{
fprintf(stderr, "Usage: %s [OPTIONS] [<COMMAND> | <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"
" reload Reload *.conf in /etc/finit.d/\n"
" status Show status of services\n"
" version Show Finit version\n\n", __progname);
return rc;
}
int client(int argc, char *argv[])
{
int c;
command_t command[] = {
{ "debug", toggle_debug },
{ "q", reload_conf }, /* SysV init compat. */
{ "reload", reload_conf },
// { "runlevel", set_runlevel },
{ "status", show_status },
{ "version", show_version },
{ NULL, NULL }
};
struct option long_options[] = {
{"debug", 0, NULL, 'd'},
{"help", 0, NULL, 'h'},
{"verbose", 0, NULL, 'v'},
{NULL, 0, NULL, 0}
};
verbose = 0;
while ((c = getopt_long(argc, argv, "h?dv", long_options, NULL)) != EOF) {
switch(c) {
case 'd':
toggle_debug();
break;
case 'h':
case '?':
return usage(0);
case 'v':
verbose = 1;
break;
}
}
if (optind < argc) {
char *cmd = argv[optind];
/* Compat: 'init <1-9>' */
if (strlen(cmd) == 1 && isdigit((int)cmd[0]))
return set_runlevel((int)cmd[0]);
for (c = 0; command[c].cmd; c++) {
if (!strcmp(command[c].cmd, cmd))
return command[c].cb();
}
}
return usage(1);
}
/**
* Local Variables:
* version-control: t
+35
View File
@@ -54,6 +54,25 @@ static char *strip_line(char *line)
return line;
}
void parse_kernel_cmdline(void)
{
FILE *fp;
char line[LINE_SIZE];
if ((fp = fopen("/proc/cmdline", "r")) != NULL) {
fgets(line, sizeof(line), fp);
_d("Kernel command line: %s", line);
if (strstr(line, "finit_debug") || strstr(line, "--debug"))
debug = 1;
if (!debug && strstr(line, "quiet"))
verbose = 0;
fclose(fp);
}
}
/* Convert optional "[!123456789S]" string into a bitmask */
int parse_runlevels(char *runlevels)
{
@@ -378,6 +397,22 @@ int parse_finit_conf(char *file)
return result;
}
/* Called on SIGHUP or 'init q' */
void reload_finit_d(void)
{
/* Mark and sweep */
svc_mark_dynamic();
/* Reload all *.conf in /etc/finit.d/ */
parse_finit_d(rcsd);
/* Reload dirty services */
service_reload_dynamic();
/* Cleanup stale services */
svc_clean_dynamic(service_unregister);
}
/**
* Local Variables:
-28
View File
@@ -52,34 +52,6 @@ char *console = NULL;
uev_ctx_t *ctx = NULL; /* Main loop context */
static void parse_kernel_cmdline(void)
{
FILE *fp;
char line[LINE_SIZE];
if ((fp = fopen("/proc/cmdline", "r")) != NULL) {
fgets(line, sizeof(line), fp);
_d("Kernel command line: %s", line);
if (strstr(line, "finit_debug") || strstr(line, "--debug"))
debug = 1;
if (!debug && strstr(line, "quiet"))
verbose = 0;
fclose(fp);
}
}
static void banner(void)
{
delline();
#ifdef INIT_HEADING
echo("%s", INIT_HEADING);
#else
echo("Finit version " VERSION " (" WHOAMI ") " __DATE__ " " __TIME__);
#endif
}
int main(int argc, char* argv[])
{
+6 -3
View File
@@ -76,6 +76,7 @@
#define INIT_MAGIC 0x03091969
#define INIT_CMD_RUNLVL 1
#define INIT_CMD_DEBUG 2
#define INIT_CMD_RELOAD 3
#define INIT_CMD_ACK 255
struct init_request {
@@ -99,9 +100,11 @@ extern char *console;
extern char *__progname;
/* conf.c */
int parse_runlevels (char *runlevels);
int parse_finit_conf (char *file);
int parse_finit_d (char *dir);
void parse_kernel_cmdline (void);
int parse_runlevels (char *runlevels);
int parse_finit_conf (char *file);
int parse_finit_d (char *dir);
void reload_finit_d (void);
#endif /* FINIT_H_ */
+44
View File
@@ -136,6 +136,50 @@ int atonum(char *str)
return val;
}
/**
* runlevel_string - Convert a bit encoded runlevel to .conf syntax
* @levels: Bit encoded runlevels
*
* Returns:
* Pointer to string on the form [2345]
*/
char *runlevel_string(int levels)
{
int i, pos = 1;;
static char lvl[10] = "[]";
for (i = 0; i < 10; i++) {
if (ISSET(levels, i)) {
if (i == 0)
lvl[pos++] = 'S';
else
lvl[pos++] = '0' + i;
}
}
lvl[pos++] = ']';
lvl[pos] = 0;
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)
{
delline();
#ifdef INIT_HEADING
echo("%s", INIT_HEADING);
#else
echo("Finit version " VERSION " (" WHOAMI ") " __DATE__ " " __TIME__);
#endif
return 0;
}
/**
* pidfile_read - Reads a PID value from a pidfile.
+2
View File
@@ -95,6 +95,8 @@ int makepath (char *path);
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);
+4
View File
@@ -121,6 +121,10 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
debug = !debug;
break;
case INIT_CMD_RELOAD:
reload_finit_d();
break;
case INIT_CMD_ACK:
_d("Client failed reading ACK.");
goto leave;
+1 -11
View File
@@ -93,17 +93,7 @@ void do_shutdown(int sig)
*/
static void sighup_cb(uev_ctx_t *UNUSED(ctx), uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
{
/* Mark and sweep */
svc_mark_dynamic();
/* Reload all *.conf in /etc/finit.d/ */
parse_finit_d(rcsd);
/* Reload dirty services */
service_reload_dynamic();
/* Cleanup stale services */
svc_clean_dynamic(service_unregister);
reload_finit_d();
}
/*