add get_bsd_hostname() and has_domain()
[systembsd.git] / src / interfaces / hostnamed / hostnamed.c
CommitLineData
3b82e3c1 1/*
2 * Copyright (c) 2014 Ian Sutton <ian@kremlin.cc>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
a35a69c5 17#include <unistd.h>
18#include <limits.h>
0f339959 19#include <signal.h>
f8257c5d 20#include <string.h>
fd8852d9 21
d15318db 22#include <sys/param.h>
f1ad9351 23#include <sys/sysctl.h>
f8257c5d 24#include <sys/sensors.h>
25#include <sys/ioctl.h>
bd24ff31 26#include <sys/utsname.h>
f8257c5d 27
28#include <machine/apmvar.h>
d15318db 29
4c04b514 30#include <glib/gprintf.h>
8caf1f61 31#include <glib-unix.h>
904d744d 32#include <polkit/polkit.h>
4c04b514 33
1e8c7c88 34#include "hostnamed-gen.h"
0f339959 35#include "hostnamed.h"
a35a69c5 36
483e90b7 37#include "../../polkit-auth.h"
38
a1bcc33c 39/* format: {
40 * (1) string to be matched against runtime machine's sysctl output.
41 * can be either the exact string or a substring contained
42 * within sysctl strings. no "guesses" here, a match should
43 * reliably indicate the chassis/icon.
44 *
45 * (2) string describing chassis type divulged by (1).
46 * must be one of "desktop", "laptop", "server",
47 * "tablet", "handset", "vm", "container" or NULL
48 * if only icon string can be ascertained. "vm" refers
49 * to operating systems running on baremetal hypervisors
50 * (hardware virtualization, like XEN) while "container"
51 * refers to OSs running on shared hypervisors like
52 * virtualbox or VMware. consider the distinction carefully
53 * as common virtualization software like KVM may share
54 * characteristics of both "vm" and "container" types.
55 *
56 * (3) string specifying icon to use. follows XDG icon spec.
57 * see http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
58 * for allowed strings.
59 *
60 * (4) chassis precedence bit. TRUE if (2) is defined and
61 * we're certain it is the proper string. FALSE in the
62 * circumstance (2) may be the correct string, unless
63 * a match with this bit set to TRUE overrides it.
64 * if (2) is NULL, this bit is inconsequential.
65 *
66 * (5) icon precedence bit. see previous definition.
67 * } */
68struct SYSCTL_LOOKUP_TABLE {
69 gchar *match_string;
70 gchar *chassis;
71 gchar *icon;
72 gboolean chassis_precedence;
73 gboolean icon_precedence;
74};
75
fd8852d9 76GPtrArray *hostnamed_freeable;
1be94ede 77Hostname1 *hostnamed_interf;
3d53b501 78
0f339959 79GMainLoop *hostnamed_loop;
80
81guint bus_descriptor;
82gboolean dbus_interface_exported; /* reliable because of gdbus operational guarantees */
83
19c6b83d 84gchar *HOSTNAME, *STATIC_HOSTNAME, *PRETTY_HOSTNAME;
a1bcc33c 85gchar *CHASSIS, *ICON;
19c6b83d 86gchar *KERN_NAME, *KERN_RELEASE, *KERN_VERS, *OS_CPENAME;
a1bcc33c 87
88/* TODO no specific vm or laptop icon in gnome
89 * NOTE paravirtualization on xen is only available for linuxes right now
90 * dmesg on linux systems reveals xen and virtualization method (HVM or PVM)
91 * but we will worry about those later */
c7028b11 92
93/* add any sysctl strings that suggest virtualization here */
a1bcc33c 94const struct SYSCTL_LOOKUP_TABLE chassis_indicator_table[] =
95{
87df323f 96 { "QEMU Virtual CPU", "vm", NULL, FALSE, FALSE }, /* could be QEMU running in userspace or as part of KVM */
6edc347a 97 { "KVM", "vm", "drive-multidisk", FALSE, FALSE },
b21074ae 98 { "SmartDC HVM", "vm", "drive-multidisk", TRUE, TRUE }, /* illumos-joyent kvm */
87df323f 99 { "VirtualBox", "vm", "drive-multidisk", TRUE, TRUE },
100 { "VMware, Inc.", "vm", "drive-multidisk", TRUE, TRUE },
101 { "VMware Virtual Platform", "vm", "drive-multidisk", TRUE, TRUE },
102 { "Parallels", "vm", "drive-multidisk", TRUE, TRUE }, /* need verification */
c7028b11 103 { "Xen", "vm", "drive-multidisk", FALSE, FALSE }
87df323f 104}; /* TODO: chroots, etc. are the actual "containers", add them */
76b67a18 105
f8257c5d 106/* archs to check against when determining if machine is server */
107const gchar *server_archs[] = {
108 "hppa",
109 "sparc",
110 "sparc64"
111};
f1ad9351 112
df8fc341 113static const gchar *DEFAULT_DOMAIN = ".home.network";
114static const gchar *OS_HOSTNAME_PATH = "/etc/myname";
115
a1bcc33c 116/* --- begin method/property/dbus signal code --- */
f1ad9351 117
2bc9066a 118/* TODO free some strings here */
3d53b501 119static gboolean
1be94ede 120on_handle_set_hostname(Hostname1 *hn1_passed_interf,
3d53b501 121 GDBusMethodInvocation *invoc,
122 const gchar *greet,
123 gpointer data) {
5b70f403 124 GVariant *params;
125 gchar *proposed_hostname, *valid_hostname_buf;
2bc9066a 126 const gchar *bus_name;
127 gboolean policykit_auth, ret, try_to_set;
3ccecdd6 128 size_t check_length;
2bc9066a 129 check_auth_result is_authed;
5b70f403 130
5b70f403 131 proposed_hostname = NULL;
2bc9066a 132 ret = try_to_set = FALSE;
5b70f403 133
134 params = g_dbus_method_invocation_get_parameters(invoc);
135 g_variant_get(params, "(sb)", &proposed_hostname, &policykit_auth);
2bc9066a 136 bus_name = g_dbus_method_invocation_get_sender(invoc);
5b70f403 137
2bc9066a 138 /* verify caller has correct permissions via polkit */
5fd84921 139 is_authed = polkit_try_auth(bus_name, "org.freedesktop.hostname1.set-hostname", policykit_auth);
5b70f403 140
2bc9066a 141 switch(is_authed) {
142
143 case AUTHORIZED_NATIVELY:
144 case AUTHORIZED_BY_PROMPT:
145 try_to_set = TRUE;
146 break;
147
148 case UNAUTHORIZED_NATIVELY:
149 case UNAUTHORIZED_FAILED_PROMPT:
150 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.EACCES", "Insufficient permissions to set hostname.");
151 break;
152
153 case ERROR_BADBUS:
154 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.EFAULT", "Provided bus name is invalid.");
155 break;
156
157 case ERROR_BADACTION:
158 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.EFAULT", "Provided action ID is invalid.");
159 break;
160
161 case ERROR_GENERIC:
162 default:
163 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.ECANCELED", "Failed to set hostname for unknown reason.");
164 break;
165 }
166
167 /* verify passed hostname's validity */
168 if(try_to_set && proposed_hostname && (valid_hostname_buf = g_hostname_to_ascii(proposed_hostname))) {
169
170 check_length = strnlen(valid_hostname_buf, MAXHOSTNAMELEN + 1);
171
172 if(check_length > MAXHOSTNAMELEN) {
5b70f403 173
3ccecdd6 174 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.ENAMETOOLONG", "Hostname string exceeded maximum length.");
2bc9066a 175 g_free(valid_hostname_buf);
176
177 } else if(sethostname(proposed_hostname, check_length)) {
178
179 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.ECANCELED", "Failed to set hostname for unknown reason.");
180 g_free(valid_hostname_buf);
5b70f403 181
2bc9066a 182 } else {
5b70f403 183
2bc9066a 184 HOSTNAME = valid_hostname_buf;
3ccecdd6 185 hostname1_set_hostname(hn1_passed_interf, HOSTNAME);
2bc9066a 186 g_ptr_array_add(hostnamed_freeable, valid_hostname_buf);
3ccecdd6 187 ret = TRUE;
188 hostname1_complete_set_hostname(hn1_passed_interf, invoc);
189 }
190 }
5b70f403 191
192 return ret;
3d53b501 193}
194
195static gboolean
1be94ede 196on_handle_set_static_hostname(Hostname1 *hn1_passed_interf,
3d53b501 197 GDBusMethodInvocation *invoc,
198 const gchar *greet,
199 gpointer data) {
933f62bb 200
201 GVariant *params;
202 gchar *proposed_static_hostname, *valid_static_hostname_buf;
203 const gchar *bus_name;
204 gboolean policykit_auth, ret, try_to_set;
205 size_t check_length;
206 check_auth_result is_authed;
207
208 proposed_static_hostname = NULL;
209 ret = try_to_set = FALSE;
210
211 params = g_dbus_method_invocation_get_parameters(invoc);
212 g_variant_get(params, "(sb)", &proposed_static_hostname, &policykit_auth);
213 bus_name = g_dbus_method_invocation_get_sender(invoc);
214
215 /* verify caller has correct permissions via polkit */
5fd84921 216 is_authed = polkit_try_auth(bus_name, "org.freedesktop.hostname1.set-static-hostname", policykit_auth);
933f62bb 217
218 switch(is_authed) {
219
220 case AUTHORIZED_NATIVELY:
221 case AUTHORIZED_BY_PROMPT:
222 try_to_set = TRUE;
223 break;
224
225 case UNAUTHORIZED_NATIVELY:
226 case UNAUTHORIZED_FAILED_PROMPT:
227 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.EACCES", "Insufficient permissions to set static hostname.");
228 break;
229
230 case ERROR_BADBUS:
231 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.EFAULT", "Provided bus name is invalid.");
232 break;
233
234 case ERROR_BADACTION:
235 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.EFAULT", "Provided action ID is invalid.");
236 break;
237
238 case ERROR_GENERIC:
239 default:
240 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.ECANCELED", "Failed to set static hostname for unknown reason.");
241 break;
242 }
243
244 /* verify passed hostname's validity */
245 if(try_to_set && proposed_static_hostname && (valid_static_hostname_buf = g_hostname_to_ascii(proposed_static_hostname))) {
246
247 check_length = strnlen(valid_static_hostname_buf, MAXHOSTNAMELEN + 1);
248
249 if(check_length > MAXHOSTNAMELEN) {
250
251 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.ENAMETOOLONG", "Static hostname string exceeded maximum length.");
252 g_free(valid_static_hostname_buf);
253
254 } else if(!(STATIC_HOSTNAME = valid_static_hostname_buf)) {
255
256 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.ECANCELED", "Failed to set static hostname for unknown reason.");
257 g_free(valid_static_hostname_buf);
258
259 } else {
260
261 g_strdelimit(STATIC_HOSTNAME, " ", '-');
057ab9c2 262 hostname1_set_static_hostname(hn1_passed_interf, STATIC_HOSTNAME);
933f62bb 263 g_ptr_array_add(hostnamed_freeable, valid_static_hostname_buf);
057ab9c2 264 ret = (!sethostname(valid_static_hostname_buf, MAXHOSTNAMELEN)) ? TRUE : FALSE; /* TODO set /etc/myname, guarantee domain or substitue .home.network" */
933f62bb 265 hostname1_complete_set_static_hostname(hn1_passed_interf, invoc);
266 }
267 }
268
269 return ret;
3d53b501 270}
271
272static gboolean
1be94ede 273on_handle_set_pretty_hostname(Hostname1 *hn1_passed_interf,
3d53b501 274 GDBusMethodInvocation *invoc,
275 const gchar *greet,
276 gpointer data) {
933f62bb 277
278 GVariant *params;
279 gchar *proposed_pretty_hostname, *valid_pretty_hostname_buf, *computed_static_hostname;
280 const gchar *bus_name;
281 gboolean policykit_auth, ret, try_to_set;
282 size_t check_length;
283 check_auth_result is_authed;
284 GKeyFile *config;
285
286 config = g_key_file_new();
287 proposed_pretty_hostname = NULL;
288 ret = try_to_set = FALSE;
289
290 params = g_dbus_method_invocation_get_parameters(invoc);
291 g_variant_get(params, "(sb)", &proposed_pretty_hostname, &policykit_auth);
292 bus_name = g_dbus_method_invocation_get_sender(invoc);
293
294 /* verify caller has correct permissions via polkit */
5fd84921 295 is_authed = polkit_try_auth(bus_name, "org.freedesktop.hostname1.set-pretty-hostname", policykit_auth);
933f62bb 296
297 switch(is_authed) {
298
299 case AUTHORIZED_NATIVELY:
300 case AUTHORIZED_BY_PROMPT:
301 try_to_set = TRUE;
302 break;
303
304 case UNAUTHORIZED_NATIVELY:
305 case UNAUTHORIZED_FAILED_PROMPT:
306 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.EACCES", "Insufficient permissions to set pretty hostname.");
307 break;
308
309 case ERROR_BADBUS:
310 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.EFAULT", "Provided bus name is invalid.");
311 break;
312
313 case ERROR_BADACTION:
314 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.EFAULT", "Provided action ID is invalid.");
315 break;
316
317 case ERROR_GENERIC:
318 default:
319 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.ECANCELED", "Failed to set pretty hostname for unknown reason.");
320 break;
321 }
322
323 /* verify passed hostname's validity */
324 if(try_to_set && proposed_pretty_hostname && (valid_pretty_hostname_buf = g_locale_to_utf8(proposed_pretty_hostname, -1, 0, 0, NULL))) {
325
326 check_length = strnlen(valid_pretty_hostname_buf, MAXHOSTNAMELEN + 1);
327
328 if(check_length > MAXHOSTNAMELEN) {
329
330 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.ENAMETOOLONG", "Static hostname string exceeded maximum length.");
331 g_free(valid_pretty_hostname_buf);
332
333 } else if(!(PRETTY_HOSTNAME = valid_pretty_hostname_buf)) {
334
335 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.ECANCELED", "Failed to set pretty hostname for unknown reason.");
336 g_free(valid_pretty_hostname_buf);
337
338 } else {
339
340 hostname1_set_pretty_hostname(hn1_passed_interf, PRETTY_HOSTNAME);
341 g_ptr_array_add(hostnamed_freeable, valid_pretty_hostname_buf);
342 hostname1_complete_set_pretty_hostname(hn1_passed_interf, invoc);
343 ret = TRUE;
344
3d6bffb8 345 if(g_key_file_load_from_file(config, "/etc/machine-info", G_KEY_FILE_NONE, NULL)) {
933f62bb 346
3d6bffb8 347 g_key_file_set_string(config, "hostnamed", "PRETTY_HOSTNAME", valid_pretty_hostname_buf);
933f62bb 348
45e09604 349 /* if((computed_static_hostname = g_hostname_to_ascii(PRETTY_HOSTNAME))) {
933f62bb 350
351 g_strdelimit(computed_static_hostname, " ", '-');
352 hostname1_set_static_hostname(hn1_passed_interf, computed_static_hostname);
353 STATIC_HOSTNAME = computed_static_hostname;
354 g_ptr_array_add(hostnamed_freeable, computed_static_hostname);
355 g_key_file_set_string(config, "hostnamed", "StaticHostname", computed_static_hostname);
356
45e09604 357 } */
933f62bb 358 }
933f62bb 359 }
360 }
361
3d6bffb8 362 g_key_file_save_to_file(config, "/etc/machine-info", NULL);
933f62bb 363 g_key_file_unref(config);
364
365 return ret;
3d53b501 366}
367
368static gboolean
1be94ede 369on_handle_set_chassis(Hostname1 *hn1_passed_interf,
3d53b501 370 GDBusMethodInvocation *invoc,
371 const gchar *greet,
372 gpointer data) {
933f62bb 373
374 GVariant *params;
375 gchar *proposed_chassis_name, *valid_chassis_name_buf;
376 const gchar *bus_name;
377 gboolean policykit_auth, ret, try_to_set;
378 check_auth_result is_authed;
379 GKeyFile *config;
380
381 config = g_key_file_new();
382 proposed_chassis_name = NULL;
383 ret = try_to_set = FALSE;
384 valid_chassis_name_buf = (gchar *)g_malloc0(8192);
385
386 params = g_dbus_method_invocation_get_parameters(invoc);
387 g_variant_get(params, "(sb)", &proposed_chassis_name, &policykit_auth);
388 bus_name = g_dbus_method_invocation_get_sender(invoc);
389
390 g_strlcpy(valid_chassis_name_buf, proposed_chassis_name, (gsize)64);
391
392 /* verify caller has correct permissions via polkit */
5fd84921 393 is_authed = polkit_try_auth(bus_name, "org.freedesktop.hostname1.set-chassis", policykit_auth);
933f62bb 394
395 switch(is_authed) {
396
397 case AUTHORIZED_NATIVELY:
398 case AUTHORIZED_BY_PROMPT:
399 try_to_set = TRUE;
400 break;
401
402 case UNAUTHORIZED_NATIVELY:
403 case UNAUTHORIZED_FAILED_PROMPT:
404 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.EACCES", "Insufficient permissions to set chassis type.");
405 break;
406
407 case ERROR_BADBUS:
408 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.EFAULT", "Provided bus name is invalid.");
409 break;
410
411 case ERROR_BADACTION:
412 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.EFAULT", "Provided action ID is invalid.");
413 break;
414
415 case ERROR_GENERIC:
416 default:
417 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.ECANCELED", "Failed to set chassis type for unknown reason.");
418 break;
419 }
420
421 /* verify passed chassis type's validity */
422 if(try_to_set && proposed_chassis_name) {
423
424 if(!is_valid_chassis_type(proposed_chassis_name)) {
425
426 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.ECANCELED", "Chassis type must be 'desktop', 'laptop', 'server', 'tablet', 'handset', 'vm', or 'container'.");
427 g_free(valid_chassis_name_buf);
428
429 } else if(!(CHASSIS = valid_chassis_name_buf)) {
430
431 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.ECANCELED", "Failed to set chassis type for unknown reason.");
432 g_free(valid_chassis_name_buf);
433
434 } else {
435
436 hostname1_set_chassis(hn1_passed_interf, CHASSIS);
437 g_ptr_array_add(hostnamed_freeable, valid_chassis_name_buf);
438 hostname1_complete_set_chassis(hn1_passed_interf, invoc);
439
3d6bffb8 440 if(g_key_file_load_from_file(config, "/etc/machine-info", G_KEY_FILE_NONE, NULL)) {
933f62bb 441
442 ret = TRUE;
443 g_key_file_set_string(config, "hostnamed", "ChassisType", valid_chassis_name_buf);
444
445 }
446 }
447 }
448
3d6bffb8 449 g_key_file_save_to_file(config, "/etc/machine-info", NULL);
933f62bb 450 g_key_file_unref(config);
451
452 return ret;
3d53b501 453}
454
455static gboolean
1be94ede 456on_handle_set_icon_name(Hostname1 *hn1_passed_interf,
3d53b501 457 GDBusMethodInvocation *invoc,
458 const gchar *greet,
459 gpointer data) {
933f62bb 460
461 GVariant *params;
462 gchar *proposed_icon_name, *valid_icon_name_buf;
463 const gchar *bus_name;
464 gboolean policykit_auth, ret, try_to_set;
465 check_auth_result is_authed;
466 GKeyFile *config;
467
468 config = g_key_file_new();
469 proposed_icon_name = NULL;
470 ret = try_to_set = FALSE;
471
472 params = g_dbus_method_invocation_get_parameters(invoc);
473 g_variant_get(params, "(sb)", &proposed_icon_name, &policykit_auth);
474 bus_name = g_dbus_method_invocation_get_sender(invoc);
475
476 /* verify caller has correct permissions via polkit */
5fd84921 477 is_authed = polkit_try_auth(bus_name, "org.freedesktop.hostname1.set-icon-name", policykit_auth);
933f62bb 478
479 switch(is_authed) {
480
481 case AUTHORIZED_NATIVELY:
482 case AUTHORIZED_BY_PROMPT:
483 try_to_set = TRUE;
484 break;
485
486 case UNAUTHORIZED_NATIVELY:
487 case UNAUTHORIZED_FAILED_PROMPT:
488 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.EACCES", "Insufficient permissions to set icon name.");
489 break;
490
491 case ERROR_BADBUS:
492 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.EFAULT", "Provided bus name is invalid.");
493 break;
494
495 case ERROR_BADACTION:
496 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.EFAULT", "Provided action ID is invalid.");
497 break;
498
499 case ERROR_GENERIC:
500 default:
501 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.ECANCELED", "Failed to set icon name for unknown reason.");
502 break;
503 }
504
505 /* verify passed chassis type's validity */
506 if(try_to_set && proposed_icon_name) {
507
508 g_strlcpy(valid_icon_name_buf, proposed_icon_name, (gsize)64);
509
510 if(!(ICON = valid_icon_name_buf)) {
511
512 g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.hostname1.Error.ECANCELED", "Failed to set icon name for unknown reason.");
513 g_free(valid_icon_name_buf);
514
515 } else {
516
517 hostname1_set_icon_name(hn1_passed_interf, ICON);
518 g_ptr_array_add(hostnamed_freeable, valid_icon_name_buf);
519 hostname1_complete_set_icon_name(hn1_passed_interf, invoc);
520
3d6bffb8 521 if(g_key_file_load_from_file(config, "/etc/machine-info", G_KEY_FILE_NONE, NULL)) {
933f62bb 522
523 ret = TRUE;
524 g_key_file_set_string(config, "hostnamed", "IconName", valid_icon_name_buf);
525
526 }
527 }
528 }
529
3d6bffb8 530 g_key_file_save_to_file(config, "/etc/machine-info", NULL);
933f62bb 531 g_key_file_unref(config);
532
533 return ret;
3d53b501 534}
535
76b67a18 536/* note: all hostnamed/hostname1's properties are read-only,
537 * and do not need set_ functions, gdbus-codegen realized
538 * this from the XML and handled the to-be error of trying
539 * to set a read-only property's value
540 */
541
542const gchar *
543our_get_hostname() {
544
3ccecdd6 545 gchar *hostname_buf;
546 hostname_buf = (gchar *)g_malloc0(MAXHOSTNAMELEN);
547
548 if(gethostname(hostname_buf, MAXHOSTNAMELEN))
549 return "localhost.home.network"; /* TODO bomb out here probably */
550
551 else if(!g_strcmp0(HOSTNAME, hostname_buf)) {
552
553 g_free(hostname_buf);
19c6b83d 554 return HOSTNAME;
3ccecdd6 555 }
556
557 g_ptr_array_add(hostnamed_freeable, hostname_buf);
558 HOSTNAME = hostname_buf;
559 hostname1_set_hostname(hostnamed_interf, HOSTNAME);
19c6b83d 560
3ccecdd6 561 return HOSTNAME;
76b67a18 562}
563
564const gchar *
565our_get_static_hostname() {
566
3ccecdd6 567 if(STATIC_HOSTNAME && g_strcmp0(STATIC_HOSTNAME, ""))
19c6b83d 568 return STATIC_HOSTNAME;
569 else if(HOSTNAME)
570 return HOSTNAME;
571
3ccecdd6 572 return "localhost.home.network";
76b67a18 573}
574
575const gchar *
576our_get_pretty_hostname() {
577
19c6b83d 578 if(PRETTY_HOSTNAME)
579 return PRETTY_HOSTNAME;
580
a2fffc07 581 return "";
76b67a18 582}
583
584const gchar *
585our_get_chassis() {
586
f8257c5d 587 if(CHASSIS)
588 return CHASSIS;
f1ad9351 589
19c6b83d 590 return "desktop"; /* this leads to the most generic beheivor in the unlikely case its returned */
76b67a18 591}
592
593const gchar *
594our_get_icon_name() {
595
f8257c5d 596 if(ICON)
597 return ICON;
598
599 return "";
76b67a18 600}
601
602const gchar *
603our_get_kernel_name() {
604
bd24ff31 605 if(KERN_NAME)
606 return KERN_NAME;
607
608 return "";
76b67a18 609}
610
611const gchar *
612our_get_kernel_version() {
613
bd24ff31 614 if(KERN_VERS)
615 return KERN_VERS;
616
617 return "";
76b67a18 618}
619
620const gchar *
621our_get_kernel_release() {
622
bd24ff31 623 if(KERN_RELEASE)
624 return KERN_RELEASE;
625
626 return "";
76b67a18 627}
628
629const gchar *
630our_get_os_cpename() {
631
3808ecc5
AJ
632 /* XXX needs to parse /etc/os-release (fallback to /usr/local/lib/os-release) */
633 return "";
76b67a18 634}
635
636const gchar *
637our_get_os_pretty_name() {
638
bd24ff31 639 return "OpenBSD";
76b67a18 640}
641
1ce41045 642/* --- end method/property/dbus signal code, begin bus/name handlers --- */
0df0018d 643
5b005882 644static void hostnamed_on_bus_acquired(GDBusConnection *conn,
9cab3afe 645 const gchar *name,
646 gpointer user_data) {
d1e1db9e 647
0f339959 648 g_printf("got bus/name, exporting %s's interface...\n", name);
d1e1db9e 649
90f54407 650 hostnamed_interf = hostname1_skeleton_new();
3d53b501 651
76b67a18 652 /* attach function pointers to generated struct's method handlers */
3d53b501 653 g_signal_connect(hostnamed_interf, "handle-set-hostname", G_CALLBACK(on_handle_set_hostname), NULL);
654 g_signal_connect(hostnamed_interf, "handle-set-static-hostname", G_CALLBACK(on_handle_set_static_hostname), NULL);
655 g_signal_connect(hostnamed_interf, "handle-set-pretty-hostname", G_CALLBACK(on_handle_set_pretty_hostname), NULL);
656 g_signal_connect(hostnamed_interf, "handle-set-chassis", G_CALLBACK(on_handle_set_chassis), NULL);
657 g_signal_connect(hostnamed_interf, "handle-set-icon-name", G_CALLBACK(on_handle_set_icon_name), NULL);
658
76b67a18 659 /* set our properties before export */
1be94ede 660 hostname1_set_hostname(hostnamed_interf, our_get_hostname());
661 hostname1_set_static_hostname(hostnamed_interf, our_get_static_hostname());
662 hostname1_set_pretty_hostname(hostnamed_interf, our_get_pretty_hostname());
663 hostname1_set_chassis(hostnamed_interf, our_get_chassis());
664 hostname1_set_icon_name(hostnamed_interf, our_get_icon_name());
665 hostname1_set_kernel_name(hostnamed_interf, our_get_kernel_name());
666 hostname1_set_kernel_version(hostnamed_interf, our_get_kernel_version());
667 hostname1_set_kernel_release(hostnamed_interf, our_get_kernel_release());
668 hostname1_set_operating_system_cpename(hostnamed_interf, our_get_os_cpename());
669 hostname1_set_operating_system_pretty_name(hostnamed_interf, our_get_os_pretty_name());
76b67a18 670
3d53b501 671 if(!g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(hostnamed_interf),
1be94ede 672 conn,
673 "/org/freedesktop/hostname1",
674 NULL)) {
3d53b501 675
0f339959 676 g_printf("failed to export %s's interface!\n", name); /* unusual edge case, TODO check errno */
90f54407 677 hostnamed_mem_clean();
3d53b501 678
0f339959 679 } else {
ea207ed3 680
90f54407 681 dbus_interface_exported = TRUE;
682 g_printf("exported %s's interface on the system bus...\n", name);
683 }
0f339959 684}
1e8c7c88 685
0f339959 686static void hostnamed_on_name_acquired(GDBusConnection *conn,
90f54407 687 const gchar *name,
0f339959 688 gpointer user_data) {
689
690 g_printf("success!\n");
1e8c7c88 691}
692
5b005882 693static void hostnamed_on_name_lost(GDBusConnection *conn,
1be94ede 694 const gchar *name,
695 gpointer user_data) {
80043b36 696
90f54407 697 if(!conn) {
0f339959 698
90f54407 699 g_printf("failed to connect to the system bus while trying to acquire name '%s': either dbus-daemon isn't running or we don't have permission to push names and/or their interfaces to it.\n", name);
700 hostnamed_mem_clean();
701 }
0f339959 702
509599f0 703 g_printf("lost name %s, exiting...\n", name);
fd8852d9 704
1cd5e6fe 705 hostnamed_mem_clean();
0f339959 706}
707
708/* --- end bus/name handlers, begin misc unix functions --- */
709
710/* safe call to clean and then exit
509599f0 711 * this stops our GMainLoop safely before letting main() return */
0f339959 712void hostnamed_mem_clean() {
713
90f54407 714 g_printf("exiting...\n");
0f339959 715
90f54407 716 if(dbus_interface_exported)
717 g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(hostnamed_interf));
0f339959 718
90f54407 719 if(g_main_loop_is_running(hostnamed_loop))
720 g_main_loop_quit(hostnamed_loop);
fd8852d9 721
ea207ed3 722}
723
0f339959 724/* wrapper for glib's unix signal handling; called only once if terminatating signal is raised against us */
725gboolean unix_sig_terminate_handler(gpointer data) {
c62bceb7 726
90f54407 727 g_printf("caught SIGINT/HUP/TERM, exiting\n");
0f339959 728
90f54407 729 hostnamed_mem_clean();
730 return G_SOURCE_REMOVE;
0f339959 731}
732
733void set_signal_handlers() {
734
90f54407 735 /* we don't care about its descriptor, we never need to unregister these */
736 g_unix_signal_add(SIGINT, unix_sig_terminate_handler, NULL);
737 g_unix_signal_add(SIGHUP, unix_sig_terminate_handler, NULL);
738 g_unix_signal_add(SIGTERM, unix_sig_terminate_handler, NULL);
04cc16f2 739
90f54407 740 /* TODO: the "only once" guarantee only counts towards specific signals.
741 * make sure calling a SIGINT and SIGHUP doesn't cause term_handler()
742 * to be called twice */
0f339959 743}
744
745int main() {
bd24ff31 746
747 hostnamed_freeable = g_ptr_array_new();
748
a0ebb315 749 /* TODO: check for valid, writable config at init. if no, complain to `make install` */
bd24ff31 750
df8fc341 751 get_bsd_hostname("adsf"); /* TODO KILL ME */
752
bd24ff31 753 CHASSIS = ICON = OS_CPENAME = 0;
754 KERN_NAME = KERN_RELEASE = KERN_VERS = 0;
baf05b70 755 HOSTNAME = STATIC_HOSTNAME = PRETTY_HOSTNAME = NULL;
483e90b7 756
90f54407 757 set_signal_handlers();
387173cb 758
46835f3e 759 if(!determine_chassis_and_icon() || !set_uname_properties() || !set_names())
a1bcc33c 760 return 1;
761
baf05b70 762 hostnamed_loop = g_main_loop_new(NULL, TRUE);
fd8852d9 763
90f54407 764 bus_descriptor = g_bus_own_name(G_BUS_TYPE_SYSTEM,
0f339959 765 "org.freedesktop.hostname1",
766 G_BUS_NAME_OWNER_FLAGS_NONE,
767 hostnamed_on_bus_acquired,
768 hostnamed_on_name_acquired,
769 hostnamed_on_name_lost,
770 NULL,
771 NULL);
496f5d66 772
90f54407 773 g_main_loop_run(hostnamed_loop);
774 /* runs until single g_main_loop_quit() call is raised inside <interface>_mem_clean() */
775 g_main_loop_unref(hostnamed_loop);
1e8c7c88 776
90f54407 777 /* guaranteed unownable */
778 g_bus_unown_name(bus_descriptor);
c62bceb7 779
90f54407 780 /* at this point no operations can occur with our data, it is safe to free it + its container */
781 g_ptr_array_free(hostnamed_freeable, TRUE);
7323a4e4 782
90f54407 783 return 0;
a35a69c5 784}
785
46835f3e 786gboolean set_names() {
787
baf05b70 788 /* (1) set up */
789 gchar *hostname_buf, *static_hostname_buf, *pretty_hostname_buf;
790 GKeyFile *config;
46835f3e 791 size_t hostname_divider;
792
baf05b70 793 hostname_buf = (gchar*) g_malloc0(MAXHOSTNAMELEN);
794 static_hostname_buf = (gchar*) g_malloc0(4096);
795 pretty_hostname_buf = (gchar*) g_malloc0(4096);
46835f3e 796
baf05b70 797 config = g_key_file_new();
46835f3e 798
baf05b70 799 g_ptr_array_add(hostnamed_freeable, hostname_buf);
800 g_ptr_array_add(hostnamed_freeable, static_hostname_buf);
801 g_ptr_array_add(hostnamed_freeable, pretty_hostname_buf);
46835f3e 802
baf05b70 803 /* (2) set HOSTNAME */
804 if(gethostname(hostname_buf, MAXHOSTNAMELEN) || !g_strcmp0(hostname_buf, ""))
805 HOSTNAME = "localhost";
46835f3e 806
baf05b70 807 HOSTNAME = hostname_buf;
46835f3e 808
baf05b70 809 /* this bit gets you the /etc/myname style hostname
810 hostname_divider = strcspn(hostname_buf, ".");
811 strncpy(ret, hostname_buf, hostname_divider); */
46835f3e 812
baf05b70 813 /* (3) set PRETTY_HOSTNAME */
3d6bffb8
AJ
814 if(g_key_file_load_from_file(config, "/etc/machine-info", G_KEY_FILE_NONE, NULL)
815 && (pretty_hostname_buf = g_key_file_get_value(config, "hostnamed", "PRETTY_HOSTNAME", NULL)))
baf05b70 816 PRETTY_HOSTNAME = pretty_hostname_buf;
817 else
818 PRETTY_HOSTNAME = "";
819
45e09604 820
821 /* (4) set STATIC_HOSTNAME */
822 if((static_hostname_buf = g_key_file_get_value(config, "hostnamed", "STATIC_HOSTNAME", NULL)))
823 STATIC_HOSTNAME = static_hostname_buf;
46835f3e 824
45e09604 825 else
826 STATIC_HOSTNAME = "";
46835f3e 827
45e09604 828 if(config)
829 g_key_file_unref(config);
46835f3e 830
baf05b70 831 return (HOSTNAME && STATIC_HOSTNAME && PRETTY_HOSTNAME) ? TRUE : FALSE;
46835f3e 832}
833
bd24ff31 834gboolean set_uname_properties() {
835
836 struct utsname un;
837
838 if(-1 == uname(&un))
839 return FALSE;
840
841 KERN_NAME = (gchar*)g_malloc0(sizeof(un.sysname));
842 g_ptr_array_add(hostnamed_freeable, KERN_NAME);
843 g_strlcpy(KERN_NAME, un.sysname, sizeof(un.sysname));
844
845 KERN_RELEASE = (gchar*)g_malloc0(sizeof(un.release));
846 g_ptr_array_add(hostnamed_freeable, KERN_RELEASE);
847 g_strlcpy(KERN_RELEASE, un.release, sizeof(un.release));
848
849 KERN_VERS = (gchar*)g_malloc0(sizeof(un.version));
850 g_ptr_array_add(hostnamed_freeable, KERN_VERS);
851 g_strlcpy(KERN_VERS, un.version, sizeof(un.version));
852
853 return TRUE;
854}
855
a1bcc33c 856gboolean determine_chassis_and_icon() {
857
b81ab32a 858 const size_t bufsize = 4096;
859
f8257c5d 860 char *hwproduct, *hwmodel, *hwvendor, *hwmachine;
861 size_t hwproduct_size, hwmodel_size, hwvendor_size, hwmachine_size;
862 int hwproduct_name[2], hwmodel_name[2], hwvendor_name[2], hwmachine_name[2];
f1ad9351 863 unsigned int i;
f8257c5d 864 gboolean UNSURE_CHASSIS_FLAG, UNSURE_ICON_FLAG;
865
b81ab32a 866 hwproduct_size = hwmodel_size = hwvendor_size = hwmachine_size = bufsize;
68e777c6 867 UNSURE_CHASSIS_FLAG = UNSURE_ICON_FLAG = FALSE;
5ac7f542 868 i = 0;
f1ad9351 869
b81ab32a 870 hwproduct = (char*)g_malloc0(4096);
871 hwmodel = (char*)g_malloc0(4096);
872 hwvendor = (char*)g_malloc0(4096);
873 hwmachine = (char*)g_malloc0(4096);
874
bd24ff31 875 g_ptr_array_add(hostnamed_freeable, hwproduct);
876 g_ptr_array_add(hostnamed_freeable, hwmodel);
877 g_ptr_array_add(hostnamed_freeable, hwvendor);
878 g_ptr_array_add(hostnamed_freeable, hwmachine);
879
a1bcc33c 880 hwproduct_name[0] = CTL_HW;
881 hwproduct_name[1] = HW_PRODUCT;
f1ad9351 882
a1bcc33c 883 hwmodel_name[0] = CTL_HW;
884 hwmodel_name[1] = HW_MODEL;
885
886 hwvendor_name[0] = CTL_HW;
887 hwvendor_name[1] = HW_VENDOR;
888
f8257c5d 889 hwmachine_name[0] = CTL_HW;
890 hwmachine_name[1] = HW_MACHINE;
891
a1bcc33c 892 /* pass NULL buffer to check size first, then pass hw to be filled according to freshly-set hw_size */
893 if(-1 == sysctl(hwproduct_name, 2, NULL, &hwproduct_size, NULL, 0) || -1 == sysctl(hwproduct_name, 2, hwproduct, &hwproduct_size, NULL, 0))
894 return FALSE;
895
896 if(-1 == sysctl(hwmodel_name, 2, NULL, &hwmodel_size, NULL, 0) || -1 == sysctl(hwmodel_name, 2, hwmodel, &hwmodel_size, NULL, 0))
897 return FALSE;
898
899 if(-1 == sysctl(hwvendor_name, 2, NULL, &hwvendor_size, NULL, 0) || -1 == sysctl(hwvendor_name, 2, hwvendor, &hwvendor_size, NULL, 0))
900 return FALSE;
901
f8257c5d 902 if(-1 == sysctl(hwmachine_name, 2, NULL, &hwmachine_size, NULL, 0) || -1 == sysctl(hwmachine_name, 2, hwmachine, &hwmachine_size, NULL, 0))
903 return FALSE;
904
a1bcc33c 905 /* TODO: test for laptop, if not, dmidecode for desktop vs. server
906 * probably move this code to vm test func and set a global after running it early, once */
907
f8257c5d 908 for(; i < G_N_ELEMENTS(chassis_indicator_table); i++) {
909 if(strcasestr(hwproduct, chassis_indicator_table[i].match_string)
910 || strcasestr(hwmodel, chassis_indicator_table[i].match_string)
911 || strcasestr(hwvendor, chassis_indicator_table[i].match_string)) {
912
913 if(!UNSURE_CHASSIS_FLAG && chassis_indicator_table[i].chassis) {
914
915 UNSURE_CHASSIS_FLAG = chassis_indicator_table[i].chassis_precedence;
916 CHASSIS = chassis_indicator_table[i].chassis;
917 }
918
919 if(!UNSURE_ICON_FLAG && chassis_indicator_table[i].icon) {
920
921 UNSURE_ICON_FLAG = chassis_indicator_table[i].icon_precedence;
922 ICON = chassis_indicator_table[i].icon;
923 }
924 }
925 }
926
927 if(up_native_is_laptop()) {
928
929 if(!CHASSIS)
930 CHASSIS = "laptop";
931 if(!ICON)
932 ICON = "input-touchpad"; /* TODO pull an icon package that actually has the icons we're looking for */
933
934 } else if(is_server(hwmachine)) {
935
936 if(!CHASSIS)
937 CHASSIS = "server";
938 if(!ICON)
939 ICON = "uninterruptible-power-supply";
940
941 } else if(!CHASSIS || !ICON) {
f1ad9351 942
f8257c5d 943 if(!CHASSIS)
944 CHASSIS = "desktop";
945 if(!ICON)
946 ICON = "computer";
947 }
948
949 return (CHASSIS && ICON);
950}
951
952gboolean is_server(gchar *arch) {
953
954 unsigned int i;
a1bcc33c 955
f8257c5d 956 for(; i < G_N_ELEMENTS(server_archs); i++)
957 if(strcasestr(arch, server_archs[i]))
958 return TRUE;
959
960 return FALSE;
961}
962
963gboolean up_native_is_laptop() {
964
965 struct apm_power_info bstate;
966 struct sensordev acpiac;
967
968 if (up_native_get_sensordev("acpiac0", &acpiac))
969 return TRUE;
970
971 if (-1 == ioctl(up_apm_get_fd(), APM_IOC_GETPOWER, &bstate))
972 g_error("ioctl on apm fd failed : %s", g_strerror(errno));
973
974 return bstate.ac_state != APM_AC_UNKNOWN;
975}
976
977int up_apm_get_fd() {
978
979 static int apm_fd = 0;
980
981 if(apm_fd == 0) {
982
983 g_debug("apm_fd is not initialized yet, opening");
984
985 /* open /dev/apm */
986 if((apm_fd = open("/dev/apm", O_RDONLY)) == -1) {
987 if(errno != ENXIO && errno != ENOENT)
988 g_error("cannot open device file");
989 }
990 }
991
992 return apm_fd;
993}
994
995gboolean up_native_get_sensordev(const char * id, struct sensordev * snsrdev) {
996
997 int devn;
998 size_t sdlen = sizeof(struct sensordev);
999 int mib[] = {CTL_HW, HW_SENSORS, 0, 0 ,0};
1000
1001 for (devn = 0 ; ; devn++) {
1002 mib[2] = devn;
1003 if(sysctl(mib, 3, snsrdev, &sdlen, NULL, 0) == -1) {
1004 if(errno == ENXIO)
1005 continue;
1006 if(errno == ENOENT)
1007 break;
1008 }
1009
1010 if (!strcmp(snsrdev->xname, id))
1011 return TRUE;
1012 }
1013
1014 return FALSE;
f1ad9351 1015}
933f62bb 1016
1017static gboolean is_valid_chassis_type(gchar *test) {
1018
1019 if(!g_strcmp0(test, "desktop") ||
1020 !g_strcmp0(test, "laptop") ||
1021 !g_strcmp0(test, "server") ||
1022 !g_strcmp0(test, "tablet") ||
1023 !g_strcmp0(test, "handset") ||
1024 !g_strcmp0(test, "vm") ||
1025 !g_strcmp0(test, "container") ||
1026 !g_strcmp0(test, ""))
1027 return TRUE;
1028
1029 return FALSE;
df8fc341 1030}
1031
1032/* returns a proper, bsd-style FQDN hostname safe to write to /etc/myname
1033 * if proposed_hostname does not contain an appended domain, the one in /etc/myname is substituted.
1034 * failing that, DEFAULT_DOMAIN is used. NULL if proposed_hostname is invalid
1035 * returns string that should be g_free()'d, or NULL if passed an invalid hostname */
1036static gchar *get_bsd_hostname(gchar *proposed_hostname) {
1037
1038 gchar *bsd_hostname, *ascii_translated_hostname, **myname_contents, *passed_domain, *temp_buf;
1039 size_t domain_len, check_len;
1040 gboolean read_result;
1041
1042 g_strdelimit(proposed_hostname, "`~!@#$%^&*()_=+[{]}|:;'\"\\", '-');
1043
1044 ascii_translated_hostname = g_hostname_to_ascii(proposed_hostname);
1045 check_len = strnlen(ascii_translated_hostname, MAXHOSTNAMELEN);
1046
1047 if(!ascii_translated_hostname || !check_len || check_len > MAXHOSTNAMELEN || !g_strcmp0("", ascii_translated_hostname) || !g_strcmp0(".", ascii_translated_hostname)) {
1048
1049 bsd_hostname = NULL;
1050 passed_domain = NULL;
1051 myname_contents = NULL;
1052
1053 } else if((passed_domain = has_domain(ascii_translated_hostname))) {
1054
1055 bsd_hostname = (gchar *) g_malloc0(MAXHOSTNAMELEN);
1056 g_strlcpy(bsd_hostname, ascii_translated_hostname, MAXHOSTNAMELEN);
1057
1058 passed_domain = NULL;
1059 myname_contents = NULL;
1060
1061 } else {
1062
1063 myname_contents = (gchar **) g_malloc0(MAXHOSTNAMELEN * 2);
1064 read_result = g_file_get_contents(OS_HOSTNAME_PATH, myname_contents, NULL, NULL);
1065
1066 if(read_result && (passed_domain = has_domain(myname_contents[0]))) {
1067
1068 domain_len = strnlen(passed_domain, MAXHOSTNAMELEN);
1069
1070 if((domain_len + check_len) > MAXHOSTNAMELEN)
1071 bsd_hostname = NULL;
1072 else
1073 bsd_hostname = g_strconcat(ascii_translated_hostname, passed_domain, NULL);
1074
1075 } else if(myname_contents[0]) {
1076
1077 g_printf("%s does not contain a proper FQDN! this is a significant error on BSD machines, otherwise OK.\nfalling back to default domain, '%s'\n", OS_HOSTNAME_PATH, DEFAULT_DOMAIN);
1078
1079 domain_len = strnlen(DEFAULT_DOMAIN, MAXHOSTNAMELEN);
1080
1081 if((domain_len + check_len) > MAXHOSTNAMELEN)
1082 bsd_hostname = NULL;
1083 else
1084 bsd_hostname = g_strconcat(ascii_translated_hostname, DEFAULT_DOMAIN, NULL);
1085
1086 } else {
1087
1088 g_printf("could not read hostname at %s, this is a major error\n", OS_HOSTNAME_PATH);
1089 bsd_hostname = NULL;
1090 passed_domain = (gchar *) g_malloc0(MAXHOSTNAMELEN);
1091 }
1092 }
1093
1094 if(passed_domain)
1095 g_free(passed_domain);
1096 if(myname_contents)
1097 g_free(myname_contents);
1098
1099 if(bsd_hostname && !strchr(bsd_hostname, '\n')) {
1100
1101 temp_buf = bsd_hostname;
1102 bsd_hostname = g_strconcat(bsd_hostname, "\n", NULL);
1103 g_free(temp_buf);
1104 }
1105
1106 return bsd_hostname;
1107}
1108
1109/* returns NULL if no domain, otherwise append-appropriate domain string you must g_free()
1110 * i.e. has_domain("foo.bar.com") returns ".bar.com"
1111 * only pass g_hostname_to_ascii'd strings */
1112static gchar *has_domain(const gchar *test) {
1113
1114 size_t hostname_len, full_len;
1115 gchar *ret;
1116
1117 hostname_len = strcspn(test, ".");
1118 full_len = strnlen(test, MAXHOSTNAMELEN);
1119
1120 if(full_len == hostname_len)
1121 return NULL;
1122
1123 ret = (gchar *) g_malloc0(MAXHOSTNAMELEN);
1124 g_strlcpy(ret, &test[hostname_len], MAXHOSTNAMELEN);
1125
1126 return ret;
1127}