]> Cypherpunks repositories - dsc.git/commitdiff
Apply functionality
authorSergey Matveev <stargrave@stargrave.org>
Thu, 27 Nov 2025 12:42:58 +0000 (15:42 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Thu, 27 Nov 2025 12:43:23 +0000 (15:43 +0300)
12 files changed:
dsc
schema/net/*/apply [new file with mode: 0755]
schema/net/apply [new file with mode: 0755]
schema/ssh/apply [new file with mode: 0755]
schema/ssh/authorized_keys/*/check [new file with mode: 0755]
schema/ssh/authorized_keys/*/pub/check [new file with mode: 0755]
schema/ssh/enabled/check [new file with mode: 0755]
schema/ssh/port/check [new file with mode: 0755]
schema/ssh/prv/bin [new file with mode: 0644]
schema/ssh/pub/check [new file with mode: 0755]
schema/ssh/title [new file with mode: 0644]
schema/sys/hostname/apply [new file with mode: 0755]

diff --git a/dsc b/dsc
index fcc6ec368b5f266c9bb2fa0bced18d0b344fd59c51900eeb7dace78db14e0562..9a7edad7a0d9759b4ab0e15f680216cb8517f796869919b32aa78927bd4a7fb6 100755 (executable)
--- a/dsc
+++ b/dsc
@@ -50,12 +50,16 @@ if {[catch {set Schema $env(DSC_SCHEMA)}]} {set Schema schema}
 if {[catch {set Stash $env(DSC_STASH)}]} {set Stash stash}
 if {[catch {set Saved $env(DSC_SAVED)}]} {set Saved saved}
 
+proc readents {root typ} {
+    set ents [glob -directory $root -tails -nocomplain -- *]
+    set ents [lmap d $ents {if {[file type $root/$d] == $typ} {set d} {continue}}]
+    return [lsort $ents]
+}
+
 proc walk {root typ} {
     set rv [list]
     set root [string trimright $root /]
-    set dirs [glob -directory $root -tails -nocomplain -- *]
-    set dirs [lmap d $dirs {if {[file type $root/$d] == $typ} {set d} {continue}}]
-    foreach s [lsort $dirs] {
+    foreach s [readents $root $typ] {
         lappend rv $root/$s
         lappend rv {*}[walk $root/$s $typ]
     }
@@ -71,21 +75,21 @@ proc fileread {fn} {
 
 proc find-opt-schema {opt} {
     global Schema
-    set path .
+    set pth .
     set opt [string trimright $opt /]
     foreach e [split $opt /] {
-        if {[file exists $Schema/$path/$e]} {
-            set path $path/$e
+        if {[file exists $Schema/$pth/$e]} {
+            set pth $pth/$e
             continue
         }
-        if {[file exists $Schema/$path/*]} {
-            set path $path/*
+        if {[file exists $Schema/$pth/*]} {
+            set pth $pth/*
             continue
         }
         puts stderr "can not find $opt in schema"
         exit 1
     }
-    return [string range $path 2 end]
+    return [string range $pth 2 end]
 }
 
 proc is-bin {opt} {
@@ -192,7 +196,7 @@ switch [lindex $argv 0] {
     has {
         assure-exists $opt
     }
-    get-checker {
+    find-opt-schema {
         puts [find-opt-schema $opt]
     }
     get {
@@ -201,10 +205,7 @@ switch [lindex $argv 0] {
             if {! [file exists $Stash/$opt]} {
                 exit
             }
-            set dirs [glob -directory $Stash/$opt -tails -nocomplain -- *]
-            set dirs [lmap d $dirs { \
-                if {[file type $Stash/$opt/$d] == "directory"} {set d} {continue}}]
-            foreach dir [lsort $dirs] {
+            foreach dir [readents $Stash/$opt directory] {
                 puts $dir
             }
             exit
@@ -223,6 +224,30 @@ switch [lindex $argv 0] {
         }
         puts -nonewline [run-checker $opt ""]
     }
+    apply {
+        set pth [find-opt-schema $opt]
+        if {[file exists $Schema/$pth/apply]} {
+            puts stderr "applying $opt..."
+            if {[catch {exec | [list "$Schema/$pth/apply" $opt] >@stdout 2>@stderr}]} {
+                exit 1
+            }
+            exit
+        }
+        if {[file exists $Schema/$pth/*]} {
+            set rc 0
+            foreach dir [readents $Stash/$opt directory] {
+                if {[catch {exec | [list dsc apply $opt/$dir] >@stdout 2>@stderr}]} {
+                    set rc 1
+                }
+            }
+            exit $rc
+        }
+        foreach ent [readents $Schema/$opt directory] {
+            if {[catch {exec | [list dsc apply $opt/$ent] >@stdout 2>@stderr}]} {
+                set rc 1
+            }
+        }
+    }
     diff {
         set dirsSaved [file tempfile dirsSaved.XXXXX]
         set fh [open $dirsSaved w]
diff --git a/schema/net/*/apply b/schema/net/*/apply
new file mode 100755 (executable)
index 0000000..fd539ca
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/env jimsh
+
+proc list-addrs {iface} {
+    set addrs [list]
+    catch {exec ip addr list dev $iface} lines
+    foreach l [split $lines \n] {
+        if {[regexp {inet (\S+)} $l _ addr]} {
+            lappend addrs $addr
+        }
+        if {[regexp {inet6 (\S+)} $l _ addr] && [string range $addr 0 4] != "fe80:"} {
+            lappend addrs $addr
+        }
+    }
+    return $addrs
+}
+
+# TODO: check that interface exists
+
+set iface [file tail [lindex $argv 0]]
+set mtu [exec dsc get net/$iface/mtu]
+puts "ip link set $iface mtu $mtu"
+puts "ip link set $iface up"
+
+set addrs [list]
+foreach addr [split [exec dsc get net/$iface/addr/*] \n] {
+    set prefixlen [exec dsc get net/$iface/addr/$addr/prefixlen]
+    lappend addrs $addr/$prefixlen
+}
+foreach addr [list-addrs $iface] {
+    set idx [lsearch -exact $addrs $addr]
+    if {$idx == -1} {
+        puts "ip addr del $addr dev $iface"
+    } else {
+        # Address is already present
+        set $addrs [lreplace $addrs $idx $idx]
+    }
+}
+foreach addr $addrs {
+    puts "ip addr add $addr dev $iface"
+}
diff --git a/schema/net/apply b/schema/net/apply
new file mode 100755 (executable)
index 0000000..bb5eeec
--- /dev/null
@@ -0,0 +1,37 @@
+#!/usr/bin/env jimsh
+
+proc list-addrs {iface} {
+    set addrs [list]
+    catch {exec ip addr list dev $iface} lines
+    foreach l [split $lines \n] {
+        if {[regexp {inet (\S+)} $l _ addr]} {
+            lappend addrs $addr
+        }
+        if {[regexp {inet6 (\S+)} $l _ addr] && [string range $addr 0 4] != "fe80:"} {
+            lappend addrs $addr
+        }
+    }
+    return $addrs
+}
+
+set sysIfaces [list]
+foreach l [split [exec ip link] \n] {
+    if {[regexp {^\d+: (\w+): } $l _ iface] && $iface != "lo"} {
+        lappend sysIfaces $iface
+    }
+}
+
+set dscIfaces [split [exec dsc get net/*] \n]
+
+foreach iface $sysIfaces {
+    if {[lsearch -exact $dscIfaces $iface] == -1} {
+        # If interface is not mentioned in dsc, then disable it and remove
+        # all addresses, as they are still known to the system
+        puts "ip link set $iface down"
+        foreach addr [list-addrs $iface] {
+            puts "ip addr del $addr dev $iface"
+        }
+    } else {
+        exec | [list dsc apply net/$iface] >@stdout 2>@stderr
+    }
+}
diff --git a/schema/ssh/apply b/schema/ssh/apply
new file mode 100755 (executable)
index 0000000..cadb323
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/sh -e
+
+tmp=$(mktemp -d)
+trap "rm -fr $tmp" HUP PIPE INT QUIT TERM EXIT
+
+port=$(dsc get ssh/port)
+dsc get ssh/pub >$tmp/id_ed25519.pub
+
+umaskPrev=$(umask)
+umask 077
+dsc get ssh/prv >$tmp/id_ed25519
+umask $umaskPrev
+
+cat >$tmp/run <<EOF
+#!/bin/sh -e
+exec dropbear -FE -p $port -P dropbear.pid -r id_ed25519
+EOF
+chmod +x $tmp/run
+
+set -f
+dsc get ssh/authorized_keys/* | while read name ; do
+    dsc has ssh/authorized_keys/$name/pub 2>/dev/null || continue
+    dsc get ssh/authorized_keys/$name/pub >>$tmp/.authorized_keys
+done
+
+enabled=$(dsc get ssh/enabled)
+if [ "$enabled" != yes ] ; then
+    touch $tmp/down
+fi
+
+if [ -d /tmp/dropbear.srv ] ; then
+    mv /tmp/dropbear.srv $tmp/prev
+    mv $tmp /tmp/dropbear.srv
+    rm -r /tmp/dropbear.srv/prev
+else
+    mv $tmp /tmp/dropbear.srv
+fi
+
+if [ "$enabled" = yes ] ; then
+    echo sv restart /tmp/dropbear.srv
+else
+    echo sv stop /tmp/dropbear.srv
+fi
diff --git a/schema/ssh/authorized_keys/*/check b/schema/ssh/authorized_keys/*/check
new file mode 100755 (executable)
index 0000000..46e4eea
--- /dev/null
@@ -0,0 +1,8 @@
+#!/usr/bin/env jimsh
+
+set n [read -nonewline stdin]
+if {[llength $n] == 0} {
+    puts {empty name}
+    exit 1
+}
+puts $n
diff --git a/schema/ssh/authorized_keys/*/pub/check b/schema/ssh/authorized_keys/*/pub/check
new file mode 100755 (executable)
index 0000000..158ef73
--- /dev/null
@@ -0,0 +1,8 @@
+#!/usr/bin/env jimsh
+
+set n [read -nonewline stdin]
+if {! [regexp {^ssh-ed25519 [.A-Za-z0-9/+]{68,68}} $n]} {
+    puts {bad ssh-ed25519 key}
+    exit 1
+}
+puts $n
diff --git a/schema/ssh/enabled/check b/schema/ssh/enabled/check
new file mode 100755 (executable)
index 0000000..f9a1b3b
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env jimsh
+
+set n [read -nonewline stdin]
+if {$n == ""} {
+    set n no
+}
+if {! [string is boolean $n]} {
+    puts {is not boolean}
+    exit 1
+}
+puts $n
diff --git a/schema/ssh/port/check b/schema/ssh/port/check
new file mode 100755 (executable)
index 0000000..8a697a1
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env tclsh8.6
+
+set n [read -nonewline stdin]
+if {$n == ""} {
+    set n 22
+}
+if {! [string is integer -strict $n]} {
+    puts "invalid integer"
+    exit 1
+}
+puts $n
diff --git a/schema/ssh/prv/bin b/schema/ssh/prv/bin
new file mode 100644 (file)
index 0000000..473a0f4
diff --git a/schema/ssh/pub/check b/schema/ssh/pub/check
new file mode 100755 (executable)
index 0000000..158ef73
--- /dev/null
@@ -0,0 +1,8 @@
+#!/usr/bin/env jimsh
+
+set n [read -nonewline stdin]
+if {! [regexp {^ssh-ed25519 [.A-Za-z0-9/+]{68,68}} $n]} {
+    puts {bad ssh-ed25519 key}
+    exit 1
+}
+puts $n
diff --git a/schema/ssh/title b/schema/ssh/title
new file mode 100644 (file)
index 0000000..cfb765d
--- /dev/null
@@ -0,0 +1 @@
+Dropbear SSH
diff --git a/schema/sys/hostname/apply b/schema/sys/hostname/apply
new file mode 100755 (executable)
index 0000000..79db824
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh -e
+
+echo "echo $(dsc get $1) >/etc/hostname"