Merge branch 'vendor/SENDMAIL'
[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 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