SQLite で正規表現を使う

【環境】
CentOS 7


CentOS 7 の SQLite(標準パッケージ)単体では、正規表現を使えない。

以下からモジュールのソースを入手して、.so ライブラリをロードすれば使えるようになる。
https://github.com/ralight/sqlite3-pcre

ビルド

ビルドには、sqlite-devel パッケージが必要。

$ make
cc -shared -o pcre.so   -fPIC -W -Werror pcre.c -lpcre   -Wl,-z,defs

使い方

ライブラリのロード

sqlite> .load /path-to/pcre.so

※毎回設定するのが面倒な場合は、~/.sqliterc にロードの記述をする。

正規表現を使ったSQLの書式

sqlite> ... WHERE x REGEXP <regex>

試す

テストデータを用意。

sqlite> select * FROM sample;
os
--------------------
Mac OS X 10.9
Mac OS X 10.10
Mac OS X 10.11
Mac OS X 10.12
Mac OS X 10.13
Windows NT 6.3
Windows NT 10.0

ライブラリをロードする。

sqlite> .load ./pcre.so

正規表現でSELECTする。

sqlite> select * FROM sample WHERE os REGEXP '[0-9]\.[0-9]$';
os
--------------------
Mac OS X 10.9
Windows NT 6.3
Windows NT 10.0

PostgreSQL で文字列と数値があるデータを(ちょっと良く)ソートする

【環境】
CentOS 6/PostgreSQL 8.4
CentOS 7/PostgreSQL 9.2


例えば、文字列型のカラムにて、文字列と数値が混じったデータの場合にソートすると、次のようになる。

db=> SELECT * FROM sample_data ORDER BY os;
       os
-----------------
 Mac OS X 10.12
 Mac OS X 10.13
 Mac OS X 10.9
 Windows NT 10.0
 Windows NT 6.3


いい感じにソートするには、少し工夫する。

db=> SELECT * FROM sample_data 
ORDER BY
  SUBSTRING(os FROM '^[A-Za-z ]+'), 
  TO_NUMBER(SUBSTRING(os FROM '[0-9.]+$'), '9999999999');
       os
-----------------
 Mac OS X 10.9
 Mac OS X 10.12
 Mac OS X 10.13
 Windows NT 6.3
 Windows NT 10.0


考え方としては、以下の通り。

・SUBSTRING を使い、ソートするカラムの先頭から英字を抜き出して、ソートの第一条件とする。
・SUBSTRING を使い、ソートするカラムの後ろから数値とドット(小数点)を抜き出して、to_numberで適当な桁数(上記では10桁)のnumeric型にキャストして、ソートの第二条件とする。


ソートの条件をSELECTで抽出してみると、どうなっているか分かりやすい。

db=> SELECT 
  os, 
  SUBSTRING(os FROM '^[A-Za-z ]+'), 
  TO_NUMBER(SUBSTRING(os FROM '[0-9.]+$'), '9999999999') 
FROM 
  sample_data;
        os        |  substring  | to_number
------------------+-------------+-----------
 Windows NT 10.0  | Windows NT  |       100
 Windows NT 6.3   | Windows NT  |        63
 Mac OS X 10.12   | Mac OS X    |      1012
 Mac OS X 10.9    | Mac OS X    |       109
 Mac OS X 10.13   | Mac OS X    |      1013


ここに、'Mac OS X 10.12.1' というデータが入ってくると、うまくいかない(´・ω・`)

db=> SELECT * FROM sample_data
ORDER BY
  SUBSTRING(os FROM '^[A-Za-z ]+'),
  TO_NUMBER(SUBSTRING(os FROM '[0-9.]+$'), '9999999999');
        os
------------------
 Mac OS X 10.9
 Mac OS X 10.12
 Mac OS X 10.13
 Mac OS X 10.12.1
 Windows NT 6.3
 Windows NT 10.0


バージョンの区切りになっているドットの出現回数が違うのが原因。
この場合、出現回ごとに桁数を揃えて(ドットで区切って0詰めする)、ソート条件に追加すると意図したようにソートされる。

db=> SELECT * FROM sample_data
ORDER BY
  SUBSTRING(os FROM '^[A-Za-z ]+'),
  LPAD(SPLIT_PART(SUBSTRING(os FROM '[0-9.]+$'), '.', 1), 3, '0'),
  LPAD(SPLIT_PART(SUBSTRING(os FROM '[0-9.]+$'), '.', 2), 3, '0'),
  LPAD(SPLIT_PART(SUBSTRING(os FROM '[0-9.]+$'), '.', 3), 3, '0');
        os
------------------
 Mac OS X 10.9
 Mac OS X 10.12
 Mac OS X 10.12.1
 Mac OS X 10.13
 Windows NT 6.3
 Windows NT 10.0


試しに、'Mac OS X 11.0' や 'Mac OS X 9.99' という架空のデータを入れて試してみる。

db=> SELECT * FROM sample_data
ORDER BY
  substring(os from '^[A-Za-z ]+'),
  LPAD(SPLIT_PART(SUBSTRING(os FROM '[0-9.]+$'), '.', 1), 3, '0'),
  LPAD(SPLIT_PART(SUBSTRING(os FROM '[0-9.]+$'), '.', 2), 3, '0'),
  LPAD(SPLIT_PART(SUBSTRING(os FROM '[0-9.]+$'), '.', 3), 3, '0');
        os
------------------
 Mac OS X 9.99
 Mac OS X 10.9
 Mac OS X 10.12
 Mac OS X 10.12.1
 Mac OS X 10.13
 Mac OS X 11.0
 Windows NT 6.3
 Windows NT 10.0


いい感じでソートされる。

ソート条件がどうなっているか、SELECTで抽出してみると、桁毎に0詰めになっている事が確認できる。

db=> SELECT 
    os,
    LPAD(SPLIT_PART(SUBSTRING(os FROM '[0-9.]+$'), '.', 1), 3, '0'),
    LPAD(SPLIT_PART(SUBSTRING(os FROM '[0-9.]+$'), '.', 2), 3, '0'),
    LPAD(SPLIT_PART(SUBSTRING(os FROM '[0-9.]+$'), '.', 3), 3, '0')
 FROM sample_data;
        os        | lpad | lpad | lpad
------------------+------+------+------
 Windows NT 10.0  | 010  | 000  | 000
 Windows NT 6.3   | 006  | 003  | 000
 Mac OS X 10.12   | 010  | 012  | 000
 Mac OS X 10.9    | 010  | 009  | 000
 Mac OS X 10.13   | 010  | 013  | 000
 Mac OS X 10.12.1 | 010  | 012  | 001
 Mac OS X 11.0    | 011  | 000  | 000
 Mac OS X 9.99    | 009  | 099  | 000


但し、前半に文字列があり、後半に数字(ドットも可)がある、という前提で、バージョン部分が「x.y.z」というフォーマットしか判別できない。

使える場面が限られるけど、とりあえずメモ。
UserAgentから取り出したOSのソートには使えそう。


参考;
https://www.postgresql.jp/document/9.2/html/functions-string.html
https://www.postgresql.jp/document/9.2/html/functions-formatting.html

rsyslog に *** Slice of root のログが出力される

【環境】

# cat /etc/redhat-release
CentOS Linux release 7.4.1708 (Core)

# rpm -qa | grep rsyslog
rsyslog-8.24.0-12.el7.x86_64

# rpm -qa | grep systemd
systemd-libs-219-42.el7_4.4.x86_64
systemd-sysv-219-42.el7_4.4.x86_64
systemd-219-42.el7_4.4.x86_64


以下のようなログが定期的に出力される。

# cat /var/log/messages
・・・省略
Jan 10 22:30:01 hoge systemd: Created slice User Slice of root.
Jan 10 22:30:01 hoge systemd: Starting User Slice of root.
Jan 10 22:30:01 hoge systemd: Started Session 236 of user root.
Jan 10 22:30:01 hoge systemd: Starting Session 236 of user root.
Jan 10 22:30:01 hoge systemd: Removed slice User Slice of root.
Jan 10 22:30:01 hoge systemd: Stopping User Slice of root.


journald にも出力されているが、rsyslog 側で無視して、ファイルへ書き込まないようにする。
手順は、以前と同じ。
shobon.hatenablog.com


以下のようなファイルを作成する。

# cat /etc/rsyslog.d/ignore-systemd-session-slice.conf
if $programname == "systemd" and ($msg contains "Starting Session" or $msg contains "Started Session" or $msg contains "Created slice" or $msg contains "Starting user-" or $msg contains "Starting User Slice of" or $msg contains "Removed session" or $msg contains "Removed slice User Slice of" or $msg contains "Stopping User Slice of") then stop

rsyslogを再起動して反映し、/var/log/messages に出力されなくなった事を確認する。

# systemctl restart rsyslog


参考;
https://access.redhat.com/solutions/1564823

Packet send failed to や Failed to send packet trying to query name がログに出力される

【環境】

# cat /etc/redhat-release
CentOS Linux release 7.4.1708 (Core)

# rpm -qa | grep samba
samba-client-libs-4.6.2-12.el7_4.x86_64
samba-common-tools-4.6.2-12.el7_4.x86_64
samba-common-4.6.2-12.el7_4.noarch
samba-libs-4.6.2-12.el7_4.x86_64
samba-4.6.2-12.el7_4.x86_64
samba-common-libs-4.6.2-12.el7_4.x86_64

/var/log/messages に次のようなログが頻繁に出力される。

Jan 10 21:07:40 hoge nmbd[1106]: [2018/01/10 21:07:40.837878,  0] ../source3/libsmb/nmblib.c:873(send_udp)
Jan 10 21:07:40 hoge nmbd[1106]:  Packet send failed to 192.168.10.255(138) ERRNO=許可されていない操作です
Jan 10 21:09:20 hoge nmbd[1106]: [2018/01/10 21:09:20.367062,  0] ../source3/libsmb/nmblib.c:873(send_udp)
Jan 10 21:09:20 hoge nmbd[1106]:  Packet send failed to 192.168.10.255(137) ERRNO=許可されていない操作です
Jan 10 21:09:20 hoge nmbd[1106]: [2018/01/10 21:09:20.367202,  0] ../source3/nmbd/nmbd_packets.c:179(send_netbios_packet)
Jan 10 21:09:20 hoge nmbd[1106]:  send_netbios_packet: send_packet() to IP 192.168.10.255 port 137 failed
Jan 10 21:09:20 hoge nmbd[1106]: [2018/01/10 21:09:20.367233,  0] ../source3/nmbd/nmbd_namequery.c:245(query_name)
Jan 10 21:09:20 hoge nmbd[1106]:  query_name: Failed to send packet trying to query name TEST<1d>

ホストのiptables等で、パケットの送信が遮断されているためと思われる。
iptablesで許可すると、journald/rsyslog いずれにも、上記のログは出なくなる)

iptables で制限を解除すれば良いけれど、何らかの事情でそれが出来ない場合の対処法。

CentOS 7の場合、ログはいったん journald が受け取り、rsyslog が受け取っている構造になっている。
ここで、journald → rsyslog へ渡される際に、rsyslog 側で無視するようにすれば、journald にログは残しつつ、rsyslog からファイルへの出力を抑制できる。
(但し、CentOS 7の journald の初期設定では、/var/run/log/journal 以下に書き込まれるので、OSを再起動すると消える)

rsyslog側で無視するには、以下のようなファイルを作成する。

# cat /etc/rsyslog.d/ignore-nmbd-query.conf
if $programname == "nmbd" and ($msg contains "Packet send failed to" or $msg contains "query_name" or $msg contains "send_netbios_packet" or $msg contains "send_udp") then stop

ここには、rsyslog の条件式である「contains」を利用して、特定のプロセスにおいて特定キーワードを含むログを無視するよう記載している。
rsyslog で他に利用可能な値や条件は、以下の通り。
http://www.rsyslog.com/doc/v8-stable/configuration/properties.html
http://www.rsyslog.com/doc/v8-stable/configuration/filters.html


また、CentOS 7のrsyslog は、/etc/rsyslog.d/*.conf を起動時に読み込むので、同様のものがあれば、別ファイルに作成すると管理しやすい。

読み込み箇所↓
# cat /etc/rsyslog.conf | grep '*.conf'
$IncludeConfig /etc/rsyslog.d/*.conf

rsyslogを再起動して反映し、/var/log/messages に出力されなくなった事を確認する。

# systemctl restart rsyslog

journald に記録されたログを見たい場合は、以下のようにする。

# journalctl -u nmb

pecl の oci8 を更新する

pecl/oci8の新しいのが出ていたので、更新した話。

バージョンとしては、oci8/2.0.11 → 2.0.12 への更新。

リリース日は、2016-08-18 だったので、1年も気づいていなかった(´・ω・`)

PECL oci8
https://pecl.php.net/package/oci8

Changelog for oci8
https://pecl.php.net/package-changelog.php?package=oci8


【環境】
CentOS 6.9/7.4(PHPは標準リポジトリ


現在のバージョン

# pecl list
Installed packages, channel pecl.php.net:
=========================================
Package Version  State
oci8    2.0.11   stable


バージョンを指定して更新(バージョン2.1以降は、PHP7が必要)

# pecl upgrade oci8-2.0.12
downloading oci8-2.0.12.tgz ...
Starting to download oci8-2.0.12.tgz (191,954 bytes)
......................................done: 191,954 bytes
11 source files, building
running: phpize
Configuring for:
PHP Api Version:         20100412
Zend Module Api No:      20100525
Zend Extension Api No:   220100525
・
・省略
・
Build process completed successfully
Installing '/usr/lib64/php/modules/oci8.so'
upgrade ok: channel://pecl.php.net/oci8-2.0.12
configuration option "php_ini" is not set to php.ini location
You should add "extension=oci8.so" to php.ini
Segmentation fault

CentOS 7の方だけ、何故か最後に「Segmentation fault」が出た。
気持ち悪いので、一度アンインストールして再インストールすることに。

# pecl uninstall oci8
Unable to remove "extension=oci8.so" from php.ini
uninstall ok: channel://pecl.php.net/oci8-2.0.12


バージョンを指定して再インストール・・・したらエラーが出なかったので良し。

# pecl install oci8-2.0.12


バージョンの確認

# pecl list
Installed packages, channel pecl.php.net:
=========================================
Package Version  State
oci8    2.0.12   stable


モジュールを読み直すために、httpdを再起動

CentOS 7
# systemctl restart httpd.service

CentOS 6
# /etc/init.d/httpd restart

cifsマウントのオプションが間違っていた事に気が付いた

CentOS 7.3→7.4に更新した後から、cifsで接続しているNASへ繋がらなくなった。


autofs で自動マントの設定をしているものの、接続しようとすると、次のようなメッセージが出た。

# ls /mnt/nas

Broadcast message from root@xxxxxxx (Fri 2017-09-15 05:33:14 JST):

Password entry required for 'Password for root@//nas/path:' (PID 8688).
Please enter password with the systemd-tty-ask-password-agent tool!

/var/log/messages には、オプションが違う旨のログが出ていた。
(ここで、/path/credential_file はNASの認証情報を記載したファイル)

Sep 15 05:33:14 localhost kernel: CIFS: Unknown mount option "credential=/path/credential_file"


autofs や mount の man でオプションを確認しても、「credential」が無い。

これは cifs-utils のものだと思い出して、そのマニュアルを参照、
https://linux.die.net/man/8/mount.cifs

すると、「credential」が無くて、「credentials」がある。

今まで正常に動作していたのに?と思ったが、とりあえず、

autofs の設定ファイルにて、以下のようにマウントのオプションを修正したら接続できた。

 credential=/path/credential_file
  ↓
 credentials=/path/credential_file


前に使っていた「credential」というオプションで動作していた方が異常だったと思われる。
なお、CentOS 6.x では、このオプションで動作していた。(2017/9/15時点)

どういう訳か調べる事にした。

まずは、RPMソースを入手

# yumdownloader --enablerepo=base-source --source cifs-utils

展開してファイルを確認。

$ rpm -ivh cifs-utils-6.2-10.el7.src.rpm
$ cd ~/rpmbuild/SOURCES
$ ls -1
0001-asn1-fix-use-after-free-in-asn1_write.patch
0001-autoconf-Use-DEFS-when-building-idmapwb.so.patch
0001-autoconf-fix-link-of-libwbclient.patch
0001-cifs-use-krb5_kt_default-to-determine-default-keytab.patch
0001-get-setcifsacl-fix-bad-bit-shifts.patch
0002-getcifsacl-remove-some-dead-code.patch
0002-mount.cifs-on-2nd-try-mount.cifs-must-also-uppercase.patch
0003-asn1-remove-some-usused-functions.patch
0003-mtab.c-include-paths.h-for-_PATH_MOUNTED.patch
0004-data_blob-clean-out-unused-functions.patch
0004-manpage-clarify-use-of-backupuid-and-backupgid-in-mo.patch
0005-mount.cifs-fix-bad-free-of-string-returned-by-dirnam.patch
0005-mount.cifs-ignore-x-mount-options.patch
0007-aclocal-fix-typo-in-idmap.m4.patch
0008-mount.cifs-Removed-extra-comma-in-front-of-domain.patch
0009-mount.cifs-Accept-empty-domains-on-the-command-line.patch
0010-mount.cifs-Fixed-command-line-parsing-and-aligned-wi.patch
0011-mount.cifs-Remove-unneeded-stdbool-header-include.patch
0012-manpage-document-mfsymlinks-in-the-mount.cifs-man-pa.patch
cifs-utils-6.2.tar.bz2


grepで関係しそうなものを検索

$ grep -rl 'cred' ./
./0001-autoconf-Use-DEFS-when-building-idmapwb.so.patch
./0010-mount.cifs-Fixed-command-line-parsing-and-aligned-wi.patch

バージョン6.2.9の方では、パッチが0005-* までしか含まれていないので、「0010-mount~」が該当する。

該当行を表示

$ grep 'cred' ./0010-mount.cifs-Fixed-command-line-parsing-and-aligned-wi.patch
-       if (strncmp(token, "cred", 4) == 0)
+       if (strcmp(token, "cred") == 0 || /* undocumented */
+               strcmp(token, "credentials") == 0)


前のバージョンでは、

-       if (strncmp(token, "cred", 4) == 0)

のように、先頭4文字だけチェックしていた模様。

今回のバージョンでは、

+       if (strcmp(token, "cred") == 0 || /* undocumented */
+               strcmp(token, "credentials") == 0)

のように、"cred" または "credentials" の完全一致をチェックするようになっている。
但し、"cred"はドキュメント化されていない旨がコメントに書いてある。


従って、先頭4文字が一致していた「credential」という存在しないオプション名でも、動作していた事になる。

なお、これは autofs に限らず、mountコマンドで cifs をマウントする時も同じ。

まだ修正されていないCentOS 6.x で、以下を試してみた。

成功:
# mount -t cifs -o cred123=/path/credential_file //host/path /mnt

失敗:
# mount -t cifs -o cre=/path/credential_file //host/path /mnt

といったように、先頭4文字だけで判定されていた。
(そのうち修正されるかも)

mail コマンドで送信したメールが添付になる

【環境】
$ cat /etc/redhat-release
CentOS Linux release 7.3.1611 (Core)

$ mail -V
12.5 7/5/10


やりたかったのは、diff の結果で差分があった時だけメールを送る事。

実査にmailコマンドでdiffの結果をメールしたら、受信側で添付ファイルになってしまった。

テキストメールのつもりが、メールヘッダを見ると、次のようになっていた。

Content-Type: application/octet-stream

diffの結果は何もないはずなのに、見えない何かがある?

となると、怪しいのは制御コードが思い付く。

調べてみると、diff対象のテキストファイルの行末にCRが入っている事がある模様で、diffの結果に改行コード(CR:^M)が含まれてしまうようだった。

diff対象のファイルを出力する側は修正できないため、こちら側で対処するしかない・・・

という訳で、次のようにしてdiffの結果から制御コード(^M)を削除する。

sed -e 's/^M//g' > output.txt

ここで、^M は、ハットと大文字のMではなく、vi で

 Ctrl + v
 Ctrl + m

を入力する。

動作確認には、cat コマンドに -v オプションを付けて制御コードを表示して確認した。