summaryrefslogtreecommitdiff
path: root/servicecmd
diff options
context:
space:
mode:
Diffstat (limited to 'servicecmd')
-rw-r--r--servicecmd/Makemodule.am12
-rw-r--r--servicecmd/disable.c75
-rw-r--r--servicecmd/enable.c84
-rw-r--r--servicecmd/help.c112
-rw-r--r--servicecmd/list.c71
-rw-r--r--servicecmd/servicecmd.c56
-rw-r--r--servicecmd/servicecmd.h36
7 files changed, 446 insertions, 0 deletions
diff --git a/servicecmd/Makemodule.am b/servicecmd/Makemodule.am
new file mode 100644
index 0000000..fd671b6
--- /dev/null
+++ b/servicecmd/Makemodule.am
@@ -0,0 +1,12 @@
+SRVHEADERS = servicecmd/servicecmd.h
+
+service_SOURCES = servicecmd/servicecmd.c servicecmd/help.c servicecmd/list.c
+service_SOURCES += servicecmd/enable.c servicecmd/disable.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
new file mode 100644
index 0000000..90d8b80
--- /dev/null
+++ b/servicecmd/disable.c
@@ -0,0 +1,75 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "servicecmd.h"
+
+static int cmd_disable(int argc, char **argv)
+{
+ int ret = EXIT_FAILURE;
+ char *linkname, *ptr;
+ struct stat sb;
+
+ if (argc < 2 || argc > 3) {
+ fputs("Wrong number of arguments for `disable'.\n"
+ "Try `service help disable' for more information.\n",
+ stderr);
+ return EXIT_FAILURE;
+ }
+
+ for (ptr = argv[1]; isalnum(*ptr) || *ptr == '_'; ++ptr)
+ ;
+
+ if (*ptr != '\0') {
+ fprintf(stderr, "Invalid service name '%s'\n", argv[1]);
+ 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 = "<name> [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/enable.c b/servicecmd/enable.c
new file mode 100644
index 0000000..8e6200c
--- /dev/null
+++ b/servicecmd/enable.c
@@ -0,0 +1,84 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "servicecmd.h"
+
+static int cmd_enable(int argc, char **argv)
+{
+ char *target, *linkname, *ptr;
+ int ret = EXIT_FAILURE;
+ struct stat sb;
+
+ if (argc < 2 || argc > 3) {
+ fputs("Wrong number of arguments for `enable'.\n"
+ "Try `service help enable' for more information.\n",
+ stderr);
+ return EXIT_FAILURE;
+ }
+
+ for (ptr = argv[1]; isalnum(*ptr) || *ptr == '_'; ++ptr)
+ ;
+
+ if (*ptr != '\0') {
+ fprintf(stderr, "Invalid service name '%s'\n", argv[1]);
+ 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 = "<name> [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
new file mode 100644
index 0000000..6cd816e
--- /dev/null
+++ b/servicecmd/help.c
@@ -0,0 +1,112 @@
+#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 <command>", __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 = "<command>",
+ .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
new file mode 100644
index 0000000..2286197
--- /dev/null
+++ b/servicecmd/list.c
@@ -0,0 +1,71 @@
+#include "servicecmd.h"
+#include "service.h"
+#include "config.h"
+
+static int cmd_list(int argc, char **argv)
+{
+ int i, ret = EXIT_SUCCESS;
+ service_list_t list;
+ service_t *svc;
+
+ (void)argc; (void)argv;
+
+ if (srvscan(SVCDIR, &list)) {
+ fprintf(stderr, "Error while reading services from %s\n",
+ SVCDIR);
+ ret = EXIT_FAILURE;
+ }
+
+ for (i = 0; i < TGT_MAX; ++i) {
+ if (list.targets[i] == NULL)
+ continue;
+
+ fputs("******** target: ", stdout);
+
+ switch (i) {
+ case TGT_BOOT:
+ fputs("boot", stdout);
+ break;
+ case TGT_SHUTDOWN:
+ fputs("shutdown", stdout);
+ break;
+ case TGT_REBOOT:
+ fputs("reboot", stdout);
+ break;
+ case TGT_CAD:
+ fputs("ctrl-alt-delete", stdout);
+ break;
+ }
+
+ fputs(" ********\n", stdout);
+
+ for (svc = list.targets[i]; svc != NULL; svc = svc->next) {
+ fprintf(stdout, "Name: %s\n", svc->name);
+ fprintf(stdout, "Descrption: %s\n", svc->desc);
+
+ fputs("Type: ", stdout);
+ switch (svc->type) {
+ case SVC_ONCE: fputs("once\n", stdout); break;
+ case SVC_WAIT: fputs("wait\n", stdout); break;
+ case SVC_RESPAWN: fputs("respawn\n", stdout); break;
+ }
+
+ fputc('\n', stdout);
+ }
+
+ fputc('\n', stdout);
+ }
+
+ del_srv_list(&list);
+ return ret;
+}
+
+static command_t list = {
+ .cmd = "list",
+ .usage = "",
+ .s_desc = "print a list of currently enabled services",
+ .l_desc = "Print a list of currently enabled services.",
+ .run_cmd = cmd_list,
+};
+
+REGISTER_COMMAND(list)
diff --git a/servicecmd/servicecmd.c b/servicecmd/servicecmd.c
new file mode 100644
index 0000000..278afaf
--- /dev/null
+++ b/servicecmd/servicecmd.c
@@ -0,0 +1,56 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#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 <command> [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 <command>' for more information "
+ "on a specific command\n", __progname);
+
+ exit(status);
+}
+
+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
new file mode 100644
index 0000000..ce5e58f
--- /dev/null
+++ b/servicecmd/servicecmd.h
@@ -0,0 +1,36 @@
+#ifndef SERVICECMD_H
+#define SERVICECMD_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "util.h"
+
+typedef struct command_t {
+ struct command_t *next;
+
+ const char *cmd;
+ const char *usage;
+ const char *s_desc;
+ const char *l_desc;
+
+ int (*run_cmd)(int argc, char **argv);
+} command_t;
+
+extern command_t *commands;
+
+void usage(int status) NORETURN;
+
+#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 */
+