漆黒な技術メモ

管理人が必要に応じて自分のメモを好き勝手に投下するたまり場的ブログ

CentOS7にmosquittoを導入しようとしたら死にかけた話

とある用事で、CentOS7に例のMQTTBroker、 Mosuquitto を入れようとしたときの話です。
公式ページには、 「/etc/yum.repos.d/にconfigを追記すれば行けるよ」って簡潔に書いてあったわけですが、まったくインストールできない
今後自分が同じ目にあった時の備忘録的に、その時の対処方法をメモしておきます。

EPELリポジトリが追加されていなかった

今回利用したインストール中に libwebsockets.so.7が必要と言われた。 本来ならば依存関係でlibwebsocketsも一緒に入ってくれるようだが、自分の利用しようとしたCentOS7はEPELリポジトリが追加されておらず、インストール時にエラーを吐いてしまった。

EPELリポジトリを追加、あらかじめlibwebsocketsをインストール

libwebsockets関係でエラーを吐いてしまった場合、このようにインストールするとうまくいく

$ sudo wget http://download.opensuse.org/repositories/home:/oojah:/mqtt/CentOS_CentOS-7/home:oojah:mqtt.repo -O "/etc/yum.repos.d/Mosquitto.repo"
$ sudo yum install epel-release
$ sudo yum --enablerepo=epel install libwebsockets
$ sudo yum install mosquitto

手元のUbuntuにUSB-LANアダプタを追加してルータ化する

以下の図のような構成を組み、Ubuntuをルータとして機能させなければならない場面に遭遇したのでメモを取っておく。
今回はかなり古いがUbuntu11.10を使った。執筆時点ではバージョン間の違いは特になさそう。 f:id:igbt3116redtrain:20180112192315p:plain

USB-LANアダプタ接続後の設定

NICの設定

/etc/network/interfacesを編集

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
address 192.168.200.90
netmask 255.255.255.0
network 192.168.200.0
broadcast 192.168.200.255
gateway 192.168.200.1
dns-nameserver 8.8.8.8


auto eth1
iface eth1 inet static
address 192.168.11.1
netmask 255.255.255.0
network 192.168.11.0
broadcast 192.168.11.255

IPフォワードを有効化

2つのNIC間をパケットが通過できるようにする。そのため/etc/sysctl.confに記述されている以下のコメントアウトを解除

# コメントアウトを解除
net.ipv4.ip_forward=1

IPマスカレードの設定

以下のコマンドを打ち込んでIPマスカレードを有効化

$ sudo iptables -t nat -A POSTROUTING -s 192.168.11.0/255.255.255.0 -o eth0 -j MASQUERADE

再起動をしてもiptablesIPマスカレードが設定されるよう、現在のiptablesの設定を保存&起動時に反映されるようシェルを作成。
設定は以下のように保存

$ sudo sh -c "/sbin/iptables-save -c > /etc/iptables.rules"

シェルは/etc/network/if-pre-up.d/iptables_startに作成

#!/bin/sh
/sbin/iptables-restore < /etc/iptables.rules
exit 0

最後にシェルに実行権限を与え、再起動すれば完了

$ sudo chmod +x /etc/network/if-pre-up.d/iptables_start
$ sudo reboot

参考文献

HDDアクセスがアクティブ状態で張り付いてしまったのを解決した話

最近、自分のデスクトップのディスクアクセスの様子がおかしかった。
自分のデスクトップはCドライブにSSD、DドライブにHDDを割り当てている。 そのDドライブのアクセスが異常に遅く、Dドライブにアクセスするアプリケーションが応答なしになることが頻発した。
その時のタスクマネージャのキャプチャがこちら
f:id:igbt3116redtrain:20171124212859p:plain

アクティブ時間がずっと100%になっているにも関わらず、読み書きバイト数は殆ど0に近い。

原因を探ってみる…がうまくいかない

まず行ったのがwindows updateとウイルスチェック
結論から言うと何も変わらなかった。

その次に行ったことはHDDの交換である。
HDDのS.M.A.R.T値を見ても特に異常は見られなかったが、稼働時間が4万時間を超えていたので「もしや」と思いディスクの中身を新しいHDDにクローンし交換した。
交換した後、1~2日ぐらいは症状が軽減したが、しばらくするとまた症状が復活してきた。

さらに原因を探るため、ググってこのあたりの記事などを参考にsuperfetchの無効化なども行ったが一向に改善しない www.pcdepot.co.jp

原因はマザボにあり

さらに原因を探るため"HDD 100% transfer 0Bytes"とググってみると、このような記事が出てきた。
www.tomshardware.com この回答の中に"SATAケーブルの交換を行い別のSATAポートにつなぐことを試してみては?"と書いてあった。
藁にも縋る思いでこの方策を実行しようと古いSATAケーブルを抜こうとすると…マザボの該当SATAポートがぐらついていた。
つまりSATAポートが壊れかけていたのである。思えば最初PCを組むときに配線パーツの都合で半ば無理やりSATAケーブルを挿しており、長時間SATAポートに横方向の圧力がかかり続けていたことが原因だったみたいだ。
SATAケーブルとポートを交換したところ…息を吹き返したようにサクサクHDDへアクセスできるようになった。
ストレージアクセスが冒頭のような状態になった方は、是非SATAケーブルとポートの交換を試していただきたい。

ubuntuユーザ作成の手順メモ

何回もやっているのに毎度手順を忘れてしまうのでメモにした。
ubuntuルート権限を持つユーザを新たに作り、公開鍵認証する手順を書いておく

$ sudo adduser username
 #ここらでパスワードが聞かれる。
 #Enter the new value, or press ENTER for the default とか聞かれるので特別必要なければEnter連打
$ sudo gpasswd -a username sudo
$ su username
$ cd ~
 #この辺でscpなどで公開鍵をサーバに転送しておく
$ mkdir .ssh
$ mv id_rsa.pub .ssh/
$ sudo chmod 700 .ssh/
$ cat id_rsa.pub >> authorized_keys
$ sudo chmod 600 authorized_keys
$ rm id_rsa.pub

【私的メモ】androidに通知を実装するために参考にするサイト一覧

久々の投稿
今まで何だかんだandroidで通知を実装したことがなかったので恥ずかしながらメモ

通知には二種類ある

ネットで「プッシュ通知」と検索すると、アプリを管理するサーバから一斉または個別にメッセージを配信して通知する仕組みの実装方法が引っかかる。
アプリだけで完結する通知(目覚ましとかに代表されるような通知)は「ローカルプッシュ」というらしい。

ローカルプッシュの実装で参考になりそうなサイト一覧

developer.android.com

qiita.com

サーバからのプッシュ通知の実装で参考になりそうなサイト一覧

qiita.com

https://firebase.google.com/docs/cloud-messaging/android/clientfirebase.google.com

また随時追加していこうと思う

C++のstd::string::clear()はメモリの解放をしてくれない話

まあ簡単に言うと自分のC++に対する知識が浅かったって話ですが…
研究のソースでstd::string::clear()を何回か使ってメモリの解放をした「つもり」でしたが、こいつは長さを0にセットするだけでメモリ領域は食ったままらしく、結果的にメモリリーク( std::bad_alloc )を起こして死にました。 ちなみに書いたソースはこんな感じ

class Buffer {
public:
    Buffer();
    virtual ~Buffer();
  
    void setData(std::string data,int type);
  
    std::string getData();
    int getType();
  
    void clearData();
    bool isexistData();

private:
    std::string data;
    int type;
    bool existData;
};

Buffer::Buffer() :
        type(-1), existData(false) {
}

Buffer::~Buffer() {
}

void Buffer::setData(std::string data,
        int type) {
    this->data = data;
    this->type = type;
    existData = true;
}

std::string Buffer::getData() {
    return data;
}

int Buffer::getType() {
    return type;
}

void Buffer::clearData() {
    data.clear();
    existData = false;
}

bool Buffer::isexistData() {
    return existData;
}

こういう状況で、いくら Buffer::clearData() を呼び出してもメモリリークを起こすので不思議だなあと思ってたら、stackoverflowにそんなことが書いてありました。

Calling std::string::clear() merely sets the size to zero. The capacity() won’t change (nor will reserve()ing less memory than currently reserved change the capacity).

stackoverflow.com

stackoverflowには、解放したいんだったらBuffer::clearData()

void Buffer::clearData() {
  std::string().swap(data);
    existData = false;
}

で行う(空文字を確保したstd::stringとのメモリ領域スワップ、スコープが終われば元々dataで確保した領域はデストラクタが呼ばれて解放される)と良いと書いてありました。
でも、この書き方だとなにやってるかわかりづらいですよね…

std::shared_ptrに変える

まあそもそもバッファなんだからshared_ptrにしろよという突っ込みが聞こえてきそうですが、その話は棚に上げて… std::shared_ptrはじめスマートポインタは reset() で所有権を放棄、つまるところ(shared_ptrの場合はカウンタが0であれば)明示的にメモリを開放することができます。 というわけで該当部を以下のようなコードに変更して事なきを得ましたとさ。

void Buffer::clearData() {
    data.reset();
    existData = false;
}

SQLで「グループごとにn件出力する」というSELECT文を書く

皆様あけましておめでとうございます。本年もどうぞよろしくお願いいたします。
さて、今日はSQLを久しぶりにいじっていて詰まったことを書きたいと思います。

下のようなテーブルがあって、「SSIDごとにステータスがONの最新の結果をn件出力する」ということを実行しなければならない場面に遭遇しました。 例を出すと、下のようなテーブルから「ステータスがONの最新の結果を1件出力したい」とします。

id time ssid status
1 12:00 ssid1 ON
2 12:00 ssid2 ON
3 12:00 ssid3 ON
4 12:10 ssid1 ON
5 12:10 ssid2 ON
6 12:10 ssid3 ON
7 12:20 ssid1 ON
8 12:20 ssid2 OFF
9 12:20 ssid3 ON

すると、上のテーブルから下のような結果が出てくるのが理想になります。

id time ssid status
7 12:20 ssid1 ON
5 12:10 ssid2 ON
9 12:20 ssid3 ON

しかしこのような抽出は、group by句、where句などの単純なものではできません…
さて、どうするかと思って検索するとこのような記事に引っかかりました。

blogs.wankuma.com

今回はこの記事を全力で参考にして解決しました。

解法:IN句を用いた副問い合わせ

これを見た瞬間「ああ、副問い合わせとかそんなものあったなあ」となりました…(←ダメな奴)
最初に示した結果を抽出するには以下のようなSQLを書きます。

select * from table as t1
where id in 
(select id from table as t2 where t1.ssid=t2.ssid and status='ON' order by id desc limit 1)
order by ssid, id desc;

主問い合わせ文のwhere句にはid in の形で指定し、副問い合わせ文のwhere句の中でグループにしたいカラムを結合します(この場合は t1.ssid=t2.ssid )。
また、「ステータスがONの」などの他の条件や「N研抽出する」などのLIMIT句も副問い合わせ文の中に記述します。
あとは適宜並び替えなどをすれば完了です。

今日はここまで!