CUPS
Sumario
Enlaces y referencias
- De las páginas del proyecto CUPS:
- El wiki de Opensuse incluye páginas muy útiles para entender el funcionamiento de Cups y su adaptabilidad:
- Funcionamiento interno de CUPS:
- Tutorial de Till Kamppeter sobre la comunicación con los servidores de impresión, los clientes y las impresoras; más útil para desarrollo que para administración, pero muy interesante.
- CUPS en otros entornos:
- Impresoras virtuales:
- PDF:
- Documentación oficial.
- Blog "Algo de Linux" describiendo la instalación desde las fuentes.
- Artículo sobre Ubuntu en inglés
- Creating a free PDFWriter using Ghostscript describe aspectos básicos sobre el uso de ghostscript en entornos Windows 98/2000/XP.
- ps2pdf: PostScript-to-PDF converter donde se detallan las opciones para convertir archivos PostScript a PDF empleando esta herramienta, que es un envoltorio para
gs
.
- Interactivas:
- i-v-p-cups A set of interactive virtual printers (pdf, ps, djvu, gtklp) through a cups backend.
- PDF:
- Códigos de impresión:
Conceptos
Los sistemas GNU/Linux tienen un formato de datos común manipular impresos: el lenguaje PostScript. Eso no significa que todas las aplicaciones tengan obligatoriamente que generar dicho formato para imprimir, sino que al ser el formato que mejor maneja CUPS se toma como base para todas las operaciones de impresión.
Tampoco es el único formato de datos base. PDF se va para sustituir al PostScript debido a ciertas ventajas en su contenido y su manipulación, así como a la similitud con Mac OS/X de Apple que a fin de cuentas es la propietaria de los derechos de copia y de la marca CUPS.
Entre las ventajas tenemos:
- Menor tamaño
- Mejor definición del contenido de cada página lo que facilita su selección y ordenación, es decir, un manejo post procesado mucho más sencillo.
- Mejor soporte para color de alta densidad y transparencias.
- ...
Tipos MIME
Aunque en circunstancias normales CUPS emplea el lenguaje PostScript como base para trabajar (siempre se puede ajustar la configuración manualmente para saltarse todo este mecanismo) necesita saber cómo convertir los textos que recibe y, sobre todo, cómo identificar su naturaleza.
Para ello emplea un diccionario de reglas que valida con el texto recibido y que se define en archivos con la extensión .types
, dentro de su directorio de configuración /etc/cups
. El archivo principal suele ser /etc/cups/mime.types
, aunque el servidor carga todos los que encuentra en ese directorio con la misma extensión, los ordena alfabéticamente y los usa posteriormente en ese orden.
Los tipos MIME contienen una clase llamada supertipo y una subclase dentro de ellas llamada subtipo. Para expresar un tipo MIME se emplea el carácter barra (/
) para separar el supertipo del subtipo, y en ocasiones también se usan asteríscos como carácter comodín.
application/postscript image/x-portable-anymap text/plain application/vnd.cups-ppd
Cada regla consiste en la declaración del tipo MIME seguida de una o más reglas que el contenido debe cumplir para ser etiquetado con ese tipo.
tipo_MIME regla [ ... reglaN ]
Las reglas tienen dos formas específicas. La primera es una extensión de archivo y la segunda es una función con valores para testear entre paréntesis; ambas pueden aparecer una o más veces y pueden agruparse empleando los siguientes operadores lógicos además de los correspondientes paréntesis:
- + es el operador lógico AND.
- , (coma o un espacio en blanco) es el operador lógico OR
- ! es el operador lógico NOT
La siguiente regla es para contenidos HTML:
text/html html htm printable(0,1024) +\ (istring(0,"<HTML>") istring(0,"<!DOCTYPE"))
Y quiere decir que si el archivo tiene la extensión html o htm o la función printable devuelve el valor verdadero con los parámetros que ha recibido y cualquiera de las dos funciones istring son ciertas, entonces damos el contenido como bueno para ser HTML.
Dentro de los parámetros se pueden especificar textos que contienen espacios en blanco entre paréntesis y usar los signo mayor y menor que para los valores hexadecimales. Los valores numéricos no necesitan envoltura.
Función | Descripción |
---|---|
match("patrón") | Comprueba la coincidencia del nombre del archivo con el patrón. match("adriano*") |
ascii( posición, longitud ) | Comprueba que los caracteres del rango indicado pertenecen al conjunto de caracteres ASCII imprimibles (CR, NL, TAB, BS, 32-126).
image/x-xpixmap xpm ascii(0,1024) + string(3,"XPM") |
printable( posición, longitud ) | Comprueba que los caracteres del rango indicado son caracteres de 8 bits imprimibles (CR, NL, TAB, BS, 32-126, 128-254) |
string( posición, "texto" ) | Comprueba si en la posición dada los datos fuente coincide con el texto indicado.
image/x-sun-raster ras string(0,<59a66a95>) |
istring( posición, "texto" ) | Igual que la anterior pero sin distinguir entre mayúsculas y minúsculas. |
char( posición, valor ) | Comprueba si el byte en esa posición tiene el valor indicado. |
short( posición, valor ) | Comprueba si el valor de 16 bits en esa posición coincide con el valor indicado. |
int( posición, valor ) | Igual que los anteriores pero para un valor de 32 bits. |
locale( "texto" ) | Verdadero si el valor locale actual coincide por nombre con el texto. |
contains( posición, rango, "texto" ) |
Comprueba si desde esa posición y hasta ese rango aparece el texto indicado. (contains(0,4096,"LANGUAGE=HPGL") \ contains(0,4096,"LANGUAGE = HPGL"))) |
priority( número ) | Fija la prioridad relativa al valor indicado para este tipo MIME. Esto se emplea para determinar qué tipo elegir si existen dos iguales con diferentes reglas. |
Conversión de tipos
Para convertir entre diferentes tipos de datos CUPS emplea una tabla de conversiones en un archivo de texto plano, que en Debian puede encontrarse en /etc/cups/mime.convs
.
Cada línea válida en este archivo tiene el siguiente formato:
tipo_MIME_origen tipo_MIME_destino coste programa_para_convertir
- El tipo MIME -tanto el origen como el destino- puede especificarse por nombre o bien usar un comodín para el super tipo y el subtipo.
- El campo coste se emplea para elegir entre dos juegos de filtros cuando existe más de uno para un determinado tipo de conversión.
- El programa es el filtro a utilizar para realizar la conversión. Si la ruta no es absoluta CUPS lo buscará en su directorio de filtros (en Debian es
/usr/lib/cups/filter/
) y si se utiliza un guión (-
) como nombre entonces se envía directamente el resultado al controlador final (backend).
Ejemplos:
# Para imprimir archivos PDF ... application/pdf application/postscript 33 pdftops # Para los archivos PostScript ... application/postscript application/vnd.cups-postscript 66 pstops # Sin filtrado alguno ... (todo el texto se envía directamente al backend) application/octet-stream application/vnd.cups-raw 0 -
Filtrado
El propósito principal del sistema de filtrado es convertir los datos originales del trabajo de impresión (texto plano en ASCII, PostScript, PDF, ...) en directivas de impresión especifícas para la impresora (PostScript, PCL, ESC/P, ... ).
El filtrado de CUPS funciona de ésta forma:
- Convierte los datos originales al lenguaje PostScript.
- Determina cuál es el formato original empleando el mecanismo MIME.
- Si el tipo MIME resultante no es PostScript se emplea el mecanismo mime.convs para obtener una versión del trabajo en ese formato.
- Inserta fragmentos PostScript para activar las funciones necesarias para imprimir el trabajo.
- Convierte el texto PostScript al formato específico de la impresora si ésta no entiende ese lenguaje. En este caso se suele emplear alguna solución intermedia como foomatic para invocar al programa ghostscript como intérprete final.
Formato vnd.cups-postscript
CUPS emplea un tipo MIME muy similar al PostScript al que clasifica como application/vnd.cups-postscript (vnd proviene de vendor) y que resulta de añadir las opciones de impresión correspondientes en forma de secuencias PostScript al documento ya convertido para imprimir. En otras palabras, el tipo MIME application/postscript es independiente del dispositivo mientras que application/vnd.cups-postscript no.
El responsable de esta modificación es el filtro pstops, el cual emplea la información del archivo PPD para ello.
Secuencias PostScript
Los trabajos de impresión pueden llevar asociadas ciertas opciones globales según la impresora a la que van destinados, tales como impresión duplex o el tamaño de la página. Dichas opciones se definen en los archivos PPD y suelen consistir realmente en fragmentos de código PostScript que deben ser intercalados en el texto a imprimir. El intérprete PostScript final, ya sea un programa o la propia impresora, se encargarán de activarlas.
Esta operación la lleva a cabo el filtro pstops y siempre que la configuración de CUPS así lo disponga. En este caso en la conversión de tipos MIME debe aparecer una línea como ésta
application/postscript application/vnd.cups-postscript 66 pstops
Errores y problemas
cupsdoprint ... client-error-not-authorized
Fragmento del registro de CUPS donde aparece dicho error:
D [ ... ] cupsdAcceptClient: 12 from 192.168.0.1:631 (IPv4) D [ ... ] cupsdReadClient: 12 GET /printers/hp2100.ppd HTTP/1.1 D [ ... ] cupsdAuthorize: No authentication data provided. D [ ... ] cupsdCloseClient: 12 D [ ... ] cupsdAcceptClient: 12 from 192.168.0.1:631 (IPv4) D [ ... ] cupsdReadClient: 12 POST /printers/hp2100 HTTP/1.1 D [ ... ] cupsdAuthorize: No authentication data provided. D [ ... ] Print-Job ipp://localhost/printers/hp2100 D [ ... ] [Job ???] Auto-typing file... I [ ... ] [Job ???] Request file type is application/postscript. D [ ... ] Print-Job client-error-not-authorized: La impresora o clase no está compartida. D [ ... ] cupsdProcessIPPRequest: 12 status_code=403 (client-error-not-autorized) D [ ... ] cupsdCloseClient: 12
Y nos fijamos en el que el error es efectivamente client-error-not-authorized, pero no habíamos visto el resto del mensaje, traducido además al idioma local del sistema: impresora ... no está compartida.
Fragmento del mismo registro con una impresión satisfactoria en una impresora conectada de la misma forma al servidor:
D [ ... ] [29/Dec/2009:10:45:45 +0100] cupsdAcceptClient: 12 from 192.168.0.1:631 (IPv4) D [ ... ] [29/Dec/2009:10:45:45 +0100] cupsdReadClient: 12 POST /printers/hpk5400 HTTP/1.1 D [ ... ] [29/Dec/2009:10:45:45 +0100] cupsdAuthorize: No authentication data provided. D [ ... ] [29/Dec/2009:10:45:46 +0100] cupsdAcceptClient: 14 from 192.168.0.1:631 (IPv4) D [ ... ] [29/Dec/2009:10:45:46 +0100] Print-Job ipp://localhost/printers/hpk5400 D [ ... ] [29/Dec/2009:10:45:46 +0100] [Job ???] Auto-typing file... I [ ... ] [Job ???] Request file type is application/postscript. D [ ... ] [29/Dec/2009:10:45:46 +0100] [Job 147166] Loading attributes... ... ... multitud de líneas más y un trabajo impreso :-) ...
Ahora vamos a ver dónde está la diferencia en la configuración de las dos impresoras revisando el archivo /etc/cups/printers.conf ya que así evitamos malas interpretaciones en capas superiores.
Las entradas para ambas impresoras muestras discrepancias en los campos Info, DeviceURI, StateTime y Shared. Los tres primeros son obvios puesto que son máquinas diferentes pero el último es el definitivo: en la impresora hpk5400 (y en todas las otras impresoras del servidor) el valor es Yes mientras que para la hp2100 el valor es No.
La documentación indica que el atributo shared significa que el servidor no anunciará a la red la existencia de dicha impresora ó clase de impresoras. Y bueno, vale, hasta ahí bien, pero ¿ por qué rechaza la impresión ? No se dice nada sobre impedir la impresión, sólo sobre anunciar la cola de impresión en red.
En cualquier caso para cambiar dicho valor sin reiniciar el servidor podemos emplear lo siguiente desde un terminal con acceso:
# /usr/sbin/lpadmin -P hp2100 -o printer-is-shared=yes
Cups no detecta los puertos locales
Si cuando conectamos una impresora a un puerto paralelo ó a un puerto USB CUPS no lo detecta es posible que tengamos un simple problema de permisos.
Conviene revisar si en el registro de actividades del programa (/var/log/cups/error_log generalmente) encontramos la siguiente advertencia:
I [12/Nov/2009:10:58:33 +0100] Loaded configuration file "/etc/cups/cupsd.conf" N [12/Nov/2009:10:58:33 +0100] Group and SystemGroup cannot use the same groups! I [12/Nov/2009:10:58:33 +0100] Resetting Group to "root"...
en otros casos el grupo puede fijarse a nogroup y casi es peor, pero de cualquier forma eso significa problemas para acceder a los diferentes dispositivos locales.
En Debian la configuración correcta es:
SystemGroup lpadmin User lp Group lp
dentro de /etc/cups/cups.conf.
Algunos sitios donde ampliar la información:
- Ref: Hilo en los foros de Ubuntu al respecto.
- Ref: Capítulo del manual de administradores que comenta los usos y disposiciones.
Unable to open the initial device, quitting
Me he encontrado este error en una impresora/fotocopiadora Canon modelo iRC 1021i -tras una actualización de seguridad en un servidor Debian (versión Squeeze) de 64 bits- en el que había instalado los controladores proporcionados por el fabricante.
Estos se entregan en formato RPM y tras la necesaria conversión a formato deb
empleando el programa alien:
$ fakeroot alien --scripts cndrvcups-common-2.20-1.x86_64.rpm $ fakeroot alien --scripts cndrvcups-ufr2-uk-2.20-1.x86_64.rpm $ sudo dpkg -i *.deb
y la configuración de la impresora para que emplee el modelo Canon iRC1021/1022 UFRII LT ver.2.2 cualquier envío a la impresora resulta en una única página con el texto que encabeza esta sección.
El problema se debe a que parte de esos controladores están en 32 bits por lo que se necesita el paquete complementario ia32-libs para que todo funcione dentro de la normalidad (el software proporcionado por Canon no es de gran calidad precisamente).
/usr/lib/cups/filter/pstoufr2cpca - Too many levels of symbolic links
Pues este error también puede aparecer con la misma combinación de impresora y controladores del punto anterior y es debido a un error en el paquete RPM original. En la fase de post instalación aparece el siguiente fragmento:
if [ -d /usr/lib64/cups ]; then
if [ -d /usr/lib/cups ]; then
cd /usr/lib/cups/backend
ln -sf ../../../lib64/cups/backend/cnusb cnusb
cd /usr/lib/cups/filter
ln -sf ../../../lib64/cups/filter/pstoufr2cpca pstoufr2cpca
fi
fi
Lo que produce, cuando se instala en una distribución Debian de 64 bits en la que /usr/lib64 direcciona a /usr/lib, un enlace simbólico que se apunta a si mismo recursiva y definitivamente.
La solución, sin parchear el paquete, consiste en extraer el ejecutable directamente del mismo con algo como
$ rpm2cpio cndrvcups-ufr2-uk-2.20-1.x86_64.rpm | cpio -i --make-directories "*/pstoufr2cpca" $ ls -la usr/lib64/cups/filter/pstoufr2cpca -rwxr-xr-x 1 victor victor 35458 jun 7 14:15 usr/lib64/cups/filter/pstoufr2cpca $
y copiarlo en el directorio de filtros de CUPS borrando antes el erróneo enlace simbólico.
using invalid Host
- Ref: CUPS printing error
Cuando aparece este error en los registros con una respuesta del servidor código 400 puede deberse a que el servidor está comprobando la cabecera Host de la petición HTTP y ésta no coincide con ninguno de los nombres a los que responde.
Para ello es cuestión de indicarle en la configuración (/etc/cups/cupsd.conf
) los alias por los que se puede conocer dicha máquina en la red:
ServerAlias example.net
ServerAlias printers.example.net
ServerAlias cups.example.net
hplip error de comunicación 5012
Este caso se me ha dado en una instalación Ubuntu con una impresora HP OfficeJet 6700 Premium pero por lo que he podido leer también se da en otros modelos HP.
El problema consiste en que la impresora tiende a cortar la comunicación y no es posible restablecerla de forma sistemática. Sigue estando en la configuración, se le pueden enviar trabajos, pero el controlador informa machaconamente sobre un fallo de comunicación (con el código 5012) y ya no hay vuelta atrás.
La solución la he encontrado en varios sitios pero especialmente en las notas de versión del propio programa de configuración de impresoras hp-setup. Cuando se da de alta la impresora a través suyo es necesario ir a las opciones avanzadas y seleccionar el uso de SLP para descubrirlas en la red. El efecto más visible después de ésto es la aparición de la dirección IP de la impresora (por lo que es muy posible que sea necesario darle una dirección estática) junto a su URL.
hp:/net/Officejet_6700?ip=192.168.1.102
Para este programa el valor predeterminado es emplear mDNS y ese parece ser el origen de los cortes de conexión.
job_originating_user_name
- Ref: KeyError: 'job-originating-user-name'
- Ref: cups: joblist displays {job_originating_user_name} instead of real username
Si en el interfaz web no aparecen títulos ni autores de los trabajos se debe retocar la configuración puesto que los valores predeterminados a partir de la versión 1.5 de CUPS no lo permiten.
Cambiar el valor predeterminado a none en el archivo /etc/cups/cupsd.conf
:
<Policy default> JobPrivateAccess default JobPrivateValues none ... </Policy>
<Policy authenticated> JobPrivateAccess default JobPrivateValues none ... </Policy>
Recetario
Obteniendo información del servidor
La mayoría de los programas administrativos actúan sobre o con el servidor local, aunque hay algunos que pueden usarse a distancia. En este caso se debe especificar el servidor, el puerto y -tal vez- el nombre del usuario.
Dispositivos disponibles
Para obtener los dispositivos a disposición de un servidor CUPS se puede emplear el programa lpinfo de esta forma:
# lpinfo -h example.com -v network socket network beh file cups-pdf:/ network http network ipp network lpd direct parallel:/dev/lp0 network smb
Si se necesitan más información se usa el parámetro -l de esta forma:
# lpinfo -h example.com -l -v ... Dispositivo: uri = beh clase = network info = Backend Error Handler marca y modelo = Unknown id dispositivo= ubicación = Dispositivo: uri = cups-pdf:/ clase = file info = CUPS-PDF marca y modelo = Virtual PDF Printer id dispositivo= MFG:Generic;MDL:CUPS-PDF Printer;DES:Generic CUPS-PDF Printer;CLS:PRINTER;CMD:POSTSCRIPT; ubicación = ...
Controladores disponibles
Para obtener una lista de los controladores disponibles en un servidor CUPS se puede emplear el programa lpinfo de esta forma:
# lpinfo -h example.com -m ... foomatic:Alps-MD-5000-md5k.ppd Alps MD-5000 Foomatic/md5k foomatic:Alps-MD-5000-md50Eco.ppd Alps MD-5000 Foomatic/md50Eco ...
aunque hay que tener en cuenta que su número puede alcanzar fácilmente varios miles. Si se desea ampliar más la información se debe emplear el parámetro -l:
# lpinfo -h example.com -l -m ... Modelo: nombre = foomatic:Brother-DCP-1200-hl1250.ppd natural_language = en make-and-model = Brother DCP-1200 Foomatic/hl1250 (recommended) device-id = DRV:Dhl1250,R1,M0,TG; Modelo: nombre = foomatic:Brother-DCP-1200-ljet2p.ppd natural_language = en make-and-model = Brother DCP-1200 Foomatic/ljet2p device-id = DRV:Dljet2p,R0,M0,TG; ...
Administrando impresoras desde consola
Empleando el programa lpadmin se pueden efectuar muchas tareas de administración de impresoras. Este programa está en la ruta habitual del administrador /usr/sbin
pero como puede ser empleado por usuarios no administrativos incluye un mecanismo de autentificación para usuarios:
Parámetro | Valor | Descripción |
---|---|---|
-E | Desactivado | Activa el cifrado de comunicaciones con el servidor CUPS siempre que se emplee antes de las opciones -d, -p o -x. |
-U usuario | $USER | Usuario que conecta con el servidor. |
-h servidor[:puerto] | localhost:631 | Nombre del servidor y -opcionalmente- número de puerto con el que conectar. |
Algunas notas:
- En las siguientes subsecciones voy emplear el término impresora como análogo a cola de impresión.
- A menos que diga otra cosa todos los programas administrativos aceptan los parámetros de conexión arriba mencionados.
Cola de impresión predeterminada
Para establecer una cola de impresión como predeterminada de forma global se puede emplear:
$ /usr/sbin/lpadmin -U victor -h lp -d laser_color ¿Contraseña de victor en lp? XXXXXX $
Parando una impresora
Existe un programa específico para detener la impresión en una cola o en una clase de impresoras al completo llamado cupsdisable, aunque puede encontrarse bajo el nombre disable en los sistemas operativos System V.
/usr/sbin/cupsdisable [opciones] destino_o_destinos_a_detener
Los parámetros pueden ser:
Parámetro | Valor | Descripción |
---|---|---|
-c | Desactivado | Cancela todos los trabajos pendientes en el destino. |
--hold | Desactivado | Mantiene retenidos los trabajos restantes en la impresora. Es útil para permitir que el trabajo actual se complete antes de detener la impresora. |
-r razón | Reason Unknown | Pega un mensaje de texto al estado de la impresora y se supone que debe servir para explicar el motivo de la detención. |
Ejemplo:
$ /usr/sbin/cupsdisable -U victor -h lp ml2855 ¿Contraseña de victor en lp? $ lpstat -h lp -p ml2855 la impresora ml2855 está deshabilitada desde sáb 16 abr 2011 13:08:01 CEST - Paused
Poniendo en marcha una impresora
Para poner en marcha la impresora se debe emplear el programa cupsenable, que también tiene su análogo en System V como enable, y que se emplea así:
$ /usr/sbin/cupsenable [opciones] destino_o_destinos_a_poner_en_marcha
Los parámetros son en este caso:
Parámetro | Valor | Descripción |
---|---|---|
-c | Desactivado | Cancela todos los trabajos pendientes en el destino. |
--release | Libera todos los trabajos retenidos por el parámetro --hold en una llamada anterior al programa cupsdisable. |
Impresión sin filtros (raw)
Cuando en un servidor CUPS es necesario habilitar la impresión sin filtrado debe tocarse lo siguiente en la configuración:
- Aceptar el tipo genérico octect-stream añadiendo la siguiente línea en el archivo
/etc/cups/mime.types
:application/octet-stream
- Especificar lo que hacer cuando se tenga que imprimir contenido genérico con la siguiente línea en el archivo
/etc/cups/mime.convs
:application/octet-stream application/vnd.cups-raw 0 -
Estas disposiciones, sin embargo, tan sólo permiten la impresión sin filtrado y no obligan a que sea así para todos los trabajos. Suelen situarse al final del archivo mime.convs para que sirvan como último recurso antes de retornar un error de conversión.
Añadiendo filtros a CUPS
Antes de plantearse la modificación del mecanismo MIME o de la tabla mime.convs conviene asegurarse de que ambos no sean sobreescritos en cada actualización del sistema.
Como CUPS emplea todos los archivos con extensión .types
y .convs
que encuentra en su directorio de configuración (/etc/cups
en Debian) conviene añadir nuestros cambios a archivos con otro nombre que podamos tener bajo nuestro control, como por ejemplo:
local.types local.convs
Una vez que CUPS carga todos los archivos los ordena alfabéticamente, por lo que es posible que surjan dificultades en el filtrado si las definiciones se solapan.
Filtros de impresión
Enlaces y referencias
- Ref: CUPS API filter
- Ref: CUPS design description
Introducción
Estas notas hablan sobre el interfaz común que emplea CUPS para convertir los trabajos de impresión a formatos imprimibles y sirven de base para crear nuevos filtros o modificar alguno de los existentes.
Estos son algunos conceptos que se manejan en las siguientes secciones:
- Planificador
- Es el programa central de CUPS (
cupsd
), el encargado de recibir órdenes y trabajos de impresión y ponerlos en marcha. - Cadena de filtros
- Es la formada por los programas de filtrado cuyo número y función varía de acuerdo al formato original del trabajo de impresión y al formato final que acepta la impresora.
- Sólo el primero de ellos recibe el archivo a imprimir, los demás trabajan con su canal estándar de entrada (
STDIN
) y todos ellos escriben su resultado en el canal estándar de salida (STDOUT
). - Backend
- Es el nombre que recibe el controlador de la impresora, aquél programa situado al final de la cadena de filtrado que se comunica con el dispositivo final y tanto le envía datos para imprimir como lee su estado y se lo transmite al planificador.
Seguridad
CUPS hace funcionar a los filtros bajo una cuenta de usuario con muy pocos privilegios -generalment el usuario lp- y sin conexión alguna con el escritorio gráfico del usuario. Intenta lo mismo con los controladores de impresión (los backend) pero eso no siempre es posible dado que tienen que leer y escribir de y hacia los dispositivos físicos.
Además toma la precaución extra de sólo aceptar que estos programas estén instalados en el sistema bajo la propiedad de root y sin permisos de escritura para el grupo ni el resto del mundo. Los permisos aconsejables son 0555 para filtros y controladores normales, 0500 para controladores que deban funcionar bajo la cuenta de 'root' y 0755 o 0555 para el directorio que los contiene, además de ser propiedad también del superusuario.
Los archivos temporales deberían crearse en el directorio indicado por la variable de entorno TMPDIR.
Filtrando
CUPS invoca los filtros de impresión pasándoles un juego de parámetros y procura que el primero (argv[0]
) sea el nombre de la impresora que también puede obtenerse de la variable de entorno PRINTER
.
filter job user title num-copies options [filename]
Parámetro | Descripción |
---|---|
job | Número de identificación del trabajo. |
user | Nombre del usuario que lanza el trabajo. |
title | Título que debe aparecer en la relación de trabajos. |
copies | Número de copias a imprimir. Este parámetro lo reciben todos los filtros pero en general sólo debería generar copias el filtro que recibe el nombre del archivo a imprimir. |
options | Opciones adicionales |
filename | Archivo con los datos a procesar sólo si es el primer filtro de la cadena. |
Los filtros de impresión deben salir con el código cero (0) si han funcionado bien (y han generado datos para imprimir) o el código uno (1) si se han encontrado con algún error.
Para comunicar información al planificador de impresión los filtros deben utilizar el canal estándar de error (STDERR
) enviando una línea de texto por cada mensaje con el siguiente prefijo y formato:
Prefijo | Descripción |
---|---|
|
Cambia el atributo printer-state-message y añade el mensaje al registro de errores con el nivel determinado por el prefijo. El nivel ERROR debería emplearse para aquellos errores no persistentes. |
ATTR: atribute=value [...] | Define los atributos de la impresora o del trabajo de impresión. |
|
Añade una entrada al registro de páginas impresas. La primera forma además añade el número de copias (#-copies) al atributo job-media-sheets-completed mientras que la segunda hace lo propio con el número de páginas (#-pages). |
PPD: keyword=value [...] | Cambia o añade palabras clave al archivo ppd de la impresora. |
STATE: [+-] printer-state-reason [...] |
Define, añade o elimina palabras clave del atributo printer-state-reason de la cola de impresión actual. Se emplea normalmente con condiciones de error o de configuración en aspectos como el papel (agotado, erróneo, atasco ... ), tinta o tóner, conectividad de la impresora y otros sucesos como puertas abiertas o bandejas inexistentes. |
Variables de entorno
CUPS define un conjunto variables de entorno cuando ejecuta filtros de impresión y programas controladores (backend); estas son algunas y el resto puede encontrarse en la documentación.
Variable | Descripción |
---|---|
CHARSET | Codificación empleada en el trabajo (normalmente utf-8). |
CONTENT_TYPE | El tipo MIME del contenido a imprimir. |
CUPS_FILETYPE |
Tipo de archivo CUPS a imprimir:
|
DEVICE_URI | Identificador del dispositivo. |
LANG | Lenguaje asociado al trabajo (locale). |
PPD | Ruta completa del archivo PPD asociado al trabajo. |
PRINTER | Nombre de la cola de impresión. |
TMPDIR | Directorio donde deben crearse los archivos temporales. |
Probando los filtros
Para probar el funcionamiento de los filtros sin llegar a imprimir nada se puede usar el programa cupsfilter con las opciones adecuadas. Precisa de un archivo con los datos a imprimir y se puede cambiar el resultado indicando el tipo MIME final que necesitamos. El valor predeterminado para este parámetro es application/pdf por lo que la salida final es un archivo PDF.
Las opciones más útiles desde mi punto de vista son:
Parámetro | Valor predeterminado | Descripción |
---|---|---|
-m mime/type | application/pdf | Tipo MIME final |
-j jobid[,N] | Ninguno | Toma la página N o la primera si se omite del trabajo de impresión número jobid como datos de entrada. Para que esto funcione es necesario el servidor de impresión guarde un registro de los trabajos con su contenido.
MaxJobs 50 PreserveJobFiles Yes |
Un ejemplo de proceso con un filtro personalizado (llamado recibos2cups) sería la siguiente:
# /usr/sbin/cupsfilter -j 66852 > /tmp/recibos.pdf DEBUG: argv[0]="cupsfilter" DEBUG: argv[1]="1" DEBUG: argv[2]="root" DEBUG: argv[3]="06c7e4f153847" DEBUG: argv[4]="1" DEBUG: argv[5]="" DEBUG: argv[6]="/tmp/06c7e4f153847" DEBUG: envp[0]="<CFProcessPath>" DEBUG: envp[1]="CONTENT_TYPE=application/recibos" DEBUG: envp[2]="CUPS_DATADIR=/usr/share/cups" DEBUG: envp[3]="CUPS_FONTPATH=/usr/share/cups/fonts" DEBUG: envp[4]="CUPS_SERVERBIN=/usr/lib/cups" DEBUG: envp[5]="CUPS_SERVERROOT=/etc/cups" DEBUG: envp[6]="LANG=es_ES.UTF8" DEBUG: envp[7]="PATH=/usr/lib/cups/filter:/usr/bin:/usr/sbin:/bin:/usr/bin" DEBUG: envp[8]="PPD=/usr/share/cups/model/laserjet.ppd" DEBUG: envp[9]="RIP_MAX_CACHE=auto" DEBUG: envp[10]="USER=root" INFO: recibos2cups (PID 27775) started. INFO: pstopdf (PID 27776) started. DEBUG: pstopdf 5 args: 1 root 06c7e4f153847 1 DEBUG: PPD: /usr/share/cups/model/laserjet.ppd DEBUG: reading from /tmp/06c7e4f153847 DEBUG2: job header: ### multibase ### recibos : charset=latin1 csv DEBUG2: Recibo: 138/1 DEBUG2: Recibo: 139/1 DEBUG2: Recibo: 140/1 DEBUG2: Recibo: 141/1 DEBUG2: Recibo: 142/1 INFO: Job completed successfully INFO: recibos2cups (PID 27775) exited with no errors. DEBUG: Resolution: DEBUG: Page size: DEBUG: Width: , height: , absolute margins: 0, 0, , DEBUG: Relative margins: , , , DEBUG: PPD options: DEBUG: PostScript to be injected: DEBUG: Running cat | /usr/bin/gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dCompatibilityLevel=1.3 -dAutoRotatePages=/None -dAutoFilterColorImages=false -dNOPLATFONTS -dPARANOIDSAFER -sstdout=%stderr -dColorImageFilter=/FlateEncode -dPDFSETTINGS=/printer -dColorConversionStrategy=/LeaveColorUnchanged -dDoNumCopies -sOutputFile=- -c .setpdfwrite -f - INFO: pstopdf (PID 27776) exited with no errors. #
En el texto anterior he resaltado el tipo detectado por CUPS, que confirma que al menos se ha reconocido correctamente el contenido, los dos filtros que encadena para obtener la salida final en PDF y el usuario, que aunque es el administrador no es absolutamente necesario ejecutarlo como tal para probar.
Impresoras virtuales
Dada la forma en la que trabaja CUPS añadirle una impresora virtual es bastante factible. Una de las más conocidas es precisamente la que produce documentos en formato PDF.
Configuración
Todo el software necesario para producir documentos PDF de cualquier impresión se encuentra en el paquete cups-pdf así que lo primero es instalarlo (lo que ya reinicia el servidor cups) y después:
- Crear una impresora virtual en el servidor:
- Asignarle el puerto de impresión (ó backend) cupspdf:/
- Seleccionar como modelo de impresora Generic/PostScript Color.
- Revisar la configuración en /etc/cups/cups-pdf.conf para adaptarla a nuestro entorno, especialmente las siguientes variables:
- Grp: grupo de trabajo al que cambiar nada más comenzar la ejecución.
- Spool: ruta de trabajo, que ya está creada por el paquete.
- Out: ruta donde situar el documento PDF final.
- AnonDirName: ruta donde guardar los PDF sin usuario reconocible ó asignados a nobody.
- GhostScript: en la rama Etch de Debian el paquete cupsys depende de gs-esp, pero los documentos PDF no se construyen correctamente, en concreto desaparece la capa de texto, por lo que no se puede buscar texto dentro de ellos. Para solucionarlo basta con instalar el paquete gs-gpl, que puede coexistir perfectamente con gs-esp, y cambiar esta variable a /usr/bin/gs-gpl.
- GSTmp: este directorio debe existir y tener permisos de escritura al menos para el grupo indicado en Grp.
- GSCall: si empleamos papel en tamaño A4 es muy conveniente añadir la opción -sPAPERSIZE=a4 a esta variable, puesto que tanto gs-esp como gs-gpl parecen usar Letter como valor predeterminado. Al menos eso es lo que he podido sacar en claro tras echarle un vistazo al archivo de definiciones gs_statd.ps donde se indica que la familia de tamaños por defecto corresponde a US y no a Europa.
Juegos de caracteres
Este aspecto es necesario tenerlo en cuenta únicamente para los nombres de los documentos PDF generados, sobre todo si proceden de entornos Windows, porque al final nos podemos encontrar con un buen número de documentos untitled o con frases extrañas dada la sustitución masiva de caracteres internacionales.
La variable DecodeHexStrings (0) de la configuración permite que se empleen caracteres UTF-8 en los documentos y que se reconozcan como tales. Si está desactivado (con valor cero o no definida) una página web con un título como RHD299H LG : LG España podría resultar en un nombre de archivo como:
job_57281-RHD299H_LG___LG_Espa__a.pdf
en lugar de
job_57276-RHD299H_LG___LG_España.pdf
y es sólo un ejemplo fácil, otros títulos quedan irreconocibles.
Envío por correo electrónico
El paquete cups-pdf incluye entre sus ejemplos un programa capaz de realizar esta tarea, muy flexible y bien pensando. Su autor es Nickolay Kondrashov, está escrito en Perl y bajo licencia GPL.
La instalación y puesta en marcha puede realizarse de esta forma:
- Instalar el programa cups-pdf-dispatch en un directorio de ejecutables como /usr/local/bin.
- Situar el archivo de configuración cups-pdf-dispatch.conf en /etc/cups y asegurarse de que tiene permisos de lectura para todo el mundo, puesto que el programa es invocado como el usuario final que recibe el documento.
- Ajustar la configuración como describo más abajo.
- Incluir este programa en el funcionamiento de cups-pdf, añadiéndolo en la opción PostProcessing de su configuración.
El archivo de configuración también está escrito en lenguaje Perl por lo que debe ser sintácticamente correcto. En una primera aproximación, y para ponerlo en marcha rápidamente, debemos modificar las siguientes variables:
$CHARSET | contiene el juego de caracteres que se emplea para el mensaje de correo. |
$FROM_MAILADDR | es la dirección de remite del mensaje. |
$MAX_ATTACHMENT_SIZE | está establecido en 5 Mb, pero puede ser necesario retocarlo porque si se sobrepasa el mensaje no se envía. |
$TRY_STRIP_JOB_IDS | activa el intento de supresión del prefijo que tanto Samba como cups-pdf realizan. |
$REMOVE_SENT | determina si los archivos se borran tras enviarlos por correo. |
$GET_USER_MAILADDR_SUB | es una función Perl que recibe como parámetro el nombre del usuario y debe retornar la dirección de correo electrónico a la que enviar el mensaje. |
La última variable que menciono es la más compleja de todas puesto que se trata de código Perl y son necesarias nociones de programación en este lenguaje.
Mi idea es que los archivos, una vez creados en la carpeta del usuario, sean remitidos por correo electrónico al usuario siempre que éste así lo disponga. Y la manera de hacerlo es crear un archivo en su carpeta llamado mailto conteniendo en una línea su dirección de correo; en caso de que este archivo no exista, ó el contenido esté comentado, no se enviará ningún mensaje. El programa de Nickolay ya ha pensando en esto, y termina sin errores en caso de no tener una dirección de envío.
Las modificaciones al archivo pueden verse aquí:
# cups-pdf-dispatch.conf
# Configuration file for cups-pdf-dispatch.
# This file is interpreted by perl.
# $GET_USER_MAILADDR_SUB
# Reference to a function which converts username to e-mail address.
# Arguments: username
# Returns: e-mail address
#
my $_user_realname = undef;
use locale;
$GET_USER_MAILADDR_SUB =
sub {
my $username = shift;
my $filename = "/var/spool/pdf/${username}/mailto";
my $mailto = undef;
if (-r $filename) {
if (open(MAILTO,"< $filename")) {
while (<MAILTO>) {
chomp;
next if /^#/;
if (not $mailto) {
$mailto = $_;
last;
}
}
close(MAILTO);
}
else {
warn "could not open ${filename}: $!";
}
}
else {
$mailto = sprintf("%s@%s", $username, hostname());
}
if ($mailto =~ m{^([^<]+)\s<([^>]+)>}) {
$_user_realname = $1;
$mailto = $2;
}
return $mailto;
};
# $GET_USER_REALNAME_SUB
# Reference to a function which converts username to user's realname (used
# when constructing To: header).
# Arguments: username
# Returns: user's real name
#
$GET_USER_REALNAME_SUB =
sub {
my $username = shift;
if (defined $_user_realname) {
return $_user_realname;
}
else {
# (i.e. user's real name from gecos)
return (split( /,/, (getpwnam($username))[6], 2 ))[0];
}
};
Es necesario advertir que el código es insertado en el propio fuente mediante el operador
eval
, por lo que se puede definir variables y funciones si se toman las debidas precauciones para no colisionar con el espacio de nombres del programa.
Para comprobar que lo escrito sea sintácticamente correcto siempre podemos usar el intérprete Perl para ello:
$ perl -cw /etc/cups/cups-pdf-dispatch.conf Name "main::GET_USER_MAILADDR_SUB" used only once: possible typo at cups-pdf-dispatch.conf line 106. Name "main::REMOVE_SENT" used only once: possible typo at cups-pdf-dispatch.conf line 83. ... cups-pdf-dispatch.conf syntax OK $
los avisos que vemos nos indican, precisamente, que esas variables han tomado un valor, así que a menos que estemos usándolo con el programa no debemos preocuparnos.
Y así, el archivo mailto puede contener algo como lo siguiente:
# # Dirección de correo a la # que enviar los documentos PDF # Víctor Moral <victor@taquiones.net>
Esquema de funcionamiento
cups-pdf, como cualquier backend de CUPS recibe los siguientes parámetros cuando es invocado
job user title num-copies options [ filename ]
numerados desde el uno al seis, puesto que el primero es siempre el nombre del programa.
Lo siguiente es un detalle de su funcionamiento en el que intento resaltar los puntos clave para depurar errores, puesto que casi siempre son achacables a permisos de acceso.
Los valores por defecto que se incluyen en el paquete Debian los transcribo entre paréntesis.
- Prepara el entorno de trabajo:
- Cambia el grupo del proceso indicado por la variable Grp de la configuración (lp).
- Intenta identificar el usuario que envía el trabajo en el archivo /etc/passwd.
- Si lo consigue y es la primera vez, crea un directorio con su nombre en la localización donde almacenar los trabajos, determinado por la variable Out (/var/spool/cups-pdf/${USER}), y le asigna los permisos a ese mismo usuario. Esta es la principal razón de que sea necesario que funcione como root.
- Si no lo consigue emplea el directorio para usuarios anónimos definido en la variable AnonDirName (/var/spool/cups-pdf/ANONYMOUS) y establece como usuario activo al indicado en la variable AnonUser (nobody).
- Crea el archivo PDF:
- Añade un archivo al directorio de trabajo, definido en la variable Spool (/var/spool/cups-pdf/SPOOL), y le asigna el identificador del usuario propietario del trabajo.
- Copia en ese archivo la fuente de entrada, bien leyendo de la entrada estándar, bien leyendo de un archivo recibido como último parámetro (el sexto).
- Obtiene y prepara un nombre para el documento final.
- Prepara una orden para invocar al programa
gs
con los parámetros necesarios para crear el documento PDF directamente en la carpeta final del usuario, para lo que se escinde en un proceso hijo en el cual:- Cambia en el entorno la variable TMPDIR con el valor de la configuración de GSTmp (/var/tmp).
- Cambia el GID y el UID del proceso a los que correspondan con el usuario propietario del trabajo.
- Ejecuta el programa
gs
con el contenido de la variable GSCall y los parámetros reunidos anteriormente. - Cambia los permisos del archivo PDF empleando el valor de UserUMask (0077) como máscara.
- Si la variable PostProcessing contiene algún valor se toma como la ruta de un programa al que llamar en este momento con los siguientes parámetros:
- Ruta al archivo PDF.
- Nombre del usuario tal y como lo ha determinado anteriormente.
- Nombre del usuario tal y como lo ha recibido él como parámetro (el segundo).
- El proceso padre espera a que te termine la creación del proceso hijo y:
- Borra el archivo de la cola de trabajo (/var/spool/cups-pdf/SPOOL).
- Libera memoria
Nombre del documento PDF
El nombre final del documento final es muy importante, y cups-pdf tiene varias formas de componerlo.
- Durante la creación del archivo de trabajo en la cola, el programa efectúa una copia línea a línea del texto y, saltándose los bloques de PostScript encapsulado que pueda contener, intenta localizar una meta variable llamada %%Title y leer su contenido.
- El tercer parámetro que recibe, si contiene algo, puede ser también el título.
¿ Cómo seleccionar uno ú otro en caso de que existan los dos ? Si la variable TitlePref (0) contiene el valor cero tiene preferencia el título encontrado en el documento, mientras que si el valor es uno la preferencia la tienen los parámetros.
Una vez elegido el texto del título el programa lo completa de esta forma:
- Elimina algunos caracteres especiales: salto de línea y retorno de carro.
- Si la variable DecodeHexStrings tiene el valor 1 se intenta decodificar los textos hexadecimales para permitir títulos internacionales.
- Elimina, si los encuentra, cualquier carácter de apertura y cierre de paréntesis.
- También borra cualquier carácter separador de directorios (/).
- Quita la extensión del archivo.
- Por último limpia otros caracteres especiales, dependiendo del valor de DecodeHexStrings:
- Si tiene el valor cero, reemplaza caracteres especiales, no mostrables según el código ASCII con guiones bajos (_).
- Si tiene el valor uno, hace lo mismo pero llama a las funciones isalnum() y isascii() en lugar de comparar numéricamente cada carácter.
Al terminar el proceso anterior podemos tener el título limpio de caracteres extraños o totalmente vacío. En el primer caso se le añade un prefijo al nombre del archivo con el texto job_ y el número de trabajo de impresión (recibido como primer parámetro), sólo si la variable Label tiene el valor uno. En el segundo caso, cuando no hay título como tal, se forma uno con el prefijo antes citado, el número de trabajo y el literal untitled_document.
Administración
Interfaz web
La directiva ServerAlias es importante para permitir acceder al panel web desde el resto de la red.
ServerAlias matraz.home
cupsctl
Este programa permite ajustar las opciones de configuración de un servidor CUPS desde la consola. Sin ningún parámetro conecta con el servidor local y recupera la configuración. Si para cualquier operación precisa credenciales las solicita en caso de no disponer de ellas.
# cupsctl _debug_logging=0 _remote_admin=1 _remote_any=0 _remote_printers=1 _share_printers=1 _user_cancel_any=0 BrowseLocalProtocols=CUPS dnssd BrowseRemoteProtocols=CUPS DefaultAuthType=Basic MaxLogSize=0 SystemGroup=lpadmin
Los parámetros comunes son:
-E
habilita el cifrado en las comunicaciones con el servidor.-U nombre
define el nombre de usuario con el que conectar.-H servidor[:puerto]
dirige la consulta o la modificación hacia el servidor indicado.