diff options
-rw-r--r-- | custom/uucp-mediaserver.nix | 59 | ||||
-rw-r--r-- | custom/uucp-mediaserver/scripts.nix | 32 | ||||
-rwxr-xr-x | custom/uucp-mediaserver/scripts/mediaspace | 9 | ||||
-rwxr-xr-x | custom/uucp-mediaserver/scripts/queue | 134 | ||||
-rwxr-xr-x | custom/uucp-mediaserver/scripts/queuesize | 24 | ||||
-rwxr-xr-x | custom/uucp-mediaserver/scripts/queuestatus | 41 | ||||
-rwxr-xr-x | custom/uucp-mediaserver/scripts/update-queuesize | 10 | ||||
-rw-r--r-- | odin.nix | 8 | ||||
-rw-r--r-- | users/gkleen.nix | 2 |
9 files changed, 318 insertions, 1 deletions
diff --git a/custom/uucp-mediaserver.nix b/custom/uucp-mediaserver.nix new file mode 100644 index 00000000..a8af8bc6 --- /dev/null +++ b/custom/uucp-mediaserver.nix | |||
@@ -0,0 +1,59 @@ | |||
1 | { config, lib, pkgs, ... }: | ||
2 | |||
3 | with lib; | ||
4 | |||
5 | let | ||
6 | cfg = config.uucp.media-server; | ||
7 | |||
8 | touchStateFiles = name: '' | ||
9 | touch ${cfg.queueDir}/${name}.queue | ||
10 | touch ${cfg.queueDir}/${name}.space | ||
11 | touch ${cfg.queueDir}/${name}.queuesize | ||
12 | |||
13 | chown ${cfg.owner}:${cfg.group} ${cfg.queueDir}/${name}.queue ${cfg.queueDir}/${name}.space ${cfg.queueDir}/${name}.queuesize | ||
14 | ''; | ||
15 | in { | ||
16 | options = { | ||
17 | services.uucp.media-server = { | ||
18 | enable = mkEnableOption "UUCP media server"; | ||
19 | |||
20 | remoteNodes = mkOption { | ||
21 | type = types.listOf types.str; | ||
22 | default = []; | ||
23 | description = '' | ||
24 | Clients to track | ||
25 | ''; | ||
26 | }; | ||
27 | |||
28 | queueDir = mkOption { | ||
29 | type = types.path; | ||
30 | default = "/var/spool/media"; | ||
31 | description = "Queue directory"; | ||
32 | }; | ||
33 | |||
34 | owner = mkOption { | ||
35 | type = types.str; | ||
36 | default = "root"; | ||
37 | description = "Owner of the various queue files"; | ||
38 | }; | ||
39 | |||
40 | group = mkOption { | ||
41 | type = types.str; | ||
42 | default = "media"; | ||
43 | description = "Group of the various queue files"; | ||
44 | }; | ||
45 | }; | ||
46 | }; | ||
47 | |||
48 | config = mkIf cfg.enable { | ||
49 | system.activationScripts."uucp-media-server" = '' | ||
50 | mkdir -p ${cfg.queueDir} | ||
51 | chmod 750 ${cfg.queueDir} | ||
52 | chown ${cfg.owner}:${cfg.group} ${cfg.queueDir} | ||
53 | |||
54 | ${concatStringsSep "\n" (map touchStateFiles cfg.remoteNodes)} | ||
55 | ''; | ||
56 | |||
57 | environment.systemPackages = [ (pkgs.callPackage ./uucp-mediaserver/scripts.nix { config = cfg; }) ]; | ||
58 | }; | ||
59 | } | ||
diff --git a/custom/uucp-mediaserver/scripts.nix b/custom/uucp-mediaserver/scripts.nix new file mode 100644 index 00000000..21eab6de --- /dev/null +++ b/custom/uucp-mediaserver/scripts.nix | |||
@@ -0,0 +1,32 @@ | |||
1 | { stdenv, zsh, config }: | ||
2 | |||
3 | let | ||
4 | mkSymlinks = name: '' | ||
5 | ln -s $out/libexec/mediaspace $out/bin/mediaspace.${name} | ||
6 | ln -s $out/libexec/queue $out/bin/queue.${name} | ||
7 | ln -s $out/libexec/queuesize $out/bin/queuesize.${name} | ||
8 | ln -s $out/libexec/queuestatus $out/bin/queuestatus.${name} | ||
9 | ln -s $out/libexec/update-queuesize $out/bin/update-queuesize.${name} | ||
10 | ''; | ||
11 | in stdenv.mkDerivation { | ||
12 | name = "uucp-mediaserver"; | ||
13 | src = ./scripts; | ||
14 | |||
15 | phases = [ "installPhase" ]; | ||
16 | |||
17 | inherit (config) queueDir; | ||
18 | |||
19 | installPhase = '' | ||
20 | mkdir -p $out/libexec | ||
21 | |||
22 | substituteFile $src/mediaspace $out/libexec/mediaspace | ||
23 | substituteFile $src/queue $out/libexec/queue | ||
24 | substituteFile $src/queuesize $out/libexec/queuesize | ||
25 | substituteFile $src/queuestatus $out/libexec/queuestatus | ||
26 | substituteFile $src/update-queuesize $out/libexec/update-queuesize | ||
27 | |||
28 | mkdir -p $out/bin | ||
29 | |||
30 | ${concatStringsSep "\n" (map mkSymlinks config.remoteNodes)} | ||
31 | ''; | ||
32 | } | ||
diff --git a/custom/uucp-mediaserver/scripts/mediaspace b/custom/uucp-mediaserver/scripts/mediaspace new file mode 100755 index 00000000..5d636a53 --- /dev/null +++ b/custom/uucp-mediaserver/scripts/mediaspace | |||
@@ -0,0 +1,9 @@ | |||
1 | #!@zsh@/bin/zsh | ||
2 | |||
3 | base=$(basename $0) | ||
4 | suffix=${base##*.} | ||
5 | |||
6 | printf "%d\n" "$1" > @queueDir@/mediaspace.$suffix | ||
7 | echo > @queueDir@/queuesize.$suffix | ||
8 | update-queuesize.$suffix | ||
9 | |||
diff --git a/custom/uucp-mediaserver/scripts/queue b/custom/uucp-mediaserver/scripts/queue new file mode 100755 index 00000000..0039b3b8 --- /dev/null +++ b/custom/uucp-mediaserver/scripts/queue | |||
@@ -0,0 +1,134 @@ | |||
1 | #!@zsh@/bin/zsh | ||
2 | |||
3 | logTag=${0:t} | ||
4 | exec 1> >(logger -t "$logTag" -p news.notice) | ||
5 | exec 2> >(logger -t "$logTag" -p news.error) | ||
6 | |||
7 | doDebug=false | ||
8 | debug() { | ||
9 | if $doDebug; then | ||
10 | logger -t "$logTag" -p news.debug | ||
11 | else | ||
12 | cat >/dev/null | ||
13 | fi | ||
14 | } | ||
15 | verbose() { $@ | debug } | ||
16 | |||
17 | function mungefilename() { | ||
18 | print ${@} | tr -s $';&*|<> \t!_' '_' | ||
19 | } | ||
20 | |||
21 | typeset -a cleanupCmds | ||
22 | cleanupCmds=() | ||
23 | |||
24 | function doCleanup { | ||
25 | for cmd (${cleanupCmds}); do | ||
26 | eval ${cmd} | debug | ||
27 | done | ||
28 | } | ||
29 | |||
30 | trap doCleanup EXIT | ||
31 | |||
32 | function cleanup() { | ||
33 | local cmd | ||
34 | cmd="" | ||
35 | for arg ($@); do | ||
36 | [[ -n "${cmd}" ]] && cmd="${cmd} " | ||
37 | cmd="${cmd}${(qq)arg}" | ||
38 | done | ||
39 | |||
40 | cleanupCmds+=(${cmd}) | ||
41 | } | ||
42 | |||
43 | base=$(basename $0) | ||
44 | suffix=${base##*.} | ||
45 | |||
46 | force=0 | ||
47 | if [[ "$1" == "-f" ]]; then | ||
48 | shift | ||
49 | force=1 | ||
50 | if [[ -n "$1" && "$1" -eq "$1" ]] 2>/dev/null; then | ||
51 | force="$1" | ||
52 | shift | ||
53 | fi | ||
54 | fi | ||
55 | |||
56 | noCall=false | ||
57 | if [[ "$1" == "-r" ]]; then | ||
58 | shift | ||
59 | noCall=true | ||
60 | fi | ||
61 | |||
62 | for f (${@}); do | ||
63 | f=$(readlink -f ${f}) | ||
64 | if grep -q ${f} @queueDir@/${suffix}.queue; then | ||
65 | printf "‘%s’ is already in queue file\n" ${f:t} | warn | ||
66 | continue | ||
67 | fi | ||
68 | if uustat | grep -q ${f:t}; then | ||
69 | printf "‘%s’ is already in uucp queue\n" ${f:t} | warn | ||
70 | fi | ||
71 | print -r ${f} >> @queueDir@/${suffix}.queue | ||
72 | done | ||
73 | |||
74 | offset=1 | ||
75 | |||
76 | advance() { | ||
77 | cat =(head -n $((offset - 1)) @queuedir@/${suffix}.queue) =(tail -n +$((offset + 1)) @queueDir@/${suffix}.queue) >@queueDir@/${suffix}.queue | ||
78 | } | ||
79 | |||
80 | while true; do | ||
81 | [[ $(wc -l @queueDir@/${suffix}.queue | cut -d ' ' -f 1) -lt $offset ]] && break | ||
82 | file=$(tail -n +${offset} @queueDir@/${suffix}.queue | head -n 1) | ||
83 | printf "Considering ‘%s’" ${file} | debug | ||
84 | if [[ -z "${file}" || ! -e "${file}" ]]; then | ||
85 | if [[ -n "${file}" ]]; then | ||
86 | printf "‘%s’ does not exist, skipping\n" "${file}" >&2 | ||
87 | printf "Subject: Missing file in %s\n\n%s" $logTag ${file} \ | ||
88 | | sendmail gkleen \ | ||
89 | && echo "Sent mail." | ||
90 | fi | ||
91 | advance | ||
92 | continue | ||
93 | fi | ||
94 | |||
95 | space=$(($(cat @queueDir@/${suffix}.space) - $(queuesize.$suffix))) | ||
96 | printf "%s left on %s\n" $(numfmt --suffix=B --to=iec-i -- $space) $suffix | debug | ||
97 | |||
98 | size=$(stat -c "%s" "${file}") | ||
99 | printf "‘%s’ is %s\n" ${file:t} $(numfmt --suffix=B --to=iec-i -- $size) | debug | ||
100 | if [[ "${size}" -le "${space}" || "${force}" -gt 0 ]]; then | ||
101 | printf "queuing ‘%s’ for %s\n" ${file:t} ${suffix} | debug | ||
102 | function send() { | ||
103 | typeset -a cmd | ||
104 | cmd=(uux $(if ${noCall}; then echo "-r"; fi) -g z) | ||
105 | |||
106 | compatibleName=${file} | ||
107 | function munge() { [[ $(mungefilename ${file}) != ${file} ]] } | ||
108 | |||
109 | if munge; then | ||
110 | compatibleName=${file:h}/$(mungefilename ${file:t}) | ||
111 | [[ -e ${compatibleName} ]] || verbose ln -vs ${file} ${compatibleName} | ||
112 | cmd+=(-C) | ||
113 | cleanup rm -v ${compatibleName} | ||
114 | fi | ||
115 | |||
116 | cmd+=("${suffix}!recv-media" "!${compatibleName}" "$(date --date=@$(stat -c '%Y' "${file}") "+%Y%m%d%H%M.%S")") | ||
117 | |||
118 | if munge; then | ||
119 | cmd+=("$(print -- ${file:t} | base64 -w0)") | ||
120 | fi | ||
121 | $cmd && printf "Queued ‘%s’ for %s\n" ${file:t} $suffix | ||
122 | } | ||
123 | |||
124 | function skip() { | ||
125 | offset=$((offset + 1)) | ||
126 | printf "Failed to queue ‘%s’\n" ${file} >&2 | ||
127 | } | ||
128 | |||
129 | { { [[ ! -e "${file}" ]] || send } && advance && update-queuesize.$suffix } || skip | ||
130 | force=$(($force - 1)) | ||
131 | else | ||
132 | break | ||
133 | fi | ||
134 | done | ||
diff --git a/custom/uucp-mediaserver/scripts/queuesize b/custom/uucp-mediaserver/scripts/queuesize new file mode 100755 index 00000000..3b98ccdb --- /dev/null +++ b/custom/uucp-mediaserver/scripts/queuesize | |||
@@ -0,0 +1,24 @@ | |||
1 | #!@zsh@/bin/zsh | ||
2 | |||
3 | base=$(basename $0) | ||
4 | suffix=${base##*.} | ||
5 | |||
6 | #sent=$(cat /var/queuesize.$suffix) | ||
7 | #uustat -s $suffix | grep 'Executing recv-media' | sed -r 's/^.*\(sending ([0-9]+) bytes\)/\1/' | awk "BEGIN{s=${sent}}{s+=\$1}END{print s}" | ||
8 | |||
9 | awk 'BEGIN{s=0}{s+=$1}END{print s;}' @queueDir@/${suffix}.queuesize | ||
10 | |||
11 | # typeset -a queued | ||
12 | # queued=() | ||
13 | |||
14 | # ids=$({ uustat -s $suffix | cut -d ' ' -f 1; cut -d ' ' -f 2 /var/queuesize.$suffix } | sort | uniq -d ) | ||
15 | # queued=(${(f)ids}) | ||
16 | |||
17 | # sum=0 | ||
18 | |||
19 | # while read size id; do | ||
20 | # [[ ${queued[(r)$id]} == ${(q)id} ]] || continue | ||
21 | # sum=$((sum + size)) | ||
22 | # done < /var/queuesize.$suffix | ||
23 | |||
24 | # print $sum | ||
diff --git a/custom/uucp-mediaserver/scripts/queuestatus b/custom/uucp-mediaserver/scripts/queuestatus new file mode 100755 index 00000000..babacfea --- /dev/null +++ b/custom/uucp-mediaserver/scripts/queuestatus | |||
@@ -0,0 +1,41 @@ | |||
1 | #!@zsh@/bin/zsh | ||
2 | |||
3 | typeset -a queue | ||
4 | queue=() | ||
5 | |||
6 | suffix=${${0:t}##*.} | ||
7 | uucpsize() { | ||
8 | command queuesize.${suffix} ${@} | ||
9 | } | ||
10 | mediaspace() { | ||
11 | cat @queueDir@/${suffix}.space | ||
12 | } | ||
13 | queuesize() { | ||
14 | local sum=0 | ||
15 | for file (${queue}); do | ||
16 | local size=$(stat -c "%s" "${file}") || continue | ||
17 | sum=$((sum + size)) | ||
18 | done | ||
19 | printf "%d" ${sum} | ||
20 | } | ||
21 | |||
22 | queueTxt=$(grep -vE '^\s*$' @queueDir@/${suffix}.queue) | ||
23 | queue=(${(f)queueTxt}) | ||
24 | |||
25 | printf "Space: %s\n Reported: %s\n UUCP queue size: %s\n" \ | ||
26 | $(numfmt --to=iec-i --suffix=B -- $(($(mediaspace) - $(uucpsize)))) \ | ||
27 | $(mediaspace | numfmt --to=iec-i --suffix=B) \ | ||
28 | $(uucpsize | numfmt --to=iec-i --suffix=B) | ||
29 | |||
30 | printf "Queue: %d\n Size: %s\n" \ | ||
31 | ${#queue} \ | ||
32 | $(queuesize | numfmt --to=iec-i --suffix=B) | ||
33 | |||
34 | printf "\n" | ||
35 | |||
36 | for file (${queue}); do | ||
37 | size() { | ||
38 | stat -c "%s" "${file}" | numfmt --to=iec-i --suffix=B | ||
39 | } | ||
40 | printf "%6s %s\n" $(size || printf "N/A") ${file:t} | ||
41 | done | ||
diff --git a/custom/uucp-mediaserver/scripts/update-queuesize b/custom/uucp-mediaserver/scripts/update-queuesize new file mode 100755 index 00000000..db1b3e2a --- /dev/null +++ b/custom/uucp-mediaserver/scripts/update-queuesize | |||
@@ -0,0 +1,10 @@ | |||
1 | #!@zsh@/bin/zsh | ||
2 | |||
3 | base=$(basename $0) | ||
4 | suffix=${base##*.} | ||
5 | |||
6 | uustat -s $suffix | grep 'Executing recv-media' | sed -r 's/^([^ ]+) .*\(sending ([0-9]+) bytes\)/\2 \1/' >> @queueDir@/${suffix}.queuesize | ||
7 | tmpFile=$(mktemp --tmpdir=$HOME) | ||
8 | sort -k 2 @queueDir@/${suffix}.queuesize | uniq -f 1 > "${tmpFile}" | ||
9 | cat "${tmpFile}" >! @queueDir@/${suffix}.queuesize | ||
10 | rm "${tmpFile}" | ||
@@ -10,6 +10,7 @@ | |||
10 | ./odin/hw.nix | 10 | ./odin/hw.nix |
11 | ./users.nix | 11 | ./users.nix |
12 | ./custom/uucp.nix | 12 | ./custom/uucp.nix |
13 | ./custom/uucp-mediaserver.nix | ||
13 | ]; | 14 | ]; |
14 | 15 | ||
15 | # Use the GRUB 2 boot loader. | 16 | # Use the GRUB 2 boot loader. |
@@ -93,8 +94,15 @@ | |||
93 | ''; | 94 | ''; |
94 | commandPath = ["${pkgs.rmail}/bin"]; | 95 | commandPath = ["${pkgs.rmail}/bin"]; |
95 | defaultCommands = ["rmail"]; | 96 | defaultCommands = ["rmail"]; |
97 | |||
98 | media-server = { | ||
99 | enable = true; | ||
100 | remoteNodes = [ "hel" ]; | ||
101 | }; | ||
96 | }; | 102 | }; |
97 | 103 | ||
104 | users.groups."media" = {}; | ||
105 | |||
98 | services.postfix = { | 106 | services.postfix = { |
99 | enable = true; | 107 | enable = true; |
100 | enableSmtp = false; | 108 | enableSmtp = false; |
diff --git a/users/gkleen.nix b/users/gkleen.nix index 648f4ab1..b503b46b 100644 --- a/users/gkleen.nix +++ b/users/gkleen.nix | |||
@@ -1,7 +1,7 @@ | |||
1 | { | 1 | { |
2 | name = "gkleen"; | 2 | name = "gkleen"; |
3 | description = "Gregor Kleen"; | 3 | description = "Gregor Kleen"; |
4 | extraGroups = [ "wheel" "network" "lp" "dialout" "audio" "xmpp" "mail" "ssh" "vboxusers" ]; | 4 | extraGroups = [ "wheel" "network" "lp" "dialout" "audio" "xmpp" "mail" "ssh" "vboxusers" "media" ]; |
5 | group = "users"; | 5 | group = "users"; |
6 | uid = 1000; | 6 | uid = 1000; |
7 | createHome = true; | 7 | createHome = true; |