diff --git a/clients/cli/devices.c b/clients/cli/devices.c index b19657ef6b..4cfa35ca0b 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -282,13 +282,14 @@ static void usage (void) { g_printerr (_("Usage: nmcli device { COMMAND | help }\n\n" - "COMMAND := { status | show | connect | disconnect | delete | wifi | lldp }\n\n" + "COMMAND := { status | show | connect | disconnect | delete | monitor | wifi | lldp }\n\n" " status\n\n" " show []\n\n" " set [ifname] [autoconnect yes|no] [managed yes|no]\n\n" " connect \n\n" " disconnect ...\n\n" " delete ...\n\n" + " monitor ...\n\n" " wifi [list [ifname ] [bssid ]]\n\n" " wifi connect <(B)SSID> [password ] [wep-key-type key|phrase] [ifname ]\n" " [bssid ] [name ] [private yes|no] [hidden yes|no]\n\n" @@ -375,6 +376,18 @@ usage_device_set (void) "Modify device properties.\n\n")); } +static void +usage_device_monitor (void) +{ + g_printerr (_("Usage: nmcli device monitor { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [] ...\n" + "\n" + "Monitor device activity.\n" + "This command prints a line whenever the specified devices change state.\n" + "Monitors all devices in case no interface is specified.\n\n")); +} + static void usage_device_wifi (void) { @@ -2062,6 +2075,97 @@ error: return nmc->return_value; } +static void +device_state (NMDevice *device, GParamSpec *pspec, NmCli *nmc) +{ + NMDeviceState state = nm_device_get_state (device); + ColorInfo color = device_state_to_color (state); + char *str = nmc_colorize (nmc, color.color, color.color_fmt, "%s: %s\n", + nm_device_get_iface (device), + nmc_device_state_to_string (state)); + + g_print ("%s", str); + g_free (str); +} + +static void +device_ac (NMDevice *device, GParamSpec *pspec, NmCli *nmc) +{ + NMActiveConnection *ac = nm_device_get_active_connection (device); + const char *id = ac ? nm_active_connection_get_id (ac) : NULL; + + if (!id) + return; + + g_print (_("%s: using connection '%s'\n"), nm_device_get_iface (device), id); +} + +static void +device_watch (NmCli *nmc, NMDevice *device) +{ + nmc->should_wait++; + g_signal_connect (device, "notify::" NM_DEVICE_STATE, G_CALLBACK (device_state), nmc); + g_signal_connect (device, "notify::" NM_DEVICE_ACTIVE_CONNECTION, G_CALLBACK (device_ac), nmc); +} + +static void +device_unwatch (NmCli *nmc, NMDevice *device) +{ + g_signal_handlers_disconnect_by_func (device, device_state, nmc); + if (g_signal_handlers_disconnect_by_func (device, device_ac, nmc)) + nmc->should_wait--; + + /* Terminate if all the watched devices disappeared. */ + if (!nmc->should_wait) + quit (); +} + +static void +device_added (NMClient *client, NMDevice *device, NmCli *nmc) +{ + g_print (_("%s: device created\n"), nm_device_get_iface (device)); + device_watch (nmc, NM_DEVICE (device)); +} + +static void +device_removed (NMClient *client, NMDevice *device, NmCli *nmc) +{ + g_print (_("%s: device removed\n"), nm_device_get_iface (device)); + device_unwatch (nmc, device); +} + +static NMCResultCode +do_device_monitor (NmCli *nmc, int argc, char **argv) +{ + if (argc == 0) { + /* No devices specified. Monitor all. */ + const GPtrArray *devices = nm_client_get_devices (nmc->client); + int i; + + for (i = 0; i < devices->len; i++) + device_watch (nmc, g_ptr_array_index (devices, i)); + + /* We'll watch the device additions too, never exit. */ + nmc->should_wait++; + g_signal_connect (nmc->client, NM_CLIENT_DEVICE_ADDED, G_CALLBACK (device_added), nmc); + } else { + /* Monitor just the specified devices. */ + GSList *queue = device_list (nmc, argc, argv); + GSList *iter; + + if (!queue) + return nmc->return_value; + + for (iter = queue; iter; iter = g_slist_next (iter)) + device_watch (nmc, NM_DEVICE (iter->data)); + g_slist_free (queue); + } + + g_signal_connect (nmc->client, NM_CLIENT_DEVICE_REMOVED, G_CALLBACK (device_removed), nmc); + + return NMC_RESULT_SUCCESS; +} + static void show_access_point_info (NMDevice *device, NmCli *nmc) { @@ -3510,6 +3614,13 @@ do_devices (NmCli *nmc, int argc, char **argv) } nmc->return_value = do_device_set (nmc, argc-1, argv+1); } + else if (matches (*argv, "monitor") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_device_monitor (); + goto usage_exit; + } + nmc->return_value = do_device_monitor (nmc, argc-1, argv+1); + } else if (matches (*argv, "wifi") == 0) { if (nmc_arg_is_help (*(argv+1))) { usage_device_wifi (); diff --git a/clients/cli/nmcli-completion b/clients/cli/nmcli-completion index 2424fc6d7c..63050a36db 100644 --- a/clients/cli/nmcli-completion +++ b/clients/cli/nmcli-completion @@ -798,7 +798,7 @@ _nmcli() # (if the current word starts with a dash) or the OBJECT list # otherwise. if [[ "${words[0]:0:1}" != '-' ]]; then - OPTIONS=(help general networking radio connection device agent) + OPTIONS=(help general networking radio connection device agent monitor) elif [[ "${words[0]:1:1}" == '-' || "${words[0]}" == "-" ]]; then OPTIONS=("${LONG_OPTIONS[@]/#/--}") else @@ -870,7 +870,7 @@ _nmcli() ;; c|co|con|conn|conne|connec|connect|connecti|connectio|connection) if [[ ${#words[@]} -eq 2 ]]; then - _nmcli_compl_COMMAND "$command" show up down add modify clone edit delete reload load + _nmcli_compl_COMMAND "$command" show up down add modify clone edit delete monitor reload load elif [[ ${#words[@]} -gt 2 ]]; then case "$command" in s|sh|sho|show) @@ -1280,7 +1280,8 @@ _nmcli() fi ;; - de|del|dele|delet|delete) + de|del|dele|delet|delete| \ + m|mo|mon|moni|monit|monito|monitor) if [[ ${#words[@]} -eq 3 ]]; then _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" elif [[ ${#words[@]} -gt 3 ]]; then @@ -1319,7 +1320,7 @@ _nmcli() ;; d|de|dev|devi|devic|device) if [[ ${#words[@]} -eq 2 ]]; then - _nmcli_compl_COMMAND "$command" status show connect disconnect delete wifi set lldp + _nmcli_compl_COMMAND "$command" status show connect disconnect delete monitor wifi set lldp elif [[ ${#words[@]} -gt 2 ]]; then case "$command" in s|st|sta|stat|statu|status) @@ -1334,7 +1335,8 @@ _nmcli() fi ;; d|di|dis|disc|disco|discon|disconn|disconne|disconnec|disconnect| \ - de|del|dele|delet|delete) + de|del|dele|delet|delete| \ + m|mo|mon|moni|monit|monito|monitor) if [[ ${#words[@]} -ge 3 ]]; then _nmcli_compl_COMMAND_nl "${words[2]}" "$(_nmcli_dev_status DEVICE)" fi @@ -1408,6 +1410,8 @@ _nmcli() _nmcli_compl_COMMAND "$command" secret polkit all fi ;; + m|mo|mon|moni|monit|monito|monitor) + ;; esac return 0 diff --git a/man/nmcli.1.in b/man/nmcli.1.in index a020ce52f9..fef275d23f 100644 --- a/man/nmcli.1.in +++ b/man/nmcli.1.in @@ -799,7 +799,7 @@ of its latest state. .B device - show and manage network interfaces .br .TP -.SS \fICOMMAND\fP := { status | show | set | connect | disconnect | delete | wifi | lldp } +.SS \fICOMMAND\fP := { status | show | set | connect | disconnect | delete | monitor | wifi | lldp } .sp .RS .TP @@ -843,6 +843,14 @@ Hardware devices (like Ethernet) cannot be deleted by the command. .br If '--wait' option is not specified, the default timeout will be 10 seconds. .TP +.B monitor [] ... +.br +Monitor device activity. This command prints a line whenever the specified devices +change state. +.br +Monitors all devices in case no interface is specified. The monitor terminates when +all specified devices disappear. +.TP .B wifi [list [ifname ] [bssid ]] .br List available Wi\(hyFi access points. The \fIifname\fP and \fIbssid\fP options