您的位置:首页 > 运维架构 > Linux

Linux 开机脚本启动顺序--inittab脚本启动解析

2013-12-10 14:26 357 查看
Linux 开机脚本启动顺序:

第一步:启动内核

第二步:执行init (配置文件/etc/inittab)

第三步:启动相应的脚本,执行inittab脚本,并且执行里面的脚本/etc/init.d rc.sysinit
rc.d rc.local。。。

第四步:启动login登录界面 login

第五步:在用户登录的时候执行sh脚本的顺序:每次登录的时候都会完全执行的 /etc/profile /etc/bashrc /root/.bashrc /root/.bash_profile

inittab脚本:

init的进程号为1,是所有进程的父进程,内核初始化完毕之后,init程序开始运行。其他软件也同时开始运行。init程序通过/etc/inittab文件进行配置。

/etc/inittab文件每一行包括四个字段:label:runlevel:action:process。详细解释如下。

1.label

登记项标志符,是一个任意指定的、4个字符以内的序列标号,在本文件内必须唯一。

label是1到4个字符的标签,用来标示输入的值。一些系统只支持2个字符的标签。鉴于此原因,多数人都将标签字符的个数限制在2个以内。该标签可以是任意字符构成的字符串,但实际上,某些特定的标签是常用的,在Red Hat Linux中使用的标签是:

id 用来定义缺省的init运行的级别

si 是系统初始化的进程

ln 其中的n从1~6,指明该进程可以使用的runlevel的级别

ud 是升级进程

ca 指明当按下Ctrl+Alt+Del是运行的进程

pf 指当UPS表明断电时运行的进程

pr 是在系统真正关闭之前,UPS发出电源恢复的信号时需要运行的进程

x 是将系统转入X终端时需要运行的进程

2.runlevels

系统运行级,即执行登记项的init级别。用于指定相应的登记项适用于哪一个运行级,即在哪一个运行级中被处理。如果该字段为空,那么相应的登记项将适用于所有的运行级。在该字段中,可以同时指定一个或多个运行级,其中各运行级分别以数字0, 1, 2, 3, 4, 5, 6或字母a, b, c表示,且无须对其进行分隔。

0-->Halt,关闭系统.

1-->单用户,在grub启动时加上为kernel加上参数single即可进入此运行等级

2-->无网络多用户模式.

3-->有网络多用户模式.

4-->有网络多用户模式.

5-->X模式

6-->reboot重启系统

S/s-->同运行等级1

a,b,c-->自定义等级,通常不使用.

3.action

表示进入对应的runlevel时,init应该运行process字段的命令的方式,有效的action值如下。

boot:只有在引导过程中,才执行该进程,但不等待该进程的结束。当该进程死亡时,也不重新启动该进程。

bootwait:只有在引导过程中,才执行该进程,并等待进程的结束。当该进程死亡时,也不重新启动该进程。实际上,只有在系统被引导后,并从单用户模式进入多用户模式时,这些登记项才被处理;如果系统的默认运行级设置为2(即多用户模式),那么这些登记项在系统引导后将马上被处理。

initdefault:指定系统的默认运行级。系统启动时,init将首先查找该登记项,如果存在,init将依据此决定系统最初要进入的运行级。具体来说,init将指定登记项"run_level"字段中的最大数字(即最高运行级)为当前系统的默认运行级;如果该字段为空,那么将其解释为"0123456",并以"6"作为默认运行级。如果不存在该登记项,那么init将要求用户在系统启动时指定一个最初的运行级。

off:如果相应的进程正在运行,那么就发出一个告警信号,等待20秒后,再通过关闭信号强行终止该进程。如果相应的进程并不存在,那么就忽略该登记项。

once:启动相应的进程,但不等待该进程结束便继续处理/etc/inittab文件中的下一个登记项;当该进程终止时,init也不重新启动该进程。在从一个运行级进入另一个运行级时,如果相应的进程仍然在运行,那么init就不重新启动该进程。

ondemand:与"respawn"的功能完全相同,但只用于运行级为a、b或c的登记项。

powerfail:只在init接收到电源失败信号时,才执行该进程,但不等待该进程结束。

powerwait:只在init接收到电源失败信号时,才执行该进程,并在继续对/etc/inittab文件进行任何处理前等待该进程结束。

respawn:如果相应的进程还不存在,那么init就启动该进程,同时不等待该进程的结束就继续扫描/etc/inittab文件;当该进程终止时,init将重新启动该进程。如果相应的进程已经存在,那么init将忽略该登记项并继续扫描/etc/inittab文件。

sysinit:只有在启动或重新启动系统并首先进入单用户模式时,init才执行这些登记项。而在系统从运行级1~6进入单用户模式时,init并不执行这些登记项。"action"字段为"sysinit"的登记项在"run_level"字段不指定任何运行级。

wait:启动进程并等待其结束,然后再处理/etc/inittab文件中的下一个登记项。

ctrlaltdel:用户在控制台键盘上按下Ctrl+Alt+Del组合键时,允许init重新启动系统。注意,如果该系统放在一个公共场所,系统管理员可将Ctrl+Alt+Del组合键配置为其他行为,比如忽略等。

4.process

具体应该执行的命令。并负责在退出运行级时将其终止(当然在进入的runlevel中仍要运行的程序除外)。当运行级别改变,并且正在运行的程序并没有在新的运行级别中指定需要运行时,那么init会先发送一个SIGTERM 信号终止,然后是SIGKILL。

5.实例分析:

/*************************/etc/inittab***********************************/

//将系统切换到 initdefault 操作所定义的运行级别即运行级别5。我们可以将运行级别看作是系统的状态。运行级别 0 定义了系统挂起状态,运行级别 1 是单用户模式。运行级别 2 到 5 是多用户状态,运行级别 6 表示重启

id:5:initdefault:

//sysinit表示在进行其他工作之前先完成系统初始化.init在处理其它运行等级的脚本之前,首先会执行这一行.是系统的初始化进程.用于设置主机名,挂载文件系统,启动交换分区等.

//rcS脚本会调用/etc/rcS.d目录下的所有脚本进行初始化

si::sysinit:/etc/init.d/rcS //在运行boot或bootwait进程之前运行系统初始化的进程

//下条语句可以让系统在重新启动、进入单用户模式的时候提示输入超级用户密码。

//S同运行等级1,并等待其结束,然后再处理/etc/inittab文件中的下一个登记项。

~~:S:wait:/sbin/sulogin

//当运行级别为5时,以5为参数运行/etc/rc5.d下的脚本,init将等待其返回(wait)

//rc.sysinit,rcS,rc这些都是shell的脚本,完成大量的系统初始化的工作。

//主要工作包括:激活交换分区,检查磁盘,加载硬件模块以及其它一些需要优先执行任务。

//执行rc脚本,传入参数为0-6,即会调用/etc/rc0.d-rc6.d目录下的所有文件

//initdefault 指定默认的 init 级别是 5(多用户模式)。在定义初始的运行级别之后,则调用rc脚本以及参数5(运行级别)来启动系统,即rc脚本(参数5)会调用/etc/rc5.d下的所有脚本。

l0:0:wait:/etc/init.d/rc
0 //使用级别0运行此程序

l1:1:wait:/etc/init.d/rc
1

l2:2:wait:/etc/init.d/rc
2

l3:3:wait:/etc/init.d/rc
3

l4:4:wait:/etc/init.d/rc
4

//会运行该/etc/rc5.d下的3个脚本:

//S10telnetd脚本:开启telnetd服务 start-stop-daemon --start --quiet --exec
$telnetd

//S20syslog脚本:开启syslog服务start-stop-daemon -S -b -n
syslogd -a /sbin/syslogd -- -n
$SYSLOG_ARGS start-stop-daemon -S -b -n
klogd -a /sbin/klogd -- -n

//S99rmnologin脚本:删除/etc/nologin文件
rm -f /etc/nologin /etc/nologin.boot

l5:5:wait:/etc/init.d/rc
5

l6:6:wait:/etc/init.d/rc
6

z6:6:respawn:/sbin/sulogin//脚本运行等级为6时才执行

//在2、3、4、5级别上以ttyX为参数执行/sbin/mingetty程序,打开ttyX终端用于用户登录,

//如果进程退出则再次运行mingetty程序(respawn),所以登录出错时,接着登录

//缺省波特率是115200

S2:2345:respawn:/sbin/mingetty
ttyS2 //修改了mingetty和login程序,系统就可以在自动登录了

/*************************etc/init.d/rcS***********************************/

//设置PATH,runlevel,prevlevel环境变量,并export

PATH=/sbin:/bin:/usr/sbin:/usr/bin

runlevel=S

prevlevel=N

umask 022 //缺省的文件权限

export PATH runlevel prevlevel

if [ -x /sbin/unconfigured.sh ]//检查/sbin/unconfigured.sh是否可执行

then

/sbin/unconfigured.sh//如果可执行就执行unconfigured.sh,我的根文件系统不存在此文件

fi

//执行default目录下的rcS,设置一些变量

//即source /etc/default/rcS

. /etc/default/rcS

//trap可以使你在脚本中捕捉信号。该命令的一般形式为:trap name signal(s)

//name是捕捉到信号以后所采取的一系列操作。实际应用中, name一般是一个专门来处理所捕捉信号的函数。

//name需要用双引号( “ ” )引起来。signal就是待捕捉的信号。

//这里就是捕捉INT QUIT TSTP三个信号,执行“:”,实际就是忽略这三个信号,防止脚本执行时使用ctrl-C 就退出脚本

trap ":" INT QUIT TSTP

//将执行/etc/init.d中rc,传入参数为“S”,目的就是为了执行/etc/init.d/rcS.d目录下的所有脚本文件,都是连接到/etc/init.d/目录下的链接

//S02banner脚本建立tty设备节点:/bin/mknod -m
0666 /dev/tty c 5 0

//S03sysfs脚本挂载proc和sysfs文件系统:mount -t proc proc /proc
mount sysfs /sys -t sysfs

//S03udev脚本:开启udev服务,后台运行udevd程序:/sbin/udevd -d

//S06alignment脚本,输出cpu信息到proc文件系统

//S10checkroot脚本

//S20modutils.sh脚本:insmod module

//S30ramdisk脚本:

//S35mountall.sh脚本:挂载Mount all filesystems

//S37populate-volatile.sh脚本

//S38devpts.sh脚本:挂载mount -t
devpts devpts /dev/pts

//S39hostname.sh脚本:输出主机名称(arago)写入/etc/hostname文件:hostname -F /etc/hostname

//S40networking脚本:开启网络服务

//S45mountnfs.sh脚本:挂载nfs

//S55bootmisc.sh脚本:

//S98configure脚本:opkg-cl configure

//S99finish.sh脚本:结束脚本

exec /etc/init.d/rc
S

//若rc.boot是目录,则执行rc.boot所有的脚本程序

[ -d /etc/rc.boot ] && run-parts /etc/rc.boot

//若setup.sh可执行,就执行,没有此程序

if [ -x /sbin/setup.sh ]

then

/sbin/setup.sh

fi

/*************************etc/init.d/rc***********************************/

//这个脚本作用主要是运行/etc/rcS.d目录下的文件,

//其中在 /etc/rcS.d/ 的目录下有一个
README 文本来说明该 /etc/rcS.d/ 目录下脚本的作用:

//即 /etc/rcS.d/ 中是一些到 /etc/init.d/ 中脚本的符号连接。

//执行完 /etc/rcS.d/ 中的脚本后,触发相应的
runlevel 事件,开始运行 /etc/rc.conf 脚本

. /etc/default/rcS

export VERBOSE //etc/default/rcS这个脚本中定义的VERBOSE=no

startup_progress() {

step=$(($step + $step_change))

if [ "$num_steps" != "0" ]; then

progress=$((($step * $progress_size / $num_steps) + $first_step))

else

progress=$progress_size

fi

if type psplash-write >/dev/null 2>&1; then

TMPDIR=/mnt/.psplash
psplash-write "PROGRESS $progress" || true

fi

}

startup() {

[ "$VERBOSE" = very ] && echo "INIT:
Running $@..."//VERBOSE=no,所以后边的不打印

//以.sh结尾的脚本是必须执行的脚本,不是以.sh结尾的脚本服务是可以开启或关闭的,通过start或stop参数

case "$1" in//传入的第一个参数是要执行的文件名,第二个参数是start

*.sh)

(//若文件名是以.sh结尾的则执行这个脚本

trap - INT QUIT TSTP

scriptname=$1

shift

. $scriptname //执行这个脚本,不带参数

)

;;

*)//若不是以.sh结尾的

/*实际上rc进程调用的脚本都称为初始化脚本。每个在/etc/init.d下的脚本都可以在执行时带上以下参数,如:start、stop、restart、pause、zap、status、ineed、iuse、needsme、usesme或者broken。

要启动、停止或者重启一个服务(和所有依赖于它的服务),应该用参数start、stop和restart。*/

"$@"//执行这个脚本带参数,比如传入的是$@=“/etc/rcS.d/S02banner
start”,即带start参数执行这个脚本,这样可以灵活的控制服务的start或者stop

;;

esac

startup_progress

}

//忽略这三个信号,防止脚本执行时使用ctrl-C 就退出脚本

trap ":" INT QUIT TSTP

//stty用于设置终端特性。在命令行中设置一个stty选项,一般格式为:stty name character

//以下将退格设置为^ H:stty erase '\^H',即ctrl+H在此脚本中是退格键

//设置终端,将 CR 字符映射为 NL 字符,避免阶梯效应

stty onlcr 0>&1

//Now find out what the current and what
the previous runlevel are.

runlevel=$RUNLEVEL

//得到第一个参数是“S”,表示等级1,得到当前运行等级1,runlevel=S

[ "$1" != "" ] && runlevel=$1

if [ "$runlevel" = "" ]//运行等级为空的话,则退出

then

echo "Usage: $0 <runlevel>" >&2

exit 1

fi

previous=$PREVLEVEL

[ "$previous" = "" ] && previous=N

//传入参数是S的话,则$runleve=S $previous=N

export runlevel previous

//若$runlevel=“S”,即检查rcS.d是否为目录。

if [ -d /etc/rc$runlevel.d ]

then

//rcS.d是目录

PROGRESS_STATE=0

//Split the remaining portion of the progress bar into thirds

progress_size=$(((100 - $PROGRESS_STATE) / 3))//progress_size = 100/3 =33

case "$runlevel" in//runlevel=S

0|6)

first_step=-100

progress_size=100

step_change=1

;;

S)

//Begin where the initramfs left off and use
2/3of the remaining space

first_step=$PROGRESS_STATE ///progress_size = 100/3 =33

progress_size=$(($progress_size * 2))//progress_size=66

step_change=1

;;

*)

//Begin where rcS left off and use
the final 1/3 ofthe space (by leaving progress_size unchanged)

first_step=$(($progress_size * 2 + $PROGRESS_STATE))

step_change=1

;;

esac

num_steps=0

for s in /etc/rc$runlevel.d/[SK]*; //s取/etc/rcS.d目录下以S或K开头的文件名

do

//这句话的含义去掉变量s中所有的/etc/rcS.d/S??的部分

//例:s=/etc/rc$runlevel.d/S10checkroot,那么去掉/etc/rc$runlevel.d/K??部分后,s为checkroot

case "${s##/etc/rc$runlevel.d/S??}" in

gdm|xdm|kdm|reboot|halt)//若s剩下的文件名中为这五个则跳出for语句

break

;;

esac

num_steps=$(($num_steps + 1))//num_steps递加,表示查找到此目录下/etc/rcS.d有多少个脚本

done//for语句结束

step=0

//首先运行KILL脚本

if [ $previous != N ]//由于$previous=N,所以以下不执行

then

for i in /etc/rc$runlevel.d/K[0-9][0-9]*//取以K开头的文件名

do

//检查是否为常规文件

[ ! -f $i ] && continue

//Stop the service.

startup $i stop

done

fi

//然后运行这个级别的START脚本

for i in /etc/rc$runlevel.d/S*//取得S开头的脚本

do

[ ! -f $i ] && continue//检查是否为常规文件,不是则进行下次循环

if [ $previous != N ] && [ $previous != S ]//由于$previous=N,所以此if语句不执行

then

//Find start script in previous runlevel and stop
script in this runlevel.

suffix=${i#/etc/rc$runlevel.d/S[0-9][0-9]}//获得i文件名的后缀,假如是S10checkroot,则suffix=checkroot

stop=/etc/rc$runlevel.d/K[0-9][0-9]$suffix //得到stop文件名,假如/etc/rc$runlevel.d/K[0-9][0-9]checkroot

previous_start=/etc/rc$previous.d/S[0-9][0-9]$suffix

//如果有起始脚本,并且没有停止脚本,则不进行这项服务,continue继续下一次循环

[ -f $previous_start ] && [ ! -f
$stop ] && continue

fi

case "$runlevel" in//runlevel = S

0|6)

startup $i stop

;;

*)

startup $i start//调用start函数,参数就是脚本名称

;;

esac

done//for循环结束

fi

/*************************etc/profile***********************************/

//为启动shell设定一些环境变量

PATH="/usr/local/bin:/usr/bin:/bin"

EDITOR="/bin/vi" //needed for packages
like cron

test -z "$TERM" && TERM="vt100" //Basic
terminal capab. For screen etc.

//若此文件存在,则设置时区

if [ ! -e /etc/localtime ]; then

TZ="UTC"

export TZ

fi

//显示用户ID是否为0,是0则设置用户的PATH路径

if [ "`id -u`" -eq
0 ]; then

PATH=$PATH:/usr/local/sbin:/usr/sbin:/sbin:

fi

//设置环境变量PS1

if [ "$PS1" ]; then

PS1='\u@\h:\w\$ '

fi

// /etc/profile.d是否为目录

if [ -d /etc/profile.d ]; then

for i in /etc/profile.d/*.sh /*遍历目录下所有以.sh结尾的脚本文件,并执行*/

do

if [ -r $i ]; then

. $i

fi

done

unset i

fi

//可以设置登录后自动运行的APP

. /etc/init.d/autorun-f

export PATH PS1 OPIEDIR QPEDIR QTDIR EDITOR TERM

umask 022

/*********************************************************mingetty.c**********************************************/

/* name of this program (argv[0]) */

static char *progname;

/* on which tty line are we sitting? (e.g. tty1) */

static char *tty;

/* some information about this host */

static struct utsname uts;

/* the hostname */

static char hn[MAXHOSTNAMELEN + 1];

/* process and session ID of this program */

static pid_t pid, sid;

/* login program invoked */

static char *loginprog = "/bin/login";

/* Do not send
a reset string to the terminal. */

static int noclear = 0;

/* Do not print
a newline. */

static int nonewline = 0;

/* Do not print /etc/issue. */

static int noissue = 0;

/* Do not call vhangup() on the
tty. */

static int nohangup = 0;

/* Do not print
any hostname. */

static int nohostname = 0;

/* Print the whole string of gethostname() instead
of just until the next "." */

static int longhostname = 0;

/* time to wait, seconds */

static int delay = 0;

/* chroot directory */

static char *ch_root = NULL;

/* working directory to change into */

static char *ch_dir = NULL;

/* 'nice' level
of the program */

static int priority = 0;

/* automatic login with this user */

static char *autologin = NULL;

/* update_utmp() - update
our utmp entry */

static void update_utmp (void)

{

struct utmp ut;

struct utmp *utp;

time_t cur_time;

setutent ();

while ((utp = getutent ()))

if (utp->ut_type == INIT_PROCESS && utp->ut_pid == pid)

break;

if (utp) {

memcpy (&ut, utp, sizeof (ut));

} else {

/* some inits don't initialize utmp... */

const char *x = tty;

memset (&ut, 0, sizeof (ut));

if (strncmp (x, "tty", 3) == 0)

x += 3;

if (strlen (x) > sizeof (ut.ut_id))

x += strlen (x) - sizeof (ut.ut_id);

strncpy (ut.ut_id, x, sizeof (ut.ut_id));

}

strncpy (ut.ut_user, "LOGIN", sizeof (ut.ut_user));

strncpy (ut.ut_line, tty, sizeof (ut.ut_line));

time (&cur_time);

ut.ut_time = cur_time;

ut.ut_type = LOGIN_PROCESS;

ut.ut_pid = pid;

ut.ut_session = sid;

pututline (&ut);

endutent ();

updwtmp (_PATH_WTMP, &ut);

}

/* open_tty - set up
tty as standard { input, output, error } */

static void open_tty (void)

{

struct sigaction sa, sa_old;

char buf[40];

int fd;

//得到要打开的tty终端名

if (tty[0] == '/')

strcpy (buf, tty);

else {

strcpy (buf, "/dev/");

strcat (buf, tty);

}

//修改设备文件属性,使其可以访问

if (chown (buf, 0, 0) || chmod (buf, 0600))

if (errno != EROFS)

error ("%s: %s", tty, strerror (errno));

sa.sa_handler = SIG_IGN;

sa.sa_flags = 0;

sigemptyset (&sa.sa_mask);

sigaction (SIGHUP, &sa, &sa_old);//终端关闭发出SIGHUP信号,忽略此信号

//打开tty终端设备,缺省波特率是115200

if ((fd = open (buf, O_RDWR, 0)) < 0)

error ("%s: cannot open tty: %s", tty, strerror (errno));

if (ioctl (fd, TIOCSCTTY, (void *) 1) == -1)

error ("%s: no controlling tty: %s", tty, strerror (errno));

if (!isatty (fd))

error ("%s: not a tty", tty);

//

if (nohangup == 0) {

if (vhangup ())

error ("%s: vhangup() failed", tty);

close (2);

close (1);

close (0);

close (fd);

if ((fd = open (buf, O_RDWR, 0)) != 0)

error ("%s: cannot open tty: %s", tty,strerror (errno));

if (ioctl (fd, TIOCSCTTY, (void *) 1) == -1)

error ("%s: no controlling tty: %s", tty,strerror (errno));

}

//将标准输入输出出错都复制给tty终端

if (dup2 (fd, 0) != 0 || dup2 (fd, 1) != 1 || dup2 (fd, 2) != 2)

error ("%s: dup2(): %s", tty, strerror (errno));

if (fd > 2)

close (fd);

if (noclear == 0)

write (0, "\033c", 2);

//恢复原来SIGHUP的信号处理

sigaction (SIGHUP, &sa_old, NULL);

}

static void output_special_char (unsigned char c)

{

switch (c) {

case 's':

printf ("%s", uts.sysname);

break;

case 'n':

printf ("%s", uts.nodename);

break;

case 'r':

printf ("%s", uts.release);

break;

case 'v':

printf ("%s", uts.version);

break;

case 'm':

printf ("%s", uts.machine);

break;

case 'o':

printf ("%s", uts.domainname);

break;

case 'd':

case 't':

{

time_t cur_time;

struct tm *tm;

#if 0

char buff[20];

time (&cur_time);

tm = localtime (&cur_time);

strftime (buff, sizeof (buff),

c == 'd'? "%a
%b %d %Y" : "%X", tm);

fputs (buff, stdout);

break;

#else

time (&cur_time);

tm = localtime (&cur_time);

if (c == 'd') /* ISO
8601 */

printf ("%d-%02d-%02d", 1900 + tm->tm_year,tm->tm_mon + 1, tm->tm_mday);

else

printf ("%02d:%02d:%02d", tm->tm_hour,tm->tm_min, tm->tm_sec);

break;

#endif

}

case 'l':

printf ("%s", tty);

break;

case 'u':

case 'U':

{

int users = 0;

struct utmp *ut;

setutent ();

while ((ut = getutent ()))

if (ut->ut_type == USER_PROCESS)

users++;

endutent ();

printf ("%d", users);

if (c == 'U')

printf (" user%s", users == 1 ? "" : "s");

break;

}

default:

putchar (c);

}

}

static void do_prompt (int showlogin)

{

FILE *fd;

int c;

if (nonewline == 0)

putchar ('\n');

if (noissue == 0 && (fd = fopen ("/etc/issue", "r"))) {//存有Arago图案,打印此登录图案

while ((c = getc (fd)) != EOF) {

if (c == '\\')

output_special_char (getc (fd));

else

putchar (c);

}

fclose (fd);

}

if (nohostname == 0)

printf ("%s ", hn);

if (showlogin)

printf ("login: ");

fflush (stdout);

}

static char *get_logname (void)

{

static char logname[40];

char *bp;

unsigned char c;

tcflush (0, TCIFLUSH); /* flush
pending input */

for (*logname = 0; *logname == 0;) {

do_prompt (1);//打印登录图案,argo:

for (bp = logname;;) {

if (read (0, &c, 1) < 1) {//从标准输入里读入一个字符

if (errno == EINTR || errno == EIO|| errno == ENOENT)

exit (EXIT_SUCCESS);

error ("%s: read: %s", tty, strerror (errno));

}

if (c == '\n' || c == '\r') {//回车或换行符号表示结束

*bp = 0;

break;

} else if (!isprint (c))

error ("%s: invalid character 0x%x in login""
name", tty, c);

else if ((size_t)(bp - logname) >= sizeof (logname) - 1)

error ("%s: too long login name", tty);

else

*bp++ = c;

}

}

return logname;

}

static struct option const long_options[] = {

{ "autologin", required_argument, NULL, 'a' },

{ "chdir", required_argument, NULL, 'w' },

{ "chroot", required_argument, NULL, 'r' },

{ "delay", required_argument, NULL, 'd' },

{ "noclear", no_argument, &noclear, 1 },

{ "nonewline", no_argument, &nonewline, 1 },

{ "noissue", no_argument, &noissue, 1 },

{ "nohangup", no_argument, &nohangup, 1 },

{ "no-hostname", no_argument, &nohostname, 1 }, /* compat option */

{ "nohostname", no_argument, &nohostname, 1 },

{ "loginprog", required_argument, NULL, 'l' },

{ "long-hostname", no_argument, &longhostname, 1 },

{ "nice", required_argument, NULL, 'n' },

{ 0, 0, 0, 0 }

};

int main (int argc, char **argv)

{

char *logname, *s;

int c;

progname = argv[0];//程序名称,即mingetty

if (!progname)

progname = "mingetty";

/*struct utsname

{

char sysname[_UTSNAME_SYSNAME_LENGTH];//当前操作系统名

char nodename[_UTSNAME_NODENAME_LENGTH];//网络上的名称

char release[_UTSNAME_RELEASE_LENGTH];//当前发布级别

char version[_UTSNAME_VERSION_LENGTH];//当前发布版本

char machine[_UTSNAME_MACHINE_LENGTH];//当前硬件体系类型

char domainname[_UTSNAME_DOMAIN_LENGTH]; //当前域名

};*/

uname (&uts);//获取当前内核名称和其它信息,并存于utsname结构中

gethostname (hn, MAXHOSTNAMELEN);//本地主机的标准主机名,存到数组hn中

hn[MAXHOSTNAMELEN] = '\0';

pid = getpid ();//取得进程ID

sid = getsid (0);//get the
process group ID of session leader

putenv ("TERM=linux");//把字符串加到当前环境中,设置的环境仅对程序本身有效

//解析命令行选项参数。

//字符串optstring可以下列元素:

//单个字符,表示选项,

//单个字符后接一个冒号:表示该选项后必须跟一个参数。参数紧跟在选项后或者以空格隔开。该参数的指针赋给optarg。

//单个字符后跟两个冒号,表示该选项后可以有参数也可以没有参数。如果有参数,参数必须紧跟在选项后不能以空格隔开。该参数的指针赋给optarg。(这个特性是GNU的扩张)。

while ((c = getopt_long (argc, argv, "a:d:l:n:w:r:", long_options,(int *) 0)) != EOF) {

switch (c) {

case 0:

break;

case 'a':

autologin = optarg;//获得参数-a后面的参数

break;

case 'd':

delay = atoi (optarg);

break;

case 'l':

loginprog = optarg;

break;

case 'n':

priority = atoi (optarg);

break;

case 'r':

ch_root = optarg;

break;

case 'w':

ch_dir = optarg;

break;

default:

usage ();

}

}

//s指针保存主机名

if (longhostname == 0 && (s = strchr (hn, '.')))

*s = '\0';

//获得终端结构,例如ttyS0等

tty = argv[optind];

if (!tty)

usage ();

//终端名添加"/dev/",变成/dev/ttyS0

if (strncmp (tty, "/dev/", 5) == 0)

tty += 5;

update_utmp ();//更新登录信息

if (delay)

sleep (delay);

open_tty ();//打开终端设备

if (autologin) { //如果前边参数为-a且-a后边带有登录名,则会设置autologin即自动登录

do_prompt (0);//打印登录图形

printf ("login: %s (automatic login)\n", autologin);

logname = autologin;//获得自动登录名

} else//不是自动登录

while ((logname = get_logname ()) == 0);//获得登录名

if (ch_root)

chroot (ch_root);

if (ch_dir)

chdir (ch_dir);

if (priority)

nice (priority);

//带着登录名参数,执行loginprog=/bin/login程序登录,即login--logname

execl (loginprog, loginprog, autologin? "-f" : "--", logname, NULL);

/*login函数片段

。。。。。。。

strcpy(buff, "exec ");

strcat(buff, pwd->pw_shell);

childArgv[childArgc++] = "/bin/sh";

childArgv[childArgc++] = "-sh";

childArgv[childArgc++] = "-c";

childArgv[childArgc++] = buff;

childArgv[childArgc++] = NULL;

//登录成功,执行/bin/sh进入shell

execvp(childArgv[0], childArgv + 1);

*/

//启动shell后,首先启动 /etc/profile
文件,然后再启动用户目录下的 ~/.bash_profile、 ~/.bash_login或 ~/.profile文件中的其中一个,执行的顺序为:~/.bash_profile、 ~/.bash_login、 ~/.profile。如果 ~/.bash_profile文件存在的话,一般还会执行 ~/.bashrc文件。

error ("%s: can't exec %s: %s", tty, loginprog, strerror (errno));

sleep (5);

exit (EXIT_FAILURE);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: