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 560