Unix/Linuxプログラミング理論と実践 第2章(プログラミング課題)

備忘録です.解答にはなっていないかったりします.
環境:FreeBSD 8.2-STABLE
http://www.amazon.co.jp/gp/product/4048700219

2.10 アイデンティティの危機

who am i

$ who am i

$ who -m

と同義.

manには「-m 標準入力に接続された端末情報のみを表示します。」と説明ある.
上記コマンドを実行すると,コマンドを実行した端末の情報のみを表示する.


who2.cを書き換える際の参考情報.
システムのstruct utmpの中から,標準入力に接続された端末の情報のみを表示する.


まずは,端末名を取得する.
ttyname()を使い,ファイルディスクリプを渡すと,ttyの名前を/dev/ttyxxの形式で返してくる.
なお,標準入力のファイルディスクリプタはSTDIN_FILENO.

#include <unistd.h>
char* ttyname(int fd)

struct utmpのut_lineに端末名があるが,これはttyxxの形式で入っている.
それで,まずは/dev/ttyxxの形式からttyxxだけを抜き出す.
抜き出すために,次のライブラリ関数を利用できる.
strrchr()は,char型のポインタsが指す文字列中で,文字cが最後に出た場所のポインタを返す.

#include <string.h>
char* strrchr(const char *s, int c);
whoami
$ whoami

は,

$ id -un

と同じ.
機能は,実効UID(Effective UID),つまりプログラム実行時の権限判断に利用するUIDの名前を表示することです.
$ who am i で表示されるのは,ユーザのログイン名.


whoamiコマンドはidコマンドと同じプログラムから生成(実行時にコマンド名で処理切り替え)するので,idコマンドのプログラムをチェックする.
id.cのl.174からが該当する処理です.

	if (uflag) {
		id = pw ? pw->pw_uid : rflag ? getuid() : geteuid();
		if (nflag && (pw = getpwuid(id)))
			(void)printf("%s\n", pw->pw_name);
		else
			(void)printf("%u\n", id);
		exit(0);
	}

まずは次の行.

		id = pw ? pw->pw_uid : rflag ? getuid() : geteuid();

変数pwはl.79で定義されている.

	struct passwd *pw;

passwd構造体は/usr/include/pwd.hのl.116から定義されている.
この構造体を,FreeBSDでユーザIDやパスワードの管理に利用している.$ man 5 passwd が参考になる.

struct passwd {
	char	*pw_name;		/* user name */
	char	*pw_passwd;		/* encrypted password */
	uid_t	pw_uid;			/* user uid */
	gid_t	pw_gid;			/* user gid */
	time_t	pw_change;		/* password change time */
	char	*pw_class;		/* user access class */
	char	*pw_gecos;		/* Honeywell login info */
	char	*pw_dir;		/* home directory */
	char	*pw_shell;		/* default shell */
	time_t	pw_expire;		/* account expiration */
	int	pw_fields;		/* internal: fields filled in */
};

変数pwは,id.cのl.153で初期化されている.

	pw = *argv ? who(*argv) : NULL;

このときargvは,コマンド実行時のオプションの後ろを指している.
オプションの後ろには,ユーザ名 or ユーザIDを指定できることになっている.
つまり,ユーザ名orユーザIDを指定していればwho()が呼ばれ,何も指定していなければNULLがpwには入る.今はNULL.
戻って,次の行.pw==NULL,rflag==0なので,idにはgeteuid()の結果が返る.
geteuid()は呼び出しプロセスの実効ユーザIDを返す.

		id = pw ? pw->pw_uid : rflag ? getuid() : geteuid();

次の行.nflag==1.getpwuid()はパスワードデータベースを検索して渡したidに該当する最初のエントリを返す.
struct passwdへのポインタがpwに返り,printf()でstruct passwdのpw_nameを表示する.
したがって,whoamiコマンドは,呼び出しプロセスの実効ユーザ名を表示する.

		if (nflag && (pw = getpwuid(id)))
			(void)printf("%s\n", pw->pw_name);