mirror of
https://github.com/neovim/neovim.git
synced 2025-10-07 02:16:31 +00:00
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?
This commit is contained in:
@@ -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.
|
||||
/// Starts listening for RPC 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).
|
||||
/// 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
|
||||
///
|
||||
/// If no port is given, a random one will be assigned.
|
||||
///
|
||||
/// @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);
|
||||
|
Reference in New Issue
Block a user