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