|
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 |
|