Virtually all Linux sessions begin with the user typing his user name at a prompt that looks like this:
login:
In this article, I will explain a little about what really happens behind the scenes and what contortions the system goes through to get a user going.
First, a quick look at the shell. The shell, which is just a program like any other, reads the characters you type and looks for a program with the same name. Program names are typed at the prompt and executed by the shell. Ending a command line with the & character causes the command to be run in the background.
The shell runs a program in two steps. First, the shell does an operation called a “fork”. Forking creates a new process that looks just like the original process, inheriting many attributes of its parent such as any open files and user ID. Although it is an exact copy of the shell program, the “child” process does not read user commands. The child shell immediately does an operation called an “exec”, short for “execute”, in which it causes the Linux kernel to load the new program over the top of the child shell and run that program in its place.
At this point, the original shell simply waits for the child program to finish. Once done, it gets the next line of input from the user, then the whole procedure is repeated. In an active UNIX system, this sort of thing is happening all the time. Even on fairly inactive systems, processes are still run to do housekeeping chores, while others are simply sleeping and waiting for something to happen.
From the bash shell, you can see how exec works by typing
exec ls -l
The ls command runs as usual, but when it is done, you are no longer logged in. The shell is replaced by ls, and when it finishes, it is as if your shell had finished.
When the kernel is first loaded into memory, it initializes itself and any hardware that may be attached to the computer. Once the kernel is established enough to be able to run programs, it does. The first program is called “init”; its job is to function as the ancestor of all processes.
When init starts, it reads a file called inittab, usually located in /etc. This file tells init which programs should be run under which conditions. Not only does init run the startup scripts that bring the rest of the system up, but init also takes care of shutting the system down.
One of the responsibilities of init is running the programs that let users log into the system. For a terminal (or virtual console), the two programs used are getty and login. getty is short for “get terminal”. A basic getty program opens the terminal device, initializes it, prints login: and waits for a user name to be entered. Modern getty programs (several are available for Linux) can do other things as well—if the terminal device is a (recent) modem, they can read status codes sent by the modem to tell if the call is voice or fax and handle the call appropriately. Most of the time, though, someone just wants to log in, so getty executes the login program, giving the user name to log in via the command line.
The login program then prompts the user for a password. If the password is wrong, login simply exits. The init process then notices that one of its children has exited and spawns another getty process on the terminal in question. If the password is good, login changes its process user ID to that of the user and executes the user's shell. At this point, the user can type commands. When the user exits by typing the shell's built-in logout command, the shell exits and init notices that its child has exited and spawns another getty on the terminal.
Why are two separate programs used to log in instead of just one? The answer is that doing it this way provides more flexibility. For example, getty doesn't have to execute login—it can execute a program to receive (or send) faxes, a PPP daemon to emulate a network connection over a serial line, or if you have a modem with “voicemail”, one of those phone tree programs that people hate so much (“press five to hear these options again”).
Similarly, login is sometimes needed without getty; for example, when a user logs in over a network, no terminal device is waiting. Instead, each new connection is handled by a program called telnetd that forks and executes a login process. telnetd remains to pass characters between the network and the new shell.
As a partial example of how the process works, Listing 1 shows an autologin replacement for getty. This replacement is meant for people who are tired of typing their user ID and password for the bazillionth time. You can boot Linux and have it drop straight into a couple of shells—sort of like DOS, but with virtual consoles.
To install autologin, copy it to the /sbin (system binaries) directory and type:
chmod +x /sbin/autologin
as root. Still as root, edit the /etc/inittab file and change the lines that look like this:
c1:12345:respawn:/sbin/getty 38400 tty1
to:
c1:12345:respawn:/sbin/autologin tty1 login -f myid
replacing myid with your own user ID. Red Hat installations typically do not have the letter c at the beginning of the line.
Be sure to leave some of the lines containing getty exactly as they are—if you do something wrong, you are going to need a way to log into your system. On my own system, I change c1 through c3 and run three initial shells. Once the file is edited, reboot the system and all should work.
The first argument to autologin is the name of the terminal. The rest of the command line is used as the login command that does the work.
The first line tells the kernel how to run this program, in this case by letting the bash shell interpret it. The first exec line is a Bourne shell trick that lets a shell script change the source/destination of its standard input, standard output and standard error. We want to set file descriptors 0, 1 and 2 to refer to the terminal device as expected by login (and many other programs) when they run. The cat command displays the system's standard logon message. The shift command shifts the positional parameters to the shell script. Argument $1 is deleted, argument $2 becomes $1, argument $3 becomes $2 and so on. The last line executes the rest of the command line as a program. In this case, the login -f option performs the normal login procedure, with the -f option telling login not to bother with passwords.