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
}