mexp_expect: Change the expect API to allow multiple regexps.
[miniexpect.git] / miniexpect.c
1 /* miniexpect
2  * Copyright (C) 2014 Red Hat Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <poll.h>
27 #include <errno.h>
28 #include <termios.h>
29 #include <time.h>
30 #include <assert.h>
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 #include <sys/time.h>
34
35 #include <pcre.h>
36
37 #include "miniexpect.h"
38
39 #define DEBUG 0
40
41 static mexp_h *
42 create_handle (void)
43 {
44   mexp_h *h = malloc (sizeof *h);
45   if (h == NULL)
46     return NULL;
47
48   /* Initialize the fields to default values. */
49   h->fd = -1;
50   h->pid = 0;
51   h->timeout = 60000;
52   h->read_size = 1024;
53   h->pcre_error = 0;
54   h->buffer = NULL;
55   h->len = h->alloc = 0;
56   h->user1 = h->user2 = h->user3 = NULL;
57
58   return h;
59 }
60
61 static void
62 clear_buffer (mexp_h *h)
63 {
64   free (h->buffer);
65   h->buffer = NULL;
66   h->alloc = h->len = 0;
67 }
68
69 int
70 mexp_close (mexp_h *h)
71 {
72   int status = 0;
73
74   free (h->buffer);
75
76   if (h->fd >= 0)
77     close (h->fd);
78   if (h->pid > 0) {
79     if (waitpid (h->pid, &status, 0) == -1)
80       return -1;
81   }
82
83   return status;
84 }
85
86 mexp_h *
87 mexp_spawnl (const char *file, const char *arg, ...)
88 {
89   char **argv, **new_argv;
90   size_t i;
91   va_list args;
92   mexp_h *h;
93
94   argv = malloc (sizeof (char *));
95   if (argv == NULL)
96     return NULL;
97   argv[0] = (char *) arg;
98
99   va_start (args, arg);
100   for (i = 1; arg != NULL; ++i) {
101     arg = va_arg (args, const char *);
102     new_argv = realloc (argv, sizeof (char *) * (i+1));
103     if (new_argv == NULL) {
104       free (argv);
105       return NULL;
106     }
107     argv = new_argv;
108     argv[i] = (char *) arg;
109   }
110
111   h = mexp_spawnv (file, argv);
112   free (argv);
113   return h;
114 }
115
116 mexp_h *
117 mexp_spawnv (const char *file, char **argv)
118 {
119   mexp_h *h;
120   int fd = -1;
121   int err;
122   char slave[1024];
123   pid_t pid = 0;
124
125   fd = posix_openpt (O_RDWR|O_NOCTTY);
126   if (fd == -1)
127     goto error;
128
129   if (grantpt (fd) == -1)
130     goto error;
131
132   if (unlockpt (fd) == -1)
133     goto error;
134
135   /* Get the slave pty name now, but don't open it in the parent. */
136   if (ptsname_r (fd, slave, sizeof slave) != 0)
137     goto error;
138
139   /* Create the handle last before we fork. */
140   h = create_handle ();
141   if (h == NULL)
142     goto error;
143
144   pid = fork ();
145   if (pid == -1)
146     goto error;
147
148   if (pid == 0) {               /* Child. */
149     struct termios terminal_settings;
150     int slave_fd;
151
152     setsid ();
153
154     /* Open the slave side of the pty.  We must do this in the child
155      * after setsid so it becomes our controlling tty.
156      */
157     slave_fd = open (slave, O_RDWR);
158     if (slave_fd == -1)
159       goto error;
160
161     /* Set raw mode. */
162     tcgetattr (slave_fd, &terminal_settings);
163     cfmakeraw (&terminal_settings);
164     tcsetattr (slave_fd, TCSANOW, &terminal_settings);
165
166     /* Set up stdin, stdout, stderr to point to the pty. */
167     dup2 (slave_fd, 0);
168     dup2 (slave_fd, 1);
169     dup2 (slave_fd, 2);
170     close (slave_fd);
171
172     /* Close the master side of the pty - do this late to avoid a
173      * kernel bug, see sshpass source code.
174      */
175     close (fd);
176
177     /* Run the subprocess. */
178     execvp (file, argv);
179     perror (file);
180     _exit (EXIT_FAILURE);
181   }
182
183   /* Parent. */
184
185   h->fd = fd;
186   h->pid = pid;
187   return h;
188
189  error:
190   err = errno;
191   if (fd >= 0)
192     close (fd);
193   if (pid > 0)
194     waitpid (pid, NULL, 0);
195   errno = err;
196   return NULL;
197 }
198
199 enum mexp_status
200 mexp_expect (mexp_h *h, const mexp_regexp *regexps, int *ovector, int ovecsize)
201 {
202   time_t start_t, now_t;
203   int timeout;
204   struct pollfd pfds[1];
205   int r;
206   ssize_t rs;
207
208   time (&start_t);
209
210   /* Clear the read buffer. */
211   clear_buffer (h);
212
213   for (;;) {
214     /* If we've got a timeout then work out how many seconds are left.
215      * Timeout == 0 is not particularly well-defined, but it probably
216      * means "return immediately if there's no data to be read".
217      */
218     if (h->timeout >= 0) {
219       time (&now_t);
220       timeout = h->timeout - ((now_t - start_t) * 1000);
221       if (timeout < 0)
222         timeout = 0;
223     }
224     else
225       timeout = 0;
226
227     pfds[0].fd = h->fd;
228     pfds[0].events = POLLIN;
229     pfds[0].revents = 0;
230     r = poll (pfds, 1, timeout);
231 #if DEBUG
232     fprintf (stderr, "DEBUG: poll returned %d\n", r);
233 #endif
234     if (r == -1)
235       return MEXP_ERROR;
236
237     if (r == 0)
238       return MEXP_TIMEOUT;
239
240     /* Otherwise we expect there is something to read from the file
241      * descriptor.
242      */
243     if (h->alloc - h->len <= h->read_size) {
244       char *new_buffer;
245       /* +1 here allows us to store \0 after the data read */
246       new_buffer = realloc (h->buffer, h->alloc + h->read_size + 1);
247       if (new_buffer == NULL)
248         return MEXP_ERROR;
249       h->buffer = new_buffer;
250       h->alloc += h->read_size;
251     }
252     rs = read (h->fd, h->buffer + h->len, h->read_size);
253 #if DEBUG
254     fprintf (stderr, "DEBUG: read returned %zd\n", rs);
255 #endif
256     if (rs == -1) {
257       /* Annoyingly on Linux (I'm fairly sure this is a bug) if the
258        * writer closes the connection, the entire pty is destroyed,
259        * and read returns -1 / EIO.  Handle that special case here.
260        */
261       if (errno == EIO)
262         return MEXP_EOF;
263       return MEXP_ERROR;
264     }
265     if (rs == 0)
266       return MEXP_EOF;
267
268     /* We read something. */
269     h->len += rs;
270     h->buffer[h->len] = '\0';
271 #if DEBUG
272     fprintf (stderr, "DEBUG: read %zd bytes from pty\n", rs);
273     fprintf (stderr, "DEBUG: buffer content: %s\n", h->buffer);
274 #endif
275
276     /* See if there is a full or partial match against any regexp. */
277     if (regexps) {
278       size_t i;
279       int can_clear_buffer = 1;
280
281       assert (h->buffer != NULL);
282
283       for (i = 0; regexps[i].r > 0; ++i) {
284         int options = regexps[i].options | PCRE_PARTIAL_SOFT;
285
286         r = pcre_exec (regexps[i].re, regexps[i].extra,
287                        h->buffer, (int)h->len, 0,
288                        options,
289                        ovector, ovecsize);
290         h->pcre_error = r;
291
292         if (r >= 0) {
293           /* A full match. */
294           return regexps[i].r;
295         }
296
297         else if (r == PCRE_ERROR_NOMATCH) {
298           /* No match at all. */
299           /* (nothing here) */
300         }
301
302         else if (r == PCRE_ERROR_PARTIAL) {
303           /* Partial match.  Keep the buffer and keep reading. */
304           can_clear_buffer = 0;
305         }
306
307         else {
308           /* An actual PCRE error. */
309           return MEXP_PCRE_ERROR;
310         }
311       }
312
313       /* If none of the regular expressions matched (not partially)
314        * then we can clear the buffer.  This is an optimization.
315        */
316       if (can_clear_buffer)
317         clear_buffer (h);
318
319     } /* if (regexps) */
320   }
321 }
322
323 int
324 mexp_printf (mexp_h *h, const char *fs, ...)
325 {
326   va_list args;
327   char *msg;
328   int len;
329   size_t n;
330   ssize_t r;
331   char *p;
332
333   va_start (args, fs);
334   len = vasprintf (&msg, fs, args);
335   va_end (args);
336
337   if (len < 0)
338     return -1;
339
340 #if DEBUG
341   fprintf (stderr, "DEBUG: writing: %s\n", msg);
342 #endif
343
344   n = len;
345   p = msg;
346   while (n > 0) {
347     r = write (h->fd, p, n);
348     if (r == -1) {
349       free (msg);
350       return -1;
351     }
352     n -= r;
353     p += r;
354   }
355
356   free (msg);
357   return len;
358 }