PGTS PGTS Pty. Ltd.   ACN: 007 008 568

point Site Navigation

point Other Blog Threads



  Valid HTML 4.01 Transitional

   Stop The Internet Filter!

   No Clean Feed

   The Internet Filter Is An Ex-parrot!






PGTS Humble Blog

Thread: Mac OS X, Apple

GP JPG
Gerry Patterson, The man who almost invented humble sarcasm tags(Invisible to non-sarcastic browsers)

Mac OS And launchctl


Chronogical Blog Entries:



Date: Mon, 12 Jul 2010 19:21:51 +1000

Since versions 10.4 of Mac OS X, management of daemons and scheduled tasks has been carried out by launchctl rather than init, xinet, cron, at, etc. According to Apple the launchd daemon gives better control because it is a central process with finer granularity. However, if you are familiar with the init, rc and cron process, and have spent a substantial portion of your life composing scripts for them, launchctl might seem a trifle obscure ... Not to mention the fact that you probably already have a significant bag of scripts from your previous unix systems.

Fortunately, you don't have to ditch your scripts altogether. Cron still works and if you prune your init scripts carefully, or perhaps just limit them to what goes into rc.local, you can might still be able to use many of them on the Apple platform.

The official advice from Apple is that the older systems will eventually fade away. However with a such a large legacy of existing scripts, it is unlikely that they going to disappear very soon ... And even if Apple ceases "official" support, it would still be possible to "launch" them with launchctl ... I'd like to say it's not "rocket science" ... But you might start groaning out loud.

For the time being launchctl starts the rc.local script in much the same way that the init sub-system does. And the cron daemon is also started by launchctl at system startup if it detects any crontab files (in /usr/lib/cron/tabs/)

So, to use cron, you only need to create a crontab entry. The super user can do so with the "crontab" commmand. Other users can also use the same command, depending on how the system is configured. The access in Mac OS X is controlled by the usual method (/usr/lib/cron/cron.deny and/or /usr/lib/cron/cron.allow). Note on the Mac the /usr/lib/cron is often a logical link to /var/at/)

After creating a crontab entry, you can reboot the system ... Or you could load the cron process with launchctl (using the com.vix.cron.plist in the LaunchDaemons folder)

You can enter the individual commands with the launchctl command. Or you can access launchctl as a standalone process with this command:

sudo launchctl

The 'help' command gives a list of the possible commands.

launchctl is controlled via XML configuration files that usually end in the .plist suffix. You will find the control files in the folder /System/Library/LaunchDaemons which contains lists of daemons and /System/Library/LaunchAgents which contains lists of agents.

There should be an equivalent .plist file for most xinet services. For example there is a file called telnet.plist which will perform many of the functions provided by the xinet configuration statements for the telnet service (port 23). As one would expect this is disabled by default. Note: There does not seem to an equivalent of the xinet telnet only_from tag in the launchctl implementation of telnet. So if you do enable telnet it will be enabled for the universe ... Access must be managed by your firewall ... Which is no big deal ... Because it probably is anyway.

Now, I expected that in order to get a service such as telnet working, all I needed to do was modify the .plist file and then restart the service. This might be true for some XML tags ... But launchctl has a few odd quirks ... I tried modifying the telnet.plist file ... Which looked like this:
<?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 >Disabled </key>
         <true />
         <key >Label</key>
         <string >com.apple.telnetd </string>
         <key >ProgramArguments </key>
         <array >
                 <string >/usr/libexec/telnetd </string>
         </array>
         <key >inetdCompatibility </key>
         <dict >
                 <key >Wait</key>
                 <false />
         </dict>
         <key >Sockets </key>
         <dict >
                 <key >Listeners </key>
                 <dict >
                         <key >SockServiceName </key>
                         <string >telnet </string>
                         <key >Bonjour </key>
                         <true />
                 </dict>
         </dict>
         <key >SessionCreate </key>
         <true />
</dict>
</plist>

I had thought that by changing the "Disabled" tag from "true" to "false" the service would be enabled ... Rather than spend any time working out how to restart things, I rebooted the machine ... And discovered that telnet was still not enabled.

Oh well, when all else fails ... RTFM!

In the man pages I found the following explanation of the -w option for the "load" command:
-w Overrides the Disabled key and sets it to false. In previous versions, this option would modify the configuration file. Now the state of the Disabled key is stored elsewhere on-disk.

It seems that launchctl has a few idiosyncracies! Previous versions of launchctl would modify the XML config file when the -w switch was invoked. However the latest version appears to ignore the XML tag altogether.

The man page for launchd.plist(5) entry has a detailed explanation of the disabled "Disabled" key:
Disabled <boolean>
This optional key is used as a hint to launchctl(1) that it should not submit this job to launchd when loading a job or jobs. The value of this key does NOT reflect the current state of the job on the running system. If you wish to know whether a job is loaded in launchd, reading this key from a configuration file yourself is not a sufficient test. You should query launchd for the pres- ence of the job using the launchctl(1) list subcommand or use the ServiceManagement framework's SMJobCopyDictionary() method.

Note that as of Mac OS X v10.6, this key's value in a configuration file conveys a default value, which is changed with the [-w] option of the launchctl(1) load and unload subcommands. These subcommands no longer modify the configuration file, so the value displayed in the configuration file is not necessarily the value that launchctl(1) will apply. See launchctl(1) for more informa- tion.

Please also be mindful that you should only use this key if the provided on-demand and KeepAlive criteria are insufficient to describe the conditions under which your job needs to run. The cost to have a job loaded in launchd is negligible, so there is no harm in loading a job which only runs once or very rarely.

In other words RTFM first!

So, in order to start telnet, you should enter a load command with the -w option ... such as the following:

sudo launchctl load -w /System/Library/LaunchDaemons/telnet.plist

You can inspect the status of the telnet daemon with the "list" command. If you try listing "com.apple.telnetd", you might see something the following:
% launchctl list com.apple.telnetd
{
        "Label" = "com.apple.telnetd";
        "LimitLoadToSessionType" = "System";
        "OnDemand" = true;
        "LastExitStatus" = 0;
        "TimeOut" = 30;
        "ProgramArguments" = (
                "/usr/libexec/telnetd";
        );
        "SessionCreate" = true;
        "inetdCompatibility" = {
                "Wait" = false;
        };
        "Sockets" = {
                "Listeners" = (
                        file-descriptor-object;
                        file-descriptor-object;
                );
        };
};

In other words everything that was in the telnet.plist configuration file ... Except of course the "Disabled" switch.

To make changes to a process, you should load and unload the associated .plist. For example if you alter NFS by making a change to /etc/exports. You can make the changes take effect (without waiting for a reboot) with these commands:

sudo launchctl unload /System/Library/LaunchDaemons/com.apple.nfsd.plist
sudo launchctl load /System/Library/LaunchDaemons/com.apple.nfsd.plist

To discover the PID and name of NFS daemon, you could use:

sudo launchctl list | grep -i nfs

Other Blog Posts In This Thread:

Copyright     2010, Gerry Patterson. All Rights Reserved.