PDA

View Full Version : Problem with launchctl Command




tiku.thakkar
Mar 3, 2008, 04:12 AM
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
Mar 3, 2008, 08:54 AM
Are you handling the SIGTERM signal? IIRC, you'll want your SIGTERM handler to cause your run loop to exit.

jeremy.king
Mar 3, 2008, 10:20 AM
You will need to post some source to get help.

tiku.thakkar
Mar 3, 2008, 10:58 PM
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

tiku.thakkar
Mar 3, 2008, 11:00 PM
You will need to post some source to get help.

What kind of souce am I supposed to post here?? As I have made very simple process...

iSee
Mar 4, 2008, 08:48 AM
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:

signal(SIGTERM, &sigtermHandler);

And notice how the handler exits the run loop:

// 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):
///////////////////////////////////////////////////////
// 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