1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 #include <errno.h> 4 #include <fcntl.h> 5 #include <sched.h> 6 #include <stdio.h> 7 #include <stdbool.h> 8 #include <sys/stat.h> 9 #include <sys/syscall.h> 10 #include <sys/types.h> 11 #include <sys/wait.h> 12 #include <time.h> 13 #include <unistd.h> 14 #include <string.h> 15 #include <pthread.h> 16 17 #include "log.h" 18 #include "timens.h" 19 20 #define OFFSET (36000) 21 22 struct thread_args { 23 char *tst_name; 24 struct timespec *now; 25 }; 26 27 static void *tcheck(void *_args) 28 { 29 struct thread_args *args = _args; 30 struct timespec *now = args->now, tst; 31 int i; 32 33 for (i = 0; i < 2; i++) { 34 _gettime(CLOCK_MONOTONIC, &tst, i); 35 if (labs(tst.tv_sec - now->tv_sec) > 5) { 36 pr_fail("%s: in-thread: unexpected value: %ld (%ld)\n", 37 args->tst_name, tst.tv_sec, now->tv_sec); 38 return (void *)1UL; 39 } 40 } 41 return NULL; 42 } 43 44 static int check_in_thread(char *tst_name, struct timespec *now) 45 { 46 struct thread_args args = { 47 .tst_name = tst_name, 48 .now = now, 49 }; 50 pthread_t th; 51 void *retval; 52 53 if (pthread_create(&th, NULL, tcheck, &args)) 54 return pr_perror("thread"); 55 if (pthread_join(th, &retval)) 56 return pr_perror("pthread_join"); 57 return !(retval == NULL); 58 } 59 60 static int check(char *tst_name, struct timespec *now) 61 { 62 struct timespec tst; 63 int i; 64 65 for (i = 0; i < 2; i++) { 66 _gettime(CLOCK_MONOTONIC, &tst, i); 67 if (labs(tst.tv_sec - now->tv_sec) > 5) 68 return pr_fail("%s: unexpected value: %ld (%ld)\n", 69 tst_name, tst.tv_sec, now->tv_sec); 70 } 71 if (check_in_thread(tst_name, now)) 72 return 1; 73 ksft_test_result_pass("%s\n", tst_name); 74 return 0; 75 } 76 77 int main(int argc, char *argv[]) 78 { 79 struct timespec now; 80 int status; 81 pid_t pid; 82 83 if (argc > 1) { 84 char *endptr; 85 86 ksft_cnt.ksft_pass = 1; 87 now.tv_sec = strtoul(argv[1], &endptr, 0); 88 if (*endptr != 0) 89 return pr_perror("strtoul"); 90 91 return check("child after exec", &now); 92 } 93 94 ksft_print_header(); 95 96 nscheck(); 97 98 ksft_set_plan(4); 99 100 clock_gettime(CLOCK_MONOTONIC, &now); 101 102 if (unshare_timens()) 103 return 1; 104 105 if (_settime(CLOCK_MONOTONIC, OFFSET)) 106 return 1; 107 108 if (check("parent before vfork", &now)) 109 return 1; 110 111 pid = vfork(); 112 if (pid < 0) 113 return pr_perror("fork"); 114 115 if (pid == 0) { 116 char now_str[64]; 117 char *cargv[] = {"exec", now_str, NULL}; 118 char *cenv[] = {NULL}; 119 120 /* Check for proper vvar offsets after execve. */ 121 snprintf(now_str, sizeof(now_str), "%ld", now.tv_sec + OFFSET); 122 execve("/proc/self/exe", cargv, cenv); 123 pr_perror("execve"); 124 _exit(1); 125 } 126 127 if (waitpid(pid, &status, 0) != pid) 128 return pr_perror("waitpid"); 129 130 if (status) 131 ksft_exit_fail(); 132 ksft_inc_pass_cnt(); 133 ksft_test_result_pass("wait for child\n"); 134 135 /* Check that we are still in the source timens. */ 136 if (check("parent after vfork", &now)) 137 return 1; 138 139 ksft_exit_pass(); 140 return 0; 141 } 142