PDA

View Full Version : Daemon App




sivaprakash
Apr 29, 2011, 04:34 AM
Hi,

Can some one give me step-by-step reference to write and launch simple (Hello World) kind of Dameon with Launchd?


Thanks
Siva



KnightWRX
Apr 29, 2011, 05:51 AM
What language are you targetting for writing your daemon ? Here's a good C reference for the BSD socket API :

http://www.few.vu.nl/~jms/socket-info.html

There's info on there about how to write a server process (daemon) using listen() and accept(). I'd suggest also looking into fork(), as you'll need some kind of parallelism (and multi-process with fork() is easier than pthreads).

Here's the recipe for launchd :

http://homepage.mac.com/kelleherk/iblog/C1901548470/E20061106114930/index.html

Frankly, this all took me about 1 minute to find on Google. Did you even try to search ?

sivaprakash
Apr 29, 2011, 06:39 AM
:-) Thanks !!

By the way, I got many articles but they were little high end I just wanted to have something to the ground level so that I can understand well, more over I am just 2 month old Mac Programmer. Sorry about it !!

sivaprakash
May 2, 2011, 02:31 AM
Hey

I just want to start a Server program (which is written in Objective-C). To achieve this

- I have to write script (Apple Script or Perl Script) to start the Server Program, where should I put this file?
- Configure plist and put it under /LaunchDaemons folder because it has to work for all users

What I am doing is correct? For some reason it is not starting up?

Thanks
Siva

jiminaus
May 2, 2011, 03:10 AM
You can put the startup script where ever you like. Or more specifically, it needs to be where ever your launchd .plist file refers to it.

You're putting the .plist in /Library/LaunchDaemons right?

Have you loaded the .plist using launchctl using something like:

sudo launchctl load -w /Library/LaunchDaemons/com.company.product.plist


Additionally (quoted from the launchctl man page):

All system-wide daemons (LaunchDaemons) must be owned by root. Configuration files must not be group- or world-writable.

Here configuration files means launchd .plist files.

Also I don't know if you can execute an apple script or perl script directly from a launchd .plist even with a she-bang line in the script. You might have to directly executed the relevant interpreter and pass the script as an argument to the interpreter. For example, for an AppleScript, you might need to setup the ProgramArguments array like so:


<key>ProgramArguments</key>
<array>
<string>/usr/bin/osascript</string>
<string>/absolute/path/to/server/script/serverscript</string>
<string>scriptarg1</string>
<string>scriptarg2</string>
<string>etc</string>
</array>



EDIT: On other thing you could do (if you haven't already) is add StandardOutPath and StandardErrorPath keys with string values being absolute paths to files. Then check those files for error messages.

Eg.

<key>StandardOutPath</key>
<string>/var/tmp/myserver.out</string>
<key>StandardErrorPath</key>
<string>/var/tmp/myserver.err</string>

subsonix
May 2, 2011, 04:46 AM
I think the recommended practice is to run "per user" or "per session" when ever possible, rather than machine wide, simply because you wan't to avoid running as root.

sivaprakash
May 2, 2011, 05:29 AM
Hi


Here is what I did, my perl file saved under /usr/local/siva/test.pl


$str ="Test Daemon !....";
open FILE, ">file.txt" or die $!;
print FILE $str;
close FILE;


and kept my .plist file under "/Library/LaunchDaemons"



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>local.service.test</string>
<key>UserName</key>
<string>root</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/siva/test.pl</string>
</array>
</dict>
</plist>


Then I did

$ sudo launchctl load /Library/LaunchDaemons/test.plist

Any stps I am missing ? Do I need to any permissions or something else ? Because in one of the tutorial I read "Remember to make it executable (PERL file); probably best to change the owner/group to root/wheel and set rwxr--r-- permissions." but I dont understand what it is?

subsonix
May 2, 2011, 06:05 AM
Hi


Here is what I did, my perl file saved under /usr/local/siva/test.pl


$str ="Test Daemon !....";
open FILE, ">file.txt" or die $!;
print FILE $str;
close FILE;


and kept my .plist file under "/Library/LaunchDaemons"



You don't need to run this in /Library/LaunchDaemons, so you shouldn't.

From Apple technical note tn2083:

If you've decided to implement a background program, you must then determine whether you need an agent or a daemon. The main reason for using a daemon is that you need to share some state between multiple processes in different login sessions. If you don't need this, consider using an agent.

So, put you plist file in ~/Library/LaunchAgent and remove username, root

Can you execute your perl script directly? You need to make it executable with chmod.

jiminaus
May 2, 2011, 06:29 AM
While the advice about agents verses daemons is generally good, this is clearly a test setup being done by the OP. If you check his other threads, his ultimate goal server-wise does fit the bill of daemon, it won't work as an agent.

However, the advise of not running as root is valid. You should follow the model of likes of Apache and MySQL. Create a special-purpose, non-root user for the purposes of running your sever. Have all the files owned by this special-purpose user.

In regards to launchd troubles, the files below do work.

/var/tmp/a/test.plist

$str ="Test Daemon !....";
open FILE, ">file.txt" or die $!;
print FILE $str;
close FILE;
exit -1; # Don't normally do this, see below.


/Library/LaunchDaemons/local.service.test.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>local.service.test</string>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<true/>
</dict>
<key>RunAtLoad</key>
<true/>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/perl</string>
<string>/var/tmp/a/test.pl</string>
</array>
<key>StandardErrorPath</key>
<string>/var/tmp/a/test.err</string>
<key>StandardOutPath</key>
<string>/var/tmp/a/test.out</string>
<key>WorkingDirectory</key>
<string>/var/tmp/a</string>
</dict>
</plist>


A word first about the exit -1. The exit -1 in the perl script is only for this test. You would normally not do this. The script launched by launchd is meant to keep running while your server is running. If the script exits, it means the server exited and needs to be relaunched. But this test script exits very quickly. The exit -1 causes an unsuccessful exit, and stops launchd from relaunching the script over and over rapidly.

The primary reason your script failed to run was because you'd coded an on-demand launchd plist file. But you never specified under what conditions the test script should be run, so it never did. You need to include the KeepAlive key like above. This particular KeepAlive setup also implies RunAtLoad, so launchd will immediately run the test script.

Even if you'd got that right, the running of the script would have failed. At no point did you setup perl to run the script. A .pl extension is not enough. You either need a she-bang line at the top of the script, or to explicitly invoke perl like I've done in the .plist file.

Lastly you use a relative path for file.txt in your test script, but what do you expect the working directory to be? You've got to set the in the .plist file.


EDIT: I seems you have very little Unix experience. Writing a server under Mac OS X without Unix server administration experience is going to be bad. I'd suggest you learn how to install, setup and administer a Unix server package such as Apache or MySQL. In doing so you will learn how a Unix server is meant to behave, and give you a model for creating your own server.

sivaprakash
May 2, 2011, 08:33 AM
Hi,

Step 1:-

Here is my PLIST under System/Library/LaunchDaemons/



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>local.service.test</string>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<true/>
</dict>
<key>RunAtLoad</key>
<true/>
<key>ProgramArguments</key>
<array>
<string>/perl</string>
<string>/users/siva/Test.pl</string>
</array>
<key>StandardErrorPath</key>
<string>/users/siva/test.err</string>
<key>StandardOutPath</key>
<string>/users/siva/test.out</string>
<key>WorkingDirectory</key>
<string>/users/siva</string>
</dict>
</plist>





Step 2:-

Perl File under Users/Siva/



$str ="Test Daemon !....";
open FILE, ">file.txt" or die $!;
print FILE $str;
close FILE;
exit -1; # Don't normally do this, see below.




Step 3:-

sudo launchctl load -w /Library/LaunchDaemons/local.service.test.plist

Step 4:-

I ran

10:/ siva$ sudo launchctl list | grep test

and got

- 2 local.service.test

This is exactly I did. But I couldn't see the out put. Can you insert some steps if I have missed or anything like permissions?

chown33
May 2, 2011, 10:39 AM
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>local.service.test</string>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<true/>
</dict>
<key>RunAtLoad</key>
<true/>
<key>ProgramArguments</key>
<array>
<string>/perl</string>
<string>/users/siva/Test.pl</string>
</array>
<key>StandardErrorPath</key>
<string>/users/siva/test.err</string>
<key>StandardOutPath</key>
<string>/users/siva/test.out</string>
<key>WorkingDirectory</key>
<string>/users/siva</string>
</dict>
</plist>


The red-hilited pathname looks wrong to me. It doesn't match jiminous's example, either.

AFAIK, there is no "/perl" executable. There may be a "/usr/bin/perl" executable, as jiminaus showed. Find out using this command in Terminal:
which perl

Or just follow the given example exactly. Or if you have your own custom-installed version of perl, then use its exact pathname.

And the pathnames "/users/siva" should use the correct case to match the actual directory names. It's a non-issue on a case-insensitive file-system, but you might not always have that. It's better to be exact.

jiminaus
May 2, 2011, 05:34 PM
In addition to the possible problem of the path to perl that chown33 pointed out, there's also 2 other potential issues I can see.

The line about your .plist file says /System/Library/LaunchDaemons (a no-no), but you're launchctl comamnd refers to /Library/LaunchDaemons. Make sure you have no versions of your .plist file in /System/Library/LaunchDaemons, and that the latest version is in /Library/LaunchDaemons.

To check the ownership and permissions of the .plist (which is critical), what's the output of this command? (copy and paste the output into code tags, don't paraphrase)

ls -al /Library/LaunchDaemons/


You need to unload the .plist file, before you reload a newer version.

sudo launchctl unload -w /Library/LaunchDaemons/local.service.test.plist

I wouldn't expect that to solve your problem, if you have a problem in regards to ownership and permissions, which I suspect you might have.

BTW You probably don't know about the online manual pages. There's a command called man which will display online manual pages. For example, you can read the manual page for launchctl with the command:

man launchctl


You can get a list of all the launchd .plist keys and their documentation with the command:

man launchd.plist


You can get the manual page for the man command itself with:

man man


Lastly the commands for change permissions and ownership are chmod and chown respectively, you might want to read those manual pages.

subsonix
May 2, 2011, 08:29 PM
While the advice about agents verses daemons is generally good, this is clearly a test setup being done by the OP. If you check his other threads, his ultimate goal server-wise does fit the bill of daemon, it won't work as an agent.


How about creating a simple launchd job first? Get Lingon from the App store read this: http://www.macworld.com/article/48705/2006/01/februarygeekfactor.html

The guy is asking for a "Hello world" example.


Create a special-purpose, non-root user for the purposes of running your sever. Have all the files owned by this special-purpose user.

Which means it can be run in a "per user" context, which is what I suggested.
There is an interesting talk about launchd by Dave Zarzycki from Apple who wrote launchd at Google tech talk: http://www.youtube.com/watch?v=mLwn_TbBntI

jiminaus
May 2, 2011, 09:11 PM
How about creating a simple launchd job first? Get Lingon from the App store read this: http://www.macworld.com/article/48705/2006/01/februarygeekfactor.html

The guy is asking for a "Hello world" example.


Fair enough. But a daemon and an agent are setup the same way, just in different directories.


Which means it can be run in a "per user" context, which is what I suggested.
There is an interesting talk about launchd by Dave Zarzycki from Apple who wrote launchd at Google tech talk: http://www.youtube.com/watch?v=mLwn_TbBntI

Except an agent is only started with a user is logged in, if I'm not mistaken. The scheduled tasks part of the OP's server plans won't run if a user is not logged in.

subsonix
May 2, 2011, 09:53 PM
Except an agent is only started with a user is logged in, if I'm not mistaken. The scheduled tasks part of the OP's server plans won't run if a user is not logged in.

That is the difference between "per session" and "per user" context, "per session" is placed in: ~/Library/LaunchAgents

"per user" in: /Library/LaunchAgents, it will last over login and logout.

jiminaus
May 2, 2011, 10:17 PM
That is the difference between "per session" and "per user" context, "per session" is placed in: ~/Library/LaunchAgents

"per user" in: /Library/LaunchAgents, it will last over login and logout.

Are you able to test this to be the case? I'm in foreign PC territory right now (I'm at work). My understanding is that a session doesn't start until a user login in. In the context of fast user switching, there can be multiple sessions. Am I wrong?

Also I was thinking the server would need to run as the server user regardless of who is actually logged into the Mac. For that the OP needs to set the UserName and GroupName keys in the .plist file, and they only work when launchd is run as root. My understanding (again I can't test this right now) is that agents are run by the 2nd launchd instance that running as the logged in user, only daemons are run by the instance running as root.

subsonix
May 2, 2011, 10:28 PM
Are you able to test this to be the case? I'm in foreign PC territory right now (I'm at work). My understanding is that a session doesn't start until a user login in. In the context of fast user switching, there can be multiple sessions. Am I wrong?


I'm not saying it should be run in a "per session" context but "per user". If you look at the presentation of launchd in the link this is what is being explained. In a "per session" layer the launch agents come and go with login sessions, in the "per user" layer it stays, which is appropriate if you for example want to run a web server. System-wide is only necessary if you must create a service that needs to operate across multiple accounts on the same computer, it's a "per machine" context.

jiminaus
May 2, 2011, 10:36 PM
Can't get away with watching youtube at work. :D I'll watch it after. I think I need because what you're saying in conflicting with my understanding.

subsonix
May 2, 2011, 10:50 PM
Can't get away with watching youtube at work. :D I'll watch it after. I think I need because what you're saying in conflicting with my understanding.

Actually the context seems to be set by the: LimitLoadToSessionType key to for example: Background, Aqua, LoginWindow. But the layer which transcends login sessions was added in Leopard apparently. So the difference between ~/ and / for LaunchAgents is only the access, administrator or user.

sivaprakash
May 3, 2011, 12:56 AM
Wow !! It works for me.

Many Thanks to every one !!

Mistakes I did:-

- I did realized that there is a folder called Var and Library at the root level since it is not there in my explorer

Like jiminaus pointed out minimum (I would say NIL) knowledge in UNIX environment caused all the issues.

Thanks
Siva

jiminaus
May 3, 2011, 03:06 AM
Wow !! It works for me.

Many Thanks to every one !!

Mistakes I did:-

- I did realized that there is a folder called Var and Library at the root level since it is not there in my explorer

Like jiminaus pointed out minimum (I would say NIL) knowledge in UNIX environment caused all the issues.

Thanks
Siva

Glad you got've this working so far.

There's lot of "under the hood" stuff that you won't see in the GUI. This is because the vastly greater majority of Mac users both don't want to and shouldn't see it.

While I think you're best off using the Terminal to access these parts, you can get the Finder to show these hidden folders. In the terminal, enter this command.

defaults write com.apple.finder AppleShowAllFiles TRUE && killall Finder


Now if you into your Macintosh HD you'll see all the hidden files and folders as ghost icons.

Mr.Texor
May 4, 2011, 07:46 PM
:D ok this is only a joke.. but after reading this thread, all I could think was this.


(again, it's only a joke)

sivaprakash
May 9, 2011, 04:58 AM
@ Texor - What are you trying to convey ?