【C++】【Linux】S.M.A.R.T.情報を取得する

HDDの健康状態の指標としてS.M.A.R.T.ってのがあるのは皆さん知っての通りだと思う.これを取得するためのライブラリがあるから使ってみた.

環境

ライブラリを試したときのバージョンは以下

  • Ubuntu 18.04 LTS
  • libatasmart
  • clang

ライブラリのインストール

ubuntuであれば以下のコマンドを叩いてlibatasmartというものをインストールする.ほかにもgccとかは入れなきゃだけどそれはそうって感じ.

# apt install libatasmart-dev

使ってみる

S.M.A.R.T.対応かどうかを調べる

S.M.A.R.T.情報を取得するにあたって,そもそも対象のディスクが対応しているかどうかがわからない.まぁよっぽどの場合は問題ないと思うけど,確かめるための関数も当然用意されているので使うに越したことはない


#include 
#include 

int main(){
  Skdisk* disk;
  SkBool isAvailable = 0;

  if(sk_disk_open("/dev/sda", &disk) < 0){
    std::cout << "Failed to open disk" << std::endl;
    return -1;
  }

  if(sk_disk_smart_is_available(disk, &isAvailable) < 0){
    std::cout << "S.M.A.R.T. is not available" << std::endl;
    sk_disk_free(disk);
    return -1;
  }

  // something what to do
}

各情報を取得してみる

S.M.A.R.T.に対応しているディスクに対しては,以下の方法で基本的な情報を取得できる.

  // さっきの続き
  if(sk_disk_smart_read_data(disk) < 0){
    std::cout << "Failed to read S.M.A.R.T data" << std::endl;
    sk_disk_free(disk);
    return -1;
  }

  cosnt SkIdentifyParsedData* data;
  sk_disk_identify_parse(disk, &data);
  std::cout << "model : " << data->model << std::endl; // デバイス名
  std::cout << "firmware : " << data->firmware << std::endl; // ファームウェア
  std::cout << "Serial : " << data->serial << std::endl; // シリアル番号
  
  unsigned long long value;
  if(!sk_disk_get_size(disk, &value)){
    std::cout << "capacity[byte] : " << value << std::endl;
  }

  if (!sk_disk_smart_get_power_cycle(disk, &value)) {
    std::cout << "Power on Cycle : " << value << std::endl; // 電源投入回数
  }

  if (!sk_disk_smart_get_power_on(disk, &value)) {
    std::cout << "Total Power on Time" << ((value / 1000) / (60 * 60)) << std::endl; // 電源投入時間(ミリ秒で取得)
  }

  if (!sk_disk_smart_get_temperature(disk, &value)) {
    std::cout << "Temperature : " << static_cast(value - 273150llu) / 1000.0 << std::endl; // ミリケルビン単位で取得(0[℃] = 273.15[K])
  }


その他の情報

さっき取得した以外の情報は,以下のパース用の関数を使用することで取得することができる.

int sk_disk_smart_parse_attributes(SkDisk d, SkSmartAttributeParseCallback cb, void* userdata);

// callbackに登録する関数は以下
static void callback(SkDisk* d, const SkSmartAttributeParsedData const* data, void* userdata);

パース用の関数の第2引数にコールバックを設定し,第3引数には値を格納するための変数を渡してあげる.この変数はコールバックの第3引数に引き継がれるため,コールバック内で取得した値を呼び出し元で取得できる.

具体的には以下の感じで使う

// 予めこれを定義しておく
// S.M.A.R.T.情報のWrapperクラス
class Attribute{
public:
  uint8_t id;
  std::string name;
  uint8_t current;
  uint8_t worst;
  uint8_t threshold;
  uint64_t raw;
};

  // これ以降はさっきの続き
  std::vector attribute;
  sk_disk_smart_parse_attributes(
      disk,
      [](SkDisk* skdisk, SkSmartAttributeParsedData const* data, void* userdata) {
        auto attr = reinterpret_cast*>(userdata);
        Attribute a{};
        a.id(data->id); // S.M.A.R.T.のID, wiki参照
        a.name(data->name); // 名称
        a.current(data->current_value); // 現在値
        a.worst(data->worst_value);  // 最悪値
        a.threshold(data->threshold); // しきい値(目安)
        for (auto i = 0; i < 6; ++i) {
          a.raw += (data->raw[i] << (8 * i))); // RAW値
        }
        attr->push_back(a);
      },
      &attribute);
  
  for(auto attr : attribute){
    std::cout << "ID : " << attr.id << std::endl;
    std::cout << "NAME : " << attr.name << std::endl;
    std::cout << "CURRENT : " << attr.current << std::endl;
    std::cout << "WORST : " << attr.worst << std::endl << std::endl;
    std::cout << "RAW : " << attr.raw << std::endl;
  }

とまぁ今回はこんな感じで全部の情報を取得してみたけど,IDを見て決め打ちで取得することももちろんできる.

他にもいろいろできるっぽいのでドキュメントを見てくれ

ちなみにGtkmmと合わせたこんな感じのGUIツールを作ってみたよっていう報告(https://github.com/koron0902/CocoaDiskInfo)

おすすめ

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です