From 6cf0a254cd9e4892a941bb5ad6f8d9ed317b1617 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Thu, 12 Apr 2018 05:30:12 +0200 Subject: Build system and directory structure cleanup Signed-off-by: David Oberhollenzer --- Makefile.am | 2 +- cmd/Makemodule.am | 15 +++- cmd/service/disable.c | 89 +++++++++++++++++++++++ cmd/service/dumpscript.c | 182 +++++++++++++++++++++++++++++++++++++++++++++++ cmd/service/enable.c | 98 +++++++++++++++++++++++++ cmd/service/help.c | 129 +++++++++++++++++++++++++++++++++ cmd/service/list.c | 102 ++++++++++++++++++++++++++ cmd/service/servicecmd.c | 90 +++++++++++++++++++++++ cmd/service/servicecmd.h | 93 ++++++++++++++++++++++++ scripts/Makemodule.am | 5 +- servicecmd/Makemodule.am | 13 ---- servicecmd/disable.c | 89 ----------------------- servicecmd/dumpscript.c | 182 ----------------------------------------------- servicecmd/enable.c | 98 ------------------------- servicecmd/help.c | 129 --------------------------------- servicecmd/list.c | 102 -------------------------- servicecmd/servicecmd.c | 90 ----------------------- servicecmd/servicecmd.h | 93 ------------------------ 18 files changed, 800 insertions(+), 801 deletions(-) create mode 100644 cmd/service/disable.c create mode 100644 cmd/service/dumpscript.c create mode 100644 cmd/service/enable.c create mode 100644 cmd/service/help.c create mode 100644 cmd/service/list.c create mode 100644 cmd/service/servicecmd.c create mode 100644 cmd/service/servicecmd.h delete mode 100644 servicecmd/Makemodule.am delete mode 100644 servicecmd/disable.c delete mode 100644 servicecmd/dumpscript.c delete mode 100644 servicecmd/enable.c delete mode 100644 servicecmd/help.c delete mode 100644 servicecmd/list.c delete mode 100644 servicecmd/servicecmd.c delete mode 100644 servicecmd/servicecmd.h diff --git a/Makefile.am b/Makefile.am index 2323042..5f3823a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,13 +9,13 @@ EXTRA_DIST = README LICENSE helperdir = @SCRIPTDIR@ helper_PROGRAMS = +helper_SCRIPTS = include lib/Makemodule.am include cmd/Makemodule.am include initd/Makemodule.am include scripts/Makemodule.am include services/Makemodule.am -include servicecmd/Makemodule.am install-data-local: $(MKDIR_P) $(DESTDIR)$(SVCDIR) diff --git a/cmd/Makemodule.am b/cmd/Makemodule.am index 40c0933..f0ae3f9 100644 --- a/cmd/Makemodule.am +++ b/cmd/Makemodule.am @@ -15,5 +15,18 @@ killall5_CPPFLAGS = $(AM_CPPFLAGS) killall5_CFLAGS = $(AM_CFLAGS) killall5_LDFLAGS = $(AM_LDFLAGS) -sbin_PROGRAMS += reboot shutdown +SRVHEADERS = cmd/service/servicecmd.h + +service_SOURCES = cmd/service/servicecmd.c cmd/service/help.c +service_SOURCES += cmd/service/enable.c cmd/service/disable.c +service_SOURCES += cmd/service/dumpscript.c cmd/service/list.c +service_SOURCES += $(SRVHEADERS) +service_CPPFLAGS = $(AM_CPPFLAGS) +service_CFLAGS = $(AM_CFLAGS) +service_LDFLAGS = $(AM_LDFLAGS) +service_LDADD = libinit.a + +EXTRA_DIST += $(SRVHEADERS) + +sbin_PROGRAMS += service reboot shutdown helper_PROGRAMS += killall5 diff --git a/cmd/service/disable.c b/cmd/service/disable.c new file mode 100644 index 0000000..8a6f9f3 --- /dev/null +++ b/cmd/service/disable.c @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * Copyright (C) 2018 - David Oberhollenzer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include +#include + +#include "servicecmd.h" + +static int cmd_disable(int argc, char **argv) +{ + int ret = EXIT_FAILURE; + char *linkname, *ptr; + struct stat sb; + + if (check_arguments(argv[0], argc, 2, 3)) + return EXIT_FAILURE; + + for (ptr = argv[1]; isalnum(*ptr) || *ptr == '_'; ++ptr) + ; + + if (*ptr != '\0') { + fprintf(stderr, "Invalid service name '%s'\n", argv[1]); + tell_read_help(argv[0]); + return EXIT_FAILURE; + } + + if (argc == 3) { + ret = asprintf(&linkname, "%s/%s@%s", + SVCDIR, argv[1], argv[2]); + } else { + ret = asprintf(&linkname, "%s/%s", SVCDIR, argv[1]); + } + + if (ret < 0) { + perror("asprintf"); + return EXIT_FAILURE; + } + + if (lstat(linkname, &sb)) { + fprintf(stderr, "lstat %s: %s\n", linkname, strerror(errno)); + goto out; + } + + if ((sb.st_mode & S_IFMT) != S_IFLNK) { + fprintf(stderr, "error: '%s' is not a symlink!", linkname); + goto out; + } + + if (unlink(linkname)) { + fprintf(stderr, "removing %s: %s\n", + linkname, strerror(errno)); + goto out; + } + + ret = EXIT_SUCCESS; +out: + free(linkname); + return ret; +} + +static command_t disable = { + .cmd = "disable", + .usage = " [argument]", + .s_desc = "disable a service", + .l_desc = "This disables a service by removing the coresponding " + "symlink in " SVCDIR ".", + .run_cmd = cmd_disable, +}; + +REGISTER_COMMAND(disable) diff --git a/cmd/service/dumpscript.c b/cmd/service/dumpscript.c new file mode 100644 index 0000000..880631f --- /dev/null +++ b/cmd/service/dumpscript.c @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * Copyright (C) 2018 - David Oberhollenzer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include + +#include "servicecmd.h" +#include "service.h" + +static service_t *try_load(const char *directory, const char *filename) +{ + int dirfd, type; + struct stat sb; + service_t *svc; + + dirfd = open(directory, O_RDONLY | O_DIRECTORY); + + if (dirfd < 0) { + perror(directory); + return NULL; + } + + if (fstatat(dirfd, filename, &sb, AT_SYMLINK_NOFOLLOW)) { + fprintf(stderr, "stat %s/%s: %s\n", + directory, filename, strerror(errno)); + close(dirfd); + return NULL; + } + + type = (sb.st_mode & S_IFMT); + + if (type != S_IFREG && type != S_IFLNK) + return NULL; + + svc = rdsvc(dirfd, filename); + close(dirfd); + return svc; +} + +enum { + NEED_QUOTES = 0x01, + NEED_ESCAPE = 0x02, +}; + +static int check_str(const char *str) +{ + int ret = 0; + + while (*str != '\0') { + if (isspace(*str)) + ret |= NEED_QUOTES; + + if (!isascii(*str) || !isprint(*str)) + ret |= NEED_ESCAPE | NEED_QUOTES; + + if (*str == '\\' || *str == '"' || *str == '\n') + ret |= NEED_ESCAPE | NEED_QUOTES; + + ++str; + } + + return ret; +} + +static void print_str(const char *str) +{ + int flags = check_str(str); + + if (flags & NEED_QUOTES) + fputc('"', stdout); + + if (flags & NEED_ESCAPE) { + while (*str != '\0') { + switch (*str) { + case '\a': fputs("\\a", stdout); break; + case '\b': fputs("\\b", stdout); break; + case '\f': fputs("\\f", stdout); break; + case '\r': fputs("\\r", stdout); break; + case '\t': fputs("\\t", stdout); break; + case '\n': fputs("\\n", stdout); break; + case '\\': + case '"': + fprintf(stdout, "\\%c", *str); + break; + default: + if (!isascii(*str) || !isprint(*str)) { + fprintf(stdout, "\\x%02X", *str); + } else { + fputc(*str, stdout); + } + break; + } + ++str; + } + } else { + fputs(str, stdout); + } + + if (flags & NEED_QUOTES) + fputc('"', stdout); +} + +static int cmd_dumpscript(int argc, char **argv) +{ + char *filename, *ptr; + service_t *svc; + size_t len; + exec_t *e; + int i; + + if (check_arguments(argv[0], argc, 2, 3)) + return EXIT_FAILURE; + + for (len = 1, i = 1; i < argc; ++i) + len += strlen(argv[i]) + 1; + + filename = alloca(len); + filename[0] = '\0'; + + for (i = 1; i < argc; ++i) { + if (i > 1) + strcat(filename, "@"); + strcat(filename, argv[i]); + } + + svc = try_load(SVCDIR, filename); + + if (svc == NULL) { + fprintf(stderr, "Could not load service '%s'\n", filename); + return EXIT_FAILURE; + } + + fprintf(stdout, "#\n# commands executed for serice '%s'\n#\n", + filename); + + for (e = svc->exec; e != NULL; e = e->next) { + ptr = e->args; + + for (i = 0; i < e->argc; ++i) { + if (i) + fputc(' ', stdout); + + print_str(ptr); + ptr += strlen(ptr) + 1; + } + + putchar('\n'); + } + + delsvc(svc); + return EXIT_SUCCESS; +} + +static command_t dumpscript = { + .cmd = "dumpscript", + .usage = " [arguments]", + .s_desc = "print commands executed for a service", + .l_desc = "This parses a service file from " SVCDIR " and " + "produces a pseudo-shell-script containing the " + "exact commands after argument expansion that init " + "runs when starting the service.", + .run_cmd = cmd_dumpscript, +}; + +REGISTER_COMMAND(dumpscript) diff --git a/cmd/service/enable.c b/cmd/service/enable.c new file mode 100644 index 0000000..5d4195e --- /dev/null +++ b/cmd/service/enable.c @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * Copyright (C) 2018 - David Oberhollenzer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include +#include + +#include "servicecmd.h" + +static int cmd_enable(int argc, char **argv) +{ + char *target, *linkname, *ptr; + int ret = EXIT_FAILURE; + struct stat sb; + + if (check_arguments(argv[0], argc, 2, 3)) + return EXIT_FAILURE; + + for (ptr = argv[1]; isalnum(*ptr) || *ptr == '_'; ++ptr) + ; + + if (*ptr != '\0') { + fprintf(stderr, "Invalid service name '%s'\n", argv[1]); + tell_read_help(argv[0]); + return EXIT_FAILURE; + } + + if (asprintf(&target, "%s/%s", TEMPLATEDIR, argv[1]) < 0) { + perror("asprintf"); + return EXIT_FAILURE; + } + + if (stat(target, &sb)) { + fprintf(stderr, "%s: %s\n", target, strerror(errno)); + goto out_tgt; + } + + if ((sb.st_mode & S_IFMT) != S_IFREG) { + fprintf(stderr, "%s: must be a regular file\n", target); + goto out_tgt; + } + + if (argc == 3) { + ret = asprintf(&linkname, "%s/%s@%s", + SVCDIR, argv[1], argv[2]); + } else { + ret = asprintf(&linkname, "%s/%s", SVCDIR, argv[1]); + } + + if (ret < 0) { + perror("asprintf"); + goto out_tgt; + } + + if (symlink(target, linkname)) { + fprintf(stderr, "creating symlink '%s' -> '%s: %s\n", + linkname, target, strerror(errno)); + goto out; + } + + ret = EXIT_SUCCESS; +out: + free(linkname); +out_tgt: + free(target); + return ret; +} + +static command_t enable = { + .cmd = "enable", + .usage = " [argument]", + .s_desc = "enable a service", + .l_desc = "This marks a service as enabled by creating a symlink in " + SVCDIR " pointing to the template file in " TEMPLATEDIR ". " + "An optional argument can be supplied to parameterize the " + "template.", + .run_cmd = cmd_enable, +}; + +REGISTER_COMMAND(enable) diff --git a/cmd/service/help.c b/cmd/service/help.c new file mode 100644 index 0000000..4fa9951 --- /dev/null +++ b/cmd/service/help.c @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * Copyright (C) 2018 - David Oberhollenzer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "servicecmd.h" + +extern char *__progname; + +static void pretty_print(const char *str, int padd, int len) +{ + int i, brk; +next: + while (isspace(*str) && *str != '\n') + ++str; + + if (!(*str)) + return; + + if (*str == '\n') { + fputc('\n', stdout); + ++str; + len = padd; + goto next; + } + + for (i = 0, brk = 0; str[i]; ++i) { + if (str[i] == '<' || str[i] == '[') + ++brk; + if (str[i] == '>' || str[i] == ']') + --brk; + if (!brk && isspace(str[i])) + break; + } + + if ((len + i) < 80) { + fwrite(str, 1, i, stdout); + str += i; + len += i; + + if ((len + 1) < 80) { + fputc(' ', stdout); + ++len; + goto next; + } + } + + printf("\n%*s", padd, ""); + len = padd; + goto next; +} + +static void print_cmd_usage(const char *cmd, const char *usage) +{ + int padd; + + padd = printf("Usage: %s %s ", __progname, cmd); + + if ((strlen(usage) + padd) < 80) { + fputs(usage, stdout); + return; + } + + pretty_print(usage, padd, padd); +} + +static int cmd_help(int argc, char **argv) +{ + const char *help; + command_t *cmd; + + if (argc < 2) + usage(EXIT_SUCCESS); + + if (argc > 2) { + fprintf(stderr, "Too many arguments\n\n" + "Usage: %s help ", __progname); + return EXIT_FAILURE; + } + + for (cmd = commands; cmd != NULL; cmd = cmd->next) { + if (strcmp(cmd->cmd, argv[1]) != 0) + continue; + + print_cmd_usage(cmd->cmd, cmd->usage); + fputs("\n\n", stdout); + + help = cmd->l_desc ? cmd->l_desc : cmd->s_desc; + + if (islower(*help)) { + fputc(toupper(*(help++)), stdout); + pretty_print(help, 0, 1); + } else { + pretty_print(help, 0, 0); + } + + fputc('\n', stdout); + return EXIT_SUCCESS; + } + + fprintf(stderr, "Unknown command '%s'\n\n" + "Try `%s help' for a list of available commands\n", + argv[1], __progname); + return EXIT_FAILURE; +} + +static command_t help = { + .cmd = "help", + .usage = "", + .s_desc = "print a help text for a command", + .l_desc = "Print a help text for a specified command. If no command " + "is specified, print a generic help text and a list of " + "available commands.", + .run_cmd = cmd_help, +}; + +REGISTER_COMMAND(help) diff --git a/cmd/service/list.c b/cmd/service/list.c new file mode 100644 index 0000000..b9e342a --- /dev/null +++ b/cmd/service/list.c @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * Copyright (C) 2018 - David Oberhollenzer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "servicecmd.h" +#include "service.h" +#include "config.h" + +static void print_services(service_t *svc) +{ + const char *ptr; + int i; + + for (; svc != NULL; svc = svc->next) { + printf("Name: %s\n", svc->name); + printf("\tDescrption: %s\n", svc->desc); + printf("\tType: %s\n", svc_type_to_string(svc->type)); + printf("\tTarget: %s\n", svc_target_to_string(svc->target)); + + if (svc->type == SVC_RESPAWN && svc->rspwn_limit > 0) + printf("\tRespawn limit: %d\n", svc->rspwn_limit); + + ptr = svc->before; + if (ptr != NULL && svc->num_before > 0) { + fputs("\tMust be run before:\n", stdout); + + for (i = 0; i < svc->num_before; ++i) { + printf("\t\t%s\n", ptr); + ptr += strlen(ptr) + 1; + } + } + + ptr = svc->after; + if (ptr != NULL && svc->num_after > 0) { + fputs("\tMust be run after:\n", stdout); + + for (i = 0; i < svc->num_after; ++i) { + printf("\t\t%s\n", ptr); + ptr += strlen(ptr) + 1; + } + } + } +} + +static int cmd_list(int argc, char **argv) +{ + int i, ret = EXIT_SUCCESS; + service_list_t list; + + if (check_arguments(argv[0], argc, 1, 2)) + return EXIT_FAILURE; + + if (svcscan(SVCDIR, &list)) { + fprintf(stderr, "Error while reading services from %s\n", + SVCDIR); + ret = EXIT_FAILURE; + } + + if (argc == 2) { + i = svc_target_from_string(argv[1]); + + if (i == -1) { + fprintf(stderr, "Unknown target `%s'\n", argv[1]); + tell_read_help(argv[0]); + ret = EXIT_FAILURE; + goto out; + } + + print_services(list.targets[i]); + } else { + for (i = 0; i < TGT_MAX; ++i) + print_services(list.targets[i]); + } +out: + del_svc_list(&list); + return ret; +} + +static command_t list = { + .cmd = "list", + .usage = "[target]", + .s_desc = "print a list of currently enabled services", + .l_desc = "Print a list of currently enabled services. If an " + "optional target is specified, print services for this " + "target.", + .run_cmd = cmd_list, +}; + +REGISTER_COMMAND(list) diff --git a/cmd/service/servicecmd.c b/cmd/service/servicecmd.c new file mode 100644 index 0000000..84e6a32 --- /dev/null +++ b/cmd/service/servicecmd.c @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * Copyright (C) 2018 - David Oberhollenzer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include + +#include "servicecmd.h" +#include "service.h" +#include "config.h" +#include "util.h" + + +command_t *commands; + +extern char *__progname; + + +void usage(int status) +{ + FILE *stream = (status == EXIT_SUCCESS ? stdout : stderr); + int padd = 0, len; + command_t *cmd; + + fprintf(stream, "usage: %s [args...]\n\n" + "Available commands:\n\n", __progname); + + for (cmd = commands; cmd != NULL; cmd = cmd->next) { + len = strlen(cmd->cmd); + + padd = len > padd ? len : padd; + } + + for (cmd = commands; cmd != NULL; cmd = cmd->next) { + fprintf(stream, "%*s - %s\n", + (int)padd + 1, cmd->cmd, cmd->s_desc); + } + + fprintf(stream, "\nTry `%s help ' for more information " + "on a specific command\n", __progname); + + exit(status); +} + +void tell_read_help(const char *cmd) +{ + fprintf(stderr, "Try `%s help %s' for more information.\n", + __progname, cmd); +} + +int check_arguments(const char *cmd, int argc, int minc, int maxc) +{ + if (argc >= minc && argc <= maxc) + return 0; + + fprintf(stderr, "Too %s arguments for `%s'\n", + argc > maxc ? "many" : "few", cmd); + tell_read_help(cmd); + return -1; +} + +int main(int argc, char **argv) +{ + command_t *cmd; + + if (argc < 2) + usage(EXIT_SUCCESS); + + for (cmd = commands; cmd != NULL; cmd = cmd->next) { + if (!strcmp(cmd->cmd, argv[1])) { + return cmd->run_cmd(argc - 1, argv + 1); + } + } + + fprintf(stderr, "Unknown command '%s'\n\n", argv[1]); + usage(EXIT_FAILURE); +} diff --git a/cmd/service/servicecmd.h b/cmd/service/servicecmd.h new file mode 100644 index 0000000..b839799 --- /dev/null +++ b/cmd/service/servicecmd.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * Copyright (C) 2018 - David Oberhollenzer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef SERVICECMD_H +#define SERVICECMD_H + +#include +#include +#include +#include + +#include "util.h" + +/* + Describes a command that can be launched by passing its name as + second command line argument to the main() function (i.e. immediately + after the actual program name). + + Short and long descriptions can be provided to print out help text. + + The main() function calls into a callback in this structure to execute + the command. +*/ +typedef struct command_t { + struct command_t *next; + + const char *cmd; /* command name */ + const char *usage; /* list of possible arguments */ + const char *s_desc; /* short description used by help */ + const char *l_desc; /* long description used by help */ + + /* + Semantics are the same as for main(). Called from main() + function with first argument (i.e. top level program name) + removed. + */ + int (*run_cmd)(int argc, char **argv); +} command_t; + +/* Global list of available commands */ +extern command_t *commands; + +/* + Implemented in servicecmd.c. Prints program usage message and + terminates with the given exit status. +*/ +void usage(int status) NORETURN; + +/* + Write a message to stderr that advises the user how to consult the + help text for a specific command. +*/ +void tell_read_help(const char *cmd); + +/* + Check if the argument count is within specified bounds (minc and maxc + inclusive). If it is, return 0. + + If it isn't, complain about a wrong number of arguments for a + command (cmd), tell the user to consult the help text and return -1. +*/ +int check_arguments(const char *cmd, int argc, int minc, int maxc); + +/* + To implement a new command, add a global, static instance of a + command_t (or derived) structure to a C file and pass it to this + macro to have it automatically registered on program startup. +*/ +#define REGISTER_COMMAND(cmd) \ + static void __attribute__((constructor)) register_##cmd(void) \ + { \ + command_t *c = (command_t *)&cmd; \ + \ + c->next = commands; \ + commands = c; \ + } + +#endif /* SERVICECMD_H */ + diff --git a/scripts/Makemodule.am b/scripts/Makemodule.am index 7ed5b10..6832788 100644 --- a/scripts/Makemodule.am +++ b/scripts/Makemodule.am @@ -1,6 +1,5 @@ -scriptdir = @SCRIPTDIR@ -script_SCRIPTS = scripts/devfs.sh scripts/trymount.sh +helper_SCRIPTS += scripts/devfs.sh scripts/trymount.sh if PYGOSCFG -script_SCRIPTS += scripts/overlay.sh +helper_SCRIPTS += scripts/overlay.sh endif diff --git a/servicecmd/Makemodule.am b/servicecmd/Makemodule.am deleted file mode 100644 index 7b85e3f..0000000 --- a/servicecmd/Makemodule.am +++ /dev/null @@ -1,13 +0,0 @@ -SRVHEADERS = servicecmd/servicecmd.h - -service_SOURCES = servicecmd/servicecmd.c servicecmd/help.c servicecmd/list.c -service_SOURCES += servicecmd/enable.c servicecmd/disable.c -service_SOURCES += servicecmd/dumpscript.c $(SRVHEADERS) -service_CPPFLAGS = $(AM_CPPFLAGS) -service_CFLAGS = $(AM_CFLAGS) -service_LDFLAGS = $(AM_LDFLAGS) -service_LDADD = libinit.a - -EXTRA_DIST += $(SRVHEADERS) - -sbin_PROGRAMS += service diff --git a/servicecmd/disable.c b/servicecmd/disable.c deleted file mode 100644 index 8a6f9f3..0000000 --- a/servicecmd/disable.c +++ /dev/null @@ -1,89 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * Copyright (C) 2018 - David Oberhollenzer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include -#include -#include -#include -#include -#include - -#include "servicecmd.h" - -static int cmd_disable(int argc, char **argv) -{ - int ret = EXIT_FAILURE; - char *linkname, *ptr; - struct stat sb; - - if (check_arguments(argv[0], argc, 2, 3)) - return EXIT_FAILURE; - - for (ptr = argv[1]; isalnum(*ptr) || *ptr == '_'; ++ptr) - ; - - if (*ptr != '\0') { - fprintf(stderr, "Invalid service name '%s'\n", argv[1]); - tell_read_help(argv[0]); - return EXIT_FAILURE; - } - - if (argc == 3) { - ret = asprintf(&linkname, "%s/%s@%s", - SVCDIR, argv[1], argv[2]); - } else { - ret = asprintf(&linkname, "%s/%s", SVCDIR, argv[1]); - } - - if (ret < 0) { - perror("asprintf"); - return EXIT_FAILURE; - } - - if (lstat(linkname, &sb)) { - fprintf(stderr, "lstat %s: %s\n", linkname, strerror(errno)); - goto out; - } - - if ((sb.st_mode & S_IFMT) != S_IFLNK) { - fprintf(stderr, "error: '%s' is not a symlink!", linkname); - goto out; - } - - if (unlink(linkname)) { - fprintf(stderr, "removing %s: %s\n", - linkname, strerror(errno)); - goto out; - } - - ret = EXIT_SUCCESS; -out: - free(linkname); - return ret; -} - -static command_t disable = { - .cmd = "disable", - .usage = " [argument]", - .s_desc = "disable a service", - .l_desc = "This disables a service by removing the coresponding " - "symlink in " SVCDIR ".", - .run_cmd = cmd_disable, -}; - -REGISTER_COMMAND(disable) diff --git a/servicecmd/dumpscript.c b/servicecmd/dumpscript.c deleted file mode 100644 index 880631f..0000000 --- a/servicecmd/dumpscript.c +++ /dev/null @@ -1,182 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * Copyright (C) 2018 - David Oberhollenzer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include -#include -#include -#include - -#include "servicecmd.h" -#include "service.h" - -static service_t *try_load(const char *directory, const char *filename) -{ - int dirfd, type; - struct stat sb; - service_t *svc; - - dirfd = open(directory, O_RDONLY | O_DIRECTORY); - - if (dirfd < 0) { - perror(directory); - return NULL; - } - - if (fstatat(dirfd, filename, &sb, AT_SYMLINK_NOFOLLOW)) { - fprintf(stderr, "stat %s/%s: %s\n", - directory, filename, strerror(errno)); - close(dirfd); - return NULL; - } - - type = (sb.st_mode & S_IFMT); - - if (type != S_IFREG && type != S_IFLNK) - return NULL; - - svc = rdsvc(dirfd, filename); - close(dirfd); - return svc; -} - -enum { - NEED_QUOTES = 0x01, - NEED_ESCAPE = 0x02, -}; - -static int check_str(const char *str) -{ - int ret = 0; - - while (*str != '\0') { - if (isspace(*str)) - ret |= NEED_QUOTES; - - if (!isascii(*str) || !isprint(*str)) - ret |= NEED_ESCAPE | NEED_QUOTES; - - if (*str == '\\' || *str == '"' || *str == '\n') - ret |= NEED_ESCAPE | NEED_QUOTES; - - ++str; - } - - return ret; -} - -static void print_str(const char *str) -{ - int flags = check_str(str); - - if (flags & NEED_QUOTES) - fputc('"', stdout); - - if (flags & NEED_ESCAPE) { - while (*str != '\0') { - switch (*str) { - case '\a': fputs("\\a", stdout); break; - case '\b': fputs("\\b", stdout); break; - case '\f': fputs("\\f", stdout); break; - case '\r': fputs("\\r", stdout); break; - case '\t': fputs("\\t", stdout); break; - case '\n': fputs("\\n", stdout); break; - case '\\': - case '"': - fprintf(stdout, "\\%c", *str); - break; - default: - if (!isascii(*str) || !isprint(*str)) { - fprintf(stdout, "\\x%02X", *str); - } else { - fputc(*str, stdout); - } - break; - } - ++str; - } - } else { - fputs(str, stdout); - } - - if (flags & NEED_QUOTES) - fputc('"', stdout); -} - -static int cmd_dumpscript(int argc, char **argv) -{ - char *filename, *ptr; - service_t *svc; - size_t len; - exec_t *e; - int i; - - if (check_arguments(argv[0], argc, 2, 3)) - return EXIT_FAILURE; - - for (len = 1, i = 1; i < argc; ++i) - len += strlen(argv[i]) + 1; - - filename = alloca(len); - filename[0] = '\0'; - - for (i = 1; i < argc; ++i) { - if (i > 1) - strcat(filename, "@"); - strcat(filename, argv[i]); - } - - svc = try_load(SVCDIR, filename); - - if (svc == NULL) { - fprintf(stderr, "Could not load service '%s'\n", filename); - return EXIT_FAILURE; - } - - fprintf(stdout, "#\n# commands executed for serice '%s'\n#\n", - filename); - - for (e = svc->exec; e != NULL; e = e->next) { - ptr = e->args; - - for (i = 0; i < e->argc; ++i) { - if (i) - fputc(' ', stdout); - - print_str(ptr); - ptr += strlen(ptr) + 1; - } - - putchar('\n'); - } - - delsvc(svc); - return EXIT_SUCCESS; -} - -static command_t dumpscript = { - .cmd = "dumpscript", - .usage = " [arguments]", - .s_desc = "print commands executed for a service", - .l_desc = "This parses a service file from " SVCDIR " and " - "produces a pseudo-shell-script containing the " - "exact commands after argument expansion that init " - "runs when starting the service.", - .run_cmd = cmd_dumpscript, -}; - -REGISTER_COMMAND(dumpscript) diff --git a/servicecmd/enable.c b/servicecmd/enable.c deleted file mode 100644 index 5d4195e..0000000 --- a/servicecmd/enable.c +++ /dev/null @@ -1,98 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * Copyright (C) 2018 - David Oberhollenzer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include -#include -#include -#include -#include -#include - -#include "servicecmd.h" - -static int cmd_enable(int argc, char **argv) -{ - char *target, *linkname, *ptr; - int ret = EXIT_FAILURE; - struct stat sb; - - if (check_arguments(argv[0], argc, 2, 3)) - return EXIT_FAILURE; - - for (ptr = argv[1]; isalnum(*ptr) || *ptr == '_'; ++ptr) - ; - - if (*ptr != '\0') { - fprintf(stderr, "Invalid service name '%s'\n", argv[1]); - tell_read_help(argv[0]); - return EXIT_FAILURE; - } - - if (asprintf(&target, "%s/%s", TEMPLATEDIR, argv[1]) < 0) { - perror("asprintf"); - return EXIT_FAILURE; - } - - if (stat(target, &sb)) { - fprintf(stderr, "%s: %s\n", target, strerror(errno)); - goto out_tgt; - } - - if ((sb.st_mode & S_IFMT) != S_IFREG) { - fprintf(stderr, "%s: must be a regular file\n", target); - goto out_tgt; - } - - if (argc == 3) { - ret = asprintf(&linkname, "%s/%s@%s", - SVCDIR, argv[1], argv[2]); - } else { - ret = asprintf(&linkname, "%s/%s", SVCDIR, argv[1]); - } - - if (ret < 0) { - perror("asprintf"); - goto out_tgt; - } - - if (symlink(target, linkname)) { - fprintf(stderr, "creating symlink '%s' -> '%s: %s\n", - linkname, target, strerror(errno)); - goto out; - } - - ret = EXIT_SUCCESS; -out: - free(linkname); -out_tgt: - free(target); - return ret; -} - -static command_t enable = { - .cmd = "enable", - .usage = " [argument]", - .s_desc = "enable a service", - .l_desc = "This marks a service as enabled by creating a symlink in " - SVCDIR " pointing to the template file in " TEMPLATEDIR ". " - "An optional argument can be supplied to parameterize the " - "template.", - .run_cmd = cmd_enable, -}; - -REGISTER_COMMAND(enable) diff --git a/servicecmd/help.c b/servicecmd/help.c deleted file mode 100644 index 4fa9951..0000000 --- a/servicecmd/help.c +++ /dev/null @@ -1,129 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * Copyright (C) 2018 - David Oberhollenzer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include "servicecmd.h" - -extern char *__progname; - -static void pretty_print(const char *str, int padd, int len) -{ - int i, brk; -next: - while (isspace(*str) && *str != '\n') - ++str; - - if (!(*str)) - return; - - if (*str == '\n') { - fputc('\n', stdout); - ++str; - len = padd; - goto next; - } - - for (i = 0, brk = 0; str[i]; ++i) { - if (str[i] == '<' || str[i] == '[') - ++brk; - if (str[i] == '>' || str[i] == ']') - --brk; - if (!brk && isspace(str[i])) - break; - } - - if ((len + i) < 80) { - fwrite(str, 1, i, stdout); - str += i; - len += i; - - if ((len + 1) < 80) { - fputc(' ', stdout); - ++len; - goto next; - } - } - - printf("\n%*s", padd, ""); - len = padd; - goto next; -} - -static void print_cmd_usage(const char *cmd, const char *usage) -{ - int padd; - - padd = printf("Usage: %s %s ", __progname, cmd); - - if ((strlen(usage) + padd) < 80) { - fputs(usage, stdout); - return; - } - - pretty_print(usage, padd, padd); -} - -static int cmd_help(int argc, char **argv) -{ - const char *help; - command_t *cmd; - - if (argc < 2) - usage(EXIT_SUCCESS); - - if (argc > 2) { - fprintf(stderr, "Too many arguments\n\n" - "Usage: %s help ", __progname); - return EXIT_FAILURE; - } - - for (cmd = commands; cmd != NULL; cmd = cmd->next) { - if (strcmp(cmd->cmd, argv[1]) != 0) - continue; - - print_cmd_usage(cmd->cmd, cmd->usage); - fputs("\n\n", stdout); - - help = cmd->l_desc ? cmd->l_desc : cmd->s_desc; - - if (islower(*help)) { - fputc(toupper(*(help++)), stdout); - pretty_print(help, 0, 1); - } else { - pretty_print(help, 0, 0); - } - - fputc('\n', stdout); - return EXIT_SUCCESS; - } - - fprintf(stderr, "Unknown command '%s'\n\n" - "Try `%s help' for a list of available commands\n", - argv[1], __progname); - return EXIT_FAILURE; -} - -static command_t help = { - .cmd = "help", - .usage = "", - .s_desc = "print a help text for a command", - .l_desc = "Print a help text for a specified command. If no command " - "is specified, print a generic help text and a list of " - "available commands.", - .run_cmd = cmd_help, -}; - -REGISTER_COMMAND(help) diff --git a/servicecmd/list.c b/servicecmd/list.c deleted file mode 100644 index b9e342a..0000000 --- a/servicecmd/list.c +++ /dev/null @@ -1,102 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * Copyright (C) 2018 - David Oberhollenzer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include "servicecmd.h" -#include "service.h" -#include "config.h" - -static void print_services(service_t *svc) -{ - const char *ptr; - int i; - - for (; svc != NULL; svc = svc->next) { - printf("Name: %s\n", svc->name); - printf("\tDescrption: %s\n", svc->desc); - printf("\tType: %s\n", svc_type_to_string(svc->type)); - printf("\tTarget: %s\n", svc_target_to_string(svc->target)); - - if (svc->type == SVC_RESPAWN && svc->rspwn_limit > 0) - printf("\tRespawn limit: %d\n", svc->rspwn_limit); - - ptr = svc->before; - if (ptr != NULL && svc->num_before > 0) { - fputs("\tMust be run before:\n", stdout); - - for (i = 0; i < svc->num_before; ++i) { - printf("\t\t%s\n", ptr); - ptr += strlen(ptr) + 1; - } - } - - ptr = svc->after; - if (ptr != NULL && svc->num_after > 0) { - fputs("\tMust be run after:\n", stdout); - - for (i = 0; i < svc->num_after; ++i) { - printf("\t\t%s\n", ptr); - ptr += strlen(ptr) + 1; - } - } - } -} - -static int cmd_list(int argc, char **argv) -{ - int i, ret = EXIT_SUCCESS; - service_list_t list; - - if (check_arguments(argv[0], argc, 1, 2)) - return EXIT_FAILURE; - - if (svcscan(SVCDIR, &list)) { - fprintf(stderr, "Error while reading services from %s\n", - SVCDIR); - ret = EXIT_FAILURE; - } - - if (argc == 2) { - i = svc_target_from_string(argv[1]); - - if (i == -1) { - fprintf(stderr, "Unknown target `%s'\n", argv[1]); - tell_read_help(argv[0]); - ret = EXIT_FAILURE; - goto out; - } - - print_services(list.targets[i]); - } else { - for (i = 0; i < TGT_MAX; ++i) - print_services(list.targets[i]); - } -out: - del_svc_list(&list); - return ret; -} - -static command_t list = { - .cmd = "list", - .usage = "[target]", - .s_desc = "print a list of currently enabled services", - .l_desc = "Print a list of currently enabled services. If an " - "optional target is specified, print services for this " - "target.", - .run_cmd = cmd_list, -}; - -REGISTER_COMMAND(list) diff --git a/servicecmd/servicecmd.c b/servicecmd/servicecmd.c deleted file mode 100644 index 84e6a32..0000000 --- a/servicecmd/servicecmd.c +++ /dev/null @@ -1,90 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * Copyright (C) 2018 - David Oberhollenzer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include - -#include "servicecmd.h" -#include "service.h" -#include "config.h" -#include "util.h" - - -command_t *commands; - -extern char *__progname; - - -void usage(int status) -{ - FILE *stream = (status == EXIT_SUCCESS ? stdout : stderr); - int padd = 0, len; - command_t *cmd; - - fprintf(stream, "usage: %s [args...]\n\n" - "Available commands:\n\n", __progname); - - for (cmd = commands; cmd != NULL; cmd = cmd->next) { - len = strlen(cmd->cmd); - - padd = len > padd ? len : padd; - } - - for (cmd = commands; cmd != NULL; cmd = cmd->next) { - fprintf(stream, "%*s - %s\n", - (int)padd + 1, cmd->cmd, cmd->s_desc); - } - - fprintf(stream, "\nTry `%s help ' for more information " - "on a specific command\n", __progname); - - exit(status); -} - -void tell_read_help(const char *cmd) -{ - fprintf(stderr, "Try `%s help %s' for more information.\n", - __progname, cmd); -} - -int check_arguments(const char *cmd, int argc, int minc, int maxc) -{ - if (argc >= minc && argc <= maxc) - return 0; - - fprintf(stderr, "Too %s arguments for `%s'\n", - argc > maxc ? "many" : "few", cmd); - tell_read_help(cmd); - return -1; -} - -int main(int argc, char **argv) -{ - command_t *cmd; - - if (argc < 2) - usage(EXIT_SUCCESS); - - for (cmd = commands; cmd != NULL; cmd = cmd->next) { - if (!strcmp(cmd->cmd, argv[1])) { - return cmd->run_cmd(argc - 1, argv + 1); - } - } - - fprintf(stderr, "Unknown command '%s'\n\n", argv[1]); - usage(EXIT_FAILURE); -} diff --git a/servicecmd/servicecmd.h b/servicecmd/servicecmd.h deleted file mode 100644 index b839799..0000000 --- a/servicecmd/servicecmd.h +++ /dev/null @@ -1,93 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * Copyright (C) 2018 - David Oberhollenzer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#ifndef SERVICECMD_H -#define SERVICECMD_H - -#include -#include -#include -#include - -#include "util.h" - -/* - Describes a command that can be launched by passing its name as - second command line argument to the main() function (i.e. immediately - after the actual program name). - - Short and long descriptions can be provided to print out help text. - - The main() function calls into a callback in this structure to execute - the command. -*/ -typedef struct command_t { - struct command_t *next; - - const char *cmd; /* command name */ - const char *usage; /* list of possible arguments */ - const char *s_desc; /* short description used by help */ - const char *l_desc; /* long description used by help */ - - /* - Semantics are the same as for main(). Called from main() - function with first argument (i.e. top level program name) - removed. - */ - int (*run_cmd)(int argc, char **argv); -} command_t; - -/* Global list of available commands */ -extern command_t *commands; - -/* - Implemented in servicecmd.c. Prints program usage message and - terminates with the given exit status. -*/ -void usage(int status) NORETURN; - -/* - Write a message to stderr that advises the user how to consult the - help text for a specific command. -*/ -void tell_read_help(const char *cmd); - -/* - Check if the argument count is within specified bounds (minc and maxc - inclusive). If it is, return 0. - - If it isn't, complain about a wrong number of arguments for a - command (cmd), tell the user to consult the help text and return -1. -*/ -int check_arguments(const char *cmd, int argc, int minc, int maxc); - -/* - To implement a new command, add a global, static instance of a - command_t (or derived) structure to a C file and pass it to this - macro to have it automatically registered on program startup. -*/ -#define REGISTER_COMMAND(cmd) \ - static void __attribute__((constructor)) register_##cmd(void) \ - { \ - command_t *c = (command_t *)&cmd; \ - \ - c->next = commands; \ - commands = c; \ - } - -#endif /* SERVICECMD_H */ - -- cgit v1.2.3