|
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)); |