#include <notify.h>
#include <utmpx.h>
#include <sys/stat.h>
#include <err.h>
#include <asl.h>
#include "PrivateLib.h"
#include "TTYKeepAwake.h"
#include "PMSettings.h"
#if !TARGET_OS_EMBEDDED
#ifndef DEVMAXPATHSIZE
#define DEVMAXPATHSIZE 128
#endif
typedef char dev_path_t[DEVMAXPATHSIZE];
typedef struct tl {
struct tl *next;
dev_path_t ttydev;
} ttylist;
static CFRunLoopSourceRef s_source = NULL;
static CFMachPortRef s_cfport = NULL;
static ttylist *s_activettys = NULL;
static int s_token = -1;
static uint32_t settingIdleSleepSeconds = 0;
static bool settingTTYSPreventSleep = true;
static ttylist *addtty(char *ttyname);
static void freettys(void);
static bool check_ttys_active( CFArrayRef *preventers );
static void read_logins(CFMachPortRef port, void *m, CFIndex size, void *info);
static void cleanup_tty_tracking(void);
void TTYKeepAwake_prime(void)
{
CFRunLoopRef rl;
mach_port_t mport;
int rval = -1;
int macherr;
rl = CFRunLoopGetCurrent();
if(!rl) goto finish;
s_cfport = CFMachPortCreate(nil, read_logins, NULL, false);
if (!s_cfport) goto finish; s_source = CFMachPortCreateRunLoopSource(nil, s_cfport, 0);
if (!s_source) goto finish;
CFRunLoopAddSource(rl, s_source, kCFRunLoopDefaultMode);
mport = CFMachPortGetPort(s_cfport);
if (mport == MACH_PORT_NULL) goto finish;
macherr = notify_register_mach_port(UTMPX_CHANGE_NOTIFICATION, &mport,
NOTIFY_REUSE, &s_token);
if (macherr) {
goto finish;
}
read_logins(NULL, NULL, 0, NULL);
TTYKeepAwakePrefsHaveChanged();
rval = 0;
finish:
if (rval) {
cleanup_tty_tracking(); }
return;
}
void TTYKeepAwakePrefsHaveChanged( void )
{
CFDictionaryRef activePMSettings;
CFNumberRef systemIdleNum;
CFNumberRef ttysPreventNum;
activePMSettings = PMSettings_CopyActivePMSettings();
if( activePMSettings )
{
systemIdleNum = isA_CFNumber(CFDictionaryGetValue(
activePMSettings,
CFSTR(kIOPMSystemSleepKey)));
if( systemIdleNum ) {
CFNumberGetValue(systemIdleNum, kCFNumberIntType, &settingIdleSleepSeconds);
settingIdleSleepSeconds *= 60;
}
ttysPreventNum = isA_CFNumber(CFDictionaryGetValue(
activePMSettings,
CFSTR(kIOPMTTYSPreventSleepKey)));
if( ttysPreventNum ) {
CFNumberGetValue(ttysPreventNum, kCFNumberIntType, &settingTTYSPreventSleep);
}
CFRelease( activePMSettings );
}
}
bool TTYKeepAwakeSleepWakeNotification ( natural_t messageType )
{
CFArrayRef preventers = NULL;
CFStringRef preventer_name;
if( (0 == settingIdleSleepSeconds)
|| !settingTTYSPreventSleep )
{
return false;
}
if( kIOMessageCanSystemSleep == messageType )
{
bool ssh_sessions_active = check_ttys_active( &preventers );
if(preventers)
{
asl_log(NULL, NULL, ASL_LEVEL_ERR,
"PowerManagement configd: System Sleep prevented by active remote login session (%d second threshold).\n", settingIdleSleepSeconds);
int i, count;
count = CFArrayGetCount(preventers);
for(i=0; i<count; i++) {
if( (preventer_name =
isA_CFString(CFArrayGetValueAtIndex(preventers, i))) )
{
asl_log(NULL, NULL, ASL_LEVEL_ERR,
"PowerManagement configd: tty sleep preventer: %s\n",
CFStringGetCStringPtr( preventer_name,
kCFStringEncodingMacRoman));
}
}
CFRelease( preventers );
}
if ( ssh_sessions_active )
{
return true;
}
}
return false;
}
static ttylist *addtty(char *ttyname)
{
ttylist *new = malloc(sizeof(ttylist));
if (new) {
if (strlcpy(new->ttydev, "/dev/", DEVMAXPATHSIZE) > DEVMAXPATHSIZE ||
strlcat(new->ttydev, ttyname, DEVMAXPATHSIZE) > DEVMAXPATHSIZE)
return NULL;
new->next = s_activettys;
s_activettys = new;
}
return new;
}
static void freettys()
{
ttylist *next, *delp = s_activettys;
while(delp) {
next = delp->next;
free(delp);
delp = next;
}
s_activettys = NULL; }
bool check_ttys_active( CFArrayRef *preventers )
{
CFMutableArrayRef preventArray = NULL;
CFStringRef preventingTTY = NULL;
bool rval = false;
ttylist *tty = s_activettys;
struct stat sb;
time_t curtime;
if( preventers ) {
*preventers = NULL;
}
if ((curtime = time(NULL)) == (time_t)-1) {
goto finish;
}
while(tty)
{
if (stat(tty->ttydev, &sb) == 0)
{
time_t current_idle_secs;
current_idle_secs = curtime - sb.st_atime;
if (current_idle_secs < settingIdleSleepSeconds)
{
rval = true;
preventingTTY = CFStringCreateWithCString( kCFStringEncodingMacRoman,
tty->ttydev, DEVMAXPATHSIZE);
if( preventingTTY )
{
if( !preventArray ) {
preventArray = CFArrayCreateMutable( 0, 0, &kCFTypeArrayCallBacks );
}
if( preventArray ) {
CFArrayAppendValue( preventArray, preventingTTY );
}
CFRelease(preventingTTY);
preventingTTY = NULL;
}
}
}
tty = tty->next;
}
finish:
if( preventArray ) {
if( preventers ) {
*preventers = preventArray;
} else {
CFRelease(preventArray);
}
}
return rval;
}
static void read_logins(
CFMachPortRef port __unused,
void *m __unused,
CFIndex size __unused,
void *info __unused)
{
int result = 1;
struct utmpx *ent;
freettys();
setutxent(); while((ent = getutxent()))
{
if ( ent->ut_type == USER_PROCESS
|| ent->ut_type == LOGIN_PROCESS) {
addtty(ent->ut_line);
}
}
result = 0;
endutxent();
}
static void cleanup_tty_tracking()
{
CFRunLoopRef rl = CFRunLoopGetCurrent();
if(!rl) return;
if (s_token != -1) {
notify_cancel(s_token);
s_token = -1;
}
if (s_source) {
CFRunLoopRemoveSource(rl, s_source, kCFRunLoopDefaultMode);
CFRelease(s_source);
s_source = NULL;
}
if (s_cfport) {
CFMachPortInvalidate(s_cfport); CFRelease(s_cfport); s_cfport = NULL;
}
freettys(); }
#endif