diff --git a/modules/cups/examples/simple.mcl b/modules/cups/examples/simple.mcl new file mode 100644 index 000000000..20256fef5 --- /dev/null +++ b/modules/cups/examples/simple.mcl @@ -0,0 +1,51 @@ +import "git://github.com/purpleidea/mgmt/modules/cups/" + +$default_printer = { + "ws1" => "Foo-Laboratory-Brother", + "lappy2" => "Bar-Office-Canon", +} +$subset_printers = { + "Foo-Laboratory-Brother" => ["ws1",], +} +include cups.base() as printers + +# helper function +# make sure you add your .ppd files +class printer($name, $st) { + $default = $default_printer[$hostname] || "" + $subset = $subset_printers[$name] || [] + + $location = $st->location || "" + $makemodel = $st->makemodel || "" + $uri str = $st->uri + + $comment = $st->comment || "" + + # XXX: if we had a method that took a struct, and added a field and returned it, that would be helpful! + # XXX: this would need to have language sugar so that we guarantee the field name string is static. + # XXX: eg: $new_st = $old_st.foo => "bar" + # XXX: eg: $new_st = { $old_st with foo => "bar" } + if $subset == [] or $hostname in $subset { + include printers.printer($name, struct{ + default => $name == $default, + info => $name, # since the name is descriptive + location => $location, + makemodel => $makemodel, + uri => $uri, + ppd => deploy.readfile("/files/ppd/${name}.ppd"), + comment => $comment, + }) + } +} + +include printer("Foo-Laboratory-Brother", struct{ + location => "Foo's Office", + makemodel => "Brother Printer, driverless, 2.1b1", + uri => "lpd://192.168.201.108:515/PASSTHRU", # TODO: change me? +}) + +include printer("Bar-Office-Canon", struct{ + location => "Bar's Office", + makemodel => "Canon iR-ADV C351 PPD", + uri => "lpd://192.168.201.120", +}) diff --git a/modules/cups/files/printer.conf.tmpl b/modules/cups/files/printer.conf.tmpl new file mode 100644 index 000000000..74f665672 --- /dev/null +++ b/modules/cups/files/printer.conf.tmpl @@ -0,0 +1,35 @@ +{{/* +TODO: A lot of this could be templated, if we knew what it did. +*/ -}} +{{ if .default -}} + +{{ else -}} + +{{ end -}} +PrinterId {{ .id }} +UUID urn:uuid:{{ .uuid }} +{{ if .info -}} +Info {{ .info }} +{{ end -}} +{{ if .location -}} +Location {{ .location }} +{{ end -}} +MakeModel {{ .makemodel }} +DeviceURI {{ .uri }} +State Idle +StateTime 1735329279 +ConfigTime 1734305561 +Type 36884 +Accepting Yes +Shared Yes +JobSheets none none +QuotaPeriod 0 +PageLimit 0 +KLimit 0 +OpPolicy default +ErrorPolicy abort-job +{{ if .default -}} + +{{ else -}} + +{{ end -}} diff --git a/modules/cups/files/printers.conf.header b/modules/cups/files/printers.conf.header new file mode 100644 index 000000000..3462efcbd --- /dev/null +++ b/modules/cups/files/printers.conf.header @@ -0,0 +1,4 @@ +# Printer configuration file for CUPS +# Written by mgmt config +# DO NOT EDIT THIS FILE WHEN CUPSD IS RUNNING +NextPrinterId 1024 diff --git a/modules/cups/main.mcl b/modules/cups/main.mcl new file mode 100644 index 000000000..8e44428ca --- /dev/null +++ b/modules/cups/main.mcl @@ -0,0 +1,167 @@ +# Mgmt +# Copyright (C) James Shubin and the project contributors +# Written by James Shubin and the project contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Additional permission under GNU GPL version 3 section 7 +# +# If you modify this program, or any covered work, by linking or combining it +# with embedded mcl code and modules (and that the embedded mcl code and +# modules which link with this program, contain a copy of their source code in +# the authoritative form) containing parts covered by the terms of any other +# license, the licensors of this program grant you additional permission to +# convey the resulting work. Furthermore, the licensors of this program grant +# the original author, James Shubin, additional permission to update this +# additional permission if he deems it necessary to achieve the goals of this +# additional permission. + +import "deploy" +import "golang" +import "golang/strconv" as golang_strconv +import "local" +import "os" +import "strings" + +class base() { + $vardir = local.vardir("cups/") + + file "/etc/cups/" { + state => $const.res.file.state.exists, + recurse => false, # not completely managed! + purge => false, # not completely managed! + owner => "root", + group => "lp", # TODO: debian? + mode => "u=rwx,go=rx", # dir + } + + file "/etc/cups/printers.conf" { + state => $const.res.file.state.exists, + fragments => [ + "${vardir}printers.conf.header", # also pull this one file + "${vardir}printers.d/", # pull from this dir + ], + owner => "root", + group => "lp", + mode => "u=rw,go=", + + Before => Exec["restorecon"], + Notify => Svc["cups"], + } + + exec "restorecon" { + cmd => "/usr/sbin/restorecon -rv /etc/cups/", + # XXX: make some magic snippets which turn into ./mgmt snippet /etc/cups/ (for example) and get substituted in here! + # XXX: or even better, instead of snippets which exec mgmt stuff, they turn into pure golang equivalents... + ifcmd => "mgmt:changed /etc/cups/", + watchcmd => "mgmt:dir /etc/cups/", + } + + svc "cups" { + # TODO: manage this + } +} + +class base:printer_base() { + file "${vardir}printers.d/" { + state => $const.res.file.state.exists, + recurse => true, + purge => true, + owner => "root", + group => "root", + mode => "u=rwx,go=", # dir + } + + file "${vardir}printers.conf.header" { + state => $const.res.file.state.exists, + content => deploy.readfile("/files/printers.conf.header"), # static, no template! + owner => "root", + group => "root", + mode => "u=rw,go=", + } +} + +class base:printer($name, $st) { + $default = $st->default || false + $info = $st->info || "" + $location = $st->location || "" + $makemodel = $st->makemodel || "" + $uri str = $st->uri + $ppd str = $st->ppd + + $comment = $st->comment || "" + + include printer_base + + $index = local.pool("cups-printer", $name) # the uid will always return the same int + # XXX: implement local.pool_max("cups-printer") to use for NextPrinterId + + panic($index < 0 or $index > 65535) # 0xffffh is the maximum + $hex = strings.left_pad(golang_strconv.format_int($index, 16), "0", 4) + $tail = strings.substr($hex, 0, 4) # TODO: support $hex[0] or $hex[0:4] ? + $uuid = "01234567-89ab-cdef-0000-00000000${tail}" + + $tmpl = struct{ + name => $name, + id => $index, # it just has to be constant and unique + uuid => $uuid, + default => $default, + info => $info, + location => $location, + makemodel => $makemodel, + uri => $uri, + #ppd => $ppd, + comment => $comment, + } + + $content = golang.template(deploy.readfile("/files/printer.conf.tmpl"), $tmpl) + #$f = os.readfile("${vardir}printers.d/${name}") # XXX: replace with something.stateok() ??? + #if $f != $content { + # # XXX: stop the svc... then change the file, then start it up again... + # # XXX: might need a flag so that we stop the svc for multiples of these all in the same place... + #} + + file "${vardir}printers.d/${name}" { + state => $const.res.file.state.exists, + content => $content, + owner => "root", + group => "root", + mode => "u=rw,go=", + } + + file "/etc/cups/ppd/${name}.ppd" { + state => $const.res.file.state.exists, + # TODO: build a file "handle" system for bigger data + content => $ppd, + #source => $ppd, + owner => "root", + group => "lp", + mode => "u=rw,g=r,o=", + + Before => Exec["restorecon"], + Notify => Svc["cups"], + } + + if $default { + # there can only be one! + # if more than one of these is set on the same machine we'd error + file "${vardir}default" { + state => $const.res.file.state.exists, + content => "${name}", + owner => "root", + group => "root", + mode => "u=rw,go=", + } + } +} diff --git a/modules/cups/metadata.yaml b/modules/cups/metadata.yaml new file mode 100644 index 000000000..e69de29bb