From fb0eeddc671039cabd9c53934e324ae218b21000 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Wed, 29 Jun 2016 21:18:20 +0100 Subject: [PATCH] Add mexp_send_interrupt, mexp_spawnvf, mexp_spawnlf and various flags. --- miniexpect.c | 81 +++++++++++++++++++++++++++++++++++----------------------- miniexpect.h | 13 ++++++++-- miniexpect.pod | 38 ++++++++++++++++++++++++++- 3 files changed, 97 insertions(+), 35 deletions(-) diff --git a/miniexpect.c b/miniexpect.c index ee2b7fa..caf498d 100644 --- a/miniexpect.c +++ b/miniexpect.c @@ -97,7 +97,7 @@ mexp_close (mexp_h *h) } mexp_h * -mexp_spawnl (const char *file, const char *arg, ...) +mexp_spawnlf (unsigned flags, const char *file, const char *arg, ...) { char **argv, **new_argv; size_t i; @@ -122,14 +122,14 @@ mexp_spawnl (const char *file, const char *arg, ...) argv[i] = (char *) arg; } - h = mexp_spawnv (file, argv); + h = mexp_spawnvf (flags, file, argv); free (argv); va_end (args); return h; } mexp_h * -mexp_spawnv (const char *file, char **argv) +mexp_spawnvf (unsigned flags, const char *file, char **argv) { mexp_h *h = NULL; int fd = -1; @@ -161,21 +161,24 @@ mexp_spawnv (const char *file, char **argv) goto error; if (pid == 0) { /* Child. */ - struct termios terminal_settings; - struct sigaction sa; - int i, slave_fd, max_fd; - - /* Remove all signal handlers. See the justification here: - * https://www.redhat.com/archives/libvir-list/2008-August/msg00303.html - * We don't mask signal handlers yet, so this isn't completely - * race-free, but better than not doing it at all. - */ - memset (&sa, 0, sizeof sa); - sa.sa_handler = SIG_DFL; - sa.sa_flags = 0; - sigemptyset (&sa.sa_mask); - for (i = 1; i < NSIG; ++i) - sigaction (i, &sa, NULL); + int slave_fd; + + if (!(flags & MEXP_SPAWN_KEEP_SIGNALS)) { + struct sigaction sa; + int i; + + /* Remove all signal handlers. See the justification here: + * https://www.redhat.com/archives/libvir-list/2008-August/msg00303.html + * We don't mask signal handlers yet, so this isn't completely + * race-free, but better than not doing it at all. + */ + memset (&sa, 0, sizeof sa); + sa.sa_handler = SIG_DFL; + sa.sa_flags = 0; + sigemptyset (&sa.sa_mask); + for (i = 1; i < NSIG; ++i) + sigaction (i, &sa, NULL); + } setsid (); @@ -186,10 +189,14 @@ mexp_spawnv (const char *file, char **argv) if (slave_fd == -1) goto error; - /* Set raw mode. */ - tcgetattr (slave_fd, &terminal_settings); - cfmakeraw (&terminal_settings); - tcsetattr (slave_fd, TCSANOW, &terminal_settings); + if (!(flags & MEXP_SPAWN_COOKED_MODE)) { + struct termios termios; + + /* Set raw mode. */ + tcgetattr (slave_fd, &termios); + cfmakeraw (&termios); + tcsetattr (slave_fd, TCSANOW, &termios); + } /* Set up stdin, stdout, stderr to point to the pty. */ dup2 (slave_fd, 0); @@ -202,16 +209,20 @@ mexp_spawnv (const char *file, char **argv) */ close (fd); - /* Close all other file descriptors. This ensures that we don't - * hold open (eg) pipes from the parent process. - */ - max_fd = sysconf (_SC_OPEN_MAX); - if (max_fd == -1) - max_fd = 1024; - if (max_fd > 65536) - max_fd = 65536; /* bound the amount of work we do here */ - for (fd = 3; fd < max_fd; ++fd) - close (fd); + if (!(flags & MEXP_SPAWN_KEEP_FDS)) { + int i, max_fd; + + /* Close all other file descriptors. This ensures that we don't + * hold open (eg) pipes from the parent process. + */ + max_fd = sysconf (_SC_OPEN_MAX); + if (max_fd == -1) + max_fd = 1024; + if (max_fd > 65536) + max_fd = 65536; /* bound the amount of work we do here */ + for (i = 3; i < max_fd; ++i) + close (i); + } /* Run the subprocess. */ execvp (file, argv); @@ -413,3 +424,9 @@ mexp_printf (mexp_h *h, const char *fs, ...) free (msg); return len; } + +int +mexp_send_interrupt (mexp_h *h) +{ + return write (h->fd, "\003", 1); +} diff --git a/miniexpect.h b/miniexpect.h index 192d180..e4d6010 100644 --- a/miniexpect.h +++ b/miniexpect.h @@ -64,8 +64,15 @@ typedef struct mexp_h mexp_h; #define mexp_get_pcre_error(h) ((h)->pcre_error) /* Spawn a subprocess. */ -extern mexp_h *mexp_spawnv (const char *file, char **argv); -extern mexp_h *mexp_spawnl (const char *file, const char *arg, ...); +extern mexp_h *mexp_spawnvf (unsigned flags, const char *file, char **argv); +extern mexp_h *mexp_spawnlf (unsigned flags, const char *file, const char *arg, ...); +#define mexp_spawnv(file,argv) mexp_spawnvf (0, (file), (argv)) +#define mexp_spawnl(file,...) mexp_spawnlf (0, (file), __VA_ARGS__) + +#define MEXP_SPAWN_KEEP_SIGNALS 1 +#define MEXP_SPAWN_KEEP_FDS 2 +#define MEXP_SPAWN_COOKED_MODE 4 +#define MEXP_SPAWN_RAW_MODE 0 /* Close the handle. */ extern int mexp_close (mexp_h *h); @@ -89,7 +96,9 @@ enum mexp_status { extern int mexp_expect (mexp_h *h, const mexp_regexp *regexps, int *ovector, int ovecsize); +/* Sending commands, keypresses. */ extern int mexp_printf (mexp_h *h, const char *fs, ...) __attribute__((format(printf,2,3))); +extern int mexp_send_interrupt (mexp_h *h); #endif /* MINIEXPECT_H_ */ diff --git a/miniexpect.pod b/miniexpect.pod index 2b05cd6..9fe235e 100644 --- a/miniexpect.pod +++ b/miniexpect.pod @@ -54,7 +54,7 @@ You can control multiple programs at the same time. =head1 SPAWNING THE SUBPROCESS -There are two calls for creating a subprocess: +There are four calls for creating a subprocess: B @@ -83,6 +83,31 @@ B This is the same as C except that you pass the arguments in a NULL-terminated array. +There are also two versions of the above calls which take flags: + +B + +B + +The flags may contain the following values, logically ORed together: + +=over 4 + +=item C + +Do not reset signal handlers to C in the subprocess. + +=item B + +Do not close file descriptors E 3 in the subprocess. + +=item B or B + +Configure the pty in cooked mode or raw mode. Raw mode is the +default. + +=back + =head1 HANDLES After spawning a subprocess, you get back a handle which is a pointer @@ -411,6 +436,17 @@ send a command followed by a newline you have to do something like: =back +B + +Send the interrupt character (C<^C>, Ctrl-C, C<\003>). This is like +pressing C<^C> - the subprocess (or remote process, if using C) +is gracefully killed. + +Note this only works if the pty is in cooked mode +(ie. C was passed to C or +C). In raw mode, all characters are passed through +without any special interpretation. + =head1 SOURCE Source is available from: -- 1.8.3.1