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