Bei der ersten Übertragung des Tages erhält das SFTP-Put eine unerwartete EOF in 50M-Datei

626
P. Heggie

Ich führe SFTP aus, um eine Datei an einen neuen externen Client zu übergeben. Wir haben ungefähr 10 andere Kunden, einige interne und andere, die wir ohne Probleme hochladen können. Dieser neue Client befindet sich bei uns in einem VPN (einige unserer anderen Clients sind auch bei VPNs, aber nicht bei allen), und antwortet auf einen Put-Befehl mit einem EOF, nachdem etwa 40 MB einer 50M-Datei übertragen wurden. Dies ist bei 10 der letzten 14 Hinrichtungen der Fall.

Mit Ausnahme von zwei Gelegenheiten wird die EOF einmalig am ersten Lauf des Tages um 5:00 Uhr empfangen. Wenn ich SFTP in ein paar Minuten erneut starte, funktioniert der Befehl put und die gesamte Datei wird übertragen. Für den Rest des Tages funktioniert es auch. In der gleichen Ausführung senden wir auch zwei andere Dateien, eine ist 1M und die andere 10M. Mein Skript sortiert die Liste der Dateien nach Dateigröße, sodass die kleineren Dateien zuerst gesendet werden. Dies ist alles in derselben Verbindung. Mein Skript stellt zunächst eine Verbindung her, sendet das Kennwort und stellt sicher, dass es akzeptiert wird, führt ein Änderungsverzeichnis durch, den Befehl ls und den Befehl df. Dann gibt es eine Schleife im Skript, um alle aufgelisteten Dateien abzulegen. Die ersten beiden Dateien werden erfolgreich abgelegt und dann die dritte, aber die EOF schlägt fehl.

Bei den beiden Gelegenheiten, bei denen es sich um die Ausnahme handelt, erhielt ich beim zweiten Versuch eine EOF, und beim dritten Versuch war der Put erfolgreich. Insgesamt waren das 10 der letzten 14 Läufe. Die anderen vier Läufe funktionierten beim ersten Mal (des Tages) erfolgreich. Dieser Job läuft ungefähr zur gleichen Zeit (4:50 bis 5:15). Ich habe mit dem Client gesprochen und die Zeit leicht geändert, um die Intervalle zu umgehen, in denen der Ordnerüberwachungsprozess ausgeführt wird. Das macht aber keinen Unterschied.

Wir führen OpenSSH_6.0p1 unter AIX 7.1 aus und sie führen OpenSSH_7.4p1 unter CentOS Linux 7.4 aus. Zweimal habe ich versucht, das SFTP von der Befehlszeile aus mit der verbose-Option (-vvv) auszuführen, und der Put arbeitete bei diesen beiden Gelegenheiten. Ich werde mein Skript aktualisieren, um die ausführliche Ausgabe irgendwie in eine Protokolldatei zu erfassen. Aber ich habe diese EOF sowohl in einem Skript als auch an der Kommandozeile erhalten, daher glaube ich nicht, dass es das Skript ist. Und ich kann zwei andere Dateien erst ok verschicken, es ist also nicht nur ein Problem mit der ersten gesendeten Datei. Und es ist nicht nur ein Problem mit der Dateigröße, auch wenn nur die 50M-Datei fehlerhaft ist, da sie einige Minuten später problemlos funktioniert.

Einige meiner Fragen sind also: Was kann eine EOF bei einer Put-Operation verursachen? Ich habe die RFC-Spezifikation für Version 2 für SFTP gefunden und sehe keine EOF für Put-Operationen, nur für get-Operationen. Könnte es einen Hintergrundprozess unter CentOS Linux geben, der bei einer großen Dateiübertragung eine EOF verursachen kann? Die Übertragung dauert etwa 25 Sekunden. Könnte dies möglicherweise ein Netzwerkfehler sein? Könnte die Netzwerkqualität anfangs schlecht sein, aber nach einigen Minuten automatisch auf ein höheres Qualitätsniveau aufgerüstet werden? Der Kunde ist 2700 Meilen entfernt. Vielen Dank für alle Kommentare. Peter

UPDATE: Der Hersteller hat mir sein SFTP-Debug-Protokoll geschickt, und ich sehe keine bestimmte Ursache:

28745 debug1: request 786: write "/upload/X_Y_Z_file_20180315040147.txt" (handle 0) off 25329664 len 32768 28745 debug3: request 786: sent status 0 28745 sent status Success 28745 debug1: request 787: write "/upload/X_Y_Z_file_20180315040147.txt" (handle 0) off 25362432 len 32768 28745 debug3: request 787: sent status 0 28745 sent status Success 28745 debug1: request 788: write "/upload/X_Y_Z_file_20180315040147.txt" (handle 0) off 25395200 len 32768 28745 debug3: request 788: sent status 0 28745 sent status Success 28745 debug1: read eof 28745 forced close "/upload/X_Y_Z_file_20180315040147.txt" bytes read 0 written 25427968 28745 session closed for local user abc from [1.2.3.4] 

Edit: Ich glaube nicht, dass mein Skript das Problem ist (aber ich weiß, wie sich das anhört), weil ich das gleiche Problem bekomme, egal ob ich ein Skript verwende oder den SFTP-Befehl von der Befehlszeile aus ausführte. Das Skript verwendet EXPECT zum Abfangen der SFTP-Antwort (100%) oder EOF oder TIMEOUT. Ich werde das Skript beifügen, sobald ich herausgefunden habe, wie das geht.

Edit: Hier ist das Skript für den SFTP-Put:

#! /usr/bin/ksh # The following line is seen as a continuecomment by Tcl\ exec $QUOVADX_INSTALL_DIR/integrator/bin/hcitcl "$0" $  # subroutines first proc gts {} {  set tt [clock format [clock seconds] -format {%y/%m/%d %H:%M:%S} ] set ms [format %03d [expr {[clock clicks -milliseconds] % 1000}]] return $tt.$ms  }  # proc to write to debug file proc debugw { global debugfile set fh [open $debugfile a] ; puts $fh "$msg" ; close $fh }   set frdir [lindex $argv 0] set lfile [lindex $argv 1] set todir [lindex $argv 2] set site [lindex $argv 3]  set debug $::env(cldebug) set parmfile $::env(clparmfile) set module "sftp_put_list"  # get debugfilename if {$debug}  if {$debug}  if {$debug}   # get SFTP connection parms from parmfile using $site as key set hfile [open $parmfile r] ; set data [read $hfile] ; close $hfile set lList [split $data "\n"] ; set pos [lsearch -regexp $lList "^key_sftp_$site"] lassign [split [lindex $lList $pos] " "] key port url pass if {$debug > 1}   # url with vertical bar means the first part is the userid and second is the url set userList [split "$url" "|"] ; lassign $userList user url if {$debug}  set ullen [llength $userList] if {$debug}    log_user 0  # start/spawn the sftp process - either simple user url or separate user  if {[llength $userList] eq 1} { set hname "$user" spawn /usr/bin/sftp -o Port=$port -o ConnectTimeout=60 $user  if {$debug}  } else { set hname "$url" spawn /usr/bin/sftp -o Port=$port -o ConnectTimeout=60 -o User=$user $url if {$debug}  }  set pass_word_prompt "*?assword:*" set sftp_prompt "*?ftp>" set timeout 300  set sftp_put_timeout 1000 set prompt_timeout 300  #-------------------------------------------------------------- # first, wait for the password prompt - if a password is required! #-------------------------------------------------------------- if {$pass ne "#"} { expect { eof { echo "sftp EOF waiting for password prompt|NA" if {$debug}  exit 8 } "$pass_word_prompt" { if {$debug}  } timeout { echo "sftp timeout waiting for password prompt|NA" if {$debug}  exit 8 } }  set timeout $prompt_timeout send "$pass\r" expect { eof { echo "sftp EOF waiting for command prompt after entering password|NA" if {$debug}  exit 8 } "$sftp_prompt" { if {$debug}  } timeout { echo "sftp timeout after $prompt_timeout seconds waiting for command prompt after entering password|NA" if {$debug}  exit 8 } } } else { expect { eof { echo "sftp EOF waiting for command prompt after logging on|NA" if {$debug}  exit 8 } "$sftp_prompt" { if {$debug}  } timeout { echo "sftp timeout after $prompt_timeout seconds waiting for command prompt after logging on|NA" if {$debug}  exit 8 } }  }  #-------------------------------------------------------------- # lcd to local directory from where you will send the file(s) #-------------------------------------------------------------- set timeout $prompt_timeout send "lcd $frdir\r" expect { eof { echo "sftp EOF waiting for command prompt after entering LCD|NA" if {$debug}  exit 8 } "$sftp_prompt" { if {$debug}  } timeout { echo "sftp timeout after $prompt_timeout seconds waiting for command prompt after entering LCD|NA" if {$debug}  exit 8 } }  #-------------------------------------------------------------- # cd to remote directory where you will put the file(s) #-------------------------------------------------------------- set timeout $prompt_timeout send "cd $todir\r" expect { eof { echo "sftp EOF waiting for command prompt after entering CD command|NA" if {$debug}  exit 8 } "No such file or directory" { echo "remote directory $todir not found or not accessible|NA" if {$debug}  exit 8 } "$sftp_prompt" { if {$debug}  } timeout { echo "sftp timeout after $prompt_timeout seconds waiting for prompt after entering CD command|NA" if {$debug}  exit 8 } }   #-------------------------------------------------------------- # print working directory #-------------------------------------------------------------- set timeout $prompt_timeout send "pwd\r" expect { eof { echo "sftp EOF waiting for command prompt after entering pwd command|NA" if {$debug}  exit 8 } "$sftp_prompt" { set response $expect_out(0,string) set rwd [lindex [split "$response" ":"] 1]  set rwd [lindex [split "$rwd" "\r" ] 0] if {$debug}  if {$debug}  } timeout { echo "sftp timeout after $prompt_timeout seconds waiting for prompt after entering pwd command|NA" if {$debug}  exit 8 } }  #-------------------------------------------------------------- # directory list #-------------------------------------------------------------- set timeout $prompt_timeout send "ls -l\r" expect { eof { echo "sftp EOF waiting for command prompt after entering dir command|NA" if {$debug}  exit 8 } "$sftp_prompt" { set response $expect_out(0,string) set dir [lrange [split "$response" "\r"] 1 end-1]  set dir [string map {\{ "" \} ""} "$dir"] set dir [string trim "$dir"] if {$debug}  if {$debug}  } timeout { echo "sftp timeout after $prompt_timeout seconds waiting for prompt after entering dir command|NA" if {$debug}  exit 8 } }  #------------------------------------------------ # loop through list of files - put each file to remote sftp server #------------------------------------------------ set hfile [open $lfile r] ; set data [read $hfile] ; close $hfile set flist [string map {"\n" " "} "$data" ] ; set flist [string trim "$flist"]  set flist [split $flist " "] ; set nfiles [llength $flist] if {$debug}   set ct 0 while {$ct < $nfiles} { set nextfile [lindex $flist $ct] if {$debug}  set dct [expr $ct + 1]  set timeout $sftp_put_timeout send "put $nextfile\r" expect { eof { set response $expect_out(0,string) echo "sftp EOF waiting for command prompt after entering put command|$nextfile" if {$debug}  if {$debug}  if {$debug}  exit 8 } "100%*$sftp_prompt" { if {$debug}  if {$debug}  } timeout { echo "sftp timeout after $sftp_put_timeout seconds waiting for prompt after entering put command|$nextfile" if {$debug}  exit 8 } }  incr ct }  #-------------------------------------------------------------- # post transfer directory list #-------------------------------------------------------------- set timeout $prompt_timeout send "ls -l\r" expect { eof { echo "sftp EOF waiting for command prompt after entering dir command|NA" if {$debug}  exit 8 } "$sftp_prompt" { set response $expect_out(0,string) set dir [lrange [split "$response" "\r"] 1 end-1]  set dir [string map {\{ "" \} ""} "$dir"] set dir [string trim "$dir"] if {$debug}  if {$debug}  } timeout { echo "sftp timeout after $prompt_timeout seconds waiting for prompt after entering dir command|NA" if {$debug}  exit 8 } }  #--------------------------------------------------------- # sftp checks for spawn EOF and will terminate prematurely # unless you add extra expect command #--------------------------------------------------------- set timeout $prompt_timeout send "quit\r" expect { eof { if {$debug}  exit 0 } "$sftp_prompt" { echo "unexpected sftp prompt after entering QUIT command - should be EOF|NA" if {$debug}  exit 2 } timeout { echo "sftp timeout after $prompt_timeout seconds waiting for EOF after QUIT command|NA" if {$debug}  exit 8 } }  exit 0 
2

0 Antworten auf die Frage