1 #include <u.h> 2 #include <libc.h> 3 #include <ctype.h> 4 5 int 6 isatty(int fd) 7 { 8 char buf[64]; 9 10 buf[0] = '\0'; 11 fd2path(fd, buf, sizeof buf); 12 if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0) 13 return 1; 14 return 0; 15 } 16 17 #define OK 0x00 18 #define ERROR 0x01 19 #define FATAL 0x02 20 21 char *progname; 22 23 int dflag; 24 int fflag; 25 int iflag; 26 int pflag; 27 int rflag; 28 int tflag; 29 int vflag; 30 31 int remote; 32 33 char *exitflag = nil; 34 35 void scperror(int, char*, ...); 36 void mustbedir(char*); 37 void receive(char*); 38 char *fileaftercolon(char*); 39 void destislocal(char *cmd, int argc, char *argv[], char *dest); 40 void destisremote(char *cmd, int argc, char *argv[], char *host, char *dest); 41 int remotessh(char *host, char *cmd); 42 void send(char*); 43 void senddir(char*, int, Dir*); 44 int getresponse(void); 45 46 char theuser[32]; 47 48 char ssh[] = "/bin/ssh"; 49 50 int remotefd0; 51 int remotefd1; 52 53 int 54 runcommand(char *cmd) 55 { 56 Waitmsg *w; 57 int pid; 58 char *argv[4]; 59 60 if (cmd == nil) 61 return -1; 62 switch(pid = fork()){ 63 case -1: 64 return -1; 65 case 0: 66 argv[0] = "rc"; 67 argv[1] = "-c"; 68 argv[2] = cmd; 69 argv[3] = nil; 70 exec("/bin/rc", argv); 71 exits("exec failed"); 72 } 73 for(;;){ 74 w = wait(); 75 if(w == nil) 76 return -1; 77 if(w->pid == pid) 78 break; 79 free(w); 80 } 81 if(w->msg[0]){ 82 free(w); 83 return -1; 84 } 85 free(w); 86 return 1; 87 } 88 89 void 90 vprint(char *fmt, ...) 91 { 92 char buf[1024]; 93 va_list arg; 94 static char *name; 95 96 if(vflag == 0) 97 return; 98 99 va_start(arg, fmt); 100 vseprint(buf, buf+sizeof(buf), fmt, arg); 101 va_end(arg); 102 103 if(name == nil){ 104 name = sysname(); 105 if(name == nil) 106 name = "<unknown>"; 107 } 108 fprint(2, "%s: %s\n", name, buf); 109 } 110 111 void 112 usage(void) 113 { 114 fprint(2, "Usage: scp [-Iidfprtv] source ... destination\n"); 115 exits("usage"); 116 } 117 118 119 #pragma varargck type "F" int 120 #pragma varargck type "V" char* 121 static int flag; 122 123 /* flag: if integer flag, take following char *value */ 124 int 125 flagfmt(Fmt *f) 126 { 127 flag = va_arg(f->args, int); 128 return 0; 129 } 130 131 /* flag: if previous integer flag, take char *value */ 132 int 133 valfmt(Fmt *f) 134 { 135 char *value; 136 137 value = va_arg(f->args, char*); 138 if(flag) 139 return fmtprint(f, " %s", value); 140 return 0; 141 } 142 143 void 144 sendokresponse(void) 145 { 146 char ok = OK; 147 148 write(remotefd1, &ok, 1); 149 } 150 151 void 152 main(int argc, char *argv[]) 153 { 154 int i, fd; 155 char cmd[32]; 156 char *p; 157 158 progname = argv[0]; 159 fmtinstall('F', flagfmt); 160 fmtinstall('V', valfmt); 161 iflag = -1; 162 163 ARGBEGIN { 164 case 'I': 165 iflag = 0; 166 break; 167 case 'i': 168 iflag = 1; 169 break; 170 case 'd': 171 dflag++; 172 break; 173 case 'f': 174 fflag++; 175 remote++; 176 break; 177 case 'p': 178 pflag++; 179 break; 180 case 'r': 181 rflag++; 182 break; 183 case 't': 184 tflag++; 185 remote++; 186 break; 187 case 'v': 188 vflag++; 189 break; 190 default: 191 scperror(1, "unknown option %c", ARGC()); 192 } ARGEND 193 194 if(iflag == -1) 195 iflag = isatty(0); 196 197 remotefd0 = 0; 198 remotefd1 = 1; 199 200 if(fflag){ 201 getresponse(); 202 for(i=0; i<argc; i++) 203 send(argv[i]); 204 exits(0); 205 } 206 if(tflag){ 207 if(argc != 1) 208 usage(); 209 receive(argv[0]); 210 exits(0); 211 } 212 213 if (argc < 2) 214 usage(); 215 if (argc > 2) 216 dflag = 1; 217 218 i = 0; 219 fd = open("/dev/user", OREAD); 220 if(fd >= 0){ 221 i = read(fd, theuser, sizeof theuser - 1); 222 close(fd); 223 } 224 if(i <= 0) 225 scperror(1, "can't read /dev/user: %r"); 226 227 remotefd0 = -1; 228 remotefd1 = -1; 229 230 snprint(cmd, sizeof cmd, "scp%F%V%F%V%F%V%F%V", 231 dflag, "-d", 232 pflag, "-p", 233 rflag, "-r", 234 vflag, "-v"); 235 236 p = fileaftercolon(argv[argc-1]); 237 if(p != nil) /* send to remote machine. */ 238 destisremote(cmd, argc-1, argv, argv[argc-1], p); 239 else{ 240 if(dflag) 241 mustbedir(argv[argc-1]); 242 destislocal(cmd, argc-1, argv, argv[argc-1]); 243 } 244 245 exits(exitflag); 246 } 247 248 void 249 destislocal(char *cmd, int argc, char *argv[], char *dst) 250 { 251 int i; 252 char *src; 253 char buf[4096]; 254 255 for(i = 0; i<argc; i++){ 256 src = fileaftercolon(argv[i]); 257 if(src == nil){ 258 /* local file; no network */ 259 snprint(buf, sizeof buf, "exec cp%F%V%F%V %s %s", 260 rflag, "-r", 261 pflag, "-p", 262 argv[i], dst); 263 vprint("remotetolocal: %s", buf); 264 if(runcommand(buf) < 0) 265 exitflag = "local cp exec"; 266 }else{ 267 /* remote file; use network */ 268 snprint(buf, sizeof buf, "%s -f %s", cmd, src); 269 if(remotessh(argv[i], buf) < 0) 270 exitflag = "remote ssh exec"; 271 else{ 272 receive(dst); 273 close(remotefd0); 274 remotefd0 = -1; 275 remotefd1 = -1; 276 } 277 } 278 } 279 } 280 281 void 282 destisremote(char *cmd, int argc, char *argv[], char *host, char *dest) 283 { 284 int i; 285 char *src; 286 char buf[4096]; 287 288 for(i = 0; i < argc; i++){ 289 vprint("remote destination: send %s to %s:%s", argv[i], host, dest); 290 /* destination is remote, but source may be local */ 291 src = fileaftercolon(argv[i]); 292 if(src != nil){ 293 /* remote to remote */ 294 snprint(buf, sizeof buf, "exec %s%F%V%F%V %s %s %s '%s:%s'", 295 ssh, 296 iflag, " -i", 297 vflag, "-v", 298 argv[i], cmd, src, 299 host, dest); 300 vprint("localtoremote: %s", buf); 301 runcommand(buf); 302 }else{ 303 /* local to remote */ 304 if(remotefd0 == -1){ 305 snprint(buf, sizeof buf, "%s -t %s", cmd, dest); 306 if(remotessh(host, buf) < 0) 307 exits("remotessh"); 308 if(getresponse() < 0) 309 exits("bad response"); 310 } 311 send(argv[i]); 312 } 313 } 314 } 315 316 void 317 readhdr(char *p, int n) 318 { 319 int i; 320 321 for(i=0; i<n; i++){ 322 if(read(remotefd0, &p[i], 1) != 1) 323 break; 324 if(p[i] == '\n'){ 325 p[i] = '\0'; 326 return; 327 } 328 } 329 /* if at beginning, this is regular EOF */ 330 if(i == 0) 331 exits(nil); 332 scperror(1, "read error on receive header: %r"); 333 } 334 335 Dir * 336 receivedir(char *dir, int exists, Dir *d, int settimes, ulong atime, ulong mtime, ulong mode) 337 { 338 Dir nd; 339 int setmodes; 340 int fd; 341 342 setmodes = pflag; 343 if(exists){ 344 if(!(d->qid.type & QTDIR)) { 345 scperror(0, "%s: protocol botch: directory requrest for non-directory", dir); 346 return d; 347 } 348 }else{ 349 /* create it writeable; will fix later */ 350 setmodes = 1; 351 fd = create(dir, OREAD, DMDIR|mode|0700); 352 if (fd < 0){ 353 scperror(0, "%s: can't create: %r", dir); 354 return d; 355 } 356 d = dirfstat(fd); 357 close(fd); 358 if(d == nil){ 359 scperror(0, "%s: can't stat: %r", dir); 360 return d; 361 } 362 } 363 receive(dir); 364 if(settimes || setmodes){ 365 nulldir(&nd); 366 if(settimes){ 367 nd.atime = atime; 368 nd.mtime = mtime; 369 d->atime = nd.atime; 370 d->mtime = nd.mtime; 371 } 372 if(setmodes){ 373 nd.mode = DMDIR | (mode & 0777); 374 d->mode = nd.mode; 375 } 376 if(dirwstat(dir, &nd) < 0){ 377 scperror(0, "can't wstat %s: %r", dir); 378 free(d); 379 return nil; 380 } 381 } 382 return d; 383 } 384 385 void 386 receive(char *dest) 387 { 388 int isdir, settimes, mode; 389 int exists, n, i, fd, m; 390 int errors; 391 ulong atime, mtime, size; 392 char buf[8192], *p; 393 char name[1024]; 394 Dir *d; 395 Dir nd; 396 397 mtime = 0L; 398 atime = 0L; 399 settimes = 0; 400 isdir = 0; 401 if ((d = dirstat(dest)) && (d->qid.type & QTDIR)) { 402 isdir = 1; 403 } 404 if(dflag && !isdir) 405 scperror(1, "%s: not a directory: %r", dest); 406 407 sendokresponse(); 408 409 for (;;) { 410 readhdr(buf, sizeof buf); 411 412 switch(buf[0]){ 413 case ERROR: 414 case FATAL: 415 if(!remote) 416 fprint(2, "%s\n", buf+1); 417 exitflag = "bad receive"; 418 if(buf[0] == FATAL) 419 exits(exitflag); 420 continue; 421 422 case 'E': 423 sendokresponse(); 424 return; 425 426 case 'T': 427 settimes = 1; 428 p = buf + 1; 429 mtime = strtol(p, &p, 10); 430 if(*p++ != ' '){ 431 Badtime: 432 scperror(1, "bad time format: %s", buf+1); 433 } 434 strtol(p, &p, 10); 435 if(*p++ != ' ') 436 goto Badtime; 437 atime = strtol(p, &p, 10); 438 if(*p++ != ' ') 439 goto Badtime; 440 strtol(p, &p, 10); 441 if(*p++ != 0) 442 goto Badtime; 443 444 sendokresponse(); 445 continue; 446 447 case 'D': 448 case 'C': 449 p = buf + 1; 450 mode = strtol(p, &p, 8); 451 if (*p++ != ' '){ 452 Badmode: 453 scperror(1, "bad mode/size format: %s", buf+1); 454 } 455 size = strtoll(p, &p, 10); 456 if(*p++ != ' ') 457 goto Badmode; 458 459 if(isdir){ 460 if(dest[0] == '\0') 461 snprint(name, sizeof name, "%s", p); 462 else 463 snprint(name, sizeof name, "%s/%s", dest, p); 464 }else 465 snprint(name, sizeof name, "%s", dest); 466 if(strlen(name) > sizeof name-UTFmax) 467 scperror(1, "file name too long: %s", dest); 468 469 exists = 1; 470 free(d); 471 if((d = dirstat(name)) == nil) 472 exists = 0; 473 474 if(buf[0] == 'D'){ 475 vprint("receive directory %s", name); 476 d = receivedir(name, exists, d, settimes, atime, mtime, mode); 477 settimes = 0; 478 continue; 479 } 480 481 vprint("receive file %s by %s", name, getuser()); 482 fd = create(name, OWRITE, mode); 483 if(fd < 0){ 484 scperror(0, "can't create %s: %r", name); 485 continue; 486 } 487 sendokresponse(); 488 489 /* 490 * Committed to receive size bytes 491 */ 492 errors = 0; 493 for(i = 0; i < size; i += m){ 494 n = sizeof buf; 495 if(n > size - i) 496 n = size - i; 497 m = readn(remotefd0, buf, n); 498 if(m <= 0) 499 scperror(1, "read error on connection: %r"); 500 if(errors == 0){ 501 n = write(fd, buf, m); 502 if(n != m) 503 errors = 1; 504 } 505 } 506 507 /* if file exists, modes could be wrong */ 508 if(errors) 509 scperror(0, "%s: write error: %r", name); 510 else if(settimes || (exists && (d->mode&0777) != (mode&0777))){ 511 nulldir(&nd); 512 if(settimes){ 513 settimes = 0; 514 nd.atime = atime; 515 nd.mtime = mtime; 516 } 517 if(exists && (d->mode&0777) != (mode&0777)) 518 nd.mode = (d->mode & ~0777) | (mode&0777); 519 if(dirwstat(name, &nd) < 0) 520 scperror(0, "can't wstat %s: %r", name); 521 } 522 free(d); 523 d = nil; 524 close(fd); 525 getresponse(); 526 if(errors) 527 exits("write error"); 528 sendokresponse(); 529 break; 530 531 default: 532 scperror(0, "unrecognized header type char %c", buf[0]); 533 scperror(1, "input line: %s", buf); 534 } 535 } 536 } 537 538 /* 539 * Lastelem is called when we have a Dir with the final element, but if the file 540 * has been bound, we want the original name that was used rather than 541 * the contents of the stat buffer, so do this lexically. 542 */ 543 char* 544 lastelem(char *file) 545 { 546 char *elem; 547 548 elem = strrchr(file, '/'); 549 if(elem == nil) 550 return file; 551 return elem+1; 552 } 553 554 void 555 send(char *file) 556 { 557 Dir *d; 558 ulong i; 559 int m, n, fd; 560 char buf[8192]; 561 562 if((fd = open(file, OREAD)) < 0){ 563 scperror(0, "can't open %s: %r", file); 564 return; 565 } 566 if((d = dirfstat(fd)) == nil){ 567 scperror(0, "can't fstat %s: %r", file); 568 goto Return; 569 } 570 571 if(d->qid.type & QTDIR){ 572 if(rflag) 573 senddir(file, fd, d); 574 else 575 scperror(0, "%s: is a directory", file); 576 goto Return; 577 } 578 579 if(pflag){ 580 fprint(remotefd1, "T%lud 0 %lud 0\n", d->mtime, d->atime); 581 if(getresponse() < 0) 582 goto Return; 583 } 584 585 fprint(remotefd1, "C%.4luo %lld %s\n", d->mode&0777, d->length, lastelem(file)); 586 if(getresponse() < 0) 587 goto Return; 588 589 /* 590 * We are now committed to send d.length bytes, regardless 591 */ 592 for(i=0; i<d->length; i+=m){ 593 n = sizeof buf; 594 if(n > d->length - i) 595 n = d->length - i; 596 m = readn(fd, buf, n); 597 if(m <= 0) 598 break; 599 write(remotefd1, buf, m); 600 } 601 602 if(i == d->length) 603 sendokresponse(); 604 else{ 605 /* continue to send gibberish up to d.length */ 606 for(; i<d->length; i+=n){ 607 n = sizeof buf; 608 if(n > d->length - i) 609 n = d->length - i; 610 write(remotefd1, buf, n); 611 } 612 scperror(0, "%s: %r", file); 613 } 614 615 getresponse(); 616 617 Return: 618 free(d); 619 close(fd); 620 } 621 622 int 623 getresponse(void) 624 { 625 uchar first, byte, buf[256]; 626 int i; 627 628 if (read(remotefd0, &first, 1) != 1) 629 scperror(1, "lost connection"); 630 631 if(first == 0) 632 return 0; 633 634 i = 0; 635 if(first > FATAL){ 636 fprint(2, "scp: unexpected response character 0x%.2ux\n", first); 637 buf[i++] = first; 638 } 639 640 /* read error message up to newline */ 641 for(;;){ 642 if(read(remotefd0, &byte, 1) != 1) 643 scperror(1, "response: dropped connection"); 644 if(byte == '\n') 645 break; 646 if(i < sizeof buf) 647 buf[i++] = byte; 648 } 649 650 exitflag = "bad response"; 651 if(!remote) 652 fprint(2, "%.*s\n", utfnlen((char*)buf, i), (char*)buf); 653 654 if (first == ERROR) 655 return -1; 656 exits(exitflag); 657 return 0; /* not reached */ 658 } 659 660 void 661 senddir(char *name, int fd, Dir *dirp) 662 { 663 Dir *d, *dir; 664 int n; 665 char file[256]; 666 667 if(pflag){ 668 fprint(remotefd1, "T%lud 0 %lud 0\n", dirp->mtime, dirp->atime); 669 if(getresponse() < 0) 670 return; 671 } 672 673 vprint("directory %s mode: D%.4lo %d %.1024s", name, dirp->mode&0777, 0, lastelem(name)); 674 675 fprint(remotefd1, "D%.4lo %d %.1024s\n", dirp->mode&0777, 0, dirp->name); 676 if(getresponse() < 0) 677 return; 678 679 n = dirreadall(fd, &dir); 680 for(d = dir; d < &dir[n]; d++){ 681 /* shouldn't happen with plan 9, but worth checking anyway */ 682 if(strcmp(d->name, ".")==0 || strcmp(d->name, "..")==0) 683 continue; 684 if(snprint(file, sizeof file, "%s/%s", name, d->name) > sizeof file-UTFmax){ 685 scperror(0, "%.20s.../%s: name too long; skipping file", file, d->name); 686 continue; 687 } 688 send(file); 689 } 690 free(dir); 691 fprint(remotefd1, "E\n"); 692 getresponse(); 693 } 694 695 int 696 remotessh(char *host, char *cmd) 697 { 698 int i, p[2]; 699 char *arg[32]; 700 701 vprint("remotessh: %s: %s", host, cmd); 702 703 if(pipe(p) < 0) 704 scperror(1, "pipe: %r"); 705 706 switch(fork()){ 707 case -1: 708 scperror(1, "fork: %r"); 709 710 case 0: 711 /* child */ 712 close(p[0]); 713 dup(p[1], 0); 714 dup(p[1], 1); 715 for (i = 3; i < 100; i++) 716 close(i); 717 718 i = 0; 719 arg[i++] = ssh; 720 arg[i++] = "-x"; 721 arg[i++] = "-a"; 722 arg[i++] = "-m"; 723 if(iflag) 724 arg[i++] = "-i"; 725 if(vflag) 726 arg[i++] = "-v"; 727 arg[i++] = host; 728 arg[i++] = cmd; 729 arg[i] = nil; 730 731 exec(ssh, arg); 732 exits("exec failed"); 733 734 default: 735 /* parent */ 736 close(p[1]); 737 remotefd0 = p[0]; 738 remotefd1 = p[0]; 739 } 740 return 0; 741 } 742 743 void 744 scperror(int exit, char *fmt, ...) 745 { 746 char buf[2048]; 747 va_list arg; 748 749 750 va_start(arg, fmt); 751 vseprint(buf, buf+sizeof(buf), fmt, arg); 752 va_end(arg); 753 754 fprint(remotefd1, "%cscp: %s\n", ERROR, buf); 755 756 if (!remote) 757 fprint(2, "scp: %s\n", buf); 758 exitflag = buf; 759 if(exit) 760 exits(exitflag); 761 } 762 763 char * 764 fileaftercolon(char *file) 765 { 766 char *c, *s; 767 768 c = utfrune(file, ':'); 769 if(c == nil) 770 return nil; 771 772 /* colon must be in middle of name to be a separator */ 773 if(c == file) 774 return nil; 775 776 /* does slash occur before colon? */ 777 s = utfrune(file, '/'); 778 if(s != nil && s < c) 779 return nil; 780 781 *c++ = '\0'; 782 if(*c == '\0') 783 return "."; 784 return c; 785 } 786 787 void 788 mustbedir(char *file) 789 { 790 Dir *d; 791 792 if((d = dirstat(file)) == nil){ 793 scperror(1, "%s: %r", file); 794 return; 795 } 796 if(!(d->qid.type & QTDIR)) 797 scperror(1, "%s: Not a directory", file); 798 free(d); 799 } 800