From 3fa377a580f568ba096bd23805ce8b359254bc1f Mon Sep 17 00:00:00 2001 From: ed Date: Sat, 16 Jul 2022 20:43:26 +0200 Subject: [PATCH] sqlite diag --- README.md | 4 ++++ copyparty/authsrv.py | 11 ++++++++++- copyparty/httpcli.py | 9 +++++++-- copyparty/svchub.py | 17 ++++++++++++++++- copyparty/up2k.py | 39 +++++++++++++++++++++++++++++++++++---- copyparty/web/browser.css | 15 ++++++++++++++- 6 files changed, 86 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 4b03903b..75f74a55 100644 --- a/README.md +++ b/README.md @@ -1238,11 +1238,15 @@ if you want thumbnails, `apt -y install ffmpeg` ideas for context to include in bug reports +in general, commandline arguments (and config file if any) + if something broke during an upload (replacing FILENAME with a part of the filename that broke): ``` journalctl -aS '48 hour ago' -u copyparty | grep -C10 FILENAME | tee bug.log ``` +if there's a wall of base64 in the log (thread stacks) then please include that, especially if you run into something freezing up or getting stuck, for example `OperationalError('database is locked')` -- alternatively you can visit `/?stack` to see the stacks live, so http://127.0.0.1:3923/?stack for example + # building diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index a21700de..8d0f3b97 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -1107,6 +1107,7 @@ class AuthSrv(object): vfs.bubble_flags() + e2vs = [] t = "volumes and permissions:\n" for zv in vfs.all_vols.values(): if not self.warn_anonwrite: @@ -1124,8 +1125,16 @@ class AuthSrv(object): u = ", ".join("\033[35meverybody\033[0m" if x == "*" else x for x in u) u = u if u else "\033[36m--none--\033[0m" t += "\n| {}: {}".format(txt, u) + + if "e2v" in zv.flags and zv.axs.uwrite: + e2vs.append(zv.vpath or "/") + t += "\n" + if e2vs: + t += "\n\033[33me2v enabled for the following volumes;\nuploads will be blocked until scan has finished:\n \033[0m" + t += " ".join(e2vs) + "\n" + if self.warn_anonwrite and not self.args.no_voldump: self.log(t) @@ -1133,7 +1142,7 @@ class AuthSrv(object): zv, _ = vfs.get("/", "*", False, True) if self.warn_anonwrite and os.getcwd() == zv.realpath: self.warn_anonwrite = False - t = "anyone can read/write the current directory: {}\n" + t = "anyone can write to the current directory: {}\n" self.log(t.format(zv.realpath), c=1) except Pebkac: self.warn_anonwrite = True diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 9480b66c..8dc27462 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -380,13 +380,18 @@ class HttpCli(object): if not self._check_nonfatal(pex, post): self.keepalive = False - msg = str(ex) if pex == ex else min_ex() + em = str(ex) + msg = em if pex == ex else min_ex() self.log("{}\033[0m, {}".format(msg, self.vpath), 3) - msg = "{}\r\nURL: {}\r\n".format(str(ex), self.vpath) + msg = "{}\r\nURL: {}\r\n".format(em, self.vpath) if self.hint: msg += "hint: {}\r\n".format(self.hint) + if "database is locked" in em: + self.conn.hsrv.broker.say("log_stacks") + msg += "hint: important info in the server log\r\n" + msg = "
" + html_escape(msg)
                 self.reply(msg.encode("utf-8", "replace"), status=pex.code, volsan=True)
                 return self.keepalive
diff --git a/copyparty/svchub.py b/copyparty/svchub.py
index 48e1c130..287dc672 100644
--- a/copyparty/svchub.py
+++ b/copyparty/svchub.py
@@ -2,7 +2,9 @@
 from __future__ import print_function, unicode_literals
 
 import argparse
+import base64
 import calendar
+import gzip
 import os
 import shlex
 import signal
@@ -27,7 +29,7 @@ from .mtag import HAVE_FFMPEG, HAVE_FFPROBE
 from .tcpsrv import TcpSrv
 from .th_srv import HAVE_PIL, HAVE_VIPS, HAVE_WEBP, ThumbSrv
 from .up2k import Up2k
-from .util import ansi_re, min_ex, mp, start_log_thrs, start_stackmon
+from .util import ansi_re, min_ex, mp, start_log_thrs, start_stackmon, alltrace
 
 
 class SvcHub(object):
@@ -56,6 +58,7 @@ class SvcHub(object):
 
         self.log_mutex = threading.Lock()
         self.next_day = 0
+        self.tstack = 0.0
 
         if args.sss or args.s >= 3:
             args.ss = True
@@ -500,3 +503,15 @@ class SvcHub(object):
             sck.sendall(b"READY=1")
         except:
             self.log("sd_notify", min_ex())
+
+    def log_stacks(self) -> None:
+        td = time.time() - self.tstack
+        if td < 300:
+            self.log("stacks", "cooldown {}".format(td))
+            return
+
+        self.tstack = time.time()
+        zb = alltrace().encode("utf-8", "replace")
+        zb = gzip.compress(zb)
+        zs = base64.b64encode(zb).decode("ascii")
+        self.log("stacks", zs)
diff --git a/copyparty/up2k.py b/copyparty/up2k.py
index 0b45895f..ccddcd19 100644
--- a/copyparty/up2k.py
+++ b/copyparty/up2k.py
@@ -98,6 +98,7 @@ class Up2k(object):
         self.gid = 0
         self.stop = False
         self.mutex = threading.Lock()
+        self.blocked: Optional[str] = None
         self.pp: Optional[ProgressPrinter] = None
         self.rescan_cond = threading.Condition()
         self.need_rescan: set[str] = set()
@@ -193,6 +194,14 @@ class Up2k(object):
     def log(self, msg: str, c: Union[int, str] = 0) -> None:
         self.log_func("up2k", msg + "\033[K", c)
 
+    def _block(self, why: str) -> None:
+        self.blocked = why
+        self.log("uploads are temporarily blocked due to " + why, 3)
+
+    def _unblock(self) -> None:
+        self.blocked = None
+        self.log("uploads are now possible", 2)
+
     def get_state(self) -> str:
         mtpq: Union[int, str] = 0
         q = "select count(w) from mt where k = 't:mtp'"
@@ -416,6 +425,9 @@ class Up2k(object):
                 self.mtag = None
 
         # e2ds(a) volumes first
+        if next((zv for zv in vols if "e2ds" in zv.flags), None):
+            self._block("indexing")
+
         for vol in vols:
             if self.stop:
                 break
@@ -445,6 +457,9 @@ class Up2k(object):
             self.volstate[vol.vpath] = t
 
         # file contents verification
+        if next((zv for zv in vols if "e2v" in zv.flags), None):
+            self._block("integrity verification")
+
         for vol in vols:
             if self.stop:
                 break
@@ -468,6 +483,9 @@ class Up2k(object):
 
             self.volstate[vol.vpath] = t
 
+        if self.blocked:
+            self._unblock()
+
         # open the rest + do any e2ts(a)
         needed_mutagen = False
         for vol in vols:
@@ -1485,11 +1503,24 @@ class Up2k(object):
 
         cur.connection.commit()
 
+    def _job_volchk(self, cj: dict[str, Any]) -> None:
+        if not self.register_vpath(cj["ptop"], cj["vcfg"]):
+            if cj["ptop"] not in self.registry:
+                raise Pebkac(410, "location unavailable")
+
     def handle_json(self, cj: dict[str, Any]) -> dict[str, Any]:
-        with self.mutex:
-            if not self.register_vpath(cj["ptop"], cj["vcfg"]):
-                if cj["ptop"] not in self.registry:
-                    raise Pebkac(410, "location unavailable")
+        try:
+            # bit expensive; 3.9=10x 3.11=2x
+            if self.mutex.acquire(timeout=10):
+                self._job_volchk(cj)
+                self.mutex.release()
+            else:
+                t = "cannot receive uploads right now;\nserver busy with {}.\nPlease wait; the client will retry..."
+                raise Pebkac(503, t.format(self.blocked or "[unknown]"))
+        except TypeError:
+            # py2
+            with self.mutex:
+                self._job_volchk(cj)
 
         cj["name"] = sanitize_fn(cj["name"], "", [".prologue.html", ".epilogue.html"])
         cj["poke"] = time.time()
diff --git a/copyparty/web/browser.css b/copyparty/web/browser.css
index 34477de2..c32d9ad7 100644
--- a/copyparty/web/browser.css
+++ b/copyparty/web/browser.css
@@ -2244,6 +2244,7 @@ html.y #bbox-overlay figcaption a {
 	max-width: none;
 }
 #u2tab td {
+	word-wrap: break-word;
 	border: 1px solid rgba(128,128,128,0.8);
 	border-width: 0 0px 1px 0;
 	padding: .2em .3em;
@@ -2258,7 +2259,19 @@ html.y #bbox-overlay figcaption a {
 #u2tab.up.ok td:nth-child(3),
 #u2tab.up.bz td:nth-child(3),
 #u2tab.up.q  td:nth-child(3) {
-	width: 19em;
+	width: 18em;
+}
+@media (max-width: 65em) {
+	#u2tab {
+		font-size: .9em;
+	}
+}
+@media (max-width: 50em) {
+	#u2tab.up.ok td:nth-child(3),
+	#u2tab.up.bz td:nth-child(3),
+	#u2tab.up.q  td:nth-child(3) {
+		width: 16em;
+	}
 }
 #op_up2k.srch td.prog {
 	font-family: sans-serif;