|
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 ] |
|---|
# 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())