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