aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/msgpack_rpc
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2022-06-01 11:28:14 -0700
committerJustin M. Keyes <justinkz@gmail.com>2022-06-15 19:29:51 -0700
commit1f2c2a35ad14cfac002d87073471bd84a52860bf (patch)
treed1830fbb6b8774249da3fbc2f9caae82aa044863 /src/nvim/msgpack_rpc
parentb6467dfc23dab476e256490b8014bbb488684e6b (diff)
downloadrneovim-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/nvim/msgpack_rpc')
-rw-r--r--src/nvim/msgpack_rpc/server.c67
1 files changed, 35 insertions, 32 deletions
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);