add WIP bit of chassis detection for hostnamed
[systembsd.git] / src / interfaces / hostnamed / hostnamed.c
CommitLineData
baa40e28 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
71e3eef1 17#include <unistd.h>
18#include <limits.h>
3eb1ef91 19#include <signal.h>
ed99526e 20
043bd2ed 21#include <sys/param.h>
962fad6d 22#include <sys/sysctl.h>
8f3d38b1 23#include <string.h>
043bd2ed 24
cd16a588 25#include <glib/gprintf.h>
ba9914b6 26#include <glib-unix.h>
cd16a588 27
3c3794ac 28#include "hostnamed-gen.h"
3eb1ef91 29#include "hostnamed.h"
71e3eef1 30
ed99526e 31GPtrArray *hostnamed_freeable;
9d2500b0 32Hostname1 *hostnamed_interf;
7ce16a35 33
3eb1ef91 34GMainLoop *hostnamed_loop;
35
36guint bus_descriptor;
37gboolean dbus_interface_exported; /* reliable because of gdbus operational guarantees */
38
ff036f75 39/* --- begin method/property/dbus signal code --- */
355f60ae 40
962fad6d 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
7ce16a35 51static gboolean
9d2500b0 52on_handle_set_hostname(Hostname1 *hn1_passed_interf,
7ce16a35 53 GDBusMethodInvocation *invoc,
54 const gchar *greet,
55 gpointer data) {
56 return FALSE;
57}
58
59static gboolean
9d2500b0 60on_handle_set_static_hostname(Hostname1 *hn1_passed_interf,
7ce16a35 61 GDBusMethodInvocation *invoc,
62 const gchar *greet,
63 gpointer data) {
64 return FALSE;
65}
66
67static gboolean
9d2500b0 68on_handle_set_pretty_hostname(Hostname1 *hn1_passed_interf,
7ce16a35 69 GDBusMethodInvocation *invoc,
70 const gchar *greet,
71 gpointer data) {
72 return FALSE;
73}
74
75static gboolean
9d2500b0 76on_handle_set_chassis(Hostname1 *hn1_passed_interf,
7ce16a35 77 GDBusMethodInvocation *invoc,
78 const gchar *greet,
79 gpointer data) {
80 return FALSE;
81}
82
83static gboolean
9d2500b0 84on_handle_set_icon_name(Hostname1 *hn1_passed_interf,
7ce16a35 85 GDBusMethodInvocation *invoc,
86 const gchar *greet,
87 gpointer data) {
88 return FALSE;
89}
90
355f60ae 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
99ef55dd 100 gchar *hostname_buf, *ret;
101 size_t hostname_divider;
043bd2ed 102
8e18351c 103 hostname_buf = (gchar*) g_malloc0(MAXHOSTNAMELEN);
99ef55dd 104 ret = (gchar*) g_malloc0(MAXHOSTNAMELEN);
8e18351c 105
99ef55dd 106 g_ptr_array_add(hostnamed_freeable, hostname_buf);
107 g_ptr_array_add(hostnamed_freeable, ret);
af9f8b50 108
8e18351c 109 if(gethostname(hostname_buf, MAXHOSTNAMELEN) || g_strcmp0(hostname_buf, "") == 0)
110 return "localhost";
043bd2ed 111
99ef55dd 112 hostname_divider = strcspn(hostname_buf, ".");
8f3d38b1 113
99ef55dd 114 return strncpy(ret, hostname_buf, hostname_divider);
355f60ae 115}
116
117const gchar *
118our_get_static_hostname() {
119
82a6a0d8 120 const gchar *pretty_hostname;
121 const gchar *ret;
8e18351c 122
123 pretty_hostname = our_get_pretty_hostname();
124
125 if(g_strcmp0(pretty_hostname, "") == 0)
126 ret = our_get_hostname();
127
82a6a0d8 128 else if((ret = g_hostname_to_ascii(pretty_hostname))) {
8e18351c 129
82a6a0d8 130 g_ptr_array_add(hostnamed_freeable, (gpointer)ret);
8e18351c 131 return ret;
132 }
133
134 return ret;
355f60ae 135}
136
137const gchar *
138our_get_pretty_hostname() {
139
8e18351c 140 GKeyFile *config;
141 gchar *ret;
142
962fad6d 143 config = g_key_file_new();
144
8e18351c 145 if(g_key_file_load_from_file(config, "/etc/systemd_compat.conf", G_KEY_FILE_NONE, NULL)
82a6a0d8 146 && (ret = g_key_file_get_value(config, "hostnamed", "PrettyHostname", NULL))) { /* ret might need to be freed, docs dont specify but i am suspicious */
8e18351c 147
962fad6d 148 g_key_file_unref(config);
8e18351c 149 return ret;
150 }
151
152 if(config)
153 g_free(config);
154
155 return "";
355f60ae 156}
157
158const gchar *
159our_get_chassis() {
160
962fad6d 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";
355f60ae 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
ff036f75 223/* --- end method/property/dbus signal code, begin bus/name handlers --- */
0df0018d 224
828caf9a 225static void hostnamed_on_bus_acquired(GDBusConnection *conn,
fbc0295f 226 const gchar *name,
227 gpointer user_data) {
11475670 228
3eb1ef91 229 g_printf("got bus/name, exporting %s's interface...\n", name);
11475670 230
99ef55dd 231 hostnamed_interf = hostname1_skeleton_new();
7ce16a35 232
355f60ae 233 /* attach function pointers to generated struct's method handlers */
7ce16a35 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
355f60ae 240 /* set our properties before export */
9d2500b0 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());
355f60ae 251
7ce16a35 252 if(!g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(hostnamed_interf),
9d2500b0 253 conn,
254 "/org/freedesktop/hostname1",
255 NULL)) {
7ce16a35 256
3eb1ef91 257 g_printf("failed to export %s's interface!\n", name); /* unusual edge case, TODO check errno */
99ef55dd 258 hostnamed_mem_clean();
7ce16a35 259
3eb1ef91 260 } else {
ea207ed3 261
99ef55dd 262 dbus_interface_exported = TRUE;
263 g_printf("exported %s's interface on the system bus...\n", name);
264 }
3eb1ef91 265}
3c3794ac 266
3eb1ef91 267static void hostnamed_on_name_acquired(GDBusConnection *conn,
99ef55dd 268 const gchar *name,
3eb1ef91 269 gpointer user_data) {
270
271 g_printf("success!\n");
3c3794ac 272}
273
828caf9a 274static void hostnamed_on_name_lost(GDBusConnection *conn,
9d2500b0 275 const gchar *name,
276 gpointer user_data) {
80043b36 277
99ef55dd 278 if(!conn) {
3eb1ef91 279
99ef55dd 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 }
3eb1ef91 283
ca5a7f9f 284 g_printf("lost name %s, exiting...\n", name);
ed99526e 285
fbc0295f 286 hostnamed_mem_clean();
3eb1ef91 287}
288
289/* --- end bus/name handlers, begin misc unix functions --- */
290
291/* safe call to clean and then exit
ca5a7f9f 292 * this stops our GMainLoop safely before letting main() return */
3eb1ef91 293void hostnamed_mem_clean() {
294
99ef55dd 295 g_printf("exiting...\n");
3eb1ef91 296
99ef55dd 297 if(dbus_interface_exported)
298 g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(hostnamed_interf));
3eb1ef91 299
99ef55dd 300 if(g_main_loop_is_running(hostnamed_loop))
301 g_main_loop_quit(hostnamed_loop);
ed99526e 302
ea207ed3 303}
304
3eb1ef91 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) {
a3041e78 307
99ef55dd 308 g_printf("caught SIGINT/HUP/TERM, exiting\n");
3eb1ef91 309
99ef55dd 310 hostnamed_mem_clean();
311 return G_SOURCE_REMOVE;
3eb1ef91 312}
313
314void set_signal_handlers() {
315
99ef55dd 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);
5c2460ab 320
99ef55dd 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 */
3eb1ef91 324}
325
326int main() {
99ef55dd 327
328 set_signal_handlers();
387173cb 329
99ef55dd 330 hostnamed_loop = g_main_loop_new(NULL, TRUE);
331 hostnamed_freeable = g_ptr_array_new();
ed99526e 332
99ef55dd 333 bus_descriptor = g_bus_own_name(G_BUS_TYPE_SYSTEM,
3eb1ef91 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
99ef55dd 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);
3c3794ac 345
99ef55dd 346 /* guaranteed unownable */
347 g_bus_unown_name(bus_descriptor);
a3041e78 348
99ef55dd 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);
af9f8b50 351
99ef55dd 352 return 0;
71e3eef1 353}
354
962fad6d 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
6981850a 369/* TODO figure out DMI variables on obsd */
71e3eef1 370/*static gchar *guess_icon_name() {
371
372 gchar *filebuf = NULL;
373 gchar *ret = NULL;
374
fbc0295f 375 #if defined(__i386__) || defined(__x86_64__)
c3b84b0a 376
71e3eef1 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
c3b84b0a 384
71e3eef1 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 }
fbc0295f 408 #endif
71e3eef1 409 ret = g_strdup ("computer");
410 out:
411 g_free (filebuf);
412 return ret;
413}*/
3c3794ac 414