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