pacemaker  2.0.3-4b1f869f0f
Scalable High-Availability cluster resource manager
cmdline.c
Go to the documentation of this file.
1 /*
2  * Copyright 2019 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8  */
9 
10 #ifndef _GNU_SOURCE
11 # define _GNU_SOURCE
12 #endif
13 
14 #include <config.h>
15 #include <glib.h>
16 
17 #include <crm/crm.h>
19 #include <crm/common/util.h>
20 
21 static gboolean
22 bump_verbosity(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
23  pcmk__common_args_t *common_args = (pcmk__common_args_t *) data;
24  common_args->verbosity++;
25  return TRUE;
26 }
27 
30 {
31  pcmk__common_args_t *args = NULL;
32 
33  args = calloc(1, sizeof(pcmk__common_args_t));
34  if (args == NULL) {
35  crm_exit(crm_errno2exit(-ENOMEM));
36  }
37 
38  args->summary = strdup(summary);
39  if (args->summary == NULL) {
40  crm_exit(crm_errno2exit(-ENOMEM));
41  }
42 
43  return args;
44 }
45 
46 static void
47 free_common_args(gpointer data) {
48  pcmk__common_args_t *common_args = (pcmk__common_args_t *) data;
49 
50  free(common_args->summary);
51  free(common_args->output_ty);
52  free(common_args->output_dest);
53 
54  if (common_args->output_as_descr != NULL) {
55  free(common_args->output_as_descr);
56  }
57 
58  free(common_args);
59 }
60 
61 GOptionContext *
62 pcmk__build_arg_context(pcmk__common_args_t *common_args, const char *fmts,
63  GOptionGroup **output_group) {
64  char *desc = crm_strdup_printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
65  GOptionContext *context;
66  GOptionGroup *main_group;
67 
68  GOptionEntry main_entries[3] = {
69  { "version", '$', 0, G_OPTION_ARG_NONE, &(common_args->version),
70  "Display software version and exit",
71  NULL },
72  { "verbose", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, bump_verbosity,
73  "Increase debug output (may be specified multiple times)",
74  NULL },
75 
76  { NULL }
77  };
78 
79  main_group = g_option_group_new(NULL, "Application Options:", NULL, common_args, free_common_args);
80  g_option_group_add_entries(main_group, main_entries);
81 
82  context = g_option_context_new(NULL);
83  g_option_context_set_summary(context, common_args->summary);
84  g_option_context_set_description(context, desc);
85  g_option_context_set_main_group(context, main_group);
86 
87  if (fmts != NULL) {
88  GOptionEntry output_entries[3] = {
89  { "output-as", 0, 0, G_OPTION_ARG_STRING, &(common_args->output_ty),
90  NULL,
91  "FORMAT" },
92  { "output-to", 0, 0, G_OPTION_ARG_STRING, &(common_args->output_dest),
93  "Specify file name for output (or \"-\" for stdout)", "DEST" },
94 
95  { NULL }
96  };
97 
98  if (*output_group == NULL) {
99  *output_group = g_option_group_new("output", "Output Options:", "Show output help", NULL, NULL);
100  }
101 
102  common_args->output_as_descr = crm_strdup_printf("Specify output format as one of: %s", fmts);
103  output_entries[0].description = common_args->output_as_descr;
104  g_option_group_add_entries(*output_group, output_entries);
105  g_option_context_add_group(context, *output_group);
106  }
107 
108  free(desc);
109 
110  return context;
111 }
112 
113 void
114 pcmk__free_arg_context(GOptionContext *context) {
115  if (context == NULL) {
116  return;
117  }
118 
119  g_option_context_free(context);
120 }
121 
122 void
123 pcmk__add_main_args(GOptionContext *context, GOptionEntry entries[])
124 {
125  GOptionGroup *main_group = g_option_context_get_main_group(context);
126 
127  g_option_group_add_entries(main_group, entries);
128 }
129 
130 void
131 pcmk__add_arg_group(GOptionContext *context, const char *name,
132  const char *header, const char *desc,
133  GOptionEntry entries[])
134 {
135  GOptionGroup *group = NULL;
136 
137  group = g_option_group_new(name, header, desc, NULL, NULL);
138  g_option_group_add_entries(group, entries);
139  g_option_context_add_group(context, group);
140 }
141 
142 char **
143 pcmk__cmdline_preproc(int argc, char **argv, const char *special) {
144  char **retval = NULL;
145  GPtrArray *arr = g_ptr_array_new();
146  bool saw_dash_dash = false;
147 
148  for (int i = 0; i < argc; i++) {
149  /* If this is the first time we saw "--" in the command line, set
150  * a flag so we know to just copy everything after it over. We also
151  * want to copy the "--" over so whatever actually parses the command
152  * line when we're done knows where arguments end.
153  */
154  if (saw_dash_dash == false && strcmp(argv[i], "--") == 0) {
155  saw_dash_dash = true;
156  }
157 
158  if (saw_dash_dash == true) {
159  g_ptr_array_add(arr, strdup(argv[i]));
160  continue;
161  }
162 
163  /* This is just a dash by itself. That could indicate stdin/stdout, or
164  * it could be user error. Copy it over and let glib figure it out.
165  */
166  if (safe_str_eq(argv[i], "-")) {
167  g_ptr_array_add(arr, strdup(argv[i]));
168  continue;
169  }
170 
171  /* This is a short argument, or perhaps several. Iterate over it
172  * and explode them out into individual arguments.
173  */
174  if (g_str_has_prefix(argv[i], "-") && !g_str_has_prefix(argv[i], "--")) {
175  /* Skip over leading dash */
176  char *ch = argv[i]+1;
177 
178  while (*ch != '\0') {
179  /* This is a special short argument that takes an option. getopt
180  * allows values to be interspersed with a list of arguments, but
181  * glib does not. Grab both the argument and its value and
182  * separate them into a new argument.
183  */
184  if (strchr(special, *ch) != NULL) {
185  /* The argument does not occur at the end of this string of
186  * arguments. Take everything through the end as its value.
187  */
188  if (*(ch+1) != '\0') {
189  g_ptr_array_add(arr, (gpointer) crm_strdup_printf("-%c", *ch));
190  g_ptr_array_add(arr, strdup(ch+1));
191  break;
192 
193  /* The argument occurs at the end of this string. Hopefully
194  * whatever comes next in argv is its value. It may not be,
195  * but that is not for us to decide.
196  */
197  } else {
198  g_ptr_array_add(arr, (gpointer) crm_strdup_printf("-%c", *ch));
199  ch++;
200  }
201 
202  /* This is a regular short argument. Just copy it over. */
203  } else {
204  g_ptr_array_add(arr, (gpointer) crm_strdup_printf("-%c", *ch));
205  ch++;
206  }
207  }
208 
209  /* This is a long argument, or an option, or something else.
210  * Copy it over - everything else is copied, so this keeps it easy for
211  * the caller to know what to do with the memory when it's done.
212  */
213  } else {
214  g_ptr_array_add(arr, strdup(argv[i]));
215  }
216  }
217 
218  /* Convert the GPtrArray into a char **, which the command line parsing
219  * code knows how to deal with. Then we can free the array (but not its
220  * contents).
221  */
222  retval = calloc(arr->len+1, sizeof(char *));
223  for (int i = 0; i < arr->len; i++) {
224  retval [i] = (char *) g_ptr_array_index(arr, i);
225  }
226 
227  g_ptr_array_free(arr, FALSE);
228 
229  return retval;
230 }
231 
232 G_GNUC_PRINTF(3, 4)
233 gboolean
234 pcmk__force_args(GOptionContext *context, GError **error, const char *format, ...) {
235  int len = 0;
236  char *buf = NULL;
237  gchar **extra_args = NULL;
238  va_list ap;
239  gboolean retval = TRUE;
240 
241  va_start(ap, format);
242  len = vasprintf(&buf, format, ap);
243  CRM_ASSERT(len > 0);
244  va_end(ap);
245 
246  if (!g_shell_parse_argv(buf, NULL, &extra_args, error)) {
247  g_strfreev(extra_args);
248  free(buf);
249  return FALSE;
250  }
251 
252  retval = g_option_context_parse_strv(context, &extra_args, error);
253 
254  g_strfreev(extra_args);
255  free(buf);
256  return retval;
257 }
pcmk__build_arg_context
GOptionContext * pcmk__build_arg_context(pcmk__common_args_t *common_args, const char *fmts, GOptionGroup **output_group)
Definition: cmdline.c:62
pcmk__common_args_t::verbosity
unsigned int verbosity
Definition: cmdline_internal.h:25
data
char data[0]
Definition: internal.h:90
pcmk__new_common_args
pcmk__common_args_t * pcmk__new_common_args(const char *summary)
Definition: cmdline.c:29
pcmk__common_args_t
Definition: cmdline_internal.h:19
safe_str_eq
#define safe_str_eq(a, b)
Definition: util.h:61
cmdline_internal.h
pcmk__add_main_args
void pcmk__add_main_args(GOptionContext *context, GOptionEntry entries[])
Definition: cmdline.c:123
pcmk__force_args
gboolean pcmk__force_args(GOptionContext *context, GError **error, const char *format,...)
Definition: cmdline.c:234
pcmk__add_arg_group
void pcmk__add_arg_group(GOptionContext *context, const char *name, const char *header, const char *desc, GOptionEntry entries[])
Definition: cmdline.c:131
pcmk__common_args_t::summary
char * summary
Definition: cmdline_internal.h:20
crm_strdup_printf
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
summary
gboolean summary(GListPtr resources)
PACKAGE_BUGREPORT
#define PACKAGE_BUGREPORT
Definition: config.h:523
pcmk__common_args_t::version
gboolean version
Definition: cmdline_internal.h:23
pcmk__common_args_t::output_ty
char * output_ty
Definition: cmdline_internal.h:27
CRM_ASSERT
#define CRM_ASSERT(expr)
Definition: results.h:42
pcmk__cmdline_preproc
char ** pcmk__cmdline_preproc(int argc, char **argv, const char *special)
Definition: cmdline.c:143
config.h
pcmk__common_args_t::output_as_descr
char * output_as_descr
Definition: cmdline_internal.h:21
pcmk__common_args_t::output_dest
char * output_dest
Definition: cmdline_internal.h:28
crm_errno2exit
crm_exit_t crm_errno2exit(int rc)
Map an errno to a similar exit status.
Definition: results.c:364
pcmk__free_arg_context
void pcmk__free_arg_context(GOptionContext *context)
Definition: cmdline.c:114
crm_exit
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition: results.c:478
util.h
Utility functions.
crm.h
A dumping ground.