[comp.unix.wizards] /dev/tty doesn't exist?

jc@minya.UUCP (John Chambers) (01/02/88)

Well, here I am with something else I've screwed up, and can't make sense
of from the Unix manuals! (:-)

What's going wrong now is that I have a program, let's call it X, which
starts up a sub-process, and execs /bin/sh in it.  It then hangs around
waiting for the shell to die.  All very normal, you say?  Well, then,
explain why, to this shell and anything it starts, /dev/tty doesn't seem
to exist.

The first hint was from a script that did something like:
|	echo "This is an error message." >/dev/tty
so that it could write to the control terminal regardless of how output
had been redirected.  Instead of the error message, what appeared on the
control terminal was:
|	/dev/tty: cannot create
This seems a bit bizarre; a bit of looking turns up the fact that:
|	crw-rw-rw-   1 root     sys        2,  0 Jan  1 20:42 /dev/tty
I.e., /dev/tty does indeed exist, and is world-writable.  After a bit
of testing, I wrote a little test C program that says:
|	if ((fn = open("/dev/tty", 2)) < 0) 
|		fprintf(stderr,"Can't open(\"/dev/tty\",2) errno=%d.\n",errno);
Guess what it says?  OK, I'll tell you:
|	Can't open("/dev/tty",2) errno=6.
Replacing the '2' with '1' or '0' gets the same results.

Anyone have an idea what my program X might be doing to screw up /dev/tty?

Some hints:  This is a rather generic Unix Sys/V.2, so the problem should
be similar to most ATT Unices.  The most unusual thing about how X is run
is that it isn't being started by a login shell; it is started as a daemon
by init, and in some cases, decides to grab a terminal (whose name is on
its command line, and which doesn't have getty on it) and run a program
there.  This is a useful thing to do if, for instance, you are playing
around with process-control applications.   

Perhaps the question could be rephrased:  What is the official, approved
way for a daemon process like this to attach a terminal?  If the answer
is RTFM, can you say where?  The 1/4-page /dev/tty entry isn't much help.
[In fact, the Unix manuals aren't very helpful in general if you want to
write a free-running background program.  I've tried to help various users
who wanted to do this, and it's frustrating never quite knowing what I'm
talking about :-]

-- 
John Chambers <{adelie,ima,maynard,mit-eddie}!minya!{jc,root}> (617/484-6393)

dave@sdeggo.UUCP (David L. Smith) (01/03/88)

In article <445@minya.UUCP >, jc@minya.UUCP (John Chambers) writes:
 > Some hints:  This is a rather generic Unix Sys/V.2, so the problem should
 > be similar to most ATT Unices.  The most unusual thing about how X is run
 > is that it isn't being started by a login shell; it is started as a daemon
 > by init, and in some cases, decides to grab a terminal (whose name is on
 > its command line, and which doesn't have getty on it) and run a program
 > there.  This is a useful thing to do if, for instance, you are playing
 > around with process-control applications.   
 > 
 > Perhaps the question could be rephrased:  What is the official, approved
 > way for a daemon process like this to attach a terminal?  If the answer
 > is RTFM, can you say where?  The 1/4-page /dev/tty entry isn't much help.
 > [In fact, the Unix manuals aren't very helpful in general if you want to
 > write a free-running background program.  I've tried to help various users
 > who wanted to do this, and it's frustrating never quite knowing what I'm
 > talking about :-]

The problem would seem to be that /dev/tty has no idea what tty you really
want to be talking to.  In order to set things up, you need to do a 
setpgrp() and then open the terminal you wish to talk to.  Termio(7) briefly
explains about the control terminal.  

-- 
David L. Smith
{sdcsvax!man,ihnp4!jack!man, hp-sdd!crash, pyramid}!sdeggo!dave
man!sdeggo!dave@sdcsvax.ucsd.edu 
Sinners can repent, but stupid is forever.

ron@topaz.rutgers.edu (Ron Natalie) (01/04/88)

Sure, /dev/tty works by redirecting the output to your
controlling teletype.  Network processes that don't explicitly
use some feature like pty's, don't have a controlling teletype so
/dev/tty doesn't mean anything.

By the way, you have a real cute fprintf there that prints out the
error number.  Why not use perror, which does exactly what you did
only printing the error in English.

-Ron

ford@crash.cts.com (Michael Ditto) (01/04/88)

In article <445@minya.UUCP> jc@minya.UUCP (John Chambers) writes:
>The first hint was from a script that did something like:
>|	echo "This is an error message." >/dev/tty
>so that it could write to the control terminal regardless of how output
>had been redirected.  Instead of the error message, what appeared on the
>control terminal was:
>|	/dev/tty: cannot create
>This seems a bit bizarre; a bit of looking turns up the fact that:
>|	crw-rw-rw-   1 root     sys        2,  0 Jan  1 20:42 /dev/tty
>I.e., /dev/tty does indeed exist, and is world-writable.

The "cannot create" message from the shell is misleading.  It means the
creat(2) system call failed.  ('creat' will create a file if it doesn't
exist, otherwise it's similar to 'open').  So the message just means that
/dev/tty could not be opened.  The shell is very stupid in that it does
not give any indication as to why the open failed.

>After a bit of testing, I wrote a little test C program that says:
>|	if ((fn = open("/dev/tty", 2)) < 0) 
>|		fprintf(stderr,"Can't open(\"/dev/tty\",2) errno=%d.\n",errno);
>Guess what it says?  OK, I'll tell you:
>|	Can't open("/dev/tty",2) errno=6.

The first thing you should have noticed is that errno=6, ENXIO.  This is
"No such device or address", an error which indicates that you are attempting
to opening a logical device that has no corresponding physical device.  In
this case, you are openning "/dev/tty", which (according to tty(7)) is
supposed to open for you the "control terminal associated with the process
group of the process".  The error means that there is no control terminal
associated with the process group of your process.  (more explanation below)

>Some hints:  This is a rather generic Unix Sys/V.2, so the problem should
>be similar to most ATT Unices.  The most unusual thing about how X is run
>is that it isn't being started by a login shell; it is started as a daemon
>by init, and in some cases, decides to grab a terminal (whose name is on
>its command line, and which doesn't have getty on it) and run a program
>there.  This is a useful thing to do if, for instance, you are playing
>around with process-control applications.   

First of all, you need to know what a process group is.  Basically, any
process can request to be separated from its current group and become a
group of its own.  Any future children of this process are also part of
this new group. In your case, your program run from init is the "process
group leader" of its own process group, which init created for your
program.  When your program (or shell script, if that's what it is) runs
a child process, it is also part of your process group.

Now the problem is that when your process (and process group) are created,
they have never opened a terminal, and therefore do not have a "controlling
terminal".  A process group gets a controlling terminal when its PROCESS
GROUP LEADER opens a terminal for the first time.  A child process in the
group may open a terminal, but this will NOT become the controlling tty.
To get a controlling tty, your process group leader must open the terminal.

>Perhaps the question could be rephrased:  What is the official, approved
>way for a daemon process like this to attach a terminal?  If the answer
>is RTFM, can you say where?  The 1/4-page /dev/tty entry isn't much help.

tty(7) explains "/dev/tty".  The semantics of getting a controlling terminal
are explained in termio(7).  Creating a process group is described in
setpgrp(2).  The terminology (such as "controlling tty" and "process group
leader") and error codes are briefly explained in intro(2).

What you need to do is have your main program (the one that is directly
running from init) open the terminal in question.  You might want to call
setpgrp() first, just to make sure you have your own process group, and to
allow this program to work when run in other ways.  Then, you can have
child processes (such as shell commands) that use /dev/tty.  You can also
leave the tty open when you fork the subprocesses, allowing them to use
the file descriptors you already have open, rather than opening the tty
again in the child.

Note that the above description applies to System V.  BSD is even more
bizarre.

Good luck with your program.

-- 

Mike Ditto					-=] Ford [=-
P.O. Box 1721					ford%kenobi@crash.CTS.COM
Bonita, CA 92002				ford@crash.CTS.COM

jra@jc3b21.UUCP (Jay R. Ashworth) (01/04/88)

In article <445@minya.UUCP>, jc@minya.UUCP (John Chambers) says:
> 
> Anyone have an idea what my program X might be doing to screw up /dev/tty?
[ Yes. ]
> Some hints:  This is a rather generic Unix Sys/V.2, so the problem should
> be similar to most ATT Unices.  The most unusual thing about how X is run
> is that it isn't being started by a login shell; it is started as a daemon
> by init, and in some cases, decides to grab a terminal (whose name is on
> its command line, and which doesn't have getty on it) and run a program
> there.  This is a useful thing to do if, for instance, you are playing
> around with process-control applications.   
> John Chambers <{adelie,ima,maynard,mit-eddie}!minya!{jc,root}> (617/484-6393)
Aha.  As I understand the way these things work, and I'm sure someone
will clarify what I'm about to say :-), /dev/tty is set by the kernel
to point to the control terminal of a process group.  Background
process, those running as daemons, have no control terminal.  Look at
a ps listing.  Also, when was the last time you tried to make an rc
script read the console?  My last time, I discovered that I had to use
/dev/console (Yes, ack, this was on a tandy 6000), just /dev/tty
bombed.  Good luck.
-- jra
-- 
Jay R. Ashworth ---+-- The Great Ashworth & ------------+...!uunet!codas!pdn!
10974 111th St. N. |       Petrillo Production Company  |          jc3b21!jra
Seminole FL 34648  +-- watch for BayLink Public Access -+- UNIX ----+---------
(813) 397-1859 ----+-- Tampa Bay's Smallest Video Production House -+ :-) !$

mitch@stride1.UUCP (Thomas P. Mitchell) (01/06/88)

In article <167@sdeggo.UUCP> dave@sdeggo.UUCP (David L. Smith) writes:
>In article <445@minya.UUCP >, jc@minya.UUCP (John Chambers) writes:
> > 
> > Perhaps the question could be rephrased:  What is the official, approved
> > way for a daemon process like this to attach a terminal?  If the answer
> > is RTFM....

It is in the manual but a bit hard to find. Try "man man" and
look for an option "man -f something".  Keep the 'something'
simple because a simple minded grep is done on the man page
headers.  

Here is part of the result of "man -f tty" on this system.  Note
that two 'tty' man pages exist.

getty  (8)		- set terminal type, modes, speed, and line discipline
stty (1)		- set the options for a terminal
tty (1)			- get the name of the terminal
tty (4)			- controlling terminal interface

>The problem would seem to be that /dev/tty has no idea what tty you really
>want to be talking to.  In order to set things up, you need to do a 
>setpgrp() and then open the terminal you wish to talk to.  Termio(7) briefly
>explains about the control terminal.  
>

It (/dev/tty) is a clever pseudo device.  The key is that it
figures out which "real" device the process is connected to.

========  man page follows  ======= this system's "man 4 tty" ===
NAME
     tty - controlling terminal interface

DESCRIPTION
     The file /dev/tty is, in each process, a synonym for the
     control terminal associated with the process group of that
     process, if any.  It is useful for programs or shell
     sequences that wish to be sure of writing messages on the
     terminal no matter how output has been redirected.  It can
     also be used for programs that demand the name of a file for
     output, when typed output is desired and it is tiresome to
     find out what terminal is currently in use.

FILES
     /dev/tty
     /dev/tty*

Thomas P. Mitchell (mitch@stride1.Stride.COM)
Phone:	(702) 322-6868 TWX:	910-395-6073
MicroSage Computer Systems Inc. a Division of Stride Micro.
Opinions expressed are probably mine. 

allbery@ncoast.UUCP (Brandon Allbery) (01/06/88)

As quoted from <243@jc3b21.UUCP> by jra@jc3b21.UUCP (Jay R. Ashworth):
+---------------
| In article <445@minya.UUCP>, jc@minya.UUCP (John Chambers) says:
| > Some hints:  This is a rather generic Unix Sys/V.2, so the problem should
| > be similar to most ATT Unices.  The most unusual thing about how X is run
| > is that it isn't being started by a login shell; it is started as a daemon
| > by init, and in some cases, decides to grab a terminal (whose name is on
| Aha.  As I understand the way these things work, and I'm sure someone
| will clarify what I'm about to say :-), /dev/tty is set by the kernel
| to point to the control terminal of a process group.  Background
| process, those running as daemons, have no control terminal.  Look at
+---------------

/dev/tty is more of an alias than a device.  A function call on /dev/tty
looks up in the user block of the process to determine the controlling
terminal, then repeats the function call on that terminal.  If there is
no controlling terminal, an open on /dev/tty will fail because the /dev/tty
open routine can't figure out what tty's open routine to call.

In particular, /dev/tty does not work at all like the /dev/window device on
the 3B1, which searches for the first UNUSED window device rather than the
current one.
-- 
	      Brandon S. Allbery, Moderator of comp.sources.misc
 {hoptoad,harvard!necntc,cbosgd,sun!mandrill!hal,uunet!hnsurg3}!ncoast!allbery
PS/2:  Half a computer.    OS/2:  Half an operating system for half a computer.

marcus@illusion.UUCP (Marcus Hall) (01/07/88)

In article <2256@crash.cts.com> ford%kenobi@crash.CTS.COM (Michael Ditto) writes:
>In article <445@minya.UUCP> jc@minya.UUCP (John Chambers) writes:
>>The first hint was from a script that did something like:
>>|	echo "This is an error message." >/dev/tty
>>so that it could write to the control terminal regardless of how output
>>had been redirected.  Instead of the error message, what appeared on the
>>control terminal was:
>>|	/dev/tty: cannot create
....
>
>What you need to do is have your main program (the one that is directly
>running from init) open the terminal in question.  You might want to call
>setpgrp() first, just to make sure you have your own process group, and to
>allow this program to work when run in other ways.  Then, you can have
>child processes (such as shell commands) that use /dev/tty.  You can also
>leave the tty open when you fork the subprocesses, allowing them to use
>the file descriptors you already have open, rather than opening the tty
>again in the child.

Well,,,,,

This is indeed the problem, but I'm afraid that this solution will not
work.  As it turns out, a tty can be the control terminal of only one
process group at a time.  (I don't have the man page here, but it reads
something like "when a process opens a tty device for the first time"
which actually means that the device is opened for the first time.)  I
have tried to figure out ways around this problem without success.  If
anyone has any suggestions, I'd really like to hear them.

If you don't do a setpgrp() call, you will still be able to access /dev/tty
because your login shell is the process group leader and it should have
a control terminal (There are ways around this, so this statement isn't
strictly true, but it usually is.)  For ksh at least, there is a flag,
-m I believe (check this to be sure) that causes it to place all backgrounded
jobs into their own process groups.  If you use ksh, clear this flag and
see if your program works, for other shells, check for a similar flag.

Incidently, the reason that a tty device can be attached to only one process
group is that there is a pointer in the tty structure that points to the
process group.  When an interrupt or kill character is processed (or other
conditions at times), this is used to send a signal to the process group.
It would have been just about as easy to just have the proc table entries
point to the control terminal and scan the proc table for pointers to the
terminal generating the signal, (the proc table has to be scanned to
distribute the signal to the process group anyway), but that's just how it
is done -- no use worrying about how it "should" have been done :-)

Marcus Hall
..!{ihnp4,mcdchg}!illusion!marcus

jc@minya.UUCP (John Chambers) (01/28/88)

> >In article <445@minya.UUCP >, jc@minya.UUCP (John Chambers) writes:
> > > 
> > > Perhaps the question could be rephrased:  What is the official, approved
> > > way for a daemon process like this to attach a terminal?  If the answer
> > > is RTFM....
> 
Well, after wading through lots of responses, most of which told me things
I already knew, and didn't answer the question, a couple of folks gave the
right hint, something that I still can't get from re-reading TFM:  the call
of setpgrp() must happen BEFORE opening any terminal device.  If there is
any file open that looks like a terminal, setpgrp() seems to have no effect
at all (or maybe it's just too subtle for me :-).

Anyhow, I have my program finding /dev/tty now, so thanks.

There is still a puzzle, in that my process starts children, including quite
often a shell, and these inherit the terminal (as with getty/login, I guess).
I've tried making the children into process-group leaders on their own, to
no avail.  No matter what I do, the ^C at the terminal goes to the original
process.  It doesn't even work for a child to close all its files, setpgrp(),
and re-open all its files.  Very, very strange stuff here.

I've sort of handled this by having the program intercept all signals and
pass them along to its children.  This works, but it doesn't seem to reach
the grandchildren, even if I do kill(0,<sig>).  The manual says it should,
but the grandchildren don't notice the signal.

Am I paranoid, or did they do this intentionally to make my life difficult?

-- 
John Chambers <{adelie,ima,maynard,mit-eddie}!minya!{jc,root}> (617/484-6393)

davel@hpcupt1.HP.COM (Dave Lennert) (01/31/88)

> There is still a puzzle, in that my process starts children, including quite
> often a shell, and these inherit the terminal (as with getty/login, I guess).
> I've tried making the children into process-group leaders on their own, to
> no avail.  No matter what I do, the ^C at the terminal goes to the original
> process.  It doesn't even work for a child to close all its files, setpgrp(),
> and re-open all its files.  Very, very strange stuff here.
> 
> John Chambers <{adelie,ima,maynard,mit-eddie}!minya!{jc,root}> (617/484-6393)

I'm guessing a little at what your programs look like but...

First some background.  (All this assumes System V without BSD style
job control.  BSD and/or job control does this differently in subtle ways.)

setpgrp():  if your process is NOT ALREADY a process group leader, setpgrp()
	    will make it a pgrp leader and then remove its controlling terminal
	    (if any).  Just the calling process loses the controlling tty,
	    other process which share this tty as a controlling tty still
	    retain it.

open():     If a pgrp leader is without a controlling tty and opens a tty
	    which IS NOT CURRENTLY a controlling tty for other processes,
	    then this tty becomes the controlling tty of the calling process.
	    If the tty is already a controlling tty for another pgrp, then
	    it does NOT become the controlling tty of the caller.  In either
	    case, the open succeeds (i.e., no indication is given that the
	    terminal did  or did not become your controlling tty).

fork():     child processes inherit the controlling tty of the parent, if any.

close():    a controlling tty will cease to be a controlling tty when either
	    (a) the pgrp leader which allocated it terminates or
	    (b) NO process has the tty open.

signals:    tty keyboard generated signals are sent to ALL processes in the
	    pgrp of the pgrp leader which allocated the controlling tty.


Now, I assume you have a program which calls setpgrp() and then open()'s
a tty, thus aquiring it as a controlling tty.  It then forks and creates
children which (a) share the controlling tty and (b) are members of the
same process group.  Keyboard signals go to all processes in the pgrp
(including the pgrp leader parent).  Even if the children close all their
file descriptors to the terminal, if the parent still has it opened it
will remain a controlling tty and keyboard signals will continue to be
sent to the child processes which remain in the same pgrp.  If the
children do a setpgrp(), they will cease to share the controlling tty
and (more importantly) cease to be in the pgrp so they will no longer
receive keyboard signals.

The pgrp leader parent cannot leave the pgrp since SysV forbids a pgrp
leader to leave its pgrp; so it will always receive the signals.

-Dave Lennert    HP    ihnp4!hplabs!hpda!davel