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ß&~'d3i WWWWWW
Current Directory: /opt/cpanel/ea-ruby24/src/passenger-release-6.0.20/src/agent/Shared/Fundamentals
Viewing File: /opt/cpanel/ea-ruby24/src/passenger-release-6.0.20/src/agent/Shared/Fundamentals/AbortHandler.cpp
/* * Phusion Passenger - https://www.phusionpassenger.com/ * Copyright (c) 2010-2017 Phusion Holding B.V. * * "Passenger", "Phusion Passenger" and "Union Station" are registered * trademarks of Phusion Holding B.V. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include <Shared/Fundamentals/AbortHandler.h> #include <boost/cstdint.hpp> #include <sys/types.h> #include <sys/stat.h> #include <sys/select.h> #include <cstdio> #include <cstdlib> #include <cstddef> #include <cstring> #include <cerrno> #include <cassert> #include <fcntl.h> #include <poll.h> #include <unistd.h> #include <signal.h> #include <libgen.h> #ifdef __linux__ #include <sys/syscall.h> #include <features.h> #endif #if defined(__APPLE__) || defined(__GNU_LIBRARY__) #define LIBC_HAS_BACKTRACE_FUNC #endif #ifdef LIBC_HAS_BACKTRACE_FUNC #include <execinfo.h> #endif #include <Shared/Fundamentals/AbortHandler.h> #include <Shared/Fundamentals/Utils.h> #include <Constants.h> #include <LoggingKit/LoggingKit.h> #include <LoggingKit/Context.h> #include <ResourceLocator.h> #include <RandomGenerator.h> #include <ProcessManagement/Utils.h> #include <Utils.h> #include <Utils/AsyncSignalSafeUtils.h> namespace Passenger { namespace Agent { namespace Fundamentals { using namespace std; namespace ASSU = AsyncSignalSafeUtils; #define RANDOM_TOKEN_SIZE 6 #define MAX_RANDOM_TOKENS 256 struct AbortHandlerContext { const AbortHandlerConfig *config; char *installSpec; char *rubyLibDir; char *tmpDir; char *crashWatchCommand; char *backtraceSanitizerCommand; bool backtraceSanitizerPassProgramInfo; /** * A string of RANDOM_TOKEN_SIZE * MAX_RANDOM_SIZES bytes. * Used by createCrashLogDir() to find a unique directory name. */ char *randomTokens; int emergencyPipe1[2]; int emergencyPipe2[2]; char *alternativeStack; volatile sig_atomic_t callCount; }; struct AbortHandlerWorkingState { pid_t pid; int signo; siginfo_t *info; char messagePrefix[32]; char messageBuf[1024]; char crashLogDir[256]; int crashLogDirFd; }; typedef void (*Callback)(AbortHandlerWorkingState &state, void *userData); #define IGNORE_SYSCALL_RESULT(code) \ do { \ int _ret = code; \ (void) _ret; \ } while (false) static AbortHandlerContext *ctx = NULL; static const char digits[] = "0123456789"; static const char hex_chars[] = "0123456789abcdef"; static void write_nowarn(int fd, const void *buf, size_t n) { ASSU::writeNoWarn(fd, buf, n); } static void printCrashLogFileCreated(AbortHandlerWorkingState &state, const char *fname) { const char *end = state.messageBuf + sizeof(state.messageBuf); char *pos = state.messageBuf; pos = ASSU::appendData(pos, end, "Dumping to "); pos = ASSU::appendData(pos, end, state.crashLogDir); pos = ASSU::appendData(pos, end, "/"); pos = ASSU::appendData(pos, end, fname); pos = ASSU::appendData(pos, end, "\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); } static void printCrashLogFileCreationError(AbortHandlerWorkingState &state, const char *fname, int e) { const char *end = state.messageBuf + sizeof(state.messageBuf); char *pos = state.messageBuf; pos = ASSU::appendData(pos, end, "Error creating "); pos = ASSU::appendData(pos, end, state.crashLogDir); pos = ASSU::appendData(pos, end, "/"); pos = ASSU::appendData(pos, end, fname); pos = ASSU::appendData(pos, end, ": "); pos = ASSU::appendData(pos, end, ASSU::limitedStrerror(e)); pos = ASSU::appendData(pos, end, " (errno="); pos = ASSU::appendInteger<int, 10>(pos, end, e); pos = ASSU::appendData(pos, end, ")\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); } static char * appendSignalName(char *pos, const char *end, int signo) { switch (signo) { case SIGABRT: pos = ASSU::appendData(pos, end, "SIGABRT"); break; case SIGSEGV: pos = ASSU::appendData(pos, end, "SIGSEGV"); break; case SIGBUS: pos = ASSU::appendData(pos, end, "SIGBUS"); break; case SIGFPE: pos = ASSU::appendData(pos, end, "SIGFPE"); break; case SIGILL: pos = ASSU::appendData(pos, end, "SIGILL"); break; default: return ASSU::appendInteger<int, 10>(pos, end, signo); } pos = ASSU::appendData(pos, end, "("); pos = ASSU::appendInteger<int, 10>(pos, end, signo); pos = ASSU::appendData(pos, end, ")"); return pos; } #define SI_CODE_HANDLER(name) \ case name: \ buf = ASSU::appendData(buf, end, #name); \ break // Must be async signal safe. static char * appendSignalReason(char *buf, const char *end, siginfo_t *info) { bool handled = true; switch (info->si_code) { SI_CODE_HANDLER(SI_USER); #ifdef SI_KERNEL SI_CODE_HANDLER(SI_KERNEL); #endif SI_CODE_HANDLER(SI_QUEUE); SI_CODE_HANDLER(SI_TIMER); #ifdef SI_ASYNCIO SI_CODE_HANDLER(SI_ASYNCIO); #endif #ifdef SI_MESGQ SI_CODE_HANDLER(SI_MESGQ); #endif #ifdef SI_SIGIO SI_CODE_HANDLER(SI_SIGIO); #endif #ifdef SI_TKILL SI_CODE_HANDLER(SI_TKILL); #endif default: switch (info->si_signo) { case SIGSEGV: switch (info->si_code) { #ifdef SEGV_MAPERR SI_CODE_HANDLER(SEGV_MAPERR); #endif #ifdef SEGV_ACCERR SI_CODE_HANDLER(SEGV_ACCERR); #endif default: handled = false; break; } break; case SIGBUS: switch (info->si_code) { #ifdef BUS_ADRALN SI_CODE_HANDLER(BUS_ADRALN); #endif #ifdef BUS_ADRERR SI_CODE_HANDLER(BUS_ADRERR); #endif #ifdef BUS_OBJERR SI_CODE_HANDLER(BUS_OBJERR); #endif default: handled = false; break; } break; default: handled = false; break; } if (!handled) { buf = ASSU::appendData(buf, end, "#"); buf = ASSU::appendInteger<int, 10>(buf, end, info->si_code); } break; } if (info->si_code <= 0) { buf = ASSU::appendData(buf, end, ", signal sent by PID "); buf = ASSU::appendInteger<pid_t, 10>(buf, end, info->si_pid); buf = ASSU::appendData(buf, end, " with UID "); buf = ASSU::appendInteger<uid_t, 10>(buf, end, info->si_uid); } buf = ASSU::appendData(buf, end, ", si_addr=0x"); buf = ASSU::appendInteger<boost::uintptr_t, 16>(buf, end, (boost::uintptr_t) info->si_addr); return buf; } static int runInSubprocessWithTimeLimit(AbortHandlerWorkingState &state, Callback callback, void *userData, int timeLimit) { char *pos; const char *end = state.messageBuf + sizeof(state.messageBuf); pid_t child; int p[2], e; if (pipe(p) == -1) { e = errno; pos = state.messageBuf; pos = ASSU::appendData(pos, end, "Could not create subprocess: pipe() failed: "); pos = ASSU::appendData(pos, end, ASSU::limitedStrerror(e)); pos = ASSU::appendData(pos, end, " (errno="); pos = ASSU::appendInteger<int, 10>(pos, end, e); pos = ASSU::appendData(pos, end, ")\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); return -1; } child = asyncFork(); if (child == 0) { close(p[0]); callback(state, userData); _exit(0); return -1; } else if (child == -1) { e = errno; close(p[0]); close(p[1]); pos = state.messageBuf; pos = ASSU::appendData(pos, end, "Could not create subprocess: fork() failed: "); pos = ASSU::appendData(pos, end, ASSU::limitedStrerror(e)); pos = ASSU::appendData(pos, end, " (errno="); pos = ASSU::appendInteger<int, 10>(pos, end, e); pos = ASSU::appendData(pos, end, ")\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); return -1; } else { int status; close(p[1]); // We give the child process a time limit. If it doesn't succeed in // exiting within the time limit, we assume that it has frozen // and we kill it. struct pollfd fd; fd.fd = p[0]; fd.events = POLLIN | POLLHUP | POLLERR; if (poll(&fd, 1, timeLimit) <= 0) { kill(child, SIGKILL); ASSU::printError("Could not run child process: it did not exit in time\n"); } close(p[0]); if (waitpid(child, &status, 0) == child) { return status; } else { return -1; } } } static void dumpUlimits(AbortHandlerWorkingState &state) { const char *end = state.messageBuf + sizeof(state.messageBuf); char *pos = state.messageBuf; pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] Dumping ulimits...\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); int fd = -1; if (state.crashLogDirFd != -1) { fd = openat(state.crashLogDirFd, "ulimits.log", O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd != -1) { printCrashLogFileCreated(state, "ulimits.log"); } else { printCrashLogFileCreationError(state, "ulimits.log", errno); } } pid_t pid = asyncFork(); int status; if (pid == 0) { if (fd != -1) { dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); } closeAllFileDescriptors(2, true); execlp("ulimit", "ulimit", "-a", (char *) 0); // On Linux 'ulimit' is a shell builtin, not a command. execlp("/bin/sh", "/bin/sh", "-c", "ulimit -a", (char *) 0); _exit(1); } else if (pid == -1) { ASSU::printError("ERROR: Could not fork a process to dump the ulimit!\n"); } else if (waitpid(pid, &status, 0) != pid || status != 0) { ASSU::printError("ERROR: Could not run 'ulimit -a'!\n"); } if (fd != -1) { close(fd); } } static void dumpFileDescriptorInfoWithLsof(AbortHandlerWorkingState &state, void *userData) { if (state.crashLogDirFd != -1) { int fd = openat(state.crashLogDirFd, "fds.log", O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd != -1) { printCrashLogFileCreated(state, "fds.log"); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); close(fd); } else { printCrashLogFileCreationError(state, "fds.log", errno); } } char *pos = state.messageBuf; const char *end = state.messageBuf + sizeof(state.messageBuf) - 1; pos = ASSU::appendInteger<pid_t, 10>(pos, end, state.pid); *pos = '\0'; closeAllFileDescriptors(2, true); execlp("lsof", "lsof", "-p", state.messageBuf, "-nP", (char *) 0); const char *command[] = { "lsof", NULL }; printExecError2(command, errno, state.messageBuf, sizeof(state.messageBuf)); _exit(1); } static void dumpFileDescriptorInfoWithLs(AbortHandlerWorkingState &state, const char *path) { pid_t pid; int fd = -1; int status; if (state.crashLogDirFd != -1) { fd = openat(state.crashLogDirFd, "fds.log", O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd != -1) { printCrashLogFileCreated(state, "fds.log"); } else { printCrashLogFileCreationError(state, "fds.log", errno); } } pid = asyncFork(); if (pid == 0) { if (fd != -1) { dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); } const char *end = state.messageBuf + sizeof(state.messageBuf); char *pos = state.messageBuf; pos = ASSU::appendData(pos, end, "Running: ls -lv "); pos = ASSU::appendData(pos, end, path); pos = ASSU::appendData(pos, end, "\n"); pos = ASSU::appendData(pos, end, "--------------------------\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); closeAllFileDescriptors(2, true); // The '-v' is for natural sorting on Linux. On BSD -v means something else but it's harmless. execlp("ls", "ls", "-lv", path, (char *) 0); const char *command[] = { "ls", NULL }; printExecError2(command, errno, state.messageBuf, sizeof(state.messageBuf)); _exit(1); } else if (pid == -1) { ASSU::printError("ERROR: Could not fork a process to dump file descriptor information!\n"); } else if (waitpid(pid, &status, 0) != pid || status != 0) { ASSU::printError("ERROR: Could not run 'ls' to dump file descriptor information!\n"); } if (fd != -1) { close(fd); } } static void dumpFileDescriptorInfo(AbortHandlerWorkingState &state) { char *messageBuf = state.messageBuf; char *pos; const char *end = state.messageBuf + sizeof(state.messageBuf) - 1; struct stat buf; int status; pos = messageBuf; pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] Open files and file descriptors:\n"); write_nowarn(STDERR_FILENO, messageBuf, pos - messageBuf); status = runInSubprocessWithTimeLimit(state, dumpFileDescriptorInfoWithLsof, NULL, 4000); if (status != 0) { char path[256]; ASSU::printError("Falling back to another mechanism for dumping file descriptors.\n"); pos = path; end = path + sizeof(path) - 1; pos = ASSU::appendData(pos, end, "/proc/"); pos = ASSU::appendInteger<pid_t, 10>(pos, end, state.pid); pos = ASSU::appendData(pos, end, "/fd"); *pos = '\0'; if (stat(path, &buf) == 0) { dumpFileDescriptorInfoWithLs(state, path); return; } pos = path; pos = ASSU::appendData(pos, end, "/dev/fd"); *pos = '\0'; if (stat(path, &buf) == 0) { dumpFileDescriptorInfoWithLs(state, path); return; } pos = messageBuf; pos = ASSU::appendData(pos, end, "ERROR: No other file descriptor dumping mechanism on current platform detected.\n"); write_nowarn(STDERR_FILENO, messageBuf, pos - messageBuf); } } static void dumpWithCrashWatch(AbortHandlerWorkingState &state) { int fd = -1; if (state.crashLogDirFd != -1) { fd = openat(state.crashLogDirFd, "backtrace.log", O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd != -1) { printCrashLogFileCreated(state, "backtrace.log"); } else { printCrashLogFileCreationError(state, "backtrace.log", errno); } } char *pos = state.messageBuf; const char *end = state.messageBuf + sizeof(state.messageBuf) - 1; pos = ASSU::appendInteger<pid_t, 10>(pos, end, state.pid); *pos = '\0'; pid_t child = asyncFork(); if (child == 0) { if (fd != -1) { dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); } closeAllFileDescriptors(2, true); execlp(ctx->config->ruby, ctx->config->ruby, ctx->crashWatchCommand, ctx->rubyLibDir, ctx->installSpec, "--dump", state.messageBuf, // PID string (char *) 0); const char *command[] = { "crash-watch", NULL }; printExecError2(command, errno, state.messageBuf, sizeof(state.messageBuf)); _exit(1); } else if (child == -1) { int e = errno; pos = state.messageBuf; pos = ASSU::appendData(pos, end, "Could not execute crash-watch: fork() failed: "); pos = ASSU::appendData(pos, end, ASSU::limitedStrerror(e)); pos = ASSU::appendData(pos, end, " (errno="); pos = ASSU::appendInteger<int, 10>(pos, end, e); pos = ASSU::appendData(pos, end, ")\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); } else { waitpid(child, NULL, 0); } if (fd != -1) { close(fd); } } #ifdef LIBC_HAS_BACKTRACE_FUNC static void dumpBacktrace(AbortHandlerWorkingState &state, void *userData) { void *backtraceStore[512]; int frames = backtrace(backtraceStore, sizeof(backtraceStore) / sizeof(void *)); char *pos; const char *end = state.messageBuf + sizeof(state.messageBuf) - 1; pos = state.messageBuf; pos = ASSU::appendData(pos, end, "--------------------------------------\n"); pos = ASSU::appendData(pos, end, "[ pid="); pos = ASSU::appendInteger<pid_t, 10>(pos, end, state.pid); pos = ASSU::appendData(pos, end, " ] Backtrace with "); pos = ASSU::appendInteger<int, 10>(pos, end, frames); pos = ASSU::appendData(pos, end, " frames:\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); if (ctx->backtraceSanitizerCommand != NULL) { int p[2]; if (pipe(p) == -1) { int e = errno; pos = state.messageBuf; pos = ASSU::appendData(pos, end, "Could not dump diagnostics through backtrace sanitizer: pipe() failed with errno="); pos = ASSU::appendInteger<int, 10>(pos, end, e); pos = ASSU::appendData(pos, end, "\n"); pos = ASSU::appendData(pos, end, "Falling back to writing to stderr directly...\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); backtrace_symbols_fd(backtraceStore, frames, STDERR_FILENO); return; } pid_t pid = asyncFork(); if (pid == 0) { const char *pidStr = pos = state.messageBuf; pos = ASSU::appendInteger<pid_t, 10>(pos, end, state.pid); *pos = '\0'; pos++; close(p[1]); dup2(p[0], STDIN_FILENO); closeAllFileDescriptors(2, true); const char *command = pos; pos = ASSU::appendData(pos, end, "exec "); pos = ASSU::appendData(pos, end, ctx->backtraceSanitizerCommand); if (ctx->backtraceSanitizerPassProgramInfo) { pos = ASSU::appendData(pos, end, " \""); pos = ASSU::appendData(pos, end, ctx->config->origArgv[0]); pos = ASSU::appendData(pos, end, "\" "); pos = ASSU::appendData(pos, end, pidStr); } *pos = '\0'; pos++; execlp("/bin/sh", "/bin/sh", "-c", command, (char *) 0); pos = state.messageBuf; pos = ASSU::appendData(pos, end, "ERROR: cannot execute '"); pos = ASSU::appendData(pos, end, ctx->backtraceSanitizerCommand); pos = ASSU::appendData(pos, end, "' for sanitizing the backtrace, trying 'cat'...\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); execlp("cat", "cat", (char *) 0); execlp("/bin/cat", "cat", (char *) 0); execlp("/usr/bin/cat", "cat", (char *) 0); const char *commandArray[] = { "cat", NULL }; printExecError2(commandArray, errno, state.messageBuf, sizeof(state.messageBuf)); _exit(1); } else if (pid == -1) { close(p[0]); close(p[1]); int e = errno; pos = state.messageBuf; pos = ASSU::appendData(pos, end, "Could not dump diagnostics through backtrace sanitizer: fork() failed: "); pos = ASSU::appendData(pos, end, ASSU::limitedStrerror(e)); pos = ASSU::appendData(pos, end, " (errno="); pos = ASSU::appendInteger<int, 10>(pos, end, e); pos = ASSU::appendData(pos, end, ")\n"); pos = ASSU::appendData(pos, end, "Falling back to writing to stderr directly...\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); backtrace_symbols_fd(backtraceStore, frames, STDERR_FILENO); } else { int status = -1; close(p[0]); backtrace_symbols_fd(backtraceStore, frames, p[1]); close(p[1]); if (waitpid(pid, &status, 0) == -1 || status != 0) { pos = state.messageBuf; pos = ASSU::appendData(pos, end, "ERROR: cannot execute '"); pos = ASSU::appendData(pos, end, ctx->backtraceSanitizerCommand); pos = ASSU::appendData(pos, end, "' for sanitizing the backtrace, writing to stderr directly...\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); backtrace_symbols_fd(backtraceStore, frames, STDERR_FILENO); } } } else { backtrace_symbols_fd(backtraceStore, frames, STDERR_FILENO); } } #endif static void runCustomDiagnosticsDumper(AbortHandlerWorkingState &state, void *userData) { unsigned int i = static_cast<unsigned int>(reinterpret_cast<boost::uintptr_t>(userData)); const AbortHandlerConfig::DiagnosticsDumper &dumper = ctx->config->diagnosticsDumpers[i]; if (state.crashLogDirFd != -1) { int fd = openat(state.crashLogDirFd, dumper.logFileName, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd != -1) { printCrashLogFileCreated(state, dumper.logFileName); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); close(fd); } else { printCrashLogFileCreationError(state, dumper.logFileName, errno); } } dumper.func(dumper.userData); } // This function is performed in a child process. static void dumpDiagnostics(AbortHandlerWorkingState &state) { char *pos; const char *end = state.messageBuf + sizeof(state.messageBuf); pid_t pid; int status; pos = state.messageBuf; pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] Date and uname:\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); // Dump human-readable time string and string. pid = asyncFork(); if (pid == 0) { closeAllFileDescriptors(2, true); execlp("date", "date", (char *) 0); _exit(1); } else if (pid == -1) { ASSU::printError("ERROR: Could not fork a process to dump the time!\n"); } else if (waitpid(pid, &status, 0) != pid || status != 0) { ASSU::printError("ERROR: Could not run 'date'!\n"); } // Dump system uname. pid = asyncFork(); if (pid == 0) { closeAllFileDescriptors(2, true); execlp("uname", "uname", "-mprsv", (char *) 0); _exit(1); } else if (pid == -1) { ASSU::printError("ERROR: Could not fork a process to dump the uname!\n"); } else if (waitpid(pid, &status, 0) != pid || status != 0) { ASSU::printError("ERROR: Could not run 'uname -mprsv'!\n"); } pos = state.messageBuf; pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] " PROGRAM_NAME " version: " PASSENGER_VERSION "\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); if (LoggingKit::lastAssertionFailure.filename != NULL) { pos = state.messageBuf; pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] Last assertion failure: ("); pos = ASSU::appendData(pos, end, LoggingKit::lastAssertionFailure.expression); pos = ASSU::appendData(pos, end, "), "); if (LoggingKit::lastAssertionFailure.function != NULL) { pos = ASSU::appendData(pos, end, "function "); pos = ASSU::appendData(pos, end, LoggingKit::lastAssertionFailure.function); pos = ASSU::appendData(pos, end, ", "); } pos = ASSU::appendData(pos, end, "file "); pos = ASSU::appendData(pos, end, LoggingKit::lastAssertionFailure.filename); pos = ASSU::appendData(pos, end, ", line "); pos = ASSU::appendInteger<unsigned int, 10>(pos, end, LoggingKit::lastAssertionFailure.line); pos = ASSU::appendData(pos, end, ".\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); } // It is important that writing the message and the backtrace are two // seperate operations because it's not entirely clear whether the // latter is async signal safe and thus can crash. pos = state.messageBuf; pos = ASSU::appendData(pos, end, state.messagePrefix); #ifdef LIBC_HAS_BACKTRACE_FUNC pos = ASSU::appendData(pos, end, " ] libc backtrace available!\n"); #else pos = ASSU::appendData(pos, end, " ] libc backtrace not available.\n"); #endif write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); #ifdef LIBC_HAS_BACKTRACE_FUNC runInSubprocessWithTimeLimit(state, dumpBacktrace, NULL, 4000); #endif ASSU::printError("--------------------------------------\n"); dumpUlimits(state); ASSU::printError("--------------------------------------\n"); for (unsigned int i = 0; i < AbortHandlerConfig::MAX_DIAGNOSTICS_DUMPERS; i++) { const AbortHandlerConfig::DiagnosticsDumper &diagnosticsDumper = ctx->config->diagnosticsDumpers[i]; if (diagnosticsDumper.func == NULL) { continue; } pos = state.messageBuf; pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] Dumping "); pos = ASSU::appendData(pos, end, diagnosticsDumper.name); pos = ASSU::appendData(pos, end, "...\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); runInSubprocessWithTimeLimit(state, runCustomDiagnosticsDumper, reinterpret_cast<void *>(static_cast<boost::uintptr_t>(i)), 2000); ASSU::printError("--------------------------------------\n"); } dumpFileDescriptorInfo(state); ASSU::printError("--------------------------------------\n"); if (ctx->config->dumpWithCrashWatch && ctx->crashWatchCommand != NULL) { pos = state.messageBuf; pos = ASSU::appendData(pos, end, state.messagePrefix); #ifdef LIBC_HAS_BACKTRACE_FUNC pos = ASSU::appendData(pos, end, " ] Dumping a more detailed backtrace with crash-watch...\n"); #else pos = ASSU::appendData(pos, end, " ] Dumping a backtrace with crash-watch...\n"); #endif write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); dumpWithCrashWatch(state); } else { write_nowarn(STDERR_FILENO, "\n", 1); } if (state.crashLogDir[0] != '\0') { ASSU::printError("--------------------------------------\n"); pos = state.messageBuf; pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] **************** LOOK HERE FOR CRASH DETAILS *****************\n\n"); pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] Crash log dumped to this directory:\n"); pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] "); pos = ASSU::appendData(pos, end, state.crashLogDir); pos = ASSU::appendData(pos, end, "\n\n"); pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] **************** LOOK ABOVE FOR CRASH DETAILS ****************\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); } } static bool createCrashLogDir(AbortHandlerWorkingState &state, time_t t) { char *suffixBegin = state.crashLogDir; const char *end = state.crashLogDir + sizeof(state.crashLogDir) - 1; suffixBegin = ASSU::appendData(suffixBegin, end, "/var/tmp/passenger-crash-log."); suffixBegin = ASSU::appendInteger<time_t, 10>(suffixBegin, end, t); suffixBegin = ASSU::appendData(suffixBegin, end, "."); // Try a bunch of times to find and create a unique path. for (unsigned int i = 0; i < MAX_RANDOM_TOKENS; i++) { char *pos = suffixBegin; pos = ASSU::appendData(pos, end, ctx->randomTokens + RANDOM_TOKEN_SIZE * i, RANDOM_TOKEN_SIZE); *pos = '\0'; int ret; do { ret = mkdir(state.crashLogDir, 0700); } while (ret == -1 && errno == EINTR); if (ret == -1) { if (errno == EEXIST) { // Directory exists; try again. continue; } else { int e = errno; end = state.messageBuf + sizeof(state.messageBuf); pos = state.messageBuf; pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] Error creating directory "); pos = ASSU::appendData(pos, end, state.crashLogDir); pos = ASSU::appendData(pos, end, " for storing crash log: "); pos = ASSU::appendData(pos, end, ASSU::limitedStrerror(e)); pos = ASSU::appendData(pos, end, " (errno="); pos = ASSU::appendInteger<int, 10>(pos, end, e); pos = ASSU::appendData(pos, end, ")\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); state.crashLogDir[0] = '\0'; return false; } } do { state.crashLogDirFd = open(state.crashLogDir, O_RDONLY); } while (state.crashLogDirFd == -1 && errno == EINTR); if (state.crashLogDirFd == -1) { int e = errno; end = state.messageBuf + sizeof(state.messageBuf); pos = state.messageBuf; pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] Error opening created directory "); pos = ASSU::appendData(pos, end, state.crashLogDir); pos = ASSU::appendData(pos, end, " for storing crash log: "); pos = ASSU::appendData(pos, end, ASSU::limitedStrerror(e)); pos = ASSU::appendData(pos, end, " (errno="); pos = ASSU::appendInteger<int, 10>(pos, end, e); pos = ASSU::appendData(pos, end, ")\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); state.crashLogDir[0] = '\0'; return false; } return true; } state.crashLogDir[0] = '\0'; return false; } static bool forkAndRedirectToTeeAndMainLogFile(const char *crashLogDir) { int p[2]; if (pipe(p) == -1) { return false; } char filename[300]; char *pos = filename; const char *end = filename + sizeof(filename) - 1; pos = ASSU::appendData(pos, end, crashLogDir); pos = ASSU::appendData(pos, end, "/"); pos = ASSU::appendData(pos, end, "main.log"); *pos = '\0'; pid_t pid = asyncFork(); if (pid == 0) { close(p[1]); dup2(p[0], STDIN_FILENO); execlp("tee", "tee", filename, (char *) 0); execlp("/usr/bin/tee", "tee", filename, (char *) 0); execlp("cat", "cat", (char *) 0); execlp("/bin/cat", "cat", (char *) 0); execlp("/usr/bin/cat", "cat", (char *) 0); ASSU::printError("ERROR: cannot execute 'tee' or 'cat'; crash log will be lost!\n"); _exit(1); return false; } else if (pid == -1) { ASSU::printError("ERROR: cannot fork a process for executing 'tee'\n"); return false; } else { close(p[0]); dup2(p[1], STDOUT_FILENO); dup2(p[1], STDERR_FILENO); return true; } } static void closeEmergencyPipes() { if (ctx->emergencyPipe1[0] != -1) { close(ctx->emergencyPipe1[0]); } if (ctx->emergencyPipe1[1] != -1) { close(ctx->emergencyPipe1[1]); } if (ctx->emergencyPipe2[0] != -1) { close(ctx->emergencyPipe2[0]); } if (ctx->emergencyPipe2[1] != -1) { close(ctx->emergencyPipe2[1]); } ctx->emergencyPipe1[0] = ctx->emergencyPipe1[1] = -1; ctx->emergencyPipe2[0] = ctx->emergencyPipe2[1] = -1; } static void abortHandler(int signo, siginfo_t *info, void *_unused) { AbortHandlerWorkingState state; state.pid = getpid(); state.signo = signo; state.info = info; pid_t child; time_t t = time(NULL); ctx->callCount++; if (ctx->callCount > 1) { // The abort handler itself crashed! const char *end = state.messageBuf + sizeof(state.messageBuf); char *pos = state.messageBuf; pos = ASSU::appendData(pos, end, "[ origpid="); pos = ASSU::appendInteger<pid_t, 10>(pos, end, state.pid); pos = ASSU::appendData(pos, end, ", pid="); pos = ASSU::appendInteger<pid_t, 10>(pos, end, getpid()); pos = ASSU::appendData(pos, end, ", timestamp="); pos = ASSU::appendInteger<time_t, 10>(pos, end, t); if (ctx->callCount == 2) { // This is the first time it crashed. pos = ASSU::appendData(pos, end, " ] Abort handler crashed! signo="); pos = appendSignalName(pos, end, state.signo); pos = ASSU::appendData(pos, end, ", reason="); pos = appendSignalReason(pos, end, state.info); pos = ASSU::appendData(pos, end, "\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); // Run default signal handler. raise(signo); } else { // This is the second time it crashed, meaning it failed to // invoke the default signal handler to abort the process! pos = ASSU::appendData(pos, end, " ] Abort handler crashed again! Force exiting this time. signo="); pos = appendSignalName(pos, end, state.signo); pos = ASSU::appendData(pos, end, ", reason="); pos = appendSignalReason(pos, end, state.info); pos = ASSU::appendData(pos, end, "\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); _exit(1); } return; } closeEmergencyPipes(); { const char *end = state.messagePrefix + sizeof(state.messagePrefix); char *pos = state.messagePrefix; pos = ASSU::appendData(pos, end, "[ pid="); pos = ASSU::appendInteger<pid_t, 10>(pos, end, state.pid); *pos = '\0'; } /* We want to dump the entire crash log to both stderr and a log file. * We use 'tee' for this. */ state.crashLogDir[0] = '\0'; state.crashLogDirFd = -1; if (createCrashLogDir(state, t)) { forkAndRedirectToTeeAndMainLogFile(state.crashLogDir); } const char *end = state.messageBuf + sizeof(state.messageBuf); char *pos = state.messageBuf; // Print a \n just in case we're aborting in the middle of a non-terminated line. pos = ASSU::appendData(pos, end, "\n"); pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, ", timestamp="); pos = ASSU::appendInteger<time_t, 10>(pos, end, t); pos = ASSU::appendData(pos, end, " ] Process aborted! signo="); pos = appendSignalName(pos, end, state.signo); pos = ASSU::appendData(pos, end, ", reason="); pos = appendSignalReason(pos, end, state.info); pos = ASSU::appendData(pos, end, ", randomSeed="); pos = ASSU::appendInteger<unsigned int, 10>(pos, end, ctx->config->randomSeed); pos = ASSU::appendData(pos, end, "\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); pos = state.messageBuf; if (state.crashLogDir[0] != '\0') { pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] Crash log files will be dumped to "); pos = ASSU::appendData(pos, end, state.crashLogDir); pos = ASSU::appendData(pos, end, " <--- ******* LOOK HERE FOR DETAILS!!! *******\n"); } else { pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] Could not create crash log directory, so dumping to stderr only.\n"); } write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); if (ctx->config->beep) { pos = state.messageBuf; pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] PASSENGER_BEEP_ON_ABORT on, executing beep...\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); child = asyncFork(); if (child == 0) { closeAllFileDescriptors(2, true); #ifdef __APPLE__ const char *command[] = { "osascript", NULL }; execlp("osascript", "osascript", "-e", "beep 2", (char *) 0); printExecError2(command, errno, state.messageBuf, sizeof(state.messageBuf)); #else const char *command[] = { "beep", NULL }; execlp("beep", "beep", (char *) 0); printExecError2(command, errno, state.messageBuf, sizeof(state.messageBuf)); #endif _exit(1); } else if (child == -1) { int e = errno; pos = state.messageBuf; pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] Could fork a child process for invoking a beep: fork() failed with errno="); pos = ASSU::appendInteger<int, 10>(pos, end, e); pos = ASSU::appendData(pos, end, "\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); } } if (ctx->config->stopProcess) { pos = state.messageBuf; pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] PASSENGER_STOP_ON_ABORT on, so process stopped. Send SIGCONT when you want to continue.\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); raise(SIGSTOP); } // It isn't safe to call any waiting functions in this signal handler, // not even read() and waitpid() even though they're async signal safe. // So we fork a child process and let it dump as much diagnostics as possible // instead of doing it in this process. child = asyncFork(); if (child == 0) { // Sleep for a short while to allow the parent process to raise SIGSTOP. // usleep() and nanosleep() aren't async signal safe so we use select() // instead. struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 100000; select(0, NULL, NULL, NULL, &tv); resetSignalHandlersAndMask(); child = asyncFork(); if (child == 0) { // OS X: for some reason the SIGPIPE handler may be reset to default after forking. // Later in this program we're going to pipe backtrace_symbols_fd() into the backtrace // sanitizer, which may fail, and we don't want the diagnostics process to crash // with SIGPIPE as a result, so we ignore SIGPIPE again. ignoreSigpipe(); dumpDiagnostics(state); // The child process may or may or may not resume the original process. // We do it ourselves just to be sure. kill(state.pid, SIGCONT); _exit(0); } else if (child == -1) { int e = errno; pos = state.messageBuf; pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, "] Could not fork a child process for dumping diagnostics: fork() failed with errno="); pos = ASSU::appendInteger<int, 10>(pos, end, e); pos = ASSU::appendData(pos, end, "\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); _exit(1); } else { // Exit immediately so that child process is adopted by init process. _exit(0); } } else if (child == -1) { int e = errno; pos = state.messageBuf; pos = ASSU::appendData(pos, end, state.messagePrefix); pos = ASSU::appendData(pos, end, " ] Could not fork a child process for dumping diagnostics: fork() failed with errno="); pos = ASSU::appendInteger<int, 10>(pos, end, e); pos = ASSU::appendData(pos, end, "\n"); write_nowarn(STDERR_FILENO, state.messageBuf, pos - state.messageBuf); } else { raise(SIGSTOP); // Will continue after the child process has done its job. } // Run default signal handler. raise(signo); } void installAbortHandler(const AbortHandlerConfig *config) { ctx = new AbortHandlerContext(); memset(ctx, 0, sizeof(AbortHandlerContext)); ctx->config = config; ctx->backtraceSanitizerPassProgramInfo = true; ctx->randomTokens = strdup(RandomGenerator().generateAsciiString( MAX_RANDOM_TOKENS * RANDOM_TOKEN_SIZE).c_str()); ctx->emergencyPipe1[0] = -1; ctx->emergencyPipe1[1] = -1; ctx->emergencyPipe2[0] = -1; ctx->emergencyPipe2[1] = -1; abortHandlerConfigChanged(); IGNORE_SYSCALL_RESULT(pipe(ctx->emergencyPipe1)); IGNORE_SYSCALL_RESULT(pipe(ctx->emergencyPipe2)); size_t alternativeStackSize = MINSIGSTKSZ + 128 * 1024; ctx->alternativeStack = (char *) malloc(alternativeStackSize); if (ctx->alternativeStack == NULL) { fprintf(stderr, "Cannot allocate an alternative stack with a size of %lu bytes!\n", (unsigned long) alternativeStackSize); fflush(stderr); abort(); } stack_t stack; stack.ss_sp = ctx->alternativeStack; stack.ss_size = alternativeStackSize; stack.ss_flags = 0; if (sigaltstack(&stack, NULL) != 0) { int e = errno; fprintf(stderr, "Cannot install an alternative stack for use in signal handlers: %s (%d)\n", strerror(e), e); fflush(stderr); abort(); } struct sigaction action; action.sa_sigaction = abortHandler; action.sa_flags = SA_RESETHAND | SA_SIGINFO; sigemptyset(&action.sa_mask); sigaction(SIGABRT, &action, NULL); sigaction(SIGSEGV, &action, NULL); sigaction(SIGBUS, &action, NULL); sigaction(SIGFPE, &action, NULL); sigaction(SIGILL, &action, NULL); } bool abortHandlerInstalled() { return ctx != NULL; } void abortHandlerLogFds() { if (ctx->emergencyPipe1[0] != -1) { P_LOG_FILE_DESCRIPTOR_OPEN4(ctx->emergencyPipe1[0], __FILE__, __LINE__, "Emergency pipe 1-0"); P_LOG_FILE_DESCRIPTOR_OPEN4(ctx->emergencyPipe1[1], __FILE__, __LINE__, "Emergency pipe 1-1"); } if (ctx->emergencyPipe2[0] != -1) { P_LOG_FILE_DESCRIPTOR_OPEN4(ctx->emergencyPipe2[0], __FILE__, __LINE__, "Emergency pipe 2-0"); P_LOG_FILE_DESCRIPTOR_OPEN4(ctx->emergencyPipe2[1], __FILE__, __LINE__, "Emergency pipe 2-1"); } } static void useCxxFiltAsBacktraceSanitizer() { ctx->backtraceSanitizerCommand = strdup("c++filt -n"); ctx->backtraceSanitizerPassProgramInfo = false; } void abortHandlerConfigChanged() { const AbortHandlerConfig *config = ctx->config; char *oldInstallSpec = ctx->installSpec; char *oldRubyLibDir = ctx->rubyLibDir; char *oldTmpDir = ctx->tmpDir; char *oldCrashWatchCommand = ctx->crashWatchCommand; char *oldBacktraceSanitizerCommand = ctx->backtraceSanitizerCommand; if (config->resourceLocator != NULL) { string path; const ResourceLocator *locator = config->resourceLocator; ctx->installSpec = strdup(locator->getInstallSpec().c_str()); ctx->rubyLibDir = strdup(locator->getRubyLibDir().c_str()); ctx->tmpDir = strdup(getSystemTempDir()); path = locator->getHelperScriptsDir() + "/crash-watch.rb"; ctx->crashWatchCommand = strdup(path.c_str()); if (ctx->installSpec == NULL || ctx->rubyLibDir == NULL || ctx->tmpDir == NULL || ctx->crashWatchCommand == NULL) { fprintf(stderr, "Cannot allocate memory for abort handler!\n"); fflush(stderr); abort(); } #ifdef __linux__ path = StaticString(config->ruby) + " \"" + locator->getHelperScriptsDir() + "/backtrace-sanitizer.rb\""; ctx->backtraceSanitizerCommand = strdup(path.c_str()); ctx->backtraceSanitizerPassProgramInfo = true; if (ctx->backtraceSanitizerCommand == NULL) { fprintf(stderr, "Cannot allocate memory for abort handler!\n"); fflush(stderr); abort(); } #else useCxxFiltAsBacktraceSanitizer(); #endif } else { ctx->installSpec = NULL; ctx->rubyLibDir = NULL; ctx->tmpDir = NULL; ctx->crashWatchCommand = NULL; useCxxFiltAsBacktraceSanitizer(); } free(oldInstallSpec); free(oldRubyLibDir); free(oldTmpDir); free(oldCrashWatchCommand); free(oldBacktraceSanitizerCommand); } void shutdownAbortHandler() { free(ctx->installSpec); free(ctx->rubyLibDir); free(ctx->tmpDir); free(ctx->crashWatchCommand); free(ctx->backtraceSanitizerCommand); free(ctx->randomTokens); free(ctx->alternativeStack); closeEmergencyPipes(); delete ctx; ctx = NULL; } } // namespace Fundamentals } // namespace Agent } // namespace Passenger /* * Override assert() to add more features and to fix bugs. We save the information * of the last assertion failure in a global variable so that we can print it * to the crash diagnostics report. */ #if defined(__GLIBC__) extern "C" __attribute__ ((__noreturn__)) void __assert_fail(__const char *__assertion, __const char *__file, unsigned int __line, __const char *__function) { using namespace Passenger; LoggingKit::lastAssertionFailure.filename = __file; LoggingKit::lastAssertionFailure.line = __line; LoggingKit::lastAssertionFailure.function = __function; LoggingKit::lastAssertionFailure.expression = __assertion; fprintf(stderr, "Assertion failed! %s:%u: %s: %s\n", __file, __line, __function, __assertion); fflush(stderr); abort(); } #elif defined(__APPLE__) /* On OS X, raise() and abort() unfortunately send SIGABRT to the main thread, * causing the original backtrace to be lost in the signal handler. * We work around this for anything in the same linkage unit by just definin * our own versions of the assert handler and abort. */ #include <pthread.h> extern "C" int raise(int sig) { return pthread_kill(pthread_self(), sig); } extern "C" void __assert_rtn(const char *func, const char *file, int line, const char *expr) { using namespace Passenger; LoggingKit::lastAssertionFailure.filename = file; LoggingKit::lastAssertionFailure.line = line; LoggingKit::lastAssertionFailure.function = func; LoggingKit::lastAssertionFailure.expression = expr; if (func) { fprintf(stderr, "Assertion failed: (%s), function %s, file %s, line %d.\n", expr, func, file, line); } else { fprintf(stderr, "Assertion failed: (%s), file %s, line %d.\n", expr, file, line); } fflush(stderr); abort(); } extern "C" void abort() { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGABRT); pthread_sigmask(SIG_UNBLOCK, &set, NULL); raise(SIGABRT); usleep(1000); __builtin_trap(); } #endif /* __APPLE__ */