OPKG命令执行过程分析

一、简介

Opkg 是一个基于 ipkg 的轻量级的软件包管理系统,主要用于嵌入式系统,目前应用opkg的有Open WRT 和 Open Embedded。1

Opkg的详细使用方法可以参考Open WRT的 WIKI页面2,不再赘述,本文将重点解释opkg的工作原理。

Opkg 的源代码可以在Google Code 3或Yocto Project 4上找到。Opkg的版本目前到了0.3.0 5,我使用的 Open WRT Barrier Breaker 14.07 系统上是0.1.8:

[email protected]:~# cat /etc/openwrt_release
DISTRIB_ID="OpenWrt"
DISTRIB_RELEASE="14.07"
DISTRIB_REVISION="r42625"
DISTRIB_CODENAME="barrier_breaker"
DISTRIB_TARGET="x86/generic"
DISTRIB_DESCRIPTION="OpenWrt Barrier Breaker 14.07"
DISTRIB_TAINTS=""
[email protected]:~# opkg --version
opkg version 0.1.8
[email protected]:~#

二、命令执行详解

0. 加载基本配置

这是执行所有命令之前的准备工作。加载配置的函数是 opkg_conf_load(). Opkg 有一个命令行参数 -f / --conf 可以明确指定配置文件,如果没有指定,那么 Opkg 使用默认的配置文件。

在 v0.1.8 版本上,Opkg 在args_parse() 函数解析完毕命令行参数后,如果没有 -f / --conf 参数,那么会默认使用 /etc/opkg.conf 作为配置文件:

if(!conf->conf_file && !conf->offline_root)
    conf->conf_file = xstrdup("/etc/opkg.conf");

v0.3.0 的版本去掉了这个默认值。以下说明都是基于 v0.3.0 的代码,其他版本可以参考对应的代码,应该大同小异。查找配置文件的步骤:

(1)     使用命令行参数指定的配置文件,如果没有,转(2);

(2)     使用命令行指定的 offline-root 路径下面的 *.conf,如果没有,转(3);

(3)     如果设置了环境变量 OFFLINE_ROOT,使用该路径下面的 *.conf,如果没有,转(4);

(4)     如果设置了环境变量 OPKG_CONF_DIR,使用该路径下面的 *.conf,如果没有,转(5);

(5)     使用默认的OPKG_CONF_DEFAULT_CONF_FILE_DIR(宏,代码中默认为 /etc/opkg),使用该路径下面的 *.conf,如果该路径不存在,则无法解析任何配置文件。

在准备过程中,配置文件是最重要的一个环节。除了配置文件,opkg 还需要初始化一些其他的变量(路径):


变量


默认值


Temp Directory


/tmp


Hash List


Lists Directory


/var/lib/opkg/lists


Info Directory


/var/lib/opkg/info


Status File


/var/lib/opkg/status


Signature File


Arch List


all, noarch, HOST_CPU_STR


Dest List


Cache Directory


/var/cache/opkg

注:HOST_CPU_STR为编译opkg时由configure指定的 --host 的值,可以在config.log 或者生成的 Makefile 中查看。

以上变量都有各自重要的用处,在后面的说明中会逐一涉及。

注:由于我是在 OpenWrt Barrier Breaker 14.07 上进行测试,所以在命令行上必须手动加上参数:

-f /etc/opkg.conf

否则opkg无法找到配置文件(目前最新版的Chaos Calmer 15.05-rc3也是同样的情况)。

解析完配置文件后,把配置文件中的 dest 指定的目录,加到 Dest List 中,例如:

dest root /
dest ram /tmp

然后,连接这个路径分别和以下几个路径连接:

(1)     和 Info Directory 连接作为该 dest 的 Info Directory;

(2)     和 Status File 连接作为该 dest 的 Status File;

(3)     和 Lists Directory 连接作为该 dest 的 Lists Directory。

以 / 为例:

//var/lib/opkg/info/
//var/lib/opkg/lists/
//var/lib/opkg/status

对比v0.1.8,这些目录是放在了 /usr/lib/opkg 下面:

[email protected]:~# ls /usr/lib/opkg -l
drwxr-xr-x    2 root     root          8192 Oct  1  2014 info
drwxr-xr-x    2 root     root          4096 Oct  1  2014 lists
-rw-r--r--    1 root     root         15932 Oct  1  2014 status
[email protected]:~#

其中,status 文件为所有软件包的基本信息,包括名称、版本、依赖、状态、初始安装日期等,以 libc 为例:

Package: libc
Version: 0.9.33.2-1
Depends: libgcc
Status: install hold installed
Essential: yes
Architecture: x86
Installed-Time: 1412176159
Auto-Installed: yes

Lists 文件夹下面是根据配置文件中的src/gz指定的URL下载下来的文件,例如:

src/gz barrier_breaker_base http://downloads.openwrt.org/barrier_breaker/14.07/x86/generic/packages/base

在执行 update 后,会使用下面的URL下载一个文件:

http://downloads.openwrt.org/barrier_breaker/14.07/x86/generic/packages/base/Packages.gz

文件下载后,将解压的文件放到 Lists 目录下面,并把第二个字段(如上面的barrier_breaker_base)作为文件名,这个文件里面包含了该Package里面所有的软件包的信息,举一为例:

Package: 6in4
Version: 17-1
Depends: libc, kmod-ipv6, kmod-sit
Source: package/network/ipv6/6in4
Section: net
Maintainer: Jo-Philipp Wich <[email protected]>
Architecture: all
Installed-Size: 1194
Filename: 6in4_17-1_all.ipk
Size: 1832
MD5Sum: 6758bb49b2a828301bdac956481ec6c1
SHA256sum: 1ad5794959c8435c7ab4c261ab86d2974d65527f7b8ffa99eee370409993976c
Description:  Provides support for 6in4 tunnels in /etc/config/network.
 Refer to http://wiki.openwrt.org/doc/uci/network for
 configuration details.

查看 Lists 目录下的内容(压缩了一些空白符):

[email protected]:~# ls /var/lib/opkg/lists/ -l
-rw-r--r-- 1 root root  485307 Jul 18 14:44 barrier_breaker_base
-rw-r--r-- 1 root root   43348 Jul 18 14:44 barrier_breaker_luci
-rw-r--r-- 1 root root    4634 Jul 18 14:44 barrier_breaker_management
-rw-r--r-- 1 root root  715082 Jul 18 14:44 barrier_breaker_oldpackages
-rw-r--r-- 1 root root  455216 Jul 18 14:44 barrier_breaker_packages
-rw-r--r-- 1 root root   34987 Jul 18 14:44 barrier_breaker_routing
-rw-r--r-- 1 root root  260681 Jul 18 14:44 barrier_breaker_telephony
[email protected]:~#

0.1    配置文件的格式

配置文件中有下面


类型(type)


说明


option


配置某些参数,可供配置的参数可以在libopkg/opkg_conf.c里面的options[]数组中找到


dist


可供更新的发行版(存疑)


dist/gz


可供更新的发行版(存疑),gzip格式


src


可供更新的软件源


src/gz


可供更新的软件源,gzip格式


dest


本地目标路径

1.      更新 update

Opkg里用结构体 opkg_cmd 来表示一个命令,对应命令行的command参数。该结构体定义如下:

struct opkg_cmd {
    const char *name;
    int requires_args;
    opkg_cmd_fun_t fun;
    unsigned int pfm;       /* package field mask */
};
typedef struct opkg_cmd opkg_cmd_t;

所有命令都保存在 libopkg/opkg_cmd.c 文件的 cmds[] 数组中,这里就不列出这个数组的值了。更新update使用的函数是opkg_update_cmd(),update后面无需参数。

更新时执行的步骤:

(1)     检查 Lists 文件夹是否存在,如果不存在,则创建该文件夹;

(2)     创建临时目录 $tmp_dir/update-XXXXXX,其中 $tmp_dir 就是前面提到的 Temp Directory;

(3)     更新dist指定的软件包;

(4)     更新src指定的软件包;

(5)     删除(2)中创建的临时目录。

下载过程是这样的:

(1)     构造下载地址:如果指定为gzip格式,那么用 $URL/Packages.gz 下载,否则用 $URL/Packages 下载;

(2)     对于gzip格式的文件,下载到 cache 目录下,文件名是把URL中的 / 替换为下划线 _ ,因此可以在 cache 目录看到如下内容:

[email protected]:~# ls /var/cache/opkg/
http:__downloads.openwrt.org_barrier_breaker_14.07_x86_generic_packages_base_Packages.gz
http:__downloads.openwrt.org_barrier_breaker_14.07_x86_generic_packages_luci_Packages.gz
http:__downloads.openwrt.org_barrier_breaker_14.07_x86_generic_packages_management_Packages.gz
http:__downloads.openwrt.org_barrier_breaker_14.07_x86_generic_packages_oldpackages_Packages.gz
http:__downloads.openwrt.org_barrier_breaker_14.07_x86_generic_packages_packages_Packages.gz
http:__downloads.openwrt.org_barrier_breaker_14.07_x86_generic_packages_routing_Packages.gz
http:__downloads.openwrt.org_barrier_breaker_14.07_x86_generic_packages_telephony_Packages.gz
[email protected]:~#

下载完成后,解压到 lists 目录下,并命名为配置文件中设置的名字;

(3)     对于非gzip格式的文件,则直接下载到 lists 目录下面,并命名为配置文件中设置的名字。

2.      列出软件包 list/list-installed

列出软件包命令list和更新upgrade稍有不同,在开始执行命令之前的准备工作,会读取lists 目录下的文件,这个文件的格式前面提到了。将所有文件内的每个软件包的信息都加载到内存中, 在读取文件的过程中,opkg会分析每个软件包的Architecture 字段,只有当该字段的值在 Arch List 中时,才会将该软件包加到 hash table 里面(opkg_config->file_hash)。

下面的事情就比较简单了,list 就是将 file_hash 里面所有的软件包打印出来。

列出安装的软件包list-installed命令,并不会读取 lists 目录下的文件,而是读取 Status List 包含的每个文件,系统已安装的软件包会放到这个文件里面,然后将Status包含installed的软件包打印出来。

3.      查看软件包状态 status/info

Status和info这两个命令使用同一个函数opkg_info_status_cmd(),这个函数的原型如下:

int opkg_info_status_cmd(int argc, char **argv, int installed_only)

使用status命令时,第三个参数 installed_only 为1,只查找已安装的软件包;使用info命令时,installed_only为0,查找所有软件包:

if (installed_only)
    pkg_hash_fetch_all_installed(available);
else
    pkg_hash_fetch_available(available);

接下来就是在设定的范围内顺序查找,如果status/info 后面有参数,那么只打印与参数名匹配的软件包的状态,否则打印全部的状态。

在这里发现opkg的一个微小的不足:当info/status后面跟的参数有多个,比如我想看 lua和 libc 两个软件包的信息,输入:

[email protected]:~# opkg status lua libc

只打印出lua的信息,没有libc的信息。这是因为opkg在查找时,如果有参数,那么只搜索第一个参数的软件包,后面的参数都被忽略了。读者有兴趣可以尝试做一个patch改进此问题。

4.      列出和搜索文件 files/search

files命令用于列出一个软件包所包含的全部文件,这些文件包括可执行文件、配置文件等。对于已安装过的软件包,则打开 $lists_dir 下面的软件包对应的 list 文件,例如软件包 busybox,对应的list文件为

/var/lib/opkg/info/busybox.list

这个文件包含了该软件包所包含的文件,每行一个。

对于未安装的软件包,打印软件包尚未安装的错误信息:

pkg = pkg_hash_fetch_installed_by_name(argv[0]);
if (pkg == NULL) {
    opkg_msg(ERROR, "Package %s not installed.\n", argv[0]);
    return 0;
}

Search 命令则用于查找某个文件属于哪个软件包,可以使用通配符(使用fnmatch函数来进行模式匹配)。对应的函数为 opkg_search_cmd(),这个过程也比较简单:获取全部安装的软件包,依次查找每个软件包所包含的文件,如果包含要查找的文件,那么就打印出该软件包。

这个函数也有一些缺陷,如果一个软件包所包含的文件,有多个可以匹配搜索的文件,那么结果该软件包就会被打印多次。以搜索ls为例,在base-files中有以下7个文件包含ls字符串:

[email protected]:~# cat /var/lib/opkg/info/base-files.list | grep ls
/etc/shells
/etc/protocols
/lib/preinit/10_indicate_failsafe
/etc/rc.button/failsafe
/lib/preinit/40_run_failsafe_hook
/lib/preinit/30_failsafe_wait
/lib/preinit/99_10_failsafe_login
[email protected]:~#

在使用

[email protected]:~# opkg search *ls*

的时候,base-files 就会被打印7次:

[email protected]:~# opkg search *ls*
base-files - 156-r42625
base-files - 156-r42625
base-files - 156-r42625
base-files - 156-r42625
base-files - 156-r42625
base-files - 156-r42625
base-files - 156-r42625
busybox - 1.22.1-2
busybox - 1.22.1-2
fstools - 2014-06-22-e0430f5c62f367e5a8e02755412977b02c3fc45e
luci-app-firewall - 0.12+svn-r10530-1
luci-app-firewall - 0.12+svn-r10530-1
luci-app-firewall - 0.12+svn-r10530-1
luci-app-firewall - 0.12+svn-r10530-1
luci-base - 0.12+svn-r10530-1
luci-base - 0.12+svn-r10530-1
luci-base - 0.12+svn-r10530-1
luci-base - 0.12+svn-r10530-1
luci-base - 0.12+svn-r10530-1
luci-base - 0.12+svn-r10530-1
netifd - 2014-09-08-46c569989f984226916fec28dd8ef152a664043e
ubox - 2014-09-16-5c45b560bc8c9e13682269ed963a8a4a65959518
[email protected]:~#

其他文件不一一说明。读者可以尝试做一个patch,使得每个软件包最多打印一次。

5.      安装 install

安装 install 后面可以跟多种类型的参数:

(1)     URL: opkg 尝试直接使用此URL下载一个软件包;

(2)     Package: 软件包名称,opkg会获取该软件包的名称(Filename字段),构造下载地址,例如 base 软件包里面的 6in4 ,Filename 字段为6in4_17-1_all.ipk,base的src为
http://downloads.openwrt.org/barrier_breaker/14.07/x86/generic/packages/base
所以 6in4 的下载地址为

http://downloads.openwrt.org/barrier_breaker/14.07/x86/generic/packages/base/6in4_17-1_all.ipk

(3)    
File: opkg会把这个名字当作一个文件名,文件的格式必须如 Lists 目录下的文件,或者如 control 文件(/var/lib/opkg/info/*.control),opkg从中解析出所有软件包的信息,并进行下载。

(4)    
File: opkg会把这个名字作为一个准备好的软件包(ipk)文件,直接进行安装,并标记为手动。

在进行实际的下载、安装动作之前,opkg 会做几件事:

检查软件状态(是否安装):在pkg_hash_load_feeds() (也就是opkg_conf_load()之后,执行install command之前),Opkg会从Lists文件(/var/lib/opkg/lists目录下)获取所有软件包的信息,如果一个软件的名字有有多个软件库提供,则把所有提供该软件包的
软件库放到provided_by 中,在检查是否安装软件时,将检查每个provided_by,如果有任何一个已安装,那么就会认为该软件已安装;

(1)    
检查是否已安装旧版本的软件包;

(2)    
查找要安装的软件包信息,如果该软件包有多个软件库提供,那么pkg_hash_fetch_best_installation_candidate() 函数会选择一个最佳的匹配(最佳匹配的规则是,手动最高,然后按照architecture的优先级排序);

(3)    
检查旧、新软件包的版本:如果旧的版本较高,并且标记强制降级(命令行参数--force-downgrade),则不做任何事情;如果新旧版本一样,表示已安装位最新版本,不做任何事;否则进行升级(或者强制降级)。

以上所有准备工作就绪,开始真正安装软件包,这个动作是在opkg_install_pkg() 函数中完成的,这个函数的原型如下:

int opkg_install_pkg(pkg_t * pkg, int from_upgrade)

第二个参数为是否升级,使用 install 命令时,即使有旧版本的软件,这个参数也为0;当使用 upgrade 命令时,该参数才为1. 这个标记有两个影响:

(1)     是否打印安装软件包时的信息:只有升级upgrade时打印;

(2)     如果已安装好最新版本的软件,或者没有强制降级,那么install会直接退出(无需做任何事情),而升级upgrade则强制把所有安装软件包的流程执行完(不明白意义在哪里?)。

安装过程如下:

(1)     检查软件包冲突:冲突是通过lists文件内的Conflicts字段表示的;

(2)     检查磁盘剩余空间是否足够:软件包占用的空间通过lists文件内的Installed-Size字段指定,磁盘空间通过statvfs()函数获取;

(3)     检查签名文件: 当 check_signature为非0 时,才会检查签名文件,可以在配置文件中加入“check_signature”来设置;

(4)     下载软件包:如果为手动模式,则无需下载;

(5)     更新文件所有者:将该软件包含的全部文件的所有者设为该软件包,并加入到opkg_config->file_hash中;

(6)     检查依赖:可以通过opkg命令行参数--nodeps忽略检查;

(7)     如果存在旧软件包,更新新、旧软件依赖:如果旧软件的某个依赖,新的软件不再依赖,检查该依赖是否还有其他软件依赖,如果没有,则可以安全删除该依赖;如果新软件的某个依赖,旧的软件没有,那么需要安装新的依赖(有点绕);

(8)     安装维护脚本:老式的ipk文件(*)是一个gz格式的压缩包,里面有3个文件:

~ $ tar -tvf binutils_2.24-2_x86.ipk
-rw-r--r-- bb/bb             4 2014-09-21 19:05 ./debian-binary
-rw-r--r-- bb/bb        838861 2014-09-21 19:05 ./data.tar.gz
-rw-r--r-- bb/bb           359 2014-09-21 19:05 ./control.tar.gz

其中 control.tar.gz 里面包含一个control文件:

~ $ tar -tvf control.tar.gz
drwxr-xr-x root/root         0 2014-09-21 19:05 ./
-rw-r--r-- root/root       298 2014-09-21 19:05 ./control

这个文件就是维护脚本(maintainer script),解压出来查看其内容:

~ $ tar xf control.tar.gz
~ $ cat control
Package: binutils
Version: 2.24-2
Depends: libc, objdump
Source: package/devel/binutils
Section: devel
Maintainer: Felix Fietkau <[email protected]>
Architecture: x86
Installed-Size: 838861
Description:  The Binutils package contains a linker, an assembler, and other tools for handling object files

这个文件被解压到info_dir下面,并以“该软件包的名字 . control”的方式命名。

除了control文件,有些软件包还包含配置文件,这个配置文件是交给UCI来管理的,其格式同UCI配置文件的格式,如果有配置文件,那么在control.tar.gz里面还会包含一个文件conffiles,比如以transmission-daemon为例,这个软件的control.tar.gz压缩包内有两个文件:

~ $ tar xvf control.tar.gz
./
./control
./conffiles

Conffiles这个文件里面包含了该软件所包含的配置文件,transmission-daemon只有一个配置文件,conffiles内容如下:

~ $ cat conffiles
/etc/config/transmission

在软件安装完成后,会根据这个配置文件进行某些设置。

注:这里使用了一个词“老式的ipk文件”,在v0.3.0版本的opkg上,软件包的格式是ar,并且不再支持gz格式,由于暂时找不到新式的软件包,所以暂时无法使用新的opkg安装老式的ipk软件。(非常郁闷!!)而v0.1.8版本的代码上支持两种格式,在libbb/unarchive.c的deb_extract()函数中:

char *
deb_extract(const char *package_filename, FILE *out_stream,
    const int extract_function, const char *prefix,
    const char *filename, int *err)
{
    if (strncmp(ar_magic,"!<arch>",7) == 0) {
    ... // 对 ar 格式文件的处理
    } else if (strncmp(ar_magic, "\037\213", 2) == 0) {
    ... // 对 gz 格式文件的处理
    }
    ...
}

所以,为了使opkg暂时支持老式的ipkg,只能对代码做些修改,找到libopkg/opkg_archive.c里面的open_outer()函数,在调用archive_read_support_format_ar()之前,加上这样一段代码:

r = archive_read_support_filter_gzip(outer);
if (r != ARCHIVE_OK) {
    opkg_msg(ERROR, "GZ filter not supported: %s\n",
             archive_error_string(outer));
    goto err_cleanup;
}

然后将调用archive_read_support_format_ar()改为调用archive_read_support_format_tar()函数:

//r = archive_read_support_format_ar(outer);
r = archive_read_support_format_tar(outer);

完整的diff如下:

重新编译opkg之后放到虚拟机里面运行,以安装 libpolarssl (这个软件包依赖较少,是个可供选择的测试软件)为例:

首先查看libpolarssl的状态,确保没有安装:

[email protected]:~# ./opkg info libpolarssl -f /etc/opkg.conf
Package: libpolarssl
Version: 1.3.9-2
Depends: libc
Status: unknown ok not-installed
Section: libs
Architecture: x86
MD5Sum: 1b6f66f94eb1f5a47959be09ccefbf2c
Size: 140082
Filename: libpolarssl_1.3.9-2_x86.ipk
Source: package/libs/polarssl
Description: The aim of the PolarSSL project is to provide a quality, open-source
 cryptographic library written in C and targeted at embedded systems.
 This package contains the PolarSSL library.

然后使用 install 命令安装:

[email protected]:~# ./opkg install libpolarssl -f /etc/opkg.conf
Installing libpolarssl (1.3.9-2) on root.
Downloading http://downloads.openwrt.org/barrier_breaker/14.07/x86/generic/packages/base/libpolarssl_1.3.9-2_x86.ipk.
Configuring libpolarssl.

可以查看该软件的control文件:

[email protected]:~# cat /var/lib/opkg/info/libpolarssl.control
Package: libpolarssl
Version: 1.3.9-2
Depends: libc
Source: /home/jow/relman/.cache/feeds/base/package/libs/polarssl
License: GPL-2.0+
Section: libs
Architecture: x86
Installed-Size: 140583
Description:  The aim of the PolarSSL project is to provide a quality, open-source
 cryptographic library written in C and targeted at embedded systems.
 This package contains the PolarSSL library.

成功。

(9)     安装数据文件:在第(8)步中已看到,ipk文件中除了control.tar.gz还有一个data.tar.gz,这个就是该软件包的所有文件(可执行程序、配置文件等),这一步就是把data.tar.gz内的文件解压到 root_dir 下面。解压完成后,还有两件事:a. 设置内存中该软件包的Essential字段;b. 将该软件包含的文件写到 info_dir 下面的 list文件中。所以在 info_dir 下面可以看到每个软件包都有两个文件:control文件和list文件。

(10)  检查冲突文件:主要是处理replace类型的软件包,函数为:
check_data_file_clashes_change() 。

(11)  设置配置文件:在安装软件的时候,用户如果之前安装过该软件的其他版本,那么可能对配置文件做过修改,因此这一步会进行适当的设置,当发现新旧配置文件有所不同时,除非用户明确指定强制、或忽略维护者设置(即新软件默认的设置),否则opkg会检查是否存在备份配置文件,如果存在,则默认使用备份的设置。备份的文件配置通常为“配置文件名称
-opkg.backup”,例如dhcp的配置文件为/etc/config/dhcp,那么它的备份文件命名为/etc/config/dhcp-opkg.backup。使用--force-maintainer来强制使用新配置,实用--ignore-maintainer来忽略新配置。

安装完成后,需要对软件进行配置,这个动作与opkg configure命令是一样的。配置动作只针对标记为UNPACKED的软件包,而新安装的软件包总是包含这个标记。软件包的配置,是通过运行postinst脚本,这个脚本位于info_dir下面,以“软件包名称 . postinst”命名,如果存在这个文件,opkg通过 system() 函数,执行命令:

sh -c $postinst configure

由于暂时没有发现包含postinst脚本的软件,所以这个过程暂时不知。

6.      升级 upgrade

更新操作基本上与安装相同,前面也已经提到了更新操作的一些不同之处,例如如果升级、强制降级的问题等。更新upgrade命令后面可以有参数,表示升级指定的软件包,如果软件包尚未安装,则不会执行安装动作;如果upgrade没有参数,那么opkg将尝试升级所有软件包(类似apt-get upgrade)。安装完成后,同样执行配置动作。如果有必要,更新status文件中该软件的状态。

7.      删除 remove

删除软件包的过程如下:

(1)     检查Essential 标记:如果该软件包为Essential的,除非用户强制删除(使用--force-removal-of-essential-packages参数),否则opkg拒绝删除该软件;

(2)     检查依赖:检查系统中有哪些其他的软件包依赖于要删除的软件包,如果有,那么打印出依赖于该软件包的其他软件包,然后结束删除动作;如果用户希望强制删除该软件以及依赖于该软件的所有其他软件,可以使用--force-removal-of-dependent-packages参数,opkg将首先删除依赖于该软件的其他软件;

(3)     执行删除前脚本:如果软件在删除前需要做一些动作,比如结束正在运行的进程等等,那么在info_dir下面会有一个“软件包名称 . prerm”的脚本,opkg首先执行:

sh -c $prerm remove

(4)     删除文件:该软件包含的全部文件保存在list_dir下面的“软件包名称 . list”文件中,每行一个,opkg会删除该软件所包含的全部文件,然后将 .list 文件一并删除;

(5)     执行删除后脚本:如果软件在删除后需要做一些清理动作,比如某些临时文件等等,那么在info_dir下面会有一个“软件包名称 . postrm”的脚本,opkg会执行:

sh -c $postrm remove

(6)     删除维护者脚本:删除在info_dir下面的control文件;

(7)     自动删除其他软件:如果用户在remove命令后加了--autoremove参数,opkg会尝试删除安装此软件包时自动安装的其他软件包(如果有)。

删除动作完成后,更新status文件中软件包的状态。

8.      清理 clean

清理就是删除cache文件夹下面的文件。

三、其他

1. 加锁

在opkg加载配置文件(函数opkg_conf_load())中,读取所有配置之前,会创建一个 /var/run/opkg.lock 的文件,并对该文件进行加锁(lockf()),在配置文件读取完毕后,释放锁,并删除这个文件。但不知为什么执行其他的操作,比如安装、删除等动作时,opkg反而不加锁了。

2. 生成软件包

生成Opkg格式的软件包,只需按照固定的格式填写一份Makefile即可6 7,官方文档说明的已经非常详细,此不赘述。

参考文献

  1. https://en.wikipedia.org/wiki/Opkg
  2. http://wiki.openwrt.org/doc/techref/opkg
  3. https://code.google.com/p/opkg/
  4. http://git.yoctoproject.org/cgit/cgit.cgi/opkg/
  5. http://downloads.yoctoproject.org/releases/opkg/opkg-0.3.0.tar.gz
  6. http://wiki.openwrt.org/doc/devel/packageshttp://wiki.openwrt.org/zh-cn/doc/devel/packages
  7. http://aboutchen.org/blog/package-a-c-program-for-openwrt/
时间: 2024-11-13 23:50:53

OPKG命令执行过程分析的相关文章

图库Gallery3D(Gallery2)分析(四) 菜单命令执行过程分析

该分析基于 Android4.2的Gallery2 1 菜单创建过程分析. Gallery的父类是AbstractGalleryActivity类,AbstractGalleryActivity的父类是Activity类.所以菜单创建是调用的AbstractGalleryActivity的菜单创建函数. public class AbstractGalleryActivity extends Activity implements GalleryContext { private static

Chromium硬件加速渲染的OpenGL命令执行过程分析

在Chromium中,由于GPU进程的存在,WebGL端.Render端和Browser端的GPU命令是代理给GPU进程执行的.Chromium将它们要执行的GPU命令进行编码,然后写入到一个命令缓冲区中,最后传递给GPU进程.GPU进程从这个命令缓冲区读出GPU命令之后,就进行解码,然后调用对应的OpenGL函数.本文就详细分析WebGL端.Render端和Browser端执行GPU命令的过程. 老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注! 在前面Ch

Android WebView执行GPU命令的过程分析

Android WebView使用的Chromium引擎,虽然没有自己的GPU进程或者线程,但是却可以执行GPU命令.原来,Android WebView会给它提供一个In-Process Command Buffer GL接口.通过这个接口,Chromium引擎就可以将GPU命令提交给App的Render Thread执行.本文接下来就详细分析Android WebView执行GPU命令的过程. 老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注! 从前面Ch

Mapreduce执行过程分析(基于Hadoop2.4)——(一)

1 概述 该瞅瞅MapReduce的内部运行原理了,以前只知道个皮毛,再不搞搞,不然怎么死的都不晓得.下文会以2.4版本中的WordCount这个经典例子作为分析的切入点,一步步来看里面到底是个什么情况. 2 为什么要使用MapReduce Map/Reduce,是一种模式,适合解决并行计算的问题,比如TopN.贝叶斯分类等.注意,是并行计算,而非迭代计算,像涉及到层次聚类的问题就不太适合了. 从名字可以看出,这种模式有两个步骤,Map和Reduce.Map即数据的映射,用于把一组键值对映射成另

ImageMagick 命令执行分析

ImageMagick是一款使用量很广的图片处理程序,很多厂商都调用了这个程序进行图片处理,包括图片的伸缩.切割.水印.格式转换等等.但近来有研究者发现,当用户传入一个包含『畸形内容』的图片的时候,就有可能触发命令注入漏洞. 国外的安全人员为此新建了一个网站: https://imagetragick.com/ ,不得不说,有些外国人蛮会玩的. 相对于之前的数个拥有『主页』的漏洞,这个洞确实不一般,确实是一个可以被利用的好洞,乌云主站上也爆出了数个被该漏洞影响的大厂商.我们先来分析一下它出现的原

我是如何从一个xss到某个浏览器的远程命令执行

0x01 前言:其实我是个小白平时就喜欢瞎搞,无意间碰到一个浏览器就想一探究竟,好了废话不多说开始!!! 0x02 可以看到我打开的新标签是怎么一个链接页面,既然是页面我是不可以XSS它呢? 于是我就打开了控制器输入XSS代码如下图: 输入payload发现能成功弹窗,我们来假设如果这个浏览器的本地域既然能弹XSS是不可以弹出其他的呢? 没错确实能弹出其他的,如图: 没错我弹出了他的CMD窗口,我们来ipconfig一下 可以看到成功弹出了,我们再来弹出它的计算器试试如图: 是不是很神奇就出来了

Metasploit溢出java RMI SERVER命令执行漏洞

利用java RMI SERVER命令执行漏洞,获得目标主机root权限. Java RMI Server的RMI注册表和RMI激活服务的默认配置存在安全漏洞,可被利用导致代码执行. 一.利用nmap工具扫描目标主机 1.1使用nmap命令对目标主机进行扫描.单击桌面空白处,右键菜单选择"在终端中打开". 1.2在终端中输入命令"nmap –sV  192.168.1.3",对目标主机进行端口扫描,发现开放1099端口. 1.3在终端中输入命令"msfco

[转]Windows中的命令行提示符里的Start命令执行路径包含空格时的问题

转自:http://www.x2009.net/articles/windows-command-line-prompt-start-path-space.html 当使用Windows 中的命令行提示符执行这段指令时(测试Start命令执行带空格的路径的程序或文件问题),第一行Start会成功执行,跳出记事本程序,而第二行,会 Start跳出一个新的命令提示符,标题上写着路径,但是不会执行任何命令,第三行Start命令行提示符会提示C:\Program文件不存在,提示无 法执行. start

Linux命令执行顺序— ||和&amp;&amp;和; 比较

Linux命令执行顺序— ||和&&和; command1 && command2: &&左边的command1执行成功(返回0表示成功)后,&&右边的command2才能被执行. command1 || command2: 如果||左边的command1执行失败(返回1表示失败),就执行&&右边的command2. command1;command2: 命令顺序执行 //第一个命令执行不成功,第二个命令才执行 [[email