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

File Manager

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

Viewing File: server.py

import asyncio
import json

from pyroute2.netlink.core import AsyncCoreSocket, CoreMessageQueue
from pyroute2.netlink.coredata import CoreConfig, CoreSocketSpec
from pyroute2.plan9 import (
    Marshal9P,
    Plan9Exit,
    Stat,
    Tattach,
    Tauth,
    Tcall,
    Tclunk,
    Tcreate,
    Topen,
    Tread,
    Tremove,
    Tstat,
    Tversion,
    Twalk,
    Twrite,
    Twstat,
    msg_rattach,
    msg_rcall,
    msg_rclunk,
    msg_rerror,
    msg_ropen,
    msg_rread,
    msg_rstat,
    msg_rversion,
    msg_rwalk,
    msg_rwrite,
    msg_rwstat,
)
from pyroute2.plan9.filesystem import Filesystem, Session

data = str(dir())


def get_exception_args(exc):
    args = []
    if hasattr(exc, 'errno'):
        args.append(exc.errno)
        args.append(exc.strerror)
    return args


def route(rtable, request, state):
    def decorator(f):
        rtable[request] = f
        return f

    return decorator


class Plan9ServerProtocol(asyncio.Protocol):
    rtable = {}

    def __init__(self, on_con_lost, marshal, filesystem):
        self.transport = None
        self.session = None
        self.filesystem = filesystem
        self.marshal = marshal
        self.on_con_lost = on_con_lost

    @route(rtable, request=Tversion, state=(None,))
    def t_version(self, req):
        m = msg_rversion()
        m['header']['tag'] = 0xFFFF
        m['msize'] = req['msize']
        m['version'] = '9P2000'
        return m

    @route(rtable, request=Tauth, state=(Tversion,))
    def t_auth(self, req):
        m = msg_rerror()
        m['ename'] = 'no authentication required'
        return m

    @route(rtable, request=Tattach, state=(Tauth,))
    def t_attach(self, req):
        m = msg_rattach()
        root = self.session.filesystem.inodes[0]
        self.session.set_fid(req['fid'], root)
        m['qid'] = root.qid
        return m

    @route(rtable, request=Twalk, state=(Tattach,))
    def t_walk(self, req):
        m = msg_rwalk()
        inode = self.session.get_fid(req['fid'])
        wqid = []
        if len(req['wname']) == 0:
            self.session.set_fid(req['newfid'], inode)
        else:
            for name in req['wname']:
                if name == '.':
                    continue
                elif name == '..':
                    inode = inode.get_parent()
                else:
                    inode = inode.get_child(name)
                wqid.append(inode.qid)
        m['wqid'] = wqid
        self.session.set_fid(req['newfid'], inode)
        return m

    @route(rtable, request=Tstat, state=(Twalk,))
    def t_stat(self, req):
        m = msg_rstat()
        inode = self.session.get_fid(req['fid'])
        inode.sync()
        m['stat'] = inode.stat
        return m

    @route(rtable, request=Twstat, state=(Twalk,))
    def t_wstat(self, req):
        m = msg_rwstat()
        return m

    @route(rtable, request=Topen, state=(Twalk, Tstat))
    def t_open(self, req):
        m = msg_ropen()
        m['qid'] = self.session.get_fid(req['fid']).qid
        m['iounit'] = 8192
        return m

    @route(rtable, request=Tcall, state=(Twalk, Topen, Tstat))
    def t_call(self, req):
        m = msg_rcall()
        inode = self.session.get_fid(req['fid'])
        m['err'] = 255
        if Tcall in inode.callbacks:
            m = inode.callbacks[Tcall](self.session, inode, req, m)
        return m

    @route(rtable, request=Twrite, state=(Topen,))
    def t_write(self, req):
        m = msg_rwrite()
        inode = self.session.get_fid(req['fid'])
        if Twrite in inode.callbacks:
            return inode.callbacks[Twrite](self.session, inode, req, m)
        if inode.qid['type'] & 0x80:
            raise TypeError('can not call write() on dir')
        inode.data.seek(req['offset'])
        m['count'] = inode.data.write(req['data'])
        return m

    @route(rtable, request=Tread, state=(Topen,))
    def t_read(self, req):
        m = msg_rread()
        inode = self.session.get_fid(req['fid'])
        if Tread in inode.callbacks:
            return inode.callbacks[Tread](self.session, inode, req, m)
        if inode.qid['type'] & 0x80:
            data = bytearray()
            offset = 0
            for child in inode.children:
                offset = Stat.encode_into(data, offset, child.stat)
            data = data[req['offset'] : req['offset'] + req['count']]
        else:
            inode.data.seek(req['offset'])
            data = inode.data.read(req['count'])
        m['data'] = data
        return m

    @route(rtable, request=Tclunk, state=(Topen, Tstat, Twalk, Tread))
    def t_clunk(self, req):
        return msg_rclunk()

    @route(rtable, request=Tcreate, state=(Twalk,))
    def t_create(self, req):
        return self.permission_denied(req)

    @route(rtable, request=Tremove, state=(Twalk,))
    def t_remove(self, req):
        return self.permission_denied(req)

    def permission_denied(self, req):
        r_message = msg_rerror()
        r_message['ename'] = 'permission denied'
        r_message['header']['tag'] = req['header']['tag']
        return r_message

    def error(self, e, tag=0):
        r_message = msg_rerror()
        spec = {
            'class': e.__class__.__name__,
            'argv': get_exception_args(e),
            'str': str(e),
        }
        r_message['ename'] = json.dumps(spec)
        r_message['header']['tag'] = tag
        r_message.encode()
        self.transport.write(r_message.data)

    def data_received(self, data):
        for t_message in self.marshal.parse(data):
            tag = t_message['header']['tag']
            try:
                r_message = self.rtable[t_message['header']['type']](
                    self, t_message
                )
                r_message['header']['tag'] = tag
                r_message.encode()
            except Plan9Exit as e:
                self.error(e, tag)
                self.transport.abort()
                self.transport.close()
                return
            except Exception as e:
                self.error(e, tag)
                return
            self.transport.write(r_message.data)

    def connection_made(self, transport):
        self.transport = transport
        self.session = Session(self.filesystem)


class Plan9ServerSocket(AsyncCoreSocket):
    '''9p2000 server.

    Requires either an IP address to listen on, or an open
    `SOCK_STREAM` socket to operate. An IP example, suitable
    to establish IPC between processes in one network:

    .. testcode::

        from pyroute2 import Plan9ClientSocket, Plan9ServerSocket

        address = ('localhost', 8149)
        p9server = Plan9ServerSocket(address=address)
        p9client = Plan9ClientSocket(address=address)

    Server/client running on a `socketpair()` suitable
    for internal API within one process, or between
    parent/child processes:

    .. testcode::

        from socket import socketpair

        from pyroute2 import Plan9ClientSocket, Plan9ServerSocket

        server, client = socketpair()
        p9server = Plan9ServerSocket(use_socket=server)
        p9client = Plan9ClientSocket(use_socket=client)


    '''

    def __init__(self, address=None, use_socket=None, use_event_loop=None):
        self.spec = CoreSocketSpec(
            CoreConfig(
                tag_field='tag',
                target='localhost',
                netns=None,
                address=address,
                use_socket=use_socket is not None,
                use_event_loop=use_event_loop is not None,
            )
        )
        self.filesystem = Filesystem()
        self.marshal = Marshal9P()
        super().__init__(use_socket=use_socket, use_event_loop=use_event_loop)

    def register_function(
        self,
        func,
        inode,
        loader=json.loads,
        dumper=lambda x: json.dumps(x).encode('utf-8'),
    ):
        '''Register a function to an file.

        The file usage:

        * `write()`: write arguments for the call as a json dictionary of
          keyword arguments to the file data.
        * `read()`:
            1. if the arguments were written to the data, call the function
               and write the result to the file data
            2. read the file data and return to the client
        * `call()`: protocol extension, `Tcall` = 80, `Rcall` = 81, make
          this in one turn.

        .. testcode::
            :hide:

            from pyroute2.plan9 import Tcall, Rcall

            assert Tcall == 80
            assert Rcall == 81

        Registering a function:

        .. testcode::

            def communicate(a, b):
                return a + b


            def example_register():
                fd = p9server.filesystem.create('test_func')
                p9server.register_function(communicate, fd)

        Communication using Twrite/Tread:

        .. testcode::

            import json


            async def example_write():
                fid = await p9client.fid('test_func')
                await p9client.write(
                    fid,
                    json.dumps({"a": 17, "b": 25})
                )
                msg = await p9client.read(fid)
                response = json.loads(msg['data'])
                assert response == 42

        Same, using a command line 9p client from plan9port::

            $ echo '{"a": 17, "b": 25}' | 9p -a localhost:8149 write test_func
            $ 9p -a localhost:8149 read test_func
            42

        And using a mounted file system via FUSE client from plan9port::

            $ 9pfuse localhost:8149 mnt
            $ echo '{"a": 17, "b": 25}' >mnt/test_func
            $ cat mnt/test_func
            42

        And the same, but using Tcall:

        .. testcode::

            async def example_call():
                fid = await p9client.fid('test_func')
                response = await p9client.call(fid, argv=(17, 25))
                assert response == 42

        And finnaly run this code:

        .. testcode::

            async def main():
                server_task = await p9server.async_run()
                example_register()
                await p9client.start_session()
                await example_write()
                await example_call()
                server_task.cancel()

            asyncio.run(main())
        '''
        return inode.register_function(func, loader, dumper)

    async def setup_endpoint(self):
        if getattr(self.local, 'server', None) is not None:
            return
        self.local.msg_queue = CoreMessageQueue(event_loop=self.event_loop)
        if self.status['use_socket']:
            self.local.server = None
            self.local.transport, self.local.protocol = (
                await self.event_loop.create_connection(
                    lambda: Plan9ServerProtocol(
                        self.connection_lost, self.marshal, self.filesystem
                    ),
                    sock=self.use_socket,
                )
            )
        else:
            self.local.transport = None
            self.local.protocol = None
            self.local.server = await self.event_loop.create_server(
                lambda: Plan9ServerProtocol(
                    self.connection_lost, self.marshal, self.filesystem
                ),
                *self.status['address'],
            )

    async def async_run(self):
        '''Return the server asyncio task.

        Using this task one can stop the server:

        .. testcode::

            async def main():
                server = Plan9ServerSocket(address=('localhost', 8149))
                server_task = await server.async_run()
                # ... server is running here
                server_task.cancel()
                # ... server is stopped

            asyncio.run(main())

        To forcefully close all client connections and stop the server
        immediately from a registered function, one can pass this task
        to the function, cancel it, and raise `Plan9Exit()` exception:

        .. testcode::

            import functools

            from pyroute2.plan9 import Plan9Exit

            server_sock, client_sock = socketpair()


            def test_exit_func(context):
                if 'server_task' in context:
                    context['server_task'].cancel()
                    raise Plan9Exit('server stopped upon client request')
                return 'server starting, please wait'


            async def server():
                p9server = Plan9ServerSocket(use_socket=server_sock)
                context = {}

                inode = p9server.filesystem.create('stop')
                p9server.register_function(
                    functools.partial(test_exit_func, context),
                    inode
                )
                context['server_task'] = await p9server.async_run()

                try:
                    await context['server_task']
                except asyncio.exceptions.CancelledError:
                    pass

                assert context['server_task'].cancelled()

        .. testcode::
            :hide:

            async def client():
                p9client = Plan9ClientSocket(use_socket=client_sock)
                await p9client.start_session()
                fid = await p9client.fid('stop')
                try:
                    await p9client.call(fid)
                except Plan9Exit:
                    pass


            async def main():
                await asyncio.gather(server(), client())

            asyncio.run(main())
        '''
        await self.setup_endpoint()
        if self.status['use_socket']:
            return self.protocol.on_con_lost
        else:
            return asyncio.create_task(self.local.server.serve_forever())

    def run(self):
        '''A simple synchronous runner.

        Uses `event_loop.run_forever()`.
        '''
        self.event_loop.create_task(self.async_run())
        self.event_loop.run_forever()
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`