PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i5h-3LL

HOME


5h-3LL 1.0
DIR: /usr/sbin
/usr/sbin/
Upload File:
Current File : /usr/sbin/dirvish
#!/usr/bin/perl

$CONFDIR = "/etc/dirvish";



#       $Id: dirvish.pl,v 12.0 2004/02/25 02:42:15 jw Exp $  $Name: Dirvish-1_2 $

$VERSION = ('$Name: Dirvish-1_2 $' =~ /Dirvish/i)
	? ('$Name: Dirvish-1_2 $' =~ m/^.*:\s+dirvish-(.*)\s*\$$/i)[0]
	: '1.1.2 patch' . ('$Id: dirvish.pl,v 12.0 2004/02/25 02:42:15 jw Exp $'
		=~ m/^.*,v(.*:\d\d)\s.*$/)[0];
$VERSION =~ s/_/./g;

#########################################################################
#                                                         		#
#	Copyright 2002 and $Date: 2004/02/25 02:42:15 $
#                         Pegasystems Technologies and J.W. Schultz 	#
#                                                         		#
#	Licensed under the Open Software License version 2.0		#
#                                                         		#
#	This program is free software; you can redistribute it		#
#	and/or modify it under the terms of the Open Software		#
#	License, version 2.0 by Lauwrence E. Rosen.			#
#                                                         		#
#	This program is distributed in the hope that it will be		#
#	useful, but WITHOUT ANY WARRANTY; without even the implied	#
#	warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR		#
#	PURPOSE.  See the Open Software License for details.		#
#                                                         		#
#########################################################################



#########################################################
#		EXIT CODES
#
#	0       success
#	1-19    warnings
#	20-39   finalization error
#	40-49   post-* error
#	50-59   post-client error code % 10forwarded
#	60-69   post-server error code % 10forwarded
#	70-79   pre-* error
#	80-89   pre-server error code % 10 forwarded
#	90-99   pre-client error code % 10 forwarded
#	100-149 non-fatal error
#	150-199 fatal error
#	200-219 loadconfig error.
#	220-254 configuration error
#	255	usage error


use POSIX qw(strftime);
use Getopt::Long;
use Time::ParseDate;
use Time::Period;

@rsyncargs = qw(-vrltH --delete);

%RSYNC_CODES = (
	  0 => [ 'success',	"No errors" ],
	  1 => [ 'fatal',	"syntax or usage error" ],
	  2 => [ 'fatal',	"protocol incompatibility" ],
	  3 => [ 'fatal',	"errors selecting input/output files, dirs" ],
	  4 => [ 'fatal',	"requested action not supported" ],
	  5 => [ 'fatal',	"error starting client-server protocol" ],

	 10 => [ 'error',	"error in socket IO" ],
	 11 => [ 'error',	"error in file IO" ],
	 12 => [ 'check',	"error in rsync protocol data stream" ],
	 13 => [ 'check',	"errors with program diagnostics" ],
	 14 => [ 'error',	"error in IPC code" ],

	 20 => [ 'error',	"status returned when sent SIGUSR1, SIGINT" ],
	 21 => [ 'error',	"some error returned by waitpid()" ],
	 22 => [ 'error',	"error allocating core memory buffers" ],
	 23 => [ 'error',	"partial transfer" ],
#KHL 2005/02/18:  rsync code 24 changed from 'error' to 'warning'
	 24 => [ 'warning',	"file vanished on sender" ],

	 30 => [ 'error',	"timeout in data send/receive" ],

	124 => [ 'fatal',	"remote shell failed" ],
	125 => [ 'error',	"remote shell killed" ],
	126 => [ 'fatal',	"command could not be run" ],
	127 => [ 'fatal',	"command not found" ],
);

@BOOLEAN_FIELDS = qw(
	permissions
	checksum
	devices
	init
	numeric-ids
	sparse
	stats
	whole-file
	xdev
	zxfer
);

%RSYNC_OPT = (		# simple options
	permissions	=> '-pgo',
	devices		=> '-D',
	sparse		=> '-S',
	checksum	=> '-c',
	'whole-file'	=> '-W',
	xdev		=> '-x',
	zxfer		=> '-z',
	stats		=> '--stats',
	'numeric-ids'	=> '--numeric-ids',
);

%RSYNC_POPT = (		# parametered options
	'password-file'		=> '--password-file',
	'rsync-client'		=> '--rsync-path',
);

sub errorscan;
sub logappend;
sub scriptrun;
sub seppuku;

sub usage
{
	my $message = shift(@_);

	length($message) and print STDERR $message, "\n\n";

	$! and exit(255); # because getopt seems to send us here for death

	print STDERR <<EOUSAGE;
USAGE
	dirvish --vault vault OPTIONS [ file_list ]
	
OPTIONS
	--image image_name
	--config configfile
	--branch branch_name
	--reference branch_name|image_name
	--expire expire_date
	--init
	--reset option
	--summary short|long
	--no-run
EOUSAGE

	exit 255;
}

$Options = { 
	'Command-Args'	=> join(' ', @ARGV),
	'numeric-ids'	=> 1,
	'devices'	=> 1,
	permissions	=> 1,
	'stats'		=> 1,
	exclude		=> [ ],
	'expire-rule'	=> [ ],
	'rsync-option'	=> [ ],
	bank		=> [ ],
	'image-default'	=> '%Y%m%d%H%M%S',
	rsh		=> 'ssh',
	summary		=> 'short',
	config		=>
		sub {
			loadconfig('f', $_[1], $Options);
		},
	client		=>
		sub {
			$$Options{$_[0]} = $_[1];
			loadconfig('fog', "$CONFDIR/$_[1]", $Options);
		},
	branch		=>
		sub {
			if ($_[1] =~ /:/)
			{
				($$Options{vault}, $$Options{branch})
					= split(/:/, $_[1]);
			} else {
				$$Options{$_[0]} = $_[1];
			}
			loadconfig('f', "$$Options{branch}", $Options);
		},
	vault		=>
		sub {
			if ($_[1] =~ /:/)
			{
				($$Options{vault}, $$Options{branch})
					= split(/:/, $_[1]);
				loadconfig('f', "$$Options{branch}", $Options);
			} else {
				$$Options{$_[0]} = $_[1];
				loadconfig('f', 'default.conf', $Options);
			}
		},
	reset		=>
		sub {
			$$Options{$_[1]} = ref($$Options{$_[1]}) eq 'ARRAY'
				? [ ]
				: undef;
		},
	version		=> sub {
			print STDERR "dirvish version $VERSION\n";
			exit(0);
		},
	help		=> \&usage,
};

if ($CONFDIR =~ /dirvish$/ && -f "$CONFDIR.conf")
{
	loadconfig('f', "$CONFDIR.conf", $Options);
}
elsif (-f "$CONFDIR/master.conf")
{
	loadconfig('f', "$CONFDIR/master.conf", $Options);
}
elsif (-f "$CONFDIR/dirvish.conf")
{
	seppuku 250, <<EOERR;
ERROR: no master configuration file.
	An old $CONFDIR/dirvish.conf file found.
	Please read the dirvish release notes.
EOERR
}
else
{
	seppuku 251, "ERROR: no master configuration file";
}

GetOptions($Options, qw(
	config=s
	vault=s
	client=s
	tree=s
	image=s
	image-time=s
	expire=s
	branch=s
	reference=s
	exclude=s@
	sparse!
	zxfer!
	checksum!
	whole-file!
	xdev!
	speed-limit=s
	file-exclude|fexclude=s
	reset=s
	index=s
	init!
	summary=s
	no-run|dry-run
	help|?
	version
	)) or usage;

chomp($$Options{Server} = `hostname`);

if ($$Options{image})
{
	$image = $$Options{Image} = $$Options{image};
}
elsif ($$Options{'image-temp'})
{
	$image = $$Options{'image-temp'};
	$$Options{Image} = $$Options{'image-default'};
}
else
{
	$image = $$Options{Image} = $$Options{'image-default'};
}

$$Options{branch} =~ /:/
	and ($$Options{vault}, $$Options{branch})
		= split(/:/, $Options{branch});
$$Options{vault} =~ /:/
	and ($$Options{vault}, $$Options{branch})
		= split(/:/, $Options{vault});

for $key (qw(vault Image client tree))
{
	length($$Options{$key}) or usage("$key undefined");
	ref($$Options{$key}) eq 'CODE' and usage("$key undefined");
}

if(!$$Options{Bank})
{
	my $bank;
	for $bank (@{$$Options{bank}})
	{
		if (-d "$bank/$$Options{vault}")
		{
			$$Options{Bank} = $bank;
			last;
		}
	}
	$$Options{Bank} or seppuku 220, "ERROR: cannot find vault $$Options{vault}";
}
$vault = join('/', $$Options{Bank}, $$Options{vault});
-d $vault or seppuku 221, "ERROR: cannot find vault $$Options{vault}";

my $now = time;

if ($$Options{'image-time'})
{
	my $n = $now;

	$now = parsedate($$Options{'image-time'},
		DATE_REQUIRED => 1, NOW => $n);
	if (!$now)
	{
		$now = parsedate($$Options{'image-time'}, NOW => $n);
		$now > $n && $$Options{'image-time'} !~ /\+/ and $now -= 24*60*60;
	}
	$now or seppuku 222, "ERROR: image-time unparseable: $$Options{'image-time'}";
}
$$Options{'Image-now'} = strftime('%Y-%m-%d %H:%M:%S', localtime($now));

$$Options{Image} =~ /%/
	and $$Options{Image} = strftime($$Options{Image}, localtime($now));
$image =~ /%/
	and $image = strftime($image, localtime($now));

!$$Options{branch} || ref($$Options{branch})
	and $$Options{branch} = $$Options{'branch-default'} || 'default';

$seppuku_prefix = join(':', $$Options{vault}, $$Options{branch}, $image);

if (-d "$vault/$$Options{'image-temp'}" && $image eq $$Options{'image-temp'})
{
	my $iinfo;
	$iinfo = loadconfig('R', "$vault/$image/summary");
	$$iinfo{Image} or seppuku 223, "cannot cope with existing $image";
	if ($$Options{'no-run'})
	{
		print "ACTION: rename $vault/$image $vault/$$iinfo{Image}\n\n";
		$have_temp = 1;
	} else {
		rename ("$vault/$image", "$vault/$$iinfo{Image}");
	}
}

-d "$vault/$$Options{Image}" and seppuku 224, "ERROR: image $$Options{Image} already exists in $vault";
-d "$vault/$image" && !$have_temp and seppuku 225, "ERROR: image $image already exists in $vault";

$$Options{Reference} = $$Options{reference} || $$Options{branch};
if (!$$Options{init} && -f "$vault/dirvish/$$Options{Reference}.hist")
{
	my (@images, $i, $s);
	open(IMAGES, "$vault/dirvish/$$Options{Reference}.hist");
	@images = <IMAGES>;
	close IMAGES;
	while ($i = pop(@images))
	{
		$i =~ s/\s.*$//s;
		-d "$vault/$i/tree" or next;

		$$Options{Reference} = $i;
		last;
	}
}
$$Options{init} || -d "$vault/$$Options{Reference}"
	or seppuku 227, "ERROR: no images for branch $$Options{branch} found";

if(!$$Options{expire} && $$Options{expire} !~ /never/i
	&& scalar(@{$$Options{'expire-rule'}}))
{
	my ($rule, $p, $t, $e);
	my @cron;
	my @pnames = qw(min hr md mo wd);

	for $rule (reverse(@{$$Options{'expire-rule'}}))
	{
		if ($rule =~ /\{.*\}/)
		{
			($p, $e) = $rule =~ m/^(.*\175)\s*([^\175]*)$/;
		} else {
			@cron = split(/\s+/, $rule, 6);
			$e = $cron[5] || '';
			$p = '';
			for ($t = 0; $t < @pnames; $t++)
			{
				$cron[$t] eq '*' and next;
				($p .= "$pnames[$t] { $cron[$t] } ")
				=~ tr/,/ /;
			}
		}
		if (!$p)
		{
			$$Options{'Expire-rule'} = $rule;
			$$Options{Expire} = $e;
			last;
		}
		$t = inPeriod($now, $p);
		if ($t == 1)
		{
			$e ||= 'Never';
			$$Options{'Expire-rule'} = $rule;
			$$Options{Expire} = $e;
			last;
		}
		$t == -1 and printf STDERR "WARNING: invalid expire rule %s\n", $rule;
		next;
	}
} else {
	$$Options{Expire} = $$Options{expire};
}

$$Options{Expire} ||= $$Options{'expire-default'};

if ($$Options{Expire} && $$Options{Expire} !~ /Never/i)
{
	$$Options{Expire} .= strftime(' == %Y-%m-%d %H:%M:%S',
		localtime(parsedate($$Options{Expire}, NOW => $now)));
} else {
	$$Options{Expire} = 'Never';
}

#+SIS: KHL 2005-02-18  SpacesInSource fix
#-SIS: ($srctree, $aliastree) = split(/\s+/, $$Options{tree})
($srctree, $aliastree) = split(/[^\\]\s+/, $$Options{tree})
	or seppuku 228, "ERROR: no source tree defined";
$srctree =~ s(\\ )( )g;                     #+SIS
$srctree =~ s(/+$)();
$aliastree =~ s(/+$)();
$aliastree ||= $srctree;

$destree = join("/", $vault, $image, 'tree');
$reftree = join('/', $vault, $$Options{Reference}, 'tree');
$err_temp = join("/", $vault, $image, 'rsync_error.tmp');
$err_file = join("/", $vault, $image, 'rsync_error');
$log_file = join("/", $vault, $image, 'log');
$log_temp = join("/", $vault, $image, 'log.tmp');
$exl_file = join("/", $vault, $image, 'exclude');
$fsb_file = join("/", $vault, $image, 'fsbuffer');

while (($k, $v) = each %RSYNC_OPT)
{
	$$Options{$k} and push @rsyncargs, $v;
}

while (($k, $v) = each %RSYNC_POPT)
{
	$$Options{$k} and push @rsyncargs, $v . '=' . $$Options{$k};
}

$$Options{'speed-limit'}
	and push @rsyncargs, '--bwlimit=' . $$Options{'speed-limit'} * 100;

scalar @{$$Options{'rsync-option'}}
	and push @rsyncargs, @{$$Options{'rsync-option'}};

scalar @{$$Options{exclude}}
	and push @rsyncargs, '--exclude-from=' . $exl_file;

if (!$$Options{'no-run'})
{
	mkdir "$vault/$image", 0700
		or seppuku 230, "mkdir $vault/$image failed";
	mkdir $destree, 0755;

	open(SUMMARY, ">$vault/$image/summary")
		or seppuku 231, "cannot create $vault/$image/summary"; 
} else {
	open(SUMMARY, ">-");
}

$Set = $Unset = '';
for (@BOOLEAN_FIELDS)
{
	$$Options{$_}
		and $Set .= $_ . ' '
		or $Unset .= $_ . ' ';
}

@summary_fields = qw(
	client tree rsh
	Server Bank vault branch
       	Image image-temp Reference
	Image-now Expire Expire-rule
	exclude
	rsync-option
	Enabled
);
$summary_reset = 0;
for $key (@summary_fields, 'RESET', sort(keys(%$Options)))
{
	if ($key eq 'RESET')
       	{
		$summary_reset++;
		$Set and print SUMMARY "SET $Set\n";
		$Unset and print SUMMARY "UNSET $Unset\n";
		print SUMMARY "\n";
		$$Options{summary} ne 'long' && !$$Options{'no-run'} and last;
		next;
	}
	grep(/^$key$/, @BOOLEAN_FIELDS) and next;
	$summary_reset && grep(/^$key$/, @summary_fields) and next;

	$val = $$Options{$key};
	if(ref($val) eq 'ARRAY')
	{
		my $v;
		scalar(@$val) or next;
		print SUMMARY "$key:\n";
		for $v (@$val)
		{
			printf SUMMARY "\t%s\n", $v;
		}
	}
	ref($val) and next;
	$val or next;
	printf SUMMARY "%s: %s\n", $key, $val;
}

$$Options{init} or push @rsyncargs, "--link-dest=$reftree";

$rclient = undef;
$$Options{client} ne $$Options{Server}
	and $rclient = $$Options{client} . ':';

$ENV{RSYNC_RSH} = $$Options{rsh};

@cmd = (
	($$Options{rsync} ? $$Options{rsync} : 'rsync'),
	@rsyncargs,
	$rclient . $srctree . '/',
	$destree
	);
printf SUMMARY "\n%s: %s\n", 'ACTION', join (' ', @cmd);

$$Options{'no-run'} and exit 0;

printf SUMMARY "%s: %s\n", 'Backup-begin', strftime('%Y-%m-%d %H:%M:%S', localtime);

$env_srctree = $srctree;		#+SIS:
$env_srctree =~ s/ /\\ /g;		#+SIS:

$WRAPPER_ENV = sprintf (" %s=%s" x 5,
	'DIRVISH_SERVER', $$Options{Server},
	'DIRVISH_CLIENT', $$Options{client},
#-SIS:	'DIRVISH_SRC', $srctree,
	'DIRVISH_SRC', $env_srctree,	#+SIS:
	'DIRVISH_DEST', $destree,
	'DIRVISH_IMAGE', join(':',
		$$Options{vault},
		$$Options{branch},
		$$Options{Image}),
);

if(scalar @{$$Options{exclude}})
{
	open(EXCLUDE, ">$exl_file");
	for (@{$$Options{exclude}})
	{
		print EXCLUDE $_, "\n";
	}	
	close(EXCLUDE);
	$ENV{DIRVISH_EXCLUDE} = $exl_file;
}

if ($$Options{'pre-server'})
{
	$status{'pre-server'} = scriptrun(
		lable	=> 'Pre-Server',
		cmd	=> $$Options{'pre-server'},
		now	=> $now,
		log	=> $log_file,
		dir	=> $destree,
		env	=> $WRAPPER_ENV,
	);

	if ($status{'pre-server'})
	{
		my $s = $status{'pre-server'} >> 8;
		printf SUMMARY "pre-server failed (%d)\n", $s;
		printf STDERR "%s:%s pre-server failed (%d)\n",
			$$Options{vault}, $$Options{branch},
			$s;
		exit 80 + ($s % 10);
	}
}

if ($$Options{'pre-client'})
{
	$status{'pre-client'} = scriptrun(
		lable	=> 'Pre-Client',
		cmd	=> $$Options{'pre-client'},
		now	=> $now,
		log	=> $log_file,
		dir	=> $srctree,
		env	=> $WRAPPER_ENV,
		shell	=> (($$Options{client} eq $$Options{Server})
			?  undef
			: "$$Options{rsh} $$Options{client}"),
	);
	if ($status{'pre-client'})
	{
		my $s = $status{'pre-client'};
		printf SUMMARY "pre-client failed (%d)\n", $s;
		printf STDERR "%s:%s pre-client failed (%d)\n",
			$$Options{vault}, $$Options{branch},
			$s;

		($$Options{'pre-server'}) && scriptrun(
			lable	=> 'Post-Server',
			cmd	=> $$Options{'post-server'},
			now	=> $now,
			log	=> $log_file,
			dir	=> $destree,
			env	=> $WRAPPER_ENV . ' DIRVISH_STATUS=fail',
		);
		exit 90 + ($s % 10);
	}
}

# create a buffer to allow logging to work after full fileystem
open (FSBUF, ">$fsb_file");
print FSBUF "         \n" x 6553;
close FSBUF;

for ($runloops = 0; $runloops < 5; ++$runloops)
{
	logappend($log_file, sprintf("\n%s: %s\n", 'ACTION', join(' ', @cmd)));

		# create error file and connect rsync STDERR to it.
		# preallocate 64KB so there will be space if rsync
		# fills the filesystem.
	open (INHOLD, "<&STDIN");
	open (ERRHOLD, ">&STDERR");
	open (STDERR, ">$err_temp");
	print STDERR "         \n" x 6553;
	seek STDERR, 0, 0;

	open (OUTHOLD, ">&STDOUT");
	open (STDOUT, ">$log_temp");

	$status{code} = (system(@cmd) >> 8) & 255;

	open (STDERR, ">&ERRHOLD");
	open (STDOUT, ">&OUTHOLD");
	open (STDIN, "<&INHOLD");

	open (LOG_FILE, ">>$log_file");
	open (LOG_TEMP, "<$log_temp");
	while (<LOG_TEMP>)
	{
		chomp;
		m(/$) and next;
		m( [-=]> ) and next;
		print LOG_FILE $_, "\n";
	}
	close (LOG_TEMP);
	close (LOG_FILE);
	unlink $log_temp;

	$status{code} and errorscan(\%status, $err_file, $err_temp);

	$status{warning} || $status{error}
       		and logappend($log_file, sprintf(
			"RESULTS: warnings = %d, errors = %d",
			$status{warning}, $status{error}
			)
		);
	if ($RSYNC_CODES{$status{code}}[0] eq 'check')
	{
		$status{fatal} and last;
		$status{error} or last;
	} else {
		$RSYNC_CODES{$status{code}}[0] eq 'fatal' and last;
		$RSYNC_CODES{$status{code}}[0] eq 'error' or last;
	}
}

scalar @{$$Options{exclude}} && unlink $exl_file;
-f $fsb_file and unlink $fsb_file;

if ($status{code})
{
	if ($RSYNC_CODES{$status{code}}[0] eq 'check')
	{
		if ($status{fatal})		{ $Status = 'fatal'; }
		elsif ($status{error})		{ $Status = 'error'; }
		elsif ($status{warning})	{ $Status = 'warning'; }
		$Status_msg = sprintf "%s (%d) -- %s",
			($Status eq 'fatal' ? 'fatal error' : $Status),
			$status{code},
			$status{message}{$Status};
	} elsif ($RSYNC_CODES{$status{code}}[0] eq 'fatal')
	{
		$Status_msg = sprintf "fatal error (%d) -- %s",
			$status{code},
			$RSYNC_CODES{$status{code}}[1];
	}

	if (!$Status_msg)
	{
		$RSYNC_CODES{$status{code}}[0] eq 'fatal' and $Status = 'fatal';
		$RSYNC_CODES{$status{code}}[0] eq 'error' and $Status = 'error';
		$RSYNC_CODES{$status{code}}[0] eq 'warning' and	$Status = 'warning';
		$RSYNC_CODES{$status{code}}[0] eq 'check' and $Status = 'unknown';
		exists $RSYNC_CODES{$status{code}} or $Status = 'unknown';
		$Status_msg = sprintf "%s (%d) -- %s",
			($Status eq 'fatal' ? 'fatal error' : $Status),
			$status{code},
			$RSYNC_CODES{$status{code}}[1];
	}
	if ($Status eq 'fatal' || $Status eq 'error' || $status eq 'unknown')
	{
		printf STDERR "dirvish %s:%s %s\n",
			$$Options{vault}, $$Options{branch},
			$Status_msg;
	}
} else {
	$Status = $Status_msg = 'success';
}
$WRAPPER_ENV .= ' DIRVISH_STATUS=' .  $Status;

if ($$Options{'post-client'})
{
	$status{'post-client'} = scriptrun(
		lable	=> 'Post-Client',
		cmd	=> $$Options{'post-client'},
		now	=> $now,
		log	=> $log_file,
		dir	=> $srctree,
		env	=> $WRAPPER_ENV,
		shell	=> (($$Options{client} eq $$Options{Server})
			?  undef
			: "$$Options{rsh} $$Options{client}"),
	);
	if ($status{'post-client'})
	{
		my $s = $status{'post-client'} >> 8;
		printf SUMMARY "post-client failed (%d)\n", $s;
		printf STDERR "%s:%s post-client failed (%d)\n",
			$$Options{vault}, $$Options{branch},
			$s;
	}
}

if ($$Options{'post-server'})
{
	$status{'post-server'} = scriptrun(
		lable	=> 'Post-Server',
		cmd	=> $$Options{'post-server'},
		now	=> $now,
		log	=> $log_file,
		dir	=> $destree,
		env	=> $WRAPPER_ENV,
	);
	if ($status{'post-server'})
	{
		my $s = $status{'post-server'} >> 8;
		printf SUMMARY "post-server failed (%d)\n", $s;
		printf STDERR "%s:%s post-server failed (%d)\n",
			$$Options{vault}, $$Options{branch},
			$s;
	}
}

if($status{fatal})
{
	system ("rm -rf $destree");
	unlink $err_temp;
	printf SUMMARY "%s: %s\n", 'Status', $Status_msg;
	exit 199;
} else {
	unlink $err_temp;
	-z $err_file and unlink $err_file;
}

printf SUMMARY "%s: %s\n",
	'Backup-complete', strftime('%Y-%m-%d %H:%M:%S', localtime);

printf SUMMARY "%s: %s\n", 'Status', $Status_msg;

# We assume warning and unknown produce useful results
$Status eq 'warning' || $Status eq 'unknown' and $Status = 'success';

if ($Status eq 'success')
{
	-s "$vault/dirvish/$$Options{branch}.hist" or $newhist = 1;
	if (open(HIST, ">>$vault/dirvish/$$Options{branch}.hist"))
	{
		$newhist == 1 and printf HIST ("#%s\t%s\t%s\t%s\n",
				qw(IMAGE CREATED REFERECE EXPIRES));
		printf HIST ("%s\t%s\t%s\t%s\n",
				$$Options{Image},
				strftime('%Y-%m-%d %H:%M:%S', localtime),
				$$Options{Reference} || '-',
				$$Options{Expire}
			);
		close (HIST);
	}
} else {
	printf STDERR "dirvish error: branch %s:%s image %s failed\n",
		$vault, $$Options{branch}, $$Options{Image};
}

length($$Options{'meta-perm'})
	and chmod oct($$Options{'meta-perm'}),
		"$vault/$image/summary",
		"$vault/$image/rsync_error",
		"$vault/$image/log";

$Status eq 'success' or exit 149;

$$Options{log} =~ /.*(gzip)|(bzip2)/
	and system "$$Options{log} $vault/$image/log";

if ($$Options{index} && $$Options{index} !~/^no/i)
{
	
	open(INDEX, ">$vault/$image/index");
	open(FIND, "find $destree -ls|") or seppuku 21, "dirvish $vault:$image cannot build index";
	while (<FIND>)
	{
		s/ $destree\// $aliastree\//g;
		print INDEX $_ or seppuku 22, "dirvish $vault:$image error writing index";
	}
	close FIND;
	close INDEX;

	length($$Options{'meta-perm'})
		and chmod oct($$Options{'meta-perm'}), "$vault/$image/index";
	$$Options{index} =~ /.*(gzip)|(bzip2)/
		and system "$$Options{index} $vault/$image/index";
}

chmod oct($$Options{'image-perm'}) || 0755, "$vault/$image";

exit 0;

sub errorscan
{
	my ($status, $err_file, $err_temp) = @_;
	my $err_this_loop = 0;
	my ($action, $pattern, $severity, $message);
	my @erraction = (
		[ 'fatal',	'^ssh:.*nection refused',		],
		[ 'fatal',	'^\S*sh: .* No such file',		],
		[ 'fatal',	'^ssh:.*No route to host',		],
		[ 'error',	'^file has vanished: ',			],
		[ 'warning',	'readlink .*: no such file or directory', ],

		[ 'fatal',	'failed to write \d+ bytes:',
			'write error, filesystem probably full'		],
		[ 'fatal',	'write failed',
			'write error, filesystem probably full'		],
		[ 'error',	'error: partial transfer',
			'partial transfer'				],
		[ 'error',	'error writing .* exiting: Broken pipe',
			'broken pipe'					],
	);

	open (ERR_FILE, ">>$err_file");
	open (ERR_TEMP, "<$err_temp");
	while (<ERR_TEMP>)
	{
		chomp;
		s/\s+$//;
		length or next;
		if (!$err_this_loop)
		{
			printf ERR_FILE "\n\n*** Execution cycle %d ***\n\n",
				$runloops;
			$err_this_loop++
		}
		print ERR_FILE $_, "\n";

		$$status{code} or next;
		
		for $action (@erraction)
		{
			($severity, $pattern, $message) = @$action;
			/$pattern/ or next;

			++$$status{$severity};
			$msg = $message || $_;
			$$status{message}{$severity} ||= $msg;
			logappend($log_file, $msg);
			$severity eq 'fatal'
				and printf STDERR "dirvish %s:%s fatal error: %s\n",
					$$Options{vault}, $$Options{branch},
					$msg;
			last;
		}
		if (/No space left on device/)
		{
			$msg = 'filesystem full';
			$$status{message}{fatal} eq $msg and next;

			-f $fsb_file and unlink $fsb_file;
			++$$status{fatal};
			$$status{message}{fatal} = $msg;
			logappend($log_file, $msg);
			printf STDERR "dirvish %s:%s fatal error: %s\n",
				$$Options{vault}, $$Options{branch},
				$msg;
		}
		if (/error: error in rsync protocol data stream/)
		{
			++$$status{error};
			$msg = $message || $_;
			$$status{message}{error} ||= $msg;
			logappend($log_file, $msg);
		}
	}
	close ERR_TEMP;
	close ERR_FILE;
}

sub logappend
{
	my ($file, @messages) = @_;
	my $message;

	open (LOGFILE, '>>' . $file) or seppuku 20, "cannot open log file $file";
	for $message (@messages)
	{
		print LOGFILE $message, "\n";
	}
	close LOGFILE;
}

sub scriptrun
{
	my (%A) = @_;
	my ($cmd, $rcmd, $return);

	$A{now} ||= time;
	$A{log} or seppuku 229, "must specify logfile for scriptrun()";
	ref($A{cmd}) and seppuku 232, "$A{lable} option specification error";

	$cmd = strftime($A{cmd}, localtime($A{now}));

#KHL 2005-02-18 BadShellCommandCWD:  fix inverted logic
#	if ($A{dir} =~ /^:/)
	if ($A{dir} !~ /^:/)
	{
		$rcmd = sprintf ("%s 'cd %s; %s %s' >>%s",
			("$A{shell}" || "/bin/sh -c"),
			$A{dir}, $A{env},
			$cmd,
			$A{log}
		);
	} else {
		$rcmd = sprintf ("%s '%s %s' >>%s",
			("$A{shell}" || "/bin/sh -c"),
			$A{env},
			$cmd,
			$A{log}
		);
	}

	$A{lable} =~ /^Post/ and logappend($A{log}, "\n");

	logappend($A{log}, "$A{lable}: $cmd");

	$return = system($rcmd);

	$A{lable} =~ /^Pre/ and logappend($A{log}, "\n");

	return $return;
}

#	Get patch level of loadconfig.pl in case exit codes
#	are needed.
#		$Id: loadconfig.pl,v 12.0 2004/02/25 02:42:15 jw Exp $


#########################################################################
#                                                         		#
#	Copyright 2002 and $Date: 2004/02/25 02:42:15 $
#                         Pegasystems Technologies and J.W. Schultz 	#
#                                                         		#
#	Licensed under the Open Software License version 2.0		#
#                                                         		#
#########################################################################

sub seppuku	# Exit with code and message.
{
	my ($status, $message) = @_;

	chomp $message;
	if ($message)
	{
		$seppuku_prefix and print STDERR $seppuku_prefix, ': ';
		print STDERR $message, "\n";
	}
	exit $status;
}

sub slurplist
{
	my ($key, $filename, $Options) = @_;
	my $f;
	my $array;

	$filename =~ m(^/) and $f = $filename;
	if (!$f && ref($$Options{vault}) ne 'CODE')
	{
		$f = join('/', $$Options{Bank}, $$Options{vault},
			'dirvish', $filename);
		-f $f or $f = undef;
	}
	$f or $f = "$CONFDIR/$filename";
	open(PATFILE, "<$f") or seppuku 229, "cannot open $filename for $key list";
	$array = $$Options{$key};
	while(<PATFILE>)
	{
		chomp;
		length or next;
		push @{$array}, $_;
	}
	close PATFILE;
}

#   loadconfig -- load configuration file
#   SYNOPSYS
#     	loadconfig($opts, $filename, \%data)
#
#   DESCRIPTION
#   	load and parse a configuration file into the data
#   	hash.  If the filename does not contain / it will be
#   	looked for in the vault if defined.  If the filename
#   	does not exist but filename.conf does that will
#   	be read.
#
#   OPTIONS
#	Options are case sensitive, upper case has the
#	opposite effect of lower case.  If conflicting
#	options are given only the last will have effect.
#
#   	f	Ignore fields in config file that are
#   		capitalized.
#   
#   	o	Config file is optional, return undef if missing.
#   
#   	R	Do not allow recoursion.
#
#   	g	Only load from global directory.
#
#	
#   
#   LIMITATIONS
#   	Only way to tell whether an option should be a list
#   	or scalar is by the formatting in the config file.
#   
#   	Options reqiring special handling have to have that
#   	hardcoded in the function.
#

sub loadconfig
{
	my ($mode, $configfile, $Options) = @_;
	my $confile = undef;
	my ($key, $val);
	my $CONFIG;
	ref($Options) or $Options = {};
	my %modes;
	my ($conf, $bank, $k);

	$modes{r} = 1;
	for $_ (split(//, $mode))
	{
		if (/[A-Z]/)
		{
			$_ =~ tr/A-Z/a-z/;
			$modes{$_} = 0;
		} else {
			$modes{$_} = 1;
		}
	}


	$CONFIG = 'CFILE' . scalar(@{$$Options{Configfiles}});

	$configfile =~ s/^.*\@//;

	if($configfile =~ m[/])
	{
		$confile = $configfile;
	}
	elsif($configfile ne '-')
	{
		if(!$modes{g} && $$Options{vault} && $$Options{vault} ne 'CODE')
		{
			if(!$$Options{Bank})
			{
				my $bank;
				for $bank (@{$$Options{bank}})
				{
					if (-d "$bank/$$Options{vault}")
					{
						$$Options{Bank} = $bank;
						last;
					}
				}
			}
			if ($$Options{Bank})
			{
				$confile = join('/', $$Options{Bank},
					$$Options{vault}, 'dirvish',
					$configfile);
				-f $confile || -f "$confile.conf"
					or $confile = undef;
			}
		}
		$confile ||= "$CONFDIR/$configfile";
	}

	if($configfile eq '-')
	{
		open($CONFIG, $configfile) or seppuku 221, "cannot open STDIN";
	} else {
		! -f $confile && -f "$confile.conf" and $confile .= '.conf';

		if (! -f "$confile")
		{
			$modes{o} and return undef;
			seppuku 222, "cannot open config file: $configfile";
		}

		grep(/^$confile$/, @{$$Options{Configfiles}})
			and seppuku 224, "ERROR: config file looping on $confile";

		open($CONFIG, $confile)
			or seppuku 225, "cannot open config file: $configfile";
	}
	push(@{$$Options{Configfiles}}, $confile);

	while(<$CONFIG>)
	{
		chomp;
		s/\s*#.*$//;
		s/\s+$//;
		/\S/ or next;
		
		if(/^\s/ && $key)
		{
			s/^\s*//;
			push @{$$Options{$key}}, $_;
		}
		elsif(/^SET\s+/)
		{
			s/^SET\s+//;
			for $k (split(/\s+/))
			{
				$$Options{$k} = 1;
			}
		}
		elsif(/^UNSET\s+/)
		{
			s/^UNSET\s+//;
			for $k (split(/\s+/))
			{
				$$Options{$k} = undef;
			}
		}
		elsif(/^RESET\s+/)
		{
			($key = $_) =~ s/^RESET\s+//;
			$$Options{$key} = [ ];
		}
		elsif(/^[A-Z]/ && $modes{f})
		{
			$key = undef;
		}
		elsif(/^\S+:/)
		{
			($key, $val) = split(/:\s*/, $_, 2);
			length($val) or next;
			$k = $key; $key = undef;

			if ($k eq 'config')
			{
				$modes{r} and loadconfig($mode . 'O', $val, $Options);
				next;
			}
			if ($k eq 'client')
			{
				if ($modes{r} && ref ($$Options{$k}) eq 'CODE')
				{
					loadconfig($mode .  'og', "$CONFDIR/$val", $Options);
				}
				$$Options{$k} = $val;
				next;
			}
			if ($k eq 'file-exclude')
			{
				$modes{r} or next;

				slurplist('exclude', $val, $Options);
				next;
			}
			if (ref ($$Options{$k}) eq 'ARRAY')
			{
				push @{$$Options{$k}}, $_;
			} else {
				$$Options{$k} = $val;
			}
		}
	}
	close $CONFIG;
	return $Options;
}