2024-04-17

Eclipse Mosquitto

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

2024-04-10

Apache HTTP Server and rivet-fcgi

這是之前在寫 rivet-fcgi 這個幫助我學習 FastCGI 的 prototype 時的筆記,關於 Apache HTTP Server 的,目前我沒有再重新測一次確定完全沒有問題,只是放在這裡先做個記錄。


簡介

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) 可以使用,主要有下列的分別:

  • Prefork MPM
  • Worker MPM
  • Event MPM

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

FastCGI

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,內容如下:

[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

(其中 -u 指定 user,-g 指定 group,隨著平台的不同可能有不同的設定。
rivet-fcgi 是我自己撰寫的工具。)

下面是測試我自己寫的工具的設定。
修改 /etc/apache2/conf.d 目錄下的檔案 mod_fcgid.conf,內容如下:

##
#<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>

而後啟動(或重新啟動)spawn-fcgi 與 apache2 的服務,接著進行測試是否有正確設定。
(注意:PHP-FPM 也可以使用類似的設定手法,只是 spawn-fcgi 設定的部份變成為設定 php-fpm 的參數, 而 web server 這邊的副檔名參數與連線參數需要修改。)

參考資料

tcl-opencv v0.17

tcl-opencv

 

已經更新版本為 v0.17 一段時間,只是現在才發佈在這個部落格上。 主要是增加 BarcodeDetector 的支援,以及修正 test cases 的錯誤。

  • Implement command: BarcodeDetector
  • Implement command: stackBlur
  • Implement command: hasNonZero
  • Add ::cv::REDUCE_SUM2 option to ::cv::reduce

2024-03-01

Tcl/Tk 8.6.14

Tcl/Tk 8.6.14 已經釋出,比較重大的改變可以參考 Changes in Tcl/Tk 8.6.14

Tcl Changes

  • TIP 402: Path normalization revised so UNC path support is cross platform
  • Interpretation of ~ in paths made cross platform.
  • Decoding of incomplete code UTF code sequences generates replacement character
  • Restore [exec %var%\] on Windows
  • Allow [char create {} \$cmd\].
  • Improved handling of non-BMP pathnames on Windows.

Tk Changes

  • Revise number parsing to match that of Tcl.
  • Iconlist ignores the options database entry for foreground text color
  • menu clone bindings corrected
  • Enable treeview display of partial final line
  • Allow return from [tk scaling\] in safe interp
  • Prevent exposure of clues to masked entry contents by navigation bindings

2024-02-27

Ffidl v0.9

Ffidl


後來我發現,openSUSE 的 devel:languages:tcl 上有 Ffidl 這個套件,只是沒有更新到後續維護的版本,所以我發了 一個 request,更新 devel:languages:tcl 上套件到最近的版本,如果被接受,就會刪除我自己的部份。

2024-01-23

List ODBC data sources

這是一個簡單的程式,列出目前環境中 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." ]
}

2024-01-21

RSS to HTML

下面的程式使用 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
}

2023-12-25

unixODBC isql

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

2023-11-30

tcl-monetdbe v0.2.0

 tcl-monetdbe


Tcl extension and TDBC driver for MonetDB Embedded.

MonetDB Embedded (MonetDB/e) 已經出現了一段時間,是 MonetDB 的 embedded solution。因為 MonetDB 我只是用來測試一些想法,所以之前一直沒有使用 MonetDB/e 的場合。

不過因為電腦上有裝 MonetDB 而且我也有安裝 MonetDB embedded library,所以考慮了一下,最近嘗試寫一個 Tcl extension 測試看看使用的情況。

2023-11-23

tdbc::mysql

最近我在整理自己的作業環境,這才發現 libmariadb-devel 對於 tdbc::mysql 而言是非必要的(以前因為 so suffixes 的關係需要裝,但是其實 tdbc::mysql 已經修正了,只是我沒注意到)。下面就是目前的 code:

static const char *const mysqlStubLibNames[] = {
    /* @LIBNAMES@: DO NOT EDIT THESE NAMES */
    "mariadbclient", "mariadb", "mysqlclient_r", "mysqlclient", "mysql", NULL
    /* @END@ */
};

/* ABI Version numbers of the MySQL API that we can cope with */

static const char mysqlSuffixes[][4] = {
    "", ".3", ".21", ".20", ".19", ".18", ".17", ".16", ".15"
};

如果說 MariaDB 是為了防止 Oracle 更改 MySQL 的授權而成立的,可是那為什麼 Michael Widenius 除了基金會,還又成立了一家企業?嘴上說我不是要用 MySQL 或者是 MariaDB 賺錢,實際上的行為卻完全相反,這是一個與說法衝突的行為(還可以參考 Uproar: MariaDB Corp. veers away from open source)。如果採用 MySQL 是被騙第一次,那 MariaDB 被騙第二次的人就應該要自我檢討了。我是說如果你擔心 Oracle 會對 MySQL 做出什麼損壞開放原始碼的事情,那麼 Michael Widenius 也可以重施故技,又賣掉 MariaDB 企業或者是上市賺取利益。

也就是說,我不認為應該要採用 MySQL 或者是 MariaDB。當然不是所有人都可以直接切換到其它的資料庫,這個時候可以考慮採用 MySQL protocol 並且高度相容的其它資料庫,在這個情況下,client 端的 source code 的變動理論上要很少,而 server 端則是換掉 MySQL 或者是 MariaDB,採用其它相容方案的資料庫。

在這個情況下,MySQL/MariaDB client API 是很重要的,而又剛好 MySQL/MariaDB client API 極大多數是 LGPL 或者是較為寬鬆的條款,只要採用動態連結的使用方式就可以在商業環境下使用,所以保留 MySQL/MariaDB client library,然後從MySQL 或者是 MariaDB 遷移出去。 

不過如果是因為學習 LAMP 架構而需要安裝一個資料庫,或者是撰寫開放原始碼的軟體,那麼 MySQL 與 MariaDB 我想可以考慮 MariaDB;理由也很簡單,因為大多數的 Linux distribution 都有內建的套件可以安裝(只是版本可能會比較舊不是最新的)。然後要小心資料庫專屬的功能,小心的避掉廠商鎖定的風險。