summaryrefslogtreecommitdiff
path: root/job.c
diff options
context:
space:
mode:
Diffstat (limited to 'job.c')
-rw-r--r--job.c583
1 files changed, 539 insertions, 44 deletions
diff --git a/job.c b/job.c
index c920b9f..c4e30c9 100644
--- a/job.c
+++ b/job.c
@@ -46,7 +46,12 @@ extern int MyExecute (char **);
directories we could trust). */
char *default_shell = "command.com";
# else /* __MSDOS__ */
+# ifdef VMS
+# include <descrip.h>
+char default_shell[] = "";
+# else
char default_shell[] = "/bin/sh";
+# endif /* VMS */
# endif /* __MSDOS__ */
int batch_mode_shell = 0;
# endif /* _AMIGA */
@@ -70,7 +75,9 @@ static int amiga_batch_file;
#ifdef VMS
# include <time.h>
-# include <processes.h>
+# ifndef __GNUC__
+# include <processes.h>
+# endif
# include <starlet.h>
# include <lib$routines.h>
#endif
@@ -186,6 +193,7 @@ extern int start_remote_job_p PARAMS ((int));
extern int remote_status PARAMS ((int *exit_code_ptr, int *signal_ptr,
int *coredump_ptr, int block));
+RETSIGTYPE child_handler PARAMS ((int));
static void free_child PARAMS ((struct child *));
static void start_job_command PARAMS ((struct child *child));
static int load_too_high PARAMS ((void));
@@ -226,7 +234,6 @@ int w32_kill(int pid, int sig)
}
#endif /* WINDOWS32 */
-
/* Write an error message describing the exit status given in
EXIT_CODE, EXIT_SIG, and COREDUMP, for the target TARGET_NAME.
Append "(ignored)" if IGNORED is nonzero. */
@@ -272,6 +279,98 @@ vmsWaitForChildren(int *status)
}
return;
}
+
+/* Set up IO redirection. */
+
+char *
+vms_redirect (desc, fname, ibuf)
+ struct dsc$descriptor_s *desc;
+ char *fname;
+ char *ibuf;
+{
+ char *fptr;
+ extern char *vmsify ();
+
+ ibuf++;
+ while (isspace (*ibuf))
+ ibuf++;
+ fptr = ibuf;
+ while (*ibuf && !isspace (*ibuf))
+ ibuf++;
+ *ibuf = 0;
+ if (strcmp (fptr, "/dev/null") != 0)
+ {
+ strcpy (fname, vmsify (fptr, 0));
+ if (strchr (fname, '.') == 0)
+ strcat (fname, ".");
+ }
+ desc->dsc$w_length = strlen(fname);
+ desc->dsc$a_pointer = fname;
+ desc->dsc$b_dtype = DSC$K_DTYPE_T;
+ desc->dsc$b_class = DSC$K_CLASS_S;
+
+ if (*fname == 0)
+ printf ("Warning: Empty redirection\n");
+ return ibuf;
+}
+
+
+/*
+ found apostrophe at (p-1)
+
+ inc p until after closing apostrophe. */
+
+static char *
+handle_apos (char *p)
+{
+ int alast;
+ int inside;
+
+#define SEPCHARS ",/()= "
+
+ inside = 0;
+
+ while (*p != 0)
+ {
+ if (*p == '"')
+ {
+ if (inside)
+ {
+ while ((alast > 0)
+ && (*p == '"'))
+ {
+ p++;
+ alast--;
+ }
+ if (alast == 0)
+ inside = 0;
+ else
+ {
+ fprintf (stderr, "Syntax error, still inside '\"'\n");
+ exit (3);
+ }
+ }
+ else
+ {
+ p++;
+ if (strchr (SEPCHARS, *p))
+ break;
+ inside = 1;
+ alast = 1;
+ while (*p == '"')
+ {
+ alast++;
+ p++;
+ }
+ }
+ }
+ else
+ p++;
+ }
+
+ return p;
+}
+
#endif
@@ -1603,6 +1702,76 @@ int vmsHandleChildTerm(struct child *child)
#define MAXCMDLEN 200
+/* local helpers to make ctrl+c and ctrl+y working, see below */
+#include <iodef.h>
+#include <libclidef.h>
+#include <ssdef.h>
+
+static int ctrlMask= LIB$M_CLI_CTRLY;
+static int oldCtrlMask;
+static int setupYAstTried= 0;
+static int pidToAbort= 0;
+static int chan= 0;
+
+static void reEnableAst(void) {
+ lib$enable_ctrl (&oldCtrlMask,0);
+}
+
+static astHandler (void) {
+ if (pidToAbort) {
+ sys$forcex (&pidToAbort, 0, SS$_ABORT);
+ pidToAbort= 0;
+ }
+ kill (getpid(),SIGQUIT);
+}
+
+static void tryToSetupYAst(void) {
+ $DESCRIPTOR(inputDsc,"SYS$COMMAND");
+ int status;
+ struct {
+ short int status, count;
+ int dvi;
+ } iosb;
+
+ setupYAstTried++;
+
+ if (!chan) {
+ status= sys$assign(&inputDsc,&chan,0,0);
+ if (!(status&SS$_NORMAL)) {
+ lib$signal(status);
+ return;
+ }
+ }
+ status= sys$qiow (0, chan, IO$_SETMODE|IO$M_CTRLYAST,&iosb,0,0,
+ astHandler,0,0,0,0,0);
+ if (status==SS$_ILLIOFUNC) {
+ sys$dassgn(chan);
+#ifdef CTRLY_ENABLED_ANYWAY
+ fprintf (stderr, "-warning, CTRL-Y will leave "
+ "sub-process(es) around.\n");
+#else
+ return;
+#endif
+ }
+ if (status==SS$_NORMAL)
+ status= iosb.status;
+ if (!(status&SS$_NORMAL)) {
+ lib$signal(status);
+ return;
+ }
+
+ /* called from AST handler ? */
+ if (setupYAstTried>1)
+ return;
+ if (atexit(reEnableAst))
+ fprintf (stderr, "-warning, you may have to re-enable CTRL-Y"
+ "handling from DCL.\n");
+ status= lib$disable_ctrl (&ctrlMask, &oldCtrlMask);
+ if (!(status&SS$_NORMAL)) {
+ lib$signal(status);
+ return;
+ }
+}
int
child_execute_job (argv, child)
char *argv;
@@ -1610,32 +1779,187 @@ child_execute_job (argv, child)
{
int i;
static struct dsc$descriptor_s cmddsc;
-#ifndef DONTWAITFORCHILD
- int spflags = 0;
-#else
+ static struct dsc$descriptor_s pnamedsc;
+ static struct dsc$descriptor_s ifiledsc;
+ static struct dsc$descriptor_s ofiledsc;
+ static struct dsc$descriptor_s efiledsc;
+ int have_redirection = 0;
+ int have_newline = 0;
+
int spflags = CLI$M_NOWAIT;
-#endif
int status;
- char cmd[4096],*p,*c;
+ char *cmd = alloca (strlen (argv) + 512), *p, *q;
+ char ifile[256], ofile[256], efile[256];
char comname[50];
+ char procname[100];
+
+ /* Parse IO redirection. */
+
+ ifile[0] = 0;
+ ofile[0] = 0;
+ efile[0] = 0;
+
+ if (debug_flag)
+ printf ("child_execute_job (%s)\n", argv);
+
+ while (isspace (*argv))
+ argv++;
+
+ if (*argv == 0)
+ return 0;
+
+ sprintf (procname, "GMAKE_%05x", getpid () & 0xfffff);
+ pnamedsc.dsc$w_length = strlen(procname);
+ pnamedsc.dsc$a_pointer = procname;
+ pnamedsc.dsc$b_dtype = DSC$K_DTYPE_T;
+ pnamedsc.dsc$b_class = DSC$K_CLASS_S;
+
+ /* Handle comments and redirection. */
+ for (p = argv, q = cmd; *p; p++, q++)
+ {
+ switch (*p)
+ {
+ case '#':
+ *p-- = 0;
+ *q-- = 0;
+ break;
+ case '\\':
+ p++;
+ if (*p == '\n')
+ p++;
+ if (isspace (*p))
+ {
+ do { p++; } while (isspace (*p));
+ p--;
+ }
+ *q = *p;
+ break;
+ case '<':
+ p = vms_redirect (&ifiledsc, ifile, p);
+ *q = ' ';
+ have_redirection = 1;
+ break;
+ case '>':
+ have_redirection = 1;
+ if (*(p-1) == '2')
+ {
+ q--;
+ if (strncmp (p, ">&1", 3) == 0)
+ {
+ p += 3;
+ strcpy (efile, "sys$output");
+ efiledsc.dsc$w_length = strlen(efile);
+ efiledsc.dsc$a_pointer = efile;
+ efiledsc.dsc$b_dtype = DSC$K_DTYPE_T;
+ efiledsc.dsc$b_class = DSC$K_CLASS_S;
+ }
+ else
+ {
+ p = vms_redirect (&efiledsc, efile, p);
+ }
+ }
+ else
+ {
+ p = vms_redirect (&ofiledsc, ofile, p);
+ }
+ *q = ' ';
+ break;
+ case '\n':
+ have_newline = 1;
+ default:
+ *q = *p;
+ break;
+ }
+ }
+ *q = *p;
-/* Remove backslashes */
- for (p = argv, c = cmd; *p; p++,c++)
+ if (strncmp (cmd, "builtin_", 8) == 0)
{
- if (*p == '\\') p++;
- *c = *p;
+ child->pid = 270163;
+ child->efn = 0;
+ child->cstatus = 1;
+
+ if (debug_flag)
+ printf ("BUILTIN [%s][%s]\n", cmd, cmd+8);
+
+ p = cmd + 8;
+
+ if ((*(p) == 'c')
+ && (*(p+1) == 'd')
+ && ((*(p+2) == ' ') || (*(p+2) == '\t')))
+ {
+ p += 3;
+ while ((*p == ' ') || (*p == '\t'))
+ p++;
+ if (debug_flag)
+ printf ("BUILTIN CD %s\n", p);
+ if (chdir (p))
+ return 0;
+ else
+ return 1;
+ }
+ else if ((*(p) == 'r')
+ && (*(p+1) == 'm')
+ && ((*(p+2) == ' ') || (*(p+2) == '\t')))
+ {
+ int in_arg;
+
+ /* rm */
+ p += 3;
+ while ((*p == ' ') || (*p == '\t'))
+ p++;
+ in_arg = 1;
+
+ if (debug_flag)
+ printf ("BUILTIN RM %s\n", p);
+ while (*p)
+ {
+ switch (*p)
+ {
+ case ' ':
+ case '\t':
+ if (in_arg)
+ {
+ *p++ = ';';
+ in_arg = 0;
+ }
+ break;
+ default:
+ break;
+ }
+ p++;
+ }
+ }
+ else
+ {
+ printf("Unknown builtin command '%s'\n", cmd);
+ fflush(stdout);
+ return 0;
+ }
}
- *c = *p;
- /* Check for maximum DCL length and create *.com file if neccesary.
- Also create a .com file if the command is more than one line long. */
+ /* Create a *.com file if either the command is too long for
+ lib$spawn, or the command contains a newline, or if redirection
+ is desired. Forcing commands with newlines into DCLs allows to
+ store search lists on user mode logicals. */
comname[0] = '\0';
- if (strlen (cmd) > MAXCMDLEN || strchr (cmd, '\n'))
+ if (strlen (cmd) > MAXCMDLEN
+ || (have_redirection != 0)
+ || (have_newline != 0))
{
FILE *outfile;
- char tmp;
+ char c;
+ char *sep;
+ int alevel = 0; /* apostrophe level */
+
+ if (strlen (cmd) == 0)
+ {
+ printf ("Error, empty command\n");
+ fflush (stdout);
+ return 0;
+ }
strcpy (comname, "sys$scratch:CMDXXXXXX.COM");
(void) mktemp (comname);
@@ -1644,27 +1968,81 @@ child_execute_job (argv, child)
if (outfile == 0)
pfatal_with_name (comname);
- fprintf (outfile, "$ ");
- c = cmd;
+ if (ifile[0])
+ {
+ fprintf (outfile, "$ assign/user %s sys$input\n", ifile);
+ DB (DB_JOBS, (_("Redirected input from %s\n"), ifile));
+ ifiledsc.dsc$w_length = 0;
+ }
+
+ if (efile[0])
+ {
+ fprintf (outfile, "$ define sys$error %s\n", efile);
+ DB (DB_JOBS, (_("Redirected error to %s\n"), efile));
+ efiledsc.dsc$w_length = 0;
+ }
+
+ if (ofile[0])
+ {
+ fprintf (outfile, "$ define sys$output %s\n", ofile);
+ DB (DB_JOBS, (_("Redirected output to %s\n"), ofile));
+ ofiledsc.dsc$w_length = 0;
+ }
- while (c)
+ p = sep = q = cmd;
+ for (c = '\n'; c; c = *q++)
{
- p = strchr (c, ',');
- if ((p == NULL) || (p-c > MAXCMDLEN))
- p = strchr (c, ' ');
- if (p != NULL)
+ switch (c)
{
- p++;
- tmp = *p;
- *p = '\0';
+ case '\n':
+ /* At a newline, skip any whitespace around a leading $
+ from the command and issue exactly one $ into the DCL. */
+ while (isspace (*p))
+ p++;
+ if (*p == '$')
+ p++;
+ while (isspace (*p))
+ p++;
+ fwrite (p, 1, q - p, outfile);
+ fputc ('$', outfile);
+ fputc (' ', outfile);
+ /* Reset variables. */
+ p = sep = q;
+ break;
+
+ /* Nice places for line breaks are after strings, after
+ comma or space and before slash. */
+ case '"':
+ q = handle_apos (q + 1);
+ sep = q;
+ break;
+ case ',':
+ case ' ':
+ sep = q;
+ break;
+ case '/':
+ case '\0':
+ sep = q - 1;
+ break;
+ default:
+ break;
}
- else
- tmp = '\0';
- fprintf (outfile, "%s%s\n", c, (tmp == '\0')?"":" -");
- if (p != NULL)
- *p = tmp;
- c = p;
- }
+ if (sep - p > 78)
+ {
+ /* Enough stuff for a line. */
+ fwrite (p, 1, sep - p, outfile);
+ p = sep;
+ if (*sep)
+ {
+ /* The command continues. */
+ fputc ('-', outfile);
+ }
+ fputc ('\n', outfile);
+ }
+ }
+
+ fwrite (p, 1, q - p, outfile);
+ fputc ('\n', outfile);
fclose (outfile);
@@ -1681,31 +2059,98 @@ child_execute_job (argv, child)
child->efn = 0;
while (child->efn < 32 || child->efn > 63)
{
- status = lib$get_ef(&child->efn);
+ status = lib$get_ef ((unsigned long *)&child->efn);
if (!(status & 1))
return 0;
}
- sys$clref(child->efn);
+ sys$clref (child->efn);
vms_jobsefnmask |= (1 << (child->efn - 32));
+/*
+ LIB$SPAWN [command-string]
+ [,input-file]
+ [,output-file]
+ [,flags]
+ [,process-name]
+ [,process-id] [,completion-status-address] [,byte-integer-event-flag-num]
+ [,AST-address] [,varying-AST-argument]
+ [,prompt-string] [,cli] [,table]
+*/
+
#ifndef DONTWAITFORCHILD
- status = lib$spawn(&cmddsc,0,0,&spflags,0,&child->pid,&child->cstatus,
- &child->efn,0,0);
+/*
+ * Code to make ctrl+c and ctrl+y working.
+ * The problem starts with the synchronous case where after lib$spawn is
+ * called any input will go to the child. But with input re-directed,
+ * both control characters won't make it to any of the programs, neither
+ * the spawning nor to the spawned one. Hence the caller needs to spawn
+ * with CLI$M_NOWAIT to NOT give up the input focus. A sys$waitfr
+ * has to follow to simulate the wanted synchronous behaviour.
+ * The next problem is ctrl+y which isn't caught by the crtl and
+ * therefore isn't converted to SIGQUIT (for a signal handler which is
+ * already established). The only way to catch ctrl+y, is an AST
+ * assigned to the input channel. But ctrl+y handling of DCL needs to be
+ * disabled, otherwise it will handle it. Not to mention the previous
+ * ctrl+y handling of DCL needs to be re-established before make exits.
+ * One more: At the time of LIB$SPAWN signals are blocked. SIGQUIT will
+ * make it to the signal handler after the child "normally" terminates.
+ * This isn't enough. It seems reasonable for simple command lines like
+ * a 'cc foobar.c' spawned in a subprocess but it is unacceptable for
+ * spawning make. Therefore we need to abort the process in the AST.
+ *
+ * Prior to the spawn it is checked if an AST is already set up for
+ * ctrl+y, if not one is set up for a channel to SYS$COMMAND. In general
+ * this will work except if make is run in a batch environment, but there
+ * nobody can press ctrl+y. During the setup the DCL handling of ctrl+y
+ * is disabled and an exit handler is established to re-enable it.
+ * If the user interrupts with ctrl+y, the assigned AST will fire, force
+ * an abort to the subprocess and signal SIGQUIT, which will be caught by
+ * the already established handler and will bring us back to common code.
+ * After the spawn (now /nowait) a sys$waitfr simulates the /wait and
+ * enables the ctrl+y be delivered to this code. And the ctrl+c too,
+ * which the crtl converts to SIGINT and which is caught by the common
+ * signal handler. Because signals were blocked before entering this code
+ * sys$waitfr will always complete and the SIGQUIT will be processed after
+ * it (after termination of the current block, somewhere in common code).
+ * And SIGINT too will be delayed. That is ctrl+c can only abort when the
+ * current command completes. Anyway it's better than nothing :-)
+ */
+
+ if (!setupYAstTried)
+ tryToSetupYAst();
+ status = lib$spawn (&cmddsc, /* cmd-string */
+ (ifiledsc.dsc$w_length == 0)?0:&ifiledsc, /* input-file */
+ (ofiledsc.dsc$w_length == 0)?0:&ofiledsc, /* output-file */
+ &spflags, /* flags */
+ &pnamedsc, /* proc name */
+ &child->pid, &child->cstatus, &child->efn,
+ 0, 0,
+ 0, 0, 0);
+ pidToAbort= child->pid;
+ status= sys$waitfr (child->efn);
+ pidToAbort= 0;
vmsHandleChildTerm(child);
#else
- status = lib$spawn(&cmddsc,0,0,&spflags,0,&child->pid,&child->cstatus,
- &child->efn,vmsHandleChildTerm,child);
+ status = lib$spawn (&cmddsc,
+ (ifiledsc.dsc$w_length == 0)?0:&ifiledsc,
+ (ofiledsc.dsc$w_length == 0)?0:&ofiledsc,
+ &spflags,
+ &pnamedsc,
+ &child->pid, &child->cstatus, &child->efn,
+ vmsHandleChildTerm, child,
+ 0, 0, 0);
#endif
if (!(status & 1))
{
- printf(_("Error spawning, %d\n"),status);
- fflush(stdout);
+ printf ("Error spawning, %d\n",status);
+ fflush (stdout);
}
- unlink (comname);
+ if (comname[0] && !ISDB (DB_JOBS)))
+ unlink (comname);
return (status & 1);
}
@@ -1748,6 +2193,10 @@ exec_command (argv, envp)
char **argv, **envp;
{
#ifdef VMS
+ /* to work around a problem with signals and execve: ignore them */
+#ifdef SIGCHLD
+ signal (SIGCHLD,SIG_IGN);
+#endif
/* Run the program. */
execve (argv[0], argv, envp);
perror_with_name ("execve: ", argv[0]);
@@ -2337,7 +2786,11 @@ construct_command_argv_internal (line, restp, shell, ifs, batch_filename_ptr)
argument list. */
unsigned int shell_len = strlen (shell);
+#ifndef VMS
static char minus_c[] = " -c ";
+#else
+ static char minus_c[] = "";
+#endif
unsigned int line_len = strlen (line);
char *new_line = (char *) alloca (shell_len + (sizeof (minus_c) - 1)
@@ -2480,6 +2933,7 @@ construct_command_argv_internal (line, restp, shell, ifs, batch_filename_ptr)
return new_argv;
}
+#endif /* !VMS */
/* Figure out the argument list necessary to run LINE as a command. Try to
avoid using a shell. This routine handles only ' quoting, and " quoting
@@ -2503,6 +2957,48 @@ construct_command_argv (line, restp, file, batch_filename_ptr)
char *shell, *ifs;
char **argv;
+#ifdef VMS
+ char *cptr;
+ int argc;
+
+ argc = 0;
+ cptr = line;
+ for (;;)
+ {
+ while ((*cptr != 0)
+ && (isspace (*cptr)))
+ cptr++;
+ if (*cptr == 0)
+ break;
+ while ((*cptr != 0)
+ && (!isspace(*cptr)))
+ cptr++;
+ argc++;
+ }
+
+ argv = (char **)malloc (argc * sizeof (char *));
+ if (argv == 0)
+ abort ();
+
+ cptr = line;
+ argc = 0;
+ for (;;)
+ {
+ while ((*cptr != 0)
+ && (isspace (*cptr)))
+ cptr++;
+ if (*cptr == 0)
+ break;
+ if (debug_flag)
+ printf ("argv[%d] = [%s]\n", argc, cptr);
+ argv[argc++] = cptr;
+ while ((*cptr != 0)
+ && (!isspace(*cptr)))
+ cptr++;
+ if (*cptr != 0)
+ *cptr++ = 0;
+ }
+#else
{
/* Turn off --warn-undefined-variables while we expand SHELL and IFS. */
int save = warn_undefined_variables_flag;
@@ -2528,10 +3024,9 @@ construct_command_argv (line, restp, file, batch_filename_ptr)
free (shell);
free (ifs);
-
+#endif /* !VMS */
return argv;
}
-#endif /* !VMS */
#if !defined(HAVE_DUP2) && !defined(_AMIGA)
int