/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License.
*/
int status, w;
w = waitpid(p, &status, WUNTRACED | WCONTINUED);
if (w != -1 && WIFEXITED(status)) { exit(WEXITSTATUS(status));
}
exit(EXIT_FAILURE);
}
argv += nopt;
argc -= nopt; /* now argv points to the executable */
if (argc == 0) { // -e turned echoing off // -w wait until signaled (SIGCONT) before executing a process // -p define pts_name to use instead of opening a new one // --env passes additional environment variable to a program // in NAME=VALUE form. For multiple variables multiple // --env options should be used. // --no-pty do not allocate pseudo-terminal - just execute a program // if --no-pty is used, -p/-w has no effect // --readenv start a new process in an environment. recorded in a // specified file. // --dumpenv record current environment in a specified file // --report record process' and exit status information into // specified file
err_quit("\n\n" "usage: %s [-e] [--no-pty] [-w] [-p pts_name] [--set-erase-key]\n" "\t\t[--readenv env_file] [[--env NAME=VALUE] ...] [--dir dir]\n" "\t\t[--redirect-error] [--report report_file] program [ arg ... ]\n\n" "\t-e\t\t turn echoing off\n" "\t-w\t\t wait SIGCONT after reporting PID/TTY/.. but before executing the program\n" "\t-p\t\t define pts_name to attach process's I/O instead of opening a new one\n" "\t--readenv\t read environment to start process in from a file\n" "\t--env\t\t pass (additional) environment variable to the process\n" "\t--dir\t\t change working directory for starting process\n" "\t--redirect-error redirect stderror to stdout for starting process (makes sense if --no-pty only)\n" "\t--report\t record process' and exit status information into specified file\n\n" "usage: %s --dumpenv env_file\n" "\t--dumpenv\t dump environment to a file\n"
, progname, progname); exit(-1);
}
// Set SIGCONT handler for both parent and child // This is to avoid a race condition
signal(SIGCONT, sigcont);
if (params.redirect_error) {
close(STDERR_FILENO);
dup2(STDOUT_FILENO, STDERR_FILENO);
}
if (params.nopty == 0) { if (params.pty != NULL) {
pid = pty_fork1(params.pty);
} else {
pid = pty_fork(&allocated_pty_fd);
}
} else {
pid = fork(); // We call setsid for a child to make it a session leader - // this allows us later send signals to the whole session // But we don't want to do this in a single case - when --no-pty is // used but we do have terminal (because in this case the connection // with existent terminal will be lost and it will not be a controlling // terminal for the process anymore. if (!isatty(STDOUT_FILENO)) {
setsid();
}
}
if (pid < 0) {
err_sys("fork error");
}
if (pid == 0) { /* child */
/* * get a name of a terminal we have
*/ if (isatty(STDOUT_FILENO)) {
params.pty = ttyname(STDOUT_FILENO);
}
// MAGIC variable to be used to find all children of this process char magic[20];
snprintf(magic, 20, "NBMAGIC=%ld", (long) getppid());
printf("PID=%d\n", getpid());
printf("TTY=%s\n", params.pty == NULL ? "null" : params.pty);
printf("PSID=%d\n", getsid(0)); if (params.reportfile != NULL) {
printf("REPORT=%s.%ld\n", params.reportfile, (long) getppid());
}
printf("%s\n", magic);
printf("\n"); // empty line means that we printed everything we needed
fflush(stdout);
// Set passed environment variables for (int i = 0; i < params.envnum; i++) {
putenv(params.envvars[i]);
}
// The last variable we set is NBMAGIC
putenv(magic);
if (params.waitSignal) { /* * Waiting for a SIGCONT, blocking (queuing) all other signals. * (but let SIGINT to pass...)
*/
sigset_t sigset;
sigfillset(&sigset);
sigdelset(&sigset, SIGCONT);
sigdelset(&sigset, SIGINT);
sigsuspend(&sigset);
} else { // Reset SIGCONT handler to default
signal(SIGCONT, SIG_DFL);
}
// Just before doing execvp - notify the parent process so that // it can take a start time snapshot // Parent doesn't explicitly wait for this signal, but guarantees that // it is not blocked
kill(getppid(), SIGCONT);
if (params.wdir != NULL) { if (chdir(params.wdir) == -1) {
err_sys2(errno == ENOENT ? 127 : 1, "failed to change directory: %s", params.wdir);
}
}
// setvbuf is not retained across an exec, (as buffering is a property // of a FILE, not fd - so need to do un-buffering in the execd process. // Use LD_PRELOAD for this.. // This is controlled by java code for now, at least
if (execvp(argv[0], argv) < 0) {
err_sys2(errno == ENOENT ? 127 : 1, "failed to start %s", argv[0]);
}
}
/* parent */
/* * We will ignore SIGINT and SIGQUIT signals generated by a terminal to * have a chance to get user's program exit status...
*/
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
if (params.envvars != NULL) {
free(params.envvars);
}
#if NEED_CHILD_INFO_BEFORE_REAP /* * We want to get a statistics of the child we just forked. * This statistics will be collected differently on different systems, * but this should be done at the very end of the process execution - * (just after it is done, but before reaping it's exit status). * The approach is to set a signal handler for SIGCHLD and wait for it. * SIGCHLD is send when child has finished it's execution (!!! actually, * when it changes it's state - so it is send when it goes to * foreground/background or when it is stoped/continued !!!) * Once signal is received - will read process's /proc and will get/reap it's * status with waitpid.
*/
/* * If we have a newly allocated terminal - start a loop that re-directs * process's I/O to a terminal.
*/ if (allocated_pty_fd > 0) { // At least on Windows, when gdb is started through this pty process // and calling process is killed (i.e. stdin gets broken) // and even when we close master_fd, gdb continues to work... // ??? will kill the process (gdb) in this case... int loop_result = loop(allocated_pty_fd); /* copies stdin -> ptym, ptym -> stdout */
if (loop_result != 0) { int attempt = 2; while (attempt-- >= 0 && kill(pid, 0) == 0) {
kill(pid, SIGTERM);
sleep(1);
}
/* * There is a race here !! * I bet it could be that got_sigchld is 0, but signal is already send * ?? What is we send SIGKILL to the child (the code above)? - seems that in * this case signal is sent as well... * * Will hung on a SIGCHLD if signal was not already received only
*/ while (!got_sigchld) {
sigset_t sigset;
sigfillset(&sigset);
sigdelset(&sigset, SIGCHLD); // Do not block SIGCONT as this is a way for child to notify the parent // about user process starting moment
sigdelset(&sigset, SIGCONT);
sigsuspend(&sigset);
}
//prusage_t usage; //psinfo_t psinfo;
if (params.reportfile != NULL) { // int pfd; // char fname[PATH_MAX]; // int r = getrusage(RUSAGE_CHILDREN, &rusage); // printf("r==%d\n", r);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.