]>
Commit | Line | Data |
---|---|---|
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 | } |