意識低い系会社員

意識低い系会社員の日常

【スポンサーリンク】

ふつうのLinuxプログラミング 第2版の内容memo(14章)

【スポンサーリンク】

 

こんにちは。

 

最近、ふつうのLinuxプログラミング 第2版を読んでいるので知識の定着のために学んだ内容を要約したメモを書きます。

このエントリは完全な個人のメモです。

 

お勉強のためにこの本を読んでいるので、内容を覚えるためにSummarizing(サマライジング)を行います。

 

13章の内容はこちら。

amistad06-a.hatenablog.com

 

プロセスの環境

この章で説明すること

プロセスの属性をはじめ、ユーザーデータベースや時刻についての説明をします。

 

カレントディレクトリ

プロセスが現在実行されているディレクトリ。

カレントワーキングディレクトリともい言います。

getcwdで取得可能です。

 

パスのバッファサイズ

limits.hのPATH_MAXを使うのは誤り。

ある程度確保しておいて、足りなかったらreallocするのが正解。

 

chdir(2)

自プロセスのカレントディレクトリを変更するシステムコール。

 

環境変数

PATHやLANGのこと。

環境変数は親プロセスから子プロセスに伝播される。

環境変数にはグローバル変数environを使用してアクセスする。

 

set-uidプログラム

/etc/passwdや/etc/shadowの書き換えを行うpasswdコマンドなどは、コマンドの実行ユーザーではなくrootの権限で行う必要があります。

そういう場合は、プログラムのファイルパーミッションのset-uidビットを立てると実行したユーザーに関わらずプログラムファイルのオーナーの権限で実行されます。

これをset-uidプログラムと言います。

グループにも同様の機能があり、その場合はset-gidビットを使用します。

 

クレデンシャル操作API

getuidやeteuidなどでクレデンシャルを取得できます。

setuidやsetgidなどで別のクレデンシャルに移動することができます。

 

ユーザ情報やグループ情報を検索するAPI

getpwuid, getpwnamでそれぞれユーザIDやユーザ名からユーザ情報を取得できます。

getgrgid, getgrnamでそれぞれユーザIDやユーザ名からユーザ情報を取得できます。

 

プロセスの使うリソース

getrusageでプロセスのリソース使用量が取得できます。

 

UNIXエポック

Linuxカーネルでは時間を1970年1月1日からの経過秒数で保持しています。

1970年1月1日の0時を俗にUNIXエポックと言います。

 

時間に関するAPI

time, gettimeofday, localtime, gmtime, kmtime, asctime, ctimeなどの紹介。

UNIXエポックからの経過秒数の取得や、取得した値を文字列に変換するものなど。

 

ログイン

ユーザがログインするまでの流れの説明。

1. systemdがgettyコマンドを起動

2. ユーザーからの入力でloginコマンドを起動して認証

3. シェルの起動

誰がどの端末にログインしたかはwコマンドやlastlogコマンドで確認できます。

ログイン情報は/var/run/utmpやvar/log/lastlogにあります。

 

演習問題

1. シェルのpwdコマンドを、シェルから独立したプログラムとして書くのは適切でしょうか。調べて、その理由を答えなさい。

 

適切。

プロセスの実行ディレクトリは親プロセスから子プロセスに引き継がれるため。

以下、確認するためのプログラム。

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char *argv[]){
    char buf[1024];
    pid_t pid;

    if(getcwd(buf, sizeof(buf)) == NULL){
        fprintf(stderr, "getcwd() failed 1.\n");
    }
    
    fprintf(stdout, "cwd = %s\n",buf);

    pid = fork();
    if(pid < 0){
        fprintf(stderr, "fork(2) failed\n");
        exit(1);
    }

    if(pid == 0){
        if(getcwd(buf, sizeof(buf)) == NULL){
            fprintf(stderr, "getcwd() failed 2.\n");
        }

        fprintf(stdout, "cwd = %s\n",buf);

        exit(0);
    }else{
        // parent process
        int status;

        waitpid(pid, &status, 0);
        exit(0);
    }
}

 

実行結果は以下。

 

$ ./a.out 
cwd = /home/user/prj/study/linux_programming/14
cwd = /home/user/prj/study/linux_programming/14

 

2. シェルのcdコマンドを、シェルから独立したプログラムとして書くのは適切でしょうか。調べて、その理由を答えなさい。

 

不適切。

カレントディレクトリの移動は自プロセスしかできないため。

他のプロセス(親プロセス含む)のカレントディレクトリを変更することはできないため。

 

以下、確認するためのプログラム。

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char *argv[]){
    char buf[1024];
    pid_t pid;

    if(getcwd(buf, sizeof(buf)) == NULL){
        fprintf(stderr, "getcwd() failed 1.\n");
    }
    
    fprintf(stdout, "cwd = %s\n",buf);

    pid = fork();
    if(pid < 0){
        fprintf(stderr, "fork(2) failed\n");
        exit(1);
    }

    if(pid == 0){
        if(chdir("/tmp") != 0){
            fprintf(stderr, "chdir() failed.\n");

        }
        if(getcwd(buf, sizeof(buf)) == NULL){
            fprintf(stderr, "getcwd() failed 2.\n");
        }

        fprintf(stdout, "cwd = %s\n",buf);

        exit(0);
    }else{
        // parent process
        int status;

        waitpid(pid, &status, 0);
        if(getcwd(buf, sizeof(buf)) == NULL){
            fprintf(stderr, "getcwd() failed 3.\n");
        }

        fprintf(stdout, "cwd = %s\n",buf);

        exit(0);
    }
}

 

実行結果は以下。

 

$ ./a.out 
cwd = /home/user/prj/study/linux_programming/14
cwd = /tmp
cwd = /home/user/prj/study/linux_programming/14

 

3. 第10章で作成したlsコマンドを改造して、ファイルのオーナーのユーザー名(IDではない)と最終更新日時を表示するようにしなさい。

 

以下、作成したプログラム。

 

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <pwd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>

static void do_ls(char *path);

int main(int argc, char *argv[]){
    int i;

    // コマンドライン引数で表示するディレクトリのパスをもらう
    // コマンドライン引数がない場合、プログラムを終了
    if(argc < 2){
        fprintf(stderr, "%s: no arguments\n", argv[0]);
        exit(1);
    }
    for(i = 1; i < argc; i++){
        do_ls(argv[i]);
    }
}

static void do_ls(char *path){
    DIR *d;
    struct dirent *ent;
    struct stat st;
    struct passwd *pwd;
    struct tm *time = NULL;

    // ディレクトリをopen
    d = opendir(path);
    if(!d){
        perror(path);
        exit(1);
    }
    // エントリがなくなるまで繰り返し
    // =演算子は代入された値を返す
    while(ent = readdir(d)){
        if(lstat(ent->d_name, &st) < 0){
            fprintf(stderr, "lstat failed. file name = %s\n", ent->d_name);
            continue;
        }
        
        // get user name
        pwd = NULL;
        pwd = getpwuid(st.st_uid);
        if(pwd == NULL){
            fprintf(stderr, "getpwuid failed. st_uid = %d\n", st.st_uid);
            continue;
        }

        // get last modified time
        time = localtime(&st.st_atime);

        printf("%s\t%s\t%s", ent->d_name, pwd->pw_name, asctime(time));
    }
    // ファイル操作と同様、openしたストリームは必ずcloseする
    closedir(d);
}

 

実行結果は以下。

比較用にls -lコマンドの実行結果も合わせて載せます。

 

$ ./a.out  .
.	user	Wed Nov 20 16:19:37 2019
cd.c	user	Wed Nov 20 16:16:31 2019
..	user	Wed Nov 20 14:53:47 2019
ls.c	user	Wed Nov 20 16:18:43 2019
a.out	user	Wed Nov 20 16:19:38 2019
pwd.c	user	Wed Nov 20 14:53:51 2019
$ ls -l
合計 28
-rwxr-xr-x 1 user user 13008 11月 20 16:19 a.out
-rw-r--r-- 1 user user   975 11月 19 13:24 cd.c
-rw-r--r-- 1 user user  1554 11月 19 18:13 ls.c
-rw-r--r-- 1 user user   728 11月 19 13:20 pwd.c

 

まとめ

14章の内容はこんな感じです。

15章はまた今度。

 

おわり。

 

【スポンサーリンク】