Home | History | Annotate | Download | only in make-3.81
      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