systemd - service 編

こんにちは、masm11 です。

前回から始まった systemd シリーズ。今回は .service ファイルの書き方です。

.service ファイルとは

このファイルにサービスの定義を記述します。

sysvinit 等であれば /etc/init.d//etc/rc.d/ に置いていた script の代わりとなるものです。

ファイルの置き場所としては、OS のパッケージに含まれる場合は /usr/lib/systemd/system//lib/systemd/system/ にある場合が多いと思いますが、 自分で作る場合は /etc/systemd/system/ に置きます。

[Unit] 部分

.service ファイルは、まず [Unit] という行から始まります。 [Unit] 部分は以下のような感じになっています。

[Unit]
Description=ZFS replication service
After=network.target
Requires=network.target

Description はこのサービスの説明です。あまりたいした意味はないのですが、 以下のように、ログに出力される時などに使われます。

10月 08 02:00:00 luna systemd[1]: Started ZFS replication service.

AfterRequires は依存関係を記述します。 Requires を書いておくと、network.target が起動に失敗した場合は この unit は起動しなくなります。

依存関係を記述する方法は、他にも WantsConflicts などいろいろありますが、 AfterRequires を使っていればわりとなんとかなっています。

[Service] 部分

.service ファイルには [Service] 部分があります。 以下のような感じになっています。

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/etc/systemd/lo-setup start
ExecStop=/etc/systemd/lo-setup stop
Type

simple, exec, forking, oneshot, dbus, notify, idle の 中から選択して指定します。よく使うのは oneshot, simple, forking です。

  • oneshot: 一度実行してすぐ終了するタイプに指定します。
  • simple: デフォルトです。 起動したプロセスがサービスを提供する場合に指定します。
  • fork: 起動したプロセスが起動時に fork する場合に指定します。
ExecStart / ExecStop / ExecStartPre

ExecStartsystemctl start した時に実行するコマンドを指定します。

ExecStopsystemctl stop した時にサービスを停止するコマンドを指定します。 省略することもよくあります。省略した場合は systemd がプロセスにシグナルを 送って止めます。

ExecStartPreExecStart の前に実行しておきたいコマンドを指定します。 ExecStartPre は複数指定することができます。

マニュアルには

If the command is not a full (absolute) path, it will be resolved to a full path using a fixed search path determinted at compilation time.

と、コマンドがフルパスでなくても大丈夫そうなことが書いてありますが、 経験的にはフルパスでないと動いてくれません。

コマンドの前に - を書いておくと、エラーを無視してくれるようです。 ExecStartPre でちょっと先に実行しておきたいけど、エラーになる場合もある、 というような場合に便利そうです。

RemainAfterExit
RemainAfterExit=yes

と指定しておくと、プロセスが終了しても、異常扱いになりません。

Restart / RestartSec
Restart=always

と指定しておくと、プロセスが終了した場合に、もう一度起動してくれます。

ただ、すぐに起動してしまうので、間隔をあけたい場合は、追加で

RestartSec=5

と指定しておけば、5秒待ってくれます。

User

プロセスのユーザを指定します。

User=masm

聞くところによると、存在しないユーザを指定すると root になってしまう 仕様だそうです。User を指定する際にはご注意ください。

StandardOutput / StandardError

これらは、プロセスの標準出力と標準エラー出力をどうするかを指定します。

私は大抵 journal と指定しています。この場合、systemd の journal に 出力され、journalctl コマンドで見ることができます。

他にも、ファイルへ出力したり、tty に出力したり、いろいろな指定ができるようです。

[Install] 部分

これを書いておくと、systemctl enable できるようになります。

[Install]
WantedBy=multi-user.target

multi-user で起動する際には、この .service も start する、 という指定です。

大抵の場合は multi-user.target で大丈夫です。

@%i

.service ファイルのファイル名に @ を含めることができます。 例えば zfsnap@.service という感じです。

この場合、systemctl enablesystemctl start の際には、

systemctl start zfsnap@2w.service

というように指定します。@ の後ろに 2w と付けました。 この 2w.service ファイルの中で、%i を使って受け取れます。 具体的には以下のように使います。

[Service]
Type=oneshot
ExecStart=/usr/bin/zfsnap snapshot -a %i zbak/android zbak/svc zbak/tama

この場合だと、%i2w に置き換わって、

/usr/bin/zfsnap snapshot -a 2w zbak/android zbak/svc zbak/tama

が実行されるわけです。

この zfsnap@.service ファイル一つで、zfsnap@2w.service, zfsnap@3d.service など いろんな使い方ができます。

あまり使わないですが、たまに便利なことがある機能です。

まとめ

今回は .service ファイルの書き方を経験を交えながら淡々と説明しました。 前回同様、私が使うものだけに絞っています。

.service ファイルはそれだけで使うこともできますが、 他の unit ファイルと組み合わせて使うこともできる、便利なものです。 組み合わせ方は次々回以降に説明しますのでご期待下さい。

ではまた!

systemd - サブコマンド編

お久しぶりです、masm11 です。 今更かと思いますが、今回から何回かに分けて、systemd について説明したいと思います。

systemd とは、Linux の init システムを置き換えるものです。 PID 1 として起動され、 サービスの管理やネットワークの管理、時刻の管理、 syslog の代替、cron の代替、果ては boot loader といった機能まで持ちます (これらの機能全てが PID 1 で実行されているわけではないです)。

今回は、systemd のコマンド systemctl について、サブコマンドをご紹介します。

list-unit-files

list-unit-files は、どんなサービスがあるのかを、列挙することができます。

luna:~ % systemctl list-unit-files | grep sshd.service
sshd.service                                                              enabled         disabled     

拡張子のように最後に .service が付くものがサービスです。 .service の他にも .timer.socket などもあります。 これらを unit file と呼び、次回以降に紹介します。

start/stop/restart

各 unit file を start や stop することができます。

luna:~ % sudo systemctl start sshd.service

サービスを起動/停止するイメージですが、タイマー等も start することができます。

サービスを start した時にどんなコマンドが実行されるかは、 unit file の中に書いてあります。

enable/disable

各 unit file は enable や disable することができます。

enable しておくと、次回 boot 時に自動的に start されます。

luna:~ % sudo systemctl enable sshd.service
Created symlink /etc/systemd/system/multi-user.target.wants/sshd.service → /usr/lib/systemd/system/sshd.service.
luna:~ % 

この出力の通り、実は enable はシンボリックリンクで表現されていますので、 単にシンボリックリンクを張ることでも同じことができます。

status

サービスの状態を表示することができます。

luna:~ % systemctl status sshd.service
● sshd.service - OpenSSH Daemon
     Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: disabled)
     Active: active (running) since Wed 2020-09-23 21:20:18 JST; 1h 10min ago
   Main PID: 1130 (sshd)
      Tasks: 1 (limit: 18425)
     Memory: 1.5M
     CGroup: /system.slice/sshd.service
             └─1130 sshd: /usr/bin/sshd -D [listener] 0 of 10-100 startups

 923 21:20:18 luna systemd[1]: Started OpenSSH Daemon.
 923 21:20:18 luna sshd[1130]: Server listening on 0.0.0.0 port 22.
 923 21:20:18 luna sshd[1130]: Server listening on :: port 22.

Loaded: 行の中央やや右寄りの enabled が enable してあるかどうかを表示しています。 右端の vendor preset: disabled はデフォルトで enable か disable かを表示しているだけですので、 間違えないようにしましょう。

Active: 行の running は実行中であることを示しています。 一度実行してすぐ終了するサービスもあって、そういうものは exited になっていますので、 running 以外だから即異常、というわけではありません。

最後の3行は sshd に関するログを表示しています。

mask/unmask

「この unit file 要らない!」と思ったら、mask することができます。

luna:~ % sudo systemctl mask sshd.service
Created symlink /etc/systemd/system/sshd.service → /dev/null.

これもシンボリックリンクを張っているだけですので、 シンボリックリンクを張れば同様のことができます。

ところで、サービスが要らないなら disable すればいいじゃん、と思いませんか? 実は disable できないサービスも存在するのです。 そんな時には mask が役に立ちます。

daemon-reload

unit file を書き換えてもすぐには反映されません。 reboot するか、systemctl daemon-reload することで反映されます。

よく忘れて戸惑います。気をつけましょう…

list-timers

.timer という種類の unit file があります。要するに cron の代替機能です。

各タイマーが、最近いつ発動して、次にいつ発動するのか、等を表示することができます。

luna:~ % systemctl list-timers -a
NEXT                        LEFT                LAST                        PASSED             UNIT                         ACTIVATES
Thu 2020-09-24 00:00:00 JST 1h 18min left       Wed 2020-09-23 00:00:00 JST 22h ago            man-db.timer                 man-db.service
Thu 2020-09-24 00:00:00 JST 1h 18min left       Wed 2020-09-23 00:00:00 JST 22h ago            shadow.timer                 shadow.service
Thu 2020-09-24 00:00:00 JST 1h 18min left       Wed 2020-09-23 00:00:00 JST 22h ago            updatedb.timer               updatedb.service
Thu 2020-09-24 21:57:52 JST 23h left            Wed 2020-09-23 21:35:20 JST 1h 5min ago        systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service

4 timers listed.

-a を付けると、stop 状態のタイマーも表示してくれます。 私は常に -a を付けることにしています。

最初に紹介した list-unit-files についてもそうなのですが、 私は「いいから全部出せ!」と思っていて、多すぎたら grep で必要な行だけ抽出する、というやり方をしています。

まとめ

systemd について、今回は概要っぽくサブコマンドについてご紹介しました。

サブコマンドは他にもまだまだありまして、今回紹介したのは私がよく使うものだけに絞りました。

先程改めて man systemctl したところ、 全然記憶にないものがいろいろありました。おそらく時々増えているのでしょう。 今後も増えていくでしょうから、時々は man を確認してみるのが良さそうです。

次回は unit file の .service について紹介したいと思います。

ではまた。

高速検索 the_silver_searcher を使ってみる

こんにちは、masm11 です。 たまには新しいツールでも導入して快適になってみましょう。

今回ご紹介するのは、高速検索ツール the_silver_searcher です。

検索の仕方の変遷

以前は grep を使ってましたよね。

grep foo *.rb

これだとカレントディレクトリしか探してくれないので、

grep foo *.rb */*.rb

なんてしてみたり、もしくは grep に -r オプションができたので

grep -r foo .

ってのもありかもしれません。

ずっと私は find, xargs, grep を使っていました。

find . -name \*.rb -print0 | xargs -0 grep foo

こんなのですね。この程度ならさらさらと打てます。 いつも同じパターンなので。

しかし、遅いのです。もっとズババッッ! と検索して欲しいです。

the_silver_searcher とは

そこで、the_silver_searcher の登場です。速いです! 速さの秘訣を本家 github サイトから超訳すると、

  • pthread を使って並列処理している
  • バッファに読み込まず mmap() を使っている
  • Boyer-Moore の検索アルゴリズムを使っている
  • 正規表現の検索には JIT コンパイラを使っている
  • 同じ正規表現を使う時には正規表現コンパイラのキャッシュが効いている
  • .gitignore の処理も配列とバイナリサーチを駆使

さて、ではまずはインストールしてみます。

macOS の場合は Homebrew が使えます。Homebrew がインストールされているなら、

brew install the_silver_searcher

これだけです。

CentOS の場合は、

sudo yum install the_silver_searcher 

ArchLinux の場合は、

sudo pacman -S the_silver_searcher 

です。

では使ってみましょう。

the_silver_searcher というのは、ソフトウェアの名前です。 これが即ちコマンド名なわけではありません。コマンド名は ag です。 silver → 銀 → ag ですね。

ざっと実行してみた感じ、↓こんな出力です。

luna:emacs % ag pgtk_menu_show
src/pgtkterm.indent
4793:  terminal->menu_show_hook = pgtk_menu_show;

src/pgtkterm.c
4650:  terminal->menu_show_hook = pgtk_menu_show;

src/menu.h
63:extern Lisp_Object pgtk_menu_show (struct frame *, int, int, int,

src/pgtkmenu.c
619:pgtk_menu_show (struct frame *f, int x, int y, int menuflags,
luna:emacs % 

実際には色が付いていてそこそこ見やすくなっています。

f:id:masm11:20200817172509p:plain

さて、検索時間を比べてみましょう。一体どのくらい速いのでしょうか?

まずは find, xargs, grep でやってみます。 できるだけキャッシュに載せるため、連続で実行しています。

luna:emacs % for i in `seq 10`; do time bash -c '(find . -type f -print0 | xargs -0 grep pgtk_menu_show) > /dev/null 2>&1'; sleep 1; done
bash -c   0.98s user 0.70s system 51% cpu 3.259 total
bash -c   1.39s user 1.03s system 47% cpu 5.137 total
bash -c   1.27s user 1.13s system 46% cpu 5.183 total
bash -c   1.26s user 1.18s system 47% cpu 5.148 total
bash -c   1.30s user 1.17s system 47% cpu 5.197 total
bash -c   1.27s user 1.01s system 44% cpu 5.051 total
bash -c   1.39s user 1.09s system 47% cpu 5.165 total
bash -c   1.33s user 1.16s system 47% cpu 5.239 total
bash -c   1.10s user 1.02s system 45% cpu 4.624 total
bash -c   0.92s user 0.96s system 47% cpu 3.942 total
luna:emacs % 

1回5秒程かかっています。次に ag を使って検索してみます。

luna:emacs % for i in `seq 10`; do time ag pgtk_menu_show > /dev/null; sleep 1; done
ag pgtk_menu_show > /dev/null  0.10s user 0.16s system 361% cpu 0.071 total
ag pgtk_menu_show > /dev/null  0.14s user 0.14s system 406% cpu 0.067 total
ag pgtk_menu_show > /dev/null  0.11s user 0.16s system 407% cpu 0.065 total
ag pgtk_menu_show > /dev/null  0.13s user 0.14s system 401% cpu 0.066 total
ag pgtk_menu_show > /dev/null  0.14s user 0.13s system 400% cpu 0.067 total
ag pgtk_menu_show > /dev/null  0.11s user 0.13s system 385% cpu 0.061 total
ag pgtk_menu_show > /dev/null  0.11s user 0.15s system 401% cpu 0.065 total
ag pgtk_menu_show > /dev/null  0.10s user 0.16s system 401% cpu 0.066 total
ag pgtk_menu_show > /dev/null  0.08s user 0.20s system 362% cpu 0.077 total
ag pgtk_menu_show > /dev/null  0.12s user 0.16s system 400% cpu 0.068 total
luna:emacs % 

1回 0.1 秒未満!? 素晴らしいスピードです。

the_silver_searcher を Emacs で使う

dumb-jump

Emacs でタグジャンプと言えば、TAGS ファイルでしょうか。 私はほとんどお世話になったことがありません。 ずっと find, xargs, grep でやってました。

上で見た通り、ag がこれだけのスピードで返答を返せるなら、 TAGS なんてなくても、その場で力任せに検索してやればいいですよね? そのためのパッケージが Emacs にあります。dumb-jump といいます (dumb-jump は grep にも対応しています)。

このパッケージは主に「このメソッドの実体はどこにあるのかな~」 と探すためにあるようです。

  1. M-x package-list-packages で dumb-jump を探してインストールします。
  2. ついでに ivy, posframe, ivy-posframe も使うことにするのでインストールします。
  3. ~/.emacs (または ~/.emacs.d/init.el ですかね?) に以下のように設定して Emacs を再起動します。 ivy と ivy-posframe の設定もしてるので長いですが、dumb-jump の 設定自体は最後の段落のみです。
(require 'ivy)
(ivy-mode 1)
(setq ivy-use-virtual-buffers t)
(setq enable-recursive-minibuffers t)
(setq ivy-height 15)
(setq ivy-extra-directories nil)
(setq ivy-re-builders-alist
      '((t . ivy--regex-plus)))
(define-key ivy-minibuffer-map "\C-t" 'ivy-scroll-down-command)

(require 'ivy-posframe)
(setq ivy-posframe-display-functions-alist
      '((t . ivy-posframe-display-at-frame-center)))
(ivy-posframe-mode 1)

(require 'dumb-jump)
(setq dumb-jump-mode t)
(setq dumb-jump-selector 'ivy)
(setq dumb-jump-use-visible-window nil)

さて、何らかのソースコードを開き、メソッドを呼び出しているところに テキストカーソルを合わせ、おもむろに M-C-g を押してみてください。 1秒かからないくらいで、メソッド定義位置の候補が childframe に 表示されたと思います。「これが定義のはず」と思う行を選択して Enter を 押せば、その行にジャンプします。

「ジャンプ前の行どこだっけ?」と思ったら、M-C-p で戻れます。

また、M-C-q を使うと、そのメソッドを使っている箇所がいくつか childframe に表示されます。メソッドが他の箇所で具体的にどう呼ばれているかを見たい 時に便利... なのでしょうか、これは便利さがまだよく解りません。

そして、言語によっては、カーソルのある位置からメソッド名を うまく取り出せないことがあるようです。 そういう時は、メソッド名を範囲指定してから M-C-g を押すと、 うまく検索してくれます。

ag

dumb-jump はタグジャンプしくれますが、もっと単純に grep みたいに 検索して欲しいことがあります。つまり M-x grep の代わりです。

その名も ag というパッケージがあります。

  1. M-x package-list-packages で ag を探してインストールします。
  2. ~/.emacs に以下のように設定して Emacs を再起動します。
(eval-after-load 'ag
  (lambda ()
    (setq ag-arguments '("--nobreak" "--smart-case" "--stats"))
    (fset 'masm11:ag (symbol-function 'ag))
    (fset 'masm11:ag-project (symbol-function 'ag-project))
    (defun ag (arg)
      (interactive "P")
      (if arg
          (masm11:ag (ag/read-from-minibuffer "Search string")
                     (read-directory-name "Directory: "))
        (masm11:ag-project (ag/read-from-minibuffer "Search string"))))
    (define-key ag-mode-map "n" 'next-error-no-select)
    (define-key ag-mode-map "p" 'previous-error-no-select)
    ))

本来 M-x ag が文字列とディレクトリを指定するバージョンで、 M-x ag-project が文字列だけを指定するバージョンです。 M-x ag-project の方はプロジェクト配下のディレクトリ全てにわたって 検索してくれます。

が、どちらかというと、M-x ag-project の方をよく使うと思うので、 そちらを M-x ag として使うようにしました。元々の M-x agC-u M-x ag で使えるようにしてあります。

そして、結果を表示しているバッファでは、n, p で次のヒットと前のヒットを 移動できますが、M-x grep のように該当ファイルの該当行を表示しては くれません。Enter を押せば表示してくれるのですが。そこで、keymap をいじって M-x grep と同様の動作になるようにしてみました。

あと、ag-arguments については、元々は、マッチを含むファイルごとに 空行が挿入されているのですが、見づらいので入れないようにしました。 元々しっかり色が付いているので、空行がなくても全然問題はありません。

おまけ

ag の出力は上記の通り、デフォルトではツールに食わせるのには向いていません。 しかし、実はそれは端末に出力する場合のみで、パイプに出力させると、 以下のようにツール向きの出力になります。

luna:emacs % ag pgtk_menu_show | cat
src/pgtkterm.indent:4793:  terminal->menu_show_hook = pgtk_menu_show;
src/pgtkterm.c:4650:  terminal->menu_show_hook = pgtk_menu_show;
src/menu.h:63:extern Lisp_Object pgtk_menu_show (struct frame *, int, int, int,
src/pgtkmenu.c:619:pgtk_menu_show (struct frame *f, int x, int y, int menuflags,
luna:emacs % 

また、ag は .gitignore を参照します。.gitignore に書かれているファイルは検索対象外です。 .gitignore に書かれているファイルも対象にするには、-t などを指定するのが良いでしょう。

(いまさらですが、実は先述のスピード差は、.gitignore に書いてあるファイルを対象外にしてるから、というのが最も大きいです)

まとめ

ag はとても良さげなものだと思っています。 ただ、ivy, posframe の方がまだまだ粗削りで、使いにくいです。 そのあたりも含めて、今後 ag に慣れていきたいな、と思います。

ではまた!!

tar で固める際に進捗を知る

こんにちは。masm11 です。

大きなディレクトリを tar で .tar.gz に固めることってありますか? 時々ありますよね。 最近は zip の方が多いのでしょうか? 私は tar を使っています。例えば以下のようにします。

tar zcf /bak/backup.tar.gz dir

しかし、大きなディレクトリを .tar.gz にすると時間がかかるので、進捗を知りたくなります。

今回はその方法を紹介したいと思います。

まずは普通に

tar に v を付けると、ファイル名を出力してくれます。

tar zcvf /bak/backup.tar.gz dir

ですが、この場合、ファイル名とエラーメッセージが両方端末に出力されるので、 エラーの確認が難しいです。

ファイル名を別ファイルに保存する

tar zcvf /bak/backup.tar.gz dir > log

こうすると、 log にファイル名が出力されていきます。エラーメッセージは端末に出力されます。

log は別端末で

tail -f log

とでもしておけば、確認できますね。

別端末に送り込む

ファイルに出力するのが嫌な場合、別端末に直接送り込むこともできます。

端末を2枚開いて、それぞれで

tty

というコマンドを実行します。端末の ID のようなものが出力されます。 ここでは、/dev/pts/0, /dev/pts/1 だとします。

/dev/pts/0

tar zcvf /bak/backup.tar.gz dir > /dev/pts/1

と実行すると、/dev/pts/1 の方にファイル名が出力されます。エラーメッセージは /dev/pts/0 の方に出力されます。

各ファイルのサイズも見たい場合

例えば

tar ztvf /bak/backup.tar.gz

を実行すると、backup.tar.gz の中にあるファイルの、ファイル名やサイズ等が出力されます。

固めている間に見たければ、別端末でこのコマンドを何度も実行すれば、そのたびにファイル名やサイズ等が出力されますが、 backup.tar.gz は大きいので、一度見た前の方の情報は不要です。できれば、tar zcvf の時のように順次出力して欲しいです。

そこで、tail -f を使う方法を考えました。

/dev/pts/1:

echo -n '' > /bak/backup.tar.gz
tail -f /bak/backup.tar.gz | tar ztvf -

/dev/pts/0:

tar zcf /bak/backup.tar.gz dir

tar zcf で backup.tar.gz を作っていきます。backup.tar.gz が大きくなったら大きくなった部分だけ tail -f で読み取り、 tar ztvf でファイル名やサイズ等を出力しています。

ただ注意として、既に大きくなった backup.tar.gz がある場合、tail -f は後ろの方しか出力してくれないので、zcat が 「ファイル形式がおかしい」とエラーを吐きます。なので、tail -f は先に実行しておく必要があるのですが、 ファイルがなければ tail -f がエラーになりますので、最初に echo -n で空ファイルを作っています。 touch でもファイルを作れますが、もしファイルがあった場合、タイムスタンプが変わるだけになってしまうので、 明示的に空ファイルにするために echo -n にしています。

結果は以下のような感じになります。

f:id:masm11:20200715123320p:plain
実行中画面サンプル

まとめ

tar zcf で大きなディレクトリを固めている際に進捗を知る方法を紹介しました。

いろいろな方法がありますね。UNIX/Linux ではいろいろな方法があって、好みの方法を選べるところが私は好きです。

ではまた!

dovecot が起動してない?

https://upload.wikimedia.org/wikipedia/commons/3/37/Dovecot-logo.png

どうも id:kizashi1122 です。

弊社が開発・運営している Re:lation というサービスの中のサーバではないのですが、とある用途で IMAP のサーバをAWSのEC2上に立てていました。ソフトウェアとしては dovecot を使っています。

Dovecot

さて、社内の利用者より「つながらないんですけど」と連絡がありました。

んなアホなと思い、みてみると確かに IMAP の接続に失敗する。認証に失敗ではなく接続に失敗する。

$ ps -ef | grep dovecot

をすると、プロセスがあがってません。 ps auxw を使ってない理由は特にないありません。昔 Solaris を使っていた手癖がまだ残ってるだけです・・・。

それはさておき。

上がってないなら上げればよいということで起動させようとしました。

[root@ip-172-31-XX-XX conf.d]# /etc/init.d/dovecot start
Starting Dovecot Imap: Error: service(pop3-login): listen(*, 995) failed: Address already in use
Error: service(imap-login): listen(*, 143) failed: Address already in use
Error: service(imap-login): listen(*, 993) failed: Address already in use
Fatal: Failed to start listeners
                                                           [FAILED]
[root@ip-172-31-XX-XX conf.d]#

誰かがポートを使ってる? んなアホな。

lsof で確かめます。

[root@ip-172-31-XX-XX conf.d]# /usr/sbin/lsof -i :993
[root@ip-172-31-XX-XX conf.d]# /usr/sbin/lsof -i :995
[root@ip-172-31-XX-XX conf.d]# /usr/sbin/lsof -i :143

ないんかい。

念の為、 netstat -tulpn でもみてみますが同ポートは誰もLISTENしてません。

困った。

困ったときの Google 先生。 以下のサイトがみつかりました。日付も近い。

stackoverflow.com

Hi it looks like you are using AWS as I am. I recently updated via Yum as well. I noticed that a new package named 'portreserve' was also installed. I killed that process, left the /etc/dovecot/dovecot.conf as it was before and then started Dovecot successfully. I was also immediately able to reconnect my mail clients connection. I hope that helps you.

I also restarted the portreserve program since it seems useful to limit port access.

まとめると、「最近 yum でアップデートしたら portreserve なるパッケージがインストールされてた。このプロセスを kill して dovecot を起動したらうまくいった」とのこと。

確かに、最近 yum でセキュリティアップデートがあったので yum update -y したばかりでした。その後再起動して放置してました。まさか dovecot が立ち上がってないとは思わず・・・。

実際に、このとおりにすると無事 dovecot が再起動しました。

ただ次にOSを再起動した際に同じことが起こるのはイヤです。

/etc/portreserve/ というディレクトリに dovecot というファイルがあります。

中身は

imap/tcp
imaps/tcp
pop3/tcp
pop3s/tcp
sieve/tcp

というような感じ。もうこいつが原因だろうということでこのファイルは削除しました。

ところで、この portreserve というサービスは何者なんでしょう? その名の通り、dovecot のために上記ポートを予約しておいてくれたのなら、なんで dovecot が立ち上がらなかったんだ?

謎が多いです。

今後は簡単に確認できるように、簡単な Perl スクリプトを作りました。

#!/usr/bin/env perl

use v5.16;
use Mail::IMAPClient;

my $imap_host = $ARGV[0] or die $!;
my $user      = $ARGV[1] or die $!;
my $pass      = $ARGV[2] or die $!;

my $imap = Mail::IMAPClient->new(
    Server   => $imap_host,
    User     => $user,
    Password => $pass,
    Ssl      => 0,
    Uid      => 1,
);

if ($imap) {
    say "AUTH OK";
} else {
    say "AUTH NG";
}

割り算の余りは?

こんにちは、masm11 です。

突然ですが、今回は、負の整数を正の整数で割った時の余りは 0 以上なのか 0 以下なのか、 という話を書きたいと思います。

具体的には、-22 % 3 はいくつなのか、というお話です。

実は、言語によって違うのです…

2 になるものたち

Ruby

Ruby は余りが 2 になります。

仕様は以下のページにあります。

https://docs.ruby-lang.org/ja/latest/method/Numeric/i/modulo.html

また、以下のページを見ると、Ruby 1.8.7 の時には既に明確に定義されていたようですね。

https://docs.ruby-lang.org/ja/1.8.7/class/Numeric.html#I_MODULO

実際に試してみました。

luna:~ % irb
irb(main):001:0> -22 % 3
=> 2
irb(main):002:0> 

Python

Python 3 は、以下の表の下の注釈 1. に記載がありました。

https://docs.python.org/ja/3/library/stdtypes.html#typesnumeric

結果(商)が負の無限大の方向に丸められるということは、余りは 0 以上です。

Python 2.7 でも、以下のページの表の下の注釈 1. に同様の記載がありました。

https://docs.python.org/ja/2.7/library/stdtypes.html#numeric-types-int-float-long-complex

Ruby と同様に実際に試してみました。

luna:~ % python
Python 3.8.2 (default, Apr  8 2020, 14:31:25) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> -22 % 3
2
>>>

-1 になるものたち

Java

Java では余りが -1 になります。

以下のページの floorDiv メソッドに説明がありました。

https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Math.html#floorDiv-int-int-

/ で普通に割り算すると、0 に向かって丸められるようです。 ということは余りは -1 以下ですね。

そして floorDiv メソッドを使った場合は Ruby や Python と同じ丸め方になるようです。従って floorMod メソッドを使うと余りは 2 ですね。 更に、このメソッドには「導入されたバージョン: 1.8」と記載があり、Java 8 以降にしかないようです。

Java 7 については、以下に記載がありました。

https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.17.2

Integer division rounds toward 0.

やはり 0 に向かって丸めていたようです。

実際に試してみました。

luna:~ % java -version
openjdk version "1.8.0_242"
OpenJDK Runtime Environment (build 1.8.0_242-b08)
OpenJDK 64-Bit Server VM (build 25.242-b08, mixed mode)
luna:~ % cat test.java
class test {
    public static void main(String[] args) {
        System.out.println(-22 % 3);
        System.out.println(Math.floorMod(-22, 3));
    }
}
luna:~ % javac test.java       
luna:~ % java -classpath . test
-1
2
luna:~ % 

C99

以下に記載がありました。

https://kikakurui.com/x3/X3010-2003-01.html の 6.5.5

整数同士の除算の場合,/演算子の結果は,代数的な商から小数部を切り捨てた値とする(87)。

(87) には以下のように書いてあります。

これは, 0 方向への切捨てとも呼ぶ。

試してみました。

luna:~ % cat test.c
#include <stdio.h>

int main(void)
{
    printf("%d\n", -22 % 3);
    return 0;
}
luna:~ % cc test.c
luna:~ % ./a.out 
-1
luna:~ % 

処理系定義のもの

C89

C99 登場以前の C は C89 と呼ばれる規格に従っていました。 C89 の規格は簡単には手に入らなさそうなので、stackoverflow.com で見つけた情報 をご紹介します。

https://ja.stackoverflow.com/questions/12649/

C89 では処理系定義だったようです。つまり、結果がコンパイラや CPU に依存していた、ということです。

そんな処理系を持っていないので、残念ながら実際に試すことはできませんでした。

まとめ

C89 のコンパイラを使っていた時、ふと、余りが負であることに気付き、 私は衝撃を受けました。 それからずーっと気になっていて、今回ようやく各言語の状況をまとめることが できました。

昔はコスト的に 0 以下にすることを選んだ CPU が多く、C ではそれがそのまま 結果として現れているようです。しかし現在は CPU の処理能力も向上し、 少しくらい処理が増えても問題ないため、現代的な言語では 0 以上になっている のでしょう。

Java について言うと、私は Java は CPU 能力が潤沢にある環境で使うものだと 思っているので、そういう意味では意外な結果でした。

ではまた!

Gmail API(Google OAuth) 利用承認取得記 - 完結編

こんにちは。Tedです。

いよいよ今回でGmail API利用にかかるGoogleからの承認の取得記のラストです。

これまでの記事はこちら。

blog.ingage.jp

blog.ingage.jp

前回までで、Googleでの審査は一通り完了しました。いよいよセキュリティ評価です。

セキュリティ診断企業選び

https://support.google.com/cloud/answer/9110914 にあるように、この承認のためには第三者機関によるセキュリティ評価が必要(the system must undergo an independent (3P) security assessment)となります。また、この企業はどこでもよいわけではありません。Googleが指定する3社(すべて米国企業)から選ぶ必要があります。そしてその費用は、1万5千ドル〜7万5千ドル(約160万円~800万円)またはそれ以上かかります。

私たちの場合、このセキュリティ評価に入る時点で2019年の12月に入っていました。Google提示の3社の内、1社は多忙で年内は受け付け終了とありました。できれば年内に終わらせたかった*1ので、残りの2社から見積もりを取得することにしました。見積もりにあたり、Re:lationシステムの概要など必要な情報を提供します。これらのやりとりもすべて英語で行います。

2社からの回答は内容はほぼ同じ(当たり前)ながら、最初に出された費用は100万円ほど差がありました。ですが話を進めていく上でほとんど同じような額となりました。その見積額は約400万円でした。

両社ともしっかりした会社だったので、選ぶのには苦労しました。最終的には、回答が早く、納期も短かったB社に決めました。(とはいえ、ホリデーシーズンのためか結果的にテスト開始・終了は大幅にずれましたが)

セキュリティ評価で感じたこと

セキュリティ評価は下記のように進められました。

  1. キックオフミーティング(Web会議)

  2. B社によるセキュリティ評価

  3. 評価結果を元にレポートとその説明(Web会議)

  4. 指摘事項の修正とその内容の連絡

  5. B社での修正内容の確認

  6. 最終診断結果のレポートとその説明(Web会議)

B社から上がってきた診断レポートには、いくつか修正すべき点も指摘されていました。このレポートには私たちは大変満足しています。中には、Re:lationの仕様を十分に理解していないと指摘できないような内容も含まれていたためです。また、これまでの他社による診断では発見されなかったことも指摘に上がっていました。

はれてGoogleからの承認を得る

B社からの最終レポートをGoogleに送信すると、その翌日にはGoogleからの承認が下りました。すでに時は2020年2月も後半になっていました。Googleに最初に申請をしてから、実に4ヶ月以上・Googleとのやりとりは122回かかりました。

あらためて、今回の承認取得のポイントを下記に記したいと思います。

  • Google Cloud Consoleでの申請内容は別途保存しておきましょう。審査期間は限られており、期限を過ぎると再申請が必要となります。そのためもし再申請が必要となった際に重宝します。*2

  • 英語力は必要です。

  • 提出資料作成のため、英作文能力も必要です。

  • セキュリティ診断企業とのWeb会議のために英会話力も必要です。

  • セキュリティ診断にはまとまった費用が必要ですし、Googleが指定する企業を使う必要があります。予算の確保を前もってしておくとよいかと思います。

  • 基本的にGoogleは1-2日後には返事をくれます。

  • ただしそうでないときもあります。そんなときはフォローメールを送りましょう。*3

  • GoogleはWeb会議には応じません。コミュニケーションは必ずメールに限られます。英文での表現力・説明力はある程度必要になります。

122回のやりとりの中には、ここでは書き切れないほどのイベントが発生したのですが、ざっとこのようにまとめてみました。元々はこの3倍はあったボリュームをここまでまとめてくれたウチのCTOに感謝です。

もしこれがこれからGoogle OAuthを取得しようと思っている企業に有益となれば幸いです。質問があれば気軽にどうぞ。コメントやツイートなどwelcomeです。

*1:私たちの場合、このGoogleへの申請が12月31日で期限切れとなる予定でした。

*2:実際、私たちは期限切れで再申請する必要がありました。

*3:2-3度ほどGoogleからの返信が遅れたことがありましたが、フォローメールを送るとその翌日には返信がありました。