Exim4でメールアドレスを量産する
EメールアドレスをGMailのようにサフィックスを付けても通るようにしたかったのでちょっとやってみました。
GMailのように '+' を使うルールだとこれを弾くサービスが増えてきたので、エイリアスの区切りには怪しくないように '.' を使うことにします。例えば
user@hoge.com user.fuga@hoge.com user.fuga.piyo@hoge.com
というメールアドレスはぜんぶ user 宛に届くようにします。
以下はDebian Wheezyでの方法です。Exim4を選んだのはDebianを入れると標準でインストールされるから。
Exim4の設定
前提として、インターネット上からメールを受け取れるようにする設定は既に済ませてあるものとします。
それに加えて /etc/exim4/conf.d/router/ 以下に 399_exim4-config_dot_alias を作成するだけです
dot_aliases: driver = redirect condition = ${if match{$local_part}{\.} {true} {}} data = ${sg {$local_part}{\N\..*$\N}{}}@$domain
これで /etc/init.d/exim4 restart すれば完了。エイリアスが欲しいだけならこれで終わりです。
振り分け
本当はこっちがやりたかった事。メールアドレスの量産の他に、自動的に規則的な階層を持つような振り分けをしたかったので以下のようにしました。
方針
振り分けルール以外は一般的なMaildir形式の設定をするだけ。
エイリアスに '.' を選んだのは、RFCのメールアドレスでの '.' の扱い方の法則がDovecotにおけるMaildir++の階層の表現方法にとって都合が良く扱いやすかったからというのもあります。
Exim4
Maildir方式に変更。
/etc/exim4/update-exim4.conf.conf の dc_localdelivery= を変更
dc_localdelivery='maildir_home'
Dovecot
dovecot-imapdをインストール。
Maildir形式への変更。
/etc/dovecot/conf.d/10-mail.conf の mail_location の行
mail_location = maildir:~/Maildir
シェルスクリプトで乱数を扱う
シェル組込変数 RANDOM
bashやzshなど一部のシェルでは特殊な変数 RANDOM があり、参照する毎に範囲の小さい整数を得ることが出来ます。
$ echo $RANDOM 30940 $ echo $((RANDOM % 100)) 22
jot(1)
jot(1)は連番や乱数を出力するプログラムです。出力回数や範囲を指定出来るのでなかなか使い出はあります。deb系では athena-jot パッケージをインストールします。
$ jot -r 1 10 99 72 $ jot -r 10 1000 9999 8881 2834 3537 5889 7430 9962 4905 9548 3575 9762
awk
awkの関数 rand() で0–1の範囲の実数の乱数を扱えます。srand() で乱数の初期化することを忘れずに。
$ awk 'BEGIN{srand();print int(rand() * 100)}' 21
/dev/urandom
urandomデバイスを読むことで文字通りランダムなバイト列を得ることが出来ます。そのままでは扱えないのでodで出力を加工します。
これで得られる整数は範囲が2の冪乗になるので、必要なら剰余を取るなどしてさらに加工します。$(()) を使った数値計算はPOSIX標準で使えるのでexprよりはこれを使う事をおすすめします。
$ od -vAn -tu2 -N2 </dev/urandom 22541 $ echo $((`od -vAn -tu2 -N2 </dev/urandom` % 1000)) 104 $ od -vAn -tu4 -N16 </dev/urandom 495822970 28206112 1470428861 335315840 $ ( for val in `od -vAn -tu1 -N8 </dev/urandom` ; do echo $((val % 100)) ; done ) 48 0 75 37 55 24 87 50 $ od -vAn -tx1 -N16 </dev/urandom |tr -d '[:space:]' |sed -e 'a\' 722b6a021c13873512c5587b176e4b63 $ dd if=/dev/urandom bs=24 count=1 2>/dev/null |base64 SxaXbo5BP/El3MWMhaFD/42k0N1g00gc
rand(1ssl)
opensslでもランダムなバイト列を生成することが出来、/dev/urandom の代わりに使えます。
普通このコマンドは -base64 オプションと合わせてナンス生成等に使う事が多いと思います。
$ openssl rand 4 |od -vAn -tu4 3671196097 $ openssl rand -base64 24 TqJAtoQ6Xlho56DvnnEkuV29mqKz8mQb
shuf(1)
shufは入力された行をランダムに入れ替えて出力します。乱数とは少し毛色が違いますが一応。
$ shuf <<heredoc > aaa > bbb > ccc > ddd > eee > fff > heredoc fff eee ccc bbb aaa ddd
Linuxでデスクトップをライブ配信する
Ustreamやニコニコ生放送でLinuxのデスクトップを配信するのに必要な物・方法のメモです。
必要な物を用意する手順はDebian Lennyでの環境を元に書いてありますので、適宜読み替えて下さい。
必要な物
ffmpeg
ffmpegでデスクトップのキャプチャが出来ます。
$ ffmpeg -version
してffmpegの情報を表示し、ビルド設定に
--enable-x11grab
が含まれていればキャプチャ機能が有効になっています。無い場合はこのオプションを付けてffmpegの再ビルドが必要です。debian-multimediaのffmpegでは有効になっています。
mjpegtools_yuv_to_v4l
ffmpegでキャプチャしたデータをvloopbackが受け取れるように変換するために使います。
ソースを http://panteltje.com/panteltje/mcamip/mjpegtools_yuv_to_v4l-0.2.tgz からダウンロードし、そのままビルド・インストールします。ビルド前にはMakefileのCFLAGSを編集して最適化オプションなどを変更すると良いでしょう。
vloopbackカーネルモジュール
vloopbackカーネルモジュールはキャプチャソフトからデータを受け取り、それをビデオデバイスとして出力させるために必要です。
ソースはvloopback-sourceとしてパッケージにまとめられています。Lennyには無いのでパッケージを直接ダウンロード・またはsid等から借りてきてこれをインストールします。
ソースをインストールしたら
# m-a a-i vloopback
でモジュールのビルド・インストールが完了します。
配信手順
まずはvloopbackをカーネルに追加してビデオデバイスを作成します。
# modprobe vloopback pipes=1
dmesgでvloopbackが作成したデバイスを確認します。
[23979.234319] [vloopback_init] : video4linux loopback driver v1.3 [23979.241036] [vloopback_init] : Loopback 0 registered, input: video0, output: video1 [23979.241045] [vloopback_init] : Loopback 0 , Using 2 buffers
この例では /dev/video0 が入力、/dev/video1 が出力として作成されています。
次に、デスクトップをキャプチャしてデータをデバイスの入力に送ります。
$ ffmpeg -f x11grab -s 512x384 -r 10 -i :0.0+100,200 -pix_fmt yuv420p -f yuv4mpegpipe - \ |mjpegtools_yuv_to_v4l /dev/video0
-f x11grab は入力にXの画面のキャプチャを使うことを意味します。-i で画面とキャプチャ位置オフセットを、-s でキャプチャサイズを指定します。キャプチャデータは mjpegtools_yuv_to_v4l で変換してビデオデバイスに流しています。他のオプションや詳細についてはffmpegのドキュメントを参照して下さい。
これで今 /dev/video1 を他のウェブカムと同じようにビデオデバイスとして扱えるようになりました。
/dev/video1 の出力の確認のためにビデオデバイスを表示するソフトを起動してみます。ここでは xawtv を使います。
$ xawtv -geometry 512x384 -c /dev/video1
これでデスクトップが表示されていればOKです。
xargs で -0 を使わずに find の出力を渡す
POSIXの xargs には -0 オプションがないので find で得たファイル名に空白などがあった場合に面倒が生じます。そのため、find の出力を加工します。
註: あまり試してない
$ find .//. -print \ |sed -n ':loop;s/./\\&/g;${p;q};x;n;/\/\//!{x;s/.*/&\\/;x};x;p;x;b loop' \ |xargs -E "" command...
「find ... -print0 |xargs -0」の時と変えることは、パスの先頭に「.//」を付けること、find と xargs の間に sed スクリプトを挟むことです。
find が出力するディレクトリの区切りは「/」1個だけというのを利用し、「今の行に『//』が含まれていない場合は前の行の続きだから前の行の最後に『\』をつける」という処理を施しています。
処理は http://programming.itags.org/unix-linux-programming/82599/ の stephane_chazelas 氏の投稿を参考にしました。awk が使われていますが、ここでしていることと同じです。
シェルスクリプトでエポック秒から日時を得る
$ date -d "$((`date +%s` - 1234567890)) seconds ago"
ただし、ここでの date は GNU core utilities 由来の物です。
xargs で空白・改行・クオーテーションが入った引数を安全に渡す
xargs で空白が入った引数を渡す場合、「'」か「"」で引数を囲む必要があります。しかし、これらでくくった場合のエスケープシーケンスが全く提供されていないため、引数にさらに同じクオーテーションが含まれていた場合に問題があります。また、改行を入れることも出来ません。
多くのシステムでは、引数の区切りにヌル文字を使い、xargs に -0 オプションを付けてファイルを渡すことで解決する方法がありますが、POSIX標準ではこの方法を使うことが出来ません。
「'」か「"」でくくらない場合、「\」の後の文字(改行含む)はそのまま扱う事が出来ます。これを利用し、引数の文字全てと行末に「\」を付け、最後の行の「\」を取り除くという操作をすることで表題を解決出来ます。
printf "%s\n" "$arg" |sed 's/./\\&/g;s/.*/&\\/;$s/\\$//' >>args
ただし、この方法では空文字列を渡すことが出来ないので、空文字列を使うおそれがある時は事前にチェックをして「''」と出力する必要があります。
xargs はデフォルトで「_」が来たら終了してしまうので、-E に空文字列を設定して無効化します。
xargs -E "" command... <args
シェルスクリプトでエポック秒を得る
POSIXのシェルユーティリティにはエポック秒を直接出力するコマンドがないので生成する方法を考えます
日付時刻からエポック秒を計算する
全てPOSIXのコマンドだけでやろうとすると多分これしかないです。
エポック秒は次の計算式で算出出来ます。
tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 + (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 - ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap04.html#tag_04_14
というわけで、dateとawkを組み合わせて次のような感じになりました。日時出力時はUTCを指定するのを忘れない。
$ date -u '+%Y %j %H %M %S' |awk '{ y = $1 - 1900; d = $2 - 1; t = $5 + $4*60 + $3*3600 + d*86400 + \ (y-70)*31536000 + int((y-69)/4)*86400 - \ int((y-1)/100)*86400 + int((y+299)/400)*86400; print t; }'
Cで書く
エポック秒なんて time_t の値そのものなんだからバイナリ使えるなら time(2) 出力するだけでいいよねみたいな。なお、time_t を出力する時は intmax_t でキャストするのが良いらしいです。
#include <stdio.h> #include <time.h> #include <stdint.h> int main(void) { printf("%jd\n", (intmax_t)time(NULL)); return 0; }