rcp.c revision 178825
150397Sobrien/* 272562Sobrien * Copyright (c) 1983, 1990, 1992, 1993 3117395Skan * The Regents of the University of California. All rights reserved. 450397Sobrien * 550397Sobrien * Redistribution and use in source and binary forms, with or without 650397Sobrien * modification, are permitted provided that the following conditions 750397Sobrien * are met: 850397Sobrien * 1. Redistributions of source code must retain the above copyright 950397Sobrien * notice, this list of conditions and the following disclaimer. 1050397Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1150397Sobrien * notice, this list of conditions and the following disclaimer in the 1250397Sobrien * documentation and/or other materials provided with the distribution. 1350397Sobrien * 3. All advertising materials mentioning features or use of this software 1450397Sobrien * must display the following acknowledgement: 1550397Sobrien * This product includes software developed by the University of 1650397Sobrien * California, Berkeley and its contributors. 1750397Sobrien * 4. Neither the name of the University nor the names of its contributors 1850397Sobrien * may be used to endorse or promote products derived from this software 1950397Sobrien * without specific prior written permission. 2050397Sobrien * 2150397Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2250397Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2350397Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2450397Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2550397Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2650397Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2750397Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2850397Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2950397Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3050397Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3150397Sobrien * SUCH DAMAGE. 3250397Sobrien */ 3350397Sobrien 3450397Sobrien#include "rcp_locl.h" 3550397Sobrien#include <getarg.h> 3650397Sobrien 3790075Sobrien#define RSH_PROGRAM "rsh" 3850397Sobrien 3990075Sobrienstruct passwd *pwd; 4090075Sobrienuid_t userid; 4150397Sobrienint errs, remin, remout; 4250397Sobrienint pflag, iamremote, iamrecursive, targetshouldbedirectory; 4390075Sobrienint doencrypt, noencrypt; 4490075Sobrienint usebroken, usekrb4, usekrb5, forwardtkt; 4590075Sobrienchar *port; 4690075Sobrienint eflag = 0; 4790075Sobrien 4850397Sobrien#define CMDNEEDS 64 4950397Sobrienchar cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 5050397Sobrien 5150397Sobrienint response (void); 5250397Sobrienvoid rsource (char *, struct stat *); 5350397Sobrienvoid sink (int, char *[]); 5450397Sobrienvoid source (int, char *[]); 5550397Sobrienvoid tolocal (int, char *[]); 5650397Sobrienvoid toremote (char *, int, char *[]); 5750397Sobrien 5850397Sobrienint do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); 5950397Sobrien 6050397Sobrienstatic int fflag, tflag; 6150397Sobrien 6250397Sobrienstatic int version_flag, help_flag; 6350397Sobrien 6450397Sobrienstruct getargs args[] = { 6550397Sobrien { NULL, '4', arg_flag, &usekrb4, "use Kerberos 4 authentication" }, 6650397Sobrien { NULL, '5', arg_flag, &usekrb5, "use Kerberos 5 authentication" }, 6750397Sobrien { NULL, 'F', arg_flag, &forwardtkt, "forward credentials" }, 6850397Sobrien { NULL, 'K', arg_flag, &usebroken, "use BSD authentication" }, 6950397Sobrien { NULL, 'P', arg_string, &port, "non-default port", "port" }, 7050397Sobrien { NULL, 'p', arg_flag, &pflag, "preserve file permissions" }, 7196263Sobrien { NULL, 'r', arg_flag, &iamrecursive, "recursive mode" }, 7296263Sobrien { NULL, 'x', arg_flag, &doencrypt, "use encryption" }, 7390075Sobrien { NULL, 'z', arg_flag, &noencrypt, "don't encrypt" }, 7490075Sobrien { NULL, 'd', arg_flag, &targetshouldbedirectory }, 7550397Sobrien { NULL, 'e', arg_flag, &eflag, "passed to rsh" }, 7650397Sobrien { NULL, 'f', arg_flag, &fflag }, 7750397Sobrien { NULL, 't', arg_flag, &tflag }, 7850397Sobrien { "version", 0, arg_flag, &version_flag }, 7996263Sobrien { "help", 0, arg_flag, &help_flag } 8096263Sobrien}; 8196263Sobrien 8250397Sobrienstatic void 8396263Sobrienusage (int ret) 8496263Sobrien{ 8596263Sobrien arg_printusage (args, 8696263Sobrien sizeof(args) / sizeof(args[0]), 8750397Sobrien NULL, 8850397Sobrien "file1 file2|file... directory"); 8950397Sobrien exit (ret); 9050397Sobrien} 9150397Sobrien 9250397Sobrienint 9350397Sobrienmain(int argc, char **argv) 9450397Sobrien{ 9550397Sobrien char *targ; 9650397Sobrien int optind = 0; 9750397Sobrien 9850397Sobrien setprogname(argv[0]); 9950397Sobrien if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, 10050397Sobrien &optind)) 10150397Sobrien usage (1); 10290075Sobrien if(help_flag) 10390075Sobrien usage(0); 10490075Sobrien if (version_flag) { 10590075Sobrien print_version (NULL); 10690075Sobrien return 0; 10790075Sobrien } 10890075Sobrien 10990075Sobrien iamremote = (fflag || tflag); 11090075Sobrien 11190075Sobrien argc -= optind; 11290075Sobrien argv += optind; 11390075Sobrien 11490075Sobrien if ((pwd = getpwuid(userid = getuid())) == NULL) 11590075Sobrien errx(1, "unknown user %d", (int)userid); 11690075Sobrien 11790075Sobrien remin = STDIN_FILENO; /* XXX */ 11890075Sobrien remout = STDOUT_FILENO; 11990075Sobrien 12050397Sobrien if (fflag) { /* Follow "protocol", send data. */ 12150397Sobrien response(); 12250397Sobrien if (setuid(userid) < 0) 12350397Sobrien errx(1, "setuid failed"); 12450397Sobrien source(argc, argv); 12552284Sobrien exit(errs); 12650397Sobrien } 12750397Sobrien 12890075Sobrien if (tflag) { /* Receive data. */ 12990075Sobrien if (setuid(userid) < 0) 13050397Sobrien errx(1, "setuid failed"); 13190075Sobrien sink(argc, argv); 13290075Sobrien exit(errs); 13390075Sobrien } 13450397Sobrien 13550397Sobrien if (argc < 2) 13650397Sobrien usage(1); 13790075Sobrien if (argc > 2) 13890075Sobrien targetshouldbedirectory = 1; 13952284Sobrien 14090075Sobrien remin = remout = -1; 14190075Sobrien /* Command to be executed on remote system using "rsh". */ 14290075Sobrien snprintf(cmd, sizeof(cmd), 14390075Sobrien "rcp%s%s%s", iamrecursive ? " -r" : "", 14490075Sobrien pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); 14590075Sobrien 14690075Sobrien signal(SIGPIPE, lostconn); 14790075Sobrien 14890075Sobrien if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 14990075Sobrien toremote(targ, argc, argv); 15090075Sobrien else { 15190075Sobrien tolocal(argc, argv); /* Dest is local host. */ 15290075Sobrien if (targetshouldbedirectory) 15390075Sobrien verifydir(argv[argc - 1]); 15490075Sobrien } 15590075Sobrien exit(errs); 15690075Sobrien} 15790075Sobrien 15890075Sobrienvoid 159117395Skantoremote(char *targ, int argc, char **argv) 160117395Skan{ 161117395Skan int i; 162117395Skan char *bp, *host, *src, *suser, *thost, *tuser; 163117395Skan 164117395Skan *targ++ = 0; 16552284Sobrien if (*targ == 0) 16690075Sobrien targ = "."; 16790075Sobrien 16890075Sobrien if ((thost = strchr(argv[argc - 1], '@'))) { 169117395Skan /* user@host */ 170117395Skan *thost++ = 0; 17196263Sobrien tuser = argv[argc - 1]; 17296263Sobrien if (*tuser == '\0') 17396263Sobrien tuser = NULL; 17496263Sobrien else if (!okname(tuser)) 17596263Sobrien exit(1); 17696263Sobrien } else { 177117395Skan thost = argv[argc - 1]; 178117395Skan tuser = NULL; 179117395Skan } 180117395Skan 18150397Sobrien for (i = 0; i < argc - 1; i++) { 18250397Sobrien src = colon(argv[i]); 18350397Sobrien if (src) { /* remote to remote */ 18450397Sobrien int ret; 18552284Sobrien *src++ = 0; 18650397Sobrien if (*src == 0) 18750397Sobrien src = "."; 18850397Sobrien host = strchr(argv[i], '@'); 18990075Sobrien if (host) { 19050397Sobrien *host++ = '\0'; 19150397Sobrien suser = argv[i]; 19250397Sobrien if (*suser == '\0') 19350397Sobrien suser = pwd->pw_name; 19450397Sobrien else if (!okname(suser)) 19550397Sobrien continue; 19650397Sobrien ret = asprintf(&bp, 19750397Sobrien "%s%s %s -l %s -n %s %s '%s%s%s:%s'", 19850397Sobrien _PATH_RSH, eflag ? " -e" : "", 19950397Sobrien host, suser, cmd, src, 20050397Sobrien tuser ? tuser : "", tuser ? "@" : "", 20150397Sobrien thost, targ); 20290075Sobrien } else { 20390075Sobrien ret = asprintf(&bp, 20450397Sobrien "exec %s%s %s -n %s %s '%s%s%s:%s'", 20590075Sobrien _PATH_RSH, eflag ? " -e" : "", 20690075Sobrien argv[i], cmd, src, 20790075Sobrien tuser ? tuser : "", tuser ? "@" : "", 20890075Sobrien thost, targ); 20990075Sobrien } 21090075Sobrien if (ret == -1) 21190075Sobrien err (1, "malloc"); 21290075Sobrien susystem(bp, userid); 21390075Sobrien free(bp); 21490075Sobrien } else { /* local to remote */ 21590075Sobrien if (remin == -1) { 21690075Sobrien if (asprintf(&bp, "%s -t %s", cmd, targ) == -1) 21790075Sobrien err (1, "malloc"); 21890075Sobrien host = thost; 21990075Sobrien 22090075Sobrien if (do_cmd(host, tuser, bp, &remin, &remout) < 0) 22190075Sobrien exit(1); 22290075Sobrien 22390075Sobrien if (response() < 0) 22490075Sobrien exit(1); 22590075Sobrien free(bp); 22690075Sobrien if (setuid(userid) < 0) 22790075Sobrien errx(1, "setuid failed"); 22890075Sobrien } 22990075Sobrien source(1, argv+i); 23090075Sobrien } 23190075Sobrien } 23290075Sobrien} 23390075Sobrien 23490075Sobrienvoid 23590075Sobrientolocal(int argc, char **argv) 236117395Skan{ 237117395Skan int i; 238117395Skan char *bp, *host, *src, *suser; 239117395Skan 24090075Sobrien for (i = 0; i < argc - 1; i++) { 241117395Skan int ret; 242117395Skan 243117395Skan if (!(src = colon(argv[i]))) { /* Local to local. */ 244117395Skan ret = asprintf(&bp, "exec %s%s%s %s %s", _PATH_CP, 245117395Skan iamrecursive ? " -PR" : "", pflag ? " -p" : "", 246117395Skan argv[i], argv[argc - 1]); 247117395Skan if (ret == -1) 248117395Skan err (1, "malloc"); 24990075Sobrien if (susystem(bp, userid)) 25090075Sobrien ++errs; 25150397Sobrien free(bp); 25250397Sobrien continue; 25350397Sobrien } 25450397Sobrien *src++ = 0; 25550397Sobrien if (*src == 0) 25650397Sobrien src = "."; 25750397Sobrien if ((host = strchr(argv[i], '@')) == NULL) { 25890075Sobrien host = argv[i]; 25990075Sobrien suser = pwd->pw_name; 26090075Sobrien } else { 26150397Sobrien *host++ = 0; 26250397Sobrien suser = argv[i]; 26350397Sobrien if (*suser == '\0') 26450397Sobrien suser = pwd->pw_name; 26550397Sobrien else if (!okname(suser)) 26650397Sobrien continue; 26750397Sobrien } 26890075Sobrien ret = asprintf(&bp, "%s -f %s", cmd, src); 26950397Sobrien if (ret == -1) 27050397Sobrien err (1, "malloc"); 27190075Sobrien if (do_cmd(host, suser, bp, &remin, &remout) < 0) { 27290075Sobrien free(bp); 27390075Sobrien ++errs; 27450397Sobrien continue; 27550397Sobrien } 27650397Sobrien free(bp); 27750397Sobrien sink(1, argv + argc - 1); 27850397Sobrien if (seteuid(0) < 0) 27952284Sobrien exit(1); 28052284Sobrien close(remin); 28150397Sobrien remin = remout = -1; 28250397Sobrien } 28350397Sobrien} 284117395Skan 28550397Sobrienvoid 28650397Sobriensource(int argc, char **argv) 28790075Sobrien{ 28850397Sobrien struct stat stb; 28950397Sobrien static BUF buffer; 29090075Sobrien BUF *bp; 29190075Sobrien off_t i; 29290075Sobrien int amt, fd, haderr, indx, result; 29390075Sobrien char *last, *name, buf[BUFSIZ]; 29490075Sobrien 29550397Sobrien for (indx = 0; indx < argc; ++indx) { 29650397Sobrien name = argv[indx]; 29750397Sobrien if ((fd = open(name, O_RDONLY, 0)) < 0) 29850397Sobrien goto syserr; 29950397Sobrien if (fstat(fd, &stb)) { 30050397Sobriensyserr: run_err("%s: %s", name, strerror(errno)); 30150397Sobrien goto next; 30250397Sobrien } 30350397Sobrien switch (stb.st_mode & S_IFMT) { 30450397Sobrien case S_IFREG: 30552284Sobrien break; 30690075Sobrien case S_IFDIR: 30790075Sobrien if (iamrecursive) { 30850397Sobrien rsource(name, &stb); 30950397Sobrien goto next; 31050397Sobrien } 31150397Sobrien /* FALLTHROUGH */ 31290075Sobrien default: 31390075Sobrien run_err("%s: not a regular file", name); 31490075Sobrien goto next; 31590075Sobrien } 31690075Sobrien if ((last = strrchr(name, '/')) == NULL) 317117395Skan last = name; 318117395Skan else 319117395Skan ++last; 32050397Sobrien if (pflag) { 32150397Sobrien /* 32290075Sobrien * Make it compatible with possible future 32390075Sobrien * versions expecting microseconds. 32450397Sobrien */ 32552284Sobrien snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n", 32650397Sobrien (long)stb.st_mtime, 32750397Sobrien (long)stb.st_atime); 32850397Sobrien write(remout, buf, strlen(buf)); 32990075Sobrien if (response() < 0) 33090075Sobrien goto next; 33150397Sobrien } 33250397Sobrien#undef MODEMASK 33390075Sobrien#define MODEMASK (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) 33490075Sobrien snprintf(buf, sizeof(buf), "C%04o %lu %s\n", 33552284Sobrien stb.st_mode & MODEMASK, 33690075Sobrien (unsigned long)stb.st_size, 33790075Sobrien last); 33852284Sobrien write(remout, buf, strlen(buf)); 33952284Sobrien if (response() < 0) 34050397Sobrien goto next; 34150397Sobrien if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) { 34252284Sobriennext: close(fd); 34352284Sobrien continue; 34452284Sobrien } 34552284Sobrien 34652284Sobrien /* Keep writing after an error so that we stay sync'd up. */ 34752284Sobrien for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 34850397Sobrien amt = bp->cnt; 34950397Sobrien if (i + amt > stb.st_size) 35050397Sobrien amt = stb.st_size - i; 35150397Sobrien if (!haderr) { 35250397Sobrien result = read(fd, bp->buf, amt); 35350397Sobrien if (result != amt) 35450397Sobrien haderr = result >= 0 ? EIO : errno; 35550397Sobrien } 35650397Sobrien if (haderr) 35750397Sobrien write(remout, bp->buf, amt); 35850397Sobrien else { 35950397Sobrien result = write(remout, bp->buf, amt); 36050397Sobrien if (result != amt) 36150397Sobrien haderr = result >= 0 ? EIO : errno; 36250397Sobrien } 36350397Sobrien } 36450397Sobrien if (close(fd) && !haderr) 36550397Sobrien haderr = errno; 36650397Sobrien if (!haderr) 36750397Sobrien write(remout, "", 1); 36850397Sobrien else 36950397Sobrien run_err("%s: %s", name, strerror(haderr)); 37050397Sobrien response(); 37150397Sobrien } 37250397Sobrien} 37350397Sobrien 37450397Sobrienvoid 37550397Sobrienrsource(char *name, struct stat *statp) 37650397Sobrien{ 37750397Sobrien DIR *dirp; 37850397Sobrien struct dirent *dp; 37950397Sobrien char *last, *vect[1], path[MAXPATHLEN]; 38050397Sobrien 38150397Sobrien if (!(dirp = opendir(name))) { 38250397Sobrien run_err("%s: %s", name, strerror(errno)); 38350397Sobrien return; 38450397Sobrien } 38550397Sobrien last = strrchr(name, '/'); 38650397Sobrien if (last == 0) 38750397Sobrien last = name; 38850397Sobrien else 38950397Sobrien last++; 39050397Sobrien if (pflag) { 39150397Sobrien snprintf(path, sizeof(path), "T%ld 0 %ld 0\n", 39250397Sobrien (long)statp->st_mtime, 39350397Sobrien (long)statp->st_atime); 39450397Sobrien write(remout, path, strlen(path)); 39550397Sobrien if (response() < 0) { 39650397Sobrien closedir(dirp); 39750397Sobrien return; 39890075Sobrien } 39990075Sobrien } 40050397Sobrien snprintf(path, sizeof(path), 40190075Sobrien "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last); 40290075Sobrien write(remout, path, strlen(path)); 40390075Sobrien if (response() < 0) { 40490075Sobrien closedir(dirp); 40550397Sobrien return; 40690075Sobrien } 40790075Sobrien while ((dp = readdir(dirp))) { 40890075Sobrien if (dp->d_ino == 0) 40990075Sobrien continue; 41090075Sobrien if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 41190075Sobrien continue; 41290075Sobrien if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) { 41390075Sobrien run_err("%s/%s: name too long", name, dp->d_name); 41490075Sobrien continue; 41590075Sobrien } 41690075Sobrien snprintf(path, sizeof(path), "%s/%s", name, dp->d_name); 41790075Sobrien vect[0] = path; 41890075Sobrien source(1, vect); 41950397Sobrien } 42050397Sobrien closedir(dirp); 42150397Sobrien write(remout, "E\n", 2); 42250397Sobrien response(); 42352284Sobrien} 42452284Sobrien 42550397Sobrienvoid 42650397Sobriensink(int argc, char **argv) 42750397Sobrien{ 42850397Sobrien static BUF buffer; 42950397Sobrien struct stat stb; 43052284Sobrien struct timeval tv[2]; 43190075Sobrien enum { YES, NO, DISPLAYED } wrerr; 432117395Skan BUF *bp; 433117395Skan off_t i, j, size; 434117395Skan int amt, count, exists, first, mask, mode, ofd, omode; 43590075Sobrien int setimes, targisdir, wrerrno = 0; 43650397Sobrien char ch, *cp, *np, *targ, *why, *vect[1], buf[BUFSIZ]; 43750397Sobrien 43850397Sobrien#define atime tv[0] 43950397Sobrien#define mtime tv[1] 44050397Sobrien#define SCREWUP(str) { why = str; goto screwup; } 44190075Sobrien 44290075Sobrien setimes = targisdir = 0; 44390075Sobrien mask = umask(0); 44490075Sobrien if (!pflag) 44550397Sobrien umask(mask); 44650397Sobrien if (argc != 1) { 44750397Sobrien run_err("ambiguous target"); 44850397Sobrien exit(1); 44950397Sobrien } 45050397Sobrien targ = *argv; 45150397Sobrien if (targetshouldbedirectory) 45250397Sobrien verifydir(targ); 45350397Sobrien write(remout, "", 1); 45450397Sobrien if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 45550397Sobrien targisdir = 1; 45650397Sobrien for (first = 1;; first = 0) { 45750397Sobrien cp = buf; 45850397Sobrien if (read(remin, cp, 1) <= 0) 45950397Sobrien return; 46050397Sobrien if (*cp++ == '\n') 46150397Sobrien SCREWUP("unexpected <newline>"); 46250397Sobrien do { 46350397Sobrien if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) 46450397Sobrien SCREWUP("lost connection"); 465117395Skan *cp++ = ch; 46690075Sobrien } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); 46750397Sobrien *cp = 0; 46850397Sobrien 46950397Sobrien if (buf[0] == '\01' || buf[0] == '\02') { 47050397Sobrien if (iamremote == 0) 47150397Sobrien write(STDERR_FILENO, 47250397Sobrien buf + 1, strlen(buf + 1)); 47350397Sobrien if (buf[0] == '\02') 47450397Sobrien exit(1); 47550397Sobrien ++errs; 47650397Sobrien continue; 47750397Sobrien } 47850397Sobrien if (buf[0] == 'E') { 47950397Sobrien write(remout, "", 1); 48050397Sobrien return; 48190075Sobrien } 48250397Sobrien 48350397Sobrien if (ch == '\n') 48450397Sobrien *--cp = 0; 48550397Sobrien 486117395Skan cp = buf; 487117395Skan if (*cp == 'T') { 488117395Skan setimes++; 489117395Skan cp++; 490117395Skan mtime.tv_sec = strtol(cp, &cp, 10); 491117395Skan if (!cp || *cp++ != ' ') 492117395Skan SCREWUP("mtime.sec not delimited"); 493117395Skan mtime.tv_usec = strtol(cp, &cp, 10); 494117395Skan if (!cp || *cp++ != ' ') 495117395Skan SCREWUP("mtime.usec not delimited"); 49650397Sobrien atime.tv_sec = strtol(cp, &cp, 10); 49750397Sobrien if (!cp || *cp++ != ' ') 49850397Sobrien SCREWUP("atime.sec not delimited"); 49990075Sobrien atime.tv_usec = strtol(cp, &cp, 10); 50050397Sobrien if (!cp || *cp++ != '\0') 50190075Sobrien SCREWUP("atime.usec not delimited"); 50250397Sobrien write(remout, "", 1); 50390075Sobrien continue; 50490075Sobrien } 50590075Sobrien if (*cp != 'C' && *cp != 'D') { 50690075Sobrien /* 50750397Sobrien * Check for the case "rcp remote:foo\* local:bar". 50896263Sobrien * In this case, the line "No match." can be returned 50996263Sobrien * by the shell before the rcp command on the remote is 51096263Sobrien * executed so the ^Aerror_message convention isn't 51196263Sobrien * followed. 51296263Sobrien */ 51396263Sobrien if (first) { 51496263Sobrien run_err("%s", cp); 51596263Sobrien exit(1); 51696263Sobrien } 51796263Sobrien SCREWUP("expected control record"); 51896263Sobrien } 51996263Sobrien mode = 0; 52096263Sobrien for (++cp; cp < buf + 5; cp++) { 52196263Sobrien if (*cp < '0' || *cp > '7') 52290075Sobrien SCREWUP("bad mode"); 52390075Sobrien mode = (mode << 3) | (*cp - '0'); 52490075Sobrien } 52590075Sobrien if (*cp++ != ' ') 52690075Sobrien SCREWUP("mode not delimited"); 52790075Sobrien 52890075Sobrien for (size = 0; isdigit((unsigned char)*cp);) 52990075Sobrien size = size * 10 + (*cp++ - '0'); 53090075Sobrien if (*cp++ != ' ') 53190075Sobrien SCREWUP("size not delimited"); 53290075Sobrien if (targisdir) { 53390075Sobrien static char *namebuf; 53490075Sobrien static int cursize; 53590075Sobrien size_t need; 53690075Sobrien 53790075Sobrien need = strlen(targ) + strlen(cp) + 250; 53890075Sobrien if (need > cursize) { 53990075Sobrien if (!(namebuf = malloc(need))) 54090075Sobrien run_err("%s", strerror(errno)); 54190075Sobrien } 54290075Sobrien snprintf(namebuf, need, "%s%s%s", targ, 54390075Sobrien *targ ? "/" : "", cp); 54490075Sobrien np = namebuf; 54550397Sobrien } else 54650397Sobrien np = targ; 54790075Sobrien exists = stat(np, &stb) == 0; 54890075Sobrien if (buf[0] == 'D') { 54990075Sobrien int mod_flag = pflag; 55090075Sobrien if (exists) { 55190075Sobrien if (!S_ISDIR(stb.st_mode)) { 55290075Sobrien errno = ENOTDIR; 55390075Sobrien goto bad; 55490075Sobrien } 55590075Sobrien if (pflag) 55690075Sobrien chmod(np, mode); 55790075Sobrien } else { 55890075Sobrien /* Handle copying from a read-only directory */ 55990075Sobrien mod_flag = 1; 56090075Sobrien if (mkdir(np, mode | S_IRWXU) < 0) 56190075Sobrien goto bad; 56290075Sobrien } 56390075Sobrien vect[0] = np; 56490075Sobrien sink(1, vect); 56590075Sobrien if (setimes) { 56690075Sobrien setimes = 0; 56790075Sobrien if (utimes(np, tv) < 0) 56890075Sobrien run_err("%s: set times: %s", 56990075Sobrien np, strerror(errno)); 57090075Sobrien } 57190075Sobrien if (mod_flag) 57290075Sobrien chmod(np, mode); 57390075Sobrien continue; 57490075Sobrien } 57590075Sobrien omode = mode; 57690075Sobrien mode |= S_IWRITE; 57790075Sobrien if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 57890075Sobrienbad: run_err("%s: %s", np, strerror(errno)); 57990075Sobrien continue; 58090075Sobrien } 58190075Sobrien write(remout, "", 1); 58290075Sobrien if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) { 58390075Sobrien close(ofd); 58490075Sobrien continue; 58590075Sobrien } 58690075Sobrien cp = bp->buf; 58790075Sobrien wrerr = NO; 58890075Sobrien for (count = i = 0; i < size; i += BUFSIZ) { 58990075Sobrien amt = BUFSIZ; 59090075Sobrien if (i + amt > size) 59190075Sobrien amt = size - i; 59290075Sobrien count += amt; 59390075Sobrien if((j = net_read(remin, cp, amt)) != amt) { 59490075Sobrien run_err("%s", j ? strerror(errno) : 59590075Sobrien "dropped connection"); 59690075Sobrien exit(1); 59790075Sobrien } 59890075Sobrien amt -= j; 59990075Sobrien cp += j; 60090075Sobrien if (count == bp->cnt) { 60150397Sobrien /* Keep reading so we stay sync'd up. */ 60250397Sobrien if (wrerr == NO) { 60350397Sobrien j = write(ofd, bp->buf, count); 60450397Sobrien if (j != count) { 60550397Sobrien wrerr = YES; 60650397Sobrien wrerrno = j >= 0 ? EIO : errno; 60750397Sobrien } 60850397Sobrien } 60950397Sobrien count = 0; 61050397Sobrien cp = bp->buf; 61150397Sobrien } 61250397Sobrien } 61350397Sobrien if (count != 0 && wrerr == NO && 61450397Sobrien (j = write(ofd, bp->buf, count)) != count) { 61550397Sobrien wrerr = YES; 61650397Sobrien wrerrno = j >= 0 ? EIO : errno; 61750397Sobrien } 61850397Sobrien if (ftruncate(ofd, size)) { 61950397Sobrien run_err("%s: truncate: %s", np, strerror(errno)); 62050397Sobrien wrerr = DISPLAYED; 62150397Sobrien } 62250397Sobrien if (pflag) { 62350397Sobrien if (exists || omode != mode) 62450397Sobrien if (fchmod(ofd, omode)) 62550397Sobrien run_err("%s: set mode: %s", 62650397Sobrien np, strerror(errno)); 62750397Sobrien } else { 62850397Sobrien if (!exists && omode != mode) 62950397Sobrien if (fchmod(ofd, omode & ~mask)) 63050397Sobrien run_err("%s: set mode: %s", 63150397Sobrien np, strerror(errno)); 63250397Sobrien } 63350397Sobrien close(ofd); 63450397Sobrien response(); 63550397Sobrien if (setimes && wrerr == NO) { 63650397Sobrien setimes = 0; 63750397Sobrien if (utimes(np, tv) < 0) { 63850397Sobrien run_err("%s: set times: %s", 63996263Sobrien np, strerror(errno)); 64096263Sobrien wrerr = DISPLAYED; 64196263Sobrien } 64296263Sobrien } 64396263Sobrien switch(wrerr) { 64496263Sobrien case YES: 64596263Sobrien run_err("%s: %s", np, strerror(wrerrno)); 64696263Sobrien break; 64796263Sobrien case NO: 64896263Sobrien write(remout, "", 1); 64996263Sobrien break; 65096263Sobrien case DISPLAYED: 65196263Sobrien break; 65296263Sobrien } 65396263Sobrien } 65496263Sobrienscrewup: 65596263Sobrien run_err("protocol error: %s", why); 65696263Sobrien exit(1); 65796263Sobrien} 65896263Sobrien 65996263Sobrienint 66050397Sobrienresponse(void) 66150397Sobrien{ 66250397Sobrien char ch, *cp, resp, rbuf[BUFSIZ]; 66350397Sobrien 66450397Sobrien if (read(remin, &resp, sizeof(resp)) != sizeof(resp)) 66550397Sobrien lostconn(0); 66650397Sobrien 66750397Sobrien cp = rbuf; 66850397Sobrien switch(resp) { 66950397Sobrien case 0: /* ok */ 67050397Sobrien return (0); 67150397Sobrien default: 67250397Sobrien *cp++ = resp; 67350397Sobrien /* FALLTHROUGH */ 67450397Sobrien case 1: /* error, followed by error msg */ 67550397Sobrien case 2: /* fatal error, "" */ 67650397Sobrien do { 67750397Sobrien if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) 67850397Sobrien lostconn(0); 67950397Sobrien *cp++ = ch; 68050397Sobrien } while (cp < &rbuf[BUFSIZ] && ch != '\n'); 68150397Sobrien 68250397Sobrien if (!iamremote) 68350397Sobrien write(STDERR_FILENO, rbuf, cp - rbuf); 68450397Sobrien ++errs; 68550397Sobrien if (resp == 1) 68650397Sobrien return (-1); 68750397Sobrien exit(1); 68850397Sobrien } 68950397Sobrien /* NOTREACHED */ 69050397Sobrien} 69150397Sobrien 69250397Sobrien#include <stdarg.h> 69350397Sobrien 69450397Sobrienvoid 69550397Sobrienrun_err(const char *fmt, ...) 69650397Sobrien{ 69750397Sobrien static FILE *fp; 69850397Sobrien va_list ap; 69950397Sobrien 70050397Sobrien ++errs; 70150397Sobrien if (fp == NULL && !(fp = fdopen(remout, "w"))) 70250397Sobrien return; 70350397Sobrien va_start(ap, fmt); 70450397Sobrien fprintf(fp, "%c", 0x01); 70550397Sobrien fprintf(fp, "rcp: "); 70650397Sobrien vfprintf(fp, fmt, ap); 70750397Sobrien fprintf(fp, "\n"); 70850397Sobrien fflush(fp); 70950397Sobrien va_end(ap); 71050397Sobrien 71150397Sobrien if (!iamremote) { 71250397Sobrien va_start(ap, fmt); 71350397Sobrien vwarnx(fmt, ap); 71450397Sobrien va_end(ap); 71550397Sobrien } 71650397Sobrien} 71750397Sobrien 71850397Sobrien/* 71950397Sobrien * This function executes the given command as the specified user on the 72090075Sobrien * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 72190075Sobrien * assigns the input and output file descriptors on success. 72290075Sobrien * 72390075Sobrien * If it cannot create necessary pipes it exits with error message. 72490075Sobrien */ 72550397Sobrien 72650397Sobrienint 72750397Sobriendo_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) 72850397Sobrien{ 72950397Sobrien int pin[2], pout[2], reserved[2]; 73050397Sobrien 73150397Sobrien /* 73250397Sobrien * Reserve two descriptors so that the real pipes won't get 73350397Sobrien * descriptors 0 and 1 because that will screw up dup2 below. 73450397Sobrien */ 73550397Sobrien pipe(reserved); 73650397Sobrien 73750397Sobrien /* Create a socket pair for communicating with rsh. */ 73850397Sobrien if (pipe(pin) < 0) { 73950397Sobrien perror("pipe"); 74050397Sobrien exit(255); 74150397Sobrien } 74250397Sobrien if (pipe(pout) < 0) { 74350397Sobrien perror("pipe"); 74450397Sobrien exit(255); 74550397Sobrien } 74650397Sobrien 74750397Sobrien /* Free the reserved descriptors. */ 74850397Sobrien close(reserved[0]); 74950397Sobrien close(reserved[1]); 75050397Sobrien 75150397Sobrien /* For a child to execute the command on the remote host using rsh. */ 75250397Sobrien if (fork() == 0) { 75350397Sobrien char *args[100]; 75450397Sobrien unsigned int i; 75550397Sobrien 75650397Sobrien /* Child. */ 75750397Sobrien close(pin[1]); 75850397Sobrien close(pout[0]); 75950397Sobrien dup2(pin[0], 0); 76050397Sobrien dup2(pout[1], 1); 76150397Sobrien close(pin[0]); 76250397Sobrien close(pout[1]); 76350397Sobrien 76450397Sobrien i = 0; 76550397Sobrien args[i++] = RSH_PROGRAM; 76650397Sobrien if (usekrb4) 76750397Sobrien args[i++] = "-4"; 76850397Sobrien if (usekrb5) 76950397Sobrien args[i++] = "-5"; 77050397Sobrien if (usebroken) 77150397Sobrien args[i++] = "-K"; 77250397Sobrien if (doencrypt) 77350397Sobrien args[i++] = "-x"; 77450397Sobrien if (forwardtkt) 77550397Sobrien args[i++] = "-F"; 77650397Sobrien if (noencrypt) 77750397Sobrien args[i++] = "-z"; 77850397Sobrien if (port != NULL) { 77950397Sobrien args[i++] = "-p"; 78050397Sobrien args[i++] = port; 78150397Sobrien } 78250397Sobrien if (eflag) 78350397Sobrien args[i++] = "-e"; 78450397Sobrien if (remuser != NULL) { 78550397Sobrien args[i++] = "-l"; 78650397Sobrien args[i++] = remuser; 78750397Sobrien } 78850397Sobrien args[i++] = host; 78950397Sobrien args[i++] = cmd; 79050397Sobrien args[i++] = NULL; 79150397Sobrien 79250397Sobrien execvp(RSH_PROGRAM, args); 79350397Sobrien perror(RSH_PROGRAM); 79450397Sobrien exit(1); 79550397Sobrien } 79650397Sobrien /* Parent. Close the other side, and return the local side. */ 79750397Sobrien close(pin[0]); 79850397Sobrien *fdout = pin[1]; 79950397Sobrien close(pout[1]); 80050397Sobrien *fdin = pout[0]; 80150397Sobrien return 0; 80250397Sobrien} 80350397Sobrien