ffi-libfuse 0.0.1.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.yardopts +1 -0
- data/README.md +100 -0
- data/lib/ffi/accessors.rb +145 -0
- data/lib/ffi/devt.rb +30 -0
- data/lib/ffi/flock.rb +47 -0
- data/lib/ffi/gnu_extensions.rb +115 -0
- data/lib/ffi/libfuse/ackbar.rb +112 -0
- data/lib/ffi/libfuse/adapter/context.rb +37 -0
- data/lib/ffi/libfuse/adapter/debug.rb +89 -0
- data/lib/ffi/libfuse/adapter/fuse2_compat.rb +91 -0
- data/lib/ffi/libfuse/adapter/fuse3_support.rb +87 -0
- data/lib/ffi/libfuse/adapter/interrupt.rb +37 -0
- data/lib/ffi/libfuse/adapter/pathname.rb +23 -0
- data/lib/ffi/libfuse/adapter/ruby.rb +334 -0
- data/lib/ffi/libfuse/adapter/safe.rb +58 -0
- data/lib/ffi/libfuse/adapter/thread_local_context.rb +36 -0
- data/lib/ffi/libfuse/adapter.rb +79 -0
- data/lib/ffi/libfuse/callbacks.rb +61 -0
- data/lib/ffi/libfuse/fuse2.rb +159 -0
- data/lib/ffi/libfuse/fuse3.rb +162 -0
- data/lib/ffi/libfuse/fuse_args.rb +166 -0
- data/lib/ffi/libfuse/fuse_buffer.rb +155 -0
- data/lib/ffi/libfuse/fuse_callbacks.rb +48 -0
- data/lib/ffi/libfuse/fuse_cmdline_opts.rb +44 -0
- data/lib/ffi/libfuse/fuse_common.rb +249 -0
- data/lib/ffi/libfuse/fuse_config.rb +205 -0
- data/lib/ffi/libfuse/fuse_conn_info.rb +211 -0
- data/lib/ffi/libfuse/fuse_context.rb +79 -0
- data/lib/ffi/libfuse/fuse_file_info.rb +100 -0
- data/lib/ffi/libfuse/fuse_loop_config.rb +43 -0
- data/lib/ffi/libfuse/fuse_operations.rb +870 -0
- data/lib/ffi/libfuse/fuse_opt.rb +54 -0
- data/lib/ffi/libfuse/fuse_poll_handle.rb +59 -0
- data/lib/ffi/libfuse/fuse_version.rb +43 -0
- data/lib/ffi/libfuse/job_pool.rb +53 -0
- data/lib/ffi/libfuse/main.rb +200 -0
- data/lib/ffi/libfuse/test/operations.rb +56 -0
- data/lib/ffi/libfuse/test.rb +3 -0
- data/lib/ffi/libfuse/thread_pool.rb +147 -0
- data/lib/ffi/libfuse/version.rb +8 -0
- data/lib/ffi/libfuse.rb +24 -0
- data/lib/ffi/ruby_object.rb +95 -0
- data/lib/ffi/stat/constants.rb +29 -0
- data/lib/ffi/stat/native.rb +50 -0
- data/lib/ffi/stat/time_spec.rb +137 -0
- data/lib/ffi/stat.rb +96 -0
- data/lib/ffi/stat_vfs.rb +81 -0
- data/lib/ffi/struct_array.rb +39 -0
- data/lib/ffi/struct_wrapper.rb +100 -0
- data/sample/memory_fs.rb +189 -0
- data/sample/no_fs.rb +69 -0
- metadata +165 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c870a0015b02637bb7423453df5f208dc2a9b263ac5d68ae9de7a71c7767e005
|
4
|
+
data.tar.gz: 6ea5d671235ce094dbcbaf1e2257cc8879a102af74fc9a5ebc0cf2238e17a6fd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d80e0f44e8bf7bbb23705c9937c142105aed1eb89bd8cad0f8eba5a8e36b6f590afb35080790df7564f0c2f2f3aec86ef3c99a83a24d8ec3201ca13963ca0e63
|
7
|
+
data.tar.gz: 9937c251e92940a61e0e6d01c9b45ce525a2e0045d6cdcc182d377926d8c671ae3e9de07d128e8344a566ff8cc71d06095514e3701d31065faec57e88e9f383e
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup=markdown
|
data/README.md
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# FFI::Libfuse
|
2
|
+
|
3
|
+
Ruby FFI Binding for [libfuse](https://github.com/libfuse/libfuse)
|
4
|
+
|
5
|
+
## Writing a FUSE Filesystem
|
6
|
+
|
7
|
+
Create a class that implements the abstract methods of {FFI::Libfuse::Main} and {FFI::Libfuse::FuseOperations}
|
8
|
+
|
9
|
+
Call {FFI::Libfuse.fuse_main} to start the filesystem
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
require 'ffi/libfuse'
|
13
|
+
|
14
|
+
class MyFS
|
15
|
+
# include helpers to abstract away from the native C callbacks to more idiomatic Ruby
|
16
|
+
include FFI::Libfuse::Adapter::Ruby
|
17
|
+
|
18
|
+
# ... FUSE callbacks .... quacks like a FFI:Libfuse::FuseOperations
|
19
|
+
def getattr(*args)
|
20
|
+
#...
|
21
|
+
end
|
22
|
+
|
23
|
+
def readdir(*args)
|
24
|
+
#...
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
FFI::Libfuse::fuse_main(operations: MyFS.new) if __FILE__ == $0
|
30
|
+
```
|
31
|
+
|
32
|
+
# Fuse2/Fuse3 compatibility
|
33
|
+
|
34
|
+
FFI::Libfuse will prefer Fuse3 over Fuse2 by default. See {FFI::Libfuse::LIBFUSE}
|
35
|
+
|
36
|
+
For writing filesystems with backwards/forwards compatibility between fuse version see
|
37
|
+
{FFI::Libfuse::Adapter::Fuse2Compat} and {FFI::Libfuse::Adapter::Fuse3Support}
|
38
|
+
|
39
|
+
## MACFuse
|
40
|
+
|
41
|
+
[macFUSE](https://osxfuse.github.io/) (previously OSXFuse) supports a superset of the Fuse2 api so FFI::Libfuse is
|
42
|
+
intended to work in that environment.
|
43
|
+
|
44
|
+
# Multi-threading
|
45
|
+
|
46
|
+
Most Ruby filesystems are unlikely to benefit from multi-threaded operation so
|
47
|
+
{FFI::Libfuse.fuse_main} as shown above injects the '-s' (single-thread) option by default.
|
48
|
+
|
49
|
+
Pass the original options in directly if multi-threaded operation is desired for your filesystem
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
FFI::Libfuse::fuse_main($0,*ARGV, operations: MyFS.new) if __FILE__ == $0
|
53
|
+
```
|
54
|
+
|
55
|
+
The {FFI::Libfuse::ThreadPool} can be configured with `-o max_threads=<n>,max_idle_threads=<n>` options
|
56
|
+
|
57
|
+
Callbacks that are about to block (and release the GVL for MRI) should call {FFI::Libfuse::ThreadPool.busy}.
|
58
|
+
|
59
|
+
A typical scenario would be a filesystem where some callbacks are blocking on network IO.
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
def read(*args)
|
63
|
+
# prep, validate args etc.. (MRI holding the GVL anyway)
|
64
|
+
FFI::Libfuse::ThreadPool.busy
|
65
|
+
# Now make some REST or other network call to read the data
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
**Note** Fuse itself has conditions under which filesystem callbacks will be serialized. In particular see
|
70
|
+
[this discussion](http://fuse.996288.n3.nabble.com/GetAttr-calls-being-serialised-td11741.html)
|
71
|
+
on the serialisation of `#getattr` and `#readdir` calls.
|
72
|
+
|
73
|
+
## Under the hood
|
74
|
+
|
75
|
+
FFI::Libfuse tries to provide raw access to the underlying libfuse but there some constraints imposed by Ruby.
|
76
|
+
|
77
|
+
The functions fuse_main(), fuse_daemonize() and fuse_loop<_mt>() are re-implemented in Ruby so we can provide
|
78
|
+
|
79
|
+
* dynamic compatibility between Fuse2 and Fuse3
|
80
|
+
* integrated support for multi-threading under MRI (see {FFI::Libfuse::ThreadPool})
|
81
|
+
* signal handling in ruby filesystem (eg HUP to reload)
|
82
|
+
|
83
|
+
Sending `-o native' will used the native C functions but this exists to assist with testing that FFI::Libfuse has
|
84
|
+
similar behaviour to libfuse itself.
|
85
|
+
|
86
|
+
See {FFI::Libfuse::Main} and {FFI::Libfuse::FuseCommon}
|
87
|
+
|
88
|
+
## Contributing
|
89
|
+
|
90
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/lwoggardner/ffi-libfuse.
|
91
|
+
|
92
|
+
### TODO
|
93
|
+
* Include a MetaFS, PathMapperFS etc (possibly a separate library)
|
94
|
+
* Build a filesystem that can make use of multi-threaded operations
|
95
|
+
* Test with macFUSE
|
96
|
+
|
97
|
+
## License
|
98
|
+
|
99
|
+
The gem is available under the terms of the [MIT](https://opensource.org/licenses/MIT) License.
|
100
|
+
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ffi'
|
4
|
+
|
5
|
+
module FFI
|
6
|
+
# Syntax sugar for FFI::Struct
|
7
|
+
module Accessors
|
8
|
+
# DSL methods for defining struct member accessors
|
9
|
+
module ClassMethods
|
10
|
+
# Define both a reader and a writer for members
|
11
|
+
# @param [Array<Symbol>] attrs the attribute names
|
12
|
+
# @param [String] format
|
13
|
+
# A format string containing a single %s to convert attr symbol to struct member
|
14
|
+
# @return [void]
|
15
|
+
def ffi_attr_accessor(*attrs, format: '%s')
|
16
|
+
ffi_attr_reader(*attrs, format: format)
|
17
|
+
ffi_attr_writer(*attrs, format: format)
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# Define a struct attribute reader for members
|
22
|
+
# @param [Array<Symbol>] attrs the attribute names
|
23
|
+
# @param [String] format
|
24
|
+
# A format string containing a single %s to convert attr symbol to struct member
|
25
|
+
# @param [Boolean] simple
|
26
|
+
# Controls how writer methods are defined using block
|
27
|
+
# @param [Proc] block
|
28
|
+
# An optional block to the struct field(s) into something more useful
|
29
|
+
#
|
30
|
+
# If simple is true then block takes the struct field value, otherwise method is defined directly from the block
|
31
|
+
# and should use __method__ to get the attr name, and self.class.ffi_attr_readers[__method__] to get the member
|
32
|
+
# name
|
33
|
+
# @return [void]
|
34
|
+
def ffi_attr_reader(*attrs, format: '%s', simple: true, &block)
|
35
|
+
attrs.each do |attr|
|
36
|
+
member = (format % attr).to_sym
|
37
|
+
ffi_attr_readers[attr] = member
|
38
|
+
if !block
|
39
|
+
define_method(attr) { self[member] }
|
40
|
+
elsif simple
|
41
|
+
define_method(attr) { block.call(self[member]) }
|
42
|
+
else
|
43
|
+
define_method(attr, &block)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Define a struct attribute writer
|
49
|
+
# @param [Array<Symbol>] attrs the attribute names
|
50
|
+
# @param [String] format
|
51
|
+
# A format string containing a single %s to convert attr symbol to struct member
|
52
|
+
# @param [Boolean] simple
|
53
|
+
# Controls how writer methods are defined using block
|
54
|
+
# @param [Proc] block
|
55
|
+
# An optional block to set the input value into the struct field.
|
56
|
+
#
|
57
|
+
# If simple is true then the struct field is set to the result of calling block with the input value,
|
58
|
+
# otherwise the method is defined directly from the block. Use __method__[0..-2] to get the attribute name
|
59
|
+
# and self.class.ffi_attr_writers[__method__[0..-2]] to get the struct field name
|
60
|
+
# @return [void]
|
61
|
+
def ffi_attr_writer(*attrs, format: '%s', simple: true, &block)
|
62
|
+
attrs.each do |attr|
|
63
|
+
member = (format % attr).to_sym
|
64
|
+
ffi_attr_writers[attr.to_sym] = member
|
65
|
+
if !block
|
66
|
+
define_method("#{attr}=") { |val| self[member] = val }
|
67
|
+
elsif simple
|
68
|
+
define_method("#{attr}=") { |val| self[member] = block.call(val) }
|
69
|
+
else
|
70
|
+
define_method("#{attr}=", &block)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# All defined readers
|
76
|
+
# @return [Hash<Symbol,Symbol>] map of attr names to member names for which readers exist
|
77
|
+
def ffi_attr_readers
|
78
|
+
@ffi_attr_readers ||= {}
|
79
|
+
end
|
80
|
+
|
81
|
+
# All defined writers
|
82
|
+
# @return [Hash<Symbol,Symbol>] map of attr names to member names for which writers exist
|
83
|
+
def ffi_attr_writers
|
84
|
+
@ffi_attr_writers ||= {}
|
85
|
+
end
|
86
|
+
|
87
|
+
# Define individual flag accessors over a bitmask field
|
88
|
+
def ffi_bitflag_accessor(attr, *flags)
|
89
|
+
ffi_bitflag_reader(attr, *flags)
|
90
|
+
ffi_bitflag_writer(attr, *flags)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Define individual flag readers over a bitmask field
|
94
|
+
# @param [Symbol] attr the bitmask member
|
95
|
+
# @param [Array<Symbol>] flags list of flags
|
96
|
+
# @return [void]
|
97
|
+
def ffi_bitflag_reader(attr, *flags)
|
98
|
+
flags.each { |f| ffi_attr_reader(f, simple: false) { self[attr].include?(f) } }
|
99
|
+
end
|
100
|
+
|
101
|
+
# Define individual flag writers over a bitmask field
|
102
|
+
# @param [Symbol] attr the bitmask member
|
103
|
+
# @param [Array<Symbol>] flags list of flags
|
104
|
+
# @return [void]
|
105
|
+
def ffi_bitflag_writer(attr, *flags)
|
106
|
+
flags.each do |f|
|
107
|
+
ffi_attr_writer(f, simple: false) do |v|
|
108
|
+
v ? self[attr] += [f] : self[attr] -= [f]
|
109
|
+
v
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.included(mod)
|
116
|
+
mod.extend(ClassMethods)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Fill the native struct from another object or list of properties
|
120
|
+
# @param [Object] from
|
121
|
+
# for each attribute we call self.attr=(from.attr)
|
122
|
+
# @param [Hash<Symbol,Object>] args
|
123
|
+
# for each entry <attr,val> we call self.attr=(val)
|
124
|
+
# @return [self]
|
125
|
+
def fill(from = nil, **args)
|
126
|
+
if from.is_a?(Hash)
|
127
|
+
args.merge!(from)
|
128
|
+
else
|
129
|
+
self.class.ffi_attr_writers.each_key { |v| send("#{v}=", from.send(v)) if from.respond_to?(v) }
|
130
|
+
end
|
131
|
+
args.each_pair { |k, v| send("#{k}=", v) }
|
132
|
+
self
|
133
|
+
end
|
134
|
+
|
135
|
+
def inspect
|
136
|
+
"#{self.class.name} {#{self.class.ffi_attr_readers.keys.map { |r| "#{r}: #{send(r)} " }.join(',')}"
|
137
|
+
end
|
138
|
+
|
139
|
+
# Convert struct to hash
|
140
|
+
# @return [Hash<Symbol,Object>] map of reader attribute name to value
|
141
|
+
def to_h
|
142
|
+
self.class.ffi_attr_readers.keys.each_with_object({}) { |r, h| h[r] = send(r) }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
data/lib/ffi/devt.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ffi'
|
4
|
+
|
5
|
+
module FFI
|
6
|
+
# Calculate major/minor device numbers for use with mknod etc..
|
7
|
+
# @see makedev(3)
|
8
|
+
module Device
|
9
|
+
extend FFI::Library
|
10
|
+
ffi_lib FFI::Library::LIBC
|
11
|
+
|
12
|
+
prefix = FFI::Platform::IS_GNU ? 'gnu_dev_' : ''
|
13
|
+
|
14
|
+
# @!method makedev(major,minor)
|
15
|
+
# @param [Integer] major
|
16
|
+
# @param [Integer] minor
|
17
|
+
# @return [Integer] combined major/minor to a single value to pass to mknod etc
|
18
|
+
attach_function :makedev, "#{prefix}makedev".to_sym, %i[int int], :int
|
19
|
+
|
20
|
+
# @!method major(dev)
|
21
|
+
# @param [Integer] dev
|
22
|
+
# @return [Integer] the major component of dev
|
23
|
+
attach_function :major, "#{prefix}major".to_sym, [:int], :int
|
24
|
+
|
25
|
+
# @!method minor(dev)
|
26
|
+
# @param [Integer] dev
|
27
|
+
# @return [Integer] the minor component of dev
|
28
|
+
attach_function :minor, "#{prefix}minor".to_sym, [:int], :int
|
29
|
+
end
|
30
|
+
end
|
data/lib/ffi/flock.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'accessors'
|
4
|
+
|
5
|
+
module FFI
|
6
|
+
# Flocking operation
|
7
|
+
class Flock < Struct
|
8
|
+
# Enum definitions associated with Flock
|
9
|
+
module Enums
|
10
|
+
extend FFI::Library
|
11
|
+
seek_whence = %i[seek_set seek_cur seek_end seek_data seek_hole]
|
12
|
+
SeekWhenceShort = enum :short, seek_whence
|
13
|
+
SeekWhence = enum :int, seek_whence
|
14
|
+
|
15
|
+
LockType = enum :short, %i[f_rdlck f_wrlck f_unlck]
|
16
|
+
LockCmd = enum :int, [:f_getlk, 5, :f_setlk, 6, :f_setlkw, 7]
|
17
|
+
end
|
18
|
+
|
19
|
+
include(Accessors)
|
20
|
+
|
21
|
+
layout(type: Enums::LockType, whence: Enums::SeekWhenceShort, start: :off_t, len: :off_t, pid: :pid_t)
|
22
|
+
|
23
|
+
ffi_attr_reader :type, :whence, :start, :len, :pid
|
24
|
+
|
25
|
+
# @!attribute [r] type
|
26
|
+
# @return [Symbol] lock type, :f_rdlck, :f_wrlck, :f_unlck
|
27
|
+
|
28
|
+
# @!attribute [r] whence
|
29
|
+
# @return [Symbol] specifies what the offset is relative to, one of :seek_set, :seek_cur or :seek_end
|
30
|
+
# corresponding to the whence argument to fseek(2) or lseek(2),
|
31
|
+
|
32
|
+
# @!attribute [r] start
|
33
|
+
# @return [Integer] the offset of the start of the region to which the lock applies, and is given in bytes
|
34
|
+
# relative to the point specified by #{whence} member.
|
35
|
+
|
36
|
+
# @!attribute [r] len
|
37
|
+
# @return [Integer] the length of the region to be locked.
|
38
|
+
#
|
39
|
+
# A value of 0 means the region extends to the end of the file.
|
40
|
+
|
41
|
+
# @!attribute [r] pid
|
42
|
+
# @return [Integer] the process ID (see Process Creation Concepts) of the process holding the lock.
|
43
|
+
# It is filled in by calling fcntl with the F_GETLK command, but is ignored when making a lock. If the
|
44
|
+
# conflicting lock is an open file description lock (see Open File Description Locks), then this field will be
|
45
|
+
# set to -1.
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ffi'
|
4
|
+
|
5
|
+
module FFI
|
6
|
+
# Support versioned functions until https://github.com/ffi/ffi/issues/889
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# require 'ffi'
|
10
|
+
#
|
11
|
+
# module MyLibrary
|
12
|
+
# extend FFI::Library
|
13
|
+
#
|
14
|
+
# if FFI::Platform::IS_GNU
|
15
|
+
# require 'ffi/gnu_extensions'
|
16
|
+
#
|
17
|
+
# extend(FFI::GNUExtensions)
|
18
|
+
# # default versions for all functions
|
19
|
+
# ffi_lib_versions(%w[VERSION_X.3 VERSION_X.2])
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# attach_function :func_one, [:int], :int
|
23
|
+
# # override default with specific version
|
24
|
+
# attach_function :func_two, [], :int, versions ['VERSION_X.Y']
|
25
|
+
#
|
26
|
+
# end
|
27
|
+
module GNUExtensions
|
28
|
+
if FFI::Platform::IS_GNU
|
29
|
+
extend FFI::Library
|
30
|
+
ffi_lib 'libdl'
|
31
|
+
|
32
|
+
# @!method dlopen(library,type)
|
33
|
+
# @return [FFI::Pointer] library address, possibly NULL
|
34
|
+
attach_function :dlopen, %i[string int], :pointer
|
35
|
+
# @!method dlvsym(handle)
|
36
|
+
# @return [FFI::Pointer] function address, possibly NULL
|
37
|
+
attach_function :dlvsym, %i[pointer string string], :pointer
|
38
|
+
end
|
39
|
+
|
40
|
+
# @!visibility private
|
41
|
+
def self.extended(mod)
|
42
|
+
mod.extend(DLV) unless mod.respond_to?(:ffi_lib_versions)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Override FFI::Library attach functions with support for dlvsym
|
46
|
+
module DLV
|
47
|
+
# Set the default version(s) for "{attach_function}" (GNU only)
|
48
|
+
# @param [Array<String>|String] versions the default list of versions to search
|
49
|
+
# @return [Array<String>|String]
|
50
|
+
def ffi_lib_versions(versions)
|
51
|
+
@versions = versions
|
52
|
+
end
|
53
|
+
|
54
|
+
# @!visibility private
|
55
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
56
|
+
def attach_function(name, func, args, returns = nil, options = nil)
|
57
|
+
versions = options.delete(:versions) if options.is_a?(Hash)
|
58
|
+
|
59
|
+
return super unless FFI::Platform::IS_GNU
|
60
|
+
|
61
|
+
# Hackety hack duplicated from FFI#attach_function
|
62
|
+
# rubocop:disable Style/ParallelAssignment, Style/TernaryParentheses, Layout/LineLength
|
63
|
+
mname, a2, a3, a4, a5 = name, func, args, returns, options
|
64
|
+
cname, arg_types, ret_type, opts = (a4 && (a2.is_a?(String) || a2.is_a?(Symbol))) ? [a2, a3, a4, a5] : [mname.to_s, a2, a3, a4]
|
65
|
+
# rubocop:enable Style/ParallelAssignment, Style/TernaryParentheses, Layout/LineLength
|
66
|
+
|
67
|
+
versions ||= @versions if defined?(@versions)
|
68
|
+
versions ||= []
|
69
|
+
|
70
|
+
return super if versions.empty?
|
71
|
+
|
72
|
+
function = versions.each do |v|
|
73
|
+
f = find_function_version(cname, v)
|
74
|
+
break f if f
|
75
|
+
end
|
76
|
+
|
77
|
+
# oh well, try the non-version function
|
78
|
+
return super unless function
|
79
|
+
|
80
|
+
arg_types = arg_types.map { |e| find_type(e) }
|
81
|
+
ret_type = find_type(ret_type)
|
82
|
+
|
83
|
+
invoker =
|
84
|
+
if arg_types.length.positive? && arg_types[arg_types.length - 1] == FFI::NativeType::VARARGS
|
85
|
+
VariadicInvoker.new(function, arg_types, ret_type, opts)
|
86
|
+
else
|
87
|
+
Function.new(ret_type, arg_types, function, opts)
|
88
|
+
end
|
89
|
+
invoker.attach(self, mname.to_s)
|
90
|
+
invoker
|
91
|
+
end
|
92
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
93
|
+
|
94
|
+
# use dlvsym to find a function address
|
95
|
+
# @param [String|Symbol] cname the function name
|
96
|
+
# @param [String] version the version name
|
97
|
+
# @return [FFI::Pointer] the function address
|
98
|
+
# @return [false] if the function/version combination does not exist in any library
|
99
|
+
def find_function_version(cname, version)
|
100
|
+
ffi_libraries.map(&:name).each do |l|
|
101
|
+
handle = GNUExtensions.dlopen(l, 1)
|
102
|
+
next if handle.null?
|
103
|
+
|
104
|
+
addr = GNUExtensions.dlvsym(handle, cname.to_s, version)
|
105
|
+
|
106
|
+
next if addr.null?
|
107
|
+
|
108
|
+
return addr
|
109
|
+
end
|
110
|
+
|
111
|
+
false
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|