nmcli: add --order option for 'nmcli connection show'
The option allows you to specify custom sorting order. Default order (when no --order is provided) corresponds to -o "active:name:path" Examples: nmcli con show -o name nmcli con show -o +name - sort connections by name alphabetically nmcli con show -o -name - sort connections by name alphabetically in reverse order mmcli con show -o active:name - sort connections first by active status, then by name mmcli con show -o -path - sort connections by D-Bus path in reverse order
This commit is contained in:
@@ -250,7 +250,8 @@ usage (void)
|
|||||||
{
|
{
|
||||||
g_printerr (_("Usage: nmcli connection { COMMAND | help }\n\n"
|
g_printerr (_("Usage: nmcli connection { COMMAND | help }\n\n"
|
||||||
"COMMAND := { show | up | down | add | modify | edit | delete | reload | load }\n\n"
|
"COMMAND := { show | up | down | add | modify | edit | delete | reload | load }\n\n"
|
||||||
" show [--active] [[--show-secrets] [id | uuid | path | apath] <ID>] ...\n\n"
|
" show [--active] [--order <order spec>]\n"
|
||||||
|
" show [--active] [--show-secrets] [id | uuid | path | apath] <ID> ...\n\n"
|
||||||
#if WITH_WIMAX
|
#if WITH_WIMAX
|
||||||
" up [[id | uuid | path] <ID>] [ifname <ifname>] [ap <BSSID>] [nsp <name>] [passwd-file <file with passwords>]\n\n"
|
" up [[id | uuid | path] <ID>] [ifname <ifname>] [ap <BSSID>] [nsp <name>] [passwd-file <file with passwords>]\n\n"
|
||||||
#else
|
#else
|
||||||
@@ -271,12 +272,12 @@ usage_connection_show (void)
|
|||||||
{
|
{
|
||||||
g_printerr (_("Usage: nmcli connection show { ARGUMENTS | help }\n"
|
g_printerr (_("Usage: nmcli connection show { ARGUMENTS | help }\n"
|
||||||
"\n"
|
"\n"
|
||||||
"ARGUMENTS := [--active]\n"
|
"ARGUMENTS := [--active] [--order <order spec>]\n"
|
||||||
"\n"
|
"\n"
|
||||||
"List in-memory and on-disk connection profiles, some of which may also be\n"
|
"List in-memory and on-disk connection profiles, some of which may also be\n"
|
||||||
"active if a device is using that connection profile. Without a parameter, all\n"
|
"active if a device is using that connection profile. Without a parameter, all\n"
|
||||||
"profiles are listed. When --active option is specified, only the active\n"
|
"profiles are listed. When --active option is specified, only the active\n"
|
||||||
"profiles are shown.\n"
|
"profiles are shown. --order allows custom connection ordering (see manual page).\n"
|
||||||
"\n"
|
"\n"
|
||||||
"ARGUMENTS := [--active] [--show-secrets] [id | uuid | path | apath] <ID> ...\n"
|
"ARGUMENTS := [--active] [--show-secrets] [id | uuid | path | apath] <ID> ...\n"
|
||||||
"\n"
|
"\n"
|
||||||
@@ -1311,44 +1312,72 @@ split_required_fields_for_con_show (const char *input,
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
NMC_SORT_ACTIVE,
|
NMC_SORT_ACTIVE = 1,
|
||||||
NMC_SORT_NAME,
|
NMC_SORT_ACTIVE_INV = -1,
|
||||||
NMC_SORT_TYPE,
|
NMC_SORT_NAME = 2,
|
||||||
NMC_SORT_PATH,
|
NMC_SORT_NAME_INV = -2,
|
||||||
|
NMC_SORT_TYPE = 3,
|
||||||
|
NMC_SORT_TYPE_INV = -3,
|
||||||
|
NMC_SORT_PATH = 4,
|
||||||
|
NMC_SORT_PATH_INV = -4,
|
||||||
} NmcSortOrder;
|
} NmcSortOrder;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
NmCli *nmc;
|
||||||
|
const GArray *order;
|
||||||
|
} NmcSortInfo;
|
||||||
|
|
||||||
static int
|
static int
|
||||||
compare_connections (gconstpointer a, gconstpointer b, gpointer user_data)
|
compare_connections (gconstpointer a, gconstpointer b, gpointer user_data)
|
||||||
{
|
{
|
||||||
NMConnection *ca = *(NMConnection **)a;
|
NMConnection *ca = *(NMConnection **)a;
|
||||||
NMConnection *cb = *(NMConnection **)b;
|
NMConnection *cb = *(NMConnection **)b;
|
||||||
NMActiveConnection *aca, *acb;
|
NMActiveConnection *aca, *acb;
|
||||||
NmCli *nmc = (NmCli *) user_data;
|
NmcSortInfo *info = (NmcSortInfo *) user_data;
|
||||||
int cmp, i;
|
GArray *default_order = NULL;
|
||||||
|
const GArray *order;
|
||||||
|
NmcSortOrder item;
|
||||||
|
int cmp = 0, i;
|
||||||
const char *tmp1, *tmp2;
|
const char *tmp1, *tmp2;
|
||||||
unsigned long tmp1_int, tmp2_int;
|
unsigned long tmp1_int, tmp2_int;
|
||||||
NmcSortOrder default_order[] = {
|
|
||||||
NMC_SORT_ACTIVE,
|
|
||||||
NMC_SORT_NAME,
|
|
||||||
NMC_SORT_PATH,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (i = 0; i < G_N_ELEMENTS (default_order); i++) {
|
if (info->order )
|
||||||
switch (default_order[i]) {
|
order = info->order;
|
||||||
|
else {
|
||||||
|
NmcSortOrder def[] = { NMC_SORT_ACTIVE, NMC_SORT_NAME, NMC_SORT_PATH };
|
||||||
|
int num = G_N_ELEMENTS (def);
|
||||||
|
default_order = g_array_sized_new (FALSE, FALSE, sizeof (NmcSortOrder), num);
|
||||||
|
g_array_append_vals (default_order, def, num);
|
||||||
|
order = default_order;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < order->len; i++) {
|
||||||
|
item = g_array_index (order, NmcSortOrder, i);
|
||||||
|
switch (item) {
|
||||||
case NMC_SORT_ACTIVE:
|
case NMC_SORT_ACTIVE:
|
||||||
aca = get_ac_for_connection (nm_client_get_active_connections (nmc->client), ca);
|
case NMC_SORT_ACTIVE_INV:
|
||||||
acb = get_ac_for_connection (nm_client_get_active_connections (nmc->client), cb);
|
aca = get_ac_for_connection (nm_client_get_active_connections (info->nmc->client), ca);
|
||||||
|
acb = get_ac_for_connection (nm_client_get_active_connections (info->nmc->client), cb);
|
||||||
cmp = (aca && !acb) ? -1 : (!aca && acb) ? 1 : 0;
|
cmp = (aca && !acb) ? -1 : (!aca && acb) ? 1 : 0;
|
||||||
|
if (item == NMC_SORT_ACTIVE_INV)
|
||||||
|
cmp = -(cmp);
|
||||||
break;
|
break;
|
||||||
case NMC_SORT_TYPE:
|
case NMC_SORT_TYPE:
|
||||||
|
case NMC_SORT_TYPE_INV:
|
||||||
cmp = g_strcmp0 (nm_connection_get_connection_type (ca),
|
cmp = g_strcmp0 (nm_connection_get_connection_type (ca),
|
||||||
nm_connection_get_connection_type (cb));
|
nm_connection_get_connection_type (cb));
|
||||||
|
if (item == NMC_SORT_TYPE_INV)
|
||||||
|
cmp = -(cmp);
|
||||||
break;
|
break;
|
||||||
case NMC_SORT_NAME:
|
case NMC_SORT_NAME:
|
||||||
|
case NMC_SORT_NAME_INV:
|
||||||
cmp = g_strcmp0 (nm_connection_get_id (ca),
|
cmp = g_strcmp0 (nm_connection_get_id (ca),
|
||||||
nm_connection_get_id (cb));
|
nm_connection_get_id (cb));
|
||||||
|
if (item == NMC_SORT_NAME_INV)
|
||||||
|
cmp = -(cmp);
|
||||||
break;
|
break;
|
||||||
case NMC_SORT_PATH:
|
case NMC_SORT_PATH:
|
||||||
|
case NMC_SORT_PATH_INV:
|
||||||
tmp1 = nm_connection_get_path (ca);
|
tmp1 = nm_connection_get_path (ca);
|
||||||
tmp2 = nm_connection_get_path (cb);
|
tmp2 = nm_connection_get_path (cb);
|
||||||
tmp1 = tmp1 ? strrchr (tmp1, '/') : "0";
|
tmp1 = tmp1 ? strrchr (tmp1, '/') : "0";
|
||||||
@@ -1356,27 +1385,36 @@ compare_connections (gconstpointer a, gconstpointer b, gpointer user_data)
|
|||||||
nmc_string_to_uint (tmp1 ? tmp1+1 : "0", FALSE, 0, 0, &tmp1_int);
|
nmc_string_to_uint (tmp1 ? tmp1+1 : "0", FALSE, 0, 0, &tmp1_int);
|
||||||
nmc_string_to_uint (tmp2 ? tmp2+1 : "0", FALSE, 0, 0, &tmp2_int);
|
nmc_string_to_uint (tmp2 ? tmp2+1 : "0", FALSE, 0, 0, &tmp2_int);
|
||||||
cmp = (int) tmp1_int - tmp2_int;
|
cmp = (int) tmp1_int - tmp2_int;
|
||||||
|
if (item == NMC_SORT_PATH_INV)
|
||||||
|
cmp = -(cmp);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
cmp = 0;
|
cmp = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (cmp != 0)
|
if (cmp != 0)
|
||||||
return cmp;
|
goto end;
|
||||||
}
|
}
|
||||||
return 0;
|
end:
|
||||||
|
if (default_order)
|
||||||
|
g_array_unref (default_order);
|
||||||
|
return cmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GPtrArray *
|
static GPtrArray *
|
||||||
sort_connections (const GPtrArray *cons, NmCli *nmc)
|
sort_connections (const GPtrArray *cons, NmCli *nmc, const GArray *order)
|
||||||
{
|
{
|
||||||
GPtrArray *sorted;
|
GPtrArray *sorted;
|
||||||
int i;
|
int i;
|
||||||
|
NmcSortInfo compare_info;
|
||||||
|
|
||||||
|
compare_info.nmc = nmc;
|
||||||
|
compare_info.order = order;
|
||||||
|
|
||||||
sorted = g_ptr_array_sized_new (cons->len);
|
sorted = g_ptr_array_sized_new (cons->len);
|
||||||
for (i = 0; cons && i < cons->len; i++)
|
for (i = 0; cons && i < cons->len; i++)
|
||||||
g_ptr_array_add (sorted, cons->pdata[i]);
|
g_ptr_array_add (sorted, cons->pdata[i]);
|
||||||
g_ptr_array_sort_with_data (sorted, compare_connections, nmc);
|
g_ptr_array_sort_with_data (sorted, compare_connections, &compare_info);
|
||||||
return sorted;
|
return sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1436,7 +1474,7 @@ get_invisible_active_connections (NmCli *nmc)
|
|||||||
|
|
||||||
static NMCResultCode
|
static NMCResultCode
|
||||||
do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets,
|
do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets,
|
||||||
int argc, char **argv)
|
const GArray *order, int argc, char **argv)
|
||||||
{
|
{
|
||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
char *profile_flds = NULL, *active_flds = NULL;
|
char *profile_flds = NULL, *active_flds = NULL;
|
||||||
@@ -1482,7 +1520,7 @@ do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets,
|
|||||||
g_ptr_array_free (invisibles, FALSE);
|
g_ptr_array_free (invisibles, FALSE);
|
||||||
|
|
||||||
/* Sort the connections and fill the output data */
|
/* Sort the connections and fill the output data */
|
||||||
sorted_cons = sort_connections (nmc->connections, nmc);
|
sorted_cons = sort_connections (nmc->connections, nmc, order);
|
||||||
for (i = 0; i < sorted_cons->len; i++)
|
for (i = 0; i < sorted_cons->len; i++)
|
||||||
fill_output_connection (sorted_cons->pdata[i], nmc, active_only);
|
fill_output_connection (sorted_cons->pdata[i], nmc, active_only);
|
||||||
g_ptr_array_free (sorted_cons, FALSE);
|
g_ptr_array_free (sorted_cons, FALSE);
|
||||||
@@ -9019,6 +9057,67 @@ nmcli_con_tab_completion (const char *text, int start, int end)
|
|||||||
return match_array;
|
return match_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GArray *
|
||||||
|
parse_preferred_connection_order (const char *order, GError **error)
|
||||||
|
{
|
||||||
|
char **strv, **iter;
|
||||||
|
const char *str;
|
||||||
|
GArray *order_arr;
|
||||||
|
NmcSortOrder val;
|
||||||
|
gboolean inverse;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
strv = nmc_strsplit_set (order, ":", -1);
|
||||||
|
if (!strv || !*strv) {
|
||||||
|
g_set_error (error, NMCLI_ERROR, 0,
|
||||||
|
_("incorrect string '%s' of '--order' option"), order);
|
||||||
|
g_strfreev (strv);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
order_arr = g_array_sized_new (FALSE, FALSE, sizeof (NmcSortOrder), 4);
|
||||||
|
for (iter = strv; iter && *iter; iter++) {
|
||||||
|
str = *iter;
|
||||||
|
inverse = FALSE;
|
||||||
|
if (str[0] == '-')
|
||||||
|
inverse = TRUE;
|
||||||
|
if (str[0] == '+' || str[0] == '-')
|
||||||
|
str++;
|
||||||
|
|
||||||
|
if (matches (str, "active") == 0)
|
||||||
|
val = inverse ? NMC_SORT_ACTIVE_INV : NMC_SORT_ACTIVE;
|
||||||
|
else if (matches (str, "name") == 0)
|
||||||
|
val = inverse ? NMC_SORT_NAME_INV : NMC_SORT_NAME;
|
||||||
|
else if (matches (str, "type") == 0)
|
||||||
|
val = inverse ? NMC_SORT_TYPE_INV : NMC_SORT_TYPE;
|
||||||
|
else if (matches (str, "path") == 0)
|
||||||
|
val = inverse ? NMC_SORT_PATH_INV : NMC_SORT_PATH;
|
||||||
|
else {
|
||||||
|
g_array_unref (order_arr);
|
||||||
|
order_arr = NULL;
|
||||||
|
g_set_error (error, NMCLI_ERROR, 0,
|
||||||
|
_("incorrect item '%s' in '--order' option"), *iter);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Check duplicates*/
|
||||||
|
for (i = 0; i < order_arr->len; i++) {
|
||||||
|
if (abs (g_array_index (order_arr, NmcSortOrder, i)) - abs (val) == 0) {
|
||||||
|
g_array_unref (order_arr);
|
||||||
|
order_arr = NULL;
|
||||||
|
g_set_error (error, NMCLI_ERROR, 0,
|
||||||
|
_("'%s' repeats in '--order' option"), str);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Value is ok and unique, add it to the array */
|
||||||
|
g_array_append_val (order_arr, val);
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
g_strfreev (strv);
|
||||||
|
return order_arr;
|
||||||
|
}
|
||||||
|
|
||||||
/* Entry point function for connections-related commands: 'nmcli connection' */
|
/* Entry point function for connections-related commands: 'nmcli connection' */
|
||||||
NMCResultCode
|
NMCResultCode
|
||||||
do_connections (NmCli *nmc, int argc, char **argv)
|
do_connections (NmCli *nmc, int argc, char **argv)
|
||||||
@@ -9061,16 +9160,17 @@ do_connections (NmCli *nmc, int argc, char **argv)
|
|||||||
if (argc == 0) {
|
if (argc == 0) {
|
||||||
if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error))
|
if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error))
|
||||||
goto opt_error;
|
goto opt_error;
|
||||||
nmc->return_value = do_connections_show (nmc, FALSE, FALSE, argc, argv);
|
nmc->return_value = do_connections_show (nmc, FALSE, FALSE, NULL, argc, argv);
|
||||||
} else {
|
} else {
|
||||||
if (matches (*argv, "show") == 0) {
|
if (matches (*argv, "show") == 0) {
|
||||||
gboolean active = FALSE;
|
gboolean active = FALSE;
|
||||||
gboolean show_secrets = FALSE;
|
gboolean show_secrets = FALSE;
|
||||||
|
GArray *order = NULL;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
next_arg (&argc, &argv);
|
next_arg (&argc, &argv);
|
||||||
/* check connection show options [--active] [--show-secrets] */
|
/* check connection show options [--active] [--show-secrets] */
|
||||||
for (i = 0; i < 2; i++) {
|
for (i = 0; i < 3; i++) {
|
||||||
if (!active && nmc_arg_is_option (*argv, "active")) {
|
if (!active && nmc_arg_is_option (*argv, "active")) {
|
||||||
active = TRUE;
|
active = TRUE;
|
||||||
next_arg (&argc, &argv);
|
next_arg (&argc, &argv);
|
||||||
@@ -9079,8 +9179,21 @@ do_connections (NmCli *nmc, int argc, char **argv)
|
|||||||
show_secrets = TRUE;
|
show_secrets = TRUE;
|
||||||
next_arg (&argc, &argv);
|
next_arg (&argc, &argv);
|
||||||
}
|
}
|
||||||
|
if (!order && nmc_arg_is_option (*argv, "order")) {
|
||||||
|
if (next_arg (&argc, &argv) != 0) {
|
||||||
|
g_set_error_literal (&error, NMCLI_ERROR, 0,
|
||||||
|
_("'--order' argument is missing"));
|
||||||
|
goto opt_error;
|
||||||
}
|
}
|
||||||
nmc->return_value = do_connections_show (nmc, active, show_secrets, argc, argv);
|
order = parse_preferred_connection_order (*argv, &error);
|
||||||
|
if (error)
|
||||||
|
goto opt_error;
|
||||||
|
next_arg (&argc, &argv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nmc->return_value = do_connections_show (nmc, active, show_secrets, order, argc, argv);
|
||||||
|
if (order)
|
||||||
|
g_array_unref (order);
|
||||||
} else if (matches(*argv, "up") == 0) {
|
} else if (matches(*argv, "up") == 0) {
|
||||||
nmc->return_value = do_connection_up (nmc, argc-1, argv+1);
|
nmc->return_value = do_connection_up (nmc, argc-1, argv+1);
|
||||||
} else if (matches(*argv, "down") == 0) {
|
} else if (matches(*argv, "down") == 0) {
|
||||||
|
@@ -289,12 +289,31 @@ active if a device is using that connection profile. Without a parameter, all
|
|||||||
profiles are listed. When --active option is specified, only the active profiles
|
profiles are listed. When --active option is specified, only the active profiles
|
||||||
are shown.
|
are shown.
|
||||||
.TP
|
.TP
|
||||||
.B show [--active] [--show-secrets] [ id | uuid | path | apath ] <ID> ...
|
.B show [--active] [--order <order spec>] [--show-secrets] [ id | uuid | path | apath ] <ID> ...
|
||||||
.br
|
.br
|
||||||
Show details for specified connections. By default, both static configuration
|
Show details for specified connections. By default, both static configuration
|
||||||
and active connection data are displayed. When --active option is specified,
|
and active connection data are displayed. When --active option is specified,
|
||||||
only the active profiles are taken into account. When --show-secrets option is
|
only the active profiles are taken into account. When --show-secrets option is
|
||||||
specified, secrets associated with the profile will be revealed too.
|
specified, secrets associated with the profile will be revealed too.
|
||||||
|
.sp
|
||||||
|
Ordering:
|
||||||
|
.br
|
||||||
|
The --order option can be used to get custom ordering of connections. The
|
||||||
|
connections can be ordered by active status, name, type or D-Bus path. If
|
||||||
|
connections are equal according to a sort order category, an additional
|
||||||
|
category can be specified.
|
||||||
|
The default sorting order is equivalent to "--order active:name:path".
|
||||||
|
.sp
|
||||||
|
<order spec> := category:category:...
|
||||||
|
.br
|
||||||
|
categogy := [+-]active | [+-]name | [+-]type | [+-]path
|
||||||
|
.br
|
||||||
|
\fI+\fP or no prefix means sorting in ascending order (alphabetically or in numbers).
|
||||||
|
.br
|
||||||
|
\fI-\fP means reverse (descending) order.
|
||||||
|
.br
|
||||||
|
The category names can be abbreviated (e.g. --order -a:na)
|
||||||
|
.sp
|
||||||
\fIid\fP, \fIuuid\fP, \fIpath\fP and \fIapath\fP keywords can be used if
|
\fIid\fP, \fIuuid\fP, \fIpath\fP and \fIapath\fP keywords can be used if
|
||||||
\fI<ID>\fP is ambiguous.
|
\fI<ID>\fP is ambiguous.
|
||||||
.RS
|
.RS
|
||||||
|
Reference in New Issue
Block a user