Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
}