blob: d7beae2786824e0bb478ef430e8a6be9680933ba [file] [log] [blame]
/*
* Copyright (C) 2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "DaemonTestUtilities.h"
#if PLATFORM(MAC) || PLATFORM(IOS)
#import <mach-o/dyld.h>
#import <wtf/Vector.h>
NS_ASSUME_NONNULL_BEGIN
#if PLATFORM(IOS)
@interface NSTask : NSObject
- (instancetype)init;
- (void)launch;
- (void)waitUntilExit;
@property (nullable, copy) NSString *launchPath;
@property (nullable, copy) NSArray<NSString *> *arguments;
@property (nullable, retain) id standardOutput;
@property (readonly, getter=isRunning) BOOL running;
@end
#endif
namespace TestWebKitAPI {
static RetainPtr<NSURL> currentExecutableLocation()
{
uint32_t size { 0 };
_NSGetExecutablePath(nullptr, &size);
Vector<char> buffer;
buffer.resize(size + 1);
_NSGetExecutablePath(buffer.data(), &size);
buffer[size] = '\0';
auto pathString = adoptNS([[NSString alloc] initWithUTF8String:buffer.data()]);
return adoptNS([[NSURL alloc] initFileURLWithPath:pathString.get() isDirectory:NO]);
}
RetainPtr<NSURL> currentExecutableDirectory()
{
return [currentExecutableLocation() URLByDeletingLastPathComponent];
}
#if PLATFORM(IOS)
static RetainPtr<xpc_object_t> convertArrayToXPC(NSArray *array)
{
auto xpc = adoptNS(xpc_array_create(nullptr, 0));
for (id value in array) {
if ([value isKindOfClass:NSString.class])
xpc_array_set_string(xpc.get(), XPC_ARRAY_APPEND, [value UTF8String]);
else
ASSERT_NOT_REACHED();
}
return xpc;
}
static RetainPtr<xpc_object_t> convertDictionaryToXPC(NSDictionary<NSString *, id> *dictionary)
{
auto xpc = adoptNS(xpc_dictionary_create(nullptr, nullptr, 0));
for (NSString *key in dictionary) {
ASSERT([key isKindOfClass:NSString.class]);
const char* keyUTF8 = key.UTF8String;
id value = dictionary[key];
if ([value isKindOfClass:NSString.class])
xpc_dictionary_set_string(xpc.get(), keyUTF8, [value UTF8String]);
else if ([value isKindOfClass:NSNumber.class]) {
uint64_t uint64Value = [value unsignedLongLongValue];
if (uint64Value == 1)
xpc_dictionary_set_bool(xpc.get(), keyUTF8, uint64Value);
else {
ASSERT([value doubleValue] == uint64Value);
xpc_dictionary_set_uint64(xpc.get(), keyUTF8, uint64Value);
}
} else if ([value isKindOfClass:NSArray.class])
xpc_dictionary_set_value(xpc.get(), keyUTF8, convertArrayToXPC(value).get());
else if ([value isKindOfClass:NSDictionary.class])
xpc_dictionary_set_value(xpc.get(), keyUTF8, convertDictionaryToXPC(value).get());
else
ASSERT_NOT_REACHED();
}
return xpc;
}
#endif
void registerPlistWithLaunchD(NSDictionary<NSString *, id> *plist, NSURL *tempDir)
{
NSError *error = nil;
#if PLATFORM(IOS)
auto xpcPlist = convertDictionaryToXPC(plist);
xpc_dictionary_set_string(xpcPlist.get(), "_ManagedBy", "TestWebKitAPI");
xpc_dictionary_set_bool(xpcPlist.get(), "RootedSimulatorPath", true);
auto launchDJob = adoptNS([[OSLaunchdJob alloc] initWithPlist:xpcPlist.get()]);
[launchDJob submit:&error];
#else
NSURL *plistLocation = [tempDir URLByAppendingPathComponent:@"DaemonInfo.plist"];
BOOL success = [[NSFileManager defaultManager] createDirectoryAtURL:tempDir withIntermediateDirectories:YES attributes:nil error:&error];
EXPECT_TRUE(success);
EXPECT_FALSE(error);
success = [plist writeToURL:plistLocation error:&error];
EXPECT_TRUE(success);
system([NSString stringWithFormat:@"launchctl unload %@ 2> /dev/null", plistLocation.path].fileSystemRepresentation);
system([NSString stringWithFormat:@"launchctl load %@", plistLocation.path].fileSystemRepresentation);
EXPECT_FALSE(error);
#endif
}
static int pidOfFirstDaemonInstance(NSString *daemonExecutableName)
{
auto task = adoptNS([[NSTask alloc] init]);
task.get().launchPath = @"/bin/ps";
task.get().arguments = @[
@"-ax",
@"-o",
@"pid,comm"
];
auto taskPipe = adoptNS([[NSPipe alloc] init]);
[task setStandardOutput:taskPipe.get()];
[task launch];
auto data = adoptNS([[NSMutableData alloc] init]);
while ([task isRunning])
[data appendData:[[taskPipe fileHandleForReading] readDataToEndOfFile]];
[data appendData:[[taskPipe fileHandleForReading] readDataToEndOfFile]];
auto psString = adoptNS([[NSString alloc] initWithData:data.get() encoding:NSUTF8StringEncoding]);
NSArray<NSString *> *psEntries = [psString componentsSeparatedByString:@"\n"];
for (NSString* entry in psEntries) {
if (![entry hasSuffix:daemonExecutableName])
continue;
NSArray<NSString *> *components = [entry componentsSeparatedByString:@" "];
EXPECT_GE([components count], 2u);
return [[components firstObject] integerValue];
}
return 0;
}
void killFirstInstanceOfDaemon(NSString *daemonExecutableName)
{
auto pid = pidOfFirstDaemonInstance(daemonExecutableName);
if (!pid)
return;
auto task = adoptNS([[NSTask alloc] init]);
task.get().launchPath = @"/bin/kill";
task.get().arguments = @[
@"-9",
[@(pid) stringValue]
];
[task launch];
[task waitUntilExit];
}
#if PLATFORM(IOS)
BOOL restartService(NSString *, NSString *daemonExecutableName)
{
killFirstInstanceOfDaemon(daemonExecutableName);
sleep(1);
return YES;
}
#else
BOOL restartService(NSString *serviceName, NSString *)
{
auto task = adoptNS([[NSTask alloc] init]);
[task setLaunchPath:@"/bin/launchctl"];
[task setArguments:@[@"kickstart", @"-k", @"-p", [NSString stringWithFormat:@"gui/%u/%@", geteuid(), serviceName]]];
[task launch];
[task waitUntilExit];
return [task terminationStatus] == EXIT_SUCCESS;
}
#endif
} // namespace TestWebKitAPI
NS_ASSUME_NONNULL_END
#endif // PLATFORM(MAC) || PLATFORM(IOS)