mirror of
https://github.com/AuxXxilium/eudev.git
synced 2025-01-19 02:28:04 +07:00
implement proper logging for services
This commit is contained in:
parent
ce578209aa
commit
071830ff32
119
execute.c
119
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;
|
||||
|
||||
|
20
execute.h
20
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);
|
||||
|
2
fixme
2
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
|
||||
|
171
load-fragment.c
171
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;
|
||||
}
|
||||
|
@ -2,4 +2,6 @@
|
||||
Description=Simple Execution Demo
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/ls
|
||||
ExecStart=/bin/cat /etc/hosts
|
||||
Type=simple
|
||||
Output=syslog
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user