首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >验证是否安装了辅助工具。

验证是否安装了辅助工具。
EN

Stack Overflow用户
提问于 2019-01-19 05:37:49
回答 3查看 1.4K关注 0票数 4

我正在用Swift编写一个macOS应用程序,它需要一个特权助手工具--希望升级不是必要的,而是看起来就像

我发现非常优秀的示例应用程序,特别是针对这个场景定制的。我已经成功地将它的代码移植到我自己的应用程序中,但是我不得不检查助手工具是否已经安装,如果没有安装,请使用SMJobBless()和朋友来安装它。

在运行示例应用程序时,如果未安装助手工具,则应用程序将停留在以下屏幕上:

要明确的是,从阅读代码开始,我以为它应该在某个时候将标签更新为“:No”,但这似乎并没有发生。

如果我单击"Install“,结果就是这样。

从现在开始,除非我手动删除助手工具,否则重新运行这个应用程序将显示这个屏幕上的"Helper Installed: Yes“。

在本例中,用户必须手动单击"Install“按钮时,这种行为可能是正常的。但是,在我的应用程序中,如果还没有安装,我希望它能够自动请求安装助手工具。如果它已经安装,我不想浪费用户的时间再次请求他们的密码。

我认为这很简单:如果助手工具不可用,在连接到它的过程中的某个地方会发生错误,这是我请求安装该工具的触发器。如果没有发生错误,则假定该工具已经安装。

下面是我为通过XPC连接到助手工具而编写的被黑客攻击的代码:

代码语言:javascript
复制
var helperConnection: NSXPCConnection?
var xpcErrorHandler: ((Error) -> Void)?
var helper: MyServiceProtocol?

// ...

helperConnection = NSXPCConnection(machServiceName: MyServiceName, options: .privileged)
helperConnection?.remoteObjectInterface = NSXPCInterface(with: MyServiceProtocol.self)
helperConnection?.resume()

helperConnection?.interruptionHandler = {
    // Handle interruption
    NSLog("interruptionHandler()")
}

helperConnection?.invalidationHandler = {
    // Handle invalidation
    NSLog("invalidationHandler()")
}

xpcErrorHandler = { error in
   NSLog("xpcErrorHandler: \(error.localizedDescription)")
}

guard
    let errorHandler = xpcErrorHandler,
    let helperService = helperConnection?.remoteObjectProxyWithErrorHandler(errorHandler) as? MyServiceProtocol
    else {
        return
}

helper = helperService

如果未安装辅助工具,则运行此代码不会产生错误或NSLog()输出。如果之后,我通过XPC (使用helper?.someFunction(...))调用一个函数,那么什么都不会发生--我还不如与/dev/null对话。

现在我只能抓着头去寻找一种检测工具是否安装的技术。解决此问题的示例应用程序的解决方案是添加一个getVersion()方法;如果它返回某项内容,“Installed”就会变成灰色,标签将更改为"Helper : Yes“。

我考虑通过在我的工具中编写一个简单的函数,立即返回,并在主应用程序中使用超时来扩展这个想法--如果在代码超时之前我没有得到结果,那么助手工具很可能不会安装。我发现这是一个很麻烦的解决方案--例如,如果助手工具(按需启动)要花费太长时间才能启动,比如因为计算机很旧,用户运行的是CPU密集型的东西?

我看到了其他的替代方案,比如在预期的位置查看文件系统(/Library/PrivilegedHelperTools/Library/LaunchDaemons),但是这个解决方案仍然让我感到不满意。

我的问题是:是否有一种方法可以轻松地检测特权XPC助手工具是否正在另一端侦听?

我的环境: macOS Mojave 10.14.2,Xcode 10.1,SWIFT4.2。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-01-21 17:38:12

由于您创建了助手工具,只需添加一个XPC消息处理程序来报告您的工具的状态。当你启动时,连接并发送这条信息。如果其中任何一个失败,您的工具都没有正确安装(或者没有响应)。

在我的代码中,我的所有XPC服务(包括我的特权助手)都采用了用于测试和操作安装的基本协议:

代码语言:javascript
复制
@protocol DDComponentInstalling /*<NSObject>*/

@required
- (void)queryBuildNumberWithReply:(void(^_Nonnull)(UInt32))reply;

@optional
- (void)didInstallComponent;
- (void)willUninstallComponent;

queryBuildNumberWithReply:返回一个整数,描述组件的版本号:

代码语言:javascript
复制
- (void)queryBuildNumberWithReply:(void(^)(UInt32))reply
{
    reply(FULL_BUILD_VERSION);
}

如果消息成功,则将返回的值与应用程序中的生成号常量进行比较。如果它们不匹配,则服务是一个旧的/更新的版本,需要替换。这个常数在我的产品每次公开发布时都会增加。

我使用的代码如下所示:

代码语言:javascript
复制
- (BOOL)verifyServiceVersion
{
    DDConnection* connection = self.serviceConnection;
    id<DDComponentInstalling> proxy = connection.serviceProxy;  // get the proxy (will connect, as needed)
    if (proxy==nil)
        // an XPC connection could not be established or the proxy object could not be obtained
        return NO;  // assume service is not installed

    // Ask for the version number and wait for a response
    NSConditionLock* barrierLock = [[NSConditionLock alloc] initWithCondition:NO];
    __block UInt32 serviceVersion = UNKNOWN_BUILD_VERSION;
    [proxy queryBuildNumberWithReply:^(UInt32 version) {
        // Executes when service returns the build version
        [barrierLock lock];
        serviceVersion = version;
        [barrierLock unlockWithCondition:YES];  // signal to foreground thead that query is finished
        }];
    // wait for the message to reply
    [barrierLock lockWhenCondition:YES beforeDate:[NSDate dateWithTimeIntervalSinceNow:30.0];
    BOOL answer = (serviceVersion==FULL_BUILD_VERSION); // YES means helper is installed, alive, and correct version
    [barrierLock unlock];

    return answer;
}

请注意,DDConnection是XPC连接的实用程序包装器,barrierLock技巧实际上封装在一个共享方法中--所以我不会反复编写它--而是为了演示目的在这里展开。

我还需要处理前/后安装/升级问题,所以我的所有组件都实现了一个可选的didInstallComponentwillUninstallComponent方法,这些方法是我在安装新助手后立即发送的,或者就在我计划卸载或替换安装的助手之前。

票数 3
EN

Stack Overflow用户

发布于 2019-01-19 14:02:09

我将检查文件系统是否存在二进制文件(in /Library/PrivilegedHelperTools,以及plist是否存在于/Library/LaunchDaemons中)。然后,您可以联系XPC服务,并调用某种ping函数,如果服务启动并运行,该函数将响应。

只是我的第二个cts

罗伯特

票数 3
EN

Stack Overflow用户

发布于 2020-08-19 08:03:31

背景

实际上,可以避免等待工具响应的超时。事实上,您引用的erikberglund/SwiftPrivilegedHelper示例确实会在"Helper“textfield旁边打印单词"No”,如果该工具没有启用(请参阅这里),当然是异步的,但实际上是瞬间的。

然而,我处于一个独特的位置,您在我自己的特权助手的实现中描述了这个问题,但是SwiftPrivilegedHelper示例完全适合我。当工具未安装时,在前者中从不调用remoteObjectProxyWithErrorHandler错误处理程序,而在后者中则调用。

因为我有一个关于你的问题的例子,也有一个有用的例子,我相信我已经找到了根本原因:

根本原因

至少在我的例子中,没有干净地卸载助手工具

在某种程度上,我从/Library/LaunchDaemons中删除了它的/Library/LaunchDaemons,从/Library/PrivilegedHelperTools中删除了工具本身,但是我想我没有运行sudo launchctl unload /Library/LaunchDaemons/com.example.foo.plist

当我运行sudo launchctl listsudo launchctl print system/com.example.foo.plist时,它仍然被列出。

在这种情况下,对该工具的调用显然不会成功(因为该工具没有安装),但不会失败(因为launchctl认为它是安装了)。

解决方案

正确的卸载可以通过以下两种方式之一完成:

  1. sudo launchctl remove com.example.foo (注:非system/com.example.foo)
  2. sudo launchctl unload /Library/LaunchDaemons/com.example.foo.plist (这要求磁盘上仍然存在plist )。

如果正确卸载,sudo launchctl print system/ca.example.foo应打印:

代码语言:javascript
复制
Bad request.
Could not find service "com.example.foo" in domain for system

奎因“爱斯基摩人”表示以下序列,它使用remove方法:

  1. 删除属性列表。
  2. 拆卸辅助工具。
  3. 把工作撤掉。

一旦我清理了东西,瞧,当工具没有安装时,我的remoteObjectProxyWithErrorHandler开始调用它的错误处理程序。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54264378

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档