I am working on a Finder Sync Extension for OS X and want to use a background XPC service.
I can start in the main app and have it launch the XPC and run correctly but nothing happens when I attempt to access it from the Finder Sync. both the finder sync and the XPC are their own bundles so that may be the reason why. What I am wanting is for the finder sync to talk to the XPC about the status of the files and the main app to talk to both the finder sync and XPC about the list of folders to watch.
Has anyone had any luck with this? Is there a better way for a on demand background service? Is it possible to talk between two XPC services?
Working with some Apple Engineers they realized this was a problem and suggested using a LoginItem until a better solution is in place.
So, it is sort of an XPC service, just one that constantly runs. XPC communication is available to both extension and host app.
It works, although it is not the most ideal solution. I recommend the apple sample project that deals with XPC login items for an example of how to get this working.
I implemented MainApp <-> FinderSyncExtension communication via CFMessagePorts. See my question and answer for some details:
How should Finder Sync Extension and Main App communicate?
You can't communicate directly between the container application and the extension, but you can do it indirectly using shared resources. I did exactly what you have done which is completely incorrect. I hope you store the file status in the database, if not store it and then share the database between the container application and the extension. I know, why do you want to use XPCService as it is in the Apple's FinderSync Doc. (Actually for the performance reason, Create a NSXPCService to the extension and from the XPCService, access the shared database)
For more information about sharing database:
http://blog.sam-oakley.co.uk/post/92323630293/sharing-core-data-between-app-and-extension-in-ios-8
Hope this helps you,
I had stubbornly ignored utahwithak's answer and tried to get it to work anyway. I eventually had to ask a similar question on Apple Developer Forums and finally received a definitive answer on why connecting the Finder Sync Extension to an embedded XPC service is not a viable system design.
Essentially:
Finder Sync Extension essentially behaves like a third party app in that it does not have the same scope as the host app to be able to establish an XPC connection with the embedded XPC service.
utahwithak's answer is correct in that in order to allow the Finder Sync Extension to communicate with the XPC service, it needs to be an XPC login item. However there are some caveats to this:
This seems to be an accidental feature. Not sure if it's something that might eventually be fixed/removed
The XPC will have to be always running even if it doesn't need to, by virtue of being an login item
If it's a login item, the user will need to explicitly opt in for this feature and be able to opt out.
Source:
Establishing an NSXPCConnection from a Finder Sync Extension to an XPC Service
Related
I have an application that spawns subprocesses. These subprocesses require XPCServices, that are in the same application bundle.
My main application has its executable in
my.app/Contents/MacOS/my
My XPC service sits in my.app/Contents/XPCServices/com.my.service.xpc
When the subprocess resides in my.app/Contents/Resources/mysubprocess, and the application is being launched, my subprocess can't connect to the XPC service (why not?), but it doesn't show up in the Dock.
If on the other hand the subprocess is in my.app/Contents/MacOS/mysubprocess the subprocess successfully connects to my XPC service (the main application doesn't need anything from this XPC service), the subprocess suddenly gets an own bouncing icon in the Dock. I guess OS X detects if something is being launched from within *.app/Contents/MacOS/* and sees it as application.
I obviously need it to work that way, that the subprocess can connect to the XPC service, but that the subprocess stays hidden and doesn't appear in the Dock. I've tried registering LSUIElement and LSBackgroundOnly at runtime to my user defaults, but that didn't do the trick. If i write LSUIElement into the user defaults, my main application doesn't get a main menu, which is also undesired (but in case of LSUIElement being NO absolutely correct behaviour).
Basically I have two questions:
When I move my subprocess binary outside of my.app/Contents/MacOS/ it can't locate the XPCServices. I find that kinda odd, because the relative path to the service stays the same, if the subprocess would be in my.app/Contents/Resources/. I also checked [NSBundle mainBundle] while debugging my subprocess and it had a valid path, even when it was in the Resources folder. Is there a way to somehow tell my subprocess where it should look for my XPC service?
The other approach would be that I can prevent the subprocess of bouncing in the Dock. My main application needs its icon and menu. So is there a way to specify at runtime that the subprocess won't launch a Dock icon, even if it's in the my.app/Contents/MacOS/ folder?
Thank You
According to the Apple Developer docs, XPC services that you as a developer can write must reside in your app’s bundle in My.app/Contents/XPC Services. Your app can only connect to XPC services that reside there and those XPC services can only be connected to from the app whose bundle they reside in.
(Note that the XPC services Apple ships in its system frameworks work a bit differently: /System/Library/PrivateFrameworks/WebKit2.framework contains XPC services that are used by any process that use the WebKit2 framework. But then again, the framework connects to that XPC service, not your app itself.)
That is probably the reason why you can’t connect to the XPC service if your subprocess’ binary is not in My.app/Contents/MacOS/. I’m not sure, but that you can connect to the XPC service from an arbitrary binary that resides in MacOS almost sounds like a bug. But I think it’s not because XPC services only work if the code signature is OK and placing an arbitrary binary in the MacOS directory would break that code signature.
As for the dock icon: How are you spawning the subprocess? Using NSTask? Does the regular app itself use the XPC service? If not, is there any reason why the subprocess isn’t an app itself? That way the XPC service could be inside the subprocess’ /Contents/XPC Services directory and everything should work OK.
Edit: Another solution that comes to mind: Don’t use XPC, but a separate process that talks via Distributed Objects to your subprocesses. Because that doesn’t require any special folder structure or something like that, you can place all auxiliary binaries wherever you want and can therefore avoid the Dock icon bouncing issue.
I am trying to install a privileged helper tool to perform some elevated work. I am using SMJobBless for the same.
I am able to install the tool fine and also able to communicate with it. I am using Mac OS X 10.8.4 and using NSXPCConnection for the same.
I have added .mach service in the plist which will be installed in /Library/LaunchDaemons. I am using [initWithMachServiceName:options:] in the app as the helper is privileged tool and [– initWithMachServiceName:] in the helper to listen. The communication is working fine.
But the problem is I tried the same communication with another application I created which did not have any codesign at all (the helper tool installer earlier was codesigned). I tried to connect to the mach service of the helper tool and was able to connect easily. This is a problem because anybody can communicate with it then and make it do anything.
I wanted some way to securely communicate between my application and the helper tool.
Thanks a lot.
As you've said that you're not signing the second app, I believe that that is the problem that is allowing a 2nd app from calling the helper application. From the Apple docs and specifically the ReadMe file in SMJobBless, it states: -
The Service Management framework uses code signatures to ensure that the helper tool is the one expected to be run by the main application
This document should be able to assist you in getting the helper app correctly associated with its owner.
Note that it references a python script, which is provided here.
Answering my own question: I had logged a radar bug for the same and Apple said that the behavior was intended:
"It is up to the privileged helper to not expose insecure operations"
I have created an application that exposes a OSX service for certain file by adding an NSService entry into my applications info.plist (as in http://www.macosxautomation.com/services/learn/), but I find that upon installing my application on a new machine the service doesn't show up quickly in the finder right click context menu.
I know that this is because pasteboard services hasn't re-indexed the /Applications folder and "discovered" the newly installed service.
I also know that I can force a re-index and discovery by manually running /System/Library/CoreServices/pbs.
The question here is what is the best way to ensure that my service shows up as quickly as possible for users who are installing my application for the first time.
I could execute a system call to "/System/Library/CoreServices/pbs" when my application starts up --If the user immediately starts my application--, but that only partly solves the problem (in addition I wonder if there is a better Cocoa API based way of doing this).
If my application is generally only accessed via the context menu, a user will never think to go out and start the application in the first place. They will only think it is broken when the context menu isn't there.
I am not distributing my application with an installer. I am simply providing a bundle that can be dragged and dropped into /Applications (as I believe Apple usually suggests).
Is there a way to expedite the process of service discovery when doing an installation in this fashion, so that there isn't any period of time where the user is without the newly installed service?
As a side note, it appears that the problem may not exist in 10.8 (or at least be as pronounced). Apple may have made this indexing happen more quickly in their most recent release.
I've actually ended up using
system("killall pbs;/System/Library/CoreServices/pbs -flush");
in one of my apps, just as you describe, though it's a long time ago, when 10.5 was in question as well.
You might want to try this function, however:
void NSUpdateDynamicServices(void)
which according to the documentation acts just like flushing pbs, but is a cleaner solution.
Also, if (according to your description), the app is nothing but a service, consider making it a really just a service - see (Installing the Service)
To build a standalone service, use the extension .service and store it in Library/Services.
Even though the API has been open since Mac OS X Leopard, there's surprisingly, and unfortunately, very little documentation on how to correctly use SMJobBless() for creating privileged helper tools. There are a lot of gotchas, even when copying code directly from Apple's sample project. Luckily, I've found my way around this, and have gotten the basis for my helper tool working.
However, it would seem that SMJobBless() only blesses the tool and copies it over, but doesn't run it. I've included code in my helper tool's main() function that should run, but doesn't (since NSLog() inexplicably doesn't work–according to the tiny bit of information I have found–I've tried syslog()ing some "Hello world" type strings, but nothing appears on the system console). There's no indication that the helper tool is launched at all.
The documentation is mostly useless. It simply says that after SMJobBless() is called, the helper tool is 'ready', with no indication of what 'ready' even means.
Furthermore, Apple's sample doesn't include any interprocess communication code, and doesn't explain how one is supposed to interact with the helper tool. Do you use Distributed Objects? Mach ports? Who knows? There's no official word on how to do it.
So, does anyone have any information on how to get this done? I've confirmed that the helper tool is installed, and authentication works, but I simply can't figure out how to launch the helper tool and communicate with it - there's simply such a gap in the documentation that this is a mystery for now. It's very frustrating; I can't be the only one with this problem (but there's little mention of it anywhere), and SMJobBless() obviously works somehow, since it's what Apple uses.
(Please don't mention AuthorizationExecuteWithPrivileges(). I'm not using it: it's deprecated, sure to go away, and is a major security hole. No thanks.)
XPC isn't an option if you're trying to elevate privileges (from https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingXPCServices.html):
By default, XPC services are run in the most restricted environment
possible—sandboxed with minimal filesystem access, network access, and
so on. Elevating a service’s privileges to root is not supported.
SMJobBless will install a helper tool and register it with Launchd, as in the SMJobBless example provided by Apple. The trick to getting your helper tool to actually launch is to simply attempt to connect to your helper tool's advertised services.
There was a WWDC2010 example called ssd that demonstrated a simple launchd client/server model via sockets. It's not available from Apple any longer, but I've found a link here: https://lists.apple.com/archives/macnetworkprog/2011/Jul/msg00005.html
I've incorporated the dispatch queue handling in the server code from the ssd example into the helper tool in the SMJobBless example and can confirm that my helper tool is indeed running (as root) when my main app attempts a connection on the appropriate port. See the WWDC2010 video on Launchd to understand the other mechanisms with which you can communicate with your helper tool (other than sockets).
I'm not sure I can legally redistribute the modified sources I have, but it should be fairly straightforward to merge the two projects and get your helper tool running.
Edit: Here is an example project I wrote that uses a distributed object for communication between the app and helper: https://www.dropbox.com/s/5kjl8koyqzvszrl/Elevator.zip
In fact #KurtRevis's comment is right, you can use XPC APIs without using XPC services, and it is ideally suited to the job since.
Nathan de Vries has an excellent writeup of using XPC APIs with SMJobBless and has even modified the SMJobBless sample app to use mach XPC to both activate the job and for bidirectional communications:
http://atnan.com/blog/2012/02/29/modern-privileged-helper-tools-using-smjobbless-plus-xpc/
https://github.com/atnan/SMJobBlessXPC
Somewhat related to all this is avoiding unnecessary admin password prompts. See the following email list thread for ideas on how to check if the bundle version and code signature of an already installed helper match (which also allows you to remove a higher versioned helper in the case of a user downgrading):
http://www.cocoabuilder.com/archive/cocoa/309298-question-about-smjobbless.html
If you don't want to wade through the thread, here is a link to the modified SMJobBless sample project provided by Eric Gorr:
http://ericgorr.net/cocoadev/SMJobBless.zip
Also note that the ssd example mentioned in other answers here is still available online from Apple as part of the WWDC 2010 download bundle:
http://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?code=y&source=x&bundleID=20645
Apple now (2015) has an "EvenBetterAuthorizationSample" that demonstrates installing a privileged helper tool and using the NSXPCConnection API to communicate between the app and the helper tool:
https://developer.apple.com/library/mac/samplecode/EvenBetterAuthorizationSample/Listings/Read_Me_About_EvenBetterAuthorizationSample_txt.html
The README is some of the best (only?) documentation of SMJobBless() available.
I feel your pain and am in the same boat. I'm in charge of the Mac version of an app that needs to perform various system configuration tasks. Of course some of these task need to be done with administrative rights. I started by using the sample code from BetterAuthorizationSample. It was a major pain to implement but it seemed to work. But then ran into cases where it would crash on some systems. I didn't understand everything that the BAS code did and my own lack of coding experience probably contributed to the problems. So I had to remove these privileged functions from my app.
Apple doesn't seem to care about the lack of documentation. See this message from the creator of the ServiceManagement framework. From his comments, I assume that XPC is the "intuitive replacement" he is referring to, but since it is only available on Lion, you'll still have to find another solution for Snow Leopard or earlier clients. It's also not clear to me if XPC can be used for privileged helpers (system level tasks that require admin or root access) or is just intended for privilege separation within your own app to make it more secure.
The BAS documentation is in desperate need of an update, but it also doesn't appear to be a top priority.
Now I'm attempting to rewrite my app from the ground up. Professional Cocoa Application Security by Graham Lee gives some insight on how to do use privileged helpers with SMJobBless, but doesn't go into much detail about on-demand access to launchd jobs.
So here's what I've been able to find:
If you want to launch your privileged helper on demand, you'll have to use an IPC socket. You should add a Sockets entry to your helper's launchd.plist. After you install the app with SMJobBless, the helper will need to "check-in" with launchd (via LAUNCH_KEY_CHECKIN) to get the socket file descriptors.
Sadly, the only mentions of LAUNCH_KEY_CHECKIN seem to be in the SampleD and BAS sample code.
I don't have any experience with sockets, so that's my roadblock at the moment. I'd like to use the highest level API I can, so I'm trying to find out if I can use any Objective-C classes for this (like NSStream).
You might find the launchd developers mailing list helpful. Another XPC option I just found out about is XPCKit. It's worth a look.
HTH
I wrote a blog post on this a few months ago, which included a cleaned up version of Apple's SMJobBless sample. Might help...
http://www.bornsleepy.com/bornsleepy/os-x-helper-applications
Itai have you looked at the SMJobBless sample code from WWDC 2010? It includes a helper tool and app to bless it.
https://developer.apple.com/library/mac/#samplecode/SMJobBless/Listings/ReadMe_txt.html#//apple_ref/doc/uid/DTS40010071-ReadMe_txt-DontLinkElementID_3
Its README file says:
This sample as it stands does not actually run the helper tool. The following samples show how to [sic] a launchd job and set up interprocess communication:
ssd (Doesn't seem to be online anymore. Was part of the WWDC 2010 sample code.)
BetterAuthorizationSample
I have a dynamic library, I intent to inject in running application & newly launched applications.
I can inject it in running applications with the help of a process running with root user permissions.
Now I am trying that library should get loaded as soon as application is launched. I know one such library capable of doing this called, application enhancer. I am looking for similar behavior.
Does anyone has an Idea how can this be achieved?
Look at SIMBL agent code. It adds a observer to application launch notification and then injects. You can follow the same approach.