1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 23 /* All Rights Reserved */ 24 25 /* 26 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 27 * Use is subject to license terms. 28 */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 #include "uucp.h" 33 34 /* 35 * uucp 36 * user id 37 * make a copy in spool directory 38 */ 39 int Copy = 0; 40 static int _Transfer = 0; 41 char Nuser[32]; 42 char *Ropt = " "; 43 char Optns[10]; 44 char Uopts[BUFSIZ]; 45 char Xopts[BUFSIZ]; 46 char Sgrade[NAMESIZE]; 47 int Mail = 0; 48 int Notify = 0; 49 50 void cleanup(), ruux(), usage(); 51 int eaccess(), guinfo(), vergrd(), gwd(), ckexpf(), uidstat(), uidxcp(), 52 copy(), gtcfile(); 53 void commitall(), wfabort(), mailst(), gename(), svcfile(); 54 55 char Sfile[MAXFULLNAME]; 56 57 main(argc, argv, envp) 58 char *argv[]; 59 char **envp; 60 { 61 char *jid(); 62 int ret; 63 int errors = 0; 64 char *fopt, *sys2p; 65 char sys1[MAXFULLNAME], sys2[MAXFULLNAME]; 66 char fwd1[MAXFULLNAME], fwd2[MAXFULLNAME]; 67 char file1[MAXFULLNAME], file2[MAXFULLNAME]; 68 short jflag = 0; /* -j flag Jobid printout */ 69 extern int split(); 70 71 72 /* Set locale environment variables local definitions */ 73 (void) setlocale(LC_ALL, ""); 74 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 75 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ 76 #endif 77 (void) textdomain(TEXT_DOMAIN); 78 79 /* this fails in some versions, but it doesn't hurt */ 80 Uid = getuid(); 81 Euid = geteuid(); 82 if (Uid == 0) 83 (void) setuid(UUCPUID); 84 85 /* choose LOGFILE */ 86 (void) strcpy(Logfile, LOGUUCP); 87 88 Env = envp; 89 fopt = NULL; 90 (void) strcpy(Progname, "uucp"); 91 Pchar = 'U'; 92 *Uopts = NULLCHAR; 93 *Xopts = NULLCHAR; 94 *Sgrade = NULLCHAR; 95 96 if (eaccess(GRADES, 0) != -1) { 97 Grade = 'A'; 98 Sgrades = TRUE; 99 sprintf(Sgrade, "%s", "default"); 100 } 101 102 /* 103 * find name of local system 104 */ 105 uucpname(Myname); 106 Optns[0] = '-'; 107 Optns[1] = 'd'; 108 Optns[2] = 'c'; 109 Optns[3] = Nuser[0] = Sfile[0] = NULLCHAR; 110 111 /* 112 * find id of user who spawned command to 113 * determine 114 */ 115 (void) guinfo(Uid, User); 116 117 /* 118 * create/append command log 119 */ 120 commandlog(argc,argv); 121 122 while ((ret = getopt(argc, argv, "Ccdfg:jmn:rs:x:")) != EOF) { 123 switch (ret) { 124 125 /* 126 * make a copy of the file in the spool 127 * directory. 128 */ 129 case 'C': 130 Copy = 1; 131 Optns[2] = 'C'; 132 break; 133 134 /* 135 * not used (default) 136 */ 137 case 'c': 138 break; 139 140 /* 141 * not used (default) 142 */ 143 case 'd': 144 break; 145 case 'f': 146 Optns[1] = 'f'; 147 break; 148 149 /* 150 * set service grade 151 */ 152 case 'g': 153 snprintf(Xopts, sizeof (Xopts), "-g%s", optarg); 154 if (!Sgrades) { 155 if (strlen(optarg) < (size_t)2 && isalnum(*optarg)) 156 Grade = *optarg; 157 else { 158 (void) fprintf(stderr, gettext("No" 159 " administrator defined service" 160 " grades available on this" 161 " machine.\n")); 162 (void) fprintf(stderr, gettext("UUCP" 163 " service grades range from" 164 " [A-Z][a-z] only.\n")); 165 cleanup(-1); 166 } 167 } 168 else { 169 (void) strncpy(Sgrade, optarg, NAMESIZE-1); 170 Sgrade[NAMESIZE-1] = NULLCHAR; 171 if (vergrd(Sgrade) != SUCCESS) 172 cleanup(FAIL); 173 } 174 break; 175 176 case 'j': /* job id */ 177 jflag = 1; 178 break; 179 180 /* 181 * send notification to local user 182 */ 183 case 'm': 184 Mail = 1; 185 (void) strcat(Optns, "m"); 186 break; 187 188 /* 189 * send notification to user on remote 190 * if no user specified do not send notification 191 */ 192 case 'n': 193 /* 194 * We should add "n" option to Optns only once, 195 * even if multiple -n option are passed to uucp 196 */ 197 if (!Notify) { 198 (void) strlcat(Optns, "n", sizeof (Optns)); 199 Notify = 1; 200 } 201 (void) sprintf(Nuser, "%.8s", optarg); 202 203 /* 204 * We do the copy multiple times when multiple 205 * -n options are specified, but 206 * only the last -n value is used. 207 */ 208 (void) snprintf(Uopts, sizeof (Uopts), "-n%s ", Nuser); 209 210 break; 211 212 /* 213 * create JCL files but do not start uucico 214 */ 215 case 'r': 216 Ropt = "-r"; 217 break; 218 219 /* 220 * return status file 221 */ 222 case 's': 223 fopt = optarg; 224 /* "m" needed for compatability */ 225 (void) strcat(Optns, "mo"); 226 break; 227 228 /* 229 * turn on debugging 230 */ 231 case 'x': 232 Debug = atoi(optarg); 233 if (Debug <= 0) 234 Debug = 1; 235 #ifdef SMALL 236 fprintf(stderr, gettext("WARNING: uucp built with SMALL" 237 " flag defined -- no debug info available\n")); 238 #endif /* SMALL */ 239 break; 240 241 default: 242 usage(); 243 break; 244 } 245 } 246 DEBUG(4, "\n\n** %s **\n", "START"); 247 gwd(Wrkdir); 248 if (fopt) { 249 if (*fopt != '/') 250 (void) snprintf(Sfile, MAXFULLNAME, "%s/%s", 251 Wrkdir, fopt); 252 else 253 (void) snprintf(Sfile, MAXFULLNAME, "%s", fopt); 254 255 } 256 else 257 if (strlcpy(Sfile, "dummy", sizeof (Sfile)) >= sizeof (Sfile)) 258 return (2); 259 260 /* 261 * work in WORKSPACE directory 262 */ 263 ret = chdir(WORKSPACE); 264 if (ret != 0) { 265 (void) fprintf(stderr, gettext("No work directory - %s -" 266 " get help\n"), WORKSPACE); 267 cleanup(-12); 268 } 269 270 if (Nuser[0] == NULLCHAR) 271 (void) strcpy(Nuser, User); 272 (void) strcpy(Loginuser, User); 273 DEBUG(4, "UID %ld, ", (long) Uid); 274 DEBUG(4, "User %s\n", User); 275 if (argc - optind < 2) { 276 usage(); 277 } 278 279 /* 280 * set up "to" system and file names 281 */ 282 283 (void) split(argv[argc - 1], sys2, fwd2, file2); 284 if (*sys2 != NULLCHAR) { 285 (void) strncpy(Rmtname, sys2, MAXBASENAME); 286 Rmtname[MAXBASENAME] = NULLCHAR; 287 288 /* get real Myname - it depends on who I'm calling--Rmtname */ 289 (void) mchFind(Rmtname); 290 myName(Myname); 291 292 if (versys(sys2) != 0) { 293 (void) fprintf(stderr, 294 gettext("bad system: %s\n"), sys2); 295 cleanup(-EX_NOHOST); 296 } 297 } 298 299 DEBUG(9, "sys2: %s, ", sys2); 300 DEBUG(9, "fwd2: %s, ", fwd2); 301 DEBUG(9, "file2: %s\n", file2); 302 303 /* 304 * if there are more than 2 argsc, file2 is a directory 305 */ 306 if (argc - optind > 2) 307 (void) strcat(file2, "/"); 308 309 /* 310 * do each from argument 311 */ 312 313 for ( ; optind < argc - 1; optind++) { 314 (void) split(argv[optind], sys1, fwd1, file1); 315 if (*sys1 != NULLCHAR) { 316 if (versys(sys1) != 0) { 317 (void) fprintf(stderr, 318 gettext("bad system: %s\n"), sys1); 319 cleanup(-EX_NOHOST); 320 } 321 } 322 323 /* source files can have at most one ! */ 324 if (*fwd1 != NULLCHAR) { 325 /* syntax error */ 326 (void) fprintf(stderr, 327 gettext("illegal syntax %s\n"), argv[optind]); 328 exit(2); 329 } 330 331 /* 332 * check for required remote expansion of file names -- generate 333 * and execute a uux command 334 * e.g. 335 * uucp owl!~/dan/.. ~/dan/ 336 * 337 * NOTE: The source file part must be full path name. 338 * If ~ it will be expanded locally - it assumes the remote 339 * names are the same. 340 */ 341 342 if (*sys1 != NULLCHAR) 343 if ((strchr(file1, '*') != NULL 344 || strchr(file1, '?') != NULL 345 || strchr(file1, '[') != NULL)) { 346 /* do a uux command */ 347 if (ckexpf(file1) == FAIL) 348 exit(6); 349 (void) strncpy(Rmtname, sys1, MAXBASENAME); 350 Rmtname[MAXBASENAME] = NULLCHAR; 351 /* get real Myname - it depends on who I'm calling--Rmtname */ 352 (void) mchFind(Rmtname); 353 myName(Myname); 354 if (*sys2 == NULLCHAR) 355 sys2p = Myname; 356 ruux(sys1, sys1, file1, sys2p, fwd2, file2); 357 continue; 358 } 359 360 /* 361 * check for forwarding -- generate and execute a uux command 362 * e.g. 363 * uucp uucp.c raven!owl!~/dan/ 364 */ 365 366 if (*fwd2 != NULLCHAR) { 367 ruux(sys2, sys1, file1, "", fwd2, file2); 368 continue; 369 } 370 371 /* 372 * check for both source and destination on other systems -- 373 * generate and execute a uux command 374 */ 375 376 if (*sys1 != NULLCHAR ) 377 if ( (!EQUALS(Myname, sys1)) 378 && *sys2 != NULLCHAR 379 && (!EQUALS(sys2, Myname)) ) { 380 ruux(sys2, sys1, file1, "", fwd2, file2); 381 continue; 382 } 383 384 385 sys2p = sys2; 386 if (*sys1 == NULLCHAR) { 387 if (*sys2 == NULLCHAR) 388 sys2p = Myname; 389 (void) strcpy(sys1, Myname); 390 } else { 391 (void) strncpy(Rmtname, sys1, MAXBASENAME); 392 Rmtname[MAXBASENAME] = NULLCHAR; 393 /* get real Myname - it depends on who I'm calling--Rmtname */ 394 (void) mchFind(Rmtname); 395 myName(Myname); 396 if (*sys2 == NULLCHAR) 397 sys2p = Myname; 398 } 399 400 DEBUG(4, "sys1 - %s, ", sys1); 401 DEBUG(4, "file1 - %s, ", file1); 402 DEBUG(4, "Rmtname - %s\n", Rmtname); 403 if (copy(sys1, file1, sys2p, file2)) 404 errors++; 405 } 406 407 /* move the work files to their proper places */ 408 commitall(); 409 410 /* 411 * Wait for all background uux processes to finish so 412 * that our caller will know that we're done with all 413 * input files and it's safe to remove them. 414 */ 415 while (wait(NULL) != -1) 416 ; 417 418 /* 419 * do not spawn daemon if -r option specified 420 */ 421 if (*Ropt != '-') { 422 #ifndef V7 423 long limit; 424 char msg[100]; 425 limit = ulimit(1, (long) 0); 426 if (limit < MINULIMIT) { 427 (void) sprintf(msg, 428 "ULIMIT (%ld) < MINULIMIT (%ld)", limit, MINULIMIT); 429 logent(msg, "Low-ULIMIT"); 430 } 431 else 432 #endif 433 xuucico(Rmtname); 434 } 435 if (jflag) { 436 (void) strncpy(Jobid, jid(), NAMESIZE); 437 printf("%s\n", Jobid); 438 } 439 cleanup(errors); 440 /*NOTREACHED*/ 441 } 442 443 /* 444 * cleanup lock files before exiting 445 */ 446 void 447 cleanup(code) 448 register int code; 449 { 450 static int first = 1; 451 452 if (first) { 453 first = 0; 454 rmlock(CNULL); 455 if (code != 0) 456 wfabort(); /* this may be extreme -- abort all work */ 457 } 458 if (code < 0) { 459 (void) fprintf(stderr, 460 gettext("uucp failed completely (%d)\n"), (-code)); 461 exit(-code); 462 } 463 else if (code > 0) { 464 (void) fprintf(stderr, gettext( 465 "uucp failed partially: %d file(s) sent; %d error(s)\n"), 466 _Transfer, code); 467 exit(code); 468 } 469 exit(code); 470 } 471 472 /* 473 * generate copy files for s1!f1 -> s2!f2 474 * Note: only one remote machine, other situations 475 * have been taken care of in main. 476 * return: 477 * 0 -> success 478 * Non-zero -> failure 479 */ 480 int 481 copy(s1, f1, s2, f2) 482 char *s1, *f1, *s2, *f2; 483 { 484 FILE *cfp; 485 static FILE *syscfile(); 486 struct stat stbuf, stbuf1; 487 int type, statret; 488 char dfile[NAMESIZE]; 489 char cfile[NAMESIZE]; 490 char command[10+(2*MAXFULLNAME)]; 491 char file1[MAXFULLNAME], file2[MAXFULLNAME]; 492 char msg[BUFSIZ]; 493 494 type = 0; 495 (void) strcpy(file1, f1); 496 (void) strcpy(file2, f2); 497 if (!EQUALS(s1, Myname)) 498 type = 1; 499 if (!EQUALS(s2, Myname)) 500 type = 2; 501 502 DEBUG(4, "copy: file1=<%s> ", file1); 503 DEBUG(4, "file2=<%s>\n", file2); 504 switch (type) { 505 case 0: 506 507 /* 508 * all work here 509 */ 510 DEBUG(4, "all work here %d\n", type); 511 512 /* 513 * check access control permissions 514 */ 515 if (ckexpf(file1)) 516 return(-6); 517 if (ckexpf(file2)) 518 return(-7); 519 520 setuid(Uid); 521 if (chkperm(file1, file2, strchr(Optns, 'd')) && 522 (access(file2, W_OK) == -1)) { 523 (void) fprintf(stderr, gettext("permission denied\n")); 524 cleanup(1); 525 } 526 527 /* 528 * copy file locally 529 * 530 * Changed from uidxcp() to fic file made and owner 531 * being modified for existing files, and local file 532 * name expansion. 533 */ 534 DEBUG(2, "local copy: %s -> ", file1); 535 DEBUG(2, "%s\n", file2); 536 537 sprintf(command, "cp %s %s", file1, file2); 538 if ((cfp = popen(command, "r")) == NULL) { 539 perror("popen"); 540 DEBUG(5, "popen failed - errno %d\n", errno); 541 setuid(Euid); 542 return (FAIL); 543 } 544 if (pclose(cfp) != 0) { 545 DEBUG(5, "Copy failed - errno %d\n", errno); 546 return (FAIL); 547 } 548 setuid(Euid); 549 550 /* 551 * if user specified -m, notify "local" user 552 */ 553 if ( Mail ) { 554 sprintf(msg, 555 "REQUEST: %s!%s --> %s!%s (%s)\n(SYSTEM %s) copy succeeded\n", 556 s1, file1, s2, file2, User, s2 ); 557 mailst(User, "copy succeeded", msg, "", ""); 558 } 559 /* 560 * if user specified -n, notify "remote" user 561 */ 562 if ( Notify ) { 563 sprintf(msg, "%s from %s!%s arrived\n", 564 file2, s1, User ); 565 mailst(Nuser, msg, msg, "", ""); 566 } 567 return(0); 568 case 1: 569 570 /* 571 * receive file 572 */ 573 DEBUG(4, "receive file - %d\n", type); 574 575 /* 576 * expand source and destination file names 577 * and check access permissions 578 */ 579 if (file1[0] != '~') 580 if (ckexpf(file1)) 581 return(6); 582 if (ckexpf(file2)) 583 return(7); 584 585 586 gename(DATAPRE, s2, Grade, dfile); 587 588 /* 589 * insert JCL card in file 590 */ 591 cfp = syscfile(cfile, s1); 592 (void) fprintf(cfp, 593 "R %s %s %s %s %s %o %s %s\n", file1, file2, 594 User, Optns, 595 *Sfile ? Sfile : "dummy", 596 0777, Nuser, dfile); 597 (void) fclose(cfp); 598 (void) sprintf(msg, "%s!%s --> %s!%s", Rmtname, file1, 599 Myname, file2); 600 logent(msg, "QUEUED"); 601 break; 602 case 2: 603 604 /* 605 * send file 606 */ 607 if (ckexpf(file1)) 608 return(6); 609 /* XQTDIR hook enables 3rd party uux requests (cough) */ 610 DEBUG(4, "Workdir = <%s>\n", Wrkdir); 611 if (file2[0] != '~' && !EQUALS(Wrkdir, XQTDIR)) 612 if (ckexpf(file2)) 613 return(7); 614 DEBUG(4, "send file - %d\n", type); 615 616 if (uidstat(file1, &stbuf) != 0) { 617 (void) fprintf(stderr, 618 gettext("can't get status for file %s\n"), file1); 619 return(8); 620 } 621 if ((stbuf.st_mode & S_IFMT) == S_IFDIR) { 622 (void) fprintf(stderr, 623 gettext("directory name illegal - %s\n"), file1); 624 return(9); 625 } 626 /* see if I can read this file as read uid, gid */ 627 if (access(file1, R_OK) != 0) { 628 (void) fprintf(stderr, 629 gettext("uucp can't read (%s) mode (%o)\n"), 630 file1, stbuf.st_mode&0777); 631 return(3); 632 } 633 634 /* 635 * make a copy of file in spool directory 636 */ 637 638 gename(DATAPRE, s2, Grade, dfile); 639 640 if (Copy || !READANY(file1) ) { 641 642 if (uidxcp(file1, dfile)) 643 return(5); 644 645 (void) chmod(dfile, DFILEMODE); 646 } 647 648 cfp = syscfile(cfile, s2); 649 (void) fprintf(cfp, "S %s %s %s %s %s %lo %s %s\n", 650 file1, file2, User, Optns, dfile, 651 (long) stbuf.st_mode & LEGALMODE, Nuser, Sfile); 652 (void) fclose(cfp); 653 (void) sprintf(msg, "%s!%s --> %s!%s", Myname, file1, 654 Rmtname, file2); 655 logent(msg, "QUEUED"); 656 break; 657 } 658 _Transfer++; 659 return(0); 660 } 661 662 663 /* 664 * syscfile(file, sys) 665 * char *file, *sys; 666 * 667 * get the cfile for system sys (creat if need be) 668 * return stream pointer 669 * 670 * returns 671 * stream pointer to open cfile 672 * 673 */ 674 675 static FILE * 676 syscfile(file, sys) 677 char *file, *sys; 678 { 679 FILE *cfp; 680 681 if (gtcfile(file, sys) == FAIL) { 682 gename(CMDPRE, sys, Grade, file); 683 ASSERT(access(file, 0) != 0, Fl_EXISTS, file, errno); 684 cfp = fdopen(creat(file, CFILEMODE), "w"); 685 svcfile(file, sys, Sgrade); 686 } else 687 cfp = fopen(file, "a"); 688 ASSERT(cfp != NULL, Ct_OPEN, file, errno); 689 return(cfp); 690 } 691 692 693 /* 694 * generate and execute a uux command 695 */ 696 697 void 698 ruux(rmt, sys1, file1, sys2, fwd2, file2) 699 char *rmt, *sys1, *file1, *sys2, *fwd2, *file2; 700 { 701 char cmd[BUFSIZ]; 702 char xcmd[BUFSIZ]; 703 char * xarg[6]; 704 int narg = 0; 705 int i; 706 707 /* get real Myname - it depends on who I'm calling--rmt */ 708 (void) mchFind(rmt); 709 myName(Myname); 710 711 xarg[narg++] = UUX; 712 xarg[narg++] = "-C"; 713 if (*Xopts != NULLCHAR) 714 xarg[narg++] = Xopts; 715 if (*Ropt != ' ') 716 xarg[narg++] = Ropt; 717 718 (void) sprintf(cmd, "%s!uucp -C", rmt); 719 720 if (*Uopts != NULLCHAR) 721 (void) sprintf(cmd+strlen(cmd), " (%s) ", Uopts); 722 723 if (*sys1 == NULLCHAR || EQUALS(sys1, Myname)) { 724 if (ckexpf(file1)) 725 exit(6); 726 (void) sprintf(cmd+strlen(cmd), " %s!%s ", sys1, file1); 727 } 728 else 729 if (!EQUALS(rmt, sys1)) 730 (void) sprintf(cmd+strlen(cmd), " (%s!%s) ", sys1, file1); 731 else 732 (void) sprintf(cmd+strlen(cmd), " (%s) ", file1); 733 734 if (*fwd2 != NULLCHAR) { 735 if (*sys2 != NULLCHAR) 736 (void) sprintf(cmd+strlen(cmd), 737 " (%s!%s!%s) ", sys2, fwd2, file2); 738 else 739 (void) sprintf(cmd+strlen(cmd), " (%s!%s) ", fwd2, file2); 740 } 741 else { 742 if (*sys2 == NULLCHAR || EQUALS(sys2, Myname)) 743 if (ckexpf(file2)) 744 exit(7); 745 (void) sprintf(cmd+strlen(cmd), " (%s!%s) ", sys2, file2); 746 } 747 748 xarg[narg++] = cmd; 749 xarg[narg] = (char *) 0; 750 751 xcmd[0] = NULLCHAR; 752 for (i=0; i < narg; i++) { 753 strcat(xcmd, xarg[i]); 754 strcat(xcmd, " "); 755 } 756 DEBUG(2, "cmd: %s\n", xcmd); 757 logent(xcmd, "QUEUED"); 758 759 if (fork() == 0) { 760 ASSERT(setuid(getuid()) == 0, "setuid", "failed", 99); 761 execv(UUX, xarg); 762 exit(0); 763 } 764 return; 765 } 766 767 void 768 usage() 769 { 770 771 (void) fprintf(stderr, gettext( 772 "Usage: %s [-c|-C] [-d|-f] [-g GRADE] [-jm] [-n USER]\\\n" 773 "[-r] [-s FILE] [-x DEBUG_LEVEL] source-files destination-file\n"), 774 Progname); 775 cleanup(-2); 776 } 777