##// END OF EJS Templates
chg: forward umask from client to server...
Jun Wu -
r28160:098cb7bd default
parent child Browse files
Show More
@@ -1,527 +1,542
1 /*
1 /*
2 * A command server client that uses Unix domain socket
2 * A command server client that uses Unix domain socket
3 *
3 *
4 * Copyright (c) 2011 Yuya Nishihara <yuya@tcha.org>
4 * Copyright (c) 2011 Yuya Nishihara <yuya@tcha.org>
5 *
5 *
6 * This software may be used and distributed according to the terms of the
6 * This software may be used and distributed according to the terms of the
7 * GNU General Public License version 2 or any later version.
7 * GNU General Public License version 2 or any later version.
8 */
8 */
9
9
10 #include <arpa/inet.h> /* for ntohl(), htonl() */
10 #include <arpa/inet.h> /* for ntohl(), htonl() */
11 #include <assert.h>
11 #include <assert.h>
12 #include <ctype.h>
12 #include <ctype.h>
13 #include <errno.h>
13 #include <errno.h>
14 #include <fcntl.h>
14 #include <fcntl.h>
15 #include <signal.h>
15 #include <signal.h>
16 #include <stdint.h>
16 #include <stdint.h>
17 #include <stdio.h>
17 #include <stdio.h>
18 #include <stdlib.h>
18 #include <stdlib.h>
19 #include <string.h>
19 #include <string.h>
20 #include <sys/socket.h>
20 #include <sys/socket.h>
21 #include <sys/stat.h>
21 #include <sys/stat.h>
22 #include <sys/un.h>
22 #include <sys/un.h>
23 #include <unistd.h>
23 #include <unistd.h>
24
24
25 #include "hgclient.h"
25 #include "hgclient.h"
26 #include "util.h"
26 #include "util.h"
27
27
28 enum {
28 enum {
29 CAP_GETENCODING = 0x0001,
29 CAP_GETENCODING = 0x0001,
30 CAP_RUNCOMMAND = 0x0002,
30 CAP_RUNCOMMAND = 0x0002,
31 /* cHg extension: */
31 /* cHg extension: */
32 CAP_ATTACHIO = 0x0100,
32 CAP_ATTACHIO = 0x0100,
33 CAP_CHDIR = 0x0200,
33 CAP_CHDIR = 0x0200,
34 CAP_GETPAGER = 0x0400,
34 CAP_GETPAGER = 0x0400,
35 CAP_SETENV = 0x0800,
35 CAP_SETENV = 0x0800,
36 CAP_SETUMASK = 0x1000,
36 };
37 };
37
38
38 typedef struct {
39 typedef struct {
39 const char *name;
40 const char *name;
40 unsigned int flag;
41 unsigned int flag;
41 } cappair_t;
42 } cappair_t;
42
43
43 static const cappair_t captable[] = {
44 static const cappair_t captable[] = {
44 {"getencoding", CAP_GETENCODING},
45 {"getencoding", CAP_GETENCODING},
45 {"runcommand", CAP_RUNCOMMAND},
46 {"runcommand", CAP_RUNCOMMAND},
46 {"attachio", CAP_ATTACHIO},
47 {"attachio", CAP_ATTACHIO},
47 {"chdir", CAP_CHDIR},
48 {"chdir", CAP_CHDIR},
48 {"getpager", CAP_GETPAGER},
49 {"getpager", CAP_GETPAGER},
49 {"setenv", CAP_SETENV},
50 {"setenv", CAP_SETENV},
51 {"setumask", CAP_SETUMASK},
50 {NULL, 0}, /* terminator */
52 {NULL, 0}, /* terminator */
51 };
53 };
52
54
53 typedef struct {
55 typedef struct {
54 char ch;
56 char ch;
55 char *data;
57 char *data;
56 size_t maxdatasize;
58 size_t maxdatasize;
57 size_t datasize;
59 size_t datasize;
58 } context_t;
60 } context_t;
59
61
60 struct hgclient_tag_ {
62 struct hgclient_tag_ {
61 int sockfd;
63 int sockfd;
62 pid_t pid;
64 pid_t pid;
63 context_t ctx;
65 context_t ctx;
64 unsigned int capflags;
66 unsigned int capflags;
65 };
67 };
66
68
67 static const size_t defaultdatasize = 4096;
69 static const size_t defaultdatasize = 4096;
68
70
69 static void initcontext(context_t *ctx)
71 static void initcontext(context_t *ctx)
70 {
72 {
71 ctx->ch = '\0';
73 ctx->ch = '\0';
72 ctx->data = malloc(defaultdatasize);
74 ctx->data = malloc(defaultdatasize);
73 ctx->maxdatasize = (ctx->data) ? defaultdatasize : 0;
75 ctx->maxdatasize = (ctx->data) ? defaultdatasize : 0;
74 ctx->datasize = 0;
76 ctx->datasize = 0;
75 debugmsg("initialize context buffer with size %zu", ctx->maxdatasize);
77 debugmsg("initialize context buffer with size %zu", ctx->maxdatasize);
76 }
78 }
77
79
78 static void enlargecontext(context_t *ctx, size_t newsize)
80 static void enlargecontext(context_t *ctx, size_t newsize)
79 {
81 {
80 if (newsize <= ctx->maxdatasize)
82 if (newsize <= ctx->maxdatasize)
81 return;
83 return;
82
84
83 newsize = defaultdatasize
85 newsize = defaultdatasize
84 * ((newsize + defaultdatasize - 1) / defaultdatasize);
86 * ((newsize + defaultdatasize - 1) / defaultdatasize);
85 char *p = realloc(ctx->data, newsize);
87 char *p = realloc(ctx->data, newsize);
86 if (!p)
88 if (!p)
87 abortmsg("failed to allocate buffer");
89 abortmsg("failed to allocate buffer");
88 ctx->data = p;
90 ctx->data = p;
89 ctx->maxdatasize = newsize;
91 ctx->maxdatasize = newsize;
90 debugmsg("enlarge context buffer to %zu", ctx->maxdatasize);
92 debugmsg("enlarge context buffer to %zu", ctx->maxdatasize);
91 }
93 }
92
94
93 static void freecontext(context_t *ctx)
95 static void freecontext(context_t *ctx)
94 {
96 {
95 debugmsg("free context buffer");
97 debugmsg("free context buffer");
96 free(ctx->data);
98 free(ctx->data);
97 ctx->data = NULL;
99 ctx->data = NULL;
98 ctx->maxdatasize = 0;
100 ctx->maxdatasize = 0;
99 ctx->datasize = 0;
101 ctx->datasize = 0;
100 }
102 }
101
103
102 /* Read channeled response from cmdserver */
104 /* Read channeled response from cmdserver */
103 static void readchannel(hgclient_t *hgc)
105 static void readchannel(hgclient_t *hgc)
104 {
106 {
105 assert(hgc);
107 assert(hgc);
106
108
107 ssize_t rsize = recv(hgc->sockfd, &hgc->ctx.ch, sizeof(hgc->ctx.ch), 0);
109 ssize_t rsize = recv(hgc->sockfd, &hgc->ctx.ch, sizeof(hgc->ctx.ch), 0);
108 if (rsize != sizeof(hgc->ctx.ch))
110 if (rsize != sizeof(hgc->ctx.ch))
109 abortmsg("failed to read channel");
111 abortmsg("failed to read channel");
110
112
111 uint32_t datasize_n;
113 uint32_t datasize_n;
112 rsize = recv(hgc->sockfd, &datasize_n, sizeof(datasize_n), 0);
114 rsize = recv(hgc->sockfd, &datasize_n, sizeof(datasize_n), 0);
113 if (rsize != sizeof(datasize_n))
115 if (rsize != sizeof(datasize_n))
114 abortmsg("failed to read data size");
116 abortmsg("failed to read data size");
115
117
116 /* datasize denotes the maximum size to write if input request */
118 /* datasize denotes the maximum size to write if input request */
117 hgc->ctx.datasize = ntohl(datasize_n);
119 hgc->ctx.datasize = ntohl(datasize_n);
118 enlargecontext(&hgc->ctx, hgc->ctx.datasize);
120 enlargecontext(&hgc->ctx, hgc->ctx.datasize);
119
121
120 if (isupper(hgc->ctx.ch) && hgc->ctx.ch != 'S')
122 if (isupper(hgc->ctx.ch) && hgc->ctx.ch != 'S')
121 return; /* assumes input request */
123 return; /* assumes input request */
122
124
123 size_t cursize = 0;
125 size_t cursize = 0;
124 while (cursize < hgc->ctx.datasize) {
126 while (cursize < hgc->ctx.datasize) {
125 rsize = recv(hgc->sockfd, hgc->ctx.data + cursize,
127 rsize = recv(hgc->sockfd, hgc->ctx.data + cursize,
126 hgc->ctx.datasize - cursize, 0);
128 hgc->ctx.datasize - cursize, 0);
127 if (rsize < 0)
129 if (rsize < 0)
128 abortmsg("failed to read data block");
130 abortmsg("failed to read data block");
129 cursize += rsize;
131 cursize += rsize;
130 }
132 }
131 }
133 }
132
134
133 static void sendall(int sockfd, const void *data, size_t datasize)
135 static void sendall(int sockfd, const void *data, size_t datasize)
134 {
136 {
135 const char *p = data;
137 const char *p = data;
136 const char *const endp = p + datasize;
138 const char *const endp = p + datasize;
137 while (p < endp) {
139 while (p < endp) {
138 ssize_t r = send(sockfd, p, endp - p, 0);
140 ssize_t r = send(sockfd, p, endp - p, 0);
139 if (r < 0)
141 if (r < 0)
140 abortmsg("cannot communicate (errno = %d)", errno);
142 abortmsg("cannot communicate (errno = %d)", errno);
141 p += r;
143 p += r;
142 }
144 }
143 }
145 }
144
146
145 /* Write lengh-data block to cmdserver */
147 /* Write lengh-data block to cmdserver */
146 static void writeblock(const hgclient_t *hgc)
148 static void writeblock(const hgclient_t *hgc)
147 {
149 {
148 assert(hgc);
150 assert(hgc);
149
151
150 const uint32_t datasize_n = htonl(hgc->ctx.datasize);
152 const uint32_t datasize_n = htonl(hgc->ctx.datasize);
151 sendall(hgc->sockfd, &datasize_n, sizeof(datasize_n));
153 sendall(hgc->sockfd, &datasize_n, sizeof(datasize_n));
152
154
153 sendall(hgc->sockfd, hgc->ctx.data, hgc->ctx.datasize);
155 sendall(hgc->sockfd, hgc->ctx.data, hgc->ctx.datasize);
154 }
156 }
155
157
156 static void writeblockrequest(const hgclient_t *hgc, const char *chcmd)
158 static void writeblockrequest(const hgclient_t *hgc, const char *chcmd)
157 {
159 {
158 debugmsg("request %s, block size %zu", chcmd, hgc->ctx.datasize);
160 debugmsg("request %s, block size %zu", chcmd, hgc->ctx.datasize);
159
161
160 char buf[strlen(chcmd) + 1];
162 char buf[strlen(chcmd) + 1];
161 memcpy(buf, chcmd, sizeof(buf) - 1);
163 memcpy(buf, chcmd, sizeof(buf) - 1);
162 buf[sizeof(buf) - 1] = '\n';
164 buf[sizeof(buf) - 1] = '\n';
163 sendall(hgc->sockfd, buf, sizeof(buf));
165 sendall(hgc->sockfd, buf, sizeof(buf));
164
166
165 writeblock(hgc);
167 writeblock(hgc);
166 }
168 }
167
169
168 /* Build '\0'-separated list of args. argsize < 0 denotes that args are
170 /* Build '\0'-separated list of args. argsize < 0 denotes that args are
169 * terminated by NULL. */
171 * terminated by NULL. */
170 static void packcmdargs(context_t *ctx, const char *const args[],
172 static void packcmdargs(context_t *ctx, const char *const args[],
171 ssize_t argsize)
173 ssize_t argsize)
172 {
174 {
173 ctx->datasize = 0;
175 ctx->datasize = 0;
174 const char *const *const end = (argsize >= 0) ? args + argsize : NULL;
176 const char *const *const end = (argsize >= 0) ? args + argsize : NULL;
175 for (const char *const *it = args; it != end && *it; ++it) {
177 for (const char *const *it = args; it != end && *it; ++it) {
176 const size_t n = strlen(*it) + 1; /* include '\0' */
178 const size_t n = strlen(*it) + 1; /* include '\0' */
177 enlargecontext(ctx, ctx->datasize + n);
179 enlargecontext(ctx, ctx->datasize + n);
178 memcpy(ctx->data + ctx->datasize, *it, n);
180 memcpy(ctx->data + ctx->datasize, *it, n);
179 ctx->datasize += n;
181 ctx->datasize += n;
180 }
182 }
181
183
182 if (ctx->datasize > 0)
184 if (ctx->datasize > 0)
183 --ctx->datasize; /* strip last '\0' */
185 --ctx->datasize; /* strip last '\0' */
184 }
186 }
185
187
186 /* Extract '\0'-separated list of args to new buffer, terminated by NULL */
188 /* Extract '\0'-separated list of args to new buffer, terminated by NULL */
187 static const char **unpackcmdargsnul(const context_t *ctx)
189 static const char **unpackcmdargsnul(const context_t *ctx)
188 {
190 {
189 const char **args = NULL;
191 const char **args = NULL;
190 size_t nargs = 0, maxnargs = 0;
192 size_t nargs = 0, maxnargs = 0;
191 const char *s = ctx->data;
193 const char *s = ctx->data;
192 const char *e = ctx->data + ctx->datasize;
194 const char *e = ctx->data + ctx->datasize;
193 for (;;) {
195 for (;;) {
194 if (nargs + 1 >= maxnargs) { /* including last NULL */
196 if (nargs + 1 >= maxnargs) { /* including last NULL */
195 maxnargs += 256;
197 maxnargs += 256;
196 args = realloc(args, maxnargs * sizeof(args[0]));
198 args = realloc(args, maxnargs * sizeof(args[0]));
197 if (!args)
199 if (!args)
198 abortmsg("failed to allocate args buffer");
200 abortmsg("failed to allocate args buffer");
199 }
201 }
200 args[nargs] = s;
202 args[nargs] = s;
201 nargs++;
203 nargs++;
202 s = memchr(s, '\0', e - s);
204 s = memchr(s, '\0', e - s);
203 if (!s)
205 if (!s)
204 break;
206 break;
205 s++;
207 s++;
206 }
208 }
207 args[nargs] = NULL;
209 args[nargs] = NULL;
208 return args;
210 return args;
209 }
211 }
210
212
211 static void handlereadrequest(hgclient_t *hgc)
213 static void handlereadrequest(hgclient_t *hgc)
212 {
214 {
213 context_t *ctx = &hgc->ctx;
215 context_t *ctx = &hgc->ctx;
214 size_t r = fread(ctx->data, sizeof(ctx->data[0]), ctx->datasize, stdin);
216 size_t r = fread(ctx->data, sizeof(ctx->data[0]), ctx->datasize, stdin);
215 ctx->datasize = r;
217 ctx->datasize = r;
216 writeblock(hgc);
218 writeblock(hgc);
217 }
219 }
218
220
219 /* Read single-line */
221 /* Read single-line */
220 static void handlereadlinerequest(hgclient_t *hgc)
222 static void handlereadlinerequest(hgclient_t *hgc)
221 {
223 {
222 context_t *ctx = &hgc->ctx;
224 context_t *ctx = &hgc->ctx;
223 if (!fgets(ctx->data, ctx->datasize, stdin))
225 if (!fgets(ctx->data, ctx->datasize, stdin))
224 ctx->data[0] = '\0';
226 ctx->data[0] = '\0';
225 ctx->datasize = strlen(ctx->data);
227 ctx->datasize = strlen(ctx->data);
226 writeblock(hgc);
228 writeblock(hgc);
227 }
229 }
228
230
229 /* Execute the requested command and write exit code */
231 /* Execute the requested command and write exit code */
230 static void handlesystemrequest(hgclient_t *hgc)
232 static void handlesystemrequest(hgclient_t *hgc)
231 {
233 {
232 context_t *ctx = &hgc->ctx;
234 context_t *ctx = &hgc->ctx;
233 enlargecontext(ctx, ctx->datasize + 1);
235 enlargecontext(ctx, ctx->datasize + 1);
234 ctx->data[ctx->datasize] = '\0'; /* terminate last string */
236 ctx->data[ctx->datasize] = '\0'; /* terminate last string */
235
237
236 const char **args = unpackcmdargsnul(ctx);
238 const char **args = unpackcmdargsnul(ctx);
237 if (!args[0] || !args[1])
239 if (!args[0] || !args[1])
238 abortmsg("missing command or cwd in system request");
240 abortmsg("missing command or cwd in system request");
239 debugmsg("run '%s' at '%s'", args[0], args[1]);
241 debugmsg("run '%s' at '%s'", args[0], args[1]);
240 int32_t r = runshellcmd(args[0], args + 2, args[1]);
242 int32_t r = runshellcmd(args[0], args + 2, args[1]);
241 free(args);
243 free(args);
242
244
243 uint32_t r_n = htonl(r);
245 uint32_t r_n = htonl(r);
244 memcpy(ctx->data, &r_n, sizeof(r_n));
246 memcpy(ctx->data, &r_n, sizeof(r_n));
245 ctx->datasize = sizeof(r_n);
247 ctx->datasize = sizeof(r_n);
246 writeblock(hgc);
248 writeblock(hgc);
247 }
249 }
248
250
249 /* Read response of command execution until receiving 'r'-esult */
251 /* Read response of command execution until receiving 'r'-esult */
250 static void handleresponse(hgclient_t *hgc)
252 static void handleresponse(hgclient_t *hgc)
251 {
253 {
252 for (;;) {
254 for (;;) {
253 readchannel(hgc);
255 readchannel(hgc);
254 context_t *ctx = &hgc->ctx;
256 context_t *ctx = &hgc->ctx;
255 debugmsg("response read from channel %c, size %zu",
257 debugmsg("response read from channel %c, size %zu",
256 ctx->ch, ctx->datasize);
258 ctx->ch, ctx->datasize);
257 switch (ctx->ch) {
259 switch (ctx->ch) {
258 case 'o':
260 case 'o':
259 fwrite(ctx->data, sizeof(ctx->data[0]), ctx->datasize,
261 fwrite(ctx->data, sizeof(ctx->data[0]), ctx->datasize,
260 stdout);
262 stdout);
261 break;
263 break;
262 case 'e':
264 case 'e':
263 fwrite(ctx->data, sizeof(ctx->data[0]), ctx->datasize,
265 fwrite(ctx->data, sizeof(ctx->data[0]), ctx->datasize,
264 stderr);
266 stderr);
265 break;
267 break;
266 case 'd':
268 case 'd':
267 /* assumes last char is '\n' */
269 /* assumes last char is '\n' */
268 ctx->data[ctx->datasize - 1] = '\0';
270 ctx->data[ctx->datasize - 1] = '\0';
269 debugmsg("server: %s", ctx->data);
271 debugmsg("server: %s", ctx->data);
270 break;
272 break;
271 case 'r':
273 case 'r':
272 return;
274 return;
273 case 'I':
275 case 'I':
274 handlereadrequest(hgc);
276 handlereadrequest(hgc);
275 break;
277 break;
276 case 'L':
278 case 'L':
277 handlereadlinerequest(hgc);
279 handlereadlinerequest(hgc);
278 break;
280 break;
279 case 'S':
281 case 'S':
280 handlesystemrequest(hgc);
282 handlesystemrequest(hgc);
281 break;
283 break;
282 default:
284 default:
283 if (isupper(ctx->ch))
285 if (isupper(ctx->ch))
284 abortmsg("cannot handle response (ch = %c)",
286 abortmsg("cannot handle response (ch = %c)",
285 ctx->ch);
287 ctx->ch);
286 }
288 }
287 }
289 }
288 }
290 }
289
291
290 static unsigned int parsecapabilities(const char *s, const char *e)
292 static unsigned int parsecapabilities(const char *s, const char *e)
291 {
293 {
292 unsigned int flags = 0;
294 unsigned int flags = 0;
293 while (s < e) {
295 while (s < e) {
294 const char *t = strchr(s, ' ');
296 const char *t = strchr(s, ' ');
295 if (!t || t > e)
297 if (!t || t > e)
296 t = e;
298 t = e;
297 const cappair_t *cap;
299 const cappair_t *cap;
298 for (cap = captable; cap->flag; ++cap) {
300 for (cap = captable; cap->flag; ++cap) {
299 size_t n = t - s;
301 size_t n = t - s;
300 if (strncmp(s, cap->name, n) == 0 &&
302 if (strncmp(s, cap->name, n) == 0 &&
301 strlen(cap->name) == n) {
303 strlen(cap->name) == n) {
302 flags |= cap->flag;
304 flags |= cap->flag;
303 break;
305 break;
304 }
306 }
305 }
307 }
306 s = t + 1;
308 s = t + 1;
307 }
309 }
308 return flags;
310 return flags;
309 }
311 }
310
312
311 static void readhello(hgclient_t *hgc)
313 static void readhello(hgclient_t *hgc)
312 {
314 {
313 readchannel(hgc);
315 readchannel(hgc);
314 context_t *ctx = &hgc->ctx;
316 context_t *ctx = &hgc->ctx;
315 if (ctx->ch != 'o')
317 if (ctx->ch != 'o')
316 abortmsg("unexpected channel of hello message (ch = %c)",
318 abortmsg("unexpected channel of hello message (ch = %c)",
317 ctx->ch);
319 ctx->ch);
318 enlargecontext(ctx, ctx->datasize + 1);
320 enlargecontext(ctx, ctx->datasize + 1);
319 ctx->data[ctx->datasize] = '\0';
321 ctx->data[ctx->datasize] = '\0';
320 debugmsg("hello received: %s (size = %zu)", ctx->data, ctx->datasize);
322 debugmsg("hello received: %s (size = %zu)", ctx->data, ctx->datasize);
321
323
322 const char *s = ctx->data;
324 const char *s = ctx->data;
323 const char *const dataend = ctx->data + ctx->datasize;
325 const char *const dataend = ctx->data + ctx->datasize;
324 while (s < dataend) {
326 while (s < dataend) {
325 const char *t = strchr(s, ':');
327 const char *t = strchr(s, ':');
326 if (!t || t[1] != ' ')
328 if (!t || t[1] != ' ')
327 break;
329 break;
328 const char *u = strchr(t + 2, '\n');
330 const char *u = strchr(t + 2, '\n');
329 if (!u)
331 if (!u)
330 u = dataend;
332 u = dataend;
331 if (strncmp(s, "capabilities:", t - s + 1) == 0) {
333 if (strncmp(s, "capabilities:", t - s + 1) == 0) {
332 hgc->capflags = parsecapabilities(t + 2, u);
334 hgc->capflags = parsecapabilities(t + 2, u);
333 } else if (strncmp(s, "pid:", t - s + 1) == 0) {
335 } else if (strncmp(s, "pid:", t - s + 1) == 0) {
334 hgc->pid = strtol(t + 2, NULL, 10);
336 hgc->pid = strtol(t + 2, NULL, 10);
335 }
337 }
336 s = u + 1;
338 s = u + 1;
337 }
339 }
338 debugmsg("capflags=0x%04x, pid=%d", hgc->capflags, hgc->pid);
340 debugmsg("capflags=0x%04x, pid=%d", hgc->capflags, hgc->pid);
339 }
341 }
340
342
341 static void attachio(hgclient_t *hgc)
343 static void attachio(hgclient_t *hgc)
342 {
344 {
343 debugmsg("request attachio");
345 debugmsg("request attachio");
344 static const char chcmd[] = "attachio\n";
346 static const char chcmd[] = "attachio\n";
345 sendall(hgc->sockfd, chcmd, sizeof(chcmd) - 1);
347 sendall(hgc->sockfd, chcmd, sizeof(chcmd) - 1);
346 readchannel(hgc);
348 readchannel(hgc);
347 context_t *ctx = &hgc->ctx;
349 context_t *ctx = &hgc->ctx;
348 if (ctx->ch != 'I')
350 if (ctx->ch != 'I')
349 abortmsg("unexpected response for attachio (ch = %c)", ctx->ch);
351 abortmsg("unexpected response for attachio (ch = %c)", ctx->ch);
350
352
351 static const int fds[3] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
353 static const int fds[3] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
352 struct msghdr msgh;
354 struct msghdr msgh;
353 memset(&msgh, 0, sizeof(msgh));
355 memset(&msgh, 0, sizeof(msgh));
354 struct iovec iov = {ctx->data, ctx->datasize}; /* dummy payload */
356 struct iovec iov = {ctx->data, ctx->datasize}; /* dummy payload */
355 msgh.msg_iov = &iov;
357 msgh.msg_iov = &iov;
356 msgh.msg_iovlen = 1;
358 msgh.msg_iovlen = 1;
357 char fdbuf[CMSG_SPACE(sizeof(fds))];
359 char fdbuf[CMSG_SPACE(sizeof(fds))];
358 msgh.msg_control = fdbuf;
360 msgh.msg_control = fdbuf;
359 msgh.msg_controllen = sizeof(fdbuf);
361 msgh.msg_controllen = sizeof(fdbuf);
360 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msgh);
362 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msgh);
361 cmsg->cmsg_level = SOL_SOCKET;
363 cmsg->cmsg_level = SOL_SOCKET;
362 cmsg->cmsg_type = SCM_RIGHTS;
364 cmsg->cmsg_type = SCM_RIGHTS;
363 cmsg->cmsg_len = CMSG_LEN(sizeof(fds));
365 cmsg->cmsg_len = CMSG_LEN(sizeof(fds));
364 memcpy(CMSG_DATA(cmsg), fds, sizeof(fds));
366 memcpy(CMSG_DATA(cmsg), fds, sizeof(fds));
365 msgh.msg_controllen = cmsg->cmsg_len;
367 msgh.msg_controllen = cmsg->cmsg_len;
366 ssize_t r = sendmsg(hgc->sockfd, &msgh, 0);
368 ssize_t r = sendmsg(hgc->sockfd, &msgh, 0);
367 if (r < 0)
369 if (r < 0)
368 abortmsg("sendmsg failed (errno = %d)", errno);
370 abortmsg("sendmsg failed (errno = %d)", errno);
369
371
370 handleresponse(hgc);
372 handleresponse(hgc);
371 int32_t n;
373 int32_t n;
372 if (ctx->datasize != sizeof(n))
374 if (ctx->datasize != sizeof(n))
373 abortmsg("unexpected size of attachio result");
375 abortmsg("unexpected size of attachio result");
374 memcpy(&n, ctx->data, sizeof(n));
376 memcpy(&n, ctx->data, sizeof(n));
375 n = ntohl(n);
377 n = ntohl(n);
376 if (n != sizeof(fds) / sizeof(fds[0]))
378 if (n != sizeof(fds) / sizeof(fds[0]))
377 abortmsg("failed to send fds (n = %d)", n);
379 abortmsg("failed to send fds (n = %d)", n);
378 }
380 }
379
381
380 static void chdirtocwd(hgclient_t *hgc)
382 static void chdirtocwd(hgclient_t *hgc)
381 {
383 {
382 if (!getcwd(hgc->ctx.data, hgc->ctx.maxdatasize))
384 if (!getcwd(hgc->ctx.data, hgc->ctx.maxdatasize))
383 abortmsg("failed to getcwd (errno = %d)", errno);
385 abortmsg("failed to getcwd (errno = %d)", errno);
384 hgc->ctx.datasize = strlen(hgc->ctx.data);
386 hgc->ctx.datasize = strlen(hgc->ctx.data);
385 writeblockrequest(hgc, "chdir");
387 writeblockrequest(hgc, "chdir");
386 }
388 }
387
389
390 static void forwardumask(hgclient_t *hgc)
391 {
392 mode_t mask = umask(0);
393 umask(mask);
394
395 static const char command[] = "setumask\n";
396 sendall(hgc->sockfd, command, sizeof(command) - 1);
397 uint32_t data = htonl(mask);
398 sendall(hgc->sockfd, &data, sizeof(data));
399 }
400
388 /*!
401 /*!
389 * Open connection to per-user cmdserver
402 * Open connection to per-user cmdserver
390 *
403 *
391 * If no background server running, returns NULL.
404 * If no background server running, returns NULL.
392 */
405 */
393 hgclient_t *hgc_open(const char *sockname)
406 hgclient_t *hgc_open(const char *sockname)
394 {
407 {
395 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
408 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
396 if (fd < 0)
409 if (fd < 0)
397 abortmsg("cannot create socket (errno = %d)", errno);
410 abortmsg("cannot create socket (errno = %d)", errno);
398
411
399 /* don't keep fd on fork(), so that it can be closed when the parent
412 /* don't keep fd on fork(), so that it can be closed when the parent
400 * process get terminated. */
413 * process get terminated. */
401 int flags = fcntl(fd, F_GETFD);
414 int flags = fcntl(fd, F_GETFD);
402 if (flags < 0)
415 if (flags < 0)
403 abortmsg("cannot get flags of socket (errno = %d)", errno);
416 abortmsg("cannot get flags of socket (errno = %d)", errno);
404 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
417 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
405 abortmsg("cannot set flags of socket (errno = %d)", errno);
418 abortmsg("cannot set flags of socket (errno = %d)", errno);
406
419
407 struct sockaddr_un addr;
420 struct sockaddr_un addr;
408 addr.sun_family = AF_UNIX;
421 addr.sun_family = AF_UNIX;
409 strncpy(addr.sun_path, sockname, sizeof(addr.sun_path));
422 strncpy(addr.sun_path, sockname, sizeof(addr.sun_path));
410 addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
423 addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
411
424
412 debugmsg("connect to %s", addr.sun_path);
425 debugmsg("connect to %s", addr.sun_path);
413 int r = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
426 int r = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
414 if (r < 0) {
427 if (r < 0) {
415 close(fd);
428 close(fd);
416 if (errno == ENOENT || errno == ECONNREFUSED)
429 if (errno == ENOENT || errno == ECONNREFUSED)
417 return NULL;
430 return NULL;
418 abortmsg("cannot connect to %s (errno = %d)",
431 abortmsg("cannot connect to %s (errno = %d)",
419 addr.sun_path, errno);
432 addr.sun_path, errno);
420 }
433 }
421
434
422 hgclient_t *hgc = malloc(sizeof(hgclient_t));
435 hgclient_t *hgc = malloc(sizeof(hgclient_t));
423 if (!hgc)
436 if (!hgc)
424 abortmsg("failed to allocate hgclient object");
437 abortmsg("failed to allocate hgclient object");
425 memset(hgc, 0, sizeof(*hgc));
438 memset(hgc, 0, sizeof(*hgc));
426 hgc->sockfd = fd;
439 hgc->sockfd = fd;
427 initcontext(&hgc->ctx);
440 initcontext(&hgc->ctx);
428
441
429 readhello(hgc);
442 readhello(hgc);
430 if (!(hgc->capflags & CAP_RUNCOMMAND))
443 if (!(hgc->capflags & CAP_RUNCOMMAND))
431 abortmsg("insufficient capability: runcommand");
444 abortmsg("insufficient capability: runcommand");
432 if (hgc->capflags & CAP_ATTACHIO)
445 if (hgc->capflags & CAP_ATTACHIO)
433 attachio(hgc);
446 attachio(hgc);
434 if (hgc->capflags & CAP_CHDIR)
447 if (hgc->capflags & CAP_CHDIR)
435 chdirtocwd(hgc);
448 chdirtocwd(hgc);
449 if (hgc->capflags & CAP_SETUMASK)
450 forwardumask(hgc);
436
451
437 return hgc;
452 return hgc;
438 }
453 }
439
454
440 /*!
455 /*!
441 * Close connection and free allocated memory
456 * Close connection and free allocated memory
442 */
457 */
443 void hgc_close(hgclient_t *hgc)
458 void hgc_close(hgclient_t *hgc)
444 {
459 {
445 assert(hgc);
460 assert(hgc);
446 freecontext(&hgc->ctx);
461 freecontext(&hgc->ctx);
447 close(hgc->sockfd);
462 close(hgc->sockfd);
448 free(hgc);
463 free(hgc);
449 }
464 }
450
465
451 pid_t hgc_peerpid(const hgclient_t *hgc)
466 pid_t hgc_peerpid(const hgclient_t *hgc)
452 {
467 {
453 assert(hgc);
468 assert(hgc);
454 return hgc->pid;
469 return hgc->pid;
455 }
470 }
456
471
457 /*!
472 /*!
458 * Execute the specified Mercurial command
473 * Execute the specified Mercurial command
459 *
474 *
460 * @return result code
475 * @return result code
461 */
476 */
462 int hgc_runcommand(hgclient_t *hgc, const char *const args[], size_t argsize)
477 int hgc_runcommand(hgclient_t *hgc, const char *const args[], size_t argsize)
463 {
478 {
464 assert(hgc);
479 assert(hgc);
465
480
466 packcmdargs(&hgc->ctx, args, argsize);
481 packcmdargs(&hgc->ctx, args, argsize);
467 writeblockrequest(hgc, "runcommand");
482 writeblockrequest(hgc, "runcommand");
468 handleresponse(hgc);
483 handleresponse(hgc);
469
484
470 int32_t exitcode_n;
485 int32_t exitcode_n;
471 if (hgc->ctx.datasize != sizeof(exitcode_n)) {
486 if (hgc->ctx.datasize != sizeof(exitcode_n)) {
472 abortmsg("unexpected size of exitcode");
487 abortmsg("unexpected size of exitcode");
473 }
488 }
474 memcpy(&exitcode_n, hgc->ctx.data, sizeof(exitcode_n));
489 memcpy(&exitcode_n, hgc->ctx.data, sizeof(exitcode_n));
475 return ntohl(exitcode_n);
490 return ntohl(exitcode_n);
476 }
491 }
477
492
478 /*!
493 /*!
479 * (Re-)send client's stdio channels so that the server can access to tty
494 * (Re-)send client's stdio channels so that the server can access to tty
480 */
495 */
481 void hgc_attachio(hgclient_t *hgc)
496 void hgc_attachio(hgclient_t *hgc)
482 {
497 {
483 assert(hgc);
498 assert(hgc);
484 if (!(hgc->capflags & CAP_ATTACHIO))
499 if (!(hgc->capflags & CAP_ATTACHIO))
485 return;
500 return;
486 attachio(hgc);
501 attachio(hgc);
487 }
502 }
488
503
489 /*!
504 /*!
490 * Get pager command for the given Mercurial command args
505 * Get pager command for the given Mercurial command args
491 *
506 *
492 * If no pager enabled, returns NULL. The return value becomes invalid
507 * If no pager enabled, returns NULL. The return value becomes invalid
493 * once you run another request to hgc.
508 * once you run another request to hgc.
494 */
509 */
495 const char *hgc_getpager(hgclient_t *hgc, const char *const args[],
510 const char *hgc_getpager(hgclient_t *hgc, const char *const args[],
496 size_t argsize)
511 size_t argsize)
497 {
512 {
498 assert(hgc);
513 assert(hgc);
499
514
500 if (!(hgc->capflags & CAP_GETPAGER))
515 if (!(hgc->capflags & CAP_GETPAGER))
501 return NULL;
516 return NULL;
502
517
503 packcmdargs(&hgc->ctx, args, argsize);
518 packcmdargs(&hgc->ctx, args, argsize);
504 writeblockrequest(hgc, "getpager");
519 writeblockrequest(hgc, "getpager");
505 handleresponse(hgc);
520 handleresponse(hgc);
506
521
507 if (hgc->ctx.datasize < 1 || hgc->ctx.data[0] == '\0')
522 if (hgc->ctx.datasize < 1 || hgc->ctx.data[0] == '\0')
508 return NULL;
523 return NULL;
509 enlargecontext(&hgc->ctx, hgc->ctx.datasize + 1);
524 enlargecontext(&hgc->ctx, hgc->ctx.datasize + 1);
510 hgc->ctx.data[hgc->ctx.datasize] = '\0';
525 hgc->ctx.data[hgc->ctx.datasize] = '\0';
511 return hgc->ctx.data;
526 return hgc->ctx.data;
512 }
527 }
513
528
514 /*!
529 /*!
515 * Update server's environment variables
530 * Update server's environment variables
516 *
531 *
517 * @param envp list of environment variables in "NAME=VALUE" format,
532 * @param envp list of environment variables in "NAME=VALUE" format,
518 * terminated by NULL.
533 * terminated by NULL.
519 */
534 */
520 void hgc_setenv(hgclient_t *hgc, const char *const envp[])
535 void hgc_setenv(hgclient_t *hgc, const char *const envp[])
521 {
536 {
522 assert(hgc && envp);
537 assert(hgc && envp);
523 if (!(hgc->capflags & CAP_SETENV))
538 if (!(hgc->capflags & CAP_SETENV))
524 return;
539 return;
525 packcmdargs(&hgc->ctx, envp, /*argsize*/ -1);
540 packcmdargs(&hgc->ctx, envp, /*argsize*/ -1);
526 writeblockrequest(hgc, "setenv");
541 writeblockrequest(hgc, "setenv");
527 }
542 }
General Comments 0
You need to be logged in to leave comments. Login now