diff --git a/crashpad.gyp b/crashpad.gyp index d538df02..ef58fddc 100644 --- a/crashpad.gyp +++ b/crashpad.gyp @@ -21,6 +21,7 @@ 'dependencies': [ 'compat/compat.gyp:*', 'minidump/minidump.gyp:*', + 'tools/tools.gyp:*', 'util/util.gyp:*', ], 'sources': [ diff --git a/tools/on_demand_service_tool.mm b/tools/on_demand_service_tool.mm new file mode 100644 index 00000000..946989c7 --- /dev/null +++ b/tools/on_demand_service_tool.mm @@ -0,0 +1,192 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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/mac/foundation_util.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 { + +using namespace crashpad; + +void Usage(const std::string& me) { + 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()); + ToolSupport::UsageTail(me); +} + +} // namespace + +int main(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 = {}; + + const struct option long_options[] = { + {"load", no_argument, NULL, kOptionLoadJob}, + {"unload", no_argument, NULL, kOptionUnloadJob}, + {"label", required_argument, NULL, kOptionJobLabel}, + {"mach_service", required_argument, NULL, kOptionMachService}, + {"help", no_argument, NULL, kOptionHelp}, + {"version", no_argument, NULL, kOptionVersion}, + {NULL, 0, NULL, 0}, + }; + + int opt; + while ((opt = getopt_long(argc, argv, "+LUl:m:", long_options, NULL)) != -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, NULL); + 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 setObject:@YES forKey:mach_service_ns]; + } + + NSMutableDictionary* mutable_job_dictionary = + [[job_dictionary mutableCopy] autorelease]; + [mutable_job_dictionary setObject:mach_services + forKey:@LAUNCH_JOBKEY_MACHSERVICES]; + job_dictionary = mutable_job_dictionary; + } + + CFDictionaryRef job_dictionary_cf = + base::mac::NSToCFCast(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; + } + } +} diff --git a/tools/tool_support.cc b/tools/tool_support.cc new file mode 100644 index 00000000..175fa431 --- /dev/null +++ b/tools/tool_support.cc @@ -0,0 +1,51 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "tools/tool_support.h" + +#include + +#include "package.h" + +namespace crashpad { + +// static +void ToolSupport::Version(const std::string& me) { + fprintf(stderr, + "%s (%s) %s\n%s\n", + me.c_str(), + PACKAGE_NAME, + PACKAGE_VERSION, + PACKAGE_COPYRIGHT); +} + +// static +void ToolSupport::UsageTail(const std::string& me) { + fprintf(stderr, + "\nReport %s bugs to\n%s\n%s home page: <%s>\n", + me.c_str(), + PACKAGE_BUGREPORT, + PACKAGE_NAME, + PACKAGE_URL); +} + +// static +void ToolSupport::UsageHint(const std::string& me, const char* hint) { + if (hint) { + fprintf(stderr, "%s: %s\n", me.c_str(), hint); + } + fprintf(stderr, "Try '%s --help' for more information.\n", me.c_str()); +} + +} // namespace crashpad diff --git a/tools/tool_support.h b/tools/tool_support.h new file mode 100644 index 00000000..37881e38 --- /dev/null +++ b/tools/tool_support.h @@ -0,0 +1,49 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TOOLS_TOOL_SUPPORT_H_ +#define CRASHPAD_TOOLS_TOOL_SUPPORT_H_ + +#include + +#include "base/basictypes.h" + +namespace crashpad { + +//! \brief Common functions used by command line tools. +class ToolSupport { + public: + //! \brief Handles `--version`. + //! + //! \param[in] me The tool’s name, the basename of `argv[0]`. + static void Version(const std::string& me); + + //! \brief Prints the footer for `--help`. + //! + //! \param[in] me The tool’s name, the basename of `argv[0]`. + static void UsageTail(const std::string& me); + + //! \brief Suggests using `--help` when a command line tool can’t make sense + //! of its arguments. + //! + //! \param[in] me The tool’s name, the basename of `argv[0]`. + static void UsageHint(const std::string& me, const char* hint); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ToolSupport); +}; + +} // namespace crashpad + +#endif // CRASHPAD_TOOLS_TOOL_SUPPORT_H_ diff --git a/tools/tools.gyp b/tools/tools.gyp new file mode 100644 index 00000000..8c623cfe --- /dev/null +++ b/tools/tools.gyp @@ -0,0 +1,48 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'targets': [ + { + 'target_name': 'tool_support', + 'type': 'static_library', + 'dependencies': [ + '../third_party/mini_chromium/mini_chromium/base/base.gyp:base', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'tool_support.cc', + 'tool_support.h', + ], + }, + { + 'target_name': 'on_demand_service_tool', + 'type': 'executable', + 'dependencies': [ + 'tool_support', + '../compat/compat.gyp:compat', + '../third_party/mini_chromium/mini_chromium/base/base.gyp:base', + '../util/util.gyp:util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'on_demand_service_tool.mm', + ], + }, + ], +}