Merge branch 'vendor/AWK'
[dragonfly.git] / bin / cpdup / hclink.c
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     hctransaction_t fill;
155     struct HCHead tmp;
156     int aligned_bytes;
157     int need_swap;
158     int n;
159     int r;
160
161     n = 0;
162     while (n < (int)sizeof(struct HCHead)) {
163         r = read(hc->fdin, (char *)&tmp + n, sizeof(struct HCHead) - n);
164         if (r <= 0)
165             goto fail;
166         n += r;
167     }
168
169     if (tmp.magic == HCMAGIC)
170         need_swap = 0;
171     else {
172         tmp.magic = hc_bswap32(tmp.magic);
173         if (tmp.magic != HCMAGIC)
174             fatal("magic mismatch with %s (%04x)", hc->host, tmp.id);
175         need_swap = 1;
176         tmp.bytes = hc_bswap32(tmp.bytes);
177         tmp.cmd   = hc_bswap16(tmp.cmd);
178         tmp.id    = hc_bswap16(tmp.id);
179         tmp.error = hc_bswap32(tmp.error);
180     }
181
182     assert(tmp.bytes >= (int)sizeof(tmp) && tmp.bytes < HC_BUFSIZE);
183
184     if (!(fill = trans))
185         fatal("cpdup hlink protocol error with %s (%04x)", hc->host, tmp.id);
186
187     fill->swap = need_swap;
188     bcopy(&tmp, fill->rbuf, n);
189     aligned_bytes = HCC_ALIGN(tmp.bytes);
190
191     while (n < aligned_bytes) {
192         r = read(hc->fdin, fill->rbuf + n, aligned_bytes - n);
193         if (r <= 0)
194             goto fail;
195         n += r;
196     }
197 #ifdef DEBUG
198     hcc_debug_dump(trans, head);
199 #endif
200     fill->state = HCT_REPLIED;
201     return((void *)fill->rbuf);
202 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