1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// CRURegistration-Private contains declarations of CRURegistration
// implementation details that need unit testing.
#ifndef CHROME_UPDATER_MAC_CLIENT_LIB_CRUREGISTRATION_PRIVATE_H_
#define CHROME_UPDATER_MAC_CLIENT_LIB_CRUREGISTRATION_PRIVATE_H_
#import <Foundation/Foundation.h>
#import "CRURegistration.h"
NS_ASSUME_NONNULL_BEGIN
extern NSString* const CRUReturnCodeErrorDomain;
typedef NS_ERROR_ENUM(CRURegistrationInternalErrorDomain,
CRURegistrationInternalError){
CRURegistrationInternalErrorTaskAlreadyLaunched = 1,
// An underlying API that can fail returned an error that we did not
// specifically anticipate.
CRURegistrationInternalErrorUnrecognized = 9999,
};
/***
* CRUTaskResultCallback is a block receiving the result of an NSTask
* invocation.
*
* Parameters:
* NSString* -- all stdout content, nil if the process never launched.
* NSString* -- all stderr content. nil if the process never launched.
* NSError* -- return value of the process.
* * nil: the process ran and returned zero
* * error domain is CRUReturnCodeErrorDomain: process ran and returned
* nonzero; error code
* is the return value. NSData* arguments will be nonnil.
* * any other error domain: the task could not be launched; this is the
* error from NSTask or
* is in CRURegistrationErrorDomain. NSData* elements will be nil.
*/
typedef void (^CRUTaskResultCallback)(NSString* _Nullable,
NSString* _Nullable,
NSError* _Nullable);
@class CRURegistrationWorkItem; // break circular reference
/***
* CRUNextTaskCallback is similar to CRUTaskResultCallback, but it is run
* synchronously by the work queue and may return a new work item to perform
* next, preempting any other queued task. The current work item (that has just
* completed or failed its task) is provided to the callback to make it easier
* to avoid dependency cycles.
*
* Parameters:
* CRURegistrationWorkItem* -- the work item that just completed, which
* contained this block as its `onDone` callback.
* NSString* -- all stdout content, nil if the process never launched.
* NSString* -- all stderr content. nil if the process never launched.
* NSError* -- return value of the process.
* * nil: the process ran and returned zero
* * error domain is CRUReturnCodeErrorDomain: process ran and returned
* nonzero; error code
* is the return value. NSData* arguments will be nonnil.
* * any other error domain: the task could not be launched; this is the
* error from NSTask or
* is in CRURegistrationErrorDomain. NSData* elements will be nil.
*/
typedef CRURegistrationWorkItem* _Nullable (^CRUNextTaskCallback)(
CRURegistrationWorkItem*,
NSString* _Nullable,
NSString* _Nullable,
NSError* _Nullable);
/**
* CRUAsyncTaskRunner runs an NSTask and asynchronously accumulates its stdout
* and stderr streams into NSMutableData buffers.
*/
@interface CRUAsyncTaskRunner : NSObject
- (instancetype)initWithTask:(NSTask*)task
targetQueue:(dispatch_queue_t)targetQueue
NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
/**
* launchWithReply launches the task and buffers its output. It calls `reply`
* with the results of the task when the task completes. If the task cannot
* be launched, it invokes `reply` with nil NSString* args and the NSError* from
* NSTask's launch failure.
*/
- (void)launchWithReply:(CRUTaskResultCallback)reply;
@end
/**
* CRURegistrationWorkItem represents a task to be constructed and invoked.
*
* It is plain data represented as an Objective-C class (instead of a struct)
* so it can be contained in an NSMutableArray.
*/
@interface CRURegistrationWorkItem : NSObject
/**
* Callback returning the path of the binary to run. This is invoked immediately
* before the path is needed to construct an NSTask.
*
* This is a callback because some work items -- notably, installing the updater
* itself -- may affect where future work items should look for the binaries
* they intend to run, so searching for them needs to be deferred until
* prior tasks have completed.
*/
@property(nonatomic, copy) NSURL* (^binPathCallback)();
/**
* Arguments to invoke the NSTask with.
*/
@property(nonatomic, copy) NSArray<NSString*>* args;
/**
* Handler to asynchronously invoke with task results. This handler is
* _not_ responsible for cycling the task queue.
*/
@property(nonatomic, copy) CRUTaskResultCallback resultCallback;
/**
* Handler invoked synchronously with the task results, which has the
* opportunity to preempt the next task or edit this CRURegistrationWorkItem
* (for example, to change `resultCallback`) before `resultCallback` is
* asynchronously invoked.
*/
@property(nonatomic, copy) CRUNextTaskCallback onDone;
@end
@interface CRURegistration (VisibleForTesting)
/**
* Asynchronously add work items and, if the work queue is not currently being
* processed, starts processing them. (If work is already in progress, these
* items will be picked up by its continued execution.)
*/
- (void)addWorkItems:(NSArray<CRURegistrationWorkItem*>*)item;
/**
* Synchronously finds the path to an installed KSAdmin binary. If a systemwide
* ksadmin is available, it prefers it; otherwise, if a user ksadmin is
* available, it returns that; if neither can be found, it returns nil.
*
* This does not depend on, or mutate, any protected state inside
* CRURegistration itself, but does check filesystem state. If the updater is
* concurrently being installed, it might not find it. This is intended for
* use while CRURegistration is not concurrently running a task.
*/
- (nullable NSURL*)syncFindBestKSAdmin;
/**
* Wrap an NSError from a failed attempt to run a task with a semantically
* appropriate domain and code. If the error is an intended part of the library
* API, it will be in CRURegistrationErrorDomain; otherwise, it represents a
* library bug that the user should, ideally, not rely on and will be wrapped
* under CRURegistrationInternalErrorDomain.
*
* Errors already in one of these two error domains are returned unchanged.
* Nil is also returned unchanged. Otherwise:
* - errors representing nonzero return codes from tasks are converted to
* CRURegistrationErrorTaskFailed
* - "file not found" errors from Apple APIs are assumed to be NSTask failing
* to find a binary and are converted to CRURegistrationErrorHelperNotFound
* - other errors are unexpected
*
* If the returned error is not the same as the input error, the result error's
* `userInfo` dictionary contains a value under `NSUnderlyingErrorKey` with the
* original error unchanged. A wrapped error's `userInfo` also contains the
* values of `gotStdout` and `gotStderr` under `CRUStdoutKey` and `CRUStderrKey`
* respectively. Values in `userInfo` are intended to assist with debugging, but
* production code should not rely on these values for identifying and handling
* the disposition of an error; file bugs against this library if more detail
* is required than is available, or if any internal error is encountered.
*/
- (nullable NSError*)wrapError:(nullable NSError*)error
withStdout:(nullable NSString*)gotStdout
andStderr:(nullable NSString*)gotStderr;
@end
NS_ASSUME_NONNULL_END
#endif // CHROME_UPDATER_MAC_CLIENT_LIB_CRUREGISTRATION_PRIVATE_H_
|