PNG  IHDRxsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<,tEXtComment File Manager

File Manager

Path: /opt/alt/python311/lib/python3.11/site-packages/pyroute2/

Viewing File: process.py

import builtins
import gc
import json
import logging
import multiprocessing
import os
import select
import signal
import socket
import struct
from collections import namedtuple
from typing import Any, Callable, Optional, Union

from pyroute2 import config
from pyroute2.common import USE_DEFAULT_TIMEOUT
from pyroute2.netlink import exceptions as pyroute2_exceptions

log = logging.getLogger(__name__)

ChildProcessReturnValue = namedtuple(
    'ChildProcessReturnValue', ('payload', 'fds')
)
ChildFuncReturnType = Union[None, ChildProcessReturnValue, bytearray, bytes]


def wrapper(
    ctrl: socket.socket,
    func: Callable[..., ChildFuncReturnType],
    argv: list[Any],
) -> None:
    '''Child function wrapper.

    This code will be executed in the child process after running fork().
    The internal data structures might be damaged, so the function `func`
    must be as simple as possible, and use only local variables of simple
    types.

    The garbage collector should be disabled as well to minimize possible
    deadlocks.

    If process doesn't response in time, it will get killed.
    '''
    gc.disable()
    if config.disable_mp_signal:
        signal.signal(signal.SIGINT, signal.default_int_handler)
        signal.signal(signal.SIGTERM, signal.SIG_DFL)
    payload: bytes = b''
    ret_data: bytes = b''
    fds: list[int] = []
    sockets: list[socket.socket] = []
    try:
        ret = func(*argv)
        if isinstance(ret, bytes):
            ret_data = ret
        if isinstance(ret, ChildProcessReturnValue):
            ret_data, sockets = ret
        if isinstance(ret_data, bytearray):
            ret_data = bytes(ret_data)
        if not isinstance(ret_data, bytes):
            raise TypeError('return values not supported')
        payload = struct.pack('B', 2) + ret_data
        if sockets:
            fds = [x.fileno() for x in sockets]
    except Exception as e:
        payload = struct.pack('B', 1) + json.dumps(
            {'exception': e.__class__.__name__, 'options': e.args}
        ).encode('utf-8')
        fds = []
    finally:
        socket.send_fds(ctrl, [payload], fds, len(fds))


class ChildProcess:
    ctrl_r: socket.socket
    ctrl_w: socket.socket

    def __init__(
        self, target: Callable[..., ChildFuncReturnType], args: list[Any]
    ):
        self._mode: str = config.child_process_mode
        self._target: Callable[..., ChildFuncReturnType] = target
        self._args: list[Any] = args
        self._proc: Optional[multiprocessing.Process] = None
        self._running: bool = False
        self._exitcode: Optional[int] = None
        self._pid: Optional[int] = None

    def close(self):
        self.stop()

    def __enter__(self):
        self.run()
        return self

    def __exit__(self, *_):
        self.close()

    @property
    def mode(self):
        return self._mode

    @property
    def pid(self):
        return self._pid

    def communicate(
        self, timeout: int = USE_DEFAULT_TIMEOUT
    ) -> tuple[bytes, list[int]]:
        '''Communicate with the child process.

        Raises:

        * struct.error -- error unpacking response from the child process
        * OSError -- OS level error communicating with the child process
        * TimeoutError -- the child process is alive, but doesn't response
        * RuntimeError -- the child process is dead or not started
        * TypeError -- error loading propagated exception
        '''
        if not self._running:
            raise RuntimeError('child process not started yet')
        if timeout == USE_DEFAULT_TIMEOUT:
            timeout = config.default_communicate_timeout

        poll = select.poll()
        poll.register(self.ctrl_r, select.POLLIN)
        poll_results = poll.poll(timeout * 1000)  # convert to microseconds
        poll.unregister(self.ctrl_r)
        if not poll_results:
            # no data received within timeout
            # 1. the child process is killed
            # 2. the child process is stuck
            #
            # So first check the process, if it is killed, raise
            # RuntimeError, otherwise raise TimeoutError.
            #
            c_pid, w_status = os.waitpid(self.pid, os.WNOHANG)
            if c_pid != 0:
                # process is dead
                self._exitcode = os.waitstatus_to_exitcode(w_status)
                raise RuntimeError(
                    f'child process is dead, status: {self.exitcode}'
                )
            # process is alive, but stuck, kill it
            self.stop(kill=True, reason='no response from the child')
            raise TimeoutError(f'no response from the child pid {self.pid}')

        ret_data = b''
        # raises OSError
        (raw_data, fds, _, _) = socket.recv_fds(self.ctrl_r, 1024, 1)
        # get the return type
        # raises struct.error
        (ret_type,) = struct.unpack('B', raw_data[:1])
        raw_data = raw_data[1:]
        if ret_type == 1:
            # exception
            payload = json.loads(raw_data.decode('utf-8'))
            if not set(payload.keys()) == set(('exception', 'options')):
                raise TypeError('error loading child exception')
            if payload.get('exception') is not None:
                error_class = getattr(builtins, payload['exception'], None)
                if error_class is None:
                    error_class = getattr(
                        pyroute2_exceptions, payload['exception'], None
                    )
                if error_class is None:
                    error_class = Exception
                if not issubclass(error_class, Exception):
                    raise TypeError('error loading child error')
                raise error_class(*payload['options'])
        elif ret_type == 2:
            # raw_data
            ret_data = raw_data
        return ret_data, fds

    def get_data(self, timeout: int = USE_DEFAULT_TIMEOUT) -> bytes:
        return self.communicate(timeout)[0]

    def get_fds(self, timeout: int = USE_DEFAULT_TIMEOUT) -> list[int]:
        return self.communicate(timeout)[1]

    @property
    def proc(self) -> multiprocessing.Process:
        if self._proc is None:
            raise RuntimeError('not started')
        return self._proc

    @proc.setter
    def proc(self, value: Optional[multiprocessing.Process]) -> None:
        self._proc = value

    def _unsupported(self) -> Exception:
        return TypeError('unsupported mode')

    def run(self) -> None:
        if self._running:
            return
        self._running = True
        self.ctrl_r, self.ctrl_w = socket.socketpair(
            socket.AF_UNIX, socket.SOCK_DGRAM
        )
        if self.mode == 'fork':
            self._pid = os.fork()
            if self._pid == 0:
                wrapper(self.ctrl_w, self._target, self._args)
                os._exit(0)
        elif self.mode == 'mp':
            self.proc = multiprocessing.Process(
                target=wrapper, args=[self.ctrl_w, self._target, self._args]
            )
            self.proc.start()
            self._pid = self.proc.pid
        else:
            raise self._unsupported()

    def stop(self, kill: bool = False, reason: Optional[str] = None) -> None:
        if not self._running:
            return
        self._running = False
        self.ctrl_r.close()
        self.ctrl_w.close()
        if config.force_gc:
            gc.collect()
        if self.mode == 'fork':
            try:
                if kill:
                    os.kill(self.pid, signal.SIGKILL)
                else:
                    os.kill(self.pid, signal.SIGTERM)
                _, status = os.waitpid(self.pid, 0)
                self._exitcode = os.waitstatus_to_exitcode(status)
            except ProcessLookupError:
                # the child process has already exited
                pass
        elif self.mode == 'mp':
            if kill:
                self.proc.kill()
            else:
                self.proc.terminate()
            self.proc.join()
        else:
            raise self._unsupported()
        if reason is not None:
            log.warning(reason)

    @property
    def exitcode(self) -> Optional[int]:
        if self.mode == 'fork':
            return self._exitcode
        elif self.mode == 'mp':
            return self.proc.exitcode
        else:
            raise self._unsupported()
b IDATxytVսϓ22 A@IR :hCiZ[v*E:WũZA ^dQeQ @ !jZ'>gsV仿$|?g)&x-EIENT ;@xT.i%-X}SvS5.r/UHz^_$-W"w)Ɗ/@Z &IoX P$K}JzX:;` &, ŋui,e6mX ԵrKb1ԗ)DADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADA݀!I*]R;I2$eZ#ORZSrr6mteffu*((Pu'v{DIߔ4^pIm'77WEEE;vƎ4-$]'RI{\I&G :IHJ DWBB=\WR޽m o$K(V9ABB.}jѢv`^?IOȅ} ڶmG}T#FJ`56$-ھ}FI&v;0(h;Б38CӧOWf!;A i:F_m9s&|q%=#wZprrrla A &P\\СC[A#! {olF} `E2}MK/vV)i{4BffV\|ۭX`b@kɶ@%i$K z5zhmX[IXZ` 'b%$r5M4º/l ԃߖxhʔ)[@=} K6IM}^5k㏷݆z ΗÿO:gdGBmyT/@+Vɶ纽z񕏵l.y޴it뭷zV0[Y^>Wsqs}\/@$(T7f.InݺiR$푔n.~?H))\ZRW'Mo~v Ov6oԃxz! S,&xm/yɞԟ?'uaSѽb,8GלKboi&3t7Y,)JJ c[nzӳdE&KsZLӄ I?@&%ӟ۶mSMMњ0iؐSZ,|J+N ~,0A0!5%Q-YQQa3}$_vVrf9f?S8`zDADADADADADADADADAdqP,تmMmg1V?rSI꒟]u|l RCyEf٢9 jURbztѰ!m5~tGj2DhG*{H9)꒟ר3:(+3\?/;TUݭʴ~S6lڧUJ*i$d(#=Yݺd{,p|3B))q:vN0Y.jkק6;SɶVzHJJЀ-utѹսk>QUU\޲~]fFnK?&ߡ5b=z9)^|u_k-[y%ZNU6 7Mi:]ۦtk[n X(e6Bb."8cۭ|~teuuw|ήI-5"~Uk;ZicEmN/:]M> cQ^uiƞ??Ңpc#TUU3UakNwA`:Y_V-8.KKfRitv޲* 9S6ֿj,ՃNOMߤ]z^fOh|<>@Å5 _/Iu?{SY4hK/2]4%it5q]GGe2%iR| W&f*^]??vq[LgE_3f}Fxu~}qd-ږFxu~I N>\;͗O֊:̗WJ@BhW=y|GgwܷH_NY?)Tdi'?խwhlmQi !SUUsw4kӺe4rfxu-[nHtMFj}H_u~w>)oV}(T'ebʒv3_[+vn@Ȭ\S}ot}w=kHFnxg S 0eޢm~l}uqZfFoZuuEg `zt~? b;t%>WTkķh[2eG8LIWx,^\thrl^Ϊ{=dž<}qV@ ⠨Wy^LF_>0UkDuʫuCs$)Iv:IK;6ֲ4{^6եm+l3>݆uM 9u?>Zc }g~qhKwڭeFMM~pМuqǿz6Tb@8@Y|jx](^]gf}M"tG -w.@vOqh~/HII`S[l.6nØXL9vUcOoB\xoǤ'T&IǍQw_wpv[kmO{w~>#=P1Pɞa-we:iǏlHo׈꒟f9SzH?+shk%Fs:qVhqY`jvO'ρ?PyX3lх]˾uV{ݞ]1,MzYNW~̈́ joYn}ȚF߾׮mS]F z+EDxm/d{F{-W-4wY듏:??_gPf ^3ecg ҵs8R2מz@TANGj)}CNi/R~}c:5{!ZHӋӾ6}T]G]7W6^n 9*,YqOZj:P?Q DFL|?-^.Ɵ7}fFh׶xe2Pscz1&5\cn[=Vn[ĶE鎀uˌd3GII k;lNmشOuuRVfBE]ۣeӶu :X-[(er4~LHi6:Ѻ@ԅrST0trk%$Č0ez" *z"T/X9|8.C5Feg}CQ%͞ˣJvL/?j^h&9xF`њZ(&yF&Iݻfg#W;3^{Wo^4'vV[[K';+mӍִ]AC@W?1^{එyh +^]fm~iԵ]AB@WTk̏t uR?l.OIHiYyԶ]Aˀ7c:q}ힽaf6Z~қm(+sK4{^6}T*UUu]n.:kx{:2 _m=sAߤU@?Z-Vކеz왍Nэ{|5 pڶn b p-@sPg]0G7fy-M{GCF'%{4`=$-Ge\ eU:m+Zt'WjO!OAF@ik&t݆ϥ_ e}=]"Wz_.͜E3leWFih|t-wZۍ-uw=6YN{6|} |*={Ѽn.S.z1zjۻTH]흾 DuDvmvK.`V]yY~sI@t?/ϓ. m&["+P?MzovVЫG3-GRR[(!!\_,^%?v@ҵő m`Y)tem8GMx.))A]Y i`ViW`?^~!S#^+ѽGZj?Vģ0.))A꨷lzL*]OXrY`DBBLOj{-MH'ii-ϰ ok7^ )쭡b]UXSְmռY|5*cֽk0B7镹%ڽP#8nȎq}mJr23_>lE5$iwui+ H~F`IjƵ@q \ @#qG0".0" l`„.0! ,AQHN6qzkKJ#o;`Xv2>,tێJJ7Z/*A .@fفjMzkg @TvZH3Zxu6Ra'%O?/dQ5xYkU]Rֽkق@DaS^RSּ5|BeHNN͘p HvcYcC5:y #`οb;z2.!kr}gUWkyZn=f Pvsn3p~;4p˚=ē~NmI] ¾ 0lH[_L hsh_ғߤc_њec)g7VIZ5yrgk̞W#IjӪv>՞y睝M8[|]\շ8M6%|@PZڨI-m>=k='aiRo-x?>Q.}`Ȏ:Wsmu u > .@,&;+!!˱tﭧDQwRW\vF\~Q7>spYw$%A~;~}6¾ g&if_=j,v+UL1(tWake:@Ș>j$Gq2t7S?vL|]u/ .(0E6Mk6hiۺzښOrifޱxm/Gx> Lal%%~{lBsR4*}{0Z/tNIɚpV^#Lf:u@k#RSu =S^ZyuR/.@n&΃z~B=0eg뺆#,Þ[B/?H uUf7y Wy}Bwegל`Wh(||`l`.;Ws?V@"c:iɍL֯PGv6zctM̠':wuW;d=;EveD}9J@B(0iհ bvP1{\P&G7D޴Iy_$-Qjm~Yrr&]CDv%bh|Yzni_ˆR;kg}nJOIIwyuL}{ЌNj}:+3Y?:WJ/N+Rzd=hb;dj͒suݔ@NKMԄ jqzC5@y°hL m;*5ezᕏ=ep XL n?מ:r`۵tŤZ|1v`V뽧_csج'ߤ%oTuumk%%%h)uy]Nk[n 'b2 l.=͜E%gf$[c;s:V-͞WߤWh-j7]4=F-X]>ZLSi[Y*We;Zan(ӇW|e(HNNP5[= r4tP &0<pc#`vTNV GFqvTi*Tyam$ߏWyE*VJKMTfFw>'$-ؽ.Ho.8c"@DADADADADADADADADA~j*֘,N;Pi3599h=goضLgiJ5փy~}&Zd9p֚ e:|hL``b/d9p? fgg+%%hMgXosج, ΩOl0Zh=xdjLmhݻoO[g_l,8a]٭+ӧ0$I]c]:粹:Teꢢ"5a^Kgh,&= =՟^߶“ߢE ܹS J}I%:8 IDAT~,9/ʃPW'Mo}zNƍ쨓zPbNZ~^z=4mswg;5 Y~SVMRXUյڱRf?s:w ;6H:ºi5-maM&O3;1IKeamZh͛7+##v+c ~u~ca]GnF'ټL~PPPbn voC4R,ӟgg %hq}@#M4IÇ Oy^xMZx ) yOw@HkN˖-Sǎmb]X@n+i͖!++K3gd\$mt$^YfJ\8PRF)77Wא!Cl$i:@@_oG I{$# 8磌ŋ91A (Im7֭>}ߴJq7ޗt^ -[ԩSj*}%]&' -ɓ'ꫯVzzvB#;a 7@GxI{j޼ƌ.LÇWBB7`O"I$/@R @eee@۷>}0,ɒ2$53Xs|cS~rpTYYY} kHc %&k.], @ADADADADADADADADA@lT<%''*Lo^={رc5h %$+CnܸQ3fҥK}vUVVs9G R,_{xˇ3o߾;TTTd}馛]uuuG~iԩ@4bnvmvfϞ /Peeeq}}za I~,誫{UWW뮻}_~YƍSMMMYχ֝waw\ďcxꩧtEƍկ_?۷5@u?1kNׯWzz/wy>}zj3 k(ٺuq_Zvf̘:~ ABQ&r|!%KҥKgԞ={<_X-z !CyFUUz~ ABQIIIjݺW$UXXDٳZ~ ABQƍecW$<(~<RSSvZujjjԧOZQu@4 8m&&&jԩg$ď1h ͟?_{768@g =@`)))5o6m3)ѣƌJ;wҿUTT /KZR{~a=@0o<*狔iFɶ[ˎ;T]]OX@?K.ۈxN pppppppppppppppppPfl߾] ,{ァk۶mڿo5BTӦMӴiӴ|r DB2e|An!Dy'tkΝ[A $***t5' "!駟oaDnΝ:t֭[gDШQ06qD;@ x M6v(PiizmZ4ew"@̴ixf [~-Fٱc&IZ2|n!?$@{[HTɏ#@hȎI# _m(F /6Z3z'\r,r!;w2Z3j=~GY7"I$iI.p_"?pN`y DD?: _  Gÿab7J !Bx@0 Bo cG@`1C[@0G @`0C_u V1 aCX>W ` | `!<S `"<. `#c`?cAC4 ?c p#~@0?:08&_MQ1J h#?/`7;I  q 7a wQ A 1 Hp !#<8/#@1Ul7=S=K.4Z?E_$i@!1!E4?`P_  @Bă10#: "aU,xbFY1 [n|n #'vEH:`xb #vD4Y hi.i&EΖv#O H4IŶ}:Ikh @tZRF#(tXҙzZ ?I3l7q@õ|ۍ1,GpuY Ꮿ@hJv#xxk$ v#9 5 }_$c S#=+"K{F*m7`#%H:NRSp6I?sIՖ{Ap$I$I:QRv2$Z @UJ*$]<FO4IENDB`