commit 533bacc

merc  ·  2026-07-31 20:06:37 +0000 UTC
parent 533bacc
init
4 files changed,  +144, -0
A bs
A bs.1
+17, -0
 1@@ -0,0 +1,17 @@
 2+PREFIX ?= /usr/local
 3+BINDIR ?= $(PREFIX)/bin
 4+MANDIR ?= $(PREFIX)/share/man/man1
 5+
 6+all:
 7+
 8+install:
 9+	mkdir -p $(DESTDIR)$(BINDIR)
10+	mkdir -p $(DESTDIR)$(MANDIR)
11+	install -m 755 bs $(DESTDIR)$(BINDIR)/bs
12+	install -m 644 bs.1 $(DESTDIR)$(MANDIR)/bs.1
13+
14+uninstall:
15+	rm -f $(DESTDIR)$(BINDIR)/bs
16+	rm -f $(DESTDIR)$(MANDIR)/bs.1
17+
18+.PHONY: all install uninstall
+1, -0
1@@ -0,0 +1 @@
2+`man bs`
A bs
+88, -0
 1@@ -0,0 +1,88 @@
 2+#!/bin/sh
 3+
 4+serviceNm="sing-box"
 5+
 6+m=0
 7+for f in /etc/sing-box/list/*.json; do
 8+	[ -f "$f" ] || continue
 9+	n=$(basename "$f" .json)
10+	[ ${#n} -gt $m ] && m=${#n}
11+done
12+
13+case "$1" in
14+	-L|--list)
15+		i=1
16+		for f in /etc/sing-box/list/*.json; do
17+			[ -f "$f" ] || continue
18+			n=$(basename "$f" .json)
19+			p=$(jq -r '.outbounds[] | select(.type != "direct") | .type' "$f")
20+			[ -z "$p" ] && p="unknown"
21+			printf "%-*s [%d] [%s]\n" "$m" "$n" "$i" "$p"
22+			i=$((i + 1))
23+		done
24+		;;
25+	-C|--current)
26+		if [ -L /etc/sing-box/active.json ] && [ -e /etc/sing-box/active.json ]; then
27+			l=$(readlink /etc/sing-box/active.json)
28+			n=$(basename "$l" .json)
29+			p=$(jq -r '.outbounds[] | select(.type != "direct") | .type' "$l")
30+			[ -z "$p" ] && p="unknown"
31+			i=1
32+			for f in /etc/sing-box/list/*.json; do
33+				if [ -f "$f" ] && [ "$(basename "$f" .json)" = "$n" ]; then
34+					printf "%-*s [%d] [%s]\n" "$m" "$n" "$i" "$p"
35+					break
36+				fi
37+				i=$((i + 1))
38+			done
39+		else
40+			echo "no active config"
41+		fi
42+		;;
43+	-S|--stop)
44+		if [ "$(id -u)" -ne 0 ]; then
45+			echo "run as root"
46+			exit 1
47+		fi
48+		rc-service "$serviceNm" stop
49+		rm -f /etc/sing-box/active.json
50+		;;
51+	-*)
52+		echo "invalid option $1"
53+		exit 1
54+		;;
55+	*)
56+		if [ -z "$1" ]; then
57+			echo "rtfm"
58+			echo "> man bs"
59+			exit 1
60+		fi
61+
62+		if [ "$1" -eq "$1" ] 2>/dev/null; then
63+			if [ "$(id -u)" -ne 0 ]; then
64+				echo "run as root"
65+				exit 1
66+			fi
67+			i=1
68+			for f in /etc/sing-box/list/*.json; do
69+				[ -f "$f" ] || continue
70+				if [ "$i" -eq "$1" ]; then
71+					ln -sf "/etc/sing-box/list/$(basename "$f")" /etc/sing-box/active.json
72+					rc-service "$serviceNm" restart
73+					exit 0
74+				fi
75+				i=$((i + 1))
76+			done
77+			echo "index $1 not found"
78+			exit 1
79+		fi
80+
81+		if [ ! -f "/etc/sing-box/list/$1.json" ]; then
82+			echo "config $1 not found"
83+			exit 1
84+		fi
85+
86+		ln -sf "/etc/sing-box/list/$1.json" /etc/sing-box/active.json
87+		rc-service "$serviceNm" restart
88+		;;
89+esac
A bs.1
+38, -0
 1@@ -0,0 +1,38 @@
 2+.Dd $Mdocdate$
 3+.Dt BS 1
 4+.Os
 5+.Sh NAME
 6+.Nm bs
 7+.Nd a shell script for controlling the
 8+.Em 'sing-box'
 9+service
10+.Sh SYNOPSIS
11+.Bl -tag -width Ds
12+.It Fl L , Fl -list
13+list available configs
14+.It Fl C , Fl -current
15+show the currently used config
16+.It Fl S , Fl -stop
17+stop the sing-box service and clean up
18+.El
19+.Sh DESCRIPTION
20+.Nm
21+switches between .json configs from
22+.Pa /etc/sing-box/list/
23+.Pp
24+the selected config is symlinked to
25+.Pa /etc/sing-box/active.json
26+.Pp
27+the
28+.Em sing-box-openrc
29+package on Alpine should provide the required service. if not, write your own.
30+.Pp 
31+this was written for
32+.Em OpenRC ,
33+so no other init systems are supported. you're free to change this script locally as you wish.
34+.Sh SEE ALSO
35+.Xr rc-service 8 ,
36+.Xr ln 1 ,
37+.Lk https://sing-box.sagernet.org/configuration/ "creating a config"
38+.Sh AUTHORS
39+.An merc <merc@4plt.ch>