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 509