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