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