1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #if !defined _GNU_SOURCE
18 #define _GNU_SOURCE
19 #endif
20
21 #include <err.h>
22 #include <fcntl.h>
23 #include <signal.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <sys/ioctl.h>
27 #include <sys/prctl.h>
28 #include <sys/wait.h>
29 #include <termios.h>
30 #include <time.h>
31 #include <unistd.h>
32
33 #define MAX_TEST_DURATION 60
34
35 time_t start_timer(void);
36 int timer_active(time_t timer_started);
37
start_timer()38 inline time_t start_timer() {
39 return time(NULL);
40 }
41
timer_active(time_t timer_started)42 inline int timer_active(time_t timer_started) {
43 return time(NULL) < (timer_started + MAX_TEST_DURATION);
44 }
45
main(void)46 int main(void) {
47 sync(); /* we're probably gonna crash... */
48
49 // set test timer
50 const time_t timer_started = start_timer();
51
52 /*
53 * We may already be process group leader but want to be session leader;
54 * therefore, do everything in a child process.
55 */
56 pid_t main_task = fork();
57 if (main_task == -1) err(EXIT_FAILURE, "initial fork");
58 if (main_task != 0) {
59 int status;
60 if (waitpid(main_task, &status, 0) != main_task) err(EXIT_FAILURE, "waitpid main_task");
61 return WEXITSTATUS(status);
62 }
63
64 printf("%d:test starts\n", getpid());
65
66 if (prctl(PR_SET_PDEATHSIG, SIGKILL)) err(EXIT_FAILURE, "PR_SET_PDEATHSIG");
67 if (getppid() == 1) exit(EXIT_FAILURE);
68
69 /* basic preparation */
70 if (signal(SIGTTOU, SIG_IGN)) err(EXIT_FAILURE, "signal");
71 if (setsid() == -1) err(EXIT_FAILURE, "start new session");
72
73 /* set up a new pty pair */
74 int ptmx = open("/dev/ptmx", O_RDWR);
75 if (ptmx == -1) err(EXIT_FAILURE, "open ptmx");
76 unlockpt(ptmx);
77 int tty = open(ptsname(ptmx), O_RDWR);
78 if (tty == -1) err(EXIT_FAILURE, "open tty");
79
80 /*
81 * Let a series of children change the ->pgrp pointer
82 * protected by the tty's ctrl_lock...
83 */
84 pid_t child = fork();
85 if (child == -1) {
86 err(EXIT_FAILURE, "fork");
87 }
88
89 // grandchildren creator process
90 if (child == 0) {
91 int ret = EXIT_SUCCESS;
92 if (prctl(PR_SET_PDEATHSIG, SIGKILL)) err(EXIT_FAILURE, "PR_SET_PDEATHSIG");
93 if (getppid() == 1) exit(EXIT_FAILURE);
94
95 while (timer_active(timer_started)) {
96 pid_t grandchild = fork();
97 if (grandchild == -1) {
98 err(EXIT_FAILURE, "fork grandchild");
99 }
100 if (grandchild == 0) {
101 if (setpgid(0, 0)) err(EXIT_FAILURE, "setpgid");
102 int pgrp = getpid();
103 if (ioctl(tty, TIOCSPGRP, &pgrp)) {
104 err(EXIT_FAILURE, "TIOCSPGRP (tty)");
105 }
106 exit(EXIT_SUCCESS);
107 }
108 int status;
109 if (waitpid(grandchild, &status, 0) != grandchild)
110 err(EXIT_FAILURE, "waitpid for grandchild");
111 if ((ret = WEXITSTATUS(status)) != EXIT_SUCCESS) {
112 break;
113 }
114 } // end while(time)
115 exit(ret);
116 } // end grandchildren creator process
117
118 /*
119 * ... while the parent changes the same ->pgrp pointer under the
120 * ctrl_lock of the other side of the pty pair.
121 */
122 int status;
123 const char* const TIOCSPGRP_ERROR = "TIOCSPGRP (ptmx)";
124 const char* const WAITPID_ERROR = "waitpid for grandchildren creator";
125 const char* message1 = NULL;
126 const char* message2 = NULL;
127
128 while (timer_active(timer_started)) {
129 int pgrp = getpid();
130 if (ioctl(ptmx, TIOCSPGRP, &pgrp)) {
131 message1 = TIOCSPGRP_ERROR;
132 break;
133 }
134 }
135
136 // wait for grandchildren creator to complete
137 if (waitpid(child, &status, 0) != child) {
138 message2 = WAITPID_ERROR;
139 }
140
141 // return exit status
142 if (message1 != NULL || message2 != NULL) {
143 err(EXIT_FAILURE, "%s %s", message1 != NULL ? message1 : "",
144 message2 != NULL ? message2 : "");
145 }
146 printf("%d:test completed\n", getpid());
147 return WEXITSTATUS(status);
148 }
149