Apache Websh 在 Ubuntu 22.04 集成使用指南

Ubuntu 22.04 从源码编译 Apache Websh 3.6.0b5 并挂载到 Apache httpd 2.4 的完整集成指南:tcl.h/-Werror/APR 三处兼容问题修复实录,mod_websh+prefork、线程版+event、CGI 三种方案实测对比

Apache WebshApache WebshTclhttpdUbuntuUbuntu 22.042026/10/16

Apache Websh 在 Ubuntu 22.04 集成使用指南

目标:在 Ubuntu 22.04 服务器上从源码编译 Websh 3.6.0b5,把 mod_websh 模块挂载到 Apache httpd 2.4,实现以 Tcl 编写动态网页(.ws3 脚本与内嵌标记的 .html)的完整集成流程;实测给出「默认编译 + mpm_prefork」「--enable-threads 线程版 + mpm_event」「CGI 备用」三种可行组合,并如实记录古董源码与现代 gcc/Tcl 8.6/apache2 的三处兼容问题及修复方法。

一、环境信息

本文所有命令与回显均在以下环境实测采集:操作系统 Ubuntu 22.04.5 LTS(x86_64);gcc 11.4.0、autoconf 2.71、build-essential 12.9ubuntu3;Tcl 8.6.12(tcl8.6-dev 8.6.12+dfsg-1build1);Apache httpd 2.4.52(apache2-dev 同版本);APR 1.7.0;Websh 3.6.0b5(官方归档最后一个发行版,仅源码)。站点根目录 /var/www/websh,httpd 监听 80 端口,Websh 安装于 /usr/local/bin/websh、/usr/local/lib/libwebsh3.6.0b5.so、/usr/local/lib/httpd/modules/mod_websh3.6.0b5.so。

cat /etc/os-release | grep -E "PRETTY|VERSION_ID"
apache2 -v
tclsh8.6 <<< "puts [info patchlevel]"
PRETTY_NAME="Ubuntu 22.04.5 LTS"
VERSION_ID="22.04"
Server version: Apache/2.4.52 (Ubuntu)
Server built:   2026-10-08T19:59:46
8.6.12

二、前置条件

Websh 是「Tcl 解释器 + Apache 模块」的网页开发框架,编译 mod_websh 需要同时具备 Tcl 与 httpd 的开发头文件,一次性装齐(httpd 本体未装时再执行 apt-get install -y apache2):

apt-get install -y tcl8.6-dev apache2-dev build-essential autoconf
which apxs2 tclsh8.6

输出节选:

tcl8.6-dev is already the newest version (8.6.12+dfsg-1build1).
apache2-dev is already the newest version (2.4.52-1ubuntu4.25).
0 upgraded, 0 newly installed, 0 to remove and 3 not upgraded.
/usr/bin/apxs2
/usr/bin/tclsh8.6

💡 技术拓展:Ubuntu 装的是 tcl8.6-dev 而非通用的 tcl-dev 元包,这是多架构(multiarch)布局导致的——tcl.h 放在 /usr/include/tcl8.6/ 子目录而不是 /usr/include/。这一点会直接影响第四章的 configure 探测(裸跑 ./configure 会报 tcl.h not found),请务必用带版本号的包名。

三、项目现状与源码获取

3.1 Websh 的发行状态(官网下线与归档位置实测)

Websh 是 Apache TCL 项目族的成员(同族还有 Apache rivet、mod_tcl),已进入 Apache Attic(退役项目库)。实测结果:官网 https://websh.apache.org/ 已下线,TLS 握手阶段连接即被对端中断;https://archive.apache.org/dist/websh/ 返回 404;现役官方归档地址为 https://archive.apache.org/dist/tcl/websh/source/,其下仅有 websh-3.6.0b5 一个版本的源码包及配套 .md5、.asc 文件,binary 目录为空。也就是说 3.6.0b5 就是官方归档的最终版本,没有更「新」的 3.6.0 正式版。

⚠️ 注意:网上教程若出现「Websh 3.6.0 正式版下载」之类的说法,与官方归档不符,请以 archive.apache.org 的目录列表为准。

3.2 下载源码包并校验 MD5

cd /root
wget https://archive.apache.org/dist/tcl/websh/source/websh-3.6.0b5-src.tar.gz
wget https://archive.apache.org/dist/tcl/websh/source/websh-3.6.0b5-src.tar.gz.md5

输出节选:

HTTP request sent, awaiting response... 200 OK
Length: 269549 (263K) [application/x-gzip]
2026-10-16 07:02:23 (268 KB/s) - 'websh-3.6.0b5-src.tar.gz' saved [269549/269549]
cat websh-3.6.0b5-src.tar.gz.md5
md5sum websh-3.6.0b5-src.tar.gz
MD5(websh-3.6.0b5-src.tar.gz)= 01d8772c8c2f76df9a096d404bc6783e
01d8772c8c2f76df9a096d404bc6783e  websh-3.6.0b5-src.tar.gz

MD5 完全一致。下载速度慢时可改走代理:wget -e use_proxy=yes -e http_proxy=http://192.168.1.62:7897 <URL>。

四、编译 mod_websh(三处兼容问题与修复实录)

本章按 0→1 顺序完整走一遍:解压 → autoconf → configure → make(两次失败、两次修复)→ make install → 测试验证。三处失败都是 Websh 3.6.0b5(2009 年代码)在 2020 年代工具链上的真实兼容问题。

4.1 解压与重新生成 configure

官方 INSTALL 要求先在 src/unix 目录跑 autoconf 重新生成 configure。Websh 提供三类构建产物:websh(独立 Tcl 解释器,内置 websh 包)、libwebsh(Tcl 扩展库)、mod_websh(Apache 模块),本章一并编出:

cd /root
tar xzf websh-3.6.0b5-src.tar.gz
cd websh-3.6.0b5/src/unix
autoconf

4.2 配置探测:第一处兼容问题(tcl.h not found)

先按官方文档裸跑 configure:

./configure

输出节选(尾部):

checking for Tcl public headers... configure: error: tcl.h not found. Please specify its location with --with-tcl or --with-tclinclude

configure 默认搜索路径不含 /usr/include/tcl8.6/。显式指定后重新运行:

./configure --with-tclinclude=/usr/include/tcl8.6

输出节选(尾部):

checking for Tcl public headers... /usr/include/tcl8.6
checking for building with threads... no (default)

configure 通过。两个关键点:building with threads... no (default)(默认非线程版,与第六章 MPM 选型相关);configure 报告「找到了 APR 头文件」,但如 4.4 节实测所示它并没有把 APR 路径写进 Makefile。

4.3 编译失败一:Ubuntu 加固标志 -Werror=format-security

生成的 Makefile 里 TCL_EXTRA_CFLAGS 取自 Ubuntu 的 tclConfig.sh,携带 -Werror=format-security(把格式化字符串安全告警升级为错误)。直接 make:

make

输出节选(尾部):

../generic/logtosyslog.c: In function 'logToSyslog':
../generic/logtosyslog.c:116:5: error: format not a string literal and no format arguments [-Werror=format-security]
  116 |     syslog((int) (*logToSyslogData), msg);
cc1: some warnings being treated as errors
make: *** [Makefile:259: logtosyslog.o] Error 1

logtosyslog.c 第 116 行把非字面量串直接当 syslog 格式串传参。修复:从 Makefile 去掉该标志(不改源码,保持 3.6.0b5 原样):

sed -i "s/-Werror=format-security//g" Makefile

4.4 编译失败二:APR 头文件路径缺失

make

输出节选(尾部):

/usr/include/apache2/ap_hooks.h:39:10: fatal error: apr.h: No such file or directory
   39 | #include "apr.h"
compilation terminated.
make: *** [Makefile:259: apchannel.o] Error 1

configure 曾报告 found /usr/include/apr-1.0,但生成的 Makefile 里 HTTPD_INCLUDES 只有 -I"/usr/include/apache2"——这是 3.6.0b5 configure 在新环境下的缺陷。修复:把 APR 头文件路径补进 Makefile:

sed -i "s|^HTTPD_INCLUDES = \(.*\)$|HTTPD_INCLUDES = \1 -I\"/usr/include/apr-1.0\"|" Makefile
grep "^HTTPD_INCLUDES" Makefile
HTTPD_INCLUDES = -I"/usr/include/apache2" -I"/usr/include/apr-1.0"

4.5 编译成功与产物确认

第三次 make 顺利通过,三类构建产物全部生成:

ls -la websh3.6.0b5 libwebsh3.6.0b5.so mod_websh3.6.0b5.so
-rwxr-xr-x 1 root root 1532408 Oct 16 07:03 libwebsh3.6.0b5.so
-rwxr-xr-x 1 root root 350528  Oct 16 07:03 mod_websh3.6.0b5.so
-rwxr-xr-x 1 root root 65000   Oct 16 07:03 websh3.6.0b5

三类产物全部生成。模块动态链接 libwebsh 与 libtcl8.6(ldd mod_websh3.6.0b5.so 可核,无 not found 项);APR/httpd 符号由 httpd 主进程运行时提供,无需单独链接。

4.6 安装到 /usr/local

make install

输出节选:

cp -f websh3.6.0b5 /usr/local/bin
rm -f /usr/local/bin/websh
ln -s websh3.6.0b5 /usr/local/bin/websh
cp -f libwebsh3.6.0b5.so /usr/local/lib
rm -f /usr/local/lib/libwebsh.so
ln -s libwebsh3.6.0b5.so /usr/local/lib/libwebsh.so
cp -f mod_websh3.6.0b5.so /usr/local/lib/httpd/modules
cp -f ../../doc/mod_websh/htdocs/*.* /usr/local/share/websh3.6/htdocs

安装布局:解释器 /usr/local/bin/websh(软链到带版本号实体)、扩展库 /usr/local/lib/libwebsh.so、Apache 模块 /usr/local/lib/httpd/modules/mod_websh.so、官方样例与处理器脚本 /usr/local/share/websh3.6/。

4.7 官方测试套件与独立解释器冒烟验证

make test 2>&1 | tail -8

输出节选(尾部):

test completed
webtest.ws3:    Total   471     Passed  465     Skipped  6       Failed  0

471 个用例 465 通过、6 跳过、0 失败。再对独立解释器做冒烟测试(不经 Apache 直接输出页面):

echo 'package require websh; puts [package present websh]; puts [info patchlevel]; web::put {<p>hello websh</p>}; puts {}' | websh
3.6.0b5
8.6.12
Content-Type: text/html
Generator: websh 3.6.0b5

<p>hello websh</p>

编译阶段至此全部打通。

五、集成方案 A:mod_websh 模块 + mpm_prefork(推荐)

5.1 方案说明

把 mod_websh 挂进 httpd,由 Apache 内嵌 Tcl 解释器执行 .ws3/.html 页面,解释器可跨请求复用,这是 Websh 的官方推荐形态。由于 4.2 节 configure 默认编译非线程版模块,httpd 需配套非线程化的 prefork MPM。

💡 技术拓展:Apache 2.4 的 MPM(多处理模块)决定请求并发模型——prefork 每请求一个独立子进程、无线程,兼容非线程库;worker 多进程多线程混合;event 在 worker 基础上把 keepalive 连接交给专门线程。本文默认构建的 mod_websh 未启用线程支持,因此选 prefork;线程版构建见第六章方案 B。

5.2 装载模块并注册处理器

cp /usr/local/lib/httpd/modules/mod_websh.so /usr/lib/apache2/modules/
cat > /etc/apache2/mods-available/websh.load <<'EOF'
# Apache Websh 3.6.0b5 模块加载
LoadModule websh_module /usr/lib/apache2/modules/mod_websh.so
EOF
cat > /etc/apache2/mods-available/websh.conf <<'EOF'
# mod_websh 处理器注册与全局配置入口
AddHandler websh .html
AddHandler websh .ws3
WebshConfig /etc/apache2/websh.conf
EOF
a2enmod websh
Enabling module websh.

三条指令的含义:LoadModule 装载模块本体;AddHandler websh .扩展名 把指定扩展名文件交给 websh 处理器;WebshConfig 指向全局 Tcl 配置文件(见 5.3)。

⚠️ 注意:AddHandler websh .html 会让所有 .html 文件都经过 websh 解释器。站点混有大量纯静态 .html 时建议只绑定 .ws3,避免无谓的解释器开销。

5.3 部署站点与全局配置文件

部署页面(官方样例 3 个 + 自建参数演示页 param.ws3 + 诊断页 diag.ws3)并把 DocumentRoot 指向站点目录:

mkdir -p /var/www/websh
cp /usr/local/share/websh3.6/htdocs/index.html /usr/local/share/websh3.6/htdocs/myApp.ws3 /usr/local/share/websh3.6/htdocs/other.html /var/www/websh/
cat > /var/www/websh/param.ws3 <<'EOF'
# web::param 查询参数示例
web::command default {
    web::put "<html><body>"
    web::put "<p>name = [web::param name]</p>"
    web::put "</body></html>"
}
web::dispatch
EOF
cat > /var/www/websh/diag.ws3 <<'EOF'
# mod_websh 运行时诊断页
web::command default {
    web::put "<html><body><pre>"
    web::put "classid = [web::interpcfg]\n"
    web::put "numreq = [web::interpcfg numreq]\n"
    web::put "starttime = [clock format [web::interpcfg starttime] -format %Y-%m-%dT%H:%M:%S]\n"
    web::put "script = [web::config script]\n"
    web::put "server_root = [web::config server_root]\n"
    web::put "document_root = [web::config document_root]\n"
    web::put "interpclass maxrequests = [web::interpclasscfg [web::interpcfg] maxrequests]\n"
    web::put "interpclass maxttl = [web::interpclasscfg [web::interpcfg] maxttl]\n"
    web::put "interpclass maxidletime = [web::interpclasscfg [web::interpcfg] maxidletime]\n"
    web::put "</pre></body></html>"
}
web::dispatch
EOF
sed -i "s|^DocumentRoot .*|DocumentRoot /var/www/websh|" /etc/apache2/sites-available/000-default.conf

最后写 websh 全局 Tcl 配置文件(web::interpmap、web::interpclasscfg 等参数逐项含义见《Apache Websh 配置参数详解》):

cat > /etc/apache2/websh.conf <<'EOF'
# mod_websh 全局配置(由 httpd 的 WebshConfig 指令加载)
set webshroot /usr/local/share/websh3.6

# 请求的脚本文件 -> 解释器类 映射
proc web::interpmap {file} {
    global webshroot
    # other.html 用 <?...?> 标记语法
    if {[string match */other.html $file]} {
        return "$webshroot/conf/otherhandler.ws3"
    }
    # 其余 .html 用 {...} 片段语法,共享 htmlhandler 解释器
    if {[string match *.html $file]} {
        return "$webshroot/conf/htmlhandler.ws3"
    }
    # 每个 .ws3 脚本一个独立解释器类
    if {[string match *.ws3 $file]} {
        return $file
    }
    return ""
}

# htmlhandler 解释器类常驻(0 = 不限请求数)
web::interpclasscfg "$webshroot/conf/htmlhandler.ws3" maxrequests 0
# myApp.ws3 解释器类每 5 个请求重建一次
web::interpclasscfg /var/www/websh/myApp.ws3 maxrequests 5
EOF

5.4 启动与逐页 curl 验证

确认 MPM 是 prefork(a2query -M 输出 prefork)、语法检查通过后启动(systemd 服务器等价命令为 systemctl start apache2):

a2query -M
apache2ctl configtest
prefork
Syntax OK
apache2ctl start
apache2ctl -M | grep -i websh
 websh_module (shared)

首页是内嵌 {...} 标记的 .html,其中的 [clock format [clock seconds]] 应被实时求值;响应头出现 Websh 版本标识,证明页面由 mod_websh 而非静态通道返回:

curl -s http://127.0.0.1/ | grep -A1 "Time on your server"
curl -sI http://127.0.0.1/ | head -8
<p>Time on your server is currently Fri Oct 16 07:28:21 UTC 2026.<br>
Note that the above line is written as:<pre>Time on your server is currently {web::put [clock format [clock seconds]]}.</pre>
</p>
HTTP/1.1 200 OK
Server: Apache/2.4.52 (Ubuntu)
Generator: websh 3.6.0b5
Content-Type: text/html

查询参数页(不带参数时 web::param 返回空串,页面依然 200 而非 500)与 <?...?> 标记页同样正常:

curl -s "http://127.0.0.1/param.ws3?name=websh-user"
curl -s http://127.0.0.1/other.html | grep "Time on your server"
<html><body><p>name = websh-user</p></body></html>
<p>Time on your server is currently Fri Oct 16 07:07:34 UTC 2026.<br>
Note that the above line is written as:<pre>Time on your server is currently &lt;?web::put [clock format [clock seconds]]?&gt;.</pre>

全部 200 且内容为实时渲染结果,方案 A 集成完成。

六、集成方案 B:--enable-threads 线程版 + mpm_event

官方 INSTALL 注明:与 Apache 2 的线程化 MPM(worker/event)配合需 configure 加 --enable-threads(Ubuntu 的 Tcl 8.6 默认已带线程支持)。独立目录编译线程版:

cd /root
tar xzf websh-3.6.0b5-src.tar.gz -C /tmp
mv /tmp/websh-3.6.0b5 /root/websh-3.6.0b5-threads
cd /root/websh-3.6.0b5-threads/src/unix
autoconf
./configure --enable-threads --with-tclinclude=/usr/include/tcl8.6

输出节选:

checking for building with threads... yes

应用与 4.3/4.4 节相同的两处修复后编译安装并挂入 httpd,再切换 MPM 到 event(MPM 变更必须完全重启:stop 后 start,graceful restart 会报 The MPM cannot be changed during restart.):

sed -i "s/-Werror=format-security//g" Makefile
sed -i "s|^HTTPD_INCLUDES = \(.*\)$|HTTPD_INCLUDES = \1 -I\"/usr/include/apr-1.0\"|" Makefile
make
make install
cp mod_websh3.6.0b5.so /usr/lib/apache2/modules/mod_websh.so
a2dismod mpm_prefork && a2enmod mpm_event
apache2ctl -k stop
sleep 3
rm -f /var/run/apache2/apache2.pid
apache2ctl start
a2query -M

输出节选:

checking for building with threads... yes
Module mpm_prefork disabled.
Enabling module mpm_event.
event

功能与并发压测(ab 500 请求、20 并发):

curl -s -o /dev/null -w "index -> %{http_code}\n" http://127.0.0.1/ && curl -s -o /dev/null -w "diag -> %{http_code}\n" http://127.0.0.1/diag.ws3
ab -n 500 -c 20 http://127.0.0.1/diag.ws3 2>&1 | grep -E "Complete requests|Failed requests|Non-2xx"
index -> 200
diag -> 200
Complete requests:      500
Failed requests:        0

线程版模块 + mpm_event 下 500 并发零失败,组合 B 完全可用。

七、集成方案 C:CGI 备用方案(websh 独立解释器)

websh 独立解释器也可作为 CGI 程序运行,适合无法装载 DSO 模块或仅低频页面的场景:

a2enmod cgi
cat > /usr/lib/cgi-bin/hello.tcl <<'EOF'
#!/usr/local/bin/websh
web::command default {
    web::put "<html><body>"
    web::put "<p>CGI name = [web::param name]</p>"
    web::put "<p>websh [package present websh] / Tcl [info patchlevel]</p>"
    web::put "</body></html>"
}
web::dispatch
EOF
chmod +x /usr/lib/cgi-bin/hello.tcl
apache2ctl -k stop
sleep 2
apache2ctl start
curl -s "http://127.0.0.1/cgi-bin/hello.tcl?name=cgi-user"

输出节选:

Enabling module cgi.
<html><body><p>CGI name = cgi-user</p><p>websh 3.6.0b5 / Tcl 8.6.12</p></body></html>

CGI 方式下查询参数同样经 web::param 取到。缺点是每请求 fork 一个解释器进程,性能远低于模块内嵌方式,只建议低频使用;实验后可 a2dismod cgi 并删除脚本还原。

八、三种方案对比与选型建议

维度方案 A:模块+prefork方案 B:线程版模块+event方案 C:CGI
编译方式默认 configure--enable-threads configure复用 4.6 的 websh 解释器
MPM 要求preforkevent/worker任意
解释器复用多请求复用、驻留多请求复用、线程安全每请求新建进程
实测并发200 并发 0 失败500 并发 0 失败未做并发(定位低频)
配置复杂度低中(多一次线程版编译)低
官方支持度默认构建路径INSTALL 注明的线程路径README 支持的独立解释器形态

选型结论:生产站点优先方案 A——默认编译路径最短、与官方默认行为一致;已确定要高并发长连接且愿意维护线程版二进制的环境选方案 B;无法加载 DSO 模块(受限托管环境)或仅个别脚本时用方案 C 兜底。

九、最小 .tcl 页面开发入门

9.1 命令分发与 web::cmdurl

myApp.ws3(官方样例)在同一 URL 内用 web::command 定义多个命令块,页面中 web::cmdurl other 生成指向 other 命令的链接。实测主页输出:

curl -s http://127.0.0.1/myApp.ws3 | grep -E "<h2>|request"
<h2>Main page</h2><p>Link to <a href="/myApp.ws3?XDZuRD2mxrPchT6QNqDtxaCLDENuE8V">other</a> page</p><p>This is request 0 in this specific interpreter</p>

web::cmdurl 的参数是加密 token(默认经 web::encryptd 处理),且每次生成结果不同。实测点击该链接与明文参数 ?cmd=other(cmdparam 默认值 cmd)都能分发到 other 命令,输出一致,下面用明文参数验证(提取标题):

curl -s "http://127.0.0.1/myApp.ws3?cmd=other" | grep "<h2>"
<h2>Other page</h2><p>Back to <a href="/myApp.ws3?XDZuRD2bXPt9z4oiyM8gpaBH88HaeVw7F">main</a> page</p><p>This is request 0 in this specific interpreter</p>

页面中的 "This is request N" 计数按解释器实例独立累加,prefork 下多子进程各持一套解释器,计数按子进程分摊(maxrequests 实测序列见《Apache Websh 配置参数详解》第五章)。

9.2 两种页面标记语法与转义

内嵌标记的 .html 页面支持两种语法,由处理器脚本中的 web::config putxmarkup 决定:默认 brace({...})与 tag(<?...?>),5.4 节两条 Time on your server 回显即两种语法各一个实例。要在页面里原样打印标记字符本身,用反斜杠转义:官方样例 index.html 第 11 行是活标记 {web::put ...}(被实时求值),第 12 行是转义写法 \{web::put ...\}(渲染后原样显示花括号),5.4 节首页输出第二行即第 12 行的渲染结果。

十、与 Apache rivet 的关系辨析

Websh 与 Apache rivet 同属 Apache TCL 项目族,都是「httpd 内嵌 Tcl 解释器渲染动态网页」的方案,极易混淆。实测视角下的核心差异:

维度Apache WebshApache rivet
项目状态已入 Apache Attic,官网下线,最终版 3.6.0b5(参考手册修订于 2009-09,归档包转存于 2014-03)活跃维护(3.3 版本线)
页面模型.ws3 脚本 + {...}/<?...?> 标记;web::command/web::dispatch 命令分发.rhtml 页面内嵌 Tcl;rivet 命名空间
解释器管理web::interpmap + web::interpclasscfg 显式管理解释器类与生命周期按请求/线程管理,配置更自动化
现代环境适配源码需三处修复才能在 gcc 11/Tcl 8.6/httpd 2.4 下编译(见第四章)官方持续适配新版本 httpd/Tcl

结论:新建项目选 rivet(活跃维护、现代兼容性好,集成方法见《Apache rivet 在 Ubuntu 22.04 集成使用指南》);Websh 适合维护历史系统或研究 Apache Tcl 演进史,集成时按本文修复路径执行即可。

十一、常见问题

编译环节的三类报错(tcl.h not found、-Werror=format-security、apr.h: No such file or directory)已含修复命令,见第四章实录,此处不再重复。

Q4:切换 MPM 后重启报 The MPM cannot be changed during restart.? MPM 不能在 graceful restart 过程中更换,必须 apache2ctl -k stop 后再 apache2ctl start(systemd 服务器用 systemctl restart apache2)。stop 与 start 之间若 80 端口未释放会报 Address already in use,等待数秒重试即可。

Q5:运行中直接覆盖 mod_websh.so 后出现 exit signal Segmentation fault (11)? 实测在 httpd 运行期间用新文件覆盖已装载的 mod_websh.so,子进程在后续请求中段错误(error.log 出现多条 AH00051: child pid ... exit signal Segmentation fault (11))。必须先完全停止 httpd 再替换模块文件。

Q6:WebshConfig 指向不存在的文件会怎样? apache2ctl configtest 仍显示 Syntax OK;完全重启后子进程报 couldn't read file ... 与 Could not init interp pool,请求返回 200 但页面内容异常(把 HTML 当 Tcl 执行报 invalid command name "<html>")。完整实测链见《Apache Websh 配置参数详解》3.3 节。

Q7:给新扩展名(如 .tcl)加 AddHandler websh .tcl 后页面输出为空? AddHandler 只把请求交给 websh 处理器,解释器类仍由 web::interpmap 决定;映射未覆盖的扩展名返回空类,error.log 报 Could not readWebInterpCode (id 0, class /etc/apache2/): couldn't read file "/etc/apache2/": illegal operation on a directory,页面 200 但正文为空。新增扩展名必须同步扩展 interpmap。

Q8:源码树里的 make apachetest 为什么跑不通? 该目标依赖 Websh 自带的 apachetest 工具按 Apache 1.3/2.0 时代的 httpd.conf 格式解析 LoadModule 行,在现代 Debian/Ubuntu 布局(mods-enabled 分文件加载)下报 No LoadModule line for auth_basic_module!,无法直接使用。按第五章手动集成后用 curl 逐页验证替代。

Q9:web::interpcfg starttime 格式化后是乱码年份? 该值是微秒级时间戳(实测原始值 1792134956101788,对应 2026-10-16),用 clock format 按秒格式化会得到异常年份,需先除以 1000000。

十二、卸载与清理

a2dismod websh
Module websh disabled.
rm -f /etc/apache2/mods-available/websh.load /etc/apache2/mods-available/websh.conf /etc/apache2/mods-enabled/websh.load /etc/apache2/mods-enabled/websh.conf
rm -f /usr/lib/apache2/modules/mod_websh.so
rm -rf /var/www/websh
rm -f /usr/local/bin/websh /usr/local/bin/websh3.6.0b5
rm -f /usr/local/lib/libwebsh.so /usr/local/lib/libwebsh3.6.0b5.so
rm -rf /usr/local/lib/httpd /usr/local/share/websh3.6
rm -rf /root/websh-3.6.0b5 /root/websh-3.6.0b5-threads /root/websh-3.6.0b5-src.tar.gz /root/websh-3.6.0b5-src.tar.gz.md5

十三、参考链接

  • Websh 官方归档(源码下载):https://archive.apache.org/dist/tcl/websh/source/
  • 源码包内官方说明:INSTALL(编译参数)、README(模块安装步骤)、doc/quickref.xml(Websh Reference 3.6.0b5)
  • Apache Attic(退役项目说明):https://attic.apache.org/
  • 同族活跃方案:《Apache rivet 在 Ubuntu 22.04 集成使用指南》
  • 配置参数逐项说明:《Apache Websh 配置参数详解》