Server IP : 178.210.175.110  /  Your IP : 216.73.217.52
Web Server : Apache
System : Linux lin4.teknikdata.com 5.14.0-611.5.1.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Tue Nov 11 08:09:09 EST 2025 x86_64
User : naciustu ( 1040)
PHP Version : 8.2.35
Disable Function : exec,passthru,shell_exec,system
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF
Directory (0755) :  /../etc/cl.python/../cl.python/../utils/

[  Home  ][  C0mmand  ][  Upload File  ]

Current File : //../etc/cl.python/../cl.python/../utils/cloudlinux_bun_client.py
# coding:utf-8

# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2026 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT

"""Account-side client for the bun delivery socket.

Runs at the account's uid, inside its cage. It says one thing -- "deliver
the modules for this application" -- and reports the verdict. It cannot say
who it is: the kernel tells the daemon that over SO_PEERCRED, which is why
nothing here is worth forging.

The account-side Selector also uses it once, after a Selector install it
drove failed its restart/health check: `unhealthy` reports that, through
the same application lock, so the daemon can count it as broken.

Exit codes are the contract with npm_wrapper:

    0  delivered (for `unhealthy`: recorded)
    1  declined, for a reason the daemon named (printed to stderr)
    2  the daemon is unreachable
    3  classic npm: administrator policy (disabled or excluded), or, on
       `deliver`, a dependency source the Store does not serve
       (unsupported_source, registry_not_allowed), an engine limit
       (lockfile_too_large, too_many_packages) or an `npm ci` it cannot
       install exactly (lockfile_mismatch)

Only 3 selects classic npm, and only for those reasons. For a classic
`deliver` fallback the reason is the one line printed on stdout. Nothing retries
after an operational or delivery failure: a silent fallback is what let two
delivery defects sit behind healthy-looking numbers during this feature's
development (see *No fallback* in the design spec).
"""

from __future__ import absolute_import

import json
import os
import re
import socket
import struct
import sys

SOCKET_PATH = '/opt/cl-bun/run/delivery.sock'

# Long enough for a cold cache to fetch a large tree, short enough that a
# wedged daemon does not hold a terminal indefinitely.
# A client can wait in the bounded daemon queue before the Bun install and
# account-side rebuild begin; leave margin for socket and hand-over work too.
TIMEOUT = 2100


_MAJOR = re.compile(r'^[0-9]{1,3}$')


def shell_major(environ=os.environ):
    """The venv the shell is in, from CL_VIRTUAL_ENV, or None.

    npm_wrapper sources the venv's own `activate` before any phase runs,
    so this names the venv the account is actually working in -- which a
    version change may have left beside a newer one the selector records.
    The root half has to deliver where the user half resolved and linked,
    so the request carries it. Anything but a small number is left unsaid
    and the daemon falls back to the record."""
    venv = environ.get('CL_VIRTUAL_ENV', '')
    name = os.path.basename(venv)
    if venv and '/' in venv and _MAJOR.match(name) and 1 <= int(name) <= 999:
        return int(name)
    return None


def request(command, app_root, path=SOCKET_PATH, timeout=TIMEOUT, major=None,
            lock_fd=None, via=None, kind=None):
    """Returns the daemon's verdict as a dict, or raises OSError."""
    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    sock.settimeout(timeout)
    try:
        sock.connect(path)
        request = {'cmd': command, 'app_root': app_root}
        if major is not None:
            request['major'] = major
        if via is not None:
            request['via'] = via
        if kind is not None:
            request['kind'] = kind
        payload = json.dumps(request)
        message = (payload + '\n').encode('utf-8')
        if lock_fd is None:
            sock.sendall(message)
        else:
            sent = sock.sendmsg([message], [(socket.SOL_SOCKET, socket.SCM_RIGHTS,
                                             struct.pack('i', lock_fd))])
            if sent <= 0:
                raise OSError('could not send delivery lock descriptor')
            if sent < len(message):
                sock.sendall(message[sent:])
        chunks = []
        while True:
            chunk = sock.recv(65536)
            if not chunk:
                break
            chunks.append(chunk)
            if b'\n' in chunk:
                break
    finally:
        sock.close()
    raw = b''.join(chunks).split(b'\n', 1)[0]
    if not raw:
        raise OSError('empty reply from delivery service')
    return json.loads(raw.decode('utf-8'))


def request_delivery(app_root, path=SOCKET_PATH, timeout=TIMEOUT, major=None,
                     lock_fd=None, via=None, kind=None):
    return request('deliver', app_root, path, timeout, major, lock_fd, via,
                   kind)


def request_policy(app_root, path=SOCKET_PATH, timeout=TIMEOUT, major=None,
                   lock_fd=None):
    return request('policy', app_root, path, timeout, major, lock_fd)


def request_unshare(app_root, path=SOCKET_PATH, timeout=TIMEOUT, major=None,
                    lock_fd=None):
    return request('unshare', app_root, path, timeout, major, lock_fd)


def request_unhealthy(app_root, path=SOCKET_PATH, timeout=TIMEOUT, major=None,
                      lock_fd=None):
    return request('unhealthy', app_root, path, timeout, major, lock_fd)


def main(argv=None):
    argv = sys.argv[1:] if argv is None else argv
    if len(argv) == 1:
        command, app_root = 'deliver', argv[0]
    elif len(argv) == 2 and argv[0] in ('policy', 'unshare', 'unhealthy'):
        command, app_root = argv
    else:
        sys.stderr.write('usage: cloudlinux-bun-client '
                         '[policy|unshare|unhealthy] <app-root>\n')
        return 2
    try:
        raw_lock_fd = os.environ.get('CL_BUN_LOCK_FD')
        lock_fd = int(raw_lock_fd) if raw_lock_fd and raw_lock_fd.isdigit() else None
        request_fn = {
            'deliver': request_delivery,
            'policy': request_policy,
            'unshare': request_unshare,
            'unhealthy': request_unhealthy,
        }[command]
        kwargs = {'major': shell_major(), 'lock_fd': lock_fd}
        # npm_wrapper sets this only after validating the Selector's app lock
        # descriptor: the install is Selector-driven, counted as `panel`.
        if command == 'deliver' and os.environ.get('CL_BUN_VIA') == 'selector':
            kwargs['via'] = 'selector'
        # npm_wrapper sets this for a bare `npm ci` it routes to the Store.
        if command == 'deliver' and os.environ.get('CL_BUN_COMMAND') == 'ci':
            kwargs['kind'] = 'ci'
        verdict = request_fn(app_root, **kwargs)
    except (OSError, ValueError) as exc:
        sys.stderr.write('bun delivery unavailable: %s\n' % exc)
        return 2
    if verdict.get('status') == 'delivered':
        return 0
    if verdict.get('status') == 'unshared':
        return 0
    if command == 'unhealthy' and verdict.get('status') == 'recorded':
        return 0
    if command == 'policy' and verdict.get('status') == 'eligible':
        return 0
    if (command in ('deliver', 'policy')
            and verdict.get('reason') in ('disabled', 'excluded')):
        # These two are explicit administrator choices of classic npm. The
        # wrapper distinguishes them from every actual delivery failure.
        return 3
    if (command == 'deliver' and verdict.get('reason')
            in ('unsupported_source', 'registry_not_allowed',
                'lockfile_mismatch', 'lockfile_too_large',
                'too_many_packages')):
        # Engine incapability, like a resolve sentinel: root refused a source
        # or package shape the Store does not serve, or an `npm ci` it cannot
        # install exactly, logged it and named the application in its
        # fallback registry for `status`. The account is not told; the
        # wrapper takes exit 3 to its classic path.
        # The one line stdout ever carries: npm_wrapper remembers an
        # `npm ci` refused for any of these (see its memo).
        sys.stdout.write('%s\n' % verdict['reason'])
        return 3
    sys.stderr.write('bun delivery declined: %s (%s)\n'
                     % (verdict.get('reason', '?'), verdict.get('detail', '')))
    return 1


if __name__ == '__main__':
    sys.exit(main())