198 lines · c
1// SPDX-License-Identifier: GPL-2.02#define _GNU_SOURCE3#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 child21 * 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