Git 2.32
[git] / unix-stream-server.c
1 #include "cache.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  */
21 static 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
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 {
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
91 void 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
106 int 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 }