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