From: yooooooooooooo on
int tcsetpgrp(int fildes, pid_t pgid_id)

The situation is like this: Parent process ppid belongs to foreground
process group, and it is associated with the session's controlling
terminal which should be no more than one per session. Later ppid fork
a child process cpid, make it process group leader of cpid, and call
tcsetpgrp to make process group cpid the foreground process group.

My question is, when the child process cpid terminates, which process
does the session's controlling terminal is associated to if cpid has
never called tcsetpgrp again to change the association of the unique
controlling terminal.

Thanks.
From: Ersek, Laszlo on
In article <9b544467-82fb-4026-af72-4c4db1d3e726(a)t9g2000prh.googlegroups.com>, yooooooooooooo <yszhou4tech(a)gmail.com> writes:
> int tcsetpgrp(int fildes, pid_t pgid_id)
>
> The situation is like this: Parent process ppid belongs to foreground
> process group, and it is associated with the session's controlling
> terminal which should be no more than one per session.

Let's call it a shell:

- PID = ppid
- SID = ppid (SID = PID, session leader)
- PGID = ppid (PGID = PID, process group leader)
- has a controlling terminal

>>A session leader that has control of a terminal is called the
"controlling process" of that terminal.<<

http://www.gnu.org/software/libc/manual/html_node/Controlling-Terminal.html

It is called "controlling process" for a reason, because it is the one
process that manipulates the terminal.

I imagine the meaning of the "controlling terminal" expression like
this: each (slave) terminal device has a (SID, PGID) pair built-in. Each
process with that SID has the terminal as its controlling terminal, and
each process with that PGID is in the foreground process group of the
terminal. The shell (the controlling process) moves process groups
between foreground and background by manipulating the PGID component of
this pair, by tcsetpgrp(). When the (slave) terminal device was open()ed
originally by the shell (without the O_NOCTTY flag), the (SID, PGID)
pair of the terminal device was initialized to (ppid, ppid). The
controlling process is simply the process whose PID matches the
terminal's SID component.


> Later ppid fork a child process cpid, make it process group leader of
> cpid,

So the user types

$ /bin/true

and the shell fork()s a child shell:

- PID = cpid
- SID = ppid (in the session of the parent, the session leader)
- PGID = ppid (in the process group of the parent)
- has the same controlling terminal

Then both the parent (setpgid(cpid, 0)) and the child (setpgid(0, 0))
shell tries to move the child shell into its own process group, making
it the leader of the new process group:

- PID = cpid
- SID = ppid (in the session of the session leader shell)
- PGID = cpid (PID = PGID, process group leader)


> and call tcsetpgrp to make process group cpid the foreground process
> group.

So the parent shell (the controlling process) changes the terminal to
point to this process group as the foreground group, with a call to
tcsetpgrp(fd, cpid). The terminal's built-in pair will look like:

(SID, PGID) == (ppid, cpid)

Then the child executes the /bin/true image and terminates.


> My question is, when the child process cpid terminates, which process
> does the session's controlling terminal is associated to if cpid has
> never called tcsetpgrp again to change the association of the unique
> controlling terminal.

The imaginary pair of the terminal still looks like

(SID, PGID) == (ppid, cpid)

The parent shell (= the controlling process, = the session leader)
notices the termination of the child by way of SIGCHLD. It then calls
tcsetpgrp(fd, cpid), putting its own group (with it as its sole member)
back to the foreground position. (This call probably requires the
blocking of ignoring of SIGTTOU, as at the time of the call, the parent
shell itself is not in the foreground process group.)


....

The above is not much more than speculation. Please read all of the
following for authoritative answers (the links are to SUSv3):

"11. General Terminal Interface"
http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap11.html

"tcsetpgrp - set the foreground process group ID"
http://www.opengroup.org/onlinepubs/000095399/functions/tcsetpgrp.html

"setpgid - set process group ID for job control" (rationale!)
http://www.opengroup.org/onlinepubs/000095399/functions/setpgid.html

(Links this time to SUSv3, because the SUSv2 page of setpgid() contains
a confusing typo.)

And the 27 "Job Control" chapter of the GNU libc manual:

http://www.gnu.org/software/libc/manual/html_node/Job-Control.html

Notably, SUS doesn't speak of any explicit "session id", it's my
reasoning device.

....

You probably questioned before why setsid() is disallowed for processes
that are already process group leaders, ie. for which PID = PGID holds.

http://www.opengroup.org/onlinepubs/000095399/functions/setsid.html

Suppose we allow setsid() for a process group leader:

session leader:
- PID = 1000
- SID = 1000 (session leader)
- PGID = 1000 (process group leader)

forked child 1:
- PID = 1001
- SID = 1000 (same session)
- PGID = 1001 (in its own process group, process group leader)

forked child 2:
- PID = 1002
- SID = 1000 (same session)
- PGID = 1001 (same process group as forked child 1)

Now if "forked child 1" could do a setsid(), setting both its SID and
PGID to its PID:

forked child 1 after imaginary setsid():
- PID = 1001
- SID = 1001 (leader of new session)
- PGID = 1001 (process group leader -- no change)

This would result in a situation where the two members of process group
1001 belong to different sessions (1000 and 1001).

"forked child 2" is allowed to do setsid(), because then its PGID is in
fact changed:

forked child 2 after setsid():
- PID = 1002
- SID = 1002 (new session, leader)
- PGID = 1002 (new process group, leader)

And this is why a fork() enables a setsid() in the child: even if PID ==
PGID holds in the parent (so that a setsid() would create a new session
but not a new process group), it won't hold in the child (because the
child's new PID will be different from the parent's PID and thus the
inherited PGID), and thus setsid() effects an actual change in PGID in
the child when it sets PGID to PID, moving the child out of the original
process group.

.... Sorry for all the errors.

Cheers,
lacos
From: Ersek, Laszlo on
In article <33NyY+nY8ulY(a)ludens>, lacos(a)ludens.elte.hu (Ersek, Laszlo) writes:

> The parent shell (= the controlling process, = the session leader)
> notices the termination of the child by way of SIGCHLD.

> It then calls
> tcsetpgrp(fd, cpid), putting its own group (with it as its sole member)
> back to the foreground position.

> (This call probably requires the
> blocking of ignoring of SIGTTOU, as at the time of the call, the parent
> shell itself is not in the foreground process group.)

The second block is full of typos. Correction:

----v----
It then calls tcsetpgrp(fd, ppid), putting its own group (with itself as
the only member of that group) back to the foreground position.
----^----

Sorry,
lacos
From: yolila on
Thank you, Ersek, for the notions of "each (slave) terminal device has
a (SID, PGID) pair built-in",
and such good material for my reference. It does make sense.

I got that the key point when calling tcsetpgrp from a background
process is the disposition of
SIGTTOU signal. I experiment it with the following code neglecting
error checking:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

int main()
{
setpgid(0, 0);

signal(SIGTTOU, SIG_IGN); //
1
tcsetpgrp(STDIN_FILENO, getpid());
printf("Hello, I am newly from background.\n");

tcsetpgrp(STDIN_FILENO, getppid()); // 2
//sleep(2);

return 0;
}

If code line 1 was commented out, running this program in the
background would make
itself be stopped.
If code line 2 was commented out, and running this program in the
background, the whole
program worked fine except that after this process was done, bash
would log out current
user which I didn't get the inner mechanism within this result.

Thanks, the answer really helped a lot.
: )