Create a MAC Service then Daemonize it

I am at the point where I need to make an existing desktop application a service and enable this application to run at computer startup without the need of a user to log in and run it. This service has the need to always be running in the back ground performing various network/db checks and responses.

At this point I have successfully removed all GUI aspects and have an event driven console app that works. Now onto the next step making a service app that will run on MAC OSX Server 10.7.5 and daemonize so it runs at startup

I have used the service app template to start with but am looking for more info about what I should pay attention to from a Xojo perspective as I work to complete this service.

Beyond the bigger picture I am looking to find out simple things like:

  1. How do you get this to run code in the debugger? I have code in the run statement but when I select run in the IDE the code never gets to the first break point
  2. Can you do this remotely?
  3. How can arguments be passed at startup
  4. How can you have another program check for existence of this service and then start the service in case it is not running?

I have looked through the help archives, language learning and RBLibrary seeking help docs on how to accomplish this and came up fairly empty. I have checked the supplied examples. I could have missed it but I did not find such information there either.

Any suggestions and/or documentation recommendations on how to create Xojo service apps on MAC as well as daemon steps would be appreciated

Thanks

Create a console app - start it using launchd
It doesn’t have to be a service app

I have used launched on both Console and Event Driven Console apps and the response is '‘launched: This program is not meant to be run directly.’

Both the Console and Event Driven Console apps were created using the Xojo Templates

Both apps start when double clicked and open in Terminal

LaunchCtl link

Carl have you looked at LaunchCtl to interface with Launchd?

[quote=50225:@Carl Fitzsimmons]I have used launched on both Console and Event Driven Console apps and the response is '‘launched: This program is not meant to be run directly.’

Both the Console and Event Driven Console apps were created using the Xojo Templates

Both apps start when double clicked and open in Terminal[/quote]

Right
For launchd you write a little script that tells the OS how & when to start your app (at boot etc) and the OS will take care of doing whatever it is you asked launchd to do
You don’t run launched directly

You write a plist that tells to OS what to do
https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man5/launchd.plist.5.html

There’s some examples on here
http://apple.stackexchange.com/questions/820/can-anyone-recommend-a-good-tutorial-for-writing-from-scratch-a-cron-style-lau

Thanks for the information. I will take a look and give it a go

Interesting. I downloaded Lingon. Setting up a Daemon or Agent seems straight forward
So I placed the entire folder that was output from Xojo build process of the Event Driven Console App into Applications folder

  1. Set permissions to root xrw as well as owner, group, everyone else
  2. Created a User Daemon for this Event Driven Console App
  3. Rebooted the machine
  4. The Event Driven Console app fails to start

I rebooted several times and then looked at the System Diagnostic Reports. The failure is at the same place which is a location in the app that writes to system log.

When I double click on the Event Driven Console App after I have logged in the app works fine

The specific sub in my app is writing to the error log. This app needs to go out on the network and perform a bunch of reads/writes to/from a db as well as other networked devices on startup. So it seems that this app is starting up at the wrong time of the boot up sequence of the machine

If that seems reasonable how do I delay the start until a certain time later or monitor an environment value such as something that would indicate the machine is up and ready to go?

What does the diagnostic report look like ?
That should be instructive

Here is the first part

[code]Process: iSecureEDC [115]
Path: /Applications/iSecureEDC/iSecureEDC
Identifier: iSecureEDC
Version: ??? (???)
Code Type: X86 (Native)
Parent Process: launchd [1]

Date/Time: 2013-12-02 11:52:05.806 -0500
OS Version: Mac OS X Server 10.7.5 (11G63)
Report Version: 9

Crashed Thread: 0 Dispatch queue: com.apple.main-thread

Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_PROTECTION_FAILURE at 0x00000000bf7ffffc

VM Regions Near 0xbf7ffffc:
Stack 00000000b0083000-00000000b0104000 [ 516K] rw-/rwx SM=COW
–> Stack 00000000bc000000-00000000bf800000 [ 56.0M] —/rwx SM=NUL
Stack 00000000bf800000-00000000c0000000 [ 8192K] rw-/rwx SM=COW

Application Specific Information:
objc[115]: garbage collection is OFF

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libsystem_c.dylib 0x9769ba43 sprintf + 8
1 rbframework.dylib 0x0040a533 0x381000 + 562483
2 rbframework.dylib 0x0042ebf5 0x381000 + 711669
3 rbframework.dylib 0x003d4fdf 0x381000 + 344031
4 rbframework.dylib 0x003d502f 0x381000 + 344111
5 rbframework.dylib 0x003d4c12 0x381000 + 343058
6 rbframework.dylib 0x003d4b80 0x381000 + 342912
7 rbframework.dylib 0x003d4b1b RuntimeRaiseException + 91
8 rbframework.dylib 0x003ba1fa 0x381000 + 233978
9 rbframework.dylib 0x003a2bed RuntimeStackCheck + 77
10 iSecureEDC 0x0023ddb5 Send_Messages.write_Log_Error%i4%i4ssi4 + 1538
11 iSecureEDC 0x00243e4d Send_Messages.write_Log_Error%i4%i4ssi4 + 26266
12 iSecureEDC 0x00243e4d Send_Messages.write_Log_Error%i4%i4ssi4 + 26266
13 iSecureEDC 0x00243e4d Send_Messages.write_Log_Error%i4%i4ssi4 + 26266
14 iSecureEDC 0x00243e4d Send_Messages.write_Log_Error%i4%i4ssi4 + 26266
15 iSecureEDC 0x00243e4d Send_Messages.write_Log_Error%i4%i4ssi4 + 26266
.
.
.
510 iSecureEDC 0x00242401 Send_Messages.write_Log_Error%i4%i4ssi4 + 19534
511 iSecureEDC 0x00242401 Send_Messages.write_Log_Error%i4%i4ssi4 + 19534
[/code]

In between is basically the same as lines 10 - 15 with the exception of the trailing number my increment further down

Here is the closing part

Thread 1:: Dispatch queue: com.apple.libdispatch-manager
0   libsystem_kernel.dylib        	0x968a690a kevent + 10
1   libdispatch.dylib             	0x9217fe04 _dispatch_mgr_invoke + 969
2   libdispatch.dylib             	0x9217e853 _dispatch_mgr_thread + 53

Thread 2:
0   libsystem_kernel.dylib        	0x968a602e __workq_kernreturn + 10
1   libsystem_c.dylib             	0x97668ccf _pthread_wqthread + 773
2   libsystem_c.dylib             	0x9766a6fe start_wqthread + 30

Thread 0 crashed with X86 Thread State (32-bit):
  eax: 0x0052aae0  ebx: 0xbf800048  ecx: 0x00000000  edx: 0x00000000
  edi: 0x0040a4f1  esi: 0xac90ea10  ebp: 0xbf800028  esp: 0xbf800000
   ss: 0x00000023  efl: 0x00010286  eip: 0x9769ba43   cs: 0x0000001b
   ds: 0x00000023   es: 0x00000023   fs: 0x00000000   gs: 0x0000000f
  cr2: 0xbf7ffffc
Logical CPU: 0

What does Send_Messages.write_Log_Error call ?

Send_message checks directories for existence, creates new directories if needed, then checks for log file, creates if needed then writes to the log file, then leaves to calling sub

Sounds like that’s the place to investigate
Is the file created ?
It may be the process has no privileges to write where you are asking it to

The file on startup will be new. The app has permissions to write set to root.

So I am not sure what to look for and since this fails on startup there is no way to debug which is why I am writing to sys log. Most importantly this is the error print routine that is called meaning the network request to the DB has failed.

The app works once the machine is running from the same location the plist file is pointed to

looks to me like a recursive call that’s causing a stack overflow:

9   rbframework.dylib             	0x003a2bed RuntimeStackCheck + 77
10  iSecureEDC                    	0x0023ddb5 Send_Messages.write_Log_Error%i4%i4ssi4 + 1538
11  iSecureEDC                    	0x00243e4d Send_Messages.write_Log_Error%i4%i4ssi4 + 26266
12  iSecureEDC                    	0x00243e4d Send_Messages.write_Log_Error%i4%i4ssi4 + 26266
13  iSecureEDC                    	0x00243e4d Send_Messages.write_Log_Error%i4%i4ssi4 + 26266

Is there any way that write_Log_Error can call itself? E.g. if there’s an error in the error logging routine?

Yes. Rather interesting. I had implemented a Exception err handler for various exceptions at the end of the routine. So when it failed it called itself.

Now to find out why it failed. The only thing this performs is checking directories, creates a directory when it does not exist, checks for a log file and creates one if it does not exist then go back to the calling sub

The big question is that this works fine when the machine is up and running. Make she think of network/File IO stuff

I’d do two things to your write_Log_Error() method

  • make sure it is bulletproof - it should trap all reasonable exceptions and have graceful failure modes
  • log messages liberally using System.DebugLog() - I would also add a “verbose” debugging mode to your app such as this:
#if VerboseDebug
  system.DebugLog("just entered method")
#endif
...
#if VerboseDebug
  system.DebugLog("about to try to make the folder")
#endif
...

#if VerboseDebug
  system.DebugLog("reached the end of the method, about to exit")
#endif

Just a note: Perhaps it would be better to use a variable which can be changed at runtime to get better logging. Then you may set a specific environment variable (or command ling argument like “-v”, “-vv”) to trigger the output.

if verboseLevel=1 then
    system.DebugLog("just entered method")
end
if verboseLevel=2 then
    system.DebugLog("just wrote the first log ;-)")
end

The benefit of this is that you may able to tell your customer how to enable logging on his machine where something does not work.

Cheers,

Tom