Re: Per-descriptor state

2023-05-07 Thread Johnny Billquist

On 2023-05-08 02:09, Johnny Billquist wrote:
On 2023-05-08 01:30, David Holland wrote> On Fri, May 05, 2023 at 
09:44:25PM -0400, Mouse wrote:



  > not nearly as
  > horrible as, say, the hack I once implemented where calling
  > wait4(0x456d756c,(int *)0x61746f72,0x4d616769,(struct rusage 
*)0x633a2d29)

   ^^
  > would produce suitably magic effects.  (This was on a 32-bit 
machine.)


surely you mean 39!


Hmm?

"EmulatorMagic:-)" don't do it for you, you want "EmulatorMagic:=)" ?

I wonder what the emoji " :=) " means... Double nose?

Ok. I'm being very silly. I need to go to bed...

:-)


And I'm clearly half asleep already. Sorry... :-9

it was...

  Johnny

--
Johnny Billquist  || "I'm on a bus
  ||  on a psychedelic trip
email: b...@softjar.se ||  Reading murder books
pdp is alive! ||  tryin' to stay hip" - B. Idol


Re: Per-descriptor state

2023-05-07 Thread David Holland
On Mon, May 08, 2023 at 12:36:21AM +, David Holland wrote:
 > I... completely failed to recognize it as an emoji and assumed it was
 > supposed to be -9 to forcibly shut something down.

emoji. smiley. yeah

-- 
David A. Holland
dholl...@netbsd.org


Re: Per-descriptor state

2023-05-07 Thread David Holland
On Mon, May 08, 2023 at 02:09:07AM +0200, Johnny Billquist wrote:
 > > > not nearly as
 > > > horrible as, say, the hack I once implemented where calling
 > > > wait4(0x456d756c,(int *)0x61746f72,0x4d616769,(struct rusage 
 > > > *)0x633a2d29)
 > >
 > > ^^
 > >   > would produce suitably magic effects.  (This was on a 32-bit machine.)
 > > 
 > > surely you mean 39!
 > 
 > Hmm?
 > 
 > "EmulatorMagic:-)" don't do it for you, you want "EmulatorMagic:=)" ?

I... completely failed to recognize it as an emoji and assumed it was
supposed to be -9 to forcibly shut something down.

:-|

-- 
David A. Holland
dholl...@netbsd.org


Re: Per-descriptor state

2023-05-07 Thread Johnny Billquist
On 2023-05-08 01:30, David Holland wrote> On Fri, May 05, 2023 at 
09:44:25PM -0400, Mouse wrote:



  > not nearly as
  > horrible as, say, the hack I once implemented where calling
  > wait4(0x456d756c,(int *)0x61746f72,0x4d616769,(struct rusage *)0x633a2d29)
   ^^
  > would produce suitably magic effects.  (This was on a 32-bit machine.)

surely you mean 39!


Hmm?

"EmulatorMagic:-)" don't do it for you, you want "EmulatorMagic:=)" ?

I wonder what the emoji " :=) " means... Double nose?

Ok. I'm being very silly. I need to go to bed...

:-)

  Johnny

--
Johnny Billquist  || "I'm on a bus
  ||  on a psychedelic trip
email: b...@softjar.se ||  Reading murder books
pdp is alive! ||  tryin' to stay hip" - B. Idol


Re: Per-descriptor state

2023-05-07 Thread David Holland
On Fri, May 05, 2023 at 09:44:25PM -0400, Mouse wrote:
 > >>> But I kind of think it'd be preferable to make a way to clone a
 > >>> second independent struct file for the same socket than to start
 > >>> mucking with per-descriptor state.
 > >> [...] it should be easy to extend dup3() to make that possible [...]
 > > If we were to add such a thing it should be called something else,
 > > not dup, because it's a fundamentally different operation from dup
 > > and we don't need people confusing them.
 > 
 > Different?  Some.  But not very.  It _is_ closely related to dup().  I
 > don't think dup3() would be a horrible way to do it - 

There's already a dup3(). But ignoring that... yes, it's quite
significant, it constructs a materially different kind of additional
reference. For everyone who knows the internal structure of
references, which is probably everyone reading this list, it's easy to
understand what the semantics are and what the difference between the
two operations; for everyone else, which is most of the world and most
of the people who will ever use the call, it is _not_ clear and if
the two cases aren't made overtly distinct they'll routinely mix them
up and get in trouble. Even if they are made overtly distinct you'll
still get bozos making wrong proclamations about them in places like
stackoverflow.

*puts on his "taught this to undergrads for many years" hat*

 > not nearly as
 > horrible as, say, the hack I once implemented where calling
 > wait4(0x456d756c,(int *)0x61746f72,0x4d616769,(struct rusage *)0x633a2d29)
  ^^
 > would produce suitably magic effects.  (This was on a 32-bit machine.)

surely you mean 39!

 > But, honestly, when I saw the idea my reaction was to make it a new
 > operation to fcntl.  F_REOPEN, say, since it's creating new per-open
 > state.  Or, if you want to be all overloady, how about
 > open(0,O_REOPEN,existingfd)?  It _is_ creating new per-open state, so
 > open is in _some_ sense right.
 > 
 > My choice, for what it's worth, would be fcntl, dup3 second, with
 > O_REOPEN a distant third.

fcntl is inherently terrible and shouldn't get new ops or use cases
(there is absolutely no reason to prefer it to a new syscall except
for conservation of syscall numbers, which isn't a useful thing).

Should be something like reopen(fd, flags).

Or it could be open("/dev/fd/%d", flags); except that neither the
pathname nor the semantics of /dev/fd are reliably portable.


(...passing a fd as a 3rd argument to open is really horrible; that's
a mode_t, not a fd)

-- 
David A. Holland
dholl...@netbsd.org


Re: Per-descriptor state

2023-05-07 Thread David Holland
On Thu, May 04, 2023 at 08:40:49AM -0400, Mouse wrote:
 > >> I can't think of any at all; to begin with it's limited to forks
 > >> that don't exec
 > [...]
 > 
 > Well, except for libraries that open fds internally, without exposing
 > them to the calling code.  Depending on the user case, they may want
 > them closed if the application forks.
 >
 > [...]
 > 
 > Because one wants to exec a child process, maybe?

If the fork is going to exec, O_CLOEXEC is sufficient; I can't think
of any realistic situation where having a second reference to the same
file for at most few extra milliseconds can actually hurt anything.

-- 
David A. Holland
dholl...@netbsd.org


Re: Per-descriptor state

2023-05-05 Thread Mouse
>>> But I kind of think it'd be preferable to make a way to clone a
>>> second independent struct file for the same socket than to start
>>> mucking with per-descriptor state.
>> [...] it should be easy to extend dup3() to make that possible [...]
> If we were to add such a thing it should be called something else,
> not dup, because it's a fundamentally different operation from dup
> and we don't need people confusing them.

Different?  Some.  But not very.  It _is_ closely related to dup().  I
don't think dup3() would be a horrible way to do it - not nearly as
horrible as, say, the hack I once implemented where calling
wait4(0x456d756c,(int *)0x61746f72,0x4d616769,(struct rusage *)0x633a2d29)
would produce suitably magic effects.  (This was on a 32-bit machine.)

But, honestly, when I saw the idea my reaction was to make it a new
operation to fcntl.  F_REOPEN, say, since it's creating new per-open
state.  Or, if you want to be all overloady, how about
open(0,O_REOPEN,existingfd)?  It _is_ creating new per-open state, so
open is in _some_ sense right.

My choice, for what it's worth, would be fcntl, dup3 second, with
O_REOPEN a distant third.

/~\ The ASCII Mouse
\ / Ribbon Campaign
 X  Against HTMLmo...@rodents-montreal.org
/ \ Email!   7D C8 61 52 5D E7 2D 39  4E F1 31 3E E8 B3 27 4B


Re: Per-descriptor state

2023-05-04 Thread Mouse
>>> I should probably add [close-on-fork] here, then, though use cases
>>> will likely be rare.  I can think of only one program I wrote where
>>> it'd be useful; I created a "close these fds post-fork" data
>>> structure internally.
>> I can't think of any at all; to begin with it's limited to forks
>> that don't exec, and unless just using it for convenience as you're
>> probably suggesting,

Yes.  If the application does all the forking (ie, except for forks
inside libraries, for which see below), it is just convenience, freeing
the application from keeping track of which fds need closing.

Well, except for libraries that open fds internally, without exposing
them to the calling code.  Depending on the user case, they may want
them closed if the application forks.

>> it only applies when also using threads, and if one's using threads
>> why is one also using forks?

Because one wants to exec a child process, maybe?

>> So it seems like it's limited to badly designed libraries that want
>> to fork behind the caller's back instead of setting up their forks
>> at initialization time.  Or something.

What about libraries that fork _not_ behind the caller's back?
(system(3) being, perhaps, the poster child.)

> Or it is needed for a little used application called Firefox.

What part of "badly designed" does that not fit?  (Okay, admittedly, I
don't know what Firefox looks like internally.  But the UI design is
bad enough I would expect the internals to be little better.)

/~\ The ASCII Mouse
\ / Ribbon Campaign
 X  Against HTMLmo...@rodents-montreal.org
/ \ Email!   7D C8 61 52 5D E7 2D 39  4E F1 31 3E E8 B3 27 4B


Re: Per-descriptor state

2023-05-04 Thread Joerg Sonnenberger
On Thu, May 04, 2023 at 09:58:49AM +0100, Robert Swindells wrote:
> 
> David Holland  wrote:
> >On Sun, Apr 30, 2023 at 09:44:49AM -0400, Mouse wrote:
> > > > Close-on-fork is apparently either coming or already here, not sure
> > > > which, but it's also per-descriptor.
> > > 
> > > I should probably add that here, then, though use cases will likely be
> > > rare.  I can think of only one program I wrote where it'd be useful; I
> > > created a "close these fds post-fork" data structure internally.
> >
> >I can't think of any at all; to begin with it's limited to forks that
> >don't exec, and unless just using it for convenience as you're
> >probably suggesting, it only applies when also using threads, and if
> >one's using threads why is one also using forks? So it seems like it's
> >limited to badly designed libraries that want to fork behind the
> >caller's back instead of setting up their forks at initialization
> >time. Or something.
> 
> Or it is needed for a little used application called Firefox.

For a sandbox, something like closefrom is actually much preferred as
you don't know what else has opened file descriptors. I really question
the sanity of close-on-fork...

Joerg


Re: Per-descriptor state

2023-05-04 Thread Robert Swindells


David Holland  wrote:
>On Sun, Apr 30, 2023 at 09:44:49AM -0400, Mouse wrote:
> > > Close-on-fork is apparently either coming or already here, not sure
> > > which, but it's also per-descriptor.
> > 
> > I should probably add that here, then, though use cases will likely be
> > rare.  I can think of only one program I wrote where it'd be useful; I
> > created a "close these fds post-fork" data structure internally.
>
>I can't think of any at all; to begin with it's limited to forks that
>don't exec, and unless just using it for convenience as you're
>probably suggesting, it only applies when also using threads, and if
>one's using threads why is one also using forks? So it seems like it's
>limited to badly designed libraries that want to fork behind the
>caller's back instead of setting up their forks at initialization
>time. Or something.

Or it is needed for a little used application called Firefox.


Re: Per-descriptor state

2023-05-03 Thread David Holland
On Sun, Apr 30, 2023 at 10:50:51PM +0700, Robert Elz wrote:
 > Date:Sun, 30 Apr 2023 05:25:41 +
 > From:David Holland 
 > Message-ID:  
 > 
 >   | Close-on-fork is apparently either coming or already here, not sure
 >   | which, but it's also per-descriptor.
 > 
 > We don't have it, but it will be in Posix-8.   Largely inspired by the
 > needs of threaded programs (without lots of critical sections, one cannot
 > otherwise open anything if another thread might fork, there's no
 > way to avoid race conditions, hence O_CLOFORK on open ... not sure if
 > anyone has thought of a way to add it to socket() - that doesn't look
 > to be trivial, though it might be possible to abuse one of the params
 > it has - probably domain - and add flags in upper bits ... while having
 > it able to be set/reset via fcntl is useful, to work, it needs to be
 > able to be set atomically with the operation that creates the fd, and
 > having it default "on", which would work, would break almost all existing
 > non-trivial code).

Given that the trend is to just add extra calls with flags arguments
without even pausing to consider, I'd be surprised if nobody's banged
out a socket4() yet.

 >   | But I kind of think it'd be preferable to make a way to
 >   | clone a second independent struct file for the same socket than to
 >   | start mucking with per-descriptor state.
 > 
 > When I saw mouse's message, I was thinking the exact same thing,
 > and it should be easy to extend dup3() to make that possible

If we were to add such a thing it should be called something else, not
dup, because it's a fundamentally different operation from dup and we
don't need people confusing them.

 > - however
 > I'm not sure what effects that might have on the semantics of sockets
 > (what assumptions the current code might make about there being only
 > one struct file, with all it contains, for a socket).

Indeed :-|

-- 
David A. Holland
dholl...@netbsd.org


Re: Per-descriptor state

2023-05-03 Thread David Holland
On Sun, Apr 30, 2023 at 09:44:49AM -0400, Mouse wrote:
 > > Close-on-fork is apparently either coming or already here, not sure
 > > which, but it's also per-descriptor.
 > 
 > I should probably add that here, then, though use cases will likely be
 > rare.  I can think of only one program I wrote where it'd be useful; I
 > created a "close these fds post-fork" data structure internally.

I can't think of any at all; to begin with it's limited to forks that
don't exec, and unless just using it for convenience as you're
probably suggesting, it only applies when also using threads, and if
one's using threads why is one also using forks? So it seems like it's
limited to badly designed libraries that want to fork behind the
caller's back instead of setting up their forks at initialization
time. Or something.

 > > The thing is, per-descriptor state is a mess and it shouldn't be
 > > allowed to proliferate.  The reason: the descriptor is an array
 > > index.  There's plenty of room to store stuff in the object it looks
 > > up (that is, struct file) but storing stuff on the key side of the
 > > array is a problem.
 > 
 > (References to -current here really mean "filedesc.h 1.70 according to
 > cvsweb.netbsd.org".)
 >
 > [...]

I had forgotten how much crap we'd added on behalf of the silly broken
close scheme. So all those costs have been paid already. :-|

 > > (Then there's also another issue, which is that in nearly all cases
 > > nonblocking I/O is a workaround for interface bugs, e.g. nonblocking
 > > accept or open of dialout modem devices, or for structural problems
 > > in software that also needs to use the same file handle, like your
 > > original problem with curses. In the long run it's probably better to
 > > kill off the reasons to want to use nonblocking I/O at all.)
 > 
 > And replace nbio with...what?  Multiple threads doing blocking calls?
 > Or do you think everything should be nonblocking by default, with
 > blocking behaviour implemented userland-side?  Or am I completely
 > misinterpreting somehow?

select?

-- 
David A. Holland
dholl...@netbsd.org


Re: Per-descriptor state

2023-05-03 Thread Christos Zoulas
In article <21465.1682869...@jacaranda.noi.kre.to>,
Robert Elz   wrote:
>Date:Sun, 30 Apr 2023 05:25:41 +
>From:David Holland 
>Message-ID:  
>
>  | Close-on-fork is apparently either coming or already here, not sure
>  | which, but it's also per-descriptor.
>
>We don't have it, but it will be in Posix-8.   Largely inspired by the
>needs of threaded programs (without lots of critical sections, one cannot
>otherwise open anything if another thread might fork, there's no
>way to avoid race conditions, hence O_CLOFORK on open ... not sure if
>anyone has thought of a way to add it to socket() - that doesn't look
>to be trivial, though it might be possible to abuse one of the params
>it has - probably domain - and add flags in upper bits ... while having
>it able to be set/reset via fcntl is useful, to work, it needs to be
>able to be set atomically with the operation that creates the fd, and
>having it default "on", which would work, would break almost all existing
>non-trivial code).

We already abuse "type":

 The following flags can be or'ed to the type to condition the returned
 file descriptor: The following flags are valid:

   SOCK_CLOEXEC Set the close on exec property.
   SOCK_NONBLOCK Sets non-blocking I/O.
   SOCK_NOSIGPIPE Return EPIPE instead of raising SIGPIPE.

christos



Re: Per-descriptor state

2023-04-30 Thread Mouse
>> Close-on-fork is apparently either coming or already here, [...]
> We don't have it, but it will be in Posix-8.  [...] not sure if
> anyone has thought of a way to add it to socket() -

It's looking to me as though more and more syscalls are having to grow
flags arguments to do things right.  Auto-close-on-fork for socket(),
accept(), and socketpair(); per-operation non-blocking for at least
some half-dozen calls

I don't see how to avoid it for socket().  For accept(), it could be
shoehorned into a SOL_SOCKET sockopt (SO_AUTO_CLOFORK_ON_ACCEPT, say,
better name welcomed).

> that doesn't look to be trivial, though it might be possible to abuse
> one of the params [socket()] has - probably domain - and add flags in
> upper bits ...

Possible?  Probably.  Good?  No, IMO.

> while having it able to be set/reset via fcntl is useful, to work, it
> needs to be able to be set atomically with the operation that creates
> the fd,

Well, to work for one particularly important use case.  It can work
just fine for various other use cases without that.

> and having it default "on", which would work, would break almost all
> existing non-trivial code).

What about having it default to a per-proces (or per-thread) settable
state?

Mouse


Re: Per-descriptor state

2023-04-30 Thread Robert Elz
Date:Sun, 30 Apr 2023 05:25:41 +
From:David Holland 
Message-ID:  

  | Close-on-fork is apparently either coming or already here, not sure
  | which, but it's also per-descriptor.

We don't have it, but it will be in Posix-8.   Largely inspired by the
needs of threaded programs (without lots of critical sections, one cannot
otherwise open anything if another thread might fork, there's no
way to avoid race conditions, hence O_CLOFORK on open ... not sure if
anyone has thought of a way to add it to socket() - that doesn't look
to be trivial, though it might be possible to abuse one of the params
it has - probably domain - and add flags in upper bits ... while having
it able to be set/reset via fcntl is useful, to work, it needs to be
able to be set atomically with the operation that creates the fd, and
having it default "on", which would work, would break almost all existing
non-trivial code).

  | But I kind of think it'd be preferable to make a way to
  | clone a second independent struct file for the same socket than to
  | start mucking with per-descriptor state.

When I saw mouse's message, I was thinking the exact same thing,
and it should be easy to extend dup3() to make that possible - however
I'm not sure what effects that might have on the semantics of sockets
(what assumptions the current code might make about there being only
one struct file, with all it contains, for a socket).

kre



Re: Per-descriptor state

2023-04-30 Thread Mouse
> Close-on-fork is apparently either coming or already here, not sure
> which, but it's also per-descriptor.

I should probably add that here, then, though use cases will likely be
rare.  I can think of only one program I wrote where it'd be useful; I
created a "close these fds post-fork" data structure internally.

> The thing is, per-descriptor state is a mess and it shouldn't be
> allowed to proliferate.  The reason: the descriptor is an array
> index.  There's plenty of room to store stuff in the object it looks
> up (that is, struct file) but storing stuff on the key side of the
> array is a problem.

(References to -current here really mean "filedesc.h 1.70 according to
cvsweb.netbsd.org".)

Looking at the include files, it looks to me as though descriptors are
indices into an array of structs (struct fdfile) in -current or 5.2, or
an index into two parallel arrays of pointers and flags in 1.4T.  Those
then point to the structs file (the per-open state).

It's true the flags fields are chars (two bits used in 1.4T, two
separate chars storing one bit each in 5.2 or -current).  But it's a
far cry from being as bad as you outline.  There are multiple bits
free, and, even if they run out, growing them from chars is a low
(memory) cost on 1.4T and probably zero on 5.2 and -current on most
ports.

> For a couple bits you can mask them off from the pointer (though even
> that's abusive);

If that were what were being done, I would agree it's abusive.

> more than that and suddenly you need to double the size of the
> fdtable so it contains an extra machine word for random state bits as
> well as the pointer to the struct file.

That is quite possibly why 1.4T uses parallel arrays rather than an
array of structs.  In 5.2 and -current, there is enough additional
state that someone (rightly, IMO) decided it wasn't worth the code
complexity of keeping parallel arrays.  (See struct fdfile in
sys/filedesc.h for the additional state I'm talking about.)

> (Then there's also another issue, which is that in nearly all cases
> nonblocking I/O is a workaround for interface bugs, e.g. nonblocking
> accept or open of dialout modem devices, or for structural problems
> in software that also needs to use the same file handle, like your
> original problem with curses. In the long run it's probably better to
> kill off the reasons to want to use nonblocking I/O at all.)

And replace nbio with...what?  Multiple threads doing blocking calls?
Or do you think everything should be nonblocking by default, with
blocking behaviour implemented userland-side?  Or am I completely
misinterpreting somehow?

> (also, "mirabile visu")

What did I write?

*checks*

Oops.  Thanks.

/~\ The ASCII Mouse
\ / Ribbon Campaign
 X  Against HTMLmo...@rodents-montreal.org
/ \ Email!   7D C8 61 52 5D E7 2D 39  4E F1 31 3E E8 B3 27 4B


Re: Per-descriptor state

2023-04-29 Thread David Holland
On Sun, Apr 30, 2023 at 12:49:03AM -0400, Mouse wrote:
 > This is pushing towards making it per-descriptor state.  At present,
 > the versions I have don't have anything but close-on-exec as true
 > per-descriptor state.  A quick look at 9.1 and cvsweb.netbsd.org
 > (which, mirabilu visu, actually works for me, unlike www.netbsd.org and
 > mail-index.netbsd.org) sys/sys/filedesc.h makes me think that that's
 > true of them as well.

Close-on-fork is apparently either coming or already here, not sure
which, but it's also per-descriptor.

The thing is, per-descriptor state is a mess and it shouldn't be
allowed to proliferate. The reason: the descriptor is an array index.
There's plenty of room to store stuff in the object it looks up (that
is, struct file) but storing stuff on the key side of the array is a
problem. For a couple bits you can mask them off from the pointer
(though even that's abusive); more than that and suddenly you need to
double the size of the fdtable so it contains an extra machine word
for random state bits as well as the pointer to the struct file.
Granted, it's not like this is going to make any machine (that can run
Unix at all) tip over from KVA exhaustion, but it's untidy, messy,
wasteful, and generally offensive to cleanliness.

In general I think there is very little reason to have per-descriptor
(rather than per-open) state and the only really valid use cases are
those that pertain specifically to individual descriptor numbers,
which in turn is pretty much limited to close behavior like
close-on-exec.

Granted, that's for files, where if you want two independent opens of
the same file it's mostly not difficult to get them. For sockets that
isn't a thing. But I kind of think it'd be preferable to make a way to
clone a second independent struct file for the same socket than to
start mucking with per-descriptor state.

(Then there's also another issue, which is that in nearly all cases
nonblocking I/O is a workaround for interface bugs, e.g. nonblocking
accept or open of dialout modem devices, or for structural problems in
software that also needs to use the same file handle, like your
original problem with curses. In the long run it's probably better to
kill off the reasons to want to use nonblocking I/O at all.)


(also, "mirabile visu")
-- 
David A. Holland
dholl...@netbsd.org


Per-descriptor state

2023-04-29 Thread Mouse
Back in late March, I wrote here (under the Subject: Re: flock(2):
locking against itself?) about a locking issue, which drifted into
discussing per-descriptor state versus per-open state (thank you
dholland for that phrasing!) versus backing-object state.  In
particular, someone (I forget who) said that non-blocking really should
be per-operation rather than being state anywhere.

That's correct, but, thinking about it since then, that is not as easy
as one might wish, because there are quite a number of calls that can
be affected.  (Offhand: {,p}{read,write}{,v}, connect, accept,
{send,recv}{,msg}, sendto, recvfrom.  There are probably others, but
this is sixteen already - though I'm not sure p{read,write}{,v} need
the option; are there any seekable objects on which non-blocking is
significant?)  Some of these, such as send*, already have a flags
argument that could grow a new bit to indicate nonblocking, but the
rest - more than half - would need to have an alternative version
created with a flags field, or some such.  While hardly impossible,
this gets annoying, and indeed might be best addressed by (to use
read() as an example) making the real syscall backing read() always
take a flags argument, with the libc stub supplying a fixed value for
the flags when the flagless API is called.

This is pushing towards making it per-descriptor state.  At present,
the versions I have don't have anything but close-on-exec as true
per-descriptor state.  A quick look at 9.1 and cvsweb.netbsd.org
(which, mirabilu visu, actually works for me, unlike www.netbsd.org and
mail-index.netbsd.org) sys/sys/filedesc.h makes me think that that's
true of them as well.

For backward compatibility, I would be inclined to leave the existing
mechanisms in place, theoretically to be removed eventually.  This also
means divorcing "non-blocking open" from "non-blocking I/O after open".

So: does anyone have any comments on the above analysis, or thoughts on
good, bad, or even just notable effects making it real per-descriptor
state might have?

/~\ The ASCII Mouse
\ / Ribbon Campaign
 X  Against HTMLmo...@rodents-montreal.org
/ \ Email!   7D C8 61 52 5D E7 2D 39  4E F1 31 3E E8 B3 27 4B