diff --git a/ext/hbsdctl.rb/ffi.c b/ext/hbsdctl.rb/ffi.c index 2348050..ecc6adc 100644 --- a/ext/hbsdctl.rb/ffi.c +++ b/ext/hbsdctl.rb/ffi.c @@ -1,5 +1,8 @@ #include #include +#include +#include +#include #include "include/ffi.h" /** @@ -56,6 +59,43 @@ ffi_reset(VALUE self, VALUE rb_feature, VALUE rb_path) } +/** + * BSD::Control::FFI.status + **/ +VALUE +ffi_status(VALUE self, VALUE rb_feature, VALUE rb_path) +{ + VALUE rb_enable_flag, rb_disable_flag; + char *enable_flag, *disable_flag, *path, enable_data[2], disable_data[2]; + int namespace; + + rb_enable_flag = rb_funcall(rb_feature, rb_intern("enable"), 0); + rb_disable_flag = rb_funcall(rb_feature, rb_intern("disable"), 0); + Check_Type(rb_path, T_STRING); + Check_Type(rb_enable_flag, T_STRING); + Check_Type(rb_disable_flag, T_STRING); + path = RSTRING_PTR(rb_path); + enable_flag = RSTRING_PTR(rb_enable_flag); + disable_flag = RSTRING_PTR(rb_disable_flag); + if (extattr_string_to_namespace("system", &namespace) == -1) { + rb_syserr_fail(errno, "extattr_string_to_namespace"); + } + if (extattr_get_file(path, namespace, enable_flag, &enable_data, 2) == -1) { + rb_syserr_fail(errno, "extattr_get_file"); + } + if (extattr_get_file(path, namespace, disable_flag, &disable_data, 2) == -1) { + rb_syserr_fail(errno, "extattr_get_file"); + } + if (strcmp(enable_data, disable_data) == 0) { + return (ID2SYM(rb_intern("conflict"))); + } else if (strcmp(enable_data, "1") == 0) { + return (ID2SYM(rb_intern("enabled"))); + } else { + return (ID2SYM(rb_intern("disabled"))); + } +} + + /** * BSD::Control::FFI.library_version **/ diff --git a/ext/hbsdctl.rb/hbsdctl.c b/ext/hbsdctl.rb/hbsdctl.c index ac29d96..814ba1e 100644 --- a/ext/hbsdctl.rb/hbsdctl.c +++ b/ext/hbsdctl.rb/hbsdctl.c @@ -15,5 +15,6 @@ Init_hbsdctl(void) rb_define_singleton_method(rb_mFFI, "available_features", ffi_available_features, 0); rb_define_singleton_method(rb_mFFI, "library_version", ffi_library_version, 0); rb_define_singleton_method(rb_mFFI, "reset!", ffi_reset, 2); + rb_define_singleton_method(rb_mFFI, "status", ffi_status, 2); rb_define_private_method(rb_cFeature, "set!", feature_set, 2); } diff --git a/ext/hbsdctl.rb/include/ffi.h b/ext/hbsdctl.rb/include/ffi.h index f42f344..0337851 100644 --- a/ext/hbsdctl.rb/include/ffi.h +++ b/ext/hbsdctl.rb/include/ffi.h @@ -1,4 +1,5 @@ #include VALUE ffi_library_version(VALUE); VALUE ffi_available_features(VALUE); +VALUE ffi_status(VALUE, VALUE, VALUE); VALUE ffi_reset(VALUE, VALUE, VALUE); diff --git a/lib/bsd/control/feature.rb b/lib/bsd/control/feature.rb index c3ec9e7..5ea8ab3 100644 --- a/lib/bsd/control/feature.rb +++ b/lib/bsd/control/feature.rb @@ -55,6 +55,47 @@ module BSD::Control FFI.reset!(self, path) end + ## + # @return [Boolean] + # Returns true when a feature is enabled. + def enabled?(path) + status(path) == :enabled + end + + ## + # @return [Boolean] + # Returns true when a feature is disabled. + def disabled?(path) + status(path) == :disabled + end + + ## + # @return [Boolean] + # Returns true when a feature is configured to use the system default. + def sysdef?(path) + status(path) == :sysdef + end + + ## + # @return [Boolean] + # Returns true when a feature is in conflict + # (i.e: the feature is both enabled and disabled at the same time). + def conflict?(path) + status(path) == :conflict + end + + ## + # @param [String] path + # The path to a file. + # @return [Symbol] + # Returns the feature status of a file. + # Status can be one of: `:conflict`, `:sysdef`, `:enabled`, `:disabled`. + def status(path) + FFI.status(self, path) + rescue Errno::ENOATTR + :sysdef + end + # @endgroup ## diff --git a/test/superuser/status_feature_test.rb b/test/superuser/status_feature_test.rb new file mode 100644 index 0000000..fba72f3 --- /dev/null +++ b/test/superuser/status_feature_test.rb @@ -0,0 +1,40 @@ +require_relative "../setup" +module BSD::Control + class EnableFeatureTest < Test::Unit::TestCase + require 'fileutils' + include FileUtils + + def test_mprotect_sysdef_status + touch(file) + assert_equal :sysdef, + BSD::Control.feature!(:mprotect).status(file) + ensure + rm(file) + end + + def test_mprotect_enabled_status + touch(file) + BSD::Control.feature!(:mprotect).enable!(file) + assert_equal :enabled, + BSD::Control.feature!(:mprotect).status(file) + ensure + rm(file) + end + + + def test_mprotect_disabled_status + touch(file) + BSD::Control.feature!(:mprotect).disable!(file) + assert_equal :disabled, + BSD::Control.feature!(:mprotect).status(file) + ensure + rm(file) + end + + private + + def file + File.join(__dir__, "file") + end + end +end