eca8e9a9280a11a8f9a3dc79f1736d8d3460d017
[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   /* XXX This is possibly incorrect because it throws away inputs that
212    * may not have been matched yet.  A better idea is to record the
213    * end of the previous match and only throw that away.
214    */
215   clear_buffer (h);
216
217   for (;;) {
218     /* If we've got a timeout then work out how many seconds are left.
219      * Timeout == 0 is not particularly well-defined, but it probably
220      * means "return immediately if there's no data to be read".
221      */
222     if (h->timeout >= 0) {
223       time (&now_t);
224       timeout = h->timeout - ((now_t - start_t) * 1000);
225       if (timeout < 0)
226         timeout = 0;
227     }
228     else
229       timeout = 0;
230
231     pfds[0].fd = h->fd;
232     pfds[0].events = POLLIN;
233     pfds[0].revents = 0;
234     r = poll (pfds, 1, timeout);
235 #if DEBUG
236     fprintf (stderr, "DEBUG: poll returned %d\n", r);
237 #endif
238     if (r == -1)
239       return MEXP_ERROR;
240
241     if (r == 0)
242       return MEXP_TIMEOUT;
243
244     /* Otherwise we expect there is something to read from the file
245      * descriptor.
246      */
247     if (h->alloc - h->len <= h->read_size) {
248       char *new_buffer;
249       /* +1 here allows us to store \0 after the data read */
250       new_buffer = realloc (h->buffer, h->alloc + h->read_size + 1);
251       if (new_buffer == NULL)
252         return MEXP_ERROR;
253       h->buffer = new_buffer;
254       h->alloc += h->read_size;
255     }
256     rs = read (h->fd, h->buffer + h->len, h->read_size);
257 #if DEBUG
258     fprintf (stderr, "DEBUG: read returned %zd\n", rs);
259 #endif
260     if (rs == -1) {
261       /* Annoyingly on Linux (I'm fairly sure this is a bug) if the
262        * writer closes the connection, the entire pty is destroyed,
263        * and read returns -1 / EIO.  Handle that special case here.
264        */
265       if (errno == EIO)
266         return MEXP_EOF;
267       return MEXP_ERROR;
268     }
269     if (rs == 0)
270       return MEXP_EOF;
271
272     /* We read something. */
273     h->len += rs;
274     h->buffer[h->len] = '\0';
275 #if DEBUG
276     fprintf (stderr, "DEBUG: read %zd bytes from pty\n", rs);
277     fprintf (stderr, "DEBUG: buffer content: %s\n", h->buffer);
278 #endif
279
280     /* See if there is a full or partial match against any regexp. */
281     if (regexps) {
282       size_t i;
283       int can_clear_buffer = 1;
284
285       assert (h->buffer != NULL);
286
287       for (i = 0; regexps[i].r > 0; ++i) {
288         int options = regexps[i].options | PCRE_PARTIAL_SOFT;
289
290         r = pcre_exec (regexps[i].re, regexps[i].extra,
291                        h->buffer, (int)h->len, 0,
292                        options,
293                        ovector, ovecsize);
294         h->pcre_error = r;
295
296         if (r >= 0) {
297           /* A full match. */
298           return regexps[i].r;
299         }
300
301         else if (r == PCRE_ERROR_NOMATCH) {
302           /* No match at all. */
303           /* (nothing here) */
304         }
305
306         else if (r == PCRE_ERROR_PARTIAL) {
307           /* Partial match.  Keep the buffer and keep reading. */
308           can_clear_buffer = 0;
309         }
310
311         else {
312           /* An actual PCRE error. */
313           return MEXP_PCRE_ERROR;
314         }
315       }
316
317       /* If none of the regular expressions matched (not partially)
318        * then we can clear the buffer.  This is an optimization.
319        */
320       if (can_clear_buffer)
321         clear_buffer (h);
322
323     } /* if (regexps) */
324   }
325 }
326
327 int
328 mexp_printf (mexp_h *h, const char *fs, ...)
329 {
330   va_list args;
331   char *msg;
332   int len;
333   size_t n;
334   ssize_t r;
335   char *p;
336
337   va_start (args, fs);
338   len = vasprintf (&msg, fs, args);
339   va_end (args);
340
341   if (len < 0)
342     return -1;
343
344 #if DEBUG
345   fprintf (stderr, "DEBUG: writing: %s\n", msg);
346 #endif
347
348   n = len;
349   p = msg;
350   while (n > 0) {
351     r = write (h->fd, p, n);
352     if (r == -1) {
353       free (msg);
354       return -1;
355     }
356     n -= r;
357     p += r;
358   }
359
360   free (msg);
361   return len;
362 }