fix mdns on windows

This commit is contained in:
ed 2022-12-03 13:31:00 +00:00
parent 2fbdc0a85e
commit 99efc290df
12 changed files with 90 additions and 198 deletions

View file

@ -12,11 +12,11 @@ except:
TYPE_CHECKING = False TYPE_CHECKING = False
if True: if True:
from typing import Any, Callable, Union from typing import Any, Callable
PY2 = sys.version_info < (3,) PY2 = sys.version_info < (3,)
if not PY2: if not PY2:
unicode: Callable[[Union[str, int, float]], str] = str unicode: Callable[[Any], str] = str
else: else:
sys.dont_write_bytecode = True sys.dont_write_bytecode = True
unicode = unicode # noqa: F821 # pylint: disable=undefined-variable,self-assigning-variable unicode = unicode # noqa: F821 # pylint: disable=undefined-variable,self-assigning-variable

View file

@ -666,15 +666,15 @@ def run_argparse(
ap2 = ap.add_argument_group("Zeroconf options") ap2 = ap.add_argument_group("Zeroconf options")
ap2.add_argument("-z", action="store_true", help="enable all zeroconf backends (mdns, ssdp)") ap2.add_argument("-z", action="store_true", help="enable all zeroconf backends (mdns, ssdp)")
ap2.add_argument("--z-on", metavar="NICS/NETS", type=u, default="", help="enable zeroconf ONLY on the comma-separated list of subnets and/or interface names\n └─example: \033[32meth0, wlo1, virhost0, 192.168.123.0/24, fd00:fda::/96\033[0m") ap2.add_argument("--z-on", metavar="NICS/NETS", type=u, default="", help="enable zeroconf ONLY on the comma-separated list of subnets and/or interface names/indexes\n └─example: \033[32meth0, wlo1, virhost0, 192.168.123.0/24, fd00:fda::/96\033[0m")
ap2.add_argument("--z-off", metavar="NICS/NETS", type=u, default="", help="disable zeroconf on the comma-separated list of subnets and/or interface names") ap2.add_argument("--z-off", metavar="NICS/NETS", type=u, default="", help="disable zeroconf on the comma-separated list of subnets and/or interface names/indexes")
ap2.add_argument("-zv", action="store_true", help="verbose all zeroconf backends") ap2.add_argument("-zv", action="store_true", help="verbose all zeroconf backends")
ap2.add_argument("--mc-hop", metavar="SEC", type=int, default=0, help="rejoin multicast groups every SEC seconds (workaround for some switches/routers which cause mDNS to suddenly stop working after some time); try [\033[32m300\033[0m] or [\033[32m180\033[0m]") ap2.add_argument("--mc-hop", metavar="SEC", type=int, default=0, help="rejoin multicast groups every SEC seconds (workaround for some switches/routers which cause mDNS to suddenly stop working after some time); try [\033[32m300\033[0m] or [\033[32m180\033[0m]")
ap2 = ap.add_argument_group("Zeroconf-mDNS options:") ap2 = ap.add_argument_group("Zeroconf-mDNS options:")
ap2.add_argument("--zm", action="store_true", help="announce the enabled protocols over mDNS (multicast DNS-SD) -- compatible with KDE, gnome, macOS, ...") ap2.add_argument("--zm", action="store_true", help="announce the enabled protocols over mDNS (multicast DNS-SD) -- compatible with KDE, gnome, macOS, ...")
ap2.add_argument("--zm-on", metavar="NICS/NETS", type=u, default="", help="enable zeroconf ONLY on the comma-separated list of subnets and/or interface names") ap2.add_argument("--zm-on", metavar="NICS/NETS", type=u, default="", help="enable zeroconf ONLY on the comma-separated list of subnets and/or interface names/indexes")
ap2.add_argument("--zm-off", metavar="NICS/NETS", type=u, default="", help="disable zeroconf on the comma-separated list of subnets and/or interface names") ap2.add_argument("--zm-off", metavar="NICS/NETS", type=u, default="", help="disable zeroconf on the comma-separated list of subnets and/or interface names/indexes")
ap2.add_argument("--zm4", action="store_true", help="IPv4 only -- try this if some clients can't connect") ap2.add_argument("--zm4", action="store_true", help="IPv4 only -- try this if some clients can't connect")
ap2.add_argument("--zm6", action="store_true", help="IPv6 only") ap2.add_argument("--zm6", action="store_true", help="IPv6 only")
ap2.add_argument("--zmv", action="store_true", help="verbose mdns") ap2.add_argument("--zmv", action="store_true", help="verbose mdns")
@ -690,8 +690,8 @@ def run_argparse(
ap2 = ap.add_argument_group("Zeroconf-SSDP options:") ap2 = ap.add_argument_group("Zeroconf-SSDP options:")
ap2.add_argument("--zs", action="store_true", help="announce the enabled protocols over SSDP -- compatible with Windows") ap2.add_argument("--zs", action="store_true", help="announce the enabled protocols over SSDP -- compatible with Windows")
ap2.add_argument("--zs-on", metavar="NICS/NETS", type=u, default="", help="enable zeroconf ONLY on the comma-separated list of subnets and/or interface names") ap2.add_argument("--zs-on", metavar="NICS/NETS", type=u, default="", help="enable zeroconf ONLY on the comma-separated list of subnets and/or interface names/indexes")
ap2.add_argument("--zs-off", metavar="NICS/NETS", type=u, default="", help="disable zeroconf on the comma-separated list of subnets and/or interface names") ap2.add_argument("--zs-off", metavar="NICS/NETS", type=u, default="", help="disable zeroconf on the comma-separated list of subnets and/or interface names/indexes")
ap2.add_argument("--zsv", action="store_true", help="verbose SSDP") ap2.add_argument("--zsv", action="store_true", help="verbose SSDP")
ap2.add_argument("--zsl", metavar="PATH", type=u, default="/?hc", help="location to include in the url (or a complete external URL), for example [\033[32mpriv/?pw=hunter2\033[0m] or [\033[32mpriv/?pw=hunter2\033[0m]") ap2.add_argument("--zsl", metavar="PATH", type=u, default="/?hc", help="location to include in the url (or a complete external URL), for example [\033[32mpriv/?pw=hunter2\033[0m] or [\033[32mpriv/?pw=hunter2\033[0m]")
ap2.add_argument("--zsid", metavar="UUID", type=u, default=uuid.uuid4().urn[4:], help="USN (device identifier) to announce") ap2.add_argument("--zsid", metavar="UUID", type=u, default=uuid.uuid4().urn[4:], help="USN (device identifier) to announce")

View file

@ -34,7 +34,6 @@ from .util import (
if True: # pylint: disable=using-constant-test if True: # pylint: disable=using-constant-test
from collections.abc import Iterable from collections.abc import Iterable
import typing
from typing import Any, Generator, Optional, Union from typing import Any, Generator, Optional, Union
from .util import RootLogger from .util import RootLogger

View file

@ -38,6 +38,7 @@ from .util import (
Garda, Garda,
Magician, Magician,
NetMap, NetMap,
Netdev,
ipnorm, ipnorm,
min_ex, min_ex,
shut_socket, shut_socket,
@ -140,7 +141,7 @@ class HttpSrv(object):
except: except:
pass pass
def set_netdevs(self, netdevs: dict[str, str]) -> None: def set_netdevs(self, netdevs: dict[str, Netdev]) -> None:
self.nm = NetMap([self.ip], netdevs) self.nm = NetMap([self.ip], netdevs)
def start_threads(self, n: int) -> None: def start_threads(self, n: int) -> None:

View file

@ -25,7 +25,7 @@ from .stolen.dnslib import (
DNSQuestion, DNSQuestion,
DNSRecord, DNSRecord,
) )
from .util import CachedSet, Daemon, min_ex from .util import CachedSet, Daemon, Netdev, min_ex
if TYPE_CHECKING: if TYPE_CHECKING:
from .svchub import SvcHub from .svchub import SvcHub
@ -42,13 +42,12 @@ class MDNS_Sck(MC_Sck):
def __init__( def __init__(
self, self,
sck: socket.socket, sck: socket.socket,
idx: int, nd: Netdev,
name: str,
grp: str, grp: str,
ip: str, ip: str,
net: Union[IPv4Network, IPv6Network], net: Union[IPv4Network, IPv6Network],
): ):
super(MDNS_Sck, self).__init__(sck, idx, name, grp, ip, net) super(MDNS_Sck, self).__init__(sck, nd, grp, ip, net)
self.bp_probe = b"" self.bp_probe = b""
self.bp_ip = b"" self.bp_ip = b""
@ -263,7 +262,8 @@ class MDNS(MCast):
try: try:
bound = self.create_servers() bound = self.create_servers()
except: except:
self.log("no server IP matches the mdns config", 1) t = "no server IP matches the mdns config\n{}"
self.log(t.format(min_ex()), 1)
bound = [] bound = []
if not bound: if not bound:

View file

@ -7,8 +7,8 @@ import time
import ipaddress import ipaddress
from ipaddress import IPv4Address, IPv4Network, IPv6Address, IPv6Network from ipaddress import IPv4Address, IPv4Network, IPv6Address, IPv6Network
from .__init__ import MACOS, TYPE_CHECKING from .__init__ import TYPE_CHECKING
from .util import min_ex, spack from .util import Netdev, min_ex, spack
if TYPE_CHECKING: if TYPE_CHECKING:
from .svchub import SvcHub from .svchub import SvcHub
@ -30,15 +30,14 @@ class MC_Sck(object):
def __init__( def __init__(
self, self,
sck: socket.socket, sck: socket.socket,
idx: int, nd: Netdev,
name: str,
grp: str, grp: str,
ip: str, ip: str,
net: Union[IPv4Network, IPv6Network], net: Union[IPv4Network, IPv6Network],
): ):
self.sck = sck self.sck = sck
self.idx = idx self.idx = nd.idx
self.name = name self.name = nd.name
self.grp = grp self.grp = grp
self.mreq = b"" self.mreq = b""
self.ip = ip self.ip = ip
@ -112,7 +111,7 @@ class MCast(object):
for lst in (on, off): for lst in (on, off):
for av in list(lst): for av in list(lst):
for sk, sv in netdevs.items(): for sk, sv in netdevs.items():
if av == sv.split(",")[0] and sk not in lst: if (av == str(sv.idx) or av == sv.name) and sk not in lst:
lst.append(sk) lst.append(sk)
if on: if on:
@ -137,12 +136,8 @@ class MCast(object):
for ip in ips: for ip in ips:
v6 = ":" in ip v6 = ":" in ip
netdev = "?" netdev = netdevs[ip]
try: if not netdev.idx:
netdev = netdevs[ip].split(",")[0]
idx = socket.if_nametoindex(netdev)
except:
idx = socket.INADDR_ANY
t = "using INADDR_ANY for ip [{}], netdev [{}]" t = "using INADDR_ANY for ip [{}], netdev [{}]"
if not self.srv and ip not in ["::", "0.0.0.0"]: if not self.srv and ip not in ["::", "0.0.0.0"]:
self.log(t.format(ip, netdev), 3) self.log(t.format(ip, netdev), 3)
@ -159,20 +154,14 @@ class MCast(object):
# most ipv6 clients expect multicast on linklocal ip only; # most ipv6 clients expect multicast on linklocal ip only;
# add a/aaaa records for the other nic IPs # add a/aaaa records for the other nic IPs
other_ips: set[str] = set() other_ips: set[str] = set()
if v6 and netdev not in ("?", ""): if v6:
for oip, onic in netdevs.items(): for nd in netdevs.values():
if ( if nd.idx == netdev.idx and nd.ip in all_selected and ":" in nd.ip:
onic.split(",")[0] == netdev other_ips.add(nd.ip)
and oip in all_selected
and ":" in oip
):
other_ips.add(oip)
net = ipaddress.ip_network(ip, False) net = ipaddress.ip_network(ip, False)
ip = ip.split("/")[0] ip = ip.split("/")[0]
srv = self.Srv( srv = self.Srv(sck, netdev, self.grp6 if ":" in ip else self.grp4, ip, net)
sck, idx, netdev, self.grp6 if ":" in ip else self.grp4, ip, net
)
for oth_ip in other_ips: for oth_ip in other_ips:
srv.ips[oth_ip.split("/")[0]] = ipaddress.ip_network(oth_ip, False) srv.ips[oth_ip.split("/")[0]] = ipaddress.ip_network(oth_ip, False)
@ -225,8 +214,11 @@ class MCast(object):
self.b2srv[bip] = srv self.b2srv[bip] = srv
self.b6.append(bip) self.b6.append(bip)
grp = self.grp6 if srv.idx and not MACOS else "" grp = self.grp6 if srv.idx else ""
sck.bind((grp, self.port, 0, srv.idx)) try:
sck.bind((grp, self.port, 0, srv.idx))
except:
sck.bind(("", self.port, 0, srv.idx))
bgrp = socket.inet_pton(socket.AF_INET6, self.grp6) bgrp = socket.inet_pton(socket.AF_INET6, self.grp6)
dev = spack(b"@I", srv.idx) dev = spack(b"@I", srv.idx)
@ -249,8 +241,12 @@ class MCast(object):
self.b2srv[bip] = srv self.b2srv[bip] = srv
self.b4.append(bip) self.b4.append(bip)
grp = self.grp4 if srv.idx and not MACOS else "" grp = self.grp4 if srv.idx else ""
sck.bind((grp, self.port)) try:
sck.bind((grp, self.port))
except:
sck.bind(("", self.port))
bgrp = socket.inet_aton(self.grp4) bgrp = socket.inet_aton(self.grp4)
dev = ( dev = (
spack(b"=I", socket.INADDR_ANY) spack(b"=I", socket.INADDR_ANY)

View file

@ -105,7 +105,8 @@ class SSDPd(MCast):
try: try:
bound = self.create_servers() bound = self.create_servers()
except: except:
self.log("no server IP matches the ssdp config", 1) t = "no server IP matches the ssdp config\n{}"
self.log(t.format(min_ex()), 1)
bound = [] bound = []
if not bound: if not bound:
@ -130,7 +131,7 @@ class SSDPd(MCast):
for sck in rx: for sck in rx:
buf, addr = sck.recvfrom(4096) buf, addr = sck.recvfrom(4096)
try: try:
self.eat(buf, addr, sck) self.eat(buf, addr)
except: except:
if not self.running: if not self.running:
return return
@ -144,7 +145,7 @@ class SSDPd(MCast):
self.running = False self.running = False
self.srv = {} self.srv = {}
def eat(self, buf: bytes, addr: tuple[str, int], sck: socket.socket) -> None: def eat(self, buf: bytes, addr: tuple[str, int]) -> None:
cip = addr[0] cip = addr[0]
if cip.startswith("169.254"): if cip.startswith("169.254"):
return return

View file

@ -6,14 +6,14 @@ import re
import socket import socket
import sys import sys
from .__init__ import ANYWIN, MACOS, PY2, TYPE_CHECKING, VT100, unicode from .__init__ import ANYWIN, PY2, TYPE_CHECKING, VT100, unicode
from .stolen.qrcodegen import QrCode from .stolen.qrcodegen import QrCode
from .util import ( from .util import (
E_ACCESS, E_ACCESS,
E_ADDR_IN_USE, E_ADDR_IN_USE,
E_ADDR_NOT_AVAIL, E_ADDR_NOT_AVAIL,
E_UNREACH, E_UNREACH,
chkcmd, Netdev,
min_ex, min_ex,
sunpack, sunpack,
termsize, termsize,
@ -101,7 +101,10 @@ class TcpSrv(object):
if pad: if pad:
self.log("tcpsrv", "") self.log("tcpsrv", "")
eps = {"127.0.0.1": "local only", "::1": "local only"} eps = {
"127.0.0.1": Netdev("127.0.0.1", 0, "", "local only"),
"::1": Netdev("::1", 0, "", "local only"),
}
nonlocals = [x for x in self.args.i if x not in [k.split("/")[0] for k in eps]] nonlocals = [x for x in self.args.i if x not in [k.split("/")[0] for k in eps]]
if nonlocals: if nonlocals:
try: try:
@ -114,7 +117,7 @@ class TcpSrv(object):
eps.update({k.split("/")[0]: v for k, v in self.netdevs.items()}) eps.update({k.split("/")[0]: v for k, v in self.netdevs.items()})
if not eps: if not eps:
for x in nonlocals: for x in nonlocals:
eps[x] = "external" eps[x] = Netdev(x, 0, "", "external")
else: else:
self.netdevs = {} self.netdevs = {}
@ -264,143 +267,11 @@ class TcpSrv(object):
self.log("tcpsrv", "ok bye") self.log("tcpsrv", "ok bye")
def ips_linux_ifconfig(self) -> dict[str, str]: def detect_interfaces(self, listen_ips: list[str]) -> dict[str, Netdev]:
# for termux
try:
txt, _ = chkcmd(["ifconfig"])
except:
return {}
eps: dict[str, str] = {}
dev = None
ip = None
up = None
for ln in (txt + "\n").split("\n"):
if not ln.strip() and dev and ip:
eps[ip] = dev + ("" if up else ", \033[31mLINK-DOWN")
dev = ip = up = None
continue
if ln == ln.lstrip():
dev = re.split(r"[: ]", ln)[0]
if "UP" in re.split(r"[<>, \t]", ln):
up = True
m = re.match(r"^\s+inet\s+([^ ]+)", ln)
if m:
ip = m.group(1)
return eps
def ips_linux(self) -> dict[str, str]:
try:
txt, _ = chkcmd(["ip", "addr"])
except:
return self.ips_linux_ifconfig()
r = re.compile(r"^\s+inet6? ([^ ]+)/")
ri = re.compile(r"^[0-9]+: ([^:]+): ")
dev = ""
up = False
eps: dict[str, str] = {}
for ln in txt.split("\n"):
m = ri.match(ln)
if m:
dev = m.group(1)
up = "UP" in re.split("[>,< ]", ln)
m = r.match(ln.rstrip())
if not m or not dev or " scope link" in ln:
continue
ip = m.group(1)
eps[ip] = dev + ("" if up else ", \033[31mLINK-DOWN")
return eps
def ips_macos(self) -> dict[str, str]:
eps: dict[str, str] = {}
try:
txt, _ = chkcmd(["ifconfig"])
except:
return eps
rdev = re.compile(r"^([^ ]+):")
rip = re.compile(r"^\tinet ([0-9\.]+) ")
dev = "UNKNOWN"
for ln in txt.split("\n"):
m = rdev.match(ln)
if m:
dev = m.group(1)
m = rip.match(ln)
if m:
eps[m.group(1)] = dev
dev = "UNKNOWN"
return eps
def ips_windows_ipconfig(self) -> tuple[dict[str, str], set[str]]:
eps: dict[str, str] = {}
offs: set[str] = set()
try:
txt, _ = chkcmd(["ipconfig"])
except:
return eps, offs
rdev = re.compile(r"(^[^ ].*):$")
rip = re.compile(r"^ +IPv?4? [^:]+: *([0-9\.]{7,15})$")
roff = re.compile(r".*: Media disconnected$")
dev = None
for ln in txt.replace("\r", "").split("\n"):
m = rdev.match(ln)
if m:
if dev and dev not in eps.values():
offs.add(dev)
dev = m.group(1).split(" adapter ", 1)[-1]
if dev and roff.match(ln):
offs.add(dev)
dev = None
m = rip.match(ln)
if m and dev:
eps[m.group(1)] = dev
dev = None
if dev and dev not in eps.values():
offs.add(dev)
return eps, offs
def ips_windows_netsh(self) -> dict[str, str]:
eps: dict[str, str] = {}
try:
txt, _ = chkcmd("netsh interface ip show address".split())
except:
return eps
rdev = re.compile(r'.* "([^"]+)"$')
rip = re.compile(r".* IP\b.*: +([0-9\.]{7,15})$")
dev = None
for ln in txt.replace("\r", "").split("\n"):
m = rdev.match(ln)
if m:
dev = m.group(1)
m = rip.match(ln)
if m and dev:
eps[m.group(1)] = dev
return eps
def detect_interfaces(self, listen_ips: list[str]) -> dict[str, str]:
from .stolen.ifaddr import get_adapters from .stolen.ifaddr import get_adapters
nics = get_adapters(True) nics = get_adapters(True)
eps = {} eps: dict[str, Netdev] = {}
for nic in nics: for nic in nics:
for nip in nic.ips: for nip in nic.ips:
ipa = nip.ip[0] if ":" in str(nip.ip) else nip.ip ipa = nip.ip[0] if ":" in str(nip.ip) else nip.ip
@ -409,14 +280,15 @@ class TcpSrv(object):
# browsers dont impl linklocal # browsers dont impl linklocal
continue continue
eps[sip] = nic.nice_name eps[sip] = Netdev(sip, nic.index or 0, nic.nice_name, "")
if "0.0.0.0" not in listen_ips and "::" not in listen_ips: if "0.0.0.0" not in listen_ips and "::" not in listen_ips:
eps = {k: v for k, v in eps.items() if k.split("/")[0] in listen_ips} eps = {k: v for k, v in eps.items() if k.split("/")[0] in listen_ips}
try: try:
ext_devs = list(self._extdevs_nix()) ext_devs = list(self._extdevs_nix())
ext_ips = [k for k, v in eps.items() if v.split(",")[0] in ext_devs] ext_ips = [k for k, v in eps.items() if v.name in ext_devs]
ext_ips = [x.split("/")[0] for x in ext_ips]
if not ext_ips: if not ext_ips:
raise Exception() raise Exception()
except: except:
@ -430,11 +302,9 @@ class TcpSrv(object):
desc = "\033[32mexternal" desc = "\033[32mexternal"
ips = ext_ips if lip in ["0.0.0.0", "::"] else [lip] ips = ext_ips if lip in ["0.0.0.0", "::"] else [lip]
for ip in ips: for ip in ips:
try: ip = next((x for x in eps if x.startswith(ip + "/")), "")
if "external" not in eps[ip]: if ip and "external" not in eps[ip].desc:
eps[ip] += ", " + desc eps[ip].desc += ", " + desc
except:
eps[ip] = desc
return eps return eps

View file

@ -188,10 +188,14 @@ IMPLICATIONS = [
["z", "zm"], ["z", "zm"],
["z", "zs"], ["z", "zs"],
["zmvv", "zmv"], ["zmvv", "zmv"],
["zm4", "zm"],
["zm6", "zm"],
["zmv", "zm"], ["zmv", "zm"],
["zms", "zm"], ["zms", "zm"],
["zsv", "zs"], ["zsv", "zs"],
] ]
if ANYWIN:
IMPLICATIONS.extend([["z", "zm4"]])
UNPLICATIONS = [["no_dav", "daw"]] UNPLICATIONS = [["no_dav", "daw"]]
@ -363,6 +367,23 @@ class Daemon(threading.Thread):
self.start() self.start()
class Netdev(object):
def __init__(self, ip: str, idx: int, name: str, desc: str):
self.ip = ip
self.idx = idx
self.name = name
self.desc = desc
def __str__(self):
return "{}-{}{}".format(self.idx, self.name, self.desc)
def __lt__(self, rhs):
return str(self) < str(rhs)
def __eq__(self, rhs):
return str(self) == str(rhs)
class Cooldown(object): class Cooldown(object):
def __init__(self, maxage: float) -> None: def __init__(self, maxage: float) -> None:
self.maxage = maxage self.maxage = maxage
@ -434,7 +455,7 @@ class HLog(logging.Handler):
class NetMap(object): class NetMap(object):
def __init__(self, ips: list[str], netdevs: dict[str, str]) -> None: def __init__(self, ips: list[str], netdevs: dict[str, Netdev]) -> None:
if "::" in ips: if "::" in ips:
ips = [x for x in ips if x != "::"] + list( ips = [x for x in ips if x != "::"] + list(
[x.split("/")[0] for x in netdevs if ":" in x] [x.split("/")[0] for x in netdevs if ":" in x]
@ -1791,11 +1812,14 @@ if not PY2 or not WINDOWS:
else: else:
# moonrunes become \x3f with bytestrings, # moonrunes become \x3f with bytestrings,
# losing mojibake support is worth # losing mojibake support is worth
def _not_actually_mbcs(txt: str) -> str: def _not_actually_mbcs_enc(txt: str) -> bytes:
return txt return txt
fsenc = _not_actually_mbcs def _not_actually_mbcs_dec(txt: bytes) -> str:
fsdec = _not_actually_mbcs return txt
fsenc = _not_actually_mbcs_enc
fsdec = _not_actually_mbcs_dec
def s3enc(mem_cur: "sqlite3.Cursor", rd: str, fn: str) -> tuple[str, str]: def s3enc(mem_cur: "sqlite3.Cursor", rd: str, fn: str) -> tuple[str, str]:

View file

@ -109,6 +109,7 @@ pre, code {
html.z pre, html.z pre,
html.z code { html.z code {
color: #9e0; color: #9e0;
background: #000;
background: rgba(0,16,0,0.2); background: rgba(0,16,0,0.2);
} }
.os { .os {

View file

@ -138,7 +138,7 @@ tmpdir="$(
)" )"
necho() { necho() {
printf '\033[G%s\033[K' "$*" printf '\033[G%s ... \033[K' "$*"
} }
[ $repack ] && { [ $repack ] && {
@ -331,7 +331,6 @@ find -name py.typed -delete
find -type f \( -name .DS_Store -or -name ._.DS_Store \) -delete find -type f \( -name .DS_Store -or -name ._.DS_Store \) -delete
find -type f -name ._\* | while IFS= read -r f; do cmp <(printf '\x00\x05\x16') <(head -c 3 -- "$f") && rm -f -- "$f"; done find -type f -name ._\* | while IFS= read -r f; do cmp <(printf '\x00\x05\x16') <(head -c 3 -- "$f") && rm -f -- "$f"; done
echo use smol web deps
rm -f copyparty/web/deps/*.full.* copyparty/web/dbg-* copyparty/web/Makefile rm -f copyparty/web/deps/*.full.* copyparty/web/dbg-* copyparty/web/Makefile
find copyparty | LC_ALL=C sort | sed 's/\.gz$//;s/$/,/' > have find copyparty | LC_ALL=C sort | sed 's/\.gz$//;s/$/,/' > have
@ -466,7 +465,7 @@ zdir="$tmpdir/cpp-mk$CSN"
[ -e "$zdir/$stamp" ] || rm -rf "$zdir" [ -e "$zdir/$stamp" ] || rm -rf "$zdir"
mkdir -p "$zdir" mkdir -p "$zdir"
echo a > "$zdir/$stamp" echo a > "$zdir/$stamp"
nf=$(ls -1 "$zdir"/arc.* | wc -l) nf=$(ls -1 "$zdir"/arc.* 2>/dev/null | wc -l)
[ $nf -ge 2 ] && [ ! $repack ] && use_zdir=1 || use_zdir= [ $nf -ge 2 ] && [ ! $repack ] && use_zdir=1 || use_zdir=
[ $use_zdir ] || { [ $use_zdir ] || {

View file

@ -3,6 +3,7 @@ set -e
curl -k https://192.168.123.1:3923/cpp/scripts/pyinstaller/build.sh | curl -k https://192.168.123.1:3923/cpp/scripts/pyinstaller/build.sh |
tee build2.sh | cmp build.sh && rm build2.sh || { tee build2.sh | cmp build.sh && rm build2.sh || {
[ -s build2.sh ] || exit 1
echo "new build script; upgrade y/n:" echo "new build script; upgrade y/n:"
while true; do read -u1 -n1 -r r; [[ $r =~ [yYnN] ]] && break; done while true; do read -u1 -n1 -r r; [[ $r =~ [yYnN] ]] && break; done
[[ $r =~ [yY] ]] && mv build{2,}.sh && exec ./build.sh [[ $r =~ [yY] ]] && mv build{2,}.sh && exec ./build.sh