From af359b71b000651812e1ee70638b480502eba0ec Mon Sep 17 00:00:00 2001
From: Gregor Kleen <gkleen@yggdrasil.li>
Date: Thu, 5 Apr 2018 22:05:05 +0200
Subject: media-server on odin

---
 custom/uucp-mediaserver.nix                      |  59 ++++++++++
 custom/uucp-mediaserver/scripts.nix              |  32 ++++++
 custom/uucp-mediaserver/scripts/mediaspace       |   9 ++
 custom/uucp-mediaserver/scripts/queue            | 134 +++++++++++++++++++++++
 custom/uucp-mediaserver/scripts/queuesize        |  24 ++++
 custom/uucp-mediaserver/scripts/queuestatus      |  41 +++++++
 custom/uucp-mediaserver/scripts/update-queuesize |  10 ++
 7 files changed, 309 insertions(+)
 create mode 100644 custom/uucp-mediaserver.nix
 create mode 100644 custom/uucp-mediaserver/scripts.nix
 create mode 100755 custom/uucp-mediaserver/scripts/mediaspace
 create mode 100755 custom/uucp-mediaserver/scripts/queue
 create mode 100755 custom/uucp-mediaserver/scripts/queuesize
 create mode 100755 custom/uucp-mediaserver/scripts/queuestatus
 create mode 100755 custom/uucp-mediaserver/scripts/update-queuesize

(limited to 'custom')

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 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.uucp.media-server;
+
+  touchStateFiles = name: ''
+    touch ${cfg.queueDir}/${name}.queue
+    touch ${cfg.queueDir}/${name}.space
+    touch ${cfg.queueDir}/${name}.queuesize
+    
+    chown ${cfg.owner}:${cfg.group} ${cfg.queueDir}/${name}.queue ${cfg.queueDir}/${name}.space ${cfg.queueDir}/${name}.queuesize
+  '';
+in {
+  options = {
+    services.uucp.media-server = {
+      enable = mkEnableOption "UUCP media server";
+
+      remoteNodes = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Clients to track
+        '';
+      };
+
+      queueDir = mkOption {
+        type = types.path;
+        default = "/var/spool/media";
+        description = "Queue directory";
+      };
+
+      owner = mkOption {
+        type = types.str;
+        default = "root";
+        description = "Owner of the various queue files";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "media";
+        description = "Group of the various queue files";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    system.activationScripts."uucp-media-server" = ''
+      mkdir -p ${cfg.queueDir}
+      chmod 750 ${cfg.queueDir}
+      chown ${cfg.owner}:${cfg.group} ${cfg.queueDir}
+      
+      ${concatStringsSep "\n" (map touchStateFiles cfg.remoteNodes)}
+    '';
+
+    environment.systemPackages = [ (pkgs.callPackage ./uucp-mediaserver/scripts.nix { config = cfg; }) ];
+  };
+}
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 @@
+{ stdenv, zsh, config }:
+
+let
+  mkSymlinks = name: ''
+    ln -s $out/libexec/mediaspace $out/bin/mediaspace.${name}
+    ln -s $out/libexec/queue $out/bin/queue.${name}
+    ln -s $out/libexec/queuesize $out/bin/queuesize.${name}
+    ln -s $out/libexec/queuestatus $out/bin/queuestatus.${name}
+    ln -s $out/libexec/update-queuesize $out/bin/update-queuesize.${name}
+  '';
+in stdenv.mkDerivation {
+  name = "uucp-mediaserver";
+  src = ./scripts;
+
+  phases = [ "installPhase" ];
+
+  inherit (config) queueDir;
+  
+  installPhase = ''
+    mkdir -p $out/libexec
+  
+    substituteFile $src/mediaspace $out/libexec/mediaspace
+    substituteFile $src/queue $out/libexec/queue
+    substituteFile $src/queuesize $out/libexec/queuesize
+    substituteFile $src/queuestatus $out/libexec/queuestatus
+    substituteFile $src/update-queuesize $out/libexec/update-queuesize
+
+    mkdir -p $out/bin
+
+    ${concatStringsSep "\n" (map mkSymlinks config.remoteNodes)}
+  '';
+}
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 @@
+#!@zsh@/bin/zsh
+
+base=$(basename $0)
+suffix=${base##*.}
+
+printf "%d\n" "$1" > @queueDir@/mediaspace.$suffix
+echo > @queueDir@/queuesize.$suffix
+update-queuesize.$suffix
+
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 @@
+#!@zsh@/bin/zsh
+
+logTag=${0:t}
+exec 1> >(logger -t "$logTag" -p news.notice)
+exec 2> >(logger -t "$logTag" -p news.error)
+
+doDebug=false
+debug() {
+    if $doDebug; then
+        logger -t "$logTag" -p news.debug
+    else
+        cat >/dev/null
+    fi
+}
+verbose() { $@ | debug }
+
+function mungefilename() {
+    print ${@} | tr -s $';&*|<> \t!_' '_'
+}
+
+typeset -a cleanupCmds
+cleanupCmds=()
+
+function doCleanup {
+    for cmd (${cleanupCmds}); do
+        eval ${cmd} | debug
+    done
+}
+
+trap doCleanup EXIT
+
+function cleanup() {
+    local cmd
+    cmd=""
+    for arg ($@); do
+        [[ -n "${cmd}" ]] && cmd="${cmd} "
+        cmd="${cmd}${(qq)arg}"
+    done
+
+    cleanupCmds+=(${cmd})
+}
+
+base=$(basename $0)
+suffix=${base##*.}
+
+force=0
+if [[ "$1" == "-f" ]]; then
+    shift
+    force=1
+    if [[ -n "$1" && "$1" -eq "$1" ]] 2>/dev/null; then
+        force="$1"
+        shift
+    fi
+fi
+
+noCall=false
+if [[ "$1" == "-r" ]]; then
+    shift
+    noCall=true
+fi
+
+for f (${@}); do
+    f=$(readlink -f ${f})
+    if grep -q ${f} @queueDir@/${suffix}.queue; then
+        printf "‘%s’ is already in queue file\n" ${f:t} | warn
+        continue
+    fi
+    if uustat | grep -q ${f:t}; then
+        printf "‘%s’ is already in uucp queue\n" ${f:t} | warn
+    fi
+    print -r ${f} >> @queueDir@/${suffix}.queue
+done
+
+offset=1
+
+advance() {
+    cat =(head -n $((offset - 1)) @queuedir@/${suffix}.queue) =(tail -n +$((offset + 1)) @queueDir@/${suffix}.queue) >@queueDir@/${suffix}.queue
+}
+
+while true; do
+    [[ $(wc -l @queueDir@/${suffix}.queue | cut -d ' ' -f 1) -lt $offset ]] && break
+    file=$(tail -n +${offset} @queueDir@/${suffix}.queue | head -n 1)
+    printf "Considering ‘%s’" ${file} | debug
+    if [[ -z "${file}" || ! -e "${file}" ]]; then
+        if [[ -n "${file}" ]]; then
+	  printf "‘%s’ does not exist, skipping\n" "${file}" >&2
+          printf "Subject: Missing file in %s\n\n%s" $logTag ${file} \
+              | sendmail gkleen \
+              && echo "Sent mail."
+        fi
+	advance
+	continue
+    fi
+
+    space=$(($(cat @queueDir@/${suffix}.space) - $(queuesize.$suffix)))
+    printf "%s left on %s\n" $(numfmt --suffix=B --to=iec-i -- $space) $suffix | debug
+
+    size=$(stat -c "%s" "${file}")
+    printf "‘%s’ is %s\n" ${file:t} $(numfmt --suffix=B --to=iec-i -- $size) | debug
+    if [[ "${size}" -le "${space}" || "${force}" -gt 0 ]]; then
+	printf "queuing ‘%s’ for %s\n" ${file:t} ${suffix} | debug
+        function send() {
+            typeset -a cmd
+            cmd=(uux $(if ${noCall}; then echo "-r"; fi) -g z)
+
+            compatibleName=${file}
+            function munge() { [[ $(mungefilename ${file}) != ${file} ]] }
+
+            if munge; then
+                compatibleName=${file:h}/$(mungefilename ${file:t})
+                [[ -e ${compatibleName} ]] || verbose ln -vs ${file} ${compatibleName}
+                cmd+=(-C)
+                cleanup rm -v ${compatibleName}
+            fi
+
+            cmd+=("${suffix}!recv-media" "!${compatibleName}" "$(date --date=@$(stat -c '%Y' "${file}") "+%Y%m%d%H%M.%S")")
+
+            if munge; then
+                cmd+=("$(print -- ${file:t} | base64 -w0)")
+            fi
+            $cmd && printf "Queued ‘%s’ for %s\n" ${file:t} $suffix
+        }
+
+        function skip() {
+            offset=$((offset + 1))
+            printf "Failed to queue ‘%s’\n" ${file} >&2
+        }
+        
+        { { [[ ! -e "${file}" ]] || send } && advance && update-queuesize.$suffix } || skip
+        force=$(($force - 1))
+    else
+	break
+    fi
+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 @@
+#!@zsh@/bin/zsh
+
+base=$(basename $0)
+suffix=${base##*.}
+
+#sent=$(cat /var/queuesize.$suffix)
+#uustat -s $suffix | grep 'Executing recv-media' | sed -r 's/^.*\(sending ([0-9]+) bytes\)/\1/' | awk "BEGIN{s=${sent}}{s+=\$1}END{print s}"
+
+awk 'BEGIN{s=0}{s+=$1}END{print s;}' @queueDir@/${suffix}.queuesize
+
+# typeset -a queued
+# queued=()
+
+# ids=$({ uustat -s $suffix | cut -d ' ' -f 1; cut -d ' ' -f 2 /var/queuesize.$suffix } | sort | uniq -d )
+# queued=(${(f)ids})
+
+# sum=0
+
+# while read size id; do
+#     [[ ${queued[(r)$id]} == ${(q)id} ]] || continue
+#     sum=$((sum + size))
+# done < /var/queuesize.$suffix
+
+# 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 @@
+#!@zsh@/bin/zsh
+
+typeset -a queue
+queue=()
+
+suffix=${${0:t}##*.}
+uucpsize() {
+    command queuesize.${suffix} ${@}
+}
+mediaspace() {
+    cat @queueDir@/${suffix}.space
+}
+queuesize() {
+    local sum=0
+    for file (${queue}); do
+        local size=$(stat -c "%s" "${file}") || continue
+        sum=$((sum + size))
+    done
+    printf "%d" ${sum}
+}
+
+queueTxt=$(grep -vE '^\s*$' @queueDir@/${suffix}.queue)
+queue=(${(f)queueTxt})
+
+printf "Space: %s\n  Reported: %s\n  UUCP queue size: %s\n" \
+       $(numfmt --to=iec-i --suffix=B -- $(($(mediaspace) - $(uucpsize)))) \
+       $(mediaspace | numfmt --to=iec-i --suffix=B) \
+       $(uucpsize | numfmt --to=iec-i --suffix=B)
+
+printf "Queue: %d\n  Size: %s\n" \
+       ${#queue} \
+       $(queuesize | numfmt --to=iec-i --suffix=B)
+
+printf "\n"
+
+for file (${queue}); do
+    size() {
+        stat -c "%s" "${file}" | numfmt --to=iec-i --suffix=B
+    }
+    printf "%6s %s\n" $(size || printf "N/A") ${file:t}
+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 @@
+#!@zsh@/bin/zsh
+
+base=$(basename $0)
+suffix=${base##*.}
+
+uustat -s $suffix | grep 'Executing recv-media' | sed -r 's/^([^ ]+) .*\(sending ([0-9]+) bytes\)/\2 \1/' >> @queueDir@/${suffix}.queuesize
+tmpFile=$(mktemp --tmpdir=$HOME)
+sort -k 2 @queueDir@/${suffix}.queuesize | uniq -f 1 > "${tmpFile}"
+cat "${tmpFile}" >! @queueDir@/${suffix}.queuesize
+rm "${tmpFile}"
-- 
cgit v1.2.3