libprocps programming

# いきなり結論
### プロセスを巡回したい場合
```c:readproc.c
#include  <stdio.h>
#include  <proc/readproc.h>

static void show_proc(proc_t *proc_info) {
  printf ("tid=%d, ppid=%d, pgrp=%d, session=%d, tty=%d, tpgid=%d [%s]\n",
	  proc_info->tid, proc_info->ppid,proc_info->pgrp, proc_info->session,
	  proc_info->tty, proc_info->tpgid,
	  proc_info->cmd);
  return;
}

int main(int argc, char* argv[]){
  PROCTAB* proc;
  proc_t *proc_info;

  proc = openproc(PROC_FILLARG | PROC_FILLSTAT);
  if (proc == NULL) {
    fprintf (stderr, "openproc fail\n");
    return 1;
  }
  while ( (proc_info=readproc(proc, NULL)) != NULL) {
    show_proc (proc_info);
    freeproc(proc_info);
  }
  closeproc(proc);
  return  0;
}
```
### 特定プロセス情報を得たい場合
1つ以上の引数としてPIDを入れて実行する例。

```c:get_proc_stats.c
#include  <stdio.h>
#include  <stdlib.h>
#include  <proc/readproc.h>

static void show_proc(proc_t *proc_info) {
  printf ("tid=%d, ppid=%d, pgrp=%d, session=%d, tty=%d, tpgid=%d [%s]\n",
	  proc_info->tid, proc_info->ppid,proc_info->pgrp, proc_info->session,
	  proc_info->tty, proc_info->tpgid,
	  proc_info->cmd);
  return;
}

int main(int argc, char* argv[]){
  int i;
  pid_t pid;
  PROCTAB* proc;
  proc_t *proc_info;

  if (argc < 2) {
    fprintf (stderr, "argument required PIDs\n");
    return 1;
  }
  for( i=1; i<argc; i++) {
    pid = atoi (argv[i]);
    proc = openproc(PROC_FILLARG | PROC_FILLSTAT | PROC_PID, &pid);
    if (proc == NULL) {
      fprintf (stderr, "openproc fail with PID %d\n", pid);
      continue;
    }
    while ( (proc_info=readproc(proc, NULL)) != NULL) {
      show_proc (proc_info);
      freeproc(proc_info);
    }
    closeproc(proc);
  }
  return  0;
}
```
### 確認環境
確認環境はgcc-4.9, Linux-3.10, aarch64, procps-ng-3.3.10くらいだったはず。
# はじめに
Linuxでプロセスの情報を得たい場合、[procfs(5)](https://linuxjm.osdn.jp/html/LDP_man-pages/man5/proc.5.html)の**/proc/[pid]/**にある**stat**,**statm**,**status**を見ればいい。が、パースするのが意外とめんどい。何番目が何かとか覚えたくもない。Linuxバージョン違いで取得できる数の違いも気にしたくない。そういうときは**libprocps**を使うのがよい。

**libprocps**は**procps-ng**(もしくはfork元の**procps**)が提供するライブラリ、つまり**ps**,**free**,**vmstat**,**top**といったコマンド群を提供しているパッケージの一部。**readelf -d**してもらえばわかるが、これらコマンドも**libprocps**を使っている。なので、いつメンテが止まったりAPIが変わったり...な心配はたぶんない。

**procps-ng**についての詳細は、[Linux From Scratch](http://www.linuxfromscratch.org/lfs/view/development/chapter06/procps-ng.html)もしくは[Github](https://gitlab.com/procps-ng/procps)あたりに行くとよい。
# 注意点
### APIの確認
広く使われている割には、manくらいしかろくなドキュメントはない模様。(manさえあれば十分？) ググってサンプルプログラムを探すよりも、**/usr/include/proc/readproc.h**
を開いて直接enumとかを確認した方が手っ取り早い。
### メモリリーク注意
**freeproc**を呼ぶのを忘れがち。[Stack Overflow](http://stackoverflow.com/questions/17359517/how-can-i-properly-free-memory-when-using-libproc)にズバリのページがあるので確認しておこう。
### get_proc_stats問題
**proc/readproc.h**を見ると**get_proc_stats**という関数があり使えるかのように見えるが、コンパイルすると**undefined reference**でリンクエラーになる。[private symbol](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=731959)に変わったのが原因。リンク先参考にprocps-ngを変更してコンパイルすれば使えるようになるが、使ってほしくないからprivate symbolに変わったわけで、使わないに越したことはない。このため上記サンプルプログラムは**openproc**の引数を使ったものとしている。

### kernel threadかどうかの確認
**ps**を実行するとkernel threadは**[procname]**と大括弧付きの表記になるが、これをどこで判定しているかわからなかった。「session==0 または pgrp==0 ならば kernel thread」でいいように見える。daemonがあるので「tty==0」では判断してはいけない。

**2017/03/29(Wed)追記**、psは\[proc_name\]にするかどうかを「**/proc/[PID]/cmdline**からreadして1バイト以上読めたかどうか」で判定する。**ESC_BRACKETS**のフラグで検索すればわかる。必ずしもkernel threadに限らないことも含め、[このへん](https://github.com/hiboma/hiboma/blob/master/ps%E3%81%A6%E3%82%99%E3%83%95%E3%82%9A%E3%83%AD%E3%82%BB%E3%82%B9%E5%90%8D%E3%81%AB%E3%83%95%E3%82%99%E3%83%A9%E3%82%B1%E3%83%83%E3%83%88%E3%81%8B%E3%82%99%E3%81%A4%E3%81%8F.md)に詳しい。...でもfork中やexec中って/proc/[PID]/cmdlineって読めなくなるんだったっけ？


### ソースを読む場合
パッケージの中に**[HACKING](https://github.com/hnw/procps-ng-android-ndk/blob/master/ps/HACKING)**というファイルがあり、中の設計の簡単な解説があるので、先にこちらを確認するとよい。
# あとがき1
特定プロセスのプロセスグループIDを確認したくて**libprocps**を調べたわけだが、**[getpgid(2)](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/getpgrp.2.html)**を使うだけでよかった...他人のプロセスであろうが、**/proc/[pid]/**や**getpgid(2)**を使うのに特に権限は必要ないようだった。組み込み畑で育ったもので、権限管理については疎い...
# あとがき2
普段は自分のサイトのブログに記事を書くけど、最近Qiitaがいいらしいと聞いて登録して使い始めてみたので、せっかくなのでこっちで書いてみた。Qiita記法になれるにはもう少し時間がかかりそう。次回以降の記事はどうするかは未定。