简介:这份资料面向需要在 Unix/Linux 环境下使用 libpcap 进行数据包捕获、流量采集与规则过滤的开发者及运维人员,提供离线自动安装方案,重点解决内网或隔离环境中依赖缺失、编译工具链不齐、手工安装繁琐等问题。压缩包共 2 个文件,包含 1 个 tar 归档和 1 个 sh 安装脚本,整体约 43.29MB;归档中集中放置 gcc、m4、bison、flex、libpcap 等源码包,脚本用于按顺序完成解压、编译与安装,减少逐步排查依赖的时间。已有 1041 人学习,适合需要快速搭建抓包开发或实验环境、补齐编译依赖与离线部署思路的读者参考。对于需要理解依赖编译顺序、验证 libpcap 可用性、构建可复用安装流程的读者,可据此完成环境准备与基础功能验证,也能作为教学实验中的组件准备材料。
1. 离线装 libpcap 先别急着解压:一条依赖链的断点在哪里
离线环境里装 libpcap,最容易被忽略的不是 libpcap 本身,而是它前面那条 gcc、m4、bison、flex 的依赖链。在隔离机房里常见的一幕是:系统只带基础工具,./configure一跑就停在flex或bison缺失上;换成源码编译,又发现没有可用的 gcc,或者 gcc 构建时反过来找 m4。把 gcc、m4、bison、flex、libpcap 五个源码包按顺序串成自动安装脚本,价值不在于省几条命令,而在于把“缺啥补啥”的临场判断变成可重复、可回看的流程。
这份资源适合手里已经有离线源码包、需要在无外网机器上落地抓包开发环境的人,也适合想把依赖安装过程固化成脚本的运维和 C 程序员。它解决的是依赖顺序、安装前缀、动态库路径和编译参数这几件落地事,不是教你怎么写抓包业务。先看清断点在哪里,再动手解压,后面能少返工。
2. 把源码包和脚本摊开看:目录编排、编译顺序与三个关键变量
离线安装最容易翻车的地方,不是某个包编译不过,而是包与包之间的先后手没排对。gcc、m4、bison、flex、libpcap 这五个名字看起来都是独立工具,实际在源码构建场景里有明确的依赖方向:bison 的运行依赖 m4,gcc 的构建过程会调用 m4、bison、flex,libpcap 的语法扫描部分又依赖 flex、bison,最终链接和编译需要 gcc。顺序排错,脚本就会在中间某个包上停住,日志里只留下一句看不出根因的报错。
2.1 为什么顺序不能按字母排:依赖链先画清楚
按字母排序会得到 bison、flex、gcc、libpcap、m4,这个顺序在离线机器上大概率跑不通。bison 自己需要 m4 做宏处理,如果系统自带的 m4 版本太旧或者干脆没有,bison 的configure阶段就会失败;gcc 的构建过程又会调用 bison、flex 和 m4,所以这三个工具要尽量在 gcc 之前装好,并且安装前缀下的bin要优先进入PATH。libpcap 放在最后,是因为它要使用前面装好的编译器工具链,部分源码包在重新生成扫描器和语法分析器时还会调用 flex、bison。
常见做法是把安装顺序固定成:m4 → bison → flex → gcc → libpcap。这个顺序不是唯一真理,但它是离线源码安装里最稳的一条线。若脚本还带了 gmp、mpfr、mpc 这类 gcc 构建常见依赖,它们要排在 gcc 前面;这份资源的标题只列了 gcc、m4、bison、flex、libpcap,所以脚本执行前要先确认目标机是否已有 gcc 构建所需的数学库,或者源码包里是否已经补齐。没有补齐时,gcc 的configure会在检查阶段直接拒绝继续,不会等到make才报错。
| 包 | 在链路中的角色 | 主要前置 | 安装后要验证的命令 |
|---|---|---|---|
| m4 | 宏处理器,bison 和部分构建脚本会调用 | 系统基础 shell、make | m4 --version |
| bison | 语法分析器生成器,gcc/libpcap 构建可能调用 | m4 | bison --version |
| flex | 词法扫描器生成器,libpcap 构建可能调用 | 无强前置,但建议在 gcc 前 | flex --version |
| gcc | C/C++ 编译器,libpcap 和测试程序使用 | m4、bison、flex,以及常见数学库 | gcc --version |
| libpcap | 抓包开发库,最终交付物 | gcc、flex、bison、m4 | pcap-config --version |
这张表的作用是给脚本排依赖,不是让你背概念。实际执行时,脚本每装完一个包就把$PREFIX/bin放到PATH最前面,保证后续configure检查到的是新装版本,而不是系统里残留的旧版本。尤其是 bison 和 flex,系统自带版本可能低于 gcc 构建要求,不固定PATH就会出现“明明装了新版本,构建时还在用旧版本”的怪现象。
2.2 离线目录怎么摆:src、build、logs 与清单文件
离线脚本要可维护,目录不能随手摊在用户主目录里。我一般会把整套东西收在一个工作目录下,源码包放src/,解压和编译产物放build/,日志放logs/,安装前缀单独放opt/offline-stack或用户目录下的.local/offline-stack。这样做的直接好处是失败后能按日志定位,重跑时也能只清build/,不碰源码包。脚本不要依赖当前目录,所有路径用变量拼出来,执行前先mkdir -p。
# 建议的离线工作目录结构,脚本里用变量指向,不要写死当前路径 WORK_DIR="${WORK_DIR:-$HOME/offline-libpcap}" SRC_DIR="$WORK_DIR/src" # 放 m4、bison、flex、gcc、libpcap 源码包 BUILD_DIR="$WORK_DIR/build" # 解压和 out-of-tree 编译目录 LOG_DIR="$WORK_DIR/logs" # 每个包的 configure/make/install 日志 PREFIX="${PREFIX:-/opt/offline-stack}" # 独立安装前缀,便于回滚 mkdir -p "$SRC_DIR" "$BUILD_DIR" "$LOG_DIR" "$PREFIX"这段准备动作的重点是“路径可覆盖”。WORK_DIR、PREFIX都留了默认值,也允许外部用环境变量改,这样同一套脚本既能装到系统级/opt,也能装到普通用户目录。SRC_DIR和BUILD_DIR分开,是为了避免源码目录被编译产物污染;LOG_DIR单独存在,是为了后面排查时不用翻终端滚动记录。参数上,PREFIX不建议直接用/usr/local,因为离线机器上可能已经有旧版 libpcap 或 gcc,混在一起很难判断最终用的是哪一套。
源码包命名也要收敛。脚本里不要用过于宽泛的*.tar.*去猜,建议放一个packages.list或SHA256SUMS,把文件名和校验值固定下来。离线环境没有外网,包一旦拿错版本,排查成本比在线环境高得多。常见做法是脚本启动时先检查SRC_DIR下每个包是否存在,再执行校验;校验不通过就直接退出,不进入解压。这样能把“包不完整”挡在编译之前。
2.3 三个环境变量先固定:PATH、LD_LIBRARY_PATH、PKG_CONFIG_PATH
离线源码安装最怕“装了但没被找到”。PATH决定命令走哪一套,LD_LIBRARY_PATH决定运行时动态库走哪一套,PKG_CONFIG_PATH决定pkg-config能不能找到新装的.pc文件。三个变量里,PATH要在脚本开头就固定,LD_LIBRARY_PATH和PKG_CONFIG_PATH要在安装后校验和测试程序编译时固定。不要等到报错再临时export,那样脚本换一台机器就容易漏。
# 把独立前缀放到最前面,避免系统旧版本抢先被找到 export PATH="$PREFIX/bin:$PATH" export LD_LIBRARY_PATH="$PREFIX/lib:$PREFIX/lib64:${LD_LIBRARY_PATH:-}" export PKG_CONFIG_PATH="$PREFIX/lib/pkgconfig:$PREFIX/lib64/pkgconfig:${PKG_CONFIG_PATH:-}" # 安装完成后可写入一个 env 文件,方便业务程序复现同一套环境 cat > "$PREFIX/offline-stack.env" <<EOF export PATH="$PREFIX/bin:\$PATH" export LD_LIBRARY_PATH="$PREFIX/lib:$PREFIX/lib64:\${LD_LIBRARY_PATH:-}" export PKG_CONFIG_PATH="$PREFIX/lib/pkgconfig:$PREFIX/lib64/pkgconfig:\${PKG_CONFIG_PATH:-}" EOF这段环境变量设置的关键在顺序:新前缀在前,系统路径在后。LD_LIBRARY_PATH里同时写lib和lib64,是因为不同构建参数下库目录可能落在其中之一;不写全,测试程序运行时就会找不到libpcap.so。offline-stack.env是给后续业务程序用的,脚本安装完不代表业务程序就能直接编译,业务侧要么source这个文件,要么在编译命令里显式写-I$PREFIX/include和-L$PREFIX/lib。参数上,PKG_CONFIG_PATH指向pkgconfig目录而不是lib目录,这一点经常被写错,写错后pkg-config --modversion libpcap会直接报找不到包。
提示:如果安装到普通用户目录,
ldconfig往往没有权限执行,这时不要硬写系统库配置,改用offline-stack.env里的LD_LIBRARY_PATH,或者在编译业务程序时优先静态链接 libpcap。
3. 手写一遍核心编译命令:m4、bison、flex、gcc、libpcap 的落地参数
脚本自动化的前提是手工命令能跑通。离线环境里,很多失败不是脚本写错,而是某条configure参数在目标机上不成立。下面按依赖顺序拆开,每个包只保留落地时最常用的参数,重点看“为什么这样选”和“失败时看哪本日志”。手工跑通一遍,再把这组命令收进脚本,比直接拿一个黑盒脚本执行更稳。
3.1 m4 与 bison:先把宏处理器和语法生成器装稳
m4 是链路的起点,它的构建相对简单,但也不要直接./configure && make就完事。离线环境里建议关掉 Native Language Support,减少对 gettext 一类运行时依赖的牵扯;安装前缀固定到独立目录,后面 bison 才能稳定找到它。bison 的configure会检查 m4,如果 m4 没有进PATH,或者系统 m4 版本不满足要求,bison 可能在检查阶段就退出。先装 m4、刷新PATH,再装 bison,这一步不能省。
# m4:先装宏处理器,bison 构建时会调用它 cd "$BUILD_DIR" tar -xf "$SRC_DIR"/m4-*.tar.* cd m4-*/ ./configure --prefix="$PREFIX" --disable-nls \ >"$LOG_DIR/m4-configure.log" 2>&1 make -j"$(nproc)" >"$LOG_DIR/m4-make.log" 2>&1 make install >"$LOG_DIR/m4-install.log" 2>&1 export PATH="$PREFIX/bin:$PATH" "$PREFIX/bin/m4" --version # bison:语法分析器生成器,依赖刚装好的 m4 cd "$BUILD_DIR" tar -xf "$SRC_DIR"/bison-*.tar.* cd bison-*/ ./configure --prefix="$PREFIX" --disable-nls \ >"$LOG_DIR/bison-configure.log" 2>&1 make -j"$(nproc)" >"$LOG_DIR/bison-make.log" 2>&1 make install >"$LOG_DIR/bison-install.log" 2>&1 "$PREFIX/bin/bison" --version这两段命令的逻辑是“先装前置,再装依赖前置的包”。m4 的--disable-nls减少离线环境里语言包缺失带来的干扰;bison 同样关掉 NLS,避免构建过程中去拉取不需要的翻译文件。每次make install后立刻把$PREFIX/bin放到PATH前面,是为了让 bison 的configure检查到新 m4,而不是系统旧 m4。参数上,-j"$(nproc)"用满 CPU 核心数,离线机器如果内存较小,可以改成-j2或-j4,避免编译并行度过高导致 OOM。失败时先看$LOG_DIR/bison-configure.log末尾,如果出现m4 not found或版本不满足,优先检查PATH和$PREFIX/bin/m4是否存在。
bison 安装完成后不要只看命令有没有返回 0,还要确认bison --version输出的是新前缀下的版本。离线机器上常见的问题是 shell 缓存了旧命令路径,或者PATH追加顺序写反,导致which bison仍然指向/usr/bin/bison。脚本里可以用command -v bison打印实际路径,日志里留一行,后面排查会省很多时间。m4 和 bison 这一层稳了,后面的 flex 和 gcc 才不会在奇怪的阶段报错。
3.2 flex 与 gcc:编译器工具链的先后手
flex 的构建通常不依赖 bison,但它在 gcc 和 libpcap 的构建里可能被调用。离线安装时把 flex 放在 gcc 前面,主要是为了让 gcc 的configure能检测到可用的词法分析器;如果目标机系统自带 flex 且版本满足要求,也可以跳过,但脚本里最好保留显式安装步骤,避免换一台机器就缺。flex 同样建议独立前缀加--disable-nls,安装后验证flex --version。
gcc 是整个链路里最重的一步。离线源码编译 gcc 有一个前提:目标机上必须已经有一个能用的 C 编译器,否则 gcc 自己没法被编译出来。脚本再自动,也变不出一个初始编译器。常见做法是确认系统cc或gcc可用,然后用--disable-bootstrap让构建过程使用系统编译器,减少三阶段自举的时间和失败面;用--enable-languages=c,c++只装需要的语言,避免把 Ada、Fortran 等不相关前端一起拉进来;用--disable-multilib避免 32 位库缺失导致构建中断。若 gcc 源码包需要 gmp、mpfr、mpc,而系统里没有,就要在 gcc 之前补上,或者使用源码包自带的下载解压机制,但离线环境要提前放好。
# flex:词法扫描器生成器,放在 gcc 前装好 cd "$BUILD_DIR" tar -xf "$SRC_DIR"/flex-*.tar.* cd flex-*/ ./configure --prefix="$PREFIX" --disable-nls \ >"$LOG_DIR/flex-configure.log" 2>&1 make -j"$(nproc)" >"$LOG_DIR/flex-make.log" 2>&1 make install >"$LOG_DIR/flex-install.log" 2>&1 "$PREFIX/bin/flex" --version # gcc:离线构建前先确认系统有初始 cc,否则这一步无法启动 command -v cc || command -v gcc || { echo "没有初始 C 编译器,gcc 源码无法自举" >&2 exit 1 } cd "$BUILD_DIR" tar -xf "$SRC_DIR"/gcc-*.tar.* mkdir -p build-gcc && cd build-gcc ../gcc-*/configure \ --prefix="$PREFIX" \ --enable-languages=c,c++ \ --disable-multilib \ --disable-bootstrap \ --disable-nls \ >"$LOG_DIR/gcc-configure.log" 2>&1 make -j"$(nproc)" >"$LOG_DIR/gcc-make.log" 2>&1 make install >"$LOG_DIR/gcc-install.log" 2>&1 "$PREFIX/bin/gcc" --version这段命令里,command -v cc || command -v gcc是 gcc 源码安装的前置检查,不是可有可无的装饰。很多离线脚本失败在“目标机没有任何编译器”,日志里表现为C compiler cannot create executables,根因不是参数写错,而是初始编译器缺失。--disable-bootstrap让构建使用系统编译器,适合离线落地和缩短时间;如果目标机系统编译器版本太旧,无法编译新版 gcc 源码,就要换用匹配的 gcc 源码版本,或者先放一个预编译工具链,不要硬扛。--disable-multilib解决的是 32 位库缺失导致的构建失败,尤其是只装 64 位系统的机器。--enable-languages=c,c++是裁剪构建范围,libpcap 和大多数抓包程序只需要 C,保留 C++ 是为了后续业务程序可能用到。
gcc 构建时间较长,日志要分开记。configure失败通常看gcc-configure.log末尾,常见是缺少 gmp、mpfr、mpc,或者系统编译器不支持源码要求的 C++ 标准;make阶段失败则要看gcc-make.log里最后一个error:上下文,不要只看最后一行。离线环境里还有一个常见问题是makeinfo缺失,文档生成阶段会报错,但核心编译器可能已经编好。这种情况可以用MAKEINFO=true make -j"$(nproc)"绕过文档生成,再执行make install;这是工程上常见的止损做法,不是修改源码逻辑。
3.3 libpcap 的 configure 参数:静态优先还是动态优先
libpcap 放在最后装,因为它要使用前面装好的 gcc、flex、bison。configure阶段重点看三件事:编译器是否能找到、flex/bison 是否可用、安装前缀是否独立。离线交付里我一般优先静态链接,也就是--disable-shared --enable-static,这样业务程序运行时不用再为libpcap.so的路径发愁;如果业务侧明确要动态库,再去掉--disable-shared,安装后补ldconfig或LD_LIBRARY_PATH。两种模式没有绝对好坏,静态省运行时路径,动态省升级时的重编译,按交付场景选。
# libpcap:静态优先,减少离线机器上的运行时库路径问题 cd "$BUILD_DIR" tar -xf "$SRC_DIR"/libpcap-*.tar.* cd libpcap-*/ ./configure \ --prefix="$PREFIX" \ --disable-shared \ --enable-static \ >"$LOG_DIR/libpcap-configure.log" 2>&1 make -j"$(nproc)" >"$LOG_DIR/libpcap-make.log" 2>&1 make install >"$LOG_DIR/libpcap-install.log" 2>&1 # 验证 pcap-config 和头文件是否落在独立前缀下 "$PREFIX/bin/pcap-config" --cflags "$PREFIX/bin/pcap-config" --libs test -f "$PREFIX/include/pcap/pcap.h"这段命令的关键参数是--disable-shared --enable-static。静态模式下,make install后主要产物是libpcap.a和头文件,业务程序编译时用pcap-config --cflags --libs取参数即可,运行时不需要再找.so。如果选动态模式,安装后要确认$PREFIX/lib下有libpcap.so,并且运行时能通过ldconfig或LD_LIBRARY_PATH找到;否则编译能过,一运行就报error while loading shared libraries: libpcap.so。参数上,--prefix必须和前面 gcc 的前缀一致,否则pcap-config输出的路径会和实际安装位置打架。
libpcap 的configure日志里如果出现 flex 或 bison 相关检查失败,不要急着怀疑 libpcap 源码,先回到$PREFIX/bin确认 flex、bison 是否存在,再确认PATH是否把它们排在系统旧版本前面。部分 libpcap 发布包已经带了生成好的扫描器和语法分析器,构建时不一定调用 flex/bison,但离线脚本不能假设每个包都这样,显式装好前置工具更稳。安装完成后用pcap-config --cflags和--libs各跑一次,输出里应该出现$PREFIX/include和$PREFIX/lib,这是后面测试程序能编译通过的前提。
| 包 | 关键 configure 参数 | 选择理由 | 失败先看 |
|---|---|---|---|
| m4 | --prefix、--disable-nls | 独立前缀,减少语言包依赖 | config.log里的编译器检查 |
| bison | --prefix、--disable-nls | 依赖 m4,关闭非必要 NLS | 是否找到新装 m4 |
| flex | --prefix、--disable-nls | 供 gcc/libpcap 构建调用 | 系统旧 flex 是否抢先 |
| gcc | --disable-bootstrap、--disable-multilib、--enable-languages=c,c++ | 用系统编译器起步,裁剪语言和 32 位库 | 初始 cc、gmp/mpfr/mpc、makeinfo |
| libpcap | --disable-shared、--enable-static | 静态优先,减少运行时库路径问题 | 编译器、flex/bison、pcap-config |
这张表建议直接贴在脚本注释里。离线环境里换一台机器,最先变的就是系统自带工具版本和库路径,表里“失败先看”这一列比参数本身更常用。参数不是越多越好,gcc 构建尤其如此,每加一个--enable-*都可能引入新的离线依赖;先把最小可用链路跑通,再按业务需要加功能。
4. 脚本自动化:从环境探测到安装后校验的完整流程
手工命令跑通后,脚本要解决的是重复执行和失败定位。离线安装脚本最忌讳“一把梭”:前面不检查、中间不打日志、失败不退出,最后只留下一个半装状态。我一般把脚本分成环境探测、包构建、安装后校验三段,每段都写日志,关键命令失败立即退出。脚本不追求花哨的参数解析,先保证在干净机器上能从头跑到尾,再考虑跳过已完成步骤和回滚。
4.1 脚本骨架:set -euo pipefail 与日志
脚本开头用set -euo pipefail,是为了让未定义变量、管道中间失败和普通命令失败都能暴露出来。离线安装里最怕“某个包其实没装成功,但脚本继续往下跑”,最后 libpcap 编译失败,根因被埋在后面。日志按包名分开,configure、make、install各一份,失败时直接打印对应日志末尾,不要让执行者去翻整个终端输出。构建函数接受包名、压缩包路径和 configure 参数,减少重复代码。
#!/usr/bin/env bash set -euo pipefail PREFIX="${PREFIX:-/opt/offline-stack}" WORK_DIR="${WORK_DIR:-$HOME/offline-libpcap}" SRC_DIR="$WORK_DIR/src" BUILD_DIR="$WORK_DIR/build" LOG_DIR="$WORK_DIR/logs" JOBS="${JOBS:-$(nproc)}" export PATH="$PREFIX/bin:$PATH" export LD_LIBRARY_PATH="$PREFIX/lib:$PREFIX/lib64:${LD_LIBRARY_PATH:-}" export PKG_CONFIG_PATH="$PREFIX/lib/pkgconfig:$PREFIX/lib64/pkgconfig:${PKG_CONFIG_PATH:-}" mkdir -p "$PREFIX" "$BUILD_DIR" "$LOG_DIR" log() { printf '[%s] %s\n' "$(date +%H:%M:%S)" "$*"; } die() { log "ERROR: $*"; exit 1; } # 通用构建函数:解压、configure、make、install 四步分开记日志 build_one() { local name="$1" tarball="$2" conf_args="$3" local src_dir src_dir="$(tar -tf "$tarball" | head -n1 | cut -d/ -f1)" [ -d "$BUILD_DIR/$src_dir" ] || tar -xf "$tarball" -C "$BUILD_DIR" cd "$BUILD_DIR/$src_dir" # shellcheck disable=SC2086 ./configure --prefix="$PREFIX" $conf_args \ >"$LOG_DIR/$name-configure.log" 2>&1 \ || die "$name configure 失败,看 $LOG_DIR/$name-configure.log" make -j"$JOBS" >"$LOG_DIR/$name-make.log" 2>&1 \ || die "$name make 失败,看 $LOG_DIR/$name-make.log" make install >"$LOG_DIR/$name-install.log" 2>&1 \ || die "$name install 失败,看 $LOG_DIR/$name-install.log" log "$name 安装完成" } # 按依赖顺序调用,参数只写本包需要的 configure 选项 build_one m4 "$SRC_DIR"/m4-*.tar.* "--disable-nls" build_one bison "$SRC_DIR"/bison-*.tar.* "--disable-nls" build_one flex "$SRC_DIR"/flex-*.tar.* "--disable-nls" build_one gcc "$SRC_DIR"/gcc-*.tar.* "--enable-languages=c,c++ --disable-multilib --disable-bootstrap --disable-nls" build_one libpcap "$SRC_DIR"/libpcap-*.tar.* "--disable-shared --enable-static" log "全部安装完成,前缀:$PREFIX"这段骨架的核心是“失败即停、日志分离、顺序固定”。set -euo pipefail让configure、make、make install任一失败都不会被忽略;build_one把四步拆开记日志,出错时die直接给出对应日志路径,不用凭记忆猜。conf_args用字符串传入并依赖 shell 分词,写脚本时要注意参数里不要出现带空格的路径;如果路径可能带空格,改成数组传参更稳。tar -tf | head -n1 | cut -d/ -f1用来取压缩包顶层目录名,适合常见源码包结构;如果包内顶层目录不唯一,脚本要改成显式指定目录,不要盲目解压。
参数上,JOBS默认取nproc,内存小的机器建议在外部传JOBS=2;PREFIX和WORK_DIR都允许覆盖,方便同一套脚本在开发机和交付机上跑。注意PATH在脚本开头就导出,但build_one里没有再次刷新;由于 m4、bison、flex 都装到同一前缀,PATH已经包含$PREFIX/bin,后续包能直接找到。若某个包安装后需要hash -r清 shell 命令缓存,可以在make install后加一句,避免极端环境下仍命中旧命令。
4.2 安装后校验:pcap-config、ldconfig 与最小测试程序
脚本返回 0 不等于 libpcap 可用。安装后校验至少做三件事:确认pcap-config能输出正确路径,确认业务程序能编译链接,确认运行时不缺动态库。静态安装模式下,运行时库路径问题少,但编译链接仍要检查;动态安装模式下,ldconfig或LD_LIBRARY_PATH必须验证。最小测试程序不要用太复杂的抓包逻辑,先用pcap_findalldevs列出设备,能编译、能运行、能输出设备名,就说明头文件、库和运行时链路基本通了。
# 校验 pcap-config 输出,确认路径落在独立前缀下 "$PREFIX/bin/pcap-config" --cflags "$PREFIX/bin/pcap-config" --libs # 写一个最小测试程序,只做设备枚举,不进入抓包循环 cat > "$WORK_DIR/test_pcap.c" <<'EOF' #include <pcap/pcap.h> #include <stdio.h> int main(void) { pcap_if_t *alldevs = NULL; char errbuf[PCAP_ERRBUF_SIZE]; if (pcap_findalldevs(&alldevs, errbuf) == -1) { fprintf(stderr, "pcap_findalldevs: %s\n", errbuf); return 1; } for (pcap_if_t *d = alldevs; d != NULL; d = d->next) { printf("%s\n", d->name ? d->name : "(unnamed)"); } pcap_freealldevs(alldevs); return 0; } EOF cc "$WORK_DIR/test_pcap.c" -o "$WORK_DIR/test_pcap" \ $("$PREFIX/bin/pcap-config" --cflags --libs) "$WORK_DIR/test_pcap"这段校验程序的重点是“最小闭环”。pcap_findalldevs只枚举设备,不打开抓包句柄,不需要 root 权限,适合安装后自动跑。编译命令用pcap-config --cflags --libs取参数,避免手写-I和-L时漏路径;如果静态安装,链接器会自动找libpcap.a,运行时不需要LD_LIBRARY_PATH。如果动态安装,运行前要确认$PREFIX/lib已进ldconfig或LD_LIBRARY_PATH,否则程序启动就会报共享库找不到。参数上,cc可以换成$PREFIX/bin/gcc,确保用的是新装编译器;用系统cc也能编译通过时,说明头文件和库路径已经正确暴露。
动态模式安装后,系统级前缀可以写/etc/ld.so.conf.d/offline-stack.conf并执行ldconfig,用户级前缀则不要硬写系统配置,改用offline-stack.env。校验时不要只看pcap-config --libs输出,还要实际运行测试程序;有些环境里编译链接通过,但运行期加载器找不到库,问题会拖到业务程序启动时才暴露。脚本里可以把测试程序编译和运行都放进日志,失败时直接定位是编译期还是运行期。
4.3 失败回滚与重复执行:幂等、清理和跳过已完成步骤
离线安装脚本经常要被跑第二次:第一次可能因为缺包、权限或磁盘空间失败,第二次要能继续,而不是在解压目录上重复覆盖。常见做法是用标记文件,每个包安装成功后写$PREFIX/.installed/<包名>,脚本启动时先检查标记,存在就跳过;build/目录在解压前判断是否存在,存在就复用,避免重复解压。清理不要默认删除整个build/,否则一次失败后所有编译进度都没了;提供--clean参数,让执行者显式选择从头来。
# 幂等安装的简化写法:成功标记 + 复用解压目录 mark_dir="$PREFIX/.installed" mkdir -p "$mark_dir" install_if_needed() { local name="$1" if [ -f "$mark_dir/$name" ]; then log "$name 已有安装标记,跳过" return 0 fi build_one "$@" touch "$mark_dir/$name" } # 需要从头构建时,显式清理 build 目录和标记,不默认删除 if [ "${1:-}" = "--clean" ]; then rm -rf "$BUILD_DIR" "$mark_dir" mkdir -p "$BUILD_DIR" "$mark_dir" fi这段幂等逻辑的关键是“成功才写标记”。build_one内部任何一步失败都会die退出,不会执行到touch,所以标记文件能代表该包至少完成过一次安装。--clean不默认开启,是为了保留失败现场的build/和日志;需要彻底重来时再手动清。参数上,mark_dir放在PREFIX下而不是WORK_DIR下,是为了让标记和安装前缀绑定;换前缀安装时不会误判为已完成。注意如果修改了 configure 参数,旧标记会让脚本跳过重装,这时要么换前缀,要么手动删对应标记,不要指望脚本自动识别参数变化。
脚本的退出码也要统一。die返回非 0,调用方才能判断失败;不要在失败分支里只echo不exit。离线交付时我一般会要求脚本最后打印一行“前缀路径 + 验证命令”,让接手的人知道下一步去哪看。重复执行不是脚本的附加功能,而是离线安装的常态:机器环境不一致、包不齐、权限不足都会导致第一次跑不完,能安全重跑比一次跑通更实际。
5. 避坑与排查:五个把离线安装拖回白天的现场
离线安装的坑大多不是“技术太难”,而是环境假设太多。下面五条是实际落地里最容易把时间拖长的现场,每条按现象、原因、解决写。看的时候不要只记命令,重点看“为什么会在这一步暴露”,下次换机器就能提前检查。
5.1 现象:configure 报 C compiler cannot create executables
现象是某个包的configure刚跑不久就退出,日志末尾出现C compiler cannot create executables,有时还伴随cannot find crt1.o或cannot find -lc。原因通常不是源码包坏了,而是目标机没有可用的 C 编译器,或者临时目录被挂载成noexec,编译器能生成目标文件但无法执行测试程序。离线机器上还常见系统cc指向一个不完整的工具链,--version能输出版本,实际链接却缺库。
解决顺序是先跑command -v cc、cc --version,再写一个最小 C 程序编译执行,确认初始编译器真的可用。若cc缺失,gcc 源码安装无法自举,需要先放一个预编译工具链或换用带编译器的系统镜像;若cc存在但链接失败,检查TMPDIR是否指向noexec挂载点,把它改到可执行目录,例如export TMPDIR="$WORK_DIR/tmp"。这一步不做,后面调 gcc 参数都是白费。
5.2 现象:bison 构建时报 m4 找不到或版本不满足
现象是 bison 的configure或make阶段报m4: command not found,或者提示 m4 版本低于要求。原因通常是 m4 已经装了,但PATH没有把$PREFIX/bin放到前面,bison 仍然调用系统旧 m4;也可能是 m4 安装失败但脚本没检查返回值,$PREFIX/bin/m4根本不存在。离线环境里还有一种情况是 shell 命令缓存,终端里which m4看到新路径,脚本子进程里却没有继承。
解决时先在 bison 构建前执行command -v m4和"$PREFIX/bin/m4" --version,确认新 m4 可执行;再检查PATH是否在脚本开头导出,而不是在某个子 shell 里临时设置。如果系统 m4 版本太低,不要试图让 bison 忽略检查,直接把新 m4 放到PATH最前面。脚本里可以在 m4 安装后加一句hash -r,并打印command -v m4的实际路径到日志,后面出问题能直接看出用的是哪一套。
5.3 现象:gcc make 阶段报 makeinfo 缺失
现象是 gcc 编译到文档生成阶段报makeinfo: command not found,前面编译器主体可能已经编了很久。原因是 gcc 构建过程会生成文档,makeinfo属于 texinfo 工具,离线机器不一定带;但核心编译器并不依赖文档生成,缺它不应该阻断整个安装。离线环境里为了这个工具再补一套 texinfo 源码包,时间和收益不成比例。
解决方式是在make
本文还有配套的精品资源,点击获取