Srook range adaptor and Srook range pipe algorithm
※ 2018/3/11 ライブラリ整理より当エントリ内で紹介されている一部機能は削除した。
Srook Range AdaptorとSrook Range Pipe AlgorithmはSTLアルゴリズムとその他諸々の機能をイテレータアダプタによる効率的なアクセスと、パイプライン記法をサポートさせたC++14自作ラッパーライブラリである。Srook Range Adaptorは基本的に、boost range adaptorsと互換性があるが、boostには搭載されていないアダプタもある。例えば:
std::vector<int> v{2,2,2,2,2,2,3,3,3,4,5,6,7,8}; std::vector<int> result = v | boost::adaptors::replaced(2,4) | srook::adaptors::remove_copied(3); // 2を4に置換し3を排除したものをコピーする result | srook::pipealgo::print(); // 4 4 4 4 4 4 4 5 6 7 8
auto v = boost::irange(0x41,0x47) | srook::adaptors::to_range<std::vector>(); // boost::irangeと併用可能 auto li = v | srook::adaptors::to_range<std::list>(); // リストへ auto t1 = v | srook::adaptors::to_range<std::tuple,6>(); // タプルへ auto str = v | srook::adaptors::to_range<std::basic_string>(); // std::stringへ auto ar = v | srook::adaptors::to_array<std::array,6>(); // std::arrayへ auto str1 = v | srook::adaptors::to_string<std::wstring>(); // std::wstringへ auto str2 = v | srook::adaptors::to_string<std::u16string>(); // std::u16stringへ auto str3 = v | srook::adaptors::to_string<std::u32string>(); // std::u32stringへ
またSrook Range Pipe AlgorithmはSTLアルゴリズムを全てパイプライン記法で記述できるようにしたもの。よってイテレータアダプタの同機能とは、処理内容が異なる。例えば:
std::vector<int> v = boost::irange(0,9) | srook::adaptors::copied; v | srook::adaptors::reversed | srook::adaptors::print(); // 8 7 6 5 4 3 2 1 0 v | boost::adaptors::reversed | srook::adaptors::print(); // 8 7 6 5 4 3 2 1 0
v | srook::pipealgo::reverse | srook::adaptors::print(); // 8 7 6 5 4 3 2 1 0
srook::adaptors::reversed
とboost::adaptors::reversed
の動作は同じ。具体的に述べれば、イテレータによって逆順にアクセスしていくため、範囲そのもの(上記の場合v
)の内部要素らがreverseされるわけではない。しかし、srook::pipealgo::reverse
はイテレータによる逆順アクセスではなく、std::reverseを範囲に適用した形となる。std::reverse
アルゴリズムの戻り値は原則void
であるが、便宜的に渡された範囲の参照を返すようにしている。
また、メンバとして同名の関数を持っていた場合はその関数を呼び出すようにしている。例えば:
#include<srook/range/adaptor/find.hpp> std::string str("That is so good C++"); std::size_t pos = str | srook::pipealgo::find("good"s); // like that: str.find("good") std::size_t pos1 = str | srook::pipealgo::find("C++"s,pos); // like that: str.find("C++",pos) std::size_t pos2 = str | srook::pipealgo::find("C++"s,pos,"C++"s.size()); // like that: str.find("C++",pos,"C++"s.size()) std::vector<int> v{1,2,3,4,5,6,7,8}; decltype(v)::const_iterator it = v | srook::pipealgo::find(6); // like that: std::find(v.cbegin(),v.cend(),6)
#include<srook/range/adaptor/find_first_of.hpp> using namespace std::string_literals; std::vector<int> v(10); std::vector<int> hs(3); std::string str="hoge foo"; std::string search="hoge"; boost::range::iota(v,0); boost::range::iota(hs,4); const auto f=[](decltype(v)::value_type x,decltype(v)::value_type y){return x<y;}; decltype(v)::const_iterator it1 = v | srook::pipealgo::find_first_of(hs); decltype(v)::const_iterator it2 = v | srook::pipealgo::find_first_of(hs.cbegin(),hs.cend()); decltype(v)::const_iterator it3 = v | srook::pipealgo::find_first_of(hs,f); decltype(v)::const_iterator it4 = v | srook::pipealgo::find_first_of(hs.cbegin(),hs.cend(),f); std::string::size_type result1 = str | srook::pipealgo::find_first_of(search); std::string::size_type result2 = str | srook::pipealgo::find_first_of(search.c_str(),4); std::string::size_type result3 = str | srook::pipealgo::find_first_of(search.c_str(),4,search.size());
全てのコードは以下のテストコードによりテストされており、幾つかのci環境でテストコードを走らせている。サポート環境は
- boost lib version 1.63.0 or above later
- GCC 6.2.0 or above later
- Clang 3.7.0 or above later
であるが、OSはLinux環境が最も好ましい。 大体の使い方も、以下のテストコードで示されている。
Samba + DAAPとnetdataの導入
何度かサーバーを弄っているエントリーをアップロードしてきたが、自宅には外付けの3TBHDDに動画や音楽などのメディアファイルを全て詰め込んでおり、それを今まではメイン機のデスクトップに接続して使ってきた。しかし、どうせならサーバーに接続してそこから再生できるようにするのが良いだろうと思い、Samba & DAAPサーバーを既存のサーバー上に構築する事にした。 今回ファイル共有をしたいのは外付けのメディアデータであり、また今までメインマシン(Windows機)で使用していた事もあって、ファイル形式はexFatである。
また、netdataというブラウザからサーバーマシンの負荷度などを参照する事のできるツールを発見したので、導入する事とした。本エントリは、その作業ログである。
尚、自宅サーバー構築の今までの投稿は以下の通りである。
roki.hateblo.jp roki.hateblo.jp roki.hateblo.jp
exFatをsambaで共有する
必要なツールの導入
% sudo apt -y install samba samba-common exfat-fuse % service smbd status
ターゲットメディアを挿しこみマウントする。
% sudo mkdir /home/roki/ShareData % sudo fdisk -l % sudo mount -t exfat /dev/sdc3 /home/roki/ShareData -o umask=000 % df -k
マウント時、exFatファイルシステムを考慮して、上記のようにumask=000オプションを設定する必要がある。 サーバーなのであまり再起動させることもないが、一応fstabを記述しておく。
% sudo echo "/dev/sdc3¥t/home/roki/ShareData¥texfat¥tdefaults,umask=000¥t0¥t0" >> /etc/fstab
sambaの設定。諸々の設定があるが主な内容は以下のとおり。
% sudo vim /etc/samba/smb.conf [ServerData] comment=server_data path=/home/roki/ShareData public = Yes read only = No writable = Yes guest ok = Yes force user = roki % sudo service smbd restart
DAAP
導入
% sudo apt -y install forked-daapd
ライブラリとするディレクトリを設定する。/etc/forked-daapd.confのdirectoriesを編集することで変更できる。
# Directories to index directories = { "/home/roki/BigData/music" }
netdata
導入
$ sudo apt -y install zlib1g-dev uuid-dev libmnl-dev gcc make git autoconf autogen automake pkg-config $ git clone https://github.com/firehol/netdata.git --depth=1 $ cd netdata/ $ sudo ./netdata-installer.sh $ sudo cp system/netdata.service /etc/systemd/system/ $ sudo systemctl enable netdata.service
デフォルトでは以下のポートで参照することができる。
http://localhost:19999
パイプラインset_segregate
※ 2018/3/11 ライブラリ整理より当機能は削除した。
set_segregateをパイプライン記法で。まあラップしただけ。
以下のように使う。
std::vector<int> left = boost::irange(0,9) | srook::adaptors::copied, right = boost::irange(7,19) | srook::adaptors::copied; std::vector<int> left_only,both,right_only; // pairかtupleで渡す。set_segregateの引数にはコンテナ直でも開始位置を示すイテレータでも良い。 std::make_pair(left,right) | srook::pipealgo::set_segregate(std::back_inserter(left_only),std::back_inserter(both),std::back_inserter(right_only)); std::make_tuple(left,right) | srook::pipealgo::set_segregate(left_only,both,right_only); left_only | srook::adaptors::print(); // 0 1 2 3 4 5 6 both | srook::adaptors::print(); // 7 8 right_only | srook::adaptors::print(); // 9 10 11 12 13 14 15 16 17 18
これはイテレータアダプタを作ると尚良いかもしれない。
iterator requirementsにassignableを要求される事による問題
※追記
当エントリは「STLコンテナの気まぐれな実装によってiteratorをassignされる事による問題」というタイトルであったが、これは気まぐれな実装ではなく、標準の文面に規定されたルールの範囲内での話であったため、誤った解釈に基づくようなタイトルや文面が編集されている。
標準が定めるイテレータの要件としてcopy assignableである事が明記されている。 *1
A type X satisfies the Iterator requirements if: X satisfies the CopyConstructible, CopyAssignable, and Destructible requirements (17.5.3.1) and lvalues of type X are swappable (17.5.3.2), and ...
@530506 コメントでご指摘して頂いた。 "X satisfies the CopyConstructible, CopyAssignable, and Destructible requirements ..."の部分。 https://t.co/v4FsBijTqb
— roκi (@530506) 2017年2月22日
STLコンテナには、アルゴリズムらの計算量を除いた実装規定というものが特にない。よって、本エントリの題名のように気まぐれにiteratorをassignしたところで何の違反性もないのだが、これについては規定した方が良いんじゃないかという意見。上記の通りcopy assignableでなければならないと定められている。
@530506 厳密にはクロージャではなく、lambda typeクロージャ
— roκi (@530506) 2017年2月14日
例えば、大抵のSTLコンテナはbegin/endのイテレータで初期化する事が可能である。
std::vector<int> v1(10,42); std::vector<int> v2(v1.begin(),v1.end());
そして、かの有名なライブラリ、Boost C++ Librariesにはboost range adaptorsというイテレータアダプタを利用した効率的な範囲横断を促す事のできるライブラリが存在する。中でも、例えばboost::adaptors::filteredはクロージャを渡す事によってその条件に応じた範囲横断を行えるイテレータアダプタである。
std::vector<int> v(10); boost::iota(v,0); boost::range::copy(v | boost::adaptors::filtered([](const typename decltype(v)::value_type& x){return x%2==0;}),std::ostream_iterator<int>(std::cout," "));
では、このfilteredイテレータアダプタを用いて、範囲を初期化する。イテレータアダプタから直接初期化するためにはOvenToBoostなどのas_containerが、Boostと互換性があり良い。
std::vector<int> v1(10); boost::iota(v1,0); std::vector<int> v2 = v1 | boost::adaptors::filtered([](const typename decltype(v1)::value_type& x){return x%2==0;}) | boost::as_container; // v2は偶数のみで初期化される。
このコードは、GCC、Clangともに正しくコンパイルに成功する。ここで例えば、コンテナをstd::dequeにするとどうなるだろうか。
std::deque<int> v1(10); boost::iota(v1,0); std::deque<int> v2 = v1 | boost::adaptors::filtered([](const typename decltype(v1)::value_type& x){return x%2==0;}) | boost::as_container;
clang(3.5〜5.0.0)のライブラリ実装上では正しくコンパイルに成功する。しかし、このコードはGCC(7.0.1までの全てのバージョン)でコンパイルに失敗する。エラー文は、この場合BoostをインクルードしているのでBoostライブラリでのassign失敗の旨が出力されるが、std::dequeの実装を実際に見て見ると、直接的にassign操作を行なっているコードが目につく(gcc7.0.1であれば、/c++/7.0.1/bits/deque.tcc:458:11)。これは、predに、ラムダ式が使えない事を意味する。boost::adaptors::filteredはfiltered_iteratorというイテレータアダプタを生成しその内部に指定されたクロージャオブジェクトを保持する。ラムダ式のoperator=は規格によってdelete指定されているため、イテレータをassignした段階で失敗する。
ラムダ式でないクロージャであればコンパイルに成功するが、ベンダによって異なる内部実装を気にしてpredをラムダ式で書かないというプログラマ側の負担は無駄であるように思う。
struct functor{ template<class T> bool operator()(const T& t){return t%2==0;} }; std::deque<int> v1(10); boost::iota(v1,0); std::deque<int> v2 = v1 | boost::adaptors::filtered(functor()) | boost::as_container; // v2は偶数のみで初期化される。
GCCの実装では、何故イテレータをassignする必要があったのか、そこまではコードを追っていないので分からないが、このような仕様は少なくともイテレータアダプタによる初期化を妨げる要因となる。よってSTLコンテナのbegin/endコンストラクタはassignableを強要すべきではないと私は考える。
一つ、妥協策ではあるが、解決策はある。内包するpredをラッパーで包み、無理やりassignableに見せかける事でコンパイルに成功する。
自作ライブラリ、srook range adaptor filterd_iteratorなどでは内部に当ラッパーを用いたクロージャを保持しており、gccで無事コンパイルを通している。
#include<srook/range/adaptor/copied.hpp> #include<srook/range/adaptor/filterd.hpp> #include<srook/range/adaptor/print.hpp> #include<deque> #include<boost/range/irange.hpp> struct functor{ template<class T> bool operator()(const T& t){return t%2==0;} }; int main() { std::deque<int> deq1 = boost::irange(1,9) | srook::adaptors::copied; std::deque<int> deq2 = deq1 | srook::adaptors::filterd([](const typename decltype(deq2)::value_type& x){return x%2==0;}) | srook::adaptors::copied; std::deque<int> deq3 = deq1 | srook::adaptors::filterd(functor()) | srook::adaptors::copied; deq2 | srook::adaptors::print(); deq3 | srook::adaptors::print(); }
しかし、依然としてラッパーを噛ませている分オーバーヘッドが発生するので、あまり心地よくはない。
縦横比率を維持しつつ、横幅に合わせて画像を囲うボックスのheightを可変的に取る
仕事(アルバイト)でWebページを弄っている。widthとの比率が考慮されたheightを可変的に取らなければならない問題があり、cssだけでなんとかできないものかと考えていたが、思いつかなかったのでjqueryを使った話。
@530506 画像をimgタグでベタ貼りした時のように、ウィンドウの横幅に合わせて画像が拡大縮小表示されなければならないのだが、この時当然画像の高さが拡大縮小に応じて可変であるので、画像を囲うボックスのheight数値をベタ打ちできない。
— roκi (@530506) 2017年2月13日
@530506 こういう。https://t.co/8ST0YCSxla
— roκi (@530506) 2017年2月13日
画像の横幅を囲うボックスのwidthを100%にし、その実際のサイズ値をjqueryで取得、元の画像の縦横比率が合うように横幅からその比率分を除算して縦幅をcssに書き込んでいる。
何かもっとスマートな方法はないだろうか。
brainf**k インタプリタジェネレータ
何だか凄く早い時間に起きてしまい、特にやることもないのと、最近アニメのセリフをbrainfuckの命令語に置き換える遊びが再発しているので、ジェネレータというかライブラリとも言えぬ小さいヘッダを書く事にした。
こう使う。一応元ネタさんと同じ命令語で動くようにしてある。
// kemono.cpp #include<srook/brainfk/brainfk.hpp> template<class... T> constexpr auto make_array(T&&... ts) -> std::array<std::decay_t<srook::First_t<T...>>,sizeof...(ts)> { return std::array<srook::First_t<T...>,sizeof...(ts)>{{std::forward<T>(ts)...}}; } int main(const int argc,const char* argv[]) { using namespace std::string_literals; if(argc<2)return -1; const auto gengo_friends_tanoshi= make_array( std::wstring(L"たのしー!"),std::wstring(L"すごーい!"),std::wstring(L"たーのしー!"), std::wstring(L"すっごーい!"),std::wstring(L"なにこれなにこれ!"),std::wstring(L"おもしろーい!"), std::wstring(L"うわー!"),std::wstring(L"わーい!") ); srook::brainfk_syntax<std::wstring,unsigned char> bksy(gengo_friends_tanoshi); const auto& status=bksy.file_open(argv[1]); if(status==boost::none)return -1; bksy.analyze(); bksy.exec(); }
因みに世界に挨拶を捧げるプログラミング言語フレンズKemonoのコードはこちら。
たのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!うわー!すごーい!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たのしー!すっごーい!わーい!すごーい!なにこれなにこれ!たのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!うわー!すごーい!たーのしー!たーのしー!たーのしー!たーのしー!たのしー!すっごーい!わーい!すごーい!たーのしー!なにこれなにこれ!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!なにこれなにこれ!なにこれなにこれ!たーのしー!たーのしー!たーのしー!なにこれなにこれ!うわー!すっごーい!わーい!たのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!うわー!すごーい!たーのしー!たーのしー!たーのしー!たーのしー!たのしー!すっごーい!わーい!すごーい!なにこれなにこれ!たのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!うわー!すごーい!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たのしー!すっごーい!わーい!すごーい!なにこれなにこれ!たのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!うわー!すごーい!たーのしー!たーのしー!たーのしー!たのしー!すっごーい!わーい!すごーい!なにこれなにこれ!たーのしー!たーのしー!たーのしー!なにこれなにこれ!すっごーい!すっごーい!すっごーい!すっごーい!すっごーい!すっごーい!なにこれなにこれ!すっごーい!すっごーい!すっごーい!すっごーい!すっごーい!すっごーい!すっごーい!すっごーい!なにこれなにこれ!うわー!すっごーい!わーい!たのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!うわー!すごーい!たーのしー!たーのしー!たーのしー!たーのしー!たのしー!すっごーい!わーい!すごーい!たーのしー!なにこれなにこれ!うわー!すっごーい!わーい!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!たーのしー!なにこれなにこれ!
普通に以下のように実行。
$ clang++ -std=c++1z -Wall -Wextra -pedantic kemono.cpp $ ./a.out hello_world.kemono
私は最近ポンコツクエストというアニメが少し面白いと思っているので、その作中で出てくる言葉を命令語にしたく思う。
#include<srook/brainfk/brainfk.hpp> //... const auto ponkue= make_array( std::wstring(L"ブラック企業かよ"),std::wstring(L"アイスうめー"),std::wstring(L"ヒョウド!"), std::wstring(L"ドルファヘキセンダー"),std::wstring(L"おもらひ"),std::wstring(L"おねひ"), std::wstring(L"労災はおりない"),std::wstring(L"ハンマー") ); srook::brainfk_syntax<std::wstring,unsigned char> bksy(ponkue); const auto& status=bksy.file_open(argv[1]); if(status==boost::none)return -1; bksy.analyze(); bksy.exec();
まあ、置き換えているだけである。この場合、世界に挨拶するためには以下のようなコードとなる。
ブラック企業かよヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!労災はおりないアイスうめーヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ブラック企業かよドルファヘキセンダーハンマーアイスうめーおもらひブラック企業かよヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!労災はおりないアイスうめーヒョウド!ヒョウド!ヒョウド!ヒョウド!ブラック企業かよドルファヘキセンダーハンマーアイスうめーヒョウド!おもらひヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!おもらひおもらひヒョウド!ヒョウド!ヒョウド!おもらひ労災はおりないドルファヘキセンダーハンマーブラック企業かよヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!労災はおりないアイスうめーヒョウド!ヒョウド!ヒョウド!ヒョウド!ブラック企業かよドルファヘキセンダーハンマーアイスうめーおもらひブラック企業かよヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!労災はおりないアイスうめーヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ブラック企業かよドルファヘキセンダーハンマーアイスうめーおもらひブラック企業かよヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!労災はおりないアイスうめーヒョウド!ヒョウド!ヒョウド!ブラック企業かよドルファヘキセンダーハンマーアイスうめーおもらひヒョウド!ヒョウド!ヒョウド!おもらひドルファヘキセンダードルファヘキセンダードルファヘキセンダードルファヘキセンダードルファヘキセンダードルファヘキセンダーおもらひドルファヘキセンダードルファヘキセンダードルファヘキセンダードルファヘキセンダードルファヘキセンダードルファヘキセンダードルファヘキセンダードルファヘキセンダーおもらひ労災はおりないドルファヘキセンダーハンマーブラック企業かよヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!労災はおりないアイスうめーヒョウド!ヒョウド!ヒョウド!ヒョウド!ブラック企業かよドルファヘキセンダーハンマーアイスうめーヒョウド!おもらひ労災はおりないドルファヘキセンダーハンマーヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!ヒョウド!おもらひ
./a.out hello_world.ponkue Hello World!
※追記
上記のコードでもそのままコンパイルが通るが、Boost.parameter を使うヘルパーを噛ませておいたので、以下のように何のキーワードを何に設定するのかより明確に書ける。
自宅ローカルサーバー(apache,motion,ping,AP)構築
前書き
Amazon dash buttonなどと連携させたmotionサーバー(監視カメラ)を以前構築したわけだが、最近は更に用途が増えてきている。
例えば、最近何故だかマンション内の回線が不安定であるのでpingを飛ばしたりしている。マンション内と断定したのは、定期的にプロバイダからの接続が切れる事を観測した事に帰結するのだが、まあ正直なところ原因ははっきりと定かではない。そこで、取り敢えず原因を探るためにも、不安定になるたびにpingを送信して、どの段階でノードがlostされたのか調べたりする必要がある。
また、別の用途としては、アクセスポイントとしての役割がある。GigaBitのルーターが欲しかったのだが、それに対応した無線LANルーターは割とお値段が張るので、学生身分も考えて有線LANルータにして、元から稼働している自宅サーバーをアクセスポイントにしてしまう事にした。
そして、それら全てを直感的に操作したり、情報にアクセスできたりする必要がある。まあ私だけが出来れば良いというのであれば必要ないのだが、彼女だけが自宅にいる時に使えないのも不便なので必要である。そのためには、apacheを使ってウェブサーバーを作り、そのページからグラフィカルなインタフェースで操作できるようにする事とした。
そんなわけで、以下に上記の機能を稼働させるサーバーを構築する作業をログを残す。
導入する機能まとめ
さて、導入する機能は以下の通りである。
環境は以下の通りである。
$ uname -a Linux Vostro-Ubuntu 4.4.0-59-generic #80-Ubuntu SMP Fri Jan 6 17:47:47 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
MySQLは特に必要としないので厳密にはLAPといったところか。
apache,php導入
$ sudo apt-get install apache2 php libapache2-mod-php
apacheについては、諸々の設定が必要であるが、本エントリでは、取り合えず機能として目立つ設定内容だけを示す。*1 設定内容としては、まずapacheサーバーへのアクセスはローカルネットワーク内のマシンに限定する。また、すぐcgiを動かしたくなるとも思うのでそのように設定した。
<Directory /var/www/html> Options ExecCGI AddType text/html cgi AllowOverride All order deny,allow deny from all allow from 192.168. allow from 10.42.0. </Directory>
このapacheサーバーは、apacheだけでなく、アクセスポイントともなっているため、そのアクセスポイントを使っているユーザー(10.42.0.)もallowする必要がある。
motion導入
motionについては、以前アップロードしたエントリを参照。 roki.hateblo.jp
cronでpingを飛ばしphpで吸い上げ
cronで定期的にpingを飛ばす。ログファイルを上書き出力させ、そのファイルをphpで読み込み、前述したページに表示させる。取り敢えず、pingは30分おきにexample.comへ送出する。
*/30 * * * * root /bin/bash /var/www/script/check_ping.sh
shスクリプトは以下のような内容。
#!/bin/bash # checkinfo.sh cd /var/www/script date > ../logfiles/ping.log echo >> ../logfiles/ping.log ping -c 10 www.example.com >> ../logfiles/ping.log echo >> ../logfiles/ping.log traceroute www.example.com >> ../logfiles/ping.log
そしてphpで吸い上げる。
<?php function file_print($filename,$filetype_name){ $fp=fopen($filename,'r'); print('<u><strong>'.$filetype_name.'</strong></u>:<br>'); if($fp){ if(flock($fp,LOCK_SH)){ while(!feof($fp)){ $buffer=fgets($fp); print($buffer.'<br>'); } flock($fp,LOCK_UN); }else{ print('File lock was failed.'); } } fclose($fp); print('<br><br>'); } file_print('../script/checkinfo.sh','script'); file_print('../logfiles/ping.log','result'); ?>
これで、定期的に実行されるpingを、apacheに設定されているhtmlページから参照できるようになる。
AP化
AP化は公開されているユーティリティツールを用いれば比較的容易に構築できる。
sudo apt -y install hostap git clone https://github.com/oblique/create_ap cd create_ap sudo make install create_ap --ieee80211n wireless_adaptor_name eth_adaptor_name SSID key &
以上で完了した。
ちなみに、ローカルネットワーク上のページは以下のような感じにしてみた。普通にhtmlとcssで書いた。
無駄に装飾を凝ってしまった気がするが画面中央の黄色い画像部分はmotionプロセスが起動すると自動的に監視カメラの映像に切り替わるようになっている。
*1:他の標準的なディストリビューションではhttpd.confによる設定が一般的だが、Ubuntuのaptでapache2を導入した場合は、/etc/apache2/apache2.confに設定内容が書かれるため、本エントリでもその内容をそのまま記載している。