// Copyright 2014 The Crashpad Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #import #include #include #include #include #include #include #include #include #include "base/apple/bridging.h" #include "base/strings/sys_string_conversions.h" #include "tools/tool_support.h" #include "util/mac/service_management.h" #include "util/stdlib/objc.h" namespace crashpad { namespace { void Usage(const std::string& me) { // clang-format off fprintf(stderr, "Usage: %s -L -l LABEL [OPTION]... COMMAND [ARG]...\n" " %s -U -l LABEL\n" "Load and unload on-demand Mach services from launchd.\n" "\n" " -L, --load load (submit) the job identified by --label;\n" " COMMAND must be specified\n" " -U, --unload unload (remove) the job identified by --label\n" " -l, --label=LABEL identify the job to launchd with LABEL\n" " -m, --mach-service=SERVICE register SERVICE with the bootstrap server\n" " --help display this help and exit\n" " --version output version information and exit\n", me.c_str(), me.c_str()); // clang-format on ToolSupport::UsageTail(me); } int OnDemandServiceToolMain(int argc, char* argv[]) { const std::string me(basename(argv[0])); enum Operation { kOperationUnknown = 0, kOperationLoadJob, kOperationUnloadJob, }; enum OptionFlags { // “Short” (single-character) options. kOptionLoadJob = 'L', kOptionUnloadJob = 'U', kOptionJobLabel = 'l', kOptionMachService = 'm', // Long options without short equivalents. kOptionLastChar = 255, // Standard options. kOptionHelp = -2, kOptionVersion = -3, }; struct { Operation operation; std::string job_label; std::vector mach_services; } options = {}; static constexpr option long_options[] = { {"load", no_argument, nullptr, kOptionLoadJob}, {"unload", no_argument, nullptr, kOptionUnloadJob}, {"label", required_argument, nullptr, kOptionJobLabel}, {"mach-service", required_argument, nullptr, kOptionMachService}, {"help", no_argument, nullptr, kOptionHelp}, {"version", no_argument, nullptr, kOptionVersion}, {nullptr, 0, nullptr, 0}, }; int opt; while ((opt = getopt_long(argc, argv, "+LUl:m:", long_options, nullptr)) != -1) { switch (opt) { case kOptionLoadJob: options.operation = kOperationLoadJob; break; case kOptionUnloadJob: options.operation = kOperationUnloadJob; break; case kOptionJobLabel: options.job_label = optarg; break; case kOptionMachService: options.mach_services.push_back(optarg); break; case kOptionHelp: Usage(me); return EXIT_SUCCESS; case kOptionVersion: ToolSupport::Version(me); return EXIT_SUCCESS; default: ToolSupport::UsageHint(me, nullptr); return EXIT_FAILURE; } } argc -= optind; argv += optind; if (options.job_label.empty()) { ToolSupport::UsageHint(me, "must provide -l"); return EXIT_FAILURE; } switch (options.operation) { case kOperationLoadJob: { if (argc == 0) { ToolSupport::UsageHint(me, "must provide COMMAND with -L"); return EXIT_FAILURE; } @autoreleasepool { NSString* job_label = base::SysUTF8ToNSString(options.job_label); NSMutableArray* command = [NSMutableArray arrayWithCapacity:argc]; for (int index = 0; index < argc; ++index) { NSString* argument = base::SysUTF8ToNSString(argv[index]); [command addObject:argument]; } NSDictionary* job_dictionary = @{ @LAUNCH_JOBKEY_LABEL : job_label, @LAUNCH_JOBKEY_PROGRAMARGUMENTS : command, }; if (!options.mach_services.empty()) { NSMutableDictionary* mach_services = [NSMutableDictionary dictionaryWithCapacity:options.mach_services.size()]; for (std::string mach_service : options.mach_services) { NSString* mach_service_ns = base::SysUTF8ToNSString(mach_service); mach_services[mach_service_ns] = @YES; } NSMutableDictionary* mutable_job_dictionary = [job_dictionary mutableCopy]; mutable_job_dictionary[@LAUNCH_JOBKEY_MACHSERVICES] = mach_services; job_dictionary = mutable_job_dictionary; } CFDictionaryRef job_dictionary_cf = base::apple::NSToCFPtrCast(job_dictionary); if (!ServiceManagementSubmitJob(job_dictionary_cf)) { fprintf(stderr, "%s: failed to submit job\n", me.c_str()); return EXIT_FAILURE; } } return EXIT_SUCCESS; } case kOperationUnloadJob: { if (!ServiceManagementRemoveJob(options.job_label, true)) { fprintf(stderr, "%s: failed to remove job\n", me.c_str()); return EXIT_FAILURE; } return EXIT_SUCCESS; } default: { ToolSupport::UsageHint(me, "must provide -L or -U"); return EXIT_FAILURE; } } } } // namespace } // namespace crashpad int main(int argc, char* argv[]) { return crashpad::OnDemandServiceToolMain(argc, argv); }