xref: /linux/tools/testing/selftests/timens/procfs.c (revision f86fd32d)
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 #include <time.h>
16 
17 #include "log.h"
18 #include "timens.h"
19 
20 /*
21  * Test shouldn't be run for a day, so add 10 days to child
22  * time and check parent's time to be in the same day.
23  */
24 #define MAX_TEST_TIME_SEC		(60*5)
25 #define DAY_IN_SEC			(60*60*24)
26 #define TEN_DAYS_IN_SEC			(10*DAY_IN_SEC)
27 
28 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
29 
30 static int child_ns, parent_ns;
31 
32 static int switch_ns(int fd)
33 {
34 	if (setns(fd, CLONE_NEWTIME))
35 		return pr_perror("setns()");
36 
37 	return 0;
38 }
39 
40 static int init_namespaces(void)
41 {
42 	char path[] = "/proc/self/ns/time_for_children";
43 	struct stat st1, st2;
44 
45 	parent_ns = open(path, O_RDONLY);
46 	if (parent_ns <= 0)
47 		return pr_perror("Unable to open %s", path);
48 
49 	if (fstat(parent_ns, &st1))
50 		return pr_perror("Unable to stat the parent timens");
51 
52 	if (unshare_timens())
53 		return -1;
54 
55 	child_ns = open(path, O_RDONLY);
56 	if (child_ns <= 0)
57 		return pr_perror("Unable to open %s", path);
58 
59 	if (fstat(child_ns, &st2))
60 		return pr_perror("Unable to stat the timens");
61 
62 	if (st1.st_ino == st2.st_ino)
63 		return pr_err("The same child_ns after CLONE_NEWTIME");
64 
65 	if (_settime(CLOCK_BOOTTIME, TEN_DAYS_IN_SEC))
66 		return -1;
67 
68 	return 0;
69 }
70 
71 static int read_proc_uptime(struct timespec *uptime)
72 {
73 	unsigned long up_sec, up_nsec;
74 	FILE *proc;
75 
76 	proc = fopen("/proc/uptime", "r");
77 	if (proc == NULL) {
78 		pr_perror("Unable to open /proc/uptime");
79 		return -1;
80 	}
81 
82 	if (fscanf(proc, "%lu.%02lu", &up_sec, &up_nsec) != 2) {
83 		if (errno) {
84 			pr_perror("fscanf");
85 			return -errno;
86 		}
87 		pr_err("failed to parse /proc/uptime");
88 		return -1;
89 	}
90 	fclose(proc);
91 
92 	uptime->tv_sec = up_sec;
93 	uptime->tv_nsec = up_nsec;
94 	return 0;
95 }
96 
97 static int check_uptime(void)
98 {
99 	struct timespec uptime_new, uptime_old;
100 	time_t uptime_expected;
101 	double prec = MAX_TEST_TIME_SEC;
102 
103 	if (switch_ns(parent_ns))
104 		return pr_err("switch_ns(%d)", parent_ns);
105 
106 	if (read_proc_uptime(&uptime_old))
107 		return 1;
108 
109 	if (switch_ns(child_ns))
110 		return pr_err("switch_ns(%d)", child_ns);
111 
112 	if (read_proc_uptime(&uptime_new))
113 		return 1;
114 
115 	uptime_expected = uptime_old.tv_sec + TEN_DAYS_IN_SEC;
116 	if (fabs(difftime(uptime_new.tv_sec, uptime_expected)) > prec) {
117 		pr_fail("uptime in /proc/uptime: old %ld, new %ld [%ld]",
118 			uptime_old.tv_sec, uptime_new.tv_sec,
119 			uptime_old.tv_sec + TEN_DAYS_IN_SEC);
120 		return 1;
121 	}
122 
123 	ksft_test_result_pass("Passed for /proc/uptime\n");
124 	return 0;
125 }
126 
127 int main(int argc, char *argv[])
128 {
129 	int ret = 0;
130 
131 	nscheck();
132 
133 	ksft_set_plan(1);
134 
135 	if (init_namespaces())
136 		return 1;
137 
138 	ret |= check_uptime();
139 
140 	if (ret)
141 		ksft_exit_fail();
142 	ksft_exit_pass();
143 	return ret;
144 }
145