int run_in_shell_no_cls(char command[]) { typedef void (*sig_handler)(int); int pid; int result; extern char **environ; sig_handler sigtstp_handler; if(command == NULL) return 1; sigtstp_handler = signal(SIGTSTP, SIG_DFL); /* We need to block SIGCHLD signal. One can't just set it to SIG_DFL, because * it will possibly cause missing of SIGCHLD from a background process * (job). */ (void)set_sigchld(1); pid = fork(); if(pid == -1) { signal(SIGTSTP, sigtstp_handler); (void)set_sigchld(0); return -1; } if(pid == 0) { char *args[4]; signal(SIGTSTP, SIG_DFL); signal(SIGINT, SIG_DFL); (void)set_sigchld(0); args[0] = cfg.shell; args[1] = "-c"; args[2] = command; args[3] = NULL; execve(cfg.shell, args, environ); exit(127); } result = get_proc_exit_status(pid); signal(SIGTSTP, sigtstp_handler); (void)set_sigchld(0); return result; }
int background_and_wait_for_errors(char cmd[], int cancellable) { #ifndef _WIN32 pid_t pid; int error_pipe[2]; int result = 0; if(pipe(error_pipe) != 0) { error_msg("File pipe error", "Error creating pipe"); return -1; } (void)set_sigchld(1); if((pid = fork()) == -1) { (void)set_sigchld(0); return -1; } if(pid == 0) { (void)set_sigchld(0); run_from_fork(error_pipe, 1, cmd); } else { char buf[80*10]; char linebuf[80]; int nread = 0; close(error_pipe[1]); /* Close write end of pipe. */ if(cancellable) { ui_cancellation_enable(); } wait_for_data_from(pid, NULL, error_pipe[0]); buf[0] = '\0'; while((nread = read(error_pipe[0], linebuf, sizeof(linebuf) - 1)) > 0) { const int read_empty_line = nread == 1 && linebuf[0] == '\n'; result = -1; linebuf[nread] = '\0'; if(!read_empty_line) { strncat(buf, linebuf, sizeof(buf) - strlen(buf) - 1); } wait_for_data_from(pid, NULL, error_pipe[0]); } close(error_pipe[0]); if(cancellable) { ui_cancellation_disable(); } if(result != 0) { error_msg("Background Process Error", buf); } else { /* Don't use "const int" variables with WEXITSTATUS() as they cause * compilation errors in case __USE_BSD is defined. Anonymous type with * "const int" is composed via compound literal expression. */ int status = get_proc_exit_status(pid); result = (status != -1 && WIFEXITED(status)) ? WEXITSTATUS(status) : -1; } } (void)set_sigchld(0); return result; #else return -1; #endif }
/* Checks status of the job. Processes error stream or checks whether process * is still running. */ static void job_check(job_t *const job) { #ifndef _WIN32 fd_set ready; int max_fd = 0; struct timeval ts = { .tv_sec = 0, .tv_usec = 1000 }; /* Setup pipe for reading */ FD_ZERO(&ready); if(job->fd >= 0) { FD_SET(job->fd, &ready); max_fd = job->fd; } if(job->error != NULL) { if(!job->skip_errors) { job->skip_errors = prompt_error_msg("Background Process Error", job->error); } free(job->error); job->error = NULL; } while(select(max_fd + 1, &ready, NULL, NULL, &ts) > 0) { char err_msg[ERR_MSG_LEN]; const ssize_t nread = read(job->fd, err_msg, sizeof(err_msg) - 1); if(nread == 0) { break; } else if(nread > 0 && !job->skip_errors) { err_msg[nread] = '\0'; job->skip_errors = prompt_error_msg("Background Process Error", err_msg); } } #else DWORD retcode; if(GetExitCodeProcess(job->hprocess, &retcode) != 0) { if(retcode != STILL_ACTIVE) { job->running = 0; } } #endif } /* Frees resources allocated by the job as well as the job_t structure itself. * The job can be NULL. */ static void job_free(job_t *const job) { if(job == NULL) { return; } if(job->type != BJT_COMMAND) { pthread_mutex_destroy(&job->bg_op_guard); } #ifndef _WIN32 if(job->fd != NO_JOB_ID) { close(job->fd); } #else if(job->hprocess != NO_JOB_ID) { CloseHandle(job->hprocess); } #endif free(job->bg_op.descr); free(job->cmd); free(job); } /* Used for FUSE mounting and unmounting only. */ int background_and_wait_for_status(char cmd[], int cancellable, int *cancelled) { #ifndef _WIN32 pid_t pid; int status; if(cancellable) { *cancelled = 0; } if(cmd == NULL) { return 1; } (void)set_sigchld(1); pid = fork(); if(pid == (pid_t)-1) { (void)set_sigchld(0); LOG_SERROR_MSG(errno, "Forking has failed."); return -1; } if(pid == (pid_t)0) { extern char **environ; (void)set_sigchld(0); (void)execve(get_execv_path(cfg.shell), make_execv_array(cfg.shell, cmd), environ); _Exit(127); } if(cancellable) { ui_cancellation_enable(); } while(waitpid(pid, &status, 0) == -1) { if(errno != EINTR) { LOG_SERROR_MSG(errno, "Failed waiting for process: %" PRINTF_ULL, (unsigned long long)pid); status = -1; break; } process_cancel_request(pid); } if(cancellable) { if(ui_cancellation_requested()) { *cancelled = 1; } ui_cancellation_disable(); } (void)set_sigchld(0); return status; #else return -1; #endif }