1.项目整体分析
制作Linux发布的目的是为了在系统中能够快速,正确地建立Linux系统环境。制作Linux发布的主要工作是决定各种软件的去留,因为有了 RPM(RedHat Package Manager)包对其提供优良的管理能力,所以以目前比较成熟的RedHat Linux发布程序作为蓝本,以RPM包作为基本的制定单元,以需求为原则对其进行取舍,得到适合实际需要的Linux系统。
由此,项目自然而然的以分析RedHat Linux的光盘安装系统为起点,在掌握了其结构和行为的基础上,在包一级(结构部分)和代码一级(行为部分)进行修改,同时建立相应的测试环境,以便对修改进行及时的规范。
2.项目分步骤实施细节
2.1对Linux光盘安装系统的分析
A 结构部分
在安装光盘中,主要的目录结构和文件大致如下:
images/ 此目录下包含了制作启动盘的映像文件(文件后缀img),
其中boot.img是当安装介质为CD-ROM时负责引导系统的映像文件
bootnet.img是当安装介质为FTP,NFS等时负责引导系统的映像文件
driver.img是由一些特殊设备驱动程序模块组成的映像文件,在当前内核不支持这些设备的情况下,提供了对它们进行访问的一种方法
其中,boot.img映像文件中主要包含以下文件:
boot.img
|----vmlinuz Linux内核
|----ldlinux.sys 引导Linux的系统文件
|----syslinux.cfg Linux内核引导参数配置文件
|----initrd.img 内存虚拟文件系统映像文件
|----*.msg文件 引导时的各种提示信息文件
其中,initrd.img为Linux ext2文件系统,构成如下:
initrd.img
|----/bin
|----/dev
|----/etc
|----/module
|----/sbin ------ loader
安装程序装载器
|----/tmp
|----/var
可执行文件/sbin/loader的任务是判断安装介质的有效性,并从中执行安装程序。
其实正是boot.img,在系统启动时被执行,经解析之后在内存建立起了Linux内核,并根据配置文件syslinux.cfg装载虚拟文件系统,形成了完整的Linux System,为后续的工作提供了必要的操作系统环境。Boot.img映像的文件系统类型为msdos,而其中的initrd.img映像的文件系统类型必为Linux系统自己的ext2,所以对于它们的解析操作是不同的,具体请参考附录A 。
RedHat/ 此目录是RedHat Linux发布的核心目录,主要的目录结构都在这里,其中
RPMS/ 包含了RedHat Linux发布的主要部分,即以RPM包的形式将Linux系统中的二进制可执行文件,配置文件,文档等等组织在一起,形成能完成一定功能的比较独立的软件包(文件后缀rpm)。这个目录就是把这些软件包都集合在一起,形成了RedHat Linux发布。
base/ 包含了在安装过程中要用到的描述组织结构和安装行为的所有文件,其中comps,hdlist和hdlist2是描述RPM包组织结构的文件。
comps 此文件把各个RPM包按一定的原则组织成若干组,即components,这样在安装过程中就不必对每一个包做出取舍,而以组为单位。comps文件为简单文本格式,它的结构如下所示:
4 表示RPM包的版本号,当前为4
1 base { }
base是此component名,{…}中是此component中所包含的RPM包
的名称列表,1表示在安装中默认为选中,即默认安装。
0 –hide IDS sensor{
snort
libpcap
}
表示IDS sensor组中包含有snort和lipcap这两个RPM包。0表示
这个组在安装中默认为不选中即默认不安装,并且由—hide指出
不在用户界面上显示此组。
hdlist和hdlist2 这两个文件维护从RPM包名到真实包文件名的映射过程,例如从snort这个RPM包名到真实包文件名snort-1.8.1- 1.1.2.i386.rpm的映射。这两个文件是用特殊的程序生成的,无法用简单的方法察看其中的内容和结构。具体的生成方法请参考附录D。
stage2.img , hdstg1.img , hdstg2.img , netstg1.img 和netstg2.img 是描述安装行为的映像文件,其中
stage2.img 是当安装介质为CD-ROM时的安装程序映像文件
hdstg1.img 是当安装介质为HardDisk时的安装程序映像文件
hdstg2.img 是当安装介质为HardDisk时的安装程序映像文件
netstg1.img 是当安装介质为FTP,NFS时的安装程序映像文件
netstg2.img 是当安装介质为FTP,NFS时的安装程序映像文件
这里主要讨论stage2.img的内容
stage2.img
|----/etc
|----/modules
|----/proc
|----/usr----/bin----anaconda
安装程序主执行文件
|
|------/lib-----/anaconda
安装程序脚本文件目录
| |----/installclasses
| |----/iw
| |----/texttw
| |----*.py
|
|------/share---/anaconda
安装程序资源文件目录
| |----/help
| |----/pixmaps
如上所示,stage2.img映像文件中的主要部分是安装程序anaconda,它的主执行体是/usr/bin下的anaconda,由其调用的大量例程分布在/usr/lib/anaconda下,而安装过程中要用到的资源文件分布在/usr/share/anaconda下。 stage2.img 的解析方法请参考附录B。
B 行为部分
RedHat 7.1的安装程序被命名为anaconda。如前所述,当boot.img所代表的启动介质被系统引导之后,在内存中就建立了一个完整的Linux系统(包括Linux内核和一个内存虚拟文件系统),之后便执行文件系统中存在的loader命令,从适当的介质中执行安装程序(例:安装介质是CD- ROM,就解析CD-ROM上的stage2.img,并从中执行安装程序),即执行anaconda,完成Linux系统的安装任务。
此次利用RedHat 7.1的安装程序源代码的SRPM包形式:anaconda-7.1-5.src.rpm来获得anaconda的源程序,经解包后在/usr/src/redhat/SOURCES/anaconda-7.1形成了源代码树。
anaconda-7.1
|-------------------/bootdisk
启动盘目录
|-------------------/docs
文档目录
|-------------------/help
安装过程帮助系统目录
|-------------------/installclasses
安装类型分类目录
|-------------------/iw
安装各步骤响应目录
|-------------------/loader
安装程序装载器目录
|-------------------/pixmap
图形资源目录
|-------------------/utils
工具目录
|-------------------*.py
各Python脚本文件
分析如下:
anaconda安装程序主要用Python语言写成,它是一种解释性的,面向对象的脚本语言。源文件后缀为.py,也可生成可执行的字节码,后缀为.pyc或.pyo。其中:
installclasses/ 子目录中各文件定义了在安装过程中用户可选择的安装类型,通常由四个文件workstation.py , server.py , laptop.py和custom.py来描述workstation(工作站)安装类型,server(服务器)安装类型,laptop(膝上型电脑) 安装类型和custom(自定义)安装类型。每个脚本文件的内部,则是根据自己安装类型的特点对安装步骤,分区策略以及包的取舍做出了不同的方案。
iw/ 子目录中各文件定义了在图形界面安装状态时各步骤对Next(下一步)和Prev(上一步)的响应函数。
loader/ 安装程序装载器的源代码目录,用C语言写成。
pixmap/ 图形资源目录,包括安装过程中使用到的所有位图,图标。
utils/ 安装程序实用工具目录。
anaconda 是安装程序的主执行文件,它建立了Python语言的运行环境,提供了程序的入口点并以模块的方式将各个子系统结合在一起。
gui.py 定义了安装程序图形界面使用的各种窗口类,包括MessageWindow,ProgressWindow,WaitWindow, ExeceptWindow等,和控制这些窗口及图形界面行为的InstallInterface, InstallControlWindow, InstallControlState类。总之,控制gui。
todo.py 定义了安装程序的各种行为函数,它是图形界面背后真正进行各项操作的函数集合。
harddrive.py 定义了当安装介质为硬盘时,系统该如何找到安装程序的光盘映像,并从中执行程序。
安装程序源代码的编译由make和make install组成,完成后在/usr/src/RedHat目录下形成了如下目录结构:
instimage
|------/etc
|------/usr
|------/bin
|------/sbin
|------/lib
| |------/anaconda
| | |------installclasses
| | |------iw
| | |------texttw
| | |------*.py
| |
| |------/anaconda-runtime
| |------/boot/loader
|
|------/share------/anaconda
|------/help
|------/pixmaps
此目录结构基本与stage2.img的文件结构相同。
2.2调试环境的建立:
C 对源程序的修改
在分析完安装程序的基本构成之后,就要建立相应的调试环境。建立此环境的目的是为了可以方便地对修改过的安装程序及裁减后的RPM包进行随时的确认。显然,可以选用CD-ROM或本地硬盘作为调试介质,下表比较了两者的差别:
CDROM 硬盘
对应的安装界面 图形界面 菜单界面
对应的映像文件 stage2.img *.iso中的hdstg1.img , hdstg2.img
优点 图形界面,直接使用映像文件stage2.img 随改随调,调试周期短,效率高
缺点 每次改动都要求刻盘,调试效率低 菜单界面,每次调试都要求提供光盘映像文件*.iso,效率上打折扣
在两者各有优缺点的情况下,考虑折衷的方案,即为了首先保证调试的效率,采用硬盘作为调试介质,但对应的映像文件选取stage2.img,这样能达到效率最大化,同时调试界面采取图形方式。采用此方案时,须修改源代码,以达到预期的效果。
从前面对安装系统的分析,可以看出在initrd.img中的/sbin/loader程序负责判断安装介质的有效性,并从中执行安装程序。所以要首先修改它的源代码文件loader.c,从中找出硬盘安装时默认读出光盘映像文件*.iso的函数setupIsoImages,并注释掉其中在硬盘目录中寻找映像文件*.iso的相关操作,具体对应Line 582 至 Line590行中包含sprintf和if(){}循环的语句,以避免打开子目录,并在其后加入mountLoopback ("/tmp/hdimage/RedHat/base/stage2.img","/mnt/runtime", "loop0");一句以便实现直接使用stage2.img的目的,并注释掉其后从errno=0开始的代码,经过整个while循环到 closedir(dir),但保留umount(“/tmp/hdimage”);注释掉if(!net) return NULL;一句。以上操作目的是防止程序读出光盘映像文件*.iso。在loader.c的主函数main()中的结尾部分,注释掉if (!FL_TESTING(flags)) { 和 }的条件判断的两条语句,让程序毫无疑问地执行硬盘上的安装程序。至此,对loader.c修改完毕。
同时还要对Python脚本的一些相关文件进行修改以保证对stage2.img文件的支持。具体的,在harddrive.py的类 class HardDriveInstallMethod中,注释掉函数 mountMedia(self, cdNum)中的所有内容并加Pass语句的方法使此函数失效,同样方法处理umountMedia函数,mountDirectory函数和 umountDirectory函数,为了保险起见,在其他函数中注释掉有关上面函数的调用。并在类的构造函数(初始化)中的# Go ahead…语句之前加self.tree=”/tmp/hdimage/”语句,并注释掉后面的所有语句。这样做仍然是要保证废弃iso映像转而对 stage2.img实现控制。不仅如此,最好还注释掉todo.py中的Line1781至Line1783调用 self.method.systemMounted一段,以确保不出差错。接着进行make和make install,重新编译程序,使修改生效,并把新的loader程序从编译的目标目录中copy到boot.img中initrd.img中的相应目录并覆盖旧的loader文件。为了启动时的快速,修改boot.img中的syslinux.cfg文件,去掉启动提示,延时和其他Linux启动选项,修改后的syslinux.cfg文件,请参考附录F
最后,把boot.img做成启动盘,方法请参考附录G。
D 建立硬盘介质中的调试目录
在硬盘的Linux分区中建立形如RedHat安装光盘目录结构的调试目录及相关文件,如下所示:
|----/images
| |------boot.img
|
|----/RedHat
|----/base
|
|------comps , hdlist , hdlist2,stage2.img
|
|----/RPMS
|
|----*.rpm
建立这种目录结构和相关文件的原因是在安装程序中已经以代码的形式确定了它们的命名及结构。其中,对boot.img和stage2.img的相关修改如前所述,而涉及到对comps,hdlist,hdlist2的修改,则需在后续的裁剪过程中确定。
至此,调试环境建立完毕。现在可以用做好的启动盘来引导系统,并且可以从指定的硬盘上测试安装程序和RPM包的正确性。
2.3对安装步骤的简化
在对RPM 包的剪裁进行之前,还要对原有的安装步骤做出简化,去掉一些与系统需求大致无关的项目,使安装者可以集中精力地配置Sensor的主要参数,忽略诸如对键盘,鼠标,和多国语言的配置。具体的如下所示:
原有的安装步骤有:
1.安装语言选择
2.键盘配置
3.鼠标配置
4.欢迎信息
5.安装类型选择(包括安装或升级,安装部分又包括workstation,server,laptop,custom四种类型)
6.选择分区方式(自动分区,手动分区,专业分区)
7.选择以上部分或全部分区格式化
8.Lilo操作系统引导器配置
9.网卡及网络配置
10.防火墙配置
11.语言配置
12.时区配置
13.账户配置
14.认证配置
15.包组及单RPM包选择
16.包独立性检查
17.X-Window配置
18.安装前确认
19.安装过程
20.制作启动盘
21.安装完毕确认
在这些安装步骤中很多都是在确定了RPM包组及调试完成后不必要存在的,所以去掉第1,2,3,4,7,8,10,11,14,16,17和第20项安装步骤,所有去掉步骤的相关设置都采取默认的设置值,如第7步采取分区全部格式化的方案,第8步,采取Lilo放置在MBR上,default boot 为Linux的设置等等,并修改第5步,去掉升级类型和安装类型中的所有4种既定类型,添加IDS sensor类型。修改完成以后的安装步骤如下所示:
1.安装类型选择(现有的为IDS sensor一种)
2.选择分区方式(自动分区,手动分区,专业分区)
3.网卡及网络配置
4.时区配置
5.账户配置
6.包组及单RPM包选择
7.安装过程
8.安装完毕确认
为此,需要修改Python脚本语言
... 在安装程序运行之初,需要先禁止掉安装语言选择,键盘配置,鼠标配置和欢迎信息,在anaconda中在判断语言是否有效之前即Line491 if lang:之前加上以下四句:
[aidcode]
instClass.addToSkipList("language");
instClass.addToSkipList("keyboard");
instClass.addToSkipList("mouse");
instClass.addToSkipList("welcome"); [/aidcode]
并在gui.py中的类class InstallInterface的run函数中注释掉Line371至Line371,即在commonSteps结构中除保留( InstallPathWindow, "installtype" )外,注释掉( LanguageWindow, "language" ),( KeyboardWindow, "keyboard" ),( MouseWindow, "mouse" )和( WelcomeWindow, "welcome" )
...对安装类型进行精简。在installclasses目录下去掉upgrateonly.py , workstation.py , server.py , laptop.py和custom.py即去掉升级类型和安装类型中的所有4种既定类型, 添加IDS sensor类型,即在目录中添加sensor.py文件,文件的具体内容请参考附录E。
除此之外,还要修改iw/installpath_gui.py脚本文件,以便在图形界面上不显示Install和Upgrate图标和选项,只显示IDS Sensor项目。具体的,注释掉Line223,Line 227,Line 233和Line 234代码,即在类class InstallPathWindow中的getScreen函数的最后部分不执行显示。
... 去掉其他多余步骤,主要修改iw/installpath_gui.py脚本文件。注释掉类class InstallPathWindow初始化函数__init__里结构self.installSteps中的( FormatWindow, "format" ) , ( FirewallWindow, "firewall" ) , ( LanguageSupportWindow, "languagesupport" ) , ( AuthWindow, "authentication" ) , ( UnresolvedDependenciesWindow, "dependencies" ) , ( XConfigWindow, "xconfig" ) , ( BootdiskWindow, "bootdisk" ),即对应着去掉的第7,10,11,14,16,17和第20步骤。
当这三步完成后,进行make和make install,重新编译程序,使修改生效,并把这些文件Copy至stage2.img中的相关位置,替换掉旧文件。这样就完成了对安装步骤的简化。
2.4对RPM 包的剪裁
对RPM包的裁剪依这次的需求进行。项目整体分析时已做出说明,制作目的是为了在系统前端Sensor 中能够快速,正确地建立运行Snort的Linux系统环境。所以只需保留Linux基本系统和运行snort所需的环境即可。
Linux基本系统的RPM包组成在comps文件中的base部分中有详细的描述,所以就以它为蓝本,去掉除base以外的所有component,并去掉base中以下不必要的RPM包:
[aidcode]
apmd
ash
autoconfig
dhcpcd
dosfstools
ed
eject
gdm
gettext
gpm gruff ksymoops lokkit mailcap mailx man mktemp mouseconfig ncurses openldap popt procmail pump raidtool readline redhat-logos redhat-release rootfiles sendmail syslinux utemper words [/aidcode]
在comps文件中增加IDS Sensor component并在此组中添加必要的RPM包,如snort-1.8.1就需要snort , libpcap , mysql , openssl , openssl-clients , perl等RPM包,除此之外,为了方便调试,也保留了一些用于操作和诊断的RPM包,如tcpdump , iputils , zip等,完成的comps请参考附录C。
同时,在/RPM目录中依照上面所有确认保留的RPM包名称,删除不予保留的各RPM文件(文件后缀.rpm)。此时在/base中的 comps文件和在/RPM中的各rpm文件都已剪裁完成,可以利用genhdlist生成hdlist和hdlist2文件了,genhdlist 在anaconda-7.1的源代码中的utils目录里。具体生成方法见附录D。
3. 存在的问题及今后目标
此次对NetCop Linux发布的制作是以RPM包作为裁剪单元进行的,所以必然存在很大缺陷,即对RPM包的内部毫无办法,无法去掉RPM包内部对需求无用的大量文件并且无法对内核,脚本文件等做出制定修改。所以,下一步所要做的工作,是逐一打开每一个候选的RPM包,依需求对包内的每一个文件做出修改或丢弃,这样才能做出真正适合sensor的Linux发布,无疑,工作量是巨大的。
4. 原始资料及参考文献
[aidcode]
Bootdisk-HOWTO
CDROM-HOWTO
CD-Writing-HOWTO
Distribution-HOWTO
HP-HOWTO
KickStart-HOWTO
Linux-From-Scratch-HOWTO
RedHat-CD-HOWTO
RPM-HOWTO
http://mail.y-min.or.jp/~nob/ml/linuxppc-jp-dev/199902/msg00150.html [/aidcode]
附录
附录A boot.img和initrd.img的解析过程
1.首先建立两个映像文件解析后的装载点(mount point):
[aidcode]
mkdir /mnt/boot /mnt/initrd [/aidcode]
2.写shell脚本进行解析和还原:
解析脚本命名为up
[aidcode]
#!/bin/sh
mount -o loop -t msdos boot.img /mnt/boot
gzip -cd /mnt/boot/initrd.img > /tmp/initrd.ext2
mount -o loop –t ext2 /tmp/initrd.ext2 /mnt/initrd [/aidcode]
还原脚本命名为down
[aidcode]
#!/bin/sh
umount /mnt/initrd
gzip -c9 /tmp/initrd.ext2 > /mnt/boot/initrd.img
umount /mnt/boot [/aidcode]
附录B stage2.img解析过程
1.首先建立映像文件解析后的装载点(mount point):
[aidcode]
mkdir /mnt/stage2 [/aidcode]
2.写shell脚本进行解析和还原:
解析脚本命名为up2
[aidcode]
#!/bin/sh
mount -o loop stage2.img /mnt/stage2 [/aidcode]
还原脚本命名为down2
[aidcode]
#!/bin/sh
umount /mnt/stage2 [/aidcode]
附录C comps文件清单
[aidcode] 4 1 Base { MAKEDEV SysVinit anacron at basesystem bash bdflush bzip2 chkconfig console-tools cpio cracklib cracklib-dicts crontabs cyrus-sasl openssl db1 db2 db3 dev devfsd diffutils e2fsprogs file filesystem fileutils findutils gawk glib glibc glibc-common grep gzip hdparm hotplug lilo info initscripts kbdconfig kernel krb5-libs kudzu less libstdc++ libtermcap logrotate losetup mingetty mkbootdisk mkinitrd modutils mount net-tools newt ntsysv pam passwd pciutils popt procps psmisc pwdb quota rpm sed setserial setup setuptool sh-utils shadow-utils slang slocate sysklogd tar termcap textutils time timeconfig tmpwatch util-linux vim-common vim-minimal vixie-cron which zlib } 1 --hide IDS Sensor { iptables iputils libpcap mysql openssh openssh-clients openssl perl rdate snort tcpdump traceroute unzip zip } [/aidcode]
附录D hdlist和hdlist2生成方
假设在/tmp/cdimage/RedHat/RPMS目录下收录着RPM包的话,则通过使用 [aidcode]
genhdlist /tmp/cdimage/ [/aidcode]
生成hdlist和hdlist2文件,文件的生成位置在目录/tmp/cdimage/RedHat/base/下
附录E sensor.py文件清单
[aidcode]
from installclass import BaseInstallClass
from translate import *
from installclass import FSEDIT_CLEAR_ALL
import os
import iutil
class InstallClass(BaseInstallClass):
name = N_("IDS Sensor")
pixmap = "sensor.png"
sortPriority = 10
def __init__(self, expert): BaseInstallClass.__init__(self) self.setGroups(["IDS Sensor"]) self.addToSkipList("lilo") self.addNewPartition('/boot', (48, -1, 0), (None, -1, 0), (0,0)) self.addNewPartition('/', (256, -1, 0), (None, -1, 0), (0,0)) self.addNewPartition('/usr', (512, -1, 1), (None, -1, 0), (0,0)) self.addNewPartition('/var', (256, -1, 0), (None, -1, 0), (0,0)) self.addNewPartition('/home',(512, -1, 1), (None, -1, 0), (0,0)) self.setClearParts(FSEDIT_CLEAR_ALL, warningText = N_("Automatic partitioning will erase ALL DATA on your hard " "drive to make room for your Linux installation.")) # self.addNewPartition('swap', (64, 256, 1), (None, -1, 0), (0,0)) # 2.4 kernel requires more swap, so base amount we try to get # on amount of memory (minswap, maxswap) = iutil.swapSuggestion() self.addNewPartition('swap', (minswap, maxswap, 1), (None, -1, 0), (0,0)) [/aidcode]
附录F syslinux.cfg文件清单
[aidcode]
label linux
kernel vmlinuz
append initrd=initrd.img lang=us devfs=nomount vga=788 [/aidcode]
附录G boot.img制作启动盘方法
[aidcode]
cat boot.img > /dev/fd0 [/aidcode]
或
[aidcode]
dd if=boot.img of=/dev/fd0 bs=1440 [/aidcode]
附录H Linux光盘映像制作方法
在Linux下光盘映像的制作用mkisofs命令,具体的,假设在/tmp/cdimage/目录下收录着将要被制作的光盘内容,则执行
[aidcode]
mkisofs –v –r –T –J –V “NetCop Linux” –b images/boot.img
-o /tmp/NetCopLinux.iso [/aidcode]
即可在/tmp目录下做出一命名为NetCopLinux.iso的光盘映像文件,它以/tmp/cdimage/images/boot.img 作为光盘启动文件。