Skip to content

Commit

Permalink
Skip already processed files in rc_service_daemon_set
Browse files Browse the repository at this point in the history
Fixes the problem described in https://bugs.gentoo.org/916947 -
start-stop-daemon hangs in infinite loop when stopping some daemons on
linux 6.6+

It appears linux 6.6 reworked tmpfs, and since then it triggers this
problem in openrc: when iterating over files via readdir, running rename
on a file could result in reading the same file again with next readdir
call.

The Open Group manual for readdir explicitly states "If a file is
removed from or added to the directory after the most recent call to
opendir() or rewinddir(), whether a subsequent call to readdir() returns
an entry for that file is unspecified.". Linux man page don't seem to
mention that, but don't seem to say anything to contradict that either.
So I presume we can't rely on some specific behaviour here.

Bug: https://bugs.gentoo.org/916947
  • Loading branch information
keltar authored and williamh committed Nov 14, 2023
1 parent 6f180e9 commit fda9dcd
Showing 1 changed file with 11 additions and 1 deletion.
12 changes: 11 additions & 1 deletion src/librc/librc-daemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ rc_service_daemon_set(const char *service, const char *exec,
bool retval = false;
DIR *dp;
struct dirent *d;
RC_STRINGLIST *match;
RC_STRINGLIST *match, *renamelist;
int i = 0;
FILE *fp;

Expand All @@ -416,11 +416,17 @@ rc_service_daemon_set(const char *service, const char *exec,
/* Regardless, erase any existing daemon info */
if ((dp = opendir(dirpath))) {
match = _match_list(exec, argv, pidfile);
renamelist = rc_stringlist_new();
while ((d = readdir(dp))) {
if (d->d_name[0] == '.')
continue;

xasprintf(&file, "%s/%s", dirpath, d->d_name);
if (rc_stringlist_find(renamelist, file)) {
free(file);
continue;
}

nfiles++;

if (!*oldfile) {
Expand All @@ -432,11 +438,15 @@ rc_service_daemon_set(const char *service, const char *exec,
} else {
rename(file, oldfile);
strlcpy(oldfile, file, sizeof(oldfile));
/* Add renamed file to renamelist, as this new file name could
* be read again from readdir() */
rc_stringlist_add(renamelist, oldfile);
}
free(file);
}
closedir(dp);
rc_stringlist_free(match);
rc_stringlist_free(renamelist);
}

/* Now store our daemon info */
Expand Down

0 comments on commit fda9dcd

Please sign in to comment.