LaunchControl Help

# User Interface Overview

Note: Learn your way around the interface using tooltips. You can enable tootips by clicking the 'Enable Tooltips' button the title bar or by pressing (⌘/).

Note: LaunchControl will present alerts before most actions to avoid accidental modifications. All of these alerts may be disabled. To re-enable all of them select Help>Re-enable all safety dialogs.

# Table of contents

- The Job Panel

- Job domains
- The job definition file name
- Enabling / disabling a job
- Job state
- Renaming a job definition file
- Adding / removing a job

- The Editor Panel

- Adding a key
- Removing a key
- Loading / unloading a job

- The Palette Panel
- The Log Panel
- Managing jobs with QuickLaunch
- Custom Agents/Daemons
- Templates

- Creating a template
- Renaming and removing templates
- Creating jobs from templates
- Dynamic values

- Finding a job
- Cron support

- Importing jobs from a crontab
- Exporting jobs to crontabs

- Debugging a job
- Removing a job from the Override Database on macOS 10.10 and later
- Making changes to System Agents and Daemons
- fdautil: A safe way to grant Full Disk Access to individual scripts on macOS Mojave and newer

# The Job Panel

## Job domains

LaunchControl supports nine different job domains:

Name | Location | Run on behalf of
--- | --- | ---
User Agents | ~/Library/LaunchAgents | Currently logged in user
Global Agents | /Library/LaunchAgents | Currently logged in user
Global Daemons | /Library/LaunchDaemons | root or the user specified with the key 'User'
System Agents | /System/Library/LaunchAgents | Currently logged in user
System Daemons | /System/Library/LaunchDaemons | root or the user specified with the key 'User'
Server Daemons | /Applications/Server.app/Contents/ServerRoot/System/Library/LaunchDaemons | root or the user specified with the key 'User'
Custom Agents | n/a | Currently logged in user
Custom Daemons | n/a | root or the user specified with the key 'User'
Templates | ~/Library/Application Support/LaunchControl/Templates | Templates cannot be loaded or started. They are just...templates.

Select the desired domain from the Job Domains popup.

Some domains hold a large number of jobs. You may filter the list using the Job Name Filter or the Job Attribute Filter (⌘⇧J). Note: On a fresh install of OSX the domain User Agents is empty.

## The job definition file name

The first column the names of the job definition files. The extension (.plist) is omitted. The color the file name is displayed in represents its validation status.

Color | Validation status | Description
--- | --- | ---
Black | Ok | Every aspect of the job definition is ok. The job should run just fine.
Yellow | Warning | There are one or more minor problems with this job definition. A warning will not keep the job from running. It just might not behave as expected.
Red | Error | There is a severe problem with the job definition. launchd(8) will not be able to start this job.

Note: The colors in this column do not reflect the run-time status of a job but rather the outcome of LaunchControl's validation.

## Enabling/Disabling a job

The checkboxes in the second column indicate if a job is enabled or not. An enabled job will be loaded automatically on system boot (daemons) or login (agents). The check mark indicates if the Disabled key of the job definition has been set to true (not checked) or false (checked). The background color indicates if this setting has been overridden with true (red) or false (green).

If a checkbox is grey, the status of the checkbox will tell you if the job is enabled or not. If a checkbox is green, the job is enabled no matter what the checkbox status indicates. If a checkbox is red, the job is disabled no matter what the checkbox indicates.

Checked | Color | Description
--- | --- | ---
No | Grey | The job's Disabled key is set to true. The job is effectively disabled and will not be automatically loaded by launchd(8).
Yes | Grey | The job's Disabled key is set to false or not set at all. The job is effectively enabled and will be automatically loaded by launchd(8).
No | Green | The job's Disabled key is set to true, but this value is ignored as it has been overridden. The job is effectively enabled and will be automatically loaded by launchd(8).
Yes | Green | The job's Disabled key is set to false or not set at all, but this value is ignored as it has been overridden. The job is effectively enabled and will be automatically loaded by launchd(8).
No | Red | The job's Disabled key is set to true, but this value is ignored as it has been overridden. The job is effectively disabled and will not be automatically loaded by launchd(8).
Yes | Red | The job's Disabled key is set to false or not set at all, but this value is ignored as it has been overridden. The job is effectively disabled and will not be automatically loaded by launchd(8).

Clicking on a check box will set a job's Disabled key. Use the Job>Override Disabled Key to change the override.

Note: Don't confuse this with the job state. Just because a job is enabled does not mean that the job is loaded or even running and vice versa.

## Job state

The third column displays the job state. A job is either loaded or unloaded. Loaded jobs may have the following sub-states:

State | Description
--- | ---
Ok | In case it has been already executed, is has returned successfully.
Running | It is currently being executed.
Error <CODE> | It has been executed and it returned an error. The return code is provided in the status column.
SIG<NAME> | It has been executed and it terminated with the signal given in the status column.

A job may also be in state 'Unknown'. In this case LaunchControl was either unable to parse the job definition file or it could not find the job label. Possible reasons for the first case might be that LaunchControl was unable to read the file due to insufficient permissions or due to a malformed job definition.

Note: This column is not displayed for job templates.

## Renaming a job definition file

Double-click on the name in the list. You can edit the name inline. The extension (.plist) is appended automatically.

Note: Job definition files cannot be renamed while there are unsaved modifications.

Note: If the configuration option 'Synchronize file name and label' has been checked, renaming a job definition file will change the job's label as well.

## Adding / removing a job

Clicking the '+' button in the lower right of the job list section will create an empty job skeleton in the currently selected job domain. The job will have its name set according to the preferences setting 'Custom prefix' appended by the string '.job'. The value of key Program defaults to /usr/bin/env which may be customized by changing the preferences value 'Custom program'.

Every job definition created in the 'Template' domain will appear in the File>New from Template menu. Selecting a template from this menu will create an instance of it in the currently active job domain.

Clicking the '-' button next to it will move the currently selected job definition to the trash. You may recover accidentally removed job definitions by selecting them in the Trash and choosing File>Put Back from the Finder menu. Re-select the current job domain from the Job Domains popup to re-read the restored job definition(s).

# The Editor Panel

LaunchControl provides two edit modes. The default mode provides custom controls for every documented key supported by launchd(8). Using this mode will guaranty that the resulting job definition is valid. The default mode also validates the configuration options you provide. It discovers problems immediately, even before you run the job. If, for example, you provide the key Program, LaunchControl will check if the program exists and if it is executable. If Program is a relative path, LaunchControl will check for the key WorkingDirectory. If this key is set, LaunchControl will assume the program is in this directory. It also considers the keys User and Group when evaluating the permissions. It performs as many checks as possible to ensure that the job definition is valid before you load it into launchd(8).

The expert mode does not restrict you in any way. It allows you to use any key of any type you want. You should rarely need this mode, but it might be useful when using some of the undocumented features of launchd(8).

You can switch between these two modes by selecting View>Toggle Expert Mode (⌘⌥X). Default and expert mode are synchronized. A change made in one editor will immediately be visible in the other one.

Note: Modifications won't affect the running instance until you load your changes into launchd(8) by selecting Job>Load (⌘⇧L) or by clicking the 'Load' button in the upper right corner of the editor panel.

## Adding a key

Make sure the Palette panel is visible View>Toggle Palette (⌘⌥P). Drag & Drop the desired key from the palette on the editor.

## Removing a key

In default mode, select the small cross symbol in the upper right corner of the configuration section. In expert mode, move the mouse pointer to the key you'd like to remove. One or more symbols will appear next to the key name: Add child, Add sibling and Remove. Click the last one.

## Loading / unloading a job

Depending of the state of the currently selected job, the button in the upper right corner of the editor panel will either load or unload the job. Alternatively you may select the appropriate action from the Job menu.

Note: Loading a job does not necessarily start it. How/when a job is started is determined by the keys you provided for launchd(8). If you want to start a job immediately, load it and select Job>Start (⌘⇧T) from the menu.

Note: Templates cannot not be loaded. Instances of templates can.

# The Palette Panel

In this panel you'll find any key officially supported by launchd(8). To make it easier for you to find the right key, LaunchControl has grouped keys into sections. Selecting a section using the popup button in the upper part of the palette panel will limit the number of displayed keys. You may also use the key filter below the section popup button to search for keys by name or description.

The palette panel may be switched on or off by selecting View>Toggle Palette (⌘⌥P).

# The Log Panel

LaunchControl does its best to verify every job-setting you provide. But certain things cannot be checked. You need to run the job and verify that it works as expected. Usually this involves opening Console.app and creating a filter for your specific job, a tail/grep on /var/log/system.log or another means of searching logs. LaunchControl lets you query launchd(8) log files without the hassle. Select the job and click the 'Play' button in the upper left corder of the log panel. Please note that querying the log subsystem can be an expensive operation. Pause the query operation if you don't need it anymore.

The log panel may be switched on or off by selecting View>Toggle Log (⌘⌥L).

Currently you need to run LaunchControl as a privileged user to use the log view for daemons. Unprivileged users may use the log feature only for agents.

Note: The Log Panel is not available in Template mode.

# Managing jobs with QuickLaunch

Sometimes it is desirable to quickly load/unload/start/stop a job on demand without hunting it down in LaunchControl every time. QuickLaunch is a small menu extra in your menu bar. It contains a list of selected jobs and their respective status.

QuickLaunch knows three job states: Unloaded (grey), Loaded (blue) and running (green). Selecting an unloaded job will load it. Loaded jobs have a submenu containing menu items to unload and start the job. For running jobs this submenu holds options to unload or stop the job.

QuickLaunch can also operate on groups of jobs. In the image above there are two groups: the root group and the group "vim". Selecting an action in the root group menu item will perform this action on all jobs in the root group. Selecting an action in the "vim" group menu item will perform this action on all jobs in the "vim" group. These actions don't affect jobs in subgroups unless you hold down the ⌥ key while selecting the action. In this case the action will be performed on all jobs in the group and its subgroups.

Select QuickLaunch Preferences… from the QuickLaunch menu item menu to bring up the QuickLaunch preferences window:

Use the buttons in the menu bar to add jobs, add folders, add menu separators or delete the selected item from the QuickLaunch menu.

Note: For obvious reasons job templates cannot be added to QuickLaunch.

# Custom Agents/Daemons

Custom jobs are just like regular jobs with the exception that they might be located anywhere on the filesystem. Use them to work with jobs not (yet) installed on your system. When creating a new custom job the user is asked for the location. Existing jobs can be imported by selecting File>Import Custom Job… from the menu. Make sure to switch to the proper domain (Custom Agents or Custom Daemons) before doing so.

# Templates

Templates are used as skeletons for new jobs.

## Creating a template

Every job definition you select may be turned into a template by selecting File>Save as Template…. You may also switch to the template job domain using the Job Domains popup in the upper left of the window. From here you can create new templates as you would create new jobs.

Templates are stored in the folder ~/Library/Application Support/LaunchControl/Templates.

## Renaming and removing templates

Renaming and removing templates works like renaming and removing regular job definitions. Double-click on a definition in the job list to rename it. The menu File>Move File to Trash (⌘⌫) will move the selected job definition to the trash.

## Creating jobs from templates

Every template will appear in the File>New from Template menu. Selecting a template from this menu will create an instance of it in the currently active job domain.

## Dynamic values

LaunchControl supports dynamic values in job definitions. When a job definition is saved every occurrence of such a variable will be replaced by its value.

Currently only a single variable is supported: #Label#

Note: Variables will also be replaced if you rename a file and have the 'Synchronize file name and label' configuration option set.

# Finding a job

Job definition files may be stored in different locations (see section Job Domains). To save you the trouble of going through every single domain and scrolling through all jobs LaunchControl provides a search facility. Click Edit>Find Job (⌘F) to bring up the search panel. The search field will already be selected so you can start typing. LaunchControl will search every job definition file name and label as well as the content of the Program/ProgramArguments keys. The results are displayed immediately below the search field. Use the ↑ and ↓ keys to navigate the result set. When the proper job is selected hit ⏎ to jump to the job definition.

# Cron support

## Importing jobs from a crontab

LaunchControl supports importing of jobs from a crontab file. First select the domain (e.g. User Agent, Global Daemon, etc) you want the cron jobs to be imported into using the job domain selector in the upper left of the window. Then select File>Import from crontab… from the menu and select a crontab to open the cron import window.

When the import window opens a list of all jobs configured in the crontab will be displayed. Use the filter field to filter the list of cron jobs by the command field.

To select a cron job for import click the check box in the first column. Alternatively you may use the ↑ or ↓ key to navigate to the job and press the ␣ key to mark it for import. You don't have to leave the filter field for this.

Environment variables defined in the crontab are listed in additional columns of the result table. Their title is prefixed with a $ sign. If a job relies on an environment variable you should check the corresponding check box.

Crontabs may contain an additional field, the user field. Use the "crontab is a system crontab" checkbox to switch between types.

To mark all jobs in the table for import click the "Check all" button in the lower left. Click "Uncheck all" for the reverse effect. Use the "Show checked only" checkbox to display only jobs which have been marked for import.

Click the Import button to import all marked jobs into the current domain.

LaunchControl will create jobs with the appropriate keys:

- ProgramArguments
- StandardInPath
- StandardOutPath
- StandardErrorPath
- EnvironmentVariables
- StartCalendarInterval

Created jobs will have names like "tmp.cronimport.#" where # is a number. Use the job filter (⌘⇧J) to find them quickly.

## Exporting jobs to crontabs

LaunchControl supports exporting of jobs to crontab files. Select File>Export to crontab… from the menu to open the cron export window.

When the export window opens a list of all launchd jobs will be displayed. Use the filter field to filter the list of cron jobs. Using the filter menu you can choose to filter by Label, filename, Program/ProgramArguments, StandardOutPath and StandardErrorPath. You can also choose to include system jobs in your search or to display only jobs of the currently active domain.

To select a cron job for export click the check box in the first column. Alternatively you may use the ↑ or ↓ key to navigate to the job and press the ␣ key to mark it for export. You don't have to leave the filter field for this.

To mark all jobs in the table for export click the Check all button in the lower left. Click Uncheck all for the reverse effect. Use the Show checked only checkbox to display only jobs which have been marked for export.

Click the Export… button and select a destination directory. LaunchControl will create one crontab per user in this directory.

LaunchControl translates the following keys to their cron equivalents:

- Program
- ProgramArguments
- StandardInPath
- StandardOutPath
- StandardErrorPath
- EnvironmentVariables
- StartCalendarInterval

# Debugging a job

While debugging a job it is not necessary to load/unload it manually every time a change to the configuration is made. Event triggered jobs might not be executed at all when loading. The preferred method is to start the job unconditionally by selecting Job>Start (⌘⇧T) from the menu. When a change is detected, a request panel will appear: click the Save, Reload and Start button.

- launchd(8) is not init(8)

  It is important to remember that launchd is responsible for turning a process into a background process. When referencing a shell script in Program or ProgramArguments make sure the script does not put the actual program into the background. Some programs require special command line arguments (something like --foreground) to prevent them from daemonizing themselves. If in doubt try to run the program/script from the terminal. The program/script should only return after the process is done.

  The same goes for resource limits, redirection of standard input/output/error, re-nicing and so on. launchd supports a wealth of configuration options. Use them and don't try to emulate their effects.

- launchd(8) is no shell

  Pipes (|), redirection (<,>) and variable expansion ($VAR) are only three examples for shell constructs that won't be interpreted by launchd(8). If you want to write shell code, give it to the shell. Instead of

  cat /etc/passwd | sort

  write

  /bin/sh -c "cat /etc/passwd | sort"

- Check for warnings and errors

  This is the obvious first step. Make sure you understand every warning and error that LaunchControl reports. Hovering over a warning or error sign in a config sections header will provide details about the error/warning.

- Check the error code

  Check for the exit code of the job in the status column of the job list. Move your mouse over the error code to bring up a tool tip with a likely error reason. See bullet "Check the launchd(8) log" to find out if this error reason applies.

- Check the launchd(8) log

  It is a good idea to have a look at what launchd(8) has to say about the job while trying run it. Open the log panel by selecting View>Toggle Log (⌘⌥L) from the menu. Click the trace-button in the upper left corner of the log panel and start the job by selecting Job>Start (⌘⇧T) from the menu.

  To find out whether a jobs exit code comes from launchd(8) or the program to be executed watch out for these lines:

  Log text | meaning
  --- | ---
  Job failed to exec(3) for weird reason: <code> | This error code was generated by launchd(8) itself. The program specified could not be run. The error details provided in the tool tip of the job list status column are definitely correct.s
  Exited with code: <code> | This error code was generated by the program/script. launchd(8) was able to execute the program but the program did return a non-zero exit code. By convention this is to be interpreted as an error, but actually the author of the script/program is free to return any exit code he/she wishes. In this case the error details provided in the tool tip of the job list status column may or may not be correct. If the author of the program adhered to the standard exit codes they are correct. Otherwise they are completely misleading.

  We are currently not aware of a way to differentiate between these two possibilities.

- Check the jobs standard error/output

  Make sure you configure the keys StandardErrorPath and StandardOutPath. The job might write relevant information to either of these. After running the job click on the trace button in the standard out/error config section. This will open the corresponding file in Console.app.

# Removing a job from the Override Database on macOS 10.10 and later

Starting with OSX 10.10 Yosemite the override database was moved to a different location. Additionally the database is read only when launchd starts. Modifications to the database will be overwritten when launchd quits. Because of this modifications have to be done in recovery mode. LaunchControl can not be used to remove jobs from the override database.

## Note current UID (user id) if you want to remove an agent from the override database.

Open Terminal.app and enter

id -u

The number returned is the user id of the current user. You will need this number later. In this example we assume that the returned user id is 502.

## Boot into Recovery Mode

- Restart your Mac
- Hold down ⌘R before OSX starts up. Hold these keys until the Apple logo appears. After your computer finishes starting up, you should see a desktop with an OS X menu bar and an OS X Utilities window
- Select Terminal from the Utilities menu

## Remove the job from the override database

The following command lines assume that your system volume is called “Macintosh HD”. Please adjust the commands below in case your system volume name differs.

The procedure is different for agents and daemons. You'll need the label of the job you want to remove. In this example: local.job. For agents type:

/usr/libexec/Plistbuddy "/Volumes/Macintosh HD/var/db/com.apple.xpc.launchd/disabled.502.plist" -c Delete:local.job

Don't forget to replace the 502 with your actual user id. For daemons the command reads

/usr/libexec/Plistbuddy "/Volumes/Macintosh HD/var/db/com.apple.xpc.launchd/disabled.plist" -c Delete:local.job

## Reboot

- Enter reboot.

# Making changes to System Agents and Daemons

## OS X 10.11 El Capitan and newer

OS X 10.11 El Capitan introduced System Integrity Protection (SIP). This feature provides an additional layer of security by protecting certain sytem files from modification even by root. In order to make changes to those protected files you have to disable SIP:

- Restart your Mac
- Hold down ⌘R before macOS starts up. Hold these keys until the Apple logo appears. After your computer finishes starting up, you should see a Desktop with a menu bar and a window offering several utility functions.
- Select Terminal from the Utilities menu (the menu, not said window)
- Type csrutil disable and press the ⏎ key.
- You should see a message that System Integrity Protection has been disabled
- Reboot by entering reboot and press the ⏎ key.

SIP is now disabled.

It is recommended to enable SIP again. Follow the instructions above. The command to enable SIP is csrutil enable

## macOS 10.15 Catalina

In addition to SIP macOS 10.15 Catalina separates the Operating System (OS) from the user data. The OS is mounted read-only, so even with SIP disabled you cannot make changes to system files. In order to mount the OS partition in read/write mode disable SIP (see above), open Termina.app and enter:

sudo mount -uw /

# fdautil: A safe way to grant Full Disk Access to individual scripts on macOS Mojave and newer

## Full Disk Access on macOS Mojave and later

With macOS Mojave Apple added yet another security measure. Applications requiring access to sensitive data need special permissions beyond Unix permissions and ACLs. This is accomplished by granting them Full Disk Access in the Security & Privacy System Preferences Panel. While this solution works well for applications it is flawed when it comes to scripts. To grant a script (be it Shell, Python, Perl, …) Full Disk Access you have grant Full Disk Access to the executing interpreter. This approach is flawed as it grants every script of this type Full Disk Access.

## fdautil

LaunchControl provides a command line utility to avoid this situation. The utility is called fdautil and can be installed via the LaunchControl preferences Utilities panel to /usr/local/bin. When installing fdautil LaunchControl will ask you to add it to the list of programs with Full Disk Access.

To grant a job Full Disk Access you'll have to prefix the command line you'd like to execute with /usr/local/bin/fdautil exec. If for example your original job performed the command

/Users/Me/Scripts/CheckDownloads.py

you change it to

/usr/local/bin/fdautil exec /Users/Me/Scripts/CheckDownloads.py

LaunchControl requires admin privileges to add your script (including the specific options and arguments) to the fdautil configuration file.

### How does it work?

fdautil stores the complete command you want to execute with Full Disk Access in the configuration file /Library/Preferences/com.soma-zone.LaunchControl.fdautil.plist. This configuration file is writable only by root. Only commands (including arguments/options) stored in this configuration file are executed by fdautil, others are rejected. It uses execvp(3) to replace itself with the command you'd like to run. LaunchControl does all the configuration for you. When you change the Program/ProgramArguments key it is updating the fdautil configuration automatically.

### What is gained?

You get the benefit of Apple's new security feature and can use scripts with Full Disk Access.