OPKG 软件包管理

常用命令
opkg update 更新可以获取的软件包列表
opkg upgrade 对已经安装的软件包升级
opkg list 获取软件列表
opkg install 安装指定的软件包
opkg remove 卸载已经安装的指定的软件包

opkg 工具 (一个 ipkg 变种) 是一个用来从本地软件仓库或互联网软件仓库上下载并安装 OpenWrt 软件包的轻量型软件包管理器。

GNU/Linux 用户可能会对 apt-getaptitudepacmanyum 等比较熟悉,也会看出其相似之处。它与 NSLU2 上同样用于嵌入式设备的 Optware 也有相似之处。OPKG 没有仅仅将软件安装到一个单独的路径(如:/opt),而是根文件系统上的一个完整的包管理器。它也包含了增加内核模块与驱动的可能性。OPKG 有时被称为 Entware ,但这主要是针对为嵌入式设备准备的 Entware 仓库。

opkg 试图在软件包仓库内来解决依赖关系。如果失败了,它将会报告一个错误并停止安装该软件包。

如果丢失第三方包的依赖关系,源码包依然可用的话,为了忽略依赖关系的错误可以使用 –force-depends 选项。

:!: 请注意: 如果你在使用一个 snapshot 、trunk 或 bleeding edge 版本,在仓库中的软件包适用内核版本比你的 Flash 上的内核更高,opkg install <pkg> 可能会失败。这种情况下,会报错『Cannot satisfy the following dependencies for…』。参见OpenWrt FAQ

调用

opkg 必须有一个子命令参数:

用法: opkg [选项…] 子命令 [参数]

其中的子命令可以是:

可以使用  glob 模式.

软件包管理

update 更新可用软件包列表
此命令直接为你安装的 OpenWrt 获取一个类似样例的文件,保存到内存分区的 /tmp/opkg-lists/snapshots 中。现在大约占用 1.3MB 空间。OPKG 需要它的内容才能安装、升级软件包或打印软件包信息。此文件内容须体现软件仓库中当前的可用软件包。为释放内存空间,你可以安全地删除这个文件,但不要忘记在安装软件前重新获取一个。
upgrade <pkgs> 升级软件包
要升级一组软件包,运行 opkg upgrade软件包名1 软件包名2 即可。
命令 opkg list-upgradable 可以获取一个可升级软件包的列表。
通常我们不推荐普通用户去升级软件包,因为一个典型的 OpenWrt 系统是存储在只读的 SquashFS 文件系统中。同时当升级工作做完后,使用了远超出在 SquashFS 文件系统或 JFFS2 文件系统默认安装基本软件包的容量。
因此,推荐用刷一个更新的 OpenWrt 固件来代替升级软件包。当然,升级已安装的软件包没有这种问题。Keep in mind though that for OpenWrt releases upgrading is for the most part not possible, since there is nothing to upgrade without changing the package repository. This is because the package repositories for OpenWrt’s releases are generally not updated. However, the package repository in the trunk snapshots are updated by the build bots to new versions very often, as this is where the packages are updated, like the OpenWrt builds themselves. Note however that for kernel packages updating can be a risky business as it may brick the device if the trunk build kernel is incompatible with the new upgraded kernel package. You should therefore only upgrade non-kernel packages.
install <pkgs|FQDN> 安装一个或多个软件包
例如:

opkg install hiawatha
opkg install http://downloads.openwrt.org/snapshots/trunk/ar71xx/packages/hiawatha_7.7-2_ar71xx.ipk
opkg install /tmp/hiawatha_7.7-2_ar71xx.ipk
configure <pkgs> 配置一个或多个未安装的包
remove <pkgs|globp> 移除一个或多个软件包
flag <flag> <pkgs> 标记一个或多个软件包
每次调用仅允许一个标记。可用标记有:
hold • noprune • user • ok • installed • unpacked

阅读有关 正则表达式http://en.wikipedia.org/wiki/Regular_expression

信息命令

list [pkg|globp] 列出可用软件包

Package name - Version - Description

描述信息 (Description) 可能包含换行,so using merely grep is inapt since grep is line-based.

list-installed 列出已安装软件包
list-upgradable 列出可升级的已安装软件包
list-changed-conffiles 列出用户修改过的配置文件
files <pkg> 列出属于软件包 <pkg> 的文件
仅适用于已安装的软件包。例如:

opkg files asterisk18
Package asterisk18 (1.8.4.4-1) is installed on root and has the following files:
/usr/lib/asterisk/modules/res_rtp_multicast.so
/usr/lib/asterisk/modules/codec_ulaw.so
/etc/asterisk/features.conf
/usr/lib/asterisk/modules/format_wav_gsm.so
/usr/lib/asterisk/modules/app_macro.so
/usr/lib/asterisk/modules/chan_sip.so
/usr/lib/asterisk/modules/app_dial.so
/usr/lib/asterisk/modules/app_playback.so
/usr/lib/asterisk/modules/format_gsm.so
/usr/lib/asterisk/modules/func_callerid.so
/usr/lib/asterisk/modules/func_timeout.so
/etc/asterisk/asterisk.conf
/etc/asterisk/modules.conf
/usr/lib/asterisk/modules/format_wav.so
/etc/asterisk/extensions.conf
/etc/init.d/asterisk
/etc/asterisk/manager.conf
/usr/lib/asterisk/modules/res_rtp_asterisk.so
/etc/asterisk/logger.conf
/etc/asterisk/rtp.conf
/usr/lib/asterisk/modules/codec_gsm.so
/etc/asterisk/indications.conf
/usr/lib/asterisk/modules/func_strings.so
/usr/lib/asterisk/modules/app_echo.so
/usr/lib/asterisk/modules/format_pcm.so
/etc/asterisk/sip_notify.conf
/etc/asterisk/sip.conf
/etc/default/asterisk
/usr/sbin/asterisk
/usr/lib/asterisk/modules/pbx_config.so
/usr/lib/asterisk/modules/func_logic.so
search <file|globp> 列出包含 <file>
info [pkg|globp] 显示软件包 <pkg> 的所有信息

Package: horst
Version: 2.0-rc1-2
Depends: libncurses
Provides:
Status: install user installed
Section: net
Architecture: ar71xx
Maintainer: Bruno Randolf <br1@einfach.org>
MD5Sum: 378cea9894ec971c419876e822666a6a
Size: 19224
Filename: horst_2.0-rc1-2_ar71xx.ipk
Source: feeds/packages/net/horst
Description: [horst] is a scanning and analysis tool for 802.11 wireless networks and
 especially IBSS (ad-hoc) mode and mesh networks (OLSR).

注1: size 是 gzip 压缩后的 tar 包尺寸。安装阶段,软件包会被展开解压缩,JFFS2 会再次将之压缩。
注2: 因 JFFS2 压缩是『透明』的,像 ls 之类的命令仍会返回未压缩的文件尺寸。

status [pkg|globp] 显示软件包 <pkg> 的状态
download <pkg> 下载软件包 <pkg> 到当前目录
compare-versions <v1> <op> <v2> 使用关系符 <=<>>==<< 或 >> 来比较两个版本 v1 和 v2 。
print-architecture 列出可安装软件包的结构
whatdepends [-A] [pkgname|pat]+ 仅适用于已安装的软件包。所以如果你想知道一个软件包及其所有依赖占用的空间,你只能将这个选项的输出分拆后传给 info 选项来计算。
whatdependsrec [-A] [pkgname|pat]+ 仅适用于已安装的软件包。所以所以如果你想知道一个软件包及其所有依赖占用的空间,你只能将这个选项的输出分拆后传给 info 选项来计算。
whatprovides [-A] [pkgname|pat]+
whatconflicts [-A] [pkgname|pat]+
whatreplaces [-A] [pkgname|pat]+

选项

短选项 长选项 描述
-A 查询全部软件包(不仅是已安装的)
-V[<level>] --verbosity[=<level>] 设置『唠叨』级别为 <level> 。
可用的级别有:
0 仅错误
1 普通消息(缺省情况)
2 有用的消息
3 调试
4 调试等级2
-f <conf_file> --conf <conf_file> 设置 opkg 配置文件为 <conf_file> 。缺省为 /etc/opkg.conf
--cache <directory> 指定包缓存
-d <dest_name> --dest <dest_name> 设置软件包安装、删除、升级的根目录为 <dest_name> 。
<dest_name> 应为已在配置文件中定义的目的名称(but can also be a directory name in a pinch)。
-o <dir> --offline-root <dir> 指定离线安装软件包的根目录。
--add-arch <arch>:<prio> 注册架构及其优先级
--add-dest <name>:<path> 注册目的名及其路径
强制性选项
--force-depends 在安装、删除软件包时无视失败的依赖
--force-maintainer 覆盖已经存在的配置文件
--force-reinstall 重安装软件包
--force-overwrite 覆盖其它软件包的文件
--force-downgrade 允许 opkg 降级软件包
--force-space 禁用可用空间检查
--force-checksum 忽略校验和失配
--force-postinstall 离线模式下仍运行安装后脚本
--noaction 无操作 – 仅测试
--download-only 无操作 – 仅下载
--nodeps 不跟踪依赖
--force-removal-of-dependent-packages 移除软件包的同时,移除其所有依赖软件包
--autoremove 移除自动安装以满足依赖的软件包
-t --tmp-dir 指定临时目录

举例

安装

要安装软件包,执行下列命令。注意重启设备会使可用软件包列表丢失,所以在试图安装软件包之前务必更新列表。

opkg update
opkg install <package>

搜索

  • opkg list 仅会显示  包名 — 版本 — 描述
  • opkg info 会显示全部可用信息

你可以直接使用 glob 模式,或者写个小的 Shell 脚本来使用正则表达式或进一步的进程信息。使用管道(|)以及 grep 、awk 或 sed 指令来过滤输出:

  • opkg list | grep pattern
  • opkg list | awk '/pattern/ {print $0}
  • opkg info kmod-ipt-* | awk '/length/ {print $0}
  • opkg list-installed | awk '{print $1}' | sed ':M;N;$!bM;s#\n# #g
  • var="packagename1 packagename2 packagename2"; for i in $var; do opkg info $i; done;
  • opkg depends dropbear 并不会起作用。

配置

调整仓库

唯一的配置文件是 /etc/opkg.conf ,它可能看起来是这样:

src/gz snapshots http://downloads.openwrt.org/snapshots/trunk/ar71xx/packages
dest root /
dest ram /tmp
lists_dir ext /var/opkg-lists
option overlay_root /overlay

本地仓库

你可以配置 opkg 从本地获取软件包:

src/gz local file:///path/to/packagesDirectory

Barrier_breaker 使用多个仓库,每个参考需要个唯一的标识符。逻辑上使用仓库原始的名称。如:

...
src/gz base file:///path/to/packages/directory/packages/base
src/gz luci file:///path/to/packages/directory/packages/luci
src/gz packages file:///path/to/packages/directory/packages/packages
src/gz oldpackages file:///path/to/packages/directory/packages/oldpackages
... etc ...
实例
r=44685
search="http://downloads.openwrt.org/snapshots/trunk/ar71xx/generic"
replace="file:///mnt/sdcard/shared/users/www/r$r"
sed -i -e "s!$search!$replace!" /etc/opkg.conf

共享给第二个路由器:

ln -s /mnt/sdcard/shared/users/www /www/pendrive

在第二个路由器上:

r=44685
search="downloads.openwrt.org/snapshots/trunk/ar71xx/generic"
replace="192.168.1.1/pendrive/r$r"
sed -i -e "s!$search!$replace!" /etc/opkg.conf

修改处理器架构

缺省情况下,opkg 仅操作标记架构为 all(即架构无关)或与目标平台相同的软件包。要从外引入兼容目标平台的软件包,可以在 opkg.conf 中使用 arch 选项追加可用的架构。

arch all 100
arch brcm4716 200
arch brcm47xx 300

上面的例子将允许将 brcm47xx(即 SoC 系列)软件包安装到 brcm4716(某特定的 SoC)平台。行尾的数字向 opkg 指定了优先级索引,以便在有多个可用包时选择最合适的那个。

代理支持

要透过代理使用 opkg,在 /etc/opkg.conf 中添加:

option http_proxy http://proxy.example.org:8080/
option ftp_proxy ftp://proxy.example.org:2121/

使用以下设置来进行代理服务器的认证:

option proxy_username xxxx
option proxy_password xxxx

有时,因为 busybox 中 wget 的限制,代理服务器的认证可能失败。这时,可以尝试在代理服务器的 URL 中传入用户名和密码:

option http_proxy http://username:password@proxy.example.org:8080/
option ftp_proxy http://username:password@proxy.example.org:2121/

安装目标

Extroot

使用 extroot 就足够了,无需进一步配置。

挂载点

opkg 有个不为那些 apt 系工具使用者熟悉的有用特性,它可以为任一软件包指定安装目的位置。

:!: 许多软件包不可重新定位,且可能无法干净地安装到非根目录下!像 LuCI 就会找不到它的模块,且若不手动修复就不能工作!使用 Extroot 吧!
:!: 别指望这个办法能『拆箱即用』,大多软件包需要额外的符号链接或 hack 才能在改变的路径下正常工作!

事实上,默认的 opkg.conf 包含三个目的位置:

dest root /
dest ram /tmp
dest mnt /mnt

目的位置行的格式很简单,关键字 dest 跟着目的位置名称(可以随便起),然后就是文件系统位置。任一如上配置好的目的位置都可以在 opkg 命令行中使用,如:

opkg install somepackage -d destination_name

dest 参数必须为 /etc/opkg.conf 中定义的目的位置名。如:-d ram 会将软件包安装到 /tmp/ 下。

若要在非根的目的位置安装内核模块,你可能要先读下<red>这个</red>。

详细说明

首先 mount 到外部文件系统,操作帮助见 Mounting Filesystems 。然后编辑 /etc/opkg.conf :

  • 在文件末尾加一行  dest usb /opt
  • 执行命令(假定你将外部文件系统挂载到了  /mnt/sda1 ):

    ln -s /mnt/sda1 /opt
  • 若你已通过  opkg -d ram 将软件包安装到了  tmp 文件夹,你需要加个新的 bin 和 lib 路径:

    export PATH=$PATH:/tmp/usr/bin/
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/tmp/usr/lib/
  • 编辑  /etc/profile ,为你的  PATH 变量追加挂载点:

    export PATH=<current default path>:/opt/bin:/opt/sbin:/opt/usr/bin:/opt/usr/sbin
    export LD_LIBRARY_PATH=<current default LD library path>:/opt/lib:/opt/usr/lib
  • 到此,你应可向挂载点安装新软件包了:

    opkg update
    opkg -dest usb install asterisk14  # or whatever else you want…
  • 若你向外部文件系统安装软件包,而该文件系统的  /etc/init.d 及(若启用) /etc/rc.d 下已有启动脚本,你需要设置个符号链接到  /etc/init.d 。如:

     ln -s /usb/etc/init.d/openvpn /etc/init.d/openvpn
  • 跟随软件包安装的库也会被安装到外部文件系统。这使得程序不会在引导时启动。你需要在每个启动脚本中手动设置  LD_LIBRARY_PATH :

     export LD_LIBRARY_PATH=/lib:/usr/lib:/tmp/lib:/tmp/usr/lib:/usb/lib:/usb/usr/lib
其它说明
  1. 挂载外部分区,见  Mounting Filesystems
  2. 修改  /etc/opkg.conf ,为预期的挂载点加入个新的目的位置或:
    1. 修改目的位置的根目录,指向你的 USB 磁盘。
    2. 将  /usr/lib/opkg 目录的内容复制到你的挂载点。比如 USB 磁盘。
    3. map directories where it is likely that they’ll receive lots of files to your usb drive, for example with mount –bind during startup.  /etc/rc.local could be one place where to map the directories.

example:

mount --bind /mnt/usb/root /root
mount --bind /mnt/usb/usr/local /usr/local
mount --bind /mnt/usb/home  /home

对于包含库的软件包,若安装到了外部磁盘,需要配置才能找到。 安装 ldconfig 并在 /etc/ld.so.conf 中列出附加库路径会解决这个问题。在新库安装后,必须执行过 ldconfig。它可以被安装到外部磁盘。在启动时执行一次它也许是个好主意。也许是在 /etc/rc.local 中。

内核模块

安装到非标准位置的内核模块有可能不会自动加载,这时就需要手动插入内核。例如,在把 libdevmapper 安装到 /mnt 下面后,插入此模块:

insmod /mnt/lib/modules/2.6.36.4/dm-mod.ko

你可以把守护进程和『内核』服务安装到内部 Flash 中,把可选的软件包安装到外部挂载点。启动过程中,若外部磁盘被挂载,外部目录的可执行文件(<挂载点>/[bin,sbin,usr/bin,usr/sbin])会被加进 PATH 变量。路由器通过这种方式继续提供相关服务,即使外部存储已经离线。

软件包若安装到外部驱动器中,可能引发下列问题:

  • file
    It installs its dependency libmagic to external drive too, but looks for that lib on root device.
    Fix: a script with be name “file” in a directory in PATH which is searched before the directory which contains the executable “file”, which calls file (full path) with option -m and path to the libmagic:

    #!/bin/sh
    /mnt/usb/usr/bin/file -m /mnt/usb/usr/share/file/magic "$@"

    after saving the script, and making it executable (chmod +x /path/of/script/file), executing once “hash -r” may or may not be needed.

  • netcat
    Not really a problem, but the link nc (busybox?) was still pointing to a file with wrong location.
    Moving the nc link to external drive (to the dir containing netcat) fixes that.
  • nfsd
    /etc/init.d/nfsd contains hardcoded paths to executables on  /usr/sbin
    Fix: edit  /etc/init.d/nfsd, by changing those paths to updated location.
  • lvm2
    /etc/init.d/lvm2 contains hardcoded paths to executables on  /sbin
    Fix: edit  /etc/init.d/lvm2, by changing these paths to updated location.

More information on installing and using ldconfig can be found in this article.

Some programs need additional config files to run, and you will have to create soft links between the root filesystem and the external storage one (e.g. USB). As an example, to successfully run Midnight Commander after installing it on a USB stick, you must run:

ln -s $USB/usr/share/terminfo/ /usb/share/
ln -s $USB/etc/mc /etc/mc

Troubleshooting

磁盘空间耗尽

若 opkg 耗尽了存储空间,它通常会报告个 Could not obtain administrative lock 错误,表示没有干净地恢复孤悬的文件锁。可以调用 rm /usr/lib/opkg/lock 命令来删除这个文件锁。

除此之外,opkg 可能不会删除之前安装的文件。
处理此事的一种办法是获取一个之前安装的文件的列表,然后删除它们。

将 URL 替换为相应的包:

(cd /; \
 wget -qO- http://downloads.openwrt.org/snapshots/trunk/ar7/packages/6in4_10-1_all.ipk | \
 tar -Oxz ./data.tar.gz | tar -tz | xargs rm)

然而,上述代码并没有可靠地删除那些伴随软件包安装进去的依赖软件包。它也遗留下了那些空目录。这个脚本尝试修正这些问题:

#!/bin/sh
#takes one argument/parameter: the name of the package which didn't install correctly and should be removed along with its dependencies
#do opkg update first
#example: ./opkgremovepartlyinstalledpackage.sh pulseaudio-daemon

#get list of all packages that would be installed along with package x
opkg update
PACKAGES=`opkg --force-space --noaction install $1 | grep "http:" | cut -f 2 -d ' ' | sed 's/.$//'`
for i in $PACKAGES
do
        LIST=`wget -qO- $i | tar -Oxz ./data.tar.gz | tar -tz | sort -r | sed 's/^./\/overlay/'`
        for f in $LIST
        do
                if [ -f $f ]
                then
                        echo "Removing file $f"
                        rm -f $f
                fi
                if [ -d $f ]
                then
                        echo "Try to remove directory $f (will only work on empty directories)"
                        rmdir $f
                fi
        done
done
echo "You may need to reboot for the free space to become visible"

将之保存在你的 OpenWrt 设备上,命名为 opkgclean.sh ,设置为可执行 chmod +x ./opkgclean.sh ,然后你可以这样执行它 ./opkgclean.sh <package-name> 。

其它

要使 opkg 的输出有颜色,可以用 http://pastie.org/5464938.

命令

To find installed pkgs of a specific install target (ex. USB) (DRAWBACK!!! if any update availlable, it will update the package, just be warned!!!):

for pkg in `opkg list-installed | sed -e "s/^([0-9A-Za-z\-]+) - .*$/\1/p" -n`; do opkg install $pkg; done | grep -i installed\ in\ <TARGET>