diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c index 6633f2f5..9a8cc494 100644 --- a/lib/libalpm/handle.c +++ b/lib/libalpm/handle.c @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -135,10 +136,59 @@ int _alpm_handle_lock(alpm_handle_t *handle) FREE(dir); do { - handle->lockfd = open(handle->lockfile, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0000); + handle->lockfd = open(handle->lockfile, O_RDWR | O_CREAT | O_CLOEXEC, 0000); } while(handle->lockfd == -1 && errno == EINTR); - return (handle->lockfd >= 0 ? 0 : -1); + if(handle->lockfd < 0) { + /* failed to open lock for writing */ + return -1; + } + + /* lock opened */ + /* check the pid */ + pid_t pid = getpid(); + char buf[12]; + ssize_t bytesread = read(handle->lockfd, buf, sizeof(buf) - 1); + + buf[bytesread] = '\0'; + pid_t lockedpid; + + if(sscanf(buf, "%d", &lockedpid) == 1 && kill(lockedpid, 0) == 0 && errno != ESRCH) { + /* could not grab lock */ + /* old pid is still active */ + close(handle->lockfd); + handle->lockfd = -1; + return -1; + } + + /* write pid to lock */ + /* first allocate string for pid */ + char *pidstr = NULL; + int len = asprintf(&pidstr, "%d\n", pid); + + if(len == -1) { + _alpm_log(handle, ALPM_LOG_ERROR, "alloc failure: could not allocate pid"); + close(handle->lockfd); + unlink(handle->lockfile); + handle->lockfd = -1; + return -1; + } + + /* finally write pid to lock */ + int writtenbytes = write(handle->lockfd, pidstr, len); + + /* cleanup */ + FREE(pidstr); + + if(writtenbytes != len) { + _alpm_log(handle, ALPM_LOG_ERROR, "could not write pid to lockfile"); + close(handle->lockfd); + unlink(handle->lockfile); + handle->lockfd = -1; + return -1; + } + + return 0; } int SYMEXPORT alpm_unlock(alpm_handle_t *handle) diff --git a/scripts/libmakepkg/util/lock.sh.in b/scripts/libmakepkg/util/lock.sh.in new file mode 100644 index 00000000..b7a9437d --- /dev/null +++ b/scripts/libmakepkg/util/lock.sh.in @@ -0,0 +1,31 @@ +#!/bin/bash +# +# lock.sh - functions to handle pacman locking +# +# Copyright (c) 2024 Pacman Development Team +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +is_pacman_locked() { + local lockfile="$(pacman-conf DBPath)/db.lck" + if [[ -f $lockfile ]]; then + local pid=$(<"$lockfile") + # Check if the pid is valid and the process is still running + if [[ -n $pid && $pid =~ ^[0-9]+$ && -d /proc/$pid ]]; then + return 0 + fi + fi + return 1 +} \ No newline at end of file diff --git a/scripts/libmakepkg/util/meson.build b/scripts/libmakepkg/util/meson.build index a160fd06..38c02ff0 100644 --- a/scripts/libmakepkg/util/meson.build +++ b/scripts/libmakepkg/util/meson.build @@ -5,6 +5,7 @@ sources = [ 'config.sh.in', 'dirsize.sh.in', 'error.sh.in', + 'lock.sh.in', 'message.sh.in', 'option.sh.in', 'parseopts.sh.in', diff --git a/scripts/makepkg.sh.in b/scripts/makepkg.sh.in index 820d068f..85207d92 100644 --- a/scripts/makepkg.sh.in +++ b/scripts/makepkg.sh.in @@ -243,14 +243,11 @@ run_pacman() { else cmd=(su root -c "$cmdescape") fi - local lockfile="$(pacman-conf DBPath)/db.lck" - while [[ -f $lockfile ]]; do - local timer=0 + local timer=0 + while is_pacman_locked && (( timer < 10 )); do + (( ++timer )) msg "$(gettext "Pacman is currently in use, please wait...")" - while [[ -f $lockfile ]] && (( timer < 10 )); do - (( ++timer )) - sleep 3 - done + sleep 3 done fi "${cmd[@]}" diff --git a/scripts/pacman-db-upgrade.sh.in b/scripts/pacman-db-upgrade.sh.in index 16d17a58..a79ac312 100644 --- a/scripts/pacman-db-upgrade.sh.in +++ b/scripts/pacman-db-upgrade.sh.in @@ -30,6 +30,7 @@ declare -r myver='@PACKAGE_VERSION@' MAKEPKG_LIBRARY=${MAKEPKG_LIBRARY:-'@libmakepkgdir@'} # Import libmakepkg +source "$MAKEPKG_LIBRARY"/util/lock.sh source "$MAKEPKG_LIBRARY"/util/message.sh source "$MAKEPKG_LIBRARY"/util/parseopts.sh @@ -132,15 +133,18 @@ fi # strip any trailing slash from our dbroot dbroot="${dbroot%/}" + +# make sure pacman isn't running +if is_pacman_locked; then + die "$(gettext "Pacman lock file was found. Cannot run while pacman is running.")" +fi + # form the path to our lockfile location lockfile="${dbroot}/db.lck" -# make sure pacman isn't running -if [[ -f $lockfile ]]; then - die "$(gettext "Pacman lock file was found. Cannot run while pacman is running.")" -fi # do not let pacman run while we do this -touch "$lockfile" +# write our pid to the lockfile +echo $$ > "$lockfile" if [[ -f "${dbroot}"/local/ALPM_DB_VERSION ]]; then db_version=$(cat "${dbroot}"/local/ALPM_DB_VERSION) diff --git a/test/pacman/pmtest.py b/test/pacman/pmtest.py index 75912654..c8d5f23e 100644 --- a/test/pacman/pmtest.py +++ b/test/pacman/pmtest.py @@ -232,7 +232,7 @@ class pmtest(object): return files def run(self, pacman): - if os.path.isfile(util.PM_LOCK): + if util.ispacmanlocked(): tap.bail("\tERROR: another pacman session is on-going -- skipping") return diff --git a/test/pacman/util.py b/test/pacman/util.py index cabdf46c..0c5403b4 100644 --- a/test/pacman/util.py +++ b/test/pacman/util.py @@ -184,3 +184,15 @@ def mkdir(path): elif os.path.isfile(path): raise OSError("'%s' already exists and is not a directory" % path) os.makedirs(path, 0o755) + +# +# Locking +# +def ispacmanlocked(): + if not os.path.exists(PM_LOCK): + return False + + with open(PM_LOCK, 'r') as f: + pid = f.read().strip() + + return pid.isdigit() and os.path.exists(f'/proc/{pid}')