NaviServer 最近釋出了一系列的新版,4.99.34, 5.0.6, 與 5.1.1。
5.1 系列是個重要的版本更新,自 5.1.0 開始實驗性的支援 HTTP/3 協定(透過 nghttp3,需要 OpenSSL 4.0 以上的版本),從 5.1.1 開始 HTTP/3 實作的狀態從 expermiental 變更為 ready-for-use。
NaviServer 最近釋出了一系列的新版,4.99.34, 5.0.6, 與 5.1.1。
5.1 系列是個重要的版本更新,自 5.1.0 開始實驗性的支援 HTTP/3 協定(透過 nghttp3,需要 OpenSSL 4.0 以上的版本),從 5.1.1 開始 HTTP/3 實作的狀態從 expermiental 變更為 ready-for-use。
NaviServer 目前提供了二種資料庫介面, nsdb 與 後來擴充一些功能的 nsdbi。 nsdb 為 NaviServer 的內建模組(只是需要自行設定載入模組),而 nsdbi 需要使用者自己編譯並且加入到 NaviServer。
注意:如果想要使用 OpenACS 並且資料庫使用 PostgreSQL,那麼必須安裝 nsdbpg 模組。 下面使用 PostgreSQL 測試 nsdb 以及 nsdbpg 模組。使用者需要編譯 nsdbpg 模組並且加入到 NaviServer。 如果要自己編譯 nsdbpg 模組,注意其 Makefile 中 NAVISERVER 所設定的位置, 以及 dbpg.h 中 pg_config.h 和 libpq-fe.h 標頭檔的位置(openSUSE 安裝的位置放在 /usr/include/pgsql)。 下面是標示差異點的 patch 檔案。
--- dbpg.h.org 2025-12-03 16:37:40.171256167 +0800
+++ dbpg.h 2025-12-03 16:37:58.707205142 +0800
@@ -25,7 +25,7 @@
* pg_config.h. However, the PACKAGE_* macros conflict with
* NaviServer's packaging information, so we drop these.
*/
-#include <pg_config.h>
+#include <pgsql/pg_config.h>
#undef PACKAGE_VERSION
#undef PACKAGE_TARNAME
#undef PACKAGE_STRING
@@ -34,7 +34,7 @@
#undef PACKAGE_URL
#include <nsdb.h>
-#include <libpq-fe.h>
+#include <pgsql/libpq-fe.h>
/*
* Forward compatibility, in case a new version of the module is compiled
--- Makefile.org 2025-12-03 16:39:07.524319290 +0800
+++ Makefile 2025-12-03 16:39:23.699037548 +0800
@@ -27,7 +27,7 @@
# version of this file under either the License or the GPL.
ifndef NAVISERVER
- NAVISERVER = /usr/local/ns
+ NAVISERVER = /var/lib/naviserver
endif
#
不過除了直接修改,也可以使用設定參數的方式,下面就是編譯與安裝的指令:
make NAVISERVER=/var/lib/naviserver PGLIB=/usr/lib64 PGINCLUDE=/usr/include/pgsql
sudo make NAVISERVER=/var/lib/naviserver install
在安裝完 nsdbpb 以後,接下來修改 nsd-config.tcl,首先加入相關的變數:
# For database
dict set defaultConfig db_name danilo
dict set defaultConfig db_user danilo
dict set defaultConfig db_password danilo
dict set defaultConfig db_host localhost
dict set defaultConfig db_port 5432
加入 nsdb 模組,讓 NaviServer 在啟動時會載入模組。
ns_section ns/server/default/modules {
if {$nscpport ne ""} {ns_param nscp nscp}
ns_param nslog nslog
ns_param nscgi nscgi
ns_param nsperm nsperm
ns_param nsdb nsdb
ns_param revproxy tcl
}
然後加入 nsdbpg driver 的設定。
ns_section ns/db/drivers {
ns_param postgres nsdbpg
}
接下來加入 connection pool 的設定。使用者可以設定不止一個 pool,我在這裡只是驗證如何設定,所以只加入一個。
ns_section ns/server/default/db {
#ns_param Pools pool1,pool2
ns_param Pools pool1
ns_param defaultpool pool1
}
ns_section ns/db/pools {
ns_param pool1 "This is pool1 for PostgreSQL"
}
ns_section ns/db/pool/pool1 {
ns_param Connections 10
ns_param LogMinDuration 10ms
ns_param LogSQLerrors false
ns_param driver postgres
ns_param DataSource ${db_host}:${db_port}:${db_name}
ns_param user $db_user
ns_param password $db_password
}
最後寫一個簡單的測試程式測試。
<%
ns_adp_puts "NaviServer nsdbpg module - get PostgreSQL version<br>"
set db [ns_db gethandle]
set result [ns_db 1row $db "select version() as version"]
ns_adp_puts [ns_set get $result version]
%>
下面嘗試安裝 nsdbi 以及 nsdbipg。
首先是 nsdbi,下面就是編譯與安裝的指令:
make NAVISERVER=/var/lib/naviserver
sudo make NAVISERVER=/var/lib/naviserver install
再來是 nsdbipg,下面就是編譯與安裝的指令:
make NAVISERVER=/var/lib/naviserver PGLIB=/usr/lib64 PGINCLUDE=/usr/include/pgsql
sudo make NAVISERVER=/var/lib/naviserver install
接下來修改 nsd-config.tcl,首先是加入模組(有二種方式,一種是全域的加入方法,一種是 per server 的加入方式, 這裡使用後一種):
ns_section ns/server/default/modules {
if {$nscpport ne ""} {ns_param nscp nscp}
ns_param nslog nslog
ns_param nscgi nscgi
ns_param nsperm nsperm
ns_param revproxy tcl
ns_param nsdbipg1 nsdbipg
}
以及 nsdbipg1 的設定:
ns_section ns/server/default/module/nsdbipg1 {
ns_param default true ;# This is the default pool
ns_param handles 10 ;# Max open handles to db.
ns_param maxwait 10 ;# Seconds to wait if handle unavailable.
ns_param maxidle 0 ;# Handle closed after maxidle seconds if unused.
ns_param maxopen 0 ;# Handle closed after maxopen seconds, regardless of use.
ns_param maxqueries 0 ;# Handle closed after maxqueries SQL queries.
ns_param checkinterval 600 ;# Check for idle handles every 10 minutes.
ns_param datasource "host='localhost' port=5432 user='danilo' password='danilo' dbname='danilo'"
}
最後寫一個簡單的測試程式測試。
<%
ns_adp_puts "NaviServer nsdbipg module - get PostgreSQL version<br>"
dbi_1row -array result {select version() as version}
ns_adp_puts $result(version)
%>
如果將取得結果回傳為一個串列:
set result [dbi_rows {select a, b from t}]
foreach {a b} $result {
lappend lresult [list $a $b]
}
nsdbi 使用 dbi_dml 執行 insert/update/delete 指令。nsdbi 支援 bind variable,使用 :variable 的形式來代表變數。
dbi_dml {insert into test values (3, 'z')}
dbi_dml {insert into test (a, b, c) values (:a, :b, :c)}
set numRows [dbi_dml {update test set b = 'z' where a = 1}]
dbi_dml {delete from test where a = 3}
要注意的是,database handle 由 NaviServer 所管理,所以你在測試程式並沒有看到開啟 handle 的動作。
另外,如果使用 NaviServer 提供的資料庫介面,建議修改 systemd service 檔案 nsd.service, 以 PostgreSQL 來說,最好加入 postgresql.service 的部份,所以可以改為如下:
[Unit]
Description=NaviServer
#After=network.target
After=network.target postgresql.service
Wants=postgresql.service
NaviServer 本身支援 ASP/PHP 類似的寫法,可以內嵌 Tcl 程式碼到網頁中,
稱為 NaviServer Dynamic Pages (ADPs),其副檔名為 adp,可以參考下列的網頁:
NaviServer ADP Development
主要的語法有以下三種,第一種為使用 script 標籤:
<script language="tcl" runat="server" stream="on">
...
</script>
第二種則是短標籤:
<% ... %>
第三種則是印出變數的短標籤:
<%= ... %>
NaviServer 已經內建 nscgi 模組。檢查 nsd-config.tcl(或者是你使用的設定檔),確定有載入模組:
ns_section ns/server/default/modules {
if {$nscpport ne ""} {ns_param nscp nscp}
ns_param nslog nslog
ns_param nscgi nscgi
ns_param nsperm nsperm
ns_param revproxy tcl
}
如果想要使用以前使用 Tcl 寫的 CGI 程式、或者是要使用 NaviServer 練習撰寫 CGI 程式, 只要注意 NaviServer cgi-bin 設定的位置正確的放置檔案,以及正確的加入 Tcl interpreter 的設定即可。 接下來修改 nsd-config.tcl:
ns_section ns/server/default/module/nscgi {
ns_param map "GET /cgi-bin $home/cgi-bin"
ns_param map "POST /cgi-bin $home/cgi-bin"
ns_param interps CGIinterps
#ns_param allowstaticresources true ;# default false; serve static resources from cgi directories
}
ns_section ns/interps/CGIinterps {
ns_param .pl "/usr/bin/perl"
ns_param .tcl "/usr/bin/tclsh"
ns_param .sh "/bin/bash"
}
PHP 目前在網頁伺服器主要有三種執行的方式,使用 CGI,使用 FastCGI 以及內嵌到網頁伺服器裡。 如果使用 PHP 8.x,php cgi 程式 (php-cgi) 在 openSUSE Tumbleweed 的套件為 php8-fastcgi。 如果想透過 NaviServer 使用 CGI 的方式執行 PHP 是可行的,只要修改 nsd-config.tcl 加入 PHP interpreter 的設定:
ns_section ns/interps/CGIinterps {
ns_param .pl "/usr/bin/perl"
ns_param .php "/usr/bin/php-cgi"
ns_param .tcl "/usr/bin/tclsh"
ns_param .sh "/bin/bash"
}
然後寫一個測試程式:
<?php
phpinfo();
?>
下面的程式使用 TclCurl 自網站下載 ATOM XML 的資料, 下載以後使用 tDom 分析並且將 title 與 link 的資料儲存為 html 格式。 只有使用自己的部落格資料測試過。
#!/usr/bin/env tclsh
package require TclCurl
package require tdom
proc get_atom {url} {
try {
set curlHandle [curl::init]
$curlHandle configure -url $url -bodyvar result
$curlHandle setopt CURLOPT_HTTP_VERSION 2TLS
catch { $curlHandle perform } curlErrorNumber
if { $curlErrorNumber != 0 } {
throw error [curl::easystrerror $curlErrorNumber]
}
} on error {em} {
error "Error: $em"
} finally {
$curlHandle cleanup
}
return $result
}
proc parse {XML ofname} {
set doc [dom parse $XML]
set root [$doc documentElement]
set ns {xmlns http://www.w3.org/2005/Atom}
$doc selectNodesNamespaces $ns
set titleList [$root selectNodes //xmlns:entry/xmlns:title]
set linkList [$root selectNodes {//xmlns:entry/xmlns:link[@rel='alternate']}]
set out [open $ofname w 0666]
foreach tnode $titleList lnode $linkList {
set ntitle [$tnode text]
set nlink [$lnode getAttribute href]
puts $out "<a href=\"$nlink\">$ntitle</a><br>"
}
close $out
}
if {$argc == 2} {
set url [lindex $argv 0]
set ofile [lindex $argv 1]
} else {
puts "Usage:"
puts "\ttclsh atom2html.tcl url filename"
exit
}
if {[catch {set data [get_atom $url]} err]} {
puts $err
} else {
parse $data $ofile
}
Eclipse Mosquitto 是一套開放原始碼的 MQTT Broker。
在 openSUSE 安裝:
sudo zypper in mosquitto
如果要執行的話,使用 systemctl 啟動服務:
sudo systemctl start mosquitto
再來使用 tcl.mqttc 驗證是否可以正確連線。
Subscribe:
package require mqttc
mqttc client "tcp://localhost:1883" "USERSSub" 1 -cleansession 1
client subscribe "MQTT Examples" 1
while 1 {
if {[catch {set result [client receive]}]} {
puts "Receive error!!!"
break
}
if {[llength $result] > 0} {
puts "[lindex $result 0] - [lindex $result 1]"
if {![string compare -nocase [lindex $result 1] "Exit"]} {
break
}
}
}
client unsubscribe "MQTT Examples"
client close
Publish:
package require mqttc
mqttc client "tcp://localhost:1883" "USERSPub" 1 -timeout 1000
client publishMessage "MQTT Examples" "Hello MQTT!" 1 0
client publishMessage "MQTT Examples" "Exit" 1 0
client close
先執行 Subscribe 的部份,再使用 Publish 發送訊息,確定可以正確執行。
下面是另外一個使用 tcl.mqttc 測試 Subscribe 與 Publish 功能的測試程式:
package require Thread
package require mqttc
catch {console show}
set ::gThread [thread::create {thread::wait} ]
set result 0
proc subscribe { } {
thread::send -async $::gThread {
package require mqttc
mqttc client "tcp://localhost:1883" "USERTest1" 1 -cleansession 1
client subscribe "MQTT Examples" 1
while 1 {
if {[catch {set result [client receive]}]} {
puts "Receive error!!!"
break
}
if {[llength $result] > 0} {
puts "[lindex $result 0] - [lindex $result 1]"
if {![string compare -nocase [lindex $result 1] "Exit"]} {
break
}
}
}
client unsubscribe "MQTT Examples"
client close
} ::result
}
subscribe
puts "started test..."
after 250
mqttc client "tcp://localhost:1883" "USERTest2" 1 -timeout 1000
client publishMessage "MQTT Examples" "Hello MQTT!" 1 0
client publishMessage "MQTT Examples" "Exit" 1 0
client close
vwait ::result
Apache HTTP Server 是 Apache 軟體基金會的一個開放原始碼的網頁伺服器軟體,可以在大多數電腦作業系統中運行, 以其高擴充的能力以及眾多的 module 著稱。雖然現在效能被其它的網頁伺服器超過,但是也仍然是一個高效能的伺服器。
在 openSUSE 安裝 Apache2:
sudo zypper install apache2
檢查 Apache 版本:
sudo httpd -v
建立 ssl.conf 設定檔
[req]
prompt = no
default_md = sha256
default_bits = 2048
distinguished_name = dn
x509_extensions = v3_req
[dn]
C = TW
ST = Taiwan
L = Taipei
O = Orange Inc.
OU = IT Department
emailAddress = admin@example.com
CN = localhost
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = *.localhost
DNS.2 = localhost
IP.1 = 127.0.0.1
透過指令建立開發測試用途的自簽憑證
openssl req -x509 -new -nodes -sha256 -utf8 -days 3650 -newkey rsa:2048 \
-keyout apache.key -out apache.crt -config ssl.conf
將 apache.key 複製到 ssl.key 目錄,apache.crt 複製到 ssl.crt 目錄(需要使用 su 切換到 root 身份或者使用 sudo)。
將 /etc/apache2/vhosts.d/vhost-ssl.template 複製為 vhost-ssl.conf,並修改內容如下:
# Template for a VirtualHost with SSL
# Note: to use the template, rename it to /etc/apache2/vhost.d/yourvhost.conf.
# Files must have the .conf suffix to be loaded.
#
# See /usr/share/doc/packages/apache2/README.QUICKSTART for further hints
# about virtual hosts.
#
# This is the Apache server configuration file providing SSL support.
# It contains the configuration directives to instruct the server how to
# serve pages over an https connection. For detailing information about these
# directives see http://httpd.apache.org/docs/2.4/mod/mod_ssl.html
#
# Do NOT simply read the instructions in here without understanding
# what they do. They're here only as hints or reminders. If you are unsure
# consult the online docs. You have been warned.
#
<IfDefine SSL>
<IfDefine !NOSSL>
##
## SSL Virtual Host Context
##
<VirtualHost _default_:443>
# General setup for the virtual host
DocumentRoot "/srv/www/htdocs"
#ServerName www.example.com:443
#ServerAdmin webmaster@example.com
ErrorLog /var/log/apache2/error_log
TransferLog /var/log/apache2/access_log
# SSL Engine Switch:
# Enable/Disable SSL for this virtual host.
SSLEngine on
# OCSP Stapling:
# Enable/Disable OCSP for this virtual host.
SSLUseStapling on
# You can use per vhost certificates if SNI is supported.
SSLCertificateFile /etc/apache2/ssl.crt/apache.crt
SSLCertificateKeyFile /etc/apache2/ssl.key/apache.key
#SSLCertificateChainFile /etc/apache2/ssl.crt/vhost-example-chain.crt
# Per-Server Logging:
# The home of a custom SSL log file. Use this when you want a
# compact non-error SSL logfile on a virtual host basis.
CustomLog /var/log/apache2/ssl_request_log ssl_combined
<Directory />
Options FollowSymLinks
AllowOverride All
# Order deny,allow
# Deny from all
</Directory>
<Directory /srv/www/htdocs>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
</Directory>
</VirtualHost>
</IfDefine>
</IfDefine>
主要就是 SSLCertificateFile 與 SSLCertificateKeyFile 這二個項目的設定,端視所放置的位置。
Apache 提供不同的 multiprocessing modules (MPMs) 可以使用,主要有下列的分別:
HTTP/2 支援無法在 Prefork MPM 下執行,所以需要設定使用其它的 MPM,目前建議使用 Event MPM。
Enable http2 mod:
sudo a2enmod http2
Enable HTTP2 flag:
sudo a2enflag HTTP2
Switch from prefork to event:
sudo zypper remove apache2-prefork
sudo zypper install apache2-event
In /etc/sysconfig/apache2 make sure to set
APACHE_MPM="event"
與
APACHE_START_TIMEOUT="10"
重啟 Apache 2:
sudo systemctl restart apache2
To automatically start the apache server after a reboot:
sudo systemctl enable apache2
Apache 內建支援 CGI,default-server.conf 在目錄內啟用 CGI 與設定 handler 的設定如下:
ScriptAlias /cgi-bin/ "/srv/www/cgi-bin/"
# "/srv/www/cgi-bin" should be changed to whatever your ScriptAliased
# CGI directory exists, if you have that configured.
#
<Directory "/srv/www/cgi-bin">
AllowOverride None
Options +ExecCGI -Includes
<IfModule !mod_access_compat.c>
Require all granted
</IfModule>
<IfModule mod_access_compat.c>
Order allow,deny
Allow from all
</IfModule>
</Directory>
而 Options +ExecCGI 就是允許執行 CGI 的設定。
檢查目前載入的模組:
sudo apache2ctl -M
(如果沒有 apache2ctl 這個指令,可以嘗試使用 sudo httpd -M)
Apache mod_fcgid 是 Apache Http Server 用來支援 FastCGI 協定的模組。 FastCGI 是另外一種克服 CGI 缺點的發展路線,也就是將網頁伺服器與應用程式伺服器分開的方式, 而程式會持續執行,這樣就不需要每一個請求都需要產生一個子行程來運行。
下面是在 openSUSE 安裝的方式:
sudo zypper install apache2-mod_fcgid
接下來設定 Apache Http Server 的部份。
首先允許必要的模式:
sudo a2enmod proxy
sudo a2enmod proxy_fcgi
sudo a2enmod setenvif
sudo a2enmod fcgid
下面使用的方式配合 spawn-fcgi。
我們需要撰寫 spawn-fcgi 的 systemd service,在 /usr/lib/systemd/system 目錄下建立 spawnfcgi.service,
通訊使用 UNIX Socket,內容如下:
[Unit]
Description=Spawn FCGI service
After=nss-user-lookup.target
[Service]
Type=forking
Environment=WORKERS=1
ExecStart=/usr/bin/spawn-fcgi \
-F ${WORKERS} \
-u wwwrun \
-g www \
-s /var/run/%p.sock \
-P /var/run/%p.pid \
-- /usr/bin/rivet-fcgi
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
如果使用 TCP:
Description=Spawn FCGI service
After=nss-user-lookup.target
[Service]
Type=forking
Environment=WORKERS=3
ExecStart=/usr/bin/spawn-fcgi \
-F ${WORKERS} \
-u wwwrun \
-g www \
-a 127.0.0.1 -p 9000 \
-P /var/run/%p.pid \
-- /usr/bin/rivet-fcgi
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
(其中 -u 指定 user,-g 指定 group,隨著平台的不同可能有不同的設定。
rivet-fcgi 是我自己撰寫的工具。)
下面是測試我自己寫的工具的設定。
修改 /etc/apache2/conf.d 目錄下的檔案 mod_fcgid.conf,通訊使用 UNIX Socket,內容如下:
##
#<FilesMatch "\.php$">
# AddHandler fcgid-script .php
# Options +ExecCGI
# FcgidWrapper /srv/www/cgi-bin/php5 .php
#</FilesMatch>
##
DirectoryIndex index.rvt index.tcl
<FilesMatch "\.(rvt|tcl)$">
SetHandler "proxy:unix:/var/run/spawnfcgi.sock|fcgi://localhost/"
#CGIPassAuth on
</FilesMatch>
如果使用 TCP:
DirectoryIndex index.rvt index.tcl
<FilesMatch "\.(rvt|tcl)$">
SetHandler "proxy:fcgi://127.0.0.1:9000/"
#CGIPassAuth on
</FilesMatch>
而後啟動(或重新啟動)spawn-fcgi 與 apache2 的服務,接著進行測試是否有正確設定。
(注意:PHP-FPM 也可以使用類似的設定手法,只是 spawn-fcgi 設定的部份變成為設定 php-fpm 的參數,
而 web server 這邊的副檔名參數與連線參數需要修改。)
這是一個簡單的程式,列出目前環境中 ODBC 的 data sources。
#!/usr/bin/env tclsh
#
# A simple program to list ODBC Data Sources by Tcl/Tk, version 0.1
#
package require Tcl 8.6
package require Tk
package require tablelist
package require tdbc::odbc
if { [catch {package require awthemes}]==0} {
ttk::setTheme "awlight"
}
wm geometry . 600x400+100+100
frame .menubar -relief raised -bd 2
pack .menubar -side top -fill x
ttk::menubutton .menubar.file -text File -menu .menubar.file.menu
menu .menubar.file.menu -tearoff 0
.menubar.file.menu add command -label Quit -command Exit
ttk::menubutton .menubar.help -text Help -menu .menubar.help.menu
menu .menubar.help.menu -tearoff 0
.menubar.help.menu add command -label About -command HelpAbout
pack .menubar.file .menubar.help -side left
# Contextual Menus
menu .menu
foreach i [list Exit] {
.menu add command -label $i -command $i
}
if {[tk windowingsystem]=="aqua"} {
bind . <2> "tk_popup .menu %X %Y"
bind . <Control-1> "tk_popup .menu %X %Y"
} else {
bind . <3> "tk_popup .menu %X %Y"
}
# Get data sources list and list
tablelist::tablelist .t -columns {0 "DSN" 0 "Driver"} -stretch all \
-background white -font {Helvetica -14}
pack .t -fill both -expand 1 -side top
set sources [::tdbc::odbc::datasources]
foreach {dsn driver} $sources {
.t insert end [list $dsn $driver]
}
# Handle special key
bind all <F1> HelpAbout
#=================================================================
# Event Handler
#=================================================================
proc Exit {} {
set answer [tk_messageBox -message "Really quit?" -type yesno -icon warning]
switch -- $answer {
yes exit
}
}
proc HelpAbout {} {
set ans [tk_messageBox -title "About" -type ok -message \
"Using TDBC-ODBC to list ODBC Data Sources." ]
}
下面的程式使用 TclCurl 自網站下載 RSS XML 的資料, 下載以後使用 tDom 分析並且將 title 與 link 的資料儲存為 html 格式。
#!/usr/bin/env tclsh
package require TclCurl
package require tdom
proc get_rss {url} {
try {
set curlHandle [curl::init]
$curlHandle configure -url $url -bodyvar result
$curlHandle setopt CURLOPT_HTTP_VERSION 2TLS
catch { $curlHandle perform } curlErrorNumber
if { $curlErrorNumber != 0 } {
throw error [curl::easystrerror $curlErrorNumber]
}
} on error {em} {
error "Error: $em"
} finally {
$curlHandle cleanup
}
return $result
}
proc parse {XML ofname} {
set doc [dom parse $XML]
set root [$doc documentElement]
set titleList [$root selectNodes //item/title]
set linkList [$root selectNodes //item/link]
set out [open $ofname w 0666]
foreach tnode $titleList lnode $linkList {
set ntitle [$tnode text]
set nlink [$lnode text]
puts $out "<a href=\"$nlink\">$ntitle</a><br>"
}
close $out
}
if {$argc == 2} {
set url [lindex $argv 0]
set ofile [lindex $argv 1]
} else {
puts "Usage:"
puts "\ttclsh rss2html.tcl url filename"
exit
}
if {[catch {set data [get_rss $url]} err]} {
puts $err
} else {
parse $data $ofile
}
unixODBC 提供了 isql 這個命令列工具。要注意的是,有不少資料庫的命令列工具也取名為 isql,使用前應該先使用下列的指令確定:
isql --version
下面是指令的參數:
isql DSN [USER [PASSWORD]] [options]
下面的範例是使用 DSN=PostgreSQL 查詢資料庫版本,並且輸出為 html 的例子:
echo "select version() as version" | isql PostgreSQL -b -w > result.html
(-b 代表不要 interactive mode,而 -w 代表輸出 html table 結果。)
然後接下來撰寫一個 Tcl 程式測試:
#!/usr/bin/env tclsh
if {$argc >= 1} {
set statement [lindex $argv 0]
} elseif {$argc == 0} {
puts "Please input a SQL statement"
exit
}
set var [list echo $statement | isql PostgreSQL -b -w > result.html]
exec {*}$var
Lighttpd 是一款以 BSD 授權條款開源的網頁伺服器, 設計目的以安全、快速、小巧為主要設計理念。Lighttpd 和 Nginx 都是主打性能(二者的效能在伯仲之間)的網頁伺服器, 同時在使用情況上有一定範圍的重覆,只是 Lighttpd 在開始搶佔市佔率的時候被發現有一些 memory leak 的問題, 雖然在之後陸陸續續問題有所修正,但是在之後已無法取得更多的市佔率。
即使如此,當使用的情況主要是以靜態網頁為主並且不需要太多功能(Lighttpd 的 memory leak 主要發生在動態網頁, 而 Lighttpd 在靜態網頁方面十分高效),或者是在硬體有一些限制的情況下,需要設定簡單、輕量級的網頁伺服器, 就十分適合使用 Lighttpd。另外,和 Nginx 不同的是,Lighttpd 同時支援 CGI 與 FastCGI。
Install Lighttpd (@openSUSE):
sudo zypper install lighttpd lighttpd-mod_openssl
注意:Lighttpd 的 conf 目錄在 openSUSE 權限設定為 640,所以一般使用者無法直接讀取。 另外,lighttpd-mod_openssl 視安裝的版本而定,需要檢查才知道是否需要安裝
使用下列的指令查看 Lighttpd 的版本資訊,有 ssl 字樣表示支援 SSL。
sudo lighttpd -v
如果要啟動服務,使用下列的指令:
sudo systemctl start lighttpd.service
如果要查詢目前服務的狀態,使用下列的指令:
sudo systemctl status lighttpd.service
如果要停止服務,使用下列的指令:
sudo systemctl stop lighttpd.service
如果要重新啟動服務,使用下列的指令:
sudo systemctl restart lighttpd.service
接下來使用自簽憑證設定 HTTPS。
首先建立 ssl.conf 設定檔:
[req]
prompt = no
default_md = sha256
default_bits = 2048
distinguished_name = dn
x509_extensions = v3_req
[dn]
C = TW
ST = Taiwan
L = Taipei
O = Orange Inc.
OU = IT Department
emailAddress = admin@example.com
CN = localhost
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = *.localhost
DNS.2 = localhost
IP.1 = 127.0.0.1
透過指令建立開發測試用途的自簽憑證:
openssl req -x509 -new -nodes -sha256 -utf8 -days 3650 \
-newkey rsa:2048 -keyout lighttpd.pem -out lighttpd.pem -config ssl.conf
(注意:如果是其它需要使用 private key 與 public key 的 server, 那麼 -keyout 可以設置為 lighttpd.key,而 -out 可以設置為 lighttpd.csr,以此類推)
可以透過以下指令查看 lighttpd.pem 內容:
openssl x509 -in lighttpd.pem -text
建立 /etc/lighttpd/ssl 目錄,將 lighttpd.pem 複製到 /etc/lighttpd/ssl 目錄下。
修改 /etc/lighttpd/modules.conf,加入下列的內容:
server.modules = (
# "mod_rewrite",
"mod_access",
# "mod_auth",
# "mod_authn_file",
# "mod_redirect",
# "mod_setenv",
# "mod_alias",
"mod_openssl",
)
修改 /etc/lighttpd/lighttpd.conf,加入下列的內容:
$SERVER["socket"] == ":443" {
ssl.engine = "enable"
ssl.pemfile = "/etc/lighttpd/ssl/lighttpd.pem"
server.name = "localhost"
server.document-root = "/srv/www/htdocs/"
}
Lighttpd 自 1.4.56 開始支援 HTTP/2(目前預設值為啟用)。
如果你想要嘗試 enable/disable HTTP/2,使用下列的設定:
server.feature-flags += ("server.h2proto" => "enable")
接下來加入 CGI 支援。
修改 /etc/lighttpd/modules.conf,修改並加入下列的內容:
server.modules = (
# "mod_rewrite",
"mod_access",
# "mod_auth",
# "mod_authn_file",
# "mod_redirect",
# "mod_setenv",
"mod_alias",
"mod_cgi",
"mod_openssl",
)
修改 /etc/lighttpd/lighttpd.annotated.conf,加入下列的內容(因為要使用 Tcl 寫 CGI 程式, 所以要避免被視為靜態檔案):
static-file.exclude-extensions = ( ".tcl", ".php", ".pl", ".fcgi", ".scgi" )
Lighttpd 的 CGI 主要可以有二種風格的設定方式,一種是以 file extension 為主, 修改 /etc/lighttpd/lighttpd.conf:
cgi.assign = ( ".tcl" => "/usr/bin/tclsh",
".cgi" => "/usr/bin/tclsh" )
另外一種則是配合 mod_alias,將 CGI 程式放在 /cgi-bin:
# For execute CGI in /cgi-bin
alias.url += ( "/cgi-bin" => server_root + "/cgi-bin" )
$HTTP["url"] =~ "^/cgi-bin" {
cgi.assign = ( "" => "" )
}
要使用何種風格的設定方式看使用者的喜好。
接下來是 FastCGI 的設定。
修改 /etc/lighttpd/modules.conf,修改並加入下列的內容:
server.modules = (
# "mod_rewrite",
"mod_access",
# "mod_auth",
# "mod_authn_file",
# "mod_redirect",
# "mod_setenv",
"mod_alias",
"mod_cgi",
"mod_fastcgi",
"mod_openssl",
)
下面使用的方式配合 spawn-fcgi。
我們需要撰寫 spawn-fcgi 的 systemd service,在 /usr/lib/systemd/system 目錄下建立 spawnfcgi.service,內容如下:
[Unit]
Description=Spawn FCGI service
After=nss-user-lookup.target
[Service]
Type=forking
Environment=WORKERS=1
ExecStart=/usr/bin/spawn-fcgi \
-F ${WORKERS} \
-u lighttpd \
-g lighttpd \
-a 127.0.0.1 -p 9000 \
-P /var/run/%p.pid \
-- /usr/bin/rivet-fcgi
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
(其中 -u 指定 user,-g 指定 group,隨著平台的不同可能有不同的設定。)
下面是測試我自己寫的工具 rivet-fcgi 的設定, 修改 /etc/lighttpd/lighttpd.conf:
fastcgi.server = (
".rvt" =>
(( "host" => "127.0.0.1",
"port" => 9000,
"docroot" => "/srv/www/htdocs"
)),
".tcl" =>
(( "host" => "127.0.0.1",
"port" => 9000,
"docroot" => "/srv/www/htdocs"
))
)
而後啟動(或重新啟動)spawn-fcgi 與 lighttpd 的服務,接著進行測試是否有正確設定。
php-fpm 設定檔在 openSUSE 主要為 /etc/php8/fpm/php-fpm.d/www.conf。在安裝以候使用預設值就可以順利執行, 如果有需要再視自己的需求修改。
修改 /etc/lighttpd/lighttpd.conf,加入下面的設定:
fastcgi.server = (
".php" =>
(( "host" => "127.0.0.1",
"port" => 9000,
"docroot" => "/srv/www/htdocs"
))
)
而後啟動(或重新啟動)spawn-fcgi 與 lighttpd 的服務,接著進行測試是否有正確設定。 在 /srv/www/htdocs 目錄下建立 info.php:
<?php
phpinfo();
?>
如果瀏緊 info.php 可以得到資訊,就表示環境架設成功。
Nginx 是非同步框架的網頁伺服器,在靜態檔案的效能上十分高效, 而且時常被用來作為反向代理、Http Cache、負載平衡器。
Install nginx (@openSUSE):
sudo zypper install nginx
如果要作為提供靜態檔案服務的 web server,openSUSE 的預設設定已經足夠, 下面只是我個人習慣更新 /etc/nginx/nginx.conf 加入下面的設定:
server {
listen 80;
listen [::]:80;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {
root /srv/www/htdocs;
try_files $uri/ $uri =404;
index index.html index.htm;
}
接下來使用自簽憑證設定 HTTPS。
首先建立 ssl.conf 設定檔:
[req]
prompt = no
default_md = sha256
default_bits = 2048
distinguished_name = dn
x509_extensions = v3_req
[dn]
C = TW
ST = Taiwan
L = Taipei
O = Orange Inc.
OU = IT Department
emailAddress = admin@example.com
CN = localhost
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = *.localhost
DNS.2 = localhost
IP.1 = 127.0.0.1
透過指令建立開發測試用途的自簽憑證:
openssl req -x509 -new -nodes -sha256 -utf8 -days 3650 \
-newkey rsa:2048 -keyout nginx.key -out nginx.crt -config ssl.conf
將 nginx.key 與 nginx.crt 複製到 /etc/nginx/ssl 目錄(需要使用 su 切換到 root 身份或者使用 sudo)。
更新 /etc/nginx/nginx.conf 加入下面的設定:
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name localhost;
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
ssl_protocols TLSv1.2 TLSv1.3;
# ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
# ssl_conf_command Ciphersuites TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384;
# ssl_prefer_server_ciphers on;
# ssl_ecdh_curve prime256v1;
# ssl_early_data on;
# ssl_session_cache shared:SSL:10m;
# ssl_session_timeout 10m;
location / {
root /srv/www/htdocs;
index index.html index.htm;
}
}
include vhosts.d/*.conf;
這樣就有一個支援 HTTPS 的 web server 可以用來測試。
如果要加入 HTTP/2 支援,更新 HTTPS 設定如下(NgINX 1.25.1 以上適用):
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
啟動 Nginx:
sudo systemctl start nginx
如果要在重新開機後會自動啟動 nginx server,使用下列的指令:
sudo systemctl enable nginx
因為 Nginx 支援 FastCGI 但是不支援 CGI, 所以需要 fcgiwrap 將網頁請求透過 FastCGI 協定傳給 CGI 程式執行。 如果有執行 CGI 的需求才需要安裝 fcgiwrap。
Install fcgiwrap (@openSUSE):
sudo zypper install fcgiwrap fcgiwrap-nginx
fcgiwrap 安裝後需要啟動服務:
sudo service fcgiwrap start
如果要在重新開機後會自動啟動 fcgiwrap service,使用下列的指令:
sudo systemctl enable fcgiwrap
在 /etc/nginx 下加入 fcgiwrap.conf,檔案內容如下:
location /cgi-bin/ {
gzip off;
root /srv/www;
fastcgi_pass unix:/var/run/fcgiwrap.sock;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
在想要加入 CGI 支援的 server section 加入下面的設定:
include fcgiwrap.conf;
重新啟動 Nginx:
sudo systemctl restart nginx
在 /srv/www/cgi-bin/ 撰寫 env.cgi 作為測試。
#!/usr/bin/tclsh
package require ncgi
package require html
::html::init
::ncgi::header
set title "Print Environment"
puts [::html::head $title]
puts [::html::bodyTag]
puts [::html::h1 $title]
puts [::html::tableFromArray env]
puts [::html::end]
需要將 env.cgi 的權限設為可執行。如果沒問題,就可以使用 Nginx 開發或者是執行 CGI 程式。
spawn-fcgi 用來啟動 FastCGI process。 spawn-fcgi 一開始是 Lighttpd 的一部份,不過現在已經獨立出來可以供其他 Web Server 使用。 當使用者撰寫了一個 FastCGI 服務,可以使用 spawn-fcgi 進行管理。
Install spawn-fcgi (@openSUSE):
sudo zypper install spawn-fcgi
接下來的設定是使用 spawn-fcgi 啟動我們撰寫的 FastCGI 服務。
這裡使用 tcl-fcgi (pure Tcl) 測試。
下面就是測試的程式 vclock.tcl,來自 tcl-fcgi 的 example(我將檔案放在 /srv/www/cgi-bin,需要將權限設為可執行):
#! /usr/bin/env tclsh
# vclock.tcl -- originally borrowed from Don Libes' cgi.tcl but rewritten
#
package require ncgi
package require textutil
package require Fcgi
package require Fcgi::helpers
namespace eval vclock {
namespace path ::fcgi::helpers
variable EXPECT_HOST http://expect.sourceforge.net
variable CGITCL $EXPECT_HOST/cgi.tcl
variable TEMPLATE [textutil::undent {
<!doctype html>
<html><head><title>Virtual Clock</title></head>
<body>
<h1>Virtual Clock - fcgi.tcl style</h1>
<p>Virtual clock has been accessed <%& $counter %> times since
startup.</p>
<hr>
<p>At the tone, the time will be <strong><%& $time %></strong></p>
<% if {[dict get $query debug]} { %>
<pre> Query: <%& $query %>
Failed: <%& $failed %></pre>
<% } %>
<hr>
<h2>Set Clock Format</h2>
<form method="post">
Show:
<% foreach name {day month day-of-month year} { %>
<input type="checkbox" id="<%& $name %>" name="<%& $name %>"
<%& [dict get $query $name] ? {checked} : {} %>>
<label for="<%& $name %>"><%& $name %></label>
<% } %>
<br>
Time style:
<% foreach value {12-hour 24-hour} { %>
<input type="radio" id="<%& $value %>" name="type" value="<%& $value %>"
<%& [dict get $query type] eq $value ? {checked} : {} %>>
<label for="<%& $value %>"><%& $value %></label>
<% } %>
<br>
<input type="reset">
<input type="submit">
</form>
<hr>
See Don Libes' cgi.tcl and original vclock
at the <a href="<%& $CGITCL %>"><%& $CGITCL %></a>
</body>
</html>
}]
}
proc vclock::main {} {
variable CGITCL
variable TEMPLATE
proc page {query failed counter time CGITCL} [tmpl_parser $TEMPLATE]
set counter 0
while {[FCGI_Accept] >= 0} {
incr counter
puts -nonewline "Content-Type: text/html\r\n\r\n"
lassign [validate-params {
day boolean false
day-of-month boolean false
debug boolean false
month boolean false
type {regexp ^(?:12|24)-hour$} 24-hour
year boolean false
} [query-params {day day-of-month debug month type year}]] query failed
set format [construct-format $query]
set time [clock format [clock seconds] -format $format]
puts [page $query $failed $counter $time $CGITCL]
ncgi::reset
} ;# while {[FCGI_Accept] >= 0}
}
proc vclock::construct-format query {
if {[dict get $query type] eq {}} {
return {%r %a %h %d '%y}
}
set format [expr {
[dict get $query type] eq {12-hour} ? {%r} : {%T}
}]
foreach {name fragment} {
day { %a}
month { %h}
day-of-month { %d}
year { '%y}
} {
if {[dict get $query $name] ne {}} {
append format $fragment
}
}
return $format
}
# If this is the main script...
if {[info exists argv0] && ([file tail [info script]] eq [file tail $argv0])} {
vclock::main
}
我們需要撰寫 spawn-fcgi 的 systemd service,在 /usr/lib/systemd/system 目錄下建立 spawnfcgi.service,內容如下:
[Unit]
Description=Spawn FCGI service
After=nss-user-lookup.target
[Service]
Type=forking
Environment=WORKERS=1
ExecStart=/usr/bin/spawn-fcgi \
-F ${WORKERS} \
-u nginx \
-g nginx \
-s /var/run/%p.sock \
-P /var/run/%p.pid \
-- /srv/www/cgi-bin/vclock.tcl
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
在 /etc/nginx 目錄下加入 spawnfcgi.conf,內容如下:
location /vclock/ {
gzip off;
fastcgi_pass unix:/var/run/spawnfcgi.sock;
include /etc/nginx/fastcgi_params;
}
在想要加入 spawn-fcgi 支援的 server section 加入下面的設定:
include spawnfcgi.conf;
啟動 spawn-fcgi:
sudo systemctl start spawnfcgi
重新啟動 Nginx:
sudo systemctl restart nginx
使用瀏覽器瀏覽 http://localhost/vclock/,檢查結果是否正確。
我們可以注意到,上面的方案只能夠指定執行某一個 script,我們需要一個能夠解讀 script 並且執行的一般性方案。 下面是測試我自己寫的工具 rivet-fcgi 的設定。
首先需要修改 /usr/lib/systemd/system 目錄下的 spawnfcgi.servic。
For unix socket -
[Unit]
Description=Spawn FCGI service
After=nss-user-lookup.target
[Service]
Type=forking
Environment=WORKERS=3
ExecStart=/usr/bin/spawn-fcgi \
-F ${WORKERS} \
-u wwwrun \
-g www \
-s /var/run/%p.sock \
-P /var/run/%p.pid \
-- /usr/bin/rivet-fcgi
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
For Tcp socket -
[Unit]
Description=Spawn FCGI service
After=nss-user-lookup.target
[Service]
Type=forking
Environment=WORKERS=3
ExecStart=/usr/bin/spawn-fcgi \
-F ${WORKERS} \
-u wwwrun \
-g www \
-a 127.0.0.1 -p 9000 \
-P /var/run/%p.pid \
-- /usr/bin/rivet-fcgi
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
需要修改 /etc/nginx 目錄下的 spawnfcgi.conf。
For unix socket -
location ~ \.(rvt|tcl)$ {
gzip off;
root /srv/www/htdocs;
try_files $uri/ $uri =404;
fastcgi_pass unix:/var/run/spawnfcgi.sock;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
For Tcp socket -
location ~ \.(rvt|tcl)$ {
gzip off;
root /srv/www/htdocs;
try_files $uri/ $uri =404;
fastcgi_pass 127.0.0.1:9000;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
啟動 spawn-fcgi 與 NGINX,接下來進行測試。 如果成功,當副檔名名為 .rvt 或者是 .tcl,就會透過 FastCGI 協定傳送到 rivet-fcgi 執行。
PHP-FPM 也是類似的修改手法,PHP-FPM 需要設定好自己的設定 (FPP-FPM 有實作自己的 daemon,所以不需要使用 spawn-fcgi,只需要設定好相關的設定), 然後再設定 NGINX FastCGI 相關的部份即可。
Linux 上可以使用 command line tool xrandr 來設定 screen resolution。
我之所以會有這個需要,是因為在使用 Wine 玩某些舊遊戲的時候,有些舊遊戲在結束以後並不會正確的將螢幕解析度設回來。所以寫了一個小程式可以使用 xrandr 快速的設定回來。目前我這台電腦設定值是 1366x768,所以小程式的預設值我也是這樣設。
#!/usr/bin/tclsh
if {$argc >= 1} {
set mysize [lindex $argv 0]
} elseif {$argc == 0} {
set mysize 1366x768
}
set var [list xrandr -s $mysize]
exec {*}$var
最近我在試著使用 zsh 作為平常使用的 shell(還在測試中),所以試寫了一個會移除 .*history* 檔案的 zsh shell script:
#!/usr/bin/env zsh
pushd $(pwd)
cd $HOME
for file in $(ls .*history*); do
rm $file && touch $file
done
popd
那麼如果用 Tcl 寫呢?程式應該會是這個樣子:
#!/usr/bin/env tclsh
set currdir [pwd]
cd $::env(HOME)
set files [glob -nocomplain -type f .*history*]
foreach filename $files {
file delete -force $filename
if {[file exists $filename]==0} {
close [open $filename a]
}
}
cd $currdir
sudo zypper in rabbitmq-server rabbitmq-server-plugins
sudo service rabbitmq-server start
sudo service rabbitmq-server restart
sudo service rabbitmq-server status
sudo service rabbitmq-server stop
sudo rabbitmq-plugins enable rabbitmq_mqtt
## ----------------------------------------------------------------------------
## RabbitMQ MQTT Adapter
##
## See https://github.com/rabbitmq/rabbitmq-mqtt/blob/stable/README.md
## for details
## ----------------------------------------------------------------------------
# =======================================
# MQTT section
# =======================================
## TCP listener settings.
##
mqtt.listeners.tcp.1 = 127.0.0.1:1883
mqtt.listeners.tcp.2 = ::1:1883
## TCP listener options (as per the broker configuration).
##
# mqtt.tcp_listen_options.backlog = 4096
# mqtt.tcp_listen_options.recbuf = 131072
# mqtt.tcp_listen_options.sndbuf = 131072
#
# mqtt.tcp_listen_options.keepalive = true
# mqtt.tcp_listen_options.nodelay = true
#
# mqtt.tcp_listen_options.exit_on_close = true
# mqtt.tcp_listen_options.send_timeout = 120
## TLS listener settings
## ## See https://rabbitmq.com/mqtt.html and https://rabbitmq.com/ssl.html for details.
#
# mqtt.listeners.ssl.default = 8883
#
# ssl_options.cacertfile = /path/to/tls/ca_certificate_bundle.pem
# ssl_options.certfile = /path/to/tls/server_certificate.pem
# ssl_options.keyfile = /path/to/tls/server_key.pem
# ssl_options.verify = verify_peer
# ssl_options.fail_if_no_peer_cert = true
#
## Number of Erlang processes that will accept connections for the TCP
## and TLS listeners.
##
# mqtt.num_acceptors.tcp = 10
# mqtt.num_acceptors.ssl = 10
## Whether or not to enable proxy protocol support.
## Once enabled, clients cannot directly connect to the broker
## anymore. They must connect through a load balancer that sends the
## proxy protocol header to the broker at connection time.
## This setting applies only to STOMP clients, other protocols
## like STOMP or AMQP have their own setting to enable proxy protocol.
## See the plugins or broker documentation for more information.
##
# mqtt.proxy_protocol = false
## Set the default user name and password used for anonymous connections (when client
## provides no credentials). Anonymous connections are highly discouraged!
##
mqtt.default_user = guest
mqtt.default_pass = guest
## Enable anonymous connections. If this is set to false, clients MUST provide
## credentials in order to connect. See also the mqtt.default_user/mqtt.default_pass
## keys. Anonymous connections are highly discouraged!
##
mqtt.allow_anonymous = false
## If you have multiple vhosts, specify the one to which the
## adapter connects.
##
mqtt.vhost = /
## Specify the exchange to which messages from MQTT clients are published.
##
mqtt.exchange = amq.topic
## Specify TTL (time to live) to control the lifetime of non-clean sessions.
##
mqtt.subscription_ttl = 1800000
## Set the prefetch count (governing the maximum number of unacknowledged
## messages that will be delivered).
##
mqtt.prefetch = 10
package require Thread
package require mqttc
catch {console show}
set ::gThread [thread::create {thread::wait} ]
set result 0
proc subscribe { } {
thread::send -async $::gThread {
package require mqttc
mqttc client "tcp://localhost:1883" "USERTest1" 1 -cleansession 1 \
-username guest -password guest
client subscribe "MQTT Examples" 1
while 1 {
if {[catch {set result [client receive]}]} {
puts "Receive error!!!"
break
}
if {[llength $result] > 0} {
puts "[lindex $result 0] - [lindex $result 1]"
if {![string compare -nocase [lindex $result 1] "Exit"]} {
break
}
}
}
client unsubscribe "MQTT Examples"
client close
} ::result
}
subscribe
puts "started test..."
after 250
mqttc client "tcp://localhost:1883" "USERTest2" 1 -timeout 1000 \
-username guest -password guest
client publishMessage "MQTT Examples" "Hello MQTT!" 1 0
client publishMessage "MQTT Examples" "Exit" 1 0
client close
vwait ::result
sudo zypper install qemu-arm
wget https://download.opensuse.org/ports/aarch64/factory/images/openSUSE-Tumbleweed-ARM-JeOS-efi.aarch64-Current.raw.xz
unxz openSUSE-Tumbleweed-ARM-JeOS-efi.aarch64-Current.raw.xz
wget https://releases.linaro.org/components/kernel/uefi-linaro/16.02/release/qemu64/QEMU_EFI.img.gz
gzip -d QEMU_EFI.img.gz
qemu-system-aarch64 -m 2048 -cpu cortex-a57 -smp 2 -M virt -bios QEMU_EFI.img -serial stdio -device virtio-net-device,netdev=hostnet0,id=net0,mac=52:54:00:09:a4:37 -netdev user,id=hostnet0 -drive if=none,format=raw,file=openSUSE-Tumbleweed-ARM-JeOS-efi.aarch64-Current.raw,id=hd0 -device virtio-blk-device,drive=hd0
sudo zypper in erlang erlang-observer
git clone https://github.com/emqtt/emq-relx.git
cd emq-relx && make
cd _rel/emqttd && ./bin/emqttd console
package require mqttc
mqttc client "tcp://localhost:1883" "USERSSub" 1 -cleansession 1
client subscribe "MQTT Examples" 1
while 1 {
if {[catch {set result [client receive]}]} {
puts "Receive error!!!"
break
}
if {[llength $result] > 0} {
puts "[lindex $result 0] - [lindex $result 1]"
if {![string compare -nocase [lindex $result 1] "Exit"]} {
break
}
}
}
client unsubscribe "MQTT Examples"
client close
package require mqttc
mqttc client "tcp://localhost:1883" "USERSPub" 1 -timeout 1000
client publishMessage "MQTT Examples" "Hello MQTT!" 1 0
client publishMessage "MQTT Examples" "Exit" 1 0
client close
NaviServer is a high performance web server written in C and Tcl. NaviServer 是 AOLserver 的分支。
如果要自己手動安裝:
./configure --prefix=/var/lib/naviserver --with-tcl=/usr/lib64 --with-zlib=/usr/lib64 --with-openssl=/usr/lib64
make
sudo make install
如果在 make 的時候遇到 -fstack-protector-strong 或者是 -fstack-clash-protection 無法辨識的問題, 修改 include/Makefile.global, 將 -fstack-protector-strong 或者是 -fstack-clash-protection 改為 -fstack-protector。 這是編譯套件時 GCC 新舊版本不同所造成的狀況。
sed -i s/stack-protector-strong/stack-protector/g include/Makefile.global
sed -i s/stack-clash-protection/stack-protector/g include/Makefile.global
NaviServer 預設的使用者是 nsadmin,所以需要使用下列的指令來增加使用者:
sudo useradd -r -c 'NaviServer' -d/var/lib/naviserver -U -M -s/bin/bash nsadmin
再來需要修改 NaviServer 目錄的擁用者設定,修改擁有者 user:group 為 nsadmin:nsadmin。
sudo chown -R nsadmin:nsadmin /var/lib/naviserver
再來要設定 nsadmin 帳號的密碼,使用 passwd 改變 nsadmin 使用者的密碼:
sudo passwd nsadmin
再來是執行 NaviServer
sudo -i -u nsadmin /var/lib/naviserver/bin/nsd -f -t /var/lib/naviserver/conf/nsd-config.tcl
再來瀏覽 http://localhost:8080/,如果出現 NaviServer 的訊息就表示成功了。
/var/lib/naviserver/conf 下還有其它的設定範例,包含 openacs-config.tcl, sample-config.tcl, simple-config.tcl, 也可以用來參考。
因為希望使用 80 port 與 443 port,所以修改 nsd-config.tcl:
dict set defaultConfig ipaddress "127.0.0.1 ::1"
dict set defaultConfig httpport 80
dict set defaultConfig httpsport 443
dict set defaultConfig nscpport ""
dict set defaultConfig home [file dirname [file dirname [info nameofexecutable]]]
dict set defaultConfig hostname localhost
dict set defaultConfig pagedir {$home/pages}
dict set defaultConfig logdir {$home/logs}
dict set defaultConfig certificate {$home/certificates/server.pem}
dict set defaultConfig vhostcertificates {$home/certificates/certificates}
如果要檢查設定是否格式正確,使用下列的指令(加上 -T),在檢查是否有打字錯誤時很有用:
/var/lib/naviserver/bin/nsd -t /var/lib/naviserver/conf/nsd-config.tcl -T
openSUSE 的 systemd service 檔案放在 /usr/lib/systemd/system,加入 nsd.service
[Unit]
Description=NaviServer
After=network.target
#After=network.target postgresql.service
#Wants=postgresql.service
[Service]
Type=forking
PIDFile=/var/lib/naviserver/logs/nsd.pid
Environment="LANG=en_US.UTF-8"
# In case, a site is using Google Perfortools malloc with the system-malloc patch for Tcl:
# Environment="LD_PRELOAD=/usr/lib/libtcmalloc.so"
ExecStartPre=-/bin/rm -f /var/lib/naviserver/logs/nsd.pid
# Standard startup with a privileged port
ExecStart=/var/lib/naviserver/bin/nsd -u nsadmin -g nsadmin -t /var/lib/naviserver/conf/nsd-config.tcl \
-b 127.0.0.1:80,[::1]:80#2,127.0.0.1:443,[::1]:443#2
Restart=on-abnormal
KillMode=process
[Install]
WantedBy=multi-user.target
讓 systemctl 重啟 daemon 並且重新載入設定:
sudo systemctl daemon-reload
啟動 nsd.service:
sudo systemctl start nsd.service
查詢 nsd.service:
sudo systemctl status nsd.service
停止 nsd.service:
sudo systemctl stop nsd.service
重新啟動 nsd.service:
sudo systemctl restart nsd.service
sudo zypper in apache2
sudo systemctl start apache2
sudo systemctl stop apache2
sudo chkconfig apache2 on
sudo chkconfig apache2 off
sudo zypper addrepo https://download.opensuse.org/repositories/Apache:/Modules/openSUSE_Leap_42.3/ Apache-Modules
sudo zypper refresh
sudo zypper install apache2-mod_rivet
sudo a2enmod rivet
(* 如果要移除,使用 sudo a2dismod rivet 來移除) sudo systemctl restart apache2
<? set hello_message "Hello world" ?>
<html>
<head>
<title><?= $hello_message ?></title>
</head>
<body><?= [::rivet::html $hello_message pre b] ?></body>
</html>
瀏覽 http://localhost/hello.rvt,如果有看到訊息表示成功安裝。sudo a2enmod ssl
[req]
default_bits = 2048
default_keyfile = localhost.key
distinguished_name = req_distinguished_name
req_extensions = req_ext
x509_extensions = v3_ca
[req_distinguished_name]
countryName = Country Name (2 letter code)
countryName_default = US
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = New York
localityName = Locality Name (eg, city)
localityName_default = Rochester
organizationName = Organization Name (eg, company)
organizationName_default = localhost
organizationalUnitName = organizationalunit
organizationalUnitName_default = Development
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_default = localhost
commonName_max = 64
[req_ext]
subjectAltName = @alt_names
[v3_ca]
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = 127.0.0.1
Run the following 2 commands using OpenSSL to create a self-signed certificate in openSUSE with OpenSSL :sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout localhost.key -out localhost.crt -config localhost.conf -passin pass:YourSecurePassword
sudo openssl pkcs12 -export -out localhost.pfx -inkey localhost.key -in localhost.crt
sudo cp localhost.crt /etc/apache2/ssl.crt/server.crt
sudo cp localhost.key /etc/apache2/ssl.key/server.key
APACHE_START_TIMEOUT="10"
APACHE_SERVER_FLAGS="SSL"
<IfDefine SSL>
<IfDefine !NOSSL>
##
## SSL Virtual Host Context
##
<VirtualHost *:443>
# General setup for the virtual host
DocumentRoot "/srv/www/htdocs"
#ServerName www.example.com:443
#ServerAdmin webmaster@example.com
ErrorLog /var/log/apache2/error_log
TransferLog /var/log/apache2/access_log
# SSL Engine Switch:
# Enable/Disable SSL for this virtual host.
SSLEngine on
# You can use per vhost certificates if SNI is supported.
SSLCertificateFile /etc/apache2/ssl.crt/server.crt
SSLCertificateKeyFile /etc/apache2/ssl.key/server.key
#SSLCertificateChainFile /etc/apache2/ssl.crt/vhost-example-chain.crt
# Per-Server Logging:
# The home of a custom SSL log file. Use this when you want a
# compact non-error SSL logfile on a virtual host basis.
CustomLog /var/log/apache2/ssl_request_log ssl_combined
</VirtualHost>
</IfDefine>
</IfDefine>
IncludeOptional /etc/apache2/conf.d/*.conf
IncludeOptional /etc/apache2/vhosts.d/*.conf
sudo systemctl restart apache2
sudo a2enmod rewrite
<VirtualHost *:80>
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule (.*) https://%{SERVER_NAME}/$1 [R,L]
</VirtualHost>
<VirtualHost *:443>
# General setup for the virtual host
DocumentRoot "/srv/www/htdocs"
#ServerName www.example.com:443
#ServerAdmin webmaster@example.com
ErrorLog /var/log/apache2/error_log
TransferLog /var/log/apache2/access_log
# SSL Engine Switch:
# Enable/Disable SSL for this virtual host.
SSLEngine on
# You can use per vhost certificates if SNI is supported.
SSLCertificateFile /etc/apache2/ssl.crt/server.crt
SSLCertificateKeyFile /etc/apache2/ssl.key/server.key
#SSLCertificateChainFile /etc/apache2/ssl.crt/vhost-example-chain.crt
# Per-Server Logging:
# The home of a custom SSL log file. Use this when you want a
# compact non-error SSL logfile on a virtual host basis.
CustomLog /var/log/apache2/ssl_request_log ssl_combined
</VirtualHost>
sudo systemctl restart apache2