Commit 169c1bda authored by Lennart Poettering's avatar Lennart Poettering
Browse files

service: optionally, create INIT_PROCESS/DEAD_PROCESS entries for a service

This should fix accounting for pam_limits and suchlike.

https://bugzilla.redhat.com/show_bug.cgi?id=636036
parent 926e4301
......@@ -374,6 +374,7 @@ libsystemd_core_la_SOURCES = \
src/path.c \
src/load-dropin.c \
src/execute.c \
src/utmp-wtmp.c \
src/exit-status.c \
src/dbus.c \
src/dbus-manager.c \
......@@ -440,7 +441,6 @@ EXTRA_DIST += \
src/dbus-common.h \
src/bus-errors.h \
src/cgroup-show.h \
src/utmp-wtmp.h \
src/build.h \
src/shutdownd.h \
src/readahead-common.h
......
......@@ -24,8 +24,6 @@
- bluetoothd (/var/run/sdp! @/org/bluez/audio!)
- distccd
* write utmp record a la upstart for processes
* selinux policy loading
* fingerprint.target, wireless.target, gps.target
......
......@@ -728,6 +728,27 @@
it.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>UtmpIdentifier=</varname></term>
<listitem><para>Takes a a four
character identifier string for an
utmp/wtmp entry for this service. This
should only be set for services such
as <command>getty</command>
implementations where utmp/wtmp
entries must be created and cleared
before and after execution. If the
configured string is longer than four
characters it is truncated and the
terminal four characters are
used. This setting interprets %I style
string replacements. This setting is
unset by default, i.e. no utmp/wtmp
entries are created or cleaned up for
this service.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
......
......@@ -84,8 +84,9 @@
" <property name=\"MountFlags\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"PrivateTmp\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"SameProcessGroup\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"KillMode\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"KillSignal\" type=\"i\" access=\"read\"/>\n"
" <property name=\"KillMode\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"KillSignal\" type=\"i\" access=\"read\"/>\n" \
" <property name=\"UtmpIdentifier\" type=\"s\" access=\"read\"/>\n"
#define BUS_EXEC_COMMAND_INTERFACE(name) \
" <property name=\"" name "\" type=\"a(sasbttuii)\" access=\"read\"/>\n"
......@@ -142,7 +143,8 @@
{ interface, "PrivateTmp", bus_property_append_bool, "b", &(context).private_tmp }, \
{ interface, "SameProcessGroup", bus_property_append_bool, "b", &(context).same_pgrp }, \
{ interface, "KillMode", bus_execute_append_kill_mode, "s", &(context).kill_mode }, \
{ interface, "KillSignal", bus_property_append_int, "i", &(context).kill_signal }
{ interface, "KillSignal", bus_property_append_int, "i", &(context).kill_signal }, \
{ interface, "UtmpIdentifier", bus_property_append_string, "s", &(context).utmp_id }
#define BUS_EXEC_STATUS_PROPERTIES(interface, estatus, prefix) \
{ interface, prefix "StartTimestamp", bus_property_append_usec, "t", &(estatus).start_timestamp.realtime }, \
......
......@@ -54,6 +54,7 @@
#include "tcpwrap.h"
#include "exit-status.h"
#include "missing.h"
#include "utmp-wtmp.h"
/* This assumes there is a 'tty' group */
#define TTY_MODE 0620
......@@ -1129,6 +1130,9 @@ int exec_spawn(ExecCommand *command,
goto fail;
}
if (context->utmp_id)
utmp_put_init_process(0, context->utmp_id, getpid(), getsid(0), context->tty_path);
if (context->user) {
username = context->user;
if (get_user_creds(&username, &uid, &gid, &home) < 0) {
......@@ -1604,6 +1608,12 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
"%sKillSignal: SIG%s\n",
prefix, kill_mode_to_string(c->kill_mode),
prefix, signal_to_string(c->kill_signal));
if (c->utmp_id)
fprintf(f,
"%sUtmpIdentifier: %s\n",
prefix, c->utmp_id);
}
void exec_status_start(ExecStatus *s, pid_t pid) {
......@@ -1614,7 +1624,7 @@ void exec_status_start(ExecStatus *s, pid_t pid) {
dual_timestamp_get(&s->start_timestamp);
}
void exec_status_exit(ExecStatus *s, pid_t pid, int code, int status) {
void exec_status_exit(ExecStatus *s, pid_t pid, int code, int status, const char *utmp_id) {
assert(s);
if ((s->pid && s->pid != pid) ||
......@@ -1626,6 +1636,9 @@ void exec_status_exit(ExecStatus *s, pid_t pid, int code, int status) {
s->code = code;
s->status = status;
if (utmp_id)
utmp_put_dead_process(utmp_id, pid, code, status);
}
void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix) {
......
......@@ -127,6 +127,8 @@ struct ExecContext {
char *pam_name;
char *utmp_id;
char **read_write_dirs, **read_only_dirs, **inaccessible_dirs;
unsigned long mount_flags;
......@@ -191,7 +193,7 @@ void exec_context_done(ExecContext *c);
void exec_context_dump(ExecContext *c, FILE* f, const char *prefix);
void exec_status_start(ExecStatus *s, pid_t pid);
void exec_status_exit(ExecStatus *s, pid_t pid, int code, int status);
void exec_status_exit(ExecStatus *s, pid_t pid, int code, int status, const char *utmp_id);
void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix);
const char* exec_output_to_string(ExecOutput i);
......
......@@ -1669,7 +1669,8 @@ static int load_from_path(Unit *u, const char *path) {
{ "TCPWrapName", config_parse_string_printf, &(context).tcpwrap_name, section }, \
{ "PAMName", config_parse_string_printf, &(context).pam_name, section }, \
{ "KillMode", config_parse_kill_mode, &(context).kill_mode, section }, \
{ "KillSignal", config_parse_kill_signal, &(context).kill_signal, section }
{ "KillSignal", config_parse_kill_signal, &(context).kill_signal, section }, \
{ "UtmpIdentifier", config_parse_string_printf, &(context).utmp_id, section }
const ConfigItem items[] = {
{ "Names", config_parse_names, u, "Unit" },
......
......@@ -1057,7 +1057,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
m->failure = m->failure || !success;
if (m->control_command) {
exec_status_exit(&m->control_command->exec_status, pid, code, status);
exec_status_exit(&m->control_command->exec_status, pid, code, status, m->exec_context.utmp_id);
m->control_command = NULL;
m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
}
......
......@@ -2406,7 +2406,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
if (s->main_pid == pid) {
s->main_pid = 0;
exec_status_exit(&s->main_exec_status, pid, code, status);
exec_status_exit(&s->main_exec_status, pid, code, status, s->exec_context.utmp_id);
if (s->type != SERVICE_FORKING && s->control_command) {
s->control_command->exec_status = s->main_exec_status;
......@@ -2483,7 +2483,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_pid = 0;
if (s->control_command) {
exec_status_exit(&s->control_command->exec_status, pid, code, status);
exec_status_exit(&s->control_command->exec_status, pid, code, status, s->exec_context.utmp_id);
if (s->control_command->ignore)
success = true;
......
......@@ -1601,7 +1601,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
success = is_clean_exit(code, status);
if (s->control_command) {
exec_status_exit(&s->control_command->exec_status, pid, code, status);
exec_status_exit(&s->control_command->exec_status, pid, code, status, s->exec_context.utmp_id);
if (s->control_command->ignore)
success = true;
......
......@@ -92,19 +92,26 @@ int utmp_get_runlevel(int *runlevel, int *previous) {
return r;
}
static void init_entry(struct utmpx *store, usec_t t) {
struct utsname uts;
static void init_timestamp(struct utmpx *store, usec_t t) {
assert(store);
zero(*store);
zero(uts);
if (t <= 0)
t = now(CLOCK_REALTIME);
store->ut_tv.tv_sec = t / USEC_PER_SEC;
store->ut_tv.tv_usec = t % USEC_PER_SEC;
}
static void init_entry(struct utmpx *store, usec_t t) {
struct utsname uts;
assert(store);
init_timestamp(store, t);
zero(uts);
if (uname(&uts) >= 0)
strncpy(store->ut_host, uts.release, sizeof(store->ut_host));
......@@ -187,6 +194,69 @@ int utmp_put_reboot(usec_t t) {
return write_entry_both(&store);
}
static const char *sanitize_id(const char *id) {
size_t l;
assert(id);
l = strlen(id);
if (l <= sizeof(((struct utmpx*) NULL)->ut_id))
return id;
return id + l - sizeof(((struct utmpx*) NULL)->ut_id);
}
int utmp_put_init_process(usec_t t, const char *id, pid_t pid, pid_t sid, const char *line) {
struct utmpx store;
assert(id);
init_timestamp(&store, t);
store.ut_type = INIT_PROCESS;
store.ut_pid = pid;
store.ut_session = sid;
strncpy(store.ut_id, sanitize_id(id), sizeof(store.ut_id));
if (line)
strncpy(store.ut_line, file_name_from_path(line), sizeof(store.ut_line));
return write_entry_both(&store);
}
int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
struct utmpx lookup, store, *found;
assert(id);
setutxent();
zero(lookup);
lookup.ut_type = INIT_PROCESS; /* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
strncpy(lookup.ut_id, sanitize_id(id), sizeof(lookup.ut_id));
if (!(found = getutxid(&lookup)))
return 0;
if (found->ut_pid != pid)
return 0;
zero(store);
memcpy(&store, &lookup, sizeof(store));
store.ut_type = DEAD_PROCESS;
store.ut_exit.e_termination = code;
store.ut_exit.e_exit = status;
zero(store.ut_user);
zero(store.ut_host);
zero(store.ut_tv);
return write_entry_both(&store);
}
int utmp_put_runlevel(usec_t t, int runlevel, int previous) {
struct utmpx store;
int r;
......
......@@ -30,6 +30,9 @@ int utmp_put_shutdown(usec_t timestamp);
int utmp_put_reboot(usec_t timestamp);
int utmp_put_runlevel(usec_t timestamp, int runlevel, int previous);
int utmp_put_dead_process(const char *id, pid_t pid, int code, int status);
int utmp_put_init_process(usec_t timestamp, const char *id, pid_t pid, pid_t sid, const char *line);
int utmp_wall(const char *message);
#endif
......@@ -33,6 +33,7 @@ Environment=TERM=linux
ExecStart=-GETTY %I
Restart=always
RestartSec=0
UtmpIdentifier=%I
KillMode=process-group
# Some login implementations ignore SIGTERM, so we send SIGHUP
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment