diff --git a/init/job_class.c b/init/job_class.c index b521cf4..1b8d1cf 100644 --- a/init/job_class.c +++ b/init/job_class.c @@ -218,6 +218,8 @@ job_class_new (const void *parent, class->chroot = NULL; class->chdir = NULL; + class->utmp_id = NULL; + class->deleted = FALSE; return class; diff --git a/init/job_class.h b/init/job_class.h index b7d4eed..1d29c59 100644 --- a/init/job_class.h +++ b/init/job_class.h @@ -145,6 +145,7 @@ typedef struct job_class { struct rlimit *limits[RLIMIT_NLIMITS]; char *chroot; char *chdir; + char *utmp_id; int deleted; } JobClass; diff --git a/init/job_process.c b/init/job_process.c index e3080d8..8ce2f72 100644 --- a/init/job_process.c +++ b/init/job_process.c @@ -38,6 +38,8 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <utmp.h> +#include <utmpx.h> #include <nih/macros.h> #include <nih/alloc.h> @@ -361,11 +363,13 @@ job_process_spawn (JobClass *class, char * const *env, int trace) { - sigset_t child_set, orig_set; - pid_t pid; - int i, fds[2]; - char filename[PATH_MAX]; - FILE *fd; + sigset_t child_set, orig_set; + pid_t pid; + int i, fds[2]; + char filename[PATH_MAX]; + FILE *fd; + struct utmpx utmp; + struct timeval tv; nih_assert (class != NULL); @@ -524,6 +528,20 @@ job_process_spawn (JobClass *class, } } + /* Write utmp INIT_PROCESS entry */ + if (class->utmp_id) { + memset(&utmp, 0, sizeof(struct utmpx)); + strncpy(utmp.ut_id, class->utmp_id, sizeof(utmp.ut_id)); + utmp.ut_pid = getpid(); + utmp.ut_type = INIT_PROCESS; + gettimeofday(&tv, NULL); + utmp.ut_tv.tv_sec = tv.tv_sec; + utmp.ut_tv.tv_usec = tv.tv_usec; + setutxent(); + pututxline(&utmp); + endutxent(); + } + /* Execute the process, if we escape from here it failed */ if (execvp (argv[0], argv) < 0) { nih_error_raise_system (); @@ -967,6 +985,8 @@ job_process_terminated (Job *job, int status) { int failed = FALSE, stop = FALSE, state = TRUE; + struct utmpx *utmptr; + struct timeval tv; nih_assert (job != NULL); @@ -1129,6 +1149,36 @@ job_process_terminated (Job *job, job->kill_process = -1; } + /* Find existing utmp entry for the process pid */ + setutxent(); + while ((utmptr = getutxent()) != NULL) { + if (utmptr->ut_pid == job->pid[process]) { + /* set type and clean ut_user, ut_host, + * ut_time as described in utmp(5) + */ + utmptr->ut_type = DEAD_PROCESS; + memset(utmptr->ut_user, 0, UT_NAMESIZE); + memset(utmptr->ut_host, 0, UT_HOSTSIZE); + utmptr->ut_time = 0; + + /* Set class utmp_id for next spawn */ + job->class->utmp_id = nih_strdup (job->class, utmptr->ut_id); + + /* Update existing utmp file. */ + pututxline(utmptr); + + /* set ut_time for log */ + gettimeofday(&tv, NULL); + utmptr->ut_tv.tv_sec = tv.tv_sec; + utmptr->ut_tv.tv_usec = tv.tv_usec; + /* Write wtmp entry */ + updwtmpx (_PATH_WTMP, utmptr); + + break; + } + } + endutxent(); + /* Clear the process pid field */ job->pid[process] = 0; diff --git a/init/tests/test_job_process.c b/init/tests/test_job_process.c index d43fa6e..b4f94e4 100644 --- a/init/tests/test_job_process.c +++ b/init/tests/test_job_process.c @@ -36,6 +36,8 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <utmp.h> +#include <utmpx.h> #include <nih/macros.h> #include <nih/string.h> @@ -4474,6 +4476,171 @@ test_find (void) } +void +test_utmp (void) +{ + JobClass * class; + Job * job = NULL; + Blocked * blocked = NULL; + Event * event; + FILE * output; + char utmpname[PATH_MAX]; + struct utmpx utmp, *utmptr; + struct timeval tv; + + TEST_FUNCTION ("job_process_handler"); + program_name = "test"; + + class = job_class_new (NULL, "test"); + class->process[PROCESS_MAIN] = process_new (class); + class->process[PROCESS_MAIN]->command = "echo"; + + class->start_on = event_operator_new (class, EVENT_MATCH, + "foo", NULL); + class->stop_on = event_operator_new (class, EVENT_MATCH, + "foo", NULL); + nih_hash_add (job_classes, &class->entry); + + event = event_new (NULL, "foo", NULL); + + TEST_FILENAME(utmpname); + + /* Check that utmp record for the running task of the job terminating + * is properly changed to DEAD_PROCESS + */ + TEST_FEATURE ("with LOGIN_PROCESS utmp entry"); + TEST_ALLOC_FAIL { + TEST_ALLOC_SAFE { + job = job_new (class, ""); + + blocked = blocked_new (job, BLOCKED_EVENT, event); + event_block (event); + nih_list_add (&job->blocking, &blocked->entry); + } + + job->goal = JOB_START; + job->state = JOB_RUNNING; + job->pid[PROCESS_MAIN] = 1; + + TEST_FREE_TAG (blocked); + + job->blocker = NULL; + event->failed = FALSE; + + job->failed = FALSE; + job->failed_process = -1; + job->exit_status = 0; + + output = fopen (utmpname, "w"); + fclose (output); + + /* set utmp file */ + utmpxname(utmpname); + + /* set up utmp entries */ + memset (&utmp, 0, sizeof utmp); + + strcpy(utmp.ut_id, "2"); + utmp.ut_type = LOGIN_PROCESS; + utmp.ut_pid = 2; + + gettimeofday(&tv, NULL); + utmp.ut_tv.tv_sec = tv.tv_sec; + utmp.ut_tv.tv_usec = tv.tv_usec; + + setutxent(); + pututxline(&utmp); + + strcpy(utmp.ut_id, "1"); + utmp.ut_pid = 1; + pututxline(&utmp); + + endutxent(); + + job_process_handler (NULL, 1, NIH_CHILD_EXITED, 0); + + setutxent(); + + utmptr = getutxent(); + TEST_NE_P(utmptr, NULL); + TEST_EQ(utmptr->ut_pid, 2); + TEST_EQ(utmptr->ut_type, LOGIN_PROCESS); + + utmptr = getutxent(); + TEST_NE_P(utmptr, NULL); + TEST_EQ(utmptr->ut_pid, 1); + TEST_EQ(utmptr->ut_type, DEAD_PROCESS); + + nih_free (job); + } + TEST_FEATURE ("with USER_PROCESS utmp entry"); + TEST_ALLOC_FAIL { + TEST_ALLOC_SAFE { + job = job_new (class, ""); + + blocked = blocked_new (job, BLOCKED_EVENT, event); + event_block (event); + nih_list_add (&job->blocking, &blocked->entry); + } + + job->goal = JOB_START; + job->state = JOB_RUNNING; + job->pid[PROCESS_MAIN] = 1; + + TEST_FREE_TAG (blocked); + + job->blocker = NULL; + event->failed = FALSE; + + job->failed = FALSE; + job->failed_process = -1; + job->exit_status = 0; + + output = fopen (utmpname, "w"); + fclose (output); + + /* set utmp file */ + utmpxname(utmpname); + + /* set up utmp entries */ + memset (&utmp, 0, sizeof utmp); + + strcpy(utmp.ut_id, "2"); + utmp.ut_type = USER_PROCESS; + utmp.ut_pid = 2; + + gettimeofday(&tv, NULL); + utmp.ut_tv.tv_sec = tv.tv_sec; + utmp.ut_tv.tv_usec = tv.tv_usec; + + setutxent(); + pututxline(&utmp); + + strcpy(utmp.ut_id, "1"); + utmp.ut_pid = 1; + pututxline(&utmp); + + endutxent(); + + job_process_handler (NULL, 1, NIH_CHILD_EXITED, 0); + + setutxent(); + + utmptr = getutxent(); + TEST_NE_P(utmptr, NULL); + TEST_EQ(utmptr->ut_pid, 2); + TEST_EQ(utmptr->ut_type, USER_PROCESS); + + utmptr = getutxent(); + TEST_NE_P(utmptr, NULL); + TEST_EQ(utmptr->ut_pid, 1); + TEST_EQ(utmptr->ut_type, DEAD_PROCESS); + + nih_free (job); + } +} + + int main (int argc, char *argv[]) @@ -4505,6 +4672,7 @@ main (int argc, test_spawn (); test_kill (); test_handler (); + test_utmp (); test_find ();