+type node = {id : int, name : string, descr : string, debian : string}
+
+fun mkNodeRow [id, name, descr, debian] =
+ {id = C.intFromSql id, name = C.stringFromSql name, descr = C.stringFromSql descr,
+ debian = C.stringFromSql debian}
+ | mkNodeRow row = rowError ("node", row)
+
+fun listNodes () =
+ C.map (getDb ()) mkNodeRow ($`SELECT id, name, descr, debian
+ FROM WebNode
+ WHERE id IN (SELECT id FROM ActiveWebNode)
+ ORDER BY name`)
+
+fun nodeName id =
+ case C.oneRow (getDb ()) ($`SELECT name
+ FROM WebNode
+ WHERE id = ^(C.intToSql id)`) of
+ [name] => C.stringFromSql name
+ | row => rowError ("nodeName", row)
+
+fun nodeDebian id =
+ case C.oneRow (getDb ()) ($`SELECT debian
+ FROM WebNode
+ WHERE id = ^(C.intToSql id)`) of
+ [debian] => C.stringFromSql debian
+ | row => rowError ("nodeDebian", row)
+
+fun explain e =
+ case e of
+ OS.SysErr (name, sop) =>
+ "System error: " ^ name ^
+ (case sop of
+ NONE => ""
+ | SOME syserr => ": " ^ OS.errorName syserr ^ ": " ^ OS.errorMsg syserr)
+ | _ => "Unknown"
+
+fun tokens () =