• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
vmsWaitForChildren(int * status)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 *
vms_redirect(struct dsc$descriptor_s * desc,char * fname,char * ibuf)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 *
vms_handle_apos(char * p)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
vmsHandleChildTerm(struct child * child)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
reEnableAst(void)226 reEnableAst(void)
227 {
228 	lib$enable_ctrl (&oldCtrlMask,0);
229 }
230 
231 static void
astHandler(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
tryToSetupYAst(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
child_execute_job(char * argv,struct child * child)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