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