git
/
unix-stream-server.c
125 строк · 2.9 Кб
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,22const struct unix_stream_listen_opts *opts)23{
24int fd = unix_stream_connect(path, opts->disallow_chdir);25if (fd >= 0) {26close(fd);27return 1;28}29
30return 0;31}
32
33int unix_ss_create(const char *path,34const struct unix_stream_listen_opts *opts,35long timeout_ms,36struct unix_ss_socket **new_server_socket)37{
38struct lock_file lock = LOCK_INIT;39int fd_socket;40struct unix_ss_socket *server_socket;41
42*new_server_socket = NULL;43
44if (timeout_ms < 0)45timeout_ms = DEFAULT_LOCK_TIMEOUT;46
47/*48* Create a lock at "<path>.lock" if we can.
49*/
50if (hold_lock_file_for_update_timeout(&lock, path, 0, timeout_ms) < 0)51return -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*/
57if (is_another_server_alive(path, opts)) {58rollback_lock_file(&lock);59errno = EADDRINUSE;60return -2;61}62
63/*64* Create and bind to a Unix domain socket at "<path>".
65*/
66fd_socket = unix_stream_listen(path, opts);67if (fd_socket < 0) {68int saved_errno = errno;69rollback_lock_file(&lock);70errno = saved_errno;71return -1;72}73
74server_socket = xcalloc(1, sizeof(*server_socket));75server_socket->path_socket = strdup(path);76server_socket->fd_socket = fd_socket;77lstat(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*/
86rollback_lock_file(&lock);87
88return 0;89}
90
91void unix_ss_free(struct unix_ss_socket *server_socket)92{
93if (!server_socket)94return;95
96if (server_socket->fd_socket >= 0) {97if (!unix_ss_was_stolen(server_socket))98unlink(server_socket->path_socket);99close(server_socket->fd_socket);100}101
102free(server_socket->path_socket);103free(server_socket);104}
105
106int unix_ss_was_stolen(struct unix_ss_socket *server_socket)107{
108struct stat st_now;109
110if (!server_socket)111return 0;112
113if (lstat(server_socket->path_socket, &st_now) == -1)114return 1;115
116if (st_now.st_ino != server_socket->st_socket.st_ino)117return 1;118if (st_now.st_dev != server_socket->st_socket.st_dev)119return 1;120
121if (!S_ISSOCK(st_now.st_mode))122return 1;123
124return 0;125}
126