xref: /dragonfly/bin/cpdup/hclink.c (revision 7ff0fc30)
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 void hcc_start_reply(hctransaction_t trans, struct HCHead *rhead);
14 static int hcc_finish_reply(hctransaction_t trans, struct HCHead *head);
15 
16 int
17 hcc_connect(struct HostConf *hc, int readonly)
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++] = (readonly ? "-RS" : "-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 HCTransaction trans;
88     int (*dispatch[256])(hctransaction_t, struct HCHead *);
89     int i;
90     int r;
91 
92     bzero(&hcslave, sizeof(hcslave));
93     bzero(&trans, sizeof(trans));
94     bzero(dispatch, sizeof(dispatch));
95     for (i = 0; i < count; ++i) {
96 	struct HCDesc *desc = &descs[i];
97 	assert(desc->cmd >= 0 && desc->cmd < 256);
98 	dispatch[desc->cmd] = desc->func;
99     }
100     for (i = 0; i < 256; ++i) {
101 	if (dispatch[i] == NULL)
102 	    dispatch[i] = rc_badop;
103     }
104     hcslave.fdin = fdin;
105     hcslave.fdout = fdout;
106     trans.hc = &hcslave;
107 
108     /*
109      * Process commands on fdin and write out results on fdout
110      */
111     for (;;) {
112 	/*
113 	 * Get the command
114 	 */
115 	head = hcc_read_command(trans.hc, &trans);
116 	if (head == NULL)
117 	    break;
118 
119 	/*
120 	 * Start the reply and dispatch, then process the return code.
121 	 */
122 	head->error = 0;
123 	hcc_start_reply(&trans, head);
124 
125 	r = dispatch[head->cmd & 255](&trans, head);
126 
127 	switch(r) {
128 	case -2:
129 		head->error = EINVAL;
130 		break;
131 	case -1:
132 		head->error = errno;
133 		break;
134 	case 0:
135 		break;
136 	default:
137 		assert(0);
138 		break;
139 	}
140 
141 	if (!hcc_finish_reply(&trans, head))
142 	    break;
143     }
144     return(0);
145 }
146 
147 /*
148  * This reads a command from fdin, fixes up the byte ordering, and returns
149  * a pointer to HCHead.
150  */
151 struct HCHead *
152 hcc_read_command(struct HostConf *hc, hctransaction_t trans)
153 {
154     struct HCHead tmp;
155     int aligned_bytes;
156     int need_swap;
157     int n;
158     int r;
159 
160     if (trans == NULL)
161 	fatal("cpdup hlink protocol error with %s", hc->host);
162 
163     n = 0;
164     while (n < (int)sizeof(struct HCHead)) {
165 	r = read(hc->fdin, (char *)&tmp + n, sizeof(struct HCHead) - n);
166 	if (r <= 0)
167 	    goto fail;
168 	n += r;
169     }
170 
171     if (tmp.magic == HCMAGIC) {
172 	need_swap = 0;
173     } else {
174 	tmp.magic = hc_bswap32(tmp.magic);
175 	if (tmp.magic != HCMAGIC)
176 	    fatal("magic mismatch with %s (%04x)", hc->host, tmp.id);
177 	need_swap = 1;
178 	tmp.bytes = hc_bswap32(tmp.bytes);
179 	tmp.cmd   = hc_bswap16(tmp.cmd);
180 	tmp.id    = hc_bswap16(tmp.id);
181 	tmp.error = hc_bswap32(tmp.error);
182     }
183 
184     assert(tmp.bytes >= (int)sizeof(tmp) && tmp.bytes < HC_BUFSIZE);
185 
186     trans->swap = need_swap;
187     bcopy(&tmp, trans->rbuf, n);
188     aligned_bytes = HCC_ALIGN(tmp.bytes);
189 
190     while (n < aligned_bytes) {
191 	r = read(hc->fdin, trans->rbuf + n, aligned_bytes - n);
192 	if (r <= 0)
193 	    goto fail;
194 	n += r;
195     }
196 #ifdef DEBUG
197     hcc_debug_dump(trans, head);
198 #endif
199     trans->state = HCT_REPLIED;
200     return((void *)trans->rbuf);
201 fail:
202     trans->state = HCT_FAIL;
203     return(NULL);
204 }
205 
206 /*
207  * Initialize for a new command
208  */
209 hctransaction_t
210 hcc_start_command(struct HostConf *hc, int16_t cmd)
211 {
212     struct HCHead *whead;
213     hctransaction_t trans;
214 
215     trans = &hc->trans;
216 
217     whead = (void *)trans->wbuf;
218     whead->magic = HCMAGIC;
219     whead->bytes = 0;
220     whead->cmd = cmd;
221     whead->id = trans->id;
222     whead->error = 0;
223 
224     trans->windex = sizeof(*whead);
225     trans->hc = hc;
226     trans->state = HCT_IDLE;
227 
228     return(trans);
229 }
230 
231 static void
232 hcc_start_reply(hctransaction_t trans, struct HCHead *rhead)
233 {
234     struct HCHead *whead = (void *)trans->wbuf;
235 
236     whead->magic = HCMAGIC;
237     whead->bytes = 0;
238     whead->cmd = rhead->cmd | HCF_REPLY;
239     whead->id = rhead->id;
240     whead->error = 0;
241 
242     trans->windex = sizeof(*whead);
243 }
244 
245 /*
246  * Finish constructing a command, transmit it, and await the reply.
247  * Return the HCHead of the reply.
248  */
249 struct HCHead *
250 hcc_finish_command(hctransaction_t trans)
251 {
252     struct HostConf *hc;
253     struct HCHead *whead;
254     struct HCHead *rhead;
255     int aligned_bytes;
256     int16_t wcmd;
257 
258     hc = trans->hc;
259     whead = (void *)trans->wbuf;
260     whead->bytes = trans->windex;
261     aligned_bytes = HCC_ALIGN(trans->windex);
262     trans->windex = 0;	/* initialize for hcc_nextchaineditem() */
263 
264     trans->state = HCT_SENT;
265 
266     if (write(hc->fdout, whead, aligned_bytes) != aligned_bytes) {
267 #ifdef __error
268 	*__error = EIO;
269 #else
270 	errno = EIO;
271 #endif
272 	if (whead->cmd < 0x0010)
273 	    return(NULL);
274 	fatal("cpdup lost connection to %s", hc->host);
275     }
276 
277     wcmd = whead->cmd;
278 
279     /*
280      * whead is invalid when we call hcc_read_command() because
281      * we may switch to another thread.
282      */
283     rhead = hcc_read_command(hc, trans);
284     if (trans->state != HCT_REPLIED || rhead->id != trans->id) {
285 #ifdef __error
286 	*__error = EIO;
287 #else
288 	errno = EIO;
289 #endif
290 	if (wcmd < 0x0010)
291 		return(NULL);
292 	fatal("cpdup lost connection to %s", hc->host);
293     }
294     trans->state = HCT_DONE;
295 
296     if (rhead->error) {
297 #ifdef __error
298 	*__error = rhead->error;
299 #else
300 	errno = rhead->error;
301 #endif
302     }
303     return (rhead);
304 }
305 
306 int
307 hcc_finish_reply(hctransaction_t trans, struct HCHead *head)
308 {
309     struct HCHead *whead;
310     int aligned_bytes;
311 
312     whead = (void *)trans->wbuf;
313     whead->bytes = trans->windex;
314     whead->error = head->error;
315     aligned_bytes = HCC_ALIGN(trans->windex);
316 #ifdef DEBUG
317     hcc_debug_dump(trans, whead);
318 #endif
319     return (write(trans->hc->fdout, whead, aligned_bytes) == aligned_bytes);
320 }
321 
322 void
323 hcc_leaf_string(hctransaction_t trans, int16_t leafid, const char *str)
324 {
325     struct HCLeaf *item;
326     int bytes = strlen(str) + 1;
327 
328     item = (void *)(trans->wbuf + trans->windex);
329     assert(trans->windex + sizeof(*item) + bytes < HC_BUFSIZE);
330     item->leafid = leafid;
331     item->reserved = 0;
332     item->bytes = sizeof(*item) + bytes;
333     bcopy(str, item + 1, bytes);
334     trans->windex = HCC_ALIGN(trans->windex + item->bytes);
335 }
336 
337 void
338 hcc_leaf_data(hctransaction_t trans, int16_t leafid, const void *ptr, int bytes)
339 {
340     struct HCLeaf *item;
341 
342     item = (void *)(trans->wbuf + trans->windex);
343     assert(trans->windex + sizeof(*item) + bytes < HC_BUFSIZE);
344     item->leafid = leafid;
345     item->reserved = 0;
346     item->bytes = sizeof(*item) + bytes;
347     bcopy(ptr, item + 1, bytes);
348     trans->windex = HCC_ALIGN(trans->windex + item->bytes);
349 }
350 
351 void
352 hcc_leaf_int32(hctransaction_t trans, int16_t leafid, int32_t value)
353 {
354     struct HCLeaf *item;
355 
356     item = (void *)(trans->wbuf + trans->windex);
357     assert(trans->windex + sizeof(*item) + sizeof(value) < HC_BUFSIZE);
358     item->leafid = leafid;
359     item->reserved = 0;
360     item->bytes = sizeof(*item) + sizeof(value);
361     *(int32_t *)(item + 1) = value;
362     trans->windex = HCC_ALIGN(trans->windex + item->bytes);
363 }
364 
365 void
366 hcc_leaf_int64(hctransaction_t trans, int16_t leafid, int64_t value)
367 {
368     struct HCLeaf *item;
369 
370     item = (void *)(trans->wbuf + trans->windex);
371     assert(trans->windex + sizeof(*item) + sizeof(value) < HC_BUFSIZE);
372     item->leafid = leafid;
373     item->reserved = 0;
374     item->bytes = sizeof(*item) + sizeof(value);
375     *(int64_t *)(item + 1) = value;
376     trans->windex = HCC_ALIGN(trans->windex + item->bytes);
377 }
378 
379 /*
380  * Check if there's enough space left in the write buffer for <n>
381  * leaves with a total of <size> data bytes.
382  * If not, the current packet will be sent with the HCF_CONTINUE flag,
383  * then the transaction is initialized for another reply packet.
384  *
385  * Returns success status (boolean).
386  */
387 int
388 hcc_check_space(hctransaction_t trans, struct HCHead *head, int n, int size)
389 {
390     size = HCC_ALIGN(size) + n * sizeof(struct HCLeaf);
391     if (size >= HC_BUFSIZE - trans->windex) {
392 	struct HCHead *whead = (void *)trans->wbuf;
393 
394 	whead->cmd |= HCF_CONTINUE;
395 	if (!hcc_finish_reply(trans, head))
396 	    return (0);
397 	hcc_start_reply(trans, head);
398     }
399     return (1);
400 }
401 
402 intptr_t
403 hcc_alloc_descriptor(struct HostConf *hc, void *ptr, int type)
404 {
405     struct HCHostDesc *hd;
406     struct HCHostDesc *hnew;
407 
408     hnew = malloc(sizeof(struct HCHostDesc));
409     hnew->type = type;
410     hnew->data = ptr;
411 
412     if ((hd = hc->hostdescs) != NULL) {
413 	hnew->desc = hd->desc + 1;
414     } else {
415 	/* start at 2 because 1 has a special meaning in hc_open() */
416 	hnew->desc = 2;
417     }
418     hnew->next = hd;
419     hc->hostdescs = hnew;
420     return(hnew->desc);
421 }
422 
423 void *
424 hcc_get_descriptor(struct HostConf *hc, intptr_t desc, int type)
425 {
426     struct HCHostDesc *hd;
427 
428     for (hd = hc->hostdescs; hd; hd = hd->next) {
429 	if (hd->desc == desc && hd->type == type)
430 	    return(hd->data);
431     }
432     return(NULL);
433 }
434 
435 void
436 hcc_set_descriptor(struct HostConf *hc, intptr_t desc, void *ptr, int type)
437 {
438     struct HCHostDesc *hd;
439     struct HCHostDesc **hdp;
440 
441     for (hdp = &hc->hostdescs; (hd = *hdp) != NULL; hdp = &hd->next) {
442 	if (hd->desc == desc) {
443 	    if (ptr) {
444 		hd->data = ptr;
445 		hd->type = type;
446 	    } else {
447 		*hdp = hd->next;
448 		free(hd);
449 	    }
450 	    return;
451 	}
452     }
453     if (ptr) {
454 	hd = malloc(sizeof(*hd));
455 	hd->desc = desc;
456 	hd->type = type;
457 	hd->data = ptr;
458 	hd->next = hc->hostdescs;
459 	hc->hostdescs = hd;
460     }
461 }
462 
463 struct HCLeaf *
464 hcc_nextitem(hctransaction_t trans, struct HCHead *head, struct HCLeaf *item)
465 {
466     int offset;
467 
468     if (item == NULL)
469 	item = (void *)(head + 1);
470     else
471 	item = (void *)((char *)item + HCC_ALIGN(item->bytes));
472     offset = (char *)item - (char *)head;
473     if (offset == head->bytes)
474 	return(NULL);
475     if (trans->swap) {
476 	int64_t *i64ptr;
477 	int32_t *i32ptr;
478 
479 	item->leafid = hc_bswap16(item->leafid);
480 	item->bytes  = hc_bswap32(item->bytes);
481 	switch (item->leafid & LCF_TYPEMASK) {
482 	    case LCF_INT32:
483 		i32ptr = (void *)(item + 1);
484 		*i32ptr = hc_bswap32(*i32ptr);
485 		break;
486 	    case LCF_INT64:
487 		i64ptr = (void *)(item + 1);
488 		*i64ptr = hc_bswap64(*i64ptr);
489 		break;
490 	}
491     }
492     assert(head->bytes >= offset + (int)sizeof(*item));
493     assert(head->bytes >= offset + item->bytes);
494     assert(item->bytes >= (int)sizeof(*item) && item->bytes < HC_BUFSIZE);
495     return (item);
496 }
497 
498 struct HCLeaf *
499 hcc_nextchaineditem(struct HostConf *hc, struct HCHead *head)
500 {
501     hctransaction_t trans = &hc->trans;
502     struct HCLeaf *item = hcc_currentchaineditem(hc, head);
503 
504     while ((item = hcc_nextitem(trans, head, item)) == NULL) {
505 	if (!(head->cmd & HCF_CONTINUE))
506 	    return (NULL);
507 	head = hcc_read_command(hc, trans);
508 	if (trans->state != HCT_REPLIED || head->id != trans->id)
509 	    return (NULL);
510     }
511     trans->windex = (char *)item - (char *)head;
512     return (item);
513 }
514 
515 struct HCLeaf *
516 hcc_currentchaineditem(struct HostConf *hc, struct HCHead *head)
517 {
518     hctransaction_t trans = &hc->trans;
519 
520     if (trans->windex == 0)
521 	return (NULL);
522     else
523 	return ((void *) ((char *)head + trans->windex));
524 }
525 
526 #ifdef DEBUG
527 
528 void
529 hcc_debug_dump(hctransaction_t trans, struct HCHead *head)
530 {
531     struct HCLeaf *item;
532     int aligned_bytes = HCC_ALIGN(head->bytes);
533 
534     fprintf(stderr, "DUMP %04x (%d)", (uint16_t)head->cmd, aligned_bytes);
535     if (head->cmd & HCF_REPLY)
536 	fprintf(stderr, " error %d", head->error);
537     fprintf(stderr, "\n");
538     FOR_EACH_ITEM(item, trans, head) {
539 	fprintf(stderr, "    ITEM %04x DATA ", item->leafid);
540 	switch(item->leafid & LCF_TYPEMASK) {
541 	case LCF_INT32:
542 	    fprintf(stderr, "int32 %d\n", HCC_INT32(item));
543 	    break;
544 	case LCF_INT64:
545 	    fprintf(stderr, "int64 %lld\n", HCC_INT64(item));
546 	    break;
547 	case LCF_STRING:
548 	    fprintf(stderr, "\"%s\"\n", HCC_STRING(item));
549 	    break;
550 	case LCF_BINARY:
551 	    fprintf(stderr, "(binary)\n");
552 	    break;
553 	default:
554 	    printf("?\n");
555 	}
556     }
557 }
558 
559 #endif
560