diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2022-06-01 11:28:14 -0700 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2022-06-15 19:29:51 -0700 |
commit | 1f2c2a35ad14cfac002d87073471bd84a52860bf (patch) | |
tree | d1830fbb6b8774249da3fbc2f9caae82aa044863 /src | |
parent | b6467dfc23dab476e256490b8014bbb488684e6b (diff) | |
download | rneovim-1f2c2a35ad14cfac002d87073471bd84a52860bf.tar.gz rneovim-1f2c2a35ad14cfac002d87073471bd84a52860bf.tar.bz2 rneovim-1f2c2a35ad14cfac002d87073471bd84a52860bf.zip |
feat(server): instance "name", store pipes in stdpath(state)
Problem:
- Unix sockets are created in random /tmp dirs.
- /tmp is messy, unclear when OSes actually clear it.
- The generated paths are very ugly. This adds friction to reasoning
about which paths belong to which Nvim instances.
- No way to provide a human-friendly way to identify Nvim instances in
logs or server addresses.
Solution:
- Store unix sockets in stdpath('state')
- Allow --listen "name" and serverstart("name") to given a name (which
is appended to a generated path).
TODO:
- is stdpath(state) the right place?
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/eval/funcs.c | 2 | ||||
-rw-r--r-- | src/nvim/log.c | 22 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/server.c | 67 | ||||
-rw-r--r-- | src/nvim/path.c | 7 |
4 files changed, 56 insertions, 42 deletions
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 25f80758d2..2225076a0a 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -8497,7 +8497,7 @@ static void f_serverstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) address = xstrdup(tv_get_string(argvars)); } } else { - address = server_address_new(); + address = server_address_new(NULL); } int result = server_start(address); diff --git a/src/nvim/log.c b/src/nvim/log.c index bf8e2f9315..d2c7782e5d 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -16,11 +16,13 @@ #include <uv.h> #include "auto/config.h" +#include "nvim/eval.h" #include "nvim/log.h" #include "nvim/main.h" #include "nvim/message.h" #include "nvim/os/os.h" #include "nvim/os/time.h" +#include "nvim/path.h" #include "nvim/types.h" /// Cached location of the expanded log file path decided by log_path_init(). @@ -291,8 +293,7 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, const char *context, return false; } char date_time[20]; - if (strftime(date_time, sizeof(date_time), "%Y-%m-%dT%H:%M:%S", - &local_time) == 0) { + if (strftime(date_time, sizeof(date_time), "%Y-%m-%dT%H:%M:%S", &local_time) == 0) { return false; } @@ -303,14 +304,19 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, const char *context, } // Get a name for this Nvim instance. - if (name[0] == '\0') { - const char *testid = os_getenv("NVIM_TEST"); - const char *parent = os_getenv(ENV_NVIM); - if (testid) { - snprintf(name, sizeof(name), "%s%s", testid ? testid : "", parent ? "/child" : ""); + // TODO(justinmk): expose this as v:name ? + if (starting || name[0] == '\0') { + // Parent servername. + const char *parent = path_tail(os_getenv(ENV_NVIM)); + // Servername. Empty until starting=false. + const char *serv = path_tail(get_vim_var_str(VV_SEND_SERVER)); + if (parent && parent[0] != NUL) { + snprintf(name, sizeof(name), "%s/c", parent); // "/c" indicates child. + } else if (serv && serv[0] != NUL) { + snprintf(name, sizeof(name), "%s", serv ? serv : ""); } else { int64_t pid = os_get_pid(); - snprintf(name, sizeof(name), "%-5" PRId64 "%s", pid, parent ? "/child" : ""); + snprintf(name, sizeof(name), "?.%-5" PRId64, pid); } } diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index 3ced39117a..c9e707aa92 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -42,7 +42,7 @@ bool server_init(const char *listen_addr) int rv = listen_addr ? server_start(listen_addr) : 1; if (0 != rv) { - listen_addr = server_address_new(); + listen_addr = server_address_new(NULL); if (!listen_addr) { return false; } @@ -55,7 +55,7 @@ bool server_init(const char *listen_addr) os_unsetenv(ENV_LISTEN); } - // TODO: this is for logging_spec. Can remove this after nvim_log #7062 is merged. + // TODO(justinmk): this is for logging_spec. Can remove this after nvim_log #7062 is merged. if (os_env_exists("__NVIM_TEST_LOG")) { ELOG("test log message"); } @@ -87,23 +87,26 @@ void server_teardown(void) /// Generates unique address for local server. /// -/// In Windows this is a named pipe in the format -/// \\.\pipe\nvim-<PID>-<COUNTER>. -/// -/// For other systems it is a path returned by vim_tempname(). -/// -/// This function is NOT thread safe -char *server_address_new(void) +/// Named pipe format: +/// - Windows: "\\.\pipe\<name>.<pid>.<counter>" +/// - Other: "~/.local/state/nvim/<name>.<pid>.<counter>" +char *server_address_new(const char *name) { -#ifdef WIN32 static uint32_t count = 0; - char template[ADDRESS_MAX_SIZE]; - snprintf(template, ADDRESS_MAX_SIZE, - "\\\\.\\pipe\\nvim-%" PRIu64 "-%" PRIu32, os_get_pid(), count++); - return xstrdup(template); + char fmt[ADDRESS_MAX_SIZE]; +#ifdef WIN32 + int r = snprintf(fmt, sizeof(fmt), "\\\\.\\pipe\\%s.%" PRIu64 ".%" PRIu32, + name ? name : "nvim", os_get_pid(), count++); #else - return (char *)vim_tempname(); + char *dir = get_xdg_home(kXDGStateHome); + int r = snprintf(fmt, sizeof(fmt), "%s/%s.%" PRIu64 ".%" PRIu32, + dir, name ? name : "nvim", os_get_pid(), count++); + xfree(dir); #endif + if ((size_t)r >= sizeof(fmt)) { + ELOG("truncated server address"); + } + return xstrdup(fmt); } /// Check if this instance owns a pipe address. @@ -118,35 +121,35 @@ bool server_owns_pipe_address(const char *path) return false; } -/// Starts listening for API calls. -/// -/// The socket type is determined by parsing `endpoint`: If it's a valid IPv4 -/// or IPv6 address in 'ip:[port]' format, then it will be a TCP socket. -/// Otherwise it will be a Unix socket or named pipe (Windows). +/// Starts listening for RPC calls. /// -/// If no port is given, a random one will be assigned. +/// Socket type is decided by the format of `addr`: +/// - TCP socket if it looks like an IPv4/6 address ("ip:[port]"). +/// - If [port] is omitted, a random one is assigned. +/// - Unix socket (or named pipe on Windows) otherwise. +/// - If the name doesn't contain slashes it is appended to a generated path. #8519 /// -/// @param endpoint Address of the server. Either a 'ip:[port]' string or an -/// arbitrary identifier (trimmed to 256 bytes) for the Unix -/// socket or named pipe. -/// @returns 0: success, 1: validation error, 2: already listening, -/// -errno: failed to bind or listen. -int server_start(const char *endpoint) +/// @param addr Server address: a "ip:[port]" string or arbitrary name or filepath (max 256 bytes) +/// for the Unix socket or named pipe. +/// @returns 0: success, 1: validation error, 2: already listening, -errno: failed to bind/listen. +int server_start(const char *addr) { - if (endpoint == NULL || endpoint[0] == '\0') { - WLOG("Empty or NULL endpoint"); + if (addr == NULL || addr[0] == '\0') { + WLOG("Empty or NULL address"); return 1; } + bool isname = !strstr(addr, ":") && !strstr(addr, "/") && !strstr(addr, "\\"); + char *addr_gen = isname ? server_address_new(addr) : NULL; SocketWatcher *watcher = xmalloc(sizeof(SocketWatcher)); - - int result = socket_watcher_init(&main_loop, watcher, endpoint); + int result = socket_watcher_init(&main_loop, watcher, isname ? addr_gen : addr); + xfree(addr_gen); if (result < 0) { xfree(watcher); return result; } - // Check if a watcher for the endpoint already exists + // Check if a watcher for the address already exists. for (int i = 0; i < watchers.ga_len; i++) { if (!strcmp(watcher->addr, ((SocketWatcher **)watchers.ga_data)[i]->addr)) { ELOG("Already listening on %s", watcher->addr); diff --git a/src/nvim/path.c b/src/nvim/path.c index 7f47ce083d..9859ca7daa 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -88,7 +88,12 @@ FileComparison path_full_compare(char_u *const s1, char_u *const s2, const bool return kDifferentFiles; } -/// Gets the tail (i.e., the filename segment) of a path `fname`. +/// Gets the tail (filename segment) of path `fname`. +/// +/// Examples: +/// - "dir/file.txt" => "file.txt" +/// - "file.txt" => "file.txt" +/// - "dir/" => "" /// /// @return pointer just past the last path separator (empty string, if fname /// ends in a slash), or empty string if fname is NULL. |