Environment:
What happens:
When grub-btrfsd is invoked with options placed after the snapshot path:
grub-btrfsd /path/to/snapshots --syslog
the daemon exits immediately with code 1 and the error:
[!] No directory found at --syslog
[!] Please specify a valid snapshot directory
Root cause:
grub-btrfsd uses bash getopts for argument parsing. getopts stops processing
options at the first non-option argument (standard POSIX behavior). With the
invocation above:
parse_arguments runs getopts — sees /path/to/snapshots first (not an option)
→ OPTIND stays at 1, the while loop exits without processing --syslog
- Back in
main(): shift $(( OPTIND - 1 )) shifts by 0
snapdirs=( "${@}" ) = ( "/path/to/snapshots" "--syslog" )
checks() runs [ -d --syslog ] → false → exits with code 1
This is consistent POSIX behavior, but is not obvious when writing service unit
files or run scripts where the primary argument (the snapshot path) is often
placed immediately after the program name, with behavioral flags appended.
The same issue affects any option placed after the path — e.g.
grub-btrfsd --syslog /path --verbose will treat --verbose as a snapshot path.
The exit code (1) is identical to the missing inotifywait error, and if the
misplaced flag is --syslog, syslog is never configured — leaving no log trace
under a service supervisor.
Additional bug found while investigating:
-o (short form of --timeshift-old) is handled in the case block but is
absent from the getopts option string (line 81 in 4.14-1.1: getopts :l:ctvrsh-:).
The short option -o is therefore silently broken — it triggers the *) unknown
option branch and exits with code 1. Only the long form --timeshift-old works.
Fix:
Minimum: add a note to the help output:
Note: all OPTIONS must appear before SNAPSHOTS_DIRS
More robust: switch from getopts to GNU getopt, which supports mixed
option/argument ordering (and correctly maps all short options including -o):
ARGS=$(getopt -o cl:tvrosh \
--long no-color,log-file:,timeshift-auto,timeshift-old,verbose,recursive,syslog,help \
-- "$@")
eval set -- "$ARGS"
Note: -l requires an argument (log file path), so it carries the : suffix;
-s (syslog) takes no argument. The original getopts string correctly has l:
but this fix clarifies the mapping explicitly.
Environment:
What happens:
When
grub-btrfsdis invoked with options placed after the snapshot path:the daemon exits immediately with code 1 and the error:
Root cause:
grub-btrfsduses bashgetoptsfor argument parsing.getoptsstops processingoptions at the first non-option argument (standard POSIX behavior). With the
invocation above:
parse_argumentsrunsgetopts— sees/path/to/snapshotsfirst (not an option)→
OPTINDstays at 1, the while loop exits without processing--syslogmain():shift $(( OPTIND - 1 ))shifts by 0snapdirs=( "${@}" )=( "/path/to/snapshots" "--syslog" )checks()runs[ -d --syslog ]→ false → exits with code 1This is consistent POSIX behavior, but is not obvious when writing service unit
files or run scripts where the primary argument (the snapshot path) is often
placed immediately after the program name, with behavioral flags appended.
The same issue affects any option placed after the path — e.g.
grub-btrfsd --syslog /path --verbosewill treat--verboseas a snapshot path.The exit code (1) is identical to the missing
inotifywaiterror, and if themisplaced flag is
--syslog, syslog is never configured — leaving no log traceunder a service supervisor.
Additional bug found while investigating:
-o(short form of--timeshift-old) is handled in thecaseblock but isabsent from the
getoptsoption string (line 81 in 4.14-1.1:getopts :l:ctvrsh-:).The short option
-ois therefore silently broken — it triggers the*)unknownoption branch and exits with code 1. Only the long form
--timeshift-oldworks.Fix:
Minimum: add a note to the help output:
More robust: switch from
getoptsto GNUgetopt, which supports mixedoption/argument ordering (and correctly maps all short options including
-o):Note:
-lrequires an argument (log file path), so it carries the:suffix;-s(syslog) takes no argument. The originalgetoptsstring correctly hasl:but this fix clarifies the mapping explicitly.