diff options
Diffstat (limited to 'src/dhcp-manager')
-rw-r--r-- | src/dhcp-manager/Makefile.am | 38 | ||||
-rw-r--r-- | src/dhcp-manager/nm-dhcp-client.c | 28 | ||||
-rw-r--r-- | src/dhcp-manager/nm-dhcp-client.h | 25 | ||||
-rw-r--r-- | src/dhcp-manager/nm-dhcp-dhclient-utils.c | 217 | ||||
-rw-r--r-- | src/dhcp-manager/nm-dhcp-dhclient-utils.h | 35 | ||||
-rw-r--r-- | src/dhcp-manager/nm-dhcp-dhclient.c | 174 | ||||
-rw-r--r-- | src/dhcp-manager/nm-dhcp-dhcpcd.c | 8 | ||||
-rw-r--r-- | src/dhcp-manager/nm-dhcp-manager.c | 2 | ||||
-rw-r--r-- | src/dhcp-manager/tests/Makefile.am | 28 | ||||
-rw-r--r-- | src/dhcp-manager/tests/test-dhcp-dhclient.c | 249 |
10 files changed, 657 insertions, 147 deletions
diff --git a/src/dhcp-manager/Makefile.am b/src/dhcp-manager/Makefile.am index e29c6718a3..14ddde0bd2 100644 --- a/src/dhcp-manager/Makefile.am +++ b/src/dhcp-manager/Makefile.am @@ -1,3 +1,5 @@ +SUBDIRS=. tests + INCLUDES = \ -I${top_srcdir} \ -I${top_srcdir}/include \ @@ -6,15 +8,40 @@ INCLUDES = \ -I${top_srcdir}/libnm-util \ -I${top_srcdir}/src -noinst_LTLIBRARIES = libdhcp-manager.la +noinst_LTLIBRARIES = libdhcp-manager.la libdhcp-dhclient.la + +################## dhclient ################## + +libdhcp_dhclient_la_SOURCES = \ + nm-dhcp-dhclient-utils.h \ + nm-dhcp-dhclient-utils.c \ + nm-dhcp-dhclient.h \ + nm-dhcp-dhclient.c + +libdhcp_dhclient_la_CPPFLAGS = \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DLOCALSTATEDIR=\"$(localstatedir)\" \ + -DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \ + -DDHCLIENT_V$(DHCLIENT_VERSION) + +libdhcp_dhclient_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) + +################## main lib ################## 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 @@ -22,18 +49,15 @@ 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)\" \ - -DDHCLIENT_V$(DHCLIENT_VERSION) \ -DDHCPCD_PATH=\"$(DHCPCD_PATH)\" libdhcp_manager_la_LIBADD = \ $(top_builddir)/marshallers/libmarshallers.la \ $(top_builddir)/src/logging/libnm-logging.la \ + $(builddir)/libdhcp-dhclient.la \ $(DBUS_LIBS) \ $(GLIB_LIBS) diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c index 0c7f3d49b5..c4dfec64d7 100644 --- a/src/dhcp-manager/nm-dhcp-client.c +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -136,10 +136,10 @@ watch_cleanup (NMDHCPClient *self) } } -static void -stop_process (GPid pid, const char *iface) +void +nm_dhcp_client_stop_pid (GPid pid, const char *iface, guint timeout_secs) { - int i = 15; /* 3 seconds */ + int i = (timeout_secs ? timeout_secs : 3) * 5; /* default 3 seconds */ g_return_if_fail (pid > 0); @@ -156,11 +156,15 @@ stop_process (GPid pid, const char *iface) if (ret == -1) { /* Child already exited */ - if (errno == ECHILD) + if (errno == ECHILD) { + /* Was it really our child and it exited? */ + if (kill (pid, 0) < 0 && errno == ESRCH) + break; + } else { + /* Took too long; shoot it in the head */ + i = 0; break; - /* Took too long; shoot it in the head */ - i = 0; - break; + } } g_usleep (G_USEC_PER_SEC / 5); } @@ -179,7 +183,7 @@ stop_process (GPid pid, const char *iface) } static void -real_stop (NMDHCPClient *self) +real_stop (NMDHCPClient *self, gboolean release) { NMDHCPClientPrivate *priv; @@ -192,7 +196,7 @@ real_stop (NMDHCPClient *self) /* Clean up the watch handler since we're explicitly killing the daemon */ watch_cleanup (self); - stop_process (priv->pid, priv->iface); + nm_dhcp_client_stop_pid (priv->pid, priv->iface, 0); priv->info_only = FALSE; } @@ -372,7 +376,7 @@ nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name) exe = proc_contents; if (!strcmp (exe, binary_name)) - stop_process ((GPid) tmp, NULL); + nm_dhcp_client_stop_pid ((GPid) tmp, NULL, 0); } } @@ -383,7 +387,7 @@ nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name) } void -nm_dhcp_client_stop (NMDHCPClient *self) +nm_dhcp_client_stop (NMDHCPClient *self, gboolean release) { NMDHCPClientPrivate *priv; @@ -394,7 +398,7 @@ nm_dhcp_client_stop (NMDHCPClient *self) /* Kill the DHCP client */ if (!priv->dead) { - NM_DHCP_CLIENT_GET_CLASS (self)->stop (self); + NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release); priv->dead = TRUE; nm_log_info (LOGD_DHCP, "(%s): canceled DHCP transaction, DHCP client pid %d", diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h index f357170b9c..19c7365edc 100644 --- a/src/dhcp-manager/nm-dhcp-client.h +++ b/src/dhcp-manager/nm-dhcp-client.h @@ -76,18 +76,19 @@ typedef struct { /* Methods */ - GPid (*ip4_start) (NMDHCPClient *self, - NMSettingIP4Config *s_ip4, - guint8 *anycast_addr, - const char *hostname); + GPid (*ip4_start) (NMDHCPClient *self, + NMSettingIP4Config *s_ip4, + guint8 *anycast_addr, + const char *hostname); - GPid (*ip6_start) (NMDHCPClient *self, - NMSettingIP6Config *s_ip6, - guint8 *anycast_addr, - const char *hostname, - gboolean info_only); + GPid (*ip6_start) (NMDHCPClient *self, + NMSettingIP6Config *s_ip6, + guint8 *anycast_addr, + const char *hostname, + gboolean info_only); - void (*stop) (NMDHCPClient *self); + void (*stop) (NMDHCPClient *self, + gboolean release); /* Signals */ void (*state_changed) (NMDHCPClient *self, NMDHCPState state); @@ -116,7 +117,7 @@ gboolean nm_dhcp_client_start_ip6 (NMDHCPClient *self, const char *hostname, gboolean info_only); -void nm_dhcp_client_stop (NMDHCPClient *self); +void nm_dhcp_client_stop (NMDHCPClient *self, gboolean release); void nm_dhcp_client_new_options (NMDHCPClient *self, GHashTable *options, @@ -133,5 +134,7 @@ 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); +void nm_dhcp_client_stop_pid (GPid pid, const char *iface, guint timeout_secs); + #endif /* NM_DHCP_CLIENT_H */ diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.c b/src/dhcp-manager/nm-dhcp-dhclient-utils.c new file mode 100644 index 0000000000..cc5255ab59 --- /dev/null +++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.c @@ -0,0 +1,217 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2010 Red Hat, Inc. + */ + +#include <config.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <string.h> +#include <ctype.h> + +#include "nm-dhcp-dhclient-utils.h" + +#define CLIENTID_TAG "send dhcp-client-identifier" +#define CLIENTID_FORMAT CLIENTID_TAG " \"%s\"; # added by NetworkManager" +#define CLIENTID_FORMAT_OCTETS CLIENTID_TAG " %s; # added by NetworkManager" + +#define HOSTNAME_TAG "send host-name" +#define HOSTNAME_FORMAT HOSTNAME_TAG " \"%s\"; # added by NetworkManager" + +#define ALSOREQ_TAG "also request " + +static void +add_also_request (GPtrArray *array, const char *item) +{ + int i; + + for (i = 0; i < array->len; i++) { + if (!strcmp (g_ptr_array_index (array, i), item)) + return; + } + g_ptr_array_add (array, g_strdup (item)); +} + +char * +nm_dhcp_dhclient_create_config (const char *interface, + NMSettingIP4Config *s_ip4, + guint8 *anycast_addr, + const char *hostname, + const char *orig_path, + const char *orig_contents) +{ + GString *new_contents; + GPtrArray *alsoreq; + int i; + + new_contents = g_string_new (_("# Created by NetworkManager\n")); + alsoreq = g_ptr_array_sized_new (5); + + if (orig_contents) { + char **lines, **line; + gboolean in_alsoreq = FALSE; + + 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++) { + char *p = *line; + + if (!strlen (g_strstrip (p))) + continue; + + /* Override config file "dhcp-client-id" and use one from the + * connection. + */ + if ( s_ip4 + && nm_setting_ip4_config_get_dhcp_client_id (s_ip4) + && !strncmp (p, CLIENTID_TAG, strlen (CLIENTID_TAG))) + continue; + + /* Override config file hostname and use one from the connection */ + if (hostname && !strncmp (p, HOSTNAME_TAG, strlen (HOSTNAME_TAG))) + continue; + + /* Check for "also require" */ + if (!strncmp (p, ALSOREQ_TAG, strlen (ALSOREQ_TAG))) { + in_alsoreq = TRUE; + p += strlen (ALSOREQ_TAG); + } + + if (in_alsoreq) { + char **areq, **aiter; + + /* Grab each 'also require' option and save for later */ + areq = g_strsplit_set (p, "\t ,", -1); + for (aiter = areq; aiter && *aiter; aiter++) { + if (!strlen (g_strstrip (*aiter))) + continue; + + if (*aiter[0] == ';') { + /* all done */ + in_alsoreq = FALSE; + break; + } + + if (!isalnum ((*aiter)[0])) + continue; + + if ((*aiter)[strlen (*aiter) - 1] == ';') { + /* Remove the EOL marker */ + (*aiter)[strlen (*aiter) - 1] = '\0'; + in_alsoreq = FALSE; + } + + add_also_request (alsoreq, *aiter); + } + + if (areq) + g_strfreev (areq); + + continue; + } + + /* Existing configuration line is OK, add it to new configuration */ + g_string_append (new_contents, *line); + g_string_append_c (new_contents, '\n'); + } + + if (lines) + g_strfreev (lines); + } else + g_string_append_c (new_contents, '\n'); + + /* Add NM options from connection */ + if (s_ip4) { + const char *tmp; + gboolean added = FALSE; + + 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, + * because 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, CLIENTID_FORMAT_OCTETS "\n", tmp); + else + g_string_append_printf (new_contents, CLIENTID_FORMAT "\n", tmp); + added = TRUE; + } + + if (hostname) { + g_string_append_printf (new_contents, HOSTNAME_FORMAT "\n", hostname); + added = TRUE; + } + + if (added) + g_string_append_c (new_contents, '\n'); + } + + /* Define options for classless static routes */ + g_string_append (new_contents, + "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"); + g_string_append (new_contents, + "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"); + /* Web Proxy Auto-Discovery option (bgo #368423) */ + g_string_append (new_contents, "option wpad code 252 = string;\n"); + + g_string_append_c (new_contents, '\n'); + + /* Everything we want to request from the DHCP server */ + add_also_request (alsoreq, "rfc3442-classless-static-routes"); + add_also_request (alsoreq, "ms-classless-static-routes"); + add_also_request (alsoreq, "wpad"); + add_also_request (alsoreq, "ntp-servers"); + + /* And add it to the dhclient configuration */ + for (i = 0; i < alsoreq->len; i++) { + char *t = g_ptr_array_index (alsoreq, i); + + g_string_append_printf (new_contents, "also request %s;\n", t); + g_free (t); + } + g_ptr_array_free (alsoreq, TRUE); + + g_string_append_c (new_contents, '\n'); + + 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", + interface, + anycast_addr[0], anycast_addr[1], + anycast_addr[2], anycast_addr[3], + anycast_addr[4], anycast_addr[5]); + } + + return g_string_free (new_contents, FALSE); +} + diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.h b/src/dhcp-manager/nm-dhcp-dhclient-utils.h new file mode 100644 index 0000000000..77def55182 --- /dev/null +++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.h @@ -0,0 +1,35 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2010 Red Hat, Inc. + */ + +#ifndef NM_DHCP_DHCLIENT_UTILS_H +#define NM_DHCP_DHCLIENT_UTILS_H + +#include <glib.h> +#include <glib-object.h> + +#include <nm-setting-ip4-config.h> + +char *nm_dhcp_dhclient_create_config (const char *interface, + NMSettingIP4Config *s_ip4, + guint8 *anycast_addr, + const char *hostname, + const char *orig_path, + const char *orig_contents); + +#endif /* NM_DHCP_DHCLIENT_UTILS_H */ + diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index d8781524e9..75684459f6 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -39,6 +39,7 @@ #include "nm-dhcp-dhclient.h" #include "nm-utils.h" #include "nm-logging.h" +#include "nm-dhcp-dhclient-utils.h" G_DEFINE_TYPE (NMDHCPDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT) @@ -302,12 +303,6 @@ out: } -#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, @@ -318,105 +313,27 @@ merge_dhclient_config (const char *iface, const char *orig_path, GError **error) { - GString *new_contents; - char *orig_contents = NULL; + char *orig = NULL, *new; 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)) { + if (!g_file_get_contents (orig_path, &orig, 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 - && hostname - && !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, - * because 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); - } - - if (hostname) - g_string_append_printf (new_contents, DHCP_HOSTNAME_FORMAT "\n", hostname); - } - - 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); + new = nm_dhcp_dhclient_create_config (iface, s_ip4, anycast_addr, hostname, orig_path, orig); + g_assert (new); + success = g_file_set_contents (conf_file, new, -1, error); + g_free (new); - g_string_free (new_contents, TRUE); return success; } @@ -494,15 +411,15 @@ dhclient_child_setup (gpointer user_data G_GNUC_UNUSED) static GPid dhclient_start (NMDHCPClient *client, - const char *ip_opt, - const char *mode_opt) + const char *mode_opt, + gboolean release) { 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; + char *binary_name, *cmd_str, *pid_file = NULL; gboolean ipv6; guint log_domain; @@ -519,28 +436,33 @@ dhclient_start (NMDHCPClient *client, nm_log_warn (log_domain, "(%s): ISC dhcp3 does not support IPv6", iface); return -1; } -#else - g_return_val_if_fail (ip_opt != NULL, -1); #endif - 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); + if (!g_file_test (priv->path, G_FILE_TEST_EXISTS)) { + nm_log_warn (log_domain, "%s does not exist.", priv->path); return -1; } - if (!g_file_test (priv->path, G_FILE_TEST_EXISTS)) { - nm_log_warn (log_domain, "%s does not exist.", priv->path); + pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhclient%s-%s.pid", + ipv6 ? "6" : "", + iface); + if (!pid_file) { + nm_log_warn (log_domain, "(%s): not enough memory for dhcpcd options.", iface); 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); + nm_dhcp_client_stop_existing (pid_file, binary_name); g_free (binary_name); + if (release) { + /* release doesn't use the pidfile after killing an old client */ + g_free (pid_file); + pid_file = NULL; + } + + g_free (priv->lease_file); 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); @@ -552,17 +474,26 @@ dhclient_start (NMDHCPClient *client, g_ptr_array_add (argv, (gpointer) "-d"); + if (release) + g_ptr_array_add (argv, (gpointer) "-r"); + #if !defined(DHCLIENT_V3) - g_ptr_array_add (argv, (gpointer) ip_opt); - if (mode_opt) - g_ptr_array_add (argv, (gpointer) mode_opt); + if (ipv6) { + g_ptr_array_add (argv, (gpointer) "-6"); + if (mode_opt) + g_ptr_array_add (argv, (gpointer) mode_opt); + } else { + g_ptr_array_add (argv, (gpointer) "-4"); + } #endif 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); + if (pid_file) { + g_ptr_array_add (argv, (gpointer) "-pf"); /* Set pid file */ + g_ptr_array_add (argv, (gpointer) pid_file); + } g_ptr_array_add (argv, (gpointer) "-lf"); /* Set lease file */ g_ptr_array_add (argv, (gpointer) priv->lease_file); @@ -584,8 +515,10 @@ dhclient_start (NMDHCPClient *client, nm_log_warn (log_domain, "dhclient failed to start: '%s'", error->message); g_error_free (error); pid = -1; - } else + } else { nm_log_info (log_domain, "dhclient started with pid %d", pid); + priv->pid_file = pid_file; + } g_ptr_array_free (argv, TRUE); return pid; @@ -608,7 +541,7 @@ real_ip4_start (NMDHCPClient *client, return -1; } - return dhclient_start (client, "-4", NULL); + return dhclient_start (client, NULL, FALSE); } static GPid @@ -618,21 +551,34 @@ real_ip6_start (NMDHCPClient *client, const char *hostname, gboolean info_only) { - return dhclient_start (client, "-6", info_only ? "-S" : "-N"); + return dhclient_start (client, info_only ? "-S" : "-N", FALSE); } static void -real_stop (NMDHCPClient *client) +real_stop (NMDHCPClient *client, gboolean release) { NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client); /* Chain up to parent */ - NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client); + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client, release); if (priv->conf_file) remove (priv->conf_file); - if (priv->pid_file) + if (priv->pid_file) { remove (priv->pid_file); + g_free (priv->pid_file); + priv->pid_file = NULL; + } + + if (release) { + GPid rpid; + + rpid = dhclient_start (client, NULL, TRUE); + if (rpid > 0) { + /* Wait a few seconds for the release to happen */ + nm_dhcp_client_stop_pid (rpid, nm_dhcp_client_get_iface (client), 5); + } + } } /***************************************************/ diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c index 378a97b611..237661fe47 100644 --- a/src/dhcp-manager/nm-dhcp-dhcpcd.c +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c @@ -128,6 +128,8 @@ real_ip4_start (NMDHCPClient *client, g_ptr_array_add (argv, (gpointer) "-L"); /* Disable built-in IPv4LL since we use avahi-autoipd */ + g_ptr_array_add (argv, (gpointer) "-G"); /* Let NM handle routing */ + g_ptr_array_add (argv, (gpointer) "-c"); /* Set script file */ g_ptr_array_add (argv, (gpointer) ACTION_SCRIPT_PATH ); @@ -168,15 +170,17 @@ real_ip6_start (NMDHCPClient *client, } static void -real_stop (NMDHCPClient *client) +real_stop (NMDHCPClient *client, gboolean release) { NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client); /* Chain up to parent */ - NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client); + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client, release); if (priv->pid_file) remove (priv->pid_file); + + /* FIXME: implement release... */ } /***************************************************/ diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c index a1e3e5e24d..d3c064199c 100644 --- a/src/dhcp-manager/nm-dhcp-manager.c +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -429,7 +429,7 @@ client_start (NMDHCPManager *self, /* Kill any old client instance */ client = get_client_for_iface (self, iface, ipv6); if (client) { - nm_dhcp_client_stop (client); + nm_dhcp_client_stop (client, FALSE); remove_client (self, client); } diff --git a/src/dhcp-manager/tests/Makefile.am b/src/dhcp-manager/tests/Makefile.am new file mode 100644 index 0000000000..b075fd683a --- /dev/null +++ b/src/dhcp-manager/tests/Makefile.am @@ -0,0 +1,28 @@ +INCLUDES = \ + -I$(top_srcdir)/include \ + -I${top_srcdir}/libnm-util \ + -I$(top_srcdir)/src/dhcp-manager + +noinst_PROGRAMS = test-dhcp-dhclient + +####### policy /etc/hosts test ####### + +test_dhcp_dhclient_SOURCES = \ + test-dhcp-dhclient.c + +test_dhcp_dhclient_CPPFLAGS = \ + $(GLIB_CFLAGS) + +test_dhcp_dhclient_LDADD = \ + -ldl \ + $(top_builddir)/src/dhcp-manager/libdhcp-dhclient.la \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(GLIB_LIBS) + +if WITH_TESTS + +check-local: test-dhcp-dhclient + $(abs_builddir)/test-dhcp-dhclient + +endif + diff --git a/src/dhcp-manager/tests/test-dhcp-dhclient.c b/src/dhcp-manager/tests/test-dhcp-dhclient.c new file mode 100644 index 0000000000..c1cd6f80c4 --- /dev/null +++ b/src/dhcp-manager/tests/test-dhcp-dhclient.c @@ -0,0 +1,249 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2010 Red Hat, Inc. + * + */ + +#include <glib.h> +#include <string.h> + +#include "nm-dhcp-dhclient-utils.h" +#include "nm-utils.h" + +#define DEBUG 0 + +static void +test_config (const char *orig, + const char *expected, + const char *hostname, + const char *dhcp_client_id, + const char *iface, + guint8 *anycast_addr) +{ + NMSettingIP4Config *s_ip4; + char *new; + + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, dhcp_client_id, NULL); + + new = nm_dhcp_dhclient_create_config (iface, + s_ip4, + anycast_addr, + hostname, + "/path/to/dhclient.conf", + orig); + g_assert (new != NULL); + +#if DEBUG + g_message ("\n- NEW ---------------------------------\n" + "%s" + "+ EXPECTED ++++++++++++++++++++++++++++++\n" + "%s" + "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + new, expected); +#endif + g_assert (strlen (new) == strlen (expected)); + g_assert (strcmp (new, expected) == 0); + g_free (new); +} + +/*******************************************/ + +static const char *orig_missing_expected = \ + "# Created by NetworkManager\n" + "\n" + "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n" + "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n" + "option wpad code 252 = string;\n" + "\n" + "also request rfc3442-classless-static-routes;\n" + "also request ms-classless-static-routes;\n" + "also request wpad;\n" + "also request ntp-servers;\n" + "\n"; + +static void +test_orig_missing (void) +{ + test_config (NULL, orig_missing_expected, + NULL, + NULL, + "eth0", + NULL); +} + +/*******************************************/ + +static const char *override_client_id_orig = \ + "send dhcp-client-identifier 00:30:04:20:7A:08;\n"; + +static const char *override_client_id_expected = \ + "# Created by NetworkManager\n" + "# Merged from /path/to/dhclient.conf\n" + "\n" + "send dhcp-client-identifier 11:22:33:44:55:66; # added by NetworkManager\n" + "\n" + "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n" + "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n" + "option wpad code 252 = string;\n" + "\n" + "also request rfc3442-classless-static-routes;\n" + "also request ms-classless-static-routes;\n" + "also request wpad;\n" + "also request ntp-servers;\n" + "\n"; + +static void +test_override_client_id (void) +{ + test_config (override_client_id_orig, override_client_id_expected, + NULL, + "11:22:33:44:55:66", + "eth0", + NULL); +} + +/*******************************************/ + +static const char *override_hostname_orig = \ + "send host-name \"foobar\";\n"; + +static const char *override_hostname_expected = \ + "# Created by NetworkManager\n" + "# Merged from /path/to/dhclient.conf\n" + "\n" + "send host-name \"blahblah\"; # added by NetworkManager\n" + "\n" + "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n" + "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n" + "option wpad code 252 = string;\n" + "\n" + "also request rfc3442-classless-static-routes;\n" + "also request ms-classless-static-routes;\n" + "also request wpad;\n" + "also request ntp-servers;\n" + "\n"; + +static void +test_override_hostname (void) +{ + test_config (override_hostname_orig, override_hostname_expected, + "blahblah", + NULL, + "eth0", + NULL); +} + +/*******************************************/ + +static const char *existing_alsoreq_orig = \ + "also request something;\n" + "also request another-thing;\n" + ; + +static const char *existing_alsoreq_expected = \ + "# Created by NetworkManager\n" + "# Merged from /path/to/dhclient.conf\n" + "\n" + "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n" + "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n" + "option wpad code 252 = string;\n" + "\n" + "also request something;\n" + "also request another-thing;\n" + "also request rfc3442-classless-static-routes;\n" + "also request ms-classless-static-routes;\n" + "also request wpad;\n" + "also request ntp-servers;\n" + "\n"; + +static void +test_existing_alsoreq (void) +{ + test_config (existing_alsoreq_orig, existing_alsoreq_expected, + NULL, + NULL, + "eth0", + NULL); +} + +/*******************************************/ + +static const char *existing_multiline_alsoreq_orig = \ + "also request something another-thing yet-another-thing\n" + " foobar baz blah;\n" + ; + +static const char *existing_multiline_alsoreq_expected = \ + "# Created by NetworkManager\n" + "# Merged from /path/to/dhclient.conf\n" + "\n" + "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n" + "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n" + "option wpad code 252 = string;\n" + "\n" + "also request something;\n" + "also request another-thing;\n" + "also request yet-another-thing;\n" + "also request foobar;\n" + "also request baz;\n" + "also request blah;\n" + "also request rfc3442-classless-static-routes;\n" + "also request ms-classless-static-routes;\n" + "also request wpad;\n" + "also request ntp-servers;\n" + "\n"; + +static void +test_existing_multiline_alsoreq (void) +{ + test_config (existing_multiline_alsoreq_orig, existing_multiline_alsoreq_expected, + NULL, + NULL, + "eth0", + NULL); +} + +/*******************************************/ + +#if GLIB_CHECK_VERSION(2,25,12) +typedef GTestFixtureFunc TCFunc; +#else +typedef void (*TCFunc)(void); +#endif + +#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL) + +int main (int argc, char **argv) +{ + GTestSuite *suite; + + g_test_init (&argc, &argv, NULL); + + g_type_init (); + + suite = g_test_get_root (); + + g_test_suite_add (suite, TESTCASE (test_orig_missing, NULL)); + g_test_suite_add (suite, TESTCASE (test_override_client_id, NULL)); + g_test_suite_add (suite, TESTCASE (test_override_hostname, NULL)); + g_test_suite_add (suite, TESTCASE (test_existing_alsoreq, NULL)); + g_test_suite_add (suite, TESTCASE (test_existing_multiline_alsoreq, NULL)); + + return g_test_run (); +} + |