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