systemd上でxrandrが動かないのを解決した回
目的
systemd上でXアプリケーションを動作させる。
理由
Manjaro Linuxではモニターを繋いだだけでは自動でマルチモニタにならないため。マルチモニタを使う場合は xrandr を用いて
xrandr --output LVDS1 --auto --primary --output VGA1 --auto --above LVDS1
といったコマンドをいちいち打つ必要がある。面倒なのでコマンドをシェルスクリプトにしてモニターを繋ぐたびに手動でシェルスクリプトを実行していたが、ぶっちゃけめんどくさくなってきたので自動化しようと考えた。
提案手法
同じようなことを考えることはいるもので、下記のようにタイマー機能で1秒ごとにスクリプトを実行して切り替えを自動化している人がいることがわかった。
ArchLinuxで自動でdisplayの接続を認識し調整する
上記のページはウィンドウマネージャーとして awesome 環境を用いているが、自分は Xfce なので cron か systemd を検討した。自分の環境に cron はインストールしていないので環境を汚したくないなと思って systemd で自動化することに決めた。
systemd での自動実行には systemd.timer を用いればできることがわかったので、これについて調べた。
systemd.timer について
ざっくりと言えば、<サービス名>.service と同じディレクトリに <サービス名>.timer を置いてやればいい。
systemctl daemon-reload も念の為忘れずに。
.service と .timer を書いた
autodualmonitor.service
[Unit] Description = Automatic dual-single monitor switcher [Service] Type = oneshot ExecStart = /home/<ユーザー名>/MyShellScript/auto_dualmonitor.sh Requires=lightdm.service
autodualmonitor.timer
[Unit] Description=Automatic dual-single monitor switcher : timer [Timer] OnBootSec=1min OnUnitActiveSec=1sec [Install] WantedBy=timers.target
両方とも /etc/systemd/system ディレクトリに配置した。スクリプトは上記の ExecStart に書かれている通りの場所に配置した。
トラブルシューティング
systemctl daemon-reload
してから動くか試すために
sudo systemctl start autodualmonitor.service
を実行したがエラーが出て動作しなかった。 .timer もenableにしてみたが Active: failed (Result: exit-code) になっており、正常に動作していないことがわかった。
systemctl status autodualmonitor.service
でログを見てみたところ、 Can't open screen というログが見られた。どうやら xrandr が動いていないようだ。
調べてみたところ、Archlinux wiki の次のページに答えがあった。
DISPLAY は X アプリケーションがどのディスプレイを使えばいいのか知るために使用されます。XAUTHORITY はユーザーの .Xauthority ファイルのパスと、X サーバーにアクセスするのに必要な cookie を指定します。systemd ユニットから X アプリケーションを起動する場合、これらの変数を設定する必要があります。バージョン 219 から、セッションが開始したときに DISPLAY と XAUTHORITY を systemd --user デーモンの環境にアップロードする X11 セッションアプレット /etc/X11/xinit/xinitrc.d/50-systemd-user.sh が systemd に付属するようになりました。これによって、X を標準の方法で起動しているかぎり、ユーザーサービスは DISPLAY や XAUTHORITY を使用することができるようになっています。<<
要約するに、DISPLAYとXAUTHORITYの環境変数が設定されていないとxrandrのようなXアプリケーションは動作しないということらしい。で、ターミナルから実行して動作するのに systemd では何故動作しないのかといえば、
ということだった。つまりはシェルスクリプト内でDISPLAYとXAUTHORITYを設定してやれば動作すると見ていいだろう。そこで autodualmonitor.sh(モニター切り替え用のスクリプト) を次のように書き換えた。
autodualmonitor.sh
#!/bin/bash IN="LVDS1" EXT="VGA1" export DISPLAY=:0.0 export XAUTHORITY="/home/<ユーザー名>/.Xauthority" if (xrandr | grep "$EXT disconnected"); then xrandr --output $IN --auto $EXT --off else xrandr --output $IN --auto --primary --output $EXT --auto --above $IN fi
大抵の場合、DISPLAYは:0.0なので特に考えずにそのまま書いた。XAUTHORITYは echo $XAUTHORITY を実行して調べてきたものをそのまま書いた。
書き換えてから念の為 sudo systemctl daemon-reload し、systemctl start autodualmonitor.service がエラーを出さないことを確認してからPCを再起動した。
再起動後に systemctl status autodualmonitor.timer を実行して見てみると Active: active (waiting) となっており、モニターを抜き差ししてみると、少し時間を置いてモニターが接続されることが確認できた。
まとめ
ThinkPadX230を買ったのでManjaro Linuxをインストールしたらとんでもなく起動に時間がかかるので解決した話
はじめに
Think Pad X230を中古で買いました。22,800円。
ちんこぱっど買った pic.twitter.com/qKbZfoCxHi
— ナッツツうにナッツの抜けツッツ殻ナツツ (@LuCielNoblesse) 2019年6月13日
メモリを増設して8GBにしておきました。低電力メモリで動くんだねこれ。
Think PadへのManjaro Linuxのインストール手順
1. Stable Releaseからxfce版の18.0.4のisoをダウンロード
Downloading File / - Manjaro Linux - OSDN
2. Image WriterでUSBにisoイメージを書き込み
3. Think PadのBIOSを開いてブートの優先度を変えておく。
BIOSはThinkPadのロゴが出ている状態でF1
shiftキー押しながら^キーで起動優先度を上に移動できる。USBの優先度を最上位にする。
4. 起動したらインストール
自分はパーティション消してインストールしました。
5. 終わるまで待つ
6. 再起動
インストール完了したが・・・
起動が遅い
起動が遅い!(重要)
なぜか起動に2分近くかかる始末。これを解決した。
はじめにやったこと
原因を探るために、
systemd-analyze
で起動時間を見てみた。5秒。
そんなわけないだろ・・・
次にブート時のログを見るために、
dmesg
でログを表示した。
明らかに時間がかかっているログがあり、メッセージをみると”crng init done”とあった。
ググってみると乱数生成器が乱数生成しきるまで時間がかかっているらしいことがわかった(というかもしかしてタイムアウトまで待ってる?)
対策を調べてみるとrng-toolsとかhavegedで乱数生成を手伝わせるのがいいらしいことがわかった。
今回は色々とあって縁のあるrng-toolsをインストールした。
Rng-tools - ArchWiki
rng-toolsのインストールと有効化
1. pacmanのパッケージリストをアップデートする
インストールしたてほやほやなのでパッケージリストをアップデートしないと何もインストールできないので、
sudo pacman -Syy
してやる。
2. rng-toolsのインストール
sudo pacman -S rng-tools
でrng-toolsをインストールする。
3. 確認
rngd -f
これで乱数生成器が起動(どんどん/dev/randomにぶち込まれていく状態)
dd if=/dev/random of=/dev/null bs=1024 count=1 iflag=fullblock
これをrngd -fが起動していない状態でやるとバチクソ時間がかかる。rngd -fを起動すると一瞬で終了する。サラマンダーよりはやい
4. rng-toolsのサービスを有効化
あとは起動時にrngdが起動するように設定する。
systemctl enable —now rngd.service
でOK。確認は
systemctl status rngd.service
でenabledになっているかを見ればよい。
結果
Manjaro Linuxの起動が速くなった!
具体的には2分かかっていたところが14秒で終わるようになった。
ちなみにcrng initが動いている理由をよくわかってないままやったので、その辺は後々調べておこうと思う。
GASとOutgoing WebhookでSlackのbotを作った時に引っかかった部分
GAS(Google Apps Script)とSlackのOutgoing WebhookでSlackのbotを作ってみました。
はじめに
もともとはRustで書いたものを動かすつもりでしたが、どこにデプロイするかという問題にぶち当たりました。
お金も出したくないので無料でSlack botを作れないかと思って調べたところ、GASに行き当たりました。
参考にしたのは
Slack BotをGASでいい感じで書くためのライブラリを作った - Qiita
です。
ぶち当たったところ
環境変数の扱い方
参考サイトを見てみると、
var prop = PropertiesService.getScriptProperties().getProperties();
prop.token
でAPIキーを環境変数から取り出しているみたいなのだけど、上手く動作しない。
なので
var token = PropertiesService.getScriptProperties().getProperty('SLACK_ACCESS_TOKEN');
で直接取り出した。
Slackのスタンプの扱い
Slackのスタンプは
:hoge:
といった感じにコロンでくくられているのだが、そのままこれを受け取って文字列の長さをlength()で出してしまうと、スタンプで1文字なのに、コロン2つと中の文字を合わせて6文字扱いになってしまう。
こんな感じで吹き出しのサイズがおかしくなる。
そこで文字列の長さを測る関数を別に用意した。
コロンからコロンまでを無視し、対応するコロンがない場合については巻き戻して文字列を数える関数である。
2バイト文字を2文字として数える関数にこの機能を盛り込んでおいた。半角と全角だと文字幅が違うからね。
URLエンコード
上と関連した話として、GASに飛んでくる文字はURLエンコードで飛んでくるみたいで、ASCII文字の記号は%xx形式になっている。
だから、コロンをそのまま判定しようとして
if ( text.charAt(i) == ':'){hogehoge}
としようとしても無駄である。
コロンの場合は"%3A"になるので、切り出すか、%に当たった場合に記号として判別するかした方がよい。
GASの更新
書いたスクリプトが反映されたりされなかったりして悩んだ。
「公開→ウェブアプリケーションとして導入」の後に、プロジェクトバージョンを最新にして更新しておかないとダメみたい。要検証。
Serenityを使ってDiscordをRustから遊ぶ
はじまり
ちょっと作りたいものがあったのでDiscordのAPIを調べてました。
ぶっちゃけた話、Pythonでdiscord.pyを使った方が楽なんだと思うんですけど、Pythonをみたら発狂して転げ回る病気にかかったということにしているので今回はRustで。
RustでDiscordAPIを叩くライブラリを探してみるとSorenityというライブラリが見つかりました。
日本語で説明しているページが見当たらなかったので、しばらくあちこちにらめっこしながら、とりあえずサンプルが動いたのでそこまで書いておきます。
下準備
まずはDiscord向けのbotを作る準備をしないといけません。Discord向けのアプリを作るためにまずデベロッパーポータルへ行きましょう。
Discordにログインした状態でアクセスすると、下のようなページが出ると思います。
右上の"New Application"をクリックし、作成するアプリケーションの名前を入力して作成します。
日本語でも大丈夫みたいですね。
作成すると下のようなページが出るはずです。
そうしたら上図の赤丸のBOTをクリックします。
右のADDBOTをクリックすると、
"ADD A BOT TO THIS APP? "
"Adding a bot user gives your app visible life in Discord. However, this action is irrevocable! Choose wisely."
と、「bot追加したらもう戻れねえぜ!賢い選択を。」的なことを聞かれるので"Yes, do it !"を力強くクリックしましょう。
余談ですが、"Choose wisely"は『インディー・ジョーンズ/最後の聖戦』を元ネタとするミームみたいですね
それはさておき、クリックすると、
上図のようなページに移動します。
"A wild bot has appeared!"
「あ!やせいの BOTが とびだしてきた!」ですかね。
Discordは本当にゲーム関連の洒落が多くて素敵ですね。
それもさておき、上図の赤丸部分がプログラムで使用することになるトークンです。後で使います。覚えておいてください。
訪れるたびに毎回少しずつ変化しますが、いつ作ったものを使っても問題ないです。
次に、左カラムのOAuth2をクリックします。
移動先のページをスクロールすると、下図のようなチェックボックスが大量に出てくるかと思います。
ここではbotにチェックを入れます。するとさらに下に下図のようなチェックボックス群が出てきます。
このチェックボックスでbotに与える権限を設定できます。これはbotを招待する際に権限オフにもできます。
とりあえず試すために管理者にチェックを入れます。
その後2つ上の図の赤丸でリンクをコピーし、それにアクセスすると下図のような画面が出ます。
そこから追加するサーバーを選んで認証を押してやると
BOTがサーバーに参加します。
が、当然このままでは何もできません。
プログラムで動かしてやる必要があります。
SerenityでDiscord BOTを動かす
とりあえずSerenityをcloneしてきましょう。
git clone https://github.com/serenity-rs/serenity.git
そうしたらexampleディレクトリ内の01_basic_ping_botに移動しましょう。
cd serenity/examples/01_basic_ping_bot/
それでもってcargo run・・・といきたいところですが、このままだと「トークンの環境変数がないぞ」と怒られて動きません。
トークンをプログラム内か環境変数に書いてやる必要があります。
コードをいじらないで動かす場合は、
export DISCORD_TOKEN=<ここにトークン>
とでもしておけばいいと思います。
コードをいじって動かす場合は45,46行目の
let token = env::var("DISCORD_TOKEN") .expect("Expected a token in the environment");
を
let token = "ここにトークン"
としておきましょう。
そうしたらcargo runします。
無事起動し、サーバーに接続できると、コンソールに
<BOTの名前> is connected!
が出ますので、そうしたらDiscord上で"!ping"と書き込んでやると・・・動きましたね。
バーニラバニラで練習題
ことの始まり
サークルの先輩方との飲み会にてズンドコキヨシプログラムを知った。
→パイセン「バニラの求人のやつもありやな」
→詳細な仕様がサークルのSlackに上がってきた(!?)
→Golangの練習がてら書いてみるか〜
→書 き ま し た
バニラの歌詞
こんな感じだったような
※ V A N I L LA バニラ! V A N I L LA バニラ! ※※ バーニラ バニラ バーニラ 求人 バーニラ バニラ 高収入 バーニラ バニラ バーニラ 求人 バーニラ バニラでアルバイト ※※繰り返し ※繰り返し バニラの求人見たーいー 見たーいー 見たーいー バニラの求人見たーいー ニャニャニャーニャーニャニャーニャー
仕様
下記のSTEP1からSTEP6までを実行。「見たーいー見たーいー・・・」の部分については考えないことにした(「にゃにゃにゃーにゃーにゃにゃーにゃー」は含めてもよさそう?)
- STEP1
V!, a!, n!, i!, l!, la!の中からランダムに出力
V!, a!, n!, i!, l!, la!の順番で出てきたら”バニラ!”を出力。
- STEP2
STEP1をもう一回
- STEP3
“バーニラ”, “バニラ”, “バニラで” の中からランダムに出力
バーニラ, バニラ, バーニラ の順番で出てきたら”求人!”を出力
- STEP4
“バーニラ”, “バニラ”, “バニラで” の中からランダムに出力
バーニラ, バニラ の順番で出てきたら”高〜収〜入〜!”を出力
- STEP5
STEP3をもう一回
- STEP6
“バーニラ”, “バニラ”, “バニラで” の中からランダムに出力
バーニラ, バニラで の順番で出てきたら”高~収~入~!”を出力しておわり。
コード
gitに投げた。
github.com
感想
Golangのいい練習になった。
ズンドコ節を知らない世代が増えてきた頃にはもしかすると需要があるのではないだろうか(本当に?)
頭の中に残る繰り返しを含む歌は練習用の題材としていいのかもしれない。多分。
.pcapファイルについてちょっと調べた
パケットキャプチャではおなじみのpcapファイルについて、調べたのでまとめます。
動機
研究室で取り扱っているpcapファイルを管理するためにpcapパーサーを作ろうと思った。
そのためにpcapファイルの構造を調べたが、知りたい情報のある日本語の解説が見つからなかったので調べてまとめた。
pcapファイルの構造
HTTPパケットの入力の概要
上記ページはHitachiのuCosminexus Stream Data Platform - Application Framework システム構築・運用ガイドである。pcapファイルの構造についてはこちらに書かれてたので参考になった。
図を見た方が早いが、
- Pcapファイル自体のファイルヘッダ(24バイト)
- キャプチャしたパケット
- キャプチャしたパケットのヘッダ(16バイト)
- キャプチャしたパケットのデータ(可変長)
という構造になっているようだ。
Pcapファイル自体のファイルヘッダの構造
Development/LibpcapFileFormat - The Wireshark Wiki
上記のページによると、Pcapファイルのヘッダは下記のようになっているとのこと。
typedef struct pcap_hdr_s { guint32 magic_number; /* magic number */ guint16 version_major; /* major version number */ guint16 version_minor; /* minor version number */ gint32 thiszone; /* GMT to local correction */ guint32 sigfigs; /* accuracy of timestamps */ guint32 snaplen; /* max length of captured packets, in octets */ guint32 network; /* data link type */ } pcap_hdr_t;
それぞれについての説明を見ていくと、
magic_number
ファイルの種類を識別する際に用いる。4バイト。
version_major
ファイルフォーマットのメジャーバージョン(ver:x.yのxの方)。2バイト。
version_minor
ファイルフォーマットのマイナーバージョン(ver:x.yのyの方)。2バイト。
sigfigs
キャプチャしたパケットのタイムゾーンの精度?らしい。基本的に値は0になるとのこと。4バイト。
snaplen
"snapshot length"の略で、パケットのキャプチャサイズを制限するために用いるもの?らしい。通常は65535もしくはそれ以上とのこと。4バイト。
network
リンク層の種類。
www.tcpdump.org
上記のサイトに書かれている数値が入るらしい。Ethernetの場合、値は1になる。4バイト。
パケットのヘッダの構造
同じページを参照した。下記のような構造になっているとのこと。
typedef struct pcaprec_hdr_s { guint32 ts_sec; / *タイムスタンプ秒* / guint32 ts_usec; / *タイムスタンプマイクロ秒* / guint32 incl_len; / *ファイルに保存されたパケットのオクテット数* / guint32 orig_len; / *パケットの実際の長さ* / } pcaprec_hdr_t;
ts_usec
パケットがキャプチャされた時間のマイクロ秒にあたる部分。4バイト。
incl_len
キャプチャした際に、実際に「保存された」パケットデータのバイト数
orig_len
キャプチャした際に、実際に「観測された」パケットデータのバイト数
パケットのデータの構造
incl_lenの長さで実際のパケットがそのまま現れる。
構造を確認してみる
SampleCaptures - The Wireshark Wiki
上記Wiresharkのwikiから"http.pcap"をダウンロードし、Wiresharkとバイナリエディタで開いて確認してみた。
pcap_hdr_s
magic_number
値は、
0xA1B2C3D4
D4 C3 B2 A1でググると、
File Signature Database:: D4C3B2A1 File Signatures
からwinpcapのキャプチャファイルだとわかります。
version_major,version_minor
それぞれ、
0x0002
0x0004
となっているので、バージョンは2.4だとわかる。
上記に挙げたWiresharkのページでも
"the commonly used format in its current version 2.4. This format version hasn't changed for quite a while (at least since libpcap 0.4 in 1998),"
と書いてあり、1998年からずっとバージョンは2.4の模様。
thiszone
0x0
sigfigs
0x0
snaplen
0xFFFF
10進数に直すと65535なので、通常の値だとわかる。
network
0x0001
Ethernetを示している模様
pcaprec_hdr_t
ts_sec
0x40A34B23
10進数に直すと1084443427である。下記のサイトでUnix時間からJSTに変換したところ、西暦2004年 3月 13日 19時 17分 7秒だとわかる。UNIXタイムスタンプ変換ツール
Wiresharkで表示されている時刻と一致していることがわかる。
ts_usec
0x0004BFB8
10進数に直すと311224である。マイクロ秒と一致していることがわかる。
incl_len,orig_len
両方とも0x3Eで、10進数に直すと62
以降の62バイトとWiresharkで表示されているパケット本体が一致していることが確認できた。
FEHの投票大戦からデータ取ってきてみた(Python)(スクレイピング)
最初に調べたこと
robots.txtを見てみる
スクレイピングする上ではやっぱり注意しないといけないのが「スクレイピング禁止」かどうかでしょうか
FEH投票大戦スコアページのrobots.txtをとりあえず見てきました。
よし(適当)多分大丈夫でしょう。
Pythonでのスクレイピング方法
ライブラリは前にBeautifulSoupというものがあるのを見たので、これについて使い方を調べてみました。
Python3 + urllib + BeautifulSoupでネット上の情報を取得する
HTMLのタグをもとに掘り下げていけば目的の部分を取り出せるようです。
スクレイピング対象の観察
今度はFEH投票大戦スコアページのソースを見に行きました。
ご存知、Chromeのデベロッパーツール(うちはSRWareIronですが)でソースを見てみると、「n回戦」がarticleタグ単位で、「キャラVSキャラ」がliタグ単位で、「キャラ名とスコア」がpタグで表示されているとわかりました。
また、過去の大戦結果のページを見てみると、最新の対戦カードは上から二番目のarticleタグにくることがわかりました。
また、クラス名は'body-section-tournament'であることもわかりました。
articleタグのクラス名'body-section-tournament'に絞ってスクレイピングしてみると、一番上のarticleタグ、クラス名'body-section-tournament current'まで拾ってきてしまいました。調べてみた感じだと、どうやら同じ文字列が入ってることだけが原因みたいでした。
仕方ないので常に上から二番目のarticle要素より、データを引っ張ってくることにしました。
実際にスクレイピング
articleタグ内のliタグ内のpタグの要素を引っ張ってきて出力してみると、取り出したかったデータが確認できました。
Twitterに投稿する
ついでなのでTwitterに取得したデータをPythonから投稿してみました。
Twitterへの投稿方法
いつも通りTwitter APIを使えるようにしました(書くの面倒)
pip install twitter
したら、あとはTwitter用のライブラリを導入して、キーやらなんやらを設定して投稿!だけなのですが、このままだと生のコンシューマキーとシークレットを晒すことになって危険です。
そこで、以前Rubyで作成したupdate_name*1でも環境変数をファイルに書いて外部に見えないようにしたのを思い出しました。Pythonでも同じことができると思い、環境変数からキーを読み込む方法を調べてみました。
【GitHub】に載せたくない環境変数の書き方 Python
こちらを参考に、.envにキーを書いて、コードをコピペして終わり!(書くの飽きてきた)
今後
とりあえず今の投票大戦期間で自動的に大戦経過をツイートできないかと考えている感じ。herokuだと止まっている時間が長いので、Red Hat Open Shiftに目をつけ始めている。
あとは劣勢時に通知が飛ばせればなぁとかそんな感じ。過去の傾向から予測して劣勢の予測とかもしてみたいなぁ(以前某所で見たことがあるけども)