From: Sergey Matveev Date: Tue, 12 Aug 2025 14:38:58 +0000 (+0300) Subject: Initial commit X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=4deff912cd03d54389ad58e7360d101fdff744246c250c044dbe6f8835575e8b;p=dsc.git Initial commit --- 4deff912cd03d54389ad58e7360d101fdff744246c250c044dbe6f8835575e8b diff --git a/dsc b/dsc new file mode 100755 index 0000000..5d2e860 --- /dev/null +++ b/dsc @@ -0,0 +1,279 @@ +#!/usr/bin/env tclsh +# dsc -- damn small configuration manager +# Copyright (C) 2025 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +if {$argc == 0} { + puts -nonewline stderr {Usage: + dsc list [-v] [prefix] -- list all available options + dsc add opt -- add /*/ section + dsc del opt -- delete /*/ section + dsc del "" -- delete the whole configuration, useful for import + dsc has opt -- check if specified /*/ option exists + dsc set opt value -- set option's value + dsc set opt "" -- unset value, make it default one + dsc get opt -- get option's value, maybe default one + dsc get opt/* -- list /*/ sections + dsc diff [prefix] -- show the difference between committed and current + dsc revert opt -- revert opt's configuration + dsc commit -- commit (save) configuration + dsc export >file.txtar -- export whole configuration + dsc import 1 && [lindex $argv 1] == "-v"} { + set verbose y + set prefix [lindex $argv 2] + } + foreach opt [walk $Schema d] { + if {! [file exists $opt/title]} {continue} + set name [string range $opt [expr {[string length $Schema] + 1}] end] + if {$prefix != ""} { + if {[string range $name 0 [string length $prefix]-1] != $prefix} { + continue + } + } + set v [fileread $opt/title] + puts -nonewline "$name\t$v" + if {$verbose} { + if {[file exists $opt/descr]} { + set lines [split [fileread $opt/descr] "\n"] + set lines [lrange $lines 0 end-1] + foreach line $lines { + puts "\t$line" + } + } + } + } + } + add { + set dir [file dirname $opt] + set tail [run-checker $opt [file tail $opt]] + set tail [string trimright $tail "\n"] + file mkdir $Curr/$dir/$tail + puts $dir/$tail + } + del { + if {$opt != ""} { + assure-exists $opt + } + file delete -force $Curr/$opt + } + set { + if {[llength $argv] == 2} { + set v [read -nonewline stdin] + } else { + set v [lindex $argv 2] + } + if {$v == ""} { + file delete $Curr/$opt + exit + } + set v [run-checker $opt $v] + file mkdir "$Curr/[file dirname $opt]" + set fh [open $Curr/$opt w] + puts -nonewline $fh $v + close $fh + } + has { + assure-exists $opt + } + get-checker { + puts [find-checker $opt] + } + get { + if {[file tail $opt] == "*"} { + set opt [file dirname $opt] + assure-exists $opt + set dirs [glob -directory $Curr/$opt -types d -tails -nocomplain -- *] + foreach dir [lsort $dirs] { + puts $dir + } + exit + } + if {[file exists $Curr/$opt]} { + puts -nonewline [fileread $Curr/$opt] + exit + } + puts -nonewline [run-checker $opt ""] + } + diff { + set fh [file tempfile dirsComm] + foreach fn [walk $Comm/$opt d] { + puts $fh [string range $fn [string length $Comm]+1 end] + } + close $fh + set fh [file tempfile dirsCurr] + foreach fn [walk $Curr/$opt d] { + puts $fh [string range $fn [string length $Curr]+1 end] + } + close $fh + set fh [open "| diff -u -L dirs/committed -L dirs/current + $dirsComm $dirsCurr" r] + puts -nonewline [read $fh] + catch {close $fh} + file delete $dirsComm + file delete $dirsCurr + set fh [open "| diff -urN $Comm/$opt $Curr/$opt" r] + puts -nonewline [read $fh] + catch {close $fh} + } + revert { + catch {file delete -force $Curr/$opt} + catch {file copy $Comm/$opt $Curr/$opt} + } + commit { + file delete -force $Comm.bak + set tmp $Comm.[expr {int(rand() * 1000000)}] + file copy $Curr $tmp + file rename $Comm $Comm.bak + file rename $tmp $Comm + file delete -force $Comm.bak + } + export { + set dirs [walk $Comm d] + puts "-- .dirs --" + foreach fn $dirs { + puts [string range $fn [string length $Comm]+1 end] + } + foreach dir $dirs { + foreach fn [walk $dir f] { + puts "-- [string range $fn [string length $Comm]+1 end] --" + puts -nonewline [fileread $fn] + } + } + } + import { + set fn "" + set lines [list] + proc filewrite {fn v} { + global Curr + if {[llength $v] == 0} { + return + } + if {$fn == ".dirs"} { + foreach dir $v { + file mkdir $Curr/$dir + } + return + } + file mkdir [file dirname $Curr/$fn] + set fh [open $Curr/$fn w] + puts $fh [join $v "\n"] + close $fh + } + while {[gets stdin line] >= 0} { + if { + [string length $line] > 6 && + [string range $line 0 2] == "-- " && + [string range $line end-2 end] == " --" + } { + if {$fn != ""} { + filewrite $fn $lines + set lines [list] + } + set fn [string range $line 3 end-3] + } else { + lappend lines $line + } + } + if {$fn != ""} { + filewrite $fn $lines + } + } +} diff --git a/schema/net/*/addr/*/check b/schema/net/*/addr/*/check new file mode 100755 index 0000000..172909c --- /dev/null +++ b/schema/net/*/addr/*/check @@ -0,0 +1,19 @@ +#!/usr/bin/env tclsh8.6 + +set addr [read -nonewline stdin] +package require ip +set version [::ip::version $addr] +if {$version == -1} { + puts "invalid format" + exit 1 +} +if {[::ip::mask $addr] != ""} { + puts "prefixlen must be empty" + exit 1 +} +if {$version == 4} { + set addr [::ip::normalize $addr] +} else { + set addr [::ip::contract $addr] +} +puts $addr diff --git a/schema/net/*/addr/*/descr b/schema/net/*/addr/*/descr new file mode 100644 index 0000000..fd3e48c --- /dev/null +++ b/schema/net/*/addr/*/descr @@ -0,0 +1,2 @@ +No CIDR notation, just a pure address. +Set prefix length through net/*/addr/*/prefixlen. diff --git a/schema/net/*/addr/*/prefixlen/check b/schema/net/*/addr/*/prefixlen/check new file mode 100755 index 0000000..e673657 --- /dev/null +++ b/schema/net/*/addr/*/prefixlen/check @@ -0,0 +1,23 @@ +#!/usr/bin/env tclsh8.6 + +set addr [file tail [file dirname [lindex $argv 0]]] +set maxlen 128 +set deflen 64 +package require ip +if {[::ip::version $addr] == 4} { + set maxlen 32 + set deflen 24 +} +set prefixlen [read -nonewline stdin] +if {$prefixlen == ""} { + set prefixlen $deflen +} +if {! [string is integer -strict $prefixlen]} { + puts "invalid integer" + exit 1 +} +if {$prefixlen <= 0 || $prefixlen > $maxlen} { + puts "not in range" + exit 1 +} +puts $prefixlen diff --git a/schema/net/*/addr/*/prefixlen/descr b/schema/net/*/addr/*/prefixlen/descr new file mode 100644 index 0000000..ea80de7 --- /dev/null +++ b/schema/net/*/addr/*/prefixlen/descr @@ -0,0 +1 @@ +If not set, then defaults to /64 (or /24 for IPv4). diff --git a/schema/net/*/addr/*/prefixlen/title b/schema/net/*/addr/*/prefixlen/title new file mode 100644 index 0000000..3c0d1c5 --- /dev/null +++ b/schema/net/*/addr/*/prefixlen/title @@ -0,0 +1 @@ +Prefix length diff --git a/schema/net/*/addr/*/title b/schema/net/*/addr/*/title new file mode 100644 index 0000000..7e93a2e --- /dev/null +++ b/schema/net/*/addr/*/title @@ -0,0 +1 @@ +Network address diff --git a/schema/net/*/check b/schema/net/*/check new file mode 100755 index 0000000..0d492a1 --- /dev/null +++ b/schema/net/*/check @@ -0,0 +1,8 @@ +#!/usr/bin/env tclsh8.6 + +set n [read -nonewline stdin] +if {! [regexp {^[a-z0-9]+$} $n]} { + puts {does not match ^[a-z0-9]+$} + exit 1 +} +puts $n diff --git a/schema/net/*/mtu/check b/schema/net/*/mtu/check new file mode 100755 index 0000000..69c3899 --- /dev/null +++ b/schema/net/*/mtu/check @@ -0,0 +1,15 @@ +#!/usr/bin/env tclsh8.6 + +set i [read -nonewline stdin] +if {$i == ""} { + set i 1500 +} +if {! [string is integer -strict $i]} { + puts "invalid integer" + exit 1 +} +if {$i <= 0 || $i > 65535} { + puts "not in (0..65536) range" + exit 1 +} +puts $i diff --git a/schema/net/*/mtu/descr b/schema/net/*/mtu/descr new file mode 100644 index 0000000..00b5788 --- /dev/null +++ b/schema/net/*/mtu/descr @@ -0,0 +1 @@ +If not set, then defaults to 1500. diff --git a/schema/net/*/mtu/title b/schema/net/*/mtu/title new file mode 100644 index 0000000..9e88021 --- /dev/null +++ b/schema/net/*/mtu/title @@ -0,0 +1 @@ +Maximum transmission unit diff --git a/schema/net/*/title b/schema/net/*/title new file mode 100644 index 0000000..e8d980d --- /dev/null +++ b/schema/net/*/title @@ -0,0 +1 @@ +Network interface name diff --git a/schema/srv/*/autostart/check b/schema/srv/*/autostart/check new file mode 100755 index 0000000..71c4051 --- /dev/null +++ b/schema/srv/*/autostart/check @@ -0,0 +1,11 @@ +#!/usr/bin/env tclsh8.6 + +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/srv/*/autostart/title b/schema/srv/*/autostart/title new file mode 100644 index 0000000..2bdb90a --- /dev/null +++ b/schema/srv/*/autostart/title @@ -0,0 +1 @@ +Is autostart enabled? diff --git a/schema/srv/*/check b/schema/srv/*/check new file mode 100755 index 0000000..0f8cef8 --- /dev/null +++ b/schema/srv/*/check @@ -0,0 +1,8 @@ +#!/usr/bin/env tclsh8.6 + +set n [read -nonewline stdin] +if {! [regexp {^[.A-Za-z0-9_-]+$} $n]} { + puts {does not match ^[.A-Za-z0-9_-]+$} + exit 1 +} +puts $n diff --git a/schema/srv/*/title b/schema/srv/*/title new file mode 100644 index 0000000..0cceb55 --- /dev/null +++ b/schema/srv/*/title @@ -0,0 +1 @@ +Service/daemon name diff --git a/schema/sys/hostname/check b/schema/sys/hostname/check new file mode 100755 index 0000000..a90bf84 --- /dev/null +++ b/schema/sys/hostname/check @@ -0,0 +1,11 @@ +#!/usr/bin/env tclsh8.6 + +set n [read -nonewline stdin] +if {$n == ""} { + set n unknown +} +if {! [regexp {^[a-z0-9]+$} $n]} { + puts {does not match ^[a-z0-9]+$} + exit 1 +} +puts $n diff --git a/schema/sys/hostname/title b/schema/sys/hostname/title new file mode 100644 index 0000000..96010e8 --- /dev/null +++ b/schema/sys/hostname/title @@ -0,0 +1 @@ +Hostname diff --git a/schema/sys/note/check b/schema/sys/note/check new file mode 100755 index 0000000..7481f32 --- /dev/null +++ b/schema/sys/note/check @@ -0,0 +1,3 @@ +#!/bin/sh + +exec cat diff --git a/schema/sys/note/title b/schema/sys/note/title new file mode 100644 index 0000000..3f7558b --- /dev/null +++ b/schema/sys/note/title @@ -0,0 +1 @@ +Arbitrary note diff --git a/schema/ui/password/check b/schema/ui/password/check new file mode 100755 index 0000000..f396097 --- /dev/null +++ b/schema/ui/password/check @@ -0,0 +1,11 @@ +#!/usr/bin/env tclsh8.6 + +set passwd [read -nonewline stdin] +if {$passwd == ""} { + set passwd admin +} +if {! [regexp {^[0-9a-z-]+$} $passwd]} { + puts {does not match ^[0-9a-z-]+$} + exit 1 +} +puts $passwd diff --git a/schema/ui/password/descr b/schema/ui/password/descr new file mode 100644 index 0000000..f3a29c6 --- /dev/null +++ b/schema/ui/password/descr @@ -0,0 +1 @@ +Password for the admin user accessing WebUI. diff --git a/schema/ui/password/title b/schema/ui/password/title new file mode 100644 index 0000000..22c409e --- /dev/null +++ b/schema/ui/password/title @@ -0,0 +1 @@ +WebUI's admin password