应用程序包开发者指南

第 5 章 软件包创建案例研究

本章提供了案例研究以展示打包方案,例如有条件地安装对象、在运行时确定要创建的文件数,以及在软件包安装和删除期间修改现有数据文件。

每个案例研究都以一段描述开始,然后介绍采用的打包技术的列表、使用这些技术时采取的方法,以及与案例研究相关的样例文件和脚本。

以下是本章中案例研究的列表:

请求来自管理员的输入

在此案例研究中,软件包有三种类型的对象。管理员可以选择安装哪一种对象类型,并选择对象在安装计算机上的位置。

技术

此案例研究展示以下技术:

方法

要在此案例研究中设置选择性安装,您必须完成以下任务:

案例研究文件

pkginfo 文件

PKG=ncmp
NAME=NCMP Utilities
CATEGORY=application, tools
BASEDIR=/
ARCH=SPARC
VERSION=RELEASE 1.0, Issue 1.0
CLASSES=""
NCMPBIN=/bin
NCMPMAN=/usr/man
EMACS=/usr/emacs

prototype 文件

i pkginfo
i request
x bin $NCMPBIN 0755 root other
f bin $NCMPBIN/dired=/usr/ncmp/bin/dired 0755 root other
f bin $NCMPBIN/less=/usr/ncmp/bin/less 0755 root other
f bin $NCMPBIN/ttype=/usr/ncmp/bin/ttype 0755 root other
f emacs $NCMPBIN/emacs=/usr/ncmp/bin/emacs 0755 root other
x emacs $EMACS 0755 root other
f emacs $EMACS/ansii=/usr/ncmp/lib/emacs/macros/ansii 0644 root other
f emacs $EMACS/box=/usr/ncmp/lib/emacs/macros/box 0644 root other
f emacs $EMACS/crypt=/usr/ncmp/lib/emacs/macros/crypt 0644 root other
f emacs $EMACS/draw=/usr/ncmp/lib/emacs/macros/draw 0644 root other
f emacs $EMACS/mail=/usr/ncmp/lib/emacs/macros/mail 0644 root other
f emacs $NCMPMAN/man1/emacs.1=/usr/ncmp/man/man1/emacs.1 0644 root other
d man $NCMPMAN 0755 root other
d man $NCMPMAN/man1 0755 root other
f man $NCMPMAN/man1/dired.1=/usr/ncmp/man/man1/dired.1 0644 root other
f man $NCMPMAN/man1/ttype.1=/usr/ncmp/man/man1/ttype.1 0644 root other
f man $NCMPMAN/man1/less.1=/usr/ncmp/man/man1/less.1 0644 inixmr other

request 脚本

trap 'exit 3' 15
# determine if and where general executables should be placed
ans=`ckyorn -d y \
-p "Should executables included in this package be installed"
` || exit $?
if [ "$ans" = y ]
then
   CLASSES="$CLASSES bin"
   NCMPBIN=`ckpath -d /usr/ncmp/bin -aoy \
   -p "Where should executables be installed"
   ` || exit $?
fi
# determine if emacs editor should be installed, and if it should
# where should the associated macros be placed
ans=`ckyorn -d y \
-p "Should emacs editor included in this package be installed"
` || exit $?
if [ "$ans" = y ]
then
   CLASSES="$CLASSES emacs"
   EMACS=`ckpath -d /usr/ncmp/lib/emacs -aoy \
   -p "Where should emacs macros be installed"
   ` || exit $?
fi

请注意,request 脚本可以在不在文件系统上留下任何文件的情况下退出。对于 2.5 和兼容版本之前的 Solaris 版本上的安装(其中没有 checkinstall 脚本可用),request 脚本是以确保安装成功所需的任何方式测试文件系统的正确位置。当 request 脚本以代码 1 退出时,安装将完全退出。

这些示例文件显示了使用参数化路径建立多个基目录。然而,首选的方法涉及使用由 pkgadd 命令管理并验证的 BASEDIR 参数。每当使用多个基目录时,在同一个平台上安装多个版本和体系结构时应格外小心。

在安装时创建文件并在删除期间保存文件

在此案例研究中,将在安装时创建一个数据库文件,并在删除软件包时保存数据库副本。

技术

此案例研究展示以下技术:

方法

要为此案例研究在安装时创建一个数据库文件,并在删除时保存副本,您必须完成以下任务:

案例研究文件

pkginfo 文件

PKG=krazy
NAME=KrAzY Applications
CATEGORY=applications
BASEDIR=/opt
ARCH=SPARC
VERSION=Version 1
CLASSES=none cfgdata admin

prototype 文件

i pkginfo
i request
i i.admin
i r.cfgdata
d none bin 555 root sys
f none bin/process1 555 root other
f none bin/process2 555 root other
f none bin/process3 555 root other
f admin bin/config 500 root sys
d admin cfg 555 root sys
f admin cfg/datafile1 444 root sys
f admin cfg/datafile2 444 root sys
f admin cfg/datafile3 444 root sys
f admin cfg/datafile4 444 root sys
d cfgdata data 555 root sys

space 文件

# extra space required by config data which is
# dynamically loaded onto the system
data 500 1

i.admin 类操作脚本

# PKGINST parameter provided by installation service
# BASEDIR parameter provided by installation service
while read src dest
do
   cp $src $dest || exit 2
done
# if this is the last time this script will be executed
# during the installation, do additional processing here.
if [ "$1" = ENDOFCLASS ]
then
# our config process will create a data file based on any changes
# made by installing files in this class; make sure the data file
# is in class `cfgdata' so special rules can apply to it during
# package removal.
   installf -c cfgdata $PKGINST $BASEDIR/data/config.data f 444 root
   sys || exit 2
   $BASEDIR/bin/config > $BASEDIR/data/config.data || exit 2
   installf -f -c cfgdata $PKGINST || exit 2
fi
exit 0

这里举例说明了一个很少见的实例,其中 installf 适用于类操作脚本。由于 space 文件用于在特定文件系统上保留空间,因此即使该新文件没有包括在 pkgmap 文件中也可以被安全地添加。

r.cfgdata 删除脚本

# the product manager for this package has suggested that
# the configuration data is so valuable that it should be
# backed up to $PKGSAV before it is removed!
while read path
do
# path names appear in reverse lexical order.
   mv $path $PKGSAV || exit 2
   rm -f $path || exit 2
done
exit 0

定义软件包兼容性和相关性

在此案例研究中,软件包使用可选信息文件来定义软件包兼容性和相关性,以及在安装期间呈现版权新息。

技术

此案例研究展示以下技术:

有关这些文件的更多信息,请参见创建信息文件

方法

要符合描述中的要求,您必须:

案例研究文件

pkginfo 文件

PKG=case3
NAME=Case Study #3
CATEGORY=application
BASEDIR=/opt
ARCH=SPARC
VERSION=Version 3.0
CLASSES=none

copyright 文件

Copyright (c) 1999 company_name
All Rights Reserved.
THIS PACKAGE CONTAINS UNPUBLISHED PROPRIETARY SOURCE CODE OF
company_name.
The copyright notice above does not evidence any
actual or intended publication of such source code

compver 文件

Version 3.0
Version 2.3
Version 2.2
Version 2.1
Version 2.1.1
Version 2.1.3
Version 1.7

depend 文件

P acu Advanced C Utilities
Issue 4 Version 1
P cc C Programming Language
Issue 4 Version 1
P dfm Directory and File Management Utilities
P ed Editing Utilities
P esg Extended Software Generation Utilities
Issue 4 Version 1
P graph Graphics Utilities
P rfs Remote File Sharing Utilities
Issue 1 Version 1
P rx Remote Execution Utilities
P sgs Software Generation Utilities
Issue 4 Version 1
P shell Shell Programming Utilities
P sys System Header Files
Release 3.1

使用标准类和类操作脚本修改文件

在此案例研究中,使用标准类和类操作脚本在软件包安装期间修改现有文件。它使用三种修改方法之一。另外两种方法将在使用 sed 类和 postinstall 脚本修改文件使用 build 类修改文件中进行介绍。修改的文件是 /etc/inittab

技术

此案例研究展示如何使用安装类操作脚本和删除类操作脚本。有关更多信息,请参见编写类操作脚本

方法

要使用类和类操作脚本在安装期间修改 /etc/inittab,您必须完成以下任务:

此案例研究比下一个更为复杂;请参见使用 sed 类和 postinstall 脚本修改文件。在此案例研究中,不是提供两个文件而是需要三个文件,而且提供的 /etc/inittab 文件实际上只是包含要插入的条目片段的占位符。此占位符可能已被放置到 i.inittab 文件中,除非 pkgadd 命令必须将一个文件传递到 i.inittab 文件。此外,必须将删除过程放置到一个单独的文件 (r.inittab) 中。虽然此方法效果很好,但最好将其保留为在涉及多个文件的非常复杂的安装情况下使用。请参见在安装期间修改 crontab 文件

由于 inittab 条目末尾的注释基于软件包实例,因此使用 sed 类和 postinstall 脚本修改文件中使用的 sed 程序支持多个软件包实例。使用 build 类修改文件中的案例研究给出了一种在安装期间编辑 /etc/inittab 的更简化的方法。

案例研究文件

pkginfo 文件

PKG=case5
NAME=Case Study #5
CATEGORY=applications
BASEDIR=/opt
ARCH=SPARC
VERSION=Version 1d05
CLASSES=inittab

prototype 文件

i pkginfo
i i.inittab
i r.inittab
e inittab /etc/inittab ? ? ?

i.inittab 安装类操作脚本

# PKGINST parameter provided by installation service
while read src dest
do
# remove all entries from the table that
# associated with this PKGINST
sed -e "/^[^:]*:[^:]*:[^:]*:[^#]*#$PKGINST$/d" $dest >
/tmp/$$itab ||
exit 2
sed -e "s/$/#$PKGINST" $src >> /tmp/$$itab ||
exit 2
mv /tmp/$$itab $dest ||
exit 2
done
if [ "$1" = ENDOFCLASS ]
then
/sbin/init q ||
exit 2
fi
exit 0

r.inittab 删除类操作脚本

# PKGINST parameter provided by installation service
while read src dest
do
# remove all entries from the table that
# are associated with this PKGINST
sed -e "/^[^:]*:[^:]*:[^:]*:[^#]*#$PKGINST$/d" $dest >
/tmp/$$itab ||
exit 2
mv /tmp/$$itab $dest ||
exit 2
done
/sbin/init q ||
exit 2
exit 0

inittab 文件

rb:023456:wait:/usr/robot/bin/setup

使用 sed 类和 postinstall 脚本修改文件

此案例研究在软件包安装期间修改安装计算机上存在的一个文件。它使用三种修改方法之一。另外两种方法将在使用标准类和类操作脚本修改文件使用 build 类修改文件中进行介绍。修改的文件是 /etc/inittab

技术

此案例研究展示以下技术:

方法

要使用 sed 类在安装时修改 /etc/inittab,您必须完成以下任务:

这种在安装期间编辑 /etc/inittab 的方法有一个缺点:虽然只是为了执行 init q 命令也必须提供一个完整的脚本(postinstall 脚本)。

案例研究文件

pkginfo 文件

PKG=case4
NAME=Case Study #4
CATEGORY=applications
BASEDIR=/opt
ARCH=SPARC
VERSION=Version 1d05
CLASSES=sed

prototype 文件

i pkginfo
i postinstall
e sed /etc/inittab ? ? ?

sed 类操作脚本 (/etc/inittab)

!remove
# remove all entries from the table that are associated
# with this package, though not necessarily just
# with this package instance
/^[^:]*:[^:]*:[^:]*:[^#]*#ROBOT$/d
!install
# remove any previous entry added to the table
# for this particular change
/^[^:]*:[^:]*:[^:]*:[^#]*#ROBOT$/d
# add the needed entry at the end of the table;
# sed(1) does not properly interpret the '$a'
# construct if you previously deleted the last
# line, so the command
# $a\
# rb:023456:wait:/usr/robot/bin/setup #ROBOT
# will not work here if the file already contained
# the modification. Instead, you will settle for
# inserting the entry before the last line!
$i\
rb:023456:wait:/usr/robot/bin/setup #ROBOT

postinstall 脚本

# make init re-read inittab
/sbin/init q ||
exit 2
exit 0

使用 build 类修改文件

此案例研究在软件包安装期间修改安装计算机上存在的一个文件。它使用三种修改方法之一。另外两种方法将在使用标准类和类操作脚本修改文件使用 sed 类和 postinstall 脚本修改文件中进行介绍。修改的文件是 /etc/inittab

技术

此案例研究展示如何使用 build 类。有关 build 类的更多信息,请参见build 类脚本

方法

这种修改 /etc/inittab 的方法使用 build 类。build 类脚本作为 shell 脚本执行,其输出成为正在执行的文件的新版本。换句话说,与该软件包一起提供的 /etc/inittab 数据文件将会执行,执行后的输出将成为 /etc/inittab

build 类脚本在软件包安装和删除期间执行。如果该文件在安装时执行,参数 install 会传递给该文件。请注意在 build 样例类脚本中,安装操作通过测试此参数来定义。

要使用 build 类编辑 /etc/inittab,您必须完成以下任务:

该解决方案解决了在使用标准类和类操作脚本修改文件使用 sed 类和 postinstall 脚本修改文件的案例研究中描述的缺点。只需要一个很短的文件(除 pkginfo prototype 文件之外)。因为使用了 PKGINST 参数,所以该文件适用于软件包的多个实例,而且由于 init q 命令可从 build 类脚本中执行,因此不需要 postinstall 脚本。

案例研究文件

pkginfo 文件

PKG=case6
NAME=Case Study #6
CATEGORY=applications
BASEDIR=/opt
ARCH=SPARC
VERSION=Version 1d05
CLASSES=build

prototype 文件

i pkginfo
e build /etc/inittab ? ? ?

生成文件

# PKGINST parameter provided by installation service
# remove all entries from the existing table that
# are associated with this PKGINST
sed -e "/^[^:]*:[^:]*:[^:]*:[^#]*#$PKGINST$/d" /etc/inittab ||
exit 2
if [ "$1" = install ]
then
# add the following entry to the table
echo "rb:023456:wait:/usr/robot/bin/setup #$PKGINST" ||
exit 2
fi
/sbin/init q ||
exit 2
exit 0

在安装期间修改 crontab 文件

此案例研究在软件包安装期间修改 crontab 文件。

技术

此案例研究展示以下技术:

方法

在安装期间编辑多个文件的最高效方式是定义一个类,然后提供类操作脚本。如果您使用了 build 类方法,您将需要为每个编辑的 crontab 文件提供一个 build 类脚本。定义 cron 类是一种更通用的方法。要使用这种方法编辑 crontab 文件,您必须:

案例研究文件

下述 i.cronr.cron 脚本由超级用户执行。以超级用户身份编辑其他用户的 crontab 文件可能导致无法预测的结果。如果需要,请在每个脚本中将以下条目:

crontab $user < /tmp/$$crontab ||

更改为

su $user -c "crontab /tmp/$$crontab" ||

pkginfo 命令

PKG=case7
NAME=Case Study #7
CATEGORY=application
BASEDIR=/opt
ARCH=SPARC
VERSION=Version 1.0
CLASSES=cron

prototype 文件

i pkginfo
i i.cron
i r.cron
e cron /var/spool/cron/crontabs/root ? ? ?
e cron /var/spool/cron/crontabs/sys ? ? ?

i.cron 安装类操作脚本

# PKGINST parameter provided by installation service
while read src dest
do
user=`basename $dest` ||
exit 2
(crontab -l $user |
sed -e "/#$PKGINST$/d" > /tmp/$$crontab) ||
exit 2
sed -e "s/$/#$PKGINST/" $src >> /tmp/$$crontab ||
exit 2
crontab $user < /tmp/$$crontab ||
exit 2
rm -f /tmp/$$crontab
done
exit 0

r.cron 删除类操作脚本

# PKGINST parameter provided by installation service
while read path
do
user=`basename $path` ||
exit 2
(crontab -l $user |
sed -e "/#$PKGINST$/d" > /tmp/$$crontab) ||
exit 2
crontab $user < /tmp/$$crontab ||
exit 2
rm -f /tmp/$$crontab
done
exit 

crontab 文件 #1

41,1,21 * * * * /usr/lib/uucp/uudemon.hour > /dev/null
45 23 * * * ulimit 5000; /usr/bin/su uucp -c
"/usr/lib/uucp/uudemon.cleanup" >
/dev/null 2>&1
11,31,51 * * * * /usr/lib/uucp/uudemon.poll > /dev/null

crontab 文件 #2

0 * * * 0-6 /usr/lib/sa/sa1
20,40 8-17 * * 1-5 /usr/lib/sa/sa1
5 18 * * 1-5 /usr/lib/sa/sa2 -s 8:00 -e 18:01 -i 1200 -A

注 –

如果对一组文件的编辑将导致文件总大小增加超过 10K,请提供一个 space 文件,以便 pkgadd 命令可以允许这种增加。有关 space 文件的更多信息,请参见在目标系统上保留额外空间


使用过程脚本安装和删除驱动程序

此软件包可安装驱动程序。

技术

此案例研究展示以下技术:

有关这些脚本的更多信息,请参见编写过程脚本

方法

案例研究文件

pkginfo 文件

PKG=bufdev
NAME=Buffer Device
CATEGORY=system
BASEDIR=/
ARCH=INTEL
VERSION=Software Issue #19
CLASSES=none

prototype 文件

要在安装时安装驱动程序,您必须在 prototype 文件中包括驱动程序的对象和配置文件。

在此示例中,驱动程序的可执行模块被命名为 bufferadd_drv 命令处理该文件。内核使用配置文件 buffer.conf 帮助配置驱动程序。

i pkginfo
i request
i postinstall
i preremove
f none $KERNDIR/buffer 444 root root
f none $KERNDIR/buffer.conf 444 root root

查看此示例中的 prototype 文件,请注意以下事项:

request 脚本

trap 'exit 3' 15
# determine where driver object should be placed; location
# must be an absolute path name that is an existing directory
KERNDIR=`ckpath -aoy -d /kernel/drv -p \
“Where do you want the driver object installed”` || exit $?

# make parameters available to installation service, and
# so to any other packaging scripts
cat >$1 <<!

CLASSES='$CLASSES'
KERNDIR='$KERNDIR'
!
exit 0

postinstall 脚本

# KERNDIR parameter provided by `request' script
err_code=1                    # an error is considered fatal
# Load the module into the system
cd $KERNDIR
add_drv -m '* 0666 root sys' buffer || exit $err_code
# Create a /dev entry for the character node
installf $PKGINST /dev/buffer0=/devices/eisa/buffer*:0 s
installf -f $PKGINST

preremove 脚本

err_code=1                    # an error is considered fatal
# Unload the driver
rem_drv buffer || exit $err_code
# remove /dev file
removef $PKGINST /dev/buffer0 ; rm /dev/buffer0
removef -f $PKGINST

使用 sed 类和过程脚本安装驱动程序

此案例研究描述如何使用 sed 类和过程脚本安装驱动程序。该案例研究还与前面的案例研究(请参见使用过程脚本安装和删除驱动程序)有所不同,因为此软件包由绝对和可重定位的对象组成。

技术

此案例研究展示以下技术:

方法

案例研究文件

pkginfo 文件

PKG=SUNWsst
NAME=Simple SCSI Target Driver
VERSION=1
CATEGORY=system
ARCH=sparc
VENDOR=Sun Microsystems
BASEDIR=/opt
CLASSES=sed

prototype 文件

例如,此案例研究使用软件包对象的分层布局,如下图所示。

图 5–1 分层软件包目录结构

下文对该图形进行了说明。

软件包对象的安装位置与上图 pkg 目录中的位置相同。驱动程序模块(sstsst.conf)安装在 /usr/kernel/drv 中,而头文件安装在 /usr/include/sys/scsi/targets 中。sstsst.confsst_def.h 文件是绝对对象。测试程序 sstest.c 及其目录 SUNWsst 是可重定位对象;它们的安装位置由 BASEDIR 参数设置。

软件包的其余组件(所有控制文件)安装在开发计算机上软件包的顶层目录中,但 sed 类脚本除外。它根据所修改的文件命名为 devlink.tab 并安装到 etc,该目录包含实际的 devlink.tab 文件。

pkg 目录运行 pkgproto 命令,如下所示:


find usr SUNWsst -print | pkgproto > prototype

上述命令的输出如下所示:

d none usr 0775 pms mts
d none usr/include 0775 pms mts
d none usr/include/sys 0775 pms mts
d none usr/include/sys/scsi 0775 pms mts
d none usr/include/sys/scsi/targets 0775 pms mts
f none usr/include/sys/scsi/targets/sst_def.h 0444 pms mts
d none usr/kernel 0775 pms mts
d none usr/kernel/drv 0775 pms mts
f none usr/kernel/drv/sst 0664 pms mts
f none usr/kernel/drv/sst.conf 0444 pms mts
d none SUNWsst 0775 pms mts
f none SUNWsst/sstest.c 0664 pms mts

prototype 文件并不是完整文件。要完成此文件,您需要进行以下修改:

以下是最终的 prototype 文件:

i pkginfo
i postinstall
i preremove
i copyright
e sed /etc/devlink.tab ? ? ?
f none /usr/include/sys/scsi/targets/sst_def.h 0644 bin bin
f none /usr/kernel/drv/sst 0755 root sys
f none /usr/kernel/drv/sst.conf 0644 root sys
d none SUNWsst 0775 root sys
f none SUNWsst/sstest.c 0664 root sys

sed 脚本条目中的问号表明不应更改安装计算机上现有文件的访问权限和拥有权。

sed 类操作脚本 (/etc/devlink.tab)

在驱动程序示例中,sed 类脚本用于向 /etc/devlink.tab 文件中添加一个驱动程序条目。devlinks 命令使用此文件创建从 /dev/devices 的符号链接。以下是 sed 脚本:

# sed class script to modify /etc/devlink.tab
!install
/name=sst;/d
$i\
type=ddi_pseudo;name=sst;minor=character	rsst\\A1

!remove
/name=sst;/d

pkgrm 命令不运行该脚本的删除部分。您可能需要向 preremove 脚本中添加一行,以便直接运行 sed/etc/devlink.tab 文件删除条目。

postinstall 安装脚本

在此示例中,该脚本所需要做的只是运行 add_drv 命令。

# Postinstallation script for SUNWsst
# This does not apply to a client.
if [$PKG_INSTALL_ROOT = "/" -o -z $PKG_INSTALL_ROOT]; then
   SAVEBASE=$BASEDIR
   BASEDIR=””; export BASEDIR
   /usr/sbin/add_drv sst
   STATUS=$?
   BASEDIR=$SAVEBASE; export BASEDIR
   if [ $STATUS -eq 0 ]
   then
	     exit 20
   else
	     exit 2
   fi
else
   echo "This cannot be installed onto a client."
   exit 2
fi

add_drv 命令使用 BASEDIR 参数,因此该脚本必须在运行此命令之前取消设置 BASEDIR,并在以后恢复它。

add_drv 命令的操作之一是运行 devlinks,它使用由 sed 类脚本放置在 /etc/devlink.tab 中的条目为驱动程序创建 /dev 条目。

postinstall 脚本的退出代码作用很重要。退出代码 20 告诉 pkgadd 命令告知用户重新引导系统(安装驱动程序后必须这样做),而退出代码 2 则告诉 pkgadd 命令告知用户安装部分失败。

preremove 删除脚本

在此驱动程序示例中,该脚本删除 /dev 中的链接并对驱动程序运行 rem_drv 命令。

# Pre removal script for the sst driver
echo “Removing /dev entries”
/usr/bin/rm -f /dev/rsst*

echo “Deinstalling driver from the kernel”
SAVEBASE=$BASEDIR
BASEDIR=””; export BASEDIR
/usr/sbin/rem_drv sst
BASEDIR=$SAVEBASE; export BASEDIR

exit 

该脚本删除 /dev 条目本身; /devices 条目由 rem_drv 命令删除。

copyright 文件

以下是一个包含版权声明文本的简单 ASCII 文件。该声明在软件包安装开始时显示,显示时与此文件中的形式完全相同。


	Copyright (c) 1999 Drivers-R-Us, Inc.
	10 Device Drive, Thebus, IO 80586

All rights reserved. This product and related documentation is
protected by copyright and distributed under licenses 
restricting its use, copying, distribution and decompilation. 
No part of this product or related documentation may be 
reproduced in any form by any means without prior written 
authorization of Drivers-R-Us and its licensors, if any.