server.c (8492B)
1 #include <inttypes.h> 2 #include <stdbool.h> 3 #include <stdio.h> 4 #include <string.h> 5 #include <uv.h> 6 7 #include "nvim/ascii_defs.h" 8 #include "nvim/channel.h" 9 #include "nvim/eval/vars.h" 10 #include "nvim/event/defs.h" 11 #include "nvim/event/socket.h" 12 #include "nvim/garray.h" 13 #include "nvim/garray_defs.h" 14 #include "nvim/globals.h" 15 #include "nvim/log.h" 16 #include "nvim/main.h" 17 #include "nvim/memory.h" 18 #include "nvim/msgpack_rpc/server.h" 19 #include "nvim/os/os.h" 20 #include "nvim/os/os_defs.h" 21 #include "nvim/os/stdpaths_defs.h" 22 #include "nvim/path.h" 23 #include "nvim/types_defs.h" 24 25 #define MAX_CONNECTIONS 32 26 #define ENV_LISTEN "NVIM_LISTEN_ADDRESS" // deprecated 27 28 static garray_T watchers = GA_EMPTY_INIT_VALUE; 29 30 #include "msgpack_rpc/server.c.generated.h" 31 32 /// Initializes resources, handles `--listen`, starts the primary server at v:servername. 33 /// 34 /// @returns true on success, false on fatal error (message stored in IObuff) 35 bool server_init(const char *listen_addr) 36 { 37 bool ok = true; 38 bool must_free = false; 39 TriState user_arg = kTrue; // User-provided --listen arg. 40 ga_init(&watchers, sizeof(SocketWatcher *), 1); 41 42 // $NVIM_LISTEN_ADDRESS (deprecated) 43 if (!listen_addr || listen_addr[0] == '\0') { 44 if (os_env_exists(ENV_LISTEN, true)) { 45 user_arg = kFalse; // User-provided env var. 46 listen_addr = os_getenv(ENV_LISTEN); 47 } else { 48 user_arg = kNone; // Autogenerated server address. 49 listen_addr = server_address_new(NULL); 50 } 51 must_free = true; 52 } 53 54 int rv = server_start(listen_addr); 55 56 // TODO(justinmk): this is for log_spec. Can remove this after nvim_log #7062 is merged. 57 if (os_env_exists("__NVIM_TEST_LOG", false)) { 58 ELOG("test log message"); 59 } 60 61 if (rv == 0 || user_arg == kNone) { 62 // The autogenerated servername can fail if the user has a broken $XDG_RUNTIME_DIR. #30282 63 // But that is not fatal (startup will continue, logged in $NVIM_LOGFILE, empty v:servername). 64 goto end; 65 } 66 67 (void)snprintf(IObuff, IOSIZE, 68 user_arg == 69 kTrue ? "Failed to --listen: %s: \"%s\"" 70 : "Failed $NVIM_LISTEN_ADDRESS: %s: \"%s\"", 71 rv < 0 ? os_strerror(rv) : (rv == 1 ? "empty address" : "?"), 72 listen_addr); 73 ok = false; 74 75 end: 76 if (os_env_exists(ENV_LISTEN, false)) { 77 // Unset $NVIM_LISTEN_ADDRESS, it's a liability hereafter. It is "input only", it must not be 78 // leaked to child jobs or :terminal. 79 os_unsetenv(ENV_LISTEN); 80 } 81 82 if (must_free) { 83 xfree((char *)listen_addr); 84 } 85 86 return ok; 87 } 88 89 /// Teardown a single server 90 static void close_socket_watcher(SocketWatcher **watcher) 91 { 92 socket_watcher_close(*watcher, free_server); 93 } 94 95 /// Sets the "primary address" (v:servername and $NVIM) to the first server in 96 /// the server list, or unsets if no servers are known. 97 static void set_vservername(garray_T *srvs) 98 { 99 char *default_server = (srvs->ga_len > 0) 100 ? ((SocketWatcher **)srvs->ga_data)[0]->addr 101 : NULL; 102 set_vim_var_string(VV_SEND_SERVER, default_server, -1); 103 } 104 105 /// Teardown the server module 106 void server_teardown(void) 107 { 108 GA_DEEP_CLEAR(&watchers, SocketWatcher *, close_socket_watcher); 109 } 110 111 /// Generates unique address for local server. 112 /// 113 /// Named pipe format: 114 /// - Windows: "\\.\pipe\<name>.<pid>.<counter>" 115 /// - Other: "/tmp/nvim.user/xxx/<name>.<pid>.<counter>" 116 char *server_address_new(const char *name) 117 FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET 118 { 119 static uint32_t count = 0; 120 char fmt[ADDRESS_MAX_SIZE]; 121 #ifdef MSWIN 122 (void)get_appname(true); // Writes appname to NameBuf. 123 int r = snprintf(fmt, sizeof(fmt), "\\\\.\\pipe\\%s.%" PRIu64 ".%" PRIu32, 124 name ? name : NameBuff, os_get_pid(), count++); 125 #else 126 char *dir = stdpaths_get_xdg_var(kXDGRuntimeDir); 127 (void)get_appname(true); // Writes appname to NameBuf. 128 int r = snprintf(fmt, sizeof(fmt), "%s/%s.%" PRIu64 ".%" PRIu32, 129 dir, name ? name : NameBuff, os_get_pid(), count++); 130 xfree(dir); 131 #endif 132 if ((size_t)r >= sizeof(fmt)) { 133 ELOG("truncated server address: %.40s...", fmt); 134 } 135 return xstrdup(fmt); 136 } 137 138 /// Check if this instance owns a pipe address (loopback). 139 bool server_owns_pipe_address(const char *address) 140 { 141 bool result = false; 142 char *path = fix_fname(address); 143 for (int i = 0; i < watchers.ga_len; i++) { 144 char *addr = fix_fname(((SocketWatcher **)watchers.ga_data)[i]->addr); 145 result = strequal(path, addr); 146 xfree(addr); 147 if (result) { 148 break; 149 } 150 } 151 xfree(path); 152 return result; 153 } 154 155 /// Starts listening for RPC calls. 156 /// 157 /// Socket type is decided by the format of `addr`: 158 /// - TCP socket if it looks like an IPv4/6 address ("ip:[port]"). 159 /// - If [port] is omitted, a random one is assigned. 160 /// - Unix socket (or named pipe on Windows) otherwise. 161 /// - If the name doesn't contain slashes it is appended to a generated path. #8519 162 /// 163 /// @param addr Server address: a "ip:[port]" string or arbitrary name or filepath (max 256 bytes) 164 /// for the Unix socket or named pipe. 165 /// @returns 0: success, 1: validation error, 2: already listening, -errno: failed to bind/listen. 166 int server_start(const char *addr) 167 { 168 if (addr == NULL || addr[0] == NUL) { 169 WLOG("Empty or NULL address"); 170 return 1; 171 } 172 173 bool isname = !strstr(addr, ":") && !strstr(addr, "/") && !strstr(addr, "\\"); 174 char *addr_gen = isname ? server_address_new(addr) : NULL; 175 SocketWatcher *watcher = xmalloc(sizeof(SocketWatcher)); 176 int result = socket_watcher_init(&main_loop, watcher, isname ? addr_gen : addr); 177 xfree(addr_gen); 178 if (result < 0) { 179 xfree(watcher); 180 return result; 181 } 182 183 // Check if a watcher for the address already exists. 184 for (int i = 0; i < watchers.ga_len; i++) { 185 if (!strcmp(watcher->addr, ((SocketWatcher **)watchers.ga_data)[i]->addr)) { 186 ELOG("Already listening on %s", watcher->addr); 187 if (watcher->stream->type == UV_TCP) { 188 uv_freeaddrinfo(watcher->uv.tcp.addrinfo); 189 } 190 socket_watcher_close(watcher, free_server); 191 return 2; 192 } 193 } 194 195 result = socket_watcher_start(watcher, MAX_CONNECTIONS, connection_cb); 196 if (result < 0) { 197 WLOG("Failed to start server: %s: %s", uv_strerror(result), watcher->addr); 198 socket_watcher_close(watcher, free_server); 199 return result; 200 } 201 202 // Add the watcher to the list. 203 ga_grow(&watchers, 1); 204 ((SocketWatcher **)watchers.ga_data)[watchers.ga_len++] = watcher; 205 206 // Update v:servername, if not set. 207 if (strlen(get_vim_var_str(VV_SEND_SERVER)) == 0) { 208 set_vservername(&watchers); 209 } 210 211 return 0; 212 } 213 214 /// Stops listening on the address specified by `endpoint`. 215 /// 216 /// @param endpoint Address of the server. 217 bool server_stop(char *endpoint) 218 { 219 SocketWatcher *watcher; 220 bool watcher_found = false; 221 char addr[ADDRESS_MAX_SIZE]; 222 223 // Trim to `ADDRESS_MAX_SIZE` 224 xstrlcpy(addr, endpoint, sizeof(addr)); 225 226 int i = 0; // Index of the server whose address equals addr. 227 for (; i < watchers.ga_len; i++) { 228 watcher = ((SocketWatcher **)watchers.ga_data)[i]; 229 if (strcmp(addr, watcher->addr) == 0) { 230 watcher_found = true; 231 break; 232 } 233 } 234 235 if (!watcher_found) { 236 WLOG("Not listening on %s", addr); 237 return false; 238 } 239 240 socket_watcher_close(watcher, free_server); 241 242 // Remove this server from the list by swapping it with the last item. 243 if (i != watchers.ga_len - 1) { 244 ((SocketWatcher **)watchers.ga_data)[i] = 245 ((SocketWatcher **)watchers.ga_data)[watchers.ga_len - 1]; 246 } 247 watchers.ga_len--; 248 249 // Bump v:servername to the next available server, if any. 250 if (strequal(addr, get_vim_var_str(VV_SEND_SERVER))) { 251 set_vservername(&watchers); 252 } 253 254 return true; 255 } 256 257 /// Returns an allocated array of server addresses. 258 /// @param[out] size The size of the returned array. 259 char **server_address_list(size_t *size) 260 FUNC_ATTR_NONNULL_ALL 261 { 262 if ((*size = (size_t)watchers.ga_len) == 0) { 263 return NULL; 264 } 265 266 char **addrs = xcalloc((size_t)watchers.ga_len, sizeof(const char *)); 267 for (int i = 0; i < watchers.ga_len; i++) { 268 addrs[i] = xstrdup(((SocketWatcher **)watchers.ga_data)[i]->addr); 269 } 270 return addrs; 271 } 272 273 static void connection_cb(SocketWatcher *watcher, int result, void *data) 274 { 275 if (result) { 276 ELOG("Failed to accept connection: %s", uv_strerror(result)); 277 return; 278 } 279 280 channel_from_connection(watcher); 281 } 282 283 static void free_server(SocketWatcher *watcher, void *data) 284 { 285 xfree(watcher); 286 }