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/dhcp-manager | |
Imported Upstream version 0.8.1 upstream/0.8.1
Diffstat (limited to 'src/dhcp-manager')
| -rw-r--r-- | src/dhcp-manager/Makefile.am | 39 | ||||
| -rw-r--r-- | src/dhcp-manager/Makefile.in | 610 | ||||
| -rw-r--r-- | src/dhcp-manager/nm-dhcp-client.c | 1243 | ||||
| -rw-r--r-- | src/dhcp-manager/nm-dhcp-client.h | 142 | ||||
| -rw-r--r-- | src/dhcp-manager/nm-dhcp-dhclient.c | 786 | ||||
| -rw-r--r-- | src/dhcp-manager/nm-dhcp-dhclient.h | 49 | ||||
| -rw-r--r-- | src/dhcp-manager/nm-dhcp-dhcpcd.c | 294 | ||||
| -rw-r--r-- | src/dhcp-manager/nm-dhcp-dhcpcd.h | 49 | ||||
| -rw-r--r-- | src/dhcp-manager/nm-dhcp-manager.c | 647 | ||||
| -rw-r--r-- | src/dhcp-manager/nm-dhcp-manager.h | 98 |
10 files changed, 3957 insertions, 0 deletions
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 */ |