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 " >&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);
}