xref: /dflybsd-src/bin/cpdup/hclink.c (revision 90ea502b8c5d21f908cedff6680ee2bc9e74ce74)
1 /*
2  * HCLINK.C
3  *
4  * This module implements a simple remote control protocol
5  *
6  * $DragonFly: src/bin/cpdup/hclink.c,v 1.10 2008/05/24 17:21:36 dillon Exp $
7  */
8 
9 #include "cpdup.h"
10 #include "hclink.h"
11 #include "hcproto.h"
12 
13 static struct HCHead *hcc_read_command(struct HostConf *hc, hctransaction_t trans);
14 static void hcc_start_reply(hctransaction_t trans, struct HCHead *rhead);
15 
16 int
17 hcc_connect(struct HostConf *hc)
18 {
19     int fdin[2];
20     int fdout[2];
21     const char *av[32];
22 
23     if (hc == NULL || hc->host == NULL)
24 	return(0);
25 
26     if (pipe(fdin) < 0)
27 	return(-1);
28     if (pipe(fdout) < 0) {
29 	close(fdin[0]);
30 	close(fdin[1]);
31 	return(-1);
32     }
33     if ((hc->pid = fork()) == 0) {
34 	/*
35 	 * Child process
36 	 */
37 	int n, m;
38 
39 	dup2(fdin[1], 1);
40 	close(fdin[0]);
41 	close(fdin[1]);
42 	dup2(fdout[0], 0);
43 	close(fdout[0]);
44 	close(fdout[1]);
45 
46 	n = 0;
47 	av[n++] = "ssh";
48 	if (CompressOpt)
49 	    av[n++] = "-C";
50 	for (m = 0; m < ssh_argc; m++)
51 	    av[n++] = ssh_argv[m];
52 	av[n++] = "-T";
53 	av[n++] = hc->host;
54 	av[n++] = "cpdup";
55 	av[n++] = "-S";
56 	av[n++] = NULL;
57 
58 	execv("/usr/bin/ssh", (void *)av);
59 	_exit(1);
60     } else if (hc->pid < 0) {
61 	return(-1);
62     } else {
63 	/*
64 	 * Parent process.  Do the initial handshake to make sure we are
65 	 * actually talking to a cpdup slave.
66 	 */
67 	close(fdin[1]);
68 	hc->fdin = fdin[0];
69 	close(fdout[0]);
70 	hc->fdout = fdout[1];
71 	return(0);
72     }
73 }
74 
75 static int
76 rc_badop(hctransaction_t trans __unused, struct HCHead *head)
77 {
78     head->error = EOPNOTSUPP;
79     return(0);
80 }
81 
82 int
83 hcc_slave(int fdin, int fdout, struct HCDesc *descs, int count)
84 {
85     struct HostConf hcslave;
86     struct HCHead *head;
87     struct HCHead *whead;
88     struct HCTransaction trans;
89     int (*dispatch[256])(hctransaction_t, struct HCHead *);
90     int aligned_bytes;
91     int i;
92     int r;
93 
94     bzero(&hcslave, sizeof(hcslave));
95     bzero(&trans, sizeof(trans));
96     bzero(dispatch, sizeof(dispatch));
97     for (i = 0; i < count; ++i) {
98 	struct HCDesc *desc = &descs[i];
99 	assert(desc->cmd >= 0 && desc->cmd < 256);
100 	dispatch[desc->cmd] = desc->func;
101     }
102     for (i = 0; i < 256; ++i) {
103 	if (dispatch[i] == NULL)
104 	    dispatch[i] = rc_badop;
105     }
106     hcslave.fdin = fdin;
107     hcslave.fdout = fdout;
108     trans.hc = &hcslave;
109 
110     /*
111      * Process commands on fdin and write out results on fdout
112      */
113     for (;;) {
114 	/*
115 	 * Get the command
116 	 */
117 	head = hcc_read_command(trans.hc, &trans);
118 	if (head == NULL)
119 	    break;
120 
121 	/*
122 	 * Start the reply and dispatch, then process the return code.
123 	 */
124 	head->error = 0;
125 	hcc_start_reply(&trans, head);
126 
127 	r = dispatch[head->cmd & 255](&trans, head);
128 
129 	switch(r) {
130 	case -2:
131 		head->error = EINVAL;
132 		break;
133 	case -1:
134 		head->error = errno;
135 		break;
136 	case 0:
137 		break;
138 	default:
139 		assert(0);
140 		break;
141 	}
142 
143 	/*
144 	 * Write out the reply
145 	 */
146 	whead = (void *)trans.wbuf;
147 	whead->bytes = trans.windex;
148 	whead->error = head->error;
149 	aligned_bytes = HCC_ALIGN(trans.windex);
150 #ifdef DEBUG
151 	hcc_debug_dump(whead);
152 #endif
153 	if (write(hcslave.fdout, whead, aligned_bytes) != aligned_bytes)
154 	    break;
155     }
156     return(0);
157 }
158 
159 /*
160  * This reads a command from fdin, fixes up the byte ordering, and returns
161  * a pointer to HCHead.
162  *
163  * The MasterMutex may or may not be held.  When threaded this command
164  * is serialized by a reader thread.
165  */
166 static
167 struct HCHead *
168 hcc_read_command(struct HostConf *hc, hctransaction_t trans)
169 {
170     hctransaction_t fill;
171     struct HCHead tmp;
172     int aligned_bytes;
173     int n;
174     int r;
175 
176     n = 0;
177     while (n < (int)sizeof(struct HCHead)) {
178 	r = read(hc->fdin, (char *)&tmp + n, sizeof(struct HCHead) - n);
179 	if (r <= 0)
180 	    goto fail;
181 	n += r;
182     }
183 
184     assert(tmp.bytes >= (int)sizeof(tmp) && tmp.bytes < 65536);
185     assert(tmp.magic == HCMAGIC);
186 
187     if (trans) {
188 	fill = trans;
189     } else {
190 	fprintf(stderr, "cpdup hlink protocol error with %s (%04x)\n",
191 		hc->host, tmp.id);
192 	exit(1);
193     }
194 
195     bcopy(&tmp, fill->rbuf, n);
196     aligned_bytes = HCC_ALIGN(tmp.bytes);
197 
198     while (n < aligned_bytes) {
199 	r = read(hc->fdin, fill->rbuf + n, aligned_bytes - n);
200 	if (r <= 0)
201 	    goto fail;
202 	n += r;
203     }
204 #ifdef DEBUG
205     hcc_debug_dump(head);
206 #endif
207     fill->state = HCT_REPLIED;
208     return((void *)fill->rbuf);
209 fail:
210     return(NULL);
211 }
212 
213 static
214 hctransaction_t
215 hcc_get_trans(struct HostConf *hc)
216 {
217     return(&hc->trans);
218 }
219 
220 void
221 hcc_free_trans(struct HostConf *hc __unused)
222 {
223     /* nop */
224 }
225 
226 /*
227  * Initialize for a new command
228  */
229 hctransaction_t
230 hcc_start_command(struct HostConf *hc, int16_t cmd)
231 {
232     struct HCHead *whead;
233     hctransaction_t trans;
234 
235     trans = hcc_get_trans(hc);
236 
237     whead = (void *)trans->wbuf;
238     whead->magic = HCMAGIC;
239     whead->bytes = 0;
240     whead->cmd = cmd;
241     whead->id = trans->id;
242     whead->error = 0;
243 
244     trans->windex = sizeof(*whead);
245     trans->hc = hc;
246     trans->state = HCT_IDLE;
247 
248     return(trans);
249 }
250 
251 static void
252 hcc_start_reply(hctransaction_t trans, struct HCHead *rhead)
253 {
254     struct HCHead *whead = (void *)trans->wbuf;
255 
256     whead->magic = HCMAGIC;
257     whead->bytes = 0;
258     whead->cmd = rhead->cmd | HCF_REPLY;
259     whead->id = rhead->id;
260     whead->error = 0;
261 
262     trans->windex = sizeof(*whead);
263 }
264 
265 /*
266  * Finish constructing a command, transmit it, and await the reply.
267  * Return the HCHead of the reply.
268  */
269 struct HCHead *
270 hcc_finish_command(hctransaction_t trans)
271 {
272     struct HostConf *hc;
273     struct HCHead *whead;
274     struct HCHead *rhead;
275     int aligned_bytes;
276     int16_t wcmd;
277 
278     hc = trans->hc;
279     whead = (void *)trans->wbuf;
280     whead->bytes = trans->windex;
281     aligned_bytes = HCC_ALIGN(trans->windex);
282 
283     trans->state = HCT_SENT;
284 
285     if (write(hc->fdout, whead, aligned_bytes) != aligned_bytes) {
286 #ifdef __error
287 	*__error = EIO;
288 #else
289 	errno = EIO;
290 #endif
291 	if (whead->cmd < 0x0010)
292 		return(NULL);
293 	fprintf(stderr, "cpdup lost connection to %s\n", hc->host);
294 	exit(1);
295     }
296 
297     wcmd = whead->cmd;
298 
299     /*
300      * whead is invalid when we call hcc_read_command() because
301      * we may switch to another thread.
302      */
303     rhead = hcc_read_command(hc, trans);
304     if (trans->state != HCT_REPLIED || rhead->id != trans->id) {
305 #ifdef __error
306 	*__error = EIO;
307 #else
308 	errno = EIO;
309 #endif
310 	if (wcmd < 0x0010)
311 		return(NULL);
312 	fprintf(stderr, "cpdup lost connection to %s\n", hc->host);
313 	exit(1);
314     }
315     trans->state = HCT_DONE;
316 
317     if (rhead->error) {
318 #ifdef __error
319 	*__error = rhead->error;
320 #else
321 	errno = rhead->error;
322 #endif
323     }
324     return (rhead);
325 }
326 
327 void
328 hcc_leaf_string(hctransaction_t trans, int16_t leafid, const char *str)
329 {
330     struct HCLeaf *item;
331     int bytes = strlen(str) + 1;
332 
333     item = (void *)(trans->wbuf + trans->windex);
334     assert(trans->windex + sizeof(*item) + bytes < 65536);
335     item->leafid = leafid;
336     item->reserved = 0;
337     item->bytes = sizeof(*item) + bytes;
338     bcopy(str, item + 1, bytes);
339     trans->windex = HCC_ALIGN(trans->windex + item->bytes);
340 }
341 
342 void
343 hcc_leaf_data(hctransaction_t trans, int16_t leafid, const void *ptr, int bytes)
344 {
345     struct HCLeaf *item;
346 
347     item = (void *)(trans->wbuf + trans->windex);
348     assert(trans->windex + sizeof(*item) + bytes < 65536);
349     item->leafid = leafid;
350     item->reserved = 0;
351     item->bytes = sizeof(*item) + bytes;
352     bcopy(ptr, item + 1, bytes);
353     trans->windex = HCC_ALIGN(trans->windex + item->bytes);
354 }
355 
356 void
357 hcc_leaf_int32(hctransaction_t trans, int16_t leafid, int32_t value)
358 {
359     struct HCLeaf *item;
360 
361     item = (void *)(trans->wbuf + trans->windex);
362     assert(trans->windex + sizeof(*item) + sizeof(value) < 65536);
363     item->leafid = leafid;
364     item->reserved = 0;
365     item->bytes = sizeof(*item) + sizeof(value);
366     *(int32_t *)(item + 1) = value;
367     trans->windex = HCC_ALIGN(trans->windex + item->bytes);
368 }
369 
370 void
371 hcc_leaf_int64(hctransaction_t trans, int16_t leafid, int64_t value)
372 {
373     struct HCLeaf *item;
374 
375     item = (void *)(trans->wbuf + trans->windex);
376     assert(trans->windex + sizeof(*item) + sizeof(value) < 65536);
377     item->leafid = leafid;
378     item->reserved = 0;
379     item->bytes = sizeof(*item) + sizeof(value);
380     *(int64_t *)(item + 1) = value;
381     trans->windex = HCC_ALIGN(trans->windex + item->bytes);
382 }
383 
384 int
385 hcc_alloc_descriptor(struct HostConf *hc, void *ptr, int type)
386 {
387     struct HCHostDesc *hd;
388     struct HCHostDesc *hnew;
389 
390     hnew = malloc(sizeof(struct HCHostDesc));
391     hnew->type = type;
392     hnew->data = ptr;
393 
394     if ((hd = hc->hostdescs) != NULL) {
395 	hnew->desc = hd->desc + 1;
396     } else {
397 	hnew->desc = 1;
398     }
399     hnew->next = hd;
400     hc->hostdescs = hnew;
401     return(hnew->desc);
402 }
403 
404 void *
405 hcc_get_descriptor(struct HostConf *hc, int desc, int type)
406 {
407     struct HCHostDesc *hd;
408 
409     for (hd = hc->hostdescs; hd; hd = hd->next) {
410 	if (hd->desc == desc && hd->type == type)
411 	    return(hd->data);
412     }
413     return(NULL);
414 }
415 
416 void
417 hcc_set_descriptor(struct HostConf *hc, int desc, void *ptr, int type)
418 {
419     struct HCHostDesc *hd;
420     struct HCHostDesc **hdp;
421 
422     for (hdp = &hc->hostdescs; (hd = *hdp) != NULL; hdp = &hd->next) {
423 	if (hd->desc == desc) {
424 	    if (ptr) {
425 		hd->data = ptr;
426 		hd->type = type;
427 	    } else {
428 		*hdp = hd->next;
429 		free(hd);
430 	    }
431 	    return;
432 	}
433     }
434     if (ptr) {
435 	hd = malloc(sizeof(*hd));
436 	hd->desc = desc;
437 	hd->type = type;
438 	hd->data = ptr;
439 	hd->next = hc->hostdescs;
440 	hc->hostdescs = hd;
441     }
442 }
443 
444 struct HCLeaf *
445 hcc_firstitem(struct HCHead *head)
446 {
447     struct HCLeaf *item;
448     int offset;
449 
450     offset = sizeof(*head);
451     if (offset == head->bytes)
452 	return(NULL);
453     assert(head->bytes >= offset + (int)sizeof(*item));
454     item = (void *)(head + 1);
455     assert(head->bytes >= offset + item->bytes);
456     assert(item->bytes >= (int)sizeof(*item) && item->bytes < 65536 - offset);
457     return (item);
458 }
459 
460 struct HCLeaf *
461 hcc_nextitem(struct HCHead *head, struct HCLeaf *item)
462 {
463     int offset;
464 
465     item = (void *)((char *)item + HCC_ALIGN(item->bytes));
466     offset = (char *)item - (char *)head;
467     if (offset == head->bytes)
468 	return(NULL);
469     assert(head->bytes >= offset + (int)sizeof(*item));
470     assert(head->bytes >= offset + item->bytes);
471     assert(item->bytes >= (int)sizeof(*item) && item->bytes < 65536 - offset);
472     return (item);
473 }
474 
475 #ifdef DEBUG
476 
477 void
478 hcc_debug_dump(struct HCHead *head)
479 {
480     struct HCLeaf *item;
481     int aligned_bytes = HCC_ALIGN(head->bytes);
482 
483     fprintf(stderr, "DUMP %04x (%d)", (u_int16_t)head->cmd, aligned_bytes);
484     if (head->cmd & HCF_REPLY)
485 	fprintf(stderr, " error %d", head->error);
486     fprintf(stderr, "\n");
487     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
488 	fprintf(stderr, "    ITEM %04x DATA ", item->leafid);
489 	switch(item->leafid & LCF_TYPEMASK) {
490 	case LCF_INT32:
491 	    fprintf(stderr, "int32 %d\n", *(int32_t *)(item + 1));
492 	    break;
493 	case LCF_INT64:
494 	    fprintf(stderr, "int64 %lld\n", *(int64_t *)(item + 1));
495 	    break;
496 	case LCF_STRING:
497 	    fprintf(stderr, "\"%s\"\n", (char *)(item + 1));
498 	    break;
499 	case LCF_BINARY:
500 	    fprintf(stderr, "(binary)\n");
501 	    break;
502 	default:
503 	    printf("?\n");
504 	}
505     }
506 }
507 
508 #endif
509