<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>michael-mccracken.net &#187; python</title>
	<atom:link href="http://michael-mccracken.net/tag/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://michael-mccracken.net</link>
	<description>This is a weblog</description>
	<lastBuildDate>Tue, 25 May 2010 15:31:35 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Snakes on Demand: How to write a Python Launchd Agent</title>
		<link>http://michael-mccracken.net/2010/03/snakes-on-demand-how-to-write-a-python-launchd-agent/</link>
		<comments>http://michael-mccracken.net/2010/03/snakes-on-demand-how-to-write-a-python-launchd-agent/#comments</comments>
		<pubDate>Mon, 15 Mar 2010 02:23:52 +0000</pubDate>
		<dc:creator>mike</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[launchd]]></category>
		<category><![CDATA[mac]]></category>
		<category><![CDATA[macdev]]></category>
		<category><![CDATA[multiprocessing]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://michael-mccracken.net/?p=269</guid>
		<description><![CDATA[I share what I've learned about writing a Launchd agent that is mostly written in Python. This includes a link to a sample project hosted on bitbucket.]]></description>
			<content:encoded><![CDATA[<p>Suppose you want to write a Launchd agent in Python that communicates using UNIX domain sockets. There&#8217;s no sample code for that, but the information is out there to figure out how (especially because the <a href="http://launchd.macosforge.net">launchd source code</a> is available). Most search results will tell you that you need to read the <a href="http://developer.apple.com/mac/library/technotes/tn2005/tn2083.html">Daemons and Agents Tech Report TN2083</a>, but it&#8217;s pretty long and not a great tutorial. A better intro reference is Chris Hanson&#8217;s blog post <a href="http://chanson.livejournal.com/179229.html">&#8220;Launchd: better than sliced bread!&#8221;</a>, but that doesn&#8217;t tell you everything, and doesn&#8217;t mention Python.</p>

<p>I decided I&#8217;d share what I&#8217;ve learned recently to make getting started a little easier. I&#8217;ll give a short description here and post the resulting code to bitbucket at <a href="http://bitbucket.org/mikemccracken/py-launchd/">py-launchd</a>.</p>

<p><em>Note:</em> This was written and tested on OS X 10.5.8 with the default <code>/usr/bin/python</code>, version 2.5.1. 
Since I wanted to use multiprocessing, I used the backport to 2.5 available at <a href="http://code.google.com/p/python-multiprocessing/">google code: python-multiprocessing</a> . For convenience, it&#8217;s included in the repository, and so is its license.</p>

<h3>Goal</h3>

<p>What we&#8217;re trying to do is have some python code that gets called when launchd notices someone connecting to a <a href="http://en.wikipedia.org/wiki/Unix_domain_socket">UNIX domain socket</a>. UNIX domain sockets are local-only, so this is ideal for agents that only serve local apps. We&#8217;re also using launchd &#8220;agents&#8221; not &#8220;daemons&#8221;, so we&#8217;re assuming that it&#8217;s OK to have one agent process for each user. If you&#8217;re managing access to something that needs to be unique system-wide, then this won&#8217;t work (but you can still use Launchd).</p>

<h3>launchd overview</h3>

<p>The launchd process will read a launchd plist you give it (at login, via launchctl or the 10.6 framework), and listen on the socket you tell it to. Once it sees a connection, it&#8217;ll start the agent program you specified in the plist, and that program can make some calls using the launchd C API to get a file descriptor for the socket that was connected. This is important &#8211; you don&#8217;t need to call bind() or listen() on the socket, because launchd already did. It&#8217;s just handing the open socket&#8217;s file descriptor straight to you and your code can just call accept() on it.</p>

<h3>Using launchd with Python</h3>

<p>You need to create a C/ObjC tool that can do the launchd check-in to get the open file descriptor, then pass that off to your Python code. This is pretty straightforward using the Python framework included in OS X and the Python C APIs.</p>

<p>What I&#8217;ve done is create an agent loader that I called PyLaunchd that loads and runs server code in Agent.py. It expects Agent.py to be in the same directory.</p>

<p>PyLaunchd is built separately and copied into the test app&#8217;s Resources folder with an XCode script phase.</p>

<p>I have the app delegate copy <code>PyLaunchd</code> and <code>Agent.py</code> to <code>~/Library/Application Support/PyLaunchd/</code> on loading.
It also customizes the launchd plist to set the path correctly for the current user, then writes that to <code>~/Library/LaunchAgents/</code>, and loads it. (Actually it first unloads it, then reloads it. I&#8217;m not convinced this is the right way to do it). It uses system() to call launchctl, but I believe in OS X 10.6 there&#8217;s an API you can call to do it directly.</p>

<p>Finally, the sample app is really simple &#8211; it just opens the socket using the multiprocessing Client class, and sends whatever you type. The example Agent I&#8217;ve included will ROT13 it and send it back.</p>

<p>Please let me know if you have any comments, questions, or improvements.</p>
]]></content:encoded>
			<wfw:commentRss>http://michael-mccracken.net/2010/03/snakes-on-demand-how-to-write-a-python-launchd-agent/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
