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;