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
This commit is contained in:
Lennart Poettering 2010-10-08 16:06:23 +02:00
parent 926e430133
commit 169c1bda80
13 changed files with 128 additions and 17 deletions

View File

@ -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

2
TODO
View File

@ -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

View File

@ -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>

View File

@ -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 }, \

View File

@ -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) {

View File

@ -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);

View File

@ -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" },

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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