summaryrefslogtreecommitdiff
path: root/lib/src
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@tele2.at>2018-02-25 14:33:19 +0100
committerDavid Oberhollenzer <david.oberhollenzer@tele2.at>2018-03-24 17:04:20 +0100
commit9a88f7da453eadc72d8f333700dbd80777feecd1 (patch)
tree8a096e37123ece1d20bcb4d0ae8e064bdd39747a /lib/src
Initial commit
Signed-off-by: David Oberhollenzer <david.oberhollenzer@tele2.at>
Diffstat (limited to 'lib/src')
-rw-r--r--lib/src/del_srv_list.c18
-rw-r--r--lib/src/delsrv.c25
-rw-r--r--lib/src/enum_by_name.c15
-rw-r--r--lib/src/opensock.c33
-rw-r--r--lib/src/rdline.c54
-rw-r--r--lib/src/rdsrv.c249
-rw-r--r--lib/src/splitkv.c124
-rw-r--r--lib/src/srv_tsort.c75
-rw-r--r--lib/src/srvscan.c93
-rw-r--r--lib/src/strexpand.c55
10 files changed, 741 insertions, 0 deletions
diff --git a/lib/src/del_srv_list.c b/lib/src/del_srv_list.c
new file mode 100644
index 0000000..916fcb2
--- /dev/null
+++ b/lib/src/del_srv_list.c
@@ -0,0 +1,18 @@
+#include <stdlib.h>
+
+#include "service.h"
+
+void del_srv_list(service_list_t *list)
+{
+ service_t *srv;
+ int i;
+
+ for (i = 0; i < TGT_MAX; ++i) {
+ while (list->targets[i] != NULL) {
+ srv = list->targets[i];
+ list->targets[i] = srv->next;
+
+ delsrv(srv);
+ }
+ }
+}
diff --git a/lib/src/delsrv.c b/lib/src/delsrv.c
new file mode 100644
index 0000000..b660558
--- /dev/null
+++ b/lib/src/delsrv.c
@@ -0,0 +1,25 @@
+#include <stdlib.h>
+
+#include "service.h"
+
+void delsrv(service_t *srv)
+{
+ size_t i;
+
+ for (i = 0; i < srv->num_exec; ++i)
+ free(srv->exec[i]);
+
+ for (i = 0; i < srv->num_before; ++i)
+ free(srv->before[i]);
+
+ for (i = 0; i < srv->num_after; ++i)
+ free(srv->after[i]);
+
+ free(srv->before);
+ free(srv->after);
+ free(srv->name);
+ free(srv->desc);
+ free(srv->exec);
+ free(srv->ctty);
+ free(srv);
+}
diff --git a/lib/src/enum_by_name.c b/lib/src/enum_by_name.c
new file mode 100644
index 0000000..1948eb9
--- /dev/null
+++ b/lib/src/enum_by_name.c
@@ -0,0 +1,15 @@
+#include <string.h>
+
+#include "util.h"
+
+const enum_map_t *enum_by_name(const enum_map_t *map, const char *name)
+{
+ size_t i;
+
+ for (i = 0; map[i].name != NULL; ++i) {
+ if (!strcmp(map[i].name, name))
+ return map + i;
+ }
+
+ return NULL;
+}
diff --git a/lib/src/opensock.c b/lib/src/opensock.c
new file mode 100644
index 0000000..06101ef
--- /dev/null
+++ b/lib/src/opensock.c
@@ -0,0 +1,33 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "telinit.h"
+
+int opensock(void)
+{
+ struct sockaddr_un un;
+ int fd;
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ memset(&un, 0, sizeof(un));
+ un.sun_family = AF_UNIX;
+
+ strcpy(un.sun_path, INITSOCK);
+
+ if (connect(fd, (struct sockaddr *)&un, sizeof(un))) {
+ perror("connect: " INITSOCK);
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
diff --git a/lib/src/rdline.c b/lib/src/rdline.c
new file mode 100644
index 0000000..591c713
--- /dev/null
+++ b/lib/src/rdline.c
@@ -0,0 +1,54 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "util.h"
+
+char *rdline(int fd)
+{
+ size_t i = 0, bufsiz = 0, newsz;
+ char c, *new, *buffer = NULL;
+ int ret;
+
+ for (;;) {
+ switch (read(fd, &c, 1)) {
+ case 0:
+ if (i == 0) {
+ errno = 0;
+ return NULL;
+ }
+ c = '\0';
+ break;
+ case 1:
+ if (c == '\n')
+ c = '\0';
+ break;
+ default:
+ if (errno == EINTR)
+ continue;
+ goto fail;
+ }
+
+ if (i == bufsiz) {
+ newsz = bufsiz ? bufsiz * 2 : 16;
+ new = realloc(buffer, newsz);
+
+ if (new == NULL)
+ goto fail;
+
+ buffer = new;
+ bufsiz = newsz;
+ }
+
+ buffer[i++] = c;
+ if (c == '\0')
+ break;
+ }
+ return buffer;
+fail:
+ ret = errno;
+ free(buffer);
+ errno = ret;
+ return NULL;
+}
diff --git a/lib/src/rdsrv.c b/lib/src/rdsrv.c
new file mode 100644
index 0000000..52eb19c
--- /dev/null
+++ b/lib/src/rdsrv.c
@@ -0,0 +1,249 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "service.h"
+#include "util.h"
+
+static const enum_map_t type_map[] = {
+ { "once", SVC_ONCE },
+ { "wait", SVC_WAIT },
+ { "respawn", SVC_RESPAWN },
+ { NULL, 0 },
+};
+
+static const enum_map_t target_map[] = {
+ { "boot", TGT_BOOT },
+ { "shutdown", TGT_SHUTDOWN },
+ { "reboot", TGT_REBOOT },
+ { "ctrlaltdel", TGT_CAD },
+ { NULL, 0 },
+};
+
+static int srv_name(service_t *srv, char *arg,
+ const char *filename, size_t lineno)
+{
+ (void)filename; (void)lineno;
+ srv->name = arg;
+ return 0;
+}
+
+static int srv_desc(service_t *srv, char *arg,
+ const char *filename, size_t lineno)
+{
+ (void)filename; (void)lineno;
+ srv->desc = arg;
+ return 0;
+}
+
+static int srv_tty(service_t *srv, char *arg,
+ const char *filename, size_t lineno)
+{
+ (void)filename; (void)lineno;
+ srv->ctty = arg;
+ return 0;
+}
+
+static int srv_exec(service_t *srv, char *arg,
+ const char *filename, size_t lineno)
+{
+ char **new = realloc(srv->exec, sizeof(char*) * (srv->num_exec + 1));
+
+ if (new == NULL) {
+ fprintf(stderr, "%s: %zu: out of memory\n", filename, lineno);
+ free(arg);
+ return -1;
+ }
+
+ srv->exec = new;
+ srv->exec[srv->num_exec++] = arg;
+ return 0;
+}
+
+static int srv_before(service_t *srv, char *arg,
+ const char *filename, size_t lineno)
+{
+ char **new = realloc(srv->before,
+ sizeof(char*) * (srv->num_before + 1));
+
+ if (new == NULL) {
+ fprintf(stderr, "%s: %zu: out of memory\n", filename, lineno);
+ free(arg);
+ return -1;
+ }
+
+ srv->before = new;
+ srv->before[srv->num_before++] = arg;
+ return 0;
+}
+
+static int srv_after(service_t *srv, char *arg,
+ const char *filename, size_t lineno)
+{
+ char **new = realloc(srv->after, sizeof(char*) * (srv->num_after + 1));
+
+ if (new == NULL) {
+ fprintf(stderr, "%s: %zu: out of memory\n", filename, lineno);
+ free(arg);
+ return -1;
+ }
+
+ srv->after = new;
+ srv->after[srv->num_after++] = arg;
+ return 0;
+}
+
+static int srv_type(service_t *srv, char *arg,
+ const char *filename, size_t lineno)
+{
+ const enum_map_t *ent = enum_by_name(type_map, arg);
+
+ if (ent == NULL) {
+ fprintf(stderr, "%s: %zu: unknown service type '%s'\n",
+ filename, lineno, arg);
+ return -1;
+ }
+
+ srv->type = ent->value;
+ free(arg);
+ return 0;
+}
+
+static int srv_target(service_t *srv, char *arg,
+ const char *filename, size_t lineno)
+{
+ const enum_map_t *ent = enum_by_name(target_map, arg);
+
+ if (ent == NULL) {
+ fprintf(stderr, "%s: %zu: unknown service target '%s'\n",
+ filename, lineno, arg);
+ return -1;
+ }
+
+ srv->target = ent->value;
+ free(arg);
+ return 0;
+}
+
+
+static const struct {
+ const char *key;
+
+ int (*handle)(service_t *srv, char *arg,
+ const char *filename, size_t lineno);
+} srv_params[] = {
+ { "name", srv_name },
+ { "description", srv_desc },
+ { "exec", srv_exec },
+ { "type", srv_type },
+ { "target", srv_target },
+ { "tty", srv_tty },
+ { "before", srv_before },
+ { "after", srv_after },
+};
+
+
+service_t *rdsrv(int dirfd, const char *filename)
+{
+ const char *arg, *args[1];
+ char *line, *key, *value;
+ size_t i, argc, lineno;
+ service_t *srv;
+ int fd;
+
+ fd = openat(dirfd, filename, O_RDONLY);
+ if (fd < 0) {
+ perror(filename);
+ return NULL;
+ }
+
+ arg = strchr(filename, '@');
+ if (arg != NULL) {
+ args[0] = arg + 1;
+ argc = 1;
+ } else {
+ argc = 0;
+ }
+
+ srv = calloc(1, sizeof(*srv));
+ if (srv == NULL) {
+ fputs("out of memory\n", stderr);
+ close(fd);
+ return NULL;
+ }
+
+ for (lineno = 1; ; ++lineno) {
+ errno = 0;
+ line = rdline(fd);
+
+ if (line == NULL) {
+ if (errno != 0) {
+ fprintf(stderr, "read: %s: %zu: %s\n",
+ filename, lineno, strerror(errno));
+ goto fail;
+ }
+ break;
+ }
+
+ if (splitkv(line, &key, &value)) {
+ if (key == NULL) {
+ fprintf(stderr,
+ "%s: %zu: expected <key> = <value>\n",
+ filename, lineno);
+ } else if (value == NULL) {
+ fprintf(stderr,
+ "%s: %zu: expected value after %s\n",
+ filename, lineno, key);
+ } else {
+ fprintf(stderr,
+ "%s: %zu: unexpected arguments "
+ "after key-value pair\n",
+ filename, lineno);
+ }
+ goto fail;
+ }
+
+ if (key == NULL) {
+ free(line);
+ continue;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(srv_params); ++i) {
+ if (!strcmp(srv_params[i].key, key))
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(srv_params)) {
+ fprintf(stderr, "%s: %zu: unknown parameter '%s'\n",
+ filename, lineno, key);
+ goto fail_line;
+ }
+
+ value = strexpand(value, argc, args);
+ if (value == NULL) {
+ fputs("out of memory", stderr);
+ goto fail_line;
+ }
+
+ if (srv_params[i].handle(srv, value, filename, lineno)) {
+ free(value);
+ goto fail_line;
+ }
+
+ free(line);
+ }
+
+ close(fd);
+ return srv;
+fail_line:
+ free(line);
+fail:
+ close(fd);
+ delsrv(srv);
+ return NULL;
+}
diff --git a/lib/src/splitkv.c b/lib/src/splitkv.c
new file mode 100644
index 0000000..6c6fe94
--- /dev/null
+++ b/lib/src/splitkv.c
@@ -0,0 +1,124 @@
+#include <ctype.h>
+
+#include "util.h"
+
+static char *skpspc(char *ptr)
+{
+ while (*ptr == ' ' || *ptr == '\t')
+ ++ptr;
+ return ptr;
+}
+
+static int xdigit(int x)
+{
+ if (isupper(x))
+ return x - 'A' + 0x0A;
+ if (islower(x))
+ return x - 'a' + 0x0A;
+ return x - '0';
+}
+
+static char *parse_str(char *src)
+{
+ char *dst = src;
+ int c;
+
+ for (;;) {
+ c = *(src++);
+
+ switch (c) {
+ case '\\':
+ c = *(src++);
+
+ switch (c) {
+ case 'a': c = '\a'; break;
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 't': c = '\t'; break;
+ case '\\': break;
+ case '"': break;
+ case 'x':
+ c = 0;
+ if (isxdigit(*src))
+ c = (c << 4) | xdigit(*(src++));
+ if (isxdigit(*src))
+ c = (c << 4) | xdigit(*(src++));
+ break;
+ case '0':
+ c = 0;
+ if (isdigit(*src) && *src < '8')
+ c = (c << 3) | (*(src++) - '0');
+ if (isdigit(*src) && *src < '8')
+ c = (c << 3) | (*(src++) - '0');
+ if (isdigit(*src) && *src < '8')
+ c = (c << 3) | (*(src++) - '0');
+ break;
+ default:
+ return NULL;
+ }
+ break;
+ case '"':
+ *(dst++) = '\0';
+ goto out;
+ }
+
+ *(dst++) = c;
+ }
+out:
+ return src;
+}
+
+int splitkv(char *line, char **key, char **value)
+{
+ *key = NULL;
+ *value = NULL;
+
+ line = skpspc(line);
+
+ if (*line == '#' || *line == '\0')
+ return 0;
+
+ if (!isalpha(*line))
+ return -1;
+
+ *key = line;
+
+ while (isalnum(*line))
+ ++line;
+
+ if (*line == ' ' || *line == '\t') {
+ *(line++) = '\0';
+ line = skpspc(line);
+ }
+
+ if (*line != '=')
+ return -1;
+
+ *(line++) = '\0';
+ line = skpspc(line);
+
+ if (*line == '"') {
+ ++line;
+ *value = line;
+
+ line = parse_str(line);
+ } else if (isalnum(*line)) {
+ *value = line;
+
+ while (isalnum(*line) || *line == '.' || *line == '_')
+ ++line;
+
+ if (*line != '\0')
+ *(line++) = '\0';
+ } else {
+ return -1;
+ }
+
+ line = skpspc(line);
+
+ if (*line != '\0' && *line != '#')
+ return -1;
+
+ return 0;
+}
diff --git a/lib/src/srv_tsort.c b/lib/src/srv_tsort.c
new file mode 100644
index 0000000..e2549b1
--- /dev/null
+++ b/lib/src/srv_tsort.c
@@ -0,0 +1,75 @@
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+
+#include "service.h"
+
+static bool has_dependencies(service_t *list, service_t *svc)
+{
+ size_t i;
+
+ while (list != NULL) {
+ for (i = 0; i < svc->num_after; ++i) {
+ if (!strcmp(svc->after[i], list->name))
+ return true;
+ }
+
+ for (i = 0; i < list->num_before; ++i) {
+ if (!strcmp(list->before[i], svc->name))
+ return true;
+ }
+
+ list = list->next;
+ }
+
+ return false;
+}
+
+service_t *srv_tsort(service_t *list)
+{
+ service_t *nl = NULL, *end = NULL;
+ service_t *svc, *prev;
+
+ while (list != NULL) {
+ /* remove first service without dependencies */
+ prev = NULL;
+ svc = list;
+
+ while (svc != NULL) {
+ if (has_dependencies(list, svc)) {
+ prev = svc;
+ svc = svc->next;
+ } else {
+ if (prev != NULL) {
+ prev->next = svc->next;
+ } else {
+ list = svc->next;
+ }
+ svc->next = NULL;
+ break;
+ }
+ }
+
+ /* cycle! */
+ if (svc == NULL) {
+ if (end == NULL) {
+ nl = list;
+ } else {
+ end->next = list;
+ }
+ errno = ELOOP;
+ break;
+ }
+
+ /* append to new list */
+ if (end == NULL) {
+ nl = end = svc;
+ } else {
+ end->next = svc;
+ end = svc;
+ }
+ }
+
+ return nl;
+}
diff --git a/lib/src/srvscan.c b/lib/src/srvscan.c
new file mode 100644
index 0000000..71ad4f9
--- /dev/null
+++ b/lib/src/srvscan.c
@@ -0,0 +1,93 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stddef.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "service.h"
+
+int srvscan(const char *directory, service_list_t *list)
+{
+ int i, dfd, type, ret = 0;
+ struct dirent *ent;
+ const char *ptr;
+ service_t *srv;
+ struct stat sb;
+ DIR *dir;
+
+ for (i = 0; i < TGT_MAX; ++i)
+ list->targets[i] = NULL;
+
+ dir = opendir(directory);
+ if (dir == NULL) {
+ perror(directory);
+ return -1;
+ }
+
+ dfd = dirfd(dir);
+ if (dfd < 0) {
+ perror(directory);
+ closedir(dir);
+ return -1;
+ }
+
+ for (;;) {
+ errno = 0;
+ ent = readdir(dir);
+
+ if (ent == NULL) {
+ if (errno != 0) {
+ perror(directory);
+ ret = -1;
+ }
+ break;
+ }
+
+ for (ptr = ent->d_name; isalnum(*ptr) || *ptr == '_'; ++ptr)
+ ;
+
+ if (*ptr != '\0' && *ptr != '@')
+ continue;
+
+ if (fstatat(dfd, ent->d_name, &sb, AT_SYMLINK_NOFOLLOW)) {
+ fprintf(stderr, "stat %s/%s: %s\n",
+ directory, ent->d_name, strerror(errno));
+ ret = -1;
+ continue;
+ }
+
+ type = (sb.st_mode & S_IFMT);
+
+ if (type != S_IFREG && type != S_IFLNK)
+ continue;
+
+ srv = rdsrv(dfd, ent->d_name);
+ if (srv == NULL) {
+ ret = -1;
+ continue;
+ }
+
+ srv->next = list->targets[srv->target];
+ list->targets[srv->target] = srv;
+ }
+
+ for (i = 0; i < TGT_MAX; ++i) {
+ if (list->targets[i] == NULL)
+ continue;
+
+ errno = 0;
+ list->targets[i] = srv_tsort(list->targets[i]);
+
+ if (errno != 0) {
+ fprintf(stderr, "sorting services read from %s: %s\n",
+ directory, strerror(errno));
+ }
+ }
+
+ closedir(dir);
+ return ret;
+}
diff --git a/lib/src/strexpand.c b/lib/src/strexpand.c
new file mode 100644
index 0000000..7e552eb
--- /dev/null
+++ b/lib/src/strexpand.c
@@ -0,0 +1,55 @@
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "util.h"
+
+char *strexpand(const char *inp, size_t argc, const char *const *argv)
+{
+ char *out, *dst;
+ const char *ptr;
+ size_t i, len;
+
+ ptr = inp;
+ len = 0;
+
+ while (*ptr != '\0') {
+ if (ptr[0] == '%' && isdigit(ptr[1])) {
+ i = ptr[1] - '0';
+ if (i < argc)
+ len += strlen(argv[i]);
+ ptr += 2;
+ } else if (ptr[0] == '%' && ptr[1] == '%') {
+ ptr += 2;
+ len += 1;
+ } else {
+ ++ptr;
+ ++len;
+ }
+ }
+
+ out = calloc(1, len + 1);
+ if (out == NULL)
+ return NULL;
+
+ dst = out;
+
+ while (*inp != '\0') {
+ if (inp[0] == '%' && isdigit(inp[1])) {
+ i = inp[1] - '0';
+ if (i < argc) {
+ len = strlen(argv[i]);
+ memcpy(dst, argv[i], len);
+ dst += len;
+ }
+ inp += 2;
+ } else if (inp[0] == '%' && inp[1] == '%') {
+ *(dst++) = '%';
+ inp += 2;
+ } else {
+ *(dst++) = *(inp++);
+ }
+ }
+
+ return out;
+}