Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions docs/man/rpm-config.5.scd
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,12 @@ _macro path_ uses this to achieve the following hierarchy of settings:

The default _macro path_ can be inspected with *rpm --showrc|grep ^Macro*.

In older versions of rpm, the path of per-user macros was _~/.rpmmacros_.
This is still processed if it exists and the new configuration directory
does not exist.
In older versions of rpm, the path of per-user macros was
_~/.rpmmacros_. RPM will try to move this file to the new
locations. If this is not possible it will create
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe mention that XDG_CONFIG_HOME is the new location.

_~/.rpmconfig_migration_lock_ to prevent attempting the migration over
and over again. In this case the file is still processed if it exists
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose "this file" is the macros file, correct? If so, I'd suggest mentioning it explicitly here, to avoid confusion.

Also, I wonder if we should mention here (briefly) what happens when the lock file is removed. As it is now, the migration would only be retried if the new directory in XDG_CONFIG_HOME doesn't exist. That means, a borked migration would have to be fixed manually by the user.

and the new configuration directory does not exist.

# CONFIGURATION
The following configurables are supported for the *rpm* runtime (as
Expand Down
7 changes: 5 additions & 2 deletions docs/man/rpm-rpmrc.5.scd
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ configuration:
. User specific configuration

In older rpm versions the path of per-user rpmrc was _~/.rpmrc_.
This is still processed if it exists and the new configuration directory
does not exist.
RPM will try to move this file to the new locations. If this is not
possible it will create _~/.rpmconfig_migration_lock_ to prevent
attempting the migration over and over again. In this case the file is
still processed if it exists and the new configuration directory does
not exist.

# CONFIGURATION
_ARCH_ and _OS_ relate to *uname*(2) machine and operating system
Expand Down
118 changes: 113 additions & 5 deletions lib/rpmrc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <string>
#include <unordered_map>
#include <vector>
#include <filesystem>

#include <fcntl.h>
#include <stdarg.h>
Expand Down Expand Up @@ -48,6 +49,8 @@
using wrlock = std::unique_lock<std::shared_mutex>;
using rdlock = std::shared_lock<std::shared_mutex>;

namespace fs = std::filesystem;

static const char * defrcfiles = NULL;
const char * macrofiles = NULL;

Expand Down Expand Up @@ -358,6 +361,109 @@ const char * lookupInDefaultTable(const char * name,
return name;
}

static int moveConfigFiles(std::string userdir)
{
/* Don't migrate in scripts */
if (!isatty(STDIN_FILENO))
return -1;
/* Don't migrate with root priviledges */
if (geteuid() == 0)
return -1;

fs::path home = std::getenv("HOME");
std::error_code ec;
std::error_code ec2;
fs::path oldmacros = home / ".rpmmacros";
fs::path oldrpmrc = home / ".rpmrc";
fs::path lockfile = home / ".rpmconfig_migration_lock";
fs::path macros_target = "";
fs::path rpmrc_target = "";

bool move_rpmrc = false;
bool move_macros = false;

if (userdir.substr(0, 2) == "~/")
userdir.replace(0, 1, home);

fs::path userdir_path = userdir;
fs::path newmacros = userdir_path / "macros";
fs::path newrpmrc = userdir_path / "rpmrc";

if (!fs::is_regular_file(oldmacros, ec) && !fs::is_regular_file(oldrpmrc, ec))
return -1;

int fd = open(lockfile.c_str(), O_CREAT|O_EXCL|O_WRONLY, 0644);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps silly, but couldn't we reuse the rpmlock mechanism here? That said, I'm not familiar with the inner workings of it myself, so not sure it's a good fit, just something that comes to mind.

if (fd < 0)
return -1;
close(fd);

/* recheck after acquiring lock */
if (fs::exists(userdir_path, ec) ||
(!fs::is_regular_file(oldmacros, ec) &&
!fs::is_regular_file(oldrpmrc, ec))) {
fs::remove(lockfile, ec);
return -1;
}

fs::create_directories(userdir_path, ec);
if (ec) goto err;

if (fs::is_regular_file(oldmacros)) {
fs::rename(oldmacros, newmacros, ec);
if (ec) goto undo;
macros_target = fs::relative(newmacros, home, ec);
if (ec) goto undo;
move_macros = true;
fs::create_symlink(macros_target , oldmacros, ec);
if (ec) goto undo;
}
if (fs::is_regular_file(oldrpmrc)) {
fs::rename(oldrpmrc, newrpmrc, ec);
if (ec) goto undo;
rpmrc_target = fs::relative(newrpmrc, home, ec);
if (ec) goto undo;
move_rpmrc = true;
fs::create_symlink(rpmrc_target , oldrpmrc, ec);
if (ec) goto undo;
}

if (fs::is_symlink(oldmacros, ec)) {
rpmlog(RPMLOG_WARNING, "Migrated %s to %s (leaving a symlink)\n",
oldmacros.c_str(), newmacros.c_str());
}
if (fs::is_symlink(oldrpmrc, ec)) {
rpmlog(RPMLOG_WARNING, "Migrated %s to %s (leaving a symlink)\n",
oldrpmrc.c_str(), newrpmrc.c_str());
}

fs::remove(lockfile, ec);

return 0;
undo:
if (move_macros && fs::is_symlink(oldmacros, ec2))
fs::remove(oldmacros, ec2);
if (move_rpmrc && fs::is_symlink(oldrpmrc, ec2))
fs::remove(oldrpmrc, ec2);

if (fs::is_regular_file(newmacros, ec2))
fs::rename(newmacros, oldmacros, ec2);
if (fs::is_regular_file(newrpmrc, ec2))
fs::rename(newrpmrc, oldrpmrc, ec2);
/* userdir_path did not exist at the beginning */
fs::remove(userdir_path, ec2);
err:
if (fs::exists(oldmacros, ec2)) {
rpmlog(RPMLOG_ERR, "Could not migrate %s to %s: %s\n",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really want this to be an error? It seems too drastic, a failed migration doesn't mean reduced functionality in rpm, it's more of a house-keeping thing that's nice to have (at least for now).

oldmacros.c_str(), newmacros.c_str(), ec.message().c_str());
}
if (fs::exists(oldrpmrc, ec2)) {
rpmlog(RPMLOG_ERR, "Could not migrate %s to %s: %s\n",
oldrpmrc.c_str(), newrpmrc.c_str(), ec.message().c_str());
}

return -1;
}

static void setDefaults(void)
{
/* If either is missing, we need to go through this whole dance */
Expand All @@ -379,11 +485,13 @@ static void setDefaults(void)
if (rpmGlob(userdir, NULL, NULL)) {
const char *oldmacros = "~/.rpmmacros";
const char *oldrc = "~/.rpmrc";
if (rpmGlob(oldmacros, NULL, NULL) == 0 || rpmGlob(oldrc, NULL, NULL) == 0) {
free(usermacros);
free(userrc);
usermacros = xstrdup(oldmacros);
userrc = xstrdup(oldrc);
if ((rpmGlob(oldmacros, NULL, NULL) == 0 || rpmGlob(oldrc, NULL, NULL) == 0)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line change seems accidental (?)

if (moveConfigFiles(userdir)) {
free(usermacros);
free(userrc);
usermacros = xstrdup(oldmacros);
userrc = xstrdup(oldrc);
}
}
}

Expand Down
60 changes: 57 additions & 3 deletions tests/rpmmacro.at
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ AT_KEYWORDS([macros])

# .rpmmacros exists, new directory not
RPMTEST_CHECK([[
rm -rf ~/.config/rpm ~/.rpmmacros
touch ~/.rpmmacros
rpm --showrc | awk '/^Macro path/{print(a[split($0,a,":")])}'
runroot rm -rf /root/.config/rpm /root/.rpmmacros
runroot touch /root/.rpmmacros
runroot rpm --showrc | awk '/^Macro path/{print(a[split($0,a,":")])}'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these changes intentional?

]],
[0],
[~/.rpmmacros
Expand Down Expand Up @@ -50,6 +50,60 @@ SOMENV=/tmp/somewhere rpm --macros "%{getenv:SOMENV}" --eval "%somewhere"
[])
RPMTEST_CLEANUP

RPMTEST_SETUP_RW([macro path])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There already is a test with this name, maybe add something after a colon?

AT_KEYWORDS([convert .macros])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This, OTOH, should probably be just macros, like in the other tests.

RPMTEST_USER

runroot_user rm -f /home/$RPMUSER/.config/rpm
runroot_user rm -f /home/$RPMUSER/.rpmmacros
runroot_user rm -f /home/$RPMUSER/.rpmrc
runroot_user bash -c "echo %foo bar > /home/$RPMUSER/.rpmmacros"
runroot_user touch "/home/$RPMUSER/.rpmrc"

RPMTEST_CHECK([[
runroot_user mkdir /home/$RPMUSER/.config
runroot_user chmod 000 /home/$RPMUSER/.config
runroot_user rpm -E "%{foo}"
runroot_user chmod 755 /home/$RPMUSER/.config
runroot_user ls /home/$RPMUSER/.rpmconfig_migration_lock
runroot_user rpm -E "%{foo}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably deserves a readlink check, like those in the subsequent test, just to be sure. It is kinda covered by the lack of a warning: Migrated ... message in the output, but that's relying on the message alone.

runroot_user rm /home/$RPMUSER/.rpmconfig_migration_lock
]],
[0],
[bar
/home/klang/.rpmconfig_migration_lock
bar
],
[error: Could not migrate /home/klang/.rpmmacros to /home/klang/.config/rpm/macros: Permission denied
error: Could not migrate /home/klang/.rpmrc to /home/klang/.config/rpm/rpmrc: Permission denied
])

RPMTEST_CHECK([[
runroot_user readlink /home/$RPMUSER/.rpmmacros
runroot_user readlink /home/$RPMUSER/.rpmrc
# don't convert if not tty
runroot_user rpm -E "%{foo}" < "${RPMTEST}"/data/macros.testfile
runroot_user readlink /home/$RPMUSER/.rpmmacros
runroot_user readlink /home/$RPMUSER/.rpmrc
runroot_user rpm -E "%{foo}"
runroot_user readlink /home/$RPMUSER/.rpmmacros
runroot_user readlink /home/$RPMUSER/.rpmrc
runroot_user rpm -E "%{foo}"
runroot_user rpm --showrc | awk '/^Macro path/{print(a[split($0,a,":")])}'
]],
[0],
[bar
bar
.config/rpm/macros
.config/rpm/rpmrc
bar
~/.config/rpm/macros
],
[warning: Migrated /home/klang/.rpmmacros to /home/klang/.config/rpm/macros (leaving a symlink)
warning: Migrated /home/klang/.rpmrc to /home/klang/.config/rpm/rpmrc (leaving a symlink)
])
RPMTEST_CLEANUP

# ------------------------------
RPMTEST_SETUP_RW([macro path: skip editor backups])
AT_KEYWORDS([macros])
Expand Down