Daemon App

Discussion in 'Mac Programming' started by sivaprakash, Apr 29, 2011.

  1. macrumors member

    Joined:
    Mar 10, 2011
    Messages:
    52
    Location:
    Chennai, India
    #1
    Hi,

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


    Thanks
    Siva
     
  2. macrumors Pentium

    KnightWRX

    Joined:
    Jan 28, 2009
    Messages:
    15,043
    Location:
    Quebec, Canada
    #2
    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 ?
     
  3. macrumors member

    Joined:
    Mar 10, 2011
    Messages:
    52
    Location:
    Chennai, India
    #3
    :) 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 !!
     
  4. macrumors member

    Joined:
    Mar 10, 2011
    Messages:
    52
    Location:
    Chennai, India
    #4
    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
     
  5. jiminaus, May 2, 2011
    Last edited: May 2, 2011

    macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Messages:
    1,448
    Location:
    Sydney
    #5
    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:
    Code:
    sudo launchctl load -w /Library/LaunchDaemons/com.company.product.plist
    
    Additionally (quoted from the launchctl man page):
    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:

    Code:
    <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.
    Code:
    <key>StandardOutPath</key>
    <string>/var/tmp/myserver.out</string>
    <key>StandardErrorPath</key>
    <string>/var/tmp/myserver.err</string>
    
     
  6. macrumors 68040

    Joined:
    Feb 2, 2008
    Messages:
    3,360
    #6
    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.
     
  7. macrumors member

    Joined:
    Mar 10, 2011
    Messages:
    52
    Location:
    Chennai, India
    #7
    Hi


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

    PHP:
    $str ="Test Daemon !....";
    open FILE">file.txt" or die $!; 
    print 
    FILE $str
    close FILE;
    and kept my .plist file under "/Library/LaunchDaemons"


    Code:
    <?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?
     
  8. macrumors 68040

    Joined:
    Feb 2, 2008
    Messages:
    3,360
    #8
    You don't need to run this in /Library/LaunchDaemons, so you shouldn't.

    From Apple technical note tn2083:

    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.
     
  9. jiminaus, May 2, 2011
    Last edited: May 2, 2011

    macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Messages:
    1,448
    Location:
    Sydney
    #9
    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
    Code:
    $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
    Code:
    <?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.
     
  10. sivaprakash, May 2, 2011
    Last edited: May 2, 2011

    macrumors member

    Joined:
    Mar 10, 2011
    Messages:
    52
    Location:
    Chennai, India
    #10
    Hi,

    Step 1:-

    Here is my PLIST under System/Library/LaunchDaemons/

    Code:
    
    <?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/

    Code:
    
    $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?
     
  11. macrumors 603

    Joined:
    Aug 9, 2009
    Messages:
    6,404
    #11
    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:
    Code:
    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.
     
  12. macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Messages:
    1,448
    Location:
    Sydney
    #12
    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)
    Code:
    ls -al /Library/LaunchDaemons/
    
    You need to unload the .plist file, before you reload a newer version.
    Code:
    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:
    Code:
    man launchctl
    
    You can get a list of all the launchd .plist keys and their documentation with the command:
    Code:
    man launchd.plist
    
    You can get the manual page for the man command itself with:
    Code:
    man man
    
    Lastly the commands for change permissions and ownership are chmod and chown respectively, you might want to read those manual pages.
     
  13. macrumors 68040

    Joined:
    Feb 2, 2008
    Messages:
    3,360
    #13
    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.

    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
     
  14. macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Messages:
    1,448
    Location:
    Sydney
    #14
    Fair enough. But a daemon and an agent are setup the same way, just in different directories.

    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.
     
  15. macrumors 68040

    Joined:
    Feb 2, 2008
    Messages:
    3,360
    #15
    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.
     
  16. macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Messages:
    1,448
    Location:
    Sydney
    #16
    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.
     
  17. macrumors 68040

    Joined:
    Feb 2, 2008
    Messages:
    3,360
    #17
    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.
     
  18. macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Messages:
    1,448
    Location:
    Sydney
    #18
    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.
     
  19. macrumors 68040

    Joined:
    Feb 2, 2008
    Messages:
    3,360
    #19
    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.
     
  20. macrumors member

    Joined:
    Mar 10, 2011
    Messages:
    52
    Location:
    Chennai, India
    #20
    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
     
  21. macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Messages:
    1,448
    Location:
    Sydney
    #21
    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.
    Code:
    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.
     
  22. macrumors regular

    Joined:
    Apr 20, 2007
    Messages:
    228
    #22
    :D ok this is only a joke.. but after reading this thread, all I could think was this.


    (again, it's only a joke)
     

    Attached Files:

  23. macrumors member

    Joined:
    Mar 10, 2011
    Messages:
    52
    Location:
    Chennai, India
    #23
    @ Texor - What are you trying to convey ?
     

Share This Page