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
42 static int fd;
43 static int v3;
44 static int acct_size;
45 static int clock_ticks;
46 static unsigned int rc;
47 static unsigned int start_time;
48
49 static 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
acct_version_is_3(void)56 static 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
run_command(void)70 static 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
verify_acct(void * acc,int elap_time)77 static 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
run(void)161 static 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
214 exit:
215 SAFE_CLOSE(fd);
216 }
217
setup(void)218 static 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
cleanup(void)250 static void cleanup(void)
251 {
252 if (fd > 0)
253 SAFE_CLOSE(fd);
254 acct(NULL);
255 }
256
257 static 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