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/modules
Viewing File: /opt/saltstack/salt/lib/python3.10/site-packages/salt/modules/mac_service.py
""" The service module for macOS .. versionadded:: 2016.3.0 This module has support for services in the following locations. .. code-block:: bash /System/Library/LaunchDaemons/ /System/Library/LaunchAgents/ /Library/LaunchDaemons/ /Library/LaunchAgents/ # As of version "2019.2.0" support for user-specific services were added. /Users/foo/Library/LaunchAgents/ .. note:: As of the 2019.2.0 release, if a service is located in a ``LaunchAgent`` path and a ``runas`` user is NOT specified, the current console user will be used to properly interact with the service. .. note:: As of the 3002 release, if a service name of ``salt-minion`` is passed this module will convert it over to it's macOS equivalent name, in this case to ``com.saltstack.salt.minion``. This is true for ``salt-master`` ``salt-api``, and ``salt-syndic`` as well. """ import logging import os import salt.utils.files import salt.utils.path import salt.utils.platform import salt.utils.stringutils from salt.exceptions import CommandExecutionError from salt.utils.versions import Version # Define the module's virtual name __virtualname__ = "service" __func_alias__ = { "list_": "list", } log = logging.getLogger(__name__) SALT_MAC_SERVICES = { "salt-minion": "com.saltstack.salt.minion", "salt-master": "com.saltstack.salt.master", "salt-api": "com.saltstack.salt.api", "salt-syndic": "com.saltstack.salt.syndic", } def __virtual__(): """ Only for macOS with launchctl """ if not salt.utils.platform.is_darwin(): return ( False, "Failed to load the mac_service module:\nOnly available on macOS systems.", ) if not salt.utils.path.which("launchctl"): return ( False, "Failed to load the mac_service module:\n" 'Required binary not found: "launchctl"', ) if not salt.utils.path.which("plutil"): return ( False, "Failed to load the mac_service module:\n" 'Required binary not found: "plutil"', ) if Version(__grains__["osrelease"]) < Version("10.11"): return ( False, "Failed to load the mac_service module:\nRequires macOS 10.11 or newer", ) return __virtualname__ def _name_in_services(name, services): """ Checks to see if the given service is in the given services. :param str name: Service label, file name, or full path :param dict services: The currently available services. :return: The service information for the service, otherwise an empty dictionary :rtype: dict """ if name in services: # Match on label return services[name] for service in services.values(): if service["file_path"].lower() == name: # Match on full path return service basename, ext = os.path.splitext(service["file_name"]) if basename.lower() == name: # Match on basename return service return dict() def _get_service(name): """ Get information about a service. If the service is not found, raise an error :param str name: Service label, file name, or full path :return: The service information for the service, otherwise an Error :rtype: dict """ services = __utils__["mac_utils.available_services"]() # fix the name differences between platforms # salt-minion becomes com.saltstack.salt.minion name = SALT_MAC_SERVICES.get(name, name).lower() service = _name_in_services(name, services) # if we would the service we can return it if service: return service # if we got here our service is not available, now we can check to see if # we received a cached batch of services, if not we did a fresh check # so we need to raise that the service could not be found. try: if not __context__["using_cached_services"]: raise CommandExecutionError(f"Service not found: {name}") except KeyError: pass # if we can't find a service and we are being run from a service.dead # state then there is no reason to check again. # fixes https://github.com/saltstack/salt/issues/57907 if __context__.get("service.state") == "dead": raise CommandExecutionError(f"Service not found: {name}") # we used a cached version to check, a service could have been made # between now and then, we should refresh our available services. services = __utils__["mac_utils.available_services"](refresh=True) # check to see if we found the service we are looking for. service = _name_in_services(name, services) if not service: # Could not find the service after refresh raise. raise CommandExecutionError(f"Service not found: {name}") # found it :) return service def _always_running_service(name): """ Check if the service should always be running based on the KeepAlive Key in the service plist. :param str name: Service label, file name, or full path :return: True if the KeepAlive key is set to True, False if set to False or not set in the plist at all. :rtype: bool .. versionadded:: 2019.2.0 """ # get all the info from the launchctl service service_info = show(name) # get the value for the KeepAlive key in service plist try: keep_alive = service_info["plist"]["KeepAlive"] except KeyError: return False # check if KeepAlive is True and not just set. if isinstance(keep_alive, dict): # check for pathstate for _file, value in keep_alive.get("PathState", {}).items(): if value is True and os.path.exists(_file): return True elif value is False and not os.path.exists(_file): return True if keep_alive is True: return True return False def _get_domain_target(name, service_target=False): """ Returns the domain/service target and path for a service. This is used to determine whether or not a service should be loaded in a user space or system space. :param str name: Service label, file name, or full path :param bool service_target: Whether to return a full service target. This is needed for the enable and disable subcommands of /bin/launchctl. Defaults to False :return: Tuple of the domain/service target and the path to the service. :rtype: tuple .. versionadded:: 2019.2.0 """ # Get service information service = _get_service(name) # get the path to the service path = service["file_path"] # most of the time we'll be at the system level. domain_target = "system" # check if a LaunchAgent as we should treat these differently. if "LaunchAgents" in path: # Get the console user so we can service in the correct session uid = __utils__["mac_utils.console_user"]() domain_target = f"gui/{uid}" # check to see if we need to make it a full service target. if service_target is True: domain_target = "{}/{}".format(domain_target, service["plist"]["Label"]) return (domain_target, path) def _launch_agent(name): """ Checks to see if the provided service is a LaunchAgent :param str name: Service label, file name, or full path :return: True if a LaunchAgent, False if not. :rtype: bool .. versionadded:: 2019.2.0 """ # Get the path to the service. path = _get_service(name)["file_path"] if "LaunchAgents" not in path: return False return True def show(name): """ Show properties of a launchctl service :param str name: Service label, file name, or full path :return: The service information if the service is found :rtype: dict CLI Example: .. code-block:: bash salt '*' service.show org.cups.cupsd # service label salt '*' service.show org.cups.cupsd.plist # file name salt '*' service.show /System/Library/LaunchDaemons/org.cups.cupsd.plist # full path """ return _get_service(name) def launchctl(sub_cmd, *args, **kwargs): """ Run a launchctl command and raise an error if it fails :param str sub_cmd: Sub command supplied to launchctl :param tuple args: Tuple containing additional arguments to pass to launchctl :param dict kwargs: Dictionary containing arguments to pass to ``cmd.run_all`` :param bool return_stdout: A keyword argument. If true return the stdout of the launchctl command :return: ``True`` if successful, raise ``CommandExecutionError`` if not, or the stdout of the launchctl command if requested :rtype: bool, str CLI Example: .. code-block:: bash salt '*' service.launchctl debug org.cups.cupsd """ return __utils__["mac_utils.launchctl"](sub_cmd, *args, **kwargs) def list_(name=None, runas=None): """ Run launchctl list and return the output :param str name: The name of the service to list :param str runas: User to run launchctl commands :return: If a name is passed returns information about the named service, otherwise returns a list of all services and pids :rtype: str CLI Example: .. code-block:: bash salt '*' service.list salt '*' service.list org.cups.cupsd """ if name: # Get service information and label service = _get_service(name) label = service["plist"]["Label"] # we can assume if we are trying to list a LaunchAgent we need # to run as a user, if not provided, we'll use the console user. if not runas and _launch_agent(name): runas = __utils__["mac_utils.console_user"](username=True) # Collect information on service: will raise an error if it fails return launchctl("list", label, return_stdout=True, runas=runas) # Collect information on all services: will raise an error if it fails return launchctl("list", return_stdout=True, runas=runas) def enable(name, runas=None): """ Enable a launchd service. Raises an error if the service fails to be enabled :param str name: Service label, file name, or full path :param str runas: User to run launchctl commands :return: ``True`` if successful or if the service is already enabled :rtype: bool CLI Example: .. code-block:: bash salt '*' service.enable org.cups.cupsd """ # Get the domain target. enable requires a full <service-target> service_target = _get_domain_target(name, service_target=True)[0] # Enable the service: will raise an error if it fails return launchctl("enable", service_target, runas=runas) def disable(name, runas=None): """ Disable a launchd service. Raises an error if the service fails to be disabled :param str name: Service label, file name, or full path :param str runas: User to run launchctl commands :return: ``True`` if successful or if the service is already disabled :rtype: bool CLI Example: .. code-block:: bash salt '*' service.disable org.cups.cupsd """ # Get the service target. enable requires a full <service-target> service_target = _get_domain_target(name, service_target=True)[0] # disable the service: will raise an error if it fails return launchctl("disable", service_target, runas=runas) def start(name, runas=None): """ Start a launchd service. Raises an error if the service fails to start .. note:: To start a service in macOS the service must be enabled first. Use ``service.enable`` to enable the service. :param str name: Service label, file name, or full path :param str runas: User to run launchctl commands :return: ``True`` if successful or if the service is already running :rtype: bool CLI Example: .. code-block:: bash salt '*' service.start org.cups.cupsd """ # Get the domain target. domain_target, path = _get_domain_target(name) # Load (bootstrap) the service: will raise an error if it fails return launchctl("bootstrap", domain_target, path, runas=runas) def stop(name, runas=None): """ Stop a launchd service. Raises an error if the service fails to stop .. note:: Though ``service.stop`` will unload a service in macOS, the service will start on next boot unless it is disabled. Use ``service.disable`` to disable the service :param str name: Service label, file name, or full path :param str runas: User to run launchctl commands :return: ``True`` if successful or if the service is already stopped :rtype: bool CLI Example: .. code-block:: bash salt '*' service.stop org.cups.cupsd """ # Get the domain target. domain_target, path = _get_domain_target(name) # Stop (bootout) the service: will raise an error if it fails return launchctl("bootout", domain_target, path, runas=runas) def restart(name, runas=None): """ Unloads and reloads a launchd service. Raises an error if the service fails to reload :param str name: Service label, file name, or full path :param str runas: User to run launchctl commands :return: ``True`` if successful :rtype: bool CLI Example: .. code-block:: bash salt '*' service.restart org.cups.cupsd """ # Restart the service: will raise an error if it fails if __salt__["service.loaded"](name, runas=runas): __salt__["service.stop"](name, runas=runas) return __salt__["service.start"](name, runas=runas) def status(name, sig=None, runas=None): """ Return the status for a service. .. note:: Previously this function would return a PID for a running service with a PID or 'loaded' for a loaded service without a PID. This was changed to have better parity with other service modules that return True/False. :param str name: Used to find the service from launchctl. Can be the service Label, file name, or path to the service file. (normally a plist) :param str sig: Find the service with status.pid instead. Note that ``name`` must still be provided. :param str runas: User to run launchctl commands. :return: True if running, otherwise False. :rtype: str CLI Example: .. code-block:: bash salt '*' service.status cups """ # Find service with ps if sig: return __salt__["status.pid"](sig) try: _get_service(name) except CommandExecutionError as msg: log.error(msg) return False if not runas and _launch_agent(name): runas = __utils__["mac_utils.console_user"](username=True) try: output = __salt__["service.list"](name, runas=runas) except CommandExecutionError: return False # we should only check for a PID if it's supposed to have one. # If we can't find a PID then something is wrong with the service. if _always_running_service(name): return True if '"PID" =' in output else False return True def available(name): """ Check that the given service is available. :param str name: The name of the service :return: True if the service is available, otherwise False :rtype: bool CLI Example: .. code-block:: bash salt '*' service.available com.openssh.sshd """ try: _get_service(name) return True except CommandExecutionError: return False def missing(name): """ The inverse of service.available Check that the given service is not available. :param str name: The name of the service :return: True if the service is not available, otherwise False :rtype: bool CLI Example: .. code-block:: bash salt '*' service.missing com.openssh.sshd """ return not available(name) def enabled(name, runas=None): """ Check if the specified service is enabled (not disabled, capable of being loaded/bootstrapped). .. note:: Previously this function would see if the service is loaded via ``launchctl list`` to determine if the service is enabled. This was not an accurate way to do so. The new behavior checks to make sure its not disabled to determine the status. Please use ``service.loaded`` for the previous behavior. :param str name: The name of the service to look up. :param str runas: User to run launchctl commands. :return: True if the specified service enabled, otherwise False :rtype: bool CLI Example: .. code-block:: bash salt '*' service.enabled org.cups.cupsd """ # There isn't a direct way to get enabled, but if its not disabled # then its enabled. return not __salt__["service.disabled"](name, runas) def disabled(name, runas=None, domain="system"): """ Check if the specified service is not enabled. This is the opposite of ``service.enabled`` :param str name: The name to look up :param str runas: User to run launchctl commands :param str domain: domain to check for disabled services. Default is system. :return: True if the specified service is NOT enabled, otherwise False :rtype: bool CLI Example: .. code-block:: bash salt '*' service.disabled org.cups.cupsd """ domain = _get_domain_target(name, service_target=True)[0] disabled = launchctl("print-disabled", domain, return_stdout=True, runas=runas) for service in disabled.split("\n"): if name in service: srv_name = service.split("=>")[0].split('"')[1] status = service.split("=>")[1] if name != srv_name: pass else: matches = ["true", "disabled"] return True if any([x in status.lower() for x in matches]) else False return False def get_all(runas=None): """ Return a list of services that are enabled or available. Can be used to find the name of a service. :param str runas: User to run launchctl commands :return: A list of all the services available or enabled :rtype: list CLI Example: .. code-block:: bash salt '*' service.get_all """ # Get list of enabled services enabled = get_enabled(runas=runas) # Get list of all services available = list(__utils__["mac_utils.available_services"]().keys()) # Return composite list return sorted(set(enabled + available)) def get_enabled(runas=None): """ Return a list of all services that are enabled. Can be used to find the name of a service. :param str runas: User to run launchctl commands :return: A list of all the services enabled on the system :rtype: list CLI Example: .. code-block:: bash salt '*' service.get_enabled """ # Collect list of enabled services stdout = list_(runas=runas) service_lines = [line for line in stdout.splitlines()] # Construct list of enabled services enabled = [] for line in service_lines: # Skip header line if line.startswith("PID"): continue pid, status, label = line.split("\t") enabled.append(label) return sorted(set(enabled)) def loaded(name, runas=None): """ Check if the specified service is loaded. :param str name: The name of the service to look up :param str runas: User to run launchctl commands :return: ``True`` if the specified service is loaded, otherwise ``False`` :rtype: bool CLI Example: .. code-block:: bash salt '*' service.loaded org.cups.cupsd """ # Try to list the service. If it can't be listed, it's not enabled try: __salt__["service.list"](name=name, runas=runas) return True except CommandExecutionError: return False