Running top without a shell -- more questions

John john at
Sat Feb 5 08:01:27 PST 2005

On Sat, Feb 05, 2005 at 12:53:43PM +0100, Anthony Atkielski wrote:
> I've set up a user account that has top for its shell (instead of one of
> the standard shells).  As expected, when I log into this account, I
> immediately find myself in top, and if I stop top, I'm instantly logged
> out.  That's what I want.  I use it to have a continuously running
> console status display without the need to be actually logged in to a
> (potentially dangerous) shell.
> However, I note that there is a login process under root that just sits
> there, and top runs as a child of that under my chosen user name. Is
> there a way to get top to run by itself, like csh or some other shell,
> with the login process out of the system? What causes login to go away
> in the normal case, when the shell is actually a shell program?  Isn't
> the user fully logged in by the time control passes to top?

I am not seeing what you are seeing.  I see a login process hanging
around with the regular shells, just like you are describing for top.

I suspect that this is the process that makes the closing entries
marking the logout whenever people log out (terminate their shell).

There's no magic about being a shell.  You could write your own.
Login isn't treating top any differently than a conventional shell
in my system.

Once upon a time, like version 7 Unix, login would simply make an
exec-family call to replace itself with the desired "shell" after
doing all the setup, ID's changes, and so forth, and that would
cause init to spawn a new getty when the wait for that pid returned.
It was simple, and it was elegant, it reclaimed the resources used
by login (this was in the days of 64k data space + 64k program code
space, period), but there was no reliable way to collect logout
information, so that hasn't been true in a long, long time.

What you could do is actually not run login, either.  You could
modify /etc/ttys to call your own little buffer program.   You
do NOT want to change /etc/ttys to run top directly, because it
would be running as root, and top has a built in k (kill) command,
so you would not want to have that!

Anyway, your buffer program could do a little of what login and
getty do in terms of configuring the port (none may be necessary
for a vterminal), changing uid (you could use the top account you've
already created, perhaps something as simple as nobody) and home
directory (maybe just /tmp)? and then you wouldn't even have to
log in - you'd have top running continuously on a vtty without even
having to log in.  If someone exited it, init would restart it

I have had the best luck working with modifying /etc/ttys if I
first flag off whatever owns that device, do the "kill -1 1", then
change the program and flagging it on and doing the "kill -1 1"
again.  I have not had uniformly good results by leaving it on and
changing the thing it calls - your results may vary.  Rebooting
will take care of it, too, of course.

In fact, here is a TEENSY STUB for that buffer program.  While this
actually WORKS, I do not recommend you use it without cleaning it
up.  It is very cavalier, and done in the old Version 7 "style",
and was whipped up with the philosophy "if you don't know what to
do about an error, don't check for it." :)  On the other hand,
when it is invoked through init, there will be stdin, stdout,
or stderr, so don't get too excited about doing a lot of error
reporting if you can't get file handles 0, 1, and 2 open!

I mean, you can substitute the numeric UID and GID you want to use,
but you might want to do something a little more elegant and
actually look them up.  You may also want to insert a "sleep"
after the exec so it won't "spin" so much if top fails a lot or is
terminated a lot (though modern "init" processes check for this
and do their own throttling).

#include <unistd.h>
#include <string.h>
#include <fcntl.h>

main(argc, argv)
int argc;
char **argv;
    char devname[128] = "/dev/";
    char *devptr;
    int devnused;

    if (argc < 2) {
	/* there probably isn't anything open to complain to, so just exit */

    /* These are probably closed, but help to simulate the init
	call for testing */
    close(2); close(1); close(0);

    devptr = argv[1];
    if (*devptr != '/') {
	devnused = strlen(devname);
	strncat(devname,argv[1],(sizeof devname) - devnused);
	devptr = devname;

    /* again, if this fails, there's nothing to complain to - so
       why check? */
    execl("/usr/bin/top","top",(char *)0);

John Lind
john at starfire.MN.ORG

More information about the freebsd-questions mailing list