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