Loading...
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 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 | /* * Copyright (c) 2018 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /*! * @header * Interfaces to implement subcommand-style command line utilities (e.g. * launchctl(1)) and automatically generate usage output. The usage generated by * these interfaces assumes a long option convention (cf. getopt_long(3)) and is * loosely based on the docopt convention described at * * http://docopt.org * * The user may define each subcommand taken by the utility as: * * static const os_subcommand_t _foo_cmd = { * .osc_version = OS_SUBCOMMAND_VERSION, * .osc_flags = 0, * .osc_name = "foo", * .osc_desc = "does a foo", * .osc_optstring = NULL, * .osc_options = NULL, * .osc_info = NULL, * .osc_invoke = &_foo_invoke, * }; * OS_SUBCOMMAND_REGISTER(_foo_cmd); * * static const os_subcommand_t _bar_cmd = { * .osc_version = OS_SUBCOMMAND_VERSION, * .osc_flags = 0, * .osc_name = "bar", * .osc_desc = "bars a foo", * .osc_optstring = "x:q", * .osc_options = _bar_opts, * .osc_info = &_bar_optinfo, * .osc_invoke = &_bar_invoke, * }; * OS_SUBCOMMAND_REGISTER(_bar_cmd); * }; * * Where the "bar" subcommand's option information is returned by the routine: * * static void * _bar_optinfo(const os_subcommand_t *osc, * os_subcommand_optarg_format_t format, const struct option *opt, * os_subcommand_option_t *scopt) * { * switch (format) { * case OS_SUBCOMMAND_OPTARG_USAGE: * switch (opt->val) { * case 'x': * scopt->osco_flags |= OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL; * scopt->osco_argdesc = "x-argument"; * break; * case 'q': * scopt->osco_argdesc = "q-argument"; * break; * default: * __builtin_unreachable(); * } * break; * case OS_SUBCOMMAND_OPTARG_HUMAN: * switch (opt->val) { * case 'x': * scopt->osco_flags |= OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL; * scopt->osco_argdesc = "argument describing x"; * break; * case 'q': * scopt->osco_argdesc = "Lorem ipsum dolor sit amet, consectetur " * "adipiscing elit. Nullam a maximus lectus. Curabitur ornare " * "convallis turpis, in porttitor augue tempus laoreet. Maecenas " * "auctor mauris tortor, et tempor libero maximus id. Donec ac " * "nunc et elit sagittis commodo. Donec tincidunt libero vehicula " * "ex eleifend sagittis. Suspendisse consectetur cursus elit. " * "Proin neque metus, commodo id rhoncus eu, cursus hendrerit ex. " * "Etiam in fringilla nulla, vitae mollis eros."; * break; * default: * __builtin_unreachable(); * } * break; * } * } * * When {@link os_subcommand_main} is called, the tool's "help" subcommand will * display approximately the following: * * $ tool help * usage: playground <subcommand> [...] | help [subcommand] * * subcommands: * foo does a foo * bar bars a foo * * $ tool help foo * usage: tool foo * * $ tool help bar * usage: tool bar [--xarg] --qarg[=q-argument] * --xarg argument describing x * --qarg[=q-argument] Lorem ipsum dolor sit amet, consectetur * adipiscing elit. Nullam a maximus lectus. * Curabitur ornare convallis turpis, in porttitor * augue tempus laoreet. Maecenas auctor mauris * tortor, et tempor libero maximus id. Donec ac * nunc et elit sagittis commodo. Donec tincidunt * libero vehicula ex eleifend sagittis. Suspendisse * consectetur cursus elit. Proin neque metus, * commodo id rhoncus eu, cursus hendrerit ex. Etiam * in fringilla nulla, vitae mollis eros. */ #ifndef __DARWIN_CTL_H #define __DARWIN_CTL_H #include <os/base.h> #include <os/api.h> #if DARWIN_TAPI #define LINKER_SET_ENTRY(_x, _y) #else #include <os/linker_set.h> #endif #include <sys/cdefs.h> #include <stdio.h> #include <getopt.h> __BEGIN_DECLS; /*! * @define OS_SUBCOMMAND_REGISTER * Registers a {@link os_subcommand_t} with the runtime. Subcommands may only be * declared as part of the main executable image -- subcommands declared in * dynamic libraries or bundles will not be recognized. */ #define OS_SUBCOMMAND_REGISTER(_subcommand) \ LINKER_SET_ENTRY(__subcommands, _subcommand) /*! * @typedef os_subcommand_t * The formal type name for the _os_subcommand structure. */ DARWIN_API_AVAILABLE_20181020 typedef struct _os_subcommand os_subcommand_t; /*! * @const OS_SUBCOMMAND_OPTION_VERSION * The maximum version of the {@link os_subcommand_option_t} structure supported * by the implementation. */ #define OS_SUBCOMMAND_OPTION_VERSION ((os_struct_version_t)0) /*! * @typedef os_subcommand_optarg_format_t * A type describing a usage format for the argument taken by an option. * * @const OS_SUBCOMMAND_OPTION_USAGE * The short-form name of the argument given to the option. For example, if the * subcommand takes a "--file" option with a required argument, this might be * "file-path" and will be displayed as * * --file=<file-path> * * @const OS_SUBCOMMAND_OPTION_HUMAN * The long-form description of the argument given to the option. Extending the * above example, this might be "The path to a file to take as input. This path * must be absolute; relative paths are not supported." and will be displayed as * * --file The path to a file to take as input. This path must be * absolute; relative paths are not supported. */ DARWIN_API_AVAILABLE_20181020 OS_CLOSED_ENUM(os_subcommand_optarg_format, uint64_t, OS_SUBCOMMAND_OPTARG_USAGE, OS_SUBCOMMAND_OPTARG_HUMAN, ); /*! * @typedef os_subcommand_option_flags_t * Flags describing an option for a subcommand. * * @const OS_SUBCOMMAND_OPTION_FLAG_INIT * No flags set. This value is suitable for initialization purposes. * * @const OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL * The option does not need to be present in the subcommand invocation. By * default, options are considered required. */ DARWIN_API_AVAILABLE_20181020 OS_CLOSED_ENUM(os_subcommand_option_flags, uint64_t, OS_SUBCOMMAND_OPTION_FLAG_INIT = 0, OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL = (1 << 0), ); /*! * @typedef os_subcommand_option_t * A structure describing human-readable information about a particular option * taken by a subcommand. This structure is to be returned when the * implementation queries about a command's options individually. This is done * when the implementation is synthesizing a usage string. * * @field osco_version * The version of the structure. Initialize to * {@link OS_SUBCOMMAND_OPTION_VERSION}. * * @field osco_flags * On return from a {@link os_subcommand_option_info_t} function, a set of flags * describing information about the option. * * @field osco_argdesc * On return from a {@link os_subcommand_option_info_t} function, this should * point to a constant string describing the argument to the option. */ DARWIN_API_AVAILABLE_20181020 typedef struct _os_subcommand_option { const os_struct_version_t osco_version; os_subcommand_option_flags_t osco_flags; const char *osco_argdesc; } os_subcommand_option_t; /*! * @typedef os_subcommand_option_info_t * A type describing a function which returns option information. * * @param osc * The subcommand to which the option belongs. * * @param format * The format of usage information required. * * @param opt * A pointer to the option structure for which to retrieve information. * * @param scopt * A pointer to a subcommand option structure to be populated with information * pertaining to the option. When passed to the callee, this structure is zero- * filled. */ DARWIN_API_AVAILABLE_20181020 typedef void (*os_subcommand_option_info_t)( const os_subcommand_t *osc, os_subcommand_optarg_format_t format, const struct option *opt, os_subcommand_option_t *scopt); /*! * @const OS_SUBCOMMAND_VERSION * The maximum version of the {@link os_subcommand_t} structure supported by the * implementation. */ #define OS_SUBCOMMAND_VERSION ((os_struct_version_t)0) /*! * @enum os_subcommand_flags_t * A type describing flags associated with a subcommand for a command line * utility. * * @const OS_SUBCOMMAND_FLAG_INIT * No flags set. This value is suitable for initialization purposes. * * @const OS_SUBCOMMAND_FLAG_REQUIRE_ROOT * This subcommand requires the user to be root. If the user is not root, the * {@link os_subcommand_dispatch} routine will return {@EX_PERM}. * * @const OS_SUBCOMMAND_FLAG_TTYONLY * This subcommand may only be invoked via a terminal interface, i.e. it should * not be invoked by scripts. Use this option to emphasize that a command's * output is human-readably only and should not be parsed. * * @const OS_SUBCOMMAND_FLAG_HIDDEN * This subcommand should not be displayed in the list of subcommands. */ DARWIN_API_AVAILABLE_20181020 OS_CLOSED_OPTIONS(os_subcommand_flags, uint64_t, OS_SUBCOMMAND_FLAG_INIT, OS_SUBCOMMAND_FLAG_REQUIRE_ROOT = (1 << 0), OS_SUBCOMMAND_FLAG_TTYONLY = (1 << 1), OS_SUBCOMMAND_FLAG_HIDDEN = (1 << 2), ); /*! * @typedef os_subcommand_invoke_t * An type describing the invocation function for a subcommand. * * @param osc * The descriptor for the command being invoked. * * @param argc * The argument vector count. * * @param argv * The argument vector. The first element of this array is the name of the * subcommand. * * @result * An exit code, preferably from sysexits(3). Note that exit codes should not * intersect with POSIX error codes from errno.h (cf. intro(2)). * * @discussion * You may exit directly from within the routine if you wish. */ DARWIN_API_AVAILABLE_20181020 typedef int (*os_subcommand_invoke_t)( const os_subcommand_t *osc, int argc, const char *argv[]); /*! * @struct os_subcommand_t * A type describing a subcommand for a command line tool. * * @field osc_version * The version of the structure. Initialize to {@link OS_SUBCOMMAND_VERSION}. * * @field osc_flags * The flags for the subcommand. * * @field osc_name * The name of the subcommand. The second argument of user input will be matched * against this name. * * @field osc_desc * A brief description of the subcommand. This description will be displayed * next to the subcommand when the user lists all subcommands. * * @field osc_optstring * The option string associated with the subcommand. The implementation does not * recognize this string directly; the intent of storing it here is to provide a * convenient place to access the string for the implementation function. * Combined with the {@link osc_options} field, this enables the following * pattern: * * int ch = 0; * while ((ch = getopt_long(argc, argv, cmd->osc_optstring, * cmd->osc_options, NULL)) != -1) { * switch (ch) { * // process option * } * } * * This pattern keeps the option string and option structs co-located in code. * * @field osc_options * A pointer to an array of option structures describing the long options * recognized by the subcommand (cf. getopt_long(3)). * * @field osc_info * A pointer to a function which returns information about the subcommand's * individual options. * * @field osc_invoke * The implementation for the subcommand. */ DARWIN_API_AVAILABLE_20181020 struct _os_subcommand { const os_struct_version_t osc_version; const os_subcommand_flags_t osc_flags; const char *const osc_name; const char *const osc_desc; const char *osc_optstring; const struct option *osc_options; const os_subcommand_option_info_t osc_info; const os_subcommand_invoke_t osc_invoke; }; /*! * @function os_subcommand_main * Dispatches the subcommand appropriate for the given arguments. All * subcommands will be implicitly discovered by virtue of their delcarations * with the OS_SUBCOMMAND_REGISTER attribute. * * @param argc * The argument count supplied to main(). * * @param argv * The argument vector supplied to main(). * * @result * The exit status from the subcommand's invocation function or an exit status * from the implementation indicating that the subcommand could not be * dispatched. Exit codes that can be returned by the implementation are: * * [EX_USAGE] The subcommand was invoked with improper syntax. Usage * has been printed to stderr. * [EX_USAGE] The subcommand specified is not recognized. * [EX_PERM] The command required root privileges, and the caller is * not running as root. * [EX_UNAVAILABLE] The command specified that it may only run within * the context of a tty(3), and either stdin or stdout are * not a tty(3). * * @discussion * In general, the code returned by this routine should immediately be passed to * exit(3). The reason this routine does not implicitly exit is to allow for the * caller to process multiple subcommand invocations as a batch. * * The caller should not print anything after this routine has returned -- the * expectation is that all relevant information has already been conveyed to the * user either by the implementation or from one of the subcommand invocation * routines. * * This routine implicitly implements a "help" subcommand. */ DARWIN_API_AVAILABLE_20181020 OS_EXPORT OS_WARN_RESULT OS_NONNULL2 int os_subcommand_main(int argc, const char *argv[]); __END_DECLS; #endif // __DARWIN_CTL_H |