[comp.unix.wizards] how can I determine controlling tty if no file descriptor is open onto it?

levy@ttrdc.UUCP (Daniel R. Levy) (05/13/88)

[Please skip this if you've already seen it once.  My first attempt fell on
the floor.]

I am trying to write a program UNDER SYSTEM V RELEASE 2 AND 3 which
disassociates itself from the controlling terminal (using setpgrp() after
redirecting all file descriptors still open onto the terminal, into a file),
tries to exec() a machine binary or shell script, and if that doesn't work
sends an error message to the terminal and exits.

The reason I want this is so I can put a command in background a la nohup,
BUT, unlike nohup, even if the command turns interrupt/quit/hangup signal
catching back on, or a gremlin tries to kill -9 my entire login process group
(like one $#@!! version of telnet I am using does upon logout), it will not
be affected by goings-on at the terminal.

However, I am having a problem with always getting an error message back to
the terminal from which the command was issued.  It does no good to try to
write to "/dev/tty" once one has divorced from the controlling terminal.
In fact, even if a file descriptor is kept open onto "/dev/tty" during the
setpgrp(), writing onto it afterward fails with errno==ENXIO (No such device
or address).   Now, of course, if I knew the explicit name of the
ex-controlling terminal, I could open that and send the error message to it.

In many cases, I can indeed determine that name as the program begins, and
save it.  If there is still a file descriptor open onto the terminal (e.g.,
fd 2, error output) I can use ttyname() to determine what it was attached to.
If there is no file descriptor open onto the terminal (the user has redirected
them all), I am still in luck if the program is a direct child of the login
shell.  I can use getppid() to find out what that pid is, and search through
/etc/utmp to determine the controlling terminal associated with it.  However,
if neither of these is the case (user redirected stdout and stderr, and put
the program into background [stdin is closed] from a non-login shell), I am
stuck.  I _could_ run /bin/ps -u <my_username> and parse through the process
geneology to find out what terminal I was attached to (or rather, the terminal
my parent process is still attached to).  But this can be slow, especially if
ps decides it must rebuild /etc/ps_data (my user may have logged out by the
time that finishes and would miss the error message, which could have warned
him of a typo in the command).  A shotgun write to all terminals the user is
logged onto is not really desireable, as the user might be running a graphics
application on one of them.

Is there any better way to do this that I am overlooking?  Wizards please
send suggestions by email, flamers please cat your flame > /dev/null.  Thanks
in advance.

Oh yes, as I indicated above, this is for SYSTEM V RELEASE 2 and 3.  I really
would like any suggested solution to work on both.  I don't really care how it
would be done under BSD.
-- 
|------------Dan Levy------------|  Path: ihnp4,<most AT&T machines>!ttrdc!levy
|              AT&T              |  Weinberg's Principle:  An expert is a
|       Data Systems Group       |  person who avoids the small errors while
|--------Skokie, Illinois--------|  sweeping on to the grand fallacy.

les@chinet.UUCP (Leslie Mikesell) (05/13/88)

In article <2661@ttrdc.UUCP> levy@ttrdc.UUCP (Daniel R. Levy) writes:
>I am trying to write a program UNDER SYSTEM V RELEASE 2 AND 3 which
>disassociates itself from the controlling terminal (using setpgrp() after
>redirecting all file descriptors still open onto the terminal, into a file),
>tries to exec() a machine binary or shell script, and if that doesn't work
>sends an error message to the terminal and exits.
>...
>However, I am having a problem with always getting an error message back to
>the terminal from which the command was issued.  It does no good to try to

If ttyname() doesn't work on file descriptor 0,1, or 2, you can:
 a) forget the error message
 b) send the error message by mail to the appropriate user found by
    the environment variable LOGNAME (it's good enough for /bin/mail) or
    looking for the uid in /etc/passwd.
 c) check all the reasons that the exec might fail before closing 
    stderr.
 d) fork before closing stderr and let the parent wait a "reasonable"
    amount of time for a signal from the child indicating that the
    exec failed.

I'd go for (a) myself. If the error message isn't going to appear on
the terminal, you might as well find it where you expected the output
from the exec'ed program.
A reasonable facsimile of (b) is available from the at command, if you are in
the at.allow file.

  Les Mikesell

terry@wsccs.UUCP (Every system needs one) (05/28/88)

In article <2661@ttrdc.UUCP> levy@ttrdc.UUCP (Daniel R. Levy) writes:
>I am trying to write a program UNDER SYSTEM V RELEASE 2 AND 3 which
>disassociates itself from the controlling terminal (using setpgrp() after
>redirecting all file descriptors still open onto the terminal, into a file),
>tries to exec() a machine binary or shell script, and if that doesn't work
>sends an error message to the terminal and exits.
>...
>However, I am having a problem with always getting an error message back to
>the terminal from which the command was issued.

Use ttyname() before leaving you program clueless.  If you are redirecting
at a shell level, don't; use freopen() after using ttyname().  This will
leave your poor program clueless just as efficiently.  Or use getpgrp()
before using setpgrp().

"exec()" a shell script?  Try an execl() of 'sh -c script' instead, unless
your exec() is weird.


| Terry Lambert           UUCP: ...{ decvax, ihnp4 } ...utah-cs!century!terry |
| @ Century Software        OR: ...utah-cs!uplherc!sp7040!obie!wsccs!terry    |
| SLC, Utah                                                                   |
|                   These opinions are not my companies, but if you find them |
|                   useful, send a $20.00 donation to Brisbane Australia...   |
| 'Admit it!  You're just harrasing me because of the quote in my signature!' |

ford@elgar.UUCP (Mike "Ford" Ditto) (06/06/88)

In article <550@wsccs.UUCP> terry@wsccs.UUCP (Every system needs one) writes:
>In article <2661@ttrdc.UUCP> levy@ttrdc.UUCP (Daniel R. Levy) writes:
>>tries to exec() a machine binary or shell script, and if that doesn't work
>>sends an error message to the terminal and exits.
>
>"exec()" a shell script?  Try an execl() of 'sh -c script' instead, unless
>your exec() is weird.

There is no function called exec(), it's a generic name for the
exec{l,v}{p,e} set of entrypoints to the exec system service.  The "p"
versions (execlp, execvp) automatically run a shell if they are passed
the name of a script.  Mr. Levy obviously meant exec() in the generic
sense, and there's nothing wrong with that.

					-=] Ford [=-

"Once there were parking lots,		(In Real Life:  Mike Ditto)
now it's a peaceful oasis.		ford%kenobi@crash.CTS.COM
This was a Pizza Hut,			...!sdcsvax!crash!kenobi!ford
now it's all covered with daisies." -- Talking Heads