xref: /linux/tools/testing/selftests/timens/procfs.c (revision 8e07e0e3964ca4e23ce7b68e2096fe660a888942)
1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <math.h>
6 #include <sched.h>
7 #include <stdio.h>
8 #include <stdbool.h>
9 #include <stdlib.h>
10 #include <sys/stat.h>
11 #include <sys/syscall.h>
12 #include <sys/types.h>
13 #include <time.h>
14 #include <unistd.h>
15 
16 #include "log.h"
17 #include "timens.h"
18 
19 /*
20  * Test shouldn't be run for a day, so add 10 days to child
21  * time and check parent's time to be in the same day.
22  */
23 #define MAX_TEST_TIME_SEC		(60*5)
24 #define DAY_IN_SEC			(60*60*24)
25 #define TEN_DAYS_IN_SEC			(10*DAY_IN_SEC)
26 
27 static int child_ns, parent_ns;
28 
29 static int switch_ns(int fd)
30 {
31 	if (setns(fd, CLONE_NEWTIME))
32 		return pr_perror("setns()");
33 
34 	return 0;
35 }
36 
37 static int init_namespaces(void)
38 {
39 	char path[] = "/proc/self/ns/time_for_children";
40 	struct stat st1, st2;
41 
42 	parent_ns = open(path, O_RDONLY);
43 	if (parent_ns <= 0)
44 		return pr_perror("Unable to open %s", path);
45 
46 	if (fstat(parent_ns, &st1))
47 		return pr_perror("Unable to stat the parent timens");
48 
49 	if (unshare_timens())
50 		return -1;
51 
52 	child_ns = open(path, O_RDONLY);
53 	if (child_ns <= 0)
54 		return pr_perror("Unable to open %s", path);
55 
56 	if (fstat(child_ns, &st2))
57 		return pr_perror("Unable to stat the timens");
58 
59 	if (st1.st_ino == st2.st_ino)
60 		return pr_err("The same child_ns after CLONE_NEWTIME");
61 
62 	if (_settime(CLOCK_BOOTTIME, TEN_DAYS_IN_SEC))
63 		return -1;
64 
65 	return 0;
66 }
67 
68 static int read_proc_uptime(struct timespec *uptime)
69 {
70 	unsigned long up_sec, up_nsec;
71 	FILE *proc;
72 
73 	proc = fopen("/proc/uptime", "r");
74 	if (proc == NULL) {
75 		pr_perror("Unable to open /proc/uptime");
76 		return -1;
77 	}
78 
79 	if (fscanf(proc, "%lu.%02lu", &up_sec, &up_nsec) != 2) {
80 		if (errno) {
81 			pr_perror("fscanf");
82 			return -errno;
83 		}
84 		pr_err("failed to parse /proc/uptime");
85 		return -1;
86 	}
87 	fclose(proc);
88 
89 	uptime->tv_sec = up_sec;
90 	uptime->tv_nsec = up_nsec;
91 	return 0;
92 }
93 
94 static int read_proc_stat_btime(unsigned long long *boottime_sec)
95 {
96 	FILE *proc;
97 	char line_buf[2048];
98 
99 	proc = fopen("/proc/stat", "r");
100 	if (proc == NULL) {
101 		pr_perror("Unable to open /proc/stat");
102 		return -1;
103 	}
104 
105 	while (fgets(line_buf, 2048, proc)) {
106 		if (sscanf(line_buf, "btime %llu", boottime_sec) != 1)
107 			continue;
108 		fclose(proc);
109 		return 0;
110 	}
111 	if (errno) {
112 		pr_perror("fscanf");
113 		fclose(proc);
114 		return -errno;
115 	}
116 	pr_err("failed to parse /proc/stat");
117 	fclose(proc);
118 	return -1;
119 }
120 
121 static int check_uptime(void)
122 {
123 	struct timespec uptime_new, uptime_old;
124 	time_t uptime_expected;
125 	double prec = MAX_TEST_TIME_SEC;
126 
127 	if (switch_ns(parent_ns))
128 		return pr_err("switch_ns(%d)", parent_ns);
129 
130 	if (read_proc_uptime(&uptime_old))
131 		return 1;
132 
133 	if (switch_ns(child_ns))
134 		return pr_err("switch_ns(%d)", child_ns);
135 
136 	if (read_proc_uptime(&uptime_new))
137 		return 1;
138 
139 	uptime_expected = uptime_old.tv_sec + TEN_DAYS_IN_SEC;
140 	if (fabs(difftime(uptime_new.tv_sec, uptime_expected)) > prec) {
141 		pr_fail("uptime in /proc/uptime: old %ld, new %ld [%ld]",
142 			uptime_old.tv_sec, uptime_new.tv_sec,
143 			uptime_old.tv_sec + TEN_DAYS_IN_SEC);
144 		return 1;
145 	}
146 
147 	ksft_test_result_pass("Passed for /proc/uptime\n");
148 	return 0;
149 }
150 
151 static int check_stat_btime(void)
152 {
153 	unsigned long long btime_new, btime_old;
154 	unsigned long long btime_expected;
155 
156 	if (switch_ns(parent_ns))
157 		return pr_err("switch_ns(%d)", parent_ns);
158 
159 	if (read_proc_stat_btime(&btime_old))
160 		return 1;
161 
162 	if (switch_ns(child_ns))
163 		return pr_err("switch_ns(%d)", child_ns);
164 
165 	if (read_proc_stat_btime(&btime_new))
166 		return 1;
167 
168 	btime_expected = btime_old - TEN_DAYS_IN_SEC;
169 	if (btime_new != btime_expected) {
170 		pr_fail("btime in /proc/stat: old %llu, new %llu [%llu]",
171 			btime_old, btime_new, btime_expected);
172 		return 1;
173 	}
174 
175 	ksft_test_result_pass("Passed for /proc/stat btime\n");
176 	return 0;
177 }
178 
179 int main(int argc, char *argv[])
180 {
181 	int ret = 0;
182 
183 	nscheck();
184 
185 	ksft_set_plan(2);
186 
187 	if (init_namespaces())
188 		return 1;
189 
190 	ret |= check_uptime();
191 	ret |= check_stat_btime();
192 
193 	if (ret)
194 		ksft_exit_fail();
195 	ksft_exit_pass();
196 	return ret;
197 }
198