pacemaker  2.0.4-2deceaa
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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, const char *param_string) {
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(param_string);
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  // main_group is now owned by context, we don't free it here
111  // cppcheck-suppress memleak
112  return context;
113 }
114 
115 void
116 pcmk__free_arg_context(GOptionContext *context) {
117  if (context == NULL) {
118  return;
119  }
120 
121  g_option_context_free(context);
122 }
123 
124 void
125 pcmk__add_main_args(GOptionContext *context, GOptionEntry entries[])
126 {
127  GOptionGroup *main_group = g_option_context_get_main_group(context);
128 
129  g_option_group_add_entries(main_group, entries);
130 }
131 
132 void
133 pcmk__add_arg_group(GOptionContext *context, const char *name,
134  const char *header, const char *desc,
135  GOptionEntry entries[])
136 {
137  GOptionGroup *group = NULL;
138 
139  group = g_option_group_new(name, header, desc, NULL, NULL);
140  g_option_group_add_entries(group, entries);
141  g_option_context_add_group(context, group);
142  // group is now owned by context, we don't free it here
143  // cppcheck-suppress memleak
144 }
145 
146 gchar **
147 pcmk__cmdline_preproc(char **argv, const char *special) {
148  gchar **retval = NULL;
149  GPtrArray *arr = NULL;
150  bool saw_dash_dash = false;
151 
152  if (argv == NULL) {
153  return retval;
154  }
155 
156  if (g_get_prgname() == NULL && argv && *argv) {
157  gchar *basename = g_path_get_basename(*argv);
158 
159  g_set_prgname(basename);
160  g_free(basename);
161  }
162 
163  arr = g_ptr_array_new();
164 
165  for (int i = 0; argv[i] != NULL; i++) {
166  /* If this is the first time we saw "--" in the command line, set
167  * a flag so we know to just copy everything after it over. We also
168  * want to copy the "--" over so whatever actually parses the command
169  * line when we're done knows where arguments end.
170  */
171  if (saw_dash_dash == false && strcmp(argv[i], "--") == 0) {
172  saw_dash_dash = true;
173  }
174 
175  if (saw_dash_dash == true) {
176  g_ptr_array_add(arr, strdup(argv[i]));
177  continue;
178  }
179 
180  /* This is just a dash by itself. That could indicate stdin/stdout, or
181  * it could be user error. Copy it over and let glib figure it out.
182  */
183  if (safe_str_eq(argv[i], "-")) {
184  g_ptr_array_add(arr, strdup(argv[i]));
185  continue;
186  }
187 
188  /* This is a short argument, or perhaps several. Iterate over it
189  * and explode them out into individual arguments.
190  */
191  if (g_str_has_prefix(argv[i], "-") && !g_str_has_prefix(argv[i], "--")) {
192  /* Skip over leading dash */
193  char *ch = argv[i]+1;
194 
195  while (*ch != '\0') {
196  /* This is a special short argument that takes an option. getopt
197  * allows values to be interspersed with a list of arguments, but
198  * glib does not. Grab both the argument and its value and
199  * separate them into a new argument.
200  */
201  if (strchr(special, *ch) != NULL) {
202  /* The argument does not occur at the end of this string of
203  * arguments. Take everything through the end as its value.
204  */
205  if (*(ch+1) != '\0') {
206  g_ptr_array_add(arr, (gpointer) crm_strdup_printf("-%c", *ch));
207  g_ptr_array_add(arr, strdup(ch+1));
208  break;
209 
210  /* The argument occurs at the end of this string. Hopefully
211  * whatever comes next in argv is its value. It may not be,
212  * but that is not for us to decide.
213  */
214  } else {
215  g_ptr_array_add(arr, (gpointer) crm_strdup_printf("-%c", *ch));
216  ch++;
217  }
218 
219  /* This is a regular short argument. Just copy it over. */
220  } else {
221  g_ptr_array_add(arr, (gpointer) crm_strdup_printf("-%c", *ch));
222  ch++;
223  }
224  }
225 
226  /* This is a long argument, or an option, or something else.
227  * Copy it over - everything else is copied, so this keeps it easy for
228  * the caller to know what to do with the memory when it's done.
229  */
230  } else {
231  g_ptr_array_add(arr, strdup(argv[i]));
232  }
233  }
234 
235  /* Convert the GPtrArray into a gchar **, which the command line parsing
236  * code knows how to deal with. Then we can free the array (but not its
237  * contents).
238  */
239  retval = calloc(arr->len+1, sizeof(char *));
240  for (int i = 0; i < arr->len; i++) {
241  retval[i] = (gchar *) g_ptr_array_index(arr, i);
242  }
243 
244  g_ptr_array_free(arr, FALSE);
245 
246  return retval;
247 }
248 
249 G_GNUC_PRINTF(3, 4)
250 gboolean
251 pcmk__force_args(GOptionContext *context, GError **error, const char *format, ...) {
252  int len = 0;
253  char *buf = NULL;
254  gchar **extra_args = NULL;
255  va_list ap;
256  gboolean retval = TRUE;
257 
258  va_start(ap, format);
259  len = vasprintf(&buf, format, ap);
260  CRM_ASSERT(len > 0);
261  va_end(ap);
262 
263  if (!g_shell_parse_argv(buf, NULL, &extra_args, error)) {
264  g_strfreev(extra_args);
265  free(buf);
266  return FALSE;
267  }
268 
269  retval = g_option_context_parse_strv(context, &extra_args, error);
270 
271  g_strfreev(extra_args);
272  free(buf);
273  return retval;
274 }
A dumping ground.
crm_exit_t crm_errno2exit(int rc)
Definition: results.c:570
pcmk__common_args_t * pcmk__new_common_args(const char *summary)
Definition: cmdline.c:29
gchar ** pcmk__cmdline_preproc(char **argv, const char *special)
Definition: cmdline.c:147
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition: results.c:751
Utility functions.
GOptionContext * pcmk__build_arg_context(pcmk__common_args_t *common_args, const char *fmts, GOptionGroup **output_group, const char *param_string)
Definition: cmdline.c:62
void pcmk__free_arg_context(GOptionContext *context)
Definition: cmdline.c:116
gboolean pcmk__force_args(GOptionContext *context, GError **error, const char *format,...)
Definition: cmdline.c:251
void pcmk__add_arg_group(GOptionContext *context, const char *name, const char *header, const char *desc, GOptionEntry entries[])
Definition: cmdline.c:133
#define CRM_ASSERT(expr)
Definition: results.h:42
unsigned int verbosity
char data[0]
Definition: internal.h:90
void pcmk__add_main_args(GOptionContext *context, GOptionEntry entries[])
Definition: cmdline.c:125
#define PACKAGE_BUGREPORT
Definition: config.h:515
#define safe_str_eq(a, b)
Definition: util.h:65
char * name
Definition: pcmk_fence.c:30
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
gboolean summary(GListPtr resources)