终端登陆
BSD终端登陆
在过去的30年中这个过程也没有太多的改变。系统管理员创建一个文件,通常常是/etc/ttys,它是每个终端设备一行。每行都指定了设备名和传递给getty程序的参数。其中一个参数是波特率,例如:当一个系统被引导,内核建立进程ID1——init进程,这个进程可以让系统启动到多用户。init进程读取/etc/ttys,并为每个允许登陆的终端设备fork一个子进程,该子进程使用exec调用getty函数。
如上图所示,所有的进程的实际用户ID和有效用户ID都是0(也就是说它们都拥有超级用户的权限)。init进程exec调用getty程序时,使用空的环境变量。
getty函数为终端设备调用open函数。终端以读、写的方式打开。如果设备是modem,open函数可以延迟设备驱动直到拨号或被响应。设备一经打开,文件描述符0、1、2就会被设置到设备上。之后getty会有一些输出(如:login:,等待我们输入用户名)。如果设备支持多种速度,getty能发现特殊字符,这些特殊字符可以改变终端速度(波特率)。对于getty程序和数据文件(gettytab)可以查看系统手册,它们决定了具体的动作。
当我们输入完用户名后getty的工作就完成了,之后它会调用login程序,类似:
execle(“/bin/login”, “login”, “-p”, username, (char *)0, envp);
(在gettytab文件中有选项,可以让它调用其它程序,但默认是login程序。)init程序带一个空环境变量调用getty;getty为login建立一个环境变量(envp参数),包含终端名(一些类似TERM=foo,这里的终端类型foo是来自gettytab文件)和任何在gettytab中指定的环境字符串。login中的-p标记是用来告诉程序保持传递过来环境变量并向其增加其它变量,而不是替代它。
上图显示了所有的过程,因为init进程是超级用户权限,所以上图所示是有超级用户权限的。下面三个进程同样是有超级用户权限的,因为exec不会改变进程ID。所以,除了最初的init进程外所有其它进程的父进程ID都是1。
login程序做了很多的事情。因为它有我们的用户名,它能调用getpwnam获得密码文件的条目。之后login调用getpass显示提示PASSWORD:并读取密码。它调用crypt去加密刚输入的密码并和shadow密码文件中的pw_passwd字段做比较。如果由于密码无效而失败login调用参数为1的exit函数。当终端的父进程得知这个情况后会再次fork另一个exec的getty,重新开始这个终端。
现在主流UNIX系统都在使用PAM。在访问相应的服务时,PAM允许管理员配置不同的授权方式。
如果登录成功,login会:
- 改变我们的home目录(chdir)
- 改变我们的终端设备所属(chown),这时登录用户拥有它。
- 改变终端设备的访问权限,让我们可以对其进行读写。
- 通过调用setgid和initgroups设置我们的组ID
- 使用所有login获得的信息初始化环境变量,home目录(HOME),shell(SHELL),用户名(USER和LOGNAME)和默认目录(PATH)
- 改变用户ID(setuid)并调用我们的登录shell,如:
execl(“/bin/sh”, “-sh”, (char *)0);
减号做为argv[0]的第一字符,是用来标记调用的shell是做为登录shell使用。shell可以根据这个字符改变它的启动过程。
login实际做的要比这个描述还要多。
为终端登录设置所有事情的过程如下:
现在我们登录的shell会读取它的启动文件(.profile是Bourne shell和Korn shell的启动文件;.bash_profile, .bash_login, .profile是GNU Bourne-again shell的启动文件;.cshrc和.login是C shell的启动文件)这些启动文件通常会改变一些环境变量并添加很多环境变量。例如很多用户会设置他们的PATH和实际终端类型(TERM)的提示符。当启动文件结束后,我们会看到shell的提示符并且可以输入命令。