Home > Latest topics

Latest topics 近況報告

たまに18歳未満の人や心臓の弱い人にはお勧めできない情報が含まれることもあるかもしれない、甘くなくて酸っぱくてしょっぱいチラシの裏。RSSによる簡単な更新情報を利用したりすると、ハッピーになるかも知れませんしそうでないかも知れません。

萌えるふぉくす子さんだば子本制作プロジェクトの動向はもえじら組ブログで。

宣伝1。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能! シス管系女子って何!? - 「シス管系女子」特設サイト

宣伝2。Firefox Hacks Rebooted発売中。本書の1/3を使って、再起動不要なアドオンの作り方のテクニックや非同期処理の効率のいい書き方などを解説しています。既刊のFirefox 3 Hacks拡張機能開発チュートリアルと併せてどうぞ。

Firefox Hacks Rebooted ―Mozillaテクノロジ徹底活用テクニック
浅井 智也 池田 譲治 小山田 昌史 五味渕 大賀 下田 洋志 寺田 真 松澤 太郎
オライリージャパン

Page 5/239: « 1 2 3 4 5 6 7 8 9 »

シェルスクリプトでだいたい1時間の間隔であれをやる - Jan 03, 2016

前のエントリに引き続いて、またシェルスクリプトの話。

「1時間間隔で決まった処理を行う」という目的だと、普通に考えたらまあcrontabを使う場面ですよね。

だから素直にそうしときゃいいんだけど、シェルスクリプト製のTwitter用botで自発的な自動投稿をやらせるにあたってどういうわけか「きっかり同じ時間間隔じゃなくて、確率でちょっとだけ揺らぎを持たせたい。その方が人間くさいよね。」と思ってしまって、それをやるのに一苦労しました……という話です。これは。

目指す状態

そもそも「きっかり同じ時間間隔じゃなくて、ちょっとだけ揺らぎをもって定期実行したい」というのは、一体どういう状態のことを指しているのか。 これをはっきりさせないことには話が始まりません。

僕が思ってる事をアスキーアートで図にすると以下のようになります。

00:00 基準時刻
  |
00:15
  |
00:30
  |
00:45
  |           ̄\
01:00 目標時刻   >だいたいこの範囲で必ず1回実行する
  |          _/
01:15
  |
01:30
  |
01:45
  |           ̄\
02:00 目標時刻   >だいたいこの範囲で必ず1回実行する
  |          _/
02:15
  |
02:30
  |

人間の行動で言うと、こんな感じ。

  • 時計を持って「1時間に1回これをやるように」と言われて、時計を見てその時刻に近かったらそれをやる。
  • ちょっと早くても「まぁもうやっちゃってもいいか」ということでやる。
  • ちょっと遅くても「まぁこのくらいの遅れは大丈夫でしょ」ということでやる。
  • 「やらない」という選択肢は無い。「やべっ時計見落としてた!」と気がついたらその時点で慌ててそれをやる。

ちょっとばかり時間にルーズな人の取るような行動、という事ですね。

これをもうちょっと厳密に、コンピュータにも分かりやすいであろう表現に直すと、以下のように言えるでしょうか。

  • 目標時刻の前後N分の範囲の時間帯を、処理を実行する可能性がある時間帯とする。
    • それより早かったり遅かったりしたら実行しない。
  • 目標時刻に近ければ近いほど実行の確率は高く、遠ければ遠いほど実行の確率は低い。
    • 目標時刻ちょうどで最大確率、目標時刻のN分前およびN分後の時点で最低確率とし、その間は確率が線形に変化するものとする。
  • ただし、その範囲の時間帯が過ぎようとしている時にまだ1回も処理が実行されていなければ、時間帯の終わりで必ず実行する。
  • 計算は分単位で行う。(cronjobでも1分未満の指定はできないので)

実行確率をパーセンテージで算出できれば、あとは前のエントリでやった「何パーセントの確率であれをやる」がそのまま使えます。

となると、問題は「どうやって実行の確率を計算するか」という話になります。

今の時刻が目標時刻から何分ずれているかを計算する

先の定義に基づいて「ある時点での実行確率」を計算しようと思った時に、時刻を時刻の形式のまま扱おうとするとややこしいというか自分にはお手上げなので、「その日の0時0分を起点として、そこから何分経過したか」を使って計算していこうと思います。

そのために、こんな関数を用意しました。

# "03:20"のような時刻を与えると、00:00からの経過分数を出力する
time_to_minutes() {
  local now="$1"
  local hours=$(echo "$now" | sed -r 's/^0?([0-9]+):.*$/\1/')
  local minutes=$(echo "$now" | sed -r 's/^[^:]*:0?([0-9]+)$/\1/')
  echo $(( $hours * 60 + $minutes ))
}

これに与える現在時刻は、コマンド置換とdateコマンドを使って$(date +%H:%M)とします。 例えば現在時刻が07:58なら、以下のような出力が得られます。

$ time_to_minutes $(date +%H:%M)
478

これを「処理を実行したい時間間隔(分)」で割った余りを得ると、現在時刻が目標時刻から何分ずれているかが分かります。 60分間隔ならこうです。

$ interval=60
$ lag=$(( 478 % $interval ))
$ echo $lag
58

58分ずれている……という結果ですが、これはどっちかというと「目標時刻からマイナス方向に2分ずれている」と扱いたいところです。 なので、実際のずれが実行間隔の半分よりも大きい場合は「マイナス方向にN分のずれ」と見なすようにします。

$ half_interval=$(( $interval / 2 ))
$ [ $lag -gt $half_interval ] && lag=$(( $interval - $lag ))
$ echo $lag
2

これで「目標時刻ピッタリから何分ずれているのか」が求まったので、次はいよいよ確率の計算です。

今の時刻での実行確率を計算する

目標時刻ピッタリで確率100%としてしまうとそこで必ず実行されてしまうので、目標時刻ちょうどでの最大の確率を90%、許容されるずれの最大時点での最低の確率を10%とすることにします。

全体の振れ幅は10%から90%までの「80」ですので、「目標時刻ちょうどで100%、目標時刻からのずれが許容範囲の最大になった時を0%」とした割合に80をかけた結果に10を足せば、確率は10%から90%までの範囲に収まることになります。 式にすると、こうです。

$ probability=$(( (($max_lag - $lag) / $max_lag) * 80 + 10 ))
$ echo $probability
10

……おや? どうも計算結果がおかしいですね。 実は算術展開の$((~))は整数のみの計算なので、計算の過程で小数が出てくると小数点以下切り捨ての計算になってしまうのです。

こうならないようにするには、小数が出てこないように注意して計算するか、小数があっても大丈夫な計算方法を使う必要があります。 例えば、先に100倍してパーセンテージを求めてから後で100で割るという方法を取るなら以下のようになります。

$ probability=$(( (($max_lag - $lag) * 100 / $max_lag) * 80 / 100 + 10 ))
$ echo $probability
58

小数として計算するのであれば、数値計算用のコマンドのbcを使います。 これは、標準入力で与えられた式の計算結果を出力するコマンドなのですが、scale=1;という指定で計算時の小数点以下の桁数を指定すると、小数部を考慮した計算結果を返してくれます。

$ probability=$(echo "scale=1; (($max_lag - $lag) / $max_lag) * 80 + 10" | bc)
$ echo $probability
58.0

ただし、if [ ... ]での条件分岐では今度は整数しか扱えないので、出力される計算結果の小数部は取り除いておく必要があります。 これはsedで行えます。

$ probability=$(echo "scale=1; (($max_lag - $lag) / $max_lag) * 80 + 10" | bc | sed -r -e 's/\.[0-9]+$//')
$ echo $probability
58

ということで、ここまでをまとめて「算出した実行確率を出力する関数」にしてみましょう。

interval=60
half_interval=$(( $interval / 2 ))
max_lag=5

calculate_probability() {
  local target_minutes=$1

  local lag=$(($target_minutes % $interval))
  [ $lag -gt $half_interval ] && lag=$(($interval - $lag))

  local probability=$(( (($max_lag - $lag) * 100 / $max_lag) * 80 / 100 + 10 ))
  # 最小の実行確率より小さい時=実行する可能性がある範囲の
  # 時間帯の外の時は、確率0%とする
  if [ $probability -lt 10 ]
  then
    echo 0
  else
    echo $probability
  fi
}

同じ時間帯では重複実行しない

単にこの確率に基づいて実行するかどうかを決めるだけだと、00:55から01:05までの範囲で「実行時刻が揺らぐ」のではなく「その範囲で、確率次第で何度も実行される」という結果になります。 そうしないためには、同じ時間帯の中での再実行を防ぐ必要があります。

そのためには、最後に処理を実行した時刻を保持しておいて、現在時刻が最終実行時刻から一定の範囲内にある時は問答無用で処理をスキップする、ということになります。 とりあえず、最終実行時刻(として、00:00からの経過時間)を保存するようにしてみます。

current_minutes=$(time_to_minutes $(date +%H:%M))
probability=$(calculate_probability $current_minutes)
if [ $(($RANDOM % 100)) -lt $probability ]
then
  # ここで定時処理を実行
  echo $current > /path/to/last_done
fi

ここで保存した値を次の実行の可否の判断時に使うのですが、「最後の実行からN分間は絶対に実行しない」という条件を加えても良ければ、以下のようにできます。

current_minutes=$(time_to_minutes $(date +%H:%M))

forbidden_minutes=10
last_done=$(cat /path/to/last_done)
if [ "$last_done" != '' ]
then
  delta=$(($current_minutes - $last_done))
  [ $delta -le $forbidden_minutes ] && exit 0
fi

...

現在時刻から最後の実行時刻を引いた結果の「最終実行時刻からの経過時間」を求めて、それが指定の範囲内であれば何もしないで終了するということです。 比較の演算子が-lt)ではなく-le)である点に注意して下さい。 -ltで比較してしまうと、00:55に実行してから10分後の01:05ちょうどの時点で「10分未満の範囲で実行されていないので、再実行してよい」と判断されてしまいます。

ただ、これだけだと日付をまたいだ時に判定が期待通りに行われません。 最後の実行時刻が例えば前日23時ちょうどだったとすると、last_doneは23*60=1380ですが、現在時刻が00:04だったとすると4-1380=-1376になってしまって、負の数は「何分間は再実行しない」という指定=正の数よりも必ず小さいので、永遠に再実行されないことになってしまいます。

なので、現在時刻から最終実行時刻を引いた結果が負の場合は、「最終実行時から0時までの経過時間」と「0時から現在までに経過した時間」の和を「最終実行時刻からの経過時間」として使う必要があります。

current_minutes=$(time_to_minutes $(date +%H:%M))

forbidden_minutes=10
last_done=$(cat /path/to/last_done)
if [ "$last_done" != '' ]
then
  delta=$(($current_minutes - $last_done))
  if [ $delta -lt 0 ]
  then
    one_day_in_minutes=$(( 24 * 60 ))
    delta=$(( $one_day_in_minutes - $last_done + $current_minutes ))
  fi
  [ $delta -le $forbidden_minutes ] && exit 0
fi

...

その時間帯で投稿が無い時は時間帯の最後のタイミングで必ず実行する

ここまで来たらあともう一息。 最後は「その時間帯で必ず1回は実行する」という要件です。

とはいえ、これはそんなに難しく考えなくても大丈夫。 前項の段階で「指定の範囲内の時間での再実行はしない」という判定が既に行われているので、その判定の後であれば、「実行するべき時間帯の最後の瞬間で、その時間帯の中ですでに実行済みである」という場面はあり得ない事になります。 なので、単純に「今この瞬間は、実行しても良い時間帯の範囲の最後の瞬間かどうか?」を判断して、そうであれば確率100%で実行するということにすればいいです。

current_minutes=$(time_to_minutes $(date +%H:%M))

forbidden_minutes=10
last_done=$(cat /path/to/last_done)
if [ "$last_done" != '' ]
then
  delta=$(($current_minutes - $last_done))
  if [ $delta -lt 0 ]
  then
    one_day_in_minutes=$(( 24 * 60 ))
    delta=$(( $one_day_in_minutes - $last_done + $current_minutes ))
  fi
  [ $delta -le $forbidden_minutes ] && exit 0
fi

# 目標時刻からのずれを計算
lag=$(($current_minutes % $interval))
if [ $lag -eq $max_lag ]
then
  # ずれが、許容されるずれの最大値と等しければ、今がまさに
  # その時間帯の最後の瞬間である。
  probability=100
else
  probability=$(calculate_probability $current_minutes)
fi

...

まとめ

ここまでのコード片を全てまとめた物が、以下になります。

time_to_minutes() {
  local now="$1"
  local hours=$(echo "$now" | sed -r 's/^0?([0-9]+):.*$/\1/')
  local minutes=$(echo "$now" | sed -r 's/^[^:]*:0?([0-9]+)$/\1/')
  echo $(( $hours * 60 + $minutes ))
}

interval=60
half_interval=$(( $interval / 2 ))
max_lag=5

calculate_probability() {
  local target_minutes=$1

  local lag=$(($target_minutes % $interval))
  [ $lag -gt $half_interval ] && lag=$(($interval - $lag))

  local probability=$(( (($max_lag - $lag) * 100 / $max_lag) * 80 / 100 + 10 ))
  if [ $probability -lt 10 ]
  then
    echo 0
  else
    echo $probability
  fi
}

current_minutes=$(time_to_minutes $(date +%H:%M))

forbidden_minutes=10
last_done=$(cat /path/to/last_done)
if [ "$last_done" != '' ]
then
  delta=$(($current_minutes - $last_done))
  if [ $delta -lt 0 ]
  then
    one_day_in_minutes=$(( 24 * 60 ))
    delta=$(( $one_day_in_minutes - $last_done + $current_minutes ))
  fi
  [ $delta -le $forbidden_minutes ] && exit 0
fi

lag=$(($current_minutes % $interval))
if [ $lag -eq $max_lag ]
then
  probability=100
else
  probability=$(calculate_probability $current_minutes)
fi

if [ $(($RANDOM % 100)) -lt $probability ]
then
  # ここで定時処理を実行
  echo $current > /path/to/last_done
fi

人間くさい振る舞いをする何かを作る時の参考にしてみて下さい。

追記:もっと単純なやり方

Qiitaのクロスポストの方に頂いたコメントで、以下のようにcronjobを設定すれば良いのでは?とのご指摘がありました。

55 * * * * sleep $(( $RANDOM \% 10 ))m; (実行したい処理)

実行の可能性がある時間帯の最初の瞬間にsleepを呼び、何秒間待つかは0~10分の間でランダムに決定する。その後、やりたい処理を実行する。という方法です。

「指定の時間間隔ちょうどの実行確率を最も高くしたい」「その時間帯の最初の瞬間から最後の瞬間までの間に運用を開始した時も、すぐに動作させたい」といったいくつかの要件を除外すれば、この方法が最もシンプルですね。 というか最初この指摘を見た時には「完全に置き換え可能じゃん!」とすら思ってしまいました。 (よくよく見返して、要件のいくつかがカバーされていない事にようやく気づくレベル)

無駄に複雑な要件を全て満たそうとすると手間がかかるけれども、要件の8割9割ほどを満たせれば良いという割り切りができれば手間を大きく減らせる場合がある、「そもそも本当にその要件は必要なの?」というレベルからの再考次第で実現手法を大きく簡素化できるという、いい例だと思いました。 そのあたりの絞り込みが足りないままこの記事を世に出してしまって、お恥ずかしい限りです……。

シェルスクリプトでランダムにあれをやる - Dec 30, 2015

「何分の一で」とかの情報は出てくるんだけど、知りたかったことそのものズバリの「何パーセントの確率でアレをやる」という例がなかなか見つからなかったので、まとめてみました。

シェルスクリプトで乱数

まず根底にある「ランダムに」っていう所だけど、これはBashかそうでないかでやり方が変わる。 Bashでは$RANDOMを参照すると0から32767の範囲でランダムな結果が得られる。

$ echo $RANDOM
15999

Bash以外では、/dev/urandomodコマンドを組み合わせて似たような事ができるようだ。

$ od -vAn --width=4 -tu4 -N4 </dev/urandom
 1939740834

0~Nの範囲で乱数を得る

以下、説明を簡単にするために$RANDOMの方でコードを書くけど、違うシェルでは適宜読み替えて下さいという事で。 あと、ここからは数値計算が出てくるので、中に書いた式を計算した結果を得る$((計算式))の書き方(算術展開)を使っていく。

気を取り直して、0~Nの範囲でランダムに1つを選ぶ方法。 これは割り算の余りを使う。 乱数をNで割った余りを求めれば、0~Nのいずれかの数字が得られる。 例えば$(($RANDOM % 10))とすれば、0~9のいずれかの数字が得られる(つまり、10パターンに分岐できる)。

$ echo $(($RANDOM % 10))
0
$ echo $(($RANDOM % 10))
5
$ echo $(($RANDOM % 10))
3

1/Nの確率で何かやる

先の結果がどれか1つの選択肢に等しくなった時だけ処理を実行すれば、「約1/Nの確率で実行」ということになる。 [ $(($RANDOM % 3)) -eq 0 ]なら、約1/3の確率で真になり&&以下が実行される。

$ [ $(($RANDOM % 3)) -eq 0 ] && echo 'Run!'
$ [ $(($RANDOM % 3)) -eq 0 ] && echo 'Run!'
Hit!
$ [ $(($RANDOM % 3)) -eq 0 ] && echo 'Run!'

ここまではすぐ例文が出てくるんだけど、ここから先が出てこなかったので自分で考える必要があった。

N%の確率で何かやる

実際に「ランダムに何かをやりたい」時というのは、多分、だいたいは「パーセンテージとか割合で頻度を指定したい」って場面だと思う。 「60%の確率で分岐したい」みたいな。

これは、「1/Nの確率で」の例を発展させるとできる。 1/100までの精度だったら、まず0~99のいずれか1つをランダムに得る。 次に、これを-lt演算子(less thanだから、左辺が右辺より小さい<の意味)で「何パーセントでやりたい」という数字と比較する。 結果が真の時だけ処理を実行すれば、つまり「何パーセントの確率で実行」ということになる。

絵を描くのが面倒なのでアスキーアートでやると、

0--------------------99

こういう数直線があって

0-----+-------------99
      ↑30

この位置に線を引いて、0から99までのどれか1つをランダムに選んだ結果が線より左にある時だけ実行するということです。

 ↓この時だけ実行  ↓こっちだったら実行しない
 ○ ○   ×    × ×
0-----+-------------99
      ↑30

これを踏まえて、30%の確率でRun!という文字列を出すコマンド列なら、以下のようになる。

$ if [ $(($RANDOM % 100)) -lt 30 ]; then echo 'Run!'; fi

30の所を変えれば任意のパーセンテージにできる。 関数にするならこんな感じか。

run_with_probability() {
  local probability=$1
  if [ $(($RANDOM % 100)) -lt $probability ]
  then
    echo 'Run!'
  fi
}

ほんとに狙ったとおりの結果を得られているか、同じ物を1000回くらい繰り返し実行して確かめてみる。 与えた数の連番を出力するseqコマンドとforループを組み合わせて、先の関数を1000回実行し、Run!が出力される頻度を見てみる。 (forループの出力結果をパイプラインでwc -lに渡して行数を数えれば、実際に出力された回数が分かる。)

$ for i in $(seq 1000); do run_with_probability 30; done | wc -l
303
$ for i in $(seq 1000); do run_with_probability 30; done | wc -l
292
$ for i in $(seq 1000); do run_with_probability 30; done | wc -l
316

1000回中の300回前後なので、まあだいたい30%になっている。 ばらつきがあるけど、試行回数を増やせば指定のパーセンテージに収束していくはず。

実際は「一定の確率で文字列を出力する」というのを汎用的にやりたかったので、こういう風にした。

probability() {
  [ $(($RANDOM % 100)) -lt $1 ] && cat
}

# 95%の確率で出力→だいたいは出力される
output_message | probability 95
# 10%の確率で出力→滅多に出ない
output_message | probability 10

入力された複数行の中からランダムに1行抜き出す

ちょっと毛色が違うけど、これもついでに。

入力に対してその中からランダムに1つをピックアップするという場面では、これはQiitaにクロスポストした方の記事のコメントで指摘を頂いて知ったんだけど、そのものずばりのshufというコマンドがある。これは標準入力で受け取った内容を行ごとにシャッフルして出力するコマンドで、-nで取り出す行数を指定できるので、以下のようにすれば「ランダムに1行取り出す」という結果になる。

# 他のコマンドから渡された結果の中からランダムに1行を出力してみる
read_messages | shuf -n 1

shufコマンドの存在を知らなかった時にそれを使わずに解いてみた時には、先の「0~Nのいずれかを得る」の応用で以下のようにしてた。

choose_random_one() {
  // 標準入力を一旦変数に保持
  local input="$(cat)"
  // 入力の行数を得る
  local n_lines="$(echo "$input" | wc -l)"
  // 「1~最終行の行番号」の範囲でどれか1つを得る
  local index=$(( ($RANDOM % $n_lines) + 1 ))
  // 得た行番号を使って、sedで「指定された番号の行だけを取り出す」操作を行う
  echo "$input" | sed -n "${index}p"
}

# 他のコマンドから渡された結果の中からランダムに1行を出力してみる
read_messages | choose_random_one

入力を「行数を数える時」と「実際に抽出する時」の2回使わないといけないので、一旦全部catで読み取って変数に保持してるというのがポイントでしょうか。

まとめ

ということで、「シェルスクリプトでランダムにアレをやる」色々でした。

なんでこんな事やってるかというと、シス管系女子の宣伝を自動化したくて、宣伝用アカウントの運用をボットにやらせたかったのですが、「コマンド&シェルスクリプト」の連載なんだからボットもシェルスクリプトの方がネタになるよね&自分で作れば「お、作者はちゃんと技術分かってる人なんだな」と技術的な信頼に繋がるかな?と思って、TwitterクライアントボットをBashでゴリゴリ書いているからなのでした。 ……って、単に宣伝を投稿するだけならTwitterクライアントができた時点でcronjobでやってしまえばよかったはずなのに、「何パーセントの確率で会話を継続する」とかそんな領域に足を踏み入れてるのは明らかにおかしいですね。ほんとに「どうしてこうなった」だ。

BtoBの仕事だったり実用のアドオンだったりでしかコード書いてないと、一定の確率で何かやるという事が必要になる場面が全く無くて(確実に何かやる、という事ばっかりだから……)、ぱっとやり方を思いつけなくて参りました。 という情けないお話。

非公開のアドオンの署名手続きからのレビューの分離について - Dec 17, 2015

De-coupling Reviews from Signing Unlisted Add-ons | Mozilla Add-ons Blogの私的な翻訳です。


長いので3行で説明 – 今週中(2015年12月4日)までに、私達は非公開のアドオンに対する署名を完全に自動化し、人力レビューを発生させないようにすることを考えています。

ここ数日、アドオンの署名手続きの最初のステップについての議論がありました。「バリデータ(検証器)」として知られる一連のコードによる、アドオン登録時の機械的レビューの改善についてです。バリデータは登録されるアドオンについて様々な理由から人力のレビューを喚起し、署名手続きを中断させます。これはアドオンのリリースを遅らせることに繋がります。なぜなら、署名を要求するという要件がFirefox 43以降のバージョンでは強制されるからです。

過去には、バリデータの有用性について議論が持ち上がったこともありました。悪意ある開発者であればバリデータによる検証を回避するコードを書くことができるからです。私達は、バリデータのできることには限界があることを承知しています。現実には、あくまで既知の危険なコードに付いてしか検出できず、それでは対応できない未知の危険なコードはたくさんあります。しかし、バリデータはレビューの過程の1要素に過ぎず、私達は開発者がアドオンをより提供しやすく、それを使う人達がより安全になるようにしたいと願っています。私達はバリデータについて、完全なマルウェア検出機構として動作することを意図しておらず、むしろ、Firefoxユーザにより適切な形でアドオンを届けられるように開発者を手助けすることを意図しています。

このことを考慮して、私達は非公開のアドオンに対する関門としてのバリデーションを取り除こうとしています。私達は開発者が非公開のアドオンを提供しやすいようにしたいと考えており、従ってレビューは署名手続きとは独立して行っていくことになります。今週末(2015年12月4日)、私達は非公開のアドオンに対する署名を完全に自動化し、人力レビューを発生させないようにすることを考えています。この日付は不確定な物で、これを可能にするために必要な技術的・手続き的・ポリシー的な変更を我々がどれだけ早くできるかに依ります。今月の初めに導入されたアドオンの署名APIは、署名手続きの完全な自動化が可能となり、この件についての解決策の一部となるでしょう。

私達は、アドオン開発者の方々には引き続き、MDNで概説されているFirefoxアドオンのポリシーへの同意を求め、署名のための登録に先立ってそれらのポリシーに自身のアドオンが違反していないかを確認することをお願いしていきます。開発者の方々はアドオンのレビュアー向けガイド(アドオンがレビューを通過しなかったりブロックリストに入れられてしまったりする主な理由を概説しています)の内容も把握するようにして下さい。

私は先週を通じて得られたそれらの情報と洞察をもたらしてくれたすべての人に感謝しています。私達はアドオン開発者やユーザの方々にとってFirefox上での体験を可能な限り痛みを伴わない物にしていきたいと思っています。また、「生き辛い」やり方であるように思われる場合があったとしても、そのような生き辛さは、私達の目標には絶対に含まれません。どうか、私や他のチームメンバーに直接、気軽に意見を言い続けて下さい。

私はそれらを可能にするための次のステップのより具体的な概要を投稿するつもりで、その進捗はbug 1229197で見ることができます。前もって、あなたの我慢強さに感謝します。

kev(訳注:Kev Needham、Mozilla Corporationの雇用スタッフ。)


ということなのですが、これはあくまで非公開(unlisted)のアドオンに限定した話のようで、公開(listed)のアドオンについてはこの限りではない模様です。実際、先日新バージョンを公開しようとした公開のアドオンでは人力レビューを経なければ署名は得られませんでした。

このエントリ中では触れられていませんが、1229197 – Allow unlisted add-ons to be signed without passing the validatorでは「非公開アドオンなのであればサイドローディングの可否に依らず署名手続きにレビューが不要なようにする」という話になっているようで、実際に行われた変更でも確かにそのようになっています。

なお、このエントリでは、「アドオンが人力レビューを通過できないのでいつまで経っても署名を得ることができず、テストも配布もできない」という状況が発生していた事について、「人力レビューの通過を署名の必須要件に含めなくした」という事が述べられていますが、その後の人力レビュー自体については言及されていません。 なので、Mozillaのポリシー的に許容され得ない危険なアドオン(例えば、リンク先のローカルファイルのショートカットを自動で開くような物)については、仮にバリデータ(Linterに名前が変わったようですが)での機械的チェックを通過して署名を得られたとしても、その後のエディターによる判断で「これはマルウェア的だからやっぱりブロックリストに入れよう」とされてしまう可能性は依然としてある、と思っておいた方が良さそう……というのが今のところの自分の認識です。

ちなみに、第一報からだいぶ時間が過ぎた今頃になって何で訳してるの? と思われそうですが、それまでは「マルウェアなどの危ないアドオンを排除したいから署名義務化します、危険なアドオンはレビューで弾くから安全です」と説明されていたのが、ここに来ての「機械的チェックで問題無ければ原則署名します、危険なアドオンでもレビュー無しで署名します」への方針転換だったので、そりゃちゃぶ台返しすぎないか?!と驚いて話を鵜呑みにできずにいたからです。

しかし実際に読んでみた限りでは、確かに非公開のアドオンについては署名を完全に自動で得られるようになったものの、その後の人力レビューで弾かれる可能性は否定されていませんでした。「先に全部閉め出しておいて安全な物だけ通す」から「先に全部通しておいて危険な物だけ潰す」への方針転換だったという事になるので、タイミングはずれはするものの危険な物はやはり許容されず、一般ユーザとしては安心して使えて、開発者は依然としてレビューを通過できないことの恐怖に怯えなくてはならないという、大枠の所は変わらないようでした。やれやれ。

system-admin-girl.comのこと - Dec 04, 2015

シス管系女子の特設サイトができました、というか例によって自分で作りました。 3日ほど夜なべして。

どうしてこうなった

電子書籍はいわゆる印税契約だけど紙の方は原稿料買い切り(書籍じゃなくムックだから)なので、プロモーションに工数かけてもあまり得にならないんですよね。なのに何故やったのかというと……要するに、欲しかったんですよ!!! 僕が!!!!!

いやね、連載5年目に入ろうとしてるのにWeb上では相変わらず知名度が低くて、知ってる人は知ってる的な立ち位置がいいかげん辛くなってきたというか、この間なんて「たまたま日経Linuxを見たらこんなの(#!シス管系女子Season3 Petitまとめ読み)あったんだけど、これってもしかしてシェルスクリプトマガジンのシェル女子の便乗企画……?」みたいに思われてしまった、というのは被害妄想もいいとこなんですが、「それもこれもみんな、ここ見れば大体分かるっていう位置付けの公式サイトが無いせいなんや!」と大人げなく嫉妬に狂いまして、手元にあった素材と原稿データをイラレの上で切り貼りしていわゆる1枚ペラのページのこんな妄想画像 を作って「こういうのがほしいんだよこういうのがああああああ!! 日経Linuxのサイトの中に特設ページ作ってもらえませんかね!?」と日経BPサイドに提案してみたものの、会社の方針とかであんまり他のページと違う物は載せられないのでPDF置いとくだけならまぁなんとか……と言われてしまって「そういうことじゃないんだよおおおおお!!!」と血の涙流しながらHTMLとCSSをゴリゴリ書いてさくらのレンタルサーバの一番安いプラン借りてお名前.comでドメイン取って(独自ドメイン取るのこれが初めてですよ! なんと!)突貫工事で作った、というのが真相ですハイ。 説明文が「Piro氏」とか微妙に客観なのは、元が日経BPへの提案用だったからで。

3日でできたのは十数年越しのイメトレのおかげや……

思い返せばかれこれ14~5年は前ですかね? CSSコミューンで偉そうなこと言ってて「Web業界に進みたいな」とか一瞬思ってたあの頃。 CSS2の仕様通りに書いた物をInternet Explorerが微妙にまともにレンダリングしてくれなくて、それでもNetscape Communicator 4での悲惨な対応具合に比べればまだマシというネスケ派の自分にとっては「ギギギ……!」と歯ぎしりせずにはおれない状況で、NC4でもIEでもきちんと表示できてW3C的にも(というかAnother HTML-lint的に?)Validで且つそこそこ凝った見た目を実現して「W3Cの理想は非現実的な絵空事なんかとちゃうんやで!!!」と世界の片隅でアピールしたい!という思いからこんなスタイルシートあんなスタイルシートそんなスタイルシートといったいろんな実験作を書いて粋がってた日々。 当時最もCSS2の実装が進んでたGeckoエンジンでさえできることは全然限られていて、「あぁ……この仕様にある:nth-child(2n-1)ってのが使えれば装飾の左右振り分けも簡単なのに……!」と思いながらclass="even"とかclass="odd"とか書いて騙し騙しやってましたとも。 ドロップシャドウひとつ作るのにも画像を作ってスライスして……よくあんなめんどくさいことやってたもんだ。 しかも人力で。 (素人でお金も無いのでDream WeaverだのFireworksだのは手が出なかった)

素人の僕でこんなんだった訳だけど、当時から業務として手がけておられた方々は僕なんかよりはるかに切実な思いでこういう事と向き合っていたのであろう。 CSS昔話 Advent Calendar 2015 - Adventarにはそういう時期の苦労話がたくさん集まってきそうな気配を感じている。

それが、今じゃどうですか。 文字の影text-shadowボックスの影box-shadow角丸border-radiusも背景色の半透明もグラデーションも奇数番目と偶数番目での振り分け:nth-child(2n-1)も、テキストでちょちょいっと書けば即反映ですよ。 当時は想像もしてなかった、CSSメディアクエリーなんて物もできてるし。 あの頃「こういう風に作れればいいのになあああ!!!」とイメージトレーニングしていた理想のCSS世界がまさに目の前にあるという感慨深さよ。 document.querySelectorAll()で要素をガッと集めてきて制御したり、今画面内にある画像をdocument.elementFromPoint()でダイレクトに取得して位置合わせしたり、Firefoxのアドオン開発でも苦労してた部分があっさりクロスブラウザで動いてくれちゃってて。 FirefoxとChromeのWindows版とAndroid版でだけ検証してリリースしちゃいましたが、後でIE11で見たら全く支障なく完璧に表示されてたし。 あまりのあっけなさに目がテンになり、その後感涙でむせび泣きかねない勢いでした。

SNSでシェアしやすく

……とまあ、昔取った杵柄で一枚ペラ&試し読み簡易マンガビューワーを作るところまではよかったんですが、宣伝のためのサイトなのにSNS連携のシェア用ボタンを入れてなかったり、FacebookやTwitterでシェアされたときにいい感じに画像を出す工夫をしてなかったり、「それやらないの今時あり得ないでしょ……」な手落ちだらけで、「エッなにそれいつの間にそんな事になってたの」と完全に浦島太郎でした。 かろうじてGoogleアナリティクスは存在を知っていたので、それだけは入れてましたが。

皆さんの助けが無ければ、作ったはいいものの結局やっぱり誰にも見られない廃墟サイト化一直線……という末路を辿っていたところでした。 大変お世話になりました。

良い物作ってるつもりでもプロモーションできてなきゃ存在しないのと同じ

思い出話8割に実用情報2割くらいでこんなエントリを書き記して何やってんのって自分でも思いますが、今僕が主たる仕事の場にしてるフリーソフトウェアの世界でも、プロモーションはやっぱ大事だなって思うんですよね。 先行実装があってそれなりに頑張って丁寧に作ってたのに、その存在を知らなかった人が後から作った荒い出来の物が「これ新しい!!! こんなの欲しかった!!!!」って人気をかっさらってって、先達の頑張りが全く誰からも評価されないまま消えていってしまう……俺達は、あと何回そんな悲劇を目にすればいい? 俺はあと何回、顧みられることの無い先駆者を目にすればいいんだ?

というわけで、今後仕事絡みで何か作って公開する時にまた参照したいので自分用のメモとして今回参照した情報をまとめたというのがこのエントリの趣旨なのでした。

まぁ、このsystem-admin-girl.comが実際どれだけ成果が出るかというのは分からない、ともすれば結局やっぱり廃墟になっちゃったねというオチもあり得そうではありますが、「だからこういう風にして欲しかったんだよぉぉおおおお!!」と地団駄踏んで不満溜め込んでるよりは、「思ってたやりたかったとおりの事やったけど駄目でしたわハハハ……」となる方がまだ精神衛生上良さそうなので、これで安心して眠れます。 おやすみなさい。

絵に描く主に女性キャラの身長体重について思ったこと - Oct 12, 2015

萌えキャラの設定体重が軽すぎる、という話を時々目にする。今日見たのは碧志摩メグがらみのやつ

この手の議論で僕の見たことのある非難のポイントはだいたい以下の2つだった。

  • 「この見た目でこの体重は物理的にあり得ない」的な、科学的整合性の問題の話
  • 「こんな体重は不健康。男の欲望のために不健康な細さを女性に押し付けるとはけしからん」的な、社会的にどうあるべきかという話

1つ目の方は、適当にイメージで設定した数字が突拍子もなかったという作り手の落ち度と、絵のリアリティレベルを無視して無粋なツッコミを入れている受け手の落ち度の両方のケースのどっちもあるけど、まあ、結局の所は巨大ロボットの設定重量はどうあるべきかというのと同じ話なんで……

ややこしいのは2つ目の話。これは「フィクションを真に受けるなよ」と言っておしまいにはできない。フランスで、痩せ過ぎのファッションモデルに影響されて一般の女性が無理なダイエットをして健康を害するというのが問題になって、痩せ過ぎのモデルを使ってはならないなんて規制が設けられた事例が実際にある。「女性の理想的なスタイル」をどう表現するかは、製作者や消費者だけの問題とは言えないという面がある事は否定できない。

自分自身、シス管系女子という漫画(の形式の技術記事)を描いてて、意識的・無意識的に女性キャラに自分の好みを反映させている所はあるので、自分の好みを軽々しく表明することが社会的に問題ありと言われると……というか、「おまえそんな女なんか現実におらんのじゃこのクソキモオタ童貞が!!! おまえがゲスな欲を丸出しにするせいで女は迷惑しとんのじゃ!!!」みたいに言われると、申し訳なく思いつつ、しかしカチンとも来る。そんなに悪し様に言われるほど悪いことしてるか?って。

んで思ったんだけど、こういうのって、「見た目」と「体重」という実は関係が薄い物を、作り手も受け手も、関係が深いと誤解してることから悲劇が生まれてる部分があるんじゃないか?って。

ダイエットというか体作りの話で、見た目の体型の良し悪しは身長体重BMIよりも体脂肪率の方が影響が大きい、という話を教えてもらった。検索してみると、そういう記事や画像が結構見つかる。

体脂肪率と体型の対応の例のイラストLifestyleSupportのタイムラインより引用)

見た目の良さは体重やBMIでは測れない。何故か?

  • 見た目の体型が同じでも、身長が高ければ体重は当然重くなる。
  • 筋肉と脂肪は比重が違うから、同じ体重でも筋肉質なら見た目は細く、脂肪ばっかりなら見た目は太くなる。
  • BMIは身長と体重から計算されるから、体重が重ければBMIの値は大きくなり、同じ見た目でも筋肉質ならBMIの値は大きく、脂肪ばっかりなら小さくなる。

じゃあ何を見れば体型の見た目の良さを数値化できるのか? それが体脂肪率だ、という事らしい。

自分が体重を落とした経緯を振り返ったり、周囲の人の事を聞いたりした感じでは、この話は結構信憑性が高いと思ってる。 数字の上で体重だけ減っても、筋肉が無ければ見た目は相変わらず貧弱なままだから、見た目を良くしたいなら筋トレもしなきゃダメなんだなー、と。 で、脂肪が落ちると同時に筋肉が増えてくると、見た目に体が引き締まってきてるのは分かるんだけど体重はあんまり変わらないという、不思議な平衡状態が発生するというのも身をもって体験しまして。

ともかく、見た目の体型は体脂肪率で決まると仮定するなら、キャラの設定は身長と体重ではなく、身長と体脂肪率と筋肉量を記載した方が各方面に誤解が無くて良いんではなかろうか?というのが、僕の提案したい事なのです。

ちなみに、シス管系女子の登場人物達の身長体重は特に設定してないのですが、先の記事にある写真 :体脂肪率21%~30%の例として挙げられている物女性が3ヶ月で腹筋を割る方法 | スチームパンク大百科Sより引用) で言うと、みんとちゃんは体脂肪率21%くらいとされている写真、大野先輩は26%くらいとされている写真がイメージに近くて、筋肉量については2人ともそんなに筋肉質ではないだろう……と思ってます。

(みんとちゃん) (大野先輩)

僕自身の好みで痩せ型の想定なのは否定できないけど、このくらいの体脂肪率ならあり得ないほど不健康に細いってことはないんじゃないでしょうか?

――ということを書いていたらこういう不思議ちゃんキャラのような設定にしとけばいいんでは? というご意見を頂いたのですが、これ、ボケ方にめっちゃセンスが要求される奴や……!


ちなみにこれ関連の話題としては、進撃の巨人のキャラクターの身長体重がある。 Wikipediaの記事には、一部出典不明だが以下の通り記載されている。

  • エレン 身長170cm, 体重63kg
  • ミカサ 身長170cm, 体重68kg
  • リヴァイ 身長160cm, 体重65kg

彼らは極限まで脂肪を削ぎ落とされた筋肉質の鋼の肉体に尋常じゃない密度の骨を併せ持っているから体重が重いのだ、ということだそうで。(作者の諫山氏が格闘技ファンであることも影響していそう)

Casual cooperation of addons - Aug 23, 2015

This is a reply to the blog entry: Firefox Add-on Changes | Bill McCloskey's Blog

Hello, I'm the author of the listed addon Tree Style Tab.

You mean that we can rebuild Tree Style Tab with the sidebar API. However, I think that you don't find out what is the essence of TST.

I believe that TST's unique value is a natural compatibility for other tab-related addons, and sidebar API based TST loses the value.

If I rebuild TST using such a sidebar API, it can probably provide tree-like GUI in a sandboxed frame, working instead of Firefox's native tabs. But, probably other addons can't cooperate with it, because most addons' authors think about only their addons and Firefox's native tabs. Even if new TST became available based on the sidebar API which can't cooperate with other addons, it's useless. Moreover, then I'll receive more and more requests for the sidebared TST, like: Please add the feature provided by another something major tab-related addon, like "copy tab title"!

On the other hand, current TST changes just the appearance of Firefox's native tabs, so other tab related addons also work with it seamlessly even if they don't know TST. If an addon provide a new menuitem "copy tab title" in the context menu on tabs, it is also available even if TST is installed, because TST's "tree item" is truly Firefox's native tab. I mean that this is TST's natural compatibility for other addons. I think this value is never available with isolated addons based on sandboxed sidebar. Like the UNIX strategy, one addon should not include too much features, because one author only can do a few work. Instead, one addon should cooperate with others casually. I think this is why Firefox is loved by many power users.

The reason, why TST seems unstable and fragile, is that there are too less entry points for addons which work on Firefox's low level layer. Because we never can insert our custom operations to Firefox's features (like dragging of tabs, bookmarking of webpages, etc.), we have to replace it entirely or rewrite internal functions. If more stable entry points become available, TST will be more safe, stable, and compatible with other addons.

Anyway, my conclusion is: I believe that low-level extensibility for addons should be kept, even if new extension APIs become landed. Extensibility based only on isolated APIs will kill addons' casual cooperation.

Thanks.

One addition.

If addons based on new APIs can cooperate with others casually around Firefox's native features like current TST, I passively agree to the decision of killing low level extensibility for legacy addons. Basically Firefox should be more safe and stable for non-power users - I think so. If new APIs truly can build addons which cooperate with others seamlessly with enough rich entry points, then I'll have no reason to negate migration from legacy way to the new way - except that it is a hard work.

ラズパイにCPUパワー使う処理はやらせるもんじゃない(バックアップの試みの失敗について) - Aug 13, 2015

Raspberry Piと外付けHDDでNASっぽい運用をしようとしてた件だけど、使い回そうとしてたバルクのHDD(SATA→USBの変換アダプタで外付け化した)が壊れたのか元から初期不良だったのか全然安定しなくて、結局、バッファロー製の外付けHDDとして売られてる製品(バックアップ領域用に使ってるのと同じ3TBのモデル)を買い足してそっちをメインに使う事にした。バルクのHDDはWindowsに繋いでもOS巻き込んで落ちる惨状なので、全く使えない。1万円強を無駄にしてしまった……落ち込むわ……

ともかく、やっと安定して動くようになってきたので満を持して duplicityを使って差分バックアップをやってみたんだけど、1TB未満のフルバックアップで5788分……丸4日、その直後の差分バックアップでも3411分……2日半ほどかかってしまった。duplicityは何やら色々凝ったことをしているっぽく、ただのI/O待ちのせいというわけでもないようで、ほんとにCPU時間がそれだけかかってた。Raspberry Piは低消費電力が売りのCPUでその分計算能力はそれほど高いわけではないっぽいというのはぼんやりと把握してたけど、ここまでかかるとは正直想像してなかった。これでは毎日の自動バックアップには使えない。

というわけで、どうしたものかと悩んでいる。rsyncでもミラーリング先をローテーションすれば最大2世代のバックアップは取っておけそうなんだけど。メインのHDDの使用率が50%を超えてしまったら複数世代バックアップはできなくなるなあ……と思うと、それで行こう!と割り切れずにいる。

duplicityを使ったバックアップを支援するスクリプト(実験中) - Jul 05, 2015

差分バックアップをやりたくて、でも自分であれこれ考えて作るのしんどいわって思って、UbuntuデフォルトのバックアップツールのDeja Dupを試してみてたんだけど、こいつはデスクトップ環境にログインしてないと動かないという制限がある。

Deja Dup自体はduplicityというコマンドラインツールのフロントエンドに過ぎないのでそっちを直接実行すればいいらしい、という事までは知ってたんだけど、なんかオプションがめっちゃ多くてやる気萎える感じだったので、諦めてしまってた。

ラズパイを使ったファイルサーバーでも、バックアップ態勢を確立するにあたってまたDeja Dup頼みかなあと思いながら試してみてたんだけど、なにげなくtopの画面眺めてたらduplicityのコマンド列(Deja Dupがいい感じにオプション指定を組み立てた状態の物)が出ていて、なんだこれコピペすればいいんじゃん!と思って、それを足がかりにして簡単なスクリプトを書いてみた。

#!/bin/bash

# バックアップの保存先
destination=/mnt/backup-external/backup
# バックアップに含める物、含めない物
includes="/root /etc /mnt/main-external"
excludes="/root/.cache /mnt/main-external/home/piro/.cache"

# アクセス権がらみのトラブルを避けるために、root以外での実行は禁止する
if [ "$EUID" != "0" ]; then
  echo "You must run this script as the root."
  exit 1
fi

# root直下でバックアップ対象以外のディレクトリはバックアップ対象から除外
for dir in /*
do
  if echo " $includes " | grep -v " $dir "
  then
    excludes="$excludes $dir"
  fi
done

echo "includes: $includes"
echo "excludes: $excludes"

mkdir -p $destination/archive
mkdir -p $destination/data

# duplicityのオプションを組み立てる

include_options=""
for include_target in $includes
do
  include_options="$include_options --include=$include_target"
done

exclude_options=""
for exclude_target in $excludes
do
  exclude_options="$exclude_options --exclude=$exclude_target"
done

other_options="--gio --volsize=50 --no-encryption --verbosity=9 --gpg-options=--no-use-agent --tempdir=/tmp \
  --archive-dir=$destination/archive \
  / file://$destination/data"

# 差分実行
time duplicity incremental $include_options $exclude_options $other_options

# 失敗したら、フルバックアップにフォールバック
if [ $? != 0 ]
then
  time duplicity full $include_options $exclude_options $other_options
fi

うまくいったら、rootのcronjobに指定しておこうと思う。

Raspberry Pi 2にUbuntu(Lubuntu)を入れて、USB接続のHDDと組み合わせてファイルサーバーとして運用する - Jul 04, 2015

先日HDDを交換したばかりだった自宅サーバ機が、起動しなくなった。 起動してもすぐに電源が切れるという状態で、使い物にならない。

HDD以外のハードウェアは少なくとも5~6年は経過しているので、寿命と思って新調する事にした。 それに伴って、GitHubのService Hookなどの処理はVPSあたりに移動して、自宅に置くのは完全にストレージ専用の物にする事にした。

で、どうするかなんだけど、せっかくついこの間買ったばかりのHDDなのだから、ここが故障したわけでないのなら流用したい。 でも実質的にファイルサーバーとしてしか使わないんだから、そのためだけにPCを導入するというのも何だか勿体ない気がする。 あれ、そういえばラズパイって5000円くらいで買えてUSBで色々繋げられてLinuxが動くんだよね? じゃあこれに既存のHDD繋いでファイルサーバーにしたらいいんじゃね?

というわけで、漫画を連載させて頂いている日経Linux誌でずっと前から特集や連載がいっぱい載ってるにも関わらず完全にスルーしていたRaspberry Piを、今更ながら自分でも導入してみることにしました。

事前に仕入れた&知ってた情報によると、Raspberry PiというのはARMプロセッサを搭載して1ボードで完結してるLinuxマシンで、DebianベースのディストリビューションのRaspbianを使うのが通常の使い方らしいけど、今出回ってるRaspberry Pi 2 Model BではUbuntuの動作実績もあると。 であれば、今まで運用してたサーバ機(Ubuntu)と似た感じで使えるのではないか? という予想ができる。 もしうまくいかなくても、値段が値段だから大して痛くはないだろう。 というのが、導入を後押しした感じです。

必要な物を買い揃える

必要な物は全部Amazonで買えた。

まず、何はなくとも本体。Ubuntuの動作実績があるのはRaspberry Pi 2 Model Bとのことなので、他と間違えないように気をつけないといけない。 Amazonで買える物は裸の基盤のままではなくケース付きなので、見苦しくなくて良いですね。

先日買ったHDDを外付けするための、ケースとアダプター。 SATAをUSB接続に変換して、HDD自体はほぼ裸で置くという感じ。

グリーンハウス 3.5インチHD用シリコンケース ブラック GH-CA-HD35K
グリーンハウス (2007-06-20)
売り上げランキング: 3,546

HDMIケーブル。 今までのサーバ機はD-sub 15pinでテレビに繋いでたけど、Raspberry Piには映像出力はHDMI端子しかないので。

マウスとキーボードは手元にある物を使った。 Raspberry Pi本体にはUSB端子が4つあるので、外付けHDDに2つ使って、キーボードとマウスで1つずつで埋まった。 (多分普段はSSHで操作するから、キーボードとマウスは必要な時だけ挿せばいいんだけど。)

Raspberry Pi本体にはmicroSDのスロットがあって、これがシステム領域のストレージになるんだけど、たまたま手元に32GBの物があったのでそれを使う事にした。 持ってないなら、これも買っておかないといけない。 後述するUbuntuのイメージを使うなら4GB以上の大きさは必要だそうです。

OSの基本的なセットアップ

このあたりの手順は、他の用途でも共通して使えそうな気がするので、ここだけ読んでも有用かも。

起動イメージの用意

有志の人が作った起動イメージがUbuntu Wikiで公開されているので、ダウンロードしてきて、Win32 Disk ImagerでmicroSDに書き込んだ。

書き込み完了した物をRaspberry Piに挿して電源を繋ぐと、ユーザ名がubuntu、パスワードがubuntuとなっているアカウントが1つだけある状態のUbuntuが(デスクトップ環境無しで)起動する。 しばらくはこれで作業することになる。

ちなみに、何かやらかして起動不可能になってしまった時(/etc/fstabにミスがあった、とか)は、最悪の場合ここからやり直すことになる。 僕は2回やり直しました。

リモート操作の準備

起動できたら、SSHで接続できるようにしておくと後が楽。 上記のイメージにはOpenSSHサーバが入っていないので、自分でインストールする必要がある。

$ sudo apt-get update
$ sudo apt-get install ssh

そうしたら、DHCPで割り当てられたこのサーバ自身のIPアドレスを調べておく。

$ ifconfig

DHCPで割り当てられたIPアドレスを確認したら、作業用の別環境から接続する。 そうすれば、その作業環境でインターネットを検索しつつ、紹介されていたコマンドラインをコピペで実行するのが容易になる。

ディスク領域の拡大

前述のUbuntu Wikiのページに書かれている通りの手順で、パーティションを修正してmicroSDの全領域を使えるようにする。

$ sudo fdisk /dev/mmcblk0

2番目のパーティションを削除して、領域を作り直し(d, Enter, 2, Enter, n, Enter, p, Enter, 2, Enter, Enter, Enter, w, Enter)、 パーティションを編集したら、再起動する。

$ sudo reboot now

2番目のパーティションを最大サイズに拡張する。

$ sudo resize2fs /dev/mmcblk0p2

既定のエディタの変更

ここから先、ターミナルの操作でファイルを編集することが多いんだけど、nanoの操作よりもvimの操作の方が自分は慣れているので、vimをデフォルトのエディタにした。 ついでに、SSH越しの接続を中断したり複数タブを開いたりという感じの事ができるように、tmuxも入れた。

$ sudo apt-get install vim tmux
$ sudo update-alternatives --config editor

日本語で使えるデスクトップ環境の導入

先のイメージで起動した環境は、必要最小限のソフトウェアしか入っておらず(OpenSSHサーバすら入ってない)、GUIで操作したかったら必要な物を別途入れないといけない。 先程、microSDのパーティションを編集して使える領域を広げたのは、それが理由。

ここから先は、技評のサイトのいくやさんの記事を参考に、日本語環境用の設定とデスクトップ環境のインストールを行う。

まず、既定の状態で設定されているファイルのダウンロード元は海外のサーバで、必要なファイル群のダウンロードにメチャクチャ時間がかかってしまうので、日本のミラーサーバからファイルをダウンロードするように変更する。 (自分が最初にやった時は、既定の状態だとファイルのダウンロードだけで5倍くらいは時間がかかった気がする)

$ sudo vim /etc/apt/sources.list

ファイルの内容を全削除して、以下のように書き換えてしまう。

deb http://jp.archive.ubuntu.com/ports/ trusty main restricted universe multiverse
#deb-src http://jp.archive.ubuntu.com/ubuntu/ trusty main restricted universe multiverse

deb http://jp.archive.ubuntu.com/ports/ trusty-security main restricted universe multiverse
#deb-src http://jp.archive.ubuntu.com/ubuntu/ trusty-security main restricted universe multiverse

deb http://jp.archive.ubuntu.com/ports/ trusty-updates restricted main multiverse universe
#deb-src http://jp.archive.ubuntu.com/ubuntu/ trusty-updates restricted main multiverse universe

deb http://jp.archive.ubuntu.com/ports/ trusty-backports restricted main multiverse universe
#deb-src http://jp.archive.ubuntu.com/ubuntu/ trusty-backports restricted main multiverse universe

そうしたら、早いとこ安全な状態にするために、インストール済みのパッケージを更新しておく。

$ sudo apt-get update && sudo apt-get upgrade

一息ついたら、Lubuntuのデスクトップ環境(LXDEを使っている。XfceのXubuntuより軽いそうなので。)をインストールする。

$ sudo apt-get install lubuntu-desktop xserver-xorg-video-fbturbo fonts-takao language-pack-ja language-pack-gnome-ja ibus-mozc dphys-swapfile

デスクトップ環境が入ったら、プロプライエタリのドライバ用の設定を作る。

$ sudo vim /etc/X11/xorg.conf

ファイルが無ければ新規に作成する。内容はいずれにせよ以下の通りにする。

Section "Device"
    Identifier "Raspberry Pi FBDEV"
    Driver "fbturbo"
    Option "fbdev" "/dev/fb0"
    Option "SwapbuffersWait" "true"
EndSection

日本語を既定の言語にする。

$ echo "Asia/Tokyo" | sudo tee /etc/timezone
$ sudo dpkg-reconfigure -f noninteractive tzdata
$ sudo locale-gen ja_JP.UTF-8
$ sudo dpkg-reconfigure -f noninteractive locales
$ echo "LANG=ja_JP.UTF-8" | sudo tee /etc/default/locale

日本語キーボードのレイアウトをデフォルトにする。

$ sudo vim /etc/default/keyboard

設定ファイルが開かれるので、以下のように変更する。

- XKBLAYOUT="us"
+ XKBLAYOUT="jp"

終わったら、ファイルを保存して、設定を更新する。

$ sudo dpkg-reconfigure -f noninteractive keyboard-configuration

普段使いのユーザを作る

デスクトップ左下のボタンから開けるメニューを辿ってユーザーアカウントの管理画面を開き、普段使いの名前でユーザーを作る。 僕の場合はpiroで作った。

ユーザの作成後は、詳細設定で適切な権限を与えた上で、グループの編集でsudosambashareグループに参加させるsudoグループに入れるのを忘れると後で詰むので、これは絶対に忘れないように。

sudoできるようになり、GUIから特権を必要とする操作をしようとしたらパスワードの入力を求められる事を確認できたら、初期ユーザだったubuntuユーザは削除しておく。

このあたりで、鍵認証を使っているのであれば公開鍵の登録をやっておく。 その後、sshdの設定を変えてパスワード認証を禁止しておく。

$ sudo vim /etc/ssh/sshd_config

以下の点だけ編集すれば、とりあえずはOK。

PermitRootLogin no # sshから直接rootでログインするのを禁止
PubkeyAuthentication yes # 鍵認証を有効化
PasswordAuthentication no # パスワード認証を無効化

設定の変更後は、デーモンを再起動しておくこと。

$ sudo service sshd restart

固定IPにする

サーバ機として使うには、DHCPでIPアドレスが頻繁に変わられてしまうと不便なので、固定IPにしておく。

$ sudo vim /etc/network/interfaces

後半のeth0の自動設定を定義している2行をコメントアウトし、以下の要領で設定を追加する(ネットワークが192.168.0.0であると仮定)。

auto eth0
iface eth0 inet static
address 192.168.0.100
network 192.168.0.0
netmask 255.255.255.0
broadcast 192.168.0.255
gateway 192.168.0.1
dns-nameservers 192.168.0.1

ネットワークの新しい設定は、再起動したらそれ以後は反映される。

$ sudo reboot now

ファイルサーバにするための設定

USB接続のHDDを常に自動マウントする

USB接続のHDDは、デスクトップ環境にログインするとマウントされるんだけど、ログインしてなくてもマウントされてて欲しい。ユーザのホームみたいにそれなりに多くのファイルが置かれる物は、外付けHDDの方に置いておきたいし。

まずはマウントポイントを作る。 バックアップの保存先のディスクと、普段使う方のディスクとで、2箇所マウントしたので2つ用意した。

$ sudo mkdir -p /mnt/main-external
$ sudo mkdir -p /mnt/backup-external

次に、外付けHDDを繋いで、各パーティションのUUIDを調べる。

$ sudo blkid
/dev/mmcblk0p1: SEC_TYPE="msdos" UUID="AB3E-B34D" TYPE="vfat" 
/dev/mmcblk0p2: UUID="3aee2e0f-21f9-43c8-a4d3-e864f5d72d37" TYPE="ext4" 
/dev/sda1: LABEL="backup-external" UUID="2fdef27e-8ad9-4f44-a46f-5799e5143489" TYPE="ext4" 
/dev/sdb2: UUID="bc802910-f850-4685-90a4-9034d5d2d931" TYPE="ext4" 
/dev/sdb3: UUID="d710499f-1ab6-4c63-98b7-c06bc08d501a" TYPE="swap"

/dev/sda1がバックアップの保存先として使っていたディスクで、/dev/sdb2は旧マシンのシステムのrootになってたパーティション。 ここで表示されたUUIDを参照しつつ、/etc/fstabに自動マウント用の設定を書く。

$ sudo vim /etc/fstab

追記する内容は以下の要領。

UUID=bc802910-f850-4685-90a4-9034d5d2d931 /mnt/main-external ext4 defaults 0 0
UUID=2fdef27e-8ad9-4f44-a46f-5799e5143489 /mnt/backup-external ext4 defaults 0 0

指定が正しいか、実際にマウントして確認する。

$ sudo mount -a

僕はここでちゃんとマウントできない状態のまま再起動したせいで、起動中にエラーで止まってしまって、リカバリが効かなくてイメージの書き込みからやり直しになってしまった。

スワップ領域の割り当て変更

ここまでの手順の中で、ディスク上の専用パーティションではなく、ファイルをスワップ領域に使うようになっている。

が、microSDに頻繁に読み書きが行われるのはなんとなく寿命を縮めそうで怖い。 実測値的には半永久的に使えるレベルらしいので、今時こんな事怖がるのはおじいちゃんもいいとこなのかも知れないけど。

そういう気持ちの問題と、あとHDDの方にスワップ領域がそのまま残ってて、使わないのも勿体ないかなと思って、HDD上にあるスワップ領域を使うように設定し直すことにした。

$ sudo vim /etc/fstab

UUIDを参照しつつ、以下の内容を追記する。

UUID=d710499f-1ab6-4c63-98b7-c06bc08d501a swap swap defaults 0 0

ファイルへのスワップ領域の割り当てを無効にする。

$ sudo dphys-swapfile swapoff
$ sudo apt-get remove dphys-swapfile

ファイルの所有権の変更

旧環境は自分で普通にインストールしたUbuntuだったんだけど、Ubuntuのインストールウィザードの中で作成した初期ユーザはuidが1000になる。 しかし、上記イメージからセットアップしたUbuntuではuid 1000ubuntuという名前のユーザに使われていて、新たに作成した普段使い用のユーザはuid 1001となる。 なので、旧マシンのHDDをそのまま使いまわす場合、所有者がズレて認識されてしまう。 これを修正するために、以下の要領で所有者を再設定する。

$ wrongr=1000
$ correct=piro
$ sudo find /mnt/main-external -user $wrong | while read file; do echo "$file"; sudo chown $correct:$correct "$file"; done

xargsとか使った方が高速なんだろうけど、ミスしてた時に途中で止めやすいかと思って、この時はこうした。

homeその他の置き換え

microSDの上に色々ファイルを増やしていくと、すぐにディスクが一杯になるのが目に見えている。 なので、基本的にファイルの保存先の実態は外付けのHDDとしておいて、適宜シンボリックリンクを作って使う、というスタイルを取ることにした。

$ for dir in "/home/piro" "/opt" "/var/shared"; do if [ -d $dir ]; then sudo mv $dir ${dir}_; fi; sudo ln -s /mnt/main-external$dir $dir; done

ホームも実態を外付けHDDに置いておく……というか元のシステムでホームにしていたディレクトリをそのまま使うようにするわけだけど、前項で述べた通り外付けHDD(=旧マシンで使っていたディスク)のファイルは所有者の情報がズレているので、前項の手順で所有者情報を直しておくのを忘れないように。

ただ、さすがにrootのホームまで シンボリックリンクにするのは怖かったので、こちらはファイルをコピーするだけに留めた。

$ sudo su
# cp -r /mnt/main-external/root/* /root/

sambaのファイル共有設定

ファイル共有のために、Sambaをインストールする。

$ sudo apt-get install samba

Sambaユーザーを作成して、パスワードを設定しておく。

$ sudo smbpasswd -a piro

次に、共有したいフォルダの設定を行う。 普通のUbuntuだとGUIから設定できるんだけど、Lubuntuのデスクトップ環境だとそれができないようだったので、自分でsmb.confを編集した。

$ sudo vim /etc/samba/smb.conf

共有したいフォルダごとのセクションを以下の要領で書いた。

[shared]
path = /mnt/main-external/var/shared
guest ok = no
read only = no
writable = yes
create mask = 0666
directory mask = 0777
browseable = yes
valid users = @sambashare

[piro-shared]
path = /mnt/main-external/home/piro/shared
guest ok = no
read only = no
writable = yes
create mask = 0600
directory mask = 0700
browseable = yes
valid users = piro

僕はシンボリックリンクを多用するので、便利なように、[global]セクションに以下を追記してシンボリックリンクを辿るようにした。

wide links = yes
unix extensions  = no

バックアップの体制を整える

旧環境では/backupにマウントした外付けHDDにファイルのバックアップを保存するようにしてたので、新しい環境でも同じ事をやることにした。 まずは、Ubuntuの「バックアップ」の実態であるDeja Dupをインストールする。

$ sudo apt-get deja-dup

これはまだエントリを書いてなかったんだけど、Deja Dupを使ってシステム全体や複数ユーザのホームをまとめてバックアップするために、僕はrootでDeja Dupを起動していた。

$ gksu deja-dup-preference

ここまでの過程で旧環境から/root以下の内容を引き継いでいるので、きちんとファイルをコピーできていれば、バックアップ先の指定などがそのまま読み込まれる。 とりあえずテストとして、バックアップを実行してみた。 バックアップ対象(ホームや共有ディレクトリ)が今の環境ではシンボリックリンクになっているので、ちゃんとバックアップできるか心配だったんだけど、処理が進行中の時のステータス表示を見てみた限りでは、シンボリックリンクの先を辿ってバックアップしてくれているようだった。

なんやかやで、Deja Dupではなくその後ろにいるduplicityを使うようにしようと実験中です。

おわりに

ということで、Raspberry Piと旧マシンから引き継いだHDDを組み合わせてファイル共有サーバを立ててみました。

本物のNAS製品に比べると性能だったり使い勝手だったりの面でかなわないとは思うんだけど、普通のPCをサーバにするのに比べれば場所を取らないし静か(なにせRaspberry Piには電源やCPUクーラーのようなファンが無い)だし、クラウド上のVPSの上にあるデータの自動バックアップなんかもcronjobでできるし、チャレンジがてら皆さんもお試しになってみてはいかがでしょうか。

片付いた部屋が苦手なのではなく、変化のための努力や、変化そのものが苦手なんじゃないのかということ - Jun 28, 2015

最近、10年近く使っていた使い勝手が微妙な机を処分して、別の机に入れ換えた。 (写真:入れ換え後の様子)

元の状態と比べて、手前への張り出しが減ったので開放感が増した。 サブディスプレイの方を、妻所有だけど今は使ってなかった物(ベゼルが細くて画面が大きくて、メインで使ってる物と仕様が似てる)に置き換えたので、普段の画面が広くなった。 作画はCintiq Companion Hybridでやってるから、作業時の状況にはほとんど影響ないんだけど。参考資料をたくさん出しておけるというくらいか。

スピーカーの置き場所はちょっと微妙になった。もっと小さいのにすればいいんだけど、これはそれなりの重さがあって落ち着いた音が出るので、高校生の頃からずっと使ってて、替えるならこれより音がいい物でないと嫌なので、このまま行こうと思ってるけど。

僕は部屋を片付けるのが苦手で、増えてきた漫画が本棚から溢れてしまったり、洗濯が終わった衣類をなかなかしまわなかったりで、妻に嫌な思いをさせがちだ。

でも、汚部屋が好きというわけでは決してない。汚部屋でもそこまで気にはならない、というのはあるかも知れないけど。

世の中には、片付いてない部屋の方が好きだとか、コタツに入ったまま必要な物全てに手が届くようにしてるのだとか、そういう人もいるみたいなんだけど、僕が自分を省みる限りは、そういうのとも違うように思う。 物が出っぱなしになっているのが、特別に便利なようになっているというわけでもないし。 汚いよりは綺麗な方がいいというのは、素直にそう思うし。

僕が苦手なのは、大きくて急激な変化なのだと思う。 毎日少しずつ変わるのは気にならない……どころか、気がつかない、鈍い。 だから、少しずつ本が溢れて、少しずつ洗濯物が溢れて……というのが続いてもそれを意識できていなくて、気がついたら本が本棚の横にうずたかく積み重ねられたり、衣類収納の中が空っぽで乾いた洗濯物の中からパンツを取り出してはいたり、そういう事になってしまうんだと思う。 そういう状態に改めて言及されると、「良くないなあ」というのは分かるし、元々自分でもうっすら「これ、良くないよなあ、なんとかしないとなぁ」と思ってはいるんだけど、その状況を改善するための大がかりな労力を割く事にどうしても抵抗を感じてしまって。

まあ、そういうのが億劫なのは誰もがそうなのかも知れないけど。 「自閉症スペクトラムの特徴の1つに、慣れた環境から変わる事を極端に嫌がるという点がある」という話を聞きかじって、それに自分を当てはめて考える事で、「片付けられないのはそういうビョーキのせいなんだ」と責任逃れをしてるだけなのかも知れないけど。

Page 5/239: « 1 2 3 4 5 6 7 8 9 »

Powered by blosxom 2.0 + starter kit
Home

カテゴリ一覧

過去の記事

1999.2~2005.8

最近のつぶやき

オススメ

Mozilla Firefox ブラウザ無料ダウンロード