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