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