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