9MjdZddlZddlZddlZddlZddlZddlZddlZddlZddl Z ddl Z ddl Z ddl Z ddl mZmZmZmZmZddl mZmZmZmZddlmZddlZddlmZddlmZdd lm Z dd l!m"Z"m#Z#m$Z$dd l%m&Z&m'Z'dd l(m)Z)dd l*m+Z+m,Z,m-Z-ddl.m/Z/ddl0m1Z1ddl2m3Z3m4Z4e j5r ddl6m7Z7m8Z8m9Z9eGddZ:ej;e<Z=GddZ>dS)zJ This module contains contains classes implementing X-Ray Agent behaviour N)Threadcurrent_threadLockEventTimer)AnyOptionalDictTuple) dataclass)BoundedThreadExecutor)gettext) get_client)local_tasks_storagetasks_base_storagesafe_id_pattern) XRayError XRayAPIError) FaultDetector)open_local_storageflush_memory_storage get_task_ids)Task) extract_creds) dbm_storageget_current_cpu_throttling_time) SendClientSmartAdviceAPIClient APIClientc$eZdZUded<eed<dS)APIDataContainerr clienttaskN)__name__ __module__ __qualname____annotations__rH/opt/cloudlinux/venv/lib64/python3.11/site-packages/xray/agent/daemon.pyr"r"-s%  JJJJJr*r"c |eZdZdZdZdZdZdZej dzej dzej dzd d d fd Z d Z d Z dd.Z*d'J # #P_cdPd $+[8(/?.J**PSTUWbfgWgPhPh0C/N++T_  v/&.1ABBB=Gy">">D    % % ' ' ' "d.>TM^ _ _ _ co ! 0     " ,%,,,&*kkmm# G'$++D,KZYeffffz'''LLW$$&&&&& '$QJE!,%,,,                                 sZE%5E C10E 1=D1.E 0D11E  E% E E%E E%%E),E)cD|||\|d<|d<dS)zj Calculate throttling values and update given data with resulting throttling stat hitting_limitsthrottled_timeN)rK)rVdatat_key cpu_values r+add_limit_faultszAgent.add_limit_faults s1 :>9L9LUT]9^9^6 %5 6 6 6r*php_pid cpu_usage caller_uidctd||||j||||jdS)z Called when php request starts and sends us welcome request meaning that request started on the php side. z=Received request init trigger from php=%s uid=%s cpu_usage=%s)uidN)rpr_rKsaveflush)rVrrrs r+_handle_request_initzAgent._handle_request_inits[  SU\^hjsttt   ) DDD !!#####r* request_datac2|d||||tdt j|d|d|||dS)Nrz&[%s] Processing trace for task %s (%s)tracing_task_idurl)getrrpr_rnameprocess_request_data)rVrrrrs r+_handle_request_endzAgent._handle_request_ends   , - - 5  ! !, C C C 4    !   . / /   U # #    !!, ;;;;;r*i@g@g>@rrcJ t|\}}}n?#t$r2td|YdSwxYw|j5|j|d|jkr=td|| ddddS|j|xxdz cc<dddn #1swxYwY | |||||j5|j|xxdzcc<|j|dkr|j|=ddddS#1swxYwYdS#|j5|j|xxdzcc<|j|dkr|j|=dddw#1swxYwYwxYw)a Handle incoming connection :param connection: socket object usable to send and receive data on the connection :param workers_pool: pool where we can place tasks for the futher processing z1Failed to extract credentials, closing connectionNrz6Too many concurrent connections from uid=%s, rejectingr ) rrdrprerrPrOrr<_process_connection)rVrr_pid_uid_gids r+rz Agent.handle_incoming_connection=s ,Z88 D$    NNN O O O       FF   ' - -$((q11T5QQQWY]^^^  """  - - - - - - - -  !$ ' ' '1 , ' ' '  - - - - - - - - - - - - - - - 4  $ $ZtT J J J+ 4 4%d+++q0+++(.!33-d3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4+ 4 4%d+++q0+++(.!33-d3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4sj8AAAC>CC#&C#+E /EE  E F"/F F"F F"F F"rrc  t|}|jd}t  fd}t |j|}| ||} n#tj $rp} t dt| dtjiYd} ~ |dSd} ~ wt$t&jf$rHtd||Y|dSwxYw |n-#|wxYw r$td|j||dS| "||||j||| dS||||j||| | dS) Nignore)errorsc tjdS#t$rYdSwxYwN)setshutdownsocket SHUT_RDWRrd)rdeadline_exceededsr+_abort_on_deadlinez5Agent._process_connection.._abort_on_deadlineisY  ! ! # # # ##F$455555    s8 AAzJSON decode failed: %st_nameextraz5Connection read timed out or failed for pid=%s uid=%szAConnection read exceeded %ss deadline for pid=%s uid=%s, dropping)rrr)rrrr)r settimeout _READ_TIMEOUTmakefilerr_MAX_READ_DURATIONr read_inputjsonJSONDecodeErrorrprstrrrcancelrrdrtimeoutreis_set_submit_uid_jobrr) rVrrrr current_cpufileobjrwatchdog input_dataers ` @r+rzAgent._process_connection[s6d;; d0111!+!4!4H!4!E!E"GG      02DEE 11JJ#    LL13q66(NL\L\LaAbL c c c FFF OO           (    NNRTXZ^ _ _ _  OO              OO           OO            # # % %  NNSUYUlnrtx    F    dD$=tWbos !       (%' !     s<BFE1%=D"F5E1F0E11F*Gct|j||jvrdndz}t|j|j|z}|j|jz }t|jt ||S)aDynamic max-min fair-share cap for one uid, in outstanding jobs. MUST be called while holding _uid_jobs_lock (it reads _uid_jobs). active = number of currently-active uids (those with > 0 outstanding jobs). _uid_jobs is kept free of zero-count entries, so its size is the active set; _uid itself is counted even on its first job (it is about to become active), so a flood never under-counts itself out of a share. The cap is: max(min_floor, min(capacity - reserve, max(min_floor, capacity // active))) * active == 1 -> capacity // 1 == capacity, clamped to capacity-reserve (e.g. 80-16=64): a lone user is bounded only by the real queue.Full, no regression, yet still leaves `reserve` slots for a new tenant. * active == N -> capacity // N: a fair split; a flooder is held to its share going forward and converges to it as its jobs drain. * min_floor stops the cap collapsing to 0 with very many active uids. * the OUTER max(min_floor, ...) keeps the cap >= min_floor even when the capacity - reserve clamp is <= 0 (e.g. maxqueuesize == 0, so capacity == max_workers == reserve): without it the cap would be 0 and _submit_uid_job would drop EVERY request from EVERY uid (total self-DoS). On such a tiny pool the per-uid cap is min_floor and the executor's real queue.Full (BoundedSemaphore = maxqueuesize + max_workers) is the backstop. rr )lenrQr>r?r=r@min)rVractive fair_share reserve_clamps r+_effective_uid_capzAgent._effective_uid_capsl6T^$$TT^-C-CK$*=*GHH +dm; 4?C z$B$BCCCr*c j5}jd|krqtdjd|t jjvrdndzjj ddddSjxxdz cc<xj dz c_ dddn #1swxYwYfd} | |dS#tj $r3 tdYdSt$r wxYw)uM Submit a worker job into the shared workers_pool, enforcing a DYNAMIC max-min per-UID fair share of the real pool capacity, UNCONDITIONALLY. At submit time the per-UID cap is recomputed from the number of currently-active uids (see _effective_uid_cap): a sole active uid gets the whole capacity minus a reserved headroom (no regression — bounded only by the real queue.Full), while with N active uids each gets capacity // N (fair split). There is NO pressure gate: the cap alone provides both no-regression for a lone user and anti-monopolization under contention, and a flooder cannot defeat it by supplying its own load. The reserved headroom guarantees a newly-arriving tenant always finds free slots even when one uid has filled its share. rzPer-UID fair-share drop: uid=%s holds %s outstanding jobs (dynamic fair-share cap %s for %s active uid(s), pool capacity %s, reserve %s); dropping this request to protect other tenantsr Nct diS#wxYw)Nr))_release_uid_job)rfnkwargsrVsr+_jobz#Agent._submit_uid_job.._jobsH ,r||F||%%d++++%%d++++s 7zRequest %s was rejected because our workers thread pool is full of tasks. Increase queuemaxsize or max_threads in configuration.)rSrrQrrprerr=r@rRrrrrr BaseException)rVrrrr effective_caprs` ``` r+rzAgent._submit_uid_jobs%  & & 33D99M~!!$**m;;EN&&tQ//!''0F0F11AN'M    & & & & & & & & N4 A %  A % # & & & & & & & & & & & & & & &, , , , , , , , ,      % % % % %z     ! !$ ' ' ' LL[    FF     ! !$ ' ' '  s*BC#2%C##C'*C'7D>E/ E/c|j5|j|xxdzcc<|j|dkr|j|=|jdkr|xjdzc_ddddS#1swxYwYdS)a*Decrement the per-UID and total outstanding-job counters under the lock. The total counter is floored at 0 so it can never underflow if a release is somehow called more often than expected, and zero-count per-UID entries are deleted to keep _uid_jobs from growing unbounded.r rN)rSrQrR)rVrs r+rzAgent._release_uid_jobs   * * N4 A % ~d#q((N4(#a''$$)$$  * * * * * * * * * * * * * * * * * *sA AA#&A#fileioc$||j}tdt |t |dkrdSt j|dS)zy Read input data and return decoded json :param fileio: a file-like object providing read method zReceived %d bytesrNF)strict)read_MAX_READ_SIZErpdebugrstriprloads)rVrrs r+rzAgent.read_inputsp {{4.// (#d))444 tzz||   ! ! Fz$**,,u5555r* fake_task_idr c tt5} ||}nA#t$r4t t d||dwxYw dddn #1swxYwYn>#t$r1}t t dt|zd}~wwxYw| |j |S)z< Initialize MongoDB client for current task z9Cannot resolve tracing_task_id: no match found in storage)idall_idsrNz"Cannot resolve tracing_task_id: %s)r6r) rrdecodeKeyErrorr_keys RuntimeErrorrrGr8)rVr task_storagereal_idrs r+instantiate_mongo_clientzAgent.instantiate_mongo_clientsE N011 \*<8??AAGG#UVV%1l>O>O>Q>QRR                N N NABCCc!ffLMM M N&&g&VVVsNB B3B>A11B5 B BB B B C,CCctd|j|j|}||j|jfS||}|}td||||j5|j|}||j|jfcdddSt|j|j kritt|j}t dt|j||j|t|||j|<dddn #1swxYwY||fS)z Returns a client and task from cache of API data or initialize client and GET task from MongoDB and add to cache zCached API data: %sNz,Adding new container in cache: %s --> %s, %sz9api_data_cache at capacity (%d), evicting oldest entry %s)r#r$)rprrJrr#r$rget_taskrHr_MAX_CACHE_ENTRIESnextiterr_popr")rVr cached_datary_t oldest_keys r+rqzAgent.get_cached_or_load s  *D,?@@@)--l;;  "%{'77 711,??    ! ! C\SUW`aaa  % \ \ -11,??K&");+;; \ \ \ \ \ \ \ \4&''4+BBB!$t':";";<<  OQTUYUhQiQiku#'' 3330@ XZ0[0[0[D  - \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ "}s*E:BE::E>E>c tt5}d|D}dddn #1swxYwYn+#t$rtdYdSwxYwt |jD]c}|j5||jvr9||vr5t d||j |dddn #1swxYwYddS)z Cleanup an API data im-memory cache dict in order not store inactive (stopped, already completed) tasks there c6g|]}|Sr))r).0ks r+ z0Agent.cleanup_api_data_cache..Hs HHHq HHHr*Nz,Unable to cleanup cache, storage unavailablez!Cleaning up inactive container %s) rrrrrprelistrJrHr_r)rVr active_tasks_tasks r+rzAgent.cleanup_api_data_cacheAs  011 I\HHL4E4E4G4GHHH  I I I I I I I I I I I I I I I    NNI J J J FF $-224455 3 3E) 3 3D///E4M4MKK CUKKK'++E222 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3sFA A A AA A A $A54A5'AC66C: =C: c|d}|tj|std|dStjtt||}tj |std||dS| |\}}t d|j t|d|j5}||j|jdkr(t d  ddddS|||}t d || t d ||n#t*$rYnwxYw|xjd z c_||jdddn #1swxYwY|jr||j|jd kr"|jdkr||dSdSdS)aK Increment request ID in /usr/share/alt-php-xray/requests/{tracing_task_id} file Substitute request_id and tracing_task_id in request_data. Send request_data to ClickHouse :param request_data: original request data :param caller_uid: UID of the connecting process (from SO_PEERCRED) rNz*Rejecting request with invalid task id: %szSRejecting request from uid=%s for task %s: no matching task file in per-UID storagezProcessing task: %s)rrmrz*Tracing count is 0, nothing should be donezSending to ClickHousezSending to SmartAdvicer ro)rmatchrprerbpathjoinrrisfilerqr_rwr is_manualrrrnrvupdate_request_datarErFrrafake_idrs complete_task) rVrrrowner_task_filerr$rzupdated_request_datas r+rzAgent.process_request_dataSs$$56  !"(66 K\ZZZ gll+=s:P\]]O7>>/22 i  )),774 )4<888  -> ?t~ V V V QZa  ' '8O ' P P P!Q&& HIII  Q Q Q Q Q Q Q Q$(#;#;L$#O#O KK/ 0 0 0   1 2 2 2  4555##$89999      # #q ( # #  ' '8O ' P P P- Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q0 > 7  ( ( 6 6 6 ?f $ $);q)@)@   t $ $ $ $ $ % $)@)@s>AH AH /GH  GH G.H  HHrr$c |jdz|d<|j|d<|dD]C}|jdz|d<|j|d<|ddkr||d|d<Dtd|d|dtd t|dg|d|S) z Substitute request_id and tracing_task_id :param data: original input :param task: a Task instance :return: updated input r request_idrrtype mysql_queryqueryz5Input updated: tracing_task_id = %s & request_id = %sz2Updated input: %d item(s) for tracing_task_id = %s)rurw hide_symbolsrpr_rrr)rVrr$items r+r zAgent.update_request_datas "/!3\"&, L A AD!%!3a!7D &*lD" #F|},, $ 1 1$w- @ @W  CTXXN_E`E`bfbjbjkwbxbx     @#dhhvWYFZFZB[B[]a]e]efw]x]x    r*r#rurvc4|||dS)z: Update task counters in mongodb instance )rvruN)update_counts_only)rVr#rurvs r+rtzAgent.update_countss# !! ]![[[[[r*rctd|jtjd||jdS)z_ Stop and complete request_qty task :param _task: tracing task to stop zTask %s should be completedr N)rpr_rwror}_run_complete_task_cmd)rVrs r+rzAgent.complete_tasksD  15=AAA 1  ##EM22222r*c tjddd|jd|gdtjdS#tjtjf$r'}t d||Yd}~dSd}~wwxYw)Nzcloudlinux-xray-managerstopz --system_idz--tracing_task_idx)rstderrzFailed to complete task %s: %s) subprocess check_outputr8PIPETimeoutExpiredCalledProcessErrorrpr)rVrwrs r+rzAgent._run_complete_task_cmds G  #*FM4;Pcelm!       ):+HI G G G LL97A F F F F F F F F F Gs,0A2 A--A2rc\d}tjd}tj|||S)zA Sanitize data in single quotes from MySQL query c|d}|d}d}tjd||d}|||p|S)zL Works with whole string in single or double quotes quotetrunccb|drdS|drdSdS)zE Works with characters inside quotes digit0symbolxN)group)inner_ms r+ inner_replz8Agent.hide_symbols..replacer..inner_repls@==))3]]8,,3r*z.((?P\d)|(?P[^0-9_:;\-/',. \\]))in_quote)r/resub)mqtr1 sanitizeds r+replacerz$Agent.hide_symbols..replacersx  A  A   PR\^_^e^efp^q^qrrI,,AF,, ,r*zH(?P['"])(?P.*?)((?\.{3})))r3compiler4)rr9patterns r+rzAgent.hide_symbolss7  - - -0*lmmvgx555r*r)FN)rN)2r%r&r'__doc__r~rrrpsutil cpu_countr[rgrkrarrboolr rNrrrrIrrrrrrrrrrio TextIOBaserrrrr rrqrrr rtrr staticmethodrr)r*r+r-r-6s! )(**Q.$F$&&*%V%''", $ Y5Y5Y5Y5v U U U Z Z ZVVVV2333*%%&-%T%RZ[^R_%ko%%%%N___ $ $C $C $XVY] $ $ $ $ <3 <3  ->7L>TW>_b> >>>>@DsDsDDDD@:,A::_c::::x *S *T * * * * 6 63 6 6 6 6WSW[WWWW$su[$=N7OB3333$8%8%8%38%RV8%8%8%8%tDT.\\K\\T\]`Ta\mq\\\\ 343D3333GGG6#6#666\666r*r-)?r<rLr@rr^rbrr3rArr!rotyping threadingrrrrrrr r r dataclassesr r=executorrxrayrrxray.apiclientrxray.internal.constantsrrrxray.internal.exceptionsrrrKrxray.internal.local_countersrrrxray.internal.typesrxray.internal.user_plugin_utilsrxray.internal.utilsrr TYPE_CHECKINGxray.apiclient.api_clientrrr r" getLoggerr%rpr-r)r*r+rRsr   @@@@@@@@@@@@@@------------!!!!!! ++++++%%%%%%\\\\\\\\\\<<<<<<<<))))))__________$$$$$$999999LLLLLLLL VUUUUUUUUUU    8 $ $g 6g 6g 6g 6g 6g 6g 6g 6g 6g 6r*