Merge branch 'stable' into 'main'
[ladish.git] / conf.c
blobb27d41b2fed5e9f2a3e0192a45890896c1b73a1a
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2010,2011,2012 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 <cdbus/cdbus.h>
37 #include "dbus_constants.h"
38 #include "common/catdup.h"
39 #include "common/dirhelpers.h"
41 #define STORAGE_BASE_DIR "/.ladish/conf/"
43 extern const struct cdbus_interface_descriptor g_interface;
45 static const char * g_dbus_unique_name;
46 static cdbus_object_path g_object;
47 static bool g_quit;
49 struct pair
51 struct list_head siblings;
52 uint64_t version;
53 char * key;
54 char * value;
55 bool stored;
58 struct list_head g_pairs;
60 static bool connect_dbus(void)
62 int ret;
64 dbus_error_init(&cdbus_g_dbus_error);
66 cdbus_g_dbus_connection = dbus_bus_get(DBUS_BUS_SESSION, &cdbus_g_dbus_error);
67 if (dbus_error_is_set(&cdbus_g_dbus_error))
69 log_error("Failed to get bus: %s", cdbus_g_dbus_error.message);
70 dbus_error_free(&cdbus_g_dbus_error);
71 goto fail;
74 g_dbus_unique_name = dbus_bus_get_unique_name(cdbus_g_dbus_connection);
75 if (g_dbus_unique_name == NULL)
77 log_error("Failed to read unique bus name");
78 goto unref_connection;
81 log_info("Connected to local session bus, unique name is \"%s\"", g_dbus_unique_name);
83 ret = dbus_bus_request_name(cdbus_g_dbus_connection, CONF_SERVICE_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE, &cdbus_g_dbus_error);
84 if (ret == -1)
86 log_error("Failed to acquire bus name: %s", cdbus_g_dbus_error.message);
87 dbus_error_free(&cdbus_g_dbus_error);
88 goto unref_connection;
91 if (ret == DBUS_REQUEST_NAME_REPLY_EXISTS)
93 log_error("Requested connection name already exists");
94 goto unref_connection;
97 g_object = cdbus_object_path_new(CONF_OBJECT_PATH, &g_interface, NULL, NULL);
98 if (g_object == NULL)
100 goto unref_connection;
103 if (!cdbus_object_path_register(cdbus_g_dbus_connection, g_object))
105 goto destroy_control_object;
108 return true;
110 destroy_control_object:
111 cdbus_object_path_destroy(cdbus_g_dbus_connection, g_object);
112 unref_connection:
113 dbus_connection_unref(cdbus_g_dbus_connection);
115 fail:
116 return false;
119 static void disconnect_dbus(void)
121 cdbus_object_path_destroy(cdbus_g_dbus_connection, g_object);
122 dbus_connection_unref(cdbus_g_dbus_connection);
125 void term_signal_handler(int signum)
127 log_info("Caught signal %d (%s), terminating", signum, strsignal(signum));
128 g_quit = true;
131 bool install_term_signal_handler(int signum, bool ignore_if_already_ignored)
133 sig_t sigh;
135 sigh = signal(signum, term_signal_handler);
136 if (sigh == SIG_ERR)
138 log_error("signal() failed to install handler function for signal %d.", signum);
139 return false;
142 if (sigh == SIG_IGN && ignore_if_already_ignored)
144 signal(SIGTERM, SIG_IGN);
147 return true;
150 int main(int UNUSED(argc), char ** UNUSED(argv))
152 if (getenv("HOME") == NULL)
154 log_error("Environment variable HOME not set");
155 return 1;
158 INIT_LIST_HEAD(&g_pairs);
160 install_term_signal_handler(SIGTERM, false);
161 install_term_signal_handler(SIGINT, true);
163 if (!connect_dbus())
165 log_error("Failed to connect to D-Bus");
166 return 1;
169 while (!g_quit)
171 dbus_connection_read_write_dispatch(cdbus_g_dbus_connection, 50);
174 disconnect_dbus();
175 return 0;
178 static struct pair * create_pair(const char * key, const char * value)
180 struct pair * pair_ptr;
182 pair_ptr = malloc(sizeof(struct pair));
183 if (pair_ptr == NULL)
185 log_error("malloc() failed to allocate memory for pair struct");
186 return NULL;
189 pair_ptr->key = strdup(key);
190 if (pair_ptr->key == NULL)
192 log_error("strdup(\"%s\") failed for key", key);
193 free(pair_ptr);
194 return NULL;
197 if (value != NULL)
199 pair_ptr->value = strdup(value);
200 if (pair_ptr->value == NULL)
202 log_error("strdup(\"%s\") failed for value", value);
203 free(pair_ptr->key);
204 free(pair_ptr);
205 return NULL;
208 else
210 /* Caller will fill this shortly */
211 pair_ptr->value = NULL;
214 pair_ptr->version = 1;
215 pair_ptr->stored = false;
217 list_add_tail(&pair_ptr->siblings, &g_pairs);
219 return pair_ptr;
222 static bool store_pair(struct pair * pair_ptr)
224 char * dirpath;
225 char * filepath;
226 int fd;
227 size_t len;
228 ssize_t written;
230 dirpath = catdupv(getenv("HOME"), STORAGE_BASE_DIR, pair_ptr->key, NULL);
231 if (dirpath == NULL)
233 return false;
236 if (!ensure_dir_exist(dirpath, 0700))
238 free(dirpath);
239 return false;
242 filepath = catdup(dirpath, "/value");
243 free(dirpath);
244 if (filepath == NULL)
246 return false;
249 fd = creat(filepath, 0700);
250 if (fd == -1)
252 log_error("Failed to create \"%s\": %d (%s)", filepath, errno, strerror(errno));
253 free(filepath);
254 return false;
257 len = strlen(pair_ptr->value);
259 written = write(fd, pair_ptr->value, len);
260 if (written < 0)
262 log_error("Failed to write() to \"%s\": %d (%s)", filepath, errno, strerror(errno));
263 free(filepath);
264 return false;
267 if ((size_t)written != len)
269 log_error("write() to \"%s\" returned %zd instead of %zu", filepath, written, len);
270 free(filepath);
271 return false;
274 close(fd);
275 free(filepath);
277 pair_ptr->stored = true;
279 return true;
282 static struct pair * load_pair(const char * key)
284 struct pair * pair_ptr;
285 char * path;
286 struct stat st;
287 int fd;
288 char * buffer;
289 ssize_t bytes_read;
291 path = catdupv(getenv("HOME"), STORAGE_BASE_DIR, key, "/value", NULL);
292 if (path == NULL)
294 return false;
297 if (stat(path, &st) != 0)
299 log_error("Failed to stat \"%s\": %d (%s)", path, errno, strerror(errno));
300 free(path);
301 return false;
304 if (!S_ISREG(st.st_mode))
306 log_error("\"%s\" is not a regular file.", path);
307 free(path);
308 return false;
311 fd = open(path, O_RDONLY);
312 if (fd == -1)
314 log_error("Failed to open \"%s\": %d (%s)", path, errno, strerror(errno));
315 free(path);
316 return false;
319 buffer = malloc((size_t)st.st_size + 1);
320 if (buffer == NULL)
322 log_error("malloc() failed to allocate %zu bytes of memory for value", (size_t)st.st_size + 1);
323 close(fd);
324 free(path);
325 return false;
328 bytes_read = read(fd, buffer, st.st_size);
329 if (bytes_read < 0)
331 log_error("Failed to read() from \"%s\": %d (%s)", path, errno, strerror(errno));
332 free(buffer);
333 close(fd);
334 free(path);
335 return false;
338 if (bytes_read != st.st_size)
340 log_error("read() from \"%s\" returned %zd instead of %llu", path, bytes_read, (unsigned long long)st.st_size);
341 free(buffer);
342 close(fd);
343 free(path);
344 return false;
347 buffer[st.st_size] = 0;
349 pair_ptr = create_pair(key, NULL);
350 if (pair_ptr == NULL)
352 free(buffer);
353 close(fd);
354 free(path);
355 return false;
358 pair_ptr->value = buffer;
360 close(fd);
361 free(path);
363 return pair_ptr;
366 static struct pair * find_pair(const char * key)
368 struct list_head * node_ptr;
369 struct pair * pair_ptr;
371 list_for_each(node_ptr, &g_pairs)
373 pair_ptr = list_entry(node_ptr, struct pair, siblings);
374 if (strcmp(pair_ptr->key, key) == 0)
376 return pair_ptr;
380 return NULL;
383 static void emit_changed(struct pair * pair_ptr)
385 cdbus_signal_emit(
386 cdbus_g_dbus_connection,
387 CONF_OBJECT_PATH,
388 CONF_IFACE,
389 "changed",
390 "sst",
391 &pair_ptr->key,
392 &pair_ptr->value,
393 &pair_ptr->version);
396 /***************************************************************************/
397 /* D-Bus interface implementation */
399 static void conf_set(struct cdbus_method_call * call_ptr)
401 const char * key;
402 const char * value;
403 struct pair * pair_ptr;
404 char * buffer;
405 bool store;
407 if (!dbus_message_get_args(
408 call_ptr->message,
409 &cdbus_g_dbus_error,
410 DBUS_TYPE_STRING, &key,
411 DBUS_TYPE_STRING, &value,
412 DBUS_TYPE_INVALID))
414 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message);
415 dbus_error_free(&cdbus_g_dbus_error);
416 return;
419 log_info("set '%s' <- '%s'", key, value);
421 pair_ptr = find_pair(key);
422 if (pair_ptr == NULL)
424 pair_ptr = create_pair(key, value);
425 if (pair_ptr == NULL)
427 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "Memory allocation failed");
428 return;
431 emit_changed(pair_ptr);
433 store = true;
435 else
437 store = strcmp(pair_ptr->value, value) != 0;
438 if (store)
440 buffer = strdup(value);
441 if (buffer == NULL)
443 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "Memory allocation failed. strdup(\"%s\") failed for value", value);
444 return;
446 free(pair_ptr->value);
447 pair_ptr->value = buffer;
448 pair_ptr->version++;
449 pair_ptr->stored = false; /* mark that new value was not stored on disk yet */
451 emit_changed(pair_ptr);
453 else if (!pair_ptr->stored) /* if store to disk failed last time, retry */
455 store = true;
459 if (store)
461 if (!store_pair(pair_ptr))
463 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "Storing the value of key '%s' to disk failed", pair_ptr->key);
464 return;
468 cdbus_method_return_new_single(call_ptr, DBUS_TYPE_UINT64, &pair_ptr->version);
471 static void conf_get(struct cdbus_method_call * call_ptr)
473 const char * key;
474 struct pair * pair_ptr;
476 if (!dbus_message_get_args(
477 call_ptr->message,
478 &cdbus_g_dbus_error,
479 DBUS_TYPE_STRING, &key,
480 DBUS_TYPE_INVALID))
482 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message);
483 dbus_error_free(&cdbus_g_dbus_error);
484 return;
487 pair_ptr = find_pair(key);
488 if (pair_ptr == NULL)
490 pair_ptr = load_pair(key);
491 if (pair_ptr == NULL)
493 cdbus_error(call_ptr, LADISH_DBUS_ERROR_KEY_NOT_FOUND, "Key '%s' not found", key);
494 return;
498 log_info("get '%s' -> '%s'", key, pair_ptr->value);
500 cdbus_method_return_new_valist(
501 call_ptr,
502 DBUS_TYPE_STRING, &pair_ptr->value,
503 DBUS_TYPE_UINT64, &pair_ptr->version,
504 DBUS_TYPE_INVALID);
507 static void conf_exit(struct cdbus_method_call * call_ptr)
509 log_info("Exit command received through D-Bus");
510 g_quit = true;
511 cdbus_method_return_new_void(call_ptr);
514 CDBUS_METHOD_ARGS_BEGIN(set, "Set conf value")
515 CDBUS_METHOD_ARG_DESCRIBE_IN("key", DBUS_TYPE_STRING_AS_STRING, "")
516 CDBUS_METHOD_ARG_DESCRIBE_IN("value", DBUS_TYPE_STRING_AS_STRING, "")
517 CDBUS_METHOD_ARG_DESCRIBE_OUT("version", DBUS_TYPE_UINT64_AS_STRING, "")
518 CDBUS_METHOD_ARGS_END
520 CDBUS_METHOD_ARGS_BEGIN(get, "Get conf value")
521 CDBUS_METHOD_ARG_DESCRIBE_IN("key", DBUS_TYPE_STRING_AS_STRING, "")
522 CDBUS_METHOD_ARG_DESCRIBE_OUT("value", DBUS_TYPE_STRING_AS_STRING, "")
523 CDBUS_METHOD_ARG_DESCRIBE_OUT("version", DBUS_TYPE_UINT64_AS_STRING, "")
524 CDBUS_METHOD_ARGS_END
526 CDBUS_METHOD_ARGS_BEGIN(exit, "Tell conf D-Bus service to exit")
527 CDBUS_METHOD_ARGS_END
529 CDBUS_METHODS_BEGIN
530 CDBUS_METHOD_DESCRIBE(set, conf_set)
531 CDBUS_METHOD_DESCRIBE(get, conf_get)
532 CDBUS_METHOD_DESCRIBE(exit, conf_exit)
533 CDBUS_METHODS_END
535 CDBUS_SIGNAL_ARGS_BEGIN(changed, "")
536 CDBUS_SIGNAL_ARG_DESCRIBE("key", DBUS_TYPE_STRING_AS_STRING, "")
537 CDBUS_SIGNAL_ARG_DESCRIBE("value", DBUS_TYPE_STRING_AS_STRING, "")
538 CDBUS_SIGNAL_ARG_DESCRIBE("version", DBUS_TYPE_UINT64_AS_STRING, "")
539 CDBUS_SIGNAL_ARGS_END
541 CDBUS_SIGNALS_BEGIN
542 CDBUS_SIGNAL_DESCRIBE(changed)
543 CDBUS_SIGNALS_END
545 CDBUS_INTERFACE_DEFAULT_HANDLER_METHODS_AND_SIGNALS(g_interface, CONF_IFACE)