├── .github └── workflows │ └── ruby.yml ├── .gitignore ├── .rspec ├── .yardopts ├── ChangeLog.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── ffi-libc.gemspec ├── gemspec.yml ├── lib └── ffi │ ├── libc.rb │ └── libc │ ├── af.rb │ ├── ifaddrs.rb │ ├── ifaddrs_union.rb │ ├── in6_addr.rb │ ├── in_addr.rb │ ├── libc.rb │ ├── rusage.rb │ ├── sockaddr.rb │ ├── sockaddr_dl.rb │ ├── sockaddr_family.rb │ ├── sockaddr_in.rb │ ├── sockaddr_in6.rb │ ├── timeval.rb │ ├── timezone.rb │ └── types.rb └── spec ├── ifaddrs_spec.rb ├── libc_spec.rb └── spec_helper.rb /.github/workflows/ruby.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | tests: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | ruby: 12 | - 2.4 13 | - 2.5 14 | - 2.6 15 | - 2.7 16 | - 3.0 17 | - jruby 18 | - truffleruby 19 | name: Ruby ${{ matrix.ruby }} 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Set up Ruby 23 | uses: ruby/setup-ruby@v1 24 | with: 25 | ruby-version: ${{ matrix.ruby }} 26 | - name: Install dependencies 27 | run: bundle install --jobs 4 --retry 3 28 | - name: Run tests 29 | run: bundle exec rake test 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /Gemfile.lock 2 | /doc/ 3 | /pkg/ 4 | /.bundle 5 | /.yardoc 6 | /vendor/bundle 7 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --colour --format documentation 2 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --markup markdown --title 'FFI LibC Documentation' --protected --files ChangeLog.md,LICENSE.txt 2 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | ### 0.1.1 / 2021-09-22 2 | 3 | * Removed `sys_errlist` and `sys_nerr` variables, as they have been removed 4 | from glibc (see: 5 | https://sourceware.org/pipermail/libc-announce/2020/000029.html). 6 | * Load `libc` via `FFI::Library::LIBC`. 7 | 8 | ### 0.1.0 / 2013-08-15 9 | 10 | * Require ffi ~> 1.0. 11 | * Added `alarm`. 12 | * Added `clock`. 13 | * Added `getc`. 14 | * Added `getchar`. 15 | * Added `gets`. 16 | * Added `ungetc`. 17 | * Added `getifaddrs`. 18 | * Added `puts`. 19 | * Added `putchar`. 20 | * Added `putc`. 21 | * Added `strcmp`. 22 | * Added `strncmp`. 23 | * Added {FFI::LibC::NULL}. 24 | * Added {FFI::LibC::Ifaddrs}. 25 | * Added {FFI::LibC.raise_error}. 26 | * Added {FFI::LibC.each_ifaddr}. 27 | * Added {FFI::LibC.rusage}. 28 | 29 | ### 0.0.5 / 2011-05-11 30 | 31 | * Skip `clearenv`, `memrchr`, `stdin`, `stdout` and `stderr` if they cannot 32 | be loaded (thanks FreneticEntropy and mephux). 33 | * The `libc` installed on OSX lacks these functions/global-variables. 34 | * Opt into `test.rubygems.org`. 35 | 36 | ### 0.0.4 / 2011-02-03 37 | 38 | * Require ffi >= 0.6.0, <= 1.1.0: 39 | * JRuby requires ffi >= 1.0.0. 40 | * A lot of projects still require ffi ~> 0.6.0. 41 | * Added `stdin`. 42 | * Added `stdout`. 43 | * Added `stderr`. 44 | 45 | ### 0.0.3 / 2010-08-03 46 | 47 | * Load libc from the current process. 48 | * Added `getgid` and `getegid`. 49 | * Added `getuid` and `geteuid`. 50 | * Added `getpid` and `getppid`. 51 | 52 | ### 0.0.2 / 2010-05-21 53 | 54 | * Removed a duplicate typedef of the `size_t` type. 55 | * Added the Structs: 56 | * {FFI::LibC::InAddr}. 57 | * {FFI::LibC::In6Addr}. 58 | * {FFI::LibC::Sockaddr}. 59 | * {FFI::LibC::SockaddrDL}. 60 | * {FFI::LibC::SockaddrIn}. 61 | * {FFI::LibC::SockaddrIn6}. 62 | 63 | ### 0.0.1 / 2010-05-19 64 | 65 | * Initial release. 66 | * Added the Structs: 67 | * {FFI::LibC::Timeval}. 68 | * {FFI::LibC::Timezone}. 69 | * Added the variables: 70 | * `sys_errlist` 71 | * `sys_nerr` 72 | * `errno` 73 | * Added the functions: 74 | * `brk` 75 | * `sbrk` 76 | * `calloc` 77 | * `malloc` 78 | * `free` 79 | * `realloc` 80 | * `getenv` 81 | * `putenv` 82 | * `unsetenv` 83 | * `clearenv` 84 | * `time` 85 | * `gettimeofday` 86 | * `settimeofday` 87 | * `mmap` 88 | * `munmap` 89 | * `bzero` 90 | * `memset` 91 | * `memcpy` 92 | * `memcmp` 93 | * `memchr` 94 | * `memrchr` 95 | * `strcpy` 96 | * `strncpy` 97 | * `strlen` 98 | * `index` 99 | * `rindex` 100 | * `strchr` 101 | * `strrchr` 102 | * `strstr` 103 | * `strerror` 104 | * `fopen` 105 | * `fdopen` 106 | * `freopen` 107 | * `fseek` 108 | * `ftell` 109 | * `rewind` 110 | * `fread` 111 | * `fwrite` 112 | * `fgetc` 113 | * `fgets` 114 | * `fputc` 115 | * `fputs` 116 | * `fflush` 117 | * `fclose` 118 | * `clearerr` 119 | * `feof` 120 | * `ferror` 121 | * `fileno` 122 | * `perror` 123 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | 5 | group :development do 6 | gem 'rake' 7 | gem 'rubygems-tasks', '~> 0.2' 8 | gem 'rspec', '~> 3.0' 9 | gem 'kramdown' 10 | gem 'yard', '~> 0.9' 11 | end 12 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2021 Hal Brodigan 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | 'Software'), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ffi-libc 2 | 3 | [![CI](https://github.com/postmodern/ffi-libc/actions/workflows/ruby.yml/badge.svg)](https://github.com/postmodern/ffi-libc/actions/workflows/ruby.yml) 4 | [![Code Climate](https://codeclimate.com/github/postmodern/ffi-libc.svg)](https://codeclimate.com/github/postmodern/ffi-libc) 5 | 6 | * [Source](https://github.com/postmodern/ffi-libc/) 7 | * [Issues](https://github.com/postmodern/ffi-libc/issues) 8 | * [Documentation](https://rubydoc.info/gems/ffi-libc) 9 | 10 | ## Description 11 | 12 | Useful Ruby FFI bindings for `libc`. 13 | 14 | ## Features 15 | 16 | * Provides common Structs used in `libc`: 17 | * {FFI::LibC::Ifaddrs} 18 | * {FFI::LibC::In6Addr} 19 | * {FFI::LibC::InAddr} 20 | * {FFI::LibC::SockaddrDL} 21 | * {FFI::LibC::SockaddrFamily} 22 | * {FFI::LibC::SockaddrIn6} 23 | * {FFI::LibC::SockaddrIn} 24 | * {FFI::LibC::Sockaddr} 25 | * {FFI::LibC::Timeval} 26 | * {FFI::LibC::Timezone} 27 | * {FFI::LibC::RUsage} 28 | * Binds to common functions and global variables in `libc`: 29 | * `errno.h`: 30 | * `sys_errlist` (BSD) 31 | * `sys_nerr` (BSD) 32 | * `errno` 33 | * `unistd.h`: 34 | * `brk` 35 | * `sbrk` 36 | * `getpid` 37 | * `getppid` 38 | * `getuid` 39 | * `geteuid` 40 | * `getgid` 41 | * `getegid` 42 | * `stdlib.h`: 43 | * `calloc` 44 | * `malloc` 45 | * `free` 46 | * `realloc` 47 | * `getenv` 48 | * `putenv` 49 | * `unsetenv` 50 | * `clearenv` 51 | * `time.h`: 52 | * `time` 53 | * `sys/time.h`: 54 | * `gettimeofday` 55 | * `settimeofday` 56 | * `sys/resource.h` / `bits/resource.h`: 57 | * `getrusage` 58 | * `sys/mman.h`: 59 | * `mmap` 60 | * `munmap` 61 | * `string.h`: 62 | * `bzero` 63 | * `memset` 64 | * `memcpy` 65 | * `memcmp` 66 | * `memchr` 67 | * `memrchr` 68 | * `strcpy` 69 | * `strncpy` 70 | * `strlen` 71 | * `index` 72 | * `rindex` 73 | * `strchr` 74 | * `strrchr` 75 | * `strstr` 76 | * `strerror` 77 | * `stdio.h`: 78 | * `stdin` 79 | * `stdout` 80 | * `stderr` 81 | * `fopen` 82 | * `fdopen` 83 | * `freopen` 84 | * `fseek` 85 | * `ftell` 86 | * `rewind` 87 | * `fread` 88 | * `fwrite` 89 | * `fgetc` 90 | * `fgets` 91 | * `fputc` 92 | * `fputs` 93 | * `fflush` 94 | * `fclose` 95 | * `clearerr` 96 | * `feof` 97 | * `ferror` 98 | * `fileno` 99 | * `perror` 100 | * `netdb.h`: 101 | * `getnameinfo` 102 | * `ifaddrs.h`: 103 | * `getifaddrs` 104 | * `freeifaddrs` 105 | 106 | ## Requirements 107 | 108 | * [ffi] ~> 1.0 109 | 110 | ## Install 111 | 112 | ```shell 113 | $ gem install ffi-libc 114 | ``` 115 | 116 | ### gemspec 117 | 118 | ```ruby 119 | gem.add_dependency 'ffi-libc', '~> 0.1' 120 | ``` 121 | 122 | ### Gemfile 123 | 124 | ```ruby 125 | gem 'ffi-libc', '~> 0.1' 126 | ``` 127 | 128 | ## License 129 | 130 | Copyright (c) 2010-2021 Hal Brodigan 131 | 132 | See {file:LICENSE.txt} for license information. 133 | 134 | [ffi]: https://github.com/ffi/ffi#readme 135 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | require 'rubygems/tasks' 4 | Gem::Tasks.new 5 | 6 | require 'rspec/core/rake_task' 7 | RSpec::Core::RakeTask.new 8 | task :test => :spec 9 | task :default => :spec 10 | 11 | require 'yard' 12 | YARD::Rake::YardocTask.new 13 | task :doc => :yard 14 | -------------------------------------------------------------------------------- /ffi-libc.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'yaml' 4 | 5 | Gem::Specification.new do |gem| 6 | gemspec = YAML.load_file('gemspec.yml') 7 | 8 | gem.name = gemspec.fetch('name') 9 | gem.version = gemspec.fetch('version') do 10 | lib_dir = File.join(File.dirname(__FILE__),'lib') 11 | $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir) 12 | 13 | require 'ffi/libc/version' 14 | FFI::LibC::VERSION 15 | end 16 | 17 | gem.summary = gemspec['summary'] 18 | gem.description = gemspec['description'] 19 | gem.licenses = Array(gemspec['license']) 20 | gem.authors = Array(gemspec['authors']) 21 | gem.email = gemspec['email'] 22 | gem.homepage = gemspec['homepage'] 23 | gem.metadata = gemspec['metadata'] if gemspec['metadata'] 24 | 25 | glob = lambda { |patterns| gem.files & Dir[*patterns] } 26 | 27 | gem.files = `git ls-files`.split($/) 28 | gem.files = glob[gemspec['files']] if gemspec['files'] 29 | 30 | gem.executables = gemspec.fetch('executables') do 31 | glob['bin/*'].map { |path| File.basename(path) } 32 | end 33 | gem.default_executable = gem.executables.first if Gem::VERSION < '1.7.' 34 | 35 | gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb'] 36 | gem.test_files = glob[gemspec['test_files'] || '{test/{**/}*_test.rb'] 37 | gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}'] 38 | 39 | gem.require_paths = Array(gemspec.fetch('require_paths') { 40 | %w[ext lib].select { |dir| File.directory?(dir) } 41 | }) 42 | 43 | gem.requirements = gemspec['requirements'] 44 | gem.required_ruby_version = gemspec['required_ruby_version'] 45 | gem.required_rubygems_version = gemspec['required_rubygems_version'] 46 | gem.post_install_message = gemspec['post_install_message'] 47 | 48 | split = lambda { |string| string.split(/,\s*/) } 49 | 50 | if gemspec['dependencies'] 51 | gemspec['dependencies'].each do |name,versions| 52 | gem.add_dependency(name,split[versions]) 53 | end 54 | end 55 | 56 | if gemspec['development_dependencies'] 57 | gemspec['development_dependencies'].each do |name,versions| 58 | gem.add_development_dependency(name,split[versions]) 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /gemspec.yml: -------------------------------------------------------------------------------- 1 | name: ffi-libc 2 | version: 0.1.1 3 | summary: Useful FFI bindings for libc 4 | description: Useful Ruby FFI bindings for libc. 5 | license: MIT 6 | authors: Postmodern 7 | email: postmodern.mod3@gmail.com 8 | homepage: https://github.com/postmodern/ffi-libc#redme 9 | has_yard: true 10 | 11 | metadata: 12 | documentation_uri: https://rubydoc.info/gems/ffi-libc 13 | source_code_uri: https://github.com/postmodern/ffi-libc 14 | bug_tracker_uri: https://github.com/postmodern/ffi-libc/issues 15 | changelog_uri: https://github.com/postmodern/ffi-libc/blob/master/ChangeLog.md 16 | 17 | dependencies: 18 | ffi: ~> 1.0 19 | 20 | development_dependencies: 21 | bundler: ~> 2.0 22 | -------------------------------------------------------------------------------- /lib/ffi/libc.rb: -------------------------------------------------------------------------------- 1 | require 'ffi/libc/types' 2 | require 'ffi/libc/af' 3 | require 'ffi/libc/ifaddrs' 4 | require 'ffi/libc/in_addr' 5 | require 'ffi/libc/in6_addr' 6 | require 'ffi/libc/sockaddr' 7 | require 'ffi/libc/sockaddr_dl' 8 | require 'ffi/libc/sockaddr_in' 9 | require 'ffi/libc/sockaddr_in6' 10 | require 'ffi/libc/timeval' 11 | require 'ffi/libc/timezone' 12 | require 'ffi/libc/rusage' 13 | require 'ffi/libc/libc' 14 | -------------------------------------------------------------------------------- /lib/ffi/libc/af.rb: -------------------------------------------------------------------------------- 1 | require 'socket' 2 | 3 | module FFI 4 | module LibC 5 | # 6 | # Contains `AF_*` constants culled from Ruby's ::Socket 7 | # 8 | module AF 9 | APPLETALK = 5 10 | AX25 = 3 11 | INET = 2 12 | INET6 = 10 13 | IPX = 4 14 | ISDN = 34 15 | LOCAL = 1 16 | MAX = 36 17 | ROUTE = 16 18 | SNA = 22 19 | UNIX = 1 20 | UNSPEC = 0 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/ffi/libc/ifaddrs.rb: -------------------------------------------------------------------------------- 1 | require 'ffi/libc/ifaddrs_union' 2 | require 'ffi/libc/sockaddr' 3 | 4 | module FFI 5 | module LibC 6 | # 7 | # @since 0.1.0 8 | # 9 | class Ifaddrs < FFI::Struct 10 | 11 | layout :ifa_next, :pointer, 12 | :ifa_name, :string, 13 | :ifa_flags, :uint, 14 | :ifa_addr, :pointer, 15 | :ifa_netmask, :pointer, 16 | :ifa_ifu, IfaddrsUnion, 17 | :ifa_data, :pointer 18 | 19 | # 20 | # Gets the next Interface Address in the list. 21 | # 22 | # @return [Ifaddrs, nil] 23 | # The next Interface Address in the list. 24 | # 25 | def next 26 | unless self[:ifa_next].null? 27 | Ifaddrs.new(self[:ifa_next]) 28 | end 29 | end 30 | 31 | # 32 | # The name of the Interface. 33 | # 34 | # @return [String] 35 | # The name. 36 | # 37 | def name 38 | self[:ifa_name] 39 | end 40 | 41 | # 42 | # The flags of the Interface. 43 | # 44 | # @return [Integer] 45 | # The flags. 46 | # 47 | def flags 48 | self[:ifa_flags] 49 | end 50 | 51 | # 52 | # The address of the Interface. 53 | # 54 | # @return [Sockaddr] 55 | # The basic socket address. 56 | # 57 | def addr 58 | Sockaddr.new(self[:ifa_addr]) 59 | end 60 | 61 | # 62 | # The netmask of the Interface. 63 | # 64 | # @return [Sockaddr] 65 | # The socket address containing the netmask. 66 | # 67 | def netmask 68 | Sockaddr.new(self[:ifa_netmask]) 69 | end 70 | 71 | # 72 | # The broadcast address of the Interface. 73 | # 74 | # @return [Sockaddr] 75 | # The broadcast address. 76 | # 77 | def broadaddr 78 | Sockaddr.new(self[:ifa_ifu][:ifu_broadaddr]) 79 | end 80 | 81 | # 82 | # The destination address of the Interface. 83 | # 84 | # @return [Sockaddr] 85 | # The destination address. 86 | # 87 | def dstaddr 88 | Sockaddr.new(self[:ifa_ifu][:ifu_dstaddr]) 89 | end 90 | 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /lib/ffi/libc/ifaddrs_union.rb: -------------------------------------------------------------------------------- 1 | require 'ffi' 2 | 3 | module FFI 4 | module LibC 5 | # 6 | # @since 0.1.0 7 | # 8 | class IfaddrsUnion < FFI::Union 9 | 10 | layout :ifu_broadaddr, :pointer, 11 | :ifu_dstaddr, :pointer 12 | 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/ffi/libc/in6_addr.rb: -------------------------------------------------------------------------------- 1 | module FFI 2 | module LibC 3 | # 4 | # Used to represent an IPv6 address in a `sock_addr_in6` structure 5 | # 6 | class In6Addr < ::FFI::Struct 7 | 8 | layout :s6_addr, [:uint8, 16] 9 | 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/ffi/libc/in_addr.rb: -------------------------------------------------------------------------------- 1 | require 'ffi/libc/types' 2 | 3 | module FFI 4 | module LibC 5 | # 6 | # Used to represent a 32-bit IPv4 address in a `sock_addr_in` structure 7 | # 8 | class InAddr < ::FFI::Struct 9 | 10 | layout :in_addr, :in_addr_t 11 | 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/ffi/libc/libc.rb: -------------------------------------------------------------------------------- 1 | require 'ffi/libc/types' 2 | require 'ffi/libc/timeval' 3 | require 'ffi/libc/timezone' 4 | require 'ffi/libc/ifaddrs' 5 | require 'ffi/libc/rusage' 6 | 7 | require 'ffi' 8 | 9 | module FFI 10 | module LibC 11 | extend FFI::Library 12 | 13 | ffi_lib FFI::Library::LIBC 14 | 15 | # The NULL constant 16 | NULL = nil 17 | 18 | # errno.h 19 | begin 20 | attach_variable :sys_errlist, :pointer 21 | attach_variable :sys_nerr, :int 22 | rescue FFI::NotFoundError 23 | end 24 | attach_variable :errno, :int 25 | 26 | def self.raise_error(error=self.errno) 27 | raise(strerror(error)) 28 | end 29 | 30 | # unistd.h 31 | attach_function :brk, [:pointer], :int 32 | attach_function :sbrk, [:pointer], :pointer 33 | attach_function :getpid, [], :pid_t 34 | attach_function :getppid, [], :pid_t 35 | attach_function :getuid, [], :uid_t 36 | attach_function :geteuid, [], :uid_t 37 | attach_function :getgid, [], :gid_t 38 | attach_function :getegid, [], :gid_t 39 | attach_function :alarm, [:uint], :uint 40 | 41 | # stdlib.h 42 | attach_function :calloc, [:size_t, :size_t], :pointer 43 | attach_function :malloc, [:size_t], :pointer 44 | attach_function :free, [:pointer], :void 45 | attach_function :realloc, [:pointer, :size_t], :pointer 46 | attach_function :getenv, [:string], :string 47 | attach_function :putenv, [:string], :int 48 | attach_function :unsetenv, [:string], :int 49 | 50 | begin 51 | attach_function :clearenv, [], :int 52 | rescue FFI::NotFoundError 53 | # clearenv is not available on OSX 54 | end 55 | 56 | # time.h 57 | attach_function :clock, [], :clock_t 58 | attach_function :time, [:pointer], :time_t 59 | 60 | # sys/time.h 61 | attach_function :gettimeofday, [:pointer, :pointer], :int 62 | attach_function :settimeofday, [:pointer, :pointer], :int 63 | 64 | # sys/mman.h 65 | attach_function :mmap, [:pointer, :size_t, :int, :int, :int, :off_t], :pointer 66 | attach_function :munmap, [:pointer, :size_t], :int 67 | 68 | # string.h 69 | attach_function :bzero, [:pointer, :size_t], :void 70 | attach_function :memset, [:pointer, :int, :size_t], :pointer 71 | attach_function :memcpy, [:buffer_out, :buffer_in, :size_t], :pointer 72 | attach_function :memcmp, [:buffer_in, :buffer_in, :size_t], :int 73 | attach_function :memchr, [:buffer_in, :int, :size_t], :pointer 74 | 75 | begin 76 | attach_function :memrchr, [:buffer_in, :int, :size_t], :pointer 77 | rescue FFI::NotFoundError 78 | # memrchr is not available on OSX 79 | end 80 | 81 | attach_function :strcpy, [:buffer_out, :string], :pointer 82 | attach_function :strncpy, [:buffer_out, :string, :size_t], :pointer 83 | attach_function :strcmp, [:buffer_in, :buffer_in], :int 84 | attach_function :strncmp, [:buffer_in, :buffer_in, :size_t], :int 85 | attach_function :strlen, [:buffer_in], :size_t 86 | attach_function :index, [:buffer_in, :int], :pointer 87 | attach_function :rindex, [:buffer_in, :int], :pointer 88 | attach_function :strchr, [:buffer_in, :int], :pointer 89 | attach_function :strrchr, [:buffer_in, :int], :pointer 90 | attach_function :strstr, [:buffer_in, :string], :pointer 91 | attach_function :strerror, [:int], :string 92 | 93 | begin 94 | attach_variable :stdin, :pointer 95 | attach_variable :stdout, :pointer 96 | attach_variable :stderr, :pointer 97 | rescue FFI::NotFoundError 98 | # stdin, stdout, stderr are not available on OSX 99 | end 100 | 101 | attach_function :fopen, [:string, :string], :FILE 102 | attach_function :fdopen, [:int, :string], :FILE 103 | attach_function :freopen, [:string, :string, :FILE], :FILE 104 | attach_function :fseek, [:FILE, :long, :int], :int 105 | attach_function :ftell, [:FILE], :long 106 | attach_function :rewind, [:FILE], :void 107 | attach_function :fread, [:buffer_out, :size_t, :size_t, :FILE], :size_t 108 | attach_function :fwrite, [:buffer_in, :size_t, :size_t, :FILE], :size_t 109 | attach_function :fgetc, [:FILE], :int 110 | attach_function :fgets, [:buffer_out, :int, :FILE], :pointer 111 | attach_function :fputc, [:int, :FILE], :int 112 | attach_function :fputs, [:buffer_in, :FILE], :int 113 | attach_function :fflush, [:FILE], :int 114 | attach_function :fclose, [:FILE], :int 115 | attach_function :clearerr, [:FILE], :void 116 | attach_function :feof, [:FILE], :int 117 | attach_function :ferror, [:FILE], :int 118 | attach_function :fileno, [:FILE], :int 119 | attach_function :perror, [:string], :void 120 | 121 | attach_function :getc, [:FILE], :int 122 | attach_function :getchar, [], :int 123 | attach_function :gets, [:buffer_out], :int 124 | attach_function :ungetc, [:int, :pointer], :int 125 | 126 | attach_function :putc, [:int, :FILE], :int 127 | attach_function :putchar, [:int], :int 128 | attach_function :puts, [:string], :int 129 | 130 | # netdb.h 131 | attach_function :getnameinfo, [ 132 | :pointer, 133 | :socklen_t, :pointer, 134 | :socklen_t, :pointer, 135 | :socklen_t, :int 136 | ], :int 137 | 138 | NI_MAXHOST = 1024 139 | NI_MAXSERV = 32 140 | 141 | NI_NUMERICHOST = 1 # Don't try to look up hostname. 142 | NI_NUMERICSERV = 2 # Don't convert port number to name. 143 | NI_NOFQDN = 4 # Only return nodename portion. 144 | NI_NAMEREQD = 8 # Don't return numeric addresses. 145 | NI_DGRAM = 16 # Look up UDP service rather than TCP. 146 | 147 | # ifaddrs.h 148 | attach_function :getifaddrs, [:pointer], :int 149 | attach_function :freeifaddrs, [:pointer], :void 150 | 151 | # 152 | # Enumerates over the Interface Addresses. 153 | # 154 | # @yield [ifaddr] 155 | # The given block will be passed each Interface Address. 156 | # 157 | # @yieldparam [Ifaddrs] ifaddr 158 | # An Interface Address. 159 | # 160 | # @return [Enumerator] 161 | # If no block is given, an enumerator will be returned. 162 | # 163 | # @since 0.1.0 164 | # 165 | def self.each_ifaddr 166 | return enum_for(__method__) unless block_given? 167 | 168 | ptr = MemoryPointer.new(:pointer) 169 | 170 | if getifaddrs(ptr) == -1 171 | raise_error 172 | end 173 | 174 | if (ifaddrs = ptr.get_pointer(0)).null? 175 | return 176 | end 177 | 178 | ifaddr = Ifaddrs.new(ifaddrs) 179 | 180 | while ifaddr 181 | yield ifaddr 182 | 183 | ifaddr = ifaddr.next 184 | end 185 | 186 | freeifaddrs(ifaddrs) 187 | end 188 | 189 | # bits/resource.h (Linux) / sys/resource.h (Darwin) 190 | RUSAGE_SELF = 0 191 | RUSAGE_CHILDREN = -1 192 | RUSAGE_THREAD = 1 # Linux/glibc only 193 | 194 | attach_function :getrusage, [:int, :pointer], :int 195 | 196 | # 197 | # Gets the RUsage for the user. 198 | # 199 | # @param [RUSAGE_SELF, RUSAGE_CHILDREN, RUSAGE_THREAD] who 200 | # Whome to get RUsage statistics for. 201 | # 202 | # @return [RUsage] 203 | # The RUsage statistics. 204 | # 205 | # @raise [RuntimeError] 206 | # An error has occurred. 207 | # 208 | # @since 0.1.0 209 | # 210 | def self.rusage(who=RUSAGE_SELF) 211 | rusage = RUsage.new 212 | 213 | unless (ret = getrusage(who,rusage)) == 0 214 | raise_error(ret) 215 | end 216 | 217 | return rusage 218 | end 219 | end 220 | end 221 | -------------------------------------------------------------------------------- /lib/ffi/libc/rusage.rb: -------------------------------------------------------------------------------- 1 | require 'ffi/libc/types' 2 | require 'ffi/libc/timeval' 3 | 4 | module FFI 5 | module LibC 6 | # 7 | # @since 0.1.0 8 | # 9 | class RUsage < FFI::Struct 10 | 11 | layout :ru_utime, Timeval, 12 | :ru_stime, Timeval, 13 | :ru_maxrss, :long, 14 | :ru_ixrss, :long, 15 | :ru_idrss, :long, 16 | :ru_isrss, :long, 17 | :ru_minflt, :long, 18 | :ru_majflt, :long, 19 | :ru_nswap, :long, 20 | :ru_inblock, :long, 21 | :ru_oublock, :long, 22 | :ru_msgsnd, :long, 23 | :ru_msgrcv, :long, 24 | :ru_nsignals, :long, 25 | :ru_nvcsw, :long, 26 | :ru_nivcsw, :long 27 | 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/ffi/libc/sockaddr.rb: -------------------------------------------------------------------------------- 1 | require 'ffi/libc/types' 2 | require 'ffi/libc/sockaddr_family' 3 | 4 | module FFI 5 | module LibC 6 | # 7 | # The generic `sockaddr` structure. 8 | # 9 | class Sockaddr < SockaddrFamily 10 | 11 | layout :family, :sa_family_t, 12 | :data, [:char, 14] 13 | 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/ffi/libc/sockaddr_dl.rb: -------------------------------------------------------------------------------- 1 | require 'ffi/libc/types' 2 | require 'ffi/libc/sockaddr_family' 3 | 4 | module FFI 5 | module LibC 6 | # 7 | # The data-link socket address 8 | # 9 | class SockaddrDL < SockaddrFamily 10 | 11 | layout :len, :uint8, 12 | :family, :sa_family_t, 13 | :sdl_index, :uint16, 14 | :dltype, :uint8, 15 | :nlen, :uint8, 16 | :alen, :uint8, 17 | :slen, :uint8, 18 | :_data, :char 19 | 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/ffi/libc/sockaddr_family.rb: -------------------------------------------------------------------------------- 1 | require 'ffi/libc/af' 2 | 3 | module FFI 4 | module LibC 5 | # 6 | # Common abstract superclass for all sockaddr struct classes 7 | # 8 | class SockaddrFamily < ::FFI::Struct 9 | 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/ffi/libc/sockaddr_in.rb: -------------------------------------------------------------------------------- 1 | require 'ffi/libc/types' 2 | require 'ffi/libc/sockaddr_family' 3 | 4 | module FFI 5 | module LibC 6 | # 7 | # sockaddr inet, always good to have around 8 | # 9 | class SockaddrIn < SockaddrFamily 10 | 11 | layout :len, :uint8, 12 | :family, :sa_family_t, 13 | :port, :in_port_t, 14 | :addr, :in_addr_t, 15 | :_sa_zero, [:uint8, 8] 16 | 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/ffi/libc/sockaddr_in6.rb: -------------------------------------------------------------------------------- 1 | require 'ffi/libc/types' 2 | require 'ffi/libc/sockaddr_family' 3 | require 'ffi/libc/in6_addr' 4 | 5 | module FFI 6 | module LibC 7 | # 8 | # IPv6 socket address 9 | # 10 | class SockaddrIn6 < SockaddrFamily 11 | 12 | layout :len, :uint8, 13 | :family, :sa_family_t, 14 | :port, :in_port_t, 15 | :flowinfo, :uint32, 16 | :addr, In6Addr 17 | 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/ffi/libc/timeval.rb: -------------------------------------------------------------------------------- 1 | require 'ffi/libc/types' 2 | 3 | module FFI 4 | module LibC 5 | class Timeval < FFI::Struct 6 | 7 | layout :tv_sec, :time_t, 8 | :tv_usec, :suseconds_t 9 | 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/ffi/libc/timezone.rb: -------------------------------------------------------------------------------- 1 | require 'ffi' 2 | 3 | module FFI 4 | module LibC 5 | class Timezone < FFI::Struct 6 | 7 | layout :tz_minutewest, :int, 8 | :tz_dsttime, :int 9 | 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/ffi/libc/types.rb: -------------------------------------------------------------------------------- 1 | require 'ffi' 2 | 3 | module FFI 4 | module LibC 5 | extend FFI::Library 6 | 7 | typedef :pointer, :FILE 8 | typedef :uint32, :in_addr_t 9 | typedef :uint16, :in_port_t 10 | 11 | # time.h 12 | typedef :long, :clock_t 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/ifaddrs_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ffi/libc/ifaddrs' 3 | 4 | describe Ifaddrs do 5 | subject { LibC.each_ifaddr.first } 6 | 7 | it "should have a name" do 8 | expect(subject.name).to_not be_empty 9 | end 10 | 11 | it "should have flags" do 12 | expect(subject.flags).to be_kind_of(Integer) 13 | end 14 | 15 | it "should have an addr" do 16 | expect(subject.addr).to be_kind_of(Sockaddr) 17 | end 18 | 19 | it "should have an netmask" do 20 | expect(subject.netmask).to be_kind_of(Sockaddr) 21 | end 22 | 23 | it "should have a broadcast addr" do 24 | expect(subject.broadaddr).to be_kind_of(Sockaddr) 25 | end 26 | 27 | it "should have a destination addr" do 28 | expect(subject.dstaddr).to be_kind_of(Sockaddr) 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/libc_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'ffi/libc' 3 | 4 | describe FFI::LibC do 5 | describe "NULL" do 6 | subject { described_class::NULL } 7 | 8 | it { expect(subject).to be == nil } 9 | end 10 | 11 | describe "each_ifaddr" do 12 | it "should yield Ifaddrs" do 13 | addrs = [] 14 | 15 | described_class.each_ifaddr do |ifaddr| 16 | addrs << ifaddr 17 | end 18 | 19 | expect(addrs).to all(be_kind_of(Ifaddrs)) 20 | end 21 | 22 | context "when not given a block" do 23 | subject { described_class.each_ifaddr } 24 | 25 | it { expect(subject).to be_kind_of(Enumerable) } 26 | end 27 | end 28 | 29 | describe "rusage" do 30 | subject { described_class.rusage } 31 | 32 | it "should be able to fetch its own memory usage" do 33 | expect(subject[:ru_maxrss]).to be > 4000 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rspec' 2 | require 'ffi/libc' 3 | 4 | include FFI 5 | include FFI::LibC 6 | --------------------------------------------------------------------------------