1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
/* NetworkManager -- Network link manager
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or
7
* (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License along
15
* with this program; if not, write to the Free Software Foundation, Inc.,
16
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
* Copyright (C) 2008 - 2010 Red Hat, Inc.
22
#include <sys/types.h>
27
#include <arpa/inet.h>
30
#include "nm-dnsmasq-manager.h"
31
#include "nm-logging.h"
32
#include "nm-glib-compat.h"
39
} NMDnsMasqManagerPrivate;
41
#define NM_DNSMASQ_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DNSMASQ_MANAGER, NMDnsMasqManagerPrivate))
43
G_DEFINE_TYPE (NMDnsMasqManager, nm_dnsmasq_manager, G_TYPE_OBJECT)
51
static guint signals[LAST_SIGNAL] = { 0 };
54
NM_DNSMASQ_MANAGER_ERROR_NOT_FOUND,
55
} NMDnsMasqManagerError;
58
nm_dnsmasq_manager_error_quark (void)
63
quark = g_quark_from_static_string ("nm_dnsmasq_manager_error");
69
nm_dnsmasq_manager_init (NMDnsMasqManager *manager)
74
finalize (GObject *object)
76
NMDnsMasqManagerPrivate *priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (object);
78
nm_dnsmasq_manager_stop (NM_DNSMASQ_MANAGER (object));
81
g_free (priv->pidfile);
83
G_OBJECT_CLASS (nm_dnsmasq_manager_parent_class)->finalize (object);
87
nm_dnsmasq_manager_class_init (NMDnsMasqManagerClass *manager_class)
89
GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
91
g_type_class_add_private (manager_class, sizeof (NMDnsMasqManagerPrivate));
93
object_class->finalize = finalize;
96
signals[STATE_CHANGED] =
97
g_signal_new ("state-changed",
98
G_OBJECT_CLASS_TYPE (object_class),
100
G_STRUCT_OFFSET (NMDnsMasqManagerClass, state_changed),
102
g_cclosure_marshal_VOID__UINT,
108
nm_dnsmasq_manager_new (const char *iface)
110
NMDnsMasqManager *manager;
111
NMDnsMasqManagerPrivate *priv;
113
manager = (NMDnsMasqManager *) g_object_new (NM_TYPE_DNSMASQ_MANAGER, NULL);
117
priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (manager);
118
priv->iface = g_strdup (iface);
119
priv->pidfile = g_strdup_printf (LOCALSTATEDIR "/run/nm-dnsmasq-%s.pid", iface);
130
nm_cmd_line_new (void)
134
cmd = g_slice_new (NMCmdLine);
135
cmd->array = g_ptr_array_new ();
136
cmd->chunk = g_string_chunk_new (1024);
142
nm_cmd_line_destroy (NMCmdLine *cmd)
144
g_ptr_array_free (cmd->array, TRUE);
145
g_string_chunk_free (cmd->chunk);
146
g_slice_free (NMCmdLine, cmd);
150
nm_cmd_line_to_str (NMCmdLine *cmd)
154
g_ptr_array_add (cmd->array, NULL);
155
str = g_strjoinv (" ", (gchar **) cmd->array->pdata);
156
g_ptr_array_remove_index (cmd->array, cmd->array->len - 1);
162
nm_cmd_line_add_string (NMCmdLine *cmd, const char *str)
164
g_ptr_array_add (cmd->array, g_string_chunk_insert (cmd->chunk, str));
167
/*******************************************/
169
static inline const char *
170
nm_find_dnsmasq (void)
172
static const char *dnsmasq_binary_paths[] = {
173
"/usr/local/sbin/dnsmasq",
179
const char **dnsmasq_binary = dnsmasq_binary_paths;
181
while (*dnsmasq_binary != NULL) {
182
if (g_file_test (*dnsmasq_binary, G_FILE_TEST_EXISTS))
187
return *dnsmasq_binary;
191
dm_exit_code (guint dm_exit_status)
193
char *msg = "Unknown error";
195
switch (dm_exit_status) {
197
msg = "Configuration problem";
200
msg = "Network access problem (address in use; permissions; etc)";
203
msg = "Filesystem problem (missing file/directory; permissions; etc)";
206
msg = "Memory allocation failure";
209
msg = "Other problem";
212
if (dm_exit_status >= 11)
213
msg = "Lease-script 'init' process failure";
217
nm_log_warn (LOGD_SHARING, "dnsmasq exited with error: %s (%d)", msg, dm_exit_status);
221
dm_watch_cb (GPid pid, gint status, gpointer user_data)
223
NMDnsMasqManager *manager = NM_DNSMASQ_MANAGER (user_data);
224
NMDnsMasqManagerPrivate *priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (manager);
227
if (WIFEXITED (status)) {
228
err = WEXITSTATUS (status);
231
} else if (WIFSTOPPED (status)) {
232
nm_log_warn (LOGD_SHARING, "dnsmasq stopped unexpectedly with signal %d", WSTOPSIG (status));
233
} else if (WIFSIGNALED (status)) {
234
nm_log_warn (LOGD_SHARING, "dnsmasq died with signal %d", WTERMSIG (status));
236
nm_log_warn (LOGD_SHARING, "dnsmasq died from an unknown cause");
241
g_signal_emit (manager, signals[STATE_CHANGED], 0, NM_DNSMASQ_STATUS_DEAD);
245
create_dm_cmd_line (const char *iface,
246
NMIP4Config *ip4_config,
250
const char *dm_binary;
255
char buf[INET_ADDRSTRLEN + 15];
256
char localaddr[INET_ADDRSTRLEN + 1];
259
dm_binary = nm_find_dnsmasq ();
261
g_set_error (error, NM_DNSMASQ_MANAGER_ERROR, NM_DNSMASQ_MANAGER_ERROR_NOT_FOUND,
262
"Could not find dnsmasq binary.");
266
/* Find the IP4 address to use */
267
tmp = nm_ip4_config_get_address (ip4_config, 0);
269
/* Create dnsmasq command line */
270
cmd = nm_cmd_line_new ();
271
nm_cmd_line_add_string (cmd, dm_binary);
273
if (getenv ("NM_DNSMASQ_DEBUG")) {
274
nm_cmd_line_add_string (cmd, "--log-dhcp");
275
nm_cmd_line_add_string (cmd, "--log-queries");
278
/* dnsmasq may read from it's default config file location, which if that
279
* location is a valid config file, it will combine with the options here
280
* and cause undesirable side-effects. Like sending bogus IP addresses
281
* as the gateway or whatever. So give dnsmasq a bogus config file
282
* location to avoid screwing up the configuration we're passing to it.
284
memset (buf, 0, sizeof (buf));
285
strcpy (buf, "/tmp/");
286
for (i = 5; i < 15; i++)
287
buf[i] = (char) (g_random_int_range ((guint32) 'a', (guint32) 'z') & 0xFF);
288
strcat (buf, ".conf");
290
nm_cmd_line_add_string (cmd, "--conf-file");
291
nm_cmd_line_add_string (cmd, buf);
293
nm_cmd_line_add_string (cmd, "--no-hosts");
294
nm_cmd_line_add_string (cmd, "--keep-in-foreground");
295
nm_cmd_line_add_string (cmd, "--bind-interfaces");
296
nm_cmd_line_add_string (cmd, "--except-interface=lo");
297
nm_cmd_line_add_string (cmd, "--clear-on-reload");
299
/* Use strict order since in the case of VPN connections, the VPN's
300
* nameservers will be first in resolv.conf, and those need to be tried
301
* first by dnsmasq to successfully resolve names from the VPN.
303
nm_cmd_line_add_string (cmd, "--strict-order");
305
s = g_string_new ("--listen-address=");
306
addr.s_addr = nm_ip4_address_get_address (tmp);
307
if (!inet_ntop (AF_INET, &addr, &localaddr[0], INET_ADDRSTRLEN)) {
308
nm_log_warn (LOGD_SHARING, "error converting IP4 address 0x%X",
309
ntohl (addr.s_addr));
312
g_string_append (s, localaddr);
313
nm_cmd_line_add_string (cmd, s->str);
314
g_string_free (s, TRUE);
316
s = g_string_new ("--dhcp-range=");
318
/* Add start of address range */
319
addr.s_addr = nm_ip4_address_get_address (tmp) + htonl (9);
320
if (!inet_ntop (AF_INET, &addr, &buf[0], INET_ADDRSTRLEN)) {
321
nm_log_warn (LOGD_SHARING, "error converting IP4 address 0x%X",
322
ntohl (addr.s_addr));
325
g_string_append (s, buf);
327
g_string_append_c (s, ',');
329
/* Add end of address range */
330
addr.s_addr = nm_ip4_address_get_address (tmp) + htonl (99);
331
if (!inet_ntop (AF_INET, &addr, &buf[0], INET_ADDRSTRLEN)) {
332
nm_log_warn (LOGD_SHARING, "error converting IP4 address 0x%X",
333
ntohl (addr.s_addr));
336
g_string_append (s, buf);
338
g_string_append (s, ",60m");
339
nm_cmd_line_add_string (cmd, s->str);
340
g_string_free (s, TRUE);
342
s = g_string_new ("--dhcp-option=option:router,");
343
g_string_append (s, localaddr);
344
nm_cmd_line_add_string (cmd, s->str);
345
g_string_free (s, TRUE);
347
nm_cmd_line_add_string (cmd, "--dhcp-lease-max=50");
349
s = g_string_new ("--pid-file=");
350
g_string_append (s, pidfile);
351
nm_cmd_line_add_string (cmd, s->str);
352
g_string_free (s, TRUE);
357
nm_cmd_line_destroy (cmd);
362
dm_child_setup (gpointer user_data G_GNUC_UNUSED)
364
/* We are in the child process at this point */
365
pid_t pid = getpid ();
370
kill_existing_for_iface (const char *iface, const char *pidfile)
372
char *contents = NULL;
374
char *proc_path = NULL;
375
char *cmdline_contents = NULL;
377
if (!g_file_get_contents (pidfile, &contents, NULL, NULL))
380
pid = strtol (contents, NULL, 10);
381
if (pid < 1 || pid > INT_MAX)
384
proc_path = g_strdup_printf ("/proc/%ld/cmdline", pid);
385
if (!g_file_get_contents (proc_path, &cmdline_contents, NULL, NULL))
388
if (strstr (cmdline_contents, "bin/dnsmasq")) {
389
if (kill (pid, 0) == 0) {
390
nm_log_dbg (LOGD_SHARING, "Killing stale dnsmasq process %ld", pid);
397
g_free (cmdline_contents);
403
nm_dnsmasq_manager_start (NMDnsMasqManager *manager,
404
NMIP4Config *ip4_config,
407
NMDnsMasqManagerPrivate *priv;
411
g_return_val_if_fail (NM_IS_DNSMASQ_MANAGER (manager), FALSE);
413
g_return_val_if_fail (*error == NULL, FALSE);
415
priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (manager);
417
kill_existing_for_iface (priv->iface, priv->pidfile);
419
dm_cmd = create_dm_cmd_line (priv->iface, ip4_config, priv->pidfile, error);
423
g_ptr_array_add (dm_cmd->array, NULL);
425
nm_log_info (LOGD_SHARING, "Starting dnsmasq...");
427
cmd_str = nm_cmd_line_to_str (dm_cmd);
428
nm_log_dbg (LOGD_SHARING, "Command line: %s", cmd_str);
432
if (!g_spawn_async (NULL, (char **) dm_cmd->array->pdata, NULL,
433
G_SPAWN_DO_NOT_REAP_CHILD,
435
NULL, &priv->pid, error)) {
439
nm_log_dbg (LOGD_SHARING, "dnsmasq started with pid %d", priv->pid);
441
priv->dm_watch_id = g_child_watch_add (priv->pid, (GChildWatchFunc) dm_watch_cb, manager);
445
nm_cmd_line_destroy (dm_cmd);
447
return priv->pid > 0;
451
ensure_killed (gpointer data)
453
int pid = GPOINTER_TO_INT (data);
455
if (kill (pid, 0) == 0)
458
/* ensure the child is reaped */
459
nm_log_dbg (LOGD_SHARING, "waiting for dnsmasq pid %d to exit", pid);
460
waitpid (pid, NULL, 0);
461
nm_log_dbg (LOGD_SHARING, "dnsmasq pid %d cleaned up", pid);
467
nm_dnsmasq_manager_stop (NMDnsMasqManager *manager)
469
NMDnsMasqManagerPrivate *priv;
471
g_return_if_fail (NM_IS_DNSMASQ_MANAGER (manager));
473
priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (manager);
475
if (priv->dm_watch_id) {
476
g_source_remove (priv->dm_watch_id);
477
priv->dm_watch_id = 0;
481
if (kill (priv->pid, SIGTERM) == 0)
482
g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid));
484
kill (priv->pid, SIGKILL);
486
/* ensure the child is reaped */
487
nm_log_dbg (LOGD_SHARING, "waiting for dnsmasq pid %d to exit", priv->pid);
488
waitpid (priv->pid, NULL, 0);
489
nm_log_dbg (LOGD_SHARING, "dnsmasq pid %d cleaned up", priv->pid);
495
unlink (priv->pidfile);