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
27static int child_ns, parent_ns;
28
29static int switch_ns(int fd)
30{
31	if (setns(fd, CLONE_NEWTIME))
32		return pr_perror("setns()");
33
34	return 0;
35}
36
37static 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
68static 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
94static 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
121static 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
151static 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
179int 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