aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/channel.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/channel.c')
-rw-r--r--src/nvim/channel.c241
1 files changed, 129 insertions, 112 deletions
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index 60af11e94b..30243a3102 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -12,11 +12,11 @@
#include "nvim/msgpack_rpc/server.h"
#include "nvim/os/shell.h"
#ifdef WIN32
-# include "nvim/os/pty_conpty_win.h"
# include "nvim/os/os_win_console.h"
+# include "nvim/os/pty_conpty_win.h"
#endif
-#include "nvim/path.h"
#include "nvim/ascii.h"
+#include "nvim/path.h"
static bool did_stdio = false;
@@ -32,13 +32,9 @@ static uint64_t next_chan_id = CHAN_STDERR+1;
/// Teardown the module
void channel_teardown(void)
{
- if (!channels) {
- return;
- }
-
Channel *channel;
- map_foreach_value(channels, channel, {
+ map_foreach_value(&channels, channel, {
channel_close(channel->id, kChannelPartAll, NULL);
});
}
@@ -70,7 +66,7 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error)
if (part == kChannelPartRpc || part == kChannelPartAll) {
close_main = true;
if (chan->is_rpc) {
- rpc_close(chan);
+ rpc_close(chan);
} else if (part == kChannelPartRpc) {
*error = (const char *)e_invstream;
return false;
@@ -82,68 +78,68 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error)
}
switch (chan->streamtype) {
- case kChannelStreamSocket:
- if (!close_main) {
- *error = (const char *)e_invstream;
- return false;
- }
- stream_may_close(&chan->stream.socket);
- break;
+ case kChannelStreamSocket:
+ if (!close_main) {
+ *error = (const char *)e_invstream;
+ return false;
+ }
+ stream_may_close(&chan->stream.socket);
+ break;
- case kChannelStreamProc:
- proc = (Process *)&chan->stream.proc;
- if (part == kChannelPartStdin || close_main) {
- stream_may_close(&proc->in);
- }
- if (part == kChannelPartStdout || close_main) {
- stream_may_close(&proc->out);
- }
- if (part == kChannelPartStderr || part == kChannelPartAll) {
- stream_may_close(&proc->err);
- }
- if (proc->type == kProcessTypePty && part == kChannelPartAll) {
- pty_process_close_master(&chan->stream.pty);
- }
+ case kChannelStreamProc:
+ proc = (Process *)&chan->stream.proc;
+ if (part == kChannelPartStdin || close_main) {
+ stream_may_close(&proc->in);
+ }
+ if (part == kChannelPartStdout || close_main) {
+ stream_may_close(&proc->out);
+ }
+ if (part == kChannelPartStderr || part == kChannelPartAll) {
+ stream_may_close(&proc->err);
+ }
+ if (proc->type == kProcessTypePty && part == kChannelPartAll) {
+ pty_process_close_master(&chan->stream.pty);
+ }
- break;
+ break;
- case kChannelStreamStdio:
- if (part == kChannelPartStdin || close_main) {
- stream_may_close(&chan->stream.stdio.in);
- }
- if (part == kChannelPartStdout || close_main) {
- stream_may_close(&chan->stream.stdio.out);
- }
- if (part == kChannelPartStderr) {
- *error = (const char *)e_invstream;
- return false;
- }
- break;
+ case kChannelStreamStdio:
+ if (part == kChannelPartStdin || close_main) {
+ stream_may_close(&chan->stream.stdio.in);
+ }
+ if (part == kChannelPartStdout || close_main) {
+ stream_may_close(&chan->stream.stdio.out);
+ }
+ if (part == kChannelPartStderr) {
+ *error = (const char *)e_invstream;
+ return false;
+ }
+ break;
- case kChannelStreamStderr:
- if (part != kChannelPartAll && part != kChannelPartStderr) {
- *error = (const char *)e_invstream;
- return false;
- }
- if (!chan->stream.err.closed) {
- chan->stream.err.closed = true;
- // Don't close on exit, in case late error messages
- if (!exiting) {
- fclose(stderr);
- }
- channel_decref(chan);
+ case kChannelStreamStderr:
+ if (part != kChannelPartAll && part != kChannelPartStderr) {
+ *error = (const char *)e_invstream;
+ return false;
+ }
+ if (!chan->stream.err.closed) {
+ chan->stream.err.closed = true;
+ // Don't close on exit, in case late error messages
+ if (!exiting) {
+ fclose(stderr);
}
- break;
+ channel_decref(chan);
+ }
+ break;
- case kChannelStreamInternal:
- if (!close_main) {
- *error = (const char *)e_invstream;
- return false;
- }
- break;
+ case kChannelStreamInternal:
+ if (!close_main) {
+ *error = (const char *)e_invstream;
+ return false;
+ }
+ break;
- default:
- abort();
+ default:
+ abort();
}
return true;
@@ -152,7 +148,6 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error)
/// Initializes the module
void channel_init(void)
{
- channels = pmap_new(uint64_t)();
channel_alloc(kChannelStreamStderr);
rpc_init();
}
@@ -177,7 +172,7 @@ Channel *channel_alloc(ChannelStreamType type)
chan->exit_status = -1;
chan->streamtype = type;
assert(chan->id <= VARNUMBER_MAX);
- pmap_put(uint64_t)(channels, chan->id, chan);
+ pmap_put(uint64_t)(&channels, chan->id, chan);
return chan;
}
@@ -245,11 +240,15 @@ static void free_channel_event(void **argv)
rpc_free(chan);
}
+ if (chan->streamtype == kChannelStreamProc) {
+ process_free(&chan->stream.proc);
+ }
+
callback_reader_free(&chan->on_data);
callback_reader_free(&chan->on_stderr);
callback_free(&chan->on_exit);
- pmap_del(uint64_t)(channels, chan->id);
+ pmap_del(uint64_t)(&channels, chan->id);
multiqueue_free(chan->events);
xfree(chan);
}
@@ -259,7 +258,7 @@ static void channel_destroy_early(Channel *chan)
if ((chan->id != --next_chan_id)) {
abort();
}
- pmap_del(uint64_t)(channels, chan->id);
+ pmap_del(uint64_t)(&channels, chan->id);
chan->id = 0;
if ((--chan->refcount != 0)) {
@@ -289,6 +288,9 @@ static void close_cb(Stream *stream, void *data)
/// `on_stdout` is ignored
/// @param[in] detach True if the job should not be killed when nvim exits,
/// ignored if `pty` is true
+/// @param[in] stdin_mode Stdin mode. Either kChannelStdinPipe to open a
+/// channel for stdin or kChannelStdinNull to leave
+/// stdin disconnected.
/// @param[in] cwd Initial working directory for the job. Nvim's working
/// directory if `cwd` is NULL
/// @param[in] pty_width Width of the pty, ignored if `pty` is false
@@ -299,12 +301,10 @@ static void close_cb(Stream *stream, void *data)
/// < 0 if the job can't start
///
/// @returns [allocated] channel
-Channel *channel_job_start(char **argv, CallbackReader on_stdout,
- CallbackReader on_stderr, Callback on_exit,
- bool pty, bool rpc, bool overlapped, bool detach,
- const char *cwd,
- uint16_t pty_width, uint16_t pty_height,
- dict_T *env, varnumber_T *status_out)
+Channel *channel_job_start(char **argv, CallbackReader on_stdout, CallbackReader on_stderr,
+ Callback on_exit, bool pty, bool rpc, bool overlapped, bool detach,
+ ChannelStdinMode stdin_mode, const char *cwd, uint16_t pty_width,
+ uint16_t pty_height, dict_T *env, varnumber_T *status_out)
{
assert(cwd == NULL || os_isdir_executable(cwd));
@@ -345,7 +345,7 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
proc->overlapped = overlapped;
char *cmd = xstrdup(proc->argv[0]);
- bool has_out, has_err;
+ bool has_in, has_out, has_err;
if (proc->type == kProcessTypePty) {
has_out = true;
has_err = false;
@@ -353,7 +353,17 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
has_out = rpc || callback_reader_set(chan->on_data);
has_err = callback_reader_set(chan->on_stderr);
}
- int status = process_spawn(proc, true, has_out, has_err);
+
+ switch (stdin_mode) {
+ case kChannelStdinPipe:
+ has_in = true;
+ break;
+ case kChannelStdinNull:
+ has_in = false;
+ break;
+ }
+
+ int status = process_spawn(proc, has_in, has_out, has_err);
if (status) {
EMSG3(_(e_jobspawn), os_strerror(status), cmd);
xfree(cmd);
@@ -369,7 +379,9 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
tv_dict_free(proc->env);
}
- wstream_init(&proc->in, 0);
+ if (has_in) {
+ wstream_init(&proc->in, 0);
+ }
if (has_out) {
rstream_init(&proc->out, 0);
}
@@ -395,8 +407,7 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
}
-uint64_t channel_connect(bool tcp, const char *address,
- bool rpc, CallbackReader on_output,
+uint64_t channel_connect(bool tcp, const char *address, bool rpc, CallbackReader on_output,
int timeout, const char **error)
{
Channel *channel;
@@ -456,8 +467,7 @@ void channel_from_connection(SocketWatcher *watcher)
/// Creates an API channel from stdin/stdout. This is used when embedding
/// Neovim
-uint64_t channel_from_stdio(bool rpc, CallbackReader on_output,
- const char **error)
+uint64_t channel_from_stdio(bool rpc, CallbackReader on_output, const char **error)
FUNC_ATTR_NONNULL_ALL
{
if (!headless_mode && !embedded_mode) {
@@ -500,8 +510,8 @@ uint64_t channel_from_stdio(bool rpc, CallbackReader on_output,
}
/// @param data will be consumed
-size_t channel_send(uint64_t id, char *data, size_t len,
- bool data_owned, const char **error)
+size_t channel_send(uint64_t id, char *data, size_t len, bool data_owned, const char **error)
+ FUNC_ATTR_NONNULL_ALL
{
Channel *chan = find_channel(id);
size_t written = 0;
@@ -569,22 +579,20 @@ static inline list_T *buffer_to_tv_list(const char *const buf, const size_t len)
return l;
}
-void on_channel_data(Stream *stream, RBuffer *buf, size_t count,
- void *data, bool eof)
+void on_channel_data(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof)
{
Channel *chan = data;
on_channel_output(stream, chan, buf, count, eof, &chan->on_data);
}
-void on_job_stderr(Stream *stream, RBuffer *buf, size_t count,
- void *data, bool eof)
+void on_job_stderr(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof)
{
Channel *chan = data;
on_channel_output(stream, chan, buf, count, eof, &chan->on_stderr);
}
-static void on_channel_output(Stream *stream, Channel *chan, RBuffer *buf,
- size_t count, bool eof, CallbackReader *reader)
+static void on_channel_output(Stream *stream, Channel *chan, RBuffer *buf, size_t count, bool eof,
+ CallbackReader *reader)
{
// stub variable, to keep reading consistent with the order of events, only
// consider the count parameter.
@@ -683,9 +691,7 @@ static void channel_process_exit_cb(Process *proc, int status, void *data)
{
Channel *chan = data;
if (chan->term) {
- char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN];
- snprintf(msg, sizeof msg, "\r\n[Process exited %d]", proc->status);
- terminal_close(chan->term, msg);
+ terminal_close(chan->term, status);
}
// If process did not exit, we only closed the handle of a detached process.
@@ -839,32 +845,43 @@ Dictionary channel_info(uint64_t id)
const char *stream_desc, *mode_desc;
switch (chan->streamtype) {
- case kChannelStreamProc:
- stream_desc = "job";
- if (chan->stream.proc.type == kProcessTypePty) {
- const char *name = pty_process_tty_name(&chan->stream.pty);
- PUT(info, "pty", STRING_OBJ(cstr_to_string(name)));
+ case kChannelStreamProc: {
+ stream_desc = "job";
+ if (chan->stream.proc.type == kProcessTypePty) {
+ const char *name = pty_process_tty_name(&chan->stream.pty);
+ PUT(info, "pty", STRING_OBJ(cstr_to_string(name)));
+ }
+
+ char **p = chan->stream.proc.argv;
+ Array argv = ARRAY_DICT_INIT;
+ if (p != NULL) {
+ while (*p != NULL) {
+ ADD(argv, STRING_OBJ(cstr_to_string(*p)));
+ p++;
}
- break;
+ }
+ PUT(info, "argv", ARRAY_OBJ(argv));
+ break;
+ }
- case kChannelStreamStdio:
- stream_desc = "stdio";
- break;
+ case kChannelStreamStdio:
+ stream_desc = "stdio";
+ break;
- case kChannelStreamStderr:
- stream_desc = "stderr";
- break;
+ case kChannelStreamStderr:
+ stream_desc = "stderr";
+ break;
- case kChannelStreamInternal:
- PUT(info, "internal", BOOLEAN_OBJ(true));
- FALLTHROUGH;
+ case kChannelStreamInternal:
+ PUT(info, "internal", BOOLEAN_OBJ(true));
+ FALLTHROUGH;
- case kChannelStreamSocket:
- stream_desc = "socket";
- break;
+ case kChannelStreamSocket:
+ stream_desc = "socket";
+ break;
- default:
- abort();
+ default:
+ abort();
}
PUT(info, "stream", STRING_OBJ(cstr_to_string(stream_desc)));
@@ -886,7 +903,7 @@ Array channel_all_info(void)
{
Channel *channel;
Array ret = ARRAY_DICT_INIT;
- map_foreach_value(channels, channel, {
+ map_foreach_value(&channels, channel, {
ADD(ret, DICTIONARY_OBJ(channel_info(channel->id)));
});
return ret;