1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) SUSE LLC, 2019 4 * Author: Christian Amann <camann@suse.com> 5 */ 6/*\ 7 * [DOCUMENTATION] 8 * 9 * This tests if the kernel writes correct data to the 10 * process accounting file. 11 * 12 * First, system-wide process accounting is turned on and the output gets 13 * directed to a defined file. After that a dummy program is run in order 14 * to generate data and the process accounting gets turned off again. 15 * 16 * To verify the written data, the entries of the accounting file get 17 * parsed into the corresponding acct structure. Since it cannot be guaranteed 18 * that only the command issued by this test gets written into the accounting 19 * file, the contents get parsed until the correct entry is found, or EOF 20 * is reached. 21 * 22 * This is also accidental regression test for: 23 * 4d9570158b626 kernel/acct.c: fix the acct->needcheck check in check_free_space() 24 */ 25 26#include <sys/stat.h> 27#include <errno.h> 28#include <string.h> 29#include <time.h> 30#include <unistd.h> 31#include "tst_kconfig.h" 32#include "tst_test.h" 33#include "lapi/acct.h" 34 35#define COMMAND "acct02_helper" 36#define OUTPUT_FILE "acct_file" 37 38#define UNPACK(x) ((x & 0x1fff) << (((x >> 13) & 0x7) * 3)) 39#define ACCT_MEMBER(x) (v3 ? ((struct acct_v3 *)acc)->x : ((struct acct *)acc)->x) 40#define ACCT_MEMBER_V3(x) (((struct acct_v3 *)acc)->x) 41 42static int fd; 43static int v3; 44static int acct_size; 45static int clock_ticks; 46static unsigned int rc; 47static unsigned int start_time; 48 49static union acct_union { 50 struct acct v0; 51 struct acct_v3 v3; 52} acct_struct; 53 54#define ACCT_V3 "CONFIG_BSD_PROCESS_ACCT_V3" 55 56static int acct_version_is_3(void) 57{ 58 struct tst_kconfig_var kconfig = { 59 .id = ACCT_V3, 60 .id_len = sizeof(ACCT_V3)-1, 61 }; 62 63 tst_kconfig_read(&kconfig, 1); 64 65 tst_res(TINFO, ACCT_V3 "=%c", kconfig.choice); 66 67 return kconfig.choice == 'y'; 68} 69 70static void run_command(void) 71{ 72 const char *const cmd[] = {COMMAND, NULL}; 73 74 rc = tst_cmd(cmd, NULL, NULL, TST_CMD_PASS_RETVAL) << 8; 75} 76 77static int verify_acct(void *acc, int elap_time) 78{ 79 int sys_time = UNPACK(ACCT_MEMBER(ac_stime)); 80 int user_time = UNPACK(ACCT_MEMBER(ac_stime)); 81 unsigned int btime_diff; 82 int ret = 0; 83 float tmp2; 84 85 if (strcmp(ACCT_MEMBER(ac_comm), COMMAND)) { 86 tst_res(TINFO, "ac_comm != '%s' ('%s')", COMMAND, 87 ACCT_MEMBER(ac_comm)); 88 ret = 1; 89 } 90 91 if (start_time > ACCT_MEMBER(ac_btime)) 92 btime_diff = start_time - ACCT_MEMBER(ac_btime); 93 else 94 btime_diff = ACCT_MEMBER(ac_btime) - start_time; 95 96 if (btime_diff > 7200) { 97 tst_res(TINFO, "ac_btime_diff %u", btime_diff); 98 ret = 1; 99 } 100 101 if (ACCT_MEMBER(ac_uid) != getuid()) { 102 tst_res(TINFO, "ac_uid != %d (%d)", getuid(), 103 ACCT_MEMBER(ac_uid)); 104 ret = 1; 105 } 106 107 if (ACCT_MEMBER(ac_gid) != getgid()) { 108 tst_res(TINFO, "ac_gid != %d (%d)", getgid(), 109 ACCT_MEMBER(ac_gid)); 110 ret = 1; 111 } 112 113 tmp2 = user_time/clock_ticks; 114 if (tmp2 > 1) { 115 tst_res(TINFO, "user_time/clock_ticks > 1 (%d/%d: %.2f)", 116 user_time, clock_ticks, tmp2); 117 ret = 1; 118 } 119 120 tmp2 = sys_time/clock_ticks; 121 if (tmp2 > 1) { 122 tst_res(TINFO, "sys_time/clock_ticks > 1 (%d/%d: %.2f)", 123 sys_time, clock_ticks, tmp2); 124 ret = 1; 125 } 126 127 tmp2 = elap_time/clock_ticks; 128 if (tmp2 >= 2) { 129 tst_res(TINFO, "elap_time/clock_ticks >= 2 (%d/%d: %.2f)", 130 elap_time, clock_ticks, tmp2); 131 ret = 1; 132 } 133 134 if (ACCT_MEMBER(ac_exitcode) != rc) { 135 tst_res(TINFO, "ac_exitcode != %d (%d)", rc, 136 ACCT_MEMBER(ac_exitcode)); 137 ret = 1; 138 } 139 if (!v3) 140 return ret; 141 142 if (ACCT_MEMBER_V3(ac_ppid) != (uint32_t)getpid()) { 143 tst_res(TINFO, "ac_ppid != %d (%d)", (uint32_t)getpid(), 144 ACCT_MEMBER_V3(ac_ppid)); 145 ret = 1; 146 } 147 148 if (ACCT_MEMBER_V3(ac_version) != (char)(3 | ACCT_BYTEORDER)) { 149 tst_res(TINFO, "ac_version != 3 (%d)", 150 ACCT_MEMBER_V3(ac_version)); 151 ret = 1; 152 } 153 154 if (ACCT_MEMBER_V3(ac_pid) < 1) { 155 tst_res(TINFO, "ac_pid < 1 (%d)", ACCT_MEMBER_V3(ac_pid)); 156 ret = 1; 157 } 158 return ret; 159} 160 161static void run(void) 162{ 163 int read_bytes, ret; 164 int entry_count = 0, i = 0; 165 166 fd = SAFE_OPEN(OUTPUT_FILE, O_RDWR | O_CREAT, 0644); 167 168 TEST(acct(OUTPUT_FILE)); 169 if (TST_RET == -1) 170 tst_brk(TBROK | TTERRNO, "Could not set acct output file"); 171 172 start_time = time(NULL); 173 run_command(); 174 acct(NULL); 175 176 do { 177 read_bytes = SAFE_READ(0, fd, &acct_struct, acct_size); 178 179 if (i == 0 && read_bytes == 0) { 180 tst_res(TFAIL, "acct file is empty"); 181 goto exit; 182 } 183 184 if (read_bytes == 0) { 185 tst_res(TFAIL, "end of file reached"); 186 goto exit; 187 } 188 189 if (read_bytes != acct_size) { 190 tst_res(TFAIL, "incomplete read %i bytes, expected %i", 191 read_bytes, acct_size); 192 goto exit; 193 } 194 195 tst_res(TINFO, "== entry %d ==", ++i); 196 197 if (v3) 198 ret = verify_acct(&acct_struct.v3, acct_struct.v3.ac_etime); 199 else 200 ret = verify_acct(&acct_struct.v0, UNPACK(acct_struct.v0.ac_etime)); 201 202 if (read_bytes) 203 entry_count++; 204 } while (read_bytes == acct_size && ret); 205 206 tst_res(TINFO, "Number of accounting file entries tested: %d", 207 entry_count); 208 209 if (ret) 210 tst_res(TFAIL, "acct() wrote incorrect file contents!"); 211 else 212 tst_res(TPASS, "acct() wrote correct file contents!"); 213 214exit: 215 SAFE_CLOSE(fd); 216} 217 218static void setup(void) 219{ 220 struct statfs buf; 221 222 clock_ticks = SAFE_SYSCONF(_SC_CLK_TCK); 223 224 SAFE_STATFS(".", &buf); 225 226 float avail = (100.00 * buf.f_bavail) / buf.f_blocks; 227 228 /* The accounting data are silently discarded on nearly FS */ 229 if (avail < 4.1) { 230 tst_brk(TCONF, 231 "Less than 4.1%% (%.2f) of free space on filesystem", 232 avail); 233 } 234 235 TEST(acct(NULL)); 236 if (TST_RET == -1) 237 tst_brk(TBROK | TTERRNO, 238 "acct() system call returned with error"); 239 240 v3 = acct_version_is_3(); 241 if (v3) { 242 tst_res(TINFO, "Verifying using 'struct acct_v3'"); 243 acct_size = sizeof(struct acct_v3); 244 } else { 245 tst_res(TINFO, "Verifying using 'struct acct'"); 246 acct_size = sizeof(struct acct); 247 } 248} 249 250static void cleanup(void) 251{ 252 if (fd > 0) 253 SAFE_CLOSE(fd); 254 acct(NULL); 255} 256 257static struct tst_test test = { 258 .test_all = run, 259 .needs_kconfigs = (const char *[]) { 260 "CONFIG_BSD_PROCESS_ACCT", 261 NULL 262 }, 263 .setup = setup, 264 .cleanup = cleanup, 265 .needs_tmpdir = 1, 266 .needs_root = 1, 267 .tags = (const struct tst_tag[]) { 268 {"linux-git", "4d9570158b626"}, 269 {} 270 } 271}; 272