VirtualBox

source: kBuild/trunk/src/gmake/vmsjobs.c@ 285

Last change on this file since 285 was 282, checked in by bird, 20 years ago

Initial revision

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.6 KB
Line 
1
2
3
4/* --------------- Moved here from job.c ---------------
5 This file must be #included in job.c, as it accesses static functions.
6*/
7
8static int vms_jobsefnmask = 0;
9
10/* Wait for nchildren children to terminate */
11void
12vmsWaitForChildren(int *status)
13{
14 while (1)
15 {
16 if (!vms_jobsefnmask)
17 {
18 *status = 0;
19 return;
20 }
21
22 *status = sys$wflor (32, vms_jobsefnmask);
23 }
24 return;
25}
26
27/* Set up IO redirection. */
28
29char *
30vms_redirect (struct dsc$descriptor_s *desc, char *fname, char *ibuf)
31{
32 char *fptr;
33
34 ibuf++;
35 while (isspace ((unsigned char)*ibuf))
36 ibuf++;
37 fptr = ibuf;
38 while (*ibuf && !isspace ((unsigned char)*ibuf))
39 ibuf++;
40 *ibuf = 0;
41 if (strcmp (fptr, "/dev/null") != 0)
42 {
43 strcpy (fname, vmsify (fptr, 0));
44 if (strchr (fname, '.') == 0)
45 strcat (fname, ".");
46 }
47 desc->dsc$w_length = strlen(fname);
48 desc->dsc$a_pointer = fname;
49 desc->dsc$b_dtype = DSC$K_DTYPE_T;
50 desc->dsc$b_class = DSC$K_CLASS_S;
51
52 if (*fname == 0)
53 printf (_("Warning: Empty redirection\n"));
54 return ibuf;
55}
56
57
58/* found apostrophe at (p-1)
59 inc p until after closing apostrophe.
60 */
61
62char *
63vms_handle_apos (char *p)
64{
65 int alast;
66
67#define SEPCHARS ",/()= "
68
69 alast = 0;
70
71 while (*p != 0)
72 {
73 if (*p == '"')
74 {
75 if (alast)
76 {
77 alast = 0;
78 p++;
79 }
80 else
81 {
82 p++;
83 if (strchr (SEPCHARS, *p))
84 break;
85 alast = 1;
86 }
87 }
88 else
89 p++;
90 }
91
92 return p;
93}
94
95#include <descrip.h>
96#include <clidef.h>
97
98/* This is called as an AST when a child process dies (it won't get
99 interrupted by anything except a higher level AST).
100*/
101int
102vmsHandleChildTerm(struct child *child)
103{
104 int status;
105 register struct child *lastc, *c;
106 int child_failed;
107
108 vms_jobsefnmask &= ~(1 << (child->efn - 32));
109
110 lib$free_ef(&child->efn);
111
112 (void) sigblock (fatal_signal_mask);
113
114 child_failed = !(child->cstatus & 1 || ((child->cstatus & 7) == 0));
115
116 /* Search for a child matching the deceased one. */
117 lastc = 0;
118#if defined(RECURSIVEJOBS) /* I've had problems with recursive stuff and process handling */
119 for (c = children; c != 0 && c != child; lastc = c, c = c->next)
120 ;
121#else
122 c = child;
123#endif
124
125 if (child_failed && !c->noerror && !ignore_errors_flag)
126 {
127 /* The commands failed. Write an error message,
128 delete non-precious targets, and abort. */
129 child_error (c->file->name, c->cstatus, 0, 0, 0);
130 c->file->update_status = 1;
131 delete_child_targets (c);
132 }
133 else
134 {
135 if (child_failed)
136 {
137 /* The commands failed, but we don't care. */
138 child_error (c->file->name, c->cstatus, 0, 0, 1);
139 child_failed = 0;
140 }
141
142#if defined(RECURSIVEJOBS) /* I've had problems with recursive stuff and process handling */
143 /* If there are more commands to run, try to start them. */
144 start_job (c);
145
146 switch (c->file->command_state)
147 {
148 case cs_running:
149 /* Successfully started. */
150 break;
151
152 case cs_finished:
153 if (c->file->update_status != 0) {
154 /* We failed to start the commands. */
155 delete_child_targets (c);
156 }
157 break;
158
159 default:
160 error (NILF, _("internal error: `%s' command_state"),
161 c->file->name);
162 abort ();
163 break;
164 }
165#endif /* RECURSIVEJOBS */
166 }
167
168 /* Set the state flag to say the commands have finished. */
169 c->file->command_state = cs_finished;
170 notice_finished_file (c->file);
171
172#if defined(RECURSIVEJOBS) /* I've had problems with recursive stuff and process handling */
173 /* Remove the child from the chain and free it. */
174 if (lastc == 0)
175 children = c->next;
176 else
177 lastc->next = c->next;
178 free_child (c);
179#endif /* RECURSIVEJOBS */
180
181 /* There is now another slot open. */
182 if (job_slots_used > 0)
183 --job_slots_used;
184
185 /* If the job failed, and the -k flag was not given, die. */
186 if (child_failed && !keep_going_flag)
187 die (EXIT_FAILURE);
188
189 (void) sigsetmask (sigblock (0) & ~(fatal_signal_mask));
190
191 return 1;
192}
193
194/* VMS:
195 Spawn a process executing the command in ARGV and return its pid. */
196
197#define MAXCMDLEN 200
198
199/* local helpers to make ctrl+c and ctrl+y working, see below */
200#include <libclidef.h>
201#include <ssdef.h>
202
203static int ctrlMask= LIB$M_CLI_CTRLY;
204static int oldCtrlMask;
205static int setupYAstTried= 0;
206static int pidToAbort= 0;
207static int chan= 0;
208
209static void
210reEnableAst(void)
211{
212 lib$enable_ctrl (&oldCtrlMask,0);
213}
214
215static void
216astHandler (void)
217{
218 if (pidToAbort) {
219 sys$forcex (&pidToAbort, 0, SS$_ABORT);
220 pidToAbort= 0;
221 }
222 kill (getpid(),SIGQUIT);
223}
224
225static void
226tryToSetupYAst(void)
227{
228 $DESCRIPTOR(inputDsc,"SYS$COMMAND");
229 int status;
230 struct {
231 short int status, count;
232 int dvi;
233 } iosb;
234
235 setupYAstTried++;
236
237 if (!chan) {
238 status= sys$assign(&inputDsc,&chan,0,0);
239 if (!(status&SS$_NORMAL)) {
240 lib$signal(status);
241 return;
242 }
243 }
244 status= sys$qiow (0, chan, IO$_SETMODE|IO$M_CTRLYAST,&iosb,0,0,
245 astHandler,0,0,0,0,0);
246 if (status==SS$_NORMAL)
247 status= iosb.status;
248 if (status==SS$_ILLIOFUNC || status==SS$_NOPRIV) {
249 sys$dassgn(chan);
250#ifdef CTRLY_ENABLED_ANYWAY
251 fprintf (stderr,
252 _("-warning, CTRL-Y will leave sub-process(es) around.\n"));
253#else
254 return;
255#endif
256 }
257 else if (!(status&SS$_NORMAL)) {
258 sys$dassgn(chan);
259 lib$signal(status);
260 return;
261 }
262
263 /* called from AST handler ? */
264 if (setupYAstTried>1)
265 return;
266 if (atexit(reEnableAst))
267 fprintf (stderr,
268 _("-warning, you may have to re-enable CTRL-Y handling from DCL.\n"));
269 status= lib$disable_ctrl (&ctrlMask, &oldCtrlMask);
270 if (!(status&SS$_NORMAL)) {
271 lib$signal(status);
272 return;
273 }
274}
275
276int
277child_execute_job (char *argv, struct child *child)
278{
279 int i;
280 static struct dsc$descriptor_s cmddsc;
281 static struct dsc$descriptor_s pnamedsc;
282 static struct dsc$descriptor_s ifiledsc;
283 static struct dsc$descriptor_s ofiledsc;
284 static struct dsc$descriptor_s efiledsc;
285 int have_redirection = 0;
286 int have_newline = 0;
287
288 int spflags = CLI$M_NOWAIT;
289 int status;
290 char *cmd = alloca (strlen (argv) + 512), *p, *q;
291 char ifile[256], ofile[256], efile[256];
292 char *comname = 0;
293 char procname[100];
294 int in_string;
295
296 /* Parse IO redirection. */
297
298 ifile[0] = 0;
299 ofile[0] = 0;
300 efile[0] = 0;
301
302 DB (DB_JOBS, ("child_execute_job (%s)\n", argv));
303
304 while (isspace ((unsigned char)*argv))
305 argv++;
306
307 if (*argv == 0)
308 return 0;
309
310 sprintf (procname, "GMAKE_%05x", getpid () & 0xfffff);
311 pnamedsc.dsc$w_length = strlen(procname);
312 pnamedsc.dsc$a_pointer = procname;
313 pnamedsc.dsc$b_dtype = DSC$K_DTYPE_T;
314 pnamedsc.dsc$b_class = DSC$K_CLASS_S;
315
316 in_string = 0;
317 /* Handle comments and redirection. */
318 for (p = argv, q = cmd; *p; p++, q++)
319 {
320 if (*p == '"')
321 in_string = !in_string;
322 if (in_string)
323 {
324 *q = *p;
325 continue;
326 }
327 switch (*p)
328 {
329 case '#':
330 *p-- = 0;
331 *q-- = 0;
332 break;
333 case '\\':
334 p++;
335 if (*p == '\n')
336 p++;
337 if (isspace ((unsigned char)*p))
338 {
339 do { p++; } while (isspace ((unsigned char)*p));
340 p--;
341 }
342 *q = *p;
343 break;
344 case '<':
345 p = vms_redirect (&ifiledsc, ifile, p);
346 *q = ' ';
347 have_redirection = 1;
348 break;
349 case '>':
350 have_redirection = 1;
351 if (*(p-1) == '2')
352 {
353 q--;
354 if (strncmp (p, ">&1", 3) == 0)
355 {
356 p += 3;
357 strcpy (efile, "sys$output");
358 efiledsc.dsc$w_length = strlen(efile);
359 efiledsc.dsc$a_pointer = efile;
360 efiledsc.dsc$b_dtype = DSC$K_DTYPE_T;
361 efiledsc.dsc$b_class = DSC$K_CLASS_S;
362 }
363 else
364 {
365 p = vms_redirect (&efiledsc, efile, p);
366 }
367 }
368 else
369 {
370 p = vms_redirect (&ofiledsc, ofile, p);
371 }
372 *q = ' ';
373 break;
374 case '\n':
375 have_newline = 1;
376 default:
377 *q = *p;
378 break;
379 }
380 }
381 *q = *p;
382 while (isspace ((unsigned char)*--q))
383 *q = '\0';
384
385 if (strncmp (cmd, "builtin_", 8) == 0)
386 {
387 child->pid = 270163;
388 child->efn = 0;
389 child->cstatus = 1;
390
391 DB (DB_JOBS, (_("BUILTIN [%s][%s]\n"), cmd, cmd+8));
392
393 p = cmd + 8;
394
395 if ((*(p) == 'c')
396 && (*(p+1) == 'd')
397 && ((*(p+2) == ' ') || (*(p+2) == '\t')))
398 {
399 p += 3;
400 while ((*p == ' ') || (*p == '\t'))
401 p++;
402 DB (DB_JOBS, (_("BUILTIN CD %s\n"), p));
403 if (chdir (p))
404 return 0;
405 else
406 return 1;
407 }
408 else if ((*(p) == 'r')
409 && (*(p+1) == 'm')
410 && ((*(p+2) == ' ') || (*(p+2) == '\t')))
411 {
412 int in_arg;
413
414 /* rm */
415 p += 3;
416 while ((*p == ' ') || (*p == '\t'))
417 p++;
418 in_arg = 1;
419
420 DB (DB_JOBS, (_("BUILTIN RM %s\n"), p));
421 while (*p)
422 {
423 switch (*p)
424 {
425 case ' ':
426 case '\t':
427 if (in_arg)
428 {
429 *p++ = ';';
430 in_arg = 0;
431 }
432 break;
433 default:
434 break;
435 }
436 p++;
437 }
438 }
439 else
440 {
441 printf(_("Unknown builtin command '%s'\n"), cmd);
442 fflush(stdout);
443 return 0;
444 }
445 }
446
447 /* Create a *.com file if either the command is too long for
448 lib$spawn, or the command contains a newline, or if redirection
449 is desired. Forcing commands with newlines into DCLs allows to
450 store search lists on user mode logicals. */
451
452 if (strlen (cmd) > MAXCMDLEN
453 || (have_redirection != 0)
454 || (have_newline != 0))
455 {
456 FILE *outfile;
457 char c;
458 char *sep;
459 int alevel = 0; /* apostrophe level */
460
461 if (strlen (cmd) == 0)
462 {
463 printf (_("Error, empty command\n"));
464 fflush (stdout);
465 return 0;
466 }
467
468 outfile = open_tmpfile (&comname, "sys$scratch:CMDXXXXXX.COM");
469 if (outfile == 0)
470 pfatal_with_name (_("fopen (temporary file)"));
471
472 if (ifile[0])
473 {
474 fprintf (outfile, "$ assign/user %s sys$input\n", ifile);
475 DB (DB_JOBS, (_("Redirected input from %s\n"), ifile));
476 ifiledsc.dsc$w_length = 0;
477 }
478
479 if (efile[0])
480 {
481 fprintf (outfile, "$ define sys$error %s\n", efile);
482 DB (DB_JOBS, (_("Redirected error to %s\n"), efile));
483 efiledsc.dsc$w_length = 0;
484 }
485
486 if (ofile[0])
487 {
488 fprintf (outfile, "$ define sys$output %s\n", ofile);
489 DB (DB_JOBS, (_("Redirected output to %s\n"), ofile));
490 ofiledsc.dsc$w_length = 0;
491 }
492
493 p = sep = q = cmd;
494 for (c = '\n'; c; c = *q++)
495 {
496 switch (c)
497 {
498 case '\n':
499 /* At a newline, skip any whitespace around a leading $
500 from the command and issue exactly one $ into the DCL. */
501 while (isspace ((unsigned char)*p))
502 p++;
503 if (*p == '$')
504 p++;
505 while (isspace ((unsigned char)*p))
506 p++;
507 fwrite (p, 1, q - p, outfile);
508 fputc ('$', outfile);
509 fputc (' ', outfile);
510 /* Reset variables. */
511 p = sep = q;
512 break;
513
514 /* Nice places for line breaks are after strings, after
515 comma or space and before slash. */
516 case '"':
517 q = vms_handle_apos (q);
518 sep = q;
519 break;
520 case ',':
521 case ' ':
522 sep = q;
523 break;
524 case '/':
525 case '\0':
526 sep = q - 1;
527 break;
528 default:
529 break;
530 }
531 if (sep - p > 78)
532 {
533 /* Enough stuff for a line. */
534 fwrite (p, 1, sep - p, outfile);
535 p = sep;
536 if (*sep)
537 {
538 /* The command continues. */
539 fputc ('-', outfile);
540 }
541 fputc ('\n', outfile);
542 }
543 }
544
545 fwrite (p, 1, q - p, outfile);
546 fputc ('\n', outfile);
547
548 fclose (outfile);
549
550 sprintf (cmd, "$ @%s", comname);
551
552 DB (DB_JOBS, (_("Executing %s instead\n"), cmd));
553 }
554
555 cmddsc.dsc$w_length = strlen(cmd);
556 cmddsc.dsc$a_pointer = cmd;
557 cmddsc.dsc$b_dtype = DSC$K_DTYPE_T;
558 cmddsc.dsc$b_class = DSC$K_CLASS_S;
559
560 child->efn = 0;
561 while (child->efn < 32 || child->efn > 63)
562 {
563 status = lib$get_ef ((unsigned long *)&child->efn);
564 if (!(status & 1))
565 return 0;
566 }
567
568 sys$clref (child->efn);
569
570 vms_jobsefnmask |= (1 << (child->efn - 32));
571
572/*
573 LIB$SPAWN [command-string]
574 [,input-file]
575 [,output-file]
576 [,flags]
577 [,process-name]
578 [,process-id] [,completion-status-address] [,byte-integer-event-flag-num]
579 [,AST-address] [,varying-AST-argument]
580 [,prompt-string] [,cli] [,table]
581*/
582
583#ifndef DONTWAITFORCHILD
584/*
585 * Code to make ctrl+c and ctrl+y working.
586 * The problem starts with the synchronous case where after lib$spawn is
587 * called any input will go to the child. But with input re-directed,
588 * both control characters won't make it to any of the programs, neither
589 * the spawning nor to the spawned one. Hence the caller needs to spawn
590 * with CLI$M_NOWAIT to NOT give up the input focus. A sys$waitfr
591 * has to follow to simulate the wanted synchronous behaviour.
592 * The next problem is ctrl+y which isn't caught by the crtl and
593 * therefore isn't converted to SIGQUIT (for a signal handler which is
594 * already established). The only way to catch ctrl+y, is an AST
595 * assigned to the input channel. But ctrl+y handling of DCL needs to be
596 * disabled, otherwise it will handle it. Not to mention the previous
597 * ctrl+y handling of DCL needs to be re-established before make exits.
598 * One more: At the time of LIB$SPAWN signals are blocked. SIGQUIT will
599 * make it to the signal handler after the child "normally" terminates.
600 * This isn't enough. It seems reasonable for simple command lines like
601 * a 'cc foobar.c' spawned in a subprocess but it is unacceptable for
602 * spawning make. Therefore we need to abort the process in the AST.
603 *
604 * Prior to the spawn it is checked if an AST is already set up for
605 * ctrl+y, if not one is set up for a channel to SYS$COMMAND. In general
606 * this will work except if make is run in a batch environment, but there
607 * nobody can press ctrl+y. During the setup the DCL handling of ctrl+y
608 * is disabled and an exit handler is established to re-enable it.
609 * If the user interrupts with ctrl+y, the assigned AST will fire, force
610 * an abort to the subprocess and signal SIGQUIT, which will be caught by
611 * the already established handler and will bring us back to common code.
612 * After the spawn (now /nowait) a sys$waitfr simulates the /wait and
613 * enables the ctrl+y be delivered to this code. And the ctrl+c too,
614 * which the crtl converts to SIGINT and which is caught by the common
615 * signal handler. Because signals were blocked before entering this code
616 * sys$waitfr will always complete and the SIGQUIT will be processed after
617 * it (after termination of the current block, somewhere in common code).
618 * And SIGINT too will be delayed. That is ctrl+c can only abort when the
619 * current command completes. Anyway it's better than nothing :-)
620 */
621
622 if (!setupYAstTried)
623 tryToSetupYAst();
624 status = lib$spawn (&cmddsc, /* cmd-string */
625 (ifiledsc.dsc$w_length == 0)?0:&ifiledsc, /* input-file */
626 (ofiledsc.dsc$w_length == 0)?0:&ofiledsc, /* output-file */
627 &spflags, /* flags */
628 &pnamedsc, /* proc name */
629 &child->pid, &child->cstatus, &child->efn,
630 0, 0,
631 0, 0, 0);
632 if (status & 1)
633 {
634 pidToAbort= child->pid;
635 status= sys$waitfr (child->efn);
636 pidToAbort= 0;
637 vmsHandleChildTerm(child);
638 }
639#else
640 status = lib$spawn (&cmddsc,
641 (ifiledsc.dsc$w_length == 0)?0:&ifiledsc,
642 (ofiledsc.dsc$w_length == 0)?0:&ofiledsc,
643 &spflags,
644 &pnamedsc,
645 &child->pid, &child->cstatus, &child->efn,
646 vmsHandleChildTerm, child,
647 0, 0, 0);
648#endif
649
650 if (!(status & 1))
651 {
652 printf (_("Error spawning, %d\n") ,status);
653 fflush (stdout);
654 switch (status)
655 {
656 case 0x1c:
657 errno = EPROCLIM;
658 break;
659 default:
660 errno = EFAIL;
661 }
662 }
663
664 if (comname && !ISDB (DB_JOBS))
665 unlink (comname);
666
667 return (status & 1);
668}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette