From d4ba644d07ac35f750ad2293a16106c1db724726 Mon Sep 17 00:00:00 2001 From: ed Date: Mon, 31 Oct 2022 19:37:37 +0000 Subject: [PATCH] autodefault -nc based on OS limits --- contrib/nginx/copyparty.conf | 2 +- copyparty/__main__.py | 18 ++++++++++++--- copyparty/svchub.py | 45 ++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/contrib/nginx/copyparty.conf b/contrib/nginx/copyparty.conf index e5218ff3..072a437a 100644 --- a/contrib/nginx/copyparty.conf +++ b/contrib/nginx/copyparty.conf @@ -1,11 +1,11 @@ # when running copyparty behind a reverse proxy, # the following arguments are recommended: # -# -nc 512 important, see next paragraph # --http-only lower latency on initial connection # -i 127.0.0.1 only accept connections from nginx # # -nc must match or exceed the webserver's max number of concurrent clients; +# copyparty default is 1024 if OS permits it (see "max clients:" on startup), # nginx default is 512 (worker_processes 1, worker_connections 512) # # you may also consider adding -j0 for CPU-intensive configurations diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 9338964c..6473e2e6 100755 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -411,7 +411,9 @@ def showlic() -> None: print(f.read().decode("utf-8", "replace")) -def run_argparse(argv: list[str], formatter: Any, retry: bool) -> argparse.Namespace: +def run_argparse( + argv: list[str], formatter: Any, retry: bool, nc: int +) -> argparse.Namespace: ap = argparse.ArgumentParser( formatter_class=formatter, prog="copyparty", @@ -569,7 +571,7 @@ def run_argparse(argv: list[str], formatter: Any, retry: bool) -> argparse.Names u = unicode ap2 = ap.add_argument_group('general options') ap2.add_argument("-c", metavar="PATH", type=u, action="append", help="add config file") - ap2.add_argument("-nc", metavar="NUM", type=int, default=64, help="max num clients") + ap2.add_argument("-nc", metavar="NUM", type=int, default=nc, help="max num clients") ap2.add_argument("-j", metavar="CORES", type=int, default=1, help="max num cpu cores, 0=all") ap2.add_argument("-a", metavar="ACCT", type=u, action="append", help="add account, \033[33mUSER\033[0m:\033[33mPASS\033[0m; example [\033[32med:wark\033[0m]") ap2.add_argument("-v", metavar="VOL", type=u, action="append", help="add volume, \033[33mSRC\033[0m:\033[33mDST\033[0m:\033[33mFLAG\033[0m; examples [\033[32m.::r\033[0m], [\033[32m/mnt/nas/music:/music:r:aed\033[0m]") @@ -855,10 +857,20 @@ def main(argv: Optional[list[str]] = None) -> None: except: pass + nc = 1024 + try: + import resource + + _, hard = resource.getrlimit(resource.RLIMIT_NOFILE) + if hard > 0: # -1 == infinite + nc = min(nc, hard // 4) + except: + nc = 512 + retry = False for fmtr in [RiceFormatter, RiceFormatter, Dodge11874, BasicDodge11874]: try: - al = run_argparse(argv, fmtr, retry) + al = run_argparse(argv, fmtr, retry, nc) except SystemExit: raise except: diff --git a/copyparty/svchub.py b/copyparty/svchub.py index 668bb6d7..44af8e9d 100644 --- a/copyparty/svchub.py +++ b/copyparty/svchub.py @@ -140,6 +140,11 @@ class SvcHub(object): if args.ls: self.asrv.dbg_ls() + if not ANYWIN: + self._setlimits() + + self.log("root", "max clients: {}".format(self.args.nc)) + self.tcpsrv = TcpSrv(self) self.up2k = Up2k(self) @@ -245,6 +250,46 @@ class SvcHub(object): Daemon(self.sd_notify, "sd-notify") + def _setlimits(self) -> None: + try: + import resource + + soft, hard = [ + x if x > 0 else 1024 * 1024 + for x in list(resource.getrlimit(resource.RLIMIT_NOFILE)) + ] + except: + self.log("root", "failed to read rlimits from os", 6) + return + + if not soft or not hard: + t = "got bogus rlimits from os ({}, {})" + self.log("root", t.format(soft, hard), 6) + return + + want = self.args.nc * 4 + new_soft = min(hard, want) + if new_soft < soft: + return + + # t = "requesting rlimit_nofile({}), have {}" + # self.log("root", t.format(new_soft, soft), 6) + + try: + import resource + + resource.setrlimit(resource.RLIMIT_NOFILE, (new_soft, hard)) + soft = new_soft + except: + t = "rlimit denied; max open files: {}" + self.log("root", t.format(soft), 3) + return + + if soft < want: + t = "max open files: {} (wanted {} for -nc {})" + self.log("root", t.format(soft, want, self.args.nc), 3) + self.args.nc = min(self.args.nc, soft // 2) + def _logname(self) -> str: dt = datetime.utcnow() fn = str(self.args.lo)