Lion’s sandboxing deadline may have been pushed back, but News Anchor came through and met the original date. Bringing News Anchor into the sandbox cost me one of the two precious DTS tickets in my quota, due to the difficulties in getting the PubSub framework to function in the environment. I’d figured I ought to write this out to contribute back to the Mac Developer community.
News Anchor is our news reader for the Mac. As a news feed reader, it relies heavily on Apple’s PubSub (Publication Subscription) framework to parse the various news feed formats that are available (mostly RSS or ATOM, but since the standards are a bit lax, various websites tend to provide their content in ever-so-slightly different formats). The application is available both at the Mac App Store and direct-download from us. The Mac App Store version has been sandboxed since release 2.4.4 and its corresponding direct-download version was released at the same time to support the changes in the MAS version. This is so that MAS customers can also use the direct-download version, achieved via a clever licensing scheme first described by Daniel Jalkut.
The challenge around sandboxing an app that uses the PubSub Framework primarily stems from the way it uses a shared database file that is shared among all applications. Fortunately, it is not system-wide — every user has her own instance of the file placed in the ~/Library
directory. Another issue is that the framework uses some mach ports for inter-process communication, but this was pretty straightforward to solve since it shows immediately as sandbox violation messages in the system log.
File access issues
When you sandbox a Mac application that uses the PubSub framework right off the bat, chances are PubSub framework couldn’t parse any RSS feed that you throw at it. Furthermore you’re likely to see these errors in your console log:
[WARNING] SQL Error: SQLITE_CANTOPEN[14.0]: Database file not found [WARNING] ***** SQL error opening database: SQLITE_CANTOPEN[14.0]: Database file not found [WARNING] SQL Error: SQLITE_CANTOPEN[14.0]: Database file not found [WARNING] SQLite::Exception "SQLITE_CANTOPEN[14.0]: Database file not found" caught in static void PubSub::ClientCore::openClientSession() [WARNING] std::exception "std::exception" caught in objc_object* -[PSFeed initWithData:URL:](PSFeed*, objc_selector*, NSData*, NSURL*)
These warning messages sounds like the PubSub framework couldn’t find the path to its shared database directory. Why? Because by being in a sandbox, the application gets its own home directory that is several levels down from the user’s own directory. Since the home folder have moved (at least from the application’s point of view) the PubSub Framework then couldn’t locate its database that is normally found in ~/Library/PubSub
and simply fail without throwing any exception on to the Objective-C side.
To regain access to the PubSub database, you will need to add read/write access entitlements to your application and add a symbolic link so that the PubSub framework can access it from within your application’s home directory. Adding an entitlement alone is not enough since the application’s view of the home directory have changed and some Unix-trickery is required to locate the file.
File entitlements
Add these read/write entitlements to your application do that it can access PubSub’s database
<key>com.apple.security.temporary-exception.files.home-relative-path.read-write</key> <array> <string>/Library/PubSub/</string> <string>/Library/PubSub/Database/</string> </array>
Symbolic Link
Adding file access entitlements only gives your application the ablility to read/write those files, but doesn’t help much on finding those files. Remember that a sandboxed application has its own home directory that is separated from the user’s normal home directory. Then how the sandboxed application can access shared files? By providing symbolic links from the sandbox to the files real positions.
Apple themselves also do this. When a sandbox directory is initialized, its Library directory is pre-populated with symbolic links of a number of important subdirectories that points to their respective true locations inside the user’s library folder. Take a look at the following sample Library folder in a sandbox:
athena:Library adib$ pwd /Users/adib/Library/Containers/com.basilsalad.newsanchor/Data/Library athena:Library adib$ ls -lah total 176 drwx------ 27 adib adib 918B Nov 12 11:58 . drwx------ 11 adib adib 374B Nov 12 11:57 .. -rw-r--r--@ 1 adib adib 12K Nov 12 11:57 .DS_Store drwx------ 5 adib adib 170B Nov 20 23:31 Application Support lrwxr-xr-x 1 adib adib 17B Nov 12 11:57 Audio -> ../../../../Audio drwx------@ 3 adib adib 102B Nov 12 11:57 Caches lrwxr-xr-x 1 adib adib 21B Nov 12 11:57 Calendars -> ../../../../Calendars lrwxr-xr-x 1 adib adib 24B Nov 12 11:57 ColorPickers -> ../../../../ColorPickers lrwxr-xr-x 1 adib adib 21B Nov 12 11:57 ColorSync -> ../../../../ColorSync lrwxr-xr-x 1 adib adib 18B Nov 12 11:57 Colors -> ../../../../Colors lrwxr-xr-x 1 adib adib 22B Nov 12 11:57 Components -> ../../../../Components lrwxr-xr-x 1 adib adib 24B Nov 12 11:57 Compositions -> ../../../../Compositions drwxr-xr-x 3 adib adib 102B Nov 20 23:48 Cookies lrwxr-xr-x 1 adib adib 21B Nov 12 11:57 Favorites -> ../../../../Favorites lrwxr-xr-x 1 adib adib 27B Nov 12 11:57 FontCollections -> ../../../../FontCollections lrwxr-xr-x 1 adib adib 17B Nov 12 11:57 Fonts -> ../../../../Fonts lrwxr-xr-x 1 adib adib 25B Nov 12 11:57 Input Methods -> ../../../../Input Methods lrwxr-xr-x 1 adib adib 23B Nov 12 11:57 KeyBindings -> ../../../../KeyBindings lrwxr-xr-x 1 adib adib 28B Nov 12 11:57 Keyboard Layouts -> ../../../../Keyboard Layouts lrwxr-xr-x 1 adib adib 21B Nov 12 11:57 Keychains -> ../../../../Keychains drwx------ 2 adib adib 68B Nov 12 11:57 Logs drwx------ 24 adib adib 816B Nov 20 23:35 Preferences lrwxr-xr-x 1 adib adib 21B Nov 12 11:57 QuickLook -> ../../../../QuickLook drwx------ 2 adib adib 68B Nov 20 23:48 Saved Application State lrwxr-xr-x 1 adib adib 18B Nov 12 11:57 Sounds -> ../../../../Sounds lrwxr-xr-x 1 adib adib 20B Nov 12 11:57 Spelling -> ../../../../Spelling athena:Library adib$
From the above example, you can see for yourself that these subdirectories are links to their real ones in ~/Library
four levels up. Unfortunately adding a directory entitlements for additional subdirectories inside ~/Library
does not imply that the necesssary symbolic links will be created for you, at least not as of Mac OS X 10.7.2. You’ll need to create those links yourself via the symlink()
function call.
Fortunately for linking to PubSub’s database folder you can use this function that I’ve written originally for News Anchor:
bool BSCreatePubSubSymlink() { NSString* libraryDirectory = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject]; NSString* pubSubDirectory = [libraryDirectory stringByAppendingPathComponent:@"PubSub"]; NSString* pubSubTarget = @"../../../../PubSub"; NSFileManager* fileManager = [NSFileManager defaultManager]; BOOL isDirectory = NO; NSString* realPubSubDirectory = [libraryDirectory stringByAppendingPathComponent:pubSubTarget]; if ([fileManager fileExistsAtPath:realPubSubDirectory isDirectory:&isDirectory]) { if (isDirectory) { int success = symlink([pubSubTarget UTF8String], [pubSubDirectory UTF8String]); if (success != 0) { if (errno != EEXIST) { return NO; } } } } return YES; }
You should find it easy to adapt the function above to create symbolic links for accessing other shared files inside ~/Library
.
Mach Ports
Last but not least you should add IPC exceptions needed by the PubSub framework. You probably saw these sandbox violation messages in the console log the moment you enable sandboxing for your app:
sandboxd: deny mach-lookup com.apple.pubsub.notification
To solve this, simply add these two exceptions to your application’s entitlements:
<key>com.apple.security.temporary-exception.mach-lookup.local-name</key> <array> <string>com.apple.pubsub.notification</string> </array> <key>com.apple.security.temporary-exception.mach-lookup.global-name</key> <array> <string>com.apple.pubsub.ipc</string> </array>
The first entitlement I found out myself whereas the second security exception for com.apple.pubsub.ipc came from the DTS ticket resolution. Since it came from Apple, it’s probably wise to just include the exception.
Sample Project
You can download the sample project here. This contains the BSCreatePubSubSymlink()
function as well as all the entitlements needed to get PubSub ready to parse your news feeds.
So thats about it. Apple may be too rushing to enforce sandboxing to the Mac App Store, with brings up issues such as this one and probably many more that others have experienced. But as Mac devs, especially indie devs, we should band together and do our best for everyone involved in the ecosystem, users and devs alike.
Thanks for reading and until next time…
Thank you! Apple hasn’t fixed this and OS X 10.9 is on its way…