* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+/* ** NOTE ** All API documentation is in the manual page.
+ *
+ * To read the manual page from the source directory, do:
+ * man ./miniexpect.3
+ * If you have installed miniexpect, do:
+ * man 3 miniexpect
+ *
+ * The source for the manual page is miniexpect.pod.
+ */
+
#ifndef MINIEXPECT_H_
#define MINIEXPECT_H_
/* This handle is created per subprocess that is spawned. */
struct mexp_h {
- int fd; /* File descriptor pointing to pty. */
- pid_t pid; /* Subprocess PID. */
-
- /* Timeout (milliseconds, 1/1000th seconds). The caller may set
- * this before calling mexp_expect. Set it to -1 to mean no
- * timeout. The default is 60000 (= 60 seconds).
- */
+ int fd;
+ pid_t pid;
int timeout;
-
- /* The read buffer is allocated by the library when mexp_expect is
- * called. It is available so you can examine the buffer to see
- * what part of the regexp matched. Note this buffer does not
- * contain the full input from the process, but it will contain at
- * least the part matched by the regular expression (and maybe some
- * more).
- */
- char *buffer; /* Read buffer. */
- size_t len; /* Length of data in the buffer. */
- size_t alloc; /* Allocated size of the buffer. */
-
- /* The caller may set this to set the size (in bytes) for reads from
- * the subprocess. The default is 1024.
- */
+ char *buffer;
+ size_t len;
+ size_t alloc;
size_t read_size;
-
- /* If mexp_expect returns MEXP_PCRE_ERROR, then the actual PCRE
- * error code is returned here. See pcre_exec(3) for details.
- */
int pcre_error;
-
- /* Opaque pointers for use of the caller. The library will not
- * touch these.
- */
void *user1;
void *user2;
void *user3;
};
typedef struct mexp_h mexp_h;
-/* Spawn a subprocess.
- *
- * If successful it returns a handle. If it fails, it returns NULL
- * and sets errno.
- */
+/* Spawn a subprocess. */
extern mexp_h *mexp_spawnv (const char *file, char **argv);
-
-/* Same as mexp_spawnv, but it uses a NULL-terminated variable length
- * list of arguments.
- */
extern mexp_h *mexp_spawnl (const char *file, const char *arg, ...);
-/* Close the handle and clean up the subprocess.
- *
- * This returns:
- * 0: successful close, subprocess exited cleanly.
- * -1: error in system call, see errno.
- * > 0: exit status of subprocess if it didn't exit cleanly. Use
- * WIFEXITED, WEXITSTATUS, WIFSIGNALED, WTERMSIG etc macros to
- * examine this.
- *
- * Notes:
- *
- * - Even in the error cases, the handle is always closed and
- * freed by this call.
- *
- * - It is normal for the kernel to send SIGHUP to the subprocess.
- * If the subprocess doesn't catch the SIGHUP, then it will die
- * (WIFSIGNALED (status) && WTERMSIG (status) == SIGHUP). This
- * case should not necessarily be considered an error.
- */
+/* Close the handle. */
extern int mexp_close (mexp_h *h);
-/* The list of regular expressions passed to mexp_expect. */
+/* Expect. */
struct mexp_regexp {
- int r; /* The returned value from mexp_expect
- * if this regular expression matches.
- * MUST be > 0.
- */
- const pcre *re; /* The compiled regular expression. */
- const pcre_extra *extra; /* See pcre_exec. */
- int options; /* See pcre_exec. */
+ int r;
+ const pcre *re;
+ const pcre_extra *extra;
+ int options;
};
typedef struct mexp_regexp mexp_regexp;
MEXP_PCRE_ERROR = -3,
};
-/* Expect some output from the subprocess. Match the output against
- * the PCRE regular expression(s) in the list, and return which one
- * matched.
- *
- * See example-sshpass.c for an example of how to pass in regular
- * expressions.
- *
- * This can return:
- *
- * MEXP_TIMEOUT:
- * No input matched before the timeout (mexp_h->timeout) was reached.
- * MEXP_EOF:
- * The subprocess closed the connection.
- * MEXP_ERROR:
- * There was a system call error (eg. from the read call). See errno.
- * MEXP_PCRE_ERROR
- * There was a pcre_exec error. h->pcre_error is set to the error code
- * (see pcreapi(3) for a list of PCRE_* error codes and what they mean).
- *
- * Notes:
- *
- * - 'regexps' may be NULL or an empty list, which means we don't
- * match against a regular expression. This is useful if you just
- * want to wait for EOF or timeout.
- *
- * - 'regexps[].re', 'regexps[].extra', 'regexps[].options', 'ovector'
- * and 'ovecsize' are passed through to the pcre_exec function.
- */
extern int mexp_expect (mexp_h *h, const mexp_regexp *regexps,
int *ovector, int ovecsize);
-/* This is a convenience function for writing something (eg. a
- * password or command) to the subprocess. You could do this by
- * writing directly to 'h->fd', but this function does all the error
- * checking for you.
- *
- * Returns the number of bytes if the whole message was written OK
- * (partial writes are not possible with this function), or -1 if
- * there was an error (check errno).
- */
extern int mexp_printf (mexp_h *h, const char *fs, ...)
__attribute__((format(printf,2,3)));
--- /dev/null
+=encoding utf8
+
+=head1 NAME
+
+miniexpect - A very simple expect library for C.
+
+=head1 SYNOPSIS
+
+ #include <errno.h>
+ #include <sys/wait.h>
+ #include <pcre.h>
+ #include <miniexpect.h>
+
+ mexp_h *h;
+ h = mexp_spawnl ("ssh", "ssh", "host");
+ switch (mexp_expect (h, regexps, ovector, ovecsize)) {
+ ...
+ }
+ mexp_close (h);
+
+ cc prog.c -o prog -lminiexpect
+
+=head1 DESCRIPTION
+
+miniexpect is a very simple expect-like library for C.
+
+It has a saner interface than libexpect, and doesn't depend on Tcl.
+It is also thread safe, const-correct and uses modern C standards.
+
+It is standalone, except that it requires the PCRE (Perl Compatible
+Regular Expressions) library from http://www.pcre.org/. The PCRE
+dependency is fundamental because we want to offer the most powerful
+regular expression syntax to match on, but more importantly because
+PCRE has a convenient way to detect partial matches which made this
+library very simple to implement.
+
+This manual page documents the API. Examples of how to use the API
+can be found in the source directory.
+
+=head1 CONCEPTS
+
+Miniexpect lets you start up an external program, control it (by
+sending commands to it), and close it down gracefully. Two things
+make this different from other APIs like L<popen(3)> and L<system(3)>:
+Firstly miniexpect creates a pseudoterminal (pty). Secondly
+miniexpect lets you match the output of the program using regular
+expressions. Both of these are handy for controlling interactive
+programs that might (for example) ask for passwords, but you can use
+miniexpect on just about any external program.
+
+You can control multiple programs at the same time.
+
+=head1 SPAWNING THE SUBPROCESS
+
+There are two calls for creating a subprocess:
+
+B<mexp_h *mexp_spawnl (const char *file, const char *arg, ...);>
+
+This creates a subprocess running the external program C<file> (the
+current C<$PATH> is searched unless you give an absolute path).
+C<arg, ...> are the arguments to the program. Usually the first
+argument should be the name of the program.
+
+The return value is a handle (see next section).
+
+If there was an error running the subprocess, C<NULL> is returned and
+the error is available in C<errno>.
+
+For example, to run an ssh subprocess you could do:
+
+ h = mexp_spawnl ("ssh", "ssh", "-l", "root", "host");
+
+or to run a particular ssh binary:
+
+ h = mexp_spawnl ("/usr/local/bin/ssh", "ssh", "-l", "root", "host");
+
+An alternative to C<mexp_spawnl> is:
+
+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.
+
+=head1 HANDLES
+
+After spawning a subprocess, you get back a handle. There are various
+fields in this handle which you can read or write:
+
+ struct mexp_h {
+ int fd;
+ pid_t pid;
+
+C<fd> is the pty of the subprocess. You can read and write to this if
+you want, although convenience functions are also provided (see
+below). C<pid> is the process ID of the subprocess. You can send it
+signals if you want.
+
+ int timeout;
+
+C<timeout> is the timeout in milliseconds (1/1000th of a second) used
+by C<mexp_expect> (see below). You can set this before calling
+C<mexp_expect> if you want. Setting it to -1 means no timeout. The
+default setting is 60000 (60 seconds).
+
+ char *buffer;
+ size_t len;
+ size_t alloc;
+
+If C<mexp_expect> returns a match then these variables contain the
+read buffer. Note this buffer does not contain the full input from
+the process, but it will contain at least the part matched by the
+regular expression (and maybe some more). C<buffer> is the read
+buffer and C<len> is the number of bytes of data in the buffer.
+
+ size_t read_size;
+
+Callers may set this to the natural size (in bytes) for reads from the
+subprocess. The default is 1024. Most callers will not need to
+change this.
+
+ int pcre_error;
+
+If C<mexp_expect> returns C<MEXP_PCRE_ERROR>, then the actual PCRE
+error code returned by L<pcre_exec(3)> is available here. For a list
+of PCRE error codes, see L<pcreapi(3)>.
+
+ void *user1;
+ void *user2;
+ void *user3;
+
+Opaque pointers for use by the caller. The library will not touch
+these.
+
+ };
+
+ typedef struct mexp_h mexp_h;
+
+=head1 CLOSING THE HANDLE
+
+To close the handle and clean up the subprocess, call:
+
+B<int mexp_close (mexp_h *h);>
+
+This returns the status code from the subprocess. This is in the form
+of a L<waitpid(2)>/L<system(3)> status so you have to use the macros
+C<WIFEXITED>, C<WEXITSTATUS>, C<WIFSIGNALED>, C<WTERMSIG> etc defined
+in C<E<lt>sys/wait.hE<gt>> to parse it.
+
+If there was a system call error, then C<-1> is returned. The error
+will be in C<errno>.
+
+Notes:
+
+=over 4
+
+=item *
+
+Even in error cases, the handle is always closed and its memory is
+freed by this call.
+
+=item *
+
+It is normal for the kernel to send SIGHUP to the subprocess.
+
+If the subprocess doesn't catch the SIGHUP, then it will die
+with status:
+
+ WIFSIGNALED (status) && WTERMSIG (status) == SIGHUP
+
+This case should not necessarily be considered an error.
+
+=back
+
+=head1 EXPECT FUNCTION
+
+Miniexpect contains a powerful regular expression matching function
+based on L<pcre(3)>:
+
+B<int mexp_expect (mexp_h *h, const mexp_regexp *regexps,
+int *ovector, int ovecsize);>
+
+The output of the subprocess is matched against the list of PCRE
+regular expressions in C<regexps>. C<regexps> is a list of regular
+expression structures:
+
+ struct mexp_regexp {
+ int r;
+ const pcre *re;
+ const pcre_extra *extra;
+ int options;
+ };
+ typedef struct mexp_regexp mexp_regexp;
+
+C<r> is the integer code returned from C<mexp_expect> if this regular
+expression matches. It B<must> be E<gt> 0. C<r == 0> indicates the
+end of the list of regular expressions. C<re> is the compiled regular
+expression.
+
+Possible return values are:
+
+=over 4
+
+=item C<MEXP_TIMEOUT>
+
+No input matched before the timeout (C<h-E<gt>timeout>) was
+reached.
+
+=item C<MEXP_EOF>
+
+The subprocess closed the connection.
+
+=item C<MEXP_ERROR>
+
+There was a system call error (eg. from the read call). The error is
+returned in C<errno>.
+
+=item C<MEXP_PCRE_ERROR>
+
+There was a C<pcre_exec> error. C<h-E<gt>pcre_error> is set to the
+error code. See L<pcreapi(3)> for a list of the C<PCRE_*> error codes
+and what they mean.
+
+=item C<r> E<gt> 0
+
+If any regexp matches, the associated integer code (C<regexps[].r>)
+is returned.
+
+=back
+
+Notes:
+
+=over 4
+
+=item *
+
+C<regexps> may be NULL or an empty list, which means we don't match
+against a regular expression. This is useful if you just want to wait
+for EOF or timeout.
+
+=item *
+
+C<regexps[].re>, C<regexps[].extra>, C<regexps[].options>, C<ovector>
+and C<ovecsize> are passed through to the L<pcre_exec(3)> function.
+
+=back
+
+=head2 mexp_expect example
+
+It is easier to understand C<mexp_expect> by considering a simple
+example.
+
+In this example we are waiting for ssh to either send us a password
+prompt, or (if no password was required) a command prompt, and based
+on the output we will either send back a password or a command.
+
+The unusual C<(mexp_regexp[]){...}> syntax is called a "compound
+literal" and is available in C99. If you need to use an older
+compiler, you can just use a local variable instead.
+
+ mexp_h *h;
+ char *errptr;
+ int offset;
+ pcre *password_re, *prompt_re;
+ int ovecsize = 12;
+ int ovector[ovecsize];
+
+ password_re = pcre_compile ("assword", 0, &errptr, &offset, NULL);
+ prompt_re = pcre_compile ("[$#] ", 0, &errptr, &offset, NULL);
+
+ switch (mexp_expect (h,
+ (mexp_regexp[]) {
+ { 100, .re = password_re },
+ { 101, .re = prompt_re },
+ { 0 },
+ }, ovector, ovecsize)) {
+ case 100:
+ /* here you would send a password */
+ break;
+ case 101:
+ /* here you would send a command */
+ break;
+ case MEXP_EOF:
+ fprintf (stderr, "error: ssh closed the connection unexpectedly\n");
+ exit (EXIT_FAILURE);
+ case MEXP_TIMEOUT:
+ fprintf (stderr, "error: timeout before reaching the prompt\n");
+ exit (EXIT_FAILURE);
+ case MEXP_ERROR:
+ perror ("mexp_expect");
+ exit (EXIT_FAILURE);
+ case MEXP_PCRE_ERROR:
+ fprintf (stderr, "error: PCRE error: %d\n", h->pcre_error);
+ exit (EXIT_FAILURE);
+ }
+
+=head1 SENDING COMMANDS TO THE SUBPROCESS
+
+You can write to the subprocess simply by writing to C<h-E<gt>fd>.
+However we also provide a convenience function:
+
+B<int mexp_printf (mexp_h *h, const char *fs, ...);>
+
+This returns the number of bytes, if the whole message was written OK.
+If there was an error, -1 is returned and the error is available in
+C<errno>. Note that this function will not do a partial write. If it
+cannot write all the data, then it will return an error.
+
+=head1 SOURCE
+
+Source is available from:
+L<http://git.annexia.org/?p=miniexpect.git;a=summary>
+
+=head1 SEE ALSO
+
+L<pcre(3)>,
+L<pcre_exec(3)>,
+L<pcreapi(3)>,
+L<waitpid(2)>,
+L<system(3)>.
+
+=head1 AUTHORS
+
+Richard W.M. Jones (C<rjones at redhat dot com>)
+
+=head1 LICENSE
+
+The library is released under the Library GPL (LGPL) version 2 or at
+your option any later version.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2014 Red Hat Inc.