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/saltstack/salt/lib/python3.10/site-packages/salt/utils
Viewing File: /opt/saltstack/salt/lib/python3.10/site-packages/salt/utils/mac_utils.py
""" Helper functions for use by mac modules .. versionadded:: 2016.3.0 """ import logging import os import plistlib import subprocess import time import xml.parsers.expat import salt.grains.extra import salt.modules.cmdmod import salt.utils.args import salt.utils.files import salt.utils.path import salt.utils.platform import salt.utils.stringutils import salt.utils.timed_subprocess from salt.exceptions import ( CommandExecutionError, SaltInvocationError, TimedProcTimeoutError, ) try: import pwd except ImportError: # The pwd module is not available on all platforms pass DEFAULT_SHELL = salt.grains.extra.shell()["shell"] # Set up logging log = logging.getLogger(__name__) __virtualname__ = "mac_utils" __salt__ = { "cmd.run_all": salt.modules.cmdmod._run_all_quiet, "cmd.run": salt.modules.cmdmod._run_quiet, } def __virtual__(): """ Load only on Mac OS """ if not salt.utils.platform.is_darwin(): return ( False, "The mac_utils utility could not be loaded: " "utility only works on MacOS systems.", ) return __virtualname__ def _run_all(cmd): """ Args: cmd: Returns: """ if not isinstance(cmd, list): cmd = salt.utils.args.shlex_split(cmd, posix=False) for idx, item in enumerate(cmd): if not isinstance(cmd[idx], str): cmd[idx] = str(cmd[idx]) cmd = " ".join(cmd) run_env = os.environ.copy() kwargs = { "cwd": None, "shell": DEFAULT_SHELL, "env": run_env, "stdin": None, "stdout": subprocess.PIPE, "stderr": subprocess.PIPE, "with_communicate": True, "timeout": None, "bg": False, } try: proc = salt.utils.timed_subprocess.TimedProc(cmd, **kwargs) except OSError as exc: raise CommandExecutionError( "Unable to run command '{}' with the context '{}', reason: {}".format( cmd, kwargs, exc ) ) ret = {} try: proc.run() except TimedProcTimeoutError as exc: ret["stdout"] = str(exc) ret["stderr"] = "" ret["retcode"] = 1 ret["pid"] = proc.process.pid return ret out, err = proc.stdout, proc.stderr if out is not None: out = salt.utils.stringutils.to_str(out).rstrip() if err is not None: err = salt.utils.stringutils.to_str(err).rstrip() ret["pid"] = proc.process.pid ret["retcode"] = proc.process.returncode ret["stdout"] = out ret["stderr"] = err return ret def _check_launchctl_stderr(ret): """ helper class to check the launchctl stderr. launchctl does not always return bad exit code if there is a failure """ err = ret["stderr"].lower() if "service is disabled" in err: return True return False def execute_return_success(cmd): """ Executes the passed command. Returns True if successful :param str cmd: The command to run :return: True if successful, otherwise False :rtype: bool :raises: Error if command fails or is not supported """ ret = _run_all(cmd) log.debug("Execute return success %s: %r", cmd, ret) if ret["retcode"] != 0 or "not supported" in ret["stdout"].lower(): msg = f"Command Failed: {cmd}\n" msg += "Return Code: {}\n".format(ret["retcode"]) msg += "Output: {}\n".format(ret["stdout"]) msg += "Error: {}\n".format(ret["stderr"]) raise CommandExecutionError(msg) return True def execute_return_result(cmd): """ Executes the passed command. Returns the standard out if successful :param str cmd: The command to run :return: The standard out of the command if successful, otherwise returns an error :rtype: str :raises: Error if command fails or is not supported """ ret = _run_all(cmd) if ret["retcode"] != 0 or "not supported" in ret["stdout"].lower(): msg = f"Command Failed: {cmd}\n" msg += "Return Code: {}\n".format(ret["retcode"]) msg += "Output: {}\n".format(ret["stdout"]) msg += "Error: {}\n".format(ret["stderr"]) raise CommandExecutionError(msg) return ret["stdout"] def parse_return(data): """ Returns the data portion of a string that is colon separated. :param str data: The string that contains the data to be parsed. Usually the standard out from a command For example: ``Time Zone: America/Denver`` will return: ``America/Denver`` """ if ": " in data: return data.split(": ")[1] if ":\n" in data: return data.split(":\n")[1] else: return data def validate_enabled(enabled): """ Helper function to validate the enabled parameter. Boolean values are converted to "on" and "off". String values are checked to make sure they are either "on" or "off"/"yes" or "no". Integer ``0`` will return "off". All other integers will return "on" :param enabled: Enabled can be boolean True or False, Integers, or string values "on" and "off"/"yes" and "no". :type: str, int, bool :return: "on" or "off" or errors :rtype: str """ if isinstance(enabled, str): if enabled.lower() not in ["on", "off", "yes", "no"]: msg = ( "\nMac Power: Invalid String Value for Enabled.\n" "String values must be 'on' or 'off'/'yes' or 'no'.\n" "Passed: {}".format(enabled) ) raise SaltInvocationError(msg) return "on" if enabled.lower() in ["on", "yes"] else "off" return "on" if bool(enabled) else "off" def confirm_updated(value, check_fun, normalize_ret=False, wait=5): """ Wait up to ``wait`` seconds for a system parameter to be changed before deciding it hasn't changed. :param str value: The value indicating a successful change :param function check_fun: The function whose return is compared with ``value`` :param bool normalize_ret: Whether to normalize the return from ``check_fun`` with ``validate_enabled`` :param int wait: The maximum amount of seconds to wait for a system parameter to change """ for i in range(wait): state = validate_enabled(check_fun()) if normalize_ret else check_fun() log.debug( "Confirm update try: %d func:%r state:%s value:%s", i, check_fun, state, value, ) if value in state: return True time.sleep(1) return False def launchctl(sub_cmd, *args, **kwargs): """ Run a launchctl command and raise an error if it fails Args: additional args are passed to launchctl sub_cmd (str): Sub command supplied to launchctl Kwargs: passed to ``cmd.run_all`` return_stdout (bool): A keyword argument. If true return the stdout of the launchctl command Returns: bool: ``True`` if successful str: The stdout of the launchctl command if requested Raises: CommandExecutionError: If command fails CLI Example: .. code-block:: bash import salt.utils.mac_service salt.utils.mac_service.launchctl('debug', 'org.cups.cupsd') """ # Get return type return_stdout = kwargs.pop("return_stdout", False) # Construct command cmd = ["launchctl", sub_cmd] cmd.extend(args) # fix for https://github.com/saltstack/salt/issues/57436 if sub_cmd == "bootout": kwargs["success_retcodes"] = [ 36, ] # Run command kwargs["python_shell"] = False kwargs = salt.utils.args.clean_kwargs(**kwargs) ret = __salt__["cmd.run_all"](cmd, **kwargs) error = _check_launchctl_stderr(ret) # Raise an error or return successful result if ret["retcode"] or error: out = f"Failed to {sub_cmd} service:\n" out += "stdout: {}\n".format(ret["stdout"]) out += "stderr: {}\n".format(ret["stderr"]) out += "retcode: {}".format(ret["retcode"]) raise CommandExecutionError(out) else: return ret["stdout"] if return_stdout else True def _read_plist_file(root, file_name): """ :param root: The root path of the plist file :param file_name: The name of the plist file :return: An empty dictionary if the plist file was invalid, otherwise, a dictionary with plist data """ file_path = os.path.join(root, file_name) log.debug("read_plist: Gathering service info for %s", file_path) # Must be a plist file if not file_path.lower().endswith(".plist"): log.debug("read_plist: Not a plist file: %s", file_path) return {} # ignore broken symlinks if not os.path.exists(os.path.realpath(file_path)): log.warning("read_plist: Ignoring broken symlink: %s", file_path) return {} try: with salt.utils.files.fopen(file_path, "rb") as handle: plist = plistlib.load(handle) except plistlib.InvalidFileException: # Raised in python3 if the file is not XML. # There's nothing we can do; move on to the next one. log.warning( 'read_plist: Unable to parse "%s" as it is invalid XML: InvalidFileException.', file_path, ) return {} except ValueError as err: # fixes https://github.com/saltstack/salt/issues/58143 # choosing not to log a Warning as this would happen on BigSur+ machines. log.debug( "Caught ValueError: '%s', while trying to parse '%s'.", err, file_path ) return {} except xml.parsers.expat.ExpatError: # Raised by py3 if the file is XML, but with errors. log.warning( 'read_plist: Unable to parse "%s" as it is invalid XML: xml.parsers.expat.ExpatError.', file_path, ) return {} if "Label" not in plist: # not all launchd plists contain a Label key log.debug( "read_plist: Service does not contain a Label key. Skipping %s.", file_path ) return {} return { "file_name": file_name, "file_path": file_path, "plist": plist, } def _available_services(refresh=False): """ This is a helper function for getting the available macOS services. The strategy is to look through the known system locations for launchd plist files, parse them, and use their information for populating the list of services. Services can run without a plist file present, but normally services which have an automated startup will have a plist file, so this is a minor compromise. """ if "available_services" in __context__ and not refresh: log.debug("Found context for available services.") __context__["using_cached_services"] = True return __context__["available_services"] launchd_paths = { "/Library/LaunchAgents", "/Library/LaunchDaemons", "/System/Library/LaunchAgents", "/System/Library/LaunchDaemons", } agent_path = "/Users/{}/Library/LaunchAgents" launchd_paths.update( { agent_path.format(user) for user in os.listdir("/Users/") if os.path.isdir(agent_path.format(user)) } ) result = {} for launch_dir in launchd_paths: for root, dirs, files in salt.utils.path.os_walk(launch_dir): for file_name in files: data = _read_plist_file(root, file_name) if data: result[data["plist"]["Label"].lower()] = data # put this in __context__ as this is a time consuming function. # a fix for this issue. https://github.com/saltstack/salt/issues/48414 __context__["available_services"] = result # this is a fresh gathering of services, set cached to false __context__["using_cached_services"] = False return result def available_services(refresh=False): """ Return a dictionary of all available services on the system :param bool refresh: If you wish to refresh the available services as this data is cached on the first run. Returns: dict: All available services CLI Example: .. code-block:: bash import salt.utils.mac_service salt.utils.mac_service.available_services() """ log.debug("Loading available services") return _available_services(refresh) def console_user(username=False): """ Gets the UID or Username of the current console user. :return: The uid or username of the console user. :param bool username: Whether to return the username of the console user instead of the UID. Defaults to False :rtype: Interger of the UID, or a string of the username. Raises: CommandExecutionError: If we fail to get the UID. CLI Example: .. code-block:: bash import salt.utils.mac_service salt.utils.mac_service.console_user() """ try: # returns the 'st_uid' stat from the /dev/console file. uid = os.stat("/dev/console")[4] except (OSError, IndexError): # we should never get here but raise an error if so raise CommandExecutionError("Failed to get a UID for the console user.") if username: return pwd.getpwuid(uid)[0] return uid def git_is_stub(): """ Return whether macOS git is the standard OS stub or a real binary. """ # On a fresh macOS install, /usr/bin/git is a stub, which if # accessed, triggers a UI dialog box prompting the user to install # the developer command line tools. We don't want that! So instead, # running the below command will return a path to the installed dev # tools and retcode 0, or print a bunch of info to stderr and # retcode 2. try: cmd = ["/usr/bin/xcode-select", "-p"] _ = subprocess.check_call( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1 ) log.debug("Xcode command line tools present") return False except subprocess.CalledProcessError: log.debug("Xcode command line tools not present") return True