Guest User

Untitled

a guest
May 30th, 2018
141
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.60 KB | None | 0 0
  1. #! /usr/bin/ksh
  2.  
  3.  
  4. #
  5. # HardFeed -- Harvest A Remote Directory via
  6. # Ftp Enhanced Exhaustive Duplication
  7. #
  8. # Perderabo 11-23-02
  9.  
  10. VERSION="1.1" # 03-16-04
  11.  
  12. USAGE="\
  13. HardFeed [ -v | -s | -d | -r | -f | -m | -p password-file
  14. -l list-command | -x ftp-command ... ] system user [directory]
  15.  
  16. use \"HardFeed -h\" use for more documentation
  17.  
  18. -v (verbose) Print stuff during run
  19. -s (symlink) Attempt to duplicate any remote symlinks
  20. -d (directory)Attempt to duplicate any remote directories
  21. -r (recurse) Attempt to descend into any directories and process them
  22. -f (freshen) If remote file is newer than local file, overwrite the local
  23. -m (mode) Attempt a chmod on files and directories that we create
  24. -p (password) Specify a file that contains the password in plaintext
  25. -x (extra) Specify a command to be sent to the ftp client
  26. -l (listcmd) Override the choice of \"ls .\" to get a remote directory"
  27.  
  28. DOCUMENTATION="HardFeed Version $VERSION
  29.  
  30. $USAGE
  31.  
  32. HardFeed copies all of the contents of a remote directory to the current
  33. directory using ftp. It establishes an ftp connection to the remote
  34. site and it uses the ftp command \"ls\" to get a listing of the remote
  35. directory. The two required parameters are the remote sytstem and the user
  36. name. The optional third parameter is the remote directory to copy. The
  37. default is just the home directory of the ftp account.
  38.  
  39. HardFeed will prompt you for the password. This is very secure but it isn't
  40. any good if you want to run HardFeed automatically. You can set the password in
  41. the environment variable HARDFEED_P as an alternate. HardFeed will set an
  42. internal variable to the password and then clobber the variable HARDFEED_P,
  43. since on some systems, the environment of another process can be displayed.
  44. With most shells, you can also set an environment variable for one command
  45. only, like this: \"HARDFEED_P=xyzzy HardFeed -dR ftpxy joeblow sourcedir\".
  46. A second alternative is to specify a \"password file\" with the -p option.
  47. Such a file contains, in plaintext, the password. HardFeed will read the file
  48. to get the password. You must decide which option makes more sense in your
  49. environment.
  50.  
  51. Only files are examined. If we don't have a copy of the remote file, we
  52. will get it. HardFeed will never overwrite an existing file system object
  53. with one exception. If you specify -f and we have both a remote file and a
  54. local file, the timestamps are compared. If the remote file is newer, a
  55. retrieval attempt will be made. The local file must be writable for this
  56. to succeed. For the timestamp compare to work, you and the remote system
  57. must be in the same timezone. (You can vary your environment to make this
  58. true.)
  59.  
  60. Normally symbolic links are ignored. But with -s, we will attempt to create
  61. a symlink with the same link data. Even with -s, we will never overwrite
  62. any existing object with a new symbolic link. You will need to review any
  63. symlinks created and probably correct them.
  64.  
  65. Normally, directories are ignored. If you specify -d, HardFeed will attempt
  66. to create the directory locally. But again, it will never overwrite an
  67. existing object to create a directory. If you specify -r, HardFeed will
  68. attempt to recurse into a directory and process all of the files there. If
  69. you use both -d and -r, it will copy an entire directory hierarchy. But you
  70. can leave off -d and only pre-create a few directories if you want.
  71.  
  72. HardFeed will attempt a chmod of any file or directory that it creates if you
  73. specify -m. It will try to match the mode of the remote object.
  74.  
  75. HardFeed operates by establishing a co-process to the ftp command. Normally,
  76. the output from the co-process is sent to an un-named file in /tmp and
  77. discarded. If you want to capture this output, connect a file to fd 3 and
  78. HardFeed will use it for this purpose. From ksh the syntax is 3>file. You
  79. can also do 3>&1 to see it real time during the run if you really want.
  80.  
  81. You can make HardFeed send the ftp co-process some extra commands after the
  82. connection is established with -x.
  83.  
  84. HardFeed gets a directory listing by sending a \"ls .\" command to the server.
  85. Some servers will list dot files with this while others won't. You can use the
  86. -l option to change the command if your server needs a different one to do want
  87. you want. -l \"ls -al\" is one example that I got to work with unix.
  88.  
  89. For a microsoft ftp server, I had some luck with:
  90. -l \"ls -la\" -x \"quote site dirstyle\"
  91. Note that everything is transferred in binary mode. -x ascii will switch
  92. everything to ascii mode. HardFeed supports embedded spaces in filenames. User
  93. names may be long and contain slashes. All of this may make it somewhat usable
  94. with microsoft ftp servers."
  95.  
  96.  
  97. IFS=""
  98.  
  99. #
  100. # If the password is coming in via the environment, save it in
  101. # a local variable and then clobber the environment variable
  102.  
  103. unset PASSWORD
  104. if [[ -n $HARDFEED_P ]] ; then
  105. PASSWORD="$HARDFEED-P"
  106. HARDFEED_P='********'
  107. fi
  108.  
  109.  
  110. #
  111. # Parse Command Line
  112. #
  113. set -A OPT_CMDS_LIST
  114. OPT_DIRCMD="ls ."
  115. OPT_VERBOSE=0
  116. OPT_SYMLINKS=0
  117. OPT_DIRECTORIES=0
  118. OPT_RECURS=0
  119. OPT_FRESHEN=0
  120. OPT_MODE=0
  121. OPT_PASSWORDFILE=""
  122. OPT_CMDS=0
  123. error=0
  124. while getopts :vsdrfmhp:x:l: o ; do
  125. case $o in
  126. v) OPT_VERBOSE=1
  127. ;;
  128. s) OPT_SYMLINKS=1
  129. ;;
  130. d) OPT_DIRECTORIES=1
  131. ;;
  132. r) OPT_RECURS=1
  133. ;;
  134. f) OPT_FRESHEN=1
  135. ;;
  136. m) OPT_MODE=1
  137. ;;
  138. h) echo "$DOCUMENTATION"
  139. exit 0
  140. ;;
  141. p) OPT_PASSWORDFILE=$OPTARG
  142. if [[ ! -f $OPT_PASSWORDFILE ]] ; then
  143. echo error $OPT_PASSWORDFILE is not a file
  144. error=1
  145. fi
  146. ;;
  147. x) OPT_CMDS_LIST[OPT_CMDS]="$OPTARG"
  148. ((OPT_CMDS=OPT_CMDS+1))
  149. ;;
  150. l) OPT_DIRCMD="$OPTARG"
  151. ;;
  152. ?) print error argument $OPTARG is illegal
  153. error=1
  154. ;;
  155. esac
  156. done
  157. shift OPTIND-1
  158. if ((error)) ; then
  159. echo "$USAGE"
  160. exit 1
  161. fi
  162. if [[ $# -ne 2 && $# -ne 3 ]] ; then
  163. echo "$USAGE"
  164. exit 1
  165. fi
  166. SYSTEM=$1
  167. USER=$2
  168. DIRECTORY=$3
  169. [[ -z $DIRECTORY ]] && DIRECTORY=.
  170.  
  171. #
  172. # Read password file if one is supplied
  173.  
  174. if [[ -n $OPT_PASSWORDFILE ]] ; then
  175. read PASSWORD < $OPT_PASSWORDFILE
  176. fi
  177.  
  178.  
  179. #
  180. # Request password if it didn't come in via env or file
  181.  
  182. if [[ -z $PASSWORD ]] ; then
  183. print -n password -
  184. stty -echo
  185. read PASSWORD
  186. echo
  187. stty echo
  188. fi
  189.  
  190. #
  191. # FD 3 will be the transcript of the ftp co-process. If the user
  192. # supplied a file for this, we will use that. Otherwise it will go
  193. # to a nameless file in /tmp
  194.  
  195. if print -u3 " Transcript of the ftp co-process for HardFeed" 2>/dev/null ; then
  196. LOGFILE=""
  197. else
  198. LOGFILE=/tmp/HardFeed.log.$$
  199. exec 3>$LOGFILE
  200. rm $LOGFILE
  201. fi
  202.  
  203. #
  204. # Max time to wait for arrivial of file. This is a long time. During
  205. # an interactive run, the user can use SIGINT if it seems to be taking
  206. # too long. This max is intended to assure that a cron job will not
  207. # hang forever.
  208.  
  209. OPT_MAXWAIT=15
  210. TIMEOUT=/tmp/HardFeed.timeout.$$
  211.  
  212. #
  213. # Various other initializations
  214.  
  215. LEV=0
  216. date "+%Y %m" | IFS=" " read THISYEAR THISMONTH
  217. ((LASTYEAR=THISYEAR-1))
  218. STARTPATH=$(pwd)
  219. set -A DIR_FILE_NAME
  220. set -A DIR_LINE_NUM
  221.  
  222. #
  223. # Function to convert month to numeric
  224.  
  225. conv_month() {
  226. typeset -l month
  227. month=$1
  228. case $month in
  229. jan) nmonth=1 ;;
  230. feb) nmonth=2 ;;
  231. mar) nmonth=3 ;;
  232. apr) nmonth=4 ;;
  233. may) nmonth=5 ;;
  234. jun) nmonth=6 ;;
  235. jul) nmonth=7 ;;
  236. aug) nmonth=8 ;;
  237. sep) nmonth=9 ;;
  238. oct) nmonth=10 ;;
  239. nov) nmonth=11 ;;
  240. dec) nmonth=12 ;;
  241. *) nmonth=0 ;;
  242. esac
  243. echo $nmonth
  244. return $((!nmonth))
  245. }
  246.  
  247.  
  248. #
  249. # Function to determine if a file system object exists
  250. #
  251. # neither -a nor -e is really portable 8(
  252.  
  253. exists() {
  254. [[ -f $1 || -d $1 || -L $1 || -p $1 || -S $1 || -b $1 || -c $1 ]]
  255. return $?
  256. }
  257.  
  258.  
  259. #
  260. # Function to wait for a file to arrive
  261.  
  262. waitfor() {
  263. wanted=$1
  264. if ((OPT_MAXWAIT)) ; then
  265. ((GIVEUP=SECONDS+OPT_MAXWAIT))
  266. else
  267. GIVEUP="-1"
  268. fi
  269.  
  270. while [[ ! -f $wanted && $SECONDS -lt $GIVEUP ]] ; do
  271. sleep 1
  272. done
  273. if [[ ! -f $wanted ]] ; then
  274. echo "FATAL ERROR:" timed out waiting for: 2>&1
  275. echo " " "$wanted" 2>&1
  276. echo
  277. print -p bye 2>/dev/null
  278. exit 2
  279. fi
  280. return 0
  281. }
  282. #
  283. # Function to decode an "ls -l" line.
  284.  
  285. lsdcode() {
  286.  
  287. typeset -Z2 nmonth day
  288. typeset -i8 octal
  289.  
  290. #
  291. # get the line, get the first character, split line into words
  292.  
  293. line="$1"
  294. char1=${line%%${line#?}}
  295. IFS=" "
  296. set -A things -- $line
  297. IFS=""
  298.  
  299. #
  300. # We may have a "total" line which needs to be ignored
  301.  
  302. if [[ ${things[0]} = total ]] ; then
  303. set -A lsdc -- skip 000 000000000000 x x
  304. return 0
  305. fi
  306.  
  307. #
  308. #
  309. parser=1
  310. month=${things[5]}
  311. xmonth=$(conv_month $month)
  312. if conv_month $month > /dev/null ; then
  313. parser=1
  314. else
  315. parser=0
  316. fi
  317.  
  318. if ((parser)); then
  319. #
  320. # Strict Left to Right Parse Routine
  321. #
  322. # Break out the fields that we want. This technique requires
  323. # that the user, group, and size fields never run together and
  324. # so they must have at least one space between them. But it
  325. # allows some limited support of filenames with embedded spaces.
  326.  
  327. echo "$line" | IFS=" " read permstring junk junk junk junk \
  328. month day swing rawname
  329. if [[ $char1 = l ]] ; then
  330. link=${rawname#*-\> }
  331. name=${rawname% -\>*}
  332. else
  333. name="$rawname"
  334. link=""
  335. fi
  336. else
  337. #
  338. # Outside to Inside Parse Routine
  339. #
  340. # Break out the fields that we want. This technique requires
  341. # that no white space exist in the filename. But the user,
  342. # group, and size fields may sometimes run together without
  343. # causing a problem.
  344.  
  345. echo "WARNING:" badly formatted line in directory listing for: >&2
  346. echo " " "${line}" >&2
  347. echo " " attempting outside-to-inside scan >&2
  348. echo >&2
  349.  
  350. ((pname=${#things[*]}-1))
  351. if [[ $char1 = l ]] ; then
  352. link=${things[pname]}
  353. ((pname=pname-2))
  354. else
  355. link=
  356. fi
  357. permstring=${things[0]}
  358. name=${things[pname]}
  359. month=${things[pname-3]}
  360. day=${things[pname-2]}
  361. swing=${things[pname-1]}
  362. if conv_month $month > /dev/null ; then
  363. :
  364. else
  365. echo "ERROR: " outside-to-inside scan has also failed >&2
  366. echo " " giving up on: >&2
  367. echo " " "$line" >&2
  368. echo >&2
  369. set -A lsdc -- skip 000 000000000000 x x
  370. return 0
  371. fi
  372. fi
  373.  
  374.  
  375. #
  376. # Ignore . and ..
  377.  
  378. if [[ $name = . || $name = .. ]] ; then
  379. set -A lsdc -- skip 000 000000000000 x x
  380. return 0
  381. fi
  382.  
  383. #
  384. # decode permissions (the permission string is first word
  385.  
  386. set -A perms -- $(print -- ${permstring#?} | sed 's/./& /g')
  387. extras=0
  388. [[ ${perms[2]} = S ]] && { ((extras=extras+4000)); perms[2]=- ; }
  389. [[ ${perms[2]} = s ]] && { ((extras=extras+4000)); perms[2]=x ; }
  390. [[ ${perms[5]} = S ]] && { ((extras=extras+2000)); perms[5]=- ; }
  391. [[ ${perms[5]} = s ]] && { ((extras=extras+2000)); perms[5]=x ; }
  392. [[ ${perms[8]} = T ]] && { ((extras=extras+1000)); perms[8]=- ; }
  393. [[ ${perms[8]} = t ]] && { ((extras=extras+1000)); perms[8]=x ; }
  394.  
  395. binary=2#$(print -- ${perms[@]} | sed 's/ //g;s/-/0/g;s/[^0]/1/g')
  396. ((octal=binary))
  397. result=$(echo $octal)
  398. result=${result#??}
  399. ((result=result+extras))
  400.  
  401. #
  402. # Decode date and time and convert it to yyyymmddhhmm
  403.  
  404. nmonth=$(conv_month $month)
  405. if [[ $swing = *:* ]] ; then
  406. if [[ $nmonth > $THISMONTH ]] ; then
  407. ((year=LASTYEAR))
  408. else
  409. ((year=THISYEAR))
  410. time1=${swing%???}
  411. time2=${swing#???}
  412. time="${time1}${time2}"
  413. fi
  414. else
  415. year=$swing
  416. time="0000"
  417. fi
  418.  
  419. #
  420. # Output the final record
  421.  
  422. set -A lsdc -- ${char1} ${result} ${year}${nmonth}${day}${time} ${name} ${link}
  423. return
  424. }
  425.  
  426.  
  427. #
  428. # Function to process a remote file
  429. # We will not overwrite and existing file unless we in "freshen" mode.
  430. # And unless we are in "freshen" mode, it is an error for a file to
  431. # pre-exist.
  432.  
  433. process_remote_file() {
  434. VMESS="${VMESS} is a remote file that"
  435. do_get=0
  436. if [[ -f $name ]] ; then
  437. VMESS="${VMESS} already exists"
  438. if ((OPT_FRESHEN)) ; then
  439. line2=$(ls -ld "$name")
  440. lsdcode "$line2"
  441. char12=${lsdc[0]}
  442. mode2=${lsdc[1]}
  443. datestamp2=${lsdc[2]}
  444. name2=${lsdc[3]}
  445. link2=${lsdc[4]}
  446. if [[ $datestamp > $datestamp2 ]] ; then
  447. VMESS="${VMESS} but is out-of-date and"
  448. do_get=1
  449. else
  450. VMESS="${VMESS} and is current"
  451. fi
  452. else
  453. VMESS="${VMESS} and cannot be retrieved"
  454. echo WARNING: no get since $name exists in ${localpath} >&2
  455. fi
  456. else
  457. do_get=1
  458. fi
  459. if ((do_get)) ; then
  460. print -p get \""$name"\"
  461. waitfor $name
  462. VMESS="${VMESS} has been retrieved"
  463. if ((OPT_MODE)) ; then
  464. chmod $mode "$name"
  465. fi
  466. fi
  467. if (($OPT_VERBOSE)) ; then
  468. echo "$VMESS"
  469. fi
  470. return 0
  471. }
  472.  
  473.  
  474. # Function to process a remote directory
  475. # To this function, a remote directory is just an object that
  476. # may need to be duplicated in the current directory
  477.  
  478. process_remote_directory() {
  479.  
  480. VMESS="${VMESS} is a remote directory that"
  481. if ((OPT_DIRECTORIES)) ; then
  482. if exists $name ; then
  483. if [[ ! -d $name ]] ; then
  484. VMESS="${VMESS} cannot be created due to pre-existing object"
  485. echo WARNING: no mkdir since $name exists in ${localpath} >&2
  486. else
  487. VMESS="${VMESS} already exists"
  488. fi
  489. else
  490. mkdir "$name"
  491. VMESS="${VMESS} has been created locally"
  492. if ((OPT_MODE)) ; then
  493. chmod $mode "$name"
  494. fi
  495. fi
  496. else
  497. VMESS="${VMESS} has been ignored"
  498. fi
  499. if (($OPT_VERBOSE)) ; then
  500. echo "$VMESS"
  501. fi
  502. if ((OPT_RECURS)) ; then
  503. if [[ -d "$name" ]] ; then
  504. cd "$name"
  505. print -p lcd \""$name"\"
  506. exec 4<&-
  507. obtain_and_process_remote_ls "$name"
  508. print -p cd ..
  509. print -p lcd ..
  510. cd ..
  511. exec 4< ${DIR_FILE_NAME[LEV]}
  512. lineno=0
  513. while (( lineno != ${DIR_LINE_NUM[LEV]})) ; do
  514. read -u4 junk
  515. ((lineno=lineno+1))
  516. done
  517. fi
  518. fi
  519. return 0
  520. }
  521.  
  522.  
  523. #
  524. # Function to process a remote symlink
  525. # Note that we deal with th symlink only -- not
  526. # the object (if any) that the link points to.
  527.  
  528. process_remote_symlink() {
  529. VMESS="${VMESS} is a remote symlink that"
  530. if ((OPT_SYMLINKS)) ; then
  531. if exists "$name" ; then
  532. if [[ ! -L $name ]] ; then
  533. VMESS="${VMESS} cannot be created due to pre-existing object"
  534. echo WARNING: no symlink since $name exists in ${localpath} >&2
  535. else
  536. VMESS="${VMESS} already exists"
  537. fi
  538. else
  539. ln -s "$link" "$name"
  540. VMESS="${VMESS} has been duplicated locally"
  541. fi
  542. else
  543. VMESS="${VMESS} has been ignored"
  544. fi
  545. if (($OPT_VERBOSE)) ; then
  546. echo "$VMESS"
  547. fi
  548. }
  549.  
  550.  
  551. #
  552. # If a remote object is not a file, directory, or
  553. # symlink, we come here.
  554.  
  555. process_remote_weirdo() {
  556. VMESS="${VMESS} is a remote unknown object that has been ignored"
  557. return 0
  558. }
  559.  
  560. #
  561. # This function obtains an "ls" listing from the remote ftp system. Then it
  562. # scans the listing line by line to figure out what to do. It will completely
  563. # process the current directory.
  564.  
  565. obtain_and_process_remote_ls() {
  566.  
  567. typeset rdir tmpfile okfile ## local scope variables ##
  568. rdir=$1
  569.  
  570. #
  571. # Set up variables or modify them if we have recursed
  572.  
  573. ((LEV=LEV+1))
  574. tmpfile=/tmp/HardFeed.tp.$$.${LEV}
  575. okfile=/tmp/HardFeed.ok.$$.${LEV}
  576. if ((LEV == 1)) ; then
  577. localpath=$STARTPATH
  578. remotepath=$rdir
  579. else
  580. localpath=${localpath}/$rdir
  581. remotepath=${remotepath}/$rdir
  582.  
  583. fi
  584.  
  585. #
  586. # Get a copy of the remote dir output in a local file
  587. # called $tmpfile
  588.  
  589. print -p cd \""$rdir"\"
  590. print -p $OPT_DIRCMD $tmpfile
  591. print -p $OPT_DIRCMD $okfile
  592. waitfor $okfile
  593. DIR_FILE_NAME[LEV]=$tmpfile
  594. DIR_LINE_NUM[LEV]=0
  595. exec 4< $tmpfile
  596.  
  597. #
  598. # process each line
  599. #
  600.  
  601. while read -u4 line ; do
  602. ((DIR_LINE_NUM[LEV]=${DIR_LINE_NUM[LEV]}+1))
  603. lsdcode "$line"
  604. char1=${lsdc[0]}
  605. mode=${lsdc[1]}
  606. datestamp=${lsdc[2]}
  607. name=${lsdc[3]}
  608. link=${lsdc[4]}
  609. VMESS="${remotepath}/${name}"
  610. case $char1 in
  611. skip) ;;
  612. -) process_remote_file
  613. ;;
  614. d) process_remote_directory
  615. ;;
  616. l) process_remote_symlink
  617. ;;
  618. *) process_remote_weirdo
  619. ;;
  620. esac
  621. done
  622.  
  623. #
  624. # We may have recursed...so we must put everything back the way
  625. # we found it
  626.  
  627. localpath=${localpath%$rdir}
  628. localpath=${localpath%/}
  629. remotepath=${remotepath%$rdir}
  630. remotepath=${remotepath%/}
  631. rm $tmpfile
  632. rm $okfile
  633. ((LEV=LEV-1))
  634.  
  635. return 0
  636. }
  637.  
  638.  
  639. #
  640. # Main Program
  641. #
  642.  
  643.  
  644. ftp -inv >&3 2>&1 |&
  645. print -p open $SYSTEM
  646. print -p user $USER $PASSWORD
  647. print -p binary
  648.  
  649. i=0
  650. while ((OPT_CMDS>i)) ; do
  651. print -p ${OPT_CMDS_LIST[i]}
  652. ((i=i+1))
  653. done
  654.  
  655. obtain_and_process_remote_ls $DIRECTORY
  656.  
  657. print -p bye
  658. wait
  659. exit 0
Add Comment
Please, Sign In to add comment