Merge from vendor branch LESS:
[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.8 2008/04/14 05:40:51 dillon Exp $
7  */
8
9 #include "cpdup.h"
10 #include "hclink.h"
11 #include "hcproto.h"
12
13 static struct HCHead *hcc_read_command(hctransaction_t trans, int anypkt);
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[16];
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;
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         av[n++] = "-T";
51         av[n++] = hc->host;
52         av[n++] = "cpdup";
53         av[n++] = "-S";
54         av[n++] = NULL;
55
56         execv("/usr/bin/ssh", (void *)av);
57         _exit(1);
58     } else if (hc->pid < 0) {
59         return(-1);
60     } else {
61         /*
62          * Parent process.  Do the initial handshake to make sure we are
63          * actually talking to a cpdup slave.
64          */
65         close(fdin[1]);
66         hc->fdin = fdin[0];
67         close(fdout[0]);
68         hc->fdout = fdout[1];
69         return(0);
70     }
71 }
72
73 static int
74 rc_badop(hctransaction_t trans __unused, struct HCHead *head)
75 {
76     head->error = EOPNOTSUPP;
77     return(0);
78 }
79
80 int
81 hcc_slave(int fdin, int fdout, struct HCDesc *descs, int count)
82 {
83     struct HostConf hcslave;
84     struct HCHead *head;
85     struct HCHead *whead;
86     struct HCTransaction trans;
87     int (*dispatch[256])(hctransaction_t, struct HCHead *);
88     int aligned_bytes;
89     int i;
90     int r;
91
92     bzero(&hcslave, sizeof(hcslave));
93     bzero(&trans, sizeof(trans));
94     for (i = 0; i < count; ++i) {
95         struct HCDesc *desc = &descs[i];
96         assert(desc->cmd >= 0 && desc->cmd < 256);
97         dispatch[desc->cmd] = desc->func;
98     }
99     for (i = 0; i < 256; ++i) {
100         if (dispatch[i] == NULL)
101             dispatch[i] = rc_badop;
102     }
103     hcslave.fdin = fdin;
104     hcslave.fdout = fdout;
105     trans.hc = &hcslave;
106
107     /*
108      * Process commands on fdin and write out results on fdout
109      */
110     for (;;) {
111         /*
112          * Get the command
113          */
114         head = hcc_read_command(&trans, 1);
115         if (head == NULL)
116             break;
117
118         /*
119          * Start the reply and dispatch, then process the return code.
120          */
121         head->error = 0;
122         hcc_start_reply(&trans, head);
123
124         r = dispatch[head->cmd & 255](&trans, head);
125
126         switch(r) {
127         case -2:
128                 head->error = EINVAL;
129                 break;
130         case -1:
131                 head->error = errno;
132                 break;
133         case 0:
134                 break;
135         default:
136                 assert(0);
137                 break;
138         }
139
140         /*
141          * Write out the reply
142          */
143         whead = (void *)trans.wbuf;
144         whead->bytes = trans.windex;
145         whead->error = head->error;
146         aligned_bytes = HCC_ALIGN(trans.windex);
147 #ifdef DEBUG
148         hcc_debug_dump(whead);
149 #endif
150         if (write(hcslave.fdout, whead, aligned_bytes) != aligned_bytes)
151             break;
152     }
153     return(0);
154 }
155
156 /*
157  * This reads a command from fdin, fixes up the byte ordering, and returns
158  * a pointer to HCHead.
159  *
160  * If id is -1 we do not match response id's
161  */
162 static
163 struct HCHead *
164 hcc_read_command(hctransaction_t trans, int anypkt)
165 {
166     struct HostConf *hc = trans->hc;
167     hctransaction_t fill;
168     struct HCHead tmp;
169     int aligned_bytes;
170     int n;
171     int r;
172
173 #if USE_PTHREADS
174     /*
175      * Shortcut if the reply has already been read in by another thread.
176      */
177     if (trans->state == HCT_REPLIED) {
178         return((void *)trans->rbuf);
179     }
180     pthread_mutex_unlock(&MasterMutex);
181     pthread_mutex_lock(&hc->read_mutex);
182 #endif
183     while (trans->state != HCT_REPLIED) {
184         n = 0;
185         while (n < (int)sizeof(struct HCHead)) {
186             r = read(hc->fdin, (char *)&tmp + n, sizeof(struct HCHead) - n);
187             if (r <= 0)
188                 goto fail;
189             n += r;
190         }
191
192         assert(tmp.bytes >= (int)sizeof(tmp) && tmp.bytes < 65536);
193         assert(tmp.magic == HCMAGIC);
194
195         if (anypkt == 0 && tmp.id != trans->id && trans->hc->version > 0) {
196 #if USE_PTHREADS
197             for (fill = trans->hc->hct_hash[tmp.id & HCTHASH_MASK];
198                  fill;
199                  fill = fill->next)
200             {
201                 if (fill->state == HCT_SENT && fill->id == tmp.id)
202                         break;
203             }
204             if (fill == NULL)
205 #endif
206             {
207                 fprintf(stderr, 
208                         "cpdup hlink protocol error with %s (%04x %04x)\n",
209                         trans->hc->host, trans->id, tmp.id);
210                 exit(1);
211             }
212         } else {
213             fill = trans;
214         }
215
216         bcopy(&tmp, fill->rbuf, n);
217         aligned_bytes = HCC_ALIGN(tmp.bytes);
218
219         while (n < aligned_bytes) {
220             r = read(hc->fdin, fill->rbuf + n, aligned_bytes - n);
221             if (r <= 0)
222                 goto fail;
223             n += r;
224         }
225 #ifdef DEBUG
226         hcc_debug_dump(head);
227 #endif
228         fill->state = HCT_REPLIED;
229     }
230     trans->state = HCT_DONE;
231 #if USE_PTHREADS
232     pthread_mutex_unlock(&hc->read_mutex);
233     pthread_mutex_lock(&MasterMutex);
234 #endif
235     return((void *)trans->rbuf);
236 fail:
237 #if USE_PTHREADS
238     pthread_mutex_unlock(&hc->read_mutex);
239     pthread_mutex_lock(&MasterMutex);
240 #endif
241     return(NULL);
242 }
243
244 #if USE_PTHREADS
245
246 static
247 hctransaction_t
248 hcc_get_trans(struct HostConf *hc)
249 {
250     hctransaction_t trans;
251     hctransaction_t scan;
252     pthread_t tid = pthread_self();
253     int i;
254
255     i = ((intptr_t)tid >> 7) & HCTHASH_MASK;
256
257     for (trans = hc->hct_hash[i]; trans; trans = trans->next) {
258         if (trans->tid == tid)
259                 break;
260     }
261     if (trans == NULL) {
262         trans = malloc(sizeof(*trans));
263         bzero(trans, sizeof(*trans));
264         trans->tid = tid;
265         trans->id = i;
266         do {
267                 for (scan = hc->hct_hash[i]; scan; scan = scan->next) {
268                         if (scan->id == trans->id) {
269                                 trans->id += HCTHASH_SIZE;
270                                 break;
271                         }
272                 }
273         } while (scan != NULL);
274
275         trans->next = hc->hct_hash[i];
276         hc->hct_hash[i] = trans;
277     }
278     return(trans);
279 }
280
281 void
282 hcc_free_trans(struct HostConf *hc)
283 {
284     hctransaction_t trans;
285     hctransaction_t *transp;
286     pthread_t tid = pthread_self();
287     int i;
288
289     i = ((intptr_t)tid >> 7) & HCTHASH_MASK;
290
291     for (transp = &hc->hct_hash[i]; *transp; transp = &trans->next) {
292         trans = *transp;
293         if (trans->tid == tid) {
294                 *transp = trans->next;
295                 free(trans);
296                 break;
297         }
298     }
299 }
300
301 #else
302
303 static
304 hctransaction_t
305 hcc_get_trans(struct HostConf *hc)
306 {
307     return(&hc->trans);
308 }
309
310 static
311 void
312 hcc_free_trans(struct HostConf *hc)
313 {
314     /* nop */
315 }
316
317 #endif
318
319 /*
320  * Initialize for a new command
321  */
322 hctransaction_t
323 hcc_start_command(struct HostConf *hc, int16_t cmd)
324 {
325     struct HCHead *whead;
326     hctransaction_t trans;
327
328     trans = hcc_get_trans(hc);
329
330     whead = (void *)trans->wbuf;
331     whead->magic = HCMAGIC;
332     whead->bytes = 0;
333     whead->cmd = cmd;
334     whead->id = trans->id;
335     whead->error = 0;
336
337     trans->windex = sizeof(*whead);
338     trans->hc = hc;
339     trans->state = HCT_IDLE;
340
341     return(trans);
342 }
343
344 static void
345 hcc_start_reply(hctransaction_t trans, struct HCHead *rhead)
346 {
347     struct HCHead *whead = (void *)trans->wbuf;
348
349     whead->magic = HCMAGIC;
350     whead->bytes = 0;
351     whead->cmd = rhead->cmd | HCF_REPLY;
352     whead->id = rhead->id;
353     whead->error = 0;
354
355     trans->windex = sizeof(*whead);
356 }
357
358 /*
359  * Finish constructing a command, transmit it, and await the reply.
360  * Return the HCHead of the reply.
361  */
362 struct HCHead *
363 hcc_finish_command(hctransaction_t trans)
364 {
365     struct HCHead *whead;
366     struct HCHead *rhead;
367     int aligned_bytes;
368     int16_t wcmd;
369
370     whead = (void *)trans->wbuf;
371     whead->bytes = trans->windex;
372     aligned_bytes = HCC_ALIGN(trans->windex);
373
374     trans->state = HCT_SENT;
375
376     if (write(trans->hc->fdout, whead, aligned_bytes) != aligned_bytes) {
377 #ifdef __error
378         *__error = EIO;
379 #else
380         errno = EIO;
381 #endif
382         if (whead->cmd < 0x0010)
383                 return(NULL);
384         fprintf(stderr, "cpdup lost connection to %s\n", trans->hc->host);
385         exit(1);
386     }
387
388     wcmd = whead->cmd;
389
390     /*
391      * whead is invalid when we call hcc_read_command() because
392      * we may switch to another thread.
393      */
394     if ((rhead = hcc_read_command(trans, 0)) == NULL) {
395 #ifdef __error
396         *__error = EIO;
397 #else
398         errno = EIO;
399 #endif
400         if (wcmd < 0x0010)
401                 return(NULL);
402         fprintf(stderr, "cpdup lost connection to %s\n", trans->hc->host);
403         exit(1);
404     }
405
406     if (rhead->error) {
407 #ifdef __error
408         *__error = rhead->error;
409 #else
410         errno = rhead->error;
411 #endif
412     }
413     return (rhead);
414 }
415
416 void
417 hcc_leaf_string(hctransaction_t trans, int16_t leafid, const char *str)
418 {
419     struct HCLeaf *item;
420     int bytes = strlen(str) + 1;
421
422     item = (void *)(trans->wbuf + trans->windex);
423     assert(trans->windex + sizeof(*item) + bytes < 65536);
424     item->leafid = leafid;
425     item->reserved = 0;
426     item->bytes = sizeof(*item) + bytes;
427     bcopy(str, item + 1, bytes);
428     trans->windex = HCC_ALIGN(trans->windex + item->bytes);
429 }
430
431 void
432 hcc_leaf_data(hctransaction_t trans, int16_t leafid, const void *ptr, int bytes)
433 {
434     struct HCLeaf *item;
435
436     item = (void *)(trans->wbuf + trans->windex);
437     assert(trans->windex + sizeof(*item) + bytes < 65536);
438     item->leafid = leafid;
439     item->reserved = 0;
440     item->bytes = sizeof(*item) + bytes;
441     bcopy(ptr, item + 1, bytes);
442     trans->windex = HCC_ALIGN(trans->windex + item->bytes);
443 }
444
445 void
446 hcc_leaf_int32(hctransaction_t trans, int16_t leafid, int32_t value)
447 {
448     struct HCLeaf *item;
449
450     item = (void *)(trans->wbuf + trans->windex);
451     assert(trans->windex + sizeof(*item) + sizeof(value) < 65536);
452     item->leafid = leafid;
453     item->reserved = 0;
454     item->bytes = sizeof(*item) + sizeof(value);
455     *(int32_t *)(item + 1) = value;
456     trans->windex = HCC_ALIGN(trans->windex + item->bytes);
457 }
458
459 void
460 hcc_leaf_int64(hctransaction_t trans, int16_t leafid, int64_t value)
461 {
462     struct HCLeaf *item;
463
464     item = (void *)(trans->wbuf + trans->windex);
465     assert(trans->windex + sizeof(*item) + sizeof(value) < 65536);
466     item->leafid = leafid;
467     item->reserved = 0;
468     item->bytes = sizeof(*item) + sizeof(value);
469     *(int64_t *)(item + 1) = value;
470     trans->windex = HCC_ALIGN(trans->windex + item->bytes);
471 }
472
473 int
474 hcc_alloc_descriptor(struct HostConf *hc, void *ptr, int type)
475 {
476     struct HCHostDesc *hd;
477     struct HCHostDesc *hnew;
478
479     hnew = malloc(sizeof(struct HCHostDesc));
480     hnew->type = type;
481     hnew->data = ptr;
482
483     if ((hd = hc->hostdescs) != NULL) {
484         hnew->desc = hd->desc + 1;
485     } else {
486         hnew->desc = 1;
487     }
488     hnew->next = hd;
489     hc->hostdescs = hnew;
490     return(hnew->desc);
491 }
492
493 void *
494 hcc_get_descriptor(struct HostConf *hc, int desc, int type)
495 {
496     struct HCHostDesc *hd;
497
498     for (hd = hc->hostdescs; hd; hd = hd->next) {
499         if (hd->desc == desc && hd->type == type)
500             return(hd->data);
501     }
502     return(NULL);
503 }
504
505 void
506 hcc_set_descriptor(struct HostConf *hc, int desc, void *ptr, int type)
507 {
508     struct HCHostDesc *hd;
509     struct HCHostDesc **hdp;
510
511     for (hdp = &hc->hostdescs; (hd = *hdp) != NULL; hdp = &hd->next) {
512         if (hd->desc == desc) {
513             if (ptr) {
514                 hd->data = ptr;
515                 hd->type = type;
516             } else {
517                 *hdp = hd->next;
518                 free(hd);
519             }
520             return;
521         }
522     }
523     if (ptr) {
524         hd = malloc(sizeof(*hd));
525         hd->desc = desc;
526         hd->type = type;
527         hd->data = ptr;
528         hd->next = hc->hostdescs;
529         hc->hostdescs = hd;
530     }
531 }
532
533 struct HCLeaf *
534 hcc_firstitem(struct HCHead *head)
535 {
536     struct HCLeaf *item;
537     int offset;
538
539     offset = sizeof(*head);
540     if (offset == head->bytes)
541         return(NULL);
542     assert(head->bytes >= offset + (int)sizeof(*item));
543     item = (void *)(head + 1);
544     assert(head->bytes >= offset + item->bytes);
545     assert(item->bytes >= (int)sizeof(*item) && item->bytes < 65536 - offset);
546     return (item);
547 }
548
549 struct HCLeaf *
550 hcc_nextitem(struct HCHead *head, struct HCLeaf *item)
551 {
552     int offset;
553
554     item = (void *)((char *)item + HCC_ALIGN(item->bytes));
555     offset = (char *)item - (char *)head;
556     if (offset == head->bytes)
557         return(NULL);
558     assert(head->bytes >= offset + (int)sizeof(*item));
559     assert(head->bytes >= offset + item->bytes);
560     assert(item->bytes >= (int)sizeof(*item) && item->bytes < 65536 - offset);
561     return (item);
562 }
563
564 #ifdef DEBUG
565
566 void
567 hcc_debug_dump(struct HCHead *head)
568 {
569     struct HCLeaf *item;
570     int aligned_bytes = HCC_ALIGN(head->bytes);
571
572     fprintf(stderr, "DUMP %04x (%d)", (u_int16_t)head->cmd, aligned_bytes);
573     if (head->cmd & HCF_REPLY)
574         fprintf(stderr, " error %d", head->error);
575     fprintf(stderr, "\n");
576     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
577         fprintf(stderr, "    ITEM %04x DATA ", item->leafid);
578         switch(item->leafid & LCF_TYPEMASK) {
579         case LCF_INT32:
580             fprintf(stderr, "int32 %d\n", *(int32_t *)(item + 1));
581             break;
582         case LCF_INT64:
583             fprintf(stderr, "int64 %lld\n", *(int64_t *)(item + 1));
584             break;
585         case LCF_STRING:
586             fprintf(stderr, "\"%s\"\n", (char *)(item + 1));
587             break;
588         case LCF_BINARY:
589             fprintf(stderr, "(binary)\n");
590             break;
591         default:
592             printf("?\n");
593         }
594     }
595 }
596
597 #endif