/* nmcli - command-line tool to control NetworkManager * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * (C) Copyright 2010 - 2014 Red Hat, Inc. */ /* Generated configuration file */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "utils.h" int matches (const char *cmd, const char *pattern) { size_t len = strlen (cmd); if (!len || len > strlen (pattern)) return -1; return memcmp (pattern, cmd, len); } int next_arg (int *argc, char ***argv) { int arg_num = *argc; if (arg_num > 0) { (*argc)--; (*argv)++; } if (arg_num <= 1) return -1; return 0; } gboolean nmc_arg_is_help (const char *arg) { if (!arg) return FALSE; if ( matches (arg, "help") == 0 || (g_str_has_prefix (arg, "-") && matches (arg+1, "help") == 0) || (g_str_has_prefix (arg, "--") && matches (arg+2, "help") == 0)) { return TRUE; } return FALSE; } gboolean nmc_arg_is_option (const char *str, const char *opt_name) { const char *p; if (!str || !*str) return FALSE; if (str[0] != '-') return FALSE; p = (str[1] == '-') ? str + 2 : str + 1; return (*p ? (matches (p, opt_name) == 0) : FALSE); } /* * Helper function to parse command-line arguments. * arg_arr: description of arguments to look for * last: whether these are last expected arguments * argc: command-line argument array * argv: command-line argument array size * error: error set on a failure (when FALSE is returned) * Returns: TRUE on success, FALSE on an error and sets 'error' */ gboolean nmc_parse_args (nmc_arg_t *arg_arr, gboolean last, int *argc, char ***argv, GError **error) { nmc_arg_t *p; gboolean found; gboolean have_mandatory; g_return_val_if_fail (arg_arr != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); while (*argc > 0) { found = FALSE; for (p = arg_arr; p->name; p++) { if (strcmp (**argv, p->name) == 0) { if (p->found) { /* Don't allow repeated arguments, because the argument of the same * name could be used later on the line for another purpose. Assume * that's the case and return. */ return TRUE; } if (p->has_value) { if (next_arg (argc, argv) != 0) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: value for '%s' argument is required."), *(*argv-1)); return FALSE; } *(p->value) = **argv; } p->found = TRUE; found = TRUE; break; } } if (!found) { have_mandatory = TRUE; for (p = arg_arr; p->name; p++) { if (p->mandatory && !p->found) { have_mandatory = FALSE; break; } } if (have_mandatory && !last) return TRUE; if (p->name) g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: Argument '%s' was expected, but '%s' provided."), p->name, **argv); else g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: Unexpected argument '%s'"), **argv); return FALSE; } next_arg (argc, argv); } return TRUE; } /* * Convert SSID to a hex string representation. * Caller has to free the returned string using g_free() */ char * ssid_to_hex (const char *str, gsize len) { GString *printable; char *printable_str; int i; if (str == NULL || len == 0) return NULL; printable = g_string_new (NULL); for (i = 0; i < len; i++) { g_string_append_printf (printable, "%02X", (unsigned char) str[i]); } printable_str = g_string_free (printable, FALSE); return printable_str; } /* * Converts IPv4 address from guint32 in network-byte order to text representation. * Returns: text form of the IP or NULL (then error is set) */ char * nmc_ip4_address_as_string (guint32 ip, GError **error) { guint32 tmp_addr; char buf[INET_ADDRSTRLEN]; g_return_val_if_fail (error == NULL || *error == NULL, NULL); memset (&buf, '\0', sizeof (buf)); tmp_addr = ip; if (inet_ntop (AF_INET, &tmp_addr, buf, INET_ADDRSTRLEN)) { return g_strdup (buf); } else { g_set_error (error, NMCLI_ERROR, 0, _("Error converting IP4 address '0x%X' to text form"), ntohl (tmp_addr)); return NULL; } } /* * Converts IPv6 address in in6_addr structure to text representation. * Returns: text form of the IP or NULL (then error is set) */ char * nmc_ip6_address_as_string (const struct in6_addr *ip, GError **error) { char buf[INET6_ADDRSTRLEN]; g_return_val_if_fail (error == NULL || *error == NULL, NULL); memset (&buf, '\0', sizeof (buf)); if (inet_ntop (AF_INET6, ip, buf, INET6_ADDRSTRLEN)) { return g_strdup (buf); } else { if (error) { int j; GString *ip6_str = g_string_new (NULL); g_string_append_printf (ip6_str, "%02X", ip->s6_addr[0]); for (j = 1; j < 16; j++) g_string_append_printf (ip6_str, " %02X", ip->s6_addr[j]); g_set_error (error, NMCLI_ERROR, 0, _("Error converting IP6 address '%s' to text form"), ip6_str->str); g_string_free (ip6_str, TRUE); } return NULL; } } /* * Erase terminal line using ANSI escape sequences. * It prints [2K sequence to erase the line and then \r to return back * to the beginning of the line. * * http://www.termsys.demon.co.uk/vtansi.htm */ void nmc_terminal_erase_line (void) { printf ("\33[2K\r"); fflush (stdout); } /* * Print animated progress for an operation. * Repeated calls of the function will show rotating slash in terminal followed * by the string passed in 'str' argument. */ void nmc_terminal_show_progress (const char *str) { static int idx = 0; const char slashes[4] = {'|', '/', '-', '\\'}; nmc_terminal_erase_line (); printf ("%c %s", slashes[idx++], str ? str : ""); fflush (stdout); if (idx == 4) idx = 0; } const char * nmc_term_color_sequence (NmcTermColor color) { switch (color) { case NMC_TERM_COLOR_BLACK: return "\33[30m"; break; case NMC_TERM_COLOR_RED: return "\33[31m"; break; case NMC_TERM_COLOR_GREEN: return "\33[32m"; break; case NMC_TERM_COLOR_YELLOW: return "\33[33m"; break; case NMC_TERM_COLOR_BLUE: return "\33[34m"; break; case NMC_TERM_COLOR_MAGENTA: return "\33[35m"; break; case NMC_TERM_COLOR_CYAN: return "\33[36m"; break; case NMC_TERM_COLOR_WHITE: return "\33[37m"; break; default: return ""; break; } } char * nmc_colorize (NmcTermColor color, const char *fmt, ...) { va_list args; char *str, *colored; const char *ansi_color, *color_end; va_start (args, fmt); str = g_strdup_vprintf (fmt, args); va_end (args); ansi_color = nmc_term_color_sequence (color); if (*ansi_color) color_end = "\33[0m"; else color_end = ""; colored = g_strdup_printf ("%s%s%s", ansi_color, str, color_end); g_free (str); return colored; } /* * Convert string to signed integer. * If required, the resulting number is checked to be in the range. */ gboolean nmc_string_to_int_base (const char *str, int base, gboolean range_check, long int min, long int max, long int *value) { char *end; long int tmp; errno = 0; tmp = strtol (str, &end, base); if (errno || *end != '\0' || (range_check && (tmp < min || tmp > max))) { return FALSE; } *value = tmp; return TRUE; } /* * Convert string to unsigned integer. * If required, the resulting number is checked to be in the range. */ gboolean nmc_string_to_uint_base (const char *str, int base, gboolean range_check, unsigned long int min, unsigned long int max, unsigned long int *value) { char *end; unsigned long int tmp; errno = 0; tmp = strtoul (str, &end, base); if (errno || *end != '\0' || (range_check && (tmp < min || tmp > max))) { return FALSE; } *value = tmp; return TRUE; } gboolean nmc_string_to_int (const char *str, gboolean range_check, long int min, long int max, long int *value) { return nmc_string_to_int_base (str, 10, range_check, min, max, value); } gboolean nmc_string_to_uint (const char *str, gboolean range_check, unsigned long int min, unsigned long int max, unsigned long int *value) { return nmc_string_to_uint_base (str, 10, range_check, min, max, value); } gboolean nmc_string_to_bool (const char *str, gboolean *val_bool, GError **error) { const char *s_true[] = { "true", "yes", "on", NULL }; const char *s_false[] = { "false", "no", "off", NULL }; g_return_val_if_fail (error == NULL || *error == NULL, FALSE); if (g_strcmp0 (str, "o") == 0) { g_set_error (error, 1, 0, _("'%s' is ambiguous (on x off)"), str); return FALSE; } if (nmc_string_is_valid (str, s_true, NULL)) *val_bool = TRUE; else if (nmc_string_is_valid (str, s_false, NULL)) *val_bool = FALSE; else { g_set_error (error, 1, 0, _("'%s' is not valid; use [%s] or [%s]"), str, "true, yes, on", "false, no, off"); return FALSE; } return TRUE; } /* * Ask user for input and return the string. * The caller is responsible for freeing the returned string. */ char * nmc_get_user_input (const char *ask_str) { char *line = NULL; size_t line_ln = 0; ssize_t num; fprintf (stdout, "%s", ask_str); num = getline (&line, &line_ln, stdin); /* Remove newline from the string */ if (num < 1 || (num == 1 && line[0] == '\n')) { g_free (line); line = NULL; } else { if (line[num-1] == '\n') line[num-1] = '\0'; } return line; } /* * Split string in 'line' according to 'delim' to (argument) array. */ int nmc_string_to_arg_array (const char *line, const char *delim, char ***argv, int *argc) { int i = 0; char **arr; arr = g_strsplit_set (line ? line : "", delim ? delim : " \t", 0); while (arr && arr[i]) i++; *argc = i; *argv = arr; return 0; } /* * Check whether 'input' is contained in 'allowed' array. It performs case * insensitive comparison and supports shortcut strings if they are unique. * Returns: a pointer to found string in allowed array on success or NULL. * On failure: error->code : 0 - string not found; 1 - string is ambiguous */ const char * nmc_string_is_valid (const char *input, const char **allowed, GError **error) { const char **p; size_t input_ln, p_len; gboolean prev_match = FALSE; const char *ret = NULL; g_return_val_if_fail (error == NULL || *error == NULL, NULL); if (!input || !*input) goto finish; input_ln = strlen (input); for (p = allowed; p && *p; p++) { p_len = strlen (*p); if (g_ascii_strncasecmp (input, *p, input_ln) == 0) { if (input_ln == p_len) { ret = *p; break; } if (!prev_match) ret = *p; else { g_set_error (error, 1, 1, _("'%s' is ambiguous (%s x %s)"), input, ret, *p); return NULL; } prev_match = TRUE; } } finish: if (ret == NULL) { char *valid_vals = g_strjoinv (", ", (char **) allowed); if (!input || !*input) { g_set_error (error, 1, 0, _("missing name, try one of [%s]"), valid_vals); } else { g_set_error (error, 1, 0, _("'%s' not among [%s]"), input ? input : "", valid_vals); } g_free (valid_vals); } return ret; } /* * Convert string array (char **) to GSList. * * Returns: pointer to newly created GSList. Caller should free it. */ GSList * nmc_util_strv_to_slist (char **strv) { GSList *list = NULL; guint i = 0; while (strv && strv[i]) list = g_slist_prepend (list, g_strdup (strv[i++])); return g_slist_reverse (list); } /* * Wrapper function for g_strsplit_set() that removes empty strings * from the vector as they are not useful in most cases. */ char ** nmc_strsplit_set (const char *str, const char *delimiter, int max_tokens) { char **result; uint i; uint j; result = g_strsplit_set (str, delimiter, max_tokens); /* remove empty strings */ for (i = 0; result && result[i]; i++) { if (*(result[i]) == '\0') { g_free (result[i]); for (j = i; result[j]; j++) result[j] = result[j + 1]; i--; } } return result; } /* * Find out how many columns an UTF-8 string occupies on the screen */ int nmc_string_screen_width (const char *start, const char *end) { int width = 0; if (end == NULL) end = start + strlen (start); while (start < end) { width += g_unichar_iswide (g_utf8_get_char (start)) ? 2 : g_unichar_iszerowidth (g_utf8_get_char (start)) ? 0 : 1; start = g_utf8_next_char (start); } return width; } void set_val_str (NmcOutputField fields_array[], guint32 idx, char *value) { fields_array[idx].value = value; fields_array[idx].value_is_array = FALSE; fields_array[idx].free_value = TRUE; } void set_val_strc (NmcOutputField fields_array[], guint32 idx, const char *value) { fields_array[idx].value = (char *) value; fields_array[idx].value_is_array = FALSE; fields_array[idx].free_value = FALSE; } void set_val_arr (NmcOutputField fields_array[], guint32 idx, char **value) { fields_array[idx].value = value; fields_array[idx].value_is_array = TRUE; fields_array[idx].free_value = TRUE; } void set_val_arrc (NmcOutputField fields_array[], guint32 idx, const char **value) { fields_array[idx].value = (char **) value; fields_array[idx].value_is_array = TRUE; fields_array[idx].free_value = FALSE; } /* * Free 'value' members in array of NmcOutputField */ void nmc_free_output_field_values (NmcOutputField fields_array[]) { NmcOutputField *iter = fields_array; while (iter && iter->name) { if (iter->free_value) { if (iter->value_is_array) g_strfreev ((char **) iter->value); else g_free ((char *) iter->value); iter->value = NULL; } iter++; } } /** * parse_output_fields: * @field_str: comma-separated field names to parse * @fields_array: array of allowed fields * @parse_groups: whether the fields can contain group prefix (e.g. general.driver) * @group_fields: (out) (allow-none): array of field names for particular groups * @error: (out) (allow-none): location to store error, or %NULL * * Parses comma separated fields in @fields_str according to @fields_array. * When @parse_groups is %TRUE, fields can be in the form 'group.field'. Then * @group_fields will be filled with the required field for particular group. * @group_fields array corresponds to the returned array. * Examples: * @field_str: "type,name,uuid" | "ip4,general.device" | "ip4.address,ip6" * returned array: 2 0 1 | 7 0 | 7 9 * @group_fields: NULL NULL NULL | NULL "device" | "address" NULL * * Returns: #GArray with indices representing fields in @fields_array. * Caller is responsible for freeing the array. */ GArray * parse_output_fields (const char *fields_str, const NmcOutputField fields_array[], gboolean parse_groups, GPtrArray **group_fields, GError **error) { char **fields, **iter; GArray *array; int i, j; g_return_val_if_fail (error == NULL || *error == NULL, NULL); g_return_val_if_fail (group_fields == NULL || *group_fields == NULL, NULL); array = g_array_new (FALSE, FALSE, sizeof (int)); if (parse_groups && group_fields) *group_fields = g_ptr_array_new_full (20, (GDestroyNotify) g_free); /* Split supplied fields string */ fields = g_strsplit_set (fields_str, ",", -1); for (iter = fields; iter && *iter; iter++) { int idx = -1; g_strstrip (*iter); if (parse_groups) { /* e.g. "general.device,general.driver,ip4,ip6" */ gboolean found = FALSE; char *left = *iter; char *right = strchr (*iter, '.'); if (right) *right++ = '\0'; for (i = 0; fields_array[i].name; i++) { if (strcasecmp (left, fields_array[i].name) == 0) { NmcOutputField *valid_names = fields_array[i].group; idx = i; if (!right && !valid_names) { found = TRUE; break; } for (j = 0; valid_names && valid_names[j].name; j++) { if (!right || strcasecmp (right, valid_names[j].name) == 0) { found = TRUE; break; } } } if (found) break; } if (found) { /* Add index to array, and field name (or NULL) to group_fields array */ g_array_append_val (array, idx); if (group_fields && *group_fields) g_ptr_array_add (*group_fields, g_strdup (right)); } if (right) *(right-1) = '.'; /* Restore the original string */ } else { /* e.g. "general,ip4,ip6" */ for (i = 0; fields_array[i].name; i++) { if (strcasecmp (*iter, fields_array[i].name) == 0) { g_array_append_val (array, i); break; } } } /* Field was not found - error case */ if (fields_array[i].name == NULL) { /* Set GError */ if (!strcasecmp (*iter, "all") || !strcasecmp (*iter, "common")) g_set_error (error, NMCLI_ERROR, 0, _("field '%s' has to be alone"), *iter); else { char *allowed_fields = nmc_get_allowed_fields (fields_array, idx); g_set_error (error, NMCLI_ERROR, 1, _("invalid field '%s'; allowed fields: %s"), *iter, allowed_fields); g_free (allowed_fields); } /* Free arrays on error */ g_array_free (array, TRUE); array = NULL; if (group_fields && *group_fields) { g_ptr_array_free (*group_fields, TRUE); *group_fields = NULL; } goto done; } } done: if (fields) g_strfreev (fields); return array; } /** * nmc_get_allowed_fields: * @fields_array: array of fields * @group_idx: index to the array (for second-level array in 'group' member), * or -1 * * Returns: string of allowed fields names. * Caller is responsible for freeing the array. */ char * nmc_get_allowed_fields (const NmcOutputField fields_array[], int group_idx) { GString *allowed_fields = g_string_sized_new (256); int i; if (group_idx != -1 && fields_array[group_idx].group) { NmcOutputField *second_level = fields_array[group_idx].group; for (i = 0; second_level[i].name; i++) g_string_append_printf (allowed_fields, "%s.%s,", fields_array[group_idx].name, second_level[i].name); } else { for (i = 0; fields_array[i].name; i++) g_string_append_printf (allowed_fields, "%s,", fields_array[i].name); } g_string_truncate (allowed_fields, allowed_fields->len - 1); return g_string_free (allowed_fields, FALSE); } gboolean nmc_terse_option_check (NMCPrintOutput print_output, const char *fields, GError **error) { g_return_val_if_fail (error == NULL || *error == NULL, FALSE); if (print_output == NMC_PRINT_TERSE) { if (!fields) { g_set_error_literal (error, NMCLI_ERROR, 0, _("Option '--terse' requires specifying '--fields'")); return FALSE; } else if ( !strcasecmp (fields, "all") || !strcasecmp (fields, "common")) { g_set_error (error, NMCLI_ERROR, 0, _("Option '--terse' requires specific '--fields' option values , not '%s'"), fields); return FALSE; } } return TRUE; } NmcOutputField * nmc_dup_fields_array (NmcOutputField fields[], size_t size, guint32 flags) { NmcOutputField *row; row = g_malloc0 (size); memcpy (row, fields, size); row[0].flags = flags; return row; } void nmc_empty_output_fields (NmCli *nmc) { guint i; /* Free values in field structure */ for (i = 0; i < nmc->output_data->len; i++) { NmcOutputField *fld_arr = g_ptr_array_index (nmc->output_data, i); nmc_free_output_field_values (fld_arr); } /* Empty output_data array */ if (nmc->output_data->len > 0) g_ptr_array_remove_range (nmc->output_data, 0, nmc->output_data->len); if (nmc->print_fields.indices) { g_array_free (nmc->print_fields.indices, TRUE); nmc->print_fields.indices = NULL; } } static char * get_value_to_print (NmcOutputField *fields, gboolean field_name, const char *not_set_str, gboolean *dealloc) { gboolean is_array = fields->value_is_array; char *value; if (field_name) value = _(fields->name_l10n); else value = fields->value ? (is_array ? g_strjoinv (" | ", (char **) fields->value) : (char *) fields->value) : (char *) not_set_str; *dealloc = fields->value && is_array && !field_name; return value; } /* * Print both headers or values of 'field_values' array. * Entries to print and their order are specified via indices in * 'nmc->print_fields.indices' array. * Various flags influencing the output of fields are set up in the first item * of 'field_values' array. */ void print_required_fields (NmCli *nmc, const NmcOutputField field_values[]) { GString *str; int width1, width2; int table_width = 0; char *line = NULL; char *indent_str; const char *not_set_str = "--"; int i; const NmcPrintFields fields = nmc->print_fields; gboolean multiline = nmc->multiline_output; gboolean terse = (nmc->print_output == NMC_PRINT_TERSE); gboolean pretty = (nmc->print_output == NMC_PRINT_PRETTY); gboolean escape = nmc->escape_values; gboolean main_header_add = field_values[0].flags & NMC_OF_FLAG_MAIN_HEADER_ADD; gboolean main_header_only = field_values[0].flags & NMC_OF_FLAG_MAIN_HEADER_ONLY; gboolean field_names = field_values[0].flags & NMC_OF_FLAG_FIELD_NAMES; gboolean section_prefix = field_values[0].flags & NMC_OF_FLAG_SECTION_PREFIX; gboolean main_header = main_header_add || main_header_only; /* No headers are printed in terse mode: * - neither main header nor field (column) names */ if ((main_header_only || field_names) && terse) return; if (multiline) { /* --- Multiline mode --- */ enum { ML_HEADER_WIDTH = 79 }; enum { ML_VALUE_INDENT = 40 }; if (main_header && pretty) { /* Print the main header */ int header_width = nmc_string_screen_width (fields.header_name, NULL) + 4; table_width = header_width < ML_HEADER_WIDTH ? ML_HEADER_WIDTH : header_width; line = g_strnfill (ML_HEADER_WIDTH, '='); width1 = strlen (fields.header_name); width2 = nmc_string_screen_width (fields.header_name, NULL); printf ("%s\n", line); printf ("%*s\n", (table_width + width2)/2 + width1 - width2, fields.header_name); printf ("%s\n", line); g_free (line); } /* Print values */ if (!main_header_only && !field_names) { for (i = 0; i < fields.indices->len; i++) { char *tmp; int idx = g_array_index (fields.indices, int, i); gboolean is_array = field_values[idx].value_is_array; /* section prefix can't be an array */ g_assert (!is_array || !section_prefix || idx != 0); if (section_prefix && idx == 0) /* The first field is section prefix */ continue; if (is_array) { /* value is a null-terminated string array */ const char **p; int j; for (p = (const char **) field_values[idx].value, j = 1; p && *p; p++, j++) { tmp = g_strdup_printf ("%s%s%s[%d]:", section_prefix ? (const char*) field_values[0].value : "", section_prefix ? "." : "", _(field_values[idx].name_l10n), j); width1 = strlen (tmp); width2 = nmc_string_screen_width (tmp, NULL); printf ("%-*s%s\n", terse ? 0 : ML_VALUE_INDENT+width1-width2, tmp, *p ? *p : not_set_str); g_free (tmp); } } else { /* value is a string */ const char *hdr_name = (const char*) field_values[0].value; const char *val = (const char*) field_values[idx].value; tmp = g_strdup_printf ("%s%s%s:", section_prefix ? hdr_name : "", section_prefix ? "." : "", _(field_values[idx].name_l10n)); width1 = strlen (tmp); width2 = nmc_string_screen_width (tmp, NULL); printf ("%-*s%s\n", terse ? 0 : ML_VALUE_INDENT+width1-width2, tmp, val ? val : not_set_str); g_free (tmp); } } if (pretty) { line = g_strnfill (ML_HEADER_WIDTH, '-'); printf ("%s\n", line); g_free (line); } } return; } /* --- Tabular mode: each line = one object --- */ str = g_string_new (NULL); for (i = 0; i < fields.indices->len; i++) { int idx = g_array_index (fields.indices, int, i); gboolean dealloc; char *value = get_value_to_print ((NmcOutputField *) field_values+idx, field_names, not_set_str, &dealloc); if (terse) { if (escape) { const char *p = value; while (*p) { if (*p == ':' || *p == '\\') g_string_append_c (str, '\\'); /* Escaping by '\' */ g_string_append_c (str, *p); p++; } } else g_string_append_printf (str, "%s", value); g_string_append_c (str, ':'); /* Column separator */ } else { width1 = strlen (value); width2 = nmc_string_screen_width (value, NULL); /* Width of the string (in screen colums) */ g_string_append_printf (str, "%-*s", field_values[idx].width + width1 - width2, strlen (value) > 0 ? value : "--"); g_string_append_c (str, ' '); /* Column separator */ table_width += field_values[idx].width + width1 - width2 + 1; } if (dealloc) g_free (value); } /* Print the main table header */ if (main_header && pretty) { int header_width = nmc_string_screen_width (fields.header_name, NULL) + 4; table_width = table_width < header_width ? header_width : table_width; line = g_strnfill (table_width, '='); width1 = strlen (fields.header_name); width2 = nmc_string_screen_width (fields.header_name, NULL); printf ("%s\n", line); printf ("%*s\n", (table_width + width2)/2 + width1 - width2, fields.header_name); printf ("%s\n", line); g_free (line); } /* Print actual values */ if (!main_header_only && str->len > 0) { g_string_truncate (str, str->len-1); /* Chop off last column separator */ if (fields.indent > 0) { indent_str = g_strnfill (fields.indent, ' '); g_string_prepend (str, indent_str); g_free (indent_str); } printf ("%s\n", str->str); } /* Print horizontal separator */ if (!main_header_only && field_names && pretty) { if (str->len > 0) { line = g_strnfill (table_width, '-'); printf ("%s\n", line); g_free (line); } } g_string_free (str, TRUE); } /* * Print nmc->output_data * * It first finds out maximal string length in columns and fill the value to * 'width' member of NmcOutputField, so that columns in tabular output are * properly aligned. Then each object (row in tabular) is printed using * print_required_fields() function. */ void print_data (NmCli *nmc) { int i, j; size_t len; NmcOutputField *row; int num_fields = 0; if (!nmc->output_data || nmc->output_data->len < 1) return; /* How many fields? */ row = g_ptr_array_index (nmc->output_data, 0); while (row->name) { num_fields++; row++; } /* Find out maximal string lengths */ for (i = 0; i < num_fields; i++) { size_t max_width = 0; for (j = 0; j < nmc->output_data->len; j++) { gboolean field_names, dealloc; char *value; row = g_ptr_array_index (nmc->output_data, j); field_names = row[0].flags & NMC_OF_FLAG_FIELD_NAMES; value = get_value_to_print (row+i, field_names, "--", &dealloc); len = nmc_string_screen_width (value, NULL); max_width = len > max_width ? len : max_width; if (dealloc) g_free (value); } for (j = 0; j < nmc->output_data->len; j++) { row = g_ptr_array_index (nmc->output_data, j); row[i].width = max_width + 1; } } /* Now we can print the data. */ for (i = 0; i < nmc->output_data->len; i++) { row = g_ptr_array_index (nmc->output_data, i); print_required_fields (nmc, row); } } /* * Compare versions of nmcli and NM daemon. * Return: TRUE - the versions match (when only major and minor match, print a warning) * FALSE - versions mismatch */ gboolean nmc_versions_match (NmCli *nmc) { const char *nm_ver = NULL; const char *dot; gboolean match = FALSE; g_return_val_if_fail (nmc != NULL, FALSE); /* --nocheck option - don't compare the versions */ if (nmc->nocheck_ver) return TRUE; nmc->get_client (nmc); nm_ver = nm_client_get_version (nmc->client); if (nm_ver) { if (!strcmp (nm_ver, VERSION)) match = TRUE; else { dot = strchr (nm_ver, '.'); if (dot) { dot = strchr (dot + 1, '.'); if (dot && !strncmp (nm_ver, VERSION, dot-nm_ver)) { fprintf(stderr, _("Warning: nmcli (%s) and NetworkManager (%s) versions don't match. Use --nocheck to suppress the warning.\n"), VERSION, nm_ver); match = TRUE; } } } } if (!match) { g_string_printf (nmc->return_text, _("Error: nmcli (%s) and NetworkManager (%s) versions don't match. Force execution using --nocheck, but the results are unpredictable."), VERSION, nm_ver ? nm_ver : _("unknown")); nmc->return_value = NMC_RESULT_ERROR_VERSIONS_MISMATCH; } return match; }