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