~ubuntu-branches/debian/sid/network-manager/sid

« back to all changes in this revision

Viewing changes to .pc/83-dnsmasq-send-no-config-file-instead-of-a-bogus-one.patch/src/dnsmasq-manager/nm-dnsmasq-manager.c

  • Committer: Bazaar Package Importer
  • Author(s): Michael Biebl
  • Date: 2011-03-06 20:59:46 UTC
  • Revision ID: james.westby@ubuntu.com-20110306205946-fpdcb3ordico3ylg
Tags: 0.8.2-6
* debian/patches/83-dnsmasq-send-no-config-file-instead-of-a-bogus-one.patch
  - Newer versions of dnsmasq validate the option parameters more strictly.
    Instead of passing a bogus file name simply use --conf-file without
    additional parameters. (Closes: #615082)
* debian/control
  - Remove old Conflicts/Replaces which were required for upgrades to
    squeeze.
  - Bump Breaks against network-manager-gnome to (<< 0.8.2) to avoid
    partial upgrades which can lead to problems with user settings for
    ethernet connections. (Closes: #612291)
* debian/ifblacklist_migrate.sh
  - Only comment out iface lines if we have an exact match for the network
    interface. (Closes: #612247)
* debian/patches/51-normalized-keys.patch
  - Normalize keys in ifupdown parser, so we accept options with either
    hyphens or underscores, like e.g. bridge_ports and bridge-ports.
    (Closes: #609831)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 
2
/* NetworkManager -- Network link manager
 
3
 *
 
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.
 
8
 *
 
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.
 
13
 *
 
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.
 
17
 *
 
18
 * Copyright (C) 2008 - 2010 Red Hat, Inc.
 
19
 */
 
20
 
 
21
#include <config.h>
 
22
#include <sys/types.h>
 
23
#include <sys/wait.h>
 
24
#include <signal.h>
 
25
#include <string.h>
 
26
#include <unistd.h>
 
27
#include <arpa/inet.h>
 
28
#include <stdlib.h>
 
29
 
 
30
#include "nm-dnsmasq-manager.h"
 
31
#include "nm-logging.h"
 
32
#include "nm-glib-compat.h"
 
33
 
 
34
typedef struct {
 
35
        char *iface;
 
36
        char *pidfile;
 
37
        GPid pid;
 
38
        guint32 dm_watch_id;
 
39
} NMDnsMasqManagerPrivate;
 
40
 
 
41
#define NM_DNSMASQ_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DNSMASQ_MANAGER, NMDnsMasqManagerPrivate))
 
42
 
 
43
G_DEFINE_TYPE (NMDnsMasqManager, nm_dnsmasq_manager, G_TYPE_OBJECT)
 
44
 
 
45
enum {
 
46
        STATE_CHANGED,
 
47
 
 
48
        LAST_SIGNAL
 
49
};
 
50
 
 
51
static guint signals[LAST_SIGNAL] = { 0 };
 
52
 
 
53
typedef enum {
 
54
        NM_DNSMASQ_MANAGER_ERROR_NOT_FOUND,
 
55
} NMDnsMasqManagerError;
 
56
 
 
57
GQuark
 
58
nm_dnsmasq_manager_error_quark (void)
 
59
{
 
60
        static GQuark quark;
 
61
 
 
62
        if (!quark)
 
63
                quark = g_quark_from_static_string ("nm_dnsmasq_manager_error");
 
64
 
 
65
        return quark;
 
66
}
 
67
 
 
68
static void
 
69
nm_dnsmasq_manager_init (NMDnsMasqManager *manager)
 
70
{
 
71
}
 
72
 
 
73
static void
 
74
finalize (GObject *object)
 
75
{
 
76
        NMDnsMasqManagerPrivate *priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (object);
 
77
 
 
78
        nm_dnsmasq_manager_stop (NM_DNSMASQ_MANAGER (object));
 
79
 
 
80
        g_free (priv->iface);
 
81
        g_free (priv->pidfile);
 
82
 
 
83
        G_OBJECT_CLASS (nm_dnsmasq_manager_parent_class)->finalize (object);
 
84
}
 
85
 
 
86
static void
 
87
nm_dnsmasq_manager_class_init (NMDnsMasqManagerClass *manager_class)
 
88
{
 
89
        GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
 
90
 
 
91
        g_type_class_add_private (manager_class, sizeof (NMDnsMasqManagerPrivate));
 
92
 
 
93
        object_class->finalize = finalize;
 
94
 
 
95
        /* signals */
 
96
        signals[STATE_CHANGED] =
 
97
                g_signal_new ("state-changed",
 
98
                                    G_OBJECT_CLASS_TYPE (object_class),
 
99
                                    G_SIGNAL_RUN_FIRST,
 
100
                                    G_STRUCT_OFFSET (NMDnsMasqManagerClass, state_changed),
 
101
                                    NULL, NULL,
 
102
                                    g_cclosure_marshal_VOID__UINT,
 
103
                                    G_TYPE_NONE, 1,
 
104
                                    G_TYPE_UINT);
 
105
}
 
106
 
 
107
NMDnsMasqManager *
 
108
nm_dnsmasq_manager_new (const char *iface)
 
109
{
 
110
        NMDnsMasqManager *manager;
 
111
        NMDnsMasqManagerPrivate *priv;
 
112
 
 
113
        manager = (NMDnsMasqManager *) g_object_new (NM_TYPE_DNSMASQ_MANAGER, NULL);
 
114
        if (!manager)
 
115
                return NULL;
 
116
 
 
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);
 
120
 
 
121
        return manager;
 
122
}
 
123
 
 
124
typedef struct {
 
125
        GPtrArray *array;
 
126
        GStringChunk *chunk;
 
127
} NMCmdLine;
 
128
 
 
129
static NMCmdLine *
 
130
nm_cmd_line_new (void)
 
131
{
 
132
        NMCmdLine *cmd;
 
133
 
 
134
        cmd = g_slice_new (NMCmdLine);
 
135
        cmd->array = g_ptr_array_new ();
 
136
        cmd->chunk = g_string_chunk_new (1024);
 
137
 
 
138
        return cmd;
 
139
}
 
140
 
 
141
static void
 
142
nm_cmd_line_destroy (NMCmdLine *cmd)
 
143
{
 
144
        g_ptr_array_free (cmd->array, TRUE);
 
145
        g_string_chunk_free (cmd->chunk);
 
146
        g_slice_free (NMCmdLine, cmd);
 
147
}
 
148
 
 
149
static char *
 
150
nm_cmd_line_to_str (NMCmdLine *cmd)
 
151
{
 
152
        char *str;
 
153
 
 
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);
 
157
 
 
158
        return str;
 
159
}
 
160
 
 
161
static void
 
162
nm_cmd_line_add_string (NMCmdLine *cmd, const char *str)
 
163
{
 
164
        g_ptr_array_add (cmd->array, g_string_chunk_insert (cmd->chunk, str));
 
165
}
 
166
 
 
167
/*******************************************/
 
168
 
 
169
static inline const char *
 
170
nm_find_dnsmasq (void)
 
171
{
 
172
        static const char *dnsmasq_binary_paths[] = {
 
173
                "/usr/local/sbin/dnsmasq",
 
174
                "/usr/sbin/dnsmasq",
 
175
                "/sbin/dnsmasq",
 
176
                NULL
 
177
        };
 
178
 
 
179
        const char **dnsmasq_binary = dnsmasq_binary_paths;
 
180
 
 
181
        while (*dnsmasq_binary != NULL) {
 
182
                if (g_file_test (*dnsmasq_binary, G_FILE_TEST_EXISTS))
 
183
                        break;
 
184
                dnsmasq_binary++;
 
185
        }
 
186
 
 
187
        return *dnsmasq_binary;
 
188
}
 
189
 
 
190
static void
 
191
dm_exit_code (guint dm_exit_status)
 
192
{
 
193
        char *msg = "Unknown error";
 
194
 
 
195
        switch (dm_exit_status) {
 
196
        case 1:
 
197
                msg = "Configuration problem";
 
198
                break;
 
199
        case 2:
 
200
                msg = "Network access problem (address in use; permissions; etc)";
 
201
                break;
 
202
        case 3:
 
203
                msg = "Filesystem problem (missing file/directory; permissions; etc)";
 
204
                break;
 
205
        case 4:
 
206
                msg = "Memory allocation failure";
 
207
                break;
 
208
        case 5: 
 
209
                msg = "Other problem";
 
210
                break;
 
211
        default:
 
212
                if (dm_exit_status >= 11)
 
213
                        msg = "Lease-script 'init' process failure";
 
214
                break;
 
215
        }
 
216
 
 
217
        nm_log_warn (LOGD_SHARING, "dnsmasq exited with error: %s (%d)", msg, dm_exit_status);
 
218
}
 
219
 
 
220
static void
 
221
dm_watch_cb (GPid pid, gint status, gpointer user_data)
 
222
{
 
223
        NMDnsMasqManager *manager = NM_DNSMASQ_MANAGER (user_data);
 
224
        NMDnsMasqManagerPrivate *priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (manager);
 
225
        guint err;
 
226
 
 
227
        if (WIFEXITED (status)) {
 
228
                err = WEXITSTATUS (status);
 
229
                if (err != 0)
 
230
                        dm_exit_code (err);
 
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));
 
235
        } else {
 
236
                nm_log_warn (LOGD_SHARING, "dnsmasq died from an unknown cause");
 
237
        }
 
238
  
 
239
        priv->pid = 0;
 
240
 
 
241
        g_signal_emit (manager, signals[STATE_CHANGED], 0, NM_DNSMASQ_STATUS_DEAD);
 
242
}
 
243
 
 
244
static NMCmdLine *
 
245
create_dm_cmd_line (const char *iface,
 
246
                    NMIP4Config *ip4_config,
 
247
                    const char *pidfile,
 
248
                    GError **error)
 
249
{
 
250
        const char *dm_binary;
 
251
        NMCmdLine *cmd;
 
252
        GString *s;
 
253
        NMIP4Address *tmp;
 
254
        struct in_addr addr;
 
255
        char buf[INET_ADDRSTRLEN + 15];
 
256
        char localaddr[INET_ADDRSTRLEN + 1];
 
257
        int i;
 
258
 
 
259
        dm_binary = nm_find_dnsmasq ();
 
260
        if (!dm_binary) {
 
261
                g_set_error (error, NM_DNSMASQ_MANAGER_ERROR, NM_DNSMASQ_MANAGER_ERROR_NOT_FOUND,
 
262
                             "Could not find dnsmasq binary.");
 
263
                return NULL;
 
264
        }
 
265
 
 
266
        /* Find the IP4 address to use */
 
267
        tmp = nm_ip4_config_get_address (ip4_config, 0);
 
268
 
 
269
        /* Create dnsmasq command line */
 
270
        cmd = nm_cmd_line_new ();
 
271
        nm_cmd_line_add_string (cmd, dm_binary);
 
272
 
 
273
        if (getenv ("NM_DNSMASQ_DEBUG")) {
 
274
                nm_cmd_line_add_string (cmd, "--log-dhcp");
 
275
                nm_cmd_line_add_string (cmd, "--log-queries");
 
276
        }
 
277
 
 
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.
 
283
         */
 
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");
 
289
 
 
290
        nm_cmd_line_add_string (cmd, "--conf-file");
 
291
        nm_cmd_line_add_string (cmd, buf);
 
292
 
 
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");
 
298
 
 
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.
 
302
         */
 
303
        nm_cmd_line_add_string (cmd, "--strict-order");
 
304
 
 
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));
 
310
                goto error;
 
311
        }
 
312
        g_string_append (s, localaddr);
 
313
        nm_cmd_line_add_string (cmd, s->str);
 
314
        g_string_free (s, TRUE);
 
315
 
 
316
        s = g_string_new ("--dhcp-range=");
 
317
 
 
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));
 
323
                goto error;
 
324
        }
 
325
        g_string_append (s, buf);
 
326
 
 
327
        g_string_append_c (s, ',');
 
328
 
 
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));
 
334
                goto error;
 
335
        }
 
336
        g_string_append (s, buf);
 
337
 
 
338
        g_string_append (s, ",60m");
 
339
        nm_cmd_line_add_string (cmd, s->str);
 
340
        g_string_free (s, TRUE);
 
341
 
 
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);
 
346
 
 
347
        nm_cmd_line_add_string (cmd, "--dhcp-lease-max=50");
 
348
 
 
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);
 
353
 
 
354
        return cmd;
 
355
 
 
356
error:
 
357
        nm_cmd_line_destroy (cmd);
 
358
        return NULL;
 
359
}
 
360
 
 
361
static void
 
362
dm_child_setup (gpointer user_data G_GNUC_UNUSED)
 
363
{
 
364
        /* We are in the child process at this point */
 
365
        pid_t pid = getpid ();
 
366
        setpgid (pid, pid);
 
367
}
 
368
 
 
369
static void
 
370
kill_existing_for_iface (const char *iface, const char *pidfile)
 
371
{
 
372
        char *contents = NULL;
 
373
        glong pid;
 
374
        char *proc_path = NULL;
 
375
        char *cmdline_contents = NULL;
 
376
 
 
377
        if (!g_file_get_contents (pidfile, &contents, NULL, NULL))
 
378
                goto out;
 
379
 
 
380
        pid = strtol (contents, NULL, 10);
 
381
        if (pid < 1 || pid > INT_MAX)
 
382
                goto out;
 
383
 
 
384
        proc_path = g_strdup_printf ("/proc/%ld/cmdline", pid);
 
385
        if (!g_file_get_contents (proc_path, &cmdline_contents, NULL, NULL))
 
386
                goto out;
 
387
 
 
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);
 
391
                        kill (pid, SIGKILL);
 
392
                }
 
393
                unlink (pidfile);
 
394
        }
 
395
 
 
396
out:
 
397
        g_free (cmdline_contents);
 
398
        g_free (proc_path);
 
399
        g_free (contents);
 
400
}
 
401
 
 
402
gboolean
 
403
nm_dnsmasq_manager_start (NMDnsMasqManager *manager,
 
404
                          NMIP4Config *ip4_config,
 
405
                          GError **error)
 
406
{
 
407
        NMDnsMasqManagerPrivate *priv;
 
408
        NMCmdLine *dm_cmd;
 
409
        char *cmd_str;
 
410
 
 
411
        g_return_val_if_fail (NM_IS_DNSMASQ_MANAGER (manager), FALSE);
 
412
        if (error)
 
413
                g_return_val_if_fail (*error == NULL, FALSE);
 
414
 
 
415
        priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (manager);
 
416
 
 
417
        kill_existing_for_iface (priv->iface, priv->pidfile);
 
418
 
 
419
        dm_cmd = create_dm_cmd_line (priv->iface, ip4_config, priv->pidfile, error);
 
420
        if (!dm_cmd)
 
421
                return FALSE;
 
422
 
 
423
        g_ptr_array_add (dm_cmd->array, NULL);
 
424
 
 
425
        nm_log_info (LOGD_SHARING, "Starting dnsmasq...");
 
426
 
 
427
        cmd_str = nm_cmd_line_to_str (dm_cmd);
 
428
        nm_log_dbg (LOGD_SHARING, "Command line: %s", cmd_str);
 
429
        g_free (cmd_str);
 
430
 
 
431
        priv->pid = 0;
 
432
        if (!g_spawn_async (NULL, (char **) dm_cmd->array->pdata, NULL,
 
433
                                        G_SPAWN_DO_NOT_REAP_CHILD,
 
434
                                        dm_child_setup,
 
435
                                        NULL, &priv->pid, error)) {
 
436
                goto out;
 
437
        }
 
438
 
 
439
        nm_log_dbg (LOGD_SHARING, "dnsmasq started with pid %d", priv->pid);
 
440
 
 
441
        priv->dm_watch_id = g_child_watch_add (priv->pid, (GChildWatchFunc) dm_watch_cb, manager);
 
442
 
 
443
 out:
 
444
        if (dm_cmd)
 
445
                nm_cmd_line_destroy (dm_cmd);
 
446
 
 
447
        return priv->pid > 0;
 
448
}
 
449
 
 
450
static gboolean
 
451
ensure_killed (gpointer data)
 
452
{
 
453
        int pid = GPOINTER_TO_INT (data);
 
454
 
 
455
        if (kill (pid, 0) == 0)
 
456
                kill (pid, SIGKILL);
 
457
 
 
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);
 
462
 
 
463
        return FALSE;
 
464
}
 
465
 
 
466
void
 
467
nm_dnsmasq_manager_stop (NMDnsMasqManager *manager)
 
468
{
 
469
        NMDnsMasqManagerPrivate *priv;
 
470
 
 
471
        g_return_if_fail (NM_IS_DNSMASQ_MANAGER (manager));
 
472
 
 
473
        priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (manager);
 
474
 
 
475
        if (priv->dm_watch_id) {
 
476
                g_source_remove (priv->dm_watch_id);
 
477
                priv->dm_watch_id = 0;
 
478
        }
 
479
 
 
480
        if (priv->pid) {
 
481
                if (kill (priv->pid, SIGTERM) == 0)
 
482
                        g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid));
 
483
                else {
 
484
                        kill (priv->pid, SIGKILL);
 
485
 
 
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);
 
490
                }
 
491
 
 
492
                priv->pid = 0;
 
493
        }
 
494
 
 
495
        unlink (priv->pidfile);
 
496
}