Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Execute command just before Mac going to sleep

Tags:

c

sleep

macos

I wrote a C program/LaunchDaemon that checks if my MacBook is at home (connected to my WLAN). If so, it disables my password protection; if not, it enables it.

Easy. But the problem is that when I take my MacBook anywhere else and password protection is disabled, it will wake up without a password protection.

My fix for this would be: enable the password protection every time just before it goes to sleep.

QUESTION: is there any way find out when my Mac is preparing for sleep? Some interupt I can let my program listen to?

like image 482
Vincent Avatar asked Nov 23 '12 18:11

Vincent


People also ask

How do I put the Mac to sleep from Command line?

Helpful answers. Option–Command–Power button* or Option–Command–Media Eject: Put your Mac to sleep. Option–Command–Power button* or Option–Command–Media Eject: Put your Mac to sleep. You can tell Siri 'go to sleep'.

Do processes still run in sleep mode Mac?

Sleep mode stops system processes on your Mac without needing to shut it down. When you close your MacBook, it automatically goes to sleep. Also, you can manually enable the sleep mode any time you need it. In this mode, all your unsaved documents, data, and running processes are stored in the RAM.


2 Answers

I attach below the contents of my C file beforesleep.c which executes some command line commands (in my case shell commands and AppleScript scripts) when a "will sleep" notification is received.

Where you can put your code:

In order to run your code when the mac is going to sleep, just replace the system(...) calls with the code you wish to run.

In my case, I use system() as it allows me to run shell commands passed as strings, but if you prefer to run just C code instead, you can just put your C code there.

How to build it

In order to build this file, I run:

gcc -framework IOKit -framework Cocoa beforesleep.c

Remark

If you are going to use this code, make sure it is always running in background. For example, I have a Cron job which makes sure that this code is always running, and it launches it again in case it is accidentally killed for any reason (although it never happened to me so far). If you are experienced enough, you can find smarter ways to ensure this.

Further info

See this link (already suggested by sidyll) for more details about how this works.

Code template

#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
 
#include <mach/mach_port.h>
#include <mach/mach_interface.h>
#include <mach/mach_init.h>
 
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <IOKit/IOMessage.h>
 
io_connect_t  root_port; // a reference to the Root Power Domain IOService
 
void
MySleepCallBack( void * refCon, io_service_t service, natural_t messageType, void * messageArgument )
{
    switch ( messageType )
    {
 
        
        case kIOMessageCanSystemSleep:
            IOAllowPowerChange( root_port, (long)messageArgument );
            break;
 
        case kIOMessageSystemWillSleep:
            system("/Users/andrea/bin/mylogger.sh");
            system("osascript /Users/andrea/bin/pause_clockwork.scpt");
 
            IOAllowPowerChange( root_port, (long)messageArgument );
            break;
 
        case kIOMessageSystemWillPowerOn:
            //System has started the wake up process...
            break;
 
        case kIOMessageSystemHasPoweredOn:
            //System has finished waking up...
        break;
 
        default:
            break;
 
    }
}
 
 
int main( int argc, char **argv )
{
    
    // notification port allocated by IORegisterForSystemPower
    IONotificationPortRef  notifyPortRef;
 
    // notifier object, used to deregister later
    io_object_t            notifierObject;
   // this parameter is passed to the callback
    void*                  refCon;
 
    // register to receive system sleep notifications
 
    root_port = IORegisterForSystemPower( refCon, &notifyPortRef, MySleepCallBack, &notifierObject );
    if ( root_port == 0 )
    {
        printf("IORegisterForSystemPower failed\n");
        return 1;
    }
 
    // add the notification port to the application runloop
    CFRunLoopAddSource( CFRunLoopGetCurrent(),
            IONotificationPortGetRunLoopSource(notifyPortRef), kCFRunLoopCommonModes );
 
    /* Start the run loop to receive sleep notifications. Don't call CFRunLoopRun if this code
        is running on the main thread of a Cocoa or Carbon application. Cocoa and Carbon
        manage the main thread's run loop for you as part of their event handling
        mechanisms.
    */
    CFRunLoopRun();
 
    //Not reached, CFRunLoopRun doesn't return in this case.
    return (0);
}
like image 64
Kubuntuer82 Avatar answered Oct 15 '22 17:10

Kubuntuer82


You can do it using I/O Kit, check Apple's QA1340: Registering and unregistering for sleep and wake notifications. You may also want to analyze the SleepWatcher utility sources or use/integrate for your needs. From the homepage:

SleepWatcher 2.2 (running with Mac OS X 10.5 to 10.8, source code included) is a command line tool (daemon) for Mac OS X that monitors sleep, wakeup and idleness of a Mac. It can be used to execute a Unix command when the Mac or the display of the Mac goes to sleep mode or wakes up, after a given time without user interaction or when the user resumes activity after a break or when the power supply of a Mac notebook is attached or detached. It also can send the Mac to sleep mode or retrieve the time since last user activity. A little bit knowledge of the Unix command line is required to benefit from this software.

like image 40
sidyll Avatar answered Oct 15 '22 15:10

sidyll