From 9b9d2a92ca128289b9e6943ad24e3d6f5f3df5ce Mon Sep 17 00:00:00 2001 From: mati1210 <49544174+mati1210@users.noreply.github.com> Date: Thu, 7 Aug 2025 12:00:53 -0300 Subject: [PATCH] support systemd socket activation (fd passing) (#515) * add support for socket passing * slight tweaks before merge --------- Signed-off-by: mat Signed-off-by: ed Co-authored-by: ed --- copyparty/__main__.py | 6 ++++-- copyparty/ftpd.py | 2 +- copyparty/svchub.py | 4 ++-- copyparty/tcpsrv.py | 21 ++++++++++++++++----- copyparty/tftpd.py | 2 +- 5 files changed, 24 insertions(+), 11 deletions(-) diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 3f07bfc1..86b6b031 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -536,7 +536,7 @@ def get_sects(): dedent( """ \033[33m-i\033[0m takes a comma-separated list of interfaces to listen on; - IP-addresses and/or unix-sockets (Unix Domain Sockets) + IP-addresses, unix-sockets and/or open file descriptors the default (\033[32m-i ::\033[0m) means all IPv4 and IPv6 addresses @@ -562,7 +562,9 @@ def get_sects(): \033[32m-i unix:\033[33m/dev/shm/party.sock\033[0m keeps umask-defined permission (usually \033[33m0600\033[0m) and the same user/group as copyparty - \033[33m-p\033[0m (tcp ports) is ignored for unix sockets + \033[32m-i fd:\033[33m3\033[0m uses the socket passed to copyparty on file descriptor 3 + + \033[33m-p\033[0m (tcp ports) is ignored for non ip-addresses """ ), ], diff --git a/copyparty/ftpd.py b/copyparty/ftpd.py index 2f45c3f4..bace7d28 100644 --- a/copyparty/ftpd.py +++ b/copyparty/ftpd.py @@ -607,7 +607,7 @@ class Ftpd(object): if "::" in ips: ips.append("0.0.0.0") - ips = [x for x in ips if "unix:" not in x] + ips = [x for x in ips if not x.startswith(("unix:", "fd:"))] if self.args.ftp4: ips = [x for x in ips if ":" not in x] diff --git a/copyparty/svchub.py b/copyparty/svchub.py index bd7562e4..9a00f890 100644 --- a/copyparty/svchub.py +++ b/copyparty/svchub.py @@ -868,7 +868,7 @@ class SvcHub(object): have_tcp = False for zs in al.i: - if not zs.startswith("unix:"): + if not zs.startswith(("unix:", "fd:")): have_tcp = True if not have_tcp: zb = False @@ -878,7 +878,7 @@ class SvcHub(object): setattr(al, zs, False) zb = True if zb: - t = "only listening on unix-sockets; cannot enable zeroconf/mdns/ssdp as requested" + t = "no ip addresses provided; cannot enable zeroconf/mdns/ssdp as requested" self.log("root", t, 3) if not self.args.no_dav: diff --git a/copyparty/tcpsrv.py b/copyparty/tcpsrv.py index 53388c5e..6c61fe61 100644 --- a/copyparty/tcpsrv.py +++ b/copyparty/tcpsrv.py @@ -25,8 +25,8 @@ from .util import ( termsize, ) -if True: - from typing import Generator, Union +if True: # pylint: disable=using-constant-test + from typing import Generator, Optional, Union if TYPE_CHECKING: from .svchub import SvcHub @@ -245,8 +245,10 @@ class TcpSrv(object): def _listen(self, ip: str, port: int) -> None: uds_perm = uds_gid = -1 + bound: Optional[socket.socket] = None + tcp = False + if "unix:" in ip: - tcp = False ipv = socket.AF_UNIX uds = ip.split(":") ip = uds[-1] @@ -259,7 +261,12 @@ class TcpSrv(object): import grp uds_gid = grp.getgrnam(uds[2]).gr_gid + elif "fd:" in ip: + fd = ip[3:] + bound = socket.socket(fileno=int(fd)) + tcp = bound.proto == socket.IPPROTO_TCP + ipv = bound.family elif ":" in ip: tcp = True ipv = socket.AF_INET6 @@ -267,7 +274,7 @@ class TcpSrv(object): tcp = True ipv = socket.AF_INET - srv = socket.socket(ipv, socket.SOCK_STREAM) + srv = bound or socket.socket(ipv, socket.SOCK_STREAM) if not ANYWIN or self.args.reuseaddr: srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) @@ -285,6 +292,10 @@ class TcpSrv(object): if getattr(self.args, "freebind", False): srv.setsockopt(socket.SOL_IP, socket.IP_FREEBIND, 1) + if bound: + self.srv.append(srv) + return + try: if tcp: srv.bind((ip, port)) @@ -437,7 +448,7 @@ class TcpSrv(object): def detect_interfaces(self, listen_ips: list[str]) -> dict[str, Netdev]: from .stolen.ifaddr import get_adapters - listen_ips = [x for x in listen_ips if "unix:" not in x] + listen_ips = [x for x in listen_ips if not x.startswith(("unix:", "fd:"))] nics = get_adapters(True) eps: dict[str, Netdev] = {} diff --git a/copyparty/tftpd.py b/copyparty/tftpd.py index 6f5726b3..4ef01103 100644 --- a/copyparty/tftpd.py +++ b/copyparty/tftpd.py @@ -179,7 +179,7 @@ class Tftpd(object): if "::" in ips: ips.append("0.0.0.0") - ips = [x for x in ips if "unix:" not in x] + ips = [x for x in ips if not x.startswith(("unix:", "fd:"))] if self.args.tftp4: ips = [x for x in ips if ":" not in x]