Make the __FILE__ and __LINE__ part of the log message.
[apple-ir-control.git] / apple-ir-control.cc
index 8a6f5df029a473f66b91d074b96444969f0ff739..5a56489d56eace4d323873284ba145cd141d0523 100644 (file)
@@ -1,3 +1,12 @@
+//
+// apple-ir-control
+// Copyright (c) 2016, Robert Sesek <https://www.bluestatic.org>
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation, either version 3 of the License, or any later version.
+//
+
 #include <CoreFoundation/CoreFoundation.h>
 #include <IOKit/hid/IOHIDLib.h>
 #include <IOKit/IOKitLib.h>
 const CFStringRef kPrefDomain = CFSTR("com.apple.driver.AppleIRController");
 const CFStringRef kPrefEnabled = CFSTR("DeviceEnabled");
 
-bool VERBOSE = true;
+#if NDEBUG
+const bool VERBOSE = false;
+#else
+const bool VERBOSE = true;
+#endif
+
+#define LOCATION_LINE(x) #x
+#define LOCATION(line) __FILE__ ":" LOCATION_LINE(line)
 
 #define LOG(msg, ...) do { \
     if (VERBOSE) { \
-      printf(msg "   [%s:%d]\n", ##__VA_ARGS__, __FILE__, __LINE__); \
+      printf("[" LOCATION(__LINE__) "] " msg "\n", ##__VA_ARGS__); \
     } \
   } while(0)
-#define ERROR(msg, ...) fprintf(stderr, msg "   [%s:%d]\n", ##__VA_ARGS__, __FILE__, __LINE__)
+#define ERROR(msg, ...) fprintf(stderr, "[" LOCATION(__LINE__) "] " msg "\n", ##__VA_ARGS__)
 
 class ScopedIOHIDManager {
  public:
@@ -27,15 +43,6 @@ class ScopedIOHIDManager {
 
   ScopedIOHIDManager(const ScopedIOHIDManager&) = delete;
 
-  bool Open() {
-    IOReturn ret = IOHIDManagerOpen(manager_, kIOHIDManagerOptionNone);
-    if (ret != kIOReturnSuccess) {
-      ERROR("Failed to IOHIDManagerOpen: 0x%x", ret);
-      return false;
-    }
-    return true;
-  }
-
   IOHIDManagerRef get() { return manager_; }
 
  private:
@@ -67,14 +74,12 @@ class ScopedCFTypeRef {
 bool IsIRAvailable() {
   ScopedIOHIDManager manager;
   IOHIDManagerSetDeviceMatching(manager.get(), nullptr);
-  if (!manager.Open()) {
-    //return false;
-  }
   ScopedCFTypeRef<CFSetRef> devices(IOHIDManagerCopyDevices(manager.get()));
   if (!devices) {
     ERROR("Failed to IOHIDManagerCopyDevices");
     return false;
   }
+
   std::vector<void*> devices_array(CFSetGetCount(devices.get()), nullptr);
   CFSetGetValues(devices.get(), const_cast<const void**>(devices_array.data()));
   for (const auto& device : devices_array) {
@@ -92,22 +97,42 @@ bool IsIRAvailable() {
   return false;
 }
 
-CFTypeRef GetUserPropertyValue() {
-  return CFPreferencesCopyValue(kPrefEnabled, kPrefDomain,
-      kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
+const char* GetBooleanDescription(CFTypeRef boolean) {
+  if (CFGetTypeID(boolean) != CFBooleanGetTypeID()) {
+    ERROR("Unexpected non-boolean CFTypeRef");
+    abort();
+  }
+  return CFBooleanGetValue(static_cast<CFBooleanRef>(boolean)) ? "on" : "off";
 }
 
-int HandleRead() {
-  CFTypeRef user_prop = GetUserPropertyValue();
-  CFShow(user_prop);
+bool SynchronizePrefs() {
+  bool rv = CFPreferencesSynchronize(kPrefDomain, kCFPreferencesAnyUser,
+      kCFPreferencesCurrentHost);
+  if (!rv) {
+    ERROR("Failed to CFPreferencesSynchronize");
+  }
+  return rv;
+}
 
+bool CreateIOServiceIterator(io_iterator_t* iterator) {
   CFMutableDictionaryRef matching_dict = IOServiceMatching("AppleIRController");
-  io_iterator_t iterator;
-  kern_return_t kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dict, &iterator);
+  kern_return_t kr = IOServiceGetMatchingServices(
+      kIOMasterPortDefault, matching_dict, iterator);
   if (kr != KERN_SUCCESS) {
     ERROR("Failed to IOServiceGetMatchingServices: 0x%x", kr);
-    return EXIT_FAILURE;
+    return false;
   }
+  return true;;
+}
+
+int HandleRead() {
+  CFTypeRef user_prop = CFPreferencesCopyValue(kPrefEnabled, kPrefDomain,
+      kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
+  printf("Userspace property value: %s\n", GetBooleanDescription(user_prop));
+
+  io_iterator_t iterator;
+  if (!CreateIOServiceIterator(&iterator))
+    return EXIT_FAILURE;
 
   io_object_t service;
   bool did_find = false;
@@ -125,21 +150,12 @@ int HandleRead() {
 
     ScopedCFTypeRef<CFTypeRef> device_enabled(
         IORegistryEntryCreateCFProperty(service, kPrefEnabled, nullptr, 0));
-    CFShow(device_enabled.get());
-
-#if 0
-    ScopedCFTypeRef<CFMutableDictionaryRef> props;
-    kr = IORegistryEntryCreateCFProperties(service, props.pointer_to(), nullptr, 0);
-    if (kr != KERN_SUCCESS) {
-      ERROR("Failed to IORegistryEntryCreateCFProperties(%s) = 0x%x", name, kr);
-      continue;
-    }
-    CFShow(props.get());
-#endif
+    printf("Kernel property value %s: %s\n",
+        name, GetBooleanDescription(device_enabled.get()));
   }
 
   if (!did_find) {
-    ERROR("Failed to match AppleIRController");
+    ERROR("Failed to match any AppleIRController");
     return EXIT_FAILURE;
   }
 
@@ -147,7 +163,41 @@ int HandleRead() {
 }
 
 int HandleWrite(bool enable) {
-  return EXIT_SUCCESS;
+  if (geteuid() != 0) {
+    ERROR("This operation must be performed as root");
+    return EXIT_FAILURE;
+  }
+
+  const CFBooleanRef enabled_value = enable ? kCFBooleanTrue : kCFBooleanFalse;
+
+  CFPreferencesSetValue(kPrefEnabled, enabled_value, kPrefDomain,
+      kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
+  if (!SynchronizePrefs())
+    return EXIT_FAILURE;
+
+  io_iterator_t iterator;
+  if (!CreateIOServiceIterator(&iterator))
+    return EXIT_FAILURE;
+
+  io_object_t service;
+  while ((service = IOIteratorNext(iterator))) {
+    io_name_t name;
+    kern_return_t kr = IORegistryEntryGetName(service, name);
+    if (kr != KERN_SUCCESS) {
+      ERROR("Failed to IORegistryEntryGetName: 0x%x", kr);
+      continue;
+    }
+
+    LOG("Setting property for %s to %d", name, enable);
+
+    kr = IORegistryEntrySetCFProperty(service, kPrefEnabled, enabled_value);
+    if (kr != KERN_SUCCESS) {
+      ERROR("Failed to IORegistryEntrySetCFProperty: 0x%x", kr);
+      continue;
+    }
+  }
+
+  return HandleRead();
 }
 
 int main(int argc, char* argv[]) {
@@ -159,9 +209,9 @@ int main(int argc, char* argv[]) {
   if (argc == 1) {
     return HandleRead();
   } else if (argc == 2) {
-    if (strcmp("on", argv[1])) {
+    if (strcmp("on", argv[1]) == 0) {
       return HandleWrite(true);
-    } else if (strcmp("off", argv[1])) {
+    } else if (strcmp("off", argv[1]) == 0) {
       return HandleWrite(false);
     }
   }