#include <CoreFoundation/CoreFoundation.h>
#include <mach/mach.h>
#include <mach/mach_host.h>
#include <mach/mach_error.h>
#include <libc.h>
#include <servers/bootstrap.h>
#include <sysexits.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOKitServer.h>
#include <IOKit/IOCFURLAccess.h>
#include <IOKit/IOCFSerialize.h>
#include <IOKit/IOCFUnserialize.h>
#include <IOKit/IOMessage.h>
#include <IOKit/ps/IOPSKeys.h>
#include <SystemConfiguration/SystemConfiguration.h>
#if UPS_DEBUG
#include <SystemConfiguration/SCPrivate.h>
#endif
#include "IOUPSPlugIn.h"
#include "IOUPSPrivate.h"
#define kDefaultUPSName "Generic UPS"
static CFRunLoopSourceRef gClientRequestRunLoopSource = NULL;
static CFRunLoopRef gMainRunLoop = NULL;
static CFMutableArrayRef gUPSDataArrayRef = NULL;
static IONotificationPortRef gNotifyPort = NULL;
static io_iterator_t gAddedIter = NULL;
typedef struct UPSData
{
io_object_t notification;
IOUPSPlugInInterface ** upsPlugInInterface;
int upsID;
Boolean isPresent;
CFMutableDictionaryRef upsStoreDict;
SCDynamicStoreRef upsStore;
CFStringRef upsStoreKey;
} UPSData;
typedef UPSData * UPSDataRef;
static void SignalHandler(int sigraised);
static void InitUPSNotifications();
static void UPSDeviceAdded(void *refCon, io_iterator_t iterator);
static void DeviceNotification(void *refCon, io_service_t service, natural_t messageType, void *messageArgument);
static void UPSEventCallback(void * target, IOReturn result, void *refcon, void *sender, CFDictionaryRef event);
static void ProcessUPSEvent(UPSDataRef upsDataRef, CFDictionaryRef event);
static UPSDataRef GetPrivateData( CFDictionaryRef properties );
static IOReturn CreatePowerManagerUPSEntry(UPSDataRef upsDataRef, CFDictionaryRef properties, CFSetRef capabilities);
static Boolean SetupMIGServer();
int main (int argc, const char * argv[]) {
openlog("upsd", LOG_PID|LOG_NDELAY, LOG_USER);
signal(SIGINT, SignalHandler);
SetupMIGServer();
InitUPSNotifications();
CFRunLoopRun();
return 0;
}
void SignalHandler(int sigraised)
{
syslog(LOG_INFO, "upsd: exiting SIGINT\n");
IONotificationPortDestroy(gNotifyPort);
if (gAddedIter)
{
IOObjectRelease(gAddedIter);
gAddedIter = 0;
}
exit(0);
}
extern void upsd_mach_port_callback(
CFMachPortRef port,
void *msg,
CFIndex size,
void *info);
Boolean SetupMIGServer()
{
Boolean result = true;
kern_return_t kern_result = KERN_SUCCESS;
CFRunLoopSourceContext sourceContext;
unsigned int sourcePriority = 1;
CFMachPortRef upsdMachPort = NULL;
kern_result = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
if (kern_result != KERN_SUCCESS)
{
result = false;
goto finish;
}
gMainRunLoop = CFRunLoopGetCurrent();
if (!gMainRunLoop) {
result = false;
goto finish;
}
bzero(&sourceContext, sizeof(CFRunLoopSourceContext));
sourceContext.version = 0;
upsdMachPort = CFMachPortCreate(kCFAllocatorDefault,
upsd_mach_port_callback, NULL, NULL);
gClientRequestRunLoopSource = CFMachPortCreateRunLoopSource(
kCFAllocatorDefault, upsdMachPort, sourcePriority++);
if (!gClientRequestRunLoopSource) {
result = false;
goto finish;
}
CFRunLoopAddSource(gMainRunLoop, gClientRequestRunLoopSource,
kCFRunLoopDefaultMode);
kern_result = bootstrap_register(bootstrap_port,
kIOUPSPlugInServerName, CFMachPortGetPort(upsdMachPort));
switch (kern_result) {
case BOOTSTRAP_SUCCESS:
break;
case BOOTSTRAP_NOT_PRIVILEGED:
syslog(LOG_INFO, "upsd exit: BOOTSTRAP_NOT_PRIVILIGED");
exit(EX_OSERR);
case BOOTSTRAP_SERVICE_ACTIVE:
syslog(LOG_INFO, "upsd exit: BOOTSTRAP_SERVICE_ACTIVE");
exit(EX_OSERR);
default:
syslog(LOG_INFO, "upsd exit: undefined mig error");
exit(EX_OSERR);
}
finish:
if (gClientRequestRunLoopSource) CFRelease(gClientRequestRunLoopSource);
if (upsdMachPort) CFRelease(upsdMachPort);
return result;
}
void InitUPSNotifications()
{
CFMutableDictionaryRef matchingDict;
CFMutableDictionaryRef propertyDict;
mach_port_t masterPort;
kern_return_t kr;
kr = IOMasterPort(bootstrap_port, &masterPort);
if (kr || !masterPort)
return;
gNotifyPort = IONotificationPortCreate(masterPort);
CFRunLoopAddSource( CFRunLoopGetCurrent(),
IONotificationPortGetRunLoopSource(gNotifyPort),
kCFRunLoopDefaultMode);
matchingDict = IOServiceMatching(kIOServiceClass);
if (!matchingDict)
return;
propertyDict = CFDictionaryCreateMutable(kCFAllocatorDefault,
0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!propertyDict)
{
CFRelease(matchingDict);
return;
}
CFDictionarySetValue(propertyDict, CFSTR(kIOUPSDeviceKey), kCFBooleanTrue);
CFDictionarySetValue(matchingDict, CFSTR(kIOPropertyMatchKey), propertyDict);
CFRelease(propertyDict);
kr = IOServiceAddMatchingNotification(gNotifyPort, kIOFirstMatchNotification, matchingDict, UPSDeviceAdded, NULL, &gAddedIter );
if ( kr != kIOReturnSuccess )
return;
UPSDeviceAdded( NULL, gAddedIter );
}
void UPSDeviceAdded(void *refCon, io_iterator_t iterator)
{
io_object_t upsDevice = NULL;
UPSDataRef upsDataRef = NULL;
CFDictionaryRef upsProperties = NULL;
CFDictionaryRef upsEvent = NULL;
CFSetRef upsCapabilites = NULL;
IOCFPlugInInterface ** plugInInterface = NULL;
IOUPSPlugInInterface ** upsPlugInInterface = NULL;
HRESULT result = S_FALSE;
IOReturn kr;
SInt32 score;
while ( upsDevice = IOIteratorNext(iterator) )
{
kr = IOCreatePlugInInterfaceForService(upsDevice, kIOUPSPlugInTypeID,
kIOCFPlugInInterfaceID, &plugInInterface, &score);
if ( kr != kIOReturnSuccess )
goto UPSDEVICEADDED_NONPLUGIN_CLEANUP;
result = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUPSPlugInInterfaceID),
(LPVOID)&upsPlugInInterface);
if ( ( result == S_OK ) && upsPlugInInterface )
{
kr = (*upsPlugInInterface)->getProperties(upsPlugInInterface, &upsProperties);
if (kr != kIOReturnSuccess)
goto UPSDEVICEADDED_FAIL;
upsDataRef = GetPrivateData(upsProperties);
if ( !upsDataRef )
goto UPSDEVICEADDED_FAIL;
upsDataRef->upsPlugInInterface = upsPlugInInterface;
upsDataRef->isPresent = true;
kr = (*upsPlugInInterface)->getCapabilities(upsPlugInInterface, &upsCapabilites);
if (kr != kIOReturnSuccess)
goto UPSDEVICEADDED_FAIL;
kr = CreatePowerManagerUPSEntry(upsDataRef, upsProperties, upsCapabilites);
if (kr != kIOReturnSuccess)
goto UPSDEVICEADDED_FAIL;
kr = (*upsPlugInInterface)->getEvent(upsPlugInInterface, &upsEvent);
if (kr != kIOReturnSuccess)
goto UPSDEVICEADDED_FAIL;
ProcessUPSEvent(upsDataRef, upsEvent);
(*upsPlugInInterface)->setEventCallback(upsPlugInInterface, UPSEventCallback, NULL, upsDataRef);
IOServiceAddInterestNotification(
gNotifyPort, upsDevice, kIOGeneralInterest, DeviceNotification, upsDataRef, &(upsDataRef->notification) );
goto UPSDEVICEADDED_CLEANUP;
}
UPSDEVICEADDED_FAIL:
if ( upsPlugInInterface )
{
(*upsPlugInInterface)->Release(upsPlugInInterface);
upsPlugInInterface = NULL;
}
UPSDEVICEADDED_CLEANUP:
(*plugInInterface)->Release(plugInInterface);
UPSDEVICEADDED_NONPLUGIN_CLEANUP:
IOObjectRelease(upsDevice);
}
}
void DeviceNotification(void * refCon,
io_service_t service,
natural_t messageType,
void * messageArgument )
{
kern_return_t kr;
UPSDataRef upsDataRef = (UPSDataRef) refCon;
if ( (upsDataRef != NULL) &&
(messageType == kIOMessageServiceIsTerminated) )
{
upsDataRef->isPresent = FALSE;
SCDynamicStoreRemoveValue(upsDataRef->upsStore, upsDataRef->upsStoreKey);
if (upsDataRef->upsPlugInInterface != NULL)
{
kr = (*(upsDataRef->upsPlugInInterface))->Release (upsDataRef->upsPlugInInterface);
upsDataRef->upsPlugInInterface = NULL;
}
if (upsDataRef->notification != NULL)
{
kr = IOObjectRelease(upsDataRef->notification);
upsDataRef->notification = NULL;
}
if (upsDataRef->upsStoreKey)
{
CFRelease(upsDataRef->upsStoreKey);
upsDataRef->upsStoreKey = NULL;
}
if (upsDataRef->upsStoreDict)
{
CFRelease(upsDataRef->upsStoreDict);
upsDataRef->upsStoreDict = NULL;
}
if (upsDataRef->upsStore)
{
CFRelease(upsDataRef->upsStore);
upsDataRef->upsStore = NULL;
}
}
}
void UPSEventCallback( void * target,
IOReturn result,
void * refcon,
void * sender,
CFDictionaryRef event)
{
ProcessUPSEvent((UPSDataRef) refcon, event);
}
void ProcessUPSEvent(UPSDataRef upsDataRef, CFDictionaryRef event)
{
UInt32 count, index;
if ( !upsDataRef || !event)
return;
if ( count = CFDictionaryGetCount(event) )
{
CFTypeRef * keys = (CFTypeRef *) malloc(sizeof(CFTypeRef) * count);
CFTypeRef * values = (CFTypeRef *) malloc(sizeof(CFTypeRef) * count);
CFDictionaryGetKeysAndValues(event, (const void **)keys, (const void **)values);
for (index = 0; index < count; index++)
CFDictionarySetValue(upsDataRef->upsStoreDict, keys[index], values[index]);
free (keys);
free (values);
SCDynamicStoreSetValue(upsDataRef->upsStore, upsDataRef->upsStoreKey, upsDataRef->upsStoreDict);
}
}
UPSDataRef GetPrivateData( CFDictionaryRef properties )
{
UPSDataRef upsDataRef = NULL;
CFMutableDataRef data = NULL;
UInt32 i = 0;
UInt32 count = 0;
if (!gUPSDataArrayRef &&
!(gUPSDataArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks)))
{
return NULL;
}
count = CFArrayGetCount(gUPSDataArrayRef);
for ( i = 0; i < count; i++)
{
data = (CFMutableDataRef)CFArrayGetValueAtIndex(gUPSDataArrayRef, i);
if ( !data )
continue;
upsDataRef =(UPSDataRef)CFDataGetMutableBytePtr(data);
if (upsDataRef && !(upsDataRef->isPresent))
break;
upsDataRef = NULL;
}
if ( (upsDataRef == NULL) &&
(data = CFDataCreateMutable(kCFAllocatorDefault, sizeof(UPSData))) )
{
upsDataRef =(UPSDataRef)CFDataGetMutableBytePtr(data);
bzero( upsDataRef, sizeof(UPSData) );
CFArrayAppendValue(gUPSDataArrayRef, data);
CFRelease(data);
}
if ( upsDataRef != NULL )
{
upsDataRef->upsID = i;
}
return upsDataRef;
}
IOReturn CreatePowerManagerUPSEntry(UPSDataRef upsDataRef, CFDictionaryRef properties, CFSetRef capabilities)
{
CFMutableDictionaryRef upsStoreDict = NULL;
CFStringRef upsName = NULL;
CFStringRef transport = NULL;
CFStringRef upsStoreKey = NULL;
CFNumberRef number = NULL;
SCDynamicStoreRef upsStore = NULL;
IOReturn status = kIOReturnSuccess;
int elementValue = 0;
char upsLabelString[255];
if ( !upsDataRef || !properties || !capabilities)
return kIOReturnError;
upsStoreDict = CFDictionaryCreateMutable(kCFAllocatorDefault,
0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if ( upsStoreDict )
{
upsName = (CFStringRef) CFDictionaryGetValue( properties, CFSTR( kIOPSNameKey ) );
if ( !upsName )
upsName = CFSTR(kDefaultUPSName);
transport = (CFStringRef) CFDictionaryGetValue( properties, CFSTR( kIOPSTransportTypeKey ) );
CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSNameKey), upsName);
CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSTransportTypeKey), transport);
CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSIsPresentKey), kCFBooleanTrue);
CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSIsChargingKey), kCFBooleanTrue);
CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSPowerSourceStateKey), CFSTR(kIOPSACPowerValue));
number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &upsDataRef->upsID);
CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSPowerSourceIDKey), number);
CFRelease(number);
elementValue = 100;
number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &elementValue);
CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSMaxCapacityKey), number);
CFRelease(number);
if (CFSetContainsValue(capabilities, CFSTR(kIOPSCurrentCapacityKey)))
{
elementValue = 100;
number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &elementValue);
CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSCurrentCapacityKey), number);
CFRelease(number);
}
if (CFSetContainsValue(capabilities, CFSTR(kIOPSTimeToEmptyKey)))
{
elementValue = 100;
number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &elementValue);
CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSTimeToEmptyKey), number);
CFRelease(number);
}
if (CFSetContainsValue(capabilities, CFSTR(kIOPSVoltageKey)))
{
elementValue = 13 * 1000 / 100;
number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &elementValue);
CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSVoltageKey), number);
CFRelease(number);
}
if (CFSetContainsValue(capabilities, CFSTR(kIOPSCurrentKey)))
{
elementValue = 1; number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &elementValue);
CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSCurrentKey), number);
CFRelease(number);
}
}
upsStore = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("UPS Power Manager"), NULL, NULL);
sprintf(upsLabelString,"/UPS%d", upsDataRef->upsID);
#if 0
SCLog(TRUE, LOG_NOTICE, CFSTR("What does CreatePowerManagerUPSEntry think our key name is?"));
SCLog(TRUE, LOG_NOTICE, CFSTR(" %@%@%@"), kSCDynamicStoreDomainState, CFSTR(kIOPSDynamicStorePath),
CFStringCreateWithCStringNoCopy(NULL, upsLabelString, kCFStringEncodingMacRoman, kCFAllocatorNull));
#endif
upsStoreKey = SCDynamicStoreKeyCreate(kCFAllocatorDefault, CFSTR("%@%@%@"),
kSCDynamicStoreDomainState, CFSTR(kIOPSDynamicStorePath),
CFStringCreateWithCStringNoCopy(NULL, upsLabelString, kCFStringEncodingMacRoman, kCFAllocatorNull));
if(!SCDynamicStoreSetValue(upsStore, upsStoreKey, upsStoreDict))
{
status = SCError();
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: Encountered SCDynamicStoreSetValue error 0x%x"), status);
#endif
}
upsDataRef->upsStoreDict = upsStoreDict;
upsDataRef->upsStore = upsStore;
upsDataRef->upsStoreKey = upsStoreKey;
return status;
}
kern_return_t _io_ups_send_command(
mach_port_t server,
int upsID,
void * commandBuffer,
IOByteCount commandSize)
{
CFDictionaryRef command;
CFMutableDataRef data;
UPSDataRef upsDataRef;
IOReturn res = kIOReturnError;
command = (CFDictionaryRef)IOCFUnserialize(commandBuffer, kCFAllocatorDefault, kNilOptions, NULL);
if (command)
{
if (!gUPSDataArrayRef || (upsID >= CFArrayGetCount(gUPSDataArrayRef)))
{
res = kIOReturnBadArgument;
}
else
{
data = (CFMutableDataRef)CFArrayGetValueAtIndex(gUPSDataArrayRef, upsID);
upsDataRef =(UPSDataRef)CFDataGetMutableBytePtr(data);
if (upsDataRef && upsDataRef->upsPlugInInterface)
res = (*upsDataRef->upsPlugInInterface)->sendCommand(upsDataRef->upsPlugInInterface, command);
}
}
return res;
}
kern_return_t _io_ups_get_event(
mach_port_t server,
int upsID,
void ** eventBufferPtr,
IOByteCount * eventBufferSizePtr)
{
CFDictionaryRef event;
CFMutableDataRef data;
CFDataRef serializedData;
UPSDataRef upsDataRef;
IOReturn res = kIOReturnError;
if (!eventBufferPtr || !eventBufferSizePtr ||
!gUPSDataArrayRef || (upsID >= CFArrayGetCount(gUPSDataArrayRef)))
{
return kIOReturnBadArgument;
}
data = (CFMutableDataRef)CFArrayGetValueAtIndex(gUPSDataArrayRef, upsID);
upsDataRef = (UPSDataRef)CFDataGetMutableBytePtr(data);
if (!upsDataRef || !upsDataRef->upsPlugInInterface)
return kIOReturnBadArgument;
res = (*upsDataRef->upsPlugInInterface)->getEvent(upsDataRef->upsPlugInInterface, &event);
if ((res != kIOReturnSuccess) || !event)
return kIOReturnError;
serializedData = (CFDataRef)IOCFSerialize( event, kNilOptions );
if (!serializedData)
return kIOReturnError;
*eventBufferSizePtr = CFDataGetLength(serializedData);
*eventBufferPtr = malloc(*eventBufferSizePtr);
if( *eventBufferPtr )
memcpy(*eventBufferPtr, CFDataGetBytePtr(serializedData), *eventBufferSizePtr);
CFRelease( serializedData );
return res;
}
kern_return_t _io_ups_get_capabilities(
mach_port_t server,
int upsID,
void ** capabilitiesBufferPtr,
IOByteCount * capabilitiesBufferSizePtr)
{
CFSetRef capabilities;
CFMutableDataRef data;
CFDataRef serializedData;
UPSDataRef upsDataRef;
IOReturn res = kIOReturnError;
if (!capabilitiesBufferPtr || !capabilitiesBufferSizePtr ||
!gUPSDataArrayRef || (upsID >= CFArrayGetCount(gUPSDataArrayRef)))
{
return kIOReturnBadArgument;
}
data = (CFMutableDataRef)CFArrayGetValueAtIndex(gUPSDataArrayRef, upsID);
upsDataRef = (UPSDataRef)CFDataGetMutableBytePtr(data);
if (!upsDataRef || !upsDataRef->upsPlugInInterface)
return kIOReturnBadArgument;
res = (*upsDataRef->upsPlugInInterface)->getCapabilities(
upsDataRef->upsPlugInInterface,
&capabilities);
if ((res != kIOReturnSuccess) || !capabilities)
return kIOReturnError;
serializedData = (CFDataRef)IOCFSerialize( capabilities, kNilOptions );
if (!serializedData)
return kIOReturnError;
*capabilitiesBufferSizePtr = CFDataGetLength(serializedData);
*capabilitiesBufferPtr = malloc(*capabilitiesBufferSizePtr);
if( *capabilitiesBufferPtr )
memcpy(*capabilitiesBufferPtr,
CFDataGetBytePtr(serializedData),
*capabilitiesBufferSizePtr);
CFRelease( serializedData );
return res;
}