add WIP bit of chassis detection for hostnamed
[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>
fd8852d9 20
d15318db 21#include <sys/param.h>
f1ad9351 22#include <sys/sysctl.h>
28cac2f0 23#include <string.h>
d15318db 24
4c04b514 25#include <glib/gprintf.h>
8caf1f61 26#include <glib-unix.h>
4c04b514 27
1e8c7c88 28#include "hostnamed-gen.h"
0f339959 29#include "hostnamed.h"
a35a69c5 30
fd8852d9 31GPtrArray *hostnamed_freeable;
1be94ede 32Hostname1 *hostnamed_interf;
3d53b501 33
0f339959 34GMainLoop *hostnamed_loop;
35
36guint bus_descriptor;
37gboolean dbus_interface_exported; /* reliable because of gdbus operational guarantees */
38
1ce41045 39/* --- begin method/property/dbus signal code --- */
76b67a18 40
f1ad9351 41/* add any sysctl strings that suggest virtualization here */
42const gchar* vmstring_list[] = {
43 "QEMU Virtual CPU",
44 "SmartDC HVM",
45 "KVM",
46 "VirtualBox"
47};
48
49static gboolean is_vm;
50
3d53b501 51static gboolean
1be94ede 52on_handle_set_hostname(Hostname1 *hn1_passed_interf,
3d53b501 53 GDBusMethodInvocation *invoc,
54 const gchar *greet,
55 gpointer data) {
56 return FALSE;
57}
58
59static gboolean
1be94ede 60on_handle_set_static_hostname(Hostname1 *hn1_passed_interf,
3d53b501 61 GDBusMethodInvocation *invoc,
62 const gchar *greet,
63 gpointer data) {
64 return FALSE;
65}
66
67static gboolean
1be94ede 68on_handle_set_pretty_hostname(Hostname1 *hn1_passed_interf,
3d53b501 69 GDBusMethodInvocation *invoc,
70 const gchar *greet,
71 gpointer data) {
72 return FALSE;
73}
74
75static gboolean
1be94ede 76on_handle_set_chassis(Hostname1 *hn1_passed_interf,
3d53b501 77 GDBusMethodInvocation *invoc,
78 const gchar *greet,
79 gpointer data) {
80 return FALSE;
81}
82
83static gboolean
1be94ede 84on_handle_set_icon_name(Hostname1 *hn1_passed_interf,
3d53b501 85 GDBusMethodInvocation *invoc,
86 const gchar *greet,
87 gpointer data) {
88 return FALSE;
89}
90
76b67a18 91/* note: all hostnamed/hostname1's properties are read-only,
92 * and do not need set_ functions, gdbus-codegen realized
93 * this from the XML and handled the to-be error of trying
94 * to set a read-only property's value
95 */
96
97const gchar *
98our_get_hostname() {
99
90f54407 100 gchar *hostname_buf, *ret;
101 size_t hostname_divider;
d15318db 102
a2fffc07 103 hostname_buf = (gchar*) g_malloc0(MAXHOSTNAMELEN);
90f54407 104 ret = (gchar*) g_malloc0(MAXHOSTNAMELEN);
a2fffc07 105
90f54407 106 g_ptr_array_add(hostnamed_freeable, hostname_buf);
107 g_ptr_array_add(hostnamed_freeable, ret);
7323a4e4 108
a2fffc07 109 if(gethostname(hostname_buf, MAXHOSTNAMELEN) || g_strcmp0(hostname_buf, "") == 0)
110 return "localhost";
d15318db 111
90f54407 112 hostname_divider = strcspn(hostname_buf, ".");
28cac2f0 113
90f54407 114 return strncpy(ret, hostname_buf, hostname_divider);
76b67a18 115}
116
117const gchar *
118our_get_static_hostname() {
119
af14252f 120 const gchar *pretty_hostname;
121 const gchar *ret;
a2fffc07 122
123 pretty_hostname = our_get_pretty_hostname();
124
125 if(g_strcmp0(pretty_hostname, "") == 0)
126 ret = our_get_hostname();
127
af14252f 128 else if((ret = g_hostname_to_ascii(pretty_hostname))) {
a2fffc07 129
af14252f 130 g_ptr_array_add(hostnamed_freeable, (gpointer)ret);
a2fffc07 131 return ret;
132 }
133
134 return ret;
76b67a18 135}
136
137const gchar *
138our_get_pretty_hostname() {
139
a2fffc07 140 GKeyFile *config;
141 gchar *ret;
142
f1ad9351 143 config = g_key_file_new();
144
a2fffc07 145 if(g_key_file_load_from_file(config, "/etc/systemd_compat.conf", G_KEY_FILE_NONE, NULL)
af14252f 146 && (ret = g_key_file_get_value(config, "hostnamed", "PrettyHostname", NULL))) { /* ret might need to be freed, docs dont specify but i am suspicious */
a2fffc07 147
f1ad9351 148 g_key_file_unref(config);
a2fffc07 149 return ret;
150 }
151
152 if(config)
153 g_free(config);
154
155 return "";
76b67a18 156}
157
158const gchar *
159our_get_chassis() {
160
f1ad9351 161 char *hwproduct, *hwmodel;
162 size_t hwproduct_size, hwmodel_size;
163 int hwproduct_name[2], hwmodel_name[2];
164
165 hwproduct_name[0] = CTL_HW;
166 hwproduct_name[1] = HW_PRODUCT;
167
168 hwmodel_name[0] = CTL_HW;
169 hwmodel_name[1] = HW_MODEL;
170
171 /* pass NULL buffer to check size first, then pass hw to be filled according to freshly-set hw_size */
172 if(sysctl(&hwproduct_name, 2, NULL, &hwproduct_size, NULL, 0) || sysctl(&hwproduct_name, 2, hwproduct, &hwproduct_size, NULL, 0))
173 return "desktop"; /* TODO error properly here */
174
175 if(sysctl(&hwmodel_name, 2, NULL, &hwmodel_size, NULL, 0) || sysctl(&hwmodel_name, 2, hwmodel, &hwmodel_size, NULL, 0))
176 return "desktop"; /* TODO error properly here */
177
178 if(test_against_known_vm_strings(hwproduct) || test_against_known_vm_strings(hwmodel))
179 return "vm"; /*TODO differentiate between VMs (hardware virt, seperate kernel) and containers (paravirt, shared kernel)
180
181 /* TODO: test for laptop, if not, dmidecode for desktop vs. server
182 * probably move this code to vm test func and set a global after running it early, once */
183
184 return "desktop";
76b67a18 185}
186
187const gchar *
188our_get_icon_name() {
189
190 return "TODO";
191}
192
193const gchar *
194our_get_kernel_name() {
195
196 return "TODO";
197}
198
199const gchar *
200our_get_kernel_version() {
201
202 return "TODO";
203}
204
205const gchar *
206our_get_kernel_release() {
207
208 return "TODO";
209}
210
211const gchar *
212our_get_os_cpename() {
213
214 return "TODO";
215}
216
217const gchar *
218our_get_os_pretty_name() {
219
220 return "TODO";
221}
222
1ce41045 223/* --- end method/property/dbus signal code, begin bus/name handlers --- */
0df0018d 224
5b005882 225static void hostnamed_on_bus_acquired(GDBusConnection *conn,
1cd5e6fe 226 const gchar *name,
227 gpointer user_data) {
d1e1db9e 228
0f339959 229 g_printf("got bus/name, exporting %s's interface...\n", name);
d1e1db9e 230
90f54407 231 hostnamed_interf = hostname1_skeleton_new();
3d53b501 232
76b67a18 233 /* attach function pointers to generated struct's method handlers */
3d53b501 234 g_signal_connect(hostnamed_interf, "handle-set-hostname", G_CALLBACK(on_handle_set_hostname), NULL);
235 g_signal_connect(hostnamed_interf, "handle-set-static-hostname", G_CALLBACK(on_handle_set_static_hostname), NULL);
236 g_signal_connect(hostnamed_interf, "handle-set-pretty-hostname", G_CALLBACK(on_handle_set_pretty_hostname), NULL);
237 g_signal_connect(hostnamed_interf, "handle-set-chassis", G_CALLBACK(on_handle_set_chassis), NULL);
238 g_signal_connect(hostnamed_interf, "handle-set-icon-name", G_CALLBACK(on_handle_set_icon_name), NULL);
239
76b67a18 240 /* set our properties before export */
1be94ede 241 hostname1_set_hostname(hostnamed_interf, our_get_hostname());
242 hostname1_set_static_hostname(hostnamed_interf, our_get_static_hostname());
243 hostname1_set_pretty_hostname(hostnamed_interf, our_get_pretty_hostname());
244 hostname1_set_chassis(hostnamed_interf, our_get_chassis());
245 hostname1_set_icon_name(hostnamed_interf, our_get_icon_name());
246 hostname1_set_kernel_name(hostnamed_interf, our_get_kernel_name());
247 hostname1_set_kernel_version(hostnamed_interf, our_get_kernel_version());
248 hostname1_set_kernel_release(hostnamed_interf, our_get_kernel_release());
249 hostname1_set_operating_system_cpename(hostnamed_interf, our_get_os_cpename());
250 hostname1_set_operating_system_pretty_name(hostnamed_interf, our_get_os_pretty_name());
76b67a18 251
3d53b501 252 if(!g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(hostnamed_interf),
1be94ede 253 conn,
254 "/org/freedesktop/hostname1",
255 NULL)) {
3d53b501 256
0f339959 257 g_printf("failed to export %s's interface!\n", name); /* unusual edge case, TODO check errno */
90f54407 258 hostnamed_mem_clean();
3d53b501 259
0f339959 260 } else {
ea207ed3 261
90f54407 262 dbus_interface_exported = TRUE;
263 g_printf("exported %s's interface on the system bus...\n", name);
264 }
0f339959 265}
1e8c7c88 266
0f339959 267static void hostnamed_on_name_acquired(GDBusConnection *conn,
90f54407 268 const gchar *name,
0f339959 269 gpointer user_data) {
270
271 g_printf("success!\n");
1e8c7c88 272}
273
5b005882 274static void hostnamed_on_name_lost(GDBusConnection *conn,
1be94ede 275 const gchar *name,
276 gpointer user_data) {
80043b36 277
90f54407 278 if(!conn) {
0f339959 279
90f54407 280 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);
281 hostnamed_mem_clean();
282 }
0f339959 283
509599f0 284 g_printf("lost name %s, exiting...\n", name);
fd8852d9 285
1cd5e6fe 286 hostnamed_mem_clean();
0f339959 287}
288
289/* --- end bus/name handlers, begin misc unix functions --- */
290
291/* safe call to clean and then exit
509599f0 292 * this stops our GMainLoop safely before letting main() return */
0f339959 293void hostnamed_mem_clean() {
294
90f54407 295 g_printf("exiting...\n");
0f339959 296
90f54407 297 if(dbus_interface_exported)
298 g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(hostnamed_interf));
0f339959 299
90f54407 300 if(g_main_loop_is_running(hostnamed_loop))
301 g_main_loop_quit(hostnamed_loop);
fd8852d9 302
ea207ed3 303}
304
0f339959 305/* wrapper for glib's unix signal handling; called only once if terminatating signal is raised against us */
306gboolean unix_sig_terminate_handler(gpointer data) {
c62bceb7 307
90f54407 308 g_printf("caught SIGINT/HUP/TERM, exiting\n");
0f339959 309
90f54407 310 hostnamed_mem_clean();
311 return G_SOURCE_REMOVE;
0f339959 312}
313
314void set_signal_handlers() {
315
90f54407 316 /* we don't care about its descriptor, we never need to unregister these */
317 g_unix_signal_add(SIGINT, unix_sig_terminate_handler, NULL);
318 g_unix_signal_add(SIGHUP, unix_sig_terminate_handler, NULL);
319 g_unix_signal_add(SIGTERM, unix_sig_terminate_handler, NULL);
04cc16f2 320
90f54407 321 /* TODO: the "only once" guarantee only counts towards specific signals.
322 * make sure calling a SIGINT and SIGHUP doesn't cause term_handler()
323 * to be called twice */
0f339959 324}
325
326int main() {
90f54407 327
328 set_signal_handlers();
387173cb 329
90f54407 330 hostnamed_loop = g_main_loop_new(NULL, TRUE);
331 hostnamed_freeable = g_ptr_array_new();
fd8852d9 332
90f54407 333 bus_descriptor = g_bus_own_name(G_BUS_TYPE_SYSTEM,
0f339959 334 "org.freedesktop.hostname1",
335 G_BUS_NAME_OWNER_FLAGS_NONE,
336 hostnamed_on_bus_acquired,
337 hostnamed_on_name_acquired,
338 hostnamed_on_name_lost,
339 NULL,
340 NULL);
496f5d66 341
90f54407 342 g_main_loop_run(hostnamed_loop);
343 /* runs until single g_main_loop_quit() call is raised inside <interface>_mem_clean() */
344 g_main_loop_unref(hostnamed_loop);
1e8c7c88 345
90f54407 346 /* guaranteed unownable */
347 g_bus_unown_name(bus_descriptor);
c62bceb7 348
90f54407 349 /* at this point no operations can occur with our data, it is safe to free it + its container */
350 g_ptr_array_free(hostnamed_freeable, TRUE);
7323a4e4 351
90f54407 352 return 0;
a35a69c5 353}
354
f1ad9351 355gboolean test_against_known_vm_strings(gchar *sysctl_string) {
356
357 unsigned int i;
358
359 if(is_vm)
360 return TRUE;
361
362 for(; i < G_N_ELEMENTS(vmstring_list); i++)
363 if(strcasestr(sysctl_string, vmstring_list[i]))
364 return (is_vm = TRUE) ? TRUE : FALSE;
365
366 return FALSE;
367}
368
a6f11205 369/* TODO figure out DMI variables on obsd */
a35a69c5 370/*static gchar *guess_icon_name() {
371
372 gchar *filebuf = NULL;
373 gchar *ret = NULL;
374
1cd5e6fe 375 #if defined(__i386__) || defined(__x86_64__)
39df6847 376
a35a69c5 377 Taken with a few minor changes from systemd's hostnamed.c,
378 copyright 2011 Lennart Poettering.
379
380 See the SMBIOS Specification 2.7.1 section 7.4.1 for
381 details about the values listed here:
382
383 http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
39df6847 384
a35a69c5 385
386 if (g_file_get_contents ("/sys/class/dmi/id/chassis_type", &filebuf, NULL, NULL)) {
387 switch (g_ascii_strtoull (filebuf, NULL, 10)) {
388 case 0x3:
389 case 0x4:
390 case 0x5:
391 case 0x6:
392 case 0x7:
393 ret = g_strdup ("computer-desktop");
394 goto out;
395 case 0x9:
396 case 0xA:
397 case 0xE:
398 ret = g_strdup ("computer-laptop");
399 goto out;
400 case 0x11:
401 case 0x17:
402 case 0x1C:
403 case 0x1D:
404 ret = g_strdup ("computer-server");
405 goto out;
406 }
407 }
1cd5e6fe 408 #endif
a35a69c5 409 ret = g_strdup ("computer");
410 out:
411 g_free (filebuf);
412 return ret;
413}*/
1e8c7c88 414