summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2010-01-28 02:06:20 +0100
committerLennart Poettering <lennart@poettering.net>2010-01-28 02:06:20 +0100
commit071830ff32351c19343ff1f0343c13d5c2b69250 (patch)
tree9e564b64a8d80ee717b5d5859ac3d56ba350a087
parentce578209aa4e061aa38d3a4727612bb388307bf9 (diff)
implement proper logging for services
-rw-r--r--execute.c119
-rw-r--r--execute.h20
-rw-r--r--fixme2
-rw-r--r--load-fragment.c171
-rw-r--r--test1/exec-demo.service4
-rw-r--r--test1/systemd-logger.socket2
6 files changed, 312 insertions, 6 deletions
diff --git a/execute.c b/execute.c
index 1ca91fddd7..ccf951a25a 100644
--- a/execute.c
+++ b/execute.c
@@ -7,6 +7,8 @@
#include <unistd.h>
#include <string.h>
#include <signal.h>
+#include <sys/socket.h>
+#include <sys/un.h>
#include "execute.h"
#include "strv.h"
@@ -139,6 +141,112 @@ static int flags_fds(int fds[], unsigned n_fds) {
return 0;
}
+static int replace_null_fd(int fd, int flags) {
+ int nfd;
+ assert(fd >= 0);
+
+ close_nointr(fd);
+
+ if ((nfd = open("/dev/null", flags|O_NOCTTY)) < 0)
+ return -errno;
+
+ if (nfd != fd) {
+ close_nointr_nofail(nfd);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int setup_output(const ExecContext *context, const char *ident) {
+ int r;
+
+ assert(context);
+
+ switch (context->output) {
+
+ case EXEC_CONSOLE:
+ return 0;
+
+ case EXEC_NULL:
+
+ if ((r = replace_null_fd(STDIN_FILENO, O_RDONLY)) < 0 ||
+ (r = replace_null_fd(STDOUT_FILENO, O_WRONLY)) < 0 ||
+ (r = replace_null_fd(STDERR_FILENO, O_WRONLY)) < 0)
+ return r;
+
+ return 0;
+
+ case EXEC_KERNEL:
+ case EXEC_SYSLOG: {
+
+ int fd;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_un un;
+ } sa;
+
+ if ((r = replace_null_fd(STDIN_FILENO, O_RDONLY)) < 0)
+ return r;
+
+ close_nointr(STDOUT_FILENO);
+ close_nointr(STDERR_FILENO);
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ return -errno;
+
+ if (fd != STDOUT_FILENO) {
+ close_nointr_nofail(fd);
+ return -EIO;
+ }
+
+ zero(sa);
+ sa.sa.sa_family = AF_UNIX;
+ strncpy(sa.un.sun_path+1, LOGGER_SOCKET, sizeof(sa.un.sun_path)-1);
+
+ if (connect(fd, &sa.sa, sizeof(sa)) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ if (shutdown(fd, SHUT_RD) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ if ((fd = dup(fd)) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ if (fd != STDERR_FILENO) {
+ close_nointr_nofail(fd);
+ return -EIO;
+ }
+
+ /* We speak a very simple protocol between log server
+ * and client: one line for the log destination (kmsg
+ * or syslog), followed by the priority field,
+ * followed by the process name. Since we replaced
+ * stdin/stderr we simple use stdio to write to
+ * it. Note that we use stderr, to minimize buffer
+ * flushing issues. */
+
+ fprintf(stderr,
+ "%s\n"
+ "%i\n"
+ "%s\n",
+ context->output == EXEC_KERNEL ? "kmsg" : "syslog",
+ context->syslog_priority,
+ context->syslog_identifier ? context->syslog_identifier : ident);
+
+ return 0;
+ }
+ }
+
+ assert_not_reached("Unknown logging type");
+}
+
int exec_spawn(const ExecCommand *command, const ExecContext *context, int *fds, unsigned n_fds, pid_t *ret) {
pid_t pid;
@@ -173,6 +281,11 @@ int exec_spawn(const ExecCommand *command, const ExecContext *context, int *fds,
goto fail;
}
+ if (setup_output(context, file_name_from_path(command->path)) < 0) {
+ r = EXIT_OUTPUT;
+ goto fail;
+ }
+
snprintf(t, sizeof(t), "%i", context->oom_adjust);
char_array_0(t);
@@ -251,6 +364,9 @@ void exec_context_init(ExecContext *c) {
cap_clear(c->capabilities);
c->oom_adjust = 0;
c->nice = 0;
+
+ c->output = 0;
+ c->syslog_priority = LOG_DAEMON|LOG_INFO;
}
void exec_context_done(ExecContext *c) {
@@ -269,6 +385,9 @@ void exec_context_done(ExecContext *c) {
free(c->directory);
c->directory = NULL;
+ free(c->syslog_identifier);
+ c->syslog_identifier = NULL;
+
free(c->user);
c->user = NULL;
diff --git a/execute.h b/execute.h
index b7dbe68491..3283e1f815 100644
--- a/execute.h
+++ b/execute.h
@@ -16,6 +16,16 @@ typedef struct ExecContext ExecContext;
#include "list.h"
#include "util.h"
+/* Abstract namespace! */
+#define LOGGER_SOCKET "/systemd/logger"
+
+typedef enum ExecOutput {
+ EXEC_CONSOLE,
+ EXEC_NULL,
+ EXEC_SYSLOG,
+ EXEC_KERNEL
+} ExecOutput;
+
struct ExecStatus {
pid_t pid;
usec_t timestamp;
@@ -33,11 +43,16 @@ struct ExecCommand {
struct ExecContext {
char **environment;
mode_t umask;
- struct rlimit *rlimit[RLIMIT_NLIMITS];
+ struct rlimit *rlimit[RLIMIT_NLIMITS]; /* FIXME: load-fragment parser missing */
int oom_adjust;
int nice;
char *directory;
+ ExecOutput output;
+ int syslog_priority;
+ char *syslog_identifier;
+
+ /* FIXME: all privs related settings need parser and enforcer */
cap_t capabilities;
bool capabilities_set:1;
@@ -72,7 +87,8 @@ typedef enum ExitStatus {
EXIT_MEMORY,
EXIT_LIMITS,
EXIT_OOM_ADJUST,
- EXIT_SIGNAL_MASK
+ EXIT_SIGNAL_MASK,
+ EXIT_OUTPUT
} ExitStatus;
int exec_spawn(const ExecCommand *command, const ExecContext *context, int *fds, unsigned n_fds, pid_t *ret);
diff --git a/fixme b/fixme
index 800801c4e7..4e82c92ed4 100644
--- a/fixme
+++ b/fixme
@@ -42,3 +42,5 @@
- rate limit startups
- automatically delete stale unix sockets
+
+- .socket needs to be notified not only by .service state changes, but also unsuccessful start jobs
diff --git a/load-fragment.c b/load-fragment.c
index 2df5c04f77..2757506aba 100644
--- a/load-fragment.c
+++ b/load-fragment.c
@@ -479,8 +479,151 @@ int config_parse_bindtodevice(
return 0;
}
-#define FOLLOW_MAX 8
+int config_parse_output(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ExecOutput *o = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (streq(rvalue, "syslog"))
+ *o = EXEC_SYSLOG;
+ else if (streq(rvalue, "null"))
+ *o = EXEC_NULL;
+ else if (streq(rvalue, "syslog"))
+ *o = EXEC_SYSLOG;
+ else if (streq(rvalue, "kernel"))
+ *o = EXEC_KERNEL;
+ else {
+ log_error("[%s:%u] Failed to parse log output: %s", filename, line, rvalue);
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+int config_parse_facility(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ static const char * const table[LOG_NFACILITIES] = {
+ [LOG_FAC(LOG_KERN)] = "kern",
+ [LOG_FAC(LOG_USER)] = "user",
+ [LOG_FAC(LOG_MAIL)] = "mail",
+ [LOG_FAC(LOG_DAEMON)] = "daemon",
+ [LOG_FAC(LOG_AUTH)] = "auth",
+ [LOG_FAC(LOG_SYSLOG)] = "syslog",
+ [LOG_FAC(LOG_LPR)] = "lpr",
+ [LOG_FAC(LOG_NEWS)] = "news",
+ [LOG_FAC(LOG_UUCP)] = "uucp",
+ [LOG_FAC(LOG_CRON)] = "cron",
+ [LOG_FAC(LOG_AUTHPRIV)] = "authpriv",
+ [LOG_FAC(LOG_FTP)] = "ftp",
+ [LOG_FAC(LOG_LOCAL0)] = "local0",
+ [LOG_FAC(LOG_LOCAL1)] = "local1",
+ [LOG_FAC(LOG_LOCAL2)] = "local2",
+ [LOG_FAC(LOG_LOCAL3)] = "local3",
+ [LOG_FAC(LOG_LOCAL4)] = "local4",
+ [LOG_FAC(LOG_LOCAL5)] = "local5",
+ [LOG_FAC(LOG_LOCAL6)] = "local6",
+ [LOG_FAC(LOG_LOCAL7)] = "local7"
+ };
+
+ ExecOutput *o = data;
+ int i;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ for (i = 0; i < (int) ELEMENTSOF(table); i++)
+ if (streq(rvalue, table[i])) {
+ *o = LOG_MAKEPRI(i, LOG_PRI(*o));
+ break;
+ }
+
+ if (i >= (int) ELEMENTSOF(table)) {
+
+ /* Second try, let's see if this is a number. */
+ if (safe_atoi(rvalue, &i) >= 0 &&
+ i >= 0 &&
+ i < (int) ELEMENTSOF(table))
+ *o = LOG_MAKEPRI(i, LOG_PRI(*o));
+ else {
+ log_error("[%s:%u] Failed to parse log output: %s", filename, line, rvalue);
+ return -EBADMSG;
+ }
+ }
+
+ return 0;
+}
+
+int config_parse_level(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ static const char * const table[LOG_DEBUG+1] = {
+ [LOG_EMERG] = "emerg",
+ [LOG_ALERT] = "alert",
+ [LOG_CRIT] = "crit",
+ [LOG_ERR] = "err",
+ [LOG_WARNING] = "warning",
+ [LOG_NOTICE] = "notice",
+ [LOG_INFO] = "info",
+ [LOG_DEBUG] = "debug"
+ };
+
+ ExecOutput *o = data;
+ int i;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ for (i = 0; i < (int) ELEMENTSOF(table); i++)
+ if (streq(rvalue, table[i])) {
+ *o = LOG_MAKEPRI(LOG_FAC(*o), i);
+ break;
+ }
+
+ if (i >= LOG_NFACILITIES) {
+
+ /* Second try, let's see if this is a number. */
+ if (safe_atoi(rvalue, &i) >= 0 &&
+ i >= 0 &&
+ i < (int) ELEMENTSOF(table))
+ *o = LOG_MAKEPRI(LOG_FAC(*o), i);
+ else {
+ log_error("[%s:%u] Failed to parse log output: %s", filename, line, rvalue);
+ return -EBADMSG;
+ }
+ }
+
+ return 0;
+}
+
+#define FOLLOW_MAX 8
static int open_follow(char **filename, FILE **_f, Set *names, char **_id) {
unsigned c = 0;
@@ -569,7 +712,11 @@ static int load_from_path(Unit *u, const char *path) {
{ "Nice", config_parse_nice, &(context).nice, section }, \
{ "OOMAdjust", config_parse_oom_adjust, &(context).oom_adjust, section }, \
{ "UMask", config_parse_umask, &(context).umask, section }, \
- { "Environment", config_parse_strv, &(context).environment, section }
+ { "Environment", config_parse_strv, &(context).environment, section }, \
+ { "Output", config_parse_output, &(context).output, section }, \
+ { "SyslogIdentifier", config_parse_string, &(context).syslog_identifier, section }, \
+ { "SyslogFacility", config_parse_facility, &(context).syslog_priority, section }, \
+ { "SyslogLevel", config_parse_level, &(context).syslog_priority, section }
const ConfigItem items[] = {
{ "Names", config_parse_names, u, "Meta" },
@@ -678,6 +825,7 @@ finish:
int unit_load_fragment(Unit *u) {
int r = -ENOENT;
+ ExecContext *c;
assert(u);
assert(u->meta.load_state == UNIT_STUB);
@@ -694,5 +842,24 @@ int unit_load_fragment(Unit *u) {
return r;
}
+ if (u->meta.type == UNIT_SOCKET)
+ c = &u->socket.exec_context;
+ else if (u->meta.type == UNIT_SERVICE)
+ c = &u->service.exec_context;
+ else
+ c = NULL;
+
+ if (r >= 0 && c &&
+ (c->output == EXEC_KERNEL || c->output == EXEC_SYSLOG)) {
+ /* If syslog or kernel logging is requested, make sure
+ * our own logging daemon is run first. */
+
+ if ((r = unit_add_dependency(u, UNIT_AFTER, u->meta.manager->special_units[SPECIAL_LOGGER_SOCKET])) < 0)
+ return r;
+
+ if ((r = unit_add_dependency(u, UNIT_REQUIRES, u->meta.manager->special_units[SPECIAL_LOGGER_SOCKET])) < 0)
+ return r;
+ }
+
return r;
}
diff --git a/test1/exec-demo.service b/test1/exec-demo.service
index ea40b1fecf..b772518e3e 100644
--- a/test1/exec-demo.service
+++ b/test1/exec-demo.service
@@ -2,4 +2,6 @@
Description=Simple Execution Demo
[Service]
-ExecStart=/bin/ls
+ExecStart=/bin/cat /etc/hosts
+Type=simple
+Output=syslog
diff --git a/test1/systemd-logger.socket b/test1/systemd-logger.socket
index a013067ab2..ae0c81a91e 100644
--- a/test1/systemd-logger.socket
+++ b/test1/systemd-logger.socket
@@ -2,6 +2,6 @@
Description=systemd Logging Socket
[Socket]
-ExecStartPre=/bin/rm /tmp/systemd-logger
+ExecStartPre=/bin/rm -f /tmp/systemd-logger
ListenStream==/systemd/logger
ListenStream=/tmp/systemd-logger