From 9a88f7da453eadc72d8f333700dbd80777feecd1 Mon Sep 17 00:00:00 2001
From: David Oberhollenzer <david.oberhollenzer@tele2.at>
Date: Sun, 25 Feb 2018 14:33:19 +0100
Subject: Initial commit

Signed-off-by: David Oberhollenzer <david.oberhollenzer@tele2.at>
---
 servicecmd/Makemodule.am |  12 +++++
 servicecmd/disable.c     |  75 +++++++++++++++++++++++++++++++
 servicecmd/enable.c      |  84 +++++++++++++++++++++++++++++++++++
 servicecmd/help.c        | 112 +++++++++++++++++++++++++++++++++++++++++++++++
 servicecmd/list.c        |  71 ++++++++++++++++++++++++++++++
 servicecmd/servicecmd.c  |  56 ++++++++++++++++++++++++
 servicecmd/servicecmd.h  |  36 +++++++++++++++
 7 files changed, 446 insertions(+)
 create mode 100644 servicecmd/Makemodule.am
 create mode 100644 servicecmd/disable.c
 create mode 100644 servicecmd/enable.c
 create mode 100644 servicecmd/help.c
 create mode 100644 servicecmd/list.c
 create mode 100644 servicecmd/servicecmd.c
 create mode 100644 servicecmd/servicecmd.h

(limited to 'servicecmd')

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 */
+
-- 
cgit v1.2.3