Creating a controlling terminal

Does anyone have some example server code (telnetd, rlogind, etc.) that
creates a controlling terminal under QNX4.25?

I’ve tried tcsetct() and tcsetpgrp() after setsid(), but the device
“/dev/tty” is not being created.

Thanks in advance,

-Jim

Jim Parnell <jparnell@wgate.com> wrote:

Does anyone have some example server code (telnetd, rlogind, etc.) that
creates a controlling terminal under QNX4.25?

I’ve tried tcsetct() and tcsetpgrp() after setsid(), but the device
“/dev/tty” is not being created.

You do realize that you will never see a “/dev/tty” device by doing
an ls of /dev, right? That “/dev/tty” is actually the posix alias
for the controlling terminal.

-David

QNX Training Services
dagibbs@qnx.com

Yes, although if you do a “ls /dev/tty” you will see the characteristics of
the device it points to.

I’m porting SSH to QNX 4. Here’s what happens when a new session connects:


  • A session is forked off the sshd daemon and a new tty is associated with
    it (e.g., “/dev/ttyp3”)
  • The forked process disassociates itself from the daemon’s controlling
    terminal by opening “/dev/tty”, then calling ioctl(TIOCNOTTY). I’ve
    found that this doesn’t work as well as expected since it returns a
    ENOMSG. Iit sets the pgrp to its pid, which prevents us from making a
    new session (via setsid()). I’ve been skipping this part.
  • Calls setsid() to create a new session ID
  • Tries to open “/dev/tty” ( to verify that it’s gone – it is)
  • The SSH distribution code sets the new tty “/dev/ttyp3” to be our
    controlling tty using an ioctl(TIOCSCTTY) call. QNX returns a “ENOENT”
    here, so I use tcsetct() instead, which returns an EOK.
  • I set the process group for the terminal using tcsetpgrp()
  • The new tty is opened “/dev/ttyp3”, and then closed. I speculate that
    this is what creates the “/dev/tty” logical device in most Unix
    implementations. If not, it’s the ioctl(), above.
  • The logical device “/dev/tty” is opened to verify that a controlling tty
    was set. This fails.

Somehow the “/dev/tty” alias is not being created when the SSH code thinks it

should be. BSD’s telnetd uses a scheme similar to the above. Maybe this has
to be handled explicitly under QNX?

-Jim

David Gibbs wrote:

Jim Parnell <> jparnell@wgate.com> > wrote:
Does anyone have some example server code (telnetd, rlogind, etc.) that
creates a controlling terminal under QNX4.25?

I’ve tried tcsetct() and tcsetpgrp() after setsid(), but the device
“/dev/tty” is not being created.

You do realize that you will never see a “/dev/tty” device by doing
an ls of /dev, right? That “/dev/tty” is actually the posix alias
for the controlling terminal.

-David

QNX Training Services
dagibbs@qnx.com

In article <957cne$7fq$4@nntp.qnx.com>, David Gibbs <dagibbs@qnx.com> wrote:

Jim Parnell <> jparnell@wgate.com> > wrote:
Does anyone have some example server code (telnetd, rlogind, etc.) that
creates a controlling terminal under QNX4.25?

I’ve tried tcsetct() and tcsetpgrp() after setsid(), but the device
“/dev/tty” is not being created.

You do realize that you will never see a “/dev/tty” device by doing
an ls of /dev, right? That “/dev/tty” is actually the posix alias
for the controlling terminal.

More to the point, POSIX says little about the semantics of /dev/tty.
The act of opening /dev/tty to obtain a new controlling tty for a daemon
process is a Unix convention. I am not sure it is even covered by
the Unix specifications.

QNX 4 didn’t deal with this situation in the conventional Unix
manner, so there is no easy way to obtain a new controlling tty
once you have become detached, whereas Neutrino (consequently RTP/QNX6)
do the Unix style.

This was an area of black magic. I think ideally you need to either
(a) iterate over the available consoles (usually /dev/con?? or
/dev/tty??, depending on the arguments to Dev.ansi/Dev.con) or take
the number of the console you want to run on, or (b) take the first
available pseudo-tty and then bind it to the process as the controlling tty.

I can’t help you with the ioctl that might be able to do this for
you, but:

  1. Detecting that there is no controlling tty is slightly different
    from others. Opening /dev/tty when there is no controlling tty
    will return EACCES or ENOENT (I forget what conditions changed,
    but probably different kernel revs).
  2. You can set the controlling tty for processes you spawn with
    a spawn option.

qnx_spawn_options.flags = _SPAWN_SETSID | _SPAWN_TCSETPGRP /* others */;
qnx_spawn_options.ctfd = ttyfd;

where ttyfd is the fd of the free tty or pseudo-tty.


Note: this is much different than tcsetpgrp which requires that
the file descriptor already be the controlling tty. (In Unix,
it’s the open(/dev/tty,…) that does the binding).

Maybe somebody else can’t point to a function or ioctl that
will make a tty the controlling terminal for a process.


Steve Furr email: furr@qnx.com
QNX Software Systems, Ltd.

Steve,

Thanks for your thoughtful answer. I believe that the SSH code successfully
allocates a ptty and ttyfd for the new task. I am also able to create the
session ID using the setsid() call. But it seems that tcsetct(), which is
supposed to “make a terminal device a controlling terminal” both does not work,
and does not return an error.

Is there a bug in tcsetct?

Also, the use of spawnX and execX functions, which use qnx_spawn_options, do not
integrate at all well with the SSH code, which uses fork(). Is there a function
or a way to call existing functions that use qnx_spawn_options, and mimic the
behavior of fork? Does fork use qnx_spawn_options way down in the guts of the
implementation?

Thanks,

-Jim

Steve Furr wrote:


More to the point, POSIX says little about the semantics of /dev/tty.
The act of opening /dev/tty to obtain a new controlling tty for a daemon
process is a Unix convention. I am not sure it is even covered by
the Unix specifications.

QNX 4 didn’t deal with this situation in the conventional Unix
manner, so there is no easy way to obtain a new controlling tty
once you have become detached, whereas Neutrino (consequently RTP/QNX6)
do the Unix style.

This was an area of black magic. I think ideally you need to either
(a) iterate over the available consoles (usually /dev/con?? or
/dev/tty??, depending on the arguments to Dev.ansi/Dev.con) or take
the number of the console you want to run on, or (b) take the first
available pseudo-tty and then bind it to the process as the controlling tty.

I can’t help you with the ioctl that might be able to do this for
you, but:

  1. Detecting that there is no controlling tty is slightly different
    from others. Opening /dev/tty when there is no controlling tty
    will return EACCES or ENOENT (I forget what conditions changed,
    but probably different kernel revs).
  2. You can set the controlling tty for processes you spawn with
    a spawn option.

qnx_spawn_options.flags = _SPAWN_SETSID | _SPAWN_TCSETPGRP /* others */;
qnx_spawn_options.ctfd = ttyfd;

where ttyfd is the fd of the free tty or pseudo-tty.

Note: this is much different than tcsetpgrp which requires that
the file descriptor already be the controlling tty. (In Unix,
it’s the open(/dev/tty,…) that does the binding).

Maybe somebody else can’t point to a function or ioctl that
will make a tty the controlling terminal for a process.


Steve Furr email: > furr@qnx.com
QNX Software Systems, Ltd.

In article <3A7858BC.22148E04@wgate.com>,
Jim Parnell <jparnell@wgate.com> wrote:

Steve,

Thanks for your thoughtful answer. I believe that the SSH code successfully
allocates a ptty and ttyfd for the new task. I am also able to create the
session ID using the setsid() call. But it seems that tcsetct(), which is
supposed to “make a terminal device a controlling terminal” both does not work,
and does not return an error.

Is there a bug in tcsetct?

I don’t believe any I/O managers support it.

Also, the use of spawnX and execX functions, which use qnx_spawn_options, do not
integrate at all well with the SSH code, which uses fork(). Is there a function
or a way to call existing functions that use qnx_spawn_options, and mimic the
behavior of fork? Does fork use qnx_spawn_options way down in the guts of the
implementation?

This depends. I presume SSH does a fork()/exec(). The spawn options work
with the spawn functions. The things you need to do depend greatly
on what the code does between the fork() and the exec(). If it
closes file descriptors, calls setsid() and tcsetpgrp(), then
you can probably replace that code with something that calls spawn().

Consider the following from xterm, for example:

{
pid_t pid = -1;
int slave;

if ((slave = open(ttydev, O_RDWR, 0)) >= 0) {

if (tcsetattr (slave, TCSANOW, &tio) == -1)
SysError(ERROR_TIOCSETP);


… [ Environment and terminal set-up ]


fcntl(slave, F_SETFD, FD_CLOEXEC);

qnx_spawn_options.flags = _SPAWN_SETSID | _SPAWN_SIGCLR | _SPAWN_TCSETPGRP;
qnx_spawn_options.ctfd = 0; // child’s fd or parent’s ?
qnx_spawn_options.iov[0] =
qnx_spawn_options.iov[1] =
qnx_spawn_options.iov[2] = slave;

if (command_to_exec) {
screen->pid = pid = spawnvp(P_NOWAIT,
command_to_exec, command_to_exec);
if ( pid == -1 )
{
/
print error message on screen */
fprintf(stderr, “%s: Can’t execvp %s\n”, xterm_name,
*command_to_exec);
}
}

if ( pid < 0 )
{
… [Accounting/finds shell to run and calls spawnlp()]
}
}
}


which replaces something like:

if ((screen->pid = fork ()) == -1)
SysError (ERROR_FORK);

if (screen->pid == 0) {
/*

  • now in child process
    */

[Environment set-up]

[close some files]

setsid();

[get the tty, change ownership]
[terminal set-up]
[accounting]

exec(…);
}


The environment, accounting, obtaining the tty and the terminal set-up
can all be done in the parent. Closing file descriptors can be done
by setting the file descriptor array, or with CLOEXEC, and the
establishment of a new session and controlling tty is done with the
flags.

Mind you, this can mean shuffling around a lot of code from between
the fork() and exec().

Thanks,

-Jim

Steve Furr wrote:


More to the point, POSIX says little about the semantics of /dev/tty.
The act of opening /dev/tty to obtain a new controlling tty for a daemon
process is a Unix convention. I am not sure it is even covered by
the Unix specifications.

QNX 4 didn’t deal with this situation in the conventional Unix
manner, so there is no easy way to obtain a new controlling tty
once you have become detached, whereas Neutrino (consequently RTP/QNX6)
do the Unix style.

This was an area of black magic. I think ideally you need to either
(a) iterate over the available consoles (usually /dev/con?? or
/dev/tty??, depending on the arguments to Dev.ansi/Dev.con) or take
the number of the console you want to run on, or (b) take the first
available pseudo-tty and then bind it to the process as the controlling tty.

I can’t help you with the ioctl that might be able to do this for
you, but:

  1. Detecting that there is no controlling tty is slightly different
    from others. Opening /dev/tty when there is no controlling tty
    will return EACCES or ENOENT (I forget what conditions changed,
    but probably different kernel revs).
  2. You can set the controlling tty for processes you spawn with
    a spawn option.

qnx_spawn_options.flags = _SPAWN_SETSID | _SPAWN_TCSETPGRP /* others */;
qnx_spawn_options.ctfd = ttyfd;

where ttyfd is the fd of the free tty or pseudo-tty.

Note: this is much different than tcsetpgrp which requires that
the file descriptor already be the controlling tty. (In Unix,
it’s the open(/dev/tty,…) that does the binding).

Maybe somebody else can’t point to a function or ioctl that
will make a tty the controlling terminal for a process.


Steve Furr email: > furr@qnx.com
QNX Software Systems, Ltd.

Steve Furr email: furr@qnx.com
QNX Software Systems, Ltd.

Nobody uses tcsetct? Ouch. I was afraid you’d say something like that. Back to my
port, er, rewrite of sshd’s session.c…

-Jim

Steve Furr wrote:

In article <> 3A7858BC.22148E04@wgate.com> >,
Jim Parnell <> jparnell@wgate.com> > wrote:
Steve,

Thanks for your thoughtful answer. I believe that the SSH code successfully
allocates a ptty and ttyfd for the new task. I am also able to create the
session ID using the setsid() call. But it seems that tcsetct(), which is
supposed to “make a terminal device a controlling terminal” both does not work,
and does not return an error.

Is there a bug in tcsetct?

I don’t believe any I/O managers support it.


Also, the use of spawnX and execX functions, which use qnx_spawn_options, do not
integrate at all well with the SSH code, which uses fork(). Is there a function
or a way to call existing functions that use qnx_spawn_options, and mimic the
behavior of fork? Does fork use qnx_spawn_options way down in the guts of the
implementation?

This depends. I presume SSH does a fork()/exec(). The spawn options work
with the spawn functions. The things you need to do depend greatly
on what the code does between the fork() and the exec(). If it
closes file descriptors, calls setsid() and tcsetpgrp(), then
you can probably replace that code with something that calls spawn().

Consider the following from xterm, for example:

{
pid_t pid = -1;
int slave;

if ((slave = open(ttydev, O_RDWR, 0)) >= 0) {

if (tcsetattr (slave, TCSANOW, &tio) == -1)
SysError(ERROR_TIOCSETP);

… [ Environment and terminal set-up ]

fcntl(slave, F_SETFD, FD_CLOEXEC);

qnx_spawn_options.flags = _SPAWN_SETSID | _SPAWN_SIGCLR | _SPAWN_TCSETPGRP;
qnx_spawn_options.ctfd = 0; // child’s fd or parent’s ?
qnx_spawn_options.iov[0] =
qnx_spawn_options.iov[1] =
qnx_spawn_options.iov[2] = slave;

if (command_to_exec) {
screen->pid = pid = spawnvp(P_NOWAIT,
command_to_exec, command_to_exec);
if ( pid == -1 )
{
/
print error message on screen */
fprintf(stderr, “%s: Can’t execvp %s\n”, xterm_name,
*command_to_exec);
}
}

if ( pid < 0 )
{
… [Accounting/finds shell to run and calls spawnlp()]
}
}
}

which replaces something like:

if ((screen->pid = fork ()) == -1)
SysError (ERROR_FORK);

if (screen->pid == 0) {
/*

  • now in child process
    */

[Environment set-up]

[close some files]

setsid();

[get the tty, change ownership]
[terminal set-up]
[accounting]

exec(…);
}


The environment, accounting, obtaining the tty and the terminal set-up
can all be done in the parent. Closing file descriptors can be done
by setting the file descriptor array, or with CLOEXEC, and the
establishment of a new session and controlling tty is done with the
flags.

Mind you, this can mean shuffling around a lot of code from between
the fork() and exec().

Thanks,

-Jim

Steve Furr wrote:


More to the point, POSIX says little about the semantics of /dev/tty.
The act of opening /dev/tty to obtain a new controlling tty for a daemon
process is a Unix convention. I am not sure it is even covered by
the Unix specifications.

QNX 4 didn’t deal with this situation in the conventional Unix
manner, so there is no easy way to obtain a new controlling tty
once you have become detached, whereas Neutrino (consequently RTP/QNX6)
do the Unix style.

This was an area of black magic. I think ideally you need to either
(a) iterate over the available consoles (usually /dev/con?? or
/dev/tty??, depending on the arguments to Dev.ansi/Dev.con) or take
the number of the console you want to run on, or (b) take the first
available pseudo-tty and then bind it to the process as the controlling tty.

I can’t help you with the ioctl that might be able to do this for
you, but:

  1. Detecting that there is no controlling tty is slightly different
    from others. Opening /dev/tty when there is no controlling tty
    will return EACCES or ENOENT (I forget what conditions changed,
    but probably different kernel revs).
  2. You can set the controlling tty for processes you spawn with
    a spawn option.

qnx_spawn_options.flags = _SPAWN_SETSID | _SPAWN_TCSETPGRP /* others */;
qnx_spawn_options.ctfd = ttyfd;

where ttyfd is the fd of the free tty or pseudo-tty.

Note: this is much different than tcsetpgrp which requires that
the file descriptor already be the controlling tty. (In Unix,
it’s the open(/dev/tty,…) that does the binding).

Maybe somebody else can’t point to a function or ioctl that
will make a tty the controlling terminal for a process.


Steve Furr email: > furr@qnx.com
QNX Software Systems, Ltd.

\


Steve Furr email: > furr@qnx.com
QNX Software Systems, Ltd.


Jim Parnell
WorldGate Communications, Inc.
(215) 354-5147 (Fax:1048)