lib/ffi/libfuse/main.rb in ffi-libfuse-0.0.1.rctest12 vs lib/ffi/libfuse/main.rb in ffi-libfuse-0.1.0.rc20220550

- old
+ new

@@ -12,86 +12,83 @@ # Main function of FUSE # # This function: # # - parses command line options - see {fuse_parse_cmdline} - # and exits immediately if help or version options were processed + # exiting immediately if help or version options were processed + # - calls {#fuse_debug}, {#fuse_options}, {#fuse_configure} if implemented by operations # - installs signal handlers for INT, HUP, TERM to unmount and exit filesystem # - installs custom signal handlers if operations implements {fuse_traps} # - creates a fuse handle mounted with registered operations - see {fuse_create} # - calls either the single-threaded (option -s) or the multi-threaded event loop - see {FuseCommon#run} # # @param [Array<String>] argv mount.fuse arguments # expects progname, mountpoint, options.... - # @param [FuseArgs] args + # @param [FuseArgs|Array<String>] args # alternatively constructed args # @param [Object|FuseOperations] operations # something that responds to the fuse callbacks and optionally our abstract configuration methods # @param [Object] private_data # any data to be made available to the {FuseOperations#init} callback # # @return [Integer] suitable for process exit code - def fuse_main(*argv, operations:, args: argv, private_data: nil) + def fuse_main(*argv, operations:, args: argv.any? ? argv : [$0, *ARGV], private_data: nil) run_args = fuse_parse_cmdline(args: args, handler: operations) return 2 unless run_args fuse_args = run_args.delete(:args) mountpoint = run_args.delete(:mountpoint) + return 3 unless fuse_configure(operations: operations, **run_args) + + warn "FuseCreate: mountpoint: #{mountpoint}, args: [#{fuse_args.argv.join(' ')}]" if run_args[:debug] + warn "FuseRun: #{run_args}" if run_args[:debug] + fuse = fuse_create(mountpoint, args: fuse_args, operations: operations, private_data: private_data) return 0 if run_args[:show_help] || run_args[:show_version] return 2 if !fuse || !mountpoint return unless fuse - warn run_args.to_s if run_args[:debug] - fuse.run(**run_args) end + alias main fuse_main # Parse command line arguments # # - parses standard command line options (-d -s -h -V) # will call {fuse_debug}, {fuse_version}, {fuse_help} if implemented by handler - # - parses custom options if handler implements {fuse_options} and {fuse_opt_proc} + # - calls {fuse_options} for custom option processing if implemented by handler # - records signal handlers if operations implements {fuse_traps} # - parses standard fuse mount options # # @param [Array<String>] argv mount.fuse arguments # expects progname, [fsname,] mountpoint, options.... from mount.fuse3 # @param [FuseArgs] args # alternatively constructed args # @param [Object] handler # something that responds to our abstract configuration methods - # @param [Object] private_data passed to handler.fuse_opt_proc - # # @return [Hash<Symbol,Object>] - # * fsname [String]: the fsspec from /etc/fstab # * mountpoint [String]: the mountpoint argument # * args [FuseArgs]: remaining fuse_args to pass to {fuse_create} # * show_help [Boolean]: -h or --help # * show_version [Boolean]: -v or --version # * debug [Boolean]: -d # * others are options to pass to {FuseCommon#run} - def fuse_parse_cmdline(*argv, args: argv, handler: nil, private_data: nil) + def fuse_parse_cmdline(*argv, args: argv, handler: nil) args = fuse_init_args(args) # Parse args and print cmdline help run_args = Fuse.parse_cmdline(args, handler: handler) + return nil unless run_args - # process custom options - if %i[fuse_options fuse_opt_proc].all? { |m| handler.respond_to?(m) } - parse_ok = args.parse!(handler.fuse_options, private_data) do |*p_args| - handler.fuse_opt_proc(*p_args) - end - return unless parse_ok - end + return nil if handler.respond_to?(:fuse_options) && !handler.fuse_options(args) run_args[:traps] = handler.fuse_traps if handler.respond_to?(:fuse_traps) - args.parse!(RUN_OPTIONS, run_args) { |*opt_args| hash_opt_proc(*opt_args, discard: %i[native max_threads]) } + return nil unless parse_run_options(args, run_args) run_args[:args] = args run_args end @@ -103,22 +100,28 @@ fuse = Fuse.new(mountpoint, args, operations, private_data) fuse if fuse.mounted? end - # Helper fuse_opt_proc function to capture options into a hash - # - # See {FuseArgs.parse!} - def hash_opt_proc(hash, arg, key, _out, discard: []) - return :keep if %i[unmatched non_option].include?(key) + # @!visibility private - hash[key] = arg =~ /=/ ? arg.split('=', 2).last : true - discard.include?(key) ? :discard : :keep + def fuse_configure(operations:, show_help: false, show_version: false, **_) + return true unless operations.respond_to?(:fuse_configure) && !show_help && !show_version + + # Provide sensible values for FuseContext in case this is referenced during configure + FFI::Libfuse::FuseContext.overrides do + operations.fuse_configure + true + rescue Error => e + warn e.message + false + rescue StandardError, ScriptError => e + warn "#{e.class.name}: #{e.message}\n\t#{e.backtrace.join("\n\t")}" + false + end end - # @!visibility private - # Version text def version "#{name}: #{VERSION}" end @@ -129,46 +132,86 @@ # https://github.com/libfuse/libfuse/issues/621 handle "source" field sent from /etc/fstab via mount.fuse3 # if arg[1] and arg[2] are both non option fields then replace arg1 with -ofsname=<arg1> unless args.size <= 2 || args[1]&.start_with?('-') || args[2]&.start_with?('-') args[1] = "-ofsname=#{args[1]}" end + warn "FuseArgs: #{args.join(' ')}" if args.include?('-d') args = FuseArgs.create(*args) end return args if args.is_a?(FuseArgs) raise ArgumentError "fuse main args: must be Array<String> or #{FuseArgs.class.name}" end + + private + + def parse_run_options(args, run_args) + args.parse!(RUN_OPTIONS) do |key:, value:, **| + run_args[key] = value + next :keep if (STANDARD_OPTIONS.values + %i[remember]).include?(key) + + :discard + end + end end # @!group Abstract Configuration - # @!method fuse_options + # @!method fuse_options(args) # @abstract - # @return [Hash] custom option schema + # Called to allow filesystem to handle custom options and observe standard mount options # + # @param [FuseArgs] args + # @return [Boolean] true if args parsed successfully # @see FuseArgs#parse! + # @example + # OPTIONS = { 'config=' => :config, '-c ' => :config } + # def fuse_options(args) + # args.parse!(OPTIONS) do |key:, value:, out:, **opts| + # + # # raise errors for invalid config + # raise FFI::Libfuse::FuseArgs::Error, "Invalid config" unless valid_config?(key,value) + # + # # Configure the file system + # @config = value if key == :config + # + # #Optionally manipulate other arguments for fuse_mount() based on the current argument and state + # out.add('-oopt=val') + # + # # Custom options must be marked :handled otherwise fuse_mount() will fail with unknown arguments + # :handled + # end + # end - # @!method fuse_opt_proc(data,arg,key,out) - # @abstract - # Process custom options - # @see FuseArgs#parse! - # @!method fuse_traps # @abstract - # @return [Hash] map of signal name or number to signal handler as per Signal.trap + # @return [Hash<String|Symbol|Integer,String|Proc>] + # map of signal name or number to signal handler as per Signal.trap + # @example + # def fuse_traps + # { HUP: ->() { reload() }} + # end # @!method fuse_version # @abstract # @return [String] a custom version string to output with -V option # @!method fuse_help # @abstract - # @return [String] help text to explain custom options to show with -h option + # Called as part of generating output for the -h option + # @return [String] help text to explain custom options # @!method fuse_debug(enabled) # @abstract - # Indicate to the filesystem whether debugging option is in use. + # Called to indicate to the filesystem whether debugging option is in use. # @param [Boolean] enabled if -d option is in use + # @return [void] + + # @!method fuse_configure + # @abstract + # Called immediately before the filesystem is mounted, after options have been parsed + # + # @raise [Error] to prevent the mount from proceeding # @return [void] # @!endgroup # @!visibility private