mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-22 17:03:23 +00:00
Add job control regression tests
This commit is contained in:
committed by
Jianfeng Jiang
parent
ff907d1131
commit
da82ca619f
235
test/apps/process/job_control.c
Normal file
235
test/apps/process/job_control.c
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
#include "../network/test.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pty.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
static pid_t sid, child;
|
||||||
|
static int master, slave;
|
||||||
|
|
||||||
|
FN_SETUP(openpty)
|
||||||
|
{
|
||||||
|
CHECK(openpty(&master, &slave, NULL, NULL, NULL));
|
||||||
|
}
|
||||||
|
END_SETUP()
|
||||||
|
|
||||||
|
FN_SETUP(run_in_new_session)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (CHECK(fork()) != 0) {
|
||||||
|
CHECK_WITH(wait(&status),
|
||||||
|
WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
sid = CHECK(setsid());
|
||||||
|
|
||||||
|
if ((child = CHECK(fork())) == 0) {
|
||||||
|
child = getpid();
|
||||||
|
|
||||||
|
// TODO: Linux allows to specify PIDs (instead of PGIDs) as
|
||||||
|
// parameters to `TIOCSPGRP`. We may want to support this as
|
||||||
|
// well. For more details, see:
|
||||||
|
// <https://elixir.bootlin.com/linux/v6.14.5/source/drivers/tty/tty_jobctrl.c#L434-L453>.
|
||||||
|
CHECK(setpgrp());
|
||||||
|
}
|
||||||
|
|
||||||
|
signal(SIGHUP, SIG_IGN);
|
||||||
|
signal(SIGTTIN, SIG_IGN);
|
||||||
|
|
||||||
|
// TODO: We should forbid some TTY operations (e.g., `TIOCSPGRP`) if
|
||||||
|
// the `SIGTTOU` signal is not blocked or ignored and the current
|
||||||
|
// process is not in the foreground process group. However, this is
|
||||||
|
// not currently implemented in Asterinas yet.
|
||||||
|
signal(SIGTTOU, SIG_IGN);
|
||||||
|
}
|
||||||
|
END_SETUP()
|
||||||
|
|
||||||
|
// From now on, we'll run all the tests twice, once as a session leader
|
||||||
|
// and once not as a session leader. Most tests will only work in one of
|
||||||
|
// the two cases, so check at the beginning and skip the test if it's
|
||||||
|
// not the expected case.
|
||||||
|
static int is_leader(void)
|
||||||
|
{
|
||||||
|
return getpid() == sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Find a better way to synchronize between the two processes.
|
||||||
|
#define __NAMED_BARRIER(name) \
|
||||||
|
FN_SETUP(name) \
|
||||||
|
{ \
|
||||||
|
CHECK(usleep(100 * 1000)); \
|
||||||
|
} \
|
||||||
|
END_SETUP()
|
||||||
|
#define NAMED_BARRIER(name) __NAMED_BARRIER(__CONCAT(barrier, name))
|
||||||
|
#define BARRIER(name) NAMED_BARRIER(__LINE__)
|
||||||
|
|
||||||
|
FN_TEST(not_our_tty)
|
||||||
|
{
|
||||||
|
pid_t arg;
|
||||||
|
|
||||||
|
// Operate on the controlling session
|
||||||
|
|
||||||
|
TEST_ERRNO(ioctl(master, TIOCNOTTY), ENOTTY);
|
||||||
|
TEST_ERRNO(ioctl(slave, TIOCNOTTY), ENOTTY);
|
||||||
|
TEST_ERRNO(ioctl(STDIN_FILENO, TIOCNOTTY), ENOTTY);
|
||||||
|
|
||||||
|
TEST_ERRNO(ioctl(master, TIOCGSID, &arg), ENOTTY);
|
||||||
|
TEST_ERRNO(ioctl(slave, TIOCGSID, &arg), ENOTTY);
|
||||||
|
TEST_ERRNO(ioctl(STDIN_FILENO, TIOCGSID, &arg), ENOTTY);
|
||||||
|
|
||||||
|
// Operate on the foreground process group
|
||||||
|
|
||||||
|
arg = 0xdeadbeef;
|
||||||
|
TEST_RES(ioctl(master, TIOCGPGRP, &arg), arg == 0);
|
||||||
|
TEST_ERRNO(ioctl(slave, TIOCGPGRP, &arg), ENOTTY);
|
||||||
|
TEST_ERRNO(ioctl(STDIN_FILENO, TIOCGPGRP, &arg), ENOTTY);
|
||||||
|
|
||||||
|
TEST_ERRNO(ioctl(master, TIOCSPGRP, &arg), ENOTTY);
|
||||||
|
TEST_ERRNO(ioctl(slave, TIOCSPGRP, &arg), ENOTTY);
|
||||||
|
TEST_ERRNO(ioctl(STDIN_FILENO, TIOCSPGRP, &arg), ENOTTY);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
FN_TEST(nonleader_set_tty)
|
||||||
|
{
|
||||||
|
if (is_leader())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Only session leaders can use `TIOCSCTTY`, but we're not the leader
|
||||||
|
TEST_ERRNO(ioctl(master, TIOCSCTTY, 0), EPERM);
|
||||||
|
TEST_ERRNO(ioctl(slave, TIOCSCTTY, 0), EPERM);
|
||||||
|
TEST_ERRNO(ioctl(STDIN_FILENO, TIOCSCTTY, 0), EPERM);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
BARRIER()
|
||||||
|
|
||||||
|
FN_TEST(leader_set_unset_tty)
|
||||||
|
{
|
||||||
|
pid_t arg;
|
||||||
|
|
||||||
|
if (!is_leader())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// We can use `TIOCSCTTY` on the master PTY
|
||||||
|
TEST_SUCC(ioctl(master, TIOCSCTTY, 0));
|
||||||
|
arg = 0xdeadbeef;
|
||||||
|
TEST_RES(ioctl(master, TIOCGSID, &arg), arg == sid);
|
||||||
|
arg = 0xdeadbeef;
|
||||||
|
TEST_RES(ioctl(slave, TIOCGSID, &arg), arg == sid);
|
||||||
|
|
||||||
|
// `TIOCSCTTY` on the same TTY will succeed
|
||||||
|
TEST_SUCC(ioctl(master, TIOCSCTTY, 0));
|
||||||
|
TEST_SUCC(ioctl(slave, TIOCSCTTY, 0));
|
||||||
|
|
||||||
|
// `TIOCSCTTY` on a different TTY will fail
|
||||||
|
TEST_ERRNO(ioctl(STDIN_FILENO, TIOCSCTTY, 0), EPERM);
|
||||||
|
|
||||||
|
// `TIOCNOTTY` to clear the associated TTY
|
||||||
|
TEST_ERRNO(ioctl(master, TIOCNOTTY), ENOTTY);
|
||||||
|
TEST_SUCC(ioctl(slave, TIOCNOTTY));
|
||||||
|
TEST_ERRNO(ioctl(master, TIOCGSID, &arg), ENOTTY);
|
||||||
|
TEST_ERRNO(ioctl(slave, TIOCGSID, &arg), ENOTTY);
|
||||||
|
|
||||||
|
// We can also use `TIOCSCTTY` on the slave PTY
|
||||||
|
TEST_SUCC(ioctl(slave, TIOCSCTTY, 0));
|
||||||
|
arg = 0xdeadbeef;
|
||||||
|
TEST_RES(ioctl(master, TIOCGSID, &arg), arg == sid);
|
||||||
|
arg = 0xdeadbeef;
|
||||||
|
TEST_RES(ioctl(slave, TIOCGSID, &arg), arg == sid);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
BARRIER()
|
||||||
|
|
||||||
|
FN_TEST(nonleader_unset_tty)
|
||||||
|
{
|
||||||
|
if (is_leader())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Only session leaders can use `TIOCNOTTY`, but we're not the leader
|
||||||
|
TEST_ERRNO(ioctl(master, TIOCNOTTY), ENOTTY);
|
||||||
|
TEST_ERRNO(ioctl(slave, TIOCNOTTY), ENOTTY);
|
||||||
|
TEST_ERRNO(ioctl(STDIN_FILENO, TIOCNOTTY), ENOTTY);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
FN_TEST(query_foreground)
|
||||||
|
{
|
||||||
|
pid_t arg;
|
||||||
|
|
||||||
|
// All processes can use `TIOCGPGRP` on the master PTY
|
||||||
|
TEST_RES(ioctl(master, TIOCGPGRP, &arg), arg == sid);
|
||||||
|
|
||||||
|
// Only session leaders can use `TIOCGPGRP` on the slave PTY
|
||||||
|
if (is_leader())
|
||||||
|
TEST_RES(ioctl(slave, TIOCGPGRP, &arg), arg == sid);
|
||||||
|
else
|
||||||
|
TEST_ERRNO(ioctl(slave, TIOCGPGRP, &arg), ENOTTY);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
BARRIER()
|
||||||
|
|
||||||
|
FN_TEST(leader_set_foreground)
|
||||||
|
{
|
||||||
|
pid_t arg;
|
||||||
|
|
||||||
|
if (!is_leader())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Only session leaders can use `TIOCSPGRP`, and we're the leader
|
||||||
|
|
||||||
|
arg = child;
|
||||||
|
TEST_SUCC(ioctl(master, TIOCSPGRP, &arg));
|
||||||
|
arg = 0xdeadbeef;
|
||||||
|
TEST_RES(ioctl(slave, TIOCGPGRP, &arg), arg == child);
|
||||||
|
|
||||||
|
arg = sid;
|
||||||
|
TEST_SUCC(ioctl(master, TIOCSPGRP, &arg));
|
||||||
|
arg = 0xdeadbeef;
|
||||||
|
TEST_RES(ioctl(slave, TIOCGPGRP, &arg), arg == sid);
|
||||||
|
|
||||||
|
arg = child;
|
||||||
|
TEST_SUCC(ioctl(slave, TIOCSPGRP, &arg));
|
||||||
|
arg = 0xdeadbeef;
|
||||||
|
TEST_RES(ioctl(master, TIOCGPGRP, &arg), arg == child);
|
||||||
|
|
||||||
|
arg = sid;
|
||||||
|
TEST_SUCC(ioctl(slave, TIOCSPGRP, &arg));
|
||||||
|
arg = 0xdeadbeef;
|
||||||
|
TEST_RES(ioctl(master, TIOCGPGRP, &arg), arg == sid);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
BARRIER()
|
||||||
|
|
||||||
|
FN_TEST(nonleader_set_foreground)
|
||||||
|
{
|
||||||
|
pid_t arg = child;
|
||||||
|
|
||||||
|
if (is_leader())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Only session leaders can use `TIOCSPGRP`, but we're not the leader
|
||||||
|
|
||||||
|
TEST_ERRNO(ioctl(master, TIOCSPGRP, &arg), ENOTTY);
|
||||||
|
TEST_ERRNO(ioctl(slave, TIOCSPGRP, &arg), ENOTTY);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
FN_SETUP(cleanup)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (!is_leader())
|
||||||
|
return;
|
||||||
|
|
||||||
|
CHECK_WITH(wait(&status),
|
||||||
|
WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||||
|
}
|
||||||
|
END_SETUP()
|
@ -30,6 +30,7 @@ mmap/mmap_and_fork
|
|||||||
mmap/mmap_shared_filebacked
|
mmap/mmap_shared_filebacked
|
||||||
mmap/mmap_readahead
|
mmap/mmap_readahead
|
||||||
process/group_session
|
process/group_session
|
||||||
|
process/job_control
|
||||||
pthread/pthread_test
|
pthread/pthread_test
|
||||||
pty/open_pty
|
pty/open_pty
|
||||||
sched/sched_attr
|
sched/sched_attr
|
||||||
|
Reference in New Issue
Block a user