One of the core components of Mac OS X is launchd, and it turns out it can do some cool things.

I particularly like the idea of using QueueDirectories to monitor and act upon files dropped into a directory, without having to run any extra daemons. The files could be uploaded to S3, transcoded to a different video format, gzipped… anything.

Anyway, I recently fell into the launchd documentation, and came out with this write-up. Let me know if you find it useful.


The first thing that the Mac OS kernel runs on boot is launchd, which bootstraps the rest of the system by loading and managing various daemons, agents, scripts and other processes. The launchd man page clarifies the difference between a daemon and an agent:

In the launchd lexicon, a “daemon” is, by definition, a system-wide service of which there is one instance for all clients. An “agent” is a service that runs on a per-user basis. Daemons should not attempt to display UI or interact directly with a user’s login session. Any and all work that involves interacting with a user should be done through agents.

Daemons and agents are declared and configured by creating .plist files in various locations of the system:

~/Library/LaunchAgents         Per-user agents provided by the user.
/Library/LaunchAgents          Per-user agents provided by the administrator.
/Library/LaunchDaemons         System-wide daemons provided by the administrator.
/System/Library/LaunchAgents   Per-user agents provided by OS X.
/System/Library/LaunchDaemons  System-wide daemons provided by OS X.

Perhaps best of all, launchd is open source under the Apache License 2.0. You can currently find the latest source code on the Apple Open Source site.

launchd as cron

The Mac OS crontab man page says:

Although cron(8) and crontab(5) are officially supported under Darwin,
their functionality has been absorbed into launchd(8), which provides a
more flexible way of automatically executing commands.

Turns out launchd has a simple StartInterval <integer> property, which starts the job every N seconds. However the true cron-like power lies in StartCalendarInterval:

StartCalendarInterval <dictionary of integers or array of dictionary of integers>

This optional key causes the job to be started every calendar interval as
specified. Missing arguments are considered to be wildcard. The semantics
are much like crontab(5).  Unlike cron which skips job invocations when the
computer is asleep, launchd will start the job the next time the computer
wakes up.  If multiple intervals transpire before the computer is woken,
those events will be coalesced into one event upon wake from sleep.

     Minute <integer>
     The minute on which this job will be run.

     Hour <integer>
     The hour on which this job will be run.

     Day <integer>
     The day on which this job will be run.

     Weekday <integer>
     The weekday on which this job will be run (0 and 7 are Sunday).

     Month <integer>
     The month on which this job will be run.

Lets find the shortest example of this in action:

pda@paulbook ~ > grep -rl StartCalendarInterval \
                   /Library/Launch* /System/Library/Launch* | \
                   xargs wc -l | sort -n | head -n1 | awk '{print $2}' | xargs cat

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "">
<plist version="1.0">

Better than cron? Apart from better handling of skipped jobs after system wake, it also supports per-job environment variables, which can save writing wrapper scripts around your cron jobs:

EnvironmentVariables <dictionary of strings>

This optional key is used to specify additional environmental variables to
be set before running the job.

So, anything XML is obviously worse than 0 52 3 * 5 /path/to/command, but launchd is packing more features than cron, so it can pull it off.

launchd as a filesystem watcher

Apart from having an awesome daemon/agent manager, Mac OS X also has an excellent Mail Transport Agent called postfix. There’s a good chance your ISP runs the same software to handle millions of emails every day. We’ll be using it as an example of how launchd can start jobs based on filesystem changes.

Because your laptop isn’t, and shouldn’t be, a mail server, you don’t want postfix running all the time. But when messages are injected into it, e.g. by a script shelling out to /usr/sbin/sendmail or /usr/bin/mail, you want them to be delivered straight away.

Here’s how Mac OS X does it (/System/Library/LaunchDaemons/org.postfix.master.plist):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "">
<plist version="1.0">

We’ll start with the simple part. ProgramArguments passes -e 60 to postfix, described thusly:

-e exit_time
              Terminate the master process after exit_time seconds.
              Child processes terminate at their convenience.

So postfix is told to exit after running for 60 seconds. The mystery (to me, earlier today, at least) is how it gets started. It could be on a cron-like schedule, but (a) it isn’t, (b) that would suck, and (c) it would result in delayed mail delivery. It turns out the magic lies in QueueDirectory, which I initially overlooked thinking it was a postfix option. The launchd.plist man page says:

WatchPaths <array of strings>
This optional key causes the job to be started if any one of the listed
paths are modified.

QueueDirectories <array of strings>
Much like the WatchPaths option, this key will watch the paths for
modifications. The difference being that the job will only be started if
the path is a directory and the directory is not empty.

The Launchd Wikipedia page actually goes into more detail:

Watch a directory for new files. The directory must be empty to begin with,
and must be returned to an empty state before QueueDirectories will launch
its task again.

So launchd can monitor a directory for new files, and then trigger an agent/daemon to consume them. In this case, the postfix sendmail(1) man page tells us that “Postfix sendmail(1) relies on the postdrop(1) command to create a queue file in the maildrop directory”, and the man page for postdrop(1) tells us that /var/spool/postfix/maildrop is the maildrop queue. launchd sees new mail there, fires up postfix, and then stops it after 60 seconds. This might cause deferred mail to stay deferred for quite some time, but again; your laptop isn’t a mail server.

launchd as inetd

Tranditionally the inetd and later xinetd “super-server daemon” were used to listen on various ports (e.g. FTP, telnet, …) and launch daemons on-demand to handle in-bound connection, keeping them out of memory at other times. Sounds like something launchd could do…

Lets create a simple inetd-style server at ~/Library/LaunchAgents/my.greeter.plist:

<plist version="1.0">
    <string>puts "Hi #{gets.match(/(\w+)\W*\z/)[1]}, happy #{"%A")}!"</string>

Load it up and give it a shot:

pda@paulbook ~ > launchctl load ~/Library/LaunchAgents/my.greeter.plist
pda@paulbook ~ > echo "My name is Paul." | nc localhost 13117
Hi Paul, happy Friday!

launchd as god!

You can use launchd to ensure a process stays alive forever using <key>KeepAlive</key><true/>, or stays alive under the following conditions.

  • SuccessfulExit — the previous run exited successfully (or if false, unsuccessful exit).
  • NetworkState — network (other than localhost) is up (or if false, down).
  • PathState — list of file paths exists (or if false, do not exist).
  • OtherJobEnabled — the other named job is enabled (or if false, disabled).

These can be combined with various other properties, for example:

  • WorkingDirectory
  • EnvironmentVariables
  • Umask
  • ThrottleInterval
  • StartOnMount
  • StandardInPath
  • StandardOutPath
  • StandardErrorPath
  • SoftResourceLimits and HardResourceLimits
  • Nice


There’s some more information at, and the launchd and launchd.plist man pages are worth reading.


在mac os x系统编译php时,在configure阶段就报如下错误:

error: Don’t know how to define struct flock on this system, set –enable-opcache=no


sudo cp /opt/mysql/lib/libmysqlclient.* /usr/local/lib/libmysqlclient.*



sqlite3 ~/Library/Keychains/*/ocspcache.sqlite3 ‘DELETE FROM responses WHERE responderURI LIKE “tp://”;’

升级xcode 后 Qt 出问题了,google 找到了解决方法。

~> Xcode 8

This problem occurs when command line tools are installed after Xcode is installed. What happens is the Xcode-select developer directory gets pointed to /Library/Developer/CommandLineTools.

Step 1:

Point Xcode-select to the correct Xcode Developer directory with the command:

sudo xcode-select -switch /Applications/

Step 2:

Confirm the license agreement with the command:

xcodebuild -license

This will prompt you to read through the license agreement. 

Enter agree to accept the terms.

>= Xcode 8

Step 1:

As Bruce said, this happens when Qt tries to find xcrun when it should be looking for xcodebuild.

Open the file:


Step 2:


isEmpty($$list($$system(“/usr/bin/xcrun -find xcrun 2>/dev/null”))))


isEmpty($$list($$system(“/usr/bin/xcrun -find xcodebuild 2>/dev/null”)))


安装macOS Sierra后,会发现系统偏好设置的“安全与隐私”中默认已经去除了允许“任何来源”App的选项,无法运行一些第三方应用。


  1. sudo spctl –master-disable



需要说明的是,如果在系统偏好设置的“安全与隐私”中重新选中允许App Store 和被认可的开发者App,即重新打开Gatekeeper后,允许“任何来源”App的选项会再次消失,可运行上述命令再次关闭Gatekeeper。

更新了 Mac OS X 11后发现,MacVim 不再能够通过Terminal用命令打开了。

mvim hello.txt

于是尝试将 mvim 重新复制到/usr/bin/中去

sudo cp -f mvim /usr/bin/


cp: /usr/bin/mvim: Operation not permitted

搜索之后发现,是El Capitan 加入了Rootless机制,不再能够随心所欲的读写很多路径下了。设置 root 权限也不行。


于是尝试关闭 Rootless。重启按住 Command+R,进入恢复模式,打开Terminal。

csrutil disable


csrutil enable


csrutil enable [–without kext | fs | debug | dtrace | nvram][–no-internal]

禁用:csrutil disable

(等同于csrutil enable –without kext –without fs –without debug –without dtrace –without nvram)


  • B0: [kext] 允许加载不受信任的kext(与已被废除的kext-dev-mode=1等效)
  • B1: [fs] 解锁文件系统限制
  • B2: [debug] 允许task_for_pid()调用
  • B3: [n/a] 允许内核调试 (官方的csrutil工具无法设置此位)
  • B4: [internal] Apple内部保留位(csrutil默认会设置此位,实际不会起作用。设置与否均可)
  • B5: [dtrace] 解锁dtrace限制
  • B6: [nvram] 解锁NVRAM限制
  • B7: [n/a] 允许设备配置(新增,具体作用暂时未确定)




sudo chflangs nohidden [文件名或目录名]


sudo chflags hidden [文件名或目录名]

I’m trying to compile PHP 5.6.10 from the source, and I encountered the following problem:

Undefined symbols for architecture x86_64:
  "_PKCS5_PBKDF2_HMAC", referenced from:
      _zif_openssl_pbkdf2 in openssl.o
  "_TLSv1_1_client_method", referenced from:
      _php_openssl_setup_crypto in xp_ssl.o
  "_TLSv1_1_server_method", referenced from:
      _php_openssl_setup_crypto in xp_ssl.o
  "_TLSv1_2_client_method", referenced from:
      _php_openssl_setup_crypto in xp_ssl.o
  "_TLSv1_2_server_method", referenced from:
      _php_openssl_setup_crypto in xp_ssl.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [libs/libphp5.bundle] Error 1

OpenSSL is installed via Brew. In PHP included like --with-openssl=/usr/local/Cellar/openssl/1.0.2c

P.S. Before tried to use just /usr for OpenSSL but got the same error.


The Makefile has a line with EXTRA_LIBS, something like:

EXTRA_LIBS = -lresolv -lmcrypt -lltdl -liconv-lm -lxml2 -lcurl -lssl -lcrypto

Remove all occurrences of -lssl and -lcrypto and add the full path to libssl.dylib and libcrypto.dylib (brew links openssl to /usr/local/opt/openssl/lib/)

EXTRA_LIBS = -lresolv -lexslt -ltidy -lmysqlclient -lmcrypt -lltdl /usr/local/lib/libiconv.dylib /usr/local/Cellar/openssl/1.0.2d_1/lib/libssl.dylib /usr/local/Cellar/openssl/1.0.2d_1/lib/libcrypto.dylib -lpng -lz -ljpeg -lcurl -lbz2 -lz  -lm -lxml2 -lz -licucore -lm -lcurl -lldap -lz -lxml2 -lz -licucore -lm -lfreetype -lmysqlclient -lxml2 -lz -licucore -lm -lxml2 -lz -licucore -lm -lxml2 -lz -licucore -lm -lxml2 -lz -licucore -lm -lxml2 -lz -licucore -lm -lxslt -lxml2 -lz -licucore -lm


sudo scutil –set HostName 新的主机名


sudo scutil –set ComputerName 新的共享名


其实制作 OS X Yosemite 正式版 USB 启动盘的方法有很多,譬如使用命令行的,也有使用第三方工具的。这个教程主要介绍前者,因为这是目前我了解到的最稳妥、简单,而且没有兼容性问题的方法了。




  1. 准备一个 8GB 或以上容量的 U 盘,确保里面的数据已经妥善备份好(该过程会抹掉 U 盘全部数据)
  2. 从这里下载苹果官方 OS X Yosemite 正式版的安装程序 (可选 AppSotre 或网盘下载)
  3. 如果你是从 Mac AppStore 下载的,下载完成后安装程序可能自动开始,这时先退出安装
  4. 如从网盘下载的,请将解压后获得的 “Install OS X” (显示为 “安装 OS X”) 移动到「应用程序」文件夹里面

二、格式化 U 盘:

插入你的 U 盘,然后在「应用程序」->「实用工具」里面找到并打开「磁盘工具」,或者直接用 Spotlight 搜索“磁盘工具” 打开,如下图。


  • 1 – 在左方列表中找到 U 盘的名称并点击
  • 右边顶部选择 2 -「分区」,然后在 3 -「分区布局」选择「1个分区」
  • 在分区信息中的 4 -「名称」输入「iPlaySoft」 (由于后面的命令中会用到此名称,如果你要修改成其他(英文),请务必对应修改后面的命令)
  • 在「格式」中选择 5 -「Mac OS 扩展 (日志式)」
  • 这时,先别急着点“应用”,还要先在 6 -「选项」里面,如下图


  • 选择「GUID 分区表」,然后点击「好」
  • 最后再点「应用」开始对 U 盘进行格式化。


  • 请再次确保名为 “安装 OS X Yosemite” 的文件是保存在「应用程序」的目录中
  • 在「应用程序」->「实用工具」里面找到「终端」并打开。也可以直接通过 Spotlight 搜索「终端」打开
  • 复制下面的命令,并粘贴到「终端」里,按回车运行:

sudo /Applications/Install\ OS\ X\ –volume /Volumes/iPlaySoft –applicationpath /Applications/Install\ OS\ X\ –nointeraction


Erasing Disk: 0%… 10%… 20%… 30%…100%…
Copying installer files to disk…
Copy complete.
Making disk bootable…
Copying boot files…
Copy complete.

当你看到最后有 「Copy complete」和「Done」 字样出现就是表示启动盘已经制作完成了!

四、U 盘启动安装 OS X Yosemite 的方法:

当你插入制作完成的 OS X Yosemite U盘启动盘之后,桌面出现「Install OS X Yosemite」的盘符那么就表示启动盘是正常的了。那么怎样通过 USB 启动进行全新的系统安装呢?

其实很简单,先在目标电脑上插上 U 盘,然后重启你的 Mac,然后一直按住「option」(alt) 按键不放,直到屏幕显示多出一个 USB 启动盘的选项,如下图。


这时选择 U 盘的图标回车,即可通过 U 盘来安装 Yosemite 了!这时,你可以直接覆盖安装系统(升级),也可以在磁盘工具里面格式化抹掉整个硬盘,或者重新分区等实现全新的干净的安装。