confd: emit "changed" signal
[ladish.git] / conf.c
blob5af7adc0b90223ffc903ec7a42d1d5a1c050a7c3
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2010 Nedko Arnaudov <nedko@arnaudov.name>
7 **************************************************************************
8 * This file contains implementation of the settings storage
9 **************************************************************************
11 * LADI Session Handler is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * LADI Session Handler is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with LADI Session Handler. If not, see <http://www.gnu.org/licenses/>
23 * or write to the Free Software Foundation, Inc.,
24 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "common.h"
29 #include <unistd.h>
30 #include <signal.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <errno.h>
36 #include "dbus/helpers.h"
37 #include "dbus/error.h"
38 #include "dbus_constants.h"
39 #include "common/catdup.h"
40 #include "common/dirhelpers.h"
42 #define STORAGE_BASE_DIR "/.ladish/conf/"
44 extern const struct dbus_interface_descriptor g_interface;
46 static const char * g_dbus_unique_name;
47 static dbus_object_path g_object;
48 static bool g_quit;
50 struct pair
52 struct list_head siblings;
53 uint64_t version;
54 char * key;
55 char * value;
56 bool stored;
59 struct list_head g_pairs;
61 static bool connect_dbus(void)
63 int ret;
65 dbus_error_init(&g_dbus_error);
67 g_dbus_connection = dbus_bus_get(DBUS_BUS_SESSION, &g_dbus_error);
68 if (dbus_error_is_set(&g_dbus_error))
70 log_error("Failed to get bus: %s", g_dbus_error.message);
71 dbus_error_free(&g_dbus_error);
72 goto fail;
75 g_dbus_unique_name = dbus_bus_get_unique_name(g_dbus_connection);
76 if (g_dbus_unique_name == NULL)
78 log_error("Failed to read unique bus name");
79 goto unref_connection;
82 log_info("Connected to local session bus, unique name is \"%s\"", g_dbus_unique_name);
84 ret = dbus_bus_request_name(g_dbus_connection, CONF_SERVICE_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE, &g_dbus_error);
85 if (ret == -1)
87 log_error("Failed to acquire bus name: %s", g_dbus_error.message);
88 dbus_error_free(&g_dbus_error);
89 goto unref_connection;
92 if (ret == DBUS_REQUEST_NAME_REPLY_EXISTS)
94 log_error("Requested connection name already exists");
95 goto unref_connection;
98 g_object = dbus_object_path_new(CONF_OBJECT_PATH, &g_interface, NULL, NULL);
99 if (g_object == NULL)
101 goto unref_connection;
104 if (!dbus_object_path_register(g_dbus_connection, g_object))
106 goto destroy_control_object;
109 return true;
111 destroy_control_object:
112 dbus_object_path_destroy(g_dbus_connection, g_object);
113 unref_connection:
114 dbus_connection_unref(g_dbus_connection);
116 fail:
117 return false;
120 static void disconnect_dbus(void)
122 dbus_object_path_destroy(g_dbus_connection, g_object);
123 dbus_connection_unref(g_dbus_connection);
126 void term_signal_handler(int signum)
128 log_info("Caught signal %d (%s), terminating", signum, strsignal(signum));
129 g_quit = true;
132 bool install_term_signal_handler(int signum, bool ignore_if_already_ignored)
134 sig_t sigh;
136 sigh = signal(signum, term_signal_handler);
137 if (sigh == SIG_ERR)
139 log_error("signal() failed to install handler function for signal %d.", signum);
140 return false;
143 if (sigh == SIG_IGN && ignore_if_already_ignored)
145 signal(SIGTERM, SIG_IGN);
148 return true;
151 int main(int argc, char ** argv)
153 int ret;
155 if (getenv("HOME") == NULL)
157 log_error("Environment variable HOME not set");
158 return 1;
161 INIT_LIST_HEAD(&g_pairs);
163 install_term_signal_handler(SIGTERM, false);
164 install_term_signal_handler(SIGINT, true);
166 if (!connect_dbus())
168 log_error("Failed to connect to D-Bus");
169 return 1;
172 while (!g_quit)
174 dbus_connection_read_write_dispatch(g_dbus_connection, 50);
177 ret = 0;
179 disconnect_dbus();
180 return 0;
183 static struct pair * create_pair(const char * key, const char * value)
185 struct pair * pair_ptr;
187 pair_ptr = malloc(sizeof(struct pair));
188 if (pair_ptr == NULL)
190 log_error("malloc() failed to allocate memory for pair struct");
191 return NULL;
194 pair_ptr->key = strdup(key);
195 if (pair_ptr->key == NULL)
197 log_error("strdup(\"%s\") failed for key", key);
198 free(pair_ptr);
199 return NULL;
202 if (value != NULL)
204 pair_ptr->value = strdup(value);
205 if (pair_ptr->value == NULL)
207 log_error("strdup(\"%s\") failed for value", value);
208 free(pair_ptr->key);
209 free(pair_ptr);
210 return NULL;
213 else
215 /* Caller will fill this shortly */
216 pair_ptr->value = NULL;
219 pair_ptr->version = 1;
220 pair_ptr->stored = false;
222 list_add_tail(&pair_ptr->siblings, &g_pairs);
224 return pair_ptr;
227 static bool store_pair(struct pair * pair_ptr)
229 char * dirpath;
230 char * filepath;
231 int fd;
232 size_t len;
233 ssize_t written;
235 dirpath = catdupv(getenv("HOME"), STORAGE_BASE_DIR, pair_ptr->key, NULL);
236 if (dirpath == NULL)
238 return false;
241 if (!ensure_dir_exist(dirpath, 0700))
243 free(dirpath);
244 return false;
247 filepath = catdup(dirpath, "/value");
248 free(dirpath);
249 if (filepath == NULL)
251 return false;
254 fd = creat(filepath, 0700);
255 if (fd == -1)
257 log_error("Failed to create \"%s\": %d (%s)", filepath, errno, strerror(errno));
258 free(filepath);
259 return false;
262 len = strlen(pair_ptr->value);
264 written = write(fd, pair_ptr->value, len);
265 if (written < 0)
267 log_error("Failed to write() to \"%s\": %d (%s)", filepath, errno, strerror(errno));
268 free(filepath);
269 return false;
272 if ((size_t)written != len)
274 log_error("write() to \"%s\" returned %zd instead of %zu", filepath, written, len);
275 free(filepath);
276 return false;
279 close(fd);
280 free(filepath);
282 pair_ptr->stored = true;
284 return true;
287 static struct pair * load_pair(const char * key)
289 struct pair * pair_ptr;
290 char * path;
291 struct stat st;
292 int fd;
293 char * buffer;
294 ssize_t bytes_read;
296 path = catdupv(getenv("HOME"), STORAGE_BASE_DIR, key, "/value", NULL);
297 if (path == NULL)
299 return false;
302 if (stat(path, &st) != 0)
304 log_error("Failed to stat \"%s\": %d (%s)", path, errno, strerror(errno));
305 free(path);
306 return false;
309 if (!S_ISREG(st.st_mode))
311 log_error("\"%s\" is not a regular file.", path);
312 free(path);
313 return false;
316 fd = open(path, O_RDONLY);
317 if (fd == -1)
319 log_error("Failed to open \"%s\": %d (%s)", path, errno, strerror(errno));
320 free(path);
321 return false;
324 buffer = malloc((size_t)st.st_size + 1);
325 if (buffer == NULL)
327 log_error("malloc() failed to allocate %zu bytes of memory for value", (size_t)st.st_size + 1);
328 close(fd);
329 free(path);
330 return false;
333 bytes_read = read(fd, buffer, st.st_size);
334 if (bytes_read < 0)
336 log_error("Failed to read() from \"%s\": %d (%s)", path, errno, strerror(errno));
337 free(buffer);
338 close(fd);
339 free(path);
340 return false;
343 if (bytes_read != st.st_size)
345 log_error("read() from \"%s\" returned %zd instead of %llu", path, bytes_read, (unsigned long long)st.st_size);
346 free(buffer);
347 close(fd);
348 free(path);
349 return false;
352 buffer[st.st_size] = 0;
354 pair_ptr = create_pair(key, NULL);
355 if (pair_ptr == NULL)
357 free(buffer);
358 close(fd);
359 free(path);
360 return false;
363 pair_ptr->value = buffer;
365 close(fd);
366 free(path);
368 return pair_ptr;
371 static struct pair * find_pair(const char * key)
373 struct list_head * node_ptr;
374 struct pair * pair_ptr;
376 list_for_each(node_ptr, &g_pairs)
378 pair_ptr = list_entry(node_ptr, struct pair, siblings);
379 if (strcmp(pair_ptr->key, key) == 0)
381 return pair_ptr;
385 return NULL;
388 /***************************************************************************/
389 /* D-Bus interface implementation */
391 static void conf_set(struct dbus_method_call * call_ptr)
393 const char * key;
394 const char * value;
395 struct pair * pair_ptr;
396 char * buffer;
397 bool store;
399 if (!dbus_message_get_args(
400 call_ptr->message,
401 &g_dbus_error,
402 DBUS_TYPE_STRING, &key,
403 DBUS_TYPE_STRING, &value,
404 DBUS_TYPE_INVALID))
406 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
407 dbus_error_free(&g_dbus_error);
408 return;
411 log_info("set '%s' <- '%s'", key, value);
413 pair_ptr = find_pair(key);
414 if (pair_ptr == NULL)
416 pair_ptr = create_pair(key, value);
417 if (pair_ptr == NULL)
419 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Memory allocation failed");
420 return;
423 store = true;
425 else
427 store = strcmp(pair_ptr->value, value) != 0;
428 if (store)
430 buffer = strdup(value);
431 if (buffer == NULL)
433 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Memory allocation failed. strdup(\"%s\") failed for value", value);
434 return;
436 free(pair_ptr->value);
437 pair_ptr->value = buffer;
438 pair_ptr->version++;
439 pair_ptr->stored = false; /* mark that new value was not stored on disk yet */
441 dbus_signal_emit(
442 g_dbus_connection,
443 CONF_OBJECT_PATH,
444 CONF_IFACE,
445 "changed",
446 "sst",
447 &pair_ptr->key,
448 &pair_ptr->value,
449 &pair_ptr->version);
451 else if (!pair_ptr->stored) /* if store to disk failed last time, retry */
453 store = true;
457 if (store)
459 if (!store_pair(pair_ptr))
461 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Storing the value of key '%s' to disk failed", pair_ptr->key);
462 return;
466 method_return_new_single(call_ptr, DBUS_TYPE_UINT64, &pair_ptr->version);
469 static void conf_get(struct dbus_method_call * call_ptr)
471 const char * key;
472 struct pair * pair_ptr;
474 if (!dbus_message_get_args(
475 call_ptr->message,
476 &g_dbus_error,
477 DBUS_TYPE_STRING, &key,
478 DBUS_TYPE_INVALID))
480 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
481 dbus_error_free(&g_dbus_error);
482 return;
485 pair_ptr = find_pair(key);
486 if (pair_ptr == NULL)
488 pair_ptr = load_pair(key);
489 if (pair_ptr == NULL)
491 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_KEY_NOT_FOUND, "Key '%s' not found", key);
492 return;
496 log_info("get '%s' -> '%s'", key, pair_ptr->value);
498 method_return_new_valist(
499 call_ptr,
500 DBUS_TYPE_STRING, &pair_ptr->value,
501 DBUS_TYPE_UINT64, &pair_ptr->version,
502 DBUS_TYPE_INVALID);
505 static void conf_exit(struct dbus_method_call * call_ptr)
507 log_info("Exit command received through D-Bus");
508 g_quit = true;
509 method_return_new_void(call_ptr);
512 METHOD_ARGS_BEGIN(set, "Set conf value")
513 METHOD_ARG_DESCRIBE_IN("key", DBUS_TYPE_STRING_AS_STRING, "")
514 METHOD_ARG_DESCRIBE_IN("value", DBUS_TYPE_STRING_AS_STRING, "")
515 METHOD_ARG_DESCRIBE_OUT("version", DBUS_TYPE_UINT64_AS_STRING, "")
516 METHOD_ARGS_END
518 METHOD_ARGS_BEGIN(get, "Get conf value")
519 METHOD_ARG_DESCRIBE_IN("key", DBUS_TYPE_STRING_AS_STRING, "")
520 METHOD_ARG_DESCRIBE_OUT("value", DBUS_TYPE_STRING_AS_STRING, "")
521 METHOD_ARG_DESCRIBE_OUT("version", DBUS_TYPE_UINT64_AS_STRING, "")
522 METHOD_ARGS_END
524 METHOD_ARGS_BEGIN(exit, "Tell conf D-Bus service to exit")
525 METHOD_ARGS_END
527 METHODS_BEGIN
528 METHOD_DESCRIBE(set, conf_set)
529 METHOD_DESCRIBE(get, conf_get)
530 METHOD_DESCRIBE(exit, conf_exit)
531 METHODS_END
533 SIGNAL_ARGS_BEGIN(changed, "")
534 SIGNAL_ARG_DESCRIBE("key", DBUS_TYPE_STRING_AS_STRING, "")
535 SIGNAL_ARG_DESCRIBE("value", DBUS_TYPE_STRING_AS_STRING, "")
536 SIGNAL_ARG_DESCRIBE("version", DBUS_TYPE_UINT64_AS_STRING, "")
537 SIGNAL_ARGS_END
539 SIGNALS_BEGIN
540 SIGNAL_DESCRIBE(changed)
541 SIGNALS_END
543 INTERFACE_BEGIN(g_interface, CONF_IFACE)
544 INTERFACE_DEFAULT_HANDLER
545 INTERFACE_EXPOSE_METHODS
546 INTERFACE_EXPOSE_SIGNALS
547 INTERFACE_END