Skip to content

Commit 0f50b6c

Browse files
committed
Move ~/.rpmmacros and ~/.rpmrc to ~/.config/rpm/
The XDG cofiguration directory is the new default location for the macros and rpmrc file. Migrate legacy files automatically. Create symlinks from the home directory for compatibility with older rpm versions. Don't migrate for more complicated setups, if running with root privileges or in scripts. Leaves ~/.rpmconfig_migration_lock_ behind if the migration fails and attempt no more migrations. Resolves: #3467
1 parent bc0b940 commit 0f50b6c

File tree

5 files changed

+179
-14
lines changed

5 files changed

+179
-14
lines changed

docs/man/rpm-config.5.scd

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,12 @@ _macro path_ uses this to achieve the following hierarchy of settings:
3838

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

41-
In older versions of rpm, the path of per-user macros was _~/.rpmmacros_.
42-
This is still processed if it exists and the new configuration directory
43-
does not exist.
41+
In older versions of rpm, the path of per-user macros was
42+
_~/.rpmmacros_. *rpm* will try to move this file to the new
43+
locations. If this is not possible it will create
44+
_~/.rpmconfig_migration_lock_ to prevent attempting the migration over
45+
and over again. In this case the file is still processed if it exists
46+
and the new configuration directory does not exist.
4447

4548
# CONFIGURATION
4649
The following configurables are supported for the *rpm* runtime (as

docs/man/rpm-rpmrc.5.scd

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,11 @@ configuration:
3333
. User specific configuration
3434

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

3942
# CONFIGURATION
4043
_ARCH_ and _OS_ relate to *uname*(2) machine and operating system

lib/rpmrc.cc

Lines changed: 110 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <string>
66
#include <unordered_map>
77
#include <vector>
8+
#include <filesystem>
89

910
#include <fcntl.h>
1011
#include <stdarg.h>
@@ -48,6 +49,8 @@
4849
using wrlock = std::unique_lock<std::shared_mutex>;
4950
using rdlock = std::shared_lock<std::shared_mutex>;
5051

52+
namespace fs = std::filesystem;
53+
5154
static const char * defrcfiles = NULL;
5255
const char * macrofiles = NULL;
5356

@@ -358,6 +361,106 @@ const char * lookupInDefaultTable(const char * name,
358361
return name;
359362
}
360363

364+
static int moveConfigFiles(std::string userdir)
365+
{
366+
/* Don't migrate in scripts */
367+
if (!isatty(STDIN_FILENO))
368+
return -1;
369+
/* Don't migrate with root priviledges */
370+
if (geteuid() == 0)
371+
return -1;
372+
373+
fs::path home = std::getenv("HOME");
374+
std::error_code ec;
375+
std::error_code ec2;
376+
fs::path oldmacros = home / ".rpmmacros";
377+
fs::path oldrpmrc = home / ".rpmrc";
378+
fs::path lockfile = home / ".rpmconfig_migration_lock";
379+
fs::path macros_target = "";
380+
fs::path rpmrc_target = "";
381+
382+
bool move_rpmrc = false;
383+
bool move_macros = false;
384+
385+
if (userdir.substr(0, 2) == "~/")
386+
userdir.replace(0, 1, home);
387+
388+
fs::path userdir_path = userdir;
389+
fs::path newmacros = userdir_path / "macros";
390+
fs::path newrpmrc = userdir_path / "rpmrc";
391+
392+
if (!fs::is_regular_file(oldmacros, ec) && !fs::is_regular_file(oldrpmrc, ec))
393+
return -1;
394+
395+
int fd = open(lockfile.c_str(), O_CREAT|O_EXCL|O_WRONLY, 0644);
396+
if (fd < 0)
397+
return -1;
398+
close(fd);
399+
400+
/* recheck after acquiring lock */
401+
if ((!fs::is_regular_file(oldmacros, ec) && !fs::is_regular_file(oldrpmrc, ec)) ||
402+
fs::exists(userdir_path, ec)) {
403+
fs::remove(lockfile, ec);
404+
return -1;
405+
}
406+
407+
fs::create_directories(userdir_path, ec);
408+
if (ec) goto err;
409+
410+
if (fs::is_regular_file(oldmacros)) {
411+
fs::rename(oldmacros, newmacros, ec);
412+
if (ec) goto undo;
413+
macros_target = fs::relative(newmacros, home, ec);
414+
if (ec) goto undo;
415+
move_macros = true;
416+
fs::create_symlink(macros_target , oldmacros, ec);
417+
if (ec) goto undo;
418+
}
419+
if (fs::is_regular_file(oldrpmrc)) {
420+
fs::rename(oldrpmrc, newrpmrc, ec);
421+
if (ec) goto undo;
422+
rpmrc_target = fs::relative(newrpmrc, home, ec);
423+
if (ec) goto undo;
424+
move_rpmrc = true;
425+
fs::create_symlink(rpmrc_target , oldrpmrc, ec);
426+
if (ec) goto undo;
427+
}
428+
429+
if (fs::is_symlink(oldmacros, ec)) {
430+
rpmlog(RPMLOG_WARNING, "Migrated %s to %s (leaving a symlink)\n",
431+
oldmacros.c_str(), newmacros.c_str());
432+
}
433+
if (fs::is_symlink(oldrpmrc, ec)) {
434+
rpmlog(RPMLOG_WARNING, "Migrated %s to %s (leaving a symlink)\n",
435+
oldrpmrc.c_str(), newrpmrc.c_str());
436+
}
437+
438+
fs::remove(lockfile, ec);
439+
440+
return 0;
441+
undo:
442+
if (move_macros && fs::is_symlink(oldmacros, ec2))
443+
fs::remove(oldmacros, ec2);
444+
if (move_rpmrc && fs::is_symlink(oldrpmrc, ec2))
445+
fs::remove(oldrpmrc, ec2);
446+
447+
if (fs::is_regular_file(newmacros, ec2))
448+
fs::rename(newmacros, oldmacros, ec2);
449+
if (fs::is_regular_file(newrpmrc, ec2))
450+
fs::rename(newrpmrc, oldrpmrc, ec2);
451+
/* userdir_path did not exist at the beginning */
452+
fs::remove(userdir_path, ec2);
453+
err:
454+
if (fs::exists(oldmacros, ec2))
455+
rpmlog(RPMLOG_ERR, "Could not migrate %s to %s: %s\n",
456+
oldmacros.c_str(), newmacros.c_str(), ec.message().c_str());
457+
if (fs::exists(oldrpmrc, ec2))
458+
rpmlog(RPMLOG_ERR, "Could not migrate %s to %s: %s\n",
459+
oldrpmrc.c_str(), newrpmrc.c_str(), ec.message().c_str());
460+
461+
return -1;
462+
}
463+
361464
static void setDefaults(void)
362465
{
363466
/* If either is missing, we need to go through this whole dance */
@@ -379,11 +482,13 @@ static void setDefaults(void)
379482
if (rpmGlob(userdir, NULL, NULL)) {
380483
const char *oldmacros = "~/.rpmmacros";
381484
const char *oldrc = "~/.rpmrc";
382-
if (rpmGlob(oldmacros, NULL, NULL) == 0 || rpmGlob(oldrc, NULL, NULL) == 0) {
383-
free(usermacros);
384-
free(userrc);
385-
usermacros = xstrdup(oldmacros);
386-
userrc = xstrdup(oldrc);
485+
if ((rpmGlob(oldmacros, NULL, NULL) == 0 || rpmGlob(oldrc, NULL, NULL) == 0)) {
486+
if (moveConfigFiles(userdir)) {
487+
free(usermacros);
488+
free(userrc);
489+
usermacros = xstrdup(oldmacros);
490+
userrc = xstrdup(oldrc);
491+
}
387492
}
388493
}
389494

tests/rpmmacro.at

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ AT_KEYWORDS([macros])
77

88
# .rpmmacros exists, new directory not
99
RPMTEST_CHECK([[
10-
rm -rf ~/.config/rpm ~/.rpmmacros
11-
touch ~/.rpmmacros
12-
rpm --showrc | awk '/^Macro path/{print(a[split($0,a,":")])}'
10+
runroot rm -rf /root/.config/rpm /root/.rpmmacros
11+
runroot touch /root/.rpmmacros
12+
runroot rpm --showrc | awk '/^Macro path/{print(a[split($0,a,":")])}'
1313
]],
1414
[0],
1515
[~/.rpmmacros
@@ -50,6 +50,60 @@ SOMENV=/tmp/somewhere rpm --macros "%{getenv:SOMENV}" --eval "%somewhere"
5050
[])
5151
RPMTEST_CLEANUP
5252

53+
RPMTEST_SETUP_RW([macro path])
54+
AT_KEYWORDS([convert .macros])
55+
RPMTEST_USER
56+
57+
runroot_user rm -f /home/$RPMUSER/.config/rpm
58+
runroot_user rm -f /home/$RPMUSER/.rpmmacros
59+
runroot_user rm -f /home/$RPMUSER/.rpmrc
60+
runroot_user bash -c "echo %foo bar > /home/$RPMUSER/.rpmmacros"
61+
runroot_user touch "/home/$RPMUSER/.rpmrc"
62+
63+
RPMTEST_CHECK([[
64+
runroot_user mkdir /home/$RPMUSER/.config
65+
runroot_user chmod 000 /home/$RPMUSER/.config
66+
runroot_user rpm -E "%{foo}"
67+
runroot_user chmod 755 /home/$RPMUSER/.config
68+
runroot_user ls /home/$RPMUSER/.rpmconfig_migration_lock
69+
runroot_user rpm -E "%{foo}"
70+
runroot_user rm /home/$RPMUSER/.rpmconfig_migration_lock
71+
]],
72+
[0],
73+
[bar
74+
/home/klang/.rpmconfig_migration_lock
75+
bar
76+
],
77+
[error: Could not migrate /home/klang/.rpmmacros to /home/klang/.config/rpm/macros: Permission denied
78+
error: Could not migrate /home/klang/.rpmrc to /home/klang/.config/rpm/rpmrc: Permission denied
79+
])
80+
81+
RPMTEST_CHECK([[
82+
runroot_user readlink /home/$RPMUSER/.rpmmacros
83+
runroot_user readlink /home/$RPMUSER/.rpmrc
84+
# don't convert if not tty
85+
runroot_user rpm -E "%{foo}" < "${RPMTEST}"/data/macros.testfile
86+
runroot_user readlink /home/$RPMUSER/.rpmmacros
87+
runroot_user readlink /home/$RPMUSER/.rpmrc
88+
runroot_user rpm -E "%{foo}"
89+
runroot_user readlink /home/$RPMUSER/.rpmmacros
90+
runroot_user readlink /home/$RPMUSER/.rpmrc
91+
runroot_user rpm -E "%{foo}"
92+
runroot_user rpm --showrc | awk '/^Macro path/{print(a[split($0,a,":")])}'
93+
]],
94+
[0],
95+
[bar
96+
bar
97+
.config/rpm/macros
98+
.config/rpm/rpmrc
99+
bar
100+
~/.config/rpm/macros
101+
],
102+
[warning: Migrated /home/klang/.rpmmacros to /home/klang/.config/rpm/macros (leaving a symlink)
103+
warning: Migrated /home/klang/.rpmrc to /home/klang/.config/rpm/rpmrc (leaving a symlink)
104+
])
105+
RPMTEST_CLEANUP
106+
53107
# ------------------------------
54108
RPMTEST_SETUP_RW([macro path: skip editor backups])
55109
AT_KEYWORDS([macros])

0 commit comments

Comments
 (0)