3 /* --------------- Moved here from job.c ---------------
4 This file must be #included in job.c, as it accesses static functions.
7 static int vms_jobsefnmask
= 0;
9 /* Wait for nchildren children to terminate */
11 vmsWaitForChildren(int *status
)
21 *status
= sys$
wflor (32, vms_jobsefnmask
);
26 /* Set up IO redirection. */
29 vms_redirect (struct dsc$descriptor_s
*desc
, char *fname
, char *ibuf
)
34 while (isspace ((unsigned char)*ibuf
))
37 while (*ibuf
&& !isspace ((unsigned char)*ibuf
))
40 if (strcmp (fptr
, "/dev/null") != 0)
42 strcpy (fname
, vmsify (fptr
, 0));
43 if (strchr (fname
, '.') == 0)
46 desc
->dsc$w_length
= strlen(fname
);
47 desc
->dsc$a_pointer
= fname
;
48 desc
->dsc$b_dtype
= DSC$K_DTYPE_T
;
49 desc
->dsc$b_class
= DSC$K_CLASS_S
;
52 printf (_("Warning: Empty redirection\n"));
57 /* found apostrophe at (p-1)
58 inc p until after closing apostrophe.
62 vms_handle_apos (char *p
)
66 #define SEPCHARS ",/()= "
82 if (strchr (SEPCHARS
, *p
))
97 /* This is called as an AST when a child process dies (it won't get
98 interrupted by anything except a higher level AST).
101 vmsHandleChildTerm(struct child
*child
)
104 register struct child
*lastc
, *c
;
107 vms_jobsefnmask
&= ~(1 << (child
->efn
- 32));
109 lib$
free_ef(&child
->efn
);
111 (void) sigblock (fatal_signal_mask
);
113 child_failed
= !(child
->cstatus
& 1 || ((child
->cstatus
& 7) == 0));
115 /* Search for a child matching the deceased one. */
117 #if defined(RECURSIVEJOBS) /* I've had problems with recursive stuff and process handling */
118 for (c
= children
; c
!= 0 && c
!= child
; lastc
= c
, c
= c
->next
)
124 if (child_failed
&& !c
->noerror
&& !ignore_errors_flag
)
126 /* The commands failed. Write an error message,
127 delete non-precious targets, and abort. */
128 child_error (c
->file
->name
, c
->cstatus
, 0, 0, 0);
129 c
->file
->update_status
= 1;
130 delete_child_targets (c
);
136 /* The commands failed, but we don't care. */
137 child_error (c
->file
->name
, c
->cstatus
, 0, 0, 1);
141 #if defined(RECURSIVEJOBS) /* I've had problems with recursive stuff and process handling */
142 /* If there are more commands to run, try to start them. */
145 switch (c
->file
->command_state
)
148 /* Successfully started. */
152 if (c
->file
->update_status
!= 0) {
153 /* We failed to start the commands. */
154 delete_child_targets (c
);
159 error (NILF
, _("internal error: `%s' command_state"),
164 #endif /* RECURSIVEJOBS */
167 /* Set the state flag to say the commands have finished. */
168 c
->file
->command_state
= cs_finished
;
169 notice_finished_file (c
->file
);
171 #if defined(RECURSIVEJOBS) /* I've had problems with recursive stuff and process handling */
172 /* Remove the child from the chain and free it. */
176 lastc
->next
= c
->next
;
178 #endif /* RECURSIVEJOBS */
180 /* There is now another slot open. */
181 if (job_slots_used
> 0)
184 /* If the job failed, and the -k flag was not given, die. */
185 if (child_failed
&& !keep_going_flag
)
188 (void) sigsetmask (sigblock (0) & ~(fatal_signal_mask
));
194 Spawn a process executing the command in ARGV and return its pid. */
196 #define MAXCMDLEN 200
198 /* local helpers to make ctrl+c and ctrl+y working, see below */
199 #include <libclidef.h>
202 static int ctrlMask
= LIB$M_CLI_CTRLY
;
203 static int oldCtrlMask
;
204 static int setupYAstTried
= 0;
205 static int pidToAbort
= 0;
211 lib$
enable_ctrl (&oldCtrlMask
,0);
218 sys$
forcex (&pidToAbort
, 0, SS$_ABORT
);
221 kill (getpid(),SIGQUIT
);
227 $
DESCRIPTOR(inputDsc
,"SYS$COMMAND");
230 short int status
, count
;
237 status
= sys$
assign(&inputDsc
,&chan
,0,0);
238 if (!(status
&SS$_NORMAL
)) {
243 status
= sys$
qiow (0, chan
, IO$_SETMODE
|IO$M_CTRLYAST
,&iosb
,0,0,
244 astHandler
,0,0,0,0,0);
245 if (status
==SS$_NORMAL
)
247 if (status
==SS$_ILLIOFUNC
|| status
==SS$_NOPRIV
) {
249 #ifdef CTRLY_ENABLED_ANYWAY
251 _("-warning, CTRL-Y will leave sub-process(es) around.\n"));
256 else if (!(status
&SS$_NORMAL
)) {
262 /* called from AST handler ? */
263 if (setupYAstTried
>1)
265 if (atexit(reEnableAst
))
267 _("-warning, you may have to re-enable CTRL-Y handling from DCL.\n"));
268 status
= lib$
disable_ctrl (&ctrlMask
, &oldCtrlMask
);
269 if (!(status
&SS$_NORMAL
)) {
276 child_execute_job (char *argv
, struct child
*child
)
279 static struct dsc$descriptor_s cmddsc
;
280 static struct dsc$descriptor_s pnamedsc
;
281 static struct dsc$descriptor_s ifiledsc
;
282 static struct dsc$descriptor_s ofiledsc
;
283 static struct dsc$descriptor_s efiledsc
;
284 int have_redirection
= 0;
285 int have_newline
= 0;
287 int spflags
= CLI$M_NOWAIT
;
289 char *cmd
= alloca (strlen (argv
) + 512), *p
, *q
;
290 char ifile
[256], ofile
[256], efile
[256];
295 /* Parse IO redirection. */
301 DB (DB_JOBS
, ("child_execute_job (%s)\n", argv
));
303 while (isspace ((unsigned char)*argv
))
309 sprintf (procname
, "GMAKE_%05x", getpid () & 0xfffff);
310 pnamedsc
.dsc$w_length
= strlen(procname
);
311 pnamedsc
.dsc$a_pointer
= procname
;
312 pnamedsc
.dsc$b_dtype
= DSC$K_DTYPE_T
;
313 pnamedsc
.dsc$b_class
= DSC$K_CLASS_S
;
316 /* Handle comments and redirection. */
317 for (p
= argv
, q
= cmd
; *p
; p
++, q
++)
320 in_string
= !in_string
;
336 if (isspace ((unsigned char)*p
))
338 do { p
++; } while (isspace ((unsigned char)*p
));
344 p
= vms_redirect (&ifiledsc
, ifile
, p
);
346 have_redirection
= 1;
349 have_redirection
= 1;
353 if (strncmp (p
, ">&1", 3) == 0)
356 strcpy (efile
, "sys$output");
357 efiledsc
.dsc$w_length
= strlen(efile
);
358 efiledsc
.dsc$a_pointer
= efile
;
359 efiledsc
.dsc$b_dtype
= DSC$K_DTYPE_T
;
360 efiledsc
.dsc$b_class
= DSC$K_CLASS_S
;
364 p
= vms_redirect (&efiledsc
, efile
, p
);
369 p
= vms_redirect (&ofiledsc
, ofile
, p
);
381 while (isspace ((unsigned char)*--q
))
384 if (strncmp (cmd
, "builtin_", 8) == 0)
390 DB (DB_JOBS
, (_("BUILTIN [%s][%s]\n"), cmd
, cmd
+8));
396 && ((*(p
+2) == ' ') || (*(p
+2) == '\t')))
399 while ((*p
== ' ') || (*p
== '\t'))
401 DB (DB_JOBS
, (_("BUILTIN CD %s\n"), p
));
407 else if ((*(p
) == 'r')
409 && ((*(p
+2) == ' ') || (*(p
+2) == '\t')))
415 while ((*p
== ' ') || (*p
== '\t'))
419 DB (DB_JOBS
, (_("BUILTIN RM %s\n"), p
));
440 printf(_("Unknown builtin command '%s'\n"), cmd
);
446 /* Create a *.com file if either the command is too long for
447 lib$spawn, or the command contains a newline, or if redirection
448 is desired. Forcing commands with newlines into DCLs allows to
449 store search lists on user mode logicals. */
451 if (strlen (cmd
) > MAXCMDLEN
452 || (have_redirection
!= 0)
453 || (have_newline
!= 0))
458 int alevel
= 0; /* apostrophe level */
460 if (strlen (cmd
) == 0)
462 printf (_("Error, empty command\n"));
467 outfile
= open_tmpfile (&comname
, "sys$scratch:CMDXXXXXX.COM");
469 pfatal_with_name (_("fopen (temporary file)"));
473 fprintf (outfile
, "$ assign/user %s sys$input\n", ifile
);
474 DB (DB_JOBS
, (_("Redirected input from %s\n"), ifile
));
475 ifiledsc
.dsc$w_length
= 0;
480 fprintf (outfile
, "$ define sys$error %s\n", efile
);
481 DB (DB_JOBS
, (_("Redirected error to %s\n"), efile
));
482 efiledsc
.dsc$w_length
= 0;
487 fprintf (outfile
, "$ define sys$output %s\n", ofile
);
488 DB (DB_JOBS
, (_("Redirected output to %s\n"), ofile
));
489 ofiledsc
.dsc$w_length
= 0;
493 for (c
= '\n'; c
; c
= *q
++)
498 /* At a newline, skip any whitespace around a leading $
499 from the command and issue exactly one $ into the DCL. */
500 while (isspace ((unsigned char)*p
))
504 while (isspace ((unsigned char)*p
))
506 fwrite (p
, 1, q
- p
, outfile
);
507 fputc ('$', outfile
);
508 fputc (' ', outfile
);
509 /* Reset variables. */
513 /* Nice places for line breaks are after strings, after
514 comma or space and before slash. */
516 q
= vms_handle_apos (q
);
532 /* Enough stuff for a line. */
533 fwrite (p
, 1, sep
- p
, outfile
);
537 /* The command continues. */
538 fputc ('-', outfile
);
540 fputc ('\n', outfile
);
544 fwrite (p
, 1, q
- p
, outfile
);
545 fputc ('\n', outfile
);
549 sprintf (cmd
, "$ @%s", comname
);
551 DB (DB_JOBS
, (_("Executing %s instead\n"), cmd
));
554 cmddsc
.dsc$w_length
= strlen(cmd
);
555 cmddsc
.dsc$a_pointer
= cmd
;
556 cmddsc
.dsc$b_dtype
= DSC$K_DTYPE_T
;
557 cmddsc
.dsc$b_class
= DSC$K_CLASS_S
;
560 while (child
->efn
< 32 || child
->efn
> 63)
562 status
= lib$
get_ef ((unsigned long *)&child
->efn
);
567 sys$
clref (child
->efn
);
569 vms_jobsefnmask
|= (1 << (child
->efn
- 32));
572 LIB$SPAWN [command-string]
577 [,process-id] [,completion-status-address] [,byte-integer-event-flag-num]
578 [,AST-address] [,varying-AST-argument]
579 [,prompt-string] [,cli] [,table]
582 #ifndef DONTWAITFORCHILD
584 * Code to make ctrl+c and ctrl+y working.
585 * The problem starts with the synchronous case where after lib$spawn is
586 * called any input will go to the child. But with input re-directed,
587 * both control characters won't make it to any of the programs, neither
588 * the spawning nor to the spawned one. Hence the caller needs to spawn
589 * with CLI$M_NOWAIT to NOT give up the input focus. A sys$waitfr
590 * has to follow to simulate the wanted synchronous behaviour.
591 * The next problem is ctrl+y which isn't caught by the crtl and
592 * therefore isn't converted to SIGQUIT (for a signal handler which is
593 * already established). The only way to catch ctrl+y, is an AST
594 * assigned to the input channel. But ctrl+y handling of DCL needs to be
595 * disabled, otherwise it will handle it. Not to mention the previous
596 * ctrl+y handling of DCL needs to be re-established before make exits.
597 * One more: At the time of LIB$SPAWN signals are blocked. SIGQUIT will
598 * make it to the signal handler after the child "normally" terminates.
599 * This isn't enough. It seems reasonable for simple command lines like
600 * a 'cc foobar.c' spawned in a subprocess but it is unacceptable for
601 * spawning make. Therefore we need to abort the process in the AST.
603 * Prior to the spawn it is checked if an AST is already set up for
604 * ctrl+y, if not one is set up for a channel to SYS$COMMAND. In general
605 * this will work except if make is run in a batch environment, but there
606 * nobody can press ctrl+y. During the setup the DCL handling of ctrl+y
607 * is disabled and an exit handler is established to re-enable it.
608 * If the user interrupts with ctrl+y, the assigned AST will fire, force
609 * an abort to the subprocess and signal SIGQUIT, which will be caught by
610 * the already established handler and will bring us back to common code.
611 * After the spawn (now /nowait) a sys$waitfr simulates the /wait and
612 * enables the ctrl+y be delivered to this code. And the ctrl+c too,
613 * which the crtl converts to SIGINT and which is caught by the common
614 * signal handler. Because signals were blocked before entering this code
615 * sys$waitfr will always complete and the SIGQUIT will be processed after
616 * it (after termination of the current block, somewhere in common code).
617 * And SIGINT too will be delayed. That is ctrl+c can only abort when the
618 * current command completes. Anyway it's better than nothing :-)
623 status
= lib$
spawn (&cmddsc
, /* cmd-string */
624 (ifiledsc
.dsc$w_length
== 0)?0:&ifiledsc
, /* input-file */
625 (ofiledsc
.dsc$w_length
== 0)?0:&ofiledsc
, /* output-file */
626 &spflags
, /* flags */
627 &pnamedsc
, /* proc name */
628 &child
->pid
, &child
->cstatus
, &child
->efn
,
633 pidToAbort
= child
->pid
;
634 status
= sys$
waitfr (child
->efn
);
636 vmsHandleChildTerm(child
);
639 status
= lib$
spawn (&cmddsc
,
640 (ifiledsc
.dsc$w_length
== 0)?0:&ifiledsc
,
641 (ofiledsc
.dsc$w_length
== 0)?0:&ofiledsc
,
644 &child
->pid
, &child
->cstatus
, &child
->efn
,
645 vmsHandleChildTerm
, child
,
651 printf (_("Error spawning, %d\n") ,status
);
663 if (comname
&& !ISDB (DB_JOBS
))