mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
process_spawn: Return status code (#6075)
This commit is contained in:
parent
3aa8795469
commit
5b8fac8ace
@ -23046,8 +23046,9 @@ static inline bool common_job_start(TerminalJobData *data, typval_T *rettv)
|
|||||||
|
|
||||||
data->refcount++;
|
data->refcount++;
|
||||||
char *cmd = xstrdup(proc->argv[0]);
|
char *cmd = xstrdup(proc->argv[0]);
|
||||||
if (!process_spawn(proc)) {
|
int status = process_spawn(proc);
|
||||||
EMSG2(_(e_jobspawn), cmd);
|
if (status) {
|
||||||
|
EMSG3(_(e_jobspawn), os_strerror(status), cmd);
|
||||||
xfree(cmd);
|
xfree(cmd);
|
||||||
if (proc->type == kProcessTypePty) {
|
if (proc->type == kProcessTypePty) {
|
||||||
xfree(data->proc.pty.term_name);
|
xfree(data->proc.pty.term_name);
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
# include "event/libuv_process.c.generated.h"
|
# include "event/libuv_process.c.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool libuv_process_spawn(LibuvProcess *uvproc)
|
/// @returns zero on success, or negative error code
|
||||||
|
int libuv_process_spawn(LibuvProcess *uvproc)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
Process *proc = (Process *)uvproc;
|
Process *proc = (Process *)uvproc;
|
||||||
@ -51,11 +52,11 @@ bool libuv_process_spawn(LibuvProcess *uvproc)
|
|||||||
int status;
|
int status;
|
||||||
if ((status = uv_spawn(&proc->loop->uv, &uvproc->uv, &uvproc->uvopts))) {
|
if ((status = uv_spawn(&proc->loop->uv, &uvproc->uv, &uvproc->uvopts))) {
|
||||||
ELOG("uv_spawn failed: %s", uv_strerror(status));
|
ELOG("uv_spawn failed: %s", uv_strerror(status));
|
||||||
return false;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
proc->pid = uvproc->uv.pid;
|
proc->pid = uvproc->uv.pid;
|
||||||
return true;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
void libuv_process_close(LibuvProcess *uvproc)
|
void libuv_process_close(LibuvProcess *uvproc)
|
||||||
|
@ -30,7 +30,8 @@
|
|||||||
|
|
||||||
static bool process_is_tearing_down = false;
|
static bool process_is_tearing_down = false;
|
||||||
|
|
||||||
bool process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL
|
/// @returns zero on success, or negative error code
|
||||||
|
int process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
if (proc->in) {
|
if (proc->in) {
|
||||||
uv_pipe_init(&proc->loop->uv, &proc->in->uv.pipe, 0);
|
uv_pipe_init(&proc->loop->uv, &proc->in->uv.pipe, 0);
|
||||||
@ -44,19 +45,19 @@ bool process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL
|
|||||||
uv_pipe_init(&proc->loop->uv, &proc->err->uv.pipe, 0);
|
uv_pipe_init(&proc->loop->uv, &proc->err->uv.pipe, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success;
|
int status;
|
||||||
switch (proc->type) {
|
switch (proc->type) {
|
||||||
case kProcessTypeUv:
|
case kProcessTypeUv:
|
||||||
success = libuv_process_spawn((LibuvProcess *)proc);
|
status = libuv_process_spawn((LibuvProcess *)proc);
|
||||||
break;
|
break;
|
||||||
case kProcessTypePty:
|
case kProcessTypePty:
|
||||||
success = pty_process_spawn((PtyProcess *)proc);
|
status = pty_process_spawn((PtyProcess *)proc);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!success) {
|
if (status) {
|
||||||
if (proc->in) {
|
if (proc->in) {
|
||||||
uv_close((uv_handle_t *)&proc->in->uv.pipe, NULL);
|
uv_close((uv_handle_t *)&proc->in->uv.pipe, NULL);
|
||||||
}
|
}
|
||||||
@ -74,7 +75,7 @@ bool process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL
|
|||||||
}
|
}
|
||||||
shell_free_argv(proc->argv);
|
shell_free_argv(proc->argv);
|
||||||
proc->status = -1;
|
proc->status = -1;
|
||||||
return false;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proc->in) {
|
if (proc->in) {
|
||||||
@ -105,7 +106,7 @@ bool process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL
|
|||||||
proc->internal_close_cb = decref;
|
proc->internal_close_cb = decref;
|
||||||
proc->refcount++;
|
proc->refcount++;
|
||||||
kl_push(WatcherPtr, proc->loop->children, proc);
|
kl_push(WatcherPtr, proc->loop->children, proc);
|
||||||
return true;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL
|
void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL
|
||||||
@ -155,19 +156,16 @@ void process_close_err(Process *proc) FUNC_ATTR_NONNULL_ALL
|
|||||||
|
|
||||||
/// Synchronously wait for a process to finish
|
/// Synchronously wait for a process to finish
|
||||||
///
|
///
|
||||||
/// @param process The Process instance
|
/// @param process Process instance
|
||||||
/// @param ms Number of milliseconds to wait, 0 for not waiting, -1 for
|
/// @param ms Time in milliseconds to wait for the process.
|
||||||
/// waiting until the process quits.
|
/// 0 for no wait. -1 to wait until the process quits.
|
||||||
/// @return returns the status code of the exited process. -1 if the process is
|
/// @return Exit code of the process.
|
||||||
/// still running and the `timeout` has expired. Note that this is
|
/// -1 if the timeout expired while the process is still running.
|
||||||
/// indistinguishable from the process returning -1 by itself. Which
|
/// -2 if the user interruped the wait.
|
||||||
/// is possible on some OS. Returns -2 if an user has interruped the
|
|
||||||
/// wait.
|
|
||||||
int process_wait(Process *proc, int ms, MultiQueue *events)
|
int process_wait(Process *proc, int ms, MultiQueue *events)
|
||||||
FUNC_ATTR_NONNULL_ARG(1)
|
FUNC_ATTR_NONNULL_ARG(1)
|
||||||
{
|
{
|
||||||
// The default status is -1, which represents a timeout
|
int status = -1; // default
|
||||||
int status = -1;
|
|
||||||
bool interrupted = false;
|
bool interrupted = false;
|
||||||
if (!proc->refcount) {
|
if (!proc->refcount) {
|
||||||
status = proc->status;
|
status = proc->status;
|
||||||
@ -179,8 +177,8 @@ int process_wait(Process *proc, int ms, MultiQueue *events)
|
|||||||
events = proc->events;
|
events = proc->events;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increase refcount to stop the exit callback from being called(and possibly
|
// Increase refcount to stop the exit callback from being called (and possibly
|
||||||
// being freed) before we have a chance to get the status.
|
// freed) before we have a chance to get the status.
|
||||||
proc->refcount++;
|
proc->refcount++;
|
||||||
LOOP_PROCESS_EVENTS_UNTIL(proc->loop, events, ms,
|
LOOP_PROCESS_EVENTS_UNTIL(proc->loop, events, ms,
|
||||||
// Until...
|
// Until...
|
||||||
|
@ -1127,7 +1127,7 @@ EXTERN char_u e_isadir2[] INIT(= N_("E17: \"%s\" is a directory"));
|
|||||||
EXTERN char_u e_invjob[] INIT(= N_("E900: Invalid job id"));
|
EXTERN char_u e_invjob[] INIT(= N_("E900: Invalid job id"));
|
||||||
EXTERN char_u e_jobtblfull[] INIT(= N_("E901: Job table is full"));
|
EXTERN char_u e_jobtblfull[] INIT(= N_("E901: Job table is full"));
|
||||||
EXTERN char_u e_jobspawn[] INIT(= N_(
|
EXTERN char_u e_jobspawn[] INIT(= N_(
|
||||||
"E903: Process for command \"%s\" could not be spawned"));
|
"E903: Process failed to start: %s: \"%s\""));
|
||||||
EXTERN char_u e_jobnotpty[] INIT(= N_("E904: Job is not connected to a pty"));
|
EXTERN char_u e_jobnotpty[] INIT(= N_("E904: Job is not connected to a pty"));
|
||||||
EXTERN char_u e_libcall[] INIT(= N_("E364: Library call failed for \"%s()\""));
|
EXTERN char_u e_libcall[] INIT(= N_("E364: Library call failed for \"%s()\""));
|
||||||
EXTERN char_u e_mkdir[] INIT(= N_("E739: Cannot create directory %s: %s"));
|
EXTERN char_u e_mkdir[] INIT(= N_("E739: Cannot create directory %s: %s"));
|
||||||
|
@ -33,7 +33,8 @@
|
|||||||
# include "os/pty_process_unix.c.generated.h"
|
# include "os/pty_process_unix.c.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool pty_process_spawn(PtyProcess *ptyproc)
|
/// @returns zero on success, or negative error code
|
||||||
|
int pty_process_spawn(PtyProcess *ptyproc)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
static struct termios termios;
|
static struct termios termios;
|
||||||
@ -41,6 +42,7 @@ bool pty_process_spawn(PtyProcess *ptyproc)
|
|||||||
init_termios(&termios);
|
init_termios(&termios);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int status = 0; // zero or negative error code (libuv convention)
|
||||||
Process *proc = (Process *)ptyproc;
|
Process *proc = (Process *)ptyproc;
|
||||||
assert(!proc->err);
|
assert(!proc->err);
|
||||||
uv_signal_start(&proc->loop->children_watcher, chld_handler, SIGCHLD);
|
uv_signal_start(&proc->loop->children_watcher, chld_handler, SIGCHLD);
|
||||||
@ -50,8 +52,9 @@ bool pty_process_spawn(PtyProcess *ptyproc)
|
|||||||
int pid = forkpty(&master, NULL, &termios, &ptyproc->winsize);
|
int pid = forkpty(&master, NULL, &termios, &ptyproc->winsize);
|
||||||
|
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
|
status = -errno;
|
||||||
ELOG("forkpty failed: %s", strerror(errno));
|
ELOG("forkpty failed: %s", strerror(errno));
|
||||||
return false;
|
return status;
|
||||||
} else if (pid == 0) {
|
} else if (pid == 0) {
|
||||||
init_child(ptyproc);
|
init_child(ptyproc);
|
||||||
abort();
|
abort();
|
||||||
@ -60,30 +63,34 @@ bool pty_process_spawn(PtyProcess *ptyproc)
|
|||||||
// make sure the master file descriptor is non blocking
|
// make sure the master file descriptor is non blocking
|
||||||
int master_status_flags = fcntl(master, F_GETFL);
|
int master_status_flags = fcntl(master, F_GETFL);
|
||||||
if (master_status_flags == -1) {
|
if (master_status_flags == -1) {
|
||||||
|
status = -errno;
|
||||||
ELOG("Failed to get master descriptor status flags: %s", strerror(errno));
|
ELOG("Failed to get master descriptor status flags: %s", strerror(errno));
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (fcntl(master, F_SETFL, master_status_flags | O_NONBLOCK) == -1) {
|
if (fcntl(master, F_SETFL, master_status_flags | O_NONBLOCK) == -1) {
|
||||||
|
status = -errno;
|
||||||
ELOG("Failed to make master descriptor non-blocking: %s", strerror(errno));
|
ELOG("Failed to make master descriptor non-blocking: %s", strerror(errno));
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proc->in && !set_duplicating_descriptor(master, &proc->in->uv.pipe)) {
|
if (proc->in
|
||||||
|
&& (status = set_duplicating_descriptor(master, &proc->in->uv.pipe))) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (proc->out && !set_duplicating_descriptor(master, &proc->out->uv.pipe)) {
|
if (proc->out
|
||||||
|
&& (status = set_duplicating_descriptor(master, &proc->out->uv.pipe))) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptyproc->tty_fd = master;
|
ptyproc->tty_fd = master;
|
||||||
proc->pid = pid;
|
proc->pid = pid;
|
||||||
return true;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
close(master);
|
close(master);
|
||||||
kill(pid, SIGKILL);
|
kill(pid, SIGKILL);
|
||||||
waitpid(pid, NULL, 0);
|
waitpid(pid, NULL, 0);
|
||||||
return false;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pty_process_resize(PtyProcess *ptyproc, uint16_t width, uint16_t height)
|
void pty_process_resize(PtyProcess *ptyproc, uint16_t width, uint16_t height)
|
||||||
@ -137,9 +144,10 @@ static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *prog = ptyproc->process.argv[0];
|
||||||
setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1);
|
setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1);
|
||||||
execvp(ptyproc->process.argv[0], ptyproc->process.argv);
|
execvp(prog, ptyproc->process.argv);
|
||||||
fprintf(stderr, "execvp failed: %s\n", strerror(errno));
|
fprintf(stderr, "execvp failed: %s: %s\n", strerror(errno), prog);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL
|
static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL
|
||||||
@ -197,22 +205,24 @@ static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL
|
|||||||
termios->c_cc[VTIME] = 0;
|
termios->c_cc[VTIME] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool set_duplicating_descriptor(int fd, uv_pipe_t *pipe)
|
static int set_duplicating_descriptor(int fd, uv_pipe_t *pipe)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
|
int status = 0; // zero or negative error code (libuv convention)
|
||||||
int fd_dup = dup(fd);
|
int fd_dup = dup(fd);
|
||||||
if (fd_dup < 0) {
|
if (fd_dup < 0) {
|
||||||
|
status = -errno;
|
||||||
ELOG("Failed to dup descriptor %d: %s", fd, strerror(errno));
|
ELOG("Failed to dup descriptor %d: %s", fd, strerror(errno));
|
||||||
return false;
|
return status;
|
||||||
}
|
}
|
||||||
int uv_result = uv_pipe_open(pipe, fd_dup);
|
status = uv_pipe_open(pipe, fd_dup);
|
||||||
if (uv_result) {
|
if (status) {
|
||||||
ELOG("Failed to set pipe to descriptor %d: %s",
|
ELOG("Failed to set pipe to descriptor %d: %s",
|
||||||
fd_dup, uv_strerror(uv_result));
|
fd_dup, uv_strerror(status));
|
||||||
close(fd_dup);
|
close(fd_dup);
|
||||||
return false;
|
return status;
|
||||||
}
|
}
|
||||||
return true;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void chld_handler(uv_signal_t *handle, int signum)
|
static void chld_handler(uv_signal_t *handle, int signum)
|
||||||
|
@ -124,13 +124,9 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args)
|
|||||||
|
|
||||||
size_t nread;
|
size_t nread;
|
||||||
|
|
||||||
int status = do_os_system(shell_build_argv((char *)cmd, (char *)extra_args),
|
int exitcode = do_os_system(shell_build_argv((char *)cmd, (char *)extra_args),
|
||||||
input.data,
|
input.data, input.len, output_ptr, &nread,
|
||||||
input.len,
|
emsg_silent, forward_output);
|
||||||
output_ptr,
|
|
||||||
&nread,
|
|
||||||
emsg_silent,
|
|
||||||
forward_output);
|
|
||||||
|
|
||||||
xfree(input.data);
|
xfree(input.data);
|
||||||
|
|
||||||
@ -139,16 +135,16 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args)
|
|||||||
xfree(output);
|
xfree(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!emsg_silent && status != 0 && !(opts & kShellOptSilent)) {
|
if (!emsg_silent && exitcode != 0 && !(opts & kShellOptSilent)) {
|
||||||
MSG_PUTS(_("\nshell returned "));
|
MSG_PUTS(_("\nshell returned "));
|
||||||
msg_outnum(status);
|
msg_outnum(exitcode);
|
||||||
msg_putchar('\n');
|
msg_putchar('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
State = current_state;
|
State = current_state;
|
||||||
signal_accept_deadly();
|
signal_accept_deadly();
|
||||||
|
|
||||||
return status;
|
return exitcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// os_system - synchronously execute a command in the shell
|
/// os_system - synchronously execute a command in the shell
|
||||||
@ -157,7 +153,7 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args)
|
|||||||
/// char *output = NULL;
|
/// char *output = NULL;
|
||||||
/// size_t nread = 0;
|
/// size_t nread = 0;
|
||||||
/// char *argv[] = {"ls", "-la", NULL};
|
/// char *argv[] = {"ls", "-la", NULL};
|
||||||
/// int status = os_sytem(argv, NULL, 0, &output, &nread);
|
/// int exitcode = os_sytem(argv, NULL, 0, &output, &nread);
|
||||||
///
|
///
|
||||||
/// @param argv The commandline arguments to be passed to the shell. `argv`
|
/// @param argv The commandline arguments to be passed to the shell. `argv`
|
||||||
/// will be consumed.
|
/// will be consumed.
|
||||||
@ -218,11 +214,14 @@ static int do_os_system(char **argv,
|
|||||||
proc->in = input != NULL ? &in : NULL;
|
proc->in = input != NULL ? &in : NULL;
|
||||||
proc->out = &out;
|
proc->out = &out;
|
||||||
proc->err = &err;
|
proc->err = &err;
|
||||||
if (!process_spawn(proc)) {
|
int status = process_spawn(proc);
|
||||||
|
if (status) {
|
||||||
loop_poll_events(&main_loop, 0);
|
loop_poll_events(&main_loop, 0);
|
||||||
// Failed, probably due to 'sh' not being executable
|
// Failed, probably 'shell' is not executable.
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
MSG_PUTS(_("\nCannot execute "));
|
MSG_PUTS(_("\nshell failed to start: "));
|
||||||
|
msg_outtrans((char_u *)os_strerror(status));
|
||||||
|
MSG_PUTS(": ");
|
||||||
msg_outtrans((char_u *)prog);
|
msg_outtrans((char_u *)prog);
|
||||||
msg_putchar('\n');
|
msg_putchar('\n');
|
||||||
}
|
}
|
||||||
@ -262,7 +261,7 @@ static int do_os_system(char **argv,
|
|||||||
// busy state.
|
// busy state.
|
||||||
ui_busy_start();
|
ui_busy_start();
|
||||||
ui_flush();
|
ui_flush();
|
||||||
int status = process_wait(proc, -1, NULL);
|
int exitcode = process_wait(proc, -1, NULL);
|
||||||
if (!got_int && out_data_decide_throttle(0)) {
|
if (!got_int && out_data_decide_throttle(0)) {
|
||||||
// Last chunk of output was skipped; display it now.
|
// Last chunk of output was skipped; display it now.
|
||||||
out_data_ring(NULL, SIZE_MAX);
|
out_data_ring(NULL, SIZE_MAX);
|
||||||
@ -289,7 +288,7 @@ static int do_os_system(char **argv,
|
|||||||
assert(multiqueue_empty(events));
|
assert(multiqueue_empty(events));
|
||||||
multiqueue_free(events);
|
multiqueue_free(events);
|
||||||
|
|
||||||
return status;
|
return exitcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// - ensures at least `desired` bytes in buffer
|
/// - ensures at least `desired` bytes in buffer
|
||||||
@ -321,7 +320,7 @@ static void system_data_cb(Stream *stream, RBuffer *buf, size_t count,
|
|||||||
/// Tracks output received for the current executing shell command, and displays
|
/// Tracks output received for the current executing shell command, and displays
|
||||||
/// a pulsing "..." when output should be skipped. Tracking depends on the
|
/// a pulsing "..." when output should be skipped. Tracking depends on the
|
||||||
/// synchronous/blocking nature of ":!".
|
/// synchronous/blocking nature of ":!".
|
||||||
//
|
///
|
||||||
/// Purpose:
|
/// Purpose:
|
||||||
/// 1. CTRL-C is more responsive. #1234 #5396
|
/// 1. CTRL-C is more responsive. #1234 #5396
|
||||||
/// 2. Improves performance of :! (UI, esp. TUI, is the bottleneck).
|
/// 2. Improves performance of :! (UI, esp. TUI, is the bottleneck).
|
||||||
|
Loading…
Reference in New Issue
Block a user