void run_setup (const char *prog) { char *cp; int i; char *run_prog; /* clean out any malloc'ed values from run_argv */ for (i = 0; i < run_argc; i++) { if (run_argv[i]) { free (run_argv[i]); run_argv[i] = (char *) 0; } } run_argc = 0; run_prog = xstrdup (prog); /* put each word into run_argv, allocating it as we go */ for (cp = strtok (run_prog, " \t"); cp; cp = strtok ((char *) NULL, " \t")) run_add_arg (cp); free (run_prog); }
/* * Builds a temporary file using setup_tmpfile() and invokes the user's * editor on the file. The header garbage in the resultant file is then * stripped and the log message is stored in the "message" argument. * * If REPOSITORY is non-NULL, process rcsinfo for that repository; if it * is NULL, use the CVSADM_TEMPLATE file instead. REPOSITORY should be * NULL when running in client mode. * * GLOBALS * Editor Set to a default value by configure and overridable using the * -e option to the CVS executable. */ void do_editor (const char *dir, char **messagep, const char *repository, List *changes) { static int reuse_log_message = 0; char *line; int line_length; size_t line_chars_allocated; char *fname; struct stat pre_stbuf, post_stbuf; int retcode = 0; assert (!current_parsed_root->isremote != !repository); if (noexec || reuse_log_message) return; /* Abort before creation of the temp file if no editor is defined. */ if (strcmp (Editor, "") == 0) error(1, 0, "no editor defined, must use -e or -m"); again: /* Create a temporary file. */ if( ( fp = cvs_temp_file( &fname ) ) == NULL ) error( 1, errno, "cannot create temporary file" ); if (*messagep) { (void) fputs (*messagep, fp); if ((*messagep)[0] == '\0' || (*messagep)[strlen (*messagep) - 1] != '\n') (void) fprintf (fp, "\n"); } else (void) fprintf (fp, "\n"); if (repository != NULL) /* tack templates on if necessary */ (void) Parse_Info (CVSROOTADM_RCSINFO, repository, rcsinfo_proc, PIOPT_ALL, NULL); else { FILE *tfp; char buf[1024]; size_t n; size_t nwrite; /* Why "b"? */ tfp = CVS_FOPEN (CVSADM_TEMPLATE, "rb"); if (tfp == NULL) { if (!existence_error (errno)) error (1, errno, "cannot read %s", CVSADM_TEMPLATE); } else { while (!feof (tfp)) { char *p = buf; n = fread (buf, 1, sizeof buf, tfp); nwrite = n; while (nwrite > 0) { n = fwrite (p, 1, nwrite, fp); nwrite -= n; p += n; } if (ferror (tfp)) error (1, errno, "cannot read %s", CVSADM_TEMPLATE); } if (fclose (tfp) < 0) error (0, errno, "cannot close %s", CVSADM_TEMPLATE); } } (void) fprintf (fp, "%s----------------------------------------------------------------------\n", CVSEDITPREFIX); (void) fprintf (fp, "%sEnter Log. Lines beginning with `%.*s' are removed automatically\n%s\n", CVSEDITPREFIX, CVSEDITPREFIXLEN, CVSEDITPREFIX, CVSEDITPREFIX); if (dir != NULL && *dir) (void) fprintf (fp, "%sCommitting in %s\n%s\n", CVSEDITPREFIX, dir, CVSEDITPREFIX); if (changes != NULL) setup_tmpfile (fp, CVSEDITPREFIX, changes); (void) fprintf (fp, "%s----------------------------------------------------------------------\n", CVSEDITPREFIX); /* finish off the temp file */ if (fclose (fp) == EOF) error (1, errno, "%s", fname); if (stat (fname, &pre_stbuf) == -1) pre_stbuf.st_mtime = 0; /* run the editor */ run_setup (Editor); run_add_arg (fname); if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL | RUN_SIGIGNORE | RUN_UNSETXID)) != 0) error (0, retcode == -1 ? errno : 0, "warning: editor session failed"); /* put the entire message back into the *messagep variable */ fp = xfopen (fname, "r"); if (*messagep) free (*messagep); if (stat (fname, &post_stbuf) != 0) error (1, errno, "cannot find size of temp file %s", fname); if (post_stbuf.st_size == 0) *messagep = NULL; else { /* On NT, we might read less than st_size bytes, but we won't read more. So this works. */ *messagep = (char *) xmalloc (post_stbuf.st_size + 1); (*messagep)[0] = '\0'; } line = NULL; line_chars_allocated = 0; if (*messagep) { size_t message_len = post_stbuf.st_size + 1; size_t offset = 0; while (1) { line_length = getline (&line, &line_chars_allocated, fp); if (line_length == -1) { if (ferror (fp)) error (0, errno, "warning: cannot read %s", fname); break; } if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0) continue; if (offset + line_length >= message_len) expand_string (messagep, &message_len, offset + line_length + 1); (void) strcpy (*messagep + offset, line); offset += line_length; } } if (fclose (fp) < 0) error (0, errno, "warning: cannot close %s", fname); /* canonicalize emply messages */ if (*messagep != NULL && (**messagep == '\0' || strcmp (*messagep, "\n") == 0)) { free (*messagep); *messagep = NULL; } if (pre_stbuf.st_mtime == post_stbuf.st_mtime || *messagep == NULL) { for (;;) { (void) printf ("\nLog message unchanged or not specified\n"); (void) printf ("a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs\n"); (void) printf ("Action: (abort) "); (void) fflush (stdout); line_length = getline (&line, &line_chars_allocated, stdin); if (line_length < 0) { error (0, errno, "cannot read from stdin"); if (unlink_file (fname) < 0) error (0, errno, "warning: cannot remove temp file %s", fname); error (1, 0, "aborting"); } else if (line_length == 0 || *line == '\n' || *line == 'a' || *line == 'A') { if (unlink_file (fname) < 0) error (0, errno, "warning: cannot remove temp file %s", fname); error (1, 0, "aborted by user"); } if (*line == 'c' || *line == 'C') break; if (*line == 'e' || *line == 'E') goto again; if (*line == '!') { reuse_log_message = 1; break; } (void) printf ("Unknown input\n"); } } if (line) free (line); if (unlink_file (fname) < 0) error (0, errno, "warning: cannot remove temp file %s", fname); free (fname); }
/* VARARGS */ void run_setup (const char *prog) { char *run_prog; char *buf, *d, *s; size_t length; size_t doff; char inquotes; int dolastarg; /* clean out any malloc'ed values from run_argv */ run_arg_free_p (run_argc, run_argv); run_argc = 0; run_prog = xstrdup (prog); s = run_prog; d = buf = NULL; length = 0; dolastarg = 1; inquotes = '\0'; doff = d - buf; expand_string(&buf, &length, doff + 1); d = buf + doff; while ((*d = *s++) != '\0') { switch (*d) { case '\\': if (*s) *d = *s++; d++; break; case '"': case '\'': if (inquotes == *d) inquotes = '\0'; else inquotes = *d; break; case ' ': case '\t': if (inquotes) d++; else { *d = '\0'; run_add_arg (buf); d = buf; while (isspace(*s)) s++; if (!*s) dolastarg = 0; } break; default: d++; break; } doff = d - buf; expand_string(&buf, &length, doff + 1); d = buf + doff; } if (dolastarg) run_add_arg (buf); /* put each word into run_argv, allocating it as we go */ if (buf) free (buf); free (run_prog); }
int run_piped (int *tofdp, int *fromfdp) { run_add_arg (NULL); return piped_child (run_argv, tofdp, fromfdp, false); }
int run_exec (const char *stin, const char *stout, const char *sterr, int flags) { int shin, shout, sherr; int mode_out, mode_err; int status; int rc = -1; int rerrno = 0; int pid, w; #ifdef POSIX_SIGNALS sigset_t sigset_mask, sigset_omask; struct sigaction act, iact, qact; #else #ifdef BSD_SIGNALS int mask; struct sigvec vec, ivec, qvec; #else RETSIGTYPE (*istat) (), (*qstat) (); #endif #endif if (trace) { cvs_outerr ( #ifdef SERVER_SUPPORT server_active ? "S" : #endif " ", 1); cvs_outerr (" -> system (", 0); run_print (stderr); cvs_outerr (")\n", 0); } if (noexec && (flags & RUN_REALLY) == 0) return 0; /* make sure that we are null terminated, since we didn't calloc */ run_add_arg (NULL); /* setup default file descriptor numbers */ shin = 0; shout = 1; sherr = 2; /* set the file modes for stdout and stderr */ mode_out = mode_err = O_WRONLY | O_CREAT; mode_out |= ((flags & RUN_STDOUT_APPEND) ? O_APPEND : O_TRUNC); mode_err |= ((flags & RUN_STDERR_APPEND) ? O_APPEND : O_TRUNC); if (stin && (shin = open (stin, O_RDONLY)) == -1) { rerrno = errno; error (0, errno, "cannot open %s for reading (prog %s)", stin, run_argv[0]); goto out0; } if (stout && (shout = open (stout, mode_out, 0666)) == -1) { rerrno = errno; error (0, errno, "cannot open %s for writing (prog %s)", stout, run_argv[0]); goto out1; } if (sterr && (flags & RUN_COMBINED) == 0) { if ((sherr = open (sterr, mode_err, 0666)) == -1) { rerrno = errno; error (0, errno, "cannot open %s for writing (prog %s)", sterr, run_argv[0]); goto out2; } } /* Make sure we don't flush this twice, once in the subprocess. */ cvs_flushout(); cvs_flusherr(); /* The output files, if any, are now created. Do the fork and dups. We use vfork not so much for a performance boost (the performance boost, if any, is modest on most modern unices), but for the sake of systems without a memory management unit, which find it difficult or impossible to implement fork at all (e.g. Amiga). The other solution is spawn (see windows-NT/run.c). */ #ifdef HAVE_VFORK pid = vfork (); #else pid = fork (); #endif if (pid == 0) { #ifdef SETXID_SUPPORT if (flags & RUN_UNSETXID) { (void) setgid (getgid ()); (void) setuid (getuid ()); } #endif if (shin != 0) { (void) dup2 (shin, 0); (void) close (shin); } if (shout != 1) { (void) dup2 (shout, 1); (void) close (shout); } if (flags & RUN_COMBINED) (void) dup2 (1, 2); else if (sherr != 2) { (void) dup2 (sherr, 2); (void) close (sherr); } #ifdef SETXID_SUPPORT /* ** This prevents a user from creating a privileged shell ** from the text editor when the SETXID_SUPPORT option is selected. */ if (!strcmp (run_argv[0], Editor) && setegid (getgid ())) { error (0, errno, "cannot set egid to gid"); _exit (127); } #endif /* dup'ing is done. try to run it now */ (void) execvp (run_argv[0], run_argv); error (0, errno, "cannot exec %s", run_argv[0]); _exit (127); } else if (pid == -1) { rerrno = errno; goto out; } /* the parent. Ignore some signals for now */ #ifdef POSIX_SIGNALS if (flags & RUN_SIGIGNORE) { act.sa_handler = SIG_IGN; (void) sigemptyset (&act.sa_mask); act.sa_flags = 0; (void) sigaction (SIGINT, &act, &iact); (void) sigaction (SIGQUIT, &act, &qact); } else { (void) sigemptyset (&sigset_mask); (void) sigaddset (&sigset_mask, SIGINT); (void) sigaddset (&sigset_mask, SIGQUIT); (void) sigprocmask (SIG_SETMASK, &sigset_mask, &sigset_omask); } #else #ifdef BSD_SIGNALS if (flags & RUN_SIGIGNORE) { memset (&vec, 0, sizeof vec); vec.sv_handler = SIG_IGN; (void) sigvec (SIGINT, &vec, &ivec); (void) sigvec (SIGQUIT, &vec, &qvec); } else mask = sigblock (sigmask (SIGINT) | sigmask (SIGQUIT)); #else istat = signal (SIGINT, SIG_IGN); qstat = signal (SIGQUIT, SIG_IGN); #endif #endif /* wait for our process to die and munge return status */ #ifdef POSIX_SIGNALS while ((w = waitpid (pid, &status, 0)) == -1 && errno == EINTR) ; #else while ((w = wait (&status)) != pid) { if (w == -1 && errno != EINTR) break; } #endif if (w == -1) { rc = -1; rerrno = errno; } #ifndef VMS /* status is return status */ else if (WIFEXITED (status)) rc = WEXITSTATUS (status); else if (WIFSIGNALED (status)) { if (WTERMSIG (status) == SIGPIPE) error (1, 0, "broken pipe"); rc = 2; } else rc = 1; #else /* VMS */ rc = WEXITSTATUS (status); #endif /* VMS */ /* restore the signals */ #ifdef POSIX_SIGNALS if (flags & RUN_SIGIGNORE) { (void) sigaction (SIGINT, &iact, NULL); (void) sigaction (SIGQUIT, &qact, NULL); } else (void) sigprocmask (SIG_SETMASK, &sigset_omask, NULL); #else #ifdef BSD_SIGNALS if (flags & RUN_SIGIGNORE) { (void) sigvec (SIGINT, &ivec, NULL); (void) sigvec (SIGQUIT, &qvec, NULL); } else (void) sigsetmask (mask); #else (void) signal (SIGINT, istat); (void) signal (SIGQUIT, qstat); #endif #endif /* cleanup the open file descriptors */ out: if (sterr) (void) close (sherr); else /* ensure things are received by the parent in the correct order * relative to the protocol pipe */ cvs_flusherr(); out2: if (stout) (void) close (shout); else /* ensure things are received by the parent in the correct order * relative to the protocol pipe */ cvs_flushout(); out1: if (stin) (void) close (shin); out0: if (rerrno) errno = rerrno; return rc; }