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