*/
struct ul_pty_callbacks {
/*
- * Executed on SIGCHLD when ssi_code is EXITED, KILLED or DUMPED
- * @
+ * Optional. Executed on SIGCHLD when ssi_code is EXITED, KILLED or
+ * DUMPED; The callback has to call ul_pty_set_child(pty, (pid_t) -1)
+ * if child is no more alive.
*/
- void (*child_wait)(void *);
+ void (*child_wait)(void *, pid_t);
+
+ /*
+ * Used when child_wait() undefined to informa about child status
+ */
+ void (*child_die)(void *, pid_t, int);
/*
* Executed on SIGCHLD when ssi_status is SIGSTOP
*/
- void (*child_sigstop)(void *);
+ void (*child_sigstop)(void *, pid_t);
/*
* Executed in master loop before ul_pty enter poll() and in time set by
void ul_pty_set_mainloop_time(struct ul_pty *pty, struct timeval *tv);
int ul_pty_get_childfd(struct ul_pty *pty);
+void ul_pty_wait_for_child(struct ul_pty *pty);
+pid_t ul_pty_get_child(struct ul_pty *pty);
#endif /* UTIL_LINUX_PTY_H */
return pty->master;
}
+pid_t ul_pty_get_child(struct ul_pty *pty)
+{
+ assert(pty);
+ return pty->child;
+}
+
/* it's active when signals are redurected to sigfd */
int ul_pty_is_running(struct ul_pty *pty)
{
return rc;
}
+void ul_pty_wait_for_child(struct ul_pty *pty)
+{
+ int status;
+ pid_t pid;
+ int options = 0;
+
+ if (pty->child == (pid_t) -1)
+ return;
+
+ DBG(SIG, ul_debug("waiting for child"));
+
+ if (ul_pty_is_running(pty)) {
+ /* wait for specific child */
+ options = WNOHANG;
+ for (;;) {
+ pid = waitpid(pty->child, &status, options);
+ if (pid != (pid_t) - 1) {
+ if (pty->callbacks.child_die)
+ pty->callbacks.child_die(
+ pty->callback_data,
+ pty->child, status);
+ ul_pty_set_child(pty, (pid_t) -1);
+ } else
+ break;
+ }
+ } else {
+ /* final wait */
+ while ((pid = wait3(&status, options, NULL)) > 0) {
+ if (pid == pty->child) {
+ if (pty->callbacks.child_die)
+ pty->callbacks.child_die(
+ pty->callback_data,
+ pty->child, status);
+ ul_pty_set_child(pty, (pid_t) -1);
+ }
+ }
+ }
+}
+
static int handle_signal(struct ul_pty *pty, int fd)
{
struct signalfd_siginfo info;
if (info.ssi_code == CLD_EXITED
|| info.ssi_code == CLD_KILLED
- || info.ssi_code == CLD_DUMPED)
- pty->callbacks.child_wait(pty->callback_data);
+ || info.ssi_code == CLD_DUMPED) {
+
+ if (pty->callbacks.child_wait)
+ pty->callbacks.child_wait(pty->callback_data,
+ pty->child);
+ else
+ ul_pty_wait_for_child(pty);
- else if (info.ssi_status == SIGSTOP && pty->child > 0)
- pty->callbacks.child_sigstop(pty->callback_data);
+ } else if (info.ssi_status == SIGSTOP && pty->child > 0)
+ pty->callbacks.child_sigstop(pty->callback_data,
+ pty->child);
if (pty->child <= 0) {
pty->poll_timeout = 10;
* ... and see for example tty(1) or "ps afu"
*/
struct ptytest {
- pid_t child;
- int childstatus;
-
struct ul_pty *pty;
};
-/* on child exit/dump/... */
-static void wait_for_child(void *data)
-{
- struct ptytest *ss = (struct ptytest *) data;
- int status;
- pid_t pid;
- int options = 0;
-
- if (ss->child == (pid_t) -1)
- return;
-
- if (ul_pty_is_running(ss->pty)) {
- /* wait for specific child */
- options = WNOHANG;
- for (;;) {
- pid = waitpid(ss->child, &status, options);
- if (pid != (pid_t) - 1) {
- ss->childstatus = status;
- ss->child = (pid_t) -1;
- ul_pty_set_child(ss->pty, (pid_t) -1);
- } else
- break;
- }
- } else {
- /* final wait */
- while ((pid = wait3(&status, options, NULL)) > 0) {
- if (pid == ss->child) {
- ss->childstatus = status;
- ss->child = (pid_t) -1;
- ul_pty_set_child(ss->pty, (pid_t) -1);
- }
- }
- }
-}
-
-static void child_sigstop(void *data)
+static void child_sigstop(void *data __attribute__((__unused__)), pid_t child)
{
- struct ptytest *ss = (struct ptytest *) data;
kill(getpid(), SIGSTOP);
- kill(ss->child, SIGCONT);
+ kill(child, SIGCONT);
}
int main(int argc, char *argv[])
{
- struct ptytest ss = { .child = (pid_t) -1 };
+ struct ptytest ss = { .pty = NULL };
struct ul_pty_callbacks *cb;
const char *shell, *command = NULL, *shname = NULL;
int caught_signal = 0;
+ pid_t child;
shell = getenv("SHELL");
if (shell == NULL)
ul_pty_set_callback_data(ss.pty, (void *) &ss);
cb = ul_pty_get_callbacks(ss.pty);
- cb->child_wait = wait_for_child;
cb->child_sigstop = child_sigstop;
if (ul_pty_setup(ss.pty))
fflush(stdout); /* ??? */
- switch ((int) (ss.child = fork())) {
+ switch ((int) (child = fork())) {
case -1: /* error */
ul_pty_cleanup(ss.pty);
err(EXIT_FAILURE, "cannot create child process");
}
/* parent */
- ul_pty_set_child(ss.pty, ss.child);
+ ul_pty_set_child(ss.pty, child);
/* this is the main loop */
ul_pty_proxy_master(ss.pty);
/* all done; cleanup and kill */
caught_signal = ul_pty_get_delivered_signal(ss.pty);
- if (!caught_signal && ss.child != (pid_t)-1)
- wait_for_child(&ss); /* final wait */
+ if (!caught_signal && ul_pty_get_child(ss.pty) != (pid_t)-1)
+ ul_pty_wait_for_child(ss.pty); /* final wait */
- if (caught_signal && ss.child != (pid_t)-1) {
+ if (caught_signal && ul_pty_get_child(ss.pty) != (pid_t)-1) {
fprintf(stderr, "\nSession terminated, killing shell...");
- kill(ss.child, SIGTERM);
+ kill(child, SIGTERM);
sleep(2);
- kill(ss.child, SIGKILL);
+ kill(child, SIGKILL);
fprintf(stderr, " ...killed.\n");
}
ul_pty_cleanup(ss.pty);
+ ul_free_pty(ss.pty);
return EXIT_SUCCESS;
}
*/
static int wait_for_child(struct su_context *su)
{
- pid_t pid = (pid_t) -1;;
+ pid_t pid = (pid_t) -1;
int status = 0;
if (su->child == (pid_t) -1)
}
#ifdef USE_PTY
-static void wait_for_child_cb(void *data)
+static void wait_for_child_cb(
+ void *data,
+ pid_t child __attribute__((__unused__)))
{
wait_for_child((struct su_context *) data);
}
#define DBG(m, x) __UL_DBG(script, SCRIPT_DEBUG_, m, x)
#define ON_DBG(m, x) __UL_DBG_CALL(script, SCRIPT_DEBUG_, m, x)
-#if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H)
-# include <pty.h>
-#endif
-
#ifdef HAVE_LIBUTEMPTER
# include <utempter.h>
#endif
int ttycols;
int ttylines;
- struct ul_pty *pty;
+ struct ul_pty *pty; /* pseudo-terminal */
pid_t child; /* child pid */
int childstatus; /* child process exit value */
log_close(ctl, ctl->in.logs[i], msg, status);
}
-static void wait_for_child(void *data)
+static void callback_child_die(
+ void *data,
+ pid_t child __attribute__((__unused__)),
+ int status)
{
struct script_control *ctl = (struct script_control *) data;
- int status;
- pid_t pid;
- int options = 0;
-
- if (ctl->child == (pid_t) -1)
- return;
- DBG(MISC, ul_debug("waiting for child"));
-
- if (ul_pty_is_running(ctl->pty)) {
- /* wait for specific child */
- options = WNOHANG;
- for (;;) {
- pid = waitpid(ctl->child, &status, options);
- if (pid != (pid_t) - 1) {
- ctl->childstatus = status;
- ctl->child = (pid_t) -1;
- ul_pty_set_child(ctl->pty, (pid_t) -1);
- } else
- break;
- }
- } else {
- /* final wait */
- while ((pid = wait3(&status, options, NULL)) > 0) {
- if (pid == ctl->child) {
- ctl->childstatus = status;
- ctl->child = (pid_t) -1;
- ul_pty_set_child(ctl->pty, (pid_t) -1);
- }
- }
- }
+ ctl->child = (pid_t) -1;
+ ctl->childstatus = status;
}
-static void callback_child_sigstop(void *data)
+static void callback_child_sigstop(
+ void *data __attribute__((__unused__)),
+ pid_t child)
{
- struct script_control *ctl = (struct script_control *) data;
-
DBG(SIGNAL, ul_debug(" child stop by SIGSTOP -- stop parent too"));
kill(getpid(), SIGSTOP);
DBG(SIGNAL, ul_debug(" resume"));
- kill(ctl->child, SIGCONT);
+ kill(child, SIGCONT);
}
static int callback_log_stream_activity(void *data, int fd, char *buf, size_t bufsz)
ul_pty_set_callback_data(ctl.pty, (void *) &ctl);
cb = ul_pty_get_callbacks(ctl.pty);
- cb->child_wait = wait_for_child;
+ cb->child_die = callback_child_die;
cb->child_sigstop = callback_child_sigstop;
cb->log_stream_activity = callback_log_stream_activity;
cb->log_signal = callback_log_signal;
if (rc)
goto done;
+ /* add extra info to advanced timing file */
if (timingfile && format == SCRIPT_FMT_TIMING_MULTI) {
char buf[FORMAT_TIMESTAMP_MAX];
time_t tvec = script_time((time_t *)NULL);
caught_signal = ul_pty_get_delivered_signal(ctl.pty);
if (!caught_signal && ctl.child != (pid_t)-1)
- wait_for_child(&ctl); /* final wait */
+ ul_pty_wait_for_child(ctl.pty); /* final wait */
if (caught_signal && ctl.child != (pid_t)-1) {
fprintf(stderr, "\nSession terminated, killing shell...");
return d;
}
-/* on child exit/dump/... */
-static void wait_for_child(void *data)
+static void callback_child_die(
+ void *data,
+ pid_t child __attribute__((__unused__)),
+ int status)
{
struct scriptlive *ss = (struct scriptlive *) data;
- int status;
- pid_t pid;
- int options = 0;
-
- if (ss->child == (pid_t) -1)
- return;
-
- if (ul_pty_is_running(ss->pty)) {
- /* wait for specific child */
- options = WNOHANG;
- for (;;) {
- pid = waitpid(ss->child, &status, options);
- if (pid != (pid_t) - 1) {
- ss->childstatus = status;
- ss->child = (pid_t) -1;
- ul_pty_set_child(ss->pty, (pid_t) -1);
- } else
- break;
- }
- } else {
- /* final wait */
- while ((pid = wait3(&status, options, NULL)) > 0) {
- if (pid == ss->child) {
- ss->childstatus = status;
- ss->child = (pid_t) -1;
- ul_pty_set_child(ss->pty, (pid_t) -1);
- }
- }
- }
+
+ ss->child = (pid_t) -1;
+ ss->childstatus = status;
}
-static void child_sigstop(void *data)
+static void callback_child_sigstop(
+ void *data __attribute__((__unused__)),
+ pid_t child)
{
- struct scriptlive *ss = (struct scriptlive *) data;
kill(getpid(), SIGSTOP);
- kill(ss->child, SIGCONT);
+ kill(child, SIGCONT);
}
static int process_next_step(struct scriptlive *ss)
ul_pty_set_callback_data(ss.pty, (void *) &ss);
cb = ul_pty_get_callbacks(ss.pty);
- cb->child_wait = wait_for_child;
- cb->child_sigstop = child_sigstop;
+ cb->child_die = callback_child_die;
+ cb->child_sigstop = callback_child_sigstop;
cb->mainloop = mainloop_cb;
if (ul_pty_setup(ss.pty))
caught_signal = ul_pty_get_delivered_signal(ss.pty);
if (!caught_signal && ss.child != (pid_t)-1)
- wait_for_child(&ss); /* final wait */
+ ul_pty_wait_for_child(ss.pty); /* final wait */
if (caught_signal && ss.child != (pid_t)-1) {
fprintf(stderr, _("\nSession terminated, killing shell..."));
/*
- * Copyright (C) 2008, Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2008-2019, Karel Zak <kzak@redhat.com>
* Copyright (C) 2008, James Youngman <jay@gnu.org>
*
* This file is free software; you can redistribute it and/or modify