Allow FIFOs to be used as config files

There are some config options which have no equivalent command line
option. Some may want to set these options dynamically or on a one-off
basis where a static config file is not ideal, and one very easy way to
do that is using process substitution:

    linkchecker --config <(printf '%s\n' '[filtering]' 'ignorewarnings=http-redirected') ...

This, however, does not work in the current code because these are
typically implemented as FIFOs, which don't pass the `os.path.isfile`
check:

    WARNING linkcheck.cmdline 2023-10-31 00:12:09,678 MainThread Unreadable config file: '/dev/fd/63'

Allow reading FIFOs as config input so that this is possible.
`fileutil.is_readable` also now doesn't check if the path leads to a
regular file: this is only used as part of cookie and config file input,
and in both cases that's not really relevant.
This commit is contained in:
Chris Down 2023-10-31 00:09:22 +00:00
parent 8ae3783c3f
commit 85ce9841eb
3 changed files with 9 additions and 4 deletions

View file

@ -135,7 +135,7 @@ def linkchecker():
files = []
if options.configfile:
path = configuration.normpath(options.configfile)
if os.path.isfile(path):
if fileutil.is_valid_config_source(path):
files.append(path)
else:
log.warn(

View file

@ -222,7 +222,7 @@ class Configuration(dict):
# filter invalid files
filtered_cfiles = []
for cfile in cfiles:
if not os.path.isfile(cfile):
if not fileutil.is_valid_config_source(cfile):
log.warn(LOG_CHECK, _("Configuration file %r does not exist."), cfile)
elif not fileutil.is_readable(cfile):
log.warn(LOG_CHECK, _("Configuration file %r is not readable."), cfile)

View file

@ -89,8 +89,8 @@ def is_tty(fp):
@lru_cache(128)
def is_readable(filename):
"""Check if file is a regular file and is readable."""
return os.path.isfile(filename) and os.access(filename, os.R_OK)
"""Check if file is readable."""
return os.access(filename, os.R_OK)
def is_accessable_by_others(filename):
@ -103,3 +103,8 @@ def is_writable_by_others(filename):
"""Check if file or directory is world writable."""
mode = os.stat(filename)[stat.ST_MODE]
return mode & stat.S_IWOTH
def is_valid_config_source(filename):
"""Check if the file is a valid config file."""
return os.path.isfile(filename) or stat.S_ISFIFO(os.stat(filename).st_mode)