pacemaker  2.1.4-dc6eb4362
Scalable High-Availability cluster resource manager
cmdline.c
Go to the documentation of this file.
1 /*
2  * Copyright 2019-2021 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 #include <crm_internal.h>
11 
12 #include <ctype.h>
13 #include <glib.h>
14 
15 #include <crm/crm.h>
18 #include <crm/common/util.h>
19 
20 static gboolean
21 bump_verbosity(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
22  pcmk__common_args_t *common_args = (pcmk__common_args_t *) data;
23  common_args->verbosity++;
24  return TRUE;
25 }
26 
28 pcmk__new_common_args(const char *summary)
29 {
30  pcmk__common_args_t *args = NULL;
31 
32  args = calloc(1, sizeof(pcmk__common_args_t));
33  if (args == NULL) {
35  }
36 
37  args->summary = strdup(summary);
38  if (args->summary == NULL) {
39  free(args);
40  args = NULL;
42  }
43 
44  return args;
45 }
46 
47 static void
48 free_common_args(gpointer data) {
49  pcmk__common_args_t *common_args = (pcmk__common_args_t *) data;
50 
51  free(common_args->summary);
52  free(common_args->output_ty);
53  free(common_args->output_dest);
54 
55  if (common_args->output_as_descr != NULL) {
56  free(common_args->output_as_descr);
57  }
58 
59  free(common_args);
60 }
61 
62 GOptionContext *
63 pcmk__build_arg_context(pcmk__common_args_t *common_args, const char *fmts,
64  GOptionGroup **output_group, const char *param_string) {
65  char *desc = crm_strdup_printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
66  GOptionContext *context;
67  GOptionGroup *main_group;
68 
69  GOptionEntry main_entries[3] = {
70  { "version", '$', 0, G_OPTION_ARG_NONE, &(common_args->version),
71  "Display software version and exit",
72  NULL },
73  { "verbose", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, bump_verbosity,
74  "Increase debug output (may be specified multiple times)",
75  NULL },
76 
77  { NULL }
78  };
79 
80  main_group = g_option_group_new(NULL, "Application Options:", NULL, common_args, free_common_args);
81  g_option_group_add_entries(main_group, main_entries);
82 
83  context = g_option_context_new(param_string);
84  g_option_context_set_summary(context, common_args->summary);
85  g_option_context_set_description(context, desc);
86  g_option_context_set_main_group(context, main_group);
87 
88  if (fmts != NULL) {
89  GOptionEntry output_entries[3] = {
90  { "output-as", 0, 0, G_OPTION_ARG_STRING, &(common_args->output_ty),
91  NULL,
92  "FORMAT" },
93  { "output-to", 0, 0, G_OPTION_ARG_STRING, &(common_args->output_dest),
94  "Specify file name for output (or \"-\" for stdout)", "DEST" },
95 
96  { NULL }
97  };
98 
99  if (*output_group == NULL) {
100  *output_group = g_option_group_new("output", "Output Options:", "Show output help", NULL, NULL);
101  }
102 
103  common_args->output_as_descr = crm_strdup_printf("Specify output format as one of: %s", fmts);
104  output_entries[0].description = common_args->output_as_descr;
105  g_option_group_add_entries(*output_group, output_entries);
106  g_option_context_add_group(context, *output_group);
107  }
108 
109  free(desc);
110 
111  // main_group is now owned by context, we don't free it here
112  // cppcheck-suppress memleak
113  return context;
114 }
115 
116 void
117 pcmk__free_arg_context(GOptionContext *context) {
118  if (context == NULL) {
119  return;
120  }
121 
122  g_option_context_free(context);
123 }
124 
125 void
126 pcmk__add_main_args(GOptionContext *context, GOptionEntry entries[])
127 {
128  GOptionGroup *main_group = g_option_context_get_main_group(context);
129 
130  g_option_group_add_entries(main_group, entries);
131 }
132 
133 void
134 pcmk__add_arg_group(GOptionContext *context, const char *name,
135  const char *header, const char *desc,
136  GOptionEntry entries[])
137 {
138  GOptionGroup *group = NULL;
139 
140  group = g_option_group_new(name, header, desc, NULL, NULL);
141  g_option_group_add_entries(group, entries);
142  g_option_context_add_group(context, group);
143  // group is now owned by context, we don't free it here
144  // cppcheck-suppress memleak
145 }
146 
147 gchar **
148 pcmk__cmdline_preproc(char **argv, const char *special) {
149  GPtrArray *arr = NULL;
150  bool saw_dash_dash = false;
151  bool copy_option = false;
152 
153  if (argv == NULL) {
154  return NULL;
155  }
156 
157  if (g_get_prgname() == NULL && argv && *argv) {
158  gchar *basename = g_path_get_basename(*argv);
159 
160  g_set_prgname(basename);
161  g_free(basename);
162  }
163 
164  arr = g_ptr_array_new();
165 
166  for (int i = 0; argv[i] != NULL; i++) {
167  /* If this is the first time we saw "--" in the command line, set
168  * a flag so we know to just copy everything after it over. We also
169  * want to copy the "--" over so whatever actually parses the command
170  * line when we're done knows where arguments end.
171  */
172  if (saw_dash_dash == false && strcmp(argv[i], "--") == 0) {
173  saw_dash_dash = true;
174  }
175 
176  if (saw_dash_dash == true) {
177  g_ptr_array_add(arr, g_strdup(argv[i]));
178  continue;
179  }
180 
181  if (copy_option == true) {
182  g_ptr_array_add(arr, g_strdup(argv[i]));
183  copy_option = false;
184  continue;
185  }
186 
187  /* This is just a dash by itself. That could indicate stdin/stdout, or
188  * it could be user error. Copy it over and let glib figure it out.
189  */
190  if (pcmk__str_eq(argv[i], "-", pcmk__str_casei)) {
191  g_ptr_array_add(arr, g_strdup(argv[i]));
192  continue;
193  }
194 
195  /* This is a short argument, or perhaps several. Iterate over it
196  * and explode them out into individual arguments.
197  */
198  if (g_str_has_prefix(argv[i], "-") && !g_str_has_prefix(argv[i], "--")) {
199  /* Skip over leading dash */
200  char *ch = argv[i]+1;
201 
202  /* This looks like the start of a number, which means it is a negative
203  * number. It's probably the argument to the preceeding option, but
204  * we can't know that here. Copy it over and let whatever handles
205  * arguments next figure it out.
206  */
207  if (*ch != '\0' && *ch >= '1' && *ch <= '9') {
208  bool is_numeric = true;
209 
210  while (*ch != '\0') {
211  if (!isdigit(*ch)) {
212  is_numeric = false;
213  break;
214  }
215 
216  ch++;
217  }
218 
219  if (is_numeric) {
220  g_ptr_array_add(arr, g_strdup_printf("%s", argv[i]));
221  continue;
222  } else {
223  /* This argument wasn't entirely numeric. Reset ch to the
224  * beginning so we can process it one character at a time.
225  */
226  ch = argv[i]+1;
227  }
228  }
229 
230  while (*ch != '\0') {
231  /* This is a special short argument that takes an option. getopt
232  * allows values to be interspersed with a list of arguments, but
233  * glib does not. Grab both the argument and its value and
234  * separate them into a new argument.
235  */
236  if (special != NULL && strchr(special, *ch) != NULL) {
237  /* The argument does not occur at the end of this string of
238  * arguments. Take everything through the end as its value.
239  */
240  if (*(ch+1) != '\0') {
241  g_ptr_array_add(arr, g_strdup_printf("-%c", *ch));
242  g_ptr_array_add(arr, g_strdup(ch+1));
243  break;
244 
245  /* The argument occurs at the end of this string. Hopefully
246  * whatever comes next in argv is its value. It may not be,
247  * but that is not for us to decide.
248  */
249  } else {
250  g_ptr_array_add(arr, g_strdup_printf("-%c", *ch));
251  copy_option = true;
252  ch++;
253  }
254 
255  /* This is a regular short argument. Just copy it over. */
256  } else {
257  g_ptr_array_add(arr, g_strdup_printf("-%c", *ch));
258  ch++;
259  }
260  }
261 
262  /* This is a long argument, or an option, or something else.
263  * Copy it over - everything else is copied, so this keeps it easy for
264  * the caller to know what to do with the memory when it's done.
265  */
266  } else {
267  g_ptr_array_add(arr, g_strdup(argv[i]));
268  }
269  }
270 
271  g_ptr_array_add(arr, NULL);
272 
273  return (char **) g_ptr_array_free(arr, FALSE);
274 }
275 
276 G_GNUC_PRINTF(3, 4)
277 gboolean
278 pcmk__force_args(GOptionContext *context, GError **error, const char *format, ...) {
279  int len = 0;
280  char *buf = NULL;
281  gchar **extra_args = NULL;
282  va_list ap;
283  gboolean retval = TRUE;
284 
285  va_start(ap, format);
286  len = vasprintf(&buf, format, ap);
287  CRM_ASSERT(len > 0);
288  va_end(ap);
289 
290  if (!g_shell_parse_argv(buf, NULL, &extra_args, error)) {
291  g_strfreev(extra_args);
292  free(buf);
293  return FALSE;
294  }
295 
296  retval = g_option_context_parse_strv(context, &extra_args, error);
297 
298  g_strfreev(extra_args);
299  free(buf);
300  return retval;
301 }
A dumping ground.
gchar ** pcmk__cmdline_preproc(char **argv, const char *special)
Definition: cmdline.c:148
char data[0]
Definition: cpg.c:55
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition: results.c:775
const char * name
Definition: cib.c:24
void pcmk__add_arg_group(GOptionContext *context, const char *name, const char *header, const char *desc, GOptionEntry entries[])
Definition: cmdline.c:134
GOptionContext * pcmk__build_arg_context(pcmk__common_args_t *common_args, const char *fmts, GOptionGroup **output_group, const char *param_string)
Definition: cmdline.c:63
Utility functions.
External (OS/environmental) problem.
Definition: results.h:258
pcmk__common_args_t * pcmk__new_common_args(const char *summary)
Definition: cmdline.c:28
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
void pcmk__free_arg_context(GOptionContext *context)
Definition: cmdline.c:117
gboolean pcmk__force_args(GOptionContext *context, GError **error, const char *format,...)
Definition: cmdline.c:278
#define CRM_ASSERT(expr)
Definition: results.h:42
unsigned int verbosity
#define PACKAGE_BUGREPORT
Definition: config.h:510
void pcmk__add_main_args(GOptionContext *context, GOptionEntry entries[])
Definition: cmdline.c:126