Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

tiku.thakkar

macrumors newbie
Original poster
Mar 3, 2008
3
0
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
 

iSee

macrumors 68040
Oct 25, 2004
3,540
272
Are you handling the SIGTERM signal? IIRC, you'll want your SIGTERM handler to cause your run loop to exit.
 

tiku.thakkar

macrumors newbie
Original poster
Mar 3, 2008
3
0
Are you handling the SIGTERM signal? IIRC, you'll want your SIGTERM handler to cause your run loop to exit.

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
 

iSee

macrumors 68040
Oct 25, 2004
3,540
272
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
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.