RHEL7でスナップショットとり放題設定をためしたメモ

インストール

RHEL7のインストーラではインストール対象としてLVM thinprovisioningが選択できるようになっている。ここを選択して、適当に空きが残るように容量を設定しておく。

状態を確認

操作する前に現在の状態を確認

[root@localhost log]# lsblk
NAME                    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda                       8:0    0   20G  0 disk 
├─sda1                    8:1    0  500M  0 part /boot
└─sda2                    8:2    0 16.4G  0 part 
  ├─rhel-swap           253:0    0    2G  0 lvm  [SWAP]
  ├─rhel-pool00_tmeta   253:1    0   12M  0 lvm  
  │ └─rhel-pool00-tpool 253:3    0   12G  0 lvm  
  │   ├─rhel-root       253:4    0   12G  0 lvm  /
  │   └─rhel-pool00     253:5    0   12G  0 lvm  
  └─rhel-pool00_tdata   253:2    0   12G  0 lvm  
    └─rhel-pool00-tpool 253:3    0   12G  0 lvm  
      ├─rhel-root       253:4    0   12G  0 lvm  /
      └─rhel-pool00     253:5    0   12G  0 lvm  
sr0                      11:0    1  3.6G  0 rom  

[root@localhost log]# lvs
  LV     VG   Attr       LSize  Pool   Origin Data%  Meta%  Move Log Cpy%Sync Convert
  pool00 rhel twi-aotz-- 12.00g               9.38   5.18                            
  root   rhel Vwi-aotz-- 12.00g pool00        9.38                                   
  swap   rhel -wi-ao----  2.00g
lsblkに同じエントリが2回でているのが不安ですが、tmetaの方はメタデータ、tdataの方は実際のデータがはいっています。
12GBのpool00の下に12GBということになっているrootが含まれている状態。
プールの詳細
[root@localhost log]# lvdisplay /dev/rhel/pool00 
  --- Logical volume ---
  LV Name                pool00
  VG Name                rhel
  LV UUID                vuooGK-VHow-JfAo-cPQb-Egf1-fCk4-qRpLqK
  LV Write Access        read/write
  LV Creation host, time localhost, 2015-05-15 19:15:03 +0900
  LV Pool metadata       pool00_tmeta
  LV Pool data           pool00_tdata
  LV Status              available
  # open                 2
  LV Size                12.00 GiB
  Allocated pool data    9.38%
  Allocated metadata     5.18%
  Current LE             3072
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     8192
  Block device           253:3
これで、lvmにより任意のタイミングでsnapshotをとり放題です。以下のようなコマンドでスナップショットを作成できます。

lvcreate --permission r --snapshot --name snapshot  rhel/root

snapper設定の作成

さて、snapshot作り放題、なのはいいですがlvcreateで都度作成するのはしんどいです。というわけでRHEL7には、SUSE出身のsnapperが含まれています。SUSEだとYaSTと連携してGUIまでありますが、RHELだとコマンドラインのみ。しかしとても便利です。
まずは設定を作成します。デフォルトだと “root” という名前の設定で、それ以外だと-c “設定名” みたいなオプションをつけて実行することでどこの操作をしているか指定します。
[root@localhost ~]# snapper create-config -f 'lvm(xfs)' /
このコマンドで /etc/snapper/configs/rootに設定本体が作成され、
さらに設定一覧が /etc/sysconfig/snapper にあるのでここに設定名が追加されます。
今の設定一覧を確認する。”root”設定があるのがわかります。
[root@localhost log]# snapper list-configs
Config | Subvolume
-------+----------
root   | /

snapshot作成

ここまで準備をするとスナップショットの作成は簡単。snapper createコマンドだけです。

[root@localhost log]# snapper create
様子を確認します。

[root@localhost log]# snapper list
Type   | # | Pre # | Date                     | User | Cleanup | Description | Userdata
-------+---+-------+--------------------------+------+---------+-------------+---------
single | 0 |       |                          | root |         | current     |         
single | 9 |       | Mon May 18 18:15:10 2015 | root |         |             |         

[root@localhost log]# lvs
  LV             VG   Attr       LSize  Pool   Origin Data%  Meta%  Move Log Cpy%Sync Convert
  pool00         rhel twi-aotz-- 12.00g               10.00  5.73                            
  root           rhel Vwi-aotz-- 12.00g pool00        10.00                                  
  root-snapshot9 rhel Vri---tz-k 12.00g pool00 root                                          
  swap           rhel -wi-ao----  2.00g
root-snapshot9 という名前のスナップショットが作成されています。

スナップショットのマウント

以下のようにスナップショット番号を指定して、readonlyでmountすることができます。
.snapshotディレクトリ以下に適当にmountされます。

[root@localhost ~]# snapper mount 10 

[root@localhost ~]# df

Filesystem                        1K-blocks    Used Available Use% Mounted on
/dev/mapper/rhel-root              12571648 1226516  11345132  10% /
devtmpfs                            1931348       0   1931348   0% /dev
tmpfs                               1941204       0   1941204   0% /dev/shm
tmpfs                               1941204    8580   1932624   1% /run
tmpfs                               1941204       0   1941204   0% /sys/fs/cgroup
/dev/sda1                            508588  168516    340072  34% /boot
/dev/mapper/rhel-root--snapshot10  12571648 1193528  11378120  10% /.snapshots/10/snapshot

snapshot間の差分

snapperではsnapshotを一時的なディレクトリにmountして、差分を取る仕組みがあります。
さらに通常のスナップショットの他に、管理に便利なようにpreとpostというタイプが導入されています。スナップショット自体は同じなのですが、なんらかの作業前にpreスナップショットを取得、作業後にpostスナップショットを取得してペアにしています。
今回は触れませんが、pre,postのスナップショットを各種の作業前後に自動的に作成して、特に差分がなければ自動的に消す仕組みも含まれています。
preスナップショット作成

[root@localhost ~]# snapper create -t pre
適当にファイル変更

[root@localhost ~]# ls
anaconda-ks.cfg  hoge  lvmdump-localhost.localdomain-20150515103428.tgz
[root@localhost ~]# rm hoge 
rm: remove regular empty file ‘hoge’? y
postスナップショット作成

[root@localhost ~]# snapper list
Type   | #  | Pre # | Date                     | User | Cleanup | Description | Userdata
-------+----+-------+--------------------------+------+---------+-------------+---------
single | 0  |       |                          | root |         | current     |         
single | 9  |       | Mon May 18 18:15:10 2015 | root |         |             |         
pre    | 10 |       | Mon May 18 18:18:32 2015 | root |         |             |         
[root@localhost ~]# snapper create -t post --pre-number=10
[root@localhost ~]# snapper list
Type   | #  | Pre # | Date                     | User | Cleanup | Description | Userdata
-------+----+-------+--------------------------+------+---------+-------------+---------
single | 0  |       |                          | root |         | current     |         
single | 9  |       | Mon May 18 18:15:10 2015 | root |         |             |         
pre    | 10 |       | Mon May 18 18:18:32 2015 | root |         |             |         
post   | 11 | 10    | Mon May 18 18:19:42 2015 | root |         |             |
差分の確認

[root@localhost ~]# snapper status 10..11
-..... /root/hoge
c..... /var/log/messages
c..... /var/log/snapper.log

差分対象の制限(フィルタ)

hogeファイルが消えているのと、/var/log以下のログが増えていることがわかります。
ログの差分をとってもあまり嬉しくないので、/etc/snapper/filters/ に、log.txtという名前で以下のようなファイルを作成します。このパターンにマッチするものは差分チェックの対象になりません。
差分対象の制限

/var/log/*
差分の確認ふたたび

[root@localhost ~]# snapper status 10..11
-..... /root/hoge

undo

差分がとれるので、patchコマンドの要領でundoができます。
ただこのundoをした時に一貫性があるのか、もろもろ大丈夫なのかなどは特に保証されていませんので注意が必要です。

[root@localhost ~]# snapper undochange 10..11 
create:1 modify:0 delete:0

TODO

snapperにはこの他にも定期的にsnapshotをとっておいて、過去数回分を残しておくなどかなり気のきいた機能があります。 裏で古いスナップショットを自動で消したり差分をしらべたりするので勝手に動くのが嫌なひとはオプションしらべてね(丸投げ)!

freeの出力が大幅改善された話

最近freeコマンドを叩くとこんな感じで出力されます。

$ free

total used free shared buff/cache available Mem: 20209620 3859396 6594188 502492 9756036 15323144 Swap: 32767996 0 32767996


availableってなんでしょう? そして -/+ buffers/cache の行がなくなっています。

その背景をちらっと紹介します。

最近linuxの3.14で/proc/meminfo に MemAvailable というフィールドが追加されました。RHEL7.0や6.6にもバックポートされています。(RHEL6.6では互換性に配慮してデフォルトではdisableされています)
http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773

    /proc/meminfo: provide estimated available memory

    Many load balancing and workload placing programs check /proc/meminfo to
    estimate how much free memory is available.  They generally do this by
    adding up "free" and "cached", which was fine ten years ago, but is
    pretty much guaranteed to be wrong today.

    It is wrong because Cached includes memory that is not freeable as page
    cache, for example shared memory segments, tmpfs, and ramfs, and it does
    not include reclaimable slab memory, which can take up a large fraction
    of system memory on mostly idle systems with lots of files.

    Currently, the amount of memory that is available for a new workload,
    without pushing the system into swap, can be estimated from MemFree,
    Active(file), Inactive(file), and SReclaimable, as well as the "low"
    watermarks from /proc/zoneinfo.

    However, this may change in the future, and user space really should not
    be expected to know kernel internals to come up with an estimate for the
    amount of free memory.

    It is more convenient to provide such an estimate in /proc/meminfo.  If
    things change in the future, we only have to change it in one place.

    Signed-off-by: Rik van Riel 
    Reported-by: Erik Mouw 
    Acked-by: Johannes Weiner 
    Signed-off-by: Andrew Morton 
    Signed-off-by: Linus Torvalds 

このフィールドは「だいたいこのくらいはswap発生させずにアプリケーションがallcoateできそう」というメモリ量の推定です。
procps-ngに含まれるfreeコマンドがbuffer や cacheとして使われているメモリをfreeに足して表示する機能があった(過去形)ことも手伝って、これらを単純に全て「カーネルがパフォーマンス改善のために未使用メモリをうまいこと使っている」と見做してシステム管理している人が多くいます。しかしこの見方は正しくありません。buffer や cacheは非常に重要で、もしこれらが0になってしまえばシステムは全く稼動できないでしょう。どれくらい存在すれば十分なパフォーマンスがでるかも、アプリケーションの特性により非常に大きくかわります。

そこでもうちょっとましな推定値をつくろうということで導入されたのがMemAvailableです。現在の実装ではかならずしもカーネルの中でこの推定をする必要はないのですが、将来的にカーネルのメモリ管理のしくみが変わっても同じように利用できるよう追加されました。


さて、これにともなってprocps-ngも3.3.10で更新されています。このprocps-ng 3.3.10はRHEL7.1に含まれています。

availableフィールドを追加  https://gitorious.org/procps/procps/commit/ba6396f886f1a9911221e1c7c4b66dc75acb6948

MemAvailableがなくても同等の計算を他のフィールドからするfallback https://gitorious.org/procps/procps/commit/b779855cf15d68f9038ff1809db18c0788e9ae70

MemAvailableはあくまでざっくりした推定値なので、既存の統計情報を足したり引いたり適当な割り算をしたりして出しています。なので、その元になっているフィールドが提供されているlinux 2.6.27以降であれば同じような計算をしてavailableを出す機能です。 

-----------------------------------------------------------
  /* zero? might need fallback for 2.6.27 <= kernel 
  if (!kb_main_available) {
    if (linux_version_code < LINUX_VERSION(2, 6, 27))
      kb_main_available = kb_main_free;
    else {
      FILE_TO_BUF(VM_MIN_FREE_FILE, vm_min_free_fd);
      kb_min_free = (unsigned long) strtoull(buf,&tail,10);

      watermark_low = kb_min_free * 5 / 4; /* should be equal to sum of all 'low' fields in /proc/zoneinfo */

      mem_available = (signed long)kb_main_free - watermark_low
      + kb_inactive_file + kb_active_file - MIN((kb_inactive_file + kb_active_file) / 2, watermark_low)
      + kb_slab_reclaimable - MIN(kb_slab_reclaimable / 2, watermark_low);

      if (mem_available < 0) mem_available = 0;
      kb_main_available = (unsigned long)mem_available;
    }
  }
-----------------------------------------------------------

もはや不要となった free: remove -/+ buffers/cacheの削除  https://gitorious.org/procps/vnwildman-procps/commit/f47001c9e91a1e9b12db4497051a212cf49a87b1

そういうわけで、freeコマンドの出力が劇的に改善されたのでした。

IPアドレスのvalid lifetime

ip addr とかで 

3: em1: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether d4:3d:7e:dc:c8:ad brd ff:ff:ff:ff:ff:ff
    inet 10.64.193.115/23 brd 10.64.193.255 scope global dynamic em1
       valid_lft 82874sec preferred_lft 82874sec
    inet6 fe80::d63d:7eff:fedc:c8ad/64 scope link 
       valid_lft forever preferred_lft forever

みたいになっているとき、em1のアドレスに有効期限がついている。
valid_lft 82874secが有効期限の設定。
この期限が切れた時には、kworkerがアドレスを切り離す。

これを設定しているのは /usr/sbin/dhclient-script で、以下みたいなコードで設定している。

ip -4 addr add ${new_ip_address}/${new_prefix} broadcast ${new_broadcast_address} dev ${interface} \valid_lft ${new_dhcp_lease_time} preferred_lft ${new_dhcp_lease_time} >/dev/null 2>&1

 

systemd-udevdによるNIC命名とbiosdevnameがまざって混乱する件

最近のsystemd-udevdはlinuxのネットワークインタフェース名を設定します。それにともない特にRHEL界隈でNIC命名規則が複雑になってきたので紹介します。。

問題: linuxカーネルNICの名前をドライバ初期化順につけるので名前とNICの対応が安定しない。再起動毎に順序がかわる可能性もある。

これに対して過去から最近までいろいろな手法で対策がとられています。

ユーザが明示的に指定する

MAC アドレスなどの識別子とインタフェース名を設定ファイルで明示して指定する。こんなかんじ。

DEVICE=eth1
HWADDR=D4:85:64:01:46:9E
ONBOOT=yes

Predictable Network Interface Name

くわしくはsystemd本家のwikiにみっちり書いてますが、ざっくりまとめるとハードウェアの物理的な位置やバス上のアドレスを根拠にした、安定した名前をつけます。

命名規則: デバイスの種類(Ethernetならen, wirelesslanならwl) + ファームウェアでの場所情報(スロット番号またはPCIバスの番号) [+VF番号など]
例: ens0

Consistent Device Naming by biosdevname

安定した名前づけの仕組みとしては、biosdevnameという別の実装があります。これは最近のFedoraや、RHEL6, 7でDell製ハードウェアの場合に利用されます。
http://linux.dell.com/biosdevname/
SMBIOSというファームウェアの規格で「オンボードの1番」や「拡張カードスロットの2番」のような場所情報を提供し、これを元にNICの名前を作ります。
例: em1

Persistent Net

これらの物理的な位置を利用する命名方法が登場する前から利用されているpersistent-netとよばれる仕組みがあります。
これは一旦linuxカーネルに適当な名前づけをされたあとに(これが物理的な配置とは無関係になっていることには目をつぶり)、次回の起動からは同じNICに同じ名前をつけるようにするため、MACアドレスとインタフェース名の対応づけをudevのルールとして永続化する仕組みです。
この対応づけは/etc/udev/rules.d/70-persistent-net.rules に保存されます。

RHEL7や最近のFedoraでpersistent-netのルール作成はされませんがRHEL6までで作成されたものをアップグレードした場合はひきつづき有効です。

こんがらがるので以下にまとめます。優先順位自体は最近のFedoraのものと同じですが、デフォルトでの有効無効が環境によってバラバラです。

最近のFedora:  ユーザの指定 > persistent-net > biosdevname > systemd > 古典的な名前づけ
RHEL6 on Dell: ユーザの指定 > persistent-net > biosdevname > 古典的な名前づけ
RHEL6 on not Dell: ユーザの指定 > persistent-net > 古典的な名前づけ
RHEL7 on Dell: ユーザの指定 > persistent-net > biosdevname > systemd > 古典的な名前づけ
RHEL7 on not Dell: ユーザの指定 > persistent-net > systemd > 古典的な名前づけ

NIC名前例:

systemd: eno1(ethernetオンボード1番)
biosdevname: em1 (ethernetオンボード1番)
古典的な名前づけ: eth0 (最初にeth%d って名前で命名されたNIC)

各自動設定の止め方
systemdのNIC命名: カーネルコマンドラインに net.ifnames=0
biosdevname: カーネルコマンドラインに biosdevname=0
persistent-net: /etc/udev/rules.d/70-persistent-net.rulesから該当する行を消す

udevのルールの比較

 

f:id:mrwk:20190501070701p:plain


# 以下RHEL7betaをためした人むけ
RHEL7 public betaを確認するとベンダにかかわらず on Dellのようになっているのですが、RHEL7RCでは上記のように動作が修正されています

なぜ cdromグループに入っていないユーザーがCD, DVDを焼けるか、あるいははじめてのsystemd-logind

このあいだ「今度買うハードウェアはDVDマルチライタがついているんだけど、読み込みは許可しつつ焼けないように制限かけたい」って質問されたら、意外に奥が深かったので再現風にまとめ。
 

事前の予想

 
自分のデスクトップにLinuxを使いはじめてから15年くらいです。
昔CD-R焼けなくて対応したときの記憶をたどると、たしかこんなかんじだったんじゃないかなと:
  • /dev/cdrom あたりはrootユーザ、cdromグループになっている
  • 使えるユーザはcdromグループに入っているから使える 
これを踏まえて「デバイスファイルを作ったりcdromグループ設定しているのは今はudevだろうから、そのルールを確認してモードを660じゃなくて640にした上でユーザをcdromグループにすれば読み込みのみにできるかな」と予想しました。
 

確認と謎

 
自分のマシン(debian testingでinitはsystemdにしてます)でチェックするとこんなかんじ  
dragon:~$ ls -l /dev/cdrom
lrwxrwxrwx 1 root root 3 May  2 03:07 /dev/cdrom -> sr0
dragon:~$ ls -l /dev/sr0
brw-rw----+ 1 root cdrom 11, 0 May  2 03:07 /dev/sr0
dragon:~$ id
uid=1000(kankun) gid=1000(kankun) groups=1000(kankun),24(cdrom),25(floppy),29(audio),30(dip),44(video),
46(plugdev),105(scanner),109(bluetooth),111(netdev)
ああcdromグループに入ってるわ…… ということでちょっと安心しましたが、長期間使っている環境なので油断できません。Fedoraマシンでみると
[kankun@snake ~]$ id uid=1000(kankun) gid=1000(kankun) groups=1000(kankun),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
cdromグループは別にあるもののユーザはcdromグループに入っていません。RHEL5,6,7betaもご同様。しかしいずれの環境でも、普段からCD読み書きできています。気になります。
 
ファイルシステムとしてmountするのはudisksあたりが頑張ってくれてるはずだから、直接デバイス読んでみよう」 less -f /dev/sr0 すると CD/DVDの内容が読めました。
brw-rw----+ 1 root cdrom 11, 0 May  2 03:07 /dev/sr0
という状態のデバイスをrootでもcdromグループでもない一般ユーザがアクセスできるのです。
 
一瞬頭に「???」が浮かびましたがbrw-rw----+」の最後に+があります。確認するとユーザにアクセスを許可するACLが設定されていました。
dragon:~$ getfacl /dev/sr0
getfacl: Removing leading '/' from absolute path names
# file: dev/sr0
# owner: root
# group: cdrom
user::rw-
user:kankun:rw-
group::rw-
mask::rw-
other::---

誰がこのACLを設定しているの?(タイトルでネタバレ)

# ここからの調査は最初RHEL6でやったんですが、より実装がためになるのでFedora20で調べなおして再現してます。なのでちょいフィクションです。
 
このACLを設定したプログラムに心あたりがないわけです。自分で明示的に指定したこともありません。自分のマシンで自分の知らない権限設定がされているとかいやですね。しらべましょう。誰がこんな設定してるんでしょうか。ちょっと予想します。
 
  • 今のログインユーザだから(pam?)
  • インストーラで作成したアカウントだから何か特別に設定が書かれている(どこぞの設定ファイルがudevのルールから参照されてる?)
  • GNOMEデスクトップユーザむけのなにかが動いている(udisksとか??)
とりあえずgnomeうんぬんがでてくると登場人物が多すぎて大変なので、状態をクリアするために再起動してXを起動せずにコンソールからログインします。
やはりACLが設定されています。GNOMEは排除できました。
 
pamをうたがって別のvirtual terminal(VT)でrootでログインします。ACLの状態がかわりました。これでインストーラも排除できました。
getfacl: Removing leading '/' from absolute path names
# file: dev/sr0
# owner: root
# group: cdrom
user::rw-
group::rw-
mask::rw-
other::---
今ログインしてるユーザ全員に許可をだしているわけではなさそうです。一般ユーザのアカウントへの許可は消えてしまいました。最後にログインしたユーザーかな??
 
VTきりかえてkankunアカウントでもういっぺん確認します。
getfacl: Removing leading '/' from absolute path names
# file: dev/sr0
# owner: root
# group: cdrom
user::rw-
user:kankun:rw-
group::rw-
mask::rw-
other::---
やばい…… VT切り替えしかしてないのにACLが書き変わってる……。pamだけじゃこの動きはできません。予想が全滅してポルナレフごっことかやりたくなります。やっても話が進まないのでぐっと堪えます。
 
ls -l /dev して同じように+がついているものをみると kvm, rfkill, video0 でも同様のACLが設定されていました。今デスクトップを目の前でつかっているアカウントがデスクトップ用途で使いやすいようにACLを変更する、という意図でやっていそうです。
 
こういうのたしかsystemdに含まれているlogindってひとが、ConsoleKitから引きついでました。
 
コンソール切り替えの情報とか監視してないかなー と、lsof -n |grep ttyとかしてみます。 
systemd-l 683 root 5r REG 0,15 4096 4386 /sys/devices/virtual/tty/tty0/active
あった!
 

systemd-logindによるACL設定

 
logindが何やってるか知りませんが、こいつかこいつ経由で情報もらった誰かがACLいじっているはず。logindはsystemdのリポジトリにはいってます。
 
systemdのリポジトリaclgrepします。。。./src/login/logind-acl.c ってのがいました。
 
logind-acl.hで定義されている関数は、 devnode_acl、devnode_acl_all で、デバイスファイルからold_uidとして指定されたuidへの許可ACLを削除して、new_uidとして指定されたuidへの許可ACLを設定します。
 
devnode_acl_all関数の中で以下のディレクトリを探索してACL設定する対象となるデバイスを決定していました。udevによるタグで絞り込まれています。
       dir = opendir("/run/udev/static_node-tags/uaccess");
このタグづけ対象デバイスは、logindのソースと同じディレクトリにある70-uaccess.rulesの中で定義されていて、たとえばCD/DVDについては以下のようなルールがありました。
# optical drives
SUBSYSTEM=="block", ENV{ID_CDROM}=="1", TAG+="uaccess"
SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", TAG+="uaccess"
これでさっきのディレクトリに登場しそうです。
$ ls /run/udev/tags/uaccess
b11:0  c10:232    c116:1    c116:10  c116:11  c116:12  c116:2  c116:3  c116:33  c116:4  c116:5  c116:6  c116:7  c116:8  c116:9  c189:257  c189:258    c189:259  c189:260  c189:385  c21:2  c226:0  c81:0
してました。b11:0 です。ブロックデバイスのmajor 11, minor 0ですね。
 
systemd-logindがACL設定をuaccessタグがついているデバイスにおこなっていそうなことはわかりましたが、どういう契機で行われるかという疑問が残っています。もうひといきです。
 

1. ログイン時のACL設定 

systemd-logindのソースと同じところにpam-module.cといういかにもあやしいのがいます。これをビルドするとpam_systemd.so というモジュールができてセッション作成時に systemd-logindへセッション作成依頼をdbus経由で投げつけます。
systemd-logindがこの依頼を受けてセッション作成し、必要に応じてACL設定の変更をおこなっています。
 

2. VTの状態によるACL設定

systemd-logind内での関数呼び出し元をおいかけると、以下のようになっていて、VTの状態を反映したACL設定をおこなっていました。VT切り替え監視そのものもsystemd-logindが見ていてmanager_dispatch_console() がハンドラになっています。
 
manager_dispatch_console()
-> seat_read_active_vt()
  -> seat_active_vt_changed() 
    -> seat_set_active() 
      -> seat_apply_acls() 
        -> devnode_acl_all() 
          -> devnode_acl()
 

3. udevdによるACL設定

さらにlogindのソースとおなじディレクトリに追いてある73-seat-late.rules.in をみると
TAG=="uaccess", ENV{MAJOR}!="", RUN{builtin}+="uaccess" 
という記載があって、uaccessタグがついているデバイスがつながるとudev組み込みのuaccessという処理が走るように設定されています。これは systemdのsrc/udev/udev-builtin-uaccess.cで定義されていて、既にseatとユーザがひもづいている状態でデバイスを接続した時に、そのデバイスについてdevnode_acl()関数を呼びだしてACL設定をおこなうものでした。
 
まとめると以下のようになっていて、ログインしたとき、画面切り替えした時、既にログイン済みでデバイスをUSBなどで追加したときにちゃんとACLが設定できるようになっていました。

はじめの疑問にもどる

そして結局一般ユーザにCD, DVDを焼けないように設定したいという当初の質問がどうなったかというと……
 
/lib/udev/rules.d/70-uaccess.rules を/etc/udev/rules.d にコピーしてCD用のエントリを消すと、uaccessタグが付与されなくなるのでまずは一般ユーザからCD, DVD焼けなくなる。 でも読む分にはudisks2がユーザ権限でmountしてくれるのでこれでOK。
 
この方法の問題点としては、70-uaccess.rules に将来の変更で対象となるデバイスが追加された場合にうまく反映されない点があるので手放しでおすすめはしづらいところ。アップデート時には注意が必要です。
 
systemd以前の世代だと類似のしくみがhal+ConsoleKitだったりudev+ConsoleKitで別途実装されているのでこの方法とちょっとかわります。「一般的にこのコマンド叩けばOK」という美しい方法はないのでした。なやましい……。

/proc/cpuinfoを目で読むのがつらい

最近のコンピュータだとCPUのコア数やスレッド数が多いので/proc/cpuinfoを直接読むよりまとめて出力してくれるlscpuやhwlocをつかったほうがよい。

最近コア数とか多いですね。デスクトップでもCore i7だと8スレッドとかありますしサーバだと4ソケット40コア80スレッドとかあります。

CPUがどういう構成なのか知りたい時は /proc/cpuinfo  や /sys/devices/system/cpu/* を読むことが多いですが表示が冗長なのでつらいです。そこで適当にまとめて表示してくれるコマンドが便利です。

lscpuはCPUのキャッシュ構成とか、ソケット-コア-スレッドの個数の関係がわかります。実機で数回実行するとわかりますがCPU MHzのところは実際の周波数にあわせてちょいちょい変わるのでアテになりません。
出力はこんなかんじ: 

$ lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                8
On-line CPU(s) list:   0-7
Thread(s) per core:    2
Core(s) per socket:    4
Socket(s):             1
NUMA node(s):          1
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 58
Stepping:              9
CPU MHz:               1802.000
BogoMIPS:              6784.54
Virtualization:        VT-x
L1d cache:             32K
L1i cache:             32K
L2 cache:              256K
L3 cache:              8192K
NUMA node0 CPU(s):     0-7

hwlocは本来はNUMA環境などをうまく使うために適切なtaskset等を生成したりするためのツールセットなのですが、今回は表示してくれるところだけ見ます。グラフィカルなのが好きな人はいいんじゃないですかね。
  $ hwloc-ls -p

f:id:mrwk:20190501082222p:plain

以下はしばらく前にとった4ソケットマシンでのhwloc-lsの出力。「でかい!」ってことしかわからない気もします。。

f:id:mrwk:20190501082339p:plain

 

プログラマ格言(2006)

2006年くらいに書いたプログラマ格言が発掘されたのでおいとく。おおむねダジャレです。

 * PHPを笑うものはPHPに泣く
   * 意味: 「PHPなんてまともなプログラミング言語じゃないよ」と笑っていたら仕事でPHPを触るはめになってしかも既存のソースが汚かったりして泣く。
   * 教訓: 好き嫌いを通せるようにえらくなれ。

 * ソースが知れる
   * 意味: 変な挙動をするソフトをさわっていると、動き方から間違ってるパターンと作った人のレベルがなんとなく透けて見える。
   * 教訓: どうやったらうまく動くか探すのも仕事のうちらしい。

 * ひいきのwiki倒し
   * 意味: 「wikiはすばらしいツールですよ!」 と、とにかくwikiを導入してメンテ不良のページを大量につくってしまう。
   * 教訓: 情報共有ツールは使う人のメンテナンス能力が一番のネック。

 * ライブラリからボタ餅
   * 意味: 延々ぐぐってみつからなかった情報がライブラリのソースであっさりみつかった。
   * 教訓: ライブラリのソースは貴重な情報源。

 * UMLの大木
   * 意味: やたら継承ばっかりしていてすごい大木になっているクラス図を見てげっぷがでる。
   * 教訓: 実装の再利用は依存関係の拡大に注意。

 * 要求仕様と秋の空
   * 意味: 要求仕様は秋の空のようにかわりやすい。
   * 教訓: わかってるつもりの人が一番の危険人物。

 * KnuthTeXの誤り
   * 意味: あのKnuth大先生ですらTeXで何個かバグを作り込んでしまったように完璧ではない。
   * 教訓: 我々はプログラムにバグを必ず作り込んでしまう。

 * とりつくcoreもない
   * 意味: 反応なくなったから再起動したよ、とか運用してる人に言われたけどログは正常処理しかでてないしcoreはないしどうにもしようがない。
   * 教訓: 何もないとこまるときは、backtrace吐いてくれるようにしとくだけでもだいぶしあわせ。

 * 臭いバグに蓋をする
   * 意味: バグっぽい挙動の原因を追及せずにとりあえずのworkaroundなパッチを書いてごまかす。
   * 教訓: 「発現しなければバグではない」が通用{する|しない}世界もある。

 * IDE屋のvi使い
   * 意味: 客にはIDEを売るけど自分はviでソースを書く。
   * 教訓: 使いやすそうなものと使いやすいものは別

 * コードは一日にして成らず
   * 意味: まともなコードは何年もかけて細かいノウハウや互換性対策などが積み重なっている。読んですぐ書けそうな気がしてもなかなかすぐには同等のものはできない。
   * 教訓: 「既存のコードを全部捨ててやりなおし」はけっこう大変。

 * 大山鳴動して虫が一匹
   * 意味: 大騒ぎになる酷い障害も、原因はくだらないバグ1個だけだったりする。
   * 教訓: 騒がなくていいからまず原因を調査。

 * コピペも山のにぎわい
   * 意味: 行数で単価が決まる案件では長いだけで無意味なコピペコードが良いということになってしまう。
   * 教訓: 行単価という評価基準はヤバスギ。

 * デザパタ読みのデザパタ知らず
   * 意味: GoF本を読んだひとが「デザインパターン使うぜ!」と、設計に無理矢理デザインパターンを盛り込んで変な設計になってしまう。
   * 教訓: 自然な適用をするためにはそれなりの修行が必要。

 * 犬も歩けばバグに当る
   * 意味: あんまり期待せずにウォークスルーをすると意外にバグがどんどんみつかって効率よかったりする。
   * 教訓: 時には無心にコードを読むのもアリ。

 * 一寸のバグにも五分の魂
   * 意味: ちょっとしたバグにもそれを作り込んだプログラマの勘違いや仕様の誤解など、幅広く影響しそうな背景がある。
   * 教訓: 「なんで?」を5回繰り返そう。

 * デバガとハサミは使いよう
   * 意味: デバッガに意外な便利コマンドがあったり、ソースを印刷した紙をハサミで切って並べてみるなど、プログラムを改良する方法にはさまざまなテクニックがある。
   * 教訓: 既に知っている道具もちゃんと調べるといいことがあるかも。

 * 地震,雷,火事,停電
   * 意味: 人間以外にも怖いものはある。
   * 教訓: このへんはマシンをデータセンターに置けばある程度避けられる。しかし人間は…!!

 * バグぶつかるも他生の縁
   * 意味: たまたまバグにぶつかっただけといっても、何かの縁なのだからバグレポートをしてあげよう。
   * 教訓: バグからはじまる恋もある(ねーよ

 * docを食らわばソースまで
   * 意味: ドキュメントを調べだしたらついでとばかりにソースまでよんでしまう。しかも本題と関係ないコードが理解できなくてなやんだりする。
   * 教訓: まずはFAQよんどこう。

 * ソースに入ってはソースに従え
   * 意味: 人のソースを変更するときはその人のコーディングスタイルに従って変更しよう。
   * 教訓: コーディングスタイルへの過度のこだわりはよくない。

 * 馬子にもGUI
   * 意味: つまんないプログラムでもGUIをつけるとそれっぽくみえるらしい。
   * 教訓: それっぽいだけかも。

 * 溺れるものは2chをも捕む
   * 意味: ハマってどうにもならないとき、2chの質問スレに頼る。
   * 教訓: とりあえず情報の裏はとっとけ。

 * 虎穴に入らずんばバグを得ず
   * 意味: (よくわからなくて怖い)フレームワークやlibcやカーネルの中まで読みこまないとバグを完全につきとめることはできないこともある。
   * 教訓: ソースさえ読めばそれなりにわかったりする。きにせずよんじゃえ。

 * 書いたコードに手をかまれる
   * 意味: ライブラリがおかしいのかとおもって延々調べても悪くなさそう。サンプルコードはちゃんと動く。まさかと思ってよくよく見ると自分が書いて大丈夫だと思ってたコードが間違えていた。
   * 教訓: それがバグ。

 * LL三年Web八年
   * 意味: どんなにLLを覚えても結局はWeb技術そのものに明るくなるために何年もかかってしまうということ。
   * 教訓: Webとブラウザ周辺は魔窟。

 * 能あるプログラマは爪を隠す
   * 意味: 有能なプログラマは、ほとんど「凄い」コードを書かないものである。
   * 教訓: 平易なコードの作者を甘く見るな。

 * 芸は身をHaskell
   * 意味: Haskell みたいなマイナー言語を使えるとかえって職にあぶれない。
   * 教訓: いろいろなプログラミング言語さわるとよい。

 * 多言語は無言語
   * 意味: いろんな言語にとりあえず手をだしてどれも中途半端にしかできないこと。
   * 教訓: 言語の深いところはあまりかわらないので、一つを極めてから他に手をだすとよい。

 * 案ずるより書くが易し
   * 意味: どの実装方針がいいかなあ、と迷うより書いてしまうほうが簡単。
   * 教訓: 迷ったら全部実装して比較。

 * 悪設計は百年の不作
   * 意味: 悪い設計を許してしまうとあらゆる場面で無駄な苦労をしてしまう。
   * 教訓: 設計は経験と能力の両方がある人にお願いしよう。

 * abで鯛を釣る
   * 意味: 「ab(apache bench)で性能測定」のような仕事のほうが意外に金をとれたりする。
   * 教訓: 自分的にたいしたことないスキルでも相手にとって価値があれば金になる。

 * makeるが勝ち
   * 意味: make一発でいろいろできるように自動化するとかなり便利
   * 教訓: 客の仕事だけでなく自分の仕事も自動化しよう

 * 転ばぬ先のテストケース
   * 意味: 先にテストを書いておけば防げるバグは多い。
   * 教訓: サボらずテストケースを書こう。