2025/06/04(水)milter-manager は OpenDMARC を自動検出しない!

2025/06/04 4:57 サーバ運営・管理
最近気づいたんですが、Postfix 側の milter インタフェースも安定してきていて、
複数の milter を直感的に指定できるようになっていたり(当初から?)しているので、milter-manager を使う出番はそろそろ・・・
という状況ですが、一度にあれもこれもカスタマイズ出来ないので、milter-manager を使い続けているわけですが・・・

最初は、milter-manager 経由で動作させている OpenDKIM は機能しているのに、どう頑張っても OpenDMARC が付けてくれるはずのメールヘッダが一向に付かないので悩んでいました。
OpenDMARC を milter-manager 経由で稼働させている実績はどうやら一般的に皆無で、マイナーな milter なのか、と思ってたりしましたが・・・

そもそも、milter-manager に OpenDMARC milter が識別されているのかどうかになります。
なので、おもむろに
# /usr/local/sbin/milter-manager -u milter-manager --show-config
を実施すると・・・
# /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:37
define_milter("milter-opendkim") do |milter|
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:45
  milter.connection_spec = "local:/var/run/milteropendkim/dkim-milter"
  # default
  milter.description = nil
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:38
  milter.enabled = true
  # default
  milter.fallback_status = "accept"
  # default
  milter.evaluation_mode = false
  # default
  milter.applicable_conditions = []
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:40
  milter.command = "/usr/local/etc/rc.d/milter-opendkim"
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:41
  milter.command_options = "start"
  # default
  milter.user_name = nil
  # default
  milter.connection_timeout = 297.0
  # default
  milter.writing_timeout = 7.0
  # default
  milter.reading_timeout = 7.0
  # default
  milter.end_of_message_timeout = 297.0
end
は、出てくるが、milter-opendmarc は出てきません。つまり、認識されていなかった。。orz...
ここから、自動認識させるための試行錯誤が始まります。

先ず、/usr/local/etc/milter-manager/defaults/freebsd.conf を修正してみます:
33行目あたりに、
  ["clamav-milter", []],
  ["milter-dkim", []],
  ["milter-opendkim", []],
  [
    "spamass-milter",
    [
      "Remote Network",
      "Unauthenticated",
      "No Stress",
    ],
  ],
とあるので、["milter-dkim", []] とある行の下に、 ["milter-opendmarc", []], を追加し、
  ["clamav-milter", []],
  ["milter-dkim", []],
  ["milter-opendkim", []],
  ["milter-opendmarc", []],
  [
    "spamass-milter",
    [
      "Remote Network",
      "Unauthenticated",
      "No Stress",
    ],
  ],
のようにします。これで、
# /usr/local/sbin/milter-manager -u milter-manager --show-config
を実施すると、"milter-opendmarc" は識別されるようになりました。
注意点としては、FreeBSD の場合、/usr/local/etc/rc.d 配下のスクリプトファイル名を milter-opendmarc にし、実行権を与えておくようにしておくことが必須です。

これで解決か?と思ったんですが、
今度はメールを受信する度に /var/log/maillog に
milter-manager[53830]: [egg][error] must set connection spec: milter-opendmarc
という、エラーが出て OpenDMARCは動作しません。再び、
# /usr/local/sbin/milter-manager -u milter-manager --show-config
とすると、
# /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:37
define_milter("milter-opendmarc") do |milter|
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:45
  milter.connection_spec = nil ← 注目
  # default
  milter.description = nil
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:38
  milter.enabled = true
  ・・・・
要するに、milter-manager からは、接続先不明状態という状況。
幸いにも OpenDKIM が機能しているので、これを手掛かりに 自動検出スクリプトの修正を行いました。。。

先ず、/usr/local/lib/milter-manager/binding/lib/milter/manager にディレクトリを移動します:
修正したのは、
detector.rb
freebsd-rc-detector.rb
の2つ。新規に opendkim-config-parser.rb を参考に、 opendmarc-config-parser.rb を追加で作成しています。
結構あちこち修正したので、差分を提示しておきます。
1) detector.rb
--- detector.rb.old 2024-08-12 05:01:29.000000000 +0900
+++ detector.rb.new 2025-06-03 23:53:01.772724000 +0900
@@ -16,6 +16,7 @@
 require 'milter/manager/clamav-milter-config-parser'
 require 'milter/manager/milter-greylist-config-parser'
 require 'milter/manager/opendkim-config-parser'
+require 'milter/manager/opendmarc-config-parser'
 require 'milter/manager/rmilter-socket-detector'
 require 'milter/manager/rspamd-proxy-detector'

@@ -85,6 +86,10 @@
       opendkim_config_parser.socket
     end

+    def detect_opendmarc_connection_spec
+      opendmarc_config_parser.socket
+    end
+
     def detect_rmilter_connection_spec
       Milter::Manager::RmilterSocketDetector.new(rmilter_conf).detect
     end
@@ -138,6 +143,7 @@
       @clamav_milter_config_parser = nil
       @milter_greylist_config_parser = nil
       @opendkim_config_parser = nil
+      @opendmarc_config_parser = nil
     end

     def set_variable(name, unnormalized_value)
@@ -265,5 +271,14 @@
       end
       @opendkim_config_parser
     end
+
+    def opendmarc_config_parser
+      if @opendmarc_config_parser.nil?
+        @opendmarc_config_parser = Milter::Manager::OpenDMARCConfigParser.new
+        @opendmarc_config_parser.parse(opendmarc_conf)
+      end
+      @opendmarc_config_parser
+    end
+
   end
 end
2)freebsd-rc-detector.rb
--- freebsd-rc-detector.rb.old  2024-08-12 05:01:29.000000000 +0900
+++ freebsd-rc-detector.rb.new  2025-06-04 00:05:45.417658000 +0900
@@ -55,6 +55,10 @@
       @script_name == "milter-opendkim" or @name == "milteropendkim"
     end

+    def opendmarc?
+      @script_name == "milter-opendmarc" or @name == "milteropendmarc"
+    end
+
     def detect_rmilter_connection_spec
       Milter::Manager::RmilterSocketDetector.new(rmilter_conf).detect
     end
@@ -92,6 +96,12 @@
         "/usr/local/etc/opendkim.conf"
     end

+    def opendmarc_conf
+      @variables["cfgfile"] ||
+        extract_parameter_from_flags(command_args, "-C") ||
+        "/usr/local/etc/opendmarc.conf"
+    end
+
     def rmilter_conf
       extract_parameter_from_flags(command_args, "-c") ||
         "/usr/local/etc/rmilter.conf"
@@ -111,6 +121,7 @@
       spec ||= detect_enma_connection_spec if enma?
       spec ||= detect_clamav_milter_connection_spec if clamav_milter?
       spec ||= detect_opendkim_connection_spec if opendkim?
+      spec ||= detect_opendmarc_connection_spec if opendmarc?
       spec ||= detect_rmilter_connection_spec if rmilter?
       spec ||= detect_rspamd_proxy_connection_spec if rspamd?
       spec
@@ -141,6 +152,17 @@
           detector.detect
           detector.apply(loader)
         end
+      end
+      if opendmarc? and _profiles and !_profiles.empty?
+        _profiles.each do |profile|
+          detector = FreeBSDRCProfileDetector.new(@configuration,
+                                                  @script_name,
+                                                  profile,
+                                                  self,
+                                                  &@connection_spec_detector)
+          detector.detect
+          detector.apply(loader)
+        end
       else
         super
       end
@@ -206,6 +228,10 @@
     end

     def detect_opendkim_connection_spec
+      super || @base_variables["socket"]
+    end
+
+    def detect_opendmarc_connection_spec
       super || @base_variables["socket"]
     end
3)opendmarc-config-parser.rb
  opendkim-config-parser.rb をコピーし、下記の修正です:
--- opendkim-config-parser.rb   2025-05-26 02:31:08.119826000 +0900
+++ opendmarc-config-parser.rb  2025-06-03 23:47:40.989538000 +0900
@@ -16,7 +16,7 @@
 require "milter/manager/file-reader"

 module Milter::Manager
-  class OpenDKIMConfigParser
+  class OpenDMARCConfigParser
     def initialize
       @variables = {}
     end
当方は、ruby は殆ど扱った経験は無いので、「見よう見真似」と「推測」で修正しており、実際はもっと小規模な修正で出来るかもしれませんし、更なる修正が必要かもしれません。

上記の修正後、
# /usr/local/sbin/milter-manager -u milter-manager --show-config
を実施すると、
# /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:37
define_milter("milter-opendmarc") do |milter|
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:45
  milter.connection_spec = "local:/var/run/opendmarc/socket"  ← 注目
  # default
  milter.description = nil
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:38
  milter.enabled = true
  # default
  milter.fallback_status = "accept"
  # default
  milter.evaluation_mode = false
  # default
  milter.applicable_conditions = []
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:40
  milter.command = "/usr/local/etc/rc.d/milter-opendmarc"
  # /usr/local/lib/milter-manager/binding/lib/milter/manager/detector.rb:41
  milter.command_options = "start"
  # default
  milter.user_name = nil
  # default
  milter.connection_timeout = 297.0
  # default
  milter.writing_timeout = 7.0
  # default
  milter.reading_timeout = 7.0
  # default
  milter.end_of_message_timeout = 297.0

接続先も認識され、めでたく OpenDMARC が milter-manager で利用できるようになりました。

これで、今年2月にやりたかった DMARC 対応が、設備移転で全く出来なかった状況を脱し、やっと準備完了となりました・・orz

2025/05/26(月)〔FreeBSD Ports〕glib、gobject-introspection のコンパイルエラー

2025/05/26 24:05 サーバ運営・管理
FreeBSD の Ports を用いてソフトウェアの更新をする際、
portupgrade コマンドを常用している方々はそこそこ居られると思うのですが、
このコマンドを使用して glib と gobject-introspection をアップデートすると・・・
glib-2.84.1,2 depends on file: /usr/local/gobject-introspection-bootstrap/bin/g-ir-scanner - not found
*** Error code 1

Stop.
make[1]: stopped in /usr/ports/devel/glib20
*** Error code 1
のようなエラーとなって、構築不能となる。
要するに構築に必要な g-ir-scanner 実行ファイルが見つからないので、どうにも出来ないという状況。

この件の情報収集をしていると、「/usr/ports/UPDATING に記載がある」といいます。
確かに、提起のテキストファイル内の 20250402: と記載がある行の下に記載があります:
20250402:
AFFECTS: users of devel/glib20 and devel/gobject-introspection building outside of Poudriere
AUTHOR: arrowd@FreeBSD.org

New versions of glib started to require gobject-introspection as a build
dependency, while gobject-introspection requires glib to build. This forms a
dependency cycle that is dealt with by introducing the @bootstrap flavor for
mentioned ports. This solution is suggested by the upstream as well, see
https://discourse.gnome.org/t/dealing-with-glib-and-gobject-introspection-circular-dependency/18701

When building devel/glib20 in Poudriere no manual intervention is required.
The devel/glib20@bootstrap gets built first and provides for
devel/gobject-introspection@bootstrap. This in turn provides for a normal
devel/glib20 build, which finally fulfills a dependency for normal devel/gobject-introspection

Users that are building outside of the isolated environments will be ending
up with useless glib-bootstrap and gobject-introspection-bootstrap packages
installed after each update. These can be removed with

pkg remove glib-bootstrap gobject-introspection-bootstrap
正直、「どう対処するのか?」という観点では非常に判りにくいです:
Google翻訳をしてみました。
20250402:
影響: Poudriere 以外で devel/glib20 および devel/gobject-introspection をビルドするユーザー
作成者: arrowd@FreeBSD.org

glib の新しいバージョンでは、ビルド依存関係として gobject-introspection が必須になりました。一方、gobject-introspection はビルドに glib を必要とします。これにより依存関係の循環が発生しますが、上記の port に @bootstrap フレーバーを導入することで対処できます。この解決策はアップストリームでも提案されています。詳細は、https://discourse.gnome.org/t/dealing-with-glib-and-gobject-introspection-circular-dependency/18701 を参照してください。

Poudriere で devel/glib20 をビルドする場合、手動による介入は不要です。
devel/glib20@bootstrap が最初にビルドされ、devel/gobject-introspection@bootstrap に提供されます。これにより、通常の devel/glib20 ビルドが可能になり、最終的に通常の devel/gobject-introspection の依存関係が満たされます。

隔離環境外でビルドを行っているユーザーは、アップデートのたびに不要な glib-bootstrap および gobject-introspection-bootstrap パッケージがインストールされてしまいます。これらのパッケージは、

pkg delete glib-bootstrap gobject-introspection-bootstrap で削除できます。
更に見ていくと、どうもこの「フレーバー」という仕組みが最近 Ports に組み込まれたらしく、現行のportupgrade (Ver 2.4.16) ではこの仕組みを解釈出来ないことが原因の模様。

この解決方法を記載されていたページを見つけたので、ここで肝の部分だけ引用してみます:
1. glibをglib-bootstrapに変更(glib-bootstrapはgobject-introspectionが必要ない)する。
2. gobject-introspectionをgobject-introspection-bootstrapに変更する。
3. glib-bootstrapをglibへ更新する。
4. gobject-introspection-bootstrapをgobject-introspectionへ更新する。

具体的には以下のように実行します。

# pkg_replace glib=/usr/ports/devel/glib20@bootstrap
# pkg_replace gobject-introspection=/usr/ports/devel/gobject-introspection@bootstrap
# pkg_replace glib-bootstrap=/usr/ports/devel/glib20
# pkg_replace gobject-introspection-bootstrap=/usr/ports/devel/gobject-introspection
glibとgobject-introspectionのアップデート | 出口の興味関心あるもの
当方でもここで提示されている方法で、glibと、gobject-introspection を更新出来ました。
しかしまぁ。。 /usr/ports/UPDATING を注意深く見ている人はそう居ないような気も、、、

※注意(2025/06/05 追記):
 引用元サイトにて明記されていないので補足します。
 ・pkg_replace は、予め Ports の ports_mgmt/pkg_replace を別途インストールする必要があります。
 ・どうやら、構築時に lang/python-3.11.12 以上のバージョンと devel/meson-1.7.0 以上のバージョンが依存するらしく、
  少なくともこの2つが提起バージョン未満だと、上手くいきません。当方は、これでしばらく悩みました。

2024/11/09(土)初めて両面基板を作ってみた(両面感光基板を使用)

2024/11/09 3:08 電子工作
初めてですが、結構上手く行ったので、一部でも参考になればと考えています。
今回は、サンハヤト社謹製の両面感光基板を使用しました。
最初にお断りしておきますが、当方は、サンハヤト社の回し者ではありません。道具を売っている製造業者をこれしか知らないので・・

○ 両面基板作成にあたって固有に必要と思われるもの
・両面感光基板 感光基板でプリント基板作るのであれば、必然的ではあるが・・・
・両面感光基板対応露光装置 当方は、昔買ったこれまたサンハヤト社謹製の BOX-W9B というものを使っています。
 しかし、これはもう現在では製造自体していません。
 後継となる対応機種はなく、両面基板対応のものは BOX-W7000 しかない。
 しかもこれは20万円以上するため、現実には、BOX-S3000 にて片面ずつ露光するか、BOX-S3000 も安くないため、各自の創意工夫が求められる部分であることがネックですね。
・両面テープ
20241109_01.JPG
 露光時、基板の位置固定に使用します。粘着力は弱いのものを選びましょう。
 撮影時、この粘着テープを使用しましたが、これでも粘着力が強すぎると感じました。


・プリント基板の切れ端
20241109_04.JPG

 両面テープと共に、基板の位置固定に使用します。
 プリント基板は1.6mm厚または1.0mm厚のものが多いので、
 現物と同じ厚みのものを2枚必ず用意しましょう。

 撮影時の不手際で、両面テープを張った状態の切れ端になっています。。
・スルピンキット
(スルーホールを作る場合)
20241109_02.JPG
 0.8mm用のものを用意しましょう。最も小径且つ最も多く使うのが 0.8mm穴。
 個人的にはこれでも太いと思う。

 ワンランク細いもの、例えば 0.7mm とか 0.6mm ものが欲しいですね。
・はんだ吸引機
(スルーホールを作る場合)
スルーホールの生成時に必須なのです。詳細はスルピンキットの説明を参照お願いします。
次に、具体的な制作方法の要点です。
20241109_03.JPG
1)パターン作成
 先ずは通常通りパターンを作成します。
 この時、基板アートワークの外側にトンボ線を必ず入れておきます。
(いわゆる十字線とかトリムマークと称するもの)

 これが重要です。
20241109_05.JPG
2)ポジフィルムの張り合わせ
 トンボ線に平行になるように2辺に両面テープで基板の切れ端を合わせ、
 裏面と表面のトンボ線を合わせつつ、貼り合わせていきます。
 この精度が裏表のズレの精度に直結しますが、トンボ線に合わせることで、
 意外と0.1mm以下の誤差にすることが可能です。

 裏表や上下を間違えないように、細心の注意を払いましょう。
20241109_06.JPG
3)通常どおり、露光とエッチング
 両面基板ですから、
 露光もエッチングも片面基板2枚分の考慮が必要なことに注意しましょう。
 いつも片面基板ばかりで作業していると忘れがちなところです。

 過露光でごく一部のパターンが切れてしまいました・・・
20241109_07.JPG
4)スルーホールの作成
 スルーホールにしたい穴にスルピンキットでスルーホールを作成。
  ― スルーホールを打ち込み、
  ― ハンダで埋め込み、
  ― ハンダ吸引機で余分なハンダを吸い取る・・
 という工程の繰り返しですが、
 慣れないので仕上がりがかなり汚くなってしまいました・・・
 ハンダ吸い取り機は小さいほうがいいかもしれません。
20241109_09.JPG
5)実装してみた
 一部部品の実装サイズ確保を誤ってしまい、
 更なる空中配線の部品が出てしまいました・・・ orz

 しかしながら、動作はしたので、これで完成としてしまいました。

2024/06/21(金)OpenLDAP 2.6.8 では、Cyrus-SASL 抜きの構築は出来ない

これも嵌ったので自分メモ:
OpenLDAP をソースコードから構築する場合、configure で指定するオプションに --without-cyrus-sasl を指定して、Cyrus-SASL のサポートを外すことが出来ます。
ところが、こうすると、 OpenLDAP 2.6.8 では、途中で下記のエラーを吐いて構築不能になるのです:
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_host_connected_to' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_int_sasl_config' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_int_sasl_get_option' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_int_sasl_open' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_int_sasl_set_option' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_cbinding' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_cbinding_parse' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_install' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_mutex_dispose' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_mutex_lock' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_mutex_new' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_mutex_unlock' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_remove' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_secprops' failed: symbol not defined
ld: error: version script assignment of 'OPENLDAP_2.200' to symbol 'ldap_pvt_sasl_secprops_unparse' failed: symbol not defined
cc: error: linker command failed with exit code 1 (use -v to see invocation)
これは、Cyrus-SASL をインストールし、更に --without-cyrus-sasl のオプションを外さないとエラーが取れない。
弊社の使用形態では Cyrus-SASL は使わないため、このオプションは必須の形で指定していたが、嫌でも Cyrus-SASL をインストールし、サポートさせないといけない状態に…

このバージョンだけの問題なのか、本来は Cyrus-SASL と OpenLDAP はセットで使う仕様であるので、今後の強制的な方針なのかは判りません。
メンテナンス・運用管理の観点からは、出来る限り余計なものは入れたくないんだけどね・・・

2024/06/20(木)FreeBSD 13.3R で dovecot 2.3.21 はそのまま構築できない

2024/06/21 16:36 サーバ運営・管理
先日、ちょっと嵌ったので自分メモ
FreeBSD 13.3 では、clang が Ver 17 になった影響か、dovecot 2.3.21 ではこんな感じで、コンパイルエラーになる:
test-mail-index-transaction-update.c:633:14: warning: comparison of function 'timezone' equal to a null pointer is always false [-Wtautological-pointer-compare]
  633 |         test_assert(timezone == 0);
      |                     ^~~~~~~~    ~
../../src/lib-test/test-common.h:20:8: note: expanded from macro 'test_assert'
   20 |         if (!(code)) test_assert_failed(#code, __FILE__, __LINE__); \ 
      |               ^~~~
test-mail-index-transaction-update.c:633:14: note: prefix with the address-of operator to silence this warning
  633 |         test_assert(timezone == 0); 
      |                     ^ 
      |                     & 
../../src/lib-test/test-common.h:20:8: note: expanded from macro 'test_assert'
   20 |         if (!(code)) test_assert_failed(#code, __FILE__, __LINE__); \ 
      |               ^ 
test-mail-index-transaction-update.c:648:42: warning: arithmetic on a pointer to the function type 'char *(int, int)' is a GNU extension [-Wgnu-pointer-arith]
  648 |                 hdr.day_stamp = tests[i].old_day_stamp + timezone; 
      |                                                        ^ ~~~~~~~~ 
test-mail-index-transaction-update.c:648:17: error: incompatible pointer to integer conversion assigning to 'uint32_t' (aka 'unsigned int') from 'char *(*)(int, int)' [-Wint-conversion]
  648 |                 hdr.day_stamp = tests[i].old_day_stamp + timezone; 
      |                               ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
test-mail-index-transaction-update.c:650:49: warning: arithmetic on a pointer to the function type 'char *(int, int)' is a GNU extension [-Wgnu-pointer-arith]
  650 |                 mail_index_update_day_headers(t, tests[i].now + timezone); 
      |                                                               ^ ~~~~~~~~ 
test-mail-index-transaction-update.c:650:36: error: incompatible pointer to integer conversion passing 'char *(*)(int, int)' to parameter of type 'time_t' (aka 'long') [-Wint-conversion]
  650 |                 mail_index_update_day_headers(t, tests[i].now + timezone); 
      |                                                  ^~~~~~~~~~~~~~~~~~~~~~~ 
./mail-index-transaction-private.h:127:77: note: passing argument to parameter 'day_stamp' here
  127 | void mail_index_update_day_headers(struct mail_index_transaction *t, time_t day_stamp); 
      |                                                                             ^ 
test-mail-index-transaction-update.c:654:63: warning: arithmetic on a pointer to the function type 'char *(int, int)' is a GNU extension [-Wgnu-pointer-arith]
  654 |                 test_assert_idx(new_hdr.day_stamp == tests[i].new_day_stamp + timezone, i); 
      |                                                                             ^ ~~~~~~~~ 
../../src/lib-test/test-common.h:26:9: note: expanded from macro 'test_assert_idx'
   26 |                 if (!(code)) test_assert_failed_idx(#code, __FILE__, __LINE__, i); \ 
      |                       ^~~~ 
test-mail-index-transaction-update.c:654:37: warning: comparison between pointer and integer ('uint32_t' (aka 'unsigned int') and 'char *(*)(int, int)') [-Wpointer-integer-compare]
  654 |                 test_assert_idx(new_hdr.day_stamp == tests[i].new_day_stamp + timezone, i); 
      |                                 ~~~~~~~~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
../../src/lib-test/test-common.h:26:9: note: expanded from macro 'test_assert_idx'
   26 |                 if (!(code)) test_assert_failed_idx(#code, __FILE__, __LINE__, i); \ 
      |                       ^~~~ 
5 warnings and 2 errors generated.
gmake[3]: *** [Makefile:916: test-mail-index-transaction-update.o] エラー 1
gmake[3]: ディレクトリ '/usr/local/src/dovecot-2.3.21/src/lib-index' から出ます
gmake[2]: *** [Makefile:573: all-recursive] エラー 1
gmake[2]: ディレクトリ '/usr/local/src/dovecot-2.3.21/src' から出ます
gmake[1]: *** [Makefile:704: all-recursive] エラー 1
gmake[1]: ディレクトリ '/usr/local/src/dovecot-2.3.21' から出ます
gmake: *** [Makefile:548: all] エラー 2
軒並み、timezone 絡みのようで、この件に関するパッチが3つ公開されています:
その1 https://github.com/dovecot/core/commit/e983ead775671186b3c8567d59973d2e52b678c7
その2 https://github.com/dovecot/core/commit/1a7b1f66fe4b86cb642dbcfe5a0192c1b77d0e17
その3 https://github.com/dovecot/core/commit/867a37fa7b74f798a931fb582214b5377f57610e

〔その1 src/lib/ioloop-notify-kqueue.c を修正〕
@@ -11,6 +11,7 @@
1111  
1212 #include "ioloop-private.h"
1313 #include "llist.h"
14+ #include "time-util.h"
1415 #include <unistd.h>
1516 #include <fcntl.h>
1617 #include <sys/types.h>

〔その2 src/lib-index/test-mail-index-transaction-update.c を修正〕
@@ -6,6 +6,7 @@
66 #include "test-common.h"
77 #include "mail-index-private.h"
88 #include "mail-index-transaction-private.h"
9+ #include "utc-offset.h"
910  
1011 #include <time.h>
1112  
@@ -630,7 +631,9 @@
630631  
631632     /* daylight savings times were confusing these tests, so we'll now
632633       just assume that TZ=UTC */
633 -    test_assert(timezone == 0);
634+    time_t now = time(NULL);
635+    struct tm *local_time = localtime(&now);
636+    test_assert(utc_offset(local_time, now) == 0);
634637  
635638     hdr.messages_count = 10;
636639     t = mail_index_transaction_new();

〔その3 src/lib-index/test-mail-index-transaction-update.c を修正〕
@@ -648,13 +648,13 @@
648648         i_zero(&hdr);
649649         for (j = 0; j < N_ELEMENTS(hdr.day_first_uid); j++)
650650            hdr.day_first_uid[j] = 8-j;
651 -        hdr.day_stamp = tests[i].old_day_stamp + timezone;
651+        hdr.day_stamp = tests[i].old_day_stamp;
652652         memcpy(t->post_hdr_change, &hdr, sizeof(hdr));
653 -        mail_index_update_day_headers(t, tests[i].now + timezone);
653+        mail_index_update_day_headers(t, tests[i].now);
654654  
655655         struct mail_index_header new_hdr;
656656         memcpy(&new_hdr, t->post_hdr_change, sizeof(new_hdr));
657 -        test_assert_idx(new_hdr.day_stamp == tests[i].new_day_stamp + timezone, i);
657+        test_assert_idx(new_hdr.day_stamp == tests[i].new_day_stamp, i);
658658         test_assert_idx(memcmp(new_hdr.day_first_uid,
659659                     tests[i].new_day_first_uid,
660660                     sizeof(uint32_t) * 8) == 0, i);

これらのパッチを手動で当て、いつもの手順でコンパイルすることで、いくつか Warning が出るものの、通常通りの使用可能となるようです。
バージョンアップで、この不具合が解消されることを期待したいところ。

2024/01/13(土)危うく騙されそうになった、、

2024/01/13 6:03
可能な限り全文晒します:
EXPIRATION REMINDER

This notice is to bring to your notice that your domain invoice number
nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn is OVERDUE. example.com to be due
in 12 January 2024 is SUSPENDED.

The contact currently listed is xxxxxxxxx xxxxxxxxx.

RENEW example.comT
https://csschecker.com/cart/nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn

EXPIRATION:
12 JANUARY 2024

DISCLAIMER NOTICE:
We can not be held legally accountable for any claims, damage or
suffering that you may incur owing to the expiration of
example.com. Any such damages may include but are not solely
limited to: sales losses, lost files without backups, loss of results
in search rankings, lost appointments, undeliverable email and any
other business, service or technical damages that you may incur. For
further reference please refer to section 22.l.3.g of our TOS


LIABILITY:
This is the final notice that we are required to send out with regards
to the expiration of example.com.

SECURE ONLINE RENEWAL
https://csschecker.com/cart/nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn



Unable to click the link? Copy and Paste this into your browser:
HTTPS://CSSCHECKER.COM/CART/nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn

All web services will be restored automatically on example.com upon
receipt of payment. We thank you for your cooperation and continued
business.

ALERT SENT ON JANUARY 12 2024

The contents of this email are strictly confidential.
当該ドメインがレジストリ的に有効期限切れで使用不能になった、みたいな通知だが、
2ヶ月くらい前に自動更新したばかりで、whois で確認してもドメインステータスには問題は無さそう。

恐らく、ドメイン更新料金を騙し取ろうとしているのだろう。
ドメインレジストラ業者でないと、入手できない個人情報を使っているので、たちが悪い。
今後、こういうのには注意が必要である。皆さまもご注意ください。

2023/12/17(日)ドラッグストアや100円ショップで入手できるエッチング液を試す

2023/12/17 19:24 電子工作
『ドラッグストアや100円ショップで入手できるエッチング液』と言っても、エッチング液そのものを販売しているわけではありません。
ですが、エッチング液を作る材料をドラッグストアや大手の100円ショップで容易に入手できます。

今回、当方では初めて試すのですが、元情報は既にネット上に複数、かなり出回っています。
なので二番煎じですが、今回は初めてということもあり、要点だけ示す形になります。

きっかけは、地元の電子部品販売店で、サンハヤト社製のエッチング液が買えなくなったからなのです。
解る人には、どこの販売店か判ってしまうと思います。狸小路にある某店です。再度置いてくれることを希望しています。はい。
通信販売で本州の販売店から入手可能ですが、送料がかかる(結果、トータルで割高になる)上に、北海道では、早くても翌々日配達なので、入手の際、使い勝手があまりよろしくない。

代替になるもの(「腐食液」「エジンバラ液」と称するもの)はあるのですが、「店舗で直接入手できるか」から調べるのが、多忙な我が身にはとても手間で時間取れないので、これから紹介する手法に落ち着いたというところです。コメントで販売情報や価格の共有を頂けると嬉しいです。

前置きがまた長くなってしまいましたが、ここから本題になります。

○必要なもの
20231217_1.jpg


・クエン酸
・食塩
・オキシドール(500㎖ ものが望ましい)
=> この3つでエッチング液を作ります。
   クエン酸やオキシドールは、現在は100円ショップでは販売されていないようです。
   最近、クエン酸・オキシドール・食塩は、ダイソーや Seria といった大手の100円ショップで販売されているようです。
   【2024/01/12 追記】


・ジップロックまたは同等品(冷凍用を選ぶこと)
=> これは、100円ショップあたりでも入手できますね。

・重曹
・アルミホイル
=> 廃液処理で使います。
   重曹もダイソーやSeria といった大手の100円ショップで販売されているようです。【2024/01/12 追記】

・重量計
・計量カップ(200㎖ ものでよい)
・耐熱プラスチックトレイ(20cm x 30cm 以上、深さ 5cm 程度が望ましい)
・コーヒーフィルター
・コーヒードリッパー(プラスチック製のもの)
=> 調合や廃液処理に使います。重量計は郵便物の重量を計測する秤などが使えます。ホームセンターなどで販売しています。
   計量カップ・耐熱プラスチックトレイ・コーヒードリッパーは、一度でもエッチング処理に使用したら、
   有害物質が付着するため、本来の用途には使えません。
   なので、専用のものを別途用意するようにしましょう。重量計以外は、100円ショップで入手できるもので充分です。

○準備
クエン酸 4:食塩 1 の割合で混合粉末を作ります。
例えば、クエン酸40g に対し 食塩 10g という割合です。

どの紹介記事見ても4:1の記載ですが、食塩の割合を少し増やすと、エッチングが早く進むようです。
次に、混合粉末をオキシドールに溶かします。
オキシドールをジップロックに入れ、混合粉末を溶かします。完全に粉末が溶けるまで、攪拌してください。

オキシドールは、エッチング対象のプリント基板が浸かる程度で充分です。
基板サイズが 150mm x 75mm だったら、オキシドールは 150㎖ が適量かと思います。
150㎖ のオキシドールに対し、クエン酸 40g 食塩 10g で上手く行きます。
オキシドールの量に応じて、クエン酸と食塩の量を変えます。但し、4:1の割合は崩さないようにしましょう。

この様子を撮影するのを忘れたので、今回は割愛ということで。。。
液は無色透明のオキシドールの色、そのままです。


○エッチング実施
オキシドール混合液にエッチング対象のプリント版を入れると、途端にエッチング反応が始まります。
オキシドール混合液は、銅イオンが溶けだし、マリンブルーの色に変わっていきます。
湯煎すると反応が早く進みます。湯煎の際、液が35℃を越えないように注意します。
泡が湧いてきますので、揺すりながら泡を落としつつ状況を観察してください。
また、生暖かくなる程度に発熱するので、この点も注意してください。

ちなみに、この工程は従来のサンハヤト社製感光基板でも上手くいきます。
2023/12/08 発売開始の、新しいサンハヤト社製感光基板では、まだ試すことが出来ていません。もし新しい感光基板で、このエッチング手法を試した方が居られたら、コメントで結果共有して頂けると嬉しいです。

塩化第二鉄液によるエッチング同様、パターンの細かい部分から、広い場所にエッチングが進んで行きます。
エッチングは、最初は「本当に進んでいるのか?」という感じですが、終盤は一気に進むようです。
ここで、耐熱プラスチックトレイに水道水を入れておきましょう。水道水はプリント版が全部浸かる程度でよいです。

エッチングが終わったら、液からプリント版を引き上げて、耐熱プラスチックトレイに満たした水道水に浸し、水洗いします。

この途中経過も撮影するのを忘れたので、今回は割愛ということで。。。
物珍しさと、どうなるか判らない状態だったので、メンタル的余裕が無かったのが実際のところ。

エッチング完了後の結果だけは撮影しました。2枚同時で15分程度にて完了しました。
感光基板で露光した際に残るマスク素材を落とした後なので、銅箔が見えています。
20231217_2.jpg


結構そこそこな規模の回路なので、最初からぶっつけ本番なのが、かなりのリスクでした。
でも見ての通り、大変上手くいったので、この点は満足です。

○廃液処理
基本的に、この方法で作成したエッチング液は、再利用できないと思ったほうがよいでしょう。
サンハヤト社製エッチング液も、使用後のエッチング液を暫く保存したあとに再度使ってみると、実に使えない状態になっていることが多々あるので、そんなものかと思っています。

このままでは、有毒ということで下水に流せないので、廃液処理を行います。
廃液処理には、マリンブルーになった液に対して、1cm x 3cm 程度の短冊状にカットしたアルミホイルを数枚ずつ投入し、銅イオンを析出するということを行います。

この反応はアルミホイルを一度にたくさん入れると、反応が激しくなって液が熱くなるので、注意しつつ作業を行います。
液がこんな色になって、アルミホイルによる反応が起きなくなったら、この工程は終了です。
また、この工程で水素が発生します。有毒ガスではないですが、換気を良くし、火気を近づけないようにしましょう。
20231217_3.jpg


析出した銅の粉末混じりのヘドロのような液になります。まだ、これで作業完了ではありません。
酸性が強すぎて、このままでは下水に流せません。試しに pH試験紙でチェックしてみるとこんな感じです。
20231217_4.jpg


重曹を混ぜて中和します。銅粉末はもえないゴミで処分する必要があるため、予めコーヒードリッパーとコーヒーフィルターで、ろ過して分離すると良いでしょう。
下水に流すには pH 5.8 ~ pH 8.6 の範囲である必要があります。100㎖ に対し、重曹20g 程度が適当かと思います。
適当にpH 見ながら少しずつ混ぜたので、適量が良く判っていません。

中和した液は、2倍量以上の水道水と一緒に下水に流します。
次回、作業した際には、もうすこしマトモな手順を書き残したいと考えています。

2023/11/12(日)Python 一部モジュール・ライブラリが更新できない

FreeBSD12 → FreeBSD13 に更新後、アプリケーションのライブラリ等が旧OSバージョンにて構築したものだったりすると、それが原因で謎の動作不具合起こしたりする場合が稀にあるため、「安定動作」を目的として、全てのアプリケーションを再構築します。

通常、OSバージョンアップの場合、前バージョンまでの動作互換性は保証しているので、必ずしも必要ではないはずなのですが、それでも不可解な挙動が生じる場合があり、それを未然に防ぐために「全てのアプリケーション再構築」という作業を行うことにしているのです。

多くのマトモな方々には信じられないかもしれないですが、この事業を始めたばかりの頃、
『たった1回のアプリケーション障害で、しかもたった1日で一方的なサーバホスティング解消』をされたことがあり、これは純粋にアプリケーションの不具合で、OSアップデートが問題ということでもなく、運用側でどうすることも出来ない不可抗力な原因の事象でした。

当然、「一方的」だったので、説明責任のかけらも果たすことが出来ませんでした。
まぁ、そんなに信用できないなら「最初から易々と近づくな」と言いたいし、何より舐められまくってますね。
そんな態度の「上から目線なクライアント」は、相手にしないことは言うまでもないです。
きっと、他所でも毛嫌いされていることだろうと思われます。
20年以上やっていますが、この類の不具合は、この件含めて2回。
2回目は説明責任果たせたので、理解してもらえました。過去20年に限れば安定稼働しています。

前置きが長くなり過ぎたのですが、ここから本題。
FreeBSD12 → FreeBSD13 にOSアップデートして、アプリケーション再構築をすると、一部の Python アプリケーションが再構築出来ずに、以下のようなエラーを吐いてしまう。
  File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 972, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "/usr/local/lib/python3.9/site-packages/setuptools/__init__.py", line 18, in 
    from setuptools.dist import Distribution
  File "/usr/local/lib/python3.9/site-packages/setuptools/dist.py", line 34, in 
    from ._importlib import metadata
  File "/usr/local/lib/python3.9/site-packages/setuptools/_importlib.py", line 39, in 
    disable_importlib_metadata_finder(metadata)
  File "/usr/local/lib/python3.9/site-packages/setuptools/_importlib.py", line 28, in disable_importlib_metadata_finder
    to_remove = [
  File "/usr/local/lib/python3.9/site-packages/setuptools/_importlib.py", line 31, in 
    if isinstance(ob, importlib_metadata.MetadataPathFinder)
AttributeError: module 'importlib_metadata' has no attribute 'MetadataPathFinder'

ERROR Backend subprocess exited when trying to invoke get_requires_for_build_wheel
*** Error code 1
この現象は、時々起きるらしく、外国のメンテナンス作業者の解決策として、どうやら
# cd /usr/local/lib
# rm -rf python3.*
としてから、
# portupgrade -rf python.3.9.18
(インストールされている Python バージョンに合わせる)
とすると、解消する模様。やや強引かつ豪快な解決手段だが、これが確実なのだとか。
これをやったら、確かに問題は解消しました。

2023/11/03(金)FreeBSD14 と FreeBSD12

2023/11/03 7:07 サーバ運営・管理
最低でも月に一度は記事をアップしようという目標を密かに立てていたのですが、実際、殆ど時間的余裕が無く、最後の投稿から7ヶ月経過してしまいました。。
質問やコメントにも目を通しているものの、殆ど反応出来ずな状態です。

さて、いつの間にか FreeBSD 12は、リリースから5年が経過しようとしています。2018/12/11 に 12.0 がリリースされました。
メンテナンスサポート期間は5年と定められたため、5年後の月末を以ってサポート終了です。
20231103.png

〔FreeBSD 公式サイト日本語版より https://www.freebsd.org/ja/security/#sup
FreeBSD 12.4 を稼動させるサーバ機器が7台あるのですが、そのうちの5台を急遽 13.2 (2023/04/11 リリース)へアップデート中。

さて、FreeBSD 14 ですが、これもいつの間にか近日リリースなのです。
FreeBSD14.png

〔FreeBSD 公式サイトより https://www.freebsd.org/releases/14.0R/schedule/

どういう新機能等があるのか、概要の意訳をして確認してみました:
・root アカウントのデフォルトシェルは、sh(1) になる。 → 何で?
・デフォルトのMTAは、sendmail(8) から dma(8) に変更される。 → 既に Postfix 使いなので、関係ないか?
・Locale が CLDR41.0 , Unicode 14.0 に対応 → ちょっと対応遅いと思う。
・Base64 ライブラリが標準装備になった
・portsnap は削除した。代わりに git を使え。 → えぇぇ!?(でも gitup が使えそうだ)
・pw(8),bsdinstall(8) は、ユーザホームディレクトリを /usr/home 配下ではなく、/home 配下に作成するようにした。→ まともにするための改修ですね。
・libfido2 ライブラリが 1.13.0 になった。→ あれ、既に(大手企業で流行りの)パスキーに対応しているらしい。
・OpenSSL ライブラリが、1.1.1 から 3.0.12 になった。→ 流石にこれはアップデート必須です。
・mergemaster(8) は非推奨になった。代わりに etcupdate(8) を使え。→ えぇぇ!?
・デバイスドライバ amr(4)、iir(4)、mn(4)、mly(4)、nlmrsa(4) 、twa(4) は削除した。→ 時代の流れだね。
・Wi-Fi6 に対応した。 → やっとか。
・FreeBSD15 以後は 32bitプラットフォームをサポートしない。但し、互換機能で32bit アプリケーションの動作は可能とする。 → これも時代の流れですね。。2029年には32bitCPU でFreeBSD は動作しなくなるのかな。

他、多くの項目ありますが、当方が興味なかったり、説明が難しかったりしたので、結構端折っています。

〔2023/12/16 追記〕
FreeBSD14 は、2023/11/20 付けでリリースされました。
FreeBSD13同様、5年間のサポート(2028/11/30 まで)を実施するようです。
また、本家から日本語のページが無くなってしまいました。非営利ベースで担当出来る人が居なくなった模様。

2023/04/15(土)複数の電源トランスを扱う場合の注意点がよく判らない・・・

2023/04/15 23:37 電子工作
実際、事例が少ない上、要点しか書いていない場合が殆どで「何故か」の言及は殆ど皆無。

パワートランジスタが壊れた回路の概略図を描くとこんな感じ ↓
20230415_Epson_1_out.jpg


トランジスタの1つ、或いは2つがコレクタ・エミッタ間で短絡故障を起こしました。
電源トランスが複数あり、一次側の0V端子側、2次側の0V端子側を共に合わせて配線しています。
こうすることで、極性が合うはずと思っているんですが、

結果的に、金属ケース筺体を利用して、ダイオードブリッジの中間を共通接地にした形というアホな回路になっていたため、何かしらの循環電流が流れる感じになっていたのかもしれません。
C05 の端子間電圧が 54V になっていたり(ここは 42.5V 以上にはならないはず・・・・)、トランジスタが壊れなかったとしても、意味不明のスパークを筺体で発生させてヒューズが飛ぶ、とか起きる謎の現象もあったので、それは、この狂った配線が原因なのだろうか・・・?

いずれにしても、色々と直観的によろしくないと思うので、下記のように変更を予定しています。
20230415_Epson_2_out.jpg


ただ、これでもパワートランジスタ故障の原因が釈然としません。
破壊するとしたら、VEBOが絶対最大定格5Vを越えたくらいしか考えられない感じで、これが発生する条件というのがどうも判りません。。