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 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 | /* * 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 struct option _template_opts[] = { * [0] = { * .name = "bar", * .has_arg = required_argument, * .flag = NULL, * .val = 'f', * }, { * .name = "baz", * .has_arg = optional_argument, * .flag = NULL, * .val = 'b', * }, { * .name = NULL, * .has_arg = 0, * .flag = NULL, * .val = 0, * }, * }; * * static const os_subcommand_option_t _template_required[] = { * [0] = { * .osco_template = OS_SUBCOMMAND_OPTION_VERSION, * .osco_flags = 0, * .osco_option = &_template_opts[0], * .osco_argument_usage = "thing-to-bar", * .osco_argument_human = "The thing to bar. May be specified as a " * "bar that has a baz. This baz should have a shnaz.", * }, * OS_SUBCOMMAND_OPTION_TERMINATOR, * }; * * static const os_subcommand_option_t _template_optional[] = { * [0] = { * .osco_template = OS_SUBCOMMAND_OPTION_VERSION, * .osco_flags = 0, * .osco_option = &_template_opts[1], * .osco_argument_usage = "thing-to-baz", * .osco_argument_human = "The baz of which to propagate a foo.", * }, * OS_SUBCOMMAND_OPTION_TERMINATOR, * }; * * static const os_subcommand_option_t _template_positional[] = { * [0] = { * .osco_template = OS_SUBCOMMAND_OPTION_VERSION, * .osco_flags = 0, * .osco_option = NULL, * .osco_argument_usage = "positional-baz", * .osco_argument_human = "A baz specified by position.", * }, * OS_SUBCOMMAND_OPTION_TERMINATOR, * }; * * static const os_subcommand_t _template_cmd = { * .osc_template = OS_SUBCOMMAND_VERSION, * .osc_flags = 0, * .osc_name = "foo", * .osc_desc = "foo a bar or maybe baz", * .osc_optstring = "f:b:", * .osc_options = _foo_opts, * .osc_required = _foo_required, * .osc_optional = _foo_optional, * .osc_positional = _template_positional, * .osc_invoke = &_foo_invoke, * } * OS_SUBCOMMAND_REGISTER(_foo_cmd); * }; * * When {@link os_subcommand_main} is called, the tool's "help" subcommand will * display approximately the following: * * $ tool help * usage: tool <subcommand> * * subcommands: * foo foo a bar or maybe baz * help Prints helpful information * * $ tool help foo * usage: tool foo [options] --bar=<thing-to-bar> <positional-baz> * * required options: * --bar=<thing-to-bar> The thing to bar. May be specified as a bar that * has a baz. This baz should have a shnaz. * * positional-baz A baz specified by position. * * optional options: * --baz[=thing-to-baz] The baz of which to propagate a foo. */ #ifndef __DARWIN_CTL_H #define __DARWIN_CTL_H #include <os/base.h> #include <os/api.h> #include <os/linker_set.h> #include <sys/cdefs.h> #include <stdio.h> #include <getopt.h> #if DARWIN_TAPI #include "tapi.h" #endif __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_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_TERMINATOR * The option terminates an array of {@link os_subcommand_option_t} structures * and does not contain any useful information. * * @const OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL_POS * The option is a positional option (that is, not identified by a long or short * flag) and is not required for the subcommand to execute successfully. * * @const OS_SUBCOMMAND_OPTION_FLAG_ENUM * The option has an explicitly-defined list of valid inputs that are enumerated * in the option's {@link osco_argument_usage} field. When printing usage * information for this option, the implementation will not transform the string * in this field in any way. * * For example, an option named "--stream" might have three valid inputs: * "stdin", "stdout", and "stderr", and the usage string may be specified as * * "stdin|stdout|stderr" * * Without this flag, the implementation would print this string as a parameter * name, i.e. in all caps: * * "<STDIN|STDOUT|STDERR>" * * With this flag, the string will be printed as it is given. */ DARWIN_API_AVAILABLE_20181020 OS_CLOSED_ENUM(os_subcommand_option_flags, uint64_t, OS_SUBCOMMAND_OPTION_FLAG_INIT = 0, OS_SUBCOMMAND_OPTION_FLAG_TERMINATOR = (1 << 0), OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL_POS = (1 << 1), OS_SUBCOMMAND_OPTION_FLAG_ENUM = (1 << 2), ); /*! * @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 invokes a {@link os_subcommand_option_info_t} function to * query 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 * A set of flags describing information about the option. * * @field osco_option * A pointer to the option structure ingested by getopt_long(3) which * corresponds to this option. * * @field osco_argument_usage * The short-form name of the argument given to the option, appropriate for * display in a usage specifier. For example, if the subcommand takes a "--file" * option with a required argument, this might be the string "FILE-PATH", and * the resulting usage specifier would be * * --file=<FILE-PATH> * * @field osco_argument_human * The long-form description of the argument given to the option. Extending the * above example, this might be the string "The path to a file to take as input. * This path must be absolute; relative paths are not supported." and the * resulting usage specifier would be * * --file=<FILE-PATH> The path to a file to take as input. This path must be * absolute; relative paths are not supported. */ DARWIN_API_AVAILABLE_20191015 typedef struct _os_subcommand_option { const os_struct_version_t osco_version; os_subcommand_option_flags_t osco_flags; const struct option *osco_option; const char *osco_argument_usage; const char *osco_argument_human; } os_subcommand_option_t; /*! * @const OS_SUBCOMMAND_OPTION_TERMINATOR * A convenience terminator for an array of {@link os_subcommand_option_t} * structures. */ #define OS_SUBCOMMAND_OPTION_TERMINATOR (os_subcommand_option_t){ \ .osco_version = OS_SUBCOMMAND_OPTION_VERSION, \ .osco_flags = OS_SUBCOMMAND_OPTION_FLAG_TERMINATOR, \ .osco_option = NULL, \ .osco_argument_usage = NULL, \ .osco_argument_human = NULL, \ } /*! * @const OS_SUBCOMMAND_GETOPT_TERMINATOR * A convenience terminator for an array of getopt(3) option structures. */ #define OS_SUBCOMMAND_GETOPT_TERMINATOR (struct option){ \ .name = NULL, \ .has_arg = 0, \ .flag = NULL, \ .val = 0, \ } /*! * @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. * * @const OS_SUBCOMMAND_FLAG_MAIN * This subcommand is the "main" subcommand. Designating a main subcommand * allows the program to specify and parse global options using an * {@link os_subcommand_t} object and {@link os_subcommand_main}. * * This flag implies the behavior of {@link OS_SUBCOMMAND_FLAG_HIDDEN}. * * If the program specifies a main subcommand, that subcommand's invocation * routine is unconditionally called before calling the subcommand invocation, * if the user provided a subcommand. The invocation function for the main * subcommand should not exit on success and should instead return 0. * * If multiple subcommands in the same program set * {@link OS_SUBCOMMAND_FLAG_MAIN}, the implementation's behavior is undefined. * * @const OS_SUBCOMMAND_FLAG_HELPFUL * When invoked with no arguments, this subcommand will print usage information * to stdout and exit with status zero. * * @const OS_SUBCOMMAND_FLAG_HELPFUL_FIRST_OPTION * Allow the implementation to detect whether the user is attempting to print * usage information for the given subcommand. If the implementation concludes * that the user is seeking help, it will print the subcommand's usage * information to stdout and exit with status 0. * * The implementation will conclude that the user is seeking help if * * - only one argument is provided to the subcommand, and * * any of the following * * - the argument is "-h" * - the argument is "-help" * - the argument is "--help" * - the argument is "help" */ 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), OS_SUBCOMMAND_FLAG_MAIN = (1 << 3), OS_SUBCOMMAND_FLAG_HELPFUL = (1 << 4), OS_SUBCOMMAND_FLAG_HELPFUL_FIRST_OPTION = (1 << 5), ); /*! * @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). Do not return a POSIX error code * directly from this routine. * * @discussion * You may exit directly on success or failure from this routine if desired. If * the routine is the invocation for a main subcommand, then on success, the * routine should return zero to the caller rather than exiting so that the * implementation may continue parsing the command line arguments. */ 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_long_desc * A long description of the subcommand. This description will be displayed * when the user invokes help on the subcommand. If it's NULL the brief * description from osc_desc will be displayed. * * @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. This array must be terminated by a NULL entry * as expected by getopt_long(3). * * @field osc_required * A pointer to an array of subcommand option descriptors. The options described * in this array are required for the subcommand to execute successfully. This * array should be terminated with {@link OS_SUBCOMMAND_OPTION_TERMINATOR}. * * This array is consulted when printing usage information. * * @field osc_optional * A pointer to an array of subcommand option descriptors. The options described * in this array are parsed by the subcommand but not required for it to execute * successfully. This array should be terminated with * {@link OS_SUBCOMMAND_OPTION_TERMINATOR}. * * This array is consulted when printing usage information. * * @field osc_positional * A pointer to an array of subcommand option descriptors. The options described * in this array are expected to follow the required and optional arguments in * the command line invocation, in the order given in this array. This array * should be terminated with {@link OS_SUBCOMMAND_OPTION_TERMINATOR}. * * These options are expected to have their {@link osco_option} fields set to * NULL. * * This array is consulted when printing usage information. * * @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_20200401 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_t *osc_required; const os_subcommand_option_t *osc_optional; const os_subcommand_option_t *osc_positional; const os_subcommand_invoke_t osc_invoke; const char *const osc_long_desc; }; /*! * @typedef os_subcommand_main_flags_t * Flags modifying the behavior of {@link os_subcommand_main}. * * @const OS_SUBCOMMAND_MAIN_FLAG_INIT * No flags set. This value is suitable for initialization purposes. */ OS_CLOSED_OPTIONS(os_subcommand_main_flags, uint64_t, OS_SUBCOMMAND_MAIN_FLAG_INIT, ); /*! * @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(). * * @param flags * Flags modifying the behavior of the implementation. * * @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_20191015 OS_EXPORT OS_WARN_RESULT OS_NONNULL2 int os_subcommand_main(int argc, const char *argv[], os_subcommand_main_flags_t flags); /*! * @function os_subcommand_fprintf * Prints a message in the context of a subcommand to the given output stream. * * @param osc * The subcommand which represents the context of the message. * * @param f * The stream to which the message shall be printed. If NULL, will be printed to * stderr(4). * * @param fmt * A printf(3)-style format string. * * @param ... * The arguments corresponding to {@link fmt}. * * @discussion * This routine provides a uniform method for printing messages in the context * of a subcommand. It will ensure that the message is prefixed appropriately * (e.g. with the program name and/or subcommand name). * * This routine should be used for printing messages intended for humans to * read; the implementation makes no guarantees about the output format's * stability. If any output is intended to be machine-parseable, it should be * written with fprintf(3) et al. */ DARWIN_API_AVAILABLE_20191015 OS_EXPORT OS_NONNULL3 OS_FORMAT_PRINTF(3, 4) void os_subcommand_fprintf(const os_subcommand_t *osc, FILE *f, const char *fmt, ...); /*! * @function os_subcommand_vfprintf * Prints a message in the context of a subcommand to the given output stream. * * @param osc * The subcommand which represents the context of the message. * * @param f * The stream to which the message shall be printed. If NULL, will be printed to * stderr(4). * * @param fmt * A printf(3)-style format string. * * @param ap * The argument list corresponding to {@link fmt}. * * @discussion * See discussion for {@link os_subcommand_fprintf}. */ DARWIN_API_AVAILABLE_20191015 OS_EXPORT OS_NONNULL3 void os_subcommand_vfprintf(const os_subcommand_t *osc, FILE *f, const char *fmt, va_list ap); __END_DECLS; #endif // __DARWIN_CTL_H |