Chinaunix首页 | 论坛 | 博客
  • 博客访问: 103750301
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类: LINUX

2008-04-22 22:42:57

出处:ChinaITLab 
 
阅读提示:本文主要包括RPM基本命令介绍,RPM包的定制过程,RPM SPEC文件的主要内容,RPM函数库简单参考和安装程序中关于RPM包管理部分源码的简单介绍。通过这部分的介绍,希望读者能对Linux系统下RPM 包的定制过程和RPM包的系统安装过程有一个基本的了解。
Notify参数指定安装过程中系统调用的进度跟踪过程。回调过程的函数指针定义:
typedef void (*rpmNotifyFunction)(const unsigned long amount, const unsigned long total);
amount指定已经安装的字节数,total指定安装的总字节数。此过程可用于在安装过程中动态更新进程条等信息。
LabelFormat指定包标签的格式。NetsharedPath参数指定和其它系统共享的本地文件系统部分。如果共享多个目录,路径要用冒号隔开。
int rpmRemovePackage(char * root, rpmdb db, unsigned int offset, int flags);
此过程除去在rpm数据库中offset位置处的包。root指定包所在的根目录,flags的值定义在rpmlib.h中,定义了如下标志:
RPMUNINSTALL_TEST 进行删除检测,但是不除去任何包
RPMUNINSTALL_NOSCRIPTS 不执行包删除脚本。
4.7 与包依赖性相关的操作
依赖性操作是完全和正常的基于包的操作隔离的。包的安装和除去过程自身并不完成依赖性处理。因此依赖性处理函数和其他rpmlib函数不同。依赖性处理的中心是rpmDependencies数据结构。这些函数只是随着操作的不同管理此数据结构,它们并不对包进行操作。在依赖性检测过程发现存在包依赖性冲突问题时,rpmDependencyConflict结构返回依赖性冲突。
rpmDependencies rpmdepDependencies(rpmdb db);
此过程返回初始化了的rpmDependencies结构。此时基于db参数指定的rpm数据库进行依赖性检测。
void rpmdepAddPackage(rpmDependencies rpmdep, Header h);
此过程加入头为h的包到rpmDependencies数据结构rpmdep中。
void rpmdepUpgradePackage(rpmDependencies rpmdep, Header h);
此过程加入头为h的包到rpmDependencies数据结构rpmdep中,同时此过程除去包依赖的老版本。
void rpmdepRemovePackage(rpmDependencies rpmdep, int dboffset);
此过程在rpmdep依赖结构中除去rpm数据库中偏移为dboffset的包。
void rpmdepAvailablePackage(rpmDependencies rpmdep, Header h, void * key);
此过程加入头为h的包到rpmDependencies数据结构rpmdep中。Key用于标识加入的包,这个参数会作为rpmdepCheck()函数返回的rpmDependencyConflict结构的一部分。
int rpmdepCheck(rpmDependencies rpmdep, struct rpmDependencyConflict ** conflicts, int * numConflicts);
此函数在rpmDependencies类型的结构变量rpmdep上进行依赖性检测。它返回一个numConflicts大小的数组,数组保存在conflicts指针中。
void rpmdepFreeConflicts(struct rpmDependencyConflict * conflicts, int numConflicts);
此函数释放conflicts指针指向的依赖性冲突结构。
void rpmdepDone(rpmDependencies rpmdep);
此函数释放rpmDependencies类型的结构变量rpmdep。
4.8 头信息处理
rpm头信息是为最小化的rpm数据库服务的,在这个数据库中可以进行特定信息的检索。
Header headerRead(int fd, int magicp);
此函数从文件fd中读入header并且转换网络字节序为主机系统字节序。如果magicp定义为HEADER_MAGIC_YES,此函数期望接收头幻数。如果magicp定义为HEADER_MAGIC_NO,此函数不包含头幻数。
void headerWrite(int fd, Header h, int magicp);
此函数将头h写入文件fd中并且转换主机系统字节序为网络字节序。如果magicp定义为HEADER_MAGIC_YES,此函数在写入的头信息中加入适当的幻数。如果magicp定义为HEADER_MAGIC_NO,此函数写入的信息不包含幻数。
Header headerCopy(Header h);
此函数返回头h的副本。
unsigned int headerSizeof(Header h, int magicp);
此函数返回头h占用的字节数。
Header headerNew(void);
此函数返回一个新的头。
void headerFree(Header h);
此函数释放h所指定的头信息。
void headerDump(Header h, FILE *f, int flags);
此函数打印头h结构到文件f中。若flags标志为HEADER_DUMP_INLINE,头数据也被打印。
4.9 包头信息处理
此节的这些函数提供处理头入口的基本操作,包含下列头入口类型:
RPM_NULL_TYPE - 不使用此类型
RPM_CHAR_TYPE - 此入口包含单一字符。
RPM_INT8_TYPE - 此入口包含八位字符。
RPM_INT16_TYPE - 此入口包含16位字符。
RPM_INT32_TYPE - 此入口包含32位字符。
RPM_INT64_TYPE - 此入口包含64位字符。
RPM_STRING_TYPE - 此入口包含字符串类型。
RPM_BIN_TYPE - 此入口包含rpmlib无法处理的二进制数据。
RPM_STRING_ARRAY_TYPE - 此入口包含字符数组。
int headerGetEntry(Header h, int_32 tag, int_32 *type, void **p, int_32 *c);
此过程从h中取出与tag标志匹配的入口。入口类型由type返回,数据指针由p返回,数据大小由c返回。
int headerAddEntry(Header h, int_32 tag, int_32 type, void *p, int_32 c);
此函数向头h中加入新的入口, tag参数指定入口的标志,type指定入口的类型。P指定入口数据,c是此数据的大小。
4.10 头重复器支持
rpmdbMatchIterator rpmdbInitIterator(rpmdb rpmdb, int rpmtag, const void * key, size_t keylen);
此函数返回新创建的重复器。
Header rpmdbNextIterator(rpmdbMatchIterator iter);
此函数获得下一个头信息。
void rpmdbFreeIterator(rpmdbMatchIterator iter);
此函数释放重复器iter占用的资源。
5 RPM包管理过程实现
在安装程序中与包管理有关的函数基本上都保存在文件pkgs.pm中。这个模块中包含的主要函数如下:
getDeps
读入文件depslist.ordered,通过它形成所有包所依赖rpm包的描述结构。
getProvides
读入所有rpm文件的头信息描述结构,通过provides字段形成包的Provides结构。
init_db,rebuild_db_open_for_traversal,clean_old_rpm_db
初始化(重建、清除)包的rpm配置数据库。
install
安装指定的rpm包。
packageByXXX
通过对读入的包头信息的解释,生成相应的包内部信息。比如读出包的大小,版本号,描述,名字等。
psUsingHdlists
读入hdlists,由它每一行保存的信息,读入对应的hdlist文件。
psUsingHdlist
由psUsingHdlists函数调用,读入所有的rpm包的文件头描述信息。
remove
删除指定的rpm包。
read_rpmsrate
读入rpmsrate,形成最基本的包类描述。
readCompssUsers
读入高层的包类描述信息,返回compssUsers结构。每选中一个compssUsers类,对应多个rpmsrate文件描述的子类。
selectPackage
选中指定的包。
unselectPackage
取消指定包的选中状态。
包的处理过程的一般流程:

#- 下面的程序是RPM包安装过程代码,它是整个包安装过程中作关键的部分。它使用了安装回调的机
#- 制,使得包安装过程中,系统可以随时显示安装进度并更换安装插图。
#- 下面的c::*方式调用的函数是对rpmlib.a中的库函数进行封装之后生成的函数,它的封装保存在#- perl语言的存根文件stuff.xs中。
sub install($$$;$$) {
my ($prefix, $isUpgrade, $toInstall, $depOrder, $media) = @_;
my %packages;
return if $::g_auto_install || !scalar(@$toInstall);
my $loop_boot = loopback::prepare_boot($prefix);
my ($total, $nb);
#- 根据数组toInstall中的内容,选择安装包,同时计算安装的大小。
foreach my $pkg (@$toInstall) {
$packages{packageName($pkg)} = $pkg;
$nb++;
$total += packageSize($pkg);
}
#- 将安装信息写入日志文件,在成功安装系统之后,会形成安装日志文件install.log
log::l("pkgs::install $prefix");
log::l("pkgs::install the following: ", join(" ", keys %packages));
eval { fs::mount("/proc", "$prefix/proc", "proc", 0) } unless -e "$prefix/proc/cpuinfo";
log::l("reading /usr/lib/rpm/rpmrc");
#- 读入rpm包的配置文件rpmrc
c::rpmReadConfigFiles() or die "can't read rpm config files";
log::l("\tdone");
#- 打开包所在文件的回调函数。
my $callbackOpen = sub {
my $p = $packages{$_[0]};
my $f = packageFile($p);
print LOG "$f $p->[$MEDIUM]{descr}\n";
my $fd = install_any::getFile($f, $p->[$MEDIUM]{descr});
$fd ? fileno $fd : -1;
};
#- 删除包所在的描述结构,并设置包的安装标志
my $callbackClose = sub { packageSetFlagInstalled(delete $packages{$_[0]}, 1) };
installCallback("Starting installation", $nb, $total);
my ($i, $min, $medium) = (0, 0, 1);
do {
my @transToInstall;
if (!$depOrder || !$media) {
@transToInstall = values %packages;
$nb = 0;
} else {
do {
#- 如果需要,改变安装介质(光盘、硬盘)
if ($i > $media->{$medium}{max}) {
#- 寻找包含指定安装包的介质
foreach (keys %$media) {
$i >= $media->{$_}{min} && $i <= $media->{$_}{max} and $medium = $_, last;
}
}
$i >= $media->{$medium}{min} && $i <= $media->{$medium}{max} or die "unable to find right medium";
#- 检查使用的安装介质,比如是光盘安装还是别的方式
install_any::useMedium($medium);
while ($i <= $media->{$medium}{max} && ($i < $min || scalar @transToInstall < $limitMinTrans)) {
my $dep = $packages{packageName($depOrder->[$i++])} or next;
if ($dep->[$MEDIUM]{selected}) {
push @transToInstall, $dep;
foreach (map { split '\|' } packageDepsId($dep)) {
$min < $_ and $min = $_;
}
} else {
log::l("ignoring package $dep->[$FILE] as its medium is not selected");
}
--$nb;
}
} while ($nb > 0 && scalar(@transToInstall) == 0);
}
#- 在任何介质都没有选择的包时,退出安装。
if ($nb == 0 && scalar(@transToInstall) == 0) {
cleanHeaders($prefix);
loopback::save_boot($loop_boot);
return;
}
extractHeaders($prefix, \@transToInstall, $media->{$medium});
#- 重设文件描述符
if ($media->{$medium}{method} eq 'cdrom') {
install_any::getFile(packageFile($transToInstall[0]), $transToInstall[0][$MEDIUM]{descr});
}
install_any::getFile('XXX');
my $retry = 3;
#- 为了保证安装过程具有更好的并发性和健壮性,在启动安装时,创建了一个子进程进行安装。
#- 同时,在父进程中对整个输出过程进行了改向。
while (@transToInstall) {
local (*INPUT, *OUTPUT); pipe INPUT, OUTPUT;
if (my $pid = fork()) {
close OUTPUT;
my $error_msg = '';
local $_;
while () {
if (/^die:(.*)/) {
$error_msg = $1;
last;
} else {
chomp;
my @params = split ":";
if ($params[0] eq 'close') {
&$callbackClose($params[1]);
} else {
installCallback(@params);
}
}
}
$error_msg and $error_msg .= join('', );
waitpid $pid, 0;
close INPUT;
$error_msg and die $error_msg;
} else {
#- 子进程执行所有的安装操作
$SIG{SEGV} = sub { log::l("segmentation fault on transactions"); c::_exit(0) };
my $db;
eval {
close INPUT;
select((select(OUTPUT), $| = 1)[0]);
$db = c::rpmdbOpen($prefix) or die "error opening RPM database: ", c::rpmErrorString();
my $trans = c::rpmtransCreateSet($db, $prefix);
log::l("opened rpm database for transaction of ". scalar @transToInstall ." new packages,
still $nb after that to do");
c::rpmtransAddPackage($trans, $_->[$HEADER], packageName($_), $isUpgrade && allowedToUpgrade(packageName($_)))
foreach @transToInstall;
c::rpmdepOrder($trans) or die "error ordering package list: " . c::rpmErrorString();
c::rpmtransSetScriptFd($trans, fileno LOG);
log::l("rpmRunTransactions start");
my @probs = c::rpmRunTransactions($trans, $callbackOpen,
 sub { print OUTPUT "close:$_[0]\n"; },
 sub { print OUTPUT join(":", @_), "\n"; },
 1);
log::l("rpmRunTransactions done, now trying to close still opened fd");
install_any::getFile('XXX');
if (@probs) {
my %parts;
@probs = reverse grep {
if (s/(installing package) .* (needs (?:.*) on the (.*) filesystem)/$1 $2/) {
$parts{$3} ? 0 : ($parts{$3} = 1);
} else {
1;
}
} reverse map { s|/mnt||; $_ } @probs;
c::rpmdbClose($db);
die "installation of rpms failed:\n ", join("\n ", @probs);
}
}; $@ and print OUTPUT "die:$@\n";
c::rpmdbClose($db);
log::l("rpm database closed");
close OUTPUT;
my (@killpid, %tree, $pid);
local (*DIR, *F, $_);
opendir DIR, "/proc";
while ($pid = readdir DIR) {
$pid =~ /^\d+$/ or next;
open F, "/proc/$pid/status";
while () {
/^Pid:\s+(\d+)/ and $pid == $1 || die "incorrect pid reported for $pid (found $1)";
if (/^PPid:\s+(\d+)/) {
$tree{$pid} and die "PPID already found for $pid, previously $tree{$pid}, now $1";
$tree{$pid} = $1;
}
}
close F;
}
closedir DIR;
foreach (keys %tree) {
$pid = $_; while ($pid = $tree{$pid}) { $pid == $$ and push @killpid, $_ }
}
if (@killpid) {
log::l("killing process ". join(", ", @killpid));
kill 15, @killpid;
sleep 2;
kill 9, @killpid;
}
c::_exit(0);
}
my @badPackages;
foreach (@transToInstall) {
if (!packageFlagInstalled($_) && $_->[$MEDIUM]{selected} && !exists($ignoreBadPkg{packageName($_)})) {
push @badPackages, $_;
} else {
packageFreeHeader($_);
}
}
@transToInstall = @badPackages;
$retry or last;
if (@transToInstall) {
foreach (@transToInstall) {
log::l("bad package $_->[$FILE]");
}
log::l("retrying transaction on bad packages");
--$retry;
}
}
packageFreeHeader($_) foreach @transToInstall;
cleanHeaders($prefix);
if (@transToInstall) {
foreach (@transToInstall) {
log::l("bad package $_->[$FILE] unable to be installed");
packageSetFlagSelected($_, 0);
}
cdie ("error installing package list: " . join(", ", map { $_->[$FILE] } @transToInstall));
}
} while ($nb > 0 && !$pkgs::cancel_install);
cleanHeaders($prefix);
loopback::save_boot($loop_boot);
}

阅读(421) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~