Failing Miserably, If Not Inventivelyby Kevin Hemenway
I am, by other people's definitions, an obsessively and dangerously organized power user. I co-wrote Mac OS X Hacks (for making your computer jump through hoops), the recently released Spidering Hacks (for repurposing data scraped from the Web), and have been listed as an example for Danny O'Brien's Life Hacks: Tech Secrets of Overprolific Alpha Geeks at O'Reilly's next Emerging Tech conference.
When something hits me, I'll drop everything I'm behind deadline on and spend 20 hours automating a task that takes five manual minutes; I know I'll eventually recoup the benefits months down the road, after I've long forgotten the automation exists ("you only notice electricity when it's missing"). I've automated iTunes album listings, video file annotations with AppleScript and Perl, have tried numerous todo and schedule outlines, and generally enjoy having my computer be far more "robot" than those of 90% of the populace.
This is a tale of how Panther broke my automation and how, with a few days of disjointed searching, experimentation, and dreaming, I didn't fix the problem. Instead, we simply follow one man's obsession as he makes steadily more-desperate attempts to scratch a bothersome itch.
Slight Changes Have Big Repercussions
Let me tell you of a normal weekday in regards to my email processing. Every morning at 7:06 AM, my alarm clock goes off. As I stumble out of bed and plunk down at my desk, Eudora has already downloaded my morning email, having been
opened via a
cron entry at 7:00 AM. At 6:08 PM, when I, on average, get home from work, my email is again waiting for me, having been automated at 6:02.
On weekends, when I'm rarely online, I "fly-by check" my email on a regular basis. Since my computer room is on the way to the bathroom, I've configured Eudora to launch whenever I press the F3 key. If I haven't checked my email in a while, I'll F3 without stopping, and by the time I've finished my business, new email is ready for a quick scanning.
I have dozens of tasks similarly automated; backups, list creation, email newsletters, RSS aggregation, web site maintenance, system reports, you name it. If it can be automated, it either already is, or I've been laying the groundwork to do so. Easily, a dozen or more items were added to my daily and weekly regiment during my work on Spidering Hacks.
Nearly half of my automation requires an active Internet connection and, as I'm religiously devoted to dial-up, the "Connect automatically when needed" checkbox of the Network System Preference (Figure 1) is a prerequisite. Under Jaguar, everything worked beautifully. Under Panther, things broke down nearly immediately, and without clear explanation.
Figure 1: "Connect automatically when needed" is checked
Regardless of whether I used an internal or external modem, with or without Airport enabled, things were always the same: "Connect automatically" just didn't. Oh sure, it'd put up a good fight: restart the machine and it'd connect at the first opportunity, but no further luck until the next reboot. It got to the point where I automated an Internet-connecting AppleScript before launching Eudora or related
tell application "Internet Connect" set configName to "name of your connecting device" set currentStatus to status of configuration configName connect configuration configName end tell
This quickly proved unwieldy: occasionally, something ... "odd" would happen, and the Finder menu bar would disappear when the script launched Internet Connect. The bar would come back nearly immediately, but not perfectly: my previously connected Airport network icon would show as disconnected, and nothing besides a reboot would return it to proper operation. Likewise, this solution would only satisfy anticipation: it could never be triggered when my SETI@home was ready to get a new work unit.
At this point, my problem became the Single. Greatest. Issue. Ever. It was so disruptive of the way I worked, and of the way I expected my computer to work, that everything else became dull noise -- this needed to be fixed immediately and without respite. I began looking around for other people reporting the same problem.
I eventually ended up at Apple's 10.3 discussion boards where the explanation screamed out from the FAQ. In it, the death knoll: when you cancel a PPP connection attempt when the modem is dialing, or you manually disconnect after an automatic connection, the "connect automatically" feature will be disabled until you "restart the computer, put it to sleep and wake it, or change your network location."
I can remember, quite clearly, being shocked and thinking how this made absolutely no sense. If the user doesn't want his or her computer to dial automatically, unclick the blasted "Connect Automatically" box! When I want my computer to connect automatically to perform a task, I ... want it ... to ... connect ... automatically to perform said task! Just because I disconnect when said task is finished doesn't invalidate my previous request: CONNECT AUTOMATICALLY WHEN NEEDED!
Help! Apple broke my robot! This sucks.
Accepting "Pfffttt!" For An Answer
I couldn't accept this at face value: I resolved to make Panther work like Jaguar, and to do it without a lot of effort or annoying disruptions to my regular workflow. My answer, it seemed, lay with the possible solutions in the support document: "restart the computer, put it to sleep and wake it, or change your network location."
My first approach was to run something every hour, which fixed the problem somehow. This immediately ruled out restarting the computer or putting it to sleep and waking it: if I was working on the machine at the toll of the cuckoo, I'd be rather rudely interrupted. I could certainly check to see if I was currently connected, but that incorrectly presumes that when I'm working, I'm online. if I was dancing to the open source StepMania, my game would be over.
The only other alternative was to "change your network location," and I descended on this with anxious fury. AppleScript-wise, I was already halfway there, as Panther ships a sample script for doing just that (shown below truncated; the full version is located in /Library/Scripts/UI Element Scripts/Set Network Location.applescript):
tell application "Finder" activate end tell tell application "System Events" click menu item "Automatic" of menu "Location" of menu item "Location" of menu "Apple" of menu bar 1 of process "Finder" end tell
All I'd have to do would be to create a duplicate network location (called "Automatic2"), and then set that location every odd hour and the default location ("Automatic") every even hour. There was one problem (there's always "one problem"): if you're currently connected, changing the network location disconnects you. Sigh. This wasn't ideal, either: some of my weekly backups take more than an hour, and they'll fail if the 'Net disappears from underneath them.
At this point, I returned to the possibility of pre-checking to see if I was already connected: if I'm connected, don't change the network location, if I'm not, have a blast. I didn't follow this path: I was getting steadily disenchanted with the idea of running something every hour when it'd only come in handy two or three times a day. In retrospect, I suspect I'll have to reopen this line of thinking.
I also had a cinder of an idea, quite literally sparked from a dream the night before. Problems consume me, and it's pretty regularly that my subconscious solves them. I've come to rely on my unknown instincts: it's helped me through numerous video-game puzzles, proper approaches to writing about difficult topics, and epiphanies concerning technical or coding issues. Sometimes, I'm smarter than myself.
Getting Darwin Involved
As most of us know, Apple's pretty GUI is built on top of Darwin, a full-fledged Linux-like operating system. My day-to-day development and sysadmin work pretty much assures that I have a Terminal open all the time, along with a bevy of shell aliases that I use regularly. One of them,
p, expands to
ps aux | more, which shows me a list of all the processes currently running on the machine. I distinctly dreamed
pppd in that list, something I probably wouldn't have noticed unless I absolutely needed to.
Sure enough, when I manually connected and checked the process list the next morning, I saw
pppd in the list. I didn't know where I was heading, but I called for the manual (
man pppd), and was greeted with the spirit guide my dream surely intended:
/etc/ppp/ip-up: A program or script which is executed when the link is available for sending and receiving IP packets (that is, IPCP has come up). It is executed with the parameters: interface-name, tty-device, speed, local-IP-address, remote-IP-address, ipparam.
/etc/ppp/ip-down: A program or script which is executed when the link is no longer available for sending and receiving IP packets. This script can be used for undoing the effects of the /etc/ppp/ip-up script. It is invoked in the same manner and with the same parameters as the ip-up script.
Plainly put, if I could rig up a /etc/ppp/ip-down script that changed the network location whenever I disconnected my dial-up connection, the "connect automatically when needed" feature would be "reset," ready to work correctly the next time it was needed. The only thing required would be to modify the previous Apple-supplied AppleScript to first find out which network location was currently enabled, and then choose the other.
I never got that far. See, there was one problem.
In testing, every time I changed the network location, it would not only disconnect the current connection (if any), but would immediately redial into the new network location as a side effect. I think this is related to Apple's
lookupd daemon and the resetting of hostnames, but I didn't look into it 100%. This happens whether I use the above AppleScript, or a rather handy command-line network switcher called
scselect. (No manual is available for it, but run it once to see a list of configured networks, and pass the network you'd like to enable as it's only argument.)
Getting more and more frustrated, I decided that if I uncheck the "Connect Automatically" checkbox before I change the network location, and then turn it back on after the change occurred, things would be perfectly ducky. I ended up with the following AppleScript, which uses the new GUI Scripting plug-in (enabled by default in Panther). If you've never used it before, I heartily recommend the PreFab UI Browser from PreFab Software, Inc.
tell application "System Preferences" set current pane to pane "com.apple.preference.network" end tell tell application "System Events" tell application process "System Preferences" click button "Configure..." of group 1 of window "Network" click button "PPP Options..." of group 1 of tab group 1 ¬ of group 1 of window "Network" click checkbox "Connect automatically when needed" of ¬ sheet 1 of window "Network" click button "Ok" of sheet 1 of window "Network" click button "Apply Now" of window "Network" end tell end tell quit application "System Preferences"
Don't bother using the above as anything but an example: it doesn't solve the problem. Once the AppleScript "clicks" the "Apply Now" button of the Network preference pane, an "ohmygod, the network has changed!" message gets sent around the OS,
lookupd is reinitialized, and a dial-up connection immediately initiates. Exactly the same as changing a network location, and certainly not what I was looking for.
At this point, any sane person would have given up.
The Brute-Forcing and the Hulking Sulk
At this point, we've straddled the line of our initial problem: the connection doesn't connect when we want it to, and our solutions have caused the connection to connect when we don't. We've either got nothing whatsoever or far too much. Not good, not good. I was steadily running out of good ideas, and my last attempt failed due to, I suspect, ignorance and pathetic inspiration.
The goal, ultimately, was to work around the workaround. I had numerous solutions that would work, all based around changing the network location. if only I could get the immediate redialing to stop, I'd be golden grahams. I knew that every time a network change occurred, a dependable set of lines would be spit into /var/log/system.log. If I could configure
pppd to dial only when those lines weren't nearby (say, within the last ten log entries), I'd be set. I began writing a replacement
pppd which would call the real
pppd when necessary.
As the article title suggests, I failed miserably. Reading the logfile was easy and determining what to do next was simplistic, but I couldn't properly determine how to pass the request to the real
pppd daemon for further operation.
As with most seemingly perplexing personal problems, they'll be one of three typical reader responses: "Uh, wow, you overcomplicated that. Press Alt-WORK, dude" or "AHAHAHA. DIAL-UP?! I HOLD MY SIDES IN CONTEMPT!" or even "Welp, that's the right behavior. It was broken in Jaguar." After talking with some folks at Apple (in response to my bug report stressing doomsday with failed critical software and virus definition updates, etc.), it appears that the current approach is in response to an overly aggressive connection bug in Jaguar, one which I've never personally experienced.
No pain is greater than our own, indeed.
Kevin Hemenway is the coauthor of Mac OS X Hacks, author of Spidering Hacks, and the alter ego of the pervasively strange Morbus Iff, creator of disobey.com, which bills itself as "content for the discontented."
Return to the Mac DevCenter