801 lines · 13.6 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <ctype.h> intisatty(int fd){	char buf[64]; 	buf[0] = '\0';	fd2path(fd, buf, sizeof buf);	if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0)		return 1;	return 0;} #define	OK	0x00#define	ERROR	0x01#define	FATAL	0x02 char	*progname; int	dflag;int	fflag;int	iflag;int	pflag;int	rflag;int	tflag;int	vflag; int	remote; char	*exitflag = nil; void	scperror(int, char*, ...);void	mustbedir(char*);void	receive(char*);char	*fileaftercolon(char*);void	destislocal(char *cmd, int argc, char *argv[], char *dest);void	destisremote(char *cmd, int argc, char *argv[], char *host, char *dest);int	remotessh(char *host, char *cmd);void	send(char*);void	senddir(char*, int, Dir*);int 	getresponse(void); char	theuser[32]; char	ssh[] = "/bin/ssh"; int	remotefd0;int	remotefd1; intruncommand(char *cmd){	Waitmsg *w;	int pid;	char *argv[4]; 	if (cmd == nil)		return -1;	switch(pid = fork()){	case -1:		return -1;	case 0:		argv[0] = "rc";		argv[1] = "-c";		argv[2] = cmd;		argv[3] = nil;		exec("/bin/rc", argv);		exits("exec failed");	}	for(;;){		w = wait();		if(w == nil)			return -1;		if(w->pid == pid)			break;		free(w);	}	if(w->msg[0]){		free(w);		return -1;	}	free(w);	return 1;} voidvprint(char *fmt, ...){	char buf[1024];	va_list arg;	static char *name; 	if(vflag == 0)		return; 	va_start(arg, fmt);	vseprint(buf, buf+sizeof(buf), fmt, arg);	va_end(arg); 	if(name == nil){		name = sysname();		if(name == nil)			name = "<unknown>";	}	fprint(2, "%s: %s\n", name, buf);} voidusage(void){	fprint(2, "usage: scp [-Iidfprtv] source ... destination\n");	exits("usage");}  #pragma	varargck	type	"F"	int#pragma	varargck	type	"V"	char*static int flag; /* flag: if integer flag, take following char *value */intflagfmt(Fmt *f){	flag = va_arg(f->args, int);	return 0;} /* flag: if previous integer flag, take char *value */intvalfmt(Fmt *f){	char *value; 	value = va_arg(f->args, char*);	if(flag)		return fmtprint(f, " %s", value);	return 0;} voidsendokresponse(void){	char ok = OK; 	write(remotefd1, &ok, 1);} voidmain(int argc, char *argv[]){	int i, fd;	char cmd[32];	char *p; 	progname = argv[0];	fmtinstall('F', flagfmt);	fmtinstall('V', valfmt);	iflag = -1; 	ARGBEGIN {	case 'I':		iflag = 0;		break;	case 'i':		iflag = 1;		break;	case 'd':		dflag++;		break;	case 'f':		fflag++;		remote++;		break;	case 'p':		pflag++;		break;	case 'r':		rflag++;		break;	case 't':		tflag++;		remote++;		break;	case 'v':		vflag++;		break;	default:		usage();	} ARGEND 	if(iflag == -1)		iflag = isatty(0); 	remotefd0 = 0;	remotefd1 = 1; 	if(fflag){		getresponse();		for(i=0; i<argc; i++)			send(argv[i]);		exits(0);	}	if(tflag){		if(argc != 1)			usage();		receive(argv[0]);		exits(0);	} 	if (argc < 2)		usage();	if (argc > 2)		dflag = 1; 	i = 0;	fd = open("/dev/user", OREAD);	if(fd >= 0){		i = read(fd, theuser, sizeof theuser - 1);		close(fd);	}	if(i <= 0)		scperror(1, "can't read /dev/user: %r"); 	remotefd0 = -1;	remotefd1 = -1; 	snprint(cmd, sizeof cmd, "scp%F%V%F%V%F%V%F%V",		dflag, "-d",		pflag, "-p",		rflag, "-r",		vflag, "-v"); 	p = fileaftercolon(argv[argc-1]);	if(p != nil)	/* send to remote machine. */		destisremote(cmd, argc-1, argv, argv[argc-1], p);	else{		if(dflag)			mustbedir(argv[argc-1]);		destislocal(cmd, argc-1, argv, argv[argc-1]);	} 	exits(exitflag);} voiddestislocal(char *cmd, int argc, char *argv[], char *dst){	int i;	char *src;	char buf[8192]; 	for(i = 0; i<argc; i++){		src = fileaftercolon(argv[i]);		if(src == nil){			/* local file; no network */			snprint(buf, sizeof buf, "exec cp%F%V%F%V %s %s",				rflag, "-r",				pflag, "-p",				argv[i], dst);	  		vprint("remotetolocal: %s", buf);			if(runcommand(buf) < 0)				exitflag = "local cp exec";		}else{			/* remote file; use network */			snprint(buf, sizeof buf, "%s -f %s", cmd, src);		  	if(remotessh(argv[i], buf) < 0)				exitflag = "remote ssh exec";			else{				receive(dst);				close(remotefd0);				remotefd0 = -1;				remotefd1 = -1;			}		}	}} voiddestisremote(char *cmd, int argc, char *argv[], char *host, char *dest){	int i;	char *src;	char buf[8192]; 	for(i = 0; i < argc; i++){		vprint("remote destination: send %s to %s:%s", argv[i], host, dest);		/* destination is remote, but source may be local */		src = fileaftercolon(argv[i]);		if(src != nil){			/* remote to remote */			snprint(buf, sizeof buf, "exec %s%F%V%F%V %s %s %s '%s:%s'",				ssh,				iflag, " -i",				vflag, "-v",				argv[i], cmd, src,				host, dest);			vprint("localtoremote: %s", buf);			runcommand(buf);		}else{			/* local to remote */			if(remotefd0 == -1){				snprint(buf, sizeof buf, "%s -t %s", cmd, dest);				if(remotessh(host, buf) < 0)					exits("remotessh");				if(getresponse() < 0)					exits("bad response");			}			send(argv[i]);		}	}} voidreadhdr(char *p, int n){	int i; 	for(i=0; i<n; i++){		if(read(remotefd0, &p[i], 1) != 1)			break;		if(p[i] == '\n'){			p[i] = '\0';			return;		}	}	/* if at beginning, this is regular EOF */	if(i == 0)		exits(nil);	scperror(1, "read error on receive header: %r");} Dir *receivedir(char *dir, int exists, Dir *d, int settimes, ulong atime, ulong mtime, ulong mode){	Dir nd;	int setmodes;	int fd; 	setmodes = pflag;	if(exists){		if(!(d->qid.type & QTDIR)) {			scperror(0, "%s: protocol botch: directory requrest for non-directory", dir);			return d;		}	}else{		/* create it writeable; will fix later */		setmodes = 1;		fd = create(dir, OREAD, DMDIR|mode|0700);		if (fd < 0){			scperror(0, "%s: can't create: %r", dir);			return d;		}		d = dirfstat(fd);		close(fd);		if(d == nil){			scperror(0, "%s: can't stat: %r", dir);			return d;		}	}	receive(dir);	if(settimes || setmodes){		nulldir(&nd);		if(settimes){			nd.atime = atime;			nd.mtime = mtime;			d->atime = nd.atime;			d->mtime = nd.mtime;		}		if(setmodes){			nd.mode = DMDIR | (mode & 0777);			d->mode = nd.mode;		}		if(dirwstat(dir, &nd) < 0){			scperror(0, "can't wstat %s: %r", dir);			free(d);			return nil;		}	}	return d;} voidreceive(char *dest){	int isdir, settimes, mode;	int exists, n, i, fd, m;	int errors;	ulong atime, mtime;	uvlong size;	char buf[32*1024], *p;	char name[8192];	Dir *d;	Dir nd; 	mtime = 0L;	atime = 0L;	settimes = 0;	isdir = 0;	if ((d = dirstat(dest)) && (d->qid.type & QTDIR)) {		isdir = 1;	}	if(dflag && !isdir)		scperror(1, "%s: not a directory: %r", dest); 	sendokresponse(); 	for (;;) {		readhdr(buf, sizeof buf); 		switch(buf[0]){		case ERROR:		case FATAL:			if(!remote)				fprint(2, "%s\n", buf+1);			exitflag = "bad receive";			if(buf[0] == FATAL)				exits(exitflag);			continue; 		case 'E':			sendokresponse();			return; 		case 'T':			settimes = 1;			p = buf + 1;			mtime = strtol(p, &p, 10);			if(*p++ != ' '){		Badtime:				scperror(1, "bad time format: %s", buf+1);			}			strtol(p, &p, 10);			if(*p++ != ' ')				goto Badtime;			atime = strtol(p, &p, 10);			if(*p++ != ' ')				goto Badtime;			strtol(p, &p, 10);			if(*p++ != 0)				goto Badtime; 			sendokresponse();			continue; 		case 'D':		case 'C':			p = buf + 1;			mode = strtol(p, &p, 8);			if (*p++ != ' '){		Badmode:				scperror(1, "bad mode/size format: %s", buf+1);			}			size = strtoll(p, &p, 10);			if(*p++ != ' ')				goto Badmode; 			if(isdir){				if(dest[0] == '\0')					snprint(name, sizeof name, "%s", p);				else					snprint(name, sizeof name, "%s/%s", dest, p);			}else				snprint(name, sizeof name, "%s", dest);			if(strlen(name) > sizeof name-UTFmax)				scperror(1, "file name too long: %s", dest); 			exists = 1;			free(d);			if((d = dirstat(name)) == nil)				exists = 0; 			if(buf[0] == 'D'){				vprint("receive directory %s", name);				d = receivedir(name, exists, d, settimes, atime, mtime, mode);				settimes = 0;				continue;			} 			vprint("receive file %s by %s", name, getuser());			fd = create(name, OWRITE, mode);			if(fd < 0){				scperror(0, "can't create %s: %r", name);				continue;			}			sendokresponse(); 			/*			 * Committed to receive size bytes			 */			errors = 0;			for(i = 0; i < size; i += m){				n = sizeof buf;				if(n > size - i)					n = size - i;				m = readn(remotefd0, buf, n);				if(m <= 0)					scperror(1, "read error on connection: %r");

Lines 1–500 of 801.