1 /****************************************************************************
2  *                                                                          *
3  *                         GNAT RUN-TIME COMPONENTS                         *
4  *                                                                          *
5  *                               E X P E C T                                *
6  *                                                                          *
7  *                          C Implementation File                           *
8  *                                                                          *
9  *                     Copyright (C) 2001-2020, AdaCore                     *
10  *                                                                          *
11  * GNAT is free software;  you can  redistribute it  and/or modify it under *
12  * terms of the  GNU General Public License as published  by the Free Soft- *
13  * ware  Foundation;  either version 3,  or (at your option) any later ver- *
14  * sion.  GNAT is distributed in the hope that it will be useful, but WITH- *
15  * OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY *
16  * or FITNESS FOR A PARTICULAR PURPOSE.                                     *
17  *                                                                          *
18  * As a special exception under Section 7 of GPL version 3, you are granted *
19  * additional permissions described in the GCC Runtime Library Exception,   *
20  * version 3.1, as published by the Free Software Foundation.               *
21  *                                                                          *
22  * You should have received a copy of the GNU General Public License and    *
23  * a copy of the GCC Runtime Library Exception along with this program;     *
24  * see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    *
25  * <http://www.gnu.org/licenses/>.                                          *
26  *                                                                          *
27  * GNAT was originally developed  by the GNAT team at  New York University. *
28  * Extensive contributions were provided by Ada Core Technologies Inc.      *
29  *                                                                          *
30  ****************************************************************************/
31 
32 #ifdef IN_RTS
33 #define POSIX
34 #include "runtime.h"
35 #include <unistd.h>
36 
37 #else
38 #include "config.h"
39 #include "system.h"
40 #endif
41 
42 #include "adaint.h"
43 #include <sys/types.h>
44 
45 #ifdef __MINGW32__
46 # if OLD_MINGW
47 #  include <sys/wait.h>
48 # endif
49 #elif defined (__vxworks) && defined (__RTP__)
50 # include <wait.h>
51 #elif defined (__Lynx__)
52   /* ??? See comment in adaint.c.  */
53 # define GCC_RESOURCE_H
54 # include <sys/wait.h>
55 #elif defined (__PikeOS__)
56   /* No wait.h available */
57 #else
58 #include <sys/wait.h>
59 #endif
60 
61 /* This file provides the low level functionalities needed to implement Expect
62    capabilities in GNAT.Expect.
63    Implementations for unix and windows systems is provided.
64    Dummy stubs are also provided for other systems. */
65 
66 #ifdef _AIX
67 /* Work around the fact that gcc/cpp does not define "__unix__" under AiX.  */
68 #define __unix__
69 #endif
70 
71 #ifdef __APPLE__
72 /* Work around the fact that gcc/cpp does not define "__unix__" on Darwin.  */
73 #define __unix__
74 #endif
75 
76 #ifdef _WIN32
77 
78 #include <windows.h>
79 #include <process.h>
80 #include <signal.h>
81 #include <io.h>
82 #include "mingw32.h"
83 
84 int
__gnat_waitpid(int pid)85 __gnat_waitpid (int pid)
86 {
87   HANDLE h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
88   DWORD exitcode = 1;
89 
90   if (h != NULL)
91     {
92       (void) WaitForSingleObject (h, INFINITE);
93       GetExitCodeProcess (h, &exitcode);
94       CloseHandle (h);
95     }
96 
97   __gnat_win32_remove_handle (NULL, pid);
98   return (int) exitcode;
99 }
100 
101 int
__gnat_expect_fork(void)102 __gnat_expect_fork (void)
103 {
104   return 0;
105 }
106 
107 void
__gnat_expect_portable_execvp(int * pid,char * cmd ATTRIBUTE_UNUSED,char * argv[])108 __gnat_expect_portable_execvp (int *pid, char *cmd ATTRIBUTE_UNUSED,
109                                char *argv[])
110 {
111   *pid = __gnat_portable_no_block_spawn (argv);
112 }
113 
114 int
__gnat_pipe(int * fd)115 __gnat_pipe (int *fd)
116 {
117   HANDLE read, write;
118 
119   CreatePipe (&read, &write, NULL, 0);
120   fd[0]=_open_osfhandle ((intptr_t)read, 0);
121   fd[1]=_open_osfhandle ((intptr_t)write, 0);
122   return 0;  /* always success */
123 }
124 
125 int
__gnat_expect_poll(int * fd,int num_fd,int timeout,int * dead_process,int * is_set)126 __gnat_expect_poll (int *fd,
127                     int num_fd,
128                     int timeout,
129                     int *dead_process,
130                     int *is_set)
131 {
132 #define MAX_DELAY 100
133 
134   int i, delay, infinite = 0;
135   DWORD avail;
136   HANDLE handles[num_fd];
137 
138   *dead_process = 0;
139 
140   for (i = 0; i < num_fd; i++)
141     is_set[i] = 0;
142 
143   for (i = 0; i < num_fd; i++)
144     handles[i] = (HANDLE) _get_osfhandle (fd [i]);
145 
146   /* Start with small delays, and then increase them, to avoid polling too
147      much when waiting a long time */
148   delay = 5;
149 
150   if (timeout < 0)
151     infinite = 1;
152 
153   while (1)
154     {
155       for (i = 0; i < num_fd; i++)
156         {
157           if (!PeekNamedPipe (handles [i], NULL, 0, NULL, &avail, NULL))
158             {
159               *dead_process = i + 1;
160               return -1;
161             }
162           if (avail > 0)
163             {
164               is_set[i] = 1;
165               return 1;
166             }
167         }
168 
169       if (!infinite && timeout <= 0)
170         return 0;
171 
172       Sleep (delay);
173       timeout -= delay;
174 
175       if (delay < MAX_DELAY)
176         delay += 10;
177     }
178 }
179 
180 #elif defined (VMS)
181 #include <unistd.h>
182 #include <stdio.h>
183 #include <unixio.h>
184 #include <stdlib.h>
185 #include <string.h>
186 #include <vms/descrip.h>
187 #include <stdio.h>
188 #include <vms/stsdef.h>
189 #include <vms/iodef.h>
190 #include <signal.h>
191 
192 int
__gnat_waitpid(int pid)193 __gnat_waitpid (int pid)
194 {
195   int status = 0;
196 
197   waitpid (pid, &status, 0);
198   status = WEXITSTATUS (status);
199 
200   return status;
201 }
202 
203 int
__gnat_pipe(int * fd)204 __gnat_pipe (int *fd)
205 {
206   return pipe (fd);
207 }
208 
209 int
__gnat_expect_fork(void)210 __gnat_expect_fork (void)
211 {
212   return -1;
213 }
214 
215 void
__gnat_expect_portable_execvp(int * pid,char * cmd,char * argv[])216 __gnat_expect_portable_execvp (int *pid, char *cmd, char *argv[])
217 {
218   *pid = (int) getpid ();
219   /* Since cmd is fully qualified, it is incorrect to call execvp */
220   execv (cmd, argv);
221   _exit (1);
222 }
223 
224 int
__gnat_expect_poll(int * fd,int num_fd,int timeout,int * dead_process,int * is_set)225 __gnat_expect_poll (int *fd,
226                     int num_fd,
227                     int timeout,
228                     int *dead_process,
229                     int *is_set)
230 {
231   int i, num, ready = 0;
232   unsigned int status;
233   int mbxchans [num_fd];
234   struct dsc$descriptor_s mbxname;
235   struct io_status_block {
236     short int condition;
237     short int count;
238     int dev;
239   } iosb;
240   char buf [256];
241 
242   *dead_process = 0;
243 
244   for (i = 0; i < num_fd; i++)
245     is_set[i] = 0;
246 
247   for (i = 0; i < num_fd; i++)
248     {
249 
250       /* Get name of the mailbox used in the pipe */
251       getname (fd [i], buf);
252 
253       /* Assign a channel to the mailbox */
254       if (strlen (buf) > 0)
255 	{
256 	  mbxname.dsc$w_length = strlen (buf);
257 	  mbxname.dsc$b_dtype = DSC$K_DTYPE_T;
258 	  mbxname.dsc$b_class = DSC$K_CLASS_S;
259 	  mbxname.dsc$a_pointer = buf;
260 
261 	  status = SYS$ASSIGN (&mbxname, &mbxchans[i], 0, 0, 0);
262 
263 	  if ((status & 1) != 1)
264 	    {
265               ready = -1;
266               *dead_process = i + 1;
267               return ready;
268 	    }
269 	}
270     }
271 
272   num = timeout / 100;
273 
274   while (1)
275     {
276       for (i = 0; i < num_fd; i++)
277 	{
278 	  if (mbxchans[i] > 0)
279 	    {
280 
281 	      /* Peek in the mailbox to see if there's data */
282 	      status = SYS$QIOW
283 		(0, mbxchans[i], IO$_SENSEMODE|IO$M_READERCHECK,
284 		 &iosb, 0, 0, 0, 0, 0, 0, 0, 0);
285 
286 	      if ((status & 1) != 1)
287 		{
288 		  ready = -1;
289 		  goto deassign;
290 		}
291 
292 	      if (iosb.count > 0)
293 		{
294 		  is_set[i] = 1;
295 		  ready = 1;
296 		  goto deassign;
297 		}
298 	    }
299 	}
300 
301       if (timeout > 0 && num == 0)
302 	{
303 	  ready = 0;
304 	  goto deassign;
305 	}
306 
307       usleep (100000);
308       num--;
309     }
310 
311  deassign:
312 
313   /* Deassign channels assigned above */
314   for (i = 0; i < num_fd; i++)
315     {
316       if (mbxchans[i] > 0)
317 	status = SYS$DASSGN (mbxchans[i]);
318     }
319 
320   return ready;
321 }
322 #elif defined (__unix__)
323 
324 #ifdef __hpux__
325 #include <sys/ptyio.h>
326 #endif
327 
328 #include <sys/time.h>
329 
330 #ifndef NO_FD_SET
331 #define SELECT_MASK fd_set
332 #else /* !NO_FD_SET */
333 #ifndef _AIX
334 typedef long fd_mask;
335 #endif /* _AIX */
336 #ifdef _IBMR2
337 #define SELECT_MASK void
338 #else /* !_IBMR2 */
339 #define SELECT_MASK int
340 #endif /* !_IBMR2 */
341 #endif /* !NO_FD_SET */
342 
343 int
__gnat_waitpid(int pid)344 __gnat_waitpid (int pid)
345 {
346   int status = 0;
347 
348   waitpid (pid, &status, 0);
349   status = WEXITSTATUS (status);
350 
351   return status;
352 }
353 
354 int
__gnat_pipe(int * fd)355 __gnat_pipe (int *fd)
356 {
357   return pipe (fd);
358 }
359 
360 int
__gnat_expect_fork(void)361 __gnat_expect_fork (void)
362 {
363   int pid = fork();
364   if (pid == 0) {
365     __gnat_in_child_after_fork = 1;
366   }
367   return pid;
368 }
369 
370 void
__gnat_expect_portable_execvp(int * pid,char * cmd,char * argv[])371 __gnat_expect_portable_execvp (int *pid, char *cmd, char *argv[])
372 {
373   *pid = (int) getpid ();
374   /* Since cmd is fully qualified, it is incorrect to call execvp */
375   execv (cmd, argv);
376   _exit (1);
377 }
378 
379 int
__gnat_expect_poll(int * fd,int num_fd,int timeout,int * dead_process,int * is_set)380 __gnat_expect_poll (int *fd,
381                     int num_fd,
382                     int timeout,
383                     int *dead_process,
384                     int *is_set)
385 {
386   struct timeval tv;
387   SELECT_MASK rset;
388   SELECT_MASK eset;
389 
390   int max_fd = 0;
391   int ready;
392   int i;
393 #ifdef __hpux__
394   int received;
395 #endif
396 
397   *dead_process = 0;
398 
399   tv.tv_sec  = timeout / 1000;
400   tv.tv_usec = (timeout % 1000) * 1000;
401 
402   do {
403     FD_ZERO (&rset);
404     FD_ZERO (&eset);
405 
406     for (i = 0; i < num_fd; i++)
407       {
408         FD_SET (fd[i], &rset);
409         FD_SET (fd[i], &eset);
410 
411         if (fd[i] > max_fd)
412 	  max_fd = fd[i];
413       }
414 
415     ready =
416       select (max_fd + 1, &rset, NULL, &eset, timeout == -1 ? NULL : &tv);
417 
418     if (ready > 0)
419       {
420 #ifdef __hpux__
421 	received = 0;
422 #endif
423 
424         for (i = 0; i < num_fd; i++)
425 	  {
426 	    if (FD_ISSET (fd[i], &rset))
427 	      {
428 		is_set[i] = 1;
429 #ifdef __hpux__
430 		received = 1;
431 #endif
432 	      }
433 	    else
434 	      is_set[i] = 0;
435 	  }
436 
437 #ifdef __hpux__
438         for (i = 0; i < num_fd; i++)
439 	  {
440 	    if (FD_ISSET (fd[i], &eset))
441 	      {
442 	        struct request_info ei;
443 
444 	        /* Only query and reset error state if no file descriptor
445 	           is ready to be read, otherwise we will be signalling a
446 	           died process too early */
447 
448 	        if (!received)
449 		  {
450 	            ioctl (fd[i], TIOCREQCHECK, &ei);
451 
452 	            if (ei.request == TIOCCLOSE)
453 		      {
454 		        ioctl (fd[i], TIOCREQSET, &ei);
455                         *dead_process = i + 1;
456 		        return -1;
457 		      }
458 
459 	            ioctl (fd[i], TIOCREQSET, &ei);
460 		  }
461 	        ready--;
462 	      }
463 	  }
464 #endif
465       }
466   } while (timeout == -1 && ready == 0);
467 
468   return ready;
469 }
470 
471 #else
472 
473 int
__gnat_waitpid(int pid ATTRIBUTE_UNUSED,int sig ATTRIBUTE_UNUSED)474 __gnat_waitpid (int pid ATTRIBUTE_UNUSED, int sig ATTRIBUTE_UNUSED)
475 {
476   return 0;
477 }
478 
479 int
__gnat_pipe(int * fd ATTRIBUTE_UNUSED)480 __gnat_pipe (int *fd ATTRIBUTE_UNUSED)
481 {
482   return -1;
483 }
484 
485 int
__gnat_expect_fork(void)486 __gnat_expect_fork (void)
487 {
488   return -1;
489 }
490 
491 void
__gnat_expect_portable_execvp(int * pid ATTRIBUTE_UNUSED,char * cmd ATTRIBUTE_UNUSED,char * argv[]ATTRIBUTE_UNUSED)492 __gnat_expect_portable_execvp (int *pid ATTRIBUTE_UNUSED,
493 			       char *cmd ATTRIBUTE_UNUSED,
494 			       char *argv[] ATTRIBUTE_UNUSED)
495 {
496   *pid = 0;
497 }
498 
499 int
__gnat_expect_poll(int * fd ATTRIBUTE_UNUSED,int num_fd ATTRIBUTE_UNUSED,int timeout ATTRIBUTE_UNUSED,int * dead_process ATTRIBUTE_UNUSED,int * is_set ATTRIBUTE_UNUSED)500 __gnat_expect_poll (int *fd ATTRIBUTE_UNUSED,
501                     int num_fd ATTRIBUTE_UNUSED,
502                     int timeout ATTRIBUTE_UNUSED,
503                     int *dead_process ATTRIBUTE_UNUSED,
504                     int *is_set ATTRIBUTE_UNUSED)
505 {
506   *dead_process = 0;
507   return -1;
508 }
509 #endif
510