Assume a session has a controlling terminal.
If a process in the session that is not the session leader, calls ioctl(fd, TIOCNOTTY)
, is it correct that it only closes the fd
for itself? Is it the same as close(fd)
?
Thanks.
Assume a session has a controlling terminal.
If a process in the session that is not the session leader, calls ioctl(fd, TIOCNOTTY)
, is it correct that it only closes the fd
for itself? Is it the same as close(fd)
?
Thanks.
It does not close the file descriptor, merely gives up the controlling terminal for the process. Since the process is not the session leader then nothing else happens. That is how the man page on my Linux machine describes it. Even though the documentation is what you should rely on as testing the implementation will not prove it works in all cases, trying out is pretty trivial if you are okay with terrible code:
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define ERR(x)
do {
perror(x);
return;
} while (0)
void
main(void)
{
/* create a new session */
pid_t leader = setsid();
if (leader == (pid_t) -1) {
/* EPERM means we are already a process group leader */
if (errno != EPERM)
ERR("setsid");
}
int pid = fork();
if (pid < 0)
ERR("fork");
if (pid > 0) {
/* super-lazy clean-up and sync in one */
wait(NULL);
return;
}
pid_t parent = getppid();
pid_t child = getpid();
printf("Child PID: %lld\n", (long long) child);
pid = fork();
if (pid < 0)
ERR("fork");
if (pid > 0) {
int fd = open(ctermid(NULL), O_RDWR);
if (fd < 0)
ERR("open");
/* middle child gives up controlling terminal */
if (ioctl(fd, TIOCNOTTY) < 0) {
close(fd);
ERR("ioctl");
}
close(fd);
printf("Child gave up ctty, now waiting\n");
/* super-lazy clean-up and sync in one */
wait(NULL);
return;
}
/* even lazier sync */
sleep(1);
pid_t grandchild = getpid();
printf("Grandchild PID: %lld\n", (long long) grandchild);
char cmd[256] = {0};
snprintf(cmd, 256,
"head -c 60 /proc/%lld/stat /proc/%lld/stat /proc/%lld/stat",
(long long) parent, (long long) child, (long long) grandchild);
system(cmd);
/* the output of the command will not end with newline */
printf("\n");
}
Running this shows:
Child PID: 293750
Child gave up ctty, now waiting
Grandchild PID: 293751
==> /proc/293749/stat <==
293749 (test) S 290544 293749 290544 34822 293749 4210688 10
==> /proc/293750/stat <==
293750 (test) S 293749 293749 290544 0 -1 4210752 40 0 0 0 0
==> /proc/293751/stat <==
293751 (test) S 293750 293749 290544 34822 293749 4210752 32
And if you look at the output the seventh field is the tty_nr
from proc(5)
man page and it shows that only the child that gave up it's controlling terminal lost it (tty_nr = 0
), not the other processes.
All that said I think this should not be used anywhere other than for research/learning purposes as usually you would call setsid(2)
before spawning any children and, if that's what you desire, open a (new) terminal without O_NOCTTY
if you want it to be assigned a controlling terminal.
_exit
: "If the process is a controlling process, the SIGHUP signal shall be sent to each process in the foreground process group of the controlling terminal belonging to the calling process.". – Jan 04 '19 at 21:13ioctl(fd, TIOCNOTTY)
is closing that fd? It isn't.echo 'int main(void){ ioctl(1, TIOCNOTTY); write(1, "foo\n", 4); }' | cc -include sys/ioctl.h -include unistd.h -Wall -x c - && ./a.out
– Jan 05 '19 at 00:04exit()
(and indeed for theioctl()
in the question). – JdeBP Jan 05 '19 at 03:33