111
|
1 /****************************************************************************
|
|
2 * *
|
|
3 * GNAT RUN-TIME COMPONENTS *
|
|
4 * *
|
|
5 * E X P E C T *
|
|
6 * *
|
|
7 * C Implementation File *
|
|
8 * *
|
131
|
9 * Copyright (C) 2001-2018, AdaCore *
|
111
|
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
|
|
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
|
|
105 __gnat_expect_fork (void)
|
|
106 {
|
|
107 return 0;
|
|
108 }
|
|
109
|
|
110 void
|
|
111 __gnat_expect_portable_execvp (int *pid, char *cmd, char *argv[])
|
|
112 {
|
|
113 *pid = __gnat_portable_no_block_spawn (argv);
|
|
114 }
|
|
115
|
|
116 int
|
|
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
|
|
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
|
|
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
|
|
206 __gnat_pipe (int *fd)
|
|
207 {
|
|
208 return pipe (fd);
|
|
209 }
|
|
210
|
|
211 int
|
|
212 __gnat_expect_fork (void)
|
|
213 {
|
|
214 return -1;
|
|
215 }
|
|
216
|
|
217 void
|
|
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
|
|
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
|
|
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
|
|
357 __gnat_pipe (int *fd)
|
|
358 {
|
|
359 return pipe (fd);
|
|
360 }
|
|
361
|
|
362 int
|
|
363 __gnat_expect_fork (void)
|
|
364 {
|
|
365 return fork ();
|
|
366 }
|
|
367
|
|
368 void
|
|
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
|
|
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
|
|
472 __gnat_waitpid (int pid ATTRIBUTE_UNUSED, int sig ATTRIBUTE_UNUSED)
|
|
473 {
|
|
474 return 0;
|
|
475 }
|
|
476
|
|
477 int
|
|
478 __gnat_pipe (int *fd ATTRIBUTE_UNUSED)
|
|
479 {
|
|
480 return -1;
|
|
481 }
|
|
482
|
|
483 int
|
|
484 __gnat_expect_fork (void)
|
|
485 {
|
|
486 return -1;
|
|
487 }
|
|
488
|
|
489 void
|
|
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
|
|
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
|