Compare commits
No commits in common. "9ecd2b4fb489fe09c4e9683d814010feefc18578" and "4e51977ae014e26e345304eb405e35a99dbf64f6" have entirely different histories.
9ecd2b4fb4
...
4e51977ae0
182
THOUGHTS.txt
182
THOUGHTS.txt
@ -7196,185 +7196,3 @@ we may still need to template the rest of the firewall if we want to have
|
|||||||
other variables (rate limits) in it, because the rules for that need to be
|
other variables (rate limits) in it, because the rules for that need to be
|
||||||
inserted ahead of the rules for accepting icmp, and there's no way to
|
inserted ahead of the rules for accepting icmp, and there's no way to
|
||||||
do that without
|
do that without
|
||||||
|
|
||||||
Sun Mar 9 21:46:30 GMT 2025
|
|
||||||
|
|
||||||
OK, we have updating firewall zones that are good neough to pass the test
|
|
||||||
(and may even work ...). We need
|
|
||||||
|
|
||||||
* to write a rule with a rate limit for incoming icmp6 for each interface,
|
|
||||||
capping it to 5% of the interface bandwidth
|
|
||||||
|
|
||||||
* some place to say what the bandwidth is, which I am thinking we could do with
|
|
||||||
a passthru attribute of some kind on services
|
|
||||||
|
|
||||||
{
|
|
||||||
run = "mdcnvdfngkj";
|
|
||||||
name = "dtret";
|
|
||||||
properties.bandwidthKbps = 80000;
|
|
||||||
}
|
|
||||||
|
|
||||||
and insert logic in the up/run commands to copy the properties to
|
|
||||||
outputs. Or we could use `data` or `env` directories, which means
|
|
||||||
that the properties would be there even while the service is down, if
|
|
||||||
that's a concern, but would need a different way to look them up. And
|
|
||||||
also, would we allow them to change? What if there's some kind of interface
|
|
||||||
where we _can_ interrogate the bandwidth at runtime?
|
|
||||||
|
|
||||||
Mon Mar 10 21:00:17 GMT 2025
|
|
||||||
|
|
||||||
The idea that occured to me at lunchtime is "what if we made the
|
|
||||||
(svc:output ) method fall back to properties if no output was present".
|
|
||||||
To do this, we'd have to
|
|
||||||
|
|
||||||
(1) arrange for /nix/store/eeee-service/.properties to exist
|
|
||||||
|
|
||||||
- add properties attribute to service functions
|
|
||||||
- write them to .properties in liminix-tools/services/builder.sh
|
|
||||||
- make sure they get passed through whne provided to all the service
|
|
||||||
builder functions
|
|
||||||
|
|
||||||
[done] (2) pass the store directory to svc.open instead of ..../.outputs
|
|
||||||
|
|
||||||
(3) make service:output look in both places
|
|
||||||
|
|
||||||
(4) write the damn firewall rule
|
|
||||||
|
|
||||||
Mon Mar 17 21:13:36 GMT 2025
|
|
||||||
|
|
||||||
Argh why is it never simple?
|
|
||||||
|
|
||||||
We need to write a rate-limiting firewall rule for each interface to
|
|
||||||
restrict icmp on that interface. This is not easy to reconcile with
|
|
||||||
putting them in default-rules because how do we generate multiple
|
|
||||||
array elements by config file templating?
|
|
||||||
|
|
||||||
There are two things in my mind now:
|
|
||||||
|
|
||||||
1) could we have some better way of manipulating the firewall rules
|
|
||||||
such that the rules from different modules are composable
|
|
||||||
|
|
||||||
this is complicated somewhat by ordering: if every rule in a chain is
|
|
||||||
"drop" or "accept" then it's easy to add another, but if the same
|
|
||||||
chain does first one then the other, doing the other and then the one
|
|
||||||
will not work
|
|
||||||
|
|
||||||
today we do e.g.
|
|
||||||
|
|
||||||
input-ip6
|
|
||||||
-> reject-bogons
|
|
||||||
-> accept non-bogus-icmp
|
|
||||||
-> process per-zone allowlist
|
|
||||||
-> allow established,related on wan
|
|
||||||
-> allow all on lan (so why did we need an allowlist?)
|
|
||||||
|
|
||||||
could we express this in a less sequential form? the
|
|
||||||
specification of what's allowed
|
|
||||||
|
|
||||||
|
|
||||||
input-ip6 for wan: is input-ip6 with the wan allowlist
|
|
||||||
input-ip6 for lan: is input-ip6 with the lan allowlist
|
|
||||||
|
|
||||||
|
|
||||||
input-ip6 for ppp0: is input-ip6 with the wan allowlist with a rate
|
|
||||||
limit for icmp
|
|
||||||
|
|
||||||
ssh module wishes to modify the allowlist for lan/wan/both so that
|
|
||||||
it includes port 22
|
|
||||||
|
|
||||||
am wondering if we could do default deny and _all_ the rules
|
|
||||||
(except for bogons) are allows
|
|
||||||
|
|
||||||
maybe we have the concept of "subtraction": a rule can be an allow
|
|
||||||
preceded by some number of drops which (at least by convention; this is
|
|
||||||
probably hard to enforce) are "carve outs" of the packets that are
|
|
||||||
being allowed.
|
|
||||||
|
|
||||||
... it's hard to express the forward-ipv6 in these terms, though.
|
|
||||||
we end up with "some drops and then multiple accepts"
|
|
||||||
|
|
||||||
we have
|
|
||||||
(and (not (or drop1 drop2 ...)) (or accept1 accept2 ...))
|
|
||||||
|
|
||||||
and to add ssh we need to break into the second clause instead of
|
|
||||||
composing at the top level
|
|
||||||
|
|
||||||
(and (not (or drop1 drop2 ...))
|
|
||||||
(or accept1 accept2 accept-ssh...))
|
|
||||||
|
|
||||||
|
|
||||||
then the icmp bogons composite rule is "drop weird icmp and then
|
|
||||||
allow what's left"
|
|
||||||
|
|
||||||
(side note: maybe we could use a map to do interface name -> bandwidth
|
|
||||||
for rate limiting)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
a composite rule might be a bunch of denies and then an allow
|
|
||||||
for anything the
|
|
||||||
|
|
||||||
2) some kinda syntax for referencing outputs (or properties) that's not
|
|
||||||
just string interpolation
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
I think we could address the immediate problem by writing a rule for
|
|
||||||
rate-limiting that looks up the rate in a map, and some maps (with
|
|
||||||
extraText) that get the rates from service properties? And that would
|
|
||||||
suffice for addressing the RoS audit, at least
|
|
||||||
|
|
||||||
Tue Mar 18 18:48:22 GMT 2025
|
|
||||||
|
|
||||||
Unless the interface exists, we do not (at least, may not) know its
|
|
||||||
name because that's an output. So the fact that it has a permanent
|
|
||||||
property is not per se terribly useful
|
|
||||||
|
|
||||||
limit rate 50000 bytes / minute accept
|
|
||||||
|
|
||||||
|
|
||||||
nft add rule ip nat postrouting snat to ip saddr map { 192.168.1.0/24 : 10.0.0.1, 192.168.2.0/24 : 10.0.0.2 }
|
|
||||||
|
|
||||||
nft add rule table-ip input-ip4 ip daddr 2.2.2.3/32 limit rate iifname map { "eth0": 10, "ppp0": 20 } kbytes/second accept
|
|
||||||
|
|
||||||
nft add map 'table-ip intf-limits { typeof 5000000 ; elements = { lan: 50000000, ppp0: 3500000 } ; }'
|
|
||||||
|
|
||||||
OK, we can't do rate lookup in a map because the nftables grammar only
|
|
||||||
supports a numeric literal for limit_rate_bytes. so we're back to writing
|
|
||||||
a collection of rules, one for each interface with an ifname output,
|
|
||||||
that sets the limit for that interface
|
|
||||||
|
|
||||||
* we could do this all in one element of a rules list, with newlines
|
|
||||||
between each actual rule
|
|
||||||
|
|
||||||
* we could add extraText to the ruleset syntax - but does it go at the
|
|
||||||
start of the rules or the end or somewhere in the middle? this is
|
|
||||||
almost worse
|
|
||||||
|
|
||||||
* we could pick up where we left off on march 17 and redesign the
|
|
||||||
firewall module
|
|
||||||
|
|
||||||
gonna be option 1 isn't it?
|
|
||||||
|
|
||||||
Tue Mar 25 00:13:31 GMT 2025
|
|
||||||
|
|
||||||
the logic for watch-outputs is not correct
|
|
||||||
|
|
||||||
(1) when we first read outputs, how do we know if the controlled
|
|
||||||
service is up to date wrt those outputs? we should perform the action
|
|
||||||
for the first time after reading initial state and before waiting
|
|
||||||
|
|
||||||
(2) it isn't writing the new outputs back into trees, so will refresh
|
|
||||||
continually after the fisrt change
|
|
||||||
|
|
||||||
set tree to empty for each service
|
|
||||||
loop:
|
|
||||||
for each service
|
|
||||||
read new tree
|
|
||||||
if different
|
|
||||||
do action
|
|
||||||
set tree = new tree
|
|
||||||
wait for changes
|
|
||||||
next iteration
|
|
||||||
|
@ -37,7 +37,6 @@ let
|
|||||||
"nft_fib"
|
"nft_fib"
|
||||||
"nft_fib_ipv4"
|
"nft_fib_ipv4"
|
||||||
"nft_fib_ipv6"
|
"nft_fib_ipv6"
|
||||||
"nft_limit"
|
|
||||||
"nft_log"
|
"nft_log"
|
||||||
"nft_masq"
|
"nft_masq"
|
||||||
"nft_nat"
|
"nft_nat"
|
||||||
@ -115,7 +114,6 @@ in
|
|||||||
NFT_CT = "m";
|
NFT_CT = "m";
|
||||||
NFT_FIB_IPV4 = "m";
|
NFT_FIB_IPV4 = "m";
|
||||||
NFT_FIB_IPV6 = "m";
|
NFT_FIB_IPV6 = "m";
|
||||||
NFT_LIMIT = "m";
|
|
||||||
NFT_LOG = "m";
|
NFT_LOG = "m";
|
||||||
NFT_MASQ = "m";
|
NFT_MASQ = "m";
|
||||||
NFT_NAT = "m";
|
NFT_NAT = "m";
|
||||||
|
@ -44,37 +44,10 @@ let
|
|||||||
end
|
end
|
||||||
}}
|
}}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
rateHook =
|
|
||||||
let rules =
|
|
||||||
map
|
|
||||||
(x: ''
|
|
||||||
{{;
|
|
||||||
local s = "${x}";
|
|
||||||
local n = output(s, "ifname");
|
|
||||||
local bw = output(s, "bandwidth");
|
|
||||||
if n and bw then
|
|
||||||
return "meta l4proto icmpv6 iifname ".. n .. " limit rate over " .. (math.floor (tonumber(bw) / 20)) .. " bytes/second drop"
|
|
||||||
else
|
|
||||||
return "# " .. (n or "not n") .. " " .. (bw or "not bw")
|
|
||||||
end
|
|
||||||
}}
|
|
||||||
'')
|
|
||||||
(concatLists (builtins.attrValues zones));
|
|
||||||
in {
|
|
||||||
type = "filter"; family = "ip6";
|
|
||||||
hook = "input"; priority = "-1"; policy = "accept";
|
|
||||||
inherit rules;
|
|
||||||
};
|
|
||||||
|
|
||||||
sets = (mapAttrs' (n: _: mkSet "ip" n) zones) //
|
sets = (mapAttrs' (n: _: mkSet "ip" n) zones) //
|
||||||
(mapAttrs' (n: _: mkSet "ip6" n) zones);
|
(mapAttrs' (n: _: mkSet "ip6" n) zones);
|
||||||
allRules =
|
allRules = lib.recursiveUpdate extraRules (lib.recursiveUpdate sets rules);
|
||||||
{ icmp6-ratehook = rateHook; } //
|
|
||||||
(lib.recursiveUpdate
|
|
||||||
extraRules
|
|
||||||
(lib.recursiveUpdate sets rules));
|
|
||||||
script = firewallgen "firewall1.nft" allRules;
|
script = firewallgen "firewall1.nft" allRules;
|
||||||
watchArg = z: intfs: map (i: "${z}:${i}") intfs;
|
watchArg = z: intfs: map (i: "${z}:${i}") intfs;
|
||||||
name = "firewall";
|
name = "firewall";
|
||||||
|
@ -115,7 +115,6 @@ let
|
|||||||
${command}
|
${command}
|
||||||
'';
|
'';
|
||||||
notification-fd = 10;
|
notification-fd = 10;
|
||||||
# properties.bandwidth = 3 * 1000 * 1000;
|
|
||||||
timeout-up =
|
timeout-up =
|
||||||
if lcpEcho.failure != null then (10 + lcpEcho.failure * lcpEcho.interval) * 1000 else 60 * 1000;
|
if lcpEcho.failure != null then (10 + lcpEcho.failure * lcpEcho.interval) * 1000 else 60 * 1000;
|
||||||
inherit dependencies;
|
inherit dependencies;
|
||||||
|
@ -9,7 +9,6 @@ check:
|
|||||||
ln -s . anoia
|
ln -s . anoia
|
||||||
fennel ./run-tests.fnl $(CHECK)
|
fennel ./run-tests.fnl $(CHECK)
|
||||||
fennel test.fnl
|
fennel test.fnl
|
||||||
mkdir -p $(outputdir)
|
|
||||||
fennel test-svc.fnl $(servicedir)
|
fennel test-svc.fnl $(servicedir)
|
||||||
find $(outputdir) -ls
|
find $(outputdir) -ls
|
||||||
test -f $(outputdir)/fish
|
test -f $(outputdir)/fish
|
||||||
|
@ -25,27 +25,8 @@
|
|||||||
(fn basename [path]
|
(fn basename [path]
|
||||||
(string.match path ".*/([^/]-)$"))
|
(string.match path ".*/([^/]-)$"))
|
||||||
|
|
||||||
|
|
||||||
(fn dirname [path]
|
(fn dirname [path]
|
||||||
(let [stripped (string.match path "(.-)/*$")]
|
(string.match path "(.*)/[^/]-$"))
|
||||||
(pick-values
|
|
||||||
1
|
|
||||||
(if (not path) "."
|
|
||||||
(= path "") "."
|
|
||||||
(not stripped) "."
|
|
||||||
(= stripped "") "/"
|
|
||||||
(string.match stripped ".+/.-") (stripped:gsub "(.*)(/.*)" "%1")
|
|
||||||
(string.match stripped "/") "/"
|
|
||||||
"."))))
|
|
||||||
|
|
||||||
(define-tests
|
|
||||||
;; these are examples from dirname(3)
|
|
||||||
(expect= (dirname "/usr/lib") "/usr")
|
|
||||||
(expect= (dirname "/usr/") "/")
|
|
||||||
(expect= (dirname "usr") ".")
|
|
||||||
(expect= (dirname "/") "/")
|
|
||||||
(expect= (dirname ".") ".")
|
|
||||||
(expect= (dirname "..") "."))
|
|
||||||
|
|
||||||
(fn append-path [dirname filename]
|
(fn append-path [dirname filename]
|
||||||
(let [base (or (string.match dirname "(.*)/$") dirname)
|
(let [base (or (string.match dirname "(.*)/$") dirname)
|
||||||
|
@ -1,56 +1,22 @@
|
|||||||
(local inotify (require :inotify))
|
(local inotify (require :inotify))
|
||||||
(local { : file-exists? : dirname : append-path } (require :anoia))
|
(local { : file-exists? : dirname : append-path } (require :anoia))
|
||||||
(local { : file-type : dir : mktree &as fs } (require :anoia.fs))
|
(local { : file-type : dir : mktree &as fs } (require :anoia.fs))
|
||||||
(local { : readlink } (require :lualinux))
|
|
||||||
|
|
||||||
(fn read-line [name]
|
(fn read-line [name]
|
||||||
(with-open [f (assert (io.open name :r) (.. "can't open file " name))]
|
(with-open [f (assert (io.open name :r) (.. "can't open file " name))]
|
||||||
(f:read "*l")))
|
(f:read "*l")))
|
||||||
|
|
||||||
|
|
||||||
;; If the directory is missing, we cannot add a inotify watch
|
|
||||||
;; for it. We need a watch that opens the parent if the pathname is missing,
|
|
||||||
;; and it needs to be the resolved path not the syntactic parent.
|
|
||||||
|
|
||||||
;; If the directory is not missing, then having a watch on the
|
|
||||||
;; parent may result in extra wakeups but should only affect
|
|
||||||
;; efficiency not correctness
|
|
||||||
|
|
||||||
;; Each time the directory may have been added we need to update
|
|
||||||
;; watches. If it's been removed, the watch will be removed
|
|
||||||
;; automatically. inotify_add_watch when it already exists will modify
|
|
||||||
;; instead of making a new one, so we can treat it as idempotent
|
|
||||||
|
|
||||||
|
|
||||||
(fn resolve-link [pathname]
|
|
||||||
(if (= (file-type pathname) :link)
|
|
||||||
(readlink pathname)
|
|
||||||
pathname))
|
|
||||||
|
|
||||||
(fn watch-fsevents [directory-name]
|
(fn watch-fsevents [directory-name]
|
||||||
(let [handle (inotify.init)
|
(let [handle (inotify.init)]
|
||||||
parent-name (dirname (resolve-link directory-name))
|
(handle:addwatch directory-name
|
||||||
refresh (fn []
|
inotify.IN_CREATE
|
||||||
(handle:addwatch directory-name
|
inotify.IN_MOVE
|
||||||
inotify.IN_CREATE
|
inotify.IN_DELETE
|
||||||
inotify.IN_MOVE
|
inotify.IN_DELETE_SELF
|
||||||
inotify.IN_DELETE
|
inotify.IN_MOVED_FROM
|
||||||
inotify.IN_DELETE_SELF
|
inotify.IN_MOVED_TO
|
||||||
inotify.IN_MOVED_FROM
|
inotify.IN_CLOSE_WRITE)
|
||||||
inotify.IN_MOVED_TO
|
handle))
|
||||||
inotify.IN_CLOSE_WRITE)
|
|
||||||
(handle:addwatch parent-name
|
|
||||||
inotify.IN_CREATE
|
|
||||||
inotify.IN_DELETE))]
|
|
||||||
;; if you are using poll() to check for events on this
|
|
||||||
;; watcher and on other events at the same time, be sure
|
|
||||||
;; to call fileno each time around the loop instead
|
|
||||||
;; of only once
|
|
||||||
{
|
|
||||||
:fileno #(do (refresh) (handle:fileno))
|
|
||||||
:wait #(do (refresh) (handle:read))
|
|
||||||
:close #(handle:close)
|
|
||||||
}))
|
|
||||||
|
|
||||||
(fn write-value [pathname value]
|
(fn write-value [pathname value]
|
||||||
(mktree (dirname pathname))
|
(mktree (dirname pathname))
|
||||||
@ -83,26 +49,20 @@
|
|||||||
(self:wait))))
|
(self:wait))))
|
||||||
|
|
||||||
(fn open [directory]
|
(fn open [directory]
|
||||||
(let [outputs-dir (append-path directory ".outputs")
|
(let [watcher (watch-fsevents directory)
|
||||||
has-file? #(file-exists? (append-path directory $1))
|
has-file? #(file-exists? (append-path directory $1))
|
||||||
watcher (watch-fsevents outputs-dir)
|
outputs-dir (append-path directory ".outputs")]
|
||||||
properties-dir (append-path directory ".properties")]
|
|
||||||
{
|
{
|
||||||
|
:wait #(watcher:read)
|
||||||
:ready? (fn [self]
|
:ready? (fn [self]
|
||||||
(and (has-file? ".outputs/state")
|
(and (has-file? ".outputs/state")
|
||||||
(not (has-file? ".outputs/.lock"))))
|
(not (has-file? ".outputs/.lock"))))
|
||||||
:property (fn [_ filename]
|
|
||||||
(read-value (append-path properties-dir filename)))
|
|
||||||
:output (fn [_ filename new-value]
|
:output (fn [_ filename new-value]
|
||||||
(if new-value
|
(if new-value
|
||||||
(write-value (append-path outputs-dir filename) new-value)
|
(write-value (append-path outputs-dir filename) new-value)
|
||||||
(or
|
(read-value (append-path outputs-dir filename))))
|
||||||
(read-value (append-path outputs-dir filename))
|
|
||||||
(read-value (append-path properties-dir filename)))))
|
|
||||||
:wait #(watcher:wait)
|
|
||||||
:close #(watcher:close)
|
:close #(watcher:close)
|
||||||
:fileno #(watcher:fileno)
|
:fileno #(watcher:fileno)
|
||||||
: directory
|
|
||||||
: events
|
: events
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ let
|
|||||||
optionalString
|
optionalString
|
||||||
;
|
;
|
||||||
inherit (lib.lists) groupBy;
|
inherit (lib.lists) groupBy;
|
||||||
inherit (lib.attrsets) attrsToList mapAttrsToList;
|
inherit (lib.attrsets) mapAttrsToList;
|
||||||
inherit (builtins) elemAt map head tail toString;
|
inherit (builtins) map head tail;
|
||||||
|
|
||||||
indentLines =
|
indentLines =
|
||||||
offset: lines:
|
offset: lines:
|
||||||
@ -68,25 +68,6 @@ let
|
|||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
domap =
|
|
||||||
{
|
|
||||||
name,
|
|
||||||
type,
|
|
||||||
elements ? [ ],
|
|
||||||
extraText ? null,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
colonize = v:
|
|
||||||
let ty = elemAt (attrsToList v) 0; in "${ty.name}: ${ty.value}";
|
|
||||||
in ''
|
|
||||||
map ${name} {
|
|
||||||
type ${colonize type}
|
|
||||||
${if elements != [ ] then "elements = { ${concatStringsSep ", " (mapAttrsToList (k: v : "${k}: ${toString v}") elements)} }" else ""}
|
|
||||||
${optionalString (extraText != null) extraText}
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
|
|
||||||
dochainorset =
|
dochainorset =
|
||||||
{
|
{
|
||||||
kind ? "chain",
|
kind ? "chain",
|
||||||
@ -95,7 +76,6 @@ let
|
|||||||
{
|
{
|
||||||
chain = dochain;
|
chain = dochain;
|
||||||
set = doset;
|
set = doset;
|
||||||
map = domap;
|
|
||||||
}
|
}
|
||||||
.${kind}
|
.${kind}
|
||||||
params;
|
params;
|
||||||
|
@ -151,18 +151,6 @@ in
|
|||||||
"eth0"
|
"eth0"
|
||||||
"eth1"
|
"eth1"
|
||||||
];
|
];
|
||||||
};
|
|
||||||
|
|
||||||
map-intf-limits-ip6 = {
|
|
||||||
name = "intf-limits";
|
|
||||||
kind = "map";
|
|
||||||
family = "ip6";
|
|
||||||
type = { ifname = "bytes"; };
|
|
||||||
elements = {
|
|
||||||
# XXX keys need to be generated from interface outputs
|
|
||||||
ppp0 = builtins.floor (70*1000*1000 * 0.05); # 5% of 70MB fttp connection
|
|
||||||
lan = builtins.floor (1000*1000*1000 * 0.05); # GB ethernet
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
diff --git a/lualinux.c b/lualinux.c
|
|
||||||
index f3d1a4d..9c5dc9c 100644
|
|
||||||
--- a/lualinux.c
|
|
||||||
+++ b/lualinux.c
|
|
||||||
@@ -387,6 +387,18 @@ static int ll_readlink(lua_State *L) {
|
|
||||||
RET_STRN(buf, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int ll_realpath(lua_State *L) {
|
|
||||||
+ const char *pname = luaL_checkstring(L, 1);
|
|
||||||
+ char * resolved = realpath(pname, NULL); /* mallocs */
|
|
||||||
+ if (resolved == 0) {
|
|
||||||
+ RET_ERRNO;
|
|
||||||
+ } else {
|
|
||||||
+ lua_pushstring(L, resolved);
|
|
||||||
+ free(resolved);
|
|
||||||
+ return 1;
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ll_lstat3(lua_State *L) {
|
|
||||||
// lua api: lstat3(path [,statflag:int])
|
|
||||||
// if statflag=1: do stat(). default: do lstat
|
|
||||||
@@ -924,6 +936,7 @@ static const struct luaL_Reg lualinuxlib[] = {
|
|
||||||
{"readdir", ll_readdir},
|
|
||||||
{"closedir", ll_closedir},
|
|
||||||
{"readlink", ll_readlink},
|
|
||||||
+ {"realpath", ll_realpath},
|
|
||||||
{"lstat3", ll_lstat3},
|
|
||||||
{"lstat", ll_lstat},
|
|
||||||
{"utime", ll_utime},
|
|
||||||
@@ -969,4 +982,3 @@ int luaopen_lualinux (lua_State *L) {
|
|
||||||
lua_settable (L, -3);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
-
|
|
@ -13,11 +13,6 @@ lua.pkgs.buildLuaPackage {
|
|||||||
version = "0.1"; # :shrug:
|
version = "0.1"; # :shrug:
|
||||||
|
|
||||||
inherit src;
|
inherit src;
|
||||||
|
|
||||||
patches = [
|
|
||||||
./0001-realpath.patch
|
|
||||||
];
|
|
||||||
|
|
||||||
postPatch = ''
|
postPatch = ''
|
||||||
sed -i -e '/strip/d' Makefile
|
sed -i -e '/strip/d' Makefile
|
||||||
'';
|
'';
|
||||||
|
@ -25,10 +25,7 @@
|
|||||||
myenv {
|
myenv {
|
||||||
: string
|
: string
|
||||||
: table
|
: table
|
||||||
: math
|
|
||||||
: ipairs
|
: ipairs
|
||||||
: tonumber
|
|
||||||
|
|
||||||
:output
|
:output
|
||||||
(fn [service-path path default]
|
(fn [service-path path default]
|
||||||
(let [s (assert (svc.open service-path))]
|
(let [s (assert (svc.open service-path))]
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
"/nix/store/s2" [["out1"] ["out2" "ifname"]]}}
|
"/nix/store/s2" [["out1"] ["out2" "ifname"]]}}
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
(fn changed? [paths old-tree new-tree]
|
(fn changed? [paths old-tree new-tree]
|
||||||
(accumulate [changed? false
|
(accumulate [changed? false
|
||||||
_ path (ipairs paths)]
|
_ path (ipairs paths)]
|
||||||
@ -51,6 +52,8 @@
|
|||||||
(expect (not (changed? [["mtu"]] {:ifname "true"} {:ifname "false"})))
|
(expect (not (changed? [["mtu"]] {:ifname "true"} {:ifname "false"})))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(fn do-action [action service]
|
(fn do-action [action service]
|
||||||
(case action
|
(case action
|
||||||
:restart (system (%% "s6-svc -r /run/service/%s" service))
|
:restart (system (%% "s6-svc -r /run/service/%s" service))
|
||||||
@ -77,19 +80,16 @@
|
|||||||
: controlled-service
|
: controlled-service
|
||||||
: action
|
: action
|
||||||
: watched-service
|
: watched-service
|
||||||
: paths } (parse-args arg)
|
: paths } (parse-args arg)]
|
||||||
services (open-services output-references)]
|
|
||||||
(while true
|
(while true
|
||||||
(when
|
(let [services (open-services output-references)
|
||||||
(accumulate [changed false
|
trees (collect [s _ (pairs services)]
|
||||||
service paths (pairs services)]
|
(values s (s:output ".")))]
|
||||||
(let [new-tree (service:output ".")]
|
(wait-for-change services)
|
||||||
(if (changed? paths (or (. trees service) {}) new-tree)
|
(each [service paths (pairs services)]
|
||||||
(do (tset trees service new-tree) true)
|
(let [new-tree (service:output ".")]
|
||||||
changed)))
|
(when (changed? paths (. trees service) new-tree)
|
||||||
(do-action action controlled-service))
|
(do-action action controlled-service))))))))
|
||||||
(wait-for-change services))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user