##// END OF EJS Templates
chg: just take it as EOF if recv() returns 0...
Yuya Nishihara -
r29719:acf27be5 default
parent child Browse files
Show More
@@ -1,590 +1,585 b''
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 CAP_SETUMASK = 0x1000,
37 CAP_VALIDATE = 0x2000,
37 CAP_VALIDATE = 0x2000,
38 };
38 };
39
39
40 typedef struct {
40 typedef struct {
41 const char *name;
41 const char *name;
42 unsigned int flag;
42 unsigned int flag;
43 } cappair_t;
43 } cappair_t;
44
44
45 static const cappair_t captable[] = {
45 static const cappair_t captable[] = {
46 {"getencoding", CAP_GETENCODING},
46 {"getencoding", CAP_GETENCODING},
47 {"runcommand", CAP_RUNCOMMAND},
47 {"runcommand", CAP_RUNCOMMAND},
48 {"attachio", CAP_ATTACHIO},
48 {"attachio", CAP_ATTACHIO},
49 {"chdir", CAP_CHDIR},
49 {"chdir", CAP_CHDIR},
50 {"getpager", CAP_GETPAGER},
50 {"getpager", CAP_GETPAGER},
51 {"setenv", CAP_SETENV},
51 {"setenv", CAP_SETENV},
52 {"setumask", CAP_SETUMASK},
52 {"setumask", CAP_SETUMASK},
53 {"validate", CAP_VALIDATE},
53 {"validate", CAP_VALIDATE},
54 {NULL, 0}, /* terminator */
54 {NULL, 0}, /* terminator */
55 };
55 };
56
56
57 typedef struct {
57 typedef struct {
58 char ch;
58 char ch;
59 char *data;
59 char *data;
60 size_t maxdatasize;
60 size_t maxdatasize;
61 size_t datasize;
61 size_t datasize;
62 } context_t;
62 } context_t;
63
63
64 struct hgclient_tag_ {
64 struct hgclient_tag_ {
65 int sockfd;
65 int sockfd;
66 pid_t pgid;
66 pid_t pgid;
67 pid_t pid;
67 pid_t pid;
68 context_t ctx;
68 context_t ctx;
69 unsigned int capflags;
69 unsigned int capflags;
70 };
70 };
71
71
72 static const size_t defaultdatasize = 4096;
72 static const size_t defaultdatasize = 4096;
73
73
74 static void initcontext(context_t *ctx)
74 static void initcontext(context_t *ctx)
75 {
75 {
76 ctx->ch = '\0';
76 ctx->ch = '\0';
77 ctx->data = malloc(defaultdatasize);
77 ctx->data = malloc(defaultdatasize);
78 ctx->maxdatasize = (ctx->data) ? defaultdatasize : 0;
78 ctx->maxdatasize = (ctx->data) ? defaultdatasize : 0;
79 ctx->datasize = 0;
79 ctx->datasize = 0;
80 debugmsg("initialize context buffer with size %zu", ctx->maxdatasize);
80 debugmsg("initialize context buffer with size %zu", ctx->maxdatasize);
81 }
81 }
82
82
83 static void enlargecontext(context_t *ctx, size_t newsize)
83 static void enlargecontext(context_t *ctx, size_t newsize)
84 {
84 {
85 if (newsize <= ctx->maxdatasize)
85 if (newsize <= ctx->maxdatasize)
86 return;
86 return;
87
87
88 newsize = defaultdatasize
88 newsize = defaultdatasize
89 * ((newsize + defaultdatasize - 1) / defaultdatasize);
89 * ((newsize + defaultdatasize - 1) / defaultdatasize);
90 ctx->data = reallocx(ctx->data, newsize);
90 ctx->data = reallocx(ctx->data, newsize);
91 ctx->maxdatasize = newsize;
91 ctx->maxdatasize = newsize;
92 debugmsg("enlarge context buffer to %zu", ctx->maxdatasize);
92 debugmsg("enlarge context buffer to %zu", ctx->maxdatasize);
93 }
93 }
94
94
95 static void freecontext(context_t *ctx)
95 static void freecontext(context_t *ctx)
96 {
96 {
97 debugmsg("free context buffer");
97 debugmsg("free context buffer");
98 free(ctx->data);
98 free(ctx->data);
99 ctx->data = NULL;
99 ctx->data = NULL;
100 ctx->maxdatasize = 0;
100 ctx->maxdatasize = 0;
101 ctx->datasize = 0;
101 ctx->datasize = 0;
102 }
102 }
103
103
104 /* Read channeled response from cmdserver */
104 /* Read channeled response from cmdserver */
105 static void readchannel(hgclient_t *hgc)
105 static void readchannel(hgclient_t *hgc)
106 {
106 {
107 assert(hgc);
107 assert(hgc);
108
108
109 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);
110 if (rsize != sizeof(hgc->ctx.ch)) {
110 if (rsize != sizeof(hgc->ctx.ch)) {
111 /* server would have exception and traceback would be printed */
111 /* server would have exception and traceback would be printed */
112 debugmsg("failed to read channel");
112 debugmsg("failed to read channel");
113 exit(255);
113 exit(255);
114 }
114 }
115
115
116 uint32_t datasize_n;
116 uint32_t datasize_n;
117 rsize = recv(hgc->sockfd, &datasize_n, sizeof(datasize_n), 0);
117 rsize = recv(hgc->sockfd, &datasize_n, sizeof(datasize_n), 0);
118 if (rsize != sizeof(datasize_n))
118 if (rsize != sizeof(datasize_n))
119 abortmsg("failed to read data size");
119 abortmsg("failed to read data size");
120
120
121 /* datasize denotes the maximum size to write if input request */
121 /* datasize denotes the maximum size to write if input request */
122 hgc->ctx.datasize = ntohl(datasize_n);
122 hgc->ctx.datasize = ntohl(datasize_n);
123 enlargecontext(&hgc->ctx, hgc->ctx.datasize);
123 enlargecontext(&hgc->ctx, hgc->ctx.datasize);
124
124
125 if (isupper(hgc->ctx.ch) && hgc->ctx.ch != 'S')
125 if (isupper(hgc->ctx.ch) && hgc->ctx.ch != 'S')
126 return; /* assumes input request */
126 return; /* assumes input request */
127
127
128 size_t cursize = 0;
128 size_t cursize = 0;
129 int emptycount = 0;
130 while (cursize < hgc->ctx.datasize) {
129 while (cursize < hgc->ctx.datasize) {
131 rsize = recv(hgc->sockfd, hgc->ctx.data + cursize,
130 rsize = recv(hgc->sockfd, hgc->ctx.data + cursize,
132 hgc->ctx.datasize - cursize, 0);
131 hgc->ctx.datasize - cursize, 0);
133 /* rsize == 0 normally indicates EOF, while it's also a valid
132 if (rsize < 1)
134 * packet size for unix socket. treat it as EOF and abort if
135 * we get many empty responses in a row. */
136 emptycount = (rsize == 0 ? emptycount + 1 : 0);
137 if (rsize < 0 || emptycount > 20)
138 abortmsg("failed to read data block");
133 abortmsg("failed to read data block");
139 cursize += rsize;
134 cursize += rsize;
140 }
135 }
141 }
136 }
142
137
143 static void sendall(int sockfd, const void *data, size_t datasize)
138 static void sendall(int sockfd, const void *data, size_t datasize)
144 {
139 {
145 const char *p = data;
140 const char *p = data;
146 const char *const endp = p + datasize;
141 const char *const endp = p + datasize;
147 while (p < endp) {
142 while (p < endp) {
148 ssize_t r = send(sockfd, p, endp - p, 0);
143 ssize_t r = send(sockfd, p, endp - p, 0);
149 if (r < 0)
144 if (r < 0)
150 abortmsgerrno("cannot communicate");
145 abortmsgerrno("cannot communicate");
151 p += r;
146 p += r;
152 }
147 }
153 }
148 }
154
149
155 /* Write lengh-data block to cmdserver */
150 /* Write lengh-data block to cmdserver */
156 static void writeblock(const hgclient_t *hgc)
151 static void writeblock(const hgclient_t *hgc)
157 {
152 {
158 assert(hgc);
153 assert(hgc);
159
154
160 const uint32_t datasize_n = htonl(hgc->ctx.datasize);
155 const uint32_t datasize_n = htonl(hgc->ctx.datasize);
161 sendall(hgc->sockfd, &datasize_n, sizeof(datasize_n));
156 sendall(hgc->sockfd, &datasize_n, sizeof(datasize_n));
162
157
163 sendall(hgc->sockfd, hgc->ctx.data, hgc->ctx.datasize);
158 sendall(hgc->sockfd, hgc->ctx.data, hgc->ctx.datasize);
164 }
159 }
165
160
166 static void writeblockrequest(const hgclient_t *hgc, const char *chcmd)
161 static void writeblockrequest(const hgclient_t *hgc, const char *chcmd)
167 {
162 {
168 debugmsg("request %s, block size %zu", chcmd, hgc->ctx.datasize);
163 debugmsg("request %s, block size %zu", chcmd, hgc->ctx.datasize);
169
164
170 char buf[strlen(chcmd) + 1];
165 char buf[strlen(chcmd) + 1];
171 memcpy(buf, chcmd, sizeof(buf) - 1);
166 memcpy(buf, chcmd, sizeof(buf) - 1);
172 buf[sizeof(buf) - 1] = '\n';
167 buf[sizeof(buf) - 1] = '\n';
173 sendall(hgc->sockfd, buf, sizeof(buf));
168 sendall(hgc->sockfd, buf, sizeof(buf));
174
169
175 writeblock(hgc);
170 writeblock(hgc);
176 }
171 }
177
172
178 /* Build '\0'-separated list of args. argsize < 0 denotes that args are
173 /* Build '\0'-separated list of args. argsize < 0 denotes that args are
179 * terminated by NULL. */
174 * terminated by NULL. */
180 static void packcmdargs(context_t *ctx, const char *const args[],
175 static void packcmdargs(context_t *ctx, const char *const args[],
181 ssize_t argsize)
176 ssize_t argsize)
182 {
177 {
183 ctx->datasize = 0;
178 ctx->datasize = 0;
184 const char *const *const end = (argsize >= 0) ? args + argsize : NULL;
179 const char *const *const end = (argsize >= 0) ? args + argsize : NULL;
185 for (const char *const *it = args; it != end && *it; ++it) {
180 for (const char *const *it = args; it != end && *it; ++it) {
186 const size_t n = strlen(*it) + 1; /* include '\0' */
181 const size_t n = strlen(*it) + 1; /* include '\0' */
187 enlargecontext(ctx, ctx->datasize + n);
182 enlargecontext(ctx, ctx->datasize + n);
188 memcpy(ctx->data + ctx->datasize, *it, n);
183 memcpy(ctx->data + ctx->datasize, *it, n);
189 ctx->datasize += n;
184 ctx->datasize += n;
190 }
185 }
191
186
192 if (ctx->datasize > 0)
187 if (ctx->datasize > 0)
193 --ctx->datasize; /* strip last '\0' */
188 --ctx->datasize; /* strip last '\0' */
194 }
189 }
195
190
196 /* Extract '\0'-separated list of args to new buffer, terminated by NULL */
191 /* Extract '\0'-separated list of args to new buffer, terminated by NULL */
197 static const char **unpackcmdargsnul(const context_t *ctx)
192 static const char **unpackcmdargsnul(const context_t *ctx)
198 {
193 {
199 const char **args = NULL;
194 const char **args = NULL;
200 size_t nargs = 0, maxnargs = 0;
195 size_t nargs = 0, maxnargs = 0;
201 const char *s = ctx->data;
196 const char *s = ctx->data;
202 const char *e = ctx->data + ctx->datasize;
197 const char *e = ctx->data + ctx->datasize;
203 for (;;) {
198 for (;;) {
204 if (nargs + 1 >= maxnargs) { /* including last NULL */
199 if (nargs + 1 >= maxnargs) { /* including last NULL */
205 maxnargs += 256;
200 maxnargs += 256;
206 args = reallocx(args, maxnargs * sizeof(args[0]));
201 args = reallocx(args, maxnargs * sizeof(args[0]));
207 }
202 }
208 args[nargs] = s;
203 args[nargs] = s;
209 nargs++;
204 nargs++;
210 s = memchr(s, '\0', e - s);
205 s = memchr(s, '\0', e - s);
211 if (!s)
206 if (!s)
212 break;
207 break;
213 s++;
208 s++;
214 }
209 }
215 args[nargs] = NULL;
210 args[nargs] = NULL;
216 return args;
211 return args;
217 }
212 }
218
213
219 static void handlereadrequest(hgclient_t *hgc)
214 static void handlereadrequest(hgclient_t *hgc)
220 {
215 {
221 context_t *ctx = &hgc->ctx;
216 context_t *ctx = &hgc->ctx;
222 size_t r = fread(ctx->data, sizeof(ctx->data[0]), ctx->datasize, stdin);
217 size_t r = fread(ctx->data, sizeof(ctx->data[0]), ctx->datasize, stdin);
223 ctx->datasize = r;
218 ctx->datasize = r;
224 writeblock(hgc);
219 writeblock(hgc);
225 }
220 }
226
221
227 /* Read single-line */
222 /* Read single-line */
228 static void handlereadlinerequest(hgclient_t *hgc)
223 static void handlereadlinerequest(hgclient_t *hgc)
229 {
224 {
230 context_t *ctx = &hgc->ctx;
225 context_t *ctx = &hgc->ctx;
231 if (!fgets(ctx->data, ctx->datasize, stdin))
226 if (!fgets(ctx->data, ctx->datasize, stdin))
232 ctx->data[0] = '\0';
227 ctx->data[0] = '\0';
233 ctx->datasize = strlen(ctx->data);
228 ctx->datasize = strlen(ctx->data);
234 writeblock(hgc);
229 writeblock(hgc);
235 }
230 }
236
231
237 /* Execute the requested command and write exit code */
232 /* Execute the requested command and write exit code */
238 static void handlesystemrequest(hgclient_t *hgc)
233 static void handlesystemrequest(hgclient_t *hgc)
239 {
234 {
240 context_t *ctx = &hgc->ctx;
235 context_t *ctx = &hgc->ctx;
241 enlargecontext(ctx, ctx->datasize + 1);
236 enlargecontext(ctx, ctx->datasize + 1);
242 ctx->data[ctx->datasize] = '\0'; /* terminate last string */
237 ctx->data[ctx->datasize] = '\0'; /* terminate last string */
243
238
244 const char **args = unpackcmdargsnul(ctx);
239 const char **args = unpackcmdargsnul(ctx);
245 if (!args[0] || !args[1])
240 if (!args[0] || !args[1])
246 abortmsg("missing command or cwd in system request");
241 abortmsg("missing command or cwd in system request");
247 debugmsg("run '%s' at '%s'", args[0], args[1]);
242 debugmsg("run '%s' at '%s'", args[0], args[1]);
248 int32_t r = runshellcmd(args[0], args + 2, args[1]);
243 int32_t r = runshellcmd(args[0], args + 2, args[1]);
249 free(args);
244 free(args);
250
245
251 uint32_t r_n = htonl(r);
246 uint32_t r_n = htonl(r);
252 memcpy(ctx->data, &r_n, sizeof(r_n));
247 memcpy(ctx->data, &r_n, sizeof(r_n));
253 ctx->datasize = sizeof(r_n);
248 ctx->datasize = sizeof(r_n);
254 writeblock(hgc);
249 writeblock(hgc);
255 }
250 }
256
251
257 /* Read response of command execution until receiving 'r'-esult */
252 /* Read response of command execution until receiving 'r'-esult */
258 static void handleresponse(hgclient_t *hgc)
253 static void handleresponse(hgclient_t *hgc)
259 {
254 {
260 for (;;) {
255 for (;;) {
261 readchannel(hgc);
256 readchannel(hgc);
262 context_t *ctx = &hgc->ctx;
257 context_t *ctx = &hgc->ctx;
263 debugmsg("response read from channel %c, size %zu",
258 debugmsg("response read from channel %c, size %zu",
264 ctx->ch, ctx->datasize);
259 ctx->ch, ctx->datasize);
265 switch (ctx->ch) {
260 switch (ctx->ch) {
266 case 'o':
261 case 'o':
267 fwrite(ctx->data, sizeof(ctx->data[0]), ctx->datasize,
262 fwrite(ctx->data, sizeof(ctx->data[0]), ctx->datasize,
268 stdout);
263 stdout);
269 break;
264 break;
270 case 'e':
265 case 'e':
271 fwrite(ctx->data, sizeof(ctx->data[0]), ctx->datasize,
266 fwrite(ctx->data, sizeof(ctx->data[0]), ctx->datasize,
272 stderr);
267 stderr);
273 break;
268 break;
274 case 'd':
269 case 'd':
275 /* assumes last char is '\n' */
270 /* assumes last char is '\n' */
276 ctx->data[ctx->datasize - 1] = '\0';
271 ctx->data[ctx->datasize - 1] = '\0';
277 debugmsg("server: %s", ctx->data);
272 debugmsg("server: %s", ctx->data);
278 break;
273 break;
279 case 'r':
274 case 'r':
280 return;
275 return;
281 case 'I':
276 case 'I':
282 handlereadrequest(hgc);
277 handlereadrequest(hgc);
283 break;
278 break;
284 case 'L':
279 case 'L':
285 handlereadlinerequest(hgc);
280 handlereadlinerequest(hgc);
286 break;
281 break;
287 case 'S':
282 case 'S':
288 handlesystemrequest(hgc);
283 handlesystemrequest(hgc);
289 break;
284 break;
290 default:
285 default:
291 if (isupper(ctx->ch))
286 if (isupper(ctx->ch))
292 abortmsg("cannot handle response (ch = %c)",
287 abortmsg("cannot handle response (ch = %c)",
293 ctx->ch);
288 ctx->ch);
294 }
289 }
295 }
290 }
296 }
291 }
297
292
298 static unsigned int parsecapabilities(const char *s, const char *e)
293 static unsigned int parsecapabilities(const char *s, const char *e)
299 {
294 {
300 unsigned int flags = 0;
295 unsigned int flags = 0;
301 while (s < e) {
296 while (s < e) {
302 const char *t = strchr(s, ' ');
297 const char *t = strchr(s, ' ');
303 if (!t || t > e)
298 if (!t || t > e)
304 t = e;
299 t = e;
305 const cappair_t *cap;
300 const cappair_t *cap;
306 for (cap = captable; cap->flag; ++cap) {
301 for (cap = captable; cap->flag; ++cap) {
307 size_t n = t - s;
302 size_t n = t - s;
308 if (strncmp(s, cap->name, n) == 0 &&
303 if (strncmp(s, cap->name, n) == 0 &&
309 strlen(cap->name) == n) {
304 strlen(cap->name) == n) {
310 flags |= cap->flag;
305 flags |= cap->flag;
311 break;
306 break;
312 }
307 }
313 }
308 }
314 s = t + 1;
309 s = t + 1;
315 }
310 }
316 return flags;
311 return flags;
317 }
312 }
318
313
319 static void readhello(hgclient_t *hgc)
314 static void readhello(hgclient_t *hgc)
320 {
315 {
321 readchannel(hgc);
316 readchannel(hgc);
322 context_t *ctx = &hgc->ctx;
317 context_t *ctx = &hgc->ctx;
323 if (ctx->ch != 'o') {
318 if (ctx->ch != 'o') {
324 char ch = ctx->ch;
319 char ch = ctx->ch;
325 if (ch == 'e') {
320 if (ch == 'e') {
326 /* write early error and will exit */
321 /* write early error and will exit */
327 fwrite(ctx->data, sizeof(ctx->data[0]), ctx->datasize,
322 fwrite(ctx->data, sizeof(ctx->data[0]), ctx->datasize,
328 stderr);
323 stderr);
329 handleresponse(hgc);
324 handleresponse(hgc);
330 }
325 }
331 abortmsg("unexpected channel of hello message (ch = %c)", ch);
326 abortmsg("unexpected channel of hello message (ch = %c)", ch);
332 }
327 }
333 enlargecontext(ctx, ctx->datasize + 1);
328 enlargecontext(ctx, ctx->datasize + 1);
334 ctx->data[ctx->datasize] = '\0';
329 ctx->data[ctx->datasize] = '\0';
335 debugmsg("hello received: %s (size = %zu)", ctx->data, ctx->datasize);
330 debugmsg("hello received: %s (size = %zu)", ctx->data, ctx->datasize);
336
331
337 const char *s = ctx->data;
332 const char *s = ctx->data;
338 const char *const dataend = ctx->data + ctx->datasize;
333 const char *const dataend = ctx->data + ctx->datasize;
339 while (s < dataend) {
334 while (s < dataend) {
340 const char *t = strchr(s, ':');
335 const char *t = strchr(s, ':');
341 if (!t || t[1] != ' ')
336 if (!t || t[1] != ' ')
342 break;
337 break;
343 const char *u = strchr(t + 2, '\n');
338 const char *u = strchr(t + 2, '\n');
344 if (!u)
339 if (!u)
345 u = dataend;
340 u = dataend;
346 if (strncmp(s, "capabilities:", t - s + 1) == 0) {
341 if (strncmp(s, "capabilities:", t - s + 1) == 0) {
347 hgc->capflags = parsecapabilities(t + 2, u);
342 hgc->capflags = parsecapabilities(t + 2, u);
348 } else if (strncmp(s, "pgid:", t - s + 1) == 0) {
343 } else if (strncmp(s, "pgid:", t - s + 1) == 0) {
349 hgc->pgid = strtol(t + 2, NULL, 10);
344 hgc->pgid = strtol(t + 2, NULL, 10);
350 } else if (strncmp(s, "pid:", t - s + 1) == 0) {
345 } else if (strncmp(s, "pid:", t - s + 1) == 0) {
351 hgc->pid = strtol(t + 2, NULL, 10);
346 hgc->pid = strtol(t + 2, NULL, 10);
352 }
347 }
353 s = u + 1;
348 s = u + 1;
354 }
349 }
355 debugmsg("capflags=0x%04x, pid=%d", hgc->capflags, hgc->pid);
350 debugmsg("capflags=0x%04x, pid=%d", hgc->capflags, hgc->pid);
356 }
351 }
357
352
358 static void attachio(hgclient_t *hgc)
353 static void attachio(hgclient_t *hgc)
359 {
354 {
360 debugmsg("request attachio");
355 debugmsg("request attachio");
361 static const char chcmd[] = "attachio\n";
356 static const char chcmd[] = "attachio\n";
362 sendall(hgc->sockfd, chcmd, sizeof(chcmd) - 1);
357 sendall(hgc->sockfd, chcmd, sizeof(chcmd) - 1);
363 readchannel(hgc);
358 readchannel(hgc);
364 context_t *ctx = &hgc->ctx;
359 context_t *ctx = &hgc->ctx;
365 if (ctx->ch != 'I')
360 if (ctx->ch != 'I')
366 abortmsg("unexpected response for attachio (ch = %c)", ctx->ch);
361 abortmsg("unexpected response for attachio (ch = %c)", ctx->ch);
367
362
368 static const int fds[3] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
363 static const int fds[3] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
369 struct msghdr msgh;
364 struct msghdr msgh;
370 memset(&msgh, 0, sizeof(msgh));
365 memset(&msgh, 0, sizeof(msgh));
371 struct iovec iov = {ctx->data, ctx->datasize}; /* dummy payload */
366 struct iovec iov = {ctx->data, ctx->datasize}; /* dummy payload */
372 msgh.msg_iov = &iov;
367 msgh.msg_iov = &iov;
373 msgh.msg_iovlen = 1;
368 msgh.msg_iovlen = 1;
374 char fdbuf[CMSG_SPACE(sizeof(fds))];
369 char fdbuf[CMSG_SPACE(sizeof(fds))];
375 msgh.msg_control = fdbuf;
370 msgh.msg_control = fdbuf;
376 msgh.msg_controllen = sizeof(fdbuf);
371 msgh.msg_controllen = sizeof(fdbuf);
377 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msgh);
372 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msgh);
378 cmsg->cmsg_level = SOL_SOCKET;
373 cmsg->cmsg_level = SOL_SOCKET;
379 cmsg->cmsg_type = SCM_RIGHTS;
374 cmsg->cmsg_type = SCM_RIGHTS;
380 cmsg->cmsg_len = CMSG_LEN(sizeof(fds));
375 cmsg->cmsg_len = CMSG_LEN(sizeof(fds));
381 memcpy(CMSG_DATA(cmsg), fds, sizeof(fds));
376 memcpy(CMSG_DATA(cmsg), fds, sizeof(fds));
382 msgh.msg_controllen = cmsg->cmsg_len;
377 msgh.msg_controllen = cmsg->cmsg_len;
383 ssize_t r = sendmsg(hgc->sockfd, &msgh, 0);
378 ssize_t r = sendmsg(hgc->sockfd, &msgh, 0);
384 if (r < 0)
379 if (r < 0)
385 abortmsgerrno("sendmsg failed");
380 abortmsgerrno("sendmsg failed");
386
381
387 handleresponse(hgc);
382 handleresponse(hgc);
388 int32_t n;
383 int32_t n;
389 if (ctx->datasize != sizeof(n))
384 if (ctx->datasize != sizeof(n))
390 abortmsg("unexpected size of attachio result");
385 abortmsg("unexpected size of attachio result");
391 memcpy(&n, ctx->data, sizeof(n));
386 memcpy(&n, ctx->data, sizeof(n));
392 n = ntohl(n);
387 n = ntohl(n);
393 if (n != sizeof(fds) / sizeof(fds[0]))
388 if (n != sizeof(fds) / sizeof(fds[0]))
394 abortmsg("failed to send fds (n = %d)", n);
389 abortmsg("failed to send fds (n = %d)", n);
395 }
390 }
396
391
397 static void chdirtocwd(hgclient_t *hgc)
392 static void chdirtocwd(hgclient_t *hgc)
398 {
393 {
399 if (!getcwd(hgc->ctx.data, hgc->ctx.maxdatasize))
394 if (!getcwd(hgc->ctx.data, hgc->ctx.maxdatasize))
400 abortmsgerrno("failed to getcwd");
395 abortmsgerrno("failed to getcwd");
401 hgc->ctx.datasize = strlen(hgc->ctx.data);
396 hgc->ctx.datasize = strlen(hgc->ctx.data);
402 writeblockrequest(hgc, "chdir");
397 writeblockrequest(hgc, "chdir");
403 }
398 }
404
399
405 static void forwardumask(hgclient_t *hgc)
400 static void forwardumask(hgclient_t *hgc)
406 {
401 {
407 mode_t mask = umask(0);
402 mode_t mask = umask(0);
408 umask(mask);
403 umask(mask);
409
404
410 static const char command[] = "setumask\n";
405 static const char command[] = "setumask\n";
411 sendall(hgc->sockfd, command, sizeof(command) - 1);
406 sendall(hgc->sockfd, command, sizeof(command) - 1);
412 uint32_t data = htonl(mask);
407 uint32_t data = htonl(mask);
413 sendall(hgc->sockfd, &data, sizeof(data));
408 sendall(hgc->sockfd, &data, sizeof(data));
414 }
409 }
415
410
416 /*!
411 /*!
417 * Open connection to per-user cmdserver
412 * Open connection to per-user cmdserver
418 *
413 *
419 * If no background server running, returns NULL.
414 * If no background server running, returns NULL.
420 */
415 */
421 hgclient_t *hgc_open(const char *sockname)
416 hgclient_t *hgc_open(const char *sockname)
422 {
417 {
423 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
418 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
424 if (fd < 0)
419 if (fd < 0)
425 abortmsgerrno("cannot create socket");
420 abortmsgerrno("cannot create socket");
426
421
427 /* don't keep fd on fork(), so that it can be closed when the parent
422 /* don't keep fd on fork(), so that it can be closed when the parent
428 * process get terminated. */
423 * process get terminated. */
429 fsetcloexec(fd);
424 fsetcloexec(fd);
430
425
431 struct sockaddr_un addr;
426 struct sockaddr_un addr;
432 addr.sun_family = AF_UNIX;
427 addr.sun_family = AF_UNIX;
433 strncpy(addr.sun_path, sockname, sizeof(addr.sun_path));
428 strncpy(addr.sun_path, sockname, sizeof(addr.sun_path));
434 addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
429 addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
435
430
436 int r = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
431 int r = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
437 if (r < 0) {
432 if (r < 0) {
438 close(fd);
433 close(fd);
439 if (errno == ENOENT || errno == ECONNREFUSED)
434 if (errno == ENOENT || errno == ECONNREFUSED)
440 return NULL;
435 return NULL;
441 abortmsgerrno("cannot connect to %s", addr.sun_path);
436 abortmsgerrno("cannot connect to %s", addr.sun_path);
442 }
437 }
443 debugmsg("connected to %s", addr.sun_path);
438 debugmsg("connected to %s", addr.sun_path);
444
439
445 hgclient_t *hgc = mallocx(sizeof(hgclient_t));
440 hgclient_t *hgc = mallocx(sizeof(hgclient_t));
446 memset(hgc, 0, sizeof(*hgc));
441 memset(hgc, 0, sizeof(*hgc));
447 hgc->sockfd = fd;
442 hgc->sockfd = fd;
448 initcontext(&hgc->ctx);
443 initcontext(&hgc->ctx);
449
444
450 readhello(hgc);
445 readhello(hgc);
451 if (!(hgc->capflags & CAP_RUNCOMMAND))
446 if (!(hgc->capflags & CAP_RUNCOMMAND))
452 abortmsg("insufficient capability: runcommand");
447 abortmsg("insufficient capability: runcommand");
453 if (hgc->capflags & CAP_ATTACHIO)
448 if (hgc->capflags & CAP_ATTACHIO)
454 attachio(hgc);
449 attachio(hgc);
455 if (hgc->capflags & CAP_CHDIR)
450 if (hgc->capflags & CAP_CHDIR)
456 chdirtocwd(hgc);
451 chdirtocwd(hgc);
457 if (hgc->capflags & CAP_SETUMASK)
452 if (hgc->capflags & CAP_SETUMASK)
458 forwardumask(hgc);
453 forwardumask(hgc);
459
454
460 return hgc;
455 return hgc;
461 }
456 }
462
457
463 /*!
458 /*!
464 * Close connection and free allocated memory
459 * Close connection and free allocated memory
465 */
460 */
466 void hgc_close(hgclient_t *hgc)
461 void hgc_close(hgclient_t *hgc)
467 {
462 {
468 assert(hgc);
463 assert(hgc);
469 freecontext(&hgc->ctx);
464 freecontext(&hgc->ctx);
470 close(hgc->sockfd);
465 close(hgc->sockfd);
471 free(hgc);
466 free(hgc);
472 }
467 }
473
468
474 pid_t hgc_peerpgid(const hgclient_t *hgc)
469 pid_t hgc_peerpgid(const hgclient_t *hgc)
475 {
470 {
476 assert(hgc);
471 assert(hgc);
477 return hgc->pgid;
472 return hgc->pgid;
478 }
473 }
479
474
480 pid_t hgc_peerpid(const hgclient_t *hgc)
475 pid_t hgc_peerpid(const hgclient_t *hgc)
481 {
476 {
482 assert(hgc);
477 assert(hgc);
483 return hgc->pid;
478 return hgc->pid;
484 }
479 }
485
480
486 /*!
481 /*!
487 * Send command line arguments to let the server load the repo config and check
482 * Send command line arguments to let the server load the repo config and check
488 * whether it can process our request directly or not.
483 * whether it can process our request directly or not.
489 * Make sure hgc_setenv is called before calling this.
484 * Make sure hgc_setenv is called before calling this.
490 *
485 *
491 * @return - NULL, the server believes it can handle our request, or does not
486 * @return - NULL, the server believes it can handle our request, or does not
492 * support "validate" command.
487 * support "validate" command.
493 * - a list of strings, the server probably cannot handle our request
488 * - a list of strings, the server probably cannot handle our request
494 * and it sent instructions telling us what to do next. See
489 * and it sent instructions telling us what to do next. See
495 * chgserver.py for possible instruction formats.
490 * chgserver.py for possible instruction formats.
496 * the list should be freed by the caller.
491 * the list should be freed by the caller.
497 * the last string is guaranteed to be NULL.
492 * the last string is guaranteed to be NULL.
498 */
493 */
499 const char **hgc_validate(hgclient_t *hgc, const char *const args[],
494 const char **hgc_validate(hgclient_t *hgc, const char *const args[],
500 size_t argsize)
495 size_t argsize)
501 {
496 {
502 assert(hgc);
497 assert(hgc);
503 if (!(hgc->capflags & CAP_VALIDATE))
498 if (!(hgc->capflags & CAP_VALIDATE))
504 return NULL;
499 return NULL;
505
500
506 packcmdargs(&hgc->ctx, args, argsize);
501 packcmdargs(&hgc->ctx, args, argsize);
507 writeblockrequest(hgc, "validate");
502 writeblockrequest(hgc, "validate");
508 handleresponse(hgc);
503 handleresponse(hgc);
509
504
510 /* the server returns '\0' if it can handle our request */
505 /* the server returns '\0' if it can handle our request */
511 if (hgc->ctx.datasize <= 1)
506 if (hgc->ctx.datasize <= 1)
512 return NULL;
507 return NULL;
513
508
514 /* make sure the buffer is '\0' terminated */
509 /* make sure the buffer is '\0' terminated */
515 enlargecontext(&hgc->ctx, hgc->ctx.datasize + 1);
510 enlargecontext(&hgc->ctx, hgc->ctx.datasize + 1);
516 hgc->ctx.data[hgc->ctx.datasize] = '\0';
511 hgc->ctx.data[hgc->ctx.datasize] = '\0';
517 return unpackcmdargsnul(&hgc->ctx);
512 return unpackcmdargsnul(&hgc->ctx);
518 }
513 }
519
514
520 /*!
515 /*!
521 * Execute the specified Mercurial command
516 * Execute the specified Mercurial command
522 *
517 *
523 * @return result code
518 * @return result code
524 */
519 */
525 int hgc_runcommand(hgclient_t *hgc, const char *const args[], size_t argsize)
520 int hgc_runcommand(hgclient_t *hgc, const char *const args[], size_t argsize)
526 {
521 {
527 assert(hgc);
522 assert(hgc);
528
523
529 packcmdargs(&hgc->ctx, args, argsize);
524 packcmdargs(&hgc->ctx, args, argsize);
530 writeblockrequest(hgc, "runcommand");
525 writeblockrequest(hgc, "runcommand");
531 handleresponse(hgc);
526 handleresponse(hgc);
532
527
533 int32_t exitcode_n;
528 int32_t exitcode_n;
534 if (hgc->ctx.datasize != sizeof(exitcode_n)) {
529 if (hgc->ctx.datasize != sizeof(exitcode_n)) {
535 abortmsg("unexpected size of exitcode");
530 abortmsg("unexpected size of exitcode");
536 }
531 }
537 memcpy(&exitcode_n, hgc->ctx.data, sizeof(exitcode_n));
532 memcpy(&exitcode_n, hgc->ctx.data, sizeof(exitcode_n));
538 return ntohl(exitcode_n);
533 return ntohl(exitcode_n);
539 }
534 }
540
535
541 /*!
536 /*!
542 * (Re-)send client's stdio channels so that the server can access to tty
537 * (Re-)send client's stdio channels so that the server can access to tty
543 */
538 */
544 void hgc_attachio(hgclient_t *hgc)
539 void hgc_attachio(hgclient_t *hgc)
545 {
540 {
546 assert(hgc);
541 assert(hgc);
547 if (!(hgc->capflags & CAP_ATTACHIO))
542 if (!(hgc->capflags & CAP_ATTACHIO))
548 return;
543 return;
549 attachio(hgc);
544 attachio(hgc);
550 }
545 }
551
546
552 /*!
547 /*!
553 * Get pager command for the given Mercurial command args
548 * Get pager command for the given Mercurial command args
554 *
549 *
555 * If no pager enabled, returns NULL. The return value becomes invalid
550 * If no pager enabled, returns NULL. The return value becomes invalid
556 * once you run another request to hgc.
551 * once you run another request to hgc.
557 */
552 */
558 const char *hgc_getpager(hgclient_t *hgc, const char *const args[],
553 const char *hgc_getpager(hgclient_t *hgc, const char *const args[],
559 size_t argsize)
554 size_t argsize)
560 {
555 {
561 assert(hgc);
556 assert(hgc);
562
557
563 if (!(hgc->capflags & CAP_GETPAGER))
558 if (!(hgc->capflags & CAP_GETPAGER))
564 return NULL;
559 return NULL;
565
560
566 packcmdargs(&hgc->ctx, args, argsize);
561 packcmdargs(&hgc->ctx, args, argsize);
567 writeblockrequest(hgc, "getpager");
562 writeblockrequest(hgc, "getpager");
568 handleresponse(hgc);
563 handleresponse(hgc);
569
564
570 if (hgc->ctx.datasize < 1 || hgc->ctx.data[0] == '\0')
565 if (hgc->ctx.datasize < 1 || hgc->ctx.data[0] == '\0')
571 return NULL;
566 return NULL;
572 enlargecontext(&hgc->ctx, hgc->ctx.datasize + 1);
567 enlargecontext(&hgc->ctx, hgc->ctx.datasize + 1);
573 hgc->ctx.data[hgc->ctx.datasize] = '\0';
568 hgc->ctx.data[hgc->ctx.datasize] = '\0';
574 return hgc->ctx.data;
569 return hgc->ctx.data;
575 }
570 }
576
571
577 /*!
572 /*!
578 * Update server's environment variables
573 * Update server's environment variables
579 *
574 *
580 * @param envp list of environment variables in "NAME=VALUE" format,
575 * @param envp list of environment variables in "NAME=VALUE" format,
581 * terminated by NULL.
576 * terminated by NULL.
582 */
577 */
583 void hgc_setenv(hgclient_t *hgc, const char *const envp[])
578 void hgc_setenv(hgclient_t *hgc, const char *const envp[])
584 {
579 {
585 assert(hgc && envp);
580 assert(hgc && envp);
586 if (!(hgc->capflags & CAP_SETENV))
581 if (!(hgc->capflags & CAP_SETENV))
587 return;
582 return;
588 packcmdargs(&hgc->ctx, envp, /*argsize*/ -1);
583 packcmdargs(&hgc->ctx, envp, /*argsize*/ -1);
589 writeblockrequest(hgc, "setenv");
584 writeblockrequest(hgc, "setenv");
590 }
585 }
General Comments 0
You need to be logged in to leave comments. Login now