• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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