Browse Source

procd: support relayoing daemon stdout/stderr to syslog

This commit adds support to procd for relaying stdout and stderr streams to
the system log. That is mainly useful for services not using syslog, e.g.
uhttpd.

Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
Jo-Philipp Wich 9 years ago
parent
commit
9851e517f1
2 changed files with 134 additions and 10 deletions
  1. 131 10
      service/instance.c
  2. 3 0
      service/instance.h

+ 131 - 10
service/instance.c

@@ -20,6 +20,7 @@
 #include <stdint.h>
 #include <fcntl.h>
 #include <pwd.h>
+#include <libgen.h>
 
 #include <libubox/md5.h>
 
@@ -42,6 +43,8 @@ enum {
 	INSTANCE_ATTR_WATCH,
 	INSTANCE_ATTR_ERROR,
 	INSTANCE_ATTR_USER,
+	INSTANCE_ATTR_STDOUT,
+	INSTANCE_ATTR_STDERR,
 	__INSTANCE_ATTR_MAX
 };
 
@@ -58,6 +61,8 @@ static const struct blobmsg_policy instance_attr[__INSTANCE_ATTR_MAX] = {
 	[INSTANCE_ATTR_WATCH] = { "watch", BLOBMSG_TYPE_ARRAY },
 	[INSTANCE_ATTR_ERROR] = { "error", BLOBMSG_TYPE_ARRAY },
 	[INSTANCE_ATTR_USER] = { "user", BLOBMSG_TYPE_STRING },
+	[INSTANCE_ATTR_STDOUT] = { "stdout", BLOBMSG_TYPE_BOOL },
+	[INSTANCE_ATTR_STDERR] = { "stderr", BLOBMSG_TYPE_BOOL },
 };
 
 struct instance_netdev {
@@ -93,6 +98,12 @@ static const struct rlimit_name rlimit_names[] = {
 	{ NULL, 0 }
 };
 
+static void closefd(int fd)
+{
+	if (fd > STDERR_FILENO)
+		close(fd);
+}
+
 static void
 instance_limits(const char *limit, const char *value)
 {
@@ -126,13 +137,13 @@ instance_limits(const char *limit, const char *value)
 }
 
 static void
-instance_run(struct service_instance *in)
+instance_run(struct service_instance *in, int stdout, int stderr)
 {
 	struct blobmsg_list_node *var;
 	struct blob_attr *cur;
 	char **argv;
 	int argc = 1; /* NULL terminated */
-	int rem, fd;
+	int rem, stdin;
 
 	if (in->nice)
 		setpriority(PRIO_PROCESS, 0, in->nice);
@@ -153,14 +164,28 @@ instance_run(struct service_instance *in)
 		argv[argc++] = blobmsg_data(cur);
 
 	argv[argc] = NULL;
-	fd = open("/dev/null", O_RDWR);
-	if (fd > -1) {
-		dup2(fd, STDIN_FILENO);
-		dup2(fd, STDOUT_FILENO);
-		dup2(fd, STDERR_FILENO);
-		if (fd > STDERR_FILENO)
-			close(fd);
+
+	stdin = open("/dev/null", O_RDONLY);
+
+	if (stdout == -1)
+		stdout = open("/dev/null", O_WRONLY);
+
+	if (stderr == -1)
+		stderr = open("/dev/null", O_WRONLY);
+
+	if (stdin > -1) {
+		dup2(stdin, STDIN_FILENO);
+		closefd(stdin);
 	}
+	if (stdout > -1) {
+		dup2(stdout, STDOUT_FILENO);
+		closefd(stdout);
+	}
+	if (stderr > -1) {
+		dup2(stderr, STDERR_FILENO);
+		closefd(stderr);
+	}
+
 	if (in->uid || in->gid) {
 		setuid(in->uid);
 		setgid(in->gid);
@@ -173,6 +198,8 @@ void
 instance_start(struct service_instance *in)
 {
 	int pid;
+	int opipe[2] = { -1, -1 };
+	int epipe[2] = { -1, -1 };
 
 	if (!avl_is_empty(&in->errors.avl)) {
 		LOG("Not starting instance %s::%s, an error was indicated\n", in->srv->name, in->name);
@@ -182,6 +209,20 @@ instance_start(struct service_instance *in)
 	if (in->proc.pending)
 		return;
 
+	if (in->stdout.fd.fd > -2) {
+		if (pipe(opipe)) {
+			ULOG_WARN("pipe() failed: %d (%s)\n", errno, strerror(errno));
+			opipe[0] = opipe[1] = -1;
+		}
+	}
+
+	if (in->stderr.fd.fd > -2) {
+		if (pipe(epipe)) {
+			ULOG_WARN("pipe() failed: %d (%s)\n", errno, strerror(errno));
+			epipe[0] = epipe[1] = -1;
+		}
+	}
+
 	in->restart = false;
 	in->halt = !in->respawn;
 
@@ -194,7 +235,9 @@ instance_start(struct service_instance *in)
 
 	if (!pid) {
 		uloop_done();
-		instance_run(in);
+		closefd(opipe[0]);
+		closefd(epipe[0]);
+		instance_run(in, opipe[1], epipe[1]);
 		return;
 	}
 
@@ -202,9 +245,63 @@ instance_start(struct service_instance *in)
 	in->proc.pid = pid;
 	clock_gettime(CLOCK_MONOTONIC, &in->start);
 	uloop_process_add(&in->proc);
+
+	if (opipe[0] > -1) {
+		ustream_fd_init(&in->stdout, opipe[0]);
+		closefd(opipe[1]);
+	}
+
+	if (epipe[0] > -1) {
+		ustream_fd_init(&in->stderr, epipe[0]);
+		closefd(epipe[1]);
+	}
+
 	service_event("instance.start", in->srv->name, in->name);
 }
 
+static void
+instance_stdio(struct ustream *s, int prio, struct service_instance *in)
+{
+	char *newline, *str, *arg0, ident[32];
+	int len;
+
+	do {
+		str = ustream_get_read_buf(s, NULL);
+		if (!str)
+			break;
+
+		newline = strchr(str, '\n');
+		if (!newline)
+			break;
+
+		*newline = 0;
+		len = newline + 1 - str;
+
+		arg0 = basename(blobmsg_data(blobmsg_data(in->command)));
+		snprintf(ident, sizeof(ident), "%s[%d]", arg0, in->proc.pid);
+
+		ulog_open(ULOG_STDIO|ULOG_SYSLOG, LOG_DAEMON, ident);
+		ulog(prio, "%s\n", str);
+		ulog_open(ULOG_STDIO|ULOG_SYSLOG, LOG_DAEMON, "procd");
+
+		ustream_consume(s, len);
+	} while (1);
+}
+
+static void
+instance_stdout(struct ustream *s, int bytes)
+{
+	instance_stdio(s, LOG_INFO,
+	               container_of(s, struct service_instance, stdout.stream));
+}
+
+static void
+instance_stderr(struct ustream *s, int bytes)
+{
+	instance_stdio(s, LOG_ERR,
+	               container_of(s, struct service_instance, stderr.stream));
+}
+
 static void
 instance_timeout(struct uloop_timeout *t)
 {
@@ -471,6 +568,12 @@ instance_config_parse(struct service_instance *in)
 		}
 	}
 
+	if (tb[INSTANCE_ATTR_STDOUT] && blobmsg_get_bool(tb[INSTANCE_ATTR_STDOUT]))
+		in->stdout.fd.fd = -1;
+
+	if (tb[INSTANCE_ATTR_STDERR] && blobmsg_get_bool(tb[INSTANCE_ATTR_STDERR]))
+		in->stderr.fd.fd = -1;
+
 	instance_fill_any(&in->data, tb[INSTANCE_ATTR_DATA]);
 
 	if (!instance_fill_array(&in->env, tb[INSTANCE_ATTR_ENV], NULL, false))
@@ -546,6 +649,16 @@ instance_update(struct service_instance *in, struct service_instance *in_new)
 void
 instance_free(struct service_instance *in)
 {
+	if (in->stdout.fd.fd > -1) {
+		ustream_free(&in->stdout.stream);
+		close(in->stdout.fd.fd);
+	}
+
+	if (in->stderr.fd.fd > -1) {
+		ustream_free(&in->stderr.stream);
+		close(in->stderr.fd.fd);
+	}
+
 	uloop_process_delete(&in->proc);
 	uloop_timeout_cancel(&in->timeout);
 	trigger_del(in);
@@ -565,6 +678,14 @@ instance_init(struct service_instance *in, struct service *s, struct blob_attr *
 	in->timeout.cb = instance_timeout;
 	in->proc.cb = instance_exit;
 
+	in->stdout.fd.fd = -2;
+	in->stdout.stream.string_data = true;
+	in->stdout.stream.notify_read = instance_stdout;
+
+	in->stderr.fd.fd = -2;
+	in->stderr.stream.string_data = true;
+	in->stderr.stream.notify_read = instance_stderr;
+
 	blobmsg_list_init(&in->netdev, struct instance_netdev, node, instance_netdev_cmp);
 	blobmsg_list_init(&in->file, struct instance_file, node, instance_file_cmp);
 	blobmsg_list_simple_init(&in->env);

+ 3 - 0
service/instance.h

@@ -17,6 +17,7 @@
 
 #include <libubox/vlist.h>
 #include <libubox/uloop.h>
+#include <libubox/ustream.h>
 #include "../utils/utils.h"
 
 #define RESPAWN_ERROR	(5 * 60)
@@ -45,6 +46,8 @@ struct service_instance {
 	struct blob_attr *config;
 	struct uloop_process proc;
 	struct uloop_timeout timeout;
+	struct ustream_fd stdout;
+	struct ustream_fd stderr;
 
 	struct blob_attr *command;
 	struct blob_attr *trigger;