Add mexp_send_interrupt, mexp_spawnvf, mexp_spawnlf and various flags.
authorRichard W.M. Jones <rjones@redhat.com>
Wed, 29 Jun 2016 20:18:20 +0000 (21:18 +0100)
committerRichard W.M. Jones <rjones@redhat.com>
Wed, 29 Jun 2016 20:18:20 +0000 (21:18 +0100)
miniexpect.c
miniexpect.h
miniexpect.pod

index ee2b7fa..caf498d 100644 (file)
@@ -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);
+}
index 192d180..e4d6010 100644 (file)
@@ -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_ */
index 2b05cd6..9fe235e 100644 (file)
@@ -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<mexp_h *mexp_spawnl (const char *file, const char *arg, ...);>
 
@@ -83,6 +83,31 @@ B<mexp_h *mexp_spawnv (const char *file, char **argv);>
 This is the same as C<mexp_spawnl> except that you pass the arguments
 in a NULL-terminated array.
 
+There are also two versions of the above calls which take flags:
+
+B<mexp_h *mexp_spawnlf (unsigned flags, const char *file, const char *arg, ...);>
+
+B<mexp_h *mexp_spawnvf (unsigned flags, const char *file, char **argv);>
+
+The flags may contain the following values, logically ORed together:
+
+=over 4
+
+=item C<MEXP_SPAWN_KEEP_SIGNALS>
+
+Do not reset signal handlers to C<SIG_DFL> in the subprocess.
+
+=item B<MEXP_SPAWN_KEEP_FDS>
+
+Do not close file descriptors E<ge> 3 in the subprocess.
+
+=item B<MEXP_SPAWN_COOKED_MODE> or B<MEXP_SPAWN_RAW_MODE>
+
+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<int mexp_send_interrupt (mexp_h *h);>
+
+Send the interrupt character (C<^C>, Ctrl-C, C<\003>).  This is like
+pressing C<^C> - the subprocess (or remote process, if using C<ssh>)
+is gracefully killed.
+
+Note this only works if the pty is in cooked mode
+(ie. C<MEXP_SPAWN_COOKED_MODE> was passed to C<mexp_spawnlf> or
+C<mexp_spawnvf>).  In raw mode, all characters are passed through
+without any special interpretation.
+
 =head1 SOURCE
 
 Source is available from: