2018/12/31

[c/c++][CERT]volatileを正しくコンパイルしないコンパイラ

C言語のセキュアなコーディング力を身につけるため、再履修するシリーズ。


DCL17-C. volatile 修飾された変数が間違ってコンパイルされることに注意
https://www.jpcert.or.jp/sc-rules/c-dcl17-c.html

うんうんvolatileは大切よね、くらいの気持ちで読み始めたのだが、思っていたのと全然違った。

しかし、"Volatiles are miscompiled, and what to do about it" [Eide and Regehr] が示したように、彼らがテストしたコンパイラのすべてが、volatile アクセスという点に関して何パーセントかの間違ったコードを生成した。

コンパイラのバグの話か!


私が新卒で入社した頃だが、組み込みのコンパイラでdoubleだったかfloatだったか、どっちか忘れたけど最適化とあわせると正しくない結果になるというコンパイラが使われていてね。
ふっとそんな昔のことを思い出してしまった。


記事では、IA32向けのGCC4.3.0+サイズ最適化オプションでの事例を紹介している。
そう、だいたいこういうのは最適化とセットで起きるのよね。
だから最適化禁止にしているプロジェクトもしばしばだ。

ここでは、引数をそのまま返す関数を用意して、変数参照ではなく関数呼び出しさせることでバグを回避する方法を紹介している。
が・・・バグがあると分かっていればやるだろうけど、まず初っぱなからこういう書き方はしないよなぁ。。。


コンパイラに頼れないので、情報だよりか、実経験だよりになる。
情報は、コンパイラのバグを知るしかないだろう。
そうなると、最新版ではなく、少し枯れたコンパイラを使う方が無難かもしれない。が、最新版だからこそバグが治っているということもあり、くぅ。。。。


実体験に頼る方も考えておいた方が良かろう。

しかし、コンパイラは常に進化し続けているため、クリティカルなコードについては、実配備を想定してコンパイルし、その結果のオブジェクトコードが正しいふるまいをするかどうかを確認すべきである。

問題はどうやって「正しいふるまいをするかどうかを確認」するかなのだが、アセンブラなんかいちいち確認したくない。
ほしいのは、このコードをコンパイルして、実行結果がこうなればOK、みたいなやつなのだが、CPUに依存してしまうとなると簡単ではないな。

実行して、volatile効果がすぐにわかるコードというと、私には並列動作くらいしか思いつかない。
スレッドでも割込みでも良いので、並列して動かして、片方はループでvolatile変数をチェック、もう片方はそのコンテキスト以外でvolatile変数を変更、というやつだ。
スレッドならまだしも、割り込みハンドラなんかを使うとなると、環境依存になってしまって難しいな。


じゃあ関数化するかというと、これも逃れられたのが確認できたというだけで、コンパイラのバグである以上、関数化すれば必ず大丈夫とは言いきれないだろう。


あれ、そうなると、volatile以外は大丈夫なのか? 関数の呼び出し順序なんかもある程度変更することが許容されているようなことが本に書かれていた気がするので、違う部分の最適化バグが出てくることもあろう。

とか考え出すときりが無い。
結局のところ、信頼せずに検証するしかないのかね。

2018/12/30

[c/c++][CERT]opaqueな型

C言語実装のセキュリティを高めるために(自分が)!の学習中。



DCL12-C. 抽象データ型は opaque な型を使って実装する
https://www.jpcert.or.jp/sc-rules/c-dcl12-c.html

なんでopaqueだけ英語なんだよ、というのが第一印象だ。
opaqueって、透過とかそういう意味だったよなー、と思ったのだが、逆で「不透明」とかそんな意味だった。
反対語はtransparentらしい。
つまり、中身を見せたくないようなデータ型を指している。


自分がこれをやるときはvoid*を引数にさせていたのだが、型がないとコンパイラでチェックできないのだ。
そう考えると、このやり方の方が賢いな。


これを使うと中身は隠蔽できるのだが、隠蔽するだけに呼び出し元で変数を作ることができない。
ポインタ型なら作ることができるので、呼び出した先で作ってもらうようなしくみがいるだろう。


01: struct xxx;
02: typedef struct xxx xxx_t;
03: 
04: void func(...)
05: {
06:    xxx_t *p_x = CreateXxx();


ちょっと嫌なのは、一時的に使いたいだけでもスタック変数として作ることができないという点だ。
何かしらのヒープ領域がいるし、それがグローバル変数でないのなら解放する処理もいる。

C++だとコンストラクタ/デストラクタで処理できて楽なのだけど、C言語だと、ねぇ。。。
それに、ヒープ領域を操作できるようにしてない環境だと、グローバル変数で予め確保しておくしかなかろう。


とはいっても、こういうことをしたいのは、OSとかライブラリのように、内部の動きを知られたくなかったり、内部の値を想定して実装されてもろくなことがないとか、だろう。


だから、これは使いたいときに使えばよい技だと判断した。
ポインタを渡している以上、それを破壊するのは相手の自由だし。

2018/12/29

[c/c++][CERT]errno_tという型があるのか

セキュアなCコードを書けるようになろう、ということで、再履修中。



DCL09-C. errno を返す関数は返り値を errno_t 型として定義する
https://www.jpcert.or.jp/sc-rules/c-dcl09-c.html

あれ・・・errnoってint型じゃなかったっけ??

ERRNO
http://linuxjm.osdn.jp/html/LDP_man-pages/man3/errno.3.html

そうそう、注意事項書かれているように、昔はerrno.hはなかったので、使うときは宣言してた。
errno.hができたというのは把握していたのだが、intでなくなったのは知らんかった。
が、ここのサンプルコードではintになってるな。


Ubuntu18.04の/usr/include/errno.hには、intをtypedefしてerrno_tに仕立てていた。
エラー値も#defineされていたし、そこまで心配はせんで良かろうが、int型に固執することもなかろう。
errnoをチェックするためにローカル保存するシーンはしばしばあるので、気をつけておかねば。

2018/12/27

[c/c++][CERT]ポインタのtypedefとconst

書いているソースコードを見て、セキュリティとかが甘いと言われた・・・。
く、屈辱だが、意識していないので反論どころか、何も言えるものが無い。


このままじゃまずいなー、と思って探していると、こちらのサイトが見つかった。

CERT C コーディングスタンダード
https://www.jpcert.or.jp/sc-rules/

英語版がすらすら読めるならよかったのだが、残念ながら私には無理だったので、日本語訳してあるのが非常にありがたいので、少しずつ読んでいこう。


読むだけだと身につかないので、気になったところだけブログの記事にしていく予定だ。
長いことC言語で書いているけど、自分の中の常識で固まっている可能性が高いので、初心に帰って読んでいこう。


DCL05-C. typedef による型定義ではポインタ型を避ける
https://www.jpcert.or.jp/sc-rules/c-dcl05-c.html


なんでも、ポインタ型をtypedefすると、それにconstしても思ったようなconstにならないらしい。
えー、ほんとかなぁ。

01: #include <stdio.h>
02: #include <string.h>
03: 
04: struct kuma {
05:     int     a;
06:     char    b[5];
07: };
08: 
09: typedef struct kuma kuma_t;
10: typedef struct kuma* kumaptr_t;
11: 
12: 
13: static void func(const kumaptr_t Kuma)
14: {
15:     Kuma->a = 20;
16:     strcpy(Kuma->b, "def");
17: }
18: 
19: 
20: int main(void)
21: {
22:     kuma_t      k;
23:     k.a = 10;
24:     strcpy(k.b, "abc");
25:     func(&k);
26:     printf("k.a=%d\n", k.a);
27:     printf("k.b=%s\n", k.b);
28:     return 0;
29: }

$ gcc -o tst -Wall -W typedeptr.c
$ ./tst
k.a=20
k.b=def


むう、コンパイラがエラーを出さない時点で書き込めるのはわかったのだが、やっぱりショックだ。

static void func(const kuma_t *Kuma)

引数をこうやると、コンパイルの時点で失敗する。
疑って悪かった。


でも、Win32 APIなんかはそういう定義がたくさんあったよなぁ、と思ったら、下の方に説明があった。
constまで付けたポインタ型をtypedefしたものも用意していて、そっちを使うのだ。
そうだ、確かにCがついた型もあった。そういう理由だったのか。

まあ、ポインタ型をtypedefしないようにしておく方が簡単だな。



あ、関数ポインタがあるやん。
私はあれが苦手で、typedefしてからじゃないと仮引数なんかにできないのだ。。

これも下の方に書いてあった。
関数ポインタ型は例外だ、と。
ありがたい。
いや、別に従わなくてもいいのだが、書いてあると安心するじゃないか。

2018/12/23

[virtualbox6]デスクトップに作ったショートカットから起動できない

VirtualBoxの6.0がリリースされた。

が、Windows10だけかもしれんが、デスクトップに作ったショートカットから起動するとエラーダイアログが表示される。

image

https://forums.virtualbox.org/viewtopic.php?f=6&t=90827&sid=09ae384d999a521bd8a7252db959f145


作られたショートカットは「VirtualBox.exe」だけど、それを「VirtualBoxVM.exe」にするとよいそうだ。
私のところは、それで動いた。
やれやれ。

2018/12/19

[c/c++]snprintfくらいしかあふれても\0付加してくれない

忘却とは忘れ去ることなり・・・。

文字列あふれなんか上位層でカットして、下位層はそのままスルーして使ってしまえ!と実装していたのだが、Linuxみたいにリソースが裕福な環境だと、チェックを減らしてROMやスタックの消費を抑えるよりも、きっちりチェックして安全を求めることが優先されるらしい。

ちっ。

以前調べた気もするが、記憶が無いので調べ直そう。


過去記事は、こちら。

[c/c++]strncatはあふれても\0付加しない
https://hiro99ma.blogspot.com/2016/12/ccstrncat0.html


どちらかといえば、snprintf()は\0を付加するが、それ以外はやってくれない、と覚えるのが良いのか。
でも、strncpy()なんかはメジャーな気がするから、やってくれるかもしれない(よくわからない期待)。


01: #include <stdio.h>
02: #include <string.h>
03: 
04: #define STRMAX      (10)
05: 
06: int main(void)
07: {
08:     char str1[STRMAX + 1];
09: 
10:     memset(str1, 'a', sizeof(str1));
11:     snprintf(str1, sizeof(str1), "12345678901234567890");
12:     printf("snprintf : [%s]\n", str1);
13: 
14:     memset(str1, 'a', sizeof(str1));
15:     strncpy(str1, "12345678901234567890", sizeof(str1));
16:     printf("strncpy  : [%s]\n", str1);
17: }


snprintf : [1234567890]
strncpy  : [12345678901]


一瞬、「おお、やってくれるやん」と思ったが、strncpy()では11文字出力されているので、STRMAX+1の後ろに\0がついていることになるな。


まあ、こういうのは、スタックの配置とアラインメントを考えて、こうするのがわかりやすかろう。


01: #include <stdio.h>
02: #include <string.h>
03: 
04: #define STRMAX      (15)
05: 
06: int main(void)
07: {
08:     char str0[STRMAX + 1];
09:     char str1[STRMAX + 1];
10:     char str2[STRMAX + 1];
11: 
12:     memset(str0, 'x', sizeof(str0));
13:     memset(str2, 'y', sizeof(str2));
14: 
15:     memset(str1, 'a', sizeof(str1));
16:     snprintf(str1, sizeof(str1), "12345678901234567890");
17:     printf("snprintf : [%s]\n", str1);
18: 
19:     memset(str1, 'a', sizeof(str1));
20:     strncpy(str1, "12345678901234567890", sizeof(str1));
21:     printf("strncpy  : [%s]\n", str1);
22: }


snprintf : [123456789012345]
strncpy  : [1234567890123456yyyyyyyyyyyyyyyy`d�D�]


メモリの配置がどっち側かわからなかったので前後に追加したが、上から順番だったようだ。
strncpy()はstr1に20文字中16文字をコピーし、そのまま\0は付加しなかった。
メモリの境界としてつながっていたので、printf()するとstr2の"yyy..."が出力され、こちらも\0がなかったのでさらに後ろのゴミが出力されて、\0が現れたので止まった、というところだろう。


strncpy()も\0付加してくれていいような気がしたのだけど、余計なことはしないということで付加はしないのだろう。
snprintf()は、そもそもフォーマットという余計なことをするので、\0の付加くらいは当たり前という立場なのか。

2018/12/08

[win10]なぜかGitHubのフォントが太い

うちでは、デスクトップPCとノートPCがあり、どっちもWindows10を使っている(バージョンは違うが)。

なぜか、ノートPCでGitHubを見たときのフォントが太いのだ。


たとえば、ここ。
https://github.com/hirokuma/leveldb-c-example


Firefox


デスクトップPC

image  


ノートPC

image



chrome


デスクトップPC

image


ノートPC

image


横に並べたいのだが、ブログエディタの設定がうまく行かん・・・。



chromeはあからさまに違うな。

firefoxは同じかも、という気がしたが、横に並べると違った。
というか、フォントの種類が違うな。

左がデスクトップPC、右がノートPCだが、右はドットフォントになっている。
私がドットフォントの方が好きなのでいいんだけど、同じ設定のつもりなのに違うというのが気にくわない。


image



デスクトップPCのバージョンは1803、ノートPCのほうは1809だ。
ノートPCの方を先にアップデートして様子を見ていたのだが、アップデート直後からそうなのか、しばらくして変わったのか、そもそも何か設定を変更してしまったのかがよくわからん。

フォントキャッシュを削除してみたが変わらんので、そういう問題ではないようだ。
フォントの種類に詳しければ何か分かったのかもしれんが、うん、私には分からん。


結論は何も出ないが、メモとして残しておこう。

2018/11/30

[linux][pthread]pthread_cond_wait()を勘違いしていた (2)

前回、けっこうがんばったつもりだったので、このタイトルで2回目の記事は書きたくなかった。。。
つまり、まだあれではダメだった、ということだ。


前回の一番最後の図で、左側に要求を出すスレッドが2つ、右側に要求を処理するスレッドが1つあった。

  1. 右側が先にmutex_lock()してcond_wait()によってロック解除
  2. 左側がmutex_lock()して、リソースを書き換えてcond_signal()
  3. 右側はcond_signal()を受けて動き出したいけど、内部でmutex_lock()をするため、左側がmutex_lock()していることにより動けない
  4. 左側がmutex_unlock()
  5. 右側が動き始める

こういうシナリオだった。


が、左側には要求を出すスレッドが複数あるので、こんなことが起きているようなのだ。

  1. 右側が先にmutex_lock()してcond_wait()によってロック解除
  2. 左側Aがmutex_lock()して、リソースを書き換えてcond_signal()
  3. 左側Bがmutex_lock()して、動けない
  4. 右側はcond_signal()を受けて動き出したいけど、内部でmutex_lock()をするため、左側Aがmutex_lock()していることにより動けない
  5. 左側Aがmutex_unlock()
  6. 左側Bが動き始めて、左側Aが書き換えたリソースを上書きしてしまう

たぶん、こうなっている。
はぁ。


この記事の真ん中から下にも書いてあるが、順番に処理したいならキューイングするしかなかろう。
(まったく読んでなかった。。。)

IBM: 一般的なスレッド: POSIX スレッドの説明: 第3回

やりたいことは異なるのでソースをまねするかは分からないけど、キューイングみたいなことはいるな。


リンク先のworkcrew.cで、L.49でmutex_unlock()、L.53でmutex_lock()しているけど、これはいるんだろうか?
cond_wait()を抜けたときには内部でmutex_lock()して、cond_wait()を呼ぶときには内部でmutex_unlock()されるということだったので、私のコードからは削除したのだ。

全体で何をやっているコードなのか見ていないので何とも言えんな。
隙を見せる(一瞬unlockする)ことで、何か利点があるのかもしれん。

2018/11/23

[win10]ウインドウの枠を太くしたい2018年秋 (3)

なぜか3回目だが、見た目の問題は使い勝手に影響するので、長引くのもしかたあるまい。


今回は、記事の修正だ。

1回目で、色の選択はすべてできるわけではないと書いたけど、そんなことはなかった。
「サポートしない」というメッセージだけで、完了ボタンを押して確定することができたのだった。

image

まあ、見づらいのだけどね。。。


今日は、このくらいの色にした。

image

image


うーん・・・悪くはないのだが、黒文字に合わせる背景色ってのは選択肢が少ない気分になってしまう。
私のセンスの問題のような気もするが、そこは忘れよう。



そもそも、最近はウィンドウを結合して文字が見えない方がデフォルトだったと思うので、気にしないものかもしれん。

image

これに慣れてしまえばいいのだろうが・・・ダメだった。
新しいものについていけない年齢なのかもしれんし、そういうのをずっとサポートするのもメーカーとしては大変なのだろう。

2018/11/18

[win10]ウインドウの枠を太くしたい2018年秋 (2)

このサイトはGoogleの「Blogger」を使ってるのだけど、貼っている画像は「フォト」と同じ扱いなのに気付いてなかった。
もうブログに貼ったから削除して良かろう、とゴミ箱に入れていたら、画像がなくなっているではないか。。。

私は、けっこうバサバサと削除してしまう方なのだ(そして、元に戻せなくて後悔することもしばしば)。
きっと、過去のブログでは画像がなくなっていることだろう。


幸い、気付くことができたので、過去のこと(画像がなくなっているだろうこと)は忘れて、前向きに生きていこう。


さて、ウインドウの枠を太くする記事の2回目。

前回は、Windows10 Homeの1809(2018年秋バージョン)で枠を太くしたのだった。
今回は、1803(2018年春バージョン)にその設定を持っていった話をしよう。


枠を太くするのに使ったのは、AeroLiteという隠しテーマらしい。
1803がインストールされているPCはWindows10 Proで、この子も以前はAeroLiteにして試していて、元に戻したのだった。


テーマファイルはエクスポートできるようだったので、1809で作った設定ファイルを、そのまま1803で開いた。
開いた、というのは、ダブルクリックした、という意味だ。
拡張子は「deskthemepack」で、cabファイルで圧縮されていた。
展開すると「theme」ファイルが入っていただけだった。まあ、壁紙も何もないからね。


image

1803の方は、それだけでインポートしてくれた。
まあ、AeroLiteを以前に有効にしていたのが効いていたのかもしれんな。



非アクティブなウィンドウタイトルの色は、ものによって違うようだ。

こちらは、TeraTerm。
非アクティブなことがちょっと分かりづらいが、文字は読みやすい。
なお、文字が太文字になっているのは、Tweakerで変更したからだ。

image


こちらは、Explorer。
私には非アクティブがちょっと見づらいが、アクティブな方はこっちの方がよいかも。
枠の色は、変化がないように見える。

image


これはChrome。
非アクティブが緑色なのは、Tweakerで設定しているため。
Tweakerの設定項目にあるのに、これが有効なアプリがどれかわからんのだ。

image


ストアアプリの電卓。
Microsoftのストアアプリは、タイトルバーのところに色が付かないようだ。
文字の色でも区別が付かないので、太くした枠が見分けるのに役立つ。

image



とまあ、こんな感じで、色のルールがよくわかっていない。

なんとなく、Microsoftはタイトルバーを嫌がっているような気がするのだが、「Windows」なのだからウィンドウは見やすくしてほしいものだ。


さて、枠を太くしてみたものの、それが何か役立っているかというと、よくわからん。

劇的に何か変わったというわけではないが、精神的には多少良くなった気がする。
まあ、Windows8になったときは「うおっ、枠が太い!」と思った気がするのだが、枠がなくなってほしいわけではなかったので、ないよりはあるほうが助かるのだ。


とはいえ、この辺は慣れとか作業環境とかあると思うので、私は枠がほしかった、というだけのことだな。

2018/11/17

[c]LevelDB C (3)

11月になって寒くなってきたので、コタツ上のPCに移動。
こちらにはleveldbの環境が無いので、せっかくだからaptでinstallできるか試しておこう。

apt install libleveldb-dev
git clone https://github.com/hirokuma/leveldb-c-example.git
cd leveldb-c-example
gcc -Wall leveldb_example.c -lleveldb
./a.out

コンパイルも通って、実行もできた。


ライブラリやincludeのディレクトリも一緒にcommitしていたけど、それを削除してもビルドできたので大丈夫だろう。


さて、このサンプルだが、実行しても後に何も残らない。
DELETEしているところと、DESTROYしているところをコメントアウトすると、ディレクトリが残った。

「testdb」というフォルダ名で、これはleveldb_open()した名前と一致している。
実行はWindows10のWSLで行ったのだが、ディレクトリの中はこうなっていた。

image

Windowsの人としては、拡張子が付いている「000003.log」がテキスト形式であってほしかったが、テキスト形式らしきものは「CURRENT」と「LOG」だった。


CURRENT

01: MANIFEST-000002
02: 


LOG

01: 2018/11/17-22:09:16.387552 7f3d2e040740 Delete type=3 #1
02: 


000003.log

image


MANIFEST-000002

image


データらしきものは、拡張子がlogのファイルに残っているようだ。
LOGの、時間とコメントの間にある数字はデータのハッシュ値かと考えたが、"testdb"を削除して実行しても値が変わるし、変わるのは一部だけだから、何か時間の要素を使っているか、乱数を使っているだろうし、固定値の意味合いもあるのだろう。
いまだと「7f...」で始まるので時間かと思ったが、7fの次が変わるので単純な時間表現でもなさそうだ。


削除しないようにしてもう一度実行すると、LOGはLOG.oldにリネームして、新しく作り直すようだ。
つまり、1世代前のLOGまで残っている。

MANIFESTの番号も増えるし、拡張子がldbのファイルも増えたりしているので、作ったファイルの中身を知りたければ実装を読むか、解説記事を読んだ方がいいだろう。


私はそういうつもりがないので、今回はここまで。

2018/11/11

[linux][pthread]pthread_cond_wait()を勘違いしていた

(2018/11/11 23:19追記あり)

Linuxでpthreadを使っている。
スレッドがいくつかあって、相手のスレッドに処理要求して、終わるまで待つ、という同期処理が必要になった。
そういうときは、pthread_cond_wait()で待って、pthread_cond_signal()で起こしてもらえば良かろう。


・・・ということはわかったのだが、あまり使い方を読まずに使っていた。
そのせいで、相手のスレッドに要求するスレッドが複数になった場合にmutexがlockするという現象が起きていた。

ちゃんと読まずに使うのはよくない、という反省を込めて記事にしよう。


共有のメモリに処理してほしい内容を通知して、相手は処理したらそのメモリに結果を返す。
処理する方は呼ばれるまで待っているし、呼び出す方のスレッドは終わるまで待たせる。

pthread_cond_wait()は引数にlockしたmutexを取るようだったので、こう考えていた。

  • cond_wait()する直前にmutex_lock()する
  • mutex_loc()するんだから、他のスレッドが同じことをしようとしたらmutex_lock()で待たされる


そして、こういう実装をしていた。
見づらくてすまんが、黄色から右側が処理するスレッドで、左側は要求するスレッドたちだ。

要求するスレッドは、まず要求側のmutexをlockして、メモリに書込み、cond_signal()で相手を起こしたら、自分はcond_wait()で寝る。
処理側は処理が終わったらcond_signal()で起こして、自分はcond_wait()で寝る。

image


これでよかろうと思っていたのだが、何だかよくわからないタイミングでスレッドが固まることが分かった。
pthread_cond_wait()の使い方など疑ってもいなかったのだが、他にあやしい箇所がない。。。


IBM: 一般的なスレッド: POSIX スレッドの説明: 第3回

これの「もう一度、復習を」を見ると、pthread_cond_wait()呼び出しはロックを解除した後にスリープ状態になる、と書かれている。
そ、そんなばかなー!


よく読むと、こういうことらしい。

  • pthread_cond_wait()は、mutexをunlockした後でスリープ状態になる
  • pthread_cond_signal()などが呼ばれると、mutexをlockしてから起き上がる

今回の現象は、pthread_cond_wait()のあとはunlockされているから、別のスレッドがlockできてしまうために起きているのだ。
実装したものがすべてじゃないのね・・・。


そういうことなので、シーケンスを修正した。
明示的に実装していないmutexのlock/unlockは赤い矢印で表した。


image


これなら、mutexのlock/unlockが意図せずクロスすることはないだろう。


いやー、mutexというかpthreadは内部の動きも理解していないといけませんな。
などと書きつつも、これも間違っていたら嫌だなぁ。。。


2018/11/11 23:19追記


やはり・・・間違えていた。
基本的な考え方は悪くなかったのだが、シーケンスが良くない。

描いたシーケンスだと、

  1. 右側のスレッドがcond_wait()する
  2. 左側のスレッドがcond_signal()を投げる
  3. 左側のスレッドがcond_wait()する
  4. 右側のスレッドがcond_signal()を投げる

としていたのだが、2番でsignalを投げた後、左側がwaitに入る前に右側のスレッドがsignalを投げてしまい、待ち人来たらずになってしまうことがあったのだ。


そもそも、cond_wait()の前にmutex_lock()することで何がよいかというと、cond_wait()されることでmutex_unlock()が行われることだ。
これによって、別のスレッドがmutex_lock()して待ち状態になっているものが解除されることで、順番を制御できるのだ。

大ざっぱに書くと、こういう感じか。

image

先にlockして、signalを投げたい方をlockで待ち状態し、waitでunlockすると、相手がsignalを投げるのは必ずwaitした後になるということだ。


修正は1箇所。
左側のスレッドがsignal()を投げた後にlock()していたのを、その前にするだけだ。
そうしておくと、右側のスレッドはsignal()を投げる前にlockしているので、順番が守られる。

image


さあ、これくらいで直ってくれないだろうか。。。

2018/11/10

[win10]ウインドウの枠を太くしたい2018年秋

定期的に気になる、Windows10のウィンドウ枠が細い問題。
問題というか、仕様なのだろうが、たくさんウィンドウを開いて選択し間違えるたびにイライラするので、個人的には問題なのだ。


以前も試したのだが、Winaero TweakerでテーマをAero Liteにしてみよう。
Windows10は、Homeの1809。
Tweakerのバージョンは、0.12.1.0。


Aero Liteはデフォルトでは有効になっていないので、レジストリ操作などで有効にできたはずだ。
戻すのが面倒そうなので、Winaero Tweakerを使う。

image


まあ、上の画面は変更後なんだけどね。
ウィンドウを重ねた場合は、こう見える。

image


アクティブなウィンドウは文字色が黒で、非アクティブはグレーだ。
変更できたかもしれないが、まあよかろう。


枠の色が黄色っぽいのは、タスクバーのせいだ。
何の話かというと、タスクバーの文字色が黒で固定されるようなのだ。

image

前回も、これが原因でテーマを元に戻している。
Windowsで使える色をそのまま使うと、どれを選んでも見づらいからだ。
私は青っぽい色を使っていたのだが、それだとこの通り。

image


当時はあまり色の選択ができなかったのかもしれないが、今はユーザが色をある程度選べるようになっていたので、自分が許容できる色が選べる。

image


image

(何気なく選んだが、グレーもいいかも)



「ある程度選べる」というのは、明るすぎる色だとWindowsが許してくれないからだ。
(2018/11/23修正:「サポートしない」といっているだけで、選択できました!)

image


なんでWindowsがそういうのを指摘するかというと、ストアアプリのリンク色に反映されるからではなかろうか。
下の図だと「夜間モードの設定」と「Windows HD Color設定」はリンクなのだ。

image


タイトルバーにも反映されるので、そこも考慮すべきだろう。
重ねてみたが、Explorerはグレーだけど、メモ帳やTeraTermはそんなことないな。ストアアプリでもOpenLiveWriterとSkypeは違うし。
そもそも、ストアアプリでタイトル領域が塗りつぶされている方が少ないのか。

image


私では非アクティブなタイトル文字色を変更する方法が分からなかったが、そういうのを探していくのもまたよいだろう。

2018/11/03

[c]LevelDB C (2)

まずは、以前参考にしたC interfaceのexampleを見る。

サンプルだからだろうが、中身はシンプルだ。

  • openオプション設定
  • open
  • writeオプション設定
  • put
  • readオプション設定
  • get
  • delete
  • close
  • destroy

destroyまですると、DBのディレクトリごと削除するようだった。


気になったのでvalgrindを使ってみると、メモリの解放をしていないところがあった。
あと、getしたものをそのまま\0無しで出力しているので、よろしくない。
そこら辺を修正した。

https://github.com/hirokuma/leveldb-c-example/blob/64f448da1f345f4c94b924ceb2dd9f62725e649e/leveldb_example.c


オプションはそれぞれ内部でmalloc()しているので、それぞれの解放APIを呼ぶことになった。
openのオプションはdestroyでも使っているが、同じleveldb_options_tである必要はないかも。
少なくとも、このサンプルを改造して、openとdestroyでそれぞれleveldb_options_create()を使ったが、特にエラーは出ていない。

c.ccを見ても、options->repを渡しているだけだし、参照してはいるものの保持している感じはしないので、たぶん・・・たぶん大丈夫なんじゃなかろうか。

[c]LevelDB C (1)

いままでLMDBを使っていたが、違うタイプのDBも検討したい。
候補として出てきたのは、以前も調べたLevelDB
C++で書かれているが、Cのインターフェースもあるので、そちらを見ていこう。


なぜ検討しようかと思ったかというと、LMDBの特徴のいくつかが、いま作っているアプリと噛み合わなくなってきたからだ。

ここのサイトにまとまっていた。
A short guide to LMDB – Kolab Now


"Caveats"の最初に書かれているように、memory mappingされるようになっていて、それがサイズの上限になってしまうのだ。
そんなに大きいデータを扱うつもりがなかったので気にしていなかったのだけど、だんだん扱うサイズが大きくなってきて、とうとうエラーが発生するようになってしまったのだった。
64bitのLinuxで開発していたら気にならなかったのだけど、Raspberry Pi3に持っていくと1.5GBくらいまでしか指定できなかった。
environmentを複数に分ければ確保できるのかもしれないが、そこまでしてLMDBに合わせなくてもいいんじゃないのか・・・。


DBの内容は削除もしていくのだが、「The database file will never shrink」なので、アクションを起こさないと小さくなってくれない。
mdb_env_copy2()でMDB_CP_COMPACTを実行すれば減りそうだが、それをやるにはcopy2した後のenvに切り替えないといかんだろう。
その時間が致命的かどうかが、ちょっと見積もることができていない。


Transactionの範囲がDBではなく、environment単位というのも手痛かった。
1つのDBだけアクセスしたくても、それを含むenvironmentをロックすることになるのだ。
まあ、だからTransactionは手短にするよう書かれているのだろうがね。

それに関して解決できていないのが、mdb_txn_begin()だ。
複数のスレッドからアクセスすることがあるので、先にmdb_txn_begin()したスレッドが優先され、後から呼んだ方はmdb_txn_commit/abort()されるまでロックされるだろう、という想定でいた。
いたのだが、両方ロックされてしまうことがあったのだ。
mdb_txn_begin()前後にログを出しながらtidを見ていたのだが、2スレッド目がmdb_txn_begin()すると両方のログが止まってしまったから、そう判断している。
なんかAPIの使い方を間違えているような気はするのだけど、よくわかってない。


そんなこんなで、LMDBの問題ではなく特徴が生かせなくなっているので、別のDBも見てみようか、と考えたのだった。


もう1つ、LMDB本体とは直接関係ないのだけど、Windows 10の2018年春バージョン以降のWSLでLMDBがうまく動かないというのも後押ししている。
WSLのディスクキャッシュが変わったのが原因だろうということだったので、予想が当たってちょっとうれしいものの、VirtualBoxなどのLinux環境が必須になったのは面倒だ。
1台のWindows10だけ2018年秋バージョンにしてみたが、そっちでも変わらなかった。

2018/10/27

[vscode]構造体メンバの候補一覧keyはselectNext/PrevSuggestion

Visual Studio CodeをVirtualBoxで立ち上げたUbuntu18.04上で使っている。
Linuxで慣れたエディタがないので助かるのだが、キーバインドが分からずに困っている。
WindowsではAutoHotKeyを動かしたままなので、どうも思った操作ができないようなのだ。


最近困っていたのが、構造体メンバの候補リストが表示されているときに矢印キーの上下で候補が選択できないこと。
デフォルトではできたのかもしれないが、いろいろ変更していたせいか、上下の矢印キーを押すとエディタ画面の方のカーソルが移動してしまうのだ。


あれこれ試した結果、該当する名前は「selectNextSuggestion」「selectPrevSuggestion」だった。
デフォルト値を見ておこう。

image

ああ、デフォルトでは普通のキーに割り当てられているのか。。。
しかし、こんなキーを変更した覚えがないのだが、どの名前がどの機能か分からないときに当てずっぽうで変更していたのかもしれん。


あらゆるキーをカスタマイズできそうな気配はするのだが、名前が分からんとカスタマイズのしようがないな。
調べ方を覚えたいものだ。

2018/10/15

[c/c++]sizeof忘れでコンパイルエラーにならなかった(当たり前だけど)

なぜか、malloc()だか、そのfree()だかわからないけど、エラーが出ていた。
サイズが足りないのにアクセスしたような感じのエラーだ。


確保するサイズは、sizeof()で求めていた。
TLVじゃないけど、Length + Valueをいくつかつなげたようなサイズを確保したかったのだ。

Lengthは1byte分で、uint8_t型。
その箇所は、uint64_t型をValueに取りたかったので、こんな感じで書いていた(つもりだった)。


int length = sizeof(uint8_t) + sizeof(uint64_t);


しかし、私は最初の方にsizeofを書き忘れていたのだった。。。

int length = (uint8_t) + sizeof(uint64_t);


驚いたことに、これがコンパイルエラーにはならないのだ。
-Wや-Wallつけてるのに、warningも出ない。

01: #include <stdio.h>
02: #include <stdint.h>
03: 
04: int main(void)
05: {
06:     int length = (uint8_t) + sizeof(uint64_t);
07:     printf("len=%d\n", length);
08: }


なんとなく想像がつくだろうが、この結果は「8」だ。
"+sizeof(uint64_t)"をuint8_tでキャストしたことになったのだろう。


なかなか気付きませんでしたなぁ。。。

2018/10/06

[win10][wsl]defender firewallを設定する

Windows10 1809がリリースされたので、アップデートした。

https://docs.microsoft.com/en-us/windows/whats-new/whats-new-windows-10-version-1809#windows-defender-firewall-now-supports-windows-subsystem-for-linux-wsl-processes

どうやら、Windows Defender FirewallにWSLのプログラムを設定できるらしい。
私はセキュリティに強くないので、Windows Firewall Controlを使って「許可していないアプリは禁止」にしているのだが、WSLの場合はpicoとかいうプロセスがアクセスして、これがどうやっても許可させることができなかったのだ。


やり方は、こちら。
python2.7のIN方向を許可するサンプルが載っていた。

https://docs.microsoft.com/en-us/windows/wsl/release-notes#build-17627-skip-ahead

netsh.exe advfirewall firewall add rule name=wsl_python dir=in action=allow program="C:\users\<username>\appdata\local\packages\canonicalgrouplimited.ubuntuonwindows_79rhkp1fndgsc\localstate\rootfs\usr\bin\python2.7" enable=yes

単純に、Windowsから見たファイルパスを登録するだけのようだ。
残念ながらWindows Firewall Controlは*.exeのようなものしか登録できないようだ。
「セキュリティが強化されたWindows Defender ファイアウォール」(長いよ...)だと追加できるようだが、local/packages以下がダイアログから見えなかったので、Explorerでたどったパスをコピペで登録させた。


まず/usr/bin/wgetを登録。
うん、いける。

では、普段よく使う/usr/bin/aptを登録・・・したのだが、これが通信できない。
拒否しているタイミングを見計らってps auxしてみたところ、/usr/lib/apt/httpというプロセスが立ち上がっていた。
そいつを許可してやるとaptが通るようになった。
やれやれ。


しかし、これは使うのが面倒だ。
当面はWSL使うときだけFirewallを許可する方が楽そうだ。

と、ふとWindows Firewall Controlの遮断ログを見ると、先ほどの/usr/lib/apt/httpが出ているじゃないか!
そうか、picoが前面に出ていたものが、各ファイルになったのだな。
そして、Windows Firewall Controlは自分でルールを追加する場合には*.exeみたいなファイルしか設定できないものの、ログから許可設定するのはOKだった。
これなら便利ですな。

2018/09/16

[esp8266]RTOS v3.0

かなり久々だが、ESP8266のRTOS_SDK v3.0がリリースされていたので、Windows10のWSLで動かしてみた。

https://github.com/espressif/ESP8266_RTOS_SDK



だいたいこんな感じだった。

sudo apt install libncurses5-dev flex bison gperf
sudo apt install python-pip
sudo pip install pyserial

cd (project_templateまで移動)
make menuconfig
make all


なお、COMポートの設定はこちらを見ながら行った。
chmodで先に変更しないと、"sudo make flash"ではダメだった。

WSLでシリアル通信をする
http://matsuneko22.hateblo.jp/entry/2017/12/09/144803


IO0をLにして起動後にFLASH書込み実行。

make flash


このproject_templateで何が動くのかと思ったら、printf()するだけだった。
baud rateがデフォルトなので74880bpsで送信しているようなのだが、使っているUSBシリアル変換がその速度をサポートしてないので確認できなかった。
まあ、動いてるんだろう。


いずれはesp-idfと同じ形にするのか、統合するのか、ともかくそういう計画らしい。
私が使ってみたかったMbedTLSのchacha20は入ってなかったので、ライブラリのバージョンもesp-idfより古いものがあるようだ。


まあ、socketとかは使えるので、Linuxで書いたプログラムを移植しやすいのは助かるな。

2018/09/03

[linux][azure]OSディスクサイズを増やした後

会社でAzureを使っている。
月も変わったことだし、少しメンテナンスをしておこうと休日に見てしまったのが間違いだった。。。
ディスクがフルになっているVMがあったのだ。


開発用にVMを立てているだけなので、実はそれほど深刻ではない。
面倒でなければ、VMごと削除して新しく作っても良いくらいだ。
ただ、面倒なので、ディスクを増やすだけで対応させたい。


私はAzureやらAWSやら、そういうものに詳しくない。
なんとなく使えているので、これでいいか、と思っているだけだ。
その程度の知識の人が書いていると思って読み進めていただきたい。


まず、OSディスクサイズを増やすには、VMを停止させる。
そうすると、「設定>ディスク」とクリックして、出てきたOSディスクをクリックした先の画面で変更ができるようになる。
フルになっているものが30GiBになっていたので、ひとまず64GiBにしておいた。


そして保存してVMを起動させる。
で、ここからがよくわからないのだが、なぜかパーティション全体が拡張されるVMと、ディスクだけが拡張されているVMの2パターンが発生するのだ。
OSは、Ubuntu16.04。
gpartedか何かで拡張すればよさそうなものだが、そもそもディスクが空いていないのでインストールもできない。。。
同じようにVMを作ったような気がするけど、何が違うんだろうね?


まっとうな使い方としては、データディスクを追加して、データが大きいものはそっちを使うとかなんだろうかね。
この辺りは勘所が分からん。


仕方ないので、パーティションが拡張されなかったものは、パーティションを追加して対応させることにした。

fdiskでデフォルトサイズのままパーティションを作り、再起動して、mkfs.ext4。


問題は、これからだ。
/etc/fstabをどう書くか。。。苦手なのだ。
こんなのでどうだろうか?

/dev/sda2       /home/xxx/yyyy  ext4    default,discard 0 0

私にとって大切なのは、mountしたディスクにユーザ権限で書き込めるかどうかだ。
が、それ以上に大切なのは、起動するかどうかでs


あ、起動しなくなった。。。


Azureの助かるところは、SSHがだめでもブラウザからログインできることだ。
fstabの設定を消したら、やはりログインできるので、なんかよくないのだろう。

$ sudo mount -t ext4 /dev/sda2 yyyy

これも大丈夫だった。
何が悪いんだ・・・。


はっ、「default」ではなく「defaults」なのか!
無事起動しました。

ユーザからのアクセスも、sudo chownで対応できた。
mountするディレクトリはユーザが作ったから大丈夫かと思っていたけど、mount後にchownしないといかんのかな?

2018/09/02

[python]pythonの仮想環境

名前はよく聞くし、なんとなく仮想的なものを意味している感じがするのは分かっている。
そんなvirtualenvだが、意識的に使ったことが無い。
使い方の説明でコマンドが書かれていて、そのまま使っている程度だ。


歳を取ってきて覚えるのが面倒になってきているのを実感しているが、それに逆らってみよう。


https://virtualenv.pypa.io/en/stable/

virtualenv is a tool to create isolated Python environments.

isolatedとあるので、分離してくれるのだろう。


pipを使っていたので意識しなかったが、パッケージはどこかにインストールされる。
"site-packages/"というディレクトリらしい。
作っているものによってはバージョンが異なるパッケージを使いたいこともあるだろう。
そういうときの問題を解決できるらしい。


らしい、というのは、私がそういう状況に陥っていないからだ。
まあ、pythonで大したものを作っていないという意味でもあるが。。。


そういえば、最近は提供されるものでpython3を使っていることが多い。
pipがpip3になっているかのように、virtualenvもvirtualenv3なのだろうか?


そもそも、pipはpython2で、pip3はpython3なのだろうか?
python2がインストールされているからpip3になっていて、python3だけだったらpipでよいのだろうか?


https://docs.python.org/ja/3/tutorial/venv.html?highlight=pip#managing-packages-with-pip

ここではpipになっている。
このサイトがそう説明しているなら、そうなのだろう。


https://docs.python.org/ja/3/installing/index.html#installing-index

python3.4からは一緒にインストールされるようになったということは、それ以前はpipを単独でインストールしていることになる。
確かに、apt installした気がする。
Ubuntu16.04からアンインストールしてからpipを使ってみよう。

$ sudo pip install --upgrade pip
The directory '/home/xxx/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
The directory '/home/xxx/.cache/pip' or its parent directory is not owned by the current user and caching wheels has been disabled. check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
Collecting pip
  Downloading https://files.pythonhosted.org/packages/5f/25/e52d3f31441505a5f3af41213346e5b6c221c9e086a166f3703d2ddaf940/pip-18.0-py2.py3-none-any.whl (1.3MB)
    100% |████████████████████████████████| 1.3MB 1.1MB/s 
Installing collected packages: pip
  Found existing installation: pip 9.0.1
    Uninstalling pip-9.0.1:
      Successfully uninstalled pip-9.0.1
Successfully installed pip-18.0

うーん、よくわからん。

$ sudo -H pip install numpy
Requirement already satisfied: numpy in /usr/lib/python3/dist-packages (1.11.0)

これはpython3だろう。


ここで、ようやく--versionオプションに気付いた。

$ pip2 --version
pip 9.0.1 from /usr/local/lib/python2.7/dist-packages (python 2.7)

$ pip3 --version
pip 18.0 from /usr/local/lib/python3.5/dist-packages/pip (python 3.5)

$ pip --version
pip 18.0 from /usr/local/lib/python3.5/dist-packages/pip (python 3.5)

python-pipをアンインストールしてもpip2は使えるらしい。が、pipはpython3のものだ。


https://docs.python.org/ja/3/installing/index.html#installing-index

で、このサイトを見ると、仮想環境を作るのはvenvが標準らしい。
virtualenvはサードパーティの代替え案とのことだ。



https://docs.python.org/ja/3.7/library/venv.html#module-venv

見ながら使ってみよう。
Ubuntu16.04にpython3.5.2がインストールされていたから、3.7にしてみる。

https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa
こういうppaって、何も考えずに信用していいのか悩むな。。

tarでやるのが確実だろうけど、pythonはUbuntuのモジュールも使っているようだから、何か怖い。
pythonのバージョンごと仮想化する方法もあるんだろう。

では、カレントディレクトリに作ってみる。

$ python3 -m venv ./env1
The virtual environment was not created successfully because ensurepip is not
available.  On Debian/Ubuntu systems, you need to install the python3-venv
package using the following command.

    apt-get install python3-venv

You may need to use sudo with that command.  After installing the python3-venv
package, recreate your virtual environment.

Failing command: ['/home/hiroshi/Python/.env1/bin/python3', '-Im', 'ensurepip', '--upgrade', '--default-pip']

えっ、venvは標準じゃなかったの??
釈然としないが、apt installでpython3-venvをインストールしてやり直す。


フォルダができたので、移動。
そこから何をしたらいいのか分からんかったが、bin/activateをたたけば良いようだ。
https://qiita.com/fiftystorm36/items/b2fd47cf32c7694adc2e

そうすると、コマンドプロンプトの先頭に環境名が出てくるようになった。

~/Python/env1$ source bin/activate
(env1) ~/Python/env1$

試しにnumpyをpipでinstallさせてみた。bin/activate前は「既にインストールされています」というようなメッセージだけだったのだが、bin/activate後はちゃんとインストールされた。


あれ、どうやって元に戻ればいいんだ・・・と思ったら、deactivateというコマンドがあるようだ。
これはbin/ ではないし、whichでも見つからなかった。
でも、実行すると元に戻った。


ここまでやって気付いたが、使っているpython3はpython3.5.2のままだった。
どうも、"python3.7"とやらないとそうなるようだ。
そして、python3.7-venvをインストールしないとvenvが作れなかった。

python3.7でvenvを実行すると、その中の実行もpython3.7になるようだ。
バージョンはそれで保てそうだ。

2018/08/19

[nfc][rpi]最近のOM5578/PN7150S

掃除をしていると、Raspberry Pi2が見つかった。
NXPのプレゼントで当選したOM5578/PN7150Sが載ったままだった。
https://hiro99ma.blogspot.com/2017/01/nfcraspiom5578pn7150s-1.html

当時はkernelにドライバを入れるので苦労したのだが、最近は楽になっているかもしれない。
確かめておこう。


https://github.com/NXPNFCLinux/linux_libnfc-nci

現在の最新リリースは、R2.4。
PDFはAN11697


0. Raspberry Piのkernelのクロスコンパイル環境

ページの上の方はセルフビルド、下の方はクロスコンパイルの記述になっているので、下を見る。
.bashrcに設定を書くようになっているが、うちは毎回sourceコマンドで読み込むことにする。

echo export PATH=~/Raspi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin:$PATH > compiler.sh
source ./compiler.sh

1. Raspberry Piのkernelダウンロード

git clone --depth=1 https://github.com/raspberrypi/linux
cd linux

2. ドライバのダウンロード

cd drivers/misc
git clone https://github.com/NXPNFCLinux/nxp-pn5xx.git

3. Makefileの編集

vi Makefile
(obj-がならんでいる一番下に追加)

obj-y               += nxp-pn5xx/

4. Kconfigの変数

vi Kconfig
(sourceがならんでいる一番下に追加)

source "drivers/misc/nxp-pn5xx/Kconfig"

5. Creating the device node

やり方が2種類提示してある。Device treeとPlatform data。
ソースファイルを変更するのは気が進まないので、Device treeにしよう。
と思ったが、どういう設定にすればいいんだ?
ああ、前回と同じ流れじゃないか!

6. ビルド

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2709_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
   (Device Drivers ---> Misc devices ---> < > NXP PN5XX based driver)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs


・・・ここまでやった後で、PDFの「3.2 Alternative to pn5xx_i2c kernel driver」に気付いた。

image

この構成だと、Kernel spaceはI2CとGPIOのドライバで済みそうだ。

https://github.com/NXPNFCLinux/linux_libnfc-nci

src/halimpl/pn54x/tml/i2c/phTmlNfc_alt.h

/* Describe PN71xx connection
  *  0 = Custom configuration
  *  1 = OM557x on Raspberry Pi
  *  2 = OM557x on UdooNeo
  *  3 = OM557x on BeagleBone black
  *
  */

やる気十分じゃないか!
マクロのCONFIGURATIONを変更するだけでよさそうだし、githubに入っているものはデフォルトが1番だから今回は変更しなくて良い。


Raspberry Pi2にセルフビルドさせよう。
Raspbianは2018-06-27のDesktopで、apt update/upgradeも行っておく。
raspi-configなどでI2Cを有効にしておこう。

git clone https://github.com/NXPNFCLinux/linux_libnfc-nci.git
sudo apt install automake autoconf libtool
cd linux_libnfc-nci
./bootstrap
./configure --enable-alt
make
sudo make install

では、デモアプリを動かそう。

nfcDemoApp poll

1回目に実行したときはエラーがたくさん出てしまったが、タグをかざすと読み取っているようだ(エラーによってコンソールが流されて見えん)。
2回目は普通に動いた。
よくわからんが、深追いは止めておこう。


もしかしたら、前回も見逃していただけで、ドライバは何もせずとも動いたのかもしれんな。


私としては、BeagleBone Greenも持っているので、PN7150SとRapberry Pi用のアタッチメントとBeagleBone用のアタッチメントが分かれているとよいのだが、まあ高いものではないから仕方ないのか(Raspiモデルで3500円くらい)。
いつもだとSonyのRC-S620/Sを使うところなのだが、プロトコルを意識せず使用できるというのは強みがある。
まあ、Linuxが載る前提にはなってしまうが。


相手から読まれるだけでよいなら、FeliCa Linkという選択もある。
試作品を作る場合なんかは、Raspberry PiでもFeliCa Linkで済ませていたりする。
ボードにどーん、というのもよいのだが、FeliCa Linkみたいな小ささだと必要なピンだけつなげば良いので、小回りがきくのだ。


image(どーん)


まあ、適材適所ということで。

2018/07/28

[c/c++][mbedtls]ChaCha20-Poly1305サポートとIETF variant (3)

第3弾まで書く気はなかったのだが、ネタは思いついたときに残しておかねば。



libsodiumからMbedTLSに全部置き換えたいと思っている。
別にlibsodiumに不満があるわけではないのだが、使っている理由がChaCha20とPoly1305だけなので、MbedTLSが対応したなら統一したいのである。


ChaCha20-Poly1305については、IETF variantのAPIを使っていたため、MbedTLSと置き換えできた。
しかし、見逃していたのだが、別の箇所でChaCha20を使っていることに気付いた。
こっちは純粋にChaCha20単体で使っていたのだが、nonceが8byteになっているからオリジナルのChaCha20っぽい。


さて、困った。
MbedTLSはChaCha20単体もIETF variantっぽい(nonceが12byteになっていたから)。

もしかしたら同じ結果になるかもしれない、と思ってやってみると、あら不思議、同じになるではないか。
しかし、条件があるに違いない。


libsodiumのChaCha20処理は、このあたりにある。
https://github.com/jedisct1/libsodium/tree/0468e778d229054a966d0fdc629ec8b677bc37c6/src/libsodium/crypto_stream/chacha20


呼ばれているのは、これのようだ。
_ietfが付いているかどうか。
https://github.com/jedisct1/libsodium/blob/bac61ebf506c1b32185a3f3cc6a582445d55bc75/src/libsodium/crypto_stream/chacha20/ref/chacha20_ref.c#L226-L262

この2つの違いはchacha_ivsetup()かchacha_ietf_ivsetup()かだけみたいだ。


IV設定は、ここ。
https://github.com/jedisct1/libsodium/blob/bac61ebf506c1b32185a3f3cc6a582445d55bc75/src/libsodium/crypto_stream/chacha20/ref/chacha20_ref.c#L62-L78

counterのバイト数と、ivのバイト数が違うだけのように見える。


RFC-7539の7ページ目に図が載っている。
https://tools.ietf.org/html/rfc7539#page-7

image

IETF variantは、bが1つ、nが3つになっている。
ということは、オリジナル版は、bが2つ、nが2つということだろう。
それなら、IV設定の関数と整合がとれる。


ということは、これを利用して、MbedTLSでもcounterとnonceの配分をうまくやればいけるのだろう。
stream()ではcounterがNULLになっているから、0扱い。
nonceは8byteだから、何も考えずに置き換えて使えるんじゃなかろうか。
エンディアンがちょっとわからんのだが、必要があれば4バイトシフトさせればいいだけだ。


私が置き換えたかったのはcrypto_stream_chacha20()だったので、前回と同じようにソースを書いてみた。

https://gist.github.com/hirokuma/f9ee0da7bd428458662473964b57fa6c

nonceの前方に4byteの0を入れると同等になるみたいね。

[c/c++][mbedtls]ChaCha20-Poly1305サポートとIETF variant (2)

前回の続きだが、あまり大したことは書けない。

  • MbedTLSにChaCha20-Poly1305がサポートされたけどIETF variantなのか?
  • ソースファイルからするとIETF variantっぽいし、libsodiumのIETF variant APIで同じ結果になった
  • じゃあ、なんで私がやったAPI置き換え版は結果が同じにならないの??


はい、なんとなく予想できたかと思うが「置き換えに失敗していたから」でした・・・。



これだけだとあんまりなので、どう間違えたのか書いておこう。

libsodiumは、エンコードしたデータ+MACをまとまったデータとして扱っていたのだ。
しかし、MbedTLSでは別々に扱っている。
だから置き換える際に、MACのアドレスも指定するし、データ長もMACを外したサイズにせねばならなかったのだ。

そこら辺を間違えていたのですな。
まあ、ありがちといえばありがちだが。

[c/c++][mbedtls]ChaCha20-Poly1305サポートとIETF variant (1)

Mbed TLS 2.12.0, 2.7.5 and 2.1.14 released


いやあ、とうとうリリースされました。

私にとって大きいのは、ChaCha20-Poly1305サポート。
だいたいの部分はMbedTLSで実装していたのだが、ChaCha20-Poly1305だけはlibsodiumを使っていたのだ。
libsodiumを別の環境でビルドするのは難しそうだったのだけど、MbedTLSなら何とかできる気がする!



で、APIを置き換えてみたものの・・・結果が違う。
全部じゃなくて、MACのチェックで失敗するものがあるのだ。


何も考えずに置き換えていたのだが、libsodiumは3種類APIがあるのだ。

Authenticated Encryption with Additional Data using ChaCha20-Poly1305

  • The original construction can safely encrypt up to 2^64 messages with the same key (even more with most protocols), without any practical limit to the size of a message (up to 2^64 bytes for a 128-bit tag).
  • The IETF variant. It can safely encrypt a pratically unlimited number of messages, but individual messages cannot exceed 64*(2^32)-64 bytes (approximatively 256 GB).
  • The XChaCha20 variant, introduced in libsodium 1.0.12. It can safely encrypt a practically unlimited number of messages of any sizes, and random nonces are safe to use.

私が使っていたのは、この真ん中のIETF variantというAPI。
ソースファイルを見比べたのだが、originalのものに似ているが、追加されているような感じがする。

MbedTLSでも同じことができるものかは別として、IETF variantが何なのかを調べておこう。


"variant"は「変形」とか「変種」というような意味。
だから、originalに対してIETFなりの変更を加えたバージョンということだろう。

libsodiumのIETF variant APIページの一番下にリンクがあった。

ChaCha20 and Poly1305 for IETF protocols

IETF variantの仕様らしい。
リンク先は、RFC-7539。

あれー、と思うだろう。
だって、MbedTLSのソースファイルにもRFC-7539と書いてあるのだから。。。



むう、もしかして、APIが違うんじゃなくて、置き換え損なっているのだろうか?
APIが同じ種類かどうかは、テストデータを動かして確認するしかあるまい。



https://gist.github.com/hirokuma/e8d3e68300810d284b7d65df5473fe22

データをMbedTLSのtestから持ってきて、APIはlibsodium 1.0.16で実装。
困ったことに、"ok"が出力されてしまった。


なんで困ったかというと、encryptのテストしかないからだ。
失敗していたのはMACのチェックなので、どちらかといえばdecryptの方なのだ。

これは、もっとちゃんと調べろ、ということなんだろう。

2018/07/21

[git失敗]最新のpushから1つresetして編集

gitで起こしてしまった、現在進行形のお話。


別の場所で作業するので、一旦pushした。
かなり適当な内容なので、pushよりはファイルごとコピーしておきたいくらいだったのだが、面倒になったのでpushした。


で、今見直したところ、あまりにもいい加減な変更だったので、前のcommitは止めようと思った。
が、そのままではダメだけど、書き換えれば使い物にならないわけでもなさそうだったので、差分を見ながら書き換えようとした。
私はvscodeを使っているのだが、差分が見やすいので助かるのだ。


そして、何も考えずにこうやった。

$ git pull
$ git reset --soft HEAD^

そうすると、ソースファイルはpushしたものが残っているが、差分としてはまだ反映されていないような形になるので、最後に変更した箇所が見える。


無事に変更を終え、push。

$ git push

error: failed to push some refs to 'git@github.com:xxxx.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

エラーらしい。
resetしたからだろう。

$ git pull

CONFLICT (content): Merge conflict in xxxxxxx
Automatic merge failed; fix conflicts and then commit the result.

コンフリクトした。

そして困ったことに、オートマージされたので、前回削除したり追加したりしてコンフリクトしなかったところが反映されてしまったのだ。

「gitでやったらいかんこと(その1)」というところか。。。



gitだけで解決できそうだけど、調べるのが面倒だったので、手動で対応させることにした。


まず、今のコンフリクトしたファイルを適当に修正して、別の場所にコピー。
そして、ローカルでやった修正をなかったことにするため、git reset --hard HEAD^。
pushした内容に戻すため、git pull。
そして、その修正はいらないので、git revert HEAD。

あとは、移動させたファイルとオリジナルのファイルをWinMergeで比較しながら手動マージ。
アナログじゃろう?


今回はコンパイルエラーが出たので気付いたが、もっとぼーっとしていたら、オートマージに気付かなかったかもしれない。


さて、今回調べたいのは、どうするのが楽だったのか、だ。
やりたかったのは「最新pushを1つ前に戻しつつ、最新pushのソースも見ながら編集したい」だ。


うん、そうだね。

$ git pull
$ 差分を見たいソースファイルをどこかにコピー
$ git revert HEAD^
$ 差分を見ながら編集

ファイルをどこかに置けばよかっただけか。


ただ、vscode+GitLensだと、履歴と比較する機能がありそうだ。
Working Treeとも比較ができるので、次回はそれを使おう。

image

2018/07/09

[java]64bit値を扱うのにBigIntegerもある

Cからもらったunsignedな64bit値をビット演算するだけでよいかと思っていたら、数値としても扱う箇所があるのに気付いた。

うーん、uint64_tはプリミティブだから、Javaもlongで受け取って、中で数値に変換できないものか。。。


調べていると、BigIntegerというものがあった。
これを使って、引数はlongで受取り、中で16進数文字列に置き換え、それをBigIntegerにすればよさそうだ。

https://gist.github.com/hirokuma/f365c2a85dae3962ff3d5f19562b9029/a0e48bef4148691ce3fcc135c9911662ce21afc3

結果

long: -81985529216486896
long 2byte: -2
BigInteger: 18364758544493064720
BigInteger 2byte: 254

意味も無くビットシフトなんかさせてみたが、ちゃんと正の数として扱っている。

文字列は好きではないのだが、C側でやらなくてよいので、まあよかろう。

2018/07/07

[java]uint64_t相当のデータをビット演算したい

JNIを使って、CとJavaをつなごうとしている。
C側でuint64_tを使っている箇所があるので、Javaで何とかしなくてはならん。


Javaのlongは64bitなのだが、Javaにはunsignedがないので、たぶん最上位ビットが立ってたら負の数と思うだろう。
byteもそうだったし。

確認しておくべし。


longだけではなく、BigDecimalも使ってみよう。

https://gist.github.com/hirokuma/c8a67b1db8e44ce45236942088d81865/1aaa1e1893b3f83f9d1d14d494b1d4902da454ee

同じようなことをさせたいが、BigDecimalにビットシフトがないので、割り算で代用。
処理が遅くなりそうだが、まあ動作確認だからな。

結果

big decimal 2byte: fe
long 2byte: fffffffffffffffe

やはり、そうなるのか。
BigDecimalは演算してるから2byte分きっちりになったが、longはビットシフトで符号が引きずられたんだ。


そういえば、Javaには「>>>」があったな。

https://gist.github.com/hirokuma/c8a67b1db8e44ce45236942088d81865/0161393d1d388b1ad97754dbc84d3efb2eff8370

結果

big decimal 2byte: fe
long 2byte: fe

おお!
なんか、うれしくなりますな。


これはHEXで出しているから、変数として正負どちらになっているかわからん。
数字で出力させて確認。

BigDecimal : 正
longで>>:負
longで>>>:正

まあ、予想通りか。
当然だが、これをbyte型にキャストして代入すると、どれも負の数になる。

2018/06/30

[github]古いgistのURLを知りたい

ちょっとしたソースコードを載せるのに、githubのgistをしばしば使っている。
便利なのだが、編集すると、その前の内容を確認するのが難しい。
いや、差分はわかるのだけど、githubのcommit historyみたいに、その時の内容だけが見たいのだ。


どうやら、その時のハッシュが分かれば載せられるらしい。
例えば、これはzlibサンプルのrevision1のURLだ。

https://gist.github.com/hirokuma/0f038adc04c1ec38ba6c01f8b2db4fb5/4b920d2c12d7893c23a93343cd74d116de731077

URLの最後がハッシュ値なのだが、どうやって取ってくれば良いのだろう?


・・・というのを、長々と調べていた。。。
いや、なに、"Revisions"というタブがあるのは知っていたよ。
そこで差分も出てきているのは知ってる。


ただ、そこに全Revisionの差分が出ているということにまでは気がつかなかったのだ。
下の方にスクロールさせると、過去の差分があるし、Viewボタンまで付いている。
Viewをクリックすれば、そのときのURLが出てくるのだな。


はは、ははは。
私は乾いた笑いを飲み込むしかなかった。

[c/c++]zlibを使ってみよう

zlibを使わなくてはならなくなった。
名前はよく聞く・・・。
リンクするときに「-lz」と短く済むので、印象に残っている。

よく使われているので、使い方も難しくは無いだろう。


WSLのUbuntu 16.04で試す。

sudo apt install zlib1g-dev


zlibのマニュアルは、こちら。

zlib 1.2.11 Manual https://zlib.net/manual.html


ただ・・・圧縮と解凍さえ分かればよいので、使い方だけ見ておこう。
やりたいのは、ファイルではなくRAMで持っているデータなので、そこは読み替えていく。

zlib Usage Example https://zlib.net/zlib_how.html

https://zlib.net/zpipe.c


まず、テストデータを作ろう。
xxdというコマンドを使うと、ファイルをC言語の配列形式にしてくれる。
便利だ・・・。

xxd -i hirokuma.png > hirokuma.h


Exampleでは、CHUNKサイズのバッファin[]とout[]を準備している。
def()が圧縮、inf()が展開。
どちらも、二重のdo-while()になっている。

まず、def()から。
外側のdo-while()は、ファイルをCHUNKずつ読み取るためのループ。
内側のdo-while()は、deflate()を実行して、avail_outが0になっている間はループする。
「keeps calling deflate() until it is done producing output」と書いてあるので、avail_outが0ということは処理が継続しているという意味なのだろう。

圧縮中、deflate()の引数はZ_NO_FLUSHで、最後だけZ_FINISHにする。
ストリーミングで圧縮するから、ある程度データが揃うまで吐き出さないのだろうね。

簡単なサンプルを作った。
圧縮のみで、圧縮しているデータは、うちのアイコン(PNG)だ。
PNG画像もzlibで圧縮されているようだが、気にするまい。

https://gist.github.com/hirokuma/0f038adc04c1ec38ba6c01f8b2db4fb5/4b920d2c12d7893c23a93343cd74d116de731077

image

まあ、データは何でもよいのだが、内側のdo-while()がどういう周り方をするのかが気になった。

$ gcc -o tst zlib_comp.c -lz
$ ./tst
p=0x6010a0
   avail_out=1022
p=0x6014a0
p=0x6014a0
   avail_out=1024
p=0x6018a0
p=0x6018a0
   avail_out=1024
p=0x601ca0
p=0x601ca0
   avail_out=1024
p=0x6020a0
p=0x6020a0
   avail_out=1024
p=0x6024a0
p=0x6024a0
   avail_out=1024
p=0x6028a0
p=0x6028a0
   avail_out=1024
p=0x602ca0
p=0x602ca0
   avail_out=0
   avail_out=0
   avail_out=0
   avail_out=0
   avail_out=0
   avail_out=0
   avail_out=0
   avail_out=103
p=0x603030

next_inは自動的に進むようだから、全データがRAM上にあるなら、初期化だけしておけばよさそうだ。


内側のループが回ったのは、最後だけだ。
何かルールがあるのだろうか?
もし最後だけなら、通常は内側のdo-while無しでもよいのかもしれんが、これだけじゃわからんな。


というわけで、マニュアルを見てみよう。
けっこう、見づらい・・・。

https://zlib.net/manual.html#Basic

If deflate returns with avail_out == 0, this function must be called again with the same value of the flush parameter and more output space (updated avail_out), until the flush is complete (deflate returns with non-zero avail_out).

In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that avail_out is greater than six to avoid repeated flush markers due to avail_out == 0 on return.

avail_outが0の場合は、flushオプションを同じ状態で呼び続けなくてはならん。
ということは、Z_FINISHの場合だけではないということか。


"more output space (updated avail_out)"と書いてあるが、サンプルではavail_outを再設定しているだけで、out[]のサイズは同じだな。
そもそも、fwirte()するサイズは「CHUNK - strm.avail_out」だから、8KBくらいのデータでは外側のループで回っているときはout[]がほぼ出力されず、最後にZ_FINISHしたときに書き込まれていることになる。


flushオプションもいくつか種類があるようだが、圧縮したデータそのものがほしい場合はサンプル通りにやっておけばよいんじゃ無かろうかね。


展開する方も、同じようにやった。

https://gist.github.com/hirokuma/0f038adc04c1ec38ba6c01f8b2db4fb5/2ae53cdeb15bc6e79ed4c65474040a3327b5dbad

$ ./tst
exec: comp
retval=1
exec: decomp
retval=-5
retval=-5
retval=-5
retval=-5
retval=-5
retval=-5
retval=1

追加したのは、decomp()。
-5は、Z_BUF_ERROR

サンプルではエラー処理していないし、最後に1(Z_STREAM_END)が出るところから見ると、普通なんだろう。

Z_BUF_ERROR if no progress was possible or if there was not enough room in the output buffer when Z_FINISH is used.

Note that Z_BUF_ERROR is not fatal, and inflate() can be called again with more input and more output space to continue decompressing.


APIに何があるのか読みづらかったので、こちらの解説記事の方がよさそう。
http://s-yata.jp/docs/zlib/

compress()なんてあったのか・・・。
https://zlib.net/manual.html#Utility

2018/06/24

[excel]自由に線を引きたい

たまにはExcelのことを書こう。


私がExcelを使うのは、文字と画像をまぜこぜにして、なおかつ広いスペースが使えるからだ。
画像も、画像ファイルだけでなく、線画を描くこともできる。


よく使うのは、曲線コネクタだ。

image

直線だと他の図形とぶつかるけど、曲線だと迂回させることもできる。


できるのだが、あんまり自由度がないのが悩みどころだ。
黄色のマーカーを動かすと、少しは形が変わるのだが、そんなに大幅に変わらない。

image image


自分で使う図ならいいのだが、たまにはきれいな形で人に見せたいこともある。
ああ、大きく迂回させたい・・・。


どうしてもExcelを使って、迂回したような線を引きたい場合は、こういう技もある。

image


何のことはない、図形と直接コネクトさせるのではなく、別の図形にコネクトさせてしまうのだ。
ここでは、円にコネクトさせている。

image


円じゃなくて、四角形の方がうまくいくかも?
この辺は、倍率で見た目が変わったり、線の太さで影響がありそうなので、あれこれやってみるのがよかろう。

image

ただ、中継点のところが薄くなってしまうようだ。
image


曲線を使えば期待した線を描きやすいのだけど、あれは後から変更できないみたいだし。
うーむ。


他のお絵かきツールを探していると、draw.ioというものが紹介されていた。

https://www.draw.io/

image

ふむ、これは自由度が高い。
やはりExcelっぽくない絵にしたいときは、Excelじゃないツールを使うのがよいな。

2018/06/17

[wireshark]npcapを使う

Windows版のWireSharkを久々に起動させ、最新版にした。
そのせいかどうかわからないが、起動しない。。。
プログレスバーの途中、pluginsがどうのこうのというところまで文字が出て、そこから進まない。

アンインストールしてやり直したり、ユーザデータを消したりしたが、関係なさそう。


https://osqa-ask.wireshark.org/questions/48178/wireshark-fails-to-start-on-windows-10

WinPcapだと動かない人もいるみたいだから、npcapにしてみるといいかもね、というところか。

WinPcapごとアンインストールし、npcapのWindowsインストーラを https://nmap.org/npcap/ から探して(今は0.99-r6が最新のようだ)、インストールしてPCを再起動。

そうすると、WireSharkが起動した。


うまくWinPcapでうまくいかないときにPC再起動はさせていないので、もしかしたら再起動だけで直ったかもしれない。
まあ、npcapだとWindowsでもlocalhostを見ることができるから、ちょうどよいだろう。

2018/06/09

[intellij]gitignoreをどうするのか(2018/06/09)

3箇所にあるPCで、Javaの開発・・・・というほどでもないことをやっている。
どのPCで作業するかわからないので、GitHubにプロジェクトを突っ込んでいるだけだ。

2台はIntelliJ、1台はAndroid Studioを使っている。
Android Studioである必要はないのだが、IntelliJとAndroid Studioをわざわざインストールするのもなぁ、ということで、試しにやっている程度だ。


C/C++のイメージでやっていたので、ソースファイルをgitに載せていればなんとかなるんだろう、と思っていたのだが、そうではないらしい。
とにかく、どの環境で作業しても、しょっぱなからgitに差分があるのだ。
ソースファイルだったらよいのだけど、設定ファイルが変わっている。
変わるというよりは、ファイルごと削除になっていることが多い。
作業するディレクトリが違うからかと思ったのだが、どうもそれだけでもなさそう。


腹が立ってきたので、調べることにした。


まずは、直近の記事を検索。

JetBrains IDE(IntelliJ IDEAなど)を使う時のgitignoreについて
https://qiita.com/mokrai/items/1f668dd0d1bee15b56f0

記事の中のリンクに、JetBrainsの記事がある。
Updatedが2018/06/08なので、定期的に更新されるページなのだろう。


「.idea」というディレクトリがあるかどうかで、対応が変わるらしい。
うちのにはあるので、そっちを読む。

  • .ideaディレクトリの中の全部だけど、workspace.xmlとtasks.xmlは除外する
  • 拡張子が.imlのもの(IntelliJ IDEAが反映したもの : applies to IntelliJ IDEA)

意外と、シンプルな方針だ。
Qiitaの人が読んだときと、まだ変わっていないようだ。


それ以外に「Be careful」と「not to shared」も書いてある。
「Be careful」はパスワード関連のようだからよいとして、「not to shared」だ。

  • 拡張子が.imlのもの(GradleやMavenがベースになったプロジェクト)
  • gradle.xml
  • dictionaries
  • .idea/libraries以下にある拡張子が.xmlのファイル(GradleやMavenが生成したもの)


まてやー!
.imlでも、共有したりしなかったりがあるのかぁぁぁぁ。


これ、ちょっと、無理じゃないかい?
今の時点でGitHubのJetBrains gitignore例は2018/May/10で、JetBrainsの記事はJune/08だから、まだそっちには反映されていないのかもしれないし、今のままでもよいのかもしれない(QAを全部読む気力は無いのだ)。
読み間違えているかもしれんので、画像を残しておこう。


image


で、だ。


いま、別の場所のIntelliJで作業して、commit / pushしたものを、また別の場所で開いている。
こちらもIntelliJだ。

.idea/libraries/*.xmlは、書いてあるとおりに除外されている。
除外というか、削除されたことになっている。
.gitignoreの「.idea/**/libraries」が効いているのだろうか?
いや、私はその行を削除していたな。


ただ、JetBrainsが言う通りに共有されていないにしても、どうしてよいのか困る。
プロジェクトの設定でDependenciesを見ると、そいつらが名前だけ載っていて、赤文字になっているのだ。
いかにも「そのファイルはありません」みたいな感じで、ビルドも失敗する。
復活させればビルドできるから、その行をコメントアウトしたこと自体がダメだったのか。


なんとなくだが、変なことをしていなければ、Mavenだか何だか知らないが、持っていないライブラリは勝手にダウンロードしてくれているようだ。
プロジェクトを開いたときに妙に時間がかかることがあるから、あれはそうなんだろう。



今月はまだJavaを扱わないといけないようだから、急いで結論を出すことはあるまい。

[java][ubuntu]bitcoinjをコンソールでビルドしてみよう

bitcoinjをビルドしようとした。
環境としてJDKはインストールされていて、Android Studioもあったので、それからできるだろうと思った。

が、protobufのコンパイラが入っていないので、途中でエラーになってしまった。
Windows向けのprotocもありそうだったが、そこまでしてビルドしたいという気分でもない(なんだそりゃ?)。

READMEを見ると、Gradleでビルドするのが普通のようなのだ。
かといって、Windows環境にGradleだけインストールしたいという気分でもない。
Android Studioのところからパスを通せば良いのだろうが、結局はprotobufがいるだろう。


ごにょごにょ書いたが、Azure上にUbuntu 16.04 VMを立てているので、そっちでビルドできるようにしておいた方が後々いいんじゃなかろうかということで、やってみよう。


Gradleのインストール方法を見ると、SDKMAN!というものをインストールするのが楽らしい。
うちのUbuntuにzipやunzipが入っていなかったので、curlするスクリプトがエラーを返してくれた。
足りないものを入れていけばインストールできるだろう。

sudo apt install openjdk-8-jdk-headless zip unzip (他にいるもの...)
curl -s "https://get.sdkman.io" | bash
source "/home/xxx/.sdkman/bin/sdkman-init.sh"
sdk install gradle 4.8


protobufはaptでもいけそうだが、後々使いそうな気もするので、今の時点で最新のv3.5.1から持ってこよう。

mkdir protobuf
cd protobuf
wget https://github.com/google/protobuf/releases/download/v3.5.1/protoc-3.5.1-linux-x86_64.zip
unzip protoc-3.5.1-linux-x86_64.zip
export PATH=$HOME/protobuf/bin


protocにパスが通っているのを確認したら、bitcoinjをビルドしよう。

git clone https://github.com/bitcoinj/bitcoinj.git
cd bitcoinj
gradle clean build
....

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':wallettemplate:compileJava'.
> Compilation failed; see the compiler error output for details.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 1m 56s
24 actionable tasks: 21 executed, 3 up-to-date


エラーで終わってしまった。。。
が、findでjarファイルを探すとできていたから、いいや。


これを書いている時点での最新リリースバージョンはv0.14.7だが、masterはそれよりも進んでいる。
構成に変更があるようで、例えばorg.bitcoinj.core.ScriptExceptionorg.bitcoinj.script.ScriptExceptionになっていた。

まあ、私も「使い始めました」という程度なので、何がどう変わったのかという以前に、使い方を覚えねば。。。
しかし、ExamplesのawaitRunning()が動かないので、最新版は私には無理かもしれんな。

・・・guavaのバージョンが古かっただけみたい。
しくみが、しくみが分かりません。。。

[win10][office]PNGのサムネイルが表示されない(未解決)

会社で使っているWindows10だが、Excelでファイル選択して画像を貼り付けたいことがある。
その際、ファイル選択するダイアログが表示されるのだが、画像ファイルのサムネイルが表示されない。
以前は気にしていなかったので、何かの際にそうなってしまったようだ。


Explorerの設定をいじったのかな、と思ったが、そうでもない。
そして、Excelだけでなく、Wordもダメだった。
かといって、全部がダメになったわけではない。ダメなのはOffice系だけのようだ。


不思議なことに、一度Explorerでサムネイルを表示させたファイルであれば、Officeでもサムネイルが表示されている。
キャッシュされたものであればOKなようだ。


ネットで検索して、現象として一番近いのはこちら。
Office系だけPNGサムネイルが表示されないのだが、解決案が「別のアカウントを使う」らしい。

https://answers.microsoft.com/ja-jp/windows/forum/windows_10-files/png%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE/3ae450be-e0c4-4fa1-a3b1-3de68f41ea4e

いやー、ちょっと待ってくれー、といいたい。
特定は難しいのかもしれないけど、多少は代替え案を出してほしいところだ。


この問題の難しいところは、Explorerの設定でサムネイルを表示させないようにできることだ。
検索すると、それが大量に引っかかってしまい、今回の現象にたどり着けないのだ。
上記で質問された方も、さんざん検索して見つからずに質問されたのではなかろうか。



日本語の検索だとらちが明かないので、英語で探してみた。
https://answers.microsoft.com/en-us/windows/forum/windows_10-files/some-jpg-and-png-thumbnails-disappeared/d2b83204-a38f-4fdd-aa32-74481923f4a7


これを書いているPCでは試せないので、次回会社に行ったときにやってみよう。


2018/06/02

[bitcoij]IntelliJでbitcoinjを使ってみよう

「唐突」の「唐」は、隋の次の唐だろうか?
隋の時代が長いこと続いていたのに、いきなり唐の時代に突入したとか、そんなことだろうか。


というわけで、今回は唐突にbitcoinjを使ってみようと思う。
どちらかといえば、IntelliJの設定方法を忘れそうなので、それをメモとして残しておきたいだけなのだがね。


まず、IntelliJと、JDK8をインストールする。
環境変数JAVA_HOMEなんかも設定しないといかんのかもしれんな。
今回は、jdk 1.8.0 172だ。


IntelliJの設定も、適当にやっておこう。
うちのWindows環境では年に数回しかIntelliJを起動しないので、起動するたびに最新版へのアップデートがいる。
今回は、2018.1だ。



当然、IntelliJの使い方も把握していないのだが、雰囲気でやっておこう。

まず、新規でプロジェクトを作る。
テンプレートからコンソールを選択しておく。


本題であるbitcoinjを追加。
どうやるのが簡単なのか分からんが、「File > Project Structure ...」でModulesを選び、Dependenciesタブから追加した。

  1. 右側の「+」をクリック
  2. 「2. Library...」「From Maven...」
  3. 出てきたダイアログから検索できるので、bitcoinj-coreを検索して、追加
  4. 同じようにして、slf4j-nopを検索して、追加

image

slf4j-nopではなく、slf4j-simpleでもよい。
デバッグ時にはそっちの方が良いのかもしれん。
両方置いておくと、このリストで上にある方が選ばれるようだ。
ただ「こっちを選んだ」みたいなログが出てうっとうしいので、片方だけ見えるようにしておいた方が良かろう。
Scopeを「Test」にしたのは、後でまた検索し直すのが面倒だったので、リストに置いておくためだ。


最後に、適当にコードを書いて実行。
というと、がんばって調べたように見えるが、こちらを読んだだけだ。

bitcoinjの入門 - Bitcoin blog
http://adrenaline2017.hatenablog.com/entry/2017/11/23/000000


オリジナルは、本家のサイトだろうが、日本語になっているだけでありがたい。

Getting started in Java
https://bitcoinj.github.io/getting-started-java

01: package com.hiro99ma.blogpost.bitcoinj_test01; 02: 03: import org.bitcoinj.core.Address; 04: import org.bitcoinj.core.ECKey; 05: import org.bitcoinj.core.NetworkParameters; 06: import org.bitcoinj.kits.WalletAppKit; 07: import org.bitcoinj.params.TestNet3Params; 08: 09: import java.io.File; 10: 11: class BitcoinjTest { 12: private NetworkParameters params; 13: private WalletAppKit wak; 14: 15: BitcoinjTest() { 16: params = TestNet3Params.get(); 17: wak = new WalletAppKit(params, new File("./wallet"), "wak") { 18: @Override 19: protected void onSetupCompleted() { 20: if (wallet().getKeyChainGroupSize() < 1) { 21: wallet().importKey(new ECKey()); 22: } 23: } 24: }; 25: wak.startAsync(); 26: } 27: 28: void exec() { 29: wak.awaitRunning(); 30: Address addr = wak.wallet().currentReceiveAddress(); 31: System.out.println("addr = " + addr.toString()); 32: } 33: } 34: 35: public class Main { 36: public static void main(String[] args) { 37: BitcoinjTest bjt = new BitcoinjTest(); 38: bjt.exec(); 39: } 40: }


着金アドレスをコンソールに出力させるだけだ。
あれこれやりたかったら、Examplesを見るのが早いだろう。
IntelliJの設定が分かったので、私は満足した。。。

[lmdb][win10]もう少しlmdbがうまく動作しない件を調査

以前、こういう記事を書いた。


hiro99ma blog: [lmdb][win10]Windows10 1803でWSLのファイル書込みがうまく動いてない?(2018/05/05)
https://hiro99ma.blogspot.com/2018/05/lmdbwin10windows10-1803wsl20180505.html


今日(2018/06/02)もまだ発生している。
Ubuntu 18.04も試してみたが、同じだ。
だから、これはOSよりもWSL側に何かあるんじゃなかろうかと考えた。

解決できないだろうけど、せっかくなので、もう少し調べてみよう。


前回からgistの内容を変更しているので、再掲しておこう(Revision 3)。

https://gist.github.com/hirokuma/4ea889b2a596c59ef4f862e9df54e469


これを実行させると、初回から3秒くらいまでの間はL.95でエラーが出力されている。

test1(): OK
ERR(95): MDB_BAD_TXN: Transaction must abort, has a child, or is invalid

前回のブログではmdb_put()でエラーになったと書いているが、mdb_get()だな。
勘違いして書いたのか、現象が変わったのか・・・。
mdb_put()がうまくいっていないから、mdb_get()に失敗したといいたかったのかもしれんが、よくわからんな。


まあ、よかろう。
mdb_get()で失敗しているのだ。


まず、実行するディレクトリを変えてみた。
前回動かしたときは、Windowsで作業しているディレクトリだったのだ。
もしかすると。WSLで管理しているディレクトリじゃないとうまくいかない、とかあるのかもしれん。

が、これは関係なさそうだった。
「~/」に移動させて実行したが、結果は同じだ。



次は、失敗したときと成功した時をstraceで眺めた。

失敗時の出力。
どのくらい抜粋すればよいかわからないので、いいところが載ってないかも。

mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x7f2044f1d000
open("testdb/data.mdb", O_RDWR|O_CREAT, 0644) = 4
fstatfs(4, {f_type=0x53464846, f_bsize=4096, f_blocks=58348607, f_bfree=35725114, f_bavail=35725114, f_files=999, f_ffree=1000000, f_fsid={1, 0}, f_namelen=255, f_frsize=4096, f_flags=1056}) = 0
pread64(4, "\0\0\0\0\0\0\0\0\0\0\10\0\0\0\0\0\336\300\357\276\1\0\0\0\0\0\0\0\0\0\0\0"..., 152, 0) = 152
pread64(4, "\1\0\0\0\0\0\0\0\0\0\10\0\0\0\0\0\336\300\357\276\1\0\0\0\0\0\0\0\0\0\0\0"..., 152, 4096) = 152
mmap(NULL, 1048576, PROT_READ, MAP_SHARED, 4, 0) = 0x7f20440e0000
open("testdb/data.mdb", O_RDWR|O_DSYNC) = 5
fcntl(3, F_SETLK, {l_type=F_RDLCK, l_whence=SEEK_SET, l_start=0, l_len=1}) = 0
write(2, "test1(): OK\n", 12test1(): OK
)           = 12
lseek(4, 16384, SEEK_SET)               = 16384
writev(4, [{"\4\0\0\0\0\0\0\0\0\0\2\0\22\0\302\17\302\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\5\0\0\0\0\0\0\0\0\0\2\0\22\0\356\17\356\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\6\0\0\0\0\0\0\0\0\0\2\0\22\0\330\17\330\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}], 3) = 12288
fdatasync(4)                            = 0
pwrite64(5, "\0\0\20\0\0\0\0\0\0\20\0\0\10\0\1\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0"..., 120, 32) = 120
write(2, "ERR(95): MDB_BAD_TXN: Transactio"..., 73ERR(95): MDB_BAD_TXN: Transaction must abort, has a child, or is invalid
) = 73


成功時。

mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x7f4d72ec0000
open("testdb/data.mdb", O_RDWR|O_CREAT, 0644) = 4
fstatfs(4, {f_type=0x53464846, f_bsize=4096, f_blocks=244189951, f_bfree=103944675, f_bavail=103944675, f_files=999, f_ffree=1000000, f_fsid={1, 0}, f_namelen=255, f_frsize=4096, f_flags=1056}) = 0
pread64(4, "\0\0\0\0\0\0\0\0\0\0\10\0\0\0\0\0\336\300\357\276\1\0\0\0\0\0\0\0\0\0\0\0"..., 152, 0) = 152
pread64(4, "\1\0\0\0\0\0\0\0\0\0\10\0\0\0\0\0\336\300\357\276\1\0\0\0\0\0\0\0\0\0\0\0"..., 152, 4096) = 152
mmap(NULL, 1048576, PROT_READ, MAP_SHARED, 4, 0) = 0x7f4d720e0000
open("testdb/data.mdb", O_RDWR|O_DSYNC) = 5
fcntl(3, F_SETLK, {l_type=F_RDLCK, l_whence=SEEK_SET, l_start=0, l_len=1}) = 0
write(2, "test1(): OK\n", 12test1(): OK
)           = 12
lseek(4, 8192, SEEK_SET)                = 8192
writev(4, [{"\2\0\0\0\0\0\0\0\0\0\2\0\22\0\302\17\302\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\3\0\0\0\0\0\0\0\0\0\2\0\22\0\356\17\356\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}], 2) = 8192
pwrite64(4, "\n\0\0\0\0\0\0\0\0\0\2\0\24\0\240\17\320\17\240\17\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096, 40960) = 4096
fdatasync(4)                            = 0
pwrite64(5, "\0\0\20\0\0\0\0\0\0\20\0\0\10\0\1\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0"..., 120, 32) = 120
write(2, "test2(): OK\n", 12test2(): OK
)           = 12


失敗straceログは、2回目の実行を載せている。
初回だとディレクトリ作成やファイル作成に違いが出てきてわかりにくいからだ。

文字色を付けたところが、コンソールログ("write"で始まる行)。
背景色を付けたところは、違いがありそうな箇所。


失敗時は、ファイル4へのpwrite64()が発生していない。
fd=4は、最初に開いた方のtestdb/data.mdbだ。。。あれ、fd=5もtestdb/data.mdbだな。
同じファイルをオープンしているのか?

2回目のopen()にはO_DSYNCが指定されていた。
「ファイルに対する書き込み操作は、同期 I/O のデータ完全性完了の要件に基づいて行われる」らしい。
https://linuxjm.osdn.jp/html/LDP_man-pages/man2/open.2.html


・・・などと、違いがあるところばかり気にしていたが、test1()でOKを出力させる前のところでファイル書込みをしてないよね?
mdb_put()に相当する処理がスキップされている??
既にデータがあるから、書き込む前にデータを比較して、同じだからスキップされたのかもしれない。
まあ、ストレージへの書込みはコストが高いので、さもありなん。


そしてもう1つ。
成功する状態でいくつかstraceを取ってみたが、lseek()されるサイズは16384の場合もあったし、違うサイズの場合もあった。
その際、背景色が緑の出力は行われていなかった。。。


何が起きているのか、さっぱりわからんな。


WSLではないLinuxでのstraceを見よう。


mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x7f10690bc000
open("testdb/data.mdb", O_RDWR|O_CREAT, 0644) = 4
fstatfs(4, {f_type="EXT2_SUPER_MAGIC", f_bsize=4096, f_blocks=7607162, f_bfree=594658, f_bavail=590562, f_files=3840000, f_ffree=2984847, f_fsid={-2044918571, 1650642210}, f_namelen=255, f_frsize=4096, f_flags=4128}) = 0
uname({sysname="Linux", nodename="lntest1", ...}) = 0
pread64(4, "\0\0\0\0\0\0\0\0\0\0\10\0\0\0\0\0\336\300\357\276\1\0\0\0\0\0\0\0\0\0\0\0"..., 152, 0) = 152
pread64(4, "\1\0\0\0\0\0\0\0\0\0\10\0\0\0\0\0\336\300\357\276\1\0\0\0\0\0\0\0\0\0\0\0"..., 152, 4096) = 152
mmap(NULL, 1048576, PROT_READ, MAP_SHARED, 4, 0) = 0x7f106839c000
open("testdb/data.mdb", O_RDWR|O_DSYNC) = 5
fcntl(3, F_SETLK, {l_type=F_RDLCK, l_whence=SEEK_SET, l_start=0, l_len=1}) = 0
write(2, "test1(): OK\n", 12test1(): OK
)           = 12
lseek(4, 16384, SEEK_SET)               = 16384
writev(4, [{"\4\0\0\0\0\0\0\0\0\0\2\0\22\0\302\17\302\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\5\0\0\0\0\0\0\0\0\0\2\0\22\0\356\17\356\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\6\0\0\0\0\0\0\0\0\0\2\0\22\0\330\17\330\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}], 3) = 12288
fdatasync(4)                            = 0
pwrite64(5, "\0\0\20\0\0\0\0\0\0\20\0\0\10\0\1\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0"..., 120, 32) = 120
write(2, "test2(): OK\n", 12test2(): OK
)           = 12


fd=4へのpwrite64()呼び出しは、起きたり起きなかったりで、これはWSLと同じだ。
連続して実行するとpwrite64()が呼び出されないようなので、キャッシュに吐き出されるまでは呼び出していないのかもしれん。


WSLで失敗したとき。

lseek(4, 16384, SEEK_SET)               = 16384
writev(4, [{"\4\0\0\0\0\0\0\0\0\0\2\0\22\0\302\17\302\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\5\0\0\0\0\0\0\0\0\0\2\0\22\0\356\17\356\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\6\0\0\0\0\0\0\0\0\0\2\0\22\0\330\17\330\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}], 3) = 12288
fdatasync(4)                            = 0
pwrite64(5, "\0\0\20\0\0\0\0\0\0\20\0\0\10\0\1\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0"..., 120, 32) = 120
write(2, "ERR(95): MDB_BAD_TXN: Transactio"..., 73ERR(95): MDB_BAD_TXN: Transaction must abort, has a child, or is invalid
) = 73


Linuxで成功したときの例。

lseek(4, 16384, SEEK_SET)               = 16384
writev(4, [{"\4\0\0\0\0\0\0\0\0\0\2\0\22\0\302\17\302\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\5\0\0\0\0\0\0\0\0\0\2\0\22\0\356\17\356\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\6\0\0\0\0\0\0\0\0\0\2\0\24\0\240\17\320\17\240\17\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}], 3) = 12288
fdatasync(4)                            = 0
pwrite64(5, "\0\0\20\0\0\0\0\0\0\20\0\0\10\0\1\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0"..., 120, 32) = 120
write(2, "test2(): OK\n", 12test2(): OK
)           = 12


lseek()の結果と、pwrite64()の一番最後にある引数は毎回変わるので、Linuxの方は何度か実行して同じような出力を探した。


つまり、straceの結果からでは違いが分からんということだ。


まだだ・・・今のわしには力が足りんのだ。。。

2018/05/26

[c/c++]socketのshutdown()だけでは足りない

socketを使った、サーバっぽいアプリを作っている。

長時間動かしていると、なんか調子が悪い・・・。
メモリの使用量なんかも問題ないし、負荷が高いわけでもない。

なんとなくnetstatで見てみると、大量のFIN_WAIT2が残っていた。
なんだこれは・・・。


原因は、shutdown()だけしかしていないせいだったようだ。
close()したら、解決したようである。


い、言い訳をさせてください!


システムコールの本を読んでいて、shutdown()のところで「close()やプロセス終了でも閉じられるので、shutdown()を省略することもあります」みたいな説明が書いてあったのだ(手元に本がないので、私の記憶では、だが)。

そこを、何を勘違いしたのか「じゃあ、close()を書かずにshutdown()でもいいんだ」と解釈したのである。
冷静に考えれば、close()だけすればいいやん、と思いそうなのだが、そのときの私の心理を解説できるものはどこにもいない。。。



その前も、cURLでcurl_easy_init()とcurl_easy_cleanup()を繰り返していたら、TIME_WAITが大量に残るという現象を味わったばかりだった。
そのときは、接続先が1つしかなかったので、最初にinitしてハンドルをもらってきて、ずっと使い回すことで対応できたようだ。
サイトにも「You can do any amount of calls to curl_easy_perform while using the same easy_handle.」と書いているし、そのやり方でいいんだろうと思っている。

しかし、毎回異なるところへ接続するようだったら、また別のやり方を考えねばならんだろう。


socket関連って、原因に思い当たるノウハウがないと、なかなかつらいですな。

2018/05/20

[git]ブランチを消すとcommit履歴も消える

以前、gitでちまちまcommitするので、Pull Requestしてマージするときにまとめたい話をした。

http://hiro99ma.blogspot.com/2018/05/git-merge-squash.html


merge --squashするときれいになったので、こりゃいいや、と使っていた。

それはよかったのだが、てっきりこう思っていたのだ。

Pull Request用に作ったブランチにcommitがまとまっただけで、ローカルにはcommit履歴が残っている

しかし、ひさびさにcommit履歴を見てみると、squashでまとめたものしか残っていない。。。
あれ、commitの履歴って、全部残っているものではないの??


わからないときは、実験だ。


$ git init
$ touch test.txt
$ git add test.txt
$ git commit -m "first"
$ git checkout -b work/aaa
$ (test.txtを編集)
$ git commit -a -m "hello"
$ (test.txtを編集)
$ git commit -a -m "world"
$ (test.txtを編集)
$ git commit -a -m "morning"

image

$ git checkout master
$ git checkout -b aaa
$ git merge --squash work/aaa
$ git commit -m "squash merge"

image

$ git branch -D work/aaa

image


ああ、消えた!!!


$ git checkout master
$ git merge aaa
$ git branch -d aaa

image


むう、ブランチaaaがあったことすらわからないのか。
reflogには残っているけど、手元に残っているだけだろう。

image


fast-forwardだったから、ログに残っていないのだろうか?


fast-forwardを無効にしてやってみよう。

$ git config merge.ff false
(同じ手順でやっていく)
$ git merge --squash work/aaa
fatal: You cannot combine --squash with --no-ff.

ダメやん。

$ git merge --ff --squash work/aaa

これならできたけど・・・branchを消すと当然同じ結果になった。



そもそも、私は何がしたかったのかというと、以下を何とかしたかったのだ。

  • mergeしたという記録が見えない
    • ログで残すしかない?
  • --squashしたcommitの履歴が残らない
    • squashしたブランチを消さない?


--squashした記録は、ログで残すしかなさそうだ。
上でやった手順では commit -mとコメントを指定したのだが、-m なしで実行するとエディタが起動して、squash対象のcommit logを自動的に埋めん込んでくれていた。

ただ、これはこれで悩ましいな。
commitを頻繁にするのは、細かく履歴を残しているときもあるが、別の環境に移動する際にgit経由でやる場合もあるからだ。
「家に帰るのでcommit」とかも、しばしば。

まあ、ログを編集すれば良いのだけど、それだったら自分で「mergeした」と入れるだけでもよい気がする。


それよりも、問題は2番目だ。
commitはgitのどこかに必ず残っていて、ブランチはcommit idをつなげているだけだと思っていたのだ。
しかし、ブランチを削除してcommit履歴も消えると言うことは、commitはブランチに紐付いているということになる。
今までsquashせずにmergeしていて、commit履歴が全部残っていたので、そう勘違いしていたのだろう。


ブランチを消さなければ残るのだろうが、私はそこまでして履歴を残したいのだろうか?
「この行は何のためにこうしたんだっけ?」という確認をしたいときに見るかもしれないけど、そもそも記憶にないのにcommitログを見ただけでわかるとは思えない。
それよりは、mergeする単位を大きくしすぎないというか、1つの目的のためにmergeするようにしておけばよいのではなかろうか。


issueを上げて対応するためにブランチを作っていたのだけど、複数の問題が絡んでいる場合がある。
そうすると、1つのブランチでの作業が大きくなってしまう。。。

そんなときは、気付いた時点でさらにブランチを作っておけばよいのかもしれん。
あれこれ試して、自分というかチームにあう運用スタイルを作っていくしかなかろう。