You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
149 lines
4.4 KiB
149 lines
4.4 KiB
/*
|
|
* Copyright (C) 2021 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#if !defined _GNU_SOURCE
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
|
|
#include <err.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/prctl.h>
|
|
#include <sys/wait.h>
|
|
#include <termios.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#define MAX_TEST_DURATION 60
|
|
|
|
time_t start_timer(void);
|
|
int timer_active(time_t timer_started);
|
|
|
|
inline time_t start_timer() {
|
|
return time(NULL);
|
|
}
|
|
|
|
inline int timer_active(time_t timer_started) {
|
|
return time(NULL) < (timer_started + MAX_TEST_DURATION);
|
|
}
|
|
|
|
int main(void) {
|
|
sync(); /* we're probably gonna crash... */
|
|
|
|
// set test timer
|
|
const time_t timer_started = start_timer();
|
|
|
|
/*
|
|
* We may already be process group leader but want to be session leader;
|
|
* therefore, do everything in a child process.
|
|
*/
|
|
pid_t main_task = fork();
|
|
if (main_task == -1) err(EXIT_FAILURE, "initial fork");
|
|
if (main_task != 0) {
|
|
int status;
|
|
if (waitpid(main_task, &status, 0) != main_task) err(EXIT_FAILURE, "waitpid main_task");
|
|
return WEXITSTATUS(status);
|
|
}
|
|
|
|
printf("%d:test starts\n", getpid());
|
|
|
|
if (prctl(PR_SET_PDEATHSIG, SIGKILL)) err(EXIT_FAILURE, "PR_SET_PDEATHSIG");
|
|
if (getppid() == 1) exit(EXIT_FAILURE);
|
|
|
|
/* basic preparation */
|
|
if (signal(SIGTTOU, SIG_IGN)) err(EXIT_FAILURE, "signal");
|
|
if (setsid() == -1) err(EXIT_FAILURE, "start new session");
|
|
|
|
/* set up a new pty pair */
|
|
int ptmx = open("/dev/ptmx", O_RDWR);
|
|
if (ptmx == -1) err(EXIT_FAILURE, "open ptmx");
|
|
unlockpt(ptmx);
|
|
int tty = open(ptsname(ptmx), O_RDWR);
|
|
if (tty == -1) err(EXIT_FAILURE, "open tty");
|
|
|
|
/*
|
|
* Let a series of children change the ->pgrp pointer
|
|
* protected by the tty's ctrl_lock...
|
|
*/
|
|
pid_t child = fork();
|
|
if (child == -1) {
|
|
err(EXIT_FAILURE, "fork");
|
|
}
|
|
|
|
// grandchildren creator process
|
|
if (child == 0) {
|
|
int ret = EXIT_SUCCESS;
|
|
if (prctl(PR_SET_PDEATHSIG, SIGKILL)) err(EXIT_FAILURE, "PR_SET_PDEATHSIG");
|
|
if (getppid() == 1) exit(EXIT_FAILURE);
|
|
|
|
while (timer_active(timer_started)) {
|
|
pid_t grandchild = fork();
|
|
if (grandchild == -1) {
|
|
err(EXIT_FAILURE, "fork grandchild");
|
|
}
|
|
if (grandchild == 0) {
|
|
if (setpgid(0, 0)) err(EXIT_FAILURE, "setpgid");
|
|
int pgrp = getpid();
|
|
if (ioctl(tty, TIOCSPGRP, &pgrp)) {
|
|
err(EXIT_FAILURE, "TIOCSPGRP (tty)");
|
|
}
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
int status;
|
|
if (waitpid(grandchild, &status, 0) != grandchild)
|
|
err(EXIT_FAILURE, "waitpid for grandchild");
|
|
if ((ret = WEXITSTATUS(status)) != EXIT_SUCCESS) {
|
|
break;
|
|
}
|
|
} // end while(time)
|
|
exit(ret);
|
|
} // end grandchildren creator process
|
|
|
|
/*
|
|
* ... while the parent changes the same ->pgrp pointer under the
|
|
* ctrl_lock of the other side of the pty pair.
|
|
*/
|
|
int status;
|
|
const char* const TIOCSPGRP_ERROR = "TIOCSPGRP (ptmx)";
|
|
const char* const WAITPID_ERROR = "waitpid for grandchildren creator";
|
|
const char* message1 = NULL;
|
|
const char* message2 = NULL;
|
|
|
|
while (timer_active(timer_started)) {
|
|
int pgrp = getpid();
|
|
if (ioctl(ptmx, TIOCSPGRP, &pgrp)) {
|
|
message1 = TIOCSPGRP_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// wait for grandchildren creator to complete
|
|
if (waitpid(child, &status, 0) != child) {
|
|
message2 = WAITPID_ERROR;
|
|
}
|
|
|
|
// return exit status
|
|
if (message1 != NULL || message2 != NULL) {
|
|
err(EXIT_FAILURE, "%s %s", message1 != NULL ? message1 : "",
|
|
message2 != NULL ? message2 : "");
|
|
}
|
|
printf("%d:test completed\n", getpid());
|
|
return WEXITSTATUS(status);
|
|
}
|