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