diff options
| author | Michael Biebl <biebl@debian.org> | 2010-11-29 00:47:11 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2010-11-29 00:47:11 +0100 |
| commit | 9fae01fa351805b2903e535736e06971bc0b925e (patch) | |
| tree | c19bccf84c9f894e50fae678ba6ee2b2044e2d30 /src | |
Imported Upstream version 0.8.1 upstream/0.8.1
Diffstat (limited to 'src')
176 files changed, 65542 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..5d2db6e3 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,278 @@ +SUBDIRS= \ + logging \ + named-manager \ + vpn-manager \ + dhcp-manager \ + ip6-manager \ + supplicant-manager \ + ppp-manager \ + backends \ + dnsmasq-manager \ + modem-manager \ + bluez-manager \ + system-settings \ + . \ + tests + +INCLUDES = -I${top_srcdir} \ + -I${top_srcdir}/include \ + -I${top_builddir}/marshallers \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/src/named-manager \ + -I${top_srcdir}/src/vpn-manager \ + -I${top_srcdir}/src/dhcp-manager \ + -I${top_srcdir}/src/ip6-manager \ + -I${top_srcdir}/src/supplicant-manager \ + -I${top_srcdir}/src/dnsmasq-manager \ + -I${top_srcdir}/src/modem-manager \ + -I$(top_srcdir)/src/bluez-manager \ + -I$(top_srcdir)/src/system-settings \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/libnm-glib \ + -I${top_srcdir}/callouts + +########################################### +# Test libraries +########################################### + +noinst_LTLIBRARIES = libtest-dhcp.la libtest-policy-hosts.la + +########################################### +# DHCP test library +########################################### + +libtest_dhcp_la_SOURCES = \ + nm-ip4-config.c \ + nm-ip6-config.c \ + nm-hostname-provider.c \ + nm-dbus-manager.c + +libtest_dhcp_la_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(LIBNL_CFLAGS) + +libtest_dhcp_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(GLIB_LIBS) \ + $(DBUS_LIBS) \ + $(LIBNL_LIBS) + +########################################### +# Hosts policy test library +########################################### + +libtest_policy_hosts_la_SOURCES = \ + nm-policy-hosts.c \ + nm-policy-hosts.h + +libtest_policy_hosts_la_CPPFLAGS = \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + $(GLIB_CFLAGS) + +libtest_policy_hosts_la_LIBADD = \ + ${top_builddir}/src/logging/libnm-logging.la \ + $(GLIB_LIBS) + + +########################################### +# NetworkManager +########################################### + +sbin_PROGRAMS = NetworkManager + +NetworkManager_SOURCES = \ + nm-call-store.c \ + nm-call-store.h \ + nm-device.c \ + nm-device.h \ + nm-device-interface.c \ + nm-device-interface.h \ + nm-device-private.h \ + nm-device-ethernet.c \ + nm-device-ethernet.h \ + nm-device-wifi.c \ + nm-device-wifi.h \ + nm-device-olpc-mesh.c \ + nm-device-olpc-mesh.h \ + nm-device-bt.c \ + nm-device-bt.h \ + nm-device-modem.h \ + nm-device-modem.c \ + nm-device-cdma.c \ + nm-device-cdma.h \ + nm-device-gsm.c \ + nm-device-gsm.h \ + nm-wifi-ap.c \ + nm-wifi-ap.h \ + nm-dbus-manager.h \ + nm-dbus-manager.c \ + nm-udev-manager.c \ + nm-udev-manager.h \ + nm-hostname-provider.c \ + nm-hostname-provider.h \ + nm-ip4-config.c \ + nm-ip4-config.h \ + nm-ip6-config.c \ + nm-ip6-config.h \ + nm-secrets-provider-interface.c \ + nm-secrets-provider-interface.h \ + nm-active-connection.h \ + nm-active-connection.c \ + main.c \ + nm-policy.c \ + nm-policy.h \ + nm-policy-hosts.c \ + nm-policy-hosts.h \ + nm-policy-hostname.c \ + nm-policy-hostname.h \ + NetworkManagerUtils.c \ + NetworkManagerUtils.h \ + nm-system.c \ + nm-system.h \ + nm-manager.c \ + nm-manager.h \ + nm-netlink-monitor.c \ + nm-netlink-monitor.h \ + nm-activation-request.c \ + nm-activation-request.h \ + nm-properties-changed-signal.c \ + nm-properties-changed-signal.h \ + wpa.c \ + wpa.h \ + nm-dhcp4-config.c \ + nm-dhcp4-config.h \ + nm-dhcp6-config.c \ + nm-dhcp6-config.h \ + nm-rfkill.h + +nm-access-point-glue.h: $(top_srcdir)/introspection/nm-access-point.xml + dbus-binding-tool --prefix=nm_access_point --mode=glib-server --output=$@ $< + +nm-manager-glue.h: $(top_srcdir)/introspection/nm-manager.xml + dbus-binding-tool --prefix=nm_manager --mode=glib-server --output=$@ $< + +nm-device-interface-glue.h: $(top_srcdir)/introspection/nm-device.xml + dbus-binding-tool --prefix=nm_device_interface --mode=glib-server --output=$@ $< + +nm-device-ethernet-glue.h: $(top_srcdir)/introspection/nm-device-ethernet.xml + dbus-binding-tool --prefix=nm_device_ethernet --mode=glib-server --output=$@ $< + +nm-device-wifi-glue.h: $(top_srcdir)/introspection/nm-device-wifi.xml + dbus-binding-tool --prefix=nm_device_wifi --mode=glib-server --output=$@ $< + +nm-device-bt-glue.h: $(top_srcdir)/introspection/nm-device-bt.xml + dbus-binding-tool --prefix=nm_device_bt --mode=glib-server --output=$@ $< + +nm-device-olpc-mesh-glue.h: $(top_srcdir)/introspection/nm-device-olpc-mesh.xml + dbus-binding-tool --prefix=nm_device_olpc_mesh --mode=glib-server --output=$@ $< + +nm-ip4-config-glue.h: $(top_srcdir)/introspection/nm-ip4-config.xml + dbus-binding-tool --prefix=nm_ip4_config --mode=glib-server --output=$@ $< + +nm-ip6-config-glue.h: $(top_srcdir)/introspection/nm-ip6-config.xml + dbus-binding-tool --prefix=nm_ip6_config --mode=glib-server --output=$@ $< + +nm-active-connection-glue.h: $(top_srcdir)/introspection/nm-active-connection.xml + dbus-binding-tool --prefix=nm_active_connection --mode=glib-server --output=$@ $< + +nm-dhcp4-config-glue.h: $(top_srcdir)/introspection/nm-dhcp4-config.xml + dbus-binding-tool --prefix=nm_dhcp4_config --mode=glib-server --output=$@ $< + +nm-dhcp6-config-glue.h: $(top_srcdir)/introspection/nm-dhcp6-config.xml + dbus-binding-tool --prefix=nm_dhcp6_config --mode=glib-server --output=$@ $< + +nm-device-cdma-glue.h: $(top_srcdir)/introspection/nm-device-cdma.xml + dbus-binding-tool --prefix=nm_device_cdma --mode=glib-server --output=$@ $< + +nm-device-gsm-glue.h: $(top_srcdir)/introspection/nm-device-gsm.xml + dbus-binding-tool --prefix=nm_device_gsm --mode=glib-server --output=$@ $< + +BUILT_SOURCES = \ + nm-access-point-glue.h \ + nm-manager-glue.h \ + nm-device-interface-glue.h \ + nm-device-ethernet-glue.h \ + nm-device-wifi-glue.h \ + nm-device-olpc-mesh-glue.h \ + nm-device-bt-glue.h \ + nm-device-cdma-glue.h \ + nm-device-gsm-glue.h \ + nm-ip4-config-glue.h \ + nm-ip6-config-glue.h \ + nm-active-connection-glue.h \ + nm-dhcp4-config-glue.h \ + nm-dhcp6-config-glue.h + +NetworkManager_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(GUDEV_CFLAGS) \ + $(LIBNL_CFLAGS) \ + $(GMODULE_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DBINDIR=\"$(bindir)\" \ + -DSBINDIR=\"$(sbindir)\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DDATADIR=\"$(datadir)\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DLOCALSTATEDIR=\"$(localstatedir)\" \ + -DNM_RUN_DIR=\"$(rundir)\" \ + -DNMLOCALEDIR=\"$(datadir)/locale\" \ + -DARP_DEBUG + +NetworkManager_LDADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + ./logging/libnm-logging.la \ + ./named-manager/libnamed-manager.la \ + ./vpn-manager/libvpn-manager.la \ + ./dhcp-manager/libdhcp-manager.la \ + ./ip6-manager/libip6-manager.la \ + ./supplicant-manager/libsupplicant-manager.la \ + ./dnsmasq-manager/libdnsmasq-manager.la \ + ./ppp-manager/libppp-manager.la \ + ./modem-manager/libmodem-manager.la \ + ./bluez-manager/libbluez-manager.la \ + ./system-settings/libsystem-settings.la \ + ./backends/libnmbackend.la \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) \ + $(GUDEV_LIBS) \ + $(LIBNL_LIBS) \ + $(GMODULE_LIBS) \ + $(LIBM) \ + $(LIBDL) + +NetworkManager_LDFLAGS = -rdynamic + +libexec_PROGRAMS = nm-crash-logger +nm_crash_logger_SOURCES = nm-crash-logger.c +nm_crash_logger_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DBINDIR=\"$(bindir)\" \ + -DSBINDIR=\"$(sbindir)\" \ + -DDATADIR=\"$(datadir)\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DLOCALSTATEDIR=\"$(localstatedir)\" +nm_crash_logger_LDADD = $(GLIB_LIBS) + + +NetworkManagerdir = $(datadir)/NetworkManager +NetworkManager_DATA = gdb-cmd + +dbusservicedir = $(DBUS_SYS_DIR) +dbusservice_DATA = NetworkManager.conf + +EXTRA_DIST = \ + $(dbusservice_DATA) \ + $(NetworkManager_DATA) + +rundir=$(localstatedir)/run/NetworkManager +install-data-hook: + $(mkinstalldirs) -m 0700 $(DESTDIR)$(rundir) + +CLEANFILES = $(BUILT_SOURCES) + diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 00000000..7943a100 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,1722 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +sbin_PROGRAMS = NetworkManager$(EXEEXT) +libexec_PROGRAMS = nm-crash-logger$(EXEEXT) +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libtest_dhcp_la_DEPENDENCIES = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/libnm-util/libnm-util.la $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libtest_dhcp_la_OBJECTS = libtest_dhcp_la-nm-ip4-config.lo \ + libtest_dhcp_la-nm-ip6-config.lo \ + libtest_dhcp_la-nm-hostname-provider.lo \ + libtest_dhcp_la-nm-dbus-manager.lo +libtest_dhcp_la_OBJECTS = $(am_libtest_dhcp_la_OBJECTS) +libtest_policy_hosts_la_DEPENDENCIES = \ + ${top_builddir}/src/logging/libnm-logging.la \ + $(am__DEPENDENCIES_1) +am_libtest_policy_hosts_la_OBJECTS = \ + libtest_policy_hosts_la-nm-policy-hosts.lo +libtest_policy_hosts_la_OBJECTS = \ + $(am_libtest_policy_hosts_la_OBJECTS) +am__installdirs = "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(sbindir)" \ + "$(DESTDIR)$(NetworkManagerdir)" "$(DESTDIR)$(dbusservicedir)" +PROGRAMS = $(libexec_PROGRAMS) $(sbin_PROGRAMS) +am_NetworkManager_OBJECTS = NetworkManager-nm-call-store.$(OBJEXT) \ + NetworkManager-nm-device.$(OBJEXT) \ + NetworkManager-nm-device-interface.$(OBJEXT) \ + NetworkManager-nm-device-ethernet.$(OBJEXT) \ + NetworkManager-nm-device-wifi.$(OBJEXT) \ + NetworkManager-nm-device-olpc-mesh.$(OBJEXT) \ + NetworkManager-nm-device-bt.$(OBJEXT) \ + NetworkManager-nm-device-modem.$(OBJEXT) \ + NetworkManager-nm-device-cdma.$(OBJEXT) \ + NetworkManager-nm-device-gsm.$(OBJEXT) \ + NetworkManager-nm-wifi-ap.$(OBJEXT) \ + NetworkManager-nm-dbus-manager.$(OBJEXT) \ + NetworkManager-nm-udev-manager.$(OBJEXT) \ + NetworkManager-nm-hostname-provider.$(OBJEXT) \ + NetworkManager-nm-ip4-config.$(OBJEXT) \ + NetworkManager-nm-ip6-config.$(OBJEXT) \ + NetworkManager-nm-secrets-provider-interface.$(OBJEXT) \ + NetworkManager-nm-active-connection.$(OBJEXT) \ + NetworkManager-main.$(OBJEXT) \ + NetworkManager-nm-policy.$(OBJEXT) \ + NetworkManager-nm-policy-hosts.$(OBJEXT) \ + NetworkManager-nm-policy-hostname.$(OBJEXT) \ + NetworkManager-NetworkManagerUtils.$(OBJEXT) \ + NetworkManager-nm-system.$(OBJEXT) \ + NetworkManager-nm-manager.$(OBJEXT) \ + NetworkManager-nm-netlink-monitor.$(OBJEXT) \ + NetworkManager-nm-activation-request.$(OBJEXT) \ + NetworkManager-nm-properties-changed-signal.$(OBJEXT) \ + NetworkManager-wpa.$(OBJEXT) \ + NetworkManager-nm-dhcp4-config.$(OBJEXT) \ + NetworkManager-nm-dhcp6-config.$(OBJEXT) +NetworkManager_OBJECTS = $(am_NetworkManager_OBJECTS) +NetworkManager_DEPENDENCIES = \ + $(top_builddir)/marshallers/libmarshallers.la \ + ./logging/libnm-logging.la ./named-manager/libnamed-manager.la \ + ./vpn-manager/libvpn-manager.la \ + ./dhcp-manager/libdhcp-manager.la \ + ./ip6-manager/libip6-manager.la \ + ./supplicant-manager/libsupplicant-manager.la \ + ./dnsmasq-manager/libdnsmasq-manager.la \ + ./ppp-manager/libppp-manager.la \ + ./modem-manager/libmodem-manager.la \ + ./bluez-manager/libbluez-manager.la \ + ./system-settings/libsystem-settings.la \ + ./backends/libnmbackend.la \ + $(top_builddir)/libnm-util/libnm-util.la $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +NetworkManager_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(NetworkManager_LDFLAGS) $(LDFLAGS) -o $@ +am_nm_crash_logger_OBJECTS = \ + nm_crash_logger-nm-crash-logger.$(OBJEXT) +nm_crash_logger_OBJECTS = $(am_nm_crash_logger_OBJECTS) +nm_crash_logger_DEPENDENCIES = $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libtest_dhcp_la_SOURCES) \ + $(libtest_policy_hosts_la_SOURCES) $(NetworkManager_SOURCES) \ + $(nm_crash_logger_SOURCES) +DIST_SOURCES = $(libtest_dhcp_la_SOURCES) \ + $(libtest_policy_hosts_la_SOURCES) $(NetworkManager_SOURCES) \ + $(nm_crash_logger_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-dvi-recursive install-exec-recursive \ + install-html-recursive install-info-recursive \ + install-pdf-recursive install-ps-recursive install-recursive \ + installcheck-recursive installdirs-recursive pdf-recursive \ + ps-recursive uninstall-recursive +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +DATA = $(NetworkManager_DATA) $(dbusservice_DATA) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ + $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ + distdir +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +IPTABLES_PATH = @IPTABLES_PATH@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBM = @LIBM@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POFILES = @POFILES@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +RANLIB = @RANLIB@ +RESOLVCONF_PATH = @RESOLVCONF_PATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = \ + logging \ + named-manager \ + vpn-manager \ + dhcp-manager \ + ip6-manager \ + supplicant-manager \ + ppp-manager \ + backends \ + dnsmasq-manager \ + modem-manager \ + bluez-manager \ + system-settings \ + . \ + tests + +INCLUDES = -I${top_srcdir} \ + -I${top_srcdir}/include \ + -I${top_builddir}/marshallers \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/src/named-manager \ + -I${top_srcdir}/src/vpn-manager \ + -I${top_srcdir}/src/dhcp-manager \ + -I${top_srcdir}/src/ip6-manager \ + -I${top_srcdir}/src/supplicant-manager \ + -I${top_srcdir}/src/dnsmasq-manager \ + -I${top_srcdir}/src/modem-manager \ + -I$(top_srcdir)/src/bluez-manager \ + -I$(top_srcdir)/src/system-settings \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/libnm-glib \ + -I${top_srcdir}/callouts + + +########################################### +# Test libraries +########################################### +noinst_LTLIBRARIES = libtest-dhcp.la libtest-policy-hosts.la + +########################################### +# DHCP test library +########################################### +libtest_dhcp_la_SOURCES = \ + nm-ip4-config.c \ + nm-ip6-config.c \ + nm-hostname-provider.c \ + nm-dbus-manager.c + +libtest_dhcp_la_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(LIBNL_CFLAGS) + +libtest_dhcp_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(GLIB_LIBS) \ + $(DBUS_LIBS) \ + $(LIBNL_LIBS) + + +########################################### +# Hosts policy test library +########################################### +libtest_policy_hosts_la_SOURCES = \ + nm-policy-hosts.c \ + nm-policy-hosts.h + +libtest_policy_hosts_la_CPPFLAGS = \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + $(GLIB_CFLAGS) + +libtest_policy_hosts_la_LIBADD = \ + ${top_builddir}/src/logging/libnm-logging.la \ + $(GLIB_LIBS) + +NetworkManager_SOURCES = \ + nm-call-store.c \ + nm-call-store.h \ + nm-device.c \ + nm-device.h \ + nm-device-interface.c \ + nm-device-interface.h \ + nm-device-private.h \ + nm-device-ethernet.c \ + nm-device-ethernet.h \ + nm-device-wifi.c \ + nm-device-wifi.h \ + nm-device-olpc-mesh.c \ + nm-device-olpc-mesh.h \ + nm-device-bt.c \ + nm-device-bt.h \ + nm-device-modem.h \ + nm-device-modem.c \ + nm-device-cdma.c \ + nm-device-cdma.h \ + nm-device-gsm.c \ + nm-device-gsm.h \ + nm-wifi-ap.c \ + nm-wifi-ap.h \ + nm-dbus-manager.h \ + nm-dbus-manager.c \ + nm-udev-manager.c \ + nm-udev-manager.h \ + nm-hostname-provider.c \ + nm-hostname-provider.h \ + nm-ip4-config.c \ + nm-ip4-config.h \ + nm-ip6-config.c \ + nm-ip6-config.h \ + nm-secrets-provider-interface.c \ + nm-secrets-provider-interface.h \ + nm-active-connection.h \ + nm-active-connection.c \ + main.c \ + nm-policy.c \ + nm-policy.h \ + nm-policy-hosts.c \ + nm-policy-hosts.h \ + nm-policy-hostname.c \ + nm-policy-hostname.h \ + NetworkManagerUtils.c \ + NetworkManagerUtils.h \ + nm-system.c \ + nm-system.h \ + nm-manager.c \ + nm-manager.h \ + nm-netlink-monitor.c \ + nm-netlink-monitor.h \ + nm-activation-request.c \ + nm-activation-request.h \ + nm-properties-changed-signal.c \ + nm-properties-changed-signal.h \ + wpa.c \ + wpa.h \ + nm-dhcp4-config.c \ + nm-dhcp4-config.h \ + nm-dhcp6-config.c \ + nm-dhcp6-config.h \ + nm-rfkill.h + +BUILT_SOURCES = \ + nm-access-point-glue.h \ + nm-manager-glue.h \ + nm-device-interface-glue.h \ + nm-device-ethernet-glue.h \ + nm-device-wifi-glue.h \ + nm-device-olpc-mesh-glue.h \ + nm-device-bt-glue.h \ + nm-device-cdma-glue.h \ + nm-device-gsm-glue.h \ + nm-ip4-config-glue.h \ + nm-ip6-config-glue.h \ + nm-active-connection-glue.h \ + nm-dhcp4-config-glue.h \ + nm-dhcp6-config-glue.h + +NetworkManager_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(GUDEV_CFLAGS) \ + $(LIBNL_CFLAGS) \ + $(GMODULE_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DBINDIR=\"$(bindir)\" \ + -DSBINDIR=\"$(sbindir)\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DDATADIR=\"$(datadir)\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DLOCALSTATEDIR=\"$(localstatedir)\" \ + -DNM_RUN_DIR=\"$(rundir)\" \ + -DNMLOCALEDIR=\"$(datadir)/locale\" \ + -DARP_DEBUG + +NetworkManager_LDADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + ./logging/libnm-logging.la \ + ./named-manager/libnamed-manager.la \ + ./vpn-manager/libvpn-manager.la \ + ./dhcp-manager/libdhcp-manager.la \ + ./ip6-manager/libip6-manager.la \ + ./supplicant-manager/libsupplicant-manager.la \ + ./dnsmasq-manager/libdnsmasq-manager.la \ + ./ppp-manager/libppp-manager.la \ + ./modem-manager/libmodem-manager.la \ + ./bluez-manager/libbluez-manager.la \ + ./system-settings/libsystem-settings.la \ + ./backends/libnmbackend.la \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) \ + $(GUDEV_LIBS) \ + $(LIBNL_LIBS) \ + $(GMODULE_LIBS) \ + $(LIBM) \ + $(LIBDL) + +NetworkManager_LDFLAGS = -rdynamic +nm_crash_logger_SOURCES = nm-crash-logger.c +nm_crash_logger_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DBINDIR=\"$(bindir)\" \ + -DSBINDIR=\"$(sbindir)\" \ + -DDATADIR=\"$(datadir)\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DLOCALSTATEDIR=\"$(localstatedir)\" + +nm_crash_logger_LDADD = $(GLIB_LIBS) +NetworkManagerdir = $(datadir)/NetworkManager +NetworkManager_DATA = gdb-cmd +dbusservicedir = $(DBUS_SYS_DIR) +dbusservice_DATA = NetworkManager.conf +EXTRA_DIST = \ + $(dbusservice_DATA) \ + $(NetworkManager_DATA) + +rundir = $(localstatedir)/run/NetworkManager +CLEANFILES = $(BUILT_SOURCES) +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libtest-dhcp.la: $(libtest_dhcp_la_OBJECTS) $(libtest_dhcp_la_DEPENDENCIES) + $(LINK) $(libtest_dhcp_la_OBJECTS) $(libtest_dhcp_la_LIBADD) $(LIBS) +libtest-policy-hosts.la: $(libtest_policy_hosts_la_OBJECTS) $(libtest_policy_hosts_la_DEPENDENCIES) + $(LINK) $(libtest_policy_hosts_la_OBJECTS) $(libtest_policy_hosts_la_LIBADD) $(LIBS) +install-libexecPROGRAMS: $(libexec_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(libexecdir)" || $(MKDIR_P) "$(DESTDIR)$(libexecdir)" + @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p || test -f $$p1; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-libexecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(libexecdir)" && rm -f $$files + +clean-libexecPROGRAMS: + @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)" + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p || test -f $$p1; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +NetworkManager$(EXEEXT): $(NetworkManager_OBJECTS) $(NetworkManager_DEPENDENCIES) + @rm -f NetworkManager$(EXEEXT) + $(NetworkManager_LINK) $(NetworkManager_OBJECTS) $(NetworkManager_LDADD) $(LIBS) +nm-crash-logger$(EXEEXT): $(nm_crash_logger_OBJECTS) $(nm_crash_logger_DEPENDENCIES) + @rm -f nm-crash-logger$(EXEEXT) + $(LINK) $(nm_crash_logger_OBJECTS) $(nm_crash_logger_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-NetworkManagerUtils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-activation-request.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-active-connection.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-call-store.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-dbus-manager.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device-bt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device-cdma.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device-ethernet.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device-gsm.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device-interface.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device-modem.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device-olpc-mesh.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device-wifi.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-dhcp4-config.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-dhcp6-config.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-hostname-provider.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-ip4-config.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-ip6-config.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-manager.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-netlink-monitor.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-policy-hostname.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-policy-hosts.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-policy.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-properties-changed-signal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-secrets-provider-interface.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-system.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-udev-manager.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-wifi-ap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-wpa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtest_dhcp_la-nm-dbus-manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtest_dhcp_la-nm-hostname-provider.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtest_dhcp_la-nm-ip4-config.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtest_dhcp_la-nm-ip6-config.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtest_policy_hosts_la-nm-policy-hosts.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm_crash_logger-nm-crash-logger.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libtest_dhcp_la-nm-ip4-config.lo: nm-ip4-config.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_dhcp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtest_dhcp_la-nm-ip4-config.lo -MD -MP -MF $(DEPDIR)/libtest_dhcp_la-nm-ip4-config.Tpo -c -o libtest_dhcp_la-nm-ip4-config.lo `test -f 'nm-ip4-config.c' || echo '$(srcdir)/'`nm-ip4-config.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libtest_dhcp_la-nm-ip4-config.Tpo $(DEPDIR)/libtest_dhcp_la-nm-ip4-config.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-ip4-config.c' object='libtest_dhcp_la-nm-ip4-config.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_dhcp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtest_dhcp_la-nm-ip4-config.lo `test -f 'nm-ip4-config.c' || echo '$(srcdir)/'`nm-ip4-config.c + +libtest_dhcp_la-nm-ip6-config.lo: nm-ip6-config.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_dhcp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtest_dhcp_la-nm-ip6-config.lo -MD -MP -MF $(DEPDIR)/libtest_dhcp_la-nm-ip6-config.Tpo -c -o libtest_dhcp_la-nm-ip6-config.lo `test -f 'nm-ip6-config.c' || echo '$(srcdir)/'`nm-ip6-config.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libtest_dhcp_la-nm-ip6-config.Tpo $(DEPDIR)/libtest_dhcp_la-nm-ip6-config.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-ip6-config.c' object='libtest_dhcp_la-nm-ip6-config.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_dhcp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtest_dhcp_la-nm-ip6-config.lo `test -f 'nm-ip6-config.c' || echo '$(srcdir)/'`nm-ip6-config.c + +libtest_dhcp_la-nm-hostname-provider.lo: nm-hostname-provider.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_dhcp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtest_dhcp_la-nm-hostname-provider.lo -MD -MP -MF $(DEPDIR)/libtest_dhcp_la-nm-hostname-provider.Tpo -c -o libtest_dhcp_la-nm-hostname-provider.lo `test -f 'nm-hostname-provider.c' || echo '$(srcdir)/'`nm-hostname-provider.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libtest_dhcp_la-nm-hostname-provider.Tpo $(DEPDIR)/libtest_dhcp_la-nm-hostname-provider.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-hostname-provider.c' object='libtest_dhcp_la-nm-hostname-provider.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_dhcp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtest_dhcp_la-nm-hostname-provider.lo `test -f 'nm-hostname-provider.c' || echo '$(srcdir)/'`nm-hostname-provider.c + +libtest_dhcp_la-nm-dbus-manager.lo: nm-dbus-manager.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_dhcp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtest_dhcp_la-nm-dbus-manager.lo -MD -MP -MF $(DEPDIR)/libtest_dhcp_la-nm-dbus-manager.Tpo -c -o libtest_dhcp_la-nm-dbus-manager.lo `test -f 'nm-dbus-manager.c' || echo '$(srcdir)/'`nm-dbus-manager.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libtest_dhcp_la-nm-dbus-manager.Tpo $(DEPDIR)/libtest_dhcp_la-nm-dbus-manager.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-dbus-manager.c' object='libtest_dhcp_la-nm-dbus-manager.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_dhcp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtest_dhcp_la-nm-dbus-manager.lo `test -f 'nm-dbus-manager.c' || echo '$(srcdir)/'`nm-dbus-manager.c + +libtest_policy_hosts_la-nm-policy-hosts.lo: nm-policy-hosts.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_policy_hosts_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtest_policy_hosts_la-nm-policy-hosts.lo -MD -MP -MF $(DEPDIR)/libtest_policy_hosts_la-nm-policy-hosts.Tpo -c -o libtest_policy_hosts_la-nm-policy-hosts.lo `test -f 'nm-policy-hosts.c' || echo '$(srcdir)/'`nm-policy-hosts.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libtest_policy_hosts_la-nm-policy-hosts.Tpo $(DEPDIR)/libtest_policy_hosts_la-nm-policy-hosts.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-policy-hosts.c' object='libtest_policy_hosts_la-nm-policy-hosts.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_policy_hosts_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtest_policy_hosts_la-nm-policy-hosts.lo `test -f 'nm-policy-hosts.c' || echo '$(srcdir)/'`nm-policy-hosts.c + +NetworkManager-nm-call-store.o: nm-call-store.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-call-store.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-call-store.Tpo -c -o NetworkManager-nm-call-store.o `test -f 'nm-call-store.c' || echo '$(srcdir)/'`nm-call-store.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-call-store.Tpo $(DEPDIR)/NetworkManager-nm-call-store.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-call-store.c' object='NetworkManager-nm-call-store.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-call-store.o `test -f 'nm-call-store.c' || echo '$(srcdir)/'`nm-call-store.c + +NetworkManager-nm-call-store.obj: nm-call-store.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-call-store.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-call-store.Tpo -c -o NetworkManager-nm-call-store.obj `if test -f 'nm-call-store.c'; then $(CYGPATH_W) 'nm-call-store.c'; else $(CYGPATH_W) '$(srcdir)/nm-call-store.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-call-store.Tpo $(DEPDIR)/NetworkManager-nm-call-store.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-call-store.c' object='NetworkManager-nm-call-store.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-call-store.obj `if test -f 'nm-call-store.c'; then $(CYGPATH_W) 'nm-call-store.c'; else $(CYGPATH_W) '$(srcdir)/nm-call-store.c'; fi` + +NetworkManager-nm-device.o: nm-device.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device.Tpo -c -o NetworkManager-nm-device.o `test -f 'nm-device.c' || echo '$(srcdir)/'`nm-device.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device.Tpo $(DEPDIR)/NetworkManager-nm-device.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device.c' object='NetworkManager-nm-device.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device.o `test -f 'nm-device.c' || echo '$(srcdir)/'`nm-device.c + +NetworkManager-nm-device.obj: nm-device.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device.Tpo -c -o NetworkManager-nm-device.obj `if test -f 'nm-device.c'; then $(CYGPATH_W) 'nm-device.c'; else $(CYGPATH_W) '$(srcdir)/nm-device.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device.Tpo $(DEPDIR)/NetworkManager-nm-device.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device.c' object='NetworkManager-nm-device.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device.obj `if test -f 'nm-device.c'; then $(CYGPATH_W) 'nm-device.c'; else $(CYGPATH_W) '$(srcdir)/nm-device.c'; fi` + +NetworkManager-nm-device-interface.o: nm-device-interface.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-interface.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-interface.Tpo -c -o NetworkManager-nm-device-interface.o `test -f 'nm-device-interface.c' || echo '$(srcdir)/'`nm-device-interface.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device-interface.Tpo $(DEPDIR)/NetworkManager-nm-device-interface.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device-interface.c' object='NetworkManager-nm-device-interface.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-interface.o `test -f 'nm-device-interface.c' || echo '$(srcdir)/'`nm-device-interface.c + +NetworkManager-nm-device-interface.obj: nm-device-interface.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-interface.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-interface.Tpo -c -o NetworkManager-nm-device-interface.obj `if test -f 'nm-device-interface.c'; then $(CYGPATH_W) 'nm-device-interface.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-interface.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device-interface.Tpo $(DEPDIR)/NetworkManager-nm-device-interface.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device-interface.c' object='NetworkManager-nm-device-interface.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-interface.obj `if test -f 'nm-device-interface.c'; then $(CYGPATH_W) 'nm-device-interface.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-interface.c'; fi` + +NetworkManager-nm-device-ethernet.o: nm-device-ethernet.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-ethernet.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-ethernet.Tpo -c -o NetworkManager-nm-device-ethernet.o `test -f 'nm-device-ethernet.c' || echo '$(srcdir)/'`nm-device-ethernet.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device-ethernet.Tpo $(DEPDIR)/NetworkManager-nm-device-ethernet.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device-ethernet.c' object='NetworkManager-nm-device-ethernet.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-ethernet.o `test -f 'nm-device-ethernet.c' || echo '$(srcdir)/'`nm-device-ethernet.c + +NetworkManager-nm-device-ethernet.obj: nm-device-ethernet.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-ethernet.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-ethernet.Tpo -c -o NetworkManager-nm-device-ethernet.obj `if test -f 'nm-device-ethernet.c'; then $(CYGPATH_W) 'nm-device-ethernet.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-ethernet.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device-ethernet.Tpo $(DEPDIR)/NetworkManager-nm-device-ethernet.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device-ethernet.c' object='NetworkManager-nm-device-ethernet.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-ethernet.obj `if test -f 'nm-device-ethernet.c'; then $(CYGPATH_W) 'nm-device-ethernet.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-ethernet.c'; fi` + +NetworkManager-nm-device-wifi.o: nm-device-wifi.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-wifi.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-wifi.Tpo -c -o NetworkManager-nm-device-wifi.o `test -f 'nm-device-wifi.c' || echo '$(srcdir)/'`nm-device-wifi.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device-wifi.Tpo $(DEPDIR)/NetworkManager-nm-device-wifi.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device-wifi.c' object='NetworkManager-nm-device-wifi.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-wifi.o `test -f 'nm-device-wifi.c' || echo '$(srcdir)/'`nm-device-wifi.c + +NetworkManager-nm-device-wifi.obj: nm-device-wifi.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-wifi.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-wifi.Tpo -c -o NetworkManager-nm-device-wifi.obj `if test -f 'nm-device-wifi.c'; then $(CYGPATH_W) 'nm-device-wifi.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-wifi.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device-wifi.Tpo $(DEPDIR)/NetworkManager-nm-device-wifi.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device-wifi.c' object='NetworkManager-nm-device-wifi.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-wifi.obj `if test -f 'nm-device-wifi.c'; then $(CYGPATH_W) 'nm-device-wifi.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-wifi.c'; fi` + +NetworkManager-nm-device-olpc-mesh.o: nm-device-olpc-mesh.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-olpc-mesh.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-olpc-mesh.Tpo -c -o NetworkManager-nm-device-olpc-mesh.o `test -f 'nm-device-olpc-mesh.c' || echo '$(srcdir)/'`nm-device-olpc-mesh.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device-olpc-mesh.Tpo $(DEPDIR)/NetworkManager-nm-device-olpc-mesh.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device-olpc-mesh.c' object='NetworkManager-nm-device-olpc-mesh.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-olpc-mesh.o `test -f 'nm-device-olpc-mesh.c' || echo '$(srcdir)/'`nm-device-olpc-mesh.c + +NetworkManager-nm-device-olpc-mesh.obj: nm-device-olpc-mesh.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-olpc-mesh.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-olpc-mesh.Tpo -c -o NetworkManager-nm-device-olpc-mesh.obj `if test -f 'nm-device-olpc-mesh.c'; then $(CYGPATH_W) 'nm-device-olpc-mesh.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-olpc-mesh.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device-olpc-mesh.Tpo $(DEPDIR)/NetworkManager-nm-device-olpc-mesh.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device-olpc-mesh.c' object='NetworkManager-nm-device-olpc-mesh.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-olpc-mesh.obj `if test -f 'nm-device-olpc-mesh.c'; then $(CYGPATH_W) 'nm-device-olpc-mesh.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-olpc-mesh.c'; fi` + +NetworkManager-nm-device-bt.o: nm-device-bt.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-bt.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-bt.Tpo -c -o NetworkManager-nm-device-bt.o `test -f 'nm-device-bt.c' || echo '$(srcdir)/'`nm-device-bt.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device-bt.Tpo $(DEPDIR)/NetworkManager-nm-device-bt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device-bt.c' object='NetworkManager-nm-device-bt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-bt.o `test -f 'nm-device-bt.c' || echo '$(srcdir)/'`nm-device-bt.c + +NetworkManager-nm-device-bt.obj: nm-device-bt.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-bt.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-bt.Tpo -c -o NetworkManager-nm-device-bt.obj `if test -f 'nm-device-bt.c'; then $(CYGPATH_W) 'nm-device-bt.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-bt.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device-bt.Tpo $(DEPDIR)/NetworkManager-nm-device-bt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device-bt.c' object='NetworkManager-nm-device-bt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-bt.obj `if test -f 'nm-device-bt.c'; then $(CYGPATH_W) 'nm-device-bt.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-bt.c'; fi` + +NetworkManager-nm-device-modem.o: nm-device-modem.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-modem.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-modem.Tpo -c -o NetworkManager-nm-device-modem.o `test -f 'nm-device-modem.c' || echo '$(srcdir)/'`nm-device-modem.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device-modem.Tpo $(DEPDIR)/NetworkManager-nm-device-modem.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device-modem.c' object='NetworkManager-nm-device-modem.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-modem.o `test -f 'nm-device-modem.c' || echo '$(srcdir)/'`nm-device-modem.c + +NetworkManager-nm-device-modem.obj: nm-device-modem.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-modem.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-modem.Tpo -c -o NetworkManager-nm-device-modem.obj `if test -f 'nm-device-modem.c'; then $(CYGPATH_W) 'nm-device-modem.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-modem.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device-modem.Tpo $(DEPDIR)/NetworkManager-nm-device-modem.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device-modem.c' object='NetworkManager-nm-device-modem.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-modem.obj `if test -f 'nm-device-modem.c'; then $(CYGPATH_W) 'nm-device-modem.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-modem.c'; fi` + +NetworkManager-nm-device-cdma.o: nm-device-cdma.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-cdma.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-cdma.Tpo -c -o NetworkManager-nm-device-cdma.o `test -f 'nm-device-cdma.c' || echo '$(srcdir)/'`nm-device-cdma.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device-cdma.Tpo $(DEPDIR)/NetworkManager-nm-device-cdma.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device-cdma.c' object='NetworkManager-nm-device-cdma.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-cdma.o `test -f 'nm-device-cdma.c' || echo '$(srcdir)/'`nm-device-cdma.c + +NetworkManager-nm-device-cdma.obj: nm-device-cdma.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-cdma.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-cdma.Tpo -c -o NetworkManager-nm-device-cdma.obj `if test -f 'nm-device-cdma.c'; then $(CYGPATH_W) 'nm-device-cdma.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-cdma.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device-cdma.Tpo $(DEPDIR)/NetworkManager-nm-device-cdma.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device-cdma.c' object='NetworkManager-nm-device-cdma.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-cdma.obj `if test -f 'nm-device-cdma.c'; then $(CYGPATH_W) 'nm-device-cdma.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-cdma.c'; fi` + +NetworkManager-nm-device-gsm.o: nm-device-gsm.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-gsm.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-gsm.Tpo -c -o NetworkManager-nm-device-gsm.o `test -f 'nm-device-gsm.c' || echo '$(srcdir)/'`nm-device-gsm.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device-gsm.Tpo $(DEPDIR)/NetworkManager-nm-device-gsm.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device-gsm.c' object='NetworkManager-nm-device-gsm.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-gsm.o `test -f 'nm-device-gsm.c' || echo '$(srcdir)/'`nm-device-gsm.c + +NetworkManager-nm-device-gsm.obj: nm-device-gsm.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-gsm.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-gsm.Tpo -c -o NetworkManager-nm-device-gsm.obj `if test -f 'nm-device-gsm.c'; then $(CYGPATH_W) 'nm-device-gsm.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-gsm.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-device-gsm.Tpo $(DEPDIR)/NetworkManager-nm-device-gsm.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-device-gsm.c' object='NetworkManager-nm-device-gsm.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-gsm.obj `if test -f 'nm-device-gsm.c'; then $(CYGPATH_W) 'nm-device-gsm.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-gsm.c'; fi` + +NetworkManager-nm-wifi-ap.o: nm-wifi-ap.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-wifi-ap.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-wifi-ap.Tpo -c -o NetworkManager-nm-wifi-ap.o `test -f 'nm-wifi-ap.c' || echo '$(srcdir)/'`nm-wifi-ap.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-wifi-ap.Tpo $(DEPDIR)/NetworkManager-nm-wifi-ap.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-wifi-ap.c' object='NetworkManager-nm-wifi-ap.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-wifi-ap.o `test -f 'nm-wifi-ap.c' || echo '$(srcdir)/'`nm-wifi-ap.c + +NetworkManager-nm-wifi-ap.obj: nm-wifi-ap.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-wifi-ap.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-wifi-ap.Tpo -c -o NetworkManager-nm-wifi-ap.obj `if test -f 'nm-wifi-ap.c'; then $(CYGPATH_W) 'nm-wifi-ap.c'; else $(CYGPATH_W) '$(srcdir)/nm-wifi-ap.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-wifi-ap.Tpo $(DEPDIR)/NetworkManager-nm-wifi-ap.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-wifi-ap.c' object='NetworkManager-nm-wifi-ap.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-wifi-ap.obj `if test -f 'nm-wifi-ap.c'; then $(CYGPATH_W) 'nm-wifi-ap.c'; else $(CYGPATH_W) '$(srcdir)/nm-wifi-ap.c'; fi` + +NetworkManager-nm-dbus-manager.o: nm-dbus-manager.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-dbus-manager.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-dbus-manager.Tpo -c -o NetworkManager-nm-dbus-manager.o `test -f 'nm-dbus-manager.c' || echo '$(srcdir)/'`nm-dbus-manager.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-dbus-manager.Tpo $(DEPDIR)/NetworkManager-nm-dbus-manager.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-dbus-manager.c' object='NetworkManager-nm-dbus-manager.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-dbus-manager.o `test -f 'nm-dbus-manager.c' || echo '$(srcdir)/'`nm-dbus-manager.c + +NetworkManager-nm-dbus-manager.obj: nm-dbus-manager.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-dbus-manager.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-dbus-manager.Tpo -c -o NetworkManager-nm-dbus-manager.obj `if test -f 'nm-dbus-manager.c'; then $(CYGPATH_W) 'nm-dbus-manager.c'; else $(CYGPATH_W) '$(srcdir)/nm-dbus-manager.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-dbus-manager.Tpo $(DEPDIR)/NetworkManager-nm-dbus-manager.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-dbus-manager.c' object='NetworkManager-nm-dbus-manager.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-dbus-manager.obj `if test -f 'nm-dbus-manager.c'; then $(CYGPATH_W) 'nm-dbus-manager.c'; else $(CYGPATH_W) '$(srcdir)/nm-dbus-manager.c'; fi` + +NetworkManager-nm-udev-manager.o: nm-udev-manager.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-udev-manager.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-udev-manager.Tpo -c -o NetworkManager-nm-udev-manager.o `test -f 'nm-udev-manager.c' || echo '$(srcdir)/'`nm-udev-manager.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-udev-manager.Tpo $(DEPDIR)/NetworkManager-nm-udev-manager.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-udev-manager.c' object='NetworkManager-nm-udev-manager.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-udev-manager.o `test -f 'nm-udev-manager.c' || echo '$(srcdir)/'`nm-udev-manager.c + +NetworkManager-nm-udev-manager.obj: nm-udev-manager.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-udev-manager.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-udev-manager.Tpo -c -o NetworkManager-nm-udev-manager.obj `if test -f 'nm-udev-manager.c'; then $(CYGPATH_W) 'nm-udev-manager.c'; else $(CYGPATH_W) '$(srcdir)/nm-udev-manager.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-udev-manager.Tpo $(DEPDIR)/NetworkManager-nm-udev-manager.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-udev-manager.c' object='NetworkManager-nm-udev-manager.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-udev-manager.obj `if test -f 'nm-udev-manager.c'; then $(CYGPATH_W) 'nm-udev-manager.c'; else $(CYGPATH_W) '$(srcdir)/nm-udev-manager.c'; fi` + +NetworkManager-nm-hostname-provider.o: nm-hostname-provider.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-hostname-provider.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-hostname-provider.Tpo -c -o NetworkManager-nm-hostname-provider.o `test -f 'nm-hostname-provider.c' || echo '$(srcdir)/'`nm-hostname-provider.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-hostname-provider.Tpo $(DEPDIR)/NetworkManager-nm-hostname-provider.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-hostname-provider.c' object='NetworkManager-nm-hostname-provider.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-hostname-provider.o `test -f 'nm-hostname-provider.c' || echo '$(srcdir)/'`nm-hostname-provider.c + +NetworkManager-nm-hostname-provider.obj: nm-hostname-provider.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-hostname-provider.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-hostname-provider.Tpo -c -o NetworkManager-nm-hostname-provider.obj `if test -f 'nm-hostname-provider.c'; then $(CYGPATH_W) 'nm-hostname-provider.c'; else $(CYGPATH_W) '$(srcdir)/nm-hostname-provider.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-hostname-provider.Tpo $(DEPDIR)/NetworkManager-nm-hostname-provider.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-hostname-provider.c' object='NetworkManager-nm-hostname-provider.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-hostname-provider.obj `if test -f 'nm-hostname-provider.c'; then $(CYGPATH_W) 'nm-hostname-provider.c'; else $(CYGPATH_W) '$(srcdir)/nm-hostname-provider.c'; fi` + +NetworkManager-nm-ip4-config.o: nm-ip4-config.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-ip4-config.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-ip4-config.Tpo -c -o NetworkManager-nm-ip4-config.o `test -f 'nm-ip4-config.c' || echo '$(srcdir)/'`nm-ip4-config.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-ip4-config.Tpo $(DEPDIR)/NetworkManager-nm-ip4-config.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-ip4-config.c' object='NetworkManager-nm-ip4-config.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-ip4-config.o `test -f 'nm-ip4-config.c' || echo '$(srcdir)/'`nm-ip4-config.c + +NetworkManager-nm-ip4-config.obj: nm-ip4-config.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-ip4-config.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-ip4-config.Tpo -c -o NetworkManager-nm-ip4-config.obj `if test -f 'nm-ip4-config.c'; then $(CYGPATH_W) 'nm-ip4-config.c'; else $(CYGPATH_W) '$(srcdir)/nm-ip4-config.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-ip4-config.Tpo $(DEPDIR)/NetworkManager-nm-ip4-config.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-ip4-config.c' object='NetworkManager-nm-ip4-config.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-ip4-config.obj `if test -f 'nm-ip4-config.c'; then $(CYGPATH_W) 'nm-ip4-config.c'; else $(CYGPATH_W) '$(srcdir)/nm-ip4-config.c'; fi` + +NetworkManager-nm-ip6-config.o: nm-ip6-config.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-ip6-config.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-ip6-config.Tpo -c -o NetworkManager-nm-ip6-config.o `test -f 'nm-ip6-config.c' || echo '$(srcdir)/'`nm-ip6-config.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-ip6-config.Tpo $(DEPDIR)/NetworkManager-nm-ip6-config.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-ip6-config.c' object='NetworkManager-nm-ip6-config.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-ip6-config.o `test -f 'nm-ip6-config.c' || echo '$(srcdir)/'`nm-ip6-config.c + +NetworkManager-nm-ip6-config.obj: nm-ip6-config.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-ip6-config.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-ip6-config.Tpo -c -o NetworkManager-nm-ip6-config.obj `if test -f 'nm-ip6-config.c'; then $(CYGPATH_W) 'nm-ip6-config.c'; else $(CYGPATH_W) '$(srcdir)/nm-ip6-config.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-ip6-config.Tpo $(DEPDIR)/NetworkManager-nm-ip6-config.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-ip6-config.c' object='NetworkManager-nm-ip6-config.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-ip6-config.obj `if test -f 'nm-ip6-config.c'; then $(CYGPATH_W) 'nm-ip6-config.c'; else $(CYGPATH_W) '$(srcdir)/nm-ip6-config.c'; fi` + +NetworkManager-nm-secrets-provider-interface.o: nm-secrets-provider-interface.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-secrets-provider-interface.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-secrets-provider-interface.Tpo -c -o NetworkManager-nm-secrets-provider-interface.o `test -f 'nm-secrets-provider-interface.c' || echo '$(srcdir)/'`nm-secrets-provider-interface.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-secrets-provider-interface.Tpo $(DEPDIR)/NetworkManager-nm-secrets-provider-interface.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-secrets-provider-interface.c' object='NetworkManager-nm-secrets-provider-interface.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-secrets-provider-interface.o `test -f 'nm-secrets-provider-interface.c' || echo '$(srcdir)/'`nm-secrets-provider-interface.c + +NetworkManager-nm-secrets-provider-interface.obj: nm-secrets-provider-interface.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-secrets-provider-interface.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-secrets-provider-interface.Tpo -c -o NetworkManager-nm-secrets-provider-interface.obj `if test -f 'nm-secrets-provider-interface.c'; then $(CYGPATH_W) 'nm-secrets-provider-interface.c'; else $(CYGPATH_W) '$(srcdir)/nm-secrets-provider-interface.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-secrets-provider-interface.Tpo $(DEPDIR)/NetworkManager-nm-secrets-provider-interface.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-secrets-provider-interface.c' object='NetworkManager-nm-secrets-provider-interface.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-secrets-provider-interface.obj `if test -f 'nm-secrets-provider-interface.c'; then $(CYGPATH_W) 'nm-secrets-provider-interface.c'; else $(CYGPATH_W) '$(srcdir)/nm-secrets-provider-interface.c'; fi` + +NetworkManager-nm-active-connection.o: nm-active-connection.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-active-connection.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-active-connection.Tpo -c -o NetworkManager-nm-active-connection.o `test -f 'nm-active-connection.c' || echo '$(srcdir)/'`nm-active-connection.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-active-connection.Tpo $(DEPDIR)/NetworkManager-nm-active-connection.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-active-connection.c' object='NetworkManager-nm-active-connection.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-active-connection.o `test -f 'nm-active-connection.c' || echo '$(srcdir)/'`nm-active-connection.c + +NetworkManager-nm-active-connection.obj: nm-active-connection.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-active-connection.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-active-connection.Tpo -c -o NetworkManager-nm-active-connection.obj `if test -f 'nm-active-connection.c'; then $(CYGPATH_W) 'nm-active-connection.c'; else $(CYGPATH_W) '$(srcdir)/nm-active-connection.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-active-connection.Tpo $(DEPDIR)/NetworkManager-nm-active-connection.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-active-connection.c' object='NetworkManager-nm-active-connection.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-active-connection.obj `if test -f 'nm-active-connection.c'; then $(CYGPATH_W) 'nm-active-connection.c'; else $(CYGPATH_W) '$(srcdir)/nm-active-connection.c'; fi` + +NetworkManager-main.o: main.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-main.o -MD -MP -MF $(DEPDIR)/NetworkManager-main.Tpo -c -o NetworkManager-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-main.Tpo $(DEPDIR)/NetworkManager-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='main.c' object='NetworkManager-main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c + +NetworkManager-main.obj: main.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-main.obj -MD -MP -MF $(DEPDIR)/NetworkManager-main.Tpo -c -o NetworkManager-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-main.Tpo $(DEPDIR)/NetworkManager-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='main.c' object='NetworkManager-main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi` + +NetworkManager-nm-policy.o: nm-policy.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-policy.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-policy.Tpo -c -o NetworkManager-nm-policy.o `test -f 'nm-policy.c' || echo '$(srcdir)/'`nm-policy.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-policy.Tpo $(DEPDIR)/NetworkManager-nm-policy.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-policy.c' object='NetworkManager-nm-policy.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-policy.o `test -f 'nm-policy.c' || echo '$(srcdir)/'`nm-policy.c + +NetworkManager-nm-policy.obj: nm-policy.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-policy.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-policy.Tpo -c -o NetworkManager-nm-policy.obj `if test -f 'nm-policy.c'; then $(CYGPATH_W) 'nm-policy.c'; else $(CYGPATH_W) '$(srcdir)/nm-policy.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-policy.Tpo $(DEPDIR)/NetworkManager-nm-policy.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-policy.c' object='NetworkManager-nm-policy.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-policy.obj `if test -f 'nm-policy.c'; then $(CYGPATH_W) 'nm-policy.c'; else $(CYGPATH_W) '$(srcdir)/nm-policy.c'; fi` + +NetworkManager-nm-policy-hosts.o: nm-policy-hosts.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-policy-hosts.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-policy-hosts.Tpo -c -o NetworkManager-nm-policy-hosts.o `test -f 'nm-policy-hosts.c' || echo '$(srcdir)/'`nm-policy-hosts.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-policy-hosts.Tpo $(DEPDIR)/NetworkManager-nm-policy-hosts.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-policy-hosts.c' object='NetworkManager-nm-policy-hosts.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-policy-hosts.o `test -f 'nm-policy-hosts.c' || echo '$(srcdir)/'`nm-policy-hosts.c + +NetworkManager-nm-policy-hosts.obj: nm-policy-hosts.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-policy-hosts.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-policy-hosts.Tpo -c -o NetworkManager-nm-policy-hosts.obj `if test -f 'nm-policy-hosts.c'; then $(CYGPATH_W) 'nm-policy-hosts.c'; else $(CYGPATH_W) '$(srcdir)/nm-policy-hosts.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-policy-hosts.Tpo $(DEPDIR)/NetworkManager-nm-policy-hosts.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-policy-hosts.c' object='NetworkManager-nm-policy-hosts.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-policy-hosts.obj `if test -f 'nm-policy-hosts.c'; then $(CYGPATH_W) 'nm-policy-hosts.c'; else $(CYGPATH_W) '$(srcdir)/nm-policy-hosts.c'; fi` + +NetworkManager-nm-policy-hostname.o: nm-policy-hostname.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-policy-hostname.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-policy-hostname.Tpo -c -o NetworkManager-nm-policy-hostname.o `test -f 'nm-policy-hostname.c' || echo '$(srcdir)/'`nm-policy-hostname.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-policy-hostname.Tpo $(DEPDIR)/NetworkManager-nm-policy-hostname.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-policy-hostname.c' object='NetworkManager-nm-policy-hostname.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-policy-hostname.o `test -f 'nm-policy-hostname.c' || echo '$(srcdir)/'`nm-policy-hostname.c + +NetworkManager-nm-policy-hostname.obj: nm-policy-hostname.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-policy-hostname.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-policy-hostname.Tpo -c -o NetworkManager-nm-policy-hostname.obj `if test -f 'nm-policy-hostname.c'; then $(CYGPATH_W) 'nm-policy-hostname.c'; else $(CYGPATH_W) '$(srcdir)/nm-policy-hostname.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-policy-hostname.Tpo $(DEPDIR)/NetworkManager-nm-policy-hostname.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-policy-hostname.c' object='NetworkManager-nm-policy-hostname.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-policy-hostname.obj `if test -f 'nm-policy-hostname.c'; then $(CYGPATH_W) 'nm-policy-hostname.c'; else $(CYGPATH_W) '$(srcdir)/nm-policy-hostname.c'; fi` + +NetworkManager-NetworkManagerUtils.o: NetworkManagerUtils.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-NetworkManagerUtils.o -MD -MP -MF $(DEPDIR)/NetworkManager-NetworkManagerUtils.Tpo -c -o NetworkManager-NetworkManagerUtils.o `test -f 'NetworkManagerUtils.c' || echo '$(srcdir)/'`NetworkManagerUtils.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-NetworkManagerUtils.Tpo $(DEPDIR)/NetworkManager-NetworkManagerUtils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='NetworkManagerUtils.c' object='NetworkManager-NetworkManagerUtils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-NetworkManagerUtils.o `test -f 'NetworkManagerUtils.c' || echo '$(srcdir)/'`NetworkManagerUtils.c + +NetworkManager-NetworkManagerUtils.obj: NetworkManagerUtils.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-NetworkManagerUtils.obj -MD -MP -MF $(DEPDIR)/NetworkManager-NetworkManagerUtils.Tpo -c -o NetworkManager-NetworkManagerUtils.obj `if test -f 'NetworkManagerUtils.c'; then $(CYGPATH_W) 'NetworkManagerUtils.c'; else $(CYGPATH_W) '$(srcdir)/NetworkManagerUtils.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-NetworkManagerUtils.Tpo $(DEPDIR)/NetworkManager-NetworkManagerUtils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='NetworkManagerUtils.c' object='NetworkManager-NetworkManagerUtils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-NetworkManagerUtils.obj `if test -f 'NetworkManagerUtils.c'; then $(CYGPATH_W) 'NetworkManagerUtils.c'; else $(CYGPATH_W) '$(srcdir)/NetworkManagerUtils.c'; fi` + +NetworkManager-nm-system.o: nm-system.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-system.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-system.Tpo -c -o NetworkManager-nm-system.o `test -f 'nm-system.c' || echo '$(srcdir)/'`nm-system.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-system.Tpo $(DEPDIR)/NetworkManager-nm-system.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-system.c' object='NetworkManager-nm-system.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-system.o `test -f 'nm-system.c' || echo '$(srcdir)/'`nm-system.c + +NetworkManager-nm-system.obj: nm-system.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-system.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-system.Tpo -c -o NetworkManager-nm-system.obj `if test -f 'nm-system.c'; then $(CYGPATH_W) 'nm-system.c'; else $(CYGPATH_W) '$(srcdir)/nm-system.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-system.Tpo $(DEPDIR)/NetworkManager-nm-system.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-system.c' object='NetworkManager-nm-system.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-system.obj `if test -f 'nm-system.c'; then $(CYGPATH_W) 'nm-system.c'; else $(CYGPATH_W) '$(srcdir)/nm-system.c'; fi` + +NetworkManager-nm-manager.o: nm-manager.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-manager.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-manager.Tpo -c -o NetworkManager-nm-manager.o `test -f 'nm-manager.c' || echo '$(srcdir)/'`nm-manager.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-manager.Tpo $(DEPDIR)/NetworkManager-nm-manager.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-manager.c' object='NetworkManager-nm-manager.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-manager.o `test -f 'nm-manager.c' || echo '$(srcdir)/'`nm-manager.c + +NetworkManager-nm-manager.obj: nm-manager.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-manager.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-manager.Tpo -c -o NetworkManager-nm-manager.obj `if test -f 'nm-manager.c'; then $(CYGPATH_W) 'nm-manager.c'; else $(CYGPATH_W) '$(srcdir)/nm-manager.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-manager.Tpo $(DEPDIR)/NetworkManager-nm-manager.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-manager.c' object='NetworkManager-nm-manager.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-manager.obj `if test -f 'nm-manager.c'; then $(CYGPATH_W) 'nm-manager.c'; else $(CYGPATH_W) '$(srcdir)/nm-manager.c'; fi` + +NetworkManager-nm-netlink-monitor.o: nm-netlink-monitor.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-netlink-monitor.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-netlink-monitor.Tpo -c -o NetworkManager-nm-netlink-monitor.o `test -f 'nm-netlink-monitor.c' || echo '$(srcdir)/'`nm-netlink-monitor.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-netlink-monitor.Tpo $(DEPDIR)/NetworkManager-nm-netlink-monitor.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-netlink-monitor.c' object='NetworkManager-nm-netlink-monitor.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-netlink-monitor.o `test -f 'nm-netlink-monitor.c' || echo '$(srcdir)/'`nm-netlink-monitor.c + +NetworkManager-nm-netlink-monitor.obj: nm-netlink-monitor.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-netlink-monitor.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-netlink-monitor.Tpo -c -o NetworkManager-nm-netlink-monitor.obj `if test -f 'nm-netlink-monitor.c'; then $(CYGPATH_W) 'nm-netlink-monitor.c'; else $(CYGPATH_W) '$(srcdir)/nm-netlink-monitor.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-netlink-monitor.Tpo $(DEPDIR)/NetworkManager-nm-netlink-monitor.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-netlink-monitor.c' object='NetworkManager-nm-netlink-monitor.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-netlink-monitor.obj `if test -f 'nm-netlink-monitor.c'; then $(CYGPATH_W) 'nm-netlink-monitor.c'; else $(CYGPATH_W) '$(srcdir)/nm-netlink-monitor.c'; fi` + +NetworkManager-nm-activation-request.o: nm-activation-request.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-activation-request.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-activation-request.Tpo -c -o NetworkManager-nm-activation-request.o `test -f 'nm-activation-request.c' || echo '$(srcdir)/'`nm-activation-request.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-activation-request.Tpo $(DEPDIR)/NetworkManager-nm-activation-request.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-activation-request.c' object='NetworkManager-nm-activation-request.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-activation-request.o `test -f 'nm-activation-request.c' || echo '$(srcdir)/'`nm-activation-request.c + +NetworkManager-nm-activation-request.obj: nm-activation-request.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-activation-request.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-activation-request.Tpo -c -o NetworkManager-nm-activation-request.obj `if test -f 'nm-activation-request.c'; then $(CYGPATH_W) 'nm-activation-request.c'; else $(CYGPATH_W) '$(srcdir)/nm-activation-request.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-activation-request.Tpo $(DEPDIR)/NetworkManager-nm-activation-request.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-activation-request.c' object='NetworkManager-nm-activation-request.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-activation-request.obj `if test -f 'nm-activation-request.c'; then $(CYGPATH_W) 'nm-activation-request.c'; else $(CYGPATH_W) '$(srcdir)/nm-activation-request.c'; fi` + +NetworkManager-nm-properties-changed-signal.o: nm-properties-changed-signal.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-properties-changed-signal.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-properties-changed-signal.Tpo -c -o NetworkManager-nm-properties-changed-signal.o `test -f 'nm-properties-changed-signal.c' || echo '$(srcdir)/'`nm-properties-changed-signal.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-properties-changed-signal.Tpo $(DEPDIR)/NetworkManager-nm-properties-changed-signal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-properties-changed-signal.c' object='NetworkManager-nm-properties-changed-signal.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-properties-changed-signal.o `test -f 'nm-properties-changed-signal.c' || echo '$(srcdir)/'`nm-properties-changed-signal.c + +NetworkManager-nm-properties-changed-signal.obj: nm-properties-changed-signal.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-properties-changed-signal.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-properties-changed-signal.Tpo -c -o NetworkManager-nm-properties-changed-signal.obj `if test -f 'nm-properties-changed-signal.c'; then $(CYGPATH_W) 'nm-properties-changed-signal.c'; else $(CYGPATH_W) '$(srcdir)/nm-properties-changed-signal.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-properties-changed-signal.Tpo $(DEPDIR)/NetworkManager-nm-properties-changed-signal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-properties-changed-signal.c' object='NetworkManager-nm-properties-changed-signal.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-properties-changed-signal.obj `if test -f 'nm-properties-changed-signal.c'; then $(CYGPATH_W) 'nm-properties-changed-signal.c'; else $(CYGPATH_W) '$(srcdir)/nm-properties-changed-signal.c'; fi` + +NetworkManager-wpa.o: wpa.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-wpa.o -MD -MP -MF $(DEPDIR)/NetworkManager-wpa.Tpo -c -o NetworkManager-wpa.o `test -f 'wpa.c' || echo '$(srcdir)/'`wpa.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-wpa.Tpo $(DEPDIR)/NetworkManager-wpa.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='wpa.c' object='NetworkManager-wpa.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-wpa.o `test -f 'wpa.c' || echo '$(srcdir)/'`wpa.c + +NetworkManager-wpa.obj: wpa.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-wpa.obj -MD -MP -MF $(DEPDIR)/NetworkManager-wpa.Tpo -c -o NetworkManager-wpa.obj `if test -f 'wpa.c'; then $(CYGPATH_W) 'wpa.c'; else $(CYGPATH_W) '$(srcdir)/wpa.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-wpa.Tpo $(DEPDIR)/NetworkManager-wpa.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='wpa.c' object='NetworkManager-wpa.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-wpa.obj `if test -f 'wpa.c'; then $(CYGPATH_W) 'wpa.c'; else $(CYGPATH_W) '$(srcdir)/wpa.c'; fi` + +NetworkManager-nm-dhcp4-config.o: nm-dhcp4-config.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-dhcp4-config.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-dhcp4-config.Tpo -c -o NetworkManager-nm-dhcp4-config.o `test -f 'nm-dhcp4-config.c' || echo '$(srcdir)/'`nm-dhcp4-config.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-dhcp4-config.Tpo $(DEPDIR)/NetworkManager-nm-dhcp4-config.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-dhcp4-config.c' object='NetworkManager-nm-dhcp4-config.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-dhcp4-config.o `test -f 'nm-dhcp4-config.c' || echo '$(srcdir)/'`nm-dhcp4-config.c + +NetworkManager-nm-dhcp4-config.obj: nm-dhcp4-config.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-dhcp4-config.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-dhcp4-config.Tpo -c -o NetworkManager-nm-dhcp4-config.obj `if test -f 'nm-dhcp4-config.c'; then $(CYGPATH_W) 'nm-dhcp4-config.c'; else $(CYGPATH_W) '$(srcdir)/nm-dhcp4-config.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-dhcp4-config.Tpo $(DEPDIR)/NetworkManager-nm-dhcp4-config.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-dhcp4-config.c' object='NetworkManager-nm-dhcp4-config.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-dhcp4-config.obj `if test -f 'nm-dhcp4-config.c'; then $(CYGPATH_W) 'nm-dhcp4-config.c'; else $(CYGPATH_W) '$(srcdir)/nm-dhcp4-config.c'; fi` + +NetworkManager-nm-dhcp6-config.o: nm-dhcp6-config.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-dhcp6-config.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-dhcp6-config.Tpo -c -o NetworkManager-nm-dhcp6-config.o `test -f 'nm-dhcp6-config.c' || echo '$(srcdir)/'`nm-dhcp6-config.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-dhcp6-config.Tpo $(DEPDIR)/NetworkManager-nm-dhcp6-config.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-dhcp6-config.c' object='NetworkManager-nm-dhcp6-config.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-dhcp6-config.o `test -f 'nm-dhcp6-config.c' || echo '$(srcdir)/'`nm-dhcp6-config.c + +NetworkManager-nm-dhcp6-config.obj: nm-dhcp6-config.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-dhcp6-config.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-dhcp6-config.Tpo -c -o NetworkManager-nm-dhcp6-config.obj `if test -f 'nm-dhcp6-config.c'; then $(CYGPATH_W) 'nm-dhcp6-config.c'; else $(CYGPATH_W) '$(srcdir)/nm-dhcp6-config.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/NetworkManager-nm-dhcp6-config.Tpo $(DEPDIR)/NetworkManager-nm-dhcp6-config.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-dhcp6-config.c' object='NetworkManager-nm-dhcp6-config.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-dhcp6-config.obj `if test -f 'nm-dhcp6-config.c'; then $(CYGPATH_W) 'nm-dhcp6-config.c'; else $(CYGPATH_W) '$(srcdir)/nm-dhcp6-config.c'; fi` + +nm_crash_logger-nm-crash-logger.o: nm-crash-logger.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nm_crash_logger_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm_crash_logger-nm-crash-logger.o -MD -MP -MF $(DEPDIR)/nm_crash_logger-nm-crash-logger.Tpo -c -o nm_crash_logger-nm-crash-logger.o `test -f 'nm-crash-logger.c' || echo '$(srcdir)/'`nm-crash-logger.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/nm_crash_logger-nm-crash-logger.Tpo $(DEPDIR)/nm_crash_logger-nm-crash-logger.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-crash-logger.c' object='nm_crash_logger-nm-crash-logger.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nm_crash_logger_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm_crash_logger-nm-crash-logger.o `test -f 'nm-crash-logger.c' || echo '$(srcdir)/'`nm-crash-logger.c + +nm_crash_logger-nm-crash-logger.obj: nm-crash-logger.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nm_crash_logger_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm_crash_logger-nm-crash-logger.obj -MD -MP -MF $(DEPDIR)/nm_crash_logger-nm-crash-logger.Tpo -c -o nm_crash_logger-nm-crash-logger.obj `if test -f 'nm-crash-logger.c'; then $(CYGPATH_W) 'nm-crash-logger.c'; else $(CYGPATH_W) '$(srcdir)/nm-crash-logger.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/nm_crash_logger-nm-crash-logger.Tpo $(DEPDIR)/nm_crash_logger-nm-crash-logger.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-crash-logger.c' object='nm_crash_logger-nm-crash-logger.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nm_crash_logger_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm_crash_logger-nm-crash-logger.obj `if test -f 'nm-crash-logger.c'; then $(CYGPATH_W) 'nm-crash-logger.c'; else $(CYGPATH_W) '$(srcdir)/nm-crash-logger.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-NetworkManagerDATA: $(NetworkManager_DATA) + @$(NORMAL_INSTALL) + test -z "$(NetworkManagerdir)" || $(MKDIR_P) "$(DESTDIR)$(NetworkManagerdir)" + @list='$(NetworkManager_DATA)'; test -n "$(NetworkManagerdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(NetworkManagerdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(NetworkManagerdir)" || exit $$?; \ + done + +uninstall-NetworkManagerDATA: + @$(NORMAL_UNINSTALL) + @list='$(NetworkManager_DATA)'; test -n "$(NetworkManagerdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(NetworkManagerdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(NetworkManagerdir)" && rm -f $$files +install-dbusserviceDATA: $(dbusservice_DATA) + @$(NORMAL_INSTALL) + test -z "$(dbusservicedir)" || $(MKDIR_P) "$(DESTDIR)$(dbusservicedir)" + @list='$(dbusservice_DATA)'; test -n "$(dbusservicedir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dbusservicedir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(dbusservicedir)" || exit $$?; \ + done + +uninstall-dbusserviceDATA: + @$(NORMAL_UNINSTALL) + @list='$(dbusservice_DATA)'; test -n "$(dbusservicedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(dbusservicedir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(dbusservicedir)" && rm -f $$files + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +$(RECURSIVE_CLEAN_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-recursive +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(DATA) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(NetworkManagerdir)" "$(DESTDIR)$(dbusservicedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-recursive + +clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \ + clean-noinstLTLIBRARIES clean-sbinPROGRAMS mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-NetworkManagerDATA install-dbusserviceDATA + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-data-hook +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-libexecPROGRAMS install-sbinPROGRAMS + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-NetworkManagerDATA uninstall-dbusserviceDATA \ + uninstall-libexecPROGRAMS uninstall-sbinPROGRAMS + +.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) all check \ + ctags-recursive install install-am install-data-am \ + install-strip tags-recursive + +.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ + all all-am check check-am clean clean-generic \ + clean-libexecPROGRAMS clean-libtool clean-noinstLTLIBRARIES \ + clean-sbinPROGRAMS ctags ctags-recursive distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-NetworkManagerDATA install-am install-data \ + install-data-am install-data-hook install-dbusserviceDATA \ + install-dvi install-dvi-am install-exec install-exec-am \ + install-html install-html-am install-info install-info-am \ + install-libexecPROGRAMS install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-sbinPROGRAMS install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-recursive uninstall \ + uninstall-NetworkManagerDATA uninstall-am \ + uninstall-dbusserviceDATA uninstall-libexecPROGRAMS \ + uninstall-sbinPROGRAMS + + +nm-access-point-glue.h: $(top_srcdir)/introspection/nm-access-point.xml + dbus-binding-tool --prefix=nm_access_point --mode=glib-server --output=$@ $< + +nm-manager-glue.h: $(top_srcdir)/introspection/nm-manager.xml + dbus-binding-tool --prefix=nm_manager --mode=glib-server --output=$@ $< + +nm-device-interface-glue.h: $(top_srcdir)/introspection/nm-device.xml + dbus-binding-tool --prefix=nm_device_interface --mode=glib-server --output=$@ $< + +nm-device-ethernet-glue.h: $(top_srcdir)/introspection/nm-device-ethernet.xml + dbus-binding-tool --prefix=nm_device_ethernet --mode=glib-server --output=$@ $< + +nm-device-wifi-glue.h: $(top_srcdir)/introspection/nm-device-wifi.xml + dbus-binding-tool --prefix=nm_device_wifi --mode=glib-server --output=$@ $< + +nm-device-bt-glue.h: $(top_srcdir)/introspection/nm-device-bt.xml + dbus-binding-tool --prefix=nm_device_bt --mode=glib-server --output=$@ $< + +nm-device-olpc-mesh-glue.h: $(top_srcdir)/introspection/nm-device-olpc-mesh.xml + dbus-binding-tool --prefix=nm_device_olpc_mesh --mode=glib-server --output=$@ $< + +nm-ip4-config-glue.h: $(top_srcdir)/introspection/nm-ip4-config.xml + dbus-binding-tool --prefix=nm_ip4_config --mode=glib-server --output=$@ $< + +nm-ip6-config-glue.h: $(top_srcdir)/introspection/nm-ip6-config.xml + dbus-binding-tool --prefix=nm_ip6_config --mode=glib-server --output=$@ $< + +nm-active-connection-glue.h: $(top_srcdir)/introspection/nm-active-connection.xml + dbus-binding-tool --prefix=nm_active_connection --mode=glib-server --output=$@ $< + +nm-dhcp4-config-glue.h: $(top_srcdir)/introspection/nm-dhcp4-config.xml + dbus-binding-tool --prefix=nm_dhcp4_config --mode=glib-server --output=$@ $< + +nm-dhcp6-config-glue.h: $(top_srcdir)/introspection/nm-dhcp6-config.xml + dbus-binding-tool --prefix=nm_dhcp6_config --mode=glib-server --output=$@ $< + +nm-device-cdma-glue.h: $(top_srcdir)/introspection/nm-device-cdma.xml + dbus-binding-tool --prefix=nm_device_cdma --mode=glib-server --output=$@ $< + +nm-device-gsm-glue.h: $(top_srcdir)/introspection/nm-device-gsm.xml + dbus-binding-tool --prefix=nm_device_gsm --mode=glib-server --output=$@ $< +install-data-hook: + $(mkinstalldirs) -m 0700 $(DESTDIR)$(rundir) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/NetworkManager.conf b/src/NetworkManager.conf new file mode 100644 index 00000000..8d083140 --- /dev/null +++ b/src/NetworkManager.conf @@ -0,0 +1,82 @@ +<!DOCTYPE busconfig PUBLIC + "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <policy user="root"> + <allow own="org.freedesktop.NetworkManager"/> + <allow own="org.freedesktop.NetworkManagerSystemSettings"/> + + <allow send_destination="org.freedesktop.NetworkManager"/> + <allow send_destination="org.freedesktop.NetworkManagerSystemSettings"/> + + <allow send_destination="org.freedesktop.NetworkManager" + send_interface="org.freedesktop.NetworkManager.PPP"/> + </policy> + <policy at_console="true"> + <allow send_destination="org.freedesktop.NetworkManager"/> + + <allow send_destination="org.freedesktop.NetworkManager" + send_interface="org.freedesktop.DBus.Introspectable"/> + + <allow send_destination="org.freedesktop.NetworkManager" + send_interface="org.freedesktop.DBus.Properties"/> + + <allow send_destination="org.freedesktop.NetworkManager" + send_interface="org.freedesktop.NetworkManager"/> + + <allow send_destination="org.freedesktop.NetworkManager" + send_interface="org.freedesktop.NetworkManager.AccessPoint"/> + + <allow send_destination="org.freedesktop.NetworkManager" + send_interface="org.freedesktop.NetworkManager.Connection.Active"/> + + <allow send_destination="org.freedesktop.NetworkManager" + send_interface="org.freedesktop.NetworkManager.Device.Cdma"/> + + <allow send_destination="org.freedesktop.NetworkManager" + send_interface="org.freedesktop.NetworkManager.Device.Wired"/> + + <allow send_destination="org.freedesktop.NetworkManager" + send_interface="org.freedesktop.NetworkManager.Device.Gsm"/> + + <allow send_destination="org.freedesktop.NetworkManager" + send_interface="org.freedesktop.NetworkManager.Device.Serial"/> + + <allow send_destination="org.freedesktop.NetworkManager" + send_interface="org.freedesktop.NetworkManager.Device.Wireless"/> + + <allow send_destination="org.freedesktop.NetworkManager" + send_interface="org.freedesktop.NetworkManager.Device"/> + + <allow send_destination="org.freedesktop.NetworkManager" + send_interface="org.freedesktop.NetworkManager.DHCP4Config"/> + + <allow send_destination="org.freedesktop.NetworkManager" + send_interface="org.freedesktop.NetworkManager.IP4Config"/> + + <allow send_destination="org.freedesktop.NetworkManager" + send_interface="org.freedesktop.NetworkManager.VPN.Connection"/> + + <deny send_destination="org.freedesktop.NetworkManager" + send_interface="org.freedesktop.NetworkManager" + send_member="SetLogging"/> + </policy> + <policy context="default"> + <deny own="org.freedesktop.NetworkManager"/> + <deny own="org.freedesktop.NetworkManagerSystemSettings"/> + + <deny send_destination="org.freedesktop.NetworkManager"/> + <allow send_destination="org.freedesktop.NetworkManagerSystemSettings"/> + + <deny send_destination="org.freedesktop.NetworkManager" + send_interface="org.freedesktop.NetworkManager" + send_member="SetLogging"/> + + <!-- The org.freedesktop.NetworkManagerSettings.Connection.Secrets + interface is secured via PolicyKit. + --> + </policy> + + <limit name="max_replies_per_connection">512</limit> +</busconfig> + diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c new file mode 100644 index 00000000..22cf2fa0 --- /dev/null +++ b/src/NetworkManagerUtils.c @@ -0,0 +1,625 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2004 - 2010 Red Hat, Inc. + * Copyright (C) 2005 - 2008 Novell, Inc. + */ + +#include <glib.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "NetworkManagerUtils.h" +#include "nm-utils.h" +#include "nm-logging.h" +#include "nm-device.h" +#include "nm-device-wifi.h" +#include "nm-device-ethernet.h" +#include "nm-dbus-manager.h" +#include "nm-dispatcher-action.h" +#include "nm-dbus-glib-types.h" + +#include <netlink/addr.h> +#include <netinet/in.h> + +/* + * nm_ethernet_address_is_valid + * + * Compares an Ethernet address against known invalid addresses. + * + */ +gboolean +nm_ethernet_address_is_valid (const struct ether_addr *test_addr) +{ + guint8 invalid_addr1[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + guint8 invalid_addr2[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + guint8 invalid_addr3[ETH_ALEN] = {0x44, 0x44, 0x44, 0x44, 0x44, 0x44}; + guint8 invalid_addr4[ETH_ALEN] = {0x00, 0x30, 0xb4, 0x00, 0x00, 0x00}; /* prism54 dummy MAC */ + + g_return_val_if_fail (test_addr != NULL, FALSE); + + /* Compare the AP address the card has with invalid ethernet MAC addresses. */ + if (!memcmp (test_addr->ether_addr_octet, &invalid_addr1, ETH_ALEN)) + return FALSE; + + if (!memcmp (test_addr->ether_addr_octet, &invalid_addr2, ETH_ALEN)) + return FALSE; + + if (!memcmp (test_addr->ether_addr_octet, &invalid_addr3, ETH_ALEN)) + return FALSE; + + if (!memcmp (test_addr->ether_addr_octet, &invalid_addr4, ETH_ALEN)) + return FALSE; + + if (test_addr->ether_addr_octet[0] & 1) /* Multicast addresses */ + return FALSE; + + return TRUE; +} + + +int +nm_spawn_process (const char *args) +{ + gint num_args; + char **argv = NULL; + int status = -1; + GError *error = NULL; + + g_return_val_if_fail (args != NULL, -1); + + if (!g_shell_parse_argv (args, &num_args, &argv, &error)) { + nm_log_warn (LOGD_CORE, "could not parse arguments for '%s': %s", args, error->message); + g_error_free (error); + return -1; + } + + if (!g_spawn_sync ("/", argv, NULL, 0, NULL, NULL, NULL, NULL, &status, &error)) { + nm_log_warn (LOGD_CORE, "could not spawn process '%s': %s", args, error->message); + g_error_free (error); + } + + g_strfreev (argv); + return status; +} + +/* + * nm_utils_ip4_netmask_to_prefix + * + * Figure out the network prefix from a netmask. Netmask + * MUST be in network byte order. + * + */ +guint32 +nm_utils_ip4_netmask_to_prefix (guint32 netmask) +{ + guchar *p, *end; + guint32 prefix = 0; + + p = (guchar *) &netmask; + end = p + sizeof (guint32); + + while ((*p == 0xFF) && p < end) { + prefix += 8; + p++; + } + + if (p < end) { + guchar v = *p; + + while (v) { + prefix++; + v <<= 1; + } + } + + return prefix; +} + +/* + * nm_utils_ip4_prefix_to_netmask + * + * Figure out the netmask from a prefix. + * + */ +guint32 +nm_utils_ip4_prefix_to_netmask (guint32 prefix) +{ + guint32 msk = 0x80000000; + guint32 netmask = 0; + + while (prefix > 0) { + netmask |= msk; + msk >>= 1; + prefix--; + } + + return (guint32) htonl (netmask); +} + +char * +nm_ether_ntop (const struct ether_addr *mac) +{ + /* we like leading zeros and all-caps, instead + * of what glibc's ether_ntop() gives us + */ + return g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", + mac->ether_addr_octet[0], mac->ether_addr_octet[1], + mac->ether_addr_octet[2], mac->ether_addr_octet[3], + mac->ether_addr_octet[4], mac->ether_addr_octet[5]); +} + +void +nm_utils_merge_ip4_config (NMIP4Config *ip4_config, NMSettingIP4Config *setting) +{ + int i, j; + + if (!setting) + return; /* Defaults are just fine */ + + if (nm_setting_ip4_config_get_ignore_auto_dns (setting)) { + nm_ip4_config_reset_nameservers (ip4_config); + nm_ip4_config_reset_domains (ip4_config); + nm_ip4_config_reset_searches (ip4_config); + } + + if (nm_setting_ip4_config_get_ignore_auto_routes (setting)) + nm_ip4_config_reset_routes (ip4_config); + + for (i = 0; i < nm_setting_ip4_config_get_num_dns (setting); i++) { + guint32 ns; + gboolean found = FALSE; + + /* Avoid dupes */ + ns = nm_setting_ip4_config_get_dns (setting, i); + for (j = 0; j < nm_ip4_config_get_num_nameservers (ip4_config); j++) { + if (nm_ip4_config_get_nameserver (ip4_config, j) == ns) { + found = TRUE; + break; + } + } + + if (!found) + nm_ip4_config_add_nameserver (ip4_config, ns); + } + + /* DNS search domains */ + for (i = 0; i < nm_setting_ip4_config_get_num_dns_searches (setting); i++) { + const char *search = nm_setting_ip4_config_get_dns_search (setting, i); + gboolean found = FALSE; + + /* Avoid dupes */ + for (j = 0; j < nm_ip4_config_get_num_searches (ip4_config); j++) { + if (!strcmp (search, nm_ip4_config_get_search (ip4_config, j))) { + found = TRUE; + break; + } + } + + if (!found) + nm_ip4_config_add_search (ip4_config, search); + } + + /* IPv4 addresses */ + for (i = 0; i < nm_setting_ip4_config_get_num_addresses (setting); i++) { + NMIP4Address *setting_addr = nm_setting_ip4_config_get_address (setting, i); + guint32 num; + + num = nm_ip4_config_get_num_addresses (ip4_config); + for (j = 0; j < num; j++) { + NMIP4Address *cfg_addr = nm_ip4_config_get_address (ip4_config, j); + + /* Dupe, override with user-specified address */ + if (nm_ip4_address_get_address (cfg_addr) == nm_ip4_address_get_address (setting_addr)) { + nm_ip4_config_replace_address (ip4_config, j, setting_addr); + break; + } + } + + if (j == num) + nm_ip4_config_add_address (ip4_config, setting_addr); + } + + /* IPv4 routes */ + for (i = 0; i < nm_setting_ip4_config_get_num_routes (setting); i++) { + NMIP4Route *setting_route = nm_setting_ip4_config_get_route (setting, i); + guint32 num; + + num = nm_ip4_config_get_num_routes (ip4_config); + for (j = 0; j < num; j++) { + NMIP4Route *cfg_route = nm_ip4_config_get_route (ip4_config, j); + + /* Dupe, override with user-specified route */ + if ( (nm_ip4_route_get_dest (cfg_route) == nm_ip4_route_get_dest (setting_route)) + && (nm_ip4_route_get_prefix (cfg_route) == nm_ip4_route_get_prefix (setting_route)) + && (nm_ip4_route_get_next_hop (cfg_route) == nm_ip4_route_get_next_hop (setting_route))) { + nm_ip4_config_replace_route (ip4_config, j, setting_route); + break; + } + } + + if (j == num) + nm_ip4_config_add_route (ip4_config, setting_route); + } + + if (nm_setting_ip4_config_get_never_default (setting)) + nm_ip4_config_set_never_default (ip4_config, TRUE); +} + +static inline gboolean +ip6_addresses_equal (const struct in6_addr *a, const struct in6_addr *b) +{ + return memcmp (a, b, sizeof (struct in6_addr)) == 0; +} + +/* This is exactly identical to nm_utils_merge_ip4_config, with s/4/6/, + * except that we can't compare addresses with ==. + */ +void +nm_utils_merge_ip6_config (NMIP6Config *ip6_config, NMSettingIP6Config *setting) +{ + int i, j; + + if (!setting) + return; /* Defaults are just fine */ + + if (nm_setting_ip6_config_get_ignore_auto_dns (setting)) { + nm_ip6_config_reset_nameservers (ip6_config); + nm_ip6_config_reset_domains (ip6_config); + nm_ip6_config_reset_searches (ip6_config); + } + + if (nm_setting_ip6_config_get_ignore_auto_routes (setting)) + nm_ip6_config_reset_routes (ip6_config); + + for (i = 0; i < nm_setting_ip6_config_get_num_dns (setting); i++) { + const struct in6_addr *ns; + gboolean found = FALSE; + + /* Avoid dupes */ + ns = nm_setting_ip6_config_get_dns (setting, i); + for (j = 0; j < nm_ip6_config_get_num_nameservers (ip6_config); j++) { + if (ip6_addresses_equal (nm_ip6_config_get_nameserver (ip6_config, j), ns)) { + found = TRUE; + break; + } + } + + if (!found) + nm_ip6_config_add_nameserver (ip6_config, ns); + } + + /* DNS search domains */ + for (i = 0; i < nm_setting_ip6_config_get_num_dns_searches (setting); i++) { + const char *search = nm_setting_ip6_config_get_dns_search (setting, i); + gboolean found = FALSE; + + /* Avoid dupes */ + for (j = 0; j < nm_ip6_config_get_num_searches (ip6_config); j++) { + if (!strcmp (search, nm_ip6_config_get_search (ip6_config, j))) { + found = TRUE; + break; + } + } + + if (!found) + nm_ip6_config_add_search (ip6_config, search); + } + + /* IPv6 addresses */ + for (i = 0; i < nm_setting_ip6_config_get_num_addresses (setting); i++) { + NMIP6Address *setting_addr = nm_setting_ip6_config_get_address (setting, i); + guint32 num; + + num = nm_ip6_config_get_num_addresses (ip6_config); + for (j = 0; j < num; j++) { + NMIP6Address *cfg_addr = nm_ip6_config_get_address (ip6_config, j); + + /* Dupe, override with user-specified address */ + if (ip6_addresses_equal (nm_ip6_address_get_address (cfg_addr), nm_ip6_address_get_address (setting_addr))) { + nm_ip6_config_replace_address (ip6_config, j, setting_addr); + break; + } + } + + if (j == num) + nm_ip6_config_add_address (ip6_config, setting_addr); + } + + /* IPv6 routes */ + for (i = 0; i < nm_setting_ip6_config_get_num_routes (setting); i++) { + NMIP6Route *setting_route = nm_setting_ip6_config_get_route (setting, i); + guint32 num; + + num = nm_ip6_config_get_num_routes (ip6_config); + for (j = 0; j < num; j++) { + NMIP6Route *cfg_route = nm_ip6_config_get_route (ip6_config, j); + + /* Dupe, override with user-specified route */ + if ( ip6_addresses_equal (nm_ip6_route_get_dest (cfg_route), nm_ip6_route_get_dest (setting_route)) + && (nm_ip6_route_get_prefix (cfg_route) == nm_ip6_route_get_prefix (setting_route)) + && ip6_addresses_equal (nm_ip6_route_get_next_hop (cfg_route), nm_ip6_route_get_next_hop (setting_route))) { + nm_ip6_config_replace_route (ip6_config, j, setting_route); + break; + } + } + + if (j == num) + nm_ip6_config_add_route (ip6_config, setting_route); + } + + if (nm_setting_ip6_config_get_never_default (setting)) + nm_ip6_config_set_never_default (ip6_config, TRUE); +} + +void +nm_utils_call_dispatcher (const char *action, + NMConnection *connection, + NMDevice *device, + const char *vpn_iface) +{ + NMDBusManager *dbus_mgr; + DBusGProxy *proxy; + DBusGConnection *g_connection; + GHashTable *connection_hash; + GHashTable *connection_props; + GHashTable *device_props; + + g_return_if_fail (action != NULL); + + /* All actions except 'hostname' require a device */ + if (strcmp (action, "hostname")) + g_return_if_fail (NM_IS_DEVICE (device)); + + dbus_mgr = nm_dbus_manager_get (); + g_connection = nm_dbus_manager_get_connection (dbus_mgr); + proxy = dbus_g_proxy_new_for_name (g_connection, + NM_DISPATCHER_DBUS_SERVICE, + NM_DISPATCHER_DBUS_PATH, + NM_DISPATCHER_DBUS_IFACE); + if (!proxy) { + nm_log_err (LOGD_CORE, "could not get dispatcher proxy!"); + g_object_unref (dbus_mgr); + return; + } + + if (connection) { + connection_hash = nm_connection_to_hash (connection); + + connection_props = value_hash_create (); + + /* Service name */ + if (nm_connection_get_scope (connection) == NM_CONNECTION_SCOPE_USER) { + value_hash_add_str (connection_props, + NMD_CONNECTION_PROPS_SERVICE_NAME, + NM_DBUS_SERVICE_USER_SETTINGS); + } else if (nm_connection_get_scope (connection) == NM_CONNECTION_SCOPE_SYSTEM) { + value_hash_add_str (connection_props, + NMD_CONNECTION_PROPS_SERVICE_NAME, + NM_DBUS_SERVICE_SYSTEM_SETTINGS); + } + + /* path */ + value_hash_add_object_path (connection_props, + NMD_CONNECTION_PROPS_PATH, + nm_connection_get_path (connection)); + } else { + connection_hash = value_hash_create (); + connection_props = value_hash_create (); + } + + device_props = value_hash_create (); + + /* Hostname actions do not require a device */ + if (strcmp (action, "hostname")) { + /* interface */ + value_hash_add_str (device_props, NMD_DEVICE_PROPS_INTERFACE, nm_device_get_iface (device)); + + /* IP interface */ + if (vpn_iface) { + value_hash_add_str (device_props, NMD_DEVICE_PROPS_IP_INTERFACE, vpn_iface); + } else if (nm_device_get_ip_iface (device)) { + value_hash_add_str (device_props, NMD_DEVICE_PROPS_IP_INTERFACE, nm_device_get_ip_iface (device)); + } + + /* type */ + value_hash_add_uint (device_props, NMD_DEVICE_PROPS_TYPE, nm_device_get_device_type (device)); + + /* state */ + value_hash_add_uint (device_props, NMD_DEVICE_PROPS_STATE, nm_device_get_state (device)); + value_hash_add_object_path (device_props, NMD_DEVICE_PROPS_PATH, nm_device_get_path (device)); + } + + dbus_g_proxy_call_no_reply (proxy, "Action", + G_TYPE_STRING, action, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash, + DBUS_TYPE_G_MAP_OF_VARIANT, connection_props, + DBUS_TYPE_G_MAP_OF_VARIANT, device_props, + G_TYPE_INVALID); + + g_object_unref (proxy); + g_hash_table_destroy (connection_hash); + g_hash_table_destroy (connection_props); + g_hash_table_destroy (device_props); + g_object_unref (dbus_mgr); +} + +gboolean +nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr) +{ + const GSList *iter; + char *hwaddr_match, *p; + + g_return_val_if_fail (hwaddr != NULL, FALSE); + + p = hwaddr_match = g_strdup_printf ("mac:%s", hwaddr); + + while (*p) { + *p = g_ascii_tolower (*p); + p++; + } + + for (iter = specs; iter; iter = g_slist_next (iter)) { + if (!strcmp ((const char *) iter->data, hwaddr_match)) { + g_free (hwaddr_match); + return TRUE; + } + } + + g_free (hwaddr_match); + return FALSE; +} + +/*********************************/ + +static void +nm_gvalue_destroy (gpointer data) +{ + GValue *value = (GValue *) data; + + g_value_unset (value); + g_slice_free (GValue, value); +} + +GHashTable * +value_hash_create (void) +{ + return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nm_gvalue_destroy); +} + +void +value_hash_add (GHashTable *hash, + const char *key, + GValue *value) +{ + g_hash_table_insert (hash, g_strdup (key), value); +} + +void +value_hash_add_str (GHashTable *hash, + const char *key, + const char *str) +{ + GValue *value; + + value = g_slice_new0 (GValue); + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, str); + + value_hash_add (hash, key, value); +} + +void +value_hash_add_object_path (GHashTable *hash, + const char *key, + const char *op) +{ + GValue *value; + + value = g_slice_new0 (GValue); + g_value_init (value, DBUS_TYPE_G_OBJECT_PATH); + g_value_set_boxed (value, op); + + value_hash_add (hash, key, value); +} + +void +value_hash_add_uint (GHashTable *hash, + const char *key, + guint32 val) +{ + GValue *value; + + value = g_slice_new0 (GValue); + g_value_init (value, G_TYPE_UINT); + g_value_set_uint (value, val); + + value_hash_add (hash, key, value); +} + +void +value_hash_add_bool (GHashTable *hash, + const char *key, + gboolean val) +{ + GValue *value; + + value = g_slice_new0 (GValue); + g_value_init (value, G_TYPE_BOOLEAN); + g_value_set_boolean (value, val); + + value_hash_add (hash, key, value); +} + +gboolean +nm_utils_do_sysctl (const char *path, const char *value) +{ + int fd, len, nwrote, total; + + fd = open (path, O_WRONLY | O_TRUNC); + if (fd == -1) + return FALSE; + + len = strlen (value); + total = 0; + do { + nwrote = write (fd, value + total, len - total); + if (nwrote == -1) { + if (errno == EINTR) + continue; + close (fd); + return FALSE; + } + total += nwrote; + } while (total < len); + + close (fd); + return TRUE; +} + +gboolean +nm_utils_get_proc_sys_net_value (const char *path, + const char *iface, + guint32 *out_value) +{ + GError *error = NULL; + char *contents = NULL; + gboolean success = FALSE; + long int tmp; + + if (!g_file_get_contents (path, &contents, NULL, &error)) { + nm_log_dbg (LOGD_DEVICE, "(%s): error reading %s: (%d) %s", + iface, path, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + } else { + errno = 0; + tmp = strtol (contents, NULL, 10); + if ((errno == 0) && (tmp == 0 || tmp == 1)) { + *out_value = (guint32) tmp; + success = TRUE; + } + g_free (contents); + } + + return success; +} + diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h new file mode 100644 index 00000000..e3d1793b --- /dev/null +++ b/src/NetworkManagerUtils.h @@ -0,0 +1,80 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2004 - 2008 Red Hat, Inc. + * Copyright (C) 2005 - 2008 Novell, Inc. + */ + +#ifndef NETWORK_MANAGER_UTILS_H +#define NETWORK_MANAGER_UTILS_H + +#include <glib.h> +#include <stdio.h> +#include <net/ethernet.h> + +#include "nm-device.h" +#include "nm-ip4-config.h" +#include "nm-setting-ip4-config.h" +#include "nm-ip6-config.h" +#include "nm-setting-ip6-config.h" +#include "nm-connection.h" + +gboolean nm_ethernet_address_is_valid (const struct ether_addr *test_addr); + +int nm_spawn_process (const char *args); + +char *nm_ether_ntop (const struct ether_addr *mac); + +void nm_utils_merge_ip4_config (NMIP4Config *ip4_config, NMSettingIP4Config *setting); +void nm_utils_merge_ip6_config (NMIP6Config *ip6_config, NMSettingIP6Config *setting); + +void nm_utils_call_dispatcher (const char *action, + NMConnection *connection, + NMDevice *device, + const char *vpn_iface); + +gboolean nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr); + + +GHashTable *value_hash_create (void); +void value_hash_add (GHashTable *hash, + const char *key, + GValue *value); + +void value_hash_add_str (GHashTable *hash, + const char *key, + const char *str); + +void value_hash_add_object_path (GHashTable *hash, + const char *key, + const char *op); + +void value_hash_add_uint (GHashTable *hash, + const char *key, + guint32 val); + +void value_hash_add_bool (GHashTable *hash, + const char *key, + gboolean val); + +gboolean nm_utils_do_sysctl (const char *path, const char *value); + +gboolean nm_utils_get_proc_sys_net_value (const char *path, + const char *iface, + guint32 *out_value); + +#endif /* NETWORK_MANAGER_UTILS_H */ diff --git a/src/backends/Makefile.am b/src/backends/Makefile.am new file mode 100644 index 00000000..c3485e5f --- /dev/null +++ b/src/backends/Makefile.am @@ -0,0 +1,70 @@ +INCLUDES = \ + -I${top_srcdir} \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/include \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/vpn-manager \ + -I${top_srcdir}/src/named-manager \ + -I${top_srcdir}/libnm-util + +noinst_LTLIBRARIES = libnmbackend.la + +libnmbackend_la_SOURCES = NetworkManagerGeneric.c \ + NetworkManagerGeneric.h + +libnmbackend_la_LIBADD = + +if TARGET_REDHAT +libnmbackend_la_SOURCES += NetworkManagerRedHat.c +endif + +if TARGET_SUSE +libnmbackend_la_SOURCES += NetworkManagerSuSE.c +endif + +if TARGET_GENTOO +libnmbackend_la_SOURCES += NetworkManagerGentoo.c +endif + +if TARGET_DEBIAN +libnmbackend_la_SOURCES += NetworkManagerDebian.c +endif + +if TARGET_SLACKWARE +libnmbackend_la_SOURCES += NetworkManagerSlackware.c +endif + +if TARGET_ARCH +libnmbackend_la_SOURCES += NetworkManagerArch.c +endif + +if TARGET_PALDO +libnmbackend_la_SOURCES += NetworkManagerPaldo.c +endif + +if TARGET_FRUGALWARE +libnmbackend_la_SOURCES += NetworkManagerFrugalware.c +libnmbackend_la_LIBADD += -lfwnetconfig -lfwutil +endif + +if TARGET_MANDRIVA +libnmbackend_la_SOURCES += NetworkManagerMandriva.c +endif + +if TARGET_PARDUS +libnmbackend_la_SOURCES += NetworkManagerPardus.c +endif + +libnmbackend_la_LIBADD += \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + +libnmbackend_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DBINDIR=\"$(bindir)\" \ + -DDATADIR=\"$(datadir)\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" + diff --git a/src/backends/Makefile.in b/src/backends/Makefile.in new file mode 100644 index 00000000..9e3b8735 --- /dev/null +++ b/src/backends/Makefile.in @@ -0,0 +1,689 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@TARGET_REDHAT_TRUE@am__append_1 = NetworkManagerRedHat.c +@TARGET_SUSE_TRUE@am__append_2 = NetworkManagerSuSE.c +@TARGET_GENTOO_TRUE@am__append_3 = NetworkManagerGentoo.c +@TARGET_DEBIAN_TRUE@am__append_4 = NetworkManagerDebian.c +@TARGET_SLACKWARE_TRUE@am__append_5 = NetworkManagerSlackware.c +@TARGET_ARCH_TRUE@am__append_6 = NetworkManagerArch.c +@TARGET_PALDO_TRUE@am__append_7 = NetworkManagerPaldo.c +@TARGET_FRUGALWARE_TRUE@am__append_8 = NetworkManagerFrugalware.c +@TARGET_FRUGALWARE_TRUE@am__append_9 = -lfwnetconfig -lfwutil +@TARGET_MANDRIVA_TRUE@am__append_10 = NetworkManagerMandriva.c +@TARGET_PARDUS_TRUE@am__append_11 = NetworkManagerPardus.c +subdir = src/backends +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libnmbackend_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am__libnmbackend_la_SOURCES_DIST = NetworkManagerGeneric.c \ + NetworkManagerGeneric.h NetworkManagerRedHat.c \ + NetworkManagerSuSE.c NetworkManagerGentoo.c \ + NetworkManagerDebian.c NetworkManagerSlackware.c \ + NetworkManagerArch.c NetworkManagerPaldo.c \ + NetworkManagerFrugalware.c NetworkManagerMandriva.c \ + NetworkManagerPardus.c +@TARGET_REDHAT_TRUE@am__objects_1 = \ +@TARGET_REDHAT_TRUE@ libnmbackend_la-NetworkManagerRedHat.lo +@TARGET_SUSE_TRUE@am__objects_2 = \ +@TARGET_SUSE_TRUE@ libnmbackend_la-NetworkManagerSuSE.lo +@TARGET_GENTOO_TRUE@am__objects_3 = \ +@TARGET_GENTOO_TRUE@ libnmbackend_la-NetworkManagerGentoo.lo +@TARGET_DEBIAN_TRUE@am__objects_4 = \ +@TARGET_DEBIAN_TRUE@ libnmbackend_la-NetworkManagerDebian.lo +@TARGET_SLACKWARE_TRUE@am__objects_5 = libnmbackend_la-NetworkManagerSlackware.lo +@TARGET_ARCH_TRUE@am__objects_6 = \ +@TARGET_ARCH_TRUE@ libnmbackend_la-NetworkManagerArch.lo +@TARGET_PALDO_TRUE@am__objects_7 = \ +@TARGET_PALDO_TRUE@ libnmbackend_la-NetworkManagerPaldo.lo +@TARGET_FRUGALWARE_TRUE@am__objects_8 = libnmbackend_la-NetworkManagerFrugalware.lo +@TARGET_MANDRIVA_TRUE@am__objects_9 = libnmbackend_la-NetworkManagerMandriva.lo +@TARGET_PARDUS_TRUE@am__objects_10 = \ +@TARGET_PARDUS_TRUE@ libnmbackend_la-NetworkManagerPardus.lo +am_libnmbackend_la_OBJECTS = libnmbackend_la-NetworkManagerGeneric.lo \ + $(am__objects_1) $(am__objects_2) $(am__objects_3) \ + $(am__objects_4) $(am__objects_5) $(am__objects_6) \ + $(am__objects_7) $(am__objects_8) $(am__objects_9) \ + $(am__objects_10) +libnmbackend_la_OBJECTS = $(am_libnmbackend_la_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libnmbackend_la_SOURCES) +DIST_SOURCES = $(am__libnmbackend_la_SOURCES_DIST) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +IPTABLES_PATH = @IPTABLES_PATH@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBM = @LIBM@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POFILES = @POFILES@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +RANLIB = @RANLIB@ +RESOLVCONF_PATH = @RESOLVCONF_PATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = \ + -I${top_srcdir} \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/include \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/vpn-manager \ + -I${top_srcdir}/src/named-manager \ + -I${top_srcdir}/libnm-util + +noinst_LTLIBRARIES = libnmbackend.la +libnmbackend_la_SOURCES = NetworkManagerGeneric.c \ + NetworkManagerGeneric.h $(am__append_1) $(am__append_2) \ + $(am__append_3) $(am__append_4) $(am__append_5) \ + $(am__append_6) $(am__append_7) $(am__append_8) \ + $(am__append_10) $(am__append_11) +libnmbackend_la_LIBADD = $(am__append_9) \ + $(top_builddir)/src/logging/libnm-logging.la $(DBUS_LIBS) \ + $(GLIB_LIBS) +libnmbackend_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DBINDIR=\"$(bindir)\" \ + -DDATADIR=\"$(datadir)\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/backends/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/backends/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libnmbackend.la: $(libnmbackend_la_OBJECTS) $(libnmbackend_la_DEPENDENCIES) + $(LINK) $(libnmbackend_la_OBJECTS) $(libnmbackend_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerArch.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerDebian.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerFrugalware.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerGeneric.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerGentoo.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerMandriva.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerPaldo.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerPardus.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerRedHat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerSlackware.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerSuSE.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libnmbackend_la-NetworkManagerGeneric.lo: NetworkManagerGeneric.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerGeneric.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerGeneric.Tpo -c -o libnmbackend_la-NetworkManagerGeneric.lo `test -f 'NetworkManagerGeneric.c' || echo '$(srcdir)/'`NetworkManagerGeneric.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerGeneric.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerGeneric.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='NetworkManagerGeneric.c' object='libnmbackend_la-NetworkManagerGeneric.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerGeneric.lo `test -f 'NetworkManagerGeneric.c' || echo '$(srcdir)/'`NetworkManagerGeneric.c + +libnmbackend_la-NetworkManagerRedHat.lo: NetworkManagerRedHat.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerRedHat.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerRedHat.Tpo -c -o libnmbackend_la-NetworkManagerRedHat.lo `test -f 'NetworkManagerRedHat.c' || echo '$(srcdir)/'`NetworkManagerRedHat.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerRedHat.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerRedHat.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='NetworkManagerRedHat.c' object='libnmbackend_la-NetworkManagerRedHat.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerRedHat.lo `test -f 'NetworkManagerRedHat.c' || echo '$(srcdir)/'`NetworkManagerRedHat.c + +libnmbackend_la-NetworkManagerSuSE.lo: NetworkManagerSuSE.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerSuSE.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerSuSE.Tpo -c -o libnmbackend_la-NetworkManagerSuSE.lo `test -f 'NetworkManagerSuSE.c' || echo '$(srcdir)/'`NetworkManagerSuSE.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerSuSE.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerSuSE.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='NetworkManagerSuSE.c' object='libnmbackend_la-NetworkManagerSuSE.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerSuSE.lo `test -f 'NetworkManagerSuSE.c' || echo '$(srcdir)/'`NetworkManagerSuSE.c + +libnmbackend_la-NetworkManagerGentoo.lo: NetworkManagerGentoo.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerGentoo.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerGentoo.Tpo -c -o libnmbackend_la-NetworkManagerGentoo.lo `test -f 'NetworkManagerGentoo.c' || echo '$(srcdir)/'`NetworkManagerGentoo.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerGentoo.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerGentoo.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='NetworkManagerGentoo.c' object='libnmbackend_la-NetworkManagerGentoo.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerGentoo.lo `test -f 'NetworkManagerGentoo.c' || echo '$(srcdir)/'`NetworkManagerGentoo.c + +libnmbackend_la-NetworkManagerDebian.lo: NetworkManagerDebian.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerDebian.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerDebian.Tpo -c -o libnmbackend_la-NetworkManagerDebian.lo `test -f 'NetworkManagerDebian.c' || echo '$(srcdir)/'`NetworkManagerDebian.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerDebian.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerDebian.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='NetworkManagerDebian.c' object='libnmbackend_la-NetworkManagerDebian.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerDebian.lo `test -f 'NetworkManagerDebian.c' || echo '$(srcdir)/'`NetworkManagerDebian.c + +libnmbackend_la-NetworkManagerSlackware.lo: NetworkManagerSlackware.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerSlackware.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerSlackware.Tpo -c -o libnmbackend_la-NetworkManagerSlackware.lo `test -f 'NetworkManagerSlackware.c' || echo '$(srcdir)/'`NetworkManagerSlackware.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerSlackware.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerSlackware.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='NetworkManagerSlackware.c' object='libnmbackend_la-NetworkManagerSlackware.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerSlackware.lo `test -f 'NetworkManagerSlackware.c' || echo '$(srcdir)/'`NetworkManagerSlackware.c + +libnmbackend_la-NetworkManagerArch.lo: NetworkManagerArch.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerArch.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerArch.Tpo -c -o libnmbackend_la-NetworkManagerArch.lo `test -f 'NetworkManagerArch.c' || echo '$(srcdir)/'`NetworkManagerArch.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerArch.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerArch.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='NetworkManagerArch.c' object='libnmbackend_la-NetworkManagerArch.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerArch.lo `test -f 'NetworkManagerArch.c' || echo '$(srcdir)/'`NetworkManagerArch.c + +libnmbackend_la-NetworkManagerPaldo.lo: NetworkManagerPaldo.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerPaldo.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerPaldo.Tpo -c -o libnmbackend_la-NetworkManagerPaldo.lo `test -f 'NetworkManagerPaldo.c' || echo '$(srcdir)/'`NetworkManagerPaldo.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerPaldo.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerPaldo.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='NetworkManagerPaldo.c' object='libnmbackend_la-NetworkManagerPaldo.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerPaldo.lo `test -f 'NetworkManagerPaldo.c' || echo '$(srcdir)/'`NetworkManagerPaldo.c + +libnmbackend_la-NetworkManagerFrugalware.lo: NetworkManagerFrugalware.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerFrugalware.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerFrugalware.Tpo -c -o libnmbackend_la-NetworkManagerFrugalware.lo `test -f 'NetworkManagerFrugalware.c' || echo '$(srcdir)/'`NetworkManagerFrugalware.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerFrugalware.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerFrugalware.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='NetworkManagerFrugalware.c' object='libnmbackend_la-NetworkManagerFrugalware.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerFrugalware.lo `test -f 'NetworkManagerFrugalware.c' || echo '$(srcdir)/'`NetworkManagerFrugalware.c + +libnmbackend_la-NetworkManagerMandriva.lo: NetworkManagerMandriva.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerMandriva.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerMandriva.Tpo -c -o libnmbackend_la-NetworkManagerMandriva.lo `test -f 'NetworkManagerMandriva.c' || echo '$(srcdir)/'`NetworkManagerMandriva.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerMandriva.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerMandriva.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='NetworkManagerMandriva.c' object='libnmbackend_la-NetworkManagerMandriva.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerMandriva.lo `test -f 'NetworkManagerMandriva.c' || echo '$(srcdir)/'`NetworkManagerMandriva.c + +libnmbackend_la-NetworkManagerPardus.lo: NetworkManagerPardus.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerPardus.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerPardus.Tpo -c -o libnmbackend_la-NetworkManagerPardus.lo `test -f 'NetworkManagerPardus.c' || echo '$(srcdir)/'`NetworkManagerPardus.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerPardus.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerPardus.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='NetworkManagerPardus.c' object='libnmbackend_la-NetworkManagerPardus.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerPardus.lo `test -f 'NetworkManagerPardus.c' || echo '$(srcdir)/'`NetworkManagerPardus.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/backends/NetworkManagerArch.c b/src/backends/NetworkManagerArch.c new file mode 100644 index 00000000..c70e2459 --- /dev/null +++ b/src/backends/NetworkManagerArch.c @@ -0,0 +1,70 @@ +/* NetworkManager -- Network link manager + * + * Backend implementation for the Arch Linux distribution http://www.archlinux.org + * + * Tor Krill <tor@krill.nu> and Will Rea <sillywilly@gmail.com> + * + * Updated by Wael Nasreddine <gandalf@siemens-mobiles.org> + * + * Updated by Valentine Sinitsyn <e_val@inbox.ru> + * + * Heavily based on NetworkManagerDebian.c by Matthew Garrett <mjg59@srcf.ucam.org> + * + * Heavily based on NetworkManagerRedhat.c by Dan Williams <dcbw@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2004 Tom Parker + * (C) Copyright 2004 Matthew Garrett + * (C) Copyright 2004 Red Hat, Inc. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "NetworkManagerGeneric.h" +#include "nm-system.h" +#include "NetworkManagerUtils.h" + +/* + * nm_system_enable_loopback + * + * Bring up the loopback interface + * + */ +void nm_system_enable_loopback (void) +{ + nm_generic_enable_loopback (); +} + +/* + * nm_system_update_dns + * + * Make glibc/nscd aware of any changes to the resolv.conf file by + * restarting nscd. + * + */ +void nm_system_update_dns (void) +{ + /* Check if the daemon was already running - do not start a new instance */ + if (g_file_test("/var/run/daemons/nscd", G_FILE_TEST_EXISTS)) + nm_spawn_process ("/etc/rc.d/nscd restart"); +} + diff --git a/src/backends/NetworkManagerDebian.c b/src/backends/NetworkManagerDebian.c new file mode 100644 index 00000000..ef9475af --- /dev/null +++ b/src/backends/NetworkManagerDebian.c @@ -0,0 +1,64 @@ +/* NetworkManager -- Network link manager + * + * Matthew Garrett <mjg59@srcf.ucam.org> + * + * Heavily based on NetworkManagerRedhat.c by Dan Williams <dcbw@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2004 Tom Parker + * (C) Copyright 2004 Matthew Garrett + * (C) Copyright 2004 Red Hat, Inc. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "NetworkManagerGeneric.h" +#include "nm-system.h" +#include "NetworkManagerUtils.h" +#include "nm-logging.h" + +/* + * nm_system_enable_loopback + * + * Bring up the loopback interface + * + */ +void nm_system_enable_loopback (void) +{ + nm_spawn_process ("/sbin/ifup lo"); +} + +/* + * nm_system_update_dns + * + * Invalidate the nscd host cache, if it exists, since + * we changed resolv.conf. + * + */ +void nm_system_update_dns (void) +{ + if (g_file_test ("/usr/sbin/nscd", G_FILE_TEST_IS_EXECUTABLE)) { + nm_log_info (LOGD_DNS, "Clearing nscd hosts cache."); + nm_spawn_process ("/usr/sbin/nscd -i hosts"); + } +} + diff --git a/src/backends/NetworkManagerFrugalware.c b/src/backends/NetworkManagerFrugalware.c new file mode 100644 index 00000000..dfe62921 --- /dev/null +++ b/src/backends/NetworkManagerFrugalware.c @@ -0,0 +1,58 @@ +/* NetworkManager -- Network link manager + * + * Implementation for the Frugalware Linux distro - http://www.frugalware.org + * + * Alex Smith <alex.extreme2@gmail.com> + * + * Based on NetworkManagerSlackware.c by Narayan Newton <narayan_newton@yahoo.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2006 Alex Smith + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "nm-system.h" + +/* Provided by the frugalwareutils package on Frugalware */ +#include <libfwnetconfig.h> + +/* + * nm_system_enable_loopback + * + * Bring up the loopback interface + * + */ +void nm_system_enable_loopback (void) +{ + fwnet_loup (); +} + + +/* + * nm_system_update_dns + * + * Make glibc/nscd aware of any changes to the resolv.conf file by + * restarting nscd. + * + */ +void nm_system_update_dns (void) +{ + /* I'm not running nscd */ +} + diff --git a/src/backends/NetworkManagerGeneric.c b/src/backends/NetworkManagerGeneric.c new file mode 100644 index 00000000..8f16066f --- /dev/null +++ b/src/backends/NetworkManagerGeneric.c @@ -0,0 +1,113 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2004 - 2010 Red Hat, Inc. + * (C) Copyright 2006 Timothee Lecomte <timothee.lecomte@ens.fr> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <stdio.h> +#include <sys/types.h> +#include <signal.h> +#include <arpa/inet.h> +#include <string.h> + +#include "NetworkManagerGeneric.h" +#include "nm-system.h" +#include "NetworkManagerUtils.h" +#include "nm-logging.h" +#include "nm-netlink-monitor.h" + +/* Because of a bug in libnl, rtnl.h should be included before route.h */ +#include <netlink/route/rtnl.h> + +#include <netlink/route/addr.h> +#include <netlink/netlink.h> + +/* + * nm_generic_enable_loopback + * + * Bring up the loopback interface + * + */ +void nm_generic_enable_loopback (void) +{ + struct nl_handle * nlh = NULL; + struct rtnl_addr * addr = NULL; + struct nl_addr * nl_addr = NULL; + guint32 binaddr = 0; + int iface_idx = -1; + int err; + + nm_system_device_set_up_down_with_iface ("lo", TRUE, NULL); + + nlh = nm_netlink_get_default_handle (); + if (!nlh) + return; + + iface_idx = nm_netlink_iface_to_index ("lo"); + if (iface_idx < 0) + return; + + addr = rtnl_addr_alloc (); + if (!addr) + return; + + binaddr = htonl (0x7f000001); /* 127.0.0.1 */ + nl_addr = nl_addr_build (AF_INET, &binaddr, sizeof(binaddr)); + if (!nl_addr) + goto out; + rtnl_addr_set_local (addr, nl_addr); + nl_addr_put (nl_addr); + + binaddr = htonl (0x7fffffff); /* 127.255.255.255 */ + nl_addr = nl_addr_build (AF_INET, &binaddr, sizeof(binaddr)); + if (!nl_addr) + goto out; + rtnl_addr_set_broadcast (addr, nl_addr); + nl_addr_put (nl_addr); + + rtnl_addr_set_prefixlen (addr, 8); + rtnl_addr_set_ifindex (addr, iface_idx); + rtnl_addr_set_scope (addr, RT_SCOPE_HOST); + rtnl_addr_set_label (addr, "lo"); + + if ((err = rtnl_addr_add (nlh, addr, 0)) < 0) { + if (err != -EEXIST) { + nm_log_warn (LOGD_CORE, "error %d returned from rtnl_addr_add():\n%s", err, nl_geterror()); + } + } +out: + if (addr) + rtnl_addr_put (addr); +} + +/* + * nm_generic_update_dns + * + * Make glibc/nscd aware of any changes to the resolv.conf file by + * restarting nscd. + * + */ +void nm_generic_update_dns (void) +{ +} + diff --git a/src/backends/NetworkManagerGeneric.h b/src/backends/NetworkManagerGeneric.h new file mode 100644 index 00000000..0a0a88a2 --- /dev/null +++ b/src/backends/NetworkManagerGeneric.h @@ -0,0 +1,30 @@ +/* NetworkManager -- Network link manager + * + * Timothee Lecomte <timothee.lecomte@ens.fr> + * + * Heavily based on NetworkManagerSystem.h by Dan Williams <dcbw@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2004 Red Hat, Inc. + */ + +#ifndef NETWORK_MANAGER_GENERIC_H +#define NETWORK_MANAGER_GENERIC_H + +void nm_generic_enable_loopback (void); +void nm_generic_update_dns (void); + +#endif diff --git a/src/backends/NetworkManagerGentoo.c b/src/backends/NetworkManagerGentoo.c new file mode 100644 index 00000000..8d9e68c5 --- /dev/null +++ b/src/backends/NetworkManagerGentoo.c @@ -0,0 +1,66 @@ +/* NetworkManager -- Network link manager + * + * Dan Williams <dcbw@redhat.com> + * Dan Willemsen <dan@willemsen.us> + * Robert Paskowitz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2004 Red Hat, Inc. + * (C) Copyright 2004 Dan Willemsen + * (C) Copyright 2004 Robert Paskowitz + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "NetworkManagerGeneric.h" +#include "nm-system.h" +#include "NetworkManagerUtils.h" +#include "nm-logging.h" + +/* + * nm_system_enable_loopback + * + * Bring up the loopback interface + * + */ +void nm_system_enable_loopback (void) +{ + /* No need to run net.lo if it is already running */ + if (nm_spawn_process ("/etc/init.d/net.lo status") != 0) + nm_spawn_process("/etc/init.d/net.lo start"); +} + +/* + * nm_system_update_dns + * + * Make glibc/nscd aware of any changes to the resolv.conf file by + * restarting nscd. Only restart if already running. + * + */ +void nm_system_update_dns (void) +{ + if (g_file_test ("/usr/sbin/nscd", G_FILE_TEST_IS_EXECUTABLE)) { + nm_log_info (LOGD_DNS, "Clearing nscd hosts cache."); + nm_spawn_process ("/usr/sbin/nscd -i hosts"); + } +} + diff --git a/src/backends/NetworkManagerMandriva.c b/src/backends/NetworkManagerMandriva.c new file mode 100644 index 00000000..8209ff4f --- /dev/null +++ b/src/backends/NetworkManagerMandriva.c @@ -0,0 +1,61 @@ +/* NetworkManager -- Network link manager + * + * Dan Williams <dcbw@redhat.com> + * Per Øyvind Karlsen <peroyvind@mandriva.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2004 Red Hat, Inc. + * (C) Copyright 2008 Per Øyvind Karlsen + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "NetworkManagerGeneric.h" +#include "nm-system.h" +#include "NetworkManagerUtils.h" + +/* + * nm_system_enable_loopback + * + * Bring up the loopback interface + * + */ +void nm_system_enable_loopback (void) +{ + nm_generic_enable_loopback (); +} + +/* + * nm_system_update_dns + * + * Invalidate the nscd host cache, if it exists, since + * we changed resolv.conf. + * + */ +void nm_system_update_dns (void) +{ + if (g_file_test ("/usr/sbin/nscd", G_FILE_TEST_EXISTS | G_FILE_TEST_IS_EXECUTABLE | G_FILE_TEST_IS_REGULAR)) { + nm_spawn_process ("/etc/init.d/nscd condrestart"); + nm_spawn_process ("/usr/sbin/nscd -i hosts"); + } +} + diff --git a/src/backends/NetworkManagerPaldo.c b/src/backends/NetworkManagerPaldo.c new file mode 100644 index 00000000..0c67e5bd --- /dev/null +++ b/src/backends/NetworkManagerPaldo.c @@ -0,0 +1,61 @@ +/* NetworkManager -- Network link manager + * + * Jürg Billeter <juerg@paldo.org> + * + * Heavily based on NetworkManagerRedhat.c by Dan Williams <dcbw@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2004 Red Hat, Inc. + * (C) Copyright 2006 Jürg Billeter + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "NetworkManagerGeneric.h" +#include "nm-system.h" +#include "NetworkManagerUtils.h" +#include "nm-logging.h" + +/* + * nm_system_enable_loopback + * + * Bring up the loopback interface + * + */ +void nm_system_enable_loopback (void) +{ + nm_generic_enable_loopback (); +} + +/* + * nm_system_update_dns + * + * Invalidate the nscd host cache, if it exists, since + * we changed resolv.conf. + * + */ +void nm_system_update_dns (void) +{ + nm_log_info (LOGD_DNS, "Clearing nscd hosts cache."); + nm_spawn_process ("/usr/sbin/nscd -i hosts"); +} + diff --git a/src/backends/NetworkManagerPardus.c b/src/backends/NetworkManagerPardus.c new file mode 100644 index 00000000..d952cf36 --- /dev/null +++ b/src/backends/NetworkManagerPardus.c @@ -0,0 +1,57 @@ +/* NetworkManager -- Network link manager + * + * Ozan Caglayan <ozan@pardus.org.tr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2004 RedHat, Inc. + * (C) Copyright 2009 TUBITAK/UEKAE + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "NetworkManagerGeneric.h" +#include "nm-system.h" + +/* + * nm_system_enable_loopback + * + * Bring up the loopback interface + * + */ +void nm_system_enable_loopback (void) +{ + nm_generic_enable_loopback (); +} + + +/* + * nm_system_update_dns + * + * Make glibc/nscd aware of any changes to the resolv.conf file by + * restarting nscd. + * + */ +void nm_system_update_dns (void) +{ + /* I'm not running nscd */ +} + diff --git a/src/backends/NetworkManagerRedHat.c b/src/backends/NetworkManagerRedHat.c new file mode 100644 index 00000000..954e58a8 --- /dev/null +++ b/src/backends/NetworkManagerRedHat.c @@ -0,0 +1,59 @@ +/* NetworkManager -- Network link manager + * + * Dan Williams <dcbw@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2004 Red Hat, Inc. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "NetworkManagerGeneric.h" +#include "nm-system.h" +#include "NetworkManagerUtils.h" + +/* + * nm_system_enable_loopback + * + * Bring up the loopback interface + * + */ +void nm_system_enable_loopback (void) +{ + nm_generic_enable_loopback (); +} + +/* + * nm_system_update_dns + * + * Invalidate the nscd host cache, if it exists, since + * we changed resolv.conf. + * + */ +void nm_system_update_dns (void) +{ + if (g_file_test ("/usr/sbin/nscd", G_FILE_TEST_EXISTS | G_FILE_TEST_IS_EXECUTABLE | G_FILE_TEST_IS_REGULAR)) { + nm_spawn_process ("/etc/init.d/nscd condrestart"); + nm_spawn_process ("/usr/sbin/nscd -i hosts"); + } +} + diff --git a/src/backends/NetworkManagerSlackware.c b/src/backends/NetworkManagerSlackware.c new file mode 100644 index 00000000..a35e1bb6 --- /dev/null +++ b/src/backends/NetworkManagerSlackware.c @@ -0,0 +1,57 @@ +/* NetworkManager -- Network link manager + * + * Narayan Newton <narayan_newton@yahoo.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2004 RedHat, Inc. + * (C) Copyright 2004 Narayan Newton + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "NetworkManagerGeneric.h" +#include "nm-system.h" + +/* + * nm_system_enable_loopback + * + * Bring up the loopback interface + * + */ +void nm_system_enable_loopback (void) +{ + nm_generic_enable_loopback (); +} + + +/* + * nm_system_update_dns + * + * Make glibc/nscd aware of any changes to the resolv.conf file by + * restarting nscd. + * + */ +void nm_system_update_dns (void) +{ + /* I'm not running nscd */ +} + diff --git a/src/backends/NetworkManagerSuSE.c b/src/backends/NetworkManagerSuSE.c new file mode 100644 index 00000000..6c8ec1a5 --- /dev/null +++ b/src/backends/NetworkManagerSuSE.c @@ -0,0 +1,62 @@ +/* + * NetworkManager -- Network link manager + * + * Dan Williams <dcbw@redhat.com> + * Kay Sievers <kay.sievers@suse.de> + * Robert Love <rml@novell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2004 Red Hat, Inc. + * (C) Copyright 2005-2006 SuSE GmbH + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "NetworkManagerGeneric.h" +#include "nm-system.h" +#include "NetworkManagerUtils.h" +#include "nm-logging.h" + +/* + * nm_system_enable_loopback + * + * Bring up the loopback interface + * + */ +void nm_system_enable_loopback (void) +{ + nm_generic_enable_loopback (); +} + +/* + * nm_system_update_dns + * + * Invalidate the nscd host cache, if it exists, since + * we changed resolv.conf. + * + */ +void nm_system_update_dns (void) +{ + nm_log_info (LOGD_DNS, "Clearing nscd hosts cache."); + nm_spawn_process ("/usr/sbin/nscd -i hosts"); +} + diff --git a/src/bluez-manager/Makefile.am b/src/bluez-manager/Makefile.am new file mode 100644 index 00000000..6c199e8c --- /dev/null +++ b/src/bluez-manager/Makefile.am @@ -0,0 +1,38 @@ +INCLUDES = \ + -I${top_srcdir} \ + -I${top_srcdir}/include \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/logging \ + -I${top_builddir}/marshallers \ + -I$(top_srcdir)/src/nm-bluez-manager + +noinst_LTLIBRARIES = libbluez-manager.la + +libbluez_manager_la_SOURCES = \ + nm-bluez-manager.c \ + nm-bluez-manager.h \ + nm-bluez-adapter.c \ + nm-bluez-adapter.h \ + nm-bluez-device.c \ + nm-bluez-device.h \ + nm-bluez-common.h + +libbluez_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(BLUEZ_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DBINDIR=\"$(bindir)\" \ + -DDATADIR=\"$(datadir)\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DLOCALSTATEDIR=\"$(localstatedir)\" + +libbluez_manager_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) \ + $(BLUEZ_LIBS) + diff --git a/src/bluez-manager/Makefile.in b/src/bluez-manager/Makefile.in new file mode 100644 index 00000000..037f9fc9 --- /dev/null +++ b/src/bluez-manager/Makefile.in @@ -0,0 +1,601 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/bluez-manager +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libbluez_manager_la_DEPENDENCIES = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libbluez_manager_la_OBJECTS = \ + libbluez_manager_la-nm-bluez-manager.lo \ + libbluez_manager_la-nm-bluez-adapter.lo \ + libbluez_manager_la-nm-bluez-device.lo +libbluez_manager_la_OBJECTS = $(am_libbluez_manager_la_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libbluez_manager_la_SOURCES) +DIST_SOURCES = $(libbluez_manager_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +IPTABLES_PATH = @IPTABLES_PATH@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBM = @LIBM@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POFILES = @POFILES@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +RANLIB = @RANLIB@ +RESOLVCONF_PATH = @RESOLVCONF_PATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = \ + -I${top_srcdir} \ + -I${top_srcdir}/include \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/logging \ + -I${top_builddir}/marshallers \ + -I$(top_srcdir)/src/nm-bluez-manager + +noinst_LTLIBRARIES = libbluez-manager.la +libbluez_manager_la_SOURCES = \ + nm-bluez-manager.c \ + nm-bluez-manager.h \ + nm-bluez-adapter.c \ + nm-bluez-adapter.h \ + nm-bluez-device.c \ + nm-bluez-device.h \ + nm-bluez-common.h + +libbluez_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(BLUEZ_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DBINDIR=\"$(bindir)\" \ + -DDATADIR=\"$(datadir)\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DLOCALSTATEDIR=\"$(localstatedir)\" + +libbluez_manager_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) \ + $(BLUEZ_LIBS) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/bluez-manager/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/bluez-manager/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libbluez-manager.la: $(libbluez_manager_la_OBJECTS) $(libbluez_manager_la_DEPENDENCIES) + $(LINK) $(libbluez_manager_la_OBJECTS) $(libbluez_manager_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libbluez_manager_la-nm-bluez-adapter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libbluez_manager_la-nm-bluez-device.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libbluez_manager_la-nm-bluez-manager.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libbluez_manager_la-nm-bluez-manager.lo: nm-bluez-manager.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbluez_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libbluez_manager_la-nm-bluez-manager.lo -MD -MP -MF $(DEPDIR)/libbluez_manager_la-nm-bluez-manager.Tpo -c -o libbluez_manager_la-nm-bluez-manager.lo `test -f 'nm-bluez-manager.c' || echo '$(srcdir)/'`nm-bluez-manager.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libbluez_manager_la-nm-bluez-manager.Tpo $(DEPDIR)/libbluez_manager_la-nm-bluez-manager.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-bluez-manager.c' object='libbluez_manager_la-nm-bluez-manager.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbluez_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libbluez_manager_la-nm-bluez-manager.lo `test -f 'nm-bluez-manager.c' || echo '$(srcdir)/'`nm-bluez-manager.c + +libbluez_manager_la-nm-bluez-adapter.lo: nm-bluez-adapter.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbluez_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libbluez_manager_la-nm-bluez-adapter.lo -MD -MP -MF $(DEPDIR)/libbluez_manager_la-nm-bluez-adapter.Tpo -c -o libbluez_manager_la-nm-bluez-adapter.lo `test -f 'nm-bluez-adapter.c' || echo '$(srcdir)/'`nm-bluez-adapter.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libbluez_manager_la-nm-bluez-adapter.Tpo $(DEPDIR)/libbluez_manager_la-nm-bluez-adapter.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-bluez-adapter.c' object='libbluez_manager_la-nm-bluez-adapter.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbluez_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libbluez_manager_la-nm-bluez-adapter.lo `test -f 'nm-bluez-adapter.c' || echo '$(srcdir)/'`nm-bluez-adapter.c + +libbluez_manager_la-nm-bluez-device.lo: nm-bluez-device.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbluez_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libbluez_manager_la-nm-bluez-device.lo -MD -MP -MF $(DEPDIR)/libbluez_manager_la-nm-bluez-device.Tpo -c -o libbluez_manager_la-nm-bluez-device.lo `test -f 'nm-bluez-device.c' || echo '$(srcdir)/'`nm-bluez-device.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libbluez_manager_la-nm-bluez-device.Tpo $(DEPDIR)/libbluez_manager_la-nm-bluez-device.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-bluez-device.c' object='libbluez_manager_la-nm-bluez-device.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbluez_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libbluez_manager_la-nm-bluez-device.lo `test -f 'nm-bluez-device.c' || echo '$(srcdir)/'`nm-bluez-device.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/bluez-manager/nm-bluez-adapter.c b/src/bluez-manager/nm-bluez-adapter.c new file mode 100644 index 00000000..27a85e78 --- /dev/null +++ b/src/bluez-manager/nm-bluez-adapter.c @@ -0,0 +1,367 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#include <glib.h> +#include <string.h> + +#include "NetworkManager.h" +#include "nm-dbus-manager.h" +#include "nm-bluez-adapter.h" +#include "nm-bluez-device.h" +#include "nm-bluez-common.h" +#include "nm-dbus-glib-types.h" +#include "nm-logging.h" + + +G_DEFINE_TYPE (NMBluezAdapter, nm_bluez_adapter, G_TYPE_OBJECT) + +#define NM_BLUEZ_ADAPTER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_BLUEZ_ADAPTER, NMBluezAdapterPrivate)) + +typedef struct { + char *path; + DBusGProxy *proxy; + gboolean initialized; + + char *address; + GHashTable *devices; +} NMBluezAdapterPrivate; + + +enum { + PROP_0, + PROP_PATH, + PROP_ADDRESS, + + LAST_PROP +}; + +/* Signals */ +enum { + INITIALIZED, + DEVICE_ADDED, + DEVICE_REMOVED, + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + +const char * +nm_bluez_adapter_get_path (NMBluezAdapter *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_ADAPTER (self), NULL); + + return NM_BLUEZ_ADAPTER_GET_PRIVATE (self)->path; +} + +const char * +nm_bluez_adapter_get_address (NMBluezAdapter *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_ADAPTER (self), NULL); + + return NM_BLUEZ_ADAPTER_GET_PRIVATE (self)->address; +} + +gboolean +nm_bluez_adapter_get_initialized (NMBluezAdapter *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_ADAPTER (self), FALSE); + + return NM_BLUEZ_ADAPTER_GET_PRIVATE (self)->initialized; +} + +static void +devices_to_list (gpointer key, gpointer data, gpointer user_data) +{ + NMBluezDevice *device = NM_BLUEZ_DEVICE (data); + GSList **list = user_data; + + if (nm_bluez_device_get_usable (device)) + *list = g_slist_append (*list, data); +} + +GSList * +nm_bluez_adapter_get_devices (NMBluezAdapter *self) +{ + NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self); + GSList *devices = NULL; + + g_hash_table_foreach (priv->devices, devices_to_list, &devices); + return devices; +} + +static void +device_usable (NMBluezDevice *device, GParamSpec *pspec, gpointer user_data) +{ + NMBluezAdapter *self = NM_BLUEZ_ADAPTER (user_data); + NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self); + + if (nm_bluez_device_get_usable (device)) + g_signal_emit (self, signals[DEVICE_ADDED], 0, device); + else { + g_object_ref (device); + g_hash_table_remove (priv->devices, nm_bluez_device_get_path (device)); + g_signal_emit (self, signals[DEVICE_REMOVED], 0, device); + g_object_unref (device); + } +} + +static void +device_initialized (NMBluezDevice *device, gboolean success, gpointer user_data) +{ + NMBluezAdapter *self = NM_BLUEZ_ADAPTER (user_data); + NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self); + + if (!success) + g_hash_table_remove (priv->devices, nm_bluez_device_get_path (device)); +} + +static void +device_created (DBusGProxy *proxy, const char *path, gpointer user_data) +{ + NMBluezAdapter *self = NM_BLUEZ_ADAPTER (user_data); + NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self); + NMBluezDevice *device; + + device = nm_bluez_device_new (path); + g_signal_connect (device, "initialized", G_CALLBACK (device_initialized), self); + g_signal_connect (device, "notify::usable", G_CALLBACK (device_usable), self); + g_hash_table_insert (priv->devices, g_strdup (path), device); +} + +static void +device_removed (DBusGProxy *proxy, const char *path, gpointer user_data) +{ + NMBluezAdapter *self = NM_BLUEZ_ADAPTER (user_data); + NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self); + NMBluezDevice *device; + + device = g_hash_table_lookup (priv->devices, path); + if (device) { + g_object_ref (device); + g_hash_table_remove (priv->devices, path); + g_signal_emit (self, signals[DEVICE_REMOVED], 0, device); + g_object_unref (device); + } +} + + +static void +get_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + NMBluezAdapter *self = NM_BLUEZ_ADAPTER (user_data); + NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self); + GHashTable *properties = NULL; + GError *err = NULL; + GValue *value; + GPtrArray *devices; + int i; + + if (!dbus_g_proxy_end_call (proxy, call, &err, + DBUS_TYPE_G_MAP_OF_VARIANT, &properties, + G_TYPE_INVALID)) { + nm_log_warn (LOGD_BT, "bluez error getting adapter properties: %s", + err && err->message ? err->message : "(unknown)"); + g_error_free (err); + goto done; + } + + value = g_hash_table_lookup (properties, "Address"); + priv->address = value ? g_value_dup_string (value) : NULL; + + value = g_hash_table_lookup (properties, "Devices"); + devices = value ? g_value_get_boxed (value) : NULL; + + for (i = 0; devices && i < devices->len; i++) + device_created (priv->proxy, g_ptr_array_index (devices, i), self); + + g_hash_table_unref (properties); + + priv->initialized = TRUE; + +done: + g_signal_emit (self, signals[INITIALIZED], 0, priv->initialized); +} + +static void +query_properties (NMBluezAdapter *self) +{ + NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self); + DBusGProxyCall *call; + + call = dbus_g_proxy_begin_call (priv->proxy, "GetProperties", + get_properties_cb, + self, + NULL, G_TYPE_INVALID); + if (!call) { + nm_log_warn (LOGD_BT, "failed to request Bluetooth adapter properties for %s.", + priv->path); + } +} + +NMBluezAdapter * +nm_bluez_adapter_new (const char *path) +{ + NMBluezAdapter *self; + NMBluezAdapterPrivate *priv; + NMDBusManager *dbus_mgr; + DBusGConnection *connection; + + self = (NMBluezAdapter *) g_object_new (NM_TYPE_BLUEZ_ADAPTER, + NM_BLUEZ_ADAPTER_PATH, path, + NULL); + if (!self) + return NULL; + + priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self); + dbus_mgr = nm_dbus_manager_get (); + connection = nm_dbus_manager_get_connection (dbus_mgr); + + priv->proxy = dbus_g_proxy_new_for_name (connection, + BLUEZ_SERVICE, + priv->path, + BLUEZ_ADAPTER_INTERFACE); + g_object_unref (dbus_mgr); + + dbus_g_proxy_add_signal (priv->proxy, "DeviceCreated", + DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "DeviceCreated", + G_CALLBACK (device_created), self, NULL); + + dbus_g_proxy_add_signal (priv->proxy, "DeviceRemoved", + DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "DeviceRemoved", + G_CALLBACK (device_removed), self, NULL); + + query_properties (self); + return self; +} + +static void +nm_bluez_adapter_init (NMBluezAdapter *self) +{ + NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self); + + priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_object_unref); +} + +static void +finalize (GObject *object) +{ + NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (object); + + g_hash_table_destroy (priv->devices); + g_free (priv->address); + g_free (priv->path); + g_object_unref (priv->proxy); + + G_OBJECT_CLASS (nm_bluez_adapter_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PATH: + g_value_set_string (value, priv->path); + break; + case PROP_ADDRESS: + g_value_set_string (value, priv->address); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PATH: + /* construct only */ + priv->path = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_bluez_adapter_class_init (NMBluezAdapterClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (config_class); + + g_type_class_add_private (config_class, sizeof (NMBluezAdapterPrivate)); + + /* virtual methods */ + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_PATH, + g_param_spec_string (NM_BLUEZ_ADAPTER_PATH, + "DBus Path", + "DBus Path", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_ADDRESS, + g_param_spec_string (NM_BLUEZ_ADAPTER_ADDRESS, + "Address", + "Address", + NULL, + G_PARAM_READABLE)); + + /* Signals */ + signals[INITIALIZED] = g_signal_new ("initialized", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMBluezAdapterClass, initialized), + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); + + signals[DEVICE_ADDED] = g_signal_new ("device-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMBluezAdapterClass, device_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + + signals[DEVICE_REMOVED] = g_signal_new ("device-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMBluezAdapterClass, device_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); +} + diff --git a/src/bluez-manager/nm-bluez-adapter.h b/src/bluez-manager/nm-bluez-adapter.h new file mode 100644 index 00000000..c552e989 --- /dev/null +++ b/src/bluez-manager/nm-bluez-adapter.h @@ -0,0 +1,67 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef NM_BLUEZ_ADAPTER_H +#define NM_BLUEZ_ADAPTER_H + +#include <glib.h> +#include <glib-object.h> + +#include "nm-bluez-device.h" + +#define NM_TYPE_BLUEZ_ADAPTER (nm_bluez_adapter_get_type ()) +#define NM_BLUEZ_ADAPTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ_ADAPTER, NMBluezAdapter)) +#define NM_BLUEZ_ADAPTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ_ADAPTER, NMBluezAdapterClass)) +#define NM_IS_BLUEZ_ADAPTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ_ADAPTER)) +#define NM_IS_BLUEZ_ADAPTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_BLUEZ_ADAPTER)) +#define NM_BLUEZ_ADAPTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ_ADAPTER, NMBluezAdapterClass)) + +#define NM_BLUEZ_ADAPTER_PATH "path" +#define NM_BLUEZ_ADAPTER_ADDRESS "address" + +typedef struct { + GObject parent; +} NMBluezAdapter; + +typedef struct { + GObjectClass parent; + + /* virtual functions */ + void (*initialized) (NMBluezAdapter *self, gboolean success); + + void (*device_added) (NMBluezAdapter *self, NMBluezDevice *device); + + void (*device_removed) (NMBluezAdapter *self, NMBluezDevice *device); +} NMBluezAdapterClass; + +GType nm_bluez_adapter_get_type (void); + +NMBluezAdapter *nm_bluez_adapter_new (const char *path); + +const char *nm_bluez_adapter_get_path (NMBluezAdapter *self); + +const char *nm_bluez_adapter_get_address (NMBluezAdapter *self); + +gboolean nm_bluez_adapter_get_initialized (NMBluezAdapter *self); + +GSList *nm_bluez_adapter_get_devices (NMBluezAdapter *self); + +#endif /* NM_BLUEZ_ADAPTER_H */ + diff --git a/src/bluez-manager/nm-bluez-common.h b/src/bluez-manager/nm-bluez-common.h new file mode 100644 index 00000000..61296255 --- /dev/null +++ b/src/bluez-manager/nm-bluez-common.h @@ -0,0 +1,34 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef NM_BLUEZ_COMMON_H +#define NM_BLUEZ_COMMON_H + +#define BLUEZ_SERVICE "org.bluez" + +#define BLUEZ_MANAGER_PATH "/" +#define BLUEZ_MANAGER_INTERFACE "org.bluez.Manager" +#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter" +#define BLUEZ_DEVICE_INTERFACE "org.bluez.Device" +#define BLUEZ_SERIAL_INTERFACE "org.bluez.Serial" +#define BLUEZ_NETWORK_INTERFACE "org.bluez.Network" + +#endif /* NM_BLUEZ_COMMON_H */ + diff --git a/src/bluez-manager/nm-bluez-device.c b/src/bluez-manager/nm-bluez-device.c new file mode 100644 index 00000000..72acaa69 --- /dev/null +++ b/src/bluez-manager/nm-bluez-device.c @@ -0,0 +1,452 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#include <glib.h> +#include <string.h> + +#include "NetworkManager.h" +#include "nm-dbus-manager.h" +#include "nm-bluez-device.h" +#include "nm-bluez-common.h" +#include "nm-dbus-glib-types.h" +#include "nm-logging.h" +#include "nm-marshal.h" + + +G_DEFINE_TYPE (NMBluezDevice, nm_bluez_device, G_TYPE_OBJECT) + +#define NM_BLUEZ_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_BLUEZ_DEVICE, NMBluezDevicePrivate)) + +typedef struct { + char *path; + DBusGProxy *proxy; + gboolean initialized; + gboolean usable; + + char *address; + char *name; + guint32 capabilities; + gint rssi; +} NMBluezDevicePrivate; + + +enum { + PROP_0, + PROP_PATH, + PROP_ADDRESS, + PROP_NAME, + PROP_CAPABILITIES, + PROP_RSSI, + PROP_USABLE, + + LAST_PROP +}; + +/* Signals */ +enum { + INITIALIZED, + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + +const char * +nm_bluez_device_get_path (NMBluezDevice *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL); + + return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->path; +} + +const char * +nm_bluez_device_get_address (NMBluezDevice *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL); + + return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->address; +} + +gboolean +nm_bluez_device_get_initialized (NMBluezDevice *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE); + + return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->initialized; +} + +gboolean +nm_bluez_device_get_usable (NMBluezDevice *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE); + + return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->usable; +} + +const char * +nm_bluez_device_get_name (NMBluezDevice *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL); + + return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->name; +} + +guint32 +nm_bluez_device_get_capabilities (NMBluezDevice *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), 0); + + return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->capabilities; +} + +gint +nm_bluez_device_get_rssi (NMBluezDevice *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), 0); + + return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->rssi; +} + +static guint32 +convert_uuids_to_capabilities (const char **strings) +{ + const char **iter; + guint32 capabilities = 0; + + for (iter = strings; iter && *iter; iter++) { + char **parts; + guint uuid16; + + parts = g_strsplit (*iter, "-", -1); + if (parts == NULL || parts[0] == NULL) { + g_strfreev (parts); + continue; + } + + uuid16 = g_ascii_strtoull (parts[0], NULL, 16); + g_strfreev (parts); + + switch (uuid16) { + case 0x1103: + capabilities |= NM_BT_CAPABILITY_DUN; + break; + case 0x1116: + capabilities |= NM_BT_CAPABILITY_NAP; + break; + default: + break; + } + } + + return capabilities; +} + +static void +check_emit_usable (NMBluezDevice *self) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + + if ( priv->initialized + && priv->capabilities + && priv->name + && priv->address) { + if (!priv->usable) { + priv->usable = TRUE; + g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_USABLE); + } + } else { + if (priv->usable) { + priv->usable = FALSE; + g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_USABLE); + } + } +} + +static void +property_changed (DBusGProxy *proxy, + const char *property, + GValue *value, + gpointer user_data) +{ + NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data); + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + const char *str; + guint32 uint_val; + gint int_val; + + if (!strcmp (property, "Name")) { + str = g_value_get_string (value); + if ( (!priv->name && str) + || (priv->name && !str) + || (priv->name && str && strcmp (priv->name, str))) { + g_free (priv->name); + priv->name = str ? g_strdup (str) : NULL; + g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_NAME); + } + } else if (!strcmp (property, "RSSI")) { + int_val = g_value_get_int (value); + if (priv->rssi != int_val) { + priv->rssi = int_val; + g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_RSSI); + } + } else if (!strcmp (property, "UUIDs")) { + uint_val = convert_uuids_to_capabilities ((const char **) g_value_get_boxed (value)); + if (priv->capabilities != uint_val) { + priv->capabilities = uint_val; + g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_CAPABILITIES); + } + } + + check_emit_usable (self); +} + +static void +get_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data); + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + GHashTable *properties = NULL; + GError *err = NULL; + GValue *value; + const char **uuids; + + if (!dbus_g_proxy_end_call (proxy, call, &err, + DBUS_TYPE_G_MAP_OF_VARIANT, &properties, + G_TYPE_INVALID)) { + nm_log_warn (LOGD_BT, "bluez error getting device properties: %s", + err && err->message ? err->message : "(unknown)"); + g_error_free (err); + g_signal_emit (self, signals[INITIALIZED], 0, FALSE); + return; + } + + value = g_hash_table_lookup (properties, "Address"); + priv->address = value ? g_value_dup_string (value) : NULL; + + value = g_hash_table_lookup (properties, "Name"); + priv->name = value ? g_value_dup_string (value) : NULL; + + value = g_hash_table_lookup (properties, "RSSI"); + priv->rssi = value ? g_value_get_int (value) : 0; + + value = g_hash_table_lookup (properties, "UUIDs"); + if (value) { + uuids = (const char **) g_value_get_boxed (value); + priv->capabilities = convert_uuids_to_capabilities (uuids); + } else + priv->capabilities = NM_BT_CAPABILITY_NONE; + + g_hash_table_unref (properties); + + priv->initialized = TRUE; + g_signal_emit (self, signals[INITIALIZED], 0, TRUE); + + check_emit_usable (self); +} + +static void +query_properties (NMBluezDevice *self) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + DBusGProxyCall *call; + + call = dbus_g_proxy_begin_call (priv->proxy, "GetProperties", + get_properties_cb, + self, + NULL, G_TYPE_INVALID); + if (!call) { + nm_log_warn (LOGD_BT, "failed to request Bluetooth device properties for %s.", + priv->path); + } +} + +NMBluezDevice * +nm_bluez_device_new (const char *path) +{ + NMBluezDevice *self; + NMBluezDevicePrivate *priv; + NMDBusManager *dbus_mgr; + DBusGConnection *connection; + + + self = (NMBluezDevice *) g_object_new (NM_TYPE_BLUEZ_DEVICE, + NM_BLUEZ_DEVICE_PATH, path, + NULL); + if (!self) + return NULL; + + priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + dbus_mgr = nm_dbus_manager_get (); + connection = nm_dbus_manager_get_connection (dbus_mgr); + + priv->proxy = dbus_g_proxy_new_for_name (connection, + BLUEZ_SERVICE, + priv->path, + BLUEZ_DEVICE_INTERFACE); + g_object_unref (dbus_mgr); + + dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_BOXED, + G_TYPE_NONE, + G_TYPE_STRING, G_TYPE_VALUE, + G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->proxy, "PropertyChanged", + G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "PropertyChanged", + G_CALLBACK (property_changed), self, NULL); + + query_properties (self); + return self; +} + +static void +nm_bluez_device_init (NMBluezDevice *self) +{ +} + +static void +finalize (GObject *object) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (object); + + g_free (priv->path); + g_free (priv->address); + g_free (priv->name); + g_object_unref (priv->proxy); + + G_OBJECT_CLASS (nm_bluez_device_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PATH: + g_value_set_string (value, priv->path); + break; + case PROP_ADDRESS: + g_value_set_string (value, priv->address); + break; + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + case PROP_CAPABILITIES: + g_value_set_uint (value, priv->capabilities); + break; + case PROP_RSSI: + g_value_set_int (value, priv->rssi); + break; + case PROP_USABLE: + g_value_set_boolean (value, priv->usable); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PATH: + /* construct only */ + priv->path = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_bluez_device_class_init (NMBluezDeviceClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (config_class); + + g_type_class_add_private (config_class, sizeof (NMBluezDevicePrivate)); + + /* virtual methods */ + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_PATH, + g_param_spec_string (NM_BLUEZ_DEVICE_PATH, + "DBus Path", + "DBus Path", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_ADDRESS, + g_param_spec_string (NM_BLUEZ_DEVICE_ADDRESS, + "Address", + "Address", + NULL, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_NAME, + g_param_spec_string (NM_BLUEZ_DEVICE_NAME, + "Name", + "Name", + NULL, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_CAPABILITIES, + g_param_spec_uint (NM_BLUEZ_DEVICE_CAPABILITIES, + "Capabilities", + "Capabilities", + 0, G_MAXUINT, 0, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_RSSI, + g_param_spec_int (NM_BLUEZ_DEVICE_RSSI, + "RSSI", + "RSSI", + G_MININT, G_MAXINT, 0, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_USABLE, + g_param_spec_boolean (NM_BLUEZ_DEVICE_USABLE, + "Usable", + "Usable", + FALSE, + G_PARAM_READABLE)); + + /* Signals */ + signals[INITIALIZED] = g_signal_new ("initialized", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMBluezDeviceClass, initialized), + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); +} + diff --git a/src/bluez-manager/nm-bluez-device.h b/src/bluez-manager/nm-bluez-device.h new file mode 100644 index 00000000..8a6b3597 --- /dev/null +++ b/src/bluez-manager/nm-bluez-device.h @@ -0,0 +1,75 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef NM_BLUEZ_DEVICE_H +#define NM_BLUEZ_DEVICE_H + +#include <glib.h> +#include <glib-object.h> + +#define NM_TYPE_BLUEZ_DEVICE (nm_bluez_device_get_type ()) +#define NM_BLUEZ_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ_DEVICE, NMBluezDevice)) +#define NM_BLUEZ_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ_DEVICE, NMBluezDeviceClass)) +#define NM_IS_BLUEZ_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ_DEVICE)) +#define NM_IS_BLUEZ_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_BLUEZ_DEVICE)) +#define NM_BLUEZ_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ_DEVICE, NMBluezDeviceClass)) + +#define NM_BLUEZ_DEVICE_PATH "path" +#define NM_BLUEZ_DEVICE_ADDRESS "address" +#define NM_BLUEZ_DEVICE_NAME "name" +#define NM_BLUEZ_DEVICE_CAPABILITIES "capabilities" +#define NM_BLUEZ_DEVICE_RSSI "rssi" +#define NM_BLUEZ_DEVICE_USABLE "usable" + +typedef struct { + GObject parent; +} NMBluezDevice; + +typedef struct { + GObjectClass parent; + + /* virtual functions */ + void (*initialized) (NMBluezDevice *self, gboolean success); + + void (*invalid) (NMBluezDevice *self); +} NMBluezDeviceClass; + +GType nm_bluez_device_get_type (void); + +NMBluezDevice *nm_bluez_device_new (const char *path); + +const char *nm_bluez_device_get_path (NMBluezDevice *self); + +gboolean nm_bluez_device_get_initialized (NMBluezDevice *self); + +gboolean nm_bluez_device_get_usable (NMBluezDevice *self); + +const char *nm_bluez_device_get_address (NMBluezDevice *self); + +const char *nm_bluez_device_get_name (NMBluezDevice *self); + +guint32 nm_bluez_device_get_class (NMBluezDevice *self); + +guint32 nm_bluez_device_get_capabilities (NMBluezDevice *self); + +gint nm_bluez_device_get_rssi (NMBluezDevice *self); + +#endif /* NM_BLUEZ_DEVICE_H */ + diff --git a/src/bluez-manager/nm-bluez-manager.c b/src/bluez-manager/nm-bluez-manager.c new file mode 100644 index 00000000..fe027c4f --- /dev/null +++ b/src/bluez-manager/nm-bluez-manager.c @@ -0,0 +1,369 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2010 Red Hat, Inc. + */ + +#include <signal.h> +#include <string.h> +#include <stdlib.h> +#include <dbus/dbus-glib.h> + +#include "nm-logging.h" +#include "nm-dbus-glib-types.h" +#include "nm-marshal.h" +#include "nm-bluez-manager.h" +#include "nm-bluez-adapter.h" +#include "nm-dbus-manager.h" +#include "nm-bluez-common.h" + + +typedef struct { + NMDBusManager *dbus_mgr; + gulong name_owner_changed_id; + + DBusGProxy *proxy; + + NMBluezAdapter *adapter; +} NMBluezManagerPrivate; + +#define NM_BLUEZ_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerPrivate)) + +G_DEFINE_TYPE (NMBluezManager, nm_bluez_manager, G_TYPE_OBJECT) + +enum { + BDADDR_ADDED, + BDADDR_REMOVED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void + +emit_bdaddr_added (NMBluezManager *self, NMBluezDevice *device) +{ + g_signal_emit (self, signals[BDADDR_ADDED], 0, + nm_bluez_device_get_address (device), + nm_bluez_device_get_name (device), + nm_bluez_device_get_path (device), + nm_bluez_device_get_capabilities (device)); +} + +void +nm_bluez_manager_query_devices (NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + GSList *devices, *iter; + + if (!priv->adapter) + return; + + devices = nm_bluez_adapter_get_devices (priv->adapter); + for (iter = devices; iter; iter = g_slist_next (iter)) + emit_bdaddr_added (self, NM_BLUEZ_DEVICE (iter->data)); + g_slist_free (devices); +} + +static void +device_added (NMBluezAdapter *adapter, NMBluezDevice *device, gpointer user_data) +{ + emit_bdaddr_added (NM_BLUEZ_MANAGER (user_data), device); +} + +static void +device_removed (NMBluezAdapter *adapter, NMBluezDevice *device, gpointer user_data) +{ + NMBluezManager *self = NM_BLUEZ_MANAGER (user_data); + + g_signal_emit (self, signals[BDADDR_REMOVED], 0, + nm_bluez_device_get_address (device), + nm_bluez_device_get_path (device)); +} + +static void +adapter_initialized (NMBluezAdapter *adapter, gboolean success, gpointer user_data) +{ + NMBluezManager *self = NM_BLUEZ_MANAGER (user_data); + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + + if (success) { + GSList *devices, *iter; + + devices = nm_bluez_adapter_get_devices (adapter); + for (iter = devices; iter; iter = g_slist_next (iter)) + emit_bdaddr_added (self, NM_BLUEZ_DEVICE (iter->data)); + g_slist_free (devices); + + g_signal_connect (adapter, "device-added", G_CALLBACK (device_added), self); + g_signal_connect (adapter, "device-removed", G_CALLBACK (device_removed), self); + } else { + g_object_unref (priv->adapter); + priv->adapter = NULL; + } +} + +static void +adapter_removed (DBusGProxy *proxy, const char *path, NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + + if (priv->adapter && !strcmp (path, nm_bluez_adapter_get_path (priv->adapter))) { + if (nm_bluez_adapter_get_initialized (priv->adapter)) { + GSList *devices, *iter; + + devices = nm_bluez_adapter_get_devices (priv->adapter); + for (iter = devices; iter; iter = g_slist_next (iter)) { + NMBluezDevice *device = NM_BLUEZ_DEVICE (iter->data); + + g_signal_emit (self, signals[BDADDR_REMOVED], 0, + nm_bluez_device_get_address (device), + nm_bluez_device_get_path (device)); + } + g_slist_free (devices); + } + + g_object_unref (priv->adapter); + priv->adapter = NULL; + } +} + +static void +default_adapter_changed (DBusGProxy *proxy, const char *path, NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + const char *cur_path = NULL; + + if (priv->adapter) + cur_path = nm_bluez_adapter_get_path (priv->adapter); + + if (cur_path) { + if (!path || strcmp (path, cur_path)) { + /* Default adapter changed */ + adapter_removed (priv->proxy, cur_path, self); + } else { + /* This adapter is already the default */ + return; + } + } + + /* Add the new default adapter */ + if (path) { + priv->adapter = nm_bluez_adapter_new (path); + g_signal_connect (priv->adapter, "initialized", G_CALLBACK (adapter_initialized), self); + } +} + +static void +default_adapter_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + NMBluezManager *self = NM_BLUEZ_MANAGER (user_data); + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + const char *default_adapter = NULL; + GError *err = NULL; + + if (!dbus_g_proxy_end_call (proxy, call, &err, + DBUS_TYPE_G_OBJECT_PATH, &default_adapter, + G_TYPE_INVALID)) { + nm_log_warn (LOGD_BT, "bluez error getting default adapter: %s", + err && err->message ? err->message : "(unknown)"); + g_error_free (err); + return; + } + + default_adapter_changed (priv->proxy, default_adapter, self); +} + +static void +query_default_adapter (NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + DBusGProxyCall *call; + + call = dbus_g_proxy_begin_call (priv->proxy, "DefaultAdapter", + default_adapter_cb, + self, + NULL, G_TYPE_INVALID); + if (!call) + nm_log_warn (LOGD_BT, "failed to request default Bluetooth adapter."); +} + +static void +bluez_connect (NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + DBusGConnection *connection; + + g_return_if_fail (priv->proxy == NULL); + + connection = nm_dbus_manager_get_connection (priv->dbus_mgr); + if (!connection) + return; + + priv->proxy = dbus_g_proxy_new_for_name (connection, + BLUEZ_SERVICE, + BLUEZ_MANAGER_PATH, + BLUEZ_MANAGER_INTERFACE); + + dbus_g_proxy_add_signal (priv->proxy, "AdapterRemoved", + DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "AdapterRemoved", + G_CALLBACK (adapter_removed), self, NULL); + + dbus_g_proxy_add_signal (priv->proxy, "DefaultAdapterChanged", + DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "DefaultAdapterChanged", + G_CALLBACK (default_adapter_changed), self, NULL); + + query_default_adapter (self); +} + +static void +remove_all_devices (NMBluezManager *self, gboolean do_signal) +{ + /* FIXME: do something */ +} + +static void +name_owner_changed_cb (NMDBusManager *dbus_mgr, + const char *name, + const char *old_owner, + const char *new_owner, + gpointer user_data) +{ + NMBluezManager *self = NM_BLUEZ_MANAGER (user_data); + gboolean old_owner_good = (old_owner && strlen (old_owner)); + gboolean new_owner_good = (new_owner && strlen (new_owner)); + + /* Can't handle the signal if its not from the supplicant service */ + if (strcmp (BLUEZ_SERVICE, name)) + return; + + if (old_owner_good && !new_owner_good) + remove_all_devices (self, TRUE); +} + +static void +bluez_cleanup (NMBluezManager *self, gboolean do_signal) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + + if (priv->proxy) { + g_object_unref (priv->proxy); + priv->proxy = NULL; + } + + remove_all_devices (self, do_signal); +} + +static void +dbus_connection_changed_cb (NMDBusManager *dbus_mgr, + DBusGConnection *connection, + gpointer user_data) +{ + NMBluezManager *self = NM_BLUEZ_MANAGER (user_data); + + if (!connection) + bluez_cleanup (self, TRUE); + else + bluez_connect (self); +} + + +NMBluezManager * +nm_bluez_manager_new (void) +{ + return NM_BLUEZ_MANAGER (g_object_new (NM_TYPE_BLUEZ_MANAGER, NULL)); +} + +NMBluezManager * +nm_bluez_manager_get (void) +{ + static NMBluezManager *singleton = NULL; + + if (!singleton) + singleton = nm_bluez_manager_new (); + else + g_object_ref (singleton); + + g_assert (singleton); + return singleton; +} + +static void +nm_bluez_manager_init (NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + + priv->dbus_mgr = nm_dbus_manager_get (); + g_assert (priv->dbus_mgr); + + g_signal_connect (priv->dbus_mgr, + "name-owner-changed", + G_CALLBACK (name_owner_changed_cb), + self); + + g_signal_connect (priv->dbus_mgr, + "dbus-connection-changed", + G_CALLBACK (dbus_connection_changed_cb), + self); + + bluez_connect (self); +} + +static void +finalize (GObject *object) +{ + NMBluezManager *self = NM_BLUEZ_MANAGER (object); + + bluez_cleanup (self, FALSE); + + G_OBJECT_CLASS (nm_bluez_manager_parent_class)->finalize (object); +} + +static void +nm_bluez_manager_class_init (NMBluezManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NMBluezManagerPrivate)); + + /* virtual methods */ + object_class->finalize = finalize; + + /* Signals */ + signals[BDADDR_ADDED] = + g_signal_new ("bdaddr-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMBluezManagerClass, bdaddr_added), + NULL, NULL, + _nm_marshal_VOID__STRING_STRING_STRING_UINT, + G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT); + + signals[BDADDR_REMOVED] = + g_signal_new ("bdaddr-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMBluezManagerClass, bdaddr_removed), + NULL, NULL, + _nm_marshal_VOID__STRING_STRING, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); +} + diff --git a/src/bluez-manager/nm-bluez-manager.h b/src/bluez-manager/nm-bluez-manager.h new file mode 100644 index 00000000..9cfab1b7 --- /dev/null +++ b/src/bluez-manager/nm-bluez-manager.h @@ -0,0 +1,63 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2009 Red Hat, Inc. + */ + +#ifndef NM_BLUEZ_MANAGER_H +#define NM_BLUEZ_MANAGER_H + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define NM_TYPE_BLUEZ_MANAGER (nm_bluez_manager_get_type ()) +#define NM_BLUEZ_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ_MANAGER, NMBluezManager)) +#define NM_BLUEZ_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerClass)) +#define NM_IS_BLUEZ_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ_MANAGER)) +#define NM_IS_BLUEZ_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_BLUEZ_MANAGER)) +#define NM_BLUEZ_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerClass)) + +typedef struct { + GObject parent; +} NMBluezManager; + +typedef struct { + GObjectClass parent; + + /* Virtual functions */ + void (*bdaddr_added) (NMBluezManager *manager, + const char *bdaddr, + const char *name, + const char *object_path, + guint uuids); + + void (*bdaddr_removed) (NMBluezManager *manager, + const char *bdaddr, + const char *object_path); +} NMBluezManagerClass; + +GType nm_bluez_manager_get_type (void); + +NMBluezManager *nm_bluez_manager_new (void); +NMBluezManager *nm_bluez_manager_get (void); +void nm_bluez_manager_query_devices (NMBluezManager *manager); + +#endif /* NM_BLUEZ_MANAGER_H */ + diff --git a/src/dhcp-manager/Makefile.am b/src/dhcp-manager/Makefile.am new file mode 100644 index 00000000..f75e6b3d --- /dev/null +++ b/src/dhcp-manager/Makefile.am @@ -0,0 +1,39 @@ +INCLUDES = \ + -I${top_srcdir} \ + -I${top_srcdir}/include \ + -I${top_builddir}/marshallers \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/named-manager + +noinst_LTLIBRARIES = libdhcp-manager.la + +libdhcp_manager_la_SOURCES = \ + nm-dhcp-client.c \ + nm-dhcp-client.h \ + nm-dhcp-manager.c \ + nm-dhcp-manager.h \ + nm-dhcp-dhclient.h \ + nm-dhcp-dhclient.c \ + nm-dhcp-dhcpcd.h \ + nm-dhcp-dhcpcd.c + +libdhcp_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DBINDIR=\"$(bindir)\" \ + -DDATADIR=\"$(datadir)\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DLOCALSTATEDIR=\"$(localstatedir)\" \ + -DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \ + -DDHCPCD_PATH=\"$(DHCPCD_PATH)\" + +libdhcp_manager_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + diff --git a/src/dhcp-manager/Makefile.in b/src/dhcp-manager/Makefile.in new file mode 100644 index 00000000..07b9be9d --- /dev/null +++ b/src/dhcp-manager/Makefile.in @@ -0,0 +1,610 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/dhcp-manager +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libdhcp_manager_la_DEPENDENCIES = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libdhcp_manager_la_OBJECTS = libdhcp_manager_la-nm-dhcp-client.lo \ + libdhcp_manager_la-nm-dhcp-manager.lo \ + libdhcp_manager_la-nm-dhcp-dhclient.lo \ + libdhcp_manager_la-nm-dhcp-dhcpcd.lo +libdhcp_manager_la_OBJECTS = $(am_libdhcp_manager_la_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libdhcp_manager_la_SOURCES) +DIST_SOURCES = $(libdhcp_manager_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +IPTABLES_PATH = @IPTABLES_PATH@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBM = @LIBM@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POFILES = @POFILES@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +RANLIB = @RANLIB@ +RESOLVCONF_PATH = @RESOLVCONF_PATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = \ + -I${top_srcdir} \ + -I${top_srcdir}/include \ + -I${top_builddir}/marshallers \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/named-manager + +noinst_LTLIBRARIES = libdhcp-manager.la +libdhcp_manager_la_SOURCES = \ + nm-dhcp-client.c \ + nm-dhcp-client.h \ + nm-dhcp-manager.c \ + nm-dhcp-manager.h \ + nm-dhcp-dhclient.h \ + nm-dhcp-dhclient.c \ + nm-dhcp-dhcpcd.h \ + nm-dhcp-dhcpcd.c + +libdhcp_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DBINDIR=\"$(bindir)\" \ + -DDATADIR=\"$(datadir)\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DLOCALSTATEDIR=\"$(localstatedir)\" \ + -DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \ + -DDHCPCD_PATH=\"$(DHCPCD_PATH)\" + +libdhcp_manager_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/dhcp-manager/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/dhcp-manager/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libdhcp-manager.la: $(libdhcp_manager_la_OBJECTS) $(libdhcp_manager_la_DEPENDENCIES) + $(LINK) $(libdhcp_manager_la_OBJECTS) $(libdhcp_manager_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdhcp_manager_la-nm-dhcp-client.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdhcp_manager_la-nm-dhcp-dhclient.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdhcp_manager_la-nm-dhcp-dhcpcd.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdhcp_manager_la-nm-dhcp-manager.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libdhcp_manager_la-nm-dhcp-client.lo: nm-dhcp-client.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdhcp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdhcp_manager_la-nm-dhcp-client.lo -MD -MP -MF $(DEPDIR)/libdhcp_manager_la-nm-dhcp-client.Tpo -c -o libdhcp_manager_la-nm-dhcp-client.lo `test -f 'nm-dhcp-client.c' || echo '$(srcdir)/'`nm-dhcp-client.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libdhcp_manager_la-nm-dhcp-client.Tpo $(DEPDIR)/libdhcp_manager_la-nm-dhcp-client.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-dhcp-client.c' object='libdhcp_manager_la-nm-dhcp-client.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdhcp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdhcp_manager_la-nm-dhcp-client.lo `test -f 'nm-dhcp-client.c' || echo '$(srcdir)/'`nm-dhcp-client.c + +libdhcp_manager_la-nm-dhcp-manager.lo: nm-dhcp-manager.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdhcp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdhcp_manager_la-nm-dhcp-manager.lo -MD -MP -MF $(DEPDIR)/libdhcp_manager_la-nm-dhcp-manager.Tpo -c -o libdhcp_manager_la-nm-dhcp-manager.lo `test -f 'nm-dhcp-manager.c' || echo '$(srcdir)/'`nm-dhcp-manager.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libdhcp_manager_la-nm-dhcp-manager.Tpo $(DEPDIR)/libdhcp_manager_la-nm-dhcp-manager.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-dhcp-manager.c' object='libdhcp_manager_la-nm-dhcp-manager.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdhcp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdhcp_manager_la-nm-dhcp-manager.lo `test -f 'nm-dhcp-manager.c' || echo '$(srcdir)/'`nm-dhcp-manager.c + +libdhcp_manager_la-nm-dhcp-dhclient.lo: nm-dhcp-dhclient.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdhcp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdhcp_manager_la-nm-dhcp-dhclient.lo -MD -MP -MF $(DEPDIR)/libdhcp_manager_la-nm-dhcp-dhclient.Tpo -c -o libdhcp_manager_la-nm-dhcp-dhclient.lo `test -f 'nm-dhcp-dhclient.c' || echo '$(srcdir)/'`nm-dhcp-dhclient.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libdhcp_manager_la-nm-dhcp-dhclient.Tpo $(DEPDIR)/libdhcp_manager_la-nm-dhcp-dhclient.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-dhcp-dhclient.c' object='libdhcp_manager_la-nm-dhcp-dhclient.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdhcp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdhcp_manager_la-nm-dhcp-dhclient.lo `test -f 'nm-dhcp-dhclient.c' || echo '$(srcdir)/'`nm-dhcp-dhclient.c + +libdhcp_manager_la-nm-dhcp-dhcpcd.lo: nm-dhcp-dhcpcd.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdhcp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdhcp_manager_la-nm-dhcp-dhcpcd.lo -MD -MP -MF $(DEPDIR)/libdhcp_manager_la-nm-dhcp-dhcpcd.Tpo -c -o libdhcp_manager_la-nm-dhcp-dhcpcd.lo `test -f 'nm-dhcp-dhcpcd.c' || echo '$(srcdir)/'`nm-dhcp-dhcpcd.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libdhcp_manager_la-nm-dhcp-dhcpcd.Tpo $(DEPDIR)/libdhcp_manager_la-nm-dhcp-dhcpcd.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-dhcp-dhcpcd.c' object='libdhcp_manager_la-nm-dhcp-dhcpcd.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdhcp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdhcp_manager_la-nm-dhcp-dhcpcd.lo `test -f 'nm-dhcp-dhcpcd.c' || echo '$(srcdir)/'`nm-dhcp-dhcpcd.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c new file mode 100644 index 00000000..31a6f83c --- /dev/null +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -0,0 +1,1243 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + * + */ + +#include <glib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdlib.h> + +#include "nm-utils.h" +#include "nm-logging.h" +#include "nm-dbus-glib-types.h" +#include "nm-dhcp-client.h" + +typedef struct { + char * iface; + gboolean ipv6; + char * uuid; + guint32 timeout; + + guchar state; + GPid pid; + gboolean dead; + guint timeout_id; + guint watch_id; + guint32 remove_id; + GHashTable * options; + gboolean info_only; + +} NMDHCPClientPrivate; + +#define NM_DHCP_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_CLIENT, NMDHCPClientPrivate)) + +G_DEFINE_TYPE_EXTENDED (NMDHCPClient, nm_dhcp_client, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, {}) + +enum { + STATE_CHANGED, + TIMEOUT, + REMOVE, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum { + PROP_0, + PROP_IFACE, + PROP_IPV6, + PROP_UUID, + PROP_TIMEOUT, + LAST_PROP +}; + +/********************************************/ + +GPid +nm_dhcp_client_get_pid (NMDHCPClient *self) +{ + g_return_val_if_fail (self != NULL, -1); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), -1); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->pid; +} + +const char * +nm_dhcp_client_get_iface (NMDHCPClient *self) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->iface; +} + +gboolean +nm_dhcp_client_get_ipv6 (NMDHCPClient *self) +{ + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->ipv6; +} + +const char * +nm_dhcp_client_get_uuid (NMDHCPClient *self) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->uuid; +} + +/********************************************/ + +static void +timeout_cleanup (NMDHCPClient *self) +{ + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } +} + +static void +watch_cleanup (NMDHCPClient *self) +{ + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + if (priv->watch_id) { + g_source_remove (priv->watch_id); + priv->watch_id = 0; + } +} + +static void +stop_process (GPid pid, const char *iface) +{ + int i = 15; /* 3 seconds */ + + g_return_if_fail (pid > 0); + + /* Tell it to quit; maybe it wants to send out a RELEASE message */ + kill (pid, SIGTERM); + + while (i-- > 0) { + gint child_status; + int ret; + + ret = waitpid (pid, &child_status, WNOHANG); + if (ret > 0) + break; + + if (ret == -1) { + /* Child already exited */ + if (errno == ECHILD) + break; + /* Took too long; shoot it in the head */ + i = 0; + break; + } + g_usleep (G_USEC_PER_SEC / 5); + } + + if (i <= 0) { + if (iface) { + nm_log_warn (LOGD_DHCP, "(%s): DHCP client pid %d didn't exit, will kill it.", + iface, pid); + } + kill (pid, SIGKILL); + + nm_log_dbg (LOGD_DHCP, "waiting for DHCP client pid %d to exit", pid); + waitpid (pid, NULL, 0); + nm_log_dbg (LOGD_DHCP, "DHCP client pid %d cleaned up", pid); + } +} + +static void +real_stop (NMDHCPClient *self) +{ + NMDHCPClientPrivate *priv; + + g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_DHCP_CLIENT (self)); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + g_return_if_fail (priv->pid > 0); + + /* Clean up the watch handler since we're explicitly killing the daemon */ + watch_cleanup (self); + + stop_process (priv->pid, priv->iface); + + priv->info_only = FALSE; +} + +static gboolean +daemon_timeout (gpointer user_data) +{ + NMDHCPClient *self = NM_DHCP_CLIENT (user_data); + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + if (priv->ipv6) { + nm_log_warn (LOGD_DHCP6, "(%s): DHCPv6 request timed out.", priv->iface); + } else { + nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 request timed out.", priv->iface); + } + g_signal_emit (G_OBJECT (self), signals[TIMEOUT], 0); + return FALSE; +} + +static gboolean +signal_remove (gpointer user_data) +{ + NMDHCPClient *self = NM_DHCP_CLIENT (user_data); + + NM_DHCP_CLIENT_GET_PRIVATE (self)->remove_id = 0; + g_signal_emit (G_OBJECT (self), signals[REMOVE], 0); + return FALSE; +} + +static void +dhcp_client_set_state (NMDHCPClient *self, + NMDHCPState state, + gboolean emit_state, + gboolean remove_now) +{ + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + priv->state = state; + + if (emit_state) + g_signal_emit (G_OBJECT (self), signals[STATE_CHANGED], 0, priv->state); + + if (state == DHC_END || state == DHC_ABEND) { + /* Start the remove signal timer */ + if (remove_now) { + g_signal_emit (G_OBJECT (self), signals[REMOVE], 0); + } else { + if (!priv->remove_id) + priv->remove_id = g_timeout_add_seconds (5, signal_remove, self); + } + } +} + +static void +daemon_watch_cb (GPid pid, gint status, gpointer user_data) +{ + NMDHCPClient *self = NM_DHCP_CLIENT (user_data); + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + NMDHCPState new_state; + + if (priv->ipv6) { + nm_log_info (LOGD_DHCP6, "(%s): DHCPv6 client pid %d exited with status %d", + priv->iface, pid, + WIFEXITED (status) ? WEXITSTATUS (status) : -1); + } else { + nm_log_info (LOGD_DHCP6, "(%s): DHCPv4 client pid %d exited with status %d", + priv->iface, pid, + WIFEXITED (status) ? WEXITSTATUS (status) : -1); + } + + if (!WIFEXITED (status)) { + new_state = DHC_ABEND; + nm_log_warn (LOGD_DHCP, "DHCP client died abnormally"); + } else + new_state = DHC_END; + + watch_cleanup (self); + timeout_cleanup (self); + priv->dead = TRUE; + + dhcp_client_set_state (self, new_state, TRUE, FALSE); +} + +static void +start_monitor (NMDHCPClient *self) +{ + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + g_return_if_fail (priv->pid > 0); + + /* Set up a timeout on the transaction to kill it after the timeout */ + priv->timeout_id = g_timeout_add_seconds (priv->timeout, + daemon_timeout, + self); + priv->watch_id = g_child_watch_add (priv->pid, + (GChildWatchFunc) daemon_watch_cb, + self); +} + +gboolean +nm_dhcp_client_start_ip4 (NMDHCPClient *self, + NMSettingIP4Config *s_ip4, + guint8 *dhcp_anycast_addr) +{ + NMDHCPClientPrivate *priv; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + g_return_val_if_fail (priv->pid == -1, FALSE); + g_return_val_if_fail (priv->ipv6 == FALSE, FALSE); + g_return_val_if_fail (priv->uuid != NULL, FALSE); + + nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv4 transaction (timeout in %d seconds)", + priv->iface, priv->timeout); + + priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, s_ip4, dhcp_anycast_addr); + if (priv->pid) + start_monitor (self); + + return priv->pid ? TRUE : FALSE; +} + +gboolean +nm_dhcp_client_start_ip6 (NMDHCPClient *self, + NMSettingIP6Config *s_ip6, + guint8 *dhcp_anycast_addr, + gboolean info_only) +{ + NMDHCPClientPrivate *priv; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + g_return_val_if_fail (priv->pid == -1, FALSE); + g_return_val_if_fail (priv->ipv6 == TRUE, FALSE); + g_return_val_if_fail (priv->uuid != NULL, FALSE); + + priv->info_only = info_only; + + nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv6 transaction (timeout in %d seconds)", + priv->iface, priv->timeout); + + priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self, s_ip6, dhcp_anycast_addr, info_only); + if (priv->pid > 0) + start_monitor (self); + + return priv->pid ? TRUE : FALSE; +} + +void +nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name) +{ + char *pid_contents = NULL, *proc_contents = NULL, *proc_path = NULL; + long int tmp; + + /* Check for an existing instance and stop it */ + if (!g_file_get_contents (pid_file, &pid_contents, NULL, NULL)) + return; + + errno = 0; + tmp = strtol (pid_contents, NULL, 10); + if ((errno == 0) && (tmp > 1)) { + const char *exe; + + /* Ensure the process is a DHCP client */ + proc_path = g_strdup_printf ("/proc/%ld/cmdline", tmp); + if (g_file_get_contents (proc_path, &proc_contents, NULL, NULL)) { + exe = strrchr (proc_contents, '/'); + if (exe) + exe++; + else + exe = proc_contents; + + if (!strcmp (exe, binary_name)) + stop_process ((GPid) tmp, NULL); + } + } + + remove (pid_file); + g_free (proc_path); + g_free (pid_contents); + g_free (proc_contents); +} + +void +nm_dhcp_client_stop (NMDHCPClient *self) +{ + NMDHCPClientPrivate *priv; + + g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_DHCP_CLIENT (self)); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + /* Kill the DHCP client */ + if (!priv->dead) { + NM_DHCP_CLIENT_GET_CLASS (self)->stop (self); + priv->dead = TRUE; + + nm_log_info (LOGD_DHCP, "(%s): canceled DHCP transaction, DHCP client pid %d", + priv->iface, priv->pid); + } + + /* And clean stuff up */ + + priv->pid = -1; + dhcp_client_set_state (self, DHC_END, FALSE, TRUE); + + g_hash_table_remove_all (priv->options); + + timeout_cleanup (self); + watch_cleanup (self); +} + +/********************************************/ + +static gboolean +state_is_bound (guint32 state) +{ + if ( (state == DHC_BOUND4) + || (state == DHC_BOUND6) + || (state == DHC_RENEW4) + || (state == DHC_RENEW6) + || (state == DHC_REBOOT) + || (state == DHC_REBIND4) + || (state == DHC_REBIND6) + || (state == DHC_IPV4LL)) + return TRUE; + + return FALSE; +} + +typedef struct { + NMDHCPState state; + const char *name; +} DhcState; + +#define STATE_TABLE_SIZE (sizeof (state_table) / sizeof (state_table[0])) + +static DhcState state_table[] = { + { DHC_NBI, "nbi" }, + { DHC_PREINIT, "preinit" }, + { DHC_PREINIT6,"preinit6" }, + { DHC_BOUND4, "bound" }, + { DHC_BOUND6, "bound6" }, + { DHC_IPV4LL, "ipv4ll" }, + { DHC_RENEW4, "renew" }, + { DHC_RENEW6, "renew6" }, + { DHC_REBOOT, "reboot" }, + { DHC_REBIND4, "rebind" }, + { DHC_REBIND6, "rebind6" }, + { DHC_STOP, "stop" }, + { DHC_STOP6, "stop6" }, + { DHC_MEDIUM, "medium" }, + { DHC_TIMEOUT, "timeout" }, + { DHC_FAIL, "fail" }, + { DHC_EXPIRE, "expire" }, + { DHC_EXPIRE6, "expire6" }, + { DHC_RELEASE, "release" }, + { DHC_RELEASE6,"release6" }, + { DHC_START, "start" }, + { DHC_ABEND, "abend" }, + { DHC_END, "end" }, + { DHC_DEPREF6, "depref6" }, +}; + +static inline const char * +state_to_string (guint32 state) +{ + int i; + + for (i = 0; i < STATE_TABLE_SIZE; i++) { + if (state == state_table[i].state) + return state_table[i].name; + } + + return NULL; +} + +static inline NMDHCPState +string_to_state (const char *name) +{ + int i; + + for (i = 0; i < STATE_TABLE_SIZE; i++) { + if (!strcasecmp (name, state_table[i].name)) + return state_table[i].state; + } + + return 255; +} + +static char * +garray_to_string (GArray *array, const char *key) +{ + GString *str; + int i; + unsigned char c; + char *converted = NULL; + + g_return_val_if_fail (array != NULL, NULL); + + /* Since the DHCP options come through environment variables, they should + * already be UTF-8 safe, but just make sure. + */ + str = g_string_sized_new (array->len); + for (i = 0; i < array->len; i++) { + c = array->data[i]; + + /* Convert NULLs to spaces and non-ASCII characters to ? */ + if (c == '\0') + c = ' '; + else if (c > 127) + c = '?'; + str = g_string_append_c (str, c); + } + str = g_string_append_c (str, '\0'); + + converted = str->str; + if (!g_utf8_validate (converted, -1, NULL)) + nm_log_warn (LOGD_DHCP, "DHCP option '%s' couldn't be converted to UTF-8", key); + g_string_free (str, FALSE); + return converted; +} + +static void +copy_option (gpointer key, + gpointer value, + gpointer user_data) +{ + GHashTable *hash = user_data; + const char *str_key = (const char *) key; + char *str_value = NULL; + + if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) { + nm_log_warn (LOGD_DHCP, "unexpected key %s value type was not " + "DBUS_TYPE_G_UCHAR_ARRAY", + str_key); + return; + } + + str_value = garray_to_string ((GArray *) g_value_get_boxed (value), str_key); + if (str_value) + g_hash_table_insert (hash, g_strdup (str_key), str_value); +} + +void +nm_dhcp_client_new_options (NMDHCPClient *self, + GHashTable *options, + const char *reason) +{ + NMDHCPClientPrivate *priv; + guint32 old_state; + guint32 new_state; + + g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_DHCP_CLIENT (self)); + g_return_if_fail (options != NULL); + g_return_if_fail (reason != NULL); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + old_state = priv->state; + new_state = string_to_state (reason); + + /* Clear old and save new DHCP options */ + g_hash_table_remove_all (priv->options); + g_hash_table_foreach (options, copy_option, priv->options); + + if (old_state == new_state) + return; + + /* Handle changed device state */ + if (state_is_bound (new_state)) { + /* Cancel the timeout if the DHCP client is now bound */ + timeout_cleanup (self); + } + + if (priv->ipv6) { + nm_log_info (LOGD_DHCP6, "(%s): DHCPv6 state changed %s -> %s", + priv->iface, + state_to_string (old_state), + state_to_string (new_state)); + } else { + nm_log_info (LOGD_DHCP4, "(%s): DHCPv4 state changed %s -> %s", + priv->iface, + state_to_string (old_state), + state_to_string (new_state)); + } + + dhcp_client_set_state (self, new_state, TRUE, FALSE); +} + +#define NEW_TAG "new_" +#define OLD_TAG "old_" + +gboolean +nm_dhcp_client_foreach_option (NMDHCPClient *self, + GHFunc func, + gpointer user_data) +{ + NMDHCPClientPrivate *priv; + GHashTableIter iter; + gpointer iterkey, itervalue; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); + g_return_val_if_fail (func != NULL, FALSE); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + if (!state_is_bound (priv->state)) { + if (priv->ipv6) { + nm_log_warn (LOGD_DHCP6, "(%s): DHCPv6 client didn't bind to a lease.", priv->iface); + } else { + nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 client didn't bind to a lease.", priv->iface); + } + } + + g_hash_table_iter_init (&iter, priv->options); + while (g_hash_table_iter_next (&iter, &iterkey, &itervalue)) { + const char *key = iterkey, *value = itervalue; + const char **p; + static const char *filter_options[] = { + "interface", "pid", "reason", "dhcp_message_type", NULL + }; + gboolean ignore = FALSE; + + /* Filter out stuff that's not actually new DHCP options */ + for (p = filter_options; *p; p++) { + if (!strcmp (*p, key) || !strncmp (key, OLD_TAG, strlen (OLD_TAG))) { + ignore = TRUE; + break; + } + } + + if (!ignore) { + const char *tmp_key = value; + + /* Remove the "new_" prefix that dhclient passes back */ + if (!strncmp (key, NEW_TAG, strlen (NEW_TAG))) + tmp_key = key + strlen (NEW_TAG); + + func ((gpointer) tmp_key, (gpointer) value, user_data); + } + } + return TRUE; +} + +/********************************************/ + +static void +process_classful_routes (GHashTable *options, NMIP4Config *ip4_config) +{ + const char *str; + char **searches, **s; + + str = g_hash_table_lookup (options, "new_static_routes"); + if (!str) + return; + + searches = g_strsplit (str, " ", 0); + if ((g_strv_length (searches) % 2)) { + nm_log_info (LOGD_DHCP, " static routes provided, but invalid"); + goto out; + } + + for (s = searches; *s; s += 2) { + NMIP4Route *route; + struct in_addr rt_addr; + struct in_addr rt_route; + + if (inet_pton (AF_INET, *s, &rt_addr) <= 0) { + nm_log_warn (LOGD_DHCP, "DHCP provided invalid static route address: '%s'", *s); + continue; + } + if (inet_pton (AF_INET, *(s + 1), &rt_route) <= 0) { + nm_log_warn (LOGD_DHCP, "DHCP provided invalid static route gateway: '%s'", *(s + 1)); + continue; + } + + // FIXME: ensure the IP addresse and route are sane + + route = nm_ip4_route_new (); + nm_ip4_route_set_dest (route, (guint32) rt_addr.s_addr); + nm_ip4_route_set_prefix (route, 32); /* 255.255.255.255 */ + nm_ip4_route_set_next_hop (route, (guint32) rt_route.s_addr); + + nm_ip4_config_take_route (ip4_config, route); + nm_log_info (LOGD_DHCP, " static route %s gw %s", *s, *(s + 1)); + } + +out: + g_strfreev (searches); +} + +static void +process_domain_search (const char *str, GFunc add_func, gpointer user_data) +{ + char **searches, **s; + char *unescaped, *p; + int i; + + g_return_if_fail (str != NULL); + g_return_if_fail (add_func != NULL); + + p = unescaped = g_strdup (str); + do { + p = strstr (p, "\\032"); + if (!p) + break; + + /* Clear the escaped space with real spaces */ + for (i = 0; i < 4; i++) + *p++ = ' '; + } while (*p++); + + if (strchr (unescaped, '\\')) { + nm_log_warn (LOGD_DHCP, " invalid domain search: '%s'", unescaped); + goto out; + } + + searches = g_strsplit (unescaped, " ", 0); + for (s = searches; *s; s++) { + if (strlen (*s)) { + nm_log_info (LOGD_DHCP, " domain search '%s'", *s); + add_func (*s, user_data); + } + } + g_strfreev (searches); + +out: + g_free (unescaped); +} + +static void +ip4_add_domain_search (gpointer data, gpointer user_data) +{ + nm_ip4_config_add_search (NM_IP4_CONFIG (user_data), (const char *) data); +} + +/* Given a table of DHCP options from the client, convert into an IP4Config */ +static NMIP4Config * +ip4_options_to_config (NMDHCPClient *self) +{ + NMDHCPClientPrivate *priv; + NMIP4Config *ip4_config = NULL; + struct in_addr tmp_addr; + NMIP4Address *addr = NULL; + char *str = NULL; + guint32 gwaddr = 0, prefix = 0; + gboolean have_classless = FALSE; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + g_return_val_if_fail (priv->options != NULL, NULL); + + ip4_config = nm_ip4_config_new (); + if (!ip4_config) { + nm_log_warn (LOGD_DHCP4, "(%s): couldn't allocate memory for an IP4Config!", priv->iface); + return NULL; + } + + addr = nm_ip4_address_new (); + if (!addr) { + nm_log_warn (LOGD_DHCP4, "(%s): couldn't allocate memory for an IP4 Address!", priv->iface); + goto error; + } + + str = g_hash_table_lookup (priv->options, "new_ip_address"); + if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) { + nm_ip4_address_set_address (addr, tmp_addr.s_addr); + nm_log_info (LOGD_DHCP4, " address %s", str); + } else + goto error; + + str = g_hash_table_lookup (priv->options, "new_subnet_mask"); + if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) { + prefix = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr); + nm_log_info (LOGD_DHCP4, " prefix %d (%s)", prefix, str); + } else { + /* Get default netmask for the IP according to appropriate class. */ + prefix = nm_utils_ip4_get_default_prefix (nm_ip4_address_get_address (addr)); + nm_log_info (LOGD_DHCP4, " prefix %d (default)", prefix); + } + nm_ip4_address_set_prefix (addr, prefix); + + /* Routes: if the server returns classless static routes, we MUST ignore + * the 'static_routes' option. + */ + if (NM_DHCP_CLIENT_GET_CLASS (self)->ip4_process_classless_routes) { + have_classless = NM_DHCP_CLIENT_GET_CLASS (self)->ip4_process_classless_routes (self, + priv->options, + ip4_config, + &gwaddr); + } + + if (!have_classless) { + gwaddr = 0; /* Ensure client code doesn't lie */ + process_classful_routes (priv->options, ip4_config); + } + + if (gwaddr) { + char buf[INET_ADDRSTRLEN + 1]; + + inet_ntop (AF_INET, &gwaddr, buf, sizeof (buf)); + nm_log_info (LOGD_DHCP4, " gateway %s", buf); + nm_ip4_address_set_gateway (addr, gwaddr); + } else { + /* If the gateway wasn't provided as a classless static route with a + * subnet length of 0, try to find it using the old-style 'routers' option. + */ + str = g_hash_table_lookup (priv->options, "new_routers"); + if (str) { + char **routers = g_strsplit (str, " ", 0); + char **s; + + for (s = routers; *s; s++) { + /* FIXME: how to handle multiple routers? */ + if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { + nm_ip4_address_set_gateway (addr, tmp_addr.s_addr); + nm_log_info (LOGD_DHCP4, " gateway %s", *s); + break; + } else + nm_log_warn (LOGD_DHCP4, "ignoring invalid gateway '%s'", *s); + } + g_strfreev (routers); + } + } + + nm_ip4_config_take_address (ip4_config, addr); + addr = NULL; + + str = g_hash_table_lookup (priv->options, "new_host_name"); + if (str) + nm_log_info (LOGD_DHCP4, " hostname '%s'", str); + + str = g_hash_table_lookup (priv->options, "new_domain_name_servers"); + if (str) { + char **searches = g_strsplit (str, " ", 0); + char **s; + + for (s = searches; *s; s++) { + if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { + nm_ip4_config_add_nameserver (ip4_config, tmp_addr.s_addr); + nm_log_info (LOGD_DHCP4, " nameserver '%s'", *s); + } else + nm_log_warn (LOGD_DHCP4, "ignoring invalid nameserver '%s'", *s); + } + g_strfreev (searches); + } + + str = g_hash_table_lookup (priv->options, "new_domain_name"); + if (str) { + char **domains = g_strsplit (str, " ", 0); + char **s; + + for (s = domains; *s; s++) { + nm_log_info (LOGD_DHCP4, " domain name '%s'", *s); + nm_ip4_config_add_domain (ip4_config, *s); + } + g_strfreev (domains); + } + + str = g_hash_table_lookup (priv->options, "new_domain_search"); + if (str) + process_domain_search (str, ip4_add_domain_search, ip4_config); + + str = g_hash_table_lookup (priv->options, "new_netbios_name_servers"); + if (str) { + char **searches = g_strsplit (str, " ", 0); + char **s; + + for (s = searches; *s; s++) { + if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { + nm_ip4_config_add_wins (ip4_config, tmp_addr.s_addr); + nm_log_info (LOGD_DHCP4, " wins '%s'", *s); + } else + nm_log_warn (LOGD_DHCP4, "ignoring invalid WINS server '%s'", *s); + } + g_strfreev (searches); + } + + str = g_hash_table_lookup (priv->options, "new_interface_mtu"); + if (str) { + int int_mtu; + + errno = 0; + int_mtu = strtol (str, NULL, 10); + if ((errno == EINVAL) || (errno == ERANGE)) + goto error; + + if (int_mtu > 576) + nm_ip4_config_set_mtu (ip4_config, int_mtu); + } + + str = g_hash_table_lookup (priv->options, "new_nis_domain"); + if (str) { + nm_log_info (LOGD_DHCP4, " NIS domain '%s'", str); + nm_ip4_config_set_nis_domain (ip4_config, str); + } + + str = g_hash_table_lookup (priv->options, "new_nis_servers"); + if (str) { + char **searches = g_strsplit (str, " ", 0); + char **s; + + for (s = searches; *s; s++) { + if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { + nm_ip4_config_add_nis_server (ip4_config, tmp_addr.s_addr); + nm_log_info (LOGD_DHCP4, " nis '%s'", *s); + } else + nm_log_warn (LOGD_DHCP4, "ignoring invalid NIS server '%s'", *s); + } + g_strfreev (searches); + } + + return ip4_config; + +error: + if (addr) + nm_ip4_address_unref (addr); + g_object_unref (ip4_config); + return NULL; +} + +NMIP4Config * +nm_dhcp_client_get_ip4_config (NMDHCPClient *self, gboolean test) +{ + NMDHCPClientPrivate *priv; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + if (test && !state_is_bound (priv->state)) { + nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 client didn't bind to a lease.", priv->iface); + return NULL; + } + + if (!g_hash_table_size (priv->options)) { + /* We never got a response from the DHCP client */ + return NULL; + } + + return ip4_options_to_config (self); +} + +/********************************************/ + +static void +ip6_add_domain_search (gpointer data, gpointer user_data) +{ + nm_ip6_config_add_search (NM_IP6_CONFIG (user_data), (const char *) data); +} + +/* Given a table of DHCP options from the client, convert into an IP6Config */ +static NMIP6Config * +ip6_options_to_config (NMDHCPClient *self) +{ + NMDHCPClientPrivate *priv; + NMIP6Config *ip6_config = NULL; + struct in6_addr tmp_addr; + NMIP6Address *addr = NULL; + char *str = NULL; + GHashTableIter iter; + gpointer key, value; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + g_return_val_if_fail (priv->options != NULL, NULL); + + g_hash_table_iter_init (&iter, priv->options); + while (g_hash_table_iter_next (&iter, &key, &value)) { + nm_log_dbg (LOGD_DHCP6, "(%s): option '%s'=>'%s'", + priv->iface, (const char *) key, (const char *) value); + } + + ip6_config = nm_ip6_config_new (); + if (!ip6_config) { + nm_log_warn (LOGD_DHCP6, "(%s): couldn't allocate memory for an IP6Config!", priv->iface); + return NULL; + } + + addr = nm_ip6_address_new (); + if (!addr) { + nm_log_warn (LOGD_DHCP6, "(%s): couldn't allocate memory for an IP6 Address!", priv->iface); + goto error; + } + + str = g_hash_table_lookup (priv->options, "new_ip6_address"); + if (str) { + if (!inet_pton (AF_INET6, str, &tmp_addr)) { + nm_log_warn (LOGD_DHCP6, "(%s): DHCP returned invalid address '%s'", + priv->iface); + goto error; + } + + nm_ip6_address_set_address (addr, &tmp_addr); + nm_log_info (LOGD_DHCP6, " address %s", str); + } else { + /* No address in managed mode is a hard error */ + if (priv->info_only == FALSE) + goto error; + + /* But "info-only" setups don't necessarily need an address */ + nm_ip6_address_unref (addr); + addr = NULL; + } + + /* Only care about prefix if we got an address */ + if (addr) { + str = g_hash_table_lookup (priv->options, "new_ip6_prefixlen"); + if (str) { + long unsigned int prefix; + + errno = 0; + prefix = strtoul (str, NULL, 10); + if (errno != 0 || prefix > 128) + goto error; + + nm_ip6_address_set_prefix (addr, (guint32) prefix); + nm_log_info (LOGD_DHCP6, " prefix %lu", prefix); + } + + nm_ip6_config_take_address (ip6_config, addr); + addr = NULL; + } + + str = g_hash_table_lookup (priv->options, "new_host_name"); + if (str) + nm_log_info (LOGD_DHCP6, " hostname '%s'", str); + + str = g_hash_table_lookup (priv->options, "new_dhcp6_name_servers"); + if (str) { + char **searches = g_strsplit (str, " ", 0); + char **s; + + for (s = searches; *s; s++) { + if (inet_pton (AF_INET6, *s, &tmp_addr) > 0) { + nm_ip6_config_add_nameserver (ip6_config, &tmp_addr); + nm_log_info (LOGD_DHCP6, " nameserver '%s'", *s); + } else + nm_log_warn (LOGD_DHCP6, "ignoring invalid nameserver '%s'", *s); + } + g_strfreev (searches); + } + + str = g_hash_table_lookup (priv->options, "new_dhcp6_domain_search"); + if (str) + process_domain_search (str, ip6_add_domain_search, ip6_config); + + return ip6_config; + +error: + if (addr) + nm_ip6_address_unref (addr); + g_object_unref (ip6_config); + return NULL; +} + +NMIP6Config * +nm_dhcp_client_get_ip6_config (NMDHCPClient *self, gboolean test) +{ + NMDHCPClientPrivate *priv; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + if (test && !state_is_bound (priv->state)) { + nm_log_warn (LOGD_DHCP6, "(%s): DHCPv6 client didn't bind to a lease.", priv->iface); + return NULL; + } + + if (!g_hash_table_size (priv->options)) { + /* We never got a response from the DHCP client */ + return NULL; + } + + return ip6_options_to_config (self); +} + +/********************************************/ + +static void +nm_dhcp_client_init (NMDHCPClient *self) +{ + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + priv->pid = -1; +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_IFACE: + g_value_set_string (value, priv->iface); + break; + case PROP_IPV6: + g_value_set_boolean (value, priv->ipv6); + break; + case PROP_UUID: + g_value_set_string (value, priv->uuid); + break; + case PROP_TIMEOUT: + g_value_set_uint (value, priv->timeout); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_IFACE: + /* construct-only */ + priv->iface = g_strdup (g_value_get_string (value)); + break; + case PROP_IPV6: + /* construct-only */ + priv->ipv6 = g_value_get_boolean (value); + break; + case PROP_UUID: + /* construct-only */ + priv->uuid = g_value_dup_string (value); + break; + case PROP_TIMEOUT: + priv->timeout = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +dispose (GObject *object) +{ + NMDHCPClient *self = NM_DHCP_CLIENT (object); + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + /* Stopping the client is left up to the controlling device + * explicitly since we may want to quit NetworkManager but not terminate + * the DHCP client. + */ + + if (priv->remove_id) + g_source_remove (priv->remove_id); + + g_hash_table_destroy (priv->options); + g_free (priv->iface); + + G_OBJECT_CLASS (nm_dhcp_client_parent_class)->dispose (object); +} + +static void +nm_dhcp_client_class_init (NMDHCPClientClass *client_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (client_class); + + g_type_class_add_private (client_class, sizeof (NMDHCPClientPrivate)); + + /* virtual methods */ + object_class->dispose = dispose; + object_class->get_property = get_property; + object_class->set_property = set_property; + + client_class->stop = real_stop; + + g_object_class_install_property + (object_class, PROP_IFACE, + g_param_spec_string (NM_DHCP_CLIENT_INTERFACE, + "iface", + "Interface", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_IPV6, + g_param_spec_boolean (NM_DHCP_CLIENT_IPV6, + "ipv6", + "IPv6", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_UUID, + g_param_spec_string (NM_DHCP_CLIENT_UUID, + "uuid", + "UUID", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_TIMEOUT, + g_param_spec_uint (NM_DHCP_CLIENT_TIMEOUT, + "timeout", + "Timeout", + 0, G_MAXUINT, 45, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /* signals */ + signals[STATE_CHANGED] = + g_signal_new ("state-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDHCPClientClass, state_changed), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + + signals[TIMEOUT] = + g_signal_new ("timeout", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDHCPClientClass, timeout), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[REMOVE] = + g_signal_new ("remove", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDHCPClientClass, remove), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h new file mode 100644 index 00000000..92b2b8fe --- /dev/null +++ b/src/dhcp-manager/nm-dhcp-client.h @@ -0,0 +1,142 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + */ + +#ifndef NM_DHCP_CLIENT_H +#define NM_DHCP_CLIENT_H + +#include <glib.h> +#include <glib-object.h> + +#include <nm-setting-ip4-config.h> +#include <nm-setting-ip6-config.h> +#include <nm-ip4-config.h> +#include <nm-ip6-config.h> + +#define NM_TYPE_DHCP_CLIENT (nm_dhcp_client_get_type ()) +#define NM_DHCP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_CLIENT, NMDHCPClient)) +#define NM_DHCP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_CLIENT, NMDHCPClientClass)) +#define NM_IS_DHCP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_CLIENT)) +#define NM_IS_DHCP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP_CLIENT)) +#define NM_DHCP_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_CLIENT, NMDHCPClientClass)) + +#define NM_DHCP_CLIENT_INTERFACE "iface" +#define NM_DHCP_CLIENT_IPV6 "ipv6" +#define NM_DHCP_CLIENT_UUID "uuid" +#define NM_DHCP_CLIENT_TIMEOUT "timeout" + +typedef enum { + DHC_NBI = 0, /* no broadcast interfaces found */ + DHC_PREINIT, /* configuration started */ + DHC_PREINIT6, /* configuration started */ + DHC_BOUND4, /* IPv4 lease obtained */ + DHC_BOUND6, /* IPv6 lease obtained */ + DHC_IPV4LL, /* IPv4LL address obtained */ + DHC_RENEW4, /* IPv4 lease renewed */ + DHC_RENEW6, /* IPv6 lease renewed */ + DHC_REBOOT, /* have valid lease, but now obtained a different one */ + DHC_REBIND4, /* IPv4 new/different lease */ + DHC_REBIND6, /* IPv6 new/different lease */ + DHC_DEPREF6, /* IPv6 lease depreferred */ + DHC_STOP, /* remove old lease */ + DHC_STOP6, /* remove old lease */ + DHC_MEDIUM, /* media selection begun */ + DHC_TIMEOUT, /* timed out contacting DHCP server */ + DHC_FAIL, /* all attempts to contact server timed out, sleeping */ + DHC_EXPIRE, /* lease has expired, renewing */ + DHC_EXPIRE6, /* lease has expired, renewing */ + DHC_RELEASE, /* releasing lease */ + DHC_RELEASE6, /* releasing lease */ + DHC_START, /* sent when dhclient started OK */ + DHC_ABEND, /* dhclient exited abnormally */ + DHC_END, /* dhclient exited normally */ + DHC_END_OPTIONS, /* last option in subscription sent */ +} NMDHCPState; + +typedef struct { + GObject parent; +} NMDHCPClient; + +typedef struct { + GObjectClass parent; + + /* Methods */ + + /* Given the options table, extract any classless routes, add them to + * the IP4 config and return TRUE if any existed. If a gateway was sent + * as a classless route return that in out_gwaddr. + */ + gboolean (*ip4_process_classless_routes) (NMDHCPClient *self, + GHashTable *options, + NMIP4Config *ip4_config, + guint32 *out_gwaddr); + + GPid (*ip4_start) (NMDHCPClient *self, + NMSettingIP4Config *s_ip4, + guint8 *anycast_addr); + + GPid (*ip6_start) (NMDHCPClient *self, + NMSettingIP6Config *s_ip6, + guint8 *anycast_addr, + gboolean info_only); + + void (*stop) (NMDHCPClient *self); + + /* Signals */ + void (*state_changed) (NMDHCPClient *self, NMDHCPState state); + void (*timeout) (NMDHCPClient *self); + void (*remove) (NMDHCPClient *self); +} NMDHCPClientClass; + +GType nm_dhcp_client_get_type (void); + +GPid nm_dhcp_client_get_pid (NMDHCPClient *self); + +const char *nm_dhcp_client_get_iface (NMDHCPClient *self); + +gboolean nm_dhcp_client_get_ipv6 (NMDHCPClient *self); + +const char *nm_dhcp_client_get_uuid (NMDHCPClient *self); + +gboolean nm_dhcp_client_start_ip4 (NMDHCPClient *self, + NMSettingIP4Config *s_ip4, + guint8 *dhcp_anycast_addr); + +gboolean nm_dhcp_client_start_ip6 (NMDHCPClient *self, + NMSettingIP6Config *s_ip6, + guint8 *dhcp_anycast_addr, + gboolean info_only); + +void nm_dhcp_client_stop (NMDHCPClient *self); + +void nm_dhcp_client_new_options (NMDHCPClient *self, + GHashTable *options, + const char *reason); + +gboolean nm_dhcp_client_foreach_option (NMDHCPClient *self, + GHFunc func, + gpointer user_data); + +NMIP4Config *nm_dhcp_client_get_ip4_config (NMDHCPClient *self, gboolean test); + +NMIP6Config *nm_dhcp_client_get_ip6_config (NMDHCPClient *self, gboolean test); + +/* Backend helpers */ +void nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name); + +#endif /* NM_DHCP_CLIENT_H */ + diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c new file mode 100644 index 00000000..bb5cd2e8 --- /dev/null +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -0,0 +1,786 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-dhcp-dhclient.c - dhclient specific hooks for NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + */ + +#define _XOPEN_SOURCE +#include <time.h> +#undef _XOPEN_SOURCE + +#include <glib.h> +#include <glib/gi18n.h> +#include <dbus/dbus.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <ctype.h> + +#include <config.h> + +#include "nm-dhcp-dhclient.h" +#include "nm-utils.h" +#include "nm-logging.h" + +G_DEFINE_TYPE (NMDHCPDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT) + +#define NM_DHCP_DHCLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientPrivate)) + +#if defined(TARGET_DEBIAN) || defined(TARGET_SUSE) || defined(TARGET_MANDRIVA) +#define NM_DHCLIENT_LEASE_DIR LOCALSTATEDIR "/lib/dhcp" +#else +#define NM_DHCLIENT_LEASE_DIR LOCALSTATEDIR "/lib/dhclient" +#endif + +#define ACTION_SCRIPT_PATH LIBEXECDIR "/nm-dhcp-client.action" + +typedef struct { + const char *path; + char *conf_file; + char *lease_file; + char *pid_file; +} NMDHCPDhclientPrivate; + +const char * +nm_dhcp_dhclient_get_path (const char *try_first) +{ + static const char *dhclient_paths[] = { + "/sbin/dhclient", + "/usr/sbin/dhclient", + "/usr/pkg/sbin/dhclient", + "/usr/local/sbin/dhclient", + NULL + }; + const char **path = dhclient_paths; + + if (strlen (try_first) && g_file_test (try_first, G_FILE_TEST_EXISTS)) + return try_first; + + while (*path != NULL) { + if (g_file_test (*path, G_FILE_TEST_EXISTS)) + break; + path++; + } + + return *path; +} + +static char * +get_leasefile_for_iface (const char * iface, const char *uuid, gboolean ipv6) +{ + return g_strdup_printf ("%s/dhclient%s-%s-%s.lease", + NM_DHCLIENT_LEASE_DIR, + ipv6 ? "6" : "", + uuid, + iface); +} + +static void +add_lease_option (GHashTable *hash, char *line) +{ + char *spc; + + spc = strchr (line, ' '); + if (!spc) { + nm_log_warn (LOGD_DHCP, "DHCP lease file line '%s' did not contain a space", line); + return; + } + + /* If it's an 'option' line, split at second space */ + if (g_str_has_prefix (line, "option ")) { + spc = strchr (spc + 1, ' '); + if (!spc) { + nm_log_warn (LOGD_DHCP, "DHCP lease file option line '%s' did not contain a second space", + line); + return; + } + } + + /* Split the line at the space */ + *spc = '\0'; + spc++; + + /* Kill the ';' at the end of the line, if any */ + if (*(spc + strlen (spc) - 1) == ';') + *(spc + strlen (spc) - 1) = '\0'; + + /* Treat 'interface' specially */ + if (g_str_has_prefix (line, "interface")) { + if (*(spc) == '"') + spc++; /* Jump past the " */ + if (*(spc + strlen (spc) - 1) == '"') + *(spc + strlen (spc) - 1) = '\0'; /* Kill trailing " */ + } + + g_hash_table_insert (hash, g_strdup (line), g_strdup (spc)); +} + +GSList * +nm_dhcp_dhclient_get_lease_config (const char *iface, const char *uuid) +{ + GSList *parsed = NULL, *iter, *leases = NULL; + char *contents = NULL; + char *leasefile; + char **line, **split = NULL; + GHashTable *hash = NULL; + + leasefile = get_leasefile_for_iface (iface, uuid, FALSE); + if (!leasefile) + return NULL; + + if (!g_file_test (leasefile, G_FILE_TEST_EXISTS)) + goto out; + + if (!g_file_get_contents (leasefile, &contents, NULL, NULL)) + goto out; + + split = g_strsplit_set (contents, "\n\r", -1); + g_free (contents); + if (!split) + goto out; + + for (line = split; line && *line; line++) { + *line = g_strstrip (*line); + + if (!strcmp (*line, "}")) { + /* Lease ends */ + parsed = g_slist_append (parsed, hash); + hash = NULL; + } else if (!strcmp (*line, "lease {")) { + /* Beginning of a new lease */ + if (hash) { + nm_log_warn (LOGD_DHCP, "DHCP lease file %s malformed; new lease started " + "without ending previous lease", + leasefile); + g_hash_table_destroy (hash); + } + + hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + } else if (strlen (*line)) + add_lease_option (hash, *line); + } + g_strfreev (split); + + /* Check if the last lease in the file was properly ended */ + if (hash) { + nm_log_warn (LOGD_DHCP, "DHCP lease file %s malformed; new lease started " + "without ending previous lease", + leasefile); + g_hash_table_destroy (hash); + hash = NULL; + } + + for (iter = parsed; iter; iter = g_slist_next (iter)) { + NMIP4Config *ip4; + NMIP4Address *addr; + const char *data; + struct in_addr tmp; + guint32 prefix; + struct tm expire; + + hash = iter->data; + + /* Make sure this lease is for the interface we want */ + data = g_hash_table_lookup (hash, "interface"); + if (!data || strcmp (data, iface)) + continue; + + data = g_hash_table_lookup (hash, "expire"); + if (data) { + time_t now_tt; + struct tm *now; + + /* Read lease expiration (in UTC) */ + if (!strptime (data, "%w %Y/%m/%d %H:%M:%S", &expire)) { + nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file expire time '%s'", + data); + continue; + } + + now_tt = time (NULL); + now = gmtime(&now_tt); + + /* Ignore this lease if it's already expired */ + if (expire.tm_year < now->tm_year) + continue; + else if (expire.tm_year == now->tm_year) { + if (expire.tm_mon < now->tm_mon) + continue; + else if (expire.tm_mon == now->tm_mon) { + if (expire.tm_mday < now->tm_mday) + continue; + else if (expire.tm_mday == now->tm_mday) { + if (expire.tm_hour < now->tm_hour) + continue; + else if (expire.tm_hour == now->tm_hour) { + if (expire.tm_min < now->tm_min) + continue; + else if (expire.tm_min == now->tm_min) { + if (expire.tm_sec <= now->tm_sec) + continue; + } + } + } + } + } + /* If we get this far, the lease hasn't expired */ + } + + data = g_hash_table_lookup (hash, "fixed-address"); + if (!data) + continue; + + ip4 = nm_ip4_config_new (); + addr = nm_ip4_address_new (); + + /* IP4 address */ + if (!inet_pton (AF_INET, data, &tmp)) { + nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file IP4 address '%s'", data); + goto error; + } + nm_ip4_address_set_address (addr, tmp.s_addr); + + /* Netmask */ + data = g_hash_table_lookup (hash, "option subnet-mask"); + if (data) { + if (!inet_pton (AF_INET, data, &tmp)) { + nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file IP4 subnet mask '%s'", data); + goto error; + } + prefix = nm_utils_ip4_netmask_to_prefix (tmp.s_addr); + } else { + /* Get default netmask for the IP according to appropriate class. */ + prefix = nm_utils_ip4_get_default_prefix (nm_ip4_address_get_address (addr)); + } + nm_ip4_address_set_prefix (addr, prefix); + + /* Gateway */ + data = g_hash_table_lookup (hash, "option routers"); + if (data) { + if (!inet_pton (AF_INET, data, &tmp)) { + nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file IP4 gateway '%s'", data); + goto error; + } + nm_ip4_address_set_gateway (addr, tmp.s_addr); + } + + nm_ip4_config_take_address (ip4, addr); + leases = g_slist_append (leases, ip4); + continue; + + error: + nm_ip4_address_unref (addr); + g_object_unref (ip4); + } + +out: + g_slist_foreach (parsed, (GFunc) g_hash_table_destroy, NULL); + g_free (leasefile); + return leases; +} + + +#define DHCP_CLIENT_ID_TAG "send dhcp-client-identifier" +#define DHCP_CLIENT_ID_FORMAT DHCP_CLIENT_ID_TAG " \"%s\"; # added by NetworkManager" +#define DHCP_CLIENT_ID_FORMAT_OCTETS DHCP_CLIENT_ID_TAG " %s; # added by NetworkManager" + +#define DHCP_HOSTNAME_TAG "send host-name" +#define DHCP_HOSTNAME_FORMAT DHCP_HOSTNAME_TAG " \"%s\"; # added by NetworkManager" + +static gboolean +merge_dhclient_config (const char *iface, + const char *conf_file, + NMSettingIP4Config *s_ip4, + guint8 *anycast_addr, + const char *orig_path, + GError **error) +{ + GString *new_contents; + char *orig_contents = NULL; + gboolean success = FALSE; + + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (conf_file != NULL, FALSE); + + new_contents = g_string_new (_("# Created by NetworkManager\n")); + + if (g_file_test (orig_path, G_FILE_TEST_EXISTS)) { + GError *read_error = NULL; + + if (!g_file_get_contents (orig_path, &orig_contents, NULL, &read_error)) { + nm_log_warn (LOGD_DHCP, "(%s): error reading dhclient configuration %s: %s", + iface, orig_path, read_error->message); + g_error_free (read_error); + } + } + + /* Add existing options, if any, but ignore stuff NM will replace. */ + if (orig_contents) { + char **lines = NULL, **line; + + g_string_append_printf (new_contents, _("# Merged from %s\n\n"), orig_path); + + lines = g_strsplit_set (orig_contents, "\n\r", 0); + for (line = lines; lines && *line; line++) { + gboolean ignore = FALSE; + + if (!strlen (g_strstrip (*line))) + continue; + + if ( s_ip4 + && nm_setting_ip4_config_get_dhcp_client_id (s_ip4) + && !strncmp (*line, DHCP_CLIENT_ID_TAG, strlen (DHCP_CLIENT_ID_TAG))) + ignore = TRUE; + + if ( s_ip4 + && nm_setting_ip4_config_get_dhcp_hostname (s_ip4) + && !strncmp (*line, DHCP_HOSTNAME_TAG, strlen (DHCP_HOSTNAME_TAG))) + ignore = TRUE; + + if (!ignore) { + g_string_append (new_contents, *line); + g_string_append_c (new_contents, '\n'); + } + } + + if (lines) + g_strfreev (lines); + g_free (orig_contents); + } else + g_string_append_c (new_contents, '\n'); + + /* Add NM options from connection */ + if (s_ip4) { + const char *tmp; + + tmp = nm_setting_ip4_config_get_dhcp_client_id (s_ip4); + if (tmp) { + gboolean is_octets = TRUE; + const char *p = tmp; + + while (*p) { + if (!isxdigit (*p) && (*p != ':')) { + is_octets = FALSE; + break; + } + p++; + } + + /* If the client ID is just hex digits and : then don't use quotes, + * becuase dhclient expects either a quoted ASCII string, or a byte + * array formated as hex octets separated by : + */ + if (is_octets) + g_string_append_printf (new_contents, DHCP_CLIENT_ID_FORMAT_OCTETS "\n", tmp); + else + g_string_append_printf (new_contents, DHCP_CLIENT_ID_FORMAT "\n", tmp); + } + + tmp = nm_setting_ip4_config_get_dhcp_hostname (s_ip4); + if (tmp) + g_string_append_printf (new_contents, DHCP_HOSTNAME_FORMAT "\n", tmp); + } + + if (anycast_addr) { + g_string_append_printf (new_contents, "interface \"%s\" {\n" + " initial-interval 1; \n" + " anycast-mac ethernet %02x:%02x:%02x:%02x:%02x:%02x;\n" + "}\n", + iface, + anycast_addr[0], anycast_addr[1], + anycast_addr[2], anycast_addr[3], + anycast_addr[4], anycast_addr[5]); + } + + success = g_file_set_contents (conf_file, new_contents->str, -1, error); + + g_string_free (new_contents, TRUE); + return success; +} + +/* NM provides interface-specific options; thus the same dhclient config + * file cannot be used since DHCP transactions can happen in parallel. + * Since some distros don't have default per-interface dhclient config files, + * read their single config file and merge that into a custom per-interface + * config file along with the NM options. + */ +static char * +create_dhclient_config (const char *iface, + NMSettingIP4Config *s_ip4, + guint8 *dhcp_anycast_addr) +{ + char *orig = NULL, *tmp, *conf_file = NULL; + GError *error = NULL; + gboolean success = FALSE; + + g_return_val_if_fail (iface != NULL, FALSE); + +#if defined(TARGET_SUSE) + orig = g_strdup (SYSCONFDIR "/dhclient.conf"); +#elif defined(TARGET_DEBIAN) || defined(TARGET_GENTOO) + orig = g_strdup (SYSCONFDIR "/dhcp/dhclient.conf"); +#else + orig = g_strdup_printf (SYSCONFDIR "/dhclient-%s.conf", iface); +#endif + + if (!orig) { + nm_log_warn (LOGD_DHCP, "(%s): not enough memory for dhclient options.", iface); + return FALSE; + } + +#if !defined(TARGET_SUSE) && !defined(TARGET_DEBIAN) && !defined(TARGET_GENTOO) + /* Try /etc/dhcp/ too (rh #607759) */ + if (!g_file_test (orig, G_FILE_TEST_EXISTS)) { + g_free (orig); + orig = g_strdup_printf (SYSCONFDIR "/dhcp/dhclient-%s.conf", iface); + if (!orig) { + nm_log_warn (LOGD_DHCP, "(%s): not enough memory for dhclient options.", iface); + return FALSE; + } + } +#endif + + tmp = g_strdup_printf ("nm-dhclient-%s.conf", iface); + conf_file = g_build_filename ("/var", "run", tmp, NULL); + g_free (tmp); + + error = NULL; + success = merge_dhclient_config (iface, conf_file, s_ip4, dhcp_anycast_addr, orig, &error); + if (!success) { + nm_log_warn (LOGD_DHCP, "(%s): error creating dhclient configuration: %s", + iface, error->message); + g_error_free (error); + } + + g_free (orig); + return conf_file; +} + + +static void +dhclient_child_setup (gpointer user_data G_GNUC_UNUSED) +{ + /* We are in the child process at this point */ + pid_t pid = getpid (); + setpgid (pid, pid); +} + +static GPid +dhclient_start (NMDHCPClient *client, + const char *ip_opt, + const char *mode_opt) +{ + NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client); + GPtrArray *argv = NULL; + GPid pid = -1; + GError *error = NULL; + const char *iface, *uuid; + char *binary_name, *cmd_str; + gboolean ipv6; + guint log_domain; + + g_return_val_if_fail (priv->pid_file == NULL, -1); + g_return_val_if_fail (ip_opt != NULL, -1); + + iface = nm_dhcp_client_get_iface (client); + uuid = nm_dhcp_client_get_uuid (client); + ipv6 = nm_dhcp_client_get_ipv6 (client); + + log_domain = ipv6 ? LOGD_DHCP6 : LOGD_DHCP4; + + priv->pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhclient%s-%s.pid", + ipv6 ? "6" : "", + iface); + if (!priv->pid_file) { + nm_log_warn (log_domain, "(%s): not enough memory for dhcpcd options.", iface); + return -1; + } + + if (!g_file_test (priv->path, G_FILE_TEST_EXISTS)) { + nm_log_warn (log_domain, "%s does not exist.", priv->path); + return -1; + } + + /* Kill any existing dhclient from the pidfile */ + binary_name = g_path_get_basename (priv->path); + nm_dhcp_client_stop_existing (priv->pid_file, binary_name); + g_free (binary_name); + + priv->lease_file = get_leasefile_for_iface (iface, uuid, ipv6); + if (!priv->lease_file) { + nm_log_warn (log_domain, "(%s): not enough memory for dhclient options.", iface); + return -1; + } + + argv = g_ptr_array_new (); + g_ptr_array_add (argv, (gpointer) priv->path); + + g_ptr_array_add (argv, (gpointer) "-d"); + + g_ptr_array_add (argv, (gpointer) ip_opt); + + if (mode_opt) + g_ptr_array_add (argv, (gpointer) mode_opt); + + g_ptr_array_add (argv, (gpointer) "-sf"); /* Set script file */ + g_ptr_array_add (argv, (gpointer) ACTION_SCRIPT_PATH ); + + g_ptr_array_add (argv, (gpointer) "-pf"); /* Set pid file */ + g_ptr_array_add (argv, (gpointer) priv->pid_file); + + g_ptr_array_add (argv, (gpointer) "-lf"); /* Set lease file */ + g_ptr_array_add (argv, (gpointer) priv->lease_file); + + if (priv->conf_file) { + g_ptr_array_add (argv, (gpointer) "-cf"); /* Set interface config file */ + g_ptr_array_add (argv, (gpointer) priv->conf_file); + } + + g_ptr_array_add (argv, (gpointer) iface); + g_ptr_array_add (argv, NULL); + + cmd_str = g_strjoinv (" ", (gchar **) argv->pdata); + nm_log_dbg (log_domain, "running: %s", cmd_str); + g_free (cmd_str); + + if (!g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD, + &dhclient_child_setup, NULL, &pid, &error)) { + nm_log_warn (log_domain, "dhclient failed to start: '%s'", error->message); + g_error_free (error); + pid = -1; + } else + nm_log_info (log_domain, "dhclient started with pid %d", pid); + + g_ptr_array_free (argv, TRUE); + return pid; +} + +static GPid +real_ip4_start (NMDHCPClient *client, + NMSettingIP4Config *s_ip4, + guint8 *dhcp_anycast_addr) +{ + NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client); + const char *iface; + + iface = nm_dhcp_client_get_iface (client); + + priv->conf_file = create_dhclient_config (iface, s_ip4, dhcp_anycast_addr); + if (!priv->conf_file) { + nm_log_warn (LOGD_DHCP4, "(%s): error creating dhclient configuration file.", iface); + return -1; + } + + return dhclient_start (client, "-4", NULL); +} + +static GPid +real_ip6_start (NMDHCPClient *client, + NMSettingIP6Config *s_ip6, + guint8 *dhcp_anycast_addr, + gboolean info_only) +{ + return dhclient_start (client, "-6", info_only ? "-S" : "-N"); +} + +static void +real_stop (NMDHCPClient *client) +{ + NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client); + + /* Chain up to parent */ + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client); + + if (priv->conf_file) + remove (priv->conf_file); + if (priv->pid_file) + remove (priv->pid_file); +} + +static const char ** +process_rfc3442_route (const char **octets, NMIP4Route **out_route) +{ + const char **o = octets; + int addr_len = 0, i = 0; + long int tmp; + NMIP4Route *route; + char *next_hop; + struct in_addr tmp_addr; + + if (!*o) + return o; /* no prefix */ + + tmp = strtol (*o, NULL, 10); + if (tmp < 0 || tmp > 32) /* 32 == max IP4 prefix length */ + return o; + + route = nm_ip4_route_new (); + nm_ip4_route_set_prefix (route, (guint32) tmp); + o++; + + if (tmp > 0) + addr_len = ((tmp - 1) / 8) + 1; + + /* ensure there's at least the address + next hop left */ + if (g_strv_length ((char **) o) < addr_len + 4) + goto error; + + if (tmp) { + const char *addr[4] = { "0", "0", "0", "0" }; + char *str_addr; + + for (i = 0; i < addr_len; i++) + addr[i] = *o++; + + str_addr = g_strjoin (".", addr[0], addr[1], addr[2], addr[3], NULL); + if (inet_pton (AF_INET, str_addr, &tmp_addr) <= 0) { + g_free (str_addr); + goto error; + } + tmp_addr.s_addr &= nm_utils_ip4_prefix_to_netmask ((guint32) tmp); + nm_ip4_route_set_dest (route, tmp_addr.s_addr); + } + + /* Handle next hop */ + next_hop = g_strjoin (".", o[0], o[1], o[2], o[3], NULL); + if (inet_pton (AF_INET, next_hop, &tmp_addr) <= 0) { + g_free (next_hop); + goto error; + } + nm_ip4_route_set_next_hop (route, tmp_addr.s_addr); + g_free (next_hop); + + *out_route = route; + return o + 4; /* advance to past the next hop */ + +error: + nm_ip4_route_unref (route); + return o; +} + +static gboolean +real_ip4_process_classless_routes (NMDHCPClient *client, + GHashTable *options, + NMIP4Config *ip4_config, + guint32 *gwaddr) +{ + const char *str; + char **octets, **o; + gboolean have_routes = FALSE; + NMIP4Route *route = NULL; + + /* dhclient doesn't have actual support for rfc3442 classless static routes + * upstream. Thus, people resort to defining the option in dhclient.conf + * and using arbitrary formats like so: + * + * option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; + * + * See https://lists.isc.org/pipermail/dhcp-users/2008-December/007629.html + */ + + str = g_hash_table_lookup (options, "new_rfc3442_classless_static_routes"); + /* Microsoft version; same as rfc3442 but with a different option # (249) */ + if (!str) + str = g_hash_table_lookup (options, "new_ms_classless_static_routes"); + + if (!str || !strlen (str)) + return FALSE; + + o = octets = g_strsplit (str, " ", 0); + if (g_strv_length (octets) < 5) { + nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes '%s'", str); + goto out; + } + + while (*o) { + route = NULL; + o = (char **) process_rfc3442_route ((const char **) o, &route); + if (!route) { + nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes"); + break; + } + + have_routes = TRUE; + if (nm_ip4_route_get_prefix (route) == 0) { + /* gateway passed as classless static route */ + *gwaddr = nm_ip4_route_get_next_hop (route); + nm_ip4_route_unref (route); + } else { + char addr[INET_ADDRSTRLEN + 1]; + char nh[INET_ADDRSTRLEN + 1]; + struct in_addr tmp; + + /* normal route */ + nm_ip4_config_take_route (ip4_config, route); + + tmp.s_addr = nm_ip4_route_get_dest (route); + inet_ntop (AF_INET, &tmp, addr, sizeof (addr)); + tmp.s_addr = nm_ip4_route_get_next_hop (route); + inet_ntop (AF_INET, &tmp, nh, sizeof (nh)); + nm_log_info (LOGD_DHCP4, " classless static route %s/%d gw %s", + addr, nm_ip4_route_get_prefix (route), nh); + } + } + +out: + g_strfreev (octets); + return have_routes; +} + +/***************************************************/ + +static void +nm_dhcp_dhclient_init (NMDHCPDhclient *self) +{ + NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self); + + priv->path = nm_dhcp_dhclient_get_path (DHCLIENT_PATH); +} + +static void +dispose (GObject *object) +{ + NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (object); + + g_free (priv->pid_file); + g_free (priv->conf_file); + g_free (priv->lease_file); + + G_OBJECT_CLASS (nm_dhcp_dhclient_parent_class)->dispose (object); +} + +static void +nm_dhcp_dhclient_class_init (NMDHCPDhclientClass *dhclient_class) +{ + NMDHCPClientClass *client_class = NM_DHCP_CLIENT_CLASS (dhclient_class); + GObjectClass *object_class = G_OBJECT_CLASS (dhclient_class); + + g_type_class_add_private (dhclient_class, sizeof (NMDHCPDhclientPrivate)); + + /* virtual methods */ + object_class->dispose = dispose; + + client_class->ip4_start = real_ip4_start; + client_class->ip6_start = real_ip6_start; + client_class->stop = real_stop; + client_class->ip4_process_classless_routes = real_ip4_process_classless_routes; +} + diff --git a/src/dhcp-manager/nm-dhcp-dhclient.h b/src/dhcp-manager/nm-dhcp-dhclient.h new file mode 100644 index 00000000..be242de0 --- /dev/null +++ b/src/dhcp-manager/nm-dhcp-dhclient.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + */ + +#ifndef NM_DHCP_DHCLIENT_H +#define NM_DHCP_DHCLIENT_H + +#include <glib.h> +#include <glib-object.h> + +#include "nm-dhcp-client.h" + +#define NM_TYPE_DHCP_DHCLIENT (nm_dhcp_dhclient_get_type ()) +#define NM_DHCP_DHCLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclient)) +#define NM_DHCP_DHCLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientClass)) +#define NM_IS_DHCP_DHCLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_DHCLIENT)) +#define NM_IS_DHCP_DHCLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP_DHCLIENT)) +#define NM_DHCP_DHCLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientClass)) + +typedef struct { + NMDHCPClient parent; +} NMDHCPDhclient; + +typedef struct { + NMDHCPClientClass parent; +} NMDHCPDhclientClass; + +GType nm_dhcp_dhclient_get_type (void); + +GSList *nm_dhcp_dhclient_get_lease_config (const char *iface, const char *uuid); + +const char *nm_dhcp_dhclient_get_path (const char *try_first); + +#endif /* NM_DHCP_DHCLIENT_H */ + diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c new file mode 100644 index 00000000..403431fc --- /dev/null +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c @@ -0,0 +1,294 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-dhcp-dhcpcd.c - dhcpcd specific hooks for NetworkManager + * + * Copyright (C) 2008 Roy Marples + * Copyright (C) 2010 Dan Williams <dcbw@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include <glib.h> +#include <glib/gi18n.h> +#include <dbus/dbus.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "nm-dhcp-dhcpcd.h" +#include "nm-utils.h" +#include "nm-logging.h" + +G_DEFINE_TYPE (NMDHCPDhcpcd, nm_dhcp_dhcpcd, NM_TYPE_DHCP_CLIENT) + +#define NM_DHCP_DHCPCD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcdPrivate)) + +#define ACTION_SCRIPT_PATH LIBEXECDIR "/nm-dhcp-client.action" + +typedef struct { + const char *path; + char *pid_file; +} NMDHCPDhcpcdPrivate; + +const char * +nm_dhcp_dhcpcd_get_path (const char *try_first) +{ + static const char *dhcpcd_paths[] = { + "/sbin/dhcpcd", + "/usr/sbin/dhcpcd", + "/usr/pkg/sbin/dhcpcd", + "/usr/local/sbin/dhcpcd", + NULL + }; + const char **path = dhcpcd_paths; + + if (strlen (try_first) && g_file_test (try_first, G_FILE_TEST_EXISTS)) + return try_first; + + while (*path != NULL) { + if (g_file_test (*path, G_FILE_TEST_EXISTS)) + break; + path++; + } + + return *path; +} + +GSList * +nm_dhcp_dhcpcd_get_lease_config (const char *iface, const char *uuid) +{ + return NULL; +} + +static void +dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED) +{ + /* We are in the child process at this point */ + pid_t pid = getpid (); + setpgid (pid, pid); +} + +static GPid +real_ip4_start (NMDHCPClient *client, + NMSettingIP4Config *s_ip4, + guint8 *dhcp_anycast_addr) +{ + NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client); + GPtrArray *argv = NULL; + GPid pid = -1; + GError *error = NULL; + char *pid_contents = NULL, *binary_name, *cmd_str; + const char *iface, *uuid, *hostname; + + g_return_val_if_fail (priv->pid_file == NULL, -1); + + iface = nm_dhcp_client_get_iface (client); + uuid = nm_dhcp_client_get_uuid (client); + + priv->pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhcpcd-%s.pid", iface); + if (!priv->pid_file) { + nm_log_warn (LOGD_DHCP4, "(%s): not enough memory for dhcpcd options.", iface); + return -1; + } + + if (!g_file_test (priv->path, G_FILE_TEST_EXISTS)) { + nm_log_warn (LOGD_DHCP4, "%s does not exist.", priv->path); + return -1; + } + + /* Kill any existing dhcpcd from the pidfile */ + binary_name = g_path_get_basename (priv->path); + nm_dhcp_client_stop_existing (priv->pid_file, binary_name); + g_free (binary_name); + + argv = g_ptr_array_new (); + g_ptr_array_add (argv, (gpointer) priv->path); + + g_ptr_array_add (argv, (gpointer) "-B"); /* Don't background on lease (disable fork()) */ + + g_ptr_array_add (argv, (gpointer) "-K"); /* Disable built-in carrier detection */ + + g_ptr_array_add (argv, (gpointer) "-L"); /* Disable built-in IPv4LL since we use avahi-autoipd */ + + g_ptr_array_add (argv, (gpointer) "-c"); /* Set script file */ + g_ptr_array_add (argv, (gpointer) ACTION_SCRIPT_PATH ); + + hostname = nm_setting_ip4_config_get_dhcp_hostname (s_ip4); + if (hostname && strlen (hostname)) { + g_ptr_array_add (argv, (gpointer) "-h"); /* Send hostname to DHCP server */ + g_ptr_array_add (argv, (gpointer) hostname ); + } + + g_ptr_array_add (argv, (gpointer) iface); + g_ptr_array_add (argv, NULL); + + cmd_str = g_strjoinv (" ", (gchar **) argv->pdata); + nm_log_dbg (LOGD_DHCP4, "running: %s", cmd_str); + g_free (cmd_str); + + if (!g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD, + &dhcpcd_child_setup, NULL, &pid, &error)) { + nm_log_warn (LOGD_DHCP4, "dhcpcd failed to start. error: '%s'", error->message); + g_error_free (error); + pid = -1; + } else + nm_log_info (LOGD_DHCP4, "dhcpcd started with pid %d", pid); + + g_free (pid_contents); + g_ptr_array_free (argv, TRUE); + return pid; +} + +static GPid +real_ip6_start (NMDHCPClient *client, + NMSettingIP6Config *s_ip6, + guint8 *dhcp_anycast_addr, + gboolean info_only) +{ + nm_log_warn (LOGD_DHCP6, "the dhcpcd backend does not support IPv6."); + return -1; +} + +static void +real_stop (NMDHCPClient *client) +{ + NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client); + + /* Chain up to parent */ + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client); + + if (priv->pid_file) + remove (priv->pid_file); +} + +static gboolean +real_ip4_process_classless_routes (NMDHCPClient *client, + GHashTable *options, + NMIP4Config *ip4_config, + guint32 *gwaddr) +{ + const char *str; + char **routes, **r; + gboolean have_routes = FALSE; + + /* Classless static routes over-ride any static routes and routers + * provided. We should also check for MS classless static routes as + * they implemented the draft RFC using their own code. + */ + str = g_hash_table_lookup (options, "new_classless_static_routes"); + if (!str) + str = g_hash_table_lookup (options, "new_ms_classless_static_routes"); + + if (!str || !strlen (str)) + return FALSE; + + routes = g_strsplit (str, " ", 0); + if (g_strv_length (routes) == 0) + goto out; + + if ((g_strv_length (routes) % 2) != 0) { + nm_log_warn (LOGD_DHCP4, " classless static routes provided, but invalid"); + goto out; + } + + for (r = routes; *r; r += 2) { + char *slash; + NMIP4Route *route; + int rt_cidr = 32; + struct in_addr rt_addr; + struct in_addr rt_route; + + slash = strchr(*r, '/'); + if (slash) { + *slash = '\0'; + errno = 0; + rt_cidr = strtol (slash + 1, NULL, 10); + if ((errno == EINVAL) || (errno == ERANGE)) { + nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route cidr: '%s'", slash + 1); + continue; + } + } + if (inet_pton (AF_INET, *r, &rt_addr) <= 0) { + nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route address: '%s'", *r); + continue; + } + if (inet_pton (AF_INET, *(r + 1), &rt_route) <= 0) { + nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route gateway: '%s'", *(r + 1)); + continue; + } + + have_routes = TRUE; + if (rt_cidr == 0 && rt_addr.s_addr == 0) { + /* FIXME: how to handle multiple routers? */ + *gwaddr = rt_addr.s_addr; + } else { + route = nm_ip4_route_new (); + nm_ip4_route_set_dest (route, (guint32) rt_addr.s_addr); + nm_ip4_route_set_prefix (route, rt_cidr); + nm_ip4_route_set_next_hop (route, (guint32) rt_route.s_addr); + + + nm_ip4_config_take_route (ip4_config, route); + nm_log_info (LOGD_DHCP4, " classless static route %s/%d gw %s", *r, rt_cidr, *(r + 1)); + } + } + +out: + g_strfreev (routes); + return have_routes; +} + +/***************************************************/ + +static void +nm_dhcp_dhcpcd_init (NMDHCPDhcpcd *self) +{ + NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (self); + + priv->path = nm_dhcp_dhcpcd_get_path (DHCPCD_PATH); +} + +static void +dispose (GObject *object) +{ + NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (object); + + g_free (priv->pid_file); + + G_OBJECT_CLASS (nm_dhcp_dhcpcd_parent_class)->dispose (object); +} + +static void +nm_dhcp_dhcpcd_class_init (NMDHCPDhcpcdClass *dhcpcd_class) +{ + NMDHCPClientClass *client_class = NM_DHCP_CLIENT_CLASS (dhcpcd_class); + GObjectClass *object_class = G_OBJECT_CLASS (dhcpcd_class); + + g_type_class_add_private (dhcpcd_class, sizeof (NMDHCPDhcpcdPrivate)); + + /* virtual methods */ + object_class->dispose = dispose; + + client_class->ip4_start = real_ip4_start; + client_class->ip6_start = real_ip6_start; + client_class->stop = real_stop; + client_class->ip4_process_classless_routes = real_ip4_process_classless_routes; +} + diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.h b/src/dhcp-manager/nm-dhcp-dhcpcd.h new file mode 100644 index 00000000..586c5697 --- /dev/null +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + */ + +#ifndef NM_DHCP_DHCPCD_H +#define NM_DHCP_DHCPCD_H + +#include <glib.h> +#include <glib-object.h> + +#include "nm-dhcp-client.h" + +#define NM_TYPE_DHCP_DHCPCD (nm_dhcp_dhcpcd_get_type ()) +#define NM_DHCP_DHCPCD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcd)) +#define NM_DHCP_DHCPCD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcdClass)) +#define NM_IS_DHCP_DHCPCD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_DHCPCD)) +#define NM_IS_DHCP_DHCPCD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP_DHCPCD)) +#define NM_DHCP_DHCPCD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcdClass)) + +typedef struct { + NMDHCPClient parent; +} NMDHCPDhcpcd; + +typedef struct { + NMDHCPClientClass parent; +} NMDHCPDhcpcdClass; + +GType nm_dhcp_dhcpcd_get_type (void); + +GSList *nm_dhcp_dhcpcd_get_lease_config (const char *iface, const char *uuid); + +const char *nm_dhcp_dhcpcd_get_path (const char *try_first); + +#endif /* NM_DHCP_DHCPCD_H */ + diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c new file mode 100644 index 00000000..7b110fb2 --- /dev/null +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -0,0 +1,647 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-dhcp-manager.c - Handle the DHCP daemon for NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + * + */ + + +#include <glib.h> +#include <glib/gi18n.h> +#include <dbus/dbus.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <signal.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> + +#include "nm-dhcp-manager.h" +#include "nm-dhcp-dhclient.h" +#include "nm-dhcp-dhcpcd.h" +#include "nm-marshal.h" +#include "nm-logging.h" +#include "nm-dbus-manager.h" +#include "nm-hostname-provider.h" +#include "nm-dbus-glib-types.h" +#include "nm-glib-compat.h" + +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GQuark +nm_dhcp_manager_error_quark (void) +{ + static GQuark ret = 0; + + if (ret == 0) + ret = g_quark_from_static_string ("nm_dhcp_manager_error"); + + return ret; +} + +GType +nm_dhcp_manager_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (NM_DHCP_MANAGER_ERROR_BAD_CLIENT, "BadClient"), + ENUM_ENTRY (NM_DHCP_MANAGER_ERROR_INTERNAL, "InternalError"), + { 0, 0, 0 } + }; + etype = g_enum_register_static ("NMDhcpManagerError", values); + } + return etype; +} + +#define NM_DHCP_CLIENT_DBUS_SERVICE "org.freedesktop.nm_dhcp_client" +#define NM_DHCP_CLIENT_DBUS_IFACE "org.freedesktop.nm_dhcp_client" + +#define DHCP_TIMEOUT 45 /* default DHCP timeout, in seconds */ + +static NMDHCPManager *singleton = NULL; + +typedef GSList * (*GetLeaseConfigFunc) (const char *iface, const char *uuid); + +typedef struct { + GType client_type; + GetLeaseConfigFunc get_lease_config_func; + + NMDBusManager * dbus_mgr; + GHashTable * clients; + DBusGProxy * proxy; + NMHostnameProvider *hostname_provider; +} NMDHCPManagerPrivate; + + +#define NM_DHCP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_MANAGER, NMDHCPManagerPrivate)) + +G_DEFINE_TYPE (NMDHCPManager, nm_dhcp_manager, G_TYPE_OBJECT) + +static char * +garray_to_string (GArray *array, const char *key) +{ + GString *str; + int i; + unsigned char c; + char *converted = NULL; + + g_return_val_if_fail (array != NULL, NULL); + + /* Since the DHCP options come through environment variables, they should + * already be UTF-8 safe, but just make sure. + */ + str = g_string_sized_new (array->len); + for (i = 0; i < array->len; i++) { + c = array->data[i]; + + /* Convert NULLs to spaces and non-ASCII characters to ? */ + if (c == '\0') + c = ' '; + else if (c > 127) + c = '?'; + str = g_string_append_c (str, c); + } + str = g_string_append_c (str, '\0'); + + converted = str->str; + if (!g_utf8_validate (converted, -1, NULL)) + nm_log_warn (LOGD_DHCP, "DHCP option '%s' couldn't be converted to UTF-8", key); + g_string_free (str, FALSE); + return converted; +} + +static NMDHCPClient * +get_client_for_pid (NMDHCPManager *manager, GPid pid) +{ + NMDHCPManagerPrivate *priv; + GHashTableIter iter; + gpointer value; + + g_return_val_if_fail (manager != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL); + + priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); + + g_hash_table_iter_init (&iter, priv->clients); + while (g_hash_table_iter_next (&iter, NULL, &value)) { + NMDHCPClient *candidate = NM_DHCP_CLIENT (value); + + if (nm_dhcp_client_get_pid (candidate) == pid) + return candidate; + } + + return NULL; +} + +static NMDHCPClient * +get_client_for_iface (NMDHCPManager *manager, + const char *iface, + gboolean ip6) +{ + NMDHCPManagerPrivate *priv; + GHashTableIter iter; + gpointer value; + + g_return_val_if_fail (manager != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL); + g_return_val_if_fail (iface, NULL); + + priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); + + g_hash_table_iter_init (&iter, priv->clients); + while (g_hash_table_iter_next (&iter, NULL, &value)) { + NMDHCPClient *candidate = NM_DHCP_CLIENT (value); + + if ( !strcmp (iface, nm_dhcp_client_get_iface (candidate)) + && (nm_dhcp_client_get_ipv6 (candidate) == ip6)) + return candidate; + } + + return NULL; +} + +static char * +get_option (GHashTable *hash, const char *key) +{ + GValue *value; + + value = g_hash_table_lookup (hash, key); + if (value == NULL) + return NULL; + + if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) { + nm_log_warn (LOGD_DHCP, "unexpected key %s value type was not " + "DBUS_TYPE_G_UCHAR_ARRAY", + (char *) key); + return NULL; + } + + return garray_to_string ((GArray *) g_value_get_boxed (value), key); +} + +static void +nm_dhcp_manager_handle_event (DBusGProxy *proxy, + GHashTable *options, + gpointer user_data) +{ + NMDHCPManager *manager; + NMDHCPManagerPrivate *priv; + NMDHCPClient *client; + char *iface = NULL; + char *pid_str = NULL; + char *reason = NULL; + unsigned long temp; + + manager = NM_DHCP_MANAGER (user_data); + priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); + + iface = get_option (options, "interface"); + if (iface == NULL) { + nm_log_warn (LOGD_DHCP, "DHCP event didn't have associated interface."); + goto out; + } + + pid_str = get_option (options, "pid"); + if (pid_str == NULL) { + nm_log_warn (LOGD_DHCP, "DHCP event didn't have associated PID."); + goto out; + } + + temp = strtoul (pid_str, NULL, 10); + if ((temp == ULONG_MAX) && (errno == ERANGE)) { + nm_log_warn (LOGD_DHCP, "couldn't convert PID"); + goto out; + } + + client = get_client_for_pid (manager, (GPid) temp); + if (client == NULL) { + nm_log_warn (LOGD_DHCP, "(pid %d) unhandled DHCP event for interface %s", temp, iface); + goto out; + } + + if (strcmp (iface, nm_dhcp_client_get_iface (client))) { + nm_log_warn (LOGD_DHCP, "(pid %d) received DHCP event from unexpected interface '%s' (expected '%s')", + temp, iface, nm_dhcp_client_get_iface (client)); + goto out; + } + + reason = get_option (options, "reason"); + if (reason == NULL) { + nm_log_warn (LOGD_DHCP, "(pid %d) DHCP event didn't have a reason", temp); + goto out; + } + + nm_dhcp_client_new_options (client, options, reason); + +out: + g_free (iface); + g_free (pid_str); + g_free (reason); +} + +static GType +get_client_type (const char *client, GError **error) +{ + const char *dhclient_path = NULL; + const char *dhcpcd_path = NULL; + + dhclient_path = nm_dhcp_dhclient_get_path (DHCLIENT_PATH); + dhcpcd_path = nm_dhcp_dhcpcd_get_path (DHCPCD_PATH); + + if (!client) { + if (dhclient_path) + return NM_TYPE_DHCP_DHCLIENT; + else if (dhcpcd_path) + return NM_TYPE_DHCP_DHCPCD; + else { + g_set_error_literal (error, + NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT, + _("no usable DHCP client could be found.")); + return 0; + } + } + + if (!strcmp (client, "dhclient")) { + if (!dhclient_path) { + g_set_error_literal (error, + NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT, + _("'dhclient' could be found.")); + return 0; + } + return NM_TYPE_DHCP_DHCLIENT; + } + + if (!strcmp (client, "dhcpcd")) { + if (!dhcpcd_path) { + g_set_error_literal (error, + NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT, + _("'dhcpcd' could be found.")); + return 0; + } + return NM_TYPE_DHCP_DHCPCD; + } + + g_set_error (error, + NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT, + _("unsupported DHCP client '%s'"), client); + return 0; +} + +NMDHCPManager * +nm_dhcp_manager_new (const char *client, GError **error) +{ + NMDHCPManagerPrivate *priv; + DBusGConnection *g_connection; + GType client_type; + + client_type = get_client_type (client, error); + if (!client_type) + return NULL; + + g_warn_if_fail (singleton == NULL); + + singleton = g_object_new (NM_TYPE_DHCP_MANAGER, NULL); + priv = NM_DHCP_MANAGER_GET_PRIVATE (singleton); + + /* Client-specific setup */ + priv->client_type = client_type; + if (priv->client_type == NM_TYPE_DHCP_DHCLIENT) + priv->get_lease_config_func = nm_dhcp_dhclient_get_lease_config; + else if (priv->client_type == NM_TYPE_DHCP_DHCPCD) + priv->get_lease_config_func = nm_dhcp_dhcpcd_get_lease_config; + else + g_assert_not_reached (); + + priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, + (GDestroyNotify) g_object_unref); + g_assert (priv->clients); + + priv->dbus_mgr = nm_dbus_manager_get (); + g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr); + priv->proxy = dbus_g_proxy_new_for_name (g_connection, + NM_DHCP_CLIENT_DBUS_SERVICE, + "/", + NM_DHCP_CLIENT_DBUS_IFACE); + g_assert (priv->proxy); + + dbus_g_proxy_add_signal (priv->proxy, + "Event", + DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_INVALID); + + dbus_g_proxy_connect_signal (priv->proxy, "Event", + G_CALLBACK (nm_dhcp_manager_handle_event), + singleton, + NULL); + + return singleton; +} + +#define REMOVE_ID_TAG "remove-id" +#define TIMEOUT_ID_TAG "timeout-id" + +static void +remove_client (NMDHCPManager *self, NMDHCPClient *client) +{ + NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self); + guint id; + + id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (client), REMOVE_ID_TAG)); + if (id) + g_signal_handler_disconnect (client, id); + + id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (client), TIMEOUT_ID_TAG)); + if (id) + g_signal_handler_disconnect (client, id); + + /* Stopping the client is left up to the controlling device + * explicitly since we may want to quit NetworkManager but not terminate + * the DHCP client. + */ + + g_hash_table_remove (priv->clients, client); +} + +static void +add_client (NMDHCPManager *self, NMDHCPClient *client) +{ + NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self); + guint id; + + id = g_signal_connect_swapped (client, "remove", G_CALLBACK (remove_client), self); + g_object_set_data (G_OBJECT (client), REMOVE_ID_TAG, GUINT_TO_POINTER (id)); + + id = g_signal_connect_swapped (client, "timeout", G_CALLBACK (remove_client), self); + g_object_set_data (G_OBJECT (client), TIMEOUT_ID_TAG, GUINT_TO_POINTER (id)); + + g_hash_table_insert (priv->clients, client, g_object_ref (client)); +} + +static NMDHCPClient * +client_start (NMDHCPManager *self, + const char *iface, + const char *uuid, + gboolean ipv6, + NMSettingIP4Config *s_ip4, + NMSettingIP6Config *s_ip6, + guint32 timeout, + guint8 *dhcp_anycast_addr, + gboolean info_only) +{ + NMDHCPManagerPrivate *priv; + NMDHCPClient *client; + gboolean success = FALSE; + + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (uuid != NULL, NULL); + + priv = NM_DHCP_MANAGER_GET_PRIVATE (self); + + /* Kill any old client instance */ + client = get_client_for_iface (self, iface, ipv6); + if (client) { + nm_dhcp_client_stop (client); + remove_client (self, client); + } + + /* And make a new one */ + client = g_object_new (priv->client_type, + NM_DHCP_CLIENT_INTERFACE, iface, + NM_DHCP_CLIENT_IPV6, ipv6, + NM_DHCP_CLIENT_UUID, uuid, + NM_DHCP_CLIENT_TIMEOUT, timeout ? timeout : DHCP_TIMEOUT, + NULL); + g_return_val_if_fail (client != NULL, NULL); + add_client (self, client); + + if (ipv6) + success = nm_dhcp_client_start_ip6 (client, s_ip6, dhcp_anycast_addr, info_only); + else + success = nm_dhcp_client_start_ip4 (client, s_ip4, dhcp_anycast_addr); + + if (!success) { + remove_client (self, client); + g_object_unref (client); + client = NULL; + } + + return client; +} + +/* Caller owns a reference to the NMDHCPClient on return */ +NMDHCPClient * +nm_dhcp_manager_start_ip4 (NMDHCPManager *self, + const char *iface, + const char *uuid, + NMSettingIP4Config *s_ip4, + guint32 timeout, + guint8 *dhcp_anycast_addr) +{ + NMDHCPManagerPrivate *priv; + NMDHCPClient *client = NULL; + + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL); + + priv = NM_DHCP_MANAGER_GET_PRIVATE (self); + + if (s_ip4) { + const char *method = nm_setting_ip4_config_get_method (s_ip4); + + if (method) { + /* Method must be 'auto' */ + g_return_val_if_fail (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0, NULL); + } + + if ( nm_setting_ip4_config_get_dhcp_send_hostname (s_ip4) + && (nm_setting_ip4_config_get_dhcp_hostname (s_ip4) == NULL) + && priv->hostname_provider != NULL) { + + s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_duplicate (NM_SETTING (s_ip4))); + + /* We're asked to send the hostname to DHCP server, the hostname + * isn't specified, and a hostname provider is registered: use that + */ + g_object_set (G_OBJECT (s_ip4), + NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, + nm_hostname_provider_get_hostname (priv->hostname_provider), + NULL); + } else + g_object_ref (s_ip4); + } + + client = client_start (self, iface, uuid, FALSE, s_ip4, NULL, timeout, dhcp_anycast_addr, FALSE); + + if (s_ip4) + g_object_unref (s_ip4); + + return client; +} + +/* Caller owns a reference to the NMDHCPClient on return */ +NMDHCPClient * +nm_dhcp_manager_start_ip6 (NMDHCPManager *self, + const char *iface, + const char *uuid, + NMSettingIP6Config *s_ip6, + guint32 timeout, + guint8 *dhcp_anycast_addr, + gboolean info_only) +{ + return client_start (self, iface, uuid, TRUE, NULL, s_ip6, timeout, dhcp_anycast_addr, info_only); +} + +static void +hostname_provider_destroyed (gpointer data, GObject *destroyed_object) +{ + NM_DHCP_MANAGER_GET_PRIVATE (data)->hostname_provider = NULL; +} + +void +nm_dhcp_manager_set_hostname_provider (NMDHCPManager *manager, + NMHostnameProvider *provider) +{ + NMDHCPManagerPrivate *priv; + + g_return_if_fail (NM_IS_DHCP_MANAGER (manager)); + + priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); + + if (priv->hostname_provider) { + g_object_weak_unref (G_OBJECT (priv->hostname_provider), hostname_provider_destroyed, manager); + priv->hostname_provider = NULL; + } + + if (provider) { + priv->hostname_provider = provider; + g_object_weak_ref (G_OBJECT (provider), hostname_provider_destroyed, manager); + } +} + +GSList * +nm_dhcp_manager_get_lease_config (NMDHCPManager *self, + const char *iface, + const char *uuid) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (uuid != NULL, NULL); + + return NM_DHCP_MANAGER_GET_PRIVATE (self)->get_lease_config_func (iface, uuid); +} + +NMIP4Config * +nm_dhcp_manager_test_ip4_options_to_config (const char *dhcp_client, + const char *iface, + GHashTable *options, + const char *reason) +{ + NMDHCPClient *client; + NMIP4Config *config; + GType client_type; + GError *error = NULL; + + client_type = get_client_type (dhcp_client, &error); + if (!client_type) { + nm_log_err (LOGD_DHCP4, "error: %s", error ? error->message : "(unknown)"); + g_clear_error (&error); + return NULL; + } + + client = (NMDHCPClient *) g_object_new (client_type, + NM_DHCP_CLIENT_INTERFACE, iface, + NULL); + g_return_val_if_fail (client != NULL, NULL); + nm_dhcp_client_new_options (client, options, reason); + config = nm_dhcp_client_get_ip4_config (client, TRUE); + g_object_unref (client); + + return config; +} + +/***************************************************/ + +NMDHCPManager * +nm_dhcp_manager_get (void) +{ + g_warn_if_fail (singleton != NULL); + return g_object_ref (singleton); +} + +static void +nm_dhcp_manager_init (NMDHCPManager *manager) +{ +} + +static void +dispose (GObject *object) +{ + NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (object); + GList *values, *iter; + + if (priv->clients) { + values = g_hash_table_get_values (priv->clients); + for (iter = values; iter; iter = g_list_next (iter)) + remove_client (NM_DHCP_MANAGER (object), NM_DHCP_CLIENT (iter->data)); + g_list_free (values); + } + + G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (object); + + if (priv->hostname_provider) { + g_object_weak_unref (G_OBJECT (priv->hostname_provider), hostname_provider_destroyed, object); + priv->hostname_provider = NULL; + } + + if (priv->clients) + g_hash_table_destroy (priv->clients); + if (priv->proxy) + g_object_unref (priv->proxy); + if (priv->dbus_mgr) + g_object_unref (priv->dbus_mgr); + + G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->finalize (object); +} + +static void +nm_dhcp_manager_class_init (NMDHCPManagerClass *manager_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (manager_class); + + g_type_class_add_private (manager_class, sizeof (NMDHCPManagerPrivate)); + + /* virtual methods */ + object_class->finalize = finalize; + object_class->dispose = dispose; +} + diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h new file mode 100644 index 00000000..9d83d096 --- /dev/null +++ b/src/dhcp-manager/nm-dhcp-manager.h @@ -0,0 +1,98 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-dhcp-manager.c - Handle the DHCP daemon for NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef NM_DHCP_MANAGER_H +#define NM_DHCP_MANAGER_H + +#include <glib.h> +#include <glib-object.h> + +#include <nm-setting-ip4-config.h> +#include <nm-setting-ip6-config.h> + +#include "nm-dhcp-client.h" +#include "nm-ip4-config.h" +#include "nm-dhcp4-config.h" +#include "nm-hostname-provider.h" + +enum { + NM_DHCP_MANAGER_ERROR_BAD_CLIENT = 0, + NM_DHCP_MANAGER_ERROR_INTERNAL = 1, +}; + +#define NM_DHCP_MANAGER_ERROR (nm_dhcp_manager_error_quark ()) +#define NN_TYPE_DHCP_MANAGER_ERROR (nm_dhcp_manager_error_get_type ()) + +GQuark nm_dhcp_manager_error_quark (void); +GType nm_dhcp_manager_error_get_type (void); + + +#define NM_TYPE_DHCP_MANAGER (nm_dhcp_manager_get_type ()) +#define NM_DHCP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_MANAGER, NMDHCPManager)) +#define NM_DHCP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_MANAGER, NMDHCPManagerClass)) +#define NM_IS_DHCP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_MANAGER)) +#define NM_IS_DHCP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP_MANAGER)) +#define NM_DHCP_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_MANAGER, NMDHCPManagerClass)) + +typedef struct { + GObject parent; +} NMDHCPManager; + +typedef struct { + GObjectClass parent; +} NMDHCPManagerClass; + +GType nm_dhcp_manager_get_type (void); + +NMDHCPManager *nm_dhcp_manager_get (void); + +void nm_dhcp_manager_set_hostname_provider(NMDHCPManager *manager, + NMHostnameProvider *provider); + +NMDHCPClient * nm_dhcp_manager_start_ip4 (NMDHCPManager *manager, + const char *iface, + const char *uuid, + NMSettingIP4Config *s_ip4, + guint32 timeout, + guint8 *dhcp_anycast_addr); + +NMDHCPClient * nm_dhcp_manager_start_ip6 (NMDHCPManager *manager, + const char *iface, + const char *uuid, + NMSettingIP6Config *s_ip6, + guint32 timeout, + guint8 *dhcp_anycast_addr, + gboolean info_only); + +GSList * nm_dhcp_manager_get_lease_config (NMDHCPManager *self, + const char *iface, + const char *uuid); + +/* For testing only */ +NMIP4Config *nm_dhcp_manager_test_ip4_options_to_config (const char *dhcp_client, + const char *iface, + GHashTable *options, + const char *reason); + +/* Only for main.c */ +NMDHCPManager *nm_dhcp_manager_new (const char *client, GError **error); + +#endif /* NM_DHCP_MANAGER_H */ diff --git a/src/dnsmasq-manager/Makefile.am b/src/dnsmasq-manager/Makefile.am new file mode 100644 index 00000000..66bbdd83 --- /dev/null +++ b/src/dnsmasq-manager/Makefile.am @@ -0,0 +1,19 @@ +INCLUDES = \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/src \ + -I${top_srcdir}/include + +noinst_LTLIBRARIES = libdnsmasq-manager.la + +libdnsmasq_manager_la_SOURCES = \ + nm-dnsmasq-manager.h \ + nm-dnsmasq-manager.c + +libdnsmasq_manager_la_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + -DLOCALSTATEDIR=\"$(localstatedir)\" + +libdnsmasq_manager_la_LIBADD = \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(GLIB_LIBS) diff --git a/src/dnsmasq-manager/Makefile.in b/src/dnsmasq-manager/Makefile.in new file mode 100644 index 00000000..c71621a8 --- /dev/null +++ b/src/dnsmasq-manager/Makefile.in @@ -0,0 +1,564 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/dnsmasq-manager +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libdnsmasq_manager_la_DEPENDENCIES = \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(am__DEPENDENCIES_1) +am_libdnsmasq_manager_la_OBJECTS = \ + libdnsmasq_manager_la-nm-dnsmasq-manager.lo +libdnsmasq_manager_la_OBJECTS = $(am_libdnsmasq_manager_la_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libdnsmasq_manager_la_SOURCES) +DIST_SOURCES = $(libdnsmasq_manager_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +IPTABLES_PATH = @IPTABLES_PATH@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBM = @LIBM@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POFILES = @POFILES@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +RANLIB = @RANLIB@ +RESOLVCONF_PATH = @RESOLVCONF_PATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/src \ + -I${top_srcdir}/include + +noinst_LTLIBRARIES = libdnsmasq-manager.la +libdnsmasq_manager_la_SOURCES = \ + nm-dnsmasq-manager.h \ + nm-dnsmasq-manager.c + +libdnsmasq_manager_la_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + -DLOCALSTATEDIR=\"$(localstatedir)\" + +libdnsmasq_manager_la_LIBADD = \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(GLIB_LIBS) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/dnsmasq-manager/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/dnsmasq-manager/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libdnsmasq-manager.la: $(libdnsmasq_manager_la_OBJECTS) $(libdnsmasq_manager_la_DEPENDENCIES) + $(LINK) $(libdnsmasq_manager_la_OBJECTS) $(libdnsmasq_manager_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdnsmasq_manager_la-nm-dnsmasq-manager.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libdnsmasq_manager_la-nm-dnsmasq-manager.lo: nm-dnsmasq-manager.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnsmasq_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnsmasq_manager_la-nm-dnsmasq-manager.lo -MD -MP -MF $(DEPDIR)/libdnsmasq_manager_la-nm-dnsmasq-manager.Tpo -c -o libdnsmasq_manager_la-nm-dnsmasq-manager.lo `test -f 'nm-dnsmasq-manager.c' || echo '$(srcdir)/'`nm-dnsmasq-manager.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libdnsmasq_manager_la-nm-dnsmasq-manager.Tpo $(DEPDIR)/libdnsmasq_manager_la-nm-dnsmasq-manager.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-dnsmasq-manager.c' object='libdnsmasq_manager_la-nm-dnsmasq-manager.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnsmasq_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnsmasq_manager_la-nm-dnsmasq-manager.lo `test -f 'nm-dnsmasq-manager.c' || echo '$(srcdir)/'`nm-dnsmasq-manager.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/dnsmasq-manager/nm-dnsmasq-manager.c b/src/dnsmasq-manager/nm-dnsmasq-manager.c new file mode 100644 index 00000000..e6c67a1e --- /dev/null +++ b/src/dnsmasq-manager/nm-dnsmasq-manager.c @@ -0,0 +1,495 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 - 2010 Red Hat, Inc. + */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <string.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <stdlib.h> + +#include "nm-dnsmasq-manager.h" +#include "nm-logging.h" +#include "nm-glib-compat.h" + +typedef struct { + char *iface; + char *pidfile; + GPid pid; + guint32 dm_watch_id; +} NMDnsMasqManagerPrivate; + +#define NM_DNSMASQ_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DNSMASQ_MANAGER, NMDnsMasqManagerPrivate)) + +G_DEFINE_TYPE (NMDnsMasqManager, nm_dnsmasq_manager, G_TYPE_OBJECT) + +enum { + STATE_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef enum { + NM_DNSMASQ_MANAGER_ERROR_NOT_FOUND, +} NMDnsMasqManagerError; + +GQuark +nm_dnsmasq_manager_error_quark (void) +{ + static GQuark quark; + + if (!quark) + quark = g_quark_from_static_string ("nm_dnsmasq_manager_error"); + + return quark; +} + +static void +nm_dnsmasq_manager_init (NMDnsMasqManager *manager) +{ +} + +static void +finalize (GObject *object) +{ + NMDnsMasqManagerPrivate *priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (object); + + nm_dnsmasq_manager_stop (NM_DNSMASQ_MANAGER (object)); + + g_free (priv->iface); + g_free (priv->pidfile); + + G_OBJECT_CLASS (nm_dnsmasq_manager_parent_class)->finalize (object); +} + +static void +nm_dnsmasq_manager_class_init (NMDnsMasqManagerClass *manager_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (manager_class); + + g_type_class_add_private (manager_class, sizeof (NMDnsMasqManagerPrivate)); + + object_class->finalize = finalize; + + /* signals */ + signals[STATE_CHANGED] = + g_signal_new ("state-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDnsMasqManagerClass, state_changed), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, + G_TYPE_UINT); +} + +NMDnsMasqManager * +nm_dnsmasq_manager_new (const char *iface) +{ + NMDnsMasqManager *manager; + NMDnsMasqManagerPrivate *priv; + + manager = (NMDnsMasqManager *) g_object_new (NM_TYPE_DNSMASQ_MANAGER, NULL); + if (!manager) + return NULL; + + priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (manager); + priv->iface = g_strdup (iface); + priv->pidfile = g_strdup_printf (LOCALSTATEDIR "/run/nm-dnsmasq-%s.pid", iface); + + return manager; +} + +typedef struct { + GPtrArray *array; + GStringChunk *chunk; +} NMCmdLine; + +static NMCmdLine * +nm_cmd_line_new (void) +{ + NMCmdLine *cmd; + + cmd = g_slice_new (NMCmdLine); + cmd->array = g_ptr_array_new (); + cmd->chunk = g_string_chunk_new (1024); + + return cmd; +} + +static void +nm_cmd_line_destroy (NMCmdLine *cmd) +{ + g_ptr_array_free (cmd->array, TRUE); + g_string_chunk_free (cmd->chunk); + g_slice_free (NMCmdLine, cmd); +} + +static char * +nm_cmd_line_to_str (NMCmdLine *cmd) +{ + char *str; + + g_ptr_array_add (cmd->array, NULL); + str = g_strjoinv (" ", (gchar **) cmd->array->pdata); + g_ptr_array_remove_index (cmd->array, cmd->array->len - 1); + + return str; +} + +static void +nm_cmd_line_add_string (NMCmdLine *cmd, const char *str) +{ + g_ptr_array_add (cmd->array, g_string_chunk_insert (cmd->chunk, str)); +} + +/*******************************************/ + +static inline const char * +nm_find_dnsmasq (void) +{ + static const char *dnsmasq_binary_paths[] = { + "/usr/local/sbin/dnsmasq", + "/usr/sbin/dnsmasq", + "/sbin/dnsmasq", + NULL + }; + + const char **dnsmasq_binary = dnsmasq_binary_paths; + + while (*dnsmasq_binary != NULL) { + if (g_file_test (*dnsmasq_binary, G_FILE_TEST_EXISTS)) + break; + dnsmasq_binary++; + } + + return *dnsmasq_binary; +} + +static void +dm_exit_code (guint dm_exit_status) +{ + char *msg = "Unknown error"; + + switch (dm_exit_status) { + case 1: + msg = "Configuration problem"; + break; + case 2: + msg = "Network access problem (address in use; permissions; etc)"; + break; + case 3: + msg = "Filesystem problem (missing file/directory; permissions; etc)"; + break; + case 4: + msg = "Memory allocation failure"; + break; + case 5: + msg = "Other problem"; + break; + default: + if (dm_exit_status >= 11) + msg = "Lease-script 'init' process failure"; + break; + } + + nm_log_warn (LOGD_SHARING, "dnsmasq exited with error: %s (%d)", msg, dm_exit_status); +} + +static void +dm_watch_cb (GPid pid, gint status, gpointer user_data) +{ + NMDnsMasqManager *manager = NM_DNSMASQ_MANAGER (user_data); + NMDnsMasqManagerPrivate *priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (manager); + guint err; + + if (WIFEXITED (status)) { + err = WEXITSTATUS (status); + if (err != 0) + dm_exit_code (err); + } else if (WIFSTOPPED (status)) { + nm_log_warn (LOGD_SHARING, "dnsmasq stopped unexpectedly with signal %d", WSTOPSIG (status)); + } else if (WIFSIGNALED (status)) { + nm_log_warn (LOGD_SHARING, "dnsmasq died with signal %d", WTERMSIG (status)); + } else { + nm_log_warn (LOGD_SHARING, "dnsmasq died from an unknown cause"); + } + + priv->pid = 0; + + g_signal_emit (manager, signals[STATE_CHANGED], 0, NM_DNSMASQ_STATUS_DEAD); +} + +static NMCmdLine * +create_dm_cmd_line (const char *iface, + NMIP4Config *ip4_config, + const char *pidfile, + GError **error) +{ + const char *dm_binary; + NMCmdLine *cmd; + GString *s; + NMIP4Address *tmp; + struct in_addr addr; + char buf[INET_ADDRSTRLEN + 15]; + char localaddr[INET_ADDRSTRLEN + 1]; + int i; + + dm_binary = nm_find_dnsmasq (); + if (!dm_binary) { + g_set_error (error, NM_DNSMASQ_MANAGER_ERROR, NM_DNSMASQ_MANAGER_ERROR_NOT_FOUND, + "Could not find dnsmasq binary."); + return NULL; + } + + /* Find the IP4 address to use */ + tmp = nm_ip4_config_get_address (ip4_config, 0); + + /* Create dnsmasq command line */ + cmd = nm_cmd_line_new (); + nm_cmd_line_add_string (cmd, dm_binary); + + if (getenv ("NM_DNSMASQ_DEBUG")) { + nm_cmd_line_add_string (cmd, "--log-dhcp"); + nm_cmd_line_add_string (cmd, "--log-queries"); + } + + /* dnsmasq may read from it's default config file location, which if that + * location is a valid config file, it will combine with the options here + * and cause undesirable side-effects. Like sending bogus IP addresses + * as the gateway or whatever. So give dnsmasq a bogus config file + * location to avoid screwing up the configuration we're passing to it. + */ + memset (buf, 0, sizeof (buf)); + strcpy (buf, "/tmp/"); + for (i = 5; i < 15; i++) + buf[i] = (char) (g_random_int_range ((guint32) 'a', (guint32) 'z') & 0xFF); + strcat (buf, ".conf"); + + nm_cmd_line_add_string (cmd, "--conf-file"); + nm_cmd_line_add_string (cmd, buf); + + nm_cmd_line_add_string (cmd, "--no-hosts"); + nm_cmd_line_add_string (cmd, "--keep-in-foreground"); + nm_cmd_line_add_string (cmd, "--bind-interfaces"); + nm_cmd_line_add_string (cmd, "--except-interface=lo"); + nm_cmd_line_add_string (cmd, "--clear-on-reload"); + + /* Use strict order since in the case of VPN connections, the VPN's + * nameservers will be first in resolv.conf, and those need to be tried + * first by dnsmasq to successfully resolve names from the VPN. + */ + nm_cmd_line_add_string (cmd, "--strict-order"); + + s = g_string_new ("--listen-address="); + addr.s_addr = nm_ip4_address_get_address (tmp); + if (!inet_ntop (AF_INET, &addr, &localaddr[0], INET_ADDRSTRLEN)) { + nm_log_warn (LOGD_SHARING, "error converting IP4 address 0x%X", + ntohl (addr.s_addr)); + goto error; + } + g_string_append (s, localaddr); + nm_cmd_line_add_string (cmd, s->str); + g_string_free (s, TRUE); + + s = g_string_new ("--dhcp-range="); + + /* Add start of address range */ + addr.s_addr = nm_ip4_address_get_address (tmp) + htonl (9); + if (!inet_ntop (AF_INET, &addr, &buf[0], INET_ADDRSTRLEN)) { + nm_log_warn (LOGD_SHARING, "error converting IP4 address 0x%X", + ntohl (addr.s_addr)); + goto error; + } + g_string_append (s, buf); + + g_string_append_c (s, ','); + + /* Add end of address range */ + addr.s_addr = nm_ip4_address_get_address (tmp) + htonl (99); + if (!inet_ntop (AF_INET, &addr, &buf[0], INET_ADDRSTRLEN)) { + nm_log_warn (LOGD_SHARING, "error converting IP4 address 0x%X", + ntohl (addr.s_addr)); + goto error; + } + g_string_append (s, buf); + + g_string_append (s, ",60m"); + nm_cmd_line_add_string (cmd, s->str); + g_string_free (s, TRUE); + + s = g_string_new ("--dhcp-option=option:router,"); + g_string_append (s, localaddr); + nm_cmd_line_add_string (cmd, s->str); + g_string_free (s, TRUE); + + nm_cmd_line_add_string (cmd, "--dhcp-lease-max=50"); + + s = g_string_new ("--pid-file="); + g_string_append (s, pidfile); + nm_cmd_line_add_string (cmd, s->str); + g_string_free (s, TRUE); + + return cmd; + +error: + nm_cmd_line_destroy (cmd); + return NULL; +} + +static void +dm_child_setup (gpointer user_data G_GNUC_UNUSED) +{ + /* We are in the child process at this point */ + pid_t pid = getpid (); + setpgid (pid, pid); +} + +static void +kill_existing_for_iface (const char *iface, const char *pidfile) +{ + char *contents = NULL; + glong pid; + char *proc_path = NULL; + char *cmdline_contents = NULL; + + if (!g_file_get_contents (pidfile, &contents, NULL, NULL)) + goto out; + + pid = strtol (contents, NULL, 10); + if (pid < 1 || pid > INT_MAX) + goto out; + + proc_path = g_strdup_printf ("/proc/%ld/cmdline", pid); + if (!g_file_get_contents (proc_path, &cmdline_contents, NULL, NULL)) + goto out; + + if (strstr (cmdline_contents, "bin/dnsmasq")) { + if (kill (pid, 0)) { + nm_log_dbg (LOGD_SHARING, "Killing stale dnsmasq process %ld", pid); + kill (pid, SIGKILL); + } + unlink (pidfile); + } + +out: + g_free (cmdline_contents); + g_free (proc_path); + g_free (contents); +} + +gboolean +nm_dnsmasq_manager_start (NMDnsMasqManager *manager, + NMIP4Config *ip4_config, + GError **error) +{ + NMDnsMasqManagerPrivate *priv; + NMCmdLine *dm_cmd; + char *cmd_str; + + g_return_val_if_fail (NM_IS_DNSMASQ_MANAGER (manager), FALSE); + if (error) + g_return_val_if_fail (*error == NULL, FALSE); + + priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (manager); + + kill_existing_for_iface (priv->iface, priv->pidfile); + + dm_cmd = create_dm_cmd_line (priv->iface, ip4_config, priv->pidfile, error); + if (!dm_cmd) + return FALSE; + + g_ptr_array_add (dm_cmd->array, NULL); + + nm_log_info (LOGD_SHARING, "Starting dnsmasq..."); + + cmd_str = nm_cmd_line_to_str (dm_cmd); + nm_log_dbg (LOGD_SHARING, "Command line: %s", cmd_str); + g_free (cmd_str); + + priv->pid = 0; + if (!g_spawn_async (NULL, (char **) dm_cmd->array->pdata, NULL, + G_SPAWN_DO_NOT_REAP_CHILD, + dm_child_setup, + NULL, &priv->pid, error)) { + goto out; + } + + nm_log_dbg (LOGD_SHARING, "dnsmasq started with pid %d", priv->pid); + + priv->dm_watch_id = g_child_watch_add (priv->pid, (GChildWatchFunc) dm_watch_cb, manager); + + out: + if (dm_cmd) + nm_cmd_line_destroy (dm_cmd); + + return priv->pid > 0; +} + +static gboolean +ensure_killed (gpointer data) +{ + int pid = GPOINTER_TO_INT (data); + + if (kill (pid, 0) == 0) + kill (pid, SIGKILL); + + /* ensure the child is reaped */ + nm_log_dbg (LOGD_SHARING, "waiting for dnsmasq pid %d to exit", pid); + waitpid (pid, NULL, 0); + nm_log_dbg (LOGD_SHARING, "dnsmasq pid %d cleaned up", pid); + + return FALSE; +} + +void +nm_dnsmasq_manager_stop (NMDnsMasqManager *manager) +{ + NMDnsMasqManagerPrivate *priv; + + g_return_if_fail (NM_IS_DNSMASQ_MANAGER (manager)); + + priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (manager); + + if (priv->dm_watch_id) { + g_source_remove (priv->dm_watch_id); + priv->dm_watch_id = 0; + } + + if (priv->pid) { + if (kill (priv->pid, SIGTERM) == 0) + g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid)); + else { + kill (priv->pid, SIGKILL); + + /* ensure the child is reaped */ + nm_log_dbg (LOGD_SHARING, "waiting for dnsmasq pid %d to exit", priv->pid); + waitpid (priv->pid, NULL, 0); + nm_log_dbg (LOGD_SHARING, "dnsmasq pid %d cleaned up", priv->pid); + } + + priv->pid = 0; + } + + unlink (priv->pidfile); +} diff --git a/src/dnsmasq-manager/nm-dnsmasq-manager.h b/src/dnsmasq-manager/nm-dnsmasq-manager.h new file mode 100644 index 00000000..1214d06b --- /dev/null +++ b/src/dnsmasq-manager/nm-dnsmasq-manager.h @@ -0,0 +1,69 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Red Hat, Inc. + */ + +#ifndef NM_DNSMASQ_MANAGER_H +#define NM_DNSMASQ_MANAGER_H + +#include <glib.h> +#include <glib-object.h> + +#include "nm-ip4-config.h" + +#define NM_TYPE_DNSMASQ_MANAGER (nm_dnsmasq_manager_get_type ()) +#define NM_DNSMASQ_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DNSMASQ_MANAGER, NMDnsMasqManager)) +#define NM_DNSMASQ_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DNSMASQ_MANAGER, NMDnsMasqManagerClass)) +#define NM_IS_DNSMASQ_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DNSMASQ_MANAGER)) +#define NM_IS_DNSMASQ_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DNSMASQ_MANAGER)) +#define NM_DNSMASQ_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DNSMASQ_MANAGER, NMDnsMasqManagerClass)) + +typedef enum { + NM_DNSMASQ_STATUS_UNKNOWN, + + NM_DNSMASQ_STATUS_DEAD, + NM_DNSMASQ_STATUS_RUNNING, +} NMDnsMasqStatus; + +typedef struct { + GObject parent; +} NMDnsMasqManager; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*state_changed) (NMDnsMasqManager *manager, NMDnsMasqStatus status); +} NMDnsMasqManagerClass; + +GType nm_dnsmasq_manager_get_type (void); + +NMDnsMasqManager *nm_dnsmasq_manager_new (const char *iface); + +gboolean nm_dnsmasq_manager_start (NMDnsMasqManager *manager, + NMIP4Config *ip4_config, + GError **error); + +void nm_dnsmasq_manager_stop (NMDnsMasqManager *manager); + +#define NM_DNSMASQ_MANAGER_ERROR nm_dnsmasq_manager_error_quark() +#define NM_TYPE_DNSMASQ_MANAGER_ERROR (nm_dnsmasq_manager_error_get_type ()) + +GQuark nm_dnsmasq_manager_error_quark (void); + +#endif /* NM_DNSMASQ_MANAGER_H */ diff --git a/src/gdb-cmd b/src/gdb-cmd new file mode 100644 index 00000000..aea32eb0 --- /dev/null +++ b/src/gdb-cmd @@ -0,0 +1,3 @@ +bt +thread apply all bt full +q diff --git a/src/ip6-manager/Makefile.am b/src/ip6-manager/Makefile.am new file mode 100644 index 00000000..c2f55914 --- /dev/null +++ b/src/ip6-manager/Makefile.am @@ -0,0 +1,26 @@ +INCLUDES = \ + -I${top_srcdir} \ + -I${top_srcdir}/include \ + -I${top_builddir}/marshallers \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/named-manager + +noinst_LTLIBRARIES = libip6-manager.la + +libip6_manager_la_SOURCES = \ + nm-ip6-manager.c \ + nm-ip6-manager.h + +libip6_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + -DG_DISABLE_DEPRECATED + +libip6_manager_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + diff --git a/src/ip6-manager/Makefile.in b/src/ip6-manager/Makefile.in new file mode 100644 index 00000000..ddb90e0d --- /dev/null +++ b/src/ip6-manager/Makefile.in @@ -0,0 +1,570 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/ip6-manager +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libip6_manager_la_DEPENDENCIES = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libip6_manager_la_OBJECTS = libip6_manager_la-nm-ip6-manager.lo +libip6_manager_la_OBJECTS = $(am_libip6_manager_la_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libip6_manager_la_SOURCES) +DIST_SOURCES = $(libip6_manager_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +IPTABLES_PATH = @IPTABLES_PATH@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBM = @LIBM@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POFILES = @POFILES@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +RANLIB = @RANLIB@ +RESOLVCONF_PATH = @RESOLVCONF_PATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = \ + -I${top_srcdir} \ + -I${top_srcdir}/include \ + -I${top_builddir}/marshallers \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/named-manager + +noinst_LTLIBRARIES = libip6-manager.la +libip6_manager_la_SOURCES = \ + nm-ip6-manager.c \ + nm-ip6-manager.h + +libip6_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + -DG_DISABLE_DEPRECATED + +libip6_manager_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/ip6-manager/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/ip6-manager/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libip6-manager.la: $(libip6_manager_la_OBJECTS) $(libip6_manager_la_DEPENDENCIES) + $(LINK) $(libip6_manager_la_OBJECTS) $(libip6_manager_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libip6_manager_la-nm-ip6-manager.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libip6_manager_la-nm-ip6-manager.lo: nm-ip6-manager.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libip6_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libip6_manager_la-nm-ip6-manager.lo -MD -MP -MF $(DEPDIR)/libip6_manager_la-nm-ip6-manager.Tpo -c -o libip6_manager_la-nm-ip6-manager.lo `test -f 'nm-ip6-manager.c' || echo '$(srcdir)/'`nm-ip6-manager.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libip6_manager_la-nm-ip6-manager.Tpo $(DEPDIR)/libip6_manager_la-nm-ip6-manager.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-ip6-manager.c' object='libip6_manager_la-nm-ip6-manager.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libip6_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libip6_manager_la-nm-ip6-manager.lo `test -f 'nm-ip6-manager.c' || echo '$(srcdir)/'`nm-ip6-manager.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/ip6-manager/nm-ip6-manager.c b/src/ip6-manager/nm-ip6-manager.c new file mode 100644 index 00000000..f6f6127c --- /dev/null +++ b/src/ip6-manager/nm-ip6-manager.c @@ -0,0 +1,1123 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-ip6-manager.c - Handle IPv6 address configuration for NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#include <errno.h> +#include <netinet/icmp6.h> + +#include <netlink/route/addr.h> +#include <netlink/route/rtnl.h> +#include <netlink/route/route.h> + +#include "nm-ip6-manager.h" +#include "nm-netlink-monitor.h" +#include "NetworkManagerUtils.h" +#include "nm-marshal.h" +#include "nm-logging.h" +#include "nm-system.h" + +/* Pre-DHCP addrconf timeout, in seconds */ +#define NM_IP6_TIMEOUT 20 + +/* FIXME? Stolen from the kernel sources */ +#define IF_RA_OTHERCONF 0x80 +#define IF_RA_MANAGED 0x40 +#define IF_RA_RCVD 0x20 +#define IF_RS_SENT 0x10 + +typedef struct { + NMNetlinkMonitor *monitor; + GHashTable *devices; + + struct nl_handle *nlh; + struct nl_cache *addr_cache, *route_cache; + + guint netlink_id; +} NMIP6ManagerPrivate; + +#define NM_IP6_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IP6_MANAGER, NMIP6ManagerPrivate)) + +G_DEFINE_TYPE (NMIP6Manager, nm_ip6_manager, G_TYPE_OBJECT) + +enum { + ADDRCONF_COMPLETE, + CONFIG_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef enum { + NM_IP6_DEVICE_UNCONFIGURED, + NM_IP6_DEVICE_GOT_LINK_LOCAL, + NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT, + NM_IP6_DEVICE_GOT_ADDRESS, + NM_IP6_DEVICE_TIMED_OUT +} NMIP6DeviceState; + +typedef struct { + struct in6_addr addr; + time_t expires; +} NMIP6RDNSS; + +/******************************************************************/ + +typedef struct { + NMIP6Manager *manager; + char *iface; + int ifindex; + + char *disable_ip6_path; + gboolean disable_ip6_save_valid; + guint32 disable_ip6_save; + + guint finish_addrconf_id; + guint config_changed_id; + + NMIP6DeviceState state; + NMIP6DeviceState target_state; + gboolean addrconf_complete; + + GArray *rdnss_servers; + guint rdnss_timeout_id; + + guint ip6flags_poll_id; + + guint32 ra_flags; +} NMIP6Device; + +static void +nm_ip6_device_destroy (NMIP6Device *device) +{ + g_return_if_fail (device != NULL); + + /* reset the saved IPv6 value */ + if (device->disable_ip6_save_valid) { + nm_utils_do_sysctl (device->disable_ip6_path, + device->disable_ip6_save ? "1\n" : "0\n"); + } + + if (device->finish_addrconf_id) + g_source_remove (device->finish_addrconf_id); + if (device->config_changed_id) + g_source_remove (device->config_changed_id); + g_free (device->iface); + if (device->rdnss_servers) + g_array_free (device->rdnss_servers, TRUE); + if (device->rdnss_timeout_id) + g_source_remove (device->rdnss_timeout_id); + if (device->ip6flags_poll_id) + g_source_remove (device->ip6flags_poll_id); + + g_slice_free (NMIP6Device, device); +} + +static NMIP6Device * +nm_ip6_device_new (NMIP6Manager *manager, int ifindex) +{ + NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager); + NMIP6Device *device; + + g_return_val_if_fail (ifindex > 0, NULL); + + device = g_slice_new0 (NMIP6Device); + if (!device) { + nm_log_err (LOGD_IP6, "(%d): out of memory creating IP6 addrconf object.", + ifindex); + return NULL; + } + + device->ifindex = ifindex; + device->iface = g_strdup (nm_netlink_index_to_iface (ifindex)); + if (!device->iface) { + nm_log_err (LOGD_IP6, "(%d): could not find interface name from index.", + ifindex); + goto error; + } + + device->manager = manager; + + device->rdnss_servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS)); + + g_hash_table_replace (priv->devices, GINT_TO_POINTER (device->ifindex), device); + + /* and the original value of IPv6 enable/disable */ + device->disable_ip6_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/disable_ipv6", + device->iface); + g_assert (device->disable_ip6_path); + device->disable_ip6_save_valid = nm_utils_get_proc_sys_net_value (device->disable_ip6_path, + device->iface, + &device->disable_ip6_save); + + return device; + +error: + nm_ip6_device_destroy (device); + return NULL; +} + +static NMIP6Device * +nm_ip6_manager_get_device (NMIP6Manager *manager, int ifindex) +{ + NMIP6ManagerPrivate *priv; + + g_return_val_if_fail (manager != NULL, NULL); + g_return_val_if_fail (NM_IS_IP6_MANAGER (manager), NULL); + + priv = NM_IP6_MANAGER_GET_PRIVATE (manager); + return g_hash_table_lookup (priv->devices, GINT_TO_POINTER (ifindex)); +} + +/******************************************************************/ + +typedef struct { + NMIP6Device *device; + guint dhcp_opts; + gboolean success; +} CallbackInfo; + +static gboolean +finish_addrconf (gpointer user_data) +{ + CallbackInfo *info = user_data; + NMIP6Device *device = info->device; + NMIP6Manager *manager = device->manager; + int ifindex; + + device->finish_addrconf_id = 0; + device->addrconf_complete = TRUE; + ifindex = device->ifindex; + + /* We're done, stop polling IPv6 flags */ + if (device->ip6flags_poll_id) { + g_source_remove (device->ip6flags_poll_id); + device->ip6flags_poll_id = 0; + } + + /* And tell listeners that addrconf is complete */ + if (info->success) { + g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0, + ifindex, info->dhcp_opts, TRUE); + } else { + nm_log_info (LOGD_IP6, "(%s): IP6 addrconf timed out or failed.", + device->iface); + + nm_ip6_manager_cancel_addrconf (manager, ifindex); + g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0, + ifindex, info->dhcp_opts, FALSE); + } + + return FALSE; +} + +static gboolean +emit_config_changed (gpointer user_data) +{ + CallbackInfo *info = user_data; + NMIP6Device *device = info->device; + NMIP6Manager *manager = device->manager; + + device->config_changed_id = 0; + g_signal_emit (manager, signals[CONFIG_CHANGED], 0, + device->ifindex, + info->dhcp_opts, + info->success); + return FALSE; +} + +static void set_rdnss_timeout (NMIP6Device *device); + +static gboolean +rdnss_expired (gpointer user_data) +{ + NMIP6Device *device = user_data; + CallbackInfo info = { device, IP6_DHCP_OPT_NONE }; + + nm_log_dbg (LOGD_IP6, "(%s): IPv6 RDNSS information expired", device->iface); + + set_rdnss_timeout (device); + emit_config_changed (&info); + return FALSE; +} + +static void +set_rdnss_timeout (NMIP6Device *device) +{ + time_t expires = 0, now = time (NULL); + NMIP6RDNSS *rdnss; + int i; + + if (device->rdnss_timeout_id) { + g_source_remove (device->rdnss_timeout_id); + device->rdnss_timeout_id = 0; + } + + /* Find the soonest expiration time. */ + for (i = 0; i < device->rdnss_servers->len; i++) { + rdnss = &g_array_index (device->rdnss_servers, NMIP6RDNSS, i); + if (rdnss->expires == 0) + continue; + + /* If the entry has already expired, remove it; the "+ 1" is + * because g_timeout_add_seconds() might fudge the timing a + * bit. + */ + if (rdnss->expires <= now + 1) { + char buf[INET6_ADDRSTRLEN + 1]; + + if (inet_ntop (AF_INET6, &(rdnss->addr), buf, sizeof (buf)) > 0) { + nm_log_dbg (LOGD_IP6, "(%s): removing expired RA-provided nameserver %s", + device->iface, buf); + } + g_array_remove_index_fast (device->rdnss_servers, i--); + continue; + } + + if (!expires || rdnss->expires < expires) + expires = rdnss->expires; + } + + if (expires) { + device->rdnss_timeout_id = g_timeout_add_seconds (expires - now, + rdnss_expired, + device); + } +} + +static CallbackInfo * +callback_info_new (NMIP6Device *device, guint dhcp_opts, gboolean success) +{ + CallbackInfo *info; + + info = g_malloc0 (sizeof (CallbackInfo)); + info->device = device; + info->dhcp_opts = dhcp_opts; + info->success = success; + return info; +} + +static const char * +state_to_string (NMIP6DeviceState state) +{ + switch (state) { + case NM_IP6_DEVICE_UNCONFIGURED: + return "unconfigured"; + case NM_IP6_DEVICE_GOT_LINK_LOCAL: + return "got-link-local"; + case NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT: + return "got-ra"; + case NM_IP6_DEVICE_GOT_ADDRESS: + return "got-address"; + case NM_IP6_DEVICE_TIMED_OUT: + return "timed-out"; + default: + return "unknown"; + } +} + +static void +nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed) +{ + NMIP6Manager *manager = device->manager; + NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager); + struct rtnl_addr *rtnladdr; + struct nl_addr *nladdr; + struct in6_addr *addr; + CallbackInfo *info; + guint dhcp_opts = IP6_DHCP_OPT_NONE; + gboolean found_linklocal = FALSE, found_other = FALSE; + + nm_log_dbg (LOGD_IP6, "(%s): syncing with netlink (ra_flags 0x%X) (state/target '%s'/'%s')", + device->iface, device->ra_flags, + state_to_string (device->state), + state_to_string (device->target_state)); + + /* Look for any IPv6 addresses the kernel may have set for the device */ + for (rtnladdr = (struct rtnl_addr *) nl_cache_get_first (priv->addr_cache); + rtnladdr; + rtnladdr = (struct rtnl_addr *) nl_cache_get_next ((struct nl_object *) rtnladdr)) { + char buf[INET6_ADDRSTRLEN]; + + if (rtnl_addr_get_ifindex (rtnladdr) != device->ifindex) + continue; + + nladdr = rtnl_addr_get_local (rtnladdr); + if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6) + continue; + + addr = nl_addr_get_binary_addr (nladdr); + + if (inet_ntop (AF_INET6, addr, buf, INET6_ADDRSTRLEN) > 0) { + nm_log_dbg (LOGD_IP6, "(%s): netlink address: %s", + device->iface, buf); + } + + if (IN6_IS_ADDR_LINKLOCAL (addr)) { + if (device->state == NM_IP6_DEVICE_UNCONFIGURED) + device->state = NM_IP6_DEVICE_GOT_LINK_LOCAL; + found_linklocal = TRUE; + } else { + if (device->state < NM_IP6_DEVICE_GOT_ADDRESS) + device->state = NM_IP6_DEVICE_GOT_ADDRESS; + found_other = TRUE; + } + } + + /* There might be a LL address hanging around on the interface from + * before in the initial run, but if it goes away later, make sure we + * regress from GOT_LINK_LOCAL back to UNCONFIGURED. + */ + if ((device->state == NM_IP6_DEVICE_GOT_LINK_LOCAL) && !found_linklocal) + device->state = NM_IP6_DEVICE_UNCONFIGURED; + + nm_log_dbg (LOGD_IP6, "(%s): addresses synced (state %s)", + device->iface, state_to_string (device->state)); + + /* We only care about router advertisements if we want a real IPv6 address */ + if ( (device->target_state == NM_IP6_DEVICE_GOT_ADDRESS) + && (device->ra_flags & IF_RA_RCVD)) { + + if (device->state < NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT) + device->state = NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT; + + if (device->ra_flags & IF_RA_MANAGED) { + dhcp_opts = IP6_DHCP_OPT_MANAGED; + nm_log_dbg (LOGD_IP6, "router advertisement deferred to DHCPv6"); + } else if (device->ra_flags & IF_RA_OTHERCONF) { + dhcp_opts = IP6_DHCP_OPT_OTHERCONF; + nm_log_dbg (LOGD_IP6, "router advertisement requests parallel DHCPv6"); + } + } + + if (!device->addrconf_complete) { + /* Managed mode (ie DHCP only) short-circuits automatic addrconf, so + * we don't bother waiting for the device's target state to be reached + * when the RA requests managed mode. + */ + if ( (device->state >= device->target_state) + || (dhcp_opts == IP6_DHCP_OPT_MANAGED)) { + /* device->finish_addrconf_id may currently be a timeout + * rather than an idle, so we remove the existing source. + */ + if (device->finish_addrconf_id) + g_source_remove (device->finish_addrconf_id); + + nm_log_dbg (LOGD_IP6, "(%s): reached target state or Managed-mode requested (state '%s') (dhcp opts 0x%X)", + device->iface, state_to_string (device->state), + dhcp_opts); + + info = callback_info_new (device, dhcp_opts, TRUE); + device->finish_addrconf_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + finish_addrconf, + info, + (GDestroyNotify) g_free); + } + } else if (config_changed) { + if (!device->config_changed_id) { + gboolean success = TRUE; + + /* If for some reason an RA-provided address disappeared, we need + * to make sure we fail the connection as it's no longer valid. + */ + if ( (device->state == NM_IP6_DEVICE_GOT_ADDRESS) + && (device->target_state == NM_IP6_DEVICE_GOT_ADDRESS) + && !found_other) { + nm_log_dbg (LOGD_IP6, "(%s): RA-provided address no longer valid", + device->iface); + success = FALSE; + } + + info = callback_info_new (device, dhcp_opts, success); + device->config_changed_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + emit_config_changed, + info, + (GDestroyNotify) g_free); + } + } +} + +static void +ref_object (struct nl_object *obj, void *data) +{ + struct nl_object **out = data; + + nl_object_get (obj); + *out = obj; +} + +static NMIP6Device * +process_addr (NMIP6Manager *manager, struct nl_msg *msg) +{ + NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager); + NMIP6Device *device; + struct rtnl_addr *rtnladdr; + int old_size; + + nm_log_dbg (LOGD_IP6, "processing netlink new/del address message"); + + rtnladdr = NULL; + nl_msg_parse (msg, ref_object, &rtnladdr); + if (!rtnladdr) { + nm_log_dbg (LOGD_IP6, "error processing netlink new/del address message"); + return NULL; + } + + device = nm_ip6_manager_get_device (manager, rtnl_addr_get_ifindex (rtnladdr)); + if (!device) { + nm_log_dbg (LOGD_IP6, "ignoring message for unknown device"); + return NULL; + } + + old_size = nl_cache_nitems (priv->addr_cache); + nl_cache_include (priv->addr_cache, (struct nl_object *)rtnladdr, NULL); + rtnl_addr_put (rtnladdr); + + /* The kernel will re-notify us of automatically-added addresses + * every time it gets another router advertisement. We only want + * to notify higher levels if we actually changed something. + */ + if (nl_cache_nitems (priv->addr_cache) == old_size) { + nm_log_dbg (LOGD_IP6, "(%s): address cache unchanged, ignoring message", + device->iface); + return NULL; + } + + return device; +} + +static NMIP6Device * +process_route (NMIP6Manager *manager, struct nl_msg *msg) +{ + NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager); + NMIP6Device *device; + struct rtnl_route *rtnlroute; + int old_size; + + nm_log_dbg (LOGD_IP6, "processing netlink new/del route message"); + + rtnlroute = NULL; + nl_msg_parse (msg, ref_object, &rtnlroute); + if (!rtnlroute) { + nm_log_dbg (LOGD_IP6, "error processing netlink new/del route message"); + return NULL; + } + + device = nm_ip6_manager_get_device (manager, rtnl_route_get_oif (rtnlroute)); + if (!device) { + nm_log_dbg (LOGD_IP6, "ignoring message for unknown device"); + return NULL; + } + + old_size = nl_cache_nitems (priv->route_cache); + nl_cache_include (priv->route_cache, (struct nl_object *)rtnlroute, NULL); + rtnl_route_put (rtnlroute); + + /* As above in process_addr */ + if (nl_cache_nitems (priv->route_cache) == old_size) { + nm_log_dbg (LOGD_IP6, "(%s): route cache unchanged, ignoring message", + device->iface); + return NULL; + } + + return device; +} + +static NMIP6Device * +process_prefix (NMIP6Manager *manager, struct nl_msg *msg) +{ + struct prefixmsg *pmsg; + NMIP6Device *device; + + /* We don't care about the prefix itself, but if we receive a + * router advertisement telling us to use DHCP, we might not + * get any RTM_NEWADDRs or RTM_NEWROUTEs, so this is our only + * way to notice immediately that an RA was received. + */ + + nm_log_dbg (LOGD_IP6, "processing netlink new prefix message"); + + pmsg = (struct prefixmsg *) NLMSG_DATA (nlmsg_hdr (msg)); + device = nm_ip6_manager_get_device (manager, pmsg->prefix_ifindex); + + if (!device || device->addrconf_complete) { + nm_log_dbg (LOGD_IP6, "(%s): ignoring unknown or completed device", + device ? device->iface : "(none)"); + return NULL; + } + + return device; +} + +/* RDNSS parsing code based on rdnssd, Copyright 2007 Pierre Ynard, + * Rémi Denis-Courmont. GPLv2/3 + */ + +#define ND_OPT_RDNSS 25 +struct nd_opt_rdnss { + uint8_t nd_opt_rdnss_type; + uint8_t nd_opt_rdnss_len; + uint16_t nd_opt_rdnss_reserved1; + uint32_t nd_opt_rdnss_lifetime; + /* followed by one or more IPv6 addresses */ +}; + +static NMIP6Device * +process_nduseropt (NMIP6Manager *manager, struct nl_msg *msg) +{ + NMIP6Device *device; + struct nduseroptmsg *ndmsg; + struct nd_opt_hdr *opt; + guint opts_len, i; + time_t now = time (NULL); + struct nd_opt_rdnss *rdnss_opt; + struct in6_addr *addr; + GArray *servers; + NMIP6RDNSS server, *sa, *sb; + gboolean changed; + + nm_log_dbg (LOGD_IP6, "processing netlink nduseropt message"); + + ndmsg = (struct nduseroptmsg *) NLMSG_DATA (nlmsg_hdr (msg)); + + if (ndmsg->nduseropt_family != AF_INET6 || + ndmsg->nduseropt_icmp_type != ND_ROUTER_ADVERT || + ndmsg->nduseropt_icmp_code != 0) { + nm_log_dbg (LOGD_IP6, "ignoring non-Router Advertisement message"); + return NULL; + } + + device = nm_ip6_manager_get_device (manager, ndmsg->nduseropt_ifindex); + if (!device) { + nm_log_dbg (LOGD_IP6, "ignoring message for unknown device"); + return NULL; + } + + servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS)); + + opt = (struct nd_opt_hdr *) (ndmsg + 1); + opts_len = ndmsg->nduseropt_opts_len; + + while (opts_len >= sizeof (struct nd_opt_hdr)) { + size_t nd_opt_len = opt->nd_opt_len; + + if (nd_opt_len == 0 || opts_len < (nd_opt_len << 3)) + break; + + if (opt->nd_opt_type != ND_OPT_RDNSS) + goto next; + + if (nd_opt_len < 3 || (nd_opt_len & 1) == 0) + goto next; + + rdnss_opt = (struct nd_opt_rdnss *) opt; + + /* Pad the DNS server expiry somewhat to give a bit of slack in cases + * where one RA gets lost or something (which can happen on unreliable + * links like wifi where certain types of frames are not retransmitted). + */ + server.expires = now + ntohl (rdnss_opt->nd_opt_rdnss_lifetime) + 10; + + for (addr = (struct in6_addr *) (rdnss_opt + 1); nd_opt_len >= 2; addr++, nd_opt_len -= 2) { + char buf[INET6_ADDRSTRLEN + 1]; + + if (inet_ntop (AF_INET6, addr, buf, sizeof (buf))) { + nm_log_dbg (LOGD_IP6, "(%s): found RA-provided nameserver %s (expires in %d seconds)", + device->iface, buf, + ntohl (rdnss_opt->nd_opt_rdnss_lifetime)); + } + + server.addr = *addr; + g_array_append_val (servers, server); + } + + next: + opts_len -= opt->nd_opt_len << 3; + opt = (struct nd_opt_hdr *) ((uint8_t *) opt + (opt->nd_opt_len << 3)); + } + + /* See if anything (other than expiration time) changed */ + if (servers->len != device->rdnss_servers->len) + changed = TRUE; + else { + for (i = 0; i < servers->len; i++) { + sa = &(g_array_index (servers, NMIP6RDNSS, i)); + sb = &(g_array_index (device->rdnss_servers, NMIP6RDNSS, i)); + if (IN6_ARE_ADDR_EQUAL (&sa->addr, &sb->addr) == FALSE) { + changed = TRUE; + break; + } + } + changed = FALSE; + } + + if (changed) { + nm_log_dbg (LOGD_IP6, "(%s): RA-provided nameservers changed", device->iface); + } + + /* Always copy in new servers (even if unchanged) to get their updated + * expiration times. + */ + g_array_free (device->rdnss_servers, TRUE); + device->rdnss_servers = servers; + + /* Timeouts may have changed even if IPs didn't */ + set_rdnss_timeout (device); + + if (changed) + return device; + else + return NULL; +} + +static struct nla_policy link_policy[IFLA_MAX + 1] = { + [IFLA_PROTINFO] = { .type = NLA_NESTED }, +}; + +static struct nla_policy link_prot_policy[IFLA_INET6_MAX + 1] = { + [IFLA_INET6_FLAGS] = { .type = NLA_U32 }, +}; + +static NMIP6Device * +process_newlink (NMIP6Manager *manager, struct nl_msg *msg) +{ + struct nlmsghdr *hdr = nlmsg_hdr (msg); + struct ifinfomsg *ifi; + NMIP6Device *device; + struct nlattr *tb[IFLA_MAX + 1]; + struct nlattr *pi[IFLA_INET6_MAX + 1]; + int err; + + ifi = nlmsg_data (hdr); + if (ifi->ifi_family != AF_INET6) { + nm_log_dbg (LOGD_IP6, "ignoring netlink message family %d", ifi->ifi_family); + return NULL; + } + + device = nm_ip6_manager_get_device (manager, ifi->ifi_index); + if (!device || device->addrconf_complete) { + nm_log_dbg (LOGD_IP6, "(%s): ignoring unknown or completed device", + device ? device->iface : "(none)"); + return NULL; + } + + /* FIXME: we have to do this manually for now since libnl doesn't yet + * support the IFLA_PROTINFO attribute of NEWLINK messages. When it does, + * we can get rid of this function and just grab IFLA_PROTINFO from + * nm_ip6_device_sync_from_netlink(), then get the IFLA_INET6_FLAGS out of + * the PROTINFO. + */ + + err = nlmsg_parse (hdr, sizeof (*ifi), tb, IFLA_MAX, link_policy); + if (err < 0) { + nm_log_dbg (LOGD_IP6, "(%s): error parsing PROTINFO attribute", device->iface); + return NULL; + } + if (!tb[IFLA_PROTINFO]) { + nm_log_dbg (LOGD_IP6, "(%s): message had no PROTINFO attribute", device->iface); + return NULL; + } + + err = nla_parse_nested (pi, IFLA_INET6_MAX, tb[IFLA_PROTINFO], link_prot_policy); + if (err < 0) { + nm_log_dbg (LOGD_IP6, "(%s): error parsing PROTINFO flags", device->iface); + return NULL; + } + if (!pi[IFLA_INET6_FLAGS]) { + nm_log_dbg (LOGD_IP6, "(%s): message had no PROTINFO flags", device->iface); + return NULL; + } + + device->ra_flags = nla_get_u32 (pi[IFLA_INET6_FLAGS]); + nm_log_dbg (LOGD_IP6, "(%s): got IPv6 flags 0x%X", device->iface, device->ra_flags); + + return device; +} + +static void +netlink_notification (NMNetlinkMonitor *monitor, struct nl_msg *msg, gpointer user_data) +{ + NMIP6Manager *manager = (NMIP6Manager *) user_data; + NMIP6Device *device; + struct nlmsghdr *hdr; + gboolean config_changed = FALSE; + + hdr = nlmsg_hdr (msg); + nm_log_dbg (LOGD_HW, "netlink notificate type %d", hdr->nlmsg_type); + switch (hdr->nlmsg_type) { + case RTM_NEWADDR: + case RTM_DELADDR: + device = process_addr (manager, msg); + config_changed = TRUE; + break; + case RTM_NEWROUTE: + case RTM_DELROUTE: + device = process_route (manager, msg); + config_changed = TRUE; + break; + case RTM_NEWPREFIX: + device = process_prefix (manager, msg); + break; + case RTM_NEWNDUSEROPT: + device = process_nduseropt (manager, msg); + config_changed = TRUE; + break; + case RTM_NEWLINK: + device = process_newlink (manager, msg); + config_changed = TRUE; + break; + default: + return; + } + + if (device) { + nm_log_dbg (LOGD_IP6, "(%s): syncing device with netlink changes", device->iface); + nm_ip6_device_sync_from_netlink (device, config_changed); + } +} + +void +nm_ip6_manager_prepare_interface (NMIP6Manager *manager, + int ifindex, + NMSettingIP6Config *s_ip6, + const char *accept_ra_path) +{ + NMIP6ManagerPrivate *priv; + NMIP6Device *device; + const char *method = NULL; + + g_return_if_fail (NM_IS_IP6_MANAGER (manager)); + g_return_if_fail (ifindex > 0); + + priv = NM_IP6_MANAGER_GET_PRIVATE (manager); + + device = nm_ip6_device_new (manager, ifindex); + g_return_if_fail (device != NULL); + g_return_if_fail ( strchr (device->iface, '/') == NULL + && strcmp (device->iface, "all") != 0 + && strcmp (device->iface, "default") != 0); + + if (s_ip6) + method = nm_setting_ip6_config_get_method (s_ip6); + if (!method) + method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; + + /* Establish target state and turn router advertisement acceptance on or off */ + if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) { + device->target_state = NM_IP6_DEVICE_GOT_LINK_LOCAL; + nm_utils_do_sysctl (accept_ra_path, "0\n"); + } else { + device->target_state = NM_IP6_DEVICE_GOT_ADDRESS; + nm_utils_do_sysctl (accept_ra_path, "1\n"); + } +} + +static gboolean +poll_ip6_flags (gpointer user_data) +{ + nm_netlink_monitor_request_ip6_info (NM_NETLINK_MONITOR (user_data), NULL); + return TRUE; +} + +void +nm_ip6_manager_begin_addrconf (NMIP6Manager *manager, int ifindex) +{ + NMIP6ManagerPrivate *priv; + NMIP6Device *device; + CallbackInfo *info; + + g_return_if_fail (NM_IS_IP6_MANAGER (manager)); + g_return_if_fail (ifindex > 0); + + priv = NM_IP6_MANAGER_GET_PRIVATE (manager); + + device = (NMIP6Device *) g_hash_table_lookup (priv->devices, GINT_TO_POINTER (ifindex)); + g_return_if_fail (device != NULL); + + nm_log_info (LOGD_IP6, "Activation (%s) Beginning IP6 addrconf.", device->iface); + + device->addrconf_complete = FALSE; + device->ra_flags = 0; + + /* Set up a timeout on the transaction to kill it after the timeout */ + info = callback_info_new (device, 0, FALSE); + device->finish_addrconf_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, + NM_IP6_TIMEOUT, + finish_addrconf, + info, + (GDestroyNotify) g_free); + + /* Bounce IPv6 on the interface to ensure the kernel will start looking for + * new RAs; there doesn't seem to be a better way to do this right now. + */ + if (device->target_state >= NM_IP6_DEVICE_GOT_ADDRESS) { + nm_utils_do_sysctl (device->disable_ip6_path, "1\n"); + g_usleep (200); + nm_utils_do_sysctl (device->disable_ip6_path, "0\n"); + } + + device->ip6flags_poll_id = g_timeout_add_seconds (1, poll_ip6_flags, priv->monitor); + + /* Kick off the initial IPv6 flags request */ + nm_netlink_monitor_request_ip6_info (priv->monitor, NULL); + + /* Sync flags, etc, from netlink; this will also notice if the + * device is already fully configured and schedule the + * ADDRCONF_COMPLETE signal in that case. + */ + nm_ip6_device_sync_from_netlink (device, FALSE); +} + +void +nm_ip6_manager_cancel_addrconf (NMIP6Manager *manager, int ifindex) +{ + g_return_if_fail (NM_IS_IP6_MANAGER (manager)); + g_return_if_fail (ifindex > 0); + + g_hash_table_remove (NM_IP6_MANAGER_GET_PRIVATE (manager)->devices, + GINT_TO_POINTER (ifindex)); +} + +#define FIRST_ROUTE(m) ((struct rtnl_route *) nl_cache_get_first (m)) +#define NEXT_ROUTE(m) ((struct rtnl_route *) nl_cache_get_next ((struct nl_object *) m)) + +#define FIRST_ADDR(m) ((struct rtnl_addr *) nl_cache_get_first (m)) +#define NEXT_ADDR(m) ((struct rtnl_addr *) nl_cache_get_next ((struct nl_object *) m)) + +NMIP6Config * +nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, int ifindex) +{ + NMIP6ManagerPrivate *priv; + NMIP6Device *device; + NMIP6Config *config; + struct rtnl_addr *rtnladdr; + struct nl_addr *nladdr; + struct in6_addr *addr; + NMIP6Address *ip6addr; + struct rtnl_route *rtnlroute; + struct nl_addr *nldest, *nlgateway; + struct in6_addr *dest, *gateway; + gboolean defgw_set = FALSE; + struct in6_addr defgw; + uint32_t metric; + NMIP6Route *ip6route; + int i; + + g_return_val_if_fail (NM_IS_IP6_MANAGER (manager), NULL); + g_return_val_if_fail (ifindex > 0, NULL); + + priv = NM_IP6_MANAGER_GET_PRIVATE (manager); + + device = (NMIP6Device *) g_hash_table_lookup (priv->devices, + GINT_TO_POINTER (ifindex)); + if (!device) { + nm_log_warn (LOGD_IP6, "(%d): addrconf not started.", ifindex); + return NULL; + } + + config = nm_ip6_config_new (); + if (!config) { + nm_log_err (LOGD_IP6, "(%s): out of memory creating IP6 config object.", + device->iface); + return NULL; + } + + /* Make sure we refill the route and address caches, otherwise we won't get + * up-to-date information here since the netlink route/addr change messages + * may be lagging a bit. + */ + nl_cache_refill (priv->nlh, priv->route_cache); + nl_cache_refill (priv->nlh, priv->addr_cache); + + /* Add routes */ + for (rtnlroute = FIRST_ROUTE (priv->route_cache); rtnlroute; rtnlroute = NEXT_ROUTE (rtnlroute)) { + /* Make sure it's an IPv6 route for this device */ + if (rtnl_route_get_oif (rtnlroute) != device->ifindex) + continue; + if (rtnl_route_get_family (rtnlroute) != AF_INET6) + continue; + + nldest = rtnl_route_get_dst (rtnlroute); + if (!nldest || nl_addr_get_family (nldest) != AF_INET6) + continue; + dest = nl_addr_get_binary_addr (nldest); + + nlgateway = rtnl_route_get_gateway (rtnlroute); + if (!nlgateway || nl_addr_get_family (nlgateway) != AF_INET6) + continue; + gateway = nl_addr_get_binary_addr (nlgateway); + + if (rtnl_route_get_dst_len (rtnlroute) == 0) { + /* Default gateway route; don't add to normal routes but to each address */ + if (!defgw_set) { + memcpy (&defgw, gateway, sizeof (defgw)); + defgw_set = TRUE; + } + continue; + } + + /* Also ignore link-local routes where the destination and gateway are + * the same, which apparently get added by the kernel but return -EINVAL + * when we try to add them via netlink. + */ + if (gateway && IN6_ARE_ADDR_EQUAL (dest, gateway)) + continue; + + ip6route = nm_ip6_route_new (); + nm_ip6_route_set_dest (ip6route, dest); + nm_ip6_route_set_prefix (ip6route, rtnl_route_get_dst_len (rtnlroute)); + nm_ip6_route_set_next_hop (ip6route, gateway); + metric = rtnl_route_get_metric (rtnlroute, 1); + if (metric != UINT_MAX) + nm_ip6_route_set_metric (ip6route, metric); + nm_ip6_config_take_route (config, ip6route); + } + + /* Add addresses */ + for (rtnladdr = FIRST_ADDR (priv->addr_cache); rtnladdr; rtnladdr = NEXT_ADDR (rtnladdr)) { + if (rtnl_addr_get_ifindex (rtnladdr) != device->ifindex) + continue; + + nladdr = rtnl_addr_get_local (rtnladdr); + if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6) + continue; + + addr = nl_addr_get_binary_addr (nladdr); + ip6addr = nm_ip6_address_new (); + nm_ip6_address_set_prefix (ip6addr, rtnl_addr_get_prefixlen (rtnladdr)); + nm_ip6_address_set_address (ip6addr, addr); + nm_ip6_config_take_address (config, ip6addr); + if (defgw_set) + nm_ip6_address_set_gateway (ip6addr, &defgw); + } + + /* Add DNS servers */ + if (device->rdnss_servers) { + NMIP6RDNSS *rdnss = (NMIP6RDNSS *)(device->rdnss_servers->data); + + for (i = 0; i < device->rdnss_servers->len; i++) + nm_ip6_config_add_nameserver (config, &rdnss[i].addr); + } + + return config; +} + +/******************************************************************/ + +static NMIP6Manager * +nm_ip6_manager_new (void) +{ + NMIP6Manager *manager; + NMIP6ManagerPrivate *priv; + + manager = g_object_new (NM_TYPE_IP6_MANAGER, NULL); + priv = NM_IP6_MANAGER_GET_PRIVATE (manager); + + if (!priv->devices) { + nm_log_err (LOGD_IP6, "not enough memory to initialize IP6 manager tables"); + g_object_unref (manager); + manager = NULL; + } + + return manager; +} + +static NMIP6Manager *singleton = NULL; + +NMIP6Manager * +nm_ip6_manager_get (void) +{ + if (!singleton) { + singleton = nm_ip6_manager_new (); + g_assert (singleton); + } else + g_object_ref (singleton); + + return singleton; +} + +static void +nm_ip6_manager_init (NMIP6Manager *manager) +{ + NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager); + + priv->devices = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, + (GDestroyNotify) nm_ip6_device_destroy); + + priv->monitor = nm_netlink_monitor_get (); + nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_IPV6_IFADDR, NULL); + nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_IPV6_PREFIX, NULL); + nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_ND_USEROPT, NULL); + nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_LINK, NULL); + + priv->netlink_id = g_signal_connect (priv->monitor, "notification", + G_CALLBACK (netlink_notification), manager); + + priv->nlh = nm_netlink_get_default_handle (); + priv->addr_cache = rtnl_addr_alloc_cache (priv->nlh); + priv->route_cache = rtnl_route_alloc_cache (priv->nlh); +} + +static void +finalize (GObject *object) +{ + NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (object); + + g_signal_handler_disconnect (priv->monitor, priv->netlink_id); + + g_hash_table_destroy (priv->devices); + g_object_unref (priv->monitor); + nl_cache_free (priv->addr_cache); + nl_cache_free (priv->route_cache); + + singleton = NULL; + + G_OBJECT_CLASS (nm_ip6_manager_parent_class)->finalize (object); +} + +static void +nm_ip6_manager_class_init (NMIP6ManagerClass *manager_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (manager_class); + + g_type_class_add_private (manager_class, sizeof (NMIP6ManagerPrivate)); + + /* virtual methods */ + object_class->finalize = finalize; + + /* signals */ + signals[ADDRCONF_COMPLETE] = + g_signal_new ("addrconf-complete", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMIP6ManagerClass, addrconf_complete), + NULL, NULL, + _nm_marshal_VOID__INT_UINT_BOOLEAN, + G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_UINT, G_TYPE_BOOLEAN); + + signals[CONFIG_CHANGED] = + g_signal_new ("config-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMIP6ManagerClass, config_changed), + NULL, NULL, + _nm_marshal_VOID__INT_UINT_BOOLEAN, + G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_UINT, G_TYPE_BOOLEAN); +} + diff --git a/src/ip6-manager/nm-ip6-manager.h b/src/ip6-manager/nm-ip6-manager.h new file mode 100644 index 00000000..77c11068 --- /dev/null +++ b/src/ip6-manager/nm-ip6-manager.h @@ -0,0 +1,86 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-ip6-manager.c - Handle IPv6 address configuration for NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#ifndef NM_IP6_MANAGER_H +#define NM_IP6_MANAGER_H + +#include <glib.h> +#include <glib-object.h> + +#include <nm-setting-ip6-config.h> + +#include "nm-ip6-config.h" + +#define NM_TYPE_IP6_MANAGER (nm_ip6_manager_get_type ()) +#define NM_IP6_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IP6_MANAGER, NMIP6Manager)) +#define NM_IP6_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IP6_MANAGER, NMIP6ManagerClass)) +#define NM_IS_IP6_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IP6_MANAGER)) +#define NM_IS_IP6_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_IP6_MANAGER)) +#define NM_IP6_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IP6_MANAGER, NMIP6ManagerClass)) + +enum { + IP6_DHCP_OPT_NONE = 0, + IP6_DHCP_OPT_OTHERCONF, + IP6_DHCP_OPT_MANAGED +}; + +typedef struct { + GObject parent; +} NMIP6Manager; + +typedef struct { + GObjectClass parent; + + /* Signals */ + + /* addrconf_complete is emitted only during initial configuration to indicate + * that the initial configuration is complete. + */ + void (*addrconf_complete) (NMIP6Manager *manager, + guint32 ifindex, + guint dhcp_opts, + gboolean success); + + /* config_changed gets emitted only *after* initial configuration is + * complete; it's like DHCP renew and indicates that the existing config + * of the interface has changed. + */ + void (*config_changed) (NMIP6Manager *manager, + guint32 ifindex, + guint dhcp_opts, + gboolean success); +} NMIP6ManagerClass; + +GType nm_ip6_manager_get_type (void); + +NMIP6Manager *nm_ip6_manager_get (void); +void nm_ip6_manager_prepare_interface (NMIP6Manager *manager, + int ifindex, + NMSettingIP6Config *s_ip6, + const char *accept_ra_path); +void nm_ip6_manager_begin_addrconf (NMIP6Manager *manager, + int ifindex); +void nm_ip6_manager_cancel_addrconf (NMIP6Manager *manager, + int ifindex); + +NMIP6Config * nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, + int ifindex); + +#endif /* NM_IP6_MANAGER_H */ diff --git a/src/logging/Makefile.am b/src/logging/Makefile.am new file mode 100644 index 00000000..51a6b25d --- /dev/null +++ b/src/logging/Makefile.am @@ -0,0 +1,15 @@ +noinst_LTLIBRARIES = libnm-logging.la + +libnm_logging_la_SOURCES = \ + nm-logging.c \ + nm-logging.h + +libnm_logging_la_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DG_DISABLE_DEPRECATED + +libnm_logging_la_LIBADD = \ + -ldl \ + $(GLIB_LIBS) + diff --git a/src/logging/Makefile.in b/src/logging/Makefile.in new file mode 100644 index 00000000..8cbe2922 --- /dev/null +++ b/src/logging/Makefile.in @@ -0,0 +1,556 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/logging +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libnm_logging_la_DEPENDENCIES = $(am__DEPENDENCIES_1) +am_libnm_logging_la_OBJECTS = libnm_logging_la-nm-logging.lo +libnm_logging_la_OBJECTS = $(am_libnm_logging_la_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libnm_logging_la_SOURCES) +DIST_SOURCES = $(libnm_logging_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +IPTABLES_PATH = @IPTABLES_PATH@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBM = @LIBM@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POFILES = @POFILES@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +RANLIB = @RANLIB@ +RESOLVCONF_PATH = @RESOLVCONF_PATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_LTLIBRARIES = libnm-logging.la +libnm_logging_la_SOURCES = \ + nm-logging.c \ + nm-logging.h + +libnm_logging_la_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DG_DISABLE_DEPRECATED + +libnm_logging_la_LIBADD = \ + -ldl \ + $(GLIB_LIBS) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/logging/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/logging/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libnm-logging.la: $(libnm_logging_la_OBJECTS) $(libnm_logging_la_DEPENDENCIES) + $(LINK) $(libnm_logging_la_OBJECTS) $(libnm_logging_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_logging_la-nm-logging.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libnm_logging_la-nm-logging.lo: nm-logging.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_logging_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_logging_la-nm-logging.lo -MD -MP -MF $(DEPDIR)/libnm_logging_la-nm-logging.Tpo -c -o libnm_logging_la-nm-logging.lo `test -f 'nm-logging.c' || echo '$(srcdir)/'`nm-logging.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnm_logging_la-nm-logging.Tpo $(DEPDIR)/libnm_logging_la-nm-logging.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-logging.c' object='libnm_logging_la-nm-logging.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_logging_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_logging_la-nm-logging.lo `test -f 'nm-logging.c' || echo '$(srcdir)/'`nm-logging.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/logging/nm-logging.c b/src/logging/nm-logging.c new file mode 100644 index 00000000..9d1905da --- /dev/null +++ b/src/logging/nm-logging.c @@ -0,0 +1,377 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2010 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#define _GNU_SOURCE +#include <dlfcn.h> +#include <syslog.h> +#include <execinfo.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <execinfo.h> +#include <strings.h> +#include <string.h> + +#include <glib/gi18n.h> + +#include "nm-logging.h" + +static guint32 log_level = LOGL_INFO | LOGL_WARN | LOGL_ERR; +static guint32 log_domains = \ + LOGD_HW | LOGD_RFKILL | LOGD_ETHER | LOGD_WIFI | LOGD_BT | LOGD_MB | \ + LOGD_DHCP4 | LOGD_DHCP6 | LOGD_PPP | LOGD_IP4 | LOGD_IP6 | LOGD_AUTOIP4 | \ + LOGD_DNS | LOGD_VPN | LOGD_SHARING | LOGD_SUPPLICANT | LOGD_USER_SET | \ + LOGD_SYS_SET | LOGD_SUSPEND | LOGD_CORE | LOGD_DEVICE | LOGD_OLPC_MESH; + +typedef struct { + guint32 num; + const char *name; +} LogDesc; + +static const LogDesc level_descs[] = { + { LOGL_ERR, "ERR" }, + { LOGL_WARN | LOGL_ERR, "WARN" }, + { LOGL_INFO | LOGL_WARN | LOGL_ERR, "INFO" }, + { LOGL_DEBUG | LOGL_INFO | LOGL_WARN | LOGL_ERR, "DEBUG" }, + { 0, NULL } +}; + +static const LogDesc domain_descs[] = { + { LOGD_NONE, "NONE" }, + { LOGD_HW, "HW" }, + { LOGD_RFKILL, "RFKILL" }, + { LOGD_ETHER, "ETHER" }, + { LOGD_WIFI, "WIFI" }, + { LOGD_BT, "BT" }, + { LOGD_MB, "MB" }, + { LOGD_DHCP4, "DHCP4" }, + { LOGD_DHCP6, "DHCP6" }, + { LOGD_PPP, "PPP" }, + { LOGD_WIFI_SCAN, "WIFI_SCAN" }, + { LOGD_IP4, "IP4" }, + { LOGD_IP6, "IP6" }, + { LOGD_AUTOIP4, "AUTOIP4" }, + { LOGD_DNS, "DNS" }, + { LOGD_VPN, "VPN" }, + { LOGD_SHARING, "SHARING" }, + { LOGD_SUPPLICANT,"SUPPLICANT" }, + { LOGD_USER_SET, "USER_SET" }, + { LOGD_SYS_SET, "SYS_SET" }, + { LOGD_SUSPEND, "SUSPEND" }, + { LOGD_CORE, "CORE" }, + { LOGD_DEVICE, "DEVICE" }, + { LOGD_OLPC_MESH, "OLPC" }, + { 0, NULL } +}; + +/************************************************************************/ + +enum { + NM_LOGGING_ERROR_UNKNOWN_LEVEL = 0, + NM_LOGGING_ERROR_UNKNOWN_DOMAIN = 1, +}; + +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GQuark +nm_logging_error_quark (void) +{ + static GQuark ret = 0; + + if (ret == 0) + ret = g_quark_from_static_string ("nm_logging_error"); + return ret; +} + +GType +nm_logging_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (NM_LOGGING_ERROR_UNKNOWN_LEVEL, "UnknownLevel"), + ENUM_ENTRY (NM_LOGGING_ERROR_UNKNOWN_DOMAIN, "UnknownDomain"), + { 0, 0, 0 } + }; + etype = g_enum_register_static ("NMLoggingError", values); + } + return etype; +} + +/************************************************************************/ + +gboolean +nm_logging_setup (const char *level, const char *domains, GError **error) +{ + char **tmp, **iter; + guint32 new_domains = 0; + + /* levels */ + if (level && strlen (level)) { + gboolean found = FALSE; + const LogDesc *diter; + + for (diter = &level_descs[0]; diter->name; diter++) { + if (!strcasecmp (diter->name, level)) { + log_level = diter->num; + found = TRUE; + break; + } + } + + if (!found) { + g_set_error (error, NM_LOGGING_ERROR, NM_LOGGING_ERROR_UNKNOWN_LEVEL, + _("Unknown log level '%s'"), level); + return FALSE; + } + } + + /* domains */ + if (domains && strlen (domains)) { + tmp = g_strsplit_set (domains, ", ", 0); + for (iter = tmp; iter && *iter; iter++) { + const LogDesc *diter; + gboolean found = FALSE; + + if (!strlen (*iter)) + continue; + + for (diter = &domain_descs[0]; diter->name; diter++) { + if (!strcasecmp (diter->name, *iter)) { + new_domains |= diter->num; + found = TRUE; + break; + } + } + + if (!found) { + g_set_error (error, NM_LOGGING_ERROR, NM_LOGGING_ERROR_UNKNOWN_DOMAIN, + _("Unknown log domain '%s'"), *iter); + return FALSE; + } + } + g_strfreev (tmp); + log_domains = new_domains; + } + + return TRUE; +} + +const char * +nm_logging_level_to_string (void) +{ + const LogDesc *diter; + + for (diter = &level_descs[0]; diter->name; diter++) { + if (diter->num == log_level) + return diter->name; + } + g_warn_if_reached (); + return ""; +} + +char * +nm_logging_domains_to_string (void) +{ + const LogDesc *diter; + GString *str; + + str = g_string_sized_new (75); + for (diter = &domain_descs[0]; diter->name; diter++) { + if (diter->num & log_domains) { + if (str->len) + g_string_append_c (str, ','); + g_string_append (str, diter->name); + } + } + return g_string_free (str, FALSE); +} + +void _nm_log (const char *loc, + const char *func, + guint32 domain, + guint32 level, + const char *fmt, + ...) +{ + va_list args; + char *msg; + GTimeVal tv; + + if (!(log_level & level) || !(log_domains & domain)) + return; + + va_start (args, fmt); + msg = g_strdup_vprintf (fmt, args); + va_end (args); + + if ((log_level & LOGL_DEBUG) && (level == LOGL_DEBUG)) { + g_get_current_time (&tv); + syslog (LOG_INFO, "<debug> [%ld.%ld] [%s] %s(): %s\n", tv.tv_sec, tv.tv_usec, loc, func, msg); + } else if ((log_level & LOGL_INFO) && (level == LOGL_INFO)) + syslog (LOG_INFO, "<info> %s\n", msg); + else if ((log_level & LOGL_WARN) && (level == LOGL_WARN)) + syslog (LOG_WARNING, "<warn> %s\n", msg); + else if ((log_level & LOGL_ERR) && (level == LOGL_ERR)) { + g_get_current_time (&tv); + syslog (LOG_ERR, "<error> [%ld.%ld] [%s] %s(): %s\n", tv.tv_sec, tv.tv_usec, loc, func, msg); + } + g_free (msg); +} + +/************************************************************************/ + +static void +fallback_get_backtrace (void) +{ + void *frames[64]; + Dl_info info; + size_t size; + guint32 i; + const char *name; + + size = backtrace (frames, G_N_ELEMENTS (frames)); + + syslog (LOG_CRIT, "******************* START **********************************"); + for (i = 0; i < size; i++) { + dladdr (frames[i], &info); + name = (info.dli_fname && *info.dli_fname) ? info.dli_fname : "(vdso)"; + if (info.dli_saddr) { + syslog (LOG_CRIT, "Frame %d: %s (%s+0x%lx) [%p]", + i, name, + info.dli_sname, + (gulong)(frames[i] - info.dli_saddr), + frames[i]); + } else { + syslog (LOG_CRIT, "Frame %d: %s (%p+0x%lx) [%p]", + i, name, + info.dli_fbase, + (gulong)(frames[i] - info.dli_saddr), + frames[i]); + } + } + syslog (LOG_CRIT, "******************* END **********************************"); +} + + +static gboolean +crashlogger_get_backtrace (void) +{ + gboolean success = FALSE; + int pid; + + pid = fork(); + if (pid > 0) + { + /* Wait for the child to finish */ + int estatus; + if (waitpid (pid, &estatus, 0) != -1) + { + /* Only succeed if the crashlogger succeeded */ + if (WIFEXITED (estatus) && (WEXITSTATUS (estatus) == 0)) + success = TRUE; + } + } + else if (pid == 0) + { + /* Child process */ + execl (LIBEXECDIR"/nm-crash-logger", + LIBEXECDIR"/nm-crash-logger", NULL); + } + + return success; +} + + +void +nm_logging_backtrace (void) +{ + struct stat s; + gboolean fallback = TRUE; + + /* Try to use gdb via nm-crash-logger if it exists, since + * we get much better information out of it. Otherwise + * fall back to execinfo. + */ + if (stat (LIBEXECDIR"/nm-crash-logger", &s) == 0) + fallback = crashlogger_get_backtrace () ? FALSE : TRUE; + + if (fallback) + fallback_get_backtrace (); +} + + +static void +nm_log_handler (const gchar *log_domain, + GLogLevelFlags level, + const gchar *message, + gpointer ignored) +{ + int syslog_priority; + + switch (level) { + case G_LOG_LEVEL_ERROR: + syslog_priority = LOG_CRIT; + break; + case G_LOG_LEVEL_CRITICAL: + syslog_priority = LOG_ERR; + break; + case G_LOG_LEVEL_WARNING: + syslog_priority = LOG_WARNING; + break; + case G_LOG_LEVEL_MESSAGE: + syslog_priority = LOG_NOTICE; + break; + case G_LOG_LEVEL_DEBUG: + syslog_priority = LOG_DEBUG; + break; + case G_LOG_LEVEL_INFO: + default: + syslog_priority = LOG_INFO; + break; + } + + syslog (syslog_priority, "%s", message); +} + +void +nm_logging_start (gboolean become_daemon) +{ + if (become_daemon) + openlog (G_LOG_DOMAIN, LOG_PID, LOG_DAEMON); + else + openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR | LOG_PID, LOG_USER); + + g_log_set_handler (G_LOG_DOMAIN, + G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, + nm_log_handler, + NULL); +} + +void +nm_logging_shutdown (void) +{ + closelog (); +} diff --git a/src/logging/nm-logging.h b/src/logging/nm-logging.h new file mode 100644 index 00000000..2eedf5ff --- /dev/null +++ b/src/logging/nm-logging.h @@ -0,0 +1,111 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2010 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef NM_LOGGING_H +#define NM_LOGGING_H + +#include <glib.h> +#include <glib-object.h> + +/* Log domains */ +enum { + LOGD_NONE = 0x00000000, + LOGD_HW = 0x00000001, /* Hardware detection and info */ + LOGD_RFKILL = 0x00000002, + LOGD_ETHER = 0x00000004, + LOGD_WIFI = 0x00000008, + LOGD_BT = 0x00000010, + LOGD_MB = 0x00000020, /* mobile broadband */ + LOGD_DHCP4 = 0x00000040, + LOGD_DHCP6 = 0x00000080, + LOGD_PPP = 0x00000100, + LOGD_WIFI_SCAN = 0x00000200, + LOGD_IP4 = 0x00000400, + LOGD_IP6 = 0x00000800, + LOGD_AUTOIP4 = 0x00001000, + LOGD_DNS = 0x00002000, + LOGD_VPN = 0x00004000, + LOGD_SHARING = 0x00008000, /* Connection sharing/dnsmasq */ + LOGD_SUPPLICANT = 0x00010000, /* WiFi and 802.1x */ + LOGD_USER_SET = 0x00020000, /* User settings */ + LOGD_SYS_SET = 0x00040000, /* System settings */ + LOGD_SUSPEND = 0x00080000, /* Suspend/Resume */ + LOGD_CORE = 0x00100000, /* Core daemon and policy stuff */ + LOGD_DEVICE = 0x00200000, /* Device state and activation */ + LOGD_OLPC_MESH = 0x00400000, +}; + +#define LOGD_DHCP (LOGD_DHCP4 | LOGD_DHCP6) + +/* Log levels */ +enum { + LOGL_ERR = 0x00000001, + LOGL_WARN = 0x00000002, + LOGL_INFO = 0x00000004, + LOGL_DEBUG = 0x00000008 +}; + +#define NM_LOGGING_ERROR (nm_logging_error_quark ()) +#define NM_TYPE_LOGGING_ERROR (nm_logging_error_get_type ()) +GQuark nm_logging_error_quark (void); +GType nm_logging_error_get_type (void); + + +#define nm_log_err(domain, fmt, args...) \ + { _nm_log (G_STRLOC, G_STRFUNC, domain, LOGL_ERR, fmt, ##args); } + +#define nm_log_warn(domain, fmt, args...) \ + { _nm_log (G_STRLOC, G_STRFUNC, domain, LOGL_WARN, fmt, ##args); } + +#define nm_log_info(domain, fmt, args...) \ + { _nm_log (G_STRLOC, G_STRFUNC, domain, LOGL_INFO, fmt, ##args); } + +#define nm_log_dbg(domain, fmt, args...) \ + { _nm_log (G_STRLOC, G_STRFUNC, domain, LOGL_DEBUG, fmt, ##args); } + +#define nm_log(domain, level, fmt, args...) \ + { _nm_log (G_STRLOC, G_STRFUNC, domain, level, fmt, ##args); } + +void _nm_log (const char *loc, const char *func, + guint32 domain, guint32 level, + const char *fmt, ...); + +const char *nm_logging_level_to_string (void); +char *nm_logging_domains_to_string (void); + +/* Undefine the nm-utils.h logging stuff to ensure errors */ +#undef nm_print_backtrace +#undef nm_get_timestamp +#undef nm_info +#undef nm_info_str +#undef nm_debug +#undef nm_debug_str +#undef nm_warning +#undef nm_warning_str +#undef nm_error +#undef nm_error_str + +gboolean nm_logging_setup (const char *level, const char *domains, GError **error); +void nm_logging_start (gboolean become_daemon); +void nm_logging_backtrace (void); +void nm_logging_shutdown (void); + +#endif /* NM_LOGGING_H */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 00000000..c2fb58be --- /dev/null +++ b/src/main.c @@ -0,0 +1,758 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2004 - 2010 Red Hat, Inc. + * Copyright (C) 2005 - 2008 Novell, Inc. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <glib.h> +#include <dbus/dbus.h> +#include <dbus/dbus-glib-lowlevel.h> +#include <dbus/dbus-glib.h> +#include <getopt.h> +#include <errno.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <glib/gi18n.h> +#include <gmodule.h> +#include <string.h> + +#include "NetworkManager.h" +#include "NetworkManagerUtils.h" +#include "nm-manager.h" +#include "nm-policy.h" +#include "nm-system.h" +#include "nm-named-manager.h" +#include "nm-dbus-manager.h" +#include "nm-supplicant-manager.h" +#include "nm-dhcp-manager.h" +#include "nm-hostname-provider.h" +#include "nm-netlink-monitor.h" +#include "nm-vpn-manager.h" +#include "nm-logging.h" + +#if !defined(NM_DIST_VERSION) +# define NM_DIST_VERSION VERSION +#endif + +#define NM_DEFAULT_PID_FILE LOCALSTATEDIR"/run/NetworkManager.pid" +#define NM_DEFAULT_SYSTEM_CONF_FILE SYSCONFDIR"/NetworkManager/NetworkManager.conf" +#define NM_OLD_SYSTEM_CONF_FILE SYSCONFDIR"/NetworkManager/nm-system-settings.conf" +#define NM_DEFAULT_SYSTEM_STATE_FILE LOCALSTATEDIR"/lib/NetworkManager/NetworkManager.state" + +/* + * Globals + */ +static NMManager *manager = NULL; +static GMainLoop *main_loop = NULL; + +typedef struct { + time_t time; + GQuark domain; + guint32 code; + guint32 count; +} MonitorInfo; + +static gboolean +detach_monitor (gpointer data) +{ + nm_log_warn (LOGD_HW, "detaching netlink event monitor"); + nm_netlink_monitor_detach (NM_NETLINK_MONITOR (data)); + return FALSE; +} + +static void +nm_error_monitoring_device_link_state (NMNetlinkMonitor *monitor, + GError *error, + gpointer user_data) +{ + MonitorInfo *info = (MonitorInfo *) user_data; + time_t now; + + now = time (NULL); + + if ( (info->domain != error->domain) + || (info->code != error->code) + || (info->time && now > info->time + 10)) { + /* FIXME: Try to handle the error instead of just printing it. */ + nm_log_warn (LOGD_HW, "error monitoring device for netlink events: %s\n", error->message); + + info->time = now; + info->domain = error->domain; + info->code = error->code; + info->count = 0; + } + + info->count++; + if (info->count > 100) { + /* Broken drivers will sometimes cause a flood of netlink errors. + * rh #459205, novell #443429, lp #284507 + */ + nm_log_warn (LOGD_HW, "excessive netlink errors ocurred, disabling netlink monitor."); + nm_log_warn (LOGD_HW, "link change events will not be processed."); + g_idle_add_full (G_PRIORITY_HIGH, detach_monitor, monitor, NULL); + } +} + +static gboolean +nm_monitor_setup (GError **error) +{ + NMNetlinkMonitor *monitor; + MonitorInfo *info; + + monitor = nm_netlink_monitor_get (); + if (!nm_netlink_monitor_open_connection (monitor, error)) { + g_object_unref (monitor); + return FALSE; + } + + info = g_new0 (MonitorInfo, 1); + g_signal_connect_data (G_OBJECT (monitor), "error", + G_CALLBACK (nm_error_monitoring_device_link_state), + info, + (GClosureNotify) g_free, + 0); + nm_netlink_monitor_attach (monitor); + + /* Request initial status of cards */ + nm_netlink_monitor_request_status (monitor, NULL); + + return TRUE; +} + +static gboolean quit_early = FALSE; + +static void +nm_signal_handler (int signo) +{ + static int in_fatal = 0; + + /* avoid loops */ + if (in_fatal > 0) + return; + ++in_fatal; + + switch (signo) + { + case SIGSEGV: + case SIGBUS: + case SIGILL: + case SIGABRT: + nm_log_warn (LOGD_CORE, "caught signal %d. Generating backtrace...", signo); + nm_logging_backtrace (); + exit (1); + break; + + case SIGFPE: + case SIGPIPE: + /* let the fatal signals interrupt us */ + --in_fatal; + + nm_log_warn (LOGD_CORE, "caught signal %d, shutting down abnormally. Generating backtrace...", signo); + nm_logging_backtrace (); + g_main_loop_quit (main_loop); + break; + + case SIGINT: + case SIGTERM: + /* let the fatal signals interrupt us */ + --in_fatal; + + nm_log_info (LOGD_CORE, "caught signal %d, shutting down normally.", signo); + quit_early = TRUE; + g_main_loop_quit (main_loop); + break; + + case SIGHUP: + --in_fatal; + /* FIXME: + * Reread config stuff like system config files, VPN service files, etc + */ + break; + + case SIGUSR1: + --in_fatal; + /* FIXME: + * Play with log levels or something + */ + break; + + default: + signal (signo, nm_signal_handler); + break; + } +} + +static void +setup_signals (void) +{ + struct sigaction action; + sigset_t mask; + + sigemptyset (&mask); + action.sa_handler = nm_signal_handler; + action.sa_mask = mask; + action.sa_flags = 0; + sigaction (SIGTERM, &action, NULL); + sigaction (SIGINT, &action, NULL); + sigaction (SIGILL, &action, NULL); + sigaction (SIGBUS, &action, NULL); + sigaction (SIGFPE, &action, NULL); + sigaction (SIGHUP, &action, NULL); + sigaction (SIGSEGV, &action, NULL); + sigaction (SIGABRT, &action, NULL); + sigaction (SIGUSR1, &action, NULL); +} + +static gboolean +write_pidfile (const char *pidfile) +{ + char pid[16]; + int fd; + gboolean success = FALSE; + + if ((fd = open (pidfile, O_CREAT|O_WRONLY|O_TRUNC, 00644)) < 0) { + fprintf (stderr, "Opening %s failed: %s\n", pidfile, strerror (errno)); + return FALSE; + } + + snprintf (pid, sizeof (pid), "%d", getpid ()); + if (write (fd, pid, strlen (pid)) < 0) + fprintf (stderr, "Writing to %s failed: %s\n", pidfile, strerror (errno)); + else + success = TRUE; + + if (close (fd)) + fprintf (stderr, "Closing %s failed: %s\n", pidfile, strerror (errno)); + + return success; +} + +/* Check whether the pidfile already exists and contains PID of a running NetworkManager + * Returns: FALSE - specified pidfile doesn't exist or doesn't contain PID of a running NM process + * TRUE - specified pidfile already exists and contains PID of a running NM process + */ +static gboolean +check_pidfile (const char *pidfile) +{ + char *contents = NULL; + gsize len = 0; + glong pid; + char *proc_cmdline = NULL; + gboolean nm_running = FALSE; + const char *process_name; + + if (!g_file_get_contents (pidfile, &contents, &len, NULL)) + return FALSE; + + if (len <= 0) + goto done; + + errno = 0; + pid = strtol (contents, NULL, 10); + if (pid <= 0 || pid > 65536 || errno) + goto done; + + g_free (contents); + proc_cmdline = g_strdup_printf ("/proc/%ld/cmdline", pid); + if (!g_file_get_contents (proc_cmdline, &contents, &len, NULL)) + goto done; + + process_name = strrchr (contents, '/'); + if (process_name) + process_name++; + else + process_name = contents; + if (strcmp (process_name, "NetworkManager") == 0) { + /* Check that the process exists */ + if (kill (pid, 0) == 0) { + fprintf (stderr, "NetworkManager is already running (pid %ld)\n", pid); + nm_running = TRUE; + } + } + +done: + g_free (proc_cmdline); + g_free (contents); + return nm_running; +} + +static gboolean +parse_config_file (const char *filename, + char **plugins, + char **dhcp_client, + char **log_level, + char **log_domains, + GError **error) +{ + GKeyFile *config; + + config = g_key_file_new (); + if (!config) { + g_set_error (error, 0, 0, + "Not enough memory to load config file."); + return FALSE; + } + + g_key_file_set_list_separator (config, ','); + if (!g_key_file_load_from_file (config, filename, G_KEY_FILE_NONE, error)) + return FALSE; + + *plugins = g_key_file_get_value (config, "main", "plugins", error); + if (*error) + return FALSE; + + *dhcp_client = g_key_file_get_value (config, "main", "dhcp", NULL); + + *log_level = g_key_file_get_value (config, "logging", "level", NULL); + *log_domains = g_key_file_get_value (config, "logging", "domains", NULL); + + g_key_file_free (config); + return TRUE; +} + +static gboolean +parse_state_file (const char *filename, + gboolean *net_enabled, + gboolean *wifi_enabled, + gboolean *wwan_enabled, + GError **error) +{ + GKeyFile *state_file; + GError *tmp_error = NULL; + gboolean wifi, net, wwan; + + g_return_val_if_fail (net_enabled != NULL, FALSE); + g_return_val_if_fail (wifi_enabled != NULL, FALSE); + g_return_val_if_fail (wwan_enabled != NULL, FALSE); + + state_file = g_key_file_new (); + if (!state_file) { + g_set_error (error, 0, 0, + "Not enough memory to load state file."); + return FALSE; + } + + g_key_file_set_list_separator (state_file, ','); + if (!g_key_file_load_from_file (state_file, filename, G_KEY_FILE_KEEP_COMMENTS, &tmp_error)) { + /* This is kinda ugly; create the file and directory if it doesn't + * exist yet. We can't rely on distros necessarily creating the + * /var/lib/NetworkManager for us since we have to ensure that + * users upgrading NM get this working too. + */ + if ( tmp_error->domain == G_FILE_ERROR + && tmp_error->code == G_FILE_ERROR_NOENT) { + char *data, *dirname; + gsize len = 0; + gboolean ret = FALSE; + + /* try to create the directory if it doesn't exist */ + dirname = g_path_get_dirname (filename); + errno = 0; + if (mkdir (dirname, 0755) != 0) { + if (errno != EEXIST) { + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_ACCES, + "Error creating state directory %s: %d", dirname, errno); + g_free (dirname); + return FALSE; + } + } + g_free (dirname); + + /* Write out the initial state to the state file */ + g_key_file_set_boolean (state_file, "main", "NetworkingEnabled", *net_enabled); + g_key_file_set_boolean (state_file, "main", "WirelessEnabled", *wifi_enabled); + g_key_file_set_boolean (state_file, "main", "WWANEnabled", *wwan_enabled); + + data = g_key_file_to_data (state_file, &len, NULL); + if (data) + ret = g_file_set_contents (filename, data, len, error); + g_free (data); + + return ret; + } else { + g_set_error_literal (error, tmp_error->domain, tmp_error->code, tmp_error->message); + g_clear_error (&tmp_error); + } + + /* Otherwise, file probably corrupt or inaccessible */ + return FALSE; + } + + /* Reading state bits of NetworkManager; an error leaves the passed-in state + * value unchanged. + */ + net = g_key_file_get_boolean (state_file, "main", "NetworkingEnabled", &tmp_error); + if (tmp_error) + g_set_error_literal (error, tmp_error->domain, tmp_error->code, tmp_error->message); + else + *net_enabled = net; + g_clear_error (&tmp_error); + + wifi = g_key_file_get_boolean (state_file, "main", "WirelessEnabled", &tmp_error); + if (tmp_error) { + g_clear_error (error); + g_set_error_literal (error, tmp_error->domain, tmp_error->code, tmp_error->message); + } else + *wifi_enabled = wifi; + g_clear_error (&tmp_error); + + wwan = g_key_file_get_boolean (state_file, "main", "WWANEnabled", &tmp_error); + if (tmp_error) { + g_clear_error (error); + g_set_error_literal (error, tmp_error->domain, tmp_error->code, tmp_error->message); + } else + *wwan_enabled = wwan; + g_clear_error (&tmp_error); + + g_key_file_free (state_file); + + return TRUE; +} + +/* + * main + * + */ +int +main (int argc, char *argv[]) +{ + GOptionContext *opt_ctx = NULL; + gboolean become_daemon = FALSE; + gboolean g_fatal_warnings = FALSE; + char *pidfile = NULL, *state_file = NULL, *dhcp = NULL; + char *config = NULL, *plugins = NULL, *conf_plugins = NULL; + char *log_level = NULL, *log_domains = NULL; + gboolean wifi_enabled = TRUE, net_enabled = TRUE, wwan_enabled = TRUE; + gboolean success; + NMPolicy *policy = NULL; + NMVPNManager *vpn_manager = NULL; + NMNamedManager *named_mgr = NULL; + NMDBusManager *dbus_mgr = NULL; + NMSupplicantManager *sup_mgr = NULL; + NMDHCPManager *dhcp_mgr = NULL; + GError *error = NULL; + gboolean wrote_pidfile = FALSE; + char *cfg_log_level = NULL, *cfg_log_domains = NULL; + + GOptionEntry options[] = { + { "no-daemon", 0, 0, G_OPTION_ARG_NONE, &become_daemon, "Don't become a daemon", NULL }, + { "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &g_fatal_warnings, "Make all warnings fatal", NULL }, + { "pid-file", 0, 0, G_OPTION_ARG_FILENAME, &pidfile, "Specify the location of a PID file", "filename" }, + { "state-file", 0, 0, G_OPTION_ARG_FILENAME, &state_file, "State file location", "/path/to/state.file" }, + { "config", 0, 0, G_OPTION_ARG_FILENAME, &config, "Config file location", "/path/to/config.file" }, + { "plugins", 0, 0, G_OPTION_ARG_STRING, &plugins, "List of plugins separated by ','", "plugin1,plugin2" }, + { "log-level", 0, 0, G_OPTION_ARG_STRING, &log_level, "Log level: one of [ERR, WARN, INFO, DEBUG]", "INFO" }, + { "log-domains", 0, 0, G_OPTION_ARG_STRING, &log_domains, + "Log domains separated by ',': any combination of [NONE,HW,RKILL,ETHER,WIFI,BT,MB,DHCP4,DHCP6,PPP,WIFI_SCAN,IP4,IP6,AUTOIP4,DNS,VPN,SHARING,SUPPLICANT,USER_SET,SYS_SET,SUSPEND,CORE,DEVICE,OLPC]", + "HW,RFKILL,WIFI" }, + {NULL} + }; + + if (getuid () != 0) { + fprintf (stderr, "You must be root to run NetworkManager!\n"); + exit (1); + } + + if (!g_module_supported ()) { + fprintf (stderr, "GModules are not supported on your platform!\n"); + exit (1); + } + + bindtextdomain (GETTEXT_PACKAGE, NMLOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + /* Parse options */ + opt_ctx = g_option_context_new (""); + g_option_context_set_translation_domain (opt_ctx, "UTF-8"); + g_option_context_set_ignore_unknown_options (opt_ctx, FALSE); + g_option_context_set_help_enabled (opt_ctx, TRUE); + g_option_context_add_main_entries (opt_ctx, options, NULL); + + g_option_context_set_summary (opt_ctx, + "NetworkManager monitors all network connections and automatically\nchooses the best connection to use. It also allows the user to\nspecify wireless access points which wireless cards in the computer\nshould associate with."); + + success = g_option_context_parse (opt_ctx, &argc, &argv, NULL); + g_option_context_free (opt_ctx); + + if (!success) { + fprintf (stderr, _("Invalid option. Please use --help to see a list of valid options.\n")); + exit (1); + } + + /* Make GIO ignore the remote VFS service; otherwise it tries to use the + * session bus to contact the remote service, and NM shouldn't ever be + * talking on the session bus. See rh #588745 + */ + setenv ("GIO_USE_VFS", "local", 1); + + pidfile = pidfile ? pidfile : g_strdup (NM_DEFAULT_PID_FILE); + state_file = state_file ? state_file : g_strdup (NM_DEFAULT_SYSTEM_STATE_FILE); + + /* check pid file */ + if (check_pidfile (pidfile)) + exit (1); + + /* Parse the config file */ + if (config) { + if (!parse_config_file (config, &conf_plugins, &dhcp, &cfg_log_level, &cfg_log_domains, &error)) { + fprintf (stderr, "Config file %s invalid: (%d) %s\n", + config, + error ? error->code : -1, + (error && error->message) ? error->message : "unknown"); + exit (1); + } + } else { + gboolean parsed = FALSE; + + /* Even though we prefer NetworkManager.conf, we need to check the + * old nm-system-settings.conf first to preserve compat with older + * setups. In package managed systems dropping a NetworkManager.conf + * onto the system would make NM use it instead of nm-system-settings.conf, + * changing behavior during an upgrade. We don't want that. + */ + + /* Try deprecated nm-system-settings.conf first */ + if (g_file_test (NM_OLD_SYSTEM_CONF_FILE, G_FILE_TEST_EXISTS)) { + config = g_strdup (NM_OLD_SYSTEM_CONF_FILE); + parsed = parse_config_file (config, &conf_plugins, &dhcp, &cfg_log_level, &cfg_log_domains, &error); + if (!parsed) { + fprintf (stderr, "Default config file %s invalid: (%d) %s\n", + config, + error ? error->code : -1, + (error && error->message) ? error->message : "unknown"); + g_free (config); + config = NULL; + g_clear_error (&error); + } + } + + /* Try the preferred NetworkManager.conf last */ + if (!parsed && g_file_test (NM_DEFAULT_SYSTEM_CONF_FILE, G_FILE_TEST_EXISTS)) { + config = g_strdup (NM_DEFAULT_SYSTEM_CONF_FILE); + parsed = parse_config_file (config, &conf_plugins, &dhcp, &cfg_log_level, &cfg_log_domains, &error); + if (!parsed) { + fprintf (stderr, "Default config file %s invalid: (%d) %s\n", + config, + error ? error->code : -1, + (error && error->message) ? error->message : "unknown"); + g_free (config); + config = NULL; + g_clear_error (&error); + } + } + } + /* Logging setup */ + if (!nm_logging_setup (log_level ? log_level : cfg_log_level, + log_domains ? log_domains : cfg_log_domains, + &error)) { + fprintf (stderr, + _("%s. Please use --help to see a list of valid options.\n"), + error->message); + exit (1); + } + + /* Plugins specified with '--plugins' override those of config file */ + plugins = plugins ? plugins : g_strdup (conf_plugins); + g_free (conf_plugins); + + /* Parse the state file */ + if (!parse_state_file (state_file, &net_enabled, &wifi_enabled, &wwan_enabled, &error)) { + fprintf (stderr, "State file %s parsing failed: (%d) %s\n", + state_file, + error ? error->code : -1, + (error && error->message) ? error->message : "unknown"); + /* Not a hard failure */ + } + g_clear_error (&error); + + /* Tricky: become_daemon is FALSE by default, so unless it's TRUE because + * of a CLI option, it'll become TRUE after this + */ + become_daemon = !become_daemon; + if (become_daemon) { + if (daemon (0, 0) < 0) { + int saved_errno; + + saved_errno = errno; + fprintf (stderr, "Could not daemonize: %s [error %u]\n", + g_strerror (saved_errno), + saved_errno); + exit (1); + } + if (write_pidfile (pidfile)) + wrote_pidfile = TRUE; + } + + if (g_fatal_warnings) { + GLogLevelFlags fatal_mask; + + fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK); + fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL; + g_log_set_always_fatal (fatal_mask); + } + + /* + * Set the umask to 0022, which results in 0666 & ~0022 = 0644. + * Otherwise, if root (or an su'ing user) has a wacky umask, we could + * write out an unreadable resolv.conf. + */ + umask (022); + + g_type_init (); + if (!g_thread_supported ()) + g_thread_init (NULL); + dbus_g_thread_init (); + + setup_signals (); + + nm_logging_start (become_daemon); + + nm_log_info (LOGD_CORE, "NetworkManager (version " NM_DIST_VERSION ") is starting..."); + success = FALSE; + + if (config) + nm_log_info (LOGD_CORE, "Read config file %s", config); + + main_loop = g_main_loop_new (NULL, FALSE); + + /* Create watch functions that monitor cards for link status. */ + if (!nm_monitor_setup (&error)) { + nm_log_err (LOGD_CORE, "failed to start monitoring devices: %s.", + error && error->message ? error->message : "(unknown)"); + goto done; + } + + /* Initialize our DBus service & connection */ + dbus_mgr = nm_dbus_manager_get (); + + vpn_manager = nm_vpn_manager_get (); + if (!vpn_manager) { + nm_log_err (LOGD_CORE, "failed to start the VPN manager."); + goto done; + } + + named_mgr = nm_named_manager_get (); + if (!named_mgr) { + nm_log_err (LOGD_CORE, "failed to start the named manager."); + goto done; + } + + manager = nm_manager_get (config, + plugins, + state_file, + net_enabled, + wifi_enabled, + wwan_enabled, + &error); + if (manager == NULL) { + nm_log_err (LOGD_CORE, "failed to initialize the network manager: %s", + error && error->message ? error->message : "(unknown)"); + goto done; + } + + policy = nm_policy_new (manager, vpn_manager); + if (policy == NULL) { + nm_log_err (LOGD_CORE, "failed to initialize the policy."); + goto done; + } + + /* Initialize the supplicant manager */ + sup_mgr = nm_supplicant_manager_get (); + if (!sup_mgr) { + nm_log_err (LOGD_CORE, "failed to initialize the supplicant manager."); + goto done; + } + + /* Initialize DHCP manager */ + dhcp_mgr = nm_dhcp_manager_new (dhcp, &error); + if (!dhcp_mgr) { + nm_log_err (LOGD_CORE, "failed to start the DHCP manager: %s.", error->message); + goto done; + } + + nm_dhcp_manager_set_hostname_provider (dhcp_mgr, NM_HOSTNAME_PROVIDER (manager)); + + /* Start our DBus service */ + if (!nm_dbus_manager_start_service (dbus_mgr)) { + nm_log_err (LOGD_CORE, "failed to start the dbus service."); + goto done; + } + + nm_manager_start (manager); + + /* Bring up the loopback interface. */ + nm_system_enable_loopback (); + + success = TRUE; + + /* Told to quit before getting to the mainloop by the signal handler */ + if (quit_early == TRUE) + goto done; + + g_main_loop_run (main_loop); + +done: + if (policy) + nm_policy_destroy (policy); + + if (manager) + g_object_unref (manager); + + if (vpn_manager) + g_object_unref (vpn_manager); + + if (named_mgr) + g_object_unref (named_mgr); + + if (dhcp_mgr) + g_object_unref (dhcp_mgr); + + if (sup_mgr) + g_object_unref (sup_mgr); + + if (dbus_mgr) + g_object_unref (dbus_mgr); + + nm_logging_shutdown (); + + if (pidfile && wrote_pidfile) + unlink (pidfile); + + /* Free options */ + g_free (pidfile); + g_free (state_file); + g_free (config); + g_free (plugins); + g_free (dhcp); + g_free (log_level); + g_free (log_domains); + g_free (cfg_log_level); + g_free (cfg_log_domains); + + nm_log_info (LOGD_CORE, "exiting (%s)", success ? "success" : "error"); + exit (success ? 0 : 1); +} diff --git a/src/modem-manager/Makefile.am b/src/modem-manager/Makefile.am new file mode 100644 index 00000000..a0f65c1b --- /dev/null +++ b/src/modem-manager/Makefile.am @@ -0,0 +1,35 @@ +INCLUDES = \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/include \ + -I${top_srcdir}/libnm-util \ + -I${top_builddir}/marshallers + +noinst_LTLIBRARIES = libmodem-manager.la + +libmodem_manager_la_SOURCES = \ + nm-modem.c \ + nm-modem.h \ + nm-modem-cdma.c \ + nm-modem-cdma.h \ + nm-modem-gsm.c \ + nm-modem-gsm.h \ + nm-modem-manager.h \ + nm-modem-manager.c \ + nm-modem-types.h + +libmodem_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) + +libmodem_manager_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(DBUS_LIBS) + +nm-serial-device-glue.h: $(top_srcdir)/introspection/nm-device-serial.xml + dbus-binding-tool --prefix=nm_serial_device --mode=glib-server --output=$@ $< + +BUILT_SOURCES = \ + nm-serial-device-glue.h + +CLEANFILES = $(BUILT_SOURCES) diff --git a/src/modem-manager/Makefile.in b/src/modem-manager/Makefile.in new file mode 100644 index 00000000..26e67a1d --- /dev/null +++ b/src/modem-manager/Makefile.in @@ -0,0 +1,611 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/modem-manager +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libmodem_manager_la_DEPENDENCIES = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(am__DEPENDENCIES_1) +am_libmodem_manager_la_OBJECTS = libmodem_manager_la-nm-modem.lo \ + libmodem_manager_la-nm-modem-cdma.lo \ + libmodem_manager_la-nm-modem-gsm.lo \ + libmodem_manager_la-nm-modem-manager.lo +libmodem_manager_la_OBJECTS = $(am_libmodem_manager_la_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libmodem_manager_la_SOURCES) +DIST_SOURCES = $(libmodem_manager_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +IPTABLES_PATH = @IPTABLES_PATH@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBM = @LIBM@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POFILES = @POFILES@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +RANLIB = @RANLIB@ +RESOLVCONF_PATH = @RESOLVCONF_PATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/include \ + -I${top_srcdir}/libnm-util \ + -I${top_builddir}/marshallers + +noinst_LTLIBRARIES = libmodem-manager.la +libmodem_manager_la_SOURCES = \ + nm-modem.c \ + nm-modem.h \ + nm-modem-cdma.c \ + nm-modem-cdma.h \ + nm-modem-gsm.c \ + nm-modem-gsm.h \ + nm-modem-manager.h \ + nm-modem-manager.c \ + nm-modem-types.h + +libmodem_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) + +libmodem_manager_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(DBUS_LIBS) + +BUILT_SOURCES = \ + nm-serial-device-glue.h + +CLEANFILES = $(BUILT_SOURCES) +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/modem-manager/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/modem-manager/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libmodem-manager.la: $(libmodem_manager_la_OBJECTS) $(libmodem_manager_la_DEPENDENCIES) + $(LINK) $(libmodem_manager_la_OBJECTS) $(libmodem_manager_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_manager_la-nm-modem-cdma.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_manager_la-nm-modem-gsm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_manager_la-nm-modem-manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_manager_la-nm-modem.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libmodem_manager_la-nm-modem.lo: nm-modem.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmodem_manager_la-nm-modem.lo -MD -MP -MF $(DEPDIR)/libmodem_manager_la-nm-modem.Tpo -c -o libmodem_manager_la-nm-modem.lo `test -f 'nm-modem.c' || echo '$(srcdir)/'`nm-modem.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libmodem_manager_la-nm-modem.Tpo $(DEPDIR)/libmodem_manager_la-nm-modem.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-modem.c' object='libmodem_manager_la-nm-modem.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmodem_manager_la-nm-modem.lo `test -f 'nm-modem.c' || echo '$(srcdir)/'`nm-modem.c + +libmodem_manager_la-nm-modem-cdma.lo: nm-modem-cdma.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmodem_manager_la-nm-modem-cdma.lo -MD -MP -MF $(DEPDIR)/libmodem_manager_la-nm-modem-cdma.Tpo -c -o libmodem_manager_la-nm-modem-cdma.lo `test -f 'nm-modem-cdma.c' || echo '$(srcdir)/'`nm-modem-cdma.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libmodem_manager_la-nm-modem-cdma.Tpo $(DEPDIR)/libmodem_manager_la-nm-modem-cdma.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-modem-cdma.c' object='libmodem_manager_la-nm-modem-cdma.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmodem_manager_la-nm-modem-cdma.lo `test -f 'nm-modem-cdma.c' || echo '$(srcdir)/'`nm-modem-cdma.c + +libmodem_manager_la-nm-modem-gsm.lo: nm-modem-gsm.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmodem_manager_la-nm-modem-gsm.lo -MD -MP -MF $(DEPDIR)/libmodem_manager_la-nm-modem-gsm.Tpo -c -o libmodem_manager_la-nm-modem-gsm.lo `test -f 'nm-modem-gsm.c' || echo '$(srcdir)/'`nm-modem-gsm.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libmodem_manager_la-nm-modem-gsm.Tpo $(DEPDIR)/libmodem_manager_la-nm-modem-gsm.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-modem-gsm.c' object='libmodem_manager_la-nm-modem-gsm.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmodem_manager_la-nm-modem-gsm.lo `test -f 'nm-modem-gsm.c' || echo '$(srcdir)/'`nm-modem-gsm.c + +libmodem_manager_la-nm-modem-manager.lo: nm-modem-manager.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmodem_manager_la-nm-modem-manager.lo -MD -MP -MF $(DEPDIR)/libmodem_manager_la-nm-modem-manager.Tpo -c -o libmodem_manager_la-nm-modem-manager.lo `test -f 'nm-modem-manager.c' || echo '$(srcdir)/'`nm-modem-manager.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libmodem_manager_la-nm-modem-manager.Tpo $(DEPDIR)/libmodem_manager_la-nm-modem-manager.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-modem-manager.c' object='libmodem_manager_la-nm-modem-manager.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmodem_manager_la-nm-modem-manager.lo `test -f 'nm-modem-manager.c' || echo '$(srcdir)/'`nm-modem-manager.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: all check install install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +nm-serial-device-glue.h: $(top_srcdir)/introspection/nm-device-serial.xml + dbus-binding-tool --prefix=nm_serial_device --mode=glib-server --output=$@ $< + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/modem-manager/nm-modem-cdma.c b/src/modem-manager/nm-modem-cdma.c new file mode 100644 index 00000000..c32c1822 --- /dev/null +++ b/src/modem-manager/nm-modem-cdma.c @@ -0,0 +1,351 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 - 2010 Red Hat, Inc. + * Copyright (C) 2009 Novell, Inc. + */ + +#include <string.h> + +#include "nm-dbus-glib-types.h" +#include "nm-modem-cdma.h" +#include "nm-modem-types.h" +#include "nm-device.h" +#include "nm-dbus-manager.h" +#include "nm-setting-connection.h" +#include "nm-setting-cdma.h" +#include "NetworkManagerUtils.h" +#include "nm-logging.h" + +G_DEFINE_TYPE (NMModemCdma, nm_modem_cdma, NM_TYPE_MODEM) + +#define NM_MODEM_CDMA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM_CDMA, NMModemCdmaPrivate)) + +typedef struct { + DBusGProxyCall *call; + + GHashTable *connect_properties; +} NMModemCdmaPrivate; + + +typedef enum { + NM_CDMA_ERROR_CONNECTION_NOT_CDMA = 0, + NM_CDMA_ERROR_CONNECTION_INVALID, + NM_CDMA_ERROR_CONNECTION_INCOMPATIBLE, +} NMCdmaError; + +#define NM_CDMA_ERROR (nm_cdma_error_quark ()) +#define NM_TYPE_CDMA_ERROR (nm_cdma_error_get_type ()) + +static GQuark +nm_cdma_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm-cdma-error"); + return quark; +} + +/* This should really be standard. */ +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +static GType +nm_cdma_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + /* Connection was not a CDMA connection. */ + ENUM_ENTRY (NM_CDMA_ERROR_CONNECTION_NOT_CDMA, "ConnectionNotCdma"), + /* Connection was not a valid CDMA connection. */ + ENUM_ENTRY (NM_CDMA_ERROR_CONNECTION_INVALID, "ConnectionInvalid"), + /* Connection does not apply to this device. */ + ENUM_ENTRY (NM_CDMA_ERROR_CONNECTION_INCOMPATIBLE, "ConnectionIncompatible"), + { 0, 0, 0 } + }; + etype = g_enum_register_static ("NMCdmaError", values); + } + return etype; +} + + +NMModem * +nm_modem_cdma_new (const char *path, + const char *device, + const char *data_device, + guint32 ip_method) +{ + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (data_device != NULL, NULL); + + return (NMModem *) g_object_new (NM_TYPE_MODEM_CDMA, + NM_MODEM_PATH, path, + NM_MODEM_DEVICE, device, + NM_MODEM_IFACE, data_device, + NM_MODEM_IP_METHOD, ip_method, + NULL); +} + +static void +stage1_prepare_done (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + NMModemCdma *self = NM_MODEM_CDMA (user_data); + NMModemCdmaPrivate *priv = NM_MODEM_CDMA_GET_PRIVATE (self); + GError *error = NULL; + + priv->call = NULL; + + if (priv->connect_properties) { + g_hash_table_destroy (priv->connect_properties); + priv->connect_properties = NULL; + } + + if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, TRUE, NM_DEVICE_STATE_REASON_NONE); + else { + nm_log_warn (LOGD_MB, "CDMA connection failed: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_error_free (error); + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_NONE); + } +} + +static void +do_connect (NMModemCdma *self) +{ + NMModemCdmaPrivate *priv = NM_MODEM_CDMA_GET_PRIVATE (self); + DBusGProxy *proxy; + + proxy = nm_modem_get_proxy (NM_MODEM (self), MM_DBUS_INTERFACE_MODEM_SIMPLE); + priv->call = dbus_g_proxy_begin_call_with_timeout (proxy, + "Connect", stage1_prepare_done, + self, NULL, 120000, + DBUS_TYPE_G_MAP_OF_VARIANT, priv->connect_properties, + G_TYPE_INVALID); +} + +static void +stage1_enable_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMModemCdma *self = NM_MODEM_CDMA (user_data); + GError *error = NULL; + + if (dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) + do_connect (self); + else { + nm_log_warn (LOGD_MB, "CDMA modem enable failed: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_error_free (error); + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED); + } +} + +static GHashTable * +create_connect_properties (NMConnection *connection) +{ + NMSettingCdma *setting; + GHashTable *properties; + const char *str; + + setting = NM_SETTING_CDMA (nm_connection_get_setting (connection, NM_TYPE_SETTING_CDMA)); + properties = value_hash_create (); + + str = nm_setting_cdma_get_number (setting); + if (str) + value_hash_add_str (properties, "number", str); + + return properties; +} + +static NMActStageReturn +real_act_stage1_prepare (NMModem *modem, + NMActRequest *req, + GPtrArray **out_hints, + const char **out_setting_name, + NMDeviceStateReason *reason) +{ + NMModemCdma *self = NM_MODEM_CDMA (modem); + NMModemCdmaPrivate *priv = NM_MODEM_CDMA_GET_PRIVATE (self); + NMConnection *connection; + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + *out_setting_name = nm_connection_need_secrets (connection, out_hints); + if (!*out_setting_name) { + gboolean enabled = nm_modem_get_mm_enabled (modem); + DBusGProxy *proxy; + + if (priv->connect_properties) + g_hash_table_destroy (priv->connect_properties); + priv->connect_properties = create_connect_properties (connection); + + if (enabled) + do_connect (self); + else { + proxy = nm_modem_get_proxy (modem, MM_DBUS_INTERFACE_MODEM); + dbus_g_proxy_begin_call_with_timeout (proxy, + "Enable", stage1_enable_done, + modem, NULL, 20000, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_INVALID); + } + } else { + /* NMModem will handle requesting secrets... */ + } + + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +static NMConnection * +real_get_best_auto_connection (NMModem *modem, + GSList *connections, + char **specific_object) +{ + GSList *iter; + + for (iter = connections; iter; iter = g_slist_next (iter)) { + NMConnection *connection = NM_CONNECTION (iter->data); + NMSettingConnection *s_con; + + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + + if (!nm_setting_connection_get_autoconnect (s_con)) + continue; + + if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_CDMA_SETTING_NAME)) + continue; + + return connection; + } + return NULL; +} + +static gboolean +real_check_connection_compatible (NMModem *modem, + NMConnection *connection, + GError **error) +{ + NMSettingConnection *s_con; + NMSettingCdma *s_cdma; + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + + if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_CDMA_SETTING_NAME)) { + g_set_error (error, + NM_CDMA_ERROR, NM_CDMA_ERROR_CONNECTION_NOT_CDMA, + "The connection was not a CDMA connection."); + return FALSE; + } + + s_cdma = NM_SETTING_CDMA (nm_connection_get_setting (connection, NM_TYPE_SETTING_CDMA)); + if (!s_cdma) { + g_set_error (error, + NM_CDMA_ERROR, NM_CDMA_ERROR_CONNECTION_INVALID, + "The connection was not a valid CDMA connection."); + return FALSE; + } + + return TRUE; +} + +static gboolean +real_get_user_pass (NMModem *modem, + NMConnection *connection, + const char **user, + const char **pass) +{ + NMSettingCdma *s_cdma; + + s_cdma = (NMSettingCdma *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CDMA); + if (!s_cdma) + return FALSE; + + if (user) + *user = nm_setting_cdma_get_username (s_cdma); + if (pass) + *pass = nm_setting_cdma_get_password (s_cdma); + + return TRUE; +} + +static const char * +real_get_setting_name (NMModem *modem) +{ + return NM_SETTING_CDMA_SETTING_NAME; +} + +static void +real_deactivate_quickly (NMModem *modem, NMDevice *device) +{ + NMModemCdmaPrivate *priv = NM_MODEM_CDMA_GET_PRIVATE (modem); + + if (priv->call) { + DBusGProxy *proxy; + + proxy = nm_modem_get_proxy (modem, MM_DBUS_INTERFACE_MODEM_SIMPLE); + dbus_g_proxy_cancel_call (proxy, priv->call); + priv->call = NULL; + } + + NM_MODEM_CLASS (nm_modem_cdma_parent_class)->deactivate_quickly (modem, device); +} + +/*****************************************************************************/ + +static void +nm_modem_cdma_init (NMModemCdma *self) +{ +} + +static void +dispose (GObject *object) +{ + NMModemCdma *self = NM_MODEM_CDMA (object); + NMModemCdmaPrivate *priv = NM_MODEM_CDMA_GET_PRIVATE (self); + + if (priv->connect_properties) + g_hash_table_destroy (priv->connect_properties); + + G_OBJECT_CLASS (nm_modem_cdma_parent_class)->dispose (object); +} + +static void +nm_modem_cdma_class_init (NMModemCdmaClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMModemClass *modem_class = NM_MODEM_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMModemCdmaPrivate)); + + /* Virtual methods */ + object_class->dispose = dispose; + modem_class->get_user_pass = real_get_user_pass; + modem_class->get_setting_name = real_get_setting_name; + modem_class->get_best_auto_connection = real_get_best_auto_connection; + modem_class->check_connection_compatible = real_check_connection_compatible; + modem_class->act_stage1_prepare = real_act_stage1_prepare; + modem_class->deactivate_quickly = real_deactivate_quickly; + + dbus_g_error_domain_register (NM_CDMA_ERROR, NULL, NM_TYPE_CDMA_ERROR); +} diff --git a/src/modem-manager/nm-modem-cdma.h b/src/modem-manager/nm-modem-cdma.h new file mode 100644 index 00000000..0abe4e0c --- /dev/null +++ b/src/modem-manager/nm-modem-cdma.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2009 Novell, Inc. + */ + +#ifndef NM_MODEM_CDMA_H +#define NM_MODEM_CDMA_H + +#include <nm-modem.h> + +G_BEGIN_DECLS + +#define NM_TYPE_MODEM_CDMA (nm_modem_cdma_get_type ()) +#define NM_MODEM_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_CDMA, NMModemCdma)) +#define NM_MODEM_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MODEM_CDMA, NMModemCdmaClass)) +#define NM_IS_MODEM_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM_CDMA)) +#define NM_IS_MODEM_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_MODEM_CDMA)) +#define NM_MODEM_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MODEM_CDMA, NMModemCdmaClass)) + +typedef struct { + NMModem parent; +} NMModemCdma; + +typedef struct { + NMModemClass parent; + + /* Signals */ + void (*signal_quality) (NMModemCdma *self, guint32 quality); +} NMModemCdmaClass; + +GType nm_modem_cdma_get_type (void); + +NMModem *nm_modem_cdma_new (const char *path, + const char *device, + const char *data_device, + guint32 ip_method); + +G_END_DECLS + +#endif /* NM_MODEM_CDMA_H */ diff --git a/src/modem-manager/nm-modem-gsm.c b/src/modem-manager/nm-modem-gsm.c new file mode 100644 index 00000000..abb96db1 --- /dev/null +++ b/src/modem-manager/nm-modem-gsm.c @@ -0,0 +1,554 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 - 2010 Red Hat, Inc. + * Copyright (C) 2009 Novell, Inc. + */ + +#include <string.h> +#include "nm-dbus-glib-types.h" +#include "nm-modem-gsm.h" +#include "nm-device.h" +#include "nm-setting-connection.h" +#include "nm-setting-gsm.h" +#include "nm-modem-types.h" +#include "nm-logging.h" +#include "NetworkManagerUtils.h" + +typedef enum { + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY = 0, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_GPRS, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_EDGE, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_UMTS, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSDPA, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_PREFERRED, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_PREFERRED, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_ONLY, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_ONLY, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSUPA, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSPA, + + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_LAST = MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSPA +} MMModemDeprecatedMode; + +typedef enum { + MM_MODEM_GSM_ALLOWED_MODE_ANY = 0, + MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED = 1, + MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED = 2, + MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY = 3, + MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY = 4, + + MM_MODEM_GSM_ALLOWED_MODE_LAST = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY +} MMModemGsmAllowedMode; + + +G_DEFINE_TYPE (NMModemGsm, nm_modem_gsm, NM_TYPE_MODEM) + +#define NM_MODEM_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM_GSM, NMModemGsmPrivate)) + +typedef struct { + DBusGProxyCall *call; + + GHashTable *connect_properties; + guint32 pin_tries; +} NMModemGsmPrivate; + + +typedef enum { + NM_GSM_ERROR_CONNECTION_NOT_GSM = 0, + NM_GSM_ERROR_CONNECTION_INVALID, + NM_GSM_ERROR_CONNECTION_INCOMPATIBLE, +} NMGsmError; + +#define NM_GSM_ERROR (nm_gsm_error_quark ()) +#define NM_TYPE_GSM_ERROR (nm_gsm_error_get_type ()) + +static GQuark +nm_gsm_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm-gsm-error"); + return quark; +} + +/* This should really be standard. */ +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +static GType +nm_gsm_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + /* Connection was not a GSM connection. */ + ENUM_ENTRY (NM_GSM_ERROR_CONNECTION_NOT_GSM, "ConnectionNotGsm"), + /* Connection was not a valid GSM connection. */ + ENUM_ENTRY (NM_GSM_ERROR_CONNECTION_INVALID, "ConnectionInvalid"), + /* Connection does not apply to this device. */ + ENUM_ENTRY (NM_GSM_ERROR_CONNECTION_INCOMPATIBLE, "ConnectionIncompatible"), + { 0, 0, 0 } + }; + etype = g_enum_register_static ("NMGsmError", values); + } + return etype; +} + + +NMModem * +nm_modem_gsm_new (const char *path, + const char *device, + const char *data_device, + guint32 ip_method) +{ + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (data_device != NULL, NULL); + + return (NMModem *) g_object_new (NM_TYPE_MODEM_GSM, + NM_MODEM_PATH, path, + NM_MODEM_DEVICE, device, + NM_MODEM_IFACE, data_device, + NM_MODEM_IP_METHOD, ip_method, + NULL); +} + +static NMDeviceStateReason +translate_mm_error (GError *error) +{ + NMDeviceStateReason reason; + + if (dbus_g_error_has_name (error, MM_MODEM_CONNECT_ERROR_NO_CARRIER)) + reason = NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER; + if (dbus_g_error_has_name (error, MM_MODEM_CONNECT_ERROR_NO_DIALTONE)) + reason = NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT; + if (dbus_g_error_has_name (error, MM_MODEM_CONNECT_ERROR_BUSY)) + reason = NM_DEVICE_STATE_REASON_MODEM_BUSY; + if (dbus_g_error_has_name (error, MM_MODEM_CONNECT_ERROR_NO_ANSWER)) + reason = NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT; + if (dbus_g_error_has_name (error, MM_MODEM_ERROR_NETWORK_NOT_ALLOWED)) + reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED; + if (dbus_g_error_has_name (error, MM_MODEM_ERROR_NETWORK_TIMEOUT)) + reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT; + if (dbus_g_error_has_name (error, MM_MODEM_ERROR_NO_NETWORK)) + reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING; + + /* FIXME: We have only GSM error messages here, and we have no idea which + activation state failed. Reasons like: + NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED, + NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED, + NM_DEVICE_STATE_REASON_GSM_APN_FAILED, + NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED, + NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED + are not used. + */ + else + reason = NM_DEVICE_STATE_REASON_UNKNOWN; + + return reason; +} + +static void +ask_for_pin (NMModemGsm *self, gboolean always_ask) +{ + NMModemGsmPrivate *priv; + guint32 tries = 0; + + g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_MODEM_GSM (self)); + + priv = NM_MODEM_GSM_GET_PRIVATE (self); + + if (!always_ask) + tries = priv->pin_tries++; + + g_signal_emit_by_name (self, NM_MODEM_NEED_AUTH, + NM_SETTING_GSM_SETTING_NAME, + (tries || always_ask) ? TRUE : FALSE, + SECRETS_CALLER_MOBILE_BROADBAND, + NM_SETTING_GSM_PIN, + NULL); +} + +static void +stage1_prepare_done (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + NMModemGsm *self = NM_MODEM_GSM (user_data); + NMModemGsmPrivate *priv = NM_MODEM_GSM_GET_PRIVATE (self); + GError *error = NULL; + + priv->call = NULL; + + if (priv->connect_properties) { + g_hash_table_destroy (priv->connect_properties); + priv->connect_properties = NULL; + } + + if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, TRUE, NM_DEVICE_STATE_REASON_NONE); + else { + if (dbus_g_error_has_name (error, MM_MODEM_ERROR_SIM_PIN)) + ask_for_pin (self, FALSE); + else if (dbus_g_error_has_name (error, MM_MODEM_ERROR_SIM_WRONG)) + ask_for_pin (self, TRUE); + else { + nm_log_warn (LOGD_MB, "GSM connection failed: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (error)); + } + + g_error_free (error); + } +} + +static void +do_connect (NMModemGsm *self) +{ + NMModemGsmPrivate *priv = NM_MODEM_GSM_GET_PRIVATE (self); + DBusGProxy *proxy; + + proxy = nm_modem_get_proxy (NM_MODEM (self), MM_DBUS_INTERFACE_MODEM_SIMPLE); + dbus_g_proxy_begin_call_with_timeout (proxy, + "Connect", stage1_prepare_done, + self, NULL, 120000, + DBUS_TYPE_G_MAP_OF_VARIANT, priv->connect_properties, + G_TYPE_INVALID); +} + +static void stage1_enable_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data); + +static void +do_enable (NMModemGsm *self) +{ + DBusGProxy *proxy; + + g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_MODEM_GSM (self)); + + proxy = nm_modem_get_proxy (NM_MODEM (self), MM_DBUS_INTERFACE_MODEM); + dbus_g_proxy_begin_call_with_timeout (proxy, + "Enable", stage1_enable_done, + self, NULL, 20000, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_INVALID); +} + +static void +stage1_pin_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMModemGsm *self = NM_MODEM_GSM (user_data); + GError *error = NULL; + + if (dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) { + /* Success; go back and try the enable again */ + do_enable (self); + } else { + nm_log_warn (LOGD_MB, "GSM PIN unlock failed: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_error_free (error); + + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED); + } +} + +static void +handle_enable_pin_required (NMModemGsm *self) +{ + NMModemGsmPrivate *priv = NM_MODEM_GSM_GET_PRIVATE (self); + const char *pin = NULL; + GValue *value; + DBusGProxy *proxy; + + /* See if we have a PIN already */ + value = g_hash_table_lookup (priv->connect_properties, "pin"); + if (value && G_VALUE_HOLDS_STRING (value)) + pin = g_value_get_string (value); + + /* If we do, send it */ + if (pin) { + proxy = nm_modem_get_proxy (NM_MODEM (self), MM_DBUS_INTERFACE_MODEM_GSM_CARD); + dbus_g_proxy_begin_call_with_timeout (proxy, + "SendPin", stage1_pin_done, + self, NULL, 10000, + G_TYPE_STRING, pin, + G_TYPE_INVALID); + } else + ask_for_pin (self, FALSE); +} + +static void +stage1_enable_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMModemGsm *self = NM_MODEM_GSM (user_data); + GError *error = NULL; + + if (dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) + do_connect (self); + else { + nm_log_warn (LOGD_MB, "GSM modem enable failed: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + + if (dbus_g_error_has_name (error, MM_MODEM_ERROR_SIM_PIN)) + handle_enable_pin_required (self); + else + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED); + + g_error_free (error); + } +} + + +static GHashTable * +create_connect_properties (NMConnection *connection) +{ + NMSettingGsm *setting; + GHashTable *properties; + const char *str; + + setting = NM_SETTING_GSM (nm_connection_get_setting (connection, NM_TYPE_SETTING_GSM)); + properties = value_hash_create (); + + str = nm_setting_gsm_get_number (setting); + if (str) + value_hash_add_str (properties, "number", str); + + str = nm_setting_gsm_get_apn (setting); + if (str) + value_hash_add_str (properties, "apn", str); + + str = nm_setting_gsm_get_network_id (setting); + if (str) + value_hash_add_str (properties, "network_id", str); + + str = nm_setting_gsm_get_pin (setting); + if (str) + value_hash_add_str (properties, "pin", str); + + str = nm_setting_gsm_get_username (setting); + if (str) + value_hash_add_str (properties, "username", str); + + str = nm_setting_gsm_get_password (setting); + if (str) + value_hash_add_str (properties, "password", str); + + /* Add both old and new preferred modes */ + switch (nm_setting_gsm_get_network_type (setting)) { + case NM_SETTING_GSM_NETWORK_TYPE_UMTS_HSPA: + value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_ONLY); + value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY); + break; + case NM_SETTING_GSM_NETWORK_TYPE_GPRS_EDGE: + value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_ONLY); + value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY); + break; + case NM_SETTING_GSM_NETWORK_TYPE_PREFER_UMTS_HSPA: + value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_PREFERRED); + value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED); + break; + case NM_SETTING_GSM_NETWORK_TYPE_PREFER_GPRS_EDGE: + value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_PREFERRED); + value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED); + break; + default: + value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY); + value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_ANY); + break; + } + + /* Roaming */ + if (nm_setting_gsm_get_home_only (setting)) + value_hash_add_bool (properties, "home_only", TRUE); + + return properties; +} + +static NMActStageReturn +real_act_stage1_prepare (NMModem *modem, + NMActRequest *req, + GPtrArray **out_hints, + const char **out_setting_name, + NMDeviceStateReason *reason) +{ + NMModemGsm *self = NM_MODEM_GSM (modem); + NMModemGsmPrivate *priv = NM_MODEM_GSM_GET_PRIVATE (self); + NMConnection *connection; + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + *out_setting_name = nm_connection_need_secrets (connection, out_hints); + if (!*out_setting_name) { + gboolean enabled = nm_modem_get_mm_enabled (modem); + + if (priv->connect_properties) + g_hash_table_destroy (priv->connect_properties); + priv->connect_properties = create_connect_properties (connection); + + if (enabled) + do_connect (self); + else + do_enable (self); + } else { + /* NMModem will handle requesting secrets... */ + } + + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +static NMConnection * +real_get_best_auto_connection (NMModem *modem, + GSList *connections, + char **specific_object) +{ + GSList *iter; + + for (iter = connections; iter; iter = g_slist_next (iter)) { + NMConnection *connection = NM_CONNECTION (iter->data); + NMSettingConnection *s_con; + + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + + if (!nm_setting_connection_get_autoconnect (s_con)) + continue; + + if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_GSM_SETTING_NAME)) + continue; + + return connection; + } + return NULL; +} + +static gboolean +real_check_connection_compatible (NMModem *modem, + NMConnection *connection, + GError **error) +{ + NMSettingConnection *s_con; + NMSettingGsm *s_gsm; + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + + if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_GSM_SETTING_NAME)) { + g_set_error (error, + NM_GSM_ERROR, NM_GSM_ERROR_CONNECTION_NOT_GSM, + "The connection was not a GSM connection."); + return FALSE; + } + + s_gsm = NM_SETTING_GSM (nm_connection_get_setting (connection, NM_TYPE_SETTING_GSM)); + if (!s_gsm) { + g_set_error (error, + NM_GSM_ERROR, NM_GSM_ERROR_CONNECTION_INVALID, + "The connection was not a valid GSM connection."); + return FALSE; + } + + return TRUE; +} + +static gboolean +real_get_user_pass (NMModem *modem, + NMConnection *connection, + const char **user, + const char **pass) +{ + NMSettingGsm *s_gsm; + + s_gsm = (NMSettingGsm *) nm_connection_get_setting (connection, NM_TYPE_SETTING_GSM); + if (!s_gsm) + return FALSE; + + if (user) + *user = nm_setting_gsm_get_username (s_gsm); + if (pass) + *pass = nm_setting_gsm_get_password (s_gsm); + + return TRUE; +} + +static const char * +real_get_setting_name (NMModem *modem) +{ + return NM_SETTING_GSM_SETTING_NAME; +} + +static void +real_deactivate_quickly (NMModem *modem, NMDevice *device) +{ + NMModemGsmPrivate *priv = NM_MODEM_GSM_GET_PRIVATE (modem); + + if (priv->call) { + DBusGProxy *proxy; + + proxy = nm_modem_get_proxy (modem, MM_DBUS_INTERFACE_MODEM_SIMPLE); + dbus_g_proxy_cancel_call (proxy, priv->call); + priv->call = NULL; + } + + priv->pin_tries = 0; + + NM_MODEM_CLASS (nm_modem_gsm_parent_class)->deactivate_quickly (modem, device); +} + + +/*****************************************************************************/ + +static void +nm_modem_gsm_init (NMModemGsm *self) +{ +} + +static void +dispose (GObject *object) +{ + NMModemGsm *self = NM_MODEM_GSM (object); + NMModemGsmPrivate *priv = NM_MODEM_GSM_GET_PRIVATE (self); + + if (priv->connect_properties) + g_hash_table_destroy (priv->connect_properties); + + G_OBJECT_CLASS (nm_modem_gsm_parent_class)->dispose (object); +} + +static void +nm_modem_gsm_class_init (NMModemGsmClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMModemClass *modem_class = NM_MODEM_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMModemGsmPrivate)); + + /* Virtual methods */ + object_class->dispose = dispose; + modem_class->get_user_pass = real_get_user_pass; + modem_class->get_setting_name = real_get_setting_name; + modem_class->get_best_auto_connection = real_get_best_auto_connection; + modem_class->check_connection_compatible = real_check_connection_compatible; + modem_class->act_stage1_prepare = real_act_stage1_prepare; + modem_class->deactivate_quickly = real_deactivate_quickly; + + dbus_g_error_domain_register (NM_GSM_ERROR, NULL, NM_TYPE_GSM_ERROR); +} diff --git a/src/modem-manager/nm-modem-gsm.h b/src/modem-manager/nm-modem-gsm.h new file mode 100644 index 00000000..f481bdb8 --- /dev/null +++ b/src/modem-manager/nm-modem-gsm.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2009 Novell, Inc. + */ + +#ifndef NM_MODEM_GSM_H +#define NM_MODEM_GSM_H + +#include <nm-modem.h> + +G_BEGIN_DECLS + +#define NM_TYPE_MODEM_GSM (nm_modem_gsm_get_type ()) +#define NM_MODEM_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_GSM, NMModemGsm)) +#define NM_MODEM_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MODEM_GSM, NMModemGsmClass)) +#define NM_IS_MODEM_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM_GSM)) +#define NM_IS_MODEM_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_MODEM_GSM)) +#define NM_MODEM_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MODEM_GSM, NMModemGsmClass)) + +typedef struct { + NMModem parent; +} NMModemGsm; + +typedef struct { + NMModemClass parent; + + /* Signals */ + void (*signal_quality) (NMModemGsm *self, guint32 quality); +} NMModemGsmClass; + +GType nm_modem_gsm_get_type (void); + +NMModem *nm_modem_gsm_new (const char *path, + const char *device, + const char *data_device, + guint32 ip_method); + +G_END_DECLS + +#endif /* NM_MODEM_GSM_H */ diff --git a/src/modem-manager/nm-modem-manager.c b/src/modem-manager/nm-modem-manager.c new file mode 100644 index 00000000..28f7b94d --- /dev/null +++ b/src/modem-manager/nm-modem-manager.c @@ -0,0 +1,443 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 - 2010 Red Hat, Inc. + * Copyright (C) 2009 Novell, Inc. + * Copyright (C) 2009 Canonical Ltd. + */ + +#include <string.h> +#include "nm-modem-manager.h" +#include "nm-logging.h" +#include "nm-modem.h" +#include "nm-modem-gsm.h" +#include "nm-modem-cdma.h" +#include "nm-dbus-manager.h" +#include "nm-modem-types.h" +#include "nm-marshal.h" + +#define MODEM_POKE_INTERVAL 120 + +G_DEFINE_TYPE (NMModemManager, nm_modem_manager, G_TYPE_OBJECT) + +#define NM_MODEM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM_MANAGER, NMModemManagerPrivate)) + +typedef struct { + NMDBusManager *dbus_mgr; + DBusGProxy *proxy; + GHashTable *modems; + gboolean disposed; + guint poke_id; +} NMModemManagerPrivate; + +enum { + MODEM_ADDED, + MODEM_REMOVED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + + +NMModemManager * +nm_modem_manager_get (void) +{ + static NMModemManager *singleton = NULL; + + if (!singleton) + singleton = NM_MODEM_MANAGER (g_object_new (NM_TYPE_MODEM_MANAGER, NULL)); + else + g_object_ref (singleton); + + g_assert (singleton); + return singleton; +} + +static gboolean +get_modem_properties (DBusGConnection *connection, + const char *path, + char **device, + char **data_device, + char **driver, + guint32 *type, + guint32 *ip_method) +{ + DBusGProxy *proxy; + GValue value = { 0 }; + GError *err = NULL; + + proxy = dbus_g_proxy_new_for_name (connection, + MM_DBUS_SERVICE, + path, + "org.freedesktop.DBus.Properties"); + + if (dbus_g_proxy_call_with_timeout (proxy, "Get", 15000, &err, + G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM, + G_TYPE_STRING, "Type", + G_TYPE_INVALID, + G_TYPE_VALUE, &value, + G_TYPE_INVALID)) { + *type = g_value_get_uint (&value); + g_value_unset (&value); + } else { + nm_log_warn (LOGD_MB, "could not get device type: %s", err->message); + goto out; + } + + if (dbus_g_proxy_call_with_timeout (proxy, "Get", 15000, &err, + G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM, + G_TYPE_STRING, "MasterDevice", + G_TYPE_INVALID, + G_TYPE_VALUE, &value, + G_TYPE_INVALID)) { + *device = g_value_dup_string (&value); + g_value_unset (&value); + } else { + nm_log_warn (LOGD_MB, "could not get device: %s", err->message); + goto out; + } + + if (dbus_g_proxy_call_with_timeout (proxy, "Get", 15000, &err, + G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM, + G_TYPE_STRING, "IpMethod", + G_TYPE_INVALID, + G_TYPE_VALUE, &value, + G_TYPE_INVALID)) { + *ip_method = g_value_get_uint (&value); + g_value_unset (&value); + } else { + nm_log_warn (LOGD_MB, "could not get IP method: %s", err->message); + goto out; + } + + if (dbus_g_proxy_call_with_timeout (proxy, "Get", 15000, &err, + G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM, + G_TYPE_STRING, "Device", + G_TYPE_INVALID, + G_TYPE_VALUE, &value, + G_TYPE_INVALID)) { + *data_device = g_value_dup_string (&value); + g_value_unset (&value); + } else { + nm_log_warn (LOGD_MB, "could not get modem data device: %s", err->message); + goto out; + } + + if (dbus_g_proxy_call_with_timeout (proxy, "Get", 15000, &err, + G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM, + G_TYPE_STRING, "Driver", + G_TYPE_INVALID, + G_TYPE_VALUE, &value, + G_TYPE_INVALID)) { + *driver = g_value_dup_string (&value); + g_value_unset (&value); + } else { + nm_log_warn (LOGD_MB, "could not get modem driver: %s", err->message); + goto out; + } + + out: + if (err) + g_error_free (err); + + g_object_unref (proxy); + + return *data_device && *driver; +} + +static void +create_modem (NMModemManager *manager, const char *path) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (manager); + NMModem *modem = NULL; + char *data_device = NULL, *driver = NULL, *master_device = NULL; + uint modem_type = MM_MODEM_TYPE_UNKNOWN; + uint ip_method = MM_MODEM_IP_METHOD_PPP; + + if (g_hash_table_lookup (priv->modems, path)) { + nm_log_warn (LOGD_MB, "modem with path %s already exists, ignoring", path); + return; + } + + if (!get_modem_properties (nm_dbus_manager_get_connection (priv->dbus_mgr), + path, &master_device, &data_device, &driver, + &modem_type, &ip_method)) + return; + + if (modem_type == MM_MODEM_TYPE_UNKNOWN) { + nm_log_warn (LOGD_MB, "modem with path %s has unknown type, ignoring", path); + return; + } + + if (!master_device || !strlen (master_device)) { + nm_log_warn (LOGD_MB, "modem with path %s has unknown device, ignoring", path); + return; + } + + if (!driver || !strlen (driver)) { + nm_log_warn (LOGD_MB, "modem with path %s has unknown driver, ignoring", path); + return; + } + + if (!data_device || !strlen (data_device)) { + nm_log_warn (LOGD_MB, "modem with path %s has unknown data device, ignoring", path); + return; + } + + if (modem_type == MM_MODEM_TYPE_GSM) + modem = nm_modem_gsm_new (path, master_device, data_device, ip_method); + else if (modem_type == MM_MODEM_TYPE_CDMA) + modem = nm_modem_cdma_new (path, master_device, data_device, ip_method); + else + nm_log_warn (LOGD_MB, "unknown modem type '%d'", modem_type); + + g_free (data_device); + + if (modem) { + g_hash_table_insert (priv->modems, g_strdup (path), modem); + g_signal_emit (manager, signals[MODEM_ADDED], 0, modem, driver); + } + + g_free (driver); +} + +static void +modem_added (DBusGProxy *proxy, const char *path, gpointer user_data) +{ + create_modem (NM_MODEM_MANAGER (user_data), path); +} + +static void +modem_removed (DBusGProxy *proxy, const char *path, gpointer user_data) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (user_data); + NMModem *modem; + + modem = (NMModem *) g_hash_table_lookup (priv->modems, path); + if (modem) { + g_signal_emit (user_data, signals[MODEM_REMOVED], 0, modem); + g_hash_table_remove (priv->modems, path); + } +} + +static gboolean +poke_modem_cb (gpointer user_data) +{ + NMModemManager *self = NM_MODEM_MANAGER (user_data); + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + DBusGConnection *g_connection; + DBusGProxy *proxy; + + g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr); + proxy = dbus_g_proxy_new_for_name (g_connection, + MM_DBUS_SERVICE, + MM_DBUS_PATH, + MM_DBUS_INTERFACE); + + dbus_g_proxy_call_no_reply (proxy, "EnumerateDevices", G_TYPE_INVALID); + g_object_unref (proxy); + + return TRUE; +} + +static void +enumerate_devices_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer data) +{ + NMModemManager *manager = NM_MODEM_MANAGER (data); + GPtrArray *modems; + GError *error = NULL; + + if (!dbus_g_proxy_end_call (proxy, call_id, &error, + dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &modems, + G_TYPE_INVALID)) { + nm_log_warn (LOGD_MB, "could not get modem list: %s", error->message); + g_error_free (error); + } else { + int i; + + for (i = 0; i < modems->len; i++) { + char *path = (char *) g_ptr_array_index (modems, i); + + create_modem (manager, path); + g_free (path); + } + + g_ptr_array_free (modems, TRUE); + } +} + +static void +modem_manager_appeared (NMModemManager *self, gboolean enumerate_devices) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + if (priv->poke_id) { + g_source_remove (priv->poke_id); + priv->poke_id = 0; + } + + nm_log_info (LOGD_MB, "modem-manager is now available"); + + priv->proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr), + MM_DBUS_SERVICE, MM_DBUS_PATH, MM_DBUS_INTERFACE); + + dbus_g_proxy_add_signal (priv->proxy, "DeviceAdded", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "DeviceAdded", + G_CALLBACK (modem_added), self, + NULL); + + dbus_g_proxy_add_signal (priv->proxy, "DeviceRemoved", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "DeviceRemoved", + G_CALLBACK (modem_removed), self, + NULL); + + if (enumerate_devices) + dbus_g_proxy_begin_call (priv->proxy, "EnumerateDevices", enumerate_devices_done, self, NULL, G_TYPE_INVALID); +} + +static gboolean +remove_one_modem (gpointer key, gpointer value, gpointer user_data) +{ + g_signal_emit (user_data, signals[MODEM_REMOVED], 0, value); + + return TRUE; +} + +static void +modem_manager_disappeared (NMModemManager *self) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + g_hash_table_foreach_remove (priv->modems, remove_one_modem, self); + + if (priv->proxy) { + g_object_unref (priv->proxy); + priv->proxy = NULL; + } + + /* Try to activate the modem-manager */ + nm_log_info (LOGD_MB, "trying to start the modem manager..."); + poke_modem_cb (self); + priv->poke_id = g_timeout_add_seconds (MODEM_POKE_INTERVAL, poke_modem_cb, self); +} + +static void +nm_modem_manager_name_owner_changed (NMDBusManager *dbus_mgr, + const char *name, + const char *old_owner, + const char *new_owner, + gpointer user_data) +{ + gboolean old_owner_good; + gboolean new_owner_good; + + /* Can't handle the signal if its not from the modem service */ + if (strcmp (MM_DBUS_SERVICE, name) != 0) + return; + + old_owner_good = (old_owner && strlen (old_owner)); + new_owner_good = (new_owner && strlen (new_owner)); + + if (!old_owner_good && new_owner_good) { + modem_manager_appeared (NM_MODEM_MANAGER (user_data), FALSE); + } else if (old_owner_good && !new_owner_good) { + nm_log_info (LOGD_MB, "the modem manager disappeared"); + modem_manager_disappeared (NM_MODEM_MANAGER (user_data)); + } +} + +/*******************************************************/ + +static void +nm_modem_manager_init (NMModemManager *self) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + priv->dbus_mgr = nm_dbus_manager_get (); + + g_signal_connect (priv->dbus_mgr, "name-owner-changed", + G_CALLBACK (nm_modem_manager_name_owner_changed), + self); + + if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, MM_DBUS_SERVICE)) + modem_manager_appeared (self, TRUE); + else + modem_manager_disappeared (self); +} + +static void +dispose (GObject *object) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (object); + + if (priv->disposed) + return; + + priv->disposed = TRUE; + + if (priv->poke_id) { + g_source_remove (priv->poke_id); + priv->poke_id = 0; + } + + g_hash_table_foreach_remove (priv->modems, remove_one_modem, object); + g_hash_table_destroy (priv->modems); + + if (priv->proxy) { + g_object_unref (priv->proxy); + priv->proxy = NULL; + } + + if (priv->dbus_mgr) { + g_object_unref (priv->dbus_mgr); + priv->dbus_mgr = NULL; + } + + /* Chain up to the parent class */ + G_OBJECT_CLASS (nm_modem_manager_parent_class)->dispose (object); +} + +static void +nm_modem_manager_class_init (NMModemManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMModemManagerPrivate)); + + object_class->dispose = dispose; + + /* signals */ + signals[MODEM_ADDED] = + g_signal_new ("modem-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemManagerClass, modem_added), + NULL, NULL, + _nm_marshal_VOID__OBJECT_STRING, + G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_STRING); + + signals[MODEM_REMOVED] = + g_signal_new ("modem-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemManagerClass, modem_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); +} diff --git a/src/modem-manager/nm-modem-manager.h b/src/modem-manager/nm-modem-manager.h new file mode 100644 index 00000000..56427baf --- /dev/null +++ b/src/modem-manager/nm-modem-manager.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2009 Novell, Inc. + * Copyright (C) 2009 Canonical Ltd. + */ + +#ifndef NM_MODEM_MANAGER_H +#define NM_MODEM_MANAGER_H + +#include <glib-object.h> +#include "nm-modem.h" + +#define NM_TYPE_MODEM_MANAGER (nm_modem_manager_get_type ()) +#define NM_MODEM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_MANAGER, NMModemManager)) +#define NM_MODEM_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MODEM_MANAGER, NMModemManagerClass)) +#define NM_IS_MODEM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM_MANAGER)) +#define NM_IS_MODEM_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_MODEM_MANAGER)) +#define NM_MODEM_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MODEM_MANAGER, NMModemManagerClass)) + +typedef struct { + GObject parent; +} NMModemManager; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*modem_added) (NMModemManager *manager, NMModem *modem, const char *driver); + + void (*modem_removed) (NMModemManager *manager, NMModem *modem); +} NMModemManagerClass; + +GType nm_modem_manager_get_type (void); + +NMModemManager *nm_modem_manager_get (void); + +#endif /* NM_MODEM_MANAGER_H */ diff --git a/src/modem-manager/nm-modem-types.h b/src/modem-manager/nm-modem-types.h new file mode 100644 index 00000000..e805cb21 --- /dev/null +++ b/src/modem-manager/nm-modem-types.h @@ -0,0 +1,111 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 Novell, Inc. + */ + +#ifndef NM_MODEM_TYPES_H +#define NM_MODEM_TYPES_H + +#define MM_DBUS_SERVICE "org.freedesktop.ModemManager" +#define MM_DBUS_PATH "/org/freedesktop/ModemManager" +#define MM_DBUS_INTERFACE "org.freedesktop.ModemManager" +#define MM_DBUS_INTERFACE_MODEM "org.freedesktop.ModemManager.Modem" +#define MM_DBUS_INTERFACE_MODEM_SIMPLE "org.freedesktop.ModemManager.Modem.Simple" +#define MM_DBUS_INTERFACE_MODEM_CDMA "org.freedesktop.ModemManager.Modem.Cdma" + +#define MM_DBUS_INTERFACE_MODEM_GSM_CARD "org.freedesktop.ModemManager.Modem.Gsm.Card" +#define MM_DBUS_INTERFACE_MODEM_GSM_NETWORK "org.freedesktop.ModemManager.Modem.Gsm.Network" + +#define MM_MODEM_TYPE_UNKNOWN 0 +#define MM_MODEM_TYPE_GSM 1 +#define MM_MODEM_TYPE_CDMA 2 + +#define MM_MODEM_IP_METHOD_PPP 0 +#define MM_MODEM_IP_METHOD_STATIC 1 +#define MM_MODEM_IP_METHOD_DHCP 2 + +/* Errors */ + +#define MM_SERIAL_OPEN_FAILED MM_DBUS_INTERFACE_MODEM ".SerialOpenFailed" +#define MM_SERIAL_SEND_FAILED MM_DBUS_INTERFACE_MODEM ".SerialSendFailed" +#define MM_SERIAL_RESPONSE_TIMEOUT MM_DBUS_INTERFACE_MODEM ".SerialResponseTimeout" + +#define MM_MODEM_ERROR_GENERAL MM_DBUS_INTERFACE_MODEM ".General" +#define MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED MM_DBUS_INTERFACE_MODEM ".OperationNotSupported" + +#define MM_MODEM_CONNECT_ERROR_NO_CARRIER MM_DBUS_INTERFACE_MODEM ".NoCarrier" +#define MM_MODEM_CONNECT_ERROR_NO_DIALTONE MM_DBUS_INTERFACE_MODEM ".NoDialtone" +#define MM_MODEM_CONNECT_ERROR_BUSY MM_DBUS_INTERFACE_MODEM ".Busy" +#define MM_MODEM_CONNECT_ERROR_NO_ANSWER MM_DBUS_INTERFACE_MODEM ".NoAnswer" + +#define MM_MODEM_ERROR "org.freedesktop.ModemManager.Modem.Gsm" + +#define MM_MODEM_ERROR_PHONE_FAILURE MM_MODEM_ERROR ".PhoneFailure" +#define MM_MODEM_ERROR_NO_CONNECTION MM_MODEM_ERROR ".NoConnection" +#define MM_MODEM_ERROR_LINK_RESERVED MM_MODEM_ERROR ".LinkReserved" +#define MM_MODEM_ERROR_NOT_ALLOWED MM_MODEM_ERROR ".OperationNotAllowed" +#define MM_MODEM_ERROR_NOT_SUPPORTED MM_MODEM_ERROR ".OperationNotSupported" +#define MM_MODEM_ERROR_PH_SIM_PIN MM_MODEM_ERROR ".PhSimPinRequired" +#define MM_MODEM_ERROR_PH_FSIM_PIN MM_MODEM_ERROR ".PhFSimPinRequired" +#define MM_MODEM_ERROR_PH_FSIM_PUK MM_MODEM_ERROR ".PhFPukRequired" +#define MM_MODEM_ERROR_SIM_NOT_INSERTED MM_MODEM_ERROR ".SimNotInserted" +#define MM_MODEM_ERROR_SIM_PIN MM_MODEM_ERROR ".SimPinRequired" +#define MM_MODEM_ERROR_SIM_PUK MM_MODEM_ERROR ".SimPukRequired" +#define MM_MODEM_ERROR_SIM_FAILURE MM_MODEM_ERROR ".SimFailure" +#define MM_MODEM_ERROR_SIM_BUSY MM_MODEM_ERROR ".SimBusy" +#define MM_MODEM_ERROR_SIM_WRONG MM_MODEM_ERROR ".SimWrong" +#define MM_MODEM_ERROR_WRONG_PASSWORD MM_MODEM_ERROR ".IncorrectPassword" +#define MM_MODEM_ERROR_SIM_PIN2 MM_MODEM_ERROR ".SimPin2Required" +#define MM_MODEM_ERROR_SIM_PUK2 MM_MODEM_ERROR ".SimPuk2Required" +#define MM_MODEM_ERROR_MEMORY_FULL MM_MODEM_ERROR ".MemoryFull" +#define MM_MODEM_ERROR_INVALID_INDEX MM_MODEM_ERROR ".InvalidIndex" +#define MM_MODEM_ERROR_NOT_FOUND MM_MODEM_ERROR ".NotFound" +#define MM_MODEM_ERROR_MEMORY_FAILURE MM_MODEM_ERROR ".MemoryFailure" +#define MM_MODEM_ERROR_TEXT_TOO_LONG MM_MODEM_ERROR ".TextTooLong" +#define MM_MODEM_ERROR_INVALID_CHARS MM_MODEM_ERROR ".InvalidChars" +#define MM_MODEM_ERROR_DIAL_STRING_TOO_LONG MM_MODEM_ERROR ".DialStringTooLong" +#define MM_MODEM_ERROR_DIAL_STRING_INVALID MM_MODEM_ERROR ".InvalidDialString" +#define MM_MODEM_ERROR_NO_NETWORK MM_MODEM_ERROR ".NoNetwork" +#define MM_MODEM_ERROR_NETWORK_TIMEOUT MM_MODEM_ERROR ".NetworkTimeout" +#define MM_MODEM_ERROR_NETWORK_NOT_ALLOWED MM_MODEM_ERROR ".NetworkNotAllowed" +#define MM_MODEM_ERROR_NETWORK_PIN MM_MODEM_ERROR ".NetworkPinRequired" +#define MM_MODEM_ERROR_NETWORK_PUK MM_MODEM_ERROR ".NetworkPukRequired" +#define MM_MODEM_ERROR_NETWORK_SUBSET_PIN MM_MODEM_ERROR ".NetworkSubsetPinRequired" +#define MM_MODEM_ERROR_NETWORK_SUBSET_PUK MM_MODEM_ERROR ".NetworkSubsetPukRequired" +#define MM_MODEM_ERROR_SERVICE_PIN MM_MODEM_ERROR ".ServicePinRequired" +#define MM_MODEM_ERROR_SERVICE_PUK MM_MODEM_ERROR ".ServicePukRequired" +#define MM_MODEM_ERROR_CORP_PIN MM_MODEM_ERROR ".CorporatePinRequired" +#define MM_MODEM_ERROR_CORP_PUK MM_MODEM_ERROR ".CorporatePukRequired" +#define MM_MODEM_ERROR_HIDDEN_KEY MM_MODEM_ERROR ".HiddenKeyRequired" +#define MM_MODEM_ERROR_EAP_NOT_SUPPORTED MM_MODEM_ERROR ".EapMethodNotSupported" +#define MM_MODEM_ERROR_INCORRECT_PARAMS MM_MODEM_ERROR ".IncorrectParams" +#define MM_MODEM_ERROR_UNKNOWN MM_MODEM_ERROR ".Unknown" +#define MM_MODEM_ERROR_GPRS_ILLEGAL_MS MM_MODEM_ERROR ".GprsIllegalMs" +#define MM_MODEM_ERROR_GPRS_ILLEGAL_ME MM_MODEM_ERROR ".GprsIllegalMe" +#define MM_MODEM_ERROR_GPRS_SERVICE_NOT_ALLOWED MM_MODEM_ERROR ".GprsServiceNotAllowed" +#define MM_MODEM_ERROR_GPRS_PLMN_NOT_ALLOWED MM_MODEM_ERROR ".GprsPlmnNotAllowed" +#define MM_MODEM_ERROR_GPRS_LOCATION_NOT_ALLOWED MM_MODEM_ERROR ".GprsLocationNotAllowed" +#define MM_MODEM_ERROR_GPRS_ROAMING_NOT_ALLOWED MM_MODEM_ERROR ".GprsRoamingNotAllowed" +#define MM_MODEM_ERROR_GPRS_OPTION_NOT_SUPPORTED MM_MODEM_ERROR ".GprsOptionNotSupported" +#define MM_MODEM_ERROR_GPRS_NOT_SUBSCRIBED MM_MODEM_ERROR ".GprsNotSubscribed" +#define MM_MODEM_ERROR_GPRS_OUT_OF_ORDER MM_MODEM_ERROR ".GprsOutOfOrder" +#define MM_MODEM_ERROR_GPRS_PDP_AUTH_FAILURE MM_MODEM_ERROR ".GprsPdpAuthFailure" +#define MM_MODEM_ERROR_GPRS_UNKNOWN MM_MODEM_ERROR ".GprsUnspecified" +#define MM_MODEM_ERROR_GPRS_INVALID_CLASS MM_MODEM_ERROR ".GprsInvalidClass" + +#endif /* NM_MODEM_TYPES_H */ diff --git a/src/modem-manager/nm-modem.c b/src/modem-manager/nm-modem.c new file mode 100644 index 00000000..e07b818b --- /dev/null +++ b/src/modem-manager/nm-modem.c @@ -0,0 +1,1147 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 - 2010 Red Hat, Inc. + * Copyright (C) 2009 Novell, Inc. + */ + +#include <string.h> +#include "nm-modem.h" +#include "nm-system.h" +#include "nm-dbus-manager.h" +#include "nm-setting-connection.h" +#include "nm-marshal.h" +#include "nm-properties-changed-signal.h" +#include "nm-modem-types.h" +#include "nm-logging.h" +#include "NetworkManagerUtils.h" +#include "nm-device-private.h" +#include "nm-device-interface.h" +#include "nm-dbus-glib-types.h" + +#include "nm-serial-device-glue.h" + +G_DEFINE_TYPE (NMModem, nm_modem, G_TYPE_OBJECT) + +#define NM_MODEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM, NMModemPrivate)) + +enum { + PROP_0, + PROP_DEVICE, + PROP_IFACE, + PROP_PATH, + PROP_IP_METHOD, + PROP_ENABLED, + + LAST_PROP +}; + +typedef struct { + NMDBusManager *dbus_mgr; + DBusGProxy *proxy; + DBusGProxy *props_proxy; + + char *path; + NMPPPManager *ppp_manager; + NMIP4Config *pending_ip4_config; + guint32 ip_method; + char *device; + char *iface; + + guint32 secrets_tries; + + DBusGProxyCall *call; + + gboolean mm_enabled; + + /* PPP stats */ + guint32 in_bytes; + guint32 out_bytes; +} NMModemPrivate; + +enum { + PPP_STATS, + PPP_FAILED, + PREPARE_RESULT, + IP4_CONFIG_RESULT, + NEED_AUTH, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +NMPPPManager * +nm_modem_get_ppp_manager (NMModem *self) +{ + g_return_val_if_fail (NM_IS_MODEM (self), NULL); + + return NM_MODEM_GET_PRIVATE (self)->ppp_manager; +} + +gboolean +nm_modem_get_mm_enabled (NMModem *self) +{ + g_return_val_if_fail (NM_IS_MODEM (self), TRUE); + + return NM_MODEM_GET_PRIVATE (self)->mm_enabled; +} + +DBusGProxy * +nm_modem_get_proxy (NMModem *self, + const char *interface) +{ + + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + const char *current_iface; + + g_return_val_if_fail (NM_IS_MODEM (self), NULL); + + /* Default to the default interface. */ + if (interface == NULL) + interface = MM_DBUS_INTERFACE_MODEM; + + if (interface && !strcmp (interface, DBUS_INTERFACE_PROPERTIES)) + return priv->props_proxy; + + current_iface = dbus_g_proxy_get_interface (priv->proxy); + if (!current_iface || strcmp (current_iface, interface)) + dbus_g_proxy_set_interface (priv->proxy, interface); + + return priv->proxy; +} + +static void +merge_ip4_config (NMActRequest *req, NMIP4Config *config) +{ + NMConnection *connection; + NMSettingIP4Config *s_ip4; + + /* Merge user-defined overrides into the IP4Config to be applied */ + connection = nm_act_request_get_connection (req); + g_assert (connection); + + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + if (s_ip4) + nm_utils_merge_ip4_config (config, s_ip4); +} + +/*****************************************************************************/ +/* IP method PPP */ + +static void +ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_data) +{ + switch (status) { + case NM_PPP_STATUS_DISCONNECT: + g_signal_emit (NM_MODEM (user_data), signals[PPP_FAILED], 0, NM_DEVICE_STATE_REASON_PPP_DISCONNECT); + break; + case NM_PPP_STATUS_DEAD: + g_signal_emit (NM_MODEM (user_data), signals[PPP_FAILED], 0, NM_DEVICE_STATE_REASON_PPP_FAILED); + break; + default: + break; + } +} + +static void +ppp_ip4_config (NMPPPManager *ppp_manager, + const char *iface, + NMIP4Config *config, + gpointer user_data) +{ + NMModem *self = NM_MODEM (user_data); + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + guint32 i, num; + guint32 bad_dns1 = htonl (0x0A0B0C0D); + guint32 good_dns1 = htonl (0x04020201); /* GTE nameserver */ + guint32 bad_dns2 = htonl (0x0A0B0C0E); + guint32 good_dns2 = htonl (0x04020202); /* GTE nameserver */ + gboolean dns_workaround = FALSE; + + /* Work around a PPP bug (#1732) which causes many mobile broadband + * providers to return 10.11.12.13 and 10.11.12.14 for the DNS servers. + * Apparently fixed in ppp-2.4.5 but we've had some reports that this is + * not the case. + * + * http://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=2e09ef6886bbf00bc5a9a641110f801e372ffde6 + * http://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=f8191bf07df374f119a07910a79217c7618f113e + */ + + num = nm_ip4_config_get_num_nameservers (config); + if (num == 2) { + gboolean found1 = FALSE, found2 = FALSE; + + for (i = 0; i < num; i++) { + guint32 ns = nm_ip4_config_get_nameserver (config, i); + + if (ns == bad_dns1) + found1 = TRUE; + else if (ns == bad_dns2) + found2 = TRUE; + } + + /* Be somewhat conservative about substitutions; the "bad" nameservers + * could actually be valid in some cases, so only substitute if ppp + * returns *only* the two bad nameservers. + */ + dns_workaround = (found1 && found2); + } + + if (!num || dns_workaround) { + nm_log_warn (LOGD_PPP, "compensating for invalid PPP-provided nameservers"); + nm_ip4_config_reset_nameservers (config); + nm_ip4_config_add_nameserver (config, good_dns1); + nm_ip4_config_add_nameserver (config, good_dns2); + } + + priv->pending_ip4_config = g_object_ref (config); + g_signal_emit (self, signals[IP4_CONFIG_RESULT], 0, iface, config, NULL); +} + +static void +ppp_stats (NMPPPManager *ppp_manager, + guint32 in_bytes, + guint32 out_bytes, + gpointer user_data) +{ + NMModem *self = NM_MODEM (user_data); + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + + if (priv->in_bytes != in_bytes || priv->out_bytes != out_bytes) { + priv->in_bytes = in_bytes; + priv->out_bytes = out_bytes; + + g_signal_emit (self, signals[PPP_STATS], 0, in_bytes, out_bytes); + } +} + +static NMActStageReturn +ppp_stage3_ip4_config_start (NMModem *self, + NMActRequest *req, + NMDeviceStateReason *reason) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + const char *ppp_name = NULL; + GError *error = NULL; + NMActStageReturn ret; + + g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + if (NM_MODEM_GET_CLASS (self)->get_user_pass) { + NMConnection *connection = nm_act_request_get_connection (req); + + g_assert (connection); + if (!NM_MODEM_GET_CLASS (self)->get_user_pass (self, connection, &ppp_name, NULL)) + return NM_ACT_STAGE_RETURN_FAILURE; + } + + priv->ppp_manager = nm_ppp_manager_new (priv->iface); + if (nm_ppp_manager_start (priv->ppp_manager, req, ppp_name, 20, &error)) { + g_signal_connect (priv->ppp_manager, "state-changed", + G_CALLBACK (ppp_state_changed), + self); + g_signal_connect (priv->ppp_manager, "ip4-config", + G_CALLBACK (ppp_ip4_config), + self); + g_signal_connect (priv->ppp_manager, "stats", + G_CALLBACK (ppp_stats), + self); + + ret = NM_ACT_STAGE_RETURN_POSTPONE; + } else { + nm_log_err (LOGD_PPP, "error starting PPP: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_error_free (error); + + g_object_unref (priv->ppp_manager); + priv->ppp_manager = NULL; + + *reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED; + ret = NM_ACT_STAGE_RETURN_FAILURE; + } + + return ret; +} + +static NMActStageReturn +ppp_stage4 (NMModem *self, + NMActRequest *req, + NMIP4Config **config, + NMDeviceStateReason *reason) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + + *config = priv->pending_ip4_config; + priv->pending_ip4_config = NULL; + + merge_ip4_config (req, *config); + + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +/*****************************************************************************/ +/* IP method static */ + +static void +static_stage3_done (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + NMModem *self = NM_MODEM (user_data); + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + GValueArray *ret_array = NULL; + GError *error = NULL; + NMIP4Config *config = NULL; + + priv->call = NULL; + + if (dbus_g_proxy_end_call (proxy, call, &error, + G_TYPE_VALUE_ARRAY, &ret_array, + G_TYPE_INVALID)) { + NMIP4Address *addr; + int i; + + config = nm_ip4_config_new (); + + addr = nm_ip4_address_new (); + nm_ip4_address_set_address (addr, g_value_get_uint (g_value_array_get_nth (ret_array, 0))); + nm_ip4_address_set_prefix (addr, 32); + nm_ip4_config_take_address (config, addr); + + for (i = 0; i < ret_array->n_values; i++) { + GValue *value = g_value_array_get_nth (ret_array, i); + + nm_ip4_config_add_nameserver (config, g_value_get_uint (value)); + } + g_value_array_free (ret_array); + + priv->pending_ip4_config = g_object_ref (config); + g_signal_emit (self, signals[IP4_CONFIG_RESULT], 0, NULL, config, NULL); + } + + g_signal_emit (self, signals[IP4_CONFIG_RESULT], 0, NULL, config, error); + g_clear_error (&error); +} + +static NMActStageReturn +static_stage3_ip4_config_start (NMModem *self, + NMActRequest *req, + NMDeviceStateReason *reason) +{ + NMModemPrivate *priv; + + g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + priv = NM_MODEM_GET_PRIVATE (self); + + priv->call = dbus_g_proxy_begin_call (nm_modem_get_proxy (self, MM_DBUS_INTERFACE_MODEM), + "GetIP4Config", static_stage3_done, + self, NULL, + G_TYPE_INVALID); + + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +static NMActStageReturn +static_stage4 (NMModem *self, + NMActRequest *req, + NMDevice *device, + NMIP4Config **config, + NMDeviceStateReason *reason) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + gboolean no_firmware = FALSE; + + if (!nm_device_hw_bring_up (device, TRUE, &no_firmware)) { + if (no_firmware) + *reason = NM_DEVICE_STATE_REASON_FIRMWARE_MISSING; + else + *reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED; + return NM_ACT_STAGE_RETURN_FAILURE; + } + + *config = priv->pending_ip4_config; + priv->pending_ip4_config = NULL; + + merge_ip4_config (req, *config); + + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +/*****************************************************************************/ + +NMActStageReturn +nm_modem_stage3_ip4_config_start (NMModem *self, + NMDevice *device, + NMDeviceClass *device_class, + NMDeviceStateReason *reason) +{ + NMModemPrivate *priv; + NMActRequest *req; + NMActStageReturn ret; + + g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (device != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (NM_IS_DEVICE (device), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (device_class != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (NM_IS_DEVICE_CLASS (device_class), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + req = nm_device_get_act_request (device); + g_assert (req); + + priv = NM_MODEM_GET_PRIVATE (self); + switch (priv->ip_method) { + case MM_MODEM_IP_METHOD_PPP: + ret = ppp_stage3_ip4_config_start (self, req, reason); + break; + case MM_MODEM_IP_METHOD_STATIC: + ret = static_stage3_ip4_config_start (self, req, reason); + break; + case MM_MODEM_IP_METHOD_DHCP: + ret = device_class->act_stage3_ip4_config_start (device, reason); + break; + default: + nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method); + ret = NM_ACT_STAGE_RETURN_FAILURE; + break; + } + + return ret; +} + +NMActStageReturn +nm_modem_stage4_get_ip4_config (NMModem *self, + NMDevice *device, + NMDeviceClass *device_class, + NMIP4Config **config, + NMDeviceStateReason *reason) +{ + NMModemPrivate *priv; + NMActRequest *req; + NMActStageReturn ret; + + g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (device != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (NM_IS_DEVICE (device), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (device_class != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (NM_IS_DEVICE_CLASS (device_class), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + req = nm_device_get_act_request (device); + g_assert (req); + + priv = NM_MODEM_GET_PRIVATE (self); + switch (priv->ip_method) { + case MM_MODEM_IP_METHOD_PPP: + ret = ppp_stage4 (self, req, config, reason); + break; + case MM_MODEM_IP_METHOD_STATIC: + ret = static_stage4 (self, req, device, config, reason); + break; + case MM_MODEM_IP_METHOD_DHCP: + ret = device_class->act_stage4_get_ip4_config (device, config, reason); + break; + default: + nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method); + ret = NM_ACT_STAGE_RETURN_FAILURE; + break; + } + + return ret; +} + +gboolean +nm_modem_connection_secrets_updated (NMModem *self, + NMActRequest *req, + NMConnection *connection, + GSList *updated_settings, + RequestSecretsCaller caller) +{ + NMModemPrivate *priv; + gboolean found = FALSE; + const char *setting_name; + GSList *iter; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_MODEM (self), FALSE); + g_return_val_if_fail (req != NULL, FALSE); + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE); + g_return_val_if_fail (connection != NULL, FALSE); + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE); + + priv = NM_MODEM_GET_PRIVATE (self); + + if (caller == SECRETS_CALLER_PPP) { + const char *user = NULL; + const char *pass = NULL; + + g_return_val_if_fail (priv->ppp_manager != NULL, FALSE); + + if (!NM_MODEM_GET_CLASS (self)->get_user_pass (self, connection, &user, &pass)) { + /* Shouldn't ever happen */ + nm_ppp_manager_update_secrets (priv->ppp_manager, + priv->iface, + NULL, + NULL, + "missing GSM/CDMA setting; no secrets could be found."); + } else { + nm_ppp_manager_update_secrets (priv->ppp_manager, + priv->iface, + user ? user : "", + pass ? pass : "", + NULL); + } + return TRUE; + } + + g_return_val_if_fail (caller == SECRETS_CALLER_MOBILE_BROADBAND, FALSE); + + g_assert (NM_MODEM_GET_CLASS (self)->get_setting_name); + setting_name = NM_MODEM_GET_CLASS (self)->get_setting_name (self); + + for (iter = updated_settings; iter; iter = g_slist_next (iter)) { + const char *candidate_setting_name = (const char *) iter->data; + + if (!strcmp (candidate_setting_name, setting_name)) + found = TRUE; + else { + nm_log_warn (LOGD_MB, "ignoring updated secrets for setting '%s'.", + candidate_setting_name); + } + } + + return found; +} + +static NMActStageReturn +real_act_stage1_prepare (NMModem *modem, + NMActRequest *req, + GPtrArray **out_hints, + const char **out_setting_name, + NMDeviceStateReason *reason) +{ + *reason = NM_DEVICE_STATE_REASON_UNKNOWN; + return NM_ACT_STAGE_RETURN_FAILURE; +} + +NMActStageReturn +nm_modem_act_stage1_prepare (NMModem *self, + NMActRequest *req, + NMDeviceStateReason *reason) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + NMActStageReturn ret; + GPtrArray *hints = NULL; + const char *setting_name = NULL; + + ret = NM_MODEM_GET_CLASS (self)->act_stage1_prepare (self, + req, + &hints, + &setting_name, + reason); + if ((ret == NM_ACT_STAGE_RETURN_POSTPONE) && setting_name) { + const char *hint1 = NULL, *hint2 = NULL; + + /* Need some secrets */ + if (hints) { + if (hints->len > 0) + hint1 = g_ptr_array_index (hints, 0); + if (hints->len > 1) + hint2 = g_ptr_array_index (hints, 1); + } + + g_signal_emit (self, signals[NEED_AUTH], 0, + setting_name, + priv->secrets_tries++ ? TRUE : FALSE, + SECRETS_CALLER_MOBILE_BROADBAND, + hint1, + hint2); + + if (hints) + g_ptr_array_free (hints, TRUE); + } + + return ret; +} + +NMActStageReturn +nm_modem_act_stage2_config (NMModem *self, + NMActRequest *req, + NMDeviceStateReason *reason) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + + /* Clear secrets tries counter since secrets were successfully used + * already if we get here. + */ + priv->secrets_tries = 0; + + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +NMConnection * +nm_modem_get_best_auto_connection (NMModem *self, + GSList *connections, + char **specific_object) +{ + if (NM_MODEM_GET_CLASS (self)->get_best_auto_connection) + return NM_MODEM_GET_CLASS (self)->get_best_auto_connection (self, connections, specific_object); + return NULL; +} + +gboolean +nm_modem_check_connection_compatible (NMModem *self, + NMConnection *connection, + GError **error) +{ + if (NM_MODEM_GET_CLASS (self)->check_connection_compatible) + return NM_MODEM_GET_CLASS (self)->check_connection_compatible (self, connection, error); + return FALSE; +} + +static void +real_deactivate_quickly (NMModem *self, NMDevice *device) +{ + NMModemPrivate *priv; + const char *iface; + + g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_MODEM (self)); + g_return_if_fail (device != NULL); + g_return_if_fail (NM_IS_DEVICE (device)); + + priv = NM_MODEM_GET_PRIVATE (self); + + priv->secrets_tries = 0; + + if (priv->call) { + dbus_g_proxy_cancel_call (priv->proxy, priv->call); + priv->call = NULL; + } + + if (priv->pending_ip4_config) { + g_object_unref (priv->pending_ip4_config); + priv->pending_ip4_config = NULL; + } + + priv->in_bytes = priv->out_bytes = 0; + + switch (priv->ip_method) { + case MM_MODEM_IP_METHOD_PPP: + if (priv->ppp_manager) { + g_object_unref (priv->ppp_manager); + priv->ppp_manager = NULL; + } + break; + case MM_MODEM_IP_METHOD_STATIC: + case MM_MODEM_IP_METHOD_DHCP: + iface = nm_device_get_ip_iface (device); + /* FIXME: use AF_UNSPEC here when we have IPv6 support */ + nm_system_device_flush_routes_with_iface (iface, AF_INET); + nm_system_device_flush_addresses_with_iface (iface); + nm_system_device_set_up_down_with_iface (iface, FALSE, NULL); + break; + default: + nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method); + break; + } +} + +void +nm_modem_deactivate_quickly (NMModem *self, NMDevice *device) +{ + NM_MODEM_GET_CLASS (self)->deactivate_quickly (self, device); +} + +static void +disconnect_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + GError *error = NULL; + + if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) { + nm_log_info (LOGD_MB, "disconnect failed: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + } +} + +void +nm_modem_device_state_changed (NMModem *self, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason) +{ + gboolean was_connected = FALSE; + + g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_MODEM (self)); + + if (IS_ACTIVATING_STATE (old_state) || (old_state == NM_DEVICE_STATE_ACTIVATED)) + was_connected = TRUE; + + /* Make sure we don't leave the serial device open */ + switch (new_state) { + case NM_DEVICE_STATE_NEED_AUTH: + if (NM_MODEM_GET_PRIVATE (self)->ppp_manager) + break; + /* else fall through */ + case NM_DEVICE_STATE_UNMANAGED: + case NM_DEVICE_STATE_UNAVAILABLE: + case NM_DEVICE_STATE_FAILED: + case NM_DEVICE_STATE_DISCONNECTED: + if (was_connected) { + dbus_g_proxy_begin_call (nm_modem_get_proxy (self, MM_DBUS_INTERFACE_MODEM), + "Disconnect", + disconnect_done, + self, + NULL, + G_TYPE_INVALID); + } + break; + default: + break; + } +} + +gboolean +nm_modem_hw_is_up (NMModem *self, NMDevice *device) +{ + guint32 ip_method = NM_MODEM_GET_PRIVATE (self)->ip_method; + + if (ip_method == MM_MODEM_IP_METHOD_STATIC || ip_method == MM_MODEM_IP_METHOD_DHCP) { + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + NMDeviceState state; + + state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); + if (priv->pending_ip4_config || state == NM_DEVICE_STATE_IP_CONFIG || state == NM_DEVICE_STATE_ACTIVATED) + return nm_system_device_is_up (device); + } + + return TRUE; +} + +gboolean +nm_modem_hw_bring_up (NMModem *self, NMDevice *device, gboolean *no_firmware) +{ + guint32 ip_method = NM_MODEM_GET_PRIVATE (self)->ip_method; + + if (ip_method == MM_MODEM_IP_METHOD_STATIC || ip_method == MM_MODEM_IP_METHOD_DHCP) { + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + NMDeviceState state; + + state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); + if (priv->pending_ip4_config || state == NM_DEVICE_STATE_IP_CONFIG || state == NM_DEVICE_STATE_ACTIVATED) + return nm_system_device_set_up_down (device, TRUE, no_firmware); + } + + return TRUE; +} + +const char * +nm_modem_get_iface (NMModem *self) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_MODEM (self), NULL); + + return NM_MODEM_GET_PRIVATE (self)->iface; +} + +const char * +nm_modem_get_path (NMModem *self) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_MODEM (self), NULL); + + return NM_MODEM_GET_PRIVATE (self)->path; +} + +static void +get_mm_enabled_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMModem *self = NM_MODEM (user_data); + GError *error = NULL; + GValue value = { 0, }; + + if (!dbus_g_proxy_end_call (proxy, call_id, &error, + G_TYPE_VALUE, &value, + G_TYPE_INVALID)) { + nm_log_warn (LOGD_MB, "failed get modem enabled state: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + return; + } + + if (G_VALUE_HOLDS_BOOLEAN (&value)) { + NM_MODEM_GET_PRIVATE (self)->mm_enabled = g_value_get_boolean (&value); + g_object_notify (G_OBJECT (self), NM_MODEM_ENABLED); + } else + nm_log_warn (LOGD_MB, "failed get modem enabled state: unexpected reply type"); + + g_value_unset (&value); +} + +static void +query_mm_enabled (NMModem *self) +{ + dbus_g_proxy_begin_call (NM_MODEM_GET_PRIVATE (self)->props_proxy, + "Get", get_mm_enabled_done, + self, NULL, + G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM, + G_TYPE_STRING, "Enabled", + G_TYPE_INVALID); +} + +static void +set_mm_enabled_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + GError *error = NULL; + + if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) { + nm_log_warn (LOGD_MB, "failed to enable/disable modem: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + } + + /* Update enabled/disabled state again */ + query_mm_enabled (NM_MODEM (user_data)); +} + +void +nm_modem_set_mm_enabled (NMModem *self, gboolean enabled) +{ + NMModemPrivate *priv; + + g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_MODEM (self)); + + priv = NM_MODEM_GET_PRIVATE (self); + + /* FIXME: For now this just toggles the ModemManager enabled state. In the + * future we want to tie this into rfkill state instead so that the user can + * toggle rfkill status of the WWAN modem. + */ + + if (priv->mm_enabled != enabled) { + DBusGProxy *proxy; + + proxy = nm_modem_get_proxy (self, MM_DBUS_INTERFACE_MODEM); + dbus_g_proxy_begin_call (proxy, + "Enable", set_mm_enabled_done, + self, NULL, + G_TYPE_BOOLEAN, enabled, + G_TYPE_INVALID); + } +} + +static void +modem_properties_changed (DBusGProxy *proxy, + const char *interface, + GHashTable *props, + gpointer user_data) +{ + NMModem *self = NM_MODEM (user_data); + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + GValue *value; + + if (strcmp (interface, MM_DBUS_INTERFACE_MODEM)) + return; + + value = g_hash_table_lookup (props, "Enabled"); + if (value && G_VALUE_HOLDS_BOOLEAN (value)) { + priv->mm_enabled = g_value_get_boolean (value); + g_object_notify (G_OBJECT (self), NM_MODEM_ENABLED); + } +} + +/*****************************************************************************/ + +static void +nm_modem_init (NMModem *self) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + + priv->dbus_mgr = nm_dbus_manager_get (); +} + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + NMModemPrivate *priv; + DBusGConnection *bus; + + object = G_OBJECT_CLASS (nm_modem_parent_class)->constructor (type, + n_construct_params, + construct_params); + if (!object) + return NULL; + + priv = NM_MODEM_GET_PRIVATE (object); + + if (!priv->device) { + nm_log_err (LOGD_HW, "modem parent device not provided"); + goto err; + } + + if (!priv->device) { + nm_log_err (LOGD_HW, "modem command interface not provided"); + goto err; + } + + if (!priv->path) { + nm_log_err (LOGD_HW, "D-Bus path not provided"); + goto err; + } + + bus = nm_dbus_manager_get_connection (priv->dbus_mgr); + priv->proxy = dbus_g_proxy_new_for_name (bus, + MM_DBUS_SERVICE, + priv->path, + MM_DBUS_INTERFACE_MODEM); + + priv->props_proxy = dbus_g_proxy_new_for_name (bus, + MM_DBUS_SERVICE, + priv->path, + DBUS_INTERFACE_PROPERTIES); + dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_BOXED, + G_TYPE_NONE, + G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->props_proxy, "MmPropertiesChanged", + G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->props_proxy, "MmPropertiesChanged", + G_CALLBACK (modem_properties_changed), + object, + NULL); + + query_mm_enabled (NM_MODEM (object)); + + return object; + + err: + g_object_unref (object); + return NULL; +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PATH: + g_value_set_string (value, priv->path); + break; + case PROP_DEVICE: + g_value_set_string (value, priv->device); + break; + case PROP_IFACE: + g_value_set_string (value, priv->iface); + break; + case PROP_IP_METHOD: + g_value_set_uint (value, priv->ip_method); + break; + case PROP_ENABLED: + g_value_set_boolean (value, priv->mm_enabled); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PATH: + /* Construct only */ + priv->path = g_value_dup_string (value); + break; + case PROP_DEVICE: + /* Construct only */ + priv->device = g_value_dup_string (value); + break; + case PROP_IFACE: + /* Construct only */ + priv->iface = g_value_dup_string (value); + break; + case PROP_IP_METHOD: + /* Construct only */ + priv->ip_method = g_value_get_uint (value); + break; + case PROP_ENABLED: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +finalize (GObject *object) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object); + + if (priv->proxy) + g_object_unref (priv->proxy); + + if (priv->props_proxy) + g_object_unref (priv->props_proxy); + + g_object_unref (priv->dbus_mgr); + + g_free (priv->iface); + g_free (priv->path); + g_free (priv->device); + + G_OBJECT_CLASS (nm_modem_parent_class)->finalize (object); +} + +static void +nm_modem_class_init (NMModemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMModemPrivate)); + + /* Virtual methods */ + object_class->constructor = constructor; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + + klass->act_stage1_prepare = real_act_stage1_prepare; + klass->deactivate_quickly = real_deactivate_quickly; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_PATH, + g_param_spec_string (NM_MODEM_PATH, + "DBus path", + "DBus path", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_DEVICE, + g_param_spec_string (NM_MODEM_DEVICE, + "Device", + "Master modem parent device", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_IFACE, + g_param_spec_string (NM_MODEM_IFACE, + "Interface", + "Modem command interface", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_IP_METHOD, + g_param_spec_uint (NM_MODEM_IP_METHOD, + "IP method", + "IP method", + MM_MODEM_IP_METHOD_PPP, + MM_MODEM_IP_METHOD_DHCP, + MM_MODEM_IP_METHOD_PPP, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_ENABLED, + g_param_spec_boolean (NM_MODEM_ENABLED, + "Enabled", + "Enabled", + TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); + + /* Signals */ + signals[PPP_STATS] = + g_signal_new ("ppp-stats", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemClass, ppp_stats), + NULL, NULL, + _nm_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, + G_TYPE_UINT, G_TYPE_UINT); + + signals[PPP_FAILED] = + g_signal_new ("ppp-failed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemClass, ppp_failed), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + + signals[IP4_CONFIG_RESULT] = + g_signal_new (NM_MODEM_IP4_CONFIG_RESULT, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemClass, ip4_config_result), + NULL, NULL, + _nm_marshal_VOID__STRING_OBJECT_POINTER, + G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_OBJECT, G_TYPE_POINTER); + + signals[PREPARE_RESULT] = + g_signal_new (NM_MODEM_PREPARE_RESULT, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemClass, prepare_result), + NULL, NULL, + _nm_marshal_VOID__BOOLEAN_UINT, + G_TYPE_NONE, 2, G_TYPE_BOOLEAN, G_TYPE_UINT); + + signals[NEED_AUTH] = + g_signal_new (NM_MODEM_NEED_AUTH, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemClass, need_auth), + NULL, NULL, + _nm_marshal_VOID__STRING_BOOLEAN_UINT_STRING_STRING, + G_TYPE_NONE, 5, + G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING); +} + +const DBusGObjectInfo * +nm_modem_get_serial_dbus_info (void) +{ + return &dbus_glib_nm_serial_device_object_info; +} + diff --git a/src/modem-manager/nm-modem.h b/src/modem-manager/nm-modem.h new file mode 100644 index 00000000..a2aed57e --- /dev/null +++ b/src/modem-manager/nm-modem.h @@ -0,0 +1,157 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2009 Novell, Inc. + */ + +#ifndef NM_MODEM_H +#define NM_MODEM_H + +#include <dbus/dbus-glib.h> +#include <glib-object.h> +#include "ppp-manager/nm-ppp-manager.h" +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_MODEM (nm_modem_get_type ()) +#define NM_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM, NMModem)) +#define NM_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MODEM, NMModemClass)) +#define NM_IS_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM)) +#define NM_IS_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_MODEM)) +#define NM_MODEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MODEM, NMModemClass)) + +#define NM_MODEM_PATH "path" +#define NM_MODEM_DEVICE "device" +#define NM_MODEM_IFACE "iface" +#define NM_MODEM_IP_METHOD "ip-method" +#define NM_MODEM_ENABLED "enabled" + +#define NM_MODEM_PPP_STATS "ppp-stats" +#define NM_MODEM_PPP_FAILED "ppp-failed" +#define NM_MODEM_PREPARE_RESULT "prepare-result" +#define NM_MODEM_IP4_CONFIG_RESULT "ip4-config-result" +#define NM_MODEM_NEED_AUTH "need-auth" + +typedef struct { + GObject parent; +} NMModem; + +typedef struct { + GObjectClass parent; + + gboolean (*get_user_pass) (NMModem *modem, + NMConnection *connection, + const char **user, + const char **pass); + + const char * (*get_setting_name) (NMModem *modem); + + gboolean (*check_connection_compatible) (NMModem *modem, + NMConnection *connection, + GError **error); + + NMConnection * (*get_best_auto_connection) (NMModem *modem, + GSList *connections, + char **specific_object); + + NMActStageReturn (*act_stage1_prepare) (NMModem *modem, + NMActRequest *req, + GPtrArray **out_hints, + const char **out_setting_name, + NMDeviceStateReason *reason); + + void (*deactivate_quickly) (NMModem *self, NMDevice *device); + + /* Signals */ + void (*ppp_stats) (NMModem *self, guint32 in_bytes, guint32 out_bytes); + void (*ppp_failed) (NMModem *self, NMDeviceStateReason reason); + + void (*prepare_result) (NMModem *self, gboolean success, NMDeviceStateReason reason); + void (*ip4_config_result) (NMModem *self, const char *iface, NMIP4Config *config, GError *error); + + void (*need_auth) (NMModem *self, + const char *setting_name, + gboolean retry, + RequestSecretsCaller caller, + const char *hint1, + const char *hint2); +} NMModemClass; + +GType nm_modem_get_type (void); + +/* Protected */ + +NMPPPManager *nm_modem_get_ppp_manager (NMModem *modem); +DBusGProxy * nm_modem_get_proxy (NMModem *modem, const char *interface); +const char * nm_modem_get_iface (NMModem *modem); +const char * nm_modem_get_path (NMModem *modem); + +NMConnection *nm_modem_get_best_auto_connection (NMModem *self, + GSList *connections, + char **specific_object); + +gboolean nm_modem_check_connection_compatible (NMModem *self, + NMConnection *connection, + GError **error); + +NMActStageReturn nm_modem_act_stage1_prepare (NMModem *modem, + NMActRequest *req, + NMDeviceStateReason *reason); + +NMActStageReturn nm_modem_act_stage2_config (NMModem *modem, + NMActRequest *req, + NMDeviceStateReason *reason); + +NMActStageReturn nm_modem_stage3_ip4_config_start (NMModem *modem, + NMDevice *device, + NMDeviceClass *device_class, + NMDeviceStateReason *reason); + +NMActStageReturn nm_modem_stage4_get_ip4_config (NMModem *modem, + NMDevice *device, + NMDeviceClass *device_class, + NMIP4Config **config, + NMDeviceStateReason *reason); + +void nm_modem_deactivate_quickly (NMModem *modem, NMDevice *device); + +void nm_modem_device_state_changed (NMModem *modem, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason); + +gboolean nm_modem_hw_is_up (NMModem *modem, NMDevice *device); + +gboolean nm_modem_hw_bring_up (NMModem *modem, NMDevice *device, gboolean *no_firmware); + +gboolean nm_modem_connection_secrets_updated (NMModem *modem, + NMActRequest *req, + NMConnection *connection, + GSList *updated_settings, + RequestSecretsCaller caller); + +const DBusGObjectInfo *nm_modem_get_serial_dbus_info (void); + +gboolean nm_modem_get_mm_enabled (NMModem *self); + +void nm_modem_set_mm_enabled (NMModem *self, gboolean enabled); + +G_END_DECLS + +#endif /* NM_MODEM_H */ diff --git a/src/named-manager/Makefile.am b/src/named-manager/Makefile.am new file mode 100644 index 00000000..a33f7d50 --- /dev/null +++ b/src/named-manager/Makefile.am @@ -0,0 +1,21 @@ +INCLUDES = \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/src \ + -I${top_srcdir}/include + +noinst_LTLIBRARIES = libnamed-manager.la + +libnamed_manager_la_SOURCES = nm-named-manager.h nm-named-manager.c + +libnamed_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + -DNM_PKGDATADIR=\"$(pkgdatadir)\" \ + -DNM_LOCALSTATEDIR=\"$(localstatedir)\" + +libnamed_manager_la_LIBADD = \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + diff --git a/src/named-manager/Makefile.in b/src/named-manager/Makefile.in new file mode 100644 index 00000000..50fb770a --- /dev/null +++ b/src/named-manager/Makefile.in @@ -0,0 +1,564 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/named-manager +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libnamed_manager_la_DEPENDENCIES = \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libnamed_manager_la_OBJECTS = \ + libnamed_manager_la-nm-named-manager.lo +libnamed_manager_la_OBJECTS = $(am_libnamed_manager_la_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libnamed_manager_la_SOURCES) +DIST_SOURCES = $(libnamed_manager_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +IPTABLES_PATH = @IPTABLES_PATH@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBM = @LIBM@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POFILES = @POFILES@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +RANLIB = @RANLIB@ +RESOLVCONF_PATH = @RESOLVCONF_PATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/src \ + -I${top_srcdir}/include + +noinst_LTLIBRARIES = libnamed-manager.la +libnamed_manager_la_SOURCES = nm-named-manager.h nm-named-manager.c +libnamed_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + -DNM_PKGDATADIR=\"$(pkgdatadir)\" \ + -DNM_LOCALSTATEDIR=\"$(localstatedir)\" + +libnamed_manager_la_LIBADD = \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/named-manager/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/named-manager/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libnamed-manager.la: $(libnamed_manager_la_OBJECTS) $(libnamed_manager_la_DEPENDENCIES) + $(LINK) $(libnamed_manager_la_OBJECTS) $(libnamed_manager_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnamed_manager_la-nm-named-manager.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libnamed_manager_la-nm-named-manager.lo: nm-named-manager.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnamed_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnamed_manager_la-nm-named-manager.lo -MD -MP -MF $(DEPDIR)/libnamed_manager_la-nm-named-manager.Tpo -c -o libnamed_manager_la-nm-named-manager.lo `test -f 'nm-named-manager.c' || echo '$(srcdir)/'`nm-named-manager.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnamed_manager_la-nm-named-manager.Tpo $(DEPDIR)/libnamed_manager_la-nm-named-manager.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-named-manager.c' object='libnamed_manager_la-nm-named-manager.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnamed_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnamed_manager_la-nm-named-manager.lo `test -f 'nm-named-manager.c' || echo '$(srcdir)/'`nm-named-manager.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/named-manager/nm-named-manager.c b/src/named-manager/nm-named-manager.c new file mode 100644 index 00000000..ba89ce42 --- /dev/null +++ b/src/named-manager/nm-named-manager.c @@ -0,0 +1,821 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2004 - 2005 Colin Walters <walters@redhat.com> + * Copyright (C) 2004 - 2010 Red Hat, Inc. + * Copyright (C) 2005 - 2008 Novell, Inc. + * and others + */ + +#include "config.h" + +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <arpa/inet.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <glib.h> + +#include <glib/gi18n.h> + +#include "nm-named-manager.h" +#include "nm-ip4-config.h" +#include "nm-ip6-config.h" +#include "nm-logging.h" +#include "nm-system.h" +#include "NetworkManagerUtils.h" + +#ifdef HAVE_SELINUX +#include <selinux/selinux.h> +#endif + +#ifndef RESOLV_CONF +#define RESOLV_CONF "/etc/resolv.conf" +#endif + +#define ADDR_BUF_LEN 50 + +G_DEFINE_TYPE(NMNamedManager, nm_named_manager, G_TYPE_OBJECT) + +#define NM_NAMED_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + NM_TYPE_NAMED_MANAGER, \ + NMNamedManagerPrivate)) + + +struct NMNamedManagerPrivate { + NMIP4Config *ip4_vpn_config; + NMIP4Config *ip4_device_config; + NMIP6Config *ip6_vpn_config; + NMIP6Config *ip6_device_config; + GSList *configs; +}; + + +NMNamedManager * +nm_named_manager_get (void) +{ + static NMNamedManager * singleton = NULL; + + if (!singleton) + singleton = NM_NAMED_MANAGER (g_object_new (NM_TYPE_NAMED_MANAGER, NULL)); + else + g_object_ref (singleton); + + g_assert (singleton); + return singleton; +} + + +GQuark +nm_named_manager_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm_named_manager_error"); + + return quark; +} + +typedef struct { + GPtrArray *nameservers; + const char *domain; + GPtrArray *searches; + const char *nis_domain; + GPtrArray *nis_servers; +} NMResolvConfData; + +static void +add_string_item (GPtrArray *array, const char *str) +{ + int i; + + g_return_if_fail (array != NULL); + g_return_if_fail (str != NULL); + + /* Check for dupes before adding */ + for (i = 0; i < array->len; i++) { + const char *candidate = g_ptr_array_index (array, i); + + if (candidate && !strcmp (candidate, str)) + return; + } + + /* No dupes, add the new item */ + g_ptr_array_add (array, g_strdup (str)); +} + +static void +merge_one_ip4_config (NMResolvConfData *rc, NMIP4Config *src) +{ + guint32 num, i; + + num = nm_ip4_config_get_num_nameservers (src); + for (i = 0; i < num; i++) { + struct in_addr addr; + char buf[INET_ADDRSTRLEN]; + + addr.s_addr = nm_ip4_config_get_nameserver (src, i); + if (inet_ntop (AF_INET, &addr, buf, INET_ADDRSTRLEN) > 0) + add_string_item (rc->nameservers, buf); + } + + num = nm_ip4_config_get_num_domains (src); + for (i = 0; i < num; i++) { + const char *domain; + + domain = nm_ip4_config_get_domain (src, i); + if (!rc->domain) + rc->domain = domain; + add_string_item (rc->searches, domain); + } + + num = nm_ip4_config_get_num_searches (src); + for (i = 0; i < num; i++) + add_string_item (rc->searches, nm_ip4_config_get_search (src, i)); + + /* NIS stuff */ + num = nm_ip4_config_get_num_nis_servers (src); + for (i = 0; i < num; i++) { + struct in_addr addr; + char buf[INET_ADDRSTRLEN]; + + addr.s_addr = nm_ip4_config_get_nis_server (src, i); + if (inet_ntop (AF_INET, &addr, buf, INET_ADDRSTRLEN) > 0) + add_string_item (rc->nis_servers, buf); + } + + if (nm_ip4_config_get_nis_domain (src)) { + /* FIXME: handle multiple domains */ + if (!rc->nis_domain) + rc->nis_domain = nm_ip4_config_get_nis_domain (src); + } +} + +static void +merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src) +{ + guint32 num, i; + + num = nm_ip6_config_get_num_nameservers (src); + for (i = 0; i < num; i++) { + const struct in6_addr *addr; + char buf[INET6_ADDRSTRLEN]; + + addr = nm_ip6_config_get_nameserver (src, i); + + /* inet_ntop is probably supposed to do this for us, but it doesn't */ + if (IN6_IS_ADDR_V4MAPPED (addr)) { + if (inet_ntop (AF_INET, &(addr->s6_addr32[3]), buf, INET_ADDRSTRLEN) > 0) + add_string_item (rc->nameservers, buf); + } else { + if (inet_ntop (AF_INET6, addr, buf, INET6_ADDRSTRLEN) > 0) + add_string_item (rc->nameservers, buf); + } + } + + num = nm_ip6_config_get_num_domains (src); + for (i = 0; i < num; i++) { + const char *domain; + + domain = nm_ip6_config_get_domain (src, i); + if (!rc->domain) + rc->domain = domain; + add_string_item (rc->searches, domain); + } + + num = nm_ip6_config_get_num_searches (src); + for (i = 0; i < num; i++) + add_string_item (rc->searches, nm_ip6_config_get_search (src, i)); +} + + +#if defined(TARGET_SUSE) +/**********************************/ +/* SUSE */ + +static void +netconfig_child_setup (gpointer user_data G_GNUC_UNUSED) +{ + pid_t pid = getpid (); + setpgid (pid, pid); +} + +static GPid +run_netconfig (GError **error, gint *stdin_fd) +{ + char *argv[5]; + char *tmp; + GPid pid = -1; + + argv[0] = "/sbin/netconfig"; + argv[1] = "modify"; + argv[2] = "--service"; + argv[3] = "NetworkManager"; + argv[4] = NULL; + + tmp = g_strjoinv (" ", argv); + nm_log_dbg (LOGD_DNS, "spawning '%s'", tmp); + g_free (tmp); + + if (!g_spawn_async_with_pipes (NULL, argv, NULL, 0, netconfig_child_setup, + NULL, &pid, stdin_fd, NULL, NULL, error)) + return -1; + + return pid; +} + +static void +write_to_netconfig (gint fd, const char *key, const char *value) +{ + char *str; + int x; + + str = g_strdup_printf ("%s='%s'\n", key, value); + nm_log_dbg (LOGD_DNS, "writing to netconfig: %s", str); + x = write (fd, str, strlen (str)); + g_free (str); +} + +static gboolean +dispatch_netconfig (const char *domain, + char **searches, + char **nameservers, + const char *nis_domain, + char **nis_servers, + const char *iface, + GError **error) +{ + char *str; + GPid pid; + gint fd; + int ret; + + pid = run_netconfig (error, &fd); + if (pid < 0) + return FALSE; + + // FIXME: this is wrong. We are not writing out the iface-specific + // resolv.conf data, we are writing out an already-fully-merged + // resolv.conf. Assuming netconfig works in the obvious way, then + // there are various failure modes, such as, eg, bringing up a VPN on + // eth0, then bringing up wlan0, then bringing down the VPN. Because + // NMNamedManager would have claimed that the VPN DNS server was also + // part of the wlan0 config, it will remain in resolv.conf after the + // VPN goes down, even though it is presumably no longer reachable + // at that point. + write_to_netconfig (fd, "INTERFACE", iface); + + if (searches) { + str = g_strjoinv (" ", searches); + + if (domain) { + char *tmp; + + tmp = g_strconcat (domain, " ", str, NULL); + g_free (str); + str = tmp; + } + + write_to_netconfig (fd, "DNSSEARCH", str); + g_free (str); + } + + if (nameservers) { + str = g_strjoinv (" ", nameservers); + write_to_netconfig (fd, "DNSSERVERS", str); + g_free (str); + } + + if (nis_domain) + write_to_netconfig (fd, "NISDOMAIN", nis_domain); + + if (nis_servers) { + str = g_strjoinv (" ", nis_servers); + write_to_netconfig (fd, "NISSERVERS", str); + g_free (str); + } + + close (fd); + + /* Wait until the process exits */ + + again: + + ret = waitpid (pid, NULL, 0); + if (ret < 0 && errno == EINTR) + goto again; + else if (ret < 0 && errno == ECHILD) { + /* When the netconfig exist, the errno is ECHILD, it should return TRUE */ + return TRUE; + } + + return ret > 0; +} +#endif + + +static gboolean +write_resolv_conf (FILE *f, const char *domain, + char **searches, + char **nameservers, + GError **error) +{ + char *domain_str = NULL; + char *searches_str = NULL; + char *nameservers_str = NULL; + int i; + gboolean retval = FALSE; + + if (fprintf (f, "%s","# Generated by NetworkManager\n") < 0) { + g_set_error (error, + NM_NAMED_MANAGER_ERROR, + NM_NAMED_MANAGER_ERROR_SYSTEM, + "Could not write " RESOLV_CONF ": %s\n", + g_strerror (errno)); + return FALSE; + } + + if (domain) + domain_str = g_strconcat ("domain ", domain, "\n", NULL); + + if (searches) { + char *tmp_str; + + tmp_str = g_strjoinv (" ", searches); + searches_str = g_strconcat ("search ", tmp_str, "\n", NULL); + g_free (tmp_str); + } + + if (nameservers) { + GString *str; + int num; + + str = g_string_new (""); + num = g_strv_length (nameservers); + + for (i = 0; i < num; i++) { + if (i == 3) { + g_string_append (str, "# "); + g_string_append (str, _("NOTE: the libc resolver may not support more than 3 nameservers.")); + g_string_append (str, "\n# "); + g_string_append (str, _("The nameservers listed below may not be recognized.")); + g_string_append_c (str, '\n'); + } + + g_string_append (str, "nameserver "); + g_string_append (str, nameservers[i]); + g_string_append_c (str, '\n'); + } + + nameservers_str = g_string_free (str, FALSE); + } + + if (fprintf (f, "%s%s%s", + domain_str ? domain_str : "", + searches_str ? searches_str : "", + nameservers_str ? nameservers_str : "") != -1) + retval = TRUE; + + g_free (domain_str); + g_free (searches_str); + g_free (nameservers_str); + + return retval; +} + +#ifdef RESOLVCONF_PATH +static gboolean +dispatch_resolvconf (const char *domain, + char **searches, + char **nameservers, + const char *iface, + GError **error) +{ + char *cmd; + FILE *f; + gboolean retval = FALSE; + + if (! g_file_test (RESOLVCONF_PATH, G_FILE_TEST_IS_EXECUTABLE)) + return FALSE; + + if (domain || searches || nameservers) { + cmd = g_strconcat (RESOLVCONF_PATH, " -a ", "NetworkManager", NULL); + nm_log_info (LOGD_DNS, "(%s): writing resolv.conf to %s", iface, RESOLVCONF_PATH); + if ((f = popen (cmd, "w")) == NULL) + g_set_error (error, + NM_NAMED_MANAGER_ERROR, + NM_NAMED_MANAGER_ERROR_SYSTEM, + "Could not write to %s: %s\n", + RESOLVCONF_PATH, + g_strerror (errno)); + else { + retval = write_resolv_conf (f, domain, searches, nameservers, error); + retval &= (pclose (f) == 0); + } + } else { + cmd = g_strconcat (RESOLVCONF_PATH, " -d ", "NetworkManager", NULL); + nm_log_info (LOGD_DNS, "(%s): removing resolv.conf from %s", iface, RESOLVCONF_PATH); + if (nm_spawn_process (cmd) == 0) + retval = TRUE; + } + + g_free (cmd); + + return retval; +} +#endif + +static gboolean +update_resolv_conf (const char *domain, + char **searches, + char **nameservers, + const char *iface, + GError **error) +{ + char *tmp_resolv_conf; + char *tmp_resolv_conf_realpath; + char *resolv_conf_realpath; + FILE *f; + int do_rename = 1; + int old_errno = 0; + + g_return_val_if_fail (error != NULL, FALSE); + + /* Find the real path of resolv.conf; it could be a symlink to something */ + resolv_conf_realpath = realpath (RESOLV_CONF, NULL); + if (!resolv_conf_realpath) + resolv_conf_realpath = strdup (RESOLV_CONF); + + /* Build up the real path for the temp resolv.conf that we're about to + * write out. + */ + tmp_resolv_conf = g_strdup_printf ("%s.tmp", resolv_conf_realpath); + tmp_resolv_conf_realpath = realpath (tmp_resolv_conf, NULL); + if (!tmp_resolv_conf_realpath) + tmp_resolv_conf_realpath = strdup (tmp_resolv_conf); + g_free (tmp_resolv_conf); + tmp_resolv_conf = NULL; + + if ((f = fopen (tmp_resolv_conf_realpath, "w")) == NULL) { + do_rename = 0; + old_errno = errno; + if ((f = fopen (RESOLV_CONF, "w")) == NULL) { + g_set_error (error, + NM_NAMED_MANAGER_ERROR, + NM_NAMED_MANAGER_ERROR_SYSTEM, + "Could not open %s: %s\nCould not open %s: %s\n", + tmp_resolv_conf_realpath, + g_strerror (old_errno), + RESOLV_CONF, + g_strerror (errno)); + goto out; + } + /* Update tmp_resolv_conf_realpath so the error message on fclose() + * failure will be correct. + */ + strcpy (tmp_resolv_conf_realpath, RESOLV_CONF); + } + + write_resolv_conf (f, domain, searches, nameservers, error); + + if (fclose (f) < 0) { + if (*error == NULL) { + /* only set an error here if write_resolv_conf() was successful, + * since its error is more important. + */ + g_set_error (error, + NM_NAMED_MANAGER_ERROR, + NM_NAMED_MANAGER_ERROR_SYSTEM, + "Could not close %s: %s\n", + tmp_resolv_conf_realpath, + g_strerror (errno)); + } + } + + /* Don't rename the tempfile over top of the existing resolv.conf if there + * was an error writing it out. + */ + if (*error == NULL && do_rename) { + if (rename (tmp_resolv_conf_realpath, resolv_conf_realpath) < 0) { + g_set_error (error, + NM_NAMED_MANAGER_ERROR, + NM_NAMED_MANAGER_ERROR_SYSTEM, + "Could not replace " RESOLV_CONF ": %s\n", + g_strerror (errno)); + } + } + +out: + free (tmp_resolv_conf_realpath); + free (resolv_conf_realpath); + return *error ? FALSE : TRUE; +} + +static gboolean +rewrite_resolv_conf (NMNamedManager *mgr, const char *iface, GError **error) +{ + NMNamedManagerPrivate *priv; + NMResolvConfData rc; + GSList *iter; + const char *domain = NULL; + const char *nis_domain = NULL; + char **searches = NULL; + char **nameservers = NULL; + char **nis_servers = NULL; + int num, i, len; + gboolean success = FALSE; + + g_return_val_if_fail (error != NULL, FALSE); + g_return_val_if_fail (*error == NULL, FALSE); + + priv = NM_NAMED_MANAGER_GET_PRIVATE (mgr); + + rc.nameservers = g_ptr_array_new (); + rc.domain = NULL; + rc.searches = g_ptr_array_new (); + rc.nis_servers = g_ptr_array_new (); + + if (priv->ip4_vpn_config) + merge_one_ip4_config (&rc, priv->ip4_vpn_config); + if (priv->ip4_device_config) + merge_one_ip4_config (&rc, priv->ip4_device_config); + + if (priv->ip6_vpn_config) + merge_one_ip6_config (&rc, priv->ip6_vpn_config); + if (priv->ip6_device_config) + merge_one_ip6_config (&rc, priv->ip6_device_config); + + for (iter = priv->configs; iter; iter = g_slist_next (iter)) { + if ( (iter->data == priv->ip4_vpn_config) + || (iter->data == priv->ip4_device_config) + || (iter->data == priv->ip6_vpn_config) + || (iter->data == priv->ip6_device_config)) + continue; + + if (NM_IS_IP4_CONFIG (iter->data)) { + NMIP4Config *config = NM_IP4_CONFIG (iter->data); + + merge_one_ip4_config (&rc, config); + } else if (NM_IS_IP6_CONFIG (iter->data)) { + NMIP6Config *config = NM_IP6_CONFIG (iter->data); + + merge_one_ip6_config (&rc, config); + } else + g_assert_not_reached (); + } + + domain = rc.domain; + + /* Per 'man resolv.conf', the search list is limited to 6 domains + * totalling 256 characters. + */ + num = MIN (rc.searches->len, 6); + for (i = 0, len = 0; i < num; i++) { + len += strlen (rc.searches->pdata[i]) + 1; /* +1 for spaces */ + if (len > 256) + break; + } + g_ptr_array_set_size (rc.searches, i); + if (rc.searches->len) { + g_ptr_array_add (rc.searches, NULL); + searches = (char **) g_ptr_array_free (rc.searches, FALSE); + } else + g_ptr_array_free (rc.searches, TRUE); + + if (rc.nameservers->len) { + g_ptr_array_add (rc.nameservers, NULL); + nameservers = (char **) g_ptr_array_free (rc.nameservers, FALSE); + } else + g_ptr_array_free (rc.nameservers, TRUE); + + if (rc.nis_servers->len) { + g_ptr_array_add (rc.nis_servers, NULL); + nis_servers = (char **) g_ptr_array_free (rc.nis_servers, FALSE); + } else + g_ptr_array_free (rc.nis_servers, TRUE); + + nis_domain = rc.nis_domain; + +#ifdef RESOLVCONF_PATH + success = dispatch_resolvconf (domain, searches, nameservers, iface, error); +#endif + +#ifdef TARGET_SUSE + if (success == FALSE) { + success = dispatch_netconfig (domain, searches, nameservers, + nis_domain, nis_servers, + iface, error); + } +#endif + + if (success == FALSE) + success = update_resolv_conf (domain, searches, nameservers, iface, error); + + if (success) + nm_system_update_dns (); + + if (searches) + g_strfreev (searches); + if (nameservers) + g_strfreev (nameservers); + if (nis_servers) + g_strfreev (nis_servers); + + return success; +} + +gboolean +nm_named_manager_add_ip4_config (NMNamedManager *mgr, + const char *iface, + NMIP4Config *config, + NMNamedIPConfigType cfg_type) +{ + NMNamedManagerPrivate *priv; + GError *error = NULL; + + g_return_val_if_fail (mgr != NULL, FALSE); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (config != NULL, FALSE); + + priv = NM_NAMED_MANAGER_GET_PRIVATE (mgr); + + switch (cfg_type) { + case NM_NAMED_IP_CONFIG_TYPE_VPN: + priv->ip4_vpn_config = config; + break; + case NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE: + priv->ip4_device_config = config; + break; + default: + break; + } + + /* Don't allow the same zone added twice */ + if (!g_slist_find (priv->configs, config)) + priv->configs = g_slist_append (priv->configs, g_object_ref (config)); + + if (!rewrite_resolv_conf (mgr, iface, &error)) { + nm_log_warn (LOGD_DNS, "could not commit DNS changes: '%s'", error ? error->message : "(none)"); + g_error_free (error); + } + + return TRUE; +} + +gboolean +nm_named_manager_remove_ip4_config (NMNamedManager *mgr, + const char *iface, + NMIP4Config *config) +{ + NMNamedManagerPrivate *priv; + GError *error = NULL; + + g_return_val_if_fail (mgr != NULL, FALSE); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (config != NULL, FALSE); + + priv = NM_NAMED_MANAGER_GET_PRIVATE (mgr); + + /* Can't remove it if it wasn't in the list to begin with */ + if (!g_slist_find (priv->configs, config)) + return FALSE; + + priv->configs = g_slist_remove (priv->configs, config); + + if (config == priv->ip4_vpn_config) + priv->ip4_vpn_config = NULL; + if (config == priv->ip4_device_config) + priv->ip4_device_config = NULL; + + g_object_unref (config); + + if (!rewrite_resolv_conf (mgr, iface, &error)) { + nm_log_warn (LOGD_DNS, "could not commit DNS changes: '%s'", error ? error->message : "(none)"); + if (error) + g_error_free (error); + } + + return TRUE; +} + +gboolean +nm_named_manager_add_ip6_config (NMNamedManager *mgr, + const char *iface, + NMIP6Config *config, + NMNamedIPConfigType cfg_type) +{ + NMNamedManagerPrivate *priv; + GError *error = NULL; + + g_return_val_if_fail (mgr != NULL, FALSE); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (config != NULL, FALSE); + + priv = NM_NAMED_MANAGER_GET_PRIVATE (mgr); + + switch (cfg_type) { + case NM_NAMED_IP_CONFIG_TYPE_VPN: + /* FIXME: not quite yet... */ + g_return_val_if_fail (cfg_type != NM_NAMED_IP_CONFIG_TYPE_VPN, FALSE); + priv->ip6_vpn_config = config; + break; + case NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE: + priv->ip6_device_config = config; + break; + default: + break; + } + + /* Don't allow the same zone added twice */ + if (!g_slist_find (priv->configs, config)) + priv->configs = g_slist_append (priv->configs, g_object_ref (config)); + + if (!rewrite_resolv_conf (mgr, iface, &error)) { + nm_log_warn (LOGD_DNS, "could not commit DNS changes: '%s'", error ? error->message : "(none)"); + g_error_free (error); + } + + return TRUE; +} + +gboolean +nm_named_manager_remove_ip6_config (NMNamedManager *mgr, + const char *iface, + NMIP6Config *config) +{ + NMNamedManagerPrivate *priv; + GError *error = NULL; + + g_return_val_if_fail (mgr != NULL, FALSE); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (config != NULL, FALSE); + + priv = NM_NAMED_MANAGER_GET_PRIVATE (mgr); + + /* Can't remove it if it wasn't in the list to begin with */ + if (!g_slist_find (priv->configs, config)) + return FALSE; + + priv->configs = g_slist_remove (priv->configs, config); + + if (config == priv->ip6_vpn_config) + priv->ip6_vpn_config = NULL; + if (config == priv->ip6_device_config) + priv->ip6_device_config = NULL; + + g_object_unref (config); + + if (!rewrite_resolv_conf (mgr, iface, &error)) { + nm_log_warn (LOGD_DNS, "could not commit DNS changes: '%s'", error ? error->message : "(none)"); + if (error) + g_error_free (error); + } + + return TRUE; +} + + +static void +nm_named_manager_init (NMNamedManager *mgr) +{ +} + +static void +nm_named_manager_finalize (GObject *object) +{ + NMNamedManagerPrivate *priv = NM_NAMED_MANAGER_GET_PRIVATE (object); + + g_slist_foreach (priv->configs, (GFunc) g_object_unref, NULL); + g_slist_free (priv->configs); + + G_OBJECT_CLASS (nm_named_manager_parent_class)->finalize (object); +} + +static void +nm_named_manager_class_init (NMNamedManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = nm_named_manager_finalize; + + g_type_class_add_private (object_class, sizeof (NMNamedManagerPrivate)); +} + diff --git a/src/named-manager/nm-named-manager.h b/src/named-manager/nm-named-manager.h new file mode 100644 index 00000000..47c8e711 --- /dev/null +++ b/src/named-manager/nm-named-manager.h @@ -0,0 +1,92 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2004 - 2005 Colin Walters <walters@redhat.com> + * Copyright (C) 2004 - 2008 Red Hat, Inc. + * Copyright (C) 2005 - 2008 Novell, Inc. + * and others + */ + +#ifndef __NM_NAMED_MANAGER_H__ +#define __NM_NAMED_MANAGER_H__ + +#include "config.h" +#include <glib-object.h> +#include <dbus/dbus.h> +#include "nm-ip4-config.h" +#include "nm-ip6-config.h" + +typedef enum { + NM_NAMED_MANAGER_ERROR_SYSTEM, + NM_NAMED_MANAGER_ERROR_INVALID_NAMESERVER, + NM_NAMED_MANAGER_ERROR_INVALID_HOST, + NM_NAMED_MANAGER_ERROR_INVALID_ID +} NMNamedManagerError; + +typedef enum { + NM_NAMED_IP_CONFIG_TYPE_DEFAULT = 0, + NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE, + NM_NAMED_IP_CONFIG_TYPE_VPN +} NMNamedIPConfigType; + +#define NM_NAMED_MANAGER_ERROR nm_named_manager_error_quark () +GQuark nm_named_manager_error_quark (void); + +G_BEGIN_DECLS + +#define NM_TYPE_NAMED_MANAGER (nm_named_manager_get_type ()) +#define NM_NAMED_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_NAMED_MANAGER, NMNamedManager)) +#define NM_NAMED_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_NAMED_MANAGER, NMNamedManagerClass)) +#define NM_IS_NAMED_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NM_TYPE_NAMED_MANAGER)) +#define NM_IS_NAMED_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NM_TYPE_NAMED_MANAGER)) +#define NM_NAMED_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NM_TYPE_NAMED_MANAGER, NMNamedManagerClass)) + +typedef struct NMNamedManagerPrivate NMNamedManagerPrivate; + +typedef struct { + GObject parent; +} NMNamedManager; + +typedef struct { + GObjectClass parent; +} NMNamedManagerClass; + +GType nm_named_manager_get_type (void); + +NMNamedManager * nm_named_manager_get (void); + +gboolean nm_named_manager_add_ip4_config (NMNamedManager *mgr, + const char *iface, + NMIP4Config *config, + NMNamedIPConfigType cfg_type); + +gboolean nm_named_manager_remove_ip4_config (NMNamedManager *mgr, + const char *iface, + NMIP4Config *config); + +gboolean nm_named_manager_add_ip6_config (NMNamedManager *mgr, + const char *iface, + NMIP6Config *config, + NMNamedIPConfigType cfg_type); + +gboolean nm_named_manager_remove_ip6_config (NMNamedManager *mgr, + const char *iface, + NMIP6Config *config); + +G_END_DECLS + +#endif /* __NM_NAMED_MANAGER_H__ */ diff --git a/src/nm-activation-request.c b/src/nm-activation-request.c new file mode 100644 index 00000000..2529e77f --- /dev/null +++ b/src/nm-activation-request.c @@ -0,0 +1,700 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + * Copyright (C) 2007 - 2008 Novell, Inc. + */ + +#include <string.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <unistd.h> +#include <dbus/dbus-glib.h> + +#include "nm-activation-request.h" +#include "nm-marshal.h" +#include "nm-logging.h" +#include "nm-setting-wireless-security.h" +#include "nm-setting-8021x.h" +#include "nm-dbus-manager.h" +#include "nm-device.h" +#include "nm-properties-changed-signal.h" +#include "nm-active-connection.h" +#include "nm-dbus-glib-types.h" + + +static void secrets_provider_interface_init (NMSecretsProviderInterface *sp_interface_class); + +G_DEFINE_TYPE_EXTENDED (NMActRequest, nm_act_request, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (NM_TYPE_SECRETS_PROVIDER_INTERFACE, + secrets_provider_interface_init)) + +#define NM_ACT_REQUEST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_ACT_REQUEST, NMActRequestPrivate)) + +enum { + CONNECTION_SECRETS_UPDATED, + CONNECTION_SECRETS_FAILED, + PROPERTIES_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct { + char *table; + char *rule; +} ShareRule; + +typedef struct { + gboolean disposed; + + NMConnection *connection; + guint32 secrets_call_id; + + char *specific_object; + NMDevice *device; + gboolean user_requested; + + NMActiveConnectionState state; + gboolean is_default; + gboolean is_default6; + gboolean shared; + GSList *share_rules; + + char *ac_path; + + gboolean assumed; +} NMActRequestPrivate; + +enum { + PROP_0, + PROP_SERVICE_NAME, + PROP_CONNECTION, + PROP_SPECIFIC_OBJECT, + PROP_DEVICES, + PROP_STATE, + PROP_DEFAULT, + PROP_DEFAULT6, + PROP_VPN, + + LAST_PROP +}; + + +static void +device_state_changed (NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason, + gpointer user_data) +{ + NMActRequest *self = NM_ACT_REQUEST (user_data); + NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (self); + NMActiveConnectionState new_ac_state; + gboolean new_default = FALSE, new_default6 = FALSE; + + /* Set NMActiveConnection state based on the device's state */ + switch (new_state) { + case NM_DEVICE_STATE_PREPARE: + case NM_DEVICE_STATE_CONFIG: + case NM_DEVICE_STATE_NEED_AUTH: + case NM_DEVICE_STATE_IP_CONFIG: + new_ac_state = NM_ACTIVE_CONNECTION_STATE_ACTIVATING; + break; + case NM_DEVICE_STATE_ACTIVATED: + new_ac_state = NM_ACTIVE_CONNECTION_STATE_ACTIVATED; + new_default = priv->is_default; + new_default6 = priv->is_default6; + break; + default: + new_ac_state = NM_ACTIVE_CONNECTION_STATE_UNKNOWN; + break; + } + + if (new_ac_state != priv->state) { + priv->state = new_ac_state; + g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_STATE); + } + + if (new_default != priv->is_default) { + priv->is_default = new_default; + g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_DEFAULT); + } + + if (new_default6 != priv->is_default6) { + priv->is_default6 = new_default6; + g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_DEFAULT6); + } +} + +NMActRequest * +nm_act_request_new (NMConnection *connection, + const char *specific_object, + gboolean user_requested, + gboolean assumed, + gpointer *device) +{ + GObject *object; + NMActRequestPrivate *priv; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (NM_DEVICE (device), NULL); + + object = g_object_new (NM_TYPE_ACT_REQUEST, NULL); + if (!object) + return NULL; + + priv = NM_ACT_REQUEST_GET_PRIVATE (object); + + priv->connection = g_object_ref (connection); + if (specific_object) + priv->specific_object = g_strdup (specific_object); + + priv->device = NM_DEVICE (device); + g_signal_connect (device, "state-changed", + G_CALLBACK (device_state_changed), + NM_ACT_REQUEST (object)); + + priv->user_requested = user_requested; + priv->assumed = assumed; + + return NM_ACT_REQUEST (object); +} + +static void +nm_act_request_init (NMActRequest *req) +{ + NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (req); + NMDBusManager *dbus_mgr; + + priv->ac_path = nm_active_connection_get_next_object_path (); + priv->state = NM_ACTIVE_CONNECTION_STATE_UNKNOWN; + + dbus_mgr = nm_dbus_manager_get (); + dbus_g_connection_register_g_object (nm_dbus_manager_get_connection (dbus_mgr), + priv->ac_path, + G_OBJECT (req)); + g_object_unref (dbus_mgr); +} + +static void +dispose (GObject *object) +{ + NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (object); + + if (priv->disposed) { + G_OBJECT_CLASS (nm_act_request_parent_class)->dispose (object); + return; + } + priv->disposed = TRUE; + + g_assert (priv->connection); + + g_signal_handlers_disconnect_by_func (G_OBJECT (priv->device), + G_CALLBACK (device_state_changed), + NM_ACT_REQUEST (object)); + + /* Clear any share rules */ + nm_act_request_set_shared (NM_ACT_REQUEST (object), FALSE); + + g_object_unref (priv->connection); + + G_OBJECT_CLASS (nm_act_request_parent_class)->dispose (object); +} + +static void +clear_share_rules (NMActRequest *req) +{ + NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (req); + GSList *iter; + + for (iter = priv->share_rules; iter; iter = g_slist_next (iter)) { + ShareRule *rule = (ShareRule *) iter->data; + + g_free (rule->table); + g_free (rule->rule); + g_free (rule); + } + + g_slist_free (priv->share_rules); + priv->share_rules = NULL; +} + +static void +finalize (GObject *object) +{ + NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (object); + + g_free (priv->specific_object); + g_free (priv->ac_path); + + clear_share_rules (NM_ACT_REQUEST (object)); + + G_OBJECT_CLASS (nm_act_request_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (object); + GPtrArray *devices; + + switch (prop_id) { + case PROP_SERVICE_NAME: + nm_active_connection_scope_to_value (priv->connection, value); + break; + case PROP_CONNECTION: + g_value_set_boxed (value, nm_connection_get_path (priv->connection)); + break; + case PROP_SPECIFIC_OBJECT: + if (priv->specific_object) + g_value_set_boxed (value, priv->specific_object); + else + g_value_set_boxed (value, "/"); + break; + case PROP_DEVICES: + devices = g_ptr_array_sized_new (1); + g_ptr_array_add (devices, g_strdup (nm_device_get_path (priv->device))); + g_value_take_boxed (value, devices); + break; + case PROP_STATE: + g_value_set_uint (value, priv->state); + break; + case PROP_DEFAULT: + g_value_set_boolean (value, priv->is_default); + break; + case PROP_DEFAULT6: + g_value_set_boolean (value, priv->is_default6); + break; + case PROP_VPN: + g_value_set_boolean (value, FALSE); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_act_request_class_init (NMActRequestClass *req_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (req_class); + + g_type_class_add_private (req_class, sizeof (NMActRequestPrivate)); + + /* virtual methods */ + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + /* properties */ + g_object_class_install_property + (object_class, PROP_SERVICE_NAME, + g_param_spec_string (NM_ACTIVE_CONNECTION_SERVICE_NAME, + "Service name", + "Service name", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_CONNECTION, + g_param_spec_boxed (NM_ACTIVE_CONNECTION_CONNECTION, + "Connection", + "Connection", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_SPECIFIC_OBJECT, + g_param_spec_boxed (NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, + "Specific object", + "Specific object", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_DEVICES, + g_param_spec_boxed (NM_ACTIVE_CONNECTION_DEVICES, + "Devices", + "Devices", + DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_STATE, + g_param_spec_uint (NM_ACTIVE_CONNECTION_STATE, + "State", + "State", + NM_ACTIVE_CONNECTION_STATE_UNKNOWN, + NM_ACTIVE_CONNECTION_STATE_ACTIVATED, + NM_ACTIVE_CONNECTION_STATE_UNKNOWN, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_DEFAULT, + g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT, + "Default", + "Is the default IPv4 active connection", + FALSE, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_DEFAULT6, + g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT6, + "Default6", + "Is the default IPv6 active connection", + FALSE, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_VPN, + g_param_spec_boolean (NM_ACTIVE_CONNECTION_VPN, + "VPN", + "Is a VPN connection", + FALSE, + G_PARAM_READABLE)); + + /* Signals */ + signals[CONNECTION_SECRETS_UPDATED] = + g_signal_new ("connection-secrets-updated", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMActRequestClass, secrets_updated), + NULL, NULL, + _nm_marshal_VOID__OBJECT_POINTER_UINT, + G_TYPE_NONE, 3, + G_TYPE_OBJECT, G_TYPE_POINTER, G_TYPE_UINT); + + signals[CONNECTION_SECRETS_FAILED] = + g_signal_new ("connection-secrets-failed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMActRequestClass, secrets_failed), + NULL, NULL, + _nm_marshal_VOID__OBJECT_STRING_UINT, + G_TYPE_NONE, 3, + G_TYPE_OBJECT, G_TYPE_STRING, G_TYPE_UINT); + + signals[PROPERTIES_CHANGED] = + nm_properties_changed_signal_new (object_class, + G_STRUCT_OFFSET (NMActRequestClass, properties_changed)); + + nm_active_connection_install_type_info (object_class); +} + +static gboolean +secrets_update_setting (NMSecretsProviderInterface *interface, + const char *setting_name, + GHashTable *new) +{ + NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (interface); + NMSetting *setting = NULL; + GError *error = NULL; + GType type; + + g_return_val_if_fail (priv->connection != NULL, FALSE); + + /* Check whether a complete & valid NMSetting object was returned. If + * yes, replace the setting object in the connection. If not, just try + * updating the secrets. + */ + type = nm_connection_lookup_setting_type (setting_name); + if (type == 0) + return FALSE; + + setting = nm_setting_new_from_hash (type, new); + if (setting) { + NMSetting *s_8021x = NULL; + GSList *all_settings = NULL; + + /* The wireless-security setting might need the 802.1x setting in + * the all_settings argument of the verify function. Ugh. + */ + s_8021x = nm_connection_get_setting (priv->connection, NM_TYPE_SETTING_802_1X); + if (s_8021x) + all_settings = g_slist_append (all_settings, s_8021x); + + if (!nm_setting_verify (setting, all_settings, NULL)) { + /* Just try updating secrets */ + g_object_unref (setting); + setting = NULL; + } + + g_slist_free (all_settings); + } + + if (setting) + nm_connection_add_setting (priv->connection, setting); + else { + if (!nm_connection_update_secrets (priv->connection, setting_name, new, &error)) { + nm_log_warn (LOGD_DEVICE, "Failed to update connection secrets: %d %s", + error ? error->code : -1, + error && error->message ? error->message : "(none)"); + g_clear_error (&error); + } + } + + return TRUE; +} + +static void +secrets_result (NMSecretsProviderInterface *interface, + const char *setting_name, + RequestSecretsCaller caller, + const GSList *updated, + GError *error) +{ + NMActRequest *self = NM_ACT_REQUEST (interface); + NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (self); + + g_return_if_fail (priv->connection != NULL); + + if (error) { + g_signal_emit (self, signals[CONNECTION_SECRETS_FAILED], 0, + priv->connection, setting_name, caller); + } else { + g_signal_emit (self, signals[CONNECTION_SECRETS_UPDATED], 0, + priv->connection, updated, caller); + } +} + +static void +secrets_provider_interface_init (NMSecretsProviderInterface *sp_interface_class) +{ + /* interface implementation */ + sp_interface_class->update_setting = secrets_update_setting; + sp_interface_class->result = secrets_result; +} + +gboolean +nm_act_request_get_secrets (NMActRequest *self, + const char *setting_name, + gboolean request_new, + RequestSecretsCaller caller, + const char *hint1, + const char *hint2) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (NM_IS_ACT_REQUEST (self), FALSE); + + return nm_secrets_provider_interface_get_secrets (NM_SECRETS_PROVIDER_INTERFACE (self), + nm_act_request_get_connection (self), + setting_name, + request_new, + caller, + hint1, + hint2); +} + +NMConnection * +nm_act_request_get_connection (NMActRequest *req) +{ + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NULL); + + return NM_ACT_REQUEST_GET_PRIVATE (req)->connection; +} + +const char * +nm_act_request_get_specific_object (NMActRequest *req) +{ + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NULL); + + return NM_ACT_REQUEST_GET_PRIVATE (req)->specific_object; +} + +void +nm_act_request_set_specific_object (NMActRequest *req, + const char *specific_object) +{ + NMActRequestPrivate *priv; + + g_return_if_fail (NM_IS_ACT_REQUEST (req)); + g_return_if_fail (specific_object != NULL); + + priv = NM_ACT_REQUEST_GET_PRIVATE (req); + + if (priv->specific_object) + g_free (priv->specific_object); + priv->specific_object = g_strdup (specific_object); +} + +gboolean +nm_act_request_get_user_requested (NMActRequest *req) +{ + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE); + + return NM_ACT_REQUEST_GET_PRIVATE (req)->user_requested; +} + +const char * +nm_act_request_get_active_connection_path (NMActRequest *req) +{ + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NULL); + + return NM_ACT_REQUEST_GET_PRIVATE (req)->ac_path; +} + +void +nm_act_request_set_default (NMActRequest *req, gboolean is_default) +{ + NMActRequestPrivate *priv; + + g_return_if_fail (NM_IS_ACT_REQUEST (req)); + + priv = NM_ACT_REQUEST_GET_PRIVATE (req); + if (priv->is_default == is_default) + return; + + priv->is_default = is_default; + g_object_notify (G_OBJECT (req), NM_ACTIVE_CONNECTION_DEFAULT); +} + +gboolean +nm_act_request_get_default (NMActRequest *req) +{ + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE); + + return NM_ACT_REQUEST_GET_PRIVATE (req)->is_default; +} + +void +nm_act_request_set_default6 (NMActRequest *req, gboolean is_default6) +{ + NMActRequestPrivate *priv; + + g_return_if_fail (NM_IS_ACT_REQUEST (req)); + + priv = NM_ACT_REQUEST_GET_PRIVATE (req); + if (priv->is_default6 == is_default6) + return; + + priv->is_default6 = is_default6; + g_object_notify (G_OBJECT (req), NM_ACTIVE_CONNECTION_DEFAULT6); +} + +gboolean +nm_act_request_get_default6 (NMActRequest *req) +{ + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE); + + return NM_ACT_REQUEST_GET_PRIVATE (req)->is_default6; +} + +static void +share_child_setup (gpointer user_data G_GNUC_UNUSED) +{ + /* We are in the child process at this point */ + pid_t pid = getpid (); + setpgid (pid, pid); +} + +void +nm_act_request_set_shared (NMActRequest *req, gboolean shared) +{ + NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (req); + GSList *list, *iter; + + g_return_if_fail (NM_IS_ACT_REQUEST (req)); + + NM_ACT_REQUEST_GET_PRIVATE (req)->shared = shared; + + /* Tear the rules down in reverse order when sharing is stopped */ + list = g_slist_copy (priv->share_rules); + if (!shared) + list = g_slist_reverse (list); + + /* Send the rules to iptables */ + for (iter = list; iter; iter = g_slist_next (iter)) { + ShareRule *rule = (ShareRule *) iter->data; + char *envp[1] = { NULL }; + char **argv; + char *cmd; + + cmd = g_strdup_printf ("%s --table %s %s %s", + IPTABLES_PATH, + rule->table, + shared ? "--insert" : "--delete", + rule->rule); + if (!cmd) + continue; + + argv = g_strsplit (cmd, " ", 0); + if (argv && argv[0]) { + int status; + GError *error = NULL; + + nm_log_info (LOGD_SHARING, "Executing: %s", cmd); + if (!g_spawn_sync ("/", argv, envp, G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, + share_child_setup, NULL, NULL, NULL, &status, &error)) { + nm_log_warn (LOGD_SHARING, "Error executing command: (%d) %s", + error ? error->code : -1, + (error && error->message) ? error->message : "(unknown)"); + g_clear_error (&error); + } else if (WEXITSTATUS (status)) { + nm_log_warn (LOGD_SHARING, "** Command returned exit status %d.", + WEXITSTATUS (status)); + } + } + g_free (cmd); + if (argv) + g_strfreev (argv); + } + + g_slist_free (list); + + /* Clear the share rule list when sharing is stopped */ + if (!shared) + clear_share_rules (req); +} + +gboolean +nm_act_request_get_shared (NMActRequest *req) +{ + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE); + + return NM_ACT_REQUEST_GET_PRIVATE (req)->shared; +} + +void +nm_act_request_add_share_rule (NMActRequest *req, + const char *table, + const char *table_rule) +{ + NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (req); + ShareRule *rule; + + g_return_if_fail (NM_IS_ACT_REQUEST (req)); + g_return_if_fail (table != NULL); + g_return_if_fail (table_rule != NULL); + + rule = g_malloc0 (sizeof (ShareRule)); + rule->table = g_strdup (table); + rule->rule = g_strdup (table_rule); + priv->share_rules = g_slist_append (priv->share_rules, rule); +} + +GObject * +nm_act_request_get_device (NMActRequest *req) +{ + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE); + + return G_OBJECT (NM_ACT_REQUEST_GET_PRIVATE (req)->device); +} + +gboolean +nm_act_request_get_assumed (NMActRequest *req) +{ + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE); + + return NM_ACT_REQUEST_GET_PRIVATE (req)->assumed; +} + diff --git a/src/nm-activation-request.h b/src/nm-activation-request.h new file mode 100644 index 00000000..a2436945 --- /dev/null +++ b/src/nm-activation-request.h @@ -0,0 +1,103 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2005 - 2010 Red Hat, Inc. + */ + +#ifndef NM_ACTIVATION_REQUEST_H +#define NM_ACTIVATION_REQUEST_H + +#include <glib.h> +#include <glib-object.h> +#include "nm-connection.h" +#include "nm-active-connection.h" +#include "nm-secrets-provider-interface.h" + +#define NM_TYPE_ACT_REQUEST (nm_act_request_get_type ()) +#define NM_ACT_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ACT_REQUEST, NMActRequest)) +#define NM_ACT_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_ACT_REQUEST, NMActRequestClass)) +#define NM_IS_ACT_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_ACT_REQUEST)) +#define NM_IS_ACT_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_ACT_REQUEST)) +#define NM_ACT_REQUEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ACT_REQUEST, NMActRequestClass)) + +typedef struct { + GObject parent; +} NMActRequest; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*secrets_updated) (NMActRequest *req, + NMConnection *connection, + GSList *updated_settings, + RequestSecretsCaller caller); + void (*secrets_failed) (NMActRequest *req, + NMConnection *connection, + const char *setting, + RequestSecretsCaller caller); + + void (*properties_changed) (NMActRequest *req, GHashTable *properties); +} NMActRequestClass; + +GType nm_act_request_get_type (void); + +NMActRequest *nm_act_request_new (NMConnection *connection, + const char *specific_object, + gboolean user_requested, + gboolean assumed, + gpointer *device); /* An NMDevice */ + +NMConnection *nm_act_request_get_connection (NMActRequest *req); +const char * nm_act_request_get_specific_object (NMActRequest *req); + +void nm_act_request_set_specific_object (NMActRequest *req, + const char *specific_object); + +gboolean nm_act_request_get_user_requested (NMActRequest *req); + +const char * nm_act_request_get_active_connection_path (NMActRequest *req); + +void nm_act_request_set_default (NMActRequest *req, gboolean is_default); + +gboolean nm_act_request_get_default (NMActRequest *req); + +void nm_act_request_set_default6 (NMActRequest *req, gboolean is_default6); + +gboolean nm_act_request_get_default6 (NMActRequest *req); + +gboolean nm_act_request_get_shared (NMActRequest *req); + +void nm_act_request_set_shared (NMActRequest *req, gboolean shared); + +void nm_act_request_add_share_rule (NMActRequest *req, + const char *table, + const char *rule); + +GObject * nm_act_request_get_device (NMActRequest *req); + +gboolean nm_act_request_get_assumed (NMActRequest *req); + +gboolean nm_act_request_get_secrets (NMActRequest *req, + const char *setting_name, + gboolean request_new, + RequestSecretsCaller caller, + const char *hint1, + const char *hint2); + +#endif /* NM_ACTIVATION_REQUEST_H */ + diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c new file mode 100644 index 00000000..4207e145 --- /dev/null +++ b/src/nm-active-connection.c @@ -0,0 +1,63 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 - 2010 Red Hat, Inc. + */ + +#include <glib.h> +#include "nm-active-connection.h" +#include "NetworkManager.h" +#include "nm-active-connection-glue.h" +#include "nm-logging.h" + +char * +nm_active_connection_get_next_object_path (void) +{ + static guint32 counter = 0; + + return g_strdup_printf (NM_DBUS_PATH "/ActiveConnection/%d", counter++); +} + +void +nm_active_connection_install_type_info (GObjectClass *klass) +{ + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), + &dbus_glib_nm_active_connection_object_info); +} + +void +nm_active_connection_scope_to_value (NMConnection *connection, GValue *value) +{ + if (!connection) { + g_value_set_string (value, ""); + return; + } + + switch (nm_connection_get_scope (connection)) { + case NM_CONNECTION_SCOPE_SYSTEM: + g_value_set_string (value, NM_DBUS_SERVICE_SYSTEM_SETTINGS); + break; + case NM_CONNECTION_SCOPE_USER: + g_value_set_string (value, NM_DBUS_SERVICE_USER_SETTINGS); + break; + default: + nm_log_err (LOGD_CORE, "unknown connection scope!"); + break; + } +} + + diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h new file mode 100644 index 00000000..6a463cb2 --- /dev/null +++ b/src/nm-active-connection.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 - 2010 Red Hat, Inc. + */ + +#ifndef NM_ACTIVE_CONNECTION_H +#define NM_ACTIVE_CONNECTION_H + +#include <glib-object.h> +#include "nm-connection.h" + +#define NM_ACTIVE_CONNECTION_SERVICE_NAME "service-name" +#define NM_ACTIVE_CONNECTION_CONNECTION "connection" +#define NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT "specific-object" +#define NM_ACTIVE_CONNECTION_DEVICES "devices" +#define NM_ACTIVE_CONNECTION_STATE "state" +#define NM_ACTIVE_CONNECTION_DEFAULT "default" +#define NM_ACTIVE_CONNECTION_DEFAULT6 "default6" +#define NM_ACTIVE_CONNECTION_VPN "vpn" + +char *nm_active_connection_get_next_object_path (void); + +void nm_active_connection_install_type_info (GObjectClass *klass); + +void nm_active_connection_scope_to_value (NMConnection *connection, GValue *value); + +#endif /* NM_ACTIVE_CONNECTION_H */ diff --git a/src/nm-call-store.c b/src/nm-call-store.c new file mode 100644 index 00000000..df400d0f --- /dev/null +++ b/src/nm-call-store.c @@ -0,0 +1,209 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2007 Novell, Inc. + * Copyright (C) 2010 Red Hat, Inc. + */ + +#include "nm-call-store.h" +#include "nm-logging.h" + +NMCallStore * +nm_call_store_new (void) +{ + return g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) g_hash_table_destroy); +} + +static void +object_destroyed_cb (gpointer data, GObject *object) +{ + g_hash_table_remove ((NMCallStore *) data, object); +} + +void +nm_call_store_add (NMCallStore *store, + GObject *object, + gpointer *call_id) +{ + GHashTable *call_ids_hash; + + g_return_if_fail (store != NULL); + g_return_if_fail (object != NULL); + g_return_if_fail (call_id != NULL); + + call_ids_hash = g_hash_table_lookup (store, object); + if (!call_ids_hash) { + call_ids_hash = g_hash_table_new (NULL, NULL); + g_hash_table_insert (store, object, call_ids_hash); + g_object_weak_ref (object, object_destroyed_cb, store); + } + + g_hash_table_insert (call_ids_hash, call_id, NULL); +} + +void +nm_call_store_remove (NMCallStore *store, + GObject *object, + gpointer call_id) +{ + GHashTable *call_ids_hash; + + g_return_if_fail (store != NULL); + g_return_if_fail (object != NULL); + g_return_if_fail (call_id != NULL); + + call_ids_hash = g_hash_table_lookup (store, object); + if (!call_ids_hash) { + nm_log_warn (LOGD_CORE, "Trying to remove a non-existant call id."); + return; + } + + if (!g_hash_table_remove (call_ids_hash, call_id)) + nm_log_warn (LOGD_CORE, "Trying to remove a non-existant call id."); + + if (g_hash_table_size (call_ids_hash) == 0) { + g_hash_table_remove (store, object); + g_object_weak_unref (object, object_destroyed_cb, store); + } +} + +typedef struct { + GObject *object; + gint count; + NMCallStoreFunc callback; + gpointer user_data; +} StoreForeachInfo; + +static void +call_callback (gpointer call_id, gpointer user_data) +{ + StoreForeachInfo *info = (StoreForeachInfo *) user_data; + + if (info->count >= 0) { + if (info->callback (info->object, call_id, info->user_data)) + info->count++; + else + info->count = -1; + } +} + +static void +prepend_id (gpointer key, gpointer value, gpointer user_data) +{ + GSList **list = (GSList **) user_data; + + *list = g_slist_prepend (*list, key); +} + +static GSList * +get_call_ids (GHashTable *hash) +{ + GSList *ids = NULL; + + g_hash_table_foreach (hash, prepend_id, &ids); + + return ids; +} + +static void +call_all_callbacks (gpointer key, gpointer value, gpointer user_data) +{ + StoreForeachInfo *info = (StoreForeachInfo *) user_data; + GSList *ids; + + info->object = G_OBJECT (key); + + /* Create a copy of the hash keys (call_ids) so that the callback is + free to modify the store */ + ids = get_call_ids ((GHashTable *) value); + g_slist_foreach (ids, call_callback, info); + g_slist_free (ids); +} + +static void +duplicate_hash (gpointer key, gpointer value, gpointer user_data) +{ + g_hash_table_insert ((GHashTable *) user_data, key, value); +} + +int +nm_call_store_foreach (NMCallStore *store, + GObject *object, + NMCallStoreFunc callback, + gpointer user_data) +{ + StoreForeachInfo info; + + g_return_val_if_fail (store != NULL, -1); + g_return_val_if_fail (callback != NULL, -1); + + info.object = object; + info.count = 0; + info.callback = callback; + info.user_data = user_data; + + if (object) { + GHashTable *call_ids_hash; + GSList *ids; + + call_ids_hash = g_hash_table_lookup (store, object); + if (!call_ids_hash) { + nm_log_warn (LOGD_CORE, "Object not in store"); + return -1; + } + + /* Create a copy of the hash keys (call_ids) so that the callback is + free to modify the store */ + ids = get_call_ids (call_ids_hash); + g_slist_foreach (ids, call_callback, &info); + g_slist_free (ids); + } else { + GHashTable *copy; + + /* Create a copy of the main store so that callbacks can modify it */ + copy = g_hash_table_new (NULL, NULL); + g_hash_table_foreach (store, duplicate_hash, copy); + g_hash_table_foreach (copy, call_all_callbacks, &info); + g_hash_table_destroy (copy); + } + + return info.count; +} + +static void +remove_weakref (gpointer key, gpointer value, gpointer user_data) +{ + g_object_weak_unref (G_OBJECT (key), object_destroyed_cb, user_data); +} + +void +nm_call_store_clear (NMCallStore *store) +{ + g_return_if_fail (store); + + g_hash_table_foreach (store, remove_weakref, store); + g_hash_table_remove_all (store); +} + +void +nm_call_store_destroy (NMCallStore *store) +{ + g_return_if_fail (store); + + g_hash_table_destroy (store); +} diff --git a/src/nm-call-store.h b/src/nm-call-store.h new file mode 100644 index 00000000..0ae5a769 --- /dev/null +++ b/src/nm-call-store.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2007 Novell, Inc. + */ + +#ifndef NM_CALLBACK_STORE_H +#define NM_CALLBACK_STORE_H + +#include <glib-object.h> + +typedef GHashTable NMCallStore; + +typedef gboolean (*NMCallStoreFunc) (GObject *object, gpointer call_id, gpointer user_data); + +NMCallStore *nm_call_store_new (void); +void nm_call_store_add (NMCallStore *store, + GObject *object, + gpointer *call_id); + +void nm_call_store_remove (NMCallStore *store, + GObject *object, + gpointer call_id); + +int nm_call_store_foreach (NMCallStore *store, + GObject *object, + NMCallStoreFunc callback, + gpointer user_data); + +void nm_call_store_clear (NMCallStore *store); +void nm_call_store_destroy (NMCallStore *store); + +#endif /* NM_CALLBACK_STORE_H */ diff --git a/src/nm-crash-logger.c b/src/nm-crash-logger.c new file mode 100644 index 00000000..9ad0412d --- /dev/null +++ b/src/nm-crash-logger.c @@ -0,0 +1,86 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2008 Red Hat, Inc. + */ + + +#include <glib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <syslog.h> + +int main (int argc, char ** argv) +{ + GPid gdb_pid; + int out; + char nm_pid[16]; + char line[256]; + int gdb_stat; + int bytes_read; + gboolean done = FALSE; + char * args[] = { BINDIR "/gdb", + "--batch", + "--quiet", + "--command=" DATADIR "/NetworkManager/gdb-cmd", + SBINDIR "/NetworkManager", + NULL, NULL }; + + snprintf (nm_pid, sizeof (nm_pid), "%d", getppid ()); + args[5] = &nm_pid[0]; + if (!g_spawn_async_with_pipes (NULL, args, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, + &gdb_pid, NULL, &out, NULL, NULL)) + { + exit (1); + } + + openlog ("NetworkManager", LOG_CONS | LOG_PERROR, LOG_DAEMON); + syslog (LOG_CRIT, "******************* START **********************************"); + while (!done) + { + bytes_read = read (out, line, sizeof (line) - 1); + if (bytes_read > 0) + { + char *end = &line[0]; + char *start = &line[0]; + + /* Can't just funnel the output to syslog, have to do a separate + * syslog () for each line in the output. + */ + line[bytes_read] = '\0'; + while (*end != '\0') + { + if (*end == '\n') + { + *end = '\0'; + syslog (LOG_CRIT, "%s", start); + start = end + 1; + } + end++; + } + } + else if ((bytes_read <= 0) || ((errno != EINTR) && (errno != EAGAIN))) + done = TRUE; + } + syslog (LOG_CRIT, "******************* END **********************************"); + close (out); + waitpid (gdb_pid, &gdb_stat, 0); + exit (0); +} diff --git a/src/nm-dbus-manager.c b/src/nm-dbus-manager.c new file mode 100644 index 00000000..9b621b42 --- /dev/null +++ b/src/nm-dbus-manager.c @@ -0,0 +1,378 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2010 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include "config.h" +#include "NetworkManager.h" +#include "nm-dbus-manager.h" +#include "nm-marshal.h" +#include "nm-glib-compat.h" + +#include <dbus/dbus.h> +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> +#include <string.h> +#include "nm-logging.h" + +enum { + DBUS_CONNECTION_CHANGED = 0, + NAME_OWNER_CHANGED, + NUMBER_OF_SIGNALS +}; + +static guint signals[NUMBER_OF_SIGNALS]; + +G_DEFINE_TYPE(NMDBusManager, nm_dbus_manager, G_TYPE_OBJECT) + +#define NM_DBUS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + NM_TYPE_DBUS_MANAGER, \ + NMDBusManagerPrivate)) + +typedef struct { + DBusConnection *connection; + DBusGConnection *g_connection; + gboolean started; + + DBusGProxy *proxy; + guint proxy_destroy_id; + + guint reconnect_id; +} NMDBusManagerPrivate; + +static gboolean nm_dbus_manager_init_bus (NMDBusManager *self); +static void nm_dbus_manager_cleanup (NMDBusManager *self, gboolean dispose); +static void start_reconnection_timeout (NMDBusManager *self); + +NMDBusManager * +nm_dbus_manager_get (void) +{ + static NMDBusManager *singleton = NULL; + + if (!singleton) { + singleton = NM_DBUS_MANAGER (g_object_new (NM_TYPE_DBUS_MANAGER, NULL)); + if (!nm_dbus_manager_init_bus (singleton)) + start_reconnection_timeout (singleton); + } else { + g_object_ref (singleton); + } + + g_assert (singleton); + return singleton; +} + +static void +nm_dbus_manager_init (NMDBusManager *self) +{ +} + +static void +nm_dbus_manager_dispose (GObject *object) +{ + NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (object); + + nm_dbus_manager_cleanup (NM_DBUS_MANAGER (object), TRUE); + + if (priv->reconnect_id) { + g_source_remove (priv->reconnect_id); + priv->reconnect_id = 0; + } + + G_OBJECT_CLASS (nm_dbus_manager_parent_class)->dispose (object); +} + +static void +nm_dbus_manager_class_init (NMDBusManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = nm_dbus_manager_dispose; + + signals[DBUS_CONNECTION_CHANGED] = + g_signal_new ("dbus-connection-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMDBusManagerClass, dbus_connection_changed), + NULL, NULL, _nm_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + signals[NAME_OWNER_CHANGED] = + g_signal_new ("name-owner-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMDBusManagerClass, name_owner_changed), + NULL, NULL, _nm_marshal_VOID__STRING_STRING_STRING, + G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + + g_type_class_add_private (klass, sizeof (NMDBusManagerPrivate)); +} + + +/* Only cleanup a specific dbus connection, not all our private data */ +static void +nm_dbus_manager_cleanup (NMDBusManager *self, gboolean dispose) +{ + NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + + if (priv->proxy) { + if (dispose) { + g_signal_handler_disconnect (priv->proxy, priv->proxy_destroy_id); + priv->proxy_destroy_id = 0; + } + g_object_unref (priv->proxy); + priv->proxy = NULL; + } + + if (priv->g_connection) { + dbus_g_connection_unref (priv->g_connection); + priv->g_connection = NULL; + priv->connection = NULL; + } + + priv->started = FALSE; +} + +static gboolean +nm_dbus_manager_reconnect (gpointer user_data) +{ + NMDBusManager *self = NM_DBUS_MANAGER (user_data); + NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + + g_assert (self != NULL); + + if (nm_dbus_manager_init_bus (self)) { + if (nm_dbus_manager_start_service (self)) { + nm_log_info (LOGD_CORE, "reconnected to the system bus."); + g_signal_emit (self, signals[DBUS_CONNECTION_CHANGED], + 0, priv->connection); + priv->reconnect_id = 0; + return FALSE; + } + } + + /* Try again */ + nm_dbus_manager_cleanup (self, FALSE); + return TRUE; +} + +static void +start_reconnection_timeout (NMDBusManager *self) +{ + NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + + if (priv->reconnect_id) + g_source_remove (priv->reconnect_id); + + /* Schedule timeout for reconnection attempts */ + priv->reconnect_id = g_timeout_add_seconds (3, nm_dbus_manager_reconnect, self); +} + +char * +nm_dbus_manager_get_name_owner (NMDBusManager *self, + const char *name, + GError **error) +{ + char *owner = NULL; + + g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), NULL); + g_return_val_if_fail (name != NULL, NULL); + if (error) + g_return_val_if_fail (*error == NULL, NULL); + + if (!dbus_g_proxy_call_with_timeout (NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy, + "GetNameOwner", 2000, error, + G_TYPE_STRING, name, + G_TYPE_INVALID, + G_TYPE_STRING, &owner, + G_TYPE_INVALID)) { + return NULL; + } + + return owner; +} + +gboolean +nm_dbus_manager_name_has_owner (NMDBusManager *self, + const char *name) +{ + gboolean has_owner = FALSE; + GError *err = NULL; + + g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), FALSE); + g_return_val_if_fail (name != NULL, FALSE); + + if (!dbus_g_proxy_call (NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy, + "NameHasOwner", &err, + G_TYPE_STRING, name, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &has_owner, + G_TYPE_INVALID)) { + nm_log_warn (LOGD_CORE, "NameHasOwner request failed: %s", + (err && err->message) ? err->message : "(unknown)"); + g_clear_error (&err); + } + + return has_owner; +} + +static void +proxy_name_owner_changed (DBusGProxy *proxy, + const char *name, + const char *old_owner, + const char *new_owner, + gpointer user_data) +{ + g_signal_emit (G_OBJECT (user_data), signals[NAME_OWNER_CHANGED], + 0, name, old_owner, new_owner); +} + +static void +destroy_cb (DBusGProxy *proxy, gpointer user_data) +{ + NMDBusManager *self = NM_DBUS_MANAGER (user_data); + + /* Clean up existing connection */ + nm_log_warn (LOGD_CORE, "disconnected by the system bus."); + NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy = NULL; + + nm_dbus_manager_cleanup (self, FALSE); + + g_signal_emit (G_OBJECT (self), signals[DBUS_CONNECTION_CHANGED], 0, NULL); + + start_reconnection_timeout (self); +} + +static gboolean +nm_dbus_manager_init_bus (NMDBusManager *self) +{ + NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + GError *err = NULL; + + if (priv->connection) { + nm_log_warn (LOGD_CORE, "DBus Manager already has a valid connection."); + return FALSE; + } + + dbus_connection_set_change_sigpipe (TRUE); + + priv->g_connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err); + if (!priv->g_connection) { + nm_log_err (LOGD_CORE, "Could not get the system bus. Make sure " + "the message bus daemon is running! Message: %s", + err->message); + g_error_free (err); + return FALSE; + } + + priv->connection = dbus_g_connection_get_connection (priv->g_connection); + dbus_connection_set_exit_on_disconnect (priv->connection, FALSE); + + priv->proxy = dbus_g_proxy_new_for_name (priv->g_connection, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus"); + + priv->proxy_destroy_id = g_signal_connect (priv->proxy, "destroy", + G_CALLBACK (destroy_cb), self); + + dbus_g_proxy_add_signal (priv->proxy, "NameOwnerChanged", + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, + "NameOwnerChanged", + G_CALLBACK (proxy_name_owner_changed), + self, NULL); + return TRUE; +} + +/* Register our service on the bus; shouldn't be called until + * all necessary message handlers have been registered, because + * when we register on the bus, clients may start to call. + */ +gboolean +nm_dbus_manager_start_service (NMDBusManager *self) +{ + NMDBusManagerPrivate *priv; + int result; + GError *err = NULL; + + g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), FALSE); + + priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + + if (priv->started) { + nm_log_err (LOGD_CORE, "Service has already started."); + return FALSE; + } + + if (!dbus_g_proxy_call (priv->proxy, "RequestName", &err, + G_TYPE_STRING, NM_DBUS_SERVICE, + G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE, + G_TYPE_INVALID, + G_TYPE_UINT, &result, + G_TYPE_INVALID)) { + nm_log_err (LOGD_CORE, "Could not acquire the NetworkManager service.\n" + " Error: '%s'", + (err && err->message) ? err->message : "(unknown)"); + g_error_free (err); + return FALSE; + } + + if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + nm_log_err (LOGD_CORE, "Could not acquire the NetworkManager service as it is already taken."); + return FALSE; + } + + if (!dbus_g_proxy_call (priv->proxy, "RequestName", &err, + G_TYPE_STRING, NM_DBUS_SERVICE_SYSTEM_SETTINGS, + G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE, + G_TYPE_INVALID, + G_TYPE_UINT, &result, + G_TYPE_INVALID)) { + nm_log_warn (LOGD_CORE, "Could not acquire the NetworkManagerSystemSettings service.\n" + " Message: '%s'", err->message); + g_error_free (err); + return FALSE; + } + + if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + nm_log_warn (LOGD_CORE, "Could not acquire the NetworkManagerSystemSettings service " + "as it is already taken."); + return FALSE; + } + + priv->started = TRUE; + return priv->started; +} + +DBusConnection * +nm_dbus_manager_get_dbus_connection (NMDBusManager *self) +{ + g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), NULL); + + return NM_DBUS_MANAGER_GET_PRIVATE (self)->connection; +} + +DBusGConnection * +nm_dbus_manager_get_connection (NMDBusManager *self) +{ + g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), NULL); + + return NM_DBUS_MANAGER_GET_PRIVATE (self)->g_connection; +} diff --git a/src/nm-dbus-manager.h b/src/nm-dbus-manager.h new file mode 100644 index 00000000..275ae4e0 --- /dev/null +++ b/src/nm-dbus-manager.h @@ -0,0 +1,78 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2008 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef __NM_DBUS_MANAGER_H__ +#define __NM_DBUS_MANAGER_H__ + +#include "config.h" +#include <glib-object.h> +#include <dbus/dbus.h> +#include <dbus/dbus-glib.h> + +G_BEGIN_DECLS + +typedef gboolean (* NMDBusSignalHandlerFunc) (DBusConnection * connection, + DBusMessage * message, + gpointer user_data); + +#define NM_TYPE_DBUS_MANAGER (nm_dbus_manager_get_type ()) +#define NM_DBUS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_DBUS_MANAGER, NMDBusManager)) +#define NM_DBUS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_DBUS_MANAGER, NMDBusManagerClass)) +#define NM_IS_DBUS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NM_TYPE_DBUS_MANAGER)) +#define NM_IS_DBUS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NM_TYPE_DBUS_MANAGER)) +#define NM_DBUS_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NM_TYPE_DBUS_MANAGER, NMDBusManagerClass)) + +typedef struct { + GObject parent; +} NMDBusManager; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*dbus_connection_changed) (NMDBusManager *mgr, + DBusConnection *connection); + + void (*name_owner_changed) (NMDBusManager *mgr, + const char *name, + const char *old_owner, + const char *new_owner); +} NMDBusManagerClass; + +GType nm_dbus_manager_get_type (void); + +NMDBusManager * nm_dbus_manager_get (void); + +char * nm_dbus_manager_get_name_owner (NMDBusManager *self, + const char *name, + GError **error); + +gboolean nm_dbus_manager_start_service (NMDBusManager *self); + +gboolean nm_dbus_manager_name_has_owner (NMDBusManager *self, + const char *name); + +DBusConnection * nm_dbus_manager_get_dbus_connection (NMDBusManager *self); +DBusGConnection * nm_dbus_manager_get_connection (NMDBusManager *self); + +G_END_DECLS + +#endif /* __NM_DBUS_MANAGER_H__ */ diff --git a/src/nm-device-bt.c b/src/nm-device-bt.c new file mode 100644 index 00000000..3ef08de8 --- /dev/null +++ b/src/nm-device-bt.c @@ -0,0 +1,1073 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#include <stdio.h> +#include <string.h> +#include <net/ethernet.h> + +#include "nm-glib-compat.h" +#include "nm-bluez-common.h" +#include "nm-dbus-manager.h" +#include "nm-device-bt.h" +#include "nm-device-interface.h" +#include "nm-device-private.h" +#include "nm-logging.h" +#include "nm-marshal.h" +#include "ppp-manager/nm-ppp-manager.h" +#include "nm-properties-changed-signal.h" +#include "nm-setting-connection.h" +#include "nm-setting-bluetooth.h" +#include "nm-setting-cdma.h" +#include "nm-setting-gsm.h" +#include "nm-device-bt-glue.h" +#include "NetworkManagerUtils.h" + +#define BLUETOOTH_DUN_UUID "dun" +#define BLUETOOTH_NAP_UUID "nap" + +G_DEFINE_TYPE (NMDeviceBt, nm_device_bt, NM_TYPE_DEVICE) + +#define NM_DEVICE_BT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_BT, NMDeviceBtPrivate)) + +typedef struct { + char *bdaddr; + char *name; + guint32 capabilities; + + gboolean connected; + gboolean have_iface; + + DBusGProxy *type_proxy; + DBusGProxy *dev_proxy; + + char *rfcomm_iface; + NMModem *modem; + guint32 timeout_id; + + guint32 bt_type; /* BT type of the current connection */ +} NMDeviceBtPrivate; + +enum { + PROP_0, + PROP_HW_ADDRESS, + PROP_BT_NAME, + PROP_BT_CAPABILITIES, + + LAST_PROP +}; + +enum { + PPP_STATS, + PROPERTIES_CHANGED, + + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + + +typedef enum { + NM_BT_ERROR_CONNECTION_NOT_BT = 0, + NM_BT_ERROR_CONNECTION_INVALID, + NM_BT_ERROR_CONNECTION_INCOMPATIBLE, +} NMBtError; + +#define NM_BT_ERROR (nm_bt_error_quark ()) +#define NM_TYPE_BT_ERROR (nm_bt_error_get_type ()) + +static GQuark +nm_bt_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm-bt-error"); + return quark; +} + +/* This should really be standard. */ +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +static GType +nm_bt_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + /* Connection was not a BT connection. */ + ENUM_ENTRY (NM_BT_ERROR_CONNECTION_NOT_BT, "ConnectionNotBt"), + /* Connection was not a valid BT connection. */ + ENUM_ENTRY (NM_BT_ERROR_CONNECTION_INVALID, "ConnectionInvalid"), + /* Connection does not apply to this device. */ + ENUM_ENTRY (NM_BT_ERROR_CONNECTION_INCOMPATIBLE, "ConnectionIncompatible"), + { 0, 0, 0 } + }; + etype = g_enum_register_static ("NMBtError", values); + } + return etype; +} + + +guint32 nm_device_bt_get_capabilities (NMDeviceBt *self) +{ + g_return_val_if_fail (self != NULL, NM_BT_CAPABILITY_NONE); + g_return_val_if_fail (NM_IS_DEVICE_BT (self), NM_BT_CAPABILITY_NONE); + + return NM_DEVICE_BT_GET_PRIVATE (self)->capabilities; +} + +const char *nm_device_bt_get_hw_address (NMDeviceBt *self) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DEVICE_BT (self), NULL); + + return NM_DEVICE_BT_GET_PRIVATE (self)->bdaddr; +} + +static guint32 +get_connection_bt_type (NMConnection *connection) +{ + NMSettingBluetooth *s_bt; + const char *bt_type; + + s_bt = (NMSettingBluetooth *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BLUETOOTH); + if (!s_bt) + return NM_BT_CAPABILITY_NONE; + + bt_type = nm_setting_bluetooth_get_connection_type (s_bt); + g_assert (bt_type); + + if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)) + return NM_BT_CAPABILITY_DUN; + else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)) + return NM_BT_CAPABILITY_NAP; + + return NM_BT_CAPABILITY_NONE; +} + +static NMConnection * +real_get_best_auto_connection (NMDevice *device, + GSList *connections, + char **specific_object) +{ + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); + GSList *iter; + + for (iter = connections; iter; iter = g_slist_next (iter)) { + NMConnection *connection = NM_CONNECTION (iter->data); + NMSettingConnection *s_con; + guint32 bt_type; + + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + + if (!nm_setting_connection_get_autoconnect (s_con)) + continue; + + if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_BLUETOOTH_SETTING_NAME)) + continue; + + bt_type = get_connection_bt_type (connection); + if (!(bt_type & priv->capabilities)) + continue; + + return connection; + } + return NULL; +} + +static gboolean +real_check_connection_compatible (NMDevice *device, + NMConnection *connection, + GError **error) +{ + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); + NMSettingConnection *s_con; + NMSettingBluetooth *s_bt; + const GByteArray *array; + char *str; + int addr_match = FALSE; + guint32 bt_type; + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + + if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_BLUETOOTH_SETTING_NAME)) { + g_set_error (error, + NM_BT_ERROR, NM_BT_ERROR_CONNECTION_NOT_BT, + "The connection was not a Bluetooth connection."); + return FALSE; + } + + s_bt = NM_SETTING_BLUETOOTH (nm_connection_get_setting (connection, NM_TYPE_SETTING_BLUETOOTH)); + if (!s_bt) { + g_set_error (error, + NM_BT_ERROR, NM_BT_ERROR_CONNECTION_INVALID, + "The connection was not a valid Bluetooth connection."); + return FALSE; + } + + array = nm_setting_bluetooth_get_bdaddr (s_bt); + if (!array || (array->len != ETH_ALEN)) { + g_set_error (error, + NM_BT_ERROR, NM_BT_ERROR_CONNECTION_INVALID, + "The connection did not contain a valid Bluetooth address."); + return FALSE; + } + + bt_type = get_connection_bt_type (connection); + if (!(bt_type & priv->capabilities)) { + g_set_error (error, + NM_BT_ERROR, NM_BT_ERROR_CONNECTION_INCOMPATIBLE, + "The connection was not compatible with the device's capabilities."); + return FALSE; + } + + str = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", + array->data[0], array->data[1], array->data[2], + array->data[3], array->data[4], array->data[5]); + addr_match = !strcmp (priv->bdaddr, str); + g_free (str); + + return addr_match; +} + +static guint32 +real_get_generic_capabilities (NMDevice *dev) +{ + return NM_DEVICE_CAP_NM_SUPPORTED; +} + +/*****************************************************************************/ +/* IP method PPP */ + +static void +ppp_stats (NMModem *modem, + guint32 in_bytes, + guint32 out_bytes, + gpointer user_data) +{ + g_signal_emit (NM_DEVICE_BT (user_data), signals[PPP_STATS], 0, in_bytes, out_bytes); +} + +static void +ppp_failed (NMModem *modem, NMDeviceStateReason reason, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + + switch (nm_device_interface_get_state (NM_DEVICE_INTERFACE (device))) { + case NM_DEVICE_STATE_PREPARE: + case NM_DEVICE_STATE_CONFIG: + case NM_DEVICE_STATE_NEED_AUTH: + case NM_DEVICE_STATE_ACTIVATED: + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); + break; + case NM_DEVICE_STATE_IP_CONFIG: + if (nm_device_ip_config_should_fail (device, FALSE)) { + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + } + break; + default: + break; + } +} + +static void +modem_need_auth (NMModem *modem, + const char *setting_name, + gboolean retry, + RequestSecretsCaller caller, + const char *hint1, + const char *hint2, + gpointer user_data) +{ + NMDeviceBt *self = NM_DEVICE_BT (user_data); + NMActRequest *req; + + req = nm_device_get_act_request (NM_DEVICE (self)); + g_assert (req); + + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); + nm_act_request_get_secrets (req, setting_name, retry, caller, hint1, hint2); +} + +static void +modem_prepare_result (NMModem *modem, + gboolean success, + NMDeviceStateReason reason, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDeviceState state; + + state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); + g_return_if_fail (state == NM_DEVICE_STATE_CONFIG || state == NM_DEVICE_STATE_NEED_AUTH); + + if (success) { + NMActRequest *req; + NMActStageReturn ret; + NMDeviceStateReason stage2_reason = NM_DEVICE_STATE_REASON_NONE; + + req = nm_device_get_act_request (device); + g_assert (req); + + ret = nm_modem_act_stage2_config (modem, req, &stage2_reason); + switch (ret) { + case NM_ACT_STAGE_RETURN_POSTPONE: + break; + case NM_ACT_STAGE_RETURN_SUCCESS: + nm_device_activate_schedule_stage3_ip_config_start (device); + break; + case NM_ACT_STAGE_RETURN_FAILURE: + default: + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, stage2_reason); + break; + } + } else + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); +} + +static void +device_state_changed (NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason, + gpointer user_data) +{ + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); + + if (priv->modem) + nm_modem_device_state_changed (priv->modem, new_state, old_state, reason); +} + +static void +modem_ip4_config_result (NMModem *self, + const char *iface, + NMIP4Config *config, + GError *error, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDeviceState state; + + state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); + g_return_if_fail (state == NM_DEVICE_STATE_IP_CONFIG); + + if (error) { + nm_log_warn (LOGD_MB | LOGD_IP4 | LOGD_BT, + "(%s): retrieving IP4 configuration failed: (%d) %s", + nm_device_get_ip_iface (device), + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + } else { + if (iface) + nm_device_set_ip_iface (device, iface); + + nm_device_activate_schedule_stage4_ip4_config_get (device); + } +} + +static gboolean +modem_stage1 (NMDeviceBt *self, NMModem *modem, NMDeviceStateReason *reason) +{ + NMActRequest *req; + NMActStageReturn ret; + + g_return_val_if_fail (reason != NULL, FALSE); + + req = nm_device_get_act_request (NM_DEVICE (self)); + g_assert (req); + + ret = nm_modem_act_stage1_prepare (modem, req, reason); + switch (ret) { + case NM_ACT_STAGE_RETURN_POSTPONE: + case NM_ACT_STAGE_RETURN_SUCCESS: + /* Success, wait for the 'prepare-result' signal */ + return TRUE; + case NM_ACT_STAGE_RETURN_FAILURE: + default: + break; + } + + return FALSE; +} + +static void +real_connection_secrets_updated (NMDevice *device, + NMConnection *connection, + GSList *updated_settings, + RequestSecretsCaller caller) +{ + NMDeviceBt *self = NM_DEVICE_BT (device); + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); + NMActRequest *req; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + + g_return_if_fail (IS_ACTIVATING_STATE (nm_device_get_state (device))); + + req = nm_device_get_act_request (device); + g_assert (req); + + if (!nm_modem_connection_secrets_updated (priv->modem, + req, + connection, + updated_settings, + caller)) { + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NO_SECRETS); + return; + } + + /* PPP handles stuff itself... */ + if (caller == SECRETS_CALLER_PPP) + return; + + /* Otherwise, on success for GSM/CDMA secrets we need to schedule modem stage1 again */ + g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH); + if (!modem_stage1 (self, priv->modem, &reason)) + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, reason); +} + +/*****************************************************************************/ + +gboolean +nm_device_bt_modem_added (NMDeviceBt *self, + NMModem *modem, + const char *driver) +{ + NMDeviceBtPrivate *priv; + const char *modem_iface; + char *base; + NMDeviceState state; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_DEVICE_BT (self), FALSE); + g_return_val_if_fail (modem != NULL, FALSE); + g_return_val_if_fail (NM_IS_MODEM (modem), FALSE); + + priv = NM_DEVICE_BT_GET_PRIVATE (self); + modem_iface = nm_modem_get_iface (modem); + g_return_val_if_fail (modem_iface != NULL, FALSE); + + if (!priv->rfcomm_iface) + return FALSE; + + base = g_path_get_basename (priv->rfcomm_iface); + if (strcmp (base, modem_iface)) { + g_free (base); + return FALSE; + } + g_free (base); + + /* Got the modem */ + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + + /* Can only accept the modem in stage2, but since the interface matched + * what we were expecting, don't let anything else claim the modem either. + */ + state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self)); + if (state != NM_DEVICE_STATE_CONFIG) { + nm_log_warn (LOGD_BT | LOGD_MB, + "(%s): modem found but device not in correct state (%d)", + nm_device_get_iface (NM_DEVICE (self)), + nm_device_get_state (NM_DEVICE (self))); + return TRUE; + } + + nm_log_info (LOGD_BT | LOGD_MB, + "Activation (%s/bluetooth) Stage 2 of 5 (Device Configure) modem found.", + nm_device_get_iface (NM_DEVICE (self))); + + if (priv->modem) { + g_warn_if_reached (); + g_object_unref (priv->modem); + } + + priv->modem = g_object_ref (modem); + g_signal_connect (modem, NM_MODEM_PPP_STATS, G_CALLBACK (ppp_stats), self); + g_signal_connect (modem, NM_MODEM_PPP_FAILED, G_CALLBACK (ppp_failed), self); + g_signal_connect (modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK (modem_prepare_result), self); + g_signal_connect (modem, NM_MODEM_IP4_CONFIG_RESULT, G_CALLBACK (modem_ip4_config_result), self); + g_signal_connect (modem, NM_MODEM_NEED_AUTH, G_CALLBACK (modem_need_auth), self); + + /* Kick off the modem connection */ + if (!modem_stage1 (self, modem, &reason)) + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, reason); + + return TRUE; +} + +gboolean +nm_device_bt_modem_removed (NMDeviceBt *self, NMModem *modem) +{ + NMDeviceBtPrivate *priv; + NMDeviceState state; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_DEVICE_BT (self), FALSE); + g_return_val_if_fail (modem != NULL, FALSE); + g_return_val_if_fail (NM_IS_MODEM (modem), FALSE); + + priv = NM_DEVICE_BT_GET_PRIVATE (self); + + if (modem != priv->modem) + return FALSE; + + state = nm_device_get_state (NM_DEVICE (self)); + nm_modem_device_state_changed (priv->modem, + NM_DEVICE_STATE_DISCONNECTED, + state, + NM_DEVICE_STATE_REASON_USER_REQUESTED); + + g_object_unref (priv->modem); + priv->modem = NULL; + return TRUE; +} + +static gboolean +modem_find_timeout (gpointer user_data) +{ + NMDeviceBt *self = NM_DEVICE_BT (user_data); + + NM_DEVICE_BT_GET_PRIVATE (self)->timeout_id = 0; + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND); + return FALSE; +} + +static void +check_connect_continue (NMDeviceBt *self) +{ + NMDevice *device = NM_DEVICE (self); + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); + gboolean pan = (priv->bt_type == NM_BT_CAPABILITY_NAP); + gboolean dun = (priv->bt_type == NM_BT_CAPABILITY_DUN); + + if (!priv->connected || !priv->have_iface) + return; + + nm_log_info (LOGD_BT, "Activation (%s %s/bluetooth) Stage 2 of 5 (Device Configure) " + "successful. Will connect via %s.", + nm_device_get_iface (device), + nm_device_get_ip_iface (device), + dun ? "DUN" : (pan ? "PAN" : "unknown")); + + /* Kill the connect timeout since we're connected now */ + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + + if (pan) { + /* Bluez says we're connected now. Start IP config. */ + nm_device_activate_schedule_stage3_ip_config_start (device); + } else if (dun) { + /* Wait for ModemManager to find the modem */ + priv->timeout_id = g_timeout_add_seconds (30, modem_find_timeout, self); + + nm_log_info (LOGD_BT | LOGD_MB, "Activation (%s/bluetooth) Stage 2 of 5 (Device Configure) " + "waiting for modem to appear.", + nm_device_get_iface (device)); + } else + g_assert_not_reached (); +} + +static void +bluez_connect_cb (DBusGProxy *proxy, + DBusGProxyCall *call_id, + void *user_data) +{ + NMDeviceBt *self = NM_DEVICE_BT (user_data); + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); + GError *error = NULL; + char *device; + + if (dbus_g_proxy_end_call (proxy, call_id, &error, + G_TYPE_STRING, &device, + G_TYPE_INVALID) == FALSE) { + nm_log_warn (LOGD_BT, "Error connecting with bluez: %s", + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_BT_FAILED); + return; + } + + if (!device || !strlen (device)) { + nm_log_warn (LOGD_BT, "Invalid network device returned by bluez"); + + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_BT_FAILED); + } + + if (priv->bt_type == NM_BT_CAPABILITY_DUN) { + g_free (priv->rfcomm_iface); + priv->rfcomm_iface = device; + } else if (priv->bt_type == NM_BT_CAPABILITY_NAP) { + nm_device_set_ip_iface (NM_DEVICE (self), device); + g_free (device); + } + + nm_log_dbg (LOGD_BT, "(%s): connect request successful", + nm_device_get_iface (NM_DEVICE (self))); + + /* Stage 3 gets scheduled when Bluez says we're connected */ + priv->have_iface = TRUE; + check_connect_continue (self); +} + +static void +bluez_property_changed (DBusGProxy *proxy, + const char *property, + GValue *value, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDeviceBt *self = NM_DEVICE_BT (user_data); + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); + gboolean connected; + NMDeviceState state; + const char *prop_str = "(unknown)"; + + if (G_VALUE_HOLDS_STRING (value)) + prop_str = g_value_get_string (value); + else if (G_VALUE_HOLDS_BOOLEAN (value)) + prop_str = g_value_get_boolean (value) ? "true" : "false"; + + nm_log_dbg (LOGD_BT, "(%s): bluez property '%s' changed to '%s'", + nm_device_get_iface (device), + property, + prop_str); + + if (strcmp (property, "Connected")) + return; + + state = nm_device_get_state (device); + connected = g_value_get_boolean (value); + if (connected) { + if (state == NM_DEVICE_STATE_CONFIG) { + nm_log_dbg (LOGD_BT, "(%s): connected to the device", + nm_device_get_iface (device)); + + priv->connected = TRUE; + check_connect_continue (self); + } + } else { + gboolean fail = FALSE; + + /* Bluez says we're disconnected from the device. Suck. */ + + if (nm_device_is_activating (device)) { + nm_log_info (LOGD_BT, + "Activation (%s/bluetooth): bluetooth link disconnected.", + nm_device_get_iface (device)); + fail = TRUE; + } else if (state == NM_DEVICE_STATE_ACTIVATED) { + nm_log_info (LOGD_BT, "(%s): bluetooth link disconnected.", + nm_device_get_iface (device)); + fail = TRUE; + } + + if (fail) { + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CARRIER); + priv->connected = FALSE; + } + } +} + +static gboolean +bt_connect_timeout (gpointer user_data) +{ + NMDeviceBt *self = NM_DEVICE_BT (user_data); + + nm_log_dbg (LOGD_BT, "(%s): initial connection timed out", + nm_device_get_iface (NM_DEVICE (self))); + + NM_DEVICE_BT_GET_PRIVATE (self)->timeout_id = 0; + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_BT_FAILED); + return FALSE; +} + +static NMActStageReturn +real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason) +{ + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); + NMActRequest *req; + NMDBusManager *dbus_mgr; + DBusGConnection *g_connection; + gboolean dun = FALSE; + + req = nm_device_get_act_request (device); + g_assert (req); + + priv->bt_type = get_connection_bt_type (nm_act_request_get_connection (req)); + if (priv->bt_type == NM_BT_CAPABILITY_NONE) { + // FIXME: set a reason code + return NM_ACT_STAGE_RETURN_FAILURE; + } + + dbus_mgr = nm_dbus_manager_get (); + g_connection = nm_dbus_manager_get_connection (dbus_mgr); + g_object_unref (dbus_mgr); + + if (priv->bt_type == NM_BT_CAPABILITY_DUN) + dun = TRUE; + else if (priv->bt_type == NM_BT_CAPABILITY_NAP) + dun = FALSE; + else + g_assert_not_reached (); + + priv->dev_proxy = dbus_g_proxy_new_for_name (g_connection, + BLUEZ_SERVICE, + nm_device_get_udi (device), + BLUEZ_DEVICE_INTERFACE); + if (!priv->dev_proxy) { + // FIXME: set a reason code + return NM_ACT_STAGE_RETURN_FAILURE; + } + + /* Watch for BT device property changes */ + dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_BOXED, + G_TYPE_NONE, + G_TYPE_STRING, G_TYPE_VALUE, + G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->dev_proxy, "PropertyChanged", + G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->dev_proxy, "PropertyChanged", + G_CALLBACK (bluez_property_changed), device, NULL); + + priv->type_proxy = dbus_g_proxy_new_for_name (g_connection, + BLUEZ_SERVICE, + nm_device_get_udi (device), + dun ? BLUEZ_SERIAL_INTERFACE : BLUEZ_NETWORK_INTERFACE); + if (!priv->type_proxy) { + // FIXME: set a reason code + return NM_ACT_STAGE_RETURN_FAILURE; + } + + nm_log_dbg (LOGD_BT, "(%s): requesting connection to the device", + nm_device_get_iface (device)); + + /* Connect to the BT device */ + dbus_g_proxy_begin_call_with_timeout (priv->type_proxy, "Connect", + bluez_connect_cb, + device, + NULL, + 20000, + G_TYPE_STRING, dun ? BLUETOOTH_DUN_UUID : BLUETOOTH_NAP_UUID, + G_TYPE_INVALID); + + if (priv->timeout_id) + g_source_remove (priv->timeout_id); + priv->timeout_id = g_timeout_add_seconds (30, bt_connect_timeout, device); + + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +static NMActStageReturn +real_act_stage3_ip4_config_start (NMDevice *device, NMDeviceStateReason *reason) +{ + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); + NMActStageReturn ret; + + if (priv->bt_type == NM_BT_CAPABILITY_DUN) { + ret = nm_modem_stage3_ip4_config_start (NM_DEVICE_BT_GET_PRIVATE (device)->modem, + device, + NM_DEVICE_CLASS (nm_device_bt_parent_class), + reason); + } else + ret = NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage3_ip4_config_start (device, reason); + + return ret; +} + +static NMActStageReturn +real_act_stage4_get_ip4_config (NMDevice *device, + NMIP4Config **config, + NMDeviceStateReason *reason) +{ + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); + NMActStageReturn ret; + + if (priv->bt_type == NM_BT_CAPABILITY_DUN) { + ret = nm_modem_stage4_get_ip4_config (NM_DEVICE_BT_GET_PRIVATE (device)->modem, + device, + NM_DEVICE_CLASS (nm_device_bt_parent_class), + config, + reason); + } else + ret = NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage4_get_ip4_config (device, config, reason); + + return ret; +} + +static void +real_deactivate_quickly (NMDevice *device) +{ + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); + + priv->have_iface = FALSE; + priv->connected = FALSE; + + if (priv->bt_type == NM_BT_CAPABILITY_DUN) { + + if (priv->modem) { + nm_modem_deactivate_quickly (priv->modem, device); + + /* Since we're killing the Modem object before it'll get the + * state change signal, simulate the state change here. + */ + nm_modem_device_state_changed (priv->modem, + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_ACTIVATED, + NM_DEVICE_STATE_REASON_USER_REQUESTED); + g_object_unref (priv->modem); + priv->modem = NULL; + } + + if (priv->type_proxy) { + /* Don't ever pass NULL through dbus; rfcomm_iface + * might happen to be NULL for some reason. + */ + if (priv->rfcomm_iface) { + dbus_g_proxy_call_no_reply (priv->type_proxy, "Disconnect", + G_TYPE_STRING, priv->rfcomm_iface, + G_TYPE_INVALID); + } + g_object_unref (priv->type_proxy); + priv->type_proxy = NULL; + } + } else if (priv->bt_type == NM_BT_CAPABILITY_NAP) { + if (priv->type_proxy) { + dbus_g_proxy_call_no_reply (priv->type_proxy, "Disconnect", + G_TYPE_INVALID); + g_object_unref (priv->type_proxy); + priv->type_proxy = NULL; + } + } + + if (priv->dev_proxy) { + g_object_unref (priv->dev_proxy); + priv->dev_proxy = NULL; + } + + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + + priv->bt_type = NM_BT_CAPABILITY_NONE; + + g_free (priv->rfcomm_iface); + priv->rfcomm_iface = NULL; + + if (NM_DEVICE_CLASS (nm_device_bt_parent_class)->deactivate_quickly) + NM_DEVICE_CLASS (nm_device_bt_parent_class)->deactivate_quickly (device); +} + +/*****************************************************************************/ + +NMDevice * +nm_device_bt_new (const char *udi, + const char *bdaddr, + const char *name, + guint32 capabilities, + gboolean managed) +{ + NMDevice *device; + + g_return_val_if_fail (udi != NULL, NULL); + g_return_val_if_fail (bdaddr != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (capabilities != NM_BT_CAPABILITY_NONE, NULL); + + device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_BT, + NM_DEVICE_INTERFACE_UDI, udi, + NM_DEVICE_INTERFACE_IFACE, bdaddr, + NM_DEVICE_INTERFACE_DRIVER, "bluez", + NM_DEVICE_BT_HW_ADDRESS, bdaddr, + NM_DEVICE_BT_NAME, name, + NM_DEVICE_BT_CAPABILITIES, capabilities, + NM_DEVICE_INTERFACE_MANAGED, managed, + NM_DEVICE_INTERFACE_TYPE_DESC, "Bluetooth", + NM_DEVICE_INTERFACE_DEVICE_TYPE, NM_DEVICE_TYPE_BT, + NULL); + if (device) + g_signal_connect (device, "state-changed", G_CALLBACK (device_state_changed), device); + + return device; +} + +static void +nm_device_bt_init (NMDeviceBt *self) +{ +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_HW_ADDRESS: + /* Construct only */ + priv->bdaddr = g_ascii_strup (g_value_get_string (value), -1); + break; + case PROP_BT_NAME: + /* Construct only */ + priv->name = g_value_dup_string (value); + break; + case PROP_BT_CAPABILITIES: + /* Construct only */ + priv->capabilities = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_HW_ADDRESS: + g_value_set_string (value, priv->bdaddr); + break; + case PROP_BT_NAME: + g_value_set_string (value, priv->name); + break; + case PROP_BT_CAPABILITIES: + g_value_set_uint (value, priv->capabilities); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +finalize (GObject *object) +{ + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object); + + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + + if (priv->type_proxy) + g_object_unref (priv->type_proxy); + + if (priv->dev_proxy) + g_object_unref (priv->dev_proxy); + + if (priv->modem) + g_object_unref (priv->modem); + + g_free (priv->rfcomm_iface); + g_free (priv->bdaddr); + g_free (priv->name); + + G_OBJECT_CLASS (nm_device_bt_parent_class)->finalize (object); +} + +static void +nm_device_bt_class_init (NMDeviceBtClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMDeviceBtPrivate)); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + + device_class->get_best_auto_connection = real_get_best_auto_connection; + device_class->get_generic_capabilities = real_get_generic_capabilities; + device_class->connection_secrets_updated = real_connection_secrets_updated; + device_class->deactivate_quickly = real_deactivate_quickly; + device_class->act_stage2_config = real_act_stage2_config; + device_class->act_stage3_ip4_config_start = real_act_stage3_ip4_config_start; + device_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config; + device_class->check_connection_compatible = real_check_connection_compatible; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_DEVICE_BT_HW_ADDRESS, + "Bluetooth address", + "Bluetooth address", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_BT_NAME, + g_param_spec_string (NM_DEVICE_BT_NAME, + "Bluetooth device name", + "Bluetooth device name", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_BT_CAPABILITIES, + g_param_spec_uint (NM_DEVICE_BT_CAPABILITIES, + "Bluetooth device capabilities", + "Bluetooth device capabilities", + NM_BT_CAPABILITY_NONE, G_MAXUINT, NM_BT_CAPABILITY_NONE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /* Signals */ + signals[PPP_STATS] = + g_signal_new ("ppp-stats", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDeviceBtClass, ppp_stats), + NULL, NULL, + _nm_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, + G_TYPE_UINT, G_TYPE_UINT); + + signals[PROPERTIES_CHANGED] = + nm_properties_changed_signal_new (object_class, + G_STRUCT_OFFSET (NMDeviceBtClass, properties_changed)); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), + &dbus_glib_nm_device_bt_object_info); + + dbus_g_error_domain_register (NM_BT_ERROR, NULL, NM_TYPE_BT_ERROR); +} diff --git a/src/nm-device-bt.h b/src/nm-device-bt.h new file mode 100644 index 00000000..d3187d76 --- /dev/null +++ b/src/nm-device-bt.h @@ -0,0 +1,72 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_BT_H +#define NM_DEVICE_BT_H + +#include <nm-device.h> +#include "nm-modem.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_BT (nm_device_bt_get_type ()) +#define NM_DEVICE_BT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_BT, NMDeviceBt)) +#define NM_DEVICE_BT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_BT, NMDeviceBtClass)) +#define NM_IS_DEVICE_BT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_BT)) +#define NM_IS_DEVICE_BT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_BT)) +#define NM_DEVICE_BT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_BT, NMDeviceBtClass)) + +#define NM_DEVICE_BT_HW_ADDRESS "hw-address" +#define NM_DEVICE_BT_NAME "name" +#define NM_DEVICE_BT_CAPABILITIES "bt-capabilities" + +typedef struct { + NMDevice parent; +} NMDeviceBt; + +typedef struct { + NMDeviceClass parent; + + /* Signals */ + void (*ppp_stats) (NMDeviceBt *device, guint32 in_bytes, guint32 out_bytes); + void (*properties_changed) (NMDeviceBt *device, GHashTable *properties); +} NMDeviceBtClass; + +GType nm_device_bt_get_type (void); + +NMDevice *nm_device_bt_new (const char *udi, + const char *bdaddr, + const char *name, + guint32 capabilities, + gboolean managed); + +guint32 nm_device_bt_get_capabilities (NMDeviceBt *device); + +const char *nm_device_bt_get_hw_address (NMDeviceBt *device); + +gboolean nm_device_bt_modem_added (NMDeviceBt *device, + NMModem *modem, + const char *driver); + +gboolean nm_device_bt_modem_removed (NMDeviceBt *device, NMModem *modem); + +G_END_DECLS + +#endif /* NM_GSM_DEVICE_H */ diff --git a/src/nm-device-cdma.c b/src/nm-device-cdma.c new file mode 100644 index 00000000..45771f42 --- /dev/null +++ b/src/nm-device-cdma.c @@ -0,0 +1,75 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#include <string.h> + +#include "nm-modem-cdma.h" +#include "nm-device-interface.h" +#include "nm-device-cdma.h" +#include "nm-properties-changed-signal.h" +#include "nm-rfkill.h" + +#include "nm-device-cdma-glue.h" + +G_DEFINE_TYPE (NMDeviceCdma, nm_device_cdma, NM_TYPE_DEVICE_MODEM) + +enum { + PROPERTIES_CHANGED, + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + +NMDevice * +nm_device_cdma_new (NMModemCdma *modem, const char *driver) +{ + g_return_val_if_fail (modem != NULL, NULL); + g_return_val_if_fail (NM_IS_MODEM_CDMA (modem), NULL); + g_return_val_if_fail (driver != NULL, NULL); + + return (NMDevice *) g_object_new (NM_TYPE_DEVICE_CDMA, + NM_DEVICE_INTERFACE_UDI, nm_modem_get_path (NM_MODEM (modem)), + NM_DEVICE_INTERFACE_IFACE, nm_modem_get_iface (NM_MODEM (modem)), + NM_DEVICE_INTERFACE_DRIVER, driver, + NM_DEVICE_INTERFACE_TYPE_DESC, "CDMA", + NM_DEVICE_INTERFACE_DEVICE_TYPE, NM_DEVICE_TYPE_CDMA, + NM_DEVICE_INTERFACE_RFKILL_TYPE, RFKILL_TYPE_WWAN, + NM_DEVICE_MODEM_MODEM, modem, + NULL); +} + +static void +nm_device_cdma_init (NMDeviceCdma *self) +{ +} + +static void +nm_device_cdma_class_init (NMDeviceCdmaClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + /* Signals */ + signals[PROPERTIES_CHANGED] = + nm_properties_changed_signal_new (object_class, + G_STRUCT_OFFSET (NMDeviceCdmaClass, properties_changed)); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), + &dbus_glib_nm_device_cdma_object_info); +} + diff --git a/src/nm-device-cdma.h b/src/nm-device-cdma.h new file mode 100644 index 00000000..f0bf3456 --- /dev/null +++ b/src/nm-device-cdma.h @@ -0,0 +1,55 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_CDMA_H +#define NM_DEVICE_CDMA_H + +#include "nm-device-modem.h" +#include "nm-modem-cdma.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_CDMA (nm_device_cdma_get_type ()) +#define NM_DEVICE_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_CDMA, NMDeviceCdma)) +#define NM_DEVICE_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_CDMA, NMDeviceCdmaClass)) +#define NM_IS_DEVICE_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_CDMA)) +#define NM_IS_DEVICE_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_CDMA)) +#define NM_DEVICE_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_CDMA, NMDeviceCdmaClass)) + +typedef struct { + NMDeviceModem parent; +} NMDeviceCdma; + +typedef struct { + NMDeviceModemClass parent; + + /* Signals */ + void (*signal_quality) (NMDeviceCdma *self, guint32 quality); + + void (*properties_changed) (NMDeviceCdma *self, GHashTable *properties); +} NMDeviceCdmaClass; + +GType nm_device_cdma_get_type (void); + +NMDevice *nm_device_cdma_new (NMModemCdma *modem, const char *driver); + +G_END_DECLS + +#endif /* NM_DEVICE_CDMA_H */ diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c new file mode 100644 index 00000000..be6d4e2f --- /dev/null +++ b/src/nm-device-ethernet.c @@ -0,0 +1,1975 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include <glib.h> +#include <glib/gi18n.h> +#include <netinet/in.h> +#include <string.h> +#include <net/ethernet.h> +#include <stdlib.h> +#include <linux/types.h> +#include <linux/sockios.h> +#include <linux/version.h> +#include <linux/ethtool.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <linux/if.h> +#include <errno.h> + +#include <netlink/route/addr.h> + +#include "nm-glib-compat.h" +#include "nm-device-ethernet.h" +#include "nm-device-interface.h" +#include "nm-device-private.h" +#include "nm-activation-request.h" +#include "NetworkManagerUtils.h" +#include "nm-supplicant-manager.h" +#include "nm-supplicant-interface.h" +#include "nm-supplicant-config.h" +#include "nm-netlink-monitor.h" +#include "nm-system.h" +#include "nm-setting-connection.h" +#include "nm-setting-wired.h" +#include "nm-setting-8021x.h" +#include "nm-setting-pppoe.h" +#include "ppp-manager/nm-ppp-manager.h" +#include "nm-logging.h" +#include "nm-properties-changed-signal.h" +#include "nm-dhcp-manager.h" + +#include "nm-device-ethernet-glue.h" + + +G_DEFINE_TYPE (NMDeviceEthernet, nm_device_ethernet, NM_TYPE_DEVICE) + +#define NM_DEVICE_ETHERNET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetPrivate)) + +#define WIRED_SECRETS_TRIES "wired-secrets-tries" + +typedef enum +{ + NM_ETHERNET_ERROR_CONNECTION_NOT_WIRED = 0, + NM_ETHERNET_ERROR_CONNECTION_INVALID, + NM_ETHERNET_ERROR_CONNECTION_INCOMPATIBLE, +} NMEthernetError; + +#define NM_ETHERNET_ERROR (nm_ethernet_error_quark ()) +#define NM_TYPE_ETHERNET_ERROR (nm_ethernet_error_get_type ()) + +typedef struct SupplicantStateTask { + NMDeviceEthernet *self; + guint32 new_state; + guint32 old_state; + gboolean mgr_task; + guint source_id; +} SupplicantStateTask; + +typedef struct Supplicant { + NMSupplicantManager *mgr; + NMSupplicantInterface *iface; + + /* signal handler ids */ + guint mgr_state_id; + guint iface_error_id; + guint iface_state_id; + guint iface_con_state_id; + + /* Timeouts and idles */ + guint iface_con_error_cb_id; + guint con_timeout_id; + + GSList *iface_tasks; + GSList *mgr_tasks; +} Supplicant; + +typedef struct { + gboolean disposed; + + struct ether_addr hw_addr; + gboolean carrier; + + NMNetlinkMonitor * monitor; + gulong link_connected_id; + gulong link_disconnected_id; + guint carrier_action_defer_id; + + Supplicant supplicant; + guint supplicant_timeout_id; + + /* PPPoE */ + NMPPPManager *ppp_manager; + NMIP4Config *pending_ip4_config; +} NMDeviceEthernetPrivate; + +enum { + PROPERTIES_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum { + PROP_0, + PROP_HW_ADDRESS, + PROP_SPEED, + PROP_CARRIER, + + LAST_PROP +}; + + +static gboolean supports_mii_carrier_detect (NMDeviceEthernet *dev); +static gboolean supports_ethtool_carrier_detect (NMDeviceEthernet *dev); + +static GQuark +nm_ethernet_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm-ethernet-error"); + return quark; +} + +/* This should really be standard. */ +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +static GType +nm_ethernet_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + /* Connection was not a wired connection. */ + ENUM_ENTRY (NM_ETHERNET_ERROR_CONNECTION_NOT_WIRED, "ConnectionNotWired"), + /* Connection was not a valid wired connection. */ + ENUM_ENTRY (NM_ETHERNET_ERROR_CONNECTION_INVALID, "ConnectionInvalid"), + /* Connection does not apply to this device. */ + ENUM_ENTRY (NM_ETHERNET_ERROR_CONNECTION_INCOMPATIBLE, "ConnectionIncompatible"), + { 0, 0, 0 } + }; + etype = g_enum_register_static ("NMEthernetError", values); + } + return etype; +} + +static void +carrier_action_defer_clear (NMDeviceEthernet *self) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + + if (priv->carrier_action_defer_id) { + g_source_remove (priv->carrier_action_defer_id); + priv->carrier_action_defer_id = 0; + } +} + +static gboolean +carrier_action_defer_cb (gpointer user_data) +{ + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + NMDeviceState state; + + priv->carrier_action_defer_id = 0; + + state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self)); + if (state == NM_DEVICE_STATE_UNAVAILABLE) { + if (priv->carrier) + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_CARRIER); + } else if (state >= NM_DEVICE_STATE_DISCONNECTED) { + if (!priv->carrier) + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_REASON_CARRIER); + } + + return FALSE; +} + +static void +set_carrier (NMDeviceEthernet *self, + const gboolean carrier, + const gboolean defer_action) +{ + NMDeviceEthernetPrivate *priv; + NMDeviceState state; + + g_return_if_fail (NM_IS_DEVICE (self)); + + priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + if (priv->carrier == carrier) + return; + + /* Clear any previous deferred action */ + carrier_action_defer_clear (self); + + priv->carrier = carrier; + g_object_notify (G_OBJECT (self), NM_DEVICE_ETHERNET_CARRIER); + + state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self)); + nm_log_info (LOGD_HW | LOGD_ETHER, "(%s): carrier now %s (device state %d%s)", + nm_device_get_iface (NM_DEVICE (self)), + carrier ? "ON" : "OFF", + state, + defer_action ? ", deferring action for 4 seconds" : ""); + + if (defer_action) + priv->carrier_action_defer_id = g_timeout_add_seconds (4, carrier_action_defer_cb, self); + else + carrier_action_defer_cb (self); +} + +static void +carrier_on (NMNetlinkMonitor *monitor, + int idx, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device); + guint32 caps; + + /* Make sure signal is for us */ + if (idx == nm_device_get_ifindex (device)) { + /* Ignore spurious netlink messages */ + caps = nm_device_get_capabilities (device); + if (!(caps & NM_DEVICE_CAP_CARRIER_DETECT)) + return; + + set_carrier (self, TRUE, FALSE); + } +} + +static void +carrier_off (NMNetlinkMonitor *monitor, + int idx, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device); + guint32 caps; + + /* Make sure signal is for us */ + if (idx == nm_device_get_ifindex (device)) { + NMDeviceState state; + gboolean defer = FALSE; + + /* Ignore spurious netlink messages */ + caps = nm_device_get_capabilities (device); + if (!(caps & NM_DEVICE_CAP_CARRIER_DETECT)) + return; + + /* Defer carrier-off event actions while connected by a few seconds + * so that tripping over a cable, power-cycling a switch, or breaking + * off the RJ45 locking tab isn't so catastrophic. + */ + state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self)); + if (state > NM_DEVICE_STATE_DISCONNECTED) + defer = TRUE; + + set_carrier (self, FALSE, defer); + } +} + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + NMDeviceEthernetPrivate *priv; + NMDevice *self; + guint32 caps; + + object = G_OBJECT_CLASS (nm_device_ethernet_parent_class)->constructor (type, + n_construct_params, + construct_params); + if (!object) + return NULL; + + self = NM_DEVICE (object); + priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + + nm_log_dbg (LOGD_HW | LOGD_OLPC_MESH, "(%s): kernel ifindex %d", + nm_device_get_iface (NM_DEVICE (self)), + nm_device_get_ifindex (NM_DEVICE (self))); + + caps = nm_device_get_capabilities (self); + if (caps & NM_DEVICE_CAP_CARRIER_DETECT) { + GError *error = NULL; + guint32 ifflags = 0; + + /* Only listen to netlink for cards that support carrier detect */ + priv->monitor = nm_netlink_monitor_get (); + + priv->link_connected_id = g_signal_connect (priv->monitor, "carrier-on", + G_CALLBACK (carrier_on), + self); + priv->link_disconnected_id = g_signal_connect (priv->monitor, "carrier-off", + G_CALLBACK (carrier_off), + self); + + /* Get initial link state */ + if (!nm_netlink_monitor_get_flags_sync (priv->monitor, + nm_device_get_ifindex (NM_DEVICE (self)), + &ifflags, + &error)) { + nm_log_warn (LOGD_HW | LOGD_ETHER, + "(%s): couldn't get initial carrier state: (%d) %s", + nm_device_get_iface (NM_DEVICE (self)), + error ? error->code : -1, + (error && error->message) ? error->message : "unknown"); + g_clear_error (&error); + } else + priv->carrier = !!(ifflags & IFF_LOWER_UP); + + nm_log_info (LOGD_HW | LOGD_ETHER, + "(%s): carrier is %s", + nm_device_get_iface (NM_DEVICE (self)), + priv->carrier ? "ON" : "OFF"); + + /* Request link state again just in case an error occurred getting the + * initial link state. + */ + if (!nm_netlink_monitor_request_status (priv->monitor, &error)) { + nm_log_warn (LOGD_HW | LOGD_ETHER, + "(%s): couldn't request carrier state: (%d) %s", + nm_device_get_iface (NM_DEVICE (self)), + error ? error->code : -1, + (error && error->message) ? error->message : "unknown"); + g_clear_error (&error); + } + } else { + nm_log_info (LOGD_HW | LOGD_ETHER, + "(%s): driver '%s' does not support carrier detection.", + nm_device_get_iface (self), + nm_device_get_driver (self)); + priv->carrier = TRUE; + } + + return object; +} + +static void +nm_device_ethernet_init (NMDeviceEthernet * self) +{ +} + +static gboolean +real_is_up (NMDevice *device) +{ + if (!NM_DEVICE_ETHERNET_GET_PRIVATE (device)->supplicant.mgr) + return FALSE; + + return TRUE; +} + +static gboolean +real_bring_up (NMDevice *dev) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (dev); + + priv->supplicant.mgr = nm_supplicant_manager_get (); + + return priv->supplicant.mgr ? TRUE : FALSE; +} + +static void +real_take_down (NMDevice *dev) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (dev); + + if (priv->supplicant.mgr) { + g_object_unref (priv->supplicant.mgr); + priv->supplicant.mgr = NULL; + } +} + +static gboolean +real_hw_is_up (NMDevice *device) +{ + return nm_system_device_is_up (device); +} + +static gboolean +real_hw_bring_up (NMDevice *dev, gboolean *no_firmware) +{ + return nm_system_device_set_up_down (dev, TRUE, no_firmware); +} + +static void +real_hw_take_down (NMDevice *dev) +{ + nm_system_device_set_up_down (dev, FALSE, NULL); +} + +NMDevice * +nm_device_ethernet_new (const char *udi, + const char *iface, + const char *driver) +{ + g_return_val_if_fail (udi != NULL, NULL); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + + return (NMDevice *) g_object_new (NM_TYPE_DEVICE_ETHERNET, + NM_DEVICE_INTERFACE_UDI, udi, + NM_DEVICE_INTERFACE_IFACE, iface, + NM_DEVICE_INTERFACE_DRIVER, driver, + NM_DEVICE_INTERFACE_TYPE_DESC, "Ethernet", + NM_DEVICE_INTERFACE_DEVICE_TYPE, NM_DEVICE_TYPE_ETHERNET, + NULL); +} + + +/* + * nm_device_ethernet_get_address + * + * Get a device's hardware address + * + */ +void +nm_device_ethernet_get_address (NMDeviceEthernet *self, struct ether_addr *addr) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (addr != NULL); + + memcpy (addr, &(NM_DEVICE_ETHERNET_GET_PRIVATE (self)->hw_addr), sizeof (struct ether_addr)); +} + +/* Returns speed in Mb/s */ +static guint32 +nm_device_ethernet_get_speed (NMDeviceEthernet *self) +{ + int fd; + struct ifreq ifr; + struct ethtool_cmd edata = { + .cmd = ETHTOOL_GSET, + }; + guint32 speed = 0; + + g_return_val_if_fail (self != NULL, 0); + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_warn (LOGD_HW, "couldn't open control socket."); + return 0; + } + + memset (&ifr, 0, sizeof (struct ifreq)); + strncpy (ifr.ifr_name, nm_device_get_iface (NM_DEVICE (self)), IFNAMSIZ); + ifr.ifr_data = (char *) &edata; + + if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) + goto out; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + speed = edata.speed; +#else + speed = ethtool_cmd_speed (&edata); +#endif + + if (speed == G_MAXUINT16 || speed == G_MAXUINT32) + speed = 0; + +out: + close (fd); + return speed; +} + +static void +real_update_hw_address (NMDevice *dev) +{ + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + struct ifreq req; + int fd; + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_warn (LOGD_HW, "couldn't open control socket."); + return; + } + + memset (&req, 0, sizeof (struct ifreq)); + strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ); + if (ioctl (fd, SIOCGIFHWADDR, &req) < 0) { + nm_log_err (LOGD_HW | LOGD_ETHER, + "(%s) error getting hardware address: %d", + nm_device_get_iface (dev), errno); + goto out; + } + + if (memcmp (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr))) { + memcpy (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr)); + g_object_notify (G_OBJECT (dev), NM_DEVICE_ETHERNET_HW_ADDRESS); + } + +out: + close (fd); +} + +static guint32 +real_get_generic_capabilities (NMDevice *dev) +{ + NMDeviceEthernet * self = NM_DEVICE_ETHERNET (dev); + guint32 caps = NM_DEVICE_CAP_NONE; + + /* cipsec devices are also explicitly unsupported at this time */ + if (strstr (nm_device_get_iface (dev), "cipsec")) + return NM_DEVICE_CAP_NONE; + + if (supports_ethtool_carrier_detect (self) || supports_mii_carrier_detect (self)) + caps |= NM_DEVICE_CAP_CARRIER_DETECT; + + caps |= NM_DEVICE_CAP_NM_SUPPORTED; + + return caps; +} + +static gboolean +real_can_interrupt_activation (NMDevice *dev) +{ + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev); + gboolean interrupt = FALSE; + + /* Devices that support carrier detect can interrupt activation + * if the link becomes inactive. + */ + if (nm_device_get_capabilities (dev) & NM_DEVICE_CAP_CARRIER_DETECT) { + if (NM_DEVICE_ETHERNET_GET_PRIVATE (self)->carrier == FALSE) + interrupt = TRUE; + } + return interrupt; +} + +static gboolean +real_is_available (NMDevice *dev) +{ + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev); + + /* Can't do anything if there isn't a carrier */ + if (!NM_DEVICE_ETHERNET_GET_PRIVATE (self)->carrier) + return FALSE; + + return TRUE; +} + +static NMConnection * +real_get_best_auto_connection (NMDevice *dev, + GSList *connections, + char **specific_object) +{ + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + GSList *iter; + + for (iter = connections; iter; iter = g_slist_next (iter)) { + NMConnection *connection = NM_CONNECTION (iter->data); + NMSettingConnection *s_con; + NMSettingWired *s_wired; + const char *connection_type; + gboolean is_pppoe = FALSE; + + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + + connection_type = nm_setting_connection_get_connection_type (s_con); + if (!strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME)) + is_pppoe = TRUE; + + if (!is_pppoe && strcmp (connection_type, NM_SETTING_WIRED_SETTING_NAME)) + continue; + if (!nm_setting_connection_get_autoconnect (s_con)) + continue; + + s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED); + /* Wired setting optional for PPPoE */ + if (!is_pppoe && !s_wired) + continue; + + if (s_wired) { + const GByteArray *mac; + + mac = nm_setting_wired_get_mac_address (s_wired); + if (mac && memcmp (mac->data, priv->hw_addr.ether_addr_octet, ETH_ALEN)) + continue; + } + + return connection; + } + return NULL; +} + +static void +real_connection_secrets_updated (NMDevice *dev, + NMConnection *connection, + GSList *updated_settings, + RequestSecretsCaller caller) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (dev); + NMActRequest *req; + gboolean valid = FALSE; + GSList *iter; + + g_return_if_fail (IS_ACTIVATING_STATE (nm_device_get_state (dev))); + + /* PPPoE? */ + if (caller == SECRETS_CALLER_PPP) { + NMSettingPPPOE *s_pppoe; + + g_assert (priv->ppp_manager); + + s_pppoe = (NMSettingPPPOE *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE); + if (!s_pppoe) { + nm_ppp_manager_update_secrets (priv->ppp_manager, + nm_device_get_iface (dev), + NULL, + NULL, + "missing PPPoE setting; no secrets could be found."); + } else { + const char *pppoe_username = nm_setting_pppoe_get_username (s_pppoe); + const char *pppoe_password = nm_setting_pppoe_get_password (s_pppoe); + + nm_ppp_manager_update_secrets (priv->ppp_manager, + nm_device_get_iface (dev), + pppoe_username ? pppoe_username : "", + pppoe_password ? pppoe_password : "", + NULL); + } + return; + } + + /* Only caller could be ourselves for 802.1x */ + g_return_if_fail (caller == SECRETS_CALLER_ETHERNET); + g_return_if_fail (nm_device_get_state (dev) == NM_DEVICE_STATE_NEED_AUTH); + + for (iter = updated_settings; iter; iter = g_slist_next (iter)) { + const char *setting_name = (const char *) iter->data; + + if (!strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME)) { + valid = TRUE; + } else { + nm_log_warn (LOGD_DEVICE, "Ignoring updated secrets for setting '%s'.", + setting_name); + } + } + + req = nm_device_get_act_request (dev); + g_assert (req); + + g_return_if_fail (nm_act_request_get_connection (req) == connection); + nm_device_activate_schedule_stage1_device_prepare (dev); +} + +/* FIXME: Move it to nm-device.c and then get rid of all foo_device_get_setting() all around. + It's here now to keep the patch short. */ +static NMSetting * +device_get_setting (NMDevice *device, GType setting_type) +{ + NMActRequest *req; + NMSetting *setting = NULL; + + req = nm_device_get_act_request (device); + if (req) { + NMConnection *connection; + + connection = nm_act_request_get_connection (req); + if (connection) + setting = nm_connection_get_setting (connection, setting_type); + } + + return setting; +} + +/*****************************************************************************/ +/* 802.1X */ + +static void +remove_supplicant_timeouts (NMDeviceEthernet *self) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + + if (priv->supplicant.con_timeout_id) { + g_source_remove (priv->supplicant.con_timeout_id); + priv->supplicant.con_timeout_id = 0; + } + + if (priv->supplicant_timeout_id) { + g_source_remove (priv->supplicant_timeout_id); + priv->supplicant_timeout_id = 0; + } +} + +static void +finish_supplicant_task (SupplicantStateTask *task, gboolean remove_source) +{ + NMDeviceEthernet *self = task->self; + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + + /* idle/timeout handlers should pass FALSE for remove_source, since they + * will tell glib to remove their source from the mainloop by returning + * FALSE when they exit. When called from this NMDevice's dispose handler, + * remove_source should be TRUE to cancel all outstanding idle/timeout + * handlers asynchronously. + */ + if (task->source_id && remove_source) + g_source_remove (task->source_id); + + if (task->mgr_task) + priv->supplicant.mgr_tasks = g_slist_remove (priv->supplicant.mgr_tasks, task); + else + priv->supplicant.iface_tasks = g_slist_remove (priv->supplicant.iface_tasks, task); + + memset (task, 0, sizeof (SupplicantStateTask)); + g_slice_free (SupplicantStateTask, task); +} + +static void +remove_supplicant_interface_error_handler (NMDeviceEthernet *self) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + + if (priv->supplicant.iface_error_id != 0) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_error_id); + priv->supplicant.iface_error_id = 0; + } + + if (priv->supplicant.iface_con_error_cb_id > 0) { + g_source_remove (priv->supplicant.iface_con_error_cb_id); + priv->supplicant.iface_con_error_cb_id = 0; + } +} + +static void +supplicant_interface_release (NMDeviceEthernet *self) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + + remove_supplicant_timeouts (self); + remove_supplicant_interface_error_handler (self); + + /* Clean up all pending supplicant interface state idle tasks */ + while (priv->supplicant.iface_tasks) + finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE); + + if (priv->supplicant.iface_con_state_id) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_con_state_id); + priv->supplicant.iface_con_state_id = 0; + } + + if (priv->supplicant.iface_state_id > 0) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_state_id); + priv->supplicant.iface_state_id = 0; + } + + if (priv->supplicant.mgr_state_id) { + g_signal_handler_disconnect (priv->supplicant.mgr, priv->supplicant.mgr_state_id); + priv->supplicant.mgr_state_id = 0; + } + + if (priv->supplicant.iface) { + nm_supplicant_interface_disconnect (priv->supplicant.iface); + nm_supplicant_manager_release_iface (priv->supplicant.mgr, priv->supplicant.iface); + priv->supplicant.iface = NULL; + } +} + +static gboolean +link_timeout_cb (gpointer user_data) +{ + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + NMDevice *dev = NM_DEVICE (self); + NMActRequest *req; + NMConnection *connection; + const char *setting_name; + + priv->supplicant_timeout_id = 0; + + req = nm_device_get_act_request (dev); + + if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED) { + nm_device_state_changed (dev, NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + return FALSE; + } + + /* Disconnect event during initial authentication and credentials + * ARE checked - we are likely to have wrong key. Ask the user for + * another one. + */ + if (nm_device_get_state (dev) != NM_DEVICE_STATE_CONFIG) + goto time_out; + + connection = nm_act_request_get_connection (req); + nm_connection_clear_secrets (connection); + setting_name = nm_connection_need_secrets (connection, NULL); + if (!setting_name) + goto time_out; + + nm_log_info (LOGD_DEVICE | LOGD_ETHER, + "Activation (%s/wired): disconnected during authentication," + " asking for new key.", + nm_device_get_iface (dev)); + supplicant_interface_release (self); + + nm_device_state_changed (dev, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + nm_act_request_get_secrets (req, + setting_name, + TRUE, + SECRETS_CALLER_ETHERNET, + NULL, + NULL); + + return FALSE; + +time_out: + nm_log_warn (LOGD_DEVICE | LOGD_ETHER, + "(%s): link timed out.", nm_device_get_iface (dev)); + nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + + return FALSE; +} + +static gboolean +schedule_state_handler (NMDeviceEthernet *self, + GSourceFunc handler, + guint32 new_state, + guint32 old_state, + gboolean mgr_task) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + SupplicantStateTask *task; + + if (new_state == old_state) + return TRUE; + + task = g_slice_new0 (SupplicantStateTask); + if (!task) { + nm_log_err (LOGD_DEVICE, "Not enough memory to process supplicant manager state change."); + return FALSE; + } + + task->self = self; + task->new_state = new_state; + task->old_state = old_state; + task->mgr_task = mgr_task; + + task->source_id = g_idle_add (handler, task); + if (mgr_task) + priv->supplicant.mgr_tasks = g_slist_append (priv->supplicant.mgr_tasks, task); + else + priv->supplicant.iface_tasks = g_slist_append (priv->supplicant.iface_tasks, task); + return TRUE; +} + +static gboolean +supplicant_mgr_state_cb_handler (gpointer user_data) +{ + SupplicantStateTask *task = (SupplicantStateTask *) user_data; + NMDevice *device = NM_DEVICE (task->self); + + /* If the supplicant went away, release the supplicant interface */ + if (task->new_state == NM_SUPPLICANT_MANAGER_STATE_DOWN) { + supplicant_interface_release (task->self); + + if (nm_device_get_state (device) > NM_DEVICE_STATE_UNAVAILABLE) { + nm_device_state_changed (device, NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + } + } + + finish_supplicant_task (task, FALSE); + return FALSE; +} + +static void +supplicant_mgr_state_cb (NMSupplicantInterface * iface, + guint32 new_state, + guint32 old_state, + gpointer user_data) +{ + nm_log_info (LOGD_DEVICE | LOGD_ETHER, + "(%s): supplicant manager state: %s -> %s", + nm_device_get_iface (NM_DEVICE (user_data)), + nm_supplicant_manager_state_to_string (old_state), + nm_supplicant_manager_state_to_string (new_state)); + + schedule_state_handler (NM_DEVICE_ETHERNET (user_data), + supplicant_mgr_state_cb_handler, + new_state, + old_state, + TRUE); +} + +static NMSupplicantConfig * +build_supplicant_config (NMDeviceEthernet *self) +{ + const char *con_path; + NMSupplicantConfig *config = NULL; + NMSetting8021x *security; + NMConnection *connection; + + connection = nm_act_request_get_connection (nm_device_get_act_request (NM_DEVICE (self))); + g_return_val_if_fail (connection, NULL); + con_path = nm_connection_get_path (connection); + + config = nm_supplicant_config_new (); + if (!config) + return NULL; + + security = NM_SETTING_802_1X (nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X)); + if (!nm_supplicant_config_add_setting_8021x (config, security, con_path, TRUE)) { + nm_log_warn (LOGD_DEVICE, "Couldn't add 802.1X security setting to supplicant config."); + g_object_unref (config); + config = NULL; + } + + return config; +} + +static gboolean +supplicant_iface_state_cb_handler (gpointer user_data) +{ + SupplicantStateTask *task = (SupplicantStateTask *) user_data; + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (task->self); + NMDevice *device = NM_DEVICE (task->self); + + if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_READY) { + NMSupplicantConfig *config; + const char *iface; + gboolean success = FALSE; + + iface = nm_device_get_iface (device); + config = build_supplicant_config (task->self); + if (config) { + success = nm_supplicant_interface_set_config (priv->supplicant.iface, config); + g_object_unref (config); + + if (!success) { + nm_log_err (LOGD_DEVICE | LOGD_ETHER, + "Activation (%s/wired): couldn't send security " + "configuration to the supplicant.", + iface); + } + } else { + nm_log_warn (LOGD_DEVICE | LOGD_ETHER, + "Activation (%s/wired): couldn't build security configuration.", + iface); + } + + if (!success) + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED); + } else if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) { + NMDeviceState state = nm_device_get_state (device); + + supplicant_interface_release (task->self); + + if (nm_device_is_activating (device) || state == NM_DEVICE_STATE_ACTIVATED) + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + } + + finish_supplicant_task (task, FALSE); + return FALSE; +} + +static void +supplicant_iface_state_cb (NMSupplicantInterface * iface, + guint32 new_state, + guint32 old_state, + gpointer user_data) +{ + + nm_log_info (LOGD_DEVICE | LOGD_ETHER, + "(%s): supplicant interface state: %s -> %s", + nm_device_get_iface (NM_DEVICE (user_data)), + nm_supplicant_interface_state_to_string (old_state), + nm_supplicant_interface_state_to_string (new_state)); + + schedule_state_handler (NM_DEVICE_ETHERNET (user_data), + supplicant_iface_state_cb_handler, + new_state, + old_state, + FALSE); +} + +static gboolean +supplicant_iface_connection_state_cb_handler (gpointer user_data) +{ + SupplicantStateTask *task = (SupplicantStateTask *) user_data; + NMDevice *dev = NM_DEVICE (task->self); + + if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED) { + remove_supplicant_interface_error_handler (task->self); + remove_supplicant_timeouts (task->self); + + /* If this is the initial association during device activation, + * schedule the next activation stage. + */ + if (nm_device_get_state (dev) == NM_DEVICE_STATE_CONFIG) { + nm_log_info (LOGD_DEVICE | LOGD_ETHER, + "Activation (%s/wired) Stage 2 of 5 (Device Configure) successful.", + nm_device_get_iface (dev)); + nm_device_activate_schedule_stage3_ip_config_start (dev); + } + } else if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED) { + if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED || nm_device_is_activating (dev)) { + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (task->self); + + /* Start the link timeout so we allow some time for reauthentication */ + if (!priv->supplicant_timeout_id) + priv->supplicant_timeout_id = g_timeout_add_seconds (15, link_timeout_cb, dev); + } + } + + finish_supplicant_task (task, FALSE); + return FALSE; +} + +static void +supplicant_iface_connection_state_cb (NMSupplicantInterface * iface, + guint32 new_state, + guint32 old_state, + gpointer user_data) +{ + nm_log_info (LOGD_DEVICE | LOGD_ETHER, + "(%s) supplicant connection state: %s -> %s", + nm_device_get_iface (NM_DEVICE (user_data)), + nm_supplicant_interface_connection_state_to_string (old_state), + nm_supplicant_interface_connection_state_to_string (new_state)); + + schedule_state_handler (NM_DEVICE_ETHERNET (user_data), + supplicant_iface_connection_state_cb_handler, + new_state, + old_state, + FALSE); +} + +static gboolean +supplicant_iface_connection_error_cb_handler (gpointer user_data) +{ + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + + supplicant_interface_release (self); + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED); + + priv->supplicant.iface_con_error_cb_id = 0; + return FALSE; +} + +static void +supplicant_iface_connection_error_cb (NMSupplicantInterface *iface, + const char *name, + const char *message, + gpointer user_data) +{ + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + guint id; + + nm_log_warn (LOGD_DEVICE | LOGD_ETHER, + "Activation (%s/wired): association request to the supplicant failed: %s - %s", + nm_device_get_iface (NM_DEVICE (self)), name, message); + + if (priv->supplicant.iface_con_error_cb_id) + g_source_remove (priv->supplicant.iface_con_error_cb_id); + + id = g_idle_add (supplicant_iface_connection_error_cb_handler, self); + priv->supplicant.iface_con_error_cb_id = id; +} + +static NMActStageReturn +handle_auth_or_fail (NMDeviceEthernet *self, + NMActRequest *req, + gboolean new_secrets) +{ + const char *setting_name; + guint32 tries; + NMConnection *connection; + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), WIRED_SECRETS_TRIES)); + if (tries > 3) + return NM_ACT_STAGE_RETURN_FAILURE; + + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); + + nm_connection_clear_secrets (connection); + setting_name = nm_connection_need_secrets (connection, NULL); + if (setting_name) { + gboolean get_new; + + /* If the caller doesn't necessarily want completely new secrets, + * only ask for new secrets after the first failure. + */ + get_new = new_secrets ? TRUE : (tries ? TRUE : FALSE); + nm_act_request_get_secrets (req, + setting_name, + get_new, + SECRETS_CALLER_ETHERNET, + NULL, + NULL); + + g_object_set_data (G_OBJECT (connection), WIRED_SECRETS_TRIES, GUINT_TO_POINTER (++tries)); + } else { + nm_log_info (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets."); + } + + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +static gboolean +supplicant_connection_timeout_cb (gpointer user_data) +{ + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + NMActRequest *req; + const char *iface; + + priv->supplicant.con_timeout_id = 0; + + iface = nm_device_get_iface (device); + + /* Authentication failed, encryption key is probably bad */ + nm_log_warn (LOGD_DEVICE | LOGD_ETHER, + "Activation (%s/wired): association took too long.", iface); + + supplicant_interface_release (self); + req = nm_device_get_act_request (device); + g_assert (req); + + if (handle_auth_or_fail (self, req, TRUE) == NM_ACT_STAGE_RETURN_POSTPONE) { + nm_log_info (LOGD_DEVICE | LOGD_ETHER, + "Activation (%s/wired): asking for new secrets", iface); + } else + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NO_SECRETS); + + return FALSE; +} + +static gboolean +supplicant_interface_init (NMDeviceEthernet *self) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + const char *iface; + + iface = nm_device_get_iface (NM_DEVICE (self)); + + /* Create supplicant interface */ + priv->supplicant.iface = nm_supplicant_manager_get_iface (priv->supplicant.mgr, iface, FALSE); + if (!priv->supplicant.iface) { + nm_log_err (LOGD_DEVICE | LOGD_ETHER, + "Couldn't initialize supplicant interface for %s.", + iface); + supplicant_interface_release (self); + + return FALSE; + } + + /* Listen for it's state signals */ + priv->supplicant.iface_state_id = g_signal_connect (priv->supplicant.iface, + "state", + G_CALLBACK (supplicant_iface_state_cb), + self); + + /* Hook up error signal handler to capture association errors */ + priv->supplicant.iface_error_id = g_signal_connect (priv->supplicant.iface, + "connection-error", + G_CALLBACK (supplicant_iface_connection_error_cb), + self); + + priv->supplicant.iface_con_state_id = g_signal_connect (priv->supplicant.iface, + "connection-state", + G_CALLBACK (supplicant_iface_connection_state_cb), + self); + + /* Listen for supplicant manager state changes */ + priv->supplicant.mgr_state_id = g_signal_connect (priv->supplicant.mgr, + "state", + G_CALLBACK (supplicant_mgr_state_cb), + self); + + /* Set up a timeout on the connection attempt to fail it after 25 seconds */ + priv->supplicant.con_timeout_id = g_timeout_add_seconds (25, supplicant_connection_timeout_cb, self); + + return TRUE; +} + +static NMActStageReturn +nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *reason) +{ + NMConnection *connection; + NMSetting8021x *security; + NMSettingConnection *s_connection; + const char *setting_name; + const char *iface; + NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + + connection = nm_act_request_get_connection (nm_device_get_act_request (NM_DEVICE (self))); + security = NM_SETTING_802_1X (nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X)); + if (!security) { + nm_log_err (LOGD_DEVICE, "Invalid or missing 802.1X security"); + *reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED; + return ret; + } + + iface = nm_device_get_iface (NM_DEVICE (self)); + s_connection = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + + /* If we need secrets, get them */ + setting_name = nm_connection_need_secrets (connection, NULL); + if (setting_name) { + NMActRequest *req = nm_device_get_act_request (NM_DEVICE (self)); + + nm_log_info (LOGD_DEVICE | LOGD_ETHER, + "Activation (%s/wired): connection '%s' has security, but secrets are required.", + iface, nm_setting_connection_get_id (s_connection)); + + ret = handle_auth_or_fail (self, req, FALSE); + if (ret != NM_ACT_STAGE_RETURN_POSTPONE) + *reason = NM_DEVICE_STATE_REASON_NO_SECRETS; + } else { + nm_log_info (LOGD_DEVICE | LOGD_ETHER, + "Activation (%s/wired): connection '%s' requires no security. No secrets needed.", + iface, nm_setting_connection_get_id (s_connection)); + + if (supplicant_interface_init (self)) + ret = NM_ACT_STAGE_RETURN_POSTPONE; + else + *reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED; + } + + return ret; +} + +/*****************************************************************************/ +/* PPPoE */ + +static void +ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + + switch (status) { + case NM_PPP_STATUS_DISCONNECT: + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_DISCONNECT); + break; + case NM_PPP_STATUS_DEAD: + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_FAILED); + break; + default: + break; + } +} + +static void +ppp_ip4_config (NMPPPManager *ppp_manager, + const char *iface, + NMIP4Config *config, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + + /* Ignore PPP IP4 events that come in after initial configuration */ + if (nm_device_get_state (device) != NM_DEVICE_STATE_IP_CONFIG) + return; + + nm_device_set_ip_iface (device, iface); + NM_DEVICE_ETHERNET_GET_PRIVATE (device)->pending_ip4_config = g_object_ref (config); + nm_device_activate_schedule_stage4_ip4_config_get (device); +} + +static NMActStageReturn +pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *reason) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + NMConnection *connection; + NMSettingPPPOE *s_pppoe; + NMActRequest *req; + GError *err = NULL; + NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + + req = nm_device_get_act_request (NM_DEVICE (self)); + g_assert (req); + + connection = nm_act_request_get_connection (req); + g_assert (req); + + s_pppoe = (NMSettingPPPOE *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE); + g_assert (s_pppoe); + + priv->ppp_manager = nm_ppp_manager_new (nm_device_get_iface (NM_DEVICE (self))); + if (nm_ppp_manager_start (priv->ppp_manager, req, nm_setting_pppoe_get_username (s_pppoe), 30, &err)) { + g_signal_connect (priv->ppp_manager, "state-changed", + G_CALLBACK (ppp_state_changed), + self); + g_signal_connect (priv->ppp_manager, "ip4-config", + G_CALLBACK (ppp_ip4_config), + self); + ret = NM_ACT_STAGE_RETURN_POSTPONE; + } else { + nm_log_warn (LOGD_DEVICE, "(%s): PPPoE failed to start: %s", + nm_device_get_iface (NM_DEVICE (self)), err->message); + g_error_free (err); + + g_object_unref (priv->ppp_manager); + priv->ppp_manager = NULL; + + *reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED; + } + + return ret; +} + +static NMActStageReturn +real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason) +{ + NMSettingConnection *s_con; + const char *connection_type; + NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; + + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + s_con = NM_SETTING_CONNECTION (device_get_setting (device, NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + + /* 802.1x has to run before any IP configuration since the 802.1x auth + * process opens the port up for normal traffic. + */ + connection_type = nm_setting_connection_get_connection_type (s_con); + if (!strcmp (connection_type, NM_SETTING_WIRED_SETTING_NAME)) { + NMSetting8021x *security; + + security = (NMSetting8021x *) device_get_setting (device, NM_TYPE_SETTING_802_1X); + if (security) + ret = nm_8021x_stage2_config (NM_DEVICE_ETHERNET (device), reason); + } + + return ret; +} + +static NMActStageReturn +real_act_stage3_ip4_config_start (NMDevice *device, NMDeviceStateReason *reason) +{ + NMSettingConnection *s_con; + const char *connection_type; + + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + s_con = NM_SETTING_CONNECTION (device_get_setting (device, NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + + connection_type = nm_setting_connection_get_connection_type (s_con); + if (!strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME)) + return pppoe_stage3_ip4_config_start (NM_DEVICE_ETHERNET (device), reason); + + return NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->act_stage3_ip4_config_start (device, reason); +} + +static NMActStageReturn +real_act_stage4_get_ip4_config (NMDevice *device, + NMIP4Config **config, + NMDeviceStateReason *reason) +{ + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + NMActStageReturn ret; + + g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + if (!priv->ppp_manager) { + /* Regular ethernet connection. */ + + /* Chain up to parent */ + ret = NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->act_stage4_get_ip4_config (device, config, reason); + + if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { + NMConnection *connection; + NMSettingWired *s_wired; + guint32 mtu; + + connection = nm_act_request_get_connection (nm_device_get_act_request (device)); + g_assert (connection); + s_wired = NM_SETTING_WIRED (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED)); + g_assert (s_wired); + + /* MTU override */ + mtu = nm_setting_wired_get_mtu (s_wired); + if (mtu) + nm_ip4_config_set_mtu (*config, mtu); + } + } else { + NMConnection *connection; + NMSettingIP4Config *s_ip4; + + /* PPPoE */ + *config = priv->pending_ip4_config; + priv->pending_ip4_config = NULL; + + /* Merge user-defined overrides into the IP4Config to be applied */ + connection = nm_act_request_get_connection (nm_device_get_act_request (device)); + g_assert (connection); + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + nm_utils_merge_ip4_config (*config, s_ip4); + + ret = NM_ACT_STAGE_RETURN_SUCCESS; + } + + return ret; +} + +static void +real_deactivate_quickly (NMDevice *device) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device); + + nm_device_set_ip_iface (device, NULL); + + if (priv->pending_ip4_config) { + g_object_unref (priv->pending_ip4_config); + priv->pending_ip4_config = NULL; + } + + if (priv->ppp_manager) { + g_object_unref (priv->ppp_manager); + priv->ppp_manager = NULL; + } + + supplicant_interface_release (NM_DEVICE_ETHERNET (device)); +} + +static gboolean +real_check_connection_compatible (NMDevice *device, + NMConnection *connection, + GError **error) +{ + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + NMSettingConnection *s_con; + NMSettingWired *s_wired; + const char *connection_type; + gboolean is_pppoe = FALSE; + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + + connection_type = nm_setting_connection_get_connection_type (s_con); + if ( strcmp (connection_type, NM_SETTING_WIRED_SETTING_NAME) + && strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME)) { + g_set_error (error, + NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_NOT_WIRED, + "The connection was not a wired or PPPoE connection."); + return FALSE; + } + + if (!strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME)) + is_pppoe = TRUE; + + s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED); + /* Wired setting is optional for PPPoE */ + if (!is_pppoe && !s_wired) { + g_set_error (error, + NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_INVALID, + "The connection was not a valid wired connection."); + return FALSE; + } + + if (s_wired) { + const GByteArray *mac; + + mac = nm_setting_wired_get_mac_address (s_wired); + if (mac && memcmp (mac->data, &(priv->hw_addr.ether_addr_octet), ETH_ALEN)) { + g_set_error (error, + NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_INCOMPATIBLE, + "The connection's MAC address did not match this device."); + return FALSE; + } + } + + // FIXME: check bitrate against device capabilities + + return TRUE; +} + +static gboolean +spec_match_list (NMDevice *device, const GSList *specs) +{ + struct ether_addr ether; + char *hwaddr; + gboolean matched; + + nm_device_ethernet_get_address (NM_DEVICE_ETHERNET (device), ðer); + hwaddr = nm_ether_ntop (ðer); + matched = nm_match_spec_hwaddr (specs, hwaddr); + g_free (hwaddr); + + return matched; +} + +static gboolean +wired_match_config (NMDevice *self, NMConnection *connection) +{ + NMSettingWired *s_wired; + struct ether_addr ether; + const GByteArray *s_ether; + + s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED); + if (!s_wired) + return FALSE; + + /* MAC address check */ + s_ether = nm_setting_wired_get_mac_address (s_wired); + if (s_ether) { + nm_device_ethernet_get_address (NM_DEVICE_ETHERNET (self), ðer); + + if (memcmp (s_ether->data, ether.ether_addr_octet, ETH_ALEN)) + return FALSE; + } + + return TRUE; +} + +typedef struct { + int ifindex; + NMIP4Address *addr; + gboolean found; +} AddrData; + +static void +check_one_address (struct nl_object *object, void *user_data) +{ + AddrData *data = user_data; + struct rtnl_addr *addr = (struct rtnl_addr *) object; + struct nl_addr *local; + struct in_addr tmp; + + if (rtnl_addr_get_ifindex (addr) != data->ifindex) + return; + if (rtnl_addr_get_family (addr) != AF_INET) + return; + + if (nm_ip4_address_get_prefix (data->addr) != rtnl_addr_get_prefixlen (addr)) + return; + + local = rtnl_addr_get_local (addr); + if (nl_addr_get_family (local) != AF_INET) + return; + if (nl_addr_get_len (local) != sizeof (struct in_addr)) + return; + if (!nl_addr_get_binary_addr (local)) + return; + + memcpy (&tmp, nl_addr_get_binary_addr (local), nl_addr_get_len (local)); + if (tmp.s_addr != nm_ip4_address_get_address (data->addr)) + return; + + /* Yay, found it */ + data->found = TRUE; +} + +static gboolean +ip4_match_config (NMDevice *self, NMConnection *connection) +{ + NMSettingIP4Config *s_ip4; + NMSettingConnection *s_con; + struct nl_handle *nlh = NULL; + struct nl_cache *addr_cache = NULL; + int i, num; + GSList *leases, *iter; + NMDHCPManager *dhcp_mgr; + const char *method; + int ifindex; + AddrData check_data; + + ifindex = nm_device_get_ifindex (self); + + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + g_assert (nm_setting_connection_get_uuid (s_con)); + + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + if (!s_ip4) + return FALSE; + + /* Read all the device's IP addresses */ + nlh = nm_netlink_get_default_handle (); + if (!nlh) + return FALSE; + + addr_cache = rtnl_addr_alloc_cache (nlh); + if (!addr_cache) + return FALSE; + nl_cache_mngt_provide (addr_cache); + + /* Get any saved leases that apply to this connection */ + dhcp_mgr = nm_dhcp_manager_get (); + leases = nm_dhcp_manager_get_lease_config (dhcp_mgr, + nm_device_get_iface (self), + nm_setting_connection_get_uuid (s_con)); + g_object_unref (dhcp_mgr); + + method = nm_setting_ip4_config_get_method (s_ip4); + if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { + gboolean found = FALSE; + + /* Find at least one lease's address on the device */ + for (iter = leases; iter; iter = g_slist_next (iter)) { + NMIP4Config *addr = iter->data; + + memset (&check_data, 0, sizeof (check_data)); + check_data.ifindex = ifindex; + check_data.found = FALSE; + check_data.addr = nm_ip4_config_get_address (addr, 0); + + nl_cache_foreach (addr_cache, check_one_address, &check_data); + if (check_data.found) { + found = TRUE; /* Yay, device has same address as a lease */ + break; + } + } + g_slist_foreach (leases, (GFunc) g_object_unref, NULL); + g_slist_free (leases); + return found; + } else { + /* Maybe the connection used to be DHCP and there are stale leases; ignore them */ + g_slist_foreach (leases, (GFunc) g_object_unref, NULL); + g_slist_free (leases); + } + + /* 'shared' and 'link-local' aren't supported methods because 'shared' + * requires too much iptables and dnsmasq state to be reclaimed, and + * avahi-autoipd isn't smart enough to allow the link-local address to be + * determined at any point other than when it was first assigned. + */ + if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) + return FALSE; + + /* Everything below for static addressing */ + + /* Find all IP4 addresses of this connection in the device's address list */ + num = nm_setting_ip4_config_get_num_addresses (s_ip4); + for (i = 0; i < num; i++) { + memset (&check_data, 0, sizeof (check_data)); + check_data.ifindex = ifindex; + check_data.found = FALSE; + check_data.addr = nm_setting_ip4_config_get_address (s_ip4, i); + + nl_cache_foreach (addr_cache, check_one_address, &check_data); + if (!check_data.found) + return FALSE; + } + + /* Success; all the connection's static IP addresses are assigned to the device */ + return TRUE; +} + +static NMConnection * +connection_match_config (NMDevice *self, const GSList *connections) +{ + GSList *iter; + NMSettingConnection *s_con; + + for (iter = (GSList *) connections; iter; iter = g_slist_next (iter)) { + NMConnection *candidate = NM_CONNECTION (iter->data); + + s_con = (NMSettingConnection *) nm_connection_get_setting (candidate, NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_WIRED_SETTING_NAME)) + continue; + + /* Can't assume 802.1x or PPPoE connections; they have too much state + * that's impossible to get on-the-fly from PPPoE or the supplicant. + */ + if ( nm_connection_get_setting (candidate, NM_TYPE_SETTING_802_1X) + || nm_connection_get_setting (candidate, NM_TYPE_SETTING_PPPOE)) + continue; + + if (!wired_match_config (self, candidate)) + continue; + + if (!ip4_match_config (self, candidate)) + continue; + + return candidate; + } + + return NULL; +} + +static void +dispose (GObject *object) +{ + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (object); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + + if (priv->disposed) { + G_OBJECT_CLASS (nm_device_ethernet_parent_class)->dispose (object); + return; + } + + priv->disposed = TRUE; + + /* Clean up all pending supplicant tasks */ + while (priv->supplicant.iface_tasks) + finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE); + while (priv->supplicant.mgr_tasks) + finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.mgr_tasks->data, TRUE); + + if (priv->link_connected_id) { + g_signal_handler_disconnect (priv->monitor, priv->link_connected_id); + priv->link_connected_id = 0; + } + if (priv->link_disconnected_id) { + g_signal_handler_disconnect (priv->monitor, priv->link_disconnected_id); + priv->link_disconnected_id = 0; + } + + carrier_action_defer_clear (self); + + if (priv->monitor) { + g_object_unref (priv->monitor); + priv->monitor = NULL; + } + + G_OBJECT_CLASS (nm_device_ethernet_parent_class)->dispose (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (object); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + struct ether_addr hw_addr; + + switch (prop_id) { + case PROP_HW_ADDRESS: + nm_device_ethernet_get_address (self, &hw_addr); + g_value_take_string (value, nm_ether_ntop (&hw_addr)); + break; + case PROP_SPEED: + g_value_set_uint (value, nm_device_ethernet_get_speed (self)); + break; + case PROP_CARRIER: + g_value_set_boolean (value, priv->carrier); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_device_ethernet_class_init (NMDeviceEthernetClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMDeviceEthernetPrivate)); + + /* virtual methods */ + object_class->constructor = constructor; + object_class->dispose = dispose; + object_class->get_property = get_property; + object_class->set_property = set_property; + + parent_class->get_generic_capabilities = real_get_generic_capabilities; + parent_class->hw_is_up = real_hw_is_up; + parent_class->hw_bring_up = real_hw_bring_up; + parent_class->hw_take_down = real_hw_take_down; + parent_class->is_up = real_is_up; + parent_class->bring_up = real_bring_up; + parent_class->take_down = real_take_down; + parent_class->can_interrupt_activation = real_can_interrupt_activation; + parent_class->update_hw_address = real_update_hw_address; + parent_class->get_best_auto_connection = real_get_best_auto_connection; + parent_class->is_available = real_is_available; + parent_class->connection_secrets_updated = real_connection_secrets_updated; + parent_class->check_connection_compatible = real_check_connection_compatible; + + parent_class->act_stage2_config = real_act_stage2_config; + parent_class->act_stage3_ip4_config_start = real_act_stage3_ip4_config_start; + parent_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config; + parent_class->deactivate_quickly = real_deactivate_quickly; + parent_class->spec_match_list = spec_match_list; + parent_class->connection_match_config = connection_match_config; + + /* properties */ + g_object_class_install_property + (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_DEVICE_ETHERNET_HW_ADDRESS, + "MAC Address", + "Hardware MAC address", + NULL, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_SPEED, + g_param_spec_uint (NM_DEVICE_ETHERNET_SPEED, + "Speed", + "Speed", + 0, G_MAXUINT32, 0, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_CARRIER, + g_param_spec_boolean (NM_DEVICE_ETHERNET_CARRIER, + "Carrier", + "Carrier", + FALSE, + G_PARAM_READABLE)); + + /* Signals */ + signals[PROPERTIES_CHANGED] = + nm_properties_changed_signal_new (object_class, + G_STRUCT_OFFSET (NMDeviceEthernetClass, properties_changed)); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), + &dbus_glib_nm_device_ethernet_object_info); + + dbus_g_error_domain_register (NM_ETHERNET_ERROR, NULL, NM_TYPE_ETHERNET_ERROR); +} + + +/**************************************/ +/* Ethtool capability detection */ +/**************************************/ + +static gboolean +supports_ethtool_carrier_detect (NMDeviceEthernet *self) +{ + int fd; + struct ifreq ifr; + gboolean supports_ethtool = FALSE; + struct ethtool_cmd edata; + + g_return_val_if_fail (self != NULL, FALSE); + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_err (LOGD_HW, "couldn't open control socket."); + return FALSE; + } + + memset (&ifr, 0, sizeof (struct ifreq)); + strncpy (ifr.ifr_name, nm_device_get_iface (NM_DEVICE (self)), IFNAMSIZ); + + edata.cmd = ETHTOOL_GLINK; + ifr.ifr_data = (char *) &edata; + + errno = 0; + if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) { + nm_log_dbg (LOGD_HW | LOGD_ETHER, "SIOCETHTOOL failed: %d", errno); + goto out; + } + + supports_ethtool = TRUE; + +out: + close (fd); + nm_log_dbg (LOGD_HW | LOGD_ETHER, "ethtool %s supported", + supports_ethtool ? "is" : "not"); + return supports_ethtool; +} + + +/**************************************/ +/* MII capability detection */ +/**************************************/ +#define _LINUX_IF_H +#include <linux/mii.h> +#undef _LINUX_IF_H + +static int +mdio_read (NMDeviceEthernet *self, int fd, struct ifreq *ifr, int location) +{ + struct mii_ioctl_data *mii; + int val = -1; + + g_return_val_if_fail (fd >= 0, -1); + g_return_val_if_fail (ifr != NULL, -1); + + mii = (struct mii_ioctl_data *) &ifr->ifr_ifru; + mii->reg_num = location; + + errno = 0; + if (ioctl (fd, SIOCGMIIREG, ifr) == 0) { + nm_log_dbg (LOGD_HW | LOGD_ETHER, "SIOCGMIIREG result 0x%X", mii->val_out); + val = mii->val_out; + } else { + nm_log_dbg (LOGD_HW | LOGD_ETHER, "SIOCGMIIREG failed: %d", errno); + } + + return val; +} + +static gboolean +supports_mii_carrier_detect (NMDeviceEthernet *self) +{ + int fd, bmsr; + struct ifreq ifr; + gboolean supports_mii = FALSE; + + g_return_val_if_fail (self != NULL, FALSE); + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_err (LOGD_HW, "couldn't open control socket."); + return 0; + } + + memset (&ifr, 0, sizeof (struct ifreq)); + strncpy (ifr.ifr_name, nm_device_get_iface (NM_DEVICE (self)), IFNAMSIZ); + + errno = 0; + if (ioctl (fd, SIOCGMIIPHY, &ifr) < 0) { + nm_log_dbg (LOGD_HW | LOGD_ETHER, "SIOCGMIIPHY failed: %d", errno); + goto out; + } + + /* If we can read the BMSR register, we assume that the card supports MII link detection */ + bmsr = mdio_read (self, fd, &ifr, MII_BMSR); + supports_mii = (bmsr != -1) ? TRUE : FALSE; + nm_log_dbg (LOGD_HW | LOGD_ETHER, "MII %s supported", + supports_mii ? "is" : "not"); + +out: + close (fd); + return supports_mii; +} diff --git a/src/nm-device-ethernet.h b/src/nm-device-ethernet.h new file mode 100644 index 00000000..7bb3db00 --- /dev/null +++ b/src/nm-device-ethernet.h @@ -0,0 +1,67 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef NM_DEVICE_ETHERNET_H +#define NM_DEVICE_ETHERNET_H + +#include <glib-object.h> +#include <net/ethernet.h> + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_ETHERNET (nm_device_ethernet_get_type ()) +#define NM_DEVICE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernet)) +#define NM_DEVICE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass)) +#define NM_IS_DEVICE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_ETHERNET)) +#define NM_IS_DEVICE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_ETHERNET)) +#define NM_DEVICE_ETHERNET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass)) + +#define NM_DEVICE_ETHERNET_HW_ADDRESS "hw-address" +#define NM_DEVICE_ETHERNET_SPEED "speed" +#define NM_DEVICE_ETHERNET_CARRIER "carrier" + +typedef struct { + NMDevice parent; +} NMDeviceEthernet; + +typedef struct { + NMDeviceClass parent; + + /* Signals */ + void (*properties_changed) (NMDeviceEthernet *device, GHashTable *properties); +} NMDeviceEthernetClass; + + +GType nm_device_ethernet_get_type (void); + + +NMDevice *nm_device_ethernet_new (const char *udi, + const char *iface, + const char *driver); + +void nm_device_ethernet_get_address (NMDeviceEthernet *dev, + struct ether_addr *addr); + +G_END_DECLS + +#endif /* NM_DEVICE_ETHERNET_H */ diff --git a/src/nm-device-gsm.c b/src/nm-device-gsm.c new file mode 100644 index 00000000..2a98a41f --- /dev/null +++ b/src/nm-device-gsm.c @@ -0,0 +1,75 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#include <string.h> + +#include "nm-modem-gsm.h" +#include "nm-device-interface.h" +#include "nm-device-gsm.h" +#include "nm-properties-changed-signal.h" +#include "nm-rfkill.h" + +#include "nm-device-gsm-glue.h" + +G_DEFINE_TYPE (NMDeviceGsm, nm_device_gsm, NM_TYPE_DEVICE_MODEM) + +enum { + PROPERTIES_CHANGED, + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + +NMDevice * +nm_device_gsm_new (NMModemGsm *modem, const char *driver) +{ + g_return_val_if_fail (modem != NULL, NULL); + g_return_val_if_fail (NM_IS_MODEM_GSM (modem), NULL); + g_return_val_if_fail (driver != NULL, NULL); + + return (NMDevice *) g_object_new (NM_TYPE_DEVICE_GSM, + NM_DEVICE_INTERFACE_UDI, nm_modem_get_path (NM_MODEM (modem)), + NM_DEVICE_INTERFACE_IFACE, nm_modem_get_iface (NM_MODEM (modem)), + NM_DEVICE_INTERFACE_DRIVER, driver, + NM_DEVICE_INTERFACE_TYPE_DESC, "GSM", + NM_DEVICE_INTERFACE_DEVICE_TYPE, NM_DEVICE_TYPE_GSM, + NM_DEVICE_INTERFACE_RFKILL_TYPE, RFKILL_TYPE_WWAN, + NM_DEVICE_MODEM_MODEM, modem, + NULL); +} + +static void +nm_device_gsm_init (NMDeviceGsm *self) +{ +} + +static void +nm_device_gsm_class_init (NMDeviceGsmClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + /* Signals */ + signals[PROPERTIES_CHANGED] = + nm_properties_changed_signal_new (object_class, + G_STRUCT_OFFSET (NMDeviceGsmClass, properties_changed)); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), + &dbus_glib_nm_device_gsm_object_info); +} + diff --git a/src/nm-device-gsm.h b/src/nm-device-gsm.h new file mode 100644 index 00000000..9b403c63 --- /dev/null +++ b/src/nm-device-gsm.h @@ -0,0 +1,55 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_GSM_H +#define NM_DEVICE_GSM_H + +#include "nm-device-modem.h" +#include "nm-modem-gsm.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_GSM (nm_device_gsm_get_type ()) +#define NM_DEVICE_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_GSM, NMDeviceGsm)) +#define NM_DEVICE_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_GSM, NMDeviceGsmClass)) +#define NM_IS_DEVICE_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_GSM)) +#define NM_IS_DEVICE_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_GSM)) +#define NM_DEVICE_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_GSM, NMDeviceGsmClass)) + +typedef struct { + NMDeviceModem parent; +} NMDeviceGsm; + +typedef struct { + NMDeviceModemClass parent; + + /* Signals */ + void (*signal_quality) (NMDeviceGsm *self, guint32 quality); + + void (*properties_changed) (NMDeviceGsm *self, GHashTable *properties); +} NMDeviceGsmClass; + +GType nm_device_gsm_get_type (void); + +NMDevice *nm_device_gsm_new (NMModemGsm *modem, const char *driver); + +G_END_DECLS + +#endif /* NM_DEVICE_GSM_H */ diff --git a/src/nm-device-interface.c b/src/nm-device-interface.c new file mode 100644 index 00000000..85f58d55 --- /dev/null +++ b/src/nm-device-interface.c @@ -0,0 +1,402 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2010 Red Hat, Inc. + */ + +#include "nm-marshal.h" +#include "nm-setting-connection.h" +#include "nm-device-interface.h" +#include "nm-logging.h" +#include "nm-properties-changed-signal.h" +#include "nm-rfkill.h" + +static gboolean impl_device_disconnect (NMDeviceInterface *device, + GError **error); + +#include "nm-device-interface-glue.h" + +GQuark +nm_device_interface_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm-device-interface-error"); + return quark; +} + +/* This should really be standard. */ +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GType +nm_device_interface_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + /* Connection is already activating. */ + ENUM_ENTRY (NM_DEVICE_INTERFACE_ERROR_CONNECTION_ACTIVATING, "ConnectionActivating"), + /* Connection is invalid for this device. */ + ENUM_ENTRY (NM_DEVICE_INTERFACE_ERROR_CONNECTION_INVALID, "ConnectionInvalid"), + /* Operation could not be performed because the device is not active. */ + ENUM_ENTRY (NM_DEVICE_INTERFACE_ERROR_NOT_ACTIVE, "NotActive"), + { 0, 0, 0 } + }; + etype = g_enum_register_static ("NMDeviceInterfaceError", values); + } + return etype; +} + + +static void +nm_device_interface_init (gpointer g_iface) +{ + GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); + static gboolean initialized = FALSE; + + if (initialized) + return; + + /* Properties */ + g_object_interface_install_property + (g_iface, + g_param_spec_string (NM_DEVICE_INTERFACE_UDI, + "UDI", + "Unique Device Identifier", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_interface_install_property + (g_iface, + g_param_spec_string (NM_DEVICE_INTERFACE_IFACE, + "Interface", + "Interface", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_object_interface_install_property + (g_iface, + g_param_spec_string (NM_DEVICE_INTERFACE_DRIVER, + "Driver", + "Driver", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_interface_install_property + (g_iface, + g_param_spec_uint (NM_DEVICE_INTERFACE_CAPABILITIES, + "Capabilities", + "Capabilities", + 0, G_MAXUINT32, NM_DEVICE_CAP_NONE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_interface_install_property + (g_iface, + g_param_spec_uint (NM_DEVICE_INTERFACE_IP4_ADDRESS, + "IP4 address", + "IP4 address", + 0, G_MAXUINT32, 0, /* FIXME */ + G_PARAM_READWRITE)); + + g_object_interface_install_property + (g_iface, + g_param_spec_boxed (NM_DEVICE_INTERFACE_IP4_CONFIG, + "IP4 Config", + "IP4 Config", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READWRITE)); + + g_object_interface_install_property + (g_iface, + g_param_spec_boxed (NM_DEVICE_INTERFACE_DHCP4_CONFIG, + "DHCP4 Config", + "DHCP4 Config", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READWRITE)); + + g_object_interface_install_property + (g_iface, + g_param_spec_boxed (NM_DEVICE_INTERFACE_IP6_CONFIG, + "IP6 Config", + "IP6 Config", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READWRITE)); + + g_object_interface_install_property + (g_iface, + g_param_spec_boxed (NM_DEVICE_INTERFACE_DHCP6_CONFIG, + "DHCP6 Config", + "DHCP6 Config", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READWRITE)); + + g_object_interface_install_property + (g_iface, + g_param_spec_uint (NM_DEVICE_INTERFACE_STATE, + "State", + "State", + 0, G_MAXUINT32, NM_DEVICE_STATE_UNKNOWN, + G_PARAM_READABLE)); + + g_object_interface_install_property + (g_iface, + g_param_spec_uint (NM_DEVICE_INTERFACE_DEVICE_TYPE, + "DeviceType", + "DeviceType", + 0, G_MAXUINT32, NM_DEVICE_TYPE_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); + + g_object_interface_install_property + (g_iface, g_param_spec_boolean (NM_DEVICE_INTERFACE_MANAGED, + "Managed", + "Managed", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_interface_install_property + (g_iface, + g_param_spec_string (NM_DEVICE_INTERFACE_TYPE_DESC, + "Type Description", + "Device type description", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); + + g_object_interface_install_property + (g_iface, g_param_spec_uint (NM_DEVICE_INTERFACE_RFKILL_TYPE, + "Rfkill Type", + "Type of rfkill switch (if any) supported by this device", + RFKILL_TYPE_WLAN, + RFKILL_TYPE_MAX, + RFKILL_TYPE_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); + + g_object_interface_install_property + (g_iface, + g_param_spec_int (NM_DEVICE_INTERFACE_IFINDEX, + "Ifindex", + "Ifindex", + 0, G_MAXINT, 0, + G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT)); + + /* Signals */ + g_signal_new ("state-changed", + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDeviceInterface, state_changed), + NULL, NULL, + _nm_marshal_VOID__UINT_UINT_UINT, + G_TYPE_NONE, 3, + G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); + + dbus_g_object_type_install_info (iface_type, + &dbus_glib_nm_device_interface_object_info); + + dbus_g_error_domain_register (NM_DEVICE_INTERFACE_ERROR, + NULL, + NM_TYPE_DEVICE_INTERFACE_ERROR); + + initialized = TRUE; +} + + +GType +nm_device_interface_get_type (void) +{ + static GType device_interface_type = 0; + + if (!device_interface_type) { + const GTypeInfo device_interface_info = { + sizeof (NMDeviceInterface), /* class_size */ + nm_device_interface_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + device_interface_type = g_type_register_static (G_TYPE_INTERFACE, + "NMDeviceInterface", + &device_interface_info, 0); + + g_type_interface_add_prerequisite (device_interface_type, G_TYPE_OBJECT); + } + + return device_interface_type; +} + +/* FIXME: This should be public and nm_device_get_iface() should be removed. */ +static char * +nm_device_interface_get_iface (NMDeviceInterface *device) +{ + char *iface = NULL; + + g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), NULL); + + g_object_get (device, NM_DEVICE_INTERFACE_IFACE, &iface, NULL); + + return iface; +} + +gboolean +nm_device_interface_check_connection_compatible (NMDeviceInterface *device, + NMConnection *connection, + GError **error) +{ + g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + g_return_val_if_fail (error != NULL, FALSE); + g_return_val_if_fail (*error == NULL, FALSE); + + if (NM_DEVICE_INTERFACE_GET_INTERFACE (device)->check_connection_compatible) + return NM_DEVICE_INTERFACE_GET_INTERFACE (device)->check_connection_compatible (device, connection, error); + return TRUE; +} + +gboolean +nm_device_interface_activate (NMDeviceInterface *device, + NMActRequest *req, + GError **error) +{ + gboolean success; + NMConnection *connection; + NMSettingConnection *s_con; + char *iface; + + g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), FALSE); + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE); + + connection = nm_act_request_get_connection (req); + g_assert (connection); + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + + iface = nm_device_interface_get_iface (device); + nm_log_info (LOGD_DEVICE, "Activation (%s) starting connection '%s'", iface, + nm_setting_connection_get_id (s_con)); + g_free (iface); + + success = NM_DEVICE_INTERFACE_GET_INTERFACE (device)->activate (device, req, error); + if (!success) + g_assert (*error); + + return success; +} + +gboolean +nm_device_interface_disconnect (NMDeviceInterface *device, + GError **error) +{ + gboolean success = FALSE; + + g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), FALSE); + + switch (nm_device_interface_get_state (device)) { + case NM_DEVICE_STATE_UNKNOWN: + case NM_DEVICE_STATE_UNMANAGED: + case NM_DEVICE_STATE_UNAVAILABLE: + case NM_DEVICE_STATE_DISCONNECTED: + g_set_error_literal (error, + NM_DEVICE_INTERFACE_ERROR, + NM_DEVICE_INTERFACE_ERROR_NOT_ACTIVE, + "Cannot disconnect an inactive device."); + break; + default: + success = NM_DEVICE_INTERFACE_GET_INTERFACE (device)->disconnect (device, error); + break; + } + + return success; +} + +static gboolean +impl_device_disconnect (NMDeviceInterface *device, + GError **error) +{ + return nm_device_interface_disconnect (device, error); +} + +void +nm_device_interface_deactivate (NMDeviceInterface *device, NMDeviceStateReason reason) +{ + g_return_if_fail (NM_IS_DEVICE_INTERFACE (device)); + + NM_DEVICE_INTERFACE_GET_INTERFACE (device)->deactivate (device, reason); +} + +NMDeviceState +nm_device_interface_get_state (NMDeviceInterface *device) +{ + NMDeviceState state; + + g_object_get (G_OBJECT (device), "state", &state, NULL); + return state; +} + +gboolean +nm_device_interface_spec_match_list (NMDeviceInterface *device, + const GSList *specs) +{ + g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), FALSE); + + if (NM_DEVICE_INTERFACE_GET_INTERFACE (device)->spec_match_list) + return NM_DEVICE_INTERFACE_GET_INTERFACE (device)->spec_match_list (device, specs); + return FALSE; +} + +NMConnection * +nm_device_interface_connection_match_config (NMDeviceInterface *device, + const GSList *connections) +{ + g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), NULL); + + if (NM_DEVICE_INTERFACE_GET_INTERFACE (device)->connection_match_config) + return NM_DEVICE_INTERFACE_GET_INTERFACE (device)->connection_match_config (device, connections); + return NULL; +} + +gboolean +nm_device_interface_can_assume_connection (NMDeviceInterface *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), FALSE); + + return !!NM_DEVICE_INTERFACE_GET_INTERFACE (device)->connection_match_config; +} + +void +nm_device_interface_set_enabled (NMDeviceInterface *device, gboolean enabled) +{ + g_return_if_fail (NM_IS_DEVICE_INTERFACE (device)); + + if (NM_DEVICE_INTERFACE_GET_INTERFACE (device)->set_enabled) + NM_DEVICE_INTERFACE_GET_INTERFACE (device)->set_enabled (device, enabled); +} + +gboolean +nm_device_interface_get_enabled (NMDeviceInterface *device) +{ + g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), FALSE); + + if (NM_DEVICE_INTERFACE_GET_INTERFACE (device)->get_enabled) + return NM_DEVICE_INTERFACE_GET_INTERFACE (device)->get_enabled (device); + return TRUE; +} + diff --git a/src/nm-device-interface.h b/src/nm-device-interface.h new file mode 100644 index 00000000..ec27f6e8 --- /dev/null +++ b/src/nm-device-interface.h @@ -0,0 +1,148 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2008 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_INTERFACE_H +#define NM_DEVICE_INTERFACE_H + +#include <glib-object.h> +#include "NetworkManager.h" +#include "nm-connection.h" +#include "nm-activation-request.h" + +#define NM_TYPE_DEVICE_INTERFACE (nm_device_interface_get_type ()) +#define NM_DEVICE_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_INTERFACE, NMDeviceInterface)) +#define NM_IS_DEVICE_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_INTERFACE)) +#define NM_DEVICE_INTERFACE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_DEVICE_INTERFACE, NMDeviceInterface)) + +#define IS_ACTIVATING_STATE(state) \ + (state > NM_DEVICE_STATE_DISCONNECTED && state < NM_DEVICE_STATE_ACTIVATED) + +typedef enum +{ + NM_DEVICE_INTERFACE_ERROR_CONNECTION_ACTIVATING = 0, + NM_DEVICE_INTERFACE_ERROR_CONNECTION_INVALID, + NM_DEVICE_INTERFACE_ERROR_NOT_ACTIVE, +} NMDeviceInterfaceError; + +#define NM_DEVICE_INTERFACE_ERROR (nm_device_interface_error_quark ()) +#define NM_TYPE_DEVICE_INTERFACE_ERROR (nm_device_interface_error_get_type ()) + +#define NM_DEVICE_INTERFACE_UDI "udi" +#define NM_DEVICE_INTERFACE_IFACE "interface" +#define NM_DEVICE_INTERFACE_DRIVER "driver" +#define NM_DEVICE_INTERFACE_CAPABILITIES "capabilities" +#define NM_DEVICE_INTERFACE_IP4_ADDRESS "ip4-address" +#define NM_DEVICE_INTERFACE_IP4_CONFIG "ip4-config" +#define NM_DEVICE_INTERFACE_DHCP4_CONFIG "dhcp4-config" +#define NM_DEVICE_INTERFACE_IP6_CONFIG "ip6-config" +#define NM_DEVICE_INTERFACE_DHCP6_CONFIG "dhcp6-config" +#define NM_DEVICE_INTERFACE_STATE "state" +#define NM_DEVICE_INTERFACE_DEVICE_TYPE "device-type" /* ugh */ +#define NM_DEVICE_INTERFACE_MANAGED "managed" +#define NM_DEVICE_INTERFACE_TYPE_DESC "type-desc" /* Internal only */ +#define NM_DEVICE_INTERFACE_RFKILL_TYPE "rfkill-type" /* Internal only */ +#define NM_DEVICE_INTERFACE_IFINDEX "ifindex" /* Internal only */ + +typedef enum { + NM_DEVICE_INTERFACE_PROP_FIRST = 0x1000, + + NM_DEVICE_INTERFACE_PROP_UDI = NM_DEVICE_INTERFACE_PROP_FIRST, + NM_DEVICE_INTERFACE_PROP_IFACE, + NM_DEVICE_INTERFACE_PROP_DRIVER, + NM_DEVICE_INTERFACE_PROP_CAPABILITIES, + NM_DEVICE_INTERFACE_PROP_IP4_ADDRESS, + NM_DEVICE_INTERFACE_PROP_IP4_CONFIG, + NM_DEVICE_INTERFACE_PROP_DHCP4_CONFIG, + NM_DEVICE_INTERFACE_PROP_IP6_CONFIG, + NM_DEVICE_INTERFACE_PROP_DHCP6_CONFIG, + NM_DEVICE_INTERFACE_PROP_STATE, + NM_DEVICE_INTERFACE_PROP_DEVICE_TYPE, + NM_DEVICE_INTERFACE_PROP_MANAGED, + NM_DEVICE_INTERFACE_PROP_TYPE_DESC, + NM_DEVICE_INTERFACE_PROP_RFKILL_TYPE, + NM_DEVICE_INTERFACE_PROP_IFINDEX, +} NMDeviceInterfaceProp; + + +typedef struct _NMDeviceInterface NMDeviceInterface; + +struct _NMDeviceInterface { + GTypeInterface g_iface; + + /* Methods */ + gboolean (*check_connection_compatible) (NMDeviceInterface *device, + NMConnection *connection, + GError **error); + + gboolean (*activate) (NMDeviceInterface *device, + NMActRequest *req, + GError **error); + + void (*deactivate) (NMDeviceInterface *device, NMDeviceStateReason reason); + gboolean (*disconnect) (NMDeviceInterface *device, GError **error); + + gboolean (*spec_match_list) (NMDeviceInterface *device, const GSList *specs); + + NMConnection * (*connection_match_config) (NMDeviceInterface *device, const GSList *specs); + + void (*set_enabled) (NMDeviceInterface *device, gboolean enabled); + + gboolean (*get_enabled) (NMDeviceInterface *device); + + /* Signals */ + void (*state_changed) (NMDeviceInterface *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason); +}; + +GQuark nm_device_interface_error_quark (void); +GType nm_device_interface_error_get_type (void); + +gboolean nm_device_interface_disconnect (NMDeviceInterface *device, GError **error); + +GType nm_device_interface_get_type (void); + +gboolean nm_device_interface_check_connection_compatible (NMDeviceInterface *device, + NMConnection *connection, + GError **error); + +gboolean nm_device_interface_activate (NMDeviceInterface *device, + NMActRequest *req, + GError **error); + +void nm_device_interface_deactivate (NMDeviceInterface *device, NMDeviceStateReason reason); + +NMDeviceState nm_device_interface_get_state (NMDeviceInterface *device); + +gboolean nm_device_interface_spec_match_list (NMDeviceInterface *device, + const GSList *specs); + +NMConnection * nm_device_interface_connection_match_config (NMDeviceInterface *device, + const GSList *connections); + +gboolean nm_device_interface_can_assume_connection (NMDeviceInterface *device); + +gboolean nm_device_interface_get_enabled (NMDeviceInterface *device); + +void nm_device_interface_set_enabled (NMDeviceInterface *device, gboolean enabled); + +#endif /* NM_DEVICE_INTERFACE_H */ diff --git a/src/nm-device-modem.c b/src/nm-device-modem.c new file mode 100644 index 00000000..441f92ce --- /dev/null +++ b/src/nm-device-modem.c @@ -0,0 +1,471 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#include <glib.h> + +#include "nm-device-modem.h" +#include "nm-device-interface.h" +#include "nm-modem.h" +#include "nm-device-private.h" +#include "nm-properties-changed-signal.h" +#include "nm-marshal.h" +#include "nm-logging.h" + +static void device_interface_init (NMDeviceInterface *iface_class); + +G_DEFINE_TYPE_EXTENDED (NMDeviceModem, nm_device_modem, NM_TYPE_DEVICE, G_TYPE_FLAG_ABSTRACT, + G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_INTERFACE, device_interface_init)) + +#define NM_DEVICE_MODEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_MODEM, NMDeviceModemPrivate)) + +typedef struct { + NMModem *modem; +} NMDeviceModemPrivate; + +enum { + PROP_0, + PROP_MODEM +}; + +enum { + PPP_STATS, + ENABLE_CHANGED, + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + +static void real_set_enabled (NMDeviceInterface *device, gboolean enabled); + +/*****************************************************************************/ + +static void +ppp_stats (NMModem *modem, + guint32 in_bytes, + guint32 out_bytes, + gpointer user_data) +{ + g_signal_emit (G_OBJECT (user_data), signals[PPP_STATS], 0, in_bytes, out_bytes); +} + +static void +ppp_failed (NMModem *modem, NMDeviceStateReason reason, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + + switch (nm_device_interface_get_state (NM_DEVICE_INTERFACE (device))) { + case NM_DEVICE_STATE_PREPARE: + case NM_DEVICE_STATE_CONFIG: + case NM_DEVICE_STATE_NEED_AUTH: + case NM_DEVICE_STATE_ACTIVATED: + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); + break; + case NM_DEVICE_STATE_IP_CONFIG: + if (nm_device_ip_config_should_fail (device, FALSE)) { + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + } + break; + default: + break; + } +} + +static void +modem_prepare_result (NMModem *modem, + gboolean success, + NMDeviceStateReason reason, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDeviceState state; + + state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); + g_return_if_fail (state == NM_DEVICE_STATE_PREPARE); + + if (success) + nm_device_activate_schedule_stage2_device_config (device); + else + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); +} + +static void +modem_need_auth (NMModem *modem, + const char *setting_name, + gboolean retry, + RequestSecretsCaller caller, + const char *hint1, + const char *hint2, + gpointer user_data) +{ + NMDeviceModem *self = NM_DEVICE_MODEM (user_data); + NMActRequest *req; + + req = nm_device_get_act_request (NM_DEVICE (self)); + g_assert (req); + + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); + nm_act_request_get_secrets (req, setting_name, retry, caller, hint1, hint2); +} + +static void +modem_ip4_config_result (NMModem *self, + const char *iface, + NMIP4Config *config, + GError *error, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDeviceState state; + + state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); + g_return_if_fail (state == NM_DEVICE_STATE_IP_CONFIG); + + if (error) { + nm_log_warn (LOGD_MB | LOGD_IP4, "retrieving IP4 configuration failed: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + } else { + if (iface) + nm_device_set_ip_iface (device, iface); + + nm_device_activate_schedule_stage4_ip4_config_get (device); + } +} + +static void +modem_enabled_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data) +{ + NMDeviceModem *self = NM_DEVICE_MODEM (user_data); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self); + + real_set_enabled (NM_DEVICE_INTERFACE (self), nm_modem_get_mm_enabled (priv->modem)); + + g_signal_emit (G_OBJECT (self), signals[ENABLE_CHANGED], 0); +} + +/*****************************************************************************/ + +NMModem * +nm_device_modem_get_modem (NMDeviceModem *self) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DEVICE_MODEM (self), NULL); + + return NM_DEVICE_MODEM_GET_PRIVATE (self)->modem; +} + +/*****************************************************************************/ + +static void +device_state_changed (NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason, + gpointer user_data) +{ + nm_modem_device_state_changed (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, + new_state, + old_state, + reason); +} + +static guint32 +real_get_generic_capabilities (NMDevice *device) +{ + return NM_DEVICE_CAP_NM_SUPPORTED; +} + +static NMConnection * +real_get_best_auto_connection (NMDevice *device, + GSList *connections, + char **specific_object) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); + + return nm_modem_get_best_auto_connection (priv->modem, connections, specific_object); +} + +static void +real_connection_secrets_updated (NMDevice *device, + NMConnection *connection, + GSList *updated_settings, + RequestSecretsCaller caller) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); + NMActRequest *req; + + g_return_if_fail (IS_ACTIVATING_STATE (nm_device_get_state (device))); + + req = nm_device_get_act_request (device); + g_assert (req); + + if (!nm_modem_connection_secrets_updated (priv->modem, + req, + connection, + updated_settings, + caller)) { + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NO_SECRETS); + return; + } + + /* PPP handles stuff itself... */ + if (caller == SECRETS_CALLER_PPP) + return; + + /* Otherwise, on success for modem secrets we need to schedule stage1 again */ + g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH); + nm_device_activate_schedule_stage1_device_prepare (device); +} + +static gboolean +real_check_connection_compatible (NMDevice *device, + NMConnection *connection, + GError **error) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); + + return nm_modem_check_connection_compatible (priv->modem, connection, error); +} + +static gboolean +real_hw_is_up (NMDevice *device) +{ + return nm_modem_hw_is_up (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, device); +} + +static gboolean +real_hw_bring_up (NMDevice *device, gboolean *no_firmware) +{ + return nm_modem_hw_bring_up (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, device, no_firmware); +} + +static void +real_deactivate_quickly (NMDevice *device) +{ + nm_modem_deactivate_quickly (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, device); +} + +static NMActStageReturn +real_act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) +{ + NMActRequest *req; + + req = nm_device_get_act_request (device); + g_assert (req); + + return nm_modem_act_stage1_prepare (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, req, reason); +} + +static NMActStageReturn +real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason) +{ + NMActRequest *req; + + req = nm_device_get_act_request (device); + g_assert (req); + + return nm_modem_act_stage2_config (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, req, reason); +} + +static NMActStageReturn +real_act_stage3_ip4_config_start (NMDevice *device, NMDeviceStateReason *reason) +{ + return nm_modem_stage3_ip4_config_start (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, + device, + NM_DEVICE_CLASS (nm_device_modem_parent_class), + reason); +} + +static NMActStageReturn +real_act_stage4_get_ip4_config (NMDevice *device, + NMIP4Config **config, + NMDeviceStateReason *reason) +{ + return nm_modem_stage4_get_ip4_config (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, + device, + NM_DEVICE_CLASS (nm_device_modem_parent_class), + config, + reason); +} + +/*****************************************************************************/ + +static gboolean +real_get_enabled (NMDeviceInterface *device) +{ + return nm_modem_get_mm_enabled (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem); +} + +static void +real_set_enabled (NMDeviceInterface *device, gboolean enabled) +{ + NMDeviceModem *self = NM_DEVICE_MODEM (device); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self); + NMDeviceState state; + + if (priv->modem) { + nm_modem_set_mm_enabled (priv->modem, enabled); + + if (enabled == FALSE) { + state = nm_device_interface_get_state (device); + if (state == NM_DEVICE_STATE_ACTIVATED) { + nm_device_state_changed (NM_DEVICE (device), + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_NONE); + } + } + } +} + +/*****************************************************************************/ + +static void +device_interface_init (NMDeviceInterface *iface_class) +{ + iface_class->get_enabled = real_get_enabled; + iface_class->set_enabled = real_set_enabled; +} + +static void +nm_device_modem_init (NMDeviceModem *self) +{ + g_signal_connect (self, "state-changed", G_CALLBACK (device_state_changed), self); +} + +static void +set_modem (NMDeviceModem *self, NMModem *modem) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self); + + g_return_if_fail (modem != NULL); + + priv->modem = g_object_ref (modem); + + g_signal_connect (modem, NM_MODEM_PPP_STATS, G_CALLBACK (ppp_stats), self); + g_signal_connect (modem, NM_MODEM_PPP_FAILED, G_CALLBACK (ppp_failed), self); + g_signal_connect (modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK (modem_prepare_result), self); + g_signal_connect (modem, NM_MODEM_IP4_CONFIG_RESULT, G_CALLBACK (modem_ip4_config_result), self); + g_signal_connect (modem, NM_MODEM_NEED_AUTH, G_CALLBACK (modem_need_auth), self); + g_signal_connect (modem, "notify::" NM_MODEM_ENABLED, G_CALLBACK (modem_enabled_cb), self); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_MODEM: + /* construct-only */ + set_modem (NM_DEVICE_MODEM (object), g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MODEM: + g_value_set_object (value, priv->modem); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +finalize (GObject *object) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object); + + g_object_unref (priv->modem); + priv->modem = NULL; + + G_OBJECT_CLASS (nm_device_modem_parent_class)->finalize (object); +} + +static void +nm_device_modem_class_init (NMDeviceModemClass *mclass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (mclass); + NMDeviceClass *device_class = NM_DEVICE_CLASS (mclass); + + g_type_class_add_private (object_class, sizeof (NMDeviceModemPrivate)); + + /* Virtual methods */ + object_class->finalize = finalize; + object_class->get_property = get_property; + object_class->set_property = set_property; + + device_class->get_generic_capabilities = real_get_generic_capabilities; + device_class->get_best_auto_connection = real_get_best_auto_connection; + device_class->connection_secrets_updated = real_connection_secrets_updated; + device_class->check_connection_compatible = real_check_connection_compatible; + device_class->hw_is_up = real_hw_is_up; + device_class->hw_bring_up = real_hw_bring_up; + device_class->deactivate_quickly = real_deactivate_quickly; + device_class->act_stage1_prepare = real_act_stage1_prepare; + device_class->act_stage2_config = real_act_stage2_config; + device_class->act_stage3_ip4_config_start = real_act_stage3_ip4_config_start; + device_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_MODEM, + g_param_spec_object (NM_DEVICE_MODEM_MODEM, + "Modem", + "Modem", + NM_TYPE_MODEM, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); + + /* Signals */ + signals[PPP_STATS] = + g_signal_new ("ppp-stats", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDeviceModemClass, ppp_stats), + NULL, NULL, + _nm_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, + G_TYPE_UINT, G_TYPE_UINT); + + signals[ENABLE_CHANGED] = + g_signal_new (NM_DEVICE_MODEM_ENABLE_CHANGED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (mclass), + nm_modem_get_serial_dbus_info ()); +} + diff --git a/src/nm-device-modem.h b/src/nm-device-modem.h new file mode 100644 index 00000000..c6ef4d21 --- /dev/null +++ b/src/nm-device-modem.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2010 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_MODEM_H +#define NM_DEVICE_MODEM_H + +#include <glib.h> +#include <glib-object.h> + +#include "nm-device.h" +#include "nm-modem.h" + +#define NM_TYPE_DEVICE_MODEM (nm_device_modem_get_type ()) +#define NM_DEVICE_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_MODEM, NMDeviceModem)) +#define NM_DEVICE_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_MODEM, NMDeviceModemClass)) +#define NM_IS_DEVICE_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_MODEM)) +#define NM_IS_DEVICE_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DEVICE_MODEM)) +#define NM_DEVICE_MODEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_MODEM, NMDeviceModemClass)) + +#define NM_DEVICE_MODEM_MODEM "modem" + +#define NM_DEVICE_MODEM_ENABLE_CHANGED "enable-changed" + +typedef struct { + NMDevice parent; +} NMDeviceModem; + +typedef struct { + NMDeviceClass parent; + + void (*ppp_stats) (NMDeviceModem *self, guint32 in_bytes, guint32 out_bytes); +} NMDeviceModemClass; + +GType nm_device_modem_get_type (void); + +/* Private for subclases */ +NMModem *nm_device_modem_get_modem (NMDeviceModem *self); + +#endif /* NM_DEVICE_MODEM_H */ diff --git a/src/nm-device-olpc-mesh.c b/src/nm-device-olpc-mesh.c new file mode 100644 index 00000000..546ecfaf --- /dev/null +++ b/src/nm-device-olpc-mesh.c @@ -0,0 +1,974 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * Dan Williams <dcbw@redhat.com> + * Sjoerd Simons <sjoerd.simons@collabora.co.uk> + * Daniel Drake <dsd@laptop.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2005 - 2010 Red Hat, Inc. + * (C) Copyright 2008 Collabora Ltd. + * (C) Copyright 2009 One Laptop per Child + */ + +#include <glib.h> +#include <glib/gi18n.h> +#include <dbus/dbus.h> +#include <netinet/in.h> +#include <string.h> +#include <net/ethernet.h> +#include <iwlib.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <signal.h> +#include <unistd.h> + +#include "nm-device.h" +#include "nm-device-wifi.h" +#include "nm-device-olpc-mesh.h" +#include "nm-device-interface.h" +#include "nm-device-private.h" +#include "nm-utils.h" +#include "nm-logging.h" +#include "NetworkManagerUtils.h" +#include "nm-activation-request.h" +#include "nm-properties-changed-signal.h" +#include "nm-setting-connection.h" +#include "nm-setting-olpc-mesh.h" +#include "nm-system.h" +#include "nm-manager.h" + +/* This is a bug; but we can't really change API now... */ +#include "NetworkManagerVPN.h" + + +#include "nm-device-olpc-mesh-glue.h" + +static void nm_device_olpc_mesh_set_ssid (NMDeviceOlpcMesh *self, const GByteArray * ssid); + +G_DEFINE_TYPE (NMDeviceOlpcMesh, nm_device_olpc_mesh, NM_TYPE_DEVICE) + +#define NM_DEVICE_OLPC_MESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshPrivate)) + + +enum { + PROP_0, + PROP_HW_ADDRESS, + PROP_COMPANION, + PROP_ACTIVE_CHANNEL, + + LAST_PROP +}; + +enum { + PROPERTIES_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef enum +{ + NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH = 0, + NM_OLPC_MESH_ERROR_CONNECTION_INVALID, + NM_OLPC_MESH_ERROR_CONNECTION_INCOMPATIBLE, +} NMOlpcMeshError; + +#define NM_OLPC_MESH_ERROR (nm_olpc_mesh_error_quark ()) +#define NM_TYPE_OLPC_MESH_ERROR (nm_olpc_mesh_error_get_type ()) + + +struct _NMDeviceOlpcMeshPrivate +{ + gboolean dispose_has_run; + + struct ether_addr hw_addr; + + GByteArray * ssid; + + gint8 num_freqs; + guint32 freqs[IW_MAX_FREQUENCIES]; + + guint8 we_version; + gboolean up; + + NMDevice * companion; + gboolean stage1_waiting; + guint device_added_id; +}; + +static GQuark +nm_olpc_mesh_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm-mesh-error"); + return quark; +} + +/* This should really be standard. */ +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +static GType +nm_olpc_mesh_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + /* Connection was not a wireless connection. */ + ENUM_ENTRY (NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH, "ConnectionNotMesh"), + /* Connection was not a valid wireless connection. */ + ENUM_ENTRY (NM_OLPC_MESH_ERROR_CONNECTION_INVALID, "ConnectionInvalid"), + /* Connection does not apply to this device. */ + ENUM_ENTRY (NM_OLPC_MESH_ERROR_CONNECTION_INCOMPATIBLE, "ConnectionIncompatible"), + { 0, 0, 0 } + }; + etype = g_enum_register_static ("NMOlpcMeshError", values); + } + return etype; +} + +static guint32 +real_get_generic_capabilities (NMDevice *dev) +{ + int fd; + guint32 caps = NM_DEVICE_CAP_NONE; + struct iw_range range; + struct iwreq wrq; + const char *iface = nm_device_get_iface (dev); + + /* Check for Wireless Extensions support >= 16 for wireless devices */ + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_warn (LOGD_OLPC_MESH, "couldn't open control socket."); + goto out; + } + + memset (&wrq, 0, sizeof (struct iwreq)); + memset (&range, 0, sizeof (struct iw_range)); + strncpy (wrq.ifr_name, iface, IFNAMSIZ); + wrq.u.data.pointer = (caddr_t) ⦥ + wrq.u.data.length = sizeof (struct iw_range); + + if (ioctl (fd, SIOCGIWRANGE, &wrq) < 0) { + nm_log_warn (LOGD_OLPC_MESH, "couldn't get driver range information."); + goto out; + } + + if ((wrq.u.data.length < 300) || (range.we_version_compiled < 16)) { + nm_log_warn (LOGD_OLPC_MESH, + "(%s): driver's Wireless Extensions version (%d) is too old.", + iface, range.we_version_compiled); + goto out; + } else { + caps |= NM_DEVICE_CAP_NM_SUPPORTED; + } + +out: + if (fd >= 0) + close (fd); + return caps; +} + +static void +nm_device_olpc_mesh_init (NMDeviceOlpcMesh * self) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + + priv->dispose_has_run = FALSE; + priv->we_version = 0; + priv->companion = NULL; + priv->stage1_waiting = FALSE; + + memset (&(priv->hw_addr), 0, sizeof (struct ether_addr)); +} + +static guint32 iw_freq_to_uint32 (struct iw_freq *freq) +{ + if (freq->e == 0) { + /* Some drivers report channel not frequency. Convert to a + * frequency; but this assumes that the device is in b/g mode. + */ + if ((freq->m >= 1) && (freq->m <= 13)) + return 2407 + (5 * freq->m); + else if (freq->m == 14) + return 2484; + } + + return (guint32) (((double) freq->m) * pow (10, freq->e) / 1000000); +} + + +/* Until a new wireless-tools comes out that has the defs and the structure, + * need to copy them here. + */ +/* Scan capability flags - in (struct iw_range *)->scan_capa */ +#define NM_IW_SCAN_CAPA_NONE 0x00 +#define NM_IW_SCAN_CAPA_ESSID 0x01 + +struct iw_range_with_scan_capa +{ + guint32 throughput; + guint32 min_nwid; + guint32 max_nwid; + guint16 old_num_channels; + guint8 old_num_frequency; + + guint8 scan_capa; +/* don't need the rest... */ +}; + + + + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + GObjectClass *klass; + NMDeviceOlpcMesh *self; + NMDeviceOlpcMeshPrivate *priv; + const char *iface; + int fd; + struct iw_range range; + struct iwreq wrq; + int i; + + klass = G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class); + object = klass->constructor (type, n_construct_params, construct_params); + if (!object) + return NULL; + + self = NM_DEVICE_OLPC_MESH (object); + priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + + nm_log_dbg (LOGD_HW | LOGD_OLPC_MESH, "(%s): kernel ifindex %d", + nm_device_get_iface (NM_DEVICE (self)), + nm_device_get_ifindex (NM_DEVICE (self))); + + iface = nm_device_get_iface (NM_DEVICE (self)); + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) + goto error; + + memset (&wrq, 0, sizeof (struct iwreq)); + memset (&range, 0, sizeof (struct iw_range)); + strncpy (wrq.ifr_name, iface, IFNAMSIZ); + wrq.u.data.pointer = (caddr_t) ⦥ + wrq.u.data.length = sizeof (struct iw_range); + + if (ioctl (fd, SIOCGIWRANGE, &wrq) < 0) + goto error; + + priv->num_freqs = MIN (range.num_frequency, IW_MAX_FREQUENCIES); + for (i = 0; i < priv->num_freqs; i++) + priv->freqs[i] = iw_freq_to_uint32 (&range.freq[i]); + + priv->we_version = range.we_version_compiled; + + close (fd); + + /* shorter timeout for mesh connectivity */ + nm_device_set_dhcp_timeout (NM_DEVICE (self), 20); + return object; + +error: + if (fd >= 0) + close (fd); + g_object_unref (object); + return NULL; +} + +static gboolean +real_hw_is_up (NMDevice *device) +{ + return nm_system_device_is_up (device); +} + +static gboolean +real_hw_bring_up (NMDevice *dev, gboolean *no_firmware) +{ + return nm_system_device_set_up_down (dev, TRUE, no_firmware); +} + +static void +real_hw_take_down (NMDevice *dev) +{ + nm_system_device_set_up_down (dev, FALSE, NULL); +} + +static gboolean +real_is_up (NMDevice *device) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + + return priv->up; +} + +static gboolean +real_bring_up (NMDevice *dev) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (dev); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + + priv->up = TRUE; + return TRUE; +} + +static void +device_cleanup (NMDeviceOlpcMesh *self) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + + if (priv->ssid) { + g_byte_array_free (priv->ssid, TRUE); + priv->ssid = NULL; + } + priv->up = FALSE; +} + +static void +real_take_down (NMDevice *dev) +{ + device_cleanup (NM_DEVICE_OLPC_MESH (dev)); +} + +static gboolean +real_check_connection_compatible (NMDevice *device, + NMConnection *connection, + GError **error) +{ + NMSettingConnection *s_con; + NMSettingOlpcMesh *s_mesh; + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + + if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_OLPC_MESH_SETTING_NAME)) { + g_set_error (error, + NM_OLPC_MESH_ERROR, NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH, + "The connection was not a Mesh connection."); + return FALSE; + } + + s_mesh = NM_SETTING_OLPC_MESH (nm_connection_get_setting (connection, NM_TYPE_SETTING_OLPC_MESH)); + if (!s_mesh) { + g_set_error (error, + NM_OLPC_MESH_ERROR, NM_OLPC_MESH_ERROR_CONNECTION_INVALID, + "The connection was not a valid Mesh connection."); + return FALSE; + } + + return TRUE; +} + +/* + * nm_device_olpc_mesh_get_address + * + * Get a device's hardware address + * + */ +static void +nm_device_olpc_mesh_get_address (NMDeviceOlpcMesh *self, + struct ether_addr *addr) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + g_return_if_fail (self != NULL); + g_return_if_fail (addr != NULL); + + memcpy (addr, &(priv->hw_addr), sizeof (struct ether_addr)); +} + +static int +create_socket_with_request (NMDevice *self, struct iwreq *req) +{ + int sk; + const char * iface; + + g_return_val_if_fail (self != NULL, -1); + + sk = socket (AF_INET, SOCK_DGRAM, 0); + if (!sk) { + nm_log_err (LOGD_OLPC_MESH, "Couldn't create socket: %d.", errno); + return -1; + } + + memset (req, 0, sizeof (struct iwreq)); + iface = nm_device_get_iface (NM_DEVICE (self)); + strncpy (req->ifr_name, iface, IFNAMSIZ); + + return sk; +} + +static guint32 +nm_device_olpc_mesh_get_channel (NMDeviceOlpcMesh *self) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + int sk; + struct iwreq req; + int ret = 0; + int i; + guint32 freq; + + sk = create_socket_with_request (NM_DEVICE (self), &req); + if (sk == -1) + return 0; + + if ((ioctl (sk, SIOCGIWFREQ, &req)) != 0) { + nm_log_warn (LOGD_OLPC_MESH, "(%s): failed to get channel (errno: %d)", + nm_device_get_iface (NM_DEVICE (self)), errno); + goto out; + } + + freq = iw_freq_to_uint32 (&req.u.freq); + + for (i = 0 ; i < priv->num_freqs; i++) { + if (freq == priv->freqs[i]) + break; + } + if (i < priv->num_freqs) + ret = i + 1; + +out: + if (sk >= 0) + close (sk); + return ret; +} + +static void +nm_device_olpc_mesh_set_channel (NMDeviceOlpcMesh *self, guint32 channel) +{ + int sk; + struct iwreq req; + + if (nm_device_olpc_mesh_get_channel (self) == channel) + return; + + sk = create_socket_with_request (NM_DEVICE (self), &req); + if (sk < 0) + return; + + if (channel > 0) { + req.u.freq.flags = IW_FREQ_FIXED; + req.u.freq.e = 0; + req.u.freq.m = channel; + } + + if (ioctl (sk, SIOCSIWFREQ, &req) != 0) { + nm_log_warn (LOGD_OLPC_MESH, "(%s): failed to set to channel %d (errno: %d)", + nm_device_get_iface (NM_DEVICE (self)), channel, errno); + } else + g_object_notify (G_OBJECT (self), NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL); + + close (sk); +} + +static void +nm_device_olpc_mesh_set_ssid (NMDeviceOlpcMesh *self, const GByteArray * ssid) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + int sk; + struct iwreq wrq; + const char * iface; + guint32 len = 0; + char buf[IW_ESSID_MAX_SIZE + 1]; + + g_return_if_fail (self != NULL); + + sk = socket (AF_INET, SOCK_DGRAM, 0); + if (!sk) { + nm_log_err (LOGD_OLPC_MESH, "Couldn't create socket: %d.", errno); + return; + } + + iface = nm_device_get_iface (NM_DEVICE (self)); + + memset (buf, 0, sizeof (buf)); + if (ssid) { + len = ssid->len; + memcpy (buf, ssid->data, MIN (sizeof (buf) - 1, len)); + } + wrq.u.essid.pointer = (caddr_t) buf; + + if (priv->we_version < 21) { + /* For historic reasons, set SSID length to include one extra + * character, C string nul termination, even though SSID is + * really an octet string that should not be presented as a C + * string. Some Linux drivers decrement the length by one and + * can thus end up missing the last octet of the SSID if the + * length is not incremented here. WE-21 changes this to + * explicitly require the length _not_ to include nul + * termination. */ + if (len) + len++; + } + wrq.u.essid.length = len; + wrq.u.essid.flags = (len > 0) ? 1 : 0; /* 1=enable SSID, 0=disable/any */ + + strncpy (wrq.ifr_name, iface, IFNAMSIZ); + + if (ioctl (sk, SIOCSIWESSID, &wrq) < 0) { + if (errno != ENODEV) { + nm_log_err (LOGD_OLPC_MESH, "(%s): error setting SSID to '%s': %s", + iface, + ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(null)", + strerror (errno)); + } + } + + close (sk); +} + +/****************************************************************************/ + +static void +real_update_hw_address (NMDevice *dev) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (dev); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + struct ifreq req; + int ret, fd; + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_warn (LOGD_OLPC_MESH, "could not open control socket."); + return; + } + + memset (&req, 0, sizeof (struct ifreq)); + strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ); + ret = ioctl (fd, SIOCGIFHWADDR, &req); + if (ret) { + nm_log_warn (LOGD_OLPC_MESH, "(%s): error getting hardware address: %d", + nm_device_get_iface (dev), errno); + goto out; + } + + if (memcmp (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr))) { + memcpy (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr)); + g_object_notify (G_OBJECT (dev), NM_DEVICE_OLPC_MESH_HW_ADDRESS); + } + +out: + close (fd); +} + + +static NMActStageReturn +real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (dev); + gboolean scanning; + + /* disconnect companion device, if it is connected */ + if (nm_device_get_act_request (NM_DEVICE (priv->companion))) { + nm_log_info (LOGD_OLPC_MESH, "(%s): disconnecting companion device %s", + nm_device_get_iface (dev), + nm_device_get_iface (priv->companion)); + /* FIXME: VPN stuff here is a bug; but we can't really change API now... */ + nm_device_state_changed (NM_DEVICE (priv->companion), + NM_DEVICE_STATE_DISCONNECTED, + NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED); + nm_log_info (LOGD_OLPC_MESH, "(%s): companion %s disconnected", + nm_device_get_iface (dev), + nm_device_get_iface (priv->companion)); + } + + + /* wait with continuing configuration untill the companion device is done scanning */ + g_object_get (priv->companion, "scanning", &scanning, NULL); + if (scanning) { + priv->stage1_waiting = TRUE; + return NM_ACT_STAGE_RETURN_POSTPONE; + } + + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +static NMActStageReturn +real_act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (dev); + NMConnection *connection; + NMSettingOlpcMesh *s_mesh; + NMActRequest *req; + guint32 channel; + const GByteArray *anycast_addr_array; + guint8 *anycast_addr = NULL; + + req = nm_device_get_act_request (dev); + g_assert (req); + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + s_mesh = NM_SETTING_OLPC_MESH (nm_connection_get_setting (connection, NM_TYPE_SETTING_OLPC_MESH)); + g_assert (s_mesh); + + channel = nm_setting_olpc_mesh_get_channel (s_mesh); + if (channel != 0) + nm_device_olpc_mesh_set_channel (self, channel); + nm_device_olpc_mesh_set_ssid (self, nm_setting_olpc_mesh_get_ssid (s_mesh)); + + anycast_addr_array = nm_setting_olpc_mesh_get_dhcp_anycast_address (s_mesh); + if (anycast_addr_array) + anycast_addr = anycast_addr_array->data; + + nm_device_set_dhcp_anycast_address (dev, anycast_addr); + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +static void +dispose (GObject *object) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (object); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + NMManager *manager; + + if (priv->dispose_has_run) { + G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->dispose (object); + return; + } + priv->dispose_has_run = TRUE; + + device_cleanup (self); + + manager = nm_manager_get (NULL, NULL, NULL, FALSE, FALSE, FALSE, NULL); + if (priv->device_added_id) + g_signal_handler_disconnect (manager, priv->device_added_id); + g_object_unref (manager); + + G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->dispose (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDeviceOlpcMesh *device = NM_DEVICE_OLPC_MESH (object); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (device); + struct ether_addr hw_addr; + + switch (prop_id) { + case PROP_HW_ADDRESS: + nm_device_olpc_mesh_get_address (device, &hw_addr); + g_value_take_string (value, nm_ether_ntop (&hw_addr)); + break; + case PROP_COMPANION: + if (priv->companion) + g_value_set_boxed (value, nm_device_get_path (priv->companion)); + else + g_value_set_boxed (value, "/"); + break; + case PROP_ACTIVE_CHANNEL: + g_value_set_uint (value, nm_device_olpc_mesh_get_channel (device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMDeviceOlpcMeshPrivate)); + + object_class->constructor = constructor; + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = dispose; + + parent_class->get_type_capabilities = NULL; + parent_class->get_generic_capabilities = real_get_generic_capabilities; + parent_class->hw_is_up = real_hw_is_up; + parent_class->hw_bring_up = real_hw_bring_up; + parent_class->hw_take_down = real_hw_take_down; + parent_class->is_up = real_is_up; + parent_class->bring_up = real_bring_up; + parent_class->take_down = real_take_down; + parent_class->update_hw_address = real_update_hw_address; + parent_class->check_connection_compatible = real_check_connection_compatible; + + parent_class->act_stage1_prepare = real_act_stage1_prepare; + parent_class->act_stage2_config = real_act_stage2_config; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_DEVICE_OLPC_MESH_HW_ADDRESS, + "MAC Address", + "Hardware MAC address", + NULL, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_COMPANION, + g_param_spec_boxed (NM_DEVICE_OLPC_MESH_COMPANION, + "Companion device", + "Companion device object path", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_ACTIVE_CHANNEL, + g_param_spec_uint (NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL, + "Active channel", + "Active channel", + 0, G_MAXUINT32, 0, + G_PARAM_READABLE)); + + signals[PROPERTIES_CHANGED] = + nm_properties_changed_signal_new (object_class, + G_STRUCT_OFFSET (NMDeviceOlpcMeshClass, properties_changed)); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), &dbus_glib_nm_device_olpc_mesh_object_info); + + dbus_g_error_domain_register (NM_OLPC_MESH_ERROR, NULL, + NM_TYPE_OLPC_MESH_ERROR); +} + +static void +companion_notify_cb (NMDeviceWifi *companion, GParamSpec *pspec, gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + gboolean scanning; + + if (!priv->stage1_waiting) + return; + + g_object_get (companion, "scanning", &scanning, NULL); + + if (!scanning) { + priv->stage1_waiting = FALSE; + nm_device_activate_schedule_stage2_device_config (NM_DEVICE (self)); + } +} + +/* disconnect from mesh if someone starts using the companion */ +static void +companion_state_changed_cb (NMDeviceWifi *companion, + NMDeviceState state, + NMDeviceState old_state, + NMDeviceStateReason reason, + gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + NMDeviceState self_state = nm_device_get_state (NM_DEVICE (self)); + + if ( self_state < NM_DEVICE_STATE_PREPARE + || self_state > NM_DEVICE_STATE_ACTIVATED + || state < NM_DEVICE_STATE_PREPARE + || state > NM_DEVICE_STATE_ACTIVATED) + return; + + nm_log_dbg (LOGD_OLPC_MESH, "(%s): disconnecting mesh due to companion connectivity", + nm_device_get_iface (NM_DEVICE (self))); + /* FIXME: VPN stuff here is a bug; but we can't really change API now... */ + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_DISCONNECTED, + NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED); +} + +static gboolean +companion_scan_allowed_cb (NMDeviceWifi *companion, gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + NMDeviceState state; + + g_object_get (G_OBJECT (self), NM_DEVICE_INTERFACE_STATE, &state, NULL); + + /* Don't allow the companion to scan while configure the mesh interface */ + return (state < NM_DEVICE_STATE_PREPARE) || (state > NM_DEVICE_STATE_IP_CONFIG); +} + +static gboolean +companion_autoconnect_allowed_cb (NMDeviceWifi *companion, gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + NMDeviceState state; + + g_object_get (G_OBJECT (self), NM_DEVICE_INTERFACE_STATE, &state, NULL); + + /* Don't allow the companion to autoconnect while a mesh connection is + * active */ + return (state < NM_DEVICE_STATE_PREPARE) || (state > NM_DEVICE_STATE_ACTIVATED); +} + +static gboolean +is_companion (NMDeviceOlpcMesh *self, NMDevice *other) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + struct ether_addr their_addr; + NMManager *manager; + + if (!NM_IS_DEVICE_WIFI (other)) + return FALSE; + + nm_device_wifi_get_address (NM_DEVICE_WIFI (other), &their_addr); + + if (memcmp (priv->hw_addr.ether_addr_octet, + their_addr.ether_addr_octet, ETH_ALEN) != 0) { + return FALSE; + } + + /* FIXME detect when our companion leaves */ + priv->companion = other; + + /* When we've found the companion, stop listening for other devices */ + manager = nm_manager_get (NULL, NULL, NULL, FALSE, FALSE, FALSE, NULL); + if (priv->device_added_id) { + g_signal_handler_disconnect (manager, priv->device_added_id); + priv->device_added_id = 0; + } + g_object_unref (manager); + + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_NONE); + + nm_log_info (LOGD_OLPC_MESH, "(%s): found companion WiFi device %s", + nm_device_get_iface (NM_DEVICE (self)), + nm_device_get_iface (other)); + + g_signal_connect (G_OBJECT (other), "state-changed", + G_CALLBACK (companion_state_changed_cb), self); + g_signal_connect (G_OBJECT (other), "notify::scanning", + G_CALLBACK (companion_notify_cb), self); + g_signal_connect (G_OBJECT (other), "scanning-allowed", + G_CALLBACK (companion_scan_allowed_cb), self); + g_signal_connect (G_OBJECT (other), "autoconnect-allowed", + G_CALLBACK (companion_autoconnect_allowed_cb), self); + + g_object_notify (G_OBJECT (self), NM_DEVICE_OLPC_MESH_COMPANION); + + return TRUE; +} + +static void +device_added_cb (NMManager *manager, NMDevice *other, gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + + is_companion (self, other); +} + +static gboolean +check_companion_cb (gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + NMManager *manager; + GSList *list; + + if (priv->companion != NULL) { + nm_device_state_changed (NM_DEVICE (user_data), + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_NONE); + return FALSE; + } + + if (priv->device_added_id != 0) + return FALSE; + + manager = nm_manager_get (NULL, NULL, NULL, FALSE, FALSE, FALSE, NULL); + + priv->device_added_id = g_signal_connect (manager, "device-added", + G_CALLBACK (device_added_cb), self); + + /* Try to find the companion if it's already known to the NMManager */ + for (list = nm_manager_get_devices (manager); list ; list = g_slist_next (list)) { + if (is_companion (self, NM_DEVICE (list->data))) + break; + } + + g_object_unref (manager); + + return FALSE; +} + +static void +state_changed_cb (NMDevice *device, NMDeviceState state, gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device); + + switch (state) { + case NM_DEVICE_STATE_UNMANAGED: + break; + case NM_DEVICE_STATE_UNAVAILABLE: + /* If transitioning to UNAVAILBLE and the companion device is known then + * transition to DISCONNECTED otherwise wait for our companion. + */ + g_idle_add (check_companion_cb, self); + break; + case NM_DEVICE_STATE_ACTIVATED: + break; + case NM_DEVICE_STATE_FAILED: + break; + case NM_DEVICE_STATE_DISCONNECTED: + break; + default: + break; + } +} + + +NMDevice * +nm_device_olpc_mesh_new (const char *udi, + const char *iface, + const char *driver) +{ + GObject *obj; + + g_return_val_if_fail (udi != NULL, NULL); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + + obj = g_object_new (NM_TYPE_DEVICE_OLPC_MESH, + NM_DEVICE_INTERFACE_UDI, udi, + NM_DEVICE_INTERFACE_IFACE, iface, + NM_DEVICE_INTERFACE_DRIVER, driver, + NM_DEVICE_INTERFACE_TYPE_DESC, "802.11 OLPC Mesh", + NM_DEVICE_INTERFACE_DEVICE_TYPE, NM_DEVICE_TYPE_OLPC_MESH, + NULL); + if (obj == NULL) + return NULL; + + g_signal_connect (obj, "state-changed", G_CALLBACK (state_changed_cb), NULL); + + return NM_DEVICE (obj); +} diff --git a/src/nm-device-olpc-mesh.h b/src/nm-device-olpc-mesh.h new file mode 100644 index 00000000..bcc03e57 --- /dev/null +++ b/src/nm-device-olpc-mesh.h @@ -0,0 +1,81 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* NetworkManager -- Network link manager + * + * Dan Williams <dcbw@redhat.com> + * Sjoerd Simons <sjoerd.simons@collabora.co.uk> + * Daniel Drake <dsd@laptop.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2005 Red Hat, Inc. + * (C) Copyright 2008 Collabora Ltd. + * (C) Copyright 2009 One Laptop per Child + */ + +#ifndef NM_DEVICE_OLPC_MESH_H +#define NM_DEVICE_OLPC_MESH_H + +#include <glib-object.h> +#include <dbus/dbus.h> + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_OLPC_MESH (nm_device_olpc_mesh_get_type ()) +#define NM_DEVICE_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMesh)) +#define NM_DEVICE_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass)) +#define NM_IS_DEVICE_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OLPC_MESH)) +#define NM_IS_DEVICE_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OLPC_MESH)) +#define NM_DEVICE_OLPC_MESH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass)) + +#define NM_DEVICE_OLPC_MESH_HW_ADDRESS "hw-address" +#define NM_DEVICE_OLPC_MESH_COMPANION "companion" +#define NM_DEVICE_OLPC_MESH_BITRATE "bitrate" +#define NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL "active-channel" + +#ifndef NM_DEVICE_OLPC_MESH_DEFINED +#define NM_DEVICE_OLPC_MESH_DEFINED +typedef struct _NMDeviceOlpcMesh NMDeviceOlpcMesh; +#endif + +typedef struct _NMDeviceOlpcMeshClass NMDeviceOlpcMeshClass; +typedef struct _NMDeviceOlpcMeshPrivate NMDeviceOlpcMeshPrivate; + +struct _NMDeviceOlpcMesh +{ + NMDevice parent; +}; + +struct _NMDeviceOlpcMeshClass +{ + NMDeviceClass parent; + + /* Signals */ + void (*properties_changed) (NMDeviceOlpcMesh *device, + GHashTable *properties); +}; + + +GType nm_device_olpc_mesh_get_type (void); + +NMDevice *nm_device_olpc_mesh_new (const char *udi, + const char *iface, + const char *driver); + +G_END_DECLS + +#endif /* NM_DEVICE_OLPC_MESH_H */ diff --git a/src/nm-device-private.h b/src/nm-device-private.h new file mode 100644 index 00000000..371f17f1 --- /dev/null +++ b/src/nm-device-private.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2008 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_PRIVATE_H +#define NM_DEVICE_PRIVATE_H + +#include "nm-device.h" + +void nm_device_set_ip_iface (NMDevice *self, const char *iface); + +void nm_device_activate_schedule_stage3_ip_config_start (NMDevice *device); + +void nm_device_state_changed (NMDevice *device, + NMDeviceState state, + NMDeviceStateReason reason); + +gboolean nm_device_hw_bring_up (NMDevice *self, gboolean wait, gboolean *no_firmware); + +void nm_device_hw_take_down (NMDevice *self, gboolean block); + +void nm_device_handle_autoip4_event (NMDevice *self, + const char *event, + const char *address); + +gboolean nm_device_ip_config_should_fail (NMDevice *self, gboolean ip6); + +#endif /* NM_DEVICE_PRIVATE_H */ diff --git a/src/nm-device-wifi.c b/src/nm-device-wifi.c new file mode 100644 index 00000000..36be9e3e --- /dev/null +++ b/src/nm-device-wifi.c @@ -0,0 +1,3936 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include <glib.h> +#include <glib/gi18n.h> +#include <dbus/dbus.h> +#include <netinet/in.h> +#include <string.h> +#include <net/ethernet.h> +#include <iwlib.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <signal.h> +#include <unistd.h> + +#include "nm-glib-compat.h" +#include "nm-device.h" +#include "nm-device-wifi.h" +#include "nm-device-interface.h" +#include "nm-device-private.h" +#include "nm-utils.h" +#include "nm-logging.h" +#include "nm-marshal.h" +#include "NetworkManagerUtils.h" +#include "nm-activation-request.h" +#include "nm-supplicant-manager.h" +#include "nm-supplicant-interface.h" +#include "nm-supplicant-config.h" +#include "nm-properties-changed-signal.h" +#include "nm-setting-connection.h" +#include "nm-setting-wireless.h" +#include "nm-setting-wireless-security.h" +#include "nm-setting-8021x.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-ip6-config.h" +#include "nm-system.h" + +static gboolean impl_device_get_access_points (NMDeviceWifi *device, + GPtrArray **aps, + GError **err); + +#include "nm-device-wifi-glue.h" + + +/* All of these are in seconds */ +#define SCAN_INTERVAL_MIN 0 +#define SCAN_INTERVAL_STEP 20 +#define SCAN_INTERVAL_MAX 120 + +#define WIRELESS_SECRETS_TRIES "wireless-secrets-tries" + +static void device_interface_init (NMDeviceInterface *iface_class); + +G_DEFINE_TYPE_EXTENDED (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE, 0, + G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_INTERFACE, device_interface_init)) + +#define NM_DEVICE_WIFI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_WIFI, NMDeviceWifiPrivate)) + + +enum { + PROP_0, + PROP_HW_ADDRESS, + PROP_MODE, + PROP_BITRATE, + PROP_ACTIVE_ACCESS_POINT, + PROP_CAPABILITIES, + PROP_SCANNING, + PROP_IPW_RFKILL_STATE, + + LAST_PROP +}; + +enum { + ACCESS_POINT_ADDED, + ACCESS_POINT_REMOVED, + HIDDEN_AP_FOUND, + PROPERTIES_CHANGED, + SCANNING_ALLOWED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef enum { + NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS = 0, + NM_WIFI_ERROR_CONNECTION_INVALID, + NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, +} NMWifiError; + +#define NM_WIFI_ERROR (nm_wifi_error_quark ()) +#define NM_TYPE_WIFI_ERROR (nm_wifi_error_get_type ()) + +typedef struct SupplicantStateTask { + NMDeviceWifi *self; + guint32 new_state; + guint32 old_state; + gboolean mgr_task; + guint source_id; +} SupplicantStateTask; + +typedef struct Supplicant { + NMSupplicantManager *mgr; + NMSupplicantInterface *iface; + + /* signal handler ids */ + guint mgr_state_id; + guint iface_error_id; + guint iface_state_id; + guint iface_scanned_ap_id; + guint iface_scan_request_result_id; + guint iface_scan_results_id; + guint iface_con_state_id; + guint iface_notify_scanning_id; + + /* Timeouts and idles */ + guint iface_con_error_cb_id; + guint con_timeout_id; + + GSList *mgr_tasks; + GSList *iface_tasks; +} Supplicant; + +struct _NMDeviceWifiPrivate { + gboolean disposed; + + struct ether_addr hw_addr; + + /* Legacy rfkill for ipw2x00; will be fixed with 2.6.33 kernel */ + char * ipw_rfkill_path; + guint ipw_rfkill_id; + RfKillState ipw_rfkill_state; + + GByteArray * ssid; + gint8 invalid_strength_counter; + iwqual max_qual; + + gint8 num_freqs; + guint32 freqs[IW_MAX_FREQUENCIES]; + + GSList * ap_list; + NMAccessPoint * current_ap; + guint32 rate; + gboolean enabled; /* rfkilled or not */ + + glong scheduled_scan_time; + guint8 scan_interval; /* seconds */ + guint pending_scan_id; + + Supplicant supplicant; + + guint32 failed_link_count; + guint periodic_source_id; + guint link_timeout_id; + + /* Static options from driver */ + guint8 we_version; + guint32 capabilities; + gboolean has_scan_capa_ssid; +}; + +static guint32 nm_device_wifi_get_frequency (NMDeviceWifi *self); + +static gboolean request_wireless_scan (gpointer user_data); + +static void schedule_scan (NMDeviceWifi *self, gboolean backoff); + +static void cancel_pending_scan (NMDeviceWifi *self); + +static int wireless_qual_to_percent (const struct iw_quality *qual, + const struct iw_quality *max_qual); + +static void cleanup_association_attempt (NMDeviceWifi * self, + gboolean disconnect); + +static void remove_supplicant_timeouts (NMDeviceWifi *self); + +static void supplicant_iface_state_cb (NMSupplicantInterface * iface, + guint32 new_state, + guint32 old_state, + NMDeviceWifi *self); + +static void supplicant_iface_connection_state_cb (NMSupplicantInterface * iface, + guint32 new_state, + guint32 old_state, + NMDeviceWifi *self); + +static void supplicant_iface_scanned_ap_cb (NMSupplicantInterface * iface, + GHashTable *properties, + NMDeviceWifi * self); + +static void supplicant_iface_scan_request_result_cb (NMSupplicantInterface * iface, + gboolean success, + NMDeviceWifi * self); + +static void supplicant_iface_scan_results_cb (NMSupplicantInterface * iface, + guint32 num_bssids, + NMDeviceWifi * self); + +static void supplicant_mgr_state_cb (NMSupplicantInterface * iface, + guint32 new_state, + guint32 old_state, + NMDeviceWifi *self); + +static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface, + GParamSpec * pspec, + NMDeviceWifi * self); + +static guint32 nm_device_wifi_get_bitrate (NMDeviceWifi *self); + +static void cull_scan_list (NMDeviceWifi *self); + +static GQuark +nm_wifi_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm-wifi-error"); + return quark; +} + +/* This should really be standard. */ +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +static GType +nm_wifi_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + /* Connection was not a wireless connection. */ + ENUM_ENTRY (NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS, "ConnectionNotWireless"), + /* Connection was not a valid wireless connection. */ + ENUM_ENTRY (NM_WIFI_ERROR_CONNECTION_INVALID, "ConnectionInvalid"), + /* Connection does not apply to this device. */ + ENUM_ENTRY (NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, "ConnectionIncompatible"), + { 0, 0, 0 } + }; + etype = g_enum_register_static ("NMWifiError", values); + } + return etype; +} + +/* IPW rfkill handling (until 2.6.33) */ +RfKillState +nm_device_wifi_get_ipw_rfkill_state (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + char *contents = NULL; + RfKillState state = RFKILL_UNBLOCKED; + const char *str_state = NULL; + + if ( priv->ipw_rfkill_path + && g_file_get_contents (priv->ipw_rfkill_path, &contents, NULL, NULL)) { + contents = g_strstrip (contents); + + /* 0 - RF kill not enabled + * 1 - SW based RF kill active (sysfs) + * 2 - HW based RF kill active + * 3 - Both HW and SW baed RF kill active + */ + switch (contents[0]) { + case '1': + state = RFKILL_SOFT_BLOCKED; + str_state = "soft-blocked"; + break; + case '2': + case '3': + state = RFKILL_HARD_BLOCKED; + str_state = "hard-blocked"; + break; + case '0': + str_state = "unblocked"; + default: + break; + } + g_free (contents); + + nm_log_dbg (LOGD_RFKILL, "(%s): ipw rfkill state '%s'", + nm_device_get_iface (NM_DEVICE (self)), + str_state ? str_state : "(unknown)"); + } + + return state; +} + +static gboolean +ipw_rfkill_state_work (gpointer user_data) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + RfKillState old_state; + + old_state = priv->ipw_rfkill_state; + priv->ipw_rfkill_state = nm_device_wifi_get_ipw_rfkill_state (self); + if (priv->ipw_rfkill_state != old_state) + g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_IPW_RFKILL_STATE); + + return TRUE; +} + +/* + * nm_device_wifi_update_signal_strength + * + * Update the device's idea of the strength of its connection to the + * current access point. + * + */ +static void +nm_device_wifi_update_signal_strength (NMDeviceWifi *self, + NMAccessPoint *ap) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + const char *iface = nm_device_get_iface (NM_DEVICE (self)); + int fd, percent = -1; + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd >= 0) { + struct iwreq wrq; + struct iw_statistics stats; + + memset (&stats, 0, sizeof (stats)); + + wrq.u.data.pointer = &stats; + wrq.u.data.length = sizeof (stats); + wrq.u.data.flags = 1; /* Clear updated flag */ + strncpy (wrq.ifr_name, iface, IFNAMSIZ); + + if (ioctl (fd, SIOCGIWSTATS, &wrq) == 0) + percent = wireless_qual_to_percent (&stats.qual, &priv->max_qual); + close (fd); + } + + /* Try to smooth out the strength. Atmel cards, for example, will give no strength + * one second and normal strength the next. + */ + if (percent >= 0 || ++priv->invalid_strength_counter > 3) { + nm_ap_set_strength (ap, (gint8) percent); + priv->invalid_strength_counter = 0; + } +} + + +static gboolean +wireless_get_range (NMDeviceWifi *self, + struct iw_range *range, + guint32 *response_len) +{ + int fd, i = 26; + gboolean success = FALSE; + const char *iface; + struct iwreq wrq; + + g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), FALSE); + g_return_val_if_fail (range != NULL, FALSE); + + iface = nm_device_get_iface (NM_DEVICE (self)); + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_err (LOGD_HW, "(%s): couldn't open control socket.", iface); + return FALSE; + } + + memset (&wrq, 0, sizeof (struct iwreq)); + strncpy (wrq.ifr_name, iface, IFNAMSIZ); + wrq.u.data.pointer = (caddr_t) range; + wrq.u.data.length = sizeof (struct iw_range); + + /* Need to give some drivers time to recover after suspend/resume + * (ex ipw3945 takes a few seconds to talk to its regulatory daemon; + * see rh bz#362421) + */ + while (i-- > 0) { + if (ioctl (fd, SIOCGIWRANGE, &wrq) == 0) { + if (response_len) + *response_len = wrq.u.data.length; + success = TRUE; + break; + } else if (errno != EAGAIN) { + nm_log_err (LOGD_HW | LOGD_WIFI, + "(%s): couldn't get driver range information (%d).", + iface, errno); + break; + } + + g_usleep (G_USEC_PER_SEC / 4); + } + + if (i <= 0) { + nm_log_warn (LOGD_HW | LOGD_WIFI, + "(%s): driver took too long to respond to IWRANGE query.", + iface); + } + + close (fd); + return success; +} + +static guint32 +real_get_generic_capabilities (NMDevice *dev) +{ + int fd, err; + guint32 caps = NM_DEVICE_CAP_NONE, response_len = 0; + struct iwreq wrq; + struct iw_range range; + const char *iface = nm_device_get_iface (dev); + gboolean success; + + memset (&range, 0, sizeof (struct iw_range)); + success = wireless_get_range (NM_DEVICE_WIFI (dev), &range, &response_len); + if (!success) + return NM_DEVICE_CAP_NONE; + + /* Check for Wireless Extensions support >= 16 for wireless devices */ + if ((response_len < 300) || (range.we_version_compiled < 16)) { + nm_log_err (LOGD_HW | LOGD_WIFI, + "(%s): driver's Wireless Extensions version (%d) is too old.", + iface, range.we_version_compiled); + return NM_DEVICE_CAP_NONE; + } + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_err (LOGD_HW, "(%s): couldn't open control socket.", iface); + goto out; + } + + /* Cards that don't scan aren't supported */ + memset (&wrq, 0, sizeof (struct iwreq)); + strncpy (wrq.ifr_name, iface, IFNAMSIZ); + err = ioctl (fd, SIOCSIWSCAN, &wrq); + close (fd); + if ((err < 0) && (errno == EOPNOTSUPP)) + caps = NM_DEVICE_CAP_NONE; + else + caps |= NM_DEVICE_CAP_NM_SUPPORTED; + +out: + return caps; +} + +#define WPA_CAPS (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | \ + NM_WIFI_DEVICE_CAP_CIPHER_CCMP | \ + NM_WIFI_DEVICE_CAP_WPA | \ + NM_WIFI_DEVICE_CAP_RSN) + +static guint32 +get_wireless_capabilities (NMDeviceWifi *self, + iwrange * range, + guint32 data_len) +{ + guint32 minlen; + guint32 caps = NM_WIFI_DEVICE_CAP_NONE; + const char * iface; + + g_return_val_if_fail (self != NULL, NM_WIFI_DEVICE_CAP_NONE); + g_return_val_if_fail (range != NULL, NM_WIFI_DEVICE_CAP_NONE); + + iface = nm_device_get_iface (NM_DEVICE (self)); + + minlen = ((char *) &range->enc_capa) - (char *) range + sizeof (range->enc_capa); + + /* All drivers should support WEP by default */ + caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104; + + if ((data_len >= minlen) && range->we_version_compiled >= 18) { + if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) + caps |= NM_WIFI_DEVICE_CAP_CIPHER_TKIP; + + if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) + caps |= NM_WIFI_DEVICE_CAP_CIPHER_CCMP; + + if (range->enc_capa & IW_ENC_CAPA_WPA) + caps |= NM_WIFI_DEVICE_CAP_WPA; + + if (range->enc_capa & IW_ENC_CAPA_WPA2) + caps |= NM_WIFI_DEVICE_CAP_RSN; + + /* Check for cipher support but not WPA support */ + if ( (caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP)) + && !(caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN))) { + nm_log_warn (LOGD_WIFI, "%s: device supports WPA ciphers but not WPA protocol; " + "WPA unavailable.", iface); + caps &= ~WPA_CAPS; + } + + /* Check for WPA support but not cipher support */ + if ( (caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN)) + && !(caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP))) { + nm_log_warn (LOGD_WIFI, "%s: device supports WPA protocol but not WPA ciphers; " + "WPA unavailable.", iface); + caps &= ~WPA_CAPS; + } + } + + return caps; +} + + +static guint32 iw_freq_to_uint32 (struct iw_freq *freq) +{ + if (freq->e == 0) { + /* Some drivers report channel not frequency. Convert to a + * frequency; but this assumes that the device is in b/g mode. + */ + if ((freq->m >= 1) && (freq->m <= 13)) + return 2407 + (5 * freq->m); + else if (freq->m == 14) + return 2484; + } + + return (guint32) (((double) freq->m) * pow (10, freq->e) / 1000000); +} + + +/* Until a new wireless-tools comes out that has the defs and the structure, + * need to copy them here. + */ +/* Scan capability flags - in (struct iw_range *)->scan_capa */ +#define NM_IW_SCAN_CAPA_NONE 0x00 +#define NM_IW_SCAN_CAPA_ESSID 0x01 + +struct iw_range_with_scan_capa +{ + guint32 throughput; + guint32 min_nwid; + guint32 max_nwid; + guint16 old_num_channels; + guint8 old_num_frequency; + + guint8 scan_capa; +/* don't need the rest... */ +}; + + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + GObjectClass *klass; + NMDeviceWifi *self; + NMDeviceWifiPrivate *priv; + struct iw_range range; + struct iw_range_with_scan_capa *scan_capa_range; + guint32 response_len = 0; + gboolean success; + int i; + + klass = G_OBJECT_CLASS (nm_device_wifi_parent_class); + object = klass->constructor (type, n_construct_params, construct_params); + if (!object) + return NULL; + + self = NM_DEVICE_WIFI (object); + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + nm_log_dbg (LOGD_HW | LOGD_WIFI, "(%s): kernel ifindex %d", + nm_device_get_iface (NM_DEVICE (self)), + nm_device_get_ifindex (NM_DEVICE (self))); + + memset (&range, 0, sizeof (struct iw_range)); + success = wireless_get_range (NM_DEVICE_WIFI (object), &range, &response_len); + if (!success) + goto error; + + priv->max_qual.qual = range.max_qual.qual; + priv->max_qual.level = range.max_qual.level; + priv->max_qual.noise = range.max_qual.noise; + priv->max_qual.updated = range.max_qual.updated; + + priv->num_freqs = MIN (range.num_frequency, IW_MAX_FREQUENCIES); + for (i = 0; i < priv->num_freqs; i++) + priv->freqs[i] = iw_freq_to_uint32 (&range.freq[i]); + + priv->we_version = range.we_version_compiled; + + /* Check for the ability to scan specific SSIDs. Until the scan_capa + * field gets added to wireless-tools, need to work around that by casting + * to the custom structure. + */ + scan_capa_range = (struct iw_range_with_scan_capa *) ⦥ + if (scan_capa_range->scan_capa & NM_IW_SCAN_CAPA_ESSID) { + priv->has_scan_capa_ssid = TRUE; + nm_log_info (LOGD_HW | LOGD_WIFI, + "(%s): driver supports SSID scans (scan_capa 0x%02X).", + nm_device_get_iface (NM_DEVICE (self)), + scan_capa_range->scan_capa); + } else { + nm_log_info (LOGD_HW | LOGD_WIFI, + "(%s): driver does not support SSID scans (scan_capa 0x%02X).", + nm_device_get_iface (NM_DEVICE (self)), + scan_capa_range->scan_capa); + } + + /* 802.11 wireless-specific capabilities */ + priv->capabilities = get_wireless_capabilities (self, &range, response_len); + + /* Connect to the supplicant manager */ + priv->supplicant.mgr = nm_supplicant_manager_get (); + priv->supplicant.mgr_state_id = g_signal_connect (priv->supplicant.mgr, + "state", + G_CALLBACK (supplicant_mgr_state_cb), + self); + + /* The ipw2x00 drivers don't integrate with the kernel rfkill subsystem until + * 2.6.33. Thus all our nice libgudev magic is useless. So we get to poll. + * + * FIXME: when 2.6.33 comes lands, we can do some sysfs parkour to figure out + * if we need to poll or not by matching /sys/class/net/ethX/device to one + * of the /sys/class/rfkill/rfkillX/device links. If there's a match, we + * don't have to poll. + */ + priv->ipw_rfkill_path = g_strdup_printf ("/sys/class/net/%s/device/rf_kill", + nm_device_get_iface (NM_DEVICE (self))); + if (!g_file_test (priv->ipw_rfkill_path, G_FILE_TEST_IS_REGULAR)) { + g_free (priv->ipw_rfkill_path); + priv->ipw_rfkill_path = NULL; + } + priv->ipw_rfkill_state = nm_device_wifi_get_ipw_rfkill_state (self); + + return object; + +error: + g_object_unref (object); + return NULL; +} + +static gboolean +supplicant_interface_acquire (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + guint id, mgr_state; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (priv->supplicant.mgr != NULL, FALSE); + /* interface already acquired? */ + g_return_val_if_fail (priv->supplicant.iface == NULL, TRUE); + + mgr_state = nm_supplicant_manager_get_state (priv->supplicant.mgr); + g_return_val_if_fail (mgr_state == NM_SUPPLICANT_MANAGER_STATE_IDLE, FALSE); + + priv->supplicant.iface = nm_supplicant_manager_get_iface (priv->supplicant.mgr, + nm_device_get_iface (NM_DEVICE (self)), + TRUE); + if (priv->supplicant.iface == NULL) { + nm_log_err (LOGD_WIFI, "Couldn't initialize supplicant interface for %s.", + nm_device_get_iface (NM_DEVICE (self))); + return FALSE; + } + + id = g_signal_connect (priv->supplicant.iface, + "state", + G_CALLBACK (supplicant_iface_state_cb), + self); + priv->supplicant.iface_state_id = id; + + id = g_signal_connect (priv->supplicant.iface, + "scanned-ap", + G_CALLBACK (supplicant_iface_scanned_ap_cb), + self); + priv->supplicant.iface_scanned_ap_id = id; + + id = g_signal_connect (priv->supplicant.iface, + "scan-req-result", + G_CALLBACK (supplicant_iface_scan_request_result_cb), + self); + priv->supplicant.iface_scan_request_result_id = id; + + id = g_signal_connect (priv->supplicant.iface, + "scan-results", + G_CALLBACK (supplicant_iface_scan_results_cb), + self); + priv->supplicant.iface_scan_results_id = id; + + id = g_signal_connect (priv->supplicant.iface, + "connection-state", + G_CALLBACK (supplicant_iface_connection_state_cb), + self); + priv->supplicant.iface_con_state_id = id; + + id = g_signal_connect (priv->supplicant.iface, + "notify::scanning", + G_CALLBACK (supplicant_iface_notify_scanning_cb), + self); + priv->supplicant.iface_notify_scanning_id = id; + + return TRUE; +} + +static void +finish_supplicant_task (SupplicantStateTask *task, gboolean remove_source) +{ + NMDeviceWifi *self = task->self; + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + /* idle/timeout handlers should pass FALSE for remove_source, since they + * will tell glib to remove their source from the mainloop by returning + * FALSE when they exit. When called from this NMDevice's dispose handler, + * remove_source should be TRUE to cancel all outstanding idle/timeout + * handlers asynchronously. + */ + if (task->source_id && remove_source) + g_source_remove (task->source_id); + + if (task->mgr_task) + priv->supplicant.mgr_tasks = g_slist_remove (priv->supplicant.mgr_tasks, task); + else + priv->supplicant.iface_tasks = g_slist_remove (priv->supplicant.iface_tasks, task); + + memset (task, 0, sizeof (SupplicantStateTask)); + g_slice_free (SupplicantStateTask, task); +} + +static void +remove_supplicant_interface_error_handler (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + if (!priv->supplicant.iface) + return; + + if (priv->supplicant.iface_error_id > 0) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_error_id); + priv->supplicant.iface_error_id = 0; + } + + if (priv->supplicant.iface_con_error_cb_id > 0) { + g_source_remove (priv->supplicant.iface_con_error_cb_id); + priv->supplicant.iface_con_error_cb_id = 0; + } +} + +static void +supplicant_interface_release (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv; + + g_return_if_fail (self != NULL); + + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + cancel_pending_scan (self); + + /* Reset the scan interval to be pretty frequent when disconnected */ + priv->scan_interval = SCAN_INTERVAL_MIN + SCAN_INTERVAL_STEP; + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): reset scanning interval to %d seconds", + nm_device_get_iface (NM_DEVICE (self))); + + remove_supplicant_interface_error_handler (self); + + /* Clean up all pending supplicant interface state idle tasks */ + while (priv->supplicant.iface_tasks) + finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE); + + if (priv->supplicant.iface_state_id > 0) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_state_id); + priv->supplicant.iface_state_id = 0; + } + + if (priv->supplicant.iface_scanned_ap_id > 0) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_scanned_ap_id); + priv->supplicant.iface_scanned_ap_id = 0; + } + + if (priv->supplicant.iface_scan_request_result_id > 0) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_scan_request_result_id); + priv->supplicant.iface_scan_request_result_id = 0; + } + + if (priv->supplicant.iface_scan_results_id > 0) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_scan_results_id); + priv->supplicant.iface_scan_results_id = 0; + } + + if (priv->supplicant.iface_con_state_id > 0) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_con_state_id); + priv->supplicant.iface_con_state_id = 0; + } + + if (priv->supplicant.iface_notify_scanning_id > 0) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_notify_scanning_id); + priv->supplicant.iface_notify_scanning_id = 0; + } + + if (priv->supplicant.iface) { + /* Tell the supplicant to disconnect from the current AP */ + nm_supplicant_interface_disconnect (priv->supplicant.iface); + + nm_supplicant_manager_release_iface (priv->supplicant.mgr, priv->supplicant.iface); + priv->supplicant.iface = NULL; + } +} + +static NMAccessPoint * +get_active_ap (NMDeviceWifi *self, + NMAccessPoint *ignore_ap, + gboolean match_hidden) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + const char *iface = nm_device_get_iface (NM_DEVICE (self)); + struct ether_addr bssid; + const GByteArray *ssid; + GSList *iter; + int i = 0; + NMAccessPoint *match_nofreq = NULL; + gboolean found_a_band = FALSE; + gboolean found_bg_band = FALSE; + NM80211Mode devmode; + guint32 devfreq; + + nm_device_wifi_get_bssid (self, &bssid); + nm_log_dbg (LOGD_WIFI, "(%s): active BSSID: %02x:%02x:%02x:%02x:%02x:%02x", + iface, + bssid.ether_addr_octet[0], bssid.ether_addr_octet[1], + bssid.ether_addr_octet[2], bssid.ether_addr_octet[3], + bssid.ether_addr_octet[4], bssid.ether_addr_octet[5]); + + if (!nm_ethernet_address_is_valid (&bssid)) + return NULL; + + ssid = nm_device_wifi_get_ssid (self); + nm_log_dbg (LOGD_WIFI, "(%s): active SSID: %s%s%s", + iface, + ssid ? "'" : "", + ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)", + ssid ? "'" : ""); + + devmode = nm_device_wifi_get_mode (self); + devfreq = nm_device_wifi_get_frequency (self); + + /* When matching hidden APs, do a second pass that ignores the SSID check, + * because NM might not yet know the SSID of the hidden AP in the scan list + * and therefore it won't get matched the first time around. + */ + while (i++ < (match_hidden ? 2 : 1)) { + nm_log_dbg (LOGD_WIFI, " Pass #%d %s", i, i > 1 ? "(ignoring SSID)" : ""); + + /* Find this SSID + BSSID in the device's AP list */ + for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) { + NMAccessPoint *ap = NM_AP (iter->data); + const struct ether_addr *ap_bssid = nm_ap_get_address (ap); + const GByteArray *ap_ssid = nm_ap_get_ssid (ap); + NM80211Mode apmode; + guint32 apfreq; + + nm_log_dbg (LOGD_WIFI, " AP: %s%s%s %02x:%02x:%02x:%02x:%02x:%02x", + ap_ssid ? "'" : "", + ap_ssid ? nm_utils_escape_ssid (ap_ssid->data, ap_ssid->len) : "(none)", + ap_ssid ? "'" : "", + ap_bssid->ether_addr_octet[0], ap_bssid->ether_addr_octet[1], + ap_bssid->ether_addr_octet[2], ap_bssid->ether_addr_octet[3], + ap_bssid->ether_addr_octet[4], ap_bssid->ether_addr_octet[5]); + + if (ignore_ap && (ap == ignore_ap)) { + nm_log_dbg (LOGD_WIFI, " ignored"); + continue; + } + + if (memcmp (bssid.ether_addr_octet, ap_bssid->ether_addr_octet, ETH_ALEN)) { + nm_log_dbg (LOGD_WIFI, " BSSID mismatch"); + continue; + } + + if ((i == 0) && !nm_utils_same_ssid (ssid, ap_ssid, TRUE)) { + nm_log_dbg (LOGD_WIFI, " SSID mismatch"); + continue; + } + + apmode = nm_ap_get_mode (ap); + if (devmode != apmode) { + nm_log_dbg (LOGD_WIFI, " mode mismatch (device %d, ap %d)", + devmode, apmode); + continue; + } + + apfreq = nm_ap_get_freq (ap); + if (devfreq != apfreq) { + nm_log_dbg (LOGD_WIFI, " frequency mismatch (device %u, ap %u)", + devfreq, apfreq); + + if (match_nofreq == NULL) + match_nofreq = ap; + + if (apfreq > 4000) + found_a_band = TRUE; + else if (apfreq > 2000) + found_bg_band = TRUE; + continue; + } + + // FIXME: handle security settings here too + nm_log_dbg (LOGD_WIFI, " matched"); + return ap; + } + } + + /* Some proprietary drivers (wl.o) report tuned frequency (like when + * scanning) instead of the associated AP's frequency. This is a great + * example of how WEXT is underspecified. We use frequency to find the + * active AP in the scan list because some configurations use the same + * SSID/BSSID on the 2GHz and 5GHz bands simultaneously, and we need to + * make sure we get the right AP in the right band. This configuration + * is uncommon though, and the frequency check penalizes closed drivers we + * can't fix. Because we're not total dicks, ignore the frequency condition + * if the associated BSSID/SSID exists only in one band since that's most + * likely the AP we want. + */ + if (match_nofreq && (found_a_band != found_bg_band)) { + const struct ether_addr *ap_bssid = nm_ap_get_address (match_nofreq); + const GByteArray *ap_ssid = nm_ap_get_ssid (match_nofreq); + + nm_log_dbg (LOGD_WIFI, " matched %s%s%s %02x:%02x:%02x:%02x:%02x:%02x", + ap_ssid ? "'" : "", + ap_ssid ? nm_utils_escape_ssid (ap_ssid->data, ap_ssid->len) : "(none)", + ap_ssid ? "'" : "", + ap_bssid->ether_addr_octet[0], ap_bssid->ether_addr_octet[1], + ap_bssid->ether_addr_octet[2], ap_bssid->ether_addr_octet[3], + ap_bssid->ether_addr_octet[4], ap_bssid->ether_addr_octet[5]); + + return match_nofreq; + } + + nm_log_dbg (LOGD_WIFI, " No matching AP found."); + return NULL; +} + +static void +set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap) +{ + NMDeviceWifiPrivate *priv; + char *old_path = NULL; + NMAccessPoint *old_ap; + + g_return_if_fail (NM_IS_DEVICE_WIFI (self)); + + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + old_ap = priv->current_ap; + + if (old_ap) { + old_path = g_strdup (nm_ap_get_dbus_path (old_ap)); + priv->current_ap = NULL; + } + + if (new_ap) { + priv->current_ap = g_object_ref (new_ap); + + /* Move the current AP to the front of the scan list. Since we + * do a lot of searches looking for the current AP, it saves + * time to have it in front. + */ + priv->ap_list = g_slist_remove (priv->ap_list, new_ap); + priv->ap_list = g_slist_prepend (priv->ap_list, new_ap); + } + + /* Unref old AP here to ensure object lives if new_ap == old_ap */ + if (old_ap) + g_object_unref (old_ap); + + /* Only notify if it's really changed */ + if ( (!old_path && new_ap) + || (old_path && !new_ap) + || (old_path && new_ap && strcmp (old_path, nm_ap_get_dbus_path (new_ap)))) + g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT); + + g_free (old_path); +} + +static void +periodic_update (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMAccessPoint *new_ap; + guint32 new_rate; + + /* In IBSS mode, most newer firmware/drivers do "BSS coalescing" where + * multiple IBSS stations using the same SSID will eventually switch to + * using the same BSSID to avoid network segmentation. When this happens, + * the card's reported BSSID will change, but the the new BSS may not + * be in the scan list, since scanning isn't done in ad-hoc mode for + * various reasons. So pull the BSSID from the card and update the + * current AP with it, if the current AP is adhoc. + */ + if (priv->current_ap && (nm_ap_get_mode (priv->current_ap) == NM_802_11_MODE_ADHOC)) { + struct ether_addr bssid = { {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} }; + + nm_device_wifi_get_bssid (self, &bssid); + /* 0x02 means "locally administered" and should be OR-ed into + * the first byte of IBSS BSSIDs. + */ + if ( (bssid.ether_addr_octet[0] & 0x02) + && nm_ethernet_address_is_valid (&bssid)) + nm_ap_set_address (priv->current_ap, &bssid); + } + + new_ap = get_active_ap (self, NULL, FALSE); + if (new_ap) + nm_device_wifi_update_signal_strength (self, new_ap); + + if ((new_ap || priv->current_ap) && (new_ap != priv->current_ap)) { + const struct ether_addr *new_bssid = NULL; + const GByteArray *new_ssid = NULL; + const struct ether_addr *old_bssid = NULL; + const GByteArray *old_ssid = NULL; + char *old_addr = NULL, *new_addr = NULL; + + if (new_ap) { + new_bssid = nm_ap_get_address (new_ap); + new_addr = nm_ether_ntop (new_bssid); + new_ssid = nm_ap_get_ssid (new_ap); + } + + if (priv->current_ap) { + old_bssid = nm_ap_get_address (priv->current_ap); + old_addr = nm_ether_ntop (old_bssid); + old_ssid = nm_ap_get_ssid (priv->current_ap); + } + + nm_log_info (LOGD_WIFI, "(%s): roamed from BSSID %s (%s) to %s (%s)", + nm_device_get_iface (NM_DEVICE (self)), + old_addr ? old_addr : "(none)", + old_ssid ? nm_utils_escape_ssid (old_ssid->data, old_ssid->len) : "(none)", + new_addr ? new_addr : "(none)", + new_ssid ? nm_utils_escape_ssid (new_ssid->data, new_ssid->len) : "(none)"); + g_free (old_addr); + g_free (new_addr); + + set_current_ap (self, new_ap); + } + + new_rate = nm_device_wifi_get_bitrate (self); + if (new_rate != priv->rate) { + priv->rate = new_rate; + g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_BITRATE); + } +} + +/* + * nm_device_wifi_periodic_update + * + * Periodically update device statistics. + * + */ +static gboolean +nm_device_wifi_periodic_update (gpointer data) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (data); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMDeviceState state; + + /* BSSID and signal strength have meaningful values only if the device + is activated and not scanning */ + state = nm_device_get_state (NM_DEVICE (self)); + if (state != NM_DEVICE_STATE_ACTIVATED) + goto out; + + if (nm_supplicant_interface_get_scanning (priv->supplicant.iface)) + goto out; + + periodic_update (self); + +out: + return TRUE; +} + +static gboolean +real_hw_is_up (NMDevice *device) +{ + return nm_system_device_is_up (device); +} + +static gboolean +real_hw_bring_up (NMDevice *device, gboolean *no_firmware) +{ + if (!NM_DEVICE_WIFI_GET_PRIVATE (device)->enabled) + return FALSE; + + return nm_system_device_set_up_down (device, TRUE, no_firmware); +} + +static void +real_hw_take_down (NMDevice *dev) +{ + nm_system_device_set_up_down (dev, FALSE, NULL); +} + +static gboolean +real_is_up (NMDevice *device) +{ + if (!NM_DEVICE_WIFI_GET_PRIVATE (device)->periodic_source_id) + return FALSE; + + return TRUE; +} + +static gboolean +real_bring_up (NMDevice *dev) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + priv->periodic_source_id = g_timeout_add_seconds (6, nm_device_wifi_periodic_update, self); + return TRUE; +} + +static void +access_point_removed (NMDeviceWifi *device, NMAccessPoint *ap) +{ + g_signal_emit (device, signals[ACCESS_POINT_REMOVED], 0, ap); +} + +static void +remove_all_aps (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + /* Remove outdated APs */ + while (g_slist_length (priv->ap_list)) { + NMAccessPoint *ap = NM_AP (priv->ap_list->data); + + access_point_removed (self, ap); + priv->ap_list = g_slist_remove (priv->ap_list, ap); + g_object_unref (ap); + } + g_slist_free (priv->ap_list); + priv->ap_list = NULL; +} + +static void +real_take_down (NMDevice *dev) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + if (priv->periodic_source_id) { + g_source_remove (priv->periodic_source_id); + priv->periodic_source_id = 0; + } + + cleanup_association_attempt (self, TRUE); + set_current_ap (self, NULL); + remove_all_aps (self); +} + +static void +real_deactivate_quickly (NMDevice *dev) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMAccessPoint *orig_ap = nm_device_wifi_get_activation_ap (self); + + cleanup_association_attempt (self, TRUE); + + set_current_ap (self, NULL); + priv->rate = 0; + + /* If the AP is 'fake', i.e. it wasn't actually found from + * a scan but the user tried to connect to it manually (maybe it + * was non-broadcasting or something) get rid of it, because 'fake' + * APs should only live for as long as we're connected to them. Fixes + * a bug where user-created Ad-Hoc APs are never removed from the scan + * list, because scanning is disabled while in Ad-Hoc mode (for stability), + * and thus the AP culling never happens. (bgo #569241) + */ + if (orig_ap && nm_ap_get_fake (orig_ap)) { + access_point_removed (self, orig_ap); + priv->ap_list = g_slist_remove (priv->ap_list, orig_ap); + g_object_unref (orig_ap); + } +} + +static void +real_deactivate (NMDevice *dev) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + + nm_device_wifi_set_mode (self, NM_802_11_MODE_INFRA); + /* FIXME: Should we reset the scan interval here? */ +/* nm_device_wifi_set_scan_interval (app_data, self, NM_WIRELESS_SCAN_INTERVAL_ACTIVE); */ +} + +static gboolean +real_check_connection_compatible (NMDevice *device, + NMConnection *connection, + GError **error) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (device); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMSettingConnection *s_con; + NMSettingWireless *s_wireless; + const GByteArray *mac; + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + + if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_WIRELESS_SETTING_NAME)) { + g_set_error (error, + NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS, + "The connection was not a WiFi connection."); + return FALSE; + } + + s_wireless = NM_SETTING_WIRELESS (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS)); + if (!s_wireless) { + g_set_error (error, + NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_INVALID, + "The connection was not a valid WiFi connection."); + return FALSE; + } + + mac = nm_setting_wireless_get_mac_address (s_wireless); + if (mac && memcmp (mac->data, &(priv->hw_addr.ether_addr_octet), ETH_ALEN)) { + g_set_error (error, + NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, + "The connection's MAC address did not match this device."); + return FALSE; + } + + // FIXME: check channel/freq/band against bands the hardware supports + // FIXME: check encryption against device capabilities + // FIXME: check bitrate against device capabilities + + return TRUE; +} + +static gboolean +real_is_available (NMDevice *dev) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMSupplicantInterface *sup_iface; + guint32 state; + + if (!priv->enabled) { + nm_log_dbg (LOGD_WIFI, "(%s): not available because not enabled", + nm_device_get_iface (dev)); + return FALSE; + } + + sup_iface = priv->supplicant.iface; + if (!sup_iface) { + nm_log_dbg (LOGD_WIFI, "(%s): not available because supplicant not running", + nm_device_get_iface (dev)); + return FALSE; + } + + state = nm_supplicant_interface_get_state (sup_iface); + if (state != NM_SUPPLICANT_INTERFACE_STATE_READY) { + nm_log_dbg (LOGD_WIFI, "(%s): not available because supplicant interface not ready", + nm_device_get_iface (dev)); + return FALSE; + } + + return TRUE; +} + +static NMConnection * +real_get_best_auto_connection (NMDevice *dev, + GSList *connections, + char **specific_object) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + GSList *iter, *ap_iter; + + for (iter = connections; iter; iter = g_slist_next (iter)) { + NMConnection *connection = NM_CONNECTION (iter->data); + NMSettingConnection *s_con; + NMSettingWireless *s_wireless; + const GByteArray *mac; + NMSettingIP4Config *s_ip4; + const char *method = NULL; + + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + if (s_con == NULL) + continue; + if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_WIRELESS_SETTING_NAME)) + continue; + if (!nm_setting_connection_get_autoconnect (s_con)) + continue; + + s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); + if (!s_wireless) + continue; + + mac = nm_setting_wireless_get_mac_address (s_wireless); + if (mac && memcmp (mac->data, priv->hw_addr.ether_addr_octet, ETH_ALEN)) + continue; + + /* Use the connection if it's a shared connection */ + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + if (s_ip4) + method = nm_setting_ip4_config_get_method (s_ip4); + + if (s_ip4 && !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) + return connection; + + for (ap_iter = priv->ap_list; ap_iter; ap_iter = g_slist_next (ap_iter)) { + NMAccessPoint *ap = NM_AP (ap_iter->data); + + if (nm_ap_check_compatible (ap, connection)) { + /* All good; connection is usable */ + *specific_object = (char *) nm_ap_get_dbus_path (ap); + return connection; + } + } + } + return NULL; +} + +/* + * nm_device_wifi_get_address + * + * Get a device's hardware address + * + */ +void +nm_device_wifi_get_address (NMDeviceWifi *self, + struct ether_addr *addr) +{ + NMDeviceWifiPrivate *priv; + + g_return_if_fail (self != NULL); + g_return_if_fail (addr != NULL); + + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + memcpy (addr, &(priv->hw_addr), sizeof (struct ether_addr)); +} + +static void +nm_device_wifi_ap_list_print (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + GSList * elt; + int i = 0; + + g_return_if_fail (NM_IS_DEVICE_WIFI (self)); + + nm_log_dbg (LOGD_WIFI_SCAN, "Current AP list:"); + for (elt = priv->ap_list; elt; elt = g_slist_next (elt), i++) { + NMAccessPoint * ap = NM_AP (elt->data); + nm_ap_print_self (ap, "AP: "); + } + nm_log_dbg (LOGD_WIFI_SCAN, "Current AP list: done"); +} + +static gboolean +impl_device_get_access_points (NMDeviceWifi *self, + GPtrArray **aps, + GError **err) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + GSList *elt; + + *aps = g_ptr_array_new (); + + for (elt = priv->ap_list; elt; elt = g_slist_next (elt)) { + NMAccessPoint * ap = NM_AP (elt->data); + + if (nm_ap_get_ssid (ap)) + g_ptr_array_add (*aps, g_strdup (nm_ap_get_dbus_path (ap))); + } + return TRUE; +} + +/* + * nm_device_get_mode + * + * Get managed/infrastructure/adhoc mode on a device + * + */ +NM80211Mode +nm_device_wifi_get_mode (NMDeviceWifi *self) +{ + int fd; + NM80211Mode mode = NM_802_11_MODE_UNKNOWN; + const char *iface; + struct iwreq wrq; + + g_return_val_if_fail (self != NULL, NM_802_11_MODE_UNKNOWN); + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) + goto out; + + memset (&wrq, 0, sizeof (struct iwreq)); + iface = nm_device_get_iface (NM_DEVICE (self)); + strncpy (wrq.ifr_name, iface, IFNAMSIZ); + + if (ioctl (fd, SIOCGIWMODE, &wrq) == 0) { + switch (wrq.u.mode) { + case IW_MODE_ADHOC: + mode = NM_802_11_MODE_ADHOC; + break; + case IW_MODE_INFRA: + mode = NM_802_11_MODE_INFRA; + break; + default: + break; + } + } else { + if (errno != ENODEV) { + nm_log_warn (LOGD_HW | LOGD_WIFI, + "(%s): error %d getting card mode", + iface, strerror (errno)); + } + } + close (fd); + +out: + return mode; +} + + +/* + * nm_device_wifi_set_mode + * + * Set managed/infrastructure/adhoc mode on a device + * + */ +gboolean +nm_device_wifi_set_mode (NMDeviceWifi *self, const NM80211Mode mode) +{ + int fd; + const char *iface; + gboolean success = FALSE; + struct iwreq wrq; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail ((mode == NM_802_11_MODE_INFRA) || (mode == NM_802_11_MODE_ADHOC), FALSE); + + if (nm_device_wifi_get_mode (self) == mode) + return TRUE; + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) + goto out; + + memset (&wrq, 0, sizeof (struct iwreq)); + switch (mode) { + case NM_802_11_MODE_ADHOC: + wrq.u.mode = IW_MODE_ADHOC; + break; + case NM_802_11_MODE_INFRA: + wrq.u.mode = IW_MODE_INFRA; + break; + default: + goto out; + } + + iface = nm_device_get_iface (NM_DEVICE (self)); + strncpy (wrq.ifr_name, iface, IFNAMSIZ); + + if (ioctl (fd, SIOCSIWMODE, &wrq) < 0) { + if (errno != ENODEV) { + nm_log_err (LOGD_HW | LOGD_WIFI, + "(%s): error setting mode %d", + iface, mode, strerror (errno)); + } + } else + success = TRUE; + close (fd); + +out: + return success; +} + + +/* + * nm_device_wifi_get_frequency + * + * Get current frequency + * + */ +static guint32 +nm_device_wifi_get_frequency (NMDeviceWifi *self) +{ + int fd; + guint32 freq = 0; + const char *iface; + struct iwreq wrq; + + g_return_val_if_fail (self != NULL, 0); + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) + return 0; + + memset (&wrq, 0, sizeof (struct iwreq)); + iface = nm_device_get_iface (NM_DEVICE (self)); + strncpy (wrq.ifr_name, iface, IFNAMSIZ); + + if (ioctl (fd, SIOCGIWFREQ, &wrq) < 0) { + nm_log_warn (LOGD_HW | LOGD_WIFI, + "(%s): error getting frequency: %s", + iface, strerror (errno)); + } else + freq = iw_freq_to_uint32 (&wrq.u.freq); + + close (fd); + return freq; +} + +/* + * wireless_stats_to_percent + * + * Convert an iw_stats structure from a scan or the card into + * a magical signal strength percentage. + * + */ +static int +wireless_qual_to_percent (const struct iw_quality *qual, + const struct iw_quality *max_qual) +{ + int percent = -1; + int level_percent = -1; + + g_return_val_if_fail (qual != NULL, -1); + g_return_val_if_fail (max_qual != NULL, -1); + + nm_log_dbg (LOGD_WIFI, + "QL: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X ** MAX: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X", + (__s8) qual->qual, qual->qual, qual->qual, + (__s8) qual->level, qual->level, qual->level, + (__s8) qual->noise, qual->noise, qual->noise, + qual->updated, + (__s8) max_qual->qual, max_qual->qual, max_qual->qual, + (__s8) max_qual->level, max_qual->level, max_qual->level, + (__s8) max_qual->noise, max_qual->noise, max_qual->noise, + max_qual->updated); + + /* Try using the card's idea of the signal quality first as long as it tells us what the max quality is. + * Drivers that fill in quality values MUST treat them as percentages, ie the "Link Quality" MUST be + * bounded by 0 and max_qual->qual, and MUST change in a linear fashion. Within those bounds, drivers + * are free to use whatever they want to calculate "Link Quality". + */ + if ((max_qual->qual != 0) && !(max_qual->updated & IW_QUAL_QUAL_INVALID) && !(qual->updated & IW_QUAL_QUAL_INVALID)) + percent = (int)(100 * ((double)qual->qual / (double)max_qual->qual)); + + /* If the driver doesn't specify a complete and valid quality, we have two options: + * + * 1) dBm: driver must specify max_qual->level = 0, and have valid values for + * qual->level and (qual->noise OR max_qual->noise) + * 2) raw RSSI: driver must specify max_qual->level > 0, and have valid values for + * qual->level and max_qual->level + * + * This is the WEXT spec. If this interpretation is wrong, I'll fix it. Otherwise, + * If drivers don't conform to it, they are wrong and need to be fixed. + */ + + if ( (max_qual->level == 0) && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level == 0 */ + && !(qual->updated & IW_QUAL_LEVEL_INVALID) /* Must have valid qual->level */ + && ( ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID)) /* Must have valid max_qual->noise */ + || ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID))) /* OR valid qual->noise */ + ) { + /* Absolute power values (dBm) */ + + /* Reasonable fallbacks for dumb drivers that don't specify either level. */ + #define FALLBACK_NOISE_FLOOR_DBM -90 + #define FALLBACK_SIGNAL_MAX_DBM -20 + int max_level = FALLBACK_SIGNAL_MAX_DBM; + int noise = FALLBACK_NOISE_FLOOR_DBM; + int level = qual->level - 0x100; + + level = CLAMP (level, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM); + + if ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID)) + noise = qual->noise - 0x100; + else if ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID)) + noise = max_qual->noise - 0x100; + noise = CLAMP (noise, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM); + + /* A sort of signal-to-noise ratio calculation */ + level_percent = (int)(100 - 70 *( + ((double)max_level - (double)level) / + ((double)max_level - (double)noise))); + nm_log_dbg (LOGD_WIFI, "QL1: level_percent is %d. max_level %d, level %d, noise_floor %d.", + level_percent, max_level, level, noise); + } else if ( (max_qual->level != 0) + && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level as upper bound */ + && !(qual->updated & IW_QUAL_LEVEL_INVALID)) { + /* Relative power values (RSSI) */ + + int level = qual->level; + + /* Signal level is relavtive (0 -> max_qual->level) */ + level = CLAMP (level, 0, max_qual->level); + level_percent = (int)(100 * ((double)level / (double)max_qual->level)); + nm_log_dbg (LOGD_WIFI, "QL2: level_percent is %d. max_level %d, level %d.", + level_percent, max_qual->level, level); + } else if (percent == -1) { + nm_log_dbg (LOGD_WIFI, "QL: Could not get quality %% value from driver. Driver is probably buggy."); + } + + /* If the quality percent was 0 or doesn't exist, then try to use signal levels instead */ + if ((percent < 1) && (level_percent >= 0)) + percent = level_percent; + + nm_log_dbg (LOGD_WIFI, "QL: Final quality percent is %d (%d).", + percent, CLAMP (percent, 0, 100)); + return (CLAMP (percent, 0, 100)); +} + + +/* + * nm_device_wifi_get_ssid + * + * If a device is wireless, return the ssid that it is attempting + * to use. + */ +const GByteArray * +nm_device_wifi_get_ssid (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv; + int sk; + struct iwreq wrq; + char ssid[IW_ESSID_MAX_SIZE + 2]; + guint32 len; + + g_return_val_if_fail (self != NULL, NULL); + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + sk = socket (AF_INET, SOCK_DGRAM, 0); + if (!sk) { + nm_log_err (LOGD_HW, "couldn't create socket: %d.", errno); + return NULL; + } + + memset (ssid, 0, sizeof (ssid)); + wrq.u.essid.pointer = (caddr_t) &ssid; + wrq.u.essid.length = sizeof (ssid); + wrq.u.essid.flags = 0; + strncpy (wrq.ifr_name, nm_device_get_iface (NM_DEVICE (self)), IFNAMSIZ); + + if (ioctl (sk, SIOCGIWESSID, &wrq) < 0) { + nm_log_err (LOGD_HW | LOGD_WIFI, "(%s): couldn't get SSID: %d", + nm_device_get_iface (NM_DEVICE (self)), errno); + goto out; + } + + if (priv->ssid) { + g_byte_array_free (priv->ssid, TRUE); + priv->ssid = NULL; + } + + len = wrq.u.essid.length; + if (!nm_utils_is_empty_ssid ((guint8 *) ssid, len)) { + /* Some drivers include nul termination in the SSID, so let's + * remove it here before further processing. WE-21 changes this + * to explicitly require the length _not_ to include nul + * termination. */ + if (len > 0 && ssid[len - 1] == '\0' && priv->we_version < 21) + len--; + + priv->ssid = g_byte_array_sized_new (len); + g_byte_array_append (priv->ssid, (const guint8 *) ssid, len); + } + +out: + close (sk); + return priv->ssid; +} + + +/* + * nm_device_wifi_get_bitrate + * + * For wireless devices, get the bitrate to broadcast/receive at. + * Returned value is rate in Kb/s. + * + */ +static guint32 +nm_device_wifi_get_bitrate (NMDeviceWifi *self) +{ + int err = -1, fd; + struct iwreq wrq; + + g_return_val_if_fail (self != NULL, 0); + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) + return 0; + + memset (&wrq, 0, sizeof (wrq)); + strncpy (wrq.ifr_name, nm_device_get_iface (NM_DEVICE (self)), IFNAMSIZ); + err = ioctl (fd, SIOCGIWRATE, &wrq); + close (fd); + + return ((err == 0) ? wrq.u.bitrate.value / 1000 : 0); +} + +/* + * nm_device_get_bssid + * + * If a device is wireless, get the access point's ethernet address + * that the card is associated with. + */ +void +nm_device_wifi_get_bssid (NMDeviceWifi *self, + struct ether_addr *bssid) +{ + int fd; + struct iwreq wrq; + + g_return_if_fail (self != NULL); + g_return_if_fail (bssid != NULL); + + memset (bssid, 0, sizeof (struct ether_addr)); + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_err (LOGD_WIFI, "failed to open control socket."); + return; + } + + memset (&wrq, 0, sizeof (wrq)); + strncpy (wrq.ifr_name, nm_device_get_iface (NM_DEVICE (self)), IFNAMSIZ); + if (ioctl (fd, SIOCGIWAP, &wrq) == 0) + memcpy (bssid->ether_addr_octet, &(wrq.u.ap_addr.sa_data), ETH_ALEN); + + close (fd); +} + + +static gboolean +scanning_allowed (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + guint32 sup_state; + NMActRequest *req; + + g_return_val_if_fail (priv->supplicant.iface != NULL, FALSE); + + switch (nm_device_get_state (NM_DEVICE (self))) { + case NM_DEVICE_STATE_UNKNOWN: + case NM_DEVICE_STATE_UNMANAGED: + case NM_DEVICE_STATE_UNAVAILABLE: + case NM_DEVICE_STATE_PREPARE: + case NM_DEVICE_STATE_CONFIG: + case NM_DEVICE_STATE_NEED_AUTH: + case NM_DEVICE_STATE_IP_CONFIG: + /* Don't scan when unusable or activating */ + return FALSE; + case NM_DEVICE_STATE_DISCONNECTED: + case NM_DEVICE_STATE_FAILED: + /* Can always scan when disconnected */ + return TRUE; + case NM_DEVICE_STATE_ACTIVATED: + /* Need to do further checks when activated */ + break; + } + + /* Don't scan if the supplicant is busy */ + sup_state = nm_supplicant_interface_get_connection_state (priv->supplicant.iface); + if ( sup_state == NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATING + || sup_state == NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATED + || sup_state == NM_SUPPLICANT_INTERFACE_CON_STATE_4WAY_HANDSHAKE + || sup_state == NM_SUPPLICANT_INTERFACE_CON_STATE_GROUP_HANDSHAKE + || nm_supplicant_interface_get_scanning (priv->supplicant.iface)) + return FALSE; + + req = nm_device_get_act_request (NM_DEVICE (self)); + if (req) { + NMConnection *connection; + NMSettingIP4Config *s_ip4; + NMSettingWireless *s_wifi; + const char *ip4_method = NULL; + const GByteArray *bssid; + + /* Don't scan when a shared connection is active; it makes drivers mad */ + connection = nm_act_request_get_connection (req); + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + if (s_ip4) + ip4_method = nm_setting_ip4_config_get_method (s_ip4); + + if (s_ip4 && !strcmp (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) + return FALSE; + + /* Don't scan when the connection is locked to a specifc AP, since + * intra-ESS roaming (which requires periodic scanning) isn't being + * used due to the specific AP lock. (bgo #513820) + */ + s_wifi = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); + g_assert (s_wifi); + bssid = nm_setting_wireless_get_bssid (s_wifi); + if (bssid && bssid->len == ETH_ALEN) + return FALSE; + } + + return TRUE; +} + +static gboolean +scanning_allowed_accumulator (GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, + gpointer data) +{ + if (!g_value_get_boolean (handler_return)) + g_value_set_boolean (return_accu, FALSE); + return TRUE; +} + +static gboolean +check_scanning_allowed (NMDeviceWifi *self) +{ + GValue instance = { 0, }; + GValue retval = { 0, }; + + g_value_init (&instance, G_TYPE_OBJECT); + g_value_take_object (&instance, self); + + g_value_init (&retval, G_TYPE_BOOLEAN); + g_value_set_boolean (&retval, TRUE); + + /* Use g_signal_emitv() rather than g_signal_emit() to avoid the return + * value being changed if no handlers are connected */ + g_signal_emitv (&instance, signals[SCANNING_ALLOWED], 0, &retval); + + return g_value_get_boolean (&retval); +} + +static gboolean +request_wireless_scan (gpointer user_data) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + gboolean backoff = FALSE; + + if (check_scanning_allowed (self)) { + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scanning requested", + nm_device_get_iface (NM_DEVICE (self))); + + if (nm_supplicant_interface_request_scan (priv->supplicant.iface)) { + /* success */ + backoff = TRUE; + } + } else { + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan requested but not allowed at this time", + nm_device_get_iface (NM_DEVICE (self))); + } + + priv->pending_scan_id = 0; + schedule_scan (self, backoff); + return FALSE; +} + + +/* + * schedule_scan + * + * Schedule a wireless scan. + * + */ +static void +schedule_scan (NMDeviceWifi *self, gboolean backoff) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + GTimeVal now; + + g_get_current_time (&now); + + /* Cancel the pending scan if it would happen later than (now + the scan_interval) */ + if (priv->pending_scan_id) { + if (now.tv_sec + priv->scan_interval < priv->scheduled_scan_time) + cancel_pending_scan (self); + } + + if (!priv->pending_scan_id) { + guint factor = 2, next_scan = priv->scan_interval; + + if ( nm_device_is_activating (NM_DEVICE (self)) + || (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED)) + factor = 1; + + priv->pending_scan_id = g_timeout_add_seconds (next_scan, + request_wireless_scan, + self); + + priv->scheduled_scan_time = now.tv_sec + priv->scan_interval; + if (backoff && (priv->scan_interval < (SCAN_INTERVAL_MAX / factor))) { + priv->scan_interval += (SCAN_INTERVAL_STEP / factor); + /* Ensure the scan interval will never be less than 20s... */ + priv->scan_interval = MAX(priv->scan_interval, SCAN_INTERVAL_MIN + SCAN_INTERVAL_STEP); + /* ... or more than 120s */ + priv->scan_interval = MIN(priv->scan_interval, SCAN_INTERVAL_MAX); + } else if (!backoff && (priv->scan_interval == 0)) { + /* Invalid combination; would cause continual rescheduling of + * the scan and hog CPU. Reset to something minimally sane. + */ + priv->scan_interval = 5; + } + + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scheduled scan in %d seconds (interval now %d seconds)", + nm_device_get_iface (NM_DEVICE (self)), + next_scan, + priv->scan_interval); + + } +} + + +static void +cancel_pending_scan (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + if (priv->pending_scan_id) { + g_source_remove (priv->pending_scan_id); + priv->pending_scan_id = 0; + } +} + + +static void +supplicant_iface_scan_request_result_cb (NMSupplicantInterface *iface, + gboolean success, + NMDeviceWifi *self) +{ + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan request %s", + nm_device_get_iface (NM_DEVICE (self)), + success ? "successful" : "failed"); + + if (check_scanning_allowed (self)) + schedule_scan (self, TRUE); +} + +static void +supplicant_iface_scan_results_cb (NMSupplicantInterface *iface, + guint32 num_results, + NMDeviceWifi *self) +{ + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan results available (%d APs found)", + nm_device_get_iface (NM_DEVICE (self)), + num_results); + if (num_results == 0) { + /* ensure that old APs get culled, which otherwise only + * happens when there are actual scan results to process. + */ + cull_scan_list (self); + nm_device_wifi_ap_list_print (self); + } +} + +static gboolean +is_encrypted (guint32 flags, guint32 wpa_flags, guint32 rsn_flags) +{ + if (flags & NM_802_11_AP_FLAGS_PRIVACY) + return TRUE; + if (wpa_flags & (NM_802_11_AP_SEC_KEY_MGMT_PSK | NM_802_11_AP_SEC_KEY_MGMT_802_1X)) + return TRUE; + if (rsn_flags & (NM_802_11_AP_SEC_KEY_MGMT_PSK | NM_802_11_AP_SEC_KEY_MGMT_802_1X)) + return TRUE; + + return FALSE; +} + +/* + * ap_auth_enforced + * + * Checks whether or not there is an encryption key present for + * this connection, and whether or not the authentication method + * in use will result in an authentication rejection if the key + * is wrong. For example, Ad Hoc mode networks don't have a + * master node and therefore nothing exists to reject the station. + * Similarly, Open System WEP access points don't reject a station + * when the key is wrong. Shared Key WEP access points will. + * + */ +static gboolean +ap_auth_enforced (NMConnection *connection, + NMAccessPoint *ap, + gboolean *encrypted) +{ + guint32 flags, wpa_flags, rsn_flags; + gboolean enforced = FALSE; + + g_return_val_if_fail (NM_IS_AP (ap), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + g_return_val_if_fail (encrypted != NULL, FALSE); + + flags = nm_ap_get_flags (ap); + wpa_flags = nm_ap_get_wpa_flags (ap); + rsn_flags = nm_ap_get_rsn_flags (ap); + + if (nm_ap_get_mode (ap) == NM_802_11_MODE_ADHOC) + goto out; + + /* Static WEP */ + if ( (flags & NM_802_11_AP_FLAGS_PRIVACY) + && (wpa_flags == NM_802_11_AP_SEC_NONE) + && (rsn_flags == NM_802_11_AP_SEC_NONE)) { + NMSettingWirelessSecurity *s_wireless_sec; + const char *auth_alg; + + /* No way to tell if the key is wrong with Open System + * auth mode in WEP. Auth is not enforced like Shared Key. + */ + s_wireless_sec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, + NM_TYPE_SETTING_WIRELESS_SECURITY); + if (s_wireless_sec) { + auth_alg = nm_setting_wireless_security_get_auth_alg (s_wireless_sec); + if (!auth_alg || !strcmp (auth_alg, "open")) + goto out; + } + + enforced = TRUE; + } else if (wpa_flags != NM_802_11_AP_SEC_NONE) { /* WPA */ + enforced = TRUE; + } else if (rsn_flags != NM_802_11_AP_SEC_NONE) { /* WPA2 */ + enforced = TRUE; + } + +out: + *encrypted = is_encrypted (flags, wpa_flags, rsn_flags); + return enforced; +} + + +/**************************************************************************** + * WPA Supplicant control stuff + * + */ + +/* + * merge_scanned_ap + * + * If there is already an entry that matches the BSSID and ESSID of the + * AP to merge, replace that entry with the scanned AP. Otherwise, add + * the scanned AP to the list. + * + * TODO: possibly need to differentiate entries based on security too; i.e. if + * there are two scan results with the same BSSID and SSID but different + * security options? + * + */ +static void +merge_scanned_ap (NMDeviceWifi *self, + NMAccessPoint *merge_ap) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMAccessPoint *found_ap = NULL; + const GByteArray *ssid; + gboolean strict_match = TRUE; + NMAccessPoint *current_ap = NULL; + + /* Let the manager try to fill in the SSID from seen-bssids lists + * if it can + */ + ssid = nm_ap_get_ssid (merge_ap); + if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) { + g_signal_emit (self, signals[HIDDEN_AP_FOUND], 0, merge_ap); + nm_ap_set_broadcast (merge_ap, FALSE); + } + + /* If the incoming scan result matches the hidden AP that NM is currently + * connected to but hasn't been seen in the scan list yet, don't use + * strict matching. Because the capabilities of the fake AP have to be + * constructed from the NMConnection of the activation request, they won't + * always be the same as the capabilities of the real AP from the scan. + */ + current_ap = nm_device_wifi_get_activation_ap (self); + if (current_ap && nm_ap_get_fake (current_ap)) + strict_match = FALSE; + + found_ap = nm_ap_match_in_list (merge_ap, priv->ap_list, strict_match); + if (found_ap) { + nm_ap_set_flags (found_ap, nm_ap_get_flags (merge_ap)); + nm_ap_set_wpa_flags (found_ap, nm_ap_get_wpa_flags (merge_ap)); + nm_ap_set_rsn_flags (found_ap, nm_ap_get_rsn_flags (merge_ap)); + nm_ap_set_strength (found_ap, nm_ap_get_strength (merge_ap)); + nm_ap_set_last_seen (found_ap, nm_ap_get_last_seen (merge_ap)); + nm_ap_set_broadcast (found_ap, nm_ap_get_broadcast (merge_ap)); + nm_ap_set_freq (found_ap, nm_ap_get_freq (merge_ap)); + nm_ap_set_max_bitrate (found_ap, nm_ap_get_max_bitrate (merge_ap)); + + /* If the AP is noticed in a scan, it's automatically no longer + * fake, since it clearly exists somewhere. + */ + nm_ap_set_fake (found_ap, FALSE); + } else { + /* New entry in the list */ + // FIXME: figure out if reference counts are correct here for AP objects + g_object_ref (merge_ap); + priv->ap_list = g_slist_prepend (priv->ap_list, merge_ap); + nm_ap_export_to_dbus (merge_ap); + g_signal_emit (self, signals[ACCESS_POINT_ADDED], 0, merge_ap); + } +} + +static void +cull_scan_list (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv; + GTimeVal cur_time; + GSList *outdated_list = NULL; + GSList *elt; + NMActRequest *req; + const char *cur_ap_path = NULL; + guint32 removed = 0, total = 0; + + g_return_if_fail (self != NULL); + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + g_get_current_time (&cur_time); + + req = nm_device_get_act_request (NM_DEVICE (self)); + if (req) + cur_ap_path = nm_act_request_get_specific_object (req); + + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): checking scan list for outdated APs", + nm_device_get_iface (NM_DEVICE (self))); + + /* Walk the access point list and remove any access points older than + * three times the inactive scan interval. + */ + for (elt = priv->ap_list; elt; elt = g_slist_next (elt), total++) { + NMAccessPoint * ap = NM_AP (elt->data); + const glong ap_time = nm_ap_get_last_seen (ap); + gboolean keep = FALSE; + const guint prune_interval_s = SCAN_INTERVAL_MAX * 3; + + /* Don't ever prune the AP we're currently associated with */ + if (cur_ap_path && !strcmp (cur_ap_path, nm_ap_get_dbus_path (ap))) + keep = TRUE; + if (nm_ap_get_fake (ap)) + keep = TRUE; + + if (!keep && (ap_time + prune_interval_s < cur_time.tv_sec)) + outdated_list = g_slist_append (outdated_list, ap); + } + + /* Remove outdated APs */ + for (elt = outdated_list; elt; elt = g_slist_next (elt)) { + NMAccessPoint *outdated_ap = NM_AP (elt->data); + const struct ether_addr *bssid; + const GByteArray *ssid; + + bssid = nm_ap_get_address (outdated_ap); + ssid = nm_ap_get_ssid (outdated_ap); + nm_log_dbg (LOGD_WIFI_SCAN, + " removing %02x:%02x:%02x:%02x:%02x:%02x (%s%s%s)", + bssid->ether_addr_octet[0], bssid->ether_addr_octet[1], + bssid->ether_addr_octet[2], bssid->ether_addr_octet[3], + bssid->ether_addr_octet[4], bssid->ether_addr_octet[5], + ssid ? "'" : "", + ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)", + ssid ? "'" : ""); + + access_point_removed (self, outdated_ap); + priv->ap_list = g_slist_remove (priv->ap_list, outdated_ap); + g_object_unref (outdated_ap); + removed++; + } + g_slist_free (outdated_list); + + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): removed %d APs (of %d)", + nm_device_get_iface (NM_DEVICE (self)), + removed, total); +} + +#define SET_QUALITY_MEMBER(qual_item, lc_member, uc_member) \ + if (lc_member != -1) { \ + qual_item.lc_member = lc_member; \ + qual_item.updated |= IW_QUAL_##uc_member##_UPDATED; \ + } else { \ + qual_item.updated |= IW_QUAL_##uc_member##_INVALID; \ + } + +static void +set_ap_strength_from_properties (NMDeviceWifi *self, + NMAccessPoint *ap, + GHashTable *properties) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + int qual, level, noise; + struct iw_quality quality; + GValue *value; + gint8 strength; + + value = (GValue *) g_hash_table_lookup (properties, "quality"); + qual = value ? g_value_get_int (value) : -1; + + value = (GValue *) g_hash_table_lookup (properties, "level"); + level = value ? g_value_get_int (value) : -1; + + value = (GValue *) g_hash_table_lookup (properties, "noise"); + noise = value ? g_value_get_int (value) : -1; + + /* Calculate and set the AP's signal quality */ + memset (&quality, 0, sizeof (struct iw_quality)); + SET_QUALITY_MEMBER (quality, qual, QUAL); + SET_QUALITY_MEMBER (quality, level, LEVEL); + SET_QUALITY_MEMBER (quality, noise, NOISE); + + strength = wireless_qual_to_percent (&quality, &priv->max_qual); + nm_ap_set_strength (ap, strength); +} + +static void +supplicant_iface_scanned_ap_cb (NMSupplicantInterface *iface, + GHashTable *properties, + NMDeviceWifi *self) +{ + NMDeviceState state; + NMAccessPoint *ap; + + g_return_if_fail (self != NULL); + g_return_if_fail (properties != NULL); + g_return_if_fail (iface != NULL); + + /* Ignore new APs when unavailable or unamnaged */ + state = nm_device_get_state (NM_DEVICE (self)); + if (state <= NM_DEVICE_STATE_UNAVAILABLE) + return; + + ap = nm_ap_new_from_properties (properties); + if (ap) { + set_ap_strength_from_properties (self, ap, properties); + + nm_ap_print_self (ap, "AP: "); + + /* Add the AP to the device's AP list */ + merge_scanned_ap (self, ap); + g_object_unref (ap); + + /* Remove outdated access points */ + cull_scan_list (self); + + nm_device_wifi_ap_list_print (self); + } else { + nm_log_warn (LOGD_WIFI_SCAN, "(%s): invalid AP properties received", + nm_device_get_iface (NM_DEVICE (self))); + } +} + + +static void +cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + remove_supplicant_interface_error_handler (self); + remove_supplicant_timeouts (self); + if (disconnect && priv->supplicant.iface) + nm_supplicant_interface_disconnect (priv->supplicant.iface); +} + + +static void +remove_link_timeout (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv; + + g_return_if_fail (self != NULL); + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + if (priv->link_timeout_id) { + g_source_remove (priv->link_timeout_id); + priv->link_timeout_id = 0; + } +} + + +/* + * link_timeout_cb + * + * Called when the link to the access point has been down for a specified + * period of time. + */ +static gboolean +link_timeout_cb (gpointer user_data) +{ + NMDevice * dev = NM_DEVICE (user_data); + NMDeviceWifi * self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMActRequest * req = NULL; + NMAccessPoint * ap = NULL; + NMConnection * connection; + const char * setting_name; + gboolean auth_enforced, encrypted = FALSE; + + g_assert (dev); + + priv->link_timeout_id = 0; + + req = nm_device_get_act_request (dev); + ap = nm_device_wifi_get_activation_ap (self); + if (req == NULL || ap == NULL) { + /* shouldn't ever happen */ + nm_log_err (LOGD_WIFI, "couldn't get activation request or activation AP."); + if (nm_device_is_activating (dev)) { + cleanup_association_attempt (self, TRUE); + nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NONE); + } + return FALSE; + } + + /* Disconnect event while activated; the supplicant hasn't been able + * to reassociate within the timeout period, so the connection must + * fail. + */ + if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED) { + nm_device_state_changed (dev, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT); + return FALSE; + } + + /* Disconnect event during initial authentication and credentials + * ARE checked - we are likely to have wrong key. Ask the user for + * another one. + */ + if (nm_device_get_state (dev) != NM_DEVICE_STATE_CONFIG) + goto time_out; + + connection = nm_act_request_get_connection (req); + if (!connection) + goto time_out; + + auth_enforced = ap_auth_enforced (connection, ap, &encrypted); + if (!encrypted || !auth_enforced) + goto time_out; + + /* Drivers are still just too crappy, and emit too many disassociation + * events during connection. So for now, just let the driver and supplicant + * keep trying to associate, and don't ask for new secrets when we get + * disconnected during association. + */ + if (0) { + nm_connection_clear_secrets (connection); + setting_name = nm_connection_need_secrets (connection, NULL); + if (!setting_name) + goto time_out; + + /* Association/authentication failed during association, probably have a + * bad encryption key and the authenticating entity (AP, RADIUS server, etc) + * denied the association due to bad credentials. + */ + nm_log_info (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): disconnected during association," + " asking for new key.", nm_device_get_iface (dev)); + cleanup_association_attempt (self, TRUE); + nm_device_state_changed (dev, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + nm_act_request_get_secrets (req, + setting_name, + TRUE, + SECRETS_CALLER_WIFI, + NULL, + NULL); + + return FALSE; + } + +time_out: + nm_log_warn (LOGD_WIFI, "(%s): link timed out.", nm_device_get_iface (dev)); + return FALSE; +} + +static gboolean +schedule_state_handler (NMDeviceWifi *self, + GSourceFunc handler, + guint32 new_state, + guint32 old_state, + gboolean mgr_task) +{ + NMDeviceWifiPrivate *priv; + SupplicantStateTask *task; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (handler != NULL, FALSE); + + if (new_state == old_state) + return TRUE; + + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + task = g_slice_new0 (SupplicantStateTask); + if (!task) { + nm_log_err (LOGD_WIFI, "Not enough memory to process supplicant manager state change."); + return FALSE; + } + + task->self = self; + task->new_state = new_state; + task->old_state = old_state; + task->mgr_task = mgr_task; + + task->source_id = g_idle_add (handler, task); + if (mgr_task) + priv->supplicant.mgr_tasks = g_slist_append (priv->supplicant.mgr_tasks, task); + else + priv->supplicant.iface_tasks = g_slist_append (priv->supplicant.iface_tasks, task); + + return TRUE; +} + +static gboolean +supplicant_iface_state_cb_handler (gpointer user_data) +{ + SupplicantStateTask *task = (SupplicantStateTask *) user_data; + NMDeviceWifi *self; + NMDeviceWifiPrivate *priv; + + g_return_val_if_fail (task != NULL, FALSE); + + self = task->self; + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + nm_log_info (LOGD_WIFI, "(%s): supplicant interface state: %s -> %s", + nm_device_get_iface (NM_DEVICE (self)), + nm_supplicant_interface_state_to_string (task->old_state), + nm_supplicant_interface_state_to_string (task->new_state)); + + if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_READY) { + priv->scan_interval = SCAN_INTERVAL_MIN; + + /* If the interface can now be activated because the supplicant is now + * available, transition to DISCONNECTED. + */ + if ( (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_UNAVAILABLE) + && nm_device_is_available (NM_DEVICE (self))) { + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE); + } + + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): supplicant ready, requesting initial scan", + nm_device_get_iface (NM_DEVICE (self))); + + /* Request a scan to get latest results */ + cancel_pending_scan (self); + request_wireless_scan (self); + } else if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) { + cleanup_association_attempt (self, FALSE); + supplicant_interface_release (self); + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + } + + finish_supplicant_task (task, FALSE); + return FALSE; +} + + +static void +supplicant_iface_state_cb (NMSupplicantInterface * iface, + guint32 new_state, + guint32 old_state, + NMDeviceWifi *self) +{ + g_return_if_fail (self != NULL); + + schedule_state_handler (self, + supplicant_iface_state_cb_handler, + new_state, + old_state, + FALSE); +} + + +static gboolean +supplicant_iface_connection_state_cb_handler (gpointer user_data) +{ + SupplicantStateTask *task = (SupplicantStateTask *) user_data; + NMDeviceWifi *self = task->self; + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMDevice *dev = NM_DEVICE (self); + gboolean scanning; + + if (!nm_device_get_act_request (dev)) { + /* The device is not activating or already activated; do nothing. */ + goto out; + } + + nm_log_info (LOGD_WIFI, "(%s): supplicant connection state: %s -> %s", + nm_device_get_iface (dev), + nm_supplicant_interface_connection_state_to_string (task->old_state), + nm_supplicant_interface_connection_state_to_string (task->new_state)); + + scanning = nm_supplicant_interface_get_scanning (priv->supplicant.iface); + + if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED) { + remove_supplicant_interface_error_handler (self); + remove_supplicant_timeouts (self); + + /* If this is the initial association during device activation, + * schedule the next activation stage. + */ + if (nm_device_get_state (dev) == NM_DEVICE_STATE_CONFIG) { + NMAccessPoint *ap = nm_device_wifi_get_activation_ap (self); + const GByteArray * ssid = nm_ap_get_ssid (ap); + + nm_log_info (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless) Stage 2 of 5 (Device Configure) " + "successful. Connected to wireless network '%s'.", + nm_device_get_iface (dev), + ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)"); + nm_device_activate_schedule_stage3_ip_config_start (dev); + } + } else if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED) { + if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED || nm_device_is_activating (dev)) { + /* Start the link timeout so we allow some time for reauthentication, + * use a longer timeout if we are scanning since some cards take a + * while to scan. + */ + if (!priv->link_timeout_id) { + priv->link_timeout_id = g_timeout_add_seconds (scanning ? 30 : 15, + link_timeout_cb, self); + } + } + } + +out: + finish_supplicant_task (task, FALSE); + return FALSE; +} + + +static void +supplicant_iface_connection_state_cb (NMSupplicantInterface * iface, + guint32 new_state, + guint32 old_state, + NMDeviceWifi *self) +{ + g_return_if_fail (self != NULL); + + schedule_state_handler (self, + supplicant_iface_connection_state_cb_handler, + new_state, + old_state, + FALSE); +} + + +static gboolean +supplicant_mgr_state_cb_handler (gpointer user_data) +{ + SupplicantStateTask *task = (SupplicantStateTask *) user_data; + NMDeviceWifi *self; + NMDeviceWifiPrivate *priv; + NMDevice *dev; + NMDeviceState dev_state; + + g_return_val_if_fail (task != NULL, FALSE); + + self = task->self; + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + dev = NM_DEVICE (self); + + nm_log_info (LOGD_WIFI, "(%s): supplicant manager state: %s -> %s", + nm_device_get_iface (NM_DEVICE (self)), + nm_supplicant_manager_state_to_string (task->old_state), + nm_supplicant_manager_state_to_string (task->new_state)); + + /* If the supplicant went away, release the supplicant interface */ + if (task->new_state == NM_SUPPLICANT_MANAGER_STATE_DOWN) { + if (priv->supplicant.iface) { + cleanup_association_attempt (self, FALSE); + supplicant_interface_release (self); + } + + if (nm_device_get_state (dev) > NM_DEVICE_STATE_UNAVAILABLE) { + nm_device_state_changed (dev, NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + } + } else if (task->new_state == NM_SUPPLICANT_MANAGER_STATE_IDLE) { + dev_state = nm_device_get_state (dev); + if ( priv->enabled + && !priv->supplicant.iface + && (dev_state >= NM_DEVICE_STATE_UNAVAILABLE)) { + /* request a supplicant interface from the supplicant manager */ + supplicant_interface_acquire (self); + + /* if wireless is enabled and we have a supplicant interface, + * we can transition to the DISCONNECTED state. + */ + if (priv->supplicant.iface) { + nm_device_state_changed (dev, NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_NONE); + } + } + } + + finish_supplicant_task (task, FALSE); + return FALSE; +} + +static void +supplicant_mgr_state_cb (NMSupplicantInterface * iface, + guint32 new_state, + guint32 old_state, + NMDeviceWifi *self) +{ + g_return_if_fail (self != NULL); + + schedule_state_handler (self, + supplicant_mgr_state_cb_handler, + new_state, + old_state, + TRUE); +} + +struct iface_con_error_cb_data { + NMDeviceWifi *self; + char *name; + char *message; +}; + +static gboolean +supplicant_iface_connection_error_cb_handler (gpointer user_data) +{ + NMDeviceWifi *self; + NMDeviceWifiPrivate *priv; + struct iface_con_error_cb_data * cb_data = (struct iface_con_error_cb_data *) user_data; + + g_return_val_if_fail (cb_data != NULL, FALSE); + + self = cb_data->self; + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + if (!nm_device_is_activating (NM_DEVICE (self))) + goto out; + + nm_log_info (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): association request to the supplicant " + "failed: %s - %s", + nm_device_get_iface (NM_DEVICE (self)), + cb_data->name, + cb_data->message); + + cleanup_association_attempt (self, TRUE); + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + +out: + priv->supplicant.iface_con_error_cb_id = 0; + g_free (cb_data->name); + g_free (cb_data->message); + g_slice_free (struct iface_con_error_cb_data, cb_data); + return FALSE; +} + + +static void +supplicant_iface_connection_error_cb (NMSupplicantInterface * iface, + const char * name, + const char * message, + NMDeviceWifi * self) +{ + NMDeviceWifiPrivate *priv; + struct iface_con_error_cb_data *cb_data; + guint id; + + g_return_if_fail (self != NULL); + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + cb_data = g_slice_new0 (struct iface_con_error_cb_data); + if (cb_data == NULL) { + nm_log_err (LOGD_WIFI, "Not enough memory to process supplicant connection error."); + return; + } + + cb_data->self = self; + cb_data->name = g_strdup (name); + cb_data->message = g_strdup (message); + + if (priv->supplicant.iface_con_error_cb_id) + g_source_remove (priv->supplicant.iface_con_error_cb_id); + + id = g_idle_add (supplicant_iface_connection_error_cb_handler, cb_data); + priv->supplicant.iface_con_error_cb_id = id; +} + +static void +supplicant_iface_notify_scanning_cb (NMSupplicantInterface *iface, + GParamSpec *pspec, + NMDeviceWifi *self) +{ + gboolean scanning; + + scanning = nm_supplicant_interface_get_scanning (iface); + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): now %s", + nm_device_get_iface (NM_DEVICE (self)), + scanning ? "scanning" : "not scanning"); + + g_object_notify (G_OBJECT (self), "scanning"); +} + +static void +remove_supplicant_connection_timeout (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv; + + g_return_if_fail (self != NULL); + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + /* Remove any pending timeouts on the request */ + if (priv->supplicant.con_timeout_id) { + g_source_remove (priv->supplicant.con_timeout_id); + priv->supplicant.con_timeout_id = 0; + } +} + +static NMActStageReturn +handle_auth_or_fail (NMDeviceWifi *self, + NMActRequest *req, + gboolean new_secrets) +{ + const char *setting_name; + guint32 tries; + NMAccessPoint *ap; + NMConnection *connection; + + g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), NM_ACT_STAGE_RETURN_FAILURE); + + if (!req) { + req = nm_device_get_act_request (NM_DEVICE (self)); + g_assert (req); + } + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + ap = nm_device_wifi_get_activation_ap (self); + g_assert (ap); + + tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES)); + if (tries > 3) + return NM_ACT_STAGE_RETURN_FAILURE; + + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); + + nm_connection_clear_secrets (connection); + setting_name = nm_connection_need_secrets (connection, NULL); + if (setting_name) { + gboolean get_new; + + /* If the caller doesn't necessarily want completely new secrets, + * only ask for new secrets after the first failure. + */ + get_new = new_secrets ? TRUE : (tries ? TRUE : FALSE); + nm_act_request_get_secrets (req, + setting_name, + get_new, + SECRETS_CALLER_WIFI, + NULL, + NULL); + + g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, GUINT_TO_POINTER (++tries)); + } else { + nm_log_warn (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets."); + } + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +/* + * supplicant_connection_timeout_cb + * + * Called when the supplicant has been unable to connect to an access point + * within a specified period of time. + */ +static gboolean +supplicant_connection_timeout_cb (gpointer user_data) +{ + NMDevice * dev = NM_DEVICE (user_data); + NMDeviceWifi * self = NM_DEVICE_WIFI (user_data); + NMAccessPoint * ap; + NMActRequest * req; + gboolean auth_enforced = FALSE, encrypted = FALSE; + NMConnection *connection; + + cleanup_association_attempt (self, TRUE); + + if (!nm_device_is_activating (dev)) + return FALSE; + + /* Timed out waiting for authentication success; if the security in use + * does not require access point side authentication (Open System + * WEP, for example) then we are likely using the wrong authentication + * algorithm or key. Request new one from the user. + */ + + req = nm_device_get_act_request (dev); + g_assert (req); + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + ap = nm_device_wifi_get_activation_ap (self); + g_assert (ap); + + auth_enforced = ap_auth_enforced (connection, ap, &encrypted); + if (!encrypted) { + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): association took too long, " + "failing activation.", + nm_device_get_iface (dev)); + nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT); + } else { + /* Authentication failed, encryption key is probably bad */ + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): association took too long.", + nm_device_get_iface (dev)); + + if (handle_auth_or_fail (self, req, TRUE) == NM_ACT_STAGE_RETURN_POSTPONE) { + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): asking for new secrets", + nm_device_get_iface (dev)); + } else { + nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); + } + } + + return FALSE; +} + + +static gboolean +start_supplicant_connection_timeout (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv; + guint id; + + g_return_val_if_fail (self != NULL, FALSE); + + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + /* Set up a timeout on the connection attempt to fail it after 25 seconds */ + id = g_timeout_add_seconds (25, supplicant_connection_timeout_cb, self); + if (id == 0) { + nm_log_err (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): couldn't start supplicant " + "timeout timer.", + nm_device_get_iface (NM_DEVICE (self))); + return FALSE; + } + priv->supplicant.con_timeout_id = id; + return TRUE; +} + + +static void +remove_supplicant_timeouts (NMDeviceWifi *self) +{ + g_return_if_fail (self != NULL); + + remove_supplicant_connection_timeout (self); + remove_link_timeout (self); +} + +static guint32 +find_supported_frequency (NMDeviceWifi *self, guint32 *freqs) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + int i; + + for (i = 0; i < priv->num_freqs; i++) { + while (*freqs) { + if (priv->freqs[i] == *freqs) + return *freqs; + freqs++; + } + } + + return 0; +} + +static NMSupplicantConfig * +build_supplicant_config (NMDeviceWifi *self, + NMConnection *connection, + NMAccessPoint *ap) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMSupplicantConfig *config = NULL; + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wireless_sec; + guint32 adhoc_freq = 0; + + g_return_val_if_fail (self != NULL, NULL); + + s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); + g_return_val_if_fail (s_wireless != NULL, NULL); + + config = nm_supplicant_config_new (); + if (!config) + return NULL; + + /* Figure out the Ad-Hoc frequency to use if creating an adhoc network; if + * nothing was specified then pick something usable. + */ + if ((nm_ap_get_mode (ap) == NM_802_11_MODE_ADHOC) && nm_ap_get_user_created (ap)) { + const char *band = nm_setting_wireless_get_band (s_wireless); + + adhoc_freq = nm_ap_get_freq (ap); + if (!adhoc_freq) { + if (band && !strcmp (band, "a")) { + guint32 a_freqs[] = {5180, 5200, 5220, 5745, 5765, 5785, 5805, 0}; + adhoc_freq = find_supported_frequency (self, a_freqs); + } else { + guint32 bg_freqs[] = {2412, 2437, 2462, 2472, 0}; + adhoc_freq = find_supported_frequency (self, bg_freqs); + } + } + + if (!adhoc_freq) { + if (band && !strcmp (band, "a")) + adhoc_freq = 5180; + else + adhoc_freq = 2462; + } + } + + if (!nm_supplicant_config_add_setting_wireless (config, + s_wireless, + nm_ap_get_broadcast (ap), + adhoc_freq, + priv->has_scan_capa_ssid)) { + nm_log_err (LOGD_WIFI, "Couldn't add 802-11-wireless setting to supplicant config."); + goto error; + } + + s_wireless_sec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY); + if (s_wireless_sec) { + NMSetting8021x *s_8021x; + const char *con_path = nm_connection_get_path (connection); + + g_assert (con_path); + s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X); + if (!nm_supplicant_config_add_setting_wireless_security (config, + s_wireless_sec, + s_8021x, + con_path)) { + nm_log_err (LOGD_WIFI, "Couldn't add 802-11-wireless-security setting to " + "supplicant config."); + goto error; + } + } else { + if (!nm_supplicant_config_add_no_security (config)) { + nm_log_err (LOGD_WIFI, "Couldn't add unsecured option to supplicant config."); + goto error; + } + } + + return config; + +error: + g_object_unref (config); + return NULL; +} + +/****************************************************************************/ + +static void +real_update_hw_address (NMDevice *dev) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + struct ifreq req; + int fd; + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_err (LOGD_HW, "could not open control socket."); + return; + } + + memset (&req, 0, sizeof (struct ifreq)); + strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ); + if (ioctl (fd, SIOCGIFHWADDR, &req) < 0) { + nm_log_err (LOGD_HW | LOGD_WIFI, "(%s) error getting hardware address: %d", + nm_device_get_iface (dev), errno); + goto out; + } + + if (memcmp (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr))) { + memcpy (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr)); + g_object_notify (G_OBJECT (dev), NM_DEVICE_WIFI_HW_ADDRESS); + } + +out: + close (fd); +} + + +static NMActStageReturn +real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMAccessPoint *ap = NULL; + NMActRequest *req; + NMConnection *connection; + GSList *iter; + + /* If the user is trying to connect to an AP that NM doesn't yet know about + * (hidden network or something), create an fake AP from the security + * settings in the connection to use until the AP is recognized from the + * scan list, which should show up when the connection is successful. + */ + ap = nm_device_wifi_get_activation_ap (self); + if (ap) + goto done; + + req = nm_device_get_act_request (NM_DEVICE (self)); + g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + connection = nm_act_request_get_connection (req); + g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + /* Find a compatible AP in the scan list */ + for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) { + NMAccessPoint *candidate = NM_AP (iter->data); + + if (nm_ap_check_compatible (candidate, connection)) { + ap = candidate; + break; + } + } + + /* If no compatible AP was found, create a fake AP (network is likely + * hidden) and try to use that. + */ + if (!ap) { + ap = nm_ap_new_fake_from_connection (connection); + g_return_val_if_fail (ap != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + switch (nm_ap_get_mode (ap)) { + case NM_802_11_MODE_ADHOC: + nm_ap_set_user_created (ap, TRUE); + break; + case NM_802_11_MODE_INFRA: + default: + nm_ap_set_broadcast (ap, FALSE); + break; + } + + priv->ap_list = g_slist_prepend (priv->ap_list, ap); + nm_ap_export_to_dbus (ap); + g_signal_emit (self, signals[ACCESS_POINT_ADDED], 0, ap); + } + + nm_act_request_set_specific_object (req, nm_ap_get_dbus_path (ap)); + +done: + set_current_ap (self, ap); + return NM_ACT_STAGE_RETURN_SUCCESS; +} + + +static void +real_connection_secrets_updated (NMDevice *dev, + NMConnection *connection, + GSList *updated_settings, + RequestSecretsCaller caller) +{ + NMActRequest *req; + gboolean valid = FALSE; + GSList *iter; + + g_return_if_fail (caller == SECRETS_CALLER_WIFI); + + if (nm_device_get_state (dev) != NM_DEVICE_STATE_NEED_AUTH) + return; + + for (iter = updated_settings; iter; iter = g_slist_next (iter)) { + const char *setting_name = (const char *) iter->data; + + if ( !strcmp (setting_name, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME) + || !strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME)) { + valid = TRUE; + } else { + nm_log_warn (LOGD_DEVICE, "Ignoring updated secrets for setting '%s'.", + setting_name); + } + } + + req = nm_device_get_act_request (dev); + g_assert (req); + + g_return_if_fail (nm_act_request_get_connection (req) == connection); + + nm_device_activate_schedule_stage1_device_prepare (dev); +} + +static NMActStageReturn +real_act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + const char *iface = nm_device_get_iface (dev); + NMSupplicantConfig *config = NULL; + gulong id = 0; + NMActRequest *req; + NMAccessPoint *ap; + NMConnection *connection; + NMSettingConnection *s_connection; + const char *setting_name; + NMSettingWireless *s_wireless; + + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + remove_supplicant_timeouts (self); + + req = nm_device_get_act_request (dev); + g_assert (req); + + ap = nm_device_wifi_get_activation_ap (self); + g_assert (ap); + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + s_connection = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + g_assert (s_connection); + + s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); + g_assert (s_wireless); + + /* If we need secrets, get them */ + setting_name = nm_connection_need_secrets (connection, NULL); + if (setting_name) { + nm_log_info (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): access point '%s' has security," + " but secrets are required.", + iface, nm_setting_connection_get_id (s_connection)); + + ret = handle_auth_or_fail (self, req, FALSE); + if (ret == NM_ACT_STAGE_RETURN_FAILURE) + *reason = NM_DEVICE_STATE_REASON_NO_SECRETS; + goto out; + } + + /* have secrets, or no secrets required */ + if (nm_setting_wireless_get_security (s_wireless)) { + nm_log_info (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): connection '%s' has security" + ", and secrets exist. No new secrets needed.", + iface, nm_setting_connection_get_id (s_connection)); + } else { + nm_log_info (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): connection '%s' requires no " + "security. No secrets needed.", + iface, nm_setting_connection_get_id (s_connection)); + } + + config = build_supplicant_config (self, connection, ap); + if (config == NULL) { + nm_log_err (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): couldn't build wireless configuration.", + iface); + *reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED; + goto out; + } + + /* Hook up error signal handler to capture association errors */ + id = g_signal_connect (priv->supplicant.iface, + "connection-error", + G_CALLBACK (supplicant_iface_connection_error_cb), + self); + priv->supplicant.iface_error_id = id; + + if (!nm_supplicant_interface_set_config (priv->supplicant.iface, config)) { + nm_log_err (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): couldn't send wireless " + "configuration to the supplicant.", iface); + *reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED; + goto out; + } + + if (!start_supplicant_connection_timeout (self)) { + *reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED; + goto out; + } + + /* We'll get stage3 started when the supplicant connects */ + ret = NM_ACT_STAGE_RETURN_POSTPONE; + +out: + if (ret == NM_ACT_STAGE_RETURN_FAILURE) + cleanup_association_attempt (self, TRUE); + + if (config) { + /* Supplicant interface object refs the config; we no longer care about + * it after this function. + */ + g_object_unref (config); + } + return ret; +} + +static NMActStageReturn +real_act_stage4_get_ip4_config (NMDevice *dev, + NMIP4Config **config, + NMDeviceStateReason *reason) +{ + NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + NMDeviceClass *parent_class; + + g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE); + + /* Chain up to parent */ + parent_class = NM_DEVICE_CLASS (nm_device_wifi_parent_class); + ret = parent_class->act_stage4_get_ip4_config (dev, config, reason); + + if ((ret == NM_ACT_STAGE_RETURN_SUCCESS) && *config) { + NMConnection *connection; + NMSettingWireless *s_wireless; + guint32 mtu; + + connection = nm_act_request_get_connection (nm_device_get_act_request (dev)); + g_assert (connection); + s_wireless = NM_SETTING_WIRELESS (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS)); + g_assert (s_wireless); + + /* MTU override */ + mtu = nm_setting_wireless_get_mtu (s_wireless); + if (mtu) + nm_ip4_config_set_mtu (*config, mtu); + } + + return ret; +} + + +static NMActStageReturn +handle_ip_config_timeout (NMDeviceWifi *self, + NMConnection *connection, + gboolean may_fail, + gboolean *chain_up, + NMDeviceStateReason *reason) +{ + NMAccessPoint *ap; + NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + gboolean auth_enforced = FALSE, encrypted = FALSE; + + g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + ap = nm_device_wifi_get_activation_ap (self); + g_assert (ap); + + /* If nothing checks the security authentication information (as in + * Open System WEP for example), and DHCP times out, then + * the encryption key is likely wrong. Ask the user for a new one. + * Otherwise the failure likely happened after a successful authentication. + */ + auth_enforced = ap_auth_enforced (connection, ap, &encrypted); + if (encrypted && !auth_enforced && !may_fail) { + NMSettingConnection *s_con; + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + + /* Activation failed, we must have bad encryption key */ + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): could not get IP configuration for " + "connection '%s'.", + nm_device_get_iface (NM_DEVICE (self)), + nm_setting_connection_get_id (s_con)); + + ret = handle_auth_or_fail (self, NULL, TRUE); + if (ret == NM_ACT_STAGE_RETURN_POSTPONE) { + nm_log_info (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): asking for new secrets", + nm_device_get_iface (NM_DEVICE (self))); + } else { + *reason = NM_DEVICE_STATE_REASON_NO_SECRETS; + } + } else { + /* Non-encrypted network or authentication is enforced by some + * entity (AP, RADIUS server, etc), but IP configure failed. Let the + * superclass handle it. + */ + *chain_up = TRUE; + } + + return ret; +} + + +static NMActStageReturn +real_act_stage4_ip4_config_timeout (NMDevice *dev, + NMIP4Config **config, + NMDeviceStateReason *reason) +{ + NMActRequest *req; + NMConnection *connection; + NMSettingIP4Config *s_ip4; + gboolean may_fail = FALSE, chain_up = FALSE; + NMActStageReturn ret; + + req = nm_device_get_act_request (dev); + g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); + + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + if (s_ip4) + may_fail = nm_setting_ip4_config_get_may_fail (s_ip4); + + ret = handle_ip_config_timeout (NM_DEVICE_WIFI (dev), connection, may_fail, &chain_up, reason); + if (chain_up) + ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip4_config_timeout (dev, config, reason); + + return ret; +} + +static NMActStageReturn +real_act_stage4_ip6_config_timeout (NMDevice *dev, + NMIP6Config **config, + NMDeviceStateReason *reason) +{ + NMActRequest *req; + NMConnection *connection; + NMSettingIP6Config *s_ip6; + gboolean may_fail = FALSE, chain_up = FALSE; + NMActStageReturn ret; + + req = nm_device_get_act_request (dev); + g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); + + s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); + if (s_ip6) + may_fail = nm_setting_ip6_config_get_may_fail (s_ip6); + + ret = handle_ip_config_timeout (NM_DEVICE_WIFI (dev), connection, may_fail, &chain_up, reason); + if (chain_up) + ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip6_config_timeout (dev, config, reason); + + return ret; +} + +static void +activation_success_handler (NMDevice *dev) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMAccessPoint *ap; + struct ether_addr bssid = { {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} }; + NMAccessPoint *tmp_ap; + NMActRequest *req; + NMConnection *connection; + + req = nm_device_get_act_request (dev); + g_assert (req); + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + /* Clear wireless secrets tries on success */ + g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL); + + ap = nm_device_wifi_get_activation_ap (self); + + /* If the AP isn't fake, it was found in the scan list and all its + * details are known. + */ + if (!nm_ap_get_fake (ap)) + goto done; + + /* If the activate AP was fake, it probably won't have a BSSID at all. + * But if activation was successful, the card will know the BSSID. Grab + * the BSSID off the card and fill in the BSSID of the activation AP. + */ + nm_device_wifi_get_bssid (self, &bssid); + if (!nm_ethernet_address_is_valid (nm_ap_get_address (ap))) + nm_ap_set_address (ap, &bssid); + if (!nm_ap_get_freq (ap)) + nm_ap_set_freq (ap, nm_device_wifi_get_frequency (self)); + if (!nm_ap_get_max_bitrate (ap) && nm_ap_get_user_created (ap)) + nm_ap_set_max_bitrate (ap, nm_device_wifi_get_bitrate (self)); + + tmp_ap = get_active_ap (self, ap, TRUE); + if (tmp_ap) { + const GByteArray *ssid = nm_ap_get_ssid (tmp_ap); + + /* Found a better match in the scan list than the fake AP. Use it + * instead. + */ + + /* If the better match was a hidden AP, update it's SSID */ + if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) + nm_ap_set_ssid (tmp_ap, nm_ap_get_ssid (ap)); + + nm_act_request_set_specific_object (req, nm_ap_get_dbus_path (tmp_ap)); + + priv->ap_list = g_slist_remove (priv->ap_list, ap); + g_object_unref (ap); + } + +done: + periodic_update (self); + + /* Reset scan interval to something reasonable */ + priv->scan_interval = SCAN_INTERVAL_MIN + (SCAN_INTERVAL_STEP * 2); +} + + +static void +activation_failure_handler (NMDevice *dev) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMAccessPoint *ap; + const GByteArray * ssid; + NMActRequest *req; + NMConnection *connection; + + req = nm_device_get_act_request (dev); + g_assert (req); + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + /* Clear wireless secrets tries on failure */ + g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL); + + if ((ap = nm_device_wifi_get_activation_ap (self))) { + if (nm_ap_get_fake (ap)) { + /* Fake APs are ones that don't show up in scans, + * but which the user explicitly attempted to connect to. + * However, if we fail on one of these, remove it from the + * list because we don't have any scan or capability info + * for it, and they are pretty much useless. + */ + access_point_removed (self, ap); + priv->ap_list = g_slist_remove (priv->ap_list, ap); + g_object_unref (ap); + } + } + + ssid = nm_ap_get_ssid (ap); + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s) failed for access point (%s)", + nm_device_get_iface (dev), + ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)"); +} + +static gboolean +real_can_interrupt_activation (NMDevice *dev) +{ + if (nm_device_get_state (dev) == NM_DEVICE_STATE_NEED_AUTH) + return TRUE; + + return FALSE; +} + + +static guint32 +real_get_type_capabilities (NMDevice *dev) +{ + return NM_DEVICE_WIFI_GET_PRIVATE (dev)->capabilities; +} + + +static gboolean +spec_match_list (NMDevice *device, const GSList *specs) +{ + struct ether_addr ether; + char *hwaddr; + gboolean matched; + + nm_device_wifi_get_address (NM_DEVICE_WIFI (device), ðer); + hwaddr = nm_ether_ntop (ðer); + matched = nm_match_spec_hwaddr (specs, hwaddr); + g_free (hwaddr); + + return matched; +} + +static void +device_state_changed (NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason, + gpointer user_data) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (device); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + gboolean clear_aps = FALSE; + + if (new_state <= NM_DEVICE_STATE_UNAVAILABLE) { + /* Clean up the supplicant interface because in these states the + * device cannot be used. + */ + if (priv->supplicant.iface) + supplicant_interface_release (self); + } + + /* Start or stop the rfkill poll worker for ipw cards */ + if (priv->ipw_rfkill_path) { + if (new_state > NM_DEVICE_STATE_UNMANAGED) { + if (!priv->ipw_rfkill_id) + priv->ipw_rfkill_id = g_timeout_add_seconds (3, ipw_rfkill_state_work, self); + } else if (new_state <= NM_DEVICE_STATE_UNMANAGED) { + if (priv->ipw_rfkill_id) { + g_source_remove (priv->ipw_rfkill_id); + priv->ipw_rfkill_id = 0; + } + } + } + + switch (new_state) { + case NM_DEVICE_STATE_UNMANAGED: + clear_aps = TRUE; + break; + case NM_DEVICE_STATE_UNAVAILABLE: + /* If the device is enabled and the supplicant manager is ready, + * acquire a supplicant interface and transition to DISCONNECTED because + * the device is now ready to use. + */ + if (priv->enabled) { + gboolean success; + struct iw_range range; + + /* Wait for some drivers like ipw3945 to come back to life */ + success = wireless_get_range (self, &range, NULL); + + if (!priv->supplicant.iface) + supplicant_interface_acquire (self); + } + clear_aps = TRUE; + break; + case NM_DEVICE_STATE_ACTIVATED: + activation_success_handler (device); + break; + case NM_DEVICE_STATE_FAILED: + activation_failure_handler (device); + break; + case NM_DEVICE_STATE_DISCONNECTED: + // FIXME: ensure that the activation request is destroyed + break; + default: + break; + } + + if (clear_aps) + remove_all_aps (self); +} + +NMAccessPoint * +nm_device_wifi_get_activation_ap (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv; + NMActRequest *req; + const char *ap_path; + GSList * elt; + + g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), NULL); + + req = nm_device_get_act_request (NM_DEVICE (self)); + if (!req) + return NULL; + + ap_path = nm_act_request_get_specific_object (req); + if (!ap_path) + return NULL; + + /* Find the AP by it's object path */ + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + for (elt = priv->ap_list; elt; elt = g_slist_next (elt)) { + NMAccessPoint *ap = NM_AP (elt->data); + + if (!strcmp (ap_path, nm_ap_get_dbus_path (ap))) + return ap; + } + return NULL; +} + +static void +real_set_enabled (NMDeviceInterface *device, gboolean enabled) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (device); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMDeviceState state; + + if (priv->enabled == enabled) + return; + + priv->enabled = enabled; + + nm_log_dbg (LOGD_WIFI, "(%s): device now %s", + nm_device_get_iface (NM_DEVICE (device)), + enabled ? "enabled" : "disabled"); + + state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self)); + if (state < NM_DEVICE_STATE_UNAVAILABLE) { + nm_log_dbg (LOGD_WIFI, "(%s): %s blocked by UNMANAGED state", + enabled ? "enable" : "disable", + nm_device_get_iface (NM_DEVICE (device))); + return; + } + + if (enabled) { + gboolean no_firmware = FALSE, success; + struct iw_range range; + + if (state != NM_DEVICE_STATE_UNAVAILABLE) + nm_log_warn (LOGD_CORE, "not in expected unavailable state!"); + + if (!nm_device_hw_bring_up (NM_DEVICE (self), TRUE, &no_firmware)) { + nm_log_dbg (LOGD_WIFI, "(%s): enable blocked by failure to bring device up", + nm_device_get_iface (NM_DEVICE (device))); + + /* The device sucks, or HAL was lying to us about the killswitch state */ + priv->enabled = FALSE; + return; + } + + /* Wait for some drivers like ipw3945 to come back to life */ + success = wireless_get_range (self, &range, NULL); + + /* iface should be NULL here, but handle it anyway if it's not */ + g_warn_if_fail (priv->supplicant.iface == NULL); + if (priv->supplicant.iface) + supplicant_interface_release (self); + + supplicant_interface_acquire (self); + nm_log_dbg (LOGD_WIFI, "(%s): enable waiting on supplicant state", + nm_device_get_iface (NM_DEVICE (device))); + } else { + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_NONE); + nm_device_hw_take_down (NM_DEVICE (self), TRUE); + } +} + +/********************************************************************/ + +NMDevice * +nm_device_wifi_new (const char *udi, + const char *iface, + const char *driver) +{ + g_return_val_if_fail (udi != NULL, NULL); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + + return (NMDevice *) g_object_new (NM_TYPE_DEVICE_WIFI, + NM_DEVICE_INTERFACE_UDI, udi, + NM_DEVICE_INTERFACE_IFACE, iface, + NM_DEVICE_INTERFACE_DRIVER, driver, + NM_DEVICE_INTERFACE_TYPE_DESC, "802.11 WiFi", + NM_DEVICE_INTERFACE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI, + NM_DEVICE_INTERFACE_RFKILL_TYPE, RFKILL_TYPE_WLAN, + NULL); +} + +static void +device_interface_init (NMDeviceInterface *iface_class) +{ + iface_class->set_enabled = real_set_enabled; +} + +static void +nm_device_wifi_init (NMDeviceWifi * self) +{ + g_signal_connect (self, "state-changed", G_CALLBACK (device_state_changed), NULL); +} + +static void +dispose (GObject *object) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (object); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + if (priv->disposed) { + G_OBJECT_CLASS (nm_device_wifi_parent_class)->dispose (object); + return; + } + + priv->disposed = TRUE; + + if (priv->periodic_source_id) { + g_source_remove (priv->periodic_source_id); + priv->periodic_source_id = 0; + } + + /* Clean up all pending supplicant tasks */ + while (priv->supplicant.iface_tasks) + finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE); + while (priv->supplicant.mgr_tasks) + finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.mgr_tasks->data, TRUE); + + cleanup_association_attempt (self, TRUE); + supplicant_interface_release (self); + + if (priv->supplicant.mgr_state_id) { + g_signal_handler_disconnect (priv->supplicant.mgr, priv->supplicant.mgr_state_id); + priv->supplicant.mgr_state_id = 0; + } + + if (priv->supplicant.mgr) { + g_object_unref (priv->supplicant.mgr); + priv->supplicant.mgr = NULL; + } + + if (priv->ssid) { + g_byte_array_free (priv->ssid, TRUE); + priv->ssid = NULL; + } + + set_current_ap (self, NULL); + remove_all_aps (self); + + g_free (priv->ipw_rfkill_path); + if (priv->ipw_rfkill_id) { + g_source_remove (priv->ipw_rfkill_id); + priv->ipw_rfkill_id = 0; + } + + G_OBJECT_CLASS (nm_device_wifi_parent_class)->dispose (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDeviceWifi *device = NM_DEVICE_WIFI (object); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device); + struct ether_addr hw_addr; + + switch (prop_id) { + case PROP_HW_ADDRESS: + nm_device_wifi_get_address (device, &hw_addr); + g_value_take_string (value, nm_ether_ntop (&hw_addr)); + break; + case PROP_MODE: + g_value_set_uint (value, nm_device_wifi_get_mode (device)); + break; + case PROP_BITRATE: + g_value_set_uint (value, priv->rate); + break; + case PROP_CAPABILITIES: + g_value_set_uint (value, priv->capabilities); + break; + case PROP_ACTIVE_ACCESS_POINT: + if (priv->current_ap) + g_value_set_boxed (value, nm_ap_get_dbus_path (priv->current_ap)); + else + g_value_set_boxed (value, "/"); + break; + case PROP_SCANNING: + g_value_set_boolean (value, nm_supplicant_interface_get_scanning (priv->supplicant.iface)); + break; + case PROP_IPW_RFKILL_STATE: + g_value_set_uint (value, nm_device_wifi_get_ipw_rfkill_state (device)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_IPW_RFKILL_STATE: + /* construct only */ + priv->ipw_rfkill_state = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +nm_device_wifi_class_init (NMDeviceWifiClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMDeviceWifiPrivate)); + + object_class->constructor = constructor; + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = dispose; + + parent_class->get_type_capabilities = real_get_type_capabilities; + parent_class->get_generic_capabilities = real_get_generic_capabilities; + parent_class->hw_is_up = real_hw_is_up; + parent_class->hw_bring_up = real_hw_bring_up; + parent_class->hw_take_down = real_hw_take_down; + parent_class->is_up = real_is_up; + parent_class->bring_up = real_bring_up; + parent_class->take_down = real_take_down; + parent_class->update_hw_address = real_update_hw_address; + parent_class->get_best_auto_connection = real_get_best_auto_connection; + parent_class->is_available = real_is_available; + parent_class->connection_secrets_updated = real_connection_secrets_updated; + parent_class->check_connection_compatible = real_check_connection_compatible; + + parent_class->act_stage1_prepare = real_act_stage1_prepare; + parent_class->act_stage2_config = real_act_stage2_config; + parent_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config; + parent_class->act_stage4_ip4_config_timeout = real_act_stage4_ip4_config_timeout; + parent_class->act_stage4_ip6_config_timeout = real_act_stage4_ip6_config_timeout; + parent_class->deactivate = real_deactivate; + parent_class->deactivate_quickly = real_deactivate_quickly; + parent_class->can_interrupt_activation = real_can_interrupt_activation; + parent_class->spec_match_list = spec_match_list; + + klass->scanning_allowed = scanning_allowed; + + /* Properties */ + g_object_class_install_property (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_DEVICE_WIFI_HW_ADDRESS, + "MAC Address", + "Hardware MAC address", + NULL, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_MODE, + g_param_spec_uint (NM_DEVICE_WIFI_MODE, + "Mode", + "Mode", + NM_802_11_MODE_UNKNOWN, + NM_802_11_MODE_INFRA, + NM_802_11_MODE_INFRA, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_BITRATE, + g_param_spec_uint (NM_DEVICE_WIFI_BITRATE, + "Bitrate", + "Bitrate", + 0, G_MAXUINT32, 0, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_ACTIVE_ACCESS_POINT, + g_param_spec_boxed (NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT, + "Active access point", + "Currently active access point", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_CAPABILITIES, + g_param_spec_uint (NM_DEVICE_WIFI_CAPABILITIES, + "Wireless Capabilities", + "Wireless Capabilities", + 0, G_MAXUINT32, NM_WIFI_DEVICE_CAP_NONE, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_SCANNING, + g_param_spec_boolean (NM_DEVICE_WIFI_SCANNING, + "Scanning", + "Scanning", + FALSE, + G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT)); + + g_object_class_install_property (object_class, PROP_IPW_RFKILL_STATE, + g_param_spec_uint (NM_DEVICE_WIFI_IPW_RFKILL_STATE, + "IpwRfkillState", + "ipw rf-kill state", + RFKILL_UNBLOCKED, RFKILL_HARD_BLOCKED, RFKILL_UNBLOCKED, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); + + /* Signals */ + signals[ACCESS_POINT_ADDED] = + g_signal_new ("access-point-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDeviceWifiClass, access_point_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + + signals[ACCESS_POINT_REMOVED] = + g_signal_new ("access-point-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDeviceWifiClass, access_point_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + + signals[HIDDEN_AP_FOUND] = + g_signal_new ("hidden-ap-found", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDeviceWifiClass, hidden_ap_found), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + + signals[PROPERTIES_CHANGED] = + nm_properties_changed_signal_new (object_class, + G_STRUCT_OFFSET (NMDeviceWifiClass, properties_changed)); + + signals[SCANNING_ALLOWED] = + g_signal_new ("scanning-allowed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMDeviceWifiClass, scanning_allowed), + scanning_allowed_accumulator, NULL, + _nm_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), &dbus_glib_nm_device_wifi_object_info); + + dbus_g_error_domain_register (NM_WIFI_ERROR, NULL, NM_TYPE_WIFI_ERROR); +} + + diff --git a/src/nm-device-wifi.h b/src/nm-device-wifi.h new file mode 100644 index 00000000..11ac8857 --- /dev/null +++ b/src/nm-device-wifi.h @@ -0,0 +1,107 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef NM_DEVICE_WIFI_H +#define NM_DEVICE_WIFI_H + +#include <glib-object.h> +#include <dbus/dbus.h> +#include <net/ethernet.h> + +#include "nm-rfkill.h" +#include "nm-device.h" +#include "nm-wifi-ap.h" + +struct NMAccessPointList; + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_WIFI (nm_device_wifi_get_type ()) +#define NM_DEVICE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_WIFI, NMDeviceWifi)) +#define NM_DEVICE_WIFI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_WIFI, NMDeviceWifiClass)) +#define NM_IS_DEVICE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_WIFI)) +#define NM_IS_DEVICE_WIFI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_WIFI)) +#define NM_DEVICE_WIFI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_WIFI, NMDeviceWifiClass)) + + +#define NM_DEVICE_WIFI_HW_ADDRESS "hw-address" +#define NM_DEVICE_WIFI_MODE "mode" +#define NM_DEVICE_WIFI_BITRATE "bitrate" +#define NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT "active-access-point" +#define NM_DEVICE_WIFI_CAPABILITIES "wireless-capabilities" +#define NM_DEVICE_WIFI_SCANNING "scanning" +#define NM_DEVICE_WIFI_IPW_RFKILL_STATE "ipw-rfkill-state" + +#ifndef NM_DEVICE_WIFI_DEFINED +#define NM_DEVICE_WIFI_DEFINED +typedef struct _NMDeviceWifi NMDeviceWifi; +#endif + +typedef struct _NMDeviceWifiClass NMDeviceWifiClass; +typedef struct _NMDeviceWifiPrivate NMDeviceWifiPrivate; + +struct _NMDeviceWifi +{ + NMDevice parent; + + /*< private >*/ + NMDeviceWifiPrivate *priv; +}; + +struct _NMDeviceWifiClass +{ + NMDeviceClass parent; + + /* Signals */ + void (*access_point_added) (NMDeviceWifi *device, NMAccessPoint *ap); + void (*access_point_removed) (NMDeviceWifi *device, NMAccessPoint *ap); + void (*hidden_ap_found) (NMDeviceWifi *device, NMAccessPoint *ap); + void (*properties_changed) (NMDeviceWifi *device, GHashTable *properties); + gboolean (*scanning_allowed) (NMDeviceWifi *device); +}; + + +GType nm_device_wifi_get_type (void); + +NMDevice *nm_device_wifi_new (const char *udi, + const char *iface, + const char *driver); + +void nm_device_wifi_get_address (NMDeviceWifi *dev, + struct ether_addr *addr); + +void nm_device_wifi_get_bssid (NMDeviceWifi *dev, + struct ether_addr *bssid); + +const GByteArray * nm_device_wifi_get_ssid (NMDeviceWifi *self); + +gboolean nm_device_wifi_set_mode (NMDeviceWifi *self, + const NM80211Mode mode); + +NM80211Mode nm_device_wifi_get_mode (NMDeviceWifi *self); + +NMAccessPoint * nm_device_wifi_get_activation_ap (NMDeviceWifi *self); + +RfKillState nm_device_wifi_get_ipw_rfkill_state (NMDeviceWifi *self); + +G_END_DECLS + +#endif /* NM_DEVICE_WIFI_H */ diff --git a/src/nm-device.c b/src/nm-device.c new file mode 100644 index 00000000..176d90f9 --- /dev/null +++ b/src/nm-device.c @@ -0,0 +1,3827 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include <glib.h> +#include <glib/gi18n.h> +#include <dbus/dbus.h> +#include <netinet/in.h> +#include <string.h> +#include <net/if.h> +#include <unistd.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <arpa/inet.h> +#include <fcntl.h> + +#include "nm-glib-compat.h" +#include "nm-device-interface.h" +#include "nm-device.h" +#include "nm-device-private.h" +#include "NetworkManagerUtils.h" +#include "nm-system.h" +#include "nm-dhcp-manager.h" +#include "nm-dbus-manager.h" +#include "nm-named-manager.h" +#include "nm-utils.h" +#include "nm-logging.h" +#include "nm-netlink-monitor.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-ip6-config.h" +#include "nm-setting-connection.h" +#include "nm-dnsmasq-manager.h" +#include "nm-dhcp4-config.h" +#include "nm-ip6-manager.h" +#include "nm-marshal.h" +#include "nm-rfkill.h" + +#define NM_ACT_REQUEST_IP4_CONFIG "nm-act-request-ip4-config" +#define NM_ACT_REQUEST_IP6_CONFIG "nm-act-request-ip6-config" + +static void device_interface_init (NMDeviceInterface *device_interface_class); + +G_DEFINE_TYPE_EXTENDED (NMDevice, nm_device, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, + G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_INTERFACE, device_interface_init)) + +enum { + AUTOCONNECT_ALLOWED, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +#define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate)) + +typedef struct { + gboolean disposed; + gboolean initialized; + + NMDeviceState state; + guint failed_to_disconnected_id; + guint unavailable_to_disconnected_id; + + char * udi; + char * path; + char * iface; /* may change, could be renamed by user */ + int ifindex; + char * ip_iface; + int ip_ifindex; + NMDeviceType type; + char * type_desc; + guint32 capabilities; + char * driver; + gboolean managed; /* whether managed by NM or not */ + RfKillType rfkill_type; + + guint32 ip4_address; + + NMActRequest * act_request; + guint act_source_id; + gpointer act_source_func; + guint act_source6_id; + gpointer act_source6_func; + gulong secrets_updated_id; + gulong secrets_failed_id; + + gboolean ip4_ready; + gboolean ip6_ready; + + /* Generic DHCP stuff */ + NMDHCPManager * dhcp_manager; + guint32 dhcp_timeout; + GByteArray * dhcp_anycast_address; + + /* IP4 configuration info */ + NMIP4Config * ip4_config; /* Config from DHCP, PPP, or system config files */ + NMDHCPClient * dhcp4_client; + gulong dhcp4_state_sigid; + gulong dhcp4_timeout_sigid; + NMDHCP4Config * dhcp4_config; + + /* dnsmasq stuff for shared connections */ + NMDnsMasqManager *dnsmasq_manager; + gulong dnsmasq_state_id; + + /* avahi-autoipd stuff */ + GPid aipd_pid; + guint aipd_watch; + guint aipd_timeout; + guint32 aipd_addr; + + /* IP6 configuration info */ + NMIP6Config * ip6_config; + NMIP6Manager * ip6_manager; + gulong ip6_addrconf_sigid; + gulong ip6_config_changed_sigid; + gboolean ip6_waiting_for_config; + + char * ip6_accept_ra_path; + guint32 ip6_accept_ra_save; + + NMDHCPClient * dhcp6_client; + guint32 dhcp6_mode; + gulong dhcp6_state_sigid; + gulong dhcp6_timeout_sigid; + NMDHCP6Config * dhcp6_config; + + /* inhibit autoconnect feature */ + gboolean autoconnect_inhibit; +} NMDevicePrivate; + +static gboolean check_connection_compatible (NMDeviceInterface *device, + NMConnection *connection, + GError **error); +static gboolean nm_device_activate (NMDeviceInterface *device, + NMActRequest *req, + GError **error); +static void nm_device_deactivate (NMDeviceInterface *device, NMDeviceStateReason reason); +static gboolean device_disconnect (NMDeviceInterface *device, GError **error); +static gboolean spec_match_list (NMDeviceInterface *device, const GSList *specs); +static NMConnection *connection_match_config (NMDeviceInterface *device, const GSList *connections); + +static void nm_device_activate_schedule_stage5_ip_config_commit (NMDevice *self, int family); + +static void nm_device_take_down (NMDevice *dev, gboolean wait, NMDeviceStateReason reason); + +static gboolean nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware); +static gboolean nm_device_is_up (NMDevice *self); + +static gboolean nm_device_set_ip4_config (NMDevice *dev, + NMIP4Config *config, + gboolean assumed, + NMDeviceStateReason *reason); +static gboolean nm_device_set_ip6_config (NMDevice *dev, + NMIP6Config *config, + gboolean assumed, + NMDeviceStateReason *reason); + +static NMActStageReturn dhcp6_start (NMDevice *self, + NMConnection *connection, + guint32 dhcp_opt, + NMDeviceStateReason *reason); + +static void addrconf6_cleanup (NMDevice *self); +static void dhcp6_cleanup (NMDevice *self, gboolean stop); +static void dhcp4_cleanup (NMDevice *self, gboolean stop); + + +static void +device_interface_init (NMDeviceInterface *device_interface_class) +{ + /* interface implementation */ + device_interface_class->check_connection_compatible = check_connection_compatible; + device_interface_class->activate = nm_device_activate; + device_interface_class->deactivate = nm_device_deactivate; + device_interface_class->disconnect = device_disconnect; + device_interface_class->spec_match_list = spec_match_list; + device_interface_class->connection_match_config = connection_match_config; +} + + +static void +nm_device_init (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + priv->type = NM_DEVICE_TYPE_UNKNOWN; + priv->capabilities = NM_DEVICE_CAP_NONE; + priv->state = NM_DEVICE_STATE_UNMANAGED; + priv->dhcp_timeout = 0; + priv->rfkill_type = RFKILL_TYPE_UNKNOWN; +} + +static void +update_accept_ra_save (NMDevice *self) +{ + NMDevicePrivate *priv; + const char *ip_iface; + char *new_path; + + g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_DEVICE (self)); + + priv = NM_DEVICE_GET_PRIVATE (self); + ip_iface = nm_device_get_ip_iface (self); + + new_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/accept_ra", ip_iface); + g_assert (new_path); + + if (priv->ip6_accept_ra_path) { + /* If the IP iface is different from before, use the new value */ + if (!strcmp (new_path, priv->ip6_accept_ra_path)) { + g_free (new_path); + return; + } + g_free (priv->ip6_accept_ra_path); + } + + /* Grab the original value of "accept_ra" so we can restore it when NM exits */ + priv->ip6_accept_ra_path = new_path; + if (!nm_utils_get_proc_sys_net_value (priv->ip6_accept_ra_path, + ip_iface, + &priv->ip6_accept_ra_save)) { + g_free (priv->ip6_accept_ra_path); + priv->ip6_accept_ra_path = NULL; + } +} + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + NMDevice *dev; + NMDevicePrivate *priv; + + object = G_OBJECT_CLASS (nm_device_parent_class)->constructor (type, + n_construct_params, + construct_params); + if (!object) + return NULL; + + dev = NM_DEVICE (object); + priv = NM_DEVICE_GET_PRIVATE (dev); + + if (!priv->udi) { + nm_log_err (LOGD_DEVICE, "No device udi provided, ignoring"); + goto error; + } + + if (!priv->iface) { + nm_log_err (LOGD_DEVICE, "No device interface provided, ignoring"); + goto error; + } + + priv->capabilities |= NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities (dev); + if (!(priv->capabilities & NM_DEVICE_CAP_NM_SUPPORTED)) { + nm_log_warn (LOGD_DEVICE, "(%s): Device unsupported, ignoring.", priv->iface); + goto error; + } + + if (NM_DEVICE_GET_CLASS (dev)->update_hw_address) + NM_DEVICE_GET_CLASS (dev)->update_hw_address (dev); + + priv->dhcp_manager = nm_dhcp_manager_get (); + + update_accept_ra_save (dev); + + priv->initialized = TRUE; + return object; + +error: + g_object_unref (dev); + return NULL; +} + +static gboolean +nm_device_hw_is_up (NMDevice *self) +{ + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + if (NM_DEVICE_GET_CLASS (self)->hw_is_up) + return NM_DEVICE_GET_CLASS (self)->hw_is_up (self); + + return TRUE; +} + +static guint32 +real_get_generic_capabilities (NMDevice *dev) +{ + return 0; +} + +void +nm_device_set_path (NMDevice *self, const char *path) +{ + NMDevicePrivate *priv; + + g_return_if_fail (self != NULL); + + priv = NM_DEVICE_GET_PRIVATE (self); + g_return_if_fail (priv->path == NULL); + + priv->path = g_strdup (path); +} + +const char * +nm_device_get_path (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + return NM_DEVICE_GET_PRIVATE (self)->path; +} + +const char * +nm_device_get_udi (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + return NM_DEVICE_GET_PRIVATE (self)->udi; +} + +/* + * Get/set functions for iface + */ +const char * +nm_device_get_iface (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + return NM_DEVICE_GET_PRIVATE (self)->iface; +} + +int +nm_device_get_ifindex (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, 0); + + return NM_DEVICE_GET_PRIVATE (self)->ifindex; +} + +const char * +nm_device_get_ip_iface (NMDevice *self) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail (self != NULL, NULL); + + priv = NM_DEVICE_GET_PRIVATE (self); + /* If it's not set, default to iface */ + return priv->ip_iface ? priv->ip_iface : priv->iface; +} + +int +nm_device_get_ip_ifindex (NMDevice *self) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail (self != NULL, 0); + + priv = NM_DEVICE_GET_PRIVATE (self); + /* If it's not set, default to iface */ + return priv->ip_iface ? priv->ip_ifindex : priv->ifindex; +} + +void +nm_device_set_ip_iface (NMDevice *self, const char *iface) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (self)); + + priv = NM_DEVICE_GET_PRIVATE (self); + g_free (priv->ip_iface); + priv->ip_ifindex = 0; + + priv->ip_iface = g_strdup (iface); + if (priv->ip_iface) { + priv->ip_ifindex = nm_netlink_iface_to_index (priv->ip_iface); + if (!priv->ip_ifindex) { + nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", iface); + } + } +} + + +/* + * Get/set functions for driver + */ +const char * +nm_device_get_driver (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + return NM_DEVICE_GET_PRIVATE (self)->driver; +} + + +/* + * Get/set functions for type + */ +NMDeviceType +nm_device_get_device_type (NMDevice *self) +{ + g_return_val_if_fail (NM_IS_DEVICE (self), NM_DEVICE_TYPE_UNKNOWN); + + return NM_DEVICE_GET_PRIVATE (self)->type; +} + + +int +nm_device_get_priority (NMDevice *dev) +{ + g_return_val_if_fail (NM_IS_DEVICE (dev), -1); + + return (int) nm_device_get_device_type (dev); +} + + +/* + * Accessor for capabilities + */ +guint32 +nm_device_get_capabilities (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, NM_DEVICE_CAP_NONE); + + return NM_DEVICE_GET_PRIVATE (self)->capabilities; +} + +/* + * Accessor for type-specific capabilities + */ +guint32 +nm_device_get_type_capabilities (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, NM_DEVICE_CAP_NONE); + + return NM_DEVICE_GET_CLASS (self)->get_type_capabilities (self); +} + +static guint32 +real_get_type_capabilities (NMDevice *self) +{ + return NM_DEVICE_CAP_NONE; +} + + +const char * +nm_device_get_type_desc (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + return NM_DEVICE_GET_PRIVATE (self)->type_desc; +} + +/* + * nm_device_get_act_request + * + * Return the devices activation request, if any. + * + */ +NMActRequest * +nm_device_get_act_request (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + return NM_DEVICE_GET_PRIVATE (self)->act_request; +} + + +gboolean +nm_device_is_available (NMDevice *self) +{ + if (NM_DEVICE_GET_CLASS (self)->is_available) + return NM_DEVICE_GET_CLASS (self)->is_available (self); + return TRUE; +} + +static gboolean +autoconnect_allowed_accumulator (GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, gpointer data) +{ + if (!g_value_get_boolean (handler_return)) + g_value_set_boolean (return_accu, FALSE); + return TRUE; +} + +gboolean +nm_device_autoconnect_allowed (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + GValue instance = { 0, }; + GValue retval = { 0, }; + + g_value_init (&instance, G_TYPE_OBJECT); + g_value_take_object (&instance, self); + + g_value_init (&retval, G_TYPE_BOOLEAN); + if (priv->autoconnect_inhibit) + g_value_set_boolean (&retval, FALSE); + else + g_value_set_boolean (&retval, TRUE); + + /* Use g_signal_emitv() rather than g_signal_emit() to avoid the return + * value being changed if no handlers are connected */ + g_signal_emitv (&instance, signals[AUTOCONNECT_ALLOWED], 0, &retval); + return g_value_get_boolean (&retval); +} + +NMConnection * +nm_device_get_best_auto_connection (NMDevice *dev, + GSList *connections, + char **specific_object) +{ + guint32 caps; + + g_return_val_if_fail (NM_IS_DEVICE (dev), NULL); + g_return_val_if_fail (specific_object != NULL, NULL); + g_return_val_if_fail (*specific_object == NULL, NULL); + + caps = nm_device_get_capabilities (dev); + /* Don't use devices that SUCK */ + if (!(caps & NM_DEVICE_CAP_NM_SUPPORTED)) + return NULL; + + if (!NM_DEVICE_GET_CLASS (dev)->get_best_auto_connection) + return NULL; + + return NM_DEVICE_GET_CLASS (dev)->get_best_auto_connection (dev, connections, specific_object); +} + +static void +dnsmasq_state_changed_cb (NMDnsMasqManager *manager, guint32 status, gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + + switch (status) { + case NM_DNSMASQ_STATUS_DEAD: + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SHARED_START_FAILED); + break; + default: + break; + } +} + +static void +activation_source_clear (NMDevice *self, gboolean remove_source, int family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + guint *act_source_id; + gpointer *act_source_func; + + if (family == AF_INET6) { + act_source_id = &priv->act_source6_id; + act_source_func = &priv->act_source6_func; + } else { + act_source_id = &priv->act_source_id; + act_source_func = &priv->act_source_func; + } + + if (*act_source_id) { + if (remove_source) + g_source_remove (*act_source_id); + *act_source_id = 0; + *act_source_func = NULL; + } +} + +static void +activation_source_schedule (NMDevice *self, GSourceFunc func, int family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + guint *act_source_id; + gpointer *act_source_func; + + if (family == AF_INET6) { + act_source_id = &priv->act_source6_id; + act_source_func = &priv->act_source6_func; + } else { + act_source_id = &priv->act_source_id; + act_source_func = &priv->act_source_func; + } + + if (*act_source_id) { + nm_log_err (LOGD_DEVICE, "activation stage already scheduled"); + } + + /* Don't bother rescheduling the same function that's about to + * run anyway. Fixes issues with crappy wireless drivers sending + * streams of associate events before NM has had a chance to process + * the first one. + */ + if (!*act_source_id || (*act_source_func != func)) { + activation_source_clear (self, TRUE, family); + *act_source_id = g_idle_add (func, self); + *act_source_func = func; + } +} + +gboolean +nm_device_ip_config_should_fail (NMDevice *self, gboolean ip6) +{ + NMActRequest *req; + NMConnection *connection; + NMSettingIP4Config *s_ip4; + NMSettingIP6Config *s_ip6; + + g_return_val_if_fail (self != NULL, TRUE); + + req = nm_device_get_act_request (self); + g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); + + /* Fail the connection if the failed IP method is required to complete */ + if (ip6) { + s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); + if (s_ip6 && !nm_setting_ip6_config_get_may_fail (s_ip6)) + return TRUE; + } else { + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + if (s_ip4 && !nm_setting_ip4_config_get_may_fail (s_ip4)) + return TRUE; + } + + return FALSE; +} + +static void +ip6_addrconf_complete (NMIP6Manager *ip6_manager, + int ifindex, + guint dhcp_opts, + gboolean success, + gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMActRequest *req; + NMConnection *connection; + NMActStageReturn ret; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + NMDeviceState state; + + if (ifindex != nm_device_get_ip_ifindex (self)) + return; + req = nm_device_get_act_request (self); + if (!req) + return; + connection = nm_act_request_get_connection (req); + g_assert (connection); + + if (!priv->ip6_waiting_for_config) + return; + + priv->ip6_waiting_for_config = FALSE; + + if (!success) { + nm_device_activate_schedule_stage4_ip6_config_timeout (self); + return; + } + + priv->dhcp6_mode = dhcp_opts; + + /* If addrconf is all that's required, we're done */ + if (priv->dhcp6_mode == IP6_DHCP_OPT_NONE) { + nm_device_activate_schedule_stage4_ip6_config_get (self); + return; + } + + /* If the router said to use DHCP for managed or otherconf, do it */ + + /* Don't re-start DHCPv6 if it's already in progress */ + state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self)); + if ((state != NM_DEVICE_STATE_IP_CONFIG) || priv->dhcp6_client) + return; + + nm_log_info (LOGD_DEVICE | LOGD_DHCP6, + "Activation (%s) Stage 3 of 5 (IP Configure Start) starting DHCPv6" + " as requested by IPv6 router...", + priv->iface); + + ret = dhcp6_start (self, connection, priv->dhcp6_mode, &reason); + switch (ret) { + case NM_ACT_STAGE_RETURN_SUCCESS: + /* Shouldn't get this, but handle it anyway */ + g_warn_if_reached (); + nm_device_activate_schedule_stage4_ip6_config_get (self); + break; + case NM_ACT_STAGE_RETURN_POSTPONE: + /* Success; wait for DHCPv6 to complete */ + break; + default: + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + break; + } +} + +static void +ip6_config_changed (NMIP6Manager *ip6_manager, + int ifindex, + guint dhcp_opts, + gboolean success, + gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + + if (ifindex != nm_device_get_ip_ifindex (self)) + return; + if (!nm_device_get_act_request (self)) + return; + + /* FIXME: re-run DHCPv6 here to get any new nameservers or whatever */ + + if (!success && (nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED)) { + nm_device_state_changed (self, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + return; + } + + nm_device_activate_schedule_stage4_ip6_config_get (self); +} + +static gboolean +ip6_method_matches (NMConnection *connection, const char *match) +{ + NMSettingIP6Config *s_ip6; + const char *method = NULL; + + s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); + if (s_ip6) { + method = nm_setting_ip6_config_get_method (s_ip6); + g_assert (method); + } + + /* Treat missing IP6 setting as IGNORE */ + if (!s_ip6 && !strcmp (match, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) + return TRUE; + + return method && !strcmp (method, match); +} + +static gboolean +addrconf6_setup (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMActRequest *req; + NMConnection *connection; + NMSettingIP6Config *s_ip6; + + req = nm_device_get_act_request (self); + g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); + + if (!priv->ip6_manager) { + priv->ip6_manager = nm_ip6_manager_get (); + priv->ip6_addrconf_sigid = g_signal_connect (priv->ip6_manager, + "addrconf-complete", + G_CALLBACK (ip6_addrconf_complete), + self); + priv->ip6_config_changed_sigid = g_signal_connect (priv->ip6_manager, + "config-changed", + G_CALLBACK (ip6_config_changed), + self); + } + + s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); + nm_ip6_manager_prepare_interface (priv->ip6_manager, + nm_device_get_ip_ifindex (self), + s_ip6, + priv->ip6_accept_ra_path); + priv->ip6_waiting_for_config = TRUE; + + return TRUE; +} + +static void +addrconf6_cleanup (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (!priv->ip6_manager) + return; + + if (priv->ip6_addrconf_sigid) { + g_signal_handler_disconnect (priv->ip6_manager, + priv->ip6_addrconf_sigid); + priv->ip6_addrconf_sigid = 0; + } + if (priv->ip6_config_changed_sigid) { + g_signal_handler_disconnect (priv->ip6_manager, + priv->ip6_config_changed_sigid); + priv->ip6_config_changed_sigid = 0; + } + + nm_ip6_manager_cancel_addrconf (priv->ip6_manager, nm_device_get_ip_ifindex (self)); + g_object_unref (priv->ip6_manager); + priv->ip6_manager = NULL; +} + +static NMActStageReturn +real_act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason) +{ + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +/* + * nm_device_activate_stage1_device_prepare + * + * Prepare for device activation + * + */ +static gboolean +nm_device_activate_stage1_device_prepare (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const char *iface; + NMActStageReturn ret; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + + /* Clear the activation source ID now that this stage has run */ + activation_source_clear (self, FALSE, 0); + + priv->ip4_ready = priv->ip6_ready = FALSE; + + iface = nm_device_get_iface (self); + nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) started...", iface); + nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); + + ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &reason); + if (ret == NM_ACT_STAGE_RETURN_POSTPONE) { + goto out; + } else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + goto out; + } + g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); + + nm_device_activate_schedule_stage2_device_config (self); + +out: + nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) complete.", iface); + return FALSE; +} + + +/* + * nm_device_activate_schedule_stage1_device_prepare + * + * Prepare a device for activation + * + */ +void +nm_device_activate_schedule_stage1_device_prepare (NMDevice *self) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (self)); + + priv = NM_DEVICE_GET_PRIVATE (self); + g_return_if_fail (priv->act_request); + + activation_source_schedule (self, nm_device_activate_stage1_device_prepare, 0); + + nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) scheduled...", + nm_device_get_iface (self)); +} + +static NMActStageReturn +real_act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) +{ + /* Nothing to do */ + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +/* + * nm_device_activate_stage2_device_config + * + * Determine device parameters and set those on the device, ie + * for wireless devices, set SSID, keys, etc. + * + */ +static gboolean +nm_device_activate_stage2_device_config (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + const char * iface; + NMActStageReturn ret; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + gboolean no_firmware = FALSE; + + /* Clear the activation source ID now that this stage has run */ + activation_source_clear (self, FALSE, 0); + + iface = nm_device_get_iface (self); + nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) starting...", iface); + nm_device_state_changed (self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE); + + if (!nm_device_bring_up (self, FALSE, &no_firmware)) { + if (no_firmware) + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_FIRMWARE_MISSING); + else + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CONFIG_FAILED); + goto out; + } + + ret = NM_DEVICE_GET_CLASS (self)->act_stage2_config (self, &reason); + if (ret == NM_ACT_STAGE_RETURN_POSTPONE) + goto out; + else if (ret == NM_ACT_STAGE_RETURN_FAILURE) + { + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + goto out; + } + g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); + + nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) successful.", iface); + + nm_device_activate_schedule_stage3_ip_config_start (self); + +out: + nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) complete.", iface); + return FALSE; +} + + +/* + * nm_device_activate_schedule_stage2_device_config + * + * Schedule setup of the hardware device + * + */ +void +nm_device_activate_schedule_stage2_device_config (NMDevice *self) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (self)); + + priv = NM_DEVICE_GET_PRIVATE (self); + g_return_if_fail (priv->act_request); + + activation_source_schedule (self, nm_device_activate_stage2_device_config, 0); + + nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) scheduled...", + nm_device_get_iface (self)); +} + +static void +aipd_timeout_remove (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->aipd_timeout) { + g_source_remove (priv->aipd_timeout); + priv->aipd_timeout = 0; + } +} + +static void +aipd_cleanup (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->aipd_watch) { + g_source_remove (priv->aipd_watch); + priv->aipd_watch = 0; + } + + if (priv->aipd_pid > 0) { + kill (priv->aipd_pid, SIGKILL); + + /* ensure the child is reaped */ + nm_log_dbg (LOGD_AUTOIP4, "waiting for avahi-autoipd pid %d to exit", priv->aipd_pid); + waitpid (priv->aipd_pid, NULL, 0); + nm_log_dbg (LOGD_AUTOIP4, "avahi-autoip pid %d cleaned up", priv->aipd_pid); + + priv->aipd_pid = -1; + } + + aipd_timeout_remove (self); + + priv->aipd_addr = 0; +} + +static NMIP4Config * +aipd_get_ip4_config (NMDevice *self, NMDeviceStateReason *reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMIP4Config *config = NULL; + NMIP4Address *addr; + NMIP4Route *route; + + g_return_val_if_fail (priv->aipd_addr > 0, NULL); + + config = nm_ip4_config_new (); + if (!config) { + *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; + return NULL; + } + + addr = nm_ip4_address_new (); + nm_ip4_address_set_address (addr, (guint32) priv->aipd_addr); + nm_ip4_address_set_prefix (addr, 16); + nm_ip4_config_take_address (config, addr); + + /* Add a multicast route for link-local connections: destination= 224.0.0.0, netmask=240.0.0.0 */ + route = nm_ip4_route_new (); + nm_ip4_route_set_dest (route, (guint32) htonl (0xE0000000L)); + nm_ip4_route_set_prefix (route, 4); + nm_ip4_route_set_next_hop (route, (guint32) 0); + nm_ip4_route_set_metric (route, 0); + nm_ip4_config_take_route (config, route); + + return config; +} + +static gboolean +handle_autoip_change (NMDevice *self, NMDeviceStateReason *reason) +{ + NMActRequest *req; + NMConnection *connection; + NMIP4Config *config; + + g_return_val_if_fail (reason != NULL, FALSE); + + config = aipd_get_ip4_config (self, reason); + if (!config) { + nm_log_err (LOGD_AUTOIP4, "failed to get autoip config for rebind"); + return FALSE; + } + + req = nm_device_get_act_request (self); + g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); + + g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP4_CONFIG, config); + + if (!nm_device_set_ip4_config (self, config, FALSE, reason)) { + nm_log_err (LOGD_AUTOIP4, "(%s): failed to update IP4 config in response to autoip event.", + nm_device_get_iface (self)); + return FALSE; + } + + return TRUE; +} + +#define IPV4LL_NETWORK (htonl (0xA9FE0000L)) +#define IPV4LL_NETMASK (htonl (0xFFFF0000L)) + +void +nm_device_handle_autoip4_event (NMDevice *self, + const char *event, + const char *address) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMActRequest *req; + NMConnection *connection = NULL; + NMSettingIP4Config *s_ip4 = NULL; + NMDeviceState state; + const char *iface, *method = NULL; + + g_return_if_fail (event != NULL); + + req = nm_device_get_act_request (self); + if (!req) + return; + + connection = nm_act_request_get_connection (req); + if (!connection) + return; + + /* Ignore if the connection isn't an AutoIP connection */ + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + if (s_ip4) + method = nm_setting_ip4_config_get_method (s_ip4); + + if (!s_ip4 || !method || strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) + return; + + iface = nm_device_get_iface (self); + state = nm_device_get_state (self); + + if (strcmp (event, "BIND") == 0) { + struct in_addr ip; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + + if (inet_pton (AF_INET, address, &ip) <= 0) { + nm_log_err (LOGD_AUTOIP4, "(%s): invalid address %s received from avahi-autoipd.", + iface, address); + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_AUTOIP_ERROR); + return; + } + + if ((ip.s_addr & IPV4LL_NETMASK) != IPV4LL_NETWORK) { + nm_log_err (LOGD_AUTOIP4, "(%s): invalid address %s received from avahi-autoipd (not link-local).", + iface, address); + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_AUTOIP_ERROR); + return; + } + + switch (state) { + case NM_DEVICE_STATE_IP_CONFIG: + if (priv->aipd_addr) { + nm_log_warn (LOGD_AUTOIP4, "(%s): already have autoip address!", iface); + return; + } + + priv->aipd_addr = ip.s_addr; + aipd_timeout_remove (self); + nm_device_activate_schedule_stage4_ip4_config_get (self); + break; + case NM_DEVICE_STATE_ACTIVATED: + priv->aipd_addr = ip.s_addr; + if (!handle_autoip_change (self, &reason)) + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + break; + default: + nm_log_warn (LOGD_AUTOIP4, "(%s): unexpected avahi-autoip event %s for %s.", + iface, event, address); + break; + } + } else { + nm_log_warn (LOGD_AUTOIP4, "(%s): autoip address %s no longer valid because '%s'.", + iface, address, event); + + /* The address is gone; terminate the connection or fail activation */ + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + } +} + +static void +aipd_watch_cb (GPid pid, gint status, gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDeviceState state; + const char *iface; + + if (!priv->aipd_watch) + return; + priv->aipd_watch = 0; + + iface = nm_device_get_iface (self); + + if (WIFEXITED (status)) { + nm_log_dbg (LOGD_AUTOIP4, "(%s): avahi-autoipd exited with error code %d", + iface, WEXITSTATUS (status)); + } else if (WIFSTOPPED (status)) { + nm_log_warn (LOGD_AUTOIP4, "(%s): avahi-autoipd stopped unexpectedly with signal %d", + iface, WSTOPSIG (status)); + } else if (WIFSIGNALED (status)) { + nm_log_warn (LOGD_AUTOIP4, "(%s): avahi-autoipd died with signal %d", + iface, WTERMSIG (status)); + } else { + nm_log_warn (LOGD_AUTOIP4, "(%s): avahi-autoipd died from an unknown cause", iface); + } + + aipd_cleanup (self); + + state = nm_device_get_state (self); + if (nm_device_is_activating (self) || (state == NM_DEVICE_STATE_ACTIVATED)) + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_AUTOIP_FAILED); +} + +static gboolean +aipd_timeout_cb (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (!priv->aipd_timeout) + return FALSE; + priv->aipd_timeout = 0; + + nm_log_info (LOGD_AUTOIP4, "(%s): avahi-autoipd timed out.", nm_device_get_iface (self)); + aipd_cleanup (self); + + if (nm_device_get_state (self) == NM_DEVICE_STATE_IP_CONFIG) + nm_device_activate_schedule_stage4_ip4_config_timeout (self); + + return FALSE; +} + +static void +aipd_child_setup (gpointer user_data G_GNUC_UNUSED) +{ + /* We are in the child process at this point. + * Give child it's own program group for signal + * separation. + */ + pid_t pid = getpid (); + setpgid (pid, pid); +} + +static gboolean +aipd_exec (NMDevice *self, GError **error) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + char *argv[5]; + gboolean success = FALSE; + const char **aipd_binary = NULL; + static const char *aipd_paths[] = { + "/usr/sbin/avahi-autoipd", + "/usr/local/sbin/avahi-autoipd", + NULL + }; + + aipd_cleanup (self); + + /* Find avahi-autoipd */ + aipd_binary = aipd_paths; + while (*aipd_binary != NULL) { + if (g_file_test (*aipd_binary, G_FILE_TEST_EXISTS)) + break; + aipd_binary++; + } + + if (!*aipd_binary) { + g_set_error (error, 0, 0, "%s", "couldn't find avahi-autoipd"); + return FALSE; + } + + argv[0] = (char *) (*aipd_binary); + argv[1] = "--script"; + argv[2] = LIBEXECDIR "/nm-avahi-autoipd.action"; + argv[3] = (char *) nm_device_get_ip_iface (self); + argv[4] = NULL; + + success = g_spawn_async ("/", argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, + &aipd_child_setup, NULL, &(priv->aipd_pid), error); + if (!success) + return FALSE; + + /* Monitor the child process so we know when it dies */ + priv->aipd_watch = g_child_watch_add (priv->aipd_pid, aipd_watch_cb, self); + + /* Start a timeout to bound the address attempt */ + priv->aipd_timeout = g_timeout_add_seconds (20, aipd_timeout_cb, self); + + return TRUE; +} + +static void +dhcp4_add_option_cb (gpointer key, gpointer value, gpointer user_data) +{ + nm_dhcp4_config_add_option (NM_DHCP4_CONFIG (user_data), + (const char *) key, + (const char *) value); +} + +static void +dhcp6_add_option_cb (gpointer key, gpointer value, gpointer user_data) +{ + nm_dhcp6_config_add_option (NM_DHCP6_CONFIG (user_data), + (const char *) key, + (const char *) value); +} + +static void +handle_dhcp_lease_change (NMDevice *device, gboolean ipv6) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMIP4Config *ip4_config; + NMSettingIP4Config *s_ip4; + NMIP6Config *ip6_config; + NMSettingIP6Config *s_ip6; + NMConnection *connection; + NMActRequest *req; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + gboolean assumed; + + req = nm_device_get_act_request (device); + g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); + assumed = nm_act_request_get_assumed (req); + + if (ipv6) { + ip6_config = nm_dhcp_client_get_ip6_config (priv->dhcp6_client, FALSE); + if (!ip6_config) { + nm_log_warn (LOGD_DHCP6, "(%s): failed to get DHCPv6 config for rebind", + nm_device_get_ip_iface (device)); + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + return; + } + + s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG)); + nm_utils_merge_ip6_config (ip6_config, s_ip6); + + g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP6_CONFIG, ip6_config); + + if (nm_device_set_ip6_config (device, ip6_config, assumed, &reason)) { + nm_dhcp6_config_reset (priv->dhcp6_config); + nm_dhcp_client_foreach_option (priv->dhcp6_client, + dhcp6_add_option_cb, + priv->dhcp6_config); + } else { + nm_log_warn (LOGD_DHCP6, "(%s): failed to update IPv6 config in response to DHCP event.", + nm_device_get_ip_iface (device)); + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); + } + } else { + ip4_config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE); + if (!ip4_config) { + nm_log_warn (LOGD_DHCP6, "(%s): failed to get DHCPv4 config for rebind", + nm_device_get_ip_iface (device)); + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + return; + } + + s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG)); + nm_utils_merge_ip4_config (ip4_config, s_ip4); + + g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP4_CONFIG, ip4_config); + + if (nm_device_set_ip4_config (device, ip4_config, assumed, &reason)) { + nm_dhcp4_config_reset (priv->dhcp4_config); + nm_dhcp_client_foreach_option (priv->dhcp4_client, + dhcp4_add_option_cb, + priv->dhcp4_config); + } else { + nm_log_warn (LOGD_DHCP6, "(%s): failed to update IPv4 config in response to DHCP event.", + nm_device_get_ip_iface (device)); + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); + } + } +} + +static void +dhcp_state_changed (NMDHCPClient *client, + NMDHCPState state, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDeviceState dev_state; + gboolean ipv6; + + ipv6 = nm_dhcp_client_get_ipv6 (client); + dev_state = nm_device_get_state (device); + + if (ipv6) { + nm_log_dbg (LOGD_DHCP6, "(%s): new DHCPv6 client state %d", + nm_device_get_iface (device), dev_state); + } else { + nm_log_dbg (LOGD_DHCP4, "(%s): new DHCPv4 client state %d", + nm_device_get_iface (device), dev_state); + } + + switch (state) { + case DHC_BOUND4: /* lease obtained */ + case DHC_BOUND6: + case DHC_RENEW4: /* lease renewed */ + case DHC_RENEW6: /* lease renewed */ + case DHC_REBOOT: /* have valid lease, but now obtained a different one */ + case DHC_REBIND4: /* new, different lease */ + case DHC_REBIND6: /* new, different lease */ + if (dev_state == NM_DEVICE_STATE_IP_CONFIG) { + if (ipv6) + nm_device_activate_schedule_stage4_ip6_config_get (device); + else + nm_device_activate_schedule_stage4_ip4_config_get (device); + } else if (dev_state == NM_DEVICE_STATE_ACTIVATED) + handle_dhcp_lease_change (device, ipv6); + break; + case DHC_TIMEOUT: /* timed out contacting DHCP server */ + if (ipv6) { + nm_dhcp6_config_reset (priv->dhcp6_config); + if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) + nm_device_activate_schedule_stage4_ip6_config_timeout (device); + } else { + nm_dhcp4_config_reset (priv->dhcp4_config); + if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) + nm_device_activate_schedule_stage4_ip4_config_timeout (device); + } + break; + case DHC_END: /* dhclient exited normally */ + /* In IPv6 info-only mode, the client doesn't handle leases so it + * may exit right after getting a response from the server. That's + * normal. In that case we just ignore the exit. + */ + if (ipv6 && (priv->dhcp6_mode == IP6_DHCP_OPT_OTHERCONF)) + break; + /* Otherwise, fall through */ + case DHC_FAIL: /* all attempts to contact server timed out, sleeping */ + case DHC_ABEND: /* dhclient exited abnormally */ + if (ipv6) + nm_dhcp6_config_reset (priv->dhcp6_config); + else + nm_dhcp4_config_reset (priv->dhcp4_config); + + /* dhclient quit and can't get/renew a lease; so kill the connection */ + if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) { + if (ipv6) + nm_device_activate_schedule_stage4_ip6_config_timeout (device); + else + nm_device_activate_schedule_stage4_ip4_config_timeout (device); + } else if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + break; + case DHC_STOP: + case DHC_STOP6: + case DHC_EXPIRE: + case DHC_EXPIRE6: + if (dev_state == NM_DEVICE_STATE_ACTIVATED) { + if (ipv6) + nm_dhcp6_config_reset (priv->dhcp6_config); + else + nm_dhcp4_config_reset (priv->dhcp4_config); + + /* dhclient quit and can't get/renew a lease; so kill the connection */ + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + } + break; + default: + break; + } +} + +static void +dhcp_timeout (NMDHCPClient *client, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + + if (!nm_device_get_act_request (device)) + return; + + nm_dhcp_client_stop (client); + + if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) { + if (nm_dhcp_client_get_ipv6 (client)) + nm_device_activate_schedule_stage4_ip6_config_timeout (device); + else + nm_device_activate_schedule_stage4_ip4_config_timeout (device); + } +} + +static NMActStageReturn +real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingIP4Config *s_ip4; + NMActRequest *req; + NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; + const char *ip_iface, *method = NULL, *uuid; + + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + /* Use the IP interface (not the control interface) for IP stuff */ + ip_iface = nm_device_get_ip_iface (self); + + /* Make sure the interface is up before trying to do anything with it */ + if (!nm_system_device_is_up_with_iface (ip_iface)) + nm_system_device_set_up_down_with_iface (ip_iface, TRUE, NULL); + + req = nm_device_get_act_request (self); + connection = nm_act_request_get_connection (req); + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + uuid = nm_setting_connection_get_uuid (s_con); + + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + + /* If we did not receive IP4 configuration information, default to DHCP */ + if (s_ip4) + method = nm_setting_ip4_config_get_method (s_ip4); + + if (!s_ip4 || !method || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { + guint8 *anycast = NULL; + + /* Begin a DHCP transaction on the interface */ + + if (priv->dhcp_anycast_address) + anycast = priv->dhcp_anycast_address->data; + + /* Clear old exported DHCP options */ + if (priv->dhcp4_config) + g_object_unref (priv->dhcp4_config); + priv->dhcp4_config = nm_dhcp4_config_new (); + + priv->dhcp4_client = nm_dhcp_manager_start_ip4 (priv->dhcp_manager, + ip_iface, + uuid, + s_ip4, + priv->dhcp_timeout, + anycast); + if (priv->dhcp4_client) { + priv->dhcp4_state_sigid = g_signal_connect (priv->dhcp4_client, + "state-changed", + G_CALLBACK (dhcp_state_changed), + self); + priv->dhcp4_timeout_sigid = g_signal_connect (priv->dhcp4_client, + "timeout", + G_CALLBACK (dhcp_timeout), + self); + + /* DHCP devices will be notified by the DHCP manager when + * stuff happens. + */ + ret = NM_ACT_STAGE_RETURN_POSTPONE; + } else { + *reason = NM_DEVICE_STATE_REASON_DHCP_START_FAILED; + ret = NM_ACT_STAGE_RETURN_FAILURE; + } + } else if (s_ip4 && !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) { + GError *error = NULL; + const char *iface = nm_device_get_iface (self); + + /* Start avahi-autoipd */ + if (aipd_exec (self, &error)) { + nm_log_info (LOGD_DEVICE | LOGD_AUTOIP4, + "Activation (%s) Stage 3 of 5 (IP Configure Start) started" + " avahi-autoipd...", iface); + ret = NM_ACT_STAGE_RETURN_POSTPONE; + } else { + nm_log_info (LOGD_DEVICE | LOGD_AUTOIP4, + "Activation (%s) Stage 3 of 5 (IP Configure Start) failed" + " to start avahi-autoipd: %s", iface, error->message); + g_error_free (error); + aipd_cleanup (self); + *reason = NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED; + ret = NM_ACT_STAGE_RETURN_FAILURE; + } + } else if (s_ip4 && !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) { + /* Nothing to do... */ + priv->ip4_ready = TRUE; + ret = NM_ACT_STAGE_RETURN_STOP; + } + + return ret; +} + +static NMActStageReturn +dhcp6_start (NMDevice *self, + NMConnection *connection, + guint32 dhcp_opt, + NMDeviceStateReason *reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + guint8 *anycast = NULL; + NMSettingIP6Config *s_ip6; + NMSettingConnection *s_con; + const char *uuid; + const char *ip_iface; + + if (!connection) { + NMActRequest *req; + + req = nm_device_get_act_request (self); + g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); + } + + /* Begin a DHCP transaction on the interface */ + + if (priv->dhcp_anycast_address) + anycast = priv->dhcp_anycast_address->data; + + /* Clear old exported DHCP options */ + if (priv->dhcp6_config) + g_object_unref (priv->dhcp6_config); + priv->dhcp6_config = nm_dhcp6_config_new (); + + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + uuid = nm_setting_connection_get_uuid (s_con); + + s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); + + ip_iface = nm_device_get_ip_iface (self); + priv->dhcp6_client = nm_dhcp_manager_start_ip6 (priv->dhcp_manager, + ip_iface, + uuid, + s_ip6, + priv->dhcp_timeout, + anycast, + (dhcp_opt == IP6_DHCP_OPT_OTHERCONF) ? TRUE : FALSE); + if (priv->dhcp6_client) { + priv->dhcp6_state_sigid = g_signal_connect (priv->dhcp6_client, + "state-changed", + G_CALLBACK (dhcp_state_changed), + self); + priv->dhcp6_timeout_sigid = g_signal_connect (priv->dhcp6_client, + "timeout", + G_CALLBACK (dhcp_timeout), + self); + + /* DHCP devices will be notified by the DHCP manager when stuff happens */ + ret = NM_ACT_STAGE_RETURN_POSTPONE; + } else { + *reason = NM_DEVICE_STATE_REASON_DHCP_START_FAILED; + ret = NM_ACT_STAGE_RETURN_FAILURE; + } + + return ret; +} + +static NMActStageReturn +real_act_stage3_ip6_config_start (NMDevice *self, NMDeviceStateReason *reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const char *ip_iface; + NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + NMActRequest *req; + NMConnection *connection; + + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + req = nm_device_get_act_request (self); + g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); + + ip_iface = nm_device_get_ip_iface (self); + + update_accept_ra_save (self); + + priv->dhcp6_mode = IP6_DHCP_OPT_NONE; + + if ( ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_AUTO) + || ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) { + if (!addrconf6_setup (self)) { + *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; + goto out; + } + nm_ip6_manager_begin_addrconf (priv->ip6_manager, nm_device_get_ip_ifindex (self)); + ret = NM_ACT_STAGE_RETURN_POSTPONE; + } else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { + /* reset the saved RA value when ipv6 is ignored */ + if (priv->ip6_accept_ra_path) { + nm_utils_do_sysctl (priv->ip6_accept_ra_path, + priv->ip6_accept_ra_save ? "1\n" : "0\n"); + } + + priv->ip6_ready = TRUE; + ret = NM_ACT_STAGE_RETURN_STOP; + } else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + /* Router advertisements shouldn't be used in manual mode */ + if (priv->ip6_accept_ra_path) + nm_utils_do_sysctl (priv->ip6_accept_ra_path, "0\n"); + ret = NM_ACT_STAGE_RETURN_SUCCESS; + } + + /* Other methods (shared) aren't implemented yet */ + +out: + return ret; +} + + +/* + * nm_device_activate_stage3_ip_config_start + * + * Begin automatic/manual IP configuration + * + */ +static gboolean +nm_device_activate_stage3_ip_config_start (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + const char *iface; + NMActStageReturn ret; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + + /* Clear the activation source ID now that this stage has run */ + activation_source_clear (self, FALSE, 0); + + iface = nm_device_get_iface (self); + nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) started...", iface); + nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); + + ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip4_config_start (self, &reason); + if (ret == NM_ACT_STAGE_RETURN_SUCCESS) + nm_device_activate_schedule_stage4_ip4_config_get (self); + else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + goto out; + } else if (ret == NM_ACT_STAGE_RETURN_STOP) { + /* Nothing to do */ + } else + g_assert (ret == NM_ACT_STAGE_RETURN_POSTPONE); + + ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip6_config_start (self, &reason); + if (ret == NM_ACT_STAGE_RETURN_SUCCESS) + nm_device_activate_schedule_stage4_ip6_config_get (self); + else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + goto out; + } else if (ret == NM_ACT_STAGE_RETURN_STOP) { + /* Nothing to do */ + } else + g_assert (ret == NM_ACT_STAGE_RETURN_POSTPONE); + +out: + nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) complete.", iface); + return FALSE; +} + + +/* + * nm_device_activate_schedule_stage3_ip_config_start + * + * Schedule IP configuration start + */ +void +nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (self)); + + priv = NM_DEVICE_GET_PRIVATE (self); + g_return_if_fail (priv->act_request); + + activation_source_schedule (self, nm_device_activate_stage3_ip_config_start, 0); + + nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) scheduled.", + nm_device_get_iface (self)); +} + +static GHashTable *shared_ips = NULL; + +static void +release_shared_ip (gpointer data) +{ + g_hash_table_remove (shared_ips, data); +} + +static guint32 +reserve_shared_ip (void) +{ + guint32 start = (guint32) ntohl (0x0a2a2b01); /* 10.42.43.1 */ + guint32 count = 0; + + while (g_hash_table_lookup (shared_ips, GUINT_TO_POINTER (start + count))) { + count += ntohl (0x100); + if (count > ntohl (0xFE00)) { + nm_log_err (LOGD_SHARING, "ran out of shared IP addresses!"); + return 0; + } + } + + g_hash_table_insert (shared_ips, GUINT_TO_POINTER (start + count), GUINT_TO_POINTER (TRUE)); + return start + count; +} + +static NMIP4Config * +nm_device_new_ip4_shared_config (NMDevice *self, NMDeviceStateReason *reason) +{ + NMIP4Config *config = NULL; + NMIP4Address *addr; + guint32 tmp_addr; + + g_return_val_if_fail (self != NULL, NULL); + + if (G_UNLIKELY (shared_ips == NULL)) + shared_ips = g_hash_table_new (g_direct_hash, g_direct_equal); + + tmp_addr = reserve_shared_ip (); + if (!tmp_addr) { + *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; + return NULL; + } + + config = nm_ip4_config_new (); + addr = nm_ip4_address_new (); + nm_ip4_address_set_address (addr, tmp_addr); + nm_ip4_address_set_prefix (addr, 24); + nm_ip4_config_take_address (config, addr); + + /* Remove the address lock when the object gets disposed */ + g_object_set_data_full (G_OBJECT (config), "shared-ip", + GUINT_TO_POINTER (tmp_addr), release_shared_ip); + + return config; +} + +static NMActStageReturn +real_act_stage4_get_ip4_config (NMDevice *self, + NMIP4Config **config, + NMDeviceStateReason *reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + NMConnection *connection; + NMSettingIP4Config *s_ip4; + const char *ip_iface; + + g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + /* Use the IP interface (not the control interface) for IP stuff */ + ip_iface = nm_device_get_ip_iface (self); + + connection = nm_act_request_get_connection (nm_device_get_act_request (self)); + g_assert (connection); + + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + + if (priv->dhcp4_client) { + /* DHCP */ + *config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE); + if (*config) { + /* Merge user-defined overrides into the IP4Config to be applied */ + nm_utils_merge_ip4_config (*config, s_ip4); + + nm_dhcp4_config_reset (priv->dhcp4_config); + nm_dhcp_client_foreach_option (priv->dhcp4_client, + dhcp4_add_option_cb, + priv->dhcp4_config); + + /* Notify of new DHCP4 config */ + g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG); + } else + *reason = NM_DEVICE_STATE_REASON_DHCP_ERROR; + } else { + /* Not DHCP */ + const char *method; + + g_assert (s_ip4); + + method = nm_setting_ip4_config_get_method (s_ip4); + g_assert (method); + + if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) { + *config = aipd_get_ip4_config (self, reason); + } else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { + *config = nm_ip4_config_new (); + if (*config) { + /* Merge user-defined overrides into the IP4Config to be applied */ + nm_utils_merge_ip4_config (*config, s_ip4); + } else + *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; + } else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { + *config = nm_device_new_ip4_shared_config (self, reason); + if (*config) + priv->dnsmasq_manager = nm_dnsmasq_manager_new (ip_iface); + } else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) + ret = NM_ACT_STAGE_RETURN_SUCCESS; + } + + if (!*config) { + /* Make sure device is up even if config fails */ + nm_device_bring_up (self, FALSE, NULL); + } else + ret = NM_ACT_STAGE_RETURN_SUCCESS; + + return ret; +} + +/* + * nm_device_activate_stage4_ip4_config_get + * + * Retrieve the correct IPv4 config. + * + */ +static gboolean +nm_device_activate_stage4_ip4_config_get (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMIP4Config *ip4_config = NULL; + NMActStageReturn ret; + const char *iface = NULL; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + + /* Clear the activation source ID now that this stage has run */ + activation_source_clear (self, FALSE, AF_INET); + + iface = nm_device_get_iface (self); + nm_log_info (LOGD_DEVICE | LOGD_IP4, + "Activation (%s) Stage 4 of 5 (IP4 Configure Get) started...", + iface); + + ret = NM_DEVICE_GET_CLASS (self)->act_stage4_get_ip4_config (self, &ip4_config, &reason); + if (ret == NM_ACT_STAGE_RETURN_POSTPONE) + goto out; + else if (!ip4_config || (ret == NM_ACT_STAGE_RETURN_FAILURE)) { + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + goto out; + } + g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); + + g_object_set_data (G_OBJECT (nm_device_get_act_request (self)), + NM_ACT_REQUEST_IP4_CONFIG, ip4_config); + + nm_device_activate_schedule_stage5_ip_config_commit (self, AF_INET); + +out: + nm_log_info (LOGD_DEVICE | LOGD_IP4, + "Activation (%s) Stage 4 of 5 (IP4 Configure Get) complete.", + iface); + return FALSE; +} + + +/* + * nm_device_activate_schedule_stage4_ip4_config_get + * + * Schedule creation of the IPv4 config + * + */ +void +nm_device_activate_schedule_stage4_ip4_config_get (NMDevice *self) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (self)); + + priv = NM_DEVICE_GET_PRIVATE (self); + g_return_if_fail (priv->act_request); + + activation_source_schedule (self, nm_device_activate_stage4_ip4_config_get, AF_INET); + + nm_log_info (LOGD_DEVICE | LOGD_IP4, + "Activation (%s) Stage 4 of 5 (IP4 Configure Get) scheduled...", + nm_device_get_iface (self)); +} + + +static NMActStageReturn +real_act_stage4_ip4_config_timeout (NMDevice *self, + NMIP4Config **config, + NMDeviceStateReason *reason) +{ + g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE); + + /* Notify of invalid DHCP4 config object */ + g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG); + + if (nm_device_ip_config_should_fail (self, FALSE)) { + *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; + return NM_ACT_STAGE_RETURN_FAILURE; + } + + return NM_ACT_STAGE_RETURN_SUCCESS; +} + + +/* + * nm_device_activate_stage4_ip4_config_timeout + * + * Time out on retrieving the IPv4 config. + * + */ +static gboolean +nm_device_activate_stage4_ip4_config_timeout (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMIP4Config *ip4_config = NULL; + const char *iface; + NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + + /* Clear the activation source ID now that this stage has run */ + activation_source_clear (self, FALSE, AF_INET); + + iface = nm_device_get_iface (self); + nm_log_info (LOGD_DEVICE | LOGD_IP4, + "Activation (%s) Stage 4 of 5 (IP4 Configure Timeout) started...", + iface); + + ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip4_config_timeout (self, &ip4_config, &reason); + if (ret == NM_ACT_STAGE_RETURN_POSTPONE) + goto out; + else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + goto out; + } + g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); + + if (ip4_config) { + g_object_set_data (G_OBJECT (nm_device_get_act_request (self)), + NM_ACT_REQUEST_IP4_CONFIG, ip4_config); + } + + nm_device_activate_schedule_stage5_ip_config_commit (self, AF_INET); + +out: + nm_log_info (LOGD_DEVICE | LOGD_IP4, + "Activation (%s) Stage 4 of 5 (IP4 Configure Timeout) complete.", + iface); + return FALSE; +} + + +/* + * nm_device_activate_schedule_stage4_ip4_config_timeout + * + * Deal with a timeout of the IPv4 configuration + * + */ +void +nm_device_activate_schedule_stage4_ip4_config_timeout (NMDevice *self) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (self)); + + priv = NM_DEVICE_GET_PRIVATE (self); + g_return_if_fail (priv->act_request); + + activation_source_schedule (self, nm_device_activate_stage4_ip4_config_timeout, AF_INET); + + nm_log_info (LOGD_DEVICE | LOGD_IP4, + "Activation (%s) Stage 4 of 5 (IP4 Configure Timeout) scheduled...", + nm_device_get_iface (self)); +} + +static void +merge_dhcp_config_to_master (NMIP6Config *dst, NMIP6Config *src) +{ + guint32 i; + + g_return_if_fail (src != NULL); + g_return_if_fail (dst != NULL); + + /* addresses */ + for (i = 0; i < nm_ip6_config_get_num_addresses (src); i++) + nm_ip6_config_add_address (dst, nm_ip6_config_get_address (src, i)); + + /* ptp address; only replace if src doesn't have one */ + if (!nm_ip6_config_get_ptp_address (dst)) + nm_ip6_config_set_ptp_address (dst, nm_ip6_config_get_ptp_address (src)); + + /* nameservers */ + for (i = 0; i < nm_ip6_config_get_num_nameservers (src); i++) + nm_ip6_config_add_nameserver (dst, nm_ip6_config_get_nameserver (src, i)); + + /* routes */ + for (i = 0; i < nm_ip6_config_get_num_routes (src); i++) + nm_ip6_config_add_route (dst, nm_ip6_config_get_route (src, i)); + + /* domains */ + for (i = 0; i < nm_ip6_config_get_num_domains (src); i++) + nm_ip6_config_add_domain (dst, nm_ip6_config_get_domain (src, i)); + + /* dns searches */ + for (i = 0; i < nm_ip6_config_get_num_searches (src); i++) + nm_ip6_config_add_search (dst, nm_ip6_config_get_search (src, i)); + + if (!nm_ip6_config_get_mss (dst)) + nm_ip6_config_set_mss (dst, nm_ip6_config_get_mss (src)); +} + +static NMActStageReturn +real_act_stage4_get_ip6_config (NMDevice *self, + NMIP6Config **config, + NMDeviceStateReason *reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMConnection *connection; + NMSettingIP6Config *s_ip6; + const char *ip_iface; + + g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + /* Use the IP interface (not the control interface) for IP stuff */ + ip_iface = nm_device_get_ip_iface (self); + + connection = nm_act_request_get_connection (nm_device_get_act_request (self)); + g_assert (connection); + + if ( ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_AUTO) + || ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) { + *config = nm_ip6_manager_get_ip6_config (priv->ip6_manager, + nm_device_get_ip_ifindex (self)); + if (!*config) { + *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; + goto out; + } + } else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + *config = nm_ip6_config_new (); + if (!*config) { + *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; + goto out; + } + } + + /* Autoconf might have triggered DHCPv6 too */ + if (priv->dhcp6_client) { + NMIP6Config *dhcp; + + dhcp = nm_dhcp_client_get_ip6_config (priv->dhcp6_client, FALSE); + if (!dhcp) { + *reason = NM_DEVICE_STATE_REASON_DHCP_ERROR; + goto out; + } + + /* For "managed" and DHCP-only setups, we use only the DHCP-supplied + * IPv6 config. But when autoconf is enabled, we have to merge the + * autoconf config and the DHCP-supplied config, then merge the + * user's overrides from the connection to get the final configuration + * that gets applied to the device. + */ + if (*config) { + /* Merge autoconf and DHCP configs */ + merge_dhcp_config_to_master (*config, dhcp); + g_object_unref (dhcp); + dhcp = NULL; + } else { + *config = dhcp; + } + + /* Copy the new DHCPv6 configuration into the DHCP config object that's + * exported over D-Bus to clients. + */ + nm_dhcp6_config_reset (priv->dhcp6_config); + nm_dhcp_client_foreach_option (priv->dhcp6_client, + dhcp6_add_option_cb, + priv->dhcp6_config); + + /* Notify of new DHCP6 config */ + g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP6_CONFIG); + } + + /* Merge user-defined overrides into the IP6Config to be applied */ + if (*config) { + s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); + nm_utils_merge_ip6_config (*config, s_ip6); + } + +out: + return *config ? NM_ACT_STAGE_RETURN_SUCCESS : NM_ACT_STAGE_RETURN_FAILURE; +} + +/* + * nm_device_activate_stage4_ip6_config_get + * + * Retrieve the correct IPv6 config. + * + */ +static gboolean +nm_device_activate_stage4_ip6_config_get (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMIP6Config *ip6_config = NULL; + NMActStageReturn ret; + const char *iface = NULL; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + + /* Clear the activation source ID now that this stage has run */ + activation_source_clear (self, FALSE, AF_INET6); + + iface = nm_device_get_iface (self); + nm_log_info (LOGD_DEVICE | LOGD_IP6, + "Activation (%s) Stage 4 of 5 (IP6 Configure Get) started...", + iface); + + ret = NM_DEVICE_GET_CLASS (self)->act_stage4_get_ip6_config (self, &ip6_config, &reason); + if (ret == NM_ACT_STAGE_RETURN_POSTPONE) + goto out; + else if (ret == NM_ACT_STAGE_RETURN_FAILURE) + { + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + goto out; + } + g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); + + g_object_set_data (G_OBJECT (nm_device_get_act_request (self)), + NM_ACT_REQUEST_IP6_CONFIG, ip6_config); + + nm_device_activate_schedule_stage5_ip_config_commit (self, AF_INET6); + +out: + nm_log_info (LOGD_DEVICE | LOGD_IP6, + "Activation (%s) Stage 4 of 5 (IP6 Configure Get) complete.", + iface); + return FALSE; +} + + +/* + * nm_device_activate_schedule_stage4_ip6_config_get + * + * Schedule creation of the IPv6 config + * + */ +void +nm_device_activate_schedule_stage4_ip6_config_get (NMDevice *self) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (self)); + + priv = NM_DEVICE_GET_PRIVATE (self); + g_return_if_fail (priv->act_request); + + activation_source_schedule (self, nm_device_activate_stage4_ip6_config_get, AF_INET6); + + nm_log_info (LOGD_DEVICE | LOGD_IP6, + "Activation (%s) Stage 4 of 5 (IP6 Configure Get) scheduled...", + nm_device_get_iface (self)); +} + + +static NMActStageReturn +real_act_stage4_ip6_config_timeout (NMDevice *self, + NMIP6Config **config, + NMDeviceStateReason *reason) +{ + g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE); + + /* Notify of invalid DHCP4 config object */ + g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP6_CONFIG); + + if (nm_device_ip_config_should_fail (self, TRUE)) { + *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; + return NM_ACT_STAGE_RETURN_FAILURE; + } + + return NM_ACT_STAGE_RETURN_SUCCESS; +} + + +/* + * nm_device_activate_stage4_ip6_config_timeout + * + * Time out on retrieving the IPv6 config. + * + */ +static gboolean +nm_device_activate_stage4_ip6_config_timeout (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMIP6Config *ip6_config = NULL; + const char *iface; + NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + + /* Clear the activation source ID now that this stage has run */ + activation_source_clear (self, FALSE, AF_INET6); + + iface = nm_device_get_iface (self); + nm_log_info (LOGD_DEVICE | LOGD_IP6, + "Activation (%s) Stage 4 of 5 (IP6 Configure Timeout) started...", + iface); + + ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip6_config_timeout (self, &ip6_config, &reason); + if (ret == NM_ACT_STAGE_RETURN_POSTPONE) + goto out; + else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + goto out; + } + g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); + + if (ip6_config) { + g_object_set_data (G_OBJECT (nm_device_get_act_request (self)), + NM_ACT_REQUEST_IP6_CONFIG, ip6_config); + } + + nm_device_activate_schedule_stage5_ip_config_commit (self, AF_INET6); + +out: + nm_log_info (LOGD_DEVICE | LOGD_IP6, + "Activation (%s) Stage 4 of 5 (IP6 Configure Timeout) complete.", + iface); + return FALSE; +} + + +/* + * nm_device_activate_schedule_stage4_ip6_config_timeout + * + * Deal with a timeout of the IPv6 configuration + * + */ +void +nm_device_activate_schedule_stage4_ip6_config_timeout (NMDevice *self) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (self)); + + priv = NM_DEVICE_GET_PRIVATE (self); + g_return_if_fail (priv->act_request); + + activation_source_schedule (self, nm_device_activate_stage4_ip6_config_timeout, AF_INET6); + + nm_log_info (LOGD_DEVICE | LOGD_IP6, + "Activation (%s) Stage 4 of 5 (IP6 Configure Timeout) scheduled...", + nm_device_get_iface (self)); +} + +static void +share_child_setup (gpointer user_data G_GNUC_UNUSED) +{ + /* We are in the child process at this point */ + pid_t pid = getpid (); + setpgid (pid, pid); +} + +static gboolean +share_init (void) +{ + int status; + char *modules[] = { "ip_tables", "iptable_nat", "nf_nat_ftp", "nf_nat_irc", + "nf_nat_sip", "nf_nat_tftp", "nf_nat_pptp", "nf_nat_h323", + NULL }; + char **iter; + + if (!nm_utils_do_sysctl ("/proc/sys/net/ipv4/ip_forward", "1\n")) { + nm_log_err (LOGD_SHARING, "Error starting IP forwarding: (%d) %s", + errno, strerror (errno)); + return FALSE; + } + + if (!nm_utils_do_sysctl ("/proc/sys/net/ipv4/ip_dynaddr", "1\n")) { + nm_log_err (LOGD_SHARING, "error starting IP forwarding: (%d) %s", + errno, strerror (errno)); + } + + for (iter = modules; *iter; iter++) { + char *argv[3] = { "/sbin/modprobe", *iter, NULL }; + char *envp[1] = { NULL }; + GError *error = NULL; + + if (!g_spawn_sync ("/", argv, envp, G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, + share_child_setup, NULL, NULL, NULL, &status, &error)) { + nm_log_err (LOGD_SHARING, "error loading NAT module %s: (%d) %s", + *iter, error ? error->code : 0, + (error && error->message) ? error->message : "unknown"); + if (error) + g_error_free (error); + } + } + + return TRUE; +} + +static void +add_share_rule (NMActRequest *req, const char *table, const char *fmt, ...) +{ + va_list args; + char *cmd; + + va_start (args, fmt); + cmd = g_strdup_vprintf (fmt, args); + va_end (args); + + nm_act_request_add_share_rule (req, table, cmd); + g_free (cmd); +} + +static gboolean +start_sharing (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMActRequest *req; + GError *error = NULL; + char str_addr[INET_ADDRSTRLEN + 1]; + char str_mask[INET_ADDRSTRLEN + 1]; + guint32 netmask, network; + NMIP4Config *ip4_config; + NMIP4Address *ip4_addr; + const char *ip_iface; + + ip_iface = nm_device_get_ip_iface (self); + + ip4_config = nm_device_get_ip4_config (self); + if (!ip4_config) + return FALSE; + + ip4_addr = nm_ip4_config_get_address (ip4_config, 0); + if (!ip4_addr || !nm_ip4_address_get_address (ip4_addr)) + return FALSE; + + netmask = nm_utils_ip4_prefix_to_netmask (nm_ip4_address_get_prefix (ip4_addr)); + if (!inet_ntop (AF_INET, &netmask, str_mask, sizeof (str_mask))) + return FALSE; + + network = nm_ip4_address_get_address (ip4_addr) & netmask; + if (!inet_ntop (AF_INET, &network, str_addr, sizeof (str_addr))) + return FALSE; + + if (!share_init ()) + return FALSE; + + req = nm_device_get_act_request (self); + g_assert (req); + + add_share_rule (req, "filter", "INPUT --in-interface %s --protocol tcp --destination-port 53 --jump ACCEPT", ip_iface); + add_share_rule (req, "filter", "INPUT --in-interface %s --protocol udp --destination-port 53 --jump ACCEPT", ip_iface); + add_share_rule (req, "filter", "INPUT --in-interface %s --protocol tcp --destination-port 67 --jump ACCEPT", ip_iface); + add_share_rule (req, "filter", "INPUT --in-interface %s --protocol udp --destination-port 67 --jump ACCEPT", ip_iface); + add_share_rule (req, "filter", "FORWARD --in-interface %s --jump REJECT", ip_iface); + add_share_rule (req, "filter", "FORWARD --out-interface %s --jump REJECT", ip_iface); + add_share_rule (req, "filter", "FORWARD --in-interface %s --out-interface %s --jump ACCEPT", ip_iface, ip_iface); + add_share_rule (req, "filter", "FORWARD --source %s/%s --in-interface %s --jump ACCEPT", str_addr, str_mask, ip_iface); + add_share_rule (req, "filter", "FORWARD --destination %s/%s --out-interface %s --match state --state ESTABLISHED,RELATED --jump ACCEPT", str_addr, str_mask, ip_iface); + add_share_rule (req, "nat", "POSTROUTING --source %s/%s --destination ! %s/%s --jump MASQUERADE", str_addr, str_mask, str_addr, str_mask); + + nm_act_request_set_shared (req, TRUE); + + if (!nm_dnsmasq_manager_start (priv->dnsmasq_manager, ip4_config, &error)) { + nm_log_err (LOGD_SHARING, "(%s/%s): failed to start dnsmasq: %s", + nm_device_get_iface (self), ip_iface, error->message); + g_error_free (error); + nm_act_request_set_shared (req, FALSE); + return FALSE; + } + + priv->dnsmasq_state_id = g_signal_connect (priv->dnsmasq_manager, "state-changed", + G_CALLBACK (dnsmasq_state_changed_cb), + self); + return TRUE; +} + +/* + * nm_device_activate_stage5_ip_config_commit + * + * Commit the IP config on the device + * + */ +static gboolean +nm_device_activate_stage5_ip_config_commit (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMActRequest *act_request; + NMIP4Config *ip4_config = NULL; + NMIP6Config *ip6_config = NULL; + const char *iface, *method = NULL; + NMConnection *connection; + NMSettingIP4Config *s_ip4; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + gboolean assumed; + + /* Get the new IP4 and IP6 configs; since this stage gets rerun + * when automatic configuration changes (DHCP lease renewal, new + * IPv6 router advertisement, etc), it's possible that only one of + * them will be set. + */ + act_request = nm_device_get_act_request (self); + + ip4_config = g_object_get_data (G_OBJECT (act_request), + NM_ACT_REQUEST_IP4_CONFIG); + g_object_set_data (G_OBJECT (act_request), + NM_ACT_REQUEST_IP4_CONFIG, NULL); + + ip6_config = g_object_get_data (G_OBJECT (act_request), + NM_ACT_REQUEST_IP6_CONFIG); + g_object_set_data (G_OBJECT (act_request), + NM_ACT_REQUEST_IP6_CONFIG, NULL); + + /* Clear the activation source ID now that this stage has run */ + activation_source_clear (self, FALSE, 0); + + iface = nm_device_get_iface (self); + nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IP Configure Commit) started...", + iface); + + assumed = nm_act_request_get_assumed (priv->act_request); + + if (!ip6_config && !ip4_config) { + nm_log_info (LOGD_DEVICE, + "Activation (%s) Stage 5 of 5 (IP Configure Commit) failed (no IP configuration found)", + iface); + nm_device_state_changed (self, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + goto out; + } + + if (ip4_config && !nm_device_set_ip4_config (self, ip4_config, assumed, &reason)) { + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + goto out; + } + + if (ip6_config && !nm_device_set_ip6_config (self, ip6_config, assumed, &reason)) { + nm_log_info (LOGD_DEVICE | LOGD_IP6, + "Activation (%s) Stage 5 of 5 (IP Configure Commit) IPv6 failed", + iface); + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + goto out; + } + + connection = nm_act_request_get_connection (nm_device_get_act_request (self)); + + if (ip4_config) { + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + if (s_ip4) + method = nm_setting_ip4_config_get_method (s_ip4); + + if (s_ip4 && !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { + if (!start_sharing (self)) { + nm_log_warn (LOGD_SHARING, "Activation (%s) Stage 5 of 5 (IP Configure Commit) start sharing failed.", iface); + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SHARED_START_FAILED); + goto out; + } + } + } + + nm_device_state_changed (self, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_REASON_NONE); + +out: + nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IP Configure Commit) complete.", + iface); + + /* Balance IP config creation; device takes ownership in set_ip*_config() */ + if (ip4_config) + g_object_unref (ip4_config); + if (ip6_config) + g_object_unref (ip6_config); + + return FALSE; +} + + +/* + * nm_device_activate_schedule_stage5_ip_config_commit + * + * Schedule commit of the IP config + */ +static void +nm_device_activate_schedule_stage5_ip_config_commit (NMDevice *self, int family) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (self)); + + priv = NM_DEVICE_GET_PRIVATE (self); + g_return_if_fail (priv->act_request); + + if (family == AF_INET) + priv->ip4_ready = TRUE; + else if (family == AF_INET6) + priv->ip6_ready = TRUE; + + /* Note that these are only set FALSE at stage3, so once you've + * made it all the way through activation once, you can jump back + * into stage4 (eg, for a DHCP lease change) and not worry about + * needing both IPv4 and IPv6 to complete. + */ + if (!priv->ip4_ready || !priv->ip6_ready) + return; + + activation_source_schedule (self, nm_device_activate_stage5_ip_config_commit, 0); + + nm_log_info (LOGD_DEVICE, + "Activation (%s) Stage 5 of 5 (IP Configure Commit) scheduled...", + nm_device_get_iface (self)); +} + + +static void +clear_act_request (NMDevice *self) +{ + NMDevicePrivate * priv; + + g_return_if_fail (self != NULL); + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (!priv->act_request) + return; + + if (priv->secrets_updated_id) { + g_signal_handler_disconnect (priv->act_request, + priv->secrets_updated_id); + priv->secrets_updated_id = 0; + } + + if (priv->secrets_failed_id) { + g_signal_handler_disconnect (priv->act_request, + priv->secrets_failed_id); + priv->secrets_failed_id = 0; + } + + nm_act_request_set_default (priv->act_request, FALSE); + + g_object_unref (priv->act_request); + priv->act_request = NULL; +} + +static void +delayed_transitions_clear (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->failed_to_disconnected_id) { + nm_log_dbg (LOGD_DEVICE, "(%s): clearing failed->disconnected transition", + nm_device_get_iface (self)); + g_source_remove (priv->failed_to_disconnected_id); + priv->failed_to_disconnected_id = 0; + } + if (priv->unavailable_to_disconnected_id) { + nm_log_dbg (LOGD_DEVICE, "(%s): clearing unavailable->disconnected transition", + nm_device_get_iface (self)); + g_source_remove (priv->unavailable_to_disconnected_id); + priv->unavailable_to_disconnected_id = 0; + } +} + +static void +dhcp4_cleanup (NMDevice *self, gboolean stop) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->dhcp4_config) { + g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG); + g_object_unref (priv->dhcp4_config); + priv->dhcp4_config = NULL; + } + + if (priv->dhcp4_client) { + /* Stop any ongoing DHCP transaction on this device */ + if (priv->dhcp4_state_sigid) { + g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_state_sigid); + priv->dhcp4_state_sigid = 0; + } + + if (priv->dhcp4_timeout_sigid) { + g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_timeout_sigid); + priv->dhcp4_timeout_sigid = 0; + } + + if (stop) + nm_dhcp_client_stop (priv->dhcp4_client); + + g_object_unref (priv->dhcp4_client); + priv->dhcp4_client = NULL; + } +} + +static void +dhcp6_cleanup (NMDevice *self, gboolean stop) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + priv->dhcp6_mode = IP6_DHCP_OPT_NONE; + + if (priv->dhcp6_config) { + g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP6_CONFIG); + g_object_unref (priv->dhcp6_config); + priv->dhcp6_config = NULL; + } + + if (priv->dhcp6_client) { + if (priv->dhcp6_state_sigid) { + g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_state_sigid); + priv->dhcp6_state_sigid = 0; + } + + if (priv->dhcp6_timeout_sigid) { + g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_timeout_sigid); + priv->dhcp6_timeout_sigid = 0; + } + + if (stop) + nm_dhcp_client_stop (priv->dhcp6_client); + + g_object_unref (priv->dhcp6_client); + priv->dhcp6_client = NULL; + } +} + +static void +dnsmasq_cleanup (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (!priv->dnsmasq_manager) + return; + + if (priv->dnsmasq_state_id) { + g_signal_handler_disconnect (priv->dnsmasq_manager, priv->dnsmasq_state_id); + priv->dnsmasq_state_id = 0; + } + + nm_dnsmasq_manager_stop (priv->dnsmasq_manager); + g_object_unref (priv->dnsmasq_manager); + priv->dnsmasq_manager = NULL; +} + +/* + * nm_device_deactivate_quickly + * + * Quickly deactivate a device, for things like sleep, etc. Doesn't + * clean much stuff up, and nm_device_deactivate() should be called + * on the device eventually. + * + */ +gboolean +nm_device_deactivate_quickly (NMDevice *self) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + + /* Break the activation chain */ + activation_source_clear (self, TRUE, AF_INET); + activation_source_clear (self, TRUE, AF_INET6); + + /* Clear any delayed transitions */ + delayed_transitions_clear (self); + + dhcp4_cleanup (self, TRUE); + dhcp6_cleanup (self, TRUE); + addrconf6_cleanup (self); + dnsmasq_cleanup (self); + aipd_cleanup (self); + + /* Turn off router advertisements until they are needed */ + if (priv->ip6_accept_ra_path) + nm_utils_do_sysctl (priv->ip6_accept_ra_path, "0\n"); + + /* Call device type-specific deactivation */ + if (NM_DEVICE_GET_CLASS (self)->deactivate_quickly) + NM_DEVICE_GET_CLASS (self)->deactivate_quickly (self); + + /* Tear down an existing activation request */ + clear_act_request (self); + + return TRUE; +} + +/* + * nm_device_deactivate + * + * Remove a device's routing table entries and IP address. + * + */ +static void +nm_device_deactivate (NMDeviceInterface *device, NMDeviceStateReason reason) +{ + NMDevice *self = NM_DEVICE (device); + NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; + + g_return_if_fail (self != NULL); + + nm_log_info (LOGD_DEVICE, "(%s): deactivating device (reason: %d).", + nm_device_get_iface (self), reason); + + nm_device_deactivate_quickly (self); + + /* Take out any entries in the routing table and any IP address the device had. */ + nm_system_device_flush_routes (self, nm_device_get_ip6_config (self) ? AF_UNSPEC : AF_INET); + nm_system_device_flush_addresses (self); + nm_device_update_ip4_address (self); + + /* Clean up nameservers and addresses */ + nm_device_set_ip4_config (self, NULL, FALSE, &ignored); + nm_device_set_ip6_config (self, NULL, FALSE, &ignored); + + /* Call device type-specific deactivation */ + if (NM_DEVICE_GET_CLASS (self)->deactivate) + NM_DEVICE_GET_CLASS (self)->deactivate (self); +} + +static gboolean +device_disconnect (NMDeviceInterface *device, + GError **error) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (NM_DEVICE (device)); + + priv->autoconnect_inhibit = TRUE; + nm_device_state_changed (NM_DEVICE (device), NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_USER_REQUESTED); + return TRUE; +} + +static gboolean +check_connection_compatible (NMDeviceInterface *dev_iface, + NMConnection *connection, + GError **error) +{ + NMDeviceClass *klass = NM_DEVICE_GET_CLASS (NM_DEVICE (dev_iface)); + + if (klass->check_connection_compatible) + return klass->check_connection_compatible (NM_DEVICE (dev_iface), connection, error); + + return TRUE; +} + +static void +connection_secrets_updated_cb (NMActRequest *req, + NMConnection *connection, + GSList *updated_settings, + RequestSecretsCaller caller, + gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + + if (NM_DEVICE_GET_CLASS (self)->connection_secrets_updated) + NM_DEVICE_GET_CLASS (self)->connection_secrets_updated (self, connection, updated_settings, caller); +} + +static void +connection_secrets_failed_cb (NMActRequest *req, + NMConnection *connection, + const char *setting_name, + RequestSecretsCaller caller, + gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NO_SECRETS); +} + +static gboolean +device_activation_precheck (NMDevice *self, NMConnection *connection, GError **error) +{ + NMConnection *current_connection; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + + if (nm_device_get_state (self) != NM_DEVICE_STATE_ACTIVATED) + return TRUE; + + if (!nm_device_is_activating (self)) + return TRUE; + + // FIXME: why not just check connection path & service? + current_connection = nm_act_request_get_connection (nm_device_get_act_request (self)); + if (nm_connection_compare (connection, current_connection, NM_SETTING_COMPARE_FLAG_EXACT)) { + /* Already activating or activated with the same connection */ + g_set_error (error, + NM_DEVICE_INTERFACE_ERROR, + NM_DEVICE_INTERFACE_ERROR_CONNECTION_ACTIVATING, + "%s", "Connection is already activating"); + return FALSE; + } + + return TRUE; +} + +static gboolean +nm_device_activate (NMDeviceInterface *device, + NMActRequest *req, + GError **error) +{ + NMDevice *self = NM_DEVICE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (!device_activation_precheck (self, nm_act_request_get_connection (req), error)) { + g_assert (*error); + return FALSE; + } + + priv->act_request = g_object_ref (req); + priv->secrets_updated_id = g_signal_connect (req, + "connection-secrets-updated", + G_CALLBACK (connection_secrets_updated_cb), + device); + priv->secrets_failed_id = g_signal_connect (req, + "connection-secrets-failed", + G_CALLBACK (connection_secrets_failed_cb), + device); + + if (!nm_act_request_get_assumed (req)) { + /* HACK: update the state a bit early to avoid a race between the + * scheduled stage1 handler and nm_policy_device_change_check() thinking + * that the activation request isn't deferred because the deferred bit + * gets cleared a bit too early, when the connection becomes valid. + */ + nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); + nm_device_activate_schedule_stage1_device_prepare (self); + } else { + /* If it's an assumed connection, let the device subclass short-circuit + * the normal connection process and just copy its IP configs from the + * interface. + */ + nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); + nm_device_activate_schedule_stage3_ip_config_start (self); + } + + return TRUE; +} + +/* + * nm_device_is_activating + * + * Return whether or not the device is currently activating itself. + * + */ +gboolean +nm_device_is_activating (NMDevice *device) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + + g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + + switch (nm_device_get_state (device)) { + case NM_DEVICE_STATE_PREPARE: + case NM_DEVICE_STATE_CONFIG: + case NM_DEVICE_STATE_NEED_AUTH: + case NM_DEVICE_STATE_IP_CONFIG: + return TRUE; + break; + default: + break; + } + + /* There's a small race between the time when stage 1 is scheduled + * and when the device actually sets STATE_PREPARE when the activation + * handler is actually run. If there's an activation handler scheduled + * we're activating anyway. + */ + if (priv->act_source_id) + return TRUE; + + return FALSE; +} + + +gboolean +nm_device_can_interrupt_activation (NMDevice *self) +{ + gboolean interrupt = FALSE; + + g_return_val_if_fail (self != NULL, FALSE); + + if (NM_DEVICE_GET_CLASS (self)->can_interrupt_activation) + interrupt = NM_DEVICE_GET_CLASS (self)->can_interrupt_activation (self); + return interrupt; +} + +/* IP Configuration stuff */ + +NMDHCP4Config * +nm_device_get_dhcp4_config (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + + return NM_DEVICE_GET_PRIVATE (self)->dhcp4_config; +} + +NMIP4Config * +nm_device_get_ip4_config (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + + return NM_DEVICE_GET_PRIVATE (self)->ip4_config; +} + + +static gboolean +nm_device_set_ip4_config (NMDevice *self, + NMIP4Config *new_config, + gboolean assumed, + NMDeviceStateReason *reason) +{ + NMDevicePrivate *priv; + const char *ip_iface; + NMIP4Config *old_config = NULL; + gboolean success = TRUE; + NMIP4ConfigCompareFlags diff = NM_IP4_COMPARE_FLAG_ALL; + NMNamedManager *named_mgr; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + g_return_val_if_fail (reason != NULL, FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + ip_iface = nm_device_get_ip_iface (self); + + old_config = priv->ip4_config; + + if (new_config && old_config) + diff = nm_ip4_config_diff (new_config, old_config); + + /* No actual change, do nothing */ + if (diff == NM_IP4_COMPARE_FLAG_NONE) + return TRUE; + + named_mgr = nm_named_manager_get (); + if (old_config) { + /* Remove any previous IP4 Config from the named manager */ + nm_named_manager_remove_ip4_config (named_mgr, ip_iface, old_config); + g_object_unref (old_config); + priv->ip4_config = NULL; + } + + if (new_config) { + priv->ip4_config = g_object_ref (new_config); + + /* Don't touch the device's actual IP config if the connection is + * assumed when NM starts. + */ + if (!assumed) + success = nm_system_apply_ip4_config (ip_iface, new_config, nm_device_get_priority (self), diff); + + if (success || assumed) { + /* Export over D-Bus */ + if (!nm_ip4_config_get_dbus_path (new_config)) + nm_ip4_config_export (new_config); + + /* Add the DNS information to the named manager */ + nm_named_manager_add_ip4_config (named_mgr, ip_iface, new_config, NM_NAMED_IP_CONFIG_TYPE_DEFAULT); + + nm_device_update_ip4_address (self); + } + } + g_object_unref (named_mgr); + + g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_IP4_CONFIG); + + return success; +} + +/* + * nm_device_get_ip4_address + * + * Get a device's IPv4 address + * + */ +guint32 +nm_device_get_ip4_address (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, 0); + + return NM_DEVICE_GET_PRIVATE (self)->ip4_address; +} + + +void +nm_device_update_ip4_address (NMDevice *self) +{ + struct ifreq req; + guint32 new_address; + int fd; + + g_return_if_fail (self != NULL); + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_err (LOGD_IP4, "couldn't open control socket."); + return; + } + + memset (&req, 0, sizeof (struct ifreq)); + strncpy (req.ifr_name, nm_device_get_ip_iface (self), IFNAMSIZ); + if (ioctl (fd, SIOCGIFADDR, &req) == 0) { + new_address = ((struct sockaddr_in *)(&req.ifr_addr))->sin_addr.s_addr; + if (new_address != nm_device_get_ip4_address (self)) + NM_DEVICE_GET_PRIVATE (self)->ip4_address = new_address; + } + close (fd); +} + +static gboolean +nm_device_set_ip6_config (NMDevice *self, + NMIP6Config *new_config, + gboolean assumed, + NMDeviceStateReason *reason) +{ + NMDevicePrivate *priv; + const char *ip_iface; + NMIP6Config *old_config = NULL; + gboolean success = TRUE; + NMIP6ConfigCompareFlags diff = NM_IP6_COMPARE_FLAG_ALL; + NMNamedManager *named_mgr; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + g_return_val_if_fail (reason != NULL, FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + ip_iface = nm_device_get_ip_iface (self); + + old_config = priv->ip6_config; + + if (new_config && old_config) + diff = nm_ip6_config_diff (new_config, old_config); + + /* No actual change, do nothing */ + if (diff == NM_IP6_COMPARE_FLAG_NONE) + return TRUE; + + named_mgr = nm_named_manager_get (); + if (old_config) { + /* Remove any previous IP6 Config from the named manager */ + nm_named_manager_remove_ip6_config (named_mgr, ip_iface, old_config); + g_object_unref (old_config); + priv->ip6_config = NULL; + } + + if (new_config) { + priv->ip6_config = g_object_ref (new_config); + + /* Don't touch the device's actual IP config if the connection is + * assumed when NM starts. + */ + if (!assumed) + success = nm_system_apply_ip6_config (ip_iface, new_config, nm_device_get_priority (self), diff); + + if (success || assumed) { + /* Export over D-Bus */ + if (!nm_ip6_config_get_dbus_path (new_config)) + nm_ip6_config_export (new_config); + + /* Add the DNS information to the named manager */ + nm_named_manager_add_ip6_config (named_mgr, ip_iface, new_config, NM_NAMED_IP_CONFIG_TYPE_DEFAULT); + } + } + g_object_unref (named_mgr); + + g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_IP6_CONFIG); + + return success; +} + +NMDHCP6Config * +nm_device_get_dhcp6_config (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + + return NM_DEVICE_GET_PRIVATE (self)->dhcp6_config; +} + +NMIP6Config * +nm_device_get_ip6_config (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + + return NM_DEVICE_GET_PRIVATE (self)->ip6_config; +} + +static gboolean +nm_device_is_up (NMDevice *self) +{ + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + if (NM_DEVICE_GET_CLASS (self)->is_up) + return NM_DEVICE_GET_CLASS (self)->is_up (self); + + return TRUE; +} + +gboolean +nm_device_hw_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) +{ + gboolean success; + guint32 tries = 0; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + if (nm_device_hw_is_up (self)) + goto out; + + nm_log_info (LOGD_HW, "(%s): bringing up device.", nm_device_get_iface (self)); + + if (NM_DEVICE_GET_CLASS (self)->hw_bring_up) { + success = NM_DEVICE_GET_CLASS (self)->hw_bring_up (self, no_firmware); + if (!success) + return FALSE; + } + + /* Wait for the device to come up if requested */ + while (block && !nm_device_hw_is_up (self) && (tries++ < 50)) + g_usleep (200); + + if (!nm_device_hw_is_up (self)) { + nm_log_warn (LOGD_HW, "(%s): device not up after timeout!", nm_device_get_iface (self)); + return FALSE; + } + +out: + /* Can only get HW address of some devices when they are up */ + if (NM_DEVICE_GET_CLASS (self)->update_hw_address) + NM_DEVICE_GET_CLASS (self)->update_hw_address (self); + + nm_device_update_ip4_address (self); + return TRUE; +} + +void +nm_device_hw_take_down (NMDevice *self, gboolean block) +{ + guint32 tries = 0; + + g_return_if_fail (NM_IS_DEVICE (self)); + + if (!nm_device_hw_is_up (self)) + return; + + nm_log_info (LOGD_HW, "(%s): taking down device.", nm_device_get_iface (self)); + + if (NM_DEVICE_GET_CLASS (self)->hw_take_down) + NM_DEVICE_GET_CLASS (self)->hw_take_down (self); + + /* Wait for the device to come up if requested */ + while (block && nm_device_hw_is_up (self) && (tries++ < 50)) + g_usleep (200); +} + +static gboolean +nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) +{ + gboolean success = FALSE; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + if (!nm_device_hw_bring_up (self, block, no_firmware)) + return FALSE; + + if (nm_device_is_up (self)) + return TRUE; + + nm_log_info (LOGD_HW, "(%s): preparing device.", nm_device_get_iface (self)); + + if (NM_DEVICE_GET_CLASS (self)->bring_up) + success = NM_DEVICE_GET_CLASS (self)->bring_up (self); + + return success; +} + +static void +nm_device_take_down (NMDevice *self, gboolean block, NMDeviceStateReason reason) +{ + g_return_if_fail (NM_IS_DEVICE (self)); + + if (nm_device_get_act_request (self)) + nm_device_interface_deactivate (NM_DEVICE_INTERFACE (self), reason); + + if (nm_device_is_up (self)) { + nm_log_info (LOGD_HW, "(%s): cleaning up...", nm_device_get_iface (self)); + + if (NM_DEVICE_GET_CLASS (self)->take_down) + NM_DEVICE_GET_CLASS (self)->take_down (self); + } + + nm_device_hw_take_down (self, block); +} + +static void +dispose (GObject *object) +{ + NMDevice *self = NM_DEVICE (object); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean take_down = TRUE; + + if (priv->disposed || !priv->initialized) + goto out; + + priv->disposed = TRUE; + + /* Don't down can-assume-connection capable devices that are activated with + * a connection that can be assumed. + */ + if ( nm_device_interface_can_assume_connection (NM_DEVICE_INTERFACE (self)) + && (nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED)) { + NMConnection *connection; + NMSettingIP4Config *s_ip4; + const char *method = NULL; + + /* Only system connections can be left up */ + connection = nm_act_request_get_connection (priv->act_request); + if ( connection + && (nm_connection_get_scope (connection) == NM_CONNECTION_SCOPE_SYSTEM)) { + + /* Only static or DHCP IPv4 connections can be left up. + * All IPv6 connections can be left up, so we don't have + * to check that. + */ + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + g_assert (s_ip4); + + method = nm_setting_ip4_config_get_method (s_ip4); + if ( !method + || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) + || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) + take_down = FALSE; + } + } + + /* Clear any delayed transitions */ + delayed_transitions_clear (self); + + /* Clean up and stop DHCP */ + dhcp4_cleanup (self, take_down); + dhcp6_cleanup (self, take_down); + addrconf6_cleanup (self); + dnsmasq_cleanup (self); + + /* Take the device itself down and clear its IPv4 configuration */ + if (priv->managed && take_down) { + NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; + + nm_device_take_down (self, FALSE, NM_DEVICE_STATE_REASON_REMOVED); + nm_device_set_ip4_config (self, NULL, FALSE, &ignored); + } + + /* reset the saved RA value */ + if (priv->ip6_accept_ra_path) { + nm_utils_do_sysctl (priv->ip6_accept_ra_path, + priv->ip6_accept_ra_save ? "1\n" : "0\n"); + } + g_free (priv->ip6_accept_ra_path); + + activation_source_clear (self, TRUE, AF_INET); + activation_source_clear (self, TRUE, AF_INET6); + + clear_act_request (self); + +out: + G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMDevice *self = NM_DEVICE (object); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + g_object_unref (priv->dhcp_manager); + + g_free (priv->udi); + g_free (priv->iface); + g_free (priv->ip_iface); + g_free (priv->driver); + g_free (priv->type_desc); + if (priv->dhcp_anycast_address) + g_byte_array_free (priv->dhcp_anycast_address, TRUE); + + G_OBJECT_CLASS (nm_device_parent_class)->finalize (object); +} + + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object); + + switch (prop_id) { + case NM_DEVICE_INTERFACE_PROP_UDI: + /* construct-only */ + priv->udi = g_strdup (g_value_get_string (value)); + break; + case NM_DEVICE_INTERFACE_PROP_IFACE: + g_free (priv->iface); + priv->ifindex = 0; + priv->iface = g_value_dup_string (value); + if (priv->iface) { + priv->ifindex = nm_netlink_iface_to_index (priv->iface); + if (priv->ifindex < 0) { + nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", priv->iface); + } + } + break; + case NM_DEVICE_INTERFACE_PROP_DRIVER: + priv->driver = g_strdup (g_value_get_string (value)); + break; + case NM_DEVICE_INTERFACE_PROP_CAPABILITIES: + priv->capabilities = g_value_get_uint (value); + break; + case NM_DEVICE_INTERFACE_PROP_IP4_ADDRESS: + priv->ip4_address = g_value_get_uint (value); + break; + case NM_DEVICE_INTERFACE_PROP_MANAGED: + priv->managed = g_value_get_boolean (value); + break; + case NM_DEVICE_INTERFACE_PROP_DEVICE_TYPE: + g_return_if_fail (priv->type == NM_DEVICE_TYPE_UNKNOWN); + priv->type = g_value_get_uint (value); + break; + case NM_DEVICE_INTERFACE_PROP_TYPE_DESC: + g_free (priv->type_desc); + priv->type_desc = g_value_dup_string (value); + break; + case NM_DEVICE_INTERFACE_PROP_RFKILL_TYPE: + priv->rfkill_type = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDevice *self = NM_DEVICE (object); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDeviceState state; + + state = nm_device_get_state (self); + + switch (prop_id) { + case NM_DEVICE_INTERFACE_PROP_UDI: + g_value_set_string (value, priv->udi); + break; + case NM_DEVICE_INTERFACE_PROP_IFACE: + g_value_set_string (value, priv->iface); + break; + case NM_DEVICE_INTERFACE_PROP_IFINDEX: + g_value_set_int (value, priv->ifindex); + break; + case NM_DEVICE_INTERFACE_PROP_DRIVER: + g_value_set_string (value, priv->driver); + break; + case NM_DEVICE_INTERFACE_PROP_CAPABILITIES: + g_value_set_uint (value, priv->capabilities); + break; + case NM_DEVICE_INTERFACE_PROP_IP4_ADDRESS: + g_value_set_uint (value, priv->ip4_address); + break; + case NM_DEVICE_INTERFACE_PROP_IP4_CONFIG: + if ((state == NM_DEVICE_STATE_ACTIVATED) || (state == NM_DEVICE_STATE_IP_CONFIG)) { + if (priv->ip4_config) { + g_value_set_boxed (value, nm_ip4_config_get_dbus_path (priv->ip4_config)); + break; + } + } + g_value_set_boxed (value, "/"); + break; + case NM_DEVICE_INTERFACE_PROP_DHCP4_CONFIG: + if ( ((state == NM_DEVICE_STATE_ACTIVATED) || (state == NM_DEVICE_STATE_IP_CONFIG)) + && priv->dhcp4_client) + g_value_set_boxed (value, nm_dhcp4_config_get_dbus_path (priv->dhcp4_config)); + else + g_value_set_boxed (value, "/"); + break; + case NM_DEVICE_INTERFACE_PROP_IP6_CONFIG: + if ((state == NM_DEVICE_STATE_ACTIVATED) || (state == NM_DEVICE_STATE_IP_CONFIG)) { + if (priv->ip6_config) { + g_value_set_boxed (value, nm_ip6_config_get_dbus_path (priv->ip6_config)); + break; + } + } + g_value_set_boxed (value, "/"); + break; + case NM_DEVICE_INTERFACE_PROP_DHCP6_CONFIG: + if ( ((state == NM_DEVICE_STATE_ACTIVATED) || (state == NM_DEVICE_STATE_IP_CONFIG)) + && priv->dhcp6_client) + g_value_set_boxed (value, nm_dhcp6_config_get_dbus_path (priv->dhcp6_config)); + else + g_value_set_boxed (value, "/"); + break; + case NM_DEVICE_INTERFACE_PROP_STATE: + g_value_set_uint (value, priv->state); + break; + case NM_DEVICE_INTERFACE_PROP_DEVICE_TYPE: + g_value_set_uint (value, priv->type); + break; + case NM_DEVICE_INTERFACE_PROP_MANAGED: + g_value_set_boolean (value, priv->managed); + break; + case NM_DEVICE_INTERFACE_PROP_TYPE_DESC: + g_value_set_string (value, priv->type_desc); + break; + case NM_DEVICE_INTERFACE_PROP_RFKILL_TYPE: + g_value_set_uint (value, priv->rfkill_type); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +nm_device_class_init (NMDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMDevicePrivate)); + + /* Virtual methods */ + object_class->dispose = dispose; + object_class->finalize = finalize; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->constructor = constructor; + + klass->get_type_capabilities = real_get_type_capabilities; + klass->get_generic_capabilities = real_get_generic_capabilities; + klass->act_stage1_prepare = real_act_stage1_prepare; + klass->act_stage2_config = real_act_stage2_config; + klass->act_stage3_ip4_config_start = real_act_stage3_ip4_config_start; + klass->act_stage3_ip6_config_start = real_act_stage3_ip6_config_start; + klass->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config; + klass->act_stage4_get_ip6_config = real_act_stage4_get_ip6_config; + klass->act_stage4_ip4_config_timeout = real_act_stage4_ip4_config_timeout; + klass->act_stage4_ip6_config_timeout = real_act_stage4_ip6_config_timeout; + + /* Properties */ + + g_object_class_override_property (object_class, + NM_DEVICE_INTERFACE_PROP_UDI, + NM_DEVICE_INTERFACE_UDI); + + g_object_class_override_property (object_class, + NM_DEVICE_INTERFACE_PROP_IFACE, + NM_DEVICE_INTERFACE_IFACE); + + g_object_class_override_property (object_class, + NM_DEVICE_INTERFACE_PROP_IFINDEX, + NM_DEVICE_INTERFACE_IFINDEX); + + g_object_class_override_property (object_class, + NM_DEVICE_INTERFACE_PROP_DRIVER, + NM_DEVICE_INTERFACE_DRIVER); + + g_object_class_override_property (object_class, + NM_DEVICE_INTERFACE_PROP_CAPABILITIES, + NM_DEVICE_INTERFACE_CAPABILITIES); + + g_object_class_override_property (object_class, + NM_DEVICE_INTERFACE_PROP_IP4_ADDRESS, + NM_DEVICE_INTERFACE_IP4_ADDRESS); + + g_object_class_override_property (object_class, + NM_DEVICE_INTERFACE_PROP_IP4_CONFIG, + NM_DEVICE_INTERFACE_IP4_CONFIG); + + g_object_class_override_property (object_class, + NM_DEVICE_INTERFACE_PROP_DHCP4_CONFIG, + NM_DEVICE_INTERFACE_DHCP4_CONFIG); + + g_object_class_override_property (object_class, + NM_DEVICE_INTERFACE_PROP_IP6_CONFIG, + NM_DEVICE_INTERFACE_IP6_CONFIG); + + g_object_class_override_property (object_class, + NM_DEVICE_INTERFACE_PROP_DHCP6_CONFIG, + NM_DEVICE_INTERFACE_DHCP6_CONFIG); + + g_object_class_override_property (object_class, + NM_DEVICE_INTERFACE_PROP_STATE, + NM_DEVICE_INTERFACE_STATE); + + g_object_class_override_property (object_class, + NM_DEVICE_INTERFACE_PROP_DEVICE_TYPE, + NM_DEVICE_INTERFACE_DEVICE_TYPE); + + g_object_class_override_property (object_class, + NM_DEVICE_INTERFACE_PROP_MANAGED, + NM_DEVICE_INTERFACE_MANAGED); + + g_object_class_override_property (object_class, + NM_DEVICE_INTERFACE_PROP_TYPE_DESC, + NM_DEVICE_INTERFACE_TYPE_DESC); + + g_object_class_override_property (object_class, + NM_DEVICE_INTERFACE_PROP_RFKILL_TYPE, + NM_DEVICE_INTERFACE_RFKILL_TYPE); + + signals[AUTOCONNECT_ALLOWED] = + g_signal_new ("autoconnect-allowed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + autoconnect_allowed_accumulator, NULL, + _nm_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); +} + +static gboolean +failed_to_disconnected (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + nm_log_dbg (LOGD_DEVICE, "(%s): running failed->disconnected transition", + nm_device_get_iface (self)); + priv->failed_to_disconnected_id = 0; + nm_device_state_changed (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE); + return FALSE; +} + +static gboolean +unavailable_to_disconnected (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + nm_log_dbg (LOGD_DEVICE, "(%s): running unavailable->disconnected transition", + nm_device_get_iface (self)); + priv->unavailable_to_disconnected_id = 0; + nm_device_state_changed (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE); + return FALSE; +} + +void +nm_device_state_changed (NMDevice *device, + NMDeviceState state, + NMDeviceStateReason reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDeviceState old_state; + NMActRequest *req; + gboolean no_firmware = FALSE; + + g_return_if_fail (NM_IS_DEVICE (device)); + + if (priv->state == state) + return; + + old_state = priv->state; + priv->state = state; + + nm_log_info (LOGD_DEVICE, "(%s): device state change: %d -> %d (reason %d)", + nm_device_get_iface (device), old_state, state, reason); + + /* Clear any delayed transitions */ + delayed_transitions_clear (device); + + /* Cache the activation request for the dispatcher */ + req = priv->act_request ? g_object_ref (priv->act_request) : NULL; + + /* Handle the new state here; but anything that could trigger + * another state change should be done below. + */ + switch (state) { + case NM_DEVICE_STATE_UNMANAGED: + if (old_state > NM_DEVICE_STATE_UNMANAGED) + nm_device_take_down (device, TRUE, reason); + break; + case NM_DEVICE_STATE_UNAVAILABLE: + if (old_state == NM_DEVICE_STATE_UNMANAGED) { + if (!nm_device_bring_up (device, TRUE, &no_firmware) && no_firmware) { + nm_log_warn (LOGD_HW, "%s: firmware may be missing.", nm_device_get_iface (device)); + } + } + /* Ensure the device gets deactivated in response to stuff like + * carrier changes or rfkill. But don't deactivate devices that are + * about to assume a connection since that defeats the purpose of + * assuming the device's existing connection. + */ + if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) + nm_device_interface_deactivate (NM_DEVICE_INTERFACE (device), reason); + break; + case NM_DEVICE_STATE_DISCONNECTED: + if (old_state != NM_DEVICE_STATE_UNAVAILABLE) + nm_device_interface_deactivate (NM_DEVICE_INTERFACE (device), reason); + break; + default: + priv->autoconnect_inhibit = FALSE; + break; + } + + g_object_notify (G_OBJECT (device), NM_DEVICE_INTERFACE_STATE); + g_signal_emit_by_name (device, "state-changed", state, old_state, reason); + + /* Post-process the event after internal notification */ + + switch (state) { + case NM_DEVICE_STATE_UNAVAILABLE: + /* If the device can activate now (ie, it's got a carrier, the supplicant + * is active, or whatever) schedule a delayed transition to DISCONNECTED + * to get things rolling. The device can't transition immediately becuase + * we can't change states again from the state handler for a variety of + * reasons. + */ + if (nm_device_is_available (device)) { + nm_log_dbg (LOGD_DEVICE, "(%s): device is available, will transition to DISCONNECTED", + nm_device_get_iface (device)); + priv->unavailable_to_disconnected_id = g_idle_add (unavailable_to_disconnected, device); + } else { + nm_log_dbg (LOGD_DEVICE, "(%s): device not yet available for transition to DISCONNECTED", + nm_device_get_iface (device)); + } + break; + case NM_DEVICE_STATE_ACTIVATED: + nm_log_info (LOGD_DEVICE, "Activation (%s) successful, device activated.", + nm_device_get_iface (device)); + nm_utils_call_dispatcher ("up", nm_act_request_get_connection (req), device, NULL); + break; + case NM_DEVICE_STATE_FAILED: + nm_log_warn (LOGD_DEVICE, "Activation (%s) failed.", nm_device_get_iface (device)); + /* Schedule the transition to DISCONNECTED. The device can't transition + * immediately becuase we can't change states again from the state + * handler for a variety of reasons. + */ + priv->failed_to_disconnected_id = g_idle_add (failed_to_disconnected, device); + break; + default: + break; + } + + if (old_state == NM_DEVICE_STATE_ACTIVATED) + nm_utils_call_dispatcher ("down", nm_act_request_get_connection (req), device, NULL); + + /* Dispose of the cached activation request */ + if (req) + g_object_unref (req); +} + +NMDeviceState +nm_device_get_state (NMDevice *device) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), NM_DEVICE_STATE_UNKNOWN); + + return NM_DEVICE_GET_PRIVATE (device)->state; +} + +gboolean +nm_device_get_managed (NMDevice *device) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + + return NM_DEVICE_GET_PRIVATE (device)->managed; +} + +void +nm_device_set_managed (NMDevice *device, + gboolean managed, + NMDeviceStateReason reason) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (device)); + + priv = NM_DEVICE_GET_PRIVATE (device); + if (priv->managed == managed) + return; + + priv->managed = managed; + nm_log_info (LOGD_DEVICE, "(%s): now %s", + nm_device_get_iface (device), + managed ? "managed" : "unmanaged"); + + g_object_notify (G_OBJECT (device), NM_DEVICE_INTERFACE_MANAGED); + + /* If now managed, jump to unavailable */ + if (managed) + nm_device_state_changed (device, NM_DEVICE_STATE_UNAVAILABLE, reason); + else + nm_device_state_changed (device, NM_DEVICE_STATE_UNMANAGED, reason); +} + +static gboolean +spec_match_list (NMDeviceInterface *device, const GSList *specs) +{ + NMDevice *self; + + g_return_val_if_fail (device != NULL, FALSE); + + self = NM_DEVICE (device); + if (NM_DEVICE_GET_CLASS (self)->spec_match_list) + return NM_DEVICE_GET_CLASS (self)->spec_match_list (self, specs); + + return FALSE; +} + +static NMConnection * +connection_match_config (NMDeviceInterface *device, const GSList *connections) +{ + g_return_val_if_fail (device != NULL, FALSE); + + if (NM_DEVICE_GET_CLASS (device)->connection_match_config) + return NM_DEVICE_GET_CLASS (device)->connection_match_config (NM_DEVICE (device), connections); + return NULL; +} + +void +nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout) +{ + g_return_if_fail (NM_IS_DEVICE (device)); + + NM_DEVICE_GET_PRIVATE (device)->dhcp_timeout = timeout; +} + +void +nm_device_set_dhcp_anycast_address (NMDevice *device, guint8 *addr) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (device)); + + priv = NM_DEVICE_GET_PRIVATE (device); + + if (priv->dhcp_anycast_address) { + g_byte_array_free (priv->dhcp_anycast_address, TRUE); + priv->dhcp_anycast_address = NULL; + } + + if (addr) { + priv->dhcp_anycast_address = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (priv->dhcp_anycast_address, addr, ETH_ALEN); + } +} + + +void +nm_device_clear_autoconnect_inhibit (NMDevice *device) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + g_return_if_fail (priv); + priv->autoconnect_inhibit = FALSE; +} + diff --git a/src/nm-device.h b/src/nm-device.h new file mode 100644 index 00000000..202f3924 --- /dev/null +++ b/src/nm-device.h @@ -0,0 +1,188 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef NM_DEVICE_H +#define NM_DEVICE_H + +#include <glib-object.h> +#include <dbus/dbus.h> +#include <netinet/in.h> + +#include "NetworkManager.h" +#include "nm-activation-request.h" +#include "nm-ip4-config.h" +#include "nm-ip6-config.h" +#include "nm-dhcp4-config.h" +#include "nm-dhcp6-config.h" +#include "nm-connection.h" + +typedef enum NMActStageReturn +{ + NM_ACT_STAGE_RETURN_FAILURE = 0, + NM_ACT_STAGE_RETURN_SUCCESS, + NM_ACT_STAGE_RETURN_POSTPONE, + NM_ACT_STAGE_RETURN_STOP /* This activation chain is done */ +} NMActStageReturn; + + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE (nm_device_get_type ()) +#define NM_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE, NMDevice)) +#define NM_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE, NMDeviceClass)) +#define NM_IS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE)) +#define NM_IS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE)) +#define NM_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE, NMDeviceClass)) + +typedef struct { + GObject parent; +} NMDevice; + +typedef struct { + GObjectClass parent; + + /* Hardware state, ie IFF_UP */ + gboolean (*hw_is_up) (NMDevice *self); + gboolean (*hw_bring_up) (NMDevice *self, gboolean *no_firmware); + void (*hw_take_down) (NMDevice *self); + + /* Additional stuff required to operate the device, like a + * connection to the supplicant, Bluez, etc + */ + gboolean (*is_up) (NMDevice *self); + gboolean (*bring_up) (NMDevice *self); + void (*take_down) (NMDevice *self); + + void (* update_hw_address) (NMDevice *self); + + guint32 (* get_type_capabilities) (NMDevice *self); + guint32 (* get_generic_capabilities) (NMDevice *self); + + gboolean (* is_available) (NMDevice *self); + + NMConnection * (* get_best_auto_connection) (NMDevice *self, + GSList *connections, + char **specific_object); + + void (* connection_secrets_updated) (NMDevice *self, + NMConnection *connection, + GSList *updated_settings, + RequestSecretsCaller caller); + + gboolean (* check_connection_compatible) (NMDevice *self, + NMConnection *connection, + GError **error); + + NMActStageReturn (* act_stage1_prepare) (NMDevice *self, + NMDeviceStateReason *reason); + NMActStageReturn (* act_stage2_config) (NMDevice *self, + NMDeviceStateReason *reason); + NMActStageReturn (* act_stage3_ip4_config_start) (NMDevice *self, + NMDeviceStateReason *reason); + NMActStageReturn (* act_stage3_ip6_config_start) (NMDevice *self, + NMDeviceStateReason *reason); + NMActStageReturn (* act_stage4_get_ip4_config) (NMDevice *self, + NMIP4Config **config, + NMDeviceStateReason *reason); + NMActStageReturn (* act_stage4_get_ip6_config) (NMDevice *self, + NMIP6Config **config, + NMDeviceStateReason *reason); + NMActStageReturn (* act_stage4_ip4_config_timeout) (NMDevice *self, + NMIP4Config **config, + NMDeviceStateReason *reason); + NMActStageReturn (* act_stage4_ip6_config_timeout) (NMDevice *self, + NMIP6Config **config, + NMDeviceStateReason *reason); + void (* deactivate) (NMDevice *self); + void (* deactivate_quickly) (NMDevice *self); + + gboolean (* can_interrupt_activation) (NMDevice *self); + + gboolean (* spec_match_list) (NMDevice *self, const GSList *specs); + + NMConnection * (* connection_match_config) (NMDevice *self, const GSList *connections); +} NMDeviceClass; + + +GType nm_device_get_type (void); + +const char * nm_device_get_path (NMDevice *dev); +void nm_device_set_path (NMDevice *dev, const char *path); + +const char * nm_device_get_udi (NMDevice *dev); +const char * nm_device_get_iface (NMDevice *dev); +int nm_device_get_ifindex (NMDevice *dev); +const char * nm_device_get_ip_iface (NMDevice *dev); +int nm_device_get_ip_ifindex(NMDevice *dev); +const char * nm_device_get_driver (NMDevice *dev); +const char * nm_device_get_type_desc (NMDevice *dev); + +NMDeviceType nm_device_get_device_type (NMDevice *dev); +guint32 nm_device_get_capabilities (NMDevice *dev); +guint32 nm_device_get_type_capabilities (NMDevice *dev); + +int nm_device_get_priority (NMDevice *dev); + +guint32 nm_device_get_ip4_address (NMDevice *dev); +void nm_device_update_ip4_address (NMDevice *dev); + +NMDHCP4Config * nm_device_get_dhcp4_config (NMDevice *dev); +NMDHCP6Config * nm_device_get_dhcp6_config (NMDevice *dev); + +NMIP4Config * nm_device_get_ip4_config (NMDevice *dev); +NMIP6Config * nm_device_get_ip6_config (NMDevice *dev); + +void * nm_device_get_system_config_data (NMDevice *dev); + +NMActRequest * nm_device_get_act_request (NMDevice *dev); + +gboolean nm_device_is_available (NMDevice *dev); + +NMConnection * nm_device_get_best_auto_connection (NMDevice *dev, + GSList *connections, + char **specific_object); + +void nm_device_activate_schedule_stage1_device_prepare (NMDevice *device); +void nm_device_activate_schedule_stage2_device_config (NMDevice *device); +void nm_device_activate_schedule_stage4_ip4_config_get (NMDevice *device); +void nm_device_activate_schedule_stage4_ip4_config_timeout (NMDevice *device); +void nm_device_activate_schedule_stage4_ip6_config_get (NMDevice *device); +void nm_device_activate_schedule_stage4_ip6_config_timeout (NMDevice *device); +gboolean nm_device_deactivate_quickly (NMDevice *dev); +gboolean nm_device_is_activating (NMDevice *dev); +gboolean nm_device_can_interrupt_activation (NMDevice *self); +gboolean nm_device_autoconnect_allowed (NMDevice *self); + +NMDeviceState nm_device_get_state (NMDevice *device); + +gboolean nm_device_get_managed (NMDevice *device); +void nm_device_set_managed (NMDevice *device, + gboolean managed, + NMDeviceStateReason reason); + +void nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout); +void nm_device_set_dhcp_anycast_address (NMDevice *device, guint8 *addr); + +void nm_device_clear_autoconnect_inhibit (NMDevice *device); + +G_END_DECLS + +#endif /* NM_DEVICE_H */ diff --git a/src/nm-dhcp4-config.c b/src/nm-dhcp4-config.c new file mode 100644 index 00000000..5acf053f --- /dev/null +++ b/src/nm-dhcp4-config.c @@ -0,0 +1,192 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Red Hat, Inc. + */ + +#include <glib.h> +#include <string.h> + +#include "NetworkManager.h" +#include "nm-dbus-manager.h" +#include "nm-dhcp4-config.h" +#include "nm-dhcp4-config-glue.h" +#include "nm-dbus-glib-types.h" +#include "nm-properties-changed-signal.h" +#include "nm-utils.h" + + +G_DEFINE_TYPE (NMDHCP4Config, nm_dhcp4_config, G_TYPE_OBJECT) + +#define NM_DHCP4_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP4_CONFIG, NMDHCP4ConfigPrivate)) + +typedef struct { + char *dbus_path; + GHashTable *options; +} NMDHCP4ConfigPrivate; + + +enum { + PROP_0, + PROP_OPTIONS, + + LAST_PROP +}; + +enum { + PROPERTIES_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + + +NMDHCP4Config * +nm_dhcp4_config_new (void) +{ + return NM_DHCP4_CONFIG (g_object_new (NM_TYPE_DHCP4_CONFIG, NULL)); +} + +void +nm_dhcp4_config_add_option (NMDHCP4Config *self, + const char *key, + const char *option) +{ + GValue *svalue; + + g_return_if_fail (NM_IS_DHCP4_CONFIG (self)); + g_return_if_fail (key != NULL); + g_return_if_fail (option != NULL); + + svalue = g_slice_new0 (GValue); + g_value_init (svalue, G_TYPE_STRING); + g_value_set_string (svalue, option); + g_hash_table_insert (NM_DHCP4_CONFIG_GET_PRIVATE (self)->options, g_strdup (key), svalue); + g_object_notify (G_OBJECT (self), NM_DHCP4_CONFIG_OPTIONS); +} + +void +nm_dhcp4_config_reset (NMDHCP4Config *self) +{ + g_return_if_fail (NM_IS_DHCP4_CONFIG (self)); + + g_hash_table_remove_all (NM_DHCP4_CONFIG_GET_PRIVATE (self)->options); + g_object_notify (G_OBJECT (self), NM_DHCP4_CONFIG_OPTIONS); +} + +const char * +nm_dhcp4_config_get_option (NMDHCP4Config *self, const char *key) +{ + GValue *value; + + g_return_val_if_fail (NM_IS_DHCP4_CONFIG (self), NULL); + g_return_val_if_fail (key != NULL, NULL); + + value = g_hash_table_lookup (NM_DHCP4_CONFIG_GET_PRIVATE (self)->options, key); + return value ? g_value_get_string (value) : NULL; +} + +const char * +nm_dhcp4_config_get_dbus_path (NMDHCP4Config *self) +{ + g_return_val_if_fail (NM_IS_DHCP4_CONFIG (self), NULL); + + return NM_DHCP4_CONFIG_GET_PRIVATE (self)->dbus_path; +} + +static void +nm_gvalue_destroy (gpointer data) +{ + GValue *value = (GValue *) data; + + g_value_unset (value); + g_slice_free (GValue, value); +} + +static void +nm_dhcp4_config_init (NMDHCP4Config *self) +{ + NMDHCP4ConfigPrivate *priv = NM_DHCP4_CONFIG_GET_PRIVATE (self); + static guint32 counter = 0; + DBusGConnection *connection; + NMDBusManager *dbus_mgr; + + dbus_mgr = nm_dbus_manager_get (); + connection = nm_dbus_manager_get_connection (dbus_mgr); + priv->dbus_path = g_strdup_printf (NM_DBUS_PATH "/DHCP4Config/%d", counter++); + dbus_g_connection_register_g_object (connection, priv->dbus_path, G_OBJECT (self)); + g_object_unref (dbus_mgr); + + priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nm_gvalue_destroy); +} + +static void +finalize (GObject *object) +{ + NMDHCP4ConfigPrivate *priv = NM_DHCP4_CONFIG_GET_PRIVATE (object); + + g_free (priv->dbus_path); + g_hash_table_destroy (priv->options); + + G_OBJECT_CLASS (nm_dhcp4_config_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDHCP4ConfigPrivate *priv = NM_DHCP4_CONFIG_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_OPTIONS: + g_value_set_boxed (value, priv->options); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_dhcp4_config_class_init (NMDHCP4ConfigClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (config_class); + + g_type_class_add_private (config_class, sizeof (NMDHCP4ConfigPrivate)); + + /* virtual methods */ + object_class->get_property = get_property; + object_class->finalize = finalize; + + /* properties */ + g_object_class_install_property + (object_class, PROP_OPTIONS, + g_param_spec_boxed (NM_DHCP4_CONFIG_OPTIONS, + "Options", + "DHCP configuration options returned by the server", + DBUS_TYPE_G_MAP_OF_VARIANT, + G_PARAM_READABLE)); + + /* Signals */ + signals[PROPERTIES_CHANGED] = + nm_properties_changed_signal_new (object_class, + G_STRUCT_OFFSET (NMDHCP4ConfigClass, properties_changed)); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (config_class), + &dbus_glib_nm_dhcp4_config_object_info); +} diff --git a/src/nm-dhcp4-config.h b/src/nm-dhcp4-config.h new file mode 100644 index 00000000..ffaa8430 --- /dev/null +++ b/src/nm-dhcp4-config.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Red Hat, Inc. + */ + +#ifndef NM_DHCP4_CONFIG_H +#define NM_DHCP4_CONFIG_H + +#include <glib.h> +#include <glib-object.h> + +#define NM_TYPE_DHCP4_CONFIG (nm_dhcp4_config_get_type ()) +#define NM_DHCP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP4_CONFIG, NMDHCP4Config)) +#define NM_DHCP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP4_CONFIG, NMDHCP4ConfigClass)) +#define NM_IS_DHCP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP4_CONFIG)) +#define NM_IS_DHCP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP4_CONFIG)) +#define NM_DHCP4_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP4_CONFIG, NMDHCP4ConfigClass)) + +typedef struct { + GObject parent; +} NMDHCP4Config; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*properties_changed) (NMDHCP4Config *config, GHashTable *properties); +} NMDHCP4ConfigClass; + +#define NM_DHCP4_CONFIG_OPTIONS "options" + +GType nm_dhcp4_config_get_type (void); + +NMDHCP4Config *nm_dhcp4_config_new (void); + +const char *nm_dhcp4_config_get_dbus_path (NMDHCP4Config *config); + +void nm_dhcp4_config_add_option (NMDHCP4Config *config, + const char *key, + const char *option); + +void nm_dhcp4_config_reset (NMDHCP4Config *config); + +const char *nm_dhcp4_config_get_option (NMDHCP4Config *config, const char *option); + +#endif /* NM_DHCP4_CONFIG_H */ diff --git a/src/nm-dhcp6-config.c b/src/nm-dhcp6-config.c new file mode 100644 index 00000000..fb6ccce5 --- /dev/null +++ b/src/nm-dhcp6-config.c @@ -0,0 +1,192 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Red Hat, Inc. + */ + +#include <glib.h> +#include <string.h> + +#include "NetworkManager.h" +#include "nm-dbus-manager.h" +#include "nm-dhcp6-config.h" +#include "nm-dhcp6-config-glue.h" +#include "nm-dbus-glib-types.h" +#include "nm-properties-changed-signal.h" +#include "nm-utils.h" + + +G_DEFINE_TYPE (NMDHCP6Config, nm_dhcp6_config, G_TYPE_OBJECT) + +#define NM_DHCP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigPrivate)) + +typedef struct { + char *dbus_path; + GHashTable *options; +} NMDHCP6ConfigPrivate; + + +enum { + PROP_0, + PROP_OPTIONS, + + LAST_PROP +}; + +enum { + PROPERTIES_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + + +NMDHCP6Config * +nm_dhcp6_config_new (void) +{ + return NM_DHCP6_CONFIG (g_object_new (NM_TYPE_DHCP6_CONFIG, NULL)); +} + +void +nm_dhcp6_config_add_option (NMDHCP6Config *self, + const char *key, + const char *option) +{ + GValue *svalue; + + g_return_if_fail (NM_IS_DHCP6_CONFIG (self)); + g_return_if_fail (key != NULL); + g_return_if_fail (option != NULL); + + svalue = g_slice_new0 (GValue); + g_value_init (svalue, G_TYPE_STRING); + g_value_set_string (svalue, option); + g_hash_table_insert (NM_DHCP6_CONFIG_GET_PRIVATE (self)->options, g_strdup (key), svalue); + g_object_notify (G_OBJECT (self), NM_DHCP6_CONFIG_OPTIONS); +} + +void +nm_dhcp6_config_reset (NMDHCP6Config *self) +{ + g_return_if_fail (NM_IS_DHCP6_CONFIG (self)); + + g_hash_table_remove_all (NM_DHCP6_CONFIG_GET_PRIVATE (self)->options); + g_object_notify (G_OBJECT (self), NM_DHCP6_CONFIG_OPTIONS); +} + +const char * +nm_dhcp6_config_get_option (NMDHCP6Config *self, const char *key) +{ + GValue *value; + + g_return_val_if_fail (NM_IS_DHCP6_CONFIG (self), NULL); + g_return_val_if_fail (key != NULL, NULL); + + value = g_hash_table_lookup (NM_DHCP6_CONFIG_GET_PRIVATE (self)->options, key); + return value ? g_value_get_string (value) : NULL; +} + +const char * +nm_dhcp6_config_get_dbus_path (NMDHCP6Config *self) +{ + g_return_val_if_fail (NM_IS_DHCP6_CONFIG (self), NULL); + + return NM_DHCP6_CONFIG_GET_PRIVATE (self)->dbus_path; +} + +static void +nm_gvalue_destroy (gpointer data) +{ + GValue *value = (GValue *) data; + + g_value_unset (value); + g_slice_free (GValue, value); +} + +static void +nm_dhcp6_config_init (NMDHCP6Config *self) +{ + NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (self); + static guint32 counter = 0; + DBusGConnection *connection; + NMDBusManager *dbus_mgr; + + dbus_mgr = nm_dbus_manager_get (); + connection = nm_dbus_manager_get_connection (dbus_mgr); + priv->dbus_path = g_strdup_printf (NM_DBUS_PATH "/DHCP6Config/%d", counter++); + dbus_g_connection_register_g_object (connection, priv->dbus_path, G_OBJECT (self)); + g_object_unref (dbus_mgr); + + priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nm_gvalue_destroy); +} + +static void +finalize (GObject *object) +{ + NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object); + + g_free (priv->dbus_path); + g_hash_table_destroy (priv->options); + + G_OBJECT_CLASS (nm_dhcp6_config_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_OPTIONS: + g_value_set_boxed (value, priv->options); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_dhcp6_config_class_init (NMDHCP6ConfigClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (config_class); + + g_type_class_add_private (config_class, sizeof (NMDHCP6ConfigPrivate)); + + /* virtual methods */ + object_class->get_property = get_property; + object_class->finalize = finalize; + + /* properties */ + g_object_class_install_property + (object_class, PROP_OPTIONS, + g_param_spec_boxed (NM_DHCP6_CONFIG_OPTIONS, + "Options", + "DHCP configuration options returned by the server", + DBUS_TYPE_G_MAP_OF_VARIANT, + G_PARAM_READABLE)); + + /* Signals */ + signals[PROPERTIES_CHANGED] = + nm_properties_changed_signal_new (object_class, + G_STRUCT_OFFSET (NMDHCP6ConfigClass, properties_changed)); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (config_class), + &dbus_glib_nm_dhcp6_config_object_info); +} diff --git a/src/nm-dhcp6-config.h b/src/nm-dhcp6-config.h new file mode 100644 index 00000000..90eb10ff --- /dev/null +++ b/src/nm-dhcp6-config.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Red Hat, Inc. + */ + +#ifndef NM_DHCP6_CONFIG_H +#define NM_DHCP6_CONFIG_H + +#include <glib.h> +#include <glib-object.h> + +#define NM_TYPE_DHCP6_CONFIG (nm_dhcp6_config_get_type ()) +#define NM_DHCP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP6_CONFIG, NMDHCP6Config)) +#define NM_DHCP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigClass)) +#define NM_IS_DHCP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP6_CONFIG)) +#define NM_IS_DHCP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP6_CONFIG)) +#define NM_DHCP6_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigClass)) + +typedef struct { + GObject parent; +} NMDHCP6Config; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*properties_changed) (NMDHCP6Config *config, GHashTable *properties); +} NMDHCP6ConfigClass; + +#define NM_DHCP6_CONFIG_OPTIONS "options" + +GType nm_dhcp6_config_get_type (void); + +NMDHCP6Config *nm_dhcp6_config_new (void); + +const char *nm_dhcp6_config_get_dbus_path (NMDHCP6Config *config); + +void nm_dhcp6_config_add_option (NMDHCP6Config *config, + const char *key, + const char *option); + +void nm_dhcp6_config_reset (NMDHCP6Config *config); + +const char *nm_dhcp6_config_get_option (NMDHCP6Config *config, const char *option); + +#endif /* NM_DHCP6_CONFIG_H */ diff --git a/src/nm-hostname-provider.c b/src/nm-hostname-provider.c new file mode 100644 index 00000000..1cd1ca6c --- /dev/null +++ b/src/nm-hostname-provider.c @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 Novell, Inc. + */ + +#include "nm-hostname-provider.h" + +const char * +nm_hostname_provider_get_hostname (NMHostnameProvider *self) +{ + g_return_val_if_fail (NM_IS_HOSTNAME_PROVIDER (self), NULL); + + return NM_HOSTNAME_PROVIDER_GET_INTERFACE (self)->get_hostname (self); +} + +GType +nm_hostname_provider_get_type (void) +{ + static GType type = 0; + + if (!G_UNLIKELY (type)) { + const GTypeInfo type_info = { + sizeof (NMHostnameProvider), /* class_size */ + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + type = g_type_register_static (G_TYPE_INTERFACE, "NMHostnameProvider", &type_info, 0); + g_type_interface_add_prerequisite (type, G_TYPE_OBJECT); + } + + return type; +} diff --git a/src/nm-hostname-provider.h b/src/nm-hostname-provider.h new file mode 100644 index 00000000..7fe5c006 --- /dev/null +++ b/src/nm-hostname-provider.h @@ -0,0 +1,44 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 Novell, Inc. + */ + +#ifndef NM_HOSTNAME_PROVIDER_H +#define NM_HOSTNAME_PROVIDER_H + +#include <glib-object.h> + +#define NM_TYPE_HOSTNAME_PROVIDER (nm_hostname_provider_get_type ()) +#define NM_HOSTNAME_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_HOSTNAME_PROVIDER, NMHostnameProvider)) +#define NM_IS_HOSTNAME_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_HOSTNAME_PROVIDER)) +#define NM_HOSTNAME_PROVIDER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_HOSTNAME_PROVIDER, NMHostnameProvider)) + +typedef struct _NMHostnameProvider NMHostnameProvider; + +struct _NMHostnameProvider { + GTypeInterface g_iface; + + /* Methods */ + const char *(*get_hostname) (NMHostnameProvider *self); +}; + +GType nm_hostname_provider_get_type (void); + +const char *nm_hostname_provider_get_hostname (NMHostnameProvider *self); + +#endif /* NM_HOSTNAME_PROVIDER_H */ diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c new file mode 100644 index 00000000..0ae3d1be --- /dev/null +++ b/src/nm-ip4-config.c @@ -0,0 +1,909 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include <glib.h> +#include <stdio.h> +#include <string.h> +#include "nm-ip4-config.h" +#include "nm-dbus-manager.h" +#include "NetworkManager.h" +#include "NetworkManagerUtils.h" +#include "nm-setting-ip4-config.h" +#include "nm-utils.h" + +#include <netlink/route/addr.h> +#include <netlink/utils.h> +#include <netinet/in.h> + +#include "nm-ip4-config-glue.h" +#include "nm-dbus-glib-types.h" + + +G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, G_TYPE_OBJECT) + +#define NM_IP4_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IP4_CONFIG, NMIP4ConfigPrivate)) + +typedef struct { + char *path; + + GSList *addresses; + guint32 ptp_address; + + guint32 mtu; /* Maximum Transmission Unit of the interface */ + guint32 mss; /* Maximum Segment Size of the route */ + + GArray *nameservers; + GPtrArray *domains; + GPtrArray *searches; + + GArray *wins; + + GArray *nis; + char * nis_domain; + + GSList *routes; + + gboolean never_default; +} NMIP4ConfigPrivate; + + +enum { + PROP_0, + PROP_ADDRESSES, + PROP_NAMESERVERS, + PROP_DOMAINS, + PROP_ROUTES, + PROP_WINS_SERVERS, + + LAST_PROP +}; + + +static struct nl_addr * +nm_utils_ip4_addr_to_nl_addr (guint32 ip4_addr) +{ + struct nl_addr * nla = NULL; + + if (!(nla = nl_addr_alloc (sizeof (in_addr_t)))) + return NULL; + nl_addr_set_family (nla, AF_INET); + nl_addr_set_binary_addr (nla, &ip4_addr, sizeof (guint32)); + + return nla; +} + + +NMIP4Config * +nm_ip4_config_new (void) +{ + return (NMIP4Config *) g_object_new (NM_TYPE_IP4_CONFIG, NULL); +} + +void +nm_ip4_config_export (NMIP4Config *config) +{ + NMIP4ConfigPrivate *priv; + NMDBusManager *dbus_mgr; + DBusGConnection *connection; + static guint32 counter = 0; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + g_return_if_fail (priv->path == NULL); + + dbus_mgr = nm_dbus_manager_get (); + connection = nm_dbus_manager_get_connection (dbus_mgr); + priv->path = g_strdup_printf (NM_DBUS_PATH "/IP4Config/%d", counter++); + + dbus_g_connection_register_g_object (connection, priv->path, G_OBJECT (config)); + g_object_unref (dbus_mgr); +} + +const char * +nm_ip4_config_get_dbus_path (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), FALSE); + + return NM_IP4_CONFIG_GET_PRIVATE (config)->path; +} + +void +nm_ip4_config_take_address (NMIP4Config *config, NMIP4Address *address) +{ + NMIP4ConfigPrivate *priv; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + g_return_if_fail (address != NULL); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + priv->addresses = g_slist_append (priv->addresses, address); +} + +void +nm_ip4_config_add_address (NMIP4Config *config, + NMIP4Address *address) +{ + NMIP4ConfigPrivate *priv; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + g_return_if_fail (address != NULL); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + priv->addresses = g_slist_append (priv->addresses, nm_ip4_address_dup (address)); +} + +void +nm_ip4_config_replace_address (NMIP4Config *config, + guint i, + NMIP4Address *new_address) +{ + NMIP4ConfigPrivate *priv; + GSList *old; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + old = g_slist_nth (priv->addresses, i); + g_return_if_fail (old != NULL); + nm_ip4_address_unref ((NMIP4Address *) old->data); + + old->data = nm_ip4_address_dup (new_address); +} + +NMIP4Address *nm_ip4_config_get_address (NMIP4Config *config, guint i) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL); + + return (NMIP4Address *) g_slist_nth_data (NM_IP4_CONFIG_GET_PRIVATE (config)->addresses, i); +} + +guint32 nm_ip4_config_get_num_addresses (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0); + + return g_slist_length (NM_IP4_CONFIG_GET_PRIVATE (config)->addresses); +} + +guint32 nm_ip4_config_get_ptp_address (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0); + + return NM_IP4_CONFIG_GET_PRIVATE (config)->ptp_address; +} + +void nm_ip4_config_set_ptp_address (NMIP4Config *config, guint32 ptp_addr) +{ + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + + NM_IP4_CONFIG_GET_PRIVATE (config)->ptp_address = ptp_addr; +} + +void nm_ip4_config_add_nameserver (NMIP4Config *config, guint32 nameserver) +{ + NMIP4ConfigPrivate *priv; + int i; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + g_return_if_fail (nameserver > 0); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + for (i = 0; i < priv->nameservers->len; i++) { + guint32 s = g_array_index (priv->nameservers, guint32, i); + + /* No dupes */ + g_return_if_fail (nameserver != s); + } + + g_array_append_val (priv->nameservers, nameserver); +} + +guint32 nm_ip4_config_get_nameserver (NMIP4Config *config, guint i) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0); + + return g_array_index (NM_IP4_CONFIG_GET_PRIVATE (config)->nameservers, guint32, i); +} + +guint32 nm_ip4_config_get_num_nameservers (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0); + + return NM_IP4_CONFIG_GET_PRIVATE (config)->nameservers->len; +} + +void nm_ip4_config_reset_nameservers (NMIP4Config *config) +{ + NMIP4ConfigPrivate *priv; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + if (priv->nameservers->len) + g_array_remove_range (priv->nameservers, 0, priv->nameservers->len); +} + +void nm_ip4_config_add_wins (NMIP4Config *config, guint32 wins) +{ + NMIP4ConfigPrivate *priv; + int i; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + g_return_if_fail (wins > 0); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + for (i = 0; i < priv->wins->len; i++) { + guint32 s = g_array_index (priv->wins, guint32, i); + + /* No dupes */ + g_return_if_fail (wins != s); + } + + g_array_append_val (priv->wins, wins); +} + +guint32 nm_ip4_config_get_wins (NMIP4Config *config, guint i) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0); + + return g_array_index (NM_IP4_CONFIG_GET_PRIVATE (config)->wins, guint32, i); +} + +guint32 nm_ip4_config_get_num_wins (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0); + + return NM_IP4_CONFIG_GET_PRIVATE (config)->wins->len; +} + +void nm_ip4_config_reset_wins (NMIP4Config *config) +{ + NMIP4ConfigPrivate *priv; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + if (priv->wins->len) + g_array_remove_range (priv->wins, 0, priv->wins->len); +} + +void +nm_ip4_config_take_route (NMIP4Config *config, NMIP4Route *route) +{ + NMIP4ConfigPrivate *priv; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + g_return_if_fail (route != NULL); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + priv->routes = g_slist_append (priv->routes, route); +} + +void +nm_ip4_config_add_route (NMIP4Config *config, NMIP4Route *route) +{ + NMIP4ConfigPrivate *priv; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + g_return_if_fail (route != NULL); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + priv->routes = g_slist_append (priv->routes, nm_ip4_route_dup (route)); +} + +void +nm_ip4_config_replace_route (NMIP4Config *config, + guint i, + NMIP4Route *new_route) +{ + NMIP4ConfigPrivate *priv; + GSList *old; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + old = g_slist_nth (priv->routes, i); + g_return_if_fail (old != NULL); + nm_ip4_route_unref ((NMIP4Route *) old->data); + + old->data = nm_ip4_route_dup (new_route); +} + +NMIP4Route * +nm_ip4_config_get_route (NMIP4Config *config, guint i) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL); + + return (NMIP4Route *) g_slist_nth_data (NM_IP4_CONFIG_GET_PRIVATE (config)->routes, i); +} + +guint32 nm_ip4_config_get_num_routes (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0); + + return g_slist_length (NM_IP4_CONFIG_GET_PRIVATE (config)->routes); +} + +void nm_ip4_config_reset_routes (NMIP4Config *config) +{ + NMIP4ConfigPrivate *priv; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + g_slist_foreach (priv->routes, (GFunc) g_free, NULL); + priv->routes = NULL; +} + +void nm_ip4_config_add_domain (NMIP4Config *config, const char *domain) +{ + NMIP4ConfigPrivate *priv; + int i; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + g_return_if_fail (domain != NULL); + g_return_if_fail (strlen (domain) > 0); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + + for (i = 0; i < priv->domains->len; i++) { + if (!strcmp (g_ptr_array_index (priv->domains, i), domain)) + return; + } + + g_ptr_array_add (priv->domains, g_strdup (domain)); +} + +const char *nm_ip4_config_get_domain (NMIP4Config *config, guint i) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL); + + return (const char *) g_ptr_array_index (NM_IP4_CONFIG_GET_PRIVATE (config)->domains, i); +} + +guint32 nm_ip4_config_get_num_domains (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0); + + return NM_IP4_CONFIG_GET_PRIVATE (config)->domains->len; +} + +void nm_ip4_config_reset_domains (NMIP4Config *config) +{ + NMIP4ConfigPrivate *priv; + int i; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + for (i = 0; i < priv->domains->len; i++) + g_free (g_ptr_array_index (priv->domains, i)); + g_ptr_array_free (priv->domains, TRUE); + priv->domains = g_ptr_array_sized_new (3); +} + +void nm_ip4_config_add_search (NMIP4Config *config, const char *search) +{ + NMIP4ConfigPrivate *priv; + int i; + + g_return_if_fail (config != NULL); + g_return_if_fail (search != NULL); + g_return_if_fail (strlen (search) > 0); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + + for (i = 0; i < priv->searches->len; i++) { + if (!strcmp (g_ptr_array_index (priv->searches, i), search)) + return; + } + + g_ptr_array_add (priv->searches, g_strdup (search)); +} + +const char *nm_ip4_config_get_search (NMIP4Config *config, guint i) +{ + g_return_val_if_fail (config != NULL, NULL); + + return (const char *) g_ptr_array_index (NM_IP4_CONFIG_GET_PRIVATE (config)->searches, i); +} + +guint32 nm_ip4_config_get_num_searches (NMIP4Config *config) +{ + g_return_val_if_fail (config != NULL, 0); + + return NM_IP4_CONFIG_GET_PRIVATE (config)->searches->len; +} + +void nm_ip4_config_reset_searches (NMIP4Config *config) +{ + NMIP4ConfigPrivate *priv; + int i; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + for (i = 0; i < priv->searches->len; i++) + g_free (g_ptr_array_index (priv->searches, i)); + g_ptr_array_free (priv->searches, TRUE); + priv->searches = g_ptr_array_sized_new (3); +} + +guint32 nm_ip4_config_get_mtu (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0); + + return NM_IP4_CONFIG_GET_PRIVATE (config)->mtu; +} + +void nm_ip4_config_set_mtu (NMIP4Config *config, guint32 mtu) +{ + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + + NM_IP4_CONFIG_GET_PRIVATE (config)->mtu = mtu; +} + +guint32 nm_ip4_config_get_mss (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0); + + return NM_IP4_CONFIG_GET_PRIVATE (config)->mss; +} + +void nm_ip4_config_set_mss (NMIP4Config *config, guint32 mss) +{ + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + + NM_IP4_CONFIG_GET_PRIVATE (config)->mss = mss; +} + +gboolean +nm_ip4_config_get_never_default (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), FALSE); + + return NM_IP4_CONFIG_GET_PRIVATE (config)->never_default; +} + +void +nm_ip4_config_set_never_default (NMIP4Config *config, gboolean never_default) +{ + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + + NM_IP4_CONFIG_GET_PRIVATE (config)->never_default = never_default; +} + +void nm_ip4_config_add_nis_server (NMIP4Config *config, guint32 nis) +{ + NMIP4ConfigPrivate *priv; + int i; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + g_return_if_fail (nis > 0); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + for (i = 0; i < priv->nis->len; i++) { + guint32 s = g_array_index (priv->nis, guint32, i); + + /* No dupes */ + g_return_if_fail (nis != s); + } + + g_array_append_val (priv->nis, nis); +} + +guint32 nm_ip4_config_get_nis_server (NMIP4Config *config, guint i) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0); + + return g_array_index (NM_IP4_CONFIG_GET_PRIVATE (config)->nis, guint32, i); +} + +guint32 nm_ip4_config_get_num_nis_servers (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0); + + return NM_IP4_CONFIG_GET_PRIVATE (config)->nis->len; +} + +void nm_ip4_config_reset_nis_servers (NMIP4Config *config) +{ + NMIP4ConfigPrivate *priv; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + if (priv->nis->len) + g_array_remove_range (priv->nis, 0, priv->nis->len); +} + +void +nm_ip4_config_set_nis_domain (NMIP4Config *config, const char *domain) +{ + NMIP4ConfigPrivate *priv; + + g_return_if_fail (NM_IS_IP4_CONFIG (config)); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + g_free (priv->nis_domain); + priv->nis_domain = g_strdup (domain); +} + +const char * +nm_ip4_config_get_nis_domain (NMIP4Config *config) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0); + + return NM_IP4_CONFIG_GET_PRIVATE (config)->nis_domain; +} + +/* libnl convenience/conversion functions */ + +static int ip4_addr_to_rtnl_local (guint32 ip4_address, struct rtnl_addr *addr) +{ + struct nl_addr * local = NULL; + int err = 0; + + g_return_val_if_fail (addr != NULL, -1); + + local = nm_utils_ip4_addr_to_nl_addr (ip4_address); + err = rtnl_addr_set_local (addr, local); + nl_addr_put (local); + + return err; +} + +static int ip4_addr_to_rtnl_peer (guint32 ip4_address, struct rtnl_addr *addr) +{ + struct nl_addr * peer = NULL; + int err = 0; + + g_return_val_if_fail (addr != NULL, -1); + + peer = nm_utils_ip4_addr_to_nl_addr (ip4_address); + err = rtnl_addr_set_peer (addr, peer); + nl_addr_put (peer); + + return err; +} + +static int ip4_addr_to_rtnl_broadcast (guint32 ip4_broadcast, struct rtnl_addr *addr) +{ + struct nl_addr * local = NULL; + int err = 0; + + g_return_val_if_fail (addr != NULL, -1); + + local = nm_utils_ip4_addr_to_nl_addr (ip4_broadcast); + err = rtnl_addr_set_broadcast (addr, local); + nl_addr_put (local); + + return err; +} + + +struct rtnl_addr * +nm_ip4_config_to_rtnl_addr (NMIP4Config *config, guint32 i, guint32 flags) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4Address *config_addr; + struct rtnl_addr *addr; + gboolean success = TRUE; + + g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL); + + config_addr = nm_ip4_config_get_address (config, i); + g_return_val_if_fail (config_addr != NULL, NULL); + + if (!(addr = rtnl_addr_alloc())) + return NULL; + + if (flags & NM_RTNL_ADDR_ADDR) + success = (ip4_addr_to_rtnl_local (nm_ip4_address_get_address (config_addr), addr) >= 0); + + if (flags & NM_RTNL_ADDR_PTP_ADDR) + success = (ip4_addr_to_rtnl_peer (priv->ptp_address, addr) >= 0); + + if (flags & NM_RTNL_ADDR_PREFIX) + rtnl_addr_set_prefixlen (addr, nm_ip4_address_get_prefix (config_addr)); + + if (flags & NM_RTNL_ADDR_BROADCAST) { + guint32 hostmask, network, bcast, netmask; + + netmask = nm_utils_ip4_prefix_to_netmask (nm_ip4_address_get_prefix (config_addr)); + network = ntohl (nm_ip4_address_get_address (config_addr)) & ntohl (netmask); + hostmask = ~ntohl (netmask); + bcast = htonl (network | hostmask); + + success = (ip4_addr_to_rtnl_broadcast (bcast, addr) >= 0); + } + + if (!success) { + rtnl_addr_put (addr); + addr = NULL; + } + + return addr; +} + +static gboolean +addr_slist_compare (GSList *a, GSList *b) +{ + GSList *iter_a, *iter_b; + gboolean found = FALSE; + + for (iter_a = a; iter_a; iter_a = g_slist_next (iter_a)) { + NMIP4Address *addr_a = (NMIP4Address *) iter_a->data; + + for (iter_b = b, found = FALSE; iter_b; iter_b = g_slist_next (iter_b)) { + NMIP4Address *addr_b = (NMIP4Address *) iter_b->data; + + if (nm_ip4_address_compare (addr_a, addr_b)) { + found = TRUE; + break; + } + } + + if (!found) + return FALSE; + } + return TRUE; +} + +static gboolean +route_slist_compare (GSList *a, GSList *b) +{ + GSList *iter_a, *iter_b; + gboolean found = FALSE; + + for (iter_a = a; iter_a; iter_a = g_slist_next (iter_a)) { + NMIP4Route *route_a = (NMIP4Route *) iter_a->data; + + for (iter_b = b, found = FALSE; iter_b; iter_b = g_slist_next (iter_b)) { + NMIP4Route *route_b = (NMIP4Route *) iter_b->data; + + if (nm_ip4_route_compare (route_a, route_b)) { + found = TRUE; + break; + } + } + + if (!found) + return FALSE; + } + return TRUE; +} + +static gboolean +string_array_compare (GPtrArray *a, GPtrArray *b) +{ + int i, j; + gboolean found = FALSE; + + for (i = 0; i < a->len; i++) { + for (j = 0, found = FALSE; j < b->len; j++) { + const char *item_a = g_ptr_array_index (a, i); + const char *item_b = g_ptr_array_index (b, j); + + if ((!item_a && !item_b) || (item_a && item_b && !strcmp (item_a, item_b))) { + found = TRUE; + break; + } + } + + if (!found) + return FALSE; + } + return TRUE; +} + +static gboolean +addr_array_compare (GArray *a, GArray *b) +{ + int i, j; + gboolean found = FALSE; + + for (i = 0; i < a->len; i++) { + for (j = 0, found = FALSE; j < b->len; j++) { + if (g_array_index (a, guint32, i) == g_array_index (b, guint32, j)) { + found = TRUE; + break; + } + } + + if (!found) + return FALSE; + } + return TRUE; +} + +NMIP4ConfigCompareFlags +nm_ip4_config_diff (NMIP4Config *a, NMIP4Config *b) +{ + NMIP4ConfigPrivate *a_priv; + NMIP4ConfigPrivate *b_priv; + NMIP4ConfigCompareFlags flags = NM_IP4_COMPARE_FLAG_NONE; + + if ((a && !b) || (b && !a)) + return 0xFFFFFFFF; + if (!a && !b) + return NM_IP4_COMPARE_FLAG_NONE; + + a_priv = NM_IP4_CONFIG_GET_PRIVATE (a); + b_priv = NM_IP4_CONFIG_GET_PRIVATE (b); + + if ( !addr_slist_compare (a_priv->addresses, b_priv->addresses) + || !addr_slist_compare (b_priv->addresses, a_priv->addresses)) + flags |= NM_IP4_COMPARE_FLAG_ADDRESSES; + + if (a_priv->ptp_address != b_priv->ptp_address) + flags |= NM_IP4_COMPARE_FLAG_PTP_ADDRESS; + + if ( (a_priv->nameservers->len != b_priv->nameservers->len) + || !addr_array_compare (a_priv->nameservers, b_priv->nameservers) + || !addr_array_compare (b_priv->nameservers, a_priv->nameservers)) + flags |= NM_IP4_COMPARE_FLAG_NAMESERVERS; + + if ( (a_priv->wins->len != b_priv->wins->len) + || !addr_array_compare (a_priv->wins, b_priv->wins) + || !addr_array_compare (b_priv->wins, a_priv->wins)) + flags |= NM_IP4_COMPARE_FLAG_WINS_SERVERS; + + if ( (a_priv->nis->len != b_priv->nis->len) + || !addr_array_compare (a_priv->nis, b_priv->nis) + || !addr_array_compare (b_priv->nis, a_priv->nis)) + flags |= NM_IP4_COMPARE_FLAG_NIS_SERVERS; + + if ( (a_priv->nis_domain || b_priv->nis_domain) + && (g_strcmp0 (a_priv->nis_domain, b_priv->nis_domain) != 0)) + flags |= NM_IP4_COMPARE_FLAG_NIS_DOMAIN; + + if ( !route_slist_compare (a_priv->routes, b_priv->routes) + || !route_slist_compare (b_priv->routes, a_priv->routes)) + flags |= NM_IP4_COMPARE_FLAG_ROUTES; + + if ( (a_priv->domains->len != b_priv->domains->len) + || !string_array_compare (a_priv->domains, b_priv->domains) + || !string_array_compare (b_priv->domains, a_priv->domains)) + flags |= NM_IP4_COMPARE_FLAG_DOMAINS; + + if ( (a_priv->searches->len != b_priv->searches->len) + || !string_array_compare (a_priv->searches, b_priv->searches) + || !string_array_compare (b_priv->searches, a_priv->searches)) + flags |= NM_IP4_COMPARE_FLAG_SEARCHES; + + if (a_priv->mtu != b_priv->mtu) + flags |= NM_IP4_COMPARE_FLAG_MTU; + + if (a_priv->mss != b_priv->mss) + flags |= NM_IP4_COMPARE_FLAG_MSS; + + return flags; +} + +static void +nm_ip4_config_init (NMIP4Config *config) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + + priv->nameservers = g_array_new (FALSE, TRUE, sizeof (guint32)); + priv->wins = g_array_new (FALSE, TRUE, sizeof (guint32)); + priv->domains = g_ptr_array_sized_new (3); + priv->searches = g_ptr_array_sized_new (3); + priv->nis = g_array_new (FALSE, TRUE, sizeof (guint32)); +} + +static void +finalize (GObject *object) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (object); + + nm_utils_slist_free (priv->addresses, (GDestroyNotify) nm_ip4_address_unref); + nm_utils_slist_free (priv->routes, (GDestroyNotify) nm_ip4_route_unref); + g_array_free (priv->wins, TRUE); + g_array_free (priv->nameservers, TRUE); + g_ptr_array_free (priv->domains, TRUE); + g_ptr_array_free (priv->searches, TRUE); + g_array_free (priv->nis, TRUE); + g_free (priv->nis_domain); + + G_OBJECT_CLASS (nm_ip4_config_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_ADDRESSES: + nm_utils_ip4_addresses_to_gvalue (priv->addresses, value); + break; + case PROP_NAMESERVERS: + g_value_set_boxed (value, priv->nameservers); + break; + case PROP_DOMAINS: + g_value_set_boxed (value, priv->domains); + break; + case PROP_ROUTES: + nm_utils_ip4_routes_to_gvalue (priv->routes, value); + break; + case PROP_WINS_SERVERS: + g_value_set_boxed (value, priv->wins); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_ip4_config_class_init (NMIP4ConfigClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (config_class); + + g_type_class_add_private (config_class, sizeof (NMIP4ConfigPrivate)); + + /* virtual methods */ + object_class->get_property = get_property; + object_class->finalize = finalize; + + /* properties */ + g_object_class_install_property + (object_class, PROP_ADDRESSES, + g_param_spec_boxed (NM_IP4_CONFIG_ADDRESSES, + "Addresses", + "IP4 addresses", + DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_NAMESERVERS, + g_param_spec_boxed (NM_IP4_CONFIG_NAMESERVERS, + "Nameservers", + "DNS list", + DBUS_TYPE_G_UINT_ARRAY, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_DOMAINS, + g_param_spec_boxed (NM_IP4_CONFIG_DOMAINS, + "Domains", + "Domains", + DBUS_TYPE_G_ARRAY_OF_STRING, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_ROUTES, + g_param_spec_boxed (NM_IP4_CONFIG_ROUTES, + "Routes", + "Routes", + DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_WINS_SERVERS, + g_param_spec_boxed (NM_IP4_CONFIG_WINS_SERVERS, + "WinsServers", + "WINS server list", + DBUS_TYPE_G_UINT_ARRAY, + G_PARAM_READABLE)); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (config_class), + &dbus_glib_nm_ip4_config_object_info); +} diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h new file mode 100644 index 00000000..2d27acff --- /dev/null +++ b/src/nm-ip4-config.h @@ -0,0 +1,142 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Red Hat, Inc. + */ + +#ifndef NM_IP4_CONFIG_H +#define NM_IP4_CONFIG_H + +#include <glib.h> +#include <glib-object.h> + +#include "nm-setting-ip4-config.h" + +#define NM_TYPE_IP4_CONFIG (nm_ip4_config_get_type ()) +#define NM_IP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IP4_CONFIG, NMIP4Config)) +#define NM_IP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IP4_CONFIG, NMIP4ConfigClass)) +#define NM_IS_IP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IP4_CONFIG)) +#define NM_IS_IP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_IP4_CONFIG)) +#define NM_IP4_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IP4_CONFIG, NMIP4ConfigClass)) + +typedef struct { + GObject parent; +} NMIP4Config; + +typedef struct { + GObjectClass parent; +} NMIP4ConfigClass; + +#define NM_IP4_CONFIG_ADDRESSES "addresses" +#define NM_IP4_CONFIG_NAMESERVERS "nameservers" +#define NM_IP4_CONFIG_DOMAINS "domains" +#define NM_IP4_CONFIG_ROUTES "routes" +#define NM_IP4_CONFIG_WINS_SERVERS "wins-servers" + +GType nm_ip4_config_get_type (void); + + +NMIP4Config * nm_ip4_config_new (void); +void nm_ip4_config_export (NMIP4Config *config); +const char * nm_ip4_config_get_dbus_path (NMIP4Config *config); + +void nm_ip4_config_take_address (NMIP4Config *config, NMIP4Address *address); +void nm_ip4_config_add_address (NMIP4Config *config, NMIP4Address *address); +void nm_ip4_config_replace_address (NMIP4Config *config, guint32 i, NMIP4Address *new_address); +NMIP4Address *nm_ip4_config_get_address (NMIP4Config *config, guint32 i); +guint32 nm_ip4_config_get_num_addresses (NMIP4Config *config); + +guint32 nm_ip4_config_get_ptp_address (NMIP4Config *config); +void nm_ip4_config_set_ptp_address (NMIP4Config *config, guint32 ptp_addr); + +void nm_ip4_config_add_nameserver (NMIP4Config *config, guint32 nameserver); +guint32 nm_ip4_config_get_nameserver (NMIP4Config *config, guint i); +guint32 nm_ip4_config_get_num_nameservers (NMIP4Config *config); +void nm_ip4_config_reset_nameservers (NMIP4Config *config); + +void nm_ip4_config_add_wins (NMIP4Config *config, guint32 wins); +guint32 nm_ip4_config_get_wins (NMIP4Config *config, guint i); +guint32 nm_ip4_config_get_num_wins (NMIP4Config *config); +void nm_ip4_config_reset_wins (NMIP4Config *config); + +void nm_ip4_config_take_route (NMIP4Config *config, NMIP4Route *route); +void nm_ip4_config_add_route (NMIP4Config *config, NMIP4Route *route); +void nm_ip4_config_replace_route (NMIP4Config *config, guint32 i, NMIP4Route *new_route); +NMIP4Route * nm_ip4_config_get_route (NMIP4Config *config, guint32 i); +guint32 nm_ip4_config_get_num_routes (NMIP4Config *config); +void nm_ip4_config_reset_routes (NMIP4Config *config); + +void nm_ip4_config_add_domain (NMIP4Config *config, const char *domain); +const char * nm_ip4_config_get_domain (NMIP4Config *config, guint i); +guint32 nm_ip4_config_get_num_domains (NMIP4Config *config); +void nm_ip4_config_reset_domains (NMIP4Config *config); + +void nm_ip4_config_add_search (NMIP4Config *config, const char *search); +const char * nm_ip4_config_get_search (NMIP4Config *config, guint i); +guint32 nm_ip4_config_get_num_searches (NMIP4Config *config); +void nm_ip4_config_reset_searches (NMIP4Config *config); + +guint32 nm_ip4_config_get_mtu (NMIP4Config *config); +void nm_ip4_config_set_mtu (NMIP4Config *config, guint32 mtu); + +guint32 nm_ip4_config_get_mss (NMIP4Config *config); +void nm_ip4_config_set_mss (NMIP4Config *config, guint32 mss); + +gboolean nm_ip4_config_get_never_default (NMIP4Config *config); +void nm_ip4_config_set_never_default (NMIP4Config *config, gboolean never_default); + +void nm_ip4_config_add_nis_server (NMIP4Config *config, guint32 nis); +guint32 nm_ip4_config_get_nis_server (NMIP4Config *config, guint i); +guint32 nm_ip4_config_get_num_nis_servers (NMIP4Config *config); +void nm_ip4_config_reset_nis_servers (NMIP4Config *config); + +void nm_ip4_config_set_nis_domain (NMIP4Config *config, const char *domain); +const char * nm_ip4_config_get_nis_domain (NMIP4Config *config); + + +/* Flags for nm_ip4_config_to_rtnl_addr() */ +#define NM_RTNL_ADDR_NONE 0x0000 +#define NM_RTNL_ADDR_ADDR 0x0001 +#define NM_RTNL_ADDR_PTP_ADDR 0x0002 +#define NM_RTNL_ADDR_PREFIX 0x0004 +#define NM_RTNL_ADDR_BROADCAST 0x0008 + +#define NM_RTNL_ADDR_DEFAULT (NM_RTNL_ADDR_ADDR | NM_RTNL_ADDR_PREFIX | NM_RTNL_ADDR_BROADCAST) +#define NM_RTNL_ADDR_PTP_DEFAULT (NM_RTNL_ADDR_ADDR | NM_RTNL_ADDR_PREFIX | NM_RTNL_ADDR_PTP_ADDR) + +struct rtnl_addr *nm_ip4_config_to_rtnl_addr (NMIP4Config *config, guint32 i, guint32 flags); + +typedef enum { + NM_IP4_COMPARE_FLAG_NONE = 0x00000000, /* match nothing, kinda pointless */ + NM_IP4_COMPARE_FLAG_ADDRESSES = 0x00000001, + NM_IP4_COMPARE_FLAG_PTP_ADDRESS = 0x00000002, + NM_IP4_COMPARE_FLAG_NAMESERVERS = 0x00000004, + NM_IP4_COMPARE_FLAG_ROUTES = 0x00000008, + NM_IP4_COMPARE_FLAG_DOMAINS = 0x00000010, + NM_IP4_COMPARE_FLAG_SEARCHES = 0x00000020, + NM_IP4_COMPARE_FLAG_MTU = 0x00000040, + NM_IP4_COMPARE_FLAG_MSS = 0x00000080, + NM_IP4_COMPARE_FLAG_WINS_SERVERS= 0x00000100, + NM_IP4_COMPARE_FLAG_NIS_SERVERS = 0x00000200, + NM_IP4_COMPARE_FLAG_NIS_DOMAIN = 0x00000400, + NM_IP4_COMPARE_FLAG_ALL = 0xFFFFFFFF /* match everything */ +} NMIP4ConfigCompareFlags; + +/* Returns a bitfield representing how the two IP4 configs differ */ +NMIP4ConfigCompareFlags nm_ip4_config_diff (NMIP4Config *a, NMIP4Config *b); + +#endif /* NM_IP4_CONFIG_H */ diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c new file mode 100644 index 00000000..4c6c5c62 --- /dev/null +++ b/src/nm-ip6-config.c @@ -0,0 +1,745 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include <glib.h> +#include <stdio.h> +#include <string.h> +#include "nm-ip6-config.h" +#include "nm-dbus-manager.h" +#include "NetworkManager.h" +#include "NetworkManagerUtils.h" +#include "nm-setting-ip6-config.h" +#include "nm-utils.h" + +#include <netlink/route/addr.h> +#include <netlink/utils.h> +#include <netinet/in.h> + +#include "nm-ip6-config-glue.h" +#include "nm-dbus-glib-types.h" + + +G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, G_TYPE_OBJECT) + +#define NM_IP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IP6_CONFIG, NMIP6ConfigPrivate)) + +typedef struct { + char *path; + + GSList *addresses; + struct in6_addr ptp_address; + + guint32 mss; /* Maximum Segment Size of the route */ + + GArray *nameservers; + GPtrArray *domains; + GPtrArray *searches; + + GSList *routes; + + gboolean never_default; +} NMIP6ConfigPrivate; + + +enum { + PROP_0, + PROP_ADDRESSES, + PROP_NAMESERVERS, + PROP_DOMAINS, + PROP_ROUTES, + + LAST_PROP +}; + + +static struct nl_addr * +nm_utils_ip6_addr_to_nl_addr (const struct in6_addr *ip6_addr) +{ + struct nl_addr * nla = NULL; + + if (!(nla = nl_addr_alloc (sizeof (struct in6_addr)))) + return NULL; + nl_addr_set_family (nla, AF_INET6); + nl_addr_set_binary_addr (nla, (struct in6_addr *)ip6_addr, sizeof (struct in6_addr)); + + return nla; +} + + +NMIP6Config * +nm_ip6_config_new (void) +{ + return (NMIP6Config *) g_object_new (NM_TYPE_IP6_CONFIG, NULL); +} + +void +nm_ip6_config_export (NMIP6Config *config) +{ + NMIP6ConfigPrivate *priv; + NMDBusManager *dbus_mgr; + DBusGConnection *connection; + static guint32 counter = 0; + + g_return_if_fail (NM_IS_IP6_CONFIG (config)); + + priv = NM_IP6_CONFIG_GET_PRIVATE (config); + g_return_if_fail (priv->path == NULL); + + dbus_mgr = nm_dbus_manager_get (); + connection = nm_dbus_manager_get_connection (dbus_mgr); + priv->path = g_strdup_printf (NM_DBUS_PATH "/IP6Config/%d", counter++); + + dbus_g_connection_register_g_object (connection, priv->path, G_OBJECT (config)); + g_object_unref (dbus_mgr); +} + +const char * +nm_ip6_config_get_dbus_path (NMIP6Config *config) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), FALSE); + + return NM_IP6_CONFIG_GET_PRIVATE (config)->path; +} + +void +nm_ip6_config_take_address (NMIP6Config *config, NMIP6Address *address) +{ + NMIP6ConfigPrivate *priv; + + g_return_if_fail (NM_IS_IP6_CONFIG (config)); + g_return_if_fail (address != NULL); + + priv = NM_IP6_CONFIG_GET_PRIVATE (config); + priv->addresses = g_slist_append (priv->addresses, address); +} + +void +nm_ip6_config_add_address (NMIP6Config *config, + NMIP6Address *address) +{ + NMIP6ConfigPrivate *priv; + + g_return_if_fail (NM_IS_IP6_CONFIG (config)); + g_return_if_fail (address != NULL); + + priv = NM_IP6_CONFIG_GET_PRIVATE (config); + priv->addresses = g_slist_append (priv->addresses, nm_ip6_address_dup (address)); +} + +void +nm_ip6_config_replace_address (NMIP6Config *config, + guint i, + NMIP6Address *new_address) +{ + NMIP6ConfigPrivate *priv; + GSList *old; + + g_return_if_fail (NM_IS_IP6_CONFIG (config)); + + priv = NM_IP6_CONFIG_GET_PRIVATE (config); + old = g_slist_nth (priv->addresses, i); + g_return_if_fail (old != NULL); + nm_ip6_address_unref ((NMIP6Address *) old->data); + + old->data = nm_ip6_address_dup (new_address); +} + +NMIP6Address *nm_ip6_config_get_address (NMIP6Config *config, guint i) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL); + + return (NMIP6Address *) g_slist_nth_data (NM_IP6_CONFIG_GET_PRIVATE (config)->addresses, i); +} + +guint32 nm_ip6_config_get_num_addresses (NMIP6Config *config) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), 0); + + return g_slist_length (NM_IP6_CONFIG_GET_PRIVATE (config)->addresses); +} + +const struct in6_addr *nm_ip6_config_get_ptp_address (NMIP6Config *config) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), 0); + + return &NM_IP6_CONFIG_GET_PRIVATE (config)->ptp_address; +} + +void nm_ip6_config_set_ptp_address (NMIP6Config *config, const struct in6_addr *ptp_addr) +{ + g_return_if_fail (NM_IS_IP6_CONFIG (config)); + + NM_IP6_CONFIG_GET_PRIVATE (config)->ptp_address = *ptp_addr; +} + +void nm_ip6_config_add_nameserver (NMIP6Config *config, const struct in6_addr *nameserver) +{ + NMIP6ConfigPrivate *priv; + struct in6_addr *nameservers; + int i; + + g_return_if_fail (NM_IS_IP6_CONFIG (config)); + g_return_if_fail (nameserver != NULL); + + priv = NM_IP6_CONFIG_GET_PRIVATE (config); + + /* No dupes */ + nameservers = (struct in6_addr *)priv->nameservers->data; + for (i = 0; i < priv->nameservers->len; i++) + g_return_if_fail (IN6_ARE_ADDR_EQUAL (nameserver, &nameservers[i]) == FALSE); + + g_array_append_val (priv->nameservers, *nameserver); +} + +const struct in6_addr *nm_ip6_config_get_nameserver (NMIP6Config *config, guint i) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), 0); + + return &g_array_index (NM_IP6_CONFIG_GET_PRIVATE (config)->nameservers, struct in6_addr, i); +} + +guint32 nm_ip6_config_get_num_nameservers (NMIP6Config *config) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), 0); + + return NM_IP6_CONFIG_GET_PRIVATE (config)->nameservers->len; +} + +void nm_ip6_config_reset_nameservers (NMIP6Config *config) +{ + NMIP6ConfigPrivate *priv; + + g_return_if_fail (NM_IS_IP6_CONFIG (config)); + + priv = NM_IP6_CONFIG_GET_PRIVATE (config); + if (priv->nameservers->len) + g_array_remove_range (priv->nameservers, 0, priv->nameservers->len); +} + +void +nm_ip6_config_take_route (NMIP6Config *config, NMIP6Route *route) +{ + NMIP6ConfigPrivate *priv; + + g_return_if_fail (NM_IS_IP6_CONFIG (config)); + g_return_if_fail (route != NULL); + + priv = NM_IP6_CONFIG_GET_PRIVATE (config); + priv->routes = g_slist_append (priv->routes, route); +} + +void +nm_ip6_config_add_route (NMIP6Config *config, NMIP6Route *route) +{ + NMIP6ConfigPrivate *priv; + + g_return_if_fail (NM_IS_IP6_CONFIG (config)); + g_return_if_fail (route != NULL); + + priv = NM_IP6_CONFIG_GET_PRIVATE (config); + priv->routes = g_slist_append (priv->routes, nm_ip6_route_dup (route)); +} + +void +nm_ip6_config_replace_route (NMIP6Config *config, + guint i, + NMIP6Route *new_route) +{ + NMIP6ConfigPrivate *priv; + GSList *old; + + g_return_if_fail (NM_IS_IP6_CONFIG (config)); + + priv = NM_IP6_CONFIG_GET_PRIVATE (config); + old = g_slist_nth (priv->routes, i); + g_return_if_fail (old != NULL); + nm_ip6_route_unref ((NMIP6Route *) old->data); + + old->data = nm_ip6_route_dup (new_route); +} + +NMIP6Route * +nm_ip6_config_get_route (NMIP6Config *config, guint i) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL); + + return (NMIP6Route *) g_slist_nth_data (NM_IP6_CONFIG_GET_PRIVATE (config)->routes, i); +} + +guint32 nm_ip6_config_get_num_routes (NMIP6Config *config) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), 0); + + return g_slist_length (NM_IP6_CONFIG_GET_PRIVATE (config)->routes); +} + +void nm_ip6_config_reset_routes (NMIP6Config *config) +{ + NMIP6ConfigPrivate *priv; + + g_return_if_fail (NM_IS_IP6_CONFIG (config)); + + priv = NM_IP6_CONFIG_GET_PRIVATE (config); + g_slist_foreach (priv->routes, (GFunc) g_free, NULL); + priv->routes = NULL; +} + +void nm_ip6_config_add_domain (NMIP6Config *config, const char *domain) +{ + NMIP6ConfigPrivate *priv; + int i; + + g_return_if_fail (NM_IS_IP6_CONFIG (config)); + g_return_if_fail (domain != NULL); + g_return_if_fail (strlen (domain) > 0); + + priv = NM_IP6_CONFIG_GET_PRIVATE (config); + + for (i = 0; i < priv->domains->len; i++) { + if (!strcmp (g_ptr_array_index (priv->domains, i), domain)) + return; + } + + g_ptr_array_add (priv->domains, g_strdup (domain)); +} + +const char *nm_ip6_config_get_domain (NMIP6Config *config, guint i) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL); + + return (const char *) g_ptr_array_index (NM_IP6_CONFIG_GET_PRIVATE (config)->domains, i); +} + +guint32 nm_ip6_config_get_num_domains (NMIP6Config *config) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), 0); + + return NM_IP6_CONFIG_GET_PRIVATE (config)->domains->len; +} + +void nm_ip6_config_reset_domains (NMIP6Config *config) +{ + NMIP6ConfigPrivate *priv; + int i; + + g_return_if_fail (NM_IS_IP6_CONFIG (config)); + + priv = NM_IP6_CONFIG_GET_PRIVATE (config); + for (i = 0; i < priv->domains->len; i++) + g_free (g_ptr_array_index (priv->domains, i)); + g_ptr_array_free (priv->domains, TRUE); + priv->domains = g_ptr_array_sized_new (3); +} + +void nm_ip6_config_add_search (NMIP6Config *config, const char *search) +{ + NMIP6ConfigPrivate *priv; + int i; + + g_return_if_fail (config != NULL); + g_return_if_fail (search != NULL); + g_return_if_fail (strlen (search) > 0); + + priv = NM_IP6_CONFIG_GET_PRIVATE (config); + + for (i = 0; i < priv->searches->len; i++) { + if (!strcmp (g_ptr_array_index (priv->searches, i), search)) + return; + } + + g_ptr_array_add (priv->searches, g_strdup (search)); +} + +const char *nm_ip6_config_get_search (NMIP6Config *config, guint i) +{ + g_return_val_if_fail (config != NULL, NULL); + + return (const char *) g_ptr_array_index (NM_IP6_CONFIG_GET_PRIVATE (config)->searches, i); +} + +guint32 nm_ip6_config_get_num_searches (NMIP6Config *config) +{ + g_return_val_if_fail (config != NULL, 0); + + return NM_IP6_CONFIG_GET_PRIVATE (config)->searches->len; +} + +void nm_ip6_config_reset_searches (NMIP6Config *config) +{ + NMIP6ConfigPrivate *priv; + int i; + + g_return_if_fail (NM_IS_IP6_CONFIG (config)); + + priv = NM_IP6_CONFIG_GET_PRIVATE (config); + for (i = 0; i < priv->searches->len; i++) + g_free (g_ptr_array_index (priv->searches, i)); + g_ptr_array_free (priv->searches, TRUE); + priv->searches = g_ptr_array_sized_new (3); +} + +guint32 nm_ip6_config_get_mss (NMIP6Config *config) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), 0); + + return NM_IP6_CONFIG_GET_PRIVATE (config)->mss; +} + +void nm_ip6_config_set_mss (NMIP6Config *config, guint32 mss) +{ + g_return_if_fail (NM_IS_IP6_CONFIG (config)); + + NM_IP6_CONFIG_GET_PRIVATE (config)->mss = mss; +} + +gboolean +nm_ip6_config_get_never_default (NMIP6Config *config) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), FALSE); + + return NM_IP6_CONFIG_GET_PRIVATE (config)->never_default; +} + +void +nm_ip6_config_set_never_default (NMIP6Config *config, gboolean never_default) +{ + g_return_if_fail (NM_IS_IP6_CONFIG (config)); + + NM_IP6_CONFIG_GET_PRIVATE (config)->never_default = never_default; +} + +/* libnl convenience/conversion functions */ + +static int ip6_addr_to_rtnl_local (const struct in6_addr *ip6_address, struct rtnl_addr *addr) +{ + struct nl_addr * local = NULL; + int err = 0; + + g_return_val_if_fail (addr != NULL, -1); + + local = nm_utils_ip6_addr_to_nl_addr (ip6_address); + err = rtnl_addr_set_local (addr, local); + nl_addr_put (local); + + return err; +} + +static int ip6_addr_to_rtnl_peer (const struct in6_addr *ip6_address, struct rtnl_addr *addr) +{ + struct nl_addr * peer = NULL; + int err = 0; + + g_return_val_if_fail (addr != NULL, -1); + + peer = nm_utils_ip6_addr_to_nl_addr (ip6_address); + err = rtnl_addr_set_peer (addr, peer); + nl_addr_put (peer); + + return err; +} + +struct rtnl_addr * +nm_ip6_config_to_rtnl_addr (NMIP6Config *config, guint32 i, guint32 flags) +{ + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6Address *config_addr; + struct rtnl_addr *addr; + gboolean success = TRUE; + + g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL); + + config_addr = nm_ip6_config_get_address (config, i); + g_return_val_if_fail (config_addr != NULL, NULL); + + if (!(addr = rtnl_addr_alloc())) + return NULL; + + if (flags & NM_RTNL_ADDR_ADDR) + success = (ip6_addr_to_rtnl_local (nm_ip6_address_get_address (config_addr), addr) >= 0); + + if (flags & NM_RTNL_ADDR_PTP_ADDR) + success = (ip6_addr_to_rtnl_peer (&priv->ptp_address, addr) >= 0); + + if (flags & NM_RTNL_ADDR_PREFIX) + rtnl_addr_set_prefixlen (addr, nm_ip6_address_get_prefix (config_addr)); + + if (!success) { + rtnl_addr_put (addr); + addr = NULL; + } + + return addr; +} + +static gboolean +addr_slist_compare (GSList *a, GSList *b) +{ + GSList *iter_a, *iter_b; + gboolean found = FALSE; + + for (iter_a = a; iter_a; iter_a = g_slist_next (iter_a)) { + NMIP6Address *addr_a = (NMIP6Address *) iter_a->data; + + for (iter_b = b, found = FALSE; iter_b; iter_b = g_slist_next (iter_b)) { + NMIP6Address *addr_b = (NMIP6Address *) iter_b->data; + + if (nm_ip6_address_compare (addr_a, addr_b)) { + found = TRUE; + break; + } + } + + if (!found) + return FALSE; + } + return TRUE; +} + +static gboolean +route_slist_compare (GSList *a, GSList *b) +{ + GSList *iter_a, *iter_b; + gboolean found = FALSE; + + for (iter_a = a; iter_a; iter_a = g_slist_next (iter_a)) { + NMIP6Route *route_a = (NMIP6Route *) iter_a->data; + + for (iter_b = b, found = FALSE; iter_b; iter_b = g_slist_next (iter_b)) { + NMIP6Route *route_b = (NMIP6Route *) iter_b->data; + + if (nm_ip6_route_compare (route_a, route_b)) { + found = TRUE; + break; + } + } + + if (!found) + return FALSE; + } + return TRUE; +} + +static gboolean +string_array_compare (GPtrArray *a, GPtrArray *b) +{ + int i, j; + gboolean found = FALSE; + + for (i = 0; i < a->len; i++) { + for (j = 0, found = FALSE; j < b->len; j++) { + const char *item_a = g_ptr_array_index (a, i); + const char *item_b = g_ptr_array_index (b, j); + + if ((!item_a && !item_b) || (item_a && item_b && !strcmp (item_a, item_b))) { + found = TRUE; + break; + } + } + + if (!found) + return FALSE; + } + return TRUE; +} + +static gboolean +addr_array_compare (GArray *a, GArray *b) +{ + struct in6_addr *addrs_a, *addrs_b; + int i, j; + gboolean found = FALSE; + + addrs_a = (struct in6_addr *)a->data; + addrs_b = (struct in6_addr *)b->data; + for (i = 0; i < a->len; i++) { + for (j = 0, found = FALSE; j < b->len; j++) { + if (IN6_ARE_ADDR_EQUAL (&addrs_a[i], &addrs_b[j])) { + found = TRUE; + break; + } + } + + if (!found) + return FALSE; + } + return TRUE; +} + +NMIP6ConfigCompareFlags +nm_ip6_config_diff (NMIP6Config *a, NMIP6Config *b) +{ + NMIP6ConfigPrivate *a_priv; + NMIP6ConfigPrivate *b_priv; + NMIP6ConfigCompareFlags flags = NM_IP6_COMPARE_FLAG_NONE; + + if ((a && !b) || (b && !a)) + return 0xFFFFFFFF; + if (!a && !b) + return NM_IP6_COMPARE_FLAG_NONE; + + a_priv = NM_IP6_CONFIG_GET_PRIVATE (a); + b_priv = NM_IP6_CONFIG_GET_PRIVATE (b); + + if ( !addr_slist_compare (a_priv->addresses, b_priv->addresses) + || !addr_slist_compare (b_priv->addresses, a_priv->addresses)) + flags |= NM_IP6_COMPARE_FLAG_ADDRESSES; + + if (memcmp (&a_priv->ptp_address, &b_priv->ptp_address, sizeof (struct in6_addr)) != 0) + flags |= NM_IP6_COMPARE_FLAG_PTP_ADDRESS; + + if ( (a_priv->nameservers->len != b_priv->nameservers->len) + || !addr_array_compare (a_priv->nameservers, b_priv->nameservers) + || !addr_array_compare (b_priv->nameservers, a_priv->nameservers)) + flags |= NM_IP6_COMPARE_FLAG_NAMESERVERS; + + if ( !route_slist_compare (a_priv->routes, b_priv->routes) + || !route_slist_compare (b_priv->routes, a_priv->routes)) + flags |= NM_IP6_COMPARE_FLAG_ROUTES; + + if ( (a_priv->domains->len != b_priv->domains->len) + || !string_array_compare (a_priv->domains, b_priv->domains) + || !string_array_compare (b_priv->domains, a_priv->domains)) + flags |= NM_IP6_COMPARE_FLAG_DOMAINS; + + if ( (a_priv->searches->len != b_priv->searches->len) + || !string_array_compare (a_priv->searches, b_priv->searches) + || !string_array_compare (b_priv->searches, a_priv->searches)) + flags |= NM_IP6_COMPARE_FLAG_SEARCHES; + + if (a_priv->mss != b_priv->mss) + flags |= NM_IP6_COMPARE_FLAG_MSS; + + return flags; +} + +static void +nm_ip6_config_init (NMIP6Config *config) +{ + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + + priv->nameservers = g_array_new (FALSE, TRUE, sizeof (struct in6_addr)); + priv->domains = g_ptr_array_sized_new (3); + priv->searches = g_ptr_array_sized_new (3); +} + +static void +finalize (GObject *object) +{ + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (object); + + nm_utils_slist_free (priv->addresses, (GDestroyNotify) nm_ip6_address_unref); + nm_utils_slist_free (priv->routes, (GDestroyNotify) nm_ip6_route_unref); + g_array_free (priv->nameservers, TRUE); + g_ptr_array_free (priv->domains, TRUE); + g_ptr_array_free (priv->searches, TRUE); + + G_OBJECT_CLASS (nm_ip6_config_parent_class)->finalize (object); +} + +static void +nameservers_to_gvalue (GArray *array, GValue *value) +{ + GPtrArray *dns; + guint i = 0; + + dns = g_ptr_array_new (); + + while (array && (i < array->len)) { + struct in6_addr *addr; + GByteArray *bytearray; + addr = &g_array_index (array, struct in6_addr, i++); + + bytearray = g_byte_array_sized_new (16); + g_byte_array_append (bytearray, (guint8 *) addr->s6_addr, 16); + g_ptr_array_add (dns, bytearray); + } + + g_value_take_boxed (value, dns); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_ADDRESSES: + nm_utils_ip6_addresses_to_gvalue (priv->addresses, value); + break; + case PROP_NAMESERVERS: + nameservers_to_gvalue (priv->nameservers, value); + break; + case PROP_DOMAINS: + g_value_set_boxed (value, priv->domains); + break; + case PROP_ROUTES: + nm_utils_ip6_routes_to_gvalue (priv->routes, value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_ip6_config_class_init (NMIP6ConfigClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (config_class); + + g_type_class_add_private (config_class, sizeof (NMIP6ConfigPrivate)); + + /* virtual methods */ + object_class->get_property = get_property; + object_class->finalize = finalize; + + /* properties */ + g_object_class_install_property (object_class, PROP_ADDRESSES, + g_param_spec_boxed (NM_IP6_CONFIG_ADDRESSES, + "Addresses", + "IP6 addresses", + DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_NAMESERVERS, + g_param_spec_boxed (NM_IP6_CONFIG_NAMESERVERS, + "Nameservers", + "DNS list", + DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_DOMAINS, + g_param_spec_boxed (NM_IP6_CONFIG_DOMAINS, + "Domains", + "Domains", + DBUS_TYPE_G_ARRAY_OF_STRING, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_ROUTES, + g_param_spec_boxed (NM_IP6_CONFIG_ROUTES, + "Routes", + "Routes", + DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE, + G_PARAM_READABLE)); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (config_class), + &dbus_glib_nm_ip6_config_object_info); +} diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h new file mode 100644 index 00000000..1e75a121 --- /dev/null +++ b/src/nm-ip6-config.h @@ -0,0 +1,120 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Red Hat, Inc. + */ + +#ifndef NM_IP6_CONFIG_H +#define NM_IP6_CONFIG_H + +#include <glib.h> +#include <glib-object.h> + +#include "nm-setting-ip6-config.h" + +#define NM_TYPE_IP6_CONFIG (nm_ip6_config_get_type ()) +#define NM_IP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IP6_CONFIG, NMIP6Config)) +#define NM_IP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IP6_CONFIG, NMIP6ConfigClass)) +#define NM_IS_IP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IP6_CONFIG)) +#define NM_IS_IP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_IP6_CONFIG)) +#define NM_IP6_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IP6_CONFIG, NMIP6ConfigClass)) + +typedef struct { + GObject parent; +} NMIP6Config; + +typedef struct { + GObjectClass parent; +} NMIP6ConfigClass; + +#define NM_IP6_CONFIG_ADDRESSES "addresses" +#define NM_IP6_CONFIG_NAMESERVERS "nameservers" +#define NM_IP6_CONFIG_DOMAINS "domains" +#define NM_IP6_CONFIG_ROUTES "routes" + +GType nm_ip6_config_get_type (void); + + +NMIP6Config * nm_ip6_config_new (void); +void nm_ip6_config_export (NMIP6Config *config); +const char * nm_ip6_config_get_dbus_path (NMIP6Config *config); + +void nm_ip6_config_take_address (NMIP6Config *config, NMIP6Address *address); +void nm_ip6_config_add_address (NMIP6Config *config, NMIP6Address *address); +void nm_ip6_config_replace_address (NMIP6Config *config, guint32 i, NMIP6Address *new_address); +NMIP6Address *nm_ip6_config_get_address (NMIP6Config *config, guint32 i); +guint32 nm_ip6_config_get_num_addresses (NMIP6Config *config); + +const struct in6_addr *nm_ip6_config_get_ptp_address (NMIP6Config *config); +void nm_ip6_config_set_ptp_address (NMIP6Config *config, const struct in6_addr *ptp_addr); + +void nm_ip6_config_add_nameserver (NMIP6Config *config, const struct in6_addr *nameserver); +const struct in6_addr *nm_ip6_config_get_nameserver (NMIP6Config *config, guint i); +guint32 nm_ip6_config_get_num_nameservers (NMIP6Config *config); +void nm_ip6_config_reset_nameservers (NMIP6Config *config); + +void nm_ip6_config_take_route (NMIP6Config *config, NMIP6Route *route); +void nm_ip6_config_add_route (NMIP6Config *config, NMIP6Route *route); +void nm_ip6_config_replace_route (NMIP6Config *config, guint32 i, NMIP6Route *new_route); +NMIP6Route * nm_ip6_config_get_route (NMIP6Config *config, guint32 i); +guint32 nm_ip6_config_get_num_routes (NMIP6Config *config); +void nm_ip6_config_reset_routes (NMIP6Config *config); + +void nm_ip6_config_add_domain (NMIP6Config *config, const char *domain); +const char * nm_ip6_config_get_domain (NMIP6Config *config, guint i); +guint32 nm_ip6_config_get_num_domains (NMIP6Config *config); +void nm_ip6_config_reset_domains (NMIP6Config *config); + +void nm_ip6_config_add_search (NMIP6Config *config, const char *search); +const char * nm_ip6_config_get_search (NMIP6Config *config, guint i); +guint32 nm_ip6_config_get_num_searches (NMIP6Config *config); +void nm_ip6_config_reset_searches (NMIP6Config *config); + +guint32 nm_ip6_config_get_mss (NMIP6Config *config); +void nm_ip6_config_set_mss (NMIP6Config *config, guint32 mss); + +gboolean nm_ip6_config_get_never_default (NMIP6Config *config); +void nm_ip6_config_set_never_default (NMIP6Config *config, gboolean never_default); + +/* Flags for nm_ip6_config_to_rtnl_addr() */ +#define NM_RTNL_ADDR_NONE 0x0000 +#define NM_RTNL_ADDR_ADDR 0x0001 +#define NM_RTNL_ADDR_PTP_ADDR 0x0002 +#define NM_RTNL_ADDR_PREFIX 0x0004 +#define NM_RTNL_ADDR_BROADCAST 0x0008 + +#define NM_RTNL_ADDR_DEFAULT (NM_RTNL_ADDR_ADDR | NM_RTNL_ADDR_PREFIX | NM_RTNL_ADDR_BROADCAST) +#define NM_RTNL_ADDR_PTP_DEFAULT (NM_RTNL_ADDR_ADDR | NM_RTNL_ADDR_PREFIX | NM_RTNL_ADDR_PTP_ADDR) + +struct rtnl_addr *nm_ip6_config_to_rtnl_addr (NMIP6Config *config, guint32 i, guint32 flags); + +typedef enum { + NM_IP6_COMPARE_FLAG_NONE = 0x00000000, /* match nothing, kinda pointless */ + NM_IP6_COMPARE_FLAG_ADDRESSES = 0x00000001, + NM_IP6_COMPARE_FLAG_PTP_ADDRESS = 0x00000002, + NM_IP6_COMPARE_FLAG_NAMESERVERS = 0x00000004, + NM_IP6_COMPARE_FLAG_ROUTES = 0x00000008, + NM_IP6_COMPARE_FLAG_DOMAINS = 0x00000010, + NM_IP6_COMPARE_FLAG_SEARCHES = 0x00000020, + NM_IP6_COMPARE_FLAG_MSS = 0x00000080, + NM_IP6_COMPARE_FLAG_ALL = 0xFFFFFFFF /* match everything */ +} NMIP6ConfigCompareFlags; + +/* Returns a bitfield representing how the two IP6 configs differ */ +NMIP6ConfigCompareFlags nm_ip6_config_diff (NMIP6Config *a, NMIP6Config *b); + +#endif /* NM_IP6_CONFIG_H */ diff --git a/src/nm-manager.c b/src/nm-manager.c new file mode 100644 index 00000000..7d46285b --- /dev/null +++ b/src/nm-manager.c @@ -0,0 +1,3463 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2007 - 2009 Novell, Inc. + * Copyright (C) 2007 - 2010 Red Hat, Inc. + */ + +#include <netinet/ether.h> +#include <string.h> +#include <dbus/dbus-glib-lowlevel.h> +#include <dbus/dbus-glib.h> + +#include "nm-glib-compat.h" +#include "nm-manager.h" +#include "nm-logging.h" +#include "nm-dbus-manager.h" +#include "nm-vpn-manager.h" +#include "nm-modem-manager.h" +#include "nm-device-bt.h" +#include "nm-device-interface.h" +#include "nm-device-private.h" +#include "nm-device-ethernet.h" +#include "nm-device-wifi.h" +#include "nm-device-olpc-mesh.h" +#include "nm-device-cdma.h" +#include "nm-device-gsm.h" +#include "nm-system.h" +#include "nm-properties-changed-signal.h" +#include "nm-setting-bluetooth.h" +#include "nm-setting-connection.h" +#include "nm-setting-wireless.h" +#include "nm-setting-vpn.h" +#include "nm-marshal.h" +#include "nm-dbus-glib-types.h" +#include "nm-udev-manager.h" +#include "nm-hostname-provider.h" +#include "nm-bluez-manager.h" +#include "nm-bluez-common.h" +#include "nm-sysconfig-settings.h" +#include "nm-secrets-provider-interface.h" +#include "nm-settings-interface.h" +#include "nm-settings-system-interface.h" + +#define NM_AUTOIP_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd" +#define NM_AUTOIP_DBUS_IFACE "org.freedesktop.nm_avahi_autoipd" + +static gboolean impl_manager_get_devices (NMManager *manager, GPtrArray **devices, GError **err); +static void impl_manager_activate_connection (NMManager *manager, + const char *service_name, + const char *connection_path, + const char *device_path, + const char *specific_object_path, + DBusGMethodInvocation *context); + +static gboolean impl_manager_deactivate_connection (NMManager *manager, + const char *connection_path, + GError **error); + +static gboolean impl_manager_sleep (NMManager *manager, gboolean sleep, GError **err); + +static gboolean impl_manager_enable (NMManager *manager, gboolean enable, GError **err); + +static gboolean impl_manager_set_logging (NMManager *manager, + const char *level, + const char *domains, + GError **error); + +/* Legacy 0.6 compatibility interface */ + +static gboolean impl_manager_legacy_sleep (NMManager *manager, GError **err); +static gboolean impl_manager_legacy_wake (NMManager *manager, GError **err); +static gboolean impl_manager_legacy_state (NMManager *manager, guint32 *state, GError **err); + +#include "nm-manager-glue.h" + +static void user_destroy_connections (NMManager *manager); + +static void connection_added_default_handler (NMManager *manager, + NMConnection *connection, + NMConnectionScope scope); + +static void udev_device_added_cb (NMUdevManager *udev_mgr, + GUdevDevice *device, + NMDeviceCreatorFn creator_fn, + gpointer user_data); + +static void udev_device_removed_cb (NMUdevManager *udev_mgr, + GUdevDevice *device, + gpointer user_data); + +static void bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr, + const char *bdaddr, + const char *name, + const char *object_path, + guint32 uuids, + NMManager *manager); + +static void bluez_manager_bdaddr_removed_cb (NMBluezManager *bluez_mgr, + const char *bdaddr, + const char *object_path, + gpointer user_data); + +static void bluez_manager_resync_devices (NMManager *self); + +static void add_device (NMManager *self, NMDevice *device); + +static void hostname_provider_init (NMHostnameProvider *provider_class); + +static const char *internal_activate_device (NMManager *manager, + NMDevice *device, + NMConnection *connection, + const char *specific_object, + gboolean user_requested, + gboolean assumed, + GError **error); + +static NMDevice *find_device_by_iface (NMManager *self, const gchar *iface); + +static GSList * remove_one_device (NMManager *manager, + GSList *list, + NMDevice *device, + gboolean quitting, + gboolean force_unmanage); + +static NMDevice *nm_manager_get_device_by_udi (NMManager *manager, const char *udi); + +#define SSD_POKE_INTERVAL 120 +#define ORIGDEV_TAG "originating-device" + +typedef struct { + DBusGMethodInvocation *context; + NMConnectionScope scope; + char *connection_path; + char *specific_object_path; + char *device_path; + guint timeout_id; +} PendingConnectionInfo; + +typedef struct { + gboolean enabled; + gboolean hw_enabled; + RfKillType rtype; + const char *desc; + const char *key; + const char *prop; + const char *hw_prop; + RfKillState (*other_enabled_func) (NMManager *); +} RadioState; + +typedef struct { + char *config_file; + char *state_file; + + GSList *devices; + NMState state; + + NMDBusManager *dbus_mgr; + NMUdevManager *udev_mgr; + NMBluezManager *bluez_mgr; + + GHashTable *user_connections; + DBusGProxy *user_proxy; + + GHashTable *system_connections; + NMSysconfigSettings *sys_settings; + char *hostname; + + GSList *secrets_calls; + + PendingConnectionInfo *pending_connection_info; + + RadioState radio_states[RFKILL_TYPE_MAX]; + gboolean sleeping; + gboolean net_enabled; + + NMVPNManager *vpn_manager; + guint vpn_manager_id; + + NMModemManager *modem_manager; + guint modem_added_id; + guint modem_removed_id; + + DBusGProxy *aipd_proxy; + + gboolean disposed; +} NMManagerPrivate; + +#define NM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MANAGER, NMManagerPrivate)) + +G_DEFINE_TYPE_EXTENDED (NMManager, nm_manager, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (NM_TYPE_HOSTNAME_PROVIDER, + hostname_provider_init)) + +enum { + DEVICE_ADDED, + DEVICE_REMOVED, + STATE_CHANGED, + STATE_CHANGE, /* DEPRECATED */ + PROPERTIES_CHANGED, + CONNECTIONS_ADDED, + CONNECTION_ADDED, + CONNECTION_UPDATED, + CONNECTION_REMOVED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum { + PROP_0, + PROP_STATE, + PROP_NETWORKING_ENABLED, + PROP_WIRELESS_ENABLED, + PROP_WIRELESS_HARDWARE_ENABLED, + PROP_WWAN_ENABLED, + PROP_WWAN_HARDWARE_ENABLED, + PROP_ACTIVE_CONNECTIONS, + + /* Not exported */ + PROP_HOSTNAME, + PROP_SLEEPING, + + LAST_PROP +}; + +typedef enum +{ + NM_MANAGER_ERROR_UNKNOWN_CONNECTION = 0, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + NM_MANAGER_ERROR_UNMANAGED_DEVICE, + NM_MANAGER_ERROR_INVALID_SERVICE, + NM_MANAGER_ERROR_SYSTEM_CONNECTION, + NM_MANAGER_ERROR_PERMISSION_DENIED, + NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, + NM_MANAGER_ERROR_ALREADY_ASLEEP_OR_AWAKE, + NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED, +} NMManagerError; + +#define NM_MANAGER_ERROR (nm_manager_error_quark ()) +#define NM_TYPE_MANAGER_ERROR (nm_manager_error_get_type ()) + +static GQuark +nm_manager_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm-manager-error"); + return quark; +} + +/* This should really be standard. */ +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +static GType +nm_manager_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + /* Connection was not provided by any known settings service. */ + ENUM_ENTRY (NM_MANAGER_ERROR_UNKNOWN_CONNECTION, "UnknownConnection"), + /* Unknown device. */ + ENUM_ENTRY (NM_MANAGER_ERROR_UNKNOWN_DEVICE, "UnknownDevice"), + /* Unmanaged device. */ + ENUM_ENTRY (NM_MANAGER_ERROR_UNMANAGED_DEVICE, "UnmanagedDevice"), + /* Invalid settings service (not a recognized system or user + * settings service name) + */ + ENUM_ENTRY (NM_MANAGER_ERROR_INVALID_SERVICE, "InvalidService"), + /* Connection was superceded by a system connection. */ + ENUM_ENTRY (NM_MANAGER_ERROR_SYSTEM_CONNECTION, "SystemConnection"), + /* User does not have the permission to activate this connection. */ + ENUM_ENTRY (NM_MANAGER_ERROR_PERMISSION_DENIED, "PermissionDenied"), + /* The connection was not active. */ + ENUM_ENTRY (NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, "ConnectionNotActive"), + /* The manager is already in the requested sleep state */ + ENUM_ENTRY (NM_MANAGER_ERROR_ALREADY_ASLEEP_OR_AWAKE, "AlreadyAsleepOrAwake"), + /* The manager is already in the requested enabled/disabled state */ + ENUM_ENTRY (NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED, "AlreadyEnabledOrDisabled"), + { 0, 0, 0 }, + }; + etype = g_enum_register_static ("NMManagerError", values); + } + return etype; +} + +static gboolean +manager_sleeping (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + + if (priv->sleeping || !priv->net_enabled) + return TRUE; + return FALSE; +} + +static void +vpn_manager_connection_deactivated_cb (NMVPNManager *manager, + NMVPNConnection *vpn, + NMVPNConnectionState state, + NMVPNConnectionStateReason reason, + gpointer user_data) +{ + g_object_notify (G_OBJECT (user_data), NM_MANAGER_ACTIVE_CONNECTIONS); +} + +static void +modem_added (NMModemManager *modem_manager, + NMModem *modem, + const char *driver, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *replace_device, *device = NULL; + const char *ip_iface; + GSList *iter; + + ip_iface = nm_modem_get_iface (modem); + + replace_device = find_device_by_iface (NM_MANAGER (user_data), ip_iface); + if (replace_device) { + priv->devices = remove_one_device (NM_MANAGER (user_data), + priv->devices, + replace_device, + FALSE, + TRUE); + } + + /* Give Bluetooth DUN devices first chance to claim the modem */ + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + if (NM_IS_DEVICE_BT (iter->data)) { + if (nm_device_bt_modem_added (NM_DEVICE_BT (iter->data), modem, driver)) + return; + } + } + + /* If it was a Bluetooth modem and no bluetooth device claimed it, ignore + * it. The rfcomm port (and thus the modem) gets created automatically + * by the Bluetooth code during the connection process. + */ + if (driver && !strcmp (driver, "bluetooth")) { + nm_log_info (LOGD_MB, "ignoring modem '%s' (no associated Bluetooth device)", ip_iface); + return; + } + + /* Otherwise make a new top-level NMDevice for it */ + if (NM_IS_MODEM_GSM (modem)) + device = nm_device_gsm_new (NM_MODEM_GSM (modem), driver); + else if (NM_IS_MODEM_CDMA (modem)) + device = nm_device_cdma_new (NM_MODEM_CDMA (modem), driver); + else + nm_log_info (LOGD_MB, "unhandled modem '%s'", ip_iface); + + if (device) + add_device (self, device); +} + +static void +nm_manager_update_state (NMManager *manager) +{ + NMManagerPrivate *priv; + NMState new_state = NM_STATE_DISCONNECTED; + + g_return_if_fail (NM_IS_MANAGER (manager)); + + priv = NM_MANAGER_GET_PRIVATE (manager); + + if (manager_sleeping (manager)) + new_state = NM_STATE_ASLEEP; + else { + GSList *iter; + + for (iter = priv->devices; iter; iter = iter->next) { + NMDevice *dev = NM_DEVICE (iter->data); + + if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED) { + new_state = NM_STATE_CONNECTED; + break; + } else if (nm_device_is_activating (dev)) { + new_state = NM_STATE_CONNECTING; + } + } + } + + if (priv->state != new_state) { + priv->state = new_state; + g_object_notify (G_OBJECT (manager), NM_MANAGER_STATE); + + g_signal_emit (manager, signals[STATE_CHANGED], 0, priv->state); + + /* Emit StateChange too for backwards compatibility */ + g_signal_emit (manager, signals[STATE_CHANGE], 0, priv->state); + } +} + +static void +manager_device_state_changed (NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason, + gpointer user_data) +{ + NMManager *manager = NM_MANAGER (user_data); + + switch (new_state) { + case NM_DEVICE_STATE_UNMANAGED: + case NM_DEVICE_STATE_UNAVAILABLE: + case NM_DEVICE_STATE_DISCONNECTED: + case NM_DEVICE_STATE_PREPARE: + case NM_DEVICE_STATE_FAILED: + g_object_notify (G_OBJECT (manager), NM_MANAGER_ACTIVE_CONNECTIONS); + break; + default: + break; + } + + nm_manager_update_state (manager); +} + +/* Removes a device from a device list; returns the start of the new device list */ +static GSList * +remove_one_device (NMManager *manager, + GSList *list, + NMDevice *device, + gboolean quitting, + gboolean force_unmanage) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + + if (nm_device_get_managed (device)) { + gboolean unmanage = !quitting; + + /* Don't unmanage active assume-connection-capable devices at shutdown */ + if ( nm_device_interface_can_assume_connection (NM_DEVICE_INTERFACE (device)) + && nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) + unmanage = FALSE; + + if (unmanage || force_unmanage) + nm_device_set_managed (device, FALSE, NM_DEVICE_STATE_REASON_REMOVED); + } + + g_signal_handlers_disconnect_by_func (device, manager_device_state_changed, manager); + + nm_sysconfig_settings_device_removed (priv->sys_settings, device); + g_signal_emit (manager, signals[DEVICE_REMOVED], 0, device); + g_object_unref (device); + + return g_slist_remove (list, device); +} + +static void +modem_removed (NMModemManager *modem_manager, + NMModem *modem, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *found; + GSList *iter; + + /* Give Bluetooth DUN devices first chance to handle the modem removal */ + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + if (NM_IS_DEVICE_BT (iter->data)) { + if (nm_device_bt_modem_removed (NM_DEVICE_BT (iter->data), modem)) + return; + } + } + + /* Otherwise remove the standalone modem */ + found = nm_manager_get_device_by_udi (self, nm_modem_get_path (modem)); + if (found) + priv->devices = remove_one_device (self, priv->devices, found, FALSE, TRUE); +} + +static void +aipd_handle_event (DBusGProxy *proxy, + const char *event, + const char *iface, + const char *address, + gpointer user_data) +{ + NMManager *manager = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + GSList *iter; + gboolean handled = FALSE; + + if (!event || !iface) { + nm_log_warn (LOGD_AUTOIP4, "incomplete message received from avahi-autoipd"); + return; + } + + if ( (strcmp (event, "BIND") != 0) + && (strcmp (event, "CONFLICT") != 0) + && (strcmp (event, "UNBIND") != 0) + && (strcmp (event, "STOP") != 0)) { + nm_log_warn (LOGD_AUTOIP4, "unknown event '%s' received from avahi-autoipd", event); + return; + } + + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + NMDevice *candidate = NM_DEVICE (iter->data); + + if (!strcmp (nm_device_get_iface (candidate), iface)) { + nm_device_handle_autoip4_event (candidate, event, address); + handled = TRUE; + break; + } + } + + if (!handled) + nm_log_warn (LOGD_AUTOIP4, "(%s): unhandled avahi-autoipd event", iface); +} + +static const char * +hostname_provider_get_hostname (NMHostnameProvider *provider) +{ + return NM_MANAGER_GET_PRIVATE (provider)->hostname; +} + +static void +hostname_provider_init (NMHostnameProvider *provider_class) +{ + provider_class->get_hostname = hostname_provider_get_hostname; +} + +NMState +nm_manager_get_state (NMManager *manager) +{ + g_return_val_if_fail (NM_IS_MANAGER (manager), NM_STATE_UNKNOWN); + + return NM_MANAGER_GET_PRIVATE (manager)->state; +} + +static void +emit_removed (gpointer key, gpointer value, gpointer user_data) +{ + NMManager *manager = NM_MANAGER (user_data); + NMConnection *connection = NM_CONNECTION (value); + + g_signal_emit (manager, signals[CONNECTION_REMOVED], 0, + connection, + nm_connection_get_scope (connection)); +} + +static void +pending_connection_info_destroy (PendingConnectionInfo *info) +{ + if (!info) + return; + + if (info->timeout_id) + g_source_remove (info->timeout_id); + + g_free (info->connection_path); + g_free (info->specific_object_path); + g_free (info->device_path); + + g_slice_free (PendingConnectionInfo, info); +} + +static GPtrArray * +get_active_connections (NMManager *manager, NMConnection *filter) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + NMVPNManager *vpn_manager; + GPtrArray *active; + GSList *iter; + + active = g_ptr_array_sized_new (3); + + /* Add active device connections */ + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + NMActRequest *req; + const char *path; + + req = nm_device_get_act_request (NM_DEVICE (iter->data)); + if (!req) + continue; + + if (!filter || (nm_act_request_get_connection (req) == filter)) { + path = nm_act_request_get_active_connection_path (req); + g_ptr_array_add (active, g_strdup (path)); + } + } + + /* Add active VPN connections */ + vpn_manager = nm_vpn_manager_get (); + nm_vpn_manager_add_active_connections (vpn_manager, filter, active); + g_object_unref (vpn_manager); + + return active; +} + +static void +remove_connection (NMManager *manager, + NMConnection *connection, + GHashTable *hash) +{ + /* Destroys the connection, then associated DBusGProxy due to the + * weak reference notify function placed on the connection when it + * was created. + */ + g_object_ref (connection); + g_hash_table_remove (hash, nm_connection_get_path (connection)); + g_signal_emit (manager, signals[CONNECTION_REMOVED], 0, + connection, + nm_connection_get_scope (connection)); + g_object_unref (connection); + + bluez_manager_resync_devices (manager); +} + +/*******************************************************************/ +/* User settings stuff via D-Bus */ +/*******************************************************************/ + +static void +user_destroy_connections (NMManager *manager) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + + if (priv->user_connections) { + g_hash_table_foreach (priv->user_connections, emit_removed, manager); + g_hash_table_remove_all (priv->user_connections); + } + + if (priv->user_proxy) { + g_object_unref (priv->user_proxy); + priv->user_proxy = NULL; + } +} + +typedef struct GetSettingsInfo { + NMManager *manager; + NMConnection *connection; + DBusGProxy *proxy; + DBusGProxyCall *call; + GSList **calls; +} GetSettingsInfo; + +static void +free_get_settings_info (gpointer data) +{ + GetSettingsInfo *info = (GetSettingsInfo *) data; + + /* If this was the last pending call for a batch of GetSettings calls, + * send out the connections-added signal. + */ + if (info->calls) { + *(info->calls) = g_slist_remove (*(info->calls), info->call); + if (g_slist_length (*(info->calls)) == 0) { + g_slist_free (*(info->calls)); + g_slice_free (GSList, (gpointer) info->calls); + g_signal_emit (info->manager, signals[CONNECTIONS_ADDED], 0, NM_CONNECTION_SCOPE_USER); + + /* Update the Bluetooth connections for all the new connections */ + bluez_manager_resync_devices (info->manager); + } + } + + if (info->manager) { + g_object_unref (info->manager); + info->manager = NULL; + } + if (info->connection) { + g_object_unref (info->connection); + info->connection = NULL; + } + if (info->proxy) { + g_object_unref (info->proxy); + info->proxy = NULL; + } + + g_slice_free (GetSettingsInfo, data); +} + +static void +user_connection_get_settings_cb (DBusGProxy *proxy, + DBusGProxyCall *call_id, + gpointer user_data) +{ + GetSettingsInfo *info = (GetSettingsInfo *) user_data; + GError *err = NULL; + GHashTable *settings = NULL; + NMConnection *connection; + NMManager *manager; + + g_return_if_fail (info != NULL); + + if (!dbus_g_proxy_end_call (proxy, call_id, &err, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &settings, + G_TYPE_INVALID)) { + nm_log_info (LOGD_USER_SET, "couldn't retrieve connection settings: %s.", + err && err->message ? err->message : "(unknown)"); + g_error_free (err); + goto out; + } + + manager = info->manager; + connection = info->connection; + if (connection == NULL) { + const char *path = dbus_g_proxy_get_path (proxy); + NMManagerPrivate *priv; + GError *error = NULL; + NMConnection *existing = NULL; + + connection = nm_connection_new_from_hash (settings, &error); + if (connection == NULL) { + nm_log_warn (LOGD_USER_SET, "invalid connection: '%s' / '%s' invalid: %d", + g_type_name (nm_connection_lookup_setting_type_by_quark (error->domain)), + error->message, error->code); + g_error_free (error); + goto out; + } + + nm_connection_set_path (connection, path); + nm_connection_set_scope (connection, NM_CONNECTION_SCOPE_USER); + + /* Add the new connection to the internal hashes only if the same + * connection isn't already there. + */ + priv = NM_MANAGER_GET_PRIVATE (manager); + + existing = g_hash_table_lookup (priv->user_connections, path); + if (!existing || !nm_connection_compare (existing, connection, NM_SETTING_COMPARE_FLAG_EXACT)) { + g_hash_table_insert (priv->user_connections, + g_strdup (path), + connection); + existing = NULL; + + /* Attach the D-Bus proxy representing the remote NMConnection + * to the local NMConnection object to ensure it stays alive to + * continue delivering signals. It'll be destroyed once the + * NMConnection is destroyed. + */ + g_object_set_data_full (G_OBJECT (connection), + "proxy", + g_object_ref (info->proxy), + g_object_unref); + } else + g_object_unref (connection); + + /* If the connection-added signal is supposed to be batched, don't + * emit the single connection-added here. Also, don't emit the signal + * if the connection wasn't actually added to the system or user hashes. + */ + if (!info->calls && !existing) { + g_signal_emit (manager, signals[CONNECTION_ADDED], 0, connection, NM_CONNECTION_SCOPE_USER); + /* Update the Bluetooth connections for that single new connection */ + bluez_manager_resync_devices (manager); + } + } else { + // FIXME: merge settings? or just replace? + nm_log_dbg (LOGD_USER_SET, "implement merge settings"); + } + +out: + if (settings) + g_hash_table_destroy (settings); + + return; +} + +static void +user_connection_removed_cb (DBusGProxy *proxy, gpointer user_data) +{ + NMManager *manager = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + NMConnection *connection = NULL; + const char *path; + + path = dbus_g_proxy_get_path (proxy); + if (path) { + connection = g_hash_table_lookup (priv->user_connections, path); + if (connection) + remove_connection (manager, connection, priv->user_connections); + } +} + +static void +user_connection_updated_cb (DBusGProxy *proxy, + GHashTable *settings, + gpointer user_data) +{ + NMManager *manager = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + NMConnection *new_connection; + NMConnection *old_connection = NULL; + gboolean valid = FALSE; + GError *error = NULL; + const char *path; + + path = dbus_g_proxy_get_path (proxy); + if (path) + old_connection = g_hash_table_lookup (priv->user_connections, path); + + g_return_if_fail (old_connection != NULL); + + new_connection = nm_connection_new_from_hash (settings, &error); + if (!new_connection) { + /* New connection invalid, remove existing connection */ + nm_log_warn (LOGD_USER_SET, "invalid connection: '%s' / '%s' invalid: %d", + g_type_name (nm_connection_lookup_setting_type_by_quark (error->domain)), + error->message, error->code); + g_error_free (error); + remove_connection (manager, old_connection, priv->user_connections); + return; + } + g_object_unref (new_connection); + + valid = nm_connection_replace_settings (old_connection, settings, NULL); + if (valid) { + g_signal_emit (manager, signals[CONNECTION_UPDATED], 0, + old_connection, + nm_connection_get_scope (old_connection)); + + bluez_manager_resync_devices (manager); + } else { + remove_connection (manager, old_connection, priv->user_connections); + } +} + +static void +user_internal_new_connection_cb (DBusGProxy *proxy, + const char *path, + NMManager *manager, + GSList **calls) +{ + struct GetSettingsInfo *info; + DBusGProxy *con_proxy; + DBusGConnection *g_connection; + DBusGProxyCall *call; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + + g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr); + con_proxy = dbus_g_proxy_new_for_name (g_connection, + dbus_g_proxy_get_bus_name (proxy), + path, + NM_DBUS_IFACE_SETTINGS_CONNECTION); + if (!con_proxy) { + nm_log_err (LOGD_USER_SET, "could not init user connection proxy"); + return; + } + + dbus_g_proxy_add_signal (con_proxy, "Updated", + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (con_proxy, "Updated", + G_CALLBACK (user_connection_updated_cb), + manager, + NULL); + + dbus_g_proxy_add_signal (con_proxy, "Removed", G_TYPE_INVALID, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (con_proxy, "Removed", + G_CALLBACK (user_connection_removed_cb), + manager, + NULL); + + info = g_slice_new0 (GetSettingsInfo); + info->manager = g_object_ref (manager); + info->calls = calls; + call = dbus_g_proxy_begin_call (con_proxy, "GetSettings", + user_connection_get_settings_cb, + info, + free_get_settings_info, + G_TYPE_INVALID); + info->call = call; + info->proxy = con_proxy; + if (info->calls) + *(info->calls) = g_slist_prepend (*(info->calls), call); +} + +static void +user_list_connections_cb (DBusGProxy *proxy, + DBusGProxyCall *call_id, + gpointer user_data) +{ + NMManager *manager = NM_MANAGER (user_data); + GError *err = NULL; + GPtrArray *ops; + GSList **calls = NULL; + int i; + + if (!dbus_g_proxy_end_call (proxy, call_id, &err, + DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, &ops, + G_TYPE_INVALID)) { + nm_log_warn (LOGD_USER_SET, "couldn't retrieve connections: %s", + err && err->message ? err->message : "(unknown)"); + g_error_free (err); + goto out; + } + + /* Keep track of all calls made here; don't want to emit connection-added for + * each one, but emit connections-added when they are all done. + */ + calls = g_slice_new0 (GSList *); + + for (i = 0; i < ops->len; i++) { + char *op = g_ptr_array_index (ops, i); + + user_internal_new_connection_cb (proxy, op, manager, calls); + g_free (op); + } + + g_ptr_array_free (ops, TRUE); + +out: + return; +} + +static void +user_new_connection_cb (DBusGProxy *proxy, const char *path, gpointer user_data) +{ + user_internal_new_connection_cb (proxy, path, NM_MANAGER (user_data), NULL); +} + +static void +user_query_connections (NMManager *manager) +{ + NMManagerPrivate *priv; + DBusGProxyCall *call; + DBusGConnection *g_connection; + + g_return_if_fail (NM_IS_MANAGER (manager)); + + priv = NM_MANAGER_GET_PRIVATE (manager); + if (!priv->user_proxy) { + g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr); + priv->user_proxy = dbus_g_proxy_new_for_name (g_connection, + NM_DBUS_SERVICE_USER_SETTINGS, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_IFACE_SETTINGS); + if (!priv->user_proxy) { + nm_log_err (LOGD_USER_SET, "could not init user settings proxy"); + return; + } + + dbus_g_proxy_add_signal (priv->user_proxy, + "NewConnection", + DBUS_TYPE_G_OBJECT_PATH, + G_TYPE_INVALID); + + dbus_g_proxy_connect_signal (priv->user_proxy, "NewConnection", + G_CALLBACK (user_new_connection_cb), + manager, + NULL); + } + + /* grab connections */ + call = dbus_g_proxy_begin_call (priv->user_proxy, "ListConnections", + user_list_connections_cb, + manager, + NULL, + G_TYPE_INVALID); +} + +/*******************************************************************/ +/* System settings stuff via NMSysconfigSettings */ +/*******************************************************************/ + +static void +system_connection_updated_cb (NMSettingsConnectionInterface *connection, + gpointer unused, + NMManager *manager) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + const char *path; + NMSettingsConnectionInterface *existing; + GError *error = NULL; + + path = nm_connection_get_path (NM_CONNECTION (connection)); + + existing = g_hash_table_lookup (priv->system_connections, path); + if (!existing) + return; + if (existing != connection) { + nm_log_warn (LOGD_SYS_SET, "existing connection didn't matched updated."); + return; + } + + if (!nm_connection_verify (NM_CONNECTION (existing), &error)) { + /* Updated connection invalid, remove existing connection */ + nm_log_warn (LOGD_SYS_SET, "invalid connection: '%s' / '%s' invalid: %d", + g_type_name (nm_connection_lookup_setting_type_by_quark (error->domain)), + error->message, error->code); + g_error_free (error); + remove_connection (manager, NM_CONNECTION (existing), priv->system_connections); + return; + } + + g_signal_emit (manager, signals[CONNECTION_UPDATED], 0, + existing, NM_CONNECTION_SCOPE_SYSTEM); + + bluez_manager_resync_devices (manager); +} + +static void +system_connection_removed_cb (NMSettingsConnectionInterface *connection, + NMManager *manager) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + const char *path; + + path = nm_connection_get_path (NM_CONNECTION (connection)); + + connection = g_hash_table_lookup (priv->system_connections, path); + if (connection) + remove_connection (manager, NM_CONNECTION (connection), priv->system_connections); +} + +static void +system_internal_new_connection (NMManager *manager, + NMSettingsConnectionInterface *connection) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + const char *path; + + g_return_if_fail (connection != NULL); + + g_signal_connect (connection, NM_SETTINGS_CONNECTION_INTERFACE_UPDATED, + G_CALLBACK (system_connection_updated_cb), manager); + g_signal_connect (connection, NM_SETTINGS_CONNECTION_INTERFACE_REMOVED, + G_CALLBACK (system_connection_removed_cb), manager); + + path = nm_connection_get_path (NM_CONNECTION (connection)); + g_hash_table_insert (priv->system_connections, g_strdup (path), + g_object_ref (connection)); +} + +static void +system_new_connection_cb (NMSysconfigSettings *settings, + NMSettingsConnectionInterface *connection, + NMManager *manager) +{ + system_internal_new_connection (manager, connection); +} + +static void +system_query_connections (NMManager *manager) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + GSList *system_connections, *iter; + + system_connections = nm_settings_interface_list_connections (NM_SETTINGS_INTERFACE (priv->sys_settings)); + for (iter = system_connections; iter; iter = g_slist_next (iter)) + system_internal_new_connection (manager, NM_SETTINGS_CONNECTION_INTERFACE (iter->data)); + g_slist_free (system_connections); +} + +static void +system_unmanaged_devices_changed_cb (NMSysconfigSettings *sys_settings, + GParamSpec *pspec, + gpointer user_data) +{ + NMManager *manager = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + const GSList *unmanaged_specs, *iter; + + unmanaged_specs = nm_sysconfig_settings_get_unmanaged_specs (sys_settings); + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + NMDevice *device = NM_DEVICE (iter->data); + gboolean managed; + + managed = !nm_device_interface_spec_match_list (NM_DEVICE_INTERFACE (device), unmanaged_specs); + nm_device_set_managed (device, + managed, + managed ? NM_DEVICE_STATE_REASON_NOW_MANAGED : + NM_DEVICE_STATE_REASON_NOW_UNMANAGED); + } +} + +static void +system_hostname_changed_cb (NMSysconfigSettings *sys_settings, + GParamSpec *pspec, + gpointer user_data) +{ + NMManager *manager = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + char *hostname; + + hostname = nm_sysconfig_settings_get_hostname (sys_settings); + + if (!hostname && !priv->hostname) + return; + + if (hostname && priv->hostname && !strcmp (hostname, priv->hostname)) + return; + + g_free (priv->hostname); + priv->hostname = (hostname && strlen (hostname)) ? g_strdup (hostname) : NULL; + g_object_notify (G_OBJECT (manager), NM_MANAGER_HOSTNAME); + + g_free (hostname); +} + +/*******************************************************************/ +/* General NMManager stuff */ +/*******************************************************************/ + +static NMDevice * +nm_manager_get_device_by_udi (NMManager *manager, const char *udi) +{ + GSList *iter; + + for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { + if (!strcmp (nm_device_get_udi (NM_DEVICE (iter->data)), udi)) + return NM_DEVICE (iter->data); + } + return NULL; +} + +static NMDevice * +nm_manager_get_device_by_path (NMManager *manager, const char *path) +{ + GSList *iter; + + for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { + if (!strcmp (nm_device_get_path (NM_DEVICE (iter->data)), path)) + return NM_DEVICE (iter->data); + } + return NULL; +} + +static void +nm_manager_name_owner_changed (NMDBusManager *mgr, + const char *name, + const char *old, + const char *new, + gpointer user_data) +{ + NMManager *manager = NM_MANAGER (user_data); + gboolean old_owner_good = (old && (strlen (old) > 0)); + gboolean new_owner_good = (new && (strlen (new) > 0)); + + if (strcmp (name, NM_DBUS_SERVICE_USER_SETTINGS) == 0) { + if (!old_owner_good && new_owner_good) { + /* User Settings service appeared, update stuff */ + user_query_connections (manager); + } else { + /* User Settings service disappeared, throw them away (?) */ + user_destroy_connections (manager); + bluez_manager_resync_devices (manager); + } + } +} + +/* Store value into key-file; supported types: boolean, int, string */ +static gboolean +write_value_to_state_file (const char *filename, + const char *group, + const char *key, + GType value_type, + gpointer value, + GError **error) +{ + GKeyFile *key_file; + char *data; + gsize len = 0; + gboolean ret = FALSE; + + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (group != NULL, FALSE); + g_return_val_if_fail (key != NULL, FALSE); + g_return_val_if_fail (value_type == G_TYPE_BOOLEAN || + value_type == G_TYPE_INT || + value_type == G_TYPE_STRING, + FALSE); + + key_file = g_key_file_new (); + if (!key_file) + return FALSE; + + g_key_file_set_list_separator (key_file, ','); + g_key_file_load_from_file (key_file, filename, G_KEY_FILE_KEEP_COMMENTS, NULL); + switch (value_type) { + case G_TYPE_BOOLEAN: + g_key_file_set_boolean (key_file, group, key, *((gboolean *) value)); + break; + case G_TYPE_INT: + g_key_file_set_integer (key_file, group, key, *((gint *) value)); + break; + case G_TYPE_STRING: + g_key_file_set_string (key_file, group, key, *((const gchar **) value)); + break; + } + + data = g_key_file_to_data (key_file, &len, NULL); + if (data) { + ret = g_file_set_contents (filename, data, len, error); + g_free (data); + } + g_key_file_free (key_file); + + return ret; +} + +static void +manager_set_radio_enabled (NMManager *manager, + RadioState *rstate, + gboolean enabled) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + GSList *iter; + GError *error = NULL; + + /* Do nothing for radio types not yet implemented */ + if (!rstate->prop) + return; + + if (rstate->enabled == enabled) + return; + + /* Can't set wireless enabled if it's disabled in hardware */ + if (!rstate->hw_enabled && enabled) + return; + + rstate->enabled = enabled; + + g_object_notify (G_OBJECT (manager), rstate->prop); + + /* Update enabled key in state file */ + if (priv->state_file) { + if (!write_value_to_state_file (priv->state_file, + "main", rstate->key, + G_TYPE_BOOLEAN, (gpointer) &enabled, + &error)) { + nm_log_warn (LOGD_CORE, "writing to state file %s failed: (%d) %s.", + priv->state_file, + error ? error->code : -1, + (error && error->message) ? error->message : "unknown"); + } + } + + /* Don't touch devices if asleep/networking disabled */ + if (manager_sleeping (manager)) + return; + + /* enable/disable wireless devices as required */ + for (iter = priv->devices; iter; iter = iter->next) { + RfKillType devtype = RFKILL_TYPE_UNKNOWN; + + g_object_get (G_OBJECT (iter->data), NM_DEVICE_INTERFACE_RFKILL_TYPE, &devtype, NULL); + if (devtype == rstate->rtype) { + nm_log_dbg (LOGD_RFKILL, "(%s): setting radio %s", + nm_device_get_iface (NM_DEVICE (iter->data)), + enabled ? "enabled" : "disabled"); + nm_device_interface_set_enabled (NM_DEVICE_INTERFACE (iter->data), enabled); + } + } +} + +static void +manager_hidden_ap_found (NMDeviceInterface *device, + NMAccessPoint *ap, + gpointer user_data) +{ + NMManager *manager = NM_MANAGER (user_data); + const struct ether_addr *ap_addr; + const GByteArray *ap_ssid; + GSList *iter; + GSList *connections; + gboolean done = FALSE; + + ap_ssid = nm_ap_get_ssid (ap); + if (ap_ssid && ap_ssid->len) + return; + + ap_addr = nm_ap_get_address (ap); + g_assert (ap_addr); + + /* Look for this AP's BSSID in the seen-bssids list of a connection, + * and if a match is found, copy over the SSID */ + connections = nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_SYSTEM); + connections = g_slist_concat (connections, nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_USER)); + + for (iter = connections; iter && !done; iter = g_slist_next (iter)) { + NMConnection *connection = NM_CONNECTION (iter->data); + NMSettingWireless *s_wireless; + const GByteArray *ssid; + guint32 num_bssids; + guint32 i; + + s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); + if (!s_wireless) + goto next; + + num_bssids = nm_setting_wireless_get_num_seen_bssids (s_wireless); + if (num_bssids < 1) + goto next; + + ssid = nm_setting_wireless_get_ssid (s_wireless); + g_assert (ssid); + + for (i = 0; i < num_bssids; i++) { + const char *seen_bssid = nm_setting_wireless_get_seen_bssid (s_wireless, i); + struct ether_addr seen_addr; + + if (!ether_aton_r (seen_bssid, &seen_addr)) + continue; + + if (memcmp (ap_addr, &seen_addr, sizeof (struct ether_addr))) + continue; + + /* Copy the SSID from the connection to the AP */ + nm_ap_set_ssid (ap, ssid); + done = TRUE; + } + +next: + g_object_unref (connection); + } + g_slist_free (connections); +} + +static RfKillState +nm_manager_get_ipw_rfkill_state (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GSList *iter; + RfKillState ipw_state = RFKILL_UNBLOCKED; + + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + NMDevice *candidate = NM_DEVICE (iter->data); + RfKillState candidate_state; + + if (NM_IS_DEVICE_WIFI (candidate)) { + candidate_state = nm_device_wifi_get_ipw_rfkill_state (NM_DEVICE_WIFI (candidate)); + + if (candidate_state > ipw_state) + ipw_state = candidate_state; + } + } + + return ipw_state; +} + +static RfKillState +nm_manager_get_modem_enabled_state (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GSList *iter; + RfKillState wwan_state = RFKILL_UNBLOCKED; + + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + NMDevice *candidate = NM_DEVICE (iter->data); + RfKillState candidate_state = RFKILL_UNBLOCKED; + RfKillType devtype = RFKILL_TYPE_UNKNOWN; + + g_object_get (G_OBJECT (candidate), NM_DEVICE_INTERFACE_RFKILL_TYPE, &devtype, NULL); + if (devtype == RFKILL_TYPE_WWAN) { + if (!nm_device_interface_get_enabled (NM_DEVICE_INTERFACE (candidate))) + candidate_state = RFKILL_SOFT_BLOCKED; + + if (candidate_state > wwan_state) + wwan_state = candidate_state; + } + } + + return wwan_state; +} + +static void +manager_rfkill_update_one_type (NMManager *self, + RadioState *rstate, + RfKillType rtype) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + RfKillState udev_state = RFKILL_UNBLOCKED; + RfKillState other_state = RFKILL_UNBLOCKED; + RfKillState composite; + gboolean new_e = TRUE, new_he = TRUE; + + udev_state = nm_udev_manager_get_rfkill_state (priv->udev_mgr, rtype); + + if (rstate->other_enabled_func) + other_state = rstate->other_enabled_func (self); + + /* The composite state is the "worst" of either udev or other states */ + if (udev_state == RFKILL_HARD_BLOCKED || other_state == RFKILL_HARD_BLOCKED) + composite = RFKILL_HARD_BLOCKED; + else if (udev_state == RFKILL_SOFT_BLOCKED || other_state == RFKILL_SOFT_BLOCKED) + composite = RFKILL_SOFT_BLOCKED; + else + composite = RFKILL_UNBLOCKED; + + switch (composite) { + case RFKILL_UNBLOCKED: + new_e = TRUE; + new_he = TRUE; + break; + case RFKILL_SOFT_BLOCKED: + new_e = FALSE; + new_he = TRUE; + break; + case RFKILL_HARD_BLOCKED: + new_e = FALSE; + new_he = FALSE; + break; + default: + break; + } + + if (rstate->desc) { + nm_log_dbg (LOGD_RFKILL, "%s hw-enabled %d enabled %d", + rstate->desc, new_he, new_e); + } + + if (new_he != rstate->hw_enabled) { + nm_log_info (LOGD_RFKILL, "%s now %s by radio killswitch", + rstate->desc, + (new_e && new_he) ? "enabled" : "disabled"); + + rstate->hw_enabled = new_he; + if (rstate->hw_prop) + g_object_notify (G_OBJECT (self), rstate->hw_prop); + } + manager_set_radio_enabled (self, rstate, new_e); +} + +static void +nm_manager_rfkill_update (NMManager *self, RfKillType rtype) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + guint i; + + if (rtype != RFKILL_TYPE_UNKNOWN) { + manager_rfkill_update_one_type (self, &priv->radio_states[rtype], rtype); + return; + } + + /* Otherwise sync all radio types */ + for (i = 0; i < RFKILL_TYPE_MAX; i++) + manager_rfkill_update_one_type (self, &priv->radio_states[i], i); +} + +static void +manager_ipw_rfkill_state_changed (NMDeviceWifi *device, + GParamSpec *pspec, + gpointer user_data) +{ + nm_manager_rfkill_update (NM_MANAGER (user_data), RFKILL_TYPE_WLAN); +} + +static void +manager_modem_enabled_changed (NMModem *device, gpointer user_data) +{ + nm_manager_rfkill_update (NM_MANAGER (user_data), RFKILL_TYPE_WWAN); +} + +static void +add_device (NMManager *self, NMDevice *device) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + const char *iface, *driver, *type_desc; + char *path; + static guint32 devcount = 0; + const GSList *unmanaged_specs; + NMConnection *existing = NULL; + GHashTableIter iter; + gpointer value; + gboolean managed = FALSE; + + iface = nm_device_get_ip_iface (device); + g_assert (iface); + + if (!NM_IS_DEVICE_MODEM (device) && find_device_by_iface (self, iface)) { + g_object_unref (device); + return; + } + + priv->devices = g_slist_append (priv->devices, device); + + g_signal_connect (device, "state-changed", + G_CALLBACK (manager_device_state_changed), + self); + + if (NM_IS_DEVICE_WIFI (device)) { + /* Attach to the access-point-added signal so that the manager can fill + * non-SSID-broadcasting APs with an SSID. + */ + g_signal_connect (device, "hidden-ap-found", + G_CALLBACK (manager_hidden_ap_found), + self); + + /* Hook up rfkill handling for ipw-based cards until they get converted + * to use the kernel's rfkill subsystem in 2.6.33. + */ + g_signal_connect (device, "notify::" NM_DEVICE_WIFI_IPW_RFKILL_STATE, + G_CALLBACK (manager_ipw_rfkill_state_changed), + self); + + /* Update global rfkill state with this device's rfkill state, and + * then set this device's rfkill state based on the global state. + */ + nm_manager_rfkill_update (self, RFKILL_TYPE_WLAN); + nm_device_interface_set_enabled (NM_DEVICE_INTERFACE (device), + priv->radio_states[RFKILL_TYPE_WLAN].enabled); + } else if (NM_IS_DEVICE_MODEM (device)) { + g_signal_connect (device, NM_DEVICE_MODEM_ENABLE_CHANGED, + G_CALLBACK (manager_modem_enabled_changed), + self); + + nm_manager_rfkill_update (self, RFKILL_TYPE_WWAN); + /* Until we start respecting WWAN rfkill switches the modem itself + * is the source of the enabled/disabled state, so the manager shouldn't + * touch it here. + nm_device_interface_set_enabled (NM_DEVICE_INTERFACE (device), + priv->radio_states[RFKILL_TYPE_WWAN].enabled); + */ + } + + type_desc = nm_device_get_type_desc (device); + g_assert (type_desc); + driver = nm_device_get_driver (device); + if (!driver) + driver = "unknown"; + nm_log_info (LOGD_HW, "(%s): new %s device (driver: '%s' ifindex: %d)", + iface, type_desc, driver, nm_device_get_ifindex (device)); + + path = g_strdup_printf ("/org/freedesktop/NetworkManager/Devices/%d", devcount++); + nm_device_set_path (device, path); + dbus_g_connection_register_g_object (nm_dbus_manager_get_connection (priv->dbus_mgr), + path, + G_OBJECT (device)); + nm_log_info (LOGD_CORE, "(%s): exported as %s", iface, path); + g_free (path); + + /* Check if we should assume the device's active connection by matching its + * config with an existing system connection. + */ + if (nm_device_interface_can_assume_connection (NM_DEVICE_INTERFACE (device))) { + GSList *connections = NULL; + + g_hash_table_iter_init (&iter, priv->system_connections); + while (g_hash_table_iter_next (&iter, NULL, &value)) + connections = g_slist_append (connections, value); + existing = nm_device_interface_connection_match_config (NM_DEVICE_INTERFACE (device), + (const GSList *) connections); + g_slist_free (connections); + + if (existing) { + NMSettingConnection *s_con; + + s_con = (NMSettingConnection *) nm_connection_get_setting (existing, NM_TYPE_SETTING_CONNECTION); + nm_log_dbg (LOGD_DEVICE, "(%s): found existing device connection '%s'", + nm_device_get_iface (device), + nm_setting_connection_get_id (s_con)); + } + } + + /* Start the device if it's supposed to be managed */ + unmanaged_specs = nm_sysconfig_settings_get_unmanaged_specs (priv->sys_settings); + if ( !manager_sleeping (self) + && !nm_device_interface_spec_match_list (NM_DEVICE_INTERFACE (device), unmanaged_specs)) { + nm_device_set_managed (device, + TRUE, + existing ? NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED : + NM_DEVICE_STATE_REASON_NOW_MANAGED); + managed = TRUE; + } + + nm_sysconfig_settings_device_added (priv->sys_settings, device); + g_signal_emit (self, signals[DEVICE_ADDED], 0, device); + + /* If the device has a connection it can assume, do that now */ + if (existing && managed && nm_device_is_available (device)) { + const char *ac_path; + GError *error = NULL; + + nm_log_dbg (LOGD_DEVICE, "(%s): will attempt to assume existing connection", + nm_device_get_iface (device)); + + ac_path = internal_activate_device (self, device, existing, NULL, FALSE, TRUE, &error); + if (ac_path) + g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS); + else { + nm_log_warn (LOGD_DEVICE, "assumed connection (%d) %s failed to activate: (%d) %s", + nm_connection_get_scope (existing), + nm_connection_get_path (existing), + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_error_free (error); + } + } +} + +static gboolean +bdaddr_matches_connection (NMSettingBluetooth *s_bt, const char *bdaddr) +{ + const GByteArray *arr; + gboolean ret = FALSE; + + arr = nm_setting_bluetooth_get_bdaddr (s_bt); + + if ( arr != NULL + && arr->len == ETH_ALEN) { + char *str; + + str = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", + arr->data[0], + arr->data[1], + arr->data[2], + arr->data[3], + arr->data[4], + arr->data[5]); + ret = g_str_equal (str, bdaddr); + g_free (str); + } + + return ret; +} + +static NMConnection * +bluez_manager_find_connection (NMManager *manager, + const char *bdaddr, + guint32 capabilities) +{ + NMConnection *found = NULL; + GSList *connections, *l; + + connections = nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_SYSTEM); + connections = g_slist_concat (connections, nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_USER)); + + for (l = connections; l != NULL; l = l->next) { + NMConnection *candidate = NM_CONNECTION (l->data); + NMSettingConnection *s_con; + NMSettingBluetooth *s_bt; + const char *con_type; + const char *bt_type; + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (candidate, NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + con_type = nm_setting_connection_get_connection_type (s_con); + g_assert (con_type); + if (!g_str_equal (con_type, NM_SETTING_BLUETOOTH_SETTING_NAME)) + continue; + + s_bt = (NMSettingBluetooth *) nm_connection_get_setting (candidate, NM_TYPE_SETTING_BLUETOOTH); + if (!s_bt) + continue; + + if (!bdaddr_matches_connection (s_bt, bdaddr)) + continue; + + bt_type = nm_setting_bluetooth_get_connection_type (s_bt); + if ( g_str_equal (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN) + && !(capabilities & NM_BT_CAPABILITY_DUN)) + continue; + if ( g_str_equal (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU) + && !(capabilities & NM_BT_CAPABILITY_NAP)) + continue; + + found = candidate; + break; + } + + g_slist_free (connections); + return found; +} + +static void +bluez_manager_resync_devices (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GSList *iter, *gone = NULL, *keep = NULL; + + /* Remove devices from the device list that don't have a corresponding connection */ + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + NMDevice *candidate = NM_DEVICE (iter->data); + guint32 uuids; + const char *bdaddr; + + if (NM_IS_DEVICE_BT (candidate)) { + uuids = nm_device_bt_get_capabilities (NM_DEVICE_BT (candidate)); + bdaddr = nm_device_bt_get_hw_address (NM_DEVICE_BT (candidate)); + + if (bluez_manager_find_connection (self, bdaddr, uuids)) + keep = g_slist_prepend (keep, candidate); + else + gone = g_slist_prepend (gone, candidate); + } else + keep = g_slist_prepend (keep, candidate); + } + + /* Only touch the device list if anything actually changed */ + if (g_slist_length (gone)) { + g_slist_free (priv->devices); + priv->devices = keep; + + while (g_slist_length (gone)) + gone = remove_one_device (self, gone, NM_DEVICE (gone->data), FALSE, TRUE); + } else { + g_slist_free (keep); + g_slist_free (gone); + } + + /* Now look for devices without connections */ + nm_bluez_manager_query_devices (priv->bluez_mgr); +} + +static void +bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr, + const char *bdaddr, + const char *name, + const char *object_path, + guint32 capabilities, + NMManager *manager) +{ + NMDevice *device; + gboolean has_dun = (capabilities & NM_BT_CAPABILITY_DUN); + gboolean has_nap = (capabilities & NM_BT_CAPABILITY_NAP); + + g_return_if_fail (bdaddr != NULL); + g_return_if_fail (name != NULL); + g_return_if_fail (object_path != NULL); + g_return_if_fail (capabilities != NM_BT_CAPABILITY_NONE); + + /* Make sure the device is not already in the device list */ + if (nm_manager_get_device_by_udi (manager, object_path)) + return; + + if (has_dun == FALSE && has_nap == FALSE) + return; + + if (!bluez_manager_find_connection (manager, bdaddr, capabilities)) + return; + + device = nm_device_bt_new (object_path, bdaddr, name, capabilities, FALSE); + if (device) { + nm_log_info (LOGD_HW, "BT device %s (%s) added (%s%s%s)", + name, + bdaddr, + has_dun ? "DUN" : "", + has_dun && has_nap ? " " : "", + has_nap ? "NAP" : ""); + + add_device (manager, device); + } +} + +static void +bluez_manager_bdaddr_removed_cb (NMBluezManager *bluez_mgr, + const char *bdaddr, + const char *object_path, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GSList *iter; + + g_return_if_fail (bdaddr != NULL); + g_return_if_fail (object_path != NULL); + + nm_log_info (LOGD_HW, "BT device %s removed", bdaddr); + + for (iter = priv->devices; iter; iter = iter->next) { + NMDevice *device = NM_DEVICE (iter->data); + + if (!strcmp (nm_device_get_udi (device), object_path)) { + priv->devices = remove_one_device (self, priv->devices, device, FALSE, TRUE); + break; + } + } +} + +static NMDevice * +find_device_by_iface (NMManager *self, const gchar *iface) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GSList *iter; + + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + NMDevice *device = NM_DEVICE (iter->data); + const gchar *d_iface = nm_device_get_ip_iface (device); + if (!strcmp (d_iface, iface)) + return device; + } + + return NULL; +} + +static NMDevice * +find_device_by_ifindex (NMManager *self, guint32 ifindex) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GSList *iter; + + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + NMDevice *candidate = NM_DEVICE (iter->data); + + if (ifindex == nm_device_get_ifindex (candidate)) + return candidate; + } + return NULL; +} + +static void +udev_device_added_cb (NMUdevManager *udev_mgr, + GUdevDevice *udev_device, + NMDeviceCreatorFn creator_fn, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + GObject *device; + guint32 ifindex; + + ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX"); + if (find_device_by_ifindex (self, ifindex)) + return; + + device = creator_fn (udev_mgr, udev_device, manager_sleeping (self)); + if (device) + add_device (self, NM_DEVICE (device)); +} + +static void +udev_device_removed_cb (NMUdevManager *manager, + GUdevDevice *udev_device, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *device; + guint32 ifindex; + + ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX"); + device = find_device_by_ifindex (self, ifindex); + if (device) + priv->devices = remove_one_device (self, priv->devices, device, FALSE, TRUE); +} + +static void +udev_manager_rfkill_changed_cb (NMUdevManager *udev_mgr, + RfKillType rtype, + RfKillState udev_state, + gpointer user_data) +{ + nm_manager_rfkill_update (NM_MANAGER (user_data), rtype); +} + +GSList * +nm_manager_get_devices (NMManager *manager) +{ + g_return_val_if_fail (NM_IS_MANAGER (manager), NULL); + + return NM_MANAGER_GET_PRIVATE (manager)->devices; +} + +static gboolean +impl_manager_get_devices (NMManager *manager, GPtrArray **devices, GError **err) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + GSList *iter; + + *devices = g_ptr_array_sized_new (g_slist_length (priv->devices)); + + for (iter = priv->devices; iter; iter = iter->next) + g_ptr_array_add (*devices, g_strdup (nm_device_get_path (NM_DEVICE (iter->data)))); + + return TRUE; +} + +static NMActRequest * +nm_manager_get_act_request_by_path (NMManager *manager, + const char *path, + NMDevice **device) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + GSList *iter; + + g_return_val_if_fail (manager != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (*device == NULL, NULL); + + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + NMActRequest *req; + const char *ac_path; + + req = nm_device_get_act_request (NM_DEVICE (iter->data)); + if (!req) + continue; + + ac_path = nm_act_request_get_active_connection_path (req); + if (!strcmp (path, ac_path)) { + *device = NM_DEVICE (iter->data); + return req; + } + } + + return NULL; +} + +typedef struct GetSecretsInfo { + NMManager *manager; + NMSecretsProviderInterface *provider; + + char *setting_name; + RequestSecretsCaller caller; + gboolean request_new; + + /* User connection bits */ + DBusGProxy *proxy; + DBusGProxyCall *call; + + /* System connection bits */ + guint32 idle_id; + char *hint1; + char *hint2; + char *connection_path; +} GetSecretsInfo; + +static void +free_get_secrets_info (gpointer data) +{ + GetSecretsInfo *info = data; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (info->manager); + + g_object_weak_unref (G_OBJECT (info->provider), (GWeakNotify) free_get_secrets_info, info); + + priv->secrets_calls = g_slist_remove (priv->secrets_calls, info); + + if (info->proxy) { + if (info->call) + dbus_g_proxy_cancel_call (info->proxy, info->call); + g_object_unref (info->proxy); + } + + if (info->idle_id) + g_source_remove (info->idle_id); + + g_free (info->hint1); + g_free (info->hint2); + g_free (info->setting_name); + g_free (info->connection_path); + memset (info, 0, sizeof (GetSecretsInfo)); + g_free (info); +} + +static void +provider_cancel_secrets (NMSecretsProviderInterface *provider, gpointer user_data) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (user_data); + GSList *iter; + + for (iter = priv->secrets_calls; iter; iter = g_slist_next (iter)) { + GetSecretsInfo *candidate = iter->data; + + if (candidate->provider == provider) { + free_get_secrets_info (candidate); + break; + } + } +} + +static void +user_get_secrets_cb (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + GetSecretsInfo *info = (GetSecretsInfo *) user_data; + GHashTable *settings = NULL; + GError *error = NULL; + GObject *provider; + + g_return_if_fail (info != NULL); + g_return_if_fail (info->provider); + g_return_if_fail (info->setting_name); + + provider = g_object_ref (info->provider); + + if (dbus_g_proxy_end_call (proxy, call, &error, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &settings, + G_TYPE_INVALID)) { + nm_secrets_provider_interface_get_secrets_result (info->provider, + info->setting_name, + info->caller, + settings, + NULL); + g_hash_table_destroy (settings); + } else { + nm_secrets_provider_interface_get_secrets_result (info->provider, + info->setting_name, + info->caller, + NULL, + error); + g_clear_error (&error); + } + + info->call = NULL; + free_get_secrets_info (info); + + g_object_unref (provider); +} + +static GetSecretsInfo * +user_get_secrets (NMManager *self, + NMSecretsProviderInterface *provider, + NMConnection *connection, + const char *setting_name, + gboolean request_new, + RequestSecretsCaller caller_id, + const char *hint1, + const char *hint2) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + DBusGConnection *g_connection; + GetSecretsInfo *info = NULL; + GPtrArray *hints = NULL; + + info = g_malloc0 (sizeof (GetSecretsInfo)); + + g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr); + info->proxy = dbus_g_proxy_new_for_name (g_connection, + NM_DBUS_SERVICE_USER_SETTINGS, + nm_connection_get_path (connection), + NM_DBUS_IFACE_SETTINGS_CONNECTION_SECRETS); + if (!info->proxy) { + nm_log_warn (LOGD_USER_SET, "could not create user connection secrets proxy"); + g_free (info); + return NULL; + } + + info->manager = self; + info->provider = provider; + info->caller = caller_id; + info->setting_name = g_strdup (setting_name); + + g_object_weak_ref (G_OBJECT (provider), (GWeakNotify) free_get_secrets_info, info); + + hints = g_ptr_array_sized_new (2); + if (hint1) + g_ptr_array_add (hints, (char *) hint1); + if (hint2) + g_ptr_array_add (hints, (char *) hint2); + + info->call = dbus_g_proxy_begin_call_with_timeout (info->proxy, "GetSecrets", + user_get_secrets_cb, + info, + NULL, + G_MAXINT32, + G_TYPE_STRING, setting_name, + DBUS_TYPE_G_ARRAY_OF_STRING, hints, + G_TYPE_BOOLEAN, request_new, + G_TYPE_INVALID); + g_ptr_array_free (hints, TRUE); + return info; +} + +static void +system_get_secrets_reply_cb (NMSettingsConnectionInterface *connection, + GHashTable *secrets, + GError *error, + gpointer user_data) +{ + GetSecretsInfo *info = user_data; + GObject *provider; + + provider = g_object_ref (info->provider); + + nm_secrets_provider_interface_get_secrets_result (info->provider, + info->setting_name, + info->caller, + error ? NULL : secrets, + error); + free_get_secrets_info (info); + g_object_unref (provider); +} + +static gboolean +system_get_secrets_idle_cb (gpointer user_data) +{ + GetSecretsInfo *info = user_data; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (info->manager); + NMSettingsConnectionInterface *connection; + GError *error = NULL; + const char *hints[3] = { NULL, NULL, NULL }; + + info->idle_id = 0; + + connection = nm_settings_interface_get_connection_by_path (NM_SETTINGS_INTERFACE (priv->sys_settings), + info->connection_path); + if (!connection) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "unknown connection (not exported by system settings)"); + nm_secrets_provider_interface_get_secrets_result (info->provider, + info->setting_name, + info->caller, + NULL, + error); + g_error_free (error); + free_get_secrets_info (info); + return FALSE; + } + + hints[0] = info->hint1; + hints[1] = info->hint2; + nm_settings_connection_interface_get_secrets (connection, + info->setting_name, + hints, + info->request_new, + system_get_secrets_reply_cb, + info); + return FALSE; +} + +static GetSecretsInfo * +system_get_secrets (NMManager *self, + NMSecretsProviderInterface *provider, + NMConnection *connection, + const char *setting_name, + gboolean request_new, + RequestSecretsCaller caller_id, + const char *hint1, + const char *hint2) +{ + GetSecretsInfo *info; + + info = g_malloc0 (sizeof (GetSecretsInfo)); + info->manager = self; + info->provider = provider; + info->caller = caller_id; + info->setting_name = g_strdup (setting_name); + info->hint1 = hint1 ? g_strdup (hint1) : NULL; + info->hint2 = hint2 ? g_strdup (hint2) : NULL; + info->connection_path = g_strdup (nm_connection_get_path (connection)); + info->request_new = request_new; + + g_object_weak_ref (G_OBJECT (provider), (GWeakNotify) free_get_secrets_info, info); + + info->idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + system_get_secrets_idle_cb, + info, + NULL); + return info; +} + +static gboolean +provider_get_secrets (NMSecretsProviderInterface *provider, + NMConnection *connection, + const char *setting_name, + gboolean request_new, + RequestSecretsCaller caller_id, + const char *hint1, + const char *hint2, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GetSecretsInfo *info = NULL; + NMConnectionScope scope; + GSList *iter; + + g_return_val_if_fail (connection != NULL, FALSE); + g_return_val_if_fail (setting_name != NULL, FALSE); + + /* Tear down any pending secrets requests for this secrets provider */ + for (iter = priv->secrets_calls; iter; iter = g_slist_next (iter)) { + GetSecretsInfo *candidate = iter->data; + + if (provider == candidate->provider) { + free_get_secrets_info (candidate); + break; + } + } + + /* Build up the new secrets request */ + scope = nm_connection_get_scope (connection); + if (scope == NM_CONNECTION_SCOPE_SYSTEM) { + info = system_get_secrets (self, provider, connection, setting_name, + request_new, caller_id, hint1, hint2); + } else if (scope == NM_CONNECTION_SCOPE_USER) { + info = user_get_secrets (self, provider, connection, setting_name, + request_new, caller_id, hint1, hint2); + } + + if (info) + priv->secrets_calls = g_slist_append (priv->secrets_calls, info); + + return !!info; +} + +static const char * +internal_activate_device (NMManager *manager, + NMDevice *device, + NMConnection *connection, + const char *specific_object, + gboolean user_requested, + gboolean assumed, + GError **error) +{ + NMActRequest *req; + NMDeviceInterface *dev_iface; + gboolean success; + + g_return_val_if_fail (NM_IS_MANAGER (manager), NULL); + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + dev_iface = NM_DEVICE_INTERFACE (device); + + /* Ensure the requested connection is compatible with the device */ + if (!nm_device_interface_check_connection_compatible (dev_iface, connection, error)) + return NULL; + + /* Tear down any existing connection */ + if (nm_device_get_act_request (device)) { + nm_device_state_changed (device, + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_NONE); + } + + req = nm_act_request_new (connection, specific_object, user_requested, assumed, (gpointer) device); + g_signal_connect (req, "manager-get-secrets", G_CALLBACK (provider_get_secrets), manager); + g_signal_connect (req, "manager-cancel-secrets", G_CALLBACK (provider_cancel_secrets), manager); + success = nm_device_interface_activate (dev_iface, req, error); + g_object_unref (req); + + return success ? nm_act_request_get_active_connection_path (req) : NULL; +} + +static gboolean +wait_for_connection_expired (gpointer data) +{ + NMManager *manager = NM_MANAGER (data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + PendingConnectionInfo *info = priv->pending_connection_info; + GError *error = NULL; + + g_return_val_if_fail (info != NULL, FALSE); + + g_set_error (&error, + NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "%s", "Connection was not provided by any settings service"); + nm_log_warn (LOGD_CORE, "connection (%d) %s failed to activate (timeout): (%d) %s", + info->scope, info->connection_path, error->code, error->message); + dbus_g_method_return_error (info->context, error); + g_error_free (error); + + info->timeout_id = 0; + pending_connection_info_destroy (priv->pending_connection_info); + priv->pending_connection_info = NULL; + + return FALSE; +} + +const char * +nm_manager_activate_connection (NMManager *manager, + NMConnection *connection, + const char *specific_object, + const char *device_path, + gboolean user_requested, + GError **error) +{ + NMManagerPrivate *priv; + NMDevice *device = NULL; + NMSettingConnection *s_con; + NMVPNConnection *vpn_connection; + const char *path; + + g_return_val_if_fail (manager != NULL, NULL); + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (error != NULL, NULL); + g_return_val_if_fail (*error == NULL, NULL); + + priv = NM_MANAGER_GET_PRIVATE (manager); + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + + if (!strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_VPN_SETTING_NAME)) { + NMActRequest *req = NULL; + NMVPNManager *vpn_manager; + + /* VPN connection */ + + if (specific_object) { + /* Find the specifc connection the client requested we use */ + req = nm_manager_get_act_request_by_path (manager, specific_object, &device); + if (!req) { + g_set_error (error, + NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, + "%s", "Base connection for VPN connection not active."); + return NULL; + } + } else { + GSList *iter; + + /* Just find the current default connection */ + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + NMDevice *candidate = NM_DEVICE (iter->data); + NMActRequest *candidate_req; + + candidate_req = nm_device_get_act_request (candidate); + if (candidate_req && nm_act_request_get_default (candidate_req)) { + device = candidate; + req = candidate_req; + break; + } + } + } + + if (!device || !req) { + g_set_error (error, + NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "%s", "Could not find source connection, or the source connection had no active device."); + return NULL; + } + + vpn_manager = nm_vpn_manager_get (); + vpn_connection = nm_vpn_manager_activate_connection (vpn_manager, + connection, + req, + device, + error); + g_signal_connect (vpn_connection, "manager-get-secrets", + G_CALLBACK (provider_get_secrets), manager); + g_signal_connect (vpn_connection, "manager-cancel-secrets", + G_CALLBACK (provider_cancel_secrets), manager); + path = nm_vpn_connection_get_active_connection_path (vpn_connection); + g_object_unref (vpn_manager); + } else { + NMDeviceState state; + + /* Device-based connection */ + device = nm_manager_get_device_by_path (manager, device_path); + if (!device) { + g_set_error (error, + NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "%s", "Device not found"); + return NULL; + } + + state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); + if (state < NM_DEVICE_STATE_DISCONNECTED) { + g_set_error (error, + NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNMANAGED_DEVICE, + "%s", "Device not managed by NetworkManager"); + return NULL; + } + + path = internal_activate_device (manager, + device, + connection, + specific_object, + user_requested, + FALSE, + error); + } + + return path; +} + +static void +connection_added_default_handler (NMManager *manager, + NMConnection *connection, + NMConnectionScope scope) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + PendingConnectionInfo *info = priv->pending_connection_info; + const char *path; + GError *error = NULL; + + if (!info) + return; + + if (scope != info->scope) + return; + + if (strcmp (info->connection_path, nm_connection_get_path (connection))) + return; + + /* Will destroy below; can't be valid during the initial activation start */ + priv->pending_connection_info = NULL; + + path = nm_manager_activate_connection (manager, + connection, + info->specific_object_path, + info->device_path, + TRUE, + &error); + if (path) { + dbus_g_method_return (info->context, path); + g_object_notify (G_OBJECT (manager), NM_MANAGER_ACTIVE_CONNECTIONS); + } else { + dbus_g_method_return_error (info->context, error); + nm_log_warn (LOGD_CORE, "connection (%d) %s failed to activate: (%d) %s", + scope, info->connection_path, error->code, error->message); + g_error_free (error); + } + + pending_connection_info_destroy (info); +} + +static gboolean +is_user_request_authorized (NMManager *manager, + DBusGMethodInvocation *context, + GError **error) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + DBusConnection *connection; + char *sender = NULL; + gulong sender_uid = G_MAXULONG; + DBusError dbus_error; + char *service_owner = NULL; + const char *service_name; + gulong service_uid = G_MAXULONG; + gboolean success = FALSE; + + /* Ensure the request to activate the user connection came from the + * same session as the user settings service. FIXME: use ConsoleKit + * too. + */ + if (!priv->user_proxy) { + g_set_error (error, NM_MANAGER_ERROR, + NM_MANAGER_ERROR_INVALID_SERVICE, + "%s", "No user settings service available"); + goto out; + } + + sender = dbus_g_method_get_sender (context); + if (!sender) { + g_set_error (error, NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "%s", "Could not determine D-Bus requestor"); + goto out; + } + + connection = nm_dbus_manager_get_dbus_connection (priv->dbus_mgr); + if (!connection) { + g_set_error (error, NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "%s", "Could not get the D-Bus system bus"); + goto out; + } + + dbus_error_init (&dbus_error); + /* FIXME: do this async */ + sender_uid = dbus_bus_get_unix_user (connection, sender, &dbus_error); + if (dbus_error_is_set (&dbus_error)) { + dbus_error_free (&dbus_error); + g_set_error (error, NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "%s", "Could not determine the Unix user ID of the requestor"); + goto out; + } + + /* Let root activate anything. + * FIXME: use a PolicyKit permission instead + */ + if (0 == sender_uid) { + success = TRUE; + goto out; + } + + service_name = dbus_g_proxy_get_bus_name (priv->user_proxy); + if (!service_name) { + g_set_error (error, NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "%s", "Could not determine user settings service name"); + goto out; + } + + service_owner = nm_dbus_manager_get_name_owner (priv->dbus_mgr, service_name, NULL); + if (!service_owner) { + g_set_error (error, NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "%s", "Could not determine D-Bus owner of the user settings service"); + goto out; + } + + dbus_error_init (&dbus_error); + /* FIXME: do this async */ + service_uid = dbus_bus_get_unix_user (connection, service_owner, &dbus_error); + if (dbus_error_is_set (&dbus_error)) { + dbus_error_free (&dbus_error); + g_set_error (error, NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "%s", "Could not determine the Unix UID of the sender of the request"); + goto out; + } + + /* And finally, the actual UID check */ + if (sender_uid != service_uid) { + g_set_error (error, NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "%s", "Requestor UID does not match the UID of the user settings service"); + goto out; + } + + success = TRUE; + +out: + g_free (sender); + g_free (service_owner); + return success; +} + +static void +impl_manager_activate_connection (NMManager *manager, + const char *service_name, + const char *connection_path, + const char *device_path, + const char *specific_object_path, + DBusGMethodInvocation *context) +{ + NMConnectionScope scope = NM_CONNECTION_SCOPE_UNKNOWN; + NMConnection *connection; + GError *error = NULL; + char *real_sop = NULL; + char *path = NULL; + + if (!strcmp (service_name, NM_DBUS_SERVICE_USER_SETTINGS)) { + if (!is_user_request_authorized (manager, context, &error)) + goto err; + + scope = NM_CONNECTION_SCOPE_USER; + } else if (!strcmp (service_name, NM_DBUS_SERVICE_SYSTEM_SETTINGS)) + scope = NM_CONNECTION_SCOPE_SYSTEM; + else { + g_set_error (&error, + NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_SERVICE, + "%s", "Invalid settings service name"); + goto err; + } + + /* "/" is special-cased to NULL to get through D-Bus */ + if (specific_object_path && strcmp (specific_object_path, "/")) + real_sop = g_strdup (specific_object_path); + + connection = nm_manager_get_connection_by_object_path (manager, scope, connection_path); + if (connection) { + path = (char *) nm_manager_activate_connection (manager, + connection, + real_sop, + device_path, + TRUE, + &error); + if (path) { + dbus_g_method_return (context, path); + g_object_notify (G_OBJECT (manager), NM_MANAGER_ACTIVE_CONNECTIONS); + } + } else { + PendingConnectionInfo *info; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + + if (priv->pending_connection_info) { + pending_connection_info_destroy (priv->pending_connection_info); + priv->pending_connection_info = NULL; + } + + /* Don't have the connection quite yet, probably created by + * the client on-the-fly. Defer the activation until we have it + */ + + info = g_slice_new0 (PendingConnectionInfo); + info->context = context; + info->device_path = g_strdup (device_path); + info->scope = scope; + info->connection_path = g_strdup (connection_path); + info->specific_object_path = g_strdup (real_sop); + info->timeout_id = g_timeout_add_seconds (5, wait_for_connection_expired, manager); + + // FIXME: should probably be per-device, not global to the manager + NM_MANAGER_GET_PRIVATE (manager)->pending_connection_info = info; + } + + err: + if (error) { + dbus_g_method_return_error (context, error); + nm_log_warn (LOGD_CORE, "connection (%d) %s failed to activate: (%d) %s", + scope, connection_path, error->code, error->message); + g_error_free (error); + } + + g_free (real_sop); +} + +gboolean +nm_manager_deactivate_connection (NMManager *manager, + const char *connection_path, + NMDeviceStateReason reason, + GError **error) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + NMVPNManager *vpn_manager; + GSList *iter; + gboolean success = FALSE; + NMVPNConnectionStateReason vpn_reason = NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED; + + /* Check for device connections first */ + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + NMDevice *device = NM_DEVICE (iter->data); + NMActRequest *req; + + req = nm_device_get_act_request (device); + if (!req) + continue; + + if (!strcmp (connection_path, nm_act_request_get_active_connection_path (req))) { + nm_device_state_changed (device, + NM_DEVICE_STATE_DISCONNECTED, + reason); + success = TRUE; + goto done; + } + } + + /* Check for VPN connections next */ + vpn_manager = nm_vpn_manager_get (); + if (reason == NM_DEVICE_STATE_REASON_CONNECTION_REMOVED) + vpn_reason = NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED; + if (nm_vpn_manager_deactivate_connection (vpn_manager, connection_path, vpn_reason)) { + success = TRUE; + } else { + g_set_error (error, + NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, + "%s", "The connection was not active."); + } + g_object_unref (vpn_manager); + +done: + g_object_notify (G_OBJECT (manager), NM_MANAGER_ACTIVE_CONNECTIONS); + return success; +} + +static gboolean +impl_manager_deactivate_connection (NMManager *manager, + const char *connection_path, + GError **error) +{ + return nm_manager_deactivate_connection (manager, + connection_path, + NM_DEVICE_STATE_REASON_USER_REQUESTED, + error); +} + +static void +do_sleep_wake (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + const GSList *unmanaged_specs; + GSList *iter; + + if (manager_sleeping (self)) { + nm_log_info (LOGD_SUSPEND, "sleeping or disabling..."); + + /* Just deactivate and down all devices from the device list, + * to keep things fast the device list will get resynced when + * the manager wakes up. + */ + for (iter = priv->devices; iter; iter = iter->next) + nm_device_set_managed (NM_DEVICE (iter->data), FALSE, NM_DEVICE_STATE_REASON_SLEEPING); + + } else { + nm_log_info (LOGD_SUSPEND, "waking up and re-enabling..."); + + unmanaged_specs = nm_sysconfig_settings_get_unmanaged_specs (priv->sys_settings); + + /* Ensure rfkill state is up-to-date since we don't respond to state + * changes during sleep. + */ + nm_manager_rfkill_update (self, RFKILL_TYPE_UNKNOWN); + + /* Re-manage managed devices */ + for (iter = priv->devices; iter; iter = iter->next) { + NMDevice *device = NM_DEVICE (iter->data); + guint i; + + /* enable/disable wireless devices since that we don't respond + * to killswitch changes during sleep. + */ + for (i = 0; i < RFKILL_TYPE_MAX; i++) { + RadioState *rstate = &priv->radio_states[i]; + gboolean enabled = (rstate->hw_enabled && rstate->enabled); + RfKillType devtype = RFKILL_TYPE_UNKNOWN; + + if (rstate->desc) { + nm_log_dbg (LOGD_RFKILL, "%s %s devices (hw_enabled %d, enabled %d)", + enabled ? "enabling" : "disabling", + rstate->desc, rstate->hw_enabled, rstate->enabled); + } + + g_object_get (G_OBJECT (device), NM_DEVICE_INTERFACE_RFKILL_TYPE, &devtype, NULL); + if (devtype == rstate->rtype) + nm_device_interface_set_enabled (NM_DEVICE_INTERFACE (device), enabled); + } + + nm_device_clear_autoconnect_inhibit (device); + if (nm_device_interface_spec_match_list (NM_DEVICE_INTERFACE (device), unmanaged_specs)) + nm_device_set_managed (device, FALSE, NM_DEVICE_STATE_REASON_NOW_UNMANAGED); + else + nm_device_set_managed (device, TRUE, NM_DEVICE_STATE_REASON_NOW_MANAGED); + } + + /* Ask for new bluetooth devices */ + bluez_manager_resync_devices (self); + } + + nm_manager_update_state (self); +} + +static gboolean +impl_manager_sleep (NMManager *self, gboolean do_sleep, GError **error) +{ + NMManagerPrivate *priv; + + g_return_val_if_fail (NM_IS_MANAGER (self), FALSE); + + priv = NM_MANAGER_GET_PRIVATE (self); + + if (priv->sleeping == do_sleep) { + g_set_error (error, + NM_MANAGER_ERROR, NM_MANAGER_ERROR_ALREADY_ASLEEP_OR_AWAKE, + "Already %s", do_sleep ? "asleep" : "awake"); + return FALSE; + } + + nm_log_info (LOGD_SUSPEND, "%s requested (sleeping: %s enabled: %s)", + do_sleep ? "sleep" : "wake", + priv->sleeping ? "yes" : "no", + priv->net_enabled ? "yes" : "no"); + + priv->sleeping = do_sleep; + + do_sleep_wake (self); + + g_object_notify (G_OBJECT (self), NM_MANAGER_SLEEPING); + return TRUE; +} + +static gboolean +impl_manager_enable (NMManager *self, gboolean enable, GError **error) +{ + NMManagerPrivate *priv; + + g_return_val_if_fail (NM_IS_MANAGER (self), FALSE); + + priv = NM_MANAGER_GET_PRIVATE (self); + + if (priv->net_enabled == enable) { + g_set_error (error, + NM_MANAGER_ERROR, NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED, + "Already %s", enable ? "enabled" : "disabled"); + return FALSE; + } + + /* Update "NetworkingEnabled" key in state file */ + if (priv->state_file) { + GError *err = NULL; + + if (!write_value_to_state_file (priv->state_file, + "main", "NetworkingEnabled", + G_TYPE_BOOLEAN, (gpointer) &enable, + &err)) { + /* Not a hard error */ + nm_log_warn (LOGD_SUSPEND, "writing to state file %s failed: (%d) %s.", + priv->state_file, + err ? err->code : -1, + (err && err->message) ? err->message : "unknown"); + } + } + + nm_log_info (LOGD_SUSPEND, "%s requested (sleeping: %s enabled: %s)", + enable ? "enable" : "disable", + priv->sleeping ? "yes" : "no", + priv->net_enabled ? "yes" : "no"); + + priv->net_enabled = enable; + + do_sleep_wake (self); + + g_object_notify (G_OBJECT (self), NM_MANAGER_NETWORKING_ENABLED); + return TRUE; +} + +/* Legacy 0.6 compatibility interface */ + +static gboolean +impl_manager_legacy_sleep (NMManager *manager, GError **error) +{ + return impl_manager_sleep (manager, TRUE, error); +} + +static gboolean +impl_manager_legacy_wake (NMManager *manager, GError **error) +{ + return impl_manager_sleep (manager, FALSE, error); +} + +static gboolean +impl_manager_legacy_state (NMManager *manager, guint32 *state, GError **err) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + + nm_manager_update_state (manager); + *state = priv->state; + return TRUE; +} + +static gboolean +impl_manager_set_logging (NMManager *manager, + const char *level, + const char *domains, + GError **error) +{ + if (nm_logging_setup (level, domains, error)) { + char *new_domains = nm_logging_domains_to_string (); + + nm_log_info (LOGD_CORE, "logging: level '%s' domains '%s'", + nm_logging_level_to_string (), + new_domains); + g_free (new_domains); + return TRUE; + } + return FALSE; +} + +/* Connections */ + +static int +connection_sort (gconstpointer pa, gconstpointer pb) +{ + NMConnection *a = NM_CONNECTION (pa); + NMSettingConnection *con_a; + NMConnection *b = NM_CONNECTION (pb); + NMSettingConnection *con_b; + + con_a = (NMSettingConnection *) nm_connection_get_setting (a, NM_TYPE_SETTING_CONNECTION); + g_assert (con_a); + con_b = (NMSettingConnection *) nm_connection_get_setting (b, NM_TYPE_SETTING_CONNECTION); + g_assert (con_b); + + if (nm_setting_connection_get_autoconnect (con_a) != nm_setting_connection_get_autoconnect (con_b)) { + if (nm_setting_connection_get_autoconnect (con_a)) + return -1; + return 1; + } + + if (nm_setting_connection_get_timestamp (con_a) > nm_setting_connection_get_timestamp (con_b)) + return -1; + else if (nm_setting_connection_get_timestamp (con_a) == nm_setting_connection_get_timestamp (con_b)) + return 0; + return 1; +} + +static void +connections_to_slist (gpointer key, gpointer value, gpointer user_data) +{ + GSList **list = (GSList **) user_data; + + *list = g_slist_insert_sorted (*list, g_object_ref (value), connection_sort); +} + +/* Returns a GSList of referenced NMConnection objects, caller must + * unref the connections in the list and destroy the list. + */ +GSList * +nm_manager_get_connections (NMManager *manager, + NMConnectionScope scope) +{ + NMManagerPrivate *priv; + GSList *list = NULL; + + g_return_val_if_fail (NM_IS_MANAGER (manager), NULL); + + priv = NM_MANAGER_GET_PRIVATE (manager); + if (scope == NM_CONNECTION_SCOPE_USER) + g_hash_table_foreach (priv->user_connections, connections_to_slist, &list); + else if (scope == NM_CONNECTION_SCOPE_SYSTEM) + g_hash_table_foreach (priv->system_connections, connections_to_slist, &list); + else + nm_log_err (LOGD_CORE, "unknown NMConnectionScope %d", scope); + return list; +} + +NMConnection * +nm_manager_get_connection_by_object_path (NMManager *manager, + NMConnectionScope scope, + const char *path) +{ + NMManagerPrivate *priv; + NMConnection *connection = NULL; + + g_return_val_if_fail (NM_IS_MANAGER (manager), NULL); + g_return_val_if_fail (path != NULL, NULL); + + priv = NM_MANAGER_GET_PRIVATE (manager); + if (scope == NM_CONNECTION_SCOPE_USER) + connection = (NMConnection *) g_hash_table_lookup (priv->user_connections, path); + else if (scope == NM_CONNECTION_SCOPE_SYSTEM) + connection = (NMConnection *) g_hash_table_lookup (priv->system_connections, path); + else + nm_log_err (LOGD_CORE, "unknown NMConnectionScope %d", scope); + return connection; +} + +GPtrArray * +nm_manager_get_active_connections_by_connection (NMManager *manager, + NMConnection *connection) +{ + return get_active_connections (manager, connection); +} + +void +nm_manager_start (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + guint i; + + /* Set initial radio enabled/disabled state */ + for (i = 0; i < RFKILL_TYPE_MAX; i++) { + RadioState *rstate = &priv->radio_states[i]; + gboolean enabled = TRUE, hw_enabled = TRUE; + + if (!rstate->desc) + continue; + + switch (nm_udev_manager_get_rfkill_state (priv->udev_mgr, i)) { + case RFKILL_UNBLOCKED: + enabled = TRUE; + hw_enabled = TRUE; + break; + case RFKILL_SOFT_BLOCKED: + enabled = FALSE; + hw_enabled = TRUE; + break; + case RFKILL_HARD_BLOCKED: + enabled = FALSE; + hw_enabled = FALSE; + break; + default: + break; + } + + rstate->hw_enabled = hw_enabled; + nm_log_info (LOGD_RFKILL, "%s %s by radio killswitch; %s by state file", + rstate->desc, + (rstate->hw_enabled && enabled) ? "enabled" : "disabled", + (rstate->enabled) ? "enabled" : "disabled"); + manager_set_radio_enabled (self, rstate, rstate->enabled && enabled); + } + + /* Log overall networking status - enabled/disabled */ + nm_log_info (LOGD_CORE, "Networking is %s by state file", + priv->net_enabled ? "enabled" : "disabled"); + + system_unmanaged_devices_changed_cb (priv->sys_settings, NULL, self); + system_hostname_changed_cb (priv->sys_settings, NULL, self); + system_query_connections (self); + + /* Get user connections if the user settings service is around, otherwise + * they will be queried when the user settings service shows up on the + * bus in nm_manager_name_owner_changed(). + */ + if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, NM_DBUS_SERVICE_USER_SETTINGS)) + user_query_connections (self); + + nm_udev_manager_query_devices (priv->udev_mgr); + bluez_manager_resync_devices (self); +} + +NMManager * +nm_manager_get (const char *config_file, + const char *plugins, + const char *state_file, + gboolean initial_net_enabled, + gboolean initial_wifi_enabled, + gboolean initial_wwan_enabled, + GError **error) +{ + static NMManager *singleton = NULL; + NMManagerPrivate *priv; + DBusGConnection *bus; + + if (singleton) + return g_object_ref (singleton); + + singleton = (NMManager *) g_object_new (NM_TYPE_MANAGER, NULL); + g_assert (singleton); + + priv = NM_MANAGER_GET_PRIVATE (singleton); + + bus = nm_dbus_manager_get_connection (priv->dbus_mgr); + g_assert (bus); + + priv->sys_settings = nm_sysconfig_settings_new (config_file, plugins, bus, error); + if (!priv->sys_settings) { + g_object_unref (singleton); + return NULL; + } + nm_settings_service_export (NM_SETTINGS_SERVICE (priv->sys_settings)); + + priv->config_file = g_strdup (config_file); + + priv->state_file = g_strdup (state_file); + + priv->net_enabled = initial_net_enabled; + + priv->radio_states[RFKILL_TYPE_WLAN].enabled = initial_wifi_enabled; + priv->radio_states[RFKILL_TYPE_WWAN].enabled = initial_wwan_enabled; + + g_signal_connect (priv->sys_settings, "notify::" NM_SYSCONFIG_SETTINGS_UNMANAGED_SPECS, + G_CALLBACK (system_unmanaged_devices_changed_cb), singleton); + g_signal_connect (priv->sys_settings, "notify::" NM_SETTINGS_SYSTEM_INTERFACE_HOSTNAME, + G_CALLBACK (system_hostname_changed_cb), singleton); + g_signal_connect (priv->sys_settings, "new-connection", + G_CALLBACK (system_new_connection_cb), singleton); + + dbus_g_connection_register_g_object (nm_dbus_manager_get_connection (priv->dbus_mgr), + NM_DBUS_PATH, + G_OBJECT (singleton)); + + g_signal_connect (priv->dbus_mgr, + "name-owner-changed", + G_CALLBACK (nm_manager_name_owner_changed), + singleton); + + priv->udev_mgr = nm_udev_manager_new (); + g_signal_connect (priv->udev_mgr, + "device-added", + G_CALLBACK (udev_device_added_cb), + singleton); + g_signal_connect (priv->udev_mgr, + "device-removed", + G_CALLBACK (udev_device_removed_cb), + singleton); + g_signal_connect (priv->udev_mgr, + "rfkill-changed", + G_CALLBACK (udev_manager_rfkill_changed_cb), + singleton); + + priv->bluez_mgr = nm_bluez_manager_get (); + + g_signal_connect (priv->bluez_mgr, + "bdaddr-added", + G_CALLBACK (bluez_manager_bdaddr_added_cb), + singleton); + + g_signal_connect (priv->bluez_mgr, + "bdaddr-removed", + G_CALLBACK (bluez_manager_bdaddr_removed_cb), + singleton); + + return singleton; +} + +static void +dispose (GObject *object) +{ + NMManager *manager = NM_MANAGER (object); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + + if (priv->disposed) { + G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object); + return; + } + priv->disposed = TRUE; + + pending_connection_info_destroy (priv->pending_connection_info); + priv->pending_connection_info = NULL; + + while (g_slist_length (priv->secrets_calls)) + free_get_secrets_info ((GetSecretsInfo *) priv->secrets_calls->data); + + while (g_slist_length (priv->devices)) { + priv->devices = remove_one_device (manager, + priv->devices, + NM_DEVICE (priv->devices->data), + TRUE, + FALSE); + } + + user_destroy_connections (manager); + g_hash_table_destroy (priv->user_connections); + priv->user_connections = NULL; + + g_hash_table_foreach (priv->system_connections, emit_removed, manager); + g_hash_table_remove_all (priv->system_connections); + g_hash_table_destroy (priv->system_connections); + priv->system_connections = NULL; + + g_free (priv->hostname); + g_free (priv->config_file); + + if (priv->sys_settings) { + g_object_unref (priv->sys_settings); + priv->sys_settings = NULL; + } + + if (priv->vpn_manager_id) { + g_source_remove (priv->vpn_manager_id); + priv->vpn_manager_id = 0; + } + g_object_unref (priv->vpn_manager); + + if (priv->modem_added_id) { + g_source_remove (priv->modem_added_id); + priv->modem_added_id = 0; + } + if (priv->modem_removed_id) { + g_source_remove (priv->modem_removed_id); + priv->modem_removed_id = 0; + } + g_object_unref (priv->modem_manager); + + g_object_unref (priv->dbus_mgr); + if (priv->bluez_mgr) + g_object_unref (priv->bluez_mgr); + + G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMManager *self = NM_MANAGER (object); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + + switch (prop_id) { + case PROP_NETWORKING_ENABLED: + /* Construct only for now */ + priv->net_enabled = g_value_get_boolean (value); + break; + case PROP_WIRELESS_ENABLED: + manager_set_radio_enabled (NM_MANAGER (object), + &priv->radio_states[RFKILL_TYPE_WLAN], + g_value_get_boolean (value)); + break; + case PROP_WWAN_ENABLED: + manager_set_radio_enabled (NM_MANAGER (object), + &priv->radio_states[RFKILL_TYPE_WWAN], + g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMManager *self = NM_MANAGER (object); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + + switch (prop_id) { + case PROP_STATE: + nm_manager_update_state (self); + g_value_set_uint (value, priv->state); + break; + case PROP_NETWORKING_ENABLED: + g_value_set_boolean (value, priv->net_enabled); + break; + case PROP_WIRELESS_ENABLED: + g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WLAN].enabled); + break; + case PROP_WIRELESS_HARDWARE_ENABLED: + g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WLAN].hw_enabled); + break; + case PROP_WWAN_ENABLED: + g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WWAN].enabled); + break; + case PROP_WWAN_HARDWARE_ENABLED: + g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WWAN].hw_enabled); + break; + case PROP_ACTIVE_CONNECTIONS: + g_value_take_boxed (value, get_active_connections (self, NULL)); + break; + case PROP_HOSTNAME: + g_value_set_string (value, priv->hostname); + break; + case PROP_SLEEPING: + g_value_set_boolean (value, priv->sleeping); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_manager_init (NMManager *manager) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + DBusGConnection *g_connection; + guint id, i; + + /* Initialize rfkill structures and states */ + memset (priv->radio_states, 0, sizeof (priv->radio_states)); + + priv->radio_states[RFKILL_TYPE_WLAN].enabled = TRUE; + priv->radio_states[RFKILL_TYPE_WLAN].key = "WirelessEnabled"; + priv->radio_states[RFKILL_TYPE_WLAN].prop = NM_MANAGER_WIRELESS_ENABLED; + priv->radio_states[RFKILL_TYPE_WLAN].hw_prop = NM_MANAGER_WIRELESS_HARDWARE_ENABLED; + priv->radio_states[RFKILL_TYPE_WLAN].desc = "WiFi"; + priv->radio_states[RFKILL_TYPE_WLAN].other_enabled_func = nm_manager_get_ipw_rfkill_state; + priv->radio_states[RFKILL_TYPE_WLAN].rtype = RFKILL_TYPE_WLAN; + + priv->radio_states[RFKILL_TYPE_WWAN].enabled = TRUE; + priv->radio_states[RFKILL_TYPE_WWAN].key = "WWANEnabled"; + priv->radio_states[RFKILL_TYPE_WWAN].prop = NM_MANAGER_WWAN_ENABLED; + priv->radio_states[RFKILL_TYPE_WWAN].hw_prop = NM_MANAGER_WWAN_HARDWARE_ENABLED; + priv->radio_states[RFKILL_TYPE_WWAN].desc = "WWAN"; + priv->radio_states[RFKILL_TYPE_WWAN].other_enabled_func = nm_manager_get_modem_enabled_state; + priv->radio_states[RFKILL_TYPE_WWAN].rtype = RFKILL_TYPE_WWAN; + + priv->radio_states[RFKILL_TYPE_WIMAX].enabled = TRUE; + priv->radio_states[RFKILL_TYPE_WIMAX].key = "WiMAXEnabled"; + priv->radio_states[RFKILL_TYPE_WIMAX].prop = NULL; + priv->radio_states[RFKILL_TYPE_WIMAX].hw_prop = NULL; + priv->radio_states[RFKILL_TYPE_WIMAX].desc = "WiMAX"; + priv->radio_states[RFKILL_TYPE_WIMAX].other_enabled_func = NULL; + priv->radio_states[RFKILL_TYPE_WIMAX].rtype = RFKILL_TYPE_WIMAX; + + for (i = 0; i < RFKILL_TYPE_MAX; i++) + priv->radio_states[i].hw_enabled = TRUE; + + priv->sleeping = FALSE; + priv->state = NM_STATE_DISCONNECTED; + + priv->dbus_mgr = nm_dbus_manager_get (); + + priv->user_connections = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); + + priv->system_connections = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); + + priv->modem_manager = nm_modem_manager_get (); + priv->modem_added_id = g_signal_connect (priv->modem_manager, "modem-added", + G_CALLBACK (modem_added), manager); + priv->modem_removed_id = g_signal_connect (priv->modem_manager, "modem-removed", + G_CALLBACK (modem_removed), manager); + + priv->vpn_manager = nm_vpn_manager_get (); + id = g_signal_connect (G_OBJECT (priv->vpn_manager), "connection-deactivated", + G_CALLBACK (vpn_manager_connection_deactivated_cb), manager); + priv->vpn_manager_id = id; + + g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr); + + /* avahi-autoipd stuff */ + priv->aipd_proxy = dbus_g_proxy_new_for_name (g_connection, + NM_AUTOIP_DBUS_SERVICE, + "/", + NM_AUTOIP_DBUS_IFACE); + if (priv->aipd_proxy) { + dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_STRING_STRING, + G_TYPE_NONE, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_INVALID); + + dbus_g_proxy_add_signal (priv->aipd_proxy, + "Event", + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_INVALID); + + dbus_g_proxy_connect_signal (priv->aipd_proxy, "Event", + G_CALLBACK (aipd_handle_event), + manager, + NULL); + } else + nm_log_warn (LOGD_AUTOIP4, "could not initialize avahi-autoipd D-Bus proxy"); +} + +static void +nm_manager_class_init (NMManagerClass *manager_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (manager_class); + + g_type_class_add_private (manager_class, sizeof (NMManagerPrivate)); + + /* virtual methods */ + manager_class->connection_added = connection_added_default_handler; + + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->dispose = dispose; + + /* properties */ + g_object_class_install_property + (object_class, PROP_STATE, + g_param_spec_uint (NM_MANAGER_STATE, + "State", + "Current state", + 0, NM_STATE_DISCONNECTED, 0, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_NETWORKING_ENABLED, + g_param_spec_boolean (NM_MANAGER_NETWORKING_ENABLED, + "NetworkingEnabled", + "Is networking enabled", + TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_WIRELESS_ENABLED, + g_param_spec_boolean (NM_MANAGER_WIRELESS_ENABLED, + "WirelessEnabled", + "Is wireless enabled", + TRUE, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_WIRELESS_HARDWARE_ENABLED, + g_param_spec_boolean (NM_MANAGER_WIRELESS_HARDWARE_ENABLED, + "WirelessHardwareEnabled", + "RF kill state", + TRUE, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_WWAN_ENABLED, + g_param_spec_boolean (NM_MANAGER_WWAN_ENABLED, + "WwanEnabled", + "Is mobile broadband enabled", + TRUE, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_WWAN_HARDWARE_ENABLED, + g_param_spec_boolean (NM_MANAGER_WWAN_HARDWARE_ENABLED, + "WwanHardwareEnabled", + "Whether WWAN is disabled by a hardware switch or not", + TRUE, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_ACTIVE_CONNECTIONS, + g_param_spec_boxed (NM_MANAGER_ACTIVE_CONNECTIONS, + "Active connections", + "Active connections", + DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, + G_PARAM_READABLE)); + + /* Hostname is not exported over D-Bus */ + g_object_class_install_property + (object_class, PROP_HOSTNAME, + g_param_spec_string (NM_MANAGER_HOSTNAME, + "Hostname", + "Hostname", + NULL, + G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT)); + + /* Sleeping is not exported over D-Bus */ + g_object_class_install_property + (object_class, PROP_SLEEPING, + g_param_spec_boolean (NM_MANAGER_SLEEPING, + "Sleeping", + "Sleeping", + FALSE, + G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT)); + + /* signals */ + signals[DEVICE_ADDED] = + g_signal_new ("device-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMManagerClass, device_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + + signals[DEVICE_REMOVED] = + g_signal_new ("device-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMManagerClass, device_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + + signals[STATE_CHANGED] = + g_signal_new ("state-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMManagerClass, state_changed), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + + signals[PROPERTIES_CHANGED] = + nm_properties_changed_signal_new (object_class, + G_STRUCT_OFFSET (NMManagerClass, properties_changed)); + + signals[CONNECTIONS_ADDED] = + g_signal_new ("connections-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMManagerClass, connections_added), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + + signals[CONNECTION_ADDED] = + g_signal_new ("connection-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMManagerClass, connection_added), + NULL, NULL, + _nm_marshal_VOID__OBJECT_UINT, + G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_UINT); + + signals[CONNECTION_UPDATED] = + g_signal_new ("connection-updated", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMManagerClass, connection_updated), + NULL, NULL, + _nm_marshal_VOID__OBJECT_UINT, + G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_UINT); + + signals[CONNECTION_REMOVED] = + g_signal_new ("connection-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMManagerClass, connection_removed), + NULL, NULL, + _nm_marshal_VOID__OBJECT_UINT, + G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_UINT); + + /* StateChange is DEPRECATED */ + signals[STATE_CHANGE] = + g_signal_new ("state-change", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (manager_class), + &dbus_glib_nm_manager_object_info); + + dbus_g_error_domain_register (NM_MANAGER_ERROR, NULL, NM_TYPE_MANAGER_ERROR); + dbus_g_error_domain_register (NM_LOGGING_ERROR, "org.freedesktop.NetworkManager.Logging", NM_TYPE_LOGGING_ERROR); +} + diff --git a/src/nm-manager.h b/src/nm-manager.h new file mode 100644 index 00000000..0f4d72f2 --- /dev/null +++ b/src/nm-manager.h @@ -0,0 +1,121 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2010 Red Hat, Inc. + */ + +#ifndef NM_MANAGER_H +#define NM_MANAGER_H 1 + +#include <glib.h> +#include <glib-object.h> +#include <dbus/dbus-glib.h> +#include "nm-device.h" +#include "nm-device-interface.h" + +#define NM_TYPE_MANAGER (nm_manager_get_type ()) +#define NM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MANAGER, NMManager)) +#define NM_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MANAGER, NMManagerClass)) +#define NM_IS_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MANAGER)) +#define NM_IS_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_MANAGER)) +#define NM_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MANAGER, NMManagerClass)) + +#define NM_MANAGER_STATE "state" +#define NM_MANAGER_NETWORKING_ENABLED "networking-enabled" +#define NM_MANAGER_WIRELESS_ENABLED "wireless-enabled" +#define NM_MANAGER_WIRELESS_HARDWARE_ENABLED "wireless-hardware-enabled" +#define NM_MANAGER_WWAN_ENABLED "wwan-enabled" +#define NM_MANAGER_WWAN_HARDWARE_ENABLED "wwan-hardware-enabled" +#define NM_MANAGER_ACTIVE_CONNECTIONS "active-connections" + +/* Not exported */ +#define NM_MANAGER_HOSTNAME "hostname" +#define NM_MANAGER_SLEEPING "sleeping" + +typedef struct { + GObject parent; +} NMManager; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*device_added) (NMManager *manager, NMDevice *device); + void (*device_removed) (NMManager *manager, NMDevice *device); + void (*state_changed) (NMManager *manager, guint state); + void (*properties_changed) (NMManager *manager, GHashTable *properties); + + void (*connections_added) (NMManager *manager, NMConnectionScope scope); + + void (*connection_added) (NMManager *manager, + NMConnection *connection, + NMConnectionScope scope); + + void (*connection_updated) (NMManager *manager, + NMConnection *connection, + NMConnectionScope scope); + + void (*connection_removed) (NMManager *manager, + NMConnection *connection, + NMConnectionScope scope); +} NMManagerClass; + +GType nm_manager_get_type (void); + +NMManager *nm_manager_get (const char *config_file, + const char *plugins, + const char *state_file, + gboolean initial_net_enabled, + gboolean initial_wifi_enabled, + gboolean initial_wwan_enabled, + GError **error); + +void nm_manager_start (NMManager *manager); + +/* Device handling */ + +GSList *nm_manager_get_devices (NMManager *manager); + +const char * nm_manager_activate_connection (NMManager *manager, + NMConnection *connection, + const char *specific_object, + const char *device_path, + gboolean user_requested, + GError **error); + +gboolean nm_manager_deactivate_connection (NMManager *manager, + const char *connection_path, + NMDeviceStateReason reason, + GError **error); + +/* State handling */ + +NMState nm_manager_get_state (NMManager *manager); + +/* Connections */ + +GSList *nm_manager_get_connections (NMManager *manager, NMConnectionScope scope); + +NMConnection * nm_manager_get_connection_by_object_path (NMManager *manager, + NMConnectionScope scope, + const char *path); + +GPtrArray * nm_manager_get_active_connections_by_connection (NMManager *manager, + NMConnection *connection); + +#endif /* NM_MANAGER_H */ diff --git a/src/nm-netlink-monitor.c b/src/nm-netlink-monitor.c new file mode 100644 index 00000000..13b3ab98 --- /dev/null +++ b/src/nm-netlink-monitor.c @@ -0,0 +1,866 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + * Copyright (C) 2005 - 2008 Novell, Inc. + * Copyright (C) 2005 Ray Strode + * + * Some code borrowed from HAL: + * + * Copyright (C) 2003 David Zeuthen, <david@fubar.dk> + * Copyright (C) 2004 Novell, Inc. + */ + +/* for struct ucred and LIBNL_NEEDS_ADDR_CACHING_WORKAROUND */ +#include <config.h> + +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <linux/types.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <linux/if.h> +#include <linux/unistd.h> +#include <unistd.h> +#include <netlink/object-api.h> +#include <netlink/route/addr.h> +#include <netlink/route/rtnl.h> + +#include <glib.h> +#include <glib/gi18n.h> + +#include "nm-netlink-monitor.h" +#include "nm-logging.h" + +#define EVENT_CONDITIONS ((GIOCondition) (G_IO_IN | G_IO_PRI)) +#define ERROR_CONDITIONS ((GIOCondition) (G_IO_ERR | G_IO_NVAL)) +#define DISCONNECT_CONDITIONS ((GIOCondition) (G_IO_HUP)) + +#define NM_NETLINK_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + NM_TYPE_NETLINK_MONITOR, \ + NMNetlinkMonitorPrivate)) + +typedef struct { + /* Async event listener connection */ + struct nl_handle *nlh_event; + GIOChannel * io_channel; + guint event_id; + + /* Sync/blocking request/response connection */ + struct nl_handle *nlh_sync; + struct nl_cache * link_cache; + + guint request_status_id; + + GHashTable *subscriptions; +} NMNetlinkMonitorPrivate; + +enum { + NOTIFICATION = 0, + CARRIER_ON, + CARRIER_OFF, + ERROR, + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + + +G_DEFINE_TYPE (NMNetlinkMonitor, nm_netlink_monitor, G_TYPE_OBJECT); + +static void +link_msg_handler (struct nl_object *obj, void *arg) +{ + NMNetlinkMonitor *self = NM_NETLINK_MONITOR (arg); + GError *error; + struct rtnl_link *filter; + struct rtnl_link *link_obj; + guint flags; + guint ifidx; + + filter = rtnl_link_alloc (); + if (!filter) { + error = g_error_new (NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_BAD_ALLOC, + _("error processing netlink message: %s"), + nl_geterror ()); + g_signal_emit (self, signals[ERROR], 0, error); + g_error_free (error); + return; + } + + /* Ensure it's a link object */ + if (nl_object_match_filter (obj, OBJ_CAST (filter)) == 0) { + rtnl_link_put (filter); + return; + } + + link_obj = (struct rtnl_link *) obj; + flags = rtnl_link_get_flags (link_obj); + ifidx = rtnl_link_get_ifindex (link_obj); + + nm_log_dbg (LOGD_HW, "netlink link message: iface idx %d flags 0x%X", ifidx, flags); + + /* IFF_LOWER_UP is the indicator of carrier status since kernel commit + * b00055aacdb172c05067612278ba27265fcd05ce in 2.6.17. + */ + if (flags & IFF_LOWER_UP) + g_signal_emit (self, signals[CARRIER_ON], 0, ifidx); + else + g_signal_emit (self, signals[CARRIER_OFF], 0, ifidx); + + rtnl_link_put (filter); +} + +static int +event_msg_recv (struct nl_msg *msg, void *arg) +{ + struct nl_handle *nlh = arg; + struct nlmsghdr *hdr = nlmsg_hdr (msg); + struct ucred *creds = nlmsg_get_creds (msg); + const struct sockaddr_nl *snl; + guint32 local_port; + gboolean accept_msg = FALSE; + + /* Only messages sent from the kernel */ + if (!creds || creds->uid != 0) { + nm_log_dbg (LOGD_HW, "ignoring netlink message from UID %d", + creds ? creds->uid : -1); + return NL_SKIP; + } + + snl = nlmsg_get_src (msg); + g_assert (snl); + + /* Accept any messages from the kernel */ + if (hdr->nlmsg_pid == 0 || snl->nl_pid == 0) + accept_msg = TRUE; + + /* And any multicast message directed to our netlink PID, since multicast + * currently requires CAP_ADMIN to use. + */ + local_port = nl_socket_get_local_port (nlh); + if ((hdr->nlmsg_pid == local_port) && snl->nl_groups) + accept_msg = TRUE; + + if (accept_msg == FALSE) { + nm_log_dbg (LOGD_HW, "ignoring netlink message from PID %d (local PID %d, multicast %d)", + hdr->nlmsg_pid, + local_port, + (hdr->nlmsg_flags & NLM_F_MULTI)); + return NL_SKIP; + } + + return NL_OK; +} + +static int +event_msg_ready (struct nl_msg *msg, void *arg) +{ + NMNetlinkMonitor *self = NM_NETLINK_MONITOR (arg); + + /* By the time the message gets here we've already checked the sender + * and we're sure it's safe to parse this message. + */ + + /* Let clients handle generic messages */ + g_signal_emit (self, signals[NOTIFICATION], 0, msg); + + /* Parse carrier messages */ + nl_msg_parse (msg, &link_msg_handler, self); + + return NL_OK; +} + +static gboolean +event_handler (GIOChannel *channel, + GIOCondition io_condition, + gpointer user_data) +{ + NMNetlinkMonitor *self = (NMNetlinkMonitor *) user_data; + NMNetlinkMonitorPrivate *priv; + GError *error = NULL; + + g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), TRUE); + + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + g_return_val_if_fail (priv->event_id > 0, TRUE); + + if (io_condition & ERROR_CONDITIONS) { + const char *err_msg; + int err_code = 0; + socklen_t err_len = sizeof (err_code); + + /* Grab error information */ + if (getsockopt (g_io_channel_unix_get_fd (channel), + SOL_SOCKET, SO_ERROR, (void *) &err_code, &err_len)) + err_msg = strerror (err_code); + else + err_msg = _("error occurred while waiting for data on socket"); + + error = g_error_new (NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_WAITING_FOR_SOCKET_DATA, + "%s", err_msg); + g_signal_emit (self, signals[ERROR], 0, error); + g_error_free (error); + return TRUE; + } else if (io_condition & DISCONNECT_CONDITIONS) + return FALSE; + + g_return_val_if_fail (!(io_condition & ~EVENT_CONDITIONS), FALSE); + + /* Process the netlink messages */ + if (nl_recvmsgs_default (priv->nlh_event) < 0) { + error = g_error_new (NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_PROCESSING_MESSAGE, + _("error processing netlink message: %s"), + nl_geterror ()); + g_signal_emit (self, signals[ERROR], 0, error); + g_error_free (error); + } + + return TRUE; +} + +static gboolean +nlh_setup (struct nl_handle *nlh, + nl_recvmsg_msg_cb_t valid_func, + gpointer cb_data, + GError **error) +{ + nl_socket_modify_cb (nlh, NL_CB_MSG_IN, NL_CB_CUSTOM, event_msg_recv, cb_data); + + if (valid_func) + nl_socket_modify_cb (nlh, NL_CB_VALID, NL_CB_CUSTOM, valid_func, cb_data); + + if (nl_connect (nlh, NETLINK_ROUTE) < 0) { + g_set_error (error, NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_NETLINK_CONNECT, + _("unable to connect to netlink for monitoring link status: %s"), + nl_geterror ()); + return FALSE; + } + + /* Enable unix socket peer credentials which we use for verifying that the + * sender of the message is actually the kernel. + */ + if (nl_set_passcred (nlh, 1) < 0) { + g_set_error (error, NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_NETLINK_CONNECT, + _("unable to enable netlink handle credential passing: %s"), + nl_geterror ()); + return FALSE; + } + + return TRUE; +} + +static gboolean +event_connection_setup (NMNetlinkMonitor *self, GError **error) +{ + NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + GError *channel_error = NULL; + GIOFlags channel_flags; + struct nl_cb *cb; + int fd; + + g_return_val_if_fail (priv->io_channel == NULL, FALSE); + + /* Set up the event listener connection */ + cb = nl_cb_alloc (NL_CB_DEFAULT); + priv->nlh_event = nl_handle_alloc_cb (cb); + nl_cb_put (cb); + if (!priv->nlh_event) { + g_set_error (error, NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_HANDLE, + _("unable to allocate netlink handle for monitoring link status: %s"), + nl_geterror ()); + goto error; + } + + if (!nlh_setup (priv->nlh_event, event_msg_ready, self, error)) + goto error; + + nl_disable_sequence_check (priv->nlh_event); + + /* Subscribe to the LINK group for internal carrier signals */ + if (!nm_netlink_monitor_subscribe (self, RTNLGRP_LINK, error)) + goto error; + + fd = nl_socket_get_fd (priv->nlh_event); + priv->io_channel = g_io_channel_unix_new (fd); + + g_io_channel_set_encoding (priv->io_channel, NULL, &channel_error); + /* Encoding is NULL, so no conversion error can possibly occur */ + g_assert (channel_error == NULL); + + g_io_channel_set_close_on_unref (priv->io_channel, TRUE); + channel_flags = g_io_channel_get_flags (priv->io_channel); + channel_error = NULL; + g_io_channel_set_flags (priv->io_channel, + channel_flags | G_IO_FLAG_NONBLOCK, + &channel_error); + if (channel_error != NULL) { + g_propagate_error (error, channel_error); + goto error; + } + + return TRUE; + +error: + if (priv->io_channel) + nm_netlink_monitor_close_connection (self); + + if (priv->nlh_event) { + nl_handle_destroy (priv->nlh_event); + priv->nlh_event = NULL; + } + + return FALSE; +} + +static gboolean +sync_connection_setup (NMNetlinkMonitor *self, GError **error) +{ + NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + struct nl_cb *cb; +#ifdef LIBNL_NEEDS_ADDR_CACHING_WORKAROUND + struct nl_cache *addr_cache; +#endif + + /* Set up the event listener connection */ + cb = nl_cb_alloc (NL_CB_DEFAULT); + priv->nlh_sync = nl_handle_alloc_cb (cb); + nl_cb_put (cb); + if (!priv->nlh_sync) { + g_set_error (error, NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_HANDLE, + _("unable to allocate netlink handle for monitoring link status: %s"), + nl_geterror ()); + goto error; + } + + if (!nlh_setup (priv->nlh_sync, NULL, self, error)) + goto error; + +#ifdef LIBNL_NEEDS_ADDR_CACHING_WORKAROUND + /* Work around apparent libnl bug; rtnl_addr requires that all + * addresses have the "peer" attribute set in order to be compared + * for equality, but this attribute is not normally set. As a + * result, most addresses will not compare as equal even to + * themselves, busting caching. + */ + addr_cache = rtnl_addr_alloc_cache (priv->nlh_sync); + nl_cache_get_ops (addr_cache)->co_obj_ops->oo_id_attrs &= ~0x80; + nl_cache_free (addr_cache); +#endif + + if ((priv->link_cache = rtnl_link_alloc_cache (priv->nlh_sync)) == NULL) { + g_set_error (error, NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_LINK_CACHE, + _("unable to allocate netlink link cache for monitoring link status: %s"), + nl_geterror ()); + goto error; + } + nl_cache_mngt_provide (priv->link_cache); + + return TRUE; + +error: + if (priv->link_cache) { + nl_cache_free (priv->link_cache); + priv->link_cache = NULL; + } + + if (priv->nlh_sync) { + nl_handle_destroy (priv->nlh_sync); + priv->nlh_sync = NULL; + } + + return FALSE; +} + +gboolean +nm_netlink_monitor_open_connection (NMNetlinkMonitor *self, GError **error) +{ + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE); + + if (!event_connection_setup (self, error)) + return FALSE; + + if (!sync_connection_setup (self, error)) + return FALSE; + + return TRUE; +} + +void +nm_netlink_monitor_close_connection (NMNetlinkMonitor *self) +{ + NMNetlinkMonitorPrivate *priv; + + g_return_if_fail (NM_IS_NETLINK_MONITOR (self)); + + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + g_return_if_fail (priv->io_channel != NULL); + + if (priv->event_id) + nm_netlink_monitor_detach (self); + + g_io_channel_shutdown (priv->io_channel, + TRUE /* flush pending data */, + NULL); + g_io_channel_unref (priv->io_channel); + priv->io_channel = NULL; +} + +void +nm_netlink_monitor_attach (NMNetlinkMonitor *self) +{ + NMNetlinkMonitorPrivate *priv; + + g_return_if_fail (NM_IS_NETLINK_MONITOR (self)); + + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + g_return_if_fail (priv->nlh_event != NULL); + g_return_if_fail (priv->event_id == 0); + + priv->event_id = g_io_add_watch (priv->io_channel, + (EVENT_CONDITIONS | ERROR_CONDITIONS | DISCONNECT_CONDITIONS), + event_handler, self); +} + +void +nm_netlink_monitor_detach (NMNetlinkMonitor *self) +{ + NMNetlinkMonitorPrivate *priv; + + g_return_if_fail (NM_IS_NETLINK_MONITOR (self)); + + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + g_return_if_fail (priv->event_id > 0); + + g_source_remove (priv->event_id); + priv->event_id = 0; +} + +static int +get_subs (NMNetlinkMonitor *self, int group) +{ + NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + return GPOINTER_TO_INT (g_hash_table_lookup (priv->subscriptions, + GINT_TO_POINTER (group))); +} + +static void +set_subs (NMNetlinkMonitor *self, int group, int new_subs) +{ + NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + g_hash_table_insert (priv->subscriptions, + GINT_TO_POINTER (group), + GINT_TO_POINTER (new_subs)); +} + +gboolean +nm_netlink_monitor_subscribe (NMNetlinkMonitor *self, int group, GError **error) +{ + NMNetlinkMonitorPrivate *priv; + int subs; + + g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE); + + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + if (!priv->nlh_event) { + if (!nm_netlink_monitor_open_connection (self, error)) + return FALSE; + } + + subs = get_subs (self, group) + 1; + if (subs == 1) { + if (nl_socket_add_membership (priv->nlh_event, group) < 0) { + g_set_error (error, NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_NETLINK_JOIN_GROUP, + _("unable to join netlink group: %s"), + nl_geterror ()); + return FALSE; + } + } + + /* Update # of subscriptions for this group */ + set_subs (self, group, subs); + + return TRUE; +} + +void +nm_netlink_monitor_unsubscribe (NMNetlinkMonitor *self, int group) +{ + NMNetlinkMonitorPrivate *priv; + int subs; + + g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_NETLINK_MONITOR (self)); + + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + g_return_if_fail (priv->nlh_event != NULL); + + subs = get_subs (self, group) - 1; + if (subs == 0) + nl_socket_drop_membership (priv->nlh_event, group); + + /* Update # of subscriptions for this group */ + set_subs (self, group, subs); +} + +/***************************************************************/ + +gboolean +nm_netlink_monitor_request_ip6_info (NMNetlinkMonitor *self, GError **error) +{ + NMNetlinkMonitorPrivate *priv; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE); + + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + /* FIXME: nl_rtgen_request() gets the return value screwed up with + * libnl-1.1; revisit this and return a proper error when we port to + * a later libnl. + */ + nl_rtgen_request (priv->nlh_event, RTM_GETLINK, AF_INET6, NLM_F_DUMP); + + return TRUE; +} + + +static gboolean +deferred_emit_carrier_state (gpointer user_data) +{ + NMNetlinkMonitor *self = NM_NETLINK_MONITOR (user_data); + NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + priv->request_status_id = 0; + + /* Update the link cache with latest state, and if there are no errors + * emit the link states for all the interfaces in the cache. + */ + if (nl_cache_refill (priv->nlh_sync, priv->link_cache)) { + nm_log_err (LOGD_HW, "error updating link cache: %s", nl_geterror ()); + } else + nl_cache_foreach_filter (priv->link_cache, NULL, link_msg_handler, self); + + return FALSE; +} + +gboolean +nm_netlink_monitor_request_status (NMNetlinkMonitor *self, GError **error) +{ + NMNetlinkMonitorPrivate *priv; + + g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE); + + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + /* Schedule the carrier state emission */ + if (!priv->request_status_id) + priv->request_status_id = g_idle_add (deferred_emit_carrier_state, self); + + return TRUE; +} + +typedef struct { + NMNetlinkMonitor *self; + struct rtnl_link *filter; + GError *error; + guint32 flags; +} GetFlagsInfo; + +static void +get_flags_sync_cb (struct nl_object *obj, void *arg) +{ + GetFlagsInfo *info = arg; + + /* Ensure this cache item matches our filter */ + if (nl_object_match_filter (obj, OBJ_CAST (info->filter)) != 0) + info->flags = rtnl_link_get_flags ((struct rtnl_link *) obj); +} + +gboolean +nm_netlink_monitor_get_flags_sync (NMNetlinkMonitor *self, + guint32 ifindex, + guint32 *ifflags, + GError **error) +{ + NMNetlinkMonitorPrivate *priv; + GetFlagsInfo info; + struct rtnl_link *filter; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE); + g_return_val_if_fail (ifflags != NULL, FALSE); + + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + /* Update the link cache with the latest information */ + if (nl_cache_refill (priv->nlh_sync, priv->link_cache)) { + g_set_error (error, + NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_LINK_CACHE_UPDATE, + _("error updating link cache: %s"), + nl_geterror ()); + return FALSE; + } + + /* HACK: Apparently to get it working we have to refill the cache twice; + * otherwise some kernels (or maybe libnl?) only send a few of the + * interfaces in the refill request. + */ + if (nl_cache_refill (priv->nlh_sync, priv->link_cache)) { + g_set_error (error, + NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_LINK_CACHE_UPDATE, + _("error updating link cache: %s"), + nl_geterror ()); + return FALSE; + } + + /* Set up the filter */ + filter = rtnl_link_alloc (); + if (!filter) { + g_set_error (error, + NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_BAD_ALLOC, + _("error processing netlink message: %s"), + nl_geterror ()); + return FALSE; + } + rtnl_link_set_ifindex (filter, ifindex); + + memset (&info, 0, sizeof (info)); + info.self = self; + info.filter = filter; + info.error = NULL; + nl_cache_foreach_filter (priv->link_cache, NULL, get_flags_sync_cb, &info); + + rtnl_link_put (filter); + + if (info.error) { + if (error) + *error = info.error; + else + g_error_free (info.error); + return FALSE; + } else + *ifflags = info.flags; + + return TRUE; /* success */ +} + +/***************************************************************/ + +struct nl_handle * +nm_netlink_get_default_handle (void) +{ + NMNetlinkMonitor *self; + struct nl_handle *nlh; + + self = nm_netlink_monitor_get (); + nlh = NM_NETLINK_MONITOR_GET_PRIVATE (self)->nlh_sync; + g_object_unref (self); + + return nlh; +} + +int +nm_netlink_iface_to_index (const char *iface) +{ + NMNetlinkMonitor *self; + NMNetlinkMonitorPrivate *priv; + int idx; + + g_return_val_if_fail (iface != NULL, -1); + + self = nm_netlink_monitor_get (); + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + nl_cache_refill (priv->nlh_sync, priv->link_cache); + idx = rtnl_link_name2i (priv->link_cache, iface); + g_object_unref (self); + + return idx; +} + +#define MAX_IFACE_LEN 33 +char * +nm_netlink_index_to_iface (int idx) +{ + NMNetlinkMonitor *self; + NMNetlinkMonitorPrivate *priv; + char *buf = NULL; + + g_return_val_if_fail (idx >= 0, NULL); + + self = nm_netlink_monitor_get (); + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + buf = g_malloc0 (MAX_IFACE_LEN); + g_assert (buf); + + nl_cache_refill (priv->nlh_sync, priv->link_cache); + if (!rtnl_link_i2name (priv->link_cache, idx, buf, MAX_IFACE_LEN - 1)) { + g_free (buf); + buf = NULL; + } + + g_object_unref (self); + return buf; +} + +struct rtnl_link * +nm_netlink_index_to_rtnl_link (int idx) +{ + NMNetlinkMonitor *self; + NMNetlinkMonitorPrivate *priv; + struct rtnl_link *ret = NULL; + + if (idx <= 0) + return NULL; + + self = nm_netlink_monitor_get (); + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + nl_cache_refill (priv->nlh_sync, priv->link_cache); + ret = rtnl_link_get (priv->link_cache, idx); + g_object_unref (self); + + return ret; +} + +/***************************************************************/ + +NMNetlinkMonitor * +nm_netlink_monitor_get (void) +{ + static NMNetlinkMonitor *singleton = NULL; + + if (!singleton) + singleton = NM_NETLINK_MONITOR (g_object_new (NM_TYPE_NETLINK_MONITOR, NULL)); + else + g_object_ref (singleton); + + return singleton; +} + +static void +nm_netlink_monitor_init (NMNetlinkMonitor *self) +{ + NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + priv->subscriptions = g_hash_table_new (g_direct_hash, g_direct_equal); +} + +static void +finalize (GObject *object) +{ + NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (object); + + if (priv->request_status_id) + g_source_remove (priv->request_status_id); + + if (priv->io_channel) + nm_netlink_monitor_close_connection (NM_NETLINK_MONITOR (object)); + + if (priv->link_cache) { + nl_cache_free (priv->link_cache); + priv->link_cache = NULL; + } + + if (priv->nlh_event) { + nl_handle_destroy (priv->nlh_event); + priv->nlh_event = NULL; + } + + if (priv->nlh_sync) { + nl_handle_destroy (priv->nlh_sync); + priv->nlh_sync = NULL; + } + + g_hash_table_destroy (priv->subscriptions); + + G_OBJECT_CLASS (nm_netlink_monitor_parent_class)->finalize (object); +} + +static void +nm_netlink_monitor_class_init (NMNetlinkMonitorClass *monitor_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (monitor_class); + + g_type_class_add_private (monitor_class, sizeof (NMNetlinkMonitorPrivate)); + + /* Virtual methods */ + object_class->finalize = finalize; + + /* Signals */ + signals[NOTIFICATION] = + g_signal_new ("notification", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMNetlinkMonitorClass, notification), + NULL, NULL, g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + signals[CARRIER_ON] = + g_signal_new ("carrier-on", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMNetlinkMonitorClass, carrier_on), + NULL, NULL, g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, G_TYPE_INT); + + signals[CARRIER_OFF] = + g_signal_new ("carrier-off", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMNetlinkMonitorClass, carrier_off), + NULL, NULL, g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, G_TYPE_INT); + + signals[ERROR] = + g_signal_new ("error", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMNetlinkMonitorClass, error), + NULL, NULL, g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); +} + +GQuark +nm_netlink_monitor_error_quark (void) +{ + static GQuark error_quark = 0; + + if (G_UNLIKELY (error_quark == 0)) + error_quark = g_quark_from_static_string ("nm-netlink-monitor-error-quark"); + return error_quark; +} + diff --git a/src/nm-netlink-monitor.h b/src/nm-netlink-monitor.h new file mode 100644 index 00000000..8b57a312 --- /dev/null +++ b/src/nm-netlink-monitor.h @@ -0,0 +1,99 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + * Copyright (C) 2005 - 2008 Novell, Inc. + * Copyright (C) 2005 Ray Strode + */ + +#ifndef NM_NETLINK_MONITOR_H +#define NM_NETLINK_MONITOR_H + +#include <glib.h> +#include <glib-object.h> +#include <netlink/netlink.h> +#include <netlink/route/link.h> + +#define NM_TYPE_NETLINK_MONITOR (nm_netlink_monitor_get_type ()) +#define NM_NETLINK_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_NETLINK_MONITOR, NMNetlinkMonitor)) +#define NM_NETLINK_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_NETLINK_MONITOR, NMNetlinkMonitorClass)) +#define NM_IS_NETLINK_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_NETLINK_MONITOR)) +#define NM_IS_NETLINK_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_NETLINK_MONITOR)) +#define NM_NETLINK_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_NETLINK_MONITOR, NMNetlinkMonitorClass)) + +typedef enum { + NM_NETLINK_MONITOR_ERROR_GENERIC = 0, + NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_HANDLE, + NM_NETLINK_MONITOR_ERROR_NETLINK_CONNECT, + NM_NETLINK_MONITOR_ERROR_NETLINK_JOIN_GROUP, + NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_LINK_CACHE, + NM_NETLINK_MONITOR_ERROR_PROCESSING_MESSAGE, + NM_NETLINK_MONITOR_ERROR_BAD_ALLOC, + NM_NETLINK_MONITOR_ERROR_WAITING_FOR_SOCKET_DATA, + NM_NETLINK_MONITOR_ERROR_LINK_CACHE_UPDATE +} NMNetlinkMonitorError; + +typedef struct { + GObject parent; +} NMNetlinkMonitor; + +typedef struct { + GObjectClass parent_class; + + /* Signals */ + void (*notification) (NMNetlinkMonitor *monitor, struct nl_msg *msg); + void (*carrier_on) (NMNetlinkMonitor *monitor, int index); + void (*carrier_off) (NMNetlinkMonitor *monitor, int index); + void (*error) (NMNetlinkMonitor *monitor, GError *error); +} NMNetlinkMonitorClass; + + +#define NM_NETLINK_MONITOR_ERROR (nm_netlink_monitor_error_quark ()) +GType nm_netlink_monitor_get_type (void) G_GNUC_CONST; +GQuark nm_netlink_monitor_error_quark (void) G_GNUC_CONST; + +NMNetlinkMonitor *nm_netlink_monitor_get (void); + +gboolean nm_netlink_monitor_open_connection (NMNetlinkMonitor *monitor, + GError **error); +void nm_netlink_monitor_close_connection (NMNetlinkMonitor *monitor); +void nm_netlink_monitor_attach (NMNetlinkMonitor *monitor); +void nm_netlink_monitor_detach (NMNetlinkMonitor *monitor); + +gboolean nm_netlink_monitor_subscribe (NMNetlinkMonitor *monitor, + int group, + GError **error); +void nm_netlink_monitor_unsubscribe (NMNetlinkMonitor *monitor, + int group); + +gboolean nm_netlink_monitor_request_ip6_info (NMNetlinkMonitor *monitor, + GError **error); + +gboolean nm_netlink_monitor_request_status (NMNetlinkMonitor *monitor, + GError **error); +gboolean nm_netlink_monitor_get_flags_sync (NMNetlinkMonitor *monitor, + guint32 ifindex, + guint32 *ifflags, + GError **error); + +/* Generic utility functions */ +int nm_netlink_iface_to_index (const char *iface); +char * nm_netlink_index_to_iface (int idx); +struct rtnl_link *nm_netlink_index_to_rtnl_link (int idx); +struct nl_handle *nm_netlink_get_default_handle (void); + +#endif /* NM_NETLINK_MONITOR_H */ diff --git a/src/nm-policy-hostname.c b/src/nm-policy-hostname.c new file mode 100644 index 00000000..a273a920 --- /dev/null +++ b/src/nm-policy-hostname.c @@ -0,0 +1,205 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2004 - 2010 Red Hat, Inc. + * Copyright (C) 2007 - 2008 Novell, Inc. + */ + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <netdb.h> +#include <ctype.h> + +#include <glib.h> + +#include "nm-logging.h" +#include "nm-policy-hostname.h" +#include "nm-policy-hosts.h" + +/************************************************************************/ + +struct HostnameThread { + GThread *thread; + + GMutex *lock; + gboolean dead; + int ret; + + guint32 ip4_addr; + char hostname[NI_MAXHOST + 1]; + + HostnameThreadCallback callback; + gpointer user_data; +}; + +static gboolean +hostname_thread_run_cb (gpointer user_data) +{ + HostnameThread *ht = (HostnameThread *) user_data; + const char *hostname = NULL; + + if (strlen (ht->hostname)) + hostname = ht->hostname; + + (*ht->callback) (ht, ht->ret, hostname, ht->user_data); + return FALSE; +} + +static gpointer +hostname_thread_worker (gpointer data) +{ + HostnameThread *ht = (HostnameThread *) data; + struct sockaddr_in addr; + int i; + + g_mutex_lock (ht->lock); + if (ht->dead) { + g_mutex_unlock (ht->lock); + return (gpointer) NULL; + } + g_mutex_unlock (ht->lock); + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = ht->ip4_addr; + + ht->ret = getnameinfo ((struct sockaddr *) &addr, sizeof (struct sockaddr_in), + ht->hostname, NI_MAXHOST, NULL, 0, + NI_NAMEREQD); + if (ht->ret == 0) { + for (i = 0; i < strlen (ht->hostname); i++) + ht->hostname[i] = tolower (ht->hostname[i]); + } + + /* Don't track the idle handler ID because by the time the g_idle_add() + * returns the ID, the handler may already have run and freed the + * HostnameThread. + */ + g_idle_add (hostname_thread_run_cb, ht); + return (gpointer) TRUE; +} + +void +hostname_thread_free (HostnameThread *ht) +{ + g_return_if_fail (ht != NULL); + + g_mutex_free (ht->lock); + memset (ht, 0, sizeof (HostnameThread)); + g_free (ht); +} + +HostnameThread * +hostname_thread_new (guint32 ip4_addr, HostnameThreadCallback callback, gpointer user_data) +{ + HostnameThread *ht; + + ht = g_malloc0 (sizeof (HostnameThread)); + g_assert (ht); + + ht->lock = g_mutex_new (); + ht->callback = callback; + ht->user_data = user_data; + ht->ip4_addr = ip4_addr; + + ht->thread = g_thread_create (hostname_thread_worker, ht, FALSE, NULL); + if (!ht->thread) { + hostname_thread_free (ht); + ht = NULL; + } + + return ht; +} + +void +hostname_thread_kill (HostnameThread *ht) +{ + g_return_if_fail (ht != NULL); + + g_mutex_lock (ht->lock); + ht->dead = TRUE; + g_mutex_unlock (ht->lock); +} + +gboolean +hostname_thread_is_dead (HostnameThread *ht) +{ + g_return_val_if_fail (ht != NULL, TRUE); + + return ht->dead; +} + +/************************************************************************/ + +#define FALLBACK_HOSTNAME "localhost.localdomain" + +gboolean +nm_policy_set_system_hostname (const char *new_hostname, const char *msg) +{ + char old_hostname[HOST_NAME_MAX + 1]; + int ret = 0; + const char *name; + gboolean set_hostname = TRUE, changed = FALSE; + + if (new_hostname) + g_warn_if_fail (strlen (new_hostname)); + + name = (new_hostname && strlen (new_hostname)) ? new_hostname : FALLBACK_HOSTNAME; + + old_hostname[HOST_NAME_MAX] = '\0'; + errno = 0; + ret = gethostname (old_hostname, HOST_NAME_MAX); + if (ret != 0) { + nm_log_warn (LOGD_DNS, "couldn't get the system hostname: (%d) %s", + errno, strerror (errno)); + } else { + /* Don't set the hostname if it isn't actually changing */ + if ( (new_hostname && !strcmp (old_hostname, new_hostname)) + || (!new_hostname && !strcmp (old_hostname, FALLBACK_HOSTNAME))) + set_hostname = FALSE; + } + + if (set_hostname) { + nm_log_info (LOGD_DNS, "Setting system hostname to '%s' (%s)", name, msg); + ret = sethostname (name, strlen (name)); + if (ret != 0) { + nm_log_warn (LOGD_DNS, "couldn't set the system hostname to '%s': (%d) %s", + name, errno, strerror (errno)); + return FALSE; + } + } + + /* But even if the hostname isn't changing, always try updating /etc/hosts + * just in case the hostname changed while NM wasn't running; we need to + * make sure that /etc/hosts has valid mappings for '127.0.0.1' and the + * current system hostname. If those exist, + * nm_policy_hosts_update_etc_hosts() will just return and won't touch + * /etc/hosts at all. + */ + if (!nm_policy_hosts_update_etc_hosts (name, FALLBACK_HOSTNAME, &changed)) { + /* error updating /etc/hosts; fallback to localhost.localdomain */ + nm_log_info (LOGD_DNS, "Setting system hostname to '" FALLBACK_HOSTNAME "' (error updating /etc/hosts)"); + ret = sethostname (FALLBACK_HOSTNAME, strlen (FALLBACK_HOSTNAME)); + if (ret != 0) { + nm_log_warn (LOGD_DNS, "couldn't set the fallback system hostname (%s): (%d) %s", + FALLBACK_HOSTNAME, errno, strerror (errno)); + } + } + + return changed; +} + diff --git a/src/nm-policy-hostname.h b/src/nm-policy-hostname.h new file mode 100644 index 00000000..c59ca410 --- /dev/null +++ b/src/nm-policy-hostname.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2004 - 2010 Red Hat, Inc. + * Copyright (C) 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_POLICY_HOSTNAME_H +#define NM_POLICY_HOSTNAME_H + +#include <glib.h> + +gboolean nm_policy_set_system_hostname (const char *new_hostname, const char *msg); + + +typedef struct HostnameThread HostnameThread; + +typedef void (*HostnameThreadCallback) (HostnameThread *ht, + int error, + const char *hostname, + gpointer user_data); + +HostnameThread * hostname_thread_new (guint32 ip4_addr, + HostnameThreadCallback callback, + gpointer user_data); + +void hostname_thread_free (HostnameThread *ht); + +gboolean hostname_thread_is_dead (HostnameThread *ht); + +void hostname_thread_kill (HostnameThread *ht); + +#endif /* NM_POLICY_HOSTNAME_H */ diff --git a/src/nm-policy-hosts.c b/src/nm-policy-hosts.c new file mode 100644 index 00000000..7723c997 --- /dev/null +++ b/src/nm-policy-hosts.c @@ -0,0 +1,235 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2004 - 2010 Red Hat, Inc. + */ + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <netdb.h> +#include <ctype.h> + +#include "nm-policy-hosts.h" +#include "nm-logging.h" + +gboolean +nm_policy_hosts_find_token (const char *line, const char *token) +{ + const char *start = line, *p = line; + + g_return_val_if_fail (line != NULL, FALSE); + g_return_val_if_fail (token != NULL, FALSE); + g_return_val_if_fail (strlen (token) > 0, FALSE); + + /* Walk through the line to find the next whitespace character */ + while (p <= line + strlen (line)) { + if (isblank (*p) || (*p == '\0')) { + /* Token starts with 'start' and ends with 'end' */ + if ((p > start) && *start && (p - start == strlen (token)) && !strncmp (start, token, (p - start))) + return TRUE; /* found */ + + /* not found; advance start and continue looking */ + start = p + 1; + } + p++; + } + + return FALSE; +} + +static gboolean +is_local_mapping (const char *str, const char *hostname) +{ + return ( !strncmp (str, "127.0.0.1", strlen ("127.0.0.1")) + && nm_policy_hosts_find_token (str, hostname ? hostname : "localhost")); +} + +GString * +nm_policy_get_etc_hosts (const char **lines, + gsize existing_len, + const char *hostname, + const char *fallback_hostname, + GError **error) +{ + GString *contents = NULL; + const char **line; + gboolean found_host_nonlocal = FALSE; + gboolean found_host = FALSE; + gboolean found_localhost = FALSE; + gboolean initial_comments = TRUE; + gboolean added = FALSE; + + g_return_val_if_fail (lines != NULL, FALSE); + g_return_val_if_fail (hostname != NULL, FALSE); + + /* /etc/hosts needs at least two things: + * + * 1) current hostname mapped to any address + * 2) 'localhost' mapped to 127.0.0.1 + * + * If both these conditions exist in /etc/hosts, we don't need to bother + * updating the file. + */ + + /* Look for the two cases from above */ + for (line = lines; lines && *line; line++) { + if (strlen (*line) && (*line[0] != '#')) { + if (nm_policy_hosts_find_token (*line, hostname)) { + if (!is_local_mapping (*line, "localhost")) { + /* hostname is not on a 127.0.0.1 line or the line does not + * contain 'localhost'. + */ + found_host_nonlocal = TRUE; + } + found_host = TRUE; + } + + if (is_local_mapping (*line, "localhost")) { + /* a 127.0.0.1 line containing 'localhost' */ + found_localhost = TRUE; + } + } + + if (found_localhost && found_host) + return NULL; /* No update required */ + } + + contents = g_string_sized_new (existing_len ? existing_len + 100 : 200); + if (!contents) { + g_set_error_literal (error, 0, 0, "not enough memory"); + return NULL; + } + + /* Construct the new hosts file; replace any 127.0.0.1 entry that is at the + * beginning of the file or right after initial comments and contains + * the string 'localhost'. If there is no 127.0.0.1 entry at the beginning + * or after initial comments that contains 'localhost', add one there + * and ignore any other 127.0.0.1 entries that contain 'localhost'. + */ + for (line = lines, initial_comments = TRUE; lines && *line; line++) { + gboolean add_line = TRUE; + + /* This is the first line after the initial comments */ + if (strlen (*line) && initial_comments && (*line[0] != '#')) { + initial_comments = FALSE; + + /* If some other line contained the hostname but not 'localhost', + * make a simple localhost mapping and assume the user knows what + * they are doing with their manual hostname entry. Otherwise if + * the hostname wasn't found somewhere else, add it to the localhost + * mapping line to make sure it's mapped to something. + */ + if (found_host_nonlocal) + g_string_append (contents, "127.0.0.1"); + else + g_string_append_printf (contents, "127.0.0.1\t%s", hostname); + + if (strcmp (hostname, fallback_hostname)) { + g_string_append_printf (contents, "\t%s", fallback_hostname); + /* Don't add a standalone 'localhost.localdomain' 127 mapping */ + if (is_local_mapping (*line, fallback_hostname)) + add_line = FALSE; + } + + g_string_append (contents, "\tlocalhost\n"); + added = TRUE; + + /* Don't add the original line if it is a 'localhost' mapping */ + if (is_local_mapping (*line, "localhost")) + add_line = FALSE; + } + + if (add_line) { + g_string_append (contents, *line); + /* Only append the new line if this isn't the last line in the file */ + if (*(line+1)) + g_string_append_c (contents, '\n'); + } + } + + /* Hmm, /etc/hosts was empty for some reason */ + if (!added) { + g_string_append (contents, "# Do not remove the following line, or various programs\n"); + g_string_append (contents, "# that require network functionality will fail.\n"); + g_string_append_printf (contents, "127.0.0.1\t%s\tlocalhost\n", fallback_hostname); + } + + return contents; +} + +gboolean +nm_policy_hosts_update_etc_hosts (const char *hostname, + const char *fallback_hostname, + gboolean *out_changed) +{ + char *contents = NULL; + char **lines = NULL; + GError *error = NULL; + GString *new_contents = NULL; + gsize contents_len = 0; + gboolean success = FALSE; + + g_return_val_if_fail (hostname != NULL, FALSE); + g_return_val_if_fail (out_changed != NULL, FALSE); + + if (!g_file_get_contents (SYSCONFDIR "/hosts", &contents, &contents_len, &error)) { + nm_log_warn (LOGD_DNS, "couldn't read " SYSCONFDIR "/hosts: (%d) %s", + error ? error->code : 0, + (error && error->message) ? error->message : "(unknown)"); + g_clear_error (&error); + return FALSE; + } + + /* Get the new /etc/hosts contents */ + lines = g_strsplit_set (contents, "\n\r", 0); + new_contents = nm_policy_get_etc_hosts ((const char **) lines, + contents_len, + hostname, + fallback_hostname, + &error); + g_strfreev (lines); + g_free (contents); + + if (new_contents) { + nm_log_info (LOGD_DNS, "Updating /etc/hosts with new system hostname"); + + g_clear_error (&error); + /* And actually update /etc/hosts */ + if (!g_file_set_contents (SYSCONFDIR "/hosts", new_contents->str, -1, &error)) { + nm_log_warn (LOGD_DNS, "couldn't update " SYSCONFDIR "/hosts: (%d) %s", + error ? error->code : 0, + (error && error->message) ? error->message : "(unknown)"); + g_clear_error (&error); + } else { + success = TRUE; + *out_changed = TRUE; + } + + g_string_free (new_contents, TRUE); + } else if (!error) { + /* No change required */ + success = TRUE; + } else { + nm_log_warn (LOGD_DNS, "couldn't read " SYSCONFDIR "/hosts: (%d) %s", + error->code, error->message ? error->message : "(unknown)"); + g_clear_error (&error); + } + + return success; +} + diff --git a/src/nm-policy-hosts.h b/src/nm-policy-hosts.h new file mode 100644 index 00000000..0a77e667 --- /dev/null +++ b/src/nm-policy-hosts.h @@ -0,0 +1,40 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2004 - 2010 Red Hat, Inc. + */ + +#ifndef NM_POLICY_HOSTS_H +#define NM_POLICT_HOSTS_H + +#include <glib.h> + +gboolean nm_policy_hosts_update_etc_hosts (const char *hostname, + const char *fallback_hostname, + gboolean *out_changed); + +/* Only for testcases; don't use outside of nm-policy-hosts.c */ +gboolean nm_policy_hosts_find_token (const char *line, const char *token); + +GString *nm_policy_get_etc_hosts (const char **lines, + gsize existing_len, + const char *hostname, + const char *fallback_hostname, + GError **error); + +#endif /* NM_POLICY_HOSTS_H */ + diff --git a/src/nm-policy.c b/src/nm-policy.c new file mode 100644 index 00000000..455545b6 --- /dev/null +++ b/src/nm-policy.c @@ -0,0 +1,1138 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2004 - 2010 Red Hat, Inc. + * Copyright (C) 2007 - 2008 Novell, Inc. + */ + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <netdb.h> +#include <ctype.h> + +#include "nm-policy.h" +#include "NetworkManagerUtils.h" +#include "nm-wifi-ap.h" +#include "nm-activation-request.h" +#include "nm-logging.h" +#include "nm-device-interface.h" +#include "nm-device.h" +#include "nm-device-wifi.h" +#include "nm-device-ethernet.h" +#include "nm-device-modem.h" +#include "nm-dbus-manager.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-connection.h" +#include "nm-system.h" +#include "nm-named-manager.h" +#include "nm-vpn-manager.h" +#include "nm-policy-hosts.h" +#include "nm-policy-hostname.h" + +struct NMPolicy { + NMManager *manager; + guint update_state_id; + GSList *pending_activation_checks; + GSList *signal_ids; + GSList *dev_signal_ids; + + NMVPNManager *vpn_manager; + gulong vpn_activated_id; + gulong vpn_deactivated_id; + + NMDevice *default_device4; + NMDevice *default_device6; + + HostnameThread *lookup; + + char *orig_hostname; /* hostname at NM start time */ +}; + +#define INVALID_TAG "invalid" + +static const char * +get_connection_id (NMConnection *connection) +{ + NMSettingConnection *s_con; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + g_return_val_if_fail (s_con != NULL, NULL); + + return nm_setting_connection_get_id (s_con); +} + +static NMDevice * +get_best_ip4_device (NMManager *manager, NMActRequest **out_req) +{ + GSList *devices, *iter; + NMDevice *best = NULL; + int best_prio = G_MAXINT; + + g_return_val_if_fail (manager != NULL, NULL); + g_return_val_if_fail (NM_IS_MANAGER (manager), NULL); + g_return_val_if_fail (out_req != NULL, NULL); + g_return_val_if_fail (*out_req == NULL, NULL); + + devices = nm_manager_get_devices (manager); + for (iter = devices; iter; iter = g_slist_next (iter)) { + NMDevice *dev = NM_DEVICE (iter->data); + NMActRequest *req; + NMConnection *connection; + NMIP4Config *ip4_config; + NMSettingIP4Config *s_ip4; + int prio; + guint i; + gboolean can_default = FALSE; + const char *method = NULL; + + if (nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED) + continue; + + ip4_config = nm_device_get_ip4_config (dev); + if (!ip4_config) + continue; + + req = nm_device_get_act_request (dev); + g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); + + /* Never set the default route through an IPv4LL-addressed device */ + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + if (s_ip4) + method = nm_setting_ip4_config_get_method (s_ip4); + + if (s_ip4 && !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) + continue; + + /* Make sure at least one of this device's IP addresses has a gateway */ + for (i = 0; i < nm_ip4_config_get_num_addresses (ip4_config); i++) { + NMIP4Address *addr; + + addr = nm_ip4_config_get_address (ip4_config, i); + if (nm_ip4_address_get_gateway (addr)) { + can_default = TRUE; + break; + } + } + + if (!can_default && !NM_IS_DEVICE_MODEM (dev)) + continue; + + /* 'never-default' devices can't ever be the default */ + if (s_ip4 && nm_setting_ip4_config_get_never_default (s_ip4)) + continue; + + prio = nm_device_get_priority (dev); + if (prio > 0 && prio < best_prio) { + best = dev; + best_prio = prio; + *out_req = req; + } + } + + return best; +} + +static NMDevice * +get_best_ip6_device (NMManager *manager, NMActRequest **out_req) +{ + GSList *devices, *iter; + NMDevice *best = NULL; + int best_prio = G_MAXINT; + + g_return_val_if_fail (manager != NULL, NULL); + g_return_val_if_fail (NM_IS_MANAGER (manager), NULL); + g_return_val_if_fail (out_req != NULL, NULL); + g_return_val_if_fail (*out_req == NULL, NULL); + + devices = nm_manager_get_devices (manager); + for (iter = devices; iter; iter = g_slist_next (iter)) { + NMDevice *dev = NM_DEVICE (iter->data); + NMActRequest *req; + NMConnection *connection; + NMIP6Config *ip6_config; + NMSettingIP6Config *s_ip6; + int prio; + guint i; + gboolean can_default = FALSE; + const char *method = NULL; + + if (nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED) + continue; + + ip6_config = nm_device_get_ip6_config (dev); + if (!ip6_config) + continue; + + req = nm_device_get_act_request (dev); + g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); + + /* Never set the default route through an IPv4LL-addressed device */ + s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); + if (s_ip6) + method = nm_setting_ip6_config_get_method (s_ip6); + + if (method && !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) + continue; + + /* Make sure at least one of this device's IP addresses has a gateway */ + for (i = 0; i < nm_ip6_config_get_num_addresses (ip6_config); i++) { + NMIP6Address *addr; + + addr = nm_ip6_config_get_address (ip6_config, i); + if (nm_ip6_address_get_gateway (addr)) { + can_default = TRUE; + break; + } + } + + if (!can_default && !NM_IS_DEVICE_MODEM (dev)) + continue; + + /* 'never-default' devices can't ever be the default */ + if (s_ip6 && nm_setting_ip6_config_get_never_default (s_ip6)) + continue; + + prio = nm_device_get_priority (dev); + if (prio > 0 && prio < best_prio) { + best = dev; + best_prio = prio; + *out_req = req; + } + } + + return best; +} + +static void +_set_hostname (const char *new_hostname, const char *msg) +{ + if (nm_policy_set_system_hostname (new_hostname, msg)) + nm_utils_call_dispatcher ("hostname", NULL, NULL, NULL); +} + +static void +lookup_callback (HostnameThread *thread, + int result, + const char *hostname, + gpointer user_data) +{ + NMPolicy *policy = (NMPolicy *) user_data; + char *msg; + + /* Update the hostname if the calling lookup thread is the in-progress one */ + if (!hostname_thread_is_dead (thread) && (thread == policy->lookup)) { + policy->lookup = NULL; + if (!hostname) { + /* No valid IP4 config (!!); fall back to localhost.localdomain */ + msg = g_strdup_printf ("address lookup failed: %d", result); + _set_hostname (NULL, msg); + g_free (msg); + } else + _set_hostname (hostname, "from address lookup"); + } + hostname_thread_free (thread); +} + +static void +update_system_hostname (NMPolicy *policy, NMDevice *best) +{ + char *configured_hostname = NULL; + NMActRequest *best_req = NULL; + NMDHCP4Config *dhcp4_config; + NMIP4Config *ip4_config; + NMIP4Address *addr; + + g_return_if_fail (policy != NULL); + + if (policy->lookup) { + hostname_thread_kill (policy->lookup); + policy->lookup = NULL; + } + + /* Hostname precedence order: + * + * 1) a configured hostname (from system-settings) + * 2) automatic hostname from the default device's config (DHCP, VPN, etc) + * 3) the original hostname when NM started + * 4) reverse-DNS of the best device's IPv4 address + * + */ + + /* Try a persistent hostname first */ + g_object_get (G_OBJECT (policy->manager), NM_MANAGER_HOSTNAME, &configured_hostname, NULL); + if (configured_hostname) { + _set_hostname (configured_hostname, "from system configuration"); + g_free (configured_hostname); + return; + } + + /* Try automatically determined hostname from the best device's IP config */ + if (!best) + best = get_best_ip4_device (policy->manager, &best_req); + + if (!best) { + /* No best device; fall back to original hostname or if there wasn't + * one, 'localhost.localdomain' + */ + _set_hostname (policy->orig_hostname, "no default device"); + return; + } + + /* Grab a hostname out of the device's DHCP4 config */ + dhcp4_config = nm_device_get_dhcp4_config (best); + if (dhcp4_config) { + const char *dhcp4_hostname, *p; + + p = dhcp4_hostname = nm_dhcp4_config_get_option (dhcp4_config, "host_name"); + if (dhcp4_hostname && strlen (dhcp4_hostname)) { + /* Sanity check */ + while (*p) { + if (!isblank (*p++)) { + _set_hostname (dhcp4_hostname, "from DHCP"); + return; + } + } + nm_log_warn (LOGD_DNS, "DHCP-provided hostname '%s' looks invalid; ignoring it", + dhcp4_hostname); + } + } + + /* If no automatically-configured hostname, try using the hostname from + * when NM started up. + */ + if (policy->orig_hostname) { + _set_hostname (policy->orig_hostname, "from system startup"); + return; + } + + /* No configured hostname, no automatically determined hostname, and + * no bootup hostname. Start reverse DNS of the current IP address. + */ + ip4_config = nm_device_get_ip4_config (best); + if ( !ip4_config + || (nm_ip4_config_get_num_nameservers (ip4_config) == 0) + || (nm_ip4_config_get_num_addresses (ip4_config) == 0)) { + /* No valid IP4 config (!!); fall back to localhost.localdomain */ + _set_hostname (NULL, "no IPv4 config"); + return; + } + + addr = nm_ip4_config_get_address (ip4_config, 0); + g_assert (addr); /* checked for > 1 address above */ + + /* Start the hostname lookup thread */ + policy->lookup = hostname_thread_new (nm_ip4_address_get_address (addr), lookup_callback, policy); + if (!policy->lookup) { + /* Fall back to 'localhost.localdomain' */ + _set_hostname (NULL, "error starting hostname thread"); + } +} + +static void +update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update) +{ + NMNamedIPConfigType dns_type = NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE; + NMDevice *best = NULL; + NMActRequest *best_req = NULL; + NMNamedManager *named_mgr; + GSList *devices = NULL, *iter, *vpns; + NMIP4Config *ip4_config = NULL; + NMIP4Address *addr; + const char *ip_iface = NULL; + NMConnection *connection = NULL; + NMSettingConnection *s_con = NULL; + const char *connection_id; + + best = get_best_ip4_device (policy->manager, &best_req); + if (!best) + goto out; + if (!force_update && (best == policy->default_device4)) + goto out; + + /* If a VPN connection is active, it is preferred */ + vpns = nm_vpn_manager_get_active_connections (policy->vpn_manager); + for (iter = vpns; iter; iter = g_slist_next (iter)) { + NMVPNConnection *candidate = NM_VPN_CONNECTION (iter->data); + NMConnection *vpn_connection; + NMSettingIP4Config *s_ip4; + gboolean can_default = TRUE; + NMVPNConnectionState vpn_state; + + /* If it's marked 'never-default', don't make it default */ + vpn_connection = nm_vpn_connection_get_connection (candidate); + g_assert (vpn_connection); + s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (vpn_connection, NM_TYPE_SETTING_IP4_CONFIG); + if (s_ip4 && nm_setting_ip4_config_get_never_default (s_ip4)) + can_default = FALSE; + + vpn_state = nm_vpn_connection_get_vpn_state (candidate); + if (can_default && (vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED)) { + NMIP4Config *parent_ip4; + NMDevice *parent; + + ip_iface = nm_vpn_connection_get_ip_iface (candidate); + connection = nm_vpn_connection_get_connection (candidate); + ip4_config = nm_vpn_connection_get_ip4_config (candidate); + addr = nm_ip4_config_get_address (ip4_config, 0); + + parent = nm_vpn_connection_get_parent_device (candidate); + parent_ip4 = nm_device_get_ip4_config (parent); + + nm_system_replace_default_ip4_route_vpn (ip_iface, + nm_ip4_address_get_gateway (addr), + nm_vpn_connection_get_ip4_internal_gateway (candidate), + nm_ip4_config_get_mss (ip4_config), + nm_device_get_ip_iface (parent), + nm_ip4_config_get_mss (parent_ip4)); + + dns_type = NM_NAMED_IP_CONFIG_TYPE_VPN; + } + g_object_unref (candidate); + } + g_slist_free (vpns); + + /* The best device gets the default route if a VPN connection didn't */ + if (!ip_iface || !ip4_config) { + connection = nm_act_request_get_connection (best_req); + ip_iface = nm_device_get_ip_iface (best); + ip4_config = nm_device_get_ip4_config (best); + g_assert (ip4_config); + addr = nm_ip4_config_get_address (ip4_config, 0); + + nm_system_replace_default_ip4_route (ip_iface, nm_ip4_address_get_gateway (addr), nm_ip4_config_get_mss (ip4_config)); + + dns_type = NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE; + } + + if (!ip_iface || !ip4_config) { + nm_log_warn (LOGD_CORE, "couldn't determine IP interface (%p) or IPv4 config (%p)!", + ip_iface, ip4_config); + goto out; + } + + /* Update the default active connection. Only mark the new default + * active connection after setting default = FALSE on all other connections + * first. The order is important, we don't want two connections marked + * default at the same time ever. + */ + devices = nm_manager_get_devices (policy->manager); + for (iter = devices; iter; iter = g_slist_next (iter)) { + NMDevice *dev = NM_DEVICE (iter->data); + NMActRequest *req; + + req = nm_device_get_act_request (dev); + if (req && (req != best_req)) + nm_act_request_set_default (req, FALSE); + } + + named_mgr = nm_named_manager_get (); + nm_named_manager_add_ip4_config (named_mgr, ip_iface, ip4_config, dns_type); + g_object_unref (named_mgr); + + /* Now set new default active connection _after_ updating DNS info, so that + * if the connection is shared dnsmasq picks up the right stuff. + */ + if (best_req) + nm_act_request_set_default (best_req, TRUE); + + if (connection) + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + + connection_id = s_con ? nm_setting_connection_get_id (s_con) : NULL; + if (connection_id) { + nm_log_info (LOGD_CORE, "Policy set '%s' (%s) as default for IPv4 routing and DNS.", connection_id, ip_iface); + } else { + nm_log_info (LOGD_CORE, "Policy set (%s) as default for IPv4 routing and DNS.", ip_iface); + } + +out: + policy->default_device4 = best; +} + +static void +update_ip6_routing_and_dns (NMPolicy *policy, gboolean force_update) +{ + NMNamedIPConfigType dns_type = NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE; + NMDevice *best = NULL; + NMActRequest *best_req = NULL; + NMNamedManager *named_mgr; + GSList *devices = NULL, *iter; +#if NOT_YET + GSList *vpns; +#endif + NMIP6Config *ip6_config = NULL; + NMIP6Address *addr; + const char *ip_iface = NULL; + NMConnection *connection = NULL; + NMSettingConnection *s_con = NULL; + const char *connection_id; + + best = get_best_ip6_device (policy->manager, &best_req); + if (!best) + goto out; + if (!force_update && (best == policy->default_device6)) + goto out; + +#if NOT_YET + /* If a VPN connection is active, it is preferred */ + vpns = nm_vpn_manager_get_active_connections (policy->vpn_manager); + for (iter = vpns; iter; iter = g_slist_next (iter)) { + NMVPNConnection *candidate = NM_VPN_CONNECTION (iter->data); + NMConnection *vpn_connection; + NMSettingIP6Config *s_ip6; + gboolean can_default = TRUE; + NMVPNConnectionState vpn_state; + + /* If it's marked 'never-default', don't make it default */ + vpn_connection = nm_vpn_connection_get_connection (candidate); + g_assert (vpn_connection); + s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (vpn_connection, NM_TYPE_SETTING_IP6_CONFIG); + if (s_ip6 && nm_setting_ip6_config_get_never_default (s_ip6)) + can_default = FALSE; + + vpn_state = nm_vpn_connection_get_vpn_state (candidate); + if (can_default && (vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED)) { + NMIP6Config *parent_ip6; + NMDevice *parent; + + ip_iface = nm_vpn_connection_get_ip_iface (candidate); + connection = nm_vpn_connection_get_connection (candidate); + ip6_config = nm_vpn_connection_get_ip6_config (candidate); + addr = nm_ip6_config_get_address (ip6_config, 0); + + parent = nm_vpn_connection_get_parent_device (candidate); + parent_ip6 = nm_device_get_ip6_config (parent); + + nm_system_replace_default_ip6_route_vpn (ip_iface, + nm_ip6_address_get_gateway (addr), + nm_vpn_connection_get_ip4_internal_gateway (candidate), + nm_ip6_config_get_mss (ip4_config), + nm_device_get_ip_iface (parent), + nm_ip6_config_get_mss (parent_ip4)); + + dns_type = NM_NAMED_IP_CONFIG_TYPE_VPN; + } + g_object_unref (candidate); + } + g_slist_free (vpns); +#endif + + /* The best device gets the default route if a VPN connection didn't */ + if (!ip_iface || !ip6_config) { + connection = nm_act_request_get_connection (best_req); + ip_iface = nm_device_get_ip_iface (best); + ip6_config = nm_device_get_ip6_config (best); + g_assert (ip6_config); + addr = nm_ip6_config_get_address (ip6_config, 0); + + nm_system_replace_default_ip6_route (ip_iface, nm_ip6_address_get_gateway (addr)); + + dns_type = NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE; + } + + if (!ip_iface || !ip6_config) { + nm_log_warn (LOGD_CORE, "couldn't determine IP interface (%p) or IPv6 config (%p)!", + ip_iface, ip6_config); + goto out; + } + + /* Update the default active connection. Only mark the new default + * active connection after setting default = FALSE on all other connections + * first. The order is important, we don't want two connections marked + * default at the same time ever. + */ + devices = nm_manager_get_devices (policy->manager); + for (iter = devices; iter; iter = g_slist_next (iter)) { + NMDevice *dev = NM_DEVICE (iter->data); + NMActRequest *req; + + req = nm_device_get_act_request (dev); + if (req && (req != best_req)) + nm_act_request_set_default6 (req, FALSE); + } + + named_mgr = nm_named_manager_get (); + nm_named_manager_add_ip6_config (named_mgr, ip_iface, ip6_config, dns_type); + g_object_unref (named_mgr); + + /* Now set new default active connection _after_ updating DNS info, so that + * if the connection is shared dnsmasq picks up the right stuff. + */ + if (best_req) + nm_act_request_set_default6 (best_req, TRUE); + + if (connection) + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + + connection_id = s_con ? nm_setting_connection_get_id (s_con) : NULL; + if (connection_id) { + nm_log_info (LOGD_CORE, "Policy set '%s' (%s) as default for IPv6 routing and DNS.", connection_id, ip_iface); + } else { + nm_log_info (LOGD_CORE, "Policy set (%s) as default for IPv6 routing and DNS.", ip_iface); + } + +out: + policy->default_device6 = best; +} + +static void +update_routing_and_dns (NMPolicy *policy, gboolean force_update) +{ + update_ip4_routing_and_dns (policy, force_update); + update_ip6_routing_and_dns (policy, force_update); + + /* Update the system hostname */ + update_system_hostname (policy, policy->default_device4); +} + +typedef struct { + NMPolicy *policy; + NMDevice *device; + guint id; +} ActivateData; + +static gboolean +auto_activate_device (gpointer user_data) +{ + ActivateData *data = (ActivateData *) user_data; + NMPolicy *policy; + NMConnection *best_connection; + char *specific_object = NULL; + GSList *connections, *iter; + + g_assert (data); + policy = data->policy; + + // FIXME: if a device is already activating (or activated) with a connection + // but another connection now overrides the current one for that device, + // deactivate the device and activate the new connection instead of just + // bailing if the device is already active + if (nm_device_get_act_request (data->device)) + goto out; + + /* System connections first, then user connections */ + connections = nm_manager_get_connections (policy->manager, NM_CONNECTION_SCOPE_SYSTEM); + connections = g_slist_concat (connections, nm_manager_get_connections (policy->manager, NM_CONNECTION_SCOPE_USER)); + + /* Remove connections that are in the invalid list. */ + iter = connections; + while (iter) { + NMConnection *iter_connection = NM_CONNECTION (iter->data); + GSList *next = g_slist_next (iter); + + if (g_object_get_data (G_OBJECT (iter_connection), INVALID_TAG)) { + connections = g_slist_remove_link (connections, iter); + g_object_unref (iter_connection); + g_slist_free (iter); + } + iter = next; + } + + best_connection = nm_device_get_best_auto_connection (data->device, connections, &specific_object); + if (best_connection) { + GError *error = NULL; + const char *device_path; + + device_path = nm_device_get_path (data->device); + if (!nm_manager_activate_connection (policy->manager, + best_connection, + specific_object, + device_path, + FALSE, + &error)) { + NMSettingConnection *s_con; + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (best_connection, NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + + nm_log_info (LOGD_DEVICE, "Connection '%s' auto-activation failed: (%d) %s", + nm_setting_connection_get_id (s_con), error->code, error->message); + g_error_free (error); + } + } + + g_slist_foreach (connections, (GFunc) g_object_unref, NULL); + g_slist_free (connections); + + out: + /* Remove this call's handler ID */ + policy->pending_activation_checks = g_slist_remove (policy->pending_activation_checks, data); + g_object_unref (data->device); + g_free (data); + + return FALSE; +} + +/*****************************************************************************/ + +static void +vpn_connection_activated (NMVPNManager *manager, + NMVPNConnection *vpn, + gpointer user_data) +{ + update_routing_and_dns ((NMPolicy *) user_data, TRUE); +} + +static void +vpn_connection_deactivated (NMVPNManager *manager, + NMVPNConnection *vpn, + NMVPNConnectionState state, + NMVPNConnectionStateReason reason, + gpointer user_data) +{ + update_routing_and_dns ((NMPolicy *) user_data, TRUE); +} + +static void +global_state_changed (NMManager *manager, NMState state, gpointer user_data) +{ +} + +static void +hostname_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data) +{ + update_system_hostname ((NMPolicy *) user_data, NULL); +} + +static void +sleeping_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data) +{ + gboolean sleeping = FALSE, enabled = FALSE; + GSList *connections, *iter; + + g_object_get (G_OBJECT (manager), NM_MANAGER_SLEEPING, &sleeping, NULL); + g_object_get (G_OBJECT (manager), NM_MANAGER_NETWORKING_ENABLED, &enabled, NULL); + + /* Clear the invalid flag on all connections so they'll get retried on wakeup */ + if (sleeping || !enabled) { + connections = nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_SYSTEM); + connections = g_slist_concat (connections, nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_USER)); + for (iter = connections; iter; iter = g_slist_next (iter)) + g_object_set_data (G_OBJECT (iter->data), INVALID_TAG, NULL); + g_slist_free (connections); + } +} + +static void +schedule_activate_check (NMPolicy *policy, NMDevice *device, guint delay_seconds) +{ + ActivateData *data; + GSList *iter; + NMDeviceState state; + + if (nm_manager_get_state (policy->manager) == NM_STATE_ASLEEP) + return; + + state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); + if (state < NM_DEVICE_STATE_DISCONNECTED) + return; + + if (!nm_device_autoconnect_allowed (device)) + return; + + for (iter = policy->pending_activation_checks; iter; iter = g_slist_next (iter)) { + /* Only one pending activation check at a time */ + if (((ActivateData *) iter->data)->device == device) + return; + } + + data = g_malloc0 (sizeof (ActivateData)); + g_return_if_fail (data != NULL); + + data->policy = policy; + data->device = g_object_ref (device); + data->id = delay_seconds ? g_timeout_add_seconds (delay_seconds, auto_activate_device, data) : g_idle_add (auto_activate_device, data); + policy->pending_activation_checks = g_slist_append (policy->pending_activation_checks, data); +} + +static NMConnection * +get_device_connection (NMDevice *device) +{ + NMActRequest *req; + + req = nm_device_get_act_request (device); + if (!req) + return NULL; + + return nm_act_request_get_connection (req); +} + +static void +device_state_changed (NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason, + gpointer user_data) +{ + NMPolicy *policy = (NMPolicy *) user_data; + NMConnection *connection = get_device_connection (device); + + switch (new_state) { + case NM_DEVICE_STATE_FAILED: + /* Mark the connection invalid if it failed during activation so that + * it doesn't get automatically chosen over and over and over again. + */ + if (connection) { + gboolean fail = FALSE; + + if (IS_ACTIVATING_STATE (old_state)) { + nm_log_info (LOGD_DEVICE, "Marking connection '%s' invalid.", get_connection_id (connection)); + fail = TRUE; + } else if ( (old_state == NM_DEVICE_STATE_ACTIVATED) + && (reason == NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED)) { + nm_log_info (LOGD_DEVICE, "Marking connection '%s' invalid because IP configuration expired.", + get_connection_id (connection)); + fail = TRUE; + } + + if (fail) { + g_object_set_data (G_OBJECT (connection), INVALID_TAG, GUINT_TO_POINTER (TRUE)); + nm_connection_clear_secrets (connection); + } + } + schedule_activate_check (policy, device, 3); + break; + case NM_DEVICE_STATE_ACTIVATED: + if (connection) { + /* Clear the invalid tag on the connection */ + g_object_set_data (G_OBJECT (connection), INVALID_TAG, NULL); + + /* And clear secrets so they will always be requested from the + * settings service when the next connection is made. + */ + nm_connection_clear_secrets (connection); + } + + update_routing_and_dns (policy, FALSE); + break; + case NM_DEVICE_STATE_UNMANAGED: + case NM_DEVICE_STATE_UNAVAILABLE: + case NM_DEVICE_STATE_DISCONNECTED: + update_routing_and_dns (policy, FALSE); + schedule_activate_check (policy, device, 0); + break; + default: + break; + } +} + +static void +device_ip_config_changed (NMDevice *device, + GParamSpec *pspec, + gpointer user_data) +{ + update_routing_and_dns ((NMPolicy *) user_data, TRUE); +} + +static void +wireless_networks_changed (NMDeviceWifi *device, NMAccessPoint *ap, gpointer user_data) +{ + schedule_activate_check ((NMPolicy *) user_data, NM_DEVICE (device), 0); +} + +typedef struct { + gulong id; + NMDevice *device; +} DeviceSignalID; + +static GSList * +add_device_signal_id (GSList *list, gulong id, NMDevice *device) +{ + DeviceSignalID *data; + + data = g_malloc0 (sizeof (DeviceSignalID)); + if (!data) + return list; + + data->id = id; + data->device = device; + return g_slist_append (list, data); +} + +static void +device_added (NMManager *manager, NMDevice *device, gpointer user_data) +{ + NMPolicy *policy = (NMPolicy *) user_data; + gulong id; + + id = g_signal_connect (device, "state-changed", + G_CALLBACK (device_state_changed), + policy); + policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device); + + id = g_signal_connect (device, "notify::" NM_DEVICE_INTERFACE_IP4_CONFIG, + G_CALLBACK (device_ip_config_changed), + policy); + policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device); + + id = g_signal_connect (device, "notify::" NM_DEVICE_INTERFACE_IP6_CONFIG, + G_CALLBACK (device_ip_config_changed), + policy); + policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device); + + if (NM_IS_DEVICE_WIFI (device)) { + id = g_signal_connect (device, "access-point-added", + G_CALLBACK (wireless_networks_changed), + policy); + policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device); + + id = g_signal_connect (device, "access-point-removed", + G_CALLBACK (wireless_networks_changed), + policy); + policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device); + } +} + +static void +device_removed (NMManager *manager, NMDevice *device, gpointer user_data) +{ + NMPolicy *policy = (NMPolicy *) user_data; + GSList *iter; + + /* Clear any idle callbacks for this device */ + iter = policy->pending_activation_checks; + while (iter) { + ActivateData *data = (ActivateData *) iter->data; + GSList *next = g_slist_next (iter); + + if (data->device == device) { + g_source_remove (data->id); + g_object_unref (data->device); + g_free (data); + policy->pending_activation_checks = g_slist_delete_link (policy->pending_activation_checks, iter); + } + iter = next; + } + + /* Clear any signal handlers for this device */ + iter = policy->dev_signal_ids; + while (iter) { + DeviceSignalID *data = (DeviceSignalID *) iter->data; + GSList *next = g_slist_next (iter); + + if (data->device == device) { + g_signal_handler_disconnect (data->device, data->id); + g_free (data); + policy->dev_signal_ids = g_slist_delete_link (policy->dev_signal_ids, iter); + } + iter = next; + } + + update_routing_and_dns (policy, FALSE); +} + +static void +schedule_activate_all (NMPolicy *policy) +{ + GSList *iter, *devices; + + devices = nm_manager_get_devices (policy->manager); + for (iter = devices; iter; iter = g_slist_next (iter)) + schedule_activate_check (policy, NM_DEVICE (iter->data), 0); +} + +static void +connections_added (NMManager *manager, + NMConnectionScope scope, + gpointer user_data) +{ + schedule_activate_all ((NMPolicy *) user_data); +} + +static void +connection_added (NMManager *manager, + NMConnection *connection, + NMConnectionScope scope, + gpointer user_data) +{ + schedule_activate_all ((NMPolicy *) user_data); +} + +static void +connection_updated (NMManager *manager, + NMConnection *connection, + NMConnectionScope scope, + gpointer user_data) +{ + /* Clear the invalid tag on the connection if it got updated. */ + g_object_set_data (G_OBJECT (connection), INVALID_TAG, NULL); + + schedule_activate_all ((NMPolicy *) user_data); +} + +static void +connection_removed (NMManager *manager, + NMConnection *connection, + NMConnectionScope scope, + gpointer user_data) +{ + NMSettingConnection *s_con; + GPtrArray *list; + int i; + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + if (!s_con) + return; + + list = nm_manager_get_active_connections_by_connection (manager, connection); + if (!list) + return; + + for (i = 0; i < list->len; i++) { + char *path = g_ptr_array_index (list, i); + GError *error = NULL; + + if (!nm_manager_deactivate_connection (manager, path, NM_DEVICE_STATE_REASON_CONNECTION_REMOVED, &error)) { + nm_log_warn (LOGD_DEVICE, "Connection '%s' disappeared, but error deactivating it: (%d) %s", + nm_setting_connection_get_id (s_con), error->code, error->message); + g_error_free (error); + } + g_free (path); + } + g_ptr_array_free (list, TRUE); +} + +NMPolicy * +nm_policy_new (NMManager *manager, NMVPNManager *vpn_manager) +{ + NMPolicy *policy; + static gboolean initialized = FALSE; + gulong id; + char hostname[HOST_NAME_MAX + 2]; + + g_return_val_if_fail (NM_IS_MANAGER (manager), NULL); + g_return_val_if_fail (initialized == FALSE, NULL); + + policy = g_malloc0 (sizeof (NMPolicy)); + policy->manager = g_object_ref (manager); + policy->update_state_id = 0; + + /* Grab hostname on startup and use that if nothing provides one */ + memset (hostname, 0, sizeof (hostname)); + if (gethostname (&hostname[0], HOST_NAME_MAX) == 0) { + /* only cache it if it's a valid hostname */ + if (strlen (hostname) && strcmp (hostname, "localhost") && strcmp (hostname, "localhost.localdomain")) + policy->orig_hostname = g_strdup (hostname); + } + + policy->vpn_manager = g_object_ref (vpn_manager); + id = g_signal_connect (policy->vpn_manager, "connection-activated", + G_CALLBACK (vpn_connection_activated), policy); + policy->vpn_activated_id = id; + id = g_signal_connect (policy->vpn_manager, "connection-deactivated", + G_CALLBACK (vpn_connection_deactivated), policy); + policy->vpn_deactivated_id = id; + + id = g_signal_connect (manager, "state-changed", + G_CALLBACK (global_state_changed), policy); + policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); + + id = g_signal_connect (manager, "notify::" NM_MANAGER_HOSTNAME, + G_CALLBACK (hostname_changed), policy); + policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); + + id = g_signal_connect (manager, "notify::" NM_MANAGER_SLEEPING, + G_CALLBACK (sleeping_changed), policy); + policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); + + id = g_signal_connect (manager, "notify::" NM_MANAGER_NETWORKING_ENABLED, + G_CALLBACK (sleeping_changed), policy); + policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); + + id = g_signal_connect (manager, "device-added", + G_CALLBACK (device_added), policy); + policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); + + id = g_signal_connect (manager, "device-removed", + G_CALLBACK (device_removed), policy); + policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); + + /* Large batch of connections added, manager doesn't want us to + * process each one individually. + */ + id = g_signal_connect (manager, "connections-added", + G_CALLBACK (connections_added), policy); + policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); + + /* Single connection added */ + id = g_signal_connect (manager, "connection-added", + G_CALLBACK (connection_added), policy); + policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); + + id = g_signal_connect (manager, "connection-updated", + G_CALLBACK (connection_updated), policy); + policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); + + id = g_signal_connect (manager, "connection-removed", + G_CALLBACK (connection_removed), policy); + policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); + + return policy; +} + +void +nm_policy_destroy (NMPolicy *policy) +{ + GSList *iter; + + g_return_if_fail (policy != NULL); + + /* Tell any existing hostname lookup thread to die, it'll get cleaned up + * by the lookup thread callback. + */ + if (policy->lookup) { + hostname_thread_kill (policy->lookup); + policy->lookup = NULL; + } + + for (iter = policy->pending_activation_checks; iter; iter = g_slist_next (iter)) { + ActivateData *data = (ActivateData *) iter->data; + + g_source_remove (data->id); + g_object_unref (data->device); + g_free (data); + } + g_slist_free (policy->pending_activation_checks); + + g_signal_handler_disconnect (policy->vpn_manager, policy->vpn_activated_id); + g_signal_handler_disconnect (policy->vpn_manager, policy->vpn_deactivated_id); + + for (iter = policy->signal_ids; iter; iter = g_slist_next (iter)) + g_signal_handler_disconnect (policy->manager, (gulong) iter->data); + g_slist_free (policy->signal_ids); + + for (iter = policy->dev_signal_ids; iter; iter = g_slist_next (iter)) { + DeviceSignalID *data = (DeviceSignalID *) iter->data; + + g_signal_handler_disconnect (data->device, data->id); + g_free (data); + } + g_slist_free (policy->dev_signal_ids); + + g_free (policy->orig_hostname); + + g_object_unref (policy->manager); + g_free (policy); +} + diff --git a/src/nm-policy.h b/src/nm-policy.h new file mode 100644 index 00000000..7d99613c --- /dev/null +++ b/src/nm-policy.h @@ -0,0 +1,36 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2004 - 2008 Red Hat, Inc. + * Copyright (C) 2007 - 2008 Novell, Inc. + */ + +#ifndef NETWORK_MANAGER_POLICY_H +#define NETWORK_MANAGER_POLICY_H + +#include "NetworkManager.h" +#include "nm-manager.h" +#include "nm-vpn-manager.h" +#include "nm-device.h" +#include "nm-activation-request.h" + +typedef struct NMPolicy NMPolicy; + +NMPolicy *nm_policy_new (NMManager *manager, NMVPNManager *vpn_manager); +void nm_policy_destroy (NMPolicy *policy); + +#endif /* NETWORK_MANAGER_POLICY_H */ diff --git a/src/nm-properties-changed-signal.c b/src/nm-properties-changed-signal.c new file mode 100644 index 00000000..4a9db8af --- /dev/null +++ b/src/nm-properties-changed-signal.c @@ -0,0 +1,201 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + */ + +#include <string.h> +#include <stdio.h> + +#include <dbus/dbus-glib.h> +#include "nm-properties-changed-signal.h" +#include "nm-dbus-glib-types.h" + +#define NM_DBUS_PROPERTY_CHANGED "NM_DBUS_PROPERTY_CHANGED" + +typedef struct { + GHashTable *hash; + gulong signal_id; + guint idle_id; +} PropertiesChangedInfo; + +static void +destroy_value (gpointer data) +{ + GValue *val = (GValue *) data; + + g_value_unset (val); + g_slice_free (GValue, val); +} + +static PropertiesChangedInfo * +properties_changed_info_new (void) +{ + PropertiesChangedInfo *info; + + info = g_slice_new0 (PropertiesChangedInfo); + info->hash = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + destroy_value); + return info; +} + +static void +properties_changed_info_destroy (gpointer data) +{ + PropertiesChangedInfo *info = (PropertiesChangedInfo *) data; + + if (info->idle_id) + g_source_remove (info->idle_id); + + g_hash_table_destroy (info->hash); + g_slice_free (PropertiesChangedInfo, info); +} + +#ifdef DEBUG +static void +add_to_string (gpointer key, gpointer value, gpointer user_data) +{ + char *buf = (char *) user_data; + GValue str_val = { 0, }; + + g_value_init (&str_val, G_TYPE_STRING); + if (!g_value_transform ((GValue *) value, &str_val)) { + if (G_VALUE_HOLDS_OBJECT (value)) { + GObject *obj = g_value_get_object (value); + + if (g_value_get_object (value)) { + sprintf (buf + strlen (buf), "{%s: %p (%s)}, ", + (const char *) key, obj, G_OBJECT_TYPE_NAME (obj)); + } else { + sprintf (buf + strlen (buf), "{%s: %p}, ", (const char *) key, obj); + } + } else + sprintf (buf + strlen (buf), "{%s: <transform error>}, ", (const char *) key); + } else { + sprintf (buf + strlen (buf), "{%s: %s}, ", (const char *) key, g_value_get_string (&str_val)); + } + g_value_unset (&str_val); +} +#endif + +static gboolean +properties_changed (gpointer data) +{ + GObject *object = G_OBJECT (data); + PropertiesChangedInfo *info = (PropertiesChangedInfo *) g_object_get_data (object, NM_DBUS_PROPERTY_CHANGED); + + g_assert (info); + +#ifdef DEBUG + { + char buf[2048] = { 0, }; + g_hash_table_foreach (info->hash, add_to_string, &buf); + nm_log_dbg (LOGD_CORE, "%s -> %s", G_OBJECT_TYPE_NAME (object), buf); + } +#endif + + g_signal_emit (object, info->signal_id, 0, info->hash); + g_hash_table_remove_all (info->hash); + + return FALSE; +} + +static void +idle_id_reset (gpointer data) +{ + GObject *object = G_OBJECT (data); + PropertiesChangedInfo *info = (PropertiesChangedInfo *) g_object_get_data (object, NM_DBUS_PROPERTY_CHANGED); + + /* info is unset when the object is being destroyed */ + if (info) + info->idle_id = 0; +} + +static char* +uscore_to_wincaps (const char *uscore) +{ + const char *p; + GString *str; + gboolean last_was_uscore; + + last_was_uscore = TRUE; + + str = g_string_new (NULL); + p = uscore; + while (p && *p) { + if (*p == '-' || *p == '_') + last_was_uscore = TRUE; + else { + if (last_was_uscore) { + g_string_append_c (str, g_ascii_toupper (*p)); + last_was_uscore = FALSE; + } else + g_string_append_c (str, *p); + } + ++p; + } + + return g_string_free (str, FALSE); +} + +static void +notify (GObject *object, GParamSpec *pspec) +{ + PropertiesChangedInfo *info; + GValue *value; + + /* Ignore properties that shouldn't be exported */ + if (pspec->flags & NM_PROPERTY_PARAM_NO_EXPORT) + return; + + info = (PropertiesChangedInfo *) g_object_get_data (object, NM_DBUS_PROPERTY_CHANGED); + if (!info) { + info = properties_changed_info_new (); + g_object_set_data_full (object, NM_DBUS_PROPERTY_CHANGED, info, properties_changed_info_destroy); + info->signal_id = g_signal_lookup ("properties-changed", G_OBJECT_TYPE (object)); + g_assert (info->signal_id); + } + + value = g_slice_new0 (GValue); + g_value_init (value, pspec->value_type); + g_object_get_property (object, pspec->name, value); + g_hash_table_insert (info->hash, uscore_to_wincaps (pspec->name), value); + + if (!info->idle_id) + info->idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, properties_changed, object, idle_id_reset); +} + +guint +nm_properties_changed_signal_new (GObjectClass *object_class, + guint class_offset) +{ + guint id; + + object_class->notify = notify; + + id = g_signal_new ("properties-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + class_offset, + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, DBUS_TYPE_G_MAP_OF_VARIANT); + + return id; +} diff --git a/src/nm-properties-changed-signal.h b/src/nm-properties-changed-signal.h new file mode 100644 index 00000000..b87e34e1 --- /dev/null +++ b/src/nm-properties-changed-signal.h @@ -0,0 +1,32 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + */ + +#ifndef _NM_PROPERTIES_CHANGED_SIGNAL_H_ +#define _NM_PROPERTIES_CHANGED_SIGNAL_H_ + +#include <glib-object.h> + +#define NM_PROPERTY_PARAM_NO_EXPORT (1 << (0 + G_PARAM_USER_SHIFT)) + +guint nm_properties_changed_signal_new (GObjectClass *object_class, + guint class_offset); + +#endif /* _NM_PROPERTIES_CHANGED_SIGNAL_H_ */ diff --git a/src/nm-rfkill.h b/src/nm-rfkill.h new file mode 100644 index 00000000..d3cd7774 --- /dev/null +++ b/src/nm-rfkill.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2008 Red Hat, Inc. + */ + +#ifndef NM_RFKILL_H +#define NM_RFKILL_H + +typedef enum { + RFKILL_UNBLOCKED = 0, + RFKILL_SOFT_BLOCKED = 1, + RFKILL_HARD_BLOCKED = 2 +} RfKillState; + +typedef enum { + RFKILL_TYPE_WLAN = 0, + RFKILL_TYPE_WWAN = 1, + RFKILL_TYPE_WIMAX = 2, + + /* UNKNOWN and MAX should always be 1 more than + * the last rfkill type since RFKILL_TYPE_MAX is + * used as an array size. + */ + RFKILL_TYPE_UNKNOWN = 3, /* KEEP LAST */ + RFKILL_TYPE_MAX = RFKILL_TYPE_UNKNOWN +} RfKillType; + +#endif /* NM_RFKILL_H */ + diff --git a/src/nm-secrets-provider-interface.c b/src/nm-secrets-provider-interface.c new file mode 100644 index 00000000..47b8e57e --- /dev/null +++ b/src/nm-secrets-provider-interface.c @@ -0,0 +1,224 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <string.h> + +#include "nm-marshal.h" +#include "nm-secrets-provider-interface.h" + +#include <nm-setting-8021x.h> +#include <nm-setting-wireless-security.h> +#include "nm-logging.h" + +static void +nm_secrets_provider_interface_init (gpointer g_iface) +{ + GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); + static gboolean initialized = FALSE; + + if (initialized) + return; + initialized = TRUE; + + /* Signals */ + g_signal_new ("manager-get-secrets", + iface_type, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMSecretsProviderInterface, manager_get_secrets), + NULL, NULL, + _nm_marshal_BOOLEAN__POINTER_STRING_BOOLEAN_UINT_STRING_STRING, + G_TYPE_BOOLEAN, 6, + G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING); + + g_signal_new ("manager-cancel-secrets", + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMSecretsProviderInterface, manager_cancel_secrets), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + + +GType +nm_secrets_provider_interface_get_type (void) +{ + static GType interface_type = 0; + + if (!interface_type) { + const GTypeInfo interface_info = { + sizeof (NMSecretsProviderInterface), /* class_size */ + nm_secrets_provider_interface_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + interface_type = g_type_register_static (G_TYPE_INTERFACE, + "NMSecretsProviderInterface", + &interface_info, 0); + + g_type_interface_add_prerequisite (interface_type, G_TYPE_OBJECT); + } + + return interface_type; +} + +gboolean +nm_secrets_provider_interface_get_secrets (NMSecretsProviderInterface *self, + NMConnection *connection, + const char *setting_name, + gboolean request_new, + RequestSecretsCaller caller, + const char *hint1, + const char *hint2) +{ + guint success = FALSE; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_SECRETS_PROVIDER_INTERFACE (self), FALSE); + g_return_val_if_fail (connection != NULL, FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + g_return_val_if_fail (setting_name != NULL, FALSE); + + nm_secrets_provider_interface_cancel_get_secrets (self); + + g_signal_emit_by_name (self, "manager-get-secrets", + connection, setting_name, request_new, caller, hint1, hint2, + &success); + if (!success) { + nm_log_warn (LOGD_CORE, "failed to get connection secrets."); + return FALSE; + } + + return TRUE; +} + +void +nm_secrets_provider_interface_cancel_get_secrets (NMSecretsProviderInterface *self) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_SECRETS_PROVIDER_INTERFACE (self)); + + g_signal_emit_by_name (self, "manager-cancel-secrets"); +} + + +static void +add_one_key_to_list (gpointer key, gpointer data, gpointer user_data) +{ + GSList **list = (GSList **) user_data; + + *list = g_slist_append (*list, key); +} + +static gint +settings_order_func (gconstpointer a, gconstpointer b) +{ + /* Just ensure the 802.1x setting gets processed _before_ the + * wireless-security one. + */ + + if ( !strcmp (a, NM_SETTING_802_1X_SETTING_NAME) + && !strcmp (b, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME)) + return -1; + + if ( !strcmp (a, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME) + && !strcmp (b, NM_SETTING_802_1X_SETTING_NAME)) + return 1; + + return 0; +} + +void +nm_secrets_provider_interface_get_secrets_result (NMSecretsProviderInterface *self, + const char *setting_name, + RequestSecretsCaller caller, + GHashTable *settings, + GError *error) +{ + GSList *keys = NULL, *iter; + GSList *updated = NULL; + GError *tmp_error = NULL; + + g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_SECRETS_PROVIDER_INTERFACE (self)); + + if (error) { + NM_SECRETS_PROVIDER_INTERFACE_GET_INTERFACE (self)->result (self, + setting_name, + caller, + NULL, + error); + return; + } + + if (g_hash_table_size (settings) == 0) { + g_set_error (&tmp_error, 0, 0, "%s", "no secrets were received!"); + NM_SECRETS_PROVIDER_INTERFACE_GET_INTERFACE (self)->result (self, + setting_name, + caller, + NULL, + tmp_error); + g_clear_error (&tmp_error); + return; + } + + g_hash_table_foreach (settings, add_one_key_to_list, &keys); + keys = g_slist_sort (keys, settings_order_func); + for (iter = keys; iter; iter = g_slist_next (iter)) { + GHashTable *hash; + const char *name = (const char *) iter->data; + + hash = g_hash_table_lookup (settings, name); + if (!hash) { + nm_log_warn (LOGD_CORE, "couldn't get setting secrets for '%s'", name); + continue; + } + + if (NM_SECRETS_PROVIDER_INTERFACE_GET_INTERFACE (self)->update_setting (self, name, hash)) + updated = g_slist_append (updated, (gpointer) setting_name); + } + g_slist_free (keys); + + if (g_slist_length (updated)) { + NM_SECRETS_PROVIDER_INTERFACE_GET_INTERFACE (self)->result (self, + setting_name, + caller, + updated, + NULL); + } else { + g_set_error (&tmp_error, 0, 0, "%s", "no secrets updated because no valid " + "settings were received!"); + NM_SECRETS_PROVIDER_INTERFACE_GET_INTERFACE (self)->result (self, + setting_name, + caller, + NULL, + tmp_error); + g_clear_error (&tmp_error); + } + + g_slist_free (updated); +} + diff --git a/src/nm-secrets-provider-interface.h b/src/nm-secrets-provider-interface.h new file mode 100644 index 00000000..3d9e08b1 --- /dev/null +++ b/src/nm-secrets-provider-interface.h @@ -0,0 +1,90 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef NM_SECRETS_PROVIDER_INTERFACE_H +#define NM_SECRETS_PROVIDER_INTERFACE_H + +#include <glib-object.h> +#include <nm-connection.h> + +typedef enum { + SECRETS_CALLER_NONE = 0, + SECRETS_CALLER_ETHERNET, + SECRETS_CALLER_WIFI, + SECRETS_CALLER_MOBILE_BROADBAND, + SECRETS_CALLER_PPP, + SECRETS_CALLER_VPN +} RequestSecretsCaller; + +#define NM_TYPE_SECRETS_PROVIDER_INTERFACE (nm_secrets_provider_interface_get_type ()) +#define NM_SECRETS_PROVIDER_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SECRETS_PROVIDER_INTERFACE, NMSecretsProviderInterface)) +#define NM_IS_SECRETS_PROVIDER_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SECRETS_PROVIDER_INTERFACE)) +#define NM_SECRETS_PROVIDER_INTERFACE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_SECRETS_PROVIDER_INTERFACE, NMSecretsProviderInterface)) + +typedef struct _NMSecretsProviderInterface NMSecretsProviderInterface; + +struct _NMSecretsProviderInterface { + GTypeInterface g_iface; + + /* Methods */ + void (*result) (NMSecretsProviderInterface *self, + const char *setting_name, + RequestSecretsCaller caller, + const GSList *updated, + GError *error); + + gboolean (*update_setting) (NMSecretsProviderInterface *self, + const char *setting_name, + GHashTable *new); + + /* Signals */ + void (*manager_get_secrets) (NMSecretsProviderInterface *self, + NMConnection *connection, + const char *setting_name, + gboolean request_new, + RequestSecretsCaller caller, + const char *hint1, + const char *hint2); + + void (*manager_cancel_secrets) (NMSecretsProviderInterface *self); +}; + +GType nm_secrets_provider_interface_get_type (void); + +/* For callers */ +gboolean nm_secrets_provider_interface_get_secrets (NMSecretsProviderInterface *self, + NMConnection *connection, + const char *setting_name, + gboolean request_new, + RequestSecretsCaller caller, + const char *hint1, + const char *hint2); + +void nm_secrets_provider_interface_cancel_get_secrets (NMSecretsProviderInterface *self); + +/* For NMManager */ +void nm_secrets_provider_interface_get_secrets_result (NMSecretsProviderInterface *self, + const char *setting_name, + RequestSecretsCaller caller, + GHashTable *settings, + GError *error); + +#endif /* NM_SECRETS_PROVIDER_INTERFACE_H */ + diff --git a/src/nm-system.c b/src/nm-system.c new file mode 100644 index 00000000..35aa36a1 --- /dev/null +++ b/src/nm-system.c @@ -0,0 +1,1347 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2004 - 2010 Red Hat, Inc. + * Copyright (C) 2005 - 2008 Novell, Inc. + * Copyright (C) 1996 - 1997 Yoichi Hariguchi <yoichi@fore.com> + * Copyright (C) January, 1998 Sergei Viznyuk <sv@phystech.com> + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <net/route.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <resolv.h> +#include <netdb.h> +#include <glib.h> +#include <ctype.h> +#include <net/if.h> + +#include "nm-system.h" +#include "nm-device.h" +#include "nm-named-manager.h" +#include "NetworkManagerUtils.h" +#include "nm-utils.h" +#include "nm-logging.h" +#include "nm-netlink-monitor.h" + +/* Because of a bug in libnl, rtnl.h should be included before route.h */ +#include <netlink/route/rtnl.h> + +#include <netlink/route/addr.h> +#include <netlink/route/route.h> +#include <netlink/netlink.h> +#include <netlink/utils.h> +#include <netlink/route/link.h> + +static void nm_system_device_set_priority (const char *iface, + NMIP4Config *config, + int priority); + +static gboolean +ip4_dest_in_same_subnet (NMIP4Config *config, guint32 dest, guint32 dest_prefix) +{ + int num; + int i; + + num = nm_ip4_config_get_num_addresses (config); + for (i = 0; i < num; i++) { + NMIP4Address *addr = nm_ip4_config_get_address (config, i); + guint32 prefix = nm_ip4_address_get_prefix (addr); + guint32 address = nm_ip4_address_get_address (addr); + + if (prefix <= dest_prefix) { + guint32 masked_addr = ntohl(address) >> (32 - prefix); + guint32 masked_dest = ntohl(dest) >> (32 - prefix); + + if (masked_addr == masked_dest) + return TRUE; + } + } + + return FALSE; +} + +static struct rtnl_route * +create_route (int iface_idx, int mss) +{ + struct rtnl_route *route; + + route = rtnl_route_alloc (); + if (route) { + rtnl_route_set_oif (route, iface_idx); + + if (mss && rtnl_route_set_metric (route, RTAX_ADVMSS, mss) < 0) { + nm_log_warn (LOGD_DEVICE, "could not set mss"); + } + } else + nm_log_err (LOGD_DEVICE, "could not allocate route"); + + return route; +} + +static struct rtnl_route * +nm_system_device_set_ip4_route (const char *iface, + guint32 ip4_dest, + guint32 ip4_prefix, + guint32 ip4_gateway, + guint32 metric, + int mss) +{ + struct nl_handle *nlh; + struct rtnl_route *route; + struct nl_addr *dest_addr; + struct nl_addr *gw_addr = NULL; + int err, iface_idx; + + nlh = nm_netlink_get_default_handle (); + g_return_val_if_fail (nlh != NULL, NULL); + + iface_idx = nm_netlink_iface_to_index (iface); + g_return_val_if_fail (iface_idx >= 0, NULL); + + route = create_route (iface_idx, mss); + g_return_val_if_fail (route != NULL, NULL); + + /* Destination */ + dest_addr = nl_addr_build (AF_INET, &ip4_dest, sizeof (ip4_dest)); + g_return_val_if_fail (dest_addr != NULL, NULL); + nl_addr_set_prefixlen (dest_addr, (int) ip4_prefix); + + rtnl_route_set_dst (route, dest_addr); + nl_addr_put (dest_addr); + + /* Gateway */ + if (ip4_gateway) { + gw_addr = nl_addr_build (AF_INET, &ip4_gateway, sizeof (ip4_gateway)); + if (gw_addr) { + rtnl_route_set_gateway (route, gw_addr); + rtnl_route_set_scope (route, RT_SCOPE_UNIVERSE); + } else { + nm_log_err (LOGD_DEVICE | LOGD_IP4, "Invalid gateway 0x%X", ip4_gateway); + rtnl_route_put (route); + return NULL; + } + } + + /* Metric */ + if (metric) + rtnl_route_set_prio (route, metric); + + /* Add the route */ + err = rtnl_route_add (nlh, route, 0); + if (err == -ESRCH && ip4_gateway) { + /* Gateway might be over a bridge; try adding a route to gateway first */ + struct rtnl_route *route2; + + route2 = create_route (iface_idx, mss); + if (route2) { + /* Add route to gateway over bridge */ + rtnl_route_set_dst (route2, gw_addr); + err = rtnl_route_add (nlh, route2, 0); + if (!err) { + /* Try adding the route again */ + err = rtnl_route_add (nlh, route, 0); + if (err) + rtnl_route_del (nlh, route2, 0); + } + rtnl_route_put (route2); + } + } + + if (gw_addr) + nl_addr_put (gw_addr); + + if (err) { + nm_log_err (LOGD_DEVICE | LOGD_IP4, + "(%s): failed to set IPv4 route: %s", + iface, nl_geterror ()); + rtnl_route_put (route); + route = NULL; + } + + return route; +} + +static gboolean +sync_addresses (const char *iface, int ifindex, int family, + struct rtnl_addr **addrs, int num_addrs) +{ + struct nl_handle *nlh; + struct nl_cache *addr_cache; + struct rtnl_addr *filter_addr, *match_addr; + struct nl_object *match; + struct nl_addr *nladdr; + int i, err; + guint32 log_domain = (family == AF_INET) ? LOGD_IP4 : LOGD_IP6; + char buf[INET6_ADDRSTRLEN + 1]; + + log_domain |= LOGD_DEVICE; + + nlh = nm_netlink_get_default_handle (); + if (!nlh) + return FALSE; + + addr_cache = rtnl_addr_alloc_cache (nlh); + if (!addr_cache) + return FALSE; + + filter_addr = rtnl_addr_alloc (); + if (!filter_addr) { + nl_cache_free (addr_cache); + return FALSE; + } + rtnl_addr_set_ifindex (filter_addr, ifindex); + if (family) + rtnl_addr_set_family (filter_addr, family); + + nm_log_dbg (log_domain, "(%s): syncing addresses (family %d)", iface, family); + + /* Walk through the cache, comparing the addresses already on + * the interface to the addresses in addrs. + */ + for (match = nl_cache_get_first (addr_cache); match; match = nl_cache_get_next (match)) { + gboolean buf_valid = FALSE; + match_addr = (struct rtnl_addr *) match; + + /* Skip addresses not on our interface */ + if (!nl_object_match_filter (match, (struct nl_object *) filter_addr)) + continue; + + if (addrs) { + for (i = 0; i < num_addrs; i++) { + if (addrs[i] && nl_object_identical (match, (struct nl_object *) addrs[i])) + break; + } + + if (addrs[i]) { + /* match == addrs[i], so remove it from addrs so we don't + * try to add it to the interface again below. + */ + rtnl_addr_put (addrs[i]); + addrs[i] = NULL; + continue; + } + } + + nladdr = rtnl_addr_get_local (match_addr); + + /* Don't delete IPv6 link-local addresses; they don't belong to NM */ + if (rtnl_addr_get_family (match_addr) == AF_INET6) { + struct in6_addr *tmp; + + if (rtnl_addr_get_scope (match_addr) == RT_SCOPE_LINK) { + nm_log_dbg (log_domain, "(%s): ignoring IPv6 link-local address", iface); + continue; + } + + tmp = nl_addr_get_binary_addr (nladdr); + if (inet_ntop (AF_INET6, tmp, buf, sizeof (buf))) + buf_valid = TRUE; + } else if (rtnl_addr_get_family (match_addr) == AF_INET) { + struct in_addr *tmp; + + tmp = nl_addr_get_binary_addr (nladdr); + if (inet_ntop (AF_INET, tmp, buf, sizeof (buf))) + buf_valid = TRUE; + } + + if (buf_valid) { + nm_log_dbg (log_domain, "(%s): removing address '%s/%d'", + iface, buf, nl_addr_get_prefixlen (nladdr)); + } + + /* Otherwise, match_addr should be removed from the interface. */ + err = rtnl_addr_delete (nlh, match_addr, 0); + if (err < 0) { + nm_log_err (log_domain, "(%s): error %d returned from rtnl_addr_delete(): %s", + iface, err, nl_geterror ()); + } + } + + rtnl_addr_put (filter_addr); + nl_cache_free (addr_cache); + + /* Now add the remaining new addresses */ + for (i = 0; i < num_addrs; i++) { + struct in6_addr *in6tmp; + struct in_addr *in4tmp; + gboolean buf_valid = FALSE; + + if (!addrs[i]) + continue; + + nladdr = rtnl_addr_get_local (addrs[i]); + if (rtnl_addr_get_family (addrs[i]) == AF_INET6) { + in6tmp = nl_addr_get_binary_addr (nladdr); + if (inet_ntop (AF_INET6, in6tmp, buf, sizeof (buf))) + buf_valid = TRUE; + } else if (rtnl_addr_get_family (addrs[i]) == AF_INET) { + in4tmp = nl_addr_get_binary_addr (nladdr); + if (inet_ntop (AF_INET, in4tmp, buf, sizeof (buf))) + buf_valid = TRUE; + } + + if (buf_valid) { + nm_log_dbg (log_domain, "(%s): adding address '%s/%d'", + iface, buf, nl_addr_get_prefixlen (nladdr)); + } + + err = rtnl_addr_add (nlh, addrs[i], 0); + if (err < 0) { + nm_log_err (log_domain, + "(%s): error %d returned from rtnl_addr_add():\n%s", + iface, err, nl_geterror ()); + } + + rtnl_addr_put (addrs[i]); + } + g_free (addrs); + + return TRUE; +} + +static gboolean +add_ip4_addresses (NMIP4Config *config, const char *iface) +{ + int num_addrs, i, iface_idx; + guint32 flags = 0; + gboolean did_gw = FALSE; + struct rtnl_addr **addrs; + + iface_idx = nm_netlink_iface_to_index (iface); + + num_addrs = nm_ip4_config_get_num_addresses (config); + addrs = g_new0 (struct rtnl_addr *, num_addrs + 1); + + for (i = 0; i < num_addrs; i++) { + NMIP4Address *addr; + + addr = nm_ip4_config_get_address (config, i); + g_assert (addr); + + flags = NM_RTNL_ADDR_DEFAULT; + if (nm_ip4_address_get_gateway (addr) && !did_gw) { + if (nm_ip4_config_get_ptp_address (config)) + flags |= NM_RTNL_ADDR_PTP_ADDR; + did_gw = TRUE; + } + + addrs[i] = nm_ip4_config_to_rtnl_addr (config, i, flags); + if (!addrs[i]) { + nm_log_warn (LOGD_DEVICE | LOGD_IP4, + "(%s): couldn't create rtnl address!", + iface); + continue; + } + rtnl_addr_set_ifindex (addrs[i], iface_idx); + } + + return sync_addresses (iface, iface_idx, AF_INET, addrs, num_addrs); +} + +struct rtnl_route * +nm_system_add_ip4_vpn_gateway_route (NMDevice *parent_device, NMIP4Config *vpn_config) +{ + NMIP4Config *parent_config; + guint32 parent_gw = 0, parent_prefix = 0, vpn_gw = 0, i; + NMIP4Address *tmp; + struct rtnl_route *route = NULL; + + g_return_val_if_fail (NM_IS_DEVICE (parent_device), NULL); + + /* Set up a route to the VPN gateway's public IP address through the default + * network device if the VPN gateway is on a different subnet. + */ + + parent_config = nm_device_get_ip4_config (parent_device); + g_return_val_if_fail (parent_config != NULL, NULL); + + for (i = 0; i < nm_ip4_config_get_num_addresses (parent_config); i++) { + tmp = nm_ip4_config_get_address (parent_config, i); + if (nm_ip4_address_get_gateway (tmp)) { + parent_gw = nm_ip4_address_get_gateway (tmp); + parent_prefix = nm_ip4_address_get_prefix (tmp); + break; + } + } + + for (i = 0; i < nm_ip4_config_get_num_addresses (vpn_config); i++) { + tmp = nm_ip4_config_get_address (vpn_config, i); + if (nm_ip4_address_get_gateway (tmp)) { + vpn_gw = nm_ip4_address_get_gateway (tmp); + break; + } + } + + if (!parent_gw || !vpn_gw) + return NULL; + + /* If the VPN gateway is in the same subnet as one of the parent device's + * IP addresses, don't add the host route to it, but a route through the + * parent device. + */ + if (ip4_dest_in_same_subnet (parent_config, vpn_gw, parent_prefix)) { + route = nm_system_device_set_ip4_route (nm_device_get_ip_iface (parent_device), + vpn_gw, 32, 0, 0, nm_ip4_config_get_mss (parent_config)); + } else { + route = nm_system_device_set_ip4_route (nm_device_get_ip_iface (parent_device), + vpn_gw, 32, parent_gw, 0, nm_ip4_config_get_mss (parent_config)); + } + + return route; +} + +/* + * nm_system_apply_ip4_config + * + * Set IPv4 configuration of the device from an NMIP4Config object. + * + */ +gboolean +nm_system_apply_ip4_config (const char *iface, + NMIP4Config *config, + int priority, + NMIP4ConfigCompareFlags flags) +{ + int i; + + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (config != NULL, FALSE); + + if (flags & NM_IP4_COMPARE_FLAG_ADDRESSES) { + if (!add_ip4_addresses (config, iface)) + return FALSE; + sleep (1); + } + + if (flags & NM_IP4_COMPARE_FLAG_ROUTES) { + for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) { + NMIP4Route *route = nm_ip4_config_get_route (config, i); + struct rtnl_route *tmp; + + /* Don't add the route if it's more specific than one of the subnets + * the device already has an IP address on. + */ + if (ip4_dest_in_same_subnet (config, + nm_ip4_route_get_dest (route), + nm_ip4_route_get_prefix (route))) + continue; + + /* Don't add the route if it doesn't have a gateway and the connection + * is never supposed to be the default connection. + */ + if ( nm_ip4_config_get_never_default (config) + && nm_ip4_route_get_dest (route) == 0) + continue; + + tmp = nm_system_device_set_ip4_route (iface, + nm_ip4_route_get_dest (route), + nm_ip4_route_get_prefix (route), + nm_ip4_route_get_next_hop (route), + nm_ip4_route_get_metric (route), + nm_ip4_config_get_mss (config)); + rtnl_route_put (tmp); + } + } + + if (flags & NM_IP4_COMPARE_FLAG_MTU) { + if (nm_ip4_config_get_mtu (config)) + nm_system_device_set_mtu (iface, nm_ip4_config_get_mtu (config)); + } + + if (priority > 0) + nm_system_device_set_priority (iface, config, priority); + + return TRUE; +} + +static struct rtnl_route * +nm_system_device_set_ip6_route (const char *iface, + const struct in6_addr *ip6_dest, + guint32 ip6_prefix, + const struct in6_addr *ip6_gateway, + guint32 metric, + int mss) +{ + struct nl_handle *nlh; + struct rtnl_route *route; + struct nl_addr *dest_addr; + struct nl_addr *gw_addr = NULL; + int err, iface_idx; + + nlh = nm_netlink_get_default_handle (); + g_return_val_if_fail (nlh != NULL, NULL); + + iface_idx = nm_netlink_iface_to_index (iface); + g_return_val_if_fail (iface_idx >= 0, NULL); + + route = create_route (iface_idx, mss); + g_return_val_if_fail (route != NULL, NULL); + + /* Destination */ + dest_addr = nl_addr_build (AF_INET6, (struct in6_addr *) ip6_dest, sizeof (*ip6_dest)); + g_return_val_if_fail (dest_addr != NULL, NULL); + nl_addr_set_prefixlen (dest_addr, (int) ip6_prefix); + + rtnl_route_set_dst (route, dest_addr); + nl_addr_put (dest_addr); + + /* Gateway */ + if (ip6_gateway && !IN6_IS_ADDR_UNSPECIFIED (ip6_gateway)) { + gw_addr = nl_addr_build (AF_INET6, (struct in6_addr *) ip6_gateway, sizeof (*ip6_gateway)); + if (gw_addr) { + rtnl_route_set_gateway (route, gw_addr); + rtnl_route_set_scope (route, RT_SCOPE_UNIVERSE); + } else { + nm_log_warn (LOGD_DEVICE | LOGD_IP6, "Invalid gateway"); + rtnl_route_put (route); + return NULL; + } + } + + /* Metric */ + if (metric) + rtnl_route_set_prio (route, metric); + + /* Add the route */ + err = rtnl_route_add (nlh, route, 0); + if (err == -ESRCH && ip6_gateway) { + /* Gateway might be over a bridge; try adding a route to gateway first */ + struct rtnl_route *route2; + + route2 = create_route (iface_idx, mss); + if (route2) { + /* Add route to gateway over bridge */ + rtnl_route_set_dst (route2, gw_addr); + err = rtnl_route_add (nlh, route2, 0); + if (!err) { + /* Try adding the route again */ + err = rtnl_route_add (nlh, route, 0); + if (err) + rtnl_route_del (nlh, route2, 0); + } + rtnl_route_put (route2); + } + } + + if (gw_addr) + nl_addr_put (gw_addr); + + if (err) { + nm_log_err (LOGD_DEVICE | LOGD_IP6, + "(%s): failed to set IPv6 route: %s", + iface, nl_geterror ()); + rtnl_route_put (route); + route = NULL; + } + + return route; +} + +static gboolean +add_ip6_addresses (NMIP6Config *config, const char *iface) +{ + int num_addrs, i, iface_idx; + struct rtnl_addr **addrs; + + iface_idx = nm_netlink_iface_to_index (iface); + + num_addrs = nm_ip6_config_get_num_addresses (config); + addrs = g_new0 (struct rtnl_addr *, num_addrs + 1); + + for (i = 0; i < num_addrs; i++) { + NMIP6Address *addr; + + addr = nm_ip6_config_get_address (config, i); + g_assert (addr); + + addrs[i] = nm_ip6_config_to_rtnl_addr (config, i, NM_RTNL_ADDR_DEFAULT); + if (!addrs[i]) { + nm_log_warn (LOGD_DEVICE | LOGD_IP6, + "(%s): couldn't create rtnl address!", + iface); + continue; + } + rtnl_addr_set_ifindex (addrs[i], iface_idx); + } + + return sync_addresses (iface, iface_idx, AF_INET6, addrs, num_addrs); +} + +/* + * nm_system_apply_ip6_config + * + * Set IPv6 configuration of the device from an NMIP6Config object. + * + */ +gboolean +nm_system_apply_ip6_config (const char *iface, + NMIP6Config *config, + int priority, + NMIP6ConfigCompareFlags flags) +{ + int i; + + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (config != NULL, FALSE); + + if (flags & NM_IP6_COMPARE_FLAG_ADDRESSES) { + if (!add_ip6_addresses (config, iface)) + return FALSE; + sleep (1); // FIXME? + } + + if (flags & NM_IP6_COMPARE_FLAG_ROUTES) { + for (i = 0; i < nm_ip6_config_get_num_routes (config); i++) { + NMIP6Route *route = nm_ip6_config_get_route (config, i); + struct rtnl_route *tmp; + + /* Don't add the route if it doesn't have a gateway and the connection + * is never supposed to be the default connection. + */ + if ( nm_ip6_config_get_never_default (config) + && IN6_IS_ADDR_UNSPECIFIED(nm_ip6_route_get_dest (route))) + continue; + + tmp = nm_system_device_set_ip6_route (iface, + nm_ip6_route_get_dest (route), + nm_ip6_route_get_prefix (route), + nm_ip6_route_get_next_hop (route), + nm_ip6_route_get_metric (route), + nm_ip6_config_get_mss (config)); + rtnl_route_put (tmp); + } + } + +// FIXME +// if (priority > 0) +// nm_system_device_set_priority (iface, config, priority); + + return TRUE; +} + +/* + * nm_system_device_set_up_down + * + * Mark the device as up or down. + * + */ +gboolean +nm_system_device_set_up_down (NMDevice *dev, + gboolean up, + gboolean *no_firmware) +{ + g_return_val_if_fail (dev != NULL, FALSE); + + return nm_system_device_set_up_down_with_iface (nm_device_get_ip_iface (dev), up, no_firmware); +} + +gboolean +nm_system_device_set_up_down_with_iface (const char *iface, + gboolean up, + gboolean *no_firmware) +{ + struct rtnl_link *request = NULL, *old = NULL; + struct nl_handle *nlh; + gboolean success = FALSE; + guint32 idx; + + g_return_val_if_fail (iface != NULL, FALSE); + if (no_firmware) + g_return_val_if_fail (*no_firmware == FALSE, FALSE); + + if (!(request = rtnl_link_alloc ())) + goto out; + + if (up) + rtnl_link_set_flags (request, IFF_UP); + else + rtnl_link_unset_flags (request, IFF_UP); + + idx = nm_netlink_iface_to_index (iface); + old = nm_netlink_index_to_rtnl_link (idx); + if (old) { + nlh = nm_netlink_get_default_handle (); + if (nlh) { + if (rtnl_link_change (nlh, old, request, 0) == 0) + success = TRUE; + else if ((nl_get_errno () == ENOENT) && no_firmware && up) + *no_firmware = TRUE; + } + } + + rtnl_link_put (old); + rtnl_link_put (request); + +out: + return success; +} + +gboolean +nm_system_device_is_up (NMDevice *device) +{ + g_return_val_if_fail (device != NULL, FALSE); + + return nm_system_device_is_up_with_iface (nm_device_get_ip_iface (device)); +} + +gboolean +nm_system_device_is_up_with_iface (const char *iface) +{ + struct ifreq ifr; + int fd; + gboolean up = FALSE; + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_err (LOGD_HW, "couldn't open control socket."); + return FALSE; + } + + /* Get device's flags */ + memset (&ifr, 0, sizeof (ifr)); + strncpy (ifr.ifr_name, iface, IFNAMSIZ); + if (ioctl (fd, SIOCGIFFLAGS, &ifr) < 0) { + if (errno != ENODEV) { + nm_log_err (LOGD_HW, "(%s): could not get flags: errno %d", + iface, errno); + } + } else { + up = !!(ifr.ifr_flags & IFF_UP); + } + close (fd); + + return up; +} + +gboolean +nm_system_device_set_mtu (const char *iface, guint32 mtu) +{ + struct rtnl_link *old; + struct rtnl_link *new; + gboolean success = FALSE; + struct nl_handle *nlh; + int iface_idx; + + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (mtu > 0, FALSE); + + new = rtnl_link_alloc (); + if (!new) + return FALSE; + + iface_idx = nm_netlink_iface_to_index (iface); + old = nm_netlink_index_to_rtnl_link (iface_idx); + if (old) { + rtnl_link_set_mtu (new, mtu); + nlh = nm_netlink_get_default_handle (); + if (nlh) { + rtnl_link_change (nlh, old, new, 0); + success = TRUE; + } + rtnl_link_put (old); + } + + rtnl_link_put (new); + return success; +} + +static struct rtnl_route * +add_ip4_route_to_gateway (const char *iface, guint32 gw, guint32 mss) +{ + struct nl_handle *nlh; + struct rtnl_route *route = NULL; + struct nl_addr *gw_addr = NULL; + int iface_idx, err; + + nlh = nm_netlink_get_default_handle (); + g_return_val_if_fail (nlh != NULL, NULL); + + iface_idx = nm_netlink_iface_to_index (iface); + if (iface_idx < 0) + return NULL; + + /* Gateway might be over a bridge; try adding a route to gateway first */ + route = rtnl_route_alloc (); + if (route == NULL) + return NULL; + + rtnl_route_set_family (route, AF_INET); + rtnl_route_set_table (route, RT_TABLE_MAIN); + rtnl_route_set_oif (route, iface_idx); + rtnl_route_set_scope (route, RT_SCOPE_LINK); + + gw_addr = nl_addr_build (AF_INET, &gw, sizeof (gw)); + if (!gw_addr) + goto error; + nl_addr_set_prefixlen (gw_addr, 32); + rtnl_route_set_dst (route, gw_addr); + nl_addr_put (gw_addr); + + if (mss) { + if (rtnl_route_set_metric (route, RTAX_ADVMSS, mss) < 0) + goto error; + } + + /* Add direct route to the gateway */ + err = rtnl_route_add (nlh, route, 0); + if (err) { + nm_log_err (LOGD_DEVICE | LOGD_IP4, + "(%s): failed to add IPv4 route to gateway (%d)", + iface, err); + goto error; + } + + return route; + +error: + rtnl_route_put (route); + return NULL; +} + +static int +replace_default_ip4_route (const char *iface, guint32 gw, guint32 mss) +{ + struct rtnl_route *route = NULL; + struct nl_handle *nlh; + struct nl_addr *dst_addr = NULL; + guint32 dst = 0; + struct nl_addr *gw_addr = NULL; + int iface_idx, err = -1; + + g_return_val_if_fail (iface != NULL, -ENODEV); + + nlh = nm_netlink_get_default_handle (); + g_return_val_if_fail (nlh != NULL, -ENOMEM); + + iface_idx = nm_netlink_iface_to_index (iface); + if (iface_idx < 0) + return -ENODEV; + + route = rtnl_route_alloc(); + g_return_val_if_fail (route != NULL, -ENOMEM); + + rtnl_route_set_family (route, AF_INET); + rtnl_route_set_table (route, RT_TABLE_MAIN); + rtnl_route_set_scope (route, RT_SCOPE_UNIVERSE); + rtnl_route_set_oif (route, iface_idx); + + /* Build up the destination address */ + dst_addr = nl_addr_build (AF_INET, &dst, sizeof (dst)); + if (!dst_addr) { + err = -ENOMEM; + goto out; + } + nl_addr_set_prefixlen (dst_addr, 0); + rtnl_route_set_dst (route, dst_addr); + + /* Build up the gateway address */ + gw_addr = nl_addr_build (AF_INET, &gw, sizeof (gw)); + if (!gw_addr) { + err = -ENOMEM; + goto out; + } + nl_addr_set_prefixlen (gw_addr, 0); + rtnl_route_set_gateway (route, gw_addr); + + if (mss > 0) { + err = rtnl_route_set_metric (route, RTAX_ADVMSS, mss); + if (err < 0) + goto out; + } + + /* Add the new default route */ + err = rtnl_route_add (nlh, route, NLM_F_REPLACE); + +out: + if (dst_addr) + nl_addr_put (dst_addr); + if (gw_addr) + nl_addr_put (gw_addr); + rtnl_route_put (route); + return err; +} + +/* + * nm_system_replace_default_ip4_route_vpn + * + * Replace default IPv4 route with one via the current device + * + */ +gboolean +nm_system_replace_default_ip4_route_vpn (const char *iface, + guint32 ext_gw, + guint32 int_gw, + guint32 mss, + const char *parent_iface, + guint32 parent_mss) +{ + struct rtnl_route *gw_route = NULL; + struct nl_handle *nlh; + gboolean success = FALSE; + int err; + + nlh = nm_netlink_get_default_handle (); + g_return_val_if_fail (nlh != NULL, FALSE); + + err = replace_default_ip4_route (iface, int_gw, mss); + if (err == 0) { + return TRUE; + } else if (err != -ESRCH) { + nm_log_err (LOGD_DEVICE | LOGD_IP4, + "(%s): failed to set IPv4 default route: %d", + iface, err); + return FALSE; + } + + /* Try adding a direct route to the gateway first */ + gw_route = add_ip4_route_to_gateway (parent_iface, ext_gw, parent_mss); + if (!gw_route) + return FALSE; + + /* Try adding the original route again */ + err = replace_default_ip4_route (iface, int_gw, mss); + if (err != 0) { + rtnl_route_del (nlh, gw_route, 0); + nm_log_err (LOGD_DEVICE | LOGD_IP4, + "(%s): failed to set IPv4 default route (pass #2): %d", + iface, err); + } else + success = TRUE; + + rtnl_route_put (gw_route); + return success; +} + +/* + * nm_system_replace_default_ip4_route + * + * Replace default IPv4 route with one via the current device + * + */ +gboolean +nm_system_replace_default_ip4_route (const char *iface, guint32 gw, guint32 mss) +{ + struct rtnl_route *gw_route = NULL; + struct nl_handle *nlh; + gboolean success = FALSE; + int err; + + nlh = nm_netlink_get_default_handle (); + g_return_val_if_fail (nlh != NULL, FALSE); + + err = replace_default_ip4_route (iface, gw, mss); + if (err == 0) { + return TRUE; + } else if (err != -ESRCH) { + nm_log_err (LOGD_DEVICE | LOGD_IP4, + "(%s): failed to set IPv4 default route: %d", + iface, err); + return FALSE; + } + + /* Try adding a direct route to the gateway first */ + gw_route = add_ip4_route_to_gateway (iface, gw, mss); + if (!gw_route) + return FALSE; + + /* Try adding the original route again */ + err = replace_default_ip4_route (iface, gw, mss); + if (err != 0) { + rtnl_route_del (nlh, gw_route, 0); + nm_log_err (LOGD_DEVICE | LOGD_IP4, + "(%s): failed to set IPv4 default route (pass #2): %d", + iface, err); + } else + success = TRUE; + + rtnl_route_put (gw_route); + return success; +} + +static struct rtnl_route * +add_ip6_route_to_gateway (const char *iface, const struct in6_addr *gw) +{ + struct nl_handle *nlh; + struct rtnl_route *route = NULL; + struct nl_addr *gw_addr = NULL; + int iface_idx, err; + + nlh = nm_netlink_get_default_handle (); + g_return_val_if_fail (nlh != NULL, NULL); + + iface_idx = nm_netlink_iface_to_index (iface); + if (iface_idx < 0) + return NULL; + + /* Gateway might be over a bridge; try adding a route to gateway first */ + route = rtnl_route_alloc (); + if (route == NULL) + return NULL; + + rtnl_route_set_family (route, AF_INET6); + rtnl_route_set_table (route, RT_TABLE_MAIN); + rtnl_route_set_oif (route, iface_idx); + rtnl_route_set_scope (route, RT_SCOPE_LINK); + + gw_addr = nl_addr_build (AF_INET, (void *) gw, sizeof (*gw)); + if (!gw_addr) + goto error; + nl_addr_set_prefixlen (gw_addr, 128); + rtnl_route_set_dst (route, gw_addr); + nl_addr_put (gw_addr); + + /* Add direct route to the gateway */ + err = rtnl_route_add (nlh, route, 0); + if (err) { + nm_log_err (LOGD_DEVICE | LOGD_IP6, + "(%s): failed to add IPv4 route to gateway (%d)", + iface, err); + goto error; + } + + return route; + +error: + rtnl_route_put (route); + return NULL; +} + +static int +replace_default_ip6_route (const char *iface, const struct in6_addr *gw) +{ + struct rtnl_route *route = NULL; + struct nl_handle *nlh; + struct nl_addr *gw_addr = NULL; + int iface_idx, err = -1; + + g_return_val_if_fail (iface != NULL, -ENODEV); + + nlh = nm_netlink_get_default_handle (); + g_return_val_if_fail (nlh != NULL, -ENOMEM); + + iface_idx = nm_netlink_iface_to_index (iface); + if (iface_idx < 0) + return -ENODEV; + + route = rtnl_route_alloc(); + g_return_val_if_fail (route != NULL, -ENOMEM); + + rtnl_route_set_family (route, AF_INET6); + rtnl_route_set_table (route, RT_TABLE_MAIN); + rtnl_route_set_scope (route, RT_SCOPE_UNIVERSE); + rtnl_route_set_oif (route, iface_idx); + + if (gw && !IN6_IS_ADDR_UNSPECIFIED (gw)) { + /* Build up the gateway address */ + gw_addr = nl_addr_build (AF_INET6, (void *) gw, sizeof (*gw)); + if (!gw_addr) { + err = -ENOMEM; + goto out; + } + nl_addr_set_prefixlen (gw_addr, -1); + rtnl_route_set_gateway (route, gw_addr); + } + + /* Add the new default route */ + err = rtnl_route_add (nlh, route, NLM_F_REPLACE); + if (err == -EEXIST) { + /* FIXME: even though we use NLM_F_REPLACE the kernel won't replace + * the route if it's the same. Should try to remove it first, then + * add the new one again here. + */ + err = 0; + } + +out: + if (gw_addr) + nl_addr_put (gw_addr); + rtnl_route_put (route); + return err; +} + +/* + * nm_system_replace_default_ip6_route + * + * Replace default IPv6 route with one via the given gateway + * + */ +gboolean +nm_system_replace_default_ip6_route (const char *iface, const struct in6_addr *gw) +{ + struct rtnl_route *gw_route = NULL; + struct nl_handle *nlh; + gboolean success = FALSE; + int err; + + nlh = nm_netlink_get_default_handle (); + g_return_val_if_fail (nlh != NULL, FALSE); + + err = replace_default_ip6_route (iface, gw); + if (err == 0) { + return TRUE; + } else if (err != -ESRCH) { + nm_log_err (LOGD_DEVICE | LOGD_IP6, + "(%s): failed to set IPv6 default route: %d", + iface, err); + return FALSE; + } + + /* Try adding a direct route to the gateway first */ + gw_route = add_ip6_route_to_gateway (iface, gw); + if (!gw_route) + return FALSE; + + /* Try adding the original route again */ + err = replace_default_ip6_route (iface, gw); + if (err != 0) { + rtnl_route_del (nlh, gw_route, 0); + nm_log_err (LOGD_DEVICE | LOGD_IP6, + "(%s): failed to set IPv6 default route (pass #2): %d", + iface, err); + } else + success = TRUE; + + rtnl_route_put (gw_route); + return success; +} + +static void flush_addresses (const char *iface, gboolean ipv4_only) +{ + int iface_idx; + + g_return_if_fail (iface != NULL); + iface_idx = nm_netlink_iface_to_index (iface); + if (iface_idx >= 0) + sync_addresses (iface, iface_idx, ipv4_only ? AF_INET : AF_UNSPEC, NULL, 0); +} + +/* + * nm_system_device_flush_addresses + * + * Flush all network addresses associated with a network device + * + */ +void nm_system_device_flush_addresses (NMDevice *dev) +{ + g_return_if_fail (dev != NULL); + + flush_addresses (nm_device_get_ip_iface (dev), + nm_device_get_ip6_config (dev) == NULL); +} + + +/* + * nm_system_device_flush_addresses_with_iface + * + * Flush all network addresses associated with a network device + * + */ +void nm_system_device_flush_addresses_with_iface (const char *iface) +{ + flush_addresses (iface, FALSE); +} + + +static void +foreach_route (void (*callback)(struct nl_object *, gpointer), + gpointer user_data) +{ + struct nl_handle *nlh; + struct nl_cache *route_cache; + + nlh = nm_netlink_get_default_handle (); + route_cache = rtnl_route_alloc_cache (nlh); + g_assert (route_cache); + nl_cache_foreach (route_cache, callback, user_data); + nl_cache_free (route_cache); +} + + +typedef struct { + const char *iface; + int iface_idx; + int family; +} RouteCheckData; + +static void +check_one_route (struct nl_object *object, void *user_data) +{ + RouteCheckData *data = (RouteCheckData *) user_data; + struct rtnl_route *route = (struct rtnl_route *) object; + int err; + + /* Delete all routes from this interface */ + if (rtnl_route_get_oif (route) != data->iface_idx) + return; + if (data->family && rtnl_route_get_family (route) != data->family) + return; + + /* We don't want to flush IPv6 link-local routes that may exist on the + * the interface since the LL address and routes should normally stay + * assigned all the time. + */ + if ( (data->family == AF_INET6 || data->family == AF_UNSPEC) + && (rtnl_route_get_family (route) == AF_INET6)) { + struct nl_addr *nl; + struct in6_addr *addr = NULL; + + nl = rtnl_route_get_dst (route); + if (nl) + addr = nl_addr_get_binary_addr (nl); + + if (addr) { + if (IN6_IS_ADDR_LINKLOCAL (addr) || IN6_IS_ADDR_MC_LINKLOCAL (addr)) + return; + } + } + + err = rtnl_route_del (nm_netlink_get_default_handle (), route, 0); + if (err < 0) { + nm_log_err (LOGD_DEVICE, + "(%s): error %d returned from rtnl_route_del(): %s", + data->iface, err, nl_geterror()); + } +} + +static void flush_routes (int ifindex, const char *iface, int family) +{ + RouteCheckData check_data; + + g_return_if_fail (iface != NULL); + + if (ifindex < 0) { + ifindex = nm_netlink_iface_to_index (iface); + if (ifindex < 0) { + nm_log_dbg (LOGD_DEVICE, "(%s) failed to lookup interface index", iface); + return; + } + } + + memset (&check_data, 0, sizeof (check_data)); + check_data.iface = iface; + check_data.iface_idx = ifindex; + check_data.family = family; + foreach_route (check_one_route, &check_data); +} + +/* + * nm_system_device_flush_routes + * + * Flush all network addresses associated with a network device + * + */ +void nm_system_device_flush_routes (NMDevice *dev, int family) +{ + g_return_if_fail (dev != NULL); + + flush_routes (nm_device_get_ip_ifindex (dev), + nm_device_get_ip_iface (dev), + family); +} + +/* + * nm_system_device_flush_routes_with_iface + * + * Flush all routes associated with a network device. 'family' is an + * address family, either AF_INET, AF_INET6, or AF_UNSPEC. + * + */ +void nm_system_device_flush_routes_with_iface (const char *iface, int family) +{ + flush_routes (-1, iface, family); +} + +typedef struct { + struct rtnl_route *route; + NMIP4Config *config; + int iface; +} SetPriorityInfo; + +static void +find_route (struct nl_object *object, gpointer user_data) +{ + struct rtnl_route *route = (struct rtnl_route *) object; + SetPriorityInfo *info = (SetPriorityInfo *) user_data; + struct nl_addr *dst; + struct in_addr *dst_addr; + int num; + int i; + + if (info->route || + rtnl_route_get_oif (route) != info->iface || + rtnl_route_get_scope (route) != RT_SCOPE_LINK) + return; + + dst = rtnl_route_get_dst (route); + if (nl_addr_get_family (dst) != AF_INET) + return; + + dst_addr = nl_addr_get_binary_addr (dst); + num = nm_ip4_config_get_num_addresses (info->config); + for (i = 0; i < num; i++) { + NMIP4Address *addr = nm_ip4_config_get_address (info->config, i); + guint32 prefix = nm_ip4_address_get_prefix (addr); + guint32 address = nm_ip4_address_get_address (addr); + + if (prefix == nl_addr_get_prefixlen (dst) && + (address & nm_utils_ip4_prefix_to_netmask (prefix)) == dst_addr->s_addr) { + + /* Ref the route so it sticks around after the cache is cleared */ + rtnl_route_get (route); + info->route = route; + break; + } + } +} + +static void +nm_system_device_set_priority (const char *iface, + NMIP4Config *config, + int priority) +{ + SetPriorityInfo info; + + info.route = NULL; + info.config = config; + info.iface = nm_netlink_iface_to_index (iface); + g_return_if_fail (info.iface >= 0); + + foreach_route (find_route, &info); + if (info.route) { + struct nl_handle *nlh; + + nlh = nm_netlink_get_default_handle (); + rtnl_route_del (nlh, info.route, 0); + + rtnl_route_set_prio (info.route, priority); + rtnl_route_add (nlh, info.route, 0); + rtnl_route_put (info.route); + } +} diff --git a/src/nm-system.h b/src/nm-system.h new file mode 100644 index 00000000..2eee0141 --- /dev/null +++ b/src/nm-system.h @@ -0,0 +1,84 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2004 - 2008 Red Hat, Inc. + * Copyright (C) 2005 - 2008 Novell, Inc. + */ + +#ifndef NETWORK_MANAGER_SYSTEM_H +#define NETWORK_MANAGER_SYSTEM_H + +#include <netlink/route/rtnl.h> +#include <netlink/route/route.h> + +#include <glib.h> +#include "nm-device.h" +#include "nm-ip4-config.h" + +/* Prototypes for system/distribution dependent functions, + * implemented in the backend files in backends/ directory + */ + +void nm_system_device_flush_routes (NMDevice *dev, int family); +void nm_system_device_flush_routes_with_iface (const char *iface, int family); + +gboolean nm_system_replace_default_ip4_route (const char *iface, + guint32 gw, + guint32 mss); + +gboolean nm_system_replace_default_ip6_route (const char *iface, + const struct in6_addr *gw); + +gboolean nm_system_replace_default_ip4_route_vpn (const char *iface, + guint32 ext_gw, + guint32 int_gw, + guint32 mss, + const char *parent_iface, + guint32 parent_mss); + +struct rtnl_route *nm_system_add_ip4_vpn_gateway_route (NMDevice *parent_device, NMIP4Config *vpn_config); + + +void nm_system_device_flush_addresses (NMDevice *dev); +void nm_system_device_flush_addresses_with_iface (const char *iface); + +void nm_system_enable_loopback (void); +void nm_system_update_dns (void); + +gboolean nm_system_apply_ip4_config (const char *iface, + NMIP4Config *config, + int priority, + NMIP4ConfigCompareFlags flags); + +gboolean nm_system_apply_ip6_config (const char *iface, + NMIP6Config *config, + int priority, + NMIP6ConfigCompareFlags flags); + +gboolean nm_system_device_set_up_down (NMDevice *dev, + gboolean up, + gboolean *no_firmware); +gboolean nm_system_device_set_up_down_with_iface (const char *iface, + gboolean up, + gboolean *no_firmware); + +gboolean nm_system_device_is_up (NMDevice *device); +gboolean nm_system_device_is_up_with_iface (const char *iface); + +gboolean nm_system_device_set_mtu (const char *iface, guint32 mtu); + +#endif diff --git a/src/nm-udev-manager.c b/src/nm-udev-manager.c new file mode 100644 index 00000000..ff0ef68c --- /dev/null +++ b/src/nm-udev-manager.c @@ -0,0 +1,578 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#include <signal.h> +#include <string.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <unistd.h> + +#include "wireless-helper.h" + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "nm-udev-manager.h" +#include "nm-marshal.h" +#include "nm-logging.h" +#include "NetworkManagerUtils.h" +#include "nm-device-wifi.h" +#include "nm-device-olpc-mesh.h" +#include "nm-device-ethernet.h" + +typedef struct { + GUdevClient *client; + + /* Authoritative rfkill state (RFKILL_* enum) */ + RfKillState rfkill_states[RFKILL_TYPE_MAX]; + GSList *killswitches; + + gboolean disposed; +} NMUdevManagerPrivate; + +#define NM_UDEV_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_UDEV_MANAGER, NMUdevManagerPrivate)) + +G_DEFINE_TYPE (NMUdevManager, nm_udev_manager, G_TYPE_OBJECT) + +enum { + DEVICE_ADDED, + DEVICE_REMOVED, + RFKILL_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + + +typedef struct { + char *name; + guint64 seqnum; + char *path; + char *driver; + RfKillType rtype; + gint state; +} Killswitch; + +RfKillState +nm_udev_manager_get_rfkill_state (NMUdevManager *self, RfKillType rtype) +{ + g_return_val_if_fail (self != NULL, RFKILL_UNBLOCKED); + g_return_val_if_fail (rtype < RFKILL_TYPE_MAX, RFKILL_UNBLOCKED); + + return NM_UDEV_MANAGER_GET_PRIVATE (self)->rfkill_states[rtype]; +} + +static const char * +rfkill_type_to_desc (RfKillType rtype) +{ + if (rtype == 0) + return "WiFi"; + else if (rtype == 1) + return "WWan"; + else if (rtype == 2) + return "WiMAX"; + return "unknown"; +} + +static const char * +rfkill_state_to_desc (RfKillState rstate) +{ + if (rstate == 0) + return "unblocked"; + else if (rstate == 1) + return "soft-blocked"; + else if (rstate == 2) + return "hard-blocked"; + return "unknown"; +} + +static Killswitch * +killswitch_new (GUdevDevice *device, RfKillType rtype) +{ + Killswitch *ks; + GUdevDevice *parent = NULL; + const char *driver; + + ks = g_malloc0 (sizeof (Killswitch)); + ks->name = g_strdup (g_udev_device_get_name (device)); + ks->seqnum = g_udev_device_get_seqnum (device); + ks->path = g_strdup (g_udev_device_get_sysfs_path (device)); + ks->rtype = rtype; + + driver = g_udev_device_get_property (device, "DRIVER"); + if (!driver) { + parent = g_udev_device_get_parent (device); + if (parent) + driver = g_udev_device_get_property (parent, "DRIVER"); + } + if (driver) + ks->driver = g_strdup (driver); + + if (parent) + g_object_unref (parent); + + return ks; +} + +static void +killswitch_destroy (Killswitch *ks) +{ + g_return_if_fail (ks != NULL); + + g_free (ks->name); + g_free (ks->path); + g_free (ks->driver); + memset (ks, 0, sizeof (Killswitch)); + g_free (ks); +} + +NMUdevManager * +nm_udev_manager_new (void) +{ + return NM_UDEV_MANAGER (g_object_new (NM_TYPE_UDEV_MANAGER, NULL)); +} + +static RfKillState +sysfs_state_to_nm_state (gint sysfs_state) +{ + switch (sysfs_state) { + case 0: + return RFKILL_SOFT_BLOCKED; + case 1: + return RFKILL_UNBLOCKED; + case 2: + return RFKILL_HARD_BLOCKED; + default: + nm_log_warn (LOGD_RFKILL, "unhandled rfkill state %d", sysfs_state); + break; + } + return RFKILL_UNBLOCKED; +} + +static void +recheck_killswitches (NMUdevManager *self) +{ + NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self); + GSList *iter; + RfKillState poll_states[RFKILL_TYPE_MAX]; + int i; + + /* Default state is unblocked */ + for (i = 0; i < RFKILL_TYPE_MAX; i++) + poll_states[i] = RFKILL_UNBLOCKED; + + for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) { + Killswitch *ks = iter->data; + GUdevDevice *device; + RfKillState dev_state; + + device = g_udev_client_query_by_subsystem_and_name (priv->client, "rfkill", ks->name); + if (!device) + continue; + + dev_state = sysfs_state_to_nm_state (g_udev_device_get_property_as_int (device, "RFKILL_STATE")); + if (dev_state > poll_states[ks->rtype]) + poll_states[ks->rtype] = dev_state; + + g_object_unref (device); + } + + for (i = 0; i < RFKILL_TYPE_MAX; i++) { + if (poll_states[i] != priv->rfkill_states[i]) { + nm_log_dbg (LOGD_RFKILL, "%s rfkill state now '%s'", + rfkill_type_to_desc (i), + rfkill_state_to_desc (poll_states[i])); + + priv->rfkill_states[i] = poll_states[i]; + g_signal_emit (self, signals[RFKILL_CHANGED], 0, i, priv->rfkill_states[i]); + } + } +} + +static Killswitch * +killswitch_find_by_name (NMUdevManager *self, const char *name) +{ + NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self); + GSList *iter; + + g_return_val_if_fail (name != NULL, NULL); + + for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) { + Killswitch *candidate = iter->data; + + if (!strcmp (name, candidate->name)) + return candidate; + } + return NULL; +} + +static const RfKillType +rfkill_type_to_enum (const char *str) +{ + g_return_val_if_fail (str != NULL, RFKILL_TYPE_UNKNOWN); + + if (!strcmp (str, "wlan")) + return RFKILL_TYPE_WLAN; + else if (!strcmp (str, "wwan")) + return RFKILL_TYPE_WWAN; + else if (!strcmp (str, "wimax")) + return RFKILL_TYPE_WIMAX; + + return RFKILL_TYPE_UNKNOWN; +} + +static void +add_one_killswitch (NMUdevManager *self, GUdevDevice *device) +{ + NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self); + const char *str_type; + RfKillType rtype; + Killswitch *ks; + + str_type = g_udev_device_get_property (device, "RFKILL_TYPE"); + rtype = rfkill_type_to_enum (str_type); + if (rtype == RFKILL_TYPE_UNKNOWN) + return; + + ks = killswitch_new (device, rtype); + priv->killswitches = g_slist_prepend (priv->killswitches, ks); + + nm_log_info (LOGD_RFKILL, "found %s radio killswitch %s (at %s) (driver %s)", + rfkill_type_to_desc (rtype), + ks->name, + ks->path, + ks->driver ? ks->driver : "<unknown>"); +} + +static void +rfkill_add (NMUdevManager *self, GUdevDevice *device) +{ + const char *name; + + g_return_if_fail (device != NULL); + name = g_udev_device_get_name (device); + g_return_if_fail (name != NULL); + + if (!killswitch_find_by_name (self, name)) + add_one_killswitch (self, device); +} + +static void +rfkill_remove (NMUdevManager *self, + GUdevDevice *device) +{ + NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self); + GSList *iter; + const char *name; + + g_return_if_fail (device != NULL); + name = g_udev_device_get_name (device); + g_return_if_fail (name != NULL); + + for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) { + Killswitch *ks = iter->data; + + if (!strcmp (ks->name, name)) { + nm_log_info (LOGD_RFKILL, "radio killswitch %s disappeared", ks->path); + priv->killswitches = g_slist_remove (priv->killswitches, ks); + killswitch_destroy (ks); + break; + } + } +} + +static gboolean +is_wireless (GUdevDevice *device) +{ + char phy80211_path[255]; + struct stat s; + int fd; + struct iwreq iwr; + const char *ifname, *path; + gboolean is_wifi = FALSE; + + ifname = g_udev_device_get_name (device); + g_assert (ifname); + + fd = socket (PF_INET, SOCK_DGRAM, 0); + strncpy (iwr.ifr_ifrn.ifrn_name, ifname, IFNAMSIZ); + + path = g_udev_device_get_sysfs_path (device); + snprintf (phy80211_path, sizeof (phy80211_path), "%s/phy80211", path); + + if ( (ioctl (fd, SIOCGIWNAME, &iwr) == 0) + || (stat (phy80211_path, &s) == 0 && (s.st_mode & S_IFDIR))) + is_wifi = TRUE; + + close (fd); + return is_wifi; +} + +static gboolean +is_olpc_mesh (GUdevDevice *device) +{ + const gchar *prop = g_udev_device_get_property (device, "ID_NM_OLPC_MESH"); + return (prop != NULL); +} + +static GObject * +device_creator (NMUdevManager *manager, + GUdevDevice *udev_device, + gboolean sleeping) +{ + GObject *device = NULL; + const char *ifname, *driver, *path, *subsys; + GUdevDevice *parent = NULL, *grandparent = NULL; + gint ifindex; + + ifname = g_udev_device_get_name (udev_device); + g_assert (ifname); + + path = g_udev_device_get_sysfs_path (udev_device); + if (!path) { + nm_log_warn (LOGD_HW, "couldn't determine device path; ignoring..."); + return NULL; + } + + driver = g_udev_device_get_driver (udev_device); + if (!driver) { + /* Try the parent */ + parent = g_udev_device_get_parent (udev_device); + if (parent) { + driver = g_udev_device_get_driver (parent); + if (!driver) { + /* try the grandparent only if it's an ibmebus device */ + subsys = g_udev_device_get_subsystem (parent); + if (subsys && !strcmp (subsys, "ibmebus")) { + grandparent = g_udev_device_get_parent (parent); + if (grandparent) + driver = g_udev_device_get_driver (grandparent); + } + } + } + } + + if (!driver) { + nm_log_warn (LOGD_HW, "%s: couldn't determine device driver; ignoring...", path); + goto out; + } + + ifindex = g_udev_device_get_sysfs_attr_as_int (udev_device, "ifindex"); + if (ifindex <= 0) { + nm_log_warn (LOGD_HW, "%s: device had invalid ifindex %d; ignoring...", path, (guint32) ifindex); + goto out; + } + + if (is_olpc_mesh (udev_device)) /* must be before is_wireless */ + device = (GObject *) nm_device_olpc_mesh_new (path, ifname, driver); + else if (is_wireless (udev_device)) + device = (GObject *) nm_device_wifi_new (path, ifname, driver); + else + device = (GObject *) nm_device_ethernet_new (path, ifname, driver); + +out: + if (grandparent) + g_object_unref (grandparent); + if (parent) + g_object_unref (parent); + return device; +} + +static void +net_add (NMUdevManager *self, GUdevDevice *device) +{ + gint etype; + const char *iface; + const char *devtype; + + g_return_if_fail (device != NULL); + + etype = g_udev_device_get_sysfs_attr_as_int (device, "type"); + if (etype != 1) { + nm_log_dbg (LOGD_HW, "ignoring interface with type %d", etype); + return; /* Not using ethernet encapsulation, don't care */ + } + + /* Not all ethernet devices are immediately usable; newer mobile broadband + * devices (Ericsson, Option, Sierra) require setup on the tty before the + * ethernet device is usable. 2.6.33 and later kernels set the 'DEVTYPE' + * uevent variable which we can use to ignore the interface as a NMDevice + * subclass. ModemManager will pick it up though and so we'll handle it + * through the mobile broadband stuff. + */ + devtype = g_udev_device_get_property (device, "DEVTYPE"); + if (devtype && !strcmp (devtype, "wwan")) { + nm_log_dbg (LOGD_HW, "ignoring interface with devtype '%s'", devtype); + return; + } + + iface = g_udev_device_get_name (device); + if (!iface) { + nm_log_dbg (LOGD_HW, "failed to get device's interface"); + return; + } + + g_signal_emit (self, signals[DEVICE_ADDED], 0, device, device_creator); +} + +static void +net_remove (NMUdevManager *self, GUdevDevice *device) +{ + g_signal_emit (self, signals[DEVICE_REMOVED], 0, device); +} + +void +nm_udev_manager_query_devices (NMUdevManager *self) +{ + NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self); + GList *devices, *iter; + + g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_UDEV_MANAGER (self)); + + devices = g_udev_client_query_by_subsystem (priv->client, "net"); + for (iter = devices; iter; iter = g_list_next (iter)) { + net_add (self, G_UDEV_DEVICE (iter->data)); + g_object_unref (G_UDEV_DEVICE (iter->data)); + } + g_list_free (devices); +} + +static void +handle_uevent (GUdevClient *client, + const char *action, + GUdevDevice *device, + gpointer user_data) +{ + NMUdevManager *self = NM_UDEV_MANAGER (user_data); + const char *subsys; + + g_return_if_fail (action != NULL); + + /* A bit paranoid */ + subsys = g_udev_device_get_subsystem (device); + g_return_if_fail (subsys != NULL); + + nm_log_dbg (LOGD_HW, "UDEV event: action '%s' subsys '%s' device '%s'", + action, subsys, g_udev_device_get_name (device)); + + g_return_if_fail (!strcmp (subsys, "rfkill") || !strcmp (subsys, "net")); + + if (!strcmp (action, "add")) { + if (!strcmp (subsys, "rfkill")) + rfkill_add (self, device); + else if (!strcmp (subsys, "net")) + net_add (self, device); + } else if (!strcmp (action, "remove")) { + if (!strcmp (subsys, "rfkill")) + rfkill_remove (self, device); + else if (!strcmp (subsys, "net")) + net_remove (self, device); + } + + recheck_killswitches (self); +} + +static void +nm_udev_manager_init (NMUdevManager *self) +{ + NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self); + const char *subsys[3] = { "rfkill", "net", NULL }; + GList *switches, *iter; + guint32 i; + + for (i = 0; i < RFKILL_TYPE_MAX; i++) + priv->rfkill_states[i] = RFKILL_UNBLOCKED; + + priv->client = g_udev_client_new (subsys); + g_signal_connect (priv->client, "uevent", G_CALLBACK (handle_uevent), self); + + switches = g_udev_client_query_by_subsystem (priv->client, "rfkill"); + for (iter = switches; iter; iter = g_list_next (iter)) { + add_one_killswitch (self, G_UDEV_DEVICE (iter->data)); + g_object_unref (G_UDEV_DEVICE (iter->data)); + } + g_list_free (switches); + + recheck_killswitches (self); +} + +static void +dispose (GObject *object) +{ + NMUdevManager *self = NM_UDEV_MANAGER (object); + NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self); + + if (priv->disposed) { + G_OBJECT_CLASS (nm_udev_manager_parent_class)->dispose (object); + return; + } + priv->disposed = TRUE; + + g_object_unref (priv->client); + + g_slist_foreach (priv->killswitches, (GFunc) killswitch_destroy, NULL); + g_slist_free (priv->killswitches); + + G_OBJECT_CLASS (nm_udev_manager_parent_class)->dispose (object); +} + +static void +nm_udev_manager_class_init (NMUdevManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NMUdevManagerPrivate)); + + /* virtual methods */ + object_class->dispose = dispose; + + /* Signals */ + signals[DEVICE_ADDED] = + g_signal_new ("device-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMUdevManagerClass, device_added), + NULL, NULL, + _nm_marshal_VOID__POINTER_POINTER, + G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER); + + signals[DEVICE_REMOVED] = + g_signal_new ("device-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMUdevManagerClass, device_removed), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + signals[RFKILL_CHANGED] = + g_signal_new ("rfkill-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMUdevManagerClass, rfkill_changed), + NULL, NULL, + _nm_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); +} + diff --git a/src/nm-udev-manager.h b/src/nm-udev-manager.h new file mode 100644 index 00000000..06a2d35c --- /dev/null +++ b/src/nm-udev-manager.h @@ -0,0 +1,72 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2008 Red Hat, Inc. + */ + +#ifndef NM_UDEV_MANAGER_H +#define NM_UDEV_MANAGER_H + +#include <glib.h> +#include <glib-object.h> + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "nm-rfkill.h" + +G_BEGIN_DECLS + +#define NM_TYPE_UDEV_MANAGER (nm_udev_manager_get_type ()) +#define NM_UDEV_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_UDEV_MANAGER, NMUdevManager)) +#define NM_UDEV_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_UDEV_MANAGER, NMUdevManagerClass)) +#define NM_IS_UDEV_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_UDEV_MANAGER)) +#define NM_IS_UDEV_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_UDEV_MANAGER)) +#define NM_UDEV_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_UDEV_MANAGER, NMUdevManagerClass)) + +typedef struct { + GObject parent; +} NMUdevManager; + +typedef GObject *(*NMDeviceCreatorFn) (NMUdevManager *manager, + GUdevDevice *device, + gboolean sleeping); + +typedef struct { + GObjectClass parent; + + /* Virtual functions */ + void (*device_added) (NMUdevManager *manager, + GUdevDevice *device, + NMDeviceCreatorFn creator_fn); + + void (*device_removed) (NMUdevManager *manager, GUdevDevice *device); + + void (*rfkill_changed) (NMUdevManager *manager, RfKillType rtype, RfKillState state); +} NMUdevManagerClass; + +GType nm_udev_manager_get_type (void); + +NMUdevManager *nm_udev_manager_new (void); + +void nm_udev_manager_query_devices (NMUdevManager *manager); + +RfKillState nm_udev_manager_get_rfkill_state (NMUdevManager *manager, RfKillType rtype); + +#endif /* NM_UDEV_MANAGER_H */ + diff --git a/src/nm-wifi-ap.c b/src/nm-wifi-ap.c new file mode 100644 index 00000000..c7b5d8a5 --- /dev/null +++ b/src/nm-wifi-ap.c @@ -0,0 +1,1477 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2004 - 2008 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include "wireless-helper.h" + +#include <string.h> + +#include "nm-wifi-ap.h" +#include "NetworkManagerUtils.h" +#include "nm-utils.h" +#include "nm-logging.h" +#include "nm-dbus-manager.h" +#include "wpa.h" +#include "nm-properties-changed-signal.h" +#include "nm-setting-wireless.h" + +#include "nm-access-point-glue.h" + +/* + * Encapsulates Access Point information + */ +typedef struct +{ + char *dbus_path; + + /* Scanned or cached values */ + GByteArray * ssid; + struct ether_addr address; + NM80211Mode mode; + gint8 strength; + guint32 freq; /* Frequency in MHz; ie 2412 (== 2.412 GHz) */ + guint32 max_bitrate;/* Maximum bitrate of the AP in Kbit/s (ie 54000 Kb/s == 54Mbit/s) */ + + guint32 flags; /* General flags */ + guint32 wpa_flags; /* WPA-related flags */ + guint32 rsn_flags; /* RSN (WPA2) -related flags */ + + /* Non-scanned attributes */ + gboolean fake; /* Whether or not the AP is from a scan */ + gboolean broadcast; /* Whether or not the AP is broadcasting (hidden) */ + gboolean user_created; /* Whether or not the AP was created + * by the user with "Create network..." + * A subset of Ad-Hoc mode. user_created + * implies Ad-Hoc, but not necessarily + * the other way around. + */ + glong last_seen; /* Last time the AP was seen in a scan in seconds */ + + /* Things from user prefs/NetworkManagerInfo */ + GTimeVal timestamp; + GSList * user_addresses; +} NMAccessPointPrivate; + +#define NM_AP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_AP, NMAccessPointPrivate)) + +G_DEFINE_TYPE (NMAccessPoint, nm_ap, G_TYPE_OBJECT) + +enum { + PROPERTIES_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum { + PROP_0, + PROP_FLAGS, + PROP_WPA_FLAGS, + PROP_RSN_FLAGS, + PROP_SSID, + PROP_FREQUENCY, + PROP_HW_ADDRESS, + PROP_MODE, + PROP_MAX_BITRATE, + PROP_STRENGTH, + LAST_PROP +}; + +static void +nm_ap_init (NMAccessPoint *ap) +{ + NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (ap); + + priv->dbus_path = NULL; + priv->mode = NM_802_11_MODE_INFRA; + priv->flags = NM_802_11_AP_FLAGS_NONE; + priv->wpa_flags = NM_802_11_AP_SEC_NONE; + priv->rsn_flags = NM_802_11_AP_SEC_NONE; + priv->broadcast = TRUE; +} + +static void +finalize (GObject *object) +{ + NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (object); + + g_free (priv->dbus_path); + if (priv->ssid) + g_byte_array_free (priv->ssid, TRUE); + g_slist_foreach (priv->user_addresses, (GFunc)g_free, NULL); + g_slist_free (priv->user_addresses); + + G_OBJECT_CLASS (nm_ap_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMAccessPoint *ap = NM_AP (object); + + switch (prop_id) { + case PROP_FLAGS: + nm_ap_set_flags (ap, g_value_get_uint (value)); + break; + case PROP_WPA_FLAGS: + nm_ap_set_wpa_flags (ap, g_value_get_uint (value)); + break; + case PROP_RSN_FLAGS: + nm_ap_set_rsn_flags (ap, g_value_get_uint (value)); + break; + case PROP_SSID: + nm_ap_set_ssid (ap, (GByteArray *) g_value_get_boxed (value)); + break; + case PROP_FREQUENCY: + nm_ap_set_freq (ap, g_value_get_uint (value)); + break; + case PROP_MODE: + nm_ap_set_mode (ap, g_value_get_uint (value)); + break; + case PROP_MAX_BITRATE: + nm_ap_set_max_bitrate (ap, g_value_get_uint (value)); + break; + case PROP_STRENGTH: + nm_ap_set_strength (ap, g_value_get_char (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (object); + GArray * ssid; + int len; + int i; + + switch (prop_id) { + case PROP_FLAGS: + g_value_set_uint (value, priv->flags); + break; + case PROP_WPA_FLAGS: + g_value_set_uint (value, priv->wpa_flags); + break; + case PROP_RSN_FLAGS: + g_value_set_uint (value, priv->rsn_flags); + break; + case PROP_SSID: + len = priv->ssid ? priv->ssid->len : 0; + ssid = g_array_sized_new (FALSE, TRUE, sizeof (unsigned char), len); + for (i = 0; i < len; i++) + g_array_append_val (ssid, priv->ssid->data[i]); + g_value_set_boxed (value, ssid); + g_array_free (ssid, TRUE); + break; + case PROP_FREQUENCY: + g_value_set_uint (value, priv->freq); + break; + case PROP_HW_ADDRESS: + g_value_take_string (value, nm_ether_ntop (&priv->address)); + break; + case PROP_MODE: + g_value_set_uint (value, priv->mode); + break; + case PROP_MAX_BITRATE: + g_value_set_uint (value, priv->max_bitrate); + break; + case PROP_STRENGTH: + g_value_set_char (value, priv->strength); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_ap_class_init (NMAccessPointClass *ap_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (ap_class); + guint32 all_sec_flags; + + g_type_class_add_private (ap_class, sizeof (NMAccessPointPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + + /* properties */ + + all_sec_flags = NM_802_11_AP_SEC_NONE + | NM_802_11_AP_SEC_PAIR_WEP40 + | NM_802_11_AP_SEC_PAIR_WEP104 + | NM_802_11_AP_SEC_PAIR_TKIP + | NM_802_11_AP_SEC_PAIR_CCMP + | NM_802_11_AP_SEC_GROUP_WEP40 + | NM_802_11_AP_SEC_GROUP_WEP104 + | NM_802_11_AP_SEC_GROUP_TKIP + | NM_802_11_AP_SEC_GROUP_CCMP + | NM_802_11_AP_SEC_KEY_MGMT_PSK + | NM_802_11_AP_SEC_KEY_MGMT_802_1X; + + g_object_class_install_property + (object_class, PROP_FLAGS, + g_param_spec_uint (NM_AP_FLAGS, + "Flags", + "Flags", + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_FLAGS_NONE, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_WPA_FLAGS, + g_param_spec_uint (NM_AP_WPA_FLAGS, + "WPA Flags", + "WPA Flags", + NM_802_11_AP_SEC_NONE, + all_sec_flags, + NM_802_11_AP_SEC_NONE, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_RSN_FLAGS, + g_param_spec_uint (NM_AP_RSN_FLAGS, + "RSN Flags", + "RSN Flags", + NM_802_11_AP_SEC_NONE, + all_sec_flags, + NM_802_11_AP_SEC_NONE, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_SSID, + g_param_spec_boxed (NM_AP_SSID, + "SSID", + "SSID", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_FREQUENCY, + g_param_spec_uint (NM_AP_FREQUENCY, + "Frequency", + "Frequency", + 0, 10000, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_AP_HW_ADDRESS, + "MAC Address", + "Hardware MAC address", + NULL, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_MODE, + g_param_spec_uint (NM_AP_MODE, + "Mode", + "Mode", + NM_802_11_MODE_ADHOC, NM_802_11_MODE_INFRA, NM_802_11_MODE_INFRA, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_MAX_BITRATE, + g_param_spec_uint (NM_AP_MAX_BITRATE, + "Max Bitrate", + "Max Bitrate", + 0, G_MAXUINT16, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_STRENGTH, + g_param_spec_char (NM_AP_STRENGTH, + "Strength", + "Strength", + G_MININT8, G_MAXINT8, 0, + G_PARAM_READWRITE)); + + /* Signals */ + signals[PROPERTIES_CHANGED] = + nm_properties_changed_signal_new (object_class, + G_STRUCT_OFFSET (NMAccessPointClass, properties_changed)); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (ap_class), + &dbus_glib_nm_access_point_object_info); +} + +void +nm_ap_export_to_dbus (NMAccessPoint *ap) +{ + NMAccessPointPrivate *priv; + NMDBusManager *mgr; + DBusGConnection *g_connection; + static guint32 counter = 0; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + if (priv->dbus_path) { + nm_log_err (LOGD_CORE, "Tried to export AP %s twice.", priv->dbus_path); + return; + } + + mgr = nm_dbus_manager_get (); + g_assert (mgr); + + g_connection = nm_dbus_manager_get_connection (mgr); + g_assert (g_connection); + + priv->dbus_path = g_strdup_printf (NM_DBUS_PATH_ACCESS_POINT "/%d", counter++); + dbus_g_connection_register_g_object (g_connection, priv->dbus_path, G_OBJECT (ap)); + + g_object_unref (mgr); +} + +/* + * nm_ap_new + * + * Create a new, blank user access point info structure + * + */ +NMAccessPoint *nm_ap_new (void) +{ + GObject *object; + + object = g_object_new (NM_TYPE_AP, NULL); + if (!object) + return NULL; + + return (NMAccessPoint *) object; +} + + +#define IEEE80211_CAP_ESS 0x0001 +#define IEEE80211_CAP_IBSS 0x0002 +#define IEEE80211_CAP_PRIVACY 0x0010 + +static void +foreach_property_cb (gpointer key, gpointer value, gpointer user_data) +{ + GValue *variant = (GValue *) value; + NMAccessPoint *ap = (NMAccessPoint *) user_data; + + if (G_VALUE_HOLDS_BOXED (variant)) { + GArray *array = g_value_get_boxed (variant); + + if (!strcmp (key, "ssid")) { + guint32 len = MIN (IW_ESSID_MAX_SIZE, array->len); + GByteArray * ssid; + + /* Stupid ieee80211 layer uses <hidden> */ + if (((len == 8) || (len == 9)) + && (memcmp (array->data, "<hidden>", 8) == 0)) + return; + + if (nm_utils_is_empty_ssid ((const guint8 *) array->data, len)) + return; + + ssid = g_byte_array_sized_new (len); + g_byte_array_append (ssid, (const guint8 *) array->data, len); + nm_ap_set_ssid (ap, ssid); + g_byte_array_free (ssid, TRUE); + } else if (!strcmp (key, "bssid")) { + struct ether_addr addr; + + if (array->len != ETH_ALEN) + return; + memset (&addr, 0, sizeof (struct ether_addr)); + memcpy (&addr, array->data, ETH_ALEN); + nm_ap_set_address (ap, &addr); + } else if (!strcmp (key, "wpaie")) { + guint8 * ie = (guint8 *) array->data; + guint32 flags = nm_ap_get_wpa_flags (ap); + + if (array->len <= 0 || array->len > WPA_MAX_IE_LEN) + return; + flags = nm_ap_add_security_from_ie (flags, ie, array->len); + nm_ap_set_wpa_flags (ap, flags); + } else if (!strcmp (key, "rsnie")) { + guint8 * ie = (guint8 *) array->data; + guint32 flags = nm_ap_get_rsn_flags (ap); + + if (array->len <= 0 || array->len > WPA_MAX_IE_LEN) + return; + flags = nm_ap_add_security_from_ie (flags, ie, array->len); + nm_ap_set_rsn_flags (ap, flags); + } + } else if (G_VALUE_HOLDS_INT (variant)) { + gint32 int_val = g_value_get_int (variant); + + if (!strcmp (key, "frequency")) { + nm_ap_set_freq (ap, (guint32) int_val); + } else if (!strcmp (key, "maxrate")) { + /* Supplicant reports as b/s, we use Kb/s internally */ + nm_ap_set_max_bitrate (ap, int_val / 1000); + } + } else if (G_VALUE_HOLDS_UINT (variant)) { + guint32 val = g_value_get_uint (variant); + + if (!strcmp (key, "capabilities")) { + if (val & IEEE80211_CAP_ESS) { + nm_ap_set_mode (ap, NM_802_11_MODE_INFRA); + } else if (val & IEEE80211_CAP_IBSS) { + nm_ap_set_mode (ap, NM_802_11_MODE_ADHOC); + } + + if (val & IEEE80211_CAP_PRIVACY) { + guint32 flags = nm_ap_get_flags (ap); + nm_ap_set_flags (ap, flags | NM_802_11_AP_FLAGS_PRIVACY); + } + } + } +} + + +NMAccessPoint * +nm_ap_new_from_properties (GHashTable *properties) +{ + NMAccessPoint *ap; + GTimeVal cur_time; + const struct ether_addr * addr; + const char bad_bssid1[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + const char bad_bssid2[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + + g_return_val_if_fail (properties != NULL, NULL); + + ap = nm_ap_new (); + + g_object_freeze_notify (G_OBJECT (ap)); + g_hash_table_foreach (properties, foreach_property_cb, ap); + + /* ignore APs with invalid BSSIDs */ + addr = nm_ap_get_address (ap); + if ( !(memcmp (addr->ether_addr_octet, bad_bssid1, ETH_ALEN)) + || !(memcmp (addr->ether_addr_octet, bad_bssid2, ETH_ALEN))) { + g_object_unref (ap); + return NULL; + } + + g_get_current_time (&cur_time); + nm_ap_set_last_seen (ap, cur_time.tv_sec); + + if (!nm_ap_get_ssid (ap)) + nm_ap_set_broadcast (ap, FALSE); + + g_object_thaw_notify (G_OBJECT (ap)); + + return ap; +} + +#define PROTO_WPA "wpa" +#define PROTO_RSN "rsn" + +static gboolean +has_proto (NMSettingWirelessSecurity *sec, const char *proto) +{ + guint32 num_protos = nm_setting_wireless_security_get_num_protos (sec); + guint32 i; + + if (num_protos == 0) + return TRUE; /* interpret no protos as "all" */ + + for (i = 0; i < num_protos; i++) { + if (!strcmp (nm_setting_wireless_security_get_proto (sec, i), proto)) + return TRUE; + } + return FALSE; +} + +static void +add_pair_ciphers (NMAccessPoint *ap, NMSettingWirelessSecurity *sec) +{ + guint32 num = nm_setting_wireless_security_get_num_pairwise (sec); + guint32 flags = NM_802_11_AP_SEC_NONE; + guint32 i; + + /* If no ciphers are specified, that means "all" WPA ciphers */ + if (num == 0) { + flags |= NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_PAIR_CCMP; + } else { + for (i = 0; i < num; i++) { + const char *cipher = nm_setting_wireless_security_get_pairwise (sec, i); + + if (!strcmp (cipher, "tkip")) + flags |= NM_802_11_AP_SEC_PAIR_TKIP; + else if (!strcmp (cipher, "ccmp")) + flags |= NM_802_11_AP_SEC_PAIR_CCMP; + } + } + + if (has_proto (sec, PROTO_WPA)) + nm_ap_set_wpa_flags (ap, nm_ap_get_wpa_flags (ap) | flags); + if (has_proto (sec, PROTO_RSN)) + nm_ap_set_rsn_flags (ap, nm_ap_get_rsn_flags (ap) | flags); +} + +static void +add_group_ciphers (NMAccessPoint *ap, NMSettingWirelessSecurity *sec) +{ + guint32 num = nm_setting_wireless_security_get_num_groups (sec); + guint32 flags = NM_802_11_AP_SEC_NONE; + guint32 i; + + /* If no ciphers are specified, that means "all" WPA ciphers */ + if (num == 0) { + flags |= NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_GROUP_CCMP; + } else { + for (i = 0; i < num; i++) { + const char *cipher = nm_setting_wireless_security_get_group (sec, i); + + if (!strcmp (cipher, "wep40")) + flags |= NM_802_11_AP_SEC_GROUP_WEP40; + else if (!strcmp (cipher, "wep104")) + flags |= NM_802_11_AP_SEC_GROUP_WEP104; + else if (!strcmp (cipher, "tkip")) + flags |= NM_802_11_AP_SEC_GROUP_TKIP; + else if (!strcmp (cipher, "ccmp")) + flags |= NM_802_11_AP_SEC_GROUP_CCMP; + } + } + + if (has_proto (sec, PROTO_WPA)) + nm_ap_set_wpa_flags (ap, nm_ap_get_wpa_flags (ap) | flags); + if (has_proto (sec, PROTO_RSN)) + nm_ap_set_rsn_flags (ap, nm_ap_get_rsn_flags (ap) | flags); +} + +NMAccessPoint * +nm_ap_new_fake_from_connection (NMConnection *connection) +{ + NMAccessPoint *ap; + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wireless_sec; + const GByteArray *ssid; + const char *mode, *band, *key_mgmt; + guint32 channel, flags; + gboolean psk = FALSE, eap = FALSE; + + g_return_val_if_fail (connection != NULL, NULL); + + s_wireless = NM_SETTING_WIRELESS (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS)); + g_return_val_if_fail (s_wireless != NULL, NULL); + + ssid = nm_setting_wireless_get_ssid (s_wireless); + g_return_val_if_fail (ssid != NULL, NULL); + g_return_val_if_fail (ssid->len > 0, NULL); + + ap = nm_ap_new (); + nm_ap_set_fake (ap, TRUE); + nm_ap_set_ssid (ap, ssid); + + // FIXME: bssid too? + + mode = nm_setting_wireless_get_mode (s_wireless); + if (mode) { + if (!strcmp (mode, "infrastructure")) + nm_ap_set_mode (ap, NM_802_11_MODE_INFRA); + else if (!strcmp (mode, "adhoc")) + nm_ap_set_mode (ap, NM_802_11_MODE_ADHOC); + else + goto error; + } else { + nm_ap_set_mode (ap, NM_802_11_MODE_INFRA); + } + + band = nm_setting_wireless_get_band (s_wireless); + channel = nm_setting_wireless_get_channel (s_wireless); + + if (band && channel) { + guint32 freq = channel_to_freq (channel, band); + + if (freq == 0) + goto error; + + nm_ap_set_freq (ap, freq); + } + + s_wireless_sec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY); + /* Assume presence of a security setting means the AP is encrypted */ + if (!s_wireless_sec) + goto done; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec); + + /* Everything below here uses encryption */ + nm_ap_set_flags (ap, nm_ap_get_flags (ap) | NM_802_11_AP_FLAGS_PRIVACY); + + /* Static & Dynamic WEP */ + if (!strcmp (key_mgmt, "none") || !strcmp (key_mgmt, "ieee8021x")) + goto done; + + psk = !strcmp (key_mgmt, "wpa-psk"); + eap = !strcmp (key_mgmt, "wpa-eap"); + if (psk || eap) { + if (has_proto (s_wireless_sec, PROTO_WPA)) { + flags = nm_ap_get_wpa_flags (ap); + flags |= eap ? NM_802_11_AP_SEC_KEY_MGMT_802_1X : NM_802_11_AP_SEC_KEY_MGMT_PSK; + nm_ap_set_wpa_flags (ap, flags); + } + if (has_proto (s_wireless_sec, PROTO_RSN)) { + flags = nm_ap_get_rsn_flags (ap); + flags |= eap ? NM_802_11_AP_SEC_KEY_MGMT_802_1X : NM_802_11_AP_SEC_KEY_MGMT_PSK; + nm_ap_set_rsn_flags (ap, flags); + } + + add_pair_ciphers (ap, s_wireless_sec); + add_group_ciphers (ap, s_wireless_sec); + } else if (!strcmp (key_mgmt, "wpa-none")) { + guint32 i; + + /* Ad-Hoc has special requirements: proto=WPA, pairwise=(none), and + * group=TKIP/CCMP (but not both). + */ + + flags = nm_ap_get_wpa_flags (ap); + flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK; + + /* Clear ciphers; pairwise must be unset anyway, and group gets set below */ + flags &= ~( NM_802_11_AP_SEC_PAIR_WEP40 + | NM_802_11_AP_SEC_PAIR_WEP104 + | NM_802_11_AP_SEC_PAIR_TKIP + | NM_802_11_AP_SEC_PAIR_CCMP + | NM_802_11_AP_SEC_GROUP_WEP40 + | NM_802_11_AP_SEC_GROUP_WEP104 + | NM_802_11_AP_SEC_GROUP_TKIP + | NM_802_11_AP_SEC_GROUP_CCMP); + + for (i = 0; i < nm_setting_wireless_security_get_num_groups (s_wireless_sec); i++) { + if (!strcmp (nm_setting_wireless_security_get_group (s_wireless_sec, i), "ccmp")) { + flags |= NM_802_11_AP_SEC_GROUP_CCMP; + break; + } + } + + /* Default to TKIP since not all WPA-capable cards can do CCMP */ + if (!(flags & NM_802_11_AP_SEC_GROUP_CCMP)) + flags |= NM_802_11_AP_SEC_GROUP_TKIP; + + nm_ap_set_wpa_flags (ap, flags); + + /* Don't use Ad-Hoc RSN yet */ + nm_ap_set_rsn_flags (ap, NM_802_11_AP_SEC_NONE); + } + +done: + return ap; + +error: + g_object_unref (ap); + return NULL; +} + + +#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" +#define MAC_ARG(x) ((guint8*)(x))[0],((guint8*)(x))[1],((guint8*)(x))[2],((guint8*)(x))[3],((guint8*)(x))[4],((guint8*)(x))[5] + +void +nm_ap_print_self (NMAccessPoint *ap, + const char * prefix) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (ap != NULL); + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + nm_log_dbg (LOGD_WIFI_SCAN, "%s'%s' (%p)", + prefix, + priv->ssid ? nm_utils_escape_ssid (priv->ssid->data, priv->ssid->len) : "(none)", + ap); + nm_log_dbg (LOGD_WIFI_SCAN, " BSSID " MAC_FMT, MAC_ARG (priv->address.ether_addr_octet)); + nm_log_dbg (LOGD_WIFI_SCAN, " mode %d", priv->mode); + nm_log_dbg (LOGD_WIFI_SCAN, " timestamp %ld", priv->timestamp.tv_sec); + nm_log_dbg (LOGD_WIFI_SCAN, " flags 0x%X", priv->flags); + nm_log_dbg (LOGD_WIFI_SCAN, " wpa flags 0x%X", priv->wpa_flags); + nm_log_dbg (LOGD_WIFI_SCAN, " rsn flags 0x%X", priv->rsn_flags); + nm_log_dbg (LOGD_WIFI_SCAN, " quality %d", priv->strength); + nm_log_dbg (LOGD_WIFI_SCAN, " frequency %d", priv->freq); + nm_log_dbg (LOGD_WIFI_SCAN, " max rate %d", priv->max_bitrate); + nm_log_dbg (LOGD_WIFI_SCAN, " last-seen %ld", priv->last_seen); +} + +const char * +nm_ap_get_dbus_path (NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), NULL); + + return NM_AP_GET_PRIVATE (ap)->dbus_path; +} + + +/* + * Get/set functions for timestamp + * + */ +const GTimeVal *nm_ap_get_timestamp (const NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), 0); + + return (&NM_AP_GET_PRIVATE (ap)->timestamp); +} + +void nm_ap_set_timestamp (NMAccessPoint *ap, glong sec, glong usec) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + priv->timestamp.tv_sec = sec; + priv->timestamp.tv_usec = usec; +} + +void nm_ap_set_timestamp_via_timestamp (NMAccessPoint *ap, const GTimeVal *timestamp) +{ + g_return_if_fail (NM_IS_AP (ap)); + + NM_AP_GET_PRIVATE (ap)->timestamp = *timestamp; +} + +/* + * Get/set functions for ssid + * + */ +const GByteArray * nm_ap_get_ssid (const NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), NULL); + + return NM_AP_GET_PRIVATE (ap)->ssid; +} + +void +nm_ap_set_ssid (NMAccessPoint *ap, const GByteArray * ssid) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + if ((ssid == priv->ssid) && ssid == NULL) + return; + + /* same SSID */ + if ((ssid && priv->ssid) && (ssid->len == priv->ssid->len)) { + if (!memcmp (ssid->data, priv->ssid->data, ssid->len)) + return; + } + + if (priv->ssid) { + g_byte_array_free (priv->ssid, TRUE); + priv->ssid = NULL; + } + + if (ssid) { + priv->ssid = g_byte_array_sized_new (ssid->len); + priv->ssid->len = ssid->len; + memcpy (priv->ssid->data, ssid->data, ssid->len); + } + + g_object_notify (G_OBJECT (ap), NM_AP_SSID); +} + + +guint32 +nm_ap_get_flags (NMAccessPoint *ap) +{ + guint32 flags; + + g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_FLAGS_NONE); + + g_object_get (ap, NM_AP_FLAGS, &flags, NULL); + + return flags; +} + + +void +nm_ap_set_flags (NMAccessPoint *ap, guint32 flags) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + if (priv->flags != flags) { + priv->flags = flags; + g_object_notify (G_OBJECT (ap), NM_AP_FLAGS); + } +} + +guint32 +nm_ap_get_wpa_flags (NMAccessPoint *ap) +{ + guint32 flags; + + g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_SEC_NONE); + + g_object_get (ap, NM_AP_WPA_FLAGS, &flags, NULL); + + return flags; +} + + +void +nm_ap_set_wpa_flags (NMAccessPoint *ap, guint32 flags) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + if (priv->wpa_flags != flags) { + priv->wpa_flags = flags; + g_object_notify (G_OBJECT (ap), NM_AP_WPA_FLAGS); + } +} + +guint32 +nm_ap_get_rsn_flags (NMAccessPoint *ap) +{ + guint32 flags; + + g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_SEC_NONE); + + g_object_get (ap, NM_AP_RSN_FLAGS, &flags, NULL); + + return flags; +} + + +void +nm_ap_set_rsn_flags (NMAccessPoint *ap, guint32 flags) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + if (priv->rsn_flags != flags) { + priv->rsn_flags = flags; + g_object_notify (G_OBJECT (ap), NM_AP_RSN_FLAGS); + } +} + +/* + * Get/set functions for address + * + */ +const struct ether_addr * nm_ap_get_address (const NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), NULL); + + return &NM_AP_GET_PRIVATE (ap)->address; +} + +void nm_ap_set_address (NMAccessPoint *ap, const struct ether_addr * addr) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + g_return_if_fail (addr != NULL); + + priv = NM_AP_GET_PRIVATE (ap); + + if (memcmp (addr, &priv->address, sizeof (priv->address))) { + memcpy (&NM_AP_GET_PRIVATE (ap)->address, addr, sizeof (struct ether_addr)); + g_object_notify (G_OBJECT (ap), NM_AP_HW_ADDRESS); + } +} + + +/* + * Get/set functions for mode (ie Ad-Hoc, Infrastructure, etc) + * + */ +NM80211Mode nm_ap_get_mode (NMAccessPoint *ap) +{ + NM80211Mode mode; + + g_return_val_if_fail (NM_IS_AP (ap), -1); + + g_object_get (ap, NM_AP_MODE, &mode, NULL); + + return mode; +} + +void nm_ap_set_mode (NMAccessPoint *ap, const NM80211Mode mode) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + + if (mode == NM_802_11_MODE_ADHOC || mode == NM_802_11_MODE_INFRA) { + priv = NM_AP_GET_PRIVATE (ap); + + if (priv->mode != mode) { + priv->mode = mode; + g_object_notify (G_OBJECT (ap), NM_AP_MODE); + } + } else + nm_log_warn (LOGD_WIFI, "Invalid AP mode '%d'", mode); +} + + +/* + * Get/set functions for strength + * + */ +gint8 nm_ap_get_strength (NMAccessPoint *ap) +{ + gint8 strength; + + g_return_val_if_fail (NM_IS_AP (ap), 0); + + g_object_get (ap, NM_AP_STRENGTH, &strength, NULL); + + return strength; +} + +void nm_ap_set_strength (NMAccessPoint *ap, const gint8 strength) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + if (priv->strength != strength) { + priv->strength = strength; + g_object_notify (G_OBJECT (ap), NM_AP_STRENGTH); + } +} + + +/* + * Get/set functions for frequency + * + */ +guint32 +nm_ap_get_freq (NMAccessPoint *ap) +{ + guint32 freq; + + g_return_val_if_fail (NM_IS_AP (ap), 0); + + g_object_get (ap, NM_AP_FREQUENCY, &freq, NULL); + + return freq; +} + +void +nm_ap_set_freq (NMAccessPoint *ap, + const guint32 freq) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + if (priv->freq != freq) { + priv->freq = freq; + g_object_notify (G_OBJECT (ap), NM_AP_FREQUENCY); + } +} + + +/* + * Get/set functions for max bitrate + * + */ +guint32 nm_ap_get_max_bitrate (NMAccessPoint *ap) +{ + guint32 rate; + + g_return_val_if_fail (NM_IS_AP (ap), 0); + + g_object_get (ap, NM_AP_MAX_BITRATE, &rate, NULL); + + return rate; +} + +void +nm_ap_set_max_bitrate (NMAccessPoint *ap, guint32 bitrate) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + if (priv->max_bitrate != bitrate) { + priv->max_bitrate = bitrate; + g_object_notify (G_OBJECT (ap), NM_AP_MAX_BITRATE); + } +} + +/* + * Get/Set functions to indicate that an access point is 'fake', ie whether + * or not it was created from scan results + */ +gboolean nm_ap_get_fake (const NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), FALSE); + + return NM_AP_GET_PRIVATE (ap)->fake; +} + +void nm_ap_set_fake (NMAccessPoint *ap, gboolean fake) +{ + g_return_if_fail (NM_IS_AP (ap)); + + NM_AP_GET_PRIVATE (ap)->fake = fake; +} + + +/* + * Get/Set functions to indicate whether an AP broadcasts its SSID. + */ +gboolean nm_ap_get_broadcast (NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), TRUE); + + return NM_AP_GET_PRIVATE (ap)->broadcast; +} + + +void nm_ap_set_broadcast (NMAccessPoint *ap, gboolean broadcast) +{ + g_return_if_fail (NM_IS_AP (ap)); + + NM_AP_GET_PRIVATE (ap)->broadcast = broadcast; +} + + +/* + * Get/Set functions for how long ago the AP was last seen in a scan. + * APs older than a certain date are dropped from the list. + * + */ +glong nm_ap_get_last_seen (const NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), FALSE); + + return NM_AP_GET_PRIVATE (ap)->last_seen; +} + +void nm_ap_set_last_seen (NMAccessPoint *ap, const glong last_seen) +{ + g_return_if_fail (NM_IS_AP (ap)); + + NM_AP_GET_PRIVATE (ap)->last_seen = last_seen; +} + + +/* + * Get/Set functions to indicate that an access point is + * user-created, ie whether or not its a network filled with + * information from the user and intended to create a new Ad-Hoc + * wireless network. + * + */ +gboolean nm_ap_get_user_created (const NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), FALSE); + + return NM_AP_GET_PRIVATE (ap)->user_created; +} + +void nm_ap_set_user_created (NMAccessPoint *ap, gboolean user_created) +{ + g_return_if_fail (NM_IS_AP (ap)); + + NM_AP_GET_PRIVATE (ap)->user_created = user_created; +} + + +/* + * Get/Set functions for user address list + * + * The internal address list is always "owned" by the AP and + * the list returned by nm_ap_get_user_addresses() is a deep copy. + * Likewise, when setting the list, a deep copy is made for the + * ap's actual list. + * + */ +GSList *nm_ap_get_user_addresses (const NMAccessPoint *ap) +{ + GSList *new = NULL; + GSList *elt = NULL; + + g_return_val_if_fail (NM_IS_AP (ap), NULL); + + for (elt = NM_AP_GET_PRIVATE (ap)->user_addresses; elt; elt = g_slist_next (elt)) + { + if (elt->data) + new = g_slist_append (new, g_strdup (elt->data)); + } + + /* Return a _deep__copy_ of the address list */ + return new; +} + +void nm_ap_set_user_addresses (NMAccessPoint *ap, GSList *list) +{ + NMAccessPointPrivate *priv; + GSList *elt = NULL; + GSList *new = NULL; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + /* Free existing list */ + g_slist_foreach (priv->user_addresses, (GFunc) g_free, NULL); + + /* Copy new list and set as our own */ + for (elt = list; elt; elt = g_slist_next (elt)) + { + if (elt->data) + new = g_slist_append (new, g_ascii_strup (elt->data, -1)); + } + + priv->user_addresses = new; +} + + +guint32 +nm_ap_add_security_from_ie (guint32 flags, + const guint8 *wpa_ie, + guint32 length) +{ + wpa_ie_data * cap_data; + + if (!(cap_data = wpa_parse_wpa_ie (wpa_ie, length))) + return NM_802_11_AP_SEC_NONE; + + /* Pairwise cipher flags */ + if (cap_data->pairwise_cipher & IW_AUTH_CIPHER_WEP40) + flags |= NM_802_11_AP_SEC_PAIR_WEP40; + if (cap_data->pairwise_cipher & IW_AUTH_CIPHER_WEP104) + flags |= NM_802_11_AP_SEC_PAIR_WEP104; + if (cap_data->pairwise_cipher & IW_AUTH_CIPHER_TKIP) + flags |= NM_802_11_AP_SEC_PAIR_TKIP; + if (cap_data->pairwise_cipher & IW_AUTH_CIPHER_CCMP) + flags |= NM_802_11_AP_SEC_PAIR_CCMP; + + /* Group cipher flags */ + if (cap_data->group_cipher & IW_AUTH_CIPHER_WEP40) + flags |= NM_802_11_AP_SEC_GROUP_WEP40; + if (cap_data->group_cipher & IW_AUTH_CIPHER_WEP104) + flags |= NM_802_11_AP_SEC_GROUP_WEP104; + if (cap_data->group_cipher & IW_AUTH_CIPHER_TKIP) + flags |= NM_802_11_AP_SEC_GROUP_TKIP; + if (cap_data->group_cipher & IW_AUTH_CIPHER_CCMP) + flags |= NM_802_11_AP_SEC_GROUP_CCMP; + + if (cap_data->key_mgmt & IW_AUTH_KEY_MGMT_802_1X) + flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X; + if (cap_data->key_mgmt & IW_AUTH_KEY_MGMT_PSK) + flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK; + + g_slice_free (wpa_ie_data, cap_data); + return flags; +} + +gboolean +nm_ap_check_compatible (NMAccessPoint *self, + NMConnection *connection) +{ + NMAccessPointPrivate *priv; + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wireless_sec; + const char *mode; + const char *band; + const GByteArray *bssid; + guint32 channel; + + g_return_val_if_fail (NM_IS_AP (self), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + + priv = NM_AP_GET_PRIVATE (self); + + s_wireless = NM_SETTING_WIRELESS (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS)); + if (s_wireless == NULL) + return FALSE; + + if (!nm_utils_same_ssid (nm_setting_wireless_get_ssid (s_wireless), priv->ssid, TRUE)) + return FALSE; + + bssid = nm_setting_wireless_get_bssid (s_wireless); + if (bssid && memcmp (bssid->data, &priv->address, ETH_ALEN)) + return FALSE; + + mode = nm_setting_wireless_get_mode (s_wireless); + if (mode) { + if (!strcmp (mode, "infrastructure") && (priv->mode != NM_802_11_MODE_INFRA)) + return FALSE; + if (!strcmp (mode, "adhoc") && (priv->mode != NM_802_11_MODE_ADHOC)) + return FALSE; + } + + band = nm_setting_wireless_get_band (s_wireless); + if (band) { + if (!strcmp (band, "a")) { + if (priv->freq < 4915 || priv->freq > 5825) + return FALSE; + } else if (!strcmp (band, "bg")) { + if (priv->freq < 2412 || priv->freq > 2484) + return FALSE; + } + } + + channel = nm_setting_wireless_get_channel (s_wireless); + if (channel) { + guint32 ap_chan = freq_to_channel (priv->freq); + + if (channel != ap_chan) + return FALSE; + } + + s_wireless_sec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, + NM_TYPE_SETTING_WIRELESS_SECURITY); + + return nm_setting_wireless_ap_security_compatible (s_wireless, + s_wireless_sec, + nm_ap_get_flags (self), + nm_ap_get_wpa_flags (self), + nm_ap_get_rsn_flags (self), + nm_ap_get_mode (self)); +} + +static gboolean +capabilities_compatible (guint32 a_flags, guint32 b_flags) +{ + if (a_flags == b_flags) + return TRUE; + + /* Make sure there's a common key management method */ + if (!((a_flags & 0x300) & (b_flags & 0x300))) + return FALSE; + + /* Ensure common pairwise ciphers */ + if (!((a_flags & 0xF) & (b_flags & 0xF))) + return FALSE; + + /* Ensure common group ciphers */ + if (!((a_flags & 0xF0) & (b_flags & 0xF0))) + return FALSE; + + return TRUE; +} + +NMAccessPoint * +nm_ap_match_in_list (NMAccessPoint *find_ap, + GSList *ap_list, + gboolean strict_match) +{ + GSList *iter; + + g_return_val_if_fail (find_ap != NULL, NULL); + + for (iter = ap_list; iter; iter = g_slist_next (iter)) { + NMAccessPoint * list_ap = NM_AP (iter->data); + const GByteArray * list_ssid = nm_ap_get_ssid (list_ap); + const struct ether_addr * list_addr = nm_ap_get_address (list_ap); + + const GByteArray * find_ssid = nm_ap_get_ssid (find_ap); + const struct ether_addr * find_addr = nm_ap_get_address (find_ap); + + /* SSID match; if both APs are hiding their SSIDs, + * let matching continue on BSSID and other properties + */ + if ( (!list_ssid && find_ssid) + || (list_ssid && !find_ssid) + || !nm_utils_same_ssid (list_ssid, find_ssid, TRUE)) + continue; + + /* BSSID match */ + if ( (strict_match || nm_ethernet_address_is_valid (find_addr)) + && nm_ethernet_address_is_valid (list_addr) + && memcmp (list_addr->ether_addr_octet, + find_addr->ether_addr_octet, + ETH_ALEN) != 0) { + continue; + } + + /* mode match */ + if (nm_ap_get_mode (list_ap) != nm_ap_get_mode (find_ap)) + continue; + + /* Frequency match */ + if (nm_ap_get_freq (list_ap) != nm_ap_get_freq (find_ap)) + continue; + + /* AP flags */ + if (nm_ap_get_flags (list_ap) != nm_ap_get_flags (find_ap)) + continue; + + if (strict_match) { + if (nm_ap_get_wpa_flags (list_ap) != nm_ap_get_wpa_flags (find_ap)) + continue; + + if (nm_ap_get_rsn_flags (list_ap) != nm_ap_get_rsn_flags (find_ap)) + continue; + } else { + guint32 list_wpa_flags = nm_ap_get_wpa_flags (list_ap); + guint32 find_wpa_flags = nm_ap_get_wpa_flags (find_ap); + guint32 list_rsn_flags = nm_ap_get_rsn_flags (list_ap); + guint32 find_rsn_flags = nm_ap_get_rsn_flags (find_ap); + + /* Just ensure that there is overlap in the capabilities */ + if ( !capabilities_compatible (list_wpa_flags, find_wpa_flags) + && !capabilities_compatible (list_rsn_flags, find_rsn_flags)) + continue; + } + + return list_ap; + } + + return NULL; +} + + +struct cf_pair { + guint32 chan; + guint32 freq; +}; + +static struct cf_pair a_table[] = { + /* A band */ + { 7, 5035 }, + { 8, 5040 }, + { 9, 5045 }, + { 11, 5055 }, + { 12, 5060 }, + { 16, 5080 }, + { 34, 5170 }, + { 36, 5180 }, + { 38, 5190 }, + { 40, 5200 }, + { 42, 5210 }, + { 44, 5220 }, + { 46, 5230 }, + { 48, 5240 }, + { 50, 5250 }, + { 52, 5260 }, + { 56, 5280 }, + { 58, 5290 }, + { 60, 5300 }, + { 64, 5320 }, + { 100, 5500 }, + { 104, 5520 }, + { 108, 5540 }, + { 112, 5560 }, + { 116, 5580 }, + { 120, 5600 }, + { 124, 5620 }, + { 128, 5640 }, + { 132, 5660 }, + { 136, 5680 }, + { 140, 5700 }, + { 149, 5745 }, + { 152, 5760 }, + { 153, 5765 }, + { 157, 5785 }, + { 160, 5800 }, + { 161, 5805 }, + { 165, 5825 }, + { 183, 4915 }, + { 184, 4920 }, + { 185, 4925 }, + { 187, 4935 }, + { 188, 4945 }, + { 192, 4960 }, + { 196, 4980 }, + { 0, -1 } +}; + +static struct cf_pair bg_table[] = { + /* B/G band */ + { 1, 2412 }, + { 2, 2417 }, + { 3, 2422 }, + { 4, 2427 }, + { 5, 2432 }, + { 6, 2437 }, + { 7, 2442 }, + { 8, 2447 }, + { 9, 2452 }, + { 10, 2457 }, + { 11, 2462 }, + { 12, 2467 }, + { 13, 2472 }, + { 14, 2484 }, + { 0, -1 } +}; + +guint32 +freq_to_channel (guint32 freq) +{ + int i = 0; + + if (freq > 4900) { + while (a_table[i].chan && (a_table[i].freq != freq)) + i++; + return a_table[i].chan; + } else { + while (bg_table[i].chan && (bg_table[i].freq != freq)) + i++; + return bg_table[i].chan; + } + + return 0; +} + +guint32 +channel_to_freq (guint32 channel, const char *band) +{ + int i = 0; + + if (!strcmp (band, "a")) { + while (a_table[i].chan && (a_table[i].chan != channel)) + i++; + return a_table[i].freq; + } else if (!strcmp (band, "bg")) { + while (bg_table[i].chan && (bg_table[i].chan != channel)) + i++; + return bg_table[i].freq; + } + + return 0; +} + diff --git a/src/nm-wifi-ap.h b/src/nm-wifi-ap.h new file mode 100644 index 00000000..edc9e56b --- /dev/null +++ b/src/nm-wifi-ap.h @@ -0,0 +1,129 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2004 - 2008 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef NM_ACCESS_POINT_H +#define NM_ACCESS_POINT_H + +#include <glib.h> +#include <glib-object.h> +#include <time.h> +#include "NetworkManager.h" +#include "nm-connection.h" + +#define NM_TYPE_AP (nm_ap_get_type ()) +#define NM_AP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_AP, NMAccessPoint)) +#define NM_AP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_AP, NMAccessPointClass)) +#define NM_IS_AP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_AP)) +#define NM_IS_AP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_AP)) +#define NM_AP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_AP, NMAccessPointClass)) + +#define NM_AP_FLAGS "flags" +#define NM_AP_WPA_FLAGS "wpa-flags" +#define NM_AP_RSN_FLAGS "rsn-flags" +#define NM_AP_SSID "ssid" +#define NM_AP_FREQUENCY "frequency" +#define NM_AP_HW_ADDRESS "hw-address" +#define NM_AP_MODE "mode" +#define NM_AP_MAX_BITRATE "max-bitrate" +#define NM_AP_STRENGTH "strength" + +typedef struct { + GObject parent; +} NMAccessPoint; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*properties_changed) (NMAccessPoint *ap, GHashTable *properties); +} NMAccessPointClass; + +GType nm_ap_get_type (void); + +NMAccessPoint * nm_ap_new (void); +NMAccessPoint * nm_ap_new_from_properties (GHashTable *properties); +NMAccessPoint * nm_ap_new_fake_from_connection (NMConnection *connection); +void nm_ap_export_to_dbus (NMAccessPoint *ap); + +const char * nm_ap_get_dbus_path (NMAccessPoint *ap); +const GTimeVal * nm_ap_get_timestamp (const NMAccessPoint *ap); +void nm_ap_set_timestamp (NMAccessPoint *ap, glong sec, glong usec); +void nm_ap_set_timestamp_via_timestamp (NMAccessPoint *ap, const GTimeVal *timestamp); + +const GByteArray * nm_ap_get_ssid (const NMAccessPoint * ap); +void nm_ap_set_ssid (NMAccessPoint * ap, const GByteArray * ssid); + +guint32 nm_ap_get_flags (NMAccessPoint *ap); +void nm_ap_set_flags (NMAccessPoint *ap, guint32 flags); + +guint32 nm_ap_get_wpa_flags (NMAccessPoint *ap); +void nm_ap_set_wpa_flags (NMAccessPoint *ap, guint32 flags); + +guint32 nm_ap_get_rsn_flags (NMAccessPoint *ap); +void nm_ap_set_rsn_flags (NMAccessPoint *ap, guint32 flags); + +const struct ether_addr * nm_ap_get_address (const NMAccessPoint *ap); +void nm_ap_set_address (NMAccessPoint *ap, const struct ether_addr *addr); + +NM80211Mode nm_ap_get_mode (NMAccessPoint *ap); +void nm_ap_set_mode (NMAccessPoint *ap, const NM80211Mode mode); + +gint8 nm_ap_get_strength (NMAccessPoint *ap); +void nm_ap_set_strength (NMAccessPoint *ap, gint8 strength); + +guint32 nm_ap_get_freq (NMAccessPoint *ap); +void nm_ap_set_freq (NMAccessPoint *ap, guint32 freq); + +guint32 nm_ap_get_max_bitrate (NMAccessPoint *ap); +void nm_ap_set_max_bitrate (NMAccessPoint *ap, guint32 bitrate); + +gboolean nm_ap_get_fake (const NMAccessPoint *ap); +void nm_ap_set_fake (NMAccessPoint *ap, gboolean fake); + +gboolean nm_ap_get_broadcast (NMAccessPoint *ap); +void nm_ap_set_broadcast (NMAccessPoint *ap, gboolean broadcast); + +glong nm_ap_get_last_seen (const NMAccessPoint *ap); +void nm_ap_set_last_seen (NMAccessPoint *ap, const glong last_seen); + +gboolean nm_ap_get_user_created (const NMAccessPoint *ap); +void nm_ap_set_user_created (NMAccessPoint *ap, gboolean user_created); + +GSList * nm_ap_get_user_addresses (const NMAccessPoint *ap); +void nm_ap_set_user_addresses (NMAccessPoint *ap, GSList *list); + +guint32 nm_ap_add_security_from_ie (guint32 flags, + const guint8 *wpa_ie, + guint32 length); + +gboolean nm_ap_check_compatible (NMAccessPoint *self, + NMConnection *connection); + +NMAccessPoint * nm_ap_match_in_list (NMAccessPoint *find_ap, + GSList *ap_list, + gboolean strict_match); + +void nm_ap_print_self (NMAccessPoint *ap, const char * prefix); + +guint32 freq_to_channel (guint32 freq); +guint32 channel_to_freq (guint32 channel, const char *band); + +#endif /* NM_ACCESS_POINT_H */ diff --git a/src/ppp-manager/Makefile.am b/src/ppp-manager/Makefile.am new file mode 100644 index 00000000..2dc7ad94 --- /dev/null +++ b/src/ppp-manager/Makefile.am @@ -0,0 +1,58 @@ +INCLUDES = \ + -I${top_srcdir} \ + -I${top_srcdir}/include \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/logging \ + -I${top_builddir}/marshallers + +noinst_LTLIBRARIES = libppp-manager.la + +libppp_manager_la_SOURCES = \ + nm-ppp-manager.c \ + nm-ppp-manager.h \ + nm-ppp-status.h + +nm-ppp-manager-glue.h: $(top_srcdir)/introspection/nm-ppp-manager.xml + dbus-binding-tool --prefix=nm_ppp_manager --mode=glib-server --output=$@ $< + +built_sources = nm-ppp-manager-glue.h + +$(libppp_manager_la_OBJECTS): $(built_sources) + +libppp_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DLIBDIR=\"$(libdir)\" \ + -DPLUGINDIR=\"$(PPPD_PLUGIN_DIR)\" + +libppp_manager_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + +pppd_plugindir = $(PPPD_PLUGIN_DIR) +pppd_plugin_LTLIBRARIES = nm-pppd-plugin.la + +nm_pppd_plugin_la_SOURCES = \ + nm-pppd-plugin.c \ + nm-pppd-plugin.h \ + nm-ppp-status.h + +nm_pppd_plugin_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) + +nm_pppd_plugin_la_LDFLAGS = -module -avoid-version + +nm_pppd_plugin_la_LIBADD = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + +BUILT_SOURCES = nm-ppp-manager-glue.h + +CLEANFILES = $(BUILT_SOURCES) + diff --git a/src/ppp-manager/Makefile.in b/src/ppp-manager/Makefile.in new file mode 100644 index 00000000..59cb32ba --- /dev/null +++ b/src/ppp-manager/Makefile.in @@ -0,0 +1,679 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/ppp-manager +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__installdirs = "$(DESTDIR)$(pppd_plugindir)" +LTLIBRARIES = $(noinst_LTLIBRARIES) $(pppd_plugin_LTLIBRARIES) +am__DEPENDENCIES_1 = +libppp_manager_la_DEPENDENCIES = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libppp_manager_la_OBJECTS = libppp_manager_la-nm-ppp-manager.lo +libppp_manager_la_OBJECTS = $(am_libppp_manager_la_OBJECTS) +nm_pppd_plugin_la_DEPENDENCIES = \ + $(top_builddir)/libnm-util/libnm-util.la $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_nm_pppd_plugin_la_OBJECTS = nm_pppd_plugin_la-nm-pppd-plugin.lo +nm_pppd_plugin_la_OBJECTS = $(am_nm_pppd_plugin_la_OBJECTS) +nm_pppd_plugin_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(nm_pppd_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libppp_manager_la_SOURCES) $(nm_pppd_plugin_la_SOURCES) +DIST_SOURCES = $(libppp_manager_la_SOURCES) \ + $(nm_pppd_plugin_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +IPTABLES_PATH = @IPTABLES_PATH@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBM = @LIBM@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POFILES = @POFILES@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +RANLIB = @RANLIB@ +RESOLVCONF_PATH = @RESOLVCONF_PATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = \ + -I${top_srcdir} \ + -I${top_srcdir}/include \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/logging \ + -I${top_builddir}/marshallers + +noinst_LTLIBRARIES = libppp-manager.la +libppp_manager_la_SOURCES = \ + nm-ppp-manager.c \ + nm-ppp-manager.h \ + nm-ppp-status.h + +built_sources = nm-ppp-manager-glue.h +libppp_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DLIBDIR=\"$(libdir)\" \ + -DPLUGINDIR=\"$(PPPD_PLUGIN_DIR)\" + +libppp_manager_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + +pppd_plugindir = $(PPPD_PLUGIN_DIR) +pppd_plugin_LTLIBRARIES = nm-pppd-plugin.la +nm_pppd_plugin_la_SOURCES = \ + nm-pppd-plugin.c \ + nm-pppd-plugin.h \ + nm-ppp-status.h + +nm_pppd_plugin_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) + +nm_pppd_plugin_la_LDFLAGS = -module -avoid-version +nm_pppd_plugin_la_LIBADD = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + +BUILT_SOURCES = nm-ppp-manager-glue.h +CLEANFILES = $(BUILT_SOURCES) +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/ppp-manager/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/ppp-manager/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +install-pppd_pluginLTLIBRARIES: $(pppd_plugin_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(pppd_plugindir)" || $(MKDIR_P) "$(DESTDIR)$(pppd_plugindir)" + @list='$(pppd_plugin_LTLIBRARIES)'; test -n "$(pppd_plugindir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pppd_plugindir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pppd_plugindir)"; \ + } + +uninstall-pppd_pluginLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(pppd_plugin_LTLIBRARIES)'; test -n "$(pppd_plugindir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pppd_plugindir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pppd_plugindir)/$$f"; \ + done + +clean-pppd_pluginLTLIBRARIES: + -test -z "$(pppd_plugin_LTLIBRARIES)" || rm -f $(pppd_plugin_LTLIBRARIES) + @list='$(pppd_plugin_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libppp-manager.la: $(libppp_manager_la_OBJECTS) $(libppp_manager_la_DEPENDENCIES) + $(LINK) $(libppp_manager_la_OBJECTS) $(libppp_manager_la_LIBADD) $(LIBS) +nm-pppd-plugin.la: $(nm_pppd_plugin_la_OBJECTS) $(nm_pppd_plugin_la_DEPENDENCIES) + $(nm_pppd_plugin_la_LINK) -rpath $(pppd_plugindir) $(nm_pppd_plugin_la_OBJECTS) $(nm_pppd_plugin_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libppp_manager_la-nm-ppp-manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm_pppd_plugin_la-nm-pppd-plugin.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libppp_manager_la-nm-ppp-manager.lo: nm-ppp-manager.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libppp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libppp_manager_la-nm-ppp-manager.lo -MD -MP -MF $(DEPDIR)/libppp_manager_la-nm-ppp-manager.Tpo -c -o libppp_manager_la-nm-ppp-manager.lo `test -f 'nm-ppp-manager.c' || echo '$(srcdir)/'`nm-ppp-manager.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libppp_manager_la-nm-ppp-manager.Tpo $(DEPDIR)/libppp_manager_la-nm-ppp-manager.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-ppp-manager.c' object='libppp_manager_la-nm-ppp-manager.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libppp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libppp_manager_la-nm-ppp-manager.lo `test -f 'nm-ppp-manager.c' || echo '$(srcdir)/'`nm-ppp-manager.c + +nm_pppd_plugin_la-nm-pppd-plugin.lo: nm-pppd-plugin.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nm_pppd_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm_pppd_plugin_la-nm-pppd-plugin.lo -MD -MP -MF $(DEPDIR)/nm_pppd_plugin_la-nm-pppd-plugin.Tpo -c -o nm_pppd_plugin_la-nm-pppd-plugin.lo `test -f 'nm-pppd-plugin.c' || echo '$(srcdir)/'`nm-pppd-plugin.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/nm_pppd_plugin_la-nm-pppd-plugin.Tpo $(DEPDIR)/nm_pppd_plugin_la-nm-pppd-plugin.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-pppd-plugin.c' object='nm_pppd_plugin_la-nm-pppd-plugin.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nm_pppd_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm_pppd_plugin_la-nm-pppd-plugin.lo `test -f 'nm-pppd-plugin.c' || echo '$(srcdir)/'`nm-pppd-plugin.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(pppd_plugindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + clean-pppd_pluginLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pppd_pluginLTLIBRARIES + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pppd_pluginLTLIBRARIES + +.MAKE: all check install install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES \ + clean-pppd_pluginLTLIBRARIES ctags distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-pppd_pluginLTLIBRARIES install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-pppd_pluginLTLIBRARIES + + +nm-ppp-manager-glue.h: $(top_srcdir)/introspection/nm-ppp-manager.xml + dbus-binding-tool --prefix=nm_ppp_manager --mode=glib-server --output=$@ $< + +$(libppp_manager_la_OBJECTS): $(built_sources) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/ppp-manager/nm-ppp-manager.c b/src/ppp-manager/nm-ppp-manager.c new file mode 100644 index 00000000..148ad2cf --- /dev/null +++ b/src/ppp-manager/nm-ppp-manager.c @@ -0,0 +1,1058 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Novell, Inc. + * Copyright (C) 2008 - 2010 Red Hat, Inc. + */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <string.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <stdlib.h> + +#include <errno.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <asm/types.h> +#include <net/if.h> +#include <sys/stat.h> + +#include <linux/ppp_defs.h> +#ifndef aligned_u64 +#define aligned_u64 unsigned long long __attribute__((aligned(8))) +#endif +#include <linux/if_ppp.h> + +#include "NetworkManager.h" +#include "nm-glib-compat.h" +#include "nm-ppp-manager.h" +#include "nm-setting-connection.h" +#include "nm-setting-ppp.h" +#include "nm-setting-pppoe.h" +#include "nm-setting-gsm.h" +#include "nm-setting-cdma.h" +#include "nm-dbus-manager.h" +#include "nm-logging.h" +#include "nm-marshal.h" + +static void impl_ppp_manager_need_secrets (NMPPPManager *manager, + DBusGMethodInvocation *context); + +static gboolean impl_ppp_manager_set_state (NMPPPManager *manager, + guint32 state, + GError **err); + +static gboolean impl_ppp_manager_set_ip4_config (NMPPPManager *manager, + GHashTable *config, + GError **err); + +#include "nm-ppp-manager-glue.h" + +#define NM_PPPD_PLUGIN PLUGINDIR "/nm-pppd-plugin.so" +#define PPP_MANAGER_SECRET_TRIES "ppp-manager-secret-tries" + +typedef struct { + GPid pid; + NMDBusManager *dbus_manager; + char *dbus_path; + + char *parent_iface; + + NMActRequest *act_req; + DBusGMethodInvocation *pending_secrets_context; + + guint32 ppp_watch_id; + guint32 ppp_timeout_handler; + + /* Monitoring */ + char *ip_iface; + int monitor_fd; + guint monitor_id; +} NMPPPManagerPrivate; + +#define NM_PPP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_PPP_MANAGER, NMPPPManagerPrivate)) + +G_DEFINE_TYPE (NMPPPManager, nm_ppp_manager, G_TYPE_OBJECT) + +enum { + STATE_CHANGED, + IP4_CONFIG, + STATS, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum { + PROP_0, + PROP_PARENT_IFACE, + LAST_PROP +}; + +typedef enum { + NM_PPP_MANAGER_ERROR_UNKOWN +} NMPPPManagerError; + +GQuark +nm_ppp_manager_error_quark (void) +{ + static GQuark quark; + + if (!quark) + quark = g_quark_from_static_string ("nm_ppp_manager_error"); + + return quark; +} + +static void +nm_ppp_manager_init (NMPPPManager *manager) +{ +} + +static GObject * +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + NMPPPManagerPrivate *priv; + DBusGConnection *connection; + static guint32 counter = 0; + + object = G_OBJECT_CLASS (nm_ppp_manager_parent_class)->constructor (type, + n_construct_params, + construct_params); + if (!object) + return NULL; + + priv = NM_PPP_MANAGER_GET_PRIVATE (object); + priv->dbus_manager = nm_dbus_manager_get (); + if (!priv->dbus_manager) { + g_object_unref (object); + return NULL; + } + + connection = nm_dbus_manager_get_connection (priv->dbus_manager); + priv->dbus_path = g_strdup_printf (NM_DBUS_PATH "/PPP/%d", counter++); + dbus_g_connection_register_g_object (connection, priv->dbus_path, object); + + return object; +} + +static void +dispose (GObject *object) +{ + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (object); + + nm_ppp_manager_stop (NM_PPP_MANAGER (object)); + + g_object_unref (priv->act_req); + g_object_unref (priv->dbus_manager); + + G_OBJECT_CLASS (nm_ppp_manager_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (object); + + g_free (priv->ip_iface); + g_free (priv->parent_iface); + + G_OBJECT_CLASS (nm_ppp_manager_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PARENT_IFACE: + if (priv->parent_iface) + g_free (priv->parent_iface); + priv->parent_iface = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PARENT_IFACE: + g_value_set_string (value, priv->parent_iface); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_ppp_manager_class_init (NMPPPManagerClass *manager_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (manager_class); + + g_type_class_add_private (manager_class, sizeof (NMPPPManagerPrivate)); + + object_class->constructor = constructor; + object_class->dispose = dispose; + object_class->finalize = finalize; + object_class->get_property = get_property; + object_class->set_property = set_property; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_PARENT_IFACE, + g_param_spec_string (NM_PPP_MANAGER_PARENT_IFACE, + "ParentIface", + "Parent interface", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /* signals */ + signals[STATE_CHANGED] = + g_signal_new ("state-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMPPPManagerClass, state_changed), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, + G_TYPE_UINT); + + signals[IP4_CONFIG] = + g_signal_new ("ip4-config", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMPPPManagerClass, ip4_config), + NULL, NULL, + _nm_marshal_VOID__STRING_OBJECT, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_OBJECT); + + signals[STATS] = + g_signal_new ("stats", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMPPPManagerClass, stats), + NULL, NULL, + _nm_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, + G_TYPE_UINT, G_TYPE_UINT); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (manager_class), + &dbus_glib_nm_ppp_manager_object_info); +} + +NMPPPManager * +nm_ppp_manager_new (const char *iface) +{ + g_return_val_if_fail (iface != NULL, NULL); + + return (NMPPPManager *) g_object_new (NM_TYPE_PPP_MANAGER, + NM_PPP_MANAGER_PARENT_IFACE, iface, + NULL); +} + +/*******************************************/ + +static gboolean +monitor_cb (gpointer user_data) +{ + NMPPPManager *manager = NM_PPP_MANAGER (user_data); + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + struct ifpppstatsreq req; + + memset (&req, 0, sizeof (req)); + req.stats_ptr = (caddr_t) &req.stats; + + strncpy (req.ifr__name, priv->ip_iface, sizeof (req.ifr__name)); + if (ioctl (priv->monitor_fd, SIOCGPPPSTATS, &req) < 0) { + nm_log_warn (LOGD_PPP, "could not read ppp stats: %s", strerror (errno)); + } else { + g_signal_emit (manager, signals[STATS], 0, + req.stats.p.ppp_ibytes, + req.stats.p.ppp_obytes); + } + + return TRUE; +} + +static void +monitor_stats (NMPPPManager *manager) +{ + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + + priv->monitor_fd = socket (AF_INET, SOCK_DGRAM, 0); + if (priv->monitor_fd > 0) + priv->monitor_id = g_timeout_add_seconds (5, monitor_cb, manager); + else + nm_log_warn (LOGD_PPP, "could not monitor PPP stats: %s", strerror (errno)); +} + +/*******************************************/ + +static void +remove_timeout_handler (NMPPPManager *manager) +{ + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + + if (priv->ppp_timeout_handler) { + g_source_remove (priv->ppp_timeout_handler); + priv->ppp_timeout_handler = 0; + } +} + +static void +impl_ppp_manager_need_secrets (NMPPPManager *manager, + DBusGMethodInvocation *context) +{ + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + NMConnection *connection; + NMSettingConnection *s_con; + const char *connection_type; + const char *setting_name; + guint32 tries; + GPtrArray *hints = NULL; + const char *hint1 = NULL, *hint2 = NULL; + + connection = nm_act_request_get_connection (priv->act_req); + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + + connection_type = nm_setting_connection_get_connection_type (s_con); + g_assert (connection_type); + + nm_connection_clear_secrets (connection); + setting_name = nm_connection_need_secrets (connection, &hints); + if (!setting_name) { + NMSetting *setting; + + setting = nm_connection_get_setting_by_name (connection, connection_type); + if (setting) { + const char *username = NULL; + const char *password = NULL; + + /* FIXME: push this down to the settings and keep PPP manager generic */ + if (NM_IS_SETTING_PPPOE (setting)) { + username = nm_setting_pppoe_get_username (NM_SETTING_PPPOE (setting)); + password = nm_setting_pppoe_get_password (NM_SETTING_PPPOE (setting)); + } else if (NM_IS_SETTING_GSM (setting)) { + username = nm_setting_gsm_get_username (NM_SETTING_GSM (setting)); + password = nm_setting_gsm_get_password (NM_SETTING_GSM (setting)); + } else if (NM_IS_SETTING_CDMA (setting)) { + username = nm_setting_cdma_get_username (NM_SETTING_CDMA (setting)); + password = nm_setting_cdma_get_password (NM_SETTING_CDMA (setting)); + } + + /* If secrets are not required, send the existing username and password + * back to the PPP plugin immediately. + */ + priv->pending_secrets_context = context; + nm_ppp_manager_update_secrets (manager, + priv->parent_iface, + username ? username : "", + password ? password : "", + NULL); + } else { + GError *err = NULL; + + g_set_error (&err, NM_PPP_MANAGER_ERROR, NM_PPP_MANAGER_ERROR_UNKOWN, + "Missing type-specific setting; no secrets could be found."); + nm_log_warn (LOGD_PPP, "%s", err->message); + dbus_g_method_return_error (context, err); + } + return; + } + + /* Extract hints */ + if (hints) { + if (hints->len > 0) + hint1 = g_ptr_array_index (hints, 0); + if (hints->len > 1) + hint2 = g_ptr_array_index (hints, 1); + } + + tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), PPP_MANAGER_SECRET_TRIES)); + /* Only ask for completely new secrets after retrying them once; some PPP + * servers (T-Mobile USA) appear to ask a few times when they actually don't + * even care what you pass back. + */ + nm_act_request_get_secrets (priv->act_req, + setting_name, + tries > 1 ? TRUE : FALSE, + SECRETS_CALLER_PPP, + hint1, + hint2); + g_object_set_data (G_OBJECT (connection), PPP_MANAGER_SECRET_TRIES, GUINT_TO_POINTER (++tries)); + priv->pending_secrets_context = context; + + if (hints) + g_ptr_array_free (hints, TRUE); +} + +static gboolean impl_ppp_manager_set_state (NMPPPManager *manager, + guint32 state, + GError **err) +{ + g_signal_emit (manager, signals[STATE_CHANGED], 0, state); + + return TRUE; +} + +static gboolean +impl_ppp_manager_set_ip4_config (NMPPPManager *manager, + GHashTable *config_hash, + GError **err) +{ + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + NMConnection *connection; + NMSettingPPP *s_ppp; + NMIP4Config *config; + NMIP4Address *addr; + GValue *val; + int i; + + nm_log_info (LOGD_PPP, "PPP manager(IP Config Get) reply received."); + + remove_timeout_handler (manager); + + config = nm_ip4_config_new (); + addr = nm_ip4_address_new (); + nm_ip4_address_set_prefix (addr, 32); + + val = (GValue *) g_hash_table_lookup (config_hash, NM_PPP_IP4_CONFIG_GATEWAY); + if (val) { + nm_ip4_address_set_gateway (addr, g_value_get_uint (val)); + nm_ip4_config_set_ptp_address (config, g_value_get_uint (val)); + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_PPP_IP4_CONFIG_ADDRESS); + if (val) + nm_ip4_address_set_address (addr, g_value_get_uint (val)); + + val = (GValue *) g_hash_table_lookup (config_hash, NM_PPP_IP4_CONFIG_PREFIX); + if (val) + nm_ip4_address_set_prefix (addr, g_value_get_uint (val)); + + if (nm_ip4_address_get_address (addr) && nm_ip4_address_get_prefix (addr)) { + nm_ip4_config_take_address (config, addr); + } else { + nm_log_err (LOGD_PPP, "invalid IPv4 address received!"); + nm_ip4_address_unref (addr); + goto out; + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_PPP_IP4_CONFIG_DNS); + if (val) { + GArray *dns = (GArray *) g_value_get_boxed (val); + + for (i = 0; i < dns->len; i++) + nm_ip4_config_add_nameserver (config, g_array_index (dns, guint, i)); + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_PPP_IP4_CONFIG_WINS); + if (val) { + GArray *wins = (GArray *) g_value_get_boxed (val); + + for (i = 0; i < wins->len; i++) + nm_ip4_config_add_wins (config, g_array_index (wins, guint, i)); + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_PPP_IP4_CONFIG_INTERFACE); + if (!val || !G_VALUE_HOLDS_STRING (val)) { + nm_log_err (LOGD_PPP, "no interface received!"); + goto out; + } + priv->ip_iface = g_value_dup_string (val); + + /* Got successful IP4 config; obviously the secrets worked */ + connection = nm_act_request_get_connection (priv->act_req); + g_assert (connection); + g_object_set_data (G_OBJECT (connection), PPP_MANAGER_SECRET_TRIES, NULL); + + /* Merge in custom MTU */ + s_ppp = (NMSettingPPP *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPP); + if (s_ppp) { + guint32 mtu = nm_setting_ppp_get_mtu (s_ppp); + + if (mtu) + nm_ip4_config_set_mtu (config, mtu); + } + + /* Push the IP4 config up to the device */ + g_signal_emit (manager, signals[IP4_CONFIG], 0, priv->ip_iface, config); + + monitor_stats (manager); + + out: + g_object_unref (config); + + return TRUE; +} + +/*******************************************/ + + + +typedef struct { + GPtrArray *array; + GStringChunk *chunk; +} NMCmdLine; + +static NMCmdLine * +nm_cmd_line_new (void) +{ + NMCmdLine *cmd; + + cmd = g_slice_new (NMCmdLine); + cmd->array = g_ptr_array_new (); + cmd->chunk = g_string_chunk_new (1024); + + return cmd; +} + +static void +nm_cmd_line_destroy (NMCmdLine *cmd) +{ + g_ptr_array_free (cmd->array, TRUE); + g_string_chunk_free (cmd->chunk); + g_slice_free (NMCmdLine, cmd); +} + +static char * +nm_cmd_line_to_str (NMCmdLine *cmd) +{ + char *str; + + g_ptr_array_add (cmd->array, NULL); + str = g_strjoinv (" ", (gchar **) cmd->array->pdata); + g_ptr_array_remove_index (cmd->array, cmd->array->len - 1); + + return str; +} + +static void +nm_cmd_line_add_string (NMCmdLine *cmd, const char *str) +{ + g_ptr_array_add (cmd->array, g_string_chunk_insert (cmd->chunk, str)); +} + +static void +nm_cmd_line_add_int (NMCmdLine *cmd, int i) +{ + char *str; + + str = g_strdup_printf ("%d", i); + nm_cmd_line_add_string (cmd, str); + g_free (str); +} + +/*******************************************/ + +static inline const char * +nm_find_pppd (void) +{ + static const char *pppd_binary_paths[] = { + "/usr/local/sbin/pppd", + "/usr/sbin/pppd", + "/sbin/pppd", + NULL + }; + + const char **pppd_binary = pppd_binary_paths; + + while (*pppd_binary != NULL) { + if (g_file_test (*pppd_binary, G_FILE_TEST_EXISTS)) + break; + pppd_binary++; + } + + return *pppd_binary; +} + +static void +ppp_exit_code (guint pppd_exit_status, GPid pid) +{ + const char *msg; + + switch (pppd_exit_status) { + case 1: + msg = "Fatal pppd error"; + break; + case 2: + msg = "pppd options error"; + break; + case 3: + msg = "No root priv error"; + break; + case 4: + msg = "No ppp module error"; + break; + case 5: + msg = "pppd received a signal"; + break; + case 6: + msg = "Serial port lock failed"; + break; + case 7: + msg = "Serial port open failed"; + break; + case 8: + msg = "Connect script failed"; + break; + case 9: + msg = "Pty program error"; + break; + case 10: + msg = "PPP negotiation failed"; + break; + case 11: + msg = "Peer didn't authenticatie itself"; + break; + case 12: + msg = "Link idle: Idle Seconds reached."; + break; + case 13: + msg = "Connect time limit reached."; + break; + case 14: + msg = "Callback negotiated, call should come back."; + break; + case 15: + msg = "Lack of LCP echo responses"; + break; + case 16: + msg = "A modem hung up the phone"; + break; + case 17: + msg = "Loopback detected"; + break; + case 18: + msg = "The init script failed"; + break; + case 19: + msg = "Authentication error.\n" + "We failed to authenticate ourselves to the peer.\n" + "Maybe bad account or password?"; + break; + default: + msg = "Unknown error"; + } + + nm_log_warn (LOGD_PPP, "pppd pid %d exited with error: %s", pid, msg); +} + +static void +ppp_watch_cb (GPid pid, gint status, gpointer user_data) +{ + NMPPPManager *manager = NM_PPP_MANAGER (user_data); + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + guint err; + + g_assert (pid == priv->pid); + + if (WIFEXITED (status)) { + err = WEXITSTATUS (status); + if (err != 0) + ppp_exit_code (err, priv->pid); + } else if (WIFSTOPPED (status)) { + nm_log_info (LOGD_PPP, "pppd pid %d stopped unexpectedly with signal %d", priv->pid, WSTOPSIG (status)); + } else if (WIFSIGNALED (status)) { + nm_log_info (LOGD_PPP, "pppd pid %d died with signal %d", priv->pid, WTERMSIG (status)); + } else + nm_log_info (LOGD_PPP, "pppd pid %d died from an unknown cause", priv->pid); + + nm_log_dbg (LOGD_PPP, "pppd pid %d cleaned up", priv->pid); + priv->pid = 0; + g_signal_emit (manager, signals[STATE_CHANGED], 0, NM_PPP_STATUS_DEAD); +} + +static gboolean +pppd_timed_out (gpointer data) +{ + NMPPPManager *manager = NM_PPP_MANAGER (data); + + nm_log_warn (LOGD_PPP, "pppd timed out or didn't initialize our dbus module"); + nm_ppp_manager_stop (manager); + + g_signal_emit (manager, signals[STATE_CHANGED], 0, NM_PPP_STATUS_DEAD); + + return FALSE; +} + +static NMCmdLine * +create_pppd_cmd_line (NMPPPManager *self, + NMSettingPPP *setting, + NMSettingPPPOE *pppoe, + const char *ppp_name, + GError **err) +{ + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (self); + const char *ppp_binary; + NMCmdLine *cmd; + const char *ppp_debug; + + ppp_binary = nm_find_pppd (); + if (!ppp_binary) { + g_set_error (err, NM_PPP_MANAGER_ERROR, NM_PPP_MANAGER_ERROR, + "Could not find ppp binary."); + return NULL; + } + + /* Create pppd command line */ + cmd = nm_cmd_line_new (); + nm_cmd_line_add_string (cmd, ppp_binary); + + nm_cmd_line_add_string (cmd, "nodetach"); + nm_cmd_line_add_string (cmd, "lock"); + + /* NM handles setting the default route */ + nm_cmd_line_add_string (cmd, "nodefaultroute"); + + ppp_debug = getenv ("NM_PPP_DEBUG"); + if (ppp_debug) + nm_cmd_line_add_string (cmd, "debug"); + + if (ppp_name) { + nm_cmd_line_add_string (cmd, "user"); + nm_cmd_line_add_string (cmd, ppp_name); + } + + if (pppoe) { + char *dev_str; + const char *pppoe_service; + + nm_cmd_line_add_string (cmd, "plugin"); + nm_cmd_line_add_string (cmd, "rp-pppoe.so"); + + dev_str = g_strdup_printf ("nic-%s", priv->parent_iface); + nm_cmd_line_add_string (cmd, dev_str); + g_free (dev_str); + + pppoe_service = nm_setting_pppoe_get_service (pppoe); + if (pppoe_service) { + nm_cmd_line_add_string (cmd, "rp_pppoe_service"); + nm_cmd_line_add_string (cmd, pppoe_service); + } + } else { + nm_cmd_line_add_string (cmd, priv->parent_iface); + /* Don't send some random address as the local address */ + nm_cmd_line_add_string (cmd, "noipdefault"); + } + + if (nm_setting_ppp_get_baud (setting)) + nm_cmd_line_add_int (cmd, nm_setting_ppp_get_baud (setting)); + + /* noauth by default, because we certainly don't have any information + * with which to verify anything the peer gives us if we ask it to + * authenticate itself, which is what 'auth' really means. + */ + nm_cmd_line_add_string (cmd, "noauth"); + + if (nm_setting_ppp_get_refuse_eap (setting)) + nm_cmd_line_add_string (cmd, "refuse-eap"); + if (nm_setting_ppp_get_refuse_pap (setting)) + nm_cmd_line_add_string (cmd, "refuse-pap"); + if (nm_setting_ppp_get_refuse_chap (setting)) + nm_cmd_line_add_string (cmd, "refuse-chap"); + if (nm_setting_ppp_get_refuse_mschap (setting)) + nm_cmd_line_add_string (cmd, "refuse-mschap"); + if (nm_setting_ppp_get_refuse_mschapv2 (setting)) + nm_cmd_line_add_string (cmd, "refuse-mschap-v2"); + if (nm_setting_ppp_get_nobsdcomp (setting)) + nm_cmd_line_add_string (cmd, "nobsdcomp"); + if (nm_setting_ppp_get_nodeflate (setting)) + nm_cmd_line_add_string (cmd, "nodeflate"); + if (nm_setting_ppp_get_require_mppe (setting)) + nm_cmd_line_add_string (cmd, "require-mppe"); + if (nm_setting_ppp_get_require_mppe_128 (setting)) + nm_cmd_line_add_string (cmd, "require-mppe-128"); + if (nm_setting_ppp_get_mppe_stateful (setting)) + nm_cmd_line_add_string (cmd, "mppe-stateful"); + if (nm_setting_ppp_get_crtscts (setting)) + nm_cmd_line_add_string (cmd, "crtscts"); + + /* Always ask for DNS, we don't have to use them if the connection + * overrides the returned servers. + */ + nm_cmd_line_add_string (cmd, "usepeerdns"); + + if (nm_setting_ppp_get_mru (setting)) { + nm_cmd_line_add_string (cmd, "mru"); + nm_cmd_line_add_int (cmd, nm_setting_ppp_get_mru (setting)); + } + + if (nm_setting_ppp_get_mtu (setting)) { + nm_cmd_line_add_string (cmd, "mtu"); + nm_cmd_line_add_int (cmd, nm_setting_ppp_get_mtu (setting)); + } + + nm_cmd_line_add_string (cmd, "lcp-echo-failure"); + nm_cmd_line_add_int (cmd, nm_setting_ppp_get_lcp_echo_failure (setting)); + + nm_cmd_line_add_string (cmd, "lcp-echo-interval"); + nm_cmd_line_add_int (cmd, nm_setting_ppp_get_lcp_echo_interval (setting)); + + nm_cmd_line_add_string (cmd, "ipparam"); + nm_cmd_line_add_string (cmd, priv->dbus_path); + + nm_cmd_line_add_string (cmd, "plugin"); + nm_cmd_line_add_string (cmd, NM_PPPD_PLUGIN); + + return cmd; +} + +static void +pppd_child_setup (gpointer user_data G_GNUC_UNUSED) +{ + /* We are in the child process at this point */ + pid_t pid = getpid (); + setpgid (pid, pid); +} + +static void +pppoe_fill_defaults (NMSettingPPP *setting) +{ + if (!nm_setting_ppp_get_mtu (setting)) + g_object_set (setting, NM_SETTING_PPP_MTU, (guint32) 1492, NULL); + + if (!nm_setting_ppp_get_mru (setting)) + g_object_set (setting, NM_SETTING_PPP_MRU, (guint32) 1492, NULL); + + if (!nm_setting_ppp_get_lcp_echo_interval (setting)) + g_object_set (setting, NM_SETTING_PPP_LCP_ECHO_INTERVAL, (guint32) 20, NULL); + + if (!nm_setting_ppp_get_lcp_echo_failure (setting)) + g_object_set (setting, NM_SETTING_PPP_LCP_ECHO_FAILURE, (guint32) 3, NULL); + + g_object_set (setting, + NM_SETTING_PPP_NOAUTH, TRUE, + NM_SETTING_PPP_NODEFLATE, TRUE, + NULL); + + /* FIXME: These commented settings should be set as well, update NMSettingPPP first. */ +#if 0 + setting->noipdefault = TRUE; + setting->default_asyncmap = TRUE; + setting->defaultroute = TRUE; + setting->hide_password = TRUE; + setting->noaccomp = TRUE; + setting->nopcomp = TRUE; + setting->novj = TRUE; + setting->novjccomp = TRUE; +#endif +} + +gboolean +nm_ppp_manager_start (NMPPPManager *manager, + NMActRequest *req, + const char *ppp_name, + guint32 timeout_secs, + GError **err) +{ + NMPPPManagerPrivate *priv; + NMConnection *connection; + NMSettingPPP *s_ppp; + gboolean s_ppp_created = FALSE; + NMSettingPPPOE *pppoe_setting; + NMCmdLine *ppp_cmd; + char *cmd_str; + struct stat st; + int ignored; + + g_return_val_if_fail (NM_IS_PPP_MANAGER (manager), FALSE); + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE); + + priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + + priv->pid = 0; + + /* Make sure /dev/ppp exists (bgo #533064) */ + if (stat ("/dev/ppp", &st) || !S_ISCHR (st.st_mode)) + ignored = system ("/sbin/modprobe ppp_generic"); + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + s_ppp = (NMSettingPPP *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPP); + if (!s_ppp) { + /* If the PPP settings are all default we may not have a PPP setting yet, + * so just make a default one here. + */ + s_ppp = NM_SETTING_PPP (nm_setting_ppp_new ()); + s_ppp_created = TRUE; + } + + pppoe_setting = (NMSettingPPPOE *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE); + if (pppoe_setting) + pppoe_fill_defaults (s_ppp); + + ppp_cmd = create_pppd_cmd_line (manager, s_ppp, pppoe_setting, ppp_name, err); + if (!ppp_cmd) + goto out; + + g_ptr_array_add (ppp_cmd->array, NULL); + + nm_log_info (LOGD_PPP, "starting PPP connection"); + + cmd_str = nm_cmd_line_to_str (ppp_cmd); + nm_log_dbg (LOGD_PPP, "command line: %s", cmd_str); + g_free (cmd_str); + + priv->pid = 0; + if (!g_spawn_async (NULL, (char **) ppp_cmd->array->pdata, NULL, + G_SPAWN_DO_NOT_REAP_CHILD, + pppd_child_setup, + NULL, &priv->pid, err)) { + goto out; + } + + nm_log_info (LOGD_PPP, "pppd started with pid %d", priv->pid); + + priv->ppp_watch_id = g_child_watch_add (priv->pid, (GChildWatchFunc) ppp_watch_cb, manager); + priv->ppp_timeout_handler = g_timeout_add_seconds (timeout_secs, pppd_timed_out, manager); + priv->act_req = g_object_ref (req); + + out: + if (s_ppp_created) + g_object_unref (s_ppp); + + if (ppp_cmd) + nm_cmd_line_destroy (ppp_cmd); + + return priv->pid > 0; +} + +void +nm_ppp_manager_update_secrets (NMPPPManager *manager, + const char *device, + const char *username, + const char *password, + const char *error_message) +{ + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + + g_return_if_fail (NM_IS_PPP_MANAGER (manager)); + g_return_if_fail (device != NULL); + g_return_if_fail (priv->pending_secrets_context != NULL); + + if (error_message) { + g_return_if_fail (username == NULL); + g_return_if_fail (password == NULL); + } else { + g_return_if_fail (username != NULL); + g_return_if_fail (password != NULL); + } + + if (error_message) { + GError *err = NULL; + + g_set_error (&err, NM_PPP_MANAGER_ERROR, NM_PPP_MANAGER_ERROR_UNKOWN, "%s", error_message); + nm_log_warn (LOGD_PPP, "%s", error_message); + dbus_g_method_return_error (priv->pending_secrets_context, err); + g_error_free (err); + } else { + /* This is sort of a hack but... + pppd plugin only ever needs username and password. + Passing the full connection there would mean some bloat: + the plugin would need to link against libnm-util just to parse this. + So instead, let's just send what it needs */ + + dbus_g_method_return (priv->pending_secrets_context, username, password); + } + priv->pending_secrets_context = NULL; +} + +static gboolean +ensure_killed (gpointer data) +{ + int pid = GPOINTER_TO_INT (data); + + if (kill (pid, 0) == 0) + kill (pid, SIGKILL); + + /* ensure the child is reaped */ + nm_log_dbg (LOGD_PPP, "waiting for pppd pid %d to exit", pid); + waitpid (pid, NULL, 0); + nm_log_dbg (LOGD_PPP, "pppd pid %d cleaned up", pid); + + return FALSE; +} + +void +nm_ppp_manager_stop (NMPPPManager *manager) +{ + NMPPPManagerPrivate *priv; + + g_return_if_fail (NM_IS_PPP_MANAGER (manager)); + + priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + + if (priv->monitor_id) { + g_source_remove (priv->monitor_id); + priv->monitor_id = 0; + } + + if (priv->monitor_fd) { + /* Get the stats one last time */ + monitor_cb (manager); + close (priv->monitor_fd); + priv->monitor_fd = 0; + } + + if (priv->ppp_timeout_handler) { + g_source_remove (priv->ppp_timeout_handler); + priv->ppp_timeout_handler = 0; + } + + if (priv->ppp_watch_id) { + g_source_remove (priv->ppp_watch_id); + priv->ppp_watch_id = 0; + } + + if (priv->pid) { + if (kill (priv->pid, SIGTERM) == 0) + g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid)); + else { + kill (priv->pid, SIGKILL); + + /* ensure the child is reaped */ + nm_log_dbg (LOGD_PPP, "waiting for pppd pid %d to exit", priv->pid); + waitpid (priv->pid, NULL, 0); + nm_log_dbg (LOGD_PPP, "pppd pid %d cleaned up", priv->pid); + } + + priv->pid = 0; + } +} diff --git a/src/ppp-manager/nm-ppp-manager.h b/src/ppp-manager/nm-ppp-manager.h new file mode 100644 index 00000000..a0200973 --- /dev/null +++ b/src/ppp-manager/nm-ppp-manager.h @@ -0,0 +1,80 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + */ + +#ifndef NM_PPP_MANAGER_H +#define NM_PPP_MANAGER_H + +#include <glib.h> +#include <glib-object.h> + +#include "nm-ppp-status.h" +#include "nm-activation-request.h" +#include "nm-connection.h" +#include "nm-ip4-config.h" +#include "nm-pppd-plugin.h" + +#define NM_TYPE_PPP_MANAGER (nm_ppp_manager_get_type ()) +#define NM_PPP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_PPP_MANAGER, NMPPPManager)) +#define NM_PPP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_PPP_MANAGER, NMPPPManagerClass)) +#define NM_IS_PPP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_PPP_MANAGER)) +#define NM_IS_PPP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_PPP_MANAGER)) +#define NM_PPP_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_PPP_MANAGER, NMPPPManagerClass)) + +#define NM_PPP_MANAGER_PARENT_IFACE "parent-iface" + +typedef struct { + GObject parent; +} NMPPPManager; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*state_changed) (NMPPPManager *manager, NMPPPStatus status); + void (*ip4_config) (NMPPPManager *manager, const char *iface, NMIP4Config *config); + void (*stats) (NMPPPManager *manager, guint32 in_bytes, guint32 out_bytes); +} NMPPPManagerClass; + +GType nm_ppp_manager_get_type (void); + +NMPPPManager *nm_ppp_manager_new (const char *iface); + +gboolean nm_ppp_manager_start (NMPPPManager *manager, + NMActRequest *req, + const char *ppp_name, + guint32 timeout_secs, + GError **err); + +void nm_ppp_manager_update_secrets (NMPPPManager *manager, + const char *device, + const char *username, + const char *password, + const char *error_message); + +void nm_ppp_manager_stop (NMPPPManager *manager); + + +#define NM_PPP_MANAGER_ERROR nm_ppp_manager_error_quark() +#define NM_TYPE_PPP_MANAGER_ERROR (nm_ppp_manager_error_get_type ()) + +GQuark nm_ppp_manager_error_quark (void); + +#endif /* NM_PPP_MANAGER_H */ diff --git a/src/ppp-manager/nm-ppp-status.h b/src/ppp-manager/nm-ppp-status.h new file mode 100644 index 00000000..cfc7c393 --- /dev/null +++ b/src/ppp-manager/nm-ppp-status.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + */ + +#ifndef NM_PPP_STATUS_H +#define NM_PPP_STATUS_H + +typedef enum { + NM_PPP_STATUS_UNKNOWN, + + NM_PPP_STATUS_DEAD, + NM_PPP_STATUS_INITIALIZE, + NM_PPP_STATUS_SERIALCONN, + NM_PPP_STATUS_DORMANT, + NM_PPP_STATUS_ESTABLISH, + NM_PPP_STATUS_AUTHENTICATE, + NM_PPP_STATUS_CALLBACK, + NM_PPP_STATUS_NETWORK, + NM_PPP_STATUS_RUNNING, + NM_PPP_STATUS_TERMINATE, + NM_PPP_STATUS_DISCONNECT, + NM_PPP_STATUS_HOLDOFF, + NM_PPP_STATUS_MASTER +} NMPPPStatus; + +#endif /* NM_PPP_STATUS_H */ diff --git a/src/ppp-manager/nm-pppd-plugin.c b/src/ppp-manager/nm-pppd-plugin.c new file mode 100644 index 00000000..c64bb87f --- /dev/null +++ b/src/ppp-manager/nm-pppd-plugin.c @@ -0,0 +1,359 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + */ + +#include <string.h> +#include <pppd/pppd.h> +#include <pppd/fsm.h> +#include <pppd/ipcp.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <glib.h> +#include <glib-object.h> +#include <dbus/dbus-glib.h> + +#include "NetworkManager.h" +#include "nm-pppd-plugin.h" +#include "nm-ppp-status.h" +#include "nm-dbus-glib-types.h" + +int plugin_init (void); + +char pppd_version[] = VERSION; + +static DBusGProxy *proxy = NULL; + +static void +nm_phasechange (void *data, int arg) +{ + NMPPPStatus ppp_status = NM_PPP_STATUS_UNKNOWN; + char *ppp_phase; + + g_return_if_fail (DBUS_IS_G_PROXY (proxy)); + + switch (arg) { + case PHASE_DEAD: + ppp_status = NM_PPP_STATUS_DEAD; + ppp_phase = "dead"; + break; + case PHASE_INITIALIZE: + ppp_status = NM_PPP_STATUS_INITIALIZE; + ppp_phase = "initialize"; + break; + case PHASE_SERIALCONN: + ppp_status = NM_PPP_STATUS_SERIALCONN; + ppp_phase = "serial connection"; + break; + case PHASE_DORMANT: + ppp_status = NM_PPP_STATUS_DORMANT; + ppp_phase = "dormant"; + break; + case PHASE_ESTABLISH: + ppp_status = NM_PPP_STATUS_ESTABLISH; + ppp_phase = "establish"; + break; + case PHASE_AUTHENTICATE: + ppp_status = NM_PPP_STATUS_AUTHENTICATE; + ppp_phase = "authenticate"; + break; + case PHASE_CALLBACK: + ppp_status = NM_PPP_STATUS_CALLBACK; + ppp_phase = "callback"; + break; + case PHASE_NETWORK: + ppp_status = NM_PPP_STATUS_NETWORK; + ppp_phase = "network"; + break; + case PHASE_RUNNING: + ppp_status = NM_PPP_STATUS_RUNNING; + ppp_phase = "running"; + break; + case PHASE_TERMINATE: + ppp_status = NM_PPP_STATUS_TERMINATE; + ppp_phase = "terminate"; + break; + case PHASE_DISCONNECT: + ppp_status = NM_PPP_STATUS_DISCONNECT; + ppp_phase = "disconnect"; + break; + case PHASE_HOLDOFF: + ppp_status = NM_PPP_STATUS_HOLDOFF; + ppp_phase = "holdoff"; + break; + case PHASE_MASTER: + ppp_status = NM_PPP_STATUS_MASTER; + ppp_phase = "master"; + break; + + default: + ppp_phase = "unknown"; + break; + } + + g_message ("nm-ppp-plugin: (%s): status %d / phase '%s'", + __func__, + ppp_status, + ppp_phase); + + if (ppp_status != NM_PPP_STATUS_UNKNOWN) { + dbus_g_proxy_call_no_reply (proxy, "SetState", + G_TYPE_UINT, ppp_status, G_TYPE_INVALID, + G_TYPE_INVALID); + } +} + +static GValue * +str_to_gvalue (const char *str) +{ + GValue *val; + + val = g_slice_new0 (GValue); + g_value_init (val, G_TYPE_STRING); + g_value_set_string (val, str); + + return val; +} + +static GValue * +uint_to_gvalue (guint32 i) +{ + GValue *val; + + val = g_slice_new0 (GValue); + g_value_init (val, G_TYPE_UINT); + g_value_set_uint (val, i); + + return val; +} + +static void +value_destroy (gpointer data) +{ + GValue *val = (GValue *) data; + + g_value_unset (val); + g_slice_free (GValue, val); +} + +static void +nm_ip_up (void *data, int arg) +{ + ipcp_options opts = ipcp_gotoptions[0]; + ipcp_options peer_opts = ipcp_hisoptions[0]; + GHashTable *hash; + GArray *array; + GValue *val; + guint32 pppd_made_up_address = htonl (0x0a404040 + ifunit); + + g_return_if_fail (DBUS_IS_G_PROXY (proxy)); + + g_message ("nm-ppp-plugin: (%s): ip-up event", __func__); + + if (!opts.ouraddr) { + g_warning ("nm-ppp-plugin: (%s): didn't receive an internal IP from pppd!", __func__); + nm_phasechange (NULL, PHASE_DEAD); + return; + } + + hash = g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, value_destroy); + + g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_INTERFACE, + str_to_gvalue (ifname)); + + g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_ADDRESS, + uint_to_gvalue (opts.ouraddr)); + + /* Prefer the peer options remote address first, _unless_ pppd made the + * address up, at which point prefer the local options remote address, + * and if that's not right, use the made-up address as a last resort. + */ + if (peer_opts.hisaddr && (peer_opts.hisaddr != pppd_made_up_address)) { + g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_GATEWAY, + uint_to_gvalue (peer_opts.hisaddr)); + } else if (opts.hisaddr) { + g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_GATEWAY, + uint_to_gvalue (opts.hisaddr)); + } else if (peer_opts.hisaddr == pppd_made_up_address) { + /* As a last resort, use the made-up address */ + g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_GATEWAY, + uint_to_gvalue (peer_opts.hisaddr)); + } + + g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_PREFIX, uint_to_gvalue (32)); + + if (opts.dnsaddr[0] || opts.dnsaddr[1]) { + array = g_array_new (FALSE, FALSE, sizeof (guint32)); + + if (opts.dnsaddr[0]) + g_array_append_val (array, opts.dnsaddr[0]); + if (opts.dnsaddr[1]) + g_array_append_val (array, opts.dnsaddr[1]); + + val = g_slice_new0 (GValue); + g_value_init (val, DBUS_TYPE_G_UINT_ARRAY); + g_value_set_boxed (val, array); + + g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_DNS, val); + } + + if (opts.winsaddr[0] || opts.winsaddr[1]) { + array = g_array_new (FALSE, FALSE, sizeof (guint32)); + + if (opts.winsaddr[0]) + g_array_append_val (array, opts.winsaddr[0]); + if (opts.winsaddr[1]) + g_array_append_val (array, opts.winsaddr[1]); + + val = g_slice_new0 (GValue); + g_value_init (val, DBUS_TYPE_G_UINT_ARRAY); + g_value_set_boxed (val, array); + + g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_WINS, val); + } + + g_message ("nm-ppp-plugin: (%s): sending Ip4Config to NetworkManager...", __func__); + + dbus_g_proxy_call_no_reply (proxy, "SetIp4Config", + DBUS_TYPE_G_MAP_OF_VARIANT, hash, G_TYPE_INVALID, + G_TYPE_INVALID); + + g_hash_table_destroy (hash); +} + +static int +get_chap_check() +{ + return 1; +} + +static int +get_pap_check() +{ + return 1; +} + +static int +get_credentials (char *username, char *password) +{ + char *my_username = NULL; + char *my_password = NULL; + size_t len; + GError *err = NULL; + + if (username && !password) { + /* pppd is checking pap support; return 1 for supported */ + return 1; + } + + g_return_val_if_fail (DBUS_IS_G_PROXY (proxy), -1); + + g_message ("nm-ppp-plugin: (%s): passwd-hook, requesting credentials...", __func__); + + dbus_g_proxy_call (proxy, "NeedSecrets", &err, + G_TYPE_INVALID, + G_TYPE_STRING, &my_username, + G_TYPE_STRING, &my_password, + G_TYPE_INVALID); + + if (err) { + g_warning ("nm-ppp-plugin: (%s): could not get secrets: (%d) %s", + __func__, + err ? err->code : -1, + err->message ? err->message : "(unknown)"); + g_error_free (err); + return -1; + } + + g_message ("nm-ppp-plugin: (%s): got credentials from NetworkManager", __func__); + + if (my_username) { + len = strlen (my_username) + 1; + len = len < MAXNAMELEN ? len : MAXNAMELEN; + + strncpy (username, my_username, len); + username[len - 1] = '\0'; + + g_free (my_username); + } + + if (my_password) { + len = strlen (my_password) + 1; + len = len < MAXSECRETLEN ? len : MAXSECRETLEN; + + strncpy (password, my_password, len); + password[len - 1] = '\0'; + + g_free (my_password); + } + + return 1; +} + +static void +nm_exit_notify (void *data, int arg) +{ + g_return_if_fail (DBUS_IS_G_PROXY (proxy)); + + g_message ("nm-ppp-plugin: (%s): cleaning up", __func__); + + g_object_unref (proxy); + proxy = NULL; +} + +int +plugin_init (void) +{ + DBusGConnection *bus; + GError *err = NULL; + + g_type_init (); + + g_message ("nm-ppp-plugin: (%s): initializing", __func__); + + bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err); + if (!bus) { + g_warning ("nm-pppd-plugin: (%s): couldn't connect to system bus: (%d) %s", + __func__, + err ? err->code : -1, + err && err->message ? err->message : "(unknown)"); + g_error_free (err); + return -1; + } + + /* NM passes in the object path of the corresponding PPPManager + * object as the 'ipparam' argument to pppd. + */ + proxy = dbus_g_proxy_new_for_name (bus, NM_DBUS_SERVICE, ipparam, NM_DBUS_INTERFACE_PPP); + + dbus_g_connection_unref (bus); + + chap_passwd_hook = get_credentials; + chap_check_hook = get_chap_check; + pap_passwd_hook = get_credentials; + pap_check_hook = get_pap_check; + + add_notifier (&phasechange, nm_phasechange, NULL); + add_notifier (&ip_up_notifier, nm_ip_up, NULL); + add_notifier (&exitnotify, nm_exit_notify, proxy); + + return 0; +} diff --git a/src/ppp-manager/nm-pppd-plugin.h b/src/ppp-manager/nm-pppd-plugin.h new file mode 100644 index 00000000..95a2a182 --- /dev/null +++ b/src/ppp-manager/nm-pppd-plugin.h @@ -0,0 +1,29 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + */ + +#define NM_DBUS_INTERFACE_PPP "org.freedesktop.NetworkManager.PPP" + +#define NM_PPP_IP4_CONFIG_INTERFACE "interface" +#define NM_PPP_IP4_CONFIG_ADDRESS "address" +#define NM_PPP_IP4_CONFIG_PREFIX "prefix" +#define NM_PPP_IP4_CONFIG_GATEWAY "gateway" +#define NM_PPP_IP4_CONFIG_DNS "dns" +#define NM_PPP_IP4_CONFIG_WINS "wins" diff --git a/src/supplicant-manager/Makefile.am b/src/supplicant-manager/Makefile.am new file mode 100644 index 00000000..e59ae843 --- /dev/null +++ b/src/supplicant-manager/Makefile.am @@ -0,0 +1,35 @@ +SUBDIRS=. tests + +INCLUDES = \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/include \ + -I${top_srcdir}/libnm-util \ + -I${top_builddir}/marshallers \ + -I${top_srcdir}/src/named-manager + +noinst_LTLIBRARIES = libsupplicant-manager.la + +libsupplicant_manager_la_SOURCES = \ + nm-supplicant-types.h \ + nm-supplicant-manager.h \ + nm-supplicant-manager.c \ + nm-supplicant-config.h \ + nm-supplicant-config.c \ + nm-supplicant-interface.c \ + nm-supplicant-interface.h \ + nm-supplicant-settings-verify.h \ + nm-supplicant-settings-verify.c + +libsupplicant_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + -DNM_PKGDATADIR=\"$(pkgdatadir)\" \ + -DNM_LOCALSTATEDIR=\"$(localstatedir)\" + +libsupplicant_manager_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + diff --git a/src/supplicant-manager/Makefile.in b/src/supplicant-manager/Makefile.in new file mode 100644 index 00000000..9d326ba7 --- /dev/null +++ b/src/supplicant-manager/Makefile.in @@ -0,0 +1,760 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/supplicant-manager +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libsupplicant_manager_la_DEPENDENCIES = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libsupplicant_manager_la_OBJECTS = \ + libsupplicant_manager_la-nm-supplicant-manager.lo \ + libsupplicant_manager_la-nm-supplicant-config.lo \ + libsupplicant_manager_la-nm-supplicant-interface.lo \ + libsupplicant_manager_la-nm-supplicant-settings-verify.lo +libsupplicant_manager_la_OBJECTS = \ + $(am_libsupplicant_manager_la_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libsupplicant_manager_la_SOURCES) +DIST_SOURCES = $(libsupplicant_manager_la_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-dvi-recursive install-exec-recursive \ + install-html-recursive install-info-recursive \ + install-pdf-recursive install-ps-recursive install-recursive \ + installcheck-recursive installdirs-recursive pdf-recursive \ + ps-recursive uninstall-recursive +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ + $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ + distdir +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +IPTABLES_PATH = @IPTABLES_PATH@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBM = @LIBM@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POFILES = @POFILES@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +RANLIB = @RANLIB@ +RESOLVCONF_PATH = @RESOLVCONF_PATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = . tests +INCLUDES = \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/include \ + -I${top_srcdir}/libnm-util \ + -I${top_builddir}/marshallers \ + -I${top_srcdir}/src/named-manager + +noinst_LTLIBRARIES = libsupplicant-manager.la +libsupplicant_manager_la_SOURCES = \ + nm-supplicant-types.h \ + nm-supplicant-manager.h \ + nm-supplicant-manager.c \ + nm-supplicant-config.h \ + nm-supplicant-config.c \ + nm-supplicant-interface.c \ + nm-supplicant-interface.h \ + nm-supplicant-settings-verify.h \ + nm-supplicant-settings-verify.c + +libsupplicant_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + -DNM_PKGDATADIR=\"$(pkgdatadir)\" \ + -DNM_LOCALSTATEDIR=\"$(localstatedir)\" + +libsupplicant_manager_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/supplicant-manager/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/supplicant-manager/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libsupplicant-manager.la: $(libsupplicant_manager_la_OBJECTS) $(libsupplicant_manager_la_DEPENDENCIES) + $(LINK) $(libsupplicant_manager_la_OBJECTS) $(libsupplicant_manager_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsupplicant_manager_la-nm-supplicant-config.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsupplicant_manager_la-nm-supplicant-interface.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsupplicant_manager_la-nm-supplicant-manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsupplicant_manager_la-nm-supplicant-settings-verify.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libsupplicant_manager_la-nm-supplicant-manager.lo: nm-supplicant-manager.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsupplicant_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsupplicant_manager_la-nm-supplicant-manager.lo -MD -MP -MF $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-manager.Tpo -c -o libsupplicant_manager_la-nm-supplicant-manager.lo `test -f 'nm-supplicant-manager.c' || echo '$(srcdir)/'`nm-supplicant-manager.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-manager.Tpo $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-manager.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-supplicant-manager.c' object='libsupplicant_manager_la-nm-supplicant-manager.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsupplicant_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsupplicant_manager_la-nm-supplicant-manager.lo `test -f 'nm-supplicant-manager.c' || echo '$(srcdir)/'`nm-supplicant-manager.c + +libsupplicant_manager_la-nm-supplicant-config.lo: nm-supplicant-config.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsupplicant_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsupplicant_manager_la-nm-supplicant-config.lo -MD -MP -MF $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-config.Tpo -c -o libsupplicant_manager_la-nm-supplicant-config.lo `test -f 'nm-supplicant-config.c' || echo '$(srcdir)/'`nm-supplicant-config.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-config.Tpo $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-config.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-supplicant-config.c' object='libsupplicant_manager_la-nm-supplicant-config.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsupplicant_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsupplicant_manager_la-nm-supplicant-config.lo `test -f 'nm-supplicant-config.c' || echo '$(srcdir)/'`nm-supplicant-config.c + +libsupplicant_manager_la-nm-supplicant-interface.lo: nm-supplicant-interface.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsupplicant_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsupplicant_manager_la-nm-supplicant-interface.lo -MD -MP -MF $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-interface.Tpo -c -o libsupplicant_manager_la-nm-supplicant-interface.lo `test -f 'nm-supplicant-interface.c' || echo '$(srcdir)/'`nm-supplicant-interface.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-interface.Tpo $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-interface.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-supplicant-interface.c' object='libsupplicant_manager_la-nm-supplicant-interface.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsupplicant_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsupplicant_manager_la-nm-supplicant-interface.lo `test -f 'nm-supplicant-interface.c' || echo '$(srcdir)/'`nm-supplicant-interface.c + +libsupplicant_manager_la-nm-supplicant-settings-verify.lo: nm-supplicant-settings-verify.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsupplicant_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsupplicant_manager_la-nm-supplicant-settings-verify.lo -MD -MP -MF $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-settings-verify.Tpo -c -o libsupplicant_manager_la-nm-supplicant-settings-verify.lo `test -f 'nm-supplicant-settings-verify.c' || echo '$(srcdir)/'`nm-supplicant-settings-verify.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-settings-verify.Tpo $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-settings-verify.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-supplicant-settings-verify.c' object='libsupplicant_manager_la-nm-supplicant-settings-verify.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsupplicant_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsupplicant_manager_la-nm-supplicant-settings-verify.lo `test -f 'nm-supplicant-settings-verify.c' || echo '$(srcdir)/'`nm-supplicant-settings-verify.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +$(RECURSIVE_CLEAN_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \ + install-am install-strip tags-recursive + +.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ + all all-am check check-am clean clean-generic clean-libtool \ + clean-noinstLTLIBRARIES ctags ctags-recursive distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \ + uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/supplicant-manager/nm-supplicant-config.c b/src/supplicant-manager/nm-supplicant-config.c new file mode 100644 index 00000000..b516de52 --- /dev/null +++ b/src/supplicant-manager/nm-supplicant-config.c @@ -0,0 +1,1014 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2010 Red Hat, Inc. + * Copyright (C) 2007 - 2008 Novell, Inc. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <stdlib.h> +#include <glib.h> +#include <netinet/ether.h> +#include <dbus/dbus-glib.h> + +#include "nm-supplicant-config.h" +#include "nm-supplicant-settings-verify.h" +#include "nm-logging.h" +#include "nm-setting.h" +#include "NetworkManagerUtils.h" + +static char *hexstr2bin (const char *hex, size_t len); + +#define NM_SUPPLICANT_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + NM_TYPE_SUPPLICANT_CONFIG, \ + NMSupplicantConfigPrivate)) + +G_DEFINE_TYPE (NMSupplicantConfig, nm_supplicant_config, G_TYPE_OBJECT) + +typedef struct { + char *value; + guint32 len; + OptType type; +} ConfigOption; + +typedef struct +{ + GHashTable *config; + GHashTable *blobs; + guint32 ap_scan; + gboolean dispose_has_run; +} NMSupplicantConfigPrivate; + +NMSupplicantConfig * +nm_supplicant_config_new (void) +{ + return g_object_new (NM_TYPE_SUPPLICANT_CONFIG, NULL); +} + +static void +config_option_free (ConfigOption *opt) +{ + g_free (opt->value); + g_slice_free (ConfigOption, opt); +} + +static void +blob_free (GByteArray *array) +{ + g_byte_array_free (array, TRUE); +} + +static void +nm_supplicant_config_init (NMSupplicantConfig * self) +{ + NMSupplicantConfigPrivate *priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self); + + priv->config = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) config_option_free); + + priv->blobs = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) blob_free); + + priv->ap_scan = 1; + priv->dispose_has_run = FALSE; +} + +static gboolean +nm_supplicant_config_add_option_with_type (NMSupplicantConfig *self, + const char *key, + const char *value, + gint32 len, + OptType opt_type, + gboolean secret) +{ + NMSupplicantConfigPrivate *priv; + ConfigOption *old_opt; + ConfigOption *opt; + OptType type; + + g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE); + g_return_val_if_fail (key != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self); + + if (len < 0) + len = strlen (value); + + if (opt_type != TYPE_INVALID) + type = opt_type; + else { + type = nm_supplicant_settings_verify_setting (key, value, len); + if (type == TYPE_INVALID) { + char buf[255]; + memset (&buf[0], 0, sizeof (buf)); + memcpy (&buf[0], value, len > 254 ? 254 : len); + nm_log_warn (LOGD_SUPPLICANT, "Key '%s' and/or value '%s' invalid.", key, buf); + return FALSE; + } + } + + old_opt = (ConfigOption *) g_hash_table_lookup (priv->config, key); + if (old_opt) { + nm_log_warn (LOGD_SUPPLICANT, "Key '%s' already in table.", key); + return FALSE; + } + + opt = g_slice_new0 (ConfigOption); + if (opt == NULL) { + nm_log_warn (LOGD_SUPPLICANT, "Couldn't allocate memory for new config option."); + return FALSE; + } + + opt->value = g_malloc0 ((sizeof (char) * len) + 1); + if (opt->value == NULL) { + nm_log_warn (LOGD_SUPPLICANT, "Couldn't allocate memory for new config option value."); + g_slice_free (ConfigOption, opt); + return FALSE; + } + memcpy (opt->value, value, len); + + opt->len = len; + opt->type = type; + + { + char buf[255]; + memset (&buf[0], 0, sizeof (buf)); + memcpy (&buf[0], opt->value, opt->len > 254 ? 254 : opt->len); + nm_log_info (LOGD_SUPPLICANT, "Config: added '%s' value '%s'", key, secret ? "<omitted>" : &buf[0]); + } + + g_hash_table_insert (priv->config, g_strdup (key), opt); + + return TRUE; +} + +static gboolean +nm_supplicant_config_add_option (NMSupplicantConfig *self, + const char *key, + const char *value, + gint32 len, + gboolean secret) +{ + return nm_supplicant_config_add_option_with_type (self, key, value, len, TYPE_INVALID, secret); +} + +static gboolean +nm_supplicant_config_add_blob (NMSupplicantConfig *self, + const char *key, + const GByteArray *value, + const char *blobid) +{ + NMSupplicantConfigPrivate *priv; + ConfigOption *old_opt; + ConfigOption *opt; + OptType type; + GByteArray *blob; + + g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE); + g_return_val_if_fail (key != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + g_return_val_if_fail (value->len > 0, FALSE); + g_return_val_if_fail (blobid != NULL, FALSE); + + priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self); + + type = nm_supplicant_settings_verify_setting (key, (const char *) value->data, value->len); + if (type == TYPE_INVALID) { + nm_log_warn (LOGD_SUPPLICANT, "Key '%s' and/or it's contained value is invalid.", key); + return FALSE; + } + + old_opt = (ConfigOption *) g_hash_table_lookup (priv->config, key); + if (old_opt) { + nm_log_warn (LOGD_SUPPLICANT, "Key '%s' already in table.", key); + return FALSE; + } + + blob = g_byte_array_sized_new (value->len); + if (!blob) { + nm_log_warn (LOGD_SUPPLICANT, "Couldn't allocate memory for new config blob."); + return FALSE; + } + g_byte_array_append (blob, value->data, value->len); + + opt = g_slice_new0 (ConfigOption); + if (opt == NULL) { + nm_log_warn (LOGD_SUPPLICANT, "Couldn't allocate memory for new config option."); + g_byte_array_free (blob, TRUE); + return FALSE; + } + + opt->value = g_strdup_printf ("blob://%s", blobid); + if (opt->value == NULL) { + nm_log_warn (LOGD_SUPPLICANT, "Couldn't allocate memory for new config option value."); + g_byte_array_free (blob, TRUE); + g_slice_free (ConfigOption, opt); + return FALSE; + } + + opt->len = strlen (opt->value); + opt->type = type; + + nm_log_info (LOGD_SUPPLICANT, "Config: added '%s' value '%s'", key, opt->value); + + g_hash_table_insert (priv->config, g_strdup (key), opt); + g_hash_table_insert (priv->blobs, g_strdup (blobid), blob); + + return TRUE; +} + +static void +nm_supplicant_config_finalize (GObject *object) +{ + /* Complete object destruction */ + g_hash_table_destroy (NM_SUPPLICANT_CONFIG_GET_PRIVATE (object)->config); + g_hash_table_destroy (NM_SUPPLICANT_CONFIG_GET_PRIVATE (object)->blobs); + + /* Chain up to the parent class */ + G_OBJECT_CLASS (nm_supplicant_config_parent_class)->finalize (object); +} + + +static void +nm_supplicant_config_class_init (NMSupplicantConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = nm_supplicant_config_finalize; + + g_type_class_add_private (object_class, sizeof (NMSupplicantConfigPrivate)); +} + +guint32 +nm_supplicant_config_get_ap_scan (NMSupplicantConfig * self) +{ + g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), 1); + + return NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->ap_scan; +} + +void +nm_supplicant_config_set_ap_scan (NMSupplicantConfig * self, + guint32 ap_scan) +{ + g_return_if_fail (NM_IS_SUPPLICANT_CONFIG (self)); + g_return_if_fail (ap_scan >= 0 && ap_scan <= 2); + + NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->ap_scan = ap_scan; +} + +static void +get_hash_cb (gpointer key, gpointer value, gpointer user_data) +{ + ConfigOption *opt = (ConfigOption *) value; + GValue *variant; + GByteArray *array; + + variant = g_slice_new0 (GValue); + + switch (opt->type) { + case TYPE_INT: + g_value_init (variant, G_TYPE_INT); + g_value_set_int (variant, atoi (opt->value)); + break; + case TYPE_BYTES: + array = g_byte_array_sized_new (opt->len); + g_byte_array_append (array, (const guint8 *) opt->value, opt->len); + g_value_init (variant, DBUS_TYPE_G_UCHAR_ARRAY); + g_value_set_boxed (variant, array); + g_byte_array_free (array, TRUE); + break; + case TYPE_KEYWORD: + case TYPE_STRING: + g_value_init (variant, G_TYPE_STRING); + g_value_set_string (variant, opt->value); + break; + default: + g_slice_free (GValue, variant); + return; + } + + g_hash_table_insert ((GHashTable *) user_data, g_strdup (key), variant); +} + +static void +destroy_hash_value (gpointer data) +{ + GValue *value = (GValue *) data; + + g_value_unset (value); + g_slice_free (GValue, value); +} + +GHashTable * +nm_supplicant_config_get_hash (NMSupplicantConfig * self) +{ + NMSupplicantConfigPrivate *priv; + GHashTable *hash; + + g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), NULL); + + hash = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + destroy_hash_value); + + priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self); + g_hash_table_foreach (priv->config, get_hash_cb, hash); + return hash; +} + +GHashTable * +nm_supplicant_config_get_blobs (NMSupplicantConfig * self) +{ + g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), NULL); + + return NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->blobs; +} + +#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" +#define MAC_ARG(x) ((guint8*)(x))[0],((guint8*)(x))[1],((guint8*)(x))[2],((guint8*)(x))[3],((guint8*)(x))[4],((guint8*)(x))[5] + +gboolean +nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self, + NMSettingWireless * setting, + gboolean is_broadcast, + guint32 adhoc_freq, + gboolean has_scan_capa_ssid) +{ + NMSupplicantConfigPrivate *priv; + gboolean is_adhoc; + const char *mode; + const GByteArray *id; + + g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE); + g_return_val_if_fail (setting != NULL, FALSE); + + priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self); + + mode = nm_setting_wireless_get_mode (setting); + is_adhoc = (mode && !strcmp (mode, "adhoc")) ? TRUE : FALSE; + if (is_adhoc) + priv->ap_scan = 2; + else if (is_broadcast == FALSE) { + /* drivers that support scanning specific SSIDs should use + * ap_scan=1, while those that do not should use ap_scan=2. + */ + priv->ap_scan = has_scan_capa_ssid ? 1 : 2; + } + + id = nm_setting_wireless_get_ssid (setting); + if (!nm_supplicant_config_add_option (self, "ssid", (char *) id->data, id->len, FALSE)) { + nm_log_warn (LOGD_SUPPLICANT, "Error adding SSID to supplicant config."); + return FALSE; + } + + if (is_adhoc) { + if (!nm_supplicant_config_add_option (self, "mode", "1", -1, FALSE)) { + nm_log_warn (LOGD_SUPPLICANT, "Error adding mode to supplicant config."); + return FALSE; + } + + if (adhoc_freq) { + char *str_freq; + + str_freq = g_strdup_printf ("%u", adhoc_freq); + if (!nm_supplicant_config_add_option (self, "frequency", str_freq, -1, FALSE)) { + g_free (str_freq); + nm_log_warn (LOGD_SUPPLICANT, "Error adding Ad-Hoc frequency to supplicant config."); + return FALSE; + } + g_free (str_freq); + } + } + + /* Except for Ad-Hoc networks, request that the driver probe for the + * specific SSID we want to associate with. + */ + if (!is_adhoc) { + if (!nm_supplicant_config_add_option (self, "scan_ssid", "1", -1, FALSE)) + return FALSE; + } + + id = nm_setting_wireless_get_bssid (setting); + if (id && id->len) { + char *str_bssid; + + str_bssid = g_strdup_printf (MAC_FMT, MAC_ARG (id->data)); + if (!nm_supplicant_config_add_option (self, "bssid", + str_bssid, strlen (str_bssid), + FALSE)) { + g_free (str_bssid); + nm_log_warn (LOGD_SUPPLICANT, "Error adding BSSID to supplicant config."); + return FALSE; + } + g_free (str_bssid); + } + + // FIXME: band & channel config items + + return TRUE; +} + +static gboolean +add_string_val (NMSupplicantConfig *self, + const char *field, + const char *name, + gboolean ucase, + gboolean secret) +{ + gboolean success; + char *value; + + if (!field) + return TRUE; + + value = ucase ? g_ascii_strup (field, -1) : g_strdup (field); + success = nm_supplicant_config_add_option (self, name, value, strlen (field), secret); + if (!success) + nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name); + g_free (value); + return success; +} + +#define ADD_STRING_LIST_VAL(setting, setting_name, field, field_plural, name, ucase, secret) \ + if (nm_setting_##setting_name##_get_num_##field_plural (setting)) { \ + guint32 k; \ + GString *str = g_string_new (NULL); \ + for (k = 0; k < nm_setting_##setting_name##_get_num_##field_plural (setting); k++) { \ + const char *item = nm_setting_##setting_name##_get_##field (setting, k); \ + if (!str->len) { \ + g_string_append (str, item); \ + } else { \ + g_string_append_c (str, ' '); \ + g_string_append (str, item); \ + } \ + } \ + if (ucase) \ + g_string_ascii_up (str); \ + if (str->len) \ + success = nm_supplicant_config_add_option (self, name, str->str, -1, secret); \ + else \ + success = TRUE; \ + g_string_free (str, TRUE); \ + if (!success) { \ + nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name); \ + return FALSE; \ + } \ + } + +static char * +get_blob_id (const char *name, const char *seed_uid) +{ + char *uid = g_strdup_printf ("%s-%s", seed_uid, name); + char *p = uid; + while (*p) { + if (*p == '/') *p = '-'; + p++; + } + return uid; +} + +#define ADD_BLOB_VAL(field, name, con_uid) \ + if (field && field->len) { \ + char *uid = get_blob_id (name, con_uid); \ + success = nm_supplicant_config_add_blob (self, name, field, uid); \ + g_free (uid); \ + if (!success) { \ + nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name); \ + return FALSE; \ + } \ + } + + +static gboolean +wep128_passphrase_hash (const char *input, + size_t input_len, + guint8 *out_digest, + size_t *out_digest_len) +{ + GChecksum *sum; + guint8 data[64]; + int i; + + g_return_val_if_fail (out_digest != NULL, FALSE); + g_return_val_if_fail (out_digest_len != NULL, FALSE); + g_return_val_if_fail (*out_digest_len >= 16, FALSE); + + /* Get at least 64 bytes by repeating the passphrase into the buffer */ + for (i = 0; i < sizeof (data); i++) + data[i] = input[i % input_len]; + + sum = g_checksum_new (G_CHECKSUM_MD5); + g_assert (sum); + g_checksum_update (sum, data, sizeof (data)); + g_checksum_get_digest (sum, out_digest, out_digest_len); + g_checksum_free (sum); + + g_assert (*out_digest_len == 16); + /* WEP104 keys are 13 bytes in length (26 hex characters) */ + *out_digest_len = 13; + return TRUE; +} + +static gboolean +add_wep_key (NMSupplicantConfig *self, + const char *key, + const char *name, + NMWepKeyType wep_type) +{ + char *value; + gboolean success; + size_t key_len = key ? strlen (key) : 0; + + if (!key || !key_len) + return TRUE; + + if ( (wep_type == NM_WEP_KEY_TYPE_UNKNOWN) + || (wep_type == NM_WEP_KEY_TYPE_KEY)) { + if ((key_len == 10) || (key_len == 26)) { + value = hexstr2bin (key, strlen (key)); + success = nm_supplicant_config_add_option (self, name, value, key_len / 2, TRUE); + g_free (value); + if (!success) { + nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name); + return FALSE; + } + } else if ((key_len == 5) || (key_len == 13)) { + if (!nm_supplicant_config_add_option (self, name, key, key_len, TRUE)) { + nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name); + return FALSE; + } + } else { + nm_log_warn (LOGD_SUPPLICANT, "Invalid WEP key '%s'", name); + return FALSE; + } + } else if (wep_type == NM_WEP_KEY_TYPE_PASSPHRASE) { + guint8 digest[16]; + size_t digest_len = sizeof (digest); + + success = wep128_passphrase_hash (key, key_len, digest, &digest_len); + if (success) + success = nm_supplicant_config_add_option (self, name, (const char *) digest, digest_len, TRUE); + if (!success) { + nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name); + return FALSE; + } + } + + return TRUE; +} + +gboolean +nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, + NMSettingWirelessSecurity *setting, + NMSetting8021x *setting_8021x, + const char *connection_uid) +{ + char *value; + gboolean success; + const char *key_mgmt, *auth_alg; + const char *psk; + + g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE); + g_return_val_if_fail (setting != NULL, FALSE); + g_return_val_if_fail (connection_uid != NULL, FALSE); + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (setting); + if (!add_string_val (self, key_mgmt, "key_mgmt", TRUE, FALSE)) + return FALSE; + + auth_alg = nm_setting_wireless_security_get_auth_alg (setting); + if (!add_string_val (self, auth_alg, "auth_alg", TRUE, FALSE)) + return FALSE; + + psk = nm_setting_wireless_security_get_psk (setting); + if (psk) { + size_t psk_len = strlen (psk); + + if (psk_len == 64) { + /* Hex PSK */ + value = hexstr2bin (psk, psk_len); + success = nm_supplicant_config_add_option (self, "psk", value, psk_len / 2, TRUE); + g_free (value); + if (!success) { + nm_log_warn (LOGD_SUPPLICANT, "Error adding 'psk' to supplicant config."); + return FALSE; + } + } else if (psk_len >= 8 && psk_len <= 63) { + /* Use TYPE_STRING here so that it gets pushed to the + * supplicant as a string, and therefore gets quoted, + * and therefore the supplicant will interpret it as a + * passphrase and not a hex key. + */ + if (!nm_supplicant_config_add_option_with_type (self, "psk", psk, -1, TYPE_STRING, TRUE)) { + nm_log_warn (LOGD_SUPPLICANT, "Error adding 'psk' to supplicant config."); + return FALSE; + } + } else { + /* Invalid PSK */ + nm_log_warn (LOGD_SUPPLICANT, "Invalid PSK length %u: not between 8 and 63 characters inclusive.", (guint32) psk_len); + return FALSE; + } + } + + /* Only WPA-specific things when using WPA */ + if ( !strcmp (key_mgmt, "wpa-none") + || !strcmp (key_mgmt, "wpa-psk") + || !strcmp (key_mgmt, "wpa-eap")) { + ADD_STRING_LIST_VAL (setting, wireless_security, proto, protos, "proto", TRUE, FALSE); + ADD_STRING_LIST_VAL (setting, wireless_security, pairwise, pairwise, "pairwise", TRUE, FALSE); + ADD_STRING_LIST_VAL (setting, wireless_security, group, groups, "group", TRUE, FALSE); + } + + /* WEP keys if required */ + if (!strcmp (key_mgmt, "none")) { + NMWepKeyType wep_type = nm_setting_wireless_security_get_wep_key_type (setting); + const char *wep0 = nm_setting_wireless_security_get_wep_key (setting, 0); + const char *wep1 = nm_setting_wireless_security_get_wep_key (setting, 1); + const char *wep2 = nm_setting_wireless_security_get_wep_key (setting, 2); + const char *wep3 = nm_setting_wireless_security_get_wep_key (setting, 3); + + if (!add_wep_key (self, wep0, "wep_key0", wep_type)) + return FALSE; + if (!add_wep_key (self, wep1, "wep_key1", wep_type)) + return FALSE; + if (!add_wep_key (self, wep2, "wep_key2", wep_type)) + return FALSE; + if (!add_wep_key (self, wep3, "wep_key3", wep_type)) + return FALSE; + + if (wep0 || wep1 || wep2 || wep3) { + value = g_strdup_printf ("%d", nm_setting_wireless_security_get_wep_tx_keyidx (setting)); + success = nm_supplicant_config_add_option (self, "wep_tx_keyidx", value, -1, FALSE); + g_free (value); + if (!success) { + nm_log_warn (LOGD_SUPPLICANT, "Error adding wep_tx_keyidx to supplicant config."); + return FALSE; + } + } + } + + if (auth_alg && !strcmp (auth_alg, "leap")) { + /* LEAP */ + if (!strcmp (key_mgmt, "ieee8021x")) { + const char *tmp; + + tmp = nm_setting_wireless_security_get_leap_username (setting); + if (!add_string_val (self, tmp, "identity", FALSE, FALSE)) + return FALSE; + + tmp = nm_setting_wireless_security_get_leap_password (setting); + if (!add_string_val (self, tmp, "password", FALSE, TRUE)) + return FALSE; + + if (!add_string_val (self, "leap", "eap", TRUE, FALSE)) + return FALSE; + } else { + return FALSE; + } + } else { + /* 802.1x for Dynamic WEP and WPA-Enterprise */ + if (!strcmp (key_mgmt, "ieee8021x") || !strcmp (key_mgmt, "wpa-eap")) { + if (!setting_8021x) + return FALSE; + if (!nm_supplicant_config_add_setting_8021x (self, setting_8021x, connection_uid, FALSE)) + return FALSE; + } + } + + return TRUE; +} + +gboolean +nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self, + NMSetting8021x *setting, + const char *connection_uid, + gboolean wired) +{ + char *tmp; + const char *peapver, *value, *path; + gboolean success, added; + GString *phase1, *phase2; + const GByteArray *array; + + g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE); + g_return_val_if_fail (setting != NULL, FALSE); + g_return_val_if_fail (connection_uid != NULL, FALSE); + + value = nm_setting_802_1x_get_password (setting); + if (!add_string_val (self, value, "password", FALSE, TRUE)) + return FALSE; + value = nm_setting_802_1x_get_pin (setting); + if (!add_string_val (self, value, "pin", FALSE, TRUE)) + return FALSE; + + if (wired) { + if (!add_string_val (self, "IEEE8021X", "key_mgmt", FALSE, FALSE)) + return FALSE; + /* Wired 802.1x must always use eapol_flags=0 */ + if (!add_string_val (self, "0", "eapol_flags", FALSE, FALSE)) + return FALSE; + } + + ADD_STRING_LIST_VAL (setting, 802_1x, eap_method, eap_methods, "eap", TRUE, FALSE); + + /* Drop the fragment size a bit for better compatibility */ + if (!nm_supplicant_config_add_option (self, "fragment_size", "1300", -1, FALSE)) + return FALSE; + + phase1 = g_string_new (NULL); + peapver = nm_setting_802_1x_get_phase1_peapver (setting); + if (peapver) { + if (!strcmp (peapver, "0")) + g_string_append (phase1, "peapver=0"); + else if (!strcmp (peapver, "1")) + g_string_append (phase1, "peapver=1"); + } + + if (nm_setting_802_1x_get_phase1_peaplabel (setting)) { + if (phase1->len) + g_string_append_c (phase1, ' '); + g_string_append_printf (phase1, "peaplabel=%s", nm_setting_802_1x_get_phase1_peaplabel (setting)); + } + + if (phase1->len) { + if (!add_string_val (self, phase1->str, "phase1", FALSE, FALSE)) { + g_string_free (phase1, TRUE); + return FALSE; + } + } + g_string_free (phase1, TRUE); + + phase2 = g_string_new (NULL); + if (nm_setting_802_1x_get_phase2_auth (setting)) { + tmp = g_ascii_strup (nm_setting_802_1x_get_phase2_auth (setting), -1); + g_string_append_printf (phase2, "auth=%s", tmp); + g_free (tmp); + } + + if (nm_setting_802_1x_get_phase2_autheap (setting)) { + if (phase2->len) + g_string_append_c (phase2, ' '); + tmp = g_ascii_strup (nm_setting_802_1x_get_phase2_autheap (setting), -1); + g_string_append_printf (phase2, "autheap=%s", tmp); + g_free (tmp); + } + + if (phase2->len) { + if (!add_string_val (self, phase2->str, "phase2", FALSE, FALSE)) { + g_string_free (phase2, TRUE); + return FALSE; + } + } + g_string_free (phase2, TRUE); + + /* CA path */ + path = nm_setting_802_1x_get_ca_path (setting); + if (nm_setting_802_1x_get_system_ca_certs (setting)) + path = SYSTEM_CA_PATH; + if (path) { + if (!add_string_val (self, path, "ca_path", FALSE, FALSE)) + return FALSE; + } + + /* Phase2 CA path */ + path = nm_setting_802_1x_get_phase2_ca_path (setting); + if (nm_setting_802_1x_get_system_ca_certs (setting)) + path = SYSTEM_CA_PATH; + if (path) { + if (!add_string_val (self, path, "ca_path2", FALSE, FALSE)) + return FALSE; + } + + /* CA certificate */ + switch (nm_setting_802_1x_get_ca_cert_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + array = nm_setting_802_1x_get_ca_cert_blob (setting); + ADD_BLOB_VAL (array, "ca_cert", connection_uid); + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_ca_cert_path (setting); + if (!add_string_val (self, path, "ca_cert", FALSE, FALSE)) + return FALSE; + break; + default: + break; + } + + /* Phase 2 CA certificate */ + switch (nm_setting_802_1x_get_phase2_ca_cert_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + array = nm_setting_802_1x_get_phase2_ca_cert_blob (setting); + ADD_BLOB_VAL (array, "ca_cert2", connection_uid); + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_phase2_ca_cert_path (setting); + if (!add_string_val (self, path, "ca_cert2", FALSE, FALSE)) + return FALSE; + break; + default: + break; + } + + /* Private key */ + added = FALSE; + switch (nm_setting_802_1x_get_private_key_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + array = nm_setting_802_1x_get_private_key_blob (setting); + ADD_BLOB_VAL (array, "private_key", connection_uid); + added = TRUE; + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_private_key_path (setting); + if (!add_string_val (self, path, "private_key", FALSE, FALSE)) + return FALSE; + added = TRUE; + break; + default: + break; + } + + if (added) { + NMSetting8021xCKFormat format; + NMSetting8021xCKScheme scheme; + + format = nm_setting_802_1x_get_private_key_format (setting); + scheme = nm_setting_802_1x_get_private_key_scheme (setting); + + if ( scheme == NM_SETTING_802_1X_CK_SCHEME_PATH + || format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { + /* Only add the private key password for PKCS#12 blobs and + * all path schemes, since in both of these cases the private key + * isn't decrypted at all. + */ + value = nm_setting_802_1x_get_private_key_password (setting); + if (!add_string_val (self, value, "private_key_passwd", FALSE, TRUE)) + return FALSE; + } + + if (format != NM_SETTING_802_1X_CK_FORMAT_PKCS12) { + /* Only add the client cert if the private key is not PKCS#12, as + * wpa_supplicant configuration directs us to do. + */ + switch (nm_setting_802_1x_get_client_cert_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + array = nm_setting_802_1x_get_client_cert_blob (setting); + ADD_BLOB_VAL (array, "client_cert", connection_uid); + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_client_cert_path (setting); + if (!add_string_val (self, path, "client_cert", FALSE, FALSE)) + return FALSE; + break; + default: + break; + } + } + } + + /* Phase 2 private key */ + added = FALSE; + switch (nm_setting_802_1x_get_phase2_private_key_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + array = nm_setting_802_1x_get_phase2_private_key_blob (setting); + ADD_BLOB_VAL (array, "private_key2", connection_uid); + added = TRUE; + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_phase2_private_key_path (setting); + if (!add_string_val (self, path, "private_key2", FALSE, FALSE)) + return FALSE; + added = TRUE; + break; + default: + break; + } + + if (added) { + NMSetting8021xCKFormat format; + NMSetting8021xCKScheme scheme; + + format = nm_setting_802_1x_get_phase2_private_key_format (setting); + scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting); + + if ( scheme == NM_SETTING_802_1X_CK_SCHEME_PATH + || format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { + /* Only add the private key password for PKCS#12 blobs and + * all path schemes, since in both of these cases the private key + * isn't decrypted at all. + */ + value = nm_setting_802_1x_get_phase2_private_key_password (setting); + if (!add_string_val (self, value, "private_key_passwd2", FALSE, TRUE)) + return FALSE; + } + + if (format != NM_SETTING_802_1X_CK_FORMAT_PKCS12) { + /* Only add the client cert if the private key is not PKCS#12, as + * wpa_supplicant configuration directs us to do. + */ + switch (nm_setting_802_1x_get_phase2_client_cert_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + array = nm_setting_802_1x_get_phase2_client_cert_blob (setting); + ADD_BLOB_VAL (array, "client_cert2", connection_uid); + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_phase2_client_cert_path (setting); + if (!add_string_val (self, path, "client_cert2", FALSE, FALSE)) + return FALSE; + break; + default: + break; + } + } + } + + value = nm_setting_802_1x_get_identity (setting); + if (!add_string_val (self, value, "identity", FALSE, FALSE)) + return FALSE; + value = nm_setting_802_1x_get_anonymous_identity (setting); + if (!add_string_val (self, value, "anonymous_identity", FALSE, FALSE)) + return FALSE; + + return TRUE; +} + +gboolean +nm_supplicant_config_add_no_security (NMSupplicantConfig *self) +{ + return nm_supplicant_config_add_option (self, "key_mgmt", "NONE", -1, FALSE); +} + +/* From hostap, Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> */ + +static int hex2num (char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + +static int hex2byte (const char *hex) +{ + int a, b; + a = hex2num(*hex++); + if (a < 0) + return -1; + b = hex2num(*hex++); + if (b < 0) + return -1; + return (a << 4) | b; +} + +static char * +hexstr2bin (const char *hex, size_t len) +{ + size_t i; + int a; + const char * ipos = hex; + char * buf = NULL; + char * opos; + + /* Length must be a multiple of 2 */ + if ((len % 2) != 0) + return NULL; + + opos = buf = g_malloc0 ((len / 2) + 1); + for (i = 0; i < len; i += 2) { + a = hex2byte (ipos); + if (a < 0) { + g_free (buf); + return NULL; + } + *opos++ = a; + ipos += 2; + } + return buf; +} + +/* End from hostap */ + diff --git a/src/supplicant-manager/nm-supplicant-config.h b/src/supplicant-manager/nm-supplicant-config.h new file mode 100644 index 00000000..dad23e29 --- /dev/null +++ b/src/supplicant-manager/nm-supplicant-config.h @@ -0,0 +1,84 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2008 Red Hat, Inc. + * Copyright (C) 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_SUPPLICANT_CONFIG_H +#define NM_SUPPLICANT_CONFIG_H + +#include <glib-object.h> +#include <nm-setting-wireless.h> +#include <nm-setting-wireless-security.h> +#include <nm-setting-8021x.h> +#include "nm-supplicant-types.h" + +G_BEGIN_DECLS + +#define NM_TYPE_SUPPLICANT_CONFIG (nm_supplicant_config_get_type ()) +#define NM_SUPPLICANT_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SUPPLICANT_CONFIG, NMSupplicantConfig)) +#define NM_SUPPLICANT_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SUPPLICANT_CONFIG, NMSupplicantConfigClass)) +#define NM_IS_SUPPLICANT_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SUPPLICANT_CONFIG)) +#define NM_IS_SUPPLICANT_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SUPPLICANT_CONFIG)) +#define NM_SUPPLICANT_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SUPPLICANT_CONFIG, NMSupplicantConfigClass)) + +struct _NMSupplicantConfig +{ + GObject parent; +}; + +typedef struct +{ + GObjectClass parent; +} NMSupplicantConfigClass; + + +GType nm_supplicant_config_get_type (void); + +NMSupplicantConfig *nm_supplicant_config_new (void); + +guint32 nm_supplicant_config_get_ap_scan (NMSupplicantConfig *self); + +void nm_supplicant_config_set_ap_scan (NMSupplicantConfig *self, + guint32 ap_scan); + +GHashTable *nm_supplicant_config_get_hash (NMSupplicantConfig *self); + +GHashTable *nm_supplicant_config_get_blobs (NMSupplicantConfig *self); + +gboolean nm_supplicant_config_add_setting_wireless (NMSupplicantConfig *self, + NMSettingWireless *setting, + gboolean is_broadcast, + guint32 adhoc_freq, + gboolean has_scan_capa_ssid); + +gboolean nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, + NMSettingWirelessSecurity *setting, + NMSetting8021x *setting_8021x, + const char *connection_uid); + +gboolean nm_supplicant_config_add_no_security (NMSupplicantConfig *self); + +gboolean nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self, + NMSetting8021x *setting, + const char *connection_uid, + gboolean wired); + +G_END_DECLS + +#endif /* NM_SUPPLICANT_CONFIG_H */ diff --git a/src/supplicant-manager/nm-supplicant-interface.c b/src/supplicant-manager/nm-supplicant-interface.c new file mode 100644 index 00000000..a65a458f --- /dev/null +++ b/src/supplicant-manager/nm-supplicant-interface.c @@ -0,0 +1,1429 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2008 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include <stdio.h> +#include <string.h> +#include <glib.h> + +#include "nm-supplicant-interface.h" +#include "nm-supplicant-manager.h" +#include "nm-logging.h" +#include "nm-marshal.h" +#include "nm-supplicant-config.h" +#include "nm-dbus-manager.h" +#include "nm-call-store.h" +#include "nm-dbus-glib-types.h" +#include "nm-glib-compat.h" + +#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface" +#define WPAS_DBUS_IFACE_BSSID WPAS_DBUS_INTERFACE ".BSSID" +#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network" +#define WPAS_ERROR_INVALID_IFACE WPAS_DBUS_INTERFACE ".InvalidInterface" +#define WPAS_ERROR_EXISTS_ERROR WPAS_DBUS_INTERFACE ".ExistsError" + + +G_DEFINE_TYPE (NMSupplicantInterface, nm_supplicant_interface, G_TYPE_OBJECT) + + +#define NM_SUPPLICANT_INTERFACE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + NM_TYPE_SUPPLICANT_INTERFACE, \ + NMSupplicantInterfacePrivate)) + +static void nm_supplicant_interface_set_property (GObject * object, + guint prop_id, + const GValue * value, + GParamSpec * pspec); + +static void nm_supplicant_interface_get_property (GObject * object, + guint prop_id, + GValue * value, + GParamSpec * pspec); + +static void nm_supplicant_interface_start (NMSupplicantInterface *self); + +static void nm_supplicant_interface_add_to_supplicant (NMSupplicantInterface *self, + gboolean get_only); + +static void nm_supplicant_interface_smgr_state_changed (NMSupplicantManager *smgr, + guint32 new_state, + guint32 old_state, + gpointer user_data); + +static void nm_supplicant_interface_set_state (NMSupplicantInterface *self, + guint32 new_state); + + +/* Signals */ +enum { + STATE, /* change in the interface's state */ + REMOVED, /* interface was removed by the supplicant */ + SCANNED_AP, /* interface saw a new access point from a scan */ + SCAN_REQ_RESULT, /* result of a wireless scan request */ + SCAN_RESULTS, /* scan results returned from supplicant */ + CONNECTION_STATE, /* link state of the device's connection */ + CONNECTION_ERROR, /* an error occurred during a connection request */ + LAST_SIGNAL +}; +static guint nm_supplicant_interface_signals[LAST_SIGNAL] = { 0 }; + + +/* Properties */ +enum { + PROP_0 = 0, + PROP_SUPPLICANT_MANAGER, + PROP_DEVICE, + PROP_STATE, + PROP_CONNECTION_STATE, + PROP_SCANNING, + LAST_PROP +}; + + +typedef struct +{ + NMSupplicantManager * smgr; + gulong smgr_state_sig_handler; + NMDBusManager * dbus_mgr; + char * dev; + gboolean is_wireless; + + char * object_path; + guint32 state; + NMCallStore * assoc_pcalls; + NMCallStore * other_pcalls; + + guint32 con_state; + gboolean scanning; + + DBusGProxy * iface_proxy; + DBusGProxy * net_proxy; + + guint scan_results_timeout; + guint32 last_scan; + + NMSupplicantConfig * cfg; + + gboolean dispose_has_run; +} NMSupplicantInterfacePrivate; + +static gboolean +cancel_all_cb (GObject *object, gpointer call_id, gpointer user_data) +{ + dbus_g_proxy_cancel_call (DBUS_G_PROXY (object), (DBusGProxyCall *) call_id); + + return TRUE; +} + +static void +cancel_all_callbacks (NMCallStore *store) +{ + nm_call_store_foreach (store, NULL, cancel_all_cb, NULL); + nm_call_store_clear (store); +} + +typedef struct { + NMSupplicantInterface *interface; + DBusGProxy *proxy; + NMCallStore *store; + DBusGProxyCall *call; + gboolean disposing; +} NMSupplicantInfo; + +static NMSupplicantInfo * +nm_supplicant_info_new (NMSupplicantInterface *interface, + DBusGProxy *proxy, + NMCallStore *store) +{ + NMSupplicantInfo *info; + + info = g_slice_new0 (NMSupplicantInfo); + info->interface = g_object_ref (interface); + info->proxy = g_object_ref (proxy); + info->store = store; + + return info; +} + +static void +nm_supplicant_info_set_call (NMSupplicantInfo *info, DBusGProxyCall *call) +{ + g_return_if_fail (info != NULL); + g_return_if_fail (call != NULL); + + nm_call_store_add (info->store, G_OBJECT (info->proxy), (gpointer) call); + info->call = call; +} + +static void +nm_supplicant_info_destroy (gpointer user_data) +{ + NMSupplicantInfo *info = (NMSupplicantInfo *) user_data; + + /* Guard against double-disposal; since DBusGProxy doesn't guard against + * double-disposal, we could infinite loop here if we're in the middle of + * some wpa_supplicant D-Bus calls. When the supplicant dies we'll dispose + * of the proxy, which kills all its pending calls, which brings us here. + * Then when we unref the proxy here, its dispose() function will get called + * again, and we get right back here until we segfault because our callstack + * is too long. + */ + if (!info->disposing) { + info->disposing = TRUE; + + if (info->call) { + nm_call_store_remove (info->store, G_OBJECT (info->proxy), info->call); + info->call = NULL; + } + + g_object_unref (info->proxy); + info->proxy = NULL; + g_object_unref (info->interface); + info->interface = NULL; + + memset (info, 0, sizeof (NMSupplicantInfo)); + g_slice_free (NMSupplicantInfo, info); + } +} + + +NMSupplicantInterface * +nm_supplicant_interface_new (NMSupplicantManager * smgr, const char *ifname, gboolean is_wireless) +{ + NMSupplicantInterface * iface; + + g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (smgr), NULL); + g_return_val_if_fail (ifname != NULL, NULL); + + iface = g_object_new (NM_TYPE_SUPPLICANT_INTERFACE, + "supplicant-manager", smgr, + "device", ifname, + NULL); + if (iface) { + NM_SUPPLICANT_INTERFACE_GET_PRIVATE (iface)->is_wireless = is_wireless; + nm_supplicant_interface_start (iface); + } + + return iface; +} + +static void +nm_supplicant_interface_init (NMSupplicantInterface * self) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + priv->state = NM_SUPPLICANT_INTERFACE_STATE_INIT; + priv->con_state = NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED; + priv->assoc_pcalls = nm_call_store_new (); + priv->other_pcalls = nm_call_store_new (); + + priv->dispose_has_run = FALSE; + + priv->dbus_mgr = nm_dbus_manager_get (); +} + + +static void +nm_supplicant_interface_set_property (GObject * object, + guint prop_id, + const GValue * value, + GParamSpec * pspec) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object); + gulong id; + + switch (prop_id) { + case PROP_SUPPLICANT_MANAGER: + priv->smgr = NM_SUPPLICANT_MANAGER (g_value_get_object (value)); + g_object_ref (G_OBJECT (priv->smgr)); + + id = g_signal_connect (priv->smgr, + "state", + G_CALLBACK (nm_supplicant_interface_smgr_state_changed), + object); + priv->smgr_state_sig_handler = id; + break; + case PROP_DEVICE: + /* Construct-only */ + priv->dev = g_strdup (g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_supplicant_interface_get_property (GObject * object, + guint prop_id, + GValue * value, + GParamSpec * pspec) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_SUPPLICANT_MANAGER: + g_value_set_object (value, G_OBJECT (priv->smgr)); + break; + case PROP_DEVICE: + g_value_set_string (value, priv->dev); + break; + case PROP_STATE: + g_value_set_uint (value, priv->state); + break; + case PROP_CONNECTION_STATE: + g_value_set_uint (value, priv->con_state); + break; + case PROP_SCANNING: + g_value_set_boolean (value, priv->scanning); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +try_remove_iface (DBusGConnection *g_connection, + const char *path) +{ + DBusGProxy *proxy; + + g_return_if_fail (g_connection != NULL); + g_return_if_fail (path != NULL); + + proxy = dbus_g_proxy_new_for_name (g_connection, + WPAS_DBUS_SERVICE, + WPAS_DBUS_PATH, + WPAS_DBUS_INTERFACE); + if (!proxy) + return; + + dbus_g_proxy_call_no_reply (proxy, "removeInterface", + DBUS_TYPE_G_OBJECT_PATH, path, + G_TYPE_INVALID); + g_object_unref (proxy); +} + +static void +nm_supplicant_interface_dispose (GObject *object) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object); + guint32 sm_state; + + if (priv->dispose_has_run) { + G_OBJECT_CLASS (nm_supplicant_interface_parent_class)->dispose (object); + return; + } + + priv->dispose_has_run = TRUE; + + /* Ask wpa_supplicant to remove this interface */ + sm_state = nm_supplicant_manager_get_state (priv->smgr); + if (sm_state == NM_SUPPLICANT_MANAGER_STATE_IDLE) { + if (priv->object_path) { + try_remove_iface (nm_dbus_manager_get_connection (priv->dbus_mgr), + priv->object_path); + } + } + + if (priv->iface_proxy) + g_object_unref (priv->iface_proxy); + + if (priv->net_proxy) + g_object_unref (priv->net_proxy); + + if (priv->scan_results_timeout) + g_source_remove (priv->scan_results_timeout); + + if (priv->smgr) { + g_signal_handler_disconnect (priv->smgr, + priv->smgr_state_sig_handler); + g_object_unref (priv->smgr); + } + + g_free (priv->dev); + + /* Cancel pending calls before unrefing the dbus manager */ + cancel_all_callbacks (priv->other_pcalls); + nm_call_store_destroy (priv->other_pcalls); + + cancel_all_callbacks (priv->assoc_pcalls); + nm_call_store_destroy (priv->assoc_pcalls); + + if (priv->dbus_mgr) + g_object_unref (priv->dbus_mgr); + + if (priv->cfg) + g_object_unref (priv->cfg); + + g_free (priv->object_path); + + /* Chain up to the parent class */ + G_OBJECT_CLASS (nm_supplicant_interface_parent_class)->dispose (object); +} + +static void +nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMSupplicantInterfacePrivate)); + + object_class->dispose = nm_supplicant_interface_dispose; + object_class->set_property = nm_supplicant_interface_set_property; + object_class->get_property = nm_supplicant_interface_get_property; + + /* Properties */ + g_object_class_install_property (object_class, + PROP_SUPPLICANT_MANAGER, + g_param_spec_object ("supplicant-manager", + "Supplicant Manager", + "Supplicant manager to which this interface belongs", + NM_TYPE_SUPPLICANT_MANAGER, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, + PROP_DEVICE, + g_param_spec_string ("device", + "Device", + "Device which this interface represents to the supplicant", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, + PROP_STATE, + g_param_spec_uint ("state", + "State", + "State of the supplicant interface; INIT, READY, or DOWN", + NM_SUPPLICANT_INTERFACE_STATE_INIT, + NM_SUPPLICANT_INTERFACE_STATE_LAST - 1, + NM_SUPPLICANT_INTERFACE_STATE_INIT, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, + PROP_SCANNING, + g_param_spec_boolean ("scanning", + "Scanning", + "Scanning", + FALSE, + G_PARAM_READABLE)); + + /* Signals */ + nm_supplicant_interface_signals[STATE] = + g_signal_new ("state", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMSupplicantInterfaceClass, state), + NULL, NULL, + _nm_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); + + nm_supplicant_interface_signals[REMOVED] = + g_signal_new ("removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMSupplicantInterfaceClass, removed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + nm_supplicant_interface_signals[SCANNED_AP] = + g_signal_new ("scanned-ap", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMSupplicantInterfaceClass, scanned_ap), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + nm_supplicant_interface_signals[SCAN_REQ_RESULT] = + g_signal_new ("scan-req-result", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMSupplicantInterfaceClass, scan_req_result), + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); + + nm_supplicant_interface_signals[SCAN_RESULTS] = + g_signal_new ("scan-results", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMSupplicantInterfaceClass, scan_results), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + + nm_supplicant_interface_signals[CONNECTION_STATE] = + g_signal_new ("connection-state", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMSupplicantInterfaceClass, connection_state), + NULL, NULL, + _nm_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); + + nm_supplicant_interface_signals[CONNECTION_ERROR] = + g_signal_new ("connection-error", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMSupplicantInterfaceClass, connection_error), + NULL, NULL, + _nm_marshal_VOID__STRING_STRING, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); +} + +static void +emit_error_helper (NMSupplicantInterface *self, + GError *err) +{ + const char *name = NULL; + + if (err->domain == DBUS_GERROR && err->code == DBUS_GERROR_REMOTE_EXCEPTION) + name = dbus_g_error_get_name (err); + + g_signal_emit (self, + nm_supplicant_interface_signals[CONNECTION_ERROR], + 0, + name, + err->message); +} + +static void +bssid_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMSupplicantInfo *info = (NMSupplicantInfo *) user_data; + GError *err = NULL; + GHashTable *hash = NULL; + + if (!dbus_g_proxy_end_call (proxy, call_id, &err, + DBUS_TYPE_G_MAP_OF_VARIANT, &hash, + G_TYPE_INVALID)) { + if (!strstr (err->message, "The BSSID requested was invalid")) { + nm_log_warn (LOGD_SUPPLICANT, "Couldn't retrieve BSSID properties: %s.", + err->message); + } + g_error_free (err); + } else { + g_signal_emit (info->interface, + nm_supplicant_interface_signals[SCANNED_AP], + 0, + hash); + + g_hash_table_destroy (hash); + } +} + +static void +request_bssid_properties (NMSupplicantInterface * self, + const char * op) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + NMSupplicantInfo *info; + DBusGProxy *proxy; + DBusGProxyCall *call; + + proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr), + WPAS_DBUS_SERVICE, + op, + WPAS_DBUS_IFACE_BSSID); + info = nm_supplicant_info_new (self, proxy, priv->other_pcalls); + call = dbus_g_proxy_begin_call (proxy, "properties", + bssid_properties_cb, + info, + nm_supplicant_info_destroy, + G_TYPE_INVALID); + nm_supplicant_info_set_call (info, call); + g_object_unref (proxy); +} + +static void +scan_results_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + GError *err = NULL; + GPtrArray *array = NULL; + + if (!dbus_g_proxy_end_call (proxy, call_id, &err, + DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, &array, + G_TYPE_INVALID)) { + nm_log_warn (LOGD_SUPPLICANT, "could not get scan results: %s.", err->message); + g_error_free (err); + } else { + int i; + NMSupplicantInfo *info = (NMSupplicantInfo *) user_data; + + /* Notify listeners of the result of the scan */ + g_signal_emit (info->interface, + nm_supplicant_interface_signals[SCAN_RESULTS], + 0, + array->len); + + /* Fire off a "properties" call for each returned BSSID */ + for (i = 0; i < array->len; i++) { + char *op = g_ptr_array_index (array, i); + + request_bssid_properties (info->interface, op); + g_free (op); + } + + g_ptr_array_free (array, TRUE); + } +} + +static gboolean +request_scan_results (gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + NMSupplicantInfo *info; + DBusGProxyCall *call; + GTimeVal cur_time; + + priv->scan_results_timeout = 0; + + g_return_val_if_fail (priv->iface_proxy != NULL, FALSE); + + info = nm_supplicant_info_new (self, priv->iface_proxy, priv->other_pcalls); + call = dbus_g_proxy_begin_call (priv->iface_proxy, "scanResults", + scan_results_cb, + info, + nm_supplicant_info_destroy, + G_TYPE_INVALID); + nm_supplicant_info_set_call (info, call); + + g_get_current_time (&cur_time); + priv->last_scan = cur_time.tv_sec; + return FALSE; +} + +static void +wpas_iface_query_scan_results (DBusGProxy *proxy, gpointer user_data) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (user_data); + GTimeVal cur_time; + + /* Only query scan results if a query is not queued */ + if (priv->scan_results_timeout) + return; + + g_get_current_time (&cur_time); + + /* Only fetch scan results every 4s max, but initially do it right away */ + if (priv->last_scan + 4 < cur_time.tv_sec) { + priv->scan_results_timeout = g_idle_add (request_scan_results, + user_data); + } else { + priv->scan_results_timeout = + g_timeout_add_seconds ((4 - (cur_time.tv_sec - priv->last_scan)), + request_scan_results, user_data); + } +} + +static guint32 +wpas_state_string_to_enum (const char * str_state) +{ + guint32 enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED; + + if (!strcmp (str_state, "DISCONNECTED")) + enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED; + else if (!strcmp (str_state, "INACTIVE")) + enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_INACTIVE; + else if (!strcmp (str_state, "SCANNING")) + enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_SCANNING; + else if (!strcmp (str_state, "ASSOCIATING")) + enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATING; + else if (!strcmp (str_state, "ASSOCIATED")) + enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATED; + else if (!strcmp (str_state, "4WAY_HANDSHAKE")) + enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_4WAY_HANDSHAKE; + else if (!strcmp (str_state, "GROUP_HANDSHAKE")) + enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_GROUP_HANDSHAKE; + else if (!strcmp (str_state, "COMPLETED")) + enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED; + + return enum_state; +} + +static void +wpas_iface_handle_state_change (DBusGProxy *proxy, + const char *str_new_state, + const char *str_old_state, + gpointer user_data) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (user_data); + guint32 old_state, enum_new_state; + + enum_new_state = wpas_state_string_to_enum (str_new_state); + old_state = priv->con_state; + priv->con_state = enum_new_state; + if (priv->con_state != old_state) { + g_signal_emit (user_data, + nm_supplicant_interface_signals[CONNECTION_STATE], + 0, + priv->con_state, + old_state); + } +} + + +static void +iface_state_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + GError *err = NULL; + char *state_str = NULL; + + if (!dbus_g_proxy_end_call (proxy, call_id, &err, + G_TYPE_STRING, &state_str, + G_TYPE_INVALID)) { + nm_log_warn (LOGD_SUPPLICANT, "could not get interface state: %s.", err->message); + g_error_free (err); + } else { + NMSupplicantInfo *info = (NMSupplicantInfo *) user_data; + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface); + + priv->con_state = wpas_state_string_to_enum (state_str); + g_free (state_str); + nm_supplicant_interface_set_state (info->interface, NM_SUPPLICANT_INTERFACE_STATE_READY); + } +} + +static void +wpas_iface_get_state (NMSupplicantInterface *self) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + NMSupplicantInfo *info; + DBusGProxyCall *call; + + info = nm_supplicant_info_new (self, priv->iface_proxy, priv->other_pcalls); + call = dbus_g_proxy_begin_call (priv->iface_proxy, "state", + iface_state_cb, + info, + nm_supplicant_info_destroy, + G_TYPE_INVALID); + nm_supplicant_info_set_call (info, call); +} + +static void +iface_scanning_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMSupplicantInfo *info = (NMSupplicantInfo *) user_data; + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface); + gboolean scanning = FALSE; + + if (dbus_g_proxy_end_call (proxy, call_id, NULL, + G_TYPE_BOOLEAN, &scanning, + G_TYPE_INVALID)) { + if (scanning != priv->scanning) { + priv->scanning = scanning; + g_object_notify (G_OBJECT (info->interface), "scanning"); + } + } +} + +static void +wpas_iface_get_scanning (NMSupplicantInterface *self) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + NMSupplicantInfo *info; + DBusGProxyCall *call; + + info = nm_supplicant_info_new (self, priv->iface_proxy, priv->other_pcalls); + call = dbus_g_proxy_begin_call (priv->iface_proxy, "scanning", + iface_scanning_cb, + info, + nm_supplicant_info_destroy, + G_TYPE_INVALID); + nm_supplicant_info_set_call (info, call); +} + +static void +wpas_iface_handle_scanning (DBusGProxy *proxy, + gboolean scanning, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (scanning != priv->scanning) { + priv->scanning = scanning; + g_object_notify (G_OBJECT (self), "scanning"); + } +} + +gboolean +nm_supplicant_interface_get_scanning (NMSupplicantInterface *self) +{ + NMSupplicantInterfacePrivate *priv; + + g_return_val_if_fail (self != NULL, FALSE); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + if (priv->scanning) + return TRUE; + if (priv->con_state == NM_SUPPLICANT_INTERFACE_CON_STATE_SCANNING) + return TRUE; + return FALSE; +} + +static void +nm_supplicant_interface_add_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMSupplicantInfo *info = (NMSupplicantInfo *) user_data; + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface); + GError *err = NULL; + char *path = NULL; + + if (!dbus_g_proxy_end_call (proxy, call_id, &err, + DBUS_TYPE_G_OBJECT_PATH, &path, + G_TYPE_INVALID)) { + + if (dbus_g_error_has_name (err, WPAS_ERROR_INVALID_IFACE)) { + /* Interface not added, try to add it */ + nm_supplicant_interface_add_to_supplicant (info->interface, FALSE); + } else if (dbus_g_error_has_name (err, WPAS_ERROR_EXISTS_ERROR)) { + /* Interface already added, just try to get the interface */ + nm_supplicant_interface_add_to_supplicant (info->interface, TRUE); + } else { + nm_log_err (LOGD_SUPPLICANT, "(%s): error getting interface: %s", + priv->dev, err->message); + } + + g_error_free (err); + } else { + nm_log_dbg (LOGD_SUPPLICANT, "(%s): interface added to supplicant", priv->dev); + + priv->object_path = path; + + priv->iface_proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr), + WPAS_DBUS_SERVICE, + path, + WPAS_DBUS_IFACE_INTERFACE); + + dbus_g_proxy_add_signal (priv->iface_proxy, "ScanResultsAvailable", G_TYPE_INVALID); + + dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_STRING, + G_TYPE_NONE, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_INVALID); + + dbus_g_proxy_add_signal (priv->iface_proxy, "StateChange", G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID); + + dbus_g_proxy_connect_signal (priv->iface_proxy, "ScanResultsAvailable", + G_CALLBACK (wpas_iface_query_scan_results), + info->interface, + NULL); + + dbus_g_proxy_connect_signal (priv->iface_proxy, "StateChange", + G_CALLBACK (wpas_iface_handle_state_change), + info->interface, + NULL); + + dbus_g_proxy_add_signal (priv->iface_proxy, "Scanning", G_TYPE_BOOLEAN, G_TYPE_INVALID); + + dbus_g_proxy_connect_signal (priv->iface_proxy, "Scanning", + G_CALLBACK (wpas_iface_handle_scanning), + info->interface, + NULL); + + /* Interface added to the supplicant; get its initial state. */ + wpas_iface_get_state (info->interface); + wpas_iface_get_scanning (info->interface); + } +} + +static void +nm_supplicant_interface_add_to_supplicant (NMSupplicantInterface * self, + gboolean get_only) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + NMSupplicantInfo *info; + DBusGProxy *proxy; + DBusGProxyCall *call; + + proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr), + WPAS_DBUS_SERVICE, + WPAS_DBUS_PATH, + WPAS_DBUS_INTERFACE); + info = nm_supplicant_info_new (self, proxy, priv->other_pcalls); + + if (get_only) { + call = dbus_g_proxy_begin_call (proxy, "getInterface", + nm_supplicant_interface_add_cb, + info, + nm_supplicant_info_destroy, + G_TYPE_STRING, priv->dev, + G_TYPE_INVALID); + } else { + GHashTable *hash = g_hash_table_new (g_str_hash, g_str_equal); + GValue *driver; + + driver = g_new0 (GValue, 1); + g_value_init (driver, G_TYPE_STRING); + g_value_set_string (driver, priv->is_wireless ? "wext" : "wired"); + g_hash_table_insert (hash, "driver", driver); + + call = dbus_g_proxy_begin_call (proxy, "addInterface", + nm_supplicant_interface_add_cb, + info, + nm_supplicant_info_destroy, + G_TYPE_STRING, priv->dev, + DBUS_TYPE_G_MAP_OF_VARIANT, hash, + G_TYPE_INVALID); + + g_value_unset (driver); + g_free (driver); + g_hash_table_destroy (hash); + } + + g_object_unref (proxy); + + nm_supplicant_info_set_call (info, call); +} + +static void +nm_supplicant_interface_start (NMSupplicantInterface * self) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + guint32 state; + + /* Can only start the interface from INIT state */ + g_return_if_fail (priv->state == NM_SUPPLICANT_INTERFACE_STATE_INIT); + + nm_log_dbg (LOGD_SUPPLICANT, "(%s): adding interface to supplicant", priv->dev); + + state = nm_supplicant_manager_get_state (priv->smgr); + if (state == NM_SUPPLICANT_MANAGER_STATE_IDLE) { + nm_supplicant_interface_set_state (self, NM_SUPPLICANT_INTERFACE_STATE_STARTING); + nm_supplicant_interface_add_to_supplicant (self, FALSE); + } else if (state == NM_SUPPLICANT_MANAGER_STATE_DOWN) { + /* Don't do anything; wait for signal from supplicant manager + * that its state has changed. + */ + } else + nm_log_warn (LOGD_SUPPLICANT, "Unknown supplicant manager state!"); +} + +static void +nm_supplicant_interface_handle_supplicant_manager_idle_state (NMSupplicantInterface * self) +{ + switch (NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->state) { + case NM_SUPPLICANT_INTERFACE_STATE_INIT: + /* Move to STARTING state when supplicant is ready */ + nm_supplicant_interface_start (self); + break; + case NM_SUPPLICANT_INTERFACE_STATE_STARTING: + /* Don't do anything here, though we should never hit this */ + break; + case NM_SUPPLICANT_INTERFACE_STATE_READY: + /* Don't do anything here, though we should never hit this */ + break; + case NM_SUPPLICANT_INTERFACE_STATE_DOWN: + /* Don't do anything here; interface can't get out of DOWN state */ + break; + default: + nm_log_warn (LOGD_SUPPLICANT, "Unknown supplicant interface state!"); + break; + } +} + + +static void +nm_supplicant_interface_set_state (NMSupplicantInterface * self, + guint32 new_state) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + guint32 old_state; + + g_return_if_fail (new_state < NM_SUPPLICANT_INTERFACE_STATE_LAST); + + if (new_state == priv->state) + return; + + old_state = priv->state; + if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) { + /* If the interface is transitioning to DOWN and there's are + * in-progress pending calls, cancel them. + */ + cancel_all_callbacks (priv->other_pcalls); + cancel_all_callbacks (priv->assoc_pcalls); + } + + priv->state = new_state; + g_signal_emit (self, + nm_supplicant_interface_signals[STATE], + 0, + priv->state, + old_state); +} + +static void +nm_supplicant_interface_smgr_state_changed (NMSupplicantManager * smgr, + guint32 new_state, + guint32 old_state, + gpointer user_data) +{ + NMSupplicantInterface * self = NM_SUPPLICANT_INTERFACE (user_data); + + switch (new_state) { + case NM_SUPPLICANT_MANAGER_STATE_DOWN: + /* The supplicant went away, likely the connection to it is also + * gone. Therefore, this interface must move to the DOWN state + * and be disposed of. + */ + nm_supplicant_interface_set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN); + break; + case NM_SUPPLICANT_MANAGER_STATE_IDLE: + /* Handle the supplicant now being available. */ + nm_supplicant_interface_handle_supplicant_manager_idle_state (self); + break; + default: + nm_log_warn (LOGD_SUPPLICANT, "Unknown supplicant manager state!"); + break; + } +} + + +static void +remove_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + GError *err = NULL; + guint tmp; + + if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) { + nm_log_dbg (LOGD_SUPPLICANT, "Couldn't remove network from supplicant interface: %s.", + err->message); + g_error_free (err); + } +} + +static void +disconnect_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + GError *err = NULL; + guint tmp; + + if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) { + nm_log_warn (LOGD_SUPPLICANT, "Couldn't disconnect supplicant interface: %s.", + err->message); + g_error_free (err); + } +} + +void +nm_supplicant_interface_disconnect (NMSupplicantInterface * self) +{ + NMSupplicantInterfacePrivate *priv; + + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + /* Clear and cancel all pending calls related to a prior + * connection attempt. + */ + cancel_all_callbacks (priv->assoc_pcalls); + + /* Don't do anything if there is no connection to the supplicant yet. */ + if (!priv->iface_proxy) + return; + + /* Don't try to disconnect if the supplicant interface is already + * disconnected. + */ + if (priv->con_state == NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED + || priv->con_state == NM_SUPPLICANT_INTERFACE_CON_STATE_INACTIVE) { + if (priv->net_proxy) { + g_object_unref (priv->net_proxy); + priv->net_proxy = NULL; + } + + return; + } + + /* Remove any network that was added by NetworkManager */ + if (priv->net_proxy) { + dbus_g_proxy_begin_call (priv->iface_proxy, "removeNetwork", + remove_network_cb, + NULL, NULL, + DBUS_TYPE_G_OBJECT_PATH, dbus_g_proxy_get_path (priv->net_proxy), + G_TYPE_INVALID); + + g_object_unref (priv->net_proxy); + priv->net_proxy = NULL; + } + + dbus_g_proxy_begin_call (priv->iface_proxy, "disconnect", + disconnect_cb, + NULL, NULL, + G_TYPE_INVALID); +} + +static void +select_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMSupplicantInfo *info = (NMSupplicantInfo *) user_data; + GError *err = NULL; + guint tmp; + + if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) { + nm_log_warn (LOGD_SUPPLICANT, "Couldn't select network config: %s.", err->message); + emit_error_helper (info->interface, err); + g_error_free (err); + } +} + +static void +set_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMSupplicantInfo *info = (NMSupplicantInfo *) user_data; + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface); + GError *err = NULL; + guint tmp; + + if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) { + nm_log_warn (LOGD_SUPPLICANT, "Couldn't set network config: %s.", err->message); + emit_error_helper (info->interface, err); + g_error_free (err); + } else { + DBusGProxyCall *call; + + info = nm_supplicant_info_new (info->interface, priv->iface_proxy, priv->assoc_pcalls); + call = dbus_g_proxy_begin_call (priv->iface_proxy, "selectNetwork", + select_network_cb, + info, + nm_supplicant_info_destroy, + DBUS_TYPE_G_OBJECT_PATH, dbus_g_proxy_get_path (proxy), + G_TYPE_INVALID); + nm_supplicant_info_set_call (info, call); + } +} + +static void +call_set_network (NMSupplicantInfo *info) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface); + GHashTable *config_hash; + DBusGProxyCall *call; + + config_hash = nm_supplicant_config_get_hash (priv->cfg); + call = dbus_g_proxy_begin_call (priv->net_proxy, "set", + set_network_cb, + info, + nm_supplicant_info_destroy, + DBUS_TYPE_G_MAP_OF_VARIANT, config_hash, + G_TYPE_INVALID); + nm_supplicant_info_set_call (info, call); + g_hash_table_destroy (config_hash); +} + +static void +set_blobs_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMSupplicantInfo *info = (NMSupplicantInfo *) user_data; + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface); + GError *err = NULL; + guint tmp; + + if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) { + nm_log_warn (LOGD_SUPPLICANT, "Couldn't set network certificates: %s.", err->message); + emit_error_helper (info->interface, err); + g_error_free (err); + } else { + info = nm_supplicant_info_new (info->interface, priv->iface_proxy, priv->assoc_pcalls); + call_set_network (info); + } +} + +static GValue * +byte_array_to_gvalue (const GByteArray *array) +{ + GValue *val; + + val = g_slice_new0 (GValue); + g_value_init (val, DBUS_TYPE_G_UCHAR_ARRAY); + g_value_set_boxed (val, array); + + return val; +} + +static void +blob_free (GValue *val) +{ + g_value_unset (val); + g_slice_free (GValue, val); +} + +static void +convert_blob (const char *key, const GByteArray *value, GHashTable *hash) +{ + GValue *val; + + val = byte_array_to_gvalue (value); + g_hash_table_insert (hash, g_strdup (key), val); +} + +static void +call_set_blobs (NMSupplicantInfo *info, GHashTable *orig_blobs) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface); + DBusGProxyCall *call; + GHashTable *blobs; + + blobs = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) blob_free); + if (!blobs) { + const char *msg = "Not enough memory to create blob table."; + + nm_log_warn (LOGD_SUPPLICANT, "%s", msg); + g_signal_emit (info->interface, + nm_supplicant_interface_signals[CONNECTION_ERROR], + 0, "SendBlobError", msg); + return; + } + + g_hash_table_foreach (orig_blobs, (GHFunc) convert_blob, blobs); + + call = dbus_g_proxy_begin_call (priv->iface_proxy, "setBlobs", + set_blobs_cb, + info, + nm_supplicant_info_destroy, + DBUS_TYPE_G_MAP_OF_VARIANT, blobs, + G_TYPE_INVALID); + nm_supplicant_info_set_call (info, call); + g_hash_table_destroy (blobs); +} + +static void +add_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMSupplicantInfo *info = (NMSupplicantInfo *) user_data; + GError *err = NULL; + char *path = NULL; + + if (!dbus_g_proxy_end_call (proxy, call_id, &err, + DBUS_TYPE_G_OBJECT_PATH, &path, + G_TYPE_INVALID)) { + nm_log_warn (LOGD_SUPPLICANT, "Couldn't add a network to the supplicant interface: %s.", + err->message); + emit_error_helper (info->interface, err); + g_error_free (err); + } else { + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface); + GHashTable *blobs; + + priv->net_proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr), + WPAS_DBUS_SERVICE, + path, + WPAS_DBUS_IFACE_NETWORK); + g_free (path); + + info = nm_supplicant_info_new (info->interface, + priv->net_proxy, + priv->assoc_pcalls); + /* Send any blobs first; if there aren't any jump to sending the + * config settings. + */ + blobs = nm_supplicant_config_get_blobs (priv->cfg); + if (g_hash_table_size (blobs) > 0) + call_set_blobs (info, blobs); + else + call_set_network (info); + } +} + +static void +set_ap_scan_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMSupplicantInfo *info = (NMSupplicantInfo *) user_data; + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface); + GError *err = NULL; + guint32 tmp; + DBusGProxyCall *call; + + if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) { + nm_log_warn (LOGD_SUPPLICANT, "Couldn't send AP scan mode to the supplicant interface: %s.", + err->message); + emit_error_helper (info->interface, err); + g_error_free (err); + return; + } + + nm_log_info (LOGD_SUPPLICANT, "Config: set interface ap_scan to %d", + nm_supplicant_config_get_ap_scan (priv->cfg)); + + info = nm_supplicant_info_new (info->interface, proxy, info->store); + call = dbus_g_proxy_begin_call (proxy, "addNetwork", + add_network_cb, + info, + nm_supplicant_info_destroy, + G_TYPE_INVALID); + nm_supplicant_info_set_call (info, call); +} + +gboolean +nm_supplicant_interface_set_config (NMSupplicantInterface * self, + NMSupplicantConfig * cfg) +{ + NMSupplicantInterfacePrivate *priv; + NMSupplicantInfo *info; + DBusGProxyCall *call; + guint32 ap_scan; + + g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + nm_supplicant_interface_disconnect (self); + + if (priv->cfg) + g_object_unref (priv->cfg); + priv->cfg = cfg; + + if (cfg == NULL) + return TRUE; + + g_object_ref (priv->cfg); + + info = nm_supplicant_info_new (self, priv->iface_proxy, priv->other_pcalls); + ap_scan = nm_supplicant_config_get_ap_scan (priv->cfg); + call = dbus_g_proxy_begin_call (priv->iface_proxy, "setAPScan", + set_ap_scan_cb, + info, + nm_supplicant_info_destroy, + G_TYPE_UINT, ap_scan, + G_TYPE_INVALID); + nm_supplicant_info_set_call (info, call); + + return call != NULL; +} + +const char * +nm_supplicant_interface_get_device (NMSupplicantInterface * self) +{ + g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NULL); + + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->dev; +} + +static void +scan_request_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMSupplicantInfo *info = (NMSupplicantInfo *) user_data; + GError *err = NULL; + guint32 success = 0; + + if (!dbus_g_proxy_end_call (proxy, call_id, &err, + G_TYPE_UINT, &success, + G_TYPE_INVALID)) { + nm_log_warn (LOGD_SUPPLICANT, "Could not get scan request result: %s", err->message); + g_error_free (err); + } + + /* Notify listeners of the result of the scan */ + g_signal_emit (info->interface, + nm_supplicant_interface_signals[SCAN_REQ_RESULT], + 0, + success ? TRUE : FALSE); +} + +gboolean +nm_supplicant_interface_request_scan (NMSupplicantInterface * self) +{ + NMSupplicantInterfacePrivate *priv; + NMSupplicantInfo *info; + DBusGProxyCall *call; + + g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + info = nm_supplicant_info_new (self, priv->iface_proxy, priv->other_pcalls); + call = dbus_g_proxy_begin_call (priv->iface_proxy, "scan", + scan_request_cb, + info, + nm_supplicant_info_destroy, + G_TYPE_INVALID); + nm_supplicant_info_set_call (info, call); + + return call != NULL; +} + +guint32 +nm_supplicant_interface_get_state (NMSupplicantInterface * self) +{ + g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NM_SUPPLICANT_INTERFACE_STATE_DOWN); + + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->state; +} + +guint32 +nm_supplicant_interface_get_connection_state (NMSupplicantInterface * self) +{ + g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED); + + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->con_state; +} + +const char * +nm_supplicant_interface_state_to_string (guint32 state) +{ + switch (state) { + case NM_SUPPLICANT_INTERFACE_STATE_INIT: + return "init"; + case NM_SUPPLICANT_INTERFACE_STATE_STARTING: + return "starting"; + case NM_SUPPLICANT_INTERFACE_STATE_READY: + return "ready"; + case NM_SUPPLICANT_INTERFACE_STATE_DOWN: + return "down"; + default: + break; + } + return "unknown"; +} + +const char * +nm_supplicant_interface_connection_state_to_string (guint32 state) +{ + switch (state) { + case NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED: + return "disconnected"; + case NM_SUPPLICANT_INTERFACE_CON_STATE_INACTIVE: + return "inactive"; + case NM_SUPPLICANT_INTERFACE_CON_STATE_SCANNING: + return "scanning"; + case NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATING: + return "associating"; + case NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATED: + return "associated"; + case NM_SUPPLICANT_INTERFACE_CON_STATE_4WAY_HANDSHAKE: + return "4-way handshake"; + case NM_SUPPLICANT_INTERFACE_CON_STATE_GROUP_HANDSHAKE: + return "group handshake"; + case NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED: + return "completed"; + default: + break; + } + return "unknown"; +} + diff --git a/src/supplicant-manager/nm-supplicant-interface.h b/src/supplicant-manager/nm-supplicant-interface.h new file mode 100644 index 00000000..bee5436f --- /dev/null +++ b/src/supplicant-manager/nm-supplicant-interface.h @@ -0,0 +1,145 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2008 Red Hat, Inc. + * Copyright (C) 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_SUPPLICANT_INTERFACE_H +#define NM_SUPPLICANT_INTERFACE_H + +#include <glib-object.h> +#include <dbus/dbus.h> +#include "nm-supplicant-types.h" + +G_BEGIN_DECLS + +/* + * Supplicant interface states + * The states are linear, ie INIT -> READY -> DOWN and state may only be + * changed in one direction. If an interface reaches the DOWN state, it + * cannot be re-initialized; it must be torn down and a new one created. + * + * INIT: interface has been created, but cannot be used yet; it is waiting + * for pending requests of the supplicant to complete. + * READY: interface is ready for use + * DOWN: interface has been removed or has otherwise been made invalid; it + * must be torn down. + * + * Note: LAST is an invalid state and only used for boundary checking. + */ +enum { + NM_SUPPLICANT_INTERFACE_STATE_INIT = 0, + NM_SUPPLICANT_INTERFACE_STATE_STARTING, + NM_SUPPLICANT_INTERFACE_STATE_READY, + NM_SUPPLICANT_INTERFACE_STATE_DOWN, + NM_SUPPLICANT_INTERFACE_STATE_LAST +}; + + +/* + * Supplicant interface connection states + * The wpa_supplicant state for the connection. + */ +enum { + NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED = 0, + NM_SUPPLICANT_INTERFACE_CON_STATE_INACTIVE, + NM_SUPPLICANT_INTERFACE_CON_STATE_SCANNING, + NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATING, + NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATED, + NM_SUPPLICANT_INTERFACE_CON_STATE_4WAY_HANDSHAKE, + NM_SUPPLICANT_INTERFACE_CON_STATE_GROUP_HANDSHAKE, + NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED, + NM_SUPPLICANT_INTERFACE_CON_STATE_LAST +}; + +#define NM_TYPE_SUPPLICANT_INTERFACE (nm_supplicant_interface_get_type ()) +#define NM_SUPPLICANT_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SUPPLICANT_INTERFACE, NMSupplicantInterface)) +#define NM_SUPPLICANT_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SUPPLICANT_INTERFACE, NMSupplicantInterfaceClass)) +#define NM_IS_SUPPLICANT_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SUPPLICANT_INTERFACE)) +#define NM_IS_SUPPLICANT_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SUPPLICANT_INTERFACE)) +#define NM_SUPPLICANT_INTERFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SUPPLICANT_INTERFACE, NMSupplicantInterfaceClass)) + +struct _NMSupplicantInterface { + GObject parent; +}; + +typedef struct { + GObjectClass parent; + + /* Signals */ + + /* change in the interface's state */ + void (*state) (NMSupplicantInterface * iface, + guint32 new_state, + guint32 old_state); + + /* interface was removed by the supplicant */ + void (*removed) (NMSupplicantInterface * iface); + + /* interface saw a new access point from a scan */ + void (*scanned_ap) (NMSupplicantInterface * iface, + DBusMessage * message); + + /* result of a wireless scan request */ + void (*scan_req_result) (NMSupplicantInterface * iface, + gboolean success); + + /* scan results returned from supplicant */ + void (*scan_results) (NMSupplicantInterface * iface, + guint num_bssids); + + /* link state of the device's connection */ + void (*connection_state) (NMSupplicantInterface * iface, + guint32 new_state, + guint32 old_state); + + /* an error occurred during a connection request */ + void (*connection_error) (NMSupplicantInterface * iface, + const char * name, + const char * message); +} NMSupplicantInterfaceClass; + + +GType nm_supplicant_interface_get_type (void); + +NMSupplicantInterface * nm_supplicant_interface_new (NMSupplicantManager * smgr, + const char *ifname, + gboolean is_wireless); + +gboolean nm_supplicant_interface_set_config (NMSupplicantInterface * iface, + NMSupplicantConfig * cfg); + +void nm_supplicant_interface_disconnect (NMSupplicantInterface * iface); + +const char * nm_supplicant_interface_get_device (NMSupplicantInterface * iface); + +gboolean nm_supplicant_interface_request_scan (NMSupplicantInterface * self); + +guint32 nm_supplicant_interface_get_state (NMSupplicantInterface * self); + +guint32 nm_supplicant_interface_get_connection_state (NMSupplicantInterface * self); + +const char *nm_supplicant_interface_state_to_string (guint32 state); + +const char *nm_supplicant_interface_connection_state_to_string (guint32 state); + +gboolean nm_supplicant_interface_get_scanning (NMSupplicantInterface *self); + +G_END_DECLS + +#endif /* NM_SUPPLICANT_INTERFACE_H */ diff --git a/src/supplicant-manager/nm-supplicant-manager.c b/src/supplicant-manager/nm-supplicant-manager.c new file mode 100644 index 00000000..a2cf58eb --- /dev/null +++ b/src/supplicant-manager/nm-supplicant-manager.c @@ -0,0 +1,343 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2008 Red Hat, Inc. + * Copyright (C) 2007 - 2008 Novell, Inc. + */ + +#include <string.h> +#include <glib.h> +#include <dbus/dbus.h> + +#include "nm-supplicant-manager.h" +#include "nm-supplicant-interface.h" +#include "nm-dbus-manager.h" +#include "nm-marshal.h" +#include "nm-logging.h" +#include "nm-glib-compat.h" + +#define SUPPLICANT_POKE_INTERVAL 120 + +typedef struct { + NMDBusManager * dbus_mgr; + guint32 state; + GSList * ifaces; + gboolean dispose_has_run; + guint poke_id; +} NMSupplicantManagerPrivate; + +#define NM_SUPPLICANT_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + NM_TYPE_SUPPLICANT_MANAGER, \ + NMSupplicantManagerPrivate)) + +G_DEFINE_TYPE (NMSupplicantManager, nm_supplicant_manager, G_TYPE_OBJECT) + + +static void nm_supplicant_manager_name_owner_changed (NMDBusManager *dbus_mgr, + const char *name, + const char *old, + const char *new, + gpointer user_data); + +static void nm_supplicant_manager_set_state (NMSupplicantManager * self, + guint32 new_state); + +static gboolean nm_supplicant_manager_startup (NMSupplicantManager * self); + + +/* Signals */ +enum { + STATE, /* change in the manager's state */ + LAST_SIGNAL +}; +static guint nm_supplicant_manager_signals[LAST_SIGNAL] = { 0 }; + + +NMSupplicantManager * +nm_supplicant_manager_get (void) +{ + static NMSupplicantManager * singleton = NULL; + + if (!singleton) { + singleton = NM_SUPPLICANT_MANAGER (g_object_new (NM_TYPE_SUPPLICANT_MANAGER, NULL)); + } else { + g_object_ref (singleton); + } + + g_assert (singleton); + return singleton; +} + +static gboolean +poke_supplicant_cb (gpointer user_data) +{ + NMSupplicantManager *self = NM_SUPPLICANT_MANAGER (user_data); + NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + DBusGConnection *g_connection; + DBusGProxy *proxy; + const char *tmp = "ignoreme"; + + g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr); + proxy = dbus_g_proxy_new_for_name (g_connection, + WPAS_DBUS_SERVICE, + WPAS_DBUS_PATH, + WPAS_DBUS_INTERFACE); + if (!proxy) { + nm_log_warn (LOGD_SUPPLICANT, "Error: could not init wpa_supplicant proxy"); + goto out; + } + + nm_log_info (LOGD_SUPPLICANT, "Trying to start the supplicant..."); + dbus_g_proxy_call_no_reply (proxy, "getInterface", G_TYPE_STRING, tmp, G_TYPE_INVALID); + g_object_unref (proxy); + +out: + /* Reschedule the poke */ + priv->poke_id = g_timeout_add_seconds (SUPPLICANT_POKE_INTERVAL, + poke_supplicant_cb, + (gpointer) self); + + return FALSE; +} + +static void +nm_supplicant_manager_init (NMSupplicantManager * self) +{ + NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + gboolean running; + + priv->dispose_has_run = FALSE; + priv->state = NM_SUPPLICANT_MANAGER_STATE_DOWN; + priv->dbus_mgr = nm_dbus_manager_get (); + priv->poke_id = 0; + + running = nm_supplicant_manager_startup (self); + + g_signal_connect (priv->dbus_mgr, + "name-owner-changed", + G_CALLBACK (nm_supplicant_manager_name_owner_changed), + self); + + if (!running) { + /* Try to activate the supplicant */ + priv->poke_id = g_idle_add (poke_supplicant_cb, (gpointer) self); + } +} + +static void +nm_supplicant_manager_dispose (GObject *object) +{ + NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (object); + + if (priv->dispose_has_run) { + G_OBJECT_CLASS (nm_supplicant_manager_parent_class)->dispose (object); + return; + } + + priv->dispose_has_run = TRUE; + + if (priv->poke_id) { + g_source_remove (priv->poke_id); + priv->poke_id = 0; + } + + if (priv->dbus_mgr) { + g_object_unref (G_OBJECT (priv->dbus_mgr)); + priv->dbus_mgr = NULL; + } + + /* Chain up to the parent class */ + G_OBJECT_CLASS (nm_supplicant_manager_parent_class)->dispose (object); +} + +static void +nm_supplicant_manager_class_init (NMSupplicantManagerClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMSupplicantManagerPrivate)); + + object_class->dispose = nm_supplicant_manager_dispose; + + /* Signals */ + nm_supplicant_manager_signals[STATE] = + g_signal_new ("state", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMSupplicantManagerClass, state), + NULL, NULL, + _nm_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); +} + +static void +nm_supplicant_manager_name_owner_changed (NMDBusManager *dbus_mgr, + const char *name, + const char *old_owner, + const char *new_owner, + gpointer user_data) +{ + NMSupplicantManager * self = (NMSupplicantManager *) user_data; + NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + gboolean old_owner_good = (old_owner && strlen (old_owner)); + gboolean new_owner_good = (new_owner && strlen (new_owner)); + + /* Can't handle the signal if its not from the supplicant service */ + if (strcmp (WPAS_DBUS_SERVICE, name) != 0) + return; + + if (!old_owner_good && new_owner_good) { + gboolean running; + + running = nm_supplicant_manager_startup (self); + + if (running && priv->poke_id) { + g_source_remove (priv->poke_id); + priv->poke_id = 0; + } + } else if (old_owner_good && !new_owner_good) { + nm_supplicant_manager_set_state (self, NM_SUPPLICANT_MANAGER_STATE_DOWN); + + if (priv->poke_id) + g_source_remove (priv->poke_id); + + /* Poke the supplicant so that it gets activated by dbus system bus + * activation. + */ + priv->poke_id = g_idle_add (poke_supplicant_cb, (gpointer) self); + } +} + + +guint32 +nm_supplicant_manager_get_state (NMSupplicantManager * self) +{ + g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), FALSE); + + return NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->state; +} + +static void +nm_supplicant_manager_set_state (NMSupplicantManager * self, guint32 new_state) +{ + NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + guint32 old_state; + + if (new_state == priv->state) + return; + + old_state = priv->state; + priv->state = new_state; + g_signal_emit (self, + nm_supplicant_manager_signals[STATE], + 0, + priv->state, + old_state); +} + +static gboolean +nm_supplicant_manager_startup (NMSupplicantManager * self) +{ + gboolean running; + + /* FIXME: convert to pending call */ + running = nm_dbus_manager_name_has_owner (NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->dbus_mgr, + WPAS_DBUS_SERVICE); + if (running) + nm_supplicant_manager_set_state (self, NM_SUPPLICANT_MANAGER_STATE_IDLE); + + return running; +} + +NMSupplicantInterface * +nm_supplicant_manager_get_iface (NMSupplicantManager * self, + const char *ifname, + gboolean is_wireless) +{ + NMSupplicantManagerPrivate *priv; + NMSupplicantInterface * iface = NULL; + GSList * elt; + + g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL); + g_return_val_if_fail (ifname != NULL, NULL); + + priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + + /* Ensure we don't already have this interface */ + for (elt = priv->ifaces; elt; elt = g_slist_next (elt)) { + NMSupplicantInterface * if_tmp = (NMSupplicantInterface *) elt->data; + + if (!strcmp (ifname, nm_supplicant_interface_get_device (if_tmp))) { + iface = if_tmp; + break; + } + } + + if (!iface) { + nm_log_dbg (LOGD_SUPPLICANT, "(%s): creating new supplicant interface", ifname); + iface = nm_supplicant_interface_new (self, ifname, is_wireless); + if (iface) + priv->ifaces = g_slist_append (priv->ifaces, iface); + } else { + nm_log_dbg (LOGD_SUPPLICANT, "(%s): returning existing supplicant interface", ifname); + } + + return iface; +} + +void +nm_supplicant_manager_release_iface (NMSupplicantManager * self, + NMSupplicantInterface * iface) +{ + NMSupplicantManagerPrivate *priv; + GSList * elt; + + g_return_if_fail (NM_IS_SUPPLICANT_MANAGER (self)); + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (iface)); + + priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + + for (elt = priv->ifaces; elt; elt = g_slist_next (elt)) { + NMSupplicantInterface * if_tmp = (NMSupplicantInterface *) elt->data; + + if (if_tmp == iface) { + /* Remove the iface from the supplicant manager's list and + * dereference to match additional reference in get_iface. + */ + priv->ifaces = g_slist_remove_link (priv->ifaces, elt); + g_slist_free_1 (elt); + g_object_unref (iface); + break; + } + } +} + +const char * +nm_supplicant_manager_state_to_string (guint32 state) +{ + switch (state) { + case NM_SUPPLICANT_MANAGER_STATE_DOWN: + return "down"; + case NM_SUPPLICANT_MANAGER_STATE_IDLE: + return "idle"; + default: + break; + } + return "unknown"; +} + + diff --git a/src/supplicant-manager/nm-supplicant-manager.h b/src/supplicant-manager/nm-supplicant-manager.h new file mode 100644 index 00000000..fef2a774 --- /dev/null +++ b/src/supplicant-manager/nm-supplicant-manager.h @@ -0,0 +1,88 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2008 Red Hat, Inc. + * Copyright (C) 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_SUPPLICANT_MANAGER_H +#define NM_SUPPLICANT_MANAGER_H + +#include <glib-object.h> +#include "nm-supplicant-types.h" +#include "nm-device.h" + +#define WPAS_DBUS_SERVICE "fi.epitest.hostap.WPASupplicant" +#define WPAS_DBUS_PATH "/fi/epitest/hostap/WPASupplicant" +#define WPAS_DBUS_INTERFACE "fi.epitest.hostap.WPASupplicant" + + +G_BEGIN_DECLS + +/* + * Supplicant manager states + * Either state may transition to the other state at any time. + * + * DOWN: supplicant manager has been created, but cannot be used; the supplicant + * is either not running or has not yet been fully initialized. + * IDLE: supplicant manager is ready for use + * + * Note: LAST is an invalid state and only used for boundary checking. + */ +enum { + NM_SUPPLICANT_MANAGER_STATE_DOWN = 0, + NM_SUPPLICANT_MANAGER_STATE_IDLE, + NM_SUPPLICANT_MANAGER_STATE_LAST +}; + + +#define NM_TYPE_SUPPLICANT_MANAGER (nm_supplicant_manager_get_type ()) +#define NM_SUPPLICANT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SUPPLICANT_MANAGER, NMSupplicantManager)) +#define NM_SUPPLICANT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SUPPLICANT_MANAGER, NMSupplicantManagerClass)) +#define NM_IS_SUPPLICANT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SUPPLICANT_MANAGER)) +#define NM_IS_SUPPLICANT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SUPPLICANT_MANAGER)) +#define NM_SUPPLICANT_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SUPPLICANT_MANAGER, NMSupplicantManagerClass)) + +struct _NMSupplicantManager +{ + GObject parent; +}; + +typedef struct +{ + GObjectClass parent; + + /* class members */ + void (* state) (NMSupplicantManager * mgr, guint32 new_state, guint32 old_state); +} NMSupplicantManagerClass; + +GType nm_supplicant_manager_get_type (void); + +NMSupplicantManager * nm_supplicant_manager_get (void); + +guint32 nm_supplicant_manager_get_state (NMSupplicantManager * mgr); + +NMSupplicantInterface * nm_supplicant_manager_get_iface (NMSupplicantManager * mgr, + const char *ifname, + gboolean is_wireless); + +void nm_supplicant_manager_release_iface (NMSupplicantManager * mgr, + NMSupplicantInterface * iface); + +const char *nm_supplicant_manager_state_to_string (guint32 state); + +#endif /* NM_SUPPLICANT_MANAGER_H */ diff --git a/src/supplicant-manager/nm-supplicant-settings-verify.c b/src/supplicant-manager/nm-supplicant-settings-verify.c new file mode 100644 index 00000000..28334655 --- /dev/null +++ b/src/supplicant-manager/nm-supplicant-settings-verify.c @@ -0,0 +1,238 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2008 Red Hat, Inc. + */ + +#include <glib.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "nm-supplicant-settings-verify.h" + +struct Opt { + const char * key; + const OptType type; + const gint32 int_low; /* Inclusive */ + const gint32 int_high; /* Inclusive; max length for strings */ + const gboolean str_allowed_multiple; + const char ** str_allowed; +}; + + +static gboolean validate_type_int (const struct Opt * opt, + const char * value, + const guint32 len); + +static gboolean validate_type_bytes (const struct Opt * opt, + const char * value, + const guint32 len); + +static gboolean validate_type_keyword (const struct Opt * opt, + const char * value, + const guint32 len); + +typedef gboolean (*validate_func)(const struct Opt *, const char *, const guint32); + +struct validate_entry { + const OptType type; + const validate_func func; +}; + +static const struct validate_entry validate_table[] = { + { TYPE_INT, validate_type_int }, + { TYPE_BYTES, validate_type_bytes }, + { TYPE_KEYWORD, validate_type_keyword }, +}; + + +const char * pairwise_allowed[] = { "CCMP", "TKIP", "NONE", NULL }; +const char * group_allowed[] = { "CCMP", "TKIP", "WEP104", "WEP40", NULL }; +const char * proto_allowed[] = { "WPA", "RSN", NULL }; +const char * key_mgmt_allowed[] = { "WPA-PSK", "WPA-EAP", "IEEE8021X", "WPA-NONE", + "NONE", NULL }; +const char * auth_alg_allowed[] = { "OPEN", "SHARED", "LEAP", NULL }; +const char * eap_allowed[] = { "LEAP", "MD5", "TLS", "PEAP", "TTLS", "SIM", + "PSK", "FAST", NULL }; + +const char * phase1_allowed[] = {"peapver=0", "peapver=1", "peaplabel=1", + "peap_outer_success=0", "include_tls_length=1", + "sim_min_num_chal=3", NULL }; +const char * phase2_allowed[] = {"auth=PAP", "auth=CHAP", "auth=MSCHAP", + "auth=MSCHAPV2", "auth=GTC", "auth=OTP", + "auth=MD5", "auth=TLS", "autheap=MD5", + "autheap=MSCHAPV2", "autheap=OTP", + "autheap=GTC", "autheap=TLS", NULL }; + +static const struct Opt opt_table[] = { + { "ssid", TYPE_BYTES, 0, 32,FALSE, NULL }, + { "bssid", TYPE_KEYWORD, 0, 0, FALSE, NULL }, + { "scan_ssid", TYPE_INT, 0, 1, FALSE, NULL }, + { "mode", TYPE_INT, 0, 1, FALSE, NULL }, + { "frequency", TYPE_INT, 2412, 5825, FALSE, NULL }, + { "auth_alg", TYPE_KEYWORD, 0, 0, FALSE, auth_alg_allowed }, + { "psk", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "pairwise", TYPE_KEYWORD, 0, 0, FALSE, pairwise_allowed }, + { "group", TYPE_KEYWORD, 0, 0, FALSE, group_allowed }, + { "proto", TYPE_KEYWORD, 0, 0, FALSE, proto_allowed }, + { "key_mgmt", TYPE_KEYWORD, 0, 0, FALSE, key_mgmt_allowed }, + { "wep_key0", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "wep_key1", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "wep_key2", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "wep_key3", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "wep_tx_keyidx", TYPE_INT, 0, 3, FALSE, NULL }, + { "eapol_flags", TYPE_INT, 0, 3, FALSE, NULL }, + { "eap", TYPE_KEYWORD, 0, 0, FALSE, eap_allowed }, + { "identity", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "password", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "ca_path", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "ca_cert", TYPE_BYTES, 0, 65536, FALSE, NULL }, + { "client_cert", TYPE_BYTES, 0, 65536, FALSE, NULL }, + { "private_key", TYPE_BYTES, 0, 65536, FALSE, NULL }, + { "private_key_passwd", TYPE_BYTES, 0, 1024, FALSE, NULL }, + { "phase1", TYPE_KEYWORD, 0, 0, TRUE, phase1_allowed }, + { "phase2", TYPE_KEYWORD, 0, 0, TRUE, phase2_allowed }, + { "anonymous_identity", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "ca_path2", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "ca_cert2", TYPE_BYTES, 0, 65536, FALSE, NULL }, + { "client_cert2", TYPE_BYTES, 0, 65536, FALSE, NULL }, + { "private_key2", TYPE_BYTES, 0, 65536, FALSE, NULL }, + { "private_key2_passwd",TYPE_BYTES, 0, 1024, FALSE, NULL }, + { "pin", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "pcsc", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "nai", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "eappsk", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "pac_file", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "engine", TYPE_INT, 0, 1, FALSE, NULL }, + { "engine_id", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "key_id", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "fragment_size", TYPE_INT, 1, 2000, FALSE, NULL }, +}; + + +static gboolean +validate_type_int (const struct Opt * opt, + const char * value, + const guint32 len) +{ + long int intval; + + g_return_val_if_fail (opt != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + errno = 0; + intval = strtol (value, NULL, 10); + if (errno != 0) + return FALSE; + + /* strtol returns a long, but we are dealing with ints */ + if (intval > INT_MAX || intval < INT_MIN) + return FALSE; + if (intval > opt->int_high || intval < opt->int_low) + return FALSE; + + return TRUE; +} + +static gboolean +validate_type_bytes (const struct Opt * opt, + const char * value, + const guint32 len) +{ + guint32 check_len; + + g_return_val_if_fail (opt != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + check_len = opt->int_high ? opt->int_high : 255; + if (len > check_len) + return FALSE; + + return TRUE; +} + +static gboolean +validate_type_keyword (const struct Opt * opt, + const char * value, + const guint32 len) +{ + char ** allowed; + gchar ** candidates = NULL; + char ** candidate; + gboolean found = FALSE; + + g_return_val_if_fail (opt != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + /* Allow everything */ + if (!opt->str_allowed) + return TRUE; + + candidates = g_strsplit (value, " ", 0); + if (!candidates) + goto out; + + /* validate each space-separated word in 'value' */ + for (candidate = candidates; *candidate; candidate++) { + found = FALSE; + for (allowed = (char **) opt->str_allowed; *allowed; allowed++) { + if (strcmp (*candidate, *allowed) == 0) { + found = TRUE; + break; + } + } + if (!found) + break; + } + +out: + g_strfreev (candidates); + return found; +} + +OptType +nm_supplicant_settings_verify_setting (const char * key, + const char * value, + const guint32 len) +{ + OptType type = TYPE_INVALID; + int opt_count = sizeof (opt_table) / sizeof (opt_table[0]); + int val_count = sizeof (validate_table) / sizeof (validate_table[0]); + int i, j; + + g_return_val_if_fail (key != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + for (i = 0; i < opt_count; i++) { + if (strcmp (opt_table[i].key, key) != 0) + continue; + + for (j = 0; j < val_count; j++) { + if (validate_table[j].type == opt_table[i].type) { + if ((*(validate_table[j].func))(&opt_table[i], value, len)) { + type = opt_table[i].type; + break; + } + } + } + } + + return type; +} + diff --git a/src/supplicant-manager/nm-supplicant-settings-verify.h b/src/supplicant-manager/nm-supplicant-settings-verify.h new file mode 100644 index 00000000..0939ebdb --- /dev/null +++ b/src/supplicant-manager/nm-supplicant-settings-verify.h @@ -0,0 +1,37 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2008 Red Hat, Inc. + */ + +#ifndef NM_SUPPLICANT_SETTINGS_VERIFY_H +#define NM_SUPPLICANT_SETTINGS_VERIFY_H + +typedef enum OptType { + TYPE_INVALID = 0, + TYPE_INT, + TYPE_BYTES, + TYPE_KEYWORD, + TYPE_STRING +} OptType; + +OptType nm_supplicant_settings_verify_setting (const char * key, + const char * value, + const guint32 len); + + +#endif /* NM_SUPPLICANT_SETTINGS_VERIFY_H */ diff --git a/src/supplicant-manager/nm-supplicant-types.h b/src/supplicant-manager/nm-supplicant-types.h new file mode 100644 index 00000000..16e4a6ba --- /dev/null +++ b/src/supplicant-manager/nm-supplicant-types.h @@ -0,0 +1,28 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2008 Red Hat, Inc. + */ + +#ifndef NM_SUPPLICANT_TYPES_H +#define NM_SUPPLICANT_TYPES_H + +typedef struct _NMSupplicantManager NMSupplicantManager; +typedef struct _NMSupplicantInterface NMSupplicantInterface; +typedef struct _NMSupplicantConfig NMSupplicantConfig; + +#endif /* NM_SUPPLICANT_TYPES_H */ diff --git a/src/supplicant-manager/tests/Makefile.am b/src/supplicant-manager/tests/Makefile.am new file mode 100644 index 00000000..b5e4db61 --- /dev/null +++ b/src/supplicant-manager/tests/Makefile.am @@ -0,0 +1,27 @@ +INCLUDES = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/libnm-util \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/supplicant-manager + +noinst_PROGRAMS = test-supplicant-config + +test_supplicant_config_SOURCES = \ + test-supplicant-config.c + +test_supplicant_config_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) + +test_supplicant_config_LDADD = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(top_builddir)/src/supplicant-manager/libsupplicant-manager.la \ + $(DBUS_LIBS) + +if WITH_TESTS + +check-local: test-supplicant-config + $(abs_builddir)/test-supplicant-config + +endif + diff --git a/src/supplicant-manager/tests/Makefile.in b/src/supplicant-manager/tests/Makefile.in new file mode 100644 index 00000000..f919b8b8 --- /dev/null +++ b/src/supplicant-manager/tests/Makefile.in @@ -0,0 +1,579 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +noinst_PROGRAMS = test-supplicant-config$(EXEEXT) +subdir = src/supplicant-manager/tests +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +PROGRAMS = $(noinst_PROGRAMS) +am_test_supplicant_config_OBJECTS = \ + test_supplicant_config-test-supplicant-config.$(OBJEXT) +test_supplicant_config_OBJECTS = $(am_test_supplicant_config_OBJECTS) +am__DEPENDENCIES_1 = +test_supplicant_config_DEPENDENCIES = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(top_builddir)/src/supplicant-manager/libsupplicant-manager.la \ + $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(test_supplicant_config_SOURCES) +DIST_SOURCES = $(test_supplicant_config_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +IPTABLES_PATH = @IPTABLES_PATH@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBM = @LIBM@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POFILES = @POFILES@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +RANLIB = @RANLIB@ +RESOLVCONF_PATH = @RESOLVCONF_PATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/libnm-util \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/supplicant-manager + +test_supplicant_config_SOURCES = \ + test-supplicant-config.c + +test_supplicant_config_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) + +test_supplicant_config_LDADD = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(top_builddir)/src/supplicant-manager/libsupplicant-manager.la \ + $(DBUS_LIBS) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/supplicant-manager/tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/supplicant-manager/tests/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +test-supplicant-config$(EXEEXT): $(test_supplicant_config_OBJECTS) $(test_supplicant_config_DEPENDENCIES) + @rm -f test-supplicant-config$(EXEEXT) + $(LINK) $(test_supplicant_config_OBJECTS) $(test_supplicant_config_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_supplicant_config-test-supplicant-config.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +test_supplicant_config-test-supplicant-config.o: test-supplicant-config.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_supplicant_config_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_supplicant_config-test-supplicant-config.o -MD -MP -MF $(DEPDIR)/test_supplicant_config-test-supplicant-config.Tpo -c -o test_supplicant_config-test-supplicant-config.o `test -f 'test-supplicant-config.c' || echo '$(srcdir)/'`test-supplicant-config.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/test_supplicant_config-test-supplicant-config.Tpo $(DEPDIR)/test_supplicant_config-test-supplicant-config.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-supplicant-config.c' object='test_supplicant_config-test-supplicant-config.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_supplicant_config_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_supplicant_config-test-supplicant-config.o `test -f 'test-supplicant-config.c' || echo '$(srcdir)/'`test-supplicant-config.c + +test_supplicant_config-test-supplicant-config.obj: test-supplicant-config.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_supplicant_config_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_supplicant_config-test-supplicant-config.obj -MD -MP -MF $(DEPDIR)/test_supplicant_config-test-supplicant-config.Tpo -c -o test_supplicant_config-test-supplicant-config.obj `if test -f 'test-supplicant-config.c'; then $(CYGPATH_W) 'test-supplicant-config.c'; else $(CYGPATH_W) '$(srcdir)/test-supplicant-config.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/test_supplicant_config-test-supplicant-config.Tpo $(DEPDIR)/test_supplicant_config-test-supplicant-config.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-supplicant-config.c' object='test_supplicant_config-test-supplicant-config.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_supplicant_config_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_supplicant_config-test-supplicant-config.obj `if test -f 'test-supplicant-config.c'; then $(CYGPATH_W) 'test-supplicant-config.c'; else $(CYGPATH_W) '$(srcdir)/test-supplicant-config.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +@WITH_TESTS_FALSE@check-local: +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-local +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am check-local clean \ + clean-generic clean-libtool clean-noinstPROGRAMS ctags \ + distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am + + +@WITH_TESTS_TRUE@check-local: test-supplicant-config +@WITH_TESTS_TRUE@ $(abs_builddir)/test-supplicant-config + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/supplicant-manager/tests/test-supplicant-config.c b/src/supplicant-manager/tests/test-supplicant-config.c new file mode 100644 index 00000000..d0868b5f --- /dev/null +++ b/src/supplicant-manager/tests/test-supplicant-config.c @@ -0,0 +1,518 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 - 2009 Red Hat, Inc. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <unistd.h> +#include <string.h> +#include <netinet/ether.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <dbus/dbus-glib.h> + +#include <nm-utils.h> +#include <nm-setting-connection.h> +#include <nm-setting-wired.h> +#include <nm-setting-wireless.h> +#include <nm-setting-wireless-security.h> +#include <nm-setting-ip4-config.h> +#include <nm-setting-8021x.h> + +#include "nm-test-helpers.h" + +#include "nm-supplicant-config.h" +#include "nm-supplicant-settings-verify.h" + +static gboolean +validate_opt (const char *detail, + GHashTable *hash, + const char *key, + OptType val_type, + gconstpointer expected, + size_t expected_len) +{ + GValue *value; + gint int_val; + GByteArray *array; + const char *s; + const unsigned char *expected_array = expected; + int result; + + ASSERT (hash != NULL, detail, "hash was NULL"); + + value = g_hash_table_lookup (hash, key); + ASSERT (value != NULL, + detail, "option '%s' expected but not found in config hash."); + + switch (val_type) { + case TYPE_INT: + ASSERT (G_VALUE_HOLDS_INT (value), + detail, "config hash item '%s' was not TYPE_INT.", key); + int_val = g_value_get_int (value); + ASSERT (int_val == GPOINTER_TO_INT (expected), + detail, "unexpected config hash item '%s' value %d (expected %d)", + key, int_val, GPOINTER_TO_INT (expected)); + break; + case TYPE_BYTES: + ASSERT (G_VALUE_HOLDS (value, DBUS_TYPE_G_UCHAR_ARRAY), + detail, "config hash item '%s' was not TYPE_BYTES.", key); + array = g_value_get_boxed (value); + ASSERT (array->len == expected_len, + detail, "unexpected config hash item '%s' length %d (expected %d)", + key, array->len, expected_len); + result = memcmp (array->data, expected_array, expected_len); + ASSERT (result == 0, detail, "unexpected config hash item '%s' value", key); + break; + case TYPE_KEYWORD: + case TYPE_STRING: + ASSERT (G_VALUE_HOLDS_STRING (value), + detail, "config hash item '%s' was not TYPE_STRING or TYPE_KEYWORD.", key); + if (expected_len == -1) + expected_len = strlen ((const char *) expected); + s = g_value_get_string (value); + ASSERT (s != NULL, detail, "unexpected NULL config hash string item '%s'.", key); + ASSERT (strlen (s) == expected_len, + detail, "unexpected config hash string item '%s' length %d (expected %d)", + key, strlen (s), expected_len); + result = strcmp (s, (const char *) expected); + ASSERT (result == 0, + detail, "unexpected config hash string item '%s' value '%s' (expected '%s')", + key, s, (const char *) expected); + break; + default: + g_warning ("unknown supplicant config hash item '%s' option type %d", + key, val_type); + return FALSE; + } + + return TRUE; +} + +static void +test_wifi_open (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWireless *s_wifi; + NMSettingIP4Config *s_ip4; + NMSupplicantConfig *config; + GHashTable *hash; + char *uuid; + gboolean success; + GError *error = NULL; + GByteArray *ssid; + const unsigned char ssid_data[] = { 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44 }; + GByteArray *bssid; + const unsigned char bssid_data[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }; + const char *bssid_str = "11:22:33:44:55:66"; + + connection = nm_connection_new (); + ASSERT (connection != NULL, + "wifi-open", "failed to allocate new connection"); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + ASSERT (s_con != NULL, + "wifi-open", "failed to allocate new %s setting", + NM_SETTING_CONNECTION_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Wifi Open", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + g_free (uuid); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + ASSERT (s_wifi != NULL, + "wifi-open", "failed to allocate new %s setting", + NM_SETTING_WIRELESS_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + + ssid = g_byte_array_sized_new (sizeof (ssid_data)); + g_byte_array_append (ssid, ssid_data, sizeof (ssid_data)); + bssid = g_byte_array_sized_new (sizeof (bssid_data)); + g_byte_array_append (bssid, bssid_data, sizeof (bssid_data)); + + g_object_set (s_wifi, + NM_SETTING_WIRELESS_SSID, ssid, + NM_SETTING_WIRELESS_BSSID, bssid, + NM_SETTING_WIRELESS_MODE, "infrastructure", + NM_SETTING_WIRELESS_BAND, "bg", + NULL); + + g_byte_array_free (ssid, TRUE); + g_byte_array_free (bssid, TRUE); + + /* IP4 setting */ + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + ASSERT (s_ip4 != NULL, + "wifi-open", "failed to allocate new %s setting", + NM_SETTING_IP4_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + ASSERT (nm_connection_verify (connection, &error) == TRUE, + "wifi-open", "failed to verify connection: %s", + (error && error->message) ? error->message : "(unknown)"); + + config = nm_supplicant_config_new (); + ASSERT (config != NULL, + "wifi-open", "failed to create new supplicant config"); + + success = nm_supplicant_config_add_setting_wireless (config, s_wifi, TRUE, 0, TRUE); + ASSERT (success == TRUE, + "wifi-open", "failed to add wireless setting to supplicant config."); + + success = nm_supplicant_config_add_no_security (config); + ASSERT (success == TRUE, + "wifi-open", "failed to add wireless security to supplicant config."); + + hash = nm_supplicant_config_get_hash (config); + ASSERT (hash != NULL, + "wifi-open", "failed to hash supplicant config options."); + + validate_opt ("wifi-open", hash, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1), -1); + validate_opt ("wifi-open", hash, "ssid", TYPE_BYTES, ssid_data, sizeof (ssid_data)); + validate_opt ("wifi-open", hash, "bssid", TYPE_KEYWORD, bssid_str, -1); + validate_opt ("wifi-open", hash, "key_mgmt", TYPE_KEYWORD, "NONE", -1); + + g_object_unref (connection); +} + +static void +test_wifi_wep_key (const char *detail, + NMWepKeyType wep_type, + const char *key_data, + const unsigned char *expected, + size_t expected_size) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + NMSettingIP4Config *s_ip4; + NMSupplicantConfig *config; + GHashTable *hash; + char *uuid; + gboolean success; + GError *error = NULL; + GByteArray *ssid; + const unsigned char ssid_data[] = { 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44 }; + GByteArray *bssid; + const unsigned char bssid_data[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }; + const char *bssid_str = "11:22:33:44:55:66"; + + connection = nm_connection_new (); + ASSERT (connection != NULL, + detail, "failed to allocate new connection"); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + ASSERT (s_con != NULL, + detail, "failed to allocate new %s setting", + NM_SETTING_CONNECTION_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Wifi WEP Key", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + g_free (uuid); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + ASSERT (s_wifi != NULL, + detail, "failed to allocate new %s setting", + NM_SETTING_WIRELESS_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + + ssid = g_byte_array_sized_new (sizeof (ssid_data)); + g_byte_array_append (ssid, ssid_data, sizeof (ssid_data)); + bssid = g_byte_array_sized_new (sizeof (bssid_data)); + g_byte_array_append (bssid, bssid_data, sizeof (bssid_data)); + + g_object_set (s_wifi, + NM_SETTING_WIRELESS_SSID, ssid, + NM_SETTING_WIRELESS_BSSID, bssid, + NM_SETTING_WIRELESS_MODE, "infrastructure", + NM_SETTING_WIRELESS_BAND, "bg", + NM_SETTING_WIRELESS_SEC, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NULL); + + g_byte_array_free (ssid, TRUE); + g_byte_array_free (bssid, TRUE); + + /* Wifi Security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + ASSERT (s_wsec != NULL, + detail, "failed to allocate new %s setting", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, wep_type, + NULL); + nm_setting_wireless_security_set_wep_key (s_wsec, 0, key_data); + + /* IP4 setting */ + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + ASSERT (s_ip4 != NULL, + detail, "failed to allocate new %s setting", + NM_SETTING_IP4_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + ASSERT (nm_connection_verify (connection, &error) == TRUE, + detail, "failed to verify connection: %s", + (error && error->message) ? error->message : "(unknown)"); + + config = nm_supplicant_config_new (); + ASSERT (config != NULL, + detail, "failed to create new supplicant config"); + + success = nm_supplicant_config_add_setting_wireless (config, s_wifi, TRUE, 0, TRUE); + ASSERT (success == TRUE, + detail, "failed to add wireless setting to supplicant config."); + + success = nm_supplicant_config_add_setting_wireless_security (config, + s_wsec, + NULL, + "376aced7-b28c-46be-9a62-fcdf072571da"); + ASSERT (success == TRUE, + detail, "failed to add wireless security to supplicant config."); + + hash = nm_supplicant_config_get_hash (config); + ASSERT (hash != NULL, + detail, "failed to hash supplicant config options."); + + validate_opt (detail, hash, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1), -1); + validate_opt (detail, hash, "ssid", TYPE_BYTES, ssid_data, sizeof (ssid_data)); + validate_opt (detail, hash, "bssid", TYPE_KEYWORD, bssid_str, -1); + validate_opt (detail, hash, "key_mgmt", TYPE_KEYWORD, "NONE", -1); + validate_opt (detail, hash, "wep_tx_keyidx", TYPE_INT, GINT_TO_POINTER (0), -1); + validate_opt (detail, hash, "wep_key0", TYPE_BYTES, expected, expected_size); + + g_object_unref (connection); +} + +static void +test_wifi_wep (void) +{ + const char *key1 = "12345"; + const unsigned char key1_expected[] = { 0x31, 0x32, 0x33, 0x34, 0x35 }; + const char *key2 = "ascii test$$$"; + const unsigned char key2_expected[] = { 0x61, 0x73, 0x63, 0x69, 0x69, 0x20, 0x74, 0x65, 0x73, 0x74, 0x24, 0x24, 0x24 }; + const char *key3 = "abcdef1234"; + const unsigned char key3_expected[] = { 0xab, 0xcd, 0xef, 0x12, 0x34 }; + const char *key4 = "96aec785c6392675f87f592972"; + const unsigned char key4_expected[] = { 0x96, 0xae, 0xc7, 0x85, 0xc6, 0x39, 0x26, 0x75, 0xf8, 0x7f, 0x59, 0x29, 0x72 }; + const char *key5 = "r34lly l33t w3p p4ssphr4s3 for t3st1ng"; + const unsigned char key5_expected[] = { 0xce, 0x68, 0x8b, 0x35, 0xf6, 0x0a, 0x2b, 0xbf, 0xc9, 0x8f, 0xed, 0x10, 0xda }; + + test_wifi_wep_key ("wifi-wep-ascii-40", NM_WEP_KEY_TYPE_KEY, key1, key1_expected, sizeof (key1_expected)); + test_wifi_wep_key ("wifi-wep-ascii-104", NM_WEP_KEY_TYPE_KEY, key2, key2_expected, sizeof (key2_expected)); + test_wifi_wep_key ("wifi-wep-hex-40", NM_WEP_KEY_TYPE_KEY, key3, key3_expected, sizeof (key3_expected)); + test_wifi_wep_key ("wifi-wep-hex-104", NM_WEP_KEY_TYPE_KEY, key4, key4_expected, sizeof (key4_expected)); + test_wifi_wep_key ("wifi-wep-passphrase-104", NM_WEP_KEY_TYPE_PASSPHRASE, key5, key5_expected, sizeof (key5_expected)); + + test_wifi_wep_key ("wifi-wep-old-hex-104", NM_WEP_KEY_TYPE_UNKNOWN, key4, key4_expected, sizeof (key4_expected)); +} + +static void +test_wifi_wpa_psk (const char *detail, + OptType key_type, + const char *key_data, + const unsigned char *expected, + size_t expected_size) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + NMSettingIP4Config *s_ip4; + NMSupplicantConfig *config; + GHashTable *hash; + char *uuid; + gboolean success; + GError *error = NULL; + GByteArray *ssid; + const unsigned char ssid_data[] = { 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44 }; + GByteArray *bssid; + const unsigned char bssid_data[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }; + const char *bssid_str = "11:22:33:44:55:66"; + + connection = nm_connection_new (); + ASSERT (connection != NULL, + detail, "failed to allocate new connection"); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + ASSERT (s_con != NULL, + detail, "failed to allocate new %s setting", + NM_SETTING_CONNECTION_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Wifi WEP Key", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + g_free (uuid); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + ASSERT (s_wifi != NULL, + detail, "failed to allocate new %s setting", + NM_SETTING_WIRELESS_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + + ssid = g_byte_array_sized_new (sizeof (ssid_data)); + g_byte_array_append (ssid, ssid_data, sizeof (ssid_data)); + bssid = g_byte_array_sized_new (sizeof (bssid_data)); + g_byte_array_append (bssid, bssid_data, sizeof (bssid_data)); + + g_object_set (s_wifi, + NM_SETTING_WIRELESS_SSID, ssid, + NM_SETTING_WIRELESS_BSSID, bssid, + NM_SETTING_WIRELESS_MODE, "infrastructure", + NM_SETTING_WIRELESS_BAND, "bg", + NM_SETTING_WIRELESS_SEC, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NULL); + + g_byte_array_free (ssid, TRUE); + g_byte_array_free (bssid, TRUE); + + /* Wifi Security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + ASSERT (s_wsec != NULL, + detail, "failed to allocate new %s setting", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", + NM_SETTING_WIRELESS_SECURITY_PSK, key_data, + NULL); + + nm_setting_wireless_security_add_proto (s_wsec, "wpa"); + nm_setting_wireless_security_add_proto (s_wsec, "rsn"); + nm_setting_wireless_security_add_pairwise (s_wsec, "tkip"); + nm_setting_wireless_security_add_pairwise (s_wsec, "ccmp"); + nm_setting_wireless_security_add_group (s_wsec, "tkip"); + nm_setting_wireless_security_add_group (s_wsec, "ccmp"); + + /* IP4 setting */ + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + ASSERT (s_ip4 != NULL, + detail, "failed to allocate new %s setting", + NM_SETTING_IP4_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + ASSERT (nm_connection_verify (connection, &error) == TRUE, + detail, "failed to verify connection: %s", + (error && error->message) ? error->message : "(unknown)"); + + config = nm_supplicant_config_new (); + ASSERT (config != NULL, + detail, "failed to create new supplicant config"); + + success = nm_supplicant_config_add_setting_wireless (config, s_wifi, TRUE, 0, TRUE); + ASSERT (success == TRUE, + detail, "failed to add wireless setting to supplicant config."); + + success = nm_supplicant_config_add_setting_wireless_security (config, + s_wsec, + NULL, + "376aced7-b28c-46be-9a62-fcdf072571da"); + ASSERT (success == TRUE, + detail, "failed to add wireless security to supplicant config."); + + hash = nm_supplicant_config_get_hash (config); + ASSERT (hash != NULL, + detail, "failed to hash supplicant config options."); + + validate_opt (detail, hash, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1), -1); + validate_opt (detail, hash, "ssid", TYPE_BYTES, ssid_data, sizeof (ssid_data)); + validate_opt (detail, hash, "bssid", TYPE_KEYWORD, bssid_str, -1); + validate_opt (detail, hash, "key_mgmt", TYPE_KEYWORD, "WPA-PSK", -1); + validate_opt (detail, hash, "proto", TYPE_KEYWORD, "WPA RSN", -1); + validate_opt (detail, hash, "pairwise", TYPE_KEYWORD, "TKIP CCMP", -1); + validate_opt (detail, hash, "group", TYPE_KEYWORD, "TKIP CCMP", -1); + validate_opt (detail, hash, "psk", key_type, expected, expected_size); + + g_object_unref (connection); +} + +static void +test_wifi_wpa_psk_types (void) +{ + const char *key1 = "d4721e911461d3cdef9793858e977fcda091779243abb7316c2f11605a160893"; + const unsigned char key1_expected[] = { 0xd4, 0x72, 0x1e, 0x91, 0x14, 0x61, 0xd3, 0xcd, + 0xef, 0x97, 0x93, 0x85, 0x8e, 0x97, 0x7f, 0xcd, + 0xa0, 0x91, 0x77, 0x92, 0x43, 0xab, 0xb7, 0x31, + 0x6c, 0x2f, 0x11, 0x60, 0x5a, 0x16, 0x08, 0x93 }; + const char *key2 = "r34lly l33t wp4 p4ssphr4s3 for t3st1ng"; + + test_wifi_wpa_psk ("wifi-wpa-psk-hex", TYPE_BYTES, key1, key1_expected, sizeof (key1_expected)); + test_wifi_wpa_psk ("wifi-wep-psk-passphrase", TYPE_STRING, key2, (gconstpointer) key2, strlen (key2)); +} + +int main (int argc, char **argv) +{ + GError *error = NULL; + DBusGConnection *bus; + char *base; + + g_type_init (); + bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); + + if (!nm_utils_init (&error)) + FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message); + + /* The tests */ + test_wifi_open (); + test_wifi_wep (); + test_wifi_wpa_psk_types (); + + base = g_path_get_basename (argv[0]); + fprintf (stdout, "%s: SUCCESS\n", base); + g_free (base); + return 0; +} + diff --git a/src/system-settings/Makefile.am b/src/system-settings/Makefile.am new file mode 100644 index 00000000..3b616b90 --- /dev/null +++ b/src/system-settings/Makefile.am @@ -0,0 +1,61 @@ +INCLUDES = -I${top_srcdir} \ + -I${top_srcdir}/include \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/libnm-glib \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/src \ + -I${top_builddir}/marshallers + +noinst_LTLIBRARIES = libsystem-settings.la + +BUILT_SOURCES = \ + nm-settings-system-glue.h + +libsystem_settings_la_SOURCES = \ + nm-sysconfig-settings.c \ + nm-sysconfig-settings.h \ + nm-inotify-helper.c \ + nm-inotify-helper.h \ + nm-polkit-helpers.h \ + nm-system-config-error.c \ + nm-system-config-error.h \ + nm-system-config-interface.c \ + nm-system-config-interface.h \ + nm-sysconfig-connection.c \ + nm-sysconfig-connection.h \ + nm-default-wired-connection.c \ + nm-default-wired-connection.h + +libsystem_settings_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(GMODULE_CFLAGS) \ + $(POLKIT_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DBINDIR=\"$(bindir)\" \ + -DSBINDIR=\"$(sbindir)\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DDATADIR=\"$(datadir)\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DLOCALSTATEDIR=\"$(localstatedir)\" \ + -DGNOMELOCALEDIR=\"$(datadir)/locale\" \ + -DPLUGINDIR=\"$(pkglibdir)\" + +libsystem_settings_la_LIBADD = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(top_builddir)/libnm-glib/libnm-glib.la \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) \ + $(GMODULE_LIBS) \ + $(POLKIT_LIBS) + +libsystem_settings_la_LDFLAGS = -rdynamic + +nm-settings-system-glue.h: $(top_srcdir)/introspection/nm-settings-system.xml + dbus-binding-tool --prefix=nm_settings_system --mode=glib-server --output=$@ $< + +CLEANFILES = \ + $(BUILT_SOURCES) + diff --git a/src/system-settings/Makefile.in b/src/system-settings/Makefile.in new file mode 100644 index 00000000..d86d4188 --- /dev/null +++ b/src/system-settings/Makefile.in @@ -0,0 +1,661 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/system-settings +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libsystem_settings_la_DEPENDENCIES = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(top_builddir)/libnm-glib/libnm-glib.la \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libsystem_settings_la_OBJECTS = \ + libsystem_settings_la-nm-sysconfig-settings.lo \ + libsystem_settings_la-nm-inotify-helper.lo \ + libsystem_settings_la-nm-system-config-error.lo \ + libsystem_settings_la-nm-system-config-interface.lo \ + libsystem_settings_la-nm-sysconfig-connection.lo \ + libsystem_settings_la-nm-default-wired-connection.lo +libsystem_settings_la_OBJECTS = $(am_libsystem_settings_la_OBJECTS) +libsystem_settings_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libsystem_settings_la_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libsystem_settings_la_SOURCES) +DIST_SOURCES = $(libsystem_settings_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +IPTABLES_PATH = @IPTABLES_PATH@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBM = @LIBM@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POFILES = @POFILES@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +RANLIB = @RANLIB@ +RESOLVCONF_PATH = @RESOLVCONF_PATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = -I${top_srcdir} \ + -I${top_srcdir}/include \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/libnm-glib \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/src \ + -I${top_builddir}/marshallers + +noinst_LTLIBRARIES = libsystem-settings.la +BUILT_SOURCES = \ + nm-settings-system-glue.h + +libsystem_settings_la_SOURCES = \ + nm-sysconfig-settings.c \ + nm-sysconfig-settings.h \ + nm-inotify-helper.c \ + nm-inotify-helper.h \ + nm-polkit-helpers.h \ + nm-system-config-error.c \ + nm-system-config-error.h \ + nm-system-config-interface.c \ + nm-system-config-interface.h \ + nm-sysconfig-connection.c \ + nm-sysconfig-connection.h \ + nm-default-wired-connection.c \ + nm-default-wired-connection.h + +libsystem_settings_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(GMODULE_CFLAGS) \ + $(POLKIT_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DBINDIR=\"$(bindir)\" \ + -DSBINDIR=\"$(sbindir)\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DDATADIR=\"$(datadir)\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DLOCALSTATEDIR=\"$(localstatedir)\" \ + -DGNOMELOCALEDIR=\"$(datadir)/locale\" \ + -DPLUGINDIR=\"$(pkglibdir)\" + +libsystem_settings_la_LIBADD = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(top_builddir)/libnm-glib/libnm-glib.la \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) \ + $(GMODULE_LIBS) \ + $(POLKIT_LIBS) + +libsystem_settings_la_LDFLAGS = -rdynamic +CLEANFILES = \ + $(BUILT_SOURCES) + +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/system-settings/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/system-settings/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libsystem-settings.la: $(libsystem_settings_la_OBJECTS) $(libsystem_settings_la_DEPENDENCIES) + $(libsystem_settings_la_LINK) $(libsystem_settings_la_OBJECTS) $(libsystem_settings_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystem_settings_la-nm-default-wired-connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystem_settings_la-nm-inotify-helper.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystem_settings_la-nm-sysconfig-connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystem_settings_la-nm-sysconfig-settings.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystem_settings_la-nm-system-config-error.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystem_settings_la-nm-system-config-interface.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libsystem_settings_la-nm-sysconfig-settings.lo: nm-sysconfig-settings.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystem_settings_la-nm-sysconfig-settings.lo -MD -MP -MF $(DEPDIR)/libsystem_settings_la-nm-sysconfig-settings.Tpo -c -o libsystem_settings_la-nm-sysconfig-settings.lo `test -f 'nm-sysconfig-settings.c' || echo '$(srcdir)/'`nm-sysconfig-settings.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsystem_settings_la-nm-sysconfig-settings.Tpo $(DEPDIR)/libsystem_settings_la-nm-sysconfig-settings.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-sysconfig-settings.c' object='libsystem_settings_la-nm-sysconfig-settings.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystem_settings_la-nm-sysconfig-settings.lo `test -f 'nm-sysconfig-settings.c' || echo '$(srcdir)/'`nm-sysconfig-settings.c + +libsystem_settings_la-nm-inotify-helper.lo: nm-inotify-helper.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystem_settings_la-nm-inotify-helper.lo -MD -MP -MF $(DEPDIR)/libsystem_settings_la-nm-inotify-helper.Tpo -c -o libsystem_settings_la-nm-inotify-helper.lo `test -f 'nm-inotify-helper.c' || echo '$(srcdir)/'`nm-inotify-helper.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsystem_settings_la-nm-inotify-helper.Tpo $(DEPDIR)/libsystem_settings_la-nm-inotify-helper.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-inotify-helper.c' object='libsystem_settings_la-nm-inotify-helper.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystem_settings_la-nm-inotify-helper.lo `test -f 'nm-inotify-helper.c' || echo '$(srcdir)/'`nm-inotify-helper.c + +libsystem_settings_la-nm-system-config-error.lo: nm-system-config-error.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystem_settings_la-nm-system-config-error.lo -MD -MP -MF $(DEPDIR)/libsystem_settings_la-nm-system-config-error.Tpo -c -o libsystem_settings_la-nm-system-config-error.lo `test -f 'nm-system-config-error.c' || echo '$(srcdir)/'`nm-system-config-error.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsystem_settings_la-nm-system-config-error.Tpo $(DEPDIR)/libsystem_settings_la-nm-system-config-error.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-system-config-error.c' object='libsystem_settings_la-nm-system-config-error.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystem_settings_la-nm-system-config-error.lo `test -f 'nm-system-config-error.c' || echo '$(srcdir)/'`nm-system-config-error.c + +libsystem_settings_la-nm-system-config-interface.lo: nm-system-config-interface.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystem_settings_la-nm-system-config-interface.lo -MD -MP -MF $(DEPDIR)/libsystem_settings_la-nm-system-config-interface.Tpo -c -o libsystem_settings_la-nm-system-config-interface.lo `test -f 'nm-system-config-interface.c' || echo '$(srcdir)/'`nm-system-config-interface.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsystem_settings_la-nm-system-config-interface.Tpo $(DEPDIR)/libsystem_settings_la-nm-system-config-interface.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-system-config-interface.c' object='libsystem_settings_la-nm-system-config-interface.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystem_settings_la-nm-system-config-interface.lo `test -f 'nm-system-config-interface.c' || echo '$(srcdir)/'`nm-system-config-interface.c + +libsystem_settings_la-nm-sysconfig-connection.lo: nm-sysconfig-connection.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystem_settings_la-nm-sysconfig-connection.lo -MD -MP -MF $(DEPDIR)/libsystem_settings_la-nm-sysconfig-connection.Tpo -c -o libsystem_settings_la-nm-sysconfig-connection.lo `test -f 'nm-sysconfig-connection.c' || echo '$(srcdir)/'`nm-sysconfig-connection.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsystem_settings_la-nm-sysconfig-connection.Tpo $(DEPDIR)/libsystem_settings_la-nm-sysconfig-connection.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-sysconfig-connection.c' object='libsystem_settings_la-nm-sysconfig-connection.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystem_settings_la-nm-sysconfig-connection.lo `test -f 'nm-sysconfig-connection.c' || echo '$(srcdir)/'`nm-sysconfig-connection.c + +libsystem_settings_la-nm-default-wired-connection.lo: nm-default-wired-connection.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystem_settings_la-nm-default-wired-connection.lo -MD -MP -MF $(DEPDIR)/libsystem_settings_la-nm-default-wired-connection.Tpo -c -o libsystem_settings_la-nm-default-wired-connection.lo `test -f 'nm-default-wired-connection.c' || echo '$(srcdir)/'`nm-default-wired-connection.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsystem_settings_la-nm-default-wired-connection.Tpo $(DEPDIR)/libsystem_settings_la-nm-default-wired-connection.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-default-wired-connection.c' object='libsystem_settings_la-nm-default-wired-connection.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystem_settings_la-nm-default-wired-connection.lo `test -f 'nm-default-wired-connection.c' || echo '$(srcdir)/'`nm-default-wired-connection.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: all check install install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +nm-settings-system-glue.h: $(top_srcdir)/introspection/nm-settings-system.xml + dbus-binding-tool --prefix=nm_settings_system --mode=glib-server --output=$@ $< + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/system-settings/nm-default-wired-connection.c b/src/system-settings/nm-default-wired-connection.c new file mode 100644 index 00000000..54e00d62 --- /dev/null +++ b/src/system-settings/nm-default-wired-connection.c @@ -0,0 +1,308 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2008 Novell, Inc. + * (C) Copyright 2009 Red Hat, Inc. + */ + +#include <netinet/ether.h> + +#include <glib/gi18n.h> + +#include <NetworkManager.h> +#include <nm-setting-connection.h> +#include <nm-setting-wired.h> +#include <nm-utils.h> + +#include "nm-dbus-glib-types.h" +#include "nm-marshal.h" +#include "nm-default-wired-connection.h" +#include "nm-settings-connection-interface.h" + +static NMSettingsConnectionInterface *parent_settings_connection_iface; + +static void settings_connection_interface_init (NMSettingsConnectionInterface *iface); + +G_DEFINE_TYPE_EXTENDED (NMDefaultWiredConnection, nm_default_wired_connection, NM_TYPE_SYSCONFIG_CONNECTION, 0, + G_IMPLEMENT_INTERFACE (NM_TYPE_SETTINGS_CONNECTION_INTERFACE, + settings_connection_interface_init)) + +#define NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnectionPrivate)) + +typedef struct { + NMDevice *device; + GByteArray *mac; + gboolean read_only; +} NMDefaultWiredConnectionPrivate; + +enum { + PROP_0, + PROP_MAC, + PROP_DEVICE, + PROP_READ_ONLY, + LAST_PROP +}; + +enum { + TRY_UPDATE, + DELETED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + + +NMDefaultWiredConnection * +nm_default_wired_connection_new (const GByteArray *mac, + NMDevice *device, + gboolean read_only) +{ + + g_return_val_if_fail (mac != NULL, NULL); + g_return_val_if_fail (mac->len == ETH_ALEN, NULL); + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + + return g_object_new (NM_TYPE_DEFAULT_WIRED_CONNECTION, + NM_DEFAULT_WIRED_CONNECTION_MAC, mac, + NM_DEFAULT_WIRED_CONNECTION_DEVICE, device, + NM_DEFAULT_WIRED_CONNECTION_READ_ONLY, read_only, + NULL); +} + +NMDevice * +nm_default_wired_connection_get_device (NMDefaultWiredConnection *wired) +{ + g_return_val_if_fail (NM_IS_DEFAULT_WIRED_CONNECTION (wired), NULL); + + return NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (wired)->device; +} + +static gboolean +update (NMSettingsConnectionInterface *connection, + NMSettingsConnectionInterfaceUpdateFunc callback, + gpointer user_data) +{ + NMDefaultWiredConnection *self = NM_DEFAULT_WIRED_CONNECTION (connection); + + /* Keep the object alive over try-update since it might get removed + * from the settings service there, but we still need it for the callback. + */ + g_object_ref (connection); + g_signal_emit (self, signals[TRY_UPDATE], 0); + callback (connection, NULL, user_data); + g_object_unref (connection); + return TRUE; +} + +static gboolean +do_delete (NMSettingsConnectionInterface *connection, + NMSettingsConnectionInterfaceDeleteFunc callback, + gpointer user_data) +{ + NMDefaultWiredConnection *self = NM_DEFAULT_WIRED_CONNECTION (connection); + NMDefaultWiredConnectionPrivate *priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (connection); + + g_signal_emit (self, signals[DELETED], 0, priv->mac); + return parent_settings_connection_iface->delete (connection, callback, user_data); +} + +/****************************************************************/ + +static void +settings_connection_interface_init (NMSettingsConnectionInterface *iface) +{ + parent_settings_connection_iface = g_type_interface_peek_parent (iface); + iface->update = update; + iface->delete = do_delete; +} + +static void +nm_default_wired_connection_init (NMDefaultWiredConnection *self) +{ +} + +static GObject * +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + NMDefaultWiredConnectionPrivate *priv; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + char *id, *uuid; + + object = G_OBJECT_CLASS (nm_default_wired_connection_parent_class)->constructor (type, n_construct_params, construct_params); + if (!object) + return NULL; + + priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (object); + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + + id = g_strdup_printf (_("Auto %s"), nm_device_get_iface (priv->device)); + uuid = nm_utils_uuid_generate (); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, id, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_READ_ONLY, priv->read_only, + NULL); + + g_free (id); + g_free (uuid); + + nm_connection_add_setting (NM_CONNECTION (object), NM_SETTING (s_con)); + + /* Lock the connection to the specific device */ + s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, priv->mac, NULL); + nm_connection_add_setting (NM_CONNECTION (object), NM_SETTING (s_wired)); + + return object; +} + +static void +finalize (GObject *object) +{ + NMDefaultWiredConnectionPrivate *priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (object); + + g_object_unref (priv->device); + g_byte_array_free (priv->mac, TRUE); + + G_OBJECT_CLASS (nm_default_wired_connection_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDefaultWiredConnectionPrivate *priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MAC: + g_value_set_pointer (value, priv->mac); + break; + case PROP_DEVICE: + g_value_set_object (value, priv->device); + break; + case PROP_READ_ONLY: + g_value_set_boolean (value, priv->read_only); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMDefaultWiredConnectionPrivate *priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (object); + GByteArray *array; + + switch (prop_id) { + case PROP_MAC: + /* Construct only */ + array = g_value_get_pointer (value); + if (priv->mac) { + g_byte_array_free (priv->mac, TRUE); + priv->mac = NULL; + } + if (array) { + g_return_if_fail (array->len == ETH_ALEN); + priv->mac = g_byte_array_sized_new (array->len); + g_byte_array_append (priv->mac, array->data, ETH_ALEN); + } + break; + case PROP_DEVICE: + if (priv->device) + g_object_unref (priv->device); + priv->device = g_value_dup_object (value); + break; + case PROP_READ_ONLY: + priv->read_only = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_default_wired_connection_class_init (NMDefaultWiredConnectionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NMDefaultWiredConnectionPrivate)); + + /* Virtual methods */ + object_class->constructor = constructor; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_MAC, + g_param_spec_pointer (NM_DEFAULT_WIRED_CONNECTION_MAC, + "MAC", + "MAC Address", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_DEVICE, + g_param_spec_object (NM_DEFAULT_WIRED_CONNECTION_DEVICE, + "Device", + "Device", + NM_TYPE_DEVICE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_READ_ONLY, + g_param_spec_boolean (NM_DEFAULT_WIRED_CONNECTION_READ_ONLY, + "ReadOnly", + "Read Only", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /* Signals */ + signals[TRY_UPDATE] = + g_signal_new ("try-update", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /* The 'deleted' signal is used to signal intentional deletions (like + * updating or user-requested deletion) rather than using the + * superclass' 'removed' signal, since that signal doesn't have the + * semantics we want; it gets emitted as a side-effect of various operations + * and is meant more for D-Bus clients instead of in-service uses. + */ + signals[DELETED] = + g_signal_new ("deleted", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); +} diff --git a/src/system-settings/nm-default-wired-connection.h b/src/system-settings/nm-default-wired-connection.h new file mode 100644 index 00000000..de89b675 --- /dev/null +++ b/src/system-settings/nm-default-wired-connection.h @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2008 Novell, Inc. + * (C) Copyright 2009 Red Hat, Inc. + */ + +#ifndef NM_DEFAULT_WIRED_CONNECTION_H +#define NM_DEFAULT_WIRED_CONNECTION_H + +#include "nm-sysconfig-connection.h" +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEFAULT_WIRED_CONNECTION (nm_default_wired_connection_get_type ()) +#define NM_DEFAULT_WIRED_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnection)) +#define NM_DEFAULT_WIRED_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnectionClass)) +#define NM_IS_DEFAULT_WIRED_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION)) +#define NM_IS_DEFAULT_WIRED_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION)) +#define NM_DEFAULT_WIRED_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnectionClass)) + +#define NM_DEFAULT_WIRED_CONNECTION_MAC "mac" +#define NM_DEFAULT_WIRED_CONNECTION_DEVICE "device" +#define NM_DEFAULT_WIRED_CONNECTION_READ_ONLY "read-only" + +typedef struct { + NMSysconfigConnection parent; +} NMDefaultWiredConnection; + +typedef struct { + NMSysconfigConnectionClass parent; +} NMDefaultWiredConnectionClass; + +GType nm_default_wired_connection_get_type (void); + +NMDefaultWiredConnection *nm_default_wired_connection_new (const GByteArray *mac, + NMDevice *device, + gboolean read_only); + +NMDevice *nm_default_wired_connection_get_device (NMDefaultWiredConnection *wired); + +G_END_DECLS + +#endif /* NM_DEFAULT_WIRED_CONNECTION_H */ diff --git a/src/system-settings/nm-inotify-helper.c b/src/system-settings/nm-inotify-helper.c new file mode 100644 index 00000000..0ee168e5 --- /dev/null +++ b/src/system-settings/nm-inotify-helper.c @@ -0,0 +1,212 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2008 Red Hat, Inc. + */ + +#include <unistd.h> +#include <string.h> +#include <sys/inotify.h> +#include <glib.h> + +#include "nm-marshal.h" +#include "nm-inotify-helper.h" +#include "nm-logging.h" + +G_DEFINE_TYPE (NMInotifyHelper, nm_inotify_helper, G_TYPE_OBJECT) + +#define NM_INOTIFY_HELPER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_INOTIFY_HELPER, NMInotifyHelperPrivate)) + +typedef struct { + int ifd; + + GHashTable *wd_refs; +} NMInotifyHelperPrivate; + +/* Signals */ +enum { + EVENT, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +int +nm_inotify_helper_add_watch (NMInotifyHelper *self, const char *path) +{ + NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self); + int wd; + guint32 refcount; + + g_return_val_if_fail (priv->ifd >= 0, -1); + + /* We only care about modifications since we're just trying to get change + * notifications on hardlinks. + */ + + wd = inotify_add_watch (priv->ifd, path, IN_CLOSE_WRITE); + if (wd < 0) + return -1; + + refcount = GPOINTER_TO_UINT (g_hash_table_lookup (priv->wd_refs, GINT_TO_POINTER (wd))); + refcount++; + g_hash_table_replace (priv->wd_refs, GINT_TO_POINTER (wd), GUINT_TO_POINTER (refcount)); + + return wd; +} + +void +nm_inotify_helper_remove_watch (NMInotifyHelper *self, int wd) +{ + NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self); + guint32 refcount; + + g_return_if_fail (priv->ifd >= 0); + + refcount = GPOINTER_TO_UINT (g_hash_table_lookup (priv->wd_refs, GINT_TO_POINTER (wd))); + if (!refcount) + return; + + refcount--; + if (!refcount) { + g_hash_table_remove (priv->wd_refs, GINT_TO_POINTER (wd)); + inotify_rm_watch (priv->ifd, wd); + } else + g_hash_table_replace (priv->wd_refs, GINT_TO_POINTER (wd), GUINT_TO_POINTER (refcount)); +} + +static gboolean +inotify_event_handler (GIOChannel *channel, GIOCondition cond, gpointer user_data) +{ + NMInotifyHelper *self = NM_INOTIFY_HELPER (user_data); + struct inotify_event evt; + + /* read the notifications from the watch descriptor */ + while (g_io_channel_read_chars (channel, (gchar *) &evt, sizeof (struct inotify_event), NULL, NULL) == G_IO_STATUS_NORMAL) { + gchar filename[PATH_MAX + 1]; + + filename[0] = '\0'; + if (evt.len > 0) { + g_io_channel_read_chars (channel, + filename, + evt.len > PATH_MAX ? PATH_MAX : evt.len, + NULL, NULL); + } + + if (!(evt.mask & IN_IGNORED)) + g_signal_emit (self, signals[EVENT], 0, &evt, &filename[0]); + } + + return TRUE; +} + +static gboolean +init_inotify (NMInotifyHelper *self) +{ + NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self); + GIOChannel *channel; + guint source_id; + + priv->ifd = inotify_init (); + if (priv->ifd == -1) { + nm_log_warn (LOGD_SYS_SET, "couldn't initialize inotify"); + return FALSE; + } + + /* Watch the inotify descriptor for file/directory change events */ + channel = g_io_channel_unix_new (priv->ifd); + if (!channel) { + nm_log_warn (LOGD_SYS_SET, "couldn't create new GIOChannel"); + close (priv->ifd); + priv->ifd = -1; + return FALSE; + } + + g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL); + g_io_channel_set_encoding (channel, NULL, NULL); + + source_id = g_io_add_watch (channel, + G_IO_IN | G_IO_ERR, + (GIOFunc) inotify_event_handler, + (gpointer) self); + g_io_channel_unref (channel); + return TRUE; +} + +NMInotifyHelper * +nm_inotify_helper_get (void) +{ + static NMInotifyHelper *singleton = NULL; + + if (!singleton) { + singleton = (NMInotifyHelper *) g_object_new (NM_TYPE_INOTIFY_HELPER, NULL); + if (!singleton) + return NULL; + + if (!init_inotify (singleton)) { + g_object_unref (singleton); + return NULL; + } + } else + g_object_ref (singleton); + + g_assert (singleton); + return singleton; +} + +static void +nm_inotify_helper_init (NMInotifyHelper *self) +{ + NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self); + + priv->wd_refs = g_hash_table_new (g_direct_hash, g_direct_equal); +} + +static void +finalize (GObject *object) +{ + NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (object); + + if (priv->ifd >= 0) + close (priv->ifd); + + g_hash_table_destroy (priv->wd_refs); + + G_OBJECT_CLASS (nm_inotify_helper_parent_class)->finalize (object); +} + +static void +nm_inotify_helper_class_init (NMInotifyHelperClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NMInotifyHelperPrivate)); + + /* Virtual methods */ + object_class->finalize = finalize; + + /* Signals */ + signals[EVENT] = + g_signal_new ("event", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMInotifyHelperClass, event), + NULL, NULL, + _nm_marshal_VOID__POINTER_STRING, + G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_STRING); +} + diff --git a/src/system-settings/nm-inotify-helper.h b/src/system-settings/nm-inotify-helper.h new file mode 100644 index 00000000..10d43ac9 --- /dev/null +++ b/src/system-settings/nm-inotify-helper.h @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2008 Red Hat, Inc. + */ + +#ifndef __INOTIFY_HELPER_H__ +#define __INOTIFY_HELPER_H__ + +#include <glib.h> +#include <glib-object.h> +#include <sys/inotify.h> + +#define NM_TYPE_INOTIFY_HELPER (nm_inotify_helper_get_type ()) +#define NM_INOTIFY_HELPER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_INOTIFY_HELPER, NMInotifyHelper)) +#define NM_INOTIFY_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_INOTIFY_HELPER, NMInotifyHelperClass)) +#define NM_IS_INOTIFY_HELPER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_INOTIFY_HELPER)) +#define NM_IS_INOTIFY_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_INOTIFY_HELPER)) +#define NM_INOTIFY_HELPER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_INOTIFY_HELPER, NMInotifyHelperClass)) + +typedef struct { + GObject parent; +} NMInotifyHelper; + +typedef struct { + GObjectClass parent; + + /* signals */ + void (* event) (NMInotifyHelper *helper, struct inotify_event *evt, const char *filename); +} NMInotifyHelperClass; + +GType nm_inotify_helper_get_type (void); + +NMInotifyHelper * nm_inotify_helper_get (void); + +int nm_inotify_helper_add_watch (NMInotifyHelper *helper, const char *path); + +void nm_inotify_helper_remove_watch (NMInotifyHelper *helper, int wd); + +#endif /* __INOTIFY_HELPER_H__ */ diff --git a/src/system-settings/nm-polkit-helpers.h b/src/system-settings/nm-polkit-helpers.h new file mode 100644 index 00000000..c26fcc2c --- /dev/null +++ b/src/system-settings/nm-polkit-helpers.h @@ -0,0 +1,32 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2008 Novell, Inc. + * (C) Copyright 2008 Red Hat, Inc. + */ + +#ifndef NM_POLKIT_HELPERS_H +#define NM_POLKIT_HELPERS_H + +#include <polkit/polkit.h> + +#define NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY "org.freedesktop.network-manager-settings.system.modify" +#define NM_SYSCONFIG_POLICY_ACTION_WIFI_SHARE_PROTECTED "org.freedesktop.network-manager-settings.system.wifi.share.protected" +#define NM_SYSCONFIG_POLICY_ACTION_WIFI_SHARE_OPEN "org.freedesktop.network-manager-settings.system.wifi.share.open" +#define NM_SYSCONFIG_POLICY_ACTION_HOSTNAME_MODIFY "org.freedesktop.network-manager-settings.system.hostname.modify" + +#endif /* NM_POLKIT_HELPERS_H */ diff --git a/src/system-settings/nm-sysconfig-connection.c b/src/system-settings/nm-sysconfig-connection.c new file mode 100644 index 00000000..f46895ac --- /dev/null +++ b/src/system-settings/nm-sysconfig-connection.c @@ -0,0 +1,658 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2008 Novell, Inc. + * (C) Copyright 2008 - 2009 Red Hat, Inc. + */ + +#include <NetworkManager.h> +#include <dbus/dbus-glib-lowlevel.h> + +#include "nm-sysconfig-connection.h" +#include "nm-system-config-error.h" +#include "nm-dbus-glib-types.h" +#include "nm-settings-connection-interface.h" +#include "nm-settings-interface.h" +#include "nm-polkit-helpers.h" +#include "nm-logging.h" + + +static void settings_connection_interface_init (NMSettingsConnectionInterface *klass); + +G_DEFINE_TYPE_EXTENDED (NMSysconfigConnection, nm_sysconfig_connection, NM_TYPE_EXPORTED_CONNECTION, 0, + G_IMPLEMENT_INTERFACE (NM_TYPE_SETTINGS_CONNECTION_INTERFACE, + settings_connection_interface_init)) + +#define NM_SYSCONFIG_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + NM_TYPE_SYSCONFIG_CONNECTION, \ + NMSysconfigConnectionPrivate)) + +typedef struct { + PolkitAuthority *authority; + GSList *pk_calls; + NMConnection *secrets; +} NMSysconfigConnectionPrivate; + +/**************************************************************/ + +static void +ignore_cb (NMSettingsConnectionInterface *connection, + GError *error, + gpointer user_data) +{ +} + +gboolean +nm_sysconfig_connection_update (NMSysconfigConnection *self, + NMConnection *new, + gboolean signal_update, + GError **error) +{ + NMSysconfigConnectionPrivate *priv; + GHashTable *new_settings; + gboolean success = FALSE; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_SYSCONFIG_CONNECTION (self), FALSE); + g_return_val_if_fail (new != NULL, FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (new), FALSE); + + priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self); + + /* Do nothing if there's nothing to update */ + if (nm_connection_compare (NM_CONNECTION (self), + NM_CONNECTION (new), + NM_SETTING_COMPARE_FLAG_EXACT)) + return TRUE; + + new_settings = nm_connection_to_hash (new); + g_assert (new_settings); + if (nm_connection_replace_settings (NM_CONNECTION (self), new_settings, error)) { + /* Copy the connection to keep its secrets around even if NM + * calls nm_connection_clear_secrets(). + */ + if (priv->secrets) + g_object_unref (priv->secrets); + priv->secrets = nm_connection_duplicate (NM_CONNECTION (self)); + + if (signal_update) { + nm_settings_connection_interface_update (NM_SETTINGS_CONNECTION_INTERFACE (self), + ignore_cb, + NULL); + } + success = TRUE; + } + g_hash_table_destroy (new_settings); + return success; +} + +/**************************************************************/ + +static GValue * +string_to_gvalue (const char *str) +{ + GValue *val = g_slice_new0 (GValue); + + g_value_init (val, G_TYPE_STRING); + g_value_set_string (val, str); + return val; +} + +static GValue * +byte_array_to_gvalue (const GByteArray *array) +{ + GValue *val; + + val = g_slice_new0 (GValue); + g_value_init (val, DBUS_TYPE_G_UCHAR_ARRAY); + g_value_set_boxed (val, array); + + return val; +} + +static void +copy_one_secret (gpointer key, gpointer value, gpointer user_data) +{ + const char *value_str = (const char *) value; + + if (value_str) { + g_hash_table_insert ((GHashTable *) user_data, + g_strdup ((char *) key), + string_to_gvalue (value_str)); + } +} + +static void +add_secrets (NMSetting *setting, + const char *key, + const GValue *value, + GParamFlags flags, + gpointer user_data) +{ + GHashTable *secrets = user_data; + + if (!(flags & NM_SETTING_PARAM_SECRET)) + return; + + /* Copy secrets into the returned hash table */ + if (G_VALUE_HOLDS_STRING (value)) { + const char *tmp; + + tmp = g_value_get_string (value); + if (tmp) + g_hash_table_insert (secrets, g_strdup (key), string_to_gvalue (tmp)); + } else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_MAP_OF_STRING)) { + /* Flatten the string hash by pulling its keys/values out */ + g_hash_table_foreach (g_value_get_boxed (value), copy_one_secret, secrets); + } else if (G_VALUE_TYPE (value) == DBUS_TYPE_G_UCHAR_ARRAY) { + GByteArray *array; + + array = g_value_get_boxed (value); + if (array) + g_hash_table_insert (secrets, g_strdup (key), byte_array_to_gvalue (array)); + } +} + +static void +destroy_gvalue (gpointer data) +{ + GValue *value = (GValue *) data; + + g_value_unset (value); + g_slice_free (GValue, value); +} + +static gboolean +get_secrets (NMSettingsConnectionInterface *connection, + const char *setting_name, + const char **hints, + gboolean request_new, + NMSettingsConnectionInterfaceGetSecretsFunc callback, + gpointer user_data) +{ + NMSysconfigConnection *self = NM_SYSCONFIG_CONNECTION (connection); + NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self); + GHashTable *settings = NULL; + GHashTable *secrets = NULL; + NMSetting *setting; + GError *error = NULL; + + /* Use priv->secrets to work around the fact that nm_connection_clear_secrets() + * will clear secrets on this object's settings. priv->secrets should be + * a complete copy of this object and kept in sync by + * nm_sysconfig_connection_update(). + */ + if (!priv->secrets) { + error = g_error_new (NM_SETTINGS_INTERFACE_ERROR, + NM_SETTINGS_INTERFACE_ERROR_INVALID_CONNECTION, + "%s.%d - Internal error; secrets cache invalid.", + __FILE__, __LINE__); + (*callback) (connection, NULL, error, user_data); + g_error_free (error); + return TRUE; + } + + setting = nm_connection_get_setting_by_name (priv->secrets, setting_name); + if (!setting) { + error = g_error_new (NM_SETTINGS_INTERFACE_ERROR, + NM_SETTINGS_INTERFACE_ERROR_INVALID_SETTING, + "%s.%d - Connection didn't have requested setting '%s'.", + __FILE__, __LINE__, setting_name); + (*callback) (connection, NULL, error, user_data); + g_error_free (error); + return TRUE; + } + + /* Returned secrets are a{sa{sv}}; this is the outer a{s...} hash that + * will contain all the individual settings hashes. + */ + settings = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) g_hash_table_destroy); + + /* Add the secrets from this setting to the inner secrets hash for this setting */ + secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_gvalue); + nm_setting_enumerate_values (setting, add_secrets, secrets); + + g_hash_table_insert (settings, g_strdup (setting_name), secrets); + callback (connection, settings, NULL, user_data); + g_hash_table_destroy (settings); + return TRUE; +} + +/**************************************************************/ + +typedef struct { + NMSysconfigConnection *self; + DBusGMethodInvocation *context; + PolkitSubject *subject; + GCancellable *cancellable; + gboolean disposed; + + /* Update */ + NMConnection *connection; + + /* Secrets */ + char *setting_name; + char **hints; + gboolean request_new; +} PolkitCall; + +static PolkitCall * +polkit_call_new (NMSysconfigConnection *self, + DBusGMethodInvocation *context, + NMConnection *connection, + const char *setting_name, + const char **hints, + gboolean request_new) +{ + PolkitCall *call; + char *sender; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (context != NULL, NULL); + + call = g_malloc0 (sizeof (PolkitCall)); + call->self = self; + call->context = context; + call->cancellable = g_cancellable_new (); + call->connection = connection; + call->setting_name = g_strdup (setting_name); + if (hints) + call->hints = g_strdupv ((char **) hints); + call->request_new = request_new; + + sender = dbus_g_method_get_sender (context); + call->subject = polkit_system_bus_name_new (sender); + g_free (sender); + + return call; +} + +static void +polkit_call_free (PolkitCall *call) +{ + if (call->connection) + g_object_unref (call->connection); + g_free (call->setting_name); + if (call->hints) + g_strfreev (call->hints); + + g_object_unref (call->subject); + g_object_unref (call->cancellable); + g_free (call); +} + +static void +con_update_cb (NMSettingsConnectionInterface *connection, + GError *error, + gpointer user_data) +{ + PolkitCall *call = user_data; + + if (error) + dbus_g_method_return_error (call->context, error); + else + dbus_g_method_return (call->context); + + polkit_call_free (call); +} + +static void +pk_update_cb (GObject *object, GAsyncResult *result, gpointer user_data) +{ + PolkitCall *call = user_data; + NMSysconfigConnection *self = call->self; + NMSysconfigConnectionPrivate *priv; + PolkitAuthorizationResult *pk_result; + GError *error = NULL; + + /* If our NMSysconfigConnection is already gone, do nothing */ + if (call->disposed) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_GENERAL, + "Request was canceled."); + dbus_g_method_return_error (call->context, error); + g_error_free (error); + polkit_call_free (call); + return; + } + + priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self); + + priv->pk_calls = g_slist_remove (priv->pk_calls, call); + + pk_result = polkit_authority_check_authorization_finish (priv->authority, + result, + &error); + /* Some random error happened */ + if (error) { + dbus_g_method_return_error (call->context, error); + g_error_free (error); + polkit_call_free (call); + return; + } + + /* Caller didn't successfully authenticate */ + if (!polkit_authorization_result_get_is_authorized (pk_result)) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED, + "Insufficient privileges."); + dbus_g_method_return_error (call->context, error); + g_error_free (error); + polkit_call_free (call); + goto out; + } + + /* Update our settings internally so the update() call will save the new + * ones. We don't let nm_sysconfig_connection_update() handle the update + * signal since we need our own callback after the update is done. + */ + if (!nm_sysconfig_connection_update (self, call->connection, FALSE, &error)) { + /* Shouldn't really happen since we've already validated the settings */ + dbus_g_method_return_error (call->context, error); + g_error_free (error); + polkit_call_free (call); + goto out; + } + + /* Caller is authenticated, now we can finally try to commit the update */ + nm_settings_connection_interface_update (NM_SETTINGS_CONNECTION_INTERFACE (self), + con_update_cb, + call); + +out: + g_object_unref (pk_result); +} + +static void +dbus_update (NMExportedConnection *exported, + GHashTable *new_settings, + DBusGMethodInvocation *context) +{ + NMSysconfigConnection *self = NM_SYSCONFIG_CONNECTION (exported); + NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self); + PolkitCall *call; + NMConnection *tmp; + GError *error = NULL; + + /* Check if the settings are valid first */ + tmp = nm_connection_new_from_hash (new_settings, &error); + if (!tmp) { + g_assert (error); + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + call = polkit_call_new (self, context, tmp, NULL, NULL, FALSE); + g_assert (call); + polkit_authority_check_authorization (priv->authority, + call->subject, + NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY, + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + call->cancellable, + pk_update_cb, + call); + priv->pk_calls = g_slist_prepend (priv->pk_calls, call); +} + +static void +con_delete_cb (NMSettingsConnectionInterface *connection, + GError *error, + gpointer user_data) +{ + PolkitCall *call = user_data; + + if (error) + dbus_g_method_return_error (call->context, error); + else + dbus_g_method_return (call->context); + + polkit_call_free (call); +} + +static void +pk_delete_cb (GObject *object, GAsyncResult *result, gpointer user_data) +{ + PolkitCall *call = user_data; + NMSysconfigConnection *self = call->self; + NMSysconfigConnectionPrivate *priv; + PolkitAuthorizationResult *pk_result; + GError *error = NULL; + + /* If our NMSysconfigConnection is already gone, do nothing */ + if (call->disposed) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_GENERAL, + "Request was canceled."); + dbus_g_method_return_error (call->context, error); + g_error_free (error); + polkit_call_free (call); + return; + } + + priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self); + + priv->pk_calls = g_slist_remove (priv->pk_calls, call); + + pk_result = polkit_authority_check_authorization_finish (priv->authority, + result, + &error); + /* Some random error happened */ + if (error) { + dbus_g_method_return_error (call->context, error); + g_error_free (error); + polkit_call_free (call); + return; + } + + /* Caller didn't successfully authenticate */ + if (!polkit_authorization_result_get_is_authorized (pk_result)) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED, + "Insufficient privileges."); + dbus_g_method_return_error (call->context, error); + g_error_free (error); + polkit_call_free (call); + goto out; + } + + /* Caller is authenticated, now we can finally try to delete */ + nm_settings_connection_interface_delete (NM_SETTINGS_CONNECTION_INTERFACE (self), + con_delete_cb, + call); + +out: + g_object_unref (pk_result); +} + +static void +dbus_delete (NMExportedConnection *exported, + DBusGMethodInvocation *context) +{ + NMSysconfigConnection *self = NM_SYSCONFIG_CONNECTION (exported); + NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self); + PolkitCall *call; + + call = polkit_call_new (self, context, NULL, NULL, NULL, FALSE); + g_assert (call); + polkit_authority_check_authorization (priv->authority, + call->subject, + NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY, + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + call->cancellable, + pk_delete_cb, + call); + priv->pk_calls = g_slist_prepend (priv->pk_calls, call); +} + +static void +con_secrets_cb (NMSettingsConnectionInterface *connection, + GHashTable *secrets, + GError *error, + gpointer user_data) +{ + PolkitCall *call = user_data; + + if (error) + dbus_g_method_return_error (call->context, error); + else + dbus_g_method_return (call->context, secrets); + + polkit_call_free (call); +} + +static void +pk_secrets_cb (GObject *object, GAsyncResult *result, gpointer user_data) +{ + PolkitCall *call = user_data; + NMSysconfigConnection *self = call->self; + NMSysconfigConnectionPrivate *priv; + PolkitAuthorizationResult *pk_result; + GError *error = NULL; + + /* If our NMSysconfigConnection is already gone, do nothing */ + if (call->disposed) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_GENERAL, + "Request was canceled."); + dbus_g_method_return_error (call->context, error); + g_error_free (error); + polkit_call_free (call); + return; + } + + priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self); + + priv->pk_calls = g_slist_remove (priv->pk_calls, call); + + pk_result = polkit_authority_check_authorization_finish (priv->authority, + result, + &error); + /* Some random error happened */ + if (error) { + dbus_g_method_return_error (call->context, error); + g_error_free (error); + polkit_call_free (call); + return; + } + + /* Caller didn't successfully authenticate */ + if (!polkit_authorization_result_get_is_authorized (pk_result)) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED, + "Insufficient privileges."); + dbus_g_method_return_error (call->context, error); + g_error_free (error); + polkit_call_free (call); + goto out; + } + + /* Caller is authenticated, now we can finally try to update */ + nm_settings_connection_interface_get_secrets (NM_SETTINGS_CONNECTION_INTERFACE (self), + call->setting_name, + (const char **) call->hints, + call->request_new, + con_secrets_cb, + call); + +out: + g_object_unref (pk_result); +} + +static void +dbus_get_secrets (NMExportedConnection *exported, + const gchar *setting_name, + const gchar **hints, + gboolean request_new, + DBusGMethodInvocation *context) +{ + NMSysconfigConnection *self = NM_SYSCONFIG_CONNECTION (exported); + NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self); + PolkitCall *call; + + call = polkit_call_new (self, context, NULL, setting_name, hints, request_new); + g_assert (call); + polkit_authority_check_authorization (priv->authority, + call->subject, + NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY, + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + call->cancellable, + pk_secrets_cb, + call); + priv->pk_calls = g_slist_prepend (priv->pk_calls, call); +} + +/**************************************************************/ + +static void +settings_connection_interface_init (NMSettingsConnectionInterface *iface) +{ + iface->get_secrets = get_secrets; +} + +static void +nm_sysconfig_connection_init (NMSysconfigConnection *self) +{ + NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self); + + priv->authority = polkit_authority_get (); + if (!priv->authority) { + nm_log_err (LOGD_SYS_SET, "%s: error creating PolicyKit authority"); + } +} + +static void +dispose (GObject *object) +{ + NMSysconfigConnection *self = NM_SYSCONFIG_CONNECTION (object); + NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self); + GSList *iter; + + if (priv->secrets) + g_object_unref (priv->secrets); + + /* Cancel PolicyKit requests */ + for (iter = priv->pk_calls; iter; iter = g_slist_next (iter)) { + PolkitCall *call = iter->data; + + call->disposed = TRUE; + g_cancellable_cancel (call->cancellable); + } + g_slist_free (priv->pk_calls); + priv->pk_calls = NULL; + + G_OBJECT_CLASS (nm_sysconfig_connection_parent_class)->dispose (object); +} + +static void +nm_sysconfig_connection_class_init (NMSysconfigConnectionClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + NMExportedConnectionClass *ec_class = NM_EXPORTED_CONNECTION_CLASS (class); + + g_type_class_add_private (class, sizeof (NMSysconfigConnectionPrivate)); + + /* Virtual methods */ + object_class->dispose = dispose; + ec_class->update = dbus_update; + ec_class->delete = dbus_delete; + ec_class->get_secrets = dbus_get_secrets; +} diff --git a/src/system-settings/nm-sysconfig-connection.h b/src/system-settings/nm-sysconfig-connection.h new file mode 100644 index 00000000..0431dc95 --- /dev/null +++ b/src/system-settings/nm-sysconfig-connection.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2008 Novell, Inc. + */ + +#ifndef NM_SYSCONFIG_CONNECTION_H +#define NM_SYSCONFIG_CONNECTION_H + +#include <nm-connection.h> +#include <nm-exported-connection.h> + +G_BEGIN_DECLS + +#define NM_TYPE_SYSCONFIG_CONNECTION (nm_sysconfig_connection_get_type ()) +#define NM_SYSCONFIG_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SYSCONFIG_CONNECTION, NMSysconfigConnection)) +#define NM_SYSCONFIG_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SYSCONFIG_CONNECTION, NMSysconfigConnectionClass)) +#define NM_IS_SYSCONFIG_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SYSCONFIG_CONNECTION)) +#define NM_IS_SYSCONFIG_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SYSCONFIG_CONNECTION)) +#define NM_SYSCONFIG_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SYSCONFIG_CONNECTION, NMSysconfigConnectionClass)) + +typedef struct { + NMExportedConnection parent; +} NMSysconfigConnection; + +typedef struct { + NMExportedConnectionClass parent; +} NMSysconfigConnectionClass; + +GType nm_sysconfig_connection_get_type (void); + +/* Called by a system-settings plugin to update a connection is out of sync + * with it's backing storage. + */ +gboolean nm_sysconfig_connection_update (NMSysconfigConnection *self, + NMConnection *new_settings, + gboolean signal_update, + GError **error); + +G_END_DECLS + +#endif /* NM_SYSCONFIG_CONNECTION_H */ diff --git a/src/system-settings/nm-sysconfig-settings.c b/src/system-settings/nm-sysconfig-settings.c new file mode 100644 index 00000000..4bcfb04b --- /dev/null +++ b/src/system-settings/nm-sysconfig-settings.c @@ -0,0 +1,1520 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * Søren Sandmann <sandmann@daimi.au.dk> + * Dan Williams <dcbw@redhat.com> + * Tambet Ingo <tambet@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2007 - 2010 Red Hat, Inc. + * (C) Copyright 2008 Novell, Inc. + */ + +#include <unistd.h> +#include <string.h> +#include <gmodule.h> +#include <net/ethernet.h> +#include <netinet/ether.h> + +#include <NetworkManager.h> +#include <nm-connection.h> +#include <dbus/dbus.h> +#include <dbus/dbus-glib-lowlevel.h> +#include <nm-settings-system-interface.h> + +#include <nm-setting-8021x.h> +#include <nm-setting-bluetooth.h> +#include <nm-setting-cdma.h> +#include <nm-setting-connection.h> +#include <nm-setting-gsm.h> +#include <nm-setting-ip4-config.h> +#include <nm-setting-ip6-config.h> +#include <nm-setting-olpc-mesh.h> +#include <nm-setting-ppp.h> +#include <nm-setting-pppoe.h> +#include <nm-setting-serial.h> +#include <nm-setting-vpn.h> +#include <nm-setting-wired.h> +#include <nm-setting-wireless.h> +#include <nm-setting-wireless-security.h> + +#include "../nm-device-ethernet.h" +#include "nm-dbus-glib-types.h" +#include "nm-sysconfig-settings.h" +#include "nm-sysconfig-connection.h" +#include "nm-polkit-helpers.h" +#include "nm-system-config-error.h" +#include "nm-default-wired-connection.h" +#include "nm-logging.h" + +#define CONFIG_KEY_NO_AUTO_DEFAULT "no-auto-default" + +/* LINKER CRACKROCK */ +#define EXPORT(sym) void * __export_##sym = &sym; + +#include "nm-inotify-helper.h" +EXPORT(nm_inotify_helper_get_type) +EXPORT(nm_inotify_helper_get) +EXPORT(nm_inotify_helper_add_watch) +EXPORT(nm_inotify_helper_remove_watch) + +EXPORT(nm_sysconfig_connection_get_type) +EXPORT(nm_sysconfig_connection_update) +/* END LINKER CRACKROCK */ + +static void claim_connection (NMSysconfigSettings *self, + NMSettingsConnectionInterface *connection, + gboolean do_export); + +static void impl_settings_save_hostname (NMSysconfigSettings *self, + const char *hostname, + DBusGMethodInvocation *context); + +static void impl_settings_get_permissions (NMSysconfigSettings *self, + DBusGMethodInvocation *context); + +#include "nm-settings-system-glue.h" + +static void unmanaged_specs_changed (NMSystemConfigInterface *config, gpointer user_data); + +typedef struct { + PolkitAuthority *authority; + guint auth_changed_id; + char *config_file; + + GSList *pk_calls; + GSList *permissions_calls; + + GSList *plugins; + gboolean connections_loaded; + GHashTable *connections; + GSList *unmanaged_specs; +} NMSysconfigSettingsPrivate; + +static void settings_system_interface_init (NMSettingsSystemInterface *klass); + +G_DEFINE_TYPE_WITH_CODE (NMSysconfigSettings, nm_sysconfig_settings, NM_TYPE_SETTINGS_SERVICE, + G_IMPLEMENT_INTERFACE (NM_TYPE_SETTINGS_SYSTEM_INTERFACE, + settings_system_interface_init)) + +#define NM_SYSCONFIG_SETTINGS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SYSCONFIG_SETTINGS, NMSysconfigSettingsPrivate)) + +enum { + PROPERTIES_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum { + PROP_0, + PROP_UNMANAGED_SPECS, + + LAST_PROP +}; + +static void +load_connections (NMSysconfigSettings *self) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + GSList *iter; + + if (priv->connections_loaded) + return; + + for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { + NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data); + GSList *plugin_connections; + GSList *elt; + + plugin_connections = nm_system_config_interface_get_connections (plugin); + + // FIXME: ensure connections from plugins loaded with a lower priority + // get rejected when they conflict with connections from a higher + // priority plugin. + + for (elt = plugin_connections; elt; elt = g_slist_next (elt)) + claim_connection (self, NM_SETTINGS_CONNECTION_INTERFACE (elt->data), TRUE); + + g_slist_free (plugin_connections); + } + + priv->connections_loaded = TRUE; + + /* FIXME: Bad hack */ + unmanaged_specs_changed (NULL, self); +} + +static GSList * +list_connections (NMSettingsService *settings) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (settings); + GHashTableIter iter; + gpointer key; + GSList *list = NULL; + + load_connections (NM_SYSCONFIG_SETTINGS (settings)); + + g_hash_table_iter_init (&iter, priv->connections); + while (g_hash_table_iter_next (&iter, &key, NULL)) + list = g_slist_prepend (list, NM_EXPORTED_CONNECTION (key)); + return g_slist_reverse (list); +} + +static void +clear_unmanaged_specs (NMSysconfigSettings *self) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + + g_slist_foreach (priv->unmanaged_specs, (GFunc) g_free, NULL); + g_slist_free (priv->unmanaged_specs); + priv->unmanaged_specs = NULL; +} + +static char* +uscore_to_wincaps (const char *uscore) +{ + const char *p; + GString *str; + gboolean last_was_uscore; + + last_was_uscore = TRUE; + + str = g_string_new (NULL); + p = uscore; + while (p && *p) { + if (*p == '-' || *p == '_') + last_was_uscore = TRUE; + else { + if (last_was_uscore) { + g_string_append_c (str, g_ascii_toupper (*p)); + last_was_uscore = FALSE; + } else + g_string_append_c (str, *p); + } + ++p; + } + + return g_string_free (str, FALSE); +} + +static void +notify (GObject *object, GParamSpec *pspec) +{ + GValue *value; + GHashTable *hash; + + value = g_slice_new0 (GValue); + hash = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL); + + g_value_init (value, pspec->value_type); + g_object_get_property (object, pspec->name, value); + g_hash_table_insert (hash, uscore_to_wincaps (pspec->name), value); + g_signal_emit (object, signals[PROPERTIES_CHANGED], 0, hash); + g_hash_table_destroy (hash); + g_value_unset (value); + g_slice_free (GValue, value); +} + +const GSList * +nm_sysconfig_settings_get_unmanaged_specs (NMSysconfigSettings *self) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + + load_connections (self); + return priv->unmanaged_specs; +} + +static NMSystemConfigInterface * +get_plugin (NMSysconfigSettings *self, guint32 capability) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + GSList *iter; + + g_return_val_if_fail (self != NULL, NULL); + + /* Do any of the plugins support setting the hostname? */ + for (iter = priv->plugins; iter; iter = iter->next) { + NMSystemConfigInterfaceCapabilities caps = NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE; + + g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES, &caps, NULL); + if (caps & capability) + return NM_SYSTEM_CONFIG_INTERFACE (iter->data); + } + + return NULL; +} + +char * +nm_sysconfig_settings_get_hostname (NMSysconfigSettings *self) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + GSList *iter; + char *hostname = NULL; + + /* Hostname returned is the hostname returned from the first plugin + * that provides one. + */ + for (iter = priv->plugins; iter; iter = iter->next) { + NMSystemConfigInterfaceCapabilities caps = NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE; + + g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES, &caps, NULL); + if (caps & NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME) { + g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME, &hostname, NULL); + if (hostname && strlen (hostname)) + return hostname; + g_free (hostname); + } + } + + return hostname; +} + +static void +plugin_connection_added (NMSystemConfigInterface *config, + NMSettingsConnectionInterface *connection, + gpointer user_data) +{ + claim_connection (NM_SYSCONFIG_SETTINGS (user_data), connection, TRUE); +} + +static gboolean +find_unmanaged_device (NMSysconfigSettings *self, const char *needle) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + GSList *iter; + + for (iter = priv->unmanaged_specs; iter; iter = g_slist_next (iter)) { + if (!strcmp ((const char *) iter->data, needle)) + return TRUE; + } + return FALSE; +} + +static void +unmanaged_specs_changed (NMSystemConfigInterface *config, + gpointer user_data) +{ + NMSysconfigSettings *self = NM_SYSCONFIG_SETTINGS (user_data); + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + GSList *iter; + + clear_unmanaged_specs (self); + + /* Ask all the plugins for their unmanaged specs */ + for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { + GSList *specs, *specs_iter; + + specs = nm_system_config_interface_get_unmanaged_specs (NM_SYSTEM_CONFIG_INTERFACE (iter->data)); + for (specs_iter = specs; specs_iter; specs_iter = specs_iter->next) { + if (!find_unmanaged_device (self, (const char *) specs_iter->data)) { + priv->unmanaged_specs = g_slist_prepend (priv->unmanaged_specs, specs_iter->data); + } else + g_free (specs_iter->data); + } + + g_slist_free (specs); + } + + g_object_notify (G_OBJECT (self), NM_SYSCONFIG_SETTINGS_UNMANAGED_SPECS); +} + +static void +hostname_changed (NMSystemConfigInterface *config, + GParamSpec *pspec, + gpointer user_data) +{ + g_object_notify (G_OBJECT (user_data), NM_SETTINGS_SYSTEM_INTERFACE_HOSTNAME); +} + +static void +add_plugin (NMSysconfigSettings *self, NMSystemConfigInterface *plugin) +{ + NMSysconfigSettingsPrivate *priv; + char *pname = NULL; + char *pinfo = NULL; + + g_return_if_fail (NM_IS_SYSCONFIG_SETTINGS (self)); + g_return_if_fail (NM_IS_SYSTEM_CONFIG_INTERFACE (plugin)); + + priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + + priv->plugins = g_slist_append (priv->plugins, g_object_ref (plugin)); + + g_signal_connect (plugin, NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED, + G_CALLBACK (plugin_connection_added), self); + g_signal_connect (plugin, "notify::hostname", G_CALLBACK (hostname_changed), self); + + nm_system_config_interface_init (plugin, NULL); + + g_object_get (G_OBJECT (plugin), + NM_SYSTEM_CONFIG_INTERFACE_NAME, &pname, + NM_SYSTEM_CONFIG_INTERFACE_INFO, &pinfo, + NULL); + + g_signal_connect (plugin, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED, + G_CALLBACK (unmanaged_specs_changed), self); + + nm_log_info (LOGD_SYS_SET, "Loaded plugin %s: %s", pname, pinfo); + g_free (pname); + g_free (pinfo); +} + +static GObject * +find_plugin (GSList *list, const char *pname) +{ + GSList *iter; + GObject *obj = NULL; + + g_return_val_if_fail (pname != NULL, FALSE); + + for (iter = list; iter && !obj; iter = g_slist_next (iter)) { + NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data); + char *list_pname = NULL; + + g_object_get (G_OBJECT (plugin), + NM_SYSTEM_CONFIG_INTERFACE_NAME, + &list_pname, + NULL); + if (list_pname && !strcmp (pname, list_pname)) + obj = G_OBJECT (plugin); + + g_free (list_pname); + } + + return obj; +} + +static gboolean +load_plugins (NMSysconfigSettings *self, const char *plugins, GError **error) +{ + GSList *list = NULL; + char **plist; + char **iter; + gboolean success = TRUE; + + plist = g_strsplit (plugins, ",", 0); + if (!plist) + return FALSE; + + for (iter = plist; *iter; iter++) { + GModule *plugin; + char *full_name, *path; + const char *pname = *iter; + GObject *obj; + GObject * (*factory_func) (void); + + /* ifcfg-fedora was renamed ifcfg-rh; handle old configs here */ + if (!strcmp (pname, "ifcfg-fedora")) + pname = "ifcfg-rh"; + + obj = find_plugin (list, pname); + if (obj) + continue; + + full_name = g_strdup_printf ("nm-settings-plugin-%s", pname); + path = g_module_build_path (PLUGINDIR, full_name); + + plugin = g_module_open (path, G_MODULE_BIND_LOCAL); + if (!plugin) { + g_set_error (error, 0, 0, + "Could not load plugin '%s': %s", + pname, g_module_error ()); + g_free (full_name); + g_free (path); + success = FALSE; + break; + } + + g_free (full_name); + g_free (path); + + if (!g_module_symbol (plugin, "nm_system_config_factory", (gpointer) (&factory_func))) { + g_set_error (error, 0, 0, + "Could not find plugin '%s' factory function.", + pname); + success = FALSE; + break; + } + + obj = (*factory_func) (); + if (!obj || !NM_IS_SYSTEM_CONFIG_INTERFACE (obj)) { + g_set_error (error, 0, 0, + "Plugin '%s' returned invalid system config object.", + pname); + success = FALSE; + break; + } + + g_module_make_resident (plugin); + g_object_weak_ref (obj, (GWeakNotify) g_module_close, plugin); + add_plugin (self, NM_SYSTEM_CONFIG_INTERFACE (obj)); + list = g_slist_append (list, obj); + } + + g_strfreev (plist); + + g_slist_foreach (list, (GFunc) g_object_unref, NULL); + g_slist_free (list); + + return success; +} + +static void +connection_removed (NMSettingsConnectionInterface *connection, + gpointer user_data) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (user_data); + + g_hash_table_remove (priv->connections, connection); +} + +static void +claim_connection (NMSysconfigSettings *self, + NMSettingsConnectionInterface *connection, + gboolean do_export) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + + g_return_if_fail (NM_IS_SYSCONFIG_SETTINGS (self)); + g_return_if_fail (NM_IS_SETTINGS_CONNECTION_INTERFACE (connection)); + + if (g_hash_table_lookup (priv->connections, connection)) + /* A plugin is lying to us. */ + return; + + g_hash_table_insert (priv->connections, g_object_ref (connection), GINT_TO_POINTER (1)); + g_signal_connect (connection, + NM_SETTINGS_CONNECTION_INTERFACE_REMOVED, + G_CALLBACK (connection_removed), + self); + + if (do_export) { + nm_settings_service_export_connection (NM_SETTINGS_SERVICE (self), connection); + g_signal_emit_by_name (self, NM_SETTINGS_INTERFACE_NEW_CONNECTION, connection); + } +} + +static void +remove_connection (NMSysconfigSettings *self, + NMSettingsConnectionInterface *connection, + gboolean do_signal) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + + g_return_if_fail (NM_IS_SYSCONFIG_SETTINGS (self)); + g_return_if_fail (NM_IS_SETTINGS_CONNECTION_INTERFACE (connection)); + + if (g_hash_table_lookup (priv->connections, connection)) { + g_signal_emit_by_name (G_OBJECT (connection), NM_SETTINGS_CONNECTION_INTERFACE_REMOVED); + g_hash_table_remove (priv->connections, connection); + } +} + +typedef struct { + NMSysconfigSettings *self; + DBusGMethodInvocation *context; + PolkitSubject *subject; + GCancellable *cancellable; + gboolean disposed; + + NMConnection *connection; + NMSettingsAddConnectionFunc callback; + gpointer callback_data; + + char *hostname; + + NMSettingsSystemPermissions permissions; + guint32 permissions_calls; +} PolkitCall; + +#include "nm-dbus-manager.h" + +static PolkitCall * +polkit_call_new (NMSysconfigSettings *self, + DBusGMethodInvocation *context, + NMConnection *connection, + NMSettingsAddConnectionFunc callback, + gpointer callback_data, + const char *hostname) +{ + PolkitCall *call; + char *sender; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (context != NULL, NULL); + + call = g_malloc0 (sizeof (PolkitCall)); + call->self = self; + call->cancellable = g_cancellable_new (); + call->context = context; + if (connection) + call->connection = g_object_ref (connection); + if (callback) { + call->callback = callback; + call->callback_data = callback_data; + } + if (hostname) + call->hostname = g_strdup (hostname); + + sender = dbus_g_method_get_sender (context); + call->subject = polkit_system_bus_name_new (sender); + g_free (sender); + + return call; +} + +static void +polkit_call_free (PolkitCall *call) +{ + if (call->connection) + g_object_unref (call->connection); + g_object_unref (call->cancellable); + g_free (call->hostname); + g_object_unref (call->subject); + g_free (call); +} + +static gboolean +add_new_connection (NMSysconfigSettings *self, + NMConnection *connection, + GError **error) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + GError *tmp_error = NULL, *last_error = NULL; + GSList *iter; + gboolean success = FALSE; + + /* Here's how it works: + 1) plugin writes a connection. + 2) plugin notices that a new connection is available for reading. + 3) plugin reads the new connection (the one it wrote in 1) and emits 'connection-added' signal. + 4) NMSysconfigSettings receives the signal and adds it to it's connection list. + */ + + for (iter = priv->plugins; iter && !success; iter = iter->next) { + success = nm_system_config_interface_add_connection (NM_SYSTEM_CONFIG_INTERFACE (iter->data), + connection, + &tmp_error); + g_clear_error (&last_error); + if (!success) { + last_error = tmp_error; + tmp_error = NULL; + } + } + + if (!success) + *error = last_error; + return success; +} + +static void +pk_add_cb (GObject *object, GAsyncResult *result, gpointer user_data) +{ + PolkitCall *call = user_data; + NMSysconfigSettings *self = call->self; + NMSysconfigSettingsPrivate *priv; + PolkitAuthorizationResult *pk_result; + GError *error = NULL, *add_error = NULL; + + /* If NMSysconfigSettings is already gone, do nothing */ + if (call->disposed) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_GENERAL, + "Request was canceled."); + dbus_g_method_return_error (call->context, error); + g_error_free (error); + polkit_call_free (call); + return; + } + + priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + + priv->pk_calls = g_slist_remove (priv->pk_calls, call); + + pk_result = polkit_authority_check_authorization_finish (priv->authority, + result, + &error); + /* Some random error happened */ + if (error) { + call->callback (NM_SETTINGS_INTERFACE (self), error, call->callback_data); + goto out; + } + + /* Caller didn't successfully authenticate */ + if (!polkit_authorization_result_get_is_authorized (pk_result)) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED, + "Insufficient privileges."); + call->callback (NM_SETTINGS_INTERFACE (self), error, call->callback_data); + goto out; + } + + if (add_new_connection (self, call->connection, &add_error)) + call->callback (NM_SETTINGS_INTERFACE (self), NULL, call->callback_data); + else { + error = g_error_new (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_ADD_FAILED, + "Saving connection failed: (%d) %s", + add_error ? add_error->code : -1, + (add_error && add_error->message) ? add_error->message : "(unknown)"); + g_error_free (add_error); + call->callback (NM_SETTINGS_INTERFACE (self), error, call->callback_data); + } + +out: + g_clear_error (&error); + polkit_call_free (call); + if (pk_result) + g_object_unref (pk_result); +} + +static void +add_connection (NMSettingsService *service, + NMConnection *connection, + DBusGMethodInvocation *context, /* Only present for D-Bus calls */ + NMSettingsAddConnectionFunc callback, + gpointer user_data) +{ + NMSysconfigSettings *self = NM_SYSCONFIG_SETTINGS (service); + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + PolkitCall *call; + GError *error = NULL; + + /* Do any of the plugins support adding? */ + if (!get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_ADD_NOT_SUPPORTED, + "None of the registered plugins support add."); + callback (NM_SETTINGS_INTERFACE (service), error, user_data); + g_error_free (error); + return; + } + + call = polkit_call_new (self, context, connection, callback, user_data, NULL); + g_assert (call); + polkit_authority_check_authorization (priv->authority, + call->subject, + NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY, + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + call->cancellable, + pk_add_cb, + call); + priv->pk_calls = g_slist_append (priv->pk_calls, call); +} + +static void +pk_hostname_cb (GObject *object, GAsyncResult *result, gpointer user_data) +{ + PolkitCall *call = user_data; + NMSysconfigSettings *self = call->self; + NMSysconfigSettingsPrivate *priv; + PolkitAuthorizationResult *pk_result; + GError *error = NULL; + GSList *iter; + gboolean success = FALSE; + + /* If our NMSysconfigConnection is already gone, do nothing */ + if (call->disposed) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_GENERAL, + "Request was canceled."); + dbus_g_method_return_error (call->context, error); + g_error_free (error); + polkit_call_free (call); + return; + } + + priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + + priv->pk_calls = g_slist_remove (priv->pk_calls, call); + + pk_result = polkit_authority_check_authorization_finish (priv->authority, + result, + &error); + /* Some random error happened */ + if (error) { + dbus_g_method_return_error (call->context, error); + goto out; + } + + /* Caller didn't successfully authenticate */ + if (!polkit_authorization_result_get_is_authorized (pk_result)) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED, + "Insufficient privileges."); + dbus_g_method_return_error (call->context, error); + goto out; + } + + /* Set the hostname in all plugins */ + for (iter = priv->plugins; iter; iter = iter->next) { + NMSystemConfigInterfaceCapabilities caps = NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE; + + g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES, &caps, NULL); + if (caps & NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME) { + g_object_set (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME, call->hostname, NULL); + success = TRUE; + } + } + + if (success) { + dbus_g_method_return (call->context); + } else { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_SAVE_HOSTNAME_FAILED, + "Saving the hostname failed."); + dbus_g_method_return_error (call->context, error); + } + +out: + g_clear_error (&error); + polkit_call_free (call); + if (pk_result) + g_object_unref (pk_result); +} + +static void +impl_settings_save_hostname (NMSysconfigSettings *self, + const char *hostname, + DBusGMethodInvocation *context) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + PolkitCall *call; + GError *error = NULL; + + /* Do any of the plugins support setting the hostname? */ + if (!get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME)) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_SAVE_HOSTNAME_NOT_SUPPORTED, + "None of the registered plugins support setting the hostname."); + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + call = polkit_call_new (self, context, NULL, NULL, NULL, hostname); + g_assert (call); + polkit_authority_check_authorization (priv->authority, + call->subject, + NM_SYSCONFIG_POLICY_ACTION_HOSTNAME_MODIFY, + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + call->cancellable, + pk_hostname_cb, + call); + priv->pk_calls = g_slist_append (priv->pk_calls, call); +} + +static void +pk_authority_changed_cb (GObject *object, gpointer user_data) +{ + /* Let clients know they should re-check their authorization */ + g_signal_emit_by_name (NM_SYSCONFIG_SETTINGS (user_data), + NM_SETTINGS_SYSTEM_INTERFACE_CHECK_PERMISSIONS); +} + +typedef struct { + PolkitCall *pk_call; + const char *pk_action; + GCancellable *cancellable; + NMSettingsSystemPermissions permission; + gboolean disposed; +} PermissionsCall; + +static void +permission_call_done (GObject *object, GAsyncResult *result, gpointer user_data) +{ + PermissionsCall *call = user_data; + PolkitCall *pk_call = call->pk_call; + NMSysconfigSettings *self = pk_call->self; + NMSysconfigSettingsPrivate *priv; + PolkitAuthorizationResult *pk_result; + GError *error = NULL; + + /* If NMSysconfigSettings is gone, just skip to the end */ + if (call->disposed) + goto done; + + priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + + priv->permissions_calls = g_slist_remove (priv->permissions_calls, call); + + pk_result = polkit_authority_check_authorization_finish (priv->authority, + result, + &error); + /* Some random error happened */ + if (error) { + nm_log_err (LOGD_SYS_SET, "error checking '%s' permission: (%d) %s", + __FILE__, __LINE__, __func__, + call->pk_action, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + if (error) + g_error_free (error); + } else { + /* If the caller is authorized, or the caller could authorize via a + * challenge, then authorization is possible. Otherwise, caller is out of + * luck. + */ + if ( polkit_authorization_result_get_is_authorized (pk_result) + || polkit_authorization_result_get_is_challenge (pk_result)) + pk_call->permissions |= call->permission; + } + + g_object_unref (pk_result); + +done: + pk_call->permissions_calls--; + if (pk_call->permissions_calls == 0) { + if (call->disposed) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_GENERAL, + "Request was canceled."); + dbus_g_method_return_error (pk_call->context, error); + g_error_free (error); + } else { + /* All the permissions calls are done, return the full permissions + * bitfield back to the user. + */ + dbus_g_method_return (pk_call->context, pk_call->permissions); + } + + polkit_call_free (pk_call); + } + memset (call, 0, sizeof (PermissionsCall)); + g_free (call); +} + +static void +start_permission_check (NMSysconfigSettings *self, + PolkitCall *pk_call, + const char *pk_action, + NMSettingsSystemPermissions permission) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + PermissionsCall *call; + + g_return_if_fail (pk_call != NULL); + g_return_if_fail (pk_action != NULL); + g_return_if_fail (permission != NM_SETTINGS_SYSTEM_PERMISSION_NONE); + + call = g_malloc0 (sizeof (PermissionsCall)); + call->pk_call = pk_call; + call->pk_action = pk_action; + call->permission = permission; + call->cancellable = g_cancellable_new (); + + pk_call->permissions_calls++; + + polkit_authority_check_authorization (priv->authority, + pk_call->subject, + pk_action, + NULL, + 0, + call->cancellable, + permission_call_done, + call); + priv->permissions_calls = g_slist_append (priv->permissions_calls, call); +} + +static void +impl_settings_get_permissions (NMSysconfigSettings *self, + DBusGMethodInvocation *context) +{ + PolkitCall *call; + + call = polkit_call_new (self, context, NULL, NULL, NULL, FALSE); + g_assert (call); + + /* Start checks for the various permissions */ + + /* Only check for connection-modify if one of our plugins supports it. */ + if (get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)) { + start_permission_check (self, call, + NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY, + NM_SETTINGS_SYSTEM_PERMISSION_CONNECTION_MODIFY); + } + + /* Only check for hostname-modify if one of our plugins supports it. */ + if (get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME)) { + start_permission_check (self, call, + NM_SYSCONFIG_POLICY_ACTION_HOSTNAME_MODIFY, + NM_SETTINGS_SYSTEM_PERMISSION_HOSTNAME_MODIFY); + } + + // FIXME: hook these into plugin permissions like the modify permissions */ + start_permission_check (self, call, + NM_SYSCONFIG_POLICY_ACTION_WIFI_SHARE_OPEN, + NM_SETTINGS_SYSTEM_PERMISSION_WIFI_SHARE_OPEN); + start_permission_check (self, call, + NM_SYSCONFIG_POLICY_ACTION_WIFI_SHARE_PROTECTED, + NM_SETTINGS_SYSTEM_PERMISSION_WIFI_SHARE_PROTECTED); +} + +static gboolean +get_permissions (NMSettingsSystemInterface *settings, + NMSettingsSystemGetPermissionsFunc callback, + gpointer user_data) +{ + NMSysconfigSettings *self = NM_SYSCONFIG_SETTINGS (settings); + NMSettingsSystemPermissions permissions = NM_SETTINGS_SYSTEM_PERMISSION_NONE; + + /* Local caller (ie, NM) gets full permissions by default because it doesn't + * need authorization. However, permissions are still subject to plugin's + * restrictions. i.e. if no plugins support connection-modify, then even + * the local caller won't get that permission. + */ + + /* Only check for connection-modify if one of our plugins supports it. */ + if (get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)) + permissions |= NM_SETTINGS_SYSTEM_PERMISSION_CONNECTION_MODIFY; + + /* Only check for hostname-modify if one of our plugins supports it. */ + if (get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME)) + permissions |= NM_SETTINGS_SYSTEM_PERMISSION_HOSTNAME_MODIFY; + + // FIXME: hook these into plugin permissions like the modify permissions */ + permissions |= NM_SETTINGS_SYSTEM_PERMISSION_WIFI_SHARE_OPEN; + permissions |= NM_SETTINGS_SYSTEM_PERMISSION_WIFI_SHARE_PROTECTED; + + callback (settings, permissions, NULL, user_data); + return TRUE; +} + +static gboolean +have_connection_for_device (NMSysconfigSettings *self, GByteArray *mac) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + GHashTableIter iter; + gpointer key; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + const GByteArray *setting_mac; + gboolean ret = FALSE; + + g_return_val_if_fail (NM_IS_SYSCONFIG_SETTINGS (self), FALSE); + g_return_val_if_fail (mac != NULL, FALSE); + + /* Find a wired connection locked to the given MAC address, if any */ + g_hash_table_iter_init (&iter, priv->connections); + while (g_hash_table_iter_next (&iter, &key, NULL)) { + NMConnection *connection = NM_CONNECTION (key); + const char *connection_type; + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + connection_type = nm_setting_connection_get_connection_type (s_con); + + if ( strcmp (connection_type, NM_SETTING_WIRED_SETTING_NAME) + && strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME)) + continue; + + s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED); + + /* No wired setting; therefore the PPPoE connection applies to any device */ + if (!s_wired && !strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME)) { + ret = TRUE; + break; + } + + setting_mac = nm_setting_wired_get_mac_address (s_wired); + if (setting_mac) { + /* A connection mac-locked to this device */ + if (!memcmp (setting_mac->data, mac->data, ETH_ALEN)) { + ret = TRUE; + break; + } + } else { + /* A connection that applies to any wired device */ + ret = TRUE; + break; + } + } + + return ret; +} + +/* Search through the list of blacklisted MAC addresses in the config file. */ +static gboolean +is_mac_auto_wired_blacklisted (NMSysconfigSettings *self, const GByteArray *mac) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + GKeyFile *config; + char **list, **iter; + gboolean found = FALSE; + + g_return_val_if_fail (mac != NULL, FALSE); + + if (!priv->config_file) + return FALSE; + + config = g_key_file_new (); + if (!config) { + nm_log_warn (LOGD_SYS_SET, "not enough memory to load config file."); + return FALSE; + } + + g_key_file_set_list_separator (config, ','); + if (!g_key_file_load_from_file (config, priv->config_file, G_KEY_FILE_NONE, NULL)) + goto out; + + list = g_key_file_get_string_list (config, "main", CONFIG_KEY_NO_AUTO_DEFAULT, NULL, NULL); + for (iter = list; iter && *iter; iter++) { + struct ether_addr *candidate; + + candidate = ether_aton (*iter); + if (candidate && !memcmp (mac->data, candidate->ether_addr_octet, ETH_ALEN)) { + found = TRUE; + break; + } + } + + if (list) + g_strfreev (list); + +out: + g_key_file_free (config); + return found; +} + +#define DEFAULT_WIRED_TAG "default-wired" + +static void +default_wired_deleted (NMDefaultWiredConnection *wired, + const GByteArray *mac, + NMSysconfigSettings *self) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + NMSettingConnection *s_con; + char *tmp; + GKeyFile *config; + char **list, **iter, **updated; + gboolean found = FALSE; + gsize len = 0, i; + char *data; + + /* If there was no config file specified, there's nothing to do */ + if (!priv->config_file) + goto cleanup; + + /* When the default wired connection is removed (either deleted or saved + * to a new persistent connection by a plugin), write the MAC address of + * the wired device to the config file and don't create a new default wired + * connection for that device again. + */ + + s_con = (NMSettingConnection *) nm_connection_get_setting (NM_CONNECTION (wired), + NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + + /* Ignore removals of read-only connections, since they couldn't have + * been removed by the user. + */ + if (nm_setting_connection_get_read_only (s_con)) + goto cleanup; + + config = g_key_file_new (); + if (!config) + goto cleanup; + + g_key_file_set_list_separator (config, ','); + g_key_file_load_from_file (config, priv->config_file, G_KEY_FILE_KEEP_COMMENTS, NULL); + + list = g_key_file_get_string_list (config, "main", CONFIG_KEY_NO_AUTO_DEFAULT, &len, NULL); + /* Traverse entire list to get count of # items */ + for (iter = list; iter && *iter; iter++) { + struct ether_addr *candidate; + + candidate = ether_aton (*iter); + if (candidate && !memcmp (mac->data, candidate->ether_addr_octet, ETH_ALEN)) + found = TRUE; + } + + /* Add this device's MAC to the list */ + if (!found) { + tmp = g_strdup_printf ("%02x:%02x:%02x:%02x:%02x:%02x", + mac->data[0], mac->data[1], mac->data[2], + mac->data[3], mac->data[4], mac->data[5]); + + /* New list; size + 1 for the new element, + 1 again for ending NULL */ + updated = g_malloc0 (sizeof (char*) * (len + 2)); + + /* Copy original list and add new MAC */ + for (i = 0; list && list[i]; i++) + updated[i] = list[i]; + updated[i++] = tmp; + updated[i] = NULL; + + g_key_file_set_string_list (config, + "main", CONFIG_KEY_NO_AUTO_DEFAULT, + (const char **) updated, + len + 2); + /* g_free() not g_strfreev() since 'updated' isn't a deep-copy */ + g_free (updated); + g_free (tmp); + + data = g_key_file_to_data (config, &len, NULL); + if (data) { + g_file_set_contents (priv->config_file, data, len, NULL); + g_free (data); + } + } + + if (list) + g_strfreev (list); + g_key_file_free (config); + +cleanup: + g_object_set_data (G_OBJECT (nm_default_wired_connection_get_device (wired)), + DEFAULT_WIRED_TAG, + NULL); +} + +static void +delete_cb (NMSettingsConnectionInterface *connection, GError *error, gpointer user_data) +{ +} + +static gboolean +default_wired_try_update (NMDefaultWiredConnection *wired, + NMSysconfigSettings *self) +{ + GError *error = NULL; + NMSettingConnection *s_con; + const char *id; + + /* Try to move this default wired conneciton to a plugin so that it has + * persistent storage. + */ + + s_con = (NMSettingConnection *) nm_connection_get_setting (NM_CONNECTION (wired), + NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + id = nm_setting_connection_get_id (s_con); + g_assert (id); + + remove_connection (self, NM_SETTINGS_CONNECTION_INTERFACE (wired), FALSE); + if (add_new_connection (self, NM_CONNECTION (wired), &error)) { + nm_settings_connection_interface_delete (NM_SETTINGS_CONNECTION_INTERFACE (wired), + delete_cb, + NULL); + + g_object_set_data (G_OBJECT (nm_default_wired_connection_get_device (wired)), + DEFAULT_WIRED_TAG, + NULL); + nm_log_info (LOGD_SYS_SET, "Saved default wired connection '%s' to persistent storage", id); + return FALSE; + } + + nm_log_warn (LOGD_SYS_SET, "couldn't save default wired connection '%s': %d / %s", + id, + error ? error->code : -1, + (error && error->message) ? error->message : "(unknown)"); + + /* If there was an error, don't destroy the default wired connection, + * but add it back to the system settings service. Connection is already + * exported on the bus, don't export it again, thus do_export == FALSE. + */ + claim_connection (self, NM_SETTINGS_CONNECTION_INTERFACE (wired), FALSE); + return TRUE; +} + +void +nm_sysconfig_settings_device_added (NMSysconfigSettings *self, NMDevice *device) +{ + GByteArray *mac = NULL; + struct ether_addr tmp; + NMDefaultWiredConnection *wired; + NMSettingConnection *s_con; + gboolean read_only = TRUE; + const char *id; + + if (nm_device_get_device_type (device) != NM_DEVICE_TYPE_ETHERNET) + return; + + /* If the device isn't managed or it already has a default wired connection, + * ignore it. + */ + if ( !nm_device_get_managed (device) + || g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_TAG)) + return; + + nm_device_ethernet_get_address (NM_DEVICE_ETHERNET (device), &tmp); + + mac = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (mac, tmp.ether_addr_octet, ETH_ALEN); + + if ( have_connection_for_device (self, mac) + || is_mac_auto_wired_blacklisted (self, mac)) + goto ignore; + + if (get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)) + read_only = FALSE; + + wired = nm_default_wired_connection_new (mac, device, read_only); + if (!wired) + goto ignore; + + s_con = (NMSettingConnection *) nm_connection_get_setting (NM_CONNECTION (wired), + NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + id = nm_setting_connection_get_id (s_con); + g_assert (id); + + nm_log_info (LOGD_SYS_SET, "Added default wired connection '%s' for %s", + id, nm_device_get_udi (device)); + + g_signal_connect (wired, "try-update", (GCallback) default_wired_try_update, self); + g_signal_connect (wired, "deleted", (GCallback) default_wired_deleted, self); + claim_connection (self, NM_SETTINGS_CONNECTION_INTERFACE (wired), TRUE); + g_object_unref (wired); + + g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_TAG, wired); + +ignore: + g_byte_array_free (mac, TRUE); +} + +void +nm_sysconfig_settings_device_removed (NMSysconfigSettings *self, NMDevice *device) +{ + NMDefaultWiredConnection *connection; + + if (nm_device_get_device_type (device) != NM_DEVICE_TYPE_ETHERNET) + return; + + connection = (NMDefaultWiredConnection *) g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_TAG); + if (connection) + remove_connection (self, NM_SETTINGS_CONNECTION_INTERFACE (connection), TRUE); +} + +NMSysconfigSettings * +nm_sysconfig_settings_new (const char *config_file, + const char *plugins, + DBusGConnection *bus, + GError **error) +{ + NMSysconfigSettings *self; + NMSysconfigSettingsPrivate *priv; + + self = g_object_new (NM_TYPE_SYSCONFIG_SETTINGS, + NM_SETTINGS_SERVICE_BUS, bus, + NM_SETTINGS_SERVICE_SCOPE, NM_CONNECTION_SCOPE_SYSTEM, + NULL); + if (!self) + return NULL; + + priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + + priv->config_file = g_strdup (config_file); + + if (plugins) { + /* Load the plugins; fail if a plugin is not found. */ + if (!load_plugins (self, plugins, error)) { + g_object_unref (self); + return NULL; + } + unmanaged_specs_changed (NULL, self); + } + + return self; +} + +/***************************************************************/ + +static void +dispose (GObject *object) +{ + NMSysconfigSettings *self = NM_SYSCONFIG_SETTINGS (object); + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + GSList *iter; + + if (priv->auth_changed_id) { + g_signal_handler_disconnect (priv->authority, priv->auth_changed_id); + priv->auth_changed_id = 0; + } + + /* Cancel PolicyKit requests */ + for (iter = priv->pk_calls; iter; iter = g_slist_next (iter)) { + PolkitCall *call = iter->data; + + call->disposed = TRUE; + g_cancellable_cancel (call->cancellable); + } + g_slist_free (priv->pk_calls); + priv->pk_calls = NULL; + + /* Cancel PolicyKit permissions requests */ + for (iter = priv->permissions_calls; iter; iter = g_slist_next (iter)) { + PermissionsCall *call = iter->data; + + call->disposed = TRUE; + g_cancellable_cancel (call->cancellable); + } + g_slist_free (priv->permissions_calls); + priv->permissions_calls = NULL; + + G_OBJECT_CLASS (nm_sysconfig_settings_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMSysconfigSettings *self = NM_SYSCONFIG_SETTINGS (object); + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + + g_hash_table_destroy (priv->connections); + + clear_unmanaged_specs (self); + + g_slist_foreach (priv->plugins, (GFunc) g_object_unref, NULL); + g_slist_free (priv->plugins); + + g_free (priv->config_file); + + G_OBJECT_CLASS (nm_sysconfig_settings_parent_class)->finalize (object); +} + +static void +settings_system_interface_init (NMSettingsSystemInterface *iface) +{ + iface->get_permissions = get_permissions; + + dbus_g_object_type_install_info (G_TYPE_FROM_INTERFACE (iface), + &dbus_glib_nm_settings_system_object_info); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMSysconfigSettings *self = NM_SYSCONFIG_SETTINGS (object); + const GSList *specs, *iter; + GSList *copy = NULL; + + switch (prop_id) { + case PROP_UNMANAGED_SPECS: + specs = nm_sysconfig_settings_get_unmanaged_specs (self); + for (iter = specs; iter; iter = g_slist_next (iter)) + copy = g_slist_append (copy, g_strdup (iter->data)); + g_value_take_boxed (value, copy); + break; + case NM_SETTINGS_SYSTEM_INTERFACE_PROP_HOSTNAME: + g_value_take_string (value, nm_sysconfig_settings_get_hostname (self)); + + /* Don't ever pass NULL through D-Bus */ + if (!g_value_get_string (value)) + g_value_set_static_string (value, ""); + break; + case NM_SETTINGS_SYSTEM_INTERFACE_PROP_CAN_MODIFY: + g_value_set_boolean (value, !!get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_sysconfig_settings_class_init (NMSysconfigSettingsClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + NMSettingsServiceClass *ss_class = NM_SETTINGS_SERVICE_CLASS (class); + + g_type_class_add_private (class, sizeof (NMSysconfigSettingsPrivate)); + + /* virtual methods */ + object_class->notify = notify; + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + ss_class->list_connections = list_connections; + ss_class->add_connection = add_connection; + + /* properties */ + g_object_class_install_property + (object_class, PROP_UNMANAGED_SPECS, + g_param_spec_boxed (NM_SYSCONFIG_SETTINGS_UNMANAGED_SPECS, + "Unamanged device specs", + "Unmanaged device specs", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READABLE)); + + g_object_class_override_property (object_class, + NM_SETTINGS_SYSTEM_INTERFACE_PROP_HOSTNAME, + NM_SETTINGS_SYSTEM_INTERFACE_HOSTNAME); + + g_object_class_override_property (object_class, + NM_SETTINGS_SYSTEM_INTERFACE_PROP_CAN_MODIFY, + NM_SETTINGS_SYSTEM_INTERFACE_CAN_MODIFY); + + /* signals */ + signals[PROPERTIES_CHANGED] = + g_signal_new ("properties-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMSysconfigSettingsClass, properties_changed), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, DBUS_TYPE_G_MAP_OF_VARIANT); + + dbus_g_error_domain_register (NM_SYSCONFIG_SETTINGS_ERROR, + NM_DBUS_IFACE_SETTINGS_SYSTEM, + NM_TYPE_SYSCONFIG_SETTINGS_ERROR); + + dbus_g_error_domain_register (NM_SETTINGS_INTERFACE_ERROR, + NM_DBUS_IFACE_SETTINGS, + NM_TYPE_SETTINGS_INTERFACE_ERROR); + + /* And register all the settings errors with D-Bus */ + dbus_g_error_domain_register (NM_CONNECTION_ERROR, NULL, NM_TYPE_CONNECTION_ERROR); + dbus_g_error_domain_register (NM_SETTING_802_1X_ERROR, NULL, NM_TYPE_SETTING_802_1X_ERROR); + dbus_g_error_domain_register (NM_SETTING_BLUETOOTH_ERROR, NULL, NM_TYPE_SETTING_BLUETOOTH_ERROR); + dbus_g_error_domain_register (NM_SETTING_CDMA_ERROR, NULL, NM_TYPE_SETTING_CDMA_ERROR); + dbus_g_error_domain_register (NM_SETTING_CONNECTION_ERROR, NULL, NM_TYPE_SETTING_CONNECTION_ERROR); + dbus_g_error_domain_register (NM_SETTING_GSM_ERROR, NULL, NM_TYPE_SETTING_GSM_ERROR); + dbus_g_error_domain_register (NM_SETTING_IP4_CONFIG_ERROR, NULL, NM_TYPE_SETTING_IP4_CONFIG_ERROR); + dbus_g_error_domain_register (NM_SETTING_IP6_CONFIG_ERROR, NULL, NM_TYPE_SETTING_IP6_CONFIG_ERROR); + dbus_g_error_domain_register (NM_SETTING_OLPC_MESH_ERROR, NULL, NM_TYPE_SETTING_OLPC_MESH_ERROR); + dbus_g_error_domain_register (NM_SETTING_PPP_ERROR, NULL, NM_TYPE_SETTING_PPP_ERROR); + dbus_g_error_domain_register (NM_SETTING_PPPOE_ERROR, NULL, NM_TYPE_SETTING_PPPOE_ERROR); + dbus_g_error_domain_register (NM_SETTING_SERIAL_ERROR, NULL, NM_TYPE_SETTING_SERIAL_ERROR); + dbus_g_error_domain_register (NM_SETTING_VPN_ERROR, NULL, NM_TYPE_SETTING_VPN_ERROR); + dbus_g_error_domain_register (NM_SETTING_WIRED_ERROR, NULL, NM_TYPE_SETTING_WIRED_ERROR); + dbus_g_error_domain_register (NM_SETTING_WIRELESS_SECURITY_ERROR, NULL, NM_TYPE_SETTING_WIRELESS_SECURITY_ERROR); + dbus_g_error_domain_register (NM_SETTING_WIRELESS_ERROR, NULL, NM_TYPE_SETTING_WIRELESS_ERROR); + dbus_g_error_domain_register (NM_SETTING_ERROR, NULL, NM_TYPE_SETTING_ERROR); +} + +static void +nm_sysconfig_settings_init (NMSysconfigSettings *self) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + + priv->connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL); + + priv->authority = polkit_authority_get (); + if (priv->authority) { + priv->auth_changed_id = g_signal_connect (priv->authority, + "changed", + G_CALLBACK (pk_authority_changed_cb), + self); + } else + nm_log_warn (LOGD_SYS_SET, "failed to create PolicyKit authority."); +} + diff --git a/src/system-settings/nm-sysconfig-settings.h b/src/system-settings/nm-sysconfig-settings.h new file mode 100644 index 00000000..ae2ba683 --- /dev/null +++ b/src/system-settings/nm-sysconfig-settings.h @@ -0,0 +1,71 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * Søren Sandmann <sandmann@daimi.au.dk> + * Dan Williams <dcbw@redhat.com> + * Tambet Ingo <tambet@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2007 - 2009 Red Hat, Inc. + * (C) Copyright 2008 Novell, Inc. + */ + +#ifndef __NM_SYSCONFIG_SETTINGS_H__ +#define __NM_SYSCONFIG_SETTINGS_H__ + +#include <nm-connection.h> +#include <nm-settings-service.h> + +#include "nm-sysconfig-connection.h" +#include "nm-system-config-interface.h" +#include "nm-device.h" + +#define NM_TYPE_SYSCONFIG_SETTINGS (nm_sysconfig_settings_get_type ()) +#define NM_SYSCONFIG_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SYSCONFIG_SETTINGS, NMSysconfigSettings)) +#define NM_SYSCONFIG_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SYSCONFIG_SETTINGS, NMSysconfigSettingsClass)) +#define NM_IS_SYSCONFIG_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SYSCONFIG_SETTINGS)) +#define NM_IS_SYSCONFIG_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SYSCONFIG_SETTINGS)) +#define NM_SYSCONFIG_SETTINGS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SYSCONFIG_SETTINGS, NMSysconfigSettingsClass)) + +#define NM_SYSCONFIG_SETTINGS_UNMANAGED_SPECS "unmanaged-specs" + +typedef struct { + NMSettingsService parent_instance; +} NMSysconfigSettings; + +typedef struct { + NMSettingsServiceClass parent_class; + + /* Signals */ + void (*properties_changed) (NMSysconfigSettings *self, GHashTable *properties); +} NMSysconfigSettingsClass; + +GType nm_sysconfig_settings_get_type (void); + +NMSysconfigSettings *nm_sysconfig_settings_new (const char *config_file, + const char *plugins, + DBusGConnection *bus, + GError **error); + +const GSList *nm_sysconfig_settings_get_unmanaged_specs (NMSysconfigSettings *self); + +char *nm_sysconfig_settings_get_hostname (NMSysconfigSettings *self); + +void nm_sysconfig_settings_device_added (NMSysconfigSettings *self, NMDevice *device); + +void nm_sysconfig_settings_device_removed (NMSysconfigSettings *self, NMDevice *device); + +#endif /* __NM_SYSCONFIG_SETTINGS_H__ */ diff --git a/src/system-settings/nm-system-config-error.c b/src/system-settings/nm-system-config-error.c new file mode 100644 index 00000000..13d47462 --- /dev/null +++ b/src/system-settings/nm-system-config-error.c @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + */ + +#include "nm-system-config-error.h" + +GQuark +nm_sysconfig_settings_error_quark (void) +{ + static GQuark ret = 0; + + if (ret == 0) + ret = g_quark_from_static_string ("nm_sysconfig_settings_error"); + + return ret; +} + +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GType +nm_sysconfig_settings_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (NM_SYSCONFIG_SETTINGS_ERROR_GENERAL, "GeneralError"), + ENUM_ENTRY (NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED, "NotPrivileged"), + ENUM_ENTRY (NM_SYSCONFIG_SETTINGS_ERROR_ADD_NOT_SUPPORTED, "AddNotSupported"), + ENUM_ENTRY (NM_SYSCONFIG_SETTINGS_ERROR_UPDATE_NOT_SUPPORTED, "UpdateNotSupported"), + ENUM_ENTRY (NM_SYSCONFIG_SETTINGS_ERROR_DELETE_NOT_SUPPORTED, "DeleteNotSupported"), + ENUM_ENTRY (NM_SYSCONFIG_SETTINGS_ERROR_ADD_FAILED, "AddFailed"), + ENUM_ENTRY (NM_SYSCONFIG_SETTINGS_ERROR_SAVE_HOSTNAME_NOT_SUPPORTED, "SaveHostnameNotSupported"), + ENUM_ENTRY (NM_SYSCONFIG_SETTINGS_ERROR_SAVE_HOSTNAME_FAILED, "SaveHostnameFailed"), + { 0, 0, 0 } + }; + + etype = g_enum_register_static ("NMSysconfigSettingsError", values); + } + + return etype; +} diff --git a/src/system-settings/nm-system-config-error.h b/src/system-settings/nm-system-config-error.h new file mode 100644 index 00000000..63896fcd --- /dev/null +++ b/src/system-settings/nm-system-config-error.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + */ + +#ifndef NM_SYSTEM_CONFIG_ERROR_H +#define NM_SYSTEM_CONFIG_ERROR_H + +#include <glib.h> +#include <glib-object.h> + +enum { + NM_SYSCONFIG_SETTINGS_ERROR_GENERAL = 0, + NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED, + NM_SYSCONFIG_SETTINGS_ERROR_ADD_NOT_SUPPORTED, + NM_SYSCONFIG_SETTINGS_ERROR_UPDATE_NOT_SUPPORTED, + NM_SYSCONFIG_SETTINGS_ERROR_DELETE_NOT_SUPPORTED, + NM_SYSCONFIG_SETTINGS_ERROR_ADD_FAILED, + NM_SYSCONFIG_SETTINGS_ERROR_SAVE_HOSTNAME_NOT_SUPPORTED, + NM_SYSCONFIG_SETTINGS_ERROR_SAVE_HOSTNAME_FAILED, +}; + +#define NM_SYSCONFIG_SETTINGS_ERROR (nm_sysconfig_settings_error_quark ()) +#define NM_TYPE_SYSCONFIG_SETTINGS_ERROR (nm_sysconfig_settings_error_get_type ()) + +GQuark nm_sysconfig_settings_error_quark (void); +GType nm_sysconfig_settings_error_get_type (void); + +#endif /* NM_SYSTEM_CONFIG_ERROR_H */ diff --git a/src/system-settings/nm-system-config-interface.c b/src/system-settings/nm-system-config-interface.c new file mode 100644 index 00000000..90fd93ba --- /dev/null +++ b/src/system-settings/nm-system-config-interface.c @@ -0,0 +1,163 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2007 - 2008 Red Hat, Inc. + * Copyright (C) 2008 Novell, Inc. + */ + +#include "nm-system-config-interface.h" + +static void +interface_init (gpointer g_iface) +{ + GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); + static gboolean initialized = FALSE; + + if (initialized) + return; + + /* Properties */ + g_object_interface_install_property + (g_iface, + g_param_spec_string (NM_SYSTEM_CONFIG_INTERFACE_NAME, + "Name", + "Plugin name", + NULL, + G_PARAM_READABLE)); + + g_object_interface_install_property + (g_iface, + g_param_spec_string (NM_SYSTEM_CONFIG_INTERFACE_INFO, + "Info", + "Plugin information", + NULL, + G_PARAM_READABLE)); + + g_object_interface_install_property + (g_iface, + g_param_spec_uint (NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES, + "Capabilities", + "Plugin capabilties", + NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE, + NM_SYSTEM_CONFIG_INTERFACE_CAP_LAST - 1, + NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE, + G_PARAM_READABLE)); + + g_object_interface_install_property + (g_iface, + g_param_spec_string (NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME, + "Hostname", + "Configured hostname", + NULL, + G_PARAM_READWRITE)); + + /* Signals */ + g_signal_new (NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED, + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMSystemConfigInterface, connection_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + NM_TYPE_SETTINGS_CONNECTION_INTERFACE); + + g_signal_new (NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED, + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMSystemConfigInterface, unmanaged_specs_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + initialized = TRUE; +} + + +GType +nm_system_config_interface_get_type (void) +{ + static GType system_config_interface_type = 0; + + if (!system_config_interface_type) { + const GTypeInfo system_config_interface_info = { + sizeof (NMSystemConfigInterface), /* class_size */ + interface_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + system_config_interface_type = g_type_register_static (G_TYPE_INTERFACE, + "NMSystemConfigInterface", + &system_config_interface_info, + 0); + + g_type_interface_add_prerequisite (system_config_interface_type, G_TYPE_OBJECT); + } + + return system_config_interface_type; +} + +void +nm_system_config_interface_init (NMSystemConfigInterface *config, + gpointer unused) +{ + g_return_if_fail (config != NULL); + + if (NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->init) + NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->init (config); +} + +GSList * +nm_system_config_interface_get_connections (NMSystemConfigInterface *config) +{ + g_return_val_if_fail (config != NULL, NULL); + + if (NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->get_connections) + return NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->get_connections (config); + return NULL; +} + +GSList * +nm_system_config_interface_get_unmanaged_specs (NMSystemConfigInterface *config) +{ + g_return_val_if_fail (config != NULL, NULL); + + if (NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->get_unmanaged_specs) + return NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->get_unmanaged_specs (config); + return NULL; +} + +gboolean +nm_system_config_interface_add_connection (NMSystemConfigInterface *config, + NMConnection *connection, + GError **error) +{ + gboolean success = FALSE; + + g_return_val_if_fail (config != NULL, FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + + if (NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->add_connection) + success = NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->add_connection (config, connection, error); + + return success; +} diff --git a/src/system-settings/nm-system-config-interface.h b/src/system-settings/nm-system-config-interface.h new file mode 100644 index 00000000..c5bbaaa9 --- /dev/null +++ b/src/system-settings/nm-system-config-interface.h @@ -0,0 +1,145 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2007 - 2008 Red Hat, Inc. + * Copyright (C) 2008 Novell, Inc. + */ + +#ifndef NM_SYSTEM_CONFIG_INTERFACE_H +#define NM_SYSTEM_CONFIG_INTERFACE_H + +#include <glib.h> +#include <glib-object.h> +#include <nm-connection.h> +#include <nm-settings-connection-interface.h> + +G_BEGIN_DECLS + +#define PLUGIN_PRINT(pname, fmt, args...) \ + { g_message (" " pname ": " fmt, ##args); } + +#define PLUGIN_WARN(pname, fmt, args...) \ + { g_warning (" " pname ": " fmt, ##args); } + + +/* Plugin's factory function that returns a GObject that implements + * NMSystemConfigInterface. + */ +GObject * nm_system_config_factory (void); + +/* NOTE: + * When passing NMConnection objects to NetworkManager, any properties + * of that NMConnection's NMSetting objects that are secrets must be set as + * GObject data items on the NMSetting object, _not_ inside the NMSetting + * object itself. This is to ensure that the secrets are only given to + * NetworkManager itself and not exposed to clients like nm-applet that need + * connection details, but not secrets. + */ + +#define NM_TYPE_SYSTEM_CONFIG_INTERFACE (nm_system_config_interface_get_type ()) +#define NM_SYSTEM_CONFIG_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SYSTEM_CONFIG_INTERFACE, NMSystemConfigInterface)) +#define NM_IS_SYSTEM_CONFIG_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SYSTEM_CONFIG_INTERFACE)) +#define NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_SYSTEM_CONFIG_INTERFACE, NMSystemConfigInterface)) + + +#define NM_SYSTEM_CONFIG_INTERFACE_NAME "name" +#define NM_SYSTEM_CONFIG_INTERFACE_INFO "info" +#define NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES "capabilities" +#define NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME "hostname" + +#define NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED "unmanaged-specs-changed" +#define NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED "connection-added" + +typedef enum { + NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE = 0x00000000, + NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS = 0x00000001, + NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME = 0x00000002, + + NM_SYSTEM_CONFIG_INTERFACE_CAP_LAST = NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME +} NMSystemConfigInterfaceCapabilities; + +typedef enum { + NM_SYSTEM_CONFIG_INTERFACE_PROP_FIRST = 0x1000, + + NM_SYSTEM_CONFIG_INTERFACE_PROP_NAME = NM_SYSTEM_CONFIG_INTERFACE_PROP_FIRST, + NM_SYSTEM_CONFIG_INTERFACE_PROP_INFO, + NM_SYSTEM_CONFIG_INTERFACE_PROP_CAPABILITIES, + NM_SYSTEM_CONFIG_INTERFACE_PROP_HOSTNAME, +} NMSystemConfigInterfaceProp; + + +typedef struct _NMSystemConfigInterface NMSystemConfigInterface; + +struct _NMSystemConfigInterface { + GTypeInterface g_iface; + + /* Called when the plugin is loaded to initialize it */ + void (*init) (NMSystemConfigInterface *config); + + /* Returns a GSList of objects that implement NMSettingsConnectionInterface + * that represent connections the plugin knows about. The returned list + * is freed by the system settings service. + */ + GSList * (*get_connections) (NMSystemConfigInterface *config); + + /* + * Return a string list of specifications of devices which NetworkManager + * should not manage. Returned list will be freed by the system settings + * service, and each element must be allocated using g_malloc() or its + * variants (g_strdup, g_strdup_printf, etc). + * + * Each string in the list must follow the format <method>:<data>, where + * the method and data are one of the following: + * + * Method: mac Data: device MAC address formatted with leading zeros and + * lowercase letters, like 00:0a:0b:0c:0d:0e + */ + GSList * (*get_unmanaged_specs) (NMSystemConfigInterface *config); + + /* + * Add a new connection. + */ + gboolean (*add_connection) (NMSystemConfigInterface *config, + NMConnection *connection, + GError **error); + + /* Signals */ + + /* Emitted when a new connection has been found by the plugin */ + void (*connection_added) (NMSystemConfigInterface *config, + NMSettingsConnectionInterface *connection); + + /* Emitted when the list of unmanaged device specifications changes */ + void (*unmanaged_specs_changed) (NMSystemConfigInterface *config); +}; + +GType nm_system_config_interface_get_type (void); + +void nm_system_config_interface_init (NMSystemConfigInterface *config, + gpointer unused); + +GSList *nm_system_config_interface_get_connections (NMSystemConfigInterface *config); + +GSList *nm_system_config_interface_get_unmanaged_specs (NMSystemConfigInterface *config); + +gboolean nm_system_config_interface_add_connection (NMSystemConfigInterface *config, + NMConnection *connection, + GError **error); + +G_END_DECLS + +#endif /* NM_SYSTEM_CONFIG_INTERFACE_H */ diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am new file mode 100644 index 00000000..62612a0e --- /dev/null +++ b/src/tests/Makefile.am @@ -0,0 +1,49 @@ +INCLUDES = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/libnm-util \ + -I$(top_srcdir)/src/dhcp-manager \ + -I$(top_builddir)/marshallers \ + -I$(top_srcdir)/src \ + -I$(top_builddir)/src + +noinst_PROGRAMS = test-dhcp-options test-policy-hosts + +####### DHCP options test ####### + +test_dhcp_options_SOURCES = \ + test-dhcp-options.c + +test_dhcp_options_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) \ + -DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \ + -DDHCPCD_PATH=\"$(DHCPCD_PATH)\" + +test_dhcp_options_LDADD = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(top_builddir)/src/dhcp-manager/libdhcp-manager.la \ + $(top_builddir)/src/libtest-dhcp.la \ + $(GLIB_LIBS) \ + $(DBUS_LIBS) + +####### policy /etc/hosts test ####### + +test_policy_hosts_SOURCES = \ + test-policy-hosts.c + +test_policy_hosts_CPPFLAGS = \ + $(GLIB_CFLAGS) + +test_policy_hosts_LDADD = \ + -ldl \ + $(top_builddir)/src/libtest-policy-hosts.la \ + $(GLIB_LIBS) + +if WITH_TESTS + +check-local: test-dhcp-options + $(abs_builddir)/test-dhcp-options + $(abs_builddir)/test-policy-hosts + +endif + diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in new file mode 100644 index 00000000..75fb715d --- /dev/null +++ b/src/tests/Makefile.in @@ -0,0 +1,628 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +noinst_PROGRAMS = test-dhcp-options$(EXEEXT) \ + test-policy-hosts$(EXEEXT) +subdir = src/tests +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +PROGRAMS = $(noinst_PROGRAMS) +am_test_dhcp_options_OBJECTS = \ + test_dhcp_options-test-dhcp-options.$(OBJEXT) +test_dhcp_options_OBJECTS = $(am_test_dhcp_options_OBJECTS) +am__DEPENDENCIES_1 = +test_dhcp_options_DEPENDENCIES = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(top_builddir)/src/dhcp-manager/libdhcp-manager.la \ + $(top_builddir)/src/libtest-dhcp.la $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_test_policy_hosts_OBJECTS = \ + test_policy_hosts-test-policy-hosts.$(OBJEXT) +test_policy_hosts_OBJECTS = $(am_test_policy_hosts_OBJECTS) +test_policy_hosts_DEPENDENCIES = \ + $(top_builddir)/src/libtest-policy-hosts.la \ + $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(test_dhcp_options_SOURCES) $(test_policy_hosts_SOURCES) +DIST_SOURCES = $(test_dhcp_options_SOURCES) \ + $(test_policy_hosts_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +IPTABLES_PATH = @IPTABLES_PATH@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBM = @LIBM@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POFILES = @POFILES@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +RANLIB = @RANLIB@ +RESOLVCONF_PATH = @RESOLVCONF_PATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/libnm-util \ + -I$(top_srcdir)/src/dhcp-manager \ + -I$(top_builddir)/marshallers \ + -I$(top_srcdir)/src \ + -I$(top_builddir)/src + + +####### DHCP options test ####### +test_dhcp_options_SOURCES = \ + test-dhcp-options.c + +test_dhcp_options_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) \ + -DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \ + -DDHCPCD_PATH=\"$(DHCPCD_PATH)\" + +test_dhcp_options_LDADD = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(top_builddir)/src/dhcp-manager/libdhcp-manager.la \ + $(top_builddir)/src/libtest-dhcp.la \ + $(GLIB_LIBS) \ + $(DBUS_LIBS) + + +####### policy /etc/hosts test ####### +test_policy_hosts_SOURCES = \ + test-policy-hosts.c + +test_policy_hosts_CPPFLAGS = \ + $(GLIB_CFLAGS) + +test_policy_hosts_LDADD = \ + -ldl \ + $(top_builddir)/src/libtest-policy-hosts.la \ + $(GLIB_LIBS) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/tests/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +test-dhcp-options$(EXEEXT): $(test_dhcp_options_OBJECTS) $(test_dhcp_options_DEPENDENCIES) + @rm -f test-dhcp-options$(EXEEXT) + $(LINK) $(test_dhcp_options_OBJECTS) $(test_dhcp_options_LDADD) $(LIBS) +test-policy-hosts$(EXEEXT): $(test_policy_hosts_OBJECTS) $(test_policy_hosts_DEPENDENCIES) + @rm -f test-policy-hosts$(EXEEXT) + $(LINK) $(test_policy_hosts_OBJECTS) $(test_policy_hosts_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dhcp_options-test-dhcp-options.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_policy_hosts-test-policy-hosts.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +test_dhcp_options-test-dhcp-options.o: test-dhcp-options.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_dhcp_options_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_dhcp_options-test-dhcp-options.o -MD -MP -MF $(DEPDIR)/test_dhcp_options-test-dhcp-options.Tpo -c -o test_dhcp_options-test-dhcp-options.o `test -f 'test-dhcp-options.c' || echo '$(srcdir)/'`test-dhcp-options.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/test_dhcp_options-test-dhcp-options.Tpo $(DEPDIR)/test_dhcp_options-test-dhcp-options.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-dhcp-options.c' object='test_dhcp_options-test-dhcp-options.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_dhcp_options_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_dhcp_options-test-dhcp-options.o `test -f 'test-dhcp-options.c' || echo '$(srcdir)/'`test-dhcp-options.c + +test_dhcp_options-test-dhcp-options.obj: test-dhcp-options.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_dhcp_options_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_dhcp_options-test-dhcp-options.obj -MD -MP -MF $(DEPDIR)/test_dhcp_options-test-dhcp-options.Tpo -c -o test_dhcp_options-test-dhcp-options.obj `if test -f 'test-dhcp-options.c'; then $(CYGPATH_W) 'test-dhcp-options.c'; else $(CYGPATH_W) '$(srcdir)/test-dhcp-options.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/test_dhcp_options-test-dhcp-options.Tpo $(DEPDIR)/test_dhcp_options-test-dhcp-options.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-dhcp-options.c' object='test_dhcp_options-test-dhcp-options.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_dhcp_options_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_dhcp_options-test-dhcp-options.obj `if test -f 'test-dhcp-options.c'; then $(CYGPATH_W) 'test-dhcp-options.c'; else $(CYGPATH_W) '$(srcdir)/test-dhcp-options.c'; fi` + +test_policy_hosts-test-policy-hosts.o: test-policy-hosts.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_policy_hosts_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_policy_hosts-test-policy-hosts.o -MD -MP -MF $(DEPDIR)/test_policy_hosts-test-policy-hosts.Tpo -c -o test_policy_hosts-test-policy-hosts.o `test -f 'test-policy-hosts.c' || echo '$(srcdir)/'`test-policy-hosts.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/test_policy_hosts-test-policy-hosts.Tpo $(DEPDIR)/test_policy_hosts-test-policy-hosts.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-policy-hosts.c' object='test_policy_hosts-test-policy-hosts.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_policy_hosts_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_policy_hosts-test-policy-hosts.o `test -f 'test-policy-hosts.c' || echo '$(srcdir)/'`test-policy-hosts.c + +test_policy_hosts-test-policy-hosts.obj: test-policy-hosts.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_policy_hosts_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_policy_hosts-test-policy-hosts.obj -MD -MP -MF $(DEPDIR)/test_policy_hosts-test-policy-hosts.Tpo -c -o test_policy_hosts-test-policy-hosts.obj `if test -f 'test-policy-hosts.c'; then $(CYGPATH_W) 'test-policy-hosts.c'; else $(CYGPATH_W) '$(srcdir)/test-policy-hosts.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/test_policy_hosts-test-policy-hosts.Tpo $(DEPDIR)/test_policy_hosts-test-policy-hosts.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-policy-hosts.c' object='test_policy_hosts-test-policy-hosts.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_policy_hosts_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_policy_hosts-test-policy-hosts.obj `if test -f 'test-policy-hosts.c'; then $(CYGPATH_W) 'test-policy-hosts.c'; else $(CYGPATH_W) '$(srcdir)/test-policy-hosts.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +@WITH_TESTS_FALSE@check-local: +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-local +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am check-local clean \ + clean-generic clean-libtool clean-noinstPROGRAMS ctags \ + distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am + + +@WITH_TESTS_TRUE@check-local: test-dhcp-options +@WITH_TESTS_TRUE@ $(abs_builddir)/test-dhcp-options +@WITH_TESTS_TRUE@ $(abs_builddir)/test-policy-hosts + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/tests/test-dhcp-options.c b/src/tests/test-dhcp-options.c new file mode 100644 index 00000000..dd1f9149 --- /dev/null +++ b/src/tests/test-dhcp-options.c @@ -0,0 +1,715 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-dhcp-manager.c - Handle the DHCP daemon for NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 - 2010 Red Hat, Inc. + * + */ + +#include <glib.h> +#include <dbus/dbus-glib.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "nm-test-helpers.h" +#include <nm-utils.h> + +#include "nm-dhcp-manager.h" + +typedef struct { + const char *name; + const char *value; +} Option; + +static void +destroy_gvalue (gpointer data) +{ + GValue *value = (GValue *) data; + + g_value_unset (value); + g_slice_free (GValue, value); +} + +static GValue * +string_to_byte_array_gvalue (const char *str) +{ + GByteArray *array; + GValue *val; + + array = g_byte_array_sized_new (strlen (str)); + g_byte_array_append (array, (const guint8 *) str, strlen (str)); + + val = g_slice_new0 (GValue); + g_value_init (val, DBUS_TYPE_G_UCHAR_ARRAY); + g_value_take_boxed (val, array); + + return val; +} + +static GHashTable * +fill_table (Option *test_options, GHashTable *table) +{ + Option *opt; + + if (!table) + table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, destroy_gvalue); + for (opt = test_options; opt->name; opt++) { + g_hash_table_insert (table, + (gpointer) opt->name, + string_to_byte_array_gvalue (opt->value)); + } + return table; +} + +static Option generic_options[] = { + { "new_subnet_mask", "255.255.255.0" }, + { "new_ip_address", "192.168.1.106" }, + { "new_network_number", "192.168.1.0" }, + { "interface", "eth0" }, + { "reason", "BOUND" }, + { "new_expiry", "1232324877" }, + { "new_dhcp_lease_time", "3600" }, + { "new_dhcp_server_identifier", "192.168.1.1" }, + { "new_routers", "192.168.1.1" }, + { "new_domain_name_servers", "216.254.95.2 216.231.41.2" }, + { "new_dhcp_message_type", "5" }, + { "new_broadcast_address", "192.168.1.255" }, + { "new_domain_search", "foobar.com blah.foobar.com" }, + { "new_host_name", "nmreallywhipsthe" }, + { "new_domain_name", "lamasass.com" }, + { "new_interface_mtu", "987" }, + { "new_static_routes", "10.1.1.5 10.1.1.1 100.99.88.56 10.1.1.1" }, + { NULL, NULL } +}; + +static void +test_generic_options (const char *client) +{ + GHashTable *options; + NMIP4Config *ip4_config; + NMIP4Address *addr; + NMIP4Route *route; + struct in_addr tmp; + const char *expected_addr = "192.168.1.106"; + const char *expected_gw = "192.168.1.1"; + const char *expected_dns1 = "216.254.95.2"; + const char *expected_dns2 = "216.231.41.2"; + const char *expected_search1 = "foobar.com"; + const char *expected_search2 = "blah.foobar.com"; + const char *expected_route1_dest = "10.1.1.5"; + const char *expected_route1_gw = "10.1.1.1"; + const char *expected_route2_dest = "100.99.88.56"; + const char *expected_route2_gw = "10.1.1.1"; + + options = fill_table (generic_options, NULL); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); + ASSERT (ip4_config != NULL, + "dhcp-generic", "failed to parse DHCP4 options"); + + /* IP4 address */ + ASSERT (nm_ip4_config_get_num_addresses (ip4_config) == 1, + "dhcp-generic", "unexpected number of IP addresses"); + addr = nm_ip4_config_get_address (ip4_config, 0); + + ASSERT (inet_pton (AF_INET, expected_addr, &tmp) > 0, + "dhcp-generic", "couldn't convert expected IP address"); + ASSERT (nm_ip4_address_get_address (addr) == tmp.s_addr, + "dhcp-generic", "unexpected IP address"); + + ASSERT (nm_ip4_address_get_prefix (addr) == 24, + "dhcp-generic", "unexpected IP address prefix length"); + + /* Gateway */ + ASSERT (inet_pton (AF_INET, expected_gw, &tmp) > 0, + "dhcp-generic", "couldn't convert expected IP gateway"); + ASSERT (nm_ip4_address_get_gateway (addr) == tmp.s_addr, + "dhcp-generic", "unexpected IP gateway"); + + ASSERT (nm_ip4_config_get_ptp_address (ip4_config) == 0, + "dhcp-generic", "unexpected PTP address"); + + ASSERT (nm_ip4_config_get_num_wins (ip4_config) == 0, + "dhcp-generic", "unexpected number of WINS servers"); + + ASSERT (nm_ip4_config_get_mtu (ip4_config) == 987, + "dhcp-generic", "unexpected MTU"); + + /* Domain searches */ + ASSERT (nm_ip4_config_get_num_searches (ip4_config) == 2, + "dhcp-generic", "unexpected number of domain searches"); + ASSERT (strcmp (nm_ip4_config_get_search (ip4_config, 0), expected_search1) == 0, + "dhcp-generic", "unexpected domain search #1"); + ASSERT (strcmp (nm_ip4_config_get_search (ip4_config, 1), expected_search2) == 0, + "dhcp-generic", "unexpected domain search #2"); + + /* DNS servers */ + ASSERT (nm_ip4_config_get_num_nameservers (ip4_config) == 2, + "dhcp-generic", "unexpected number of domain name servers"); + ASSERT (inet_pton (AF_INET, expected_dns1, &tmp) > 0, + "dhcp-generic", "couldn't convert expected DNS server address #1"); + ASSERT (nm_ip4_config_get_nameserver (ip4_config, 0) == tmp.s_addr, + "dhcp-generic", "unexpected domain name server #1"); + ASSERT (inet_pton (AF_INET, expected_dns2, &tmp) > 0, + "dhcp-generic", "couldn't convert expected DNS server address #2"); + ASSERT (nm_ip4_config_get_nameserver (ip4_config, 1) == tmp.s_addr, + "dhcp-generic", "unexpected domain name server #2"); + + /* Routes */ + ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2, + "dhcp-generic", "unexpected number of routes"); + + /* Route #1 */ + route = nm_ip4_config_get_route (ip4_config, 0); + ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0, + "dhcp-generic", "couldn't convert expected route destination #1"); + ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr, + "dhcp-generic", "unexpected route #1 destination"); + + ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0, + "dhcp-generic", "couldn't convert expected route next hop #1"); + ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr, + "dhcp-generic", "unexpected route #1 next hop"); + + ASSERT (nm_ip4_route_get_prefix (route) == 32, + "dhcp-generic", "unexpected route #1 prefix"); + ASSERT (nm_ip4_route_get_metric (route) == 0, + "dhcp-generic", "unexpected route #1 metric"); + + /* Route #2 */ + route = nm_ip4_config_get_route (ip4_config, 1); + ASSERT (inet_pton (AF_INET, expected_route2_dest, &tmp) > 0, + "dhcp-generic", "couldn't convert expected route destination #2"); + ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr, + "dhcp-generic", "unexpected route #2 destination"); + + ASSERT (inet_pton (AF_INET, expected_route2_gw, &tmp) > 0, + "dhcp-generic", "couldn't convert expected route next hop #2"); + ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr, + "dhcp-generic", "unexpected route #2 next hop"); + + ASSERT (nm_ip4_route_get_prefix (route) == 32, + "dhcp-generic", "unexpected route #2 prefix"); + ASSERT (nm_ip4_route_get_metric (route) == 0, + "dhcp-generic", "unexpected route #2 metric"); + + g_hash_table_destroy (options); +} + +static Option wins_options[] = { + { "new_netbios_name_servers", "63.12.199.5 150.4.88.120" }, + { NULL, NULL } +}; + +static void +test_wins_options (const char *client) +{ + GHashTable *options; + NMIP4Config *ip4_config; + NMIP4Address *addr; + struct in_addr tmp; + const char *expected_wins1 = "63.12.199.5"; + const char *expected_wins2 = "150.4.88.120"; + + options = fill_table (generic_options, NULL); + options = fill_table (wins_options, options); + + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); + ASSERT (ip4_config != NULL, + "dhcp-wins", "failed to parse DHCP4 options"); + + /* IP4 address */ + ASSERT (nm_ip4_config_get_num_addresses (ip4_config) == 1, + "dhcp-wins", "unexpected number of IP addresses"); + addr = nm_ip4_config_get_address (ip4_config, 0); + + ASSERT (nm_ip4_config_get_num_wins (ip4_config) == 2, + "dhcp-wins", "unexpected number of WINS servers"); + ASSERT (inet_pton (AF_INET, expected_wins1, &tmp) > 0, + "dhcp-wins", "couldn't convert expected WINS server address #1"); + ASSERT (nm_ip4_config_get_wins (ip4_config, 0) == tmp.s_addr, + "dhcp-wins", "unexpected WINS server #1"); + ASSERT (inet_pton (AF_INET, expected_wins2, &tmp) > 0, + "dhcp-wins", "couldn't convert expected WINS server address #1"); + ASSERT (nm_ip4_config_get_wins (ip4_config, 1) == tmp.s_addr, + "dhcp-wins", "unexpected WINS server #1"); + + g_hash_table_destroy (options); +} + +static Option classless_routes_options[] = { + /* For dhclient */ + { "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 8 10 10 17 66 41" }, + /* For dhcpcd */ + { "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.0.0/8 10.17.66.41" }, + { NULL, NULL } +}; + +static void +test_classless_static_routes (const char *client) +{ + GHashTable *options; + NMIP4Config *ip4_config; + NMIP4Route *route; + struct in_addr tmp; + const char *expected_route1_dest = "192.168.10.0"; + const char *expected_route1_gw = "192.168.1.1"; + const char *expected_route2_dest = "10.0.0.0"; + const char *expected_route2_gw = "10.17.66.41"; + + options = fill_table (generic_options, NULL); + options = fill_table (classless_routes_options, options); + + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); + ASSERT (ip4_config != NULL, + "dhcp-rfc3442", "failed to parse DHCP4 options"); + + /* IP4 routes */ + ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2, + "dhcp-rfc3442", "unexpected number of IP routes"); + + /* Route #1 */ + route = nm_ip4_config_get_route (ip4_config, 0); + ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0, + "dhcp-rfc3442", "couldn't convert expected route destination #1"); + ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr, + "dhcp-rfc3442", "unexpected route #1 destination"); + + ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0, + "dhcp-rfc3442", "couldn't convert expected route next hop #1"); + ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr, + "dhcp-rfc3442", "unexpected route #1 next hop"); + + ASSERT (nm_ip4_route_get_prefix (route) == 24, + "dhcp-rfc3442", "unexpected route #1 prefix"); + ASSERT (nm_ip4_route_get_metric (route) == 0, + "dhcp-rfc3442", "unexpected route #1 metric"); + + /* Route #2 */ + route = nm_ip4_config_get_route (ip4_config, 1); + ASSERT (inet_pton (AF_INET, expected_route2_dest, &tmp) > 0, + "dhcp-rfc3442", "couldn't convert expected route destination #2"); + ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr, + "dhcp-rfc3442", "unexpected route #2 destination"); + + ASSERT (inet_pton (AF_INET, expected_route2_gw, &tmp) > 0, + "dhcp-rfc3442", "couldn't convert expected route next hop #2"); + ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr, + "dhcp-rfc3442", "unexpected route #2 next hop"); + + ASSERT (nm_ip4_route_get_prefix (route) == 8, + "dhcp-rfc3442", "unexpected route #2 prefix"); + ASSERT (nm_ip4_route_get_metric (route) == 0, + "dhcp-rfc3442", "unexpected route #2 metric"); + + g_hash_table_destroy (options); +} + +static Option invalid_classless_routes1[] = { + /* For dhclient */ + { "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 45 10 17 66 41" }, + /* For dhcpcd */ + { "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.adfadf/44 10.17.66.41" }, + { NULL, NULL } +}; + +static void +test_invalid_classless_routes1 (const char *client) +{ + GHashTable *options; + NMIP4Config *ip4_config; + NMIP4Route *route; + struct in_addr tmp; + const char *expected_route1_dest = "192.168.10.0"; + const char *expected_route1_gw = "192.168.1.1"; + + options = fill_table (generic_options, NULL); + options = fill_table (invalid_classless_routes1, options); + + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); + ASSERT (ip4_config != NULL, + "dhcp-rfc3442-invalid-1", "failed to parse DHCP4 options"); + + /* IP4 routes */ + ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 1, + "dhcp-rfc3442-invalid-1", "unexpected number of IP routes"); + + /* Route #1 */ + route = nm_ip4_config_get_route (ip4_config, 0); + ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0, + "dhcp-rfc3442-invalid-1", "couldn't convert expected route destination #1"); + ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr, + "dhcp-rfc3442-invalid-1", "unexpected route #1 destination"); + + ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0, + "dhcp-rfc3442-invalid-1", "couldn't convert expected route next hop #1"); + ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr, + "dhcp-rfc3442-invalid-1", "unexpected route #1 next hop"); + + ASSERT (nm_ip4_route_get_prefix (route) == 24, + "dhcp-rfc3442-invalid-1", "unexpected route #1 prefix"); + ASSERT (nm_ip4_route_get_metric (route) == 0, + "dhcp-rfc3442-invalid-1", "unexpected route #1 metric"); + + g_hash_table_destroy (options); +} + +static Option invalid_classless_routes2[] = { + /* For dhclient */ + { "new_rfc3442_classless_static_routes", "45 10 17 66 41 24 192 168 10 192 168 1 1" }, + /* For dhcpcd */ + { "new_classless_static_routes", "10.0.adfadf/44 10.17.66.41 192.168.10.0/24 192.168.1.1" }, + { NULL, NULL } +}; + +static void +test_invalid_classless_routes2 (const char *client) +{ + GHashTable *options; + NMIP4Config *ip4_config; + NMIP4Route *route; + struct in_addr tmp; + const char *expected_route1_dest = "10.1.1.5"; + const char *expected_route1_gw = "10.1.1.1"; + const char *expected_route2_dest = "100.99.88.56"; + const char *expected_route2_gw = "10.1.1.1"; + + options = fill_table (generic_options, NULL); + options = fill_table (invalid_classless_routes2, options); + + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); + ASSERT (ip4_config != NULL, + "dhcp-rfc3442-invalid-2", "failed to parse DHCP4 options"); + + /* Test falling back to old-style static routes if the classless static + * routes are invalid. + */ + + /* Routes */ + ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2, + "dhcp-rfc3442-invalid-2", "unexpected number of routes"); + + /* Route #1 */ + route = nm_ip4_config_get_route (ip4_config, 0); + ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0, + "dhcp-rfc3442-invalid-2", "couldn't convert expected route destination #1"); + ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr, + "dhcp-rfc3442-invalid-2", "unexpected route #1 destination"); + + ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0, + "dhcp-rfc3442-invalid-2", "couldn't convert expected route next hop #1"); + ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr, + "dhcp-rfc3442-invalid-2", "unexpected route #1 next hop"); + + ASSERT (nm_ip4_route_get_prefix (route) == 32, + "dhcp-rfc3442-invalid-2", "unexpected route #1 prefix"); + ASSERT (nm_ip4_route_get_metric (route) == 0, + "dhcp-rfc3442-invalid-2", "unexpected route #1 metric"); + + /* Route #2 */ + route = nm_ip4_config_get_route (ip4_config, 1); + ASSERT (inet_pton (AF_INET, expected_route2_dest, &tmp) > 0, + "dhcp-rfc3442-invalid-2", "couldn't convert expected route destination #2"); + ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr, + "dhcp-rfc3442-invalid-2", "unexpected route #2 destination"); + + ASSERT (inet_pton (AF_INET, expected_route2_gw, &tmp) > 0, + "dhcp-rfc3442-invalid-2", "couldn't convert expected route next hop #2"); + ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr, + "dhcp-rfc3442-invalid-2", "unexpected route #2 next hop"); + + ASSERT (nm_ip4_route_get_prefix (route) == 32, + "dhcp-rfc3442-invalid-2", "unexpected route #2 prefix"); + ASSERT (nm_ip4_route_get_metric (route) == 0, + "dhcp-rfc3442-invalid-2", "unexpected route #2 metric"); + + g_hash_table_destroy (options); +} + +static Option invalid_classless_routes3[] = { + /* For dhclient */ + { "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 32 128 10 17 66 41" }, + /* For dhcpcd */ + { "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 128/32 10.17.66.41" }, + { NULL, NULL } +}; + +static void +test_invalid_classless_routes3 (const char *client) +{ + GHashTable *options; + NMIP4Config *ip4_config; + NMIP4Route *route; + struct in_addr tmp; + const char *expected_route1_dest = "192.168.10.0"; + const char *expected_route1_gw = "192.168.1.1"; + + options = fill_table (generic_options, NULL); + options = fill_table (invalid_classless_routes3, options); + + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); + ASSERT (ip4_config != NULL, + "dhcp-rfc3442-invalid-3", "failed to parse DHCP4 options"); + + /* IP4 routes */ + ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 1, + "dhcp-rfc3442-invalid-3", "unexpected number of IP routes"); + + /* Route #1 */ + route = nm_ip4_config_get_route (ip4_config, 0); + ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0, + "dhcp-rfc3442-invalid-3", "couldn't convert expected route destination #1"); + ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr, + "dhcp-rfc3442-invalid-3", "unexpected route #1 destination"); + + ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0, + "dhcp-rfc3442-invalid-3", "couldn't convert expected route next hop #1"); + ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr, + "dhcp-rfc3442-invalid-3", "unexpected route #1 next hop"); + + ASSERT (nm_ip4_route_get_prefix (route) == 24, + "dhcp-rfc3442-invalid-3", "unexpected route #1 prefix"); + ASSERT (nm_ip4_route_get_metric (route) == 0, + "dhcp-rfc3442-invalid-3", "unexpected route #1 metric"); + + g_hash_table_destroy (options); +} + +static Option gw_in_classless_routes[] = { + /* For dhclient */ + { "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 0 192 2 3 4" }, + /* For dhcpcd */ + { "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 0.0.0.0/0 192.2.3.4" }, + { NULL, NULL } +}; + +static void +test_gateway_in_classless_routes (const char *client) +{ + GHashTable *options; + NMIP4Config *ip4_config; + NMIP4Address *addr; + NMIP4Route *route; + struct in_addr tmp; + const char *expected_route1_dest = "192.168.10.0"; + const char *expected_route1_gw = "192.168.1.1"; + const char *expected_gateway = "192.2.3.4"; + + options = fill_table (generic_options, NULL); + options = fill_table (gw_in_classless_routes, options); + + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); + ASSERT (ip4_config != NULL, + "dhcp-rfc3442-gateway", "failed to parse DHCP4 options"); + + /* IP4 routes */ + ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 1, + "dhcp-rfc3442-gateway", "unexpected number of IP routes"); + + /* Route #1 */ + route = nm_ip4_config_get_route (ip4_config, 0); + ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0, + "dhcp-rfc3442-gateway", "couldn't convert expected route destination #1"); + ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr, + "dhcp-rfc3442-gateway", "unexpected route #1 destination"); + + ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0, + "dhcp-rfc3442-gateway", "couldn't convert expected route next hop #1"); + ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr, + "dhcp-rfc3442-gateway", "unexpected route #1 next hop"); + + ASSERT (nm_ip4_route_get_prefix (route) == 24, + "dhcp-rfc3442-gateway", "unexpected route #1 prefix"); + ASSERT (nm_ip4_route_get_metric (route) == 0, + "dhcp-rfc3442-gateway", "unexpected route #1 metric"); + + /* Address */ + ASSERT (nm_ip4_config_get_num_addresses (ip4_config) == 1, + "dhcp-rfc3442-gateway", "unexpected number of IP addresses"); + addr = nm_ip4_config_get_address (ip4_config, 0); + ASSERT (inet_pton (AF_INET, expected_gateway, &tmp) > 0, + "dhcp-rfc3442-gateway", "couldn't convert expected IP gateway"); + ASSERT (nm_ip4_address_get_gateway (addr) == tmp.s_addr, + "dhcp-rfc3442-gateway", "unexpected IP gateway"); + + g_hash_table_destroy (options); +} + +static Option escaped_searches_options[] = { + { "new_domain_search", "host1\\032host2\\032host3" }, + { NULL, NULL } +}; + +static void +test_escaped_domain_searches (const char *client) +{ + GHashTable *options; + NMIP4Config *ip4_config; + const char *expected_search0 = "host1"; + const char *expected_search1 = "host2"; + const char *expected_search2 = "host3"; + + options = fill_table (generic_options, NULL); + options = fill_table (escaped_searches_options, options); + + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); + ASSERT (ip4_config != NULL, + "dhcp-escaped-domain-searches", "failed to parse DHCP4 options"); + + /* domain searches */ + ASSERT (nm_ip4_config_get_num_searches (ip4_config) == 3, + "dhcp-escaped-domain-searches", "unexpected number of searches"); + ASSERT (!strcmp (nm_ip4_config_get_search (ip4_config, 0), expected_search0), + "dhcp-escaped-domain-searches", "unexpected domain search #1"); + ASSERT (!strcmp (nm_ip4_config_get_search (ip4_config, 1), expected_search1), + "dhcp-escaped-domain-searches", "unexpected domain search #1"); + ASSERT (!strcmp (nm_ip4_config_get_search (ip4_config, 2), expected_search2), + "dhcp-escaped-domain-searches", "unexpected domain search #1"); + + g_hash_table_destroy (options); +} + +static Option invalid_escaped_searches_options[] = { + { "new_domain_search", "host1\\aahost2\\032host3" }, + { NULL, NULL } +}; + +static void +test_invalid_escaped_domain_searches (const char *client) +{ + GHashTable *options; + NMIP4Config *ip4_config; + + options = fill_table (generic_options, NULL); + options = fill_table (invalid_escaped_searches_options, options); + + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); + ASSERT (ip4_config != NULL, + "dhcp-invalid-escaped-domain-searches", "failed to parse DHCP4 options"); + + /* domain searches */ + ASSERT (nm_ip4_config_get_num_searches (ip4_config) == 0, + "dhcp-invalid-escaped-domain-searches", "unexpected domain searches"); + + g_hash_table_destroy (options); +} + +static void +test_ip4_missing_prefix (const char *client, const char *ip, guint32 expected_prefix) +{ + GHashTable *options; + NMIP4Config *ip4_config; + NMIP4Address *addr; + + options = fill_table (generic_options, NULL); + g_hash_table_insert (options, "new_ip_address", string_to_byte_array_gvalue (ip)); + g_hash_table_remove (options, "new_subnet_mask"); + + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); + ASSERT (ip4_config != NULL, + "dhcp-ip4-missing-prefix", "failed to parse DHCP4 options"); + + ASSERT (nm_ip4_config_get_num_addresses (ip4_config) == 1, + "dhcp-ip4-missing-prefix", "unexpected number of IP4 addresses (not 1)"); + + addr = nm_ip4_config_get_address (ip4_config, 0); + ASSERT (addr != NULL, + "dhcp-ip4-missing-prefix", "missing IP4 address #1"); + + ASSERT (nm_ip4_address_get_prefix (addr) == expected_prefix, + "dhcp-ip4-missing-prefix", "unexpected IP4 address prefix %d (expected %d)", + nm_ip4_address_get_prefix (addr), expected_prefix); + + g_hash_table_destroy (options); +} + +static void +test_ip4_prefix_classless (const char *client) +{ + GHashTable *options; + NMIP4Config *ip4_config; + NMIP4Address *addr; + + /* Ensure that the missing-subnet-mask handler doesn't mangle classless + * subnet masks at all. The handler should trigger only if the server + * doesn't send the subnet mask. + */ + + options = fill_table (generic_options, NULL); + g_hash_table_insert (options, "new_ip_address", string_to_byte_array_gvalue ("172.16.54.22")); + g_hash_table_insert (options, "new_subnet_mask", string_to_byte_array_gvalue ("255.255.252.0")); + + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); + ASSERT (ip4_config != NULL, + "dhcp-ip4-prefix-classless", "failed to parse DHCP4 options"); + + ASSERT (nm_ip4_config_get_num_addresses (ip4_config) == 1, + "dhcp-ip4-prefix-classless", "unexpected number of IP4 addresses (not 1)"); + + addr = nm_ip4_config_get_address (ip4_config, 0); + ASSERT (addr != NULL, + "dhcp-ip4-prefix-classless", "missing IP4 address #1"); + + ASSERT (nm_ip4_address_get_prefix (addr) == 22, + "dhcp-ip4-prefix-classless", "unexpected IP4 address prefix %d (expected 22)", + nm_ip4_address_get_prefix (addr)); + + g_hash_table_destroy (options); +} + +int main (int argc, char **argv) +{ + GError *error = NULL; + DBusGConnection *bus; + char *base; + const char *clients[2][2] = { {DHCLIENT_PATH, "dhclient"}, {DHCPCD_PATH, "dhcpcd"} }; + guint32 i; + + g_type_init (); + bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); + + if (!nm_utils_init (&error)) + FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message); + + /* The tests */ + for (i = 0; i < 2; i++) { + const char *client_path = clients[i][0]; + const char *client = clients[i][1]; + + if (!client_path || !strlen (client_path)) + continue; + + test_generic_options (client); + test_wins_options (client); + test_classless_static_routes (client); + test_invalid_classless_routes1 (client); + test_invalid_classless_routes2 (client); + test_invalid_classless_routes3 (client); + test_gateway_in_classless_routes (client); + test_escaped_domain_searches (client); + test_invalid_escaped_domain_searches (client); + test_ip4_missing_prefix (client, "192.168.1.10", 24); + test_ip4_missing_prefix (client, "172.16.54.50", 16); + test_ip4_missing_prefix (client, "10.1.2.3", 8); + test_ip4_prefix_classless (client); + } + + base = g_path_get_basename (argv[0]); + fprintf (stdout, "%s: SUCCESS\n", base); + g_free (base); + return 0; +} + diff --git a/src/tests/test-policy-hosts.c b/src/tests/test-policy-hosts.c new file mode 100644 index 00000000..e14f15e5 --- /dev/null +++ b/src/tests/test-policy-hosts.c @@ -0,0 +1,394 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2010 Red Hat, Inc. + * + */ + +#include <glib.h> +#include <string.h> + +#include "nm-policy-hosts.h" + +#define FALLBACK_HOSTNAME "localhost.localdomain" + +static void +test_generic (const char *before, + const char *after, + const char *hostname, + gboolean expect_error) +{ + char **lines; + GString *newc; + GError *error = NULL; + + /* Get the new /etc/hosts contents */ + lines = g_strsplit_set (before, "\n\r", 0); + newc = nm_policy_get_etc_hosts ((const char **) lines, + strlen (before), + hostname, + FALLBACK_HOSTNAME, + &error); + g_strfreev (lines); + + if (expect_error) { + g_assert (newc == NULL); + g_assert (error != NULL); + g_clear_error (&error); + } else if (after == NULL) { + /* No change to /etc/hosts required */ + g_assert (newc == NULL); + g_assert (error == NULL); + } else { + g_assert (newc != NULL); + g_assert (error == NULL); + +#if 0 + g_message ("\n--------------------------------------\n" + "%s" + "--------------------------------------", + newc->str); +#endif + g_assert (strlen (newc->str) == strlen (after)); + g_assert (strcmp (newc->str, after) == 0); + g_string_free (newc, TRUE); + } +} + +/*******************************************/ + +static const char *generic_before = \ + "# Do not remove the following line, or various programs\n" + "# that require network functionality will fail.\n" + "127.0.0.1 localhost.localdomain localhost\n" + "::1 localhost6.localdomain6 localhost6\n" + "127.0.0.1 lcmd.us.intellitxt.com\n"; + +static void +test_hosts_generic (void) +{ + test_generic (generic_before, NULL, "localhost.localdomain", FALSE); +} + +/*******************************************/ + +static const char *generic_no_boilerplate_before = \ + "127.0.0.1 localhost.localdomain localhost\n" + "::1 localhost6.localdomain6 localhost6\n" + "127.0.0.1 lcmd.us.intellitxt.com\n"; + +static void +test_hosts_generic_no_boilerplate (void) +{ + test_generic (generic_no_boilerplate_before, NULL, "localhost.localdomain", FALSE); +} + +/*******************************************/ + +static const char *generic_no_boilerplate_no_lh_before = \ + "127.0.0.1 localhost.localdomain\n" + "::1 localhost6.localdomain6 localhost6\n" + "127.0.0.1 lcmd.us.intellitxt.com\n"; + +static const char *generic_no_boilerplate_no_lh_after = \ + "127.0.0.1 localhost\n" + "127.0.0.1 localhost.localdomain\n" + "::1 localhost6.localdomain6 localhost6\n" + "127.0.0.1 lcmd.us.intellitxt.com\n"; + +static void +test_hosts_generic_no_boilerplate_no_lh (void) +{ + test_generic (generic_no_boilerplate_no_lh_before, + generic_no_boilerplate_no_lh_after, + "localhost.localdomain", + FALSE); +} + +/*******************************************/ + + +static const char *generic_no_boilerplate_no_lh_no_host_before = \ + "127.0.0.1 localhost.localdomain\n" + "::1 localhost6.localdomain6 localhost6\n" + "127.0.0.1 lcmd.us.intellitxt.com\n"; + +static const char *generic_no_boilerplate_no_lh_no_host_after = \ + "127.0.0.1 comet localhost.localdomain localhost\n" + "::1 localhost6.localdomain6 localhost6\n" + "127.0.0.1 lcmd.us.intellitxt.com\n"; + +static void +test_hosts_generic_no_boilerplate_no_lh_no_host (void) +{ + test_generic (generic_no_boilerplate_no_lh_no_host_before, + generic_no_boilerplate_no_lh_no_host_after, + "comet", + FALSE); +} + +/*******************************************/ +static const char *named_generic_before = \ + "# Do not remove the following line, or various programs\n" + "# that require network functionality will fail.\n" + "127.0.0.1 playboy localhost\n" + "::1 localhost6.localdomain6 localhost6\n" + "127.0.0.1 lcmd.us.intellitxt.com\n"; + +static void +test_hosts_named_generic (void) +{ + test_generic (named_generic_before, NULL, "playboy", FALSE); +} + +/*******************************************/ + +static const char *named_non127_before = \ + "# Do not remove the following line, or various programs\n" + "# that require network functionality will fail.\n" + "127.0.0.1 localhost.localdomain localhost\n" + "::1 localhost6.localdomain6 localhost6\n" + "127.0.0.1 lcmd.us.intellitxt.com\n" + "192.168.1.2 tomcat\n"; + +static void +test_hosts_named_non127 (void) +{ + test_generic (named_non127_before, NULL, "tomcat", FALSE); +} + +/*******************************************/ + +static const char *named2_non127_before = \ + "# Do not remove the following line, or various programs\n" + "# that require network functionality will fail.\n" + "127.0.0.1 localhost.localdomain localhost\n" + "::1 localhost6.localdomain6 localhost6\n" + "127.0.0.1 lcmd.us.intellitxt.com\n" + "192.168.1.2 tomcat\n" + "127.0.0.1 lcmd.us.intellitxt.com\n" + "127.0.0.1 srx.main.ebayrtm.com\n" + "127.0.0.1 cdn5.tribalfusion.com\n"; + +static void +test_hosts_named2_non127 (void) +{ + test_generic (named2_non127_before, NULL, "tomcat", FALSE); +} + +/*******************************************/ + +static const char *named_no_lh_before = \ + "# Do not remove the following line, or various programs\n" + "# that require network functionality will fail.\n" + "127.0.0.1 localhost.localdomain\n" + "::1 localhost6.localdomain6 localhost6\n" + "127.0.0.1 lcmd.us.intellitxt.com\n" + "192.168.1.2 tomcat\n"; + +static const char *named_no_lh_after = \ + "# Do not remove the following line, or various programs\n" + "# that require network functionality will fail.\n" + "127.0.0.1 localhost.localdomain localhost\n" + "::1 localhost6.localdomain6 localhost6\n" + "127.0.0.1 lcmd.us.intellitxt.com\n" + "192.168.1.2 tomcat\n"; + +static void +test_hosts_named_no_localhost (void) +{ + test_generic (named_no_lh_before, named_no_lh_after, "tomcat", FALSE); +} + +/*******************************************/ + +static const char *no_lh_before = \ + "# Do not remove the following line, or various programs\n" + "# that require network functionality will fail.\n" + "127.0.0.1 tomcat\n" + "::1 localhost6.localdomain6 localhost6\n" + "127.0.0.1 lcmd.us.intellitxt.com\n"; + +static const char *no_lh_after = \ + "# Do not remove the following line, or various programs\n" + "# that require network functionality will fail.\n" + "127.0.0.1 localhost.localdomain localhost\n" + "127.0.0.1 tomcat\n" + "::1 localhost6.localdomain6 localhost6\n" + "127.0.0.1 lcmd.us.intellitxt.com\n"; + +static void +test_hosts_no_localhost (void) +{ + test_generic (no_lh_before, no_lh_after, "tomcat", FALSE); +} + +/*******************************************/ + +static const char *named_last_before = \ + "# Do not remove the following line, or various programs\n" + "# that require network functionality will fail.\n" + "127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 sparcbook.ausil.us\n" + "::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 sparcbook.ausil.us\n"; + +static void +test_hosts_named_last (void) +{ + test_generic (named_last_before, NULL, "sparcbook.ausil.us", FALSE); +} + +/*******************************************/ + +static const char *no_host_before = \ + "# Do not remove the following line, or various programs\n" + "# that require network functionality will fail.\n" + "::1 localhost6.localdomain6 localhost6\n" + "\n" + "127.0.0.1 lcmd.us.intellitxt.com\n" + "127.0.0.1 srx.main.ebayrtm.com\n" + "127.0.0.1 cdn5.tribalfusion.com\n" + "127.0.0.1 a.tribalfusion.com\n"; + +static const char *no_host_after = \ + "# Do not remove the following line, or various programs\n" + "# that require network functionality will fail.\n" + "127.0.0.1 comet localhost.localdomain localhost\n" + "::1 localhost6.localdomain6 localhost6\n" + "\n" + "127.0.0.1 lcmd.us.intellitxt.com\n" + "127.0.0.1 srx.main.ebayrtm.com\n" + "127.0.0.1 cdn5.tribalfusion.com\n" + "127.0.0.1 a.tribalfusion.com\n"; + +static void +test_hosts_no_host (void) +{ + test_generic (no_host_before, no_host_after, "comet", FALSE); +} + +/*******************************************/ + +static const char *long_before = \ + "# Do not remove the following line, or various programs\n" + "# that require network functionality will fail.\n" + "127.0.0.1 localhost.localdomain localhost comet\n" + "::1 localhost6.localdomain6 localhost6\n" + "\n" + "127.0.0.1 lcmd.us.intellitxt.com\n" + "127.0.0.1 adserver.adtech.de\n" + "127.0.0.1 a.as-us.falkag.net\n" + "127.0.0.1 a.as-eu.falkag.net\n" + "127.0.0.1 ads.doubleclick.com\n" + "\n" + "# random comment\n" + "127.0.0.1 m1.2mdn.net\n" + "127.0.0.1 ds.serving-sys.com\n" + "127.0.0.1 pagead2.googlesyndication.com\n" + "127.0.0.1 ad.doubleclick.com\n" + "127.0.0.1 ad.doubleclick.net\n" + "127.0.0.1 oascentral.movietickets.com\n" + "127.0.0.1 view.atdmt.com\n" + "127.0.0.1 ads.chumcity.com\n" + "127.0.0.1 ads.as4x.tmcs.net\n" + "127.0.0.1 n4403ad.doubleclick.net\n" + "127.0.0.1 www.assoc-amazon.com\n" + "127.0.0.1 s25.sitemeter.com\n" + "127.0.0.1 adlog.com.com\n" + "127.0.0.1 ahs.laptopmag.com\n" + "127.0.0.1 altfarm.mediaplex.com\n" + "127.0.0.1 ads.addynamix.com\n" + "127.0.0.1 srx.main.ebayrtm.com\n" + "127.0.0.1 cdn5.tribalfusion.com\n" + "127.0.0.1 a.tribalfusion.com\n"; + + +static void +test_hosts_long (void) +{ + test_generic (long_before, NULL, "comet", FALSE); +} + +/*******************************************/ + +typedef struct { + const char *line; + const char *token; + gboolean expected; +} Foo; + +static Foo foo[] = { + /* Using \t here to easily differentiate tabs vs. spaces for testing */ + { "127.0.0.1\tfoobar\tblah", "blah", TRUE }, + { "", "blah", FALSE }, + { "1.1.1.1\tbork\tfoo", "blah", FALSE }, + { "127.0.0.1 foobar\tblah", "blah", TRUE }, + { "127.0.0.1 foobar blah", "blah", TRUE }, + { "127.0.0.1 localhost", "localhost.localdomain", FALSE }, + { "192.168.1.1 blah borkbork", "blah", TRUE }, + { "192.168.1.1 foobar\tblah borkbork", "blah", TRUE }, + { "192.168.1.1\tfoobar\tblah\tborkbork", "blah", TRUE }, + { "192.168.1.1 \tfoobar \tblah \tborkbork\t ", "blah", TRUE }, + { "\t\t\t\t \t\t\tasdfadf a\t\t\t\t\t \t\t\t\t\t ", "blah", FALSE }, + { NULL, NULL, FALSE } +}; + +static void +test_find_token (void) +{ + Foo *iter = &foo[0]; + + while (iter->line) { + gboolean found; + + found = nm_policy_hosts_find_token (iter->line, iter->token); + if (found != iter->expected) { + g_warning ("find-token: unexpected token result %d for '%s' <= '%s' (expected %d)", + found, iter->line, iter->token, iter->expected); + } + g_assert (found == iter->expected); + iter++; + } +} + +typedef void (*TCFunc)(void); + +#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL) + +int main (int argc, char **argv) +{ + GTestSuite *suite; + + g_test_init (&argc, &argv, NULL); + + suite = g_test_get_root (); + + g_test_suite_add (suite, TESTCASE (test_find_token, NULL)); + g_test_suite_add (suite, TESTCASE (test_hosts_generic, NULL)); + g_test_suite_add (suite, TESTCASE (test_hosts_generic_no_boilerplate, NULL)); + g_test_suite_add (suite, TESTCASE (test_hosts_generic_no_boilerplate_no_lh, NULL)); + g_test_suite_add (suite, TESTCASE (test_hosts_generic_no_boilerplate_no_lh_no_host, NULL)); + g_test_suite_add (suite, TESTCASE (test_hosts_named_generic, NULL)); + g_test_suite_add (suite, TESTCASE (test_hosts_named_non127, NULL)); + g_test_suite_add (suite, TESTCASE (test_hosts_named2_non127, NULL)); + g_test_suite_add (suite, TESTCASE (test_hosts_named_no_localhost, NULL)); + g_test_suite_add (suite, TESTCASE (test_hosts_no_localhost, NULL)); + g_test_suite_add (suite, TESTCASE (test_hosts_named_last, NULL)); + g_test_suite_add (suite, TESTCASE (test_hosts_no_host, NULL)); + g_test_suite_add (suite, TESTCASE (test_hosts_long, NULL)); + + return g_test_run (); +} + diff --git a/src/vpn-manager/Makefile.am b/src/vpn-manager/Makefile.am new file mode 100644 index 00000000..56e4a4ce --- /dev/null +++ b/src/vpn-manager/Makefile.am @@ -0,0 +1,46 @@ +INCLUDES = \ + -I${top_srcdir} \ + -I${top_srcdir}/include \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/src \ + -I${top_builddir}/marshallers \ + -I${top_srcdir}/src/named-manager \ + -DVPN_NAME_FILES_DIR=\""$(sysconfdir)/NetworkManager/VPN"\" + + +noinst_LTLIBRARIES = libvpn-manager.la + +libvpn_manager_la_SOURCES = \ + nm-vpn-manager.c \ + nm-vpn-manager.h \ + nm-vpn-service.c \ + nm-vpn-service.h \ + nm-vpn-connection.c \ + nm-vpn-connection.h + +libvpn_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + -DG_DISABLE_DEPRECATED + +libvpn_manager_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + +nm-vpn-connection-glue.h: $(top_srcdir)/introspection/nm-vpn-connection.xml + dbus-binding-tool --prefix=nm_vpn_connection --mode=glib-server --output=$@ $< + +nm-vpn-plugin-bindings.h: $(top_srcdir)/introspection/nm-vpn-plugin.xml + dbus-binding-tool --prefix=nm_vpn_plugin --mode=glib-client --output=$@ $< + + +BUILT_SOURCES = \ + nm-vpn-connection-glue.h \ + nm-vpn-plugin-bindings.h + +CLEANFILES = $(BUILT_SOURCES) + diff --git a/src/vpn-manager/Makefile.in b/src/vpn-manager/Makefile.in new file mode 100644 index 00000000..dc1ba727 --- /dev/null +++ b/src/vpn-manager/Makefile.in @@ -0,0 +1,611 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/vpn-manager +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libvpn_manager_la_DEPENDENCIES = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(top_builddir)/libnm-util/libnm-util.la $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_libvpn_manager_la_OBJECTS = libvpn_manager_la-nm-vpn-manager.lo \ + libvpn_manager_la-nm-vpn-service.lo \ + libvpn_manager_la-nm-vpn-connection.lo +libvpn_manager_la_OBJECTS = $(am_libvpn_manager_la_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libvpn_manager_la_SOURCES) +DIST_SOURCES = $(libvpn_manager_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +IPTABLES_PATH = @IPTABLES_PATH@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBM = @LIBM@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POFILES = @POFILES@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +RANLIB = @RANLIB@ +RESOLVCONF_PATH = @RESOLVCONF_PATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = \ + -I${top_srcdir} \ + -I${top_srcdir}/include \ + -I${top_srcdir}/libnm-util \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/src \ + -I${top_builddir}/marshallers \ + -I${top_srcdir}/src/named-manager \ + -DVPN_NAME_FILES_DIR=\""$(sysconfdir)/NetworkManager/VPN"\" + +noinst_LTLIBRARIES = libvpn-manager.la +libvpn_manager_la_SOURCES = \ + nm-vpn-manager.c \ + nm-vpn-manager.h \ + nm-vpn-service.c \ + nm-vpn-service.h \ + nm-vpn-connection.c \ + nm-vpn-connection.h + +libvpn_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + -DG_DISABLE_DEPRECATED + +libvpn_manager_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + +BUILT_SOURCES = \ + nm-vpn-connection-glue.h \ + nm-vpn-plugin-bindings.h + +CLEANFILES = $(BUILT_SOURCES) +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/vpn-manager/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/vpn-manager/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libvpn-manager.la: $(libvpn_manager_la_OBJECTS) $(libvpn_manager_la_DEPENDENCIES) + $(LINK) $(libvpn_manager_la_OBJECTS) $(libvpn_manager_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvpn_manager_la-nm-vpn-connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvpn_manager_la-nm-vpn-manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvpn_manager_la-nm-vpn-service.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libvpn_manager_la-nm-vpn-manager.lo: nm-vpn-manager.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libvpn_manager_la-nm-vpn-manager.lo -MD -MP -MF $(DEPDIR)/libvpn_manager_la-nm-vpn-manager.Tpo -c -o libvpn_manager_la-nm-vpn-manager.lo `test -f 'nm-vpn-manager.c' || echo '$(srcdir)/'`nm-vpn-manager.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libvpn_manager_la-nm-vpn-manager.Tpo $(DEPDIR)/libvpn_manager_la-nm-vpn-manager.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-vpn-manager.c' object='libvpn_manager_la-nm-vpn-manager.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libvpn_manager_la-nm-vpn-manager.lo `test -f 'nm-vpn-manager.c' || echo '$(srcdir)/'`nm-vpn-manager.c + +libvpn_manager_la-nm-vpn-service.lo: nm-vpn-service.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libvpn_manager_la-nm-vpn-service.lo -MD -MP -MF $(DEPDIR)/libvpn_manager_la-nm-vpn-service.Tpo -c -o libvpn_manager_la-nm-vpn-service.lo `test -f 'nm-vpn-service.c' || echo '$(srcdir)/'`nm-vpn-service.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libvpn_manager_la-nm-vpn-service.Tpo $(DEPDIR)/libvpn_manager_la-nm-vpn-service.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-vpn-service.c' object='libvpn_manager_la-nm-vpn-service.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libvpn_manager_la-nm-vpn-service.lo `test -f 'nm-vpn-service.c' || echo '$(srcdir)/'`nm-vpn-service.c + +libvpn_manager_la-nm-vpn-connection.lo: nm-vpn-connection.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libvpn_manager_la-nm-vpn-connection.lo -MD -MP -MF $(DEPDIR)/libvpn_manager_la-nm-vpn-connection.Tpo -c -o libvpn_manager_la-nm-vpn-connection.lo `test -f 'nm-vpn-connection.c' || echo '$(srcdir)/'`nm-vpn-connection.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libvpn_manager_la-nm-vpn-connection.Tpo $(DEPDIR)/libvpn_manager_la-nm-vpn-connection.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nm-vpn-connection.c' object='libvpn_manager_la-nm-vpn-connection.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libvpn_manager_la-nm-vpn-connection.lo `test -f 'nm-vpn-connection.c' || echo '$(srcdir)/'`nm-vpn-connection.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: all check install install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +nm-vpn-connection-glue.h: $(top_srcdir)/introspection/nm-vpn-connection.xml + dbus-binding-tool --prefix=nm_vpn_connection --mode=glib-server --output=$@ $< + +nm-vpn-plugin-bindings.h: $(top_srcdir)/introspection/nm-vpn-plugin.xml + dbus-binding-tool --prefix=nm_vpn_plugin --mode=glib-client --output=$@ $< + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c new file mode 100644 index 00000000..7eae972e --- /dev/null +++ b/src/vpn-manager/nm-vpn-connection.c @@ -0,0 +1,1192 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include <glib.h> +#include <string.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <stdlib.h> + +#include "NetworkManager.h" +#include "NetworkManagerVPN.h" +#include "nm-vpn-connection.h" +#include "nm-setting-connection.h" +#include "nm-setting-vpn.h" +#include "nm-setting-ip4-config.h" +#include "nm-dbus-manager.h" +#include "nm-manager.h" +#include "nm-system.h" +#include "nm-logging.h" +#include "nm-utils.h" +#include "nm-vpn-plugin-bindings.h" +#include "nm-marshal.h" +#include "nm-active-connection.h" +#include "nm-properties-changed-signal.h" +#include "nm-dbus-glib-types.h" +#include "NetworkManagerUtils.h" +#include "nm-named-manager.h" +#include "nm-netlink-monitor.h" +#include "nm-glib-compat.h" + +#include "nm-vpn-connection-glue.h" + +static void secrets_provider_interface_init (NMSecretsProviderInterface *sp_interface_class); + +G_DEFINE_TYPE_EXTENDED (NMVPNConnection, nm_vpn_connection, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (NM_TYPE_SECRETS_PROVIDER_INTERFACE, + secrets_provider_interface_init)) + +typedef struct { + gboolean disposed; + + NMConnection *connection; + + NMActRequest *act_request; + char *ac_path; + + NMDevice *parent_dev; + gulong device_monitor; + gulong device_ip4; + + gboolean is_default; + gboolean is_default6; + NMActiveConnectionState state; + + NMVPNConnectionState vpn_state; + NMVPNConnectionStateReason failure_reason; + DBusGProxy *proxy; + guint ipconfig_timeout; + NMIP4Config *ip4_config; + guint32 ip4_internal_gw; + char *tundev; + char *banner; + + struct rtnl_route *gw_route; +} NMVPNConnectionPrivate; + +#define NM_VPN_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_CONNECTION, NMVPNConnectionPrivate)) + +enum { + PROPERTIES_CHANGED, + VPN_STATE_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum { + PROP_0, + PROP_SERVICE_NAME, + PROP_CONNECTION, + PROP_SPECIFIC_OBJECT, + PROP_DEVICES, + PROP_STATE, + PROP_DEFAULT, + PROP_DEFAULT6, + PROP_VPN, + PROP_VPN_STATE, + PROP_BANNER, + + LAST_PROP +}; + +static void +nm_vpn_connection_set_vpn_state (NMVPNConnection *connection, + NMVPNConnectionState vpn_state, + NMVPNConnectionStateReason reason) +{ + NMVPNConnectionPrivate *priv; + NMActiveConnectionState new_ac_state; + NMVPNConnectionState old_vpn_state; + char *ip_iface; + + g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); + + priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + if (vpn_state == priv->vpn_state) + return; + + old_vpn_state = priv->vpn_state; + priv->vpn_state = vpn_state; + + /* Save ip_iface since when the VPN goes down it may get freed + * before we're done with it. + */ + ip_iface = g_strdup (priv->tundev); + + /* Set the NMActiveConnection state based on VPN state */ + switch (vpn_state) { + case NM_VPN_CONNECTION_STATE_PREPARE: + case NM_VPN_CONNECTION_STATE_NEED_AUTH: + case NM_VPN_CONNECTION_STATE_CONNECT: + case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET: + new_ac_state = NM_ACTIVE_CONNECTION_STATE_ACTIVATING; + break; + case NM_VPN_CONNECTION_STATE_ACTIVATED: + new_ac_state = NM_ACTIVE_CONNECTION_STATE_ACTIVATED; + break; + default: + new_ac_state = NM_ACTIVE_CONNECTION_STATE_UNKNOWN; + break; + } + + if (new_ac_state != priv->state) { + priv->state = new_ac_state; + g_object_notify (G_OBJECT (connection), NM_ACTIVE_CONNECTION_STATE); + } + + /* The connection gets destroyed by the VPN manager when it enters the + * disconnected/failed state, but we need to keep it around for a bit + * to send out signals and handle the dispatcher. So ref it. + */ + g_object_ref (connection); + + g_signal_emit (connection, signals[VPN_STATE_CHANGED], 0, vpn_state, reason); + g_object_notify (G_OBJECT (connection), NM_VPN_CONNECTION_VPN_STATE); + + /* Call dispatcher after the event gets processed internally */ + switch (vpn_state) { + case NM_VPN_CONNECTION_STATE_ACTIVATED: + nm_utils_call_dispatcher ("vpn-up", + priv->connection, + priv->parent_dev, + ip_iface); + break; + case NM_VPN_CONNECTION_STATE_FAILED: + case NM_VPN_CONNECTION_STATE_DISCONNECTED: + if (old_vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED) { + nm_utils_call_dispatcher ("vpn-down", + priv->connection, + priv->parent_dev, + ip_iface); + } + break; + default: + break; + } + + g_free (ip_iface); + g_object_unref (connection); +} + +static void +device_state_changed (NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason, + gpointer user_data) +{ + NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); + + if (new_state <= NM_DEVICE_STATE_DISCONNECTED) { + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_DISCONNECTED, + NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); + } else if (new_state == NM_DEVICE_STATE_FAILED) { + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_FAILED, + NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); + } +} + +static void +device_ip4_config_changed (NMDevice *device, + GParamSpec *pspec, + gpointer user_data) +{ + NMVPNConnection *vpn = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (vpn); + + if ( (priv->vpn_state != NM_VPN_CONNECTION_STATE_ACTIVATED) + || !nm_device_get_ip4_config (device)) + return; + + if (priv->gw_route) + rtnl_route_put (priv->gw_route); + + /* Re-add the VPN gateway route */ + priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev, priv->ip4_config); +} + +NMVPNConnection * +nm_vpn_connection_new (NMConnection *connection, + NMActRequest *act_request, + NMDevice *parent_device) +{ + NMVPNConnection *self; + NMVPNConnectionPrivate *priv; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (NM_IS_ACT_REQUEST (act_request), NULL); + g_return_val_if_fail (NM_IS_DEVICE (parent_device), NULL); + + self = (NMVPNConnection *) g_object_new (NM_TYPE_VPN_CONNECTION, NULL); + if (!self) + return NULL; + + priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + + priv->connection = g_object_ref (connection); + priv->parent_dev = g_object_ref (parent_device); + priv->act_request = g_object_ref (act_request); + + priv->device_monitor = g_signal_connect (parent_device, "state-changed", + G_CALLBACK (device_state_changed), + self); + + priv->device_ip4 = g_signal_connect (parent_device, "notify::" NM_DEVICE_INTERFACE_IP4_CONFIG, + G_CALLBACK (device_ip4_config_changed), + self); + return self; +} + +static const char * +nm_vpn_connection_get_service (NMVPNConnection *connection) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + NMSettingVPN *setting; + + setting = (NMSettingVPN *) nm_connection_get_setting (priv->connection, NM_TYPE_SETTING_VPN); + return nm_setting_vpn_get_service_type (setting); +} + +static void +plugin_failed (DBusGProxy *proxy, + NMVPNPluginFailure plugin_failure, + gpointer user_data) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (user_data); + + nm_log_warn (LOGD_VPN, "VPN plugin failed: %d", plugin_failure); + + switch (plugin_failure) { + case NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED: + priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED; + break; + case NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG: + priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID; + break; + default: + priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_UNKNOWN; + } +} + +static void +plugin_state_changed (DBusGProxy *proxy, + NMVPNServiceState state, + gpointer user_data) +{ + NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + nm_log_info (LOGD_VPN, "VPN plugin state changed: %d", state); + + if (state == NM_VPN_SERVICE_STATE_STOPPED) { + /* Clear connection secrets to ensure secrets get requested each time the + * connection is activated. + */ + nm_connection_clear_secrets (priv->connection); + + switch (nm_vpn_connection_get_vpn_state (connection)) { + case NM_VPN_CONNECTION_STATE_PREPARE: + case NM_VPN_CONNECTION_STATE_NEED_AUTH: + case NM_VPN_CONNECTION_STATE_CONNECT: + case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET: + case NM_VPN_CONNECTION_STATE_ACTIVATED: + nm_log_info (LOGD_VPN, "VPN plugin state change reason: %d", priv->failure_reason); + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_FAILED, + priv->failure_reason); + + /* Reset the failure reason */ + priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_UNKNOWN; + break; + default: + break; + } + } +} + +static const char * +ip_address_to_string (guint32 numeric) +{ + struct in_addr temp_addr; + static char buf[INET_ADDRSTRLEN + 1]; + + memset (&buf, '\0', sizeof (buf)); + temp_addr.s_addr = numeric; + + if (inet_ntop (AF_INET, &temp_addr, buf, INET_ADDRSTRLEN)) { + return buf; + } else { + nm_log_warn (LOGD_VPN, "error converting IP4 address 0x%X", + ntohl (temp_addr.s_addr)); + return NULL; + } +} + +static void +print_vpn_config (NMIP4Config *config, + guint32 internal_gw, + const char *tundev, + const char *banner) +{ + NMIP4Address *addr; + char *dns_domain = NULL; + guint32 num, i; + + g_return_if_fail (config != NULL); + + addr = nm_ip4_config_get_address (config, 0); + + nm_log_info (LOGD_VPN, "VPN Gateway: %s", ip_address_to_string (nm_ip4_address_get_gateway (addr))); + if (internal_gw) + nm_log_info (LOGD_VPN, "Internal Gateway: %s", ip_address_to_string (internal_gw)); + nm_log_info (LOGD_VPN, "Tunnel Device: %s", tundev); + nm_log_info (LOGD_VPN, "Internal IP4 Address: %s", ip_address_to_string (nm_ip4_address_get_address (addr))); + nm_log_info (LOGD_VPN, "Internal IP4 Prefix: %d", nm_ip4_address_get_prefix (addr)); + nm_log_info (LOGD_VPN, "Internal IP4 Point-to-Point Address: %s", + ip_address_to_string (nm_ip4_config_get_ptp_address (config))); + nm_log_info (LOGD_VPN, "Maximum Segment Size (MSS): %d", nm_ip4_config_get_mss (config)); + + num = nm_ip4_config_get_num_routes (config); + for (i = 0; i < num; i++) { + NMIP4Route *route; + + route = nm_ip4_config_get_route (config, i); + nm_log_info (LOGD_VPN, "Static Route: %s/%d Next Hop: %s", + ip_address_to_string (nm_ip4_route_get_dest (route)), + nm_ip4_route_get_prefix (route), + ip_address_to_string (nm_ip4_route_get_next_hop (route))); + } + + num = nm_ip4_config_get_num_nameservers (config); + for (i = 0; i < num; i++) { + nm_log_info (LOGD_VPN, "Internal IP4 DNS: %s", + ip_address_to_string (nm_ip4_config_get_nameserver (config, i))); + } + + if (nm_ip4_config_get_num_domains (config) > 0) + dns_domain = (char *) nm_ip4_config_get_domain (config, 0); + + nm_log_info (LOGD_VPN, "DNS Domain: '%s'", dns_domain ? dns_domain : "(none)"); + + if (banner && strlen (banner)) { + nm_log_info (LOGD_VPN, "Login Banner:"); + nm_log_info (LOGD_VPN, "-----------------------------------------"); + nm_log_info (LOGD_VPN, "%s", banner); + nm_log_info (LOGD_VPN, "-----------------------------------------"); + } +} + +static void +nm_vpn_connection_ip4_config_get (DBusGProxy *proxy, + GHashTable *config_hash, + gpointer user_data) +{ + NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + NMSettingIP4Config *s_ip4; + NMIP4Address *addr; + NMIP4Config *config; + GValue *val; + int i; + guint32 vpn_ext_gw = 0; + + nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) reply received.", + nm_vpn_connection_get_name (connection)); + + g_source_remove (priv->ipconfig_timeout); + priv->ipconfig_timeout = 0; + + config = nm_ip4_config_new (); + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV); + if (val) + priv->tundev = g_strdup (g_value_get_string (val)); + else { + nm_log_err (LOGD_VPN, "invalid or missing tunnel device received!"); + goto error; + } + + addr = nm_ip4_address_new (); + nm_ip4_address_set_prefix (addr, 24); /* default to class C */ + + /* Internal address of the VPN subnet's gateway */ + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY); + if (val) + priv->ip4_internal_gw = g_value_get_uint (val); + + /* External world-visible address of the VPN server */ + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY); + if (val) { + nm_ip4_address_set_gateway (addr, g_value_get_uint (val)); + vpn_ext_gw = g_value_get_uint (val); + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS); + if (val) + nm_ip4_address_set_address (addr, g_value_get_uint (val)); + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_PTP); + if (val) + nm_ip4_config_set_ptp_address (config, g_value_get_uint (val)); + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX); + if (val) + nm_ip4_address_set_prefix (addr, g_value_get_uint (val)); + + if (nm_ip4_address_get_address (addr) && nm_ip4_address_get_prefix (addr)) { + nm_ip4_config_take_address (config, addr); + } else { + nm_log_err (LOGD_VPN, "invalid IP4 config received!"); + nm_ip4_address_unref (addr); + goto error; + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_DNS); + if (val) { + GArray *dns = (GArray *) g_value_get_boxed (val); + + for (i = 0; i < dns->len; i++) + nm_ip4_config_add_nameserver (config, g_array_index (dns, guint, i)); + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_NBNS); + if (val) { + GArray *nbns = (GArray *) g_value_get_boxed (val); + + for (i = 0; i < nbns->len; i++) + nm_ip4_config_add_wins (config, g_array_index (nbns, guint, i)); + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_MSS); + if (val) + nm_ip4_config_set_mss (config, g_value_get_uint (val)); + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_MTU); + if (val) + nm_ip4_config_set_mtu (config, g_value_get_uint (val)); + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN); + if (val) + nm_ip4_config_add_domain (config, g_value_get_string (val)); + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_BANNER); + if (val) { + if (priv->banner) + g_free (priv->banner); + priv->banner = g_strdup (g_value_get_string (val)); + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES); + if (val) { + GSList *routes; + GSList *iter; + + routes = nm_utils_ip4_routes_from_gvalue (val); + for (iter = routes; iter; iter = iter->next) { + NMIP4Route *route = iter->data; + + /* Ignore host routes to the VPN gateway since NM adds one itself + * below. Since NM knows more about the routing situation than + * the VPN server, we want to use the NM created route instead of + * whatever the server provides. + */ + if ( vpn_ext_gw + && nm_ip4_route_get_dest (route) == vpn_ext_gw + && nm_ip4_route_get_prefix (route) == 32) + continue; + + /* Otherwise accept the VPN-provided route */ + nm_ip4_config_take_route (config, route); + } + + g_slist_free (routes); + } + + print_vpn_config (config, priv->ip4_internal_gw, priv->tundev, priv->banner); + + /* Merge in user overrides from the NMConnection's IPv4 setting */ + s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (priv->connection, NM_TYPE_SETTING_IP4_CONFIG)); + nm_utils_merge_ip4_config (config, s_ip4); + + nm_system_device_set_up_down_with_iface (priv->tundev, TRUE, NULL); + + if (nm_system_apply_ip4_config (priv->tundev, config, 0, NM_IP4_COMPARE_FLAG_ALL)) { + NMNamedManager *named_mgr; + + /* Add any explicit route to the VPN gateway through the parent device */ + priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev, config); + + /* Add the VPN to DNS */ + named_mgr = nm_named_manager_get (); + nm_named_manager_add_ip4_config (named_mgr, priv->tundev, config, NM_NAMED_IP_CONFIG_TYPE_VPN); + g_object_unref (named_mgr); + + priv->ip4_config = config; + + nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.", + nm_vpn_connection_get_name (connection)); + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_ACTIVATED, + NM_VPN_CONNECTION_STATE_REASON_NONE); + return; + } + +error: + nm_log_warn (LOGD_VPN, "VPN connection '%s' did not receive valid IP config information.", + nm_vpn_connection_get_name (connection)); + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_FAILED, + NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID); + g_object_unref (config); +} + +static gboolean +nm_vpn_connection_ip_config_timeout (gpointer user_data) +{ + NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + priv->ipconfig_timeout = 0; + + /* If the activation request's state is still IP_CONFIG_GET and we're + * in this timeout, cancel activation because it's taken too long. + */ + if (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_IP_CONFIG_GET) { + nm_log_warn (LOGD_VPN, "VPN connection '%s' (IP Config Get) timeout exceeded.", + nm_vpn_connection_get_name (connection)); + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_FAILED, + NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT); + } + + return FALSE; +} + +static void +nm_vpn_connection_connect_cb (DBusGProxy *proxy, GError *err, gpointer user_data) +{ + NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + nm_log_info (LOGD_VPN, "VPN connection '%s' (Connect) reply received.", + nm_vpn_connection_get_name (connection)); + + if (err) { + nm_log_warn (LOGD_VPN, "VPN connection '%s' failed to connect: '%s'.", + nm_vpn_connection_get_name (connection), err->message); + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_FAILED, + NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED); + } else { + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_IP_CONFIG_GET, + NM_VPN_CONNECTION_STATE_REASON_NONE); + + /* 40 second timeout waiting for IP config signal from VPN service */ + priv->ipconfig_timeout = g_timeout_add_seconds (40, nm_vpn_connection_ip_config_timeout, connection); + } +} + +static void +really_activate (NMVPNConnection *connection) +{ + NMVPNConnectionPrivate *priv; + + g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); + g_return_if_fail (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_NEED_AUTH); + + priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + /* Ip4Config signal */ + dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, G_TYPE_VALUE, G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->proxy, "Ip4Config", + DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "Ip4Config", + G_CALLBACK (nm_vpn_connection_ip4_config_get), + connection, NULL); + + org_freedesktop_NetworkManager_VPN_Plugin_connect_async (priv->proxy, + nm_connection_to_hash (priv->connection), + nm_vpn_connection_connect_cb, + connection); + + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_CONNECT, + NM_VPN_CONNECTION_STATE_REASON_NONE); +} + +void +nm_vpn_connection_activate (NMVPNConnection *connection) +{ + NMVPNConnectionPrivate *priv; + NMDBusManager *dbus_mgr; + + g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); + g_return_if_fail (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_PREPARE); + + priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + dbus_mgr = nm_dbus_manager_get (); + priv->proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (dbus_mgr), + nm_vpn_connection_get_service (connection), + NM_VPN_DBUS_PLUGIN_PATH, + NM_VPN_DBUS_PLUGIN_INTERFACE); + g_object_unref (dbus_mgr); + + dbus_g_proxy_add_signal (priv->proxy, "Failure", G_TYPE_UINT, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "Failure", + G_CALLBACK (plugin_failed), + connection, NULL); + + /* StateChanged signal */ + dbus_g_proxy_add_signal (priv->proxy, "StateChanged", G_TYPE_UINT, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "StateChanged", + G_CALLBACK (plugin_state_changed), + connection, NULL); + + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_NEED_AUTH, + NM_VPN_CONNECTION_STATE_REASON_NONE); +} + +const char * +nm_vpn_connection_get_active_connection_path (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ac_path; +} + +const char * +nm_vpn_connection_get_name (NMVPNConnection *connection) +{ + NMVPNConnectionPrivate *priv; + NMSettingConnection *setting; + + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL); + + priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + setting = (NMSettingConnection *) nm_connection_get_setting (priv->connection, NM_TYPE_SETTING_CONNECTION); + + return nm_setting_connection_get_id (setting); +} + +NMConnection * +nm_vpn_connection_get_connection (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->connection; +} + +NMVPNConnectionState +nm_vpn_connection_get_vpn_state (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NM_VPN_CONNECTION_STATE_UNKNOWN); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->vpn_state; +} + +const char * +nm_vpn_connection_get_banner (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->banner; +} + +NMIP4Config * +nm_vpn_connection_get_ip4_config (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip4_config; +} + +const char * +nm_vpn_connection_get_ip_iface (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->tundev; +} + +NMDevice * +nm_vpn_connection_get_parent_device (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->parent_dev; +} + +guint32 +nm_vpn_connection_get_ip4_internal_gateway (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), 0); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip4_internal_gw; +} + +void +nm_vpn_connection_fail (NMVPNConnection *connection, + NMVPNConnectionStateReason reason) +{ + g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); + + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_FAILED, + reason); +} + +void +nm_vpn_connection_disconnect (NMVPNConnection *connection, + NMVPNConnectionStateReason reason) +{ + g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); + + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_DISCONNECTED, + reason); +} + +/******************************************************************************/ + +static gboolean +secrets_update_setting (NMSecretsProviderInterface *interface, + const char *setting_name, + GHashTable *new) +{ + NMVPNConnection *self = NM_VPN_CONNECTION (interface); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + GError *error = NULL; + + g_return_val_if_fail (priv->connection != NULL, FALSE); + + if (strcmp (setting_name, NM_SETTING_VPN_SETTING_NAME)) + return FALSE; + + if (!nm_connection_update_secrets (priv->connection, NM_SETTING_VPN_SETTING_NAME, new, &error)) { + nm_log_warn (LOGD_VPN, "Failed to update VPN secrets: %d %s", + error ? error->code : -1, + error && error->message ? error->message : "(none)"); + g_clear_error (&error); + return FALSE; + } + return TRUE; +} + +static void +secrets_result (NMSecretsProviderInterface *interface, + const char *setting_name, + RequestSecretsCaller caller, + const GSList *updated, + GError *error) +{ + NMVPNConnection *self = NM_VPN_CONNECTION (interface); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + + g_return_if_fail (priv->connection != NULL); + g_return_if_fail (caller == SECRETS_CALLER_VPN); + + if (error) + nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); + else + really_activate (self); +} + +static void +secrets_provider_interface_init (NMSecretsProviderInterface *sp_interface_class) +{ + /* interface implementation */ + sp_interface_class->update_setting = secrets_update_setting; + sp_interface_class->result = secrets_result; +} + +static void +connection_need_secrets_cb (DBusGProxy *proxy, + char *setting_name, + GError *error, + gpointer user_data) +{ + NMVPNConnection *self = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + + if (error) { + nm_log_err (LOGD_VPN, "NeedSecrets failed: %s %s", + g_quark_to_string (error->domain), + error->message); + nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); + return; + } + + if (!setting_name || !strlen (setting_name)) { + /* No secrets required */ + really_activate (self); + return; + } + + /* Get the secrets the VPN plugin wants */ + if (!nm_secrets_provider_interface_get_secrets (NM_SECRETS_PROVIDER_INTERFACE (self), + priv->connection, + setting_name, + FALSE, + SECRETS_CALLER_VPN, + NULL, + NULL)) + nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); +} + +static void +call_need_secrets (NMVPNConnection *vpn_connection) +{ + NMVPNConnectionPrivate *priv; + GHashTable *settings; + + priv = NM_VPN_CONNECTION_GET_PRIVATE (vpn_connection); + settings = nm_connection_to_hash (priv->connection); + org_freedesktop_NetworkManager_VPN_Plugin_need_secrets_async (priv->proxy, + settings, + connection_need_secrets_cb, + vpn_connection); + g_hash_table_destroy (settings); +} + +static void +vpn_cleanup (NMVPNConnection *connection) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + if (priv->tundev) { + nm_system_device_set_up_down_with_iface (priv->tundev, FALSE, NULL); + /* FIXME: use AF_UNSPEC here when we have IPv6 support */ + nm_system_device_flush_routes_with_iface (priv->tundev, AF_INET); + nm_system_device_flush_addresses_with_iface (priv->tundev); + } + + if (priv->ip4_config) { + NMIP4Config *parent_config; + NMNamedManager *named_mgr; + + /* Remove attributes of the VPN's IP4 Config */ + named_mgr = nm_named_manager_get (); + nm_named_manager_remove_ip4_config (named_mgr, priv->tundev, priv->ip4_config); + g_object_unref (named_mgr); + + /* Remove any previously added VPN gateway host route */ + if (priv->gw_route) + rtnl_route_del (nm_netlink_get_default_handle (), priv->gw_route, 0); + + /* Reset routes and addresses of the currently active device */ + parent_config = nm_device_get_ip4_config (priv->parent_dev); + if (parent_config) { + if (!nm_system_apply_ip4_config (nm_device_get_ip_iface (priv->parent_dev), + nm_device_get_ip4_config (priv->parent_dev), + nm_device_get_priority (priv->parent_dev), + NM_IP4_COMPARE_FLAG_ADDRESSES | NM_IP4_COMPARE_FLAG_ROUTES)) { + nm_log_err (LOGD_VPN, "failed to re-apply VPN parent device addresses and routes."); + } + } + } + + if (priv->gw_route) { + rtnl_route_put (priv->gw_route); + priv->gw_route = NULL; + } + + if (priv->banner) { + g_free (priv->banner); + priv->banner = NULL; + } + + if (priv->tundev) { + g_free (priv->tundev); + priv->tundev = NULL; + } + + /* Clear out connection secrets to ensure that the settings service + * gets asked for them next time the connection is activated. + */ + if (priv->connection) + nm_connection_clear_secrets (priv->connection); +} + +static void +connection_state_changed (NMVPNConnection *connection, + NMVPNConnectionState state, + NMVPNConnectionStateReason reason) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + nm_secrets_provider_interface_cancel_get_secrets (NM_SECRETS_PROVIDER_INTERFACE (priv->act_request)); + + switch (state) { + case NM_VPN_CONNECTION_STATE_NEED_AUTH: + call_need_secrets (connection); + break; + case NM_VPN_CONNECTION_STATE_DISCONNECTED: + case NM_VPN_CONNECTION_STATE_FAILED: + if (priv->proxy) { + GError *err = NULL; + + org_freedesktop_NetworkManager_VPN_Plugin_disconnect (priv->proxy, &err); + if (err) { + nm_log_warn (LOGD_VPN, "error disconnecting VPN: %s", err->message); + g_error_free (err); + } + + g_object_unref (priv->proxy); + priv->proxy = NULL; + } + vpn_cleanup (connection); + break; + default: + break; + } +} + +static void +nm_vpn_connection_init (NMVPNConnection *connection) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + NMDBusManager *dbus_mgr; + + priv->state = NM_ACTIVE_CONNECTION_STATE_UNKNOWN; + priv->vpn_state = NM_VPN_CONNECTION_STATE_PREPARE; + priv->ac_path = nm_active_connection_get_next_object_path (); + + dbus_mgr = nm_dbus_manager_get (); + dbus_g_connection_register_g_object (nm_dbus_manager_get_connection (dbus_mgr), + priv->ac_path, + G_OBJECT (connection)); + g_object_unref (dbus_mgr); +} + +static void +dispose (GObject *object) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object); + + if (priv->disposed) { + G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object); + return; + } + priv->disposed = TRUE; + + if (priv->gw_route) + rtnl_route_put (priv->gw_route); + + if (priv->device_ip4) + g_signal_handler_disconnect (priv->parent_dev, priv->device_ip4); + + if (priv->device_monitor) + g_signal_handler_disconnect (priv->parent_dev, priv->device_monitor); + + g_object_unref (priv->parent_dev); + + if (priv->ip4_config) + g_object_unref (priv->ip4_config); + + if (priv->ipconfig_timeout) + g_source_remove (priv->ipconfig_timeout); + + if (priv->proxy) + g_object_unref (priv->proxy); + + g_object_unref (priv->act_request); + g_object_unref (priv->connection); + + G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object); + + g_free (priv->banner); + g_free (priv->tundev); + g_free (priv->ac_path); + + G_OBJECT_CLASS (nm_vpn_connection_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_SERVICE_NAME: + nm_active_connection_scope_to_value (priv->connection, value); + break; + case PROP_CONNECTION: + g_value_set_boxed (value, nm_connection_get_path (priv->connection)); + break; + case PROP_SPECIFIC_OBJECT: + g_value_set_boxed (value, nm_act_request_get_active_connection_path (priv->act_request)); + break; + case PROP_DEVICES: + g_value_take_boxed (value, g_ptr_array_new ()); + break; + case PROP_STATE: + g_value_set_uint (value, priv->state); + break; + case PROP_DEFAULT: + g_value_set_boolean (value, priv->is_default); + break; + case PROP_DEFAULT6: + g_value_set_boolean (value, priv->is_default6); + break; + case PROP_VPN: + g_value_set_boolean (value, TRUE); + break; + case PROP_VPN_STATE: + g_value_set_uint (value, priv->vpn_state); + break; + case PROP_BANNER: + g_value_set_string (value, priv->banner ? priv->banner : ""); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_vpn_connection_class_init (NMVPNConnectionClass *connection_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (connection_class); + + g_type_class_add_private (connection_class, sizeof (NMVPNConnectionPrivate)); + + /* virtual methods */ + connection_class->vpn_state_changed = connection_state_changed; + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + /* properties */ + g_object_class_install_property + (object_class, PROP_SERVICE_NAME, + g_param_spec_string (NM_ACTIVE_CONNECTION_SERVICE_NAME, + "Service name", + "Service name", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_CONNECTION, + g_param_spec_boxed (NM_ACTIVE_CONNECTION_CONNECTION, + "Connection", + "Connection", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_SPECIFIC_OBJECT, + g_param_spec_boxed (NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, + "Specific object", + "Specific object", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_DEVICES, + g_param_spec_boxed (NM_ACTIVE_CONNECTION_DEVICES, + "Devices", + "Devices", + DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_STATE, + g_param_spec_uint (NM_ACTIVE_CONNECTION_STATE, + "State", + "State", + NM_ACTIVE_CONNECTION_STATE_UNKNOWN, + NM_ACTIVE_CONNECTION_STATE_ACTIVATED, + NM_ACTIVE_CONNECTION_STATE_UNKNOWN, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_DEFAULT, + g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT, + "Default", + "Is the default IPv4 active connection", + FALSE, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_DEFAULT6, + g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT6, + "Default6", + "Is the default IPv6 active connection", + FALSE, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_VPN, + g_param_spec_boolean (NM_ACTIVE_CONNECTION_VPN, + "VPN", + "Is a VPN connection", + TRUE, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_VPN_STATE, + g_param_spec_uint (NM_VPN_CONNECTION_VPN_STATE, + "VpnState", + "Current VPN state", + NM_VPN_CONNECTION_STATE_UNKNOWN, + NM_VPN_CONNECTION_STATE_DISCONNECTED, + NM_VPN_CONNECTION_STATE_UNKNOWN, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_BANNER, + g_param_spec_string (NM_VPN_CONNECTION_BANNER, + "Banner", + "Login Banner", + NULL, + G_PARAM_READABLE)); + + /* signals */ + signals[VPN_STATE_CHANGED] = + g_signal_new ("vpn-state-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMVPNConnectionClass, vpn_state_changed), + NULL, NULL, + _nm_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, + G_TYPE_UINT, G_TYPE_UINT); + + signals[PROPERTIES_CHANGED] = + nm_properties_changed_signal_new (object_class, + G_STRUCT_OFFSET (NMVPNConnectionClass, properties_changed)); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (object_class), + &dbus_glib_nm_vpn_connection_object_info); +} + diff --git a/src/vpn-manager/nm-vpn-connection.h b/src/vpn-manager/nm-vpn-connection.h new file mode 100644 index 00000000..d69674db --- /dev/null +++ b/src/vpn-manager/nm-vpn-connection.h @@ -0,0 +1,78 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef NM_VPN_CONNECTION_H +#define NM_VPN_CONNECTION_H + +#include <glib.h> +#include <glib-object.h> +#include "NetworkManagerVPN.h" +#include "nm-device.h" +#include "nm-activation-request.h" +#include "nm-secrets-provider-interface.h" + +#define NM_TYPE_VPN_CONNECTION (nm_vpn_connection_get_type ()) +#define NM_VPN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_CONNECTION, NMVPNConnection)) +#define NM_VPN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_VPN_CONNECTION, NMVPNConnectionClass)) +#define NM_IS_VPN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_CONNECTION)) +#define NM_IS_VPN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_VPN_CONNECTION)) +#define NM_VPN_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_CONNECTION, NMVPNConnectionClass)) + +#define NM_VPN_CONNECTION_VPN_STATE "vpn-state" +#define NM_VPN_CONNECTION_BANNER "banner" + +typedef struct { + GObject parent; +} NMVPNConnection; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*vpn_state_changed) (NMVPNConnection *connection, + NMVPNConnectionState state, + NMVPNConnectionStateReason reason); + + void (*properties_changed) (NMVPNConnection *connection, GHashTable *properties); +} NMVPNConnectionClass; + +GType nm_vpn_connection_get_type (void); + +NMVPNConnection * nm_vpn_connection_new (NMConnection *connection, + NMActRequest *act_request, + NMDevice *parent_device); + +void nm_vpn_connection_activate (NMVPNConnection *connection); +NMConnection * nm_vpn_connection_get_connection (NMVPNConnection *connection); +const char * nm_vpn_connection_get_active_connection_path (NMVPNConnection *connection); +const char * nm_vpn_connection_get_name (NMVPNConnection *connection); +NMVPNConnectionState nm_vpn_connection_get_vpn_state (NMVPNConnection *connection); +const char * nm_vpn_connection_get_banner (NMVPNConnection *connection); +void nm_vpn_connection_fail (NMVPNConnection *connection, + NMVPNConnectionStateReason reason); +void nm_vpn_connection_disconnect (NMVPNConnection *connection, + NMVPNConnectionStateReason reason); +NMIP4Config * nm_vpn_connection_get_ip4_config (NMVPNConnection *connection); +const char * nm_vpn_connection_get_ip_iface (NMVPNConnection *connection); +NMDevice * nm_vpn_connection_get_parent_device (NMVPNConnection *connection); +guint32 nm_vpn_connection_get_ip4_internal_gateway (NMVPNConnection *connection); + +#endif /* NM_VPN_CONNECTION_H */ diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c new file mode 100644 index 00000000..221a8b54 --- /dev/null +++ b/src/vpn-manager/nm-vpn-manager.c @@ -0,0 +1,369 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include <string.h> + +#include "nm-vpn-manager.h" +#include "nm-vpn-service.h" +#include "nm-vpn-connection.h" +#include "nm-setting-vpn.h" +#include "nm-dbus-manager.h" +#include "NetworkManagerVPN.h" +#include "nm-marshal.h" + +G_DEFINE_TYPE (NMVPNManager, nm_vpn_manager, G_TYPE_OBJECT) + +#define NM_VPN_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_MANAGER, NMVPNManagerPrivate)) + +typedef struct { + GSList *services; +} NMVPNManagerPrivate; + +enum { + CONNECTION_ACTIVATED, + CONNECTION_DEACTIVATED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +GQuark +nm_vpn_manager_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm-vpn-manager-error"); + return quark; +} + +/* This should really be standard. */ +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GType +nm_vpn_manager_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + /* The base device for the VPN connection is not active. */ + ENUM_ENTRY (NM_VPN_MANAGER_ERROR_DEVICE_NOT_ACTIVE, "BaseDeviceNotActive"), + /* The requested VPN connection was invalid. */ + ENUM_ENTRY (NM_VPN_MANAGER_ERROR_CONNECTION_INVALID, "ConnectionInvalid"), + /* The VPN service required by this VPN connection did not exist or was invalid. */ + ENUM_ENTRY (NM_VPN_MANAGER_ERROR_SERVICE_INVALID, "ServiceInvalid"), + /* The VPN service required by this VPN connection could not be started. */ + ENUM_ENTRY (NM_VPN_MANAGER_ERROR_SERVICE_START_FAILED, "ServiceStartFailed"), + { 0, 0, 0 } + }; + etype = g_enum_register_static ("NMVPNManagerError", values); + } + return etype; +} + + + +static NMVPNService * +nm_vpn_manager_get_service (NMVPNManager *manager, const char *service_name) +{ + GSList *iter; + + for (iter = NM_VPN_MANAGER_GET_PRIVATE (manager)->services; iter; iter = iter->next) { + NMVPNService *service = NM_VPN_SERVICE (iter->data); + + if (!strcmp (service_name, nm_vpn_service_get_name (service))) + return g_object_ref (service); + } + + return NULL; +} + +static void +remove_service (gpointer data, GObject *service) +{ + NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (data); + + priv->services = g_slist_remove (priv->services, service); +} + +static void +nm_vpn_manager_add_service (NMVPNManager *manager, NMVPNService *service) +{ + NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (manager); + + priv->services = g_slist_prepend (priv->services, service); + g_object_weak_ref (G_OBJECT (service), remove_service, manager); +} + +static NMVPNConnection * +find_active_vpn_connection_by_connection (NMVPNManager *manager, NMConnection *connection) +{ + NMVPNManagerPrivate *priv; + GSList *iter; + + g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL); + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + priv = NM_VPN_MANAGER_GET_PRIVATE (manager); + for (iter = priv->services; iter; iter = g_slist_next (iter)) { + GSList *connections, *elt; + + connections = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data)); + for (elt = connections; elt; elt = g_slist_next (elt)) { + NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data); + + if (nm_vpn_connection_get_connection (vpn) == connection) + return vpn; + } + } + return NULL; +} + +static void +connection_vpn_state_changed (NMVPNConnection *connection, + NMVPNConnectionState state, + NMVPNConnectionStateReason reason, + gpointer user_data) +{ + NMVPNManager *manager = NM_VPN_MANAGER (user_data); + + switch (state) { + case NM_VPN_CONNECTION_STATE_ACTIVATED: + g_signal_emit (manager, signals[CONNECTION_ACTIVATED], 0, connection); + break; + case NM_VPN_CONNECTION_STATE_FAILED: + case NM_VPN_CONNECTION_STATE_DISCONNECTED: + g_signal_emit (manager, signals[CONNECTION_DEACTIVATED], 0, connection, state, reason); + break; + default: + break; + } +} + +NMVPNConnection * +nm_vpn_manager_activate_connection (NMVPNManager *manager, + NMConnection *connection, + NMActRequest *act_request, + NMDevice *device, + GError **error) +{ + NMSettingVPN *vpn_setting; + NMVPNService *service; + NMVPNConnection *vpn = NULL; + const char *service_type; + + g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL); + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (NM_IS_ACT_REQUEST (act_request), NULL); + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + g_return_val_if_fail (error != NULL, NULL); + g_return_val_if_fail (*error == NULL, NULL); + + if (nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED) { + g_set_error (error, + NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_DEVICE_NOT_ACTIVE, + "%s", "The base device for the VPN connection was not active."); + return NULL; + } + + vpn_setting = (NMSettingVPN *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN); + if (!vpn_setting) { + g_set_error (error, + NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_CONNECTION_INVALID, + "%s", "The connection was not a VPN connection."); + return NULL; + } + + vpn = find_active_vpn_connection_by_connection (manager, connection); + if (vpn) { + nm_vpn_connection_disconnect (vpn, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED); + vpn = NULL; + } + + service_type = nm_setting_vpn_get_service_type (vpn_setting); + service = nm_vpn_manager_get_service (manager, service_type); + if (!service) { + service = nm_vpn_service_new (service_type); + if (service) + nm_vpn_manager_add_service (manager, service); + } + + if (service) { + vpn = nm_vpn_service_activate (service, connection, act_request, device, error); + if (vpn) { + g_signal_connect (vpn, "vpn-state-changed", + G_CALLBACK (connection_vpn_state_changed), + manager); + } + } else { + g_set_error (error, + NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_INVALID, + "%s", "The VPN service was invalid."); + } + + return vpn; +} + +gboolean +nm_vpn_manager_deactivate_connection (NMVPNManager *manager, + const char *path, + NMVPNConnectionStateReason reason) +{ + NMVPNManagerPrivate *priv; + GSList *iter; + gboolean found = FALSE; + + g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + priv = NM_VPN_MANAGER_GET_PRIVATE (manager); + for (iter = priv->services; iter; iter = g_slist_next (iter)) { + GSList *connections, *elt; + + connections = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data)); + for (elt = connections; elt; elt = g_slist_next (elt)) { + NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data); + const char *vpn_path; + + vpn_path = nm_vpn_connection_get_active_connection_path (vpn); + if (!strcmp (path, vpn_path)) { + nm_vpn_connection_disconnect (vpn, reason); + found = TRUE; + } + } + } + + return found ? TRUE : FALSE; +} + +void +nm_vpn_manager_add_active_connections (NMVPNManager *manager, + NMConnection *filter, + GPtrArray *array) +{ + NMVPNManagerPrivate *priv; + GSList *iter; + + g_return_if_fail (NM_IS_VPN_MANAGER (manager)); + g_return_if_fail (array != NULL); + + priv = NM_VPN_MANAGER_GET_PRIVATE (manager); + for (iter = priv->services; iter; iter = g_slist_next (iter)) { + GSList *active, *elt; + + active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data)); + for (elt = active; elt; elt = g_slist_next (elt)) { + NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data); + const char *path; + + if (!filter || (nm_vpn_connection_get_connection (vpn) == filter)) { + path = nm_vpn_connection_get_active_connection_path (vpn); + g_ptr_array_add (array, g_strdup (path)); + } + } + } +} + +GSList * +nm_vpn_manager_get_active_connections (NMVPNManager *manager) +{ + NMVPNManagerPrivate *priv; + GSList *iter; + GSList *list = NULL; + + g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL); + + priv = NM_VPN_MANAGER_GET_PRIVATE (manager); + for (iter = priv->services; iter; iter = g_slist_next (iter)) { + GSList *active, *elt; + + active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data)); + for (elt = active; elt; elt = g_slist_next (elt)) + list = g_slist_append (list, g_object_ref (NM_VPN_CONNECTION (elt->data))); + } + + return list; +} + +NMVPNManager * +nm_vpn_manager_get (void) +{ + static NMVPNManager *singleton = NULL; + + if (!singleton) + singleton = NM_VPN_MANAGER (g_object_new (NM_TYPE_VPN_MANAGER, NULL)); + else + g_object_ref (singleton); + + g_assert (singleton); + return singleton; +} + +/******************************************************************************/ + +static void +nm_vpn_manager_init (NMVPNManager *manager) +{ +} + +static void +finalize (GObject *object) +{ + NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (object); + + g_slist_foreach (priv->services, (GFunc) g_object_unref, NULL); + + G_OBJECT_CLASS (nm_vpn_manager_parent_class)->finalize (object); +} + +static void +nm_vpn_manager_class_init (NMVPNManagerClass *manager_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (manager_class); + + g_type_class_add_private (manager_class, sizeof (NMVPNManagerPrivate)); + + /* virtual methods */ + object_class->finalize = finalize; + + /* signals */ + signals[CONNECTION_ACTIVATED] = + g_signal_new ("connection-activated", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + + signals[CONNECTION_DEACTIVATED] = + g_signal_new ("connection-deactivated", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMVPNManagerClass, connection_deactivated), + NULL, NULL, + _nm_marshal_VOID__OBJECT_UINT_UINT, + G_TYPE_NONE, 3, + G_TYPE_OBJECT, G_TYPE_UINT, G_TYPE_UINT); + + dbus_g_error_domain_register (NM_VPN_MANAGER_ERROR, NULL, NM_TYPE_VPN_MANAGER_ERROR); +} + diff --git a/src/vpn-manager/nm-vpn-manager.h b/src/vpn-manager/nm-vpn-manager.h new file mode 100644 index 00000000..d07aa250 --- /dev/null +++ b/src/vpn-manager/nm-vpn-manager.h @@ -0,0 +1,86 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef NM_VPN_MANAGER_H +#define NM_VPN_MANAGER_H + +#include <glib.h> +#include <glib-object.h> +#include "nm-vpn-connection.h" +#include "nm-activation-request.h" + +#define NM_TYPE_VPN_MANAGER (nm_vpn_manager_get_type ()) +#define NM_VPN_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_MANAGER, NMVPNManager)) +#define NM_VPN_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_VPN_MANAGER, NMVPNManagerClass)) +#define NM_IS_VPN_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_MANAGER)) +#define NM_IS_VPN_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_VPN_MANAGER)) +#define NM_VPN_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_MANAGER, NMVPNManagerClass)) + +typedef enum +{ + NM_VPN_MANAGER_ERROR_DEVICE_NOT_ACTIVE = 0, + NM_VPN_MANAGER_ERROR_CONNECTION_INVALID, + NM_VPN_MANAGER_ERROR_SERVICE_INVALID, + NM_VPN_MANAGER_ERROR_SERVICE_START_FAILED, +} NMVPNManagerError; + +#define NM_VPN_MANAGER_ERROR (nm_vpn_manager_error_quark ()) +#define NM_TYPE_VPN_MANAGER_ERROR (nm_vpn_manager_error_get_type ()) + +GQuark nm_vpn_manager_error_quark (void); +GType nm_vpn_manager_error_get_type (void); + + +typedef struct { + GObject parent; +} NMVPNManager; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*connection_deactivated) (NMVPNManager *manager, + NMVPNConnection *connection, + NMVPNConnectionState state, + NMVPNConnectionStateReason reason); +} NMVPNManagerClass; + +GType nm_vpn_manager_get_type (void); + +NMVPNManager *nm_vpn_manager_get (void); + +NMVPNConnection *nm_vpn_manager_activate_connection (NMVPNManager *manager, + NMConnection *connection, + NMActRequest *act_request, + NMDevice *device, + GError **error); + +gboolean nm_vpn_manager_deactivate_connection (NMVPNManager *manager, + const char *path, + NMVPNConnectionStateReason reason); + +void nm_vpn_manager_add_active_connections (NMVPNManager *manager, + NMConnection *filter, + GPtrArray *list); + +GSList *nm_vpn_manager_get_active_connections (NMVPNManager *manager); + +#endif /* NM_VPN_VPN_MANAGER_H */ diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c new file mode 100644 index 00000000..bdbb3774 --- /dev/null +++ b/src/vpn-manager/nm-vpn-service.c @@ -0,0 +1,478 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2005 - 2008 Novell, Inc. + */ + +#include <glib.h> +#include <string.h> +#include <dbus/dbus.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <unistd.h> + +#include "nm-vpn-service.h" +#include "nm-dbus-manager.h" +#include "nm-logging.h" +#include "nm-vpn-manager.h" +#include "nm-glib-compat.h" + +G_DEFINE_TYPE (NMVPNService, nm_vpn_service, G_TYPE_OBJECT) + +typedef struct { + NMDBusManager *dbus_mgr; + char *name; + char *dbus_service; + char *program; + + GPid pid; + GSList *connections; + guint service_start_timeout; + guint service_child_watch; + gulong name_owner_id; +} NMVPNServicePrivate; + +#define NM_VPN_SERVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_SERVICE, NMVPNServicePrivate)) + +#define VPN_CONNECTION_GROUP "VPN Connection" + +static GKeyFile * +find_service_file (const char *name) +{ + GDir *dir; + const char *fn; + GKeyFile *key_file = NULL; + + dir = g_dir_open (VPN_NAME_FILES_DIR, 0, NULL); + if (!dir) + return NULL; + + while ((fn = g_dir_read_name (dir))) { + char *path; + gboolean found = FALSE; + + /* only parse filenames that end with .name */ + if (!g_str_has_suffix (fn, ".name")) + continue; + + key_file = g_key_file_new (); + path = g_build_filename (VPN_NAME_FILES_DIR, fn, NULL); + + if (g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, NULL)) { + gchar *val; + + val = g_key_file_get_string (key_file, VPN_CONNECTION_GROUP, "service", NULL); + if (val) { + if (!strcmp (val, name)) + found = TRUE; + g_free (val); + } + } + + g_free (path); + + if (found) + break; + + g_key_file_free (key_file); + key_file = NULL; + } + + g_dir_close (dir); + + return key_file; +} + +NMVPNService * +nm_vpn_service_new (const char *name) +{ + GKeyFile *key_file; + NMVPNService *service = NULL; + NMVPNServicePrivate *priv; + char *dbus_service = NULL; + char *program = NULL; + gboolean success = FALSE; + + g_return_val_if_fail (name != NULL, NULL); + + key_file = find_service_file (name); + if (!key_file) + return NULL; + + dbus_service = g_key_file_get_string (key_file, VPN_CONNECTION_GROUP, "service", NULL); + if (!dbus_service) + goto out; + + program = g_key_file_get_string (key_file, VPN_CONNECTION_GROUP, "program", NULL); + if (!program) + goto out; + + service = (NMVPNService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL); + if (!service) + goto out; + + priv = NM_VPN_SERVICE_GET_PRIVATE (service); + + priv->name = g_strdup (name); + priv->dbus_service = dbus_service; + priv->program = program; + + success = TRUE; + + out: + g_key_file_free (key_file); + + if (!success) { + g_free (dbus_service); + g_free (program); + } + + return service; +} + +const char * +nm_vpn_service_get_name (NMVPNService *service) +{ + g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL); + + return NM_VPN_SERVICE_GET_PRIVATE (service)->name; +} + +static void +nm_vpn_service_connections_stop (NMVPNService *service, + gboolean fail, + NMVPNConnectionStateReason reason) +{ + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); + GSList *iter, *copy; + + /* Copy because stopping the connection may remove it from the list + * in the the NMVPNService objects' VPN connection state handler. + */ + copy = g_slist_copy (priv->connections); + for (iter = copy; iter; iter = iter->next) { + if (fail) + nm_vpn_connection_fail (NM_VPN_CONNECTION (iter->data), reason); + else + nm_vpn_connection_disconnect (NM_VPN_CONNECTION (iter->data), reason); + } + g_slist_free (copy); +} + +/* + * nm_vpn_service_child_setup + * + * Set the process group ID of the newly forked process + * + */ +static void +nm_vpn_service_child_setup (gpointer user_data G_GNUC_UNUSED) +{ + /* We are in the child process at this point */ + pid_t pid = getpid (); + setpgid (pid, pid); +} + +static void +vpn_service_watch_cb (GPid pid, gint status, gpointer user_data) +{ + NMVPNService *service = NM_VPN_SERVICE (user_data); + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); + + if (WIFEXITED (status)) { + guint err = WEXITSTATUS (status); + + if (err != 0) { + nm_log_warn (LOGD_VPN, "VPN service '%s' exited with error: %d", + nm_vpn_service_get_name (service), WSTOPSIG (status)); + } + } else if (WIFSTOPPED (status)) { + nm_log_warn (LOGD_VPN, "VPN service '%s' stopped unexpectedly with signal %d", + nm_vpn_service_get_name (service), WSTOPSIG (status)); + } else if (WIFSIGNALED (status)) { + nm_log_warn (LOGD_VPN, "VPN service '%s' died with signal %d", + nm_vpn_service_get_name (service), WTERMSIG (status)); + } else { + nm_log_warn (LOGD_VPN, "VPN service '%s' died from an unknown cause", + nm_vpn_service_get_name (service)); + } + + priv->pid = 0; + priv->service_child_watch = 0; + + nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); +} + +static gboolean +nm_vpn_service_timeout (gpointer data) +{ + NMVPNService *service = NM_VPN_SERVICE (data); + + nm_log_warn (LOGD_VPN, "VPN service '%s' did not start in time, cancelling connections", + nm_vpn_service_get_name (service)); + + NM_VPN_SERVICE_GET_PRIVATE (service)->service_start_timeout = 0; + nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT); + + return FALSE; +} + +static gboolean +nm_vpn_service_daemon_exec (NMVPNService *service, GError **error) +{ + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); + char *vpn_argv[2]; + gboolean success = FALSE; + GError *spawn_error = NULL; + + g_return_val_if_fail (NM_IS_VPN_SERVICE (service), FALSE); + g_return_val_if_fail (error != NULL, FALSE); + g_return_val_if_fail (*error == NULL, FALSE); + + vpn_argv[0] = priv->program; + vpn_argv[1] = NULL; + + success = g_spawn_async (NULL, vpn_argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, + nm_vpn_service_child_setup, NULL, &priv->pid, + &spawn_error); + if (success) { + nm_log_info (LOGD_VPN, "VPN service '%s' started (%s), PID %d", + nm_vpn_service_get_name (service), priv->dbus_service, priv->pid); + + priv->service_child_watch = g_child_watch_add (priv->pid, vpn_service_watch_cb, service); + priv->service_start_timeout = g_timeout_add_seconds (5, nm_vpn_service_timeout, service); + } else { + nm_log_warn (LOGD_VPN, "VPN service '%s': could not launch the VPN service. error: (%d) %s.", + nm_vpn_service_get_name (service), spawn_error->code, spawn_error->message); + + g_set_error (error, + NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_START_FAILED, + "%s", spawn_error ? spawn_error->message : "unknown g_spawn_async() error"); + + nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED); + if (spawn_error) + g_error_free (spawn_error); + } + + return success; +} + +static gboolean +destroy_service (gpointer data) +{ + g_object_unref (data); + + return FALSE; +} + +static void +connection_vpn_state_changed (NMVPNConnection *connection, + NMVPNConnectionState state, + NMVPNConnectionStateReason reason, + gpointer user_data) +{ + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (user_data); + + switch (state) { + case NM_VPN_CONNECTION_STATE_FAILED: + case NM_VPN_CONNECTION_STATE_DISCONNECTED: + /* Remove the connection from our list */ + priv->connections = g_slist_remove (priv->connections, connection); + g_object_unref (connection); + + if (priv->connections == NULL) { + /* schedule a timeout (10 seconds) to destroy the service */ + g_timeout_add_seconds (10, destroy_service, user_data); + } + break; + default: + break; + } +} + +NMVPNConnection * +nm_vpn_service_activate (NMVPNService *service, + NMConnection *connection, + NMActRequest *act_request, + NMDevice *device, + GError **error) +{ + NMVPNConnection *vpn; + NMVPNServicePrivate *priv; + + g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL); + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (NM_IS_ACT_REQUEST (act_request), NULL); + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + g_return_val_if_fail (error != NULL, NULL); + g_return_val_if_fail (*error == NULL, NULL); + + priv = NM_VPN_SERVICE_GET_PRIVATE (service); + + vpn = nm_vpn_connection_new (connection, act_request, device); + g_signal_connect (vpn, "vpn-state-changed", + G_CALLBACK (connection_vpn_state_changed), + service); + + priv->connections = g_slist_prepend (priv->connections, vpn); + + if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, priv->dbus_service)) { + // FIXME: fill in error when errors happen + nm_vpn_connection_activate (vpn); + } else if (priv->service_start_timeout == 0) { + nm_log_info (LOGD_VPN, "Starting VPN service '%s'...", + nm_vpn_service_get_name (service)); + if (!nm_vpn_service_daemon_exec (service, error)) + vpn = NULL; + } + + return vpn; +} + +GSList * +nm_vpn_service_get_active_connections (NMVPNService *service) +{ + g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL); + + return g_slist_copy (NM_VPN_SERVICE_GET_PRIVATE (service)->connections); +} + +static void +nm_vpn_service_name_owner_changed (NMDBusManager *mgr, + const char *name, + const char *old, + const char *new, + gpointer user_data) +{ + NMVPNService *service = NM_VPN_SERVICE (user_data); + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); + gboolean old_owner_good; + gboolean new_owner_good; + + if (strcmp (name, priv->dbus_service)) + return; + + /* Service changed, no need to wait for the timeout any longer */ + if (priv->service_start_timeout) { + g_source_remove (priv->service_start_timeout); + priv->service_start_timeout = 0; + } + + old_owner_good = (old && (strlen (old) > 0)); + new_owner_good = (new && (strlen (new) > 0)); + + if (!old_owner_good && new_owner_good) { + /* service just appeared */ + GSList *iter; + + nm_log_info (LOGD_VPN, "VPN service '%s' appeared, activating connections", + nm_vpn_service_get_name (service)); + + for (iter = priv->connections; iter; iter = iter->next) + nm_vpn_connection_activate (NM_VPN_CONNECTION (iter->data)); + + } else if (old_owner_good && !new_owner_good) { + /* service went away */ + nm_log_info (LOGD_VPN, "VPN service '%s' disappeared, cancelling connections", + nm_vpn_service_get_name (service)); + nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); + } +} + +/******************************************************************************/ + +static void +nm_vpn_service_init (NMVPNService *service) +{ + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); + + priv->dbus_mgr = nm_dbus_manager_get (); + + priv->name_owner_id = g_signal_connect (priv->dbus_mgr, "name-owner-changed", + G_CALLBACK (nm_vpn_service_name_owner_changed), + service); +} + +static gboolean +ensure_killed (gpointer data) +{ + int pid = GPOINTER_TO_INT (data); + + if (kill (pid, 0) == 0) + kill (pid, SIGKILL); + + /* ensure the child is reaped */ + nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", pid); + waitpid (pid, NULL, 0); + nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", pid); + + return FALSE; +} + +static void +finalize (GObject *object) +{ + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (object); + + if (priv->service_start_timeout) + g_source_remove (priv->service_start_timeout); + + nm_vpn_service_connections_stop (NM_VPN_SERVICE (object), + FALSE, + NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); + + g_signal_handler_disconnect (priv->dbus_mgr, priv->name_owner_id); + + if (priv->service_child_watch) + g_source_remove (priv->service_child_watch); + + if (priv->pid) { + if (kill (priv->pid, SIGTERM) == 0) + g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid)); + else { + kill (priv->pid, SIGKILL); + + /* ensure the child is reaped */ + nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", priv->pid); + waitpid (priv->pid, NULL, 0); + nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", priv->pid); + } + + priv->pid = 0; + } + + g_object_unref (priv->dbus_mgr); + + g_free (priv->name); + g_free (priv->dbus_service); + g_free (priv->program); + + G_OBJECT_CLASS (nm_vpn_service_parent_class)->finalize (object); +} + +static void +nm_vpn_service_class_init (NMVPNServiceClass *service_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (service_class); + + g_type_class_add_private (service_class, sizeof (NMVPNServicePrivate)); + + /* virtual methods */ + object_class->finalize = finalize; +} diff --git a/src/vpn-manager/nm-vpn-service.h b/src/vpn-manager/nm-vpn-service.h new file mode 100644 index 00000000..9c5e0eef --- /dev/null +++ b/src/vpn-manager/nm-vpn-service.h @@ -0,0 +1,60 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2005 - 2008 Novell, Inc. + */ + +#ifndef NM_VPN_SERVICE_H +#define NM_VPN_SERVICE_H + +#include <glib.h> +#include <glib-object.h> +#include "nm-device.h" +#include "nm-vpn-connection.h" +#include "nm-activation-request.h" + +#define NM_TYPE_VPN_SERVICE (nm_vpn_service_get_type ()) +#define NM_VPN_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_SERVICE, NMVPNService)) +#define NM_VPN_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_VPN_SERVICE, NMVPNServiceClass)) +#define NM_IS_VPN_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_SERVICE)) +#define NM_IS_VPN_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_VPN_SERVICE)) +#define NM_VPN_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_SERVICE, NMVPNServiceClass)) + +typedef struct { + GObject parent; +} NMVPNService; + +typedef struct { + GObjectClass parent; +} NMVPNServiceClass; + +GType nm_vpn_service_get_type (void); + +NMVPNService * nm_vpn_service_new (const char *service_name); + +const char * nm_vpn_service_get_name (NMVPNService *service); + +NMVPNConnection * nm_vpn_service_activate (NMVPNService *service, + NMConnection *connection, + NMActRequest *act_request, + NMDevice *device, + GError **error); + +GSList * nm_vpn_service_get_active_connections (NMVPNService *service); + +#endif /* NM_VPN_VPN_SERVICE_H */ diff --git a/src/wpa.c b/src/wpa.c new file mode 100644 index 00000000..5f4fd024 --- /dev/null +++ b/src/wpa.c @@ -0,0 +1,455 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * WPA Supplicant - WPA state machine and EAPOL-Key processing + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this file may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "wireless-helper.h" + +#include <stdint.h> +#include <string.h> +#include <glib.h> + +#include "wpa.h" +#include "nm-logging.h" + +typedef guint16 u16; +typedef guint8 u8; + +#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0])) + +#define BIT(n) (1 << (n)) +#define WPA_CAPABILITY_PREAUTH BIT(0) + +#define WPA_REPLAY_COUNTER_LEN 8 +#define WPA_NONCE_LEN 32 + +#define PMKID_LEN 16 + +static const int WPA_SELECTOR_LEN = 4; +static const u8 WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 }; +static const u16 WPA_VERSION = 1; +static const u8 WPA_AUTH_KEY_MGMT_NONE[] = { 0x00, 0x50, 0xf2, 0 }; +static const u8 WPA_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x50, 0xf2, 1 }; +static const u8 WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x50, 0xf2, 2 }; +static const u8 WPA_CIPHER_SUITE_NONE[] = { 0x00, 0x50, 0xf2, 0 }; +static const u8 WPA_CIPHER_SUITE_WEP40[] = { 0x00, 0x50, 0xf2, 1 }; +static const u8 WPA_CIPHER_SUITE_TKIP[] = { 0x00, 0x50, 0xf2, 2 }; +static const u8 WPA_CIPHER_SUITE_WRAP[] = { 0x00, 0x50, 0xf2, 3 }; +static const u8 WPA_CIPHER_SUITE_CCMP[] = { 0x00, 0x50, 0xf2, 4 }; +static const u8 WPA_CIPHER_SUITE_WEP104[] = { 0x00, 0x50, 0xf2, 5 }; + +/* WPA IE version 1 + * 00-50-f2:1 (OUI:OUI type) + * 0x01 0x00 (version; little endian) + * (all following fields are optional:) + * Group Suite Selector (4 octets) (default: TKIP) + * Pairwise Suite Count (2 octets, little endian) (default: 1) + * Pairwise Suite List (4 * n octets) (default: TKIP) + * Authenticated Key Management Suite Count (2 octets, little endian) + * (default: 1) + * Authenticated Key Management Suite List (4 * n octets) + * (default: unspec 802.1X) + * WPA Capabilities (2 octets, little endian) (default: 0) + */ + +struct wpa_ie_hdr { + u8 elem_id; + u8 len; + u8 oui[3]; + u8 oui_type; + u8 version[2]; +} __attribute__ ((packed)); + + +static const int RSN_SELECTOR_LEN = 4; +static const u16 RSN_VERSION = 1; +static const u8 RSN_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x0f, 0xac, 1 }; +static const u8 RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x0f, 0xac, 2 }; +static const u8 RSN_CIPHER_SUITE_NONE[] = { 0x00, 0x0f, 0xac, 0 }; +static const u8 RSN_CIPHER_SUITE_WEP40[] = { 0x00, 0x0f, 0xac, 1 }; +static const u8 RSN_CIPHER_SUITE_TKIP[] = { 0x00, 0x0f, 0xac, 2 }; +static const u8 RSN_CIPHER_SUITE_WRAP[] = { 0x00, 0x0f, 0xac, 3 }; +static const u8 RSN_CIPHER_SUITE_CCMP[] = { 0x00, 0x0f, 0xac, 4 }; +static const u8 RSN_CIPHER_SUITE_WEP104[] = { 0x00, 0x0f, 0xac, 5 }; + +/* EAPOL-Key Key Data Encapsulation + * GroupKey and STAKey require encryption, otherwise, encryption is optional. + */ +static const u8 RSN_KEY_DATA_GROUPKEY[] = { 0x00, 0x0f, 0xac, 1 }; +static const u8 RSN_KEY_DATA_STAKEY[] = { 0x00, 0x0f, 0xac, 2 }; +static const u8 RSN_KEY_DATA_MAC_ADDR[] = { 0x00, 0x0f, 0xac, 3 }; +static const u8 RSN_KEY_DATA_PMKID[] = { 0x00, 0x0f, 0xac, 4 }; + +/* RSN IE version 1 + * 0x01 0x00 (version; little endian) + * (all following fields are optional:) + * Group Suite Selector (4 octets) (default: CCMP) + * Pairwise Suite Count (2 octets, little endian) (default: 1) + * Pairwise Suite List (4 * n octets) (default: CCMP) + * Authenticated Key Management Suite Count (2 octets, little endian) + * (default: 1) + * Authenticated Key Management Suite List (4 * n octets) + * (default: unspec 802.1X) + * RSN Capabilities (2 octets, little endian) (default: 0) + * PMKID Count (2 octets) (default: 0) + * PMKID List (16 * n octets) + */ + +struct rsn_ie_hdr { + u8 elem_id; /* WLAN_EID_RSN */ + u8 len; + u8 version[2]; +} __attribute__ ((packed)); + + +#define WPA_KEY_INFO_TYPE_MASK (BIT(0) | BIT(1) | BIT(2)) +#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0) +#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1) +#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */ +/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */ +#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5)) +#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4 +#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */ +#define WPA_KEY_INFO_TXRX BIT(6) /* group */ +#define WPA_KEY_INFO_ACK BIT(7) +#define WPA_KEY_INFO_MIC BIT(8) +#define WPA_KEY_INFO_SECURE BIT(9) +#define WPA_KEY_INFO_ERROR BIT(10) +#define WPA_KEY_INFO_REQUEST BIT(11) +#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */ + + + +static int wpa_selector_to_bitfield(const u8 *s) +{ + if (memcmp(s, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN) == 0) + return IW_AUTH_CIPHER_NONE; + if (memcmp(s, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN) == 0) + return IW_AUTH_CIPHER_WEP40; + if (memcmp(s, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN) == 0) + return IW_AUTH_CIPHER_TKIP; + if (memcmp(s, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN) == 0) + return IW_AUTH_CIPHER_CCMP; + if (memcmp(s, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN) == 0) + return IW_AUTH_CIPHER_WEP104; + return 0; +} + + +static int wpa_key_mgmt_to_bitfield(const u8 *s) +{ + if (memcmp(s, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN) == 0) + return IW_AUTH_KEY_MGMT_802_1X; + if (memcmp(s, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, WPA_SELECTOR_LEN) == + 0) + return IW_AUTH_KEY_MGMT_PSK; + if (memcmp(s, WPA_AUTH_KEY_MGMT_NONE, WPA_SELECTOR_LEN) == 0) + return 0; + return 0; +} + + +static int rsn_selector_to_bitfield(const u8 *s) +{ + if (memcmp(s, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN) == 0) + return IW_AUTH_CIPHER_NONE; + if (memcmp(s, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN) == 0) + return IW_AUTH_CIPHER_WEP40; + if (memcmp(s, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN) == 0) + return IW_AUTH_CIPHER_TKIP; + if (memcmp(s, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN) == 0) + return IW_AUTH_CIPHER_CCMP; + if (memcmp(s, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN) == 0) + return IW_AUTH_CIPHER_WEP104; + return 0; +} + + +static int rsn_key_mgmt_to_bitfield(const u8 *s) +{ + if (memcmp(s, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN) == 0) + return IW_AUTH_KEY_MGMT_802_1X; + if (memcmp(s, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, RSN_SELECTOR_LEN) == + 0) + return IW_AUTH_KEY_MGMT_PSK; + return 0; +} + + +static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + const struct wpa_ie_hdr *hdr; + const u8 *pos; + int left; + int i, count; + + data->proto = IW_AUTH_WPA_VERSION_WPA; + data->pairwise_cipher = IW_AUTH_CIPHER_TKIP; + data->group_cipher = IW_AUTH_CIPHER_TKIP; + data->key_mgmt = IW_AUTH_KEY_MGMT_802_1X; + data->capabilities = 0; + data->pmkid = NULL; + data->num_pmkid = 0; + + if (wpa_ie_len == 0) { + /* No WPA IE - fail silently */ + return -1; + } + + if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) { + nm_log_dbg (LOGD_WIFI, "IE len too short %lu", + (unsigned long) wpa_ie_len); + return -1; + } + + hdr = (const struct wpa_ie_hdr *) wpa_ie; + + if (hdr->elem_id != WPA_GENERIC_INFO_ELEM || + hdr->len != wpa_ie_len - 2 || + memcmp(hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN) != 0 || + WPA_GET_LE16(hdr->version) != WPA_VERSION) { + nm_log_dbg (LOGD_WIFI, "malformed IE or unknown version"); + return -1; + } + + pos = (const u8 *) (hdr + 1); + left = wpa_ie_len - sizeof(*hdr); + + if (left >= WPA_SELECTOR_LEN) { + data->group_cipher = wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } else if (left > 0) { + nm_log_dbg (LOGD_WIFI, "IE length mismatch, %u too much", left); + return -1; + } + + if (left >= 2) { + data->pairwise_cipher = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) { + nm_log_dbg (LOGD_WIFI, "IE count botch (pairwise), " + "count %u left %u", count, left); + return -1; + } + for (i = 0; i < count; i++) { + data->pairwise_cipher |= wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) { + nm_log_dbg (LOGD_WIFI, "IE too short (for key mgmt)"); + return -1; + } + + if (left >= 2) { + data->key_mgmt = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) { + nm_log_dbg (LOGD_WIFI, "IE count botch (key mgmt), " + "count %u left %u", count, left); + return -1; + } + for (i = 0; i < count; i++) { + data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) { + nm_log_dbg (LOGD_WIFI, "IE too short (for capabilities)"); + return -1; + } + + if (left >= 2) { + if (WPA_GET_LE16 (pos) & WPA_CAPABILITY_PREAUTH) + data->capabilities |= IW_PMKID_CAND_PREAUTH; + pos += 2; + left -= 2; + } + + if (left > 0) { + nm_log_dbg (LOGD_WIFI, "IE has %u trailing bytes", left); + return -1; + } + + return 0; +} + + +static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, + struct wpa_ie_data *data) +{ + const struct rsn_ie_hdr *hdr; + const u8 *pos; + int left; + int i, count; + + data->proto = IW_AUTH_WPA_VERSION_WPA2; + data->pairwise_cipher = IW_AUTH_CIPHER_CCMP; + data->group_cipher = IW_AUTH_CIPHER_CCMP; + data->key_mgmt = IW_AUTH_KEY_MGMT_802_1X; + data->capabilities = 0; + data->pmkid = NULL; + data->num_pmkid = 0; + + if (rsn_ie_len == 0) { + /* No RSN IE - fail silently */ + return -1; + } + + if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) { + nm_log_dbg (LOGD_WIFI, "IE len too short %lu", + (unsigned long) rsn_ie_len); + return -1; + } + + hdr = (const struct rsn_ie_hdr *) rsn_ie; + + if (hdr->elem_id != WPA_RSN_INFO_ELEM || + hdr->len != rsn_ie_len - 2 || + WPA_GET_LE16(hdr->version) != RSN_VERSION) { + nm_log_dbg (LOGD_WIFI, "malformed IE or unknown version"); + return -1; + } + + pos = (const u8 *) (hdr + 1); + left = rsn_ie_len - sizeof(*hdr); + + if (left >= RSN_SELECTOR_LEN) { + data->group_cipher = rsn_selector_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } else if (left > 0) { + nm_log_dbg (LOGD_WIFI, "IE length mismatch, %u too much", left); + return -1; + } + + if (left >= 2) { + data->pairwise_cipher = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * RSN_SELECTOR_LEN) { + nm_log_dbg (LOGD_WIFI, "IE count botch (pairwise), " + "count %u left %u", count, left); + return -1; + } + for (i = 0; i < count; i++) { + data->pairwise_cipher |= rsn_selector_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } + } else if (left == 1) { + nm_log_dbg (LOGD_WIFI, "IE too short (for key mgmt)"); + return -1; + } + + if (left >= 2) { + data->key_mgmt = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * RSN_SELECTOR_LEN) { + nm_log_dbg (LOGD_WIFI, "IE count botch (key mgmt), " + "count %u left %u", count, left); + return -1; + } + for (i = 0; i < count; i++) { + data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } + } else if (left == 1) { + nm_log_dbg (LOGD_WIFI, "IE too short (for capabilities)"); + return -1; + } + + if (left >= 2) { + if (WPA_GET_LE16 (pos) & WPA_CAPABILITY_PREAUTH) + data->capabilities |= IW_PMKID_CAND_PREAUTH; + pos += 2; + left -= 2; + } + + if (left >= 2) { + data->num_pmkid = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (left < data->num_pmkid * PMKID_LEN) { + nm_log_dbg (LOGD_WIFI, "PMKID underflow " + "(num_pmkid=%d left=%d)", + data->num_pmkid, left); + data->num_pmkid = 0; + } else { + data->pmkid = pos; + pos += data->num_pmkid * PMKID_LEN; + left -= data->num_pmkid * PMKID_LEN; + } + } + + if (left > 0) { + nm_log_dbg (LOGD_WIFI, "IE has %u trailing bytes - ignored", left); + } + + return 0; +} + + +/** + * wpa_parse_wpa_ie - Parse WPA/RSN IE + * @wpa_ie: Pointer to WPA or RSN IE + * @wpa_ie_len: Length of the WPA/RSN IE + * @data: Pointer to data area for parsing results + * Returns: parsed results on success, NULL on failure + * + * Parse the contents of WPA or RSN IE and write the parsed data into data. + */ +wpa_ie_data * wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len) +{ + wpa_ie_data *data = NULL; + int err = -1; + + if (!wpa_ie || wpa_ie_len <= 0) + return NULL; + + data = g_slice_new0 (wpa_ie_data); + + if (wpa_ie_len >= 1 && wpa_ie[0] == WPA_RSN_INFO_ELEM) + err = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); + else + err = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data); + + if (err != 0) { + g_slice_free (wpa_ie_data, data); + data = NULL; + } + + if (data) { + nm_log_dbg (LOGD_WIFI, "WPA IE: -------------------"); + nm_log_dbg (LOGD_WIFI, " proto 0x%X", data->proto); + nm_log_dbg (LOGD_WIFI, " pw cipher 0x%X", data->pairwise_cipher); + nm_log_dbg (LOGD_WIFI, " gr cipher 0x%X", data->group_cipher); + nm_log_dbg (LOGD_WIFI, " key mgmt 0x%X", data->key_mgmt); + nm_log_dbg (LOGD_WIFI, " capabilities 0x%X", data->capabilities); + nm_log_dbg (LOGD_WIFI, " # pmkid 0x%X", data->num_pmkid); + nm_log_dbg (LOGD_WIFI, ""); + } + + return data; +} + diff --git a/src/wpa.h b/src/wpa.h new file mode 100644 index 00000000..18d1dbee --- /dev/null +++ b/src/wpa.h @@ -0,0 +1,38 @@ +/* + * wpa_supplicant - WPA definitions + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this file may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_H +#define WPA_H + +#include <stdint.h> + +#define WPA_GENERIC_INFO_ELEM 0xdd +#define WPA_RSN_INFO_ELEM 0x30 + +#define WPA_MAX_IE_LEN 40 + +typedef struct wpa_ie_data { + int proto; + int pairwise_cipher; + int group_cipher; + int key_mgmt; + int capabilities; + int num_pmkid; + const uint8_t *pmkid; +} wpa_ie_data; + + +wpa_ie_data * wpa_parse_wpa_ie (const uint8_t *wpa_ie, size_t wpa_ie_len); + +#endif /* WPA_H */ |