@@ -729,44 +729,51 @@ static void *accept_thread_proc(void *_accept_thread_data)
*/
#define LISTEN_BACKLOG (50)
-static struct unix_stream_server_socket *create_listener_socket(
+static int create_listener_socket(
const char *path,
- const struct ipc_server_opts *ipc_opts)
+ const struct ipc_server_opts *ipc_opts,
+ struct unix_stream_server_socket **new_server_socket)
{
struct unix_stream_server_socket *server_socket = NULL;
struct unix_stream_listen_opts uslg_opts = UNIX_STREAM_LISTEN_OPTS_INIT;
+ int ret;
uslg_opts.listen_backlog_size = LISTEN_BACKLOG;
uslg_opts.disallow_chdir = ipc_opts->uds_disallow_chdir;
- server_socket = unix_stream_server__listen_with_lock(path, &uslg_opts);
- if (!server_socket)
- return NULL;
+ ret = unix_stream_server__create(path, &uslg_opts, &server_socket);
+ if (ret)
+ return ret;
if (set_socket_blocking_flag(server_socket->fd_socket, 1)) {
int saved_errno = errno;
- error_errno(_("could not set listener socket nonblocking '%s'"),
- path);
unix_stream_server__free(server_socket);
errno = saved_errno;
- return NULL;
+ return -1;
}
+ *new_server_socket = server_socket;
+
trace2_data_string("ipc-server", NULL, "listen-with-lock", path);
- return server_socket;
+ return 0;
}
-static struct unix_stream_server_socket *setup_listener_socket(
+static int setup_listener_socket(
const char *path,
- const struct ipc_server_opts *ipc_opts)
+ const struct ipc_server_opts *ipc_opts,
+ struct unix_stream_server_socket **new_server_socket)
{
- struct unix_stream_server_socket *server_socket;
+ int ret, saved_errno;
trace2_region_enter("ipc-server", "create-listener_socket", NULL);
- server_socket = create_listener_socket(path, ipc_opts);
+
+ ret = create_listener_socket(path, ipc_opts, new_server_socket);
+
+ saved_errno = errno;
trace2_region_leave("ipc-server", "create-listener_socket", NULL);
+ errno = saved_errno;
- return server_socket;
+ return ret;
}
/*
@@ -781,6 +788,7 @@ int ipc_server_run_async(struct ipc_server_data **returned_server_data,
struct ipc_server_data *server_data;
int sv[2];
int k;
+ int ret;
int nr_threads = opts->nr_threads;
*returned_server_data = NULL;
@@ -792,25 +800,23 @@ int ipc_server_run_async(struct ipc_server_data **returned_server_data,
* connection or a shutdown request without spinning.
*/
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0)
- return error_errno(_("could not create socketpair for '%s'"),
- path);
+ return -1;
if (set_socket_blocking_flag(sv[1], 1)) {
int saved_errno = errno;
close(sv[0]);
close(sv[1]);
errno = saved_errno;
- return error_errno(_("making socketpair nonblocking '%s'"),
- path);
+ return -1;
}
- server_socket = setup_listener_socket(path, opts);
- if (!server_socket) {
+ ret = setup_listener_socket(path, opts, &server_socket);
+ if (ret) {
int saved_errno = errno;
close(sv[0]);
close(sv[1]);
errno = saved_errno;
- return -1;
+ return ret;
}
server_data = xcalloc(1, sizeof(*server_data));
@@ -614,14 +614,16 @@ int ipc_server_run_async(struct ipc_server_data **returned_server_data,
*returned_server_data = NULL;
ret = initialize_pipe_name(path, wpath, ARRAY_SIZE(wpath));
- if (ret < 0)
- return error(
- _("could not create normalized wchar_t path for '%s'"),
- path);
+ if (ret < 0) {
+ errno = EINVAL;
+ return -1;
+ }
hPipeFirst = create_new_pipe(wpath, 1);
- if (hPipeFirst == INVALID_HANDLE_VALUE)
- return error(_("IPC server already running on '%s'"), path);
+ if (hPipeFirst == INVALID_HANDLE_VALUE) {
+ errno = EADDRINUSE;
+ return -2;
+ }
server_data = xcalloc(1, sizeof(*server_data));
server_data->magic = MAGIC_SERVER_DATA;
@@ -178,6 +178,8 @@ struct ipc_server_opts
*
* Returns 0 if the asynchronous server pool was started successfully.
* Returns -1 if not.
+ * Returns -2 if we could not startup because another server is using
+ * the socket or named pipe.
*
* When a client IPC message is received, the `application_cb` will be
* called (possibly on a random thread) to handle the message and
@@ -217,6 +219,8 @@ void ipc_server_free(struct ipc_server_data *server_data);
*
* Returns 0 after the server has completed successfully.
* Returns -1 if the server cannot be started.
+ * Returns -2 if we could not startup because another server is using
+ * the socket or named pipe.
*
* When a client IPC message is received, the `application_cb` will be
* called (possibly on a random thread) to handle the message and
@@ -219,6 +219,8 @@ static int test_app_cb(void *application_data,
*/
static int daemon__run_server(const char *path, int argc, const char **argv)
{
+ int ret;
+
struct ipc_server_opts opts = {
.nr_threads = 5
};
@@ -243,7 +245,13 @@ static int daemon__run_server(const char *path, int argc, const char **argv)
* instance data, so pass an arbitrary pointer (that we'll later
* verify made the round trip).
*/
- return ipc_server_run(path, &opts, test_app_cb, (void*)&my_app_data);
+ ret = ipc_server_run(path, &opts, test_app_cb, (void*)&my_app_data);
+ if (ret == -2)
+ error(_("socket/pipe already in use: '%s'"), path);
+ else if (ret == -1)
+ error_errno(_("could not start server on: '%s'"), path);
+
+ return ret;
}
#ifndef GIT_WINDOWS_NATIVE
@@ -5,41 +5,44 @@
#define DEFAULT_UNIX_STREAM_LISTEN_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)
{
- struct stat st;
- int fd;
-
- if (!lstat(path, &st) && S_ISSOCK(st.st_mode)) {
- /*
- * A socket-inode exists on disk at `path`, but we
- * don't know whether it belongs to an active server
- * or whether the last server died without cleaning
- * up.
- *
- * Poke it with a trivial connection to try to find
- * out.
- */
- fd = unix_stream_connect(path, opts->disallow_chdir);
- if (fd >= 0) {
- close(fd);
- return 1;
- }
+ int fd = unix_stream_connect(path, opts->disallow_chdir);
+
+ if (fd >= 0) {
+ close(fd);
+ return 1;
}
return 0;
}
-struct unix_stream_server_socket *unix_stream_server__listen_with_lock(
+int unix_stream_server__create(
const char *path,
- const struct unix_stream_listen_opts *opts)
+ const struct unix_stream_listen_opts *opts,
+ struct unix_stream_server_socket **new_server_socket)
{
struct lock_file lock = LOCK_INIT;
long timeout;
int fd_socket;
struct unix_stream_server_socket *server_socket;
+ *new_server_socket = NULL;
+
timeout = opts->timeout_ms;
if (opts->timeout_ms <= 0)
timeout = DEFAULT_UNIX_STREAM_LISTEN_TIMEOUT;
@@ -47,20 +50,17 @@ struct unix_stream_server_socket *unix_stream_server__listen_with_lock(
/*
* Create a lock at "<path>.lock" if we can.
*/
- if (hold_lock_file_for_update_timeout(&lock, path, 0, timeout) < 0) {
- error_errno(_("could not lock listener socket '%s'"), path);
- return NULL;
- }
+ if (hold_lock_file_for_update_timeout(&lock, path, 0, timeout) < 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)) {
- errno = EADDRINUSE;
- error_errno(_("listener socket already in use '%s'"), path);
rollback_lock_file(&lock);
- return NULL;
+ errno = EADDRINUSE;
+ return -2;
}
/*
@@ -68,9 +68,10 @@ struct unix_stream_server_socket *unix_stream_server__listen_with_lock(
*/
fd_socket = unix_stream_listen(path, opts);
if (fd_socket < 0) {
- error_errno(_("could not create listener socket '%s'"), path);
+ int saved_errno = errno;
rollback_lock_file(&lock);
- return NULL;
+ errno = saved_errno;
+ return -1;
}
server_socket = xcalloc(1, sizeof(*server_socket));
@@ -78,6 +79,8 @@ struct unix_stream_server_socket *unix_stream_server__listen_with_lock(
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
@@ -85,7 +88,7 @@ struct unix_stream_server_socket *unix_stream_server__listen_with_lock(
*/
rollback_lock_file(&lock);
- return server_socket;
+ return 0;
}
void unix_stream_server__free(
@@ -12,10 +12,13 @@ struct unix_stream_server_socket {
/*
* Create a Unix Domain Socket at the given path under the protection
* of a '.lock' lockfile.
+ *
+ * Returns 0 on success, -1 on error, -2 if socket is in use.
*/
-struct unix_stream_server_socket *unix_stream_server__listen_with_lock(
+int unix_stream_server__create(
const char *path,
- const struct unix_stream_listen_opts *opts);
+ const struct unix_stream_listen_opts *opts,
+ struct unix_stream_server_socket **server_socket);
/*
* Close and delete the socket.