unix-socket.c 2.39 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
#include "cache.h"
#include "unix-socket.h"

static int unix_stream_socket(void)
{
	int fd = socket(AF_UNIX, SOCK_STREAM, 0);
	if (fd < 0)
		die_errno("unable to create socket");
	return fd;
}

12 13 14 15 16 17 18 19 20
static int chdir_len(const char *orig, int len)
{
	char *path = xmemdupz(orig, len);
	int r = chdir(path);
	free(path);
	return r;
}

struct unix_sockaddr_context {
21
	char *orig_dir;
22 23 24 25
};

static void unix_sockaddr_cleanup(struct unix_sockaddr_context *ctx)
{
26
	if (!ctx->orig_dir)
27 28 29 30 31 32 33 34
		return;
	/*
	 * If we fail, we can't just return an error, since we have
	 * moved the cwd of the whole process, which could confuse calling
	 * code.  We are better off to just die.
	 */
	if (chdir(ctx->orig_dir) < 0)
		die("unable to restore original working directory");
35
	free(ctx->orig_dir);
36 37 38 39
}

static int unix_sockaddr_init(struct sockaddr_un *sa, const char *path,
			      struct unix_sockaddr_context *ctx)
40 41
{
	int size = strlen(path) + 1;
42

43
	ctx->orig_dir = NULL;
44 45 46
	if (size > sizeof(sa->sun_path)) {
		const char *slash = find_last_dir_sep(path);
		const char *dir;
47
		struct strbuf cwd = STRBUF_INIT;
48 49 50 51 52 53 54 55 56 57 58 59 60

		if (!slash) {
			errno = ENAMETOOLONG;
			return -1;
		}

		dir = path;
		path = slash + 1;
		size = strlen(path) + 1;
		if (size > sizeof(sa->sun_path)) {
			errno = ENAMETOOLONG;
			return -1;
		}
61
		if (strbuf_getcwd(&cwd))
62
			return -1;
63
		ctx->orig_dir = strbuf_detach(&cwd, NULL);
64 65 66 67
		if (chdir_len(dir, slash - dir) < 0)
			return -1;
	}

68 69 70
	memset(sa, 0, sizeof(*sa));
	sa->sun_family = AF_UNIX;
	memcpy(sa->sun_path, path, size);
71
	return 0;
72 73 74 75
}

int unix_stream_connect(const char *path)
{
76
	int fd, saved_errno;
77
	struct sockaddr_un sa;
78
	struct unix_sockaddr_context ctx;
79

80 81
	if (unix_sockaddr_init(&sa, path, &ctx) < 0)
		return -1;
82
	fd = unix_stream_socket();
83 84
	if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
		goto fail;
85
	unix_sockaddr_cleanup(&ctx);
86
	return fd;
87 88 89 90 91 92 93

fail:
	saved_errno = errno;
	unix_sockaddr_cleanup(&ctx);
	close(fd);
	errno = saved_errno;
	return -1;
94 95 96 97
}

int unix_stream_listen(const char *path)
{
98
	int fd, saved_errno;
99
	struct sockaddr_un sa;
100
	struct unix_sockaddr_context ctx;
101

102 103
	unlink(path);

104 105
	if (unix_sockaddr_init(&sa, path, &ctx) < 0)
		return -1;
106 107
	fd = unix_stream_socket();

108 109
	if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
		goto fail;
110

111 112
	if (listen(fd, 5) < 0)
		goto fail;
113

114
	unix_sockaddr_cleanup(&ctx);
115
	return fd;
116 117 118 119 120 121 122

fail:
	saved_errno = errno;
	unix_sockaddr_cleanup(&ctx);
	close(fd);
	errno = saved_errno;
	return -1;
123
}