The Launchd Nightmare

My Mac application, MailSteward, has always had a feature allowing the user to schedule the app to archive their email in a database. To do this MailSteward uses an old standby in the UNIX system, called crontab, which has been around since the beginning of time, i.e., the creation of UNIX.

Now, running the beta of Mojave, MacOS 10.14, it doesn’t work anymore. So I am switching over to launchd, the Apple system for scheduling daemons and agents. What a nightmare! The crontab is simple and elegant. Launchd is incredibly complicated and has some of the worst documentation I have ever seen.

If you google something like “macOS cocoa schedule application with launchd”, you will get a list of links to very frustrated developers. I never was able to find a clear explanation of how the Hell to do it. There were a few quite confident how tos, but they used old commands that Apple has since deprecated and which don’t work anymore.

So I had to use the traditional computer science technique, trial and error. It only took a couple of days. Here is the code it takes to set up a simple schedule in launchd:

– (IBAction)schedule:(id)sender {
int rc;
NSRange aRange;
NSTask *task1;
NSTask *task2;
NSTask *task3;
NSTask *task4;
NSPipe *stdOutPipe = [ NSPipe pipe ];
NSFileHandle *stdOutReadHandle = [ stdOutPipe fileHandleForReading ];
NSMutableData *idData;
NSMutableArray *args = [ NSMutableArray array ];
NSFileManager *manager = [NSFileManager defaultManager];
NSMutableString *idString = [NSMutableString string];
NSMutableString *guiString = [NSMutableString string];
NSMutableString *dictAll = [NSMutableString string];
NSMutableString *dictWeekday = [NSMutableString string];
NSMutableString *junk = [NSMutableString string];
NSString *plistPath = [[NSBundle mainBundle] pathForResource:@”ms.schedule” ofType:@”plist”];
NSMutableString *plistFile = [NSMutableString stringWithContentsOfFile:plistPath encoding:NSUTF8StringEncoding error:NULL];
[dictAll setString:@”\nHour\n8\nMinute\n45\n\n”];
[dictWeekday setString:@”\nHour\n8\nMinute\n45\nWeekday\n1\n\n”];

[self savePrefButton:self];
[self getPrefs];

task1 = [[NSTask alloc] init];
[args removeAllObjects];
[args addObject:@”-u”];
[ task1 setCurrentDirectoryPath: @”.” ];
[ task1 setLaunchPath:@”/usr/bin/id”];
[ task1 setArguments: args ];
[ task1 setStandardOutput: stdOutPipe ];
[ task1 launch ];
[ task1 waitUntilExit ];
idData = [ [ NSMutableData alloc ] initWithData :[ stdOutReadHandle readDataToEndOfFile ] ];
[stdOutReadHandle closeFile];
idString = [[ NSMutableString alloc ] initWithData: idData encoding: [ NSString defaultCStringEncoding ]];
[task1 release];

if ( [schedAll state] ) {
aRange = [plistFile rangeOfString:@”StartCalendarInterval“];
aRange.location += 1 + aRange.length;
[plistFile insertString:dictAll atIndex:aRange.location];
[junk setString:@”Hour\n“];
[junk appendString:[NSString stringWithFormat:@”%d”,schedHour]];
rc = [plistFile replaceOccurrencesOfString:@”Hour\n8″ withString:junk options:NSBackwardsSearch range:NSMakeRange(0, [plistFile length])];
[junk setString:@”Minute\n“];
[junk appendString:[NSString stringWithFormat:@”%d”,schedMin]];
rc = [plistFile replaceOccurrencesOfString:@”Minute\n45″ withString:junk options:NSBackwardsSearch range:NSMakeRange(0, [plistFile length])];
//NSLog(@”plistFile = %@”,plistFile);
} else {

}
[junk setString:@”~/Library/LaunchAgents/mailsteward.schedule.plist”];
[junk setString:[junk stringByExpandingTildeInPath]];
if ( [manager fileExistsAtPath:junk] ) {
[manager removeItemAtPath:junk error:nil];
}
[manager createFileAtPath:junk contents:[NSData dataWithBytes:[plistFile UTF8String] length:[plistFile length]] attributes:nil];

task4 = [[NSTask alloc] init];
[guiString setString:@”gui/”];
[guiString appendString:idString];
[args removeAllObjects];
[args addObject:@”bootout”];
[args addObject:guiString];
[args addObject:junk];
[task4 setLaunchPath:launchctlCommand];
[task4 setArguments:args];
[task4 launch];
[task4 waitUntilExit];
[task4 release];

task2 = [[NSTask alloc] init];
[guiString setString:@”gui/”];
[guiString appendString:idString];
[args removeAllObjects];
[args addObject:@”bootstrap”];
[args addObject:guiString];
[args addObject:junk];
[task2 setLaunchPath:launchctlCommand];
[task2 setArguments:args];
[task2 launch];
[task2 waitUntilExit];
[task2 release];

[guiString appendString:@”/mailsteward.schedule”];
task3 = [[NSTask alloc] init];
[args removeAllObjects];
[args addObject:@”enable”];
[args addObject:guiString];
[task3 setLaunchPath:launchctlCommand];
[task3 setArguments:args];
[task3 launch];
[task3 waitUntilExit];
[task3 release];

}

Brains Are Doing It for Themselves

This is an interesting article about Google, about it’s a great place to work, so why are so many people leaving? To paraphrase the Eurythmics and Aretha, brains are doing it for themselves. I was just thinking the other day that there must be a huge brain drain happening at Microsoft. The days of becoming a millionaire from stock options are over. Microsoft is not doing anything exciting or interesting. The original smart people have all already cashed out. Microsoft has a terrible reputation among the cognoscenti. Why would anyone want to work there? They’ve always hired people mostly straight out of college with no experience, at low wages, who join for the cachet and the stock options. But there is no cachet or stock options anymore. They must be getting the Computer Science dregs these days.

Google is another matter of course. They may be slightly on the downside of the glory days, but they are still a happening company. However, the internet, and Google for that matter, are bringing about the age of the individual entrepreneur. Being one myself, I can say that all of the stresses and sacrifices of being on one’s own and trying to start a business from scratch, are made up for by not having to put up with jerks and fools. I lament the many years I spent taking orders from idiots and making other people rich.

These guys who are leaving Google are leaving with vastly more money than I have ever had, access to venture capital, youth, and considerably more technical chops. Of course they’re leaving! I’m very happy being out here on my own, but I only did it because I had no other choice, and after two years of no income, I make a middle-class living. The ex-Googlers have options I’ve never dreamed of.

As the future unfolds, this is going to be a problem for every company that succeeds and becomes huge as a result. They will lose all their brains, the only capital asset that matters anymore, become stupid, and die. Microsoft is now in a death spiral, albeit a very slow one. Google is not there yet, but the end is inevitable. Not that Google will disappear. They will become a utility. Everyone who has played Monopoly knows what the utilities are worth. Microsoft, on the other hand, will just go under.

Apple is somewhat of an anomaly. It is really an example of an individual entrepreneur. It is Steve Jobs’ baby. You can still get rich on options there, and it is exciting, and has plenty of cachet, but without Steve Jobs Apple is dead meat. Apple stock is my entire retirement plan, and so far it is working out nicely. As long as Steve Jobs is alive, Apple will continue to take market share from Microsoft until it is all gone. They will also become the dominant player in music, TV, and movies. At that point, they may become a utility, but I will have already retired.

Actually Macs are Cheaper than PCs

It is pretty generally accepted that, no matter how superior they may be, Macintosh computers are significantly more expensive than Windows PCs. Just out of curiosity, I decided to do a little comparison shopping. Apple just upgraded the iMac today, so I compared the price of the entry-level iMac with a comparable computer from DELL. Here are the specs:

Dell XPS One

  • 20 inch widescreen display with Intel® Core™2 Duo 2.2GHz
  • Adobe Elements Studio for XPS™ One
  • XPS One® Wireless Keyboard
  • XPS One® Wireless Mouse
  • 1Yr In-Home Service, Parts Labor, 24×7 Phone Support
  • Included 3 GB DataSafe Online Backup for 1Yr
  • Genuine Windows Vista® Home Premium
  • Norton Internet Security™ 2007 Edition 15-months
  • Microsoft Works 8.5 – Does not include Microsoft Word
  • 2GB Dual Channel DDR2 SDRAM at 667MHz – 2 DIMMs
  • 250GB Serial ATA 3Gb/s Hard Drive (7200RPM) w/DataBurst Cache™
  • 8-in-1 Media Reader Included
  • Internal PCI 802.11 a/b/g/n wireless network card included
  • 8X Slot load CD/DVD burner (DVD+/-RW)
  • Integrated Audio – 2.0 Speakers
  • Hybrid Analog/Digital TV Tuner with Remote Control
  • Internal Bluetooth 2.0 Included
  • Apple iMac

  • 2.4GHz Intel Core 2 Duo
  • 1GB 800MHz DDR2 SDRAM – 1x1GB
  • 250GB Serial ATA Drive
  • Apple Mighty Mouse
  • Apple Keyboard (English) + User’s Guide
  • Accessory kit
  • Integrated Speakers
  • SuperDrive 8x (DVD±R DL/DVD±RW/CD-RW)
  • ATI Radeon HD 2400 XT with 128MB memory
  • 20-inch glossy widescreen LCD
  • AirPort Extreme
  • Bluetooth 2.1 + EDR
  • remote control
  • iLife ’08 suite
  • Front Row
  • Photo Booth
  • 1 year .mac membership
  • They are not exactly the same of course. The Dell has a wireless keyboard and mouse. The iMac doesn’t. The iMac has a built-in iSight video camera. The Dell doesn’t. The Dell has 2GB of ram, the iMac only 1GB, but then Vista needs that much more ram than OS X to perform acceptably. The Dell CPU is slightly slower than the iMac. All in all, they are pretty close.

    The price tag on the Dell is $1,299. The iMac is $1,269, $30 cheaper than the Dell. This is not taking into account any differences in longevity, productivity, or resale value, all of which favor the Mac.

    Now it’s true that Dell has cheaper computers than the XPS One, but they are not at all comparable to the iMac. Apple just doesn’t sell low end products. What is not true is that you can buy a PC comparable to a Mac for a lot less, or any less, money.

    Cocoa Sheets How To

    Sheets, of course, being those windows that drop down from the top of, and are attached to, another window. It’s pretty simple, but I had a hard time finding any documentation or Google info about it.  So here is what I finally found/figured out:

    1. First create the windows or panels in Interface Builder that will be the sheet and the window/panel that the sheet is attached to. If your sheet is a panel, set the style to “Document Modal”.

    2. Then do this in obj-c:

    [NSApp beginSheet:mySheet modalForWindow:myPanel
         modalDelegate:self didEndSelector:NULL contextInfo:nil];
    .
    .
    .
    [NSApp endSheet:mySheet];
    [mySheet orderOut:self];
    [mySheet performClose:self];

    I’m not sure if that last performClose statement is needed, but it can’t hurt.

    blog moving

    I have moved the mailsteward.com domain to a new provider, which means I have to build this blog back from scratch. So it’s gonna take a little while before all the posts are back up, and the look has been customized. And then I promise (myself) to post more often.

    OS X 10.4.4 update breaks phpMyAdmin

    If you are running MySQL and phpMyAdmin on Mac OS X, and you just upgraded to OS X 10.4.4, you may have seen this error message when trying to access your MySQL server using phpMyAdmin: “The server is not responding (or the local MySQL server’s socket is not correctly configured)”. If this has happened to you, here is the fix:

    1. load /private/etc/php.ini into your favorite editor. You may have to become root and change permissions on the file in order to edit it.

    2. find the line that says ‘mysql.default_socket = ‘.

    3. change that line to say ‘mysql.default_socket = “/tmp/mysql.sock”‘, and save php.ini.

    4. as root, on the command line in a terminal window, restart the Apache webserver by typing ‘apachectl graceful’.

    That should do it.

    Taming the West

    Computer languages are designed at a high level of abstraction, and have a relatively short list of rules. Learn the rules and start programming. Frameworks and class libraries, like Cocoa, on the other hand, are very different. They are designed to do a set of specific jobs. How they are designed and implemented will be different depending on what mind collective does the design and implementation. Every group, and individual within the group, will approach it differently. There is no short list of rules. Each object and each of its methods is a set of rules unto itself. Of course there is a great deal of consistency to how things are done in Cocoa and any well-designed library, but it can’t be relied upon. One has to learn each object and each method, one by one.

    I think Cocoa is great, but it necessarily has a huge learning curve, compared to learning a computer language. I have programmed in a number of different computer languages, IBM 360 Basic Assembler Language, COBOL, BASIC, PL-1, Forth, Perl, PHP, C, C++, and now Objective C. C was my favorite, but now it’s ObjC. It’s a lovely implementation of objects in C. My least favorite language is C++, which is an abomination, in my opinion. But that is all beside the point. Developing software for the Macintosh is not about Objective C. It’s about Cocoa.

    Although I appreciate the advantages of object-oriented programming, and I have done and continue to do a lot of it, I have never liked the feeling of being trapped inside somebody else’s idea of how to do things. If you want to develop a Cocoa app, you have to do it the Cocoa way. No matter how beautifully a library is done, it never quite works the way I want it to. It never quite does all the things that I need to do. The only alternative is to write your own set of classes and use them instead of Cocoa, a more than daunting task. These days, as an application programmer in the modern programming world, you really have no choice. Like Dylan says, “You got to serve somebody”, whether its Cocoa or Carbon, the Windows API, or Gnome, the Devil or the Lord, or any class library or API. You have to do things the way somebody else decided you should do them.

    I’m not complaining. I like Cocoa quite a lot, and of course you can always create your own classes that inherit from Cocoa and do whatever you need to do. I’m just saying there’s been a real sea change in the nature of application programming, because of the now ubiquitous practice of object-oriented development. There is more power, but also more restriction. In the olden times, whatever language I was using, or type of application I was developing, I would gradually build up a repository of semi-reusable code that could be cut and pasted into whatever I was currently developing. As time went by there was more and more cutting and pasting and less and less original coding. And everything worked the way I wanted it to, and if it didn’t, I knew how to fix it.

    So something is gained and something is lost. I’m sure that assembly language programmers felt the same way about the advent of high level languages, starting with Fortran. In fact that’s one of the things I liked so much about C. It’s really more of a meta-assembler than a high level language. It doesn’t restrict you. It just speeds up your coding, whereas COBOL puts you in the COBOL box. So, what’s the point? No point, just some thoughts from an old guy who’s still programming. I think it’s analagous to the history of a civilization. Over time, everything gets more and more defined, and the original anarchy becomes confined and restricted. It’s the settlement of the Old West. You can’t just shoot anybody you want to anymore, but, as compensation, the streets are paved, and somebody collects the garbage.

    Cocoa custom popup/pulldown buttons

    If you don’t like the standard popup/pulldown button and would rather have a cool custom button for your popup and pulldown menus, like this:

    Then here’s a neat little trick for doing just that. In Interface Builder, first drag a standard NSPopupButton from the palette, and specify it as ‘hidden’. After you’ve added the menu items that you want, drag over an NSButton and add your custom icon to it. Then control-click drag from the NSButton to the NSPopupButton and connect it to the ‘performClick’ target/action. In Interface Builder your buttons will look like the ones below:

    And that’s all there is to it.

    podcasting is about to happen

    There’s a brilliant article by Rex Hammock over at rexblog.com entitled How Apple will change everything about Podcasting, #2 — How much could Howard Stern make podcasting via iTunes vs. broadcasting via Sirius?. The world of podcasting is about to leap out of the its small pond and into the ocean, or at least the Great Lakes, via Apple’s imminent integration of podcasting into the Apple iTunes Music Store. Disclosure: Rex uses my wife Candace’s podcast, The Nashville Nobody Knows, throughout the article, which only adds to the brilliance of the piece.

    blognashville

    Candace and I had a great time at Blognashville this weekend. It was very exciting actually. Attended by many blogosphere luminaries including, Glenn Reynolds, Dave Winer (creator of the RSS feed), Mark Glaser (main writer for Online Journalism Review), Bill Hobbs, Mark Tapscott (Director of Media and Public Policy at the Heritage Foundation), Dan Gillmor, Ed Cone, Henry Copeland of Blogads, and on and on. A bunch of very geeky, very smart, passionate, adventurous loudmouths. Candace got to say her piece at the podcasting discussion. We both did, but mostly her. And we got to hang out and drink wine at length with Mark Glaser and with Dave Winer, and had a great time talking to Brendan Greely, Mike Kelley, Jim & Lynnette Walczak, Mick Wright, Doug Petch, Nick Levay, and many others . It felt like the old days in what is now Silicon Valley, when microcomputers were young and still in the hands of the community of aficionados. It had that same nerds on acid buzz. Not to imply that anyone there really was on acid.