えっ？ 何でリダイレクトなの？

# まずは挨拶

Qiita に初投稿です。よろしくお願いいたします。

# サスペンドが出来ない

ある時 Ubuntu をアップデートしました。アップデート後の環境は

```sh
$ lsb_release -d -r
Description:    Ubuntu 14.04.5 LTS
Release:        14.04
```

です。

さて、寝ようと PC をサスペンドをしたのですがすぐに起き上がります。何かキーボートを押したかな？ と、またサスペンドをしたのですがすぐに起き上がります。寝れません。(うそ。寝ました) 調べます。いろいろ検索に掛かりますが、だいたいが [ArchWiki](https://wiki.archlinux.org/index.php/MacBook#Suspend_and_Hibernate) に書いてある解決策になるようです。曰く、 */proc/acpi/wakeup* の
`LID0` 以外を `disabled` にしなさい。以下の様に。

```sh
# echo XHC1 > /proc/acpi/wakeup
```

、、、えっ？ 何でリダイレクトなの？ ま、まあ 素直に従います。

```sh
$ cat /proc/acpi/wakeup
Device  S-state   Status   Sysfs node
UAR1      S4    *disabled  pnp:00:0b
USB1      S3    *disabled
RP01      S4    *disabled  pci:0000:00:1c.0
RP03      S4    *disabled  pci:0000:00:1c.2
BR40      S4    *disabled  pci:0000:02:00.0
RP04      S4    *disabled  pci:0000:00:1c.3
GLAN      S4    *disabled
EHC1      S4    *enabled   pci:0000:00:1d.0
EHC2      S4    *enabled   pci:0000:00:1a.0
XHC       S4    *enabled   pci:0000:00:14.0
HDEF      S4    *disabled  pci:0000:00:1b.0
PEG0      S4    *disabled
PEGP      S4    *disabled
PEG1      S4    *disabled
PEG2      S4    *disabled
```

(上記の wakeup の内容はサスペンドが効かない状況とは若干違うかもしれません。解決してからまたアップデートしたので。大筋ということで)

自分の環境だと `EHC1` と `EHC2` と `XHC` を `disabled` にすればいいのだな。

```sh
$ sudo sh -c "echo EHC1 > /proc/acpi/wakeup"
$ sudo sh -c "echo EHC2 > /proc/acpi/wakeup"
$ sudo sh -c "echo XHC > /proc/acpi/wakeup"

$ cat /proc/acpi/wakeup
Device  S-state   Status   Sysfs node
UAR1      S4    *disabled  pnp:00:0b
USB1      S3    *disabled
RP01      S4    *disabled  pci:0000:00:1c.0
RP03      S4    *disabled  pci:0000:00:1c.2
BR40      S4    *disabled  pci:0000:02:00.0
RP04      S4    *disabled  pci:0000:00:1c.3
GLAN      S4    *disabled
EHC1      S4    *disabled  pci:0000:00:1d.0
EHC2      S4    *disabled  pci:0000:00:1a.0
XHC       S4    *disabled  pci:0000:00:14.0
HDEF      S4    *disabled  pci:0000:00:1b.0
PEG0      S4    *disabled
PEGP      S4    *disabled
PEG1      S4    *disabled
PEG2      S4    *disabled
```

サスペンドが出来るようになりました。めでたしめでたし。が、この記事の主題はそこではありません。主題は

**なぜ リダイレクトによる /proc/acpi/wakeup への書き込みで結果が内容と違うのか** 


という事です。

# /proc/acpi/wakeup に書き込むとはどういうことか？

上述の出力を見れば分かるように */proc/acpi/wakeup* は複数行です。普通は `echo EHC1 > /proc/acpi/wakeup`は 1行になると考えられます。

-   /proc下は 仮想ファイルシステム(procfs)で、プロセス、カーネルにファイル形式でアクセスするインターフェースの役割を持つ。逐次作成している。

しかしこれは 前述の例で 1行にならない説明になっていません。

-   想像だが、wakeup を扱っているモジュールが ファイルディスクリプタの操作で文字列を受け内容を解釈し、wakeupファイルを逐次作成している。

これまた ぼんやりしています。内容を解釈って具体的にはなんだろう？

よく分からないので具体的に何をやっているのか見てみましょう。

# strace で確認

`strace` コマンドを使います。 `strace` はシステムコールやシグナルを監視するツールです。

まずは `EHC1` を `enabled` にして

```sh
$ sudo sh -c "echo EHC1=enabled > /proc/acpi/wakeup" 
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1      S4    *enabled   pci:0000:00:1d.0
```

、、、実はこの実行の認識が間違っている事を後で気付くのですが。

そして `strace` 。(なのだが、ちょっと長い上に getcwd("/home/xxx" &#x2026; はまずいので)

```sh
$ sudo -p "sudo> " test
sudo> 

$ sudo strace sh -c "echo EHC1 > /proc/acpi/wakeup" 2> strace_write_ehc1_to_wakeup.txt

## strace の出力行数
$ wc -l strace_write_ehc1_to_wakeup.txt 
50 strace_write_ehc1_to_wakeup.txt

## head -10
$ head -10 strace_write_ehc1_to_wakeup.txt
execve("/bin/sh", ["sh", "-c", "echo EHC1 > /proc/acpi/wakeup"], [/* 18 vars */]) = 0
brk(0)                                  = 0x7f03e785b000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f03e6ed7000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=106703, ...}) = 0
mmap(NULL, 106703, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f03e6ebc000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)

## tail -10
$ tail -10 strace_write_ehc1_to_wakeup.txt
close(1)                                = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0
write(1, "EHC1\n", 5)                   = 4
write(1, "\n", 1)                       = 1
dup2(10, 1)                             = 1
close(10)                               = 0
exit_group(0)                           = ?
+++ exited with 0 +++

## システムコールの履歴
$ cat strace_write_ehc1_to_wakeup.txt | cut -s -d \( -f 1 |paste -d, - - - - - 
execve,brk,access,mmap,access
open,fstat,mmap,close,access
open,read,fstat,mmap,mprotect
mmap,mmap,close,mmap,mmap
arch_prctl,mprotect,mprotect,mprotect,munmap
getpid,rt_sigaction,geteuid,brk,brk
getppid,getcwd,rt_sigaction,rt_sigaction,rt_sigaction
rt_sigaction,rt_sigaction,rt_sigaction,open,fcntl
close,fcntl,dup2,close,write
write,dup2,close,exit_group,
```

おそらく tail の出力に現れている writeシステムコール が */proc/acpi/wakeup*への書き込み処理の実態でしょう。しかしこれだけではよく分かりません。

そこで */proc/acpi/wakeup* への書き込みと */dev/null* への書き込みとの `strace` の
出力の比較をしてみます。と、その前に `strace` の出力で `0x7f03e785b000` などの0x(16進数12桁) の文字列があります。これはアドレスなので `0xzzzz` に置換して比較します。

```diff
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1      S4    *enabled   pci:0000:00:1d.0

$ diff -U0 \
<(sudo strace sh -c "echo EHC1 > /dev/null"         2>&1 |sed -E 's/0x[a-fA-F0-9]{12}/0xzzzz/g') \
<(sudo strace sh -c "echo EHC1 > /proc/acpi/wakeup" 2>&1 |sed -E 's/0x[a-fA-F0-9]{12}/0xzzzz/g')
--- /proc/self/fd/11    2016-09-28 18:44:26.882716060 +0900
+++ /proc/self/fd/13    2016-09-28 18:44:26.883716065 +0900
@@ -1 +1 @@
-execve("/bin/sh", ["sh", "-c", "echo EHC1 > /dev/null"], [/* 18 vars */]) = 0
+execve("/bin/sh", ["sh", "-c", "echo EHC1 > /proc/acpi/wakeup"], [/* 18 vars */]) = 0
@@ -26 +26 @@
-getpid()                                = 30419
+getpid()                                = 30421
@@ -31 +31 @@
-getppid()                               = 30414
+getppid()                               = 30415
@@ -39 +39 @@
-open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
+open("/proc/acpi/wakeup", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
@@ -45 +45,2 @@
-write(1, "EHC1\n", 5)                   = 5
+write(1, "EHC1\n", 5)                   = 4
+write(1, "\n", 1)                       = 1
```

上記の結果から 文字列 "EHC1\n" の書き込み処理は、対象のファイルによってwriteシステムコール を

-   */dev/null(通常ファイルもか)*: 5バイトを一気に処理
-   */proc/acpi/wakeup*: 4バイト処理(返り値[^fname1]) した後、残りの 1バイトを処理

という様に呼び出している事が分かります。

さて、こうなると長い文字列を書き込んだ時どうなるのかが気になります。たぶん */proc/acpi/wakeup* には何を書き込んでもいいような気がします。そこで 文字列 `euu45678901234567890` を書き込んでみます。

```diff
$ diff -U0 \
<(sudo strace sh -c "echo euu45678901234567890 > /dev/null"         2>&1 |sed -E 's/0x[a-fA-F0-9]{12}/0xzzzz/g') \
<(sudo strace sh -c "echo euu45678901234567890 > /proc/acpi/wakeup" 2>&1 |sed -E 's/0x[a-fA-F0-9]{12}/0xzzzz/g')
--- /proc/self/fd/11    2016-09-28 20:57:35.643468246 +0900
+++ /proc/self/fd/13    2016-09-28 20:57:35.643468246 +0900
@@ -1 +1 @@
-execve("/bin/sh", ["sh", "-c", "echo euu45678901234567890 > /dev"...], [/* 18 vars */]) = 0
+execve("/bin/sh", ["sh", "-c", "echo euu45678901234567890 > /pro"...], [/* 18 vars */]) = 0
@@ -26 +26 @@
-getpid()                                = 31593
+getpid()                                = 31590
@@ -31 +31 @@
-getppid()                               = 31586
+getppid()                               = 31587
@@ -39 +39 @@
-open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
+open("/proc/acpi/wakeup", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
@@ -45 +45,6 @@
-write(1, "euu45678901234567890\n", 21)  = 21
+write(1, "euu45678901234567890\n", 21)  = 4
+write(1, "5678901234567890\n", 17)      = 4
+write(1, "901234567890\n", 13)          = 4
+write(1, "34567890\n", 9)               = 4
+write(1, "7890\n", 5)                   = 4
+write(1, "\n", 1)                       = 1
```

上記の結果から 文字列の書き込み処理は、対象のファイルによって writeシステムコール を

-   */dev/null(通常ファイルもか)*: 文字列を一気に(リミットあり[^fname1])処理
-   */proc/acpi/wakeup*: 4バイトずつ処理

という様に呼び出している事が分かります。

# /proc/acpi/wakeup への書き込みの実際

先程、実行の認識が間違っていた、と書きましたが、実際に */proc/acpi/wakeup* への書き込みの例を示します。

## echo EHC1 をリダイレクト

```sh
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1      S4    *enabled   pci:0000:00:1d.0

$ sudo sh -c "echo EHC1 > /proc/acpi/wakeup"

$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1      S4    *disabled  pci:0000:00:1d.0
```

EHC1 の状態: enabled  ->  disabled

## echo EHC1=disabled をリダイレクト

```sh
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1      S4    *disabled  pci:0000:00:1d.0

$ sudo sh -c "echo EHC1=disabled > /proc/acpi/wakeup"

$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1      S4    *enabled   pci:0000:00:1d.0
```

EHC1 の状態: disabled  ->  enabled

よって XXX=disabled 形式の設定は無効

## echo EHC1=1 をリダイレクト

```sh
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1      S4    *enabled   pci:0000:00:1d.0

$ sudo sh -c "echo EHC1=1 > /proc/acpi/wakeup"

$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1      S4    *disabled  pci:0000:00:1d.0
```

EHC1 の状態: enabled  ->  disabled

よって XXX=1 形式の設定は無効

## echo EHC1 を連続でリダイレクト

```sh
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1      S4    *disabled  pci:0000:00:1d.0

$ sudo sh -c "echo EHC1 > /proc/acpi/wakeup"

$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1      S4    *enabled   pci:0000:00:1d.0

$ sudo sh -c "echo EHC1 > /proc/acpi/wakeup"

$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1      S4    *disabled  pci:0000:00:1d.0
```

EHC1 の状態: disabled  ->  enabled  ->  disabled

トグル処理 になる

## echo EHC123 をリダイレクト

```sh
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1      S4    *disabled  pci:0000:00:1d.0

$ sudo sh -c "echo EHC123 > /proc/acpi/wakeup"

$ cat /proc/acpi/wakeup |grep -E "^EHC" 
EHC1      S4    *enabled   pci:0000:00:1d.0
EHC2      S4    *disabled  pci:0000:00:1a.0
```

EHC1 の状態: disabled  ->  enabled

変数 EHC1 への書き込みと解釈される。EHC123 という変数が出来るわけではない

## echo XHC を連続でリダイレクト

```sh
$ cat /proc/acpi/wakeup |grep -E "^XHC"
XHC       S4    *disabled  pci:0000:00:14.0

$ sudo sh -c "echo XHC > /proc/acpi/wakeup" 

$ cat /proc/acpi/wakeup |grep -E "^XHC"
XHC       S4    *enabled   pci:0000:00:14.0

$ sudo sh -c "echo XHC > /proc/acpi/wakeup"

$ cat /proc/acpi/wakeup |grep -E "^XHC"
XHC       S4    *disabled  pci:0000:00:14.0
```

XHC の状態: disabled  ->  enabled  ->  disabled

トグル処理 になる

## echo XHC5, echo XHC56 をリダイレクト

```sh
$ cat /proc/acpi/wakeup |grep -E "^XHC"
XHC       S4    *disabled  pci:0000:00:14.0

$ sudo sh -c "echo XHC5 > /proc/acpi/wakeup"

$ cat /proc/acpi/wakeup |grep -E "^XHC"
XHC       S4    *disabled  pci:0000:00:14.0

$ sudo sh -c "echo XHC56 > /proc/acpi/wakeup"

$ cat /proc/acpi/wakeup |grep -E "^XHC"
XHC       S4    *disabled  pci:0000:00:14.0
```

XHC の状態: disabled -> disabled  ->  disabled

XHC5, XHC56 の様に 最初の 3バイトが XHC でも 変数 XHC と解釈しない

## echo XHC 0 をリダイレクト

```sh
$ cat /proc/acpi/wakeup |grep -E "^XHC"
XHC       S4    *disabled  pci:0000:00:14.0

$ sudo sh -c "echo XHC 0 > /proc/acpi/wakeup"

$ cat /proc/acpi/wakeup |grep -E "^XHC"
XHC       S4    *enabled   pci:0000:00:14.0
```

XHC の状態: disabled  ->  enabled

最初の 4バイト が "XHC " (この場合最後の1バイトはスペース) の時 変数として解釈するのだろう

# /proc/acpi/wakeup に書き込むとは

以上のリダイレクト時のシステムコールや書き込み例から推察すると、 */proc/acpi/wakeup* への書き込みは、 `EHC1=enabled` といった様に値を指定出来るわけではなく、単に

**リダイレクトで送る文字列の最初の4バイトが /proc/acpi/wakeup 内の変数であった時、その変数のトグル処理が行われる** 


という事です。

*/proc* 下のファイルに `echo 1`, `echo 0` とかをリダイクレトするのはよくやりますが、この wakeupファイル の様な、最初の4バイトだけを受け付けるファイルもある ってのは常識なのだろうか？

# wakeup の他の設定方法

[こちらの記事](http://dothiko.hatenablog.com/entry/2014/04/23/201923) の様な手順で設定することも出来ます。まず

```sh
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1      S4    *enabled   pci:0000:00:1d.0
```

と `pci:0000:00:1d.0` なので */sys/bus/pci/devices/0000:00:1d.0/power/wakeup* 
を読みます。

```sh
$ cat /sys/bus/pci/devices/0000:00:1d.0/power/wakeup
enabled
```

*/proc/acpi/wakeup* の `EHC1` と同じく `enabled` です。そこで `disabled`を書き込んでみます。

```sh
$ sudo sh -c "echo disabled > /sys/bus/pci/devices/0000:00:1d.0/power/wakeup"
$ cat /sys/bus/pci/devices/0000:00:1d.0/power/wakeup
disabled
$ cat /proc/acpi/wakeup |grep -E "^EHC1"
EHC1      S4    *disabled  pci:0000:00:1d.0
```

*/proc* , */sys* どちらにも反映されました。

ただし

```sh
$ sudo sh -c "echo enab > /sys/bus/pci/devices/0000:00:1d.0/power/wakeup"
sh: echo: I/O error
$ sudo sh -c "echo 1 > /sys/bus/pci/devices/0000:00:1d.0/power/wakeup"
sh: echo: I/O error

$ cat /sys/bus/pci/devices/0000:00:1d.0/power/wakeup
disabled
```

誤った文字列を書き込もうとすると `I/O error` (I/Oエラーなんだ) を吐きます。正確な情報を設定しましょう。

```sh
$ sudo sh -c "echo enabled > /sys/bus/pci/devices/0000:00:1d.0/power/wakeup"
$ cat /sys/bus/pci/devices/0000:00:1d.0/power/wakeup
enabled
```

そう考えると、wakeup に関しては `procfs` へのアクセスの方が `sysfs` へのアクセスよりも安全な気がします。(まあ、この辺を変更する人間が安全に配慮をしないわけがないのですが)

# あらためて

今後ともよろしくお願いいたします。


[^fname1]:
  https://linuxjm.osdn.jp/html/LDP_man-pages/man2/write.2.html
  単に `$ man 2 write` だけど