1 #include "cache.h"
2 #include "unix-socket.h"
3 
4 #define DEFAULT_UNIX_STREAM_LISTEN_BACKLOG (5)
5 
chdir_len(const char * orig,int len)6 static int chdir_len(const char *orig, int len)
7 {
8 	char *path = xmemdupz(orig, len);
9 	int r = chdir(path);
10 	free(path);
11 	return r;
12 }
13 
14 struct unix_sockaddr_context {
15 	char *orig_dir;
16 };
17 
unix_sockaddr_cleanup(struct unix_sockaddr_context * ctx)18 static void unix_sockaddr_cleanup(struct unix_sockaddr_context *ctx)
19 {
20 	if (!ctx->orig_dir)
21 		return;
22 	/*
23 	 * If we fail, we can't just return an error, since we have
24 	 * moved the cwd of the whole process, which could confuse calling
25 	 * code.  We are better off to just die.
26 	 */
27 	if (chdir(ctx->orig_dir) < 0)
28 		die("unable to restore original working directory");
29 	free(ctx->orig_dir);
30 }
31 
unix_sockaddr_init(struct sockaddr_un * sa,const char * path,struct unix_sockaddr_context * ctx,int disallow_chdir)32 static int unix_sockaddr_init(struct sockaddr_un *sa, const char *path,
33 			      struct unix_sockaddr_context *ctx,
34 			      int disallow_chdir)
35 {
36 	int size = strlen(path) + 1;
37 
38 	ctx->orig_dir = NULL;
39 	if (size > sizeof(sa->sun_path)) {
40 		const char *slash;
41 		const char *dir;
42 		struct strbuf cwd = STRBUF_INIT;
43 
44 		if (disallow_chdir) {
45 			errno = ENAMETOOLONG;
46 			return -1;
47 		}
48 
49 		slash = find_last_dir_sep(path);
50 		if (!slash) {
51 			errno = ENAMETOOLONG;
52 			return -1;
53 		}
54 
55 		dir = path;
56 		path = slash + 1;
57 		size = strlen(path) + 1;
58 		if (size > sizeof(sa->sun_path)) {
59 			errno = ENAMETOOLONG;
60 			return -1;
61 		}
62 		if (strbuf_getcwd(&cwd))
63 			return -1;
64 		ctx->orig_dir = strbuf_detach(&cwd, NULL);
65 		if (chdir_len(dir, slash - dir) < 0)
66 			return -1;
67 	}
68 
69 	memset(sa, 0, sizeof(*sa));
70 	sa->sun_family = AF_UNIX;
71 	memcpy(sa->sun_path, path, size);
72 	return 0;
73 }
74 
unix_stream_connect(const char * path,int disallow_chdir)75 int unix_stream_connect(const char *path, int disallow_chdir)
76 {
77 	int fd = -1, saved_errno;
78 	struct sockaddr_un sa;
79 	struct unix_sockaddr_context ctx;
80 
81 	if (unix_sockaddr_init(&sa, path, &ctx, disallow_chdir) < 0)
82 		return -1;
83 	fd = socket(AF_UNIX, SOCK_STREAM, 0);
84 	if (fd < 0)
85 		goto fail;
86 
87 	if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
88 		goto fail;
89 	unix_sockaddr_cleanup(&ctx);
90 	return fd;
91 
92 fail:
93 	saved_errno = errno;
94 	if (fd != -1)
95 		close(fd);
96 	unix_sockaddr_cleanup(&ctx);
97 	errno = saved_errno;
98 	return -1;
99 }
100 
unix_stream_listen(const char * path,const struct unix_stream_listen_opts * opts)101 int unix_stream_listen(const char *path,
102 		       const struct unix_stream_listen_opts *opts)
103 {
104 	int fd = -1, saved_errno;
105 	int backlog;
106 	struct sockaddr_un sa;
107 	struct unix_sockaddr_context ctx;
108 
109 	unlink(path);
110 
111 	if (unix_sockaddr_init(&sa, path, &ctx, opts->disallow_chdir) < 0)
112 		return -1;
113 	fd = socket(AF_UNIX, SOCK_STREAM, 0);
114 	if (fd < 0)
115 		goto fail;
116 
117 	if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
118 		goto fail;
119 
120 	backlog = opts->listen_backlog_size;
121 	if (backlog <= 0)
122 		backlog = DEFAULT_UNIX_STREAM_LISTEN_BACKLOG;
123 	if (listen(fd, backlog) < 0)
124 		goto fail;
125 
126 	unix_sockaddr_cleanup(&ctx);
127 	return fd;
128 
129 fail:
130 	saved_errno = errno;
131 	if (fd != -1)
132 		close(fd);
133 	unix_sockaddr_cleanup(&ctx);
134 	errno = saved_errno;
135 	return -1;
136 }
137