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