deprecated TIOCSPGRP and TIOCGPGRP ioctl command

Bruce Evans brde at optusnet.com.au
Fri Nov 18 18:24:46 UTC 2011


On Thu, 10 Nov 2011, Peter Wemm wrote:

> On Wed, Nov 9, 2011 at 5:52 PM, Garrett Cooper <yanegomi at gmail.com> wrote:
>> On Nov 9, 2011, at 4:49 PM, Peter Wemm wrote:
>>
>>> On Wed, Nov 9, 2011 at 3:39 PM, Giovanni Trematerra <gianni at freebsd.org> wrote:
>>>> Are they deprecated enough to be removed, now?

"They" (TIOC[SG]PGRP) aren't deprecated.  They are fundamental ioctls
used to implement tc[sg]etpgrp(3).  There are no specific syscalls for
the latter.  The ioctls are probably deprecated, or even never supported
for direct use.

>>>> FYI FIFO doesn't support them.

These interfaces are for job control.  What they mean for pipes and
fifos is unclear.  I don't really understand what sys_pipe.c does for
them.  It just makes TIOC[SG]PRGP the same as FIO[SG]ETOWN with the
arg negated.  That makes some sense -- pgrps are normally represented
as negative pids, so if the underlying setown functions support both
prgps and processes as they should, using the usual sign hack to
distinguish pgrps for procs, then a sign change is needed to meet the
API of the under underlying functions.

>>>> =================================
>>>> --- sys/kern/sys_pipe.c (revision 227233)
>>>> +++ sys/kern/sys_pipe.c (working copy)
>>>> @@ -1304,17 +1304,6 @@ pipe_ioctl(fp, cmd, data, active_cred, td)
>>>>        *(int *)data = fgetown(&mpipe->pipe_sigio);
>>>>        break;
>>>>
>>>> -   /* This is deprecated, FIOSETOWN should be used instead. */

These "deprecated" comments were written by truckman.  He fixed many
bugs in the [gs]etown interfaces.  I made some comments about this.  AFAIR
we only tried to fix only parts of [gs]etown including F_SETOWN (part of
fcntl()), and changes to the [gs]etpgroup interfaces were mainly side
effects, including the above fairly-misleading comment.

>>>> -   case TIOCSPGRP:
>>>> -       PIPE_UNLOCK(mpipe);
>>>> -       error = fsetown(-(*(int *)data), &mpipe->pipe_sigio);
>>>> -       goto out_unlocked;
>>>> -
>>>> -   /* This is deprecated, FIOGETOWN should be used instead. */
>>>> -   case TIOCGPGRP:
>>>> -       *(int *)data = -fgetown(&mpipe->pipe_sigio);
>>>> -       break;
>>>> -
>>>>    default:
>>>>        error = ENOTTY;

I think what these comments mean is:
"TIOC[SG]PGRP should only be used on ttys (and then only via
tc[gs]etpgrp()), but in case something abused it on pipes, keep doing
things the old way as above)."  Diffs for the above between 2.2.9 and
3.final:

% Index: sys_pipe.c
% ===================================================================
% RCS file: /home/ncvs/src/sys/kern/sys_pipe.c,v
% retrieving revision 1.21
% retrieving revision 1.46.2.5
% diff -u -2 -r1.21 -r1.46.2.5
% --- sys_pipe.c	11 Oct 1996 02:27:30 -0000	1.21
% +++ sys_pipe.c	24 Mar 2000 00:48:57 -0000	1.46.2.5
% @@ -962,10 +952,18 @@
%  		return (0);
% 
% -	case TIOCSPGRP:
% -		mpipe->pipe_pgid = *(int *)data;
% +	case FIOSETOWN:
% +		return (fsetown(*(int *)data, &mpipe->pipe_sigio));
% +
% +	case FIOGETOWN:
% +		*(int *)data = fgetown(mpipe->pipe_sigio);
%  		return (0);
% 
% +	/* This is deprecated, FIOSETOWN should be used instead. */
% +	case TIOCSPGRP:
% +		return (fsetown(-(*(int *)data), &mpipe->pipe_sigio));
% +
% +	/* This is deprecated, FIOGETOWN should be used instead. */
%  	case TIOCGPGRP:
% -		*(int *)data = mpipe->pipe_pgid;
% +		*(int *)data = -fgetown(mpipe->pipe_sigio);
%  		return (0);

So the old way was to only support the TIOC* interfaces, presumably with
the same semantics as now modulo bugs that are hopefully fixed now.

Note that we usually get here, at least now, for fcntl(fd, F_[SG]ETOWN, ...).
This converts to the FIO[SG]ETOWN ioctls.  Direct use of these ioctls is
or should be deprecated.  The bugs hopefully fixed by truckman are caused
by the ioctls being device-specific while the fcntl()s are file-descriptor
or file-specific (I can never remember which).  Old code used a single
pid or pgid per device.  This more or less works for devices, but it
cannot work for multiple fd's or files open on the same device.

The fcntl() APIs support both processes and process groups encoded in
a single arg using the usual sign hack, but the old TIOC[SG]PGRP should
be limited by their very name to only supporting process groups.  I
think they aren't actually limited.  The current one, especially,
blindly flips the sign and does no range checking before getting to the
lower level function which accept all values (with originally negative
pgids being becoming positive and being interpreted as pids).  That's
another reason why TIOC[SG]PGRP is garbage for pipes.

Another reason is that POSIX requires tcsetprgrp() "shall fail" if the
calling process does not have a controlling terminal, or the file is
not the controlling terminal, or the controlling terminal is no longer
associated with the session of the calling process.  The error for
this is ENOTTY.  Since FreeBSD's implementation of tcsetprgrp() uses
ioctl(), it will return this error automatically if the device is not
a tty, provided TIOC[SG]PRGP is not bogusly referenced in files for
non-tty devices.

None of these ioctls or fcntl()s is used often (except the TIOC* ones
for ttys).  Perhaps the TIOC* ones have never been abused (to give
FIO*/fcntl() ones) for pipes.

>>> Be very very careful with this.  It's part of the classic BSD job
>>> control API.  It would be wise to survey whether any ports shells use
>>> this.

Well, not really.  TIOC[SG]PGRP never gave job control for pipes.

>>> You might also want to consider things like this in libc:
>>> int
>>> tcsetpgrp(int fd, pid_t pgrp)
>>> {
>>>        int s;
>>>
>>>        s = pgrp;
>>>        return (_ioctl(fd, TIOCSPGRP, &s));
>>> }
>>> Our own libc code uses this, albeit on an API intended to be used on a tty.

This stuff in sys_pipe.c is simple compared with tty.c :-).  tty.c actually
understands controlling terminals.  In particular, it checks all the POSIX
conditions stated above for tcsetprgrp().  Its handling of FIO[GS]ETOWN is
quite different from its handling of TIOC[GS]PGRP.  There are fewer
restrictions for the former, and the complications are in the f[sg]etown()
functions.

>>> The shell I'd be most concerned about is csh/tcsh in our tree. It has
>>> quite an #ifdef legacy layer and I couldn't convince myself it wasn't
>>> using this indirectly (or the tc* functions) on pipes.
>>>
>>> It might also be an idea to see if the linux compat layer can be
>>> switched over to using the newer API.
>>
>> Move to a compat library perhaps?
>> -Garrett
>
> It's not a compat library candidate.
>
> Summary of the issue:
> ioctl(fd, TIOCSPGRP, arg) can be used on sockets, pipes, ttys.  But not fifos.
> this is a classic BSD job control API.

Not really on pipes -- see above.  Even less on sockets:
- sockets don't seem to ever have abused TIOC[SG]PGRP
- instead, for sockets, there is SIOC[SG]PGRP.  This is used in sys_socket.c
   in exactly the same way that TIOC[SG]PGRP is abused in sys_pipe.c.
   - since its name is spelled with an 'S' (strictly, since its number is
     spelled with an 's'), job control code like tcstpgrp() can't reach
     the socket layer even if it is misapplied to a socket fd, and so it
     can't bogusly succeed.
   - SIOC[SG]PGRP shouldn't exist, since like TIOC[SG]PGRP on pipes, it
     just reverses the sign, so it was superseded by FIO[SG]ETOWN (or
     better the fcntl()).  As for pipes, it now bogusly works on non-pgrps.
   - SIOC[SG]PGRP is not documented in any man page.  This matches its
     supersedence, but was probably a bug -- it is a very old interface;
     FreeBSD-1 has it for sockets.
       (FreeBSD-1 also has TIOC*PGRP for the log device and perhaps for
       some other devices outside of kern, but not for pipes).  It also
       has the following messy translations between FIO* and TIOC*, which
       must have been cleaned up by truckman, and show that the old
       way was probably not for these TIOC and FIO ioctls to actually
       be used; instead, what always sort of worked was the fcntl(),
       using these messy translations to get to older internals using
       TIOC or FIO:

 	FreeBSD-1 ioctl():

% 	case FIOSETOWN:
% 		tmp = *(int *)data;
% 		if (fp->f_type == DTYPE_SOCKET) {
% 			((struct socket *)fp->f_data)->so_pgid = tmp;

 	This starts with the FIOSETOWN ioctl.  (Unfortunately, that is
 	a very old ioctl, so we can't just remove it.)  This is
 	apparently not supported by socket code (it wants SIOCSPGRP?),
 	so hack up something here.

% 			error = 0;
% 			break;
% 		}
% 		if (tmp <= 0) {
% 			tmp = -tmp;
% 		} else {
% 			struct proc *p1 = pfind(tmp);
% 			if (p1 == 0) {
% 				error = ESRCH;
% 				break;
% 			}
% 			tmp = p1->p_pgrp->pg_id;
% 		}
% 		error = (*fp->f_ops->fo_ioctl)
% 			(fp, (int)TIOCSPGRP, (caddr_t)&tmp, p);

 	For ttys but not much else, TIOCSPGRP exists and sort of
 	corresponds to FIOSETOWN.  There is some overlap, but
 	especially for ttys, TIOCSPGRP has very different semantics,
 	so this call caused problems if anyone actually makes it.
 	It is likely to just fail, but I added workarounds to problems
 	near here in some versions of FreeBSD.  These have been
 	replaced by better methods.  E.g., pass FIOSETOWN to lower
 	levels here (done by truckman?).

% 		break;

 	FreeBSD-1 fcntl():

% 	case F_SETOWN:
% 		if (fp->f_type == DTYPE_SOCKET) {
% 			((struct socket *)fp->f_data)->so_pgid = uap->arg;
% 			return (0);
% 		}
% 		if (uap->arg <= 0) {
% 			uap->arg = -uap->arg;
% 		} else {
% 			struct proc *p1 = pfind(uap->arg);
% 			if (p1 == 0)
% 				return (ESRCH);
% 			uap->arg = p1->p_pgrp->pg_id;
% 		}
% 		return ((*fp->f_ops->fo_ioctl)
% 			(fp, (int)TIOCSPGRP, (caddr_t)&uap->arg, p));

 	Essentially the same as for the ioctl.  The ioctl() is essentially
 	an archaic spelling of the fcntl().  This must be supported, but
 	hopefully its misimplementation details (mainly the translation
 	to a PGRP ioctl) don't need to be.
       ) <End FreeBSD-1 stuff>
   <End SIOC[GS]PGRP stuff>

- the garbage or implementaion-detail TIOC[SG]PGRP ioctls are unfortunately
   documented in many places:
   - tap(4), tun(4), tty(4) (legit there), mdoc(7) (bad example)
   and is used in the correspoding source places and others (now checking
   more than kern but not all of sys):
   - kern/subr_log.c, net/bpf.c: like sys/pipe.c
   - net/if_tap.c, net/if_tun.c: like sys/pipe.c, except the ioctl is
     actually documented.

> libc itself uses this internally on ttys.
> libc doesn't check that the function that does this is actually a tty,
> it could be being used by shells and pipes as an obscure side effect.

POSIX requires tcsetpgrp() etc to restrict to more than a tty (requires
controlling terminal and more), so libc must check if the kernel doesn't,
but this is very hard to do in libc.

> The API is equivalent to FIOSETOWN etc and sockets and pipes use
> common code to implement both ioctl() calls.

Actually, very different.  Even F_SETOWN is merely similar to FIOSETOWN,
since the former operates on open files while the latter operates on
devices, at least logically.

> The proposed patch was to remove the TIOCSPGRP -> FIOSETOWN mapping
> for pipes only, to sync them with fifos.  ttys and sockets would be
> unaffected.

No reason to remove it for pipes but not for logs, bpfs, taps or tuns.
Or break POSIX consistently and make the exceptional cases match pipes:
- fifos are missing support for TIOC[SG]PGRP in 2 ways:
   - it is not passed to the socket layer
   - the socket layer doesn't support it anyway, unless it is translated
     to the socket layer spelling
- for sockets, it is spelled SIOC[SG]PGRP and undocumented.
Does POSIX specify sockets yet?  If so it should disallow tiocsetpgrp()
on them, and give more details about how FSETOWN works on them.

> The implementation function still has to stay.  Its not a large chunk
> of obsolete code that goes away, its just the mapping between the
> ioctl number and the implementation.

And it has nothing to do with what is deprecated :-).

Bruce


More information about the freebsd-arch mailing list