简介:cups-2.3.3-source.tar.gz 是 CUPS 2.3.3 的源码压缩包,面向 Linux/Unix 系统管理员、打印系统开发者以及需要定制打印方案的技术人员,可用于本地编译、部署和二次开发。包内共 1294 个文件,整体约 7.76MB,主要由 512 个模板文件、201 个 C 源码、81 个 HTML 页面、73 个头文件、55 个测试脚本及 configure、Makefile、man 手册、驱动与策略配置、国际化资源构成,覆盖构建、服务端、过滤器、文档和测试模块。已有 928 人学习,说明其在打印系统运维与源码研究场景中具有参考价值。借助这套源码,读者可梳理 IPP 打印作业处理、过滤器驱动模型、Web 管理接口与 SSL/TLS 传输安全等实现脉络,并依据 src、docs、include、policies、etc、tests 等目录开展定制编译、功能裁剪、驱动适配和问题排查;同时可结合 IPP、LPD、AirPrint 等协议支持与加密传输机制,按目录定位源码模块,适合作为理解 CUPS 架构、实践打印解决方案与二次开发的基础材料。
1. 一台打印机接五台机器:cups-2.3.3 源码包到底解决什么问题
办公室里最典型的场景是这样:一台支持网络的打印机,五台机器要打,Windows、Linux、macOS 混着来,谁装驱动谁崩,最后只有一个人能打印。cups-2.3.3-source.tar.gz 就是把这个局面收掉的原料——它是 CUPS 2.3.3 的完整源码包,不是发行版打好的二进制包,编译出来能得到 cupsd 调度器、lp/lpadmin/lpstat 一整套客户端、过滤器链和 socket/usb/ipp 各个后端。
源码包的价值在于可控:版本锁死在 2.3.3,不跟着系统包管理器漂移;模块可以裁,dnssd、libusb 不要就直接关掉;装到独立前缀里,和系统自带的 CUPS 客户端井水不犯河水。适合三类人——内网要搭统一打印入口的运维、还在跑老发行版又得让新打印机出纸的工程师、以及需要把打印服务塞进嵌入式镜像的人。
2. 拆开调度器与过滤器链:一次打印请求在 CUPS 2.3.3 里走过的四段路
动 configure 之前先把请求路径理顺,后面排错才不会靠猜。CUPS 不是“装个驱动就能打”的模型,它是一个常驻的 HTTP 服务加一条流水线,任何一次lp提交都要穿过四段:调度器接单、MIME 转换、过滤器加工、后端出纸。搞不清这条链路,日志里那些Job 42、filter failed、backend exited with status 1就全是黑匣子。
2.1 631 端口背后是谁:cupsd 的三个职责
cupsd 是唯一常驻进程,它同时干三件看起来不搭的事。第一件是 IPP 服务器,监听 631,处理 Print-Job、Get-Printer-Attributes、Cancel-Job 这些标准操作,lp、lpstat、图形界面、手机端的打印入口,本质上都是往这个端口发 IPP 请求。第二件是作业调度器,接单后把作业元数据写进/var/spool/cups/c00042,控制文件写c00042,数据文件写d00042,然后按队列状态决定什么时候把它推下去。第三件是配置与权限中心,/etc/cups/cupsd.conf里的<Location>段决定谁能从哪个网段提交、谁能进管理页、谁能改队列。
作业在队列里有明确的状态机:pending → processing → completed,中间任何一步卡住都对应一个具体原因。pending长时间不动,通常是队列被cupsdisable停用或者后端不可达;processing挂着不走,多半是过滤器在等某个不存在的输入格式,或者后端在等一个永远不回话的 TCP 连接。习惯上我第一眼看lpstat -t,它一次把调度器状态、队列状态、默认目标、当前作业全列出来,比翻日志快。
还有一点容易被忽略:cupsd 是单进程多线程模型,每个作业的处理线程里跑过滤器子进程。这意味着一个卡死的过滤器不会直接拖垮整个服务,但会占住一个作业槽位。MaxJobs和MaxJobsPerPrinter就是给这种情况兜底的,默认值偏宽松,长期跑的打印服务器建议显式设一下。
2.2 过滤器链与 MIME 转换:PDF 是怎么变成打印机听得懂的话
CUPS 最反直觉的设计是:客户端永远只负责“描述我要打什么”,不负责“翻译成打印机语言”。你lp一个 PDF,cupsd 收到的就是 PDF,怎么变成 PCL、PostScript 或者位图,是服务端按/etc/cups/mime.types和/etc/cups/mime.convs两张表现场决定的。
mime.types定义格式识别规则,比如application/pdf pdf,靠文件头魔数判断;mime.convs定义转换边,比如application/pdf application/vnd.cups-postscript 66 pdftops,意思是从 PDF 到 CUPS 内部 PostScript 格式,代价 66,用pdftops过滤器。cupsd 拿着一堆转换边跑最短路,拼出一条从“输入类型”到“队列 PPD 声明的类型”的过滤器链。链上任何一个环节缺失,作业就会以filter failed或Unsupported format收场。
2.3.3 的filter/目录里,常用的几个角色是:pdftops把 PDF 转 PS,pdftopdf做页面级处理(多页合一、双面、缩放),pstops处理 PostScript 页面顺序和份数,rasterto*系列把 CUPS 光栅格式转成各家打印机能吃的位图,commandtops处理打印机控制命令,gziptoany负责解压。后端在backend/里,socket走裸 TCP(9100 端口那套)、ipp走 IPP、usb走 libusb、lpd走老式 LPD 协议、dnssd做服务发现。
这条链路带来的最大好处是驱动可以很薄。只要打印机支持 PostScript 或 PCL,一个通用 PPD 就能覆盖一大片机型;只要打印机支持 IPP 2.0 以上,甚至可以不装 PPD,直接走无驱动路径。反过来,链路也是故障高发区——报错信息往往只有一句filter failed,真正的错误藏在过滤器的 stderr 里,得把LogLevel提到debug2才看得见。
2.3 源码树目录与构建产物的对应关系
解压后先别急着 configure,花两分钟把目录和最终落盘位置对上,后面调参数会顺很多。
| 源码目录 | 内容 | 安装后大致位置 |
|---|---|---|
scheduler/ | cupsd 主程序、配置解析、作业调度、IPP 处理 | $prefix/sbin/cupsd |
cups/ | 客户端库与 lp/lpadmin/lpstat/lpinfo/ipptool | $prefix/bin/、$prefix/lib/libcups.so |
filter/ | pdftops、pdftopdf、pstops、rasterto* 等过滤器 | $prefix/lib/cups/filter/ |
backend/ | socket、ipp、usb、lpd、dnssd 各后端 | $prefix/lib/cups/backend/ |
ppd/ | PPD 文件解析与工具库 | 编进 libcups,供 lpadmin 使用 |
ppdc/ | PPD 编译器,把 .drv 源文件编成 PPD | $prefix/bin/ppdc、$prefix/sbin/ppdc |
notifier/ | 作业事件通知(dbus、mailto 等) | $prefix/lib/cups/notifier/ |
templates/、locale/ | Web 管理界面模板与多语言 | $prefix/share/cups/、$prefix/share/locale/ |
data/ | mime.types、mime.convs、默认 PPD | $prefix/share/cups/ |
看清这张表的意义在于:当你想裁模块时,知道关掉哪个 configure 选项会让哪一行消失。比如关掉 libusb,backend/usb就不会编出来,lpinfo -v里也就看不到direct usb://开头的设备,这在内网纯网络打印的环境里是完全合理的裁剪。
2.4 2.3.3 的构建选项:哪些必须改,哪些别碰
configure 的选项不少,但真正会改你部署方式的就那么几个。我一般先跑一遍帮助,把候选筛出来,而不是照抄别人的命令。
# 解压后先筛出与部署方式强相关的选项 tar -xzf cups-2.3.3-source.tar.gz cd cups-2.3.3 ./configure --help | grep -E 'prefix|sysconf|localstate|docdir|tls|dnssd|libusb|debug|shared|static'这条命令本身不构建任何东西,只是把 configure 脚本里的选项名和说明打出来。grep的五个关键词对应五类决策:装到哪(prefix 系)、加密走哪套(tls)、要不要服务发现(dnssd)、要不要 USB 直连(libusb)、要不要静态链接过滤器(shared/static)。
--prefix决定二进制和库的位置,我习惯装到/usr/local/cups-2.3.3这种带版本号的独立目录,好处是升级或卸载时直接删目录,不会和系统自带的 CUPS 客户端混在一起。代价是 PATH 里多一条,且lp、lpstat这些命令可能出现两个版本同时存在的情况,后面排错章节会专门讲这个坑。
--sysconfdir决定配置文件位置,默认是$prefix/etc,但那意味着所有配置散落在安装目录里。实际部署我固定给/etc/cups,和系统惯例一致,运维交接成本低。
--localstatedir决定“会变的数据”放哪,包括/var/spool/cups(作业暂存)、/var/log/cups(日志)、/var/run/cups(套接字与 pid 文件)。这个参数设成/var就对了,别设成$prefix/var,否则日志和 pid 会跑到安装目录里,systemd 单元和日志轮转脚本都得跟着改。
--with-tls取值决定用哪套 TLS 库,openssl和gnutls都行,取决于目标机器上哪个开发包更完整。--with-dnssd控制是否编译 DNS-SD 广播与发现,纯内网固定 IP 的场景关掉能少一堆依赖,但关掉之后lpinfo -v就看不到dnssd://的设备,移动端那种“自动发现打印机”的体验也没了。--disable-shared --enable-static会让过滤器和后端静态链接 libcups,好处是把$prefix/lib/cups/filter/整个目录拷到另一台机器就能跑,坏处是体积大,且升级 libcups 时必须全部重编。
3. 从 tar 包到 cupsd 常驻:依赖、configure 参数与首启验证
编译 CUPS 本身不难,难的是依赖不全时报错信息很绕,以及装完之后自启动那一环。这一章按“依赖怎么凑齐 → configure 怎么配 → cupsd 怎么起 → 起没起怎么验证”的顺序走一遍,命令都可以直接抄。
3.1 依赖怎么列:从 configure 报错反推
CUPS 的强依赖其实只有 C 编译器、make、zlib 和一套 TLS 库,其余大多是可选。但“可选”不等于“不需要”,缺了对应开发包,configure 会静默关掉功能,等你后面发现打印机发现不了、USB 打印机认不到时才回头补,反而更费时间。
我的做法是先按最常见的组合一次装齐,再根据 configure 输出里no的那几行做减法或加法。基于 deb 包管理的发行版:
# 编译工具链 + CUPS 常见可选依赖的开发包 sudo apt-get install -y \ build-essential pkg-config \ zlib1g-dev libssl-dev \ libpng-dev libjpeg-dev libfreetype6-dev \ libavahi-client-dev libusb-1.0-0-dev \ libpaper-dev基于 rpm 包管理的发行版:
# 对应的开发包命名略有差异,缺哪个补哪个 sudo dnf install -y \ gcc gcc-c++ make pkgconfig \ zlib-devel openssl-devel \ libpng-devel libjpeg-turbo-devel freetype-devel \ avahi-compat-libdns_sd-devel libusb1-devel \ libpaper-devel两段的对应关系是:zlib 提供gziptoany过滤器需要的压缩支持;openssl 给--with-tls=openssl用;png/jpeg/freetype 服务于光栅转换和横幅页渲染;avahi-client 或 dns_sd 给 DNS-SD 广播用;libusb 给usb后端用;libpaper 决定默认纸张尺寸,缺了会退回 Letter,A4 环境里这点很要命。
装完不要急着 configure,先确认两件事:pkg-config --version有输出,说明 pkg-config 在;ls /usr/include/openssl/ssl.h存在,说明 TLS 头文件到位。这两个是最容易漏的。
3.2 configure 与 make 的完整命令,参数逐条对目录
下面这套参数是我在独立前缀部署里用得最稳的一组,注释里标了每条落到哪个目录。
cd cups-2.3.3 ./configure \ --prefix=/usr/local/cups-2.3.3 \ --sysconfdir=/etc/cups \ --localstatedir=/var \ --with-docdir=/usr/local/cups-2.3.3/share/doc \ --with-tls=openssl \ --with-dnssd=avahi \ --enable-debug \ --disable-shared # 并行编译,核数按机器来;CUPS 的 makefile 对 -j 支持良好 make -j"$(nproc)" # 安装前先看一眼会往哪儿写文件,避免覆盖系统已有 CUPS sudo make install参数含义逐条说明。--prefix=/usr/local/cups-2.3.3把所有可执行文件放到$prefix/sbin和$prefix/bin,库放$prefix/lib,过滤器与后端放$prefix/lib/cups/{filter,backend}。--sysconfdir=/etc/cups让cupsd.conf、cups-files.conf、printers.conf、ppd/都落在系统标准位置,好处是运维和监控脚本不用改路径。--localstatedir=/var展开后是/var/spool/cups、/var/log/cups、/var/run/cups三处。--with-docdir单独指到安装目录里,避免文档和系统包管理器打架。--with-tls=openssl明确指定 TLS 实现,不让 configure 自己猜。--with-dnssd=avahi打开服务发现,局域网里设备能被自动列出。--enable-debug保留调试符号,出问题时能拿到有意义的栈。--disable-shared让过滤器和后端静态链接,便于后续整目录迁移。
提示:
make install之前先sudo make -n install | head -50看一眼安装清单,确认没有往/usr/bin或/usr/lib里塞东西。真混进系统路径,后面两套 CUPS 抢套接字的排查会很痛苦。
装完立刻核对产物是否齐全,这一步能挡住八成“装完了但用不了”的问题:
# 检查关键二进制、过滤器、后端是否都落盘 ls -l /usr/local/cups-2.3.3/sbin/cupsd ls /usr/local/cups-2.3.3/bin/{lp,lpstat,lpadmin,lpinfo,ipptool} ls /usr/local/cups-2.3.3/lib/cups/filter/ | head ls /usr/local/cups-2.3.3/lib/cups/backend/3.3 cupsd.conf 与 cups-files.conf 的最小可用组合
从 1.6 起 CUPS 把配置文件拆成了两个:cupsd.conf管服务行为与访问控制,cups-files.conf管路径、用户组、临时目录这些“启动前必须知道”的东西。两个文件缺一个,cupsd 直接拒绝启动。这是 2.3.3 上非常高频的一个翻车点,很多人只从旧文档里抄了cupsd.conf。
# /etc/cups/cupsd.conf —— 最小可用版本 LogLevel warn MaxLogSize 1m Listen 0.0.0.0:631 Listen /var/run/cups/cups.sock Browsing On DefaultAuthType Basic WebInterface Yes PreserveJobHistory Yes PreserveJobFiles No MaxJobs 200 MaxJobsPerPrinter 20 <Location /> Order allow,deny Allow @LOCAL </Location> <Location /admin> Order allow,deny Allow @LOCAL </Location> <Location /admin/conf> AuthType Default Require user @SYSTEM Order allow,deny Allow @LOCAL </Location>Listen 0.0.0.0:631让服务对局域网开放,不想对外就用Listen localhost:631加一个内网网段地址。Browsing On打开队列广播,配合前面编进去的 avahi 才能被自动发现。PreserveJobHistory Yes保留已完成作业的记录,做打印审计和计费必需;PreserveJobFiles No不保留作业数据文件,避免/var/spool/cups被撑爆——这两个参数一开一关是有意的。MaxJobs和MaxJobsPerPrinter限制并发,防止一个坏驱动把槽位全占满。
访问控制这里要特别留意语法年代。2.3.3 用的是Order allow,deny+Allow这套老式写法,@LOCAL表示“与服务器直接相连的网段”。如果从更新的配置样例里抄来Require all granted,在 2.3.3 上未必按预期生效,最典型的症状就是浏览器打开 631 一直 403。老版本就用老语法,别混着来。
# /etc/cups/cups-files.conf —— 路径与权限,缺了 cupsd 起不来 SystemGroup lpadmin User lp Group lp # 下面这些路径由 --localstatedir=/var 推导而来,一般不用改 # RequestRoot /var/spool/cups # ErrorLog /var/log/cups/error_logSystemGroup lpadmin决定哪些组的成员能用lpadmin改配置、能进/admin页面。装完之后记得确认这个组存在,并把运维账号加进去,否则所有管理操作都会卡在认证这一步。
3.4 首启验证:四个命令确认服务活着
启动之前先用-t做配置语法检查,这一步能省掉大量“启动失败但不知道为什么”的时间。
# -t 只校验配置不启动;-c 指定配置文件路径 sudo /usr/local/cups-2.3.3/sbin/cupsd -t -c /etc/cups/cupsd.conf没有输出就是通过。有输出会明确指到某个文件某一行,比如cups-files.conf缺字段、Listen端口被占用、目录不可写。校验通过再正式拉起:
# 前台启动便于看首行日志,调试期建议这么干 sudo /usr/local/cups-2.3.3/sbin/cupsd -c /etc/cups/cupsd.conf -f & # 确认端口在听 ss -lntp | grep 631 # 确认 IPP 服务有响应(会返回 HTTP 状态码) curl -sI http://127.0.0.1:631/ | head -1 # 确认调度器与队列状态 /usr/local/cups-2.3.3/bin/lpstat -t四条命令对应四个层面:进程起没起、端口听没听、HTTP 层通不通、IPP 层认不认。lpstat -t输出的第一段是scheduler is running,第二段是默认目标,后面是各队列状态。如果第一段就报scheduler is not running,别急着看应用日志,先回error_log顶部找第一条 FATAL,几乎都写在最前面几行。
4. 队列落地:lpinfo 探设备、lpadmin 建队列与过滤器链排错
服务起来了只是有了调度器,一个队列都没有的 CUPS 什么也打不了。这一章从“设备在哪”开始,一路做到“第一张纸出来”,中间把 PPD 的来源选择和过滤器链的观察方法讲透。
4.1 lpinfo -v 先看清楚设备在哪
建队列前先让 CUPS 自己报一遍它能看见什么,这比对着打印机手册猜 URI 靠谱得多。
# -v 列出可用设备及其 URI /usr/local/cups-2.3.3/bin/lpinfo -v输出大致分几类。direct usb://...是 USB 直连设备,能不能出现取决于usb后端是否编进去、当前用户是否有权限访问/dev/bus/usb。network socket://192.168.10.21:9100表示局域网里有个在 9100 端口裸监听 TCP 的设备,这是最省事的一类,不需要驱动协商。network ipp://192.168.10.21:631/ipp/print表示对方支持 IPP,这类可以走无驱动路径。network dnssd://...是 DNS-SD 广播出来的条目,可读性最好,但依赖 avahi 和网络环境。
一条设备都没列出来,先分清是哪一层的问题:lpinfo -v完全空,说明后端目录没编出来或者权限不对;只缺dnssd://,说明 avahi 没起或者编译时没开;只缺usb://,多半是 libusb 开发包没装或当前用户不在lp组。分清楚再动手,比盲目重编强。
4.2 PPD 的四个来源与选择标准
队列必须绑定一个 PPD,即使是无驱动打印,CUPS 也会在后台合成一份。PPD 的来源有四种,选错了后面会一直难受。
| 来源 | 适用场景 | 注意点 |
|---|---|---|
| 厂商提供的 PPD | 功能复杂的一体机、需要访问特殊纸盒或双面单元 | 版本要和固件对得上,老 PPD 用到新固件上可能少选项 |
lpadmin -m everywhere自动生成 | 设备支持 IPP 2.0 及以上、希望零维护 | 生成的 PPD 功能偏保守,高级选项可能缺失 |
ppdc从 .drv 编译 | 自研设备、批量定制同一系列 | 需要写 drv 源文件,学习成本在源文件语法上 |
| 通用 PostScript / PCL PPD | 只要能出纸、不需要精细控制 | 纸张尺寸和边距可能不准,A4 环境要手工校 |
我一般的取舍顺序是:先用-m everywhere试,能出纸且功能够用就到此为止;不行再换厂商 PPD;厂商 PPD 找不到或者太老,再考虑用ppdc自己编一份。
4.3 lpadmin 建队列:参数逐条拆
下面这条命令建一个走 IPP 的 A4 队列,参数注释写在后面。
sudo /usr/local/cups-2.3.3/sbin/lpadmin \ -p office-a4 \ -E \ -v ipp://192.168.10.21/ipp/print \ -m everywhere \ -D "Office A4 Printer" \ -L "Room 302" \ -o printer-is-shared=true \ -o media=A4 \ -o sides=one-sided \ -o job-sheets=none # 设为默认目标 sudo /usr/local/cups-2.3.3/sbin/lpadmin -d office-a4 # 确认队列已建好且处于接受作业状态 /usr/local/cups-2.3.3/bin/lpstat -t /usr/local/cups-2.3.3/bin/lpstat -l -p office-a4-p给队列起名,名字里只用小写字母、数字和短横线,后面脚本引用不会出引号问题。-E表示建完立刻启用并接受作业,漏了它队列会停在停用状态,症状就是作业提交成功但永远 pending。-v是设备 URI,必须和lpinfo -v里看到的一致,路径少一段都会连不上。-m everywhere走无驱动路径,CUPS 会去问设备要属性并合成 PPD;如果设备不支持,这条会直接报错,那就退回指定 PPD 文件路径的写法。-D和-L是描述与位置,纯展示用,但多队列环境里非常必要。-o后面的键值对会写进 PPD 的默认选项,printer-is-shared=true允许其他机器通过这台服务器打印,media=A4定死纸张,sides=one-sided定死单面,job-sheets=none关掉封面页——封面页在某些 PPD 下会额外消耗一张纸,批量打印时很烦。
有一条要提前说清楚:-m everywhere在 2.3.x 上可用,但前提是设备确实响应 IPP 的 Get-Printer-Attributes。如果建队列时报Unable to copy PPD file,先手动curl http://192.168.10.21:631/ipp/print看有没有响应,别在 CUPS 这边反复试。
4.4 打印第一张与过滤器链排错
队列建好,先打一张最小的测试件,把链路跑通再谈别的。
# 生成一个最小 PostScript 测试页并提交 printf '%%!PS\n/Helvetica findfont 24 scalefont setfont\n72 720 moveto (CUPS 2.3.3 test page) show\nshowpage\n' > /tmp/test.ps /usr/local/cups-2.3.3/bin/lp -d office-a4 -o media=A4 /tmp/test.ps # 看作业状态流转 /usr/local/cups-2.3.3/bin/lpstat -o /usr/local/cups-2.3.3/bin/lpstat -W completed -o提交后立刻用lpstat -o盯住,正常的话几秒内从pending转processing再到列表里消失。如果卡住不动,把日志级别提上来,重新提交一次,看过滤器到底在哪一步失败。
# 打开调试日志,注意这个命令会改写 /etc/cups/cupsd.conf sudo /usr/local/cups-2.3.3/sbin/cupsctl --debug-logging # 复现问题后立刻看日志,重点找 Job 编号和 filter 名 sudo tail -n 200 /var/log/cups/error_log # 排查完一定关掉,debug2 级别的日志写盘非常快 sudo /usr/local/cups-2.3.3/sbin/cupsctl --no-debug-loggingcupsctl --debug-logging等价于把LogLevel临时改成debug2,会记录每个过滤器的启动命令、退出码和 stderr 前若干行。日志里搜Job <编号>能串起一次作业的完整生命周期,搜filter能看到链上每个环节的入参。排查完不关掉,一晚上能把/var/log写满,这个血泪经验值得记一下。
日志里最常见的三类结论:filter failed通常意味着某个过滤器退出码非零,往上一行看它的 stderr;Unable to write print data说明后端连不上设备或者写失败;No such file or directory出现在过滤器路径上,说明--prefix和 PPD 里记录的过滤器路径不一致,这种在独立前缀部署里出现得不少。
5. 排查手记:五个让编译和打印卡死的真实场景
前面三章讲的是顺路径,这一章讲逆路径。下面五条都是我在不同机器上真撞过的问题,按“现象 → 原因 → 解决”记,遇到时可以直接对照。
5.1 cupsd 起不来,日志第一行就 FATAL
现象:cupsd -t通过,正式启动秒退,error_log只有一行Unable to read cups-files.conf或者No such file or directory,进程根本留不住。
原因:从 1.6 起配置拆成cupsd.conf和cups-files.conf两个文件,很多老部署脚本只拷了前一个。另外用--sysconfdir指到非默认目录时,make install装进去的是.default后缀模板,真正生效的文件名少了后缀。
解决:先确认/etc/cups/cups-files.conf存在且非空,缺了就从安装目录的模板拷一份改名;再确认文件里的User、Group、SystemGroup三个字段对应的用户和组在系统里真实存在,lp用户没有的话 cupsd 同样拒绝启动。
# 确认两个配置文件都在,且关键用户组存在 ls -l /etc/cups/cupsd.conf /etc/cups/cups-files.conf id lp; getent group lpadmin5.2 make install 之后,lpstat 显示的还是老版本
现象:明明装的是 2.3.3,lpstat -v却报错或者行为对不上,lpstat -t里显示的队列和/etc/cups/printers.conf里的内容不一致。
原因:PATH 里/usr/bin/lpstat排在/usr/local/cups-2.3.3/bin/lpstat前面,客户端连的是系统自带 CUPS 的套接字,服务端却是新编的那个。两套 CUPS 各连各的 socket,一个在/var/run/cups/cups.sock,另一个在别处,表面上都叫 CUPS,实际互相看不见。
解决:调试期一律用绝对路径调用,别依赖 PATH;确认lpstat连的套接字和服务端一致;cupsd.conf里的Listen /var/run/cups/cups.sock两端要对齐。长期方案是给独立前缀的 bin 目录做 shell 别名,或者在脚本里写死路径。
# 谁在听这个套接字,一眼看清 ls -l /var/run/cups/cups.sock # 用绝对路径避免版本混用 /usr/local/cups-2.3.3/bin/lpstat -t5.3 浏览器打开 631 是 403,命令行提交却正常
现象:curl http://127.0.0.1:631/返回 200,局域网另一台机器打开http://打印服务器:631一直 403 Forbidden,但lp -h 打印服务器:631提交作业能成功。
原因:<Location />段里的Allow @LOCAL只匹配与服务器直接相连的网段。跨网段、经过 NAT、或者服务器有多张网卡而请求从非预期网卡进来,来源都不算@LOCAL。另外 2.3.3 用的是老式Order allow,deny语法,从新版文档抄来的Require all granted在这里未必按预期工作。
解决:在<Location />里显式加网段,比如Allow 192.168.10.0/24;确认cupsd.conf里没有同时混用两套语法;改完用cupsd -t校验再重载。只在隔离内网里才考虑放开到更宽的网段。
<Location /> Order allow,deny Allow 192.168.10.0/24 Allow @LOCAL </Location>5.4 作业卡在 Processing,一直不往下走
现象:lpstat -o里作业状态长期是processing,不报错也不出纸,重启 cupsd 后作业重新开始又卡在同一处。
原因:后端在等一个永远不回话的连接。最常见的是设备 URI 写错端口或者路径,socket://ip:9100里设备其实监听在 9101;其次是 IPP 路径写成了/ipp/print而设备只认/;也可能是网络通了但中间有设备拦截了长连接。
解决:绕过 CUPS 直接手工调后端,看它的 stdout/stderr。后端约定用DEVICE_URI环境变量传设备地址,参数依次是 job-id、user、title、copies、options、文件路径。
# 手工执行 socket 后端,能看到它到底连没连上 sudo DEVICE_URI=socket://192.168.10.21:9100 \ /usr/local/cups-2.3.3/lib/cups/backend/socket \ 1 root "manual-test" 1 "" /tmp/test.ps # 顺便确认端口可达 nc -vz 192.168.10.21 9100手工跑能出纸,问题就在 CUPS 的队列配置或权限;手工跑也卡住,问题在网络或设备侧,别在 CUPS 上继续折腾。
5.5 重启一次,队列全没了
现象:服务跑得好好的,机器重启或者重装了一次系统包之后,lpstat -p一个队列都看不到,/etc/cups/printers.conf变成空文件或者被.O后缀的备份顶掉。
原因:队列定义存在printers.conf里,权限是 cupsd 的User可读写。如果这个文件被包管理器重装覆盖、或者属主被改成了 root 而 cupsd 以lp身份运行,写入就失败,cupsd 会退回内存态,重启即丢。
解决:固定printers.conf的属主和权限,把它纳入备份清单;用独立--prefix部署避免和系统包互相覆盖;改完队列后用lpadmin导出一份配置留档。
# 权限与属主核对,正确形态应是 lp:lp 且 cupsd 可写 ls -l /etc/cups/printers.conf sudo chown lp:lp /etc/cups/printers.conf # 留一份队列配置快照,重装后能快速恢复 sudo cp /etc/cups/printers.conf /root/printers.conf.bak.$(date +%F)6. 验收与加固:把 631 端口守住的三个具体动作
装完能打不等于交付完成。交付前我会固定做三件事:用ipptool做一次协议层回归、把访问控制收到最小网段、把作业历史打开以便追溯。这三件事加起来不到二十分钟,但能省掉后面大量的“说不清是谁打的”。
第一件是协议层回归。lpstat只能看到队列状态,看不到 IPP 属性是否完整。用一个最小的ipptool测试文件,直接对队列发Get-Printer-Attributes,把关键属性打出来:
# 写一个最小测试文件,只查状态和是否接受作业 cat > /tmp/attrs.test <<'EOF' { OPERATION Get-Printer-Attributes GROUP operation-attributes-tag ATTR charset attributes-charset utf-8 ATTR language attributes-natural-language en ATTR uri printer-uri $uri DISPLAY printer-state DISPLAY printer-is-accepting-jobs DISPLAY printer-state-reasons } EOF # -t 打印请求过程,-v 打印属性值 /usr/local/cups-2.3.3/bin/ipptool -tv \ ipp://127.0.0.1:631/printers/office-a4 /tmp/attrs.test$uri是ipptool的内置变量,会被命令行里给的 URI 自动填充。printer-state应该是 3(idle),printer-is-accepting-jobs应该是 true,printer-state-reasons是none。三项里任何一个不对,说明队列虽然建了但没真正就绪。
第二件是收紧访问控制。把<Location />里的Allow @LOCAL换成明确的网段,<Location /admin>只留运维机 IP。这一步做完,cupsd -t校验一次再重启,别在线改完就撒手。
第三件是打开历史留痕。PreserveJobHistory Yes配合page_log,能回答“上周三谁打了五十页”这种问题。PageLog默认在/var/log/cups/page_log,每行一条作业的页数和目标,配合日志轮转保留 90 天足够。
最后说个习惯。我早年有一次在客户现场重编 CUPS,make install之前没看安装清单,结果把系统自带的libcups.so覆盖了,整台机器上所有依赖打印库的图形程序全部起不来,回滚花了两个小时。从那以后我每次编译前都强制走一遍./configure --help | grep确认参数、make -n install确认落盘路径,两步加起来一分钟,但能把后悔药省下来。这套 2.3.3 的源码包我按上面流程在几台机器上跑通过,参数和坑都记在上面了,按cups-2.3.3-source.tar.gz找到对应包就能照着复现。希望帮到你。
本文还有配套的精品资源,点击获取