View | Details | Raw Unified | Return to bug 2338
Collapse All | Expand All

(-)a/scp.c (-2 / +240 lines)
Lines 295-300 do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) Link Here
295
	return 0;
295
	return 0;
296
}
296
}
297
297
298
static int scpio(void *, size_t);
299
300
/* 
301
 * Read and forward SCP reponse, error messages dupplicate to stderr
302
 * return value determines if FSM continues or ends (pipe closed, fatal errors)
303
 */
304
int
305
handle_accept(int fdin, int fdout, char *buffer)
306
{
307
	char *ch = buffer;
308
309
	/* read first byte */
310
	int res = atomicio(read, fdin, buffer, 1);
311
	if (res == 0){
312
		close(fdout);
313
		return 0; //active = 0;
314
	}
315
	if (buffer[0] == '\01' || buffer[0] == '\02') {
316
		ch = buffer + 1;
317
318
		/* read the rest of input */
319
		do {
320
			res = read(fdin, ch, COPY_BUFLEN-1);
321
		} while (res == -1 && errno == EINTR);
322
		if (res == 0) {
323
			close(fdout);
324
			return 0; // active = 0
325
		}
326
		(void) atomicio(vwrite, STDERR_FILENO, ch, res);
327
		(void) atomicio(vwrite, fdout, buffer, res+1);
328
		if (buffer[0] == '\02')
329
			return 0; // active = 0
330
		return 1; // active = 1;
331
	} else
332
		(void) atomicio(vwrite, fdout, buffer, 1);
333
	//printf("Write %d -> %d data: `%s`\n", fdin, fdout, buffer);
334
	return 1; // active = 1
335
}
336
337
/*
338
 * Unified handling of broken SCP protocol in FSM, or synchronization,
339
 * which is signalized by invalid input in states
340
 */
341
int
342
break_protocol(int state, int fdin, int fdout)
343
{
344
	char ch;
345
	if (atomicio(read, fdin, &ch, 1) == 0)
346
		close(fdout);
347
	fprintf(stderr, "BROKEN PROTOCOL: State: %d, Write %d -> %d data: `%c`\n", state, fdin, fdout, ch);
348
	return 0; // active = 0
349
}
350
298
/*
351
/*
299
 * This functions executes a command simlar to do_cmd(), but expects the
352
 * This functions executes a command simlar to do_cmd(), but expects the
300
 * input and output descriptors to be setup by a previous call to do_cmd().
353
 * input and output descriptors to be setup by a previous call to do_cmd().
Lines 305-310 do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) Link Here
305
{
358
{
306
	pid_t pid;
359
	pid_t pid;
307
	int status;
360
	int status;
361
	int pin[2], pout[2], reserved[2];
308
362
309
	if (verbose_mode)
363
	if (verbose_mode)
310
		fprintf(stderr,
364
		fprintf(stderr,
Lines 312-322 do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) Link Here
312
		    ssh_program, host,
366
		    ssh_program, host,
313
		    remuser ? remuser : "(unspecified)", cmd);
367
		    remuser ? remuser : "(unspecified)", cmd);
314
368
369
	/*
370
	 * Reserve two descriptors so that the real pipes won't get
371
	 * descriptors 0 and 1 because that will screw up dup2 below.
372
	 */
373
	if (pipe(reserved) < 0)
374
		fatal("pipe: %s", strerror(errno));
375
376
	/* Create a socket pair for communicating with ssh. */
377
	if (pipe(pin) < 0)
378
		fatal("pipe: %s", strerror(errno));
379
	if (pipe(pout) < 0)
380
		fatal("pipe: %s", strerror(errno));
381
382
	/* Free the reserved descriptors. */
383
	close(reserved[0]);
384
	close(reserved[1]);
385
315
	/* Fork a child to execute the command on the remote host using ssh. */
386
	/* Fork a child to execute the command on the remote host using ssh. */
316
	pid = fork();
387
	pid = fork();
317
	if (pid == 0) {
388
	if (pid == 0) {
318
		dup2(fdin, 0);
389
		close(pin[0]);
319
		dup2(fdout, 1);
390
		close(pout[1]);
391
		dup2(pin[1], 1);
392
		dup2(pout[0], 0);
393
		close(pin[1]);
394
		close(pout[0]);
320
395
321
		replacearg(&args, 0, "%s", ssh_program);
396
		replacearg(&args, 0, "%s", ssh_program);
322
		if (remuser != NULL) {
397
		if (remuser != NULL) {
Lines 333-338 do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) Link Here
333
	} else if (pid == -1) {
408
	} else if (pid == -1) {
334
		fatal("fork: %s", strerror(errno));
409
		fatal("fork: %s", strerror(errno));
335
	}
410
	}
411
	close(pin[1]);
412
	close(pout[0]);
413
414
	typedef enum {START, ACCEPT, TRANSFER_ACCEPT, TRANSFER} state_t;
415
	state_t state = START;
416
	fd_set readset, activeset;
417
	int maxfd, res, active = 1, ptr, amt;
418
	off_t size, statbytes, i;
419
	static char buffer[COPY_BUFLEN+1];
420
	char *filename = NULL;
421
	size_t filename_size = 0, filename_length, j;
422
423
	/* Prepare select structures */
424
	FD_ZERO(&readset);
425
	FD_SET(pin[0], &readset);
426
	FD_SET(fdin, &readset);
427
	maxfd = fdin > pin[0] ? fdin : pin[0];
428
	do {
429
		activeset = readset;
430
		res = select(maxfd+1, &activeset, NULL, NULL, NULL);
431
		if (res < 0) {
432
			perror("select");
433
			break;
434
		} else {
435
			/* FSM doing forwarding between pipes */
436
			/*      fdin   -->  pout[1]           */
437
			/*      pin[0] -->  fdout             */
438
			switch (state) {
439
				case START:
440
					/*                       START                      */
441
					/* Accept confirmation signal from destination      */
442
					//printf("  START: \n");
443
					if (FD_ISSET(pin[0], &activeset)) { // destination -> source
444
						active = handle_accept(pin[0], fdout, buffer);
445
						state = ACCEPT;
446
					}
447
					if (FD_ISSET(fdin, &activeset)) { // source -> destination
448
						active = break_protocol(state, fdin, pout[1]);
449
					}
450
					break;
451
				case ACCEPT:
452
					/*                      ACCEPT                        */
453
					/* Destination is alive so source will send metadata  */
454
					//printf("  ACCEPT: \n");
455
					if (FD_ISSET(pin[0], &activeset)) { // destination -> source
456
						active = break_protocol(state, pin[0], fdout);
457
					}
458
					if (FD_ISSET(fdin, &activeset)) { // source -> destination
459
						/* read all you can until you reach for end of line */
460
						do {
461
							res = read(fdin, buffer, COPY_BUFLEN);
462
						} while (res == -1 && errno == EINTR);
463
						if (res == 0){
464
							close(pout[1]);
465
							active = 0;
466
							break;
467
						}
468
						/* Forward it to destination */
469
						(void) atomicio(vwrite, pout[1], buffer, res);
470
						//buffer[res] = '\0';
471
						//printf("Write %d -> %d data: `%s`\n", fdin, pout[1], buffer);
472
473
						/* Parse out file size and file name for progressmeter */
474
						if (buffer[0] == 'C') {
475
							ptr = 6; // skip initial C, 4 bytes mode and space
476
							for(size = 0; isdigit((unsigned char) buffer[ptr]);)
477
								size = size * 10 + (buffer[ptr++] - '0');
478
							if (showprogress) {
479
								ptr++;
480
								filename_length = (int)(strchr(buffer+ptr, '\n')-buffer-ptr);
481
								if (filename_length >= filename_size) {
482
									filename_size = filename_length + 50;
483
									// extend little bit more to avoid further reallocs
484
									filename = realloc(filename, (filename_size)*sizeof(char));
485
									if (filename == NULL) {
486
										fprintf(stderr, "Failed to allocate memory for progressmeter, disabling\n");
487
										showprogress = 0;
488
										filename_size = 0;
489
									}
490
								}
491
								strncpy(filename, buffer+ptr, filename_length);
492
								filename[filename_length] = '\0';
493
							}
494
							state = TRANSFER_ACCEPT;
495
						/* Directory, end of directory and times are just passed along */
496
						} else if (buffer[0] == 'D' || buffer[0] == 'E' || buffer[0] == 'T')
497
							state = START;
498
						/* Errors are written out. Fatal errors end cycle */
499
						else if (buffer[0] == '\01' || buffer[0] == '\02') {
500
							(void) atomicio(vwrite, STDERR_FILENO, buffer + 1, res-1);
501
							if (buffer[0] == '\02')
502
								active = 0;
503
							state = ACCEPT;
504
						} else {
505
							fprintf(stderr, "Received unexpedced string from source: `%s`", buffer);
506
							active = 0;
507
						}
508
					}
509
					break;
510
				case TRANSFER_ACCEPT:
511
					/*                  TRANFER_ACCEPT                    */
512
					/* Source is ready, wait for destination to confirm   */
513
					//printf("  TRANSFER_ACCEPT: \n");
514
					if (FD_ISSET(pin[0], &activeset)) { // destination -> source
515
						active = handle_accept(pin[0], fdout, buffer);
516
						state = TRANSFER;
517
					}
518
					if (FD_ISSET(fdin, &activeset)) { // source -> destination
519
						active = break_protocol(state, fdin, pout[1]);
520
					}
521
					break;
522
				case TRANSFER:
523
					/*                      TRANSFER                         */
524
					/* Source is sending data, we are handling progressmeter */
525
					//printf("  TRANSFER: \n");
526
					if (FD_ISSET(pin[0], &activeset)) { // destination -> source
527
						active = break_protocol(state, fdin, pout[1]);
528
					}
529
					if (FD_ISSET(fdin, &activeset)) { // source -> destination
530
						if (showprogress) {
531
							statbytes = 0;
532
							start_progress_meter(filename, size, &statbytes);
533
						}
534
						set_nonblock(fdin);
535
						for (i = 0; i < size; i += COPY_BUFLEN) {
536
							/* How much we can read? */
537
							amt = COPY_BUFLEN;
538
							if (i + (off_t)amt > size)
539
								amt = size - i;
540
							do {
541
								j = atomicio(read, fdin, buffer, amt);
542
								if (j == 0) {
543
									active = 0;
544
									close(pout[1]);
545
									break;
546
								}
547
								//buffer[amt] = '\0';
548
								//printf("Write %d -> %d data: `%s`\n", fdin, pout[1], buffer);
549
								j = atomicio6(vwrite, pout[1], buffer, amt,
550
									scpio, &statbytes);
551
								amt -= j;
552
							} while (amt > 0);
553
						}
554
						unset_nonblock(fdin);
555
						if (showprogress) {
556
							stop_progress_meter();
557
						}
558
						/* Confirm end of data without error*/
559
						active = handle_accept(fdin, pout[1], buffer);
560
						state = START;
561
					}
562
					break;
563
				default:
564
					fprintf(stderr, "Unknown state\n");
565
					active = 0;
566
					break;
567
			}
568
		}
569
	} while (active);
570
571
	/* cleanup filename if it was used */
572
	if (filename_size > 0)
573
		free(filename);
336
	while (waitpid(pid, &status, 0) == -1)
574
	while (waitpid(pid, &status, 0) == -1)
337
		if (errno != EINTR)
575
		if (errno != EINTR)
338
			fatal("do_cmd2: waitpid: %s", strerror(errno));
576
			fatal("do_cmd2: waitpid: %s", strerror(errno));

Return to bug 2338