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