Lines 44-67
Link Here
|
44 |
#include "nm-named-manager.h" |
44 |
#include "nm-named-manager.h" |
45 |
#include "nm-vpn-manager.h" |
45 |
#include "nm-vpn-manager.h" |
46 |
|
46 |
|
47 |
typedef struct LookupThread LookupThread; |
|
|
48 |
|
49 |
typedef void (*LookupCallback) (LookupThread *thread, gpointer user_data); |
50 |
|
51 |
struct LookupThread { |
52 |
GThread *thread; |
53 |
|
54 |
GMutex *lock; |
55 |
gboolean die; |
56 |
int ret; |
57 |
|
58 |
guint32 ip4_addr; |
59 |
char hostname[NI_MAXHOST + 1]; |
60 |
|
61 |
LookupCallback callback; |
62 |
gpointer user_data; |
63 |
}; |
64 |
|
65 |
struct NMPolicy { |
47 |
struct NMPolicy { |
66 |
NMManager *manager; |
48 |
NMManager *manager; |
67 |
guint update_state_id; |
49 |
guint update_state_id; |
Lines 74-169
Link Here
|
74 |
gulong vpn_deactivated_id; |
56 |
gulong vpn_deactivated_id; |
75 |
|
57 |
|
76 |
NMDevice *default_device; |
58 |
NMDevice *default_device; |
77 |
|
|
|
78 |
LookupThread *lookup; |
79 |
}; |
59 |
}; |
80 |
|
60 |
|
81 |
static gboolean |
|
|
82 |
lookup_thread_run_cb (gpointer user_data) |
83 |
{ |
84 |
LookupThread *thread = (LookupThread *) user_data; |
85 |
|
86 |
(*thread->callback) (thread, thread->user_data); |
87 |
return FALSE; |
88 |
} |
89 |
|
90 |
static gpointer |
91 |
lookup_thread_worker (gpointer data) |
92 |
{ |
93 |
LookupThread *thread = (LookupThread *) data; |
94 |
struct sockaddr_in addr; |
95 |
|
96 |
g_mutex_lock (thread->lock); |
97 |
if (thread->die) { |
98 |
g_mutex_unlock (thread->lock); |
99 |
return (gpointer) NULL; |
100 |
} |
101 |
g_mutex_unlock (thread->lock); |
102 |
|
103 |
addr.sin_family = AF_INET; |
104 |
addr.sin_addr.s_addr = thread->ip4_addr; |
105 |
|
106 |
thread->ret = getnameinfo ((struct sockaddr *) &addr, sizeof (struct sockaddr_in), |
107 |
thread->hostname, NI_MAXHOST, NULL, 0, |
108 |
NI_NAMEREQD); |
109 |
if (thread->ret == 0) { |
110 |
int i; |
111 |
|
112 |
for (i = 0; i < strlen (thread->hostname); i++) |
113 |
thread->hostname[i] = tolower (thread->hostname[i]); |
114 |
} |
115 |
|
116 |
/* Don't track the idle handler ID because by the time the g_idle_add() |
117 |
* returns the ID, the handler may already have run and freed the |
118 |
* LookupThread. |
119 |
*/ |
120 |
g_idle_add (lookup_thread_run_cb, thread); |
121 |
return (gpointer) TRUE; |
122 |
} |
123 |
|
124 |
static void |
125 |
lookup_thread_free (LookupThread *thread) |
126 |
{ |
127 |
g_return_if_fail (thread != NULL); |
128 |
|
129 |
g_mutex_free (thread->lock); |
130 |
memset (thread, 0, sizeof (LookupThread)); |
131 |
g_free (thread); |
132 |
} |
133 |
|
134 |
static LookupThread * |
135 |
lookup_thread_new (guint32 ip4_addr, LookupCallback callback, gpointer user_data) |
136 |
{ |
137 |
LookupThread *thread; |
138 |
|
139 |
thread = g_malloc0 (sizeof (LookupThread)); |
140 |
if (!thread) |
141 |
return NULL; |
142 |
|
143 |
thread->lock = g_mutex_new (); |
144 |
thread->callback = callback; |
145 |
thread->user_data = user_data; |
146 |
thread->ip4_addr = ip4_addr; |
147 |
|
148 |
thread->thread = g_thread_create (lookup_thread_worker, thread, FALSE, NULL); |
149 |
if (!thread->thread) { |
150 |
lookup_thread_free (thread); |
151 |
return NULL; |
152 |
} |
153 |
|
154 |
return thread; |
155 |
} |
156 |
|
157 |
static void |
158 |
lookup_thread_die (LookupThread *thread) |
159 |
{ |
160 |
g_return_if_fail (thread != NULL); |
161 |
|
162 |
g_mutex_lock (thread->lock); |
163 |
thread->die = TRUE; |
164 |
g_mutex_unlock (thread->lock); |
165 |
} |
166 |
|
167 |
#define INVALID_TAG "invalid" |
61 |
#define INVALID_TAG "invalid" |
168 |
|
62 |
|
169 |
static const char * |
63 |
static const char * |
Lines 253-485
Link Here
|
253 |
return best; |
147 |
return best; |
254 |
} |
148 |
} |
255 |
|
149 |
|
256 |
#define FALLBACK_HOSTNAME "localhost.localdomain" |
|
|
257 |
|
258 |
static gboolean |
259 |
update_etc_hosts (const char *hostname) |
260 |
{ |
261 |
char *contents = NULL; |
262 |
char **lines = NULL, **line; |
263 |
GError *error = NULL; |
264 |
gboolean initial_comments = TRUE; |
265 |
gboolean added = FALSE; |
266 |
gsize contents_len = 0; |
267 |
GString *new_contents; |
268 |
gboolean success = FALSE; |
269 |
|
270 |
g_return_val_if_fail (hostname != NULL, FALSE); |
271 |
|
272 |
if (!g_file_get_contents (SYSCONFDIR "/hosts", &contents, &contents_len, &error)) { |
273 |
nm_warning ("%s: couldn't read " SYSCONFDIR "/hosts: (%d) %s", |
274 |
__func__, error ? error->code : 0, |
275 |
(error && error->message) ? error->message : "(unknown)"); |
276 |
if (error) |
277 |
g_error_free (error); |
278 |
} else { |
279 |
lines = g_strsplit_set (contents, "\n\r", 0); |
280 |
g_free (contents); |
281 |
} |
282 |
|
283 |
new_contents = g_string_sized_new (contents_len ? contents_len + 100 : 200); |
284 |
if (!new_contents) { |
285 |
nm_warning ("%s: not enough memory to update " SYSCONFDIR "/hosts", __func__); |
286 |
return FALSE; |
287 |
} |
288 |
|
289 |
/* Replace any 127.0.0.1 entry that is at the beginning of the file or right |
290 |
* after initial comments. If there is no 127.0.0.1 entry at the beginning |
291 |
* or after initial comments, add one there and ignore any other 127.0.0.1 |
292 |
* entries. |
293 |
*/ |
294 |
for (line = lines; lines && *line; line++) { |
295 |
gboolean add_line = TRUE; |
296 |
|
297 |
/* This is the first line after the initial comments */ |
298 |
if (initial_comments && (*line[0] != '#')) { |
299 |
initial_comments = FALSE; |
300 |
g_string_append_printf (new_contents, "127.0.0.1\t%s", hostname); |
301 |
if (strcmp (hostname, FALLBACK_HOSTNAME)) |
302 |
g_string_append_printf (new_contents, "\t" FALLBACK_HOSTNAME); |
303 |
g_string_append (new_contents, "\tlocalhost\n"); |
304 |
added = TRUE; |
305 |
|
306 |
/* Don't add the entry if it's supposed to be the actual localhost reverse mapping */ |
307 |
if (!strncmp (*line, "127.0.0.1", strlen ("127.0.0.1")) && strstr (*line, "localhost")) |
308 |
add_line = FALSE; |
309 |
} |
310 |
|
311 |
if (add_line) { |
312 |
g_string_append (new_contents, *line); |
313 |
/* Only append the new line if this isn't the last line in the file */ |
314 |
if (*(line+1)) |
315 |
g_string_append_c (new_contents, '\n'); |
316 |
} |
317 |
} |
318 |
|
319 |
/* Hmm, /etc/hosts was empty for some reason */ |
320 |
if (!added) { |
321 |
g_string_append (new_contents, "# Do not remove the following line, or various programs"); |
322 |
g_string_append (new_contents, "# that require network functionality will fail."); |
323 |
g_string_append (new_contents, "127.0.0.1\t" FALLBACK_HOSTNAME "\tlocalhost"); |
324 |
} |
325 |
|
326 |
error = NULL; |
327 |
if (!g_file_set_contents (SYSCONFDIR "/hosts", new_contents->str, -1, &error)) { |
328 |
nm_warning ("%s: couldn't update " SYSCONFDIR "/hosts: (%d) %s", |
329 |
__func__, error ? error->code : 0, |
330 |
(error && error->message) ? error->message : "(unknown)"); |
331 |
if (error) |
332 |
g_error_free (error); |
333 |
} else |
334 |
success = TRUE; |
335 |
|
336 |
g_string_free (new_contents, TRUE); |
337 |
return success; |
338 |
} |
339 |
|
340 |
static void |
341 |
set_system_hostname (const char *new_hostname, const char *msg) |
342 |
{ |
343 |
char old_hostname[HOST_NAME_MAX + 1]; |
344 |
int ret = 0; |
345 |
const char *name = new_hostname ? new_hostname : FALLBACK_HOSTNAME; |
346 |
|
347 |
old_hostname[HOST_NAME_MAX] = '\0'; |
348 |
errno = 0; |
349 |
ret = gethostname (old_hostname, HOST_NAME_MAX); |
350 |
if (ret != 0) { |
351 |
nm_warning ("%s: couldn't get the system hostname: (%d) %s", |
352 |
__func__, errno, strerror (errno)); |
353 |
} else { |
354 |
/* Do nothing if the hostname isn't actually changing */ |
355 |
if ( (new_hostname && !strcmp (old_hostname, new_hostname)) |
356 |
|| (!new_hostname && !strcmp (old_hostname, FALLBACK_HOSTNAME))) |
357 |
return; |
358 |
} |
359 |
|
360 |
nm_info ("Setting system hostname to '%s' (%s)", name, msg); |
361 |
|
362 |
ret = sethostname (name, strlen (name)); |
363 |
if (ret == 0) { |
364 |
if (!update_etc_hosts (name)) { |
365 |
/* error updating /etc/hosts; fallback to localhost.localdomain */ |
366 |
nm_info ("Setting system hostname to '" FALLBACK_HOSTNAME "' (error updating /etc/hosts)"); |
367 |
ret = sethostname (FALLBACK_HOSTNAME, strlen (FALLBACK_HOSTNAME)); |
368 |
if (ret != 0) { |
369 |
nm_warning ("%s: couldn't set the fallback system hostname (%s): (%d) %s", |
370 |
__func__, FALLBACK_HOSTNAME, errno, strerror (errno)); |
371 |
} |
372 |
} |
373 |
nm_utils_call_dispatcher ("hostname", NULL, NULL, NULL); |
374 |
} else { |
375 |
nm_warning ("%s: couldn't set the system hostname to '%s': (%d) %s", |
376 |
__func__, name, errno, strerror (errno)); |
377 |
} |
378 |
} |
379 |
|
380 |
static void |
381 |
lookup_callback (LookupThread *thread, gpointer user_data) |
382 |
{ |
383 |
NMPolicy *policy = (NMPolicy *) user_data; |
384 |
|
385 |
/* If the thread was told to die or it's not the current in-progress |
386 |
* hostname lookup, nothing to do. |
387 |
*/ |
388 |
if (thread->die || (thread != policy->lookup)) |
389 |
goto done; |
390 |
|
391 |
policy->lookup = NULL; |
392 |
if (!strlen (thread->hostname)) { |
393 |
char *msg; |
394 |
|
395 |
/* No valid IP4 config (!!); fall back to localhost.localdomain */ |
396 |
msg = g_strdup_printf ("address lookup failed: %d", thread->ret); |
397 |
set_system_hostname (NULL, msg); |
398 |
g_free (msg); |
399 |
} else |
400 |
set_system_hostname (thread->hostname, "from address lookup"); |
401 |
|
402 |
done: |
403 |
lookup_thread_free (thread); |
404 |
} |
405 |
|
406 |
static void |
407 |
update_system_hostname (NMPolicy *policy, NMDevice *best) |
408 |
{ |
409 |
char *configured_hostname = NULL; |
410 |
NMActRequest *best_req = NULL; |
411 |
NMDHCP4Config *dhcp4_config; |
412 |
NMIP4Config *ip4_config; |
413 |
NMIP4Address *addr; |
414 |
|
415 |
g_return_if_fail (policy != NULL); |
416 |
|
417 |
if (policy->lookup) { |
418 |
lookup_thread_die (policy->lookup); |
419 |
policy->lookup = NULL; |
420 |
} |
421 |
|
422 |
/* A configured hostname (via the system-settings service) overrides |
423 |
* all automatic hostname determination. If there is no configured hostname, |
424 |
* the best device's automatically determined hostname (from DHCP, VPN, PPP, |
425 |
* etc) is used. If there is no automatically determined hostname, reverse |
426 |
* DNS lookup using the best device's IP address is started to determined the |
427 |
* the hostname. |
428 |
*/ |
429 |
|
430 |
/* Try a configured hostname first */ |
431 |
g_object_get (G_OBJECT (policy->manager), NM_MANAGER_HOSTNAME, &configured_hostname, NULL); |
432 |
if (configured_hostname) { |
433 |
set_system_hostname (configured_hostname, "from system configuration"); |
434 |
g_free (configured_hostname); |
435 |
return; |
436 |
} |
437 |
|
438 |
/* Try automatically determined hostname from the best device's IP config */ |
439 |
if (!best) |
440 |
best = get_best_device (policy->manager, &best_req); |
441 |
|
442 |
if (!best) { |
443 |
/* No best device; fall back to localhost.localdomain */ |
444 |
set_system_hostname (NULL, "no default device"); |
445 |
return; |
446 |
} |
447 |
|
448 |
/* Grab a hostname out of the device's DHCP4 config */ |
449 |
dhcp4_config = nm_device_get_dhcp4_config (best); |
450 |
if (dhcp4_config) { |
451 |
const char *dhcp4_hostname; |
452 |
|
453 |
dhcp4_hostname = nm_dhcp4_config_get_option (dhcp4_config, "host_name"); |
454 |
if (dhcp4_hostname && strlen (dhcp4_hostname)) { |
455 |
set_system_hostname (dhcp4_hostname, "from DHCP"); |
456 |
return; |
457 |
} |
458 |
} |
459 |
|
460 |
/* No configured hostname, no automatically determined hostname either. Start |
461 |
* reverse DNS of the current IP address to try and find it. |
462 |
*/ |
463 |
ip4_config = nm_device_get_ip4_config (best); |
464 |
if ( !ip4_config |
465 |
|| (nm_ip4_config_get_num_nameservers (ip4_config) == 0) |
466 |
|| (nm_ip4_config_get_num_addresses (ip4_config) == 0)) { |
467 |
/* No valid IP4 config (!!); fall back to localhost.localdomain */ |
468 |
set_system_hostname (NULL, "no IPv4 config"); |
469 |
return; |
470 |
} |
471 |
|
472 |
addr = nm_ip4_config_get_address (ip4_config, 0); |
473 |
g_assert (addr); /* checked for > 1 address above */ |
474 |
|
475 |
/* Start the hostname lookup thread */ |
476 |
policy->lookup = lookup_thread_new (nm_ip4_address_get_address (addr), lookup_callback, policy); |
477 |
if (!policy->lookup) { |
478 |
/* Fall back to 'localhost.localdomain' */ |
479 |
set_system_hostname (NULL, "error starting hostname thread"); |
480 |
} |
481 |
} |
482 |
|
483 |
static void |
150 |
static void |
484 |
update_routing_and_dns (NMPolicy *policy, gboolean force_update) |
151 |
update_routing_and_dns (NMPolicy *policy, gboolean force_update) |
485 |
{ |
152 |
{ |
Lines 597-605
Link Here
|
597 |
nm_info ("Policy set (%s) as default for routing and DNS.", ip_iface); |
264 |
nm_info ("Policy set (%s) as default for routing and DNS.", ip_iface); |
598 |
|
265 |
|
599 |
out: |
266 |
out: |
600 |
/* Update the system hostname */ |
|
|
601 |
update_system_hostname (policy, best); |
602 |
|
603 |
policy->default_device = best; |
267 |
policy->default_device = best; |
604 |
} |
268 |
} |
605 |
|
269 |
|
Lines 707-718
Link Here
|
707 |
} |
371 |
} |
708 |
|
372 |
|
709 |
static void |
373 |
static void |
710 |
hostname_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data) |
|
|
711 |
{ |
712 |
update_system_hostname ((NMPolicy *) user_data, NULL); |
713 |
} |
714 |
|
715 |
static void |
716 |
schedule_activate_check (NMPolicy *policy, NMDevice *device) |
374 |
schedule_activate_check (NMPolicy *policy, NMDevice *device) |
717 |
{ |
375 |
{ |
718 |
ActivateData *data; |
376 |
ActivateData *data; |
Lines 991-1000
Link Here
|
991 |
G_CALLBACK (global_state_changed), policy); |
649 |
G_CALLBACK (global_state_changed), policy); |
992 |
policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); |
650 |
policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); |
993 |
|
651 |
|
994 |
id = g_signal_connect (manager, "notify::hostname", |
|
|
995 |
G_CALLBACK (hostname_changed), policy); |
996 |
policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); |
997 |
|
998 |
id = g_signal_connect (manager, "device-added", |
652 |
id = g_signal_connect (manager, "device-added", |
999 |
G_CALLBACK (device_added), policy); |
653 |
G_CALLBACK (device_added), policy); |
1000 |
policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); |
654 |
policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); |
Lines 1033-1046
Link Here
|
1033 |
|
687 |
|
1034 |
g_return_if_fail (policy != NULL); |
688 |
g_return_if_fail (policy != NULL); |
1035 |
|
689 |
|
1036 |
/* Tell any existing hostname lookup thread to die, it'll get cleaned up |
|
|
1037 |
* by the lookup thread callback. |
1038 |
*/ |
1039 |
if (policy->lookup) { |
1040 |
lookup_thread_die (policy->lookup); |
1041 |
policy->lookup = NULL; |
1042 |
} |
1043 |
|
1044 |
for (iter = policy->pending_activation_checks; iter; iter = g_slist_next (iter)) { |
690 |
for (iter = policy->pending_activation_checks; iter; iter = g_slist_next (iter)) { |
1045 |
ActivateData *data = (ActivateData *) iter->data; |
691 |
ActivateData *data = (ActivateData *) iter->data; |
1046 |
|
692 |
|