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