acct
acctシステムコールは、全プロセス終了時のプロセス属性をファイルに追加更新します。カーネルプロセスも対象となり、カーネルプロセスのexeは、lkmをrmmod以外稀で、従ってシェルからfork新規プロセス作成によるコマンド履歴が主たる目的となっているわけです。
履歴ファイルのnameを、カレントプロセスのpidネームスペースのns->bacct->fileにnameのFILEを設定し、プロセス終了時にdo_exet()で、終了プロセスpidネームスペースのns->bacct->fileに、プロセスの属性をACCTに設定し、追加更新されます。
acctファイルはカレントプロセスのpidネームスペースで管理されるため、acct処理はpidネームスペース下の全プロセスに適用されます。
acct_file_reopen()でns->bacctのacctファイルをクローズし、新規acctファイルをns->bacctに設定します。新規acctファイルがNULLなら、既存acctファイルをクローズしるだけで、acct機能が無効とします。
登録したacctはacct_listにネームスペースに依存せずリストされます。これはumount時、全acctファイルをクローズするため走査参照されます。
ACCTファイルはumountされるまでクローズされません。従ってシステムがダウンした場合、それまでの全プロセス情報が更新されている保障はありません。
ACCT_VERSION=3なら、acct_v3で、ACCT_VERSION=1/2ならacctとなり、ACCT_VERSION=1/2の違いは、タイマ計算にかかるクロックタイマHZ=の違いです。
履歴ファイルのnameを、カレントプロセスのpidネームスペースのns->bacct->fileにnameのFILEを設定し、プロセス終了時にdo_exet()で、終了プロセスpidネームスペースのns->bacct->fileに、プロセスの属性をACCTに設定し、追加更新されます。
acctファイルはカレントプロセスのpidネームスペースで管理されるため、acct処理はpidネームスペース下の全プロセスに適用されます。
SYSCALL_DEFINE1(acct, const char __user *, name) { int error = 0; if (!capable(CAP_SYS_PACCT)) return -EPERM; if (name) { char *tmp = getname(name); if (IS_ERR(tmp)) return (PTR_ERR(tmp)); error = acct_on(tmp); putname(tmp); } else { struct bsd_acct_struct *acct; acct = task_active_pid_ns(current)->bacct; if (acct == NULL) return 0; spin_lock(&acct_lock); acct_file_reopen(acct, NULL, NULL); spin_unlock(&acct_lock); } return error; }acct_on()でacctファイルをO_WRONLY|O_APPEND|O_LARGEFILEでオープンします。O_CREAT無い故、既存ファイルでなければなりません。acctファイルの更新はカーネルが直接行うため、acctファイルのファイルID/ファイル属性に影響されず、参照するユーザの参照権限のみ考慮すればOKです。
acct_file_reopen()でns->bacctのacctファイルをクローズし、新規acctファイルをns->bacctに設定します。新規acctファイルがNULLなら、既存acctファイルをクローズしるだけで、acct機能が無効とします。
登録したacctはacct_listにネームスペースに依存せずリストされます。これはumount時、全acctファイルをクローズするため走査参照されます。
static int acct_on(char *name) { struct file *file; struct vfsmount *mnt; struct pid_namespace *ns; struct bsd_acct_struct *acct = NULL; file = filp_open(name, O_WRONLY|O_APPEND|O_LARGEFILE, 0); if (IS_ERR(file)) return PTR_ERR(file); if (!S_ISREG(file->f_path.dentry->d_inode->i_mode)) { filp_close(file, NULL); return -EACCES; } if (!file->f_op->write) { filp_close(file, NULL); return -EIO; } ns = task_active_pid_ns(current); if (ns->bacct == NULL) { acct = kzalloc(sizeof(struct bsd_acct_struct), GFP_KERNEL); if (acct == NULL) { filp_close(file, NULL); return -ENOMEM; } } spin_lock(&acct_lock); if (ns->bacct == NULL) { ns->bacct = acct; acct = NULL; } mnt = file->f_path.mnt; mnt_pin(mnt); acct_file_reopen(ns->bacct, file, ns); spin_unlock(&acct_lock); mntput(mnt); /* it's pinned, now give up active reference */ kfree(acct); return 0; } static void acct_file_reopen(struct bsd_acct_struct *acct, struct file *file, struct pid_namespace *ns) { struct file *old_acct = NULL; struct pid_namespace *old_ns = NULL; if (acct->file) { old_acct = acct->file; old_ns = acct->ns; acct->active = 0; acct->file = NULL; acct->ns = NULL; list_del(&acct->list); } if (file) { acct->file = file; acct->ns = ns; acct->needcheck = jiffies + ACCT_TIMEOUT*HZ; acct->active = 1; list_add(&acct->list, &acct_list); } if (old_acct) { mnt_unpin(old_acct->f_path.mnt); spin_unlock(&acct_lock); do_acct_process(acct, old_ns, old_acct); filp_close(old_acct, NULL); spin_lock(&acct_lock); } }do_exit()から acct_collect()でプロセス属性をcurrent->signal->pacctに設定し、acct_process()がコールされ、プロセスのネームスペースから親に遡って設定されている全ACCTファイルにプロセスのACCT情報が更新されます。
void acct_process(void) { struct pid_namespace *ns; for (ns = task_active_pid_ns(current); ns != NULL; ns = ns->parent) acct_process_in_ns(ns); } static void acct_process_in_ns(struct pid_namespace *ns) { struct file *file = NULL; struct bsd_acct_struct *acct; acct = ns->bacct; if (!acct || !acct->file) return; spin_lock(&acct_lock); file = acct->file; if (unlikely(!file)) { spin_unlock(&acct_lock); return; } get_file(file); spin_unlock(&acct_lock); do_acct_process(acct, ns, file); fput(file); }do_acct_process()でacctファイルを更新します。ACCTファイルはACCT_VERSION=1/2/3のカーネル依存の3つの属性構造体を有しており、ACCT_VERSIONに従ったacct_tに、current->signal->pacctを設定し、acct_tバイナリイメージで更新します。ACCT_VERSIONはac.ac_versionに、インデアン(ACCT_BYTEORDER)を考慮して設定されており、ユーザ空間からacct参照は、ac.ac_versionが1/2/3に応じて、参照する必要があります。
static void do_acct_process(struct bsd_acct_struct *acct, struct pid_namespace *ns, struct file *file) { struct pacct_struct *pacct = ¤t->signal->pacct; acct_t ac; mm_segment_t fs; unsigned long flim; u64 elapsed; u64 run_time; struct timespec uptime; struct tty_struct *tty; const struct cred *orig_cred; orig_cred = override_creds(file->f_cred); if (!check_free_space(acct, file)) goto out; memset(&ac, 0, sizeof(acct_t)); ac.ac_version = ACCT_VERSION | ACCT_BYTEORDER; strlcpy(ac.ac_comm, current->comm, sizeof(ac.ac_comm)); do_posix_clock_monotonic_gettime(&uptime); run_time = (u64)uptime.tv_sec*NSEC_PER_SEC + uptime.tv_nsec; run_time -= (u64)current->group_leader->start_time.tv_sec * NSEC_PER_SEC + current->group_leader->start_time.tv_nsec; elapsed = nsec_to_AHZ(run_time); #if ACCT_VERSION==3 ac.ac_etime = encode_float(elapsed); #else ac.ac_etime = encode_comp_t(elapsed < (unsigned long) -1l ? (unsigned long) elapsed : (unsigned long) -1l); #endif #if ACCT_VERSION==1 || ACCT_VERSION==2 { comp2_t etime = encode_comp2_t(elapsed); ac.ac_etime_hi = etime >> 16; ac.ac_etime_lo = (u16) etime; } #endif do_div(elapsed, AHZ); ac.ac_btime = get_seconds() - elapsed; ac.ac_uid = orig_cred->uid; ac.ac_gid = orig_cred->gid; #if ACCT_VERSION==2 ac.ac_ahz = AHZ; #endif #if ACCT_VERSION==1 || ACCT_VERSION==2 ac.ac_uid16 = ac.ac_uid; ac.ac_gid16 = ac.ac_gid; #endif #if ACCT_VERSION==3 ac.ac_pid = task_tgid_nr_ns(current, ns); rcu_read_lock(); ac.ac_ppid = task_tgid_nr_ns(rcu_dereference(current->real_parent), ns); rcu_read_unlock(); #endif spin_lock_irq(¤t->sighand->siglock); tty = current->signal->tty; /* Safe as we hold the siglock */ ac.ac_tty = tty ? old_encode_dev(tty_devnum(tty)) : 0; ac.ac_utime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_utime))); ac.ac_stime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_stime))); ac.ac_flag = pacct->ac_flag; ac.ac_mem = encode_comp_t(pacct->ac_mem); ac.ac_minflt = encode_comp_t(pacct->ac_minflt); ac.ac_majflt = encode_comp_t(pacct->ac_majflt); ac.ac_exitcode = pacct->ac_exitcode; spin_unlock_irq(¤t->sighand->siglock); ac.ac_io = encode_comp_t(0 /* current->io_usage */); /* %% */ ac.ac_rw = encode_comp_t(ac.ac_io / 1024); ac.ac_swaps = encode_comp_t(0); fs = get_fs(); set_fs(KERNEL_DS); flim = current->signal->rlim[RLIMIT_FSIZE].rlim_cur; current->signal->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY; file->f_op->write(file, (char *)&ac, sizeof(acct_t), &file->f_pos); current->signal->rlim[RLIMIT_FSIZE].rlim_cur = flim; set_fs(fs); out: revert_creds(orig_cred); }
補足
ACCTファイル更新は、current->signal->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITYで、サイズの制限を無効とし、更新後プロセスの元サイズに戻しています。これはACCTファイルはユーザ空間から更新できるからです。ACCTファイルはumountされるまでクローズされません。従ってシステムがダウンした場合、それまでの全プロセス情報が更新されている保障はありません。
ACCT_VERSION=3なら、acct_v3で、ACCT_VERSION=1/2ならacctとなり、ACCT_VERSION=1/2の違いは、タイマ計算にかかるクロックタイマHZ=の違いです。
#ifdef CONFIG_BSD_PROCESS_ACCT_V3 #define ACCT_VERSION 3 #define AHZ 100 typedef struct acct_v3 acct_t; #else #ifdef CONFIG_M68K #define ACCT_VERSION 1 #else #define ACCT_VERSION 2 #endif #define AHZ (USER_HZ) typedef struct acct acct_t; #endif struct acct { char ac_flag; /* Flags */ char ac_version; /* Always set to ACCT_VERSION */ /* for binary compatibility back until 2.0 */ __u16 ac_uid16; /* LSB of Real User ID */ __u16 ac_gid16; /* LSB of Real Group ID */ __u16 ac_tty; /* Control Terminal */ __u32 ac_btime; /* Process Creation Time */ comp_t ac_utime; /* User Time */ comp_t ac_stime; /* System Time */ comp_t ac_etime; /* Elapsed Time */ comp_t ac_mem; /* Average Memory Usage */ comp_t ac_io; /* Chars Transferred */ comp_t ac_rw; /* Blocks Read or Written */ comp_t ac_minflt; /* Minor Pagefaults */ comp_t ac_majflt; /* Major Pagefaults */ comp_t ac_swaps; /* Number of Swaps */ /* m68k had no padding here. */ #if !defined(CONFIG_M68K) || !defined(__KERNEL__) __u16 ac_ahz; /* AHZ */ #endif __u32 ac_exitcode; /* Exitcode */ char ac_comm[ACCT_COMM + 1]; /* Command Name */ __u8 ac_etime_hi; /* Elapsed Time MSB */ __u16 ac_etime_lo; /* Elapsed Time LSB */ __u32 ac_uid; /* Real User ID */ __u32 ac_gid; /* Real Group ID */ }; struct acct_v3 { char ac_flag; /* Flags */ char ac_version; /* Always set to ACCT_VERSION */ __u16 ac_tty; /* Control Terminal */ __u32 ac_exitcode; /* Exitcode */ __u32 ac_uid; /* Real User ID */ __u32 ac_gid; /* Real Group ID */ __u32 ac_pid; /* Process ID */ __u32 ac_ppid; /* Parent Process ID */ __u32 ac_btime; /* Process Creation Time */ #ifdef __KERNEL__ __u32 ac_etime; /* Elapsed Time */ #else float ac_etime; /* Elapsed Time */ #endif comp_t ac_utime; /* User Time */ comp_t ac_stime; /* System Time */ comp_t ac_mem; /* Average Memory Usage */ comp_t ac_io; /* Chars Transferred */ comp_t ac_rw; /* Blocks Read or Written */ comp_t ac_minflt; /* Minor Pagefaults */ comp_t ac_majflt; /* Major Pagefaults */ comp_t ac_swaps; /* Number of Swaps */ char ac_comm[ACCT_COMM]; /* Command Name */ };