6

我有一个我创建的 deb 包。从 postinst 脚本,我想运行:

apt-get update

该软件包通过在 /etc/apt/apt.conf.d/ 中放置一个文件来为 apt 系统添加一个代理。我想强制 apt 系统执行相当于“apt-get update”的操作。但是,我不能直接从 postinst 运行该命令,因为 apt lock 文件已由正在安装此软件包的 dpkg 放置!是否有一些 debconf 工具/命令可以做到这一点?

作为奖励,我希望能够从 preinst/postinst 中删除一个包:

apt-get remove popularitycontest

注意 - 这个包是用于一个内部项目的 - 不是一个将被释放到野外或提交给 Debian 的 deb。

4

4 回答 4

3

无法从包脚本(preinst、postinst、prerm、postrm...)中调用 APT 命令(apt-get、aptitude..)。

启用 so 会引发很多问题,特别是对于包安装的依赖和排序。

通过使用适当的包(预)依赖项或通过为您的用户提供易于使用的工具(如模块助手和其他工具),已经使用了各种解决方法。

在您的情况下,您的软件包可能会与流行性竞赛冲突以卸载它。另外,如果你的用户有“你的”包,这意味着他们已经在他们的sources.list中添加了一个条目,所以他们可以添加另一个!

于 2013-09-05T16:53:09.090 回答
1

嵌套 dpkg/apt-get 方法

正如富兰克林指出的那样,启用它会产生很多问题,但是如果它是供内部使用的,您的包可以处理它们,仍然会很吓人,所以请尽量避免它,因为该方法将涉及:

  1. 暂时禁用 apt/dpkg 锁
  2. 更新/安装/删除软件
  3. 合并对 ​​dpkg 数据库的更改
  4. 启用返回 apt/dpkg 锁
  5. 利润

1. 暂时禁用 apt/dpkg 锁

您需要临时移动以下文件:

  • /var/lib/dpkg/lock
  • /var/lib/dpkg/updates/
  • /var/cache/apt/archives/lock

2.更新/安装/删除软件

现在你可以运行apt-get/dpkg,如果你只想更新你可以运行的索引apt-get update,重新启用锁并继续,否则准备好处理dpkg数据库。

如果要安装/删除软件,则需要考虑 apt-get/dpkg 将其最终状态写入:

  • /var/lib/dpkg/状态

假设您有一个包含以下软件包的系统:firefox, htop, curl,并且假设您的软件包已foo删除curl,因此在安装软件包时,您应该拥有firefox, htop, foo但是由于 dpkg 每个实例都会更新其状态一次,因此您的嵌套状态将被父进程覆盖而留下跟随状态firefox, htop, curl, foo

所以,你不会有 curl 文件,但 dpkg 仍然会安装包,这也会发生在新的软件和依赖项中。

假设您的foo软件包安装apache2依赖于apache2-data,您希望它们在您的 dpkg 数据库中为:firefox, htop, curl, foo, apache2, apache2-data,但是您将拥有firefox, htop, curl, foo,嵌套输出被父进程覆盖,该父进程只知道安装foo

3.合并对dpkg数据库的更改

为了避免这种混淆,您需要手动处理 dpkg 更改,而且由于文件被 apt-get/dpkg 实例覆盖,您需要将更改保存在不同的位置并应用它们仅在主 apt-get/dpkg 实例完成后才将其复制到原始文件,因为您的脚本将在此之前结束,因此您需要留下一个 cronjob 条目或手工制作的守护程序。

4.启用回apt/dpkg锁

5. 利润

由于上述过程可能会有些麻烦,因此我将留下一个幼稚的实现,请务必在使用之前了解它并期待极端情况。

package=my-pkg

_dpkg_suspend_process() {
    #unlock standard files
    busybox mv     /var/lib/dpkg/lock           /var/lib/dpkg/lock.suspended
    busybox rm -rf /var/lib/dpkg/updates.suspended/
    busybox mv     /var/lib/dpkg/updates/       /var/lib/dpkg/updates.suspended
    busybox mkdir  /var/lib/dpkg/updates/
    busybox mv     /var/cache/apt/archives/lock /var/cache/apt/archives/lock.suspended

    #debconf missing file descriptors workaround
    busybox cp /usr/share/debconf/confmodule       /usr/share/debconf/confmodule.bk  
    busybox cp /usr/share/minos/debconf/confmodule /usr/share/debconf/confmodule

    #while apt is being executed it modifies the status file which brings conflicts
    #to new packages if they're installed/removed in abused apt instances, therefore
    #the status-old file (which represent the original state in which the first
    #apt instance was launched) is used to create temporal diffs which will be merged
    #at the end
    busybox cp     /var/lib/dpkg/status         /var/lib/dpkg/status.suspended
    busybox cp     /var/lib/dpkg/status-old     /var/lib/dpkg/status-orig
    busybox cp     /var/lib/dpkg/status-orig    /var/lib/dpkg/status
}

_dpkg_continue_process() {
    #relock standard files
    busybox rm -rf /var/lib/dpkg/updates
    busybox mv     /var/lib/dpkg/lock.suspended           /var/lib/dpkg/lock
    busybox mv     /var/lib/dpkg/updates.suspended        /var/lib/dpkg/updates
    busybox mv     /var/cache/apt/archives/lock.suspended /var/cache/apt/archives/lock
    busybox mv     /var/lib/dpkg/status.suspended         /var/lib/dpkg/status

    #debconf missing file descriptors workaround
    busybox mv     /usr/share/debconf/confmodule.bk       /usr/share/debconf/confmodule

    #keep status-old file to survive multiple abused apt instances
    busybox mv     /var/lib/dpkg/status-orig              /var/lib/dpkg/status-old
}

_dpkg_sync_status_db() {
    _dpkg_sync_status_db_script="/var/lib/dpkg/dpkg-sync-status-db"
    _dpkg_sync_status_db_script_generator() {
        printf "%s\\n" "#!/bin/sh"
        printf "%s\\n" "#autogenerated by ${package}: $(date +%d-%m-%Y:%H:%M)"
        printf "\\n"
        printf "%s\\n" '##close stdout'
        printf "%s\\n" '#exec 1<&-'
        printf "%s\\n" '##close stderr'
        printf "%s\\n" '#exec 2<&-'
        printf "%s\\n" '##open stdout as $log_file file for read and write.'
        printf "%s\\n" "#exec 1<> /tmp/${package}.\${$}.debug"
        printf "%s\\n" '##redirect stderr to stdout'
        printf "%s\\n" '#exec 2>&1'
        printf "%s\\n" '#set -x #enable trace mode'
        printf "\\n"
        printf "%s\\n" "while fuser /var/lib/dpkg/lock >/dev/null 2>&1; do sleep 1; done"
        printf "\\n"
        printf "%s\\n" 'pkgs__add="$(cat /var/lib/apt/apt-add-queue)"'
        printf "%s\\n" 'if [ -n "${pkgs__add}" ]; then'
        printf "%s\\n" '  for pkg in $pkgs__add; do'
        printf "%s\\n" '    if ! busybox grep "^Package: ${pkg}$" /var/lib/dpkg/status >/dev/null 2>&1; then'
        printf "%s\\n" '      busybox sed -n "/Package: ${pkg}$/,/^$/p" \'
        printf "%s\\n" "      /var/lib/dpkg/status-append-queue >> /var/lib/dpkg/status"
        printf "%s\\n" "    fi"
        printf "%s\\n" "  done"
        printf "%s\\n" "fi"
        printf "\\n"
        printf "%s\\n" 'pkgs__rm="$(cat /var/lib/apt/apt-rm-queue)"'
        printf "%s\\n" 'if [ -n "${pkgs__rm}" ]; then'
        printf "%s\\n" '  for pkg in $pkgs__rm; do'
        printf "%s\\n" '    busybox sed -i "/Package: ${pkg}$/,/^$/d" /var/lib/dpkg/status'
        printf "%s\\n" "  done"
        printf "%s\\n" "fi"
        printf "\\n"
        printf "%s\\n" "mv /var/lib/apt/apt-add-queue /var/lib/apt/apt-add-queue.bk"
        printf "%s\\n" "mv /var/lib/apt/apt-rm-queue  /var/lib/apt/apt-rm-queue.bk"
        printf "%s\\n" "mv /var/lib/dpkg/status-append-queue /var/lib/dpkg/status-append-queue.bk"
        printf "\\n"
        printf "%s\\n" "rm -rf /var/lib/apt/apt-add-queue /var/lib/apt/apt-rm-queue"
        printf "%s\\n" "rm -rf ${_dpkg_sync_status_db_script}"
    }

    _dpkg_sync_status_db_script_generator > "${_dpkg_sync_status_db_script}"
    chmod +x "${_dpkg_sync_status_db_script}"
    _daemonize /bin/sh -c "${_dpkg_sync_status_db_script}"
}

_daemonize() {
    #http://blog.n01se.net/blog-n01se-net-p-145.html
    [ -z "${1}" ] && return 1
    (   #1. fork, to guarantee the child is not a process
        #group leader, necessary for setsid) and have the
        #parent exit (to allow control to return to the shell)

        #2. redirect stdin/stdout/stderr before running child
        [ -t 0 ] && exec  </dev/null
        [ -t 1 ] && exec  >/dev/null
        [ -t 2 ] && exec 2>/dev/null
        if ! command -v "setsid" >/dev/null 2>&1; then
            #2.1 guard against HUP and INT (in child)
            trap '' 1 2
        fi

        #3. ensure cwd isn't a mounted fs so it does't block
        #umount invocations
        cd /

        #4. umask (leave this to caller)
        #umask 0

        #5. close unneeded fds
        #XCU 2.7 Redirection says: open files are represented by
        #decimal numbers starting with zero. The largest possible
        #value is implementation-defined; however, all
        #implementations shall support at least 0 to 9, inclusive,
        #for use by the application.
        i=3; while [ "${i}" -le "9" ]; do
            eval "exec ${i}>&-"
            i="$(($i + 1))"
        done

        #6. create new session, so the child has no
        #controlling terminal, this prevents the child from
        #accesing a terminal (using /dev/tty) and getting
        #signals from the controlling terminal (e.g. HUP, INT)
        if command -v "setsid" >/dev/null 2>&1; then
            exec setsid "$@"
        elif command -v "nohup" >/dev/null 2>&1; then
            exec nohup "$@" >/dev/null 2>&1
        else
            if [ ! -f "${1}" ]; then
                "$@"
            else
                exec "$@"
            fi
        fi
    ) &
    #2.2 guard against HUP (in parent)
    if ! command -v "setsid" >/dev/null 2>&1 \ &&
       ! command -v "nohup"  >/dev/null 2>&1; then
        disown -h "${!}"
    fi
}

_apt_add_queue() {
    for pkg in "${@}"; do
        if  busybox grep "${pkg}" /var/lib/apt/apt-rm-queue >/dev/null 2>&1; then
            busybox sed -i "/^${pkg}$/d" /var/lib/apt/apt-rm-queue
        else
            if ! busybox grep "^Package: ${pkg}$" /var/lib/dpkg/status >/dev/null 2>&1; then
                printf "%s\\n" "${pkg}" >> /var/lib/apt/apt-add-queue
            fi
        fi
    done; unset pkg
}

_apt_rm_queue() {
    for pkg in "${@}"; do
        if  busybox grep "${pkg}" /var/lib/apt/apt-add-queue >/dev/null 2>&1; then
            busybox sed -i "/^${pkg}$/d" /var/lib/apt/apt-add-queue
        else
            if busybox grep "^Package: ${pkg}$" /var/lib/dpkg/status >/dev/null 2>&1; then
                printf "%s\\n" "${pkg}" >> /var/lib/apt/apt-rm-queue
            fi
        fi
    done; unset pkg
}

_apt_install() {
    [ -z "${1}" ] && return
    _apt_add_queue $(printf "%s\\n" "${@}" | busybox sed "s:${package}::g")
}

_apt_purge() {
    [ -z "${1}" ] && return
    _apt_rm_queue $(printf "%s\\n" "${@}" | busybox sed "s:${package}::g")
}

_apt_run() {
    [ ! -f /var/lib/apt/apt-add-queue ] && [ ! -f /var/lib/apt/apt-rm-queue ] && return

    pkgs__add="$(cat /var/lib/apt/apt-add-queue 2>/dev/null)"
    if [ -n "${pkgs__add}" ]; then
        _dpkg_suspend_process
        busybox awk '/^Package: /{print $2}' /var/lib/dpkg/status | \
            busybox sort > /var/lib/dpkg/status-pkgs.orig
        _apt_run__output="$(DEBIAN_FRONTEND=noninteractive apt-get install  \
            --no-install-recommends -y -o Dpkg::Options::="--force-confdef" \
            -o Dpkg::Options::="--force-confold" --force-yes ${pkgs__add} 2>&1)" || \
            printf "%s\\n" "${_apt_run__output}" >&2
        busybox awk '/^Package: /{print $2}' /var/lib/dpkg/status | \
            busybox sort > /var/lib/dpkg/status-pkgs.current
        _dpkg__added_pkgs="$(busybox diff -Naur /var/lib/dpkg/status-pkgs.orig \
            /var/lib/dpkg/status-pkgs.current | busybox awk '/^\+[a-zA-Z]/{gsub("^+","");print;}')"
        busybox rm -rf /var/lib/dpkg/status-pkgs*
        #add dependencies
        if [ -n "${_dpkg__added_pkgs}" ]; then
            printf "%s\\n" "${_dpkg__added_pkgs}" >> /var/lib/apt/apt-add-queue
            printf "%s\\n" "$(busybox sort /var/lib/apt/apt-add-queue | busybox uniq)" \
                > /var/lib/apt/apt-add-queue
        fi

        #extract dpkg status output to append it at the end
        for pkg in $_dpkg__added_pkgs; do
            busybox sed -n '/Package: '"${pkg}"'$/,/^$/p' /var/lib/dpkg/status \
                >> /var/lib/dpkg/status-append-queue
        done
        _dpkg_continue_process
    fi

    pkgs__rm="$(cat /var/lib/apt/apt-rm-queue 2>/dev/null)"
    if [ -n "${pkgs__rm}" ]; then
        _dpkg_suspend_process
        busybox awk '/^Package: /{print $2}' /var/lib/dpkg/status | \
            busybox sort > /var/lib/dpkg/status-pkgs.orig
        _apt_run__output="$(DEBIAN_FRONTEND=noninteractive apt-get purge \
            -y ${pkgs__rm} 2>&1)" || printf "%s\\n" "${_apt_run__output}" >&2
        busybox awk '/^Package: /{print $2}' /var/lib/dpkg/status | \
            busybox sort > /var/lib/dpkg/status-pkgs.current
        _dpkg__removed_pkgs="$(busybox diff -Naur /var/lib/dpkg/status-pkgs.orig \
            /var/lib/dpkg/status-pkgs.current | busybox awk '/^-[a-zA-Z]/{gsub("^-","");print;}')"
        busybox rm -rf /var/lib/dpkg/status-pkgs*
        #remove dependencies
        if [ -n "${_dpkg__removed_pkgs}" ]; then
            printf "%s\\n" "${_dpkg__removed_pkgs}" >> /var/lib/apt/apt-rm-queue
            printf "%s\\n" "$(busybox sort /var/lib/apt/apt-rm-queue | busybox uniq)" \
                > /var/lib/apt/apt-rm-queue
        fi
        _dpkg_continue_process
    fi

    _dpkg_sync_status_db
}

_apt_install foo bar
_apt_purge   ugly packages
_apt_run
于 2017-01-17T20:24:30.397 回答
0

守护进程 dpkg/apt-get 方法

正如富兰克林指出的那样,启用它会产生很多问题,但是如果它是供内部使用的,您的包可以处理它们,仍然会很吓人,所以请尽量避免它,因为该方法将涉及:

  1. 定义和填充安装/删除 apt-get 队列
  2. 或者依赖aptdaemon或类似工具 [可选]
  3. 留下一个守护进程,它作用于安装后定义的 apt-get/dpkg 队列
  4. 或者添加一个删除自身的 cronjob 条目 [可选]
  5. 利润

1. 定义和填充安装/删除 apt-get 队列

由于您不会运行额外的 dpkg/apt-get 实例,您可以定义临时文件以用作 apt/dpkg 队列,例如:

  • /var/lib/apt/apt-add-queue
  • /var/lib/apt/apt-rm-queue

2.依赖aptdaemon

如果您选择依赖 aptdaemon 或类似工具,您需要将它添加到您的控制文件中的PreDepends字段并从您的维护者脚本中调用它。

3. 留下一个守护进程,它作用于安装后定义的 apt-get/dpkg 队列

您的守护进程需要等待/var/lib/dpkg/lock文件解锁才能运行其 apt-get/dpkg 实例。

4.添加一个删除自己的cronjob条目

5. 利润

由于上述过程可能会有些麻烦,因此我将留下一个幼稚的实现,请务必在使用之前了解它并期待极端情况。

package=my-pkg

_daemonize() {
    #http://blog.n01se.net/blog-n01se-net-p-145.html
    [ -z "${1}" ] && return 1
    (   #1. fork, to guarantee the child is not a process
        #group leader, necessary for setsid) and have the
        #parent exit (to allow control to return to the shell)

        #2. redirect stdin/stdout/stderr before running child
        [ -t 0 ] && exec  </dev/null
        [ -t 1 ] && exec  >/dev/null
        [ -t 2 ] && exec 2>/dev/null
        if ! command -v "setsid" >/dev/null 2>&1; then
            #2.1 guard against HUP and INT (in child)
            trap '' 1 2
        fi

        #3. ensure cwd isn't a mounted fs so it does't block
        #umount invocations
        cd /

        #4. umask (leave this to caller)
        #umask 0

        #5. close unneeded fds
        #XCU 2.7 Redirection says: open files are represented by
        #decimal numbers starting with zero. The largest possible
        #value is implementation-defined; however, all
        #implementations shall support at least 0 to 9, inclusive,
        #for use by the application.
        i=3; while [ "${i}" -le "9" ]; do
            eval "exec ${i}>&-"
            i="$(($i + 1))"
        done

        #6. create new session, so the child has no
        #controlling terminal, this prevents the child from
        #accesing a terminal (using /dev/tty) and getting
        #signals from the controlling terminal (e.g. HUP, INT)
        if command -v "setsid" >/dev/null 2>&1; then
            exec setsid "$@"
        elif command -v "nohup" >/dev/null 2>&1; then
            exec nohup "$@" >/dev/null 2>&1
        else
            if [ ! -f "${1}" ]; then
                "$@"
            else
                exec "$@"
            fi
        fi
    ) &
    #2.2 guard against HUP (in parent)
    if ! command -v "setsid" >/dev/null 2>&1 \ &&
       ! command -v "nohup"  >/dev/null 2>&1; then
        disown -h "${!}"
    fi
}

_apt_add_queue() {
    for pkg in "${@}"; do
        if  busybox grep "${pkg}" /var/lib/apt/apt-rm-queue >/dev/null 2>&1; then
            busybox sed -i "/^${pkg}$/d" /var/lib/apt/apt-rm-queue
        else
            if ! busybox grep "^Package: ${pkg}$" /var/lib/dpkg/status >/dev/null 2>&1; then
                printf "%s\\n" "${pkg}" >> /var/lib/apt/apt-add-queue
            fi
        fi
    done; unset pkg
}

_apt_rm_queue() {
    for pkg in "${@}"; do
        if  busybox grep "${pkg}" /var/lib/apt/apt-add-queue >/dev/null 2>&1; then
            busybox sed -i "/^${pkg}$/d" /var/lib/apt/apt-add-queue
        else
            if busybox grep "^Package: ${pkg}$" /var/lib/dpkg/status >/dev/null 2>&1; then
                printf "%s\\n" "${pkg}" >> /var/lib/apt/apt-rm-queue
            fi
        fi
    done; unset pkg
}

_apt_install() {
    [ -z "${1}" ] && return
    _apt_add_queue $(printf "%s\\n" "${@}" | busybox sed "s:${package}::g")
}

_apt_purge() {
    [ -z "${1}" ] && return
    _apt_rm_queue $(printf "%s\\n" "${@}" | busybox sed "s:${package}::g")
}

_apt_daemon() {
    [ ! -f /var/lib/apt/apt-add-queue ] && [ ! -f /var/lib/apt/apt-rm-queue ] && return

    _apt_daemon__script="/var/lib/apt/apt-daemon"
    _apt_daemon_generator() {
        printf "%s\\n" "#!/bin/sh"
        printf "%s\\n" "#autogenerated by ${package}: $(date +%d-%m-%Y:%H:%M)"
        printf "\\n"
        printf "%s\\n" "if [ -f /var/lib/apt/apt-add-queue ]; then"
        printf "%s\\n" "  cp /usr/share/debconf/confmodule /usr/share/debconf/confmodule.bk"
        printf "%s\\n" "  cp /usr/share/minos/debconf/confmodule /usr/share/debconf/confmodule"
        printf "%s\\n" "  while fuser /var/lib/dpkg/lock >/dev/null 2>&1; do sleep 1; done"
        printf "%s\\n" "  DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \\"
        printf "%s\\n" "      -o Dpkg::Options::=\"--force-confdef\" \\"
        printf "%s\\n" "      -o Dpkg::Options::=\"--force-confold\" \\"
        printf "%s\\n" "      --force-yes \$(cat /var/lib/apt/apt-add-queue)"
        printf "%s\\n" "  mv /usr/share/debconf/confmodule.bk /usr/share/debconf/confmodule"
        printf "%s\\n" "fi"
        printf "\\n"
        printf "%s\\n" "if [ -f /var/lib/apt/apt-rm-queue ]; then"
        printf "%s\\n" "  cp /usr/share/debconf/confmodule /usr/share/debconf/confmodule.bk"
        printf "%s\\n" "  cp /usr/share/minos/debconf/confmodule /usr/share/debconf/confmodule"
        printf "%s\\n" "  while fuser /var/lib/dpkg/lock >/dev/null 2>&1; do sleep 1; done"
        printf "%s\\n" "    DEBIAN_FRONTEND=noninteractive apt-get purge -y \$(cat /var/lib/apt/apt-rm-queue)"
        printf "%s\\n" "  mv /usr/share/debconf/confmodule.bk /usr/share/debconf/confmodule"
        printf "%s\\n" "fi"
        printf "\\n"
        printf "%s\\n" "mv /var/lib/apt/apt-add-queue /var/lib/apt/apt-add-queue.bk"
        printf "%s\\n" "mv /var/lib/apt/apt-rm-queue  /var/lib/apt/apt-rm-queue.bk"
        printf "\\n"
        printf "%s\\n" "rm -rf /var/lib/apt/apt-add-queue"
        printf "%s\\n" "rm -rf /var/lib/apt/apt-rm-queue"
        printf "%s\\n" "rm -rf \"${_apt_daemon__script}\""
    }
    _apt_daemon_generator > "${_apt_daemon__script}" && chmod +x "${_apt_daemon__script}"
    _daemonize /bin/sh   -c "${_apt_daemon__script}"
    printf "%s\\n" "${package}: package changes will be applied shortly upon completion of this apt/dpkg instance"
    printf "%s\\n" "${package}: DO NOT POWEROFF the system until it completes"
}

_apt_install foo bar
_apt_purge   ugly packages
_apt_daemon
于 2017-01-17T20:53:45.487 回答
0

是的,你不应该这样做——但如果你必须这样做——这里是你可以从脚本apt-get内部调用的方法。postinst同样的方法也适用于dpkg直接调用。

这个想法是启动一个子shell,它在后台耐心等待,直到所有安装活动结束,然后执行它的操作。

((
   # wait for install to finish
   while pgrep -x 'dpkg|apt|apt-get' > /dev/null; do sleep 1; done
   export DEBIAN_FRONTEND=noninteractive
   # make sure no one else is locking the dpkg database
   flock --exclusive --close /var/lib/dpkg/lock \
       apt-get remove popularitycontest
) 2>&1 >/dev/null </dev/null &)

显然,如果有多个包尝试此技巧,这将导致问题。所以正如我上面所说,你真的不应该这样做。

于 2022-02-01T16:43:55.493 回答