pacemaker  2.0.5-ba59be712
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 #ifndef PCMK__CONFIG_H
15 # define PCMK__CONFIG_H
16 # include <config.h>
17 #endif
18 
19 #include <glib.h>
20 
21 #include <crm/crm.h>
24 #include <crm/common/util.h>
25 
26 static gboolean
27 bump_verbosity(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
28  pcmk__common_args_t *common_args = (pcmk__common_args_t *) data;
29  common_args->verbosity++;
30  return TRUE;
31 }
32 
35 {
36  pcmk__common_args_t *args = NULL;
37 
38  args = calloc(1, sizeof(pcmk__common_args_t));
39  if (args == NULL) {
40  crm_exit(crm_errno2exit(-ENOMEM));
41  }
42 
43  args->summary = strdup(summary);
44  if (args->summary == NULL) {
45  crm_exit(crm_errno2exit(-ENOMEM));
46  }
47 
48  return args;
49 }
50 
51 static void
52 free_common_args(gpointer data) {
53  pcmk__common_args_t *common_args = (pcmk__common_args_t *) data;
54 
55  free(common_args->summary);
56  free(common_args->output_ty);
57  free(common_args->output_dest);
58 
59  if (common_args->output_as_descr != NULL) {
60  free(common_args->output_as_descr);
61  }
62 
63  free(common_args);
64 }
65 
66 GOptionContext *
67 pcmk__build_arg_context(pcmk__common_args_t *common_args, const char *fmts,
68  GOptionGroup **output_group, const char *param_string) {
69  char *desc = crm_strdup_printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
70  GOptionContext *context;
71  GOptionGroup *main_group;
72 
73  GOptionEntry main_entries[3] = {
74  { "version", '$', 0, G_OPTION_ARG_NONE, &(common_args->version),
75  "Display software version and exit",
76  NULL },
77  { "verbose", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, bump_verbosity,
78  "Increase debug output (may be specified multiple times)",
79  NULL },
80 
81  { NULL }
82  };
83 
84  main_group = g_option_group_new(NULL, "Application Options:", NULL, common_args, free_common_args);
85  g_option_group_add_entries(main_group, main_entries);
86 
87  context = g_option_context_new(param_string);
88  g_option_context_set_summary(context, common_args->summary);
89  g_option_context_set_description(context, desc);
90  g_option_context_set_main_group(context, main_group);
91 
92  if (fmts != NULL) {
93  GOptionEntry output_entries[3] = {
94  { "output-as", 0, 0, G_OPTION_ARG_STRING, &(common_args->output_ty),
95  NULL,
96  "FORMAT" },
97  { "output-to", 0, 0, G_OPTION_ARG_STRING, &(common_args->output_dest),
98  "Specify file name for output (or \"-\" for stdout)", "DEST" },
99 
100  { NULL }
101  };
102 
103  if (*output_group == NULL) {
104  *output_group = g_option_group_new("output", "Output Options:", "Show output help", NULL, NULL);
105  }
106 
107  common_args->output_as_descr = crm_strdup_printf("Specify output format as one of: %s", fmts);
108  output_entries[0].description = common_args->output_as_descr;
109  g_option_group_add_entries(*output_group, output_entries);
110  g_option_context_add_group(context, *output_group);
111  }
112 
113  free(desc);
114 
115  // main_group is now owned by context, we don't free it here
116  // cppcheck-suppress memleak
117  return context;
118 }
119 
120 void
121 pcmk__free_arg_context(GOptionContext *context) {
122  if (context == NULL) {
123  return;
124  }
125 
126  g_option_context_free(context);
127 }
128 
129 void
130 pcmk__add_main_args(GOptionContext *context, GOptionEntry entries[])
131 {
132  GOptionGroup *main_group = g_option_context_get_main_group(context);
133 
134  g_option_group_add_entries(main_group, entries);
135 }
136 
137 void
138 pcmk__add_arg_group(GOptionContext *context, const char *name,
139  const char *header, const char *desc,
140  GOptionEntry entries[])
141 {
142  GOptionGroup *group = NULL;
143 
144  group = g_option_group_new(name, header, desc, NULL, NULL);
145  g_option_group_add_entries(group, entries);
146  g_option_context_add_group(context, group);
147  // group is now owned by context, we don't free it here
148  // cppcheck-suppress memleak
149 }
150 
151 gchar **
152 pcmk__cmdline_preproc(char **argv, const char *special) {
153  gchar **retval = NULL;
154  GPtrArray *arr = NULL;
155  bool saw_dash_dash = false;
156 
157  if (argv == NULL) {
158  return retval;
159  }
160 
161  if (g_get_prgname() == NULL && argv && *argv) {
162  gchar *basename = g_path_get_basename(*argv);
163 
164  g_set_prgname(basename);
165  g_free(basename);
166  }
167 
168  arr = g_ptr_array_new();
169 
170  for (int i = 0; argv[i] != NULL; i++) {
171  /* If this is the first time we saw "--" in the command line, set
172  * a flag so we know to just copy everything after it over. We also
173  * want to copy the "--" over so whatever actually parses the command
174  * line when we're done knows where arguments end.
175  */
176  if (saw_dash_dash == false && strcmp(argv[i], "--") == 0) {
177  saw_dash_dash = true;
178  }
179 
180  if (saw_dash_dash == true) {
181  g_ptr_array_add(arr, g_strdup(argv[i]));
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  while (*ch != '\0') {
201  /* This is a special short argument that takes an option. getopt
202  * allows values to be interspersed with a list of arguments, but
203  * glib does not. Grab both the argument and its value and
204  * separate them into a new argument.
205  */
206  if (strchr(special, *ch) != NULL) {
207  /* The argument does not occur at the end of this string of
208  * arguments. Take everything through the end as its value.
209  */
210  if (*(ch+1) != '\0') {
211  g_ptr_array_add(arr, g_strdup_printf("-%c", *ch));
212  g_ptr_array_add(arr, g_strdup(ch+1));
213  break;
214 
215  /* The argument occurs at the end of this string. Hopefully
216  * whatever comes next in argv is its value. It may not be,
217  * but that is not for us to decide.
218  */
219  } else {
220  g_ptr_array_add(arr, g_strdup_printf("-%c", *ch));
221  ch++;
222  }
223 
224  /* This is a regular short argument. Just copy it over. */
225  } else {
226  g_ptr_array_add(arr, g_strdup_printf("-%c", *ch));
227  ch++;
228  }
229  }
230 
231  /* This is a long argument, or an option, or something else.
232  * Copy it over - everything else is copied, so this keeps it easy for
233  * the caller to know what to do with the memory when it's done.
234  */
235  } else {
236  g_ptr_array_add(arr, g_strdup(argv[i]));
237  }
238  }
239 
240  /* Convert the GPtrArray into a gchar **, which the command line parsing
241  * code knows how to deal with. Then we can free the array (but not its
242  * contents).
243  */
244  retval = calloc(arr->len+1, sizeof(char *));
245  for (int i = 0; i < arr->len; i++) {
246  retval[i] = (gchar *) g_ptr_array_index(arr, i);
247  }
248 
249  g_ptr_array_free(arr, FALSE);
250 
251  return retval;
252 }
253 
254 G_GNUC_PRINTF(3, 4)
255 gboolean
256 pcmk__force_args(GOptionContext *context, GError **error, const char *format, ...) {
257  int len = 0;
258  char *buf = NULL;
259  gchar **extra_args = NULL;
260  va_list ap;
261  gboolean retval = TRUE;
262 
263  va_start(ap, format);
264  len = vasprintf(&buf, format, ap);
265  CRM_ASSERT(len > 0);
266  va_end(ap);
267 
268  if (!g_shell_parse_argv(buf, NULL, &extra_args, error)) {
269  g_strfreev(extra_args);
270  free(buf);
271  return FALSE;
272  }
273 
274  retval = g_option_context_parse_strv(context, &extra_args, error);
275 
276  g_strfreev(extra_args);
277  free(buf);
278  return retval;
279 }
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:152
_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:138
GOptionContext * pcmk__build_arg_context(pcmk__common_args_t *common_args, const char *fmts, GOptionGroup **output_group, const char *param_string)
Definition: cmdline.c:67
Utility functions.
pcmk__common_args_t * pcmk__new_common_args(const char *summary)
Definition: cmdline.c:34
void pcmk__free_arg_context(GOptionContext *context)
Definition: cmdline.c:121
gboolean pcmk__force_args(GOptionContext *context, GError **error, const char *format,...)
Definition: cmdline.c:256
#define CRM_ASSERT(expr)
Definition: results.h:42
unsigned int verbosity
char data[0]
Definition: internal.h:90
#define PACKAGE_BUGREPORT
Definition: config.h:520
void pcmk__add_main_args(GOptionContext *context, GOptionEntry entries[])
Definition: cmdline.c:130
char * name
Definition: pcmk_fence.c:31
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
gboolean summary(GListPtr resources)