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