简介:CUPS 2.3.3 的官方源代码包,面向 Linux/Unix 系统管理员、运维工程师及打印服务定制开发者,主要解决打印服务在本地编译、个性化改造与底层机制学习中的实际需求。压缩包体积约 7.76MB,共包含 1294 个文件,文件类型以 tmpl、c、html、h、in、cxx、m4、makefile 为主,内含 configure 构建脚本、Makefile 模板、src 源码目录、docs 文档、include 头文件、policies 策略文件及 tests 测试用例,目录结构清晰,便于按模块检索与二次开发。目前已有 927 人下载学习,适合具备基础 Linux 操作经验并希望深入打印子系统的人员使用。解压后可借助 autoconf、automake 工具链完成配置、编译与安装,也能通过阅读源码理解 IPP 作业管理、过滤器链、SSL/TLS 加密和 AirPrint 支持等核心实现,进而定制驱动、扩展后端或为社区贡献改进代码。对需要掌握 CUPS 运维、安全加固和驱动扩展的技术人员而言,这是一份完整且实用的底层参考资源。
1. 一台没有打印服务的机器,和一份必踩坑的 CUPS 2.3.3 源码包
打印机在 Linux 下从来不是装上驱动就能用的角色,CUPS 2.3.3 也不会例外。很多桌面系统默认带着一套打印服务,可一旦你面对的是一台无桌面的服务器、一个必须离线交付的内网环境,或者一份指定 CUPS 2.3.3 的部署清单,apt install cups这条路就被堵死了——剩下的唯一选择,就是老老实实编译这份 cups-2.3.3-source.tar.gz 源码包。这个版本我装过三轮,第一轮卡在 configure 缺头文件,第二轮卡在 631 端口被旧服务占着,第三轮才算把它完整跑通。这篇笔记就是那三轮踩坑的记录,适合正要在一台干净机器上从源码部署 CUPS 2.3.3 的人。
2. 编译前的环境检查:依赖没装齐,configure 迟早跟你翻脸
2.1 先拆包看看源码包里都有什么
拿到 cups-2.3.3-source.tar.gz 后,不要急着管依赖,先解压扫一眼目录布局。这份 tarball 的顶层结构很规整,configure、Makefile.in、INSTALL、README 放在根目录,然后是 scheduler、cups、filter、backend、ppdc 这几个核心目录。scheduler 是 cupsd 守护进程的代码,cups 是核心库 libcups 的实现,filter 是打印命令链里的过滤器,backend 是 usb、socket、ipp 这些设备后端。知道这个结构不是为了装样子,是为了后面排错时能快速定位:configure 报错多半在库探测环节,make 报错多半在某个 filter 或 backend 的编译单元,cupsd 起不来则要去 scheduler 和支持库里找原因。
2.2 依赖清单:缺哪个,configure 就卡在哪
CUPS 2.3.3 不是单一可执行文件,它拆成主进程、核心库、过滤器、后端四个层面,每个层面都有自己的外部依赖。编译前我把最小集整理成了一张表,每次装机器都拿它对着核对:
| 依赖库 | pkg-config 名字 | 作用 | 缺失时的报错 |
|---|---|---|---|
| zlib | zlib | 压缩支持 | cannot find zlib |
| libjpeg | libjpeg | JPEG 图像解码 | JPEG 打印任务失败 |
| libpng | libpng | PNG 图像解码 | PNG 打印任务失败 |
| libtiff | libtiff | TIFF 图像处理 | TIFF 打印任务失败 |
| avahi | avahi-client | mDNS 设备发现 | 局域网自动发现不可用 |
| gnutls | gnutls | TLS 加密 | cannot find TLS functions |
| pam | pam | 本地认证 | cannot find PAM headers |
| dbus | dbus-1 | 系统总线与热插拔通知 | USB 热插拔提示缺失 |
| libusb | libusb-1.0 | USB 打印机后端 | USB 打印机无法枚举 |
这张表右边一列排错时直接对号入座。注意 gnutls 和 openssl 二选一即可,CUPS 2.3.3 的 configure 默认优先探测 gnutls,我一般都装 gnutls,原因是源码构建时的默认 TLS 后端就是 gnutls,两个库混装反而会在 HTTPS 证书行为上出现细微差异。还有一个小坑:dbus 和 libusb 在 pkg-config 里的名字分别是 dbus-1 和 libusb-1.0,带后缀,写脚本时漏掉后缀会一直误报 MISSING。
2.3 Debian/Ubuntu 系的依赖安装
在 Debian 系发行版上,依赖一条命令装齐:
sudo apt update sudo apt install -y build-essential make pkg-config \ zlib1g-dev libjpeg-dev libpng-dev libtiff-dev \ libavahi-client-dev libgnutls28-dev libpam0g-dev \ libdbus-1-dev libusb-1.0-0-dev libacl1-devbuild-essential是个元包,装了就有 gcc 和 make;libgnutls28-dev是 gnutls 开发包,libusb-1.0-0-dev对应 libusb;libacl1-dev提供 ACL 权限控制,CUPS 2.3.x 在 spool 文件上要用到它。这里最容易踩的坑是只装运行库不装开发包,比如只有libgnutls30而没有libgnutls28-dev。运行库提供.so,开发包才提供.h,CUPS 的 configure 探测的是头文件,所以缺失时即便动态库存在,依然会报 cannot find。这是我第一轮装 2.3.3 时实际卡住的地方,后来养成了装完依赖先跑一遍自查脚本再进 configure 的习惯。
2.4 RHEL/CentOS 系的依赖安装
RHEL 系发行版包名后缀换成-devel,同样的清单这样装:
sudo yum groupinstall -y "Development Tools" sudo yum install -y zlib-devel libjpeg-turbo-devel libpng-devel libtiff-devel \ avahi-devel gnutls-devel pam-devel dbus-devel libusb-devel libacl-develgroupinstall "Development Tools"装的是工具链本体,gcc、gcc-c++、make 都在里面,但不包含上面表格里的库,所以第二行命令不能省。装完验证一下:gcc --version有输出,pkg-config --modversion gnutls能打出版本号,就说明这个环境可以进 configure 阶段了。提醒一下,RHEL 系默认仓库里 gnutls 的版本通常比 Debian 系旧,但应付 CUPS 2.3.3 绰绰有余,不用担心。
提示:libusb 和 dbus 两个包在 pkg-config 里的名字是 libusb-1.0 和 dbus-1,不是 libusb 和 dbus。写自查脚本时名字不对会误判缺库,我在这上面浪费过十几分钟。
2.5 用一段脚本自查依赖缺口
等依赖装齐,我还会跑一段小脚本确认,比直接进 configure 更快定位缺口:
for lib in zlib libjpeg libpng libtiff avahi-client gnutls pam dbus-1 libusb-1.0; do if pkg-config --exists "$lib"; then printf "%-14s OK\n" "$lib" else printf "%-14s MISSING\n" "$lib" fi done脚本的核心是pkg-config --exists,它只检查对应开发库的.pc文件是否存在,存在就认为开发环境准备好了。输出里出现 MISSING 的那一行,就是接下来要补的包。这段脚本还有一个价值:把“先自查依赖,再进入 configure”变成固定动作,省得 configure 跑一半才报错,回头装依赖再重来一遍。
2.6 准备编译用户和目录,避免 root 编译的麻烦
我不习惯用 root 编译源码,CUPS 的 configure 会在源码目录写 config.cache,make 会生成一批中间文件,root 编译留下的文件后续清理费劲,权限还可能污染安装目录。所以我会先建好专用用户和目录:
sudo groupadd --system lpadmin sudo useradd --system --user-group lp sudo mkdir -p /opt/src sudo chown 编译用户:编译用户 /opt/srclp是 cupsd 运行时的系统用户,lpadmin是管理打印机的授权组。发行版自带 CUPS 的机器上这两样已经存在,再执行会报 already exists,直接跳过即可;极简系统上提前建好,能保证make install之后 cupsd 以正确身份运行。chown /opt/src是为了让编译用户能写源码目录。后面所有 configure 和 make 都以普通用户身份做,只有make install会切到 sudo。
3. 下载解压与 configure:这一步的参数,决定你的 CUPS 是哪个形态
3.1 校验 tarball 并解压
拿到 cups-2.3.3-source.tar.gz 后,我从不直接解压。先做一次完整性校验:
sha256sum cups-2.3.3-source.tar.gz然后和官方提供的 SHA-256 校验值比对。这一步在内网传输场景是救命步骤:tarball 在 U 盘拷贝、FTP 传输里损坏的情况不少见,文件坏了解压出的源码经常是 configure 缺段、make 到一半报无规律错误,查半天都查不出原因。我第一次在内网环境装 2.3.3 就吃过这个亏,最后回头比对哈希才发现 tarball 本身是坏的。校验通过后解压:
tar -xzf cups-2.3.3-source.tar.gz cd cups-2.3.3解压完成后进目录,头一件事是读 INSTALL 和configure --help。INSTALL 写着 CUPS 项目推荐的安装流程,configure 的输出有几百行不用全看,重点挑这几类关键字:--prefix、--sysconfdir、--localstatedir、--with-tls、--with-systemd、--enable-debug。后面的参数选择,全是从这些关键字展开的。
3.2 configure 参数怎么选:prefix、confdir、服务管理
configure 的核心职责是探测环境并生成 Makefile。它不直接装软件,但它生成的 Makefile 决定了软件以什么形态装进系统。我经过三轮编译,定型了这套参数:
./configure \ --prefix=/usr \ --sysconfdir=/etc \ --localstatedir=/var \ --with-docdir=/usr/share/cups/doc \ --with-rcdir=no \ --with-systemd=yes \ --with-tls=gnutls \ --enable-debug=no \ --enable-libpaper=no逐个说。--prefix=/usr把主程序、库、数据文件装进 /usr,cupsd 落在 /usr/sbin。如果机器上已有发行版自带的 CUPS,我建议把 prefix 换到 /opt/cups,避免和包管理器打架,细节在下一章展开。--sysconfdir=/etc决定 cupsd.conf、printers.conf 放 /etc/cups,写成别的路径后找配置会很别扭。--localstatedir=/var控制日志和缓冲目录,显式写出来是让 /var/log/cups 这个路径在脑子里固定住。
--with-rcdir=no --with-systemd=yes是配套的:关掉老的 System V init 脚本安装,同时启用 systemd unit 文件生成。这两个参数在 systemd 系统上必须这么配,否则要么装了旧脚本用不上,要么没有 unit 文件只能手动起进程。--with-tls=gnutls指定 TLS 后端,和前面依赖选择保持一致。最后两个开关,--enable-debug=no关掉运行时断言和调试信息,减少一点不必要的开销;--enable-libpaper=no不依赖外部纸型库,CUPS 自带的纸型表对绝大多数场景够用,少一个依赖,configure 阶段就少一个失败点。
注意:这些参数按 systemd 发行版场景写的。如果目标机器是 sysvinit 的老系统,
--with-rcdir=no要反着改成--with-rcdir=yes,否则启动脚本不会装出来。
3.3 configure 报错自救:三个高频错误
configure 卡住的报错,九成集中在三句话上。
第一句:configure: error: Cannot find PAM headers.现象是 configure 走到中间停下。原因是缺 pam 的开发包,只装了 libpam0g 运行库而没有 libpam0g-dev。解决办法是回第 2 章装依赖,然后重新 configure。有个陷阱:直接重跑 configure 有时还是报同样的错,因为 config.cache 缓存了上次的探测结果。先make distclean或者手动删掉 config.cache 再重跑。
第二句:configure: error: Cannot find TLS functions。原因基本是 gnutls 和 openssl 的开发文件一个都没装,或者两者都装了但.pc文件路径不对。解决:装 libgnutls28-dev 或 gnutls-devel 后重跑。验证方法非常简单,pkg-config --exists gnutls && echo ok,输出 ok 就说明 TLS 开发环境就绪。
第三句:configure: error: Unable to find a C99-compatible compiler。原因不是缺库而是缺编译器,GCC 没装或者版本太老。解决:apt install build-essential或yum groupinstall "Development Tools",装完确认gcc --version。这三句报错的共同特点是,报错信息头部就写清了丢失项的名字,不需要去看后续几百行。configure 的输出看起来是个黑匣子,实际抓取configure: error那一行就能确定下一步动作。
4. make 与 make install:编译环节的最后一公里,和那些玄学报错
4.1 并行编译:-j 参数不是越大越好
configure 通过后就进编译:
make -j$(nproc)-j$(nproc)让 make 按 CPU 核数并行编译。CUPS 2.3.3 这个体量的项目,8 核机器上通常两三分钟编完,单核跑要 15 分钟上下。但并行度不是越高越好:内存不足 4G 的机器,-j8 很容易把内存打满,内核的 OOM Killer 会把编译进程杀掉,终端只输出一个Killed,看起来特别像系统抽风。我一般保守写-j4,两台机器上连着编都没出过问题。
make 结束后立刻看退出码:
echo $?输出 0 说明编译阶段整体通过。如果中途报错,先记住报错文件名和行号再去查。CUPS 的编译报错大部分是头文件缺失,对应回第 2 章依赖表补包重编即可;少部分是源码和编译器版本不兼容,这种报错通常会指向某一个具体函数,不建议硬改源码,优先换编译器版本或者换回发行版 gcc。
4.2 make install 之后的关键目录
编译完成进入安装:
sudo make install这步会把文件写进 /usr 和 /etc,所以必须 sudo。装完以后目录布局是这样的:
| 路径 | 内容 |
|---|---|
| /usr/sbin/cupsd | 主守护进程 |
| /usr/bin/lp*、/usr/bin/cups* | 客户端命令 |
| /usr/lib/cups/backend | 打印机设备后端 |
| /usr/lib/cups/filter | 打印过滤器 |
| /usr/lib/libcups.so.2 | 核心库 |
| /etc/cups/cupsd.conf | 主配置文件 |
| /etc/cups/printers.conf | 打印机队列配置 |
| /var/spool/cups | 打印任务缓冲 |
| /var/log/cups/error_log | 运行日志 |
真正需要手工确认的是最后三行。printers.conf 在首次添加打印机前可能不存在,cupsd 启动时会自动创建。error_log 没有时,检查 /var/log/cups 目录是否存在且属主是 lp。spool 目录同样要求属主 lp:lp,权限不对会出现下一章要说的“队列卡住打不出”怪病。如果用了 --prefix=/opt/cups,表格路径整体换成 /opt/cups/...,尤其注意后端和过滤器路径变成 /opt/cups/lib/cups/backend 和 /opt/cups/lib/cups/filter,配置文件里写错这些路径,打印任务会挂在队列里一动不动。
4.3 和发行版自带 CUPS 正面交锋:覆盖还是共存
不少桌面系统默认带一套 CUPS 包。直接 make install 到 /usr 时,源码版会覆盖发行版的 cupsd 和相关文件,多数情况下能跑,但有一个后悔药难吃的场景:发行版升级时,包管理器发现 /usr/sbin/cupsd 不在它的数据库里,要么拦截要么顺手覆盖回旧版本,整套源码安装相当于白干。
我现在的习惯是:机器上有自带 cups 时,优先把源码版装到 /opt/cups,两者共存,互不干扰。做法是改几个 configure 参数重新生成 Makefile:
make distclean ./configure \ --prefix=/opt/cups \ --sysconfdir=/opt/cups/etc \ --localstatedir=/opt/cups/var \ --with-rcdir=no \ --with-systemd=yes make -j4 sudo make installmake distclean很重要,它清掉上一次 configure 生成的 Makefile 和缓存,否则旧参数会残留。装完以后,cupsd 在 /opt/cups/sbin,配置文件在 /opt/cups/etc/cups,数据在 /opt/cups/var,完全和系统分区隔开。启动时对应的 systemd unit 里要写对 ExecStart 路径,后续升级源码也不会动到系统自带那套。
如果不想共存,那就先卸载系统自带包再装:
sudo systemctl stop cups sudo apt remove --purge cups cups-browsed sudo apt autoremovecups-browsed 来自 cups-filters 包,它和 CUPS 本体不是一回事,但特别喜欢占 631 端口,卸载时顺手清掉,能避免下一章说到的端口冲突。
5. 避坑排查:cupsd 起不来、631 端口进不去,这几个坑我替你先踩了
5.1 排查前先看 error_log
开始逐个说坑之前,先说一个通用习惯:CUPS 的日志默认写在 /var/log/cups/error_log,这是理解所有故障的第一入口。
tail -n 50 /var/log/cups/error_log日志行的级别前缀是 D、E、W、I,分别对应 Debug、Error、Warning、Info。排故障时主要看 E 和 W。比如E [Job 5] Unable to open print file,意思就是作业文件打不开,八成是 spool 权限问题。看到 D 开头的不要慌,那是调试信息,不代表故障。先把这个文件翻一遍再动手,很多问题根本不用重启服务就能定位。
5.2 cupsd 起不来:动态链接库找不到
现象:执行 /usr/sbin/cupsd 直接报error while loading shared libraries: libcups.so.2,或者服务起来一半就退出。
原因:源码版 libcups.so.2 装在了 /usr/lib 或 /opt/cups/lib,但系统动态链接器缓存 ld.so.cache 里没记录这个目录。cupsd 的 main 函数还没开始跑,链接器就先失败了。
解决:先看缺什么,再补缓存:
ldd /usr/sbin/cupsd | grep "not found" sudo sh -c "echo '/opt/cups/lib' >> /etc/ld.so.conf" sudo ldconfigldd列出 cupsd 依赖的动态库,not found就是缺的。CUPS 源码默认把库装到 /usr/lib,多数发行版已经收录;如果 prefix 是 /opt/cups,就把 /opt/cups/lib 追加进 /etc/ld.so.conf,再 ldconfig 刷新缓存。这条做完,cupsd 起不来这类玄学问题能消灭一半。
5.3 端口 631 被占用:cups-browsed 或旧实例
现象:启动时报Address already in use,或者cupsd: bind() failed。
原因:631 是 IPP 默认端口,系统里可能还跑着另一个 cupsd 实例,或者 cups-filters 的 cups-browsed 服务占着端口。在装过完整 cups 包的机器上,这种冲突很常见。
解决:先看占用者是谁:
ss -lntp | grep 631 sudo systemctl stop cups-browsed sudo systemctl disable cups-browsed sudo systemctl stop cups sudo systemctl start cupsss -lntp会直接显示 PID 和进程名。如果是 cups-browsed,停掉并禁用;如果另一个 cupsd 是系统自带的,用 systemctl stop 先停掉。如果所有服务都想留着,就改源码版的监听端口,把 cupsd.conf 里的Listen localhost:631改成Listen localhost:6631,之后网页和作业全走 6631。
5.4 网页管理界面打不开:默认只监听回环地址
现象:本机能打开 localhost:631,局域网其它机器访问服务器 IP 的 631 端口却超时或拒绝。
原因:CUPS 2.3.3 编译完的默认 cupsd.conf 只监听 localhost:631,安全上偏保守,但对内网打印机管理来说,等于默认拒绝了来自其它机器的访问需求。这不是 bug,是默认策略。
解决:改监听地址和访问控制。cupsd.conf 里把Listen localhost:631改为Listen 0.0.0.0:631,然后在 Location 块里加上允许网段。改完先跑语法检查再重启:
sudo cupsd -t && sudo systemctl restart cupscupsd -t只做配置语法校验,不启动服务,这是个能救命的命令。语法错误时 cupsd 会直接拒绝启动,先在校验阶段暴露问题,比服务起不来再去翻日志快得多。这个习惯我后来一直保留,每次改完 cupsd.conf 都先跑一遍它。
5.5 打印任务挂在队列里:spool 权限和过滤器路径
现象:网页上能看到打印机,作业状态却停在 queued 或 stopped,日志里出现Unable to open print file或Job stopped due to filter errors。
原因:两类,一类是 /var/spool/cups 属主不对,cupsd 无法读写打印任务文件;另一类是过滤器路径和 cupsd.conf 不一致,比如源码版过滤器在 /opt/cups/lib/cups/filter,配置里写的还是 /usr/lib/cups/filter。
解决:先查路径,再修权限:
ls -ld /var/spool/cups grep -r "filter" /etc/cups/cupsd.confspool 目录属主必须是 lp:lp,权限一般是 710。过滤器路径如果不对,把 cupsd.conf 里的 FilterPath 改到实际安装位置,或干脆用符号链接把两个目录连起来。改完同样先 cupsd -t 再重启。这个坑最容易出现在 prefix 和默认参数混用的场景,前面第 4 章选 /opt/cups 时特别注意过滤器和后端路径。
5.6 管理页面认证失败:lpadmin 组的边界
现象:网页管理界面要求登录,输入 root 密码却提示认证失败。
原因:CUPS 默认允许 root 在 localhost 上认证,但很多管理场景里浏览器走的是内网地址或反向代理,root 默认被拒绝。这是 CUPS 的默认安全策略,不该硬改。
解决:把日常管理用户加进 lpadmin 组:
sudo usermod -aG lpadmin 你的用户名 sudo systemctl restart cups加组之后重开浏览器再登录,管理界面就能正常操作。这是 CUPS 推荐的管理授权方式,比放开 root 远程认证安全得多。lpadmin 组只控制打印管理权限,不会给普通用户越权的系统权利,正好符合日常维护需要。
6. 装完怎么验证跑通:服务状态、网页和命令行三路齐发
6.1 服务、端口、日志三连查
先做一轮最基础的检查,确认服务真的在跑:
ps -ef | grep cupsd ss -lntp | grep 631 tail -n 20 /var/log/cups/error_logps看到 cupsd 进程存在,ss看到 631 端口在监听,error_log 末尾没有新的 E 开头报错,三个条件都满足,服务这层就算过了。
6.2 网页管理界面验证
浏览器访问 http://localhost:631。能打开 CUPS 首页,并且顶部有 Administration 标签,说明 Web 界面模块正常。如果用的是源码版自签名证书,浏览器会先弹证书不信任的提示,这是正常的,点继续就行。能进入管理页并能看到打印机列表为空,说明数据库和权限层的读写都没问题。
6.3 命令行验证:lpinfo、lpadmin、lp 一条龙
网页正常后,用命令行做端到端验证,这三条命令是打印链路的完整走查:
lpinfo -v lpinfo -m lpadmin -p fileprinter -E -v file:/tmp/cups_print_out -m raw lp -d fileprinter /etc/hosts cat /tmp/cups_print_outlpinfo -v列出所有能被 CUPS 识别的打印设备,USB 打印机会显示 usb:// 开头,网络打印机会显示 socket:// 或 ipp:// 开头。lpinfo -m列出可用 PPD 驱动。lpadmin这里用的是 file:/tmp/cups_print_out 后端,它不接真实打印机,而是把打印任务内容写到指定文件,是验证 CUPS 全链路最省事的办法。这个后端在源码编译版本里一定会构建出来,发行版裁剪过的包偶尔会去掉,而我们这套源码装出来是有的。最后lp -d fileprinter /etc/hosts发送一个真实任务,然后 cat 那个输出文件,能看到 /etc/hosts 的内容就说明整条链路是通的。这一套跑完,CUPS 2.3.3 从源码装到可用,才算真正收尾。
如果机器上确实有网络打印机,把-v file:/tmp/cups_print_out换成实际设备 URI,比如-v socket://192.168.1.100:9100,用同样的命令序列就能把物理打印机纳管进队列。
6.4 一个收尾习惯:每次改完配置都先 cupsd -t
从那以后,我每次动完 cupsd.conf 或者 printers.conf,都强制自己先跑一遍sudo cupsd -t再做重启。这个命令只校验语法不起服务,三秒钟就能把拼写错误、括号不匹配这类低级问题挡在服务重启之前。CUPS 的坑大多不在功能复杂,而在默认策略和路径假设,比如只监听回环地址、过滤器路径写死、spool 属主不对。把这些默认值在安装阶段就核对一遍,后面能省出一整个下午。希望这份源码安装的记录,能帮你把 CUPS 2.3.3 一次装通。
本文还有配套的精品资源,点击获取