From 2fad85ee08e8b5f9e2514df0fc46d6a853564493 Mon Sep 17 00:00:00 2001 From: mat Date: Wed, 6 Aug 2025 09:04:17 -0300 Subject: [PATCH] add support for socket passing --- copyparty/__main__.py | 4 ++- copyparty/ftpd.py | 2 +- copyparty/svchub.py | 4 +-- copyparty/tcpsrv.py | 59 +++++++++++++++++++++++++------------------ copyparty/tftpd.py | 2 +- 5 files changed, 42 insertions(+), 29 deletions(-) diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 3bb8c69e..d176deb0 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,6 +562,8 @@ 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[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 unix sockets """ ), 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..c44f96c4 100644 --- a/copyparty/tcpsrv.py +++ b/copyparty/tcpsrv.py @@ -245,8 +245,10 @@ class TcpSrv(object): def _listen(self, ip: str, port: int) -> None: uds_perm = uds_gid = -1 + bound = False + 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,10 @@ class TcpSrv(object): tcp = True ipv = socket.AF_INET - srv = socket.socket(ipv, socket.SOCK_STREAM) + if not bound: + srv = socket.socket(ipv, socket.SOCK_STREAM) + else: + srv = bound if not ANYWIN or self.args.reuseaddr: srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) @@ -286,29 +296,30 @@ class TcpSrv(object): srv.setsockopt(socket.SOL_IP, socket.IP_FREEBIND, 1) try: - if tcp: - srv.bind((ip, port)) - else: - if ANYWIN or self.args.rm_sck: - if os.path.exists(ip): - os.unlink(ip) - srv.bind(ip) + if not bound: + if tcp: + srv.bind((ip, port)) else: - tf = "%s.%d" % (ip, os.getpid()) - if os.path.exists(tf): - os.unlink(tf) - srv.bind(tf) - if uds_gid != -1: - os.chown(tf, -1, uds_gid) - if uds_perm != -1: - os.chmod(tf, uds_perm) - atomic_move(self.nlog, tf, ip, VF_CAREFUL) + if ANYWIN or self.args.rm_sck: + if os.path.exists(ip): + os.unlink(ip) + srv.bind(ip) + else: + tf = "%s.%d" % (ip, os.getpid()) + if os.path.exists(tf): + os.unlink(tf) + srv.bind(tf) + if uds_gid != -1: + os.chown(tf, -1, uds_gid) + if uds_perm != -1: + os.chmod(tf, uds_perm) + atomic_move(self.nlog, tf, ip, VF_CAREFUL) - sport = srv.getsockname()[1] if tcp else port - if port != sport: - # linux 6.0.16 lets you bind a port which is in use - # except it just gives you a random port instead - raise OSError(E_ADDR_IN_USE[0], "") + sport = srv.getsockname()[1] if tcp else port + if port != sport: + # linux 6.0.16 lets you bind a port which is in use + # except it just gives you a random port instead + raise OSError(E_ADDR_IN_USE[0], "") self.srv.append(srv) except (OSError, socket.error) as ex: try: @@ -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]