As I wrote that title, I thought for one moment that I was going to start another parody of Dan Brown (his “Angels and Demons” thriller hasn’t yet been turned into a blockbuster movie, but I think it’s in the pipeline). But no, I stopped myself just in time. In fact I’ve been trying to develop a small utility for my own purposes, and I ended up reading a technical note from Apple about agents and daemons, which had a section wonderfully entitled Daemonomicon.
Apple’s note explains the difference between agents (background applications that work on behalf of a specific user) and daemons (programs that operate system-wide, not directly for the benefit of a user). So I ended up writing an agent rather than a daemon. A pity really, because I like the idea of conjuring up Unix daemons.
I think (but I’m not much of an expert) that newLISP is a good language for writing these agents. As a language it’s small, fast, and light on its feet, yet has many useful features.
One of the jobs I wanted to do was to keep a record of all the music being played in iTunes. With a bit of AppleScript and a bit of newLISP I rustled up this little script:
#!/usr/bin/newlisp
(set 'previous-music "")
(while true
(and
(exec {ps x | grep [/]Applications/iTunes.app/Contents/MacOS/iTunes}) ; iTunes running?
(set 'current-music
(exec [text]osascript -e 'tell application "iTunes" to if
player state is playing then get (player state & "~~~" &
name of current track & "~~~" & artist of current track &
"~~~" & current stream title) as Unicode text'[/text]))
(set 'current-music
(parse (first current-music) "~~~"))
(= (current-music 0) "playing") ; and playing?
(!= previous-music current-music) ; different track?
(set 'the-date (date (date-value) 0 "%Y-%m-%d %H:%M:%S "))
(if (find "SomaFM" (current-music 1))
; streaming
(set 'display-string
(string the-date
"[SomaFM] "
(0 12 (current-music 1))
"... : "
(current-music 3) "\n"))
; not streaming
(set 'display-string
(string the-date
"[Library] "
(current-music 1)
": "
(current-music 2) "\n")))
(append-file "/Users/me/Music/itunes-playlist" display-string)
(set 'previous-music current-music)) ; remember this one
; in any case, sleep for 20 seconds before trying again
(sleep 20000))
Notice that I’ve been listening to SomaFM internet radio recently, so I’m asking iTunes for the current stream title as well as the current track’s name and artist. If you listen to – or record :-) – streaming radio, you can get the latest song details as the songs change, even though iTunes thinks it’s just playing the same track.
I’ve used a separator of “~~~” which makes parsing the string returned from iTunes easy. And I’ve found myself using that and construct again, which pleases me strangely.
This all seems to work fine, so the one remaining question is: how do I make it launch as a background ‘agent’ whenever I log in, without a visit to the terminal? Apple have done something a bit different, here. They’ve developed a new Unix architecture for launching processes, which uses the command ‘launchd’. This is gradually replacing the old Unix standards such as rc (whatever that is), and which is also going to supersede things like cron, eventually. (In fact, if you look at the /etc/crontab file, it says that “the periodic and atrun jobs have moved to launchd jobs”. So the supersession is already under way.)
Since this is a Mac, though, you can usually avoid learning all those command-line mystic runes by using a nice friendly GUI application. For controlling your daemons and agents, all you need is Lingon, a lovely bit of programming by Peter Borg that makes all the launchd and launchctl stuff easy to use: “Launch your dreams right here” is his slogan, and all you do is show it your script and a new agent will be sent on its mission.
In use, this secret agent doesn’t look like it’s using much memory, compared with cron, for example:
%cpu VMem Real mem User PID CPU # Private Shared Messages
cron 0.00 26.88 MB 504.00 KB root 76 00.01 1 136.00 KB 352.00 KB 56
newlisp 0.00 26.93 MB 512.00 KB me 5062 00.00 1 176.00 KB 532.00 KB 62
What’s cron like for programming in?