]>
Commit | Line | Data |
---|---|---|
1 | #include "cache.h" | |
2 | #include "unix-socket.h" | |
3 | ||
4 | #define DEFAULT_UNIX_STREAM_LISTEN_BACKLOG (5) | |
5 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | } |