Problem with launchctl Command

Discussion in 'Mac Programming' started by tiku.thakkar, Mar 3, 2008.

  1. macrumors newbie

    Joined:
    Mar 3, 2008
    #1
    Hello All,

    I have created a sample service on mac called TestService.

    I have used the following commands for Starting and Stoping the service from the terminal.

    To Load the service:
    sudo launchctl load -w /System/Library/LaunchDaemons/TestService.plist

    To Start the service:
    sudo launchctl start TestService

    To Stop the service:
    sudo launchctl stop TestService

    To Unload the service:
    sudo launchctl unload -w /System/Library/LaunchDaemons/TestService.plist

    To List the loaded services:
    sudo launchctl list

    Once I have started the service I am able to see its instance in the Activity Monitor, but even after stopping the service the instance still remains in the Activity Monitor.

    I want this instance to be removed from the Activity Monitor as well as from the memory on stoping the service.

    Please suggest a solution.

    Thanks in advance.
    --Twinkle
     
  2. macrumors 68040

    iSee

    Joined:
    Oct 25, 2004
    #2
    Are you handling the SIGTERM signal? IIRC, you'll want your SIGTERM handler to cause your run loop to exit.
     
  3. macrumors 603

    jeremy.king

    Joined:
    Jul 23, 2002
    Location:
    Fuquay Varina, NC
    #3
    You will need to post some source to get help.
     
  4. thread starter macrumors newbie

    Joined:
    Mar 3, 2008
    #4
    I am a new bee to this MAC related stuff...

    Can you please let me know how to find about what kind of signal am I using ?

    Actually I have made a simple service/process and started working with that.

    Thanks.
    Twinkle
     
  5. thread starter macrumors newbie

    Joined:
    Mar 3, 2008
    #5
    What kind of souce am I supposed to post here?? As I have made very simple process...
     
  6. macrumors 68040

    iSee

    Joined:
    Oct 25, 2004
    #6
    Post your source code. I'm not sure how else to say it.

    Anyway, here's the main file for my own service, which seems to terminate fine. Notice how I register a handler for SIGTERM before the runloop starts:

    Code:
    signal(SIGTERM, &sigtermHandler);
    And notice how the handler exits the run loop:

    Code:
    // This is a callback to handle SIGTERM and SIGINT signals
    void sigtermHandler(int sig)
    {
    	CFRunLoopRef rl = CFRunLoopGetCurrent();
    	if (rl == NULL)
    		exit(1); // something when wrong. Better just exit
    	else
    		CFRunLoopStop(rl);
    }
    Here's the whole thing (This service remaps the enter key to the right mouse button--that is what the eventtap stuff is about):
    Code:
    ///////////////////////////////////////////////////////
    // This program is a daemon that causes the "enter" key
    // (not the return key) on your Mac keyboard to act like
    // a right-mouse button.
    // 
    // It requires Mac OS 10.4 (or later, probably) because is uses an event tap
    // callback to capture and convert the enter key event at a low level,
    // and is designed to run as a lauchd daemon. Event taps and launchd are
    // both new to Mac OS 10.4.
    //
    // The daemon's ability to capture and convert the enter key is not foolproof.
    // The enter key will not act as a right mouse button for anything that 
    // bypasses the event tap system. I've noticed that Parallels, for example,
    // does this. Also, if other event taps are installed, they could have a higher 
    // priority. It looks like event taps installed after this one could request
    // higher priority.
    //
    // NOTE: This program must be run as the root user OR access for assistive 
    // devices must be enabled to this program to work. When launched with launchd 
    // it runs as a root process. However, if you want to run it manually I suggest
    // you turn on access for assistive devices (in the universal access pane of
    // system preferences.)
    //
    // NOTE: the log stuff is essentially #ifdef'ed out when compiled in release mode.
    // Under debug mode these just resolve to printf statements.
    
    #include <CoreServices/CoreServices.h>
    #include <ApplicationServices/ApplicationServices.h>
    #include <signal.h>
    #include <launch.h>
    
    #include "log.h"
    
    #define SAFE_RELEASE(ref) if ((ref) != NULL) { CFRelease(ref); (ref) = NULL; }
    
    static 	CFStringRef myRunLoopMode = CFSTR("myRunLoopMode");
    
    CGEventRef myEventTapCallback(
       CGEventTapProxy proxy, 
       CGEventType type, 
       CGEventRef event, 
       void *refcon)
    {
    	if (type == kCGEventKeyDown || type == kCGEventKeyUp)
    	{
    		CGKeyCode keycode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
    
    		#ifdef _DEBUG
    		{
    			CFStringRef msg = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("code: %d, type: %c, auto: %d"), (int)keycode, (type == kCGEventKeyDown) ? 'D' : 'U', (int)(CGEventGetIntegerValueField(event, kCGKeyboardEventAutorepeat) != 0));
    			LOGLINE_CF(msg);
    			SAFE_RELEASE(msg);
    		}
    		#endif
    		
    		if (keycode == 76 /* the "enter" key--note: not the return key */)
    		{
    			bool isAutoRepeat = (CGEventGetIntegerValueField(event, kCGKeyboardEventAutorepeat) != 0);
    			if (isAutoRepeat)
    				return NULL;
    			
    			// change it to a right mouse button event
    			CGEventSetType(event, (type == kCGEventKeyDown) ? kCGEventRightMouseDown : kCGEventRightMouseUp);
    		}
    	}
    	
    	return event;
    }
    
    // This is a callback to handle SIGTERM and SIGINT signals
    void sigtermHandler(int sig)
    {
    	CFRunLoopRef rl = CFRunLoopGetCurrent();
    	if (rl == NULL)
    		exit(1); // something when wrong. Better just exit
    	else
    		CFRunLoopStop(rl);
    }
    
    int main (int argc, const char * argv[]) 
    {
    	// Check-in with launchd, as recommended.
    	// I couldn't find any documentation on this other than the SampleD code.
    	// I am following the SampleD model. As far as I can tell, this is what is 
    	// meant by checking in. NOTE: In the sample code I've seen, the call to
    	// launch_data_new_string() is not paired with some kind of 
    	// a free/dealloc/delete call. 
    	{
    		launch_data_t checkin_request = launch_data_new_string(LAUNCH_KEY_CHECKIN);
    		if (checkin_request == NULL)
    			exit(1); // need to add some logging or something here
    		else
    		{
    			launch_data_t checkin_response = launch_msg(checkin_request);
    			if (checkin_response == NULL)
    				exit(1); // need to add some logging or something here
    		}
    	}
    	
    	///////////////////////////////////////////////////
    
    	// Setup the event tap to convert enter key presses to right mouse button presses
    
    	CFRunLoopRef rl = NULL;			// My run loop
    	CFMachPortRef mp = NULL;		// The event tap
    	CFRunLoopSourceRef rls = NULL;	// The event source for event tap
    	
    	// get the runloop
    	rl = CFRunLoopGetCurrent();
    	if (rl != NULL)
    		CFRetain(rl);
    	
    	// create the event tap to watch key up/down events. myEventTapCallback is
    	// called when these events occur
    	if (rl != NULL)
    	{
    		mp = CGEventTapCreate(
    			kCGHIDEventTap,
    			kCGHeadInsertEventTap,
    			0x00000000 /* kCGEventTapOptionDefault */,
    			CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventKeyUp),
    			myEventTapCallback,
    			NULL);
    	}
    	
    	// create the source for the event tap
    	if (mp != NULL)
    	{
    		rls = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, mp, 0);
    	}
    	
    	// add the source to the run loop
    	if (rls != NULL)
    	{
    		CFRunLoopAddSource(rl, rls, myRunLoopMode);
    	}
    	
    	/////////////////////////////////////////////////////
    
    	// Now start the runloop
    	
    	if (rls != NULL)
    	{
    		LOGLINE_C("starting...");
    		
    		// as recommended for launchd daemons, we handle SEGTERM signals.
    		// sigtermHandler just tells the runloop to stop. Everything seemed to
    		// work fine even when I didn't handle SIGTERM, but this makes me
    		// feel better because my cleanup code gets called.
    		signal(SIGTERM, &sigtermHandler);
    		
    		SInt32 result = CFRunLoopRunInMode(myRunLoopMode, 3153600000.0, false); // run for ~100 years (60*60*24*365*100 seconds)
    		
    		#ifdef _DEBUG
    			char buf[1024];
    			sprintf(buf, "done! result: %i", result);
    			LOGLINE_C(buf);
    		#else
    			result; // makes warnign go away
    		#endif
    	}
    	
    	/////////////////////////////////////////////////////
    	
    	// cleanup
    	if (rls != NULL)
    		CFRunLoopRemoveSource(rl, rls, kCFRunLoopCommonModes); // remove the source from the runloop
    	SAFE_RELEASE(rls);
    	SAFE_RELEASE(mp);
    	SAFE_RELEASE(rl);
    	
    	return 0;
    }
    
    You can download the whole project here, if you want: http://www.jjmn.net/dl/EnterKeyRemapperD.zip
     

Share This Page