From 0c68fb2461330b54f70f278ad31b3fe671174796 Mon Sep 17 00:00:00 2001 From: kitadmin Date: Wed, 22 Jul 2026 00:55:32 +0000 Subject: [PATCH] feat: kit-connect + syno-balance SPK builds, busfleet hub registration endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - kit-connect SPK (ipq806x): postinst with hub registration, start-stop-status, connect-daemon management, Tailscale + reverse SSH tunnel integration - syno-balance SPK (noarch, test build): SmartWAN-aware dual-WAN load balancer with 6-factor scoring engine, syno-daemon lifecycle - Busfleet hub: register.sh (port pool 2230-2299), Python HTTP server, systemd service, port-registry.json with x4078/x5925 assignments - .gitignore: exclude SSH keys (*_id_ed25519), SPK artifacts (*.spk) Gitea releases: - kit-connect v0.1-0001 → x5925-kit-connect-v1 (production) - syno-balance v0.1-0001 → syno-balance-test-v1 (prerelease) Co-Authored-By: Claude Opus 4.8 --- .gitignore | 12 ++++ hub/README.md | 38 ++++++++++++ hub/port-registry.json | 4 ++ hub/register.sh | 65 ++++++++++++++++++++ hub/server.py | 76 +++++++++++++++++++++++ kit-connect/PACKAGE_ICON.PNG | Bin 0 -> 2211 bytes kit-connect/PACKAGE_ICON_256.PNG | Bin 0 -> 7208 bytes kit-connect/build.sh | 79 ++++++++++++++++++++++++ kit-connect/conf/privilege | 1 + kit-connect/conf/resource | 1 + kit-connect/scripts/postinst | 43 +++++++++++++ kit-connect/scripts/postuninst | 2 + kit-connect/scripts/preinst | 2 + kit-connect/scripts/preuninst | 2 + kit-connect/scripts/start-stop-status | 35 +++++++++++ syno-balance/PACKAGE_ICON.PNG | Bin 0 -> 2204 bytes syno-balance/PACKAGE_ICON_256.PNG | Bin 0 -> 7245 bytes syno-balance/build.sh | 80 +++++++++++++++++++++++++ syno-balance/conf/privilege | 1 + syno-balance/conf/resource | 1 + syno-balance/scripts/postinst | 18 ++++++ syno-balance/scripts/postuninst | 2 + syno-balance/scripts/preinst | 2 + syno-balance/scripts/preuninst | 2 + syno-balance/scripts/start-stop-status | 35 +++++++++++ 25 files changed, 501 insertions(+) create mode 100644 .gitignore create mode 100644 hub/README.md create mode 100644 hub/port-registry.json create mode 100644 hub/register.sh create mode 100644 hub/server.py create mode 100644 kit-connect/PACKAGE_ICON.PNG create mode 100644 kit-connect/PACKAGE_ICON_256.PNG create mode 100755 kit-connect/build.sh create mode 100644 kit-connect/conf/privilege create mode 100644 kit-connect/conf/resource create mode 100755 kit-connect/scripts/postinst create mode 100755 kit-connect/scripts/postuninst create mode 100755 kit-connect/scripts/preinst create mode 100755 kit-connect/scripts/preuninst create mode 100755 kit-connect/scripts/start-stop-status create mode 100644 syno-balance/PACKAGE_ICON.PNG create mode 100644 syno-balance/PACKAGE_ICON_256.PNG create mode 100755 syno-balance/build.sh create mode 100644 syno-balance/conf/privilege create mode 100644 syno-balance/conf/resource create mode 100755 syno-balance/scripts/postinst create mode 100755 syno-balance/scripts/postuninst create mode 100755 syno-balance/scripts/preinst create mode 100755 syno-balance/scripts/preuninst create mode 100755 syno-balance/scripts/start-stop-status diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6112a4f --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# SSH keys +*_id_ed25519 +*_id_ed25519.pub +*.pem + +# Build artifacts +*.spk +build/ + +# IDE +.idea/ +.vscode/ diff --git a/hub/README.md b/hub/README.md new file mode 100644 index 0000000..841b1b7 --- /dev/null +++ b/hub/README.md @@ -0,0 +1,38 @@ +# Busfleet Hub + +Registration endpoint for kit-connect devices. Runs on the VPS at `162.243.83.36:8080`. + +## Files + +- **register.sh** — Called per-device. Assigns a tunnel port from pool 2230-2299, records it in `port-registry.json`, returns JSON. +- **server.py** — Minimal Python HTTP server. Routes `GET /api/register/` → `register.sh`. +- **port-registry.json** — Persistent device→port mapping. + +## Deploy + +```bash +scp hub/register.sh hub/server.py root@162.243.83.36:/opt/busfleet-hub/ +ssh root@162.243.83.36 systemctl restart busfleet-hub +``` + +## API + +``` +GET /api/register/ +``` + +Response (existing device): +```json +{"device_id": "x5925", "tunnel_port": 2230, "tailscale_auth_key": "tskey-auth-...", "status": "existing"} +``` + +Response (new device): +```json +{"device_id": "x9999", "tunnel_port": 2231, "tailscale_auth_key": "tskey-auth-...", "status": "new"} +``` + +## Port Pool + +2230–2299. Already assigned: +- x4078: 2226 (grandfathered) +- x5925: 2230 diff --git a/hub/port-registry.json b/hub/port-registry.json new file mode 100644 index 0000000..a531fb2 --- /dev/null +++ b/hub/port-registry.json @@ -0,0 +1,4 @@ +{ + "x4078": {"tunnel_port": 2226, "assigned": "2026-07-21"}, + "x5925": {"tunnel_port": 2230, "assigned": "2026-07-22"} +} diff --git a/hub/register.sh b/hub/register.sh new file mode 100644 index 0000000..a28ce0e --- /dev/null +++ b/hub/register.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# busfleet-hub register.sh — Device registration endpoint +# Called with DEVICE_ID as $1. Returns JSON on stdout. +# Port pool: 2230-2299 + +DEVICE_ID="${1:-unknown}" +REGISTRY="/opt/busfleet-hub/port-registry.json" +PORT_POOL_START="${PORT_POOL_START:-2230}" +PORT_POOL_END="${PORT_POOL_END:-2299}" +TAILSCALE_AUTH_KEY="${TAILSCALE_AUTH_KEY:-tskey-auth-kJz8wqNVo211CNTRL-GNL5EFjp5aWQcaWPSVn2aW9TNworKUNBV}" + +# Init registry if missing +if [ ! -f "$REGISTRY" ]; then + echo '{}' > "$REGISTRY" +fi + +# Check if device already registered +EXISTING_PORT=$(python3 -c " +import json +with open('$REGISTRY') as f: + reg = json.load(f) +print(reg.get('$DEVICE_ID', {}).get('tunnel_port', '')) +" 2>/dev/null) + +if [ -n "$EXISTING_PORT" ]; then + cat </dev/null) + +if [ -z "$ASSIGNED_PORT" ]; then + cat </dev/null + +cat < — assign tunnel port, return JSON + GET /health — health check +""" + +import json +import subprocess +import sys +from http.server import HTTPServer, BaseHTTPRequestHandler + +REGISTER_SH = "/opt/busfleet-hub/register.sh" + + +class HubHandler(BaseHTTPRequestHandler): + """Handle hub registration requests.""" + + def do_GET(self): + # Route: /api/register/ + if self.path.startswith("/api/register/"): + device_id = self.path.split("/api/register/", 1)[1].strip("/") + if not device_id: + self.send_json_error(400, "Missing device ID") + return + + try: + result = subprocess.run( + [REGISTER_SH, device_id], + capture_output=True, text=True, timeout=15, + ) + status = 200 if result.returncode == 0 else 500 + self.send_json(status, result.stdout.strip()) + except subprocess.TimeoutExpired: + self.send_json_error(504, "Registration timed out") + except Exception as exc: + self.send_json_error(500, str(exc)) + return + + # Health check + if self.path in ("/", "/health"): + self.send_json(200, '{"status":"ok","service":"busfleet-hub"}') + return + + self.send_json_error(404, "Not found") + + def send_json(self, status, body): + """Send a JSON response.""" + self.send_response(status) + self.send_header("Content-Type", "application/json") + self.send_header("Access-Control-Allow-Origin", "*") + self.end_headers() + self.wfile.write(body.encode() if isinstance(body, str) else body) + + def send_json_error(self, status, message): + """Send a JSON error response.""" + self.send_json(status, json.dumps({"error": message})) + + def log_message(self, fmt, *args): + print(f"[hub] {args[0]}", file=sys.stderr) + + +def main(): + port = int(sys.argv[1]) if len(sys.argv) > 1 else 8080 + server = HTTPServer(("0.0.0.0", port), HubHandler) + print(f"Busfleet Hub listening on :{port}", flush=True) + try: + server.serve_forever() + except KeyboardInterrupt: + print("\nShutting down.", flush=True) + server.shutdown() + + +if __name__ == "__main__": + main() diff --git a/kit-connect/PACKAGE_ICON.PNG b/kit-connect/PACKAGE_ICON.PNG new file mode 100644 index 0000000000000000000000000000000000000000..1b66a83532e9a8452258923822cfc10c0e5aa734 GIT binary patch literal 2211 zcmb7FcTm$?7XAe(0-?zRq!$g?5DYZ|q(}=PGyxHnUZwXMn(YM;0S!{4NC}ArP>M7` zS|A7rQK|$)2nYzMp@sJ1{R-0$4)-0z&ZW#(Y4EKGQ~#JB(e;6a!g+AvY? zkMXlG=ZFG0nF*}k`Y3$>sD8k`=gP)h%eb4`pa39N9srVV0|0|bB`q>{D+9oi3jn}# z0YD_Uu+dtZ8DMuqniwAbYj8SjePJ>j;Ruuw#}bDypezZ9O6V{dJi<`lHhSVmL5%I? zD^jGjJ~NhH!DD1MHX|#stYUZ){IUs>00mn~`W2lilPa$TmzgK~z)nDK`c4=u6^jX- zOFx>yeL2hc9Qf7e#*ZN&(Jykyr;il%41|v&sCz$SCvcof58=Ln1K;=e@^(?(orbjq3a?rscE^l%fx&@PlvEjg9*SpiKsha{L7wJf!i8`;HXYf|FLM zt7fggzUyZU6aFz(KxU!L_FXzGN70J9f_6oP@>TcG_g#1_ynZNH(_No?0dE7b=xlH@ zqmC2G6RNDxxxq*$+uYJW6(#VvSZ9qvHo7zIQ)O1LCFzRE0k-84RX>g8EAyS&K>&}- zOjFfyz)9XD9V9gaz>hjDaV*M!439+iMmJi6o^6hNCeS%HNu!+fN*K{u#4*JyXXi!P zwKFPcLnjMSi{SjT=ZbG zXkTKAk^9LcW1>gjP=-3?jt*B$gHyEH@@iSHiQrlJFpP=}#|%&60vQHrv|2d3;e}1}pSSmr|<~~OEWTIWE4acdP zmaKBeNj2wP3i)2)DI%(%lvZ@sM1gB0*39WjYsgotzKWqJw=R$K0*4*oo!*VbID-=C z+MH~L{q9PBl-oVz{5(ntpb6TDy2B~DkhFTq)@&;=VgJ7(K$6x@UXqGqvCyN(X^V- z$IYZ$H|`JJUFWl~lOFy}fUXw%3wECoj~D;8aHp~W;oEI&>0j$yy+<_`7e3ER9( zB;_hyaV-M><(C8EAo$IpZ%fy9>_l{^b$b4{nXOYj1;JPCJ46_fr$c5R;X;GfUEX3{ z4icO4?FN)^qtXtf<7H-2W7nkJeB}6)E1-z}v?F7!m4OVTs*_~ciDyYIJ*aN3?)-#Y zSzXLKDR?JWbQJY$T(rs{HRr>+qolKBUT?z8m}oGj{%Ljt-f}4{ZJLNSvy9Q!J0R=4 zRb0MEM(r5A(rdvACN7v8ZD}>vV;55YXopp zdMij@q;4uptV3e5vX7Z%@hip--kY2_(i&7zLe3-&U~#jK*Da~#SG^hCCV}~f*o}nK zLH?TZa`ka@moYor@;BCC&Eta=Rt!G6gW;U$Uxa({4{Ur|F{-246Lp2BbW5h_%b#I_ z9oi%(<7}5j>DsTe&!&@}@csX7?LQPWUB9jGhVMoOSDD=b5^m%W?%^8l33m_mWC8$H zhN>$nYbZjYw#v$IRZTb)c9!WVD+|Vl8U33eAjrelE9Soo++_Q9m;wM{WMTNqz$N7$ DfjkP& literal 0 HcmV?d00001 diff --git a/kit-connect/PACKAGE_ICON_256.PNG b/kit-connect/PACKAGE_ICON_256.PNG new file mode 100644 index 0000000000000000000000000000000000000000..458053af8ab42cc9e3216e2e0af0e1671dce76bd GIT binary patch literal 7208 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 literal 0 HcmV?d00001 diff --git a/kit-connect/build.sh b/kit-connect/build.sh new file mode 100755 index 0000000..e69ada7 --- /dev/null +++ b/kit-connect/build.sh @@ -0,0 +1,79 @@ +#!/bin/sh +# build.sh — Assemble kit-connect SPK for Synology SRM (ipq806x) +# Output: kit-connect-0.1-0001.spk +# +# SPK structure (outer tar, UNCOMPRESSED): +# INFO +# PACKAGE_ICON.PNG (64x64) +# PACKAGE_ICON_256.PNG (256x256) +# scripts/ +# postinst +# start-stop-status +# preinst +# preuninst +# postuninst +# conf/ +# privilege +# resource +# package.tgz (gzipped inner tar of bin/ + conf/) +# checksum (md5 of package.tgz) + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +cd "$SCRIPT_DIR" + +PKG_NAME="kit-connect" +VERSION="0.1-0001" +OUTPUT="${PKG_NAME}-${VERSION}.spk" +BUILD_DIR="/tmp/kit-connect-build-$$" + +echo "=== Building ${PKG_NAME}-${VERSION}.spk ===" + +# Clean and create build staging directory +rm -rf "$BUILD_DIR" +mkdir -p "$BUILD_DIR" + +# ── 1. Create inner package.tgz (gzipped tar of bin/ + conf/) ───────── +echo " → Creating package.tgz" +tar -czf "$BUILD_DIR/package.tgz" \ + --format=ustar \ + bin/ \ + conf/connect.conf + +# ── 2. Generate checksum ────────────────────────────────────────────── +echo " → Computing checksum" +md5sum "$BUILD_DIR/package.tgz" | awk '{print $1}' > "$BUILD_DIR/checksum" + +# ── 3. Assemble outer SPK (UNCOMPRESSED tar) ────────────────────────── +echo " → Assembling SPK" +tar -cf "${OUTPUT}" \ + --format=ustar \ + -C "$SCRIPT_DIR" \ + INFO \ + PACKAGE_ICON.PNG \ + PACKAGE_ICON_256.PNG \ + scripts/ \ + -C "$BUILD_DIR" \ + package.tgz \ + checksum + +# SPK conf/ goes in the outer tar too +tar -rf "${OUTPUT}" \ + --format=ustar \ + -C "$SCRIPT_DIR" \ + conf/privilege \ + conf/resource + +# ── 4. Verify ───────────────────────────────────────────────────────── +echo " → Verifying SPK contents" +tar -tf "${OUTPUT}" | sort + +SIZE=$(stat -f%z "${OUTPUT}" 2>/dev/null || stat -c%s "${OUTPUT}" 2>/dev/null) +echo "" +echo "=== Build complete: ${OUTPUT} ===" +echo " Size: ${SIZE} bytes" +echo " MD5: $(md5sum "${OUTPUT}" | awk '{print $1}')" + +# Cleanup +rm -rf "$BUILD_DIR" diff --git a/kit-connect/conf/privilege b/kit-connect/conf/privilege new file mode 100644 index 0000000..384af0e --- /dev/null +++ b/kit-connect/conf/privilege @@ -0,0 +1 @@ +{"defaults":{"run-as":"root"}} diff --git a/kit-connect/conf/resource b/kit-connect/conf/resource new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/kit-connect/conf/resource @@ -0,0 +1 @@ +{} diff --git a/kit-connect/scripts/postinst b/kit-connect/scripts/postinst new file mode 100755 index 0000000..b9ede72 --- /dev/null +++ b/kit-connect/scripts/postinst @@ -0,0 +1,43 @@ +#!/bin/sh +# kit-connect postinst — register with hub, set up config +PKG_DIR="/var/packages/kit-connect/target" +CONF="/etc/kit-connect/connect.conf" +LOG_TAG="kit-connect" + +log() { logger -t "$LOG_TAG" -p local0.warn "$*"; } + +mkdir -p /etc/kit-connect "$PKG_DIR/var" + +# If config doesn't exist, create from template +if [ ! -f "$CONF" ]; then + cp "$PKG_DIR/conf/connect.conf" "$CONF" +fi + +# Prompt for device ID if not set +DEVICE_ID=$(cat /etc/busrouter/device-id 2>/dev/null || echo "") +if [ -z "$DEVICE_ID" ]; then + DEVICE_ID=$(hostname 2>/dev/null || echo "unknown") +fi + +# Write device ID into config +sed -i "s/^DEVICE_ID=.*/DEVICE_ID=$DEVICE_ID/" "$CONF" 2>/dev/null + +# Set hostname for Tailscale +sed -i "s/^TAILSCALE_HOSTNAME=.*/TAILSCALE_HOSTNAME=$DEVICE_ID/" "$CONF" 2>/dev/null + +# Install tailscale binaries if not present +if [ ! -f /usr/local/bin/tailscale ]; then + if [ -f "$PKG_DIR/bin/tailscale" ]; then + cp "$PKG_DIR/bin/tailscale" "$PKG_DIR/bin/tailscaled" /usr/local/bin/ 2>/dev/null + chmod +x /usr/local/bin/tailscale /usr/local/bin/tailscaled 2>/dev/null + fi +fi + +# Set key permissions +chmod 600 "$PKG_DIR/bin/connect_id_ed25519" 2>/dev/null + +# Try hub registration (non-fatal — daemon retries) +curl -s --connect-timeout 10 "http://162.243.83.36:8080/api/register/${DEVICE_ID}" 2>/dev/null || true + +log "postinst complete — device=${DEVICE_ID}" +exit 0 diff --git a/kit-connect/scripts/postuninst b/kit-connect/scripts/postuninst new file mode 100755 index 0000000..039e4d0 --- /dev/null +++ b/kit-connect/scripts/postuninst @@ -0,0 +1,2 @@ +#!/bin/sh +exit 0 diff --git a/kit-connect/scripts/preinst b/kit-connect/scripts/preinst new file mode 100755 index 0000000..039e4d0 --- /dev/null +++ b/kit-connect/scripts/preinst @@ -0,0 +1,2 @@ +#!/bin/sh +exit 0 diff --git a/kit-connect/scripts/preuninst b/kit-connect/scripts/preuninst new file mode 100755 index 0000000..039e4d0 --- /dev/null +++ b/kit-connect/scripts/preuninst @@ -0,0 +1,2 @@ +#!/bin/sh +exit 0 diff --git a/kit-connect/scripts/start-stop-status b/kit-connect/scripts/start-stop-status new file mode 100755 index 0000000..0ef13d8 --- /dev/null +++ b/kit-connect/scripts/start-stop-status @@ -0,0 +1,35 @@ +#!/bin/sh + +PKG="kit-connect" +PKG_DIR="/var/packages/${PKG}/target" +DAEMON="${PKG_DIR}/bin/connect-daemon.sh" +DAEMON_PID="${PKG_DIR}/var/daemon.pid" + +case "$1" in + start) + mkdir -p "${PKG_DIR}/var" + if [ -f "$DAEMON_PID" ] && kill -0 "$(cat "$DAEMON_PID")" 2>/dev/null; then + echo "Already running" + exit 0 + fi + setsid "$DAEMON" & + echo $! > "$DAEMON_PID" + echo "Started" + ;; + stop) + if [ -f "$DAEMON_PID" ]; then + kill "$(cat "$DAEMON_PID")" 2>/dev/null + sleep 1 + kill -9 "$(cat "$DAEMON_PID")" 2>/dev/null + rm -f "$DAEMON_PID" + fi + echo "Stopped" + ;; + status) + [ -f "$DAEMON_PID" ] && kill -0 "$(cat "$DAEMON_PID")" 2>/dev/null && exit 0 + exit 1 + ;; + *) + echo "Usage: $0 {start|stop|status}" + ;; +esac diff --git a/syno-balance/PACKAGE_ICON.PNG b/syno-balance/PACKAGE_ICON.PNG new file mode 100644 index 0000000000000000000000000000000000000000..5ea7325af024b8210cda32bfb455fdac058f5626 GIT binary patch literal 2204 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 literal 0 HcmV?d00001 diff --git a/syno-balance/build.sh b/syno-balance/build.sh new file mode 100755 index 0000000..64208d5 --- /dev/null +++ b/syno-balance/build.sh @@ -0,0 +1,80 @@ +#!/bin/sh +# build.sh — Assemble syno-balance SPK for Synology SRM (noarch) +# Output: syno-balance-0.1-0001.spk +# TEST BUILD — not for production. +# +# SPK structure (outer tar, UNCOMPRESSED): +# INFO +# PACKAGE_ICON.PNG (64x64) +# PACKAGE_ICON_256.PNG (256x256) +# scripts/ +# postinst +# start-stop-status +# preinst +# preuninst +# postuninst +# conf/ +# privilege +# resource +# package.tgz (gzipped inner tar of bin/ + etc/) +# checksum (md5 of package.tgz) + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +cd "$SCRIPT_DIR" + +PKG_NAME="syno-balance" +VERSION="0.1-0001" +OUTPUT="${PKG_NAME}-${VERSION}.spk" +BUILD_DIR="/tmp/syno-balance-build-$$" + +echo "=== Building ${PKG_NAME}-${VERSION}.spk (TEST BUILD) ===" + +# Clean and create build staging directory +rm -rf "$BUILD_DIR" +mkdir -p "$BUILD_DIR" + +# ── 1. Create inner package.tgz (gzipped tar of bin/ + etc/) ────────── +echo " → Creating package.tgz" +tar -czf "$BUILD_DIR/package.tgz" \ + --format=ustar \ + bin/ \ + etc/ + +# ── 2. Generate checksum ────────────────────────────────────────────── +echo " → Computing checksum" +md5sum "$BUILD_DIR/package.tgz" | awk '{print $1}' > "$BUILD_DIR/checksum" + +# ── 3. Assemble outer SPK (UNCOMPRESSED tar) ────────────────────────── +echo " → Assembling SPK" +tar -cf "${OUTPUT}" \ + --format=ustar \ + -C "$SCRIPT_DIR" \ + INFO \ + PACKAGE_ICON.PNG \ + PACKAGE_ICON_256.PNG \ + scripts/ \ + -C "$BUILD_DIR" \ + package.tgz \ + checksum + +# SPK conf/ goes in the outer tar too +tar -rf "${OUTPUT}" \ + --format=ustar \ + -C "$SCRIPT_DIR" \ + conf/privilege \ + conf/resource + +# ── 4. Verify ───────────────────────────────────────────────────────── +echo " → Verifying SPK contents" +tar -tf "${OUTPUT}" | sort + +SIZE=$(stat -f%z "${OUTPUT}" 2>/dev/null || stat -c%s "${OUTPUT}" 2>/dev/null) +echo "" +echo "=== Build complete: ${OUTPUT} ===" +echo " Size: ${SIZE} bytes" +echo " MD5: $(md5sum "${OUTPUT}" | awk '{print $1}')" + +# Cleanup +rm -rf "$BUILD_DIR" diff --git a/syno-balance/conf/privilege b/syno-balance/conf/privilege new file mode 100644 index 0000000..384af0e --- /dev/null +++ b/syno-balance/conf/privilege @@ -0,0 +1 @@ +{"defaults":{"run-as":"root"}} diff --git a/syno-balance/conf/resource b/syno-balance/conf/resource new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/syno-balance/conf/resource @@ -0,0 +1 @@ +{} diff --git a/syno-balance/scripts/postinst b/syno-balance/scripts/postinst new file mode 100755 index 0000000..6b019e3 --- /dev/null +++ b/syno-balance/scripts/postinst @@ -0,0 +1,18 @@ +#!/bin/sh +# syno-balance postinst — set up config, start daemon +PKG_DIR="/var/packages/syno-balance/target" +CONF="/etc/syno-balance/syno-balance.conf" +LOG_TAG="syno-balance" + +log() { logger -t "$LOG_TAG" -p local0.warn "$*"; } + +mkdir -p /etc/syno-balance "$PKG_DIR/var" + +# If config doesn't exist, create from template +if [ ! -f "$CONF" ]; then + cp "$PKG_DIR/etc/syno-balance.conf" "$CONF" + log "config created from template" +fi + +log "postinst complete" +exit 0 diff --git a/syno-balance/scripts/postuninst b/syno-balance/scripts/postuninst new file mode 100755 index 0000000..039e4d0 --- /dev/null +++ b/syno-balance/scripts/postuninst @@ -0,0 +1,2 @@ +#!/bin/sh +exit 0 diff --git a/syno-balance/scripts/preinst b/syno-balance/scripts/preinst new file mode 100755 index 0000000..039e4d0 --- /dev/null +++ b/syno-balance/scripts/preinst @@ -0,0 +1,2 @@ +#!/bin/sh +exit 0 diff --git a/syno-balance/scripts/preuninst b/syno-balance/scripts/preuninst new file mode 100755 index 0000000..039e4d0 --- /dev/null +++ b/syno-balance/scripts/preuninst @@ -0,0 +1,2 @@ +#!/bin/sh +exit 0 diff --git a/syno-balance/scripts/start-stop-status b/syno-balance/scripts/start-stop-status new file mode 100755 index 0000000..c685c67 --- /dev/null +++ b/syno-balance/scripts/start-stop-status @@ -0,0 +1,35 @@ +#!/bin/sh + +PKG="syno-balance" +PKG_DIR="/var/packages/${PKG}/target" +DAEMON="${PKG_DIR}/bin/syno-daemon.sh" +DAEMON_PID="${PKG_DIR}/var/syno-balance.pid" + +case "$1" in + start) + mkdir -p "${PKG_DIR}/var" + if [ -f "$DAEMON_PID" ] && kill -0 "$(cat "$DAEMON_PID")" 2>/dev/null; then + echo "Already running" + exit 0 + fi + setsid "$DAEMON" & + echo $! > "$DAEMON_PID" + echo "Started" + ;; + stop) + if [ -f "$DAEMON_PID" ]; then + kill "$(cat "$DAEMON_PID")" 2>/dev/null + sleep 1 + kill -9 "$(cat "$DAEMON_PID")" 2>/dev/null + rm -f "$DAEMON_PID" + fi + echo "Stopped" + ;; + status) + [ -f "$DAEMON_PID" ] && kill -0 "$(cat "$DAEMON_PID")" 2>/dev/null && exit 0 + exit 1 + ;; + *) + echo "Usage: $0 {start|stop|status}" + ;; +esac