Git fork
at reftables-rust 125 lines 3.0 kB view raw
1#include "git-compat-util.h" 2#include "lockfile.h" 3#include "unix-socket.h" 4#include "unix-stream-server.h" 5 6#define DEFAULT_LOCK_TIMEOUT (100) 7 8/* 9 * Try to connect to a unix domain socket at `path` (if it exists) and 10 * see if there is a server listening. 11 * 12 * We don't know if the socket exists, whether a server died and 13 * failed to cleanup, or whether we have a live server listening, so 14 * we "poke" it. 15 * 16 * We immediately hangup without sending/receiving any data because we 17 * don't know anything about the protocol spoken and don't want to 18 * block while writing/reading data. It is sufficient to just know 19 * that someone is listening. 20 */ 21static int is_another_server_alive(const char *path, 22 const struct unix_stream_listen_opts *opts) 23{ 24 int fd = unix_stream_connect(path, opts->disallow_chdir); 25 if (fd >= 0) { 26 close(fd); 27 return 1; 28 } 29 30 return 0; 31} 32 33int unix_ss_create(const char *path, 34 const struct unix_stream_listen_opts *opts, 35 long timeout_ms, 36 struct unix_ss_socket **new_server_socket) 37{ 38 struct lock_file lock = LOCK_INIT; 39 int fd_socket; 40 struct unix_ss_socket *server_socket; 41 42 *new_server_socket = NULL; 43 44 if (timeout_ms < 0) 45 timeout_ms = DEFAULT_LOCK_TIMEOUT; 46 47 /* 48 * Create a lock at "<path>.lock" if we can. 49 */ 50 if (hold_lock_file_for_update_timeout(&lock, path, 0, timeout_ms) < 0) 51 return -1; 52 53 /* 54 * If another server is listening on "<path>" give up. We do not 55 * want to create a socket and steal future connections from them. 56 */ 57 if (is_another_server_alive(path, opts)) { 58 rollback_lock_file(&lock); 59 errno = EADDRINUSE; 60 return -2; 61 } 62 63 /* 64 * Create and bind to a Unix domain socket at "<path>". 65 */ 66 fd_socket = unix_stream_listen(path, opts); 67 if (fd_socket < 0) { 68 int saved_errno = errno; 69 rollback_lock_file(&lock); 70 errno = saved_errno; 71 return -1; 72 } 73 74 server_socket = xcalloc(1, sizeof(*server_socket)); 75 server_socket->path_socket = strdup(path); 76 server_socket->fd_socket = fd_socket; 77 lstat(path, &server_socket->st_socket); 78 79 *new_server_socket = server_socket; 80 81 /* 82 * Always rollback (just delete) "<path>.lock" because we already created 83 * "<path>" as a socket and do not want to commit_lock to do the atomic 84 * rename trick. 85 */ 86 rollback_lock_file(&lock); 87 88 return 0; 89} 90 91void unix_ss_free(struct unix_ss_socket *server_socket) 92{ 93 if (!server_socket) 94 return; 95 96 if (server_socket->fd_socket >= 0) { 97 if (!unix_ss_was_stolen(server_socket)) 98 unlink(server_socket->path_socket); 99 close(server_socket->fd_socket); 100 } 101 102 free(server_socket->path_socket); 103 free(server_socket); 104} 105 106int unix_ss_was_stolen(struct unix_ss_socket *server_socket) 107{ 108 struct stat st_now; 109 110 if (!server_socket) 111 return 0; 112 113 if (lstat(server_socket->path_socket, &st_now) == -1) 114 return 1; 115 116 if (st_now.st_ino != server_socket->st_socket.st_ino) 117 return 1; 118 if (st_now.st_dev != server_socket->st_socket.st_dev) 119 return 1; 120 121 if (!S_ISSOCK(st_now.st_mode)) 122 return 1; 123 124 return 0; 125}