Sandboxing the PubSub framework

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 2 IconNews 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…



Avoid App Review rules by distributing outside the Mac App Store!


Get my FREE cheat sheets to help you distribute real macOS applications directly to power users.

* indicates required

When you subscribe you’ll also get programming tips, business advices, and career rants from the trenches about twice a month. I respect your e-mail privacy.

Avoid Delays and Rejections when Submitting Your App to The Store!


Follow my FREE cheat sheets to design, develop, or even amend your app to deserve its virtual shelf space in the App Store.

* indicates required

When you subscribe you’ll also get programming tips, business advices, and career rants from the trenches about twice a month. I respect your e-mail privacy.

One thought on “Sandboxing the PubSub framework

Leave a Reply to copyCancel reply