diff options
| author | Michael Biebl <biebl@debian.org> | 2014-07-06 02:16:10 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2014-07-06 02:16:10 +0200 |
| commit | 33491bc4279481db8ae47213e34a6d695a0e8830 (patch) | |
| tree | 097d2b0fdff3fae6885381ae5e57a182cd8cbbba /cli | |
| parent | 59c3714a494c3b3765657c0551ad82842d98a7d2 (diff) | |
Imported Upstream version 0.9.10.0 upstream/0.9.10.0
Diffstat (limited to 'cli')
| -rw-r--r-- | cli/Makefile.in | 21 | ||||
| -rw-r--r-- | cli/completion/Makefile.in | 21 | ||||
| -rw-r--r-- | cli/completion/nmcli | 1307 | ||||
| -rw-r--r-- | cli/src/Makefile.am | 5 | ||||
| -rw-r--r-- | cli/src/Makefile.in | 147 | ||||
| -rw-r--r-- | cli/src/common.c | 785 | ||||
| -rw-r--r-- | cli/src/common.h | 33 | ||||
| -rw-r--r-- | cli/src/connections.c | 8455 | ||||
| -rw-r--r-- | cli/src/connections.h | 2 | ||||
| -rw-r--r-- | cli/src/devices.c | 2095 | ||||
| -rw-r--r-- | cli/src/network-manager.c | 920 | ||||
| -rw-r--r-- | cli/src/network-manager.h | 6 | ||||
| -rw-r--r-- | cli/src/nmcli.c | 149 | ||||
| -rw-r--r-- | cli/src/nmcli.h | 56 | ||||
| -rw-r--r-- | cli/src/settings.c | 7434 | ||||
| -rw-r--r-- | cli/src/settings.h | 63 | ||||
| -rw-r--r-- | cli/src/utils.c | 799 | ||||
| -rw-r--r-- | cli/src/utils.h | 67 |
18 files changed, 18790 insertions, 3575 deletions
diff --git a/cli/Makefile.in b/cli/Makefile.in index dd649e36..11a64d42 100644 --- a/cli/Makefile.in +++ b/cli/Makefile.in @@ -80,7 +80,8 @@ host_triplet = @host@ subdir = cli DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ + $(top_srcdir)/m4/compiler_warnings.m4 \ $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/gnome-code-coverage.m4 \ $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \ @@ -204,12 +205,15 @@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DATADIRNAME = @DATADIRNAME@ DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@ +DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@ DBUS_LIBS = @DBUS_LIBS@ DBUS_SYS_DIR = @DBUS_SYS_DIR@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DHCLIENT_PATH = @DHCLIENT_PATH@ DHCPCD_PATH = @DHCPCD_PATH@ +DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@ DLLTOOL = @DLLTOOL@ DNSMASQ_PATH = @DNSMASQ_PATH@ DSYMUTIL = @DSYMUTIL@ @@ -234,7 +238,6 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ GNUTLS_LIBS = @GNUTLS_LIBS@ GREP = @GREP@ GTKDOC_CHECK = @GTKDOC_CHECK@ -GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@ GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ GTKDOC_MKPDF = @GTKDOC_MKPDF@ @@ -279,12 +282,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBM = @LIBM@ +LIBNDP_CFLAGS = @LIBNDP_CFLAGS@ +LIBNDP_LIBS = @LIBNDP_LIBS@ LIBNL_CFLAGS = @LIBNL_CFLAGS@ LIBNL_LIBS = @LIBNL_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@ LIBSOUP_LIBS = @LIBSOUP_LIBS@ +LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@ +LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ @@ -301,6 +308,8 @@ MOC = @MOC@ MSGFMT = @MSGFMT@ MSGFMT_015 = @MSGFMT_015@ MSGMERGE = @MSGMERGE@ +NEWT_CFLAGS = @NEWT_CFLAGS@ +NEWT_LIBS = @NEWT_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NM_MAJOR_VERSION = @NM_MAJOR_VERSION@ @@ -328,11 +337,16 @@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POLKIT_CFLAGS = @POLKIT_CFLAGS@ POLKIT_LIBS = @POLKIT_LIBS@ POSUB = @POSUB@ +PPPD_PATH = @PPPD_PATH@ PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +PPPOE_PATH = @PPPOE_PATH@ QT_CFLAGS = @QT_CFLAGS@ QT_LIBS = @QT_LIBS@ RANLIB = @RANLIB@ +READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ +SELINUX_CFLAGS = @SELINUX_CFLAGS@ +SELINUX_LIBS = @SELINUX_LIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ @@ -347,6 +361,7 @@ UDEV_BASE_DIR = @UDEV_BASE_DIR@ USE_NLS = @USE_NLS@ UUID_CFLAGS = @UUID_CFLAGS@ UUID_LIBS = @UUID_LIBS@ +VALGRIND_RULES = @VALGRIND_RULES@ VAPIGEN = @VAPIGEN@ VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@ VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@ @@ -409,6 +424,7 @@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ +subdirs = @subdirs@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ @@ -419,6 +435,7 @@ with_dhclient = @with_dhclient@ with_dhcpcd = @with_dhcpcd@ with_netconfig = @with_netconfig@ with_resolvconf = @with_resolvconf@ +with_valgrind = @with_valgrind@ SUBDIRS = src completion all: all-recursive diff --git a/cli/completion/Makefile.in b/cli/completion/Makefile.in index 62cd8b59..7b45c2b4 100644 --- a/cli/completion/Makefile.in +++ b/cli/completion/Makefile.in @@ -82,7 +82,8 @@ subdir = cli/completion DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(dist_completion_DATA) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ + $(top_srcdir)/m4/compiler_warnings.m4 \ $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/gnome-code-coverage.m4 \ $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \ @@ -175,12 +176,15 @@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DATADIRNAME = @DATADIRNAME@ DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@ +DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@ DBUS_LIBS = @DBUS_LIBS@ DBUS_SYS_DIR = @DBUS_SYS_DIR@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DHCLIENT_PATH = @DHCLIENT_PATH@ DHCPCD_PATH = @DHCPCD_PATH@ +DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@ DLLTOOL = @DLLTOOL@ DNSMASQ_PATH = @DNSMASQ_PATH@ DSYMUTIL = @DSYMUTIL@ @@ -205,7 +209,6 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ GNUTLS_LIBS = @GNUTLS_LIBS@ GREP = @GREP@ GTKDOC_CHECK = @GTKDOC_CHECK@ -GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@ GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ GTKDOC_MKPDF = @GTKDOC_MKPDF@ @@ -250,12 +253,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBM = @LIBM@ +LIBNDP_CFLAGS = @LIBNDP_CFLAGS@ +LIBNDP_LIBS = @LIBNDP_LIBS@ LIBNL_CFLAGS = @LIBNL_CFLAGS@ LIBNL_LIBS = @LIBNL_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@ LIBSOUP_LIBS = @LIBSOUP_LIBS@ +LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@ +LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ @@ -272,6 +279,8 @@ MOC = @MOC@ MSGFMT = @MSGFMT@ MSGFMT_015 = @MSGFMT_015@ MSGMERGE = @MSGMERGE@ +NEWT_CFLAGS = @NEWT_CFLAGS@ +NEWT_LIBS = @NEWT_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NM_MAJOR_VERSION = @NM_MAJOR_VERSION@ @@ -299,11 +308,16 @@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POLKIT_CFLAGS = @POLKIT_CFLAGS@ POLKIT_LIBS = @POLKIT_LIBS@ POSUB = @POSUB@ +PPPD_PATH = @PPPD_PATH@ PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +PPPOE_PATH = @PPPOE_PATH@ QT_CFLAGS = @QT_CFLAGS@ QT_LIBS = @QT_LIBS@ RANLIB = @RANLIB@ +READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ +SELINUX_CFLAGS = @SELINUX_CFLAGS@ +SELINUX_LIBS = @SELINUX_LIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ @@ -318,6 +332,7 @@ UDEV_BASE_DIR = @UDEV_BASE_DIR@ USE_NLS = @USE_NLS@ UUID_CFLAGS = @UUID_CFLAGS@ UUID_LIBS = @UUID_LIBS@ +VALGRIND_RULES = @VALGRIND_RULES@ VAPIGEN = @VAPIGEN@ VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@ VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@ @@ -380,6 +395,7 @@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ +subdirs = @subdirs@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ @@ -390,6 +406,7 @@ with_dhclient = @with_dhclient@ with_dhcpcd = @with_dhcpcd@ with_netconfig = @with_netconfig@ with_resolvconf = @with_resolvconf@ +with_valgrind = @with_valgrind@ completiondir = $(datadir)/bash-completion/completions dist_completion_DATA = nmcli all: all-am diff --git a/cli/completion/nmcli b/cli/completion/nmcli index 7bd9431a..343c7322 100644 --- a/cli/completion/nmcli +++ b/cli/completion/nmcli @@ -1,5 +1,5 @@ -# nmcli completion -*- shell-script -*- -# Based on +# nmcli(1) completion -*- shell-script -*- +# Originally based on # https://github.com/GArik/bash-completion/blob/master/completions/nmcli _nmcli_list() @@ -7,188 +7,1227 @@ _nmcli_list() COMPREPLY=( $( compgen -W '$1' -- $cur ) ) } -_nmcli_con_id() +_nmcli_list_nl() { local IFS=$'\n' - COMPREPLY=( $( compgen -W "$(nmcli -t -f NAME con list 2>/dev/null )" -- $cur ) ) + COMPREPLY=( $( compgen -W '$1' -- $cur ) ) + + # Now escape special characters (spaces, single and double quotes), + # so that the argument is really regarded a single argument by bash. + # See http://stackoverflow.com/questions/1146098/properly-handling-spaces-and-quotes-in-bash-completion + local escaped_single_quote="'\''" + local i=0 + local entry + for entry in ${COMPREPLY[*]} + do + if [[ "${cur:0:1}" == "'" ]]; then + # started with single quote, escaping only other single quotes + # [']bla'bla"bla\bla bla --> [']bla'\''bla"bla\bla bla + COMPREPLY[$i]="${entry//\'/${escaped_single_quote}}" + elif [[ "${cur:0:1}" == '"' ]]; then + # started with double quote, escaping all double quotes and all backslashes + # ["]bla'bla"bla\bla bla --> ["]bla'bla\"bla\\bla bla + entry="${entry//\\/\\\\}" + entry="${entry//\"/\\\"}" + COMPREPLY[$i]="$entry" + else + # no quotes in front, escaping _everything_ + # [ ]bla'bla"bla\bla bla --> [ ]bla\'bla\"bla\\bla\ bla + entry="${entry//\\/\\\\}" + entry="${entry//\'/\'}" + entry="${entry//\"/\\\"}" + entry="${entry// /\\ }" + COMPREPLY[$i]="$entry" + fi + (( i++ )) + done } -_nmcli_con_uuid() +_nmcli_con_show() { - COMPREPLY=( $( compgen -W "$(nmcli -t -f UUID con list 2>/dev/null )" -- $cur ) ) + nmcli -t -f "$1" connection show $2 2> /dev/null } -_nmcli_ap_ssid() +_nmcli_wifi_list() { - local IFS=$'\n' - COMPREPLY=( $( compgen -W "$(nmcli -t -f SSID dev wifi list 2>/dev/null )" -- $cur ) ) + nmcli -t -f "$1" device wifi list 2>/dev/null } -_nmcli_ab_bssid() +_nmcli_dev_status() { - COMPREPLY=( $( compgen -W "$(nmcli -t -f BSSID dev wifi list 2>/dev/null )" -- $cur ) ) + nmcli -t -f "$1" device status 2>/dev/null } -_nmcli() +_nmcli_array_has_value() { + # expects the name of an array as first parameter and + # returns true if if one of the remaining arguments is + # contained in the array ${$1[@]} + eval "local ARRAY=(\"\${$1[@]}\")" + local arg a + shift + for arg; do + for a in "${ARRAY[@]}"; do + if [[ "$a" = "$arg" ]]; then + return 0 + fi + done + done + return 1 +} + +_nmcli_array_delete_at() { - local cur prev words cword - _init_completion || return + eval "local ARRAY=(\"\${$1[@]}\")" + local i + local tmp=() + local lower=$2 + local upper=${3:-$lower} - case $prev in - -m|--mode) - COMPREPLY=( $( compgen -W 'tabular multiline' -- $cur ) ) - return 0 + # for some reason the following fails. So this clumsy workaround... + # A=(a "") + # echo " >> ${#A[@]}" + # >> 2 + # A=("${A[@]:1}") + # echo " >> ${#A[@]}" + # >> 0 + # ... seriously??? + + for i in "${!ARRAY[@]}"; do + if [[ "$i" -lt "$2" || "$i" -gt "${3-$2}" ]]; then + tmp=("${tmp[@]}" "${ARRAY[$i]}") + fi + done + eval "$1=(\"\${tmp[@]}\")" +} + +_nmcli_compl_match_option() +{ + local S="$1" + local V + shift + if [[ "${S:0:2}" == "--" ]]; then + S="${S:2}" + elif [[ "${S:0:1}" == "-" ]]; then + S="${S:1}" + fi + for V; do + case "$V" in + "$S"*) + printf "%s" "$V" + return 0 + ;; + esac + done + return 1 +} + +# OPTIONS appear first at the command line (before the OBJECT). +# This iterates over the argument list and tries to complete +# the options. If there are options that are to be completed, +# zero is returned and completion will be performed. +# Otherwise it will remove all the option parameters from the ${words[@]} +# array and return with zero (so that completion of OBJECT can continue). +_nmcli_compl_OPTIONS() +{ + local i W + + for (( ; ; )); do + if [[ "${#words[@]}" -le 1 ]]; then + return 1 + fi + W="$(_nmcli_compl_match_option "${words[0]}" "${LONG_OPTIONS[@]}")" + if [[ $? != 0 ]]; then + return 2 + fi + + # remove the options already seen. + for i in ${!LONG_OPTIONS[@]}; do + if [[ "${LONG_OPTIONS[$i]}" == "$W" ]]; then + _nmcli_array_delete_at LONG_OPTIONS $i + break + fi + done + + if [[ "$HELP_ONLY_AS_FIRST" == '1' ]]; then + for i in ${!LONG_OPTIONS[@]}; do + if [[ "${LONG_OPTIONS[$i]}" == "help" ]]; then + _nmcli_array_delete_at LONG_OPTIONS $i + break + fi + done + fi + + case "$W" in + terse) + _nmcli_array_delete_at words 0 + ;; + pretty) + _nmcli_array_delete_at words 0 + ;; + nocheck) + _nmcli_array_delete_at words 0 + ;; + ask) + _nmcli_array_delete_at words 0 + ;; + active) + _nmcli_array_delete_at words 0 + ;; + version) + _nmcli_array_delete_at words 0 + ;; + help) + _nmcli_array_delete_at words 0 + if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then + HELP_ONLY_AS_FIRST=0 + return 0 + fi + HELP_ONLY_AS_FIRST=0 + ;; + temporary) + _nmcli_array_delete_at words 0 + ;; + mode) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "tabular multiline" + return 0 + fi + _nmcli_array_delete_at words 0 1 + ;; + fields) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "all common + NAME UUID TYPE TIMESTAMP TIMESTAMP-REAL AUTOCONNECT READONLY DBUS-PATH ACTIVE DEVICE STATE ACTIVE-PATH + connection 802-3-ethernet 802-1x 802-11-wireless 802-11-wireless-security ipv4 ipv6 serial ppp pppoe gsm cdma bluetooth 802-11-olpc-mesh vpn wimax infiniband bond vlan adsl bridge bridge-port team team-port dcb + GENERAL IP4 DHCP4 IP6 DHCP6 VPN + profile active" + return 0 + fi + _nmcli_array_delete_at words 0 1 + ;; + escape) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "no yes" + return 0 + fi + _nmcli_array_delete_at words 0 1 + ;; + wait) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "" + return 0 + fi + _nmcli_array_delete_at words 0 1 + ;; + *) + # something unexpected. We are finished with parsing the OPTIONS. + return 2 + ;; + esac + done +} + +# after the OPTIONS, the OBJECT, the COMMAND and possible the COMMAND_CONNECTION, the syntax for nmcli +# expects several options with parameters. This function can parse them and remove them from the words array. +_nmcli_compl_ARGS() +{ + local OPTIONS_ALL N_REMOVE_WORDS REMOVE_OPTIONS OPTIONS_HAS_MANDATORY i + OPTIONS_ALL=("${OPTIONS[@]}") + OPTIONS_UNKNOWN_OPTION= + + OPTIONS_HAS_MANDATORY=0 + if [[ "${#OPTIONS_MANDATORY[@]}" -ge 1 ]]; then + OPTIONS_HAS_MANDATORY=1 + fi + + for (( ; ; )); do + if [[ "${#words[@]}" -le 1 ]]; then + # we have no more words left... + if [[ ${#OPTIONS[@]} -eq 0 ]]; then + return 1; + fi + if [[ "$COMMAND_ARGS_WAIT_OPTIONS" -ne 1 ]]; then + _nmcli_list "$(echo "${OPTIONS[@]}")" + return 0 + fi + COMMAND_ARGS_WAIT_OPTIONS=0 + return 1 + fi + if ! _nmcli_array_has_value OPTIONS_ALL "${words[0]}"; then + # This is an entirely unknown option. + OPTIONS_UNKNOWN_OPTION="?${words[0]}" + return 1 + fi + if [[ "$OPTIONS_HAS_MANDATORY" -eq 1 && "${#OPTIONS_MANDATORY[@]}" -eq 0 ]]; then + # we had some mandatory options, but they are all satisfied... stop here... + # This means, that we can continue with more additional options from the NEXT_GROUP. + return 1 + fi + + N_REMOVE_WORDS=2 + REMOVE_OPTIONS=("${words[0]}") + case "${words[0]}" in + level) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "ERR WARN INFO DEBUG" + return 0 + fi + ;; + domains) + if [[ "${#words[@]}" -eq 2 ]]; then + local OPTIONS_DOM=(ALL DEFAULT PLATFORM RFKILL ETHER WIFI BT MB DHCP4 DHCP6 PPP WIFI_SCAN IP4 IP6 AUTOIP4 DNS VPN SHARING SUPPLICANT AGENTS SETTINGS SUSPEND CORE DEVICE OLPC WIMAX INFINIBAND FIREWALL ADSL BOND VLAN BRIDGE DBUS_PROPS TEAM CONCHECK DCB DISPATCH) + if [[ "${words[1]}" != "" ]]; then + + # split the comma separaeted domain string into its parts LOGD + local oIFS="$IFS" + IFS="," + local LOGD=($(printf '%s' "${words[1]}" | sed 's/\(^\|,\)/,#/g')) + IFS="$oIFS" + unset oIFS + + local LOGDLAST LOGDLAST_IS_OPTION LOGDI i + # first we iterate over all present domains and remove them from OPTIONS_DOM + for LOGDI in ${LOGD[@]}; do + LOGDI="${LOGDI:1}" + LOGDLAST="$LOGDI" + LOGDLAST_IS_OPTION=0 + for i in ${!OPTIONS_DOM[*]}; do + if [[ "${OPTIONS_DOM[$i]}" = "$LOGDI" ]]; then + LOGDLAST_IS_OPTION=1 + unset OPTIONS_DOM[$i] + fi + done + done + + local OPTIONS_DOM2=() + if [[ "$LOGDLAST" = "" ]]; then + # we have a word that ends with ','. Just append all remaining options. + for i in ${!OPTIONS_DOM[*]}; do + OPTIONS_DOM2[${#OPTIONS_DOM2[@]}]="${words[1]}${OPTIONS_DOM[$i]}" + done + else + # if the last option is not "" we keep only those option with the same prefix + # as the last domain (LOGDLAST) + for i in ${!OPTIONS_DOM[*]}; do + if [[ "${OPTIONS_DOM[$i]:0:${#LOGDLAST}}" == "$LOGDLAST" ]]; then + # modify the option with the present prefix + OPTIONS_DOM2[${#OPTIONS_DOM2[@]}]="${words[1]}${OPTIONS_DOM[$i]:${#LOGDLAST}}" + fi + done + + if [[ $LOGDLAST_IS_OPTION -eq 1 ]]; then + # if the last logd itself was a valid iption, ${words[1]} is itself a valid match + OPTIONS_DOM2[${#OPTIONS_DOM2[@]}]="${words[1]}" + + # also, add all remaining options by comma separated to the word. + for i in ${!OPTIONS_DOM[*]}; do + OPTIONS_DOM2[${#OPTIONS_DOM2[@]}]="${words[1]},${OPTIONS_DOM[$i]}" + done + fi + if [[ ${#OPTIONS_DOM2[@]} -eq 1 ]]; then + for i in ${!OPTIONS_DOM[*]}; do + if [[ "$LOGDLAST" != "${OPTIONS_DOM[$i]:0:${#LOGDLAST}}" ]]; then + OPTIONS_DOM2[${#OPTIONS_DOM2[@]}]="${OPTIONS_DOM2[0]},${OPTIONS_DOM[$i]}" + fi + done + fi + + fi + OPTIONS_DOM=(${OPTIONS_DOM2[@]}) + fi + + _nmcli_list "$(echo "${OPTIONS_DOM[@]}")" + return 0 + fi + ;; + type) + if [[ "$OPTIONS_TYPE" != "" ]]; then + return 1 + fi + if [[ "${#words[@]}" -eq 2 ]]; then + if [[ "${words[1]:0:1}" = "8" ]]; then + # usually we don't want to show the 802-x types (because the shorter aliases are more + # user friendly. Only complete them, if the current word already starts with an "8". + _nmcli_list "802-3-ethernet 802-11-wireless 802-11-olpc-mesh" + else + _nmcli_list "ethernet wifi wimax gsm cdma infiniband bluetooth vpn olpc-mesh vlan bond bond-slave bridge bridge-slave team team-slave pppoe" + fi + return 0 + fi + OPTIONS_TYPE="${words[1]}" + + if [[ "x$OPTIONS_MANDATORY_IFNAME" != x ]]; then + # the ifname is not a mandatory option for a few connection types... + # Check, if we have such a 'type' and remove the 'ifname' from the list + # of mandatory options. + case "$OPTIONS_TYPE" in + vl|vla|vlan| \ + bond| \ + team| \ + bridge) + for i in ${!OPTIONS_MANDATORY[*]}; do + if [[ "${OPTIONS_MANDATORY[$i]}" = "ifname" ]]; then + unset OPTIONS_MANDATORY[$i] + fi + done + ;; + *) + ;; + + esac + OPTIONS_MANDATORY_IFNAME= + fi + ;; + master) + if [[ "${#words[@]}" -eq 2 ]]; then + if [[ "${words[1]}" = "" ]]; then + _nmcli_list_nl "$(_nmcli_dev_status DEVICE)" + else + _nmcli_list_nl "$(printf "%s\n%s\n%s" "$(_nmcli_dev_status DEVICE)" "$(_nmcli_con_show UUID)")" + fi + return 0 + fi + ;; + dev) + if [[ "${#words[@]}" -eq 2 ]]; then + if [[ "${words[1]}" = "" ]]; then + _nmcli_list_nl "$(_nmcli_dev_status DEVICE)" + else + _nmcli_list_nl "$(printf "%s\n%s\n%s" "$(_nmcli_dev_status DEVICE)" "$(_nmcli_wifi_list BSSID)" "$(_nmcli_con_show UUID)")" + fi + return 0 + fi + ;; + primary| \ + ifname) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list_nl "$(_nmcli_dev_status DEVICE)" + return 0 + fi + ;; + mode) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "balance-rr active-backup balance-xor broadcast 802.3ad balance-tlb balance-alb" + return 0 + fi + ;; + transport-mode) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "datagram connected" + return 0 + fi + ;; + vpn-type) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "vpnc openvpn pptp openconnect openswan libreswan ssh l2tp iodine" + return 0 + fi + ;; + bt-type) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "panu dun-gsm dun-cdma" + return 0 + fi + ;; + wep-key-type) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "key phrase" + return 0 + fi + ;; + autoconnect| \ + stp| \ + hairpin| \ + save| \ + private) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "yes no" + return 0 + fi + ;; + config) + if [[ "${#words[@]}" -eq 2 ]]; then + compopt -o default + COMPREPLY=() + return 0 + fi + ;; + ip4| \ + ip6| \ + gw4| \ + gw6| \ + priority| \ + forward-delay| \ + hello-time| \ + max-age| \ + ageing-time| \ + nsp| \ + path-cost| \ + name| \ + mtu| \ + cloned-mac| \ + addr| \ + parent| \ + miimon| \ + arp-interval| \ + arp-ip-target| \ + downdelay| \ + updelay| \ + p-key| \ + mac| \ + id| \ + flags| \ + ingress| \ + dhcp-anycast| \ + channel| \ + egress| \ + apn| \ + con-name| \ + user| \ + username| \ + service| \ + password) + if [[ "${#words[@]}" -eq 2 ]]; then + return 0 + fi + ;; + ssid) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list_nl "$(_nmcli_wifi_list SSID)" + return 0 + fi + ;; + ap| \ + bssid) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list_nl "$(_nmcli_wifi_list BSSID)" + return 0 + fi + ;; + *) + return 1 + ;; + esac + + + if [[ "${#OPTIONS_NEXT_GROUP[@]}" -gt 0 ]]; then + if _nmcli_array_has_value OPTIONS_NEXT_GROUP "${words[0]}"; then + # the current value is from the next group... + # We back off, because the current group is complete. + return 1 + fi + fi + + _nmcli_array_delete_at words 0 $((N_REMOVE_WORDS-1)) + # remove the options already seen. + for i in ${!OPTIONS[*]}; do + if [[ "${OPTIONS[$i]}" = "${REMOVE_OPTIONS[0]}" || "${OPTIONS[$i]}" = "${REMOVE_OPTIONS[1]}" ]]; then + unset OPTIONS[$i] + fi + done + for i in ${!OPTIONS_MANDATORY[*]}; do + if [[ "${OPTIONS_MANDATORY[$i]}" = "${REMOVE_OPTIONS[0]}" || "${OPTIONS_MANDATORY[$i]}" = "${REMOVE_OPTIONS[1]}" ]]; then + unset OPTIONS_MANDATORY[$i] + fi + done + done +} + +# some commands expect a connection as parameter. This connection can either be given +# as id|uuid|path|apath. Parse that connection parameter. +_nmcli_compl_ARGS_CONNECTION() +{ + if ! _nmcli_array_has_value OPTIONS "${words[0]}"; then + COMMAND_CONNECTION_TYPE= + COMMAND_CONNECTION_ID="${words[0]}" + _nmcli_array_delete_at words 0 + return 1 + fi + COMMAND_CONNECTION_TYPE="${words[0]}" + COMMAND_CONNECTION_ID="${words[1]}" + local CON_TYPE= + if [[ "x$COMMAND_CONNECTION_ACTIVE" != x ]]; then + CON_TYPE=--active + fi + case "${words[0]}" in + id) + if [[ ${#words[@]} -le 2 ]]; then + _nmcli_list_nl "$(_nmcli_con_show NAME $CON_TYPE)" + return 0 + fi + _nmcli_array_delete_at words 0 1 ;; - -f|--fields) - COMPREPLY=( $( compgen -W 'all common' -- $cur ) ) - return 0 + uuid) + if [[ ${#words[@]} -le 2 ]]; then + _nmcli_list_nl "$(_nmcli_con_show UUID $CON_TYPE)" + return 0 + fi + _nmcli_array_delete_at words 0 1 ;; - -e|--escape) - _nmcli_list "yes no" - return 0 + path) + if [[ ${#words[@]} -le 2 ]]; then + _nmcli_list_nl "$(_nmcli_con_show DBUS-PATH $CON_TYPE)" + return 0 + fi + _nmcli_array_delete_at words 0 1 ;; - id) - _nmcli_con_id - return 0 + apath) + if [[ ${#words[@]} -le 2 ]]; then + _nmcli_list_nl "$(_nmcli_con_show ACTIVE-PATH --active)" + return 0 + fi + _nmcli_array_delete_at words 0 1 ;; - uuid) - _nmcli_con_uuid - return 0 + ifname) + if [[ ${#words[@]} -le 2 ]]; then + _nmcli_list_nl "$(_nmcli_dev_status DEVICE)" + return 0 + fi + _nmcli_array_delete_at words 0 1 ;; - iface) - _available_interfaces - return 0 + *) + COMMAND_CONNECTION_TYPE= + COMMAND_CONNECTION_ID="${words[0]}" + _nmcli_array_delete_at words 0 ;; - bssid) - _nmcli_ab_bssid + esac + return 1 +} + +_nmcli_compl_COMMAND() { + local command="$1" + shift + local V=("$@") + local H= + if [[ "${command[0]:0:1}" != '-' ]]; then + H=help + elif [[ "${command[0]:1:1}" == '-' || "${command[0]}" == "-" ]]; then + H=--help + else + H=-help + fi + if [[ "x$COMPL_COMMAND_NO_HELP" == x ]]; then + V=("${V[@]}" "$H") + fi + _nmcli_list "${V[*]}" +} + +_nmcli_compl_COMMAND_nl() { + local command="$1" + local a="$2" + shift + shift + local V=("$@") + local H= + if [[ "${command[0]:0:1}" != '-' ]]; then + V=("${V[@]/#/--}") + H=help + elif [[ "${command[0]:1:1}" == '-' || "${command[0]}" == "-" ]]; then + V=("${V[@]/#/--}") + H=--help + else + V=("${V[@]/#/-}") + H=-help + fi + if [[ "x$COMPL_COMMAND_NO_HELP" == x ]]; then + V=("${V[@]}" "$H") + fi + local IFS=$'\n' + V="${V[*]}" + _nmcli_list_nl "$(printf "%s%s\n%s" "" "$V" "$a")" +} + +_nmcli() +{ + local cur prev words cword i + _init_completion || return + + # we don't care about any arguments after the current cursor position + # because we only parse from left to right. So, if there are some arguments + # right of the cursor, just ignore them. Also don't care about ${words[0]}. + _nmcli_array_delete_at words $((cword+1)) ${#words[@]} + _nmcli_array_delete_at words 0 + + # _init_completion returns the words with all the quotes and escaping + # characters. We don't care about them, drop them at first. + for i in ${!words[@]}; do + words[i]="$(printf '%s' "${words[i]}" | xargs printf '%s\n' 2>/dev/null || true)" + done + + # In case the cursor is not at the end of the line, + # $cur consists of spaces that we want do remove. + # For example: `nmcli connection modify id <TAB> lo` + if [[ "$cur" =~ ^[[:space:]]+ ]]; then + cur='' + fi + + local OPTIONS_UNKNOWN_OPTION OPTIONS_TYPE OPTIONS_TYPED OPTIONS OPTIONS_MANDATORY COMMAND_ARGS_WAIT_OPTIONS OPTIONS_IP OPTIONS_MANDATORY OPTIONS_NEXT_GROUP + local COMMAND_CONNECTION_TYPE COMMAND_CONNECTION_ID OPTIONS_MANDATORY_IFNAME HELP_ONLY_AS_FIRST + local COMMAND_CONNECTION_ACTIVE="" + + HELP_ONLY_AS_FIRST= + local LONG_OPTIONS=(terse pretty mode fields escape nocheck ask wait version help) + _nmcli_compl_OPTIONS + i=$? + + if [[ "$HELP_ONLY_AS_FIRST" == '0' ]]; then + # got a --help. No more completion. + return 0 + fi + + case $i in + 0) return 0 ;; - wep-key-type) - _nmcli_list "key phrase" + 1) + # we show for completion either the (remaining) OPTIONS + # (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) + elif [[ "${words[0]:1:1}" == '-' || "${words[0]}" == "-" ]]; then + OPTIONS=("${LONG_OPTIONS[@]/#/--}") + else + OPTIONS=("${LONG_OPTIONS[@]/#/-}") + fi + _nmcli_list "${OPTIONS[*]}" return 0 ;; esac - if [[ $cword -eq 1 ]] ; then - if [[ "$cur" == -* ]]; then - COMPREPLY=( $( compgen -W '--terse --pretty --mode --fields \ - --escape --version --help' -- $cur ) ) - else - COMPREPLY=( $( compgen -W "nm con dev" -- $cur ) ) - fi - else - local object=${words[1]} - local command=${words[2]} - - case $object in - nm) - case $command in - enable) - _nmcli_list "true false" - return 0 - ;; - sleep) - _nmcli_list "true false" - return 0 + local command="${words[1]}" + case "${words[0]}" in + h|he|hel|help) + ;; + g|ge|gen|gene|gener|genera|general) + if [[ ${#words[@]} -eq 2 ]]; then + _nmcli_compl_COMMAND "$command" status permissions logging hostname + elif [[ ${#words[@]} -gt 2 ]]; then + case "$command" in + ho|hos|host|hostn|hostna|hostnam|hostname) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" \ + "$(printf '%s\n%s\n%s\n' \ + "$(nmcli general hostname 2>/dev/null)" \ + "$(cat /etc/hostname 2>/dev/null)" \ + "$(hostnamectl status 2>/dev/null | sed -n '1s/^.\+hostname: \(.\+\)$/\1/p')" \ + "$HOSTNAME")" + fi ;; - wifi) - _nmcli_list "on off" - return 0 + l|lo|log|logg|loggi|loggin|logging) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND "${words[2]}" level domains + else + _nmcli_array_delete_at words 0 1 + OPTIONS=(level domains) + _nmcli_compl_ARGS + fi ;; - wwan) - _nmcli_list "on off" - return 0 + esac + fi + ;; + n|ne|net|netw|netwo|networ|network|networki|networkin|networking) + if [[ ${#words[@]} -eq 2 ]]; then + _nmcli_compl_COMMAND "$command" on off connectivity + elif [[ ${#words[@]} -eq 3 ]]; then + case "$command" in + c|co|con|conn|conne|connec|connect|connecti|connectiv|connectivi|connectivit|connectivity) + _nmcli_compl_COMMAND "${words[2]}" "check" ;; - wimax) - _nmcli_list "on off" - return 0 + esac + fi + ;; + r|ra|rad|radi|radio) + if [[ ${#words[@]} -eq 2 ]]; then + _nmcli_compl_COMMAND "$command" all wifi wwan wimax + elif [[ ${#words[@]} -eq 3 ]]; then + case "$command" in + a|al|all | w|wi|wif|wifi | ww|wwa|wwan | wim|wima|wimax) + _nmcli_compl_COMMAND "${words[2]}" "on off" ;; esac + fi + ;; + c|co|con|conn|conne|connec|connect|connecti|connectio|connection) + if [[ ${#words[@]} -eq 2 ]]; then + _nmcli_compl_COMMAND "$command" show up down add modify edit delete reload load + elif [[ ${#words[@]} -gt 2 ]]; then + case "$command" in + s|sh|sho|show) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME)")" active + elif [[ ${#words[@]} -gt 3 ]]; then + _nmcli_array_delete_at words 0 1 - COMPREPLY=( $( compgen -W 'status permissions enable sleep \ - wifi wwan wimax' -- $cur ) ) - ;; - con) - case $command in - list) - COMPREPLY=( $( compgen -W 'id uuid' -- $cur ) ) - return 0 - ;; - up) - if [[ "$cur" == -* ]]; then - COMPREPLY=( $( compgen -W '--nowait --timeout' \ - -- $cur ) ) - else - COMPREPLY=( $( compgen -W 'id uuid iface ap nsp' \ - -- $cur ) ) + LONG_OPTIONS=(help active) + HELP_ONLY_AS_FIRST=1 + _nmcli_compl_OPTIONS + i=$? + + if ! _nmcli_array_has_value LONG_OPTIONS active; then + COMMAND_CONNECTION_ACTIVE=1 + fi + + case $i in + 0) + return 0 + ;; + 1) + if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then + if [[ "x$COMMAND_CONNECTION_ACTIVE" = x ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME)")" "${LONG_OPTIONS[@]}" + + else + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME --active)")" "${LONG_OPTIONS[@]}" + fi + fi + return 0 + ;; + esac + + OPTIONS=(id uuid path apath) + while [[ ${#words[@]} -gt 0 ]]; do + _nmcli_compl_ARGS_CONNECTION && return 0 + done + if [[ "x$COMMAND_CONNECTION_ACTIVE" = x ]]; then + _nmcli_list_nl "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME)")" + else + _nmcli_list_nl "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME --active)")" + fi fi - return 0 ;; - down) - COMPREPLY=( $( compgen -W 'id uuid' -- $cur ) ) - return 0 - ;; - delete) - COMPREPLY=( $( compgen -W 'id uuid' -- $cur ) ) - return 0 + u|up) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "ifname\nid\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" + elif [[ ${#words[@]} -gt 3 ]]; then + _nmcli_array_delete_at words 0 1 + + LONG_OPTIONS=(help) + HELP_ONLY_AS_FIRST=1 + _nmcli_compl_OPTIONS + + case $? in + 0) + return 0 + ;; + 1) + if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "ifname\nid\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" "${LONG_OPTIONS[@]}" + fi + return 0 + ;; + esac + + local COMMAND_CONNECTION_TYPE='' + OPTIONS=(ifname id uuid path) + _nmcli_compl_ARGS_CONNECTION && return 0 + + if [[ "$COMMAND_CONNECTION_TYPE" = "ifname" ]]; then + OPTIONS=(ap nsp) + else + OPTIONS=(ifname ap nsp) + fi + _nmcli_compl_ARGS + fi ;; - esac + d|do|dow|down) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME --active)")" + elif [[ ${#words[@]} -gt 3 ]]; then + _nmcli_array_delete_at words 0 1 - COMPREPLY=( $( compgen -W 'list status up down delete' \ - -- $cur ) ) - ;; - dev) - case $command in - list) - COMPREPLY=( $( compgen -W 'iface' -- $cur ) ) - return 0 - ;; - disconnect) - if [[ "$cur" == -* ]]; then - COMPREPLY=( $( compgen -W '--nowait --timeout' \ - -- $cur ) ) - else - COMPREPLY=( $( compgen -W 'iface' -- $cur ) ) + LONG_OPTIONS=(help) + HELP_ONLY_AS_FIRST=1 + _nmcli_compl_OPTIONS + case $? in + 0) + return 0 + ;; + 1) + if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME --active)")" "${LONG_OPTIONS[@]}" + fi + return 0 + ;; + esac + + OPTIONS=(id uuid path apath) + COMMAND_CONNECTION_ACTIVE=1 + _nmcli_compl_ARGS_CONNECTION && return 0 fi - return 0 ;; - wifi) - local subcommand=${words[3]} + a|ad|add) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND "${words[2]}" type ifname con-name autoconnect + elif [[ ${#words[@]} -gt 3 ]]; then + _nmcli_array_delete_at words 0 1 + + LONG_OPTIONS=(help) + HELP_ONLY_AS_FIRST=1 + _nmcli_compl_OPTIONS + case $? in + 0) + return 0 + ;; + 1) + if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then + _nmcli_compl_COMMAND "${words[2]}" type ifname con-name autoconnect + fi + return 0 + ;; + esac + + OPTIONS_TYPE= + OPTIONS=(type ifname con-name autoconnect save) + OPTIONS_MANDATORY=(type ifname) + COMMAND_ARGS_WAIT_OPTIONS=1 + OPTIONS_MANDATORY_IFNAME=1 + _nmcli_compl_ARGS && return 0 - case $subcommand in - list) - COMPREPLY=( $( compgen -W 'iface bssid' \ - -- $cur ) ) + OPTIONS_MANDATORY_IFNAME= + if _nmcli_array_has_value OPTIONS "${OPTIONS_MANDATORY[@]}"; then + # we still have some missing mandatory options... + if [[ "$OPTIONS_UNKNOWN_OPTION" != '' ]]; then + if ! _nmcli_array_has_value OPTIONS "${OPTIONS_UNKNOWN_OPTION:1}"; then + # if we encountered an unknown option while having mandatory + # options, just return. + return 0 + fi + fi + _nmcli_list "$(echo "${OPTIONS[@]}")" return 0 - ;; - connect) - if [[ "$cur" == -* ]]; then - COMPREPLY=( $( compgen -W '--private \ - --nowait --timeout' -- $cur ) ) + fi + + OPTIONS_IP=(ip4 ip6 gw4 gw6) + OPTIONS_MANDATORY=() + case "$OPTIONS_TYPE" in + 802-3|802-3-|802-3-e|802-3-et|802-3-eth|802-3-ethe|802-3-ether|802-3-ethern|802-3-etherne|802-3-ethernet| \ + e|et|eth|ethe|ether|ethern|etherne|ethernet) + OPTIONS_TYPED=(mac cloned-mac mtu) + ;; + 802-11-w|802-11-wi|802-11-wir|802-11-wire|802-11-wirel|802-11-wirele|802-11-wireles|802-11-wireless| \ + wif|wifi) + OPTIONS_TYPED=(ssid mac cloned-mac mtu) + OPTIONS_MANDATORY=(ssid) + ;; + wim|wima|wimax) + OPTIONS_TYPED=(mac nsp) + ;; + g|gs|gsm) + OPTIONS_TYPED=(apn user password) + OPTIONS_MANDATORY=(apn) + ;; + c|cd|cdm|cdma) + OPTIONS_TYPED=(user password) + ;; + i|in|inf|infi|infin|infini|infinib|infiniba|infiniban|infiniband) + OPTIONS_TYPED=(mac mtu transport-mode parent p-key) + ;; + bl|blu|blue|bluet|blueto|bluetoo|bluetoot|bluetooth) + OPTIONS_TYPED=(addr bt-type) + ;; + vl|vla|vlan) + OPTIONS_TYPED=(dev id flags ingress egress mtu) + OPTIONS_MANDATORY=(dev) + ;; + bond) + OPTIONS_TYPED=(mode miimon downdelay updelay arp-interval arp-ip-target primary) + ;; + bond-|bond-s|bond-sl|bond-sla|bond-slav|bond-slave) + OPTIONS_TYPED=(master) + OPTIONS_MANDATORY=(master) + OPTIONS_IP=() + ;; + team) + OPTIONS_TYPED=(config) + ;; + team-|team-s|team-sl|team-sla|team-slav|team-slave) + OPTIONS_TYPED=(master config) + OPTIONS_MANDATORY=(master) + OPTIONS_IP=() + ;; + bridge) + OPTIONS_TYPED=(stp priority forward-delay hello-time max-age ageing-time mac) + ;; + bridge-|bridge-s|bridge-sl|bridge-sla|bridge-slav|bridge-slave) + OPTIONS_TYPED=(master priority path-cost hairpin) + OPTIONS_MANDATORY=(master) + OPTIONS_IP=() + ;; + vp|vpn) + OPTIONS_TYPED=(vpn-type user) + OPTIONS_MANDATORY=(vpn-type) + ;; + 802-11-o|802-11-ol|802-11-olp|802-11-olpc|802-11-olpc-|802-11-olpc-m|802-11-olpc-me|802-11-olpc-mes|802-11-olpc-mesh| \ + o|ol|olp|olpc|olpc-|olpc-m|olpc-me|olpc-mes|olpc-mesh) + OPTIONS_TYPED=(ssid channel dhcp-anycast) + OPTIONS_MANDATORY=(ssid) + ;; + p|pp|ppp|pppo|pppoe) + OPTIONS_TYPED=(username password service mtu mac) + OPTIONS_MANDATORY=(username) + ;; + *) + # for an unknown connection type, we stop completion here + return 0 + ;; + esac + if [[ "$COMMAND_ARGS_WAIT_OPTIONS" -ne 1 ]]; then + # means, we are at the end of options. Nothing more to parse, just show + # what are the options now. + if [[ "${#OPTIONS_MANDATORY[@]}" -gt 0 ]]; then + _nmcli_list "$(echo "${OPTIONS[@]}") $(echo "${OPTIONS_TYPED[@]}")" else - if [[ "$prev" == "connect" ]]; then - _nmcli_ap_ssid + _nmcli_list "$(echo "${OPTIONS[@]}") $(echo "${OPTIONS_TYPED[@]}") $(echo "${OPTIONS_IP[@]}")" + fi + return 0 + fi + if [[ "${#OPTIONS[@]}" -gt 0 ]]; then + # we still have some options from before, but no mandatory ones. Mix them with OPTIONS_TYPED + # and continue parsing the options... + OPTIONS=("${OPTIONS[@]}" "${OPTIONS_TYPED[@]}") + OPTIONS_NEXT_GROUP=("${OPTIONS_TYPED[@]}") + _nmcli_compl_ARGS && return 0 + + if [[ "$COMMAND_ARGS_WAIT_OPTIONS" -ne 1 ]]; then + # means, we are at the end of options. Nothing more to parse, just show + # what are the options now. + if [[ "${#OPTIONS_MANDATORY[@]}" -gt 0 ]]; then + _nmcli_list "$(echo "${OPTIONS[@]}")" else - COMPREPLY=( $( compgen -W 'password \ - wep-key-type iface bssid name' \ - -- $cur ) ) + _nmcli_list "$(echo "${OPTIONS[@]}") $(echo "${OPTIONS_IP[@]}")" + fi + return 0 + fi + + if [[ "$OPTIONS_UNKNOWN_OPTION" != "" ]]; then + # there was an unknown option specified. Maybe we have to stop with the completion. + if [[ "${#OPTIONS_MANDATORY[@]}" -gt 0 ]]; then + # we have an unknown option, but still mandatory ones that must be fullfiled first. + return 0 fi + if ! _nmcli_array_has_value OPTIONS_IP "${OPTIONS_UNKNOWN_OPTION:1}"; then + # the unknown option is NOT an IP option. + return 0 + fi + # The unknown option is an IP option, which is fine... continue... fi + + fi + OPTIONS=("${OPTIONS_TYPED[@]}") + OPTIONS_NEXT_GROUP=() + + if [[ "${#OPTIONS_MANDATORY[@]}" -ge 1 ]]; then + # we have some mandatory options... don't check for IP options yet... + _nmcli_compl_ARGS && return 0 + + if _nmcli_array_has_value OPTIONS "${OPTIONS_MANDATORY[@]}"; then + _nmcli_list "$(echo "${OPTIONS[@]}")" + return 0 + fi + + if [[ "$OPTIONS_UNKNOWN_OPTION" != "" ]]; then + if ! _nmcli_array_has_value OPTIONS_IP "${OPTIONS_UNKNOWN_OPTION:1}"; then + # the unknown option is NOT an IP option. + return 0 + fi + fi + fi + + + # no mandatory options... do final completion including IP options + OPTIONS=("${OPTIONS[@]}" "${OPTIONS_IP[@]}") + OPTIONS_NEXT_GROUP=("${OPTIONS_IP[@]}") + _nmcli_compl_ARGS && return 0 + + if [[ "$OPTIONS_UNKNOWN_OPTION" != "" ]]; then return 0 - ;; - esac + fi - COMPREPLY=( $( compgen -W 'list connect' -- $cur ) ) - return 0 + if [[ "$COMMAND_ARGS_WAIT_OPTIONS" -ne 1 ]]; then + # means, we are at the end of options. Nothing more to parse, just show + # what are the options now. + _nmcli_list "$(echo "${OPTIONS[@]}")" + return 0 + fi + + # process the last group of options, as the OPTIONS_TYPED are already handled... + OPTIONS=("${OPTIONS_IP[@]}") + OPTIONS_NEXT_GROUP=() + COMMAND_ARGS_WAIT_OPTIONS=0 + _nmcli_compl_ARGS && return 0 + fi ;; - esac + e|ed|edi|edit) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\ntype\ncon-name\n%s" "$(_nmcli_con_show NAME)")" + elif [[ ${#words[@]} -gt 3 ]]; then + _nmcli_array_delete_at words 0 1 - COMPREPLY=( $( compgen -W 'status list disconnect wifi' \ - -- $cur ) ) - ;; - esac + LONG_OPTIONS=(help) + HELP_ONLY_AS_FIRST=1 + _nmcli_compl_OPTIONS - fi + case $? in + 0) + return 0 + ;; + 1) + if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\ntype\ncon-name\n%s" "$(_nmcli_con_show NAME)")" "${LONG_OPTIONS[@]}" + fi + return 0 + ;; + esac + + if [[ "${words[0]}" = 'type' || "${words[0]}" = 'con-name' ]]; then + OPTIONS=(type con-name) + _nmcli_compl_ARGS + else + OPTIONS=(id uuid path apath) + _nmcli_compl_ARGS_CONNECTION + fi + fi + ;; + m|mo|mod|modi|modif|modify) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" temporary + elif [[ ${#words[@]} -gt 3 ]]; then + _nmcli_array_delete_at words 0 1 + + LONG_OPTIONS=(help temporary) + HELP_ONLY_AS_FIRST=1 + _nmcli_compl_OPTIONS + case $? in + 0) + return 0 + ;; + 1) + if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" "${LONG_OPTIONS[@]}" + fi + return 0 + ;; + esac + + OPTIONS=(id uuid path apath) + _nmcli_compl_ARGS_CONNECTION && return 0 + while [[ "${#words[@]}" -gt 0 ]]; do + if [[ ${#words[@]} -le 1 ]]; then + _nmcli_list_nl "$(nmcli --fields profile connection show "${COMMAND_CONNECTION_TYPE:-id}" "$COMMAND_CONNECTION_ID" 2>/dev/null | sed -n 's/^\([^:]\+\):.*/\1/p')" + return 0 + elif [[ ${#words[@]} -le 2 ]]; then + return 0 + fi + _nmcli_array_delete_at words 0 1 + done + _nmcli_list_nl "$(nmcli --fields profile connection show "${COMMAND_CONNECTION_TYPE:-id}" "$COMMAND_CONNECTION_ID" 2>/dev/null | sed -n 's/^\([^:]\+\):.*/\1/p')" + return 0 + fi + ;; + de|del|dele|delet|delete) + 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 + _nmcli_array_delete_at words 0 1 + + LONG_OPTIONS=(help) + _nmcli_compl_OPTIONS + case $? in + 0) + return 0 + ;; + 1) + if ! _nmcli_array_has_value LONG_OPTIONS "help"; then + return 0 + fi + ;; + esac + + OPTIONS=(id uuid path apath) + while [[ ${#words[@]} -gt 0 ]]; do + _nmcli_compl_ARGS_CONNECTION && return 0 + done + _nmcli_list_nl "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" + fi + ;; + l|lo|loa|load) + if [[ ${#words[@]} -gt 2 ]]; then + # we should also complete for help/--help, but who to mix that + # with file name completion? + compopt -o default + COMPREPLY=() + fi + ;; + esac + fi + ;; + d|de|dev|devi|devic|device) + if [[ ${#words[@]} -eq 2 ]]; then + _nmcli_compl_COMMAND "$command" status show connect disconnect wifi wimax + elif [[ ${#words[@]} -gt 2 ]]; then + case "$command" in + s|st|sta|stat|statu|status) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND "${words[2]}" + fi + ;; + sh|sho|show| \ + c|co|con|conn|conne|connec|connect| \ + d|di|dis|disc|disco|discon|disconn|disconne|disconnec|disconnect) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(_nmcli_dev_status DEVICE)" + fi + ;; + w|wi|wif|wifi) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND "${words[2]}" list connect rescan + else + case "${words[2]}" in + l|li|lis|list) + _nmcli_array_delete_at words 0 2 + OPTIONS=(ifname bssid) + _nmcli_compl_ARGS + ;; + c|co|con|conn|conne|connec|connect) + if [[ ${#words[@]} -eq 4 ]]; then + if [[ "${words[3]}" = "" ]]; then + _nmcli_list_nl "$(_nmcli_wifi_list SSID)" + else + _nmcli_list_nl "$(printf "%s\n%s" "$(_nmcli_wifi_list SSID)" "$(_nmcli_wifi_list BSSID)")" + fi + else + _nmcli_array_delete_at words 0 3 + local OPTIONS=(password wep-key-type ifname bssid name private) + _nmcli_compl_ARGS + fi + ;; + r|re|res|resc|resca|rescan) + _nmcli_array_delete_at words 0 2 + OPTIONS=(ifname) + _nmcli_compl_ARGS + ;; + esac + fi + ;; + wim|wima|wimax) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND "${words[2]}" list + else + case "${words[2]}" in + l|li|lis|list) + _nmcli_array_delete_at words 0 2 + OPTIONS=(ifname nsp) + _nmcli_compl_ARGS + ;; + esac + fi + ;; + + esac + fi + ;; + esac return 0 } && diff --git a/cli/src/Makefile.am b/cli/src/Makefile.am index 1cd2a006..e61c0368 100644 --- a/cli/src/Makefile.am +++ b/cli/src/Makefile.am @@ -1,7 +1,7 @@ bin_PROGRAMS = \ nmcli -nmcli_CPPFLAGS = \ +AM_CPPFLAGS = \ -I${top_srcdir} \ -I${top_srcdir}/include \ -I${top_builddir}/include \ @@ -10,6 +10,8 @@ nmcli_CPPFLAGS = \ -I${top_srcdir}/libnm-glib \ $(DBUS_CFLAGS) \ $(GLIB_CFLAGS) \ + -DG_LOG_DOMAIN=\""nmcli"\" \ + -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ -DNMCLI_LOCALEDIR=\"$(datadir)/locale\" nmcli_SOURCES = \ @@ -31,6 +33,7 @@ nmcli_SOURCES = \ nmcli_LDADD = \ $(DBUS_LIBS) \ $(GLIB_LIBS) \ + $(READLINE_LIBS) \ $(top_builddir)/libnm-util/libnm-util.la \ $(top_builddir)/libnm-glib/libnm-glib.la diff --git a/cli/src/Makefile.in b/cli/src/Makefile.in index 46846571..8f3129c7 100644 --- a/cli/src/Makefile.in +++ b/cli/src/Makefile.in @@ -83,7 +83,8 @@ subdir = cli/src DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/build-aux/depcomp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ + $(top_srcdir)/m4/compiler_warnings.m4 \ $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/gnome-code-coverage.m4 \ $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \ @@ -103,14 +104,13 @@ CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" PROGRAMS = $(bin_PROGRAMS) -am_nmcli_OBJECTS = nmcli-common.$(OBJEXT) nmcli-connections.$(OBJEXT) \ - nmcli-devices.$(OBJEXT) nmcli-network-manager.$(OBJEXT) \ - nmcli-settings.$(OBJEXT) nmcli-nmcli.$(OBJEXT) \ - nmcli-utils.$(OBJEXT) +am_nmcli_OBJECTS = common.$(OBJEXT) connections.$(OBJEXT) \ + devices.$(OBJEXT) network-manager.$(OBJEXT) settings.$(OBJEXT) \ + nmcli.$(OBJEXT) utils.$(OBJEXT) nmcli_OBJECTS = $(am_nmcli_OBJECTS) am__DEPENDENCIES_1 = nmcli_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(top_builddir)/libnm-util/libnm-util.la \ + $(am__DEPENDENCIES_1) $(top_builddir)/libnm-util/libnm-util.la \ $(top_builddir)/libnm-glib/libnm-glib.la AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -202,12 +202,15 @@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DATADIRNAME = @DATADIRNAME@ DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@ +DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@ DBUS_LIBS = @DBUS_LIBS@ DBUS_SYS_DIR = @DBUS_SYS_DIR@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DHCLIENT_PATH = @DHCLIENT_PATH@ DHCPCD_PATH = @DHCPCD_PATH@ +DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@ DLLTOOL = @DLLTOOL@ DNSMASQ_PATH = @DNSMASQ_PATH@ DSYMUTIL = @DSYMUTIL@ @@ -232,7 +235,6 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ GNUTLS_LIBS = @GNUTLS_LIBS@ GREP = @GREP@ GTKDOC_CHECK = @GTKDOC_CHECK@ -GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@ GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ GTKDOC_MKPDF = @GTKDOC_MKPDF@ @@ -277,12 +279,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBM = @LIBM@ +LIBNDP_CFLAGS = @LIBNDP_CFLAGS@ +LIBNDP_LIBS = @LIBNDP_LIBS@ LIBNL_CFLAGS = @LIBNL_CFLAGS@ LIBNL_LIBS = @LIBNL_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@ LIBSOUP_LIBS = @LIBSOUP_LIBS@ +LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@ +LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ @@ -299,6 +305,8 @@ MOC = @MOC@ MSGFMT = @MSGFMT@ MSGFMT_015 = @MSGFMT_015@ MSGMERGE = @MSGMERGE@ +NEWT_CFLAGS = @NEWT_CFLAGS@ +NEWT_LIBS = @NEWT_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NM_MAJOR_VERSION = @NM_MAJOR_VERSION@ @@ -326,11 +334,16 @@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POLKIT_CFLAGS = @POLKIT_CFLAGS@ POLKIT_LIBS = @POLKIT_LIBS@ POSUB = @POSUB@ +PPPD_PATH = @PPPD_PATH@ PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +PPPOE_PATH = @PPPOE_PATH@ QT_CFLAGS = @QT_CFLAGS@ QT_LIBS = @QT_LIBS@ RANLIB = @RANLIB@ +READLINE_LIBS = @READLINE_LIBS@ SED = @SED@ +SELINUX_CFLAGS = @SELINUX_CFLAGS@ +SELINUX_LIBS = @SELINUX_LIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ @@ -345,6 +358,7 @@ UDEV_BASE_DIR = @UDEV_BASE_DIR@ USE_NLS = @USE_NLS@ UUID_CFLAGS = @UUID_CFLAGS@ UUID_LIBS = @UUID_LIBS@ +VALGRIND_RULES = @VALGRIND_RULES@ VAPIGEN = @VAPIGEN@ VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@ VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@ @@ -407,6 +421,7 @@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ +subdirs = @subdirs@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ @@ -417,7 +432,8 @@ with_dhclient = @with_dhclient@ with_dhcpcd = @with_dhcpcd@ with_netconfig = @with_netconfig@ with_resolvconf = @with_resolvconf@ -nmcli_CPPFLAGS = \ +with_valgrind = @with_valgrind@ +AM_CPPFLAGS = \ -I${top_srcdir} \ -I${top_srcdir}/include \ -I${top_builddir}/include \ @@ -426,6 +442,8 @@ nmcli_CPPFLAGS = \ -I${top_srcdir}/libnm-glib \ $(DBUS_CFLAGS) \ $(GLIB_CFLAGS) \ + -DG_LOG_DOMAIN=\""nmcli"\" \ + -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ -DNMCLI_LOCALEDIR=\"$(datadir)/locale\" nmcli_SOURCES = \ @@ -447,6 +465,7 @@ nmcli_SOURCES = \ nmcli_LDADD = \ $(DBUS_LIBS) \ $(GLIB_LIBS) \ + $(READLINE_LIBS) \ $(top_builddir)/libnm-util/libnm-util.la \ $(top_builddir)/libnm-glib/libnm-glib.la @@ -544,13 +563,13 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmcli-common.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmcli-connections.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmcli-devices.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmcli-network-manager.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmcli-nmcli.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmcli-settings.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmcli-utils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connections.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/devices.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/network-manager.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmcli.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/settings.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -573,104 +592,6 @@ distclean-compile: @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< -nmcli-common.o: common.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nmcli-common.o -MD -MP -MF $(DEPDIR)/nmcli-common.Tpo -c -o nmcli-common.o `test -f 'common.c' || echo '$(srcdir)/'`common.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nmcli-common.Tpo $(DEPDIR)/nmcli-common.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='common.c' object='nmcli-common.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nmcli-common.o `test -f 'common.c' || echo '$(srcdir)/'`common.c - -nmcli-common.obj: common.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nmcli-common.obj -MD -MP -MF $(DEPDIR)/nmcli-common.Tpo -c -o nmcli-common.obj `if test -f 'common.c'; then $(CYGPATH_W) 'common.c'; else $(CYGPATH_W) '$(srcdir)/common.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nmcli-common.Tpo $(DEPDIR)/nmcli-common.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='common.c' object='nmcli-common.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nmcli-common.obj `if test -f 'common.c'; then $(CYGPATH_W) 'common.c'; else $(CYGPATH_W) '$(srcdir)/common.c'; fi` - -nmcli-connections.o: connections.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nmcli-connections.o -MD -MP -MF $(DEPDIR)/nmcli-connections.Tpo -c -o nmcli-connections.o `test -f 'connections.c' || echo '$(srcdir)/'`connections.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nmcli-connections.Tpo $(DEPDIR)/nmcli-connections.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='connections.c' object='nmcli-connections.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nmcli-connections.o `test -f 'connections.c' || echo '$(srcdir)/'`connections.c - -nmcli-connections.obj: connections.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nmcli-connections.obj -MD -MP -MF $(DEPDIR)/nmcli-connections.Tpo -c -o nmcli-connections.obj `if test -f 'connections.c'; then $(CYGPATH_W) 'connections.c'; else $(CYGPATH_W) '$(srcdir)/connections.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nmcli-connections.Tpo $(DEPDIR)/nmcli-connections.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='connections.c' object='nmcli-connections.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nmcli-connections.obj `if test -f 'connections.c'; then $(CYGPATH_W) 'connections.c'; else $(CYGPATH_W) '$(srcdir)/connections.c'; fi` - -nmcli-devices.o: devices.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nmcli-devices.o -MD -MP -MF $(DEPDIR)/nmcli-devices.Tpo -c -o nmcli-devices.o `test -f 'devices.c' || echo '$(srcdir)/'`devices.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nmcli-devices.Tpo $(DEPDIR)/nmcli-devices.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='devices.c' object='nmcli-devices.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nmcli-devices.o `test -f 'devices.c' || echo '$(srcdir)/'`devices.c - -nmcli-devices.obj: devices.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nmcli-devices.obj -MD -MP -MF $(DEPDIR)/nmcli-devices.Tpo -c -o nmcli-devices.obj `if test -f 'devices.c'; then $(CYGPATH_W) 'devices.c'; else $(CYGPATH_W) '$(srcdir)/devices.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nmcli-devices.Tpo $(DEPDIR)/nmcli-devices.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='devices.c' object='nmcli-devices.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nmcli-devices.obj `if test -f 'devices.c'; then $(CYGPATH_W) 'devices.c'; else $(CYGPATH_W) '$(srcdir)/devices.c'; fi` - -nmcli-network-manager.o: network-manager.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nmcli-network-manager.o -MD -MP -MF $(DEPDIR)/nmcli-network-manager.Tpo -c -o nmcli-network-manager.o `test -f 'network-manager.c' || echo '$(srcdir)/'`network-manager.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nmcli-network-manager.Tpo $(DEPDIR)/nmcli-network-manager.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='network-manager.c' object='nmcli-network-manager.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nmcli-network-manager.o `test -f 'network-manager.c' || echo '$(srcdir)/'`network-manager.c - -nmcli-network-manager.obj: network-manager.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nmcli-network-manager.obj -MD -MP -MF $(DEPDIR)/nmcli-network-manager.Tpo -c -o nmcli-network-manager.obj `if test -f 'network-manager.c'; then $(CYGPATH_W) 'network-manager.c'; else $(CYGPATH_W) '$(srcdir)/network-manager.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nmcli-network-manager.Tpo $(DEPDIR)/nmcli-network-manager.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='network-manager.c' object='nmcli-network-manager.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nmcli-network-manager.obj `if test -f 'network-manager.c'; then $(CYGPATH_W) 'network-manager.c'; else $(CYGPATH_W) '$(srcdir)/network-manager.c'; fi` - -nmcli-settings.o: settings.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nmcli-settings.o -MD -MP -MF $(DEPDIR)/nmcli-settings.Tpo -c -o nmcli-settings.o `test -f 'settings.c' || echo '$(srcdir)/'`settings.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nmcli-settings.Tpo $(DEPDIR)/nmcli-settings.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='settings.c' object='nmcli-settings.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nmcli-settings.o `test -f 'settings.c' || echo '$(srcdir)/'`settings.c - -nmcli-settings.obj: settings.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nmcli-settings.obj -MD -MP -MF $(DEPDIR)/nmcli-settings.Tpo -c -o nmcli-settings.obj `if test -f 'settings.c'; then $(CYGPATH_W) 'settings.c'; else $(CYGPATH_W) '$(srcdir)/settings.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nmcli-settings.Tpo $(DEPDIR)/nmcli-settings.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='settings.c' object='nmcli-settings.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nmcli-settings.obj `if test -f 'settings.c'; then $(CYGPATH_W) 'settings.c'; else $(CYGPATH_W) '$(srcdir)/settings.c'; fi` - -nmcli-nmcli.o: nmcli.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nmcli-nmcli.o -MD -MP -MF $(DEPDIR)/nmcli-nmcli.Tpo -c -o nmcli-nmcli.o `test -f 'nmcli.c' || echo '$(srcdir)/'`nmcli.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nmcli-nmcli.Tpo $(DEPDIR)/nmcli-nmcli.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nmcli.c' object='nmcli-nmcli.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nmcli-nmcli.o `test -f 'nmcli.c' || echo '$(srcdir)/'`nmcli.c - -nmcli-nmcli.obj: nmcli.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nmcli-nmcli.obj -MD -MP -MF $(DEPDIR)/nmcli-nmcli.Tpo -c -o nmcli-nmcli.obj `if test -f 'nmcli.c'; then $(CYGPATH_W) 'nmcli.c'; else $(CYGPATH_W) '$(srcdir)/nmcli.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nmcli-nmcli.Tpo $(DEPDIR)/nmcli-nmcli.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nmcli.c' object='nmcli-nmcli.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nmcli-nmcli.obj `if test -f 'nmcli.c'; then $(CYGPATH_W) 'nmcli.c'; else $(CYGPATH_W) '$(srcdir)/nmcli.c'; fi` - -nmcli-utils.o: utils.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nmcli-utils.o -MD -MP -MF $(DEPDIR)/nmcli-utils.Tpo -c -o nmcli-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nmcli-utils.Tpo $(DEPDIR)/nmcli-utils.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils.c' object='nmcli-utils.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nmcli-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c - -nmcli-utils.obj: utils.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nmcli-utils.obj -MD -MP -MF $(DEPDIR)/nmcli-utils.Tpo -c -o nmcli-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nmcli-utils.Tpo $(DEPDIR)/nmcli-utils.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils.c' object='nmcli-utils.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nmcli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nmcli-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi` - mostlyclean-libtool: -rm -f *.lo diff --git a/cli/src/common.c b/cli/src/common.c index 15ddcada..e6712a8d 100644 --- a/cli/src/common.c +++ b/cli/src/common.c @@ -16,59 +16,68 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * (C) Copyright 2012 Red Hat, Inc. + * (C) Copyright 2012 - 2014 Red Hat, Inc. */ #include "config.h" #include <glib.h> #include <glib/gi18n.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include <readline/readline.h> +#include <readline/history.h> #include "common.h" #include "utils.h" /* Available fields for IPv4 group */ -static NmcOutputField nmc_fields_ip4_config[] = { - {"GROUP", N_("GROUP"), 15, NULL, 0}, /* 0 */ - {"ADDRESS", N_("ADDRESS"), 68, NULL, 0}, /* 1 */ - {"ROUTE", N_("ROUTE"), 68, NULL, 0}, /* 2 */ - {"DNS", N_("DNS"), 35, NULL, 0}, /* 3 */ - {"DOMAIN", N_("DOMAIN"), 35, NULL, 0}, /* 4 */ - {"WINS", N_("WINS"), 20, NULL, 0}, /* 5 */ - {NULL, NULL, 0, NULL, 0} +NmcOutputField nmc_fields_ip4_config[] = { + {"GROUP", N_("GROUP"), 15}, /* 0 */ + {"ADDRESS", N_("ADDRESS"), 68}, /* 1 */ + {"ROUTE", N_("ROUTE"), 68}, /* 2 */ + {"DNS", N_("DNS"), 35}, /* 3 */ + {"DOMAIN", N_("DOMAIN"), 35}, /* 4 */ + {"WINS", N_("WINS"), 20}, /* 5 */ + {NULL, NULL, 0} }; #define NMC_FIELDS_IP4_CONFIG_ALL "GROUP,ADDRESS,ROUTE,DNS,DOMAIN,WINS" /* Available fields for DHCPv4 group */ -static NmcOutputField nmc_fields_dhcp4_config[] = { - {"GROUP", N_("GROUP"), 15, NULL, 0}, /* 0 */ - {"OPTION", N_("OPTION"), 80, NULL, 0}, /* 1 */ - {NULL, NULL, 0, NULL, 0} +NmcOutputField nmc_fields_dhcp4_config[] = { + {"GROUP", N_("GROUP"), 15}, /* 0 */ + {"OPTION", N_("OPTION"), 80}, /* 1 */ + {NULL, NULL, 0} }; #define NMC_FIELDS_DHCP4_CONFIG_ALL "GROUP,OPTION" /* Available fields for IPv6 group */ -static NmcOutputField nmc_fields_ip6_config[] = { - {"GROUP", N_("GROUP"), 15, NULL, 0}, /* 0 */ - {"ADDRESS", N_("ADDRESS"), 95, NULL, 0}, /* 1 */ - {"ROUTE", N_("ROUTE"), 95, NULL, 0}, /* 2 */ - {"DNS", N_("DNS"), 60, NULL, 0}, /* 3 */ - {"DOMAIN", N_("DOMAIN"), 35, NULL, 0}, /* 4 */ - {NULL, NULL, 0, NULL, 0} +NmcOutputField nmc_fields_ip6_config[] = { + {"GROUP", N_("GROUP"), 15}, /* 0 */ + {"ADDRESS", N_("ADDRESS"), 95}, /* 1 */ + {"ROUTE", N_("ROUTE"), 95}, /* 2 */ + {"DNS", N_("DNS"), 60}, /* 3 */ + {"DOMAIN", N_("DOMAIN"), 35}, /* 4 */ + {NULL, NULL, 0} }; #define NMC_FIELDS_IP6_CONFIG_ALL "GROUP,ADDRESS,ROUTE,DNS,DOMAIN" /* Available fields for DHCPv6 group */ -static NmcOutputField nmc_fields_dhcp6_config[] = { - {"GROUP", N_("GROUP"), 15, NULL, 0}, /* 0 */ - {"OPTION", N_("OPTION"), 80, NULL, 0}, /* 1 */ - {NULL, NULL, 0, NULL, 0} +NmcOutputField nmc_fields_dhcp6_config[] = { + {"GROUP", N_("GROUP"), 15}, /* 0 */ + {"OPTION", N_("OPTION"), 80}, /* 1 */ + {NULL, NULL, 0} }; #define NMC_FIELDS_DHCP6_CONFIG_ALL "GROUP,OPTION" gboolean -print_ip4_config (NMIP4Config *cfg4, NmCli *nmc, const char *group_prefix) +print_ip4_config (NMIP4Config *cfg4, + NmCli *nmc, + const char *group_prefix, + const char *one_field) { GSList *list, *iter; const GArray *array; @@ -79,17 +88,18 @@ print_ip4_config (NMIP4Config *cfg4, NmCli *nmc, const char *group_prefix) char **domain_arr = NULL; char **wins_arr = NULL; int i = 0; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; if (cfg4 == NULL) return FALSE; - nmc->allowed_fields = nmc_fields_ip4_config; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_IP4_CONFIG_ALL, nmc->allowed_fields, NULL); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ + tmpl = nmc_fields_ip4_config; + tmpl_len = sizeof (nmc_fields_ip4_config); + nmc->print_fields.indices = parse_output_fields (one_field ? one_field : NMC_FIELDS_IP4_CONFIG_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); /* addresses */ list = (GSList *) nm_ip4_config_get_addresses (cfg4); @@ -144,7 +154,7 @@ print_ip4_config (NMIP4Config *cfg4, NmCli *nmc, const char *group_prefix) if (ptr_array) { domain_arr = g_new (char *, ptr_array->len + 1); for (i = 0; i < ptr_array->len; i++) - domain_arr[i] = g_ptr_array_index (ptr_array, i); + domain_arr[i] = g_strdup (g_ptr_array_index (ptr_array, i)); domain_arr[i] = NULL; } @@ -159,27 +169,28 @@ print_ip4_config (NMIP4Config *cfg4, NmCli *nmc, const char *group_prefix) wins_arr[i] = NULL; } - set_val_str (nmc->allowed_fields, 0, group_prefix); - set_val_arr (nmc->allowed_fields, 1, (const char **) addr_arr); - set_val_arr (nmc->allowed_fields, 2, (const char **) route_arr); - set_val_arr (nmc->allowed_fields, 3, (const char **) dns_arr); - set_val_arr (nmc->allowed_fields, 4, (const char **) domain_arr); - set_val_arr (nmc->allowed_fields, 5, (const char **) wins_arr); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, group_prefix); + set_val_arr (arr, 1, addr_arr); + set_val_arr (arr, 2, route_arr); + set_val_arr (arr, 3, dns_arr); + set_val_arr (arr, 4, domain_arr); + set_val_arr (arr, 5, wins_arr); + g_ptr_array_add (nmc->output_data, arr); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + print_data (nmc); /* Print all data */ - g_strfreev (addr_arr); - g_strfreev (route_arr); - g_strfreev (dns_arr); - g_free (domain_arr); - g_strfreev (wins_arr); + /* Remove any previous data */ + nmc_empty_output_fields (nmc); return TRUE; } gboolean -print_ip6_config (NMIP6Config *cfg6, NmCli *nmc, const char *group_prefix) +print_ip6_config (NMIP6Config *cfg6, + NmCli *nmc, + const char *group_prefix, + const char *one_field) { GSList *list, *iter; const GPtrArray *ptr_array; @@ -188,17 +199,18 @@ print_ip6_config (NMIP6Config *cfg6, NmCli *nmc, const char *group_prefix) char **dns_arr = NULL; char **domain_arr = NULL; int i = 0; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; if (cfg6 == NULL) return FALSE; - nmc->allowed_fields = nmc_fields_ip6_config; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_IP6_CONFIG_ALL, nmc->allowed_fields, NULL); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ + tmpl = nmc_fields_ip6_config; + tmpl_len = sizeof (nmc_fields_ip6_config); + nmc->print_fields.indices = parse_output_fields (one_field ? one_field : NMC_FIELDS_IP6_CONFIG_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); /* addresses */ list = (GSList *) nm_ip6_config_get_addresses (cfg6); @@ -252,35 +264,36 @@ print_ip6_config (NMIP6Config *cfg6, NmCli *nmc, const char *group_prefix) if (ptr_array) { domain_arr = g_new (char *, ptr_array->len + 1); for (i = 0; i < ptr_array->len; i++) - domain_arr[i] = g_ptr_array_index (ptr_array, i); + domain_arr[i] = g_strdup (g_ptr_array_index (ptr_array, i)); domain_arr[i] = NULL; } - set_val_str (nmc->allowed_fields, 0, group_prefix); - set_val_arr (nmc->allowed_fields, 1, (const char **) addr_arr); - set_val_arr (nmc->allowed_fields, 2, (const char **) route_arr); - set_val_arr (nmc->allowed_fields, 3, (const char **) dns_arr); - set_val_arr (nmc->allowed_fields, 4, (const char **) domain_arr); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, group_prefix); + set_val_arr (arr, 1, addr_arr); + set_val_arr (arr, 2, route_arr); + set_val_arr (arr, 3, dns_arr); + set_val_arr (arr, 4, domain_arr); + g_ptr_array_add (nmc->output_data, arr); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + print_data (nmc); /* Print all data */ - g_strfreev (addr_arr); - g_strfreev (route_arr); - g_strfreev (dns_arr); - g_free (domain_arr); + /* Remove any previous data */ + nmc_empty_output_fields (nmc); return TRUE; } gboolean -print_dhcp4_config (NMDHCP4Config *dhcp4, NmCli *nmc, const char *group_prefix) +print_dhcp4_config (NMDHCP4Config *dhcp4, + NmCli *nmc, + const char *group_prefix, + const char *one_field) { GHashTable *table; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; if (dhcp4 == NULL) return FALSE; @@ -292,10 +305,12 @@ print_dhcp4_config (NMDHCP4Config *dhcp4, NmCli *nmc, const char *group_prefix) char **options_arr = NULL; int i = 0; - nmc->allowed_fields = nmc_fields_dhcp4_config; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_DHCP4_CONFIG_ALL, nmc->allowed_fields, NULL); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ + tmpl = nmc_fields_dhcp4_config; + tmpl_len = sizeof (nmc_fields_dhcp4_config); + nmc->print_fields.indices = parse_output_fields (one_field ? one_field : NMC_FIELDS_DHCP4_CONFIG_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); options_arr = g_new (char *, g_hash_table_size (table) + 1); g_hash_table_iter_init (&table_iter, table); @@ -303,13 +318,15 @@ print_dhcp4_config (NMDHCP4Config *dhcp4, NmCli *nmc, const char *group_prefix) options_arr[i++] = g_strdup_printf ("%s = %s", (char *) key, (char *) value); options_arr[i] = NULL; - set_val_str (nmc->allowed_fields, 0, group_prefix); - set_val_arr (nmc->allowed_fields, 1, (const char **) options_arr); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, group_prefix); + set_val_arr (arr, 1, options_arr); + g_ptr_array_add (nmc->output_data, arr); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + print_data (nmc); /* Print all data */ - g_strfreev (options_arr); + /* Remove any previous data */ + nmc_empty_output_fields (nmc); return TRUE; } @@ -317,12 +334,14 @@ print_dhcp4_config (NMDHCP4Config *dhcp4, NmCli *nmc, const char *group_prefix) } gboolean -print_dhcp6_config (NMDHCP6Config *dhcp6, NmCli *nmc, const char *group_prefix) +print_dhcp6_config (NMDHCP6Config *dhcp6, + NmCli *nmc, + const char *group_prefix, + const char *one_field) { GHashTable *table; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; if (dhcp6 == NULL) return FALSE; @@ -334,10 +353,12 @@ print_dhcp6_config (NMDHCP6Config *dhcp6, NmCli *nmc, const char *group_prefix) char **options_arr = NULL; int i = 0; - nmc->allowed_fields = nmc_fields_dhcp6_config; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_DHCP6_CONFIG_ALL, nmc->allowed_fields, NULL); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ + tmpl = nmc_fields_dhcp6_config; + tmpl_len = sizeof (nmc_fields_dhcp6_config); + nmc->print_fields.indices = parse_output_fields (one_field ? one_field : NMC_FIELDS_DHCP6_CONFIG_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); options_arr = g_new (char *, g_hash_table_size (table) + 1); g_hash_table_iter_init (&table_iter, table); @@ -345,19 +366,313 @@ print_dhcp6_config (NMDHCP6Config *dhcp6, NmCli *nmc, const char *group_prefix) options_arr[i++] = g_strdup_printf ("%s = %s", (char *) key, (char *) value); options_arr[i] = NULL; - set_val_str (nmc->allowed_fields, 0, group_prefix); - set_val_arr (nmc->allowed_fields, 1, (const char **) options_arr); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, group_prefix); + set_val_arr (arr, 1, options_arr); + g_ptr_array_add (nmc->output_data, arr); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + print_data (nmc); /* Print all data */ - g_strfreev (options_arr); + /* Remove any previous data */ + nmc_empty_output_fields (nmc); return TRUE; } return FALSE; } +/* + * Parse IPv4 address from string to NMIP4Address stucture. + * ip_str is the IPv4 address in the form address/prefix + * gw_str is the gateway address (it is optional) + */ +NMIP4Address * +nmc_parse_and_build_ip4_address (const char *ip_str, const char *gw_str, GError **error) +{ + NMIP4Address *addr = NULL; + guint32 ip4_addr, gw_addr; + char *tmp; + char *plen; + long int prefix; + + g_return_val_if_fail (ip_str != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + tmp = g_strdup (ip_str); + plen = strchr (tmp, '/'); /* prefix delimiter */ + if (plen) + *plen++ = '\0'; + + if (inet_pton (AF_INET, tmp, &ip4_addr) < 1) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("invalid IPv4 address '%s'"), tmp); + goto finish; + } + + prefix = 32; + if (plen) { + if (!nmc_string_to_int (plen, TRUE, 1, 32, &prefix)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("invalid prefix '%s'; <1-32> allowed"), plen); + goto finish; + } + } + + if (inet_pton (AF_INET, gw_str ? gw_str : "0.0.0.0", &gw_addr) < 1) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("invalid gateway '%s'"), gw_str); + goto finish; + } + + addr = nm_ip4_address_new (); + nm_ip4_address_set_address (addr, ip4_addr); + nm_ip4_address_set_prefix (addr, (guint32) prefix); + nm_ip4_address_set_gateway (addr, gw_addr); + +finish: + g_free (tmp); + return addr; +} + +/* + * Parse IPv6 address from string to NMIP6Address stucture. + * ip_str is the IPv6 address in the form address/prefix + * gw_str is the gateway address (it is optional) + */ +NMIP6Address * +nmc_parse_and_build_ip6_address (const char *ip_str, const char *gw_str, GError **error) +{ + NMIP6Address *addr = NULL; + struct in6_addr ip_addr, gw_addr; + char *tmp; + char *plen; + long int prefix; + + g_return_val_if_fail (ip_str != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + tmp = g_strdup (ip_str); + plen = strchr (tmp, '/'); /* prefix delimiter */ + if (plen) + *plen++ = '\0'; + + if (inet_pton (AF_INET6, tmp, &ip_addr) < 1) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("invalid IPv6 address '%s'"), tmp); + goto finish; + } + + prefix = 128; + if (plen) { + if (!nmc_string_to_int (plen, TRUE, 1, 128, &prefix)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("invalid prefix '%s'; <1-128> allowed"), plen); + goto finish; + } + } + + if (inet_pton (AF_INET6, gw_str ? gw_str : "::", &gw_addr) < 1) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("invalid gateway '%s'"), gw_str); + goto finish; + } + + addr = nm_ip6_address_new (); + nm_ip6_address_set_address (addr, &ip_addr); + nm_ip6_address_set_prefix (addr, (guint32) prefix); + nm_ip6_address_set_gateway (addr, &gw_addr); + +finish: + g_free (tmp); + return addr; +} + +typedef struct { + long int prefix; + long int metric; + union _IpDest { + guint32 ip4_dst; + struct in6_addr ip6_dst; + } dst; + union _IpNextHop { + guint32 ip4_nh; + struct in6_addr ip6_nh; + } nh; +} ParsedRoute; + +/* + * _parse_and_build_route: + * @family: AF_INET or AF_INET6 + * @first: the route destination in the form of "address/prefix" + (/prefix is optional) + * @second: (allow-none): next hop address, if third is not NULL. Otherwise it could be + either next hop address or metric. (It can be NULL when @third is NULL). + * @third: (allow-none): route metric + * @out: (out): route struct to fill + * @error: location to store GError + * + * Parse route from strings and fill @out parameter. + * + * Returns: %TRUE on success, %FALSE on failure + */ +static gboolean +_parse_and_build_route (int family, + const char *first, + const char *second, + const char *third, + ParsedRoute *out, + GError **error) +{ + int max_prefix; + char *tmp, *plen; + gboolean success = FALSE; + + g_return_val_if_fail (family == AF_INET || family == AF_INET6, FALSE); + g_return_val_if_fail (first != NULL, FALSE); + g_return_val_if_fail (second || !third, FALSE); + g_return_val_if_fail (out, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + max_prefix = (family == AF_INET) ? 32 : 128; + /* initialize default values */ + out->prefix = max_prefix; + out->metric = 0; + if (family == AF_INET) + out->nh.ip4_nh = 0; + else + out->nh.ip6_nh = in6addr_any; + + tmp = g_strdup (first); + plen = strchr (tmp, '/'); /* prefix delimiter */ + if (plen) + *plen++ = '\0'; + + if (inet_pton (family, tmp, family == AF_INET ? (void *) &out->dst.ip4_dst : (void *) &out->dst.ip6_dst) < 1) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("invalid route destination address '%s'"), tmp); + goto finish; + } + + if (plen) { + if (!nmc_string_to_int (plen, TRUE, 1, max_prefix, &out->prefix)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("invalid prefix '%s'; <1-%d> allowed"), + plen, max_prefix); + goto finish; + } + } + + if (second) { + if (inet_pton (family, second, family == AF_INET ? (void *) &out->nh.ip4_nh : (void *) &out->nh.ip6_nh) < 1) { + if (third) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("invalid next hop address '%s'"), second); + goto finish; + } else { + /* 'second' can be a metric */ + if (!nmc_string_to_int (second, TRUE, 0, G_MAXUINT32, &out->metric)) { + g_set_error (error, 1, 0, _("the second component of route ('%s') is neither " + "a next hop address nor a metric"), second); + goto finish; + } + } + } + } + + if (third) { + if (!nmc_string_to_int (third, TRUE, 0, G_MAXUINT32, &out->metric)) { + g_set_error (error, 1, 0, _("invalid metric '%s'"), third); + goto finish; + } + } + + /* We don't accept default routes as NetworkManager handles it itself */ + if ( (family == AF_INET && out->dst.ip4_dst == 0) + || (family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED (&out->dst.ip6_dst))) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("default route cannot be added (NetworkManager handles it by itself)")); + goto finish; + } + + success = TRUE; + +finish: + g_free (tmp); + return success; +} + +/* + * nmc_parse_and_build_ip4_route: + * @first: the IPv4 route destination in the form of "address/prefix" + (/prefix is optional) + * @second: (allow-none): next hop address, if third is not NULL. Otherwise it could be + either next hop address or metric. (It can be NULL when @third is NULL). + * @third: (allow-none): route metric + * @error: location to store GError + * + * Parse IPv4 route from strings to NMIP4Route stucture. + * + * Returns: route as a NMIP4Route object, or %NULL on failure + */ +NMIP4Route * +nmc_parse_and_build_ip4_route (const char *first, + const char *second, + const char *third, + GError **error) +{ + ParsedRoute tmp_route; + NMIP4Route *route = NULL; + + g_return_val_if_fail (first != NULL, NULL); + g_return_val_if_fail (second || !third, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (_parse_and_build_route (AF_INET, first, second, third, &tmp_route, error)) { + route = nm_ip4_route_new (); + nm_ip4_route_set_dest (route, tmp_route.dst.ip4_dst); + nm_ip4_route_set_prefix (route, (guint32) tmp_route.prefix); + nm_ip4_route_set_next_hop (route, tmp_route.nh.ip4_nh); + nm_ip4_route_set_metric (route, (guint32) tmp_route.metric); + } + return route; +} + +/* + * nmc_parse_and_build_ip6_route: + * @first: the IPv6 route destination in the form of "address/prefix" + (/prefix is optional) + * @second: (allow-none): next hop address, if third is not NULL. Otherwise it could be + either next hop address or metric. (It can be NULL when @third is NULL). + * @third: (allow-none): route metric + * @error: location to store GError + * + * Parse IPv6 route from strings to NMIP6Route stucture. + * + * Returns: route as a NMIP6Route object, or %NULL on failure + */ +NMIP6Route * +nmc_parse_and_build_ip6_route (const char *first, + const char *second, + const char *third, + GError **error) +{ + ParsedRoute tmp_route; + NMIP6Route *route = NULL; + + g_return_val_if_fail (first != NULL, NULL); + g_return_val_if_fail (second || !third, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (_parse_and_build_route (AF_INET6, first, second, third, &tmp_route, error)) { + route = nm_ip6_route_new (); + nm_ip6_route_set_dest (route, &tmp_route.dst.ip6_dst); + nm_ip6_route_set_prefix (route, (guint32) tmp_route.prefix); + nm_ip6_route_set_next_hop (route, &tmp_route.nh.ip6_nh); + nm_ip6_route_set_metric (route, (guint32) tmp_route.metric); + } + return route; +} + const char * nmc_device_state_to_string (NMDeviceState state) { @@ -560,8 +875,294 @@ nmc_device_reason_to_string (NMDeviceStateReason reason) case NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED: return _("A secondary connection of the base connection failed"); + case NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED: + return _("DCB or FCoE setup failed"); + + case NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED: + return _("teamd control failed"); + + case NM_DEVICE_STATE_REASON_MODEM_FAILED: + return _("Modem failed or no longer available"); + + case NM_DEVICE_STATE_REASON_MODEM_AVAILABLE: + return _("Modem now ready and available"); + + case NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT: + return _("SIM PIN was incorrect"); + default: + /* TRANSLATORS: Unknown reason for a device state change (NMDeviceStateReason) */ return _("Unknown"); } } + +/* Max priority values from libnm-util/nm-setting-vlan.c */ +#define MAX_SKB_PRIO G_MAXUINT32 +#define MAX_8021P_PRIO 7 /* Max 802.1p priority */ + +/* + * Parse VLAN priority mappings from the following format: 2:1,3:4,7:3 + * and verify if the priority numbers are valid + * + * Return: string array with split maps, or NULL on error + * Caller is responsible for freeing the array. + */ +char ** +nmc_vlan_parse_priority_maps (const char *priority_map, + NMVlanPriorityMap map_type, + GError **error) +{ + char **mapping = NULL, **iter; + unsigned long from, to, from_max, to_max; + + g_return_val_if_fail (priority_map != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (map_type == NM_VLAN_INGRESS_MAP) { + from_max = MAX_8021P_PRIO; + to_max = MAX_SKB_PRIO; + } else { + from_max = MAX_SKB_PRIO; + to_max = MAX_8021P_PRIO; + } + + mapping = g_strsplit (priority_map, ",", 0); + for (iter = mapping; iter && *iter; iter++) { + char *left, *right; + + left = g_strstrip (*iter); + right = strchr (left, ':'); + if (!right) { + g_set_error (error, 1, 0, _("invalid priority map '%s'"), *iter); + g_strfreev (mapping); + return NULL; + } + *right++ = '\0'; + + if (!nmc_string_to_uint (left, TRUE, 0, from_max, &from)) { + g_set_error (error, 1, 0, _("priority '%s' is not valid (<0-%ld>)"), + left, from_max); + g_strfreev (mapping); + return NULL; + } + if (!nmc_string_to_uint (right, TRUE, 0, to_max, &to)) { + g_set_error (error, 1, 0, _("priority '%s' is not valid (<0-%ld>)"), + right, to_max); + g_strfreev (mapping); + return NULL; + } + *(right-1) = ':'; /* Put back ':' */ + } + return mapping; +} + +const char * +nmc_bond_validate_mode (const char *mode, GError **error) +{ + unsigned long mode_int; + static const char *valid_modes[] = { "balance-rr", + "active-backup", + "balance-xor", + "broadcast", + "802.3ad", + "balance-tlb", + "balance-alb", + NULL }; + if (nmc_string_to_uint (mode, TRUE, 0, 6, &mode_int)) { + /* Translate bonding mode numbers to mode names: + * https://www.kernel.org/doc/Documentation/networking/bonding.txt + */ + return valid_modes[mode_int]; + } else + return nmc_string_is_valid (mode, valid_modes, error); +} + +/* + * nmc_team_check_config: + * @config: file name with team config, or raw team JSON config data + * @out_config: raw team JSON config data (with removed new-line characters) + * @error: location to store error, or %NUL + * + * Check team config from @config parameter and return the checked/sanitized + * config in @out_config. + * + * Returns: %TRUE if the config is valid, %FALSE if it is invalid + */ +gboolean +nmc_team_check_config (const char *config, char **out_config, GError **error) +{ + char *contents = NULL; + size_t c_len = 0; + + *out_config = NULL; + + if (!config || strlen (config) == strspn (config, " \t")) + return TRUE; + + /* 'config' can be either a file name or raw JSON config data */ + if (g_file_test (config, G_FILE_TEST_EXISTS)) + (void) g_file_get_contents (config, &contents, NULL, NULL); + else + contents = g_strdup (config); + + if (contents) { + g_strstrip (contents); + c_len = strlen (contents); + } + + /* Do a simple validity check */ + if (!contents || !contents[0] || c_len > 100000 || contents[0] != '{' || contents[c_len-1] != '}') { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("'%s' is not a valid team configuration or file name."), config); + g_free (contents); + return FALSE; + } + *out_config = g_strdelimit (contents, "\r\n", ' '); + return TRUE; +} + +/* + * nmc_find_connection: + * @list: list of NMConnections to search in + * @filter_type: "id", "uuid", "path" or %NULL + * @filter_val: connection to find (connection name, UUID or path) + * @start: where to start in @list. The location is updated so that the function + * can be called multiple times (for connections with the same name). + * + * Find a connection in @list according to @filter_val. @filter_type determines + * what property is used for comparison. When @filter_type is NULL, compare + * @filter_val against all types. Otherwise, only compare against the specified + * type. If 'path' filter type is specified, comparison against numeric index + * (in addition to the whole path) is allowed. + * + * Returns: found connection, or %NULL + */ +NMConnection * +nmc_find_connection (GSList *list, + const char *filter_type, + const char *filter_val, + GSList **start) +{ + NMConnection *connection; + NMConnection *found = NULL; + GSList *iterator; + const char *id; + const char *uuid; + const char *path, *path_num; + + iterator = (start && *start) ? *start : list; + while (iterator) { + connection = NM_CONNECTION (iterator->data); + + id = nm_connection_get_id (connection); + uuid = nm_connection_get_uuid (connection); + path = nm_connection_get_path (connection); + path_num = path ? strrchr (path, '/') + 1 : NULL; + + /* When filter_type is NULL, compare connection ID (filter_val) + * against all types. Otherwise, only compare against the specific + * type. If 'path' filter type is specified, comparison against + * numeric index (in addition to the whole path) is allowed. + */ + if ( ( (!filter_type || strcmp (filter_type, "id") == 0) + && strcmp (filter_val, id) == 0) + || ( (!filter_type || strcmp (filter_type, "uuid") == 0) + && strcmp (filter_val, uuid) == 0) + || ( (!filter_type || strcmp (filter_type, "path") == 0) + && (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0)))) { + if (!start) + return connection; + if (found) { + *start = iterator; + return found; + } + found = connection; + } + + iterator = g_slist_next (iterator); + } + + if (start) + *start = NULL; + return found; +} + +/** + * nmc_cleanup_readline: + * + * Cleanup readline when nmcli is terminated with a signal. + * It makes sure the terminal is not garbled. + */ +void +nmc_cleanup_readline (void) +{ + rl_free_line_state (); + rl_cleanup_after_signal (); +} + +/** + * nmc_readline: + * @prompt_fmt: prompt to print (telling user what to enter). It is standard + * printf() format string + * @...: a list of arguments according to the @prompt_fmt format string + * + * Wrapper around libreadline's readline() function. + * + * Returns: the user provided string. In case the user entered empty string, + * this function returns NULL. + */ +char * +nmc_readline (const char *prompt_fmt, ...) +{ + va_list args; + char *prompt, *str; + + va_start (args, prompt_fmt); + prompt = g_strdup_vprintf (prompt_fmt, args); + va_end (args); + + str = readline (prompt); + /* Return NULL, not empty string */ + if (str && *str == '\0') { + g_free (str); + str = NULL; + } + + if (str && *str) + add_history (str); + + g_free (prompt); + return str; +} + +/** + * nmc_rl_gen_func_basic: + * @text: text to complete + * @state: readline state; says whether start from scratch (state == 0) + * @words: strings for completion + * + * Basic function generating list of completion strings for readline. + * See e.g. http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC49 + */ +char * +nmc_rl_gen_func_basic (const char *text, int state, const char **words) +{ + static int list_idx, len; + const char *name; + + if (!state) { + list_idx = 0; + len = strlen (text); + } + + /* Return the next name which partially matches one from the 'words' list. */ + while ((name = words[list_idx])) { + list_idx++; + + if (strncmp (name, text, len) == 0) + return g_strdup (name); + } + return NULL; +} + diff --git a/cli/src/common.h b/cli/src/common.h index 89412d28..228375b9 100644 --- a/cli/src/common.h +++ b/cli/src/common.h @@ -16,7 +16,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * (C) Copyright 2012 Red Hat, Inc. + * (C) Copyright 2012 - 2014 Red Hat, Inc. */ #ifndef NMC_COMMON_H @@ -32,12 +32,35 @@ #include "nmcli.h" -gboolean print_ip4_config (NMIP4Config *cfg4, NmCli *nmc, const char *group_prefix); -gboolean print_ip6_config (NMIP6Config *cfg6, NmCli *nmc, const char *group_prefix); -gboolean print_dhcp4_config (NMDHCP4Config *dhcp4, NmCli *nmc, const char *group_prefix); -gboolean print_dhcp6_config (NMDHCP6Config *dhcp6, NmCli *nmc, const char *group_prefix); +gboolean print_ip4_config (NMIP4Config *cfg4, NmCli *nmc, const char *group_prefix, const char *one_field); +gboolean print_ip6_config (NMIP6Config *cfg6, NmCli *nmc, const char *group_prefix, const char *one_field); +gboolean print_dhcp4_config (NMDHCP4Config *dhcp4, NmCli *nmc, const char *group_prefix, const char *one_field); +gboolean print_dhcp6_config (NMDHCP6Config *dhcp6, NmCli *nmc, const char *group_prefix, const char *one_field); + +NMIP4Address *nmc_parse_and_build_ip4_address (const char *ip_str, const char *gw_str, GError **error); +NMIP6Address *nmc_parse_and_build_ip6_address (const char *ip_str, const char *gw_str, GError **error); + +NMIP4Route *nmc_parse_and_build_ip4_route (const char *first, const char *second, const char *third, GError **error); +NMIP6Route *nmc_parse_and_build_ip6_route (const char *first, const char *second, const char *third, GError **error); const char * nmc_device_state_to_string (NMDeviceState state); const char * nmc_device_reason_to_string (NMDeviceStateReason reason); +char ** +nmc_vlan_parse_priority_maps (const char *priority_map, + NMVlanPriorityMap map_type, + GError **error); + +const char *nmc_bond_validate_mode (const char *mode, GError **error); +gboolean nmc_team_check_config (const char *config, char **out_config, GError **error); + +NMConnection *nmc_find_connection (GSList *list, + const char *filter_type, + const char *filter_val, + GSList **start); + +void nmc_cleanup_readline (void); +char *nmc_readline (const char *prompt_fmt, ...) G_GNUC_PRINTF (1, 2); +char *nmc_rl_gen_func_basic (const char *text, int state, const char **words); + #endif /* NMC_COMMON_H */ diff --git a/cli/src/connections.c b/cli/src/connections.c index b39b447e..d17f7f12 100644 --- a/cli/src/connections.c +++ b/cli/src/connections.c @@ -14,7 +14,7 @@ * 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 - 2012 Red Hat, Inc. + * (C) Copyright 2010 - 2014 Red Hat, Inc. */ #include "config.h" @@ -30,6 +30,8 @@ #include <errno.h> #include <signal.h> #include <netinet/ether.h> +#include <readline/readline.h> +#include <readline/history.h> #include <nm-client.h> #include <nm-device-ethernet.h> @@ -43,6 +45,7 @@ #include <nm-device-olpc-mesh.h> #include <nm-device-infiniband.h> #include <nm-device-bond.h> +#include <nm-device-team.h> #include <nm-device-bridge.h> #include <nm-device-vlan.h> #include <nm-remote-settings.h> @@ -54,50 +57,104 @@ #include "settings.h" #include "connections.h" - -/* Available fields for 'con list' */ -static NmcOutputField nmc_fields_con_list[] = { - {"NAME", N_("NAME"), 25, NULL, 0}, /* 0 */ - {"UUID", N_("UUID"), 38, NULL, 0}, /* 1 */ - {"TYPE", N_("TYPE"), 17, NULL, 0}, /* 2 */ - {"TIMESTAMP", N_("TIMESTAMP"), 12, NULL, 0}, /* 3 */ - {"TIMESTAMP-REAL", N_("TIMESTAMP-REAL"), 34, NULL, 0}, /* 4 */ - {"AUTOCONNECT", N_("AUTOCONNECT"), 13, NULL, 0}, /* 5 */ - {"READONLY", N_("READONLY"), 10, NULL, 0}, /* 6 */ - {"DBUS-PATH", N_("DBUS-PATH"), 42, NULL, 0}, /* 7 */ - {NULL, NULL, 0, NULL, 0} +/* Activation timeout waiting for bond/team/bridge slaves (in seconds) */ +#define SLAVES_UP_TIMEOUT 10 + +/* define some prompts for connection editor */ +#define EDITOR_PROMPT_SETTING _("Setting name? ") +#define EDITOR_PROMPT_PROPERTY _("Property name? ") +#define EDITOR_PROMPT_CON_TYPE _("Enter connection type: ") + +/* define some other prompts */ +#define PROMPT_CON_TYPE _("Connection type: ") +#define PROMPT_VPN_TYPE _("VPN type: ") +#define PROMPT_BOND_MASTER _("Bond master: ") +#define PROMPT_TEAM_MASTER _("Team master: ") +#define PROMPT_BRIDGE_MASTER _("Bridge master: ") +#define PROMPT_CONNECTION _("Connection (name, UUID, or path): ") + +static const char *nmc_known_vpns[] = + { "openvpn", "vpnc", "pptp", "openconnect", "openswan", "libreswan", + "ssh", "l2tp", "iodine", NULL }; + +/* Available fields for 'connection show' */ +static NmcOutputField nmc_fields_con_show[] = { + {"NAME", N_("NAME"), 25}, /* 0 */ + {"UUID", N_("UUID"), 38}, /* 1 */ + {"TYPE", N_("TYPE"), 17}, /* 2 */ + {"TIMESTAMP", N_("TIMESTAMP"), 12}, /* 3 */ + {"TIMESTAMP-REAL", N_("TIMESTAMP-REAL"), 34}, /* 4 */ + {"AUTOCONNECT", N_("AUTOCONNECT"), 13}, /* 5 */ + {"READONLY", N_("READONLY"), 10}, /* 6 */ + {"DBUS-PATH", N_("DBUS-PATH"), 42}, /* 7 */ + {"ACTIVE", N_("ACTIVE"), 10}, /* 8 */ + {"DEVICE", N_("DEVICE"), 10}, /* 9 */ + {"STATE", N_("STATE"), 12}, /* 10 */ + {"ACTIVE-PATH", N_("ACTIVE-PATH"), 51}, /* 11 */ + {NULL, NULL, 0} }; -#define NMC_FIELDS_CON_LIST_ALL "NAME,UUID,TYPE,TIMESTAMP,TIMESTAMP-REAL,AUTOCONNECT,READONLY,DBUS-PATH" -#define NMC_FIELDS_CON_LIST_COMMON "NAME,UUID,TYPE,TIMESTAMP-REAL" +#define NMC_FIELDS_CON_SHOW_ALL "NAME,UUID,TYPE,TIMESTAMP,TIMESTAMP-REAL,AUTOCONNECT,READONLY,DBUS-PATH,"\ + "ACTIVE,DEVICE,STATE,ACTIVE-PATH" +#define NMC_FIELDS_CON_SHOW_COMMON "NAME,UUID,TYPE,DEVICE" /* Helper macro to define fields */ -#define SETTING_FIELD(setting, width) { setting, N_(setting), width, NULL, 0 } - -/* Available settings for 'con list id/uuid <con>' */ +#define SETTING_FIELD(setting, props) { setting, N_(setting), 0, props, NULL, FALSE, FALSE, 0 } + +/* defined in settings.c */ +extern NmcOutputField nmc_fields_setting_connection[]; +extern NmcOutputField nmc_fields_setting_wired[]; +extern NmcOutputField nmc_fields_setting_8021X[]; +extern NmcOutputField nmc_fields_setting_wireless[]; +extern NmcOutputField nmc_fields_setting_wireless_security[]; +extern NmcOutputField nmc_fields_setting_ip4_config[]; +extern NmcOutputField nmc_fields_setting_ip6_config[]; +extern NmcOutputField nmc_fields_setting_serial[]; +extern NmcOutputField nmc_fields_setting_ppp[]; +extern NmcOutputField nmc_fields_setting_pppoe[]; +extern NmcOutputField nmc_fields_setting_adsl[]; +extern NmcOutputField nmc_fields_setting_gsm[]; +extern NmcOutputField nmc_fields_setting_cdma[]; +extern NmcOutputField nmc_fields_setting_bluetooth[]; +extern NmcOutputField nmc_fields_setting_olpc_mesh[]; +extern NmcOutputField nmc_fields_setting_vpn[]; +extern NmcOutputField nmc_fields_setting_wimax[]; +extern NmcOutputField nmc_fields_setting_infiniband[]; +extern NmcOutputField nmc_fields_setting_bond[]; +extern NmcOutputField nmc_fields_setting_vlan[]; +extern NmcOutputField nmc_fields_setting_bridge[]; +extern NmcOutputField nmc_fields_setting_bridge_port[]; +extern NmcOutputField nmc_fields_setting_team[]; +extern NmcOutputField nmc_fields_setting_team_port[]; +extern NmcOutputField nmc_fields_setting_dcb[]; + +/* Available settings for 'connection show <con>' - profile part */ static NmcOutputField nmc_fields_settings_names[] = { - SETTING_FIELD (NM_SETTING_CONNECTION_SETTING_NAME, 0), /* 0 */ - SETTING_FIELD (NM_SETTING_WIRED_SETTING_NAME, 0), /* 1 */ - SETTING_FIELD (NM_SETTING_802_1X_SETTING_NAME, 0), /* 2 */ - SETTING_FIELD (NM_SETTING_WIRELESS_SETTING_NAME, 0), /* 3 */ - SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, 0), /* 4 */ - SETTING_FIELD (NM_SETTING_IP4_CONFIG_SETTING_NAME, 0), /* 5 */ - SETTING_FIELD (NM_SETTING_IP6_CONFIG_SETTING_NAME, 0), /* 6 */ - SETTING_FIELD (NM_SETTING_SERIAL_SETTING_NAME, 0), /* 7 */ - SETTING_FIELD (NM_SETTING_PPP_SETTING_NAME, 0), /* 8 */ - SETTING_FIELD (NM_SETTING_PPPOE_SETTING_NAME, 0), /* 9 */ - SETTING_FIELD (NM_SETTING_GSM_SETTING_NAME, 0), /* 10 */ - SETTING_FIELD (NM_SETTING_CDMA_SETTING_NAME, 0), /* 11 */ - SETTING_FIELD (NM_SETTING_BLUETOOTH_SETTING_NAME, 0), /* 12 */ - SETTING_FIELD (NM_SETTING_OLPC_MESH_SETTING_NAME, 0), /* 13 */ - SETTING_FIELD (NM_SETTING_VPN_SETTING_NAME, 0), /* 14 */ - SETTING_FIELD (NM_SETTING_WIMAX_SETTING_NAME, 0), /* 15 */ - SETTING_FIELD (NM_SETTING_INFINIBAND_SETTING_NAME, 0), /* 16 */ - SETTING_FIELD (NM_SETTING_BOND_SETTING_NAME, 0), /* 17 */ - SETTING_FIELD (NM_SETTING_VLAN_SETTING_NAME, 0), /* 18 */ - SETTING_FIELD (NM_SETTING_ADSL_SETTING_NAME, 0), /* 19 */ - SETTING_FIELD (NM_SETTING_BRIDGE_SETTING_NAME, 0), /* 20 */ - SETTING_FIELD (NM_SETTING_BRIDGE_PORT_SETTING_NAME, 0), /* 21 */ - {NULL, NULL, 0, NULL, 0} + SETTING_FIELD (NM_SETTING_CONNECTION_SETTING_NAME, nmc_fields_setting_connection + 1), /* 0 */ + SETTING_FIELD (NM_SETTING_WIRED_SETTING_NAME, nmc_fields_setting_wired + 1), /* 1 */ + SETTING_FIELD (NM_SETTING_802_1X_SETTING_NAME, nmc_fields_setting_8021X + 1), /* 2 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SETTING_NAME, nmc_fields_setting_wireless + 1), /* 3 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, nmc_fields_setting_wireless_security + 1), /* 4 */ + SETTING_FIELD (NM_SETTING_IP4_CONFIG_SETTING_NAME, nmc_fields_setting_ip4_config + 1), /* 5 */ + SETTING_FIELD (NM_SETTING_IP6_CONFIG_SETTING_NAME, nmc_fields_setting_ip6_config + 1), /* 6 */ + SETTING_FIELD (NM_SETTING_SERIAL_SETTING_NAME, nmc_fields_setting_serial + 1), /* 7 */ + SETTING_FIELD (NM_SETTING_PPP_SETTING_NAME, nmc_fields_setting_ppp + 1), /* 8 */ + SETTING_FIELD (NM_SETTING_PPPOE_SETTING_NAME, nmc_fields_setting_pppoe + 1), /* 9 */ + SETTING_FIELD (NM_SETTING_GSM_SETTING_NAME, nmc_fields_setting_gsm + 1), /* 10 */ + SETTING_FIELD (NM_SETTING_CDMA_SETTING_NAME, nmc_fields_setting_cdma + 1), /* 11 */ + SETTING_FIELD (NM_SETTING_BLUETOOTH_SETTING_NAME, nmc_fields_setting_bluetooth + 1), /* 12 */ + SETTING_FIELD (NM_SETTING_OLPC_MESH_SETTING_NAME, nmc_fields_setting_olpc_mesh + 1), /* 13 */ + SETTING_FIELD (NM_SETTING_VPN_SETTING_NAME, nmc_fields_setting_vpn + 1), /* 14 */ + SETTING_FIELD (NM_SETTING_WIMAX_SETTING_NAME, nmc_fields_setting_wimax + 1), /* 15 */ + SETTING_FIELD (NM_SETTING_INFINIBAND_SETTING_NAME, nmc_fields_setting_infiniband + 1), /* 16 */ + SETTING_FIELD (NM_SETTING_BOND_SETTING_NAME, nmc_fields_setting_bond + 1), /* 17 */ + SETTING_FIELD (NM_SETTING_VLAN_SETTING_NAME, nmc_fields_setting_vlan + 1), /* 18 */ + SETTING_FIELD (NM_SETTING_ADSL_SETTING_NAME, nmc_fields_setting_adsl + 1), /* 19 */ + SETTING_FIELD (NM_SETTING_BRIDGE_SETTING_NAME, nmc_fields_setting_bridge + 1), /* 20 */ + SETTING_FIELD (NM_SETTING_BRIDGE_PORT_SETTING_NAME, nmc_fields_setting_bridge_port + 1), /* 21 */ + SETTING_FIELD (NM_SETTING_TEAM_SETTING_NAME, nmc_fields_setting_team + 1), /* 22 */ + SETTING_FIELD (NM_SETTING_TEAM_PORT_SETTING_NAME, nmc_fields_setting_team_port + 1), /* 23 */ + SETTING_FIELD (NM_SETTING_DCB_SETTING_NAME, nmc_fields_setting_dcb + 1), /* 24 */ + {NULL, NULL, 0, NULL, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTINGS_NAMES_ALL_X NM_SETTING_CONNECTION_SETTING_NAME","\ NM_SETTING_WIRED_SETTING_NAME","\ @@ -119,7 +176,10 @@ static NmcOutputField nmc_fields_settings_names[] = { NM_SETTING_BOND_SETTING_NAME","\ NM_SETTING_VLAN_SETTING_NAME","\ NM_SETTING_BRIDGE_SETTING_NAME","\ - NM_SETTING_BRIDGE_PORT_SETTING_NAME + NM_SETTING_BRIDGE_PORT_SETTING_NAME","\ + NM_SETTING_TEAM_SETTING_NAME","\ + NM_SETTING_TEAM_PORT_SETTING_NAME"," \ + NM_SETTING_DCB_SETTING_NAME #if WITH_WIMAX #define NMC_FIELDS_SETTINGS_NAMES_ALL NMC_FIELDS_SETTINGS_NAMES_ALL_X","\ NM_SETTING_WIMAX_SETTING_NAME @@ -127,54 +187,65 @@ static NmcOutputField nmc_fields_settings_names[] = { #define NMC_FIELDS_SETTINGS_NAMES_ALL NMC_FIELDS_SETTINGS_NAMES_ALL_X #endif - -/* Available fields for 'con status' */ -static NmcOutputField nmc_fields_con_status[] = { - {"GROUP", N_("GROUP"), 9, NULL, 0}, /* 0 */ /* used only for 'GENERAL' group listing */ - {"NAME", N_("NAME"), 25, NULL, 0}, /* 1 */ - {"UUID", N_("UUID"), 38, NULL, 0}, /* 2 */ - {"DEVICES", N_("DEVICES"), 10, NULL, 0}, /* 3 */ - {"STATE", N_("STATE"), 12, NULL, 0}, /* 4 */ - {"DEFAULT", N_("DEFAULT"), 8, NULL, 0}, /* 5 */ - {"DEFAULT6", N_("DEFAULT6"), 9, NULL, 0}, /* 6 */ - {"SPEC-OBJECT", N_("SPEC-OBJECT"), 10, NULL, 0}, /* 7 */ - {"VPN", N_("VPN"), 5, NULL, 0}, /* 8 */ - {"DBUS-PATH", N_("DBUS-PATH"), 51, NULL, 0}, /* 9 */ - {"CON-PATH", N_("CON-PATH"), 44, NULL, 0}, /* 10 */ - {"ZONE", N_("ZONE"), 15, NULL, 0}, /* 11 */ - {"MASTER-PATH", N_("MASTER-PATH"), 44, NULL, 0}, /* 12 */ - {NULL, NULL, 0, NULL, 0} +/* Active connection data */ +/* Available fields for GENERAL group */ +static NmcOutputField nmc_fields_con_active_details_general[] = { + {"GROUP", N_("GROUP"), 9}, /* 0 */ + {"NAME", N_("NAME"), 25}, /* 1 */ + {"UUID", N_("UUID"), 38}, /* 2 */ + {"DEVICES", N_("DEVICES"), 10}, /* 3 */ + {"STATE", N_("STATE"), 12}, /* 4 */ + {"DEFAULT", N_("DEFAULT"), 8}, /* 5 */ + {"DEFAULT6", N_("DEFAULT6"), 9}, /* 6 */ + {"SPEC-OBJECT", N_("SPEC-OBJECT"), 10}, /* 7 */ + {"VPN", N_("VPN"), 5}, /* 8 */ + {"DBUS-PATH", N_("DBUS-PATH"), 51}, /* 9 */ + {"CON-PATH", N_("CON-PATH"), 44}, /* 10 */ + {"ZONE", N_("ZONE"), 15}, /* 11 */ + {"MASTER-PATH", N_("MASTER-PATH"), 44}, /* 12 */ + {NULL, NULL, 0} }; -#define NMC_FIELDS_CON_STATUS_ALL "NAME,UUID,DEVICES,STATE,DEFAULT,DEFAULT6,VPN,ZONE,DBUS-PATH,CON-PATH,SPEC-OBJECT,MASTER-PATH" -#define NMC_FIELDS_CON_STATUS_COMMON "NAME,UUID,DEVICES,DEFAULT,VPN,MASTER-PATH" - -/* Available fields for 'con status id/uuid/path <con>' */ -static NmcOutputField nmc_fields_status_details_groups[] = { - {"GENERAL", N_("GENERAL"), 9, NULL, 0}, /* 0 */ - {"IP", N_("IP"), 5, NULL, 0}, /* 1 */ - {"VPN", N_("VPN"), 5, NULL, 0}, /* 2 */ - {NULL, NULL, 0, NULL, 0} -}; -#define NMC_FIELDS_CON_STATUS_DETAILS_ALL "GENERAL,IP,VPN" - -/* GENERAL group is the same as nmc_fields_con_status */ -#define NMC_FIELDS_CON_STATUS_DETAILS_GENERAL_ALL "GROUP,"NMC_FIELDS_CON_STATUS_ALL +#define NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL "GROUP,NAME,UUID,DEVICES,STATE,DEFAULT,DEFAULT6,"\ + "VPN,ZONE,DBUS-PATH,CON-PATH,SPEC-OBJECT,MASTER-PATH" /* IP group is handled by common.c */ /* Available fields for VPN group */ -static NmcOutputField nmc_fields_status_details_vpn[] = { - {"GROUP", N_("GROUP"), 9, NULL, 0}, /* 0 */ - {"TYPE", N_("TYPE"), 15, NULL, 0}, /* 1 */ - {"USERNAME", N_("USERNAME"), 15, NULL, 0}, /* 2 */ - {"GATEWAY", N_("GATEWAY"), 25, NULL, 0}, /* 3 */ - {"BANNER", N_("BANNER"), 120, NULL, 0}, /* 4 */ - {"VPN-STATE", N_("VPN-STATE"), 40, NULL, 0}, /* 5 */ - {"CFG", N_("CFG"), 120, NULL, 0}, /* 6 */ - {NULL, NULL, 0, NULL, 0} +static NmcOutputField nmc_fields_con_active_details_vpn[] = { + {"GROUP", N_("GROUP"), 9}, /* 0 */ + {"TYPE", N_("TYPE"), 15}, /* 1 */ + {"USERNAME", N_("USERNAME"), 15}, /* 2 */ + {"GATEWAY", N_("GATEWAY"), 25}, /* 3 */ + {"BANNER", N_("BANNER"), 120}, /* 4 */ + {"VPN-STATE", N_("VPN-STATE"), 40}, /* 5 */ + {"CFG", N_("CFG"), 120}, /* 6 */ + {NULL, NULL, 0} }; -#define NMC_FIELDS_CON_STATUS_DETAILS_VPN_ALL "GROUP,TYPE,USERNAME,GATEWAY,BANNER,VPN-STATE,CFG" +#define NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL "GROUP,TYPE,USERNAME,GATEWAY,BANNER,VPN-STATE,CFG" + +/* defined in common.c */ +extern NmcOutputField nmc_fields_ip4_config[]; +extern NmcOutputField nmc_fields_ip6_config[]; +extern NmcOutputField nmc_fields_dhcp4_config[]; +extern NmcOutputField nmc_fields_dhcp6_config[]; + +/* Available fields for 'connection show <con>' - active part */ +static NmcOutputField nmc_fields_con_active_details_groups[] = { + {"GENERAL", N_("GENERAL"), 0, nmc_fields_con_active_details_general + 1}, /* 0 */ + {"IP4", N_("IP4"), 0, nmc_fields_ip4_config + 1 }, /* 1 */ + {"DHCP4", N_("DHCP4"), 0, nmc_fields_dhcp4_config + 1 }, /* 2 */ + {"IP6", N_("IP6"), 0, nmc_fields_ip6_config + 1 }, /* 3 */ + {"DHCP6", N_("DHCP6"), 0, nmc_fields_dhcp6_config + 1 }, /* 4 */ + {"VPN", N_("VPN"), 0, nmc_fields_con_active_details_vpn + 1 }, /* 5 */ + {NULL, NULL, 0, NULL} +}; +#define NMC_FIELDS_CON_ACTIVE_DETAILS_ALL "GENERAL,IP4,DHCP4,IP6,DHCP6,VPN" +/* Pseudo group names for 'connection show <con>' */ +/* e.g.: nmcli -f profile con show my-eth0 */ +/* e.g.: nmcli -f active con show my-eth0 */ +#define CON_SHOW_DETAIL_GROUP_PROFILE "profile" +#define CON_SHOW_DETAIL_GROUP_ACTIVE "active" typedef struct { NmCli *nmc; @@ -188,270 +259,293 @@ extern GMainLoop *loop; static ArgsInfo args_info; static guint progress_id = 0; /* ID of event source for displaying progress */ +/* for readline TAB completion in editor */ +typedef struct { + NmCli *nmc; + char *con_type; + NMConnection *connection; + NMSetting *setting; +} TabCompletionInfo; +static TabCompletionInfo nmc_tab_completion = {NULL, NULL, NULL, NULL}; + +/* Global variable defined in nmcli.c - used for TAB completion */ +extern NmCli nm_cli; + +static char *gen_connection_types (const char *text, int state); + static void usage (void) { fprintf (stderr, - _("Usage: nmcli connection { COMMAND | help }\n" - " COMMAND := { list | status | up | down | delete }\n\n" - " list [id <id> | uuid <id>]\n" - " status [id <id> | uuid <id> | path <path>]\n" + _("Usage: nmcli connection { COMMAND | help }\n\n" + "COMMAND := { show | up | down | add | modify | edit | delete | reload | load }\n\n" + " show [--active] [[id | uuid | path | apath] <ID>] ...\n\n" #if WITH_WIMAX - " up id <id> | uuid <id> [iface <iface>] [ap <BSSID>] [nsp <name>] [--nowait] [--timeout <timeout>]\n" + " up [[id | uuid | path] <ID>] [ifname <ifname>] [ap <BSSID>] [nsp <name>]\n\n" #else - " up id <id> | uuid <id> [iface <iface>] [ap <BSSID>] [--nowait] [--timeout <timeout>]\n" + " up [[id | uuid | path] <ID>] [ifname <ifname>] [ap <BSSID>]\n\n" #endif - " down id <id> | uuid <id>\n" - " delete id <id> | uuid <id>\n" - "\n" - )); + " down [id | uuid | path | apath] <ID>\n\n" + " add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS\n\n" + " modify [--temporary] [id | uuid | path] <ID> ([+|-]<setting>.<property> <value>)+\n\n" + " edit [id | uuid | path] <ID>\n" + " edit [type <new_con_type>] [con-name <new_con_name>]\n\n" + " delete [id | uuid | path] <ID>\n\n" + " reload\n\n" + " load <filename> [ <filename>... ]\n\n")); } -/* The real commands that do something - i.e. not 'help', etc. */ -static const char *real_con_commands[] = { - "list", - "status", - "up", - "down", - "delete", - NULL -}; - -/* quit main loop */ static void -quit (void) +usage_connection_show (void) { - if (progress_id) { - g_source_remove (progress_id); - nmc_terminal_erase_line (); - } - - g_main_loop_quit (loop); /* quit main loop */ + fprintf (stderr, + _("Usage: nmcli connection show { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [--active]\n" + "\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" + "profiles are listed. When --active option is specified, only the active\n" + "profiles are shown.\n" + "\n" + "ARGUMENTS := [--active] [id | uuid | path | apath] <ID> ...\n" + "\n" + "Show details for specified connections. By default, both static configuration\n" + "and active connection data are displayed. It is possible to filter the output\n" + "using global '--fields' option. Refer to the manual page for more information.\n" + "When --active option is specified, only the active profiles are taken into\n" + "account.\n")); } -static gboolean -nmc_connection_detail (NMConnection *connection, NmCli *nmc) +static void +usage_connection_up (void) { - GError *error = NULL; - GArray *print_settings_array; - int i; - char *fields_str; - char *fields_all = NMC_FIELDS_SETTINGS_NAMES_ALL; - char *fields_common = NMC_FIELDS_SETTINGS_NAMES_ALL; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; - gboolean was_output = FALSE; - - if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) - fields_str = fields_common; - else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) - fields_str = fields_all; - else - fields_str = nmc->required_fields; - - print_settings_array = parse_output_fields (fields_str, nmc_fields_settings_names, &error); - if (error) { - if (error->code == 0) - g_string_printf (nmc->return_text, _("Error: 'con list': %s"), error->message); - else - g_string_printf (nmc->return_text, _("Error: 'con list': %s; allowed fields: %s"), error->message, NMC_FIELDS_SETTINGS_NAMES_ALL); - g_error_free (error); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return FALSE; - } - - nmc->allowed_fields = nmc_fields_settings_names; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ONLY; - nmc->print_fields.header_name = _("Connection details"); - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTINGS_NAMES_ALL, nmc->allowed_fields, NULL); - print_fields (nmc->print_fields, nmc->allowed_fields); - - /* Loop through the required settings and print them. */ - for (i = 0; i < print_settings_array->len; i++) { - NMSetting *setting; - int section_idx = g_array_index (print_settings_array, int, i); - - if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output) - printf ("\n"); /* Empty line */ - - was_output = FALSE; - - setting = nm_connection_get_setting_by_name (connection, nmc_fields_settings_names[section_idx].name); - if (setting) { - setting_details (setting, nmc); - was_output = TRUE; - continue; - } - } - - if (print_settings_array) - g_array_free (print_settings_array, FALSE); - - return NMC_RESULT_SUCCESS; + fprintf (stderr, + _("Usage: nmcli connection up { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [id | uuid | path] <ID> [ifname <ifname>] [ap <BSSID>] [nsp <name>]\n" + "\n" + "Activate a connection on a device. The profile to activate is identified by its\n" + "name, UUID or D-Bus path.\n" + "\n" + "ARGUMENTS := ifname <ifname> [ap <BSSID>] [nsp <name>]\n" + "\n" + "Activate a device with a connection. The connection profile is selected\n" + "automatically by NetworkManager.\n" + "\n" + "ifname - specifies the device to active the connection on\n" + "ap - specifies AP to connect to (only valid for Wi-Fi)\n" + "nsp - specifies NSP to connect to (only valid for WiMAX)\n\n")); } static void -show_connection (NMConnection *data, gpointer user_data) +usage_connection_down (void) { - NMConnection *connection = (NMConnection *) data; - NmCli *nmc = (NmCli *) user_data; - NMSettingConnection *s_con; - guint64 timestamp; - time_t timestamp_real; - char *timestamp_str; - char timestamp_real_str[64]; - - s_con = nm_connection_get_setting_connection (connection); - if (s_con) { - /* Obtain field values */ - timestamp = nm_setting_connection_get_timestamp (s_con); - timestamp_str = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp); - timestamp_real = timestamp; - strftime (timestamp_real_str, sizeof (timestamp_real_str), "%c", localtime (×tamp_real)); - - nmc->allowed_fields[0].value = nm_setting_connection_get_id (s_con); - nmc->allowed_fields[1].value = nm_setting_connection_get_uuid (s_con); - nmc->allowed_fields[2].value = nm_setting_connection_get_connection_type (s_con); - nmc->allowed_fields[3].value = timestamp_str; - nmc->allowed_fields[4].value = timestamp ? timestamp_real_str : _("never"); - nmc->allowed_fields[5].value = nm_setting_connection_get_autoconnect (s_con) ? _("yes") : _("no"); - nmc->allowed_fields[6].value = nm_setting_connection_get_read_only (s_con) ? _("yes") : _("no"); - nmc->allowed_fields[7].value = nm_connection_get_path (connection); - - nmc->print_fields.flags &= ~NMC_PF_FLAG_MAIN_HEADER_ADD & ~NMC_PF_FLAG_MAIN_HEADER_ONLY & ~NMC_PF_FLAG_FIELD_NAMES; /* Clear header flags */ - print_fields (nmc->print_fields, nmc->allowed_fields); - - g_free (timestamp_str); - } + fprintf (stderr, + _("Usage: nmcli connection down { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [id | uuid | path | apath] <ID>\n" + "\n" + "Deactivate a connection from a device (without preventing the device from\n" + "further auto-activation). The profile to deactivate is identified by its name,\n" + "UUID or D-Bus path.\n\n")); } -static NMConnection * -find_connection (GSList *list, const char *filter_type, const char *filter_val) +static void +usage_connection_add (void) { - NMSettingConnection *s_con; - NMConnection *connection; - GSList *iterator; - const char *id; - const char *uuid; - - iterator = list; - while (iterator) { - connection = NM_CONNECTION (iterator->data); - s_con = nm_connection_get_setting_connection (connection); - if (s_con) { - id = nm_setting_connection_get_id (s_con); - uuid = nm_setting_connection_get_uuid (s_con); - if (filter_type) { - if ((strcmp (filter_type, "id") == 0 && strcmp (filter_val, id) == 0) || - (strcmp (filter_type, "uuid") == 0 && strcmp (filter_val, uuid) == 0)) { - return connection; - } - } - } - iterator = g_slist_next (iterator); - } + fprintf (stderr, + _("Usage: nmcli connection add { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS\n\n" + " COMMON_OPTIONS:\n" + " type <type>\n" + " ifname <interface name> | \"*\"\n" + " [con-name <connection name>]\n" + " [autoconnect yes|no]\n\n" + " [save yes|no]\n\n" + " TYPE_SPECIFIC_OPTIONS:\n" + " ethernet: [mac <MAC address>]\n" + " [cloned-mac <cloned MAC address>]\n" + " [mtu <MTU>]\n\n" + " wifi: ssid <SSID>\n" + " [mac <MAC address>]\n" + " [cloned-mac <cloned MAC address>]\n" + " [mtu <MTU>]\n\n" + " wimax: [mac <MAC address>]\n" + " [nsp <NSP>]\n\n" + " pppoe: username <PPPoE username>\n" + " [password <PPPoE password>]\n" + " [service <PPPoE service name>]\n" + " [mtu <MTU>]\n" + " [mac <MAC address>]\n\n" + " gsm: apn <APN>\n" + " [user <username>]\n" + " [password <password>]\n\n" + " cdma: [user <username>]\n" + " [password <password>]\n\n" + " infiniband: [mac <MAC address>]\n" + " [mtu <MTU>]\n" + " [transport-mode datagram | connected]\n" + " [parent <ifname>]\n" + " [p-key <IPoIB P_Key>]\n\n" + " bluetooth: [addr <bluetooth address>]\n" + " [bt-type panu|dun-gsm|dun-cdma]\n\n" + " vlan: dev <parent device (connection UUID, ifname, or MAC)>\n" + " id <VLAN ID>\n" + " [flags <VLAN flags>]\n" + " [ingress <ingress priority mapping>]\n" + " [egress <egress priority mapping>]\n" + " [mtu <MTU>]\n\n" + " bond: [mode balance-rr (0) | active-backup (1) | balance-xor (2) | broadcast (3) |\n" + " 802.3ad (4) | balance-tlb (5) | balance-alb (6)]\n" + " [primary <ifname>]\n" + " [miimon <num>]\n" + " [downdelay <num>]\n" + " [updelay <num>]\n" + " [arp-interval <num>]\n" + " [arp-ip-target <num>]\n\n" + " bond-slave: master <master (ifname, or connection UUID or name)>\n\n" + " team: [config <file>|<raw JSON data>]\n\n" + " team-slave: master <master (ifname, or connection UUID or name)>\n" + " [config <file>|<raw JSON data>]\n\n" + " bridge: [stp yes|no]\n" + " [priority <num>]\n" + " [forward-delay <2-30>]\n" + " [hello-time <1-10>]\n" + " [max-age <6-40>]\n" + " [ageing-time <0-1000000>]\n" + " [mac <MAC address>]\n\n" + " bridge-slave: master <master (ifname, or connection UUID or name)>\n" + " [priority <0-63>]\n" + " [path-cost <1-65535>]\n" + " [hairpin yes|no]\n\n" + " vpn: vpn-type vpnc|openvpn|pptp|openconnect|openswan|libreswan|ssh|l2tp|iodine|...\n" + " [user <username>]\n\n" + " olpc-mesh: ssid <SSID>\n" + " [channel <1-13>]\n" + " [dhcp-anycast <MAC address>]\n\n" + " IP_OPTIONS:\n" + " [ip4 <IPv4 address>] [gw4 <IPv4 gateway>]\n" + " [ip6 <IPv6 address>] [gw6 <IPv6 gateway>]\n\n")); +} - return NULL; +static void +usage_connection_modify (void) +{ + fprintf (stderr, + _("Usage: nmcli connection modify { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [id | uuid | path] <ID> ([+|-]<setting>.<property> <value>)+\n" + "\n" + "Modify one or more properties of the connection profile.\n" + "The profile is identified by its name, UUID or D-Bus path. For multi-valued\n" + "properties you can use optional '+' or '-' prefix to the property name.\n" + "The '+' sign allows appending items instead of overwriting the whole value.\n" + "The '-' sign allows removing selected items instead of the whole value.\n" + "\n" + "Examples:\n" + "nmcli con mod home-wifi wifi.ssid rakosnicek\n" + "nmcli con mod em1-1 ipv4.method manual ipv4.addr \"192.168.1.2/24, 10.10.1.5/8\"\n" + "nmcli con mod em1-1 +ipv4.dns 8.8.4.4\n" + "nmcli con mod em1-1 -ipv4.dns 1\n" + "nmcli con mod em1-1 -ipv6.addr \"abbe::cafe/56\"\n" + "nmcli con mod bond0 +bond.options mii=500\n" + "nmcli con mod bond0 -bond.options downdelay\n\n")); } -static NMCResultCode -do_connections_list (NmCli *nmc, int argc, char **argv) +static void +usage_connection_edit (void) { - GError *error1 = NULL; - GError *error2 = NULL; - char *fields_str; - char *fields_all = NMC_FIELDS_CON_LIST_ALL; - char *fields_common = NMC_FIELDS_CON_LIST_COMMON; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; - gboolean valid_param_specified = FALSE; + fprintf (stderr, + _("Usage: nmcli connection edit { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [id | uuid | path] <ID>\n" + "\n" + "Edit an existing connection profile in an interactive editor.\n" + "The profile is identified by its name, UUID or D-Bus path\n" + "\n" + "ARGUMENTS := [type <new connection type>] [con-name <new connection name>]\n" + "\n" + "Add a new connection profile in an interactive editor.\n\n")); +} - nmc->should_wait = FALSE; +static void +usage_connection_delete (void) +{ + fprintf (stderr, + _("Usage: nmcli connection delete { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [id | uuid | path] <ID>\n" + "\n" + "Delete a connection profile.\n" + "The profile is identified by its name, UUID or D-Bus path.\n\n")); +} - if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) - fields_str = fields_common; - else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) - fields_str = fields_all; - else - fields_str = nmc->required_fields; +static void +usage_connection_reload (void) +{ + fprintf (stderr, + _("Usage: nmcli connection reload { help }\n" + "\n" + "Reload all connection files from disk.\n\n")); +} - nmc->allowed_fields = nmc_fields_con_list; - nmc->print_fields.indices = parse_output_fields (fields_str, nmc->allowed_fields, &error1); - /* error1 is checked later - it's not valid for connection details */ +static void +usage_connection_load (void) +{ + fprintf (stderr, + _("Usage: nmcli connection load { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := <filename> [<filename>...]\n" + "\n" + "Load/reload one or more connection files from disk. Use this after manually\n" + "editing a connection file to ensure that NetworkManager is aware of its latest\n" + "state.\n\n")); +} - if (argc == 0) { - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error2)) - goto error; - if (error1) - goto error; - valid_param_specified = TRUE; - /* Print headers */ - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ADD | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.header_name = _("Connection list"); - print_fields (nmc->print_fields, nmc->allowed_fields); +/* The real commands that do something - i.e. not 'help', etc. */ +static const char *real_con_commands[] = { + "show", + "up", + "down", + "add", + "modify", + "edit", + "delete", + "reload", + "load", + NULL +}; - /* Print values */ - g_slist_foreach (nmc->system_connections, (GFunc) show_connection, nmc); +/* quit main loop */ +static void +quit (void) +{ + if (progress_id) { + g_source_remove (progress_id); + progress_id = 0; + nmc_terminal_erase_line (); } - else { - while (argc > 0) { - if (strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0) { - const char *selector = *argv; - NMConnection *con; - - if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return nmc->return_value; - } - valid_param_specified = TRUE; - if (!nmc->mode_specified) - nmc->multiline_output = TRUE; /* multiline mode is default for 'con list id|uuid' */ - con = find_connection (nmc->system_connections, selector, *argv); - if (con) { - nmc_connection_detail (con, nmc); - } - else { - g_string_printf (nmc->return_text, _("Error: %s - no such connection."), *argv); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - } - break; - } - else { - fprintf (stderr, _("Unknown parameter: %s\n"), *argv); - } + g_main_loop_quit (loop); /* quit main loop */ +} - argc--; - argv++; - } - } +static const char * +construct_header_name (const char *base, const char *spec) +{ + static char header_name[128]; - if (!valid_param_specified) { - g_string_printf (nmc->return_text, _("Error: no valid parameter specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - } - return nmc->return_value; + if (spec == NULL) + return base; -error: - if (error1) { - if (error1->code == 0) - g_string_printf (nmc->return_text, _("Error: 'con list': %s"), error1->message); - else - g_string_printf (nmc->return_text, _("Error: 'con list': %s; allowed fields: %s"), error1->message, NMC_FIELDS_CON_LIST_ALL); - g_error_free (error1); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - } - if (error2) { - g_string_printf (nmc->return_text, _("Error: %s."), error2->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - g_error_free (error2); - } + g_strlcpy (header_name, base, sizeof (header_name)); + g_strlcat (header_name, " (", sizeof (header_name)); + g_strlcat (header_name, spec, sizeof (header_name)); + g_strlcat (header_name, ")", sizeof (header_name)); - return nmc->return_value; + return header_name; } static const char * @@ -495,6 +589,55 @@ vpn_connection_state_to_string (NMVPNConnectionState state) } } +/* Caller has to free the returned string */ +static char * +get_ac_device_string (NMActiveConnection *active) +{ + GString *dev_str; + const GPtrArray *devices; + int i; + + if (!active) + return NULL; + + /* Get devices of the active connection */ + dev_str = g_string_new (NULL); + devices = nm_active_connection_get_devices (active); + for (i = 0; devices && (i < devices->len); i++) { + NMDevice *device = g_ptr_array_index (devices, i); + const char *dev_iface = nm_device_get_iface (device); + + if (dev_iface) { + g_string_append (dev_str, dev_iface); + g_string_append_c (dev_str, ','); + } + } + if (dev_str->len > 0) + g_string_truncate (dev_str, dev_str->len - 1); /* Cut off last ',' */ + + return g_string_free (dev_str, FALSE); +} + +static NMActiveConnection * +get_ac_for_connection (const GPtrArray *active_cons, NMConnection *connection) +{ + const char *con_path; + int i; + NMActiveConnection *ac = NULL; + + /* Is the connection active? */ + con_path = nm_connection_get_path (connection); + for (i = 0; active_cons && i < active_cons->len; i++) { + NMActiveConnection *candidate = g_ptr_array_index (active_cons, i); + + if (!g_strcmp0 (nm_active_connection_get_connection (candidate), con_path)) { + ac = candidate; + break; + } + } + return ac; +} + static NMConnection * get_connection_for_active (const GSList *con_list, NMActiveConnection *active) { @@ -514,7 +657,188 @@ get_connection_for_active (const GSList *con_list, NMActiveConnection *active) } static gboolean -fill_in_fields_con_status (NMActiveConnection *active, GSList *con_list) +nmc_connection_profile_details (NMConnection *connection, NmCli *nmc) +{ + GError *error = NULL; + GArray *print_settings_array; + GPtrArray *prop_array = NULL; + int i; + char *fields_str; + char *fields_all = NMC_FIELDS_SETTINGS_NAMES_ALL; + char *fields_common = NMC_FIELDS_SETTINGS_NAMES_ALL; + const char *base_hdr = _("Connection profile details"); + gboolean was_output = FALSE; + + if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) + fields_str = fields_common; + else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) + fields_str = fields_all; + else + fields_str = nmc->required_fields; + + print_settings_array = parse_output_fields (fields_str, nmc_fields_settings_names, TRUE, &prop_array, &error); + if (error) { + g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return FALSE; + } + g_assert (print_settings_array); + + /* Main header */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_connection_get_id (connection)); + nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTINGS_NAMES_ALL, + nmc_fields_settings_names, FALSE, NULL, NULL); + + nmc_fields_settings_names[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY; + print_required_fields (nmc, nmc_fields_settings_names); + + /* Loop through the required settings and print them. */ + for (i = 0; i < print_settings_array->len; i++) { + NMSetting *setting; + int section_idx = g_array_index (print_settings_array, int, i); + const char *prop_name = (const char *) g_ptr_array_index (prop_array, i); + + if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output) + printf ("\n"); /* Empty line */ + + was_output = FALSE; + + /* Remove any previous data */ + nmc_empty_output_fields (nmc); + + setting = nm_connection_get_setting_by_name (connection, nmc_fields_settings_names[section_idx].name); + if (setting) { + setting_details (setting, nmc, prop_name); + was_output = TRUE; + continue; + } + } + + g_array_free (print_settings_array, TRUE); + if (prop_array) + g_ptr_array_free (prop_array, TRUE); + + return TRUE; +} + +static NMActiveConnection * +find_active_connection (const GPtrArray *active_cons, + const GSList *cons, + const char *filter_type, + const char *filter_val, + int *idx) +{ + int i; + int start = (idx && *idx > 0) ? *idx : 0; + const char *path, *a_path, *path_num, *a_path_num; + const char *id; + const char *uuid; + NMConnection *con; + NMActiveConnection *found = NULL; + + for (i = start; active_cons && (i < active_cons->len); i++) { + NMActiveConnection *candidate = g_ptr_array_index (active_cons, i); + + path = nm_active_connection_get_connection (candidate); + a_path = nm_object_get_path (NM_OBJECT (candidate)); + uuid = nm_active_connection_get_uuid (candidate); + path_num = path ? strrchr (path, '/') + 1 : NULL; + a_path_num = a_path ? strrchr (a_path, '/') + 1 : NULL; + + con = get_connection_for_active (cons, candidate); + id = nm_connection_get_id (con); + + /* When filter_type is NULL, compare connection ID (filter_val) + * against all types. Otherwise, only compare against the specific + * type. If 'path' or 'apath' filter types are specified, comparison + * against numeric index (in addition to the whole path) is allowed. + */ + if ( ( (!filter_type || strcmp (filter_type, "id") == 0) + && strcmp (filter_val, id) == 0) + || ( (!filter_type || strcmp (filter_type, "uuid") == 0) + && strcmp (filter_val, uuid) == 0) + || ( (!filter_type || strcmp (filter_type, "path") == 0) + && (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0))) + || ( (!filter_type || strcmp (filter_type, "apath") == 0) + && (g_strcmp0 (filter_val, a_path) == 0 || (filter_type && g_strcmp0 (filter_val, a_path_num) == 0)))) { + if (!idx) + return candidate; + if (found) { + *idx = i; + return found; + } + found = candidate; + } + } + + if (idx) + *idx = 0; + return found; +} + +static void +fill_output_connection (gpointer data, gpointer user_data, gboolean active_only) +{ + NMConnection *connection = (NMConnection *) data; + NmCli *nmc = (NmCli *) user_data; + NMSettingConnection *s_con; + guint64 timestamp; + time_t timestamp_real; + char *timestamp_str; + char *timestamp_real_str = ""; + NmcOutputField *arr; + NMActiveConnection *ac = NULL; + const char *ac_path = NULL; + const char *ac_state = NULL; + char *ac_dev = NULL; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + ac = get_ac_for_connection (nm_client_get_active_connections (nmc->client), connection); + if (active_only && !ac) + return; + + if (ac) { + ac_path = nm_object_get_path (NM_OBJECT (ac)); + ac_state = active_connection_state_to_string (nm_active_connection_get_state (ac)); + ac_dev = get_ac_device_string (ac); + } + + /* Obtain field values */ + timestamp = nm_setting_connection_get_timestamp (s_con); + timestamp_str = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp); + if (timestamp) { + timestamp_real = timestamp; + timestamp_real_str = g_malloc0 (64); + strftime (timestamp_real_str, 64, "%c", localtime (×tamp_real)); + } + + arr = nmc_dup_fields_array (nmc_fields_con_show, + sizeof (nmc_fields_con_show), + 0); + set_val_strc (arr, 0, nm_setting_connection_get_id (s_con)); + set_val_strc (arr, 1, nm_setting_connection_get_uuid (s_con)); + set_val_strc (arr, 2, nm_setting_connection_get_connection_type (s_con)); + set_val_str (arr, 3, timestamp_str); + set_val_str (arr, 4, timestamp ? timestamp_real_str : g_strdup (_("never"))); + set_val_strc (arr, 5, nm_setting_connection_get_autoconnect (s_con) ? _("yes") : _("no")); + set_val_strc (arr, 6, nm_setting_connection_get_read_only (s_con) ? _("yes") : _("no")); + set_val_strc (arr, 7, nm_connection_get_path (connection)); + set_val_strc (arr, 8, ac ? _("yes") : _("no")); + set_val_str (arr, 9, ac_dev); + set_val_strc (arr, 10, ac_state); + set_val_strc (arr, 11, ac_path); + + g_ptr_array_add (nmc->output_data, arr); +} + +static void +fill_output_active_connection (NMActiveConnection *active, + NmCli *nmc, + gboolean with_group, + guint32 o_flags) { GSList *iter; const char *active_path; @@ -523,7 +847,10 @@ fill_in_fields_con_status (NMActiveConnection *active, GSList *con_list) GString *dev_str; NMActiveConnectionState state; int i; - gboolean success = FALSE; + GSList *con_list = nmc->system_connections; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + int idx_start = with_group ? 0 : 1; active_path = nm_active_connection_get_connection (active); state = nm_active_connection_get_state (active); @@ -543,20 +870,29 @@ fill_in_fields_con_status (NMActiveConnection *active, GSList *con_list) if (dev_str->len > 0) g_string_truncate (dev_str, dev_str->len - 1); /* Cut off last ',' */ + tmpl = nmc_fields_con_active_details_general; + tmpl_len = sizeof (nmc_fields_con_active_details_general); + if (!with_group) { + tmpl++; + tmpl_len -= sizeof (NmcOutputField); + } + /* Fill field values */ - nmc_fields_con_status[0].value = nmc_fields_status_details_groups[0].name; - nmc_fields_con_status[1].value = _("N/A"); - nmc_fields_con_status[2].value = nm_active_connection_get_uuid (active); - nmc_fields_con_status[3].value = dev_str->str; - nmc_fields_con_status[4].value = active_connection_state_to_string (state); - nmc_fields_con_status[5].value = nm_active_connection_get_default (active) ? _("yes") : _("no"); - nmc_fields_con_status[6].value = nm_active_connection_get_default6 (active) ? _("yes") : _("no"); - nmc_fields_con_status[7].value = nm_active_connection_get_specific_object (active); - nmc_fields_con_status[8].value = NM_IS_VPN_CONNECTION (active) ? _("yes") : _("no"); - nmc_fields_con_status[9].value = nm_object_get_path (NM_OBJECT (active)); - nmc_fields_con_status[10].value = nm_active_connection_get_connection (active); - nmc_fields_con_status[11].value = _("N/A"); - nmc_fields_con_status[12].value = nm_active_connection_get_master (active); + arr = nmc_dup_fields_array (tmpl, tmpl_len, o_flags); + if (with_group) + set_val_strc (arr, 0, nmc_fields_con_active_details_groups[0].name); + set_val_strc (arr, 1-idx_start, _("N/A")); + set_val_strc (arr, 2-idx_start, nm_active_connection_get_uuid (active)); + set_val_str (arr, 3-idx_start, dev_str->str); + set_val_strc (arr, 4-idx_start, active_connection_state_to_string (state)); + set_val_strc (arr, 5-idx_start, nm_active_connection_get_default (active) ? _("yes") : _("no")); + set_val_strc (arr, 6-idx_start, nm_active_connection_get_default6 (active) ? _("yes") : _("no")); + set_val_strc (arr, 7-idx_start, nm_active_connection_get_specific_object (active)); + set_val_strc (arr, 8-idx_start, NM_IS_VPN_CONNECTION (active) ? _("yes") : _("no")); + set_val_strc (arr, 9-idx_start, nm_object_get_path (NM_OBJECT (active))); + set_val_strc (arr, 10-idx_start, nm_active_connection_get_connection (active)); + set_val_strc (arr, 11-idx_start, _("N/A")); + set_val_strc (arr, 12-idx_start, nm_active_connection_get_master (active)); for (iter = con_list; iter; iter = g_slist_next (iter)) { NMConnection *connection = (NMConnection *) iter->data; @@ -568,75 +904,15 @@ fill_in_fields_con_status (NMActiveConnection *active, GSList *con_list) g_assert (s_con != NULL); /* Fill field values that depend on NMConnection */ - nmc_fields_con_status[1].value = nm_setting_connection_get_id (s_con); - nmc_fields_con_status[11].value = nm_setting_connection_get_zone (s_con); + set_val_strc (arr, 1-idx_start, nm_setting_connection_get_id (s_con)); + set_val_strc (arr, 11-idx_start, nm_setting_connection_get_zone (s_con)); - success = TRUE; break; } } + g_ptr_array_add (nmc->output_data, arr); - /* Just free GString here, the char array has to be freed after printing - * (by free_fields_con_status()) */ g_string_free (dev_str, FALSE); - return success; -} - -static void -free_fields_con_status (void) -{ - /* Just DEVICES string was dynamically allocated */ - g_free ((char *) nmc_fields_con_status[3].value); - nmc_fields_con_status[3].value = NULL; -} - -static void -show_active_connection (gpointer data, gpointer user_data) -{ - NMActiveConnection *active = NM_ACTIVE_CONNECTION (data); - NmCli *nmc = (NmCli *) user_data; - - fill_in_fields_con_status (active, nmc->system_connections); - - nmc->print_fields.flags &= ~NMC_PF_FLAG_MAIN_HEADER_ADD & ~NMC_PF_FLAG_MAIN_HEADER_ONLY & ~NMC_PF_FLAG_FIELD_NAMES; /* Clear header flags */ - print_fields (nmc->print_fields, nmc->allowed_fields); - - free_fields_con_status (); -} - -static NMActiveConnection * -find_active_connection (const GPtrArray *active_cons, const GSList *cons, - const char *filter_type, const char *filter_val) -{ - int i; - const char *s_path, *a_path; - const char *id; - const char *uuid; - NMConnection *con; - NMSettingConnection *s_con; - - for (i = 0; active_cons && (i < active_cons->len); i++) { - NMActiveConnection *candidate = g_ptr_array_index (active_cons, i); - - s_path = nm_active_connection_get_connection (candidate); - a_path = nm_object_get_path (NM_OBJECT (candidate)); - uuid = nm_active_connection_get_uuid (candidate); - - con = get_connection_for_active (cons, candidate); - s_con = nm_connection_get_setting_connection (con); - g_assert (s_con != NULL); - id = nm_setting_connection_get_id (s_con); - - if (filter_type) { - if ( (strcmp (filter_type, "id") == 0 && strcmp (filter_val, id) == 0) - || (strcmp (filter_type, "uuid") == 0 && strcmp (filter_val, uuid) == 0) - || (strcmp (filter_type, "path") == 0 && strcmp (filter_val, s_path) == 0) - || (strcmp (filter_type, "path") == 0 && strcmp (filter_val, a_path) == 0)) { - return candidate; - } - } - } - return NULL; } typedef struct { @@ -683,6 +959,9 @@ find_vpn_gateway_key (const char *vpn_type) if (g_strcmp0 (vpn_type, "pptp") == 0) return "gateway"; if (g_strcmp0 (vpn_type, "openconnect") == 0) return "gateway"; if (g_strcmp0 (vpn_type, "openswan") == 0) return "right"; + if (g_strcmp0 (vpn_type, "libreswan") == 0) return "right"; + if (g_strcmp0 (vpn_type, "ssh") == 0) return "remote"; + if (g_strcmp0 (vpn_type, "l2tp") == 0) return "gateway"; return ""; } @@ -694,6 +973,8 @@ find_vpn_username_key (const char *vpn_type) if (g_strcmp0 (vpn_type, "pptp") == 0) return "user"; if (g_strcmp0 (vpn_type, "openconnect") == 0) return "username"; if (g_strcmp0 (vpn_type, "openswan") == 0) return "leftxauthusername"; + if (g_strcmp0 (vpn_type, "libreswan") == 0) return "leftxauthusername"; + if (g_strcmp0 (vpn_type, "l2tp") == 0) return "user"; return ""; } @@ -726,17 +1007,18 @@ get_vpn_data_item (NMConnection *connection, enum VpnDataItem vpn_data_item) /* FIXME end */ static gboolean -nmc_active_connection_detail (NMActiveConnection *acon, NmCli *nmc) +nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc) { GError *error = NULL; GArray *print_groups; + GPtrArray *group_fields = NULL; int i; char *fields_str; - char *fields_all = NMC_FIELDS_CON_STATUS_DETAILS_ALL; - char *fields_common = NMC_FIELDS_CON_STATUS_DETAILS_ALL; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + char *fields_all = NMC_FIELDS_CON_ACTIVE_DETAILS_ALL; + char *fields_common = NMC_FIELDS_CON_ACTIVE_DETAILS_ALL; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + const char *base_hdr = _("Activate connection details"); gboolean was_output = FALSE; if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) @@ -746,77 +1028,93 @@ nmc_active_connection_detail (NMActiveConnection *acon, NmCli *nmc) else fields_str = nmc->required_fields; - print_groups = parse_output_fields (fields_str, nmc_fields_status_details_groups, &error); + print_groups = parse_output_fields (fields_str, nmc_fields_con_active_details_groups, TRUE, &group_fields, &error); if (error) { - if (error->code == 0) - g_string_printf (nmc->return_text, _("Error: 'con status': %s"), error->message); - else - g_string_printf (nmc->return_text, _("Error: 'con status': %s; allowed fields: %s"), error->message, NMC_FIELDS_CON_STATUS_DETAILS_ALL); + g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message); g_error_free (error); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return FALSE; } + g_assert (print_groups); + + /* Main header */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_active_connection_get_uuid (acon)); + nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_ACTIVE_DETAILS_ALL, + nmc_fields_con_active_details_groups, FALSE, NULL, NULL); - nmc->allowed_fields = nmc_fields_status_details_groups; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ONLY; - nmc->print_fields.header_name = _("Active connection details"); - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_STATUS_DETAILS_ALL, nmc->allowed_fields, NULL); - print_fields (nmc->print_fields, nmc->allowed_fields); + nmc_fields_con_active_details_groups[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY; + print_required_fields (nmc, nmc_fields_con_active_details_groups); /* Loop through the groups and print them. */ for (i = 0; i < print_groups->len; i++) { int group_idx = g_array_index (print_groups, int, i); + char *group_fld = (char *) g_ptr_array_index (group_fields, i); if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output) printf ("\n"); /* Empty line */ was_output = FALSE; - /* GENERAL */ - if (strcasecmp (nmc_fields_status_details_groups[group_idx].name, nmc_fields_status_details_groups[0].name) == 0) { - nmc->allowed_fields = nmc_fields_con_status; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_STATUS_DETAILS_GENERAL_ALL, nmc->allowed_fields, NULL); + /* Remove any previous data */ + nmc_empty_output_fields (nmc); - /* Print field names */ - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); + /* GENERAL */ + if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[0].name) == 0) { + /* Add field names */ + tmpl = nmc_fields_con_active_details_general; + tmpl_len = sizeof (nmc_fields_con_active_details_general); + nmc->print_fields.indices = parse_output_fields (group_fld ? group_fld : NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); /* Fill in values */ - fill_in_fields_con_status (acon, nmc->system_connections); + fill_output_active_connection (acon, nmc, TRUE, NMC_OF_FLAG_SECTION_PREFIX); - /* and print them */ - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); + print_data (nmc); /* Print all data */ - free_fields_con_status (); was_output = TRUE; } - /* IP */ - if (strcasecmp (nmc_fields_status_details_groups[group_idx].name, nmc_fields_status_details_groups[1].name) == 0) { - const GPtrArray *devices; - int j; + /* IP4 */ + if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[1].name) == 0) { + gboolean b1 = FALSE; + NMIP4Config *cfg4 = nm_active_connection_get_ip4_config (acon); - devices = nm_active_connection_get_devices (acon); - for (j = 0; devices && (j < devices->len); j++) { - gboolean b1 = FALSE, b2 = FALSE, b3 = FALSE, b4 = FALSE; - NMDevice *device = g_ptr_array_index (devices, j); - NMIP4Config *cfg4 = nm_device_get_ip4_config (device); - NMIP6Config *cfg6 = nm_device_get_ip6_config (device); - NMDHCP4Config *dhcp4 = nm_device_get_dhcp4_config (device); - NMDHCP6Config *dhcp6 = nm_device_get_dhcp6_config (device); + b1 = print_ip4_config (cfg4, nmc, "IP4", group_fld); + was_output = was_output || b1; + } - b1 = print_ip4_config (cfg4, nmc, "IP4"); - b2 = print_dhcp4_config (dhcp4, nmc, "DHCP4"); - b3 = print_ip6_config (cfg6, nmc, "IP6"); - b4 = print_dhcp6_config (dhcp6, nmc, "DHCP6"); - was_output = was_output || b1 || b2 || b3 || b4; - } + /* DHCP4 */ + if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[2].name) == 0) { + gboolean b1 = FALSE; + NMDHCP4Config *dhcp4 = nm_active_connection_get_dhcp4_config (acon); + + b1 = print_dhcp4_config (dhcp4, nmc, "DHCP4", group_fld); + was_output = was_output || b1; + } + + /* IP6 */ + if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[3].name) == 0) { + gboolean b1 = FALSE; + NMIP6Config *cfg6 = nm_active_connection_get_ip6_config (acon); + + b1 = print_ip6_config (cfg6, nmc, "IP6", group_fld); + was_output = was_output || b1; + } + + /* DHCP6 */ + if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[4].name) == 0) { + gboolean b1 = FALSE; + NMDHCP6Config *dhcp6 = nm_active_connection_get_dhcp6_config (acon); + + b1 = print_dhcp6_config (dhcp6, nmc, "DHCP6", group_fld); + was_output = was_output || b1; } /* VPN */ if (NM_IS_VPN_CONNECTION (acon) && - strcasecmp (nmc_fields_status_details_groups[group_idx].name, nmc_fields_status_details_groups[2].name) == 0) { + strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[5].name) == 0) { NMConnection *con; NMSettingConnection *s_con; NMSettingVPN *s_vpn; @@ -831,12 +1129,12 @@ nmc_active_connection_detail (NMActiveConnection *acon, NmCli *nmc) s_con = nm_connection_get_setting_connection (con); g_assert (s_con != NULL); - nmc->allowed_fields = nmc_fields_status_details_vpn; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_STATUS_DETAILS_VPN_ALL, nmc->allowed_fields, NULL); - - /* Print field names */ - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); + tmpl = nmc_fields_con_active_details_vpn; + tmpl_len = sizeof (nmc_fields_con_active_details_vpn); + nmc->print_fields.indices = parse_output_fields (group_fld ? group_fld : NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); s_vpn = nm_connection_get_setting_vpn (con); if (s_vpn) { @@ -858,64 +1156,167 @@ nmc_active_connection_detail (NMActiveConnection *acon, NmCli *nmc) vpn_state = nm_vpn_connection_get_vpn_state (NM_VPN_CONNECTION (acon)); vpn_state_str = g_strdup_printf ("%d - %s", vpn_state, vpn_connection_state_to_string (vpn_state)); - /* Print values */ - set_val_str (nmc->allowed_fields, 0, nmc_fields_status_details_groups[2].name); - set_val_str (nmc->allowed_fields, 1, type_str); - set_val_str (nmc->allowed_fields, 2, username ? username : get_vpn_data_item (con, VPN_DATA_ITEM_USERNAME)); - set_val_str (nmc->allowed_fields, 3, get_vpn_data_item (con, VPN_DATA_ITEM_GATEWAY)); - set_val_str (nmc->allowed_fields, 4, banner_str); - set_val_str (nmc->allowed_fields, 5, vpn_state_str); - set_val_arr (nmc->allowed_fields, 6, (const char **) vpn_data_array); - - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); - - g_free (type_str); - g_free (banner_str); - g_free (vpn_state_str); - g_strfreev (vpn_data_array); + /* Add values */ + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_con_active_details_groups[5].name); + set_val_str (arr, 1, type_str); + set_val_strc (arr, 2, username ? username : get_vpn_data_item (con, VPN_DATA_ITEM_USERNAME)); + set_val_strc (arr, 3, get_vpn_data_item (con, VPN_DATA_ITEM_GATEWAY)); + set_val_str (arr, 4, banner_str); + set_val_str (arr, 5, vpn_state_str); + set_val_arr (arr, 6, vpn_data_array); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ was_output = TRUE; } } - if (print_groups) - g_array_free (print_groups, FALSE); + g_array_free (print_groups, TRUE); + if (group_fields) + g_ptr_array_free (group_fields, TRUE); return TRUE; } -static NMCResultCode -do_connections_status (NmCli *nmc, int argc, char **argv) +static gboolean +split_required_fields_for_con_show (const char *input, + char **profile_flds, + char **active_flds, + GError **error) { - const GPtrArray *active_cons; - GError *err = NULL; - GError *err1 = NULL; + char **fields, **iter; + char *dot; + GString *str1, *str2; + gboolean found; + gboolean group_profile = FALSE; + gboolean group_active = FALSE; + gboolean success = TRUE; + gboolean is_all, is_common; + int i; - nmc->should_wait = FALSE; + if (!input) { + *profile_flds = NULL; + *active_flds = NULL; + return TRUE; + } - if (!nmc_is_nm_running (nmc, &err)) { - if (err) { - g_string_printf (nmc->return_text, _("Error: Can't find out if NetworkManager is running: %s."), err->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - g_error_free (err); - } else { - g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); - nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + str1 = g_string_new (NULL); + str2 = g_string_new (NULL); + + /* Split supplied fields string */ + fields = g_strsplit_set (input, ",", -1); + for (iter = fields; iter && *iter; iter++) { + g_strstrip (*iter); + dot = strchr (*iter, '.'); + if (dot) + *dot = '\0'; + + is_all = !dot && strcasecmp (*iter, "all") == 0; + is_common = !dot && strcasecmp (*iter, "common") == 0; + + found = FALSE; + + for (i = 0; nmc_fields_settings_names[i].name; i++) { + if ( is_all || is_common + || !strcasecmp (*iter, nmc_fields_settings_names[i].name)) { + if (dot) + *dot = '.'; + g_string_append (str1, *iter); + g_string_append_c (str1, ','); + found = TRUE; + break; + } } - goto error; + if (found) + continue; + for (i = 0; nmc_fields_con_active_details_groups[i].name; i++) { + if ( is_all || is_common + || !strcasecmp (*iter, nmc_fields_con_active_details_groups[i].name)) { + if (dot) + *dot = '.'; + g_string_append (str2, *iter); + g_string_append_c (str2, ','); + found = TRUE; + break; + } + } + if (!found) { + if (dot) + *dot = '.'; + if (!strcasecmp (*iter, CON_SHOW_DETAIL_GROUP_PROFILE)) + group_profile = TRUE; + else if (!strcasecmp (*iter, CON_SHOW_DETAIL_GROUP_ACTIVE)) + group_active = TRUE; + else { + char *allowed1 = nmc_get_allowed_fields (nmc_fields_settings_names, -1); + char *allowed2 = nmc_get_allowed_fields (nmc_fields_con_active_details_groups, -1); + g_set_error (error, NMCLI_ERROR, 0, _("invalid field '%s'; allowed fields: %s and %s, or %s,%s"), + *iter, allowed1, allowed2, CON_SHOW_DETAIL_GROUP_PROFILE, CON_SHOW_DETAIL_GROUP_ACTIVE); + g_free (allowed1); + g_free (allowed2); + success = FALSE; + break; + } + } + } + if (fields) + g_strfreev (fields); + + /* Handle pseudo groups: profile, active */ + if (success && group_profile) { + if (str1->len > 0) { + g_set_error (error, NMCLI_ERROR, 0, _("'%s' has to be alone"), + CON_SHOW_DETAIL_GROUP_PROFILE); + success = FALSE; + } else + g_string_assign (str1, "all,"); + } + if (success && group_active) { + if (str2->len > 0) { + g_set_error (error, NMCLI_ERROR, 0, _("'%s' has to be alone"), + CON_SHOW_DETAIL_GROUP_ACTIVE); + success = FALSE; + } else + g_string_assign (str2, "all,"); } - /* Get active connections */ + if (success) { + if (str1->len > 0) + g_string_truncate (str1, str1->len - 1); + if (str2->len > 0) + g_string_truncate (str2, str2->len - 1); + *profile_flds = g_string_free (str1, str1->len == 0); + *active_flds = g_string_free (str2, str2->len == 0); + } else { + g_string_free (str1, TRUE); + g_string_free (str2, TRUE); + } + return success; +} + +static NMCResultCode +do_connections_show (NmCli *nmc, gboolean active_only, int argc, char **argv) +{ + GError *err = NULL; + char *profile_flds = NULL, *active_flds = NULL; + + nmc->should_wait = FALSE; nmc->get_client (nmc); - active_cons = nm_client_get_active_connections (nmc->client); + + if (!nm_client_get_manager_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + goto finish; + } if (argc == 0) { char *fields_str; - char *fields_all = NMC_FIELDS_CON_STATUS_ALL; - char *fields_common = NMC_FIELDS_CON_STATUS_COMMON; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + char *fields_all = NMC_FIELDS_CON_SHOW_ALL; + char *fields_common = NMC_FIELDS_CON_SHOW_COMMON; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + GSList *iter; if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) fields_str = fields_common; @@ -924,61 +1325,127 @@ do_connections_status (NmCli *nmc, int argc, char **argv) else fields_str = nmc->required_fields; - nmc->allowed_fields = nmc_fields_con_status + 1 ; - nmc->print_fields.indices = parse_output_fields (fields_str, nmc->allowed_fields, &err1); - if (err1) - goto error; + tmpl = nmc_fields_con_show; + tmpl_len = sizeof (nmc_fields_con_show); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &err); + if (err) { + goto finish; + } + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &err)) + goto finish; + + /* Add headers */ + nmc->print_fields.header_name = active_only ? _("NetworkManager active profiles") : + _("NetworkManager connection profiles"); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + /* Add values */ + for (iter = nmc->system_connections; iter; iter = g_slist_next (iter)) { + NMConnection *con = NM_CONNECTION (iter->data); + fill_output_connection (con, nmc, active_only); + } + print_data (nmc); /* Print all data */ + } else { + gboolean new_line = FALSE; + gboolean without_fields = (nmc->required_fields == NULL); + const GPtrArray *active_cons = nm_client_get_active_connections (nmc->client); + GSList *pos = NULL; - /* Print headers */ - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ADD | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.header_name = _("Active connections"); - print_fields (nmc->print_fields, nmc->allowed_fields); + /* multiline mode is default for 'connection show <ID>' */ + if (!nmc->mode_specified) + nmc->multiline_output = TRUE; + + /* Split required fields into the settings and active ones. */ + if (!split_required_fields_for_con_show (nmc->required_fields, &profile_flds, &active_flds, &err)) + goto finish; + g_free (nmc->required_fields); + nmc->required_fields = NULL; - if (active_cons && active_cons->len) - g_ptr_array_foreach ((GPtrArray *) active_cons, show_active_connection, (gpointer) nmc); - } else { while (argc > 0) { + NMConnection *con; + NMActiveConnection *acon = NULL; + const char *selector = NULL; + if ( strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0 - || strcmp (*argv, "path") == 0) { - const char *selector = *argv; - NMActiveConnection *acon; - + || strcmp (*argv, "path") == 0 + || strcmp (*argv, "apath") == 0) { + selector = *argv; if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return nmc->return_value; + goto finish; } - if (!nmc->mode_specified) - nmc->multiline_output = TRUE; /* multiline mode is default for 'con status id|uuid|path' */ + } - acon = find_active_connection (active_cons, nmc->system_connections, selector, *argv); - if (acon) { - nmc_active_connection_detail (acon, nmc); - } else { - g_string_printf (nmc->return_text, _("Error: '%s' is not an active connection."), *argv); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + /* Find connection by id, uuid, path or apath */ + con = nmc_find_connection (nmc->system_connections, selector, *argv, &pos); + if (!con) { + acon = find_active_connection (active_cons, nmc->system_connections, selector, *argv, NULL); + if (acon) + con = get_connection_for_active (nmc->system_connections, acon); + } + + /* Print connection details */ + if (con) { + gboolean res; + + /* Filter only active connections */ + if (!acon) + acon = get_ac_for_connection (active_cons, con); + if (active_only && !acon) { + next_arg (&argc, &argv); + continue; } - break; + + /* Show an empty line between connections */ + if (new_line) + printf ("\n"); + + /* Show profile configuration */ + if (without_fields || profile_flds) { + nmc->required_fields = profile_flds; + res = nmc_connection_profile_details (con, nmc); + nmc->required_fields = NULL; + if (!res) + goto finish; + } + + /* If the profile is active, print also active details */ + if (without_fields || active_flds) { + if (acon) { + nmc->required_fields = active_flds; + res = nmc_active_connection_details (acon, nmc); + nmc->required_fields = NULL; + if (!res) + goto finish; + } + } + new_line = TRUE; } else { - g_string_printf (nmc->return_text, _("Error: unknown parameter: %s"), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_string_printf (nmc->return_text, _("Error: %s - no such connection profile."), *argv); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto finish; } - - argc--; - argv++; + + /* Take next argument. + * But for pos != NULL we have more connections of the same name, + * so process the same argument again. + */ + if (!pos) + next_arg (&argc, &argv); } } -error: - if (err1) { - if (err1->code == 0) - g_string_printf (nmc->return_text, _("Error: 'con status': %s"), err1->message); - else - g_string_printf (nmc->return_text, _("Error: 'con status': %s; allowed fields: %s"), err1->message, NMC_FIELDS_CON_STATUS_ALL); - g_error_free (err1); +finish: + if (err) { + g_string_printf (nmc->return_text, _("Error: %s."), err->message); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_error_free (err); } + g_free (profile_flds); + g_free (active_flds); return nmc->return_value; } @@ -1199,13 +1666,13 @@ active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, gpoin state = nm_active_connection_get_state (active); if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { - if (nmc->print_output == NMC_PRINT_PRETTY) { + if (nmc->print_output == NMC_PRINT_PRETTY) nmc_terminal_erase_line (); - printf (_("Connection successfully activated (D-Bus active path: %s)\n"), - nm_object_get_path (NM_OBJECT (active))); - } + printf (_("Connection successfully activated (D-Bus active path: %s)\n"), + nm_object_get_path (NM_OBJECT (active))); quit (); - } else if (state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) { + } else if ( state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED + || state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) { g_string_printf (nmc->return_text, _("Error: Connection activation failed.")); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; quit (); @@ -1229,11 +1696,10 @@ vpn_connection_state_cb (NMVPNConnection *vpn, break; case NM_VPN_CONNECTION_STATE_ACTIVATED: - if (nmc->print_output == NMC_PRINT_PRETTY) { + if (nmc->print_output == NMC_PRINT_PRETTY) nmc_terminal_erase_line (); - printf (_("VPN connection successfully activated (D-Bus active path: %s)\n"), - nm_object_get_path (NM_OBJECT (vpn))); - } + printf (_("VPN connection successfully activated (D-Bus active path: %s)\n"), + nm_object_get_path (NM_OBJECT (vpn))); quit (); break; @@ -1250,85 +1716,6 @@ vpn_connection_state_cb (NMVPNConnection *vpn, } } -/* --- VPN state workaround BEGIN --- */ -static NMVPNConnectionState -dbus_get_vpn_state (const char *path) -{ - DBusGConnection *connection; - DBusGProxy *proxy; - GError *error = NULL; - GValue value = {0,}; - NMVPNConnectionState state = NM_VPN_CONNECTION_STATE_UNKNOWN; - - connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL); - proxy = dbus_g_proxy_new_for_name (connection, - NM_DBUS_SERVICE, - path, - "org.freedesktop.DBus.Properties"); - if (!proxy) { - g_warning ("%s: couldn't create D-Bus object proxy.", __func__); - dbus_g_connection_unref (connection); - return NM_VPN_CONNECTION_STATE_UNKNOWN; - } - - /* Get VpnState property for object 'path' through D-Bus Get() call. */ - if (dbus_g_proxy_call (proxy, - "Get", &error, - G_TYPE_STRING, NM_DBUS_INTERFACE_VPN_CONNECTION, - G_TYPE_STRING, "VpnState", - G_TYPE_INVALID, - G_TYPE_VALUE, &value, G_TYPE_INVALID)) { - - state = g_value_get_uint (&value); - } else { - g_warning ("Error in getting active connection 'Vpn' property: (%d) %s", - error->code, error->message); - g_error_free (error); - } - - g_object_unref (proxy); - dbus_g_connection_unref (connection); - - return state; -} - -typedef struct { - NmCli *nmc; - NMVPNConnection *vpn; -} VpnGetStateInfo; - -static gboolean -get_vpn_state_cb (gpointer user_data) -{ - VpnGetStateInfo *info = (VpnGetStateInfo *) user_data; - NmCli *nmc = info->nmc; - NMVPNConnection *vpn = info->vpn; - NMVPNConnectionState state; - - if (!NM_IS_OBJECT (vpn)) { - /* Active connection failed and dissapeared, quit. */ - g_string_printf (nmc->return_text, _("Error: Connection activation failed.")); - nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; - quit (); - g_free (info); - return FALSE; - } - - state = dbus_get_vpn_state (nm_object_get_path (NM_OBJECT (vpn))); - - vpn_connection_state_cb (vpn, state, NM_VPN_CONNECTION_STATE_REASON_UNKNOWN, nmc); - - if ( state == NM_VPN_CONNECTION_STATE_ACTIVATED - || state == NM_VPN_CONNECTION_STATE_FAILED - || state == NM_VPN_CONNECTION_STATE_DISCONNECTED) { - - g_free (info); - return FALSE; - } else - return TRUE; -} -/* --- VPN state workaround END --- */ - static gboolean timeout_cb (gpointer user_data) { @@ -1368,15 +1755,9 @@ progress_vpn_cb (gpointer user_data) NMVPNConnection *vpn = (NMVPNConnection *) user_data; const char *str; - /* VPN state workaround */ -#if 0 str = NM_IS_VPN_CONNECTION (vpn) ? vpn_connection_state_to_string (nm_vpn_connection_get_vpn_state (vpn)) : ""; -#endif - str = NM_IS_OBJECT (vpn) ? - vpn_connection_state_to_string (dbus_get_vpn_state (nm_object_get_path (NM_OBJECT (vpn)))) : - ""; nmc_terminal_show_progress (str); @@ -1388,6 +1769,35 @@ typedef struct { NMDevice *device; } ActivateConnectionInfo; +static gboolean +master_iface_slaves_check (gpointer user_data) +{ + ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data; + NmCli *nmc = info->nmc; + NMDevice *device = info->device; + const GPtrArray *slaves = NULL; + + if (NM_IS_DEVICE_BOND (device)) + slaves = nm_device_bond_get_slaves (NM_DEVICE_BOND (device)); + else if (NM_IS_DEVICE_TEAM (device)) + slaves = nm_device_team_get_slaves (NM_DEVICE_TEAM (device)); + else if (NM_IS_DEVICE_BRIDGE (device)) + slaves = nm_device_bridge_get_slaves (NM_DEVICE_BRIDGE (device)); + else + g_warning ("%s: should not be reached.", __func__); + + if (!slaves) { + g_string_printf (nmc->return_text, + _("Error: Device '%s' is waiting for slaves before proceeding with activation."), + nm_device_get_iface (device)); + nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED; + quit (); + } + + g_free (info); + return FALSE; +} + static void activate_connection_cb (NMClient *client, NMActiveConnection *active, GError *error, gpointer user_data) { @@ -1395,6 +1805,7 @@ activate_connection_cb (NMClient *client, NMActiveConnection *active, GError *er NmCli *nmc = info->nmc; NMDevice *device = info->device; NMActiveConnectionState state; + const GPtrArray *ac_devs; if (error) { g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"), error->message); @@ -1402,18 +1813,23 @@ activate_connection_cb (NMClient *client, NMActiveConnection *active, GError *er quit (); } else { state = nm_active_connection_get_state (active); + if (!device) { + /* device could be NULL for virtual devices. Fill it here. */ + ac_devs = nm_active_connection_get_devices (active); + info->device = device = ac_devs && ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL; + } if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { /* User doesn't want to wait or already activated */ - if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED && nmc->print_output == NMC_PRINT_PRETTY) { + if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + if (nmc->print_output == NMC_PRINT_PRETTY) + nmc_terminal_erase_line (); printf (_("Connection successfully activated (D-Bus active path: %s)\n"), nm_object_get_path (NM_OBJECT (active))); } quit (); } else { if (NM_IS_VPN_CONNECTION (active)) { - VpnGetStateInfo *vpn_info; - /* Monitor VPN state */ g_signal_connect (G_OBJECT (active), "vpn-state-changed", G_CALLBACK (vpn_connection_state_cb), nmc); @@ -1423,19 +1839,6 @@ activate_connection_cb (NMClient *client, NMActiveConnection *active, GError *er g_source_remove (progress_id); progress_id = g_timeout_add (120, progress_vpn_cb, NM_VPN_CONNECTION (active)); } - - /* --- workaround BEGIN --- */ - /* There is a bug in libnm-glib or dbus-glib - 'vpn-state-changed' signal - * is never issued. After inspection it appears that PropertiesChanged and - * VpnStateChanged D-Bus signals are not processed, even if they are seen - * on D-Bus (checked via dbus-monitor). Until the bug is identified and - * fixed, we check the VPN state every second. - */ - vpn_info = g_malloc0 (sizeof (VpnGetStateInfo)); - vpn_info->nmc = nmc; - vpn_info->vpn = NM_VPN_CONNECTION (active); - g_timeout_add_seconds (1, get_vpn_state_cb, vpn_info); - /* --- workaround END --- */ } else { g_signal_connect (active, "notify::state", G_CALLBACK (active_connection_state_cb), nmc); @@ -1449,61 +1852,150 @@ activate_connection_cb (NMClient *client, NMActiveConnection *active, GError *er /* Start timer not to loop forever when signals are not emitted */ g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc); + + /* Check for bond or team or bridge slaves */ + if ( NM_IS_DEVICE_BOND (device) + || NM_IS_DEVICE_TEAM (device) + || NM_IS_DEVICE_BRIDGE (device)) { + g_timeout_add_seconds (SLAVES_UP_TIMEOUT, master_iface_slaves_check, info); + return; /* info will be freed in master_iface_slaves_check () */ + } } } g_free (info); } -static NMCResultCode -do_connection_up (NmCli *nmc, int argc, char **argv) +/* We were using nm_connection_get_virtual_iface_name() to determine whether the + * connection is virtual or not. But it did't work for VLANs without + * vlan.interface-name. nm_connection_get_virtual_iface_name() returns NULL for those. + * So we need to use our own implementation for now. + */ +static gboolean +is_connection_virtual (NMConnection *connection) +{ + if ( nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME) + || nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME) + || nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME) + || nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) + return TRUE; + if (nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME)) { + NMSettingInfiniband *s_infi = nm_connection_get_setting_infiniband (connection); + int p_key = nm_setting_infiniband_get_p_key (s_infi); + const char *parent = nm_setting_infiniband_get_parent (s_infi); + + if (p_key != -1 && parent) + return TRUE; + } + return FALSE; +} + +static gboolean +nmc_activate_connection (NmCli *nmc, + NMConnection *connection, + const char *ifname, + const char *ap, + const char *nsp, + NMClientActivateFn callback, + GError **error) { ActivateConnectionInfo *info; NMDevice *device = NULL; const char *spec_object = NULL; gboolean device_found; + GError *local = NULL; + + g_return_val_if_fail (nmc != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (connection) { + device_found = find_device_for_connection (nmc, connection, ifname, ap, nsp, &device, &spec_object, &local); + + /* Virtual connection may not have their interfaces created yet */ + if (!device_found && !is_connection_virtual (connection)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_CON_ACTIVATION, + "%s", local && local->message ? local->message : _("unknown error")); + g_clear_error (&local); + return FALSE; + } + } else if (ifname) { + device = nm_client_get_device_by_iface (nmc->client, ifname); + if (!device) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND, + _("unknown device '%s'."), ifname); + return FALSE; + } + } else { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND, + _("neither a valid connection nor device given")); + return FALSE; + } + + info = g_malloc0 (sizeof (ActivateConnectionInfo)); + info->nmc = nmc; + info->device = device; + + nm_client_activate_connection (nmc->client, + connection, + device, + spec_object, + callback, + info); + return TRUE; +} + +static NMCResultCode +do_connection_up (NmCli *nmc, int argc, char **argv) +{ NMConnection *connection = NULL; - NMSettingConnection *s_con; - const char *con_type; - const char *iface = NULL; + const char *ifname = NULL; const char *ap = NULL; const char *nsp = NULL; - gboolean id_specified = FALSE; - gboolean wait = TRUE; GError *error = NULL; - gboolean is_virtual = FALSE; + const char *selector = NULL; + const char *name = NULL; + char *line = NULL; - /* Set default timeout for connection activation. It can take quite a long time. - * Using 90 seconds. + /* + * Set default timeout for connection activation. + * Activation can take quite a long time, use 90 seconds. */ - nmc->timeout = 90; + if (nmc->timeout == -1) + nmc->timeout = 90; - while (argc > 0) { - if (strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0) { - const char *selector = *argv; - id_specified = TRUE; + if (argc == 0) { + if (nmc->ask) { + line = nmc_readline (PROMPT_CONNECTION); + name = line ? line : ""; + } + } else if (strcmp (*argv, "ifname") != 0) { + if ( strcmp (*argv, "id") == 0 + || strcmp (*argv, "uuid") == 0 + || strcmp (*argv, "path") == 0) { + selector = *argv; if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } + name = *argv; + } + name = *argv; + next_arg (&argc, &argv); + } - connection = find_connection (nmc->system_connections, selector, *argv); + if (name) + connection = nmc_find_connection (nmc->system_connections, selector, name, NULL); - if (!connection) { - g_string_printf (nmc->return_text, _("Error: Unknown connection: %s."), *argv); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - goto error; - } - } - else if (strcmp (*argv, "iface") == 0) { + while (argc > 0) { + if (strcmp (*argv, "ifname") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } - iface = *argv; + ifname = *argv; } else if (strcmp (*argv, "ap") == 0) { if (next_arg (&argc, &argv) != 0) { @@ -1525,23 +2017,7 @@ do_connection_up (NmCli *nmc, int argc, char **argv) nsp = *argv; } #endif - else if (strcmp (*argv, "--nowait") == 0) { - wait = FALSE; - } else if (strcmp (*argv, "--timeout") == 0) { - if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } - - errno = 0; - nmc->timeout = strtol (*argv, NULL, 10); - if (errno || nmc->timeout < 0) { - g_string_printf (nmc->return_text, _("Error: timeout value '%s' is not valid."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } - } else { + else { fprintf (stderr, _("Unknown parameter: %s\n"), *argv); } @@ -1549,292 +2025,6591 @@ do_connection_up (NmCli *nmc, int argc, char **argv) argv++; } - if (!id_specified) { - g_string_printf (nmc->return_text, _("Error: id or uuid has to be specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + /* create NMClient */ + nmc->get_client (nmc); + + if (!nm_client_get_manager_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; goto error; } - if (!nmc_is_nm_running (nmc, &error)) { - if (error) { - g_string_printf (nmc->return_text, _("Error: Can't find out if NetworkManager is running: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - g_error_free (error); - } else { - g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); - nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; - } + /* Use nowait_flag instead of should_wait because exiting has to be postponed till + * active_connection_state_cb() is called. That gives NM time to check our permissions + * and we can follow activation progress. + */ + nmc->nowait_flag = (nmc->timeout == 0); + nmc->should_wait = TRUE; + + if (!nmc_activate_connection (nmc, connection, ifname, ap, nsp, activate_connection_cb, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), + error ? error->message : _("unknown error")); + nmc->return_value = error ? error->code : NMC_RESULT_ERROR_CON_ACTIVATION; + g_clear_error (&error); goto error; } + /* Start progress indication */ + if (nmc->print_output == NMC_PRINT_PRETTY) + progress_id = g_timeout_add (120, progress_cb, _("preparing")); + + g_free (line); + return nmc->return_value; +error: + nmc->should_wait = FALSE; + g_free (line); + return nmc->return_value; +} + +static NMCResultCode +do_connection_down (NmCli *nmc, int argc, char **argv) +{ + NMActiveConnection *active; + const GPtrArray *active_cons; + char *line = NULL; + char **arg_arr = NULL; + char **arg_ptr = argv; + int arg_num = argc; + int idx = 0; + + if (argc == 0) { + if (nmc->ask) { + line = nmc_readline (PROMPT_CONNECTION); + nmc_string_to_arg_array (line, "", &arg_arr, &arg_num); + arg_ptr = arg_arr; + } + if (arg_num == 0) { + g_string_printf (nmc->return_text, _("Error: No connection specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } + /* create NMClient */ nmc->get_client (nmc); + if (!nm_client_get_manager_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + goto error; + } + + /* Get active connections */ + active_cons = nm_client_get_active_connections (nmc->client); + while (arg_num > 0) { + const char *selector = NULL; + + if ( strcmp (*arg_ptr, "id") == 0 + || strcmp (*arg_ptr, "uuid") == 0 + || strcmp (*arg_ptr, "path") == 0 + || strcmp (*arg_ptr, "apath") == 0) { + + selector = *arg_ptr; + if (next_arg (&arg_num, &arg_ptr) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } + + active = find_active_connection (active_cons, nmc->system_connections, selector, *arg_ptr, &idx); + if (active) { + nm_client_deactivate_connection (nmc->client, active); + } else { + g_string_printf (nmc->return_text, _("Error: '%s' is not an active connection."), *arg_ptr); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + + if (idx == 0) + next_arg (&arg_num, &arg_ptr); + } + + // FIXME: do something better then sleep() + /* Don't quit immediatelly and give NM time to check our permissions */ + sleep (1); + +error: + nmc->should_wait = FALSE; + g_strfreev (arg_arr); + return nmc->return_value; +} + +/*----------------------------------------------------------------------------*/ + +typedef struct NameItem { + const char *name; + const char *alias; + const struct NameItem *settings; + gboolean mandatory; +} NameItem; + +static const NameItem nmc_generic_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_ethernet_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE }, + { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_DCB_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_infiniband_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_INFINIBAND_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_wifi_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRELESS_SETTING_NAME, "wifi", NULL, TRUE }, + { NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, "wifi-sec", NULL, FALSE }, + { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_wimax_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIMAX_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_gsm_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_GSM_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_SERIAL_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_cdma_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_CDMA_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_SERIAL_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_mobile_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_SERIAL_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_PPP_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_GSM_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_CDMA_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_bluetooth_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_BLUETOOTH_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_adsl_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_ADSL_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +/* PPPoE is a base connection type from historical reasons. + * See libnm-util/nm-setting.c:_nm_setting_is_base_type() + */ +static const NameItem nmc_pppoe_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE }, + { NM_SETTING_PPPOE_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_PPP_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_olpc_mesh_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_OLPC_MESH_SETTING_NAME, "olpc-mesh", NULL, TRUE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_vpn_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_VPN_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_vlan_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE }, + { NM_SETTING_VLAN_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_bond_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_BOND_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_team_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_TEAM_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_bridge_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_BRIDGE_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_bond_slave_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE }, + { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_team_slave_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE }, + { NM_SETTING_TEAM_PORT_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + +static const NameItem nmc_bridge_slave_settings [] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_BRIDGE_PORT_SETTING_NAME, NULL, NULL, TRUE }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE }, + { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, + { NULL, NULL, NULL, FALSE } +}; + + +/* Available connection types */ +static const NameItem nmc_valid_connection_types[] = { + { NM_SETTING_GENERIC_SETTING_NAME, NULL, nmc_generic_settings }, + { NM_SETTING_WIRED_SETTING_NAME, "ethernet", nmc_ethernet_settings }, + { NM_SETTING_PPPOE_SETTING_NAME, NULL, nmc_pppoe_settings }, + { NM_SETTING_WIRELESS_SETTING_NAME, "wifi", nmc_wifi_settings }, + { NM_SETTING_WIMAX_SETTING_NAME, NULL, nmc_wimax_settings }, + { NM_SETTING_GSM_SETTING_NAME, NULL, nmc_gsm_settings }, + { NM_SETTING_CDMA_SETTING_NAME, NULL, nmc_cdma_settings }, + { NM_SETTING_INFINIBAND_SETTING_NAME, NULL, nmc_infiniband_settings }, + { NM_SETTING_ADSL_SETTING_NAME, NULL, nmc_adsl_settings }, + { NM_SETTING_BLUETOOTH_SETTING_NAME, NULL, nmc_bluetooth_settings }, + { NM_SETTING_VPN_SETTING_NAME, NULL, nmc_vpn_settings }, + { NM_SETTING_OLPC_MESH_SETTING_NAME, "olpc-mesh", nmc_olpc_mesh_settings }, + { NM_SETTING_VLAN_SETTING_NAME, NULL, nmc_vlan_settings }, + { NM_SETTING_BOND_SETTING_NAME, NULL, nmc_bond_settings }, + { NM_SETTING_TEAM_SETTING_NAME, NULL, nmc_team_settings }, + { NM_SETTING_BRIDGE_SETTING_NAME, NULL, nmc_bridge_settings }, + { "bond-slave", NULL, nmc_bond_slave_settings }, + { "team-slave", NULL, nmc_team_slave_settings }, + { "bridge-slave", NULL, nmc_bridge_slave_settings }, + { NULL, NULL, NULL } +}; + +/* + * Return an alias for the 'name' if exists, else return the 'name'. + * The returned string must not be freed. + */ +static const char * +get_name_alias (const char *name, const NameItem array[]) +{ + const NameItem *iter = &array[0]; + + if (!name) + return NULL; + + while (iter && iter->name) { + if (!strcmp (name, iter->name)) { + if (iter->alias) + return iter->alias; + else + return iter->name; + } + iter++; + } + return name; +} + +/* + * Construct a string with names and aliases from the array formatted as: + * "name (alias), name, name (alias), name, name" + * + * Returns: string; the caller is responsible for freeing it. + */ +static char * +get_valid_options_string (const NameItem array[]) +{ + const NameItem *iter = &array[0]; + GString *str; + + str = g_string_sized_new (150); + while (iter && iter->name) { + if (str->len) + g_string_append (str, ", "); + if (iter->alias) + g_string_append_printf (str, "%s (%s)", iter->name, iter->alias); + else + g_string_append (str, iter->name); + iter++; + } + return g_string_free (str, FALSE); +} + +/* + * Check if 'val' is valid string in either array->name or array->alias. + * It accepts shorter string provided they are not ambiguous. + * 'val' == NULL doesn't hurt. + * + * Returns: pointer to array->name string or NULL on failure. + * The returned string must not be freed. + */ +static const char * +check_valid_name (const char *val, const NameItem array[], GError **error) +{ + const NameItem *iter; + GPtrArray *tmp_arr; + const char *str; + GError *tmp_err = NULL; + + /* Create a temporary array that can be used in nmc_string_is_valid() */ + tmp_arr = g_ptr_array_sized_new (30); + iter = &array[0]; + while (iter && iter->name) { + g_ptr_array_add (tmp_arr, (gpointer) iter->name); + if (iter->alias) + g_ptr_array_add (tmp_arr, (gpointer) iter->alias); + iter++; + } + g_ptr_array_add (tmp_arr, (gpointer) NULL); + + /* Check string validity */ + str = nmc_string_is_valid (val, (const char **) tmp_arr->pdata, &tmp_err); + if (!str) { + if (tmp_err->code == 1) + g_propagate_error (error, tmp_err); + else { + /* We want to handle aliases, so construct own error message */ + char *err_str = get_valid_options_string (array); + g_set_error (error, 1, 0, _("'%s' not among [%s]"), + val ? val : "", err_str); + g_free (err_str); + g_clear_error (&tmp_err); + } + g_ptr_array_free (tmp_arr, TRUE); + return NULL; + } + + /* Return a pointer to the found string in passed 'array' */ + iter = &array[0]; + while (iter && iter->name) { + if ( (iter->name && g_strcmp0 (iter->name, str) == 0) + || (iter->alias && g_strcmp0 (iter->alias, str) == 0)) { + g_ptr_array_free (tmp_arr, TRUE); + return iter->name; + } + iter++; + } + /* We should not really come here */ + g_ptr_array_free (tmp_arr, TRUE); + g_set_error (error, 1, 0, _("Unknown error")); + return NULL; +} + +static const NameItem * +get_valid_settings_array (const char *con_type) +{ + guint i, num; + + if (!con_type) + return NULL; + + num = G_N_ELEMENTS (nmc_valid_connection_types); + for (i = 0; i < num; i++) { + if (!g_strcmp0 (con_type, nmc_valid_connection_types[i].name)) + return nmc_valid_connection_types[i].settings; + } + return NULL; +} + +static gboolean +is_setting_mandatory (NMConnection *connection, NMSetting *setting) +{ + NMSettingConnection *s_con; + const char *c_type; + const NameItem *item; + const char *name; + s_con = nm_connection_get_setting_connection (connection); g_assert (s_con); - con_type = nm_setting_connection_get_connection_type (s_con); + c_type = nm_setting_connection_get_connection_type (s_con); - if ( nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME) - || nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME) - || nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) - is_virtual = TRUE; + name = nm_setting_get_name (setting); + + item = get_valid_settings_array (c_type); + while (item && item->name) { + if (!strcmp (name, item->name)) + return item->mandatory; + item++; + } + return FALSE; +} + +/*----------------------------------------------------------------------------*/ + +static gboolean +check_and_convert_mac (const char *mac, + GByteArray **mac_array, + int type, + const char *keyword, + GError **error) +{ + GByteArray *local_mac_array = NULL; + g_return_val_if_fail (mac_array == NULL || *mac_array == NULL, FALSE); + + if (!mac) + return TRUE; + + local_mac_array = nm_utils_hwaddr_atoba (mac, type); + if (!local_mac_array) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s': '%s' is not a valid %s MAC address."), + keyword, mac, type == ARPHRD_INFINIBAND ? _("InfiniBand") : _("Ethernet")); + return FALSE; + } + + if (mac_array) + *mac_array = local_mac_array; + else + if (local_mac_array) + g_byte_array_free (local_mac_array, TRUE); + + return TRUE; +} + +static gboolean +check_and_convert_mtu (const char *mtu, guint32 *mtu_int, GError **error) +{ + unsigned long local_mtu_int; + + if (!mtu) + return TRUE; + + if (!nmc_string_to_uint (mtu, TRUE, 0, G_MAXUINT32, &local_mtu_int)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'mtu': '%s' is not a valid MTU."), mtu); + return FALSE; + } + if (mtu_int) + *mtu_int = (guint32) local_mtu_int; + return TRUE; +} + +static gboolean +check_infiniband_parent (const char *parent, GError **error) +{ + if (!parent) + return TRUE; + + if (!nm_utils_iface_valid_name (parent)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'parent': '%s' is not a valid interface name."), parent); + return FALSE; + } + return TRUE; +} + + +static gboolean +check_infiniband_p_key (const char *p_key, guint32 *p_key_int, GError **error) +{ + unsigned long local_p_key_int; + gboolean p_key_valid = FALSE; + if (!p_key) + return TRUE; + + if (!strncmp (p_key, "0x", 2)) + p_key_valid = nmc_string_to_uint_base (p_key + 2, 16, TRUE, 0, G_MAXUINT16, &local_p_key_int); + else + p_key_valid = nmc_string_to_uint (p_key, TRUE, 0, G_MAXUINT16, &local_p_key_int); + if (!p_key_valid) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'p-key': '%s' is not a valid InfiniBand P_KEY."), p_key); + return FALSE; + } + if (p_key_int) + *p_key_int = (guint32) local_p_key_int; + return TRUE; +} + +/* Checks InfiniBand mode. + * It accepts shortcuts and normalizes them ('mode' argument is modified on success). + */ +static gboolean +check_infiniband_mode (char **mode, GError **error) +{ + char *tmp; + const char *checked_mode; + const char *modes[] = { "datagram", "connected", NULL }; + + if (!mode || !*mode) + return TRUE; + + tmp = g_strstrip (g_strdup (*mode)); + checked_mode = nmc_string_is_valid (tmp, modes, NULL); + g_free (tmp); + if (checked_mode) { + g_free (*mode); + *mode = g_strdup (checked_mode); + } else + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'mode': '%s' is not a valid InfiniBand transport mode [datagram, connected]."), *mode); + return !!checked_mode; +} + +static gboolean +check_and_convert_vlan_flags (const char *flags, guint32 *flags_int, GError **error) +{ + unsigned long local_flags_int; + + if (!flags) + return TRUE; + + if (!nmc_string_to_uint (flags, TRUE, 0, 7, &local_flags_int)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'flags': '%s' is not valid; use <0-7>."), flags); + return FALSE; + } + if (flags_int) + *flags_int = (guint32) local_flags_int; + return TRUE; +} + +static gboolean +check_and_convert_vlan_prio_maps (const char *prio_map, + NMVlanPriorityMap type, + char ***prio_map_arr, + GError **error) +{ + char **local_prio_map_arr; + GError *local_err = NULL; + + if (!prio_map) + return TRUE; + + if (!(local_prio_map_arr = nmc_vlan_parse_priority_maps (prio_map, type, &local_err))) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s': '%s' is not valid; %s "), + type == NM_VLAN_INGRESS_MAP ? "ingress" : "egress", + prio_map, local_err->message); + return FALSE; + } + + if (prio_map_arr) + *prio_map_arr = local_prio_map_arr; + return TRUE; +} + +static gboolean +add_ip4_address_to_connection (NMIP4Address *ip4addr, NMConnection *connection) +{ + NMSettingIP4Config *s_ip4; + gboolean ret; + + if (!ip4addr) + return TRUE; + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + if (!s_ip4) { + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + g_object_set (s_ip4, + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NULL); + } + ret = nm_setting_ip4_config_add_address (s_ip4, ip4addr); + nm_ip4_address_unref (ip4addr); + + return ret; +} + +static gboolean +add_ip6_address_to_connection (NMIP6Address *ip6addr, NMConnection *connection) +{ + NMSettingIP6Config *s_ip6; + gboolean ret; + + if (!ip6addr) + return TRUE; + + s_ip6 = nm_connection_get_setting_ip6_config (connection); + if (!s_ip6) { + s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + NULL); + } + ret = nm_setting_ip6_config_add_address (s_ip6, ip6addr); + nm_ip6_address_unref (ip6addr); + + return ret; +} + +static char * +unique_master_iface_ifname (GSList *list, + const char *type, + const char *ifname_property, + const char *try_name) +{ + NMConnection *connection; + NMSetting *setting; + char *new_name; + unsigned int num = 1; + GSList *iterator = list; + char *ifname_val = NULL; + + new_name = g_strdup (try_name); + while (iterator) { + connection = NM_CONNECTION (iterator->data); + setting = nm_connection_get_setting_by_name (connection, type); + if (!setting) { + iterator = g_slist_next (iterator); + continue; + } + + g_object_get (setting, ifname_property, &ifname_val, NULL); + if (g_strcmp0 (new_name, ifname_val) == 0) { + g_free (new_name); + new_name = g_strdup_printf ("%s%d", try_name, num++); + iterator = list; + } else + iterator = g_slist_next (iterator); + g_free (ifname_val); + } + return new_name; +} + +static const char * +_strip_master_prefix (const char *master, const char *(**func)(NMConnection *)) +{ + if (!master) + return NULL; + + if (g_str_has_prefix (master, "ifname/")) { + master = master + strlen ("ifname/"); + if (func) + *func = nm_connection_get_virtual_iface_name; + } else if (g_str_has_prefix (master, "uuid/")) { + master = master + strlen ("uuid/"); + if (func) + *func = nm_connection_get_uuid; + } else if (g_str_has_prefix (master, "id/")) { + master = master + strlen ("id/"); + if (func) + *func = nm_connection_get_id; + } + return master; +} + +/* verify_master_for_slave: + * @connections: list af all connections + * @master: UUID, ifname or ID of the master connection + * @type: virtual connection type (bond, team, bridge, ...) + * + * Check whether master is a valid interface name, UUID or ID of some @type connection. + * First UUID and ifname are checked. If they don't match, ID is checked + * and replaced by UUID on a match. + * + * Returns: identifier of master connection if found, %NULL otherwise + */ +static const char * +verify_master_for_slave (GSList *connections, + const char *master, + const char *type) +{ + NMConnection *connection; + NMSettingConnection *s_con; + const char *con_type, *id, *uuid, *ifname; + GSList *iterator = connections; + const char *found_by_id = NULL; + const char *out_master = NULL; + const char *(*func) (NMConnection *) = NULL; + + if (!master) + return NULL; + + master = _strip_master_prefix (master, &func); + while (iterator) { + connection = NM_CONNECTION (iterator->data); + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + con_type = nm_setting_connection_get_connection_type (s_con); + if (g_strcmp0 (con_type, type) != 0) { + iterator = g_slist_next (iterator); + continue; + } + if (func) { + /* There was a prefix; only compare to that type. */ + if (g_strcmp0 (master, func (connection)) == 0) { + if (func == nm_connection_get_id) + out_master = nm_connection_get_uuid (connection); + else + out_master = master; + break; + } + } else { + id = nm_connection_get_id (connection); + uuid = nm_connection_get_uuid (connection); + ifname = nm_connection_get_virtual_iface_name (connection); + if ( g_strcmp0 (master, uuid) == 0 + || g_strcmp0 (master, ifname) == 0) { + out_master = master; + break; + } + if (!found_by_id && g_strcmp0 (master, id) == 0) + found_by_id = uuid; + } + + iterator = g_slist_next (iterator); + } + return out_master ? out_master : found_by_id; +} + +static gboolean +bridge_prop_string_to_uint (const char *str, + const char *nmc_arg, + GType bridge_type, + const char *propname, + unsigned long *out_val, + GError **error) +{ + GParamSpecUInt *pspec; + + pspec = (GParamSpecUInt *) g_object_class_find_property (g_type_class_peek (bridge_type), + propname); + g_assert (G_IS_PARAM_SPEC_UINT (pspec)); + + if (!nmc_string_to_uint (str, TRUE, pspec->minimum, pspec->maximum, out_val)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s': '%s' is not valid; use <%u-%u>."), + nmc_arg, str, pspec->minimum, pspec->maximum); + return FALSE; + } + return TRUE; +} + +#define WORD_YES "yes" +#define WORD_NO "no" +#define WORD_LOC_YES _("yes") +#define WORD_LOC_NO _("no") +static const char * +prompt_yes_no (gboolean default_yes, char *delim) +{ + static char prompt[128] = { 0 }; + + if (!delim) + delim = ""; + + snprintf (prompt, sizeof (prompt), "(%s/%s) [%s]%s ", + WORD_LOC_YES, WORD_LOC_NO, + default_yes ? WORD_LOC_YES : WORD_LOC_NO, delim); + + return prompt; +} + +static gboolean +normalize_yes_no (char **yes_no) +{ + char *tmp; + const char *checked_yes_no; + const char *strv[] = { WORD_LOC_YES, WORD_LOC_NO, NULL }; + + if (!yes_no || !*yes_no) + return FALSE; + + tmp = g_strstrip (g_strdup (*yes_no)); + checked_yes_no = nmc_string_is_valid (tmp, strv, NULL); + g_free (tmp); + if (g_strcmp0 (checked_yes_no, WORD_LOC_YES) == 0) { + g_free (*yes_no); + *yes_no = g_strdup (WORD_YES); + } else if (g_strcmp0 (checked_yes_no, WORD_LOC_NO) == 0) { + g_free (*yes_no); + *yes_no = g_strdup (WORD_NO); + } + return !!checked_yes_no; +} + +static gboolean +want_provide_opt_args (const char *type, int num) +{ + char *answer; + gboolean ret = TRUE; + + /* Ask for optional arguments. */ + printf (ngettext ("There is %d optional argument for '%s' connection type.\n", + "There are %d optional arguments for '%s' connection type.\n", num), + num, type); + answer = nmc_readline (ngettext ("Do you want to provide it? %s", + "Do you want to provide them? %s", num), + prompt_yes_no (TRUE, NULL)); + answer = answer ? g_strstrip (answer) : NULL; + if (answer && matches (answer, WORD_LOC_YES) != 0) + ret = FALSE; + g_free (answer); + return ret; +} + +static void +do_questionnaire_ethernet (gboolean ethernet, char **mtu, char **mac, char **cloned_mac) +{ + gboolean once_more; + GError *error = NULL; + const char *type = ethernet ? _("ethernet") : _("Wi-Fi"); + + /* Ask for optional arguments */ + if (!want_provide_opt_args (type, 3)) + return; + + if (!*mtu) { + do { + *mtu = nmc_readline (_("MTU [auto]: ")); + once_more = !check_and_convert_mtu (*mtu, NULL, &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*mtu); + } + } while (once_more); + } + if (!*mac) { + do { + *mac = nmc_readline (_("MAC [none]: ")); + once_more = !check_and_convert_mac (*mac, NULL, ARPHRD_ETHER, "mac", &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*mac); + } + } while (once_more); + } + if (!*cloned_mac) { + do { + *cloned_mac = nmc_readline (_("Cloned MAC [none]: ")); + once_more = !check_and_convert_mac (*cloned_mac, NULL, ARPHRD_ETHER, "cloned-mac", &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*cloned_mac); + } + } while (once_more); + } +} + +#define WORD_DATAGRAM "datagram" +#define WORD_CONNECTED "connected" +#define PROMPT_IB_MODE "(" WORD_DATAGRAM "/" WORD_CONNECTED ") [" WORD_DATAGRAM "]: " +static void +do_questionnaire_infiniband (char **mtu, char **mac, char **mode, char **parent, char **p_key) +{ + gboolean once_more; + GError *error = NULL; + + /* Ask for optional arguments */ + if (!want_provide_opt_args (_("InfiniBand"), 5)) + return; + + if (!*mtu) { + do { + *mtu = nmc_readline (_("MTU [auto]: ")); + once_more = !check_and_convert_mtu (*mtu, NULL, &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*mtu); + } + } while (once_more); + } + if (!*mac) { + do { + *mac = nmc_readline (_("MAC [none]: ")); + once_more = !check_and_convert_mac (*mac, NULL, ARPHRD_INFINIBAND, "mac", &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*mac); + } + } while (once_more); + } + if (!*mode) { + do { + *mode = nmc_readline (_("Transport mode %s"), PROMPT_IB_MODE); + if (!*mode) + *mode = g_strdup ("datagram"); + once_more = !check_infiniband_mode (mode, &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*mode); + } + } while (once_more); + } + if (!*parent) { + do { + *parent = nmc_readline (_("Parent interface [none]: ")); + once_more = !check_infiniband_parent (*parent, &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*parent); + } + } while (once_more); + } + if (!*p_key) { + do { + *p_key = nmc_readline (_("P_KEY [none]: ")); + once_more = !check_infiniband_p_key (*p_key, NULL, &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*p_key); + } + /* If parent is specified, so has to be P_KEY */ + if (!once_more && *parent && !*p_key) { + once_more = TRUE; + printf (_("Error: 'p-key' is mandatory when 'parent' is specified.\n")); + } + } while (once_more); + } +} + +static void +do_questionnaire_wifi (char **mtu, char **mac, char **cloned_mac) +{ + /* At present, the optional Wi-Fi arguments are the same as for ethernet. */ + return do_questionnaire_ethernet (FALSE, mtu, mac, cloned_mac); +} + +static void +do_questionnaire_wimax (char **mac) +{ + gboolean once_more; + GError *error = NULL; + + /* Ask for optional 'wimax' arguments. */ + if (!want_provide_opt_args (_("WiMAX"), 1)) + return; + + if (!*mac) { + do { + *mac = nmc_readline (_("MAC [none]: ")); + once_more = !check_and_convert_mac (*mac, NULL, ARPHRD_ETHER, "mac", &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*mac); + } + } while (once_more); + } +} + +static void +do_questionnaire_pppoe (char **password, char **service, char **mtu, char **mac) +{ + gboolean once_more; + GError *error = NULL; + + /* Ask for optional 'pppoe' arguments. */ + if (!want_provide_opt_args (_("PPPoE"), 4)) + return; + + if (!*password) + *password = nmc_readline (_("Password [none]: ")); + if (!*service) + *service = nmc_readline (_("Service [none]: ")); + + if (!*mtu) { + do { + *mtu = nmc_readline (_("MTU [auto]: ")); + once_more = !check_and_convert_mtu (*mtu, NULL, &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*mtu); + } + } while (once_more); + } + if (!*mac) { + do { + *mac = nmc_readline (_("MAC [none]: ")); + once_more = !check_and_convert_mac (*mac, NULL, ARPHRD_ETHER, "mac", &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*mac); + } + } while (once_more); + } +} + +static void +do_questionnaire_mobile (char **user, char **password) +{ + /* Ask for optional 'gsm' or 'cdma' arguments. */ + if (!want_provide_opt_args (_("mobile broadband"), 2)) + return; + + if (!*user) + *user = nmc_readline (_("Username [none]: ")); + if (!*password) + *password = nmc_readline (_("Password [none]: ")); +} + +#define WORD_PANU "panu" +#define WORD_DUN_GSM "dun-gsm" +#define WORD_DUN_CDMA "dun-cdma" +#define PROMPT_BT_TYPE "(" WORD_PANU "/" WORD_DUN_GSM "/" WORD_DUN_CDMA ") [" WORD_PANU "]: " +static void +do_questionnaire_bluetooth (char **bt_type) +{ + gboolean once_more; + + /* Ask for optional 'bluetooth' arguments. */ + if (!want_provide_opt_args (_("bluetooth"), 1)) + return; + + if (!*bt_type) { + const char *types[] = { "dun", "dun-gsm", "dun-cdma", "panu", NULL }; + const char *tmp; + do { + *bt_type = nmc_readline (_("Bluetooth type %s"), PROMPT_BT_TYPE); + if (!*bt_type) + *bt_type = g_strdup ("panu"); + tmp = nmc_string_is_valid (*bt_type, types, NULL); + once_more = !tmp; + if (once_more) { + printf (_("Error: 'bt-type': '%s' is not a valid bluetooth type.\n"), *bt_type); + g_free (*bt_type); + } + } while (once_more); + g_free (*bt_type); + *bt_type = g_strdup (tmp); + } +} + +static void +do_questionnaire_vlan (char **mtu, char **flags, char **ingress, char **egress) +{ + gboolean once_more; + GError *error = NULL; + + /* Ask for optional 'vlan' arguments. */ + if (!want_provide_opt_args (_("VLAN"), 4)) + return; + + if (!*mtu) { + do { + *mtu = nmc_readline (_("MTU [auto]: ")); + once_more = !check_and_convert_mtu (*mtu, NULL, &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*mtu); + } + } while (once_more); + } + if (!*flags) { + do { + *flags = nmc_readline (_("VLAN flags (<0-7>) [none]: ")); + once_more = !check_and_convert_vlan_flags (*flags, NULL, &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*flags); + } + } while (once_more); + } + if (!*ingress) { + do { + *ingress = nmc_readline (_("Ingress priority maps [none]: ")); + once_more = !check_and_convert_vlan_prio_maps (*ingress, NM_VLAN_INGRESS_MAP, NULL, &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*ingress); + } + } while (once_more); + } + if (!*egress) { + do { + *egress = nmc_readline (_("Egress priority maps [none]: ")); + once_more = !check_and_convert_vlan_prio_maps (*egress, NM_VLAN_EGRESS_MAP, NULL, &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*egress); + } + } while (once_more); + } +} + +#define PROMPT_BOND_MODE _("Bonding mode [balance-rr]: ") +#define WORD_MIIMON "miimon" +#define WORD_ARP "arp" +#define PROMPT_BOND_MON_MODE "(" WORD_MIIMON "/" WORD_ARP ") [" WORD_MIIMON "]: " +static void +do_questionnaire_bond (char **mode, char **primary, char **miimon, + char **downdelay, char **updelay, + char **arpinterval, char **arpiptarget) +{ + char *monitor_mode; + unsigned long tmp; + gboolean once_more; + GError *error = NULL; + + /* Ask for optional 'bond' arguments. */ + if (!want_provide_opt_args (_("bond"), 7)) + return; + + if (!*mode) { + const char *mode_tmp; + do { + *mode = nmc_readline (PROMPT_BOND_MODE); + if (!*mode) + *mode = g_strdup ("balance-rr"); + mode_tmp = nmc_bond_validate_mode (*mode, &error); + g_free (*mode); + if (mode_tmp) { + *mode = g_strdup (mode_tmp); + } else { + printf ("%s\n", error->message); + g_clear_error (&error); + } + } while (!mode_tmp); + } + + if (g_strcmp0 (*mode, "active-backup") == 0 && !*primary) { + do { + *primary = nmc_readline (_("Bonding primary interface [none]: ")); + once_more = *primary && !nm_utils_iface_valid_name (*primary); + if (once_more) { + printf (_("Error: 'primary': '%s' is not a valid interface name.\n"), + *primary); + g_free (*primary); + } + } while (once_more); + } + + do { + monitor_mode = nmc_readline (_("Bonding monitoring mode %s"), PROMPT_BOND_MON_MODE); + if (!monitor_mode) + monitor_mode = g_strdup (WORD_MIIMON); + g_strstrip (monitor_mode); + once_more = matches (monitor_mode, WORD_MIIMON) != 0 && matches (monitor_mode, WORD_ARP) != 0; + if (once_more) { + printf (_("Error: '%s' is not a valid monitoring mode; use '%s' or '%s'.\n"), + monitor_mode, WORD_MIIMON, WORD_ARP); + g_free (monitor_mode); + } + } while (once_more); + + if (matches (monitor_mode, WORD_MIIMON) == 0) { + if (!*miimon) { + do { + *miimon = nmc_readline (_("Bonding miimon [100]: ")); + once_more = *miimon && !nmc_string_to_uint (*miimon, TRUE, 0, G_MAXUINT32, &tmp); + if (once_more) { + printf (_("Error: 'miimon': '%s' is not a valid number <0-%u>.\n"), + *miimon, G_MAXUINT32); + g_free (*miimon); + } + } while (once_more); + } + if (!*downdelay) { + do { + *downdelay = nmc_readline (_("Bonding downdelay [0]: ")); + once_more = *downdelay && !nmc_string_to_uint (*downdelay, TRUE, 0, G_MAXUINT32, &tmp); + if (once_more) { + printf (_("Error: 'downdelay': '%s' is not a valid number <0-%u>.\n"), + *downdelay, G_MAXUINT32); + g_free (*downdelay); + } + } while (once_more); + } + if (!*updelay) { + do { + *updelay = nmc_readline (_("Bonding updelay [0]: ")); + once_more = *updelay && !nmc_string_to_uint (*updelay, TRUE, 0, G_MAXUINT32, &tmp); + if (once_more) { + printf (_("Error: 'updelay': '%s' is not a valid number <0-%u>.\n"), + *updelay, G_MAXUINT32); + g_free (*updelay); + } + } while (once_more); + } + } else { + if (!*arpinterval) { + do { + *arpinterval = nmc_readline (_("Bonding arp-interval [0]: ")); + once_more = *arpinterval && !nmc_string_to_uint (*arpinterval, TRUE, 0, G_MAXUINT32, &tmp); + if (once_more) { + printf (_("Error: 'arp-interval': '%s' is not a valid number <0-%u>.\n"), + *arpinterval, G_MAXUINT32); + g_free (*arpinterval); + } + } while (once_more); + } + if (!*arpiptarget) { + //FIXME: verify the string + *arpiptarget = nmc_readline (_("Bonding arp-ip-target [none]: ")); + } + } + + g_free (monitor_mode); +} + +static void +do_questionnaire_team_common (const char *type_name, char **config) +{ + gboolean once_more; + char *json = NULL; + GError *error = NULL; + + /* Ask for optional arguments. */ + if (!want_provide_opt_args (type_name, 1)) + return; + + if (!*config) { + do { + *config = nmc_readline (_("Team JSON configuration [none]: ")); + once_more = !nmc_team_check_config (*config, &json, &error); + if (once_more) { + printf ("Error: %s\n", error->message); + g_clear_error (&error); + g_free (*config); + } + } while (once_more); + } + + *config = json; +} + +/* Both team and team-slave curently have just ithe same one optional argument */ +static void +do_questionnaire_team (char **config) +{ + do_questionnaire_team_common (_("team"), config); +} + +static void +do_questionnaire_team_slave (char **config) +{ + do_questionnaire_team_common (_("team-slave"), config); +} + +static void +do_questionnaire_bridge (char **stp, char **priority, char **fwd_delay, char **hello_time, + char **max_age, char **ageing_time, char **mac) +{ + unsigned long tmp; + gboolean once_more; + GError *error = NULL; + + /* Ask for optional 'bridge' arguments. */ + if (!want_provide_opt_args (_("bridge"), 7)) + return; + + if (!*stp) { + gboolean stp_bool; + do { + *stp = nmc_readline (_("Enable STP %s"), prompt_yes_no (TRUE, ":")); + *stp = *stp ? *stp : g_strdup ("yes"); + normalize_yes_no (stp); + once_more = !nmc_string_to_bool (*stp, &stp_bool, &error); + if (once_more) { + printf (_("Error: 'stp': %s.\n"), error->message); + g_clear_error (&error); + g_free (*stp); + } + } while (once_more); + } + if (!*priority) { + do { + *priority = nmc_readline (_("STP priority [32768]: ")); + *priority = *priority ? *priority : g_strdup ("32768"); + once_more = !nmc_string_to_uint (*priority, TRUE, 0, G_MAXUINT16, &tmp); + if (once_more) { + printf (_("Error: 'priority': '%s' is not a valid number <0-%d>.\n"), + *priority, G_MAXUINT16); + g_free (*priority); + } + } while (once_more); + } + if (!*fwd_delay) { + do { + *fwd_delay = nmc_readline (_("Forward delay [15]: ")); + *fwd_delay = *fwd_delay ? *fwd_delay : g_strdup ("15"); + once_more = !nmc_string_to_uint (*fwd_delay, TRUE, 2, 30, &tmp); + if (once_more) { + printf (_("Error: 'forward-delay': '%s' is not a valid number <2-30>.\n"), + *fwd_delay); + g_free (*fwd_delay); + } + } while (once_more); + } + + if (!*hello_time) { + do { + *hello_time = nmc_readline (_("Hello time [2]: ")); + *hello_time = *hello_time ? *hello_time : g_strdup ("2"); + once_more = !nmc_string_to_uint (*hello_time, TRUE, 1, 10, &tmp); + if (once_more) { + printf (_("Error: 'hello-time': '%s' is not a valid number <1-10>.\n"), + *hello_time); + g_free (*hello_time); + } + } while (once_more); + } + if (!*max_age) { + do { + *max_age = nmc_readline (_("Max age [20]: ")); + *max_age = *max_age ? *max_age : g_strdup ("20"); + once_more = !nmc_string_to_uint (*max_age, TRUE, 6, 40, &tmp); + if (once_more) { + printf (_("Error: 'max-age': '%s' is not a valid number <6-40>.\n"), + *max_age); + g_free (*max_age); + } + } while (once_more); + } + if (!*ageing_time) { + do { + *ageing_time = nmc_readline (_("MAC address ageing time [300]: ")); + *ageing_time = *ageing_time ? *ageing_time : g_strdup ("300"); + once_more = !nmc_string_to_uint (*ageing_time, TRUE, 0, 1000000, &tmp); + if (once_more) { + printf (_("Error: 'ageing-time': '%s' is not a valid number <0-1000000>.\n"), + *ageing_time); + g_free (*ageing_time); + } + } while (once_more); + } + if (!*mac) { + do { + *mac = nmc_get_user_input (_("MAC [none]: ")); + once_more = !check_and_convert_mac (*mac, NULL, ARPHRD_ETHER, "mac", &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*mac); + } + } while (once_more); + } +} + +static void +do_questionnaire_bridge_slave (char **priority, char **path_cost, char **hairpin) +{ + unsigned long tmp; + gboolean once_more; + GError *error = NULL; + + /* Ask for optional 'bridge-slave' arguments. */ + if (!want_provide_opt_args (_("bridge-slave"), 3)) + return; + + if (!*priority) { + do { + *priority = nmc_readline (_("Bridge port priority [32]: ")); + *priority = *priority ? *priority : g_strdup ("32"); + once_more = !bridge_prop_string_to_uint (*priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT, + NM_SETTING_BRIDGE_PORT_PRIORITY, &tmp, &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*priority); + } + } while (once_more); + } + if (!*path_cost) { + do { + *path_cost = nmc_readline (_("Bridge port STP path cost [100]: ")); + *path_cost = *path_cost ? *path_cost : g_strdup ("100"); + once_more = !bridge_prop_string_to_uint (*path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT, + NM_SETTING_BRIDGE_PORT_PATH_COST, &tmp, &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*path_cost); + } + } while (once_more); + } + if (!*hairpin) { + gboolean hairpin_bool; + do { + *hairpin = nmc_readline (_("Hairpin %s"), prompt_yes_no (TRUE, ":")); + *hairpin = *hairpin ? *hairpin : g_strdup ("yes"); + normalize_yes_no (hairpin); + once_more = !nmc_string_to_bool (*hairpin, &hairpin_bool, &error); + if (once_more) { + printf (_("Error: 'hairpin': %s.\n"), error->message); + g_clear_error (&error); + g_free (*hairpin); + } + } while (once_more); + } +} + +static void +do_questionnaire_vpn (char **user) +{ + /* Ask for optional 'vpn' arguments. */ + if (!want_provide_opt_args (_("VPN"), 1)) + return; + + if (!*user) + *user = nmc_readline (_("Username [none]: ")); +} + +static void +do_questionnaire_olpc (char **channel, char **dhcp_anycast) +{ + unsigned long tmp; + gboolean once_more; + GError *error = NULL; + + /* Ask for optional 'olpc' arguments. */ + if (!want_provide_opt_args (_("OLPC Mesh"), 2)) + return; + + if (!*channel) { + do { + *channel = nmc_readline (_("OLPC Mesh channel [1]: ")); + once_more = *channel && !nmc_string_to_uint (*channel, TRUE, 1, 13, &tmp); + if (once_more) { + printf (_("Error: 'channel': '%s' is not a valid number <1-13>.\n"), + *channel); + g_free (*channel); + } + } while (once_more); + } + if (!*dhcp_anycast) { + do { + *dhcp_anycast = nmc_readline (_("DHCP anycast MAC address [none]: ")); + once_more = !check_and_convert_mac (*dhcp_anycast, NULL, ARPHRD_ETHER, "dhcp-anycast", &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*dhcp_anycast); + } + } while (once_more); + } +} + +static gboolean +split_address (char* str, char **ip, char **gw, char **rest) +{ + size_t n1, n2, n3, n4, n5; + + *ip = *gw = *rest = NULL; + if (!str) + return FALSE; + + n1 = strspn (str, " \t"); + n2 = strcspn (str+n1, " \t\0") + n1; + n3 = strspn (str+n2, " \t") + n2; + n4 = strcspn (str+n3, " \t\0") + n3; + n5 = strspn (str+n4, " \t") + n4; + + str[n2] = str[n4] = '\0'; + *ip = str[n1] ? str + n1 : NULL; + *gw = str[n3] ? str + n3 : NULL; + *rest = str[n5] ? str + n5 : NULL; + + return TRUE; +} + +static void +ask_for_ip_addresses (NMConnection *connection, int family) +{ + gboolean ip_loop; + GError *error = NULL; + char *str, *ip, *gw, *rest; + const char *prompt; + gboolean added; + gpointer ipaddr; + + if (family == 4) + prompt =_("IPv4 address (IP[/plen] [gateway]) [none]: "); + else + prompt =_("IPv6 address (IP[/plen] [gateway]) [none]: "); + + ip_loop = TRUE; + do { + str = nmc_readline ("%s", prompt); + split_address (str, &ip, &gw, &rest); + if (ip) { + if (family == 4) + ipaddr = nmc_parse_and_build_ip4_address (ip, gw, &error); + else + ipaddr = nmc_parse_and_build_ip6_address (ip, gw, &error); + if (ipaddr) { + if (family == 4) + added = add_ip4_address_to_connection ((NMIP4Address *) ipaddr, connection); + else + added = add_ip6_address_to_connection ((NMIP6Address *) ipaddr, connection); + gw = gw ? gw : (family == 4) ? "0.0.0.0" : "::"; + if (added) + printf (_(" Address successfully added: %s %s\n"), ip, gw); + else + printf (_(" Warning: address already present: %s %s\n"), ip, gw); + if (rest) + printf (_(" Warning: ignoring garbage at the end: '%s'\n"), rest); + } else { + g_prefix_error (&error, _("Error: ")); + printf ("%s\n", error->message); + g_clear_error (&error); + } + } else + ip_loop = FALSE; + + g_free (str); + } while (ip_loop); +} + +static void +do_questionnaire_ip (NMConnection *connection) +{ + char *answer; + + /* Ask for IP addresses */ + answer = nmc_readline (_("Do you want to add IP addresses? %s"), prompt_yes_no (TRUE, NULL)); + answer = answer ? g_strstrip (answer) : NULL; + if (answer && matches (answer, WORD_LOC_YES) != 0) { + g_free (answer); + return; + } + + printf (_("Press <Enter> to finish adding addresses.\n")); + + ask_for_ip_addresses (connection, 4); + ask_for_ip_addresses (connection, 6); + + g_free (answer); + return; +} + +static gboolean +complete_connection_by_type (NMConnection *connection, + const char *con_type, + GSList *all_connections, + gboolean ask, + int argc, + char **argv, + GError **error) +{ + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingInfiniband *s_infiniband; + NMSettingWireless *s_wifi; + NMSettingWimax *s_wimax; + NMSettingPPPOE *s_pppoe; + NMSettingGsm *s_gsm; + NMSettingCdma *s_cdma; + NMSettingBluetooth *s_bt; + NMSettingVlan *s_vlan; + NMSettingBond *s_bond; + NMSettingTeam *s_team; + NMSettingTeamPort *s_team_port; + NMSettingBridge *s_bridge; + NMSettingBridgePort *s_bridge_port; + NMSettingVPN *s_vpn; + NMSettingOlpcMesh *s_olpc_mesh; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + if (!strcmp (con_type, NM_SETTING_WIRED_SETTING_NAME)) { + /* Build up the settings required for 'ethernet' */ + gboolean success = FALSE; + const char *mtu_c = NULL; + char *mtu = NULL; + guint32 mtu_int = 0; + const char *mac_c = NULL; + char *mac = NULL; + const char *cloned_mac_c = NULL; + char *cloned_mac = NULL; + GByteArray *array = NULL; + GByteArray *cloned_array = NULL; + nmc_arg_t exp_args[] = { {"mtu", TRUE, &mtu_c, FALSE}, + {"mac", TRUE, &mac_c, FALSE}, + {"cloned-mac", TRUE, &cloned_mac_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + /* Also ask for all optional arguments if '--ask' is specified. */ + mtu = mtu_c ? g_strdup (mtu_c) : NULL; + mac = mac_c ? g_strdup (mac_c) : NULL; + cloned_mac = cloned_mac_c ? g_strdup (cloned_mac_c) : NULL; + if (ask) + do_questionnaire_ethernet (TRUE, &mtu, &mac, &cloned_mac); + + if (!check_and_convert_mtu (mtu, &mtu_int, error)) + goto cleanup_wired; + if (!check_and_convert_mac (mac, &array, ARPHRD_ETHER, "mac", error)) + goto cleanup_wired; + if (!check_and_convert_mac (cloned_mac, &cloned_array, ARPHRD_ETHER, "cloned-mac", error)) + goto cleanup_wired; + + /* Add ethernet setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + if (mtu) + g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL); + if (array) + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, array, NULL); + if (cloned_array) + g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, cloned_array, NULL); + + success = TRUE; +cleanup_wired: + g_free (mtu); + g_free (mac); + g_free (cloned_mac); + if (array) + g_byte_array_free (array, TRUE); + if (cloned_array) + g_byte_array_free (cloned_array, TRUE); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_INFINIBAND_SETTING_NAME)) { + /* Build up the settings required for 'infiniband' */ + gboolean success = FALSE; + const char *mtu_c = NULL; + char *mtu = NULL; + guint32 mtu_int = 0; + const char *mac_c = NULL; + char *mac = NULL; + GByteArray *array = NULL; + const char *mode_c = NULL; + char *mode = NULL; + const char *parent_c = NULL; + char *parent = NULL; + const char *p_key_c = NULL; + char *p_key = NULL; + guint32 p_key_int = 0; + nmc_arg_t exp_args[] = { {"mtu", TRUE, &mtu_c, FALSE}, + {"mac", TRUE, &mac_c, FALSE}, + {"transport-mode", TRUE, &mode_c, FALSE}, + {"parent", TRUE, &parent_c, FALSE}, + {"p-key", TRUE, &p_key_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + /* Also ask for all optional arguments if '--ask' is specified. */ + mtu = mtu_c ? g_strdup (mtu_c) : NULL; + mac = mac_c ? g_strdup (mac_c) : NULL; + mode = mode_c ? g_strdup (mode_c) : NULL; + parent = parent_c ? g_strdup (parent_c) : NULL; + p_key = p_key_c ? g_strdup (p_key_c) : NULL; + if (ask) + do_questionnaire_infiniband (&mtu, &mac, &mode, &parent, &p_key); + + if (!check_and_convert_mtu (mtu, &mtu_int, error)) + goto cleanup_ib; + if (!check_and_convert_mac (mac, &array, ARPHRD_INFINIBAND, "mac", error)) + goto cleanup_ib; + if (!check_infiniband_mode (&mode, error)) + goto cleanup_ib; + if (p_key) { + if (!check_infiniband_p_key (p_key, &p_key_int, error)) + goto cleanup_ib; + if (!check_infiniband_parent (parent, error)) + goto cleanup_ib; + } else if (parent) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'parent': not valid without 'p-key'.")); + goto cleanup_ib; + } + + /* Add 'infiniband' setting */ + s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new (); + nm_connection_add_setting (connection, NM_SETTING (s_infiniband)); + + g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, mode ? mode : "datagram", NULL); + if (mtu) + g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MTU, mtu_int, NULL); + if (array) { + g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, array, NULL); + g_byte_array_free (array, TRUE); + } + if (p_key) + g_object_set (s_infiniband, NM_SETTING_INFINIBAND_P_KEY, p_key_int, NULL); + if (parent) + g_object_set (s_infiniband, NM_SETTING_INFINIBAND_PARENT, parent, NULL); + + + success = TRUE; +cleanup_ib: + g_free (mtu); + g_free (mac); + g_free (mode); + g_free (parent); + g_free (p_key); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME)) { + /* Build up the settings required for 'wifi' */ + gboolean success = FALSE; + char *ssid_ask = NULL; + const char *ssid = NULL; + GByteArray *ssid_arr = NULL; + const char *mtu_c = NULL; + char *mtu = NULL; + guint32 mtu_int = 0; + const char *mac_c = NULL; + char *mac = NULL; + GByteArray *mac_array = NULL; + const char *cloned_mac_c = NULL; + char *cloned_mac = NULL; + GByteArray *cloned_mac_array = NULL; + nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask}, + {"mtu", TRUE, &mtu_c, FALSE}, + {"mac", TRUE, &mac_c, FALSE}, + {"cloned-mac", TRUE, &cloned_mac_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!ssid && ask) + ssid = ssid_ask = nmc_readline (_("SSID: ")); + if (!ssid) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'ssid' is required.")); + return FALSE; + } + + /* Also ask for all optional arguments if '--ask' is specified. */ + mtu = mtu_c ? g_strdup (mtu_c) : NULL; + mac = mac_c ? g_strdup (mac_c) : NULL; + cloned_mac = cloned_mac_c ? g_strdup (cloned_mac_c) : NULL; + if (ask) + do_questionnaire_wifi (&mtu, &mac, &cloned_mac); + + if (!check_and_convert_mtu (mtu, &mtu_int, error)) + goto cleanup_wifi; + if (!check_and_convert_mac (mac, &mac_array, ARPHRD_ETHER, "mac", error)) + goto cleanup_wifi; + if (!check_and_convert_mac (cloned_mac, &cloned_mac_array, ARPHRD_ETHER, "cloned-mac", error)) + goto cleanup_wifi; + + /* Add wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + + ssid_arr = g_byte_array_sized_new (strlen (ssid)); + g_byte_array_append (ssid_arr, (const guint8 *) ssid, strlen (ssid)); + g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid_arr, NULL); + + if (mtu) + g_object_set (s_wifi, NM_SETTING_WIRELESS_MTU, mtu_int, NULL); + if (mac_array) + g_object_set (s_wifi, NM_SETTING_WIRELESS_MAC_ADDRESS, mac_array, NULL); + if (cloned_mac_array) + g_object_set (s_wifi, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, cloned_mac_array, NULL); + + success = TRUE; +cleanup_wifi: + g_free (ssid_ask); + g_free (mtu); + g_free (mac); + g_free (cloned_mac); + if (ssid_arr) + g_byte_array_free (ssid_arr, TRUE); + if (mac_array) + g_byte_array_free (mac_array, TRUE); + if (cloned_mac_array) + g_byte_array_free (cloned_mac_array, TRUE); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_WIMAX_SETTING_NAME)) { + /* Build up the settings required for 'wimax' */ + gboolean success = FALSE; + const char *nsp_name = NULL; + char *nsp_name_ask = NULL; + const char *mac_c = NULL; + char *mac = NULL; + GByteArray *mac_array = NULL; + nmc_arg_t exp_args[] = { {"nsp", TRUE, &nsp_name, !ask}, + {"mac", TRUE, &mac_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!nsp_name && ask) + nsp_name = nsp_name_ask = nmc_readline (_("WiMAX NSP name: ")); + if (!nsp_name) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'nsp' is required.")); + goto cleanup_wimax; + } + + /* Also ask for all optional arguments if '--ask' is specified. */ + mac = mac_c ? g_strdup (mac_c) : NULL; + if (ask) + do_questionnaire_wimax (&mac); + + if (!check_and_convert_mac (mac, &mac_array, ARPHRD_ETHER, "mac", error)) + goto cleanup_wimax; + + /* Add 'wimax' setting */ + s_wimax = (NMSettingWimax *) nm_setting_wimax_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wimax)); + g_object_set (s_wimax, NM_SETTING_WIMAX_NETWORK_NAME, nsp_name, NULL); + + if (mac_array) { + g_object_set (s_wimax, NM_SETTING_WIMAX_MAC_ADDRESS, mac_array, NULL); + g_byte_array_free (mac_array, TRUE); + } + + success = TRUE; +cleanup_wimax: + g_free (nsp_name_ask); + g_free (mac); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_PPPOE_SETTING_NAME)) { + /* Build up the settings required for 'pppoe' */ + gboolean success = FALSE; + const char *username = NULL; + char *username_ask = NULL; + const char *password_c = NULL; + char *password = NULL; + const char *service_c = NULL; + char *service = NULL; + const char *mtu_c = NULL; + char *mtu = NULL; + guint32 mtu_int = 0; + const char *mac_c = NULL; + char *mac = NULL; + GByteArray *mac_array = NULL; + nmc_arg_t exp_args[] = { {"username", TRUE, &username, !ask}, + {"password", TRUE, &password_c, FALSE}, + {"service", TRUE, &service_c, FALSE}, + {"mtu", TRUE, &mtu_c, FALSE}, + {"mac", TRUE, &mac_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!username && ask) + username = username_ask = nmc_readline (_("PPPoE username: ")); + if (!username) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'username' is required.")); + goto cleanup_pppoe; + } + + /* Also ask for all optional arguments if '--ask' is specified. */ + password = g_strdup (password_c); + service = g_strdup (service_c); + mtu = g_strdup (mtu_c); + mac = g_strdup (mac_c); + if (ask) + do_questionnaire_pppoe (&password, &service, &mtu, &mac); + + if (!check_and_convert_mtu (mtu, &mtu_int, error)) + goto cleanup_pppoe; + if (!check_and_convert_mac (mac, &mac_array, ARPHRD_ETHER, "mac", error)) + goto cleanup_pppoe; + + /* Add 'pppoe' setting */ + s_pppoe = (NMSettingPPPOE *) nm_setting_pppoe_new (); + nm_connection_add_setting (connection, NM_SETTING (s_pppoe)); + g_object_set (s_pppoe, NM_SETTING_PPPOE_USERNAME, username, NULL); + g_object_set (s_pppoe, NM_SETTING_PPPOE_PASSWORD, password, NULL); + g_object_set (s_pppoe, NM_SETTING_PPPOE_SERVICE, service, NULL); + + /* Add ethernet setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + if (mtu) + g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL); + if (mac_array) + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac_array, NULL); + + success = TRUE; +cleanup_pppoe: + g_free (username_ask); + g_free (password); + g_free (service); + g_free (mtu); + g_free (mac); + if (mac_array) + g_byte_array_free (mac_array, TRUE); + if (!success) + return FALSE; + + } else if ( !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME) + || !strcmp (con_type, NM_SETTING_CDMA_SETTING_NAME)) { + /* Build up the settings required for 'gsm' or 'cdma' mobile broadband */ + gboolean success = FALSE; + const char *apn = NULL; + char *apn_ask = NULL; + const char *user_c = NULL; + char *user = NULL; + const char *password_c = NULL; + char *password = NULL; + gboolean is_gsm; + int i = 0; + nmc_arg_t gsm_args[] = { {NULL}, {NULL}, {NULL}, /* placeholders */ + {NULL} }; + + is_gsm = !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME); + + if (is_gsm) + gsm_args[i++] = (nmc_arg_t) {"apn", TRUE, &apn, !ask}; + gsm_args[i++] = (nmc_arg_t) {"user", TRUE, &user_c, FALSE}; + gsm_args[i++] = (nmc_arg_t) {"password", TRUE, &password_c, FALSE}; + gsm_args[i++] = (nmc_arg_t) {NULL}; + + if (!nmc_parse_args (gsm_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!apn && ask && is_gsm) + apn = apn_ask = nmc_readline (_("APN: ")); + if (!apn && is_gsm) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'apn' is required.")); + goto cleanup_mobile; + } + + /* Also ask for all optional arguments if '--ask' is specified. */ + user = user_c ? g_strdup (user_c) : NULL; + password = password_c ? g_strdup (password_c) : NULL; + if (ask) + do_questionnaire_mobile (&user, &password); + + if (is_gsm) { + g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME, NULL); + + /* Add 'gsm' setting */ + s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); + nm_connection_add_setting (connection, NM_SETTING (s_gsm)); + g_object_set (s_gsm, + NM_SETTING_GSM_NUMBER, "*99#", + NM_SETTING_GSM_APN, apn, + NM_SETTING_GSM_USERNAME, user, + NM_SETTING_GSM_PASSWORD, password, + NULL); + g_free (apn_ask); + } else { + g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_CDMA_SETTING_NAME, NULL); + + /* Add 'cdma' setting */ + s_cdma = (NMSettingCdma *) nm_setting_cdma_new (); + nm_connection_add_setting (connection, NM_SETTING (s_cdma)); + g_object_set (s_cdma, + NM_SETTING_CDMA_NUMBER, "#777", + NM_SETTING_CDMA_USERNAME, user, + NM_SETTING_CDMA_PASSWORD, password, + NULL); + } + + success = TRUE; +cleanup_mobile: + g_free (user); + g_free (password); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_BLUETOOTH_SETTING_NAME)) { + /* Build up the settings required for 'bluetooth' */ + gboolean success = FALSE; + const char *addr = NULL; + char *addr_ask = NULL; + const char *bt_type_c = NULL; + char *bt_type = NULL; + GByteArray *array = NULL; + nmc_arg_t exp_args[] = { {"addr", TRUE, &addr, !ask}, + {"bt-type", TRUE, &bt_type_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!addr && ask) + addr = addr_ask = nmc_readline (_("Bluetooth device address: ")); + if (!addr) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'addr' is required.")); + return FALSE; + } + if (!check_and_convert_mac (addr, &array, ARPHRD_ETHER, "addr", error)) + goto cleanup_bt; + + /* Also ask for all optional arguments if '--ask' is specified. */ + bt_type = bt_type_c ? g_strdup (bt_type_c) : NULL; + if (ask) + do_questionnaire_bluetooth (&bt_type); + + /* Default to 'panu' if bt-type is not provided. */ + if (!bt_type) + bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_PANU); + + /* Add 'bluetooth' setting */ + s_bt = (NMSettingBluetooth *) nm_setting_bluetooth_new (); + nm_connection_add_setting (connection, NM_SETTING (s_bt)); + + if (array) { + g_object_set (s_bt, NM_SETTING_BLUETOOTH_BDADDR, array, NULL); + g_byte_array_free (array, TRUE); + } + + /* 'dun' type requires adding 'gsm' or 'cdma' setting */ + if ( !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN) + || !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm")) { + bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN); + s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); + nm_connection_add_setting (connection, NM_SETTING (s_gsm)); + g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL); +// g_object_set (s_gsm, NM_SETTING_GSM_APN, "FIXME", NULL; + + } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma")) { + bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN); + s_cdma = (NMSettingCdma *) nm_setting_cdma_new (); + nm_connection_add_setting (connection, NM_SETTING (s_cdma)); + g_object_set (s_cdma, NM_SETTING_CDMA_NUMBER, "#777", NULL); + + } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)) { + /* no op */ + } else { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'bt-type': '%s' not valid; use [%s, %s (%s), %s]."), + bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU, NM_SETTING_BLUETOOTH_TYPE_DUN, + NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm", NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma"); + goto cleanup_bt; + } + g_object_set (s_bt, NM_SETTING_BLUETOOTH_TYPE, bt_type, NULL); + + success = TRUE; +cleanup_bt: + g_free (addr_ask); + g_free (bt_type); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_VLAN_SETTING_NAME)) { + /* Build up the settings required for 'vlan' */ + gboolean success = FALSE; + const char *ifname = NULL; + const char *parent = NULL; + char *parent_ask = NULL; + const char *vlan_id = NULL; + char *vlan_id_ask = NULL; + unsigned long id = 0; + const char *flags_c = NULL; + char *flags = NULL; + guint32 flags_int = 0; + const char *ingress_c = NULL, *egress_c = NULL; + char *ingress = NULL, *egress = NULL; + char **ingress_arr = NULL, **egress_arr = NULL, **p; + const char *mtu_c = NULL; + char *mtu = NULL; + guint32 mtu_int; + GByteArray *addr_array = NULL; + nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !ask}, + {"id", TRUE, &vlan_id, !ask}, + {"flags", TRUE, &flags_c, FALSE}, + {"ingress", TRUE, &ingress_c, FALSE}, + {"egress", TRUE, &egress_c, FALSE}, + {"mtu", TRUE, &mtu_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!parent && ask) + parent = parent_ask = nmc_readline (_("VLAN parent device or connection UUID: ")); + if (!parent) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'dev' is required.")); + return FALSE; + } + if (!vlan_id && ask) + vlan_id = vlan_id_ask = nmc_readline (_("VLAN ID <0-4095>: ")); + if (!vlan_id) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'id' is required.")); + goto cleanup_vlan; + } + if (vlan_id) { + if (!nmc_string_to_uint (vlan_id, TRUE, 0, 4095, &id)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'id': '%s' is not valid; use <0-4095>."), + vlan_id); + goto cleanup_vlan; + } + } + + if ( !(addr_array = nm_utils_hwaddr_atoba (parent, ARPHRD_ETHER)) + && !nm_utils_is_uuid (parent) + && !nm_utils_iface_valid_name (parent)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."), + parent); + goto cleanup_vlan; + } + + /* Also ask for all optional arguments if '--ask' is specified. */ + mtu = mtu_c ? g_strdup (mtu_c) : NULL; + flags = flags_c ? g_strdup (flags_c) : NULL; + ingress = ingress_c ? g_strdup (ingress_c) : NULL; + egress = egress_c ? g_strdup (egress_c) : NULL; + if (ask) + do_questionnaire_vlan (&mtu, &flags, &ingress, &egress); + + /* ifname is taken from connection's ifname */ + ifname = nm_setting_connection_get_interface_name (s_con); + + if (!check_and_convert_mtu (mtu, &mtu_int, error)) + goto cleanup_vlan; + if (!check_and_convert_vlan_flags (flags, &flags_int, error)) + goto cleanup_vlan; + if (!check_and_convert_vlan_prio_maps (ingress, NM_VLAN_INGRESS_MAP, &ingress_arr, error)) + goto cleanup_vlan; + if (!check_and_convert_vlan_prio_maps (egress, NM_VLAN_EGRESS_MAP, &egress_arr, error)) + goto cleanup_vlan; + + /* Add 'vlan' setting */ + s_vlan = (NMSettingVlan *) nm_setting_vlan_new (); + nm_connection_add_setting (connection, NM_SETTING (s_vlan)); + + /* Add 'wired' setting if necessary */ + if (mtu || addr_array) { + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + if (mtu) + g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL); + if (addr_array) + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, addr_array, NULL); + } + + /* Set 'vlan' properties */ + if (!addr_array) + g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, parent, NULL); + + if (ifname) + g_object_set (s_vlan, NM_SETTING_VLAN_INTERFACE_NAME, ifname, NULL); + g_object_set (s_vlan, NM_SETTING_VLAN_ID, id, NULL); + + if (flags) + g_object_set (s_vlan, NM_SETTING_VLAN_FLAGS, flags_int, NULL); + for (p = ingress_arr; p && *p; p++) + nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_INGRESS_MAP, *p); + for (p = egress_arr; p && *p; p++) + nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_EGRESS_MAP, *p); + + success = TRUE; +cleanup_vlan: + g_free (mtu); + g_free (flags); + g_free (ingress); + g_free (egress); + if (addr_array) + g_byte_array_free (addr_array, TRUE); + g_free (parent_ask); + g_free (vlan_id_ask); + g_strfreev (ingress_arr); + g_strfreev (egress_arr); + if (!success) + return FALSE; - device_found = find_device_for_connection (nmc, connection, iface, ap, nsp, &device, &spec_object, &error); - /* Virtual connection may not have their interfaces created yet */ - if (!device_found && !is_virtual) { - if (error) - g_string_printf (nmc->return_text, _("Error: No suitable device found: %s."), error->message); + } else if (!strcmp (con_type, NM_SETTING_BOND_SETTING_NAME)) { + /* Build up the settings required for 'bond' */ + gboolean success = FALSE; + char *bond_ifname = NULL; + const char *ifname = NULL; + const char *bond_mode_c = NULL; + char *bond_mode = NULL; + const char *bond_primary_c = NULL; + char *bond_primary = NULL; + const char *bond_miimon_c = NULL; + char *bond_miimon = NULL; + const char *bond_downdelay_c = NULL; + char *bond_downdelay = NULL; + const char *bond_updelay_c = NULL; + char *bond_updelay = NULL; + const char *bond_arpinterval_c = NULL; + char *bond_arpinterval = NULL; + const char *bond_arpiptarget_c = NULL; + char *bond_arpiptarget = NULL; + nmc_arg_t exp_args[] = { {"mode", TRUE, &bond_mode_c, FALSE}, + {"primary", TRUE, &bond_primary_c, FALSE}, + {"miimon", TRUE, &bond_miimon_c, FALSE}, + {"downdelay", TRUE, &bond_downdelay_c, FALSE}, + {"updelay", TRUE, &bond_updelay_c, FALSE}, + {"arp-interval", TRUE, &bond_arpinterval_c, FALSE}, + {"arp-ip-target", TRUE, &bond_arpiptarget_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + /* Also ask for all optional arguments if '--ask' is specified. */ + bond_mode = bond_mode_c ? g_strdup (bond_mode_c) : NULL; + bond_primary = bond_primary_c ? g_strdup (bond_primary_c) : NULL; + bond_miimon = bond_miimon_c ? g_strdup (bond_miimon_c) : NULL; + bond_downdelay = bond_downdelay_c ? g_strdup (bond_downdelay_c) : NULL; + bond_updelay = bond_updelay_c ? g_strdup (bond_updelay_c) : NULL; + bond_arpinterval = bond_arpinterval_c ? g_strdup (bond_arpinterval_c) : NULL; + bond_arpiptarget = bond_arpiptarget_c ? g_strdup (bond_arpiptarget_c) : NULL; + if (ask) + do_questionnaire_bond (&bond_mode, &bond_primary, &bond_miimon, + &bond_downdelay, &bond_updelay, + &bond_arpinterval, &bond_arpiptarget); + + /* Use connection's ifname as 'bond' ifname if exists, else generate one */ + ifname = nm_setting_connection_get_interface_name (s_con); + if (!ifname) + bond_ifname = unique_master_iface_ifname (all_connections, + NM_SETTING_BOND_SETTING_NAME, + NM_SETTING_BOND_INTERFACE_NAME, + "nm-bond"); + else + bond_ifname = g_strdup (ifname); + + /* Add 'bond' setting */ + s_bond = (NMSettingBond *) nm_setting_bond_new (); + nm_connection_add_setting (connection, NM_SETTING (s_bond)); + + /* Set bond options */ + g_object_set (s_bond, NM_SETTING_BOND_INTERFACE_NAME, bond_ifname, NULL); + if (bond_mode) { + GError *err = NULL; + const char *bm; + if (!(bm = nmc_bond_validate_mode (bond_mode, &err))) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'mode': %s."), err->message); + g_clear_error (&err); + goto cleanup_bond; + } + nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MODE, bm); + } + if (bond_primary) { + if (!nm_utils_iface_valid_name (bond_primary)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'primary': '%s' is not a valid interface name."), + bond_primary); + goto cleanup_bond; + } + nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_PRIMARY, bond_primary); + } + if (bond_miimon) + nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MIIMON, bond_miimon); + if (bond_downdelay && strcmp (bond_downdelay, "0") != 0) + nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, bond_downdelay); + if (bond_updelay && strcmp (bond_updelay, "0") != 0) + nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_UPDELAY, bond_updelay); + if (bond_arpinterval && strcmp (bond_arpinterval, "0") != 0) + nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL, bond_arpinterval); + if (bond_arpiptarget) + nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, bond_arpiptarget); + + success = TRUE; +cleanup_bond: + g_free (bond_ifname); + g_free (bond_mode); + g_free (bond_primary); + g_free (bond_miimon); + g_free (bond_downdelay); + g_free (bond_updelay); + g_free (bond_arpinterval); + g_free (bond_arpiptarget); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, "bond-slave")) { + /* Build up the settings required for 'bond-slave' */ + const char *master = NULL; + char *master_ask = NULL; + const char *checked_master = NULL; + const char *type = NULL; + nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask}, + {"type", TRUE, &type, FALSE}, + {NULL} }; + + /* Set global variables for use in TAB completion */ + nmc_tab_completion.con_type = NM_SETTING_BOND_SETTING_NAME; + + if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error)) + return FALSE; + + if (!master && ask) + master = master_ask = nmc_readline (PROMPT_BOND_MASTER); + if (!master) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'master' is required.")); + return FALSE; + } + /* Verify master argument */ + checked_master = verify_master_for_slave (all_connections, master, NM_SETTING_BOND_SETTING_NAME); + if (!checked_master) + printf (_("Warning: master='%s' doesn't refer to any existing profile.\n"), master); + + if (type) + printf (_("Warning: 'type' is currently ignored. " + "We only support ethernet slaves for now.\n")); + + /* Change properties in 'connection' setting */ + g_object_set (s_con, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_MASTER, checked_master ? checked_master : _strip_master_prefix (master, NULL), + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME, + NULL); + + /* Add ethernet setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + g_free (master_ask); + + } else if (!strcmp (con_type, NM_SETTING_TEAM_SETTING_NAME)) { + /* Build up the settings required for 'team' */ + gboolean success = FALSE; + char *team_ifname = NULL; + const char *ifname = NULL; + const char *config_c = NULL; + char *config = NULL; + char *json = NULL; + nmc_arg_t exp_args[] = { {"config", TRUE, &config_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + /* Also ask for all optional arguments if '--ask' is specified. */ + config = g_strdup (config_c); + if (ask) + do_questionnaire_team (&config); + + /* Use connection's ifname as 'team' ifname if exists, else generate one */ + ifname = nm_setting_connection_get_interface_name (s_con); + if (!ifname) + team_ifname = unique_master_iface_ifname (all_connections, + NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_INTERFACE_NAME, + "nm-team"); + else + team_ifname = g_strdup (ifname); + + /* Add 'team' setting */ + s_team = (NMSettingTeam *) nm_setting_team_new (); + nm_connection_add_setting (connection, NM_SETTING (s_team)); + + if (!nmc_team_check_config (config, &json, error)) { + g_prefix_error (error, _("Error: ")); + goto cleanup_team; + } + + /* Set team options */ + g_object_set (s_team, NM_SETTING_TEAM_INTERFACE_NAME, team_ifname, NULL); + g_object_set (s_team, NM_SETTING_TEAM_CONFIG, json, NULL); + + success = TRUE; +cleanup_team: + g_free (team_ifname); + g_free (config); + g_free (json); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, "team-slave")) { + /* Build up the settings required for 'team-slave' */ + gboolean success = FALSE; + const char *master = NULL; + char *master_ask = NULL; + const char *checked_master = NULL; + const char *type = NULL; + const char *config_c = NULL; + char *config = NULL; + char *json = NULL; + nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask}, + {"type", TRUE, &type, FALSE}, + {"config", TRUE, &config_c, FALSE}, + {NULL} }; + + /* Set global variables for use in TAB completion */ + nmc_tab_completion.con_type = NM_SETTING_TEAM_SETTING_NAME; + + if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error)) + return FALSE; + + if (!master && ask) + master = master_ask = nmc_readline (PROMPT_TEAM_MASTER); + if (!master) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'master' is required.")); + return FALSE; + } + /* Verify master argument */ + checked_master = verify_master_for_slave (all_connections, master, NM_SETTING_TEAM_SETTING_NAME); + if (!checked_master) + printf (_("Warning: master='%s' doesn't refer to any existing profile.\n"), master); + + /* Also ask for all optional arguments if '--ask' is specified. */ + config = g_strdup (config_c); + if (ask) + do_questionnaire_team_slave (&config); + + if (type) + printf (_("Warning: 'type' is currently ignored. " + "We only support ethernet slaves for now.\n")); + + /* Add 'team-port' setting */ + s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new (); + nm_connection_add_setting (connection, NM_SETTING (s_team_port)); + + if (!nmc_team_check_config (config, &json, error)) { + g_prefix_error (error, _("Error: ")); + goto cleanup_team_slave; + } + + /* Set team-port options */ + g_object_set (s_team_port, NM_SETTING_TEAM_PORT_CONFIG, json, NULL); + + /* Change properties in 'connection' setting */ + g_object_set (s_con, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_MASTER, checked_master ? checked_master : _strip_master_prefix (master, NULL), + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME, + NULL); + + /* Add ethernet setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + success = TRUE; +cleanup_team_slave: + g_free (master_ask); + g_free (config); + g_free (json); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_BRIDGE_SETTING_NAME)) { + /* Build up the settings required for 'bridge' */ + gboolean success = FALSE; + char *bridge_ifname = NULL; + const char *ifname = NULL; + const char *stp_c = NULL; + char *stp = NULL; + const char *priority_c = NULL; + char *priority = NULL; + const char *fwd_delay_c = NULL; + char *fwd_delay = NULL; + const char *hello_time_c = NULL; + char *hello_time = NULL; + const char *max_age_c = NULL; + char *max_age = NULL; + const char *ageing_time_c = NULL; + char *ageing_time = NULL; + gboolean stp_bool; + unsigned long stp_prio_int, fwd_delay_int, hello_time_int, + max_age_int, ageing_time_int; + const char *mac_c = NULL; + char *mac = NULL; + GByteArray *mac_array = NULL; + nmc_arg_t exp_args[] = { {"stp", TRUE, &stp_c, FALSE}, + {"priority", TRUE, &priority_c, FALSE}, + {"forward-delay", TRUE, &fwd_delay_c, FALSE}, + {"hello-time", TRUE, &hello_time_c, FALSE}, + {"max-age", TRUE, &max_age_c, FALSE}, + {"ageing-time", TRUE, &ageing_time_c, FALSE}, + {"mac", TRUE, &mac_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + /* Also ask for all optional arguments if '--ask' is specified. */ + stp = stp_c ? g_strdup (stp_c) : NULL; + priority = priority_c ? g_strdup (priority_c) : NULL; + fwd_delay = fwd_delay_c ? g_strdup (fwd_delay_c) : NULL; + hello_time = hello_time_c ? g_strdup (hello_time_c) : NULL; + max_age = max_age_c ? g_strdup (max_age_c) : NULL; + ageing_time = ageing_time_c ? g_strdup (ageing_time_c) : NULL; + mac = g_strdup (mac_c); + if (ask) + do_questionnaire_bridge (&stp, &priority, &fwd_delay, &hello_time, + &max_age, &ageing_time, &mac); + + /* Use connection's ifname as 'bridge' ifname if exists, else generate one */ + ifname = nm_setting_connection_get_interface_name (s_con); + if (!ifname) + bridge_ifname = unique_master_iface_ifname (all_connections, + NM_SETTING_BRIDGE_SETTING_NAME, + NM_SETTING_BRIDGE_INTERFACE_NAME, + "nm-bridge"); + else + bridge_ifname = g_strdup (ifname); + + if (stp) { + GError *tmp_err = NULL; + if (!nmc_string_to_bool (stp, &stp_bool, &tmp_err)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'stp': %s."), tmp_err->message); + g_clear_error (&tmp_err); + goto cleanup_bridge; + } + } + + /* Add 'bond' setting */ + /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */ + s_bridge = (NMSettingBridge *) nm_setting_bridge_new (); + nm_connection_add_setting (connection, NM_SETTING (s_bridge)); + + if (priority) + if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE, + NM_SETTING_BRIDGE_PRIORITY, &stp_prio_int, error)) + goto cleanup_bridge; + if (fwd_delay) + if (!bridge_prop_string_to_uint (fwd_delay, "forward-delay", NM_TYPE_SETTING_BRIDGE, + NM_SETTING_BRIDGE_FORWARD_DELAY, &fwd_delay_int, error)) + goto cleanup_bridge; + if (hello_time) + if (!bridge_prop_string_to_uint (hello_time, "hello-time", NM_TYPE_SETTING_BRIDGE, + NM_SETTING_BRIDGE_HELLO_TIME, &hello_time_int, error)) + goto cleanup_bridge; + if (max_age) + if (!bridge_prop_string_to_uint (max_age, "max-age", NM_TYPE_SETTING_BRIDGE, + NM_SETTING_BRIDGE_MAX_AGE, &max_age_int, error)) + goto cleanup_bridge; + if (ageing_time) + if (!bridge_prop_string_to_uint (ageing_time, "ageing-time", NM_TYPE_SETTING_BRIDGE, + NM_SETTING_BRIDGE_AGEING_TIME, &ageing_time_int, error)) + goto cleanup_bridge; + if (!check_and_convert_mac (mac, &mac_array, ARPHRD_ETHER, "mac", error)) + goto cleanup_bridge; + + /* Set bridge options */ + g_object_set (s_bridge, NM_SETTING_BRIDGE_INTERFACE_NAME, bridge_ifname, NULL); + if (stp) + g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, stp_bool, NULL); + if (priority) + g_object_set (s_bridge, NM_SETTING_BRIDGE_PRIORITY, stp_prio_int, NULL); + if (fwd_delay) + g_object_set (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, fwd_delay_int, NULL); + if (hello_time) + g_object_set (s_bridge, NM_SETTING_BRIDGE_HELLO_TIME, hello_time_int, NULL); + if (max_age) + g_object_set (s_bridge, NM_SETTING_BRIDGE_MAX_AGE, max_age_int, NULL); + if (ageing_time) + g_object_set (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME, ageing_time_int, NULL); + if (mac_array) + g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, mac_array, NULL); + + success = TRUE; +cleanup_bridge: + g_free (bridge_ifname); + g_free (stp); + g_free (priority); + g_free (fwd_delay); + g_free (hello_time); + g_free (max_age); + g_free (ageing_time); + g_free (mac); + if (mac_array) + g_byte_array_free (mac_array, TRUE); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, "bridge-slave")) { + /* Build up the settings required for 'bridge-slave' */ + gboolean success = FALSE; + const char *master = NULL; + char *master_ask = NULL; + const char *checked_master = NULL; + const char *type = NULL; + const char *priority_c = NULL; + char *priority = NULL; + const char *path_cost_c = NULL; + char *path_cost = NULL; + const char *hairpin_c = NULL; + char *hairpin = NULL; + unsigned long prio_int, path_cost_int; + gboolean hairpin_bool; + nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask}, + {"type", TRUE, &type, FALSE}, + {"priority", TRUE, &priority_c, FALSE}, + {"path-cost", TRUE, &path_cost_c, FALSE}, + {"hairpin", TRUE, &hairpin_c, FALSE}, + {NULL} }; + + /* Set global variables for use in TAB completion */ + nmc_tab_completion.con_type = NM_SETTING_BRIDGE_SETTING_NAME; + + if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error)) + return FALSE; + + if (!master && ask) + master = master_ask = nmc_readline (PROMPT_BRIDGE_MASTER); + if (!master) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'master' is required.")); + return FALSE; + } + /* Verify master argument */ + checked_master = verify_master_for_slave (all_connections, master, NM_SETTING_BRIDGE_SETTING_NAME); + if (!checked_master) + printf (_("Warning: master='%s' doesn't refer to any existing profile.\n"), master); + + if (type) + printf (_("Warning: 'type' is currently ignored. " + "We only support ethernet slaves for now.\n")); + + /* Add 'bridge-port' setting */ + /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */ + s_bridge_port = (NMSettingBridgePort *) nm_setting_bridge_port_new (); + nm_connection_add_setting (connection, NM_SETTING (s_bridge_port)); + + /* Also ask for all optional arguments if '--ask' is specified. */ + priority = priority_c ? g_strdup (priority_c) : NULL; + path_cost = path_cost_c ? g_strdup (path_cost_c) : NULL; + hairpin = hairpin_c ? g_strdup (hairpin_c) : NULL; + if (ask) + do_questionnaire_bridge_slave (&priority, &path_cost, &hairpin); + + if (priority) + if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT, + NM_SETTING_BRIDGE_PORT_PRIORITY, &prio_int, error)) + goto cleanup_bridge_slave; + if (path_cost) + if (!bridge_prop_string_to_uint (path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT, + NM_SETTING_BRIDGE_PORT_PATH_COST, &path_cost_int, error)) + goto cleanup_bridge_slave; + if (hairpin) { + GError *tmp_err = NULL; + if (!nmc_string_to_bool (hairpin, &hairpin_bool, &tmp_err)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'hairpin': %s."), tmp_err->message); + g_clear_error (&tmp_err); + goto cleanup_bridge_slave; + } + } + + /* Change properties in 'connection' setting */ + g_object_set (s_con, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_MASTER, checked_master ? checked_master : _strip_master_prefix (master, NULL), + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME, + NULL); + + /* Add ethernet setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + if (priority) + g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PRIORITY, prio_int, NULL); + if (path_cost) + g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PATH_COST, path_cost_int, NULL); + if (hairpin) + g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, hairpin_bool, NULL); + + success = TRUE; +cleanup_bridge_slave: + g_free (master_ask); + g_free (priority); + g_free (path_cost); + g_free (hairpin); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_VPN_SETTING_NAME)) { + /* Build up the settings required for 'vpn' */ + gboolean success = FALSE; + const char *vpn_type = NULL; + char *vpn_type_ask = NULL; + const char *user_c = NULL; + char *user = NULL; + const char *st; + char *service_type = NULL; + nmc_arg_t exp_args[] = { {"vpn-type", TRUE, &vpn_type, !ask}, + {"user", TRUE, &user_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!vpn_type && ask) + vpn_type = vpn_type_ask = nmc_readline (PROMPT_VPN_TYPE); + if (!vpn_type) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'vpn-type' is required.")); + goto cleanup_vpn; + } + if (vpn_type_ask) + vpn_type = g_strstrip (vpn_type_ask); + + if (!(st = nmc_string_is_valid (vpn_type, nmc_known_vpns, NULL))) { + printf (_("Warning: 'vpn-type': %s not known.\n"), vpn_type); + st = vpn_type; + } + service_type = g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, st); + + /* Also ask for all optional arguments if '--ask' is specified. */ + user = user_c ? g_strdup (user_c) : NULL; + if (ask) + do_questionnaire_vpn (&user); + + /* Add 'vpn' setting */ + s_vpn = (NMSettingVPN *) nm_setting_vpn_new (); + nm_connection_add_setting (connection, NM_SETTING (s_vpn)); + + g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service_type, NULL); + g_object_set (s_vpn, NM_SETTING_VPN_USER_NAME, user, NULL); + + success = TRUE; +cleanup_vpn: + g_free (vpn_type_ask); + g_free (service_type); + g_free (user); + if (!success) + return FALSE; + + } else if (!strcmp (con_type, NM_SETTING_OLPC_MESH_SETTING_NAME)) { + /* Build up the settings required for 'olpc' */ + gboolean success = FALSE; + char *ssid_ask = NULL; + const char *ssid = NULL; + GByteArray *ssid_arr; + const char *channel_c = NULL; + char *channel = NULL; + unsigned long chan; + const char *dhcp_anycast_c = NULL; + char *dhcp_anycast = NULL; + GByteArray *array = NULL; + nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask}, + {"channel", TRUE, &channel_c, FALSE}, + {"dhcp-anycast", TRUE, &dhcp_anycast_c, FALSE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + return FALSE; + + if (!ssid && ask) + ssid = ssid_ask = nmc_readline (_("SSID: ")); + if (!ssid) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'ssid' is required.")); + goto cleanup_olpc; + } + + /* Also ask for all optional arguments if '--ask' is specified. */ + channel = channel_c ? g_strdup (channel_c) : NULL; + dhcp_anycast = dhcp_anycast_c ? g_strdup (dhcp_anycast_c) : NULL; + if (ask) + do_questionnaire_olpc (&channel, &dhcp_anycast); + + if (channel) { + if (!nmc_string_to_uint (channel, TRUE, 1, 13, &chan)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'channel': '%s' is not valid; use <1-13>."), + channel); + goto cleanup_olpc; + } + } + if (!check_and_convert_mac (dhcp_anycast, &array, ARPHRD_ETHER, "dhcp-anycast", error)) + goto cleanup_olpc; + + /* Add OLPC mesh setting */ + s_olpc_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new (); + nm_connection_add_setting (connection, NM_SETTING (s_olpc_mesh)); + + ssid_arr = g_byte_array_sized_new (strlen (ssid)); + g_byte_array_append (ssid_arr, (const guint8 *) ssid, strlen (ssid)); + g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_SSID, ssid_arr, NULL); + if (channel) + g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, chan, NULL); else - g_string_printf (nmc->return_text, _("Error: No suitable device found.")); + g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, 1, NULL); + if (array) { + g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, array, NULL); + g_byte_array_free (array, TRUE); + } + g_byte_array_free (ssid_arr, TRUE); + + success = TRUE; +cleanup_olpc: + g_free (ssid_ask); + g_free (channel); + g_free (dhcp_anycast); + if (!success) + return FALSE; + + } else { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s' is not a valid connection type."), + con_type); + return FALSE; + } + + /* Read and add IP configuration */ + if ( strcmp (con_type, "bond-slave") != 0 + && strcmp (con_type, "team-slave") != 0 + && strcmp (con_type, "bridge-slave") != 0) { + + NMIP4Address *ip4addr = NULL; + NMIP6Address *ip6addr = NULL; + const char *ip4 = NULL, *gw4 = NULL, *ip6 = NULL, *gw6 = NULL; + nmc_arg_t exp_args[] = { {"ip4", TRUE, &ip4, FALSE}, {"gw4", TRUE, &gw4, FALSE}, + {"ip6", TRUE, &ip6, FALSE}, {"gw6", TRUE, &gw6, FALSE}, + {NULL} }; + + while (argc) { + nmc_arg_t *p; + + /* reset 'found' flag */ + for (p = exp_args; p->name; p++) + p->found = FALSE; + + ip4 = gw4 = ip6 = gw6 = NULL; + + if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error)) + return FALSE; + + /* coverity[dead_error_begin] */ + if (ip4) { + ip4addr = nmc_parse_and_build_ip4_address (ip4, gw4, error); + if (!ip4addr) { + g_prefix_error (error, _("Error: ")); + return FALSE; + } + add_ip4_address_to_connection (ip4addr, connection); + } + + /* coverity[dead_error_begin] */ + if (ip6) { + ip6addr = nmc_parse_and_build_ip6_address (ip6, gw6, error); + if (!ip6addr) { + g_prefix_error (error, _("Error: ")); + return FALSE; + } + add_ip6_address_to_connection (ip6addr, connection); + } + } + + /* Ask for addresses if '--ask' is specified. */ + if (ask) + do_questionnaire_ip (connection); + } + + return TRUE; +} + +static char * +unique_connection_name (GSList *list, const char *try_name) +{ + NMConnection *connection; + const char *name; + char *new_name; + unsigned int num = 1; + GSList *iterator = list; + + new_name = g_strdup (try_name); + while (iterator) { + connection = NM_CONNECTION (iterator->data); + + name = nm_connection_get_id (connection); + if (g_strcmp0 (new_name, name) == 0) { + g_free (new_name); + new_name = g_strdup_printf ("%s-%d", try_name, num++); + iterator = list; + } + iterator = g_slist_next (iterator); + } + return new_name; +} + +typedef struct { + NmCli *nmc; + char *con_name; +} AddConnectionInfo; + +static void +add_connection_cb (NMRemoteSettings *settings, + NMRemoteConnection *connection, + GError *error, + gpointer user_data) +{ + AddConnectionInfo *info = (AddConnectionInfo *) user_data; + NmCli *nmc = info->nmc; + + if (error) { + g_string_printf (nmc->return_text, + _("Error: Failed to add '%s' connection: (%d) %s"), + info->con_name, error->code, error->message); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; + } else { + printf (_("Connection '%s' (%s) successfully added.\n"), + nm_connection_get_id (NM_CONNECTION (connection)), + nm_connection_get_uuid (NM_CONNECTION (connection))); + } + + g_free (info->con_name); + g_free (info); + quit (); +} + +static gboolean +add_new_connection (gboolean persistent, + NMRemoteSettings *settings, + NMConnection *connection, + NMRemoteSettingsAddConnectionFunc callback, + gpointer user_data) +{ + if (persistent) + return nm_remote_settings_add_connection (settings, connection, callback, user_data); + else + return nm_remote_settings_add_connection_unsaved (settings, connection, callback, user_data); +} + +static void +update_connection (gboolean persistent, + NMRemoteConnection *connection, + NMRemoteConnectionResultFunc callback, + gpointer user_data) +{ + if (persistent) + nm_remote_connection_commit_changes (connection, callback, user_data); + else + nm_remote_connection_commit_changes_unsaved (connection, callback, user_data); +} + +static char * +gen_func_vpn_types (const char *text, int state) +{ + return nmc_rl_gen_func_basic (text, state, nmc_known_vpns); +} + +static char * +gen_func_bool_values_l10n (const char *text, int state) +{ + const char *words[] = { WORD_LOC_YES, WORD_LOC_NO, NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_func_ib_type (const char *text, int state) +{ + const char *words[] = { "datagram", "connected", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_func_bt_type (const char *text, int state) +{ + const char *words[] = { "panu", "dun-gsm", "dun-cdma", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_func_bond_mode (const char *text, int state) +{ + const char *words[] = { "balance-rr", "active-backup", "balance-xor", "broadcast", + "802.3ad", "balance-tlb", "balance-alb", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} +static char * +gen_func_bond_mon_mode (const char *text, int state) +{ + const char *words[] = { "miimon", "arp", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_func_master_ifnames (const char *text, int state) +{ + GSList *iter; + GPtrArray *ifnames; + char *ret; + NMConnection *con; + NMSettingConnection *s_con; + const char *con_type, *ifname; + + if (!nm_cli.system_connections) + return NULL; + + /* Disable appending space after completion */ + rl_completion_append_character = '\0'; + + ifnames = g_ptr_array_sized_new (20); + for (iter = nm_cli.system_connections; iter; iter = g_slist_next (iter)) { + con = NM_CONNECTION (iter->data); + s_con = nm_connection_get_setting_connection (con); + g_assert (s_con); + con_type = nm_setting_connection_get_connection_type (s_con); + if (g_strcmp0 (con_type, nmc_tab_completion.con_type) != 0) + continue; + ifname = nm_connection_get_virtual_iface_name (con); + g_ptr_array_add (ifnames, (gpointer) ifname); + } + g_ptr_array_add (ifnames, (gpointer) NULL); + + ret = nmc_rl_gen_func_basic (text, state, (const char **) ifnames->pdata); + + g_ptr_array_free (ifnames, TRUE); + return ret; +} + +static gboolean +is_single_word (const char* line) +{ + size_t n1, n2, n3; + + n1 = strspn (line, " \t"); + n2 = strcspn (line+n1, " \t\0") + n1; + n3 = strspn (line+n2, " \t"); + + if (n3 == 0) + return TRUE; + else + return FALSE; +} + +static char ** +nmcli_con_add_tab_completion (const char *text, int start, int end) +{ + char **match_array = NULL; + rl_compentry_func_t *generator_func = NULL; + + /* Disable readline's default filename completion */ + rl_attempted_completion_over = 1; + + /* Restore standard append character to space */ + rl_completion_append_character = ' '; + + if (!is_single_word (rl_line_buffer)) + return NULL; + + if (g_strcmp0 (rl_prompt, PROMPT_CON_TYPE) == 0) + generator_func = gen_connection_types; + else if (g_strcmp0 (rl_prompt, PROMPT_VPN_TYPE) == 0) + generator_func = gen_func_vpn_types; + else if ( g_strcmp0 (rl_prompt, PROMPT_BOND_MASTER) == 0 + || g_strcmp0 (rl_prompt, PROMPT_TEAM_MASTER) == 0 + || g_strcmp0 (rl_prompt, PROMPT_BRIDGE_MASTER) == 0) + generator_func = gen_func_master_ifnames; + else if ( g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL)) + || g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, ":")) + || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, NULL)) + || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, ":"))) + generator_func = gen_func_bool_values_l10n; + else if (g_str_has_suffix (rl_prompt, PROMPT_IB_MODE)) + generator_func = gen_func_ib_type; + else if (g_str_has_suffix (rl_prompt, PROMPT_BT_TYPE)) + generator_func = gen_func_bt_type; + else if (g_str_has_prefix (rl_prompt, PROMPT_BOND_MODE)) + generator_func = gen_func_bond_mode; + else if (g_str_has_suffix (rl_prompt, PROMPT_BOND_MON_MODE)) + generator_func = gen_func_bond_mon_mode; + + if (generator_func) + match_array = rl_completion_matches (text, generator_func); + + return match_array; +} + +static NMCResultCode +do_connection_add (NmCli *nmc, int argc, char **argv) +{ + NMConnection *connection = NULL; + NMSettingConnection *s_con; + char *uuid; + char *default_name = NULL; + const char *type = NULL; + char *type_ask = NULL; + const char *con_name = NULL; + const char *autoconnect = NULL; + gboolean auto_bool = TRUE; + const char *ifname = NULL; + char *ifname_ask = NULL; + gboolean ifname_mandatory = TRUE; + const char *save = NULL; + gboolean save_bool = TRUE; + AddConnectionInfo *info = NULL; + const char *setting_name; + GError *error = NULL; + nmc_arg_t exp_args[] = { {"type", TRUE, &type, !nmc->ask}, + {"con-name", TRUE, &con_name, FALSE}, + {"autoconnect", TRUE, &autoconnect, FALSE}, + {"ifname", TRUE, &ifname, FALSE}, + {"save", TRUE, &save, FALSE}, + {NULL} }; + + rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_add_tab_completion; + + nmc->return_value = NMC_RESULT_SUCCESS; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) { + g_string_assign (nmc->return_text, error->message); + nmc->return_value = error->code; + g_clear_error (&error); + goto error; + } + + if (!type && nmc->ask) { + char *types_tmp = get_valid_options_string (nmc_valid_connection_types); + printf ("Valid types: [%s]\n", types_tmp); + type = type_ask = nmc_readline (PROMPT_CON_TYPE); + g_free (types_tmp); + } + if (!type) { + g_string_printf (nmc->return_text, _("Error: 'type' argument is required.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + if (type_ask) + type = g_strstrip (type_ask); + + if (!(setting_name = check_valid_name (type, nmc_valid_connection_types, &error))) { + g_string_printf (nmc->return_text, _("Error: invalid connection type; %s."), + error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_clear_error (&error); + goto error; + } + if (autoconnect) { + GError *tmp_err = NULL; + if (!nmc_string_to_bool (autoconnect, &auto_bool, &tmp_err)) { + g_string_printf (nmc->return_text, _("Error: 'autoconnect': %s."), + tmp_err->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_clear_error (&tmp_err); + goto error; + } + } + if (save) { + GError *tmp_err = NULL; + if (!nmc_string_to_bool (save, &save_bool, &tmp_err)) { + g_string_printf (nmc->return_text, _("Error: 'save': %s."), + tmp_err->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_clear_error (&tmp_err); + goto error; + } + } + + /* ifname is mandatory for all connection types except virtual ones (bond, team, bridge, vlan) */ + if ( strcmp (type, NM_SETTING_BOND_SETTING_NAME) == 0 + || strcmp (type, NM_SETTING_TEAM_SETTING_NAME) == 0 + || strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME) == 0 + || strcmp (type, NM_SETTING_VLAN_SETTING_NAME) == 0) + ifname_mandatory = FALSE; + + if (!ifname && ifname_mandatory && nmc->ask) { + ifname = ifname_ask = nmc_readline (_("Interface name [*]: ")); + if (!ifname) + ifname = ifname_ask = g_strdup ("*"); + } + if (!ifname && ifname_mandatory) { + g_string_printf (nmc->return_text, _("Error: 'ifname' argument is required.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + if (ifname) { + if (!nm_utils_iface_valid_name (ifname) && strcmp (ifname, "*") != 0) { + g_string_printf (nmc->return_text, + _("Error: 'ifname': '%s' is not a valid interface nor '*'."), + ifname); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + /* Special value of '*' means no specific interface name */ + if (strcmp (ifname, "*") == 0) + ifname = NULL; + } + + /* Create a new connection object */ + connection = nm_connection_new (); + + /* Build up the 'connection' setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + uuid = nm_utils_uuid_generate (); + if (con_name) + default_name = g_strdup (con_name); + else { + char *try_name = ifname ? + g_strdup_printf ("%s-%s", get_name_alias (setting_name, nmc_valid_connection_types), ifname) + : g_strdup (get_name_alias (setting_name, nmc_valid_connection_types)); + default_name = unique_connection_name (nmc->system_connections, try_name); + g_free (try_name); + } + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, default_name, + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, setting_name, + NM_SETTING_CONNECTION_AUTOCONNECT, auto_bool, + NM_SETTING_CONNECTION_INTERFACE_NAME, ifname, + NULL); + g_free (uuid); + g_free (default_name); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + if (!complete_connection_by_type (connection, + setting_name, + nmc->system_connections, + nmc->ask, + argc, + argv, + &error)) { + g_string_assign (nmc->return_text, error->message); + nmc->return_value = error->code; g_clear_error (&error); goto error; } - /* Use nowait_flag instead of should_wait because exiting has to be postponed till - * active_connection_state_cb() is called. That gives NM time to check our permissions - * and we can follow activation progress. - */ - nmc->nowait_flag = !wait; nmc->should_wait = TRUE; - info = g_malloc0 (sizeof (ActivateConnectionInfo)); + info = g_malloc0 (sizeof (AddConnectionInfo)); info->nmc = nmc; - info->device = device; + info->con_name = g_strdup (nm_connection_get_id (connection)); - nm_client_activate_connection (nmc->client, - connection, - device, - spec_object, - activate_connection_cb, - info); + /* Tell the settings service to add the new connection */ + add_new_connection (save_bool, + nmc->system_settings, + connection, + add_connection_cb, + info); - /* Start progress indication */ - if (nmc->print_output == NMC_PRINT_PRETTY) - progress_id = g_timeout_add (120, progress_cb, "preparing"); + if (connection) + g_object_unref (connection); return nmc->return_value; + error: + if (connection) + g_object_unref (connection); + g_free (type_ask); + g_free (ifname_ask); + nmc->should_wait = FALSE; return nmc->return_value; } -static NMCResultCode -do_connection_down (NmCli *nmc, int argc, char **argv) + +/*----------------------------------------------------------------------------*/ +/* Functions for readline TAB completion in editor */ + +static void +uuid_display_hook (char **array, int len, int max_len) { - NMConnection *connection = NULL; - NMActiveConnection *active = NULL; - GError *error = NULL; - const GPtrArray *active_cons; - const char *con_path; - const char *active_path; - gboolean id_specified = FALSE; - gboolean wait = TRUE; - int i; + NMConnection *con; + int i, max = 0; + char *tmp; + const char *id; - while (argc > 0) { - if (strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0) { - const char *selector = *argv; - id_specified = TRUE; + for (i = 1; i <= len; i++) { + con = nmc_find_connection (nmc_tab_completion.nmc->system_connections, "uuid", array[i], NULL); + id = con ? nm_connection_get_id (con) : NULL; + if (id) { + tmp = g_strdup_printf ("%s (%s)", array[i], id); + g_free (array[i]); + array[i] = tmp; + if (max < strlen (id)) + max = strlen (id); + } + } + rl_display_match_list (array, len, max_len + max + 3); + rl_forced_update_display (); +} - if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; +static char *pre_input_deftext; +static int +set_deftext (void) +{ + if (pre_input_deftext && rl_startup_hook) { + rl_insert_text (pre_input_deftext); + g_free (pre_input_deftext); + pre_input_deftext = NULL; + rl_startup_hook = NULL; + } + return 0; +} + +static char * +gen_nmcli_cmds_menu (const char *text, int state) +{ + const char *words[] = { "goto", "set", "remove", "describe", "print", "verify", + "save", "activate", "back", "help", "quit", "nmcli", + NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_nmcli_cmds_submenu (const char *text, int state) +{ + const char *words[] = { "set", "add", "change", "remove", "describe", + "print", "back", "help", "quit", + NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_cmd_nmcli (const char *text, int state) +{ + const char *words[] = { "status-line", "save-confirmation", "prompt-color", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_cmd_nmcli_prompt_color (const char *text, int state) +{ + const char *words[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_func_bool_values (const char *text, int state) +{ + const char *words[] = { "yes", "no", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_cmd_verify0 (const char *text, int state) +{ + const char *words[] = { "all", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_cmd_print0 (const char *text, int state) +{ + static char **words = NULL; + char *ret = NULL; + + if (!state) { + GHashTable *settings; + GHashTableIter iter; + const char *setting_name; + int i = 0; + + settings = nm_connection_to_hash (nmc_tab_completion.connection, NM_SETTING_HASH_FLAG_NO_SECRETS); + words = g_new (char *, g_hash_table_size (settings) + 2); + g_hash_table_iter_init (&iter, settings); + while (g_hash_table_iter_next (&iter, (gpointer) &setting_name, NULL)) + words [i++] = g_strdup (setting_name); + words[i++] = g_strdup ("all"); + words[i] = NULL; + g_hash_table_unref (settings); + } + + if (words) { + ret = nmc_rl_gen_func_basic (text, state, (const char **) words); + if (ret == NULL) { + g_strfreev (words); + words = NULL; + } + } + return ret; +} + +static char * +gen_cmd_print2 (const char *text, int state) +{ + const char *words[] = { "setting", "connection", "all", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_cmd_save (const char *text, int state) +{ + const char *words[] = { "persistent", "temporary", NULL }; + return nmc_rl_gen_func_basic (text, state, words); +} + +static char * +gen_connection_types (const char *text, int state) +{ + static int list_idx, len; + const char *c_type, *a_type; + + if (!state) { + list_idx = 0; + len = strlen (text); + } + + while (nmc_valid_connection_types[list_idx].name) { + a_type = nmc_valid_connection_types[list_idx].alias; + c_type = nmc_valid_connection_types[list_idx].name; + list_idx++; + if (a_type && !strncmp (text, a_type, len)) + return g_strdup (a_type); + if (c_type && !strncmp (text, c_type, len)) + return g_strdup (c_type); + } + + return NULL; +} + +static char * +gen_setting_names (const char *text, int state) +{ + static int list_idx, len; + const char *s_name, *a_name; + const NameItem *valid_settings_arr; + + if (!state) { + list_idx = 0; + len = strlen (text); + } + + valid_settings_arr = get_valid_settings_array (nmc_tab_completion.con_type); + if (!valid_settings_arr) + return NULL; + while (valid_settings_arr[list_idx].name) { + a_name = valid_settings_arr[list_idx].alias; + s_name = valid_settings_arr[list_idx].name; + list_idx++; + if (len == 0 && a_name) + return g_strdup_printf ("%s (%s)", s_name, a_name); + if (a_name && !strncmp (text, a_name, len)) + return g_strdup (a_name); + if (s_name && !strncmp (text, s_name, len)) + return g_strdup (s_name); + } + return NULL; +} + +static char * +gen_property_names (const char *text, int state) +{ + NMSetting *setting = NULL; + char **valid_props = NULL; + char *ret = NULL; + const char *line = rl_line_buffer; + const char *setting_name; + char **strv = NULL; + const NameItem *valid_settings_arr; + const char *p1; + + /* Try to get the setting from 'line' - setting_name.property */ + p1 = strchr (line, '.'); + if (p1) { + while (p1 > line && !g_ascii_isspace (*p1)) + p1--; + + strv = g_strsplit (p1+1, ".", 2); + + valid_settings_arr = get_valid_settings_array (nmc_tab_completion.con_type); + setting_name = check_valid_name (strv[0], valid_settings_arr, NULL); + setting = nmc_setting_new_for_name (setting_name); + } else { + /* Else take the current setting, if any */ + setting = nmc_tab_completion.setting ? g_object_ref (nmc_tab_completion.setting) : NULL; + } + + if (setting) { + valid_props = nmc_setting_get_valid_properties (setting); + ret = nmc_rl_gen_func_basic (text, state, (const char **) valid_props); + } + + g_strfreev (strv); + g_strfreev (valid_props); + if (setting) + g_object_unref (setting); + return ret; +} + +static char * +gen_compat_devices (const char *text, int state) +{ + int i, j = 0; + const GPtrArray *devices; + const char **compatible_devices; + char *ret; + + devices = nm_client_get_devices (nmc_tab_completion.nmc->client); + if (!devices || devices->len < 1) + return NULL; + + compatible_devices = g_new (const char *, devices->len + 1); + for (i = 0; i < devices->len; i++) { + NMDevice *dev = g_ptr_array_index (devices, i); + const char *ifname = nm_device_get_iface (dev); + NMDevice *device = NULL; + const char *spec_object = NULL; + + if (find_device_for_connection (nmc_tab_completion.nmc, nmc_tab_completion.connection, + ifname, NULL, NULL, &device, &spec_object, NULL)) { + compatible_devices[j++] = ifname; + } + } + compatible_devices[j] = NULL; + + ret = nmc_rl_gen_func_basic (text, state, compatible_devices); + + g_free (compatible_devices); + return ret; +} + +static char * +gen_vpn_uuids (const char *text, int state) +{ + GSList *iter; + guint len; + int i = 0; + const char **uuids; + char *ret; + + len = g_slist_length (nmc_tab_completion.nmc->system_connections); + if (len < 1) + return NULL; + + uuids = g_new (const char *, len + 1); + for (iter = nmc_tab_completion.nmc->system_connections; iter; iter = g_slist_next (iter)) { + const char *type = nm_connection_get_connection_type (NM_CONNECTION (iter->data)); + + if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) == 0) + uuids[i++] = nm_connection_get_uuid (NM_CONNECTION (iter->data)); + } + uuids[i] = NULL; + + ret = nmc_rl_gen_func_basic (text, state, uuids); + + g_free (uuids); + return ret; +} + +static rl_compentry_func_t * +get_gen_func_cmd_nmcli (const char *str) +{ + if (!str) + return NULL; + if (matches (str, "status-line") == 0) + return gen_func_bool_values; + if (matches (str, "save-confirmation") == 0) + return gen_func_bool_values; + if (matches (str, "prompt-color") == 0) + return gen_cmd_nmcli_prompt_color; + return NULL; +} + +/* + * Helper function parsing line for completion. + * IN: + * line : the whole line to be parsed + * end : the position of cursor in the line + * cmd : command to match + * OUT: + * cw_num : is set to the word number being completed (1, 2, 3, 4). + * prev_word : returns the previous word (so that we have some context). + * + * Returns TRUE when the first word of the 'line' matches 'cmd'. + * + * Examples: + * line="rem" cmd="remove" -> TRUE cw_num=1 + * line="set con" cmd="set" -> TRUE cw_num=2 + * line="go ipv4.method" cmd="goto" -> TRUE cw_num=2 + * line=" des eth.mtu " cmd="describe" -> TRUE cw_num=3 + * line=" bla ipv4.method" cmd="goto" -> FALSE + */ +static gboolean +should_complete_cmd (const char *line, int end, const char *cmd, + int *cw_num, char **prev_word) +{ + char *tmp; + const char *word1, *word2, *word3; + size_t n1, n2, n3, n4, n5, n6; + gboolean word1_done, word2_done, word3_done; + gboolean ret = FALSE; + + if (!line) + return FALSE; + + tmp = g_strdup (line); + + n1 = strspn (tmp, " \t"); + n2 = strcspn (tmp+n1, " \t\0") + n1; + n3 = strspn (tmp+n2, " \t") + n2; + n4 = strcspn (tmp+n3, " \t\0") + n3; + n5 = strspn (tmp+n4, " \t") + n4; + n6 = strcspn (tmp+n5, " \t\0") + n5; + + word1_done = end > n2; + word2_done = end > n4; + word3_done = end > n6; + tmp[n2] = tmp[n4] = tmp[n6] = '\0'; + + word1 = tmp[n1] ? tmp + n1 : NULL; + word2 = tmp[n3] ? tmp + n3 : NULL; + word3 = tmp[n5] ? tmp + n5 : NULL; + + if (!word1_done) { + if (cw_num) + *cw_num = 1; + if (prev_word) + *prev_word = NULL; + } else if (!word2_done) { + if (cw_num) + *cw_num = 2; + if (prev_word) + *prev_word = g_strdup (word1); + } else if (!word3_done) { + if (cw_num) + *cw_num = 3; + if (prev_word) + *prev_word = g_strdup (word2); + } else { + if (cw_num) + *cw_num = 4; + if (prev_word) + *prev_word = g_strdup (word3); + } + + if (word1 && matches (word1, cmd) == 0) + ret = TRUE; + + g_free (tmp); + return ret; +} + +static char * +extract_property_name (const char *prompt, const char *line) +{ + char *prop = NULL; + + /* If prompt is set take the property name from it, else extract it from line */ + if (!prompt) { + const char *p1; + size_t num; + p1 = strchr (line, '.'); + if (p1) { + p1++; + } else { + size_t n1, n2, n3; + n1 = strspn (line, " \t"); + n2 = strcspn (line+n1, " \t\0") + n1; + n3 = strspn (line+n2, " \t") + n2; + p1 = line + n3; + } + num = strcspn (p1, " \t\0"); + prop = g_strndup (p1, num); + } else { + const char *p1, *dot; + size_t num; + p1 = strchr (prompt, ' '); + /* prompt looks like this: "nmcli 802-1x>" or "nmcli 802-1x.pac-file>" */ + if (p1) { + dot = strchr (p1 + 1, '.'); + p1 = dot ? dot + 1 : p1; + num = strcspn (p1, ">"); + prop = g_strndup (p1, num); + } + } + + return prop; +} + +static gboolean +should_complete_files (const char *prompt, const char *line) +{ + char *prop; + gboolean found = FALSE; + const char *file_properties[] = { + /* '802-1x' properties */ + "ca-cert", + "ca-path", + "client-cert", + "pac-file", + "phase2-ca-cert", + "phase2-ca-path", + "phase2-client-cert", + "private-key", + "phase2-private-key", + /* 'team' and 'team-port' properties */ + "config", + NULL + }; + + prop = extract_property_name (prompt, line); + if (prop) { + found = !!nmc_string_is_valid (prop, file_properties, NULL); + g_free (prop); + } + return found; +} + +static gboolean +should_complete_vpn_uuids (const char *prompt, const char *line) +{ + char *prop; + gboolean found = FALSE; + const char *uuid_properties[] = { + /* 'connection' properties */ + "secondaries", + NULL + }; + + prop = extract_property_name (prompt, line); + if (prop) { + found = !!nmc_string_is_valid (prop, uuid_properties, NULL); + g_free (prop); + } + return found; +} + +/* from readline */ +extern int rl_complete_with_tilde_expansion; + +/* + * Attempt to complete on the contents of TEXT. START and END show the + * region of TEXT that contains the word to complete. We can use the + * entire line in case we want to do some simple parsing. Return the + * array of matches, or NULL if there aren't any. + */ +static char ** +nmcli_editor_tab_completion (const char *text, int start, int end) +{ + char **match_array = NULL; + const char *line = rl_line_buffer; + const char *prompt = rl_prompt; + rl_compentry_func_t *generator_func = NULL; + gboolean copy_char; + const char *p1; + char *p2, *prompt_tmp; + char *word = NULL; + size_t n1; + int num; + + /* Restore standard append character to space */ + rl_completion_append_character = ' '; + + /* Restore standard function for displaying matches */ + rl_completion_display_matches_hook = NULL; + + /* Disable default filename completion */ + rl_attempted_completion_over = 1; + + /* Enable tilde expansion when filenames are completed */ + rl_complete_with_tilde_expansion = 1; + + /* Filter out possible ANSI color escape sequences */ + p1 = prompt; + p2 = prompt_tmp = g_strdup (prompt); + copy_char = TRUE; + while (*p1) { + if (*p1 == '\33') + copy_char = FALSE; + if (copy_char) + *p2++ = *p1; + if (!copy_char && *p1 == 'm') + copy_char = TRUE; + p1++; + } + *p2 = '\0'; + + /* Find the first non-space character */ + n1 = strspn (line, " \t"); + + /* Choose the right generator function */ + if (strcmp (prompt_tmp, EDITOR_PROMPT_CON_TYPE) == 0) + generator_func = gen_connection_types; + else if (strcmp (prompt_tmp, EDITOR_PROMPT_SETTING) == 0) + generator_func = gen_setting_names; + else if (strcmp (prompt_tmp, EDITOR_PROMPT_PROPERTY) == 0) + generator_func = gen_property_names; + else if ( g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL)) + || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, NULL))) + generator_func = gen_func_bool_values_l10n; + else if (g_str_has_prefix (prompt_tmp, "nmcli")) { + if (!strchr (prompt_tmp, '.')) { + int level = g_str_has_prefix (prompt_tmp, "nmcli>") ? 0 : 1; + const char *dot = strchr (line, '.'); + + /* Main menu - level 0,1 */ + if (start == n1) + generator_func = gen_nmcli_cmds_menu; + else { + if (should_complete_cmd (line, end, "goto", &num, NULL) && num <= 2) { + if (level == 0 && (!dot || dot >= line + end)) + generator_func = gen_setting_names; + else + generator_func = gen_property_names; + } else if (should_complete_cmd (line, end, "set", &num, NULL)) { + if (num < 3) { + if (level == 0 && (!dot || dot >= line + end)) { + generator_func = gen_setting_names; + rl_completion_append_character = '.'; + } else + generator_func = gen_property_names; + } else if (num >= 3) { + if (num == 3 && should_complete_files (NULL, line)) + rl_attempted_completion_over = 0; + if (should_complete_vpn_uuids (NULL, line)) { + rl_completion_display_matches_hook = uuid_display_hook; + generator_func = gen_vpn_uuids; + } + } + } else if ( ( should_complete_cmd (line, end, "remove", &num, NULL) + || should_complete_cmd (line, end, "describe", &num, NULL)) + && num <= 2) { + if (level == 0 && (!dot || dot >= line + end)) { + generator_func = gen_setting_names; + rl_completion_append_character = '.'; + } else + generator_func = gen_property_names; + } else if (should_complete_cmd (line, end, "nmcli", &num, &word)) { + if (num < 3) + generator_func = gen_cmd_nmcli; + else if (num == 3) + generator_func = get_gen_func_cmd_nmcli (word); + } else if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2) { + if (level == 0 && (!dot || dot >= line + end)) + generator_func = gen_cmd_print0; + else + generator_func = gen_property_names; + } else if (should_complete_cmd (line, end, "verify", &num, NULL) && num <= 2) { + generator_func = gen_cmd_verify0; + } else if (should_complete_cmd (line, end, "activate", &num, NULL) && num <= 2) { + generator_func = gen_compat_devices; + } else if (should_complete_cmd (line, end, "save", &num, NULL) && num <= 2) { + generator_func = gen_cmd_save; + } else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2) + generator_func = gen_nmcli_cmds_menu; } + } else { + /* Submenu - level 2 */ + if (start == n1) + generator_func = gen_nmcli_cmds_submenu; + else { + if ( should_complete_cmd (line, end, "add", &num, NULL) + || should_complete_cmd (line, end, "set", &num, NULL)) { + if (num <= 2 && should_complete_files (prompt_tmp, line)) + rl_attempted_completion_over = 0; + else if (should_complete_vpn_uuids (prompt_tmp, line)) { + rl_completion_display_matches_hook = uuid_display_hook; + generator_func = gen_vpn_uuids; + } + } + if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2) + generator_func = gen_cmd_print2; + else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2) + generator_func = gen_nmcli_cmds_submenu; + } + } + } - connection = find_connection (nmc->system_connections, selector, *argv); + if (generator_func) + match_array = rl_completion_matches (text, generator_func); - if (!connection) { - g_string_printf (nmc->return_text, _("Error: Unknown connection: %s."), *argv); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - goto error; + g_free (prompt_tmp); + g_free (word); + return match_array; +} + +#define NMCLI_EDITOR_HISTORY ".nmcli-history" + +static void +load_history_cmds (const char *uuid) +{ + GKeyFile *kf; + char *filename; + char **keys; + char *line; + size_t i; + GError *err = NULL; + + filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL); + kf = g_key_file_new (); + if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) { + if (err->code == G_KEY_FILE_ERROR_PARSE) + printf ("Warning: %s parse error: %s\n", filename, err->message); + g_key_file_free (kf); + g_free (filename); + return; + } + keys = g_key_file_get_keys (kf, uuid, NULL, NULL); + for (i = 0; keys && keys[i]; i++) { + line = g_key_file_get_string (kf, uuid, keys[i], NULL); + if (line && *line) + add_history (line); + g_free (line); + } + g_strfreev (keys); + g_key_file_free (kf); + g_free (filename); +} + +static void +save_history_cmds (const char *uuid) +{ + HIST_ENTRY **hist = NULL; + GKeyFile *kf; + char *filename; + size_t i; + char *key; + char *data; + gsize len = 0; + GError *err = NULL; + + hist = history_list (); + if (hist) { + filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL); + kf = g_key_file_new (); + if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) { + if ( err->code != G_FILE_ERROR_NOENT + && err->code != G_KEY_FILE_ERROR_NOT_FOUND) { + printf ("Warning: %s parse error: %s\n", filename, err->message); + g_key_file_free (kf); + g_free (filename); + g_clear_error (&err); + return; } + g_clear_error (&err); } - else if (strcmp (*argv, "--nowait") == 0) { - wait = FALSE; + + /* Remove previous history group and save new history entries */ + g_key_file_remove_group (kf, uuid, NULL); + for (i = 0; hist[i]; i++) + { + key = g_strdup_printf ("%zd", i); + g_key_file_set_string (kf, uuid, key, hist[i]->line); + g_free (key); } - else { - fprintf (stderr, _("Unknown parameter: %s\n"), *argv); + + /* Write history to file */ + data = g_key_file_to_data (kf, &len, NULL); + if (data) { + g_file_set_contents (filename, data, len, NULL); + g_free (data); } + g_key_file_free (kf); + g_free (filename); + } +} - argc--; - argv++; +/*----------------------------------------------------------------------------*/ + +static void +editor_show_connection (NMConnection *connection, NmCli *nmc) +{ + nmc->print_output = NMC_PRINT_PRETTY; + nmc->multiline_output = TRUE; + nmc->escape_values = 0; + + /* Remove any previous data */ + nmc_empty_output_fields (nmc); + + nmc_connection_profile_details (connection, nmc); +} + +static void +editor_show_setting (NMSetting *setting, NmCli *nmc) +{ + printf (_("['%s' setting values]\n"), + nm_setting_get_name (setting)); + + nmc->print_output = NMC_PRINT_NORMAL; + nmc->multiline_output = TRUE; + nmc->escape_values = 0; + + /* Remove any previous data */ + nmc_empty_output_fields (nmc); + + setting_details (setting, nmc, NULL); +} + +typedef enum { + NMC_EDITOR_MAIN_CMD_UNKNOWN = 0, + NMC_EDITOR_MAIN_CMD_GOTO, + NMC_EDITOR_MAIN_CMD_REMOVE, + NMC_EDITOR_MAIN_CMD_SET, + NMC_EDITOR_MAIN_CMD_DESCRIBE, + NMC_EDITOR_MAIN_CMD_PRINT, + NMC_EDITOR_MAIN_CMD_VERIFY, + NMC_EDITOR_MAIN_CMD_SAVE, + NMC_EDITOR_MAIN_CMD_ACTIVATE, + NMC_EDITOR_MAIN_CMD_BACK, + NMC_EDITOR_MAIN_CMD_HELP, + NMC_EDITOR_MAIN_CMD_NMCLI, + NMC_EDITOR_MAIN_CMD_QUIT, +} NmcEditorMainCmd; + +static NmcEditorMainCmd +parse_editor_main_cmd (const char *cmd, char **cmd_arg) +{ + NmcEditorMainCmd editor_cmd = NMC_EDITOR_MAIN_CMD_UNKNOWN; + char **vec; + + vec = nmc_strsplit_set (cmd, " \t", 2); + if (g_strv_length (vec) < 1) { + if (cmd_arg) + *cmd_arg = NULL; + return NMC_EDITOR_MAIN_CMD_UNKNOWN; } - if (!id_specified) { - g_string_printf (nmc->return_text, _("Error: id or uuid has to be specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + if (matches (vec[0], "goto") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_GOTO; + else if (matches (vec[0], "remove") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_REMOVE; + else if (matches (vec[0], "set") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_SET; + else if (matches (vec[0], "describe") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_DESCRIBE; + else if (matches (vec[0], "print") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_PRINT; + else if (matches (vec[0], "verify") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_VERIFY; + else if (matches (vec[0], "save") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_SAVE; + else if (matches (vec[0], "activate") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_ACTIVATE; + else if (matches (vec[0], "back") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_BACK; + else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_HELP; + else if (matches (vec[0], "quit") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_QUIT; + else if (matches (vec[0], "nmcli") == 0) + editor_cmd = NMC_EDITOR_MAIN_CMD_NMCLI; + + /* set pointer to command argument */ + if (cmd_arg) + *cmd_arg = vec[1] ? g_strstrip (g_strdup (vec[1])) : NULL; + + g_strfreev (vec); + return editor_cmd; +} + +static void +editor_main_usage (void) +{ + printf ("------------------------------------------------------------------------------\n"); + /* TRANSLATORS: do not translate command names and keywords before :: + * However, you should translate terms enclosed in <>. + */ + printf (_("---[ Main menu ]---\n" + "goto [<setting> | <prop>] :: go to a setting or property\n" + "remove <setting>[.<prop>] | <prop> :: remove setting or reset property value\n" + "set [<setting>.<prop> <value>] :: set property value\n" + "describe [<setting>.<prop>] :: describe property\n" + "print [all | <setting>[.<prop>]] :: print the connection\n" + "verify [all] :: verify the connection\n" + "save [persistent|temporary] :: save the connection\n" + "activate [<ifname>] [/<ap>|<nsp>] :: activate the connection\n" + "back :: go one level up (back)\n" + "help/? [<command>] :: print this help\n" + "nmcli <conf-option> <value> :: nmcli configuration\n" + "quit :: exit nmcli\n")); + printf ("------------------------------------------------------------------------------\n"); +} + +static void +editor_main_help (const char *command) +{ + if (!command) + editor_main_usage (); + else { + /* detailed command descriptions */ + NmcEditorMainCmd cmd = parse_editor_main_cmd (command, NULL); + + switch (cmd) { + case NMC_EDITOR_MAIN_CMD_GOTO: + printf (_("goto <setting>[.<prop>] | <prop> :: enter setting/property for editing\n\n" + "This command enters into a setting or property for editing it.\n\n" + "Examples: nmcli> goto connection\n" + " nmcli connection> goto secondaries\n" + " nmcli> goto ipv4.addresses\n")); + break; + case NMC_EDITOR_MAIN_CMD_REMOVE: + printf (_("remove <setting>[.<prop>] :: remove setting or reset property value\n\n" + "This command removes an entire setting from the connection, or if a property\n" + "is given, resets that property to the default value.\n\n" + "Examples: nmcli> remove wifi-sec\n" + " nmcli> remove eth.mtu\n")); + break; + case NMC_EDITOR_MAIN_CMD_SET: + printf (_("set [<setting>.<prop> <value>] :: set property value\n\n" + "This command sets property value.\n\n" + "Example: nmcli> set con.id My connection\n")); + break; + case NMC_EDITOR_MAIN_CMD_DESCRIBE: + printf (_("describe [<setting>.<prop>] :: describe property\n\n" + "Shows property description. You can consult nm-settings(5) " + "manual page to see all NM settings and properties.\n")); + break; + case NMC_EDITOR_MAIN_CMD_PRINT: + printf (_("print [all] :: print setting or connection values\n\n" + "Shows current property or the whole connection.\n\n" + "Example: nmcli ipv4> print all\n")); + break; + case NMC_EDITOR_MAIN_CMD_VERIFY: + printf (_("verify [all] :: verify setting or connection validity\n\n" + "Verifies whether the setting or connection is valid and can " + "be saved later. It indicates invalid values on error.\n\n" + "Examples: nmcli> verify\n" + " nmcli bond> verify\n")); + break; + case NMC_EDITOR_MAIN_CMD_SAVE: + printf (_("save [persistent|temporary] :: save the connection\n\n" + "Sends the connection profile to NetworkManager that either will save it\n" + "persistently, or will only keep it in memory. 'save' without an argument\n" + "means 'save persistent'.\n" + "Note that once you save the profile persistently those settings are saved\n" + "across reboot or restart. Subsequent changes can also be temporary or\n" + "persistent, but any temporary changes will not persist across reboot or\n" + "restart. If you want to fully remove the persistent connection, the connection\n" + "profile must be deleted.\n")); + break; + case NMC_EDITOR_MAIN_CMD_ACTIVATE: + printf (_("activate [<ifname>] [/<ap>|<nsp>] :: activate the connection\n\n" + "Activates the connection.\n\n" + "Available options:\n" + "<ifname> - device the connection will be activated on\n" + "/<ap>|<nsp> - AP (Wi-Fi) or NSP (WiMAX) (prepend with / when <ifname> is not specified)\n")); + break; + case NMC_EDITOR_MAIN_CMD_BACK: + printf (_("back :: go to upper menu level\n\n")); + break; + case NMC_EDITOR_MAIN_CMD_HELP: + printf (_("help/? [<command>] :: help for the nmcli commands\n\n")); + break; + case NMC_EDITOR_MAIN_CMD_NMCLI: + printf (_("nmcli [<conf-option> <value>] :: nmcli configuration\n\n" + "Configures nmcli. The following options are available:\n" + "status-line yes | no [default: no]\n" + "save-confirmation yes | no [default: yes]\n" + "prompt-color <0-8> [default: 0]\n" + " 0 = normal\n" + " 1 = \33[30mblack\33[0m\n" + " 2 = \33[31mred\33[0m\n" + " 3 = \33[32mgreen\33[0m\n" + " 4 = \33[33myellow\33[0m\n" + " 5 = \33[34mblue\33[0m\n" + " 6 = \33[35mmagenta\33[0m\n" + " 7 = \33[36mcyan\33[0m\n" + " 8 = \33[37mwhite\33[0m\n" + "\n" + "Examples: nmcli> nmcli status-line yes\n" + " nmcli> nmcli save-confirmation no\n" + " nmcli> nmcli prompt-color 3\n")); + break; + case NMC_EDITOR_MAIN_CMD_QUIT: + printf (_("quit :: exit nmcli\n\n" + "This command exits nmcli. When the connection being edited " + "is not saved, the user is asked to confirm the action.\n")); + break; + default: + printf (_("Unknown command: '%s'\n"), command); + break; + } } +} - if (!nmc_is_nm_running (nmc, &error)) { - if (error) { - g_string_printf (nmc->return_text, _("Error: Can't find out if NetworkManager is running: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - g_error_free (error); +typedef enum { + NMC_EDITOR_SUB_CMD_UNKNOWN = 0, + NMC_EDITOR_SUB_CMD_SET, + NMC_EDITOR_SUB_CMD_ADD, + NMC_EDITOR_SUB_CMD_CHANGE, + NMC_EDITOR_SUB_CMD_REMOVE, + NMC_EDITOR_SUB_CMD_DESCRIBE, + NMC_EDITOR_SUB_CMD_PRINT, + NMC_EDITOR_SUB_CMD_BACK, + NMC_EDITOR_SUB_CMD_HELP, + NMC_EDITOR_SUB_CMD_QUIT +} NmcEditorSubCmd; + +static NmcEditorSubCmd +parse_editor_sub_cmd (const char *cmd, char **cmd_arg) +{ + NmcEditorSubCmd editor_cmd = NMC_EDITOR_SUB_CMD_UNKNOWN; + char **vec; + + vec = nmc_strsplit_set (cmd, " \t", 2); + if (g_strv_length (vec) < 1) { + if (cmd_arg) + *cmd_arg = NULL; + return NMC_EDITOR_SUB_CMD_UNKNOWN; + } + + if (matches (vec[0], "set") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_SET; + else if (matches (vec[0], "add") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_ADD; + else if (matches (vec[0], "change") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_CHANGE; + else if (matches (vec[0], "remove") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_REMOVE; + else if (matches (vec[0], "describe") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_DESCRIBE; + else if (matches (vec[0], "print") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_PRINT; + else if (matches (vec[0], "back") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_BACK; + else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_HELP; + else if (matches (vec[0], "quit") == 0) + editor_cmd = NMC_EDITOR_SUB_CMD_QUIT; + + /* set pointer to command argument */ + if (cmd_arg) + *cmd_arg = g_strdup (vec[1]); + + g_strfreev (vec); + return editor_cmd; +} + +static void +editor_sub_help (void) +{ + printf ("------------------------------------------------------------------------------\n"); + /* TRANSLATORS: do not translate command names and keywords before :: + * However, you should translate terms enclosed in <>. + */ + printf (_("---[ Property menu ]---\n" + "set [<value>] :: set new value\n" + "add [<value>] :: add new option to the property\n" + "change :: change current value\n" + "remove [<index> | <option>] :: delete the value\n" + "describe :: describe property\n" + "print [setting | connection] :: print property (setting/connection) value(s)\n" + "back :: go to upper level\n" + "help/? [<command>] :: print this help or command description\n" + "quit :: exit nmcli\n")); + printf ("------------------------------------------------------------------------------\n"); +} + +static void +editor_sub_usage (const char *command) +{ + + if (!command) + editor_sub_help (); + else { + /* detailed command descriptions */ + NmcEditorSubCmd cmdsub = parse_editor_sub_cmd (command, NULL); + + switch (cmdsub) { + case NMC_EDITOR_SUB_CMD_SET: + printf (_("set [<value>] :: set new value\n\n" + "This command sets provided <value> to this property\n")); + break; + case NMC_EDITOR_SUB_CMD_ADD: + printf (_("add [<value>] :: append new value to the property\n\n" + "This command adds provided <value> to this property, if " + "the property is of a container type. For single-valued " + "properties the property value is replaced (same as 'set').\n")); + break; + case NMC_EDITOR_SUB_CMD_CHANGE: + printf (_("change :: change current value\n\n" + "Displays current value and allows editing it.\n")); + break; + case NMC_EDITOR_SUB_CMD_REMOVE: + printf (_("remove [<value>|<index>|<option name>] :: delete the value\n\n" + "Removes the property value. For single-valued properties, this sets the\n" + "property back to its default value. For container-type properties, this removes\n" + "all the values of that property, or you can specify an argument to remove just\n" + "a single item or option. The argument is either a value or index of the item to\n" + "remove, or an option name (for properties with named options).\n\n" + "Examples: nmcli ipv4.dns> remove 8.8.8.8\n" + " nmcli ipv4.dns> remove 2\n" + " nmcli bond.options> remove downdelay\n\n")); + break; + case NMC_EDITOR_SUB_CMD_DESCRIBE: + printf (_("describe :: describe property\n\n" + "Shows property description. You can consult nm-settings(5) " + "manual page to see all NM settings and properties.\n")); + break; + case NMC_EDITOR_SUB_CMD_PRINT: + printf (_("print [property|setting|connection] :: print property (setting, connection) value(s)\n\n" + "Shows property value. Providing an argument you can also display " + "values for the whole setting or connection.\n")); + break; + case NMC_EDITOR_SUB_CMD_BACK: + printf (_("back :: go to upper menu level\n\n")); + break; + case NMC_EDITOR_SUB_CMD_HELP: + printf (_("help/? [<command>] :: help for nmcli commands\n\n")); + break; + case NMC_EDITOR_SUB_CMD_QUIT: + printf (_("quit :: exit nmcli\n\n" + "This command exits nmcli. When the connection being edited " + "is not saved, the user is asked to confirm the action.\n")); + break; + default: + printf (_("Unknown command: '%s'\n"), command); + break; + } + } +} + +/*----------------------------------------------------------------------------*/ + +typedef struct { + NMDevice *device; + NMActiveConnection *ac; + guint monitor_id; +} MonitorACInfo; + +static gboolean nmc_editor_cb_called; +static GError *nmc_editor_error; +static MonitorACInfo *nmc_editor_monitor_ac; +static GMutex nmc_editor_mutex; +static GCond nmc_editor_cond; + +/* + * Store 'error' to shared 'nmc_editor_error' and monitoring info to + * 'nmc_editor_monitor_ac' and signal the condition so that + * the 'editor-thread' thread could process that. + */ +static void +set_info_and_signal_editor_thread (GError *error, MonitorACInfo *monitor_ac_info) +{ + g_mutex_lock (&nmc_editor_mutex); + nmc_editor_cb_called = TRUE; + nmc_editor_error = error ? g_error_copy (error) : NULL; + nmc_editor_monitor_ac = monitor_ac_info; + g_cond_signal (&nmc_editor_cond); + g_mutex_unlock (&nmc_editor_mutex); +} + +static void +add_connection_editor_cb (NMRemoteSettings *settings, + NMRemoteConnection *connection, + GError *error, + gpointer user_data) +{ + set_info_and_signal_editor_thread (error, NULL); +} + +static void +update_connection_editor_cb (NMRemoteConnection *connection, + GError *error, + gpointer user_data) +{ + set_info_and_signal_editor_thread (error, NULL); +} + +static gboolean +progress_activation_editor_cb (gpointer user_data) +{ + MonitorACInfo *info = (MonitorACInfo *) user_data; + NMDevice *device = info->device; + NMActiveConnection *ac = info->ac; + NMActiveConnectionState ac_state; + NMDeviceState dev_state; + + if (!device || !ac) + goto finish; + + ac_state = nm_active_connection_get_state (ac); + dev_state = nm_device_get_state (device); + + nmc_terminal_show_progress (nmc_device_state_to_string (dev_state)); + + if ( ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED + || dev_state == NM_DEVICE_STATE_ACTIVATED) { + nmc_terminal_erase_line (); + printf (_("Connection successfully activated (D-Bus active path: %s)\n"), + nm_object_get_path (NM_OBJECT (ac))); + goto finish; /* we are done */ + } else if ( ac_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED + || dev_state == NM_DEVICE_STATE_FAILED) { + nmc_terminal_erase_line (); + printf (_("Error: Connection activation failed.\n")); + goto finish; /* we are done */ + } + + return TRUE; + +finish: + if (device) + g_object_unref (device); + if (ac) + g_object_unref (ac); + return FALSE; +} + +static void +activate_connection_editor_cb (NMClient *client, + NMActiveConnection *active, + GError *error, + gpointer user_data) +{ + ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data; + NMDevice *device = info->device; + const GPtrArray *ac_devs; + MonitorACInfo *monitor_ac_info = NULL; + + if (!error) { + if (!device) { + ac_devs = nm_active_connection_get_devices (active); + device = ac_devs && ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL; + } + if (device) { + monitor_ac_info = g_malloc0 (sizeof (AddConnectionInfo)); + monitor_ac_info->device = g_object_ref (device); + monitor_ac_info->ac = active ? g_object_ref (active) : NULL; + monitor_ac_info->monitor_id = g_timeout_add (120, progress_activation_editor_cb, monitor_ac_info); + } + } + set_info_and_signal_editor_thread (error, monitor_ac_info); +} + +/*----------------------------------------------------------------------------*/ + +static void +print_property_description (NMSetting *setting, const char *prop_name) +{ + char *desc; + + desc = nmc_setting_get_property_desc (setting, prop_name); + printf ("\n=== [%s] ===\n%s\n", prop_name, desc); + g_free (desc); +} + +static void +print_setting_description (NMSetting *setting) +{ + /* Show description of all properties */ + char **all_props; + int i; + + all_props = nmc_setting_get_valid_properties (setting); + printf (("<<< %s >>>\n"), nm_setting_get_name (setting)); + for (i = 0; all_props && all_props[i]; i++) + print_property_description (setting, all_props[i]); + g_strfreev (all_props); +} + +static gboolean +connection_remove_setting (NMConnection *connection, NMSetting *setting) +{ + gboolean mandatory; + + g_return_val_if_fail (setting, FALSE); + + mandatory = is_setting_mandatory (connection, setting); + if (!mandatory) { + nm_connection_remove_setting (connection, G_OBJECT_TYPE (setting)); + return TRUE; + } + printf (_("Error: setting '%s' is mandatory and cannot be removed.\n"), + nm_setting_get_name (setting)); + return FALSE; +} + +static void +editor_show_status_line (NMConnection *connection, gboolean dirty, gboolean temp) +{ + NMSettingConnection *s_con; + const char *con_type, *con_id, *con_uuid; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + con_type = nm_setting_connection_get_connection_type (s_con); + con_id = nm_connection_get_id (connection); + con_uuid = nm_connection_get_uuid (connection); + + /* TRANSLATORS: status line in nmcli connection editor */ + printf (_("[ Type: %s | Name: %s | UUID: %s | Dirty: %s | Temp: %s ]\n"), + con_type, con_id, con_uuid, + dirty ? _("yes") : _("no"), + temp ? _("yes") : _("no")); +} + +static gboolean +refresh_remote_connection (GWeakRef *weak, NMRemoteConnection **remote) +{ + gboolean previous; + + g_return_val_if_fail (remote != NULL, FALSE); + + previous = (*remote != NULL); + if (*remote) + g_object_unref (*remote); + *remote = g_weak_ref_get (weak); + + return (previous && !*remote); +} + +static gboolean +is_connection_dirty (NMConnection *connection, NMRemoteConnection *remote) +{ + return !nm_connection_compare (connection, + remote ? NM_CONNECTION (remote) : NULL, + NM_SETTING_COMPARE_FLAG_EXACT); +} + +static gboolean +confirm_quit (void) +{ + char *answer; + gboolean want_quit = FALSE; + + answer = nmc_readline (_("The connection is not saved. " + "Do you really want to quit? %s"), + prompt_yes_no (FALSE, NULL)); + answer = answer ? g_strstrip (answer) : NULL; + if (answer && matches (answer, WORD_LOC_YES) == 0) + want_quit = TRUE; + + g_free (answer); + return want_quit; +} + +/* + * Submenu for detailed property editing + * Return: TRUE - continue; FALSE - should quit + */ +static gboolean +property_edit_submenu (NmCli *nmc, + NMConnection *connection, + NMRemoteConnection **rem_con, + GWeakRef *rem_con_weak, + NMSetting *curr_setting, + const char *prop_name) +{ + NmcEditorSubCmd cmdsub; + gboolean cmd_property_loop = TRUE; + gboolean should_quit = FALSE; + char *prop_val_user; + gboolean set_result; + GError *tmp_err = NULL; + char *prompt; + gboolean dirty; + GValue prop_g_value = G_VALUE_INIT; + gboolean temp_changes; + gboolean removed; + + prompt = nmc_colorize (nmc->editor_prompt_color, "nmcli %s.%s> ", + nm_setting_get_name (curr_setting), prop_name); + + while (cmd_property_loop) { + char *cmd_property_user; + char *cmd_property_arg; + + /* Get the remote connection again, it may have disapeared */ + removed = refresh_remote_connection (rem_con_weak, rem_con); + if (removed) + printf (_("The connection profile has been removed from another client. " + "You may type 'save' in the main menu to restore it.\n")); + + /* Connection is dirty? (not saved or differs from the saved) */ + dirty = is_connection_dirty (connection, *rem_con); + temp_changes = *rem_con ? nm_remote_connection_get_unsaved (*rem_con) : TRUE; + if (nmc->editor_status_line) + editor_show_status_line (connection, dirty, temp_changes); + + cmd_property_user = nmc_readline ("%s", prompt); + if (!cmd_property_user || *cmd_property_user == '\0') + continue; + cmdsub = parse_editor_sub_cmd (g_strstrip (cmd_property_user), &cmd_property_arg); + + switch (cmdsub) { + case NMC_EDITOR_SUB_CMD_SET: + case NMC_EDITOR_SUB_CMD_ADD: + /* list, arrays,...: SET replaces the whole property value + * ADD adds the new value(s) + * single values: : both SET and ADD sets the new value + */ + if (!cmd_property_arg) + prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name); + else + prop_val_user = g_strdup (cmd_property_arg); + + /* nmc_setting_set_property() only adds new value, thus we have to + * remove the original value and save it for error cases. + */ + if (cmdsub == NMC_EDITOR_SUB_CMD_SET) { + nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value); + nmc_property_set_default_value (curr_setting, prop_name); + } + + set_result = nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err); + g_free (prop_val_user); + if (!set_result) { + printf (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message); + g_clear_error (&tmp_err); + if (cmdsub == NMC_EDITOR_SUB_CMD_SET) + nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value); + } + if (G_IS_VALUE (&prop_g_value)) + g_value_unset (&prop_g_value); + break; + + case NMC_EDITOR_SUB_CMD_CHANGE: + rl_startup_hook = set_deftext; + pre_input_deftext = nmc_setting_get_property_out2in (curr_setting, prop_name, NULL); + prop_val_user = nmc_readline (_("Edit '%s' value: "), prop_name); + + nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value); + nmc_property_set_default_value (curr_setting, prop_name); + + if (!nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err)) { + printf (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message); + g_clear_error (&tmp_err); + nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value); + } + g_free (prop_val_user); + if (G_IS_VALUE (&prop_g_value)) + g_value_unset (&prop_g_value); + break; + + case NMC_EDITOR_SUB_CMD_REMOVE: + if (cmd_property_arg) { + unsigned long val_int = G_MAXUINT32; + char *option = NULL; + + if (!nmc_string_to_uint (cmd_property_arg, TRUE, 0, G_MAXUINT32, &val_int)) + option = g_strdup (cmd_property_arg); + + if (!nmc_setting_remove_property_option (curr_setting, prop_name, + option ? g_strstrip (option) : NULL, + (guint32) val_int, + &tmp_err)) { + printf (_("Error: %s\n"), tmp_err->message); + g_clear_error (&tmp_err); + } + g_free (option); + } else { + if (!nmc_setting_reset_property (curr_setting, prop_name, &tmp_err)) { + printf (_("Error: failed to remove value of '%s': %s\n"), prop_name, + tmp_err->message); + g_clear_error (&tmp_err); + } + } + break; + + case NMC_EDITOR_SUB_CMD_DESCRIBE: + /* Show property description */ + print_property_description (curr_setting, prop_name); + break; + + case NMC_EDITOR_SUB_CMD_PRINT: + /* Print current connection settings/properties */ + if (cmd_property_arg) { + if (matches (cmd_property_arg, "setting") == 0) + editor_show_setting (curr_setting, nmc); + else if ( matches (cmd_property_arg, "connection") == 0 + || matches (cmd_property_arg, "all") == 0) + editor_show_connection (connection, nmc); + else + printf (_("Unknown command argument: '%s'\n"), cmd_property_arg); + } else { + char *prop_val = nmc_setting_get_property (curr_setting, prop_name, NULL); + printf ("%s: %s\n", prop_name, prop_val); + g_free (prop_val); + } + break; + + case NMC_EDITOR_SUB_CMD_BACK: + cmd_property_loop = FALSE; + break; + + case NMC_EDITOR_SUB_CMD_HELP: + editor_sub_usage (cmd_property_arg); + break; + + case NMC_EDITOR_SUB_CMD_QUIT: + if (is_connection_dirty (connection, *rem_con)) { + if (confirm_quit ()) { + cmd_property_loop = FALSE; + should_quit = TRUE; /* we will quit nmcli */ + } + } else { + cmd_property_loop = FALSE; + should_quit = TRUE; /* we will quit nmcli */ + } + break; + + case NMC_EDITOR_SUB_CMD_UNKNOWN: + default: + printf (_("Unknown command: '%s'\n"), cmd_property_user); + break; + } + g_free (cmd_property_user); + g_free (cmd_property_arg); + } + g_free (prompt); + + return !should_quit; +} + +/* + * Split 'str' in the following format: [[[setting.]property] [value]] + * and return the components in 'setting', 'property' and 'value' + * Use g_free() to deallocate the returned strings. + */ +static void +split_editor_main_cmd_args (const char *str, char **setting, char **property, char **value) +{ + char **args, **items; + + if (!str) + return; + + args = nmc_strsplit_set (str, " \t", 2); + if (args[0]) { + items = nmc_strsplit_set (args[0], ".", 2); + if (g_strv_length (items) == 2) { + if (setting) + *setting = g_strdup (items[0]); + if (property) + *property = g_strdup (items[1]); } else { - g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); - nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + if (property) + *property = g_strdup (items[0]); } - goto error; + g_strfreev (items); + + if (value && args[1]) + *value = g_strstrip (g_strdup (args[1])); } + g_strfreev (args); +} - /* create NMClient */ - nmc->get_client (nmc); +static NMSetting * +is_setting_valid (NMConnection *connection, const NameItem *valid_settings, char *setting) +{ + const char *setting_name; - con_path = nm_connection_get_path (connection); + if (!(setting_name = check_valid_name (setting, valid_settings, NULL))) + return NULL; + return nm_connection_get_setting_by_name (connection, setting_name); +} - active_cons = nm_client_get_active_connections (nmc->client); - for (i = 0; active_cons && (i < active_cons->len); i++) { - NMActiveConnection *candidate = g_ptr_array_index (active_cons, i); +static char * +is_property_valid (NMSetting *setting, const char *property, GError **error) +{ + char **valid_props = NULL; + const char *prop_name; + char *ret; + + valid_props = nmc_setting_get_valid_properties (setting); + prop_name = nmc_string_is_valid (property, (const char **) valid_props, error); + ret = prop_name ? g_strdup (prop_name) : NULL; + g_strfreev (valid_props); + return ret; +} - active_path = nm_active_connection_get_connection (candidate); - if (!strcmp (active_path, con_path)) { - active = candidate; +static NMSetting * +create_setting_by_name (const char *name, const NameItem *valid_settings) +{ + const char *setting_name; + NMSetting *setting = NULL; + + /* Get a valid setting name */ + setting_name = check_valid_name (name, valid_settings, NULL); + + if (setting_name) { + setting = nmc_setting_new_for_name (setting_name); + if (!setting) + return NULL; /* This should really not happen */ + nmc_setting_custom_init (setting); + } + return setting; +} + +static const char * +ask_check_setting (const char *arg, + const NameItem *valid_settings_arr, + const char *valid_settings_str) +{ + char *setting_name_user; + const char *setting_name; + GError *err = NULL; + + if (!arg) { + printf (_("Available settings: %s\n"), valid_settings_str); + setting_name_user = nmc_readline (EDITOR_PROMPT_SETTING); + } else + setting_name_user = g_strdup (arg); + + if (setting_name_user) + g_strstrip (setting_name_user); + + if (!(setting_name = check_valid_name (setting_name_user, valid_settings_arr, &err))) { + printf (_("Error: invalid setting name; %s\n"), err->message); + g_clear_error (&err); + } + g_free (setting_name_user); + return setting_name; +} + +static const char * +ask_check_property (const char *arg, + const char **valid_props, + const char *valid_props_str) +{ + char *prop_name_user; + const char *prop_name; + GError *tmp_err = NULL; + + if (!arg) { + printf (_("Available properties: %s\n"), valid_props_str); + prop_name_user = nmc_readline (EDITOR_PROMPT_PROPERTY); + if (prop_name_user) + g_strstrip (prop_name_user); + } else + prop_name_user = g_strdup (arg); + + if (!(prop_name = nmc_string_is_valid (prop_name_user, valid_props, &tmp_err))) { + printf (_("Error: property %s\n"), tmp_err->message); + g_clear_error (&tmp_err); + } + g_free (prop_name_user); + return prop_name; +} + +/* Copy timestamp from src do dst */ +static void +update_connection_timestamp (NMConnection *src, NMConnection *dst) +{ + NMSettingConnection *s_con_src, *s_con_dst; + + s_con_src = nm_connection_get_setting_connection (src); + s_con_dst = nm_connection_get_setting_connection (dst); + if (s_con_src && s_con_dst) { + guint64 timestamp = nm_setting_connection_get_timestamp (s_con_src); + g_object_set (s_con_dst, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL); + } +} + +static gboolean +confirm_connection_saving (NMConnection *local, NMConnection *remote) +{ + NMSettingConnection *s_con_loc, *s_con_rem; + gboolean ac_local, ac_remote; + gboolean confirmed = TRUE; + + s_con_loc = nm_connection_get_setting_connection (local); + g_assert (s_con_loc); + ac_local = nm_setting_connection_get_autoconnect (s_con_loc); + + if (remote) { + s_con_rem = nm_connection_get_setting_connection (remote); + g_assert (s_con_rem); + ac_remote = nm_setting_connection_get_autoconnect (s_con_rem); + } else + ac_remote = FALSE; + + if (ac_local && !ac_remote) { + char *answer; + answer = nmc_readline (_("Saving the connection with 'autoconnect=yes'. " + "That might result in an immediate activation of the connection.\n" + "Do you still want to save? %s"), prompt_yes_no (TRUE, NULL)); + answer = answer ? g_strstrip (answer) : NULL; + if (!answer || matches (answer, WORD_LOC_YES) == 0) + confirmed = TRUE; + else + confirmed = FALSE; + g_free (answer); + } + return confirmed; +} + +typedef struct { + guint level; + char *main_prompt; + NMSetting *curr_setting; + char **valid_props; + char *valid_props_str; +} NmcEditorMenuContext; + +static void +menu_switch_to_level0 (NmcEditorMenuContext *menu_ctx, + const char *prompt, + NmcTermColor prompt_color) +{ + menu_ctx->level = 0; + g_free (menu_ctx->main_prompt); + menu_ctx->main_prompt = nmc_colorize (prompt_color, "%s", prompt); + menu_ctx->curr_setting = NULL; + g_strfreev (menu_ctx->valid_props); + menu_ctx->valid_props = NULL; + g_free (menu_ctx->valid_props_str); + menu_ctx->valid_props_str = NULL; +} + +static void +menu_switch_to_level1 (NmcEditorMenuContext *menu_ctx, + NMSetting *setting, + const char *setting_name, + NmcTermColor prompt_color) +{ + menu_ctx->level = 1; + g_free (menu_ctx->main_prompt); + menu_ctx->main_prompt = nmc_colorize (prompt_color, "nmcli %s> ", setting_name); + menu_ctx->curr_setting = setting; + g_strfreev (menu_ctx->valid_props); + menu_ctx->valid_props = nmc_setting_get_valid_properties (menu_ctx->curr_setting); + g_free (menu_ctx->valid_props_str); + menu_ctx->valid_props_str = g_strjoinv (", ", menu_ctx->valid_props); +} + +static gboolean +editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_type) +{ + NMRemoteConnection *rem_con; + NMRemoteConnection *con_tmp; + GWeakRef weak = { { NULL } }; + gboolean removed; + NmcEditorMainCmd cmd; + char *cmd_user; + gboolean cmd_loop = TRUE; + char *cmd_arg = NULL; + char *cmd_arg_s, *cmd_arg_p, *cmd_arg_v; + const char *BASE_PROMPT = "nmcli> "; + const NameItem *valid_settings_arr = NULL; + char *valid_settings_str = NULL; + AddConnectionInfo *info = NULL; + gboolean dirty; + gboolean temp_changes; + GError *err1 = NULL; + NmcEditorMenuContext menu_ctx; + + valid_settings_arr = get_valid_settings_array (connection_type); + valid_settings_str = get_valid_options_string (valid_settings_arr); + printf (_("You may edit the following settings: %s\n"), valid_settings_str); + + menu_ctx.level = 0; + menu_ctx.main_prompt = nmc_colorize (nmc->editor_prompt_color, BASE_PROMPT); + menu_ctx.curr_setting = NULL; + menu_ctx.valid_props = NULL; + menu_ctx.valid_props_str = NULL; + + /* Get remote connection */ + con_tmp = nm_remote_settings_get_connection_by_uuid (nmc->system_settings, + nm_connection_get_uuid (connection)); + g_weak_ref_init (&weak, con_tmp); + rem_con = g_weak_ref_get (&weak); + + while (cmd_loop) { + /* Connection is dirty? (not saved or differs from the saved) */ + dirty = is_connection_dirty (connection, rem_con); + temp_changes = rem_con ? nm_remote_connection_get_unsaved (rem_con) : TRUE; + if (nmc->editor_status_line) + editor_show_status_line (connection, dirty, temp_changes); + + /* Read user input */ + cmd_user = nmc_readline ("%s", menu_ctx.main_prompt); + + /* Get the remote connection again, it may have disapeared */ + removed = refresh_remote_connection (&weak, &rem_con); + if (removed) + printf (_("The connection profile has been removed from another client. " + "You may type 'save' to restore it.\n")); + + if (!cmd_user || *cmd_user == '\0') + continue; + cmd = parse_editor_main_cmd (g_strstrip (cmd_user), &cmd_arg); + + cmd_arg_s = NULL; + cmd_arg_p = NULL; + cmd_arg_v = NULL; + split_editor_main_cmd_args (cmd_arg, &cmd_arg_s, &cmd_arg_p, &cmd_arg_v); + switch (cmd) { + case NMC_EDITOR_MAIN_CMD_SET: + /* Set property value */ + if (!cmd_arg) { + if (menu_ctx.level == 1) { + const char *prop_name; + char *prop_val_user = NULL; + const char *avals; + GError *tmp_err = NULL; + + prop_name = ask_check_property (cmd_arg, + (const char **) menu_ctx.valid_props, + menu_ctx.valid_props_str); + if (!prop_name) + break; + + avals = nmc_setting_get_property_allowed_values (menu_ctx.curr_setting, prop_name); + if (avals) + printf (_("Allowed values for '%s' property: %s\n"), prop_name, avals); + + prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name); + + /* Set property value */ + if (!nmc_setting_set_property (menu_ctx.curr_setting, prop_name, prop_val_user, &tmp_err)) { + printf (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message); + g_clear_error (&tmp_err); + } + } else { + printf (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str); + printf (_("use 'goto <setting>' first, or 'set <setting>.<property>'\n")); + } + } else { + NMSetting *ss = NULL; + gboolean created_ss = FALSE; + char *prop_name; + GError *tmp_err = NULL; + + if (cmd_arg_s) { + /* setting provided as "setting.property" */ + ss = is_setting_valid (connection, valid_settings_arr, cmd_arg_s); + if (!ss) { + ss = create_setting_by_name (cmd_arg_s, valid_settings_arr); + if (!ss) { + printf (_("Error: invalid setting argument '%s'; valid are [%s]\n"), + cmd_arg_s, valid_settings_str); + break; + } + created_ss = TRUE; + } + } else { + if (menu_ctx.curr_setting) + ss = menu_ctx.curr_setting; + else { + printf (_("Error: missing setting for '%s' property\n"), cmd_arg_p); + break; + } + } + + prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err); + if (!prop_name) { + printf (_("Error: invalid property: %s\n"), tmp_err->message); + g_clear_error (&tmp_err); + if (created_ss) + g_object_unref (ss); + break; + } + + + + /* Ask for value */ + if (!cmd_arg_v) { + const char *avals = nmc_setting_get_property_allowed_values (ss, prop_name); + if (avals) + printf (_("Allowed values for '%s' property: %s\n"), prop_name, avals); + + cmd_arg_v = nmc_readline (_("Enter '%s' value: "), prop_name); + } + + /* Set property value */ + if (!nmc_setting_set_property (ss, prop_name, cmd_arg_v, &tmp_err)) { + printf (_("Error: failed to set '%s' property: %s\n"), + prop_name, tmp_err->message); + g_clear_error (&tmp_err); + } + + if (created_ss) + nm_connection_add_setting (connection, ss); + g_free (prop_name); + } + break; + + case NMC_EDITOR_MAIN_CMD_GOTO: + /* cmd_arg_s != NULL means 'setting.property' argument */ + if (menu_ctx.level == 0 || cmd_arg_s) { + /* in top level - no setting selected yet */ + const char *setting_name; + NMSetting *setting; + const char *user_arg = cmd_arg_s ? cmd_arg_s : cmd_arg_p; + + setting_name = ask_check_setting (user_arg, valid_settings_arr, valid_settings_str); + if (!setting_name) + break; + + setting = nm_connection_get_setting_by_name (connection, setting_name); + if (!setting) { + setting = nmc_setting_new_for_name (setting_name); + if (!setting) { + printf (_("Error: unknown setting '%s'\n"), setting_name); + break; + } + nmc_setting_custom_init (setting); + nm_connection_add_setting (connection, setting); + } + /* Set global variable for use in TAB completion */ + nmc_tab_completion.setting = setting; + + /* Switch to level 1 */ + menu_switch_to_level1 (&menu_ctx, setting, setting_name, nmc->editor_prompt_color); + + if (!cmd_arg_s) { + printf (_("You may edit the following properties: %s\n"), menu_ctx.valid_props_str); + break; + } + } + if (menu_ctx.level == 1 || cmd_arg_s) { + /* level 1 - setting selected */ + const char *prop_name; + + prop_name = ask_check_property (cmd_arg_p, + (const char **) menu_ctx.valid_props, + menu_ctx.valid_props_str); + if (!prop_name) + break; + + /* submenu - level 2 - editing properties */ + cmd_loop = property_edit_submenu (nmc, + connection, + &rem_con, + &weak, + menu_ctx.curr_setting, + prop_name); + } + break; + + case NMC_EDITOR_MAIN_CMD_REMOVE: + /* Remove setting from connection, or delete value of a property */ + if (!cmd_arg) { + if (menu_ctx.level == 1) { + GError *tmp_err = NULL; + const char *prop_name; + + prop_name = ask_check_property (cmd_arg, + (const char **) menu_ctx.valid_props, + menu_ctx.valid_props_str); + if (!prop_name) + break; + + /* Delete property value */ + if (!nmc_setting_reset_property (menu_ctx.curr_setting, prop_name, &tmp_err)) { + printf (_("Error: failed to remove value of '%s': %s\n"), prop_name, + tmp_err->message); + g_clear_error (&tmp_err); + } + } else + printf (_("Error: no argument given; valid are [%s]\n"), valid_settings_str); + } else { + NMSetting *ss = NULL; + gboolean descr_all; + char *user_s; + + /* cmd_arg_s != NULL means argument is "setting.property" */ + descr_all = !cmd_arg_s && !menu_ctx.curr_setting; + user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL; + if (user_s) { + ss = is_setting_valid (connection, valid_settings_arr, user_s); + if (!ss) { + if (check_valid_name (user_s, valid_settings_arr, NULL)) + printf (_("Setting '%s' is not present in the connection.\n"), user_s); + else + printf (_("Error: invalid setting argument '%s'; valid are [%s]\n"), + user_s, valid_settings_str); + break; + } + } else + ss = menu_ctx.curr_setting; + + if (descr_all) { + /* Remove setting from the connection */ + connection_remove_setting (connection, ss); + if (ss == menu_ctx.curr_setting) { + /* If we removed the setting we are in, go up */ + menu_switch_to_level0 (&menu_ctx, BASE_PROMPT, nmc->editor_prompt_color); + nmc_tab_completion.setting = NULL; /* for TAB completion */ + } + } else { + GError *tmp_err = NULL; + char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err); + if (prop_name) { + /* Delete property value */ + if (!nmc_setting_reset_property (ss, prop_name, &tmp_err)) { + printf (_("Error: failed to remove value of '%s': %s\n"), prop_name, + tmp_err->message); + g_clear_error (&tmp_err); + } + } else { + /* If the string is not a property, try it as a setting */ + NMSetting *s_tmp; + s_tmp = is_setting_valid (connection, valid_settings_arr, cmd_arg_p); + if (s_tmp) { + /* Remove setting from the connection */ + connection_remove_setting (connection, s_tmp); + /* coverity[copy_paste_error] - suppress Coverity COPY_PASTE_ERROR defect */ + if (ss == menu_ctx.curr_setting) { + /* If we removed the setting we are in, go up */ + menu_switch_to_level0 (&menu_ctx, BASE_PROMPT, nmc->editor_prompt_color); + nmc_tab_completion.setting = NULL; /* for TAB completion */ + } + } else + printf (_("Error: %s properties, nor it is a setting name.\n"), + tmp_err->message); + g_clear_error (&tmp_err); + } + g_free (prop_name); + } + } + break; + + case NMC_EDITOR_MAIN_CMD_DESCRIBE: + /* Print property description */ + if (!cmd_arg) { + if (menu_ctx.level == 1) { + const char *prop_name; + + prop_name = ask_check_property (cmd_arg, + (const char **) menu_ctx.valid_props, + menu_ctx.valid_props_str); + if (!prop_name) + break; + + /* Show property description */ + print_property_description (menu_ctx.curr_setting, prop_name); + } else { + printf (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str); + printf (_("use 'goto <setting>' first, or 'describe <setting>.<property>'\n")); + } + } else { + NMSetting *ss = NULL; + gboolean unref_ss = FALSE; + gboolean descr_all; + char *user_s; + + /* cmd_arg_s != NULL means argument is "setting.property" */ + descr_all = !cmd_arg_s && !menu_ctx.curr_setting; + user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL; + if (user_s) { + ss = is_setting_valid (connection, valid_settings_arr, user_s); + if (!ss) { + ss = create_setting_by_name (user_s, valid_settings_arr); + if (!ss) { + printf (_("Error: invalid setting argument '%s'; valid are [%s]\n"), + user_s, valid_settings_str); + break; + } + unref_ss = TRUE; + } + } else + ss = menu_ctx.curr_setting; + + if (descr_all) { + /* Show description for all properties */ + print_setting_description (ss); + } else { + GError *tmp_err = NULL; + char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err); + if (prop_name) { + /* Show property description */ + print_property_description (ss, prop_name); + } else { + /* If the string is not a property, try it as a setting */ + NMSetting *s_tmp; + s_tmp = is_setting_valid (connection, valid_settings_arr, cmd_arg_p); + if (s_tmp) + print_setting_description (s_tmp); + else + printf (_("Error: invalid property: %s, " + "neither a valid setting name.\n"), + tmp_err->message); + g_clear_error (&tmp_err); + } + g_free (prop_name); + } + if (unref_ss) + g_object_unref (ss); + } + break; + + case NMC_EDITOR_MAIN_CMD_PRINT: + /* Print current connection settings/properties */ + if (cmd_arg) { + if (strcmp (cmd_arg, "all") == 0) + editor_show_connection (connection, nmc); + else { + NMSetting *ss = NULL; + gboolean whole_setting; + char *user_s; + + /* cmd_arg_s != NULL means argument is "setting.property" */ + whole_setting = !cmd_arg_s && !menu_ctx.curr_setting; + user_s = whole_setting ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL; + if (user_s) { + const char *s_name; + s_name = check_valid_name (user_s, valid_settings_arr, NULL); + if (!s_name) { + printf (_("Error: unknown setting: '%s'\n"), user_s); + break; + } + ss = nm_connection_get_setting_by_name (connection, s_name); + if (!ss) { + printf (_("Error: '%s' setting not present in the connection\n"), s_name); + break; + } + } else + ss = menu_ctx.curr_setting; + + if (whole_setting) { + /* Print the whole setting */ + editor_show_setting (ss, nmc); + } else { + GError *err = NULL; + char *prop_name = is_property_valid (ss, cmd_arg_p, &err); + if (prop_name) { + /* Print one property */ + char *prop_val = nmc_setting_get_property (ss, prop_name, NULL); + printf ("%s.%s: %s\n", nm_setting_get_name (ss),prop_name , prop_val); + g_free (prop_val); + } else { + /* If the string is not a property, try it as a setting */ + NMSetting *s_tmp; + s_tmp = is_setting_valid (connection, valid_settings_arr, cmd_arg_p); + if (s_tmp) { + /* Print the whole setting */ + editor_show_setting (s_tmp, nmc); + } else + printf (_("Error: invalid property: %s%s\n"), + err->message, + cmd_arg_s ? "" : _(", neither a valid setting name")); + g_clear_error (&err); + } + g_free (prop_name); + } + } + } else { + if (menu_ctx.curr_setting) + editor_show_setting (menu_ctx.curr_setting, nmc); + else + editor_show_connection (connection, nmc); + } + break; + + case NMC_EDITOR_MAIN_CMD_VERIFY: + /* Verify current setting or the whole connection */ + if ( menu_ctx.curr_setting + && (!cmd_arg || strcmp (cmd_arg, "all") != 0)) { + GError *tmp_err = NULL; + (void) nm_setting_verify (menu_ctx.curr_setting, NULL, &tmp_err); + printf (_("Verify setting '%s': %s\n"), + nm_setting_get_name (menu_ctx.curr_setting), + tmp_err ? tmp_err->message : "OK"); + g_clear_error (&tmp_err); + } else { + GError *tmp_err = NULL; + (void) nm_connection_verify (connection, &tmp_err); + printf (_("Verify connection: %s\n"), + tmp_err ? tmp_err->message : "OK"); + g_clear_error (&tmp_err); + } + break; + + case NMC_EDITOR_MAIN_CMD_SAVE: + /* Save the connection */ + if (nm_connection_verify (connection, &err1)) { + gboolean persistent = TRUE; + + /* parse argument */ + if (cmd_arg) { + if (matches (cmd_arg, "temporary") == 0) + persistent = FALSE; + else if (matches (cmd_arg, "persistent") == 0) + persistent = TRUE; + else { + printf (_("Error: invalid argument '%s'\n"), cmd_arg); + break; + } + } + + /* Ask for save confirmation if the connection changes to autoconnect=yes */ + if (nmc->editor_save_confirmation) + if (!confirm_connection_saving (connection, NM_CONNECTION (rem_con))) + break; + + if (!rem_con) { + /* Tell the settings service to add the new connection */ + info = g_malloc0 (sizeof (AddConnectionInfo)); + info->nmc = nmc; + info->con_name = g_strdup (nm_connection_get_id (connection)); + add_new_connection (persistent, + nmc->system_settings, + connection, + add_connection_editor_cb, + info); + } else { + /* Save/update already saved (existing) connection */ + nm_connection_replace_settings_from_connection (NM_CONNECTION (rem_con), + connection, + NULL); + update_connection (persistent, rem_con, update_connection_editor_cb, NULL); + } + + g_mutex_lock (&nmc_editor_mutex); + //FIXME: add also a timeout for cases the callback is not called + while (!nmc_editor_cb_called) + g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex); + + if (nmc_editor_error) { + printf (_("Error: Failed to save '%s' (%s) connection: (%d) %s\n"), + nm_connection_get_id (connection), + nm_connection_get_uuid (connection), + nmc_editor_error->code, nmc_editor_error->message); + + g_error_free (nmc_editor_error); + } else { + printf (!rem_con ? + _("Connection '%s' (%s) successfully saved.\n") : + _("Connection '%s' (%s) successfully updated.\n"), + nm_connection_get_id (connection), + nm_connection_get_uuid (connection)); + + con_tmp = nm_remote_settings_get_connection_by_uuid (nmc->system_settings, + nm_connection_get_uuid (connection)); + g_weak_ref_set (&weak, con_tmp); + refresh_remote_connection (&weak, &rem_con); + + /* Replace local connection with the remote one to be sure they are equal. + * This mitigates problems with plugins not preserving some properties or + * adding ipv{4,6} settings when not present. + */ + if (con_tmp) { + char *s_name = NULL; + if (menu_ctx.curr_setting) + s_name = g_strdup (nm_setting_get_name (menu_ctx.curr_setting)); + + /* Update settings in the local connection */ + nm_connection_replace_settings_from_connection (connection, + NM_CONNECTION (con_tmp), + NULL); + + /* Also update setting for menu context and TAB-completion */ + menu_ctx.curr_setting = s_name ? nm_connection_get_setting_by_name (connection, s_name) : NULL; + nmc_tab_completion.setting = menu_ctx.curr_setting; + g_free (s_name); + } + } + + nmc_editor_cb_called = FALSE; + nmc_editor_error = NULL; + g_mutex_unlock (&nmc_editor_mutex); + } else + printf (_("Error: connection verification failed: %s\n"), + err1 ? err1->message : _("(unknown error)")); + + g_clear_error (&err1); break; + + case NMC_EDITOR_MAIN_CMD_ACTIVATE: + { + GError *tmp_err = NULL; + const char *ifname = cmd_arg_p; + const char *ap_nsp = cmd_arg_v; + + /* When only AP/NSP is specified it is prepended with '/' */ + if (!cmd_arg_v) { + if (ifname && ifname[0] == '/') { + ap_nsp = ifname + 1; + ifname = NULL; + } + } else + ap_nsp = ap_nsp && ap_nsp[0] == '/' ? ap_nsp + 1 : ap_nsp; + + if (is_connection_dirty (connection, rem_con)) { + printf (_("Error: connection is not saved. Type 'save' first.\n")); + break; + } + if (!nm_connection_verify (NM_CONNECTION (rem_con), &tmp_err)) { + printf (_("Error: connection is not valid: %s\n"), tmp_err->message); + g_clear_error (&tmp_err); + break; + } + nmc->get_client (nmc); + + nmc->nowait_flag = FALSE; + nmc->should_wait = TRUE; + nmc->print_output = NMC_PRINT_PRETTY; + if (!nmc_activate_connection (nmc, NM_CONNECTION (rem_con), ifname, ap_nsp, ap_nsp, + activate_connection_editor_cb, &tmp_err)) { + printf (_("Error: Cannot activate connection: %s.\n"), tmp_err->message); + g_clear_error (&tmp_err); + break; + } + + g_mutex_lock (&nmc_editor_mutex); + while (!nmc_editor_cb_called) + g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex); + + if (nmc_editor_error) { + printf (_("Error: Failed to activate '%s' (%s) connection: (%d) %s\n"), + nm_connection_get_id (connection), + nm_connection_get_uuid (connection), + nmc_editor_error->code, nmc_editor_error->message); + g_error_free (nmc_editor_error); + } else { + printf (_("Monitoring connection activation (press any key to continue)\n")); + nmc_get_user_input (""); + } + + if (nmc_editor_monitor_ac) { + if (nmc_editor_monitor_ac->monitor_id) + g_source_remove (nmc_editor_monitor_ac->monitor_id); + g_free (nmc_editor_monitor_ac); + } + nmc_editor_cb_called = FALSE; + nmc_editor_error = NULL; + nmc_editor_monitor_ac = NULL; + g_mutex_unlock (&nmc_editor_mutex); + + /* Update timestamp in local connection */ + update_connection_timestamp (NM_CONNECTION (rem_con), connection); + + } + break; + + case NMC_EDITOR_MAIN_CMD_BACK: + /* Go back (up) an the menu */ + if (menu_ctx.level == 1) { + menu_switch_to_level0 (&menu_ctx, BASE_PROMPT, nmc->editor_prompt_color); + nmc_tab_completion.setting = NULL; /* for TAB completion */ + } + break; + + case NMC_EDITOR_MAIN_CMD_HELP: + /* Print command help */ + editor_main_help (cmd_arg); + break; + + case NMC_EDITOR_MAIN_CMD_NMCLI: + if (cmd_arg_p && matches (cmd_arg_p, "status-line") == 0) { + GError *tmp_err = NULL; + gboolean bb; + if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) { + printf (_("Error: status-line: %s\n"), tmp_err->message); + g_clear_error (&tmp_err); + } else + nmc->editor_status_line = bb; + } else if (cmd_arg_p && matches (cmd_arg_p, "save-confirmation") == 0) { + GError *tmp_err = NULL; + gboolean bb; + if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) { + printf (_("Error: save-confirmation: %s\n"), tmp_err->message); + g_clear_error (&tmp_err); + } else + nmc->editor_save_confirmation = bb; + } else if (cmd_arg_p && matches (cmd_arg_p, "prompt-color") == 0) { + unsigned long color; + if (!nmc_string_to_uint (cmd_arg_v ? g_strstrip (cmd_arg_v) : "X", + TRUE, 0, 8, &color)) + printf (_("Error: bad color number: '%s'; use <0-8>\n"), + cmd_arg_v ? cmd_arg_v : ""); + else { + nmc->editor_prompt_color = color; + g_free (menu_ctx.main_prompt); + if (menu_ctx.level == 0) + menu_ctx.main_prompt = nmc_colorize (nmc->editor_prompt_color, BASE_PROMPT); + else + menu_ctx.main_prompt = nmc_colorize (nmc->editor_prompt_color, "nmcli %s> ", + nm_setting_get_name (menu_ctx.curr_setting)); + } + } else if (!cmd_arg_p) { + printf (_("Current nmcli configuration:\n")); + printf ("status-line: %s\n" + "save-confirmation: %s\n" + "prompt-color: %d\n", + nmc->editor_status_line ? "yes" : "no", + nmc->editor_save_confirmation ? "yes" : "no", + nmc->editor_prompt_color); + } else + printf (_("Invalid configuration option '%s'; allowed [%s]\n"), + cmd_arg_v ? cmd_arg_v : "", "status-line, save-confirmation, prompt-color"); + + break; + + case NMC_EDITOR_MAIN_CMD_QUIT: + if (is_connection_dirty (connection, rem_con)) { + if (confirm_quit ()) + cmd_loop = FALSE; /* quit command loop */ + } else + cmd_loop = FALSE; /* quit command loop */ + break; + + case NMC_EDITOR_MAIN_CMD_UNKNOWN: + default: + printf (_("Unknown command: '%s'\n"), cmd_user); + break; + } + + g_free (cmd_user); + g_free (cmd_arg); + g_free (cmd_arg_s); + g_free (cmd_arg_p); + g_free (cmd_arg_v); + } + g_free (valid_settings_str); + g_free (menu_ctx.main_prompt); + g_strfreev (menu_ctx.valid_props); + g_free (menu_ctx.valid_props_str); + if (rem_con) + g_object_unref (rem_con); + g_weak_ref_clear (&weak); + + /* Save history file */ + save_history_cmds (nm_connection_get_uuid (connection)); + + return TRUE; +} + +static const char * +get_ethernet_device_name (NmCli *nmc) +{ + const GPtrArray *devices; + int i; + + nmc->get_client (nmc); + devices = nm_client_get_devices (nmc->client); + for (i = 0; devices && (i < devices->len); i++) { + NMDevice *dev = g_ptr_array_index (devices, i); + if (NM_IS_DEVICE_ETHERNET (dev)) + return nm_device_get_iface (dev); + } + return NULL; +} + +static void +editor_init_new_connection (NmCli *nmc, NMConnection *connection) +{ + NMSetting *setting, *base_setting; + NMSettingConnection *s_con; + const char *con_type; + const char *slave_type = NULL; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + con_type = nm_setting_connection_get_connection_type (s_con); + + /* Initialize new connection according to its type using sensible defaults. */ + + if (g_strcmp0 (con_type, "bond-slave") == 0) + slave_type = NM_SETTING_BOND_SETTING_NAME; + if (g_strcmp0 (con_type, "team-slave") == 0) + slave_type = NM_SETTING_TEAM_SETTING_NAME; + if (g_strcmp0 (con_type, "bridge-slave") == 0) + slave_type = NM_SETTING_BRIDGE_SETTING_NAME; + + if (slave_type) { + const char *dev_ifname = get_ethernet_device_name (nmc); + + /* For bond/team/bridge slaves add 'wired' setting */ + setting = nm_setting_wired_new (); + nm_connection_add_setting (connection, setting); + + g_object_set (s_con, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0", + NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type, + NULL); + } else { + /* Add a "base" setting to the connection by default */ + base_setting = nmc_setting_new_for_name (con_type); + if (!base_setting) + return; + nm_connection_add_setting (connection, base_setting); + + /* Set a sensible bond/team/bridge interface name by default */ + if (g_strcmp0 (con_type, NM_SETTING_BOND_SETTING_NAME) == 0) + g_object_set (NM_SETTING_BOND (base_setting), + NM_SETTING_BOND_INTERFACE_NAME, "nm-bond", + NULL); + if (g_strcmp0 (con_type, NM_SETTING_TEAM_SETTING_NAME) == 0) + g_object_set (NM_SETTING_TEAM (base_setting), + NM_SETTING_TEAM_INTERFACE_NAME, "nm-team", + NULL); + if (g_strcmp0 (con_type, NM_SETTING_BRIDGE_SETTING_NAME) == 0) + g_object_set (NM_SETTING_BRIDGE (base_setting), + NM_SETTING_BRIDGE_INTERFACE_NAME, "nm-bridge", + NULL); + + /* Set sensible initial VLAN values */ + if (g_strcmp0 (con_type, NM_SETTING_VLAN_SETTING_NAME) == 0) { + const char *dev_ifname = get_ethernet_device_name (nmc); + + g_object_set (NM_SETTING_VLAN (base_setting), + NM_SETTING_VLAN_PARENT, dev_ifname ? dev_ifname : "eth0", + NM_SETTING_VLAN_ID, 1, + NULL); + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_VLAN_SETTING_NAME, + NULL); + } + + /* Initialize 'transport-mode' so that 'infiniband' is valid */ + if (g_strcmp0 (con_type, NM_SETTING_INFINIBAND_SETTING_NAME) == 0) + g_object_set (NM_SETTING_INFINIBAND (base_setting), + NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", + NULL); + + /* Initialize 'number' so that 'cdma' is valid */ + if (g_strcmp0 (con_type, NM_SETTING_CDMA_SETTING_NAME) == 0) + g_object_set (NM_SETTING_CDMA (base_setting), + NM_SETTING_CDMA_NUMBER, "#777", + NULL); + + /* Initialize 'number' so that 'gsm' is valid */ + if (g_strcmp0 (con_type, NM_SETTING_GSM_SETTING_NAME) == 0) + g_object_set (NM_SETTING_GSM (base_setting), + NM_SETTING_GSM_NUMBER, "*99#", + NULL); + + /* Wi-Fi */ + if (g_strcmp0 (con_type, NM_SETTING_WIRELESS_SETTING_NAME) == 0) { + /* For Wi-Fi set mode to "infrastructure". Even though mode == NULL + * is regarded as "infrastructure", explicit value makes no doubts. + */ + g_object_set (NM_SETTING_WIRELESS (base_setting), + NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, + NULL); + + /* Do custom initialization for wifi setting */ + nmc_setting_custom_init (base_setting); + } + + /* Always add IPv4 and IPv6 settings for non-slave connections */ + setting = nm_setting_ip4_config_new (); + nmc_setting_custom_init (setting); + nm_connection_add_setting (connection, setting); + + setting = nm_setting_ip6_config_new (); + nmc_setting_custom_init (setting); + nm_connection_add_setting (connection, setting); + } +} + +static void +editor_init_existing_connection (NMConnection *connection) +{ + NMSettingIP4Config *s_ip4; + NMSettingIP6Config *s_ip6; + NMSettingWireless *s_wireless; + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + s_ip6 = nm_connection_get_setting_ip6_config (connection); + s_wireless = nm_connection_get_setting_wireless (connection); + + if (s_ip4) + nmc_setting_ip4_connect_handlers (s_ip4); + if (s_ip6) + nmc_setting_ip6_connect_handlers (s_ip6); + if (s_wireless) + nmc_setting_wireless_connect_handlers (s_wireless); +} + +static NMCResultCode +do_connection_edit (NmCli *nmc, int argc, char **argv) +{ + NMConnection *connection = NULL; + NMSettingConnection *s_con; + const char *connection_type; + char *uuid; + char *default_name = NULL; + const char *type = NULL; + char *type_ask = NULL; + const char *con_name = NULL; + const char *con = NULL; + const char *con_id = NULL; + const char *con_uuid = NULL; + const char *con_path = NULL; + const char *selector = NULL; + char *tmp_str; + GError *error = NULL; + GError *err1 = NULL; + nmc_arg_t exp_args[] = { {"type", TRUE, &type, FALSE}, + {"con-name", TRUE, &con_name, FALSE}, + {"id", TRUE, &con_id, FALSE}, + {"uuid", TRUE, &con_uuid, FALSE}, + {"path", TRUE, &con_path, FALSE}, + {NULL} }; + + nmc->return_value = NMC_RESULT_SUCCESS; + + if (argc == 1) + con = *argv; + else { + if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, &error)) { + g_string_assign (nmc->return_text, error->message); + nmc->return_value = error->code; + g_clear_error (&error); + goto error; + } + } + + /* Setup some readline completion stuff */ + /* Set a pointer to an alternative function to create matches */ + rl_attempted_completion_function = (rl_completion_func_t *) nmcli_editor_tab_completion; + /* Use ' ' and '.' as word break characters */ + rl_completer_word_break_characters = ". "; + + if (!con) { + if (con_id && !con_uuid && !con_path) { + con = con_id; + selector = "id"; + } else if (con_uuid && !con_id && !con_path) { + con = con_uuid; + selector = "uuid"; + } else if (con_path && !con_id && !con_uuid) { + con = con_path; + selector = "path"; + } else if (!con_path && !con_id && !con_uuid) { + /* no-op */ + } else { + g_string_printf (nmc->return_text, + _("Error: only one of 'id', uuid, or 'path' can be provided.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } + + if (con) { + /* Existing connection */ + NMConnection *found_con; + + found_con = nmc_find_connection (nmc->system_connections, selector, con, NULL); + if (!found_con) { + g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), con); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + + /* Duplicate the connection and use that so that we need not + * differentiate existing vs. new later + */ + connection = nm_connection_duplicate (found_con); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + connection_type = nm_setting_connection_get_connection_type (s_con); + + if (type) + printf (_("Warning: editing existing connection '%s'; 'type' argument is ignored\n"), + nm_connection_get_id (connection)); + if (con_name) + printf (_("Warning: editing existing connection '%s'; 'con-name' argument is ignored\n"), + nm_connection_get_id (connection)); + + /* Load previously saved history commands for the connection */ + load_history_cmds (nm_connection_get_uuid (connection)); + + editor_init_existing_connection (connection); + } else { + /* New connection */ + connection_type = check_valid_name (type, nmc_valid_connection_types, &err1); + tmp_str = get_valid_options_string (nmc_valid_connection_types); + + while (!connection_type) { + if (!type) + printf (_("Valid connection types: %s\n"), tmp_str); + else + printf (_("Error: invalid connection type; %s\n"), err1->message); + g_clear_error (&err1); + + type_ask = nmc_readline (EDITOR_PROMPT_CON_TYPE); + type = type_ask = type_ask ? g_strstrip (type_ask) : NULL; + connection_type = check_valid_name (type_ask, nmc_valid_connection_types, &err1); + g_free (type_ask); } + g_free (tmp_str); + + /* Create a new connection object */ + connection = nm_connection_new (); + + /* Build up the 'connection' setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + uuid = nm_utils_uuid_generate (); + if (con_name) + default_name = g_strdup (con_name); + else + default_name = unique_connection_name (nmc->system_connections, + get_name_alias (connection_type, nmc_valid_connection_types)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, default_name, + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, connection_type, + NULL); + g_free (uuid); + g_free (default_name); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + /* Initialize the new connection so that it is valid from the start */ + editor_init_new_connection (nmc, connection); } - if (active) - nm_client_deactivate_connection (nmc->client, active); + printf ("\n"); + printf (_("===| nmcli interactive connection editor |===")); + printf ("\n\n"); + if (con) + printf (_("Editing existing '%s' connection: '%s'"), connection_type, con); else - fprintf (stderr, _("Warning: Connection not active\n")); - sleep (1); /* Don't quit immediatelly and give NM time to check our permissions */ + printf (_("Adding a new '%s' connection"), connection_type); + printf ("\n\n"); + printf (_("Type 'help' or '?' for available commands.")); + printf ("\n"); + printf (_("Type 'describe [<setting>.<prop>]' for detailed property description.")); + printf ("\n\n"); + + /* Set global variables for use in TAB completion */ + nmc_tab_completion.nmc = nmc; + nmc_tab_completion.con_type = g_strdup (connection_type); + nmc_tab_completion.connection = connection; + + /* Run menu loop */ + editor_menu_main (nmc, connection, connection_type); + + if (connection) + g_object_unref (connection); + g_free (nmc_tab_completion.con_type); + + nmc->should_wait = TRUE; + return nmc->return_value; error: + g_assert (!connection); + g_free (type_ask); + nmc->should_wait = FALSE; return nmc->return_value; } + static void -delete_cb (NMRemoteConnection *con, GError *err, gpointer user_data) +modify_connection_cb (NMRemoteConnection *connection, + GError *error, + gpointer user_data) { NmCli *nmc = (NmCli *) user_data; - if (err) { - g_string_printf (nmc->return_text, _("Error: Connection deletion failed: %s"), err->message); - nmc->return_value = NMC_RESULT_ERROR_CON_DEL; + if (error) { + g_string_printf (nmc->return_text, + _("Error: Failed to modify connection '%s': (%d) %s"), + nm_connection_get_id (NM_CONNECTION (connection)), + error->code, error->message); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + } else { + if (nmc->print_output == NMC_PRINT_PRETTY) + printf (_("Connection '%s' (%s) successfully modified.\n"), + nm_connection_get_id (NM_CONNECTION (connection)), + nm_connection_get_uuid (NM_CONNECTION (connection))); } quit (); } +static NMCResultCode +do_connection_modify (NmCli *nmc, + gboolean temporary, + int argc, + char **argv) +{ + NMConnection *connection = NULL; + NMRemoteConnection *rc = NULL; + NMSetting *setting; + NMSettingConnection *s_con; + const char *con_type; + const char *name; + const char *selector = NULL; + const char *s_dot_p; + const char *value; + char **strv = NULL; + const char *setting_name; + char *property_name = NULL; + gboolean append = FALSE; + gboolean remove = FALSE; + GError *error = NULL; + + nmc->should_wait = FALSE; + + /* create NMClient */ + nmc->get_client (nmc); + + if (!nm_client_get_manager_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + goto finish; + } + + if (argc == 0) { + g_string_printf (nmc->return_text, _("Error: No arguments provided.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + if ( strcmp (*argv, "id") == 0 + || strcmp (*argv, "uuid") == 0 + || strcmp (*argv, "path") == 0) { + + selector = *argv; + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), + selector); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + name = *argv; + } + name = *argv; + if (!name) { + g_string_printf (nmc->return_text, _("Error: connection ID is missing.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + connection = nmc_find_connection (nmc->system_connections, selector, name, NULL); + if (!connection) { + g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto finish; + } + rc = nm_remote_settings_get_connection_by_uuid (nmc->system_settings, + nm_connection_get_uuid (connection)); + if (!rc) { + g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto finish; + } + s_con = nm_connection_get_setting_connection (NM_CONNECTION (rc)); + g_assert (s_con); + con_type = nm_setting_connection_get_connection_type (s_con); + + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: <setting>.<property> argument is missing.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + + /* Go through arguments and set properties */ + while (argc) { + s_dot_p = *argv; + next_arg (&argc, &argv); + value = *argv; + next_arg (&argc, &argv); + + if (!s_dot_p) { + g_string_printf (nmc->return_text, _("Error: <setting>.<property> argument is missing.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + if (!value) { + g_string_printf (nmc->return_text, _("Error: value for '%s' is missing."), s_dot_p); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + /* Empty string will reset the value to default */ + if (value[0] == '\0') + value = NULL; + + if (s_dot_p[0] == '+') { + s_dot_p++; + append = TRUE; + } else if (s_dot_p[0] == '-') { + s_dot_p++; + remove = TRUE; + } + + strv = g_strsplit (s_dot_p, ".", 2); + if (g_strv_length (strv) != 2) { + g_string_printf (nmc->return_text, _("Error: invalid <setting>.<property> '%s'."), + s_dot_p); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + + setting_name = check_valid_name (strv[0], get_valid_settings_array (con_type), &error); + if (!setting_name) { + g_string_printf (nmc->return_text, _("Error: invalid or not allowed setting '%s': %s."), + strv[0], error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + setting = nm_connection_get_setting_by_name (NM_CONNECTION (rc), setting_name); + if (!setting) { + setting = nmc_setting_new_for_name (setting_name); + if (!setting) { + /* This should really not happen */ + g_string_printf (nmc->return_text, + "Error: don't know how to create '%s' setting.", + setting_name); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + goto finish; + } + nm_connection_add_setting (NM_CONNECTION (rc), setting); + } + + property_name = is_property_valid (setting, strv[1], &error); + if (!property_name) { + g_string_printf (nmc->return_text, _("Error: invalid property '%s': %s."), + strv[1], error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + + if (!remove) { + /* Set/add value */ + if (!append) + nmc_setting_reset_property (setting, property_name, NULL); + if (!nmc_setting_set_property (setting, property_name, value, &error)) { + g_string_printf (nmc->return_text, _("Error: failed to modify %s.%s: %s."), + strv[0], strv[1], error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + } else { + /* Remove value + * - either empty: remove whole value + * - or specified by index <0-n>: remove item at the index + * - or option name: remove item with the option name + */ + if (value) { + unsigned long idx; + if (nmc_string_to_uint (value, TRUE, 0, G_MAXUINT32, &idx)) + nmc_setting_remove_property_option (setting, property_name, NULL, idx, &error); + else + nmc_setting_remove_property_option (setting, property_name, value, 0, &error); + if (error) { + g_string_printf (nmc->return_text, _("Error: failed to remove a value from %s.%s: %s."), + strv[0], strv[1], error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + } else + nmc_setting_reset_property (setting, property_name, NULL); + } + + g_strfreev (strv); + strv = NULL; + } + + update_connection (!temporary, rc, modify_connection_cb, nmc); + +finish: + nmc->should_wait = (nmc->return_value == NMC_RESULT_SUCCESS); + g_free (property_name); + if (strv) + g_strfreev (strv); + g_clear_error (&error); + return nmc->return_value; +} + + +typedef struct { + NmCli *nmc; + int counter; +} DeleteStateInfo; + static void -connection_removed_cb (NMRemoteConnection *con, gpointer user_data) +delete_cb (NMRemoteConnection *con, GError *err, gpointer user_data) { - NmCli *nmc = (NmCli *) user_data; + DeleteStateInfo *info = (DeleteStateInfo *) user_data; - nmc->return_value = NMC_RESULT_SUCCESS; - quit (); + if (err) { + g_string_printf (info->nmc->return_text, _("Error: Connection deletion failed: %s"), err->message); + info->nmc->return_value = NMC_RESULT_ERROR_CON_DEL; + } + + info->counter--; + if (info->counter == 0) { + g_free (info); + quit (); + } } static NMCResultCode do_connection_delete (NmCli *nmc, int argc, char **argv) { NMConnection *connection = NULL; - const char *selector = NULL; - const char *id = NULL; - GError *error = NULL; + DeleteStateInfo *del_info = NULL; + char *line = NULL; + char **arg_arr = NULL; + char **arg_ptr = argv; + int arg_num = argc; + GString *invalid_cons = NULL; + gboolean del_info_free = FALSE; + GSList *pos = NULL; - while (argc > 0) { - if (strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0) { - selector = *argv; - if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_SUCCESS; + nmc->should_wait = FALSE; + + /* create NMClient */ + nmc->get_client (nmc); + + if (!nm_client_get_manager_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + goto finish; + } + + if (argc == 0) { + if (nmc->ask) { + line = nmc_readline (PROMPT_CONNECTION); + nmc_string_to_arg_array (line, "", &arg_arr, &arg_num); + arg_ptr = arg_arr; + } + if (arg_num == 0) { + g_string_printf (nmc->return_text, _("Error: No connection specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + } + + del_info = g_malloc0 (sizeof (DeleteStateInfo)); + del_info->nmc = nmc; + del_info->counter = 0; + del_info_free = TRUE; + + while (arg_num > 0) { + const char *selector = NULL; + + if ( strcmp (*arg_ptr, "id") == 0 + || strcmp (*arg_ptr, "uuid") == 0 + || strcmp (*arg_ptr, "path") == 0) { + selector = *arg_ptr; + if (next_arg (&arg_num, &arg_ptr) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } - id = *argv; } - else - fprintf (stderr, _("Unknown parameter: %s\n"), *argv); - argc--; - argv++; + connection = nmc_find_connection (nmc->system_connections, selector, *arg_ptr, &pos); + if (!connection) { + if (nmc->print_output != NMC_PRINT_TERSE) + printf (_("Error: unknown connection: %s\n"), *arg_ptr); + + if (!invalid_cons) + invalid_cons = g_string_new (NULL); + g_string_append_printf (invalid_cons, "'%s', ", *arg_ptr); + + /* take the next argument and continue */ + next_arg (&arg_num, &arg_ptr); + continue; + } + + /* We need to wait a bit so that nmcli's permissions can be checked. + * We will exit when D-Bus return (error) messages are received. + */ + nmc->should_wait = TRUE; + + /* del_info deallocation is handled in delete_cb() */ + del_info_free = FALSE; + + del_info->counter++; + + /* Delete the connection */ + nm_remote_connection_delete (NM_REMOTE_CONNECTION (connection), delete_cb, del_info); + + /* Take next argument (if there's no other connection of the same name) */ + if (!pos) + next_arg (&arg_num, &arg_ptr); } - if (!id) { - g_string_printf (nmc->return_text, _("Error: id or uuid has to be specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; +finish: + if (del_info_free) + g_free (del_info); + g_strfreev (arg_arr); + + if (invalid_cons) { + g_string_truncate (invalid_cons, invalid_cons->len-2); /* truncate trailing ", " */ + g_string_printf (nmc->return_text, _("Error: cannot delete unknown connection(s): %s."), + invalid_cons->str); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + g_string_free (invalid_cons, TRUE); } + return nmc->return_value; +} - if (!nmc_is_nm_running (nmc, &error)) { - if (error) { - g_string_printf (nmc->return_text, _("Error: Can't find out if NetworkManager is running: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - g_error_free (error); - } else { - g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); +static NMCResultCode +do_connection_reload (NmCli *nmc, int argc, char **argv) +{ + GError *error = NULL; + + nmc->return_value = NMC_RESULT_SUCCESS; + nmc->should_wait = FALSE; + + if (!nm_client_get_manager_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + return nmc->return_value; + } + + if (!nm_remote_settings_reload_connections (nmc->system_settings, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + if (error->code == NM_REMOTE_SETTINGS_ERROR_SERVICE_UNAVAILABLE) nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; - } - goto error; + else + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_clear_error (&error); } - connection = find_connection (nmc->system_connections, selector, id); + return nmc->return_value; +} - if (!connection) { - g_string_printf (nmc->return_text, _("Error: Unknown connection: %s."), id); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - goto error; +static NMCResultCode +do_connection_load (NmCli *nmc, int argc, char **argv) +{ + GError *error = NULL; + char **filenames, **failures = NULL; + int i; + + nmc->return_value = NMC_RESULT_SUCCESS; + nmc->should_wait = FALSE; + + if (!nm_client_get_manager_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + return nmc->return_value; } - /* We need to wait a bit so that nmcli's permissions can be queried. - * We will exit either on "Removed" signal or when D-Bus return (error) - * message is received. - */ - nmc->should_wait = TRUE; + if (argc == 0) { + g_string_printf (nmc->return_text, _("Error: No connection specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return nmc->return_value; + } - /* Connect to "Removed" signal to be able to exit when connection was removed */ - g_signal_connect (connection, NM_REMOTE_CONNECTION_REMOVED, G_CALLBACK (connection_removed_cb), nmc); + filenames = g_new (char *, argc + 1); + for (i = 0; i < argc; i++) + filenames[i] = argv[i]; + filenames[i] = NULL; - /* Delete the connection */ - nm_remote_connection_delete (NM_REMOTE_CONNECTION (connection), delete_cb, nmc); + nm_remote_settings_load_connections (nmc->system_settings, filenames, &failures, &error); + g_free (filenames); + if (error) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_error_free (error); + } - return nmc->return_value; + if (failures) { + for (i = 0; failures[i]; i++) + fprintf (stderr, _("Could not load file '%s'\n"), failures[i]); + g_strfreev (failures); + } -error: - nmc->should_wait = FALSE; return nmc->return_value; } + +typedef struct { + NmCli *nmc; + int argc; + char **argv; +} NmcEditorThreadData; + +static GThread *editor_thread; +static NmcEditorThreadData editor_thread_data; + +/* + * We need to run do_connection_edit() in a thread so that + * glib main loop is not blocked and could receive and process D-Bus + * return messages. + */ +static gpointer +connection_editor_thread_func (gpointer data) +{ + NmcEditorThreadData *td = (NmcEditorThreadData *) data; + + /* run editor for editing/adding connections */ + td->nmc->return_value = do_connection_edit (td->nmc, td->argc, td->argv); + + /* quit glib main loop now that we are done with this thread */ + quit (); + + return NULL; +} + +static char * +gen_func_connection_names (const char *text, int state) +{ + int i = 0; + GSList *iter; + const char **connections; + char *ret; + + if (!nm_cli.system_connections) + return NULL; + + connections = g_new (const char *, g_slist_length (nm_cli.system_connections) + 1); + for (iter = nm_cli.system_connections; iter; iter = g_slist_next (iter)) { + NMConnection *con = NM_CONNECTION (iter->data); + const char *id = nm_connection_get_id (con); + connections[i++] = id; + } + connections[i] = NULL; + + ret = nmc_rl_gen_func_basic (text, state, connections); + + g_free (connections); + return ret; +} + +static char ** +nmcli_con_tab_completion (const char *text, int start, int end) +{ + char **match_array = NULL; + rl_compentry_func_t *generator_func = NULL; + + /* Disable readline's default filename completion */ + rl_attempted_completion_over = 1; + + /* Disable appending space after completion */ + rl_completion_append_character = '\0'; + + if (!is_single_word (rl_line_buffer)) + return NULL; + + if (g_strcmp0 (rl_prompt, PROMPT_CONNECTION) == 0) + generator_func = gen_func_connection_names; + + if (generator_func) + match_array = rl_completion_matches (text, generator_func); + + return match_array; +} + static NMCResultCode parse_cmd (NmCli *nmc, int argc, char **argv) { GError *error = NULL; + rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_tab_completion; + if (argc == 0) { if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) goto opt_error; - nmc->return_value = do_connections_list (nmc, argc, argv); + nmc->return_value = do_connections_show (nmc, FALSE, argc, argv); } else { - - if (matches (*argv, "list") == 0) { - nmc->return_value = do_connections_list (nmc, argc-1, argv+1); + if (nmc_arg_is_help (*argv)) { + usage (); + goto usage_exit; } - else if (matches(*argv, "status") == 0) { - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; - nmc->return_value = do_connections_status (nmc, argc-1, argv+1); + else if (matches (*argv, "show") == 0) { + gboolean active = FALSE; + + if (nmc_arg_is_help (*(argv+1))) { + usage_connection_show (); + goto usage_exit; + } + + next_arg (&argc, &argv); + if (nmc_arg_is_option (*argv, "active")) { + active = TRUE; + next_arg (&argc, &argv); + } + nmc->return_value = do_connections_show (nmc, active, argc, argv); } else if (matches(*argv, "up") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_connection_up (); + goto usage_exit; + } nmc->return_value = do_connection_up (nmc, argc-1, argv+1); } else if (matches(*argv, "down") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_connection_down (); + goto usage_exit; + } nmc->return_value = do_connection_down (nmc, argc-1, argv+1); } + else if (matches(*argv, "add") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_connection_add (); + goto usage_exit; + } + nmc->return_value = do_connection_add (nmc, argc-1, argv+1); + } + else if (matches(*argv, "edit") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_connection_edit (); + goto usage_exit; + } + editor_thread_data.nmc = nmc; + editor_thread_data.argc = argc - 1; + editor_thread_data.argv = argv + 1; + editor_thread = g_thread_new ("editor-thread", connection_editor_thread_func, &editor_thread_data); + g_thread_unref (editor_thread); + } else if (matches(*argv, "delete") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_connection_delete (); + goto usage_exit; + } nmc->return_value = do_connection_delete (nmc, argc-1, argv+1); } - else if ( matches (*argv, "help") == 0 - || (g_str_has_prefix (*argv, "-") && matches ((*argv)+1, "help") == 0) - || (g_str_has_prefix (*argv, "--") && matches ((*argv)+2, "help") == 0)) { - usage (); - nmc->should_wait = FALSE; + else if (matches(*argv, "reload") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_connection_reload (); + goto usage_exit; + } + nmc->return_value = do_connection_reload (nmc, argc-1, argv+1); + } + else if (matches(*argv, "load") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_connection_load (); + goto usage_exit; + } + nmc->return_value = do_connection_load (nmc, argc-1, argv+1); + } + else if (matches (*argv, "modify") == 0) { + gboolean temporary = FALSE; + + if (nmc_arg_is_help (*(argv+1))) { + usage_connection_modify (); + goto usage_exit; + } + next_arg (&argc, &argv); + if (nmc_arg_is_option (*argv, "temporary")) { + temporary = TRUE; + next_arg (&argc, &argv); + } + nmc->return_value = do_connection_modify (nmc, temporary, argc, argv); } else { usage (); - g_string_printf (nmc->return_text, _("Error: 'con' command '%s' is not valid."), *argv); + g_string_printf (nmc->return_text, _("Error: '%s' is not valid 'connection' command."), *argv); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; nmc->should_wait = FALSE; } @@ -1842,6 +8617,10 @@ parse_cmd (NmCli *nmc, int argc, char **argv) return nmc->return_value; +usage_exit: + nmc->should_wait = FALSE; + return nmc->return_value; + opt_error: g_string_printf (nmc->return_text, _("Error: %s."), error->message); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; @@ -1869,8 +8648,6 @@ get_connections_cb (NMRemoteSettings *settings, gpointer user_data) NMCResultCode do_connections (NmCli *nmc, int argc, char **argv) { - DBusGConnection *bus; - GError *error = NULL; int i = 0; gboolean real_cmd = FALSE; @@ -1899,17 +8676,8 @@ do_connections (NmCli *nmc, int argc, char **argv) args_info.argc = argc; args_info.argv = argv; - /* connect to DBus' system bus */ - bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); - if (error || !bus) { - g_string_printf (nmc->return_text, _("Error: could not connect to D-Bus.")); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - nmc->should_wait = FALSE; - return nmc->return_value; - } - /* get system settings */ - if (!(nmc->system_settings = nm_remote_settings_new (bus))) { + if (!(nmc->system_settings = nm_remote_settings_new (NULL))) { g_string_printf (nmc->return_text, _("Error: Could not get system settings.")); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; nmc->should_wait = FALSE; @@ -1930,9 +8698,6 @@ do_connections (NmCli *nmc, int argc, char **argv) g_signal_connect (nmc->system_settings, NM_REMOTE_SETTINGS_CONNECTIONS_READ, G_CALLBACK (get_connections_cb), &args_info); - - dbus_g_connection_unref (bus); - /* The rest will be done in get_connection_cb() callback. * We need to wait for signals that connections are read. */ diff --git a/cli/src/connections.h b/cli/src/connections.h index a1ed1c1c..c7368597 100644 --- a/cli/src/connections.h +++ b/cli/src/connections.h @@ -14,7 +14,7 @@ * 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 Red Hat, Inc. + * (C) Copyright 2010 - 2014 Red Hat, Inc. */ #ifndef NMC_CONNECTIONS_H diff --git a/cli/src/devices.c b/cli/src/devices.c index ffad9801..17492066 100644 --- a/cli/src/devices.c +++ b/cli/src/devices.c @@ -14,7 +14,7 @@ * 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 - 2012 Red Hat, Inc. + * (C) Copyright 2010 - 2014 Red Hat, Inc. */ #include "config.h" @@ -24,6 +24,7 @@ #include <stdlib.h> #include <errno.h> #include <netinet/ether.h> +#include <readline/readline.h> #include <glib.h> #include <glib/gi18n.h> @@ -65,185 +66,203 @@ #include "common.h" #include "devices.h" +/* define some prompts */ +#define PROMPT_INTERFACE _("Interface: ") -/* Available fields for 'dev status' */ +/* Available fields for 'device status' */ static NmcOutputField nmc_fields_dev_status[] = { - {"DEVICE", N_("DEVICE"), 10, NULL, 0}, /* 0 */ - {"TYPE", N_("TYPE"), 17, NULL, 0}, /* 1 */ - {"STATE", N_("STATE"), 13, NULL, 0}, /* 2 */ - {"DBUS-PATH", N_("DBUS-PATH"), 43, NULL, 0}, /* 3 */ - {NULL, NULL, 0, NULL, 0} + {"DEVICE", N_("DEVICE"), 10}, /* 0 */ + {"TYPE", N_("TYPE"), 17}, /* 1 */ + {"STATE", N_("STATE"), 13}, /* 2 */ + {"DBUS-PATH", N_("DBUS-PATH"), 43}, /* 3 */ + {"CONNECTION", N_("CONNECTION"), 20}, /* 4 */ + {"CON-UUID", N_("CON-UUID"), 38}, /* 5 */ + {"CON-PATH", N_("CON-PATH"), 51}, /* 6 */ + {NULL, NULL, 0} }; -#define NMC_FIELDS_DEV_STATUS_ALL "DEVICE,TYPE,STATE,DBUS-PATH" -#define NMC_FIELDS_DEV_STATUS_COMMON "DEVICE,TYPE,STATE" - - -/* Available sections for 'dev list' */ -static NmcOutputField nmc_fields_dev_list_sections[] = { - {"GENERAL", N_("GENERAL"), 0, NULL, 0}, /* 0 */ - {"CAPABILITIES", N_("CAPABILITIES"), 0, NULL, 0}, /* 1 */ - {"WIFI-PROPERTIES", N_("WIFI-PROPERTIES"), 0, NULL, 0}, /* 2 */ - {"AP", N_("AP"), 0, NULL, 0}, /* 3 */ - {"WIRED-PROPERTIES", N_("WIRED-PROPERTIES"), 0, NULL, 0}, /* 4 */ - {"WIMAX-PROPERTIES", N_("WIMAX-PROPERTIES"), 0, NULL, 0}, /* 5 */ - {"NSP", N_("NSP"), 0, NULL, 0}, /* 6 */ - {"IP4", N_("IP4"), 0, NULL, 0}, /* 7 */ - {"DHCP4", N_("DHCP4"), 0, NULL, 0}, /* 8 */ - {"IP6", N_("IP6"), 0, NULL, 0}, /* 9 */ - {"DHCP6", N_("DHCP6"), 0, NULL, 0}, /* 10 */ - {"BOND", N_("BOND"), 0, NULL, 0}, /* 11 */ - {"VLAN", N_("VLAN"), 0, NULL, 0}, /* 12 */ - {"CONNECTIONS", N_("CONNECTIONS"), 0, NULL, 0}, /* 13 */ - {NULL, NULL, 0, NULL, 0} +#define NMC_FIELDS_DEV_STATUS_ALL "DEVICE,TYPE,STATE,DBUS-PATH,CONNECTION,CON-UUID,CON-PATH" +#define NMC_FIELDS_DEV_STATUS_COMMON "DEVICE,TYPE,STATE,CONNECTION" + + +/* Available fields for 'device show' - GENERAL part */ +static NmcOutputField nmc_fields_dev_show_general[] = { + {"NAME", N_("NAME"), 10}, /* 0 */ + {"DEVICE", N_("DEVICE"), 10}, /* 1 */ + {"TYPE", N_("TYPE"), 17}, /* 2 */ + {"VENDOR", N_("VENDOR"), 20}, /* 3 */ + {"PRODUCT", N_("PRODUCT"), 50}, /* 4 */ + {"DRIVER", N_("DRIVER"), 9}, /* 5 */ + {"DRIVER-VERSION", N_("DRIVER-VERSION"), 18}, /* 6 */ + {"FIRMWARE-VERSION", N_("FIRMWARE-VERSION"), 18}, /* 7 */ + {"HWADDR", N_("HWADDR"), 19}, /* 8 */ + {"MTU", N_("MTU"), 10}, /* 9 */ + {"STATE", N_("STATE"), 14}, /* 10 */ + {"REASON", N_("REASON"), 25}, /* 11 */ + {"UDI", N_("UDI"), 64}, /* 12 */ + {"IP-IFACE", N_("IP-IFACE"), 10}, /* 13 */ + {"NM-MANAGED", N_("NM-MANAGED"), 15}, /* 14 */ + {"AUTOCONNECT", N_("AUTOCONNECT"), 15}, /* 15 */ + {"FIRMWARE-MISSING", N_("FIRMWARE-MISSING"), 18}, /* 16 */ + {"CONNECTION", N_("CONNECTION"), 20}, /* 17 */ + {"CON-UUID", N_("CON-UUID"), 38}, /* 18 */ + {"CON-PATH", N_("CON-PATH"), 51}, /* 19 */ + {NULL, NULL, 0} }; -#if WITH_WIMAX -#define NMC_FIELDS_DEV_LIST_SECTIONS_ALL "GENERAL,CAPABILITIES,BOND,VLAN,CONNECTIONS,WIFI-PROPERTIES,AP,WIRED-PROPERTIES,WIMAX-PROPERTIES,NSP,IP4,DHCP4,IP6,DHCP6" -#define NMC_FIELDS_DEV_LIST_SECTIONS_COMMON "GENERAL,CAPABILITIES,BOND,VLAN,CONNECTIONS,WIFI-PROPERTIES,AP,WIRED-PROPERTIES,WIMAX-PROPERTIES,NSP,IP4,DHCP4,IP6,DHCP6" -#else -#define NMC_FIELDS_DEV_LIST_SECTIONS_ALL "GENERAL,CAPABILITIES,BOND,VLAN,CONNECTIONS,WIFI-PROPERTIES,AP,WIRED-PROPERTIES,IP4,DHCP4,IP6,DHCP6" -#define NMC_FIELDS_DEV_LIST_SECTIONS_COMMON "GENERAL,CAPABILITIES,BOND,VLAN,CONNECTIONS,WIFI-PROPERTIES,AP,WIRED-PROPERTIES,IP4,DHCP4,IP6,DHCP6" -#endif - -/* Available fields for 'dev list' - GENERAL part */ -static NmcOutputField nmc_fields_dev_list_general[] = { - {"NAME", N_("NAME"), 10, NULL, 0}, /* 0 */ - {"DEVICE", N_("DEVICE"), 10, NULL, 0}, /* 1 */ - {"TYPE", N_("TYPE"), 17, NULL, 0}, /* 2 */ - {"VENDOR", N_("VENDOR"), 20, NULL, 0}, /* 3 */ - {"PRODUCT", N_("PRODUCT"), 50, NULL, 0}, /* 4 */ - {"DRIVER", N_("DRIVER"), 9, NULL, 0}, /* 5 */ - {"DRIVER-VERSION", N_("DRIVER-VERSION"), 18, NULL, 0}, /* 6 */ - {"FIRMWARE-VERSION", N_("FIRMWARE-VERSION"), 18, NULL, 0}, /* 7 */ - {"HWADDR", N_("HWADDR"), 19, NULL, 0}, /* 8 */ - {"STATE", N_("STATE"), 14, NULL, 0}, /* 9 */ - {"REASON", N_("REASON"), 25, NULL, 0}, /* 10 */ - {"UDI", N_("UDI"), 64, NULL, 0}, /* 11 */ - {"IP-IFACE", N_("IP-IFACE"), 10, NULL, 0}, /* 12 */ - {"NM-MANAGED", N_("NM-MANAGED"), 15, NULL, 0}, /* 13 */ - {"AUTOCONNECT", N_("AUTOCONNECT"), 15, NULL, 0}, /* 14 */ - {"FIRMWARE-MISSING", N_("FIRMWARE-MISSING"), 18, NULL, 0}, /* 15 */ - {"CONNECTION", N_("CONNECTION"), 51, NULL, 0}, /* 16 */ - {NULL, NULL, 0, NULL, 0} +#define NMC_FIELDS_DEV_SHOW_GENERAL_ALL "NAME,DEVICE,TYPE,VENDOR,PRODUCT,DRIVER,DRIVER-VERSION,FIRMWARE-VERSION,HWADDR,MTU,STATE,REASON,"\ + "UDI,IP-IFACE,NM-MANAGED,AUTOCONNECT,FIRMWARE-MISSING,CONNECTION,CON-UUID,CON-PATH" +#define NMC_FIELDS_DEV_SHOW_GENERAL_COMMON "NAME,DEVICE,TYPE,VENDOR,PRODUCT,DRIVER,HWADDR,STATE" + +/* Available fields for 'device show' - CONNECTIONS part */ +static NmcOutputField nmc_fields_dev_show_connections[] = { + {"NAME", N_("NAME"), 10}, /* 0 */ + {"AVAILABLE-CONNECTION-PATHS", N_("AVAILABLE-CONNECTION-PATHS"), 80}, /* 1 */ + {"AVAILABLE-CONNECTIONS", N_("AVAILABLE-CONNECTIONS"), 80}, /* 2 */ + {NULL, NULL, 0} }; -#define NMC_FIELDS_DEV_LIST_GENERAL_ALL "NAME,DEVICE,TYPE,VENDOR,PRODUCT,DRIVER,DRIVER-VERSION,FIRMWARE-VERSION,HWADDR,STATE,REASON,UDI,IP-IFACE,"\ - "NM-MANAGED,AUTOCONNECT,FIRMWARE-MISSING,CONNECTION" -#define NMC_FIELDS_DEV_LIST_GENERAL_COMMON "NAME,DEVICE,TYPE,VENDOR,PRODUCT,DRIVER,HWADDR,STATE" - -/* Available fields for 'dev list' - CONNECTIONS part */ -static NmcOutputField nmc_fields_dev_list_connections[] = { - {"NAME", N_("NAME"), 10, NULL, 0}, /* 0 */ - {"AVAILABLE-CONNECTION-PATHS", N_("AVAILABLE-CONNECTION-PATHS"), 80, NULL, 0}, /* 1 */ - {"AVAILABLE-CONNECTIONS", N_("AVAILABLE-CONNECTIONS"), 80, NULL, 0}, /* 2 */ - {NULL, NULL, 0, NULL, 0} +#define NMC_FIELDS_DEV_SHOW_CONNECTIONS_ALL "AVAILABLE-CONNECTION-PATHS,AVAILABLE-CONNECTIONS" +#define NMC_FIELDS_DEV_SHOW_CONNECTIONS_COMMON "AVAILABLE-CONNECTION-PATHS,AVAILABLE-CONNECTIONS" + +/* Available fields for 'device show' - CAPABILITIES part */ +static NmcOutputField nmc_fields_dev_show_cap[] = { + {"NAME", N_("NAME"), 13}, /* 0 */ + {"CARRIER-DETECT", N_("CARRIER-DETECT"), 16}, /* 1 */ + {"SPEED", N_("SPEED"), 10}, /* 2 */ + {NULL, NULL, 0} }; -#define NMC_FIELDS_DEV_LIST_CONNECTIONS_ALL "AVAILABLE-CONNECTION-PATHS,AVAILABLE-CONNECTIONS" -#define NMC_FIELDS_DEV_LIST_CONNECTIONS_COMMON "AVAILABLE-CONNECTION-PATHS,AVAILABLE-CONNECTIONS" - -/* Available fields for 'dev list' - CAPABILITIES part */ -static NmcOutputField nmc_fields_dev_list_cap[] = { - {"NAME", N_("NAME"), 13, NULL, 0}, /* 0 */ - {"CARRIER-DETECT", N_("CARRIER-DETECT"), 16, NULL, 0}, /* 1 */ - {"SPEED", N_("SPEED"), 10, NULL, 0}, /* 2 */ - {NULL, NULL, 0, NULL, 0} +#define NMC_FIELDS_DEV_SHOW_CAP_ALL "NAME,CARRIER-DETECT,SPEED" +#define NMC_FIELDS_DEV_SHOW_CAP_COMMON "NAME,CARRIER-DETECT,SPEED" + +/* Available fields for 'device show' - wired properties part */ +static NmcOutputField nmc_fields_dev_show_wired_prop[] = { + {"NAME", N_("NAME"), 18}, /* 0 */ + {"CARRIER", N_("CARRIER"), 10}, /* 1 */ + {NULL, NULL, 0} }; -#define NMC_FIELDS_DEV_LIST_CAP_ALL "NAME,CARRIER-DETECT,SPEED" -#define NMC_FIELDS_DEV_LIST_CAP_COMMON "NAME,CARRIER-DETECT,SPEED" - -/* Available fields for 'dev list' - wired properties part */ -static NmcOutputField nmc_fields_dev_list_wired_prop[] = { - {"NAME", N_("NAME"), 18, NULL, 0}, /* 0 */ - {"CARRIER", N_("CARRIER"), 10, NULL, 0}, /* 1 */ - {NULL, NULL, 0, NULL, 0} +#define NMC_FIELDS_DEV_SHOW_WIRED_PROP_ALL "NAME,CARRIER" +#define NMC_FIELDS_DEV_SHOW_WIRED_PROP_COMMON "NAME,CARRIER" + +/* Available fields for 'device show' - wireless properties part */ +static NmcOutputField nmc_fields_dev_show_wifi_prop[] = { + {"NAME", N_("NAME"), 18}, /* 0 */ + {"WEP", N_("WEP"), 5}, /* 1 */ + {"WPA", N_("WPA"), 5}, /* 2 */ + {"WPA2", N_("WPA2"), 6}, /* 3 */ + {"TKIP", N_("TKIP"), 6}, /* 4 */ + {"CCMP", N_("CCMP"), 6}, /* 5 */ + {"AP", N_("AP"), 6}, /* 6 */ + {"ADHOC", N_("ADHOC"), 6}, /* 7 */ + {NULL, NULL, 0} }; -#define NMC_FIELDS_DEV_LIST_WIRED_PROP_ALL "NAME,CARRIER" -#define NMC_FIELDS_DEV_LIST_WIRED_PROP_COMMON "NAME,CARRIER" - - -/* Available fields for 'dev list' - wireless properties part */ -static NmcOutputField nmc_fields_dev_list_wifi_prop[] = { - {"NAME", N_("NAME"), 18, NULL, 0}, /* 0 */ - {"WEP", N_("WEP"), 5, NULL, 0}, /* 1 */ - {"WPA", N_("WPA"), 5, NULL, 0}, /* 2 */ - {"WPA2", N_("WPA2"), 6, NULL, 0}, /* 3 */ - {"TKIP", N_("TKIP"), 6, NULL, 0}, /* 4 */ - {"CCMP", N_("CCMP"), 6, NULL, 0}, /* 5 */ - {"AP", N_("AP"), 6, NULL, 0}, /* 6 */ - {"ADHOC", N_("ADHOC"), 6, NULL, 0}, /* 7 */ - {NULL, NULL, 0, NULL, 0} -}; -#define NMC_FIELDS_DEV_LIST_WIFI_PROP_ALL "NAME,WEP,WPA,WPA2,TKIP,CCMP,AP,ADHOC" -#define NMC_FIELDS_DEV_LIST_WIFI_PROP_COMMON "NAME,WEP,WPA,WPA2,TKIP,CCMP,AP,ADHOC" - -#if WITH_WIMAX -/* Available fields for 'dev list' - wimax properties part */ -static NmcOutputField nmc_fields_dev_list_wimax_prop[] = { - {"NAME", N_("NAME"), 18, NULL, 0}, /* 0 */ - {"CTR-FREQ", N_("CTR-FREQ"), 7, NULL, 0}, /* 1 */ - {"RSSI", N_("RSSI"), 5, NULL, 0}, /* 2 */ - {"CINR", N_("CINR"), 5, NULL, 0}, /* 3 */ - {"TX-POW", N_("TX-POW"), 5, NULL, 0}, /* 4 */ - {"BSID", N_("BSID"), 18, NULL, 0}, /* 5 */ - {NULL, NULL, 0, NULL, 0} +#define NMC_FIELDS_DEV_SHOW_WIFI_PROP_ALL "NAME,WEP,WPA,WPA2,TKIP,CCMP,AP,ADHOC" +#define NMC_FIELDS_DEV_SHOW_WIFI_PROP_COMMON "NAME,WEP,WPA,WPA2,TKIP,CCMP,AP,ADHOC" + +/* Available fields for 'device show' - wimax properties part */ +static NmcOutputField nmc_fields_dev_show_wimax_prop[] = { + {"NAME", N_("NAME"), 18}, /* 0 */ + {"CTR-FREQ", N_("CTR-FREQ"), 7}, /* 1 */ + {"RSSI", N_("RSSI"), 5}, /* 2 */ + {"CINR", N_("CINR"), 5}, /* 3 */ + {"TX-POW", N_("TX-POW"), 5}, /* 4 */ + {"BSID", N_("BSID"), 18}, /* 5 */ + {NULL, NULL, 0} }; -#define NMC_FIELDS_DEV_LIST_WIMAX_PROP_ALL "NAME,CTR-FREQ,RSSI,CINR,TX-POW,BSID" -#define NMC_FIELDS_DEV_LIST_WIMAX_PROP_COMMON "NAME,CTR-FREQ,RSSI,CINR,TX-POW,BSID" -#endif +#define NMC_FIELDS_DEV_SHOW_WIMAX_PROP_ALL "NAME,CTR-FREQ,RSSI,CINR,TX-POW,BSID" +#define NMC_FIELDS_DEV_SHOW_WIMAX_PROP_COMMON "NAME,CTR-FREQ,RSSI,CINR,TX-POW,BSID" -/* Available fields for 'dev wifi list' */ +/* Available fields for 'device wifi list' */ static NmcOutputField nmc_fields_dev_wifi_list[] = { - {"NAME", N_("NAME"), 15, NULL, 0}, /* 0 */ - {"SSID", N_("SSID"), 33, NULL, 0}, /* 1 */ - {"BSSID", N_("BSSID"), 19, NULL, 0}, /* 2 */ - {"MODE", N_("MODE"), 16, NULL, 0}, /* 3 */ - {"FREQ", N_("FREQ"), 10, NULL, 0}, /* 4 */ - {"RATE", N_("RATE"), 10, NULL, 0}, /* 5 */ - {"SIGNAL", N_("SIGNAL"), 8, NULL, 0}, /* 6 */ - {"SECURITY", N_("SECURITY"), 10, NULL, 0}, /* 7 */ - {"WPA-FLAGS", N_("WPA-FLAGS"), 25, NULL, 0}, /* 8 */ - {"RSN-FLAGS", N_("RSN-FLAGS"), 25, NULL, 0}, /* 9 */ - {"DEVICE", N_("DEVICE"), 10, NULL, 0}, /* 10 */ - {"ACTIVE", N_("ACTIVE"), 8, NULL, 0}, /* 11 */ - {"DBUS-PATH", N_("DBUS-PATH"), 46, NULL, 0}, /* 12 */ - {NULL, NULL, 0, NULL, 0} + {"NAME", N_("NAME"), 15}, /* 0 */ + {"SSID", N_("SSID"), 33}, /* 1 */ + {"SSID-HEX", N_("SSID-HEX"), 66}, /* 2 */ + {"BSSID", N_("BSSID"), 19}, /* 3 */ + {"MODE", N_("MODE"), 16}, /* 4 */ + {"CHAN", N_("CHAN"), 6}, /* 5 */ + {"FREQ", N_("FREQ"), 10}, /* 6 */ + {"RATE", N_("RATE"), 10}, /* 7 */ + {"SIGNAL", N_("SIGNAL"), 8}, /* 8 */ + {"BARS", N_("BARS"), 6}, /* 9 */ + {"SECURITY", N_("SECURITY"), 10}, /* 10 */ + {"WPA-FLAGS", N_("WPA-FLAGS"), 25}, /* 11 */ + {"RSN-FLAGS", N_("RSN-FLAGS"), 25}, /* 12 */ + {"DEVICE", N_("DEVICE"), 10}, /* 13 */ + {"ACTIVE", N_("ACTIVE"), 8}, /* 14 */ + {"IN-USE", N_("*"), 1}, /* 15 */ + {"DBUS-PATH", N_("DBUS-PATH"), 46}, /* 16 */ + {NULL, NULL, 0} }; -#define NMC_FIELDS_DEV_WIFI_LIST_ALL "SSID,BSSID,MODE,FREQ,RATE,SIGNAL,SECURITY,WPA-FLAGS,RSN-FLAGS,DEVICE,ACTIVE,DBUS-PATH" -#define NMC_FIELDS_DEV_WIFI_LIST_COMMON "SSID,BSSID,MODE,FREQ,RATE,SIGNAL,SECURITY,ACTIVE" +#define NMC_FIELDS_DEV_WIFI_LIST_ALL "SSID,SSID-HEX,BSSID,MODE,CHAN,FREQ,RATE,SIGNAL,BARS,SECURITY,"\ + "WPA-FLAGS,RSN-FLAGS,DEVICE,ACTIVE,IN-USE,DBUS-PATH" +#define NMC_FIELDS_DEV_WIFI_LIST_COMMON "IN-USE,SSID,MODE,CHAN,RATE,SIGNAL,BARS,SECURITY" #define NMC_FIELDS_DEV_WIFI_LIST_FOR_DEV_LIST "NAME,"NMC_FIELDS_DEV_WIFI_LIST_COMMON -#if WITH_WIMAX -/* Available fields for 'dev wimax list' */ +/* Available fields for 'device wimax list' */ static NmcOutputField nmc_fields_dev_wimax_list[] = { - {"NAME", N_("NAME"), 15, NULL, 0}, /* 0 */ - {"NSP", N_("NSP"), 33, NULL, 0}, /* 1 */ - {"SIGNAL", N_("SIGNAL"), 8, NULL, 0}, /* 2 */ - {"TYPE", N_("TYPE"), 16, NULL, 0}, /* 3 */ - {"DEVICE", N_("DEVICE"), 10, NULL, 0}, /* 4 */ - {"ACTIVE", N_("ACTIVE"), 8, NULL, 0}, /* 5 */ - {"DBUS-PATH", N_("DBUS-PATH"), 46, NULL, 0}, /* 6 */ - {NULL, NULL, 0, NULL, 0} + {"NAME", N_("NAME"), 15}, /* 0 */ + {"NSP", N_("NSP"), 33}, /* 1 */ + {"SIGNAL", N_("SIGNAL"), 8}, /* 2 */ + {"TYPE", N_("TYPE"), 16}, /* 3 */ + {"DEVICE", N_("DEVICE"), 10}, /* 4 */ + {"ACTIVE", N_("ACTIVE"), 8}, /* 5 */ + {"DBUS-PATH", N_("DBUS-PATH"), 46}, /* 6 */ + {NULL, NULL, 0} }; #define NMC_FIELDS_DEV_WIMAX_LIST_ALL "NSP,SIGNAL,TYPE,DEVICE,ACTIVE,DBUS-PATH" #define NMC_FIELDS_DEV_WIMAX_LIST_COMMON "NSP,SIGNAL,TYPE,DEVICE,ACTIVE" #define NMC_FIELDS_DEV_WIMAX_LIST_FOR_DEV_LIST "NAME,"NMC_FIELDS_DEV_WIMAX_LIST_COMMON -#endif -/* Available fields for 'dev list' - BOND part */ -static NmcOutputField nmc_fields_dev_list_bond_prop[] = { - {"NAME", N_("NAME"), 18, NULL, 0}, /* 0 */ - {"SLAVES", N_("SLAVES"), 20, NULL, 0}, /* 1 */ - {NULL, NULL, 0, NULL, 0} +/* Available fields for 'device show' - BOND part */ +static NmcOutputField nmc_fields_dev_show_bond_prop[] = { + {"NAME", N_("NAME"), 18}, /* 0 */ + {"SLAVES", N_("SLAVES"), 20}, /* 1 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_DEV_SHOW_BOND_PROP_ALL "NAME,SLAVES" +#define NMC_FIELDS_DEV_SHOW_BOND_PROP_COMMON "NAME,SLAVES" + +/* Available fields for 'device show' - VLAN part */ +static NmcOutputField nmc_fields_dev_show_vlan_prop[] = { + {"NAME", N_("NAME"), 18}, /* 0 */ + {"ID", N_("ID"), 5}, /* 1 */ + {NULL, NULL, 0} }; -#define NMC_FIELDS_DEV_LIST_BOND_PROP_ALL "NAME,SLAVES" -#define NMC_FIELDS_DEV_LIST_BOND_PROP_COMMON "NAME,SLAVES" - -/* Available fields for 'dev list' - VLAN part */ -static NmcOutputField nmc_fields_dev_list_vlan_prop[] = { - {"NAME", N_("NAME"), 18, NULL, 0}, /* 0 */ - {"ID", N_("ID"), 5, NULL, 0}, /* 1 */ - {NULL, NULL, 0, NULL, 0} +#define NMC_FIELDS_DEV_SHOW_VLAN_PROP_ALL "NAME,ID" +#define NMC_FIELDS_DEV_SHOW_VLAN_PROP_COMMON "NAME,ID" + +/* defined in common.c */ +extern NmcOutputField nmc_fields_ip4_config[]; +extern NmcOutputField nmc_fields_ip6_config[]; +extern NmcOutputField nmc_fields_dhcp4_config[]; +extern NmcOutputField nmc_fields_dhcp6_config[]; + +/* Available sections for 'device show' */ +static NmcOutputField nmc_fields_dev_show_sections[] = { + {"GENERAL", N_("GENERAL"), 0, nmc_fields_dev_show_general + 1 }, /* 0 */ + {"CAPABILITIES", N_("CAPABILITIES"), 0, nmc_fields_dev_show_cap + 1 }, /* 1 */ + {"WIFI-PROPERTIES", N_("WIFI-PROPERTIES"), 0, nmc_fields_dev_show_wifi_prop + 1 }, /* 2 */ + {"AP", N_("AP"), 0, nmc_fields_dev_wifi_list + 1 }, /* 3 */ + {"WIRED-PROPERTIES", N_("WIRED-PROPERTIES"), 0, nmc_fields_dev_show_wired_prop + 1 }, /* 4 */ + {"WIMAX-PROPERTIES", N_("WIMAX-PROPERTIES"), 0, nmc_fields_dev_show_wimax_prop + 1 }, /* 5 */ + {"NSP", N_("NSP"), 0, nmc_fields_dev_wimax_list + 1 }, /* 6 */ + {"IP4", N_("IP4"), 0, nmc_fields_ip4_config + 1 }, /* 7 */ + {"DHCP4", N_("DHCP4"), 0, nmc_fields_dhcp4_config + 1 }, /* 8 */ + {"IP6", N_("IP6"), 0, nmc_fields_ip6_config + 1 }, /* 9 */ + {"DHCP6", N_("DHCP6"), 0, nmc_fields_dhcp6_config + 1 }, /* 10 */ + {"BOND", N_("BOND"), 0, nmc_fields_dev_show_bond_prop + 1 }, /* 11 */ + {"VLAN", N_("VLAN"), 0, nmc_fields_dev_show_vlan_prop + 1 }, /* 12 */ + {"CONNECTIONS", N_("CONNECTIONS"), 0, nmc_fields_dev_show_connections + 1 }, /* 13 */ + {NULL, NULL, 0, NULL } }; -#define NMC_FIELDS_DEV_LIST_VLAN_PROP_ALL "NAME,ID" -#define NMC_FIELDS_DEV_LIST_VLAN_PROP_COMMON "NAME,ID" +#if WITH_WIMAX +#define NMC_FIELDS_DEV_SHOW_SECTIONS_ALL "GENERAL,CAPABILITIES,BOND,VLAN,CONNECTIONS,WIFI-PROPERTIES,AP,WIRED-PROPERTIES,"\ + "WIMAX-PROPERTIES,NSP,IP4,DHCP4,IP6,DHCP6" +#define NMC_FIELDS_DEV_SHOW_SECTIONS_COMMON "GENERAL.DEVICE,GENERAL.TYPE,GENERAL.HWADDR,GENERAL.MTU,GENERAL.STATE,"\ + "GENERAL.CONNECTION,GENERAL.CON-PATH,WIRED-PROPERTIES,IP4,IP6" +#else +#define NMC_FIELDS_DEV_SHOW_SECTIONS_ALL "GENERAL,CAPABILITIES,BOND,VLAN,CONNECTIONS,WIFI-PROPERTIES,AP,WIRED-PROPERTIES,"\ + "IP4,DHCP4,IP6,DHCP6" +#define NMC_FIELDS_DEV_SHOW_SECTIONS_COMMON "GENERAL.DEVICE,GENERAL.TYPE,GENERAL.HWADDR,GENERAL.MTU,GENERAL.STATE,"\ + "GENERAL.CONNECTION,GENERAL.CON-PATH,WIRED-PROPERTIES,IP4,IP6" +#endif /* glib main loop variable - defined in nmcli.c */ @@ -257,23 +276,127 @@ usage (void) fprintf (stderr, _("Usage: nmcli device { COMMAND | help }\n\n" #if WITH_WIMAX - " COMMAND := { status | list | disconnect | wifi | wimax }\n\n" + "COMMAND := { status | show | connect | disconnect | wifi | wimax }\n\n" #else - " COMMAND := { status | list | disconnect | wifi }\n\n" + "COMMAND := { status | show | connect | disconnect | wifi }\n\n" #endif - " status\n" - " list [iface <iface>]\n" - " disconnect iface <iface> [--nowait] [--timeout <timeout>]\n" - " wifi [list [iface <iface>] [bssid <BSSID>]]\n" - " wifi connect <(B)SSID> [password <password>] [wep-key-type key|phrase] [iface <iface>] [bssid <BSSID>] [name <name>]\n" - " [--private] [--nowait] [--timeout <timeout>]\n" + " status\n\n" + " show [<ifname>]\n\n" + " connect <ifname>\n\n" + " disconnect <ifname>\n\n" + " wifi [list [ifname <ifname>] [bssid <BSSID>]]\n\n" + " wifi connect <(B)SSID> [password <password>] [wep-key-type key|phrase] [ifname <ifname>]\n" + " [bssid <BSSID>] [name <name>] [private yes|no]\n\n" + " wifi rescan [[ifname] <ifname>]\n\n" #if WITH_WIMAX - " wimax [list [iface <iface>] [nsp <name>]]\n" + " wimax [list [ifname <ifname>] [nsp <name>]]\n\n" #endif - "\n" )); } +static void +usage_device_status (void) +{ + fprintf (stderr, + _("Usage: nmcli device status { help }\n" + "\n" + "Show status for all devices.\n" + "By default, the following columns are shown:\n" + " DEVICE - interface name\n" + " TYPE - device type\n" + " STATE - device state\n" + " CONNECTION - connection activated on device (if any)\n" + "Displayed columns can be changed using '--fields' global option. 'status' is\n" + "the default command, which means 'nmcli device' calls 'nmcli device status'.\n\n")); +} + +static void +usage_device_show (void) +{ + fprintf (stderr, + _("Usage: nmcli device show { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [<ifname>]\n" + "\n" + "Show details of device(s).\n" + "The command lists details for all devices, or for a given device.\n\n")); +} + +static void +usage_device_connect (void) +{ + fprintf (stderr, + _("Usage: nmcli device connect { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := <ifname>\n" + "\n" + "Connect the device.\n" + "NetworkManager will try to find a suitable connection that will be activated.\n" + "It will also consider connections that are not set to auto-connect.\n\n")); +} + +static void +usage_device_disconnect (void) +{ + fprintf (stderr, + _("Usage: nmcli device disconnect { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := <ifname>\n" + "\n" + "Disconnect the device.\n" + "The command disconnects the device and prevents it from auto-activating\n" + "further connections without user/manual intervention.\n\n")); +} + +static void +usage_device_wifi (void) +{ + fprintf (stderr, + _("Usage: nmcli device wifi { ARGUMENTS | help }\n" + "\n" + "Perform operation on Wi-Fi devices.\n" + "\n" + "ARGUMENTS := [list [ifname <ifname>] [bssid <BSSID>]]\n" + "\n" + "List available Wi-Fi access points. The 'ifname' and 'bssid' options can be\n" + "used to list APs for a particular interface, or with a specific BSSID.\n" + "\n" + "ARGUMENTS := connect <(B)SSID> [password <password>] [wep-key-type key|phrase] [ifname <ifname>]\n" + " [bssid <BSSID>] [name <name>] [private yes|no]\n" + "\n" + "Connect to a Wi-Fi network specified by SSID or BSSID. The command creates\n" + "a new connection and then activates it on a device. This is a command-line\n" + "counterpart of clicking an SSID in a GUI client. The command always creates\n" + "a new connection and thus it is mainly useful for connecting to new Wi-Fi\n" + "networks. If a connection for the network already exists, it is better to\n" + "bring up the existing profile as follows: nmcli con up id <name>. Note that\n" + "only open, WEP and WPA-PSK networks are supported at the moment. It is also\n" + "assumed that IP configuration is obtained via DHCP.\n" + "\n" + "ARGUMENTS := rescan [[ifname] <ifname>]\n" + "\n" + "Request that NetworkManager immediately re-scan for available access points.\n" + "NetworkManager scans Wi-Fi networks periodically, but in some cases it might\n" + "be useful to start scanning manually. Note that this command does not show\n" + "the APs, use 'nmcli device wifi list' for that.\n\n")); +} + +#if WITH_WIMAX +static void +usage_device_wimax (void) +{ + fprintf (stderr, + _("Usage: nmcli device wimax { ARGUMENTS | help }\n" + "\n" + "Perform operation on WiMAX devices.\n" + "\n" + "ARGUMENTS := [list [ifname <ifname>] [nsp <name>]]\n" + "\n" + "List available WiMAX NSPs. The 'ifname' and 'nsp' options can be used to\n" + "list networks for a particular interface, or with a specific NSP.\n\n")); +} +#endif + /* quit main loop */ static void quit (void) @@ -286,47 +409,46 @@ quit (void) g_main_loop_quit (loop); /* quit main loop */ } -/* Convert device type to string. Use setting names strings to match with - * connection type names. - */ -static const char * -device_type_to_string (NMDevice *device) +static int +compare_devices (const void *a, const void *b) { - NMDeviceModemCapabilities caps = NM_DEVICE_MODEM_CAPABILITY_NONE; - - switch (nm_device_get_device_type (device)) { - case NM_DEVICE_TYPE_ETHERNET: - return NM_SETTING_WIRED_SETTING_NAME; - case NM_DEVICE_TYPE_ADSL: - return NM_SETTING_ADSL_SETTING_NAME; - case NM_DEVICE_TYPE_WIFI: - return NM_SETTING_WIRELESS_SETTING_NAME; - case NM_DEVICE_TYPE_MODEM: - caps = nm_device_modem_get_current_capabilities (NM_DEVICE_MODEM (device)); - if (caps & NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS) - return NM_SETTING_GSM_SETTING_NAME; - else if (caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO) - return NM_SETTING_CDMA_SETTING_NAME; - return _("Unknown"); - case NM_DEVICE_TYPE_BT: - return NM_SETTING_BLUETOOTH_SETTING_NAME; - case NM_DEVICE_TYPE_OLPC_MESH: - return NM_SETTING_OLPC_MESH_SETTING_NAME; -#if WITH_WIMAX - case NM_DEVICE_TYPE_WIMAX: - return NM_SETTING_WIMAX_SETTING_NAME; -#endif - case NM_DEVICE_TYPE_INFINIBAND: - return NM_SETTING_INFINIBAND_SETTING_NAME; - case NM_DEVICE_TYPE_BOND: - return NM_SETTING_BOND_SETTING_NAME; - case NM_DEVICE_TYPE_VLAN: - return NM_SETTING_VLAN_SETTING_NAME; - case NM_DEVICE_TYPE_BRIDGE: - return NM_SETTING_BRIDGE_SETTING_NAME; - default: - return _("Unknown"); + NMDevice *da = *(NMDevice **)a; + NMDevice *db = *(NMDevice **)b; + int cmp; + + /* Sort by later device states first */ + cmp = nm_device_get_state (db) - nm_device_get_state (da); + if (cmp != 0) + return cmp; + + cmp = g_strcmp0 (nm_device_get_type_description (da), + nm_device_get_type_description (db)); + if (cmp != 0) + return cmp; + + return g_strcmp0 (nm_device_get_iface (da), + nm_device_get_iface (db)); +} + +static NMDevice ** +get_devices_sorted (NMClient *client) +{ + const GPtrArray *devs; + NMDevice **sorted; + + devs = nm_client_get_devices (client); + if (!devs) { + sorted = g_new (NMDevice *, 1); + sorted[0] = NULL; + return sorted; } + + sorted = g_new (NMDevice *, devs->len + 1); + memcpy (sorted, devs->pdata, devs->len * sizeof (NMDevice *)); + sorted[devs->len] = NULL; + + qsort (sorted, devs->len, sizeof (NMDevice *), compare_devices); + return sorted; } static char * @@ -374,15 +496,17 @@ ap_wpa_rsn_flags_to_string (NM80211ApSecurityFlags flags) typedef struct { NmCli *nmc; int index; + guint32 output_flags; const char* active_bssid; const char* device; } APInfo; static void -detail_access_point (gpointer data, gpointer user_data) +fill_output_access_point (gpointer data, gpointer user_data) { NMAccessPoint *ap = NM_ACCESS_POINT (data); APInfo *info = (APInfo *) user_data; + NmcOutputField *arr; gboolean active = FALSE; NM80211ApFlags flags; NM80211ApSecurityFlags wpa_flags, rsn_flags; @@ -391,9 +515,16 @@ detail_access_point (gpointer data, gpointer user_data) const GByteArray *ssid; const char *bssid; NM80211Mode mode; - char *freq_str, *ssid_str, *bitrate_str, *strength_str, *wpa_flags_str, *rsn_flags_str; + char *channel_str, *freq_str, *ssid_str = NULL, *ssid_hex_str = NULL, + *bitrate_str, *strength_str, *wpa_flags_str, *rsn_flags_str; GString *security_str; char *ap_name; + const char *sig_level_0 = "____"; + const char *sig_level_1 = "â–‚___"; + const char *sig_level_2 = "â–‚â–„__"; + const char *sig_level_3 = "â–‚â–„â–†_"; + const char *sig_level_4 = "▂▄▆█"; + const char *sig_bars; if (info->active_bssid) { const char *current_bssid = nm_access_point_get_bssid (ap); @@ -410,75 +541,89 @@ detail_access_point (gpointer data, gpointer user_data) freq = nm_access_point_get_frequency (ap); mode = nm_access_point_get_mode (ap); bitrate = nm_access_point_get_max_bitrate (ap); - strength = nm_access_point_get_strength (ap); + strength = MIN (nm_access_point_get_strength (ap), 100); /* Convert to strings */ - ssid_str = ssid_to_printable ((const char *) ssid->data, ssid->len); + if (ssid) { + ssid_str = nm_utils_ssid_to_utf8 (ssid); + ssid_hex_str = ssid_to_hex ((const char *) ssid->data, ssid->len); + } + channel_str = g_strdup_printf ("%u", nm_utils_wifi_freq_to_channel (freq)); freq_str = g_strdup_printf (_("%u MHz"), freq); - bitrate_str = g_strdup_printf (_("%u MB/s"), bitrate/1000); + bitrate_str = g_strdup_printf (_("%u Mbit/s"), bitrate/1000); strength_str = g_strdup_printf ("%u", strength); wpa_flags_str = ap_wpa_rsn_flags_to_string (wpa_flags); rsn_flags_str = ap_wpa_rsn_flags_to_string (rsn_flags); + sig_bars = strength > 80 ? sig_level_4 : + strength > 55 ? sig_level_3 : + strength > 30 ? sig_level_2 : + strength > 5 ? sig_level_1 : + sig_level_0; security_str = g_string_new (NULL); - if ( !(flags & NM_802_11_AP_FLAGS_PRIVACY) - && (wpa_flags != NM_802_11_AP_SEC_NONE) - && (rsn_flags != NM_802_11_AP_SEC_NONE)) - g_string_append (security_str, _("Encrypted: ")); if ( (flags & NM_802_11_AP_FLAGS_PRIVACY) && (wpa_flags == NM_802_11_AP_SEC_NONE) - && (rsn_flags == NM_802_11_AP_SEC_NONE)) - g_string_append (security_str, _("WEP ")); - if (wpa_flags != NM_802_11_AP_SEC_NONE) - g_string_append (security_str, _("WPA ")); - if (rsn_flags != NM_802_11_AP_SEC_NONE) - g_string_append (security_str, _("WPA2 ")); + && (rsn_flags == NM_802_11_AP_SEC_NONE)) { + g_string_append (security_str, _("WEP")); + g_string_append_c (security_str, ' '); + } + if (wpa_flags != NM_802_11_AP_SEC_NONE) { + g_string_append (security_str, _("WPA1")); + g_string_append_c (security_str, ' '); + } + if (rsn_flags != NM_802_11_AP_SEC_NONE) { + g_string_append (security_str, _("WPA2")); + g_string_append_c (security_str, ' '); + } if ( (wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) - || (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) - g_string_append (security_str, _("Enterprise ")); + || (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { + g_string_append (security_str, _("802.1X")); + g_string_append_c (security_str, ' '); + } if (security_str->len > 0) g_string_truncate (security_str, security_str->len-1); /* Chop off last space */ + arr = nmc_dup_fields_array (nmc_fields_dev_wifi_list, + sizeof (nmc_fields_dev_wifi_list), + info->output_flags); + ap_name = g_strdup_printf ("AP[%d]", info->index++); /* AP */ - info->nmc->allowed_fields[0].value = ap_name; - info->nmc->allowed_fields[1].value = ssid_str; - info->nmc->allowed_fields[2].value = bssid; - info->nmc->allowed_fields[3].value = mode == NM_802_11_MODE_ADHOC ? _("Ad-Hoc") - : mode == NM_802_11_MODE_INFRA ? _("Infrastructure") - : _("Unknown"); - info->nmc->allowed_fields[4].value = freq_str; - info->nmc->allowed_fields[5].value = bitrate_str; - info->nmc->allowed_fields[6].value = strength_str; - info->nmc->allowed_fields[7].value = security_str->str; - info->nmc->allowed_fields[8].value = wpa_flags_str; - info->nmc->allowed_fields[9].value = rsn_flags_str; - info->nmc->allowed_fields[10].value = info->device; - info->nmc->allowed_fields[11].value = active ? _("yes") : _("no"); - info->nmc->allowed_fields[12].value = nm_object_get_path (NM_OBJECT (ap)); - - info->nmc->print_fields.flags &= ~NMC_PF_FLAG_MAIN_HEADER_ADD & ~NMC_PF_FLAG_MAIN_HEADER_ONLY & ~NMC_PF_FLAG_FIELD_NAMES; /* Clear header flags */ - print_fields (info->nmc->print_fields, info->nmc->allowed_fields); - - g_free (ap_name); - g_free (ssid_str); - g_free (freq_str); - g_free (bitrate_str); - g_free (strength_str); - g_free (wpa_flags_str); - g_free (rsn_flags_str); - g_string_free (security_str, TRUE); + set_val_str (arr, 0, ap_name); + set_val_str (arr, 1, ssid_str); + set_val_str (arr, 2, ssid_hex_str); + set_val_strc (arr, 3, bssid); + set_val_strc (arr, 4, mode == NM_802_11_MODE_ADHOC ? _("Ad-Hoc") + : mode == NM_802_11_MODE_INFRA ? _("Infra") + : _("N/A")); + set_val_str (arr, 5, channel_str); + set_val_str (arr, 6, freq_str); + set_val_str (arr, 7, bitrate_str); + set_val_str (arr, 8, strength_str); + set_val_strc (arr, 9, sig_bars); + set_val_str (arr, 10, security_str->str); + set_val_str (arr, 11, wpa_flags_str); + set_val_str (arr, 12, rsn_flags_str); + set_val_strc (arr, 13, info->device); + set_val_strc (arr, 14, active ? _("yes") : _("no")); + set_val_strc (arr, 15, active ? "*" : " "); + set_val_strc (arr, 16, nm_object_get_path (NM_OBJECT (ap))); + + g_ptr_array_add (info->nmc->output_data, arr); + + g_string_free (security_str, FALSE); } #if WITH_WIMAX static void -detail_wimax_nsp (NMWimaxNsp *nsp, NmCli *nmc, NMDevice *dev, int idx) +fill_output_wimax_nsp (NMWimaxNsp *nsp, NmCli *nmc, NMDevice *dev, int idx, guint32 o_flags) { NMDeviceWimax *wimax = NM_DEVICE_WIMAX (dev); char *nsp_name, *quality_str; const char *ntype; gboolean active = FALSE; + NmcOutputField *arr; switch (nm_wimax_nsp_get_network_type (nsp)) { case NM_WIMAX_NSP_NETWORK_TYPE_HOME: @@ -503,32 +648,64 @@ detail_wimax_nsp (NMWimaxNsp *nsp, NmCli *nmc, NMDevice *dev, int idx) quality_str = g_strdup_printf ("%u", nm_wimax_nsp_get_signal_quality (nsp)); nsp_name = g_strdup_printf ("NSP[%d]", idx); /* NSP */ - nmc->allowed_fields[0].value = nsp_name; - nmc->allowed_fields[1].value = nm_wimax_nsp_get_name (nsp); - nmc->allowed_fields[2].value = quality_str; - nmc->allowed_fields[3].value = ntype; - nmc->allowed_fields[4].value = nm_device_get_iface (dev); - nmc->allowed_fields[5].value = active ? _("yes") : _("no"); - nmc->allowed_fields[6].value = nm_object_get_path (NM_OBJECT (nsp)); + arr = nmc_dup_fields_array (nmc_fields_dev_wimax_list, + sizeof (nmc_fields_dev_wimax_list), + o_flags); + set_val_str (arr, 0, nsp_name); + set_val_strc (arr, 1, nm_wimax_nsp_get_name (nsp)); + set_val_str (arr, 2, quality_str); + set_val_strc (arr, 3, ntype); + set_val_strc (arr, 4, nm_device_get_iface (dev)); + set_val_strc (arr, 5, active ? _("yes") : _("no")); + set_val_strc (arr, 6, nm_object_get_path (NM_OBJECT (nsp))); + + g_ptr_array_add (nmc->output_data, arr); +} +#endif + +static const char * +construct_header_name (const char *base, const char *spec) +{ + static char header_name[128]; - nmc->print_fields.flags &= ~NMC_PF_FLAG_MAIN_HEADER_ADD & ~NMC_PF_FLAG_MAIN_HEADER_ONLY & ~NMC_PF_FLAG_FIELD_NAMES; /* Clear header flags */ - print_fields (nmc->print_fields, nmc->allowed_fields); + if (spec == NULL) + return base; - g_free (nsp_name); - g_free (quality_str); + g_strlcpy (header_name, base, sizeof (header_name)); + g_strlcat (header_name, " (", sizeof (header_name)); + g_strlcat (header_name, spec, sizeof (header_name)); + g_strlcat (header_name, ")", sizeof (header_name)); + + return header_name; } -#endif -struct cb_info { - NMClient *client; - const GPtrArray *active; -}; +static const char * +get_active_connection_id (NMDevice *device) +{ + const GPtrArray *avail_cons; + NMActiveConnection *ac; + const char *ac_uuid; + int i; -static void -show_device_info (gpointer data, gpointer user_data) + ac = nm_device_get_active_connection (device); + if (!ac) + return NULL; + ac_uuid = nm_active_connection_get_uuid (ac); + + avail_cons = nm_device_get_available_connections (device); + for (i = 0; avail_cons && (i < avail_cons->len); i++) { + NMRemoteConnection *candidate = g_ptr_array_index (avail_cons, i); + const char *test_uuid = nm_connection_get_uuid (NM_CONNECTION (candidate)); + + if (g_strcmp0 (ac_uuid, test_uuid) == 0) + return nm_connection_get_id (NM_CONNECTION (candidate)); + } + return NULL; +} + +static gboolean +show_device_info (NMDevice *device, NmCli *nmc) { - NMDevice *device = NM_DEVICE (data); - NmCli *nmc = (NmCli *) user_data; GError *error = NULL; APInfo *info; const char *hwaddr = NULL; @@ -537,18 +714,21 @@ show_device_info (gpointer data, gpointer user_data) NMDeviceCapabilities caps; NMActiveConnection *acon; guint32 speed; - char *speed_str = NULL; - char *state_str = NULL; - char *reason_str = NULL; + char *speed_str, *state_str, *reason_str, *mtu_str; GArray *sections_array; int k; char *fields_str; - char *fields_all = NMC_FIELDS_DEV_LIST_SECTIONS_ALL; - char *fields_common = NMC_FIELDS_DEV_LIST_SECTIONS_COMMON; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + char *fields_all = NMC_FIELDS_DEV_SHOW_SECTIONS_ALL; + char *fields_common = NMC_FIELDS_DEV_SHOW_SECTIONS_COMMON; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; gboolean was_output = FALSE; + NMIP4Config *cfg4; + NMIP6Config *cfg6; + NMDHCP4Config *dhcp4; + NMDHCP6Config *dhcp6; + const char *base_hdr = _("Device details"); + GPtrArray *fields_in_section = NULL; if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) fields_str = fields_common; @@ -557,94 +737,87 @@ show_device_info (gpointer data, gpointer user_data) else fields_str = nmc->required_fields; - sections_array = parse_output_fields (fields_str, nmc_fields_dev_list_sections, &error); + sections_array = parse_output_fields (fields_str, nmc_fields_dev_show_sections, TRUE, &fields_in_section, &error); if (error) { - if (error->code == 0) - g_string_printf (nmc->return_text, _("Error: 'dev list': %s"), error->message); - else - g_string_printf (nmc->return_text, _("Error: 'dev list': %s; allowed fields: %s"), error->message, NMC_FIELDS_DEV_LIST_SECTIONS_ALL); + g_string_printf (nmc->return_text, _("Error: 'device show': %s"), error->message); g_error_free (error); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return; + return FALSE; } /* Main header */ - nmc->allowed_fields = nmc_fields_dev_list_general; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ONLY; - nmc->print_fields.header_name = _("Device details"); - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_DEV_LIST_GENERAL_ALL, nmc->allowed_fields, NULL); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_device_get_iface (device)); + nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_DEV_SHOW_GENERAL_ALL, + nmc_fields_dev_show_general, FALSE, NULL, NULL); + + nmc_fields_dev_show_general[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY; + print_required_fields (nmc, nmc_fields_dev_show_general); /* Loop through the required sections and print them. */ for (k = 0; k < sections_array->len; k++) { int section_idx = g_array_index (sections_array, int, k); + char *section_fld = (char *) g_ptr_array_index (fields_in_section, k); if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output) - printf ("\n"); /* Empty line */ + printf ("\n"); /* Print empty line between groups in tabular mode */ was_output = FALSE; + /* Remove any previous data */ + nmc_empty_output_fields (nmc); + state = nm_device_get_state_reason (device, &reason); /* section GENERAL */ - if (!strcasecmp (nmc_fields_dev_list_sections[section_idx].name, nmc_fields_dev_list_sections[0].name)) { - nmc->allowed_fields = nmc_fields_dev_list_general; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_DEV_LIST_GENERAL_ALL, nmc->allowed_fields, NULL); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ - - if (NM_IS_DEVICE_ETHERNET (device)) - hwaddr = nm_device_ethernet_get_hw_address (NM_DEVICE_ETHERNET (device)); - else if (NM_IS_DEVICE_WIFI (device)) - hwaddr = nm_device_wifi_get_hw_address (NM_DEVICE_WIFI (device)); -#if WITH_WIMAX - else if (NM_IS_DEVICE_WIMAX (device)) - hwaddr = nm_device_wimax_get_hw_address (NM_DEVICE_WIMAX (device)); -#endif - else if (NM_IS_DEVICE_INFINIBAND (device)) - hwaddr = nm_device_infiniband_get_hw_address (NM_DEVICE_INFINIBAND (device)); - else if (NM_IS_DEVICE_BOND (device)) - hwaddr = nm_device_bond_get_hw_address (NM_DEVICE_BOND (device)); - else if (NM_IS_DEVICE_VLAN (device)) - hwaddr = nm_device_vlan_get_hw_address (NM_DEVICE_VLAN (device)); - else if (NM_IS_DEVICE_BRIDGE (device)) - hwaddr = nm_device_bridge_get_hw_address (NM_DEVICE_BRIDGE (device)); + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[0].name)) { + tmpl = nmc_fields_dev_show_general; + tmpl_len = sizeof (nmc_fields_dev_show_general); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_SHOW_GENERAL_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); state_str = g_strdup_printf ("%d (%s)", state, nmc_device_state_to_string (state)); reason_str = g_strdup_printf ("%d (%s)", reason, nmc_device_reason_to_string (reason)); - - nmc->allowed_fields[0].value = nmc_fields_dev_list_sections[0].name; /* "GENERAL"*/ - nmc->allowed_fields[1].value = nm_device_get_iface (device); - nmc->allowed_fields[2].value = device_type_to_string (device); - nmc->allowed_fields[3].value = nm_device_get_vendor (device); - nmc->allowed_fields[4].value = nm_device_get_product (device); - nmc->allowed_fields[5].value = nm_device_get_driver (device) ? nm_device_get_driver (device) : _("(unknown)"); - nmc->allowed_fields[6].value = nm_device_get_driver_version (device); - nmc->allowed_fields[7].value = nm_device_get_firmware_version (device); - nmc->allowed_fields[8].value = hwaddr ? hwaddr : _("(unknown)"); - nmc->allowed_fields[9].value = state_str; - nmc->allowed_fields[10].value = reason_str; - nmc->allowed_fields[11].value = nm_device_get_udi (device); - nmc->allowed_fields[12].value = nm_device_get_ip_iface (device); - nmc->allowed_fields[13].value = nm_device_get_managed (device) ? _("yes") : _("no"); - nmc->allowed_fields[14].value = nm_device_get_autoconnect (device) ? _("yes") : _("no"); - nmc->allowed_fields[15].value = nm_device_get_firmware_missing (device) ? _("yes") : _("no"); - nmc->allowed_fields[16].value = (acon = nm_device_get_active_connection (device)) ? - nm_object_get_path (NM_OBJECT (acon)) : _("not connected"); - - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ - g_free (state_str); - g_free (reason_str); + hwaddr = nm_device_get_hw_address (device); + mtu_str = g_strdup_printf ("%u", nm_device_get_mtu (device)); + acon = nm_device_get_active_connection (device); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_dev_show_sections[0].name); /* "GENERAL"*/ + set_val_strc (arr, 1, nm_device_get_iface (device)); + set_val_strc (arr, 2, nm_device_get_type_description (device)); + set_val_strc (arr, 3, nm_device_get_vendor (device)); + set_val_strc (arr, 4, nm_device_get_product (device)); + set_val_strc (arr, 5, nm_device_get_driver (device) ? nm_device_get_driver (device) : _("(unknown)")); + set_val_strc (arr, 6, nm_device_get_driver_version (device)); + set_val_strc (arr, 7, nm_device_get_firmware_version (device)); + set_val_strc (arr, 8, hwaddr ? hwaddr : _("(unknown)")); + set_val_str (arr, 9, mtu_str); + set_val_str (arr, 10, state_str); + set_val_str (arr, 11, reason_str); + set_val_strc (arr, 12, nm_device_get_udi (device)); + set_val_strc (arr, 13, nm_device_get_ip_iface (device)); + set_val_strc (arr, 14, nm_device_get_managed (device) ? _("yes") : _("no")); + set_val_strc (arr, 15, nm_device_get_autoconnect (device) ? _("yes") : _("no")); + set_val_strc (arr, 16, nm_device_get_firmware_missing (device) ? _("yes") : _("no")); + set_val_strc (arr, 17, get_active_connection_id (device)); + set_val_strc (arr, 18, acon ? nm_active_connection_get_uuid (acon) : NULL); + set_val_strc (arr, 19, acon ? nm_object_get_path (NM_OBJECT (acon)) : NULL); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ was_output = TRUE; } /* section CAPABILITIES */ - if (!strcasecmp (nmc_fields_dev_list_sections[section_idx].name, nmc_fields_dev_list_sections[1].name)) { - nmc->allowed_fields = nmc_fields_dev_list_cap; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_DEV_LIST_CAP_ALL, nmc->allowed_fields, NULL); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[1].name)) { + tmpl = nmc_fields_dev_show_cap; + tmpl_len = sizeof (nmc_fields_dev_show_cap); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_SHOW_CAP_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); caps = nm_device_get_capabilities (device); speed = 0; @@ -656,20 +829,18 @@ show_device_info (gpointer data, gpointer user_data) speed = nm_device_wifi_get_bitrate (NM_DEVICE_WIFI (device)); speed /= 1000; } - if (speed) - speed_str = g_strdup_printf (_("%u Mb/s"), speed); + speed_str = speed ? g_strdup_printf (_("%u Mb/s"), speed) : g_strdup (_("unknown")); - nmc->allowed_fields[0].value = nmc_fields_dev_list_sections[1].name; /* "CAPABILITIES" */ - nmc->allowed_fields[1].value = (caps & NM_DEVICE_CAP_CARRIER_DETECT) ? _("yes") : _("no"); - nmc->allowed_fields[2].value = speed_str ? speed_str : _("unknown"); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_dev_show_sections[1].name); /* "CAPABILITIES" */ + set_val_strc (arr, 1, (caps & NM_DEVICE_CAP_CARRIER_DETECT) ? _("yes") : _("no")); + set_val_str (arr, 2, speed_str); + g_ptr_array_add (nmc->output_data, arr); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ - g_free (speed_str); + print_data (nmc); /* Print all data */ was_output = TRUE; } - /* Wireless specific information */ if ((NM_IS_DEVICE_WIFI (device))) { NMDeviceWifiCapabilities wcaps; @@ -678,167 +849,177 @@ show_device_info (gpointer data, gpointer user_data) const GPtrArray *aps; /* section WIFI-PROPERTIES */ - if (!strcasecmp (nmc_fields_dev_list_sections[section_idx].name, nmc_fields_dev_list_sections[2].name)) { + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[2].name)) { wcaps = nm_device_wifi_get_capabilities (NM_DEVICE_WIFI (device)); - nmc->allowed_fields = nmc_fields_dev_list_wifi_prop; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_DEV_LIST_WIFI_PROP_ALL, nmc->allowed_fields, NULL); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ - - nmc->allowed_fields[0].value = nmc_fields_dev_list_sections[2].name; /* "WIFI-PROPERTIES" */ - nmc->allowed_fields[1].value = (wcaps & (NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104)) ? _("yes") : _("no"); - nmc->allowed_fields[2].value = (wcaps & NM_WIFI_DEVICE_CAP_WPA) ? _("yes") : _("no"); - nmc->allowed_fields[3].value = (wcaps & NM_WIFI_DEVICE_CAP_RSN) ? _("yes") : _("no"); - nmc->allowed_fields[4].value = (wcaps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP) ? _("yes") : _("no"); - nmc->allowed_fields[5].value = (wcaps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP) ? _("yes") : _("no"); - nmc->allowed_fields[6].value = (wcaps & NM_WIFI_DEVICE_CAP_AP) ? _("yes") : _("no"); - nmc->allowed_fields[7].value = (wcaps & NM_WIFI_DEVICE_CAP_ADHOC) ? _("yes") : _("no"); - - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + tmpl = nmc_fields_dev_show_wifi_prop; + tmpl_len = sizeof (nmc_fields_dev_show_wifi_prop); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_SHOW_WIFI_PROP_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_dev_show_sections[2].name); /* "WIFI-PROPERTIES" */ + set_val_strc (arr, 1, (wcaps & (NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104)) ? + _("yes") : _("no")); + set_val_strc (arr, 2, (wcaps & NM_WIFI_DEVICE_CAP_WPA) ? _("yes") : _("no")); + set_val_strc (arr, 3, (wcaps & NM_WIFI_DEVICE_CAP_RSN) ? _("yes") : _("no")); + set_val_strc (arr, 4, (wcaps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP) ? _("yes") : _("no")); + set_val_strc (arr, 5, (wcaps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP) ? _("yes") : _("no")); + set_val_strc (arr, 6, (wcaps & NM_WIFI_DEVICE_CAP_AP) ? _("yes") : _("no")); + set_val_strc (arr, 7, (wcaps & NM_WIFI_DEVICE_CAP_ADHOC) ? _("yes") : _("no")); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ was_output = TRUE; } /* section AP */ - if (!strcasecmp (nmc_fields_dev_list_sections[section_idx].name, nmc_fields_dev_list_sections[3].name)) { + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[3].name)) { if (state == NM_DEVICE_STATE_ACTIVATED) { active_ap = nm_device_wifi_get_active_access_point (NM_DEVICE_WIFI (device)); active_bssid = active_ap ? nm_access_point_get_bssid (active_ap) : NULL; } - nmc->allowed_fields = nmc_fields_dev_wifi_list; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_DEV_WIFI_LIST_FOR_DEV_LIST, nmc->allowed_fields, NULL); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ + tmpl = nmc_fields_dev_wifi_list; + tmpl_len = sizeof (nmc_fields_dev_wifi_list); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_WIFI_LIST_FOR_DEV_LIST, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; info = g_malloc0 (sizeof (APInfo)); info->nmc = nmc; info->index = 1; + info->output_flags = NMC_OF_FLAG_SECTION_PREFIX; info->active_bssid = active_bssid; info->device = nm_device_get_iface (device); aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (device)); if (aps && aps->len) - g_ptr_array_foreach ((GPtrArray *) aps, detail_access_point, (gpointer) info); + g_ptr_array_foreach ((GPtrArray *) aps, fill_output_access_point, (gpointer) info); g_free (info); + print_data (nmc); /* Print all data */ was_output = TRUE; } } else if (NM_IS_DEVICE_ETHERNET (device)) { /* WIRED-PROPERTIES */ - if (!strcasecmp (nmc_fields_dev_list_sections[section_idx].name, nmc_fields_dev_list_sections[4].name)) { - nmc->allowed_fields = nmc_fields_dev_list_wired_prop; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_DEV_LIST_WIRED_PROP_ALL, nmc->allowed_fields, NULL); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ - - nmc->allowed_fields[0].value = nmc_fields_dev_list_sections[4].name; /* "WIRED-PROPERTIES" */ - nmc->allowed_fields[1].value = (nm_device_ethernet_get_carrier (NM_DEVICE_ETHERNET (device))) ? _("on") : _("off"); - - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[4].name)) { + tmpl = nmc_fields_dev_show_wired_prop; + tmpl_len = sizeof (nmc_fields_dev_show_wired_prop); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_SHOW_WIRED_PROP_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_dev_show_sections[4].name); /* "WIRED-PROPERTIES" */ + set_val_strc (arr, 1, (nm_device_ethernet_get_carrier (NM_DEVICE_ETHERNET (device))) ? + _("on") : _("off")); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ was_output = TRUE; } } #if WITH_WIMAX else if (NM_IS_DEVICE_WIMAX (device)) { /* WIMAX-PROPERTIES */ - if (!strcasecmp (nmc_fields_dev_list_sections[section_idx].name, nmc_fields_dev_list_sections[5].name)) { + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[5].name)) { char *cfreq = NULL, *rssi = NULL, *cinr = NULL, *txpow = NULL; guint tmp_uint; gint tmp_int; - const char *bsid; - - nmc->allowed_fields = nmc_fields_dev_list_wimax_prop; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_DEV_LIST_WIMAX_PROP_ALL, nmc->allowed_fields, NULL); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ - nmc->allowed_fields[0].value = nmc_fields_dev_list_sections[5].name; /* "WIMAX-PROPERTIES" */ + /* Field names */ + tmpl = nmc_fields_dev_show_wimax_prop; + tmpl_len = sizeof (nmc_fields_dev_show_wimax_prop); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_SHOW_WIMAX_PROP_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); /* Center frequency */ tmp_uint = nm_device_wimax_get_center_frequency (NM_DEVICE_WIMAX (device)); if (tmp_uint) cfreq = g_strdup_printf ("%'.1f MHz", (double) tmp_uint / 1000.0); - nmc->allowed_fields[1].value = cfreq ? cfreq : ""; /* RSSI */ tmp_int = nm_device_wimax_get_rssi (NM_DEVICE_WIMAX (device)); if (tmp_int) rssi = g_strdup_printf ("%d dBm", tmp_int); - nmc->allowed_fields[2].value = rssi ? rssi : ""; /* CINR */ tmp_int = nm_device_wimax_get_cinr (NM_DEVICE_WIMAX (device)); if (tmp_int) cinr = g_strdup_printf ("%d dB", tmp_int); - nmc->allowed_fields[3].value = cinr ? cinr : ""; /* TX Power */ tmp_int = nm_device_wimax_get_tx_power (NM_DEVICE_WIMAX (device)); if (tmp_int) txpow = g_strdup_printf ("%'.2f dBm", (float) tmp_int / 2.0); - nmc->allowed_fields[4].value = txpow ? txpow : ""; - /* BSID */ - bsid = nm_device_wimax_get_bsid (NM_DEVICE_WIMAX (device)); - nmc->allowed_fields[5].value = bsid ? bsid : ""; + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_dev_show_sections[5].name); /* "WIMAX-PROPERTIES" */ + set_val_str (arr, 1, cfreq); + set_val_str (arr, 2, rssi); + set_val_str (arr, 3, cinr); + set_val_str (arr, 4, txpow); + set_val_strc (arr, 5, nm_device_wimax_get_bsid (NM_DEVICE_WIMAX (device))); + g_ptr_array_add (nmc->output_data, arr); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + print_data (nmc); /* Print all data */ was_output = TRUE; } /* section NSP */ - if (!strcasecmp (nmc_fields_dev_list_sections[section_idx].name, nmc_fields_dev_list_sections[6].name)) { + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[6].name)) { const GPtrArray *nsps; int g, idx = 1; - nmc->allowed_fields = nmc_fields_dev_wimax_list; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_DEV_WIMAX_LIST_FOR_DEV_LIST, nmc->allowed_fields, NULL); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ - - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; + tmpl = nmc_fields_dev_wimax_list; + tmpl_len = sizeof (nmc_fields_dev_wimax_list); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_WIMAX_LIST_FOR_DEV_LIST, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); nsps = nm_device_wimax_get_nsps (NM_DEVICE_WIMAX (device)); for (g = 0; nsps && g < nsps->len; g++) { NMWimaxNsp *nsp = g_ptr_array_index (nsps, g); - detail_wimax_nsp (nsp, nmc, device, idx++); + fill_output_wimax_nsp (nsp, nmc, device, idx++, NMC_OF_FLAG_SECTION_PREFIX); } + print_data (nmc); /* Print all data */ was_output = TRUE; } } #endif /* IP configuration info */ - if (state == NM_DEVICE_STATE_ACTIVATED) { - NMIP4Config *cfg4 = nm_device_get_ip4_config (device); - NMIP6Config *cfg6 = nm_device_get_ip6_config (device); - NMDHCP4Config *dhcp4 = nm_device_get_dhcp4_config (device); - NMDHCP6Config *dhcp6 = nm_device_get_dhcp6_config (device); - - /* IP4 */ - if (cfg4 && !strcasecmp (nmc_fields_dev_list_sections[section_idx].name, nmc_fields_dev_list_sections[7].name)) - was_output = print_ip4_config (cfg4, nmc, nmc_fields_dev_list_sections[7].name); - - /* DHCP4 */ - if (dhcp4 && !strcasecmp (nmc_fields_dev_list_sections[section_idx].name, nmc_fields_dev_list_sections[8].name)) - was_output = print_dhcp4_config (dhcp4, nmc, nmc_fields_dev_list_sections[8].name); - - /* IP6 */ - if (cfg6 && !strcasecmp (nmc_fields_dev_list_sections[section_idx].name, nmc_fields_dev_list_sections[9].name)) - was_output = print_ip6_config (cfg6, nmc, nmc_fields_dev_list_sections[9].name); - - /* DHCP6 */ - if (dhcp6 && !strcasecmp (nmc_fields_dev_list_sections[section_idx].name, nmc_fields_dev_list_sections[10].name)) - was_output = print_dhcp6_config (dhcp6, nmc, nmc_fields_dev_list_sections[10].name); - } + cfg4 = nm_device_get_ip4_config (device); + cfg6 = nm_device_get_ip6_config (device); + dhcp4 = nm_device_get_dhcp4_config (device); + dhcp6 = nm_device_get_dhcp6_config (device); + + /* IP4 */ + if (cfg4 && !strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[7].name)) + was_output = print_ip4_config (cfg4, nmc, nmc_fields_dev_show_sections[7].name, section_fld); + + /* DHCP4 */ + if (dhcp4 && !strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[8].name)) + was_output = print_dhcp4_config (dhcp4, nmc, nmc_fields_dev_show_sections[8].name, section_fld); + + /* IP6 */ + if (cfg6 && !strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[9].name)) + was_output = print_ip6_config (cfg6, nmc, nmc_fields_dev_show_sections[9].name, section_fld); + + /* DHCP6 */ + if (dhcp6 && !strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[10].name)) + was_output = print_dhcp6_config (dhcp6, nmc, nmc_fields_dev_show_sections[10].name, section_fld); /* Bond-specific information */ if ((NM_IS_DEVICE_BOND (device))) { - if (!strcasecmp (nmc_fields_dev_list_sections[section_idx].name, nmc_fields_dev_list_sections[11].name)) { + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[11].name)) { const GPtrArray *slaves; GString *bond_slaves_str; int idx; @@ -857,54 +1038,61 @@ show_device_info (gpointer data, gpointer user_data) if (bond_slaves_str->len > 0) g_string_truncate (bond_slaves_str, bond_slaves_str->len-1); /* Chop off last space */ - nmc->allowed_fields = nmc_fields_dev_list_bond_prop; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_DEV_LIST_BOND_PROP_ALL, nmc->allowed_fields, NULL); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ + tmpl = nmc_fields_dev_show_bond_prop; + tmpl_len = sizeof (nmc_fields_dev_show_bond_prop); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_SHOW_BOND_PROP_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); - nmc->allowed_fields[0].value = nmc_fields_dev_list_sections[11].name; /* "BOND" */ - nmc->allowed_fields[1].value = bond_slaves_str->str; + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_dev_show_sections[11].name); /* "BOND" */ + set_val_str (arr, 1, bond_slaves_str->str); + g_ptr_array_add (nmc->output_data, arr); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + print_data (nmc); /* Print all data */ - g_string_free (bond_slaves_str, TRUE); + g_string_free (bond_slaves_str, FALSE); was_output = TRUE; } } /* VLAN-specific information */ if ((NM_IS_DEVICE_VLAN (device))) { - if (!strcasecmp (nmc_fields_dev_list_sections[section_idx].name, nmc_fields_dev_list_sections[12].name)) { + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[12].name)) { char * vlan_id_str = g_strdup_printf ("%u", nm_device_vlan_get_vlan_id (NM_DEVICE_VLAN (device))); - nmc->allowed_fields = nmc_fields_dev_list_vlan_prop; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_DEV_LIST_VLAN_PROP_ALL, nmc->allowed_fields, NULL); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ + tmpl = nmc_fields_dev_show_vlan_prop; + tmpl_len = sizeof (nmc_fields_dev_show_vlan_prop); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_SHOW_VLAN_PROP_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); - nmc->allowed_fields[0].value = nmc_fields_dev_list_sections[12].name; /* "VLAN" */ - nmc->allowed_fields[1].value = vlan_id_str; + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_dev_show_sections[12].name); /* "VLAN" */ + set_val_str (arr, 1, vlan_id_str); + g_ptr_array_add (nmc->output_data, arr); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + print_data (nmc); /* Print all data */ - g_free (vlan_id_str); was_output = TRUE; } } /* section CONNECTIONS */ - if (!strcasecmp (nmc_fields_dev_list_sections[section_idx].name, nmc_fields_dev_list_sections[13].name)) { + if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[13].name)) { const GPtrArray *avail_cons; GString *ac_paths_str; char **ac_arr = NULL; int i; - nmc->allowed_fields = nmc_fields_dev_list_connections; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_DEV_LIST_CONNECTIONS_ALL, nmc->allowed_fields, NULL); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ + tmpl = nmc_fields_dev_show_connections; + tmpl_len = sizeof (nmc_fields_dev_show_connections); + nmc->print_fields.indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_SHOW_CONNECTIONS_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); /* available-connections */ avail_cons = nm_device_get_available_connections (device); @@ -930,47 +1118,59 @@ show_device_info (gpointer data, gpointer user_data) if (ac_paths_str->len > 0) g_string_append_c (ac_paths_str, '}'); - set_val_str (nmc->allowed_fields, 0, nmc_fields_dev_list_sections[13].name); /* "CONNECTIONS" */ - set_val_str (nmc->allowed_fields, 1, ac_paths_str->str); - set_val_arr (nmc->allowed_fields, 2, (const char **) ac_arr); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, nmc_fields_dev_show_sections[13].name); /* "CONNECTIONS" */ + set_val_str (arr, 1, ac_paths_str->str); + set_val_arr (arr, 2, (ac_arr)); + g_ptr_array_add (nmc->output_data, arr); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + print_data (nmc); /* Print all data */ - g_string_free (ac_paths_str, TRUE); - g_strfreev (ac_arr); + g_string_free (ac_paths_str, FALSE); was_output = TRUE; } } if (sections_array) g_array_free (sections_array, TRUE); + if (fields_in_section) + g_ptr_array_free (fields_in_section, TRUE); + + return TRUE; } static void -show_device_status (NMDevice *device, NmCli *nmc) +fill_output_device_status (NMDevice *device, NmCli *nmc) { - nmc->allowed_fields[0].value = nm_device_get_iface (device); - nmc->allowed_fields[1].value = device_type_to_string (device); - nmc->allowed_fields[2].value = nmc_device_state_to_string (nm_device_get_state (device)); - nmc->allowed_fields[3].value = nm_object_get_path (NM_OBJECT (device)); - - nmc->print_fields.flags &= ~NMC_PF_FLAG_MAIN_HEADER_ADD & ~NMC_PF_FLAG_MAIN_HEADER_ONLY & ~NMC_PF_FLAG_FIELD_NAMES; /* Clear header flags */ - print_fields (nmc->print_fields, nmc->allowed_fields); + NMActiveConnection *ac; + NmcOutputField *arr = nmc_dup_fields_array (nmc_fields_dev_status, + sizeof (nmc_fields_dev_status), + 0); + + ac = nm_device_get_active_connection (device); + + set_val_strc (arr, 0, nm_device_get_iface (device)); + set_val_strc (arr, 1, nm_device_get_type_description (device)); + set_val_strc (arr, 2, nmc_device_state_to_string (nm_device_get_state (device))); + set_val_strc (arr, 3, nm_object_get_path (NM_OBJECT (device))); + set_val_strc (arr, 4, get_active_connection_id (device)); + set_val_strc (arr, 5, ac ? nm_active_connection_get_uuid (ac) : NULL); + set_val_strc (arr, 6, ac ? nm_object_get_path (NM_OBJECT (ac)) : NULL); + + g_ptr_array_add (nmc->output_data, arr); } static NMCResultCode do_devices_status (NmCli *nmc, int argc, char **argv) { GError *error = NULL; - const GPtrArray *devices; + NMDevice **devices; int i; char *fields_str; char *fields_all = NMC_FIELDS_DEV_STATUS_ALL; char *fields_common = NMC_FIELDS_DEV_STATUS_COMMON; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; while (argc > 0) { fprintf (stderr, _("Unknown parameter: %s\n"), *argv); @@ -985,45 +1185,41 @@ do_devices_status (NmCli *nmc, int argc, char **argv) else fields_str = nmc->required_fields; - nmc->allowed_fields = nmc_fields_dev_status; - nmc->print_fields.indices = parse_output_fields (fields_str, nmc->allowed_fields, &error); + tmpl = nmc_fields_dev_status; + tmpl_len = sizeof (nmc_fields_dev_status); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &error); if (error) { - if (error->code == 0) - g_string_printf (nmc->return_text, _("Error: 'dev status': %s"), error->message); - else - g_string_printf (nmc->return_text, _("Error: 'dev status': %s; allowed fields: %s"), error->message, NMC_FIELDS_DEV_STATUS_ALL); + g_string_printf (nmc->return_text, _("Error: 'device status': %s"), error->message); g_error_free (error); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } - if (!nmc_is_nm_running (nmc, &error)) { - if (error) { - g_string_printf (nmc->return_text, _("Error: Can't find out if NetworkManager is running: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - g_error_free (error); - } else { - g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); - nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; - } + nmc->get_client (nmc); + + if (!nm_client_get_manager_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; goto error; } if (!nmc_versions_match (nmc)) goto error; - /* Print headers */ - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ADD | NMC_PF_FLAG_FIELD_NAMES; + /* Add headers */ nmc->print_fields.header_name = _("Status of devices"); - print_fields (nmc->print_fields, nmc->allowed_fields); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); - nmc->get_client (nmc); - devices = nm_client_get_devices (nmc->client); - for (i = 0; devices && (i < devices->len); i++) { - NMDevice *device = g_ptr_array_index (devices, i); - show_device_status (device, nmc); - } + devices = get_devices_sorted (nmc->client); + for (i = 0; devices[i]; i++) + fill_output_device_status (devices[i], nmc); + + /* Now print all data */ + print_data (nmc); + + g_free (devices); return NMC_RESULT_SUCCESS; @@ -1032,89 +1228,67 @@ error: } static NMCResultCode -do_devices_list (NmCli *nmc, int argc, char **argv) +do_devices_show (NmCli *nmc, int argc, char **argv) { - const GPtrArray *devices; - GError *error = NULL; + NMDevice **devices = NULL; NMDevice *device = NULL; - const char *iface = NULL; - gboolean iface_specified = FALSE; + const char *ifname = NULL; int i; + gboolean ret; - while (argc > 0) { - if (strcmp (*argv, "iface") == 0) { - iface_specified = TRUE; - - if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: '%s' argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } - - iface = *argv; - } else { - fprintf (stderr, _("Unknown parameter: %s\n"), *argv); - } - - argc--; - argv++; + if (argc == 1) + ifname = *argv; + else if (argc > 1) { + g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *(argv+1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; } - if (!nmc_is_nm_running (nmc, &error)) { - if (error) { - g_string_printf (nmc->return_text, _("Error: Can't find out if NetworkManager is running: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - g_error_free (error); - } else { - g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); - nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; - } + nmc->get_client (nmc); + + if (!nm_client_get_manager_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; goto error; } if (!nmc_versions_match (nmc)) goto error; - nmc->get_client (nmc); - devices = nm_client_get_devices (nmc->client); + devices = get_devices_sorted (nmc->client); - if (iface_specified) { - for (i = 0; devices && (i < devices->len); i++) { - NMDevice *candidate = g_ptr_array_index (devices, i); + if (ifname) { + /* Interface specified; show details only for the device */ + for (i = 0; devices[i]; i++) { + NMDevice *candidate = devices[i]; const char *dev_iface = nm_device_get_iface (candidate); - if (!g_strcmp0 (dev_iface, iface)) + if (!g_strcmp0 (dev_iface, ifname)) device = candidate; } if (!device) { - g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), iface); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; goto error; } show_device_info (device, nmc); } else { - if (devices) - g_ptr_array_foreach ((GPtrArray *) devices, show_device_info, nmc); + /* Show details for all devices */ + for (i = 0; devices[i]; i++) { + nmc_empty_output_fields (nmc); + ret = show_device_info (devices[i], nmc); + if (!ret) + break; + if (devices[i + 1]) + printf ("\n"); /* Empty line */ + } } error: + g_free (devices); return nmc->return_value; } -static void -device_state_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data) -{ - NmCli *nmc = (NmCli *) user_data; - NMDeviceState state; - - state = nm_device_get_state (device); - - if (state == NM_DEVICE_STATE_DISCONNECTED) { - g_string_printf (nmc->return_text, _("Success: Device '%s' successfully disconnected."), nm_device_get_iface (device)); - quit (); - } -} - static gboolean timeout_cb (gpointer user_data) { @@ -1139,6 +1313,165 @@ progress_cb (gpointer user_data) } static void +connected_state_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data) +{ + NMActiveConnection *active = (NMActiveConnection *) user_data; + NMDeviceState state; + + state = nm_device_get_state (device); + + if (state == NM_DEVICE_STATE_ACTIVATED) { + nmc_terminal_erase_line (); + printf (_("Device '%s' successfully activated with '%s'.\n"), + nm_device_get_iface (device), + nm_active_connection_get_uuid (active)); + quit (); + } +} + +static void +connect_device_cb (NMClient *client, NMActiveConnection *active, GError *error, gpointer user_data) +{ + NmCli *nmc = (NmCli *) user_data; + const GPtrArray *devices; + NMDevice *device; + NMDeviceState state; + + if (error) { + g_string_printf (nmc->return_text, _("Error: Device activation failed: %s"), + error->message ? error->message : _("(unknown)")); + nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; + quit (); + } else { + g_assert (active); + devices = nm_active_connection_get_devices (active); + if (!devices || devices->len == 0) { + g_string_printf (nmc->return_text, _("Error: Device activation failed: device was disconnected")); + nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; + quit (); + return; + } + + device = g_ptr_array_index (devices, 0); + state = nm_device_get_state (device); + + if (nmc->nowait_flag || state == NM_DEVICE_STATE_ACTIVATED) { + /* Don't want to wait or device already activated */ + if (state == NM_DEVICE_STATE_ACTIVATED && nmc->print_output == NMC_PRINT_PRETTY) { + nmc_terminal_erase_line (); + printf (_("Device '%s' has been connected.\n"), nm_device_get_iface (device)); + } + quit (); + } else { + g_signal_connect (device, "notify::state", G_CALLBACK (connected_state_cb), active); + /* Start timer not to loop forever if "notify::state" signal is not issued */ + g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc); + } + } +} + +static NMCResultCode +do_device_connect (NmCli *nmc, int argc, char **argv) +{ + NMDevice **devices; + NMDevice *device = NULL; + const char *ifname = NULL; + char *ifname_ask = NULL; + int i; + + /* Set default timeout for connect operation. */ + if (nmc->timeout == -1) + nmc->timeout = 90; + + if (argc == 0) { + if (nmc->ask) + ifname = ifname_ask = nmc_readline (PROMPT_INTERFACE); + + if (!ifname_ask) { + g_string_printf (nmc->return_text, _("Error: No interface specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } else { + ifname = *argv; + } + + if (!ifname) { + g_string_printf (nmc->return_text, _("Error: No interface specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + if (next_arg (&argc, &argv) == 0) { + g_string_printf (nmc->return_text, _("Error: extra argument not allowed: '%s'."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + nmc->get_client (nmc); + if (!nm_client_get_manager_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + goto error; + } + + if (!nmc_versions_match (nmc)) + goto error; + + devices = get_devices_sorted (nmc->client); + for (i = 0; devices[i]; i++) { + NMDevice *candidate = devices[i]; + const char *dev_iface = nm_device_get_iface (candidate); + + if (!g_strcmp0 (dev_iface, ifname)) + device = candidate; + } + g_free (devices); + + if (!device) { + g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + + /* + * Use nowait_flag instead of should_wait, because exiting has to be postponed + * till connect_device_cb() is called, giving NM time to check our permissions. + */ + nmc->nowait_flag = (nmc->timeout == 0); + nmc->should_wait = TRUE; + nm_client_activate_connection (nmc->client, + NULL, /* let NM find a connection automatically */ + device, + NULL, + connect_device_cb, + nmc); + + /* Start progress indication */ + if (nmc->print_output == NMC_PRINT_PRETTY) + progress_id = g_timeout_add (120, progress_cb, device); + +error: + g_free (ifname_ask); + + return nmc->return_value; +} + +static void +disconnect_state_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data) +{ + NmCli *nmc = (NmCli *) user_data; + NMDeviceState state; + + state = nm_device_get_state (device); + + if (state == NM_DEVICE_STATE_DISCONNECTED) { + g_string_printf (nmc->return_text, _("Success: Device '%s' successfully disconnected."), nm_device_get_iface (device)); + quit (); + } +} + +static void disconnect_device_cb (NMDevice *device, GError *error, gpointer user_data) { NmCli *nmc = (NmCli *) user_data; @@ -1156,13 +1489,14 @@ disconnect_device_cb (NMDevice *device, GError *error, gpointer user_data) if (nmc->nowait_flag || state == NM_DEVICE_STATE_DISCONNECTED) { /* Don't want to wait or device already disconnected */ - if (state == NM_DEVICE_STATE_DISCONNECTED && nmc->print_output == NMC_PRINT_PRETTY) { - nmc_terminal_erase_line (); + if (state == NM_DEVICE_STATE_DISCONNECTED) { + if (nmc->print_output == NMC_PRINT_PRETTY) + nmc_terminal_erase_line (); printf (_("Device '%s' has been disconnected.\n"), nm_device_get_iface (device)); } quit (); } else { - g_signal_connect (device, "notify::state", G_CALLBACK (device_state_cb), nmc); + g_signal_connect (device, "notify::state", G_CALLBACK (disconnect_state_cb), nmc); /* Start timer not to loop forever if "notify::state" signal is not issued */ g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc); } @@ -1173,93 +1507,72 @@ disconnect_device_cb (NMDevice *device, GError *error, gpointer user_data) static NMCResultCode do_device_disconnect (NmCli *nmc, int argc, char **argv) { - const GPtrArray *devices; - GError *error = NULL; + NMDevice **devices; NMDevice *device = NULL; - const char *iface = NULL; - gboolean iface_specified = FALSE; - gboolean wait = TRUE; + const char *ifname = NULL; + char *ifname_ask = NULL; int i; - /* Set default timeout for disconnect operation */ - nmc->timeout = 10; + /* Set default timeout for disconnect operation. */ + if (nmc->timeout == -1) + nmc->timeout = 10; - while (argc > 0) { - if (strcmp (*argv, "iface") == 0) { - iface_specified = TRUE; - - if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } - - iface = *argv; - } else if (strcmp (*argv, "--nowait") == 0) { - wait = FALSE; - } else if (strcmp (*argv, "--timeout") == 0) { - if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } - - errno = 0; - nmc->timeout = strtol (*argv, NULL, 10); - if (errno || nmc->timeout < 0) { - g_string_printf (nmc->return_text, _("Error: timeout value '%s' is not valid."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } + if (argc == 0) { + if (nmc->ask) + ifname = ifname_ask = nmc_readline (PROMPT_INTERFACE); - } else { - fprintf (stderr, _("Unknown parameter: %s\n"), *argv); + if (!ifname_ask) { + g_string_printf (nmc->return_text, _("Error: No interface specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; } + } else { + ifname = *argv; + } - argc--; - argv++; + if (!ifname) { + g_string_printf (nmc->return_text, _("Error: No interface specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; } - if (!iface_specified) { - g_string_printf (nmc->return_text, _("Error: iface has to be specified.")); + if (next_arg (&argc, &argv) == 0) { + g_string_printf (nmc->return_text, _("Error: extra argument not allowed: '%s'."), *argv); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } - if (!nmc_is_nm_running (nmc, &error)) { - if (error) { - g_string_printf (nmc->return_text, _("Error: Can't find out if NetworkManager is running: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - g_error_free (error); - } else { - g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); - nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; - } + nmc->get_client (nmc); + if (!nm_client_get_manager_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; goto error; } if (!nmc_versions_match (nmc)) goto error; - nmc->get_client (nmc); - devices = nm_client_get_devices (nmc->client); - for (i = 0; devices && (i < devices->len); i++) { - NMDevice *candidate = g_ptr_array_index (devices, i); + devices = get_devices_sorted (nmc->client); + for (i = 0; devices[i]; i++) { + NMDevice *candidate = devices[i]; const char *dev_iface = nm_device_get_iface (candidate); - if (!g_strcmp0 (dev_iface, iface)) + if (!g_strcmp0 (dev_iface, ifname)) device = candidate; } + g_free (devices); if (!device) { - g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), iface); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; goto error; } - /* Use nowait_flag instead of should_wait because exitting has to be postponed till disconnect_device_cb() - * is called, giving NM time to check our permissions */ - nmc->nowait_flag = !wait; + /* + * Use nowait_flag instead of should_wait, because exiting has to be postponed + * till disconnect_device_cb() is called, giving NM time to check our permissions. + */ + nmc->nowait_flag = (nmc->timeout == 0); nmc->should_wait = TRUE; nm_device_disconnect (device, disconnect_device_cb, nmc); @@ -1278,20 +1591,29 @@ show_acces_point_info (NMDevice *device, NmCli *nmc) const char *active_bssid = NULL; const GPtrArray *aps; APInfo *info; + NmcOutputField *arr; if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) { active_ap = nm_device_wifi_get_active_access_point (NM_DEVICE_WIFI (device)); active_bssid = active_ap ? nm_access_point_get_bssid (active_ap) : NULL; } + arr = nmc_dup_fields_array (nmc_fields_dev_wifi_list, sizeof (nmc_fields_dev_wifi_list), + NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + info = g_malloc0 (sizeof (APInfo)); info->nmc = nmc; info->index = 1; + info->output_flags = 0; info->active_bssid = active_bssid; info->device = nm_device_get_iface (device); aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (device)); if (aps && aps->len) - g_ptr_array_foreach ((GPtrArray *) aps, detail_access_point, (gpointer) info); + g_ptr_array_foreach ((GPtrArray *) aps, fill_output_access_point, (gpointer) info); + + print_data (nmc); /* Print all data */ + nmc_empty_output_fields (nmc); g_free (info); } @@ -1301,27 +1623,27 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) GError *error = NULL; NMDevice *device = NULL; NMAccessPoint *ap = NULL; - const char *iface = NULL; + const char *ifname = NULL; const char *bssid_user = NULL; - const GPtrArray *devices; + NMDevice **devices = NULL; const GPtrArray *aps; APInfo *info; int i, j; char *fields_str; char *fields_all = NMC_FIELDS_DEV_WIFI_LIST_ALL; char *fields_common = NMC_FIELDS_DEV_WIFI_LIST_COMMON; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + const char *base_hdr = _("Wi-Fi scan list"); while (argc > 0) { - if (strcmp (*argv, "iface") == 0) { + if (strcmp (*argv, "ifname") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } - iface = *argv; + ifname = *argv; } else if (strcmp (*argv, "bssid") == 0 || strcmp (*argv, "hwaddr") == 0) { /* hwaddr is deprecated and will be removed later */ if (next_arg (&argc, &argv) != 0) { @@ -1338,7 +1660,6 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) argv++; } - if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) fields_str = fields_common; else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) @@ -1346,58 +1667,49 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) else fields_str = nmc->required_fields; - nmc->allowed_fields = nmc_fields_dev_wifi_list; - nmc->print_fields.indices = parse_output_fields (fields_str, nmc->allowed_fields, &error); + tmpl = nmc_fields_dev_wifi_list; + tmpl_len = sizeof (nmc_fields_dev_wifi_list); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &error); if (error) { - if (error->code == 0) - g_string_printf (nmc->return_text, _("Error: 'dev wifi': %s"), error->message); - else - g_string_printf (nmc->return_text, _("Error: 'dev wifi': %s; allowed fields: %s"), error->message, NMC_FIELDS_DEV_WIFI_LIST_ALL); + g_string_printf (nmc->return_text, _("Error: 'device wifi': %s"), error->message); g_error_free (error); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } - if (!nmc_is_nm_running (nmc, &error)) { - if (error) { - g_string_printf (nmc->return_text, _("Error: Can't find out if NetworkManager is running: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - g_error_free (error); - } else { - g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); - nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; - } + nmc->get_client (nmc); + + if (!nm_client_get_manager_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; goto error; } if (!nmc_versions_match (nmc)) goto error; - /* Print headers */ - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ADD | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.header_name = _("WiFi scan list"); - - nmc->get_client (nmc); - devices = nm_client_get_devices (nmc->client); - if (iface) { + devices = get_devices_sorted (nmc->client); + if (ifname) { /* Device specified - list only APs of this interface */ - for (i = 0; devices && (i < devices->len); i++) { - NMDevice *candidate = g_ptr_array_index (devices, i); + for (i = 0; devices[i]; i++) { + NMDevice *candidate = devices[i]; const char *dev_iface = nm_device_get_iface (candidate); - if (!g_strcmp0 (dev_iface, iface)) { + if (!g_strcmp0 (dev_iface, ifname)) { device = candidate; break; } } - if (!device) { - g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), iface); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; goto error; } + /* Main header name */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, ifname); + if (NM_IS_DEVICE_WIFI (device)) { if (bssid_user) { /* Specific AP requested - list only that */ @@ -1413,38 +1725,50 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) g_free (bssid_up); } if (!ap) { - g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."), bssid_user); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."), + bssid_user); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; goto error; } + /* Add headers (field names) */ + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + info = g_malloc0 (sizeof (APInfo)); info->nmc = nmc; info->index = 1; + info->output_flags = 0; info->active_bssid = NULL; info->device = nm_device_get_iface (device); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ - detail_access_point (ap, info); + + fill_output_access_point (ap, info); + + print_data (nmc); /* Print all data */ g_free (info); } else { - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ show_acces_point_info (device, nmc); } } else { - g_string_printf (nmc->return_text, _("Error: Device '%s' is not a WiFi device."), iface); + g_string_printf (nmc->return_text, _("Error: Device '%s' is not a Wi-Fi device."), ifname); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; goto error; } } else { /* List APs for all devices */ - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ if (bssid_user) { /* Specific AP requested - list only that */ - for (i = 0; devices && (i < devices->len); i++) { - NMDevice *dev = g_ptr_array_index (devices, i); + for (i = 0; devices[i]; i++) { + NMDevice *dev = devices[i]; if (!NM_IS_DEVICE_WIFI (dev)) continue; + /* Main header name */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_device_get_iface (dev)); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (dev)); for (j = 0; aps && (j < aps->len); j++) { char *bssid_up; @@ -1458,22 +1782,30 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) info = g_malloc0 (sizeof (APInfo)); info->nmc = nmc; info->index = 1; + info->output_flags = 0; info->active_bssid = NULL; info->device = nm_device_get_iface (dev); - detail_access_point (ap, info); + fill_output_access_point (ap, info); g_free (info); } g_free (bssid_up); } + print_data (nmc); /* Print all data */ + nmc_empty_output_fields (nmc); } if (!ap) { - g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."), bssid_user); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."), + bssid_user); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; goto error; } } else { - for (i = 0; devices && (i < devices->len); i++) { - NMDevice *dev = g_ptr_array_index (devices, i); + for (i = 0; devices[i]; i++) { + NMDevice *dev = devices[i]; + + /* Main header name */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, + nm_device_get_iface (dev)); if (NM_IS_DEVICE_WIFI (dev)) show_acces_point_info (dev, nmc); } @@ -1481,6 +1813,7 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) } error: + g_free (devices); return nmc->return_value; } @@ -1494,13 +1827,12 @@ monitor_device_state_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data state = nm_device_get_state_reason (device, &reason); if (state == NM_DEVICE_STATE_ACTIVATED) { - if (nmc->print_output == NMC_PRINT_PRETTY) { - NMActiveConnection *active = nm_device_get_active_connection (device); + NMActiveConnection *active = nm_device_get_active_connection (device); + if (nmc->print_output == NMC_PRINT_PRETTY) nmc_terminal_erase_line (); - printf (_("Connection with UUID '%s' created and activated on device '%s'\n"), - nm_active_connection_get_uuid (active), nm_device_get_iface (device)); - } + printf (_("Connection with UUID '%s' created and activated on device '%s'\n"), + nm_active_connection_get_uuid (active), nm_device_get_iface (device)); quit (); } else if (state == NM_DEVICE_STATE_FAILED) { g_string_printf (nmc->return_text, _("Error: Connection activation failed: (%d) %s."), @@ -1543,7 +1875,9 @@ add_and_activate_cb (NMClient *client, if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { /* User doesn't want to wait or already activated */ - if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED && nmc->print_output == NMC_PRINT_PRETTY) { + if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + if (nmc->print_output == NMC_PRINT_PRETTY) + nmc_terminal_erase_line (); printf (_("Connection with UUID '%s' created and activated on device '%s'\n"), nm_active_connection_get_uuid (active), nm_device_get_iface (device)); } @@ -1617,16 +1951,20 @@ find_ap_on_device (NMDevice *device, GByteArray *bssid, const char *ssid) if (ssid) { /* Parameter is SSID */ - const GByteArray *candidate_ssid = nm_access_point_get_ssid (candidate_ap); - char *ssid_tmp = nm_utils_ssid_to_utf8 (candidate_ssid); + const GByteArray *candidate_ssid; - /* Compare SSIDs */ - if (strcmp (ssid, ssid_tmp) == 0) { - ap = candidate_ap; + candidate_ssid = nm_access_point_get_ssid (candidate_ap); + if (candidate_ssid) { + char *ssid_tmp = nm_utils_ssid_to_utf8 (candidate_ssid); + + /* Compare SSIDs */ + if (strcmp (ssid, ssid_tmp) == 0) { + ap = candidate_ap; + g_free (ssid_tmp); + break; + } g_free (ssid_tmp); - break; } - g_free (ssid_tmp); } else if (bssid) { /* Parameter is BSSID */ const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap); @@ -1650,27 +1988,31 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) { NMDevice *device = NULL; NMAccessPoint *ap = NULL; + NM80211ApFlags ap_flags; + NM80211ApSecurityFlags ap_wpa_flags; + NM80211ApSecurityFlags ap_rsn_flags; NMConnection *connection = NULL; NMSettingConnection *s_con; NMSettingWireless *s_wifi; NMSettingWirelessSecurity *s_wsec; AddAndActivateInfo *info; const char *param_user = NULL; - const char *iface = NULL; + const char *ifname = NULL; const char *bssid = NULL; const char *password = NULL; const char *con_name = NULL; gboolean private = FALSE; - gboolean wait = TRUE; gboolean wep_passphrase = FALSE; GByteArray *bssid1_arr = NULL; GByteArray *bssid2_arr = NULL; const GPtrArray *devices; int devices_idx; - GError *error = NULL; + char *ssid_ask = NULL; + char *passwd_ask = NULL; - /* Default timeout for waiting for operation completion */ - nmc->timeout = 90; + /* Set default timeout waiting for operation completion. */ + if (nmc->timeout == -1) + nmc->timeout = 90; /* Get the first compulsory argument (SSID or BSSID) */ if (argc > 0) { @@ -1680,20 +2022,27 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) argc--; argv++; } else { - g_string_printf (nmc->return_text, _("Error: SSID or BSSID are missing.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + if (nmc->ask) { + ssid_ask = nmc_readline (_("SSID or BSSID: ")); + param_user = ssid_ask ? ssid_ask : ""; + bssid1_arr = nm_utils_hwaddr_atoba (param_user, ARPHRD_ETHER); + } + if (!ssid_ask) { + g_string_printf (nmc->return_text, _("Error: SSID or BSSID are missing.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } } /* Get the rest of the parameters */ while (argc > 0) { - if (strcmp (*argv, "iface") == 0) { + if (strcmp (*argv, "ifname") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } - iface = *argv; + ifname = *argv; } else if (strcmp (*argv, "bssid") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); @@ -1739,22 +2088,17 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) goto error; } con_name = *argv; - } else if (strcmp (*argv, "--private") == 0) { - private = TRUE; - } else if (strcmp (*argv, "--nowait") == 0) { - wait = FALSE; - } else if (strcmp (*argv, "--timeout") == 0) { + } else if (strcmp (*argv, "private") == 0) { + GError *err_tmp = NULL; if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } - - errno = 0; - nmc->timeout = strtol (*argv, NULL, 10); - if (errno || nmc->timeout < 0) { - g_string_printf (nmc->return_text, _("Error: timeout value '%s' is not valid."), *argv); + if (!nmc_string_to_bool (*argv, &private, &err_tmp)) { + g_string_printf (nmc->return_text, _("Error: %s: %s."), *(argv-1), err_tmp->message); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_clear_error (&err_tmp); goto error; } } else { @@ -1778,31 +2122,26 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) goto error; } - if (!nmc_is_nm_running (nmc, &error)) { - if (error) { - g_string_printf (nmc->return_text, _("Error: Can't find out if NetworkManager is running: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - g_error_free (error); - } else { - g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); - nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; - } + nmc->get_client (nmc); + + if (!nm_client_get_manager_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; goto error; } if (!nmc_versions_match (nmc)) goto error; - nmc->get_client (nmc); devices = nm_client_get_devices (nmc->client); /* Find a device to activate the connection on */ devices_idx = 0; - device = find_wifi_device_by_iface (devices, iface, &devices_idx); + device = find_wifi_device_by_iface (devices, ifname, &devices_idx); if (!device) { - if (iface) - g_string_printf (nmc->return_text, _("Error: Device '%s' is not a Wi-Fi device."), iface); + if (ifname) + g_string_printf (nmc->return_text, _("Error: Device '%s' is not a Wi-Fi device."), ifname); else g_string_printf (nmc->return_text, _("Error: No Wi-Fi device found.")); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; @@ -1811,8 +2150,8 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) /* Find an AP to connect to */ ap = find_ap_on_device (device, bssid1_arr, bssid1_arr ? NULL : param_user); - if (!ap && !iface) { - /* AP not found. iface was not specified, so try finding the AP on another device. */ + if (!ap && !ifname) { + /* AP not found. ifname was not specified, so try finding the AP on another device. */ while ((device = find_wifi_device_by_iface (devices, NULL, &devices_idx)) != NULL) { ap = find_ap_on_device (device, bssid1_arr, bssid1_arr ? NULL : param_user); if (ap) @@ -1825,7 +2164,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) g_string_printf (nmc->return_text, _("Error: No network with SSID '%s' found."), param_user); else g_string_printf (nmc->return_text, _("Error: No access point with BSSID '%s' found."), param_user); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; goto error; } @@ -1853,42 +2192,50 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) /* 'bssid' parameter is used to restrict the conenction only to the BSSID */ g_object_set (s_wifi, NM_SETTING_WIRELESS_BSSID, bssid2_arr, NULL); } - if (password) { - NM80211ApFlags flags = nm_access_point_get_flags (ap); - NM80211ApSecurityFlags wpa_flags = nm_access_point_get_wpa_flags (ap); - NM80211ApSecurityFlags rsn_flags = nm_access_point_get_rsn_flags (ap); - s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + /* handle password */ + ap_flags = nm_access_point_get_flags (ap); + ap_wpa_flags = nm_access_point_get_wpa_flags (ap); + ap_rsn_flags = nm_access_point_get_rsn_flags (ap); + + /* Set password for WEP or WPA-PSK. */ + if (ap_flags & NM_802_11_AP_FLAGS_PRIVACY) { + /* Ask for missing password when one is expected and '--ask' is used */ + if (!password && nmc->ask) + password = passwd_ask = nmc_readline (_("Password: ")); + + if (password) { + if (!connection) + connection = nm_connection_new (); + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); - /* Set password for WEP or WPA-PSK. */ - if (flags & NM_802_11_AP_FLAGS_PRIVACY) { - if (wpa_flags == NM_802_11_AP_SEC_NONE && rsn_flags == NM_802_11_AP_SEC_NONE) { + if (ap_wpa_flags == NM_802_11_AP_SEC_NONE && ap_rsn_flags == NM_802_11_AP_SEC_NONE) { /* WEP */ nm_setting_wireless_security_set_wep_key (s_wsec, 0, password); g_object_set (G_OBJECT (s_wsec), NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, wep_passphrase ? NM_WEP_KEY_TYPE_PASSPHRASE: NM_WEP_KEY_TYPE_KEY, NULL); - } else if ( !(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) - && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { + } else if ( !(ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) + && !(ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { /* WPA PSK */ g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_PSK, password, NULL); } } - // FIXME: WPA-Enterprise is not supported yet. - // We are not able to determine and fill all the parameters for - // 802.1X authentication automatically without user providing - // the data. Adding nmcli options for the 8021x setting would - // clutter the command. However, that could be solved later by - // implementing add/edit connections support for nmcli. } + // FIXME: WPA-Enterprise is not supported yet. + // We are not able to determine and fill all the parameters for + // 802.1X authentication automatically without user providing + // the data. Adding nmcli options for the 8021x setting would + // clutter the command. However, that could be solved later by + // implementing add/edit connections support for nmcli. /* nowait_flag indicates user input. should_wait says whether quit in start(). * We have to delay exit after add_and_activate_cb() is called, even if * the user doesn't want to wait, in order to give NM time to check our * permissions. */ - nmc->nowait_flag = !wait; + nmc->nowait_flag = (nmc->timeout == 0); nmc->should_wait = TRUE; info = g_malloc0 (sizeof (AddAndActivateInfo)); @@ -1907,7 +2254,67 @@ error: g_byte_array_free (bssid1_arr, TRUE); if (bssid2_arr) g_byte_array_free (bssid2_arr, TRUE); + g_free (ssid_ask); + g_free (passwd_ask); + + return nmc->return_value; +} + +static void +request_rescan_cb (NMDeviceWifi *device, GError *error, gpointer user_data) +{ + NmCli *nmc = (NmCli *) user_data; + + if (error) { + g_string_printf (nmc->return_text, _("Error: %s."), + error->message ? error->message : _("unknown")); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + } + quit (); +} + +static NMCResultCode +do_device_wifi_rescan (NmCli *nmc, int argc, char **argv) +{ + NMDevice *device; + const char *ifname = NULL; + const GPtrArray *devices; + int devices_idx; + + nmc->should_wait = TRUE; + + /* Get the parameters */ + if (argc > 0) { + if (strcmp (*argv, "ifname") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } + ifname = *argv; + } + + /* Find Wi-Fi device to scan on. When no ifname is provided, the first Wi-Fi is used. */ + nmc->get_client (nmc); + devices = nm_client_get_devices (nmc->client); + devices_idx = 0; + device = find_wifi_device_by_iface (devices, ifname, &devices_idx); + + if (!device) { + if (ifname) + g_string_printf (nmc->return_text, _("Error: Device '%s' is not a Wi-Fi device."), ifname); + else + g_string_printf (nmc->return_text, _("Error: No Wi-Fi device found.")); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + goto error; + } + nm_device_wifi_request_scan_simple (NM_DEVICE_WIFI (device), request_rescan_cb, nmc); + + return nmc->return_value; +error: + nmc->should_wait = FALSE; return nmc->return_value; } @@ -1921,8 +2328,10 @@ do_device_wifi (NmCli *nmc, int argc, char **argv) nmc->return_value = do_device_wifi_list (nmc, argc-1, argv+1); } else if (matches (*argv, "connect") == 0) { nmc->return_value = do_device_wifi_connect_network (nmc, argc-1, argv+1); + } else if (matches (*argv, "rescan") == 0) { + nmc->return_value = do_device_wifi_rescan (nmc, argc-1, argv+1); } else { - g_string_printf (nmc->return_text, _("Error: 'dev wifi' command '%s' is not valid."), *argv); + g_string_printf (nmc->return_text, _("Error: 'device wifi' command '%s' is not valid."), *argv); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; } } @@ -1936,13 +2345,21 @@ show_nsp_info (NMDevice *device, NmCli *nmc) { const GPtrArray *nsps; int i, idx = 1; + NmcOutputField *arr; + + /* Add headers (field names) */ + arr = nmc_dup_fields_array (nmc_fields_dev_wimax_list, sizeof (nmc_fields_dev_wimax_list), + NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); nsps = nm_device_wimax_get_nsps (NM_DEVICE_WIMAX (device)); for (i = 0; nsps && i < nsps->len; i++) { NMWimaxNsp *nsp = g_ptr_array_index (nsps, i); - detail_wimax_nsp (nsp, nmc, device, idx++); + fill_output_wimax_nsp (nsp, nmc, device, idx++, 0); } + print_data (nmc); /* Print all data */ + nmc_empty_output_fields (nmc); } static NMCResultCode @@ -1951,7 +2368,7 @@ do_device_wimax_list (NmCli *nmc, int argc, char **argv) GError *error = NULL; NMDevice *device = NULL; NMWimaxNsp *nsp = NULL; - const char *iface = NULL; + const char *ifname = NULL; const char *nsp_user = NULL; const GPtrArray *devices; const GPtrArray *nsps; @@ -1959,18 +2376,18 @@ do_device_wimax_list (NmCli *nmc, int argc, char **argv) char *fields_str; char *fields_all = NMC_FIELDS_DEV_WIMAX_LIST_ALL; char *fields_common = NMC_FIELDS_DEV_WIMAX_LIST_COMMON; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + const char *base_hdr = _("WiMAX NSP list"); while (argc > 0) { - if (strcmp (*argv, "iface") == 0) { + if (strcmp (*argv, "ifname") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } - iface = *argv; + ifname = *argv; } else if (strcmp (*argv, "nsp") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); @@ -1993,58 +2410,50 @@ do_device_wimax_list (NmCli *nmc, int argc, char **argv) else fields_str = nmc->required_fields; - nmc->allowed_fields = nmc_fields_dev_wimax_list; - nmc->print_fields.indices = parse_output_fields (fields_str, nmc->allowed_fields, &error); + tmpl = nmc_fields_dev_wimax_list; + tmpl_len = sizeof (nmc_fields_dev_wimax_list); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &error); if (error) { - if (error->code == 0) - g_string_printf (nmc->return_text, _("Error: 'dev wimax': %s"), error->message); - else - g_string_printf (nmc->return_text, _("Error: 'dev wimax': %s; allowed fields: %s"), error->message, NMC_FIELDS_DEV_WIMAX_LIST_ALL); + g_string_printf (nmc->return_text, _("Error: 'device wimax': %s"), error->message); g_error_free (error); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } - if (!nmc_is_nm_running (nmc, &error)) { - if (error) { - g_string_printf (nmc->return_text, _("Error: Can't find out if NetworkManager is running: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - g_error_free (error); - } else { - g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); - nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; - } + nmc->get_client (nmc); + + if (!nm_client_get_manager_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; goto error; } if (!nmc_versions_match (nmc)) goto error; - /* Print headers */ - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ADD | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.header_name = _("WiMAX NSP list"); - - nmc->get_client (nmc); devices = nm_client_get_devices (nmc->client); - if (iface) { + if (ifname) { /* Device specified - list only NSPs of this interface */ for (i = 0; devices && (i < devices->len); i++) { NMDevice *candidate = g_ptr_array_index (devices, i); const char *dev_iface = nm_device_get_iface (candidate); - if (!g_strcmp0 (dev_iface, iface)) { + if (!g_strcmp0 (dev_iface, ifname)) { device = candidate; break; } } if (!device) { - g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), iface); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; goto error; } + /* Main header name */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, ifname); + if (NM_IS_DEVICE_WIMAX (device)) { if (nsp_user) { /* Specific NSP requested - list only that */ @@ -2061,23 +2470,24 @@ do_device_wimax_list (NmCli *nmc, int argc, char **argv) } if (!nsp) { g_string_printf (nmc->return_text, _("Error: NSP with name '%s' not found."), nsp_user); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; goto error; } - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ - detail_wimax_nsp (nsp, nmc, device, 1); + /* Add headers (field names) */ + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + fill_output_wimax_nsp (nsp, nmc, device, 1, 0); + print_data (nmc); /* Print all data */ } else { - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ show_nsp_info (device, nmc); } } else { - g_string_printf (nmc->return_text, _("Error: Device '%s' is not a WiMAX device."), iface); + g_string_printf (nmc->return_text, _("Error: Device '%s' is not a WiMAX device."), ifname); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; goto error; } } else { /* List NSPs for all devices */ - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ if (nsp_user) { /* Specific NSP requested - list only that */ for (i = 0; devices && (i < devices->len); i++) { @@ -2087,6 +2497,12 @@ do_device_wimax_list (NmCli *nmc, int argc, char **argv) if (!NM_IS_DEVICE_WIMAX (dev)) continue; + /* Main header name */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_device_get_iface (dev)); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + nsps = nm_device_wimax_get_nsps (NM_DEVICE_WIMAX (dev)); for (j = 0, nsp = NULL; nsps && (j < nsps->len); j++) { NMWimaxNsp *candidate_nsp = g_ptr_array_index (nsps, j); @@ -2096,19 +2512,26 @@ do_device_wimax_list (NmCli *nmc, int argc, char **argv) nsp_up = g_ascii_strup (nsp_user, -1); if (!strcmp (nsp_up, candidate_name)) { nsp = candidate_nsp; - detail_wimax_nsp (nsp, nmc, dev, idx); + fill_output_wimax_nsp (nsp, nmc, dev, idx, 0); } g_free (nsp_up); } + print_data (nmc); /* Print all data */ + nmc_empty_output_fields (nmc); } if (!nsp) { g_string_printf (nmc->return_text, _("Error: Access point with nsp '%s' not found."), nsp_user); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; goto error; } } else { for (i = 0; devices && (i < devices->len); i++) { NMDevice *dev = g_ptr_array_index (devices, i); + + /* Main header name */ + nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, + nm_device_get_iface (dev)); + if (NM_IS_DEVICE_WIMAX (dev)) show_nsp_info (dev, nmc); } @@ -2129,7 +2552,7 @@ do_device_wimax (NmCli *nmc, int argc, char **argv) nmc->return_value = do_device_wimax_list (nmc, argc-1, argv+1); } else { - g_string_printf (nmc->return_text, _("Error: 'dev wimax' command '%s' is not valid."), *argv); + g_string_printf (nmc->return_text, _("Error: 'device wimax' command '%s' is not valid."), *argv); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; } } @@ -2138,11 +2561,82 @@ do_device_wimax (NmCli *nmc, int argc, char **argv) } #endif +static gboolean +is_single_word (const char* line) +{ + size_t n1, n2, n3; + + n1 = strspn (line, " \t"); + n2 = strcspn (line+n1, " \t\0") + n1; + n3 = strspn (line+n2, " \t"); + + if (n3 == 0) + return TRUE; + else + return FALSE; +} + +/* Global variable defined in nmcli.c */ +extern NmCli nm_cli; + +static char * +gen_func_ifnames (const char *text, int state) +{ + int i, j = 0; + const GPtrArray *devices; + const char **ifnames; + char *ret; + + nm_cli.get_client (&nm_cli); + devices = nm_client_get_devices (nm_cli.client); + if (!devices || devices->len < 1) + return NULL; + + ifnames = g_new (const char *, devices->len + 1); + for (i = 0; i < devices->len; i++) { + NMDevice *dev = g_ptr_array_index (devices, i); + const char *ifname = nm_device_get_iface (dev); + ifnames[j++] = ifname; + } + ifnames[j] = NULL; + + ret = nmc_rl_gen_func_basic (text, state, ifnames); + + g_free (ifnames); + return ret; +} + +static char ** +nmcli_device_tab_completion (const char *text, int start, int end) +{ + char **match_array = NULL; + rl_compentry_func_t *generator_func = NULL; + + /* Disable readline's default filename completion */ + rl_attempted_completion_over = 1; + + /* Disable appending space after completion */ + rl_completion_append_character = '\0'; + + if (!is_single_word (rl_line_buffer)) + return NULL; + + if (g_strcmp0 (rl_prompt, PROMPT_INTERFACE) == 0) + generator_func = gen_func_ifnames; + + if (generator_func) + match_array = rl_completion_matches (text, generator_func); + + return match_array; +} + NMCResultCode do_devices (NmCli *nmc, int argc, char **argv) { GError *error = NULL; + rl_attempted_completion_function = (rl_completion_func_t *) nmcli_device_tab_completion; + if (argc == 0) { if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) goto opt_error; @@ -2150,36 +2644,62 @@ do_devices (NmCli *nmc, int argc, char **argv) } if (argc > 0) { - if (matches (*argv, "status") == 0) { + if (nmc_arg_is_help (*argv)) { + usage (); + goto usage_exit; + } + else if (matches (*argv, "status") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_device_status (); + goto usage_exit; + } if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) goto opt_error; nmc->return_value = do_devices_status (nmc, argc-1, argv+1); } - else if (matches (*argv, "list") == 0) { + else if (matches (*argv, "show") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_device_show (); + goto usage_exit; + } if (!nmc->mode_specified) - nmc->multiline_output = TRUE; /* multiline mode is default for 'dev list' */ - nmc->return_value = do_devices_list (nmc, argc-1, argv+1); + nmc->multiline_output = TRUE; /* multiline mode is default for 'device show' */ + nmc->return_value = do_devices_show (nmc, argc-1, argv+1); + } + else if (matches (*argv, "connect") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_device_connect (); + goto usage_exit; + } + nmc->return_value = do_device_connect (nmc, argc-1, argv+1); } else if (matches (*argv, "disconnect") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_device_disconnect (); + goto usage_exit; + } nmc->return_value = do_device_disconnect (nmc, argc-1, argv+1); } else if (matches (*argv, "wifi") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_device_wifi (); + goto usage_exit; + } if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) goto opt_error; nmc->return_value = do_device_wifi (nmc, argc-1, argv+1); } #if WITH_WIMAX else if (matches (*argv, "wimax") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_device_wimax (); + goto usage_exit; + } if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) goto opt_error; nmc->return_value = do_device_wimax (nmc, argc-1, argv+1); } #endif - else if ( matches (*argv, "help") == 0 - || (g_str_has_prefix (*argv, "-") && matches ((*argv)+1, "help") == 0) - || (g_str_has_prefix (*argv, "--") && matches ((*argv)+2, "help") == 0)) { - usage (); - } else { usage (); g_string_printf (nmc->return_text, _("Error: 'dev' command '%s' is not valid."), *argv); @@ -2187,6 +2707,7 @@ do_devices (NmCli *nmc, int argc, char **argv) } } +usage_exit: return nmc->return_value; opt_error: diff --git a/cli/src/network-manager.c b/cli/src/network-manager.c index 8fb5f6cf..ed074c6d 100644 --- a/cli/src/network-manager.c +++ b/cli/src/network-manager.c @@ -14,7 +14,7 @@ * 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 - 2012 Red Hat, Inc. + * (C) Copyright 2010 - 2014 Red Hat, Inc. */ #include "config.h" @@ -32,67 +32,223 @@ #include "network-manager.h" -/* Available fields for 'nm status' */ +/* Available fields for 'general status' */ static NmcOutputField nmc_fields_nm_status[] = { - {"RUNNING", N_("RUNNING"), 15, NULL, 0}, /* 0 */ - {"VERSION", N_("VERSION"), 10, NULL, 0}, /* 1 */ - {"STATE", N_("STATE"), 15, NULL, 0}, /* 2 */ - {"NET-ENABLED", N_("NET-ENABLED"), 13, NULL, 0}, /* 3 */ - {"WIFI-HARDWARE", N_("WIFI-HARDWARE"), 15, NULL, 0}, /* 4 */ - {"WIFI", N_("WIFI"), 10, NULL, 0}, /* 5 */ - {"WWAN-HARDWARE", N_("WWAN-HARDWARE"), 15, NULL, 0}, /* 6 */ - {"WWAN", N_("WWAN"), 10, NULL, 0}, /* 7 */ - {"WIMAX-HARDWARE", N_("WIMAX-HARDWARE"), 15, NULL, 0}, /* 8 */ - {"WIMAX", N_("WIMAX"), 10, NULL, 0}, /* 9 */ - {NULL, NULL, 0, NULL, 0} + {"RUNNING", N_("RUNNING"), 15}, /* 0 */ + {"VERSION", N_("VERSION"), 10}, /* 1 */ + {"STATE", N_("STATE"), 15}, /* 2 */ + {"STARTUP", N_("STARTUP"), 10}, /* 3 */ + {"CONNECTIVITY", N_("CONNECTIVITY"), 15}, /* 4 */ + {"NETWORKING", N_("NETWORKING"), 13}, /* 5 */ + {"WIFI-HW", N_("WIFI-HW"), 15}, /* 6 */ + {"WIFI", N_("WIFI"), 10}, /* 7 */ + {"WWAN-HW", N_("WWAN-HW"), 15}, /* 8 */ + {"WWAN", N_("WWAN"), 10}, /* 9 */ + {"WIMAX-HW", N_("WIMAX-HW"), 15}, /* 10 */ + {"WIMAX", N_("WIMAX"), 10}, /* 11 */ + {NULL, NULL, 0} }; #if WITH_WIMAX -#define NMC_FIELDS_NM_STATUS_ALL "RUNNING,VERSION,STATE,NET-ENABLED,WIFI-HARDWARE,WIFI,WWAN-HARDWARE,WWAN,WIMAX-HARDWARE,WIMAX" +#define NMC_FIELDS_NM_STATUS_ALL "RUNNING,VERSION,STATE,STARTUP,CONNECTIVITY,NETWORKING,WIFI-HW,WIFI,WWAN-HW,WWAN,WIMAX-HW,WIMAX" +#define NMC_FIELDS_NM_STATUS_SWITCH "NETWORKING,WIFI-HW,WIFI,WWAN-HW,WWAN,WIMAX-HW,WIMAX" +#define NMC_FIELDS_NM_STATUS_RADIO "WIFI-HW,WIFI,WWAN-HW,WWAN,WIMAX-HW,WIMAX" #else -#define NMC_FIELDS_NM_STATUS_ALL "RUNNING,VERSION,STATE,NET-ENABLED,WIFI-HARDWARE,WIFI,WWAN-HARDWARE,WWAN" +#define NMC_FIELDS_NM_STATUS_ALL "RUNNING,VERSION,STATE,STARTUP,CONNECTIVITY,NETWORKING,WIFI-HW,WIFI,WWAN-HW,WWAN" +#define NMC_FIELDS_NM_STATUS_SWITCH "NETWORKING,WIFI-HW,WIFI,WWAN-HW,WWAN" +#define NMC_FIELDS_NM_STATUS_RADIO "WIFI-HW,WIFI,WWAN-HW,WWAN" #endif -#define NMC_FIELDS_NM_STATUS_COMMON "RUNNING,STATE,WIFI-HARDWARE,WIFI,WWAN-HARDWARE,WWAN" -#define NMC_FIELDS_NM_NET_ENABLED "NET-ENABLED" +#define NMC_FIELDS_NM_STATUS_COMMON "STATE,CONNECTIVITY,WIFI-HW,WIFI,WWAN-HW,WWAN" +#define NMC_FIELDS_NM_NETWORKING "NETWORKING" #define NMC_FIELDS_NM_WIFI "WIFI" #define NMC_FIELDS_NM_WWAN "WWAN" #define NMC_FIELDS_NM_WIMAX "WIMAX" +#define NMC_FIELDS_NM_CONNECTIVITY "CONNECTIVITY" -/* Available fields for 'nm permissions' */ + +/* Available fields for 'general permissions' */ static NmcOutputField nmc_fields_nm_permissions[] = { - {"PERMISSION", N_("PERMISSION"), 57, NULL, 0}, /* 0 */ - {"VALUE", N_("VALUE"), 10, NULL, 0}, /* 1 */ - {NULL, NULL, 0, NULL, 0} + {"PERMISSION", N_("PERMISSION"), 57}, /* 0 */ + {"VALUE", N_("VALUE"), 10}, /* 1 */ + {NULL, NULL, 0} }; #define NMC_FIELDS_NM_PERMISSIONS_ALL "PERMISSION,VALUE" #define NMC_FIELDS_NM_PERMISSIONS_COMMON "PERMISSION,VALUE" +/* Available fields for 'general logging' */ +static NmcOutputField nmc_fields_nm_logging[] = { + {"LEVEL", N_("LEVEL"), 10}, /* 0 */ + {"DOMAINS", N_("DOMAINS"), 70}, /* 1 */ + {NULL, NULL, 0} +}; +#define NMC_FIELDS_NM_LOGGING_ALL "LEVEL,DOMAINS" +#define NMC_FIELDS_NM_LOGGING_COMMON "LEVEL,DOMAINS" + /* glib main loop variable - defined in nmcli.c */ extern GMainLoop *loop; + +static void +usage_general (void) +{ + fprintf (stderr, + _("Usage: nmcli general { COMMAND | help }\n\n" + "COMMAND := { status | hostname | permissions | logging }\n\n" + " status\n\n" + " hostname [<hostname>]\n\n" + " permissions\n\n" + " logging [level <log level>] [domains <log domains>]\n\n")); +} + +static void +usage_general_status (void) +{ + fprintf (stderr, + _("Usage: nmcli general status { help }\n" + "\n" + "Show overall status of NetworkManager.\n" + "'status' is the default action, which means 'nmcli gen' calls 'nmcli gen status'\n\n")); +} + +static void +usage_general_hostname (void) +{ + fprintf (stderr, + _("Usage: nmcli general hostname { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [<hostname>]\n" + "\n" + "Get or change persistent system hostname.\n" + "With no arguments, this prints currently configured hostname. When you pass\n" + "a hostname, NetworkManager will set it as the new persistent system hostname.\n\n")); +} + +static void +usage_general_permissions (void) +{ + fprintf (stderr, + _("Usage: nmcli general permissions { help }\n" + "\n" + "Show caller permissions for authenticated operations.\n\n")); +} + +static void +usage_general_logging (void) +{ + fprintf (stderr, + _("Usage: nmcli general logging { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [level <log level>] [domains <log domains>]\n" + "\n" + "Get or change NetworkManager logging level and domains.\n" + "Without any argument current logging level and domains are shown. In order to\n" + "change logging state, provide level and/or domain. Please refer to the man page\n" + "for the list of possible logging domains.\n\n")); +} + +static void +usage_networking (void) +{ + fprintf (stderr, + _("Usage: nmcli networking { COMMAND | help }\n\n" + "COMMAND := { [ on | off | connectivity ] }\n\n" + " on\n\n" + " off\n\n" + " connectivity [check]\n\n")); +} + +static void +usage_networking_on (void) +{ + fprintf (stderr, + _("Usage: nmcli networking on { help }\n" + "\n" + "Switch networking on.\n\n")); +} + +static void +usage_networking_off (void) +{ + fprintf (stderr, + _("Usage: nmcli networking off { help }\n" + "\n" + "Switch networking off.\n\n")); +} + +static void +usage_networking_connectivity (void) +{ + fprintf (stderr, + _("Usage: nmcli networking connectivity { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [check]\n" + "\n" + "Get network connectivity state.\n" + "The optional 'check' argument makes NetworkManager re-check the connectivity.\n\n")); + +} + static void -usage (void) +usage_radio (void) { fprintf (stderr, - _("Usage: nmcli nm { COMMAND | help }\n\n" + _("Usage: nmcli radio { COMMAND | help }\n\n" #if WITH_WIMAX - " COMMAND := { status | permissions | enable | sleep | wifi | wwan | wimax }\n\n" + "COMMAND := { all | wifi | wwan | wimax }\n\n" + " all | wifi | wwan | wimax [ on | off ]\n\n" #else - " COMMAND := { status | permissions | enable | sleep | wifi | wwan }\n\n" -#endif - " status\n" - " permissions\n" - " enable [true|false]\n" - " sleep [true|false]\n" - " wifi [on|off]\n" - " wwan [on|off]\n" -#if WITH_WIMAX - " wimax [on|off]\n" + "COMMAND := { all | wifi | wwan }\n\n" + " all | wifi | wwan [ on | off ]\n\n" #endif - "\n" )); } +static void +usage_radio_all (void) +{ + fprintf (stderr, + _("Usage: nmcli radio all { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [on | off]\n" + "\n" + "Get status of all radio switches, or turn them on/off.\n\n")); +} + +static void +usage_radio_wifi (void) +{ + fprintf (stderr, + _("Usage: nmcli radio wifi { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [on | off]\n" + "\n" + "Get status of Wi-Fi radio switch, or turn it on/off.\n\n")); +} + +static void +usage_radio_wwan (void) +{ + fprintf (stderr, + _("Usage: nmcli radio wwan { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [on | off]\n" + "\n" + "Get status of mobile broadband radio switch, or turn it on/off.\n\n")); +} + +#if WITH_WIMAX +static void +usage_radio_wimax (void) +{ + fprintf (stderr, + _("Usage: nmcli radio wimax { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := [on | off]\n" + "\n" + "Get status of WiMAX radio switch, or turn it on/off.\n\n")); +} +#endif + /* quit main loop */ static void quit (void) @@ -124,11 +280,30 @@ nm_state_to_string (NMState state) } } -static NMCResultCode -show_nm_status (NmCli *nmc) +static const char * +nm_connectivity_to_string (NMConnectivityState connectivity) +{ + switch (connectivity) { + case NM_CONNECTIVITY_NONE: + return _("none"); + case NM_CONNECTIVITY_PORTAL: + return _("portal"); + case NM_CONNECTIVITY_LIMITED: + return _("limited"); + case NM_CONNECTIVITY_FULL: + return _("full"); + case NM_CONNECTIVITY_UNKNOWN: + default: + return _("unknown"); + } +} + +static gboolean +show_nm_status (NmCli *nmc, const char *pretty_header_name, const char *print_flds) { - gboolean nm_running; + gboolean nm_running, startup = FALSE; NMState state = NM_STATE_UNKNOWN; + NMConnectivityState connectivity = NM_CONNECTIVITY_UNKNOWN; const char *net_enabled_str; const char *wireless_hw_enabled_str, *wireless_enabled_str; const char *wwan_hw_enabled_str, *wwan_enabled_str; @@ -137,11 +312,10 @@ show_nm_status (NmCli *nmc) #endif GError *error = NULL; const char *fields_str; - const char *fields_all = NMC_FIELDS_NM_STATUS_ALL; - const char *fields_common = NMC_FIELDS_NM_STATUS_COMMON; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + const char *fields_all = print_flds ? print_flds : NMC_FIELDS_NM_STATUS_ALL; + const char *fields_common = print_flds ? print_flds : NMC_FIELDS_NM_STATUS_COMMON; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) fields_str = fields_common; @@ -150,26 +324,27 @@ show_nm_status (NmCli *nmc) else fields_str = nmc->required_fields; - nmc->allowed_fields = nmc_fields_nm_status; - nmc->print_fields.indices = parse_output_fields (fields_str, nmc->allowed_fields, &error); + tmpl = nmc_fields_nm_status; + tmpl_len = sizeof (nmc_fields_nm_status); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &error); if (error) { - if (error->code == 0) - g_string_printf (nmc->return_text, _("Error: 'nm status': %s"), error->message); - else - g_string_printf (nmc->return_text, _("Error: 'nm status': %s; allowed fields: %s"), error->message, NMC_FIELDS_NM_STATUS_ALL); + g_string_printf (nmc->return_text, _("Error: only these fields are allowed: %s"), fields_all); g_error_free (error); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return nmc->return_value; + return FALSE; } - nm_running = nmc_is_nm_running (nmc, NULL); + nmc->get_client (nmc); /* create NMClient */ + + nm_running = nm_client_get_manager_running (nmc->client); if (nm_running) { if (!nmc_versions_match (nmc)) - goto error; + return FALSE; - nmc->get_client (nmc); /* create NMClient */ state = nm_client_get_state (nmc->client); + startup = nm_client_get_startup (nmc->client); + connectivity = nm_client_get_connectivity (nmc->client); net_enabled_str = nm_client_networking_get_enabled (nmc->client) ? _("enabled") : _("disabled"); wireless_hw_enabled_str = nm_client_wireless_hardware_get_enabled (nmc->client) ? _("enabled") : _("disabled"); wireless_enabled_str = nm_client_wireless_get_enabled (nmc->client) ? _("enabled") : _("disabled"); @@ -189,30 +364,30 @@ show_nm_status (NmCli *nmc) #endif } - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ADD | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.header_name = _("NetworkManager status"); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ - - nmc->allowed_fields[0].value = nm_running ? _("running") : _("not running"); - nmc->allowed_fields[1].value = nm_running ? nm_client_get_version (nmc->client) : _("unknown"); - nmc->allowed_fields[2].value = nm_state_to_string (state); - nmc->allowed_fields[3].value = net_enabled_str; - nmc->allowed_fields[4].value = wireless_hw_enabled_str; - nmc->allowed_fields[5].value = wireless_enabled_str; - nmc->allowed_fields[6].value = wwan_hw_enabled_str; - nmc->allowed_fields[7].value = wwan_enabled_str; + nmc->print_fields.header_name = pretty_header_name ? (char *) pretty_header_name : _("NetworkManager status"); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, 0); + set_val_strc (arr, 0, nm_running ? _("running") : _("not running")); + set_val_strc (arr, 1, nm_running ? nm_client_get_version (nmc->client) : _("unknown")); + set_val_strc (arr, 2, nm_state_to_string (state)); + set_val_strc (arr, 3, nm_running ? (startup ? _("starting") : _("started")) : _("unknown")); + set_val_strc (arr, 4, nm_connectivity_to_string (connectivity)); + set_val_strc (arr, 5, net_enabled_str); + set_val_strc (arr, 6, wireless_hw_enabled_str); + set_val_strc (arr, 7, wireless_enabled_str); + set_val_strc (arr, 8, wwan_hw_enabled_str); + set_val_strc (arr, 9, wwan_enabled_str); #if WITH_WIMAX - nmc->allowed_fields[8].value = wimax_hw_enabled_str; - nmc->allowed_fields[9].value = wimax_enabled_str; + set_val_strc (arr, 10, wimax_hw_enabled_str); + set_val_strc (arr, 11, wimax_enabled_str); #endif + g_ptr_array_add (nmc->output_data, arr); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + print_data (nmc); /* Print all data */ - return NMC_RESULT_SUCCESS; - -error: - return nmc->return_value; + return TRUE; } #define NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK "org.freedesktop.NetworkManager.enable-disable-network" @@ -274,7 +449,7 @@ permission_result_to_string (NMClientPermissionResult perm_result) } } -static NMCResultCode +static gboolean show_nm_permissions (NmCli *nmc) { NMClientPermission perm; @@ -282,9 +457,8 @@ show_nm_permissions (NmCli *nmc) const char *fields_str; const char *fields_all = NMC_FIELDS_NM_PERMISSIONS_ALL; const char *fields_common = NMC_FIELDS_NM_PERMISSIONS_COMMON; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) fields_str = fields_common; @@ -293,311 +467,451 @@ show_nm_permissions (NmCli *nmc) else fields_str = nmc->required_fields; - nmc->allowed_fields = nmc_fields_nm_permissions; - nmc->print_fields.indices = parse_output_fields (fields_str, nmc->allowed_fields, &error); + tmpl = nmc_fields_nm_permissions; + tmpl_len = sizeof (nmc_fields_nm_permissions); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &error); if (error) { - if (error->code == 0) - g_string_printf (nmc->return_text, _("Error: 'nm permissions': %s"), error->message); - else - g_string_printf (nmc->return_text, _("Error: 'nm permissions': %s; allowed fields: %s"), error->message, NMC_FIELDS_NM_PERMISSIONS_ALL); + g_string_printf (nmc->return_text, _("Error: 'general permissions': %s"), error->message); g_error_free (error); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return nmc->return_value; - } - - if (!nmc_is_nm_running (nmc, &error)) { - if (error) { - g_string_printf (nmc->return_text, _("Error: Can't find out if NetworkManager is running: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - g_error_free (error); - } else { - g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); - nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; - } - goto error; + return FALSE; } nmc->get_client (nmc); /* create NMClient */ - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ADD | NMC_PF_FLAG_FIELD_NAMES; + if (!nm_client_get_manager_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + return FALSE; + } + nmc->print_fields.header_name = _("NetworkManager permissions"); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); for (perm = NM_CLIENT_PERMISSION_NONE + 1; perm <= NM_CLIENT_PERMISSION_LAST; perm++) { NMClientPermissionResult perm_result = nm_client_get_permission_result (nmc->client, perm); - set_val_str (nmc->allowed_fields, 0, permission_to_string (perm)); - set_val_str (nmc->allowed_fields, 1, permission_result_to_string (perm_result)); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + arr = nmc_dup_fields_array (tmpl, tmpl_len, 0); + set_val_strc (arr, 0, permission_to_string (perm)); + set_val_strc (arr, 1, permission_result_to_string (perm_result)); + g_ptr_array_add (nmc->output_data, arr); } - return NMC_RESULT_SUCCESS; + print_data (nmc); /* Print all data */ -error: - return nmc->return_value; + return TRUE; } -/* libnm-glib doesn't provide API fro Sleep method - implement D-Bus call ourselves */ -static void networking_set_sleep (NmCli *nmc, gboolean in_sleep) +static gboolean +show_general_logging (NmCli *nmc) { - DBusGConnection *connection = NULL; - DBusGProxy *proxy = NULL; - GError *err = NULL; + char *level = NULL; + char *domains = NULL; + GError *error = NULL; + const char *fields_str; + const char *fields_all = NMC_FIELDS_NM_LOGGING_ALL; + const char *fields_common = NMC_FIELDS_NM_LOGGING_COMMON; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; - connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err); - if (!connection) { - g_string_printf (nmc->return_text, _("Error: Couldn't connect to system bus: %s"), err->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - g_error_free (err); - goto gone; + if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) + fields_str = fields_common; + else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) + fields_str = fields_all; + else + fields_str = nmc->required_fields; + + tmpl = nmc_fields_nm_logging; + tmpl_len = sizeof (nmc_fields_nm_logging); + nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &error); + + if (error) { + g_string_printf (nmc->return_text, _("Error: 'general logging': %s"), error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return FALSE; } - proxy = dbus_g_proxy_new_for_name (connection, - "org.freedesktop.NetworkManager", - "/org/freedesktop/NetworkManager", - "org.freedesktop.NetworkManager"); - if (!proxy) { - g_string_printf (nmc->return_text, _("Error: Couldn't create D-Bus object proxy.")); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - goto gone; + nmc->get_client (nmc); /* create NMClient */ + nm_client_get_logging (nmc->client, &level, &domains, &error); + if (error) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_error_free (error); + return FALSE; } - if (!dbus_g_proxy_call (proxy, "Sleep", &err, G_TYPE_BOOLEAN, in_sleep, G_TYPE_INVALID, G_TYPE_INVALID)) { - g_string_printf (nmc->return_text, _("Error in sleep: %s"), err->message); + nmc->print_fields.header_name = _("NetworkManager logging"); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, 0); + set_val_str (arr, 0, level); + set_val_str (arr, 1, domains); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static void +save_hostname_cb (NMRemoteSettings *settings, GError *error, gpointer user_data) +{ + NmCli *nmc = (NmCli *) user_data; + + if (error) { + g_string_printf (nmc->return_text, _("Error: failed to set hostname: (%d) %s"), + error->code, error->message); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - g_error_free (err); } - -gone: - if (connection) dbus_g_connection_unref (connection); - if (proxy) g_object_unref (proxy); + quit (); } -/* entry point function for global network manager related commands 'nmcli nm' */ +/* + * Entry point function for general operations 'nmcli general' + */ NMCResultCode -do_network_manager (NmCli *nmc, int argc, char **argv) +do_general (NmCli *nmc, int argc, char **argv) { GError *error = NULL; - gboolean sleep_flag; - gboolean enable_net; - gboolean enable_wifi; - gboolean enable_wwan; -#if WITH_WIMAX - gboolean enable_wimax; -#endif - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; if (argc == 0) { - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; - nmc->return_value = show_nm_status (nmc); + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + show_nm_status (nmc, NULL, NULL); } if (argc > 0) { - if (matches (*argv, "status") == 0) { - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; - nmc->return_value = show_nm_status (nmc); + if (nmc_arg_is_help (*argv)) { + usage_general (); + } + else if (matches (*argv, "status") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_general_status (); + goto finish; + } + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + show_nm_status (nmc, NULL, NULL); + } + else if (matches (*argv, "hostname") == 0) { + NMRemoteSettings *rem_settings; + + if (nmc_arg_is_help (*(argv+1))) { + usage_general_hostname (); + goto finish; + } + + /* get system settings */ + if (!(rem_settings = nm_remote_settings_new (NULL))) { + g_string_printf (nmc->return_text, _("Error: Could not get system settings.")); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + goto finish; + } + + if (next_arg (&argc, &argv) != 0) { + /* no arguments -> get hostname */ + char *hostname = NULL; + + g_object_get (rem_settings, NM_REMOTE_SETTINGS_HOSTNAME, &hostname, NULL); + if (hostname) + printf ("%s\n", hostname); + g_free (hostname); + } else { + /* hostname provided -> set it */ + const char *hostname = *argv; + + if (next_arg (&argc, &argv) == 0) + printf ("Warning: ignoring extra garbage after '%s' hostname\n", hostname); + + nmc->should_wait = TRUE; + nm_remote_settings_save_hostname (rem_settings, hostname, save_hostname_cb, nmc); + } } else if (matches (*argv, "permissions") == 0) { - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; - nmc->return_value = show_nm_permissions (nmc); + if (nmc_arg_is_help (*(argv+1))) { + usage_general_permissions (); + goto finish; + } + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + show_nm_permissions (nmc); } - else if (matches (*argv, "enable") == 0) { + else if (matches (*argv, "logging") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_general_logging (); + goto finish; + } if (next_arg (&argc, &argv) != 0) { - /* no argument, show current state of networking */ - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; - if (nmc->required_fields && strcasecmp (nmc->required_fields, "NET-ENABLED")) { - g_string_printf (nmc->return_text, _("Error: '--fields' value '%s' is not valid here; allowed fields: %s"), - nmc->required_fields, NMC_FIELDS_NM_NET_ENABLED); + /* no arguments -> get logging level and domains */ + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto end; + goto finish; } - nmc->allowed_fields = nmc_fields_nm_status; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_NM_NET_ENABLED, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ADD | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.header_name = _("Networking enabled"); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ - - if (nmc_is_nm_running (nmc, NULL)) { - nmc->get_client (nmc); /* create NMClient */ - nmc->allowed_fields[3].value = nm_client_networking_get_enabled (nmc->client) ? _("enabled") : _("disabled"); - } else - nmc->allowed_fields[3].value = _("unknown"); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + show_general_logging (nmc); } else { - if (!strcmp (*argv, "true")) - enable_net = TRUE; - else if (!strcmp (*argv, "false")) - enable_net = FALSE; - else { - g_string_printf (nmc->return_text, _("Error: invalid 'enable' parameter: '%s'; use 'true' or 'false'."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto end; + /* arguments provided -> set logging level and domains */ + const char *level = NULL; + const char *domains = NULL; + nmc_arg_t exp_args[] = { {"level", TRUE, &level, TRUE}, + {"domains", TRUE, &domains, TRUE}, + {NULL} }; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) { + g_string_assign (nmc->return_text, error->message); + nmc->return_value = error->code; + goto finish; } + nmc->get_client (nmc); /* create NMClient */ - nm_client_networking_set_enabled (nmc->client, enable_net); + nm_client_set_logging (nmc->client, level, domains, &error); + if (error) { + if (g_error_matches (error, DBUS_GERROR, DBUS_GERROR_ACCESS_DENIED)) + g_string_printf (nmc->return_text, _("Error: access denied to set logging; %s"), error->message); + else + g_string_printf (nmc->return_text, _("Error: %s"), error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } } } - else if (matches (*argv, "sleep") == 0) { + else { + usage_general (); + g_string_printf (nmc->return_text, _("Error: 'general' command '%s' is not valid."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + } + } + +finish: + if (error) + g_error_free (error); + return nmc->return_value; +} + +static gboolean +nmc_switch_show (NmCli *nmc, const char *switch_name, const char *header) +{ + g_return_val_if_fail (nmc != NULL, FALSE); + g_return_val_if_fail (switch_name != NULL, FALSE); + + if (nmc->required_fields && strcasecmp (nmc->required_fields, switch_name) != 0) { + g_string_printf (nmc->return_text, _("Error: '--fields' value '%s' is not valid here (allowed field: %s)"), + nmc->required_fields, switch_name); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return FALSE; + } + if (nmc->print_output == NMC_PRINT_NORMAL) + nmc->print_output = NMC_PRINT_TERSE; + + if (!nmc->required_fields) + nmc->required_fields = g_strdup (switch_name); + return show_nm_status (nmc, header, NULL); +} + +static gboolean +nmc_switch_parse_on_off (NmCli *nmc, const char *arg1, const char *arg2, gboolean *res) +{ + g_return_val_if_fail (nmc != NULL, FALSE); + g_return_val_if_fail (arg1 && arg2, FALSE); + g_return_val_if_fail (res != NULL, FALSE); + + if (!strcmp (arg2, "on")) + *res = TRUE; + else if (!strcmp (arg2, "off")) + *res = FALSE; + else { + g_string_printf (nmc->return_text, _("Error: invalid '%s' argument: '%s' (use on/off)."), arg1, arg2); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return FALSE; + } + + return TRUE; +} + +static gboolean +show_networking_connectivity (NmCli *nmc) +{ + return nmc_switch_show (nmc, NMC_FIELDS_NM_CONNECTIVITY, _("Connectivity")); +} + +/* + * Entry point function for 'nmcli networking' + */ +NMCResultCode +do_networking (NmCli *nmc, int argc, char **argv) +{ + gboolean enable_flag; + + if (argc == 0) + nmc_switch_show (nmc, NMC_FIELDS_NM_NETWORKING, _("Networking")); + else if (argc > 0) { + if (nmc_arg_is_help (*argv)) { + usage_networking (); + } else if (matches (*argv, "connectivity") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_networking_connectivity (); + goto finish; + } if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: Sleeping status is not exported by NetworkManager.")); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + /* no arguments -> get current state */ + show_networking_connectivity (nmc); + } else if (matches (*argv, "check") == 0) { + GError *error = NULL; + + nmc->get_client (nmc); /* create NMClient */ + nm_client_check_connectivity (nmc->client, NULL, &error); + if (error) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_clear_error (&error); + } else + show_networking_connectivity (nmc); } else { - if (!strcmp (*argv, "true")) - sleep_flag = TRUE; - else if (!strcmp (*argv, "false")) - sleep_flag = FALSE; - else { - g_string_printf (nmc->return_text, _("Error: invalid 'sleep' parameter: '%s'; use 'true' or 'false'."), *argv); + usage_networking (); + g_string_printf (nmc->return_text, _("Error: 'networking connectivity' command '%s' is not valid."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + } + } else if (nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) { + if (nmc_arg_is_help (*(argv+1))) { + if (enable_flag) + usage_networking_on (); + else + usage_networking_off (); + goto finish; + } + + nmc->get_client (nmc); /* create NMClient */ + nm_client_networking_set_enabled (nmc->client, enable_flag); + } else { + usage_networking (); + g_string_printf (nmc->return_text, _("Error: 'networking' command '%s' is not valid."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + } + } + +finish: + quit (); + return nmc->return_value; +} + +/* + * Entry point function for radio switch commands 'nmcli radio' + */ +NMCResultCode +do_radio (NmCli *nmc, int argc, char **argv) +{ + GError *error = NULL; + gboolean enable_flag; + + if (argc == 0) { + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_error_free (error); + goto finish; + } + show_nm_status (nmc, _("Radio switches"), NMC_FIELDS_NM_STATUS_RADIO); + } + + if (argc > 0) { + if (nmc_arg_is_help (*argv)) { + usage_radio (); + } + else if (matches (*argv, "all") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_radio_all (); + goto finish; + } + if (next_arg (&argc, &argv) != 0) { + /* no argument, show all radio switches */ + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto end; + g_error_free (error); + goto finish; } - networking_set_sleep (nmc, sleep_flag); + show_nm_status (nmc, _("Radio switches"), NMC_FIELDS_NM_STATUS_RADIO); + } else { + if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) + goto finish; + + nmc->get_client (nmc); /* create NMClient */ + nm_client_wireless_set_enabled (nmc->client, enable_flag); + nm_client_wimax_set_enabled (nmc->client, enable_flag); + nm_client_wwan_set_enabled (nmc->client, enable_flag); } } else if (matches (*argv, "wifi") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_radio_wifi (); + goto finish; + } if (next_arg (&argc, &argv) != 0) { /* no argument, show current WiFi state */ - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; - if (nmc->required_fields && strcasecmp (nmc->required_fields, "WIFI")) { - g_string_printf (nmc->return_text, _("Error: '--fields' value '%s' is not valid here; allowed fields: %s"), - nmc->required_fields, NMC_FIELDS_NM_WIFI); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto end; - } - nmc->allowed_fields = nmc_fields_nm_status; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_NM_WIFI, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ADD | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.header_name = _("WiFi enabled"); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ - - if (nmc_is_nm_running (nmc, NULL)) { - nmc->get_client (nmc); /* create NMClient */ - nmc->allowed_fields[5].value = nm_client_wireless_get_enabled (nmc->client) ? _("enabled") : _("disabled"); - } else - nmc->allowed_fields[5].value = _("unknown"); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + nmc_switch_show (nmc, NMC_FIELDS_NM_WIFI, _("Wi-Fi radio switch")); } else { - if (!strcmp (*argv, "on")) - enable_wifi = TRUE; - else if (!strcmp (*argv, "off")) - enable_wifi = FALSE; - else { - g_string_printf (nmc->return_text, _("Error: invalid 'wifi' parameter: '%s'."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto end; - } + if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) + goto finish; + nmc->get_client (nmc); /* create NMClient */ - nm_client_wireless_set_enabled (nmc->client, enable_wifi); + nm_client_wireless_set_enabled (nmc->client, enable_flag); } } else if (matches (*argv, "wwan") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_radio_wwan (); + goto finish; + } if (next_arg (&argc, &argv) != 0) { - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; - /* no argument, show current WWAN state */ - if (nmc->required_fields && strcasecmp (nmc->required_fields, "WWAN")) { - g_string_printf (nmc->return_text, _("Error: '--fields' value '%s' is not valid here; allowed fields: %s"), - nmc->required_fields, NMC_FIELDS_NM_WWAN); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto end; - } - nmc->allowed_fields = nmc_fields_nm_status; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_NM_WWAN, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ADD | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.header_name = _("WWAN enabled"); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ - - if (nmc_is_nm_running (nmc, NULL)) { - nmc->get_client (nmc); /* create NMClient */ - nmc->allowed_fields[7].value = nm_client_wwan_get_enabled (nmc->client) ? _("enabled") : _("disabled"); - } else - nmc->allowed_fields[7].value = _("unknown"); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + /* no argument, show current WWAN (mobile broadband) state */ + nmc_switch_show (nmc, NMC_FIELDS_NM_WWAN, _("WWAN radio switch")); } else { - if (!strcmp (*argv, "on")) - enable_wwan = TRUE; - else if (!strcmp (*argv, "off")) - enable_wwan = FALSE; - else { - g_string_printf (nmc->return_text, _("Error: invalid 'wwan' parameter: '%s'."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto end; - } + if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) + goto finish; + nmc->get_client (nmc); /* create NMClient */ - nm_client_wwan_set_enabled (nmc->client, enable_wwan); + nm_client_wwan_set_enabled (nmc->client, enable_flag); } } #if WITH_WIMAX else if (matches (*argv, "wimax") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_radio_wimax (); + goto finish; + } if (next_arg (&argc, &argv) != 0) { - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; /* no argument, show current WiMAX state */ - if (nmc->required_fields && strcasecmp (nmc->required_fields, "WIMAX")) { - g_string_printf (nmc->return_text, _("Error: '--fields' value '%s' is not valid here; allowed fields: %s"), - nmc->required_fields, NMC_FIELDS_NM_WIMAX); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto end; - } - nmc->allowed_fields = nmc_fields_nm_status; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_NM_WIMAX, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ADD | NMC_PF_FLAG_FIELD_NAMES; - nmc->print_fields.header_name = _("WiMAX enabled"); - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print header */ - - if (nmc_is_nm_running (nmc, NULL)) { - nmc->get_client (nmc); /* create NMClient */ - nmc->allowed_fields[9].value = nm_client_wimax_get_enabled (nmc->client) ? _("enabled") : _("disabled"); - } else - nmc->allowed_fields[9].value = _("unknown"); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + nmc_switch_show (nmc, NMC_FIELDS_NM_WIMAX, _("WiMAX radio switch")); } else { - if (!strcmp (*argv, "on")) - enable_wimax = TRUE; - else if (!strcmp (*argv, "off")) - enable_wimax = FALSE; - else { - g_string_printf (nmc->return_text, _("Error: invalid 'wimax' parameter: '%s'."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto end; - } + if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) + goto finish; + nmc->get_client (nmc); /* create NMClient */ - nm_client_wimax_set_enabled (nmc->client, enable_wimax); + nm_client_wimax_set_enabled (nmc->client, enable_flag); } } #endif - else if ( matches (*argv, "help") == 0 - || (g_str_has_prefix (*argv, "-") && matches ((*argv)+1, "help") == 0) - || (g_str_has_prefix (*argv, "--") && matches ((*argv)+2, "help") == 0)) { - usage (); - } else { - usage (); - g_string_printf (nmc->return_text, _("Error: 'nm' command '%s' is not valid."), *argv); + usage_radio (); + g_string_printf (nmc->return_text, _("Error: 'radio' command '%s' is not valid."), *argv); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; } } -end: +finish: quit (); return nmc->return_value; - -opt_error: - quit (); - g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - g_error_free (error); - return nmc->return_value; } + diff --git a/cli/src/network-manager.h b/cli/src/network-manager.h index 93cc1b07..bccba7ce 100644 --- a/cli/src/network-manager.h +++ b/cli/src/network-manager.h @@ -14,7 +14,7 @@ * 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 Red Hat, Inc. + * (C) Copyright 2010 - 2012 Red Hat, Inc. */ #ifndef NMC_NETWORK_MANAGER_H @@ -22,6 +22,8 @@ #include "nmcli.h" -NMCResultCode do_network_manager (NmCli *nmc, int argc, char **argv); +NMCResultCode do_general (NmCli *nmc, int argc, char **argv); +NMCResultCode do_networking (NmCli *nmc, int argc, char **argv); +NMCResultCode do_radio (NmCli *nmc, int argc, char **argv); #endif /* NMC_NETWORK_MANAGER_H */ diff --git a/cli/src/nmcli.c b/cli/src/nmcli.c index 757ff92b..66288c20 100644 --- a/cli/src/nmcli.c +++ b/cli/src/nmcli.c @@ -16,7 +16,7 @@ * 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 - 2012 Red Hat, Inc. + * (C) Copyright 2010 - 2014 Red Hat, Inc. */ /* Generated configuration file */ @@ -26,6 +26,7 @@ #include <string.h> #include <stdlib.h> #include <signal.h> +#include <pthread.h> #include <locale.h> #include <glib.h> @@ -36,6 +37,7 @@ #include "nmcli.h" #include "utils.h" +#include "common.h" #include "connections.h" #include "devices.h" #include "network-manager.h" @@ -46,6 +48,10 @@ # define NMCLI_VERSION VERSION #endif +/* Global NmCli object */ +// FIXME: Currently, we pass NmCli over in most APIs, but we might refactor +// that and use the global variable directly instead. +NmCli nm_cli; typedef struct { NmCli *nmc; @@ -55,6 +61,7 @@ typedef struct { /* --- Global variables --- */ GMainLoop *loop = NULL; +static sigset_t signal_set; /* Get an error quark for use with GError */ @@ -82,11 +89,15 @@ usage (const char *prog_name) " -f[ields] <field1,field2,...>|all|common specify fields to output\n" " -e[scape] yes|no escape columns separators in values\n" " -n[ocheck] don't check nmcli and NetworkManager versions\n" + " -a[sk] ask for missing parameters\n" + " -w[ait] <seconds> set timeout waiting for finishing operations\n" " -v[ersion] show program version\n" " -h[elp] print this help\n" "\n" "OBJECT\n" - " nm NetworkManager's status\n" + " g[eneral] NetworkManager's general status and operations\n" + " n[etworking] overall networking control\n" + " r[adio] NetworkManager radio switches\n" " c[onnection] NetworkManager's connections\n" " d[evice] devices managed by NetworkManager\n" "\n"), @@ -104,7 +115,9 @@ static const struct cmd { const char *cmd; NMCResultCode (*func) (NmCli *nmc, int argc, char **argv); } nmcli_cmds[] = { - { "nm", do_network_manager }, + { "general", do_general }, + { "networking", do_networking }, + { "radio", do_radio }, { "connection", do_connections }, { "device", do_devices }, { "help", do_help }, @@ -183,9 +196,9 @@ parse_command_line (NmCli *nmc, int argc, char **argv) nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } - if (!strcmp (argv[1], "tabular")) + if (matches (argv[1], "tabular") == 0) nmc->multiline_output = FALSE; - else if (!strcmp (argv[1], "multiline")) + else if (matches (argv[1], "multiline") == 0) nmc->multiline_output = TRUE; else { g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[1], opt); @@ -199,9 +212,9 @@ parse_command_line (NmCli *nmc, int argc, char **argv) nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } - if (!strcmp (argv[1], "yes")) + if (matches (argv[1], "yes") == 0) nmc->escape_values = TRUE; - else if (!strcmp (argv[1], "no")) + else if (matches (argv[1], "no") == 0) nmc->escape_values = FALSE; else { g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[1], opt); @@ -218,6 +231,23 @@ parse_command_line (NmCli *nmc, int argc, char **argv) nmc->required_fields = g_strdup (argv[1]); } else if (matches (opt, "-nocheck") == 0) { nmc->nocheck_ver = TRUE; + } else if (matches (opt, "-ask") == 0) { + nmc->ask = TRUE; + } else if (matches (opt, "-wait") == 0) { + unsigned long timeout; + next_arg (&argc, &argv); + if (argc <= 1) { + g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return nmc->return_value; + } + if (!nmc_string_to_uint (argv[1], TRUE, 0, G_MAXINT, &timeout)) { + g_string_printf (nmc->return_text, _("Error: '%s' is not a valid timeout for '%s' option."), + argv[1], opt); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return nmc->return_value; + } + nmc->timeout = (int) timeout; } else if (matches (opt, "-version") == 0) { printf (_("nmcli tool, version %s\n"), NMCLI_VERSION); return NMC_RESULT_SUCCESS; @@ -240,27 +270,68 @@ parse_command_line (NmCli *nmc, int argc, char **argv) return nmc->return_value; } -static void -signal_handler (int signo) -{ - if (signo == SIGINT || signo == SIGTERM) { - g_message (_("Caught signal %d, shutting down..."), signo); - g_main_loop_quit (loop); +void *signal_handling_thread (void *arg); +/* + * Thread function waiting for signals and processing them. + * Wait for signals in signal set. The semantics of sigwait() require that all + * threads (including the thread calling sigwait()) have the signal masked, for + * reliable operation. Otherwise, a signal that arrives while this thread is + * not blocked in sigwait() might be delivered to another thread. + */ +void * +signal_handling_thread (void *arg) { + int signo; + + while (1) { + sigwait (&signal_set, &signo); + + switch (signo) { + case SIGINT: + case SIGQUIT: + case SIGTERM: + nmc_cleanup_readline (); + printf (_("\nError: nmcli terminated by signal %d."), signo); + exit (1); + break; + default: + break; + } } + return NULL; } -static void +/* + * Mask the signals we are interested in and create a signal handling thread. + * Because all threads inherit the signal mask from their creator, all threads + * in the process will have the signals masked. That's why setup_signals() has + * to be called before creating other threads. + */ +static gboolean setup_signals (void) { - struct sigaction action; - sigset_t mask; - - sigemptyset (&mask); - action.sa_handler = signal_handler; - action.sa_mask = mask; - action.sa_flags = 0; - sigaction (SIGTERM, &action, NULL); - sigaction (SIGINT, &action, NULL); + pthread_t signal_thread_id; + int status; + + sigemptyset (&signal_set); + sigaddset (&signal_set, SIGINT); + sigaddset (&signal_set, SIGQUIT); + sigaddset (&signal_set, SIGTERM); + + /* Block all signals of interest. */ + status = pthread_sigmask (SIG_BLOCK, &signal_set, NULL); + if (status != 0) { + fprintf (stderr, _("Failed to set signal mask: %d"), status); + return FALSE; + } + + /* Create the signal handling thread. */ + status = pthread_create (&signal_thread_id, NULL, signal_handling_thread, NULL); + if (status != 0) { + fprintf (stderr, _("Failed to create signal handling thread: %d"), status); + return FALSE; + } + + return TRUE; } static NMClient * @@ -287,7 +358,7 @@ nmc_init (NmCli *nmc) nmc->return_value = NMC_RESULT_SUCCESS; nmc->return_text = g_string_new (_("Success")); - nmc->timeout = 10; + nmc->timeout = -1; nmc->system_settings = NULL; nmc->system_settings_running = FALSE; @@ -300,9 +371,13 @@ nmc_init (NmCli *nmc) nmc->mode_specified = FALSE; nmc->escape_values = TRUE; nmc->required_fields = NULL; - nmc->allowed_fields = NULL; + nmc->output_data = g_ptr_array_new_full (20, g_free); memset (&nmc->print_fields, '\0', sizeof (NmcPrintFields)); nmc->nocheck_ver = FALSE; + nmc->ask = FALSE; + nmc->editor_status_line = FALSE; + nmc->editor_save_confirmation = TRUE; + nmc->editor_prompt_color = NMC_TERM_COLOR_NORMAL; } static void @@ -316,8 +391,8 @@ nmc_cleanup (NmCli *nmc) g_slist_free (nmc->system_connections); g_free (nmc->required_fields); - if (nmc->print_fields.indices) - g_array_free (nmc->print_fields.indices, TRUE); + nmc_empty_output_fields (nmc); + g_ptr_array_unref (nmc->output_data); } static gboolean @@ -336,8 +411,11 @@ start (gpointer data) int main (int argc, char *argv[]) { - NmCli nmc; - ArgsInfo args_info = { &nmc, argc, argv }; + ArgsInfo args_info = { &nm_cli, argc, argv }; + + /* Set up unix signal handling */ + if (!setup_signals ()) + exit (NMC_RESULT_ERROR_UNKNOWN); /* Set locale to use environment variables */ setlocale (LC_ALL, ""); @@ -349,22 +427,23 @@ main (int argc, char *argv[]) textdomain (GETTEXT_PACKAGE); #endif +#if !GLIB_CHECK_VERSION (2, 35, 0) g_type_init (); +#endif - nmc_init (&nmc); + nmc_init (&nm_cli); g_idle_add (start, &args_info); loop = g_main_loop_new (NULL, FALSE); /* create main loop */ - setup_signals (); /* setup UNIX signals */ g_main_loop_run (loop); /* run main loop */ /* Print result descripting text */ - if (nmc.return_value != NMC_RESULT_SUCCESS) { - fprintf (stderr, "%s\n", nmc.return_text->str); + if (nm_cli.return_value != NMC_RESULT_SUCCESS) { + fprintf (stderr, "%s\n", nm_cli.return_text->str); } g_main_loop_unref (loop); - nmc_cleanup (&nmc); + nmc_cleanup (&nm_cli); - return nmc.return_value; + return nm_cli.return_value; } diff --git a/cli/src/nmcli.h b/cli/src/nmcli.h index 8928126b..b300c6d3 100644 --- a/cli/src/nmcli.h +++ b/cli/src/nmcli.h @@ -55,7 +55,10 @@ typedef enum { NMC_RESULT_ERROR_NM_NOT_RUNNING = 8, /* nmcli and NetworkManager versions mismatch */ - NMC_RESULT_ERROR_VERSIONS_MISMATCH = 9 + NMC_RESULT_ERROR_VERSIONS_MISMATCH = 9, + + /* Connection/Device/AP not found */ + NMC_RESULT_ERROR_NOT_FOUND = 10 } NMCResultCode; typedef enum { @@ -66,33 +69,40 @@ typedef enum { /* === Output fields === */ /* Flags for NmcOutputField */ -#define NMC_OF_FLAG_ARRAY 0x00000001 /* 'value' is an NULL-terminated array rather then a single string */ - -typedef struct { - const char *name; /* Field's name */ - const char *name_l10n; /* Field's name for translation */ - int width; /* Width in screen columns */ - const void *value; /* Value of current field - char* or char** */ - guint32 flags; /* Flags */ +#define NMC_OF_FLAG_FIELD_NAMES 0x00000001 /* Print field names instead of values */ +#define NMC_OF_FLAG_SECTION_PREFIX 0x00000002 /* Use the first value as section prefix for the other field names - just in multiline */ +#define NMC_OF_FLAG_MAIN_HEADER_ADD 0x00000004 /* Print main header in addition to values/field names */ +#define NMC_OF_FLAG_MAIN_HEADER_ONLY 0x00000008 /* Print main header only */ + +typedef struct _NmcOutputField { + const char *name; /* Field's name */ + const char *name_l10n; /* Field's name for translation */ + int width; /* Width in screen columns */ + struct _NmcOutputField *group; /* Points to an array with available section field names if this is a section (group) field */ + void *value; /* Value of current field - char* or char** (NULL-terminated array) */ + gboolean value_is_array; /* Whether value is char** instead of char* */ + gboolean free_value; /* Whether to free the value */ + guint32 flags; /* Flags - whether and how to print values/field names/headers */ } NmcOutputField; -/* Flags for NmcPrintFields */ -#define NMC_PF_FLAG_MULTILINE 0x00000001 /* Multiline output instead of tabular */ -#define NMC_PF_FLAG_TERSE 0x00000002 /* Terse output mode */ -#define NMC_PF_FLAG_PRETTY 0x00000004 /* Pretty output mode */ -#define NMC_PF_FLAG_MAIN_HEADER_ADD 0x00000008 /* Print main header in addition to values/field names */ -#define NMC_PF_FLAG_MAIN_HEADER_ONLY 0x00000010 /* Print main header only */ -#define NMC_PF_FLAG_FIELD_NAMES 0x00000020 /* Print field names instead of values */ -#define NMC_PF_FLAG_ESCAPE 0x00000040 /* Escape column separator and '\' */ -#define NMC_PF_FLAG_SECTION_PREFIX 0x00000080 /* Use the first value as section prefix for the other field names - just in multiline */ - typedef struct { GArray *indices; /* Array of field indices to the array of allowed fields */ char *header_name; /* Name of the output */ int indent; /* Indent by this number of spaces */ - guint32 flags; /* Various flags for controlling output: see NMC_PF_FLAG_* values */ } NmcPrintFields; +typedef enum { + NMC_TERM_COLOR_NORMAL = 0, + NMC_TERM_COLOR_BLACK = 1, + NMC_TERM_COLOR_RED = 2, + NMC_TERM_COLOR_GREEN = 3, + NMC_TERM_COLOR_YELLOW = 4, + NMC_TERM_COLOR_BLUE = 5, + NMC_TERM_COLOR_MAGENTA = 6, + NMC_TERM_COLOR_CYAN = 7, + NMC_TERM_COLOR_WHITE = 8 +} NmcTermColor; + /* NmCli - main structure */ typedef struct _NmCli { NMClient *client; /* Pointer to NMClient of libnm-glib */ @@ -114,9 +124,13 @@ typedef struct _NmCli { gboolean mode_specified; /* Whether tabular/multiline mode was specified via '--mode' option */ gboolean escape_values; /* Whether to escape ':' and '\' in terse tabular mode */ char *required_fields; /* Required fields in output: '--fields' option */ - NmcOutputField *allowed_fields; /* Array of allowed fields for particular commands */ + GPtrArray *output_data; /* GPtrArray of arrays of NmcOutputField structs - accumulates data for output */ NmcPrintFields print_fields; /* Structure with field indices to print */ gboolean nocheck_ver; /* Don't check nmcli and NM versions: option '--nocheck' */ + gboolean ask; /* Ask for missing parameters: option '--ask' */ + gboolean editor_status_line; /* Whether to display status line in connection editor */ + gboolean editor_save_confirmation; /* Whether to ask for confirmation on saving connections with 'autoconnect=yes' */ + NmcTermColor editor_prompt_color; /* Color of prompt in connection editor */ } NmCli; /* Error quark for GError domain */ diff --git a/cli/src/settings.c b/cli/src/settings.c index 1b3d0fd0..0fc708b3 100644 --- a/cli/src/settings.c +++ b/cli/src/settings.c @@ -14,43 +14,50 @@ * 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 - 2012 Red Hat, Inc. + * (C) Copyright 2010 - 2014 Red Hat, Inc. */ #include "config.h" -#include "net/if_arp.h" +#include <net/if_arp.h> +#include <stdio.h> #include <glib.h> #include <glib/gi18n.h> #include <libnm-util/nm-utils.h> #include "utils.h" +#include "common.h" #include "settings.h" +/* Forward declarations */ +static char *wep_key_type_to_string (NMWepKeyType type); /* Helper macro to define fields */ -#define SETTING_FIELD(setting, width) { setting, N_(setting), width, NULL, 0 } +#define SETTING_FIELD(setting, width) { setting, N_(setting), width, NULL, FALSE, FALSE, 0 } /* Available fields for NM_SETTING_CONNECTION_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_connection[] = { - SETTING_FIELD ("name", 15), /* 0 */ - SETTING_FIELD (NM_SETTING_CONNECTION_ID, 25), /* 1 */ - SETTING_FIELD (NM_SETTING_CONNECTION_UUID, 38), /* 2 */ - SETTING_FIELD (NM_SETTING_CONNECTION_TYPE, 17), /* 3 */ - SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT, 13), /* 4 */ - SETTING_FIELD (NM_SETTING_CONNECTION_TIMESTAMP, 10), /* 5 */ - SETTING_FIELD (NM_SETTING_CONNECTION_READ_ONLY, 10), /* 6 */ - SETTING_FIELD (NM_SETTING_CONNECTION_PERMISSIONS, 30), /* 7 */ - SETTING_FIELD (NM_SETTING_CONNECTION_ZONE, 10), /* 8 */ - SETTING_FIELD (NM_SETTING_CONNECTION_MASTER, 20), /* 9 */ - SETTING_FIELD (NM_SETTING_CONNECTION_SLAVE_TYPE, 20), /* 10 */ - SETTING_FIELD (NM_SETTING_CONNECTION_SECONDARIES, 40), /* 11 */ - {NULL, NULL, 0, NULL, 0} +NmcOutputField nmc_fields_setting_connection[] = { + SETTING_FIELD ("name", 15), /* 0 */ + SETTING_FIELD (NM_SETTING_CONNECTION_ID, 25), /* 1 */ + SETTING_FIELD (NM_SETTING_CONNECTION_UUID, 38), /* 2 */ + SETTING_FIELD (NM_SETTING_CONNECTION_INTERFACE_NAME, 20), /* 3 */ + SETTING_FIELD (NM_SETTING_CONNECTION_TYPE, 17), /* 4 */ + SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT, 13), /* 5 */ + SETTING_FIELD (NM_SETTING_CONNECTION_TIMESTAMP, 10), /* 6 */ + SETTING_FIELD (NM_SETTING_CONNECTION_READ_ONLY, 10), /* 7 */ + SETTING_FIELD (NM_SETTING_CONNECTION_PERMISSIONS, 30), /* 8 */ + SETTING_FIELD (NM_SETTING_CONNECTION_ZONE, 10), /* 9 */ + SETTING_FIELD (NM_SETTING_CONNECTION_MASTER, 20), /* 10 */ + SETTING_FIELD (NM_SETTING_CONNECTION_SLAVE_TYPE, 20), /* 11 */ + SETTING_FIELD (NM_SETTING_CONNECTION_SECONDARIES, 40), /* 12 */ + SETTING_FIELD (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, 30), /* 13 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_CONNECTION_ALL "name"","\ NM_SETTING_CONNECTION_ID","\ NM_SETTING_CONNECTION_UUID","\ + NM_SETTING_CONNECTION_INTERFACE_NAME","\ NM_SETTING_CONNECTION_TYPE","\ NM_SETTING_CONNECTION_AUTOCONNECT","\ NM_SETTING_CONNECTION_TIMESTAMP","\ @@ -59,11 +66,12 @@ static NmcOutputField nmc_fields_setting_connection[] = { NM_SETTING_CONNECTION_ZONE","\ NM_SETTING_CONNECTION_MASTER","\ NM_SETTING_CONNECTION_SLAVE_TYPE","\ - NM_SETTING_CONNECTION_SECONDARIES + NM_SETTING_CONNECTION_SECONDARIES","\ + NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT #define NMC_FIELDS_SETTING_CONNECTION_COMMON NMC_FIELDS_SETTING_CONNECTION_ALL /* Available fields for NM_SETTING_WIRED_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_wired[] = { +NmcOutputField nmc_fields_setting_wired[] = { SETTING_FIELD ("name", 17), /* 0 */ SETTING_FIELD (NM_SETTING_WIRED_PORT, 8), /* 1 */ SETTING_FIELD (NM_SETTING_WIRED_SPEED, 10), /* 2 */ @@ -76,7 +84,7 @@ static NmcOutputField nmc_fields_setting_wired[] = { SETTING_FIELD (NM_SETTING_WIRED_S390_SUBCHANNELS, 20), /* 9 */ SETTING_FIELD (NM_SETTING_WIRED_S390_NETTYPE, 15), /* 10 */ SETTING_FIELD (NM_SETTING_WIRED_S390_OPTIONS, 20), /* 11 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_WIRED_ALL "name"","\ NM_SETTING_WIRED_PORT","\ @@ -93,7 +101,7 @@ static NmcOutputField nmc_fields_setting_wired[] = { #define NMC_FIELDS_SETTING_WIRED_COMMON NMC_FIELDS_SETTING_WIRED_ALL /* Available fields for NM_SETTING_802_1X_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_8021X[] = { +NmcOutputField nmc_fields_setting_8021X[] = { SETTING_FIELD ("name", 10), /* 0 */ SETTING_FIELD (NM_SETTING_802_1X_EAP, 10), /* 1 */ SETTING_FIELD (NM_SETTING_802_1X_IDENTITY, 15), /* 2 */ @@ -127,7 +135,7 @@ static NmcOutputField nmc_fields_setting_8021X[] = { SETTING_FIELD (NM_SETTING_802_1X_PIN, 8), /* 30 */ SETTING_FIELD (NM_SETTING_802_1X_PIN_FLAGS, 20), /* 31 */ SETTING_FIELD (NM_SETTING_802_1X_SYSTEM_CA_CERTS, 17), /* 32 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_802_1X_ALL "name"","\ NM_SETTING_802_1X_EAP","\ @@ -165,7 +173,7 @@ static NmcOutputField nmc_fields_setting_8021X[] = { #define NMC_FIELDS_SETTING_802_1X_COMMON NMC_FIELDS_SETTING_802_1X_ALL /* Available fields for NM_SETTING_WIRELESS_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_wireless[] = { +NmcOutputField nmc_fields_setting_wireless[] = { SETTING_FIELD ("name", 17), /* 0 */ SETTING_FIELD (NM_SETTING_WIRELESS_SSID, 34), /* 1 */ SETTING_FIELD (NM_SETTING_WIRELESS_MODE, 15), /* 2 */ @@ -179,9 +187,8 @@ static NmcOutputField nmc_fields_setting_wireless[] = { SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST, 39), /* 10 */ SETTING_FIELD (NM_SETTING_WIRELESS_MTU, 6), /* 11 */ SETTING_FIELD (NM_SETTING_WIRELESS_SEEN_BSSIDS, 35), /* 12 */ - SETTING_FIELD (NM_SETTING_WIRELESS_SEC, 25), /* 13 */ - SETTING_FIELD (NM_SETTING_WIRELESS_HIDDEN, 10), /* 14 */ - {NULL, NULL, 0, NULL, 0} + SETTING_FIELD (NM_SETTING_WIRELESS_HIDDEN, 10), /* 13 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_WIRELESS_ALL "name"","\ NM_SETTING_WIRELESS_SSID","\ @@ -196,12 +203,11 @@ static NmcOutputField nmc_fields_setting_wireless[] = { NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST","\ NM_SETTING_WIRELESS_MTU","\ NM_SETTING_WIRELESS_SEEN_BSSIDS","\ - NM_SETTING_WIRELESS_SEC","\ NM_SETTING_WIRELESS_HIDDEN #define NMC_FIELDS_SETTING_WIRELESS_COMMON NMC_FIELDS_SETTING_WIRELESS_ALL /* Available fields for NM_SETTING_WIRELESS_SECURITY_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_wireless_security[] = { +NmcOutputField nmc_fields_setting_wireless_security[] = { SETTING_FIELD ("name", 25), /* 0 */ SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, 10), /* 1 */ SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, 15), /* 2 */ @@ -220,7 +226,7 @@ static NmcOutputField nmc_fields_setting_wireless_security[] = { SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS, 20), /* 15 */ SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, 15), /* 16 */ SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, 20), /* 17 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_WIRELESS_SECURITY_ALL "name"","\ NM_SETTING_WIRELESS_SECURITY_KEY_MGMT","\ @@ -243,7 +249,7 @@ static NmcOutputField nmc_fields_setting_wireless_security[] = { #define NMC_FIELDS_SETTING_WIRELESS_SECURITY_COMMON NMC_FIELDS_SETTING_WIRELESS_SECURITY_ALL /* Available fields for NM_SETTING_IP4_CONFIG_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_ip4_config[] = { +NmcOutputField nmc_fields_setting_ip4_config[] = { SETTING_FIELD ("name", 8), /* 0 */ SETTING_FIELD (NM_SETTING_IP4_CONFIG_METHOD, 10), /* 1 */ SETTING_FIELD (NM_SETTING_IP4_CONFIG_DNS, 20), /* 2 */ @@ -257,7 +263,7 @@ static NmcOutputField nmc_fields_setting_ip4_config[] = { SETTING_FIELD (NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, 14), /* 10 */ SETTING_FIELD (NM_SETTING_IP4_CONFIG_NEVER_DEFAULT, 15), /* 11 */ SETTING_FIELD (NM_SETTING_IP4_CONFIG_MAY_FAIL, 12), /* 12 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_IP4_CONFIG_ALL "name"","\ NM_SETTING_IP4_CONFIG_METHOD","\ @@ -275,7 +281,7 @@ static NmcOutputField nmc_fields_setting_ip4_config[] = { #define NMC_FIELDS_SETTING_IP4_CONFIG_COMMON NMC_FIELDS_SETTING_IP4_CONFIG_ALL /* Available fields for NM_SETTING_IP6_CONFIG_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_ip6_config[] = { +NmcOutputField nmc_fields_setting_ip6_config[] = { SETTING_FIELD ("name", 8), /* 0 */ SETTING_FIELD (NM_SETTING_IP6_CONFIG_METHOD, 10), /* 1 */ SETTING_FIELD (NM_SETTING_IP6_CONFIG_DNS, 20), /* 2 */ @@ -288,7 +294,7 @@ static NmcOutputField nmc_fields_setting_ip6_config[] = { SETTING_FIELD (NM_SETTING_IP6_CONFIG_MAY_FAIL, 12), /* 9 */ SETTING_FIELD (NM_SETTING_IP6_CONFIG_IP6_PRIVACY, 15), /* 10 */ SETTING_FIELD (NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME, 14), /* 11 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_IP6_CONFIG_ALL "name"","\ NM_SETTING_IP6_CONFIG_METHOD","\ @@ -305,14 +311,14 @@ static NmcOutputField nmc_fields_setting_ip6_config[] = { #define NMC_FIELDS_SETTING_IP6_CONFIG_COMMON NMC_FIELDS_SETTING_IP4_CONFIG_ALL /* Available fields for NM_SETTING_SERIAL_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_serial[] = { +NmcOutputField nmc_fields_setting_serial[] = { SETTING_FIELD ("name", 10), /* 0 */ SETTING_FIELD (NM_SETTING_SERIAL_BAUD, 10), /* 1 */ SETTING_FIELD (NM_SETTING_SERIAL_BITS, 10), /* 2 */ SETTING_FIELD (NM_SETTING_SERIAL_PARITY, 10), /* 3 */ SETTING_FIELD (NM_SETTING_SERIAL_STOPBITS, 10), /* 4 */ SETTING_FIELD (NM_SETTING_SERIAL_SEND_DELAY, 12), /* 5 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_SERIAL_ALL "name"","\ NM_SETTING_SERIAL_BAUD","\ @@ -323,7 +329,7 @@ static NmcOutputField nmc_fields_setting_serial[] = { #define NMC_FIELDS_SETTING_SERIAL_COMMON NMC_FIELDS_SETTING_SERIAL_ALL /* Available fields for NM_SETTING_PPP_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_ppp[] = { +NmcOutputField nmc_fields_setting_ppp[] = { SETTING_FIELD ("name", 10), /* 0 */ SETTING_FIELD (NM_SETTING_PPP_NOAUTH, 10), /* 1 */ SETTING_FIELD (NM_SETTING_PPP_REFUSE_EAP, 10), /* 2 */ @@ -343,7 +349,7 @@ static NmcOutputField nmc_fields_setting_ppp[] = { SETTING_FIELD (NM_SETTING_PPP_MTU, 10), /* 16 */ SETTING_FIELD (NM_SETTING_PPP_LCP_ECHO_FAILURE, 17), /* 17 */ SETTING_FIELD (NM_SETTING_PPP_LCP_ECHO_INTERVAL, 18), /* 18 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_PPP_ALL "name"","\ NM_SETTING_PPP_NOAUTH","\ @@ -367,13 +373,13 @@ static NmcOutputField nmc_fields_setting_ppp[] = { #define NMC_FIELDS_SETTING_PPP_COMMON NMC_FIELDS_SETTING_PPP_ALL /* Available fields for NM_SETTING_PPPOE_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_pppoe[] = { +NmcOutputField nmc_fields_setting_pppoe[] = { SETTING_FIELD ("name", 10), /* 0 */ SETTING_FIELD (NM_SETTING_PPPOE_SERVICE, 12), /* 1 */ SETTING_FIELD (NM_SETTING_PPPOE_USERNAME, 15), /* 2 */ SETTING_FIELD (NM_SETTING_PPPOE_PASSWORD, 15), /* 3 */ SETTING_FIELD (NM_SETTING_PPPOE_PASSWORD_FLAGS, 20), /* 4 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_PPPOE_ALL "name"","\ NM_SETTING_PPPOE_SERVICE","\ @@ -383,7 +389,7 @@ static NmcOutputField nmc_fields_setting_pppoe[] = { #define NMC_FIELDS_SETTING_PPPOE_COMMON NMC_FIELDS_SETTING_PPPOE_ALL /* Available fields for NM_SETTING_ADSL_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_adsl[] = { +NmcOutputField nmc_fields_setting_adsl[] = { SETTING_FIELD ("name", 10), /* 0 */ SETTING_FIELD (NM_SETTING_ADSL_USERNAME, 15), /* 1 */ SETTING_FIELD (NM_SETTING_ADSL_PASSWORD, 15), /* 2 */ @@ -392,7 +398,7 @@ static NmcOutputField nmc_fields_setting_adsl[] = { SETTING_FIELD (NM_SETTING_ADSL_ENCAPSULATION, 10), /* 5 */ SETTING_FIELD (NM_SETTING_ADSL_VPI, 10), /* 6 */ SETTING_FIELD (NM_SETTING_ADSL_VCI, 10), /* 7 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_ADSL_ALL "name"","\ NM_SETTING_ADSL_USERNAME","\ @@ -405,7 +411,7 @@ static NmcOutputField nmc_fields_setting_adsl[] = { #define NMC_FIELDS_SETTING_ADSL_COMMON NMC_FIELDS_SETTING_ADSL_ALL /* Available fields for NM_SETTING_GSM_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_gsm[] = { +NmcOutputField nmc_fields_setting_gsm[] = { SETTING_FIELD ("name", 10), /* 0 */ SETTING_FIELD (NM_SETTING_GSM_NUMBER, 10), /* 1 */ SETTING_FIELD (NM_SETTING_GSM_USERNAME, 15), /* 2 */ @@ -418,7 +424,7 @@ static NmcOutputField nmc_fields_setting_gsm[] = { SETTING_FIELD (NM_SETTING_GSM_PIN, 10), /* 9 */ SETTING_FIELD (NM_SETTING_GSM_PIN_FLAGS, 20), /* 10 */ SETTING_FIELD (NM_SETTING_GSM_HOME_ONLY, 10), /* 11 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_GSM_ALL "name"","\ NM_SETTING_GSM_NUMBER","\ @@ -435,13 +441,13 @@ static NmcOutputField nmc_fields_setting_gsm[] = { #define NMC_FIELDS_SETTING_GSM_COMMON NMC_FIELDS_SETTING_GSM_ALL /* Available fields for NM_SETTING_CDMA_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_cdma[] = { +NmcOutputField nmc_fields_setting_cdma[] = { SETTING_FIELD ("name", 10), /* 0 */ SETTING_FIELD (NM_SETTING_CDMA_NUMBER, 15), /* 1 */ SETTING_FIELD (NM_SETTING_CDMA_USERNAME, 15), /* 2 */ SETTING_FIELD (NM_SETTING_CDMA_PASSWORD, 15), /* 3 */ SETTING_FIELD (NM_SETTING_CDMA_PASSWORD_FLAGS, 20), /* 4 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_CDMA_ALL "name"","\ NM_SETTING_CDMA_NUMBER","\ @@ -451,11 +457,11 @@ static NmcOutputField nmc_fields_setting_cdma[] = { #define NMC_FIELDS_SETTING_CDMA_COMMON NMC_FIELDS_SETTING_CDMA_ALL /* Available fields for NM_SETTING_BLUETOOTH_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_bluetooth[] = { +NmcOutputField nmc_fields_setting_bluetooth[] = { SETTING_FIELD ("name", 11), /* 0 */ SETTING_FIELD (NM_SETTING_BLUETOOTH_BDADDR, 19), /* 1 */ SETTING_FIELD (NM_SETTING_BLUETOOTH_TYPE, 10), /* 2 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_BLUETOOTH_ALL "name"","\ NM_SETTING_BLUETOOTH_BDADDR","\ @@ -463,12 +469,12 @@ static NmcOutputField nmc_fields_setting_bluetooth[] = { #define NMC_FIELDS_SETTING_BLUETOOTH_COMMON NMC_FIELDS_SETTING_BLUETOOTH_ALL /* Available fields for NM_SETTING_OLPC_MESH_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_olpc_mesh[] = { +NmcOutputField nmc_fields_setting_olpc_mesh[] = { SETTING_FIELD ("name", 18), /* 0 */ SETTING_FIELD (NM_SETTING_OLPC_MESH_SSID, 34), /* 1 */ SETTING_FIELD (NM_SETTING_OLPC_MESH_CHANNEL, 12), /* 2 */ SETTING_FIELD (NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, 17), /* 3 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_OLPC_MESH_ALL "name"","\ NM_SETTING_OLPC_MESH_SSID","\ @@ -477,13 +483,13 @@ static NmcOutputField nmc_fields_setting_olpc_mesh[] = { #define NMC_FIELDS_SETTING_OLPC_MESH_COMMON NMC_FIELDS_SETTING_OLPC_MESH_ALL /* Available fields for NM_SETTING_VPN_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_vpn[] = { +NmcOutputField nmc_fields_setting_vpn[] = { SETTING_FIELD ("name", 6), /* 0 */ SETTING_FIELD (NM_SETTING_VPN_SERVICE_TYPE, 40), /* 1 */ SETTING_FIELD (NM_SETTING_VPN_USER_NAME, 12), /* 2 */ SETTING_FIELD (NM_SETTING_VPN_DATA, 30), /* 3 */ SETTING_FIELD (NM_SETTING_VPN_SECRETS, 15), /* 4 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_VPN_ALL "name"","\ NM_SETTING_VPN_SERVICE_TYPE","\ @@ -493,11 +499,11 @@ static NmcOutputField nmc_fields_setting_vpn[] = { #define NMC_FIELDS_SETTING_VPN_COMMON NMC_FIELDS_SETTING_VPN_ALL /* Available fields for NM_SETTING_WIMAX_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_wimax[] = { +NmcOutputField nmc_fields_setting_wimax[] = { SETTING_FIELD ("name", 6), /* 0 */ SETTING_FIELD (NM_SETTING_WIMAX_MAC_ADDRESS, 19), /* 1 */ SETTING_FIELD (NM_SETTING_WIMAX_NETWORK_NAME, 40), /* 2 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_WIMAX_ALL "name"","\ NM_SETTING_WIMAX_MAC_ADDRESS","\ @@ -505,25 +511,29 @@ static NmcOutputField nmc_fields_setting_wimax[] = { #define NMC_FIELDS_SETTING_WIMAX_COMMON NMC_FIELDS_SETTING_WIMAX_ALL /* Available fields for NM_SETTING_INFINIBAND_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_infiniband[] = { +NmcOutputField nmc_fields_setting_infiniband[] = { SETTING_FIELD ("name", 12), /* 0 */ SETTING_FIELD (NM_SETTING_INFINIBAND_MAC_ADDRESS, 61), /* 1 */ SETTING_FIELD (NM_SETTING_INFINIBAND_MTU, 6), /* 2 */ SETTING_FIELD (NM_SETTING_INFINIBAND_TRANSPORT_MODE, 12), /* 3 */ - {NULL, NULL, 0, NULL, 0} + SETTING_FIELD (NM_SETTING_INFINIBAND_P_KEY, 6), /* 4 */ + SETTING_FIELD (NM_SETTING_INFINIBAND_PARENT, 16), /* 5 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_INFINIBAND_ALL "name"","\ NM_SETTING_INFINIBAND_MAC_ADDRESS","\ - NM_SETTING_INFINIBAND_MTU","\ - NM_SETTING_INFINIBAND_TRANSPORT_MODE -#define NMC_FIELDS_SETTING_INFINIBAND_COMMON NMC_FIELDS_SETTING_INFINIBAND_ALL + NM_SETTING_INFINIBAND_MTU"," \ + NM_SETTING_INFINIBAND_TRANSPORT_MODE"," \ + NM_SETTING_INFINIBAND_P_KEY"," \ + NM_SETTING_INFINIBAND_PARENT +#define NMC_FIELDS_SETTING_INFINIBAND_COMMON NMC_FIELDS_SETTING_INFINIBAND_ALL \ /* Available fields for NM_SETTING_BOND_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_bond[] = { +NmcOutputField nmc_fields_setting_bond[] = { SETTING_FIELD ("name", 8), /* 0 */ SETTING_FIELD (NM_SETTING_BOND_INTERFACE_NAME, 15), /* 1 */ SETTING_FIELD (NM_SETTING_BOND_OPTIONS, 30), /* 2 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_BOND_ALL "name"","\ NM_SETTING_BOND_INTERFACE_NAME","\ @@ -531,7 +541,7 @@ static NmcOutputField nmc_fields_setting_bond[] = { #define NMC_FIELDS_SETTING_BOND_COMMON NMC_FIELDS_SETTING_BOND_ALL /* Available fields for NM_SETTING_VLAN_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_vlan[] = { +NmcOutputField nmc_fields_setting_vlan[] = { SETTING_FIELD ("name", 6), /* 0 */ SETTING_FIELD (NM_SETTING_VLAN_INTERFACE_NAME, 15), /* 1 */ SETTING_FIELD (NM_SETTING_VLAN_PARENT, 8), /* 2 */ @@ -539,7 +549,7 @@ static NmcOutputField nmc_fields_setting_vlan[] = { SETTING_FIELD (NM_SETTING_VLAN_FLAGS, 45), /* 4 */ SETTING_FIELD (NM_SETTING_VLAN_INGRESS_PRIORITY_MAP, 22), /* 5 */ SETTING_FIELD (NM_SETTING_VLAN_EGRESS_PRIORITY_MAP, 22), /* 6 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_VLAN_ALL "name"","\ NM_SETTING_VLAN_INTERFACE_NAME","\ @@ -551,19 +561,21 @@ static NmcOutputField nmc_fields_setting_vlan[] = { #define NMC_FIELDS_SETTING_VLAN_COMMON NMC_FIELDS_SETTING_VLAN_ALL /* Available fields for NM_SETTING_BRIDGE_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_bridge[] = { +NmcOutputField nmc_fields_setting_bridge[] = { SETTING_FIELD ("name", 8), /* 0 */ SETTING_FIELD (NM_SETTING_BRIDGE_INTERFACE_NAME, 15), /* 1 */ - SETTING_FIELD (NM_SETTING_BRIDGE_STP, 5), /* 2 */ - SETTING_FIELD (NM_SETTING_BRIDGE_PRIORITY, 6), /* 3 */ - SETTING_FIELD (NM_SETTING_BRIDGE_FORWARD_DELAY, 6), /* 4 */ - SETTING_FIELD (NM_SETTING_BRIDGE_HELLO_TIME, 6), /* 5 */ - SETTING_FIELD (NM_SETTING_BRIDGE_MAX_AGE, 6), /* 6 */ - SETTING_FIELD (NM_SETTING_BRIDGE_AGEING_TIME, 6), /* 7 */ - {NULL, NULL, 0, NULL, 0} + SETTING_FIELD (NM_SETTING_BRIDGE_MAC_ADDRESS, 19), /* 2 */ + SETTING_FIELD (NM_SETTING_BRIDGE_STP, 5), /* 3 */ + SETTING_FIELD (NM_SETTING_BRIDGE_PRIORITY, 6), /* 4 */ + SETTING_FIELD (NM_SETTING_BRIDGE_FORWARD_DELAY, 6), /* 5 */ + SETTING_FIELD (NM_SETTING_BRIDGE_HELLO_TIME, 6), /* 6 */ + SETTING_FIELD (NM_SETTING_BRIDGE_MAX_AGE, 6), /* 7 */ + SETTING_FIELD (NM_SETTING_BRIDGE_AGEING_TIME, 6), /* 8 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_BRIDGE_ALL "name"","\ NM_SETTING_BRIDGE_INTERFACE_NAME","\ + NM_SETTING_BRIDGE_MAC_ADDRESS","\ NM_SETTING_BRIDGE_STP","\ NM_SETTING_BRIDGE_PRIORITY","\ NM_SETTING_BRIDGE_FORWARD_DELAY","\ @@ -573,12 +585,12 @@ static NmcOutputField nmc_fields_setting_bridge[] = { #define NMC_FIELDS_SETTING_BRIDGE_COMMON NMC_FIELDS_SETTING_BRIDGE_ALL /* Available fields for NM_SETTING_BRIDGE_PORT_SETTING_NAME */ -static NmcOutputField nmc_fields_setting_bridge_port[] = { +NmcOutputField nmc_fields_setting_bridge_port[] = { SETTING_FIELD ("name", 8), /* 0 */ SETTING_FIELD (NM_SETTING_BRIDGE_PORT_PRIORITY, 10), /* 1 */ SETTING_FIELD (NM_SETTING_BRIDGE_PORT_PATH_COST, 12), /* 2 */ SETTING_FIELD (NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, 15), /* 3 */ - {NULL, NULL, 0, NULL, 0} + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_BRIDGE_PORT_ALL "name"","\ NM_SETTING_BRIDGE_PORT_PRIORITY","\ @@ -586,15 +598,76 @@ static NmcOutputField nmc_fields_setting_bridge_port[] = { NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE #define NMC_FIELDS_SETTING_BRIDGE_PORT_COMMON NMC_FIELDS_SETTING_BRIDGE_PORT_ALL +/* Available fields for NM_SETTING_TEAM_SETTING_NAME */ +NmcOutputField nmc_fields_setting_team[] = { + SETTING_FIELD ("name", 8), /* 0 */ + SETTING_FIELD (NM_SETTING_TEAM_INTERFACE_NAME, 15), /* 1 */ + SETTING_FIELD (NM_SETTING_TEAM_CONFIG, 30), /* 2 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_TEAM_ALL "name"","\ + NM_SETTING_TEAM_INTERFACE_NAME","\ + NM_SETTING_TEAM_CONFIG +#define NMC_FIELDS_SETTING_TEAM_COMMON NMC_FIELDS_SETTING_TEAM_ALL + +/* Available fields for NM_SETTING_TEAM_PORT_SETTING_NAME */ +NmcOutputField nmc_fields_setting_team_port[] = { + SETTING_FIELD ("name", 8), /* 0 */ + SETTING_FIELD (NM_SETTING_TEAM_PORT_CONFIG, 30), /* 1 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_TEAM_PORT_ALL "name"","\ + NM_SETTING_TEAM_PORT_CONFIG +#define NMC_FIELDS_SETTING_TEAM_PORT_COMMON NMC_FIELDS_SETTING_TEAM_PORT_ALL + +/* Available fields for NM_SETTING_DCB_SETTING_NAME */ +NmcOutputField nmc_fields_setting_dcb[] = { + SETTING_FIELD ("name", 8), /* 0 */ + SETTING_FIELD (NM_SETTING_DCB_APP_FCOE_FLAGS, 5), /* 1 */ + SETTING_FIELD (NM_SETTING_DCB_APP_FCOE_PRIORITY, 5), /* 2 */ + SETTING_FIELD (NM_SETTING_DCB_APP_FCOE_MODE, 8), /* 3 */ + SETTING_FIELD (NM_SETTING_DCB_APP_ISCSI_FLAGS, 5), /* 4 */ + SETTING_FIELD (NM_SETTING_DCB_APP_ISCSI_PRIORITY, 5), /* 5 */ + SETTING_FIELD (NM_SETTING_DCB_APP_FIP_FLAGS, 5), /* 6 */ + SETTING_FIELD (NM_SETTING_DCB_APP_FIP_PRIORITY, 5), /* 7 */ + SETTING_FIELD (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, 5), /* 8 */ + SETTING_FIELD (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL, 10), /* 9 */ + SETTING_FIELD (NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, 5), /* 10 */ + SETTING_FIELD (NM_SETTING_DCB_PRIORITY_GROUP_ID, 10), /* 11 */ + SETTING_FIELD (NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH, 30), /* 12 */ + SETTING_FIELD (NM_SETTING_DCB_PRIORITY_BANDWIDTH, 30), /* 13 */ + SETTING_FIELD (NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH, 10), /* 14 */ + SETTING_FIELD (NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS, 30), /* 15 */ + {NULL, NULL, 0, NULL, FALSE, FALSE, 0} +}; +#define NMC_FIELDS_SETTING_DCB_ALL "name"","\ + NM_SETTING_DCB_APP_FCOE_FLAGS","\ + NM_SETTING_DCB_APP_FCOE_PRIORITY","\ + NM_SETTING_DCB_APP_FCOE_MODE","\ + NM_SETTING_DCB_APP_ISCSI_FLAGS","\ + NM_SETTING_DCB_APP_ISCSI_PRIORITY","\ + NM_SETTING_DCB_APP_FIP_FLAGS","\ + NM_SETTING_DCB_APP_FIP_PRIORITY","\ + NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS","\ + NM_SETTING_DCB_PRIORITY_FLOW_CONTROL","\ + NM_SETTING_DCB_PRIORITY_GROUP_FLAGS","\ + NM_SETTING_DCB_PRIORITY_GROUP_ID","\ + NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH","\ + NM_SETTING_DCB_PRIORITY_BANDWIDTH","\ + NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH","\ + NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS +#define NMC_FIELDS_SETTING_DCB_COMMON NMC_FIELDS_SETTING_DCB_ALL + +/*----------------------------------------------------------------------------*/ static char * wep_key_type_to_string (NMWepKeyType type) { switch (type) { case NM_WEP_KEY_TYPE_KEY: - return g_strdup_printf (_("%d (hex-ascii-key)"), type); + return g_strdup_printf (_("%d (key)"), type); case NM_WEP_KEY_TYPE_PASSPHRASE: - return g_strdup_printf (_("%d (104/128-bit passphrase)"), type); + return g_strdup_printf (_("%d (passphrase)"), type); case NM_WEP_KEY_TYPE_UNKNOWN: default: return g_strdup_printf (_("%d (unknown)"), type); @@ -658,7 +731,7 @@ allowed_bands_to_string (guint32 bands) g_string_append (band_str, _("WCDMA 3GPP UMTS 2600 MHz, ")); if (band_str->str[band_str->len-1] == '(') - g_string_assign (band_str, _("unknown")); + g_string_append (band_str, _("unknown")); else g_string_truncate (band_str, band_str->len-2); /* chop off trailing ', ' */ @@ -686,7 +759,7 @@ vlan_flags_to_string (guint32 flags) g_string_append (flag_str, _("LOOSE_BINDING, ")); if (flag_str->str[flag_str->len-1] == '(') - g_string_assign (flag_str, _("unknown")); + g_string_append (flag_str, _("unknown")); else g_string_truncate (flag_str, flag_str->len-2); /* chop off trailing ', ' */ @@ -748,7 +821,7 @@ secret_flags_to_string (guint32 flags) g_string_append (flag_str, _("not required, ")); if (flag_str->str[flag_str->len-1] == '(') - g_string_assign (flag_str, _("unknown")); + g_string_append (flag_str, _("unknown")); else g_string_truncate (flag_str, flag_str->len-2); /* chop off trailing ', ' */ @@ -757,1335 +830,6504 @@ secret_flags_to_string (guint32 flags) return g_string_free (flag_str, FALSE); } -/*----------------------------------------------------------------------------*/ - -gboolean -setting_details (NMSetting *setting, NmCli *nmc) +static void +vpn_data_item (const char *key, const char *value, gpointer user_data) { - GType setting_type; + GString *ret_str = (GString *) user_data; - g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + if (ret_str->len != 0) + g_string_append (ret_str, ", "); - setting_type = G_OBJECT_TYPE (setting); - - if (setting_type == NM_TYPE_SETTING_CONNECTION) - return setting_connection_details (NM_SETTING_CONNECTION (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_WIRED) - return setting_wired_details (NM_SETTING_WIRED (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_802_1X) - return setting_802_1X_details (NM_SETTING_802_1X (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_WIRELESS) - return setting_wireless_details (NM_SETTING_WIRELESS (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_WIRELESS_SECURITY) - return setting_wireless_security_details (NM_SETTING_WIRELESS_SECURITY (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_IP4_CONFIG) - return setting_ip4_config_details (NM_SETTING_IP4_CONFIG (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_IP6_CONFIG) - return setting_ip6_config_details (NM_SETTING_IP6_CONFIG (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_SERIAL) - return setting_serial_details (NM_SETTING_SERIAL (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_PPP) - return setting_ppp_details (NM_SETTING_PPP (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_PPPOE) - return setting_pppoe_details (NM_SETTING_PPPOE (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_GSM) - return setting_gsm_details (NM_SETTING_GSM (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_CDMA) - return setting_cdma_details (NM_SETTING_CDMA (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_BLUETOOTH) - return setting_bluetooth_details (NM_SETTING_BLUETOOTH (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_OLPC_MESH) - return setting_olpc_mesh_details (NM_SETTING_OLPC_MESH (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_VPN) - return setting_vpn_details (NM_SETTING_VPN (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_WIMAX) - return setting_wimax_details (NM_SETTING_WIMAX (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_INFINIBAND) - return setting_infiniband_details (NM_SETTING_INFINIBAND (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_BOND) - return setting_bond_details (NM_SETTING_BOND (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_VLAN) - return setting_vlan_details (NM_SETTING_VLAN (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_ADSL) - return setting_adsl_details (NM_SETTING_ADSL (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_BRIDGE) - return setting_bridge_details (NM_SETTING_BRIDGE (setting), nmc); - else if (setting_type == NM_TYPE_SETTING_BRIDGE_PORT) - return setting_bridge_port_details (NM_SETTING_BRIDGE_PORT (setting), nmc); - else - /* should not be reached */ - return FALSE; + g_string_append_printf (ret_str, "%s = %s", key, value); } -gboolean -setting_connection_details (NMSettingConnection *s_con, NmCli *nmc) -{ - guint64 timestamp; - char *timestamp_str; - const char *perm_item; - const char *perm_type; - GString *perm = NULL; - GString *secondaries = NULL; - int i; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; - - g_return_val_if_fail (NM_IS_SETTING_CONNECTION (s_con), FALSE); - nmc->allowed_fields = nmc_fields_setting_connection; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_CONNECTION_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ - - timestamp = nm_setting_connection_get_timestamp (s_con); - timestamp_str = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp); - - /* get permissions */ - perm = g_string_new (NULL); - for (i = 0; i < nm_setting_connection_get_num_permissions (s_con); i++) { - nm_setting_connection_get_permission (s_con, i, &perm_type, &perm_item, NULL); - g_string_append_printf (perm, "%s:%s,", perm_type, perm_item); +/* === property get functions === */ +#define DEFINE_GETTER(func_name, property_name) \ + static char * \ + func_name (NMSetting *setting) \ + { \ + char *s; \ + GValue val = G_VALUE_INIT; \ + g_value_init (&val, G_TYPE_STRING); \ + g_object_get_property (G_OBJECT (setting), property_name, &val); \ + /* Getters return allocated values, and returning the string \ + * the GValue copied from the object without unsetting the \ + * GValue fulfills that requirement. */ \ + s = g_value_dup_string (&val); \ + g_value_unset (&val); \ + return s; \ } - if (perm->len > 0) - g_string_truncate (perm, perm->len-1); /* remove trailing , */ - /* get secondaries */ - secondaries = g_string_new (NULL); - for (i = 0; i < nm_setting_connection_get_num_secondaries (s_con); i++) { - const char *sec_uuid = nm_setting_connection_get_secondary (s_con, i); - g_string_append_printf (secondaries, "%s,", sec_uuid); +#define DEFINE_SECRET_FLAGS_GETTER(func_name, property_name) \ + static char * \ + func_name (NMSetting *setting) \ + { \ + guint v; \ + GValue val = G_VALUE_INIT; \ + g_value_init (&val, G_TYPE_UINT); \ + g_object_get_property (G_OBJECT (setting), property_name, &val); \ + v = g_value_get_uint (&val); \ + g_value_unset (&val); \ + return secret_flags_to_string (v); \ } - if (secondaries->len > 0) - g_string_truncate (secondaries, secondaries->len-1); /* remove trailing , */ - - nmc->allowed_fields[0].value = NM_SETTING_CONNECTION_SETTING_NAME; - nmc->allowed_fields[1].value = nm_setting_connection_get_id (s_con); - nmc->allowed_fields[2].value = nm_setting_connection_get_uuid (s_con); - nmc->allowed_fields[3].value = nm_setting_connection_get_connection_type (s_con); - nmc->allowed_fields[4].value = nm_setting_connection_get_autoconnect (s_con) ? _("yes") : _("no"); - nmc->allowed_fields[5].value = timestamp_str; - nmc->allowed_fields[6].value = nm_setting_connection_get_read_only (s_con) ? ("yes") : _("no"); - nmc->allowed_fields[7].value = perm->str; - nmc->allowed_fields[8].value = nm_setting_connection_get_zone (s_con); - nmc->allowed_fields[9].value = nm_setting_connection_get_master (s_con); - nmc->allowed_fields[10].value = nm_setting_connection_get_slave_type (s_con); - nmc->allowed_fields[11].value = secondaries->str; - - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ - - g_free (timestamp_str); - g_string_free (perm, TRUE); - g_string_free (secondaries, TRUE); - return TRUE; -} - -gboolean -setting_wired_details (NMSettingWired *s_wired, NmCli *nmc) -{ - const GByteArray *mac; - const GSList *iter; - const GPtrArray *s390_channels; - int i; - char *speed_str, *mtu_str, *device_mac_str = NULL, *cloned_mac_str = NULL; - GString *mac_blacklist_s, *s390_channels_s, *s390_options_s; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; - - g_return_val_if_fail (NM_IS_SETTING_WIRED (s_wired), FALSE); - - nmc->allowed_fields = nmc_fields_setting_wired; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_WIRED_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ - - speed_str = g_strdup_printf ("%d", nm_setting_wired_get_speed (s_wired)); - mtu_str = g_strdup_printf ("%d", nm_setting_wired_get_mtu (s_wired)); - mac = nm_setting_wired_get_mac_address (s_wired); - if (mac) - device_mac_str = nm_utils_hwaddr_ntoa (mac->data, ARPHRD_ETHER); - mac = nm_setting_wired_get_cloned_mac_address (s_wired); - if (mac) - cloned_mac_str = nm_utils_hwaddr_ntoa (mac->data, ARPHRD_ETHER); - - mac_blacklist_s = g_string_new (NULL); - iter = nm_setting_wired_get_mac_address_blacklist (s_wired); - while (iter) { - g_string_append_printf (mac_blacklist_s, "%s,", (char *) iter->data); - iter = iter->next; - } - g_string_truncate (mac_blacklist_s, mac_blacklist_s->len-1); /* chop off trailing ',' */ - - s390_channels_s = g_string_new (NULL); - s390_channels = nm_setting_wired_get_s390_subchannels (s_wired); - for (i = 0; s390_channels && i < s390_channels->len; i++) - g_string_append_printf (s390_channels_s, "%s,", (char *) g_ptr_array_index (s390_channels, i)); - g_string_truncate (s390_channels_s, s390_channels_s->len-1); /* chop off trailing ',' */ - - s390_options_s = g_string_new (NULL); - for (i = 0; i < nm_setting_wired_get_num_s390_options (s_wired); i++) { - const char *key, *value; - - nm_setting_wired_get_s390_option (s_wired, i, &key, &value); - g_string_append_printf (s390_options_s, "%s=%s,", key, value); +#define DEFINE_HWADDR_GETTER(func_name, property_name) \ + static char * \ + func_name (NMSetting *setting) \ + { \ + GValue val = G_VALUE_INIT; \ + GArray *array; \ + char *hwaddr = NULL; \ + g_value_init (&val, DBUS_TYPE_G_UCHAR_ARRAY); \ + g_object_get_property (G_OBJECT (setting), property_name, &val); \ + array = g_value_get_boxed (&val); \ + if (array && array->len) \ + hwaddr = nm_utils_hwaddr_ntoa_len (array->data, array->len); \ + g_value_unset (&val); \ + return hwaddr; \ } - g_string_truncate (s390_options_s, s390_options_s->len-1); /* chop off trailing ',' */ - - nmc->allowed_fields[0].value = NM_SETTING_WIRED_SETTING_NAME; - nmc->allowed_fields[1].value = nm_setting_wired_get_port (s_wired); - nmc->allowed_fields[2].value = speed_str; - nmc->allowed_fields[3].value = nm_setting_wired_get_duplex (s_wired); - nmc->allowed_fields[4].value = nm_setting_wired_get_auto_negotiate (s_wired) ? _("yes") : _("no"); - nmc->allowed_fields[5].value = device_mac_str; - nmc->allowed_fields[6].value = cloned_mac_str; - nmc->allowed_fields[7].value = mac_blacklist_s->str; - nmc->allowed_fields[8].value = strcmp (mtu_str, "0") ? mtu_str : _("auto"); - nmc->allowed_fields[9].value = s390_channels_s->str; - nmc->allowed_fields[10].value = nm_setting_wired_get_s390_nettype (s_wired); - nmc->allowed_fields[11].value = s390_options_s->str; - - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ - g_free (speed_str); - g_free (device_mac_str); - g_free (cloned_mac_str); - g_free (mtu_str); - g_string_free (mac_blacklist_s, TRUE); - g_string_free (s390_channels_s, TRUE); - g_string_free (s390_options_s, TRUE); - - return TRUE; -} +/* --- NM_SETTING_802_1X_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_802_1X_get_eap, NM_SETTING_802_1X_EAP) +DEFINE_GETTER (nmc_property_802_1X_get_identity, NM_SETTING_802_1X_IDENTITY) +DEFINE_GETTER (nmc_property_802_1X_get_anonymous_identity, NM_SETTING_802_1X_ANONYMOUS_IDENTITY) +DEFINE_GETTER (nmc_property_802_1X_get_pac_file, NM_SETTING_802_1X_PAC_FILE) +DEFINE_GETTER (nmc_property_802_1X_get_ca_path, NM_SETTING_802_1X_CA_PATH) +DEFINE_GETTER (nmc_property_802_1X_get_subject_match, NM_SETTING_802_1X_SUBJECT_MATCH) +DEFINE_GETTER (nmc_property_802_1X_get_altsubject_matches, NM_SETTING_802_1X_ALTSUBJECT_MATCHES) +DEFINE_GETTER (nmc_property_802_1X_get_phase1_peapver, NM_SETTING_802_1X_PHASE1_PEAPVER) +DEFINE_GETTER (nmc_property_802_1X_get_phase1_peaplabel, NM_SETTING_802_1X_PHASE1_PEAPLABEL) +DEFINE_GETTER (nmc_property_802_1X_get_phase1_fast_provisioning, NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING) +DEFINE_GETTER (nmc_property_802_1X_get_phase2_auth, NM_SETTING_802_1X_PHASE2_AUTH) +DEFINE_GETTER (nmc_property_802_1X_get_phase2_autheap, NM_SETTING_802_1X_PHASE2_AUTHEAP) +DEFINE_GETTER (nmc_property_802_1X_get_phase2_ca_path, NM_SETTING_802_1X_PHASE2_CA_PATH) +DEFINE_GETTER (nmc_property_802_1X_get_phase2_subject_match, NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH) +DEFINE_GETTER (nmc_property_802_1X_get_phase2_altsubject_matches, NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES) +DEFINE_GETTER (nmc_property_802_1X_get_password, NM_SETTING_802_1X_PASSWORD) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_802_1X_get_password_flags, NM_SETTING_802_1X_PASSWORD_FLAGS) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_802_1X_get_password_raw_flags, NM_SETTING_802_1X_PASSWORD_RAW_FLAGS) +DEFINE_GETTER (nmc_property_802_1X_get_private_key_password, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_802_1X_get_private_key_password_flags, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS) +DEFINE_GETTER (nmc_property_802_1X_get_phase2_private_key_password, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_802_1X_get_phase2_private_key_password_flags, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS) +DEFINE_GETTER (nmc_property_802_1X_get_pin, NM_SETTING_802_1X_PIN) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_802_1X_get_pin_flags, NM_SETTING_802_1X_PIN_FLAGS) +DEFINE_GETTER (nmc_property_802_1X_get_system_ca_certs, NM_SETTING_802_1X_SYSTEM_CA_CERTS) -gboolean -setting_802_1X_details (NMSetting8021x *s_8021X, NmCli *nmc) +static char * +nmc_property_802_1X_get_ca_cert (NMSetting *setting) { + NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting); NMSetting8021xCKScheme scheme; - GString *eap_str, *alt_sub_match, *phase2_alt_sub_match; - char *ca_cert_str = NULL, *client_cert_str = NULL, *phase2_ca_cert_str = NULL; - char *phase2_client_cert_str = NULL, *private_key_str = NULL, *phase2_private_key_str = NULL, *pswd_raw_str = NULL; - char *pswd_flags, *pswd_raw_flags, *priv_key_pswd_flags, *phase2_priv_key_pswd_flags, *pin_flags; - int i; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (s_8021X), FALSE); + char *ca_cert_str = NULL; - nmc->allowed_fields = nmc_fields_setting_8021X; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_802_1X_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ - - eap_str = g_string_new (NULL); - for (i = 0; i < nm_setting_802_1x_get_num_eap_methods (s_8021X); i++) { - if (i > 0) - g_string_append_c (eap_str, ','); - g_string_append (eap_str, nm_setting_802_1x_get_eap_method (s_8021X, i)); - } scheme = nm_setting_802_1x_get_ca_cert_scheme (s_8021X); if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) ca_cert_str = byte_array_to_string (nm_setting_802_1x_get_ca_cert_blob (s_8021X)); if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) ca_cert_str = g_strdup (nm_setting_802_1x_get_ca_cert_path (s_8021X)); + return ca_cert_str; +} + +static char * +nmc_property_802_1X_get_client_cert (NMSetting *setting) +{ + NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting); + NMSetting8021xCKScheme scheme; + char *client_cert_str = NULL; + scheme = nm_setting_802_1x_get_client_cert_scheme (s_8021X); if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) client_cert_str = byte_array_to_string (nm_setting_802_1x_get_client_cert_blob (s_8021X)); if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) client_cert_str = g_strdup (nm_setting_802_1x_get_client_cert_path (s_8021X)); + return client_cert_str; +} + +static char * +nmc_property_802_1X_get_phase2_ca_cert (NMSetting *setting) +{ + NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting); + NMSetting8021xCKScheme scheme; + char *phase2_ca_cert_str = NULL; + scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (s_8021X); if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) phase2_ca_cert_str = byte_array_to_string (nm_setting_802_1x_get_phase2_ca_cert_blob (s_8021X)); if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) phase2_ca_cert_str = g_strdup (nm_setting_802_1x_get_phase2_ca_cert_path (s_8021X)); + return phase2_ca_cert_str; +} + +static char * +nmc_property_802_1X_get_phase2_client_cert (NMSetting *setting) +{ + NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting); + NMSetting8021xCKScheme scheme; + char *phase2_client_cert_str = NULL; + scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (s_8021X); if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) phase2_client_cert_str = byte_array_to_string (nm_setting_802_1x_get_phase2_client_cert_blob (s_8021X)); if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) phase2_client_cert_str = g_strdup (nm_setting_802_1x_get_phase2_client_cert_path (s_8021X)); + return phase2_client_cert_str; +} + +static char * +nmc_property_802_1X_get_password_raw (NMSetting *setting) +{ + NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting); + return byte_array_to_string (nm_setting_802_1x_get_password_raw (s_8021X)); +} + +static char * +nmc_property_802_1X_get_private_key (NMSetting *setting) +{ + NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting); + NMSetting8021xCKScheme scheme; + char *private_key_str = NULL; + scheme = nm_setting_802_1x_get_private_key_scheme (s_8021X); if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) private_key_str = byte_array_to_string (nm_setting_802_1x_get_private_key_blob (s_8021X)); if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) private_key_str = g_strdup (nm_setting_802_1x_get_private_key_path (s_8021X)); + return private_key_str; +} + +static char * +nmc_property_802_1X_get_phase2_private_key (NMSetting *setting) +{ + NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting); + NMSetting8021xCKScheme scheme; + char *phase2_private_key_str = NULL; + scheme = nm_setting_802_1x_get_phase2_private_key_scheme (s_8021X); if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) phase2_private_key_str = byte_array_to_string (nm_setting_802_1x_get_phase2_private_key_blob (s_8021X)); if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) phase2_private_key_str = g_strdup (nm_setting_802_1x_get_phase2_private_key_path (s_8021X)); - alt_sub_match = g_string_new (NULL); - for (i = 0; i < nm_setting_802_1x_get_num_altsubject_matches (s_8021X); i++) - g_string_append_printf (alt_sub_match, "%s,", nm_setting_802_1x_get_altsubject_match (s_8021X, i)); - g_string_truncate (alt_sub_match, alt_sub_match->len-1); /* chop off trailing ',' */ - - phase2_alt_sub_match = g_string_new (NULL); - for (i = 0; i < nm_setting_802_1x_get_num_phase2_altsubject_matches (s_8021X); i++) - g_string_append_printf (phase2_alt_sub_match, "%s,", nm_setting_802_1x_get_phase2_altsubject_match (s_8021X, i)); - g_string_truncate (phase2_alt_sub_match, phase2_alt_sub_match->len-1); /* chop off trailing ',' */ - - pswd_raw_str = byte_array_to_string (nm_setting_802_1x_get_password_raw (s_8021X)); - - pswd_flags = secret_flags_to_string (nm_setting_802_1x_get_password_flags (s_8021X)); - pswd_raw_flags = secret_flags_to_string (nm_setting_802_1x_get_password_raw_flags (s_8021X)); - priv_key_pswd_flags = secret_flags_to_string (nm_setting_802_1x_get_private_key_password_flags (s_8021X)); - phase2_priv_key_pswd_flags = secret_flags_to_string (nm_setting_802_1x_get_phase2_private_key_password_flags (s_8021X)); - pin_flags = secret_flags_to_string (nm_setting_802_1x_get_pin_flags (s_8021X)); - - nmc->allowed_fields[0].value = NM_SETTING_802_1X_SETTING_NAME; - nmc->allowed_fields[1].value = eap_str->str; - nmc->allowed_fields[2].value = nm_setting_802_1x_get_identity (s_8021X); - nmc->allowed_fields[3].value = nm_setting_802_1x_get_anonymous_identity (s_8021X); - nmc->allowed_fields[4].value = nm_setting_802_1x_get_pac_file (s_8021X); - nmc->allowed_fields[5].value = ca_cert_str; - nmc->allowed_fields[6].value = nm_setting_802_1x_get_ca_path (s_8021X); - nmc->allowed_fields[7].value = nm_setting_802_1x_get_subject_match (s_8021X); - nmc->allowed_fields[8].value = alt_sub_match->str; - nmc->allowed_fields[9].value = client_cert_str; - nmc->allowed_fields[10].value = nm_setting_802_1x_get_phase1_peapver (s_8021X); - nmc->allowed_fields[11].value = nm_setting_802_1x_get_phase1_peaplabel (s_8021X); - nmc->allowed_fields[12].value = nm_setting_802_1x_get_phase1_fast_provisioning (s_8021X); - nmc->allowed_fields[13].value = nm_setting_802_1x_get_phase2_auth (s_8021X); - nmc->allowed_fields[14].value = nm_setting_802_1x_get_phase2_autheap (s_8021X); - nmc->allowed_fields[15].value = phase2_ca_cert_str; - nmc->allowed_fields[16].value = nm_setting_802_1x_get_phase2_ca_path (s_8021X); - nmc->allowed_fields[17].value = nm_setting_802_1x_get_phase2_subject_match (s_8021X); - nmc->allowed_fields[18].value = phase2_alt_sub_match->str; - nmc->allowed_fields[19].value = phase2_client_cert_str; - nmc->allowed_fields[20].value = nm_setting_802_1x_get_password (s_8021X); - nmc->allowed_fields[21].value = pswd_flags; - nmc->allowed_fields[22].value = pswd_raw_str; - nmc->allowed_fields[23].value = pswd_raw_flags; - nmc->allowed_fields[24].value = private_key_str; - nmc->allowed_fields[25].value = nm_setting_802_1x_get_private_key_password (s_8021X); - nmc->allowed_fields[26].value = priv_key_pswd_flags; - nmc->allowed_fields[27].value = phase2_private_key_str; - nmc->allowed_fields[28].value = nm_setting_802_1x_get_phase2_private_key_password (s_8021X); - nmc->allowed_fields[29].value = phase2_priv_key_pswd_flags; - nmc->allowed_fields[30].value = nm_setting_802_1x_get_pin (s_8021X); - nmc->allowed_fields[31].value = pin_flags; - nmc->allowed_fields[32].value = nm_setting_802_1x_get_system_ca_certs (s_8021X) ? _("yes") : _("no"); - - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ - - g_free (ca_cert_str); - g_free (client_cert_str); - g_free (phase2_ca_cert_str); - g_free (phase2_client_cert_str); - g_free (private_key_str); - g_free (phase2_private_key_str); - g_free (pswd_flags); - g_free (pswd_raw_str); - g_free (pswd_raw_flags); - g_free (priv_key_pswd_flags); - g_free (phase2_priv_key_pswd_flags); - g_free (pin_flags); - g_string_free (eap_str, TRUE); - g_string_free (alt_sub_match, TRUE); - g_string_free (phase2_alt_sub_match, TRUE); + return phase2_private_key_str; +} - return TRUE; +/* --- NM_SETTING_ADSL_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_adsl_get_username, NM_SETTING_ADSL_USERNAME) +DEFINE_GETTER (nmc_property_adsl_get_password, NM_SETTING_ADSL_PASSWORD) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_adsl_get_password_flags, NM_SETTING_ADSL_PASSWORD_FLAGS) +DEFINE_GETTER (nmc_property_adsl_get_protocol, NM_SETTING_ADSL_PROTOCOL) +DEFINE_GETTER (nmc_property_adsl_get_encapsulation, NM_SETTING_ADSL_ENCAPSULATION) +DEFINE_GETTER (nmc_property_adsl_get_vpi, NM_SETTING_ADSL_VPI) +DEFINE_GETTER (nmc_property_adsl_get_vci, NM_SETTING_ADSL_VCI) + +/* --- NM_SETTING_BLUETOOTH_SETTING_NAME property get functions --- */ +DEFINE_HWADDR_GETTER (nmc_property_bluetooth_get_bdaddr, NM_SETTING_BLUETOOTH_BDADDR) +DEFINE_GETTER (nmc_property_bluetooth_get_type, NM_SETTING_BLUETOOTH_TYPE) + +/* --- NM_SETTING_BOND_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_bond_get_interface_name, NM_SETTING_BOND_INTERFACE_NAME) + +static char * +nmc_property_bond_get_options (NMSetting *setting) +{ + NMSettingBond *s_bond = NM_SETTING_BOND (setting); + GString *bond_options_s; + int i; + + bond_options_s = g_string_new (NULL); + for (i = 0; i < nm_setting_bond_get_num_options (s_bond); i++) { + const char *key, *value; + + nm_setting_bond_get_option (s_bond, i, &key, &value); + g_string_append_printf (bond_options_s, "%s=%s,", key, value); + } + g_string_truncate (bond_options_s, bond_options_s->len-1); /* chop off trailing ',' */ + + return g_string_free (bond_options_s, FALSE); } -gboolean -setting_wireless_details (NMSettingWireless *s_wireless, NmCli *nmc) +/* --- NM_SETTING_BRIDGE_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_bridge_get_interface_name, NM_SETTING_BRIDGE_INTERFACE_NAME) +DEFINE_HWADDR_GETTER (nmc_property_bridge_get_mac_address, NM_SETTING_BRIDGE_MAC_ADDRESS) +DEFINE_GETTER (nmc_property_bridge_get_stp, NM_SETTING_BRIDGE_STP) +DEFINE_GETTER (nmc_property_bridge_get_priority, NM_SETTING_BRIDGE_PRIORITY) +DEFINE_GETTER (nmc_property_bridge_get_forward_delay, NM_SETTING_BRIDGE_FORWARD_DELAY) +DEFINE_GETTER (nmc_property_bridge_get_hello_time, NM_SETTING_BRIDGE_HELLO_TIME) +DEFINE_GETTER (nmc_property_bridge_get_max_age, NM_SETTING_BRIDGE_MAX_AGE) +DEFINE_GETTER (nmc_property_bridge_get_ageing_time, NM_SETTING_BRIDGE_AGEING_TIME) + +/* --- NM_SETTING_BRIDGE_PORT_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_bridge_port_get_priority, NM_SETTING_BRIDGE_PORT_PRIORITY) +DEFINE_GETTER (nmc_property_bridge_port_get_path_cost, NM_SETTING_BRIDGE_PORT_PATH_COST) +DEFINE_GETTER (nmc_property_bridge_port_get_hairpin_mode, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE) + +/* --- NM_SETTING_TEAM_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_team_get_interface_name, NM_SETTING_TEAM_INTERFACE_NAME) +DEFINE_GETTER (nmc_property_team_get_config, NM_SETTING_TEAM_CONFIG) + +/* --- NM_SETTING_TEAM_PORT_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_team_port_get_config, NM_SETTING_TEAM_PORT_CONFIG) + +/* --- NM_SETTING_CDMA_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_cdma_get_number, NM_SETTING_CDMA_NUMBER) +DEFINE_GETTER (nmc_property_cdma_get_username, NM_SETTING_CDMA_USERNAME) +DEFINE_GETTER (nmc_property_cdma_get_password, NM_SETTING_CDMA_PASSWORD) + +DEFINE_SECRET_FLAGS_GETTER (nmc_property_cdma_get_password_flags, NM_SETTING_CDMA_PASSWORD_FLAGS) + +/* --- NM_SETTING_CONNECTION_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_connection_get_id, NM_SETTING_CONNECTION_ID) +DEFINE_GETTER (nmc_property_connection_get_uuid, NM_SETTING_CONNECTION_UUID) +DEFINE_GETTER (nmc_property_connection_get_interface_name, NM_SETTING_CONNECTION_INTERFACE_NAME) +DEFINE_GETTER (nmc_property_connection_get_type, NM_SETTING_CONNECTION_TYPE) +DEFINE_GETTER (nmc_property_connection_get_autoconnect, NM_SETTING_CONNECTION_AUTOCONNECT) +DEFINE_GETTER (nmc_property_connection_get_timestamp, NM_SETTING_CONNECTION_TIMESTAMP) +DEFINE_GETTER (nmc_property_connection_get_read_only, NM_SETTING_CONNECTION_READ_ONLY) + +static char * +nmc_property_connection_get_permissions (NMSetting *setting) { + NMSettingConnection *s_con = NM_SETTING_CONNECTION (setting); + GString *perm = NULL; + const char *perm_item; + const char *perm_type; int i; - const GByteArray *ssid, *bssid, *mac; - char *channel_str, *rate_str, *tx_power_str, *mtu_str; - char *device_mac_str = NULL, *cloned_mac_str = NULL, *ssid_str = NULL, *bssid_str = NULL; - GString *mac_blacklist, *seen_bssids; - const GSList *iter; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; - g_return_val_if_fail (NM_IS_SETTING_WIRELESS (s_wireless), FALSE); + perm = g_string_new (NULL); + for (i = 0; i < nm_setting_connection_get_num_permissions (s_con); i++) { + if (nm_setting_connection_get_permission (s_con, i, &perm_type, &perm_item, NULL)) + g_string_append_printf (perm, "%s:%s,", perm_type, perm_item); + } + if (perm->len > 0) + g_string_truncate (perm, perm->len-1); /* remove trailing , */ + + return g_string_free (perm, FALSE); +} + +DEFINE_GETTER (nmc_property_connection_get_zone, NM_SETTING_CONNECTION_ZONE) +DEFINE_GETTER (nmc_property_connection_get_master, NM_SETTING_CONNECTION_MASTER) +DEFINE_GETTER (nmc_property_connection_get_slave_type, NM_SETTING_CONNECTION_SLAVE_TYPE) +DEFINE_GETTER (nmc_property_connection_get_secondaries, NM_SETTING_CONNECTION_SECONDARIES) +DEFINE_GETTER (nmc_property_connection_get_gateway_ping_timeout, NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT) + +/* --- NM_SETTING_DCB_SETTING_NAME property get functions --- */ +static char * +dcb_flags_to_string (NMSettingDcbFlags flags) +{ + GString *flag_str; + + if (flags == 0) + return g_strdup (_("0 (disabled)")); + + flag_str = g_string_new (NULL); + g_string_printf (flag_str, "%d (", flags); + + if (flags & NM_SETTING_DCB_FLAG_ENABLE) + g_string_append (flag_str, _("enabled, ")); + if (flags & NM_SETTING_DCB_FLAG_ADVERTISE) + g_string_append (flag_str, _("advertise, ")); + if (flags & NM_SETTING_DCB_FLAG_WILLING) + g_string_append (flag_str, _("willing, ")); + + if (flag_str->str[flag_str->len-1] == '(') + g_string_append (flag_str, _("unknown")); + else + g_string_truncate (flag_str, flag_str->len-2); /* chop off trailing ', ' */ + + g_string_append_c (flag_str, ')'); + + return g_string_free (flag_str, FALSE); +} + +#define DEFINE_DCB_FLAGS_GETTER(func_name, property_name) \ + static char * \ + func_name (NMSetting *setting) \ + { \ + guint v; \ + GValue val = G_VALUE_INIT; \ + g_value_init (&val, G_TYPE_UINT); \ + g_object_get_property (G_OBJECT (setting), property_name, &val); \ + v = g_value_get_uint (&val); \ + g_value_unset (&val); \ + return dcb_flags_to_string (v); \ + } + +static char * +dcb_app_priority_to_string (gint priority) +{ + return (priority == -1) ? g_strdup (_("-1 (unset)")) : g_strdup_printf ("%d", priority); +} + +#define DEFINE_DCB_APP_PRIORITY_GETTER(func_name, property_name) \ + static char * \ + func_name (NMSetting *setting) \ + { \ + int v; \ + GValue val = G_VALUE_INIT; \ + g_value_init (&val, G_TYPE_INT); \ + g_object_get_property (G_OBJECT (setting), property_name, &val); \ + v = g_value_get_int (&val); \ + g_value_unset (&val); \ + return dcb_app_priority_to_string (v); \ + } - nmc->allowed_fields = nmc_fields_setting_wireless; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_WIRELESS_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ +#define DEFINE_DCB_BOOL_GETTER(func_name, getter_func_name) \ + static char * \ + func_name (NMSetting *setting) \ + { \ + NMSettingDcb *s_dcb = NM_SETTING_DCB (setting); \ + GString *str; \ + guint i; \ + \ + str = g_string_new (NULL); \ + for (i = 0; i < 8; i++) { \ + if (getter_func_name (s_dcb, i)) \ + g_string_append_c (str, '1'); \ + else \ + g_string_append_c (str, '0'); \ +\ + if (i < 7) \ + g_string_append_c (str, ','); \ + } \ +\ + return g_string_free (str, FALSE); \ + } + +#define DEFINE_DCB_UINT_GETTER(func_name, getter_func_name) \ + static char * \ + func_name (NMSetting *setting) \ + { \ + NMSettingDcb *s_dcb = NM_SETTING_DCB (setting); \ + GString *str; \ + guint i; \ + \ + str = g_string_new (NULL); \ + for (i = 0; i < 8; i++) { \ + g_string_append_printf (str, "%u", getter_func_name (s_dcb, i)); \ + if (i < 7) \ + g_string_append_c (str, ','); \ + } \ +\ + return g_string_free (str, FALSE); \ + } + +DEFINE_DCB_FLAGS_GETTER (nmc_property_dcb_get_app_fcoe_flags, NM_SETTING_DCB_APP_FCOE_FLAGS) +DEFINE_DCB_APP_PRIORITY_GETTER (nmc_property_dcb_get_app_fcoe_priority, NM_SETTING_DCB_APP_FCOE_PRIORITY) +DEFINE_GETTER (nmc_property_dcb_get_app_fcoe_mode, NM_SETTING_DCB_APP_FCOE_MODE) +DEFINE_DCB_FLAGS_GETTER (nmc_property_dcb_get_app_iscsi_flags, NM_SETTING_DCB_APP_ISCSI_FLAGS) +DEFINE_DCB_APP_PRIORITY_GETTER (nmc_property_dcb_get_app_iscsi_priority, NM_SETTING_DCB_APP_ISCSI_PRIORITY) +DEFINE_DCB_FLAGS_GETTER (nmc_property_dcb_get_app_fip_flags, NM_SETTING_DCB_APP_FIP_FLAGS) +DEFINE_DCB_APP_PRIORITY_GETTER (nmc_property_dcb_get_app_fip_priority, NM_SETTING_DCB_APP_FIP_PRIORITY) + +DEFINE_DCB_FLAGS_GETTER (nmc_property_dcb_get_pfc_flags, NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS) +DEFINE_DCB_BOOL_GETTER (nmc_property_dcb_get_pfc, nm_setting_dcb_get_priority_flow_control) + +DEFINE_DCB_FLAGS_GETTER (nmc_property_dcb_get_pg_flags, NM_SETTING_DCB_PRIORITY_GROUP_FLAGS) +DEFINE_DCB_UINT_GETTER (nmc_property_dcb_get_pg_group_id, nm_setting_dcb_get_priority_group_id) +DEFINE_DCB_UINT_GETTER (nmc_property_dcb_get_pg_group_bandwidth, nm_setting_dcb_get_priority_group_bandwidth) +DEFINE_DCB_UINT_GETTER (nmc_property_dcb_get_pg_bandwidth, nm_setting_dcb_get_priority_bandwidth) +DEFINE_DCB_BOOL_GETTER (nmc_property_dcb_get_pg_strict, nm_setting_dcb_get_priority_strict_bandwidth) +DEFINE_DCB_UINT_GETTER (nmc_property_dcb_get_pg_traffic_class, nm_setting_dcb_get_priority_traffic_class) + +/* --- NM_SETTING_GSM_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_gsm_get_number, NM_SETTING_GSM_NUMBER) +DEFINE_GETTER (nmc_property_gsm_get_username, NM_SETTING_GSM_USERNAME) +DEFINE_GETTER (nmc_property_gsm_get_password, NM_SETTING_GSM_PASSWORD) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_gsm_get_password_flags, NM_SETTING_GSM_PASSWORD_FLAGS) +DEFINE_GETTER (nmc_property_gsm_get_apn, NM_SETTING_GSM_APN) +DEFINE_GETTER (nmc_property_gsm_get_network_id, NM_SETTING_GSM_NETWORK_ID) +DEFINE_GETTER (nmc_property_gsm_get_network_type, NM_SETTING_GSM_NETWORK_TYPE) + +static char * +nmc_property_gsm_get_allowed_bands (NMSetting *setting) +{ + NMSettingGsm *s_gsm = NM_SETTING_GSM (setting); +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + return allowed_bands_to_string (nm_setting_gsm_get_allowed_bands (s_gsm)); +G_GNUC_END_IGNORE_DEPRECATIONS +} + +DEFINE_GETTER (nmc_property_gsm_get_pin, NM_SETTING_GSM_PIN) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_gsm_get_pin_flags, NM_SETTING_GSM_PIN_FLAGS) +DEFINE_GETTER (nmc_property_gsm_get_home_only, NM_SETTING_GSM_HOME_ONLY) + +/* --- NM_SETTING_INFINIBAND_SETTING_NAME property get functions --- */ +DEFINE_HWADDR_GETTER (nmc_property_ib_get_mac_address, NM_SETTING_INFINIBAND_MAC_ADDRESS) +DEFINE_GETTER (nmc_property_ib_get_transport_mode, NM_SETTING_INFINIBAND_TRANSPORT_MODE) + +static char * +nmc_property_ib_get_mtu (NMSetting *setting) +{ + NMSettingInfiniband *s_infiniband = NM_SETTING_INFINIBAND (setting); + int mtu; + + mtu = nm_setting_infiniband_get_mtu (s_infiniband); + if (mtu == 0) + return g_strdup (_("auto")); + else + return g_strdup_printf ("%d", mtu); +} + +static char * +nmc_property_ib_get_p_key (NMSetting *setting) +{ + NMSettingInfiniband *s_infiniband = NM_SETTING_INFINIBAND (setting); + int p_key; + + p_key = nm_setting_infiniband_get_p_key (s_infiniband); + if (p_key == -1) + return g_strdup (_("default")); + else + return g_strdup_printf ("0x%04x", p_key); +} + +DEFINE_GETTER (nmc_property_ib_get_parent, NM_SETTING_INFINIBAND_PARENT) + +/* --- NM_SETTING_IP4_CONFIG_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_ipv4_get_method, NM_SETTING_IP4_CONFIG_METHOD) +DEFINE_GETTER (nmc_property_ipv4_get_dns, NM_SETTING_IP4_CONFIG_DNS) +DEFINE_GETTER (nmc_property_ipv4_get_dns_search, NM_SETTING_IP4_CONFIG_DNS_SEARCH) +DEFINE_GETTER (nmc_property_ipv4_get_addresses, NM_SETTING_IP4_CONFIG_ADDRESSES) +DEFINE_GETTER (nmc_property_ipv4_get_routes, NM_SETTING_IP4_CONFIG_ROUTES) +DEFINE_GETTER (nmc_property_ipv4_get_ignore_auto_routes, NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES) +DEFINE_GETTER (nmc_property_ipv4_get_ignore_auto_dns, NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS) +DEFINE_GETTER (nmc_property_ipv4_get_dhcp_client_id, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID) +DEFINE_GETTER (nmc_property_ipv4_get_dhcp_send_hostname, NM_SETTING_IP4_CONFIG_DHCP_SEND_HOSTNAME) +DEFINE_GETTER (nmc_property_ipv4_get_dhcp_hostname, NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME) +DEFINE_GETTER (nmc_property_ipv4_get_never_default, NM_SETTING_IP4_CONFIG_NEVER_DEFAULT) +DEFINE_GETTER (nmc_property_ipv4_get_may_fail, NM_SETTING_IP4_CONFIG_MAY_FAIL) + +/* --- NM_SETTING_IP6_CONFIG_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_ipv6_get_method, NM_SETTING_IP6_CONFIG_METHOD) +DEFINE_GETTER (nmc_property_ipv6_get_dns, NM_SETTING_IP6_CONFIG_DNS) +DEFINE_GETTER (nmc_property_ipv6_get_dns_search, NM_SETTING_IP6_CONFIG_DNS_SEARCH) +DEFINE_GETTER (nmc_property_ipv6_get_addresses, NM_SETTING_IP6_CONFIG_ADDRESSES) +DEFINE_GETTER (nmc_property_ipv6_get_routes, NM_SETTING_IP6_CONFIG_ROUTES) +DEFINE_GETTER (nmc_property_ipv6_get_ignore_auto_routes, NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES) +DEFINE_GETTER (nmc_property_ipv6_get_ignore_auto_dns, NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS) +DEFINE_GETTER (nmc_property_ipv6_get_never_default, NM_SETTING_IP6_CONFIG_NEVER_DEFAULT) +DEFINE_GETTER (nmc_property_ipv6_get_may_fail, NM_SETTING_IP6_CONFIG_MAY_FAIL) +DEFINE_GETTER (nmc_property_ipv6_get_dhcp_hostname, NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME) + +static char * +nmc_property_ipv6_get_ip6_privacy (NMSetting *setting) +{ + NMSettingIP6Config *s_ip6 = NM_SETTING_IP6_CONFIG (setting); + return ip6_privacy_to_string (nm_setting_ip6_config_get_ip6_privacy (s_ip6)); +} + +/* --- NM_SETTING_OLPC_MESH_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_olpc_get_channel, NM_SETTING_OLPC_MESH_CHANNEL) +DEFINE_HWADDR_GETTER (nmc_property_olpc_get_anycast_address, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS) + +static char * +nmc_property_olpc_get_ssid (NMSetting *setting) +{ + NMSettingOlpcMesh *s_olpc_mesh = NM_SETTING_OLPC_MESH (setting); + const GByteArray *ssid; + char *ssid_str = NULL; + + ssid = nm_setting_olpc_mesh_get_ssid (s_olpc_mesh); + if (ssid) + ssid_str = nm_utils_ssid_to_utf8 (ssid); + + return ssid_str; +} + +/* --- NM_SETTING_PPP_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_ppp_get_noauth, NM_SETTING_PPP_NOAUTH) +DEFINE_GETTER (nmc_property_ppp_get_refuse_eap, NM_SETTING_PPP_REFUSE_EAP) +DEFINE_GETTER (nmc_property_ppp_get_refuse_pap, NM_SETTING_PPP_REFUSE_PAP) +DEFINE_GETTER (nmc_property_ppp_get_refuse_chap, NM_SETTING_PPP_REFUSE_CHAP) +DEFINE_GETTER (nmc_property_ppp_get_refuse_mschap, NM_SETTING_PPP_REFUSE_MSCHAP) +DEFINE_GETTER (nmc_property_ppp_get_refuse_mschapv2, NM_SETTING_PPP_REFUSE_MSCHAPV2) +DEFINE_GETTER (nmc_property_ppp_get_nobsdcomp, NM_SETTING_PPP_NOBSDCOMP) +DEFINE_GETTER (nmc_property_ppp_get_nodeflate, NM_SETTING_PPP_NODEFLATE) +DEFINE_GETTER (nmc_property_ppp_get_no_vj_comp, NM_SETTING_PPP_NO_VJ_COMP) +DEFINE_GETTER (nmc_property_ppp_get_require_mppe, NM_SETTING_PPP_REQUIRE_MPPE) +DEFINE_GETTER (nmc_property_ppp_get_require_mppe_128, NM_SETTING_PPP_REQUIRE_MPPE_128) +DEFINE_GETTER (nmc_property_ppp_get_mppe_stateful, NM_SETTING_PPP_MPPE_STATEFUL) +DEFINE_GETTER (nmc_property_ppp_get_crtscts, NM_SETTING_PPP_CRTSCTS) +DEFINE_GETTER (nmc_property_ppp_get_baud, NM_SETTING_PPP_BAUD) +DEFINE_GETTER (nmc_property_ppp_get_mru, NM_SETTING_PPP_MRU) +DEFINE_GETTER (nmc_property_ppp_get_mtu, NM_SETTING_PPP_MTU) +DEFINE_GETTER (nmc_property_ppp_get_lcp_echo_failure, NM_SETTING_PPP_LCP_ECHO_FAILURE) +DEFINE_GETTER (nmc_property_ppp_get_lcp_echo_interval, NM_SETTING_PPP_LCP_ECHO_INTERVAL) + +/* --- NM_SETTING_PPPOE_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_pppoe_get_service, NM_SETTING_PPPOE_SERVICE) +DEFINE_GETTER (nmc_property_pppoe_get_username, NM_SETTING_PPPOE_USERNAME) +DEFINE_GETTER (nmc_property_pppoe_get_password, NM_SETTING_PPPOE_PASSWORD) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_pppoe_get_password_flags, NM_SETTING_PPPOE_PASSWORD_FLAGS) + +/* --- NM_SETTING_SERIAL_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_serial_get_baud, NM_SETTING_SERIAL_BAUD) +DEFINE_GETTER (nmc_property_serial_get_bits, NM_SETTING_SERIAL_BITS) +DEFINE_GETTER (nmc_property_serial_get_parity, NM_SETTING_SERIAL_PARITY) +DEFINE_GETTER (nmc_property_serial_get_stopbits, NM_SETTING_SERIAL_STOPBITS) +DEFINE_GETTER (nmc_property_serial_get_send_delay, NM_SETTING_SERIAL_SEND_DELAY) + +/* --- NM_SETTING_VLAN_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_vlan_get_interface_name, NM_SETTING_VLAN_INTERFACE_NAME) +DEFINE_GETTER (nmc_property_vlan_get_parent, NM_SETTING_VLAN_PARENT) +DEFINE_GETTER (nmc_property_vlan_get_id, NM_SETTING_VLAN_ID) + + +static char * +nmc_property_vlan_get_flags (NMSetting *setting) +{ + NMSettingVlan *s_vlan = NM_SETTING_VLAN (setting); + return vlan_flags_to_string (nm_setting_vlan_get_flags (s_vlan)); +} + +static char * +nmc_property_vlan_get_ingress_priority_map (NMSetting *setting) +{ + NMSettingVlan *s_vlan = NM_SETTING_VLAN (setting); + return vlan_priorities_to_string (s_vlan, NM_VLAN_INGRESS_MAP); +} + +static char * +nmc_property_vlan_get_egress_priority_map (NMSetting *setting) +{ + NMSettingVlan *s_vlan = NM_SETTING_VLAN (setting); + return vlan_priorities_to_string (s_vlan, NM_VLAN_EGRESS_MAP); +} + +/* --- NM_SETTING_VPN_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_vpn_get_service_type, NM_SETTING_VPN_SERVICE_TYPE) +DEFINE_GETTER (nmc_property_vpn_get_user_name, NM_SETTING_VPN_USER_NAME) + +static char * +nmc_property_vpn_get_data (NMSetting *setting) +{ + NMSettingVPN *s_vpn = NM_SETTING_VPN (setting); + GString *data_item_str; + + data_item_str = g_string_new (NULL); + nm_setting_vpn_foreach_data_item (s_vpn, &vpn_data_item, data_item_str); + + return g_string_free (data_item_str, FALSE); +} + +static char * +nmc_property_vpn_get_secrets (NMSetting *setting) +{ + NMSettingVPN *s_vpn = NM_SETTING_VPN (setting); + GString *secret_str; + + secret_str = g_string_new (NULL); + nm_setting_vpn_foreach_secret (s_vpn, &vpn_data_item, secret_str); + + return g_string_free (secret_str, FALSE); +} + +/* --- NM_SETTING_WIMAX_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_wimax_get_network_name, NM_SETTING_WIMAX_NETWORK_NAME) +DEFINE_HWADDR_GETTER (nmc_property_wimax_get_mac_address, NM_SETTING_WIMAX_MAC_ADDRESS) + +/* --- NM_SETTING_WIRED_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_wired_get_port, NM_SETTING_WIRED_PORT) +DEFINE_GETTER (nmc_property_wired_get_speed, NM_SETTING_WIRED_SPEED) +DEFINE_GETTER (nmc_property_wired_get_duplex, NM_SETTING_WIRED_DUPLEX) +DEFINE_GETTER (nmc_property_wired_get_auto_negotiate, NM_SETTING_WIRED_AUTO_NEGOTIATE) +DEFINE_HWADDR_GETTER (nmc_property_wired_get_mac_address, NM_SETTING_WIRED_MAC_ADDRESS) +DEFINE_HWADDR_GETTER (nmc_property_wired_get_cloned_mac_address, NM_SETTING_WIRED_CLONED_MAC_ADDRESS) +DEFINE_GETTER (nmc_property_wired_get_mac_address_blacklist, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST) +DEFINE_GETTER (nmc_property_wired_get_s390_subchannels, NM_SETTING_WIRED_S390_SUBCHANNELS) +DEFINE_GETTER (nmc_property_wired_get_s390_nettype, NM_SETTING_WIRED_S390_NETTYPE) +DEFINE_GETTER (nmc_property_wired_get_s390_options, NM_SETTING_WIRED_S390_OPTIONS) + +static char * +nmc_property_wired_get_mtu (NMSetting *setting) +{ + NMSettingWired *s_wired = NM_SETTING_WIRED (setting); + int mtu; + + mtu = nm_setting_wired_get_mtu (s_wired); + if (mtu == 0) + return g_strdup (_("auto")); + else + return g_strdup_printf ("%d", mtu); +} + +/* --- NM_SETTING_WIRELESS_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_wireless_get_mode, NM_SETTING_WIRELESS_MODE) +DEFINE_GETTER (nmc_property_wireless_get_band, NM_SETTING_WIRELESS_BAND) +DEFINE_GETTER (nmc_property_wireless_get_channel, NM_SETTING_WIRELESS_CHANNEL) +DEFINE_HWADDR_GETTER (nmc_property_wireless_get_bssid, NM_SETTING_WIRELESS_BSSID) +DEFINE_GETTER (nmc_property_wireless_get_rate, NM_SETTING_WIRELESS_RATE) +DEFINE_GETTER (nmc_property_wireless_get_tx_power, NM_SETTING_WIRELESS_TX_POWER) +DEFINE_HWADDR_GETTER (nmc_property_wireless_get_mac_address, NM_SETTING_WIRELESS_MAC_ADDRESS) +DEFINE_HWADDR_GETTER (nmc_property_wireless_get_cloned_mac_address, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS) +DEFINE_GETTER (nmc_property_wireless_get_mac_address_blacklist, NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST) +DEFINE_GETTER (nmc_property_wireless_get_seen_bssids, NM_SETTING_WIRELESS_SEEN_BSSIDS) +DEFINE_GETTER (nmc_property_wireless_get_hidden, NM_SETTING_WIRELESS_HIDDEN) + +static char * +nmc_property_wireless_get_ssid (NMSetting *setting) +{ + NMSettingWireless *s_wireless = NM_SETTING_WIRELESS (setting); + const GByteArray *ssid; + char *ssid_str = NULL; ssid = nm_setting_wireless_get_ssid (s_wireless); if (ssid) - ssid_str = ssid_to_printable ((const char *) ssid->data, ssid->len); - channel_str = g_strdup_printf ("%d", nm_setting_wireless_get_channel (s_wireless)); - rate_str = g_strdup_printf ("%d", nm_setting_wireless_get_rate (s_wireless)); - bssid = nm_setting_wireless_get_bssid (s_wireless); - if (bssid) - bssid_str = nm_utils_hwaddr_ntoa (bssid->data, ARPHRD_ETHER); - tx_power_str = g_strdup_printf ("%d", nm_setting_wireless_get_tx_power (s_wireless)); - mtu_str = g_strdup_printf ("%d", nm_setting_wireless_get_mtu (s_wireless)); - mac = nm_setting_wireless_get_mac_address (s_wireless); - if (mac) - device_mac_str = nm_utils_hwaddr_ntoa (mac->data, ARPHRD_ETHER); - mac = nm_setting_wireless_get_cloned_mac_address (s_wireless); - if (mac) - cloned_mac_str = nm_utils_hwaddr_ntoa (mac->data, ARPHRD_ETHER); - - mac_blacklist = g_string_new (NULL); - iter = nm_setting_wireless_get_mac_address_blacklist (s_wireless); - while (iter) { - g_string_append_printf (mac_blacklist, "%s,", (char *) iter->data); - iter = iter->next; - } - g_string_truncate (mac_blacklist, mac_blacklist->len-1); /* chop off trailing ',' */ - - seen_bssids = g_string_new (NULL); - for (i = 0; i < nm_setting_wireless_get_num_seen_bssids (s_wireless); i++) { - if (i > 0) - g_string_append_c (seen_bssids, ','); - g_string_append (seen_bssids, nm_setting_wireless_get_seen_bssid (s_wireless, i)); - } - - nmc->allowed_fields[0].value = NM_SETTING_WIRELESS_SETTING_NAME; - nmc->allowed_fields[1].value = ssid_str; - nmc->allowed_fields[2].value = nm_setting_wireless_get_mode (s_wireless); - nmc->allowed_fields[3].value = nm_setting_wireless_get_band (s_wireless); - nmc->allowed_fields[4].value = channel_str; - nmc->allowed_fields[5].value = bssid_str; - nmc->allowed_fields[6].value = rate_str; - nmc->allowed_fields[7].value = tx_power_str; - nmc->allowed_fields[8].value = device_mac_str; - nmc->allowed_fields[9].value = cloned_mac_str; - nmc->allowed_fields[10].value = mac_blacklist->str; - nmc->allowed_fields[11].value = strcmp (mtu_str, "0") ? mtu_str : _("auto"); - nmc->allowed_fields[12].value = seen_bssids->str; - nmc->allowed_fields[13].value = nm_setting_wireless_get_security (s_wireless); - nmc->allowed_fields[14].value = nm_setting_wireless_get_hidden (s_wireless) ? _("yes") : _("no"); - - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ - - g_free (ssid_str); - g_free (channel_str); - g_free (bssid_str); - g_free (rate_str); - g_free (tx_power_str); - g_free (device_mac_str); - g_free (cloned_mac_str); - g_free (mtu_str); - g_string_free (seen_bssids, TRUE); - g_string_free (mac_blacklist, TRUE); + ssid_str = nm_utils_ssid_to_utf8 (ssid); + + return ssid_str; +} + +static char * +nmc_property_wireless_get_mtu (NMSetting *setting) +{ + NMSettingWireless *s_wireless = NM_SETTING_WIRELESS (setting); + int mtu; + + mtu = nm_setting_wireless_get_mtu (s_wireless); + if (mtu == 0) + return g_strdup (_("auto")); + else + return g_strdup_printf ("%d", mtu); +} + +/* --- NM_SETTING_WIRELESS_SECURITY_SETTING_NAME property get functions --- */ +DEFINE_GETTER (nmc_property_wifi_sec_get_key_mgmt, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT) +DEFINE_GETTER (nmc_property_wifi_sec_get_wep_tx_keyidx, NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX) +DEFINE_GETTER (nmc_property_wifi_sec_get_auth_alg, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG) +DEFINE_GETTER (nmc_property_wifi_sec_get_proto, NM_SETTING_WIRELESS_SECURITY_PROTO) +DEFINE_GETTER (nmc_property_wifi_sec_get_pairwise, NM_SETTING_WIRELESS_SECURITY_PAIRWISE) +DEFINE_GETTER (nmc_property_wifi_sec_get_group, NM_SETTING_WIRELESS_SECURITY_GROUP) +DEFINE_GETTER (nmc_property_wifi_sec_get_leap_username, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_wifi_sec_get_wep_key_flags, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS) +DEFINE_GETTER (nmc_property_wifi_sec_get_psk, NM_SETTING_WIRELESS_SECURITY_PSK) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_wifi_sec_get_psk_flags, NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS) +DEFINE_GETTER (nmc_property_wifi_sec_get_leap_password, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD) +DEFINE_SECRET_FLAGS_GETTER (nmc_property_wifi_sec_get_leap_password_flags, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS) + +static char * +nmc_property_wifi_sec_get_wep_key0 (NMSetting *setting) +{ + NMSettingWirelessSecurity *s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (setting); + return g_strdup (nm_setting_wireless_security_get_wep_key (s_wireless_sec, 0)); +} + +static char * +nmc_property_wifi_sec_get_wep_key1 (NMSetting *setting) +{ + NMSettingWirelessSecurity *s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (setting); + return g_strdup (nm_setting_wireless_security_get_wep_key (s_wireless_sec, 1)); +} + +static char * +nmc_property_wifi_sec_get_wep_key2 (NMSetting *setting) +{ + NMSettingWirelessSecurity *s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (setting); + return g_strdup (nm_setting_wireless_security_get_wep_key (s_wireless_sec, 2)); +} + +static char * +nmc_property_wifi_sec_get_wep_key3 (NMSetting *setting) +{ + NMSettingWirelessSecurity *s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (setting); + return g_strdup (nm_setting_wireless_security_get_wep_key (s_wireless_sec, 3)); +} + +static char * +nmc_property_wifi_sec_get_wep_key_type (NMSetting *setting) +{ + NMSettingWirelessSecurity *s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (setting); + return wep_key_type_to_string (nm_setting_wireless_security_get_wep_key_type (s_wireless_sec)); +} + +/*----------------------------------------------------------------------------*/ + +static void +nmc_value_transform_bool_string (const GValue *src_value, + GValue *dest_value) +{ + dest_value->data[0].v_pointer = g_strdup (src_value->data[0].v_int ? "yes" : "no"); +} + +static void +nmc_value_transform_char_string (const GValue *src_value, + GValue *dest_value) +{ + dest_value->data[0].v_pointer = g_strdup_printf ("%c", src_value->data[0].v_uint); +} + +static void __attribute__((constructor)) +register_nmcli_value_transforms (void) +{ + g_value_register_transform_func (G_TYPE_BOOLEAN, G_TYPE_STRING, nmc_value_transform_bool_string); + g_value_register_transform_func (G_TYPE_CHAR, G_TYPE_STRING, nmc_value_transform_char_string); +} + +/*----------------------------------------------------------------------------*/ + +/* Main hash table storing function pointer for manipulating properties */ +static GHashTable *nmc_properties = NULL; +typedef char * (*NmcPropertyGetFunc) (NMSetting *); +typedef gboolean (*NmcPropertySetFunc) (NMSetting *, const char *, const char *, GError **); +typedef gboolean (*NmcPropertyRemoveFunc) (NMSetting *, const char *, const char *, guint32, GError **); +typedef const char * (*NmcPropertyDescribeFunc) (NMSetting *, const char *); +typedef const char * (*NmcPropertyValuesFunc) (NMSetting *, const char *); +typedef char * (*NmcPropertyOut2InFunc) (const char *); + +typedef struct { + NmcPropertyGetFunc get_func; /* func getting property values */ + NmcPropertySetFunc set_func; /* func adding/setting property values */ + NmcPropertyRemoveFunc remove_func; /* func removing items from container options */ + NmcPropertyDescribeFunc describe_func; /* func returning property description */ + NmcPropertyValuesFunc values_func; /* func returning allowed property values */ + NmcPropertyOut2InFunc out2in_func; /* func converting property values from output to input format */ +} NmcPropertyFuncs; + +/* + * We need NmCli in some _set_property functions, and they aren't passed NmCli. + * So use the global variable. + */ +/* Global variable defined in nmcli.c */ +extern NmCli nm_cli; + +NMSetting * +nmc_setting_new_for_name (const char *name) +{ + GType stype; + NMSetting *setting = NULL; + + if (name) { + stype = nm_connection_lookup_setting_type (name); + if (stype != G_TYPE_INVALID) { + setting = g_object_new (stype, NULL); + g_warn_if_fail (NM_IS_SETTING (setting)); + } + } + return setting; +} + +static gboolean +get_answer (const char *prop, const char *value) +{ + char *tmp_str; + char *question; + gboolean answer = FALSE; + + if (value) + question = g_strdup_printf (_("Do you also want to set '%s' to '%s'? [yes]: "), prop, value); + else + question = g_strdup_printf (_("Do you also want to clear '%s'? [yes]: "), prop); + tmp_str = nmc_get_user_input (question); + if (!tmp_str || matches (tmp_str, "yes") == 0) + answer = TRUE; + g_free (tmp_str); + g_free (question); + return answer; +} + +static void ipv4_method_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data); +static void ipv6_method_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data); + +static void +ipv4_addresses_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + static gboolean answered = FALSE; + static gboolean answer = FALSE; + + g_signal_handlers_block_by_func (object, G_CALLBACK (ipv4_method_changed_cb), NULL); + + /* If we have some IP addresses set method to 'manual'. + * Else if the method was 'manual', change it back to 'auto'. + */ + if (nm_setting_ip4_config_get_num_addresses (NM_SETTING_IP4_CONFIG (object))) { + if (g_strcmp0 (nm_setting_ip4_config_get_method (NM_SETTING_IP4_CONFIG (object)), NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { + if (!answered) { + answered = TRUE; + answer = get_answer ("ipv4.method", "manual"); + } + if (answer) + g_object_set (object, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL); + } + } else { + answered = FALSE; + if (!g_strcmp0 (nm_setting_ip4_config_get_method (NM_SETTING_IP4_CONFIG (object)), NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) + g_object_set (object, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + } + + g_signal_handlers_unblock_by_func (object, G_CALLBACK (ipv4_method_changed_cb), NULL); +} + +static void +ipv4_method_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + static GValue value = G_VALUE_INIT; + static gboolean answered = FALSE; + static gboolean answer = FALSE; + + g_signal_handlers_block_by_func (object, G_CALLBACK (ipv4_addresses_changed_cb), NULL); + + /* If method != manual, remove addresses (save them for restoring them later when method becomes 'manual' */ + if (g_strcmp0 (nm_setting_ip4_config_get_method (NM_SETTING_IP4_CONFIG (object)), NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { + if (nm_setting_ip4_config_get_num_addresses (NM_SETTING_IP4_CONFIG (object))) { + if (!answered) { + answered = TRUE; + answer = get_answer ("ipv4.addresses", NULL); + } + if (answer) { + if (G_IS_VALUE (&value)) + g_value_unset (&value); + nmc_property_get_gvalue (NM_SETTING (object), NM_SETTING_IP4_CONFIG_ADDRESSES, &value); + g_object_set (object, NM_SETTING_IP4_CONFIG_ADDRESSES, NULL, NULL); + } + } + } else { + answered = FALSE; + if (G_IS_VALUE (&value)) { + nmc_property_set_gvalue (NM_SETTING (object), NM_SETTING_IP4_CONFIG_ADDRESSES, &value); + g_value_unset (&value); + } + } + + g_signal_handlers_unblock_by_func (object, G_CALLBACK (ipv4_addresses_changed_cb), NULL); +} + +static void +ipv6_addresses_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + static gboolean answered = FALSE; + static gboolean answer = FALSE; + + g_signal_handlers_block_by_func (object, G_CALLBACK (ipv6_method_changed_cb), NULL); + + /* If we have some IP addresses set method to 'manual'. + * Else if the method was 'manual', change it back to 'auto'. + */ + if (nm_setting_ip6_config_get_num_addresses (NM_SETTING_IP6_CONFIG (object))) { + if (g_strcmp0 (nm_setting_ip6_config_get_method (NM_SETTING_IP6_CONFIG (object)), NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + if (!answered) { + answered = TRUE; + answer = get_answer ("ipv6.method", "manual"); + } + if (answer) + g_object_set (object, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, NULL); + } + } else { + answered = FALSE; + if (!g_strcmp0 (nm_setting_ip6_config_get_method (NM_SETTING_IP6_CONFIG (object)), NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) + g_object_set (object, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL); + } + + g_signal_handlers_unblock_by_func (object, G_CALLBACK (ipv6_method_changed_cb), NULL); +} + +static void +ipv6_method_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + static GValue value = G_VALUE_INIT; + static gboolean answered = FALSE; + static gboolean answer = FALSE; + + g_signal_handlers_block_by_func (object, G_CALLBACK (ipv6_addresses_changed_cb), NULL); + + /* If method != manual, remove addresses (save them for restoring them later when method becomes 'manual' */ + if (g_strcmp0 (nm_setting_ip6_config_get_method (NM_SETTING_IP6_CONFIG (object)), NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + if (nm_setting_ip6_config_get_num_addresses (NM_SETTING_IP6_CONFIG (object))) { + if (!answered) { + answered = TRUE; + answer = get_answer ("ipv6.addresses", NULL); + } + if (answer) { + if (G_IS_VALUE (&value)) + g_value_unset (&value); + nmc_property_get_gvalue (NM_SETTING (object), NM_SETTING_IP6_CONFIG_ADDRESSES, &value); + g_object_set (object, NM_SETTING_IP6_CONFIG_ADDRESSES, NULL, NULL); + } + } + } else { + answered = FALSE; + if (G_IS_VALUE (&value)) { + nmc_property_set_gvalue (NM_SETTING (object), NM_SETTING_IP6_CONFIG_ADDRESSES, &value); + g_value_unset (&value); + } + } + g_signal_handlers_unblock_by_func (object, G_CALLBACK (ipv6_addresses_changed_cb), NULL); +} + +static void +wireless_band_channel_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + const char *value = NULL, *mode; + char str[16]; + NMSettingWireless *s_wireless = NM_SETTING_WIRELESS (object); + + if (strcmp (g_param_spec_get_name (pspec), NM_SETTING_WIRELESS_BAND) == 0) { + value = nm_setting_wireless_get_band (s_wireless); + if (!value) + return; + } else { + guint32 channel = nm_setting_wireless_get_channel (s_wireless); + + if (channel == 0) + return; + + g_snprintf (str, sizeof (str), "%d", nm_setting_wireless_get_channel (s_wireless)); + value = str; + } + + mode = nm_setting_wireless_get_mode (NM_SETTING_WIRELESS (object)); + if (!mode || !*mode || strcmp (mode, NM_SETTING_WIRELESS_MODE_INFRA) == 0) { + printf (_("Warning: %s.%s set to '%s', but it might be ignored in infrastructure mode\n"), + nm_setting_get_name (NM_SETTING (s_wireless)), g_param_spec_get_name (pspec), + value); + } +} + +void +nmc_setting_ip4_connect_handlers (NMSettingIP4Config *setting) +{ + g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting)); + + g_signal_connect (setting, "notify::" NM_SETTING_IP4_CONFIG_ADDRESSES, + G_CALLBACK (ipv4_addresses_changed_cb), NULL); + g_signal_connect (setting, "notify::" NM_SETTING_IP4_CONFIG_METHOD, + G_CALLBACK (ipv4_method_changed_cb), NULL); +} + +void +nmc_setting_ip6_connect_handlers (NMSettingIP6Config *setting) +{ + g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting)); + + g_signal_connect (setting, "notify::" NM_SETTING_IP6_CONFIG_ADDRESSES, + G_CALLBACK (ipv6_addresses_changed_cb), NULL); + g_signal_connect (setting, "notify::" NM_SETTING_IP6_CONFIG_METHOD, + G_CALLBACK (ipv6_method_changed_cb), NULL); +} + +void +nmc_setting_wireless_connect_handlers (NMSettingWireless *setting) +{ + g_return_if_fail (NM_IS_SETTING_WIRELESS (setting)); + + g_signal_connect (setting, "notify::" NM_SETTING_WIRELESS_BAND, + G_CALLBACK (wireless_band_channel_changed_cb), NULL); + g_signal_connect (setting, "notify::" NM_SETTING_WIRELESS_CHANNEL, + G_CALLBACK (wireless_band_channel_changed_cb), NULL); +} + +/* + * Customize some properties of the setting so that the setting has sensible + * values. + */ +void +nmc_setting_custom_init (NMSetting *setting) +{ + g_return_if_fail (NM_IS_SETTING (setting)); + + if (NM_IS_SETTING_IP4_CONFIG (setting)) { + g_object_set (NM_SETTING_IP4_CONFIG (setting), + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + nmc_setting_ip4_connect_handlers (NM_SETTING_IP4_CONFIG (setting)); + } else if (NM_IS_SETTING_IP6_CONFIG (setting)) { + g_object_set (NM_SETTING_IP6_CONFIG (setting), + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NULL); + nmc_setting_ip6_connect_handlers (NM_SETTING_IP6_CONFIG (setting)); + } else if (NM_IS_SETTING_WIRELESS (setting)) { + g_object_set (NM_SETTING_WIRELESS (setting), + NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, + NULL); + nmc_setting_wireless_connect_handlers (NM_SETTING_WIRELESS (setting)); + } +} + +/* === SetFunc, RemoveFunc, DescribeFunc, ValuesFunc functions === */ +static gboolean +verify_string_list (char **strv, + const char *prop, + gboolean (*validate_func) (const char *), + GError **error) +{ + char **iter; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + for (iter = strv; iter && *iter; iter++) { + if (**iter == '\0') + continue; + if (validate_func) { + if (!validate_func (*iter)) { + g_set_error (error, 1, 0, _("'%s' is not valid"), + *iter); + return FALSE; + } + } + } return TRUE; } -gboolean -setting_wireless_security_details (NMSettingWirelessSecurity *s_wireless_sec, NmCli *nmc) +/* Validate 'val' number against to int property spec */ +static gboolean +validate_int (NMSetting *setting, const char* prop, gint val, GError **error) { - int i; - char *wep_tx_keyidx_str, *wep_key_type_str, *wep_flags, *psk_flags, *leap_flags; - GString *protos, *pairwises, *groups; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + GParamSpec *pspec; + GValue value = G_VALUE_INIT; + gboolean success = TRUE; + + g_value_init (&value, G_TYPE_INT); + g_value_set_int (&value, val); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), prop); + g_assert (G_IS_PARAM_SPEC (pspec)); + if (g_param_value_validate (pspec, &value)) { + GParamSpecInt *pspec_int = (GParamSpecInt *) pspec; + g_set_error (error, 1, 0, _("'%d' is not valid; use <%d-%d>"), + val, pspec_int->minimum, pspec_int->maximum); + success = FALSE; + } + g_value_unset (&value); + return success; +} - g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (s_wireless_sec), FALSE); +/* Validate 'val' number against to uint property spec */ +static gboolean +validate_uint (NMSetting *setting, const char* prop, guint val, GError **error) +{ + GParamSpec *pspec; + GValue value = G_VALUE_INIT; + gboolean success = TRUE; + + g_value_init (&value, G_TYPE_UINT); + g_value_set_uint (&value, val); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), prop); + g_assert (G_IS_PARAM_SPEC (pspec)); + if (g_param_value_validate (pspec, &value)) { + GParamSpecUInt *pspec_uint = (GParamSpecUInt *) pspec; + g_set_error (error, 1, 0, _("'%u' is not valid; use <%d-%d>"), + val, pspec_uint->minimum, pspec_uint->maximum); + success = FALSE; + } + g_value_unset (&value); + return success; +} + +static gboolean +check_and_set_string (NMSetting *setting, + const char *prop, + const char *val, + const char **valid_strv, + GError **error) +{ + const char *checked_val; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - nmc->allowed_fields = nmc_fields_setting_wireless_security; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_WIRELESS_SECURITY_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ - - wep_tx_keyidx_str = g_strdup_printf ("%d", nm_setting_wireless_security_get_wep_tx_keyidx (s_wireless_sec)); - protos = g_string_new (NULL); - for (i = 0; i < nm_setting_wireless_security_get_num_protos (s_wireless_sec); i++) { - if (i > 0) - g_string_append_c (protos, ','); - g_string_append (protos, nm_setting_wireless_security_get_proto (s_wireless_sec, i)); - } - pairwises = g_string_new (NULL); - for (i = 0; i < nm_setting_wireless_security_get_num_pairwise (s_wireless_sec); i++) { - if (i > 0) - g_string_append_c (pairwises, ','); - g_string_append (pairwises, nm_setting_wireless_security_get_pairwise (s_wireless_sec, i)); - } - groups = g_string_new (NULL); - for (i = 0; i < nm_setting_wireless_security_get_num_groups (s_wireless_sec); i++) { - if (i > 0) - g_string_append_c (groups, ','); - g_string_append (groups, nm_setting_wireless_security_get_group (s_wireless_sec, i)); - } - wep_key_type_str = wep_key_type_to_string (nm_setting_wireless_security_get_wep_key_type (s_wireless_sec)); - - wep_flags = secret_flags_to_string (nm_setting_wireless_security_get_wep_key_flags (s_wireless_sec)); - psk_flags = secret_flags_to_string (nm_setting_wireless_security_get_psk_flags (s_wireless_sec)); - leap_flags = secret_flags_to_string (nm_setting_wireless_security_get_leap_password_flags (s_wireless_sec)); - - nmc->allowed_fields[0].value = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME; - nmc->allowed_fields[1].value = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec); - nmc->allowed_fields[2].value = wep_tx_keyidx_str; - nmc->allowed_fields[3].value = nm_setting_wireless_security_get_auth_alg (s_wireless_sec); - nmc->allowed_fields[4].value = protos->str; - nmc->allowed_fields[5].value = pairwises->str; - nmc->allowed_fields[6].value = groups->str; - nmc->allowed_fields[7].value = nm_setting_wireless_security_get_leap_username (s_wireless_sec); - nmc->allowed_fields[8].value = nm_setting_wireless_security_get_wep_key (s_wireless_sec, 0); - nmc->allowed_fields[9].value = nm_setting_wireless_security_get_wep_key (s_wireless_sec, 1); - nmc->allowed_fields[10].value = nm_setting_wireless_security_get_wep_key (s_wireless_sec, 2); - nmc->allowed_fields[11].value = nm_setting_wireless_security_get_wep_key (s_wireless_sec, 3); - nmc->allowed_fields[12].value = wep_flags; - nmc->allowed_fields[13].value = wep_key_type_str; - nmc->allowed_fields[14].value = nm_setting_wireless_security_get_psk (s_wireless_sec); - nmc->allowed_fields[15].value = psk_flags; - nmc->allowed_fields[16].value = nm_setting_wireless_security_get_leap_password (s_wireless_sec); - nmc->allowed_fields[17].value = leap_flags; - - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ - - g_free (wep_tx_keyidx_str); - g_free (wep_key_type_str); - g_free (wep_flags); - g_free (psk_flags); - g_free (leap_flags); - g_string_free (protos, TRUE); - g_string_free (pairwises, TRUE); - g_string_free (groups, TRUE); + checked_val = nmc_string_is_valid (val, valid_strv, error); + if (!checked_val) + return FALSE; + g_object_set (setting, prop, checked_val, NULL); return TRUE; } -gboolean -setting_ip4_config_details (NMSettingIP4Config *s_ip4, NmCli *nmc) +#define DEFINE_SETTER_STR_LIST_MULTI(def_func, s_macro, set_func) \ + static gboolean \ + def_func (NMSetting *setting, \ + const char *prop, \ + const char *val, \ + const char **valid_strv, \ + GError **error) \ + { \ + char **strv = NULL, **iter; \ + const char *item; \ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); \ + strv = nmc_strsplit_set (val, " \t,", 0); \ + for (iter = strv; iter && *iter; iter++) { \ + if (!(item = nmc_string_is_valid (g_strstrip (*iter), valid_strv, error))) { \ + g_strfreev (strv); \ + return FALSE; \ + } \ + set_func (s_macro (setting), item); \ + } \ + g_strfreev (strv); \ + return TRUE; \ + } + +#define DEFINE_SETTER_OPTIONS(def_func, s_macro, s_type, add_func, valid_func1, valid_func2) \ + static gboolean \ + def_func (NMSetting *setting, const char *prop, const char *val, GError **error) \ + { \ + char **strv = NULL, **iter; \ + const char **(*valid_func1_p) (s_type *) = valid_func1; \ + const char * (*valid_func2_p) (const char *, const char *, GError **) = valid_func2; \ + const char *opt_name, *opt_val; \ + \ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); \ + \ + strv = nmc_strsplit_set (val, ",", 0); \ + for (iter = strv; iter && *iter; iter++) { \ + char *left = g_strstrip (*iter); \ + char *right = strchr (left, '='); \ + if (!right) { \ + g_set_error (error, 1, 0, _("'%s' is not valid; use <option>=<value>"), *iter); \ + g_strfreev (strv); \ + return FALSE; \ + } \ + *right++ = '\0'; \ + \ + if (valid_func1_p) { \ + const char **valid_options = valid_func1_p (s_macro (setting)); \ + if (!(opt_name = nmc_string_is_valid (g_strstrip (left), valid_options, error))) { \ + g_strfreev (strv); \ + return FALSE; \ + } \ + } else \ + opt_name = g_strstrip (left);\ + \ + opt_val = g_strstrip (right); \ + if (valid_func2_p) { \ + if (!(opt_val = valid_func2_p ((const char *) left, (const char *) opt_val, error))) { \ + g_strfreev (strv); \ + return FALSE; \ + }\ + }\ + add_func (s_macro (setting), opt_name, opt_val); \ + } \ + g_strfreev (strv); \ + return TRUE; \ + } + +#define DEFINE_REMOVER_INDEX(def_func, s_macro, num_func, rem_func) \ + static gboolean \ + def_func (NMSetting *setting, const char *prop, const char *option, guint32 idx, GError **error) \ + { \ + guint32 num; \ + if (option) { \ + g_set_error (error, 1, 0, _("index '%s' is not valid"), option); \ + return FALSE; \ + } \ + num = num_func (s_macro (setting)); \ + if (num == 0) { \ + g_set_error_literal (error, 1, 0, _("no item to remove")); \ + return FALSE; \ + } \ + if (idx >= num) { \ + g_set_error (error, 1, 0, _("index '%d' is not in range <0-%d>"), idx, num - 1); \ + return FALSE; \ + } \ + rem_func (s_macro (setting), idx); \ + return TRUE; \ + } + +#define DEFINE_REMOVER_INDEX_OR_VALUE(def_func, s_macro, num_func, rem_func_idx, rem_func_val) \ + static gboolean \ + def_func (NMSetting *setting, const char *prop, const char *value, guint32 idx, GError **error) \ + { \ + guint32 num; \ + if (value) { \ + gboolean ret; \ + char *value_stripped = g_strstrip (g_strdup (value)); \ + ret = rem_func_val (s_macro (setting), value_stripped, error); \ + g_free (value_stripped); \ + return ret; \ + } \ + num = num_func (s_macro (setting)); \ + if (num == 0) { \ + g_set_error_literal (error, 1, 0, _("no item to remove")); \ + return FALSE; \ + } \ + if (idx >= num) { \ + g_set_error (error, 1, 0, _("index '%d' is not in range <0-%d>"), idx, num - 1); \ + return FALSE; \ + } \ + rem_func_idx (s_macro (setting), idx); \ + return TRUE; \ + } + +#define DEFINE_REMOVER_OPTION(def_func, s_macro, rem_func) \ + static gboolean \ + def_func (NMSetting *setting, const char *prop, const char *option, guint32 idx, GError **error) \ + { \ + gboolean success = FALSE; \ + if (option && *option) { \ + success = rem_func (s_macro (setting), option); \ + if (!success) \ + g_set_error (error, 1, 0, _("invalid option '%s'"), option); \ + } else \ + g_set_error_literal (error, 1, 0, _("missing option")); \ + return success; \ + } + +#define DEFINE_ALLOWED_VAL_FUNC(def_func, valid_values) \ + static const char * \ + def_func (NMSetting *setting, const char *prop) \ + { \ + static char *values = NULL; \ + if (G_UNLIKELY (values == NULL)) \ + values = g_strjoinv (", ", (char **) valid_values); \ + return values; \ + } + +/* --- generic property setter functions --- */ +static gboolean +nmc_property_set_string (NMSetting *setting, const char *prop, const char *val, GError **error) { - GString *dns_str, *dns_search_str, *addr_str, *route_str; - int i, num; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + g_object_set (setting, prop, val, NULL); + return TRUE; +} - g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (s_ip4), FALSE); +static gboolean +nmc_property_set_int (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + long val_int; - nmc->allowed_fields = nmc_fields_setting_ip4_config; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_IP4_CONFIG_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - dns_str = g_string_new (NULL); - num = nm_setting_ip4_config_get_num_dns (s_ip4); - for (i = 0; i < num; i++) { - char buf[INET_ADDRSTRLEN]; - guint32 ip; - - ip = nm_setting_ip4_config_get_dns (s_ip4, i); - memset (buf, 0, sizeof (buf)); - inet_ntop (AF_INET, (const void *) &ip, buf, sizeof (buf)); - if (i > 0) - g_string_append (dns_str, ", "); - g_string_append (dns_str, buf); + if (!nmc_string_to_int (val, TRUE, G_MININT, G_MAXINT, &val_int)) { + g_set_error (error, 1, 0, _("'%s' is not a valid number (or out of range)"), val); + return FALSE; } - dns_search_str = g_string_new (NULL); - num = nm_setting_ip4_config_get_num_dns_searches (s_ip4); - for (i = 0; i < num; i++) { - const char *domain; - - domain = nm_setting_ip4_config_get_dns_search (s_ip4, i); - if (i > 0) - g_string_append (dns_search_str, ", "); - g_string_append (dns_search_str, domain); + /* Validate the number according to the property spec */ + if (!validate_int (setting, prop, (gint) val_int, error)) + return FALSE; + + g_object_set (setting, prop, val_int, NULL); + return TRUE; +} + +static gboolean +nmc_property_set_uint (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + unsigned long val_int; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_string_to_uint (val, TRUE, 0, G_MAXUINT, &val_int)) { + g_set_error (error, 1, 0, _("'%s' is not a valid number (or out of range)"), val); + return FALSE; + } + + /* Validate the number according to the property spec */ + if (!validate_uint (setting, prop, (guint) val_int, error)) + return FALSE; + + g_object_set (setting, prop, val_int, NULL); + return TRUE; +} + +static gboolean +nmc_property_set_bool (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + gboolean val_bool; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_string_to_bool (val, &val_bool, error)) + return FALSE; + + g_object_set (setting, prop, val_bool, NULL); + return TRUE; +} + +static gboolean +nmc_property_set_ssid (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + GByteArray *ssid_arr; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (strlen (val) > 32) { + g_set_error (error, 1, 0, _("'%s' is not valid"), val); + return FALSE; } - addr_str = g_string_new (NULL); - num = nm_setting_ip4_config_get_num_addresses (s_ip4); - for (i = 0; i < num; i++) { - char buf[INET_ADDRSTRLEN]; - char *tmp; - NMIP4Address *addr; - guint32 ip; + ssid_arr = g_byte_array_sized_new (strlen (val)); + g_byte_array_append (ssid_arr, (const guint8 *) val, strlen (val)); + g_object_set (setting, prop, ssid_arr, NULL); + + return TRUE; +} + +static gboolean +nmc_property_set_mac (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + GByteArray *array; - if (i > 0) - g_string_append (addr_str, "; "); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - g_string_append (addr_str, "{ "); + array = nm_utils_hwaddr_atoba (val, ARPHRD_ETHER); + if (!array) { + g_set_error (error, 1, 0, _("'%s' is not a valid Ethernet MAC"), val); + return FALSE; + } - addr = nm_setting_ip4_config_get_address (s_ip4, i); + g_object_set (setting, prop, array, NULL); + g_byte_array_free (array, TRUE); + return TRUE; +} - memset (buf, 0, sizeof (buf)); - ip = nm_ip4_address_get_address (addr); - inet_ntop (AF_INET, (const void *) &ip, buf, sizeof (buf)); - g_string_append_printf (addr_str, "ip = %s", buf); +static gboolean +nmc_property_set_mtu (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + const char *mtu = val; - tmp = g_strdup_printf ("/%u", nm_ip4_address_get_prefix (addr)); - g_string_append (addr_str, tmp); - g_free (tmp); + if (strcmp (mtu, "auto") == 0) + mtu = "0"; - memset (buf, 0, sizeof (buf)); - ip = nm_ip4_address_get_gateway (addr); - inet_ntop (AF_INET, (const void *) &ip, buf, sizeof (buf)); - g_string_append_printf (addr_str, ", gw = %s", buf); + return nmc_property_set_uint (setting, prop, mtu, error); +} - g_string_append (addr_str, " }"); +static gboolean +nmc_property_set_ifname (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nm_utils_iface_valid_name (val)) { + g_set_error (error, 1, 0, _("'%s' is not a valid interface name"), val); + return FALSE; + } + g_object_set (setting, prop, val, NULL); + return TRUE; +} + +static gboolean +nmc_property_set_flags (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter; + unsigned long flags = 0, val_int; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, " \t,", 0); + for (iter = strv; iter && *iter; iter++) { + if (!nmc_string_to_uint (*iter, TRUE, 0, G_MAXUINT32, &val_int)) { + g_set_error (error, 1, 0, _("'%s' is not a number"), *iter); + g_strfreev (strv); + return FALSE; + } + flags += val_int; } + g_strfreev (strv); - route_str = g_string_new (NULL); - num = nm_setting_ip4_config_get_num_routes (s_ip4); - for (i = 0; i < num; i++) { - char buf[INET_ADDRSTRLEN]; - char *tmp; - NMIP4Route *route; - guint32 ip; + /* Validate the number according to the property spec */ + if (!validate_uint (setting, prop, (guint) flags, error)) + return FALSE; - if (i > 0) - g_string_append (route_str, "; "); + g_object_set (setting, prop, (guint) flags, NULL); + return TRUE; +} + +static gboolean +nmc_util_is_domain (const char *domain) +{ + //FIXME: implement + return TRUE; +} + +static gboolean +nmc_property_set_byte_array (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter; + char *val_strip; + const char *delimiters = " \t,"; + long int val_int; + char *bin; + GByteArray *array = NULL; + gboolean success = TRUE; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + val_strip = g_strstrip (g_strdup (val)); + + /* First try hex string in the format of AAbbCCDd */ + bin = nm_utils_hexstr2bin (val_strip, strlen (val_strip)); + if (bin) { + array = g_byte_array_sized_new (strlen (val_strip)/2); + g_byte_array_append (array, (const guint8 *) bin, strlen (val_strip)/2); + g_free (bin); + goto done; + } + + /* Otherwise, consider the following format: AA b 0xCc D */ + strv = nmc_strsplit_set (val_strip, delimiters, 0); + array = g_byte_array_sized_new (g_strv_length (strv)); + for (iter = strv; iter && *iter; iter++) { + if (!nmc_string_to_int_base (g_strstrip (*iter), 16, TRUE, 0, 255, &val_int)) { + g_set_error (error, 1, 0, _("'%s' is not a valid hex character"), *iter); + success = FALSE; + goto done; + } + g_byte_array_append (array, (const guint8 *) &val_int, 1); + } - g_string_append (route_str, "{ "); +done: + if (success) + g_object_set (setting, prop, array, NULL); - route = nm_setting_ip4_config_get_route (s_ip4, i); + g_strfreev (strv); + if (array) + g_byte_array_free (array, TRUE); + return success; +} - memset (buf, 0, sizeof (buf)); - ip = nm_ip4_route_get_dest (route); - inet_ntop (AF_INET, (const void *) &ip, buf, sizeof (buf)); - g_string_append_printf (route_str, "dst = %s", buf); +#define DEFINE_SETTER_MAC_BLACKLIST(def_func, s_macro, add_func) \ + static gboolean \ + def_func (NMSetting *setting, const char *prop, const char *val, GError **error) \ + { \ + guint8 buf[32]; \ + char **list = NULL, **iter; \ + GSList *macaddr_blacklist = NULL; \ + \ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); \ + \ + list = nmc_strsplit_set (val, " \t,", 0); \ + for (iter = list; iter && *iter; iter++) { \ + if (!nm_utils_hwaddr_aton (*iter, ARPHRD_ETHER, buf)) { \ + g_set_error (error, 1, 0, _("'%s' is not a valid MAC"), *iter); \ + g_strfreev (list); \ + g_slist_free (macaddr_blacklist); \ + return FALSE; \ + } \ + } \ + \ + for (iter = list; iter && *iter; iter++) \ + add_func (s_macro (setting), *iter); \ + \ + g_strfreev (list); \ + return TRUE; \ + } - tmp = g_strdup_printf ("/%u", nm_ip4_route_get_prefix (route)); - g_string_append (route_str, tmp); - g_free (tmp); +static char * +nmc_property_out2in_cut_paren (const char *out_format) +{ + const char *p; + size_t n; - memset (buf, 0, sizeof (buf)); - ip = nm_ip4_route_get_next_hop (route); - inet_ntop (AF_INET, (const void *) &ip, buf, sizeof (buf)); - g_string_append_printf (route_str, ", nh = %s", buf); + p = strstr (out_format, " ("); + n = p ? p - out_format : strlen (out_format); + return g_strndup (out_format, n); +} - tmp = g_strdup_printf (", mt = %u", nm_ip4_route_get_metric (route)); - g_string_append (route_str, tmp); - g_free (tmp); +/* --- NM_SETTING_CONNECTION_SETTING_NAME property setter functions --- */ +#if 0 +/* + * Setting/removing UUID has been forbidden. + * Should it be enabled later, this function can be used. + */ +static gboolean +nmc_property_con_set_uuid (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - g_string_append (route_str, " }"); + if (!nm_utils_is_uuid (val)) { + g_set_error (error, 1, 0, _("'%s' is not a valid UUID"), val); + return FALSE; } + g_object_set (setting, prop, val, NULL); + return TRUE; +} +#endif - nmc->allowed_fields[0].value = NM_SETTING_IP4_CONFIG_SETTING_NAME; - nmc->allowed_fields[1].value = nm_setting_ip4_config_get_method (s_ip4); - nmc->allowed_fields[2].value = dns_str->str; - nmc->allowed_fields[3].value = dns_search_str->str; - nmc->allowed_fields[4].value = addr_str->str; - nmc->allowed_fields[5].value = route_str->str; - nmc->allowed_fields[6].value = nm_setting_ip4_config_get_ignore_auto_routes (s_ip4) ? _("yes") : _("no"); - nmc->allowed_fields[7].value = nm_setting_ip4_config_get_ignore_auto_dns (s_ip4) ? _("yes") : _("no"); - nmc->allowed_fields[8].value = nm_setting_ip4_config_get_dhcp_client_id (s_ip4); - nmc->allowed_fields[9].value = nm_setting_ip4_config_get_dhcp_send_hostname (s_ip4) ? _("yes") : _("no"); - nmc->allowed_fields[10].value = nm_setting_ip4_config_get_dhcp_hostname (s_ip4); - nmc->allowed_fields[11].value = nm_setting_ip4_config_get_never_default (s_ip4) ? _("yes") : _("no"); - nmc->allowed_fields[12].value = nm_setting_ip4_config_get_may_fail (s_ip4) ? _("yes") : _("no"); +/* 'permissions' */ +/* define from libnm-util/nm-setting-connection.c */ +#define PERM_USER_PREFIX "user:" - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ +static gboolean +permissions_valid (const char *perm) +{ + if (!perm || perm[0] == '\0') + return FALSE; - g_string_free (dns_str, TRUE); - g_string_free (dns_search_str, TRUE); - g_string_free (addr_str, TRUE); - g_string_free (route_str, TRUE); + if (strncmp (perm, PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0) { + if ( strlen (perm) <= strlen (PERM_USER_PREFIX) + || strchr (perm + strlen (PERM_USER_PREFIX), ':')) + return FALSE; + } else { + if (strchr (perm, ':')) + return FALSE; + } return TRUE; } -gboolean -setting_ip6_config_details (NMSettingIP6Config *s_ip6, NmCli *nmc) +static gboolean +nmc_property_connection_set_permissions (NMSetting *setting, const char *prop, const char *val, GError **error) { - GString *dns_str, *dns_search_str, *addr_str, *route_str; - char *ip6_privacy_str; - int i, num; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + char **strv = NULL; + guint i = 0; - g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (s_ip6), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - nmc->allowed_fields = nmc_fields_setting_ip6_config; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_IP6_CONFIG_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ + strv = nmc_strsplit_set (val, " \t,", 0); + if (!verify_string_list (strv, prop, permissions_valid, error)) { + g_strfreev (strv); + return FALSE; + } + + for (i = 0; strv && strv[i]; i++) { + const char *user; - dns_str = g_string_new (NULL); - num = nm_setting_ip6_config_get_num_dns (s_ip6); - for (i = 0; i < num; i++) { - char buf[INET6_ADDRSTRLEN]; - const struct in6_addr *ip; + if (strncmp (strv[i], PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0) + user = strv[i]+strlen (PERM_USER_PREFIX); + else + user = strv[i]; - ip = nm_setting_ip6_config_get_dns (s_ip6, i); - memset (buf, 0, sizeof (buf)); - inet_ntop (AF_INET6, (const void *) ip, buf, sizeof (buf)); - if (i > 0) - g_string_append (dns_str, ", "); - g_string_append (dns_str, buf); + nm_setting_connection_add_permission (NM_SETTING_CONNECTION (setting), "user", user, NULL); } - dns_search_str = g_string_new (NULL); - num = nm_setting_ip6_config_get_num_dns_searches (s_ip6); - for (i = 0; i < num; i++) { - const char *domain; + return TRUE; +} + +static gboolean +_validate_and_remove_connection_permission (NMSettingConnection *setting, + const char *perm, + GError **error) +{ + gboolean ret; + + ret = nm_setting_connection_remove_permission_by_value (setting, "user", perm, NULL); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain permission '%s'"), perm); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_connection_remove_permissions, + NM_SETTING_CONNECTION, + nm_setting_connection_get_num_permissions, + nm_setting_connection_remove_permission, + _validate_and_remove_connection_permission) + +static const char * +nmc_property_connection_describe_permissions (NMSetting *setting, const char *prop) +{ + return _("Enter a list of user permissions. This is a list of user names formatted as:\n" + " [user:]<user name 1>, [user:]<user name 2>,...\n" + "The items can be separated by commas or spaces.\n\n" + "Example: alice bob charlie\n"); +} + +/* 'master' */ +static gboolean +nmc_property_con_set_master (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!val) + ; + else if (!*val) + val = NULL; + else if ( !nm_utils_iface_valid_name (val) + && !nm_utils_is_uuid (val)) { + g_set_error (error, 1, 0, + _("'%s' is not valid master; use ifname or connection UUID"), + val); + return FALSE; + } + g_object_set (setting, prop, val, NULL); + return TRUE; +} + +/* 'slave-type' */ +static const char *con_valid_slave_types[] = { + NM_SETTING_BOND_SETTING_NAME, + NM_SETTING_BRIDGE_SETTING_NAME, + NM_SETTING_VLAN_SETTING_NAME, + NULL +}; + +static gboolean +nmc_property_con_set_slave_type (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, con_valid_slave_types, error); +} + + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_con_allowed_slave_type, con_valid_slave_types) - domain = nm_setting_ip6_config_get_dns_search (s_ip6, i); - if (i > 0) - g_string_append (dns_search_str, ", "); - g_string_append (dns_search_str, domain); +/* 'secondaries' */ +static gboolean +nmc_property_connection_set_secondaries (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + NMConnection *con; + char **strv = NULL, **iter; + guint i = 0; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, " \t,", 0); + for (iter = strv; iter && *iter; iter++) { + if (**iter == '\0') + continue; + + if (nm_utils_is_uuid (*iter)) { + con = nmc_find_connection (nm_cli.system_connections, + "uuid", *iter, NULL); + if (!con) + printf (_("Warning: %s is not an UUID of any existing connection profile\n"), *iter); + else { + /* Currenly NM only supports VPN connections as secondaries */ + if (!nm_connection_is_type (con, NM_SETTING_VPN_SETTING_NAME)) { + g_set_error (error, 1, 0, _("'%s' is not a VPN connection profile"), *iter); + g_strfreev (strv); + return FALSE; + } + } + } else { + con = nmc_find_connection (nm_cli.system_connections, + "id", *iter, NULL); + if (!con) { + g_set_error (error, 1, 0, _("'%s' is not a name of any exiting profile"), *iter); + g_strfreev (strv); + return FALSE; + } + + /* Currenly NM only supports VPN connections as secondaries */ + if (!nm_connection_is_type (con, NM_SETTING_VPN_SETTING_NAME)) { + g_set_error (error, 1, 0, _("'%s' is not a VPN connection profile"), *iter); + g_strfreev (strv); + return FALSE; + } + + /* translate id to uuid */ + g_free (*iter); + *iter = g_strdup (nm_connection_get_uuid (con)); + } } - addr_str = g_string_new (NULL); - num = nm_setting_ip6_config_get_num_addresses (s_ip6); - for (i = 0; i < num; i++) { - char buf[INET6_ADDRSTRLEN]; - char *tmp; - NMIP6Address *addr; - const struct in6_addr *ip; + while (strv && strv[i]) + nm_setting_connection_add_secondary (NM_SETTING_CONNECTION (setting), strv[i++]); + g_strfreev (strv); - if (i > 0) - g_string_append (addr_str, "; "); + return TRUE; +} - g_string_append (addr_str, "{ "); +static gboolean +_validate_and_remove_connection_secondary (NMSettingConnection *setting, + const char *secondary_uuid, + GError **error) +{ + gboolean ret; - addr = nm_setting_ip6_config_get_address (s_ip6, i); + if (!nm_utils_is_uuid (secondary_uuid)) { + g_set_error (error, 1, 0, + _("the value '%s' is not a valid UUID"), secondary_uuid); + return FALSE; + } - memset (buf, 0, sizeof (buf)); - ip = nm_ip6_address_get_address (addr); - inet_ntop (AF_INET6, (const void *) ip, buf, sizeof (buf)); - g_string_append_printf (addr_str, "ip = %s", buf); + ret = nm_setting_connection_remove_secondary_by_value (setting, secondary_uuid); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain UUID '%s'"), secondary_uuid); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_connection_remove_secondaries, + NM_SETTING_CONNECTION, + nm_setting_connection_get_num_secondaries, + nm_setting_connection_remove_secondary, + _validate_and_remove_connection_secondary) + +static const char * +nmc_property_connection_describe_secondaries (NMSetting *setting, const char *prop) +{ + return _("Enter secondary connections that should be activated when this connection is\n" + "activated. Connections can be specified either by UUID or ID (name). nmcli\n" + "transparently translates names to UUIDs. Note that NetworkManager only supports\n" + "VPNs as secondary connections at the moment.\n" + "The items can be separated by commas or spaces.\n\n" + "Example: private-openvpn, fe6ba5d8-c2fc-4aae-b2e3-97efddd8d9a7\n"); +} - tmp = g_strdup_printf ("/%u", nm_ip6_address_get_prefix (addr)); - g_string_append (addr_str, tmp); - g_free (tmp); +/* --- NM_SETTING_802_1X_SETTING_NAME property setter functions --- */ +#define DEFINE_SETTER_STR_LIST(def_func, set_func) \ + static gboolean \ + def_func (NMSetting *setting, const char *prop, const char *val, GError **error) \ + { \ + char **strv = NULL; \ + guint i = 0; \ + \ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); \ + \ + strv = nmc_strsplit_set (val, " \t,", 0); \ + while (strv && strv[i]) \ + set_func (NM_SETTING_802_1X (setting), strv[i++]); \ + g_strfreev (strv); \ + return TRUE; \ + } - memset (buf, 0, sizeof (buf)); - ip = nm_ip6_address_get_gateway (addr); - inet_ntop (AF_INET6, (const void *) ip, buf, sizeof (buf)); - g_string_append_printf (addr_str, ", gw = %s", buf); +#define DEFINE_SETTER_CERT(def_func, set_func) \ + static gboolean \ + def_func (NMSetting *setting, const char *prop, const char *val, GError **error) \ + { \ + const char *SCHEME_PATH = "file://"; \ + char *val_strip = g_strstrip (g_strdup (val)); \ + char *p = val_strip; \ + gboolean success; \ + \ + if (strncmp (val_strip, SCHEME_PATH, strlen (SCHEME_PATH)) == 0) \ + p += strlen (SCHEME_PATH); \ + \ + success = set_func (NM_SETTING_802_1X (setting), \ + p, \ + NM_SETTING_802_1X_CK_SCHEME_PATH, \ + NULL, \ + error); \ + g_free (val_strip); \ + return success; \ + } - g_string_append (addr_str, " }"); +#define DEFINE_SETTER_PRIV_KEY(def_func, pwd_func, set_func) \ + static gboolean \ + def_func (NMSetting *setting, const char *prop, const char *val, GError **error) \ + { \ + char **strv = NULL; \ + const char *SCHEME_PATH = "file://"; \ + char *val_strip = g_strstrip (g_strdup (val)); \ + char *p = val_strip; \ + const char *path, *password; \ + gboolean success; \ + \ + if (strncmp (val_strip, SCHEME_PATH, strlen (SCHEME_PATH)) == 0) \ + p += strlen (SCHEME_PATH); \ + \ + strv = nmc_strsplit_set (p, " \t,", 2); \ + path = strv[0]; \ + if (g_strv_length (strv) == 2) \ + password = strv[1]; \ + else \ + password = pwd_func (NM_SETTING_802_1X (setting)); \ + if (password) { \ + char *tmp_pwd = g_strdup (password); \ + success = set_func (NM_SETTING_802_1X (setting), \ + path, \ + tmp_pwd, \ + NM_SETTING_802_1X_CK_SCHEME_PATH, \ + NULL, \ + error); \ + g_free (tmp_pwd); \ + } else { \ + success = FALSE; \ + g_set_error_literal (error, 1, 0, _("private key password not provided")); \ + } \ + g_free (val_strip); \ + g_strfreev (strv); \ + return success; \ } - route_str = g_string_new (NULL); - num = nm_setting_ip6_config_get_num_routes (s_ip6); - for (i = 0; i < num; i++) { - char buf[INET6_ADDRSTRLEN]; - char *tmp; - NMIP6Route *route; - const struct in6_addr *ip; +/* 'eap' */ +DEFINE_SETTER_STR_LIST_MULTI (check_and_add_802_1X_eap, + NM_SETTING_802_1X, + nm_setting_802_1x_add_eap_method) +static gboolean +nmc_property_802_1X_set_eap (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + const char *valid_eap[] = { "leap", "md5", "tls", "peap", "ttls", "sim", "fast", "pwd", NULL }; + return check_and_add_802_1X_eap (setting, prop, val, valid_eap, error); +} + +static gboolean +_validate_and_remove_eap_method (NMSetting8021x *setting, + const char *eap, + GError **error) +{ + gboolean ret; + + ret = nm_setting_802_1x_remove_eap_method_by_value(setting, eap); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain EAP method '%s'"), eap); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_802_1X_remove_eap, + NM_SETTING_802_1X, + nm_setting_802_1x_get_num_eap_methods, + nm_setting_802_1x_remove_eap_method, + _validate_and_remove_eap_method) + +/* 'ca-cert' */ +DEFINE_SETTER_CERT (nmc_property_802_1X_set_ca_cert, nm_setting_802_1x_set_ca_cert) + +/* 'altsubject-matches' */ +DEFINE_SETTER_STR_LIST (nmc_property_802_1X_set_altsubject_matches, nm_setting_802_1x_add_altsubject_match) + +static gboolean +_validate_and_remove_altsubject_match (NMSetting8021x *setting, + const char *altsubject_match, + GError **error) +{ + gboolean ret; + + ret = nm_setting_802_1x_remove_altsubject_match_by_value (setting, altsubject_match); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain alternative subject match '%s'"), + altsubject_match); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_802_1X_remove_altsubject_matches, + NM_SETTING_802_1X, + nm_setting_802_1x_get_num_altsubject_matches, + nm_setting_802_1x_remove_altsubject_match, + _validate_and_remove_altsubject_match) + +/* 'client-cert' */ +DEFINE_SETTER_CERT (nmc_property_802_1X_set_client_cert, nm_setting_802_1x_set_client_cert) + +/* 'phase2-ca-cert' */ +DEFINE_SETTER_CERT (nmc_property_802_1X_set_phase2_ca_cert, nm_setting_802_1x_set_phase2_ca_cert) + +/* 'phase2-altsubject-matches' */ +DEFINE_SETTER_STR_LIST (nmc_property_802_1X_set_phase2_altsubject_matches, nm_setting_802_1x_add_phase2_altsubject_match) + +static gboolean +_validate_and_remove_phase2_altsubject_match (NMSetting8021x *setting, + const char *phase2_altsubject_match, + GError **error) +{ + gboolean ret; + + ret = nm_setting_802_1x_remove_phase2_altsubject_match_by_value (setting, phase2_altsubject_match); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain \"phase2\" alternative subject match '%s'"), + phase2_altsubject_match); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_802_1X_remove_phase2_altsubject_matches, + NM_SETTING_802_1X, + nm_setting_802_1x_get_num_phase2_altsubject_matches, + nm_setting_802_1x_remove_phase2_altsubject_match, + _validate_and_remove_phase2_altsubject_match) + +/* 'phase2-client-cert' */ +DEFINE_SETTER_CERT (nmc_property_802_1X_set_phase2_client_cert, nm_setting_802_1x_set_phase2_client_cert) + +/* 'private-key' */ +DEFINE_SETTER_PRIV_KEY (nmc_property_802_1X_set_private_key, + nm_setting_802_1x_get_private_key_password, + nm_setting_802_1x_set_private_key) + +/* 'phase2-private-key' */ +DEFINE_SETTER_PRIV_KEY (nmc_property_802_1X_set_phase2_private_key, + nm_setting_802_1x_get_phase2_private_key_password, + nm_setting_802_1x_set_phase2_private_key) + +static const char * +nmc_property_802_1X_describe_private_key (NMSetting *setting, const char *prop) +{ + return _("Enter path to a private key and the key password (if not set yet):\n" + " <file path> [<password>]\n" + "Example: /home/cimrman/jara-priv-key Dardanely\n"); +} + +/* 'phase1-peapver' */ +static const char *_802_1X_valid_phase1_peapvers[] = { "0", "1", NULL }; + +static gboolean +nmc_property_802_1X_set_phase1_peapver (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, _802_1X_valid_phase1_peapvers, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_802_1X_allowed_phase1_peapver, _802_1X_valid_phase1_peapvers) + +/* 'phase1-peaplabel' */ +static const char *_802_1X_valid_phase1_peaplabels[] = { "0", "1", NULL }; + +static gboolean +nmc_property_802_1X_set_phase1_peaplabel (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, _802_1X_valid_phase1_peaplabels, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_802_1X_allowed_phase1_peaplabel, _802_1X_valid_phase1_peaplabels) + +/* 'phase1-fast-provisioning' */ +static const char *_802_1X_valid_phase1_fast_provisionings[] = { "0", "1", "2", "3", NULL }; + +static gboolean +nmc_property_802_1X_set_phase1_fast_provisioning (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, _802_1X_valid_phase1_fast_provisionings, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_802_1X_allowed_phase1_fast_provisioning, _802_1X_valid_phase1_fast_provisionings) + +/* 'phase2-auth' */ +static const char *_802_1X_valid_phase2_auths[] = + { "pap", "chap", "mschap", "mschapv2", "gtc", "otp", "md5", "tls", NULL }; + +static gboolean +nmc_property_802_1X_set_phase2_auth (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, _802_1X_valid_phase2_auths, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_802_1X_allowed_phase2_auth, _802_1X_valid_phase2_auths) + +/* 'phase2-autheap' */ +static const char *_802_1X_valid_phase2_autheaps[] = { "md5", "mschapv2", "otp", "gtc", "tls", NULL }; +static gboolean +nmc_property_802_1X_set_phase2_autheap (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, _802_1X_valid_phase2_autheaps, error); +} - if (i > 0) - g_string_append (route_str, "; "); +DEFINE_ALLOWED_VAL_FUNC (nmc_property_802_1X_allowed_phase2_autheap, _802_1X_valid_phase2_autheaps) - g_string_append (route_str, "{ "); +/* 'password-raw' */ +static gboolean +nmc_property_802_1X_set_password_raw (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return nmc_property_set_byte_array (setting, prop, val, error); +} - route = nm_setting_ip6_config_get_route (s_ip6, i); +static const char * +nmc_property_802_1X_describe_password_raw (NMSetting *setting, const char *prop) +{ + return _("Enter bytes as a list of hexadecimal values.\n" + "Two formats are accepted:\n" + "(a) a string of hexadecimal digits, where each two digits represent one byte\n" + "(b) space-separated list of bytes written as hexadecimal digits " + "(with optional 0x/0X prefix, and optional leading 0).\n\n" + "Examples: ab0455a6ea3a74C2\n" + " ab 4 55 0xa6 ea 3a 74 C2\n"); +} - memset (buf, 0, sizeof (buf)); - ip = nm_ip6_route_get_dest (route); - inet_ntop (AF_INET6, (const void *) ip, buf, sizeof (buf)); - g_string_append_printf (route_str, "dst = %s", buf); +/* --- NM_SETTING_ADSL_SETTING_NAME property setter functions --- */ +/* 'protocol' */ +static const char *adsl_valid_protocols[] = { + NM_SETTING_ADSL_PROTOCOL_PPPOA, + NM_SETTING_ADSL_PROTOCOL_PPPOE, + NM_SETTING_ADSL_PROTOCOL_IPOATM, + NULL +}; - tmp = g_strdup_printf ("/%u", nm_ip6_route_get_prefix (route)); - g_string_append (route_str, tmp); - g_free (tmp); +static gboolean +nmc_property_adsl_set_protocol (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, adsl_valid_protocols, error); +} - memset (buf, 0, sizeof (buf)); - ip = nm_ip6_route_get_next_hop (route); - inet_ntop (AF_INET6, (const void *) ip, buf, sizeof (buf)); - g_string_append_printf (route_str, ", nh = %s", buf); +DEFINE_ALLOWED_VAL_FUNC (nmc_property_adsl_allowed_protocol, adsl_valid_protocols) - tmp = g_strdup_printf (", mt = %u", nm_ip6_route_get_metric (route)); - g_string_append (route_str, tmp); - g_free (tmp); +/* 'encapsulation' */ +static const char *adsl_valid_encapsulations[] = { + NM_SETTING_ADSL_ENCAPSULATION_VCMUX, + NM_SETTING_ADSL_ENCAPSULATION_LLC, + NULL +}; - g_string_append (route_str, " }"); +static gboolean +nmc_property_adsl_set_encapsulation (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, adsl_valid_encapsulations, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_adsl_allowed_encapsulation, adsl_valid_encapsulations) + +/* --- NM_SETTING_BLUETOOTH_SETTING_NAME property setter functions --- */ +/* 'type' */ +static gboolean +nmc_property_bluetooth_set_type (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + const char *types[] = { + NM_SETTING_BLUETOOTH_TYPE_DUN, + NM_SETTING_BLUETOOTH_TYPE_PANU, + NULL }; + + return check_and_set_string (setting, prop, val, types, error); +} + +/* --- NM_SETTING_BOND_SETTING_NAME property setter functions --- */ +/* 'options' */ +/* example: miimon=100,mode=balance-rr, updelay=5 */ +static gboolean +_validate_and_remove_bond_option (NMSettingBond *setting, const char *option) +{ + const char *opt; + const char **valid_options; + + valid_options = nm_setting_bond_get_valid_options (setting); + opt = nmc_string_is_valid (option, valid_options, NULL); + + if (opt) + return nm_setting_bond_remove_option (setting, opt); + else + return FALSE; +} + +/* Validate bonding 'options' values */ +static const char * +_validate_bond_option_value (const char *option, const char *value, GError **error) +{ + if (!g_strcmp0 (option, NM_SETTING_BOND_OPTION_MODE)) + return nmc_bond_validate_mode (value, error); + + return value; +} + +DEFINE_SETTER_OPTIONS (nmc_property_bond_set_options, + NM_SETTING_BOND, + NMSettingBond, + nm_setting_bond_add_option, + nm_setting_bond_get_valid_options, + _validate_bond_option_value) +DEFINE_REMOVER_OPTION (nmc_property_bond_remove_option_options, + NM_SETTING_BOND, + _validate_and_remove_bond_option) + +static const char * +nmc_property_bond_describe_options (NMSetting *setting, const char *prop) +{ + static char *desc = NULL; + const char **valid_options; + char *options_str; + + if (G_UNLIKELY (desc == NULL)) { + valid_options = nm_setting_bond_get_valid_options (NM_SETTING_BOND (setting)); + options_str = g_strjoinv (", ", (char **) valid_options); + + desc = g_strdup_printf (_("Enter a list of bonding options formatted as:\n" + " option = <value>, option = <value>,... \n" + "Valid options are: %s\n" + "'mode' can be provided as a name or a number:\n" + "balance-rr = 0\n" + "active-backup = 1\n" + "balance-xor = 2\n" + "broadcast = 3\n" + "802.3ad = 4\n" + "balance-tlb = 5\n" + "balance-alb = 6\n\n" + "Example: mode=2,miimon=120\n"), options_str); + g_free (options_str); } + return desc; +} + +static const char * +nmc_property_bond_allowed_options (NMSetting *setting, const char *prop) +{ + const char **valid_options; + static char *allowed_vals = NULL; - ip6_privacy_str = ip6_privacy_to_string (nm_setting_ip6_config_get_ip6_privacy (s_ip6)); + if (G_UNLIKELY (allowed_vals == NULL)) { + valid_options = nm_setting_bond_get_valid_options (NM_SETTING_BOND (setting)); + allowed_vals = g_strjoinv (", ", (char **) valid_options); + } + return allowed_vals; +} - nmc->allowed_fields[0].value = NM_SETTING_IP6_CONFIG_SETTING_NAME; - nmc->allowed_fields[1].value = nm_setting_ip6_config_get_method (s_ip6); - nmc->allowed_fields[2].value = dns_str->str; - nmc->allowed_fields[3].value = dns_search_str->str; - nmc->allowed_fields[4].value = addr_str->str; - nmc->allowed_fields[5].value = route_str->str; - nmc->allowed_fields[6].value = nm_setting_ip6_config_get_ignore_auto_routes (s_ip6) ? _("yes") : _("no"); - nmc->allowed_fields[7].value = nm_setting_ip6_config_get_ignore_auto_dns (s_ip6) ? _("yes") : _("no"); - nmc->allowed_fields[8].value = nm_setting_ip6_config_get_never_default (s_ip6) ? _("yes") : _("no"); - nmc->allowed_fields[9].value = nm_setting_ip6_config_get_may_fail (s_ip6) ? _("yes") : _("no"); - nmc->allowed_fields[10].value = ip6_privacy_str; - nmc->allowed_fields[11].value = nm_setting_ip6_config_get_dhcp_hostname (s_ip6); +/* --- NM_SETTING_INFINIBAND_SETTING_NAME property setter functions --- */ +/* 'mac-addresss' */ +static gboolean +nmc_property_ib_set_mac (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + GByteArray *array; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - g_string_free (dns_str, TRUE); - g_string_free (dns_search_str, TRUE); - g_string_free (addr_str, TRUE); - g_string_free (route_str, TRUE); - g_free (ip6_privacy_str); + array = nm_utils_hwaddr_atoba (val, ARPHRD_INFINIBAND); + if (!array) { + g_set_error (error, 1, 0, _("'%s' is not a valid InfiniBand MAC"), val); + return FALSE; + } + g_object_set (setting, prop, array, NULL); + g_byte_array_free (array, TRUE); return TRUE; } -gboolean -setting_serial_details (NMSettingSerial *s_serial, NmCli *nmc) +/* 'transport-mode' */ +static const char *ib_valid_transport_modes[] = { "datagram", "connected", NULL }; + +static gboolean +nmc_property_ib_set_transport_mode (NMSetting *setting, const char *prop, const char *val, GError **error) { - char *baud_str, *bits_str, *parity_str, *stopbits_str, *send_delay_str; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + return check_and_set_string (setting, prop, val, ib_valid_transport_modes, error); +} - g_return_val_if_fail (NM_IS_SETTING_SERIAL (s_serial), FALSE); +DEFINE_ALLOWED_VAL_FUNC (nmc_property_ib_allowed_transport_mode, ib_valid_transport_modes) - nmc->allowed_fields = nmc_fields_setting_serial; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_SERIAL_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ +/* 'p-key' */ +static gboolean +nmc_property_ib_set_p_key (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + gboolean p_key_valid = FALSE; + long p_key_int; - baud_str = g_strdup_printf ("%d", nm_setting_serial_get_baud (s_serial)); - bits_str = g_strdup_printf ("%d", nm_setting_serial_get_bits (s_serial)); - parity_str = g_strdup_printf ("%c", nm_setting_serial_get_parity (s_serial)); - stopbits_str = g_strdup_printf ("%d", nm_setting_serial_get_stopbits (s_serial)); - send_delay_str = g_strdup_printf ("%" G_GUINT64_FORMAT, nm_setting_serial_get_send_delay (s_serial)); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - nmc->allowed_fields[0].value = NM_SETTING_SERIAL_SETTING_NAME; - nmc->allowed_fields[1].value = baud_str; - nmc->allowed_fields[2].value = bits_str; - nmc->allowed_fields[3].value = parity_str; - nmc->allowed_fields[4].value = stopbits_str; - nmc->allowed_fields[5].value = send_delay_str; + if (!strncasecmp (val, "0x", 2)) + p_key_valid = nmc_string_to_int_base (val + 2, 16, TRUE, 0, G_MAXUINT16, &p_key_int); + else + p_key_valid = nmc_string_to_int (val, TRUE, -1, G_MAXUINT16, &p_key_int); + + if (!p_key_valid) { + if (strcmp (val, "default") == 0) + p_key_int = -1; + else { + g_set_error (error, 1, 0, _("'%s' is not a valid IBoIP P_Key"), val); + return FALSE; + } + } + g_object_set (setting, prop, (gint) p_key_int, NULL); + return TRUE; +} - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ +/* --- NM_SETTING_IP4_CONFIG_SETTING_NAME property setter functions --- */ +/* 'method' */ +static const char *ipv4_valid_methods[] = { + NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP4_CONFIG_METHOD_SHARED, + NM_SETTING_IP4_CONFIG_METHOD_DISABLED, + NULL +}; - g_free (baud_str); - g_free (bits_str); - g_free (parity_str); - g_free (stopbits_str); - g_free (send_delay_str); +static gboolean +nmc_property_ipv4_set_method (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + /* Silently accept "static" and convert to "manual" */ + if (val && strlen (val) > 1 && matches (val, "static") == 0) + val = NM_SETTING_IP4_CONFIG_METHOD_MANUAL; + return check_and_set_string (setting, prop, val, ipv4_valid_methods, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_ipv4_allowed_method, ipv4_valid_methods) + +/* 'dns' */ +static gboolean +nmc_property_ipv4_set_dns (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter; + guint32 ip4_addr; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, " \t,", 0); + for (iter = strv; iter && *iter; iter++) { + if (inet_pton (AF_INET, g_strstrip (*iter), &ip4_addr) < 1) { + g_set_error (error, 1, 0, _("invalid IPv4 address '%s'"), *iter); + g_strfreev (strv); + return FALSE; + } + nm_setting_ip4_config_add_dns (NM_SETTING_IP4_CONFIG (setting), ip4_addr); + } + g_strfreev (strv); return TRUE; } -gboolean -setting_ppp_details (NMSettingPPP *s_ppp, NmCli *nmc) +static gboolean +_validate_and_remove_ipv4_dns (NMSettingIP4Config *setting, + const char *dns, + GError **error) { - char *baud_str, *mru_str, *mtu_str, *lcp_echo_failure_str, *lcp_echo_interval_str; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + guint32 ip4_addr; + gboolean ret; - g_return_val_if_fail (NM_IS_SETTING_PPP (s_ppp), FALSE); + if (inet_pton (AF_INET, dns, &ip4_addr) < 1) { + g_set_error (error, 1, 0, _("invalid IPv4 address '%s'"), dns); + return FALSE; + } - nmc->allowed_fields = nmc_fields_setting_ppp; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_PPP_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ - - baud_str = g_strdup_printf ("%d", nm_setting_ppp_get_baud (s_ppp)); - mru_str = g_strdup_printf ("%d", nm_setting_ppp_get_mru (s_ppp)); - mtu_str = g_strdup_printf ("%d", nm_setting_ppp_get_mtu (s_ppp)); - lcp_echo_failure_str = g_strdup_printf ("%d", nm_setting_ppp_get_lcp_echo_failure (s_ppp)); - lcp_echo_interval_str = g_strdup_printf ("%d", nm_setting_ppp_get_lcp_echo_interval (s_ppp)); - - nmc->allowed_fields[0].value = NM_SETTING_PPP_SETTING_NAME; - nmc->allowed_fields[1].value = nm_setting_ppp_get_noauth (s_ppp) ? _("yes") : _("no"); - nmc->allowed_fields[2].value = nm_setting_ppp_get_refuse_eap (s_ppp) ? _("yes") : _("no"); - nmc->allowed_fields[3].value = nm_setting_ppp_get_refuse_pap (s_ppp) ? _("yes") : _("no"); - nmc->allowed_fields[4].value = nm_setting_ppp_get_refuse_chap (s_ppp) ? _("yes") : _("no"); - nmc->allowed_fields[5].value = nm_setting_ppp_get_refuse_mschap (s_ppp) ? _("yes") : _("no"); - nmc->allowed_fields[6].value = nm_setting_ppp_get_refuse_mschapv2 (s_ppp) ? _("yes") : _("no"); - nmc->allowed_fields[7].value = nm_setting_ppp_get_nobsdcomp (s_ppp) ? _("yes") : _("no"); - nmc->allowed_fields[8].value = nm_setting_ppp_get_nodeflate (s_ppp) ? _("yes") : _("no"); - nmc->allowed_fields[9].value = nm_setting_ppp_get_no_vj_comp (s_ppp) ? _("yes") : _("no"); - nmc->allowed_fields[10].value = nm_setting_ppp_get_require_mppe (s_ppp) ? _("yes") : _("no"); - nmc->allowed_fields[11].value = nm_setting_ppp_get_require_mppe_128 (s_ppp) ? _("yes") : _("no"); - nmc->allowed_fields[12].value = nm_setting_ppp_get_mppe_stateful (s_ppp) ? _("yes") : _("no"); - nmc->allowed_fields[13].value = nm_setting_ppp_get_crtscts (s_ppp) ? _("yes") : _("no"); - nmc->allowed_fields[14].value = baud_str; - nmc->allowed_fields[15].value = mru_str; - nmc->allowed_fields[16].value = mtu_str; - nmc->allowed_fields[17].value = lcp_echo_failure_str; - nmc->allowed_fields[18].value = lcp_echo_interval_str; - - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ - - g_free (baud_str); - g_free (mru_str); - g_free (mtu_str); - g_free (lcp_echo_failure_str); - g_free (lcp_echo_interval_str); + ret = nm_setting_ip4_config_remove_dns_by_value (setting, ip4_addr); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain DNS server '%s'"), dns); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_ipv4_remove_dns, + NM_SETTING_IP4_CONFIG, + nm_setting_ip4_config_get_num_dns, + nm_setting_ip4_config_remove_dns, + _validate_and_remove_ipv4_dns) + +static const char * +nmc_property_ipv4_describe_dns (NMSetting *setting, const char *prop) +{ + return _("Enter a list of IPv4 addresses of DNS servers.\n\n" + "Example: 8.8.8.8, 8.8.4.4\n"); +} + +/* 'dns-search' */ +static gboolean +nmc_property_ipv4_set_dns_search (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL; + guint i = 0; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, " \t,", 0); + if (!verify_string_list (strv, prop, nmc_util_is_domain, error)) { + g_strfreev (strv); + return FALSE; + } + + while (strv && strv[i]) + nm_setting_ip4_config_add_dns_search (NM_SETTING_IP4_CONFIG (setting), strv[i++]); + g_strfreev (strv); return TRUE; } -gboolean -setting_pppoe_details (NMSettingPPPOE *s_pppoe, NmCli *nmc) +static gboolean +_validate_and_remove_ipv4_dns_search (NMSettingIP4Config *setting, + const char *dns_search, + GError **error) { - char *pswd_flags; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + gboolean ret; + + ret = nm_setting_ip4_config_remove_dns_search_by_value (setting, dns_search); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain DNS search domain '%s'"), + dns_search); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_ipv4_remove_dns_search, + NM_SETTING_IP4_CONFIG, + nm_setting_ip4_config_get_num_dns_searches, + nm_setting_ip4_config_remove_dns_search, + _validate_and_remove_ipv4_dns_search) + +/* 'addresses' */ +static NMIP4Address * +_parse_ipv4_address (const char *address, GError **error) +{ + char *value = g_strdup (address); + char **addrv; + NMIP4Address *ip4addr; + + addrv = nmc_strsplit_set (g_strstrip (value), " \t", 0); + if (addrv[0] == NULL || g_strv_length (addrv) > 2) { + g_set_error (error, 1, 0, _("'%s' is not valid (use ip[/prefix] [gateway])"), + address); + g_free (value); + g_strfreev (addrv); + return NULL; + } + ip4addr = nmc_parse_and_build_ip4_address (addrv[0], addrv[1], error); + g_free (value); + g_strfreev (addrv); + return ip4addr; +} - g_return_val_if_fail (NM_IS_SETTING_PPPOE (s_pppoe), FALSE); +static gboolean +nmc_property_ipv4_set_addresses (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter; + NMIP4Address *ip4addr; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, ",", 0); + for (iter = strv; iter && *iter; iter++) { + ip4addr = _parse_ipv4_address (*iter, error); + if (!ip4addr) { + g_strfreev (strv); + return FALSE; + } + nm_setting_ip4_config_add_address (NM_SETTING_IP4_CONFIG (setting), ip4addr); + nm_ip4_address_unref (ip4addr); + } + g_strfreev (strv); + return TRUE; +} + +static gboolean +_validate_and_remove_ipv4_address (NMSettingIP4Config *setting, + const char *address, + GError **error) +{ + NMIP4Address *ip4addr; + gboolean ret; - nmc->allowed_fields = nmc_fields_setting_pppoe; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_PPPOE_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ + ip4addr = _parse_ipv4_address (address, error); + if (!ip4addr) + return FALSE; - pswd_flags = secret_flags_to_string (nm_setting_pppoe_get_password_flags (s_pppoe)); + ret = nm_setting_ip4_config_remove_address_by_value (setting, ip4addr); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain IP address '%s'"), address); + nm_ip4_address_unref (ip4addr); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_ipv4_remove_addresses, + NM_SETTING_IP4_CONFIG, + nm_setting_ip4_config_get_num_addresses, + nm_setting_ip4_config_remove_address, + _validate_and_remove_ipv4_address) + +static const char * +nmc_property_ipv4_describe_addresses (NMSetting *setting, const char *prop) +{ + return _("Enter a list of IPv4 addresses formatted as:\n" + " ip[/prefix] [gateway], ip[/prefix] [gateway],...\n" + "Missing prefix is regarded as prefix of 32.\n\n" + "Example: 192.168.1.5/24 192.168.1.1, 10.0.0.11/24\n"); +} - nmc->allowed_fields[0].value = NM_SETTING_PPPOE_SETTING_NAME; - nmc->allowed_fields[1].value = nm_setting_pppoe_get_service (s_pppoe); - nmc->allowed_fields[2].value = nm_setting_pppoe_get_username (s_pppoe); - nmc->allowed_fields[3].value = nm_setting_pppoe_get_password (s_pppoe); - nmc->allowed_fields[4].value = pswd_flags; +/* + * from: { ip = 1.2.3.4/24, gw = 1.2.3.254 }; { ip = 2.2.2.2/16, gw = 5.5.5.5 } + * to: 1.2.3.4/24 1.2.3.254, 2.2.2.2/16 5.5.5.5 + * from: { ip = 11::22/64, gw = 22::33 }; { ip = ab::cd/64, gw = ab::1 } + * to: 11::22/64 22:33, ab::cd/64 ab::1 +*/ +static char * +nmc_property_out2in_addresses (const char *out_format) +{ + GRegex *regex; + GString *str; + char **strv; + int i; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + str = g_string_sized_new (128); + regex = g_regex_new ("\\{ ip = ([^/]+)/([^,]+), gw = ([^ ]+) \\}", 0, 0, NULL); + + strv = g_regex_split (regex, out_format, 0); + for (i = 1; strv && strv[i] && strv[i+1] && strv[i+2]; i=i+4) { + g_string_append (str, strv[i]); /* IP */ + g_string_append_c (str, '/'); + g_string_append (str, strv[i+1]); /* prefix */ + g_string_append_c (str, ' '); + g_string_append (str, strv[i+2]); /* gateway */ + g_string_append (str, ", "); + } + if (str->len > 0) + g_string_truncate (str, str->len - 2); - g_free (pswd_flags); + g_strfreev (strv); + g_regex_unref (regex); + return g_string_free (str, FALSE); +} + +/* 'routes' */ +static NMIP4Route * +_parse_ipv4_route (const char *route, GError **error) +{ + char *value = g_strdup (route); + char **routev; + guint len; + NMIP4Route *ip4route = NULL; + + routev = nmc_strsplit_set (g_strstrip (value), " \t", 0); + len = g_strv_length (routev); + if (len < 1 || len > 3) { + g_set_error (error, 1, 0, _("'%s' is not valid (the format is: ip[/prefix] [next-hop] [metric])"), + route); + goto finish; + } + ip4route = nmc_parse_and_build_ip4_route (routev[0], routev[1], len >= 2 ? routev[2] : NULL, error); + +finish: + g_free (value); + g_strfreev (routev); + return ip4route; +} + +static gboolean +nmc_property_ipv4_set_routes (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter; + NMIP4Route *ip4route; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, ",", 0); + for (iter = strv; iter && *iter; iter++) { + ip4route = _parse_ipv4_route (*iter, error); + if (!ip4route) { + g_strfreev (strv); + return FALSE; + } + nm_setting_ip4_config_add_route (NM_SETTING_IP4_CONFIG (setting), ip4route); + nm_ip4_route_unref (ip4route); + } + g_strfreev (strv); return TRUE; } -gboolean -setting_gsm_details (NMSettingGsm *s_gsm, NmCli *nmc) +static gboolean +_validate_and_remove_ipv4_route (NMSettingIP4Config *setting, + const char *route, + GError **error) { - char *network_type_str, *allowed_bands_str, *pswd_flags, *pin_flags; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + NMIP4Route *ip4route; + gboolean ret; - g_return_val_if_fail (NM_IS_SETTING_GSM (s_gsm), FALSE); + ip4route = _parse_ipv4_route (route, error); + if (!ip4route) + return FALSE; + + ret = nm_setting_ip4_config_remove_route_by_value (setting, ip4route); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain route '%s'"), route); + nm_ip4_route_unref (ip4route); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_ipv4_remove_routes, + NM_SETTING_IP4_CONFIG, + nm_setting_ip4_config_get_num_routes, + nm_setting_ip4_config_remove_route, + _validate_and_remove_ipv4_route) + +static const char * +nmc_property_ipv4_describe_routes (NMSetting *setting, const char *prop) +{ + return _("Enter a list of IPv4 routes formatted as:\n" + " ip[/prefix] [next-hop] [metric],...\n\n" + "Missing prefix is regarded as a prefix of 32.\n" + "Missing next-hop is regarded as 0.0.0.0.\n" + "Missing metric or 0 means a default metric (NM/kernel will set a default value).\n\n" + "Examples: 192.168.2.0/24 192.168.2.1 3, 10.1.0.0/16 10.0.0.254\n" + " 10.1.2.0/24\n"); +} + +static char * +nmc_property_out2in_routes (const char *out_format) +{ + GRegex *regex; + GString *str; + char **strv; + int i; - nmc->allowed_fields = nmc_fields_setting_gsm; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_GSM_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ + str = g_string_sized_new (128); + regex = g_regex_new ("\\{ dst = ([^/]+)/([^,]+), nh = ([^,]+), mt = ([^ ]+) \\}", 0, 0, NULL); + + strv = g_regex_split (regex, out_format, 0); + for (i = 1; strv && strv[i] && strv[i+1] && strv[i+2] && strv[i+3]; i=i+5) { + g_string_append (str, strv[i]); /* IP */ + g_string_append_c (str, '/'); + g_string_append (str, strv[i+1]); /* prefix */ + g_string_append_c (str, ' '); + g_string_append (str, strv[i+2]); /* next hop */ + g_string_append_c (str, ' '); + g_string_append (str, strv[i+3]); /* metric */ + g_string_append (str, ", "); + } + if (str->len > 0) + g_string_truncate (str, str->len - 2); - network_type_str = g_strdup_printf ("%d", nm_setting_gsm_get_network_type (s_gsm)); - allowed_bands_str = allowed_bands_to_string (nm_setting_gsm_get_allowed_bands (s_gsm)); + g_strfreev (strv); + g_regex_unref (regex); - pswd_flags = secret_flags_to_string (nm_setting_gsm_get_password_flags (s_gsm)); - pin_flags = secret_flags_to_string (nm_setting_gsm_get_pin_flags (s_gsm)); + return g_string_free (str, FALSE); +} - nmc->allowed_fields[0].value = NM_SETTING_GSM_SETTING_NAME; - nmc->allowed_fields[1].value = nm_setting_gsm_get_number (s_gsm); - nmc->allowed_fields[2].value = nm_setting_gsm_get_username (s_gsm); - nmc->allowed_fields[3].value = nm_setting_gsm_get_password (s_gsm); - nmc->allowed_fields[4].value = pswd_flags; - nmc->allowed_fields[5].value = nm_setting_gsm_get_apn (s_gsm); - nmc->allowed_fields[6].value = nm_setting_gsm_get_network_id (s_gsm); - nmc->allowed_fields[7].value = network_type_str; - nmc->allowed_fields[8].value = allowed_bands_str; - nmc->allowed_fields[9].value = nm_setting_gsm_get_pin (s_gsm); - nmc->allowed_fields[10].value = pin_flags; - nmc->allowed_fields[11].value = nm_setting_gsm_get_home_only (s_gsm) ? _("yes") : _("no"); +/* --- NM_SETTING_IP6_CONFIG_SETTING_NAME property setter functions --- */ +/* 'method' */ +static const char *ipv6_valid_methods[] = { + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_DHCP, + NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + NM_SETTING_IP6_CONFIG_METHOD_SHARED, + NULL +}; + +static gboolean +nmc_property_ipv6_set_method (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + /* Silently accept "static" and convert to "manual" */ + if (val && strlen (val) > 1 && matches (val, "static") == 0) + val = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + return check_and_set_string (setting, prop, val, ipv6_valid_methods, error); +} - g_free (network_type_str); - g_free (allowed_bands_str); - g_free (pswd_flags); - g_free (pin_flags); +DEFINE_ALLOWED_VAL_FUNC (nmc_property_ipv6_allowed_method, ipv6_valid_methods) +/* 'dns' */ +static gboolean +nmc_property_ipv6_set_dns (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter; + struct in6_addr ip6_addr; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, " \t,", 0); + for (iter = strv; iter && *iter; iter++) { + if (inet_pton (AF_INET6, g_strstrip (*iter), &ip6_addr) < 1) { + g_set_error (error, 1, 0, _("invalid IPv6 address '%s'"), *iter); + g_strfreev (strv); + return FALSE; + } + nm_setting_ip6_config_add_dns (NM_SETTING_IP6_CONFIG (setting), &ip6_addr); + } + g_strfreev (strv); return TRUE; } -gboolean -setting_cdma_details (NMSettingCdma *s_cdma, NmCli *nmc) +static gboolean +_validate_and_remove_ipv6_dns (NMSettingIP6Config *setting, + const char *dns, + GError **error) { - char *pswd_flags; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + struct in6_addr ip6_addr; + gboolean ret; - g_return_val_if_fail (NM_IS_SETTING_CDMA (s_cdma), FALSE); + if (inet_pton (AF_INET6, dns, &ip6_addr) < 1) { + g_set_error (error, 1, 0, _("invalid IPv6 address '%s'"), dns); + return FALSE; + } - nmc->allowed_fields = nmc_fields_setting_cdma; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_CDMA_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ + ret = nm_setting_ip6_config_remove_dns_by_value (setting, &ip6_addr); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain DNS server '%s'"), dns); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_ipv6_remove_dns, + NM_SETTING_IP6_CONFIG, + nm_setting_ip6_config_get_num_dns, + nm_setting_ip6_config_remove_dns, + _validate_and_remove_ipv6_dns) + +static const char * +nmc_property_ipv6_describe_dns (NMSetting *setting, const char *prop) +{ + return _("Enter a list of IPv6 addresses of DNS servers. If the IPv6 " + "configuration method is 'auto' these DNS servers are appended " + "to those (if any) returned by automatic configuration. DNS " + "servers cannot be used with the 'shared' or 'link-local' IPv6 " + "configuration methods, as there is no upstream network. In " + "all other IPv6 configuration methods, these DNS " + "servers are used as the only DNS servers for this connection.\n\n" + "Example: 2607:f0d0:1002:51::4, 2607:f0d0:1002:51::1\n"); +} - pswd_flags = secret_flags_to_string (nm_setting_cdma_get_password_flags (s_cdma)); +/* 'dns-search' */ +static gboolean +nmc_property_ipv6_set_dns_search (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL; + guint i = 0; - nmc->allowed_fields[0].value = NM_SETTING_CDMA_SETTING_NAME; - nmc->allowed_fields[1].value = nm_setting_cdma_get_number (s_cdma); - nmc->allowed_fields[2].value = nm_setting_cdma_get_username (s_cdma); - nmc->allowed_fields[3].value = nm_setting_cdma_get_password (s_cdma); - nmc->allowed_fields[4].value = pswd_flags; + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + strv = nmc_strsplit_set (val, " \t,", 0); + if (!verify_string_list (strv, prop, nmc_util_is_domain, error)) { + g_strfreev (strv); + return FALSE; + } - g_free (pswd_flags); + while (strv && strv[i]) + nm_setting_ip6_config_add_dns_search (NM_SETTING_IP6_CONFIG (setting), strv[i++]); + g_strfreev (strv); return TRUE; } -gboolean -setting_bluetooth_details (NMSettingBluetooth *s_bluetooth, NmCli *nmc) +static gboolean +_validate_and_remove_ipv6_dns_search (NMSettingIP6Config *setting, + const char *dns_search, + GError **error) { - const GByteArray *bdaddr; - char *bdaddr_str = NULL; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + gboolean ret; + + ret = nm_setting_ip6_config_remove_dns_search_by_value (setting, dns_search); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain DNS search domain '%s'"), + dns_search); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_ipv6_remove_dns_search, + NM_SETTING_IP6_CONFIG, + nm_setting_ip6_config_get_num_dns_searches, + nm_setting_ip6_config_remove_dns_search, + _validate_and_remove_ipv6_dns_search) + +/* 'addresses' */ +static NMIP6Address * +_parse_ipv6_address (const char *address, GError **error) +{ + char *value = g_strstrip (g_strdup (address)); + char **addrv; + NMIP6Address *ip6addr; + + addrv = nmc_strsplit_set (g_strstrip (value), " \t", 0); + if (addrv[0] == NULL || g_strv_length (addrv) > 2) { + g_set_error (error, 1, 0, _("'%s' is not valid (use ip[/prefix] [gateway])"), + address); + g_free (value); + g_strfreev (addrv); + return NULL; + } + ip6addr = nmc_parse_and_build_ip6_address (addrv[0], addrv[1], error); + g_free (value); + g_strfreev (addrv); + return ip6addr; +} - g_return_val_if_fail (NM_IS_SETTING_BLUETOOTH (s_bluetooth), FALSE); +static gboolean +nmc_property_ipv6_set_addresses (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter; + NMIP6Address *ip6addr; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, ",", 0); + for (iter = strv; iter && *iter; iter++) { + ip6addr = _parse_ipv6_address (*iter, error); + if (!ip6addr) { + g_strfreev (strv); + return FALSE; + } + nm_setting_ip6_config_add_address (NM_SETTING_IP6_CONFIG (setting), ip6addr); + nm_ip6_address_unref (ip6addr); + } + g_strfreev (strv); + return TRUE; +} - nmc->allowed_fields = nmc_fields_setting_bluetooth; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_BLUETOOTH_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ +static gboolean +_validate_and_remove_ipv6_address (NMSettingIP6Config *setting, + const char *address, + GError **error) +{ + NMIP6Address *ip6addr; + gboolean ret; - bdaddr = nm_setting_bluetooth_get_bdaddr (s_bluetooth); - if (bdaddr) - bdaddr_str = nm_utils_hwaddr_ntoa (bdaddr->data, ARPHRD_ETHER); + ip6addr = _parse_ipv6_address (address, error); + if (!ip6addr) + return FALSE; - nmc->allowed_fields[0].value = NM_SETTING_BLUETOOTH_SETTING_NAME; - nmc->allowed_fields[1].value = bdaddr_str; - nmc->allowed_fields[2].value = nm_setting_bluetooth_get_connection_type (s_bluetooth); + ret = nm_setting_ip6_config_remove_address_by_value (setting, ip6addr); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain IP address '%s'"), address); + nm_ip6_address_unref (ip6addr); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_ipv6_remove_addresses, + NM_SETTING_IP6_CONFIG, + nm_setting_ip6_config_get_num_addresses, + nm_setting_ip6_config_remove_address, + _validate_and_remove_ipv6_address) + +static const char * +nmc_property_ipv6_describe_addresses (NMSetting *setting, const char *prop) +{ + return _("Enter a list of IPv6 addresses formatted as:\n" + " ip[/prefix] [gateway], ip[/prefix] [gateway],...\n" + "Missing prefix is regarded as prefix of 128.\n\n" + "Example: 2607:f0d0:1002:51::4/64 2607:f0d0:1002:51::1, 1050:0:0:0:5:600:300c:326b\n"); +} - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ +/* 'routes' */ +static NMIP6Route * +_parse_ipv6_route (const char *route, GError **error) +{ + char *value = g_strdup (route); + char **routev; + guint len; + NMIP6Route *ip6route = NULL; + + routev = nmc_strsplit_set (g_strstrip (value), " \t", 0); + len = g_strv_length (routev); + if (len < 1 || len > 3) { + g_set_error (error, 1, 0, _("'%s' is not valid (the format is: ip[/prefix] [next-hop] [metric])"), + route); + goto finish; + } + ip6route = nmc_parse_and_build_ip6_route (routev[0], routev[1], len >= 2 ? routev[2] : NULL, error); - g_free (bdaddr_str); +finish: + g_free (value); + g_strfreev (routev); + return ip6route; +} +static gboolean +nmc_property_ipv6_set_routes (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter; + NMIP6Route *ip6route; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + strv = nmc_strsplit_set (val, ",", 0); + for (iter = strv; iter && *iter; iter++) { + ip6route = _parse_ipv6_route (*iter, error); + if (!ip6route) { + g_strfreev (strv); + return FALSE; + } + nm_setting_ip6_config_add_route (NM_SETTING_IP6_CONFIG (setting), ip6route); + nm_ip6_route_unref (ip6route); + } + g_strfreev (strv); return TRUE; } -gboolean -setting_olpc_mesh_details (NMSettingOlpcMesh *s_olpc_mesh, NmCli *nmc) +static gboolean +_validate_and_remove_ipv6_route (NMSettingIP6Config *setting, + const char *route, + GError **error) { - const GByteArray *ssid, *dhcp_anycast; - char *channel_str, *ssid_str = NULL, *dhcp_anycast_str = NULL; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + NMIP6Route *ip6route; + gboolean ret; - g_return_val_if_fail (NM_IS_SETTING_OLPC_MESH (s_olpc_mesh), FALSE); + ip6route = _parse_ipv6_route (route, error); + if (!ip6route) + return FALSE; + + ret = nm_setting_ip6_config_remove_route_by_value (setting, ip6route); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain route '%s'"), route); + nm_ip6_route_unref (ip6route); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_ipv6_remove_routes, + NM_SETTING_IP6_CONFIG, + nm_setting_ip6_config_get_num_routes, + nm_setting_ip6_config_remove_route, + _validate_and_remove_ipv6_route) + +static const char * +nmc_property_ipv6_describe_routes (NMSetting *setting, const char *prop) +{ + return _("Enter a list of IPv6 routes formatted as:\n" + " ip[/prefix] [next-hop] [metric],...\n\n" + "Missing prefix is regarded as a prefix of 128.\n" + "Missing next-hop is regarded as \"::\".\n" + "Missing metric or 0 means a default metric (NM/kernel will set a default value).\n\n" + "Examples: 2001:db8:beef:2::/64 2001:db8:beef::2, 2001:db8:beef:3::/64 2001:db8:beef::3 2\n" + " abbe::/64 55\n"); +} - nmc->allowed_fields = nmc_fields_setting_olpc_mesh; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_OLPC_MESH_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ +static gboolean +nmc_property_ipv6_set_ip6_privacy (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + unsigned long val_int; - ssid = nm_setting_olpc_mesh_get_ssid (s_olpc_mesh); - if (ssid) - ssid_str = ssid_to_printable ((const char *) ssid->data, ssid->len); - channel_str = g_strdup_printf ("%d", nm_setting_olpc_mesh_get_channel (s_olpc_mesh)); - dhcp_anycast = nm_setting_olpc_mesh_get_dhcp_anycast_address (s_olpc_mesh); - if (dhcp_anycast) - dhcp_anycast_str = nm_utils_hwaddr_ntoa (dhcp_anycast->data, ARPHRD_ETHER); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_string_to_uint (val, FALSE, 0, 0, &val_int)) { + g_set_error (error, 1, 0, _("'%s' is not a number"), val); + return FALSE; + } + + if ( val_int != NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED + && val_int != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR + && val_int != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR) { + g_set_error (error, 1, 0, _("'%s' is not valid; use 0, 1, or 2"), val); + return FALSE; + } + + g_object_set (setting, prop, val_int, NULL); + return TRUE; +} + +/* --- NM_SETTING_OLPC_MESH_SETTING_NAME property setter functions --- */ +static gboolean +nmc_property_olpc_set_channel (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + unsigned long chan_int; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_string_to_uint (val, TRUE, 1, 13, &chan_int)) { + g_set_error (error, 1, 0, _("'%s' is not a valid channel; use <1-13>"), val); + return FALSE; + } + g_object_set (setting, prop, chan_int, NULL); + return TRUE; +} + + +/* --- NM_SETTING_SERIAL_SETTING_NAME property setter functions --- */ +static gboolean +nmc_property_serial_set_parity (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char parity; + + if (val[0] == 'E' || val[0] == 'e') + parity = 'E'; + else if (val[0] == 'O' || val[0] == 'o') + parity = 'o'; + else if (val[0] == 'N' || val[0] == 'n') + parity = 'n'; + else { + g_set_error (error, 1, 0, _("'%s' is not valid; use [e, o, n]"), val); + return FALSE; + } + + g_object_set (setting, prop, parity, NULL); + return TRUE; +} + +/* --- NM_SETTING_TEAM_SETTING_NAME property functions --- */ +/* --- NM_SETTING_TEAM_PORT_SETTING_NAME property functions --- */ +static gboolean +nmc_property_team_set_config (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char *json = NULL; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_team_check_config (val, &json, error)) { + return FALSE; + } + g_object_set (setting, prop, json, NULL); + g_free (json); + return TRUE; +} + +static const char * +nmc_property_team_describe_config (NMSetting *setting, const char *prop) +{ + return _("nmcli can accepts both direct JSON configuration data and a file name containing " + "the configuration. In the latter case the file is read and the contents is put " + "into this property.\n\n" + "Examples: set team.config " + "{ \"device\": \"team0\", \"runner\": {\"name\": \"roundrobin\"}, \"ports\": {\"eth1\": {}, \"eth2\": {}} }\n" + " set team.config /etc/my-team.conf\n"); +} + +/* --- NM_SETTING_VLAN_SETTING_NAME property setter functions --- */ +static gboolean +nmc_property_vlan_set_prio_map (NMSetting *setting, + const char *prop, + const char *val, + NMVlanPriorityMap map_type, + GError **error) +{ + char **prio_map, **p; + + prio_map = nmc_vlan_parse_priority_maps (val, map_type, error); + if (!prio_map) + return FALSE; + + for (p = prio_map; p && *p; p++) + nm_setting_vlan_add_priority_str (NM_SETTING_VLAN (setting), map_type, *p); + + g_strfreev (prio_map); + return TRUE; +} + +static gboolean +nmc_property_vlan_remove_prio_map (NMSetting *setting, + const char *prop, + guint32 idx, + NMVlanPriorityMap map_type, + GError **error) +{ + guint32 num; + + num = nm_setting_vlan_get_num_priorities (NM_SETTING_VLAN (setting), map_type); + if (num == 0) { + g_set_error_literal (error, 1, 0, _("no priority to remove")); + return FALSE; + } + if (idx >= num) { + g_set_error (error, 1, 0, _("index '%d' is not in the range of <0-%d>"), + idx, num - 1); + return FALSE; + } + + nm_setting_vlan_remove_priority (NM_SETTING_VLAN (setting), map_type, idx); + return TRUE; +} + +static gboolean +nmc_property_vlan_set_ingress_priority_map (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return nmc_property_vlan_set_prio_map (setting, prop, val, NM_VLAN_INGRESS_MAP, error); +} + +static gboolean +nmc_property_vlan_set_egress_priority_map (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return nmc_property_vlan_set_prio_map (setting, prop, val, NM_VLAN_EGRESS_MAP, error); +} + +static gboolean +nmc_property_vlan_remove_priority_map (NMSetting *setting, + const char *prop, + const char *value, + guint32 idx, + NMVlanPriorityMap map, + GError **error) +{ + /* If value != NULL, remove by value */ + if (value) { + gboolean ret; + char **prio_map; + char *val = g_strdup (value); + + prio_map = nmc_vlan_parse_priority_maps (val, map, error); + if (!prio_map) + return FALSE; + if (prio_map[1]) + printf (_("Warning: only one mapping at a time is supported; taking the first one (%s)\n"), + prio_map[0]); + ret = nm_setting_vlan_remove_priority_str_by_value (NM_SETTING_VLAN (setting), + map, + prio_map[0]); + + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain mapping '%s'"), prio_map[0]); + g_free (val); + g_strfreev (prio_map); + return ret; + } + + /* Else remove by index */ + return nmc_property_vlan_remove_prio_map (setting, prop, idx, map, error); +} + +static gboolean +nmc_property_vlan_remove_ingress_priority_map (NMSetting *setting, + const char *prop, + const char *value, + guint32 idx, + GError **error) +{ + return nmc_property_vlan_remove_priority_map (setting, + prop, + value, + idx, + NM_VLAN_INGRESS_MAP, + error); +} + +static gboolean +nmc_property_vlan_remove_egress_priority_map (NMSetting *setting, + const char *prop, + const char *value, + guint32 idx, + GError **error) +{ + return nmc_property_vlan_remove_priority_map (setting, + prop, + value, + idx, + NM_VLAN_EGRESS_MAP, + error); +} + +/* --- NM_SETTING_VPN_SETTING_NAME property setter functions --- */ +/* 'data' */ +DEFINE_SETTER_OPTIONS (nmc_property_vpn_set_data, + NM_SETTING_VPN, + NMSettingVPN, + nm_setting_vpn_add_data_item, + NULL, + NULL) +DEFINE_REMOVER_OPTION (nmc_property_vpn_remove_option_data, + NM_SETTING_VPN, + nm_setting_vpn_remove_data_item) + +/* 'secrets' */ +DEFINE_SETTER_OPTIONS (nmc_property_vpn_set_secrets, + NM_SETTING_VPN, + NMSettingVPN, + nm_setting_vpn_add_secret, + NULL, + NULL) +DEFINE_REMOVER_OPTION (nmc_property_vpn_remove_option_secret, + NM_SETTING_VPN, + nm_setting_vpn_remove_secret) + +/* --- NM_SETTING_WIMAX_SETTING_NAME property setter functions --- */ +/* No specific functions */ + +/* --- NM_SETTING_WIRED_SETTING_NAME property setter functions --- */ +#if 0 +/* + * Do not allow setting 'port' and 'duplex' for now. They are not implemented in + * NM core, nor in ifcfg-rh plugin. Enable this when it gets done. + */ +/* 'port' */ +static const char *wired_valid_ports[] = { "tp", "aui", "bnc", "mii", NULL }; + +static gboolean +nmc_property_wired_set_port (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, wired_valid_ports, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wired_allowed_port, wired_valid_ports) + +/* 'duplex' */ +static const char *wired_valid_duplexes[] = { "half", "full", NULL }; + +static gboolean +nmc_property_wired_set_duplex (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, wired_valid_duplexes, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wired_allowed_duplex, wired_valid_duplexes) +#endif + +/* 'mac-address-blacklist' */ +DEFINE_SETTER_MAC_BLACKLIST (nmc_property_wired_set_mac_address_blacklist, + NM_SETTING_WIRED, + nm_setting_wired_add_mac_blacklist_item) + +static gboolean +_validate_and_remove_wired_mac_blacklist_item (NMSettingWired *setting, + const char *mac, + GError **error) +{ + gboolean ret; + guint8 buf[32]; + + if (!nm_utils_hwaddr_aton (mac, ARPHRD_ETHER, buf)) { + g_set_error (error, 1, 0, _("'%s' is not a valid MAC address"), mac); + return FALSE; + } + + ret = nm_setting_wired_remove_mac_blacklist_item_by_value (setting, mac); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain MAC address '%s'"), mac); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_wired_remove_mac_address_blacklist, + NM_SETTING_WIRED, + nm_setting_wired_get_num_mac_blacklist_items, + nm_setting_wired_remove_mac_blacklist_item, + _validate_and_remove_wired_mac_blacklist_item) + +/* 's390-subchannels' */ +static gboolean +nmc_property_wired_set_s390_subchannels (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter; + GPtrArray *s390_subchannels; + + //FIXME: both libnm-util and ifcfg-rh also allow two strings (3rd is optional) + strv = nmc_strsplit_set (val, " ,\t", 0); + if (g_strv_length (strv) != 3) { + g_set_error (error, 1, 0, _("'%s' is not valid; 3 strings should be provided"), + val); + g_strfreev (strv); + return FALSE; + } + + s390_subchannels = g_ptr_array_sized_new (3); + for (iter = strv; iter && *iter; iter++) + g_ptr_array_add (s390_subchannels, *iter); + + g_object_set (setting, prop, s390_subchannels, NULL); + g_strfreev (strv); + g_ptr_array_free (s390_subchannels, TRUE); + return TRUE; +} + +static const char * +nmc_property_wired_describe_s390_subchannels (NMSetting *setting, const char *prop) +{ + return _("Enter a list of three channels (comma or space separated).\n\n" + "Example: 0.0.0e20 0.0.0e21 0.0.0e22\n"); +} + +/* 's390-nettype' */ +static const char *wired_valid_s390_nettypes[] = { "qeth", "lcs", "ctc", NULL }; + +static gboolean +nmc_property_wired_set_s390_nettype (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, wired_valid_s390_nettypes, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wired_allowed_s390_nettype, wired_valid_s390_nettypes) + +/* 's390-options' */ +DEFINE_SETTER_OPTIONS (nmc_property_wired_set_s390_options, + NM_SETTING_WIRED, + NMSettingWired, + nm_setting_wired_add_s390_option, + nm_setting_wired_get_valid_s390_options, + NULL) +DEFINE_REMOVER_OPTION (nmc_property_wired_remove_option_s390_options, + NM_SETTING_WIRED, + nm_setting_wired_remove_s390_option) + +static const char * +nmc_property_wired_allowed_s390_options (NMSetting *setting, const char *prop) +{ + const char **valid_options; + static char *allowed_vals = NULL; + + if (G_UNLIKELY (allowed_vals == NULL)) { + valid_options = nm_setting_wired_get_valid_s390_options (NM_SETTING_WIRED (setting)); + allowed_vals = g_strjoinv (", ", (char **) valid_options); + } + return allowed_vals; +} - nmc->allowed_fields[0].value = NM_SETTING_OLPC_MESH_SETTING_NAME; - nmc->allowed_fields[1].value = ssid_str; - nmc->allowed_fields[2].value = channel_str; - nmc->allowed_fields[3].value = dhcp_anycast_str; +static const char * +nmc_property_wired_describe_s390_options (NMSetting *setting, const char *prop) +{ + static char *desc = NULL; + const char **valid_options; + char *options_str; + + if (G_UNLIKELY (desc == NULL)) { + valid_options = nm_setting_wired_get_valid_s390_options (NM_SETTING_WIRED (setting)); + options_str = g_strjoinv (", ", (char **) valid_options); + + desc = g_strdup_printf (_("Enter a list of S/390 options formatted as:\n" + " option = <value>, option = <value>,...\n" + "Valid options are: %s\n"), + options_str); + g_free (options_str); + } + return desc; +} + +/* --- NM_SETTING_WIRELESS_SETTING_NAME property setter functions --- */ +/* 'mode' */ +static const char *wifi_valid_modes[] = { + NM_SETTING_WIRELESS_MODE_INFRA, + NM_SETTING_WIRELESS_MODE_ADHOC, + NM_SETTING_WIRELESS_MODE_AP, + NULL +}; + +static gboolean +nmc_property_wifi_set_mode (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, wifi_valid_modes, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wifi_allowed_mode, wifi_valid_modes) + +/* 'band' */ +static const char *wifi_valid_bands[] = { "a", "bg", NULL }; + +static gboolean +nmc_property_wifi_set_band (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, wifi_valid_bands, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wifi_allowed_band, wifi_valid_bands) + +/* 'channel' */ +static gboolean +nmc_property_wifi_set_channel (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + unsigned long chan_int; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_string_to_uint (val, FALSE, 0, 0, &chan_int)) { + g_set_error (error, 1, 0, _("'%s' is not a valid channel"), val); + return FALSE; + } + + if ( !nm_utils_wifi_is_channel_valid (chan_int, "a") + && !nm_utils_wifi_is_channel_valid (chan_int, "bg")) { + g_set_error (error, 1, 0, _("'%ld' is not a valid channel"), chan_int); + return FALSE; + } + + g_object_set (setting, prop, chan_int, NULL); + return TRUE; +} + +/* 'mac-address-blacklist' */ +DEFINE_SETTER_MAC_BLACKLIST (nmc_property_wireless_set_mac_address_blacklist, + NM_SETTING_WIRELESS, + nm_setting_wireless_add_mac_blacklist_item) + +static gboolean +_validate_and_remove_wifi_mac_blacklist_item (NMSettingWireless *setting, + const char *mac, + GError **error) +{ + gboolean ret; + guint8 buf[32]; + + if (!nm_utils_hwaddr_aton (mac, ARPHRD_ETHER, buf)) { + g_set_error (error, 1, 0, _("'%s' is not a valid MAC address"), mac); + return FALSE; + } + + ret = nm_setting_wireless_remove_mac_blacklist_item_by_value (setting, mac); + if (!ret) + g_set_error (error, 1, 0, _("the property doesn't contain MAC address '%s'"), mac); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_wireless_remove_mac_address_blacklist, + NM_SETTING_WIRELESS, + nm_setting_wireless_get_num_mac_blacklist_items, + nm_setting_wireless_remove_mac_blacklist_item, + _validate_and_remove_wifi_mac_blacklist_item) + +/* --- NM_SETTING_WIRELESS_SECURITY_SETTING_NAME property setter functions --- */ +/* 'key-mgmt' */ +static const char *wifi_sec_valid_key_mgmts[] = { "none", "ieee8021x", "wpa-none", "wpa-psk", "wpa-eap", NULL }; + +static gboolean +nmc_property_wifi_sec_set_key_mgmt (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, wifi_sec_valid_key_mgmts, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wifi_sec_allowed_key_mgmt, wifi_sec_valid_key_mgmts) + +/* 'auth-alg' */ +static const char *wifi_sec_valid_auth_algs[] = { "open", "shared", "leap", NULL }; + +static gboolean +nmc_property_wifi_sec_set_auth_alg (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, wifi_sec_valid_auth_algs, error); +} + +DEFINE_SETTER_STR_LIST_MULTI (check_and_add_wifi_sec_proto, + NM_SETTING_WIRELESS_SECURITY, + nm_setting_wireless_security_add_proto) +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wifi_sec_allowed_auth_alg, wifi_sec_valid_auth_algs) + +/* 'proto' */ +static const char *wifi_sec_valid_protos[] = { "wpa", "rsn", NULL }; + +static gboolean +nmc_property_wifi_sec_set_proto (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_add_wifi_sec_proto (setting, prop, val, wifi_sec_valid_protos, error); +} + +static gboolean +_validate_and_remove_wifi_sec_proto (NMSettingWirelessSecurity *setting, + const char *proto, + GError **error) +{ + gboolean ret; + const char *valid; + + valid = nmc_string_is_valid (proto, wifi_sec_valid_protos, error); + if (!valid) + return FALSE; + + ret = nm_setting_wireless_security_remove_proto_by_value (setting, proto); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain protocol '%s'"), proto); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_wifi_sec_remove_proto, + NM_SETTING_WIRELESS_SECURITY, + nm_setting_wireless_security_get_num_protos, + nm_setting_wireless_security_remove_proto, + _validate_and_remove_wifi_sec_proto) + +DEFINE_SETTER_STR_LIST_MULTI (check_and_add_wifi_sec_pairwise, + NM_SETTING_WIRELESS_SECURITY, + nm_setting_wireless_security_add_pairwise) +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wifi_sec_allowed_proto, wifi_sec_valid_protos) + +/* 'pairwise' */ +static const char *wifi_sec_valid_pairwises[] = { "tkip", "ccmp", NULL }; + +static gboolean +nmc_property_wifi_sec_set_pairwise (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_add_wifi_sec_pairwise (setting, prop, val, wifi_sec_valid_pairwises, error); +} + +static gboolean +_validate_and_remove_wifi_sec_pairwise (NMSettingWirelessSecurity *setting, + const char *pairwise, + GError **error) +{ + gboolean ret; + const char *valid; + + valid = nmc_string_is_valid (pairwise, wifi_sec_valid_pairwises, error); + if (!valid) + return FALSE; + + ret = nm_setting_wireless_security_remove_pairwise_by_value (setting, pairwise); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain protocol '%s'"), pairwise); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_wifi_sec_remove_pairwise, + NM_SETTING_WIRELESS_SECURITY, + nm_setting_wireless_security_get_num_pairwise, + nm_setting_wireless_security_remove_pairwise, + _validate_and_remove_wifi_sec_pairwise) + +DEFINE_SETTER_STR_LIST_MULTI (check_and_add_wifi_sec_group, + NM_SETTING_WIRELESS_SECURITY, + nm_setting_wireless_security_add_group) +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wifi_sec_allowed_pairwise, wifi_sec_valid_pairwises) + +/* 'group' */ +static const char *wifi_sec_valid_groups[] = { "wep40", "wep104", "tkip", "ccmp", NULL }; + +static gboolean +nmc_property_wifi_sec_set_group (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_add_wifi_sec_group (setting, prop, val, wifi_sec_valid_groups, error); +} + +static gboolean +_validate_and_remove_wifi_sec_group (NMSettingWirelessSecurity *setting, + const char *group, + GError **error) +{ + gboolean ret; + const char *valid; + + valid = nmc_string_is_valid (group, wifi_sec_valid_groups, error); + if (!valid) + return FALSE; + + ret = nm_setting_wireless_security_remove_group_by_value (setting, group); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain protocol '%s'"), group); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (nmc_property_wifi_sec_remove_group, + NM_SETTING_WIRELESS_SECURITY, + nm_setting_wireless_security_get_num_groups, + nm_setting_wireless_security_remove_group, + _validate_and_remove_wifi_sec_group) +DEFINE_ALLOWED_VAL_FUNC (nmc_property_wifi_sec_allowed_group, wifi_sec_valid_groups) + +/* 'wep-key' */ +static gboolean +nmc_property_wifi_set_wep_key (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + NMWepKeyType guessed_type = NM_WEP_KEY_TYPE_UNKNOWN; + NMWepKeyType type; + guint32 prev_idx, idx; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* Get currently set type */ + type = nm_setting_wireless_security_get_wep_key_type (NM_SETTING_WIRELESS_SECURITY (setting)); + + /* Guess key type */ + if (nm_utils_wep_key_valid (val, NM_WEP_KEY_TYPE_KEY)) + guessed_type = NM_WEP_KEY_TYPE_KEY; + else if (nm_utils_wep_key_valid (val, NM_WEP_KEY_TYPE_PASSPHRASE)) + guessed_type = NM_WEP_KEY_TYPE_PASSPHRASE; + + if (guessed_type == NM_WEP_KEY_TYPE_UNKNOWN) { + g_set_error (error, 1, 0, _("'%s' is not valid"), val); + return FALSE; + } + + if (type != NM_WEP_KEY_TYPE_UNKNOWN && type != guessed_type) { + if (nm_utils_wep_key_valid (val, type)) + guessed_type = type; + else { + g_set_error (error, 1, 0, + _("'%s' not compatible with %s '%s', please change the key or set the right %s first."), + val, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, wep_key_type_to_string (type), + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE); + return FALSE; + } + } + prev_idx = nm_setting_wireless_security_get_wep_tx_keyidx (NM_SETTING_WIRELESS_SECURITY (setting)); + idx = prop[strlen (prop) - 1] - '0'; + printf (_("WEP key is guessed to be of '%s'\n"), wep_key_type_to_string (guessed_type)); + if (idx != prev_idx) + printf (_("WEP key index set to '%d'\n"), idx); + + g_object_set (setting, prop, val, NULL); + g_object_set (setting, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, guessed_type, NULL); + if (idx != prev_idx) + g_object_set (setting, NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, idx, NULL); + return TRUE; +} - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ +/* 'wep-key-type' */ +static gboolean +nmc_property_wifi_set_wep_key_type (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + unsigned long type_int; + const char *valid_wep_types[] = { "unknown", "key", "passphrase", NULL }; + const char *type_str = NULL; + const char *key0, *key1,* key2, *key3; + NMWepKeyType type = NM_WEP_KEY_TYPE_UNKNOWN; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_string_to_uint (val, TRUE, 0, 2, &type_int)) { + if (!(type_str = nmc_string_is_valid (val, valid_wep_types, NULL))) { + g_set_error (error, 1, 0, _("'%s' not among [0 (unknown), 1 (key), 2 (passphrase)]"), val); + return FALSE; + } + if (type_str == valid_wep_types[1]) + type = NM_WEP_KEY_TYPE_KEY; + else if (type_str == valid_wep_types[2]) + type = NM_WEP_KEY_TYPE_PASSPHRASE; + } else + type = (NMWepKeyType) type_int; + + /* Check type compatibility with set keys */ + key0 = nm_setting_wireless_security_get_wep_key (NM_SETTING_WIRELESS_SECURITY (setting), 0); + key1 = nm_setting_wireless_security_get_wep_key (NM_SETTING_WIRELESS_SECURITY (setting), 1); + key2 = nm_setting_wireless_security_get_wep_key (NM_SETTING_WIRELESS_SECURITY (setting), 2); + key3 = nm_setting_wireless_security_get_wep_key (NM_SETTING_WIRELESS_SECURITY (setting), 3); + if (key0 && !nm_utils_wep_key_valid (key0, type)) + printf (_("Warning: '%s' is not compatible with '%s' type, please change or delete the key.\n"), + NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, wep_key_type_to_string (type)); + if (key1 && !nm_utils_wep_key_valid (key1, type)) + printf (_("Warning: '%s' is not compatible with '%s' type, please change or delete the key.\n"), + NM_SETTING_WIRELESS_SECURITY_WEP_KEY1, wep_key_type_to_string (type)); + if (key2 && !nm_utils_wep_key_valid (key2, type)) + printf (_("Warning: '%s' is not compatible with '%s' type, please change or delete the key.\n"), + NM_SETTING_WIRELESS_SECURITY_WEP_KEY2, wep_key_type_to_string (type)); + if (key3 && !nm_utils_wep_key_valid (key3, type)) + printf (_("Warning: '%s' is not compatible with '%s' type, please change or delete the key.\n"), + NM_SETTING_WIRELESS_SECURITY_WEP_KEY3, wep_key_type_to_string (type)); + + g_object_set (setting, prop, type, NULL); + return TRUE; +} + +static const char * +nmc_property_wifi_describe_wep_key_type (NMSetting *setting, const char *prop) +{ + static char *desc = NULL; + + if (G_UNLIKELY (desc == NULL)) { + desc = g_strdup_printf (_("Enter the type of WEP keys. The accepted values are: " + "0 or unknown, 1 or key, and 2 or passphrase.\n")); + } + return desc; +} + +/* 'psk' */ +static gboolean +nmc_property_wifi_set_psk (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nm_utils_wpa_psk_valid (val)) { + g_set_error (error, 1, 0, _("'%s' is not a valid PSK"), val); + return FALSE; + } + g_object_set (setting, prop, val, NULL); + return TRUE; +} + +#define DCB_ALL_FLAGS (NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_ADVERTISE | NM_SETTING_DCB_FLAG_WILLING) + +/* DCB stuff */ +static gboolean +nmc_property_dcb_set_flags (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + char **strv = NULL, **iter; + NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE; + long int t; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* Check for overall hex numeric value */ + if (nmc_string_to_int_base (val, 0, TRUE, 0, DCB_ALL_FLAGS, &t)) + flags = (guint) t; + else { + /* Check for individual flag numbers */ + strv = nmc_strsplit_set (val, " \t,", 0); + for (iter = strv; iter && *iter; iter++) { + if (!nmc_string_to_int_base (*iter, 0, TRUE, 0, DCB_ALL_FLAGS, &t)) + t = -1; + + if ( g_ascii_strcasecmp (*iter, "enable") == 0 + || g_ascii_strcasecmp (*iter, "enabled") == 0 + || t == NM_SETTING_DCB_FLAG_ENABLE) + flags |= NM_SETTING_DCB_FLAG_ENABLE; + else if ( g_ascii_strcasecmp (*iter, "advertise") == 0 + || t == NM_SETTING_DCB_FLAG_ADVERTISE) + flags |= NM_SETTING_DCB_FLAG_ADVERTISE; + else if ( g_ascii_strcasecmp (*iter, "willing") == 0 + || t == NM_SETTING_DCB_FLAG_WILLING) + flags |= NM_SETTING_DCB_FLAG_WILLING; + else if ( g_ascii_strcasecmp (*iter, "disable") == 0 + || g_ascii_strcasecmp (*iter, "disabled") == 0 + || t == 0) { + /* pass */ + } else { + g_set_error (error, 1, 0, _("'%s' is not a valid DCB flag"), *iter); + return FALSE; + } + } + g_strfreev (strv); + } + + /* Validate the number according to the property spec */ + if (!validate_uint (setting, prop, (guint) flags, error)) + return FALSE; + + g_object_set (setting, prop, (guint) flags, NULL); + return TRUE; +} + +static gboolean +nmc_property_dcb_set_priority (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + long int priority = 0; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_string_to_int (val, FALSE, -1, 7, &priority)) { + g_set_error (error, 1, 0, _("'%s' is not a DCB app priority"), val); + return FALSE; + } + + /* Validate the number according to the property spec */ + if (!validate_int (setting, prop, (gint) priority, error)) + return FALSE; + + g_object_set (setting, prop, (gint) priority, NULL); + return TRUE; +} + +static gboolean +dcb_parse_uint_array (const char *val, + guint max, + guint other, + guint *out_array, + GError **error) +{ + char **items, **iter; + guint i = 0; + + g_return_val_if_fail (out_array != NULL, FALSE); + + items = g_strsplit_set (val, ",", -1); + if (g_strv_length (items) != 8) { + g_set_error_literal (error, 1, 0, _("must contain 8 comma-separated numbers")); + goto error; + } - g_free (ssid_str); - g_free (channel_str); - g_free (dhcp_anycast_str); + for (iter = items; iter && *iter; iter++) { + long int num = 0; + gboolean success; + + *iter = g_strstrip (*iter); + success = nmc_string_to_int_base (*iter, 10, TRUE, 0, other ? other : max, &num); + + /* If number is greater than 'max' it must equal 'other' */ + if (success && other && (num > max) && (num != other)) + success = FALSE; + + if (!success) { + if (other) { + g_set_error (error, 1, 0, _("'%s' not a number between 0 and %u (inclusive) or %u"), + *iter, max, other); + } else { + g_set_error (error, 1, 0, _("'%s' not a number between 0 and %u (inclusive)"), + *iter, max); + } + goto error; + } + out_array[i++] = (guint) num; + } return TRUE; + +error: + g_strfreev (items); + return FALSE; } static void -vpn_data_item (const char *key, const char *value, gpointer user_data) +dcb_check_feature_enabled (NMSettingDcb *s_dcb, const char *flags_prop) { - GString *ret_str = (GString *) user_data; + NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE; - if (ret_str->len != 0) - g_string_append (ret_str, ", "); + g_object_get (s_dcb, flags_prop, &flags, NULL); + if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) + printf (_("Warning: changes will have no effect until '%s' includes 1 (enabled)\n\n"), flags_prop); +} - g_string_append_printf (ret_str, "%s = %s", key, value); +static gboolean +nmc_property_dcb_set_pfc (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + guint i = 0; + guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!dcb_parse_uint_array (val, 1, 0, nums, error)) + return FALSE; + + for (i = 0; i < 8; i++) + nm_setting_dcb_set_priority_flow_control (NM_SETTING_DCB (setting), i, !!nums[i]); + + dcb_check_feature_enabled (NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS); + return TRUE; } -gboolean -setting_vpn_details (NMSettingVPN *s_vpn, NmCli *nmc) +static gboolean +nmc_property_dcb_set_pg_group_id (NMSetting *setting, const char *prop, const char *val, GError **error) { - GString *data_item_str, *secret_str; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + guint i = 0; + guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - g_return_val_if_fail (NM_IS_SETTING_VPN (s_vpn), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - nmc->allowed_fields = nmc_fields_setting_vpn; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_VPN_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ + if (!dcb_parse_uint_array (val, 7, 15, nums, error)) + return FALSE; - data_item_str = g_string_new (NULL); - secret_str = g_string_new (NULL); - nm_setting_vpn_foreach_data_item (s_vpn, &vpn_data_item, data_item_str); - nm_setting_vpn_foreach_secret (s_vpn, &vpn_data_item, secret_str); + for (i = 0; i < 8; i++) + nm_setting_dcb_set_priority_group_id (NM_SETTING_DCB (setting), i, nums[i]); + + dcb_check_feature_enabled (NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS); + return TRUE; +} - nmc->allowed_fields[0].value = NM_SETTING_VPN_SETTING_NAME; - nmc->allowed_fields[1].value = nm_setting_vpn_get_service_type (s_vpn); - nmc->allowed_fields[2].value = nm_setting_vpn_get_user_name (s_vpn); - nmc->allowed_fields[3].value = data_item_str->str; - nmc->allowed_fields[4].value = secret_str->str; +static gboolean +nmc_property_dcb_set_pg_group_bandwidth (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + guint i = 0, sum = 0; + guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - g_string_free (data_item_str, TRUE); - g_string_free (secret_str, TRUE); + if (!dcb_parse_uint_array (val, 100, 0, nums, error)) + return FALSE; + for (i = 0; i < 8; i++) + sum += nums[i]; + if (sum != 100) { + g_set_error_literal (error, 1, 0, _("bandwidth percentages must total 100%%")); + return FALSE; + } + + for (i = 0; i < 8; i++) + nm_setting_dcb_set_priority_group_bandwidth (NM_SETTING_DCB (setting), i, nums[i]); + + dcb_check_feature_enabled (NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS); return TRUE; } -gboolean -setting_wimax_details (NMSettingWimax *s_wimax, NmCli *nmc) +static gboolean +nmc_property_dcb_set_pg_bandwidth (NMSetting *setting, const char *prop, const char *val, GError **error) { - const GByteArray *mac; - char *device_mac_str = NULL; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + guint i = 0; + guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - g_return_val_if_fail (NM_IS_SETTING_WIMAX (s_wimax), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!dcb_parse_uint_array (val, 100, 0, nums, error)) + return FALSE; + + for (i = 0; i < 8; i++) + nm_setting_dcb_set_priority_bandwidth (NM_SETTING_DCB (setting), i, nums[i]); + + dcb_check_feature_enabled (NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS); + return TRUE; +} + +static gboolean +nmc_property_dcb_set_pg_strict (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + guint i = 0; + guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!dcb_parse_uint_array (val, 1, 0, nums, error)) + return FALSE; - nmc->allowed_fields = nmc_fields_setting_wimax; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_WIMAX_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ + for (i = 0; i < 8; i++) + nm_setting_dcb_set_priority_strict_bandwidth (NM_SETTING_DCB (setting), i, !!nums[i]); - mac = nm_setting_wimax_get_mac_address (s_wimax); - if (mac) - device_mac_str = nm_utils_hwaddr_ntoa (mac->data, ARPHRD_ETHER); + dcb_check_feature_enabled (NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS); + return TRUE; +} + +static gboolean +nmc_property_dcb_set_pg_traffic_class (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + guint i = 0; + guint nums[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - nmc->allowed_fields[0].value = NM_SETTING_WIMAX_SETTING_NAME; - nmc->allowed_fields[1].value = device_mac_str; - nmc->allowed_fields[2].value = nm_setting_wimax_get_network_name (s_wimax); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + if (!dcb_parse_uint_array (val, 7, 0, nums, error)) + return FALSE; - g_free (device_mac_str); + for (i = 0; i < 8; i++) + nm_setting_dcb_set_priority_traffic_class (NM_SETTING_DCB (setting), i, nums[i]); + dcb_check_feature_enabled (NM_SETTING_DCB (setting), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS); return TRUE; } +/* 'app-fcoe-mode' */ +static const char *_dcb_valid_fcoe_modes[] = { NM_SETTING_DCB_FCOE_MODE_FABRIC, + NM_SETTING_DCB_FCOE_MODE_VN2VN, + NULL }; + +static gboolean +nmc_property_dcb_set_app_fcoe_mode (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return check_and_set_string (setting, prop, val, _dcb_valid_fcoe_modes, error); +} + +DEFINE_ALLOWED_VAL_FUNC (nmc_property_dcb_allowed_app_fcoe_modes, _dcb_valid_fcoe_modes) + +/*----------------------------------------------------------------------------*/ + +static void +nmc_add_prop_funcs (char *key, + NmcPropertyGetFunc get_func, + NmcPropertySetFunc set_func, + NmcPropertyRemoveFunc remove_func, + NmcPropertyDescribeFunc describe_func, + NmcPropertyValuesFunc values_func, + NmcPropertyOut2InFunc out2in_func) +{ + NmcPropertyFuncs *item = g_malloc0 (sizeof (NmcPropertyFuncs)); + item->get_func = get_func; + item->set_func = set_func; + item->remove_func = remove_func; + item->describe_func = describe_func; + item->values_func = values_func; + item->out2in_func = out2in_func; + + g_hash_table_insert (nmc_properties, key, item); +} + +/* concatenate setting name and property name */ +#define GLUE(A,B) (g_strconcat ((NM_SETTING_##A##_SETTING_NAME),(NM_SETTING_##A##_##B), NULL)) +void +nmc_properties_init (void) +{ + if (G_LIKELY (nmc_properties)) + return; + + /* create properties hash table */ + nmc_properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + /* Add editable properties for NM_SETTING_802_1X_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (802_1X, EAP), + nmc_property_802_1X_get_eap, + nmc_property_802_1X_set_eap, + nmc_property_802_1X_remove_eap, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, IDENTITY), + nmc_property_802_1X_get_identity, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, ANONYMOUS_IDENTITY), + nmc_property_802_1X_get_anonymous_identity, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PAC_FILE), + nmc_property_802_1X_get_pac_file, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, CA_CERT), + nmc_property_802_1X_get_ca_cert, + nmc_property_802_1X_set_ca_cert, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, CA_PATH), + nmc_property_802_1X_get_ca_path, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, SUBJECT_MATCH), + nmc_property_802_1X_get_subject_match, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, ALTSUBJECT_MATCHES), + nmc_property_802_1X_get_altsubject_matches, + nmc_property_802_1X_set_altsubject_matches, + nmc_property_802_1X_remove_altsubject_matches, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, CLIENT_CERT), + nmc_property_802_1X_get_client_cert, + nmc_property_802_1X_set_client_cert, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE1_PEAPVER), + nmc_property_802_1X_get_phase1_peapver, + nmc_property_802_1X_set_phase1_peapver, + NULL, + NULL, + nmc_property_802_1X_allowed_phase1_peapver, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE1_PEAPLABEL), + nmc_property_802_1X_get_phase1_peaplabel, + nmc_property_802_1X_set_phase1_peaplabel, + NULL, + NULL, + nmc_property_802_1X_allowed_phase1_peaplabel, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE1_FAST_PROVISIONING), + nmc_property_802_1X_get_phase1_fast_provisioning, + nmc_property_802_1X_set_phase1_fast_provisioning, + NULL, + NULL, + nmc_property_802_1X_allowed_phase1_fast_provisioning, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_AUTH), + nmc_property_802_1X_get_phase2_auth, + nmc_property_802_1X_set_phase2_auth, + NULL, + NULL, + nmc_property_802_1X_allowed_phase2_auth, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_AUTHEAP), + nmc_property_802_1X_get_phase2_autheap, + nmc_property_802_1X_set_phase2_autheap, + NULL, + NULL, + nmc_property_802_1X_allowed_phase2_autheap, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_CA_CERT), + nmc_property_802_1X_get_phase2_ca_cert, + nmc_property_802_1X_set_phase2_ca_cert, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_CA_PATH), + nmc_property_802_1X_get_phase2_ca_path, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_SUBJECT_MATCH), + nmc_property_802_1X_get_phase2_subject_match, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_ALTSUBJECT_MATCHES), + nmc_property_802_1X_get_phase2_altsubject_matches, + nmc_property_802_1X_set_phase2_altsubject_matches, + nmc_property_802_1X_remove_phase2_altsubject_matches, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_CLIENT_CERT), + nmc_property_802_1X_get_phase2_client_cert, + nmc_property_802_1X_set_phase2_client_cert, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PASSWORD), + nmc_property_802_1X_get_password, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PASSWORD_FLAGS), + nmc_property_802_1X_get_password_flags, + nmc_property_set_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (802_1X, PASSWORD_RAW), + nmc_property_802_1X_get_password_raw, + nmc_property_802_1X_set_password_raw, + NULL, + nmc_property_802_1X_describe_password_raw, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PASSWORD_RAW_FLAGS), + nmc_property_802_1X_get_password_raw_flags, + nmc_property_set_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (802_1X, PRIVATE_KEY), + nmc_property_802_1X_get_private_key, + nmc_property_802_1X_set_private_key, + NULL, + nmc_property_802_1X_describe_private_key, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PRIVATE_KEY_PASSWORD), + nmc_property_802_1X_get_private_key_password, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PRIVATE_KEY_PASSWORD_FLAGS), + nmc_property_802_1X_get_private_key_password_flags, + nmc_property_set_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_PRIVATE_KEY), + nmc_property_802_1X_get_phase2_private_key, + nmc_property_802_1X_set_phase2_private_key, + NULL, + nmc_property_802_1X_describe_private_key, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_PRIVATE_KEY_PASSWORD), + nmc_property_802_1X_get_phase2_private_key_password, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PHASE2_PRIVATE_KEY_PASSWORD_FLAGS), + nmc_property_802_1X_get_phase2_private_key_password_flags, + nmc_property_set_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (802_1X, PIN), + nmc_property_802_1X_get_pin, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (802_1X, PIN_FLAGS), + nmc_property_802_1X_get_pin_flags, + nmc_property_set_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (802_1X, SYSTEM_CA_CERTS), + nmc_property_802_1X_get_system_ca_certs, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_ADSL_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (ADSL, USERNAME), + nmc_property_adsl_get_username, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (ADSL, PASSWORD), + nmc_property_adsl_get_password, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (ADSL, PASSWORD_FLAGS), + nmc_property_adsl_get_password_flags, + nmc_property_set_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (ADSL, PROTOCOL), + nmc_property_adsl_get_protocol, + nmc_property_adsl_set_protocol, + NULL, + NULL, + nmc_property_adsl_allowed_protocol, + NULL); + nmc_add_prop_funcs (GLUE (ADSL, ENCAPSULATION), + nmc_property_adsl_get_encapsulation, + nmc_property_adsl_set_encapsulation, + NULL, + NULL, + nmc_property_adsl_allowed_encapsulation, + NULL); + nmc_add_prop_funcs (GLUE (ADSL, VPI), + nmc_property_adsl_get_vpi, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (ADSL, VCI), + nmc_property_adsl_get_vci, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_BLUETOOTH_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (BLUETOOTH, BDADDR), + nmc_property_bluetooth_get_bdaddr, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BLUETOOTH, TYPE), + nmc_property_bluetooth_get_type, + nmc_property_bluetooth_set_type, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_BOND_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (BOND, INTERFACE_NAME), + nmc_property_bond_get_interface_name, + nmc_property_set_ifname, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BOND, OPTIONS), + nmc_property_bond_get_options, + nmc_property_bond_set_options, + nmc_property_bond_remove_option_options, + nmc_property_bond_describe_options, + nmc_property_bond_allowed_options, + NULL); + + /* Add editable properties for NM_SETTING_BRIDGE_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (BRIDGE, INTERFACE_NAME), + nmc_property_bridge_get_interface_name, + nmc_property_set_ifname, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BRIDGE, MAC_ADDRESS), + nmc_property_bridge_get_mac_address, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BRIDGE, STP), + nmc_property_bridge_get_stp, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BRIDGE, PRIORITY), + nmc_property_bridge_get_priority, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BRIDGE, FORWARD_DELAY), + nmc_property_bridge_get_forward_delay, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BRIDGE, HELLO_TIME), + nmc_property_bridge_get_hello_time, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BRIDGE, MAX_AGE), + nmc_property_bridge_get_max_age, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BRIDGE, AGEING_TIME), + nmc_property_bridge_get_ageing_time, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_BRIDGE_PORT_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (BRIDGE_PORT, PRIORITY), + nmc_property_bridge_port_get_priority, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BRIDGE_PORT, PATH_COST), + nmc_property_bridge_port_get_path_cost, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (BRIDGE_PORT, HAIRPIN_MODE), + nmc_property_bridge_port_get_hairpin_mode, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_CDMA_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (CDMA, NUMBER), + nmc_property_cdma_get_number, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CDMA, USERNAME), + nmc_property_cdma_get_username, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CDMA, PASSWORD), + nmc_property_cdma_get_password, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CDMA, PASSWORD_FLAGS), + nmc_property_cdma_get_password_flags, + nmc_property_set_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + + /* Add editable properties for NM_SETTING_CONNECTION_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (CONNECTION, ID), + nmc_property_connection_get_id, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, UUID), + nmc_property_connection_get_uuid, + NULL, /* forbid setting/removing UUID */ + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, INTERFACE_NAME), + nmc_property_connection_get_interface_name, + nmc_property_set_ifname, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, TYPE), + nmc_property_connection_get_type, + NULL, /* read-only */ + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, AUTOCONNECT), + nmc_property_connection_get_autoconnect, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, TIMESTAMP), + nmc_property_connection_get_timestamp, + NULL, /* read-only */ + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, READ_ONLY), + nmc_property_connection_get_read_only, + NULL, /* 'read-only' is read-only :-) */ + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, PERMISSIONS), + nmc_property_connection_get_permissions, + nmc_property_connection_set_permissions, + nmc_property_connection_remove_permissions, + nmc_property_connection_describe_permissions, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, ZONE), + nmc_property_connection_get_zone, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, MASTER), + nmc_property_connection_get_master, + nmc_property_con_set_master, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, SLAVE_TYPE), + nmc_property_connection_get_slave_type, + nmc_property_con_set_slave_type, + NULL, + NULL, + nmc_property_con_allowed_slave_type, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, SECONDARIES), + nmc_property_connection_get_secondaries, + nmc_property_connection_set_secondaries, + nmc_property_connection_remove_secondaries, + nmc_property_connection_describe_secondaries, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, GATEWAY_PING_TIMEOUT), + nmc_property_connection_get_gateway_ping_timeout, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_DCB_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (DCB, APP_FCOE_FLAGS), + nmc_property_dcb_get_app_fcoe_flags, + nmc_property_dcb_set_flags, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, APP_FCOE_MODE), + nmc_property_dcb_get_app_fcoe_mode, + nmc_property_dcb_set_app_fcoe_mode, + NULL, + NULL, + nmc_property_dcb_allowed_app_fcoe_modes, + NULL); + nmc_add_prop_funcs (GLUE (DCB, APP_FCOE_PRIORITY), + nmc_property_dcb_get_app_fcoe_priority, + nmc_property_dcb_set_priority, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, APP_ISCSI_FLAGS), + nmc_property_dcb_get_app_iscsi_flags, + nmc_property_dcb_set_flags, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, APP_ISCSI_PRIORITY), + nmc_property_dcb_get_app_iscsi_priority, + nmc_property_dcb_set_priority, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, APP_FIP_FLAGS), + nmc_property_dcb_get_app_fip_flags, + nmc_property_dcb_set_flags, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, APP_FIP_PRIORITY), + nmc_property_dcb_get_app_fip_priority, + nmc_property_dcb_set_priority, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, PRIORITY_FLOW_CONTROL_FLAGS), + nmc_property_dcb_get_pfc_flags, + nmc_property_dcb_set_flags, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, PRIORITY_FLOW_CONTROL), + nmc_property_dcb_get_pfc, + nmc_property_dcb_set_pfc, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, PRIORITY_GROUP_FLAGS), + nmc_property_dcb_get_pg_flags, + nmc_property_dcb_set_flags, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, PRIORITY_GROUP_ID), + nmc_property_dcb_get_pg_group_id, + nmc_property_dcb_set_pg_group_id, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, PRIORITY_GROUP_BANDWIDTH), + nmc_property_dcb_get_pg_group_bandwidth, + nmc_property_dcb_set_pg_group_bandwidth, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, PRIORITY_BANDWIDTH), + nmc_property_dcb_get_pg_bandwidth, + nmc_property_dcb_set_pg_bandwidth, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, PRIORITY_STRICT_BANDWIDTH), + nmc_property_dcb_get_pg_strict, + nmc_property_dcb_set_pg_strict, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (DCB, PRIORITY_TRAFFIC_CLASS), + nmc_property_dcb_get_pg_traffic_class, + nmc_property_dcb_set_pg_traffic_class, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_GSM_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (GSM, NUMBER), + nmc_property_gsm_get_number, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (GSM, USERNAME), + nmc_property_gsm_get_username, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (GSM, PASSWORD), + nmc_property_gsm_get_password, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (GSM, PASSWORD_FLAGS), + nmc_property_gsm_get_password_flags, + nmc_property_set_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (GSM, APN), + nmc_property_gsm_get_apn, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (GSM, NETWORK_ID), + nmc_property_gsm_get_network_id, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (GSM, NETWORK_TYPE), + nmc_property_gsm_get_network_type, + nmc_property_set_int, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (GSM, ALLOWED_BANDS), + nmc_property_gsm_get_allowed_bands, + nmc_property_set_uint, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (GSM, PIN), + nmc_property_gsm_get_pin, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (GSM, PIN_FLAGS), + nmc_property_gsm_get_pin_flags, + nmc_property_set_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (GSM, HOME_ONLY), + nmc_property_gsm_get_home_only, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_INFINIBAND_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (INFINIBAND, MAC_ADDRESS), + nmc_property_ib_get_mac_address, + nmc_property_ib_set_mac, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (INFINIBAND, MTU), + nmc_property_ib_get_mtu, + nmc_property_set_mtu, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (INFINIBAND, TRANSPORT_MODE), + nmc_property_ib_get_transport_mode, + nmc_property_ib_set_transport_mode, + NULL, + NULL, + nmc_property_ib_allowed_transport_mode, + NULL); + nmc_add_prop_funcs (GLUE (INFINIBAND, P_KEY), + nmc_property_ib_get_p_key, + nmc_property_ib_set_p_key, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (INFINIBAND, PARENT), + nmc_property_ib_get_parent, + nmc_property_set_ifname, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_IP4_CONFIG_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (IP4_CONFIG, METHOD), + nmc_property_ipv4_get_method, + nmc_property_ipv4_set_method, + NULL, + NULL, + nmc_property_ipv4_allowed_method, + NULL); + nmc_add_prop_funcs (GLUE (IP4_CONFIG, DNS), + nmc_property_ipv4_get_dns, + nmc_property_ipv4_set_dns, + nmc_property_ipv4_remove_dns, + nmc_property_ipv4_describe_dns, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (IP4_CONFIG, DNS_SEARCH), + nmc_property_ipv4_get_dns_search, + nmc_property_ipv4_set_dns_search, + nmc_property_ipv4_remove_dns_search, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (IP4_CONFIG, ADDRESSES), + nmc_property_ipv4_get_addresses, + nmc_property_ipv4_set_addresses, + nmc_property_ipv4_remove_addresses, + nmc_property_ipv4_describe_addresses, + NULL, + nmc_property_out2in_addresses); + nmc_add_prop_funcs (GLUE (IP4_CONFIG, ROUTES), + nmc_property_ipv4_get_routes, + nmc_property_ipv4_set_routes, + nmc_property_ipv4_remove_routes, + nmc_property_ipv4_describe_routes, + NULL, + nmc_property_out2in_routes); + nmc_add_prop_funcs (GLUE (IP4_CONFIG, IGNORE_AUTO_ROUTES), + nmc_property_ipv4_get_ignore_auto_routes, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (IP4_CONFIG, IGNORE_AUTO_DNS), + nmc_property_ipv4_get_ignore_auto_dns, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (IP4_CONFIG, DHCP_CLIENT_ID), + nmc_property_ipv4_get_dhcp_client_id, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (IP4_CONFIG, DHCP_SEND_HOSTNAME), + nmc_property_ipv4_get_dhcp_send_hostname, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (IP4_CONFIG, DHCP_HOSTNAME), + nmc_property_ipv4_get_dhcp_hostname, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (IP4_CONFIG, NEVER_DEFAULT), + nmc_property_ipv4_get_never_default, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (IP4_CONFIG, MAY_FAIL), + nmc_property_ipv4_get_may_fail, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_IP6_CONFIG_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (IP6_CONFIG, METHOD), + nmc_property_ipv6_get_method, + nmc_property_ipv6_set_method, + NULL, + NULL, + nmc_property_ipv6_allowed_method, + NULL); + nmc_add_prop_funcs (GLUE (IP6_CONFIG, DNS), + nmc_property_ipv6_get_dns, + nmc_property_ipv6_set_dns, + nmc_property_ipv6_remove_dns, + nmc_property_ipv6_describe_dns, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (IP6_CONFIG, DNS_SEARCH), + nmc_property_ipv6_get_dns_search, + nmc_property_ipv6_set_dns_search, + nmc_property_ipv6_remove_dns_search, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (IP6_CONFIG, ADDRESSES), + nmc_property_ipv6_get_addresses, + nmc_property_ipv6_set_addresses, + nmc_property_ipv6_remove_addresses, + nmc_property_ipv6_describe_addresses, + NULL, + nmc_property_out2in_addresses); + nmc_add_prop_funcs (GLUE (IP6_CONFIG, ROUTES), + nmc_property_ipv6_get_routes, + nmc_property_ipv6_set_routes, + nmc_property_ipv6_remove_routes, + nmc_property_ipv6_describe_routes, + NULL, + nmc_property_out2in_routes); + nmc_add_prop_funcs (GLUE (IP6_CONFIG, IGNORE_AUTO_ROUTES), + nmc_property_ipv6_get_ignore_auto_routes, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (IP6_CONFIG, IGNORE_AUTO_DNS), + nmc_property_ipv6_get_ignore_auto_dns, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (IP6_CONFIG, NEVER_DEFAULT), + nmc_property_ipv6_get_never_default, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (IP6_CONFIG, MAY_FAIL), + nmc_property_ipv6_get_may_fail, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (IP6_CONFIG, IP6_PRIVACY), + nmc_property_ipv6_get_ip6_privacy, + nmc_property_ipv6_set_ip6_privacy, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (IP6_CONFIG, DHCP_HOSTNAME), + nmc_property_ipv6_get_dhcp_hostname, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_OLPC_MESH_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (OLPC_MESH, SSID), + nmc_property_olpc_get_ssid, + nmc_property_set_ssid, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (OLPC_MESH, CHANNEL), + nmc_property_olpc_get_channel, + nmc_property_olpc_set_channel, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (OLPC_MESH, DHCP_ANYCAST_ADDRESS), + nmc_property_olpc_get_anycast_address, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_PPP_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (PPP, NOAUTH), + nmc_property_ppp_get_noauth, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, REFUSE_EAP), + nmc_property_ppp_get_refuse_eap, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, REFUSE_PAP), + nmc_property_ppp_get_refuse_pap, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, REFUSE_CHAP), + nmc_property_ppp_get_refuse_chap, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, REFUSE_MSCHAP), + nmc_property_ppp_get_refuse_mschap, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, REFUSE_MSCHAPV2), + nmc_property_ppp_get_refuse_mschapv2, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, NOBSDCOMP), + nmc_property_ppp_get_nobsdcomp, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, NODEFLATE), + nmc_property_ppp_get_nodeflate, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, NO_VJ_COMP), + nmc_property_ppp_get_no_vj_comp, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, REQUIRE_MPPE), + nmc_property_ppp_get_require_mppe, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, REQUIRE_MPPE_128), + nmc_property_ppp_get_require_mppe_128, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, MPPE_STATEFUL), + nmc_property_ppp_get_mppe_stateful, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, CRTSCTS), + nmc_property_ppp_get_crtscts, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, BAUD), + nmc_property_ppp_get_baud, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, MRU), + nmc_property_ppp_get_mru, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, MTU), + nmc_property_ppp_get_mtu, + nmc_property_set_mtu, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, LCP_ECHO_FAILURE), + nmc_property_ppp_get_lcp_echo_failure, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPP, LCP_ECHO_INTERVAL), + nmc_property_ppp_get_lcp_echo_interval, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_PPPOE_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (PPPOE, SERVICE), + nmc_property_pppoe_get_service, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPPOE, USERNAME), + nmc_property_pppoe_get_username, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPPOE, PASSWORD), + nmc_property_pppoe_get_password, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (PPPOE, PASSWORD_FLAGS), + nmc_property_pppoe_get_password_flags, + nmc_property_set_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + + /* Add editable properties for NM_SETTING_SERIAL_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (SERIAL, BAUD), + nmc_property_serial_get_baud, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (SERIAL, BITS), + nmc_property_serial_get_bits, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (SERIAL, PARITY), + nmc_property_serial_get_parity, + nmc_property_serial_set_parity, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (SERIAL, STOPBITS), + nmc_property_serial_get_stopbits, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (SERIAL, SEND_DELAY), + nmc_property_serial_get_send_delay, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_TEAM_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (TEAM, INTERFACE_NAME), + nmc_property_team_get_interface_name, + nmc_property_set_ifname, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (TEAM, CONFIG), + nmc_property_team_get_config, + nmc_property_team_set_config, + NULL, + nmc_property_team_describe_config, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_TEAM_PORT_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (TEAM_PORT, CONFIG), + nmc_property_team_port_get_config, + nmc_property_team_set_config, + NULL, + nmc_property_team_describe_config, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_VLAN_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (VLAN, INTERFACE_NAME), + nmc_property_vlan_get_interface_name, + nmc_property_set_ifname, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (VLAN, PARENT), + nmc_property_vlan_get_parent, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (VLAN, ID), + nmc_property_vlan_get_id, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (VLAN, FLAGS), + nmc_property_vlan_get_flags, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (VLAN, INGRESS_PRIORITY_MAP), + nmc_property_vlan_get_ingress_priority_map, + nmc_property_vlan_set_ingress_priority_map, + nmc_property_vlan_remove_ingress_priority_map, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (VLAN, EGRESS_PRIORITY_MAP), + nmc_property_vlan_get_egress_priority_map, + nmc_property_vlan_set_egress_priority_map, + nmc_property_vlan_remove_egress_priority_map, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_VPN_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (VPN, SERVICE_TYPE), + nmc_property_vpn_get_service_type, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (VPN, USER_NAME), + nmc_property_vpn_get_user_name, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (VPN, DATA), + nmc_property_vpn_get_data, + nmc_property_vpn_set_data, + nmc_property_vpn_remove_option_data, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (VPN, SECRETS), + nmc_property_vpn_get_secrets, + nmc_property_vpn_set_secrets, + nmc_property_vpn_remove_option_secret, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_WIMAX_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (WIMAX, NETWORK_NAME), + nmc_property_wimax_get_network_name, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIMAX, MAC_ADDRESS), + nmc_property_wimax_get_mac_address, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_WIRED_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (WIRED, PORT), + nmc_property_wired_get_port, + NULL, /*nmc_property_wired_set_port,*/ + NULL, + NULL, + NULL, /*nmc_property_wired_allowed_port,*/ + NULL); + nmc_add_prop_funcs (GLUE (WIRED, SPEED), + nmc_property_wired_get_speed, + NULL, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRED, DUPLEX), + nmc_property_wired_get_duplex, + NULL, /*nmc_property_wired_set_duplex,*/ + NULL, + NULL, + NULL, + NULL); /*nmc_property_wired_allowed_duplex);*/ + nmc_add_prop_funcs (GLUE (WIRED, AUTO_NEGOTIATE), + nmc_property_wired_get_auto_negotiate, + NULL, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRED, MAC_ADDRESS), + nmc_property_wired_get_mac_address, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRED, CLONED_MAC_ADDRESS), + nmc_property_wired_get_cloned_mac_address, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRED, MAC_ADDRESS_BLACKLIST), + nmc_property_wired_get_mac_address_blacklist, + nmc_property_wired_set_mac_address_blacklist, + nmc_property_wired_remove_mac_address_blacklist, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRED, MTU), + nmc_property_wired_get_mtu, + nmc_property_set_mtu, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRED, S390_SUBCHANNELS), + nmc_property_wired_get_s390_subchannels, + nmc_property_wired_set_s390_subchannels, + NULL, + nmc_property_wired_describe_s390_subchannels, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRED, S390_NETTYPE), + nmc_property_wired_get_s390_nettype, + nmc_property_wired_set_s390_nettype, + NULL, + NULL, + nmc_property_wired_allowed_s390_nettype, + NULL); + nmc_add_prop_funcs (GLUE (WIRED, S390_OPTIONS), + nmc_property_wired_get_s390_options, + nmc_property_wired_set_s390_options, + nmc_property_wired_remove_option_s390_options, + nmc_property_wired_describe_s390_options, + nmc_property_wired_allowed_s390_options, + NULL); + + /* Add editable properties for NM_SETTING_WIRELESS_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (WIRELESS, SSID), + nmc_property_wireless_get_ssid, + nmc_property_set_ssid, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, MODE), + nmc_property_wireless_get_mode, + nmc_property_wifi_set_mode, + NULL, + NULL, + nmc_property_wifi_allowed_mode, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, BAND), + nmc_property_wireless_get_band, + nmc_property_wifi_set_band, + NULL, + NULL, + nmc_property_wifi_allowed_band, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, CHANNEL), + nmc_property_wireless_get_channel, + nmc_property_wifi_set_channel, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, BSSID), + nmc_property_wireless_get_bssid, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + /* + * Do not allow setting 'rate' and 'tx-power'. They are not implemented in + * NM core, nor in ifcfg-rh plugin (thus not preserved over re-reading). + */ + nmc_add_prop_funcs (GLUE (WIRELESS, RATE), + nmc_property_wireless_get_rate, + NULL, /* editing rate disabled */ + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, TX_POWER), + nmc_property_wireless_get_tx_power, + NULL, /* editing tx-power disabled */ + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, MAC_ADDRESS), + nmc_property_wireless_get_mac_address, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, CLONED_MAC_ADDRESS), + nmc_property_wireless_get_cloned_mac_address, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, MAC_ADDRESS_BLACKLIST), + nmc_property_wireless_get_mac_address_blacklist, + nmc_property_wireless_set_mac_address_blacklist, + nmc_property_wireless_remove_mac_address_blacklist, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, SEEN_BSSIDS), + nmc_property_wireless_get_seen_bssids, + NULL, /* read-only */ + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, MTU), + nmc_property_wireless_get_mtu, + nmc_property_set_mtu, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, HIDDEN), + nmc_property_wireless_get_hidden, + nmc_property_set_bool, + NULL, + NULL, + NULL, + NULL); + + /* Add editable properties for NM_SETTING_WIRELESS_SECURITY_SETTING_NAME */ + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, KEY_MGMT), + nmc_property_wifi_sec_get_key_mgmt, + nmc_property_wifi_sec_set_key_mgmt, + NULL, + NULL, + nmc_property_wifi_sec_allowed_key_mgmt, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, WEP_TX_KEYIDX), + nmc_property_wifi_sec_get_wep_tx_keyidx, + nmc_property_set_uint, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, AUTH_ALG), + nmc_property_wifi_sec_get_auth_alg, + nmc_property_wifi_sec_set_auth_alg, + NULL, + NULL, + nmc_property_wifi_sec_allowed_auth_alg, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, PROTO), + nmc_property_wifi_sec_get_proto, + nmc_property_wifi_sec_set_proto, + nmc_property_wifi_sec_remove_proto, + NULL, + nmc_property_wifi_sec_allowed_proto, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, PAIRWISE), + nmc_property_wifi_sec_get_pairwise, + nmc_property_wifi_sec_set_pairwise, + nmc_property_wifi_sec_remove_pairwise, + NULL, + nmc_property_wifi_sec_allowed_pairwise, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, GROUP), + nmc_property_wifi_sec_get_group, + nmc_property_wifi_sec_set_group, + nmc_property_wifi_sec_remove_group, + NULL, + nmc_property_wifi_sec_allowed_group, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, LEAP_USERNAME), + nmc_property_wifi_sec_get_leap_username, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, WEP_KEY0), + nmc_property_wifi_sec_get_wep_key0, + nmc_property_wifi_set_wep_key, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, WEP_KEY1), + nmc_property_wifi_sec_get_wep_key1, + nmc_property_wifi_set_wep_key, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, WEP_KEY2), + nmc_property_wifi_sec_get_wep_key2, + nmc_property_wifi_set_wep_key, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, WEP_KEY3), + nmc_property_wifi_sec_get_wep_key3, + nmc_property_wifi_set_wep_key, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, WEP_KEY_FLAGS), + nmc_property_wifi_sec_get_wep_key_flags, + nmc_property_set_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, WEP_KEY_TYPE), + nmc_property_wifi_sec_get_wep_key_type, + nmc_property_wifi_set_wep_key_type, + NULL, + nmc_property_wifi_describe_wep_key_type, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, PSK), + nmc_property_wifi_sec_get_psk, + nmc_property_wifi_set_psk, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, PSK_FLAGS), + nmc_property_wifi_sec_get_psk_flags, + nmc_property_set_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, LEAP_PASSWORD), + nmc_property_wifi_sec_get_leap_password, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS_SECURITY, LEAP_PASSWORD_FLAGS), + nmc_property_wifi_sec_get_leap_password_flags, + nmc_property_set_flags, + NULL, + NULL, + NULL, + nmc_property_out2in_cut_paren); +} + +void +nmc_properties_cleanup () +{ + if (nmc_properties) + g_hash_table_destroy (nmc_properties); +} + +static const NmcPropertyFuncs * +nmc_properties_find (const char *s_name, const char *p_name) +{ + NmcPropertyFuncs *item = NULL; + char *key; + + /* FIXME: maybe it's better to init it globally */ + nmc_properties_init (); + + key = g_strdup_printf ("%s%s", s_name, p_name); + item = (NmcPropertyFuncs *) g_hash_table_lookup (nmc_properties, key); + g_free (key); + + return item; +} + +static char * +get_property_val (NMSetting *setting, const char *prop, gboolean convert, GError **error) +{ + const NmcPropertyFuncs *item; + + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + item = nmc_properties_find (nm_setting_get_name (setting), prop); + if (item && item->get_func) { + char *prop_val = item->get_func (setting); + if (convert && item->out2in_func) { + char *converted = item->out2in_func (prop_val); + g_free (prop_val); + return converted; + } else + return prop_val; + } + + g_set_error_literal (error, 1, 0, _("don't know how to get the property value")); + return NULL; +} + +/* + * Generic function for getting property value. + * + * Gets property value as a string by calling specialized functions. + * + * Returns: current property value. The caller must free the returned string. + */ +char * +nmc_setting_get_property (NMSetting *setting, const char *prop, GError **error) +{ + return get_property_val (setting, prop, FALSE, error); +} + +/* + * The same as nmc_setting_get_property(), but in addition converts + * usual output format into a simpler one, used as input in the editor. + */ +char * +nmc_setting_get_property_out2in (NMSetting *setting, const char *prop, GError **error) +{ + return get_property_val (setting, prop, TRUE, error); +} + +/* + * Generic function for setting property value. + * + * Sets property=val in setting by calling specialized functions. + * If val is NULL then default property value is set. + * + * Returns: TRUE on success; FALSE on failure and sets error + */ gboolean -setting_infiniband_details (NMSettingInfiniband *s_infiniband, NmCli *nmc) +nmc_setting_set_property (NMSetting *setting, const char *prop, const char *val, GError **error) { - const GByteArray *mac; - char *mtu_str, *mac_str = NULL; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + const NmcPropertyFuncs *item; - g_return_val_if_fail (NM_IS_SETTING_INFINIBAND (s_infiniband), FALSE); + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + item = nmc_properties_find (nm_setting_get_name (setting), prop); + if (item && item->set_func) { + if (!val) { + /* No value argument sets default value */ + nmc_property_set_default_value (setting, prop); + return TRUE; + } + return item->set_func (setting, prop, val, error); + } - nmc->allowed_fields = nmc_fields_setting_infiniband; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_INFINIBAND_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ + g_set_error_literal (error, 1, 0, _("the property can't be changed")); + return FALSE; +} - mac = nm_setting_infiniband_get_mac_address (s_infiniband); - if (mac) - mac_str = nm_utils_hwaddr_ntoa (mac->data, ARPHRD_INFINIBAND); - mtu_str = g_strdup_printf ("%d", nm_setting_infiniband_get_mtu (s_infiniband)); +void +nmc_property_set_default_value (NMSetting *setting, const char *prop) +{ + GValue value = G_VALUE_INIT; + GParamSpec *param_spec; + + param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), prop); + if (param_spec) { + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (param_spec)); + g_param_value_set_default (param_spec, &value); + g_object_set_property (G_OBJECT (setting), prop, &value); + } +} + +/* + * Generic function for reseting (single value) properties. + * + * The function resets the property value to the default one. It respects + * nmcli restrictions for changing properties. So if 'set_func' is NULL, + * reseting the value is denied. + * + * Returns: TRUE on success; FALSE on failure and sets error + */ +gboolean +nmc_setting_reset_property (NMSetting *setting, const char *prop, GError **error) +{ + const NmcPropertyFuncs *item; - nmc->allowed_fields[0].value = NM_SETTING_INFINIBAND_SETTING_NAME; - nmc->allowed_fields[1].value = mac_str; - nmc->allowed_fields[2].value = strcmp (mtu_str, "0") ? mtu_str : _("auto"); - nmc->allowed_fields[3].value = nm_setting_infiniband_get_transport_mode (s_infiniband); + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + item = nmc_properties_find (nm_setting_get_name (setting), prop); + if (item && item->set_func) { + nmc_property_set_default_value (setting, prop); + return TRUE; + } + g_set_error_literal (error, 1, 0, _("the property can't be changed")); + return FALSE; +} + +/* + * Generic function for removing items for collection-type properties. + * + * If 'option' is not NULL, it tries to remove it, otherwise 'idx' is used. + * For single-value properties (not having specialized remove function) this + * function does nothing and just returns TRUE. + * + * Returns: TRUE on success; FALSE on failure and sets error + */ +gboolean +nmc_setting_remove_property_option (NMSetting *setting, + const char *prop, + const char *option, + guint32 idx, + GError **error) +{ + const NmcPropertyFuncs *item; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - g_free (mac_str); - g_free (mtu_str); + item = nmc_properties_find (nm_setting_get_name (setting), prop); + if (item && item->remove_func) + return item->remove_func (setting, prop, option, idx, error); return TRUE; } -gboolean -setting_bond_details (NMSettingBond *s_bond, NmCli *nmc) +/* + * Get valid property names for a setting. + * + * Returns: string array with the properties or NULL on failure. + * The returned value should be freed with g_strfreev() + */ +char ** +nmc_setting_get_valid_properties (NMSetting *setting) { - GString *bond_options_s; + char **valid_props = NULL; + GParamSpec **props, **iter; + guint num; int i; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; - g_return_val_if_fail (NM_IS_SETTING_BOND (s_bond), FALSE); + /* Iterate through properties */ + i = 0; + props = g_object_class_list_properties (G_OBJECT_GET_CLASS (G_OBJECT (setting)), &num); + valid_props = g_malloc0 (sizeof (char*) * (num + 1)); + for (iter = props; iter && *iter; iter++) { + const char *key_name = g_param_spec_get_name (*iter); - nmc->allowed_fields = nmc_fields_setting_bond; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_BOND_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ + /* Add all properties except for "name" that is non-editable */ + if (g_strcmp0 (key_name, "name") != 0) + valid_props[i++] = g_strdup (key_name); + } + valid_props[i] = NULL; - bond_options_s = g_string_new (NULL); - for (i = 0; i < nm_setting_bond_get_num_options (s_bond); i++) { - const char *key, *value; + return valid_props; +} - nm_setting_bond_get_option (s_bond, i, &key, &value); - g_string_append_printf (bond_options_s, "%s=%s,", key, value); +/* + * Return allowed values for 'prop' as a string. + */ +const char * +nmc_setting_get_property_allowed_values (NMSetting *setting, const char *prop) +{ + + const NmcPropertyFuncs *item; + + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + + item = nmc_properties_find (nm_setting_get_name (setting), prop); + if (item && item->values_func) + return item->values_func (setting, prop); + + return NULL; +} + +/* + * Create a description string for a property. + * + * It returns a description got from properties blurb, concatenated with + * nmcli specific description (if it exists). + * + * Returns: property description or NULL on failure. The caller must free the string. + */ +char * +nmc_setting_get_property_desc (NMSetting *setting, const char *prop) +{ + GParamSpec *spec; + const NmcPropertyFuncs *item; + const char *setting_desc = ""; + const char *setting_desc_title = ""; + const char *nmcli_desc = NULL; + const char *nmcli_desc_title = ""; + const char *nmcli_nl = ""; + + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + + spec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), prop); + if (spec) { + setting_desc = g_param_spec_get_blurb (spec); + setting_desc_title = _("[NM property description]"); } - g_string_truncate (bond_options_s, bond_options_s->len-1); /* chop off trailing ',' */ - nmc->allowed_fields[0].value = NM_SETTING_BOND_SETTING_NAME; - nmc->allowed_fields[1].value = nm_setting_bond_get_interface_name (s_bond); - nmc->allowed_fields[2].value = bond_options_s->str; + item = nmc_properties_find (nm_setting_get_name (setting), prop); + if (item && item->describe_func) { + nmcli_desc = item->describe_func (setting, prop); + nmcli_desc_title = _("[nmcli specific description]"); + nmcli_nl = "\n"; + } + + return g_strdup_printf ("%s\n%s\n%s%s%s%s", + setting_desc_title, setting_desc, + nmcli_nl, nmcli_desc_title, nmcli_nl, + nmcli_desc ? nmcli_desc : ""); +} + +/* + * Gets setting:prop property value and returns it in 'value'. + * Caller is responsible for freeing the GValue resources using g_value_unset() + */ +gboolean +nmc_property_get_gvalue (NMSetting *setting, const char *prop, GValue *value) +{ + GParamSpec *param_spec; + + param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), prop); + if (param_spec) { + memset (value, 0, sizeof (GValue)); + g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (param_spec)); + g_object_get_property (G_OBJECT (setting), prop, value); + return TRUE; + } + return FALSE; +} + +/* + * Sets setting:prop property value from 'value'. + */ +gboolean +nmc_property_set_gvalue (NMSetting *setting, const char *prop, GValue *value) +{ + GParamSpec *param_spec; + + param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), prop); + if (param_spec && G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (param_spec)) { + g_object_set_property (G_OBJECT (setting), prop, value); + return TRUE; + } + return FALSE; +} + +/*----------------------------------------------------------------------------*/ + +static gboolean +setting_connection_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingConnection *s_con = NM_SETTING_CONNECTION (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (s_con), FALSE); - g_string_free (bond_options_s, TRUE); + tmpl = nmc_fields_setting_connection; + tmpl_len = sizeof (nmc_fields_setting_connection); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_CONNECTION_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_connection_get_id (setting)); + set_val_str (arr, 2, nmc_property_connection_get_uuid (setting)); + set_val_str (arr, 3, nmc_property_connection_get_interface_name (setting)); + set_val_str (arr, 4, nmc_property_connection_get_type (setting)); + set_val_str (arr, 5, nmc_property_connection_get_autoconnect (setting)); + set_val_str (arr, 6, nmc_property_connection_get_timestamp (setting)); + set_val_str (arr, 7, nmc_property_connection_get_read_only (setting)); + set_val_str (arr, 8, nmc_property_connection_get_permissions (setting)); + set_val_str (arr, 9, nmc_property_connection_get_zone (setting)); + set_val_str (arr, 10, nmc_property_connection_get_master (setting)); + set_val_str (arr, 11, nmc_property_connection_get_slave_type (setting)); + set_val_str (arr, 12, nmc_property_connection_get_secondaries (setting)); + set_val_str (arr, 13, nmc_property_connection_get_gateway_ping_timeout (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ return TRUE; } -gboolean -setting_vlan_details (NMSettingVlan *s_vlan, NmCli *nmc) +static gboolean +setting_wired_details (NMSetting *setting, NmCli *nmc, const char *one_prop) { - char *vlan_id_str, *vlan_flags_str, *vlan_ingress_prio_str, *vlan_egress_prio_str; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + NMSettingWired *s_wired = NM_SETTING_WIRED (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; - g_return_val_if_fail (NM_IS_SETTING_VLAN (s_vlan), FALSE); + g_return_val_if_fail (NM_IS_SETTING_WIRED (s_wired), FALSE); - nmc->allowed_fields = nmc_fields_setting_vlan; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_VLAN_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ + tmpl = nmc_fields_setting_wired; + tmpl_len = sizeof (nmc_fields_setting_wired); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_WIRED_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_wired_get_port (setting)); + set_val_str (arr, 2, nmc_property_wired_get_speed (setting)); + set_val_str (arr, 3, nmc_property_wired_get_duplex (setting)); + set_val_str (arr, 4, nmc_property_wired_get_auto_negotiate (setting)); + set_val_str (arr, 5, nmc_property_wired_get_mac_address (setting)); + set_val_str (arr, 6, nmc_property_wired_get_cloned_mac_address (setting)); + set_val_str (arr, 7, nmc_property_wired_get_mac_address_blacklist (setting)); + set_val_str (arr, 8, nmc_property_wired_get_mtu (setting)); + set_val_str (arr, 9, nmc_property_wired_get_s390_subchannels (setting)); + set_val_str (arr, 10, nmc_property_wired_get_s390_nettype (setting)); + set_val_str (arr, 11, nmc_property_wired_get_s390_options (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ - vlan_id_str = g_strdup_printf ("%d", nm_setting_vlan_get_id (s_vlan)); - vlan_flags_str = vlan_flags_to_string (nm_setting_vlan_get_flags (s_vlan)); - vlan_ingress_prio_str = vlan_priorities_to_string (s_vlan, NM_VLAN_INGRESS_MAP); - vlan_egress_prio_str = vlan_priorities_to_string (s_vlan, NM_VLAN_EGRESS_MAP); + return TRUE; +} - nmc->allowed_fields[0].value = NM_SETTING_VLAN_SETTING_NAME; - nmc->allowed_fields[1].value = nm_setting_vlan_get_interface_name (s_vlan); - nmc->allowed_fields[2].value = nm_setting_vlan_get_parent (s_vlan); - nmc->allowed_fields[3].value = vlan_id_str; - nmc->allowed_fields[4].value = vlan_flags_str; - nmc->allowed_fields[5].value = vlan_ingress_prio_str; - nmc->allowed_fields[6].value = vlan_egress_prio_str; +static gboolean +setting_802_1X_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSetting8021x *s_8021x = NM_SETTING_802_1X (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (s_8021x), FALSE); + + tmpl = nmc_fields_setting_8021X; + tmpl_len = sizeof (nmc_fields_setting_8021X); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_802_1X_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_802_1X_get_eap (setting)); + set_val_str (arr, 2, nmc_property_802_1X_get_identity (setting)); + set_val_str (arr, 3, nmc_property_802_1X_get_anonymous_identity (setting)); + set_val_str (arr, 4, nmc_property_802_1X_get_pac_file (setting)); + set_val_str (arr, 5, nmc_property_802_1X_get_ca_cert (setting)); + set_val_str (arr, 6, nmc_property_802_1X_get_ca_path (setting)); + set_val_str (arr, 7, nmc_property_802_1X_get_subject_match (setting)); + set_val_str (arr, 8, nmc_property_802_1X_get_altsubject_matches (setting)); + set_val_str (arr, 9, nmc_property_802_1X_get_client_cert (setting)); + set_val_str (arr, 10, nmc_property_802_1X_get_phase1_peapver (setting)); + set_val_str (arr, 11, nmc_property_802_1X_get_phase1_peaplabel (setting)); + set_val_str (arr, 12, nmc_property_802_1X_get_phase1_fast_provisioning (setting)); + set_val_str (arr, 13, nmc_property_802_1X_get_phase2_auth (setting)); + set_val_str (arr, 14, nmc_property_802_1X_get_phase2_autheap (setting)); + set_val_str (arr, 15, nmc_property_802_1X_get_phase2_ca_cert (setting)); + set_val_str (arr, 16, nmc_property_802_1X_get_phase2_ca_path (setting)); + set_val_str (arr, 17, nmc_property_802_1X_get_phase2_subject_match (setting)); + set_val_str (arr, 18, nmc_property_802_1X_get_phase2_altsubject_matches (setting)); + set_val_str (arr, 19, nmc_property_802_1X_get_phase2_client_cert (setting)); + set_val_str (arr, 20, nmc_property_802_1X_get_password (setting)); + set_val_str (arr, 21, nmc_property_802_1X_get_password_flags (setting)); + set_val_str (arr, 22, nmc_property_802_1X_get_password_raw (setting)); + set_val_str (arr, 23, nmc_property_802_1X_get_password_raw_flags (setting)); + set_val_str (arr, 24, nmc_property_802_1X_get_private_key (setting)); + set_val_str (arr, 25, nmc_property_802_1X_get_private_key_password (setting)); + set_val_str (arr, 26, nmc_property_802_1X_get_private_key_password_flags (setting)); + set_val_str (arr, 27, nmc_property_802_1X_get_phase2_private_key (setting)); + set_val_str (arr, 28, nmc_property_802_1X_get_phase2_private_key_password (setting)); + set_val_str (arr, 29, nmc_property_802_1X_get_phase2_private_key_password_flags (setting)); + set_val_str (arr, 30, nmc_property_802_1X_get_pin (setting)); + set_val_str (arr, 31, nmc_property_802_1X_get_pin_flags (setting)); + set_val_str (arr, 32, nmc_property_802_1X_get_system_ca_certs (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + return TRUE; +} - g_free (vlan_id_str); - g_free (vlan_flags_str); - g_free (vlan_ingress_prio_str); - g_free (vlan_egress_prio_str); +static gboolean +setting_wireless_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingWireless *s_wireless = NM_SETTING_WIRELESS (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (s_wireless), FALSE); + + tmpl = nmc_fields_setting_wireless; + tmpl_len = sizeof (nmc_fields_setting_wireless); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_WIRELESS_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_wireless_get_ssid (setting)); + set_val_str (arr, 2, nmc_property_wireless_get_mode (setting)); + set_val_str (arr, 3, nmc_property_wireless_get_band (setting)); + set_val_str (arr, 4, nmc_property_wireless_get_channel (setting)); + set_val_str (arr, 5, nmc_property_wireless_get_bssid (setting)); + set_val_str (arr, 6, nmc_property_wireless_get_rate (setting)); + set_val_str (arr, 7, nmc_property_wireless_get_tx_power (setting)); + set_val_str (arr, 8, nmc_property_wireless_get_mac_address (setting)); + set_val_str (arr, 9, nmc_property_wireless_get_cloned_mac_address (setting)); + set_val_str (arr, 10, nmc_property_wireless_get_mac_address_blacklist (setting)); + set_val_str (arr, 11, nmc_property_wireless_get_mtu (setting)); + set_val_str (arr, 12, nmc_property_wireless_get_seen_bssids (setting)); + set_val_str (arr, 13, nmc_property_wireless_get_hidden (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ return TRUE; } -gboolean -setting_adsl_details (NMSettingAdsl *s_adsl, NmCli *nmc) +static gboolean +setting_wireless_security_details (NMSetting *setting, NmCli *nmc, const char *one_prop) { - char *pswd_flags; - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + NMSettingWirelessSecurity *s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; - g_return_val_if_fail (NM_IS_SETTING_ADSL (s_adsl), FALSE); + g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (s_wireless_sec), FALSE); - nmc->allowed_fields = nmc_fields_setting_adsl; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_ADSL_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ + tmpl = nmc_fields_setting_wireless_security; + tmpl_len = sizeof (nmc_fields_setting_wireless_security); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_WIRELESS_SECURITY_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_wifi_sec_get_key_mgmt (setting)); + set_val_str (arr, 2, nmc_property_wifi_sec_get_wep_tx_keyidx (setting)); + set_val_str (arr, 3, nmc_property_wifi_sec_get_auth_alg (setting)); + set_val_str (arr, 4, nmc_property_wifi_sec_get_proto (setting)); + set_val_str (arr, 5, nmc_property_wifi_sec_get_pairwise (setting)); + set_val_str (arr, 6, nmc_property_wifi_sec_get_group (setting)); + set_val_str (arr, 7, nmc_property_wifi_sec_get_leap_username (setting)); + set_val_str (arr, 8, nmc_property_wifi_sec_get_wep_key0 (setting)); + set_val_str (arr, 9, nmc_property_wifi_sec_get_wep_key1 (setting)); + set_val_str (arr, 10, nmc_property_wifi_sec_get_wep_key2 (setting)); + set_val_str (arr, 11, nmc_property_wifi_sec_get_wep_key3 (setting)); + set_val_str (arr, 12, nmc_property_wifi_sec_get_wep_key_flags (setting)); + set_val_str (arr, 13, nmc_property_wifi_sec_get_wep_key_type (setting)); + set_val_str (arr, 14, nmc_property_wifi_sec_get_psk (setting)); + set_val_str (arr, 15, nmc_property_wifi_sec_get_psk_flags (setting)); + set_val_str (arr, 16, nmc_property_wifi_sec_get_leap_password (setting)); + set_val_str (arr, 17, nmc_property_wifi_sec_get_leap_password_flags (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ - pswd_flags = secret_flags_to_string (nm_setting_adsl_get_password_flags (s_adsl)); + return TRUE; +} - nmc->allowed_fields[0].value = NM_SETTING_ADSL_SETTING_NAME; - nmc->allowed_fields[1].value = nm_setting_adsl_get_username (s_adsl); - nmc->allowed_fields[2].value = nm_setting_adsl_get_password (s_adsl); - nmc->allowed_fields[3].value = pswd_flags; - nmc->allowed_fields[4].value = nm_setting_adsl_get_protocol (s_adsl); - nmc->allowed_fields[5].value = nm_setting_adsl_get_encapsulation (s_adsl); - nmc->allowed_fields[6].value = g_strdup_printf ("%d", nm_setting_adsl_get_vpi (s_adsl)); - nmc->allowed_fields[7].value = g_strdup_printf ("%d", nm_setting_adsl_get_vci (s_adsl)); +static gboolean +setting_ip4_config_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingIP4Config *s_ip4 = NM_SETTING_IP4_CONFIG (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (s_ip4), FALSE); - g_free (pswd_flags); - g_free ((char*) nmc->allowed_fields[6].value); - g_free ((char*) nmc->allowed_fields[7].value); + tmpl = nmc_fields_setting_ip4_config; + tmpl_len = sizeof (nmc_fields_setting_ip4_config); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_IP4_CONFIG_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_ipv4_get_method (setting)); + set_val_str (arr, 2, nmc_property_ipv4_get_dns (setting)); + set_val_str (arr, 3, nmc_property_ipv4_get_dns_search (setting)); + set_val_str (arr, 4, nmc_property_ipv4_get_addresses (setting)); + set_val_str (arr, 5, nmc_property_ipv4_get_routes (setting)); + set_val_str (arr, 6, nmc_property_ipv4_get_ignore_auto_routes (setting)); + set_val_str (arr, 7, nmc_property_ipv4_get_ignore_auto_dns (setting)); + set_val_str (arr, 8, nmc_property_ipv4_get_dhcp_client_id (setting)); + set_val_str (arr, 9, nmc_property_ipv4_get_dhcp_send_hostname (setting)); + set_val_str (arr, 10, nmc_property_ipv4_get_dhcp_hostname (setting)); + set_val_str (arr, 11, nmc_property_ipv4_get_never_default (setting)); + set_val_str (arr, 12, nmc_property_ipv4_get_may_fail (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ return TRUE; } -gboolean -setting_bridge_details (NMSettingBridge *s_bridge, NmCli *nmc) +static gboolean +setting_ip6_config_details (NMSetting *setting, NmCli *nmc, const char *one_prop) { - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + NMSettingIP6Config *s_ip6 = NM_SETTING_IP6_CONFIG (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; - g_return_val_if_fail (NM_IS_SETTING_BRIDGE (s_bridge), FALSE); + g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (s_ip6), FALSE); + + tmpl = nmc_fields_setting_ip6_config; + tmpl_len = sizeof (nmc_fields_setting_ip6_config); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_IP6_CONFIG_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_ipv6_get_method (setting)); + set_val_str (arr, 2, nmc_property_ipv6_get_dns (setting)); + set_val_str (arr, 3, nmc_property_ipv6_get_dns_search (setting)); + set_val_str (arr, 4, nmc_property_ipv6_get_addresses (setting)); + set_val_str (arr, 5, nmc_property_ipv6_get_routes (setting)); + set_val_str (arr, 6, nmc_property_ipv6_get_ignore_auto_routes (setting)); + set_val_str (arr, 7, nmc_property_ipv6_get_ignore_auto_dns (setting)); + set_val_str (arr, 8, nmc_property_ipv6_get_never_default (setting)); + set_val_str (arr, 9, nmc_property_ipv6_get_may_fail (setting)); + set_val_str (arr, 10, nmc_property_ipv6_get_ip6_privacy (setting)); + set_val_str (arr, 11, nmc_property_ipv6_get_dhcp_hostname (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_serial_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingSerial *s_serial = NM_SETTING_SERIAL (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; - nmc->allowed_fields = nmc_fields_setting_bridge; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_BRIDGE_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ + g_return_val_if_fail (NM_IS_SETTING_SERIAL (s_serial), FALSE); - nmc->allowed_fields[0].value = NM_SETTING_BRIDGE_SETTING_NAME; - nmc->allowed_fields[1].value = nm_setting_bridge_get_interface_name (s_bridge); - nmc->allowed_fields[2].value = nm_setting_bridge_get_stp (s_bridge) ? _("yes") : _("no"); - nmc->allowed_fields[3].value = g_strdup_printf ("%u", nm_setting_bridge_get_priority (s_bridge)); - nmc->allowed_fields[4].value = g_strdup_printf ("%u", nm_setting_bridge_get_forward_delay (s_bridge)); - nmc->allowed_fields[5].value = g_strdup_printf ("%u", nm_setting_bridge_get_hello_time (s_bridge)); - nmc->allowed_fields[6].value = g_strdup_printf ("%u", nm_setting_bridge_get_max_age (s_bridge)); - nmc->allowed_fields[7].value = g_strdup_printf ("%u", nm_setting_bridge_get_ageing_time (s_bridge)); + tmpl = nmc_fields_setting_serial; + tmpl_len = sizeof (nmc_fields_setting_serial); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_SERIAL_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_serial_get_baud (setting)); + set_val_str (arr, 2, nmc_property_serial_get_bits (setting)); + set_val_str (arr, 3, nmc_property_serial_get_parity (setting)); + set_val_str (arr, 4, nmc_property_serial_get_stopbits (setting)); + set_val_str (arr, 5, nmc_property_serial_get_send_delay (setting)); + g_ptr_array_add (nmc->output_data, arr); - /* free values */ - g_free ((char*) nmc->allowed_fields[3].value); - g_free ((char*) nmc->allowed_fields[4].value); - g_free ((char*) nmc->allowed_fields[5].value); - g_free ((char*) nmc->allowed_fields[6].value); - g_free ((char*) nmc->allowed_fields[7].value); + print_data (nmc); /* Print all data */ return TRUE; } -gboolean -setting_bridge_port_details (NMSettingBridgePort *s_bridge_port, NmCli *nmc) +static gboolean +setting_ppp_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingPPP *s_ppp = NM_SETTING_PPP (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_PPP (s_ppp), FALSE); + + tmpl = nmc_fields_setting_ppp; + tmpl_len = sizeof (nmc_fields_setting_ppp); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_PPP_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_ppp_get_noauth (setting)); + set_val_str (arr, 2, nmc_property_ppp_get_refuse_eap (setting)); + set_val_str (arr, 3, nmc_property_ppp_get_refuse_pap (setting)); + set_val_str (arr, 4, nmc_property_ppp_get_refuse_chap (setting)); + set_val_str (arr, 5, nmc_property_ppp_get_refuse_mschap (setting)); + set_val_str (arr, 6, nmc_property_ppp_get_refuse_mschapv2 (setting)); + set_val_str (arr, 7, nmc_property_ppp_get_nobsdcomp (setting)); + set_val_str (arr, 8, nmc_property_ppp_get_nodeflate (setting)); + set_val_str (arr, 9, nmc_property_ppp_get_no_vj_comp (setting)); + set_val_str (arr, 10, nmc_property_ppp_get_require_mppe (setting)); + set_val_str (arr, 11, nmc_property_ppp_get_require_mppe_128 (setting)); + set_val_str (arr, 12, nmc_property_ppp_get_mppe_stateful (setting)); + set_val_str (arr, 13, nmc_property_ppp_get_crtscts (setting)); + set_val_str (arr, 14, nmc_property_ppp_get_baud (setting)); + set_val_str (arr, 15, nmc_property_ppp_get_mru (setting)); + set_val_str (arr, 16, nmc_property_ppp_get_mtu (setting)); + set_val_str (arr, 17, nmc_property_ppp_get_lcp_echo_failure (setting)); + set_val_str (arr, 18, nmc_property_ppp_get_lcp_echo_interval (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_pppoe_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingPPPOE *s_pppoe = NM_SETTING_PPPOE (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_PPPOE (s_pppoe), FALSE); + + tmpl = nmc_fields_setting_pppoe; + tmpl_len = sizeof (nmc_fields_setting_pppoe); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_PPPOE_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_pppoe_get_service (setting)); + set_val_str (arr, 2, nmc_property_pppoe_get_username (setting)); + set_val_str (arr, 3, nmc_property_pppoe_get_password (setting)); + set_val_str (arr, 4, nmc_property_pppoe_get_password_flags (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_gsm_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingGsm *s_gsm = NM_SETTING_GSM (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_GSM (s_gsm), FALSE); + + tmpl = nmc_fields_setting_gsm; + tmpl_len = sizeof (nmc_fields_setting_gsm); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_GSM_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_gsm_get_number (setting)); + set_val_str (arr, 2, nmc_property_gsm_get_username (setting)); + set_val_str (arr, 3, nmc_property_gsm_get_password (setting)); + set_val_str (arr, 4, nmc_property_gsm_get_password_flags (setting)); + set_val_str (arr, 5, nmc_property_gsm_get_apn (setting)); + set_val_str (arr, 6, nmc_property_gsm_get_network_id (setting)); + set_val_str (arr, 7, nmc_property_gsm_get_network_type (setting)); + set_val_str (arr, 8, nmc_property_gsm_get_allowed_bands (setting)); + set_val_str (arr, 9, nmc_property_gsm_get_pin (setting)); + set_val_str (arr, 10, nmc_property_gsm_get_pin_flags (setting)); + set_val_str (arr, 11, nmc_property_gsm_get_home_only (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_cdma_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingCdma *s_cdma = NM_SETTING_CDMA (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_CDMA (s_cdma), FALSE); + + tmpl = nmc_fields_setting_cdma; + tmpl_len = sizeof (nmc_fields_setting_cdma); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_CDMA_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_cdma_get_number (setting)); + set_val_str (arr, 2, nmc_property_cdma_get_username (setting)); + set_val_str (arr, 3, nmc_property_cdma_get_password (setting)); + set_val_str (arr, 4, nmc_property_cdma_get_password_flags (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_bluetooth_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingBluetooth *s_bluetooth = NM_SETTING_BLUETOOTH (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_BLUETOOTH (s_bluetooth), FALSE); + + tmpl = nmc_fields_setting_bluetooth; + tmpl_len = sizeof (nmc_fields_setting_bluetooth); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_BLUETOOTH_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_bluetooth_get_bdaddr (setting)); + set_val_str (arr, 2, nmc_property_bluetooth_get_type (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_olpc_mesh_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingOlpcMesh *s_olpc_mesh = NM_SETTING_OLPC_MESH (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_OLPC_MESH (s_olpc_mesh), FALSE); + + tmpl = nmc_fields_setting_olpc_mesh; + tmpl_len = sizeof (nmc_fields_setting_olpc_mesh); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_OLPC_MESH_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_olpc_get_ssid (setting)); + set_val_str (arr, 2, nmc_property_olpc_get_channel (setting)); + set_val_str (arr, 3, nmc_property_olpc_get_anycast_address (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_vpn_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingVPN *s_vpn = NM_SETTING_VPN (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_VPN (s_vpn), FALSE); + + tmpl = nmc_fields_setting_vpn; + tmpl_len = sizeof (nmc_fields_setting_vpn); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_VPN_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_vpn_get_service_type (setting)); + set_val_str (arr, 2, nmc_property_vpn_get_user_name (setting)); + set_val_str (arr, 3, nmc_property_vpn_get_data (setting)); + set_val_str (arr, 4, nmc_property_vpn_get_secrets (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_wimax_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingWimax *s_wimax = NM_SETTING_WIMAX (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_WIMAX (s_wimax), FALSE); + + tmpl = nmc_fields_setting_wimax; + tmpl_len = sizeof (nmc_fields_setting_wimax); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_WIMAX_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_wimax_get_mac_address (setting)); + set_val_str (arr, 2, nmc_property_wimax_get_network_name (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_infiniband_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingInfiniband *s_infiniband = NM_SETTING_INFINIBAND (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_INFINIBAND (s_infiniband), FALSE); + + tmpl = nmc_fields_setting_infiniband; + tmpl_len = sizeof (nmc_fields_setting_infiniband); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_INFINIBAND_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_ib_get_mac_address (setting)); + set_val_str (arr, 2, nmc_property_ib_get_mtu (setting)); + set_val_str (arr, 3, nmc_property_ib_get_transport_mode (setting)); + set_val_str (arr, 4, nmc_property_ib_get_p_key (setting)); + set_val_str (arr, 5, nmc_property_ib_get_parent (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_bond_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingBond *s_bond = NM_SETTING_BOND (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_BOND (s_bond), FALSE); + + tmpl = nmc_fields_setting_bond; + tmpl_len = sizeof (nmc_fields_setting_bond); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_BOND_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_bond_get_interface_name (setting)); + set_val_str (arr, 2, nmc_property_bond_get_options (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_vlan_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingVlan *s_vlan = NM_SETTING_VLAN (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_VLAN (s_vlan), FALSE); + + tmpl = nmc_fields_setting_vlan; + tmpl_len = sizeof (nmc_fields_setting_vlan); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_VLAN_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_vlan_get_interface_name (setting)); + set_val_str (arr, 2, nmc_property_vlan_get_parent (setting)); + set_val_str (arr, 3, nmc_property_vlan_get_id (setting)); + set_val_str (arr, 4, nmc_property_vlan_get_flags (setting)); + set_val_str (arr, 5, nmc_property_vlan_get_ingress_priority_map (setting)); + set_val_str (arr, 6, nmc_property_vlan_get_egress_priority_map (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_adsl_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingAdsl *s_adsl = NM_SETTING_ADSL (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_ADSL (s_adsl), FALSE); + + tmpl = nmc_fields_setting_adsl; + tmpl_len = sizeof (nmc_fields_setting_adsl); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_ADSL_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_adsl_get_username (setting)); + set_val_str (arr, 2, nmc_property_adsl_get_password (setting)); + set_val_str (arr, 3, nmc_property_adsl_get_password_flags (setting)); + set_val_str (arr, 4, nmc_property_adsl_get_protocol (setting)); + set_val_str (arr, 5, nmc_property_adsl_get_encapsulation (setting)); + set_val_str (arr, 6, nmc_property_adsl_get_vpi (setting)); + set_val_str (arr, 7, nmc_property_adsl_get_vci (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_bridge_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingBridge *s_bridge = NM_SETTING_BRIDGE (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_BRIDGE (s_bridge), FALSE); + + tmpl = nmc_fields_setting_bridge; + tmpl_len = sizeof (nmc_fields_setting_bridge); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_BRIDGE_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_bridge_get_interface_name (setting)); + set_val_str (arr, 2, nmc_property_bridge_get_mac_address (setting)); + set_val_str (arr, 3, nmc_property_bridge_get_stp (setting)); + set_val_str (arr, 4, nmc_property_bridge_get_priority (setting)); + set_val_str (arr, 5, nmc_property_bridge_get_forward_delay (setting)); + set_val_str (arr, 6, nmc_property_bridge_get_hello_time (setting)); + set_val_str (arr, 7, nmc_property_bridge_get_max_age (setting)); + set_val_str (arr, 8, nmc_property_bridge_get_ageing_time (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_bridge_port_details (NMSetting *setting, NmCli *nmc, const char *one_prop) { - guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; - guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; - guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; + NMSettingBridgePort *s_bridge_port = NM_SETTING_BRIDGE_PORT (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; g_return_val_if_fail (NM_IS_SETTING_BRIDGE_PORT (s_bridge_port), FALSE); - nmc->allowed_fields = nmc_fields_setting_bridge_port; - nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTING_BRIDGE_PORT_ALL, nmc->allowed_fields, NULL); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print field names */ + tmpl = nmc_fields_setting_bridge_port; + tmpl_len = sizeof (nmc_fields_setting_bridge_port); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_BRIDGE_PORT_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_bridge_port_get_priority (setting)); + set_val_str (arr, 2, nmc_property_bridge_port_get_path_cost (setting)); + set_val_str (arr, 3, nmc_property_bridge_port_get_hairpin_mode (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_team_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingTeam *s_team = NM_SETTING_TEAM (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; - nmc->allowed_fields[0].value = NM_SETTING_BRIDGE_PORT_SETTING_NAME; - nmc->allowed_fields[1].value = g_strdup_printf ("%u", nm_setting_bridge_port_get_priority (s_bridge_port)); - nmc->allowed_fields[2].value = g_strdup_printf ("%u", nm_setting_bridge_port_get_path_cost (s_bridge_port)); - nmc->allowed_fields[3].value = nm_setting_bridge_port_get_hairpin_mode (s_bridge_port) ? _("yes") : _("no"); + g_return_val_if_fail (NM_IS_SETTING_TEAM (s_team), FALSE); - nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; - print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ + tmpl = nmc_fields_setting_team; + tmpl_len = sizeof (nmc_fields_setting_team); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_TEAM_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); - g_free ((char*) nmc->allowed_fields[1].value); - g_free ((char*) nmc->allowed_fields[2].value); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_team_get_interface_name (setting)); + set_val_str (arr, 2, nmc_property_team_get_config (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ return TRUE; } +static gboolean +setting_team_port_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingTeamPort *s_team_port = NM_SETTING_TEAM_PORT (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (s_team_port), FALSE); + + tmpl = nmc_fields_setting_team_port; + tmpl_len = sizeof (nmc_fields_setting_team_port); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_TEAM_PORT_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_team_port_get_config (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +static gboolean +setting_dcb_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + NMSettingDcb *s_dcb = NM_SETTING_DCB (setting); + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + g_return_val_if_fail (NM_IS_SETTING_DCB (s_dcb), FALSE); + + tmpl = nmc_fields_setting_dcb; + tmpl_len = sizeof (nmc_fields_setting_dcb); + nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_DCB_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); + set_val_str (arr, 1, nmc_property_dcb_get_app_fcoe_flags (setting)); + set_val_str (arr, 2, nmc_property_dcb_get_app_fcoe_priority (setting)); + set_val_str (arr, 3, nmc_property_dcb_get_app_fcoe_mode (setting)); + set_val_str (arr, 4, nmc_property_dcb_get_app_iscsi_flags (setting)); + set_val_str (arr, 5, nmc_property_dcb_get_app_iscsi_priority (setting)); + set_val_str (arr, 6, nmc_property_dcb_get_app_fip_flags (setting)); + set_val_str (arr, 7, nmc_property_dcb_get_app_fip_priority (setting)); + set_val_str (arr, 8, nmc_property_dcb_get_pfc_flags (setting)); + set_val_str (arr, 9, nmc_property_dcb_get_pfc (setting)); + set_val_str (arr, 10, nmc_property_dcb_get_pg_flags (setting)); + set_val_str (arr, 11, nmc_property_dcb_get_pg_group_id (setting)); + set_val_str (arr, 12, nmc_property_dcb_get_pg_group_bandwidth (setting)); + set_val_str (arr, 13, nmc_property_dcb_get_pg_bandwidth (setting)); + set_val_str (arr, 14, nmc_property_dcb_get_pg_strict (setting)); + set_val_str (arr, 15, nmc_property_dcb_get_pg_traffic_class (setting)); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + return TRUE; +} + +typedef struct { + const char *sname; + gboolean (*func) (NMSetting *setting, NmCli *nmc, const char *one_prop); +} SettingDetails; + +static const SettingDetails detail_printers[] = { + { NM_SETTING_CONNECTION_SETTING_NAME, setting_connection_details }, + { NM_SETTING_WIRED_SETTING_NAME, setting_wired_details }, + { NM_SETTING_802_1X_SETTING_NAME, setting_802_1X_details }, + { NM_SETTING_WIRELESS_SETTING_NAME, setting_wireless_details }, + { NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, setting_wireless_security_details }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, setting_ip4_config_details }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, setting_ip6_config_details }, + { NM_SETTING_SERIAL_SETTING_NAME, setting_serial_details }, + { NM_SETTING_PPP_SETTING_NAME, setting_ppp_details }, + { NM_SETTING_PPPOE_SETTING_NAME, setting_pppoe_details }, + { NM_SETTING_GSM_SETTING_NAME, setting_gsm_details }, + { NM_SETTING_CDMA_SETTING_NAME, setting_cdma_details }, + { NM_SETTING_BLUETOOTH_SETTING_NAME, setting_bluetooth_details }, + { NM_SETTING_OLPC_MESH_SETTING_NAME, setting_olpc_mesh_details }, + { NM_SETTING_VPN_SETTING_NAME, setting_vpn_details }, + { NM_SETTING_WIMAX_SETTING_NAME, setting_wimax_details }, + { NM_SETTING_INFINIBAND_SETTING_NAME, setting_infiniband_details }, + { NM_SETTING_BOND_SETTING_NAME, setting_bond_details }, + { NM_SETTING_VLAN_SETTING_NAME, setting_vlan_details }, + { NM_SETTING_ADSL_SETTING_NAME, setting_adsl_details }, + { NM_SETTING_BRIDGE_SETTING_NAME, setting_bridge_details }, + { NM_SETTING_BRIDGE_PORT_SETTING_NAME, setting_bridge_port_details }, + { NM_SETTING_TEAM_SETTING_NAME, setting_team_details }, + { NM_SETTING_TEAM_PORT_SETTING_NAME, setting_team_port_details }, + { NM_SETTING_DCB_SETTING_NAME, setting_dcb_details }, + { NULL }, +}; + +gboolean +setting_details (NMSetting *setting, NmCli *nmc, const char *one_prop) +{ + const SettingDetails *iter = &detail_printers[0]; + + g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); + + while (iter->sname) { + if (nm_connection_lookup_setting_type (iter->sname) == G_OBJECT_TYPE (setting)) + return iter->func (setting, nmc, one_prop); + iter++; + } + + g_assert_not_reached (); + return FALSE; +} + diff --git a/cli/src/settings.h b/cli/src/settings.h index 15e5577f..709c8977 100644 --- a/cli/src/settings.h +++ b/cli/src/settings.h @@ -14,7 +14,7 @@ * 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 - 2012 Red Hat, Inc. + * (C) Copyright 2010 - 2013 Red Hat, Inc. */ #ifndef NMC_SETTINGS_H @@ -42,33 +42,48 @@ #include <nm-setting-bridge.h> #include <nm-setting-bridge-port.h> #include <nm-setting-vlan.h> +#include <nm-setting-dcb.h> #include "nmcli.h" #include "utils.h" +/* --- Functions --- */ -gboolean setting_details (NMSetting *ssetting, NmCli *nmc); -gboolean setting_connection_details (NMSettingConnection *s_con, NmCli *nmc); -gboolean setting_wired_details (NMSettingWired *s_wired, NmCli *nmc); -gboolean setting_802_1X_details (NMSetting8021x *s_8021X, NmCli *nmc); -gboolean setting_wireless_details (NMSettingWireless *s_wireless, NmCli *nmc); -gboolean setting_wireless_security_details (NMSettingWirelessSecurity *s_wsec, NmCli *nmc); -gboolean setting_ip4_config_details (NMSettingIP4Config *s_ip4, NmCli *nmc); -gboolean setting_ip6_config_details (NMSettingIP6Config *s_ip6, NmCli *nmc); -gboolean setting_serial_details (NMSettingSerial *s_serial, NmCli *nmc); -gboolean setting_ppp_details (NMSettingPPP *s_ppp, NmCli *nmc); -gboolean setting_pppoe_details (NMSettingPPPOE *s_pppoe, NmCli *nmc); -gboolean setting_gsm_details (NMSettingGsm *s_gsm, NmCli *nmc); -gboolean setting_cdma_details (NMSettingCdma *s_cdma, NmCli *nmc); -gboolean setting_bluetooth_details (NMSettingBluetooth *s_bluetooth, NmCli *nmc); -gboolean setting_olpc_mesh_details (NMSettingOlpcMesh *s_olpc_mesh, NmCli *nmc); -gboolean setting_vpn_details (NMSettingVPN *s_vpn, NmCli *nmc); -gboolean setting_wimax_details (NMSettingWimax *s_wimax, NmCli *nmc); -gboolean setting_infiniband_details (NMSettingInfiniband *s_infiniband, NmCli *nmc); -gboolean setting_bond_details (NMSettingBond *s_bond, NmCli *nmc); -gboolean setting_vlan_details (NMSettingVlan *s_vlan, NmCli *nmc); -gboolean setting_adsl_details (NMSettingAdsl *s_adsl, NmCli *nmc); -gboolean setting_bridge_details (NMSettingBridge *s_bridge, NmCli *nmc); -gboolean setting_bridge_port_details (NMSettingBridgePort *s_bridge_port, NmCli *nmc); +void nmc_properties_init (void); +void nmc_properties_cleanup (void); + +NMSetting *nmc_setting_new_for_name (const char *name); +void nmc_setting_custom_init (NMSetting *setting); +void nmc_setting_ip4_connect_handlers (NMSettingIP4Config *setting); +void nmc_setting_ip6_connect_handlers (NMSettingIP6Config *setting); +void nmc_setting_wireless_connect_handlers (NMSettingWireless *setting); + +char **nmc_setting_get_valid_properties (NMSetting *setting); +char *nmc_setting_get_property_desc (NMSetting *setting, const char *prop); +const char *nmc_setting_get_property_allowed_values (NMSetting *setting, const char *prop); +char *nmc_setting_get_property (NMSetting *setting, + const char *prop, + GError **error); +char *nmc_setting_get_property_out2in (NMSetting *setting, + const char *prop, + GError **error); +gboolean nmc_setting_set_property (NMSetting *setting, + const char *prop, + const char *val, + GError **error); +gboolean nmc_setting_reset_property (NMSetting *setting, + const char *prop, + GError **error); +gboolean nmc_setting_remove_property_option (NMSetting *setting, + const char *prop, + const char *option, + guint32 idx, + GError **error); +void nmc_property_set_default_value (NMSetting *setting, const char *prop); + +gboolean nmc_property_get_gvalue (NMSetting *setting, const char *prop, GValue *value); +gboolean nmc_property_set_gvalue (NMSetting *setting, const char *prop, GValue *value); + +gboolean setting_details (NMSetting *ssetting, NmCli *nmc, const char *one_prop); #endif /* NMC_SETTINGS_H */ diff --git a/cli/src/utils.c b/cli/src/utils.c index ab0a5dba..9b513f5a 100644 --- a/cli/src/utils.c +++ b/cli/src/utils.c @@ -14,7 +14,7 @@ * 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 - 2012 Red Hat, Inc. + * (C) Copyright 2010 - 2014 Red Hat, Inc. */ /* Generated configuration file */ @@ -22,6 +22,8 @@ #include <stdio.h> #include <string.h> +#include <stdlib.h> +#include <errno.h> #include <sys/socket.h> #include <netinet/in.h> @@ -36,8 +38,8 @@ int matches (const char *cmd, const char *pattern) { - int len = strlen (cmd); - if (len > strlen (pattern)) + size_t len = strlen (cmd); + if (!len || len > strlen (pattern)) return -1; return memcmp (pattern, cmd, len); } @@ -57,14 +59,116 @@ next_arg (int *argc, char ***argv) 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); +} + + /* - * Convert SSID to a printable form. - * If it is an UTF-8 string, enclose it in quotes and return it. - * Otherwise convert it to a hex string representation. + * 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_printable (const char *str, gsize len) +ssid_to_hex (const char *str, gsize len) { GString *printable; char *printable_str; @@ -73,9 +177,6 @@ ssid_to_printable (const char *str, gsize len) if (str == NULL || len == 0) return NULL; - if (g_utf8_validate (str, len, NULL)) - return g_strdup_printf ("'%.*s'", (int) len, str); - printable = g_string_new (NULL); for (i = 0; i < len; i++) { g_string_append_printf (printable, "%02X", (unsigned char) str[i]); @@ -91,19 +192,19 @@ ssid_to_printable (const char *str, gsize len) char * nmc_ip4_address_as_string (guint32 ip, GError **error) { - struct in_addr tmp_addr; + guint32 tmp_addr; char buf[INET_ADDRSTRLEN]; g_return_val_if_fail (error == NULL || *error == NULL, NULL); memset (&buf, '\0', sizeof (buf)); - tmp_addr.s_addr = ip; + 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.s_addr)); + ntohl (tmp_addr)); return NULL; } } @@ -170,6 +271,298 @@ nmc_terminal_show_progress (const char *str) 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 <min,max> 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 <min,max> 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 */ @@ -189,54 +582,165 @@ nmc_string_screen_width (const char *start, const char *end) } void -set_val_str (NmcOutputField fields_array[], guint32 idx, const char *value) +set_val_str (NmcOutputField fields_array[], guint32 idx, char *value) { - fields_array[idx].flags = 0; fields_array[idx].value = value; + fields_array[idx].value_is_array = FALSE; + fields_array[idx].free_value = TRUE; } void -set_val_arr (NmcOutputField fields_array[], guint32 idx, const char **value) +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].flags = NMC_OF_FLAG_ARRAY; 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; } /* - * Parse comma separated fields in 'fields_str' according to 'fields_array'. - * IN: 'field_str': comma-separated fields names - * 'fields_array': array of allowed fields - * RETURN: GArray with indices representing fields in 'fields_array'. - * Caller is responsible to free it. + * 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[], GError **error) +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; + 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++) { - for (i = 0; fields_array[i].name; i++) { - if (strcasecmp (*iter, fields_array[i].name) == 0) { - g_array_append_val (array, i); - break; + 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); + } - else - g_set_error (error, NMCLI_ERROR, 1, _("invalid field '%s'"), *iter); + /* 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; } } @@ -246,6 +750,35 @@ done: 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) { @@ -264,14 +797,68 @@ nmc_terse_option_check (NMCPrintOutput print_output, const char *fields, GError 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 'fields.indices' array. - * 'fields.flags' specify various aspects influencing the output. + * 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_fields (const NmcPrintFields fields, const NmcOutputField field_values[]) +print_required_fields (NmCli *nmc, const NmcOutputField field_values[]) { GString *str; int width1, width2; @@ -280,14 +867,15 @@ print_fields (const NmcPrintFields fields, const NmcOutputField field_values[]) char *indent_str; const char *not_set_str = "--"; int i; - gboolean multiline = fields.flags & NMC_PF_FLAG_MULTILINE; - gboolean terse = fields.flags & NMC_PF_FLAG_TERSE; - gboolean pretty = fields.flags & NMC_PF_FLAG_PRETTY; - gboolean main_header_add = fields.flags & NMC_PF_FLAG_MAIN_HEADER_ADD; - gboolean main_header_only = fields.flags & NMC_PF_FLAG_MAIN_HEADER_ONLY; - gboolean field_names = fields.flags & NMC_PF_FLAG_FIELD_NAMES; - gboolean escape = fields.flags & NMC_PF_FLAG_ESCAPE; - gboolean section_prefix = fields.flags & NMC_PF_FLAG_SECTION_PREFIX; + 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: @@ -319,25 +907,29 @@ print_fields (const NmcPrintFields fields, const NmcOutputField field_values[]) for (i = 0; i < fields.indices->len; i++) { char *tmp; int idx = g_array_index (fields.indices, int, i); - guint32 value_is_array = field_values[idx].flags & NMC_OF_FLAG_ARRAY; + gboolean is_array = field_values[idx].value_is_array; /* section prefix can't be an array */ - g_assert (!value_is_array || !section_prefix || idx != 0); + g_assert (!is_array || !section_prefix || idx != 0); if (section_prefix && idx == 0) /* The first field is section prefix */ continue; - if (value_is_array) { + 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); - printf ("%-*s%s\n", terse ? 0 : ML_VALUE_INDENT, tmp, *p ? *p : not_set_str); + 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 { @@ -345,10 +937,14 @@ print_fields (const NmcPrintFields fields, const NmcOutputField field_values[]) 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)); - printf ("%-*s%s\n", terse ? 0 : ML_VALUE_INDENT, tmp, val ? val : not_set_str); + 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); } } @@ -366,14 +962,8 @@ print_fields (const NmcPrintFields fields, const NmcOutputField field_values[]) for (i = 0; i < fields.indices->len; i++) { int idx = g_array_index (fields.indices, int, i); - guint32 value_is_array = field_values[idx].flags & NMC_OF_FLAG_ARRAY; - char *value; - if (field_names) - value = _(field_values[idx].name_l10n); - else - value = field_values[idx].value ? - (value_is_array ? g_strjoinv (" | ", (char **) field_values[idx].value) : (char *) field_values[idx].value) : - (char *) not_set_str; + gboolean dealloc; + char *value = get_value_to_print ((NmcOutputField *) field_values+idx, field_names, not_set_str, &dealloc); if (terse) { if (escape) { @@ -396,7 +986,7 @@ print_fields (const NmcPrintFields fields, const NmcOutputField field_values[]) table_width += field_values[idx].width + width1 - width2 + 1; } - if (value_is_array && field_values[idx].value && !field_values) + if (dealloc) g_free (value); } @@ -438,57 +1028,56 @@ print_fields (const NmcPrintFields fields, const NmcOutputField field_values[]) } /* - * Find out whether NetworkManager is running (via D-Bus NameHasOwner), assuring - * NetworkManager won't be autostart (by D-Bus) if not running. - * We can't use NMClient (nm_client_get_manager_running()) because NMClient - * constructor calls GetPermissions of NM_DBUS_SERVICE, which would autostart - * NetworkManger if it is configured as D-Bus launchable service. + * 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. */ -gboolean -nmc_is_nm_running (NmCli *nmc, GError **error) +void +print_data (NmCli *nmc) { - DBusGConnection *connection = NULL; - DBusGProxy *proxy = NULL; - GError *err = NULL; - gboolean has_owner = FALSE; + int i, j; + size_t len; + NmcOutputField *row; + int num_fields = 0; - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + if (!nmc->output_data || nmc->output_data->len < 1) + return; - connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err); - if (!connection) { - g_string_printf (nmc->return_text, _("Error: Couldn't connect to system bus: %s"), err->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - g_propagate_error (error, err); - goto done; + /* How many fields? */ + row = g_ptr_array_index (nmc->output_data, 0); + while (row->name) { + num_fields++; + row++; } - proxy = dbus_g_proxy_new_for_name (connection, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus"); - if (!proxy) { - g_string_printf (nmc->return_text, _("Error: Couldn't create D-Bus object proxy for org.freedesktop.DBus")); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - if (error) - g_set_error_literal (error, NMCLI_ERROR, 0, nmc->return_text->str); - goto done; + /* 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; + } } - if (!org_freedesktop_DBus_name_has_owner (proxy, NM_DBUS_SERVICE, &has_owner, &err)) { - g_string_printf (nmc->return_text, _("Error: NameHasOwner request failed: %s"), - (err && err->message) ? err->message : _("(unknown)")); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - g_propagate_error (error, err); - goto done; + /* 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); } - -done: - if (connection) - dbus_g_connection_unref (connection); - if (proxy) - g_object_unref (proxy); - - return has_owner; } /* diff --git a/cli/src/utils.h b/cli/src/utils.h index 81ce1633..e92952f3 100644 --- a/cli/src/utils.h +++ b/cli/src/utils.h @@ -14,7 +14,7 @@ * 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 - 2012 Red Hat, Inc. + * (C) Copyright 2010 - 2014 Red Hat, Inc. */ #ifndef NMC_UTILS_H @@ -24,21 +24,74 @@ #include "nmcli.h" +/* === Types === */ + +typedef struct { + const char *name; + gboolean has_value; + const char **value; + gboolean mandatory; + gboolean found; +} nmc_arg_t; + /* === Functions === */ int matches (const char *cmd, const char *pattern); int next_arg (int *argc, char ***argv); -char *ssid_to_printable (const char *str, gsize len); +gboolean nmc_arg_is_help (const char *arg); +gboolean nmc_arg_is_option (const char *arg, const char *opt_name); +gboolean nmc_parse_args (nmc_arg_t *arg_arr, gboolean last, int *argc, char ***argv, GError **error); +char *ssid_to_hex (const char *str, gsize len); +gboolean nmc_string_to_int_base (const char *str, + int base, + gboolean range_check, + long int min, + long int max, + long int *value); +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); +gboolean nmc_string_to_int (const char *str, + gboolean range_check, + long int min, + long int max, + long int *value); +gboolean nmc_string_to_uint (const char *str, + gboolean range_check, + unsigned long int min, + unsigned long int max, + unsigned long int *value); +gboolean nmc_string_to_bool (const char *str, gboolean *val_bool, GError **error); char *nmc_ip4_address_as_string (guint32 ip, GError **error); char *nmc_ip6_address_as_string (const struct in6_addr *ip, GError **error); void nmc_terminal_erase_line (void); void nmc_terminal_show_progress (const char *str); +const char *nmc_term_color_sequence (NmcTermColor color); +char *nmc_colorize (NmcTermColor color, const char * fmt, ...); +char *nmc_get_user_input (const char *ask_str); +int nmc_string_to_arg_array (const char *line, const char *delim, char ***argv, int *argc); +const char *nmc_string_is_valid (const char *input, const char **allowed, GError **error); +GSList *nmc_util_strv_to_slist (char **strv); +char **nmc_strsplit_set (const char *str, const char *delimiter, int max_tokens); int nmc_string_screen_width (const char *start, const char *end); -void set_val_str (NmcOutputField fields_array[], guint32 index, const char *value); -void set_val_arr (NmcOutputField fields_array[], guint32 index, const char **value); -GArray *parse_output_fields (const char *fields_str, const NmcOutputField fields_array[], GError **error); +void set_val_str (NmcOutputField fields_array[], guint32 index, char *value); +void set_val_strc (NmcOutputField fields_array[], guint32 index, const char *value); +void set_val_arr (NmcOutputField fields_array[], guint32 index, char **value); +void set_val_arrc (NmcOutputField fields_array[], guint32 index, const char **value); +void nmc_free_output_field_values (NmcOutputField fields_array[]); +GArray *parse_output_fields (const char *fields_str, + const NmcOutputField fields_array[], + gboolean parse_groups, + GPtrArray **group_fields, + GError **error); +char *nmc_get_allowed_fields (const NmcOutputField fields_array[], int group_idx); gboolean nmc_terse_option_check (NMCPrintOutput print_output, const char *fields, GError **error); -void print_fields (const NmcPrintFields fields, const NmcOutputField field_values[]); -gboolean nmc_is_nm_running (NmCli *nmc, GError **error); +NmcOutputField *nmc_dup_fields_array (NmcOutputField fields[], size_t size, guint32 flags); +void nmc_empty_output_fields (NmCli *nmc); +void print_required_fields (NmCli *nmc, const NmcOutputField field_values[]); +void print_data (NmCli *nmc); gboolean nmc_versions_match (NmCli *nmc); #endif /* NMC_UTILS_H */ |