162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#define _GNU_SOURCE
362306a36Sopenharmony_ci#include <errno.h>
462306a36Sopenharmony_ci#include <fcntl.h>
562306a36Sopenharmony_ci#include <math.h>
662306a36Sopenharmony_ci#include <sched.h>
762306a36Sopenharmony_ci#include <stdio.h>
862306a36Sopenharmony_ci#include <stdbool.h>
962306a36Sopenharmony_ci#include <stdlib.h>
1062306a36Sopenharmony_ci#include <sys/stat.h>
1162306a36Sopenharmony_ci#include <sys/syscall.h>
1262306a36Sopenharmony_ci#include <sys/types.h>
1362306a36Sopenharmony_ci#include <time.h>
1462306a36Sopenharmony_ci#include <unistd.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "log.h"
1762306a36Sopenharmony_ci#include "timens.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*
2062306a36Sopenharmony_ci * Test shouldn't be run for a day, so add 10 days to child
2162306a36Sopenharmony_ci * time and check parent's time to be in the same day.
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci#define MAX_TEST_TIME_SEC		(60*5)
2462306a36Sopenharmony_ci#define DAY_IN_SEC			(60*60*24)
2562306a36Sopenharmony_ci#define TEN_DAYS_IN_SEC			(10*DAY_IN_SEC)
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int child_ns, parent_ns;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic int switch_ns(int fd)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	if (setns(fd, CLONE_NEWTIME))
3262306a36Sopenharmony_ci		return pr_perror("setns()");
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	return 0;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic int init_namespaces(void)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	char path[] = "/proc/self/ns/time_for_children";
4062306a36Sopenharmony_ci	struct stat st1, st2;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	parent_ns = open(path, O_RDONLY);
4362306a36Sopenharmony_ci	if (parent_ns <= 0)
4462306a36Sopenharmony_ci		return pr_perror("Unable to open %s", path);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (fstat(parent_ns, &st1))
4762306a36Sopenharmony_ci		return pr_perror("Unable to stat the parent timens");
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if (unshare_timens())
5062306a36Sopenharmony_ci		return -1;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	child_ns = open(path, O_RDONLY);
5362306a36Sopenharmony_ci	if (child_ns <= 0)
5462306a36Sopenharmony_ci		return pr_perror("Unable to open %s", path);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (fstat(child_ns, &st2))
5762306a36Sopenharmony_ci		return pr_perror("Unable to stat the timens");
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (st1.st_ino == st2.st_ino)
6062306a36Sopenharmony_ci		return pr_err("The same child_ns after CLONE_NEWTIME");
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (_settime(CLOCK_BOOTTIME, TEN_DAYS_IN_SEC))
6362306a36Sopenharmony_ci		return -1;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return 0;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int read_proc_uptime(struct timespec *uptime)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	unsigned long up_sec, up_nsec;
7162306a36Sopenharmony_ci	FILE *proc;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	proc = fopen("/proc/uptime", "r");
7462306a36Sopenharmony_ci	if (proc == NULL) {
7562306a36Sopenharmony_ci		pr_perror("Unable to open /proc/uptime");
7662306a36Sopenharmony_ci		return -1;
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (fscanf(proc, "%lu.%02lu", &up_sec, &up_nsec) != 2) {
8062306a36Sopenharmony_ci		if (errno) {
8162306a36Sopenharmony_ci			pr_perror("fscanf");
8262306a36Sopenharmony_ci			return -errno;
8362306a36Sopenharmony_ci		}
8462306a36Sopenharmony_ci		pr_err("failed to parse /proc/uptime");
8562306a36Sopenharmony_ci		return -1;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci	fclose(proc);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	uptime->tv_sec = up_sec;
9062306a36Sopenharmony_ci	uptime->tv_nsec = up_nsec;
9162306a36Sopenharmony_ci	return 0;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic int read_proc_stat_btime(unsigned long long *boottime_sec)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	FILE *proc;
9762306a36Sopenharmony_ci	char line_buf[2048];
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	proc = fopen("/proc/stat", "r");
10062306a36Sopenharmony_ci	if (proc == NULL) {
10162306a36Sopenharmony_ci		pr_perror("Unable to open /proc/stat");
10262306a36Sopenharmony_ci		return -1;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	while (fgets(line_buf, 2048, proc)) {
10662306a36Sopenharmony_ci		if (sscanf(line_buf, "btime %llu", boottime_sec) != 1)
10762306a36Sopenharmony_ci			continue;
10862306a36Sopenharmony_ci		fclose(proc);
10962306a36Sopenharmony_ci		return 0;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci	if (errno) {
11262306a36Sopenharmony_ci		pr_perror("fscanf");
11362306a36Sopenharmony_ci		fclose(proc);
11462306a36Sopenharmony_ci		return -errno;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci	pr_err("failed to parse /proc/stat");
11762306a36Sopenharmony_ci	fclose(proc);
11862306a36Sopenharmony_ci	return -1;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic int check_uptime(void)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct timespec uptime_new, uptime_old;
12462306a36Sopenharmony_ci	time_t uptime_expected;
12562306a36Sopenharmony_ci	double prec = MAX_TEST_TIME_SEC;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (switch_ns(parent_ns))
12862306a36Sopenharmony_ci		return pr_err("switch_ns(%d)", parent_ns);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (read_proc_uptime(&uptime_old))
13162306a36Sopenharmony_ci		return 1;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (switch_ns(child_ns))
13462306a36Sopenharmony_ci		return pr_err("switch_ns(%d)", child_ns);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (read_proc_uptime(&uptime_new))
13762306a36Sopenharmony_ci		return 1;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	uptime_expected = uptime_old.tv_sec + TEN_DAYS_IN_SEC;
14062306a36Sopenharmony_ci	if (fabs(difftime(uptime_new.tv_sec, uptime_expected)) > prec) {
14162306a36Sopenharmony_ci		pr_fail("uptime in /proc/uptime: old %ld, new %ld [%ld]",
14262306a36Sopenharmony_ci			uptime_old.tv_sec, uptime_new.tv_sec,
14362306a36Sopenharmony_ci			uptime_old.tv_sec + TEN_DAYS_IN_SEC);
14462306a36Sopenharmony_ci		return 1;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	ksft_test_result_pass("Passed for /proc/uptime\n");
14862306a36Sopenharmony_ci	return 0;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic int check_stat_btime(void)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	unsigned long long btime_new, btime_old;
15462306a36Sopenharmony_ci	unsigned long long btime_expected;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (switch_ns(parent_ns))
15762306a36Sopenharmony_ci		return pr_err("switch_ns(%d)", parent_ns);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (read_proc_stat_btime(&btime_old))
16062306a36Sopenharmony_ci		return 1;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (switch_ns(child_ns))
16362306a36Sopenharmony_ci		return pr_err("switch_ns(%d)", child_ns);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (read_proc_stat_btime(&btime_new))
16662306a36Sopenharmony_ci		return 1;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	btime_expected = btime_old - TEN_DAYS_IN_SEC;
16962306a36Sopenharmony_ci	if (btime_new != btime_expected) {
17062306a36Sopenharmony_ci		pr_fail("btime in /proc/stat: old %llu, new %llu [%llu]",
17162306a36Sopenharmony_ci			btime_old, btime_new, btime_expected);
17262306a36Sopenharmony_ci		return 1;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	ksft_test_result_pass("Passed for /proc/stat btime\n");
17662306a36Sopenharmony_ci	return 0;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ciint main(int argc, char *argv[])
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	int ret = 0;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	nscheck();
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	ksft_set_plan(2);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (init_namespaces())
18862306a36Sopenharmony_ci		return 1;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	ret |= check_uptime();
19162306a36Sopenharmony_ci	ret |= check_stat_btime();
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (ret)
19462306a36Sopenharmony_ci		ksft_exit_fail();
19562306a36Sopenharmony_ci	ksft_exit_pass();
19662306a36Sopenharmony_ci	return ret;
19762306a36Sopenharmony_ci}
198