xref: /dragonfly/bin/cpdup/hclink.c (revision 8bf5b238b6869b07d32313f388d8d715ae7c284d)
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
hcc_connect(struct HostConf * hc,int readonly)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
rc_badop(hctransaction_t trans __unused,struct HCHead * head)76 rc_badop(hctransaction_t trans __unused, struct HCHead *head)
77 {
78     head->error = EOPNOTSUPP;
79     return(0);
80 }
81 
82 int
hcc_slave(int fdin,int fdout,struct HCDesc * descs,int count)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 *
hcc_read_command(struct HostConf * hc,hctransaction_t trans)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
hcc_start_command(struct HostConf * hc,int16_t cmd)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
hcc_start_reply(hctransaction_t trans,struct HCHead * rhead)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 *
hcc_finish_command(hctransaction_t trans)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
hcc_finish_reply(hctransaction_t trans,struct HCHead * head)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
hcc_leaf_string(hctransaction_t trans,int16_t leafid,const char * str)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
hcc_leaf_data(hctransaction_t trans,int16_t leafid,const void * ptr,int bytes)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
hcc_leaf_int32(hctransaction_t trans,int16_t leafid,int32_t value)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
hcc_leaf_int64(hctransaction_t trans,int16_t leafid,int64_t value)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
hcc_check_space(hctransaction_t trans,struct HCHead * head,int n,int size)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
hcc_alloc_descriptor(struct HostConf * hc,void * ptr,int type)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 *
hcc_get_descriptor(struct HostConf * hc,intptr_t desc,int type)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
hcc_set_descriptor(struct HostConf * hc,intptr_t desc,void * ptr,int type)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 *
hcc_nextitem(hctransaction_t trans,struct HCHead * head,struct HCLeaf * item)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 *
hcc_nextchaineditem(struct HostConf * hc,struct HCHead * head)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 *
hcc_currentchaineditem(struct HostConf * hc,struct HCHead * head)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
hcc_debug_dump(hctransaction_t trans,struct HCHead * head)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