Files
mesh-catalog/modules/cups/cmd/cups-tools/main.go
T
jochen b352f3920e cups: the scheduler and driverless printing, and the printers as tools (hq to-be 42 phase 2.9)
Research 027 asked for cups with the printer's driver. Measured: both
Brother queues already print through IPP Everywhere, so cups and
cups-filters are the whole driver, and the AUR vendor packages beside them
serve no queue. The desktop's Canon is the exception: its 2012 driver is
AUR-only and waits for the mesh's package repository; it stays as found.

Seven Go tools: printers (state, device, driverless or not, supply levels),
queue, cancel (the account first, sudo -n when CUPS refuses it), print,
default, resume, and drivers (which packages bring drivers, which are
foreign, which no queue uses).
2026-10-04 13:02:23 +02:00

178 lines
6.2 KiB
Go

// The cups module's tools (novox/hq research 027/02, 026/05): the printers, their state, supplies and
// driver, the queue, and printing, cancelling and choosing the default. A Go bundle the node's runtime
// launches over stdio (ADR 0188, ADR 0193); it runs as the operator account. An act CUPS keeps for
// its administrators goes through `sudo -n`.
package main
import (
"fmt"
"os"
stdio "git.novox.be/novox/mesh-sdk/go"
)
var providedBy = map[string]string{
"lpstat": "the cups package, which this module installs",
"lpoptions": "the cups package, which this module installs",
"lp": "the cups package, which this module installs",
"cancel": "the cups package, which this module installs",
"lpadmin": "the cups package, which this module installs",
"lpinfo": "the cups package, which this module installs",
"cupsenable": "the cups package, which this module installs",
"cupsaccept": "the cups package, which this module installs",
"pacman": "this is not an Arch machine",
}
func main() {
if err := stdio.Serve("", tools()); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
var printerArg = map[string]any{"type": "string", "description": "the printer's queue name, as cups_printers answers it"}
func tools() []stdio.Tool {
return []stdio.Tool{
{
Name: "cups_printers",
Description: "Every printer queue: state, whether it is enabled and accepting jobs, the default, its device " +
"address, make and model, whether it prints driverless (IPP Everywhere), the reasons for its state, and " +
"supply levels where the printer reports them. (r)",
Input: map[string]any{},
Run: func(map[string]any) (any, error) { return Printers() },
},
{
Name: "cups_queue",
Description: "The jobs waiting or printing, on every printer or one; or, with completed, the finished ones. " +
"Each with its id, printer, owner, size and when it was submitted. (r)",
Input: map[string]any{
"printer": printerArg,
"completed": map[string]any{"type": "boolean", "description": "the finished jobs instead"},
"limit": map[string]any{"type": "integer", "description": "at most this many, newest first (default 50, at most 500)"},
},
Run: func(args map[string]any) (any, error) {
p, err := optText(args, "printer", "")
if err != nil {
return nil, err
}
done, err := optFlag(args, "completed", false)
if err != nil {
return nil, err
}
limit, err := optWhole(args, "limit", 50, 1, 500)
if err != nil {
return nil, err
}
return Queue(p, done, limit)
},
},
{
Name: "cups_cancel",
Description: "Cancel one job by its id (\"Brother-12\" or 12), or every job on a printer with all. Another " +
"account's job is cancelled through sudo -n. (a)",
Input: map[string]any{
"job": map[string]any{"type": "string", "description": "the job id"},
"printer": printerArg,
"all": map[string]any{"type": "boolean", "description": "every job on printer"},
},
Run: func(args map[string]any) (any, error) {
job, err := optText(args, "job", "")
if err != nil {
return nil, err
}
p, err := optText(args, "printer", "")
if err != nil {
return nil, err
}
all, err := optFlag(args, "all", false)
if err != nil {
return nil, err
}
return Cancel(job, p, all)
},
},
{
Name: "cups_print",
Description: "Print a file on this machine, to a printer or the default, with copies and IPP options such as " +
"sides=two-sided-long-edge or media=A4. Answers the job id. (a)",
Input: map[string]any{
"file": map[string]any{"type": "string", "description": "the file's absolute path on this machine"},
"printer": printerArg,
"copies": map[string]any{"type": "integer", "description": "copies (default 1, at most 99)"},
"options": map[string]any{"type": "object", "additionalProperties": map[string]any{"type": "string"}, "description": "IPP options, name to value"},
"title": map[string]any{"type": "string", "description": "the job's title (default the file's name)"},
},
Run: func(args map[string]any) (any, error) {
file, err := text(args, "file")
if err != nil {
return nil, err
}
p, err := optText(args, "printer", "")
if err != nil {
return nil, err
}
copies, err := optWhole(args, "copies", 1, 1, 99)
if err != nil {
return nil, err
}
opts, err := optionsOf(args)
if err != nil {
return nil, err
}
title, err := optText(args, "title", "")
if err != nil {
return nil, err
}
return Print(file, p, copies, opts, title)
},
},
{
Name: "cups_default",
Description: "The machine's default printer; with printer, make that printer the default (through sudo -n). " +
"The default is CUPS's own setting, kept as the operator chose it: the mesh does not declare it. (r/a)",
Input: map[string]any{"printer": printerArg},
Run: func(args map[string]any) (any, error) {
p, err := optText(args, "printer", "")
if err != nil {
return nil, err
}
return Default(p)
},
},
{
Name: "cups_resume",
Description: "Enable a printer and make it accept jobs again, after CUPS stopped it on an error. Through sudo -n. (a)",
Input: map[string]any{"printer": printerArg},
Run: func(args map[string]any) (any, error) {
p, err := text(args, "printer")
if err != nil {
return nil, err
}
return Resume(p)
},
},
{
Name: "cups_drivers",
Description: "What each printer prints through (driverless or a driver's PPD), the packages that bring drivers " +
"and backends, which of them are from outside the official repositories, and the driver models CUPS " +
"offers, filtered by match. (r)",
Input: map[string]any{
"match": map[string]any{"type": "string", "description": "only models whose description contains this, any case"},
"limit": map[string]any{"type": "integer", "description": "at most this many models (default 50, at most 1000)"},
},
Run: func(args map[string]any) (any, error) {
match, err := optText(args, "match", "")
if err != nil {
return nil, err
}
limit, err := optWhole(args, "limit", 50, 0, 1000)
if err != nil {
return nil, err
}
return Drivers(match, limit)
},
},
}
}