f20f889e2ba01864203517b7d07a0081cf926d02
[NetworkManager.git] / src / dns-manager / nm-dns-plugin.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2, or (at your option)
5  * any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License along
13  * with this program; if not, write to the Free Software Foundation, Inc.,
14  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
15  *
16  * Copyright (C) 2010 - 2012 Red Hat, Inc.
17  *
18  */
19
20 #include "nm-default.h"
21
22 #include <string.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27
28 #include "nm-core-internal.h"
29
30 #include "nm-dns-plugin.h"
31 #include "NetworkManagerUtils.h"
32
33 typedef struct {
34         GPid pid;
35         guint watch_id;
36         char *progname;
37         char *pidfile;
38 } NMDnsPluginPrivate;
39
40 #define NM_DNS_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DNS_PLUGIN, NMDnsPluginPrivate))
41
42 G_DEFINE_TYPE_EXTENDED (NMDnsPlugin, nm_dns_plugin, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, {})
43
44 enum {
45         FAILED,
46         CHILD_QUIT,
47         LAST_SIGNAL
48 };
49 static guint signals[LAST_SIGNAL] = { 0 };
50
51 /********************************************/
52
53 gboolean
54 nm_dns_plugin_update (NMDnsPlugin *self,
55                       const GSList *vpn_configs,
56                       const GSList *dev_configs,
57                       const GSList *other_configs,
58                       const NMGlobalDnsConfig *global_config,
59                       const char *hostname)
60 {
61         g_return_val_if_fail (NM_DNS_PLUGIN_GET_CLASS (self)->update != NULL, FALSE);
62
63         return NM_DNS_PLUGIN_GET_CLASS (self)->update (self,
64                                                        vpn_configs,
65                                                        dev_configs,
66                                                        other_configs,
67                                                        global_config,
68                                                        hostname);
69 }
70
71 static gboolean
72 is_caching (NMDnsPlugin *self)
73 {
74         return FALSE;
75 }
76
77 gboolean
78 nm_dns_plugin_is_caching (NMDnsPlugin *self)
79 {
80         return NM_DNS_PLUGIN_GET_CLASS (self)->is_caching (self);
81 }
82
83 const char *
84 nm_dns_plugin_get_name (NMDnsPlugin *self)
85 {
86         g_assert (NM_DNS_PLUGIN_GET_CLASS (self)->get_name);
87         return NM_DNS_PLUGIN_GET_CLASS (self)->get_name (self);
88 }
89
90 /********************************************/
91
92 static void
93 _clear_pidfile (NMDnsPlugin *self)
94 {
95         NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
96
97         if (priv->pidfile) {
98                 unlink (priv->pidfile);
99                 g_free (priv->pidfile);
100                 priv->pidfile = NULL;
101         }
102 }
103
104 static void
105 kill_existing (const char *progname, const char *pidfile, const char *kill_match)
106 {
107         glong pid;
108         gs_free char *contents = NULL;
109         gs_free char *cmdline_contents = NULL;
110         guint64 start_time;
111         char proc_path[256];
112         gs_free GError *error = NULL;
113
114         if (!pidfile)
115                 return;
116
117         if (!kill_match)
118                 g_return_if_reached ();
119
120         if (!g_file_get_contents (pidfile, &contents, NULL, &error)) {
121                 if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
122                         return;
123                 goto out;
124         }
125
126         pid = _nm_utils_ascii_str_to_int64 (contents, 10, 2, INT_MAX, -1);
127         if (pid == -1)
128                 goto out;
129
130         start_time = nm_utils_get_start_time_for_pid (pid, NULL, NULL);
131         if (start_time == 0)
132                 goto out;
133
134         nm_sprintf_buf (proc_path, "/proc/%ld/cmdline", pid);
135         if (!g_file_get_contents (proc_path, &cmdline_contents, NULL, NULL))
136                 goto out;
137
138         if (!strstr (cmdline_contents, kill_match))
139                 goto out;
140
141         nm_utils_kill_process_sync (pid, start_time, SIGKILL, LOGD_DNS,
142                                     progname ?: "<dns-process>",
143                                     0, 0, 1000);
144
145 out:
146         unlink (pidfile);
147 }
148
149 static void
150 watch_cb (GPid pid, gint status, gpointer user_data)
151 {
152         NMDnsPlugin *self = NM_DNS_PLUGIN (user_data);
153         NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
154
155         priv->pid = 0;
156         priv->watch_id = 0;
157         g_free (priv->progname);
158         priv->progname = NULL;
159
160         _clear_pidfile (self);
161
162         g_signal_emit (self, signals[CHILD_QUIT], 0, status);
163 }
164
165 GPid
166 nm_dns_plugin_child_spawn (NMDnsPlugin *self,
167                            const char **argv,
168                            const char *pidfile,
169                            const char *kill_match)
170 {
171         NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
172         GError *error = NULL;
173         char *cmdline;
174
175         g_return_val_if_fail (argv != NULL, 0);
176         g_return_val_if_fail (argv[0] != NULL, 0);
177
178         g_warn_if_fail (priv->progname == NULL);
179         g_free (priv->progname);
180         priv->progname = g_path_get_basename (argv[0]);
181
182         kill_existing (priv->progname, pidfile, kill_match);
183
184         g_warn_if_fail (priv->pidfile == NULL);
185         g_clear_pointer (&priv->pidfile, g_free);
186         priv->pidfile = g_strdup (pidfile);
187
188         nm_log_info (LOGD_DNS, "DNS: starting %s...", priv->progname);
189         cmdline = g_strjoinv (" ", (char **) argv);
190         nm_log_dbg (LOGD_DNS, "DNS: command line: %s", cmdline);
191         g_free (cmdline);
192
193         priv->pid = 0;
194         if (g_spawn_async (NULL, (char **) argv, NULL,
195                            G_SPAWN_DO_NOT_REAP_CHILD,
196                            nm_utils_setpgid, NULL,
197                            &priv->pid,
198                            &error)) {
199                 nm_log_dbg (LOGD_DNS, "%s started with pid %d", priv->progname, priv->pid);
200                 priv->watch_id = g_child_watch_add (priv->pid, (GChildWatchFunc) watch_cb, self);
201         } else {
202                 nm_log_warn (LOGD_DNS, "Failed to spawn %s: %s",
203                              priv->progname, error->message);
204                 g_clear_error (&error);
205         }
206
207         return priv->pid;
208 }
209
210 gboolean
211 nm_dns_plugin_child_kill (NMDnsPlugin *self)
212 {
213         NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
214
215         nm_clear_g_source (&priv->watch_id);
216
217         if (priv->pid) {
218                 nm_utils_kill_child_sync (priv->pid, SIGTERM, LOGD_DNS, priv->progname, NULL, 1000, 0);
219                 priv->pid = 0;
220                 g_free (priv->progname);
221                 priv->progname = NULL;
222         }
223
224         _clear_pidfile (self);
225
226         return TRUE;
227 }
228
229 /********************************************/
230
231 static void
232 nm_dns_plugin_init (NMDnsPlugin *self)
233 {
234 }
235
236 static void
237 dispose (GObject *object)
238 {
239         NMDnsPlugin *self = NM_DNS_PLUGIN (object);
240
241         nm_dns_plugin_child_kill (self);
242
243         G_OBJECT_CLASS (nm_dns_plugin_parent_class)->dispose (object);
244 }
245
246 static void
247 finalize (GObject *object)
248 {
249         NMDnsPlugin *self = NM_DNS_PLUGIN (object);
250         NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
251
252         g_free (priv->progname);
253         g_free (priv->pidfile);
254
255         G_OBJECT_CLASS (nm_dns_plugin_parent_class)->finalize (object);
256 }
257
258 static void
259 nm_dns_plugin_class_init (NMDnsPluginClass *plugin_class)
260 {
261         GObjectClass *object_class = G_OBJECT_CLASS (plugin_class);
262
263         g_type_class_add_private (plugin_class, sizeof (NMDnsPluginPrivate));
264
265         /* virtual methods */
266         object_class->dispose = dispose;
267         object_class->finalize = finalize;
268         plugin_class->is_caching = is_caching;
269
270         /* signals */
271         signals[FAILED] =
272             g_signal_new (NM_DNS_PLUGIN_FAILED,
273                           G_OBJECT_CLASS_TYPE (object_class),
274                           G_SIGNAL_RUN_FIRST,
275                           G_STRUCT_OFFSET (NMDnsPluginClass, failed),
276                           NULL, NULL,
277                           g_cclosure_marshal_VOID__VOID,
278                           G_TYPE_NONE, 0);
279
280         signals[CHILD_QUIT] =
281             g_signal_new (NM_DNS_PLUGIN_CHILD_QUIT,
282                           G_OBJECT_CLASS_TYPE (object_class),
283                           G_SIGNAL_RUN_FIRST,
284                           G_STRUCT_OFFSET (NMDnsPluginClass, child_quit),
285                           NULL, NULL,
286                           g_cclosure_marshal_VOID__INT,
287                           G_TYPE_NONE, 1, G_TYPE_INT);
288 }
289