/* -*- Mode: C ; c-basic-offset: 2 -*- */ /* * LADI Session Handler (ladish) * * Copyright (C) 2009,2010,2011,2012 Nedko Arnaudov * ************************************************************************** * This file contains the liblash implementaiton ************************************************************************** * * LADI Session Handler is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * LADI Session Handler is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with LADI Session Handler. If not, see * or write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include "lash/lash.h" #include "../../common.h" #include "../../common/catdup.h" #include "../../common/dirhelpers.h" #include "../../common/file.h" #include "../../log.h" #include #include "../../dbus_constants.h" #define LASH_CONFIG_SUBDIR "/.ladish_lash_dict/" static cdbus_object_path g_object; extern const struct cdbus_interface_descriptor g_interface __attribute__((visibility("hidden"))); struct _lash_client { int flags; }; static struct _lash_client g_client; struct _lash_event { enum LASH_Event_Type type; char * string; }; static struct _lash_event g_event = {0, NULL}; static bool g_quit = false; static bool g_busy = false; /* whether the app is processing the event */ struct _lash_config { struct list_head siblings; char * key; size_t size; void * value; }; static LIST_HEAD(g_pending_configs); static void clean_pending_configs(void) { struct _lash_config * config_ptr; while (!list_empty(&g_pending_configs)) { config_ptr = list_entry(g_pending_configs.next, struct _lash_config, siblings); list_del(g_pending_configs.next); lash_config_destroy(config_ptr); } } static void save_config(const char * dir, struct _lash_config * config_ptr) { char * path; int fd; ssize_t written; log_debug("saving dict key '%s'", config_ptr->key); path = catdup3(dir, LASH_CONFIG_SUBDIR, config_ptr->key); if (path == NULL) { goto exit; } fd = creat(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (fd == -1) { log_error("error creating config file '%s' (%s)", path, strerror(errno)); goto free; } written = write(fd, config_ptr->value, config_ptr->size); if (written == -1) { log_error("error writing config file '%s' (%s)", path, strerror(errno)); goto close; } if ((size_t)written < config_ptr->size) { log_error("error writing config file '%s' (%zd instead of %zu)", path, written, config_ptr->size); goto close; } log_debug("saved dict key '%s'", config_ptr->key); close: close(fd); free: free(path); exit: return; } static void save_pending_configs(const char * dir) { struct _lash_config * config_ptr; if (!ensure_dir_exist_varg(S_IRWXU | S_IRWXG | S_IRWXO, dir, LASH_CONFIG_SUBDIR, NULL)) { log_error("ensure_dir_exist_varg() failed for %s%s", dir, LASH_CONFIG_SUBDIR); clean_pending_configs(); return; } while (!list_empty(&g_pending_configs)) { config_ptr = list_entry(g_pending_configs.next, struct _lash_config, siblings); list_del(g_pending_configs.next); save_config(dir, config_ptr); lash_config_destroy(config_ptr); } } static void load_configs(const char * appdir) { char * dirpath; char * filepath; DIR * dir; struct dirent * dentry; struct stat st; lash_config_t * config_ptr; clean_pending_configs(); ASSERT(list_empty(&g_pending_configs)); log_debug("Loading configs from '%s'", appdir); dirpath = catdup(appdir, LASH_CONFIG_SUBDIR); if (dirpath == NULL) { goto exit; } dir = opendir(dirpath); if (dir == NULL) { log_error("Cannot open directory '%s': %d (%s)", dirpath, errno, strerror(errno)); goto free; } while ((dentry = readdir(dir)) != NULL) { if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0) { continue; } filepath = catdup3(dirpath, "/", dentry->d_name); if (filepath == NULL) { log_error("catdup() failed"); goto close; } if (stat(filepath, &st) != 0) { log_error("failed to stat '%s': %d (%s)", filepath, errno, strerror(errno)); goto next; } if (!S_ISREG(st.st_mode)) { log_error("not regular file '%s' with mode is %07o", filepath, st.st_mode); goto next; } config_ptr = lash_config_new_with_key(dentry->d_name); if (config_ptr == NULL) { goto next; } config_ptr->value = read_file_contents(filepath); if (config_ptr->value == NULL) { log_error("read from '%s' failed", filepath); lash_config_destroy(config_ptr); goto next; } config_ptr->size = (size_t)st.st_size; list_add_tail(&config_ptr->siblings, &g_pending_configs); log_debug("loaded dict key '%s'", dentry->d_name); next: free(filepath); } close: closedir(dir); free: free(dirpath); exit: return; } LADISH_PUBLIC const char * lash_protocol_string(lash_protocol_t UNUSED(protocol)) { return "ladish"; } LADISH_PUBLIC lash_args_t * lash_extract_args(int * UNUSED(argc), char *** UNUSED(argv)) { /* nothing to do, ladish does not pass any specific arguments */ return NULL; } LADISH_PUBLIC void lash_args_destroy(lash_args_t * UNUSED(args)) { /* nothing to do, ladish does not pass any specific arguments */ } LADISH_PUBLIC lash_client_t * lash_init(const lash_args_t * UNUSED(args), const char * class, int client_flags, lash_protocol_t UNUSED(protocol)) { DBusError error; DBusMessage * msg_ptr; const char * dbus_unique_name; bool ret; dbus_uint32_t flags32; dbus_uint64_t pid; if ((client_flags & LASH_Server_Interface) != 0) { log_error("ladish does not implement LASH server interface."); goto fail; } dbus_error_init(&error); cdbus_g_dbus_connection = dbus_bus_get_private(DBUS_BUS_SESSION, &error); if (cdbus_g_dbus_connection == NULL) { log_error("Cannot connect to D-Bus session bus: %s", error.message); dbus_error_free(&error); goto fail; } dbus_connection_set_exit_on_disconnect(cdbus_g_dbus_connection, FALSE); dbus_unique_name = dbus_bus_get_unique_name(cdbus_g_dbus_connection); if (dbus_unique_name == NULL) { log_error("Failed to read unique bus name"); goto close_connection; } log_info("Connected to session bus, unique name is \"%s\"", dbus_unique_name); g_object = cdbus_object_path_new("/", &g_interface, NULL, NULL); if (g_object == NULL) { goto close_connection; } if (!cdbus_object_path_register(cdbus_g_dbus_connection, g_object)) { goto destroy_object; } flags32 = client_flags; pid = getpid(); msg_ptr = cdbus_new_method_call_message(SERVICE_NAME, LASH_SERVER_OBJECT_PATH, IFACE_LASH_SERVER, "RegisterClient", "tsu", &pid, &class, &flags32); if (msg_ptr == NULL) { goto close_connection; } ret = dbus_connection_send(cdbus_g_dbus_connection, msg_ptr, NULL); dbus_message_unref(msg_ptr); if (!ret) { log_error("Cannot send message over D-Bus due to lack of memory"); goto close_connection; } log_debug("ladish LASH support initialized (%s %s)", (client_flags & LASH_Config_File) != 0 ? "file" : "", (client_flags & LASH_Config_Data_Set) != 0 ? "dict" : ""); g_client.flags = client_flags; return &g_client; destroy_object: cdbus_object_path_destroy(cdbus_g_dbus_connection, g_object); close_connection: dbus_connection_close(cdbus_g_dbus_connection); dbus_connection_unref(cdbus_g_dbus_connection); fail: return NULL; } LADISH_PUBLIC unsigned int lash_get_pending_event_count(lash_client_t * client_ptr) { ASSERT(client_ptr == &g_client); return !g_busy && g_event.type != 0 ? 1 : 0; } static void dispatch(void) { do { dbus_connection_read_write_dispatch(cdbus_g_dbus_connection, 0); } while (dbus_connection_get_dispatch_status(cdbus_g_dbus_connection) == DBUS_DISPATCH_DATA_REMAINS); } LADISH_PUBLIC unsigned int lash_get_pending_config_count(lash_client_t * client_ptr) { struct list_head * node_ptr; unsigned int count; ASSERT(client_ptr == &g_client); dispatch(); count = 0; list_for_each(node_ptr, &g_pending_configs) { count++; } return count; } LADISH_PUBLIC lash_event_t * lash_get_event(lash_client_t * client_ptr) { ASSERT(client_ptr == &g_client); dispatch(); if (g_busy) { if (g_event.type == LASH_Restore_Data_Set && list_empty(&g_pending_configs)) { /* the example lash_simple_client client does not send LASH_Restore_Data_Set event back */ lash_send_event(&g_client, &g_event); ASSERT(!g_busy); } else { return NULL; } } if (g_event.type == 0) { if (g_quit) { g_event.type = LASH_Quit; } else { return NULL; } } if (g_event.type == LASH_Save_Data_Set) { clean_pending_configs(); /* TODO: change status to "saving" */ } else if (g_event.type == LASH_Restore_Data_Set) { load_configs(g_event.string); /* TODO: change status to "restoring (data)" */ } else if (g_event.type == LASH_Restore_File) { /* TODO: change status to "restoring (file)" */ } else if (g_event.type == LASH_Save_File) { /* TODO: change status to "saving" */ } else if (g_event.type == LASH_Quit) { /* TODO: change status to "quitting" */ } g_busy = true; log_debug("App begins to process event %d (%s)", g_event.type, g_event.string); return &g_event; } LADISH_PUBLIC lash_config_t * lash_get_config(lash_client_t * client_ptr) { struct _lash_config * config_ptr; ASSERT(client_ptr == &g_client); if (list_empty(&g_pending_configs)) { return NULL; } config_ptr = list_entry(g_pending_configs.next, struct _lash_config, siblings); list_del(g_pending_configs.next); return config_ptr; } LADISH_PUBLIC void lash_send_event(lash_client_t * client_ptr, lash_event_t * event_ptr) { ASSERT(client_ptr == &g_client); log_debug("lash_send_event() called. type=%d string=%s", event_ptr->type, event_ptr->string != NULL ? event_ptr->string : "(NULL)"); dispatch(); if (!g_busy) { return; } if (event_ptr->type == LASH_Save_File) { log_debug("Save to file complete (%s)", g_event.string); g_busy = false; if ((g_client.flags & LASH_Config_Data_Set) != 0) { log_debug("Client wants to save a dict too"); g_event.type = LASH_Save_Data_Set; if (event_ptr == &g_event) { return; } } else { g_event.type = 0; free(g_event.string); g_event.string = NULL; /* TODO: change status to "saved" */ } } else if (event_ptr->type == LASH_Save_Data_Set) { log_debug("Save to dict complete (%s)", g_event.string); save_pending_configs(g_event.string); g_busy = false; g_event.type = 0; free(g_event.string); g_event.string = NULL; /* TODO: change status to "saved" */ } else if (event_ptr->type == LASH_Restore_Data_Set) { log_debug("Restore from dict complete (%s)", g_event.string); g_busy = false; if ((g_client.flags & LASH_Config_File) != 0) { log_debug("Client wants to restore from file too"); g_event.type = LASH_Restore_File; if (event_ptr == &g_event) { return; } } else { g_event.type = 0; free(g_event.string); g_event.string = NULL; /* TODO: change status to "restored" */ } } else if (event_ptr->type == LASH_Restore_File) { log_debug("Restore from file complete (%s)", g_event.string); g_busy = false; g_event.type = 0; free(g_event.string); g_event.string = NULL; /* TODO: change status to "restored" */ } lash_event_destroy(event_ptr); } LADISH_PUBLIC void lash_send_config(lash_client_t * client_ptr, lash_config_t * config_ptr) { ASSERT(client_ptr == &g_client); log_debug("lash_send_config() called. key=%s value_size=%zu", config_ptr->key, config_ptr->size); dispatch(); if (g_event.type == LASH_Save_Data_Set) { list_add_tail(&config_ptr->siblings, &g_pending_configs); } else { log_error("Ignoring config with key '%s' because app is not saving data set", config_ptr->key); lash_config_destroy(config_ptr); } } LADISH_PUBLIC int lash_server_connected(lash_client_t * client_ptr) { ASSERT(client_ptr == &g_client); return 1; /* yes */ } LADISH_PUBLIC const char * lash_get_server_name(lash_client_t * client_ptr) { ASSERT(client_ptr == &g_client); return "localhost"; } LADISH_PUBLIC lash_event_t * lash_event_new(void) { struct _lash_event * event_ptr; event_ptr = malloc(sizeof(struct _lash_event)); if (event_ptr == NULL) { log_error("malloc() failed to allocate lash event struct"); return NULL; } event_ptr->type = 0; event_ptr->string = NULL; return event_ptr; } LADISH_PUBLIC lash_event_t * lash_event_new_with_type(enum LASH_Event_Type type) { lash_event_t * event_ptr; event_ptr = lash_event_new(); if (event_ptr == NULL) { return NULL; } lash_event_set_type(event_ptr, type); return event_ptr; } LADISH_PUBLIC lash_event_t * lash_event_new_with_all(enum LASH_Event_Type type, const char * string) { lash_event_t * event_ptr; event_ptr = lash_event_new_with_type(type); if (event_ptr == NULL) { return NULL; } if (string != NULL) { event_ptr->string = strdup(string); if (event_ptr->string == NULL) { log_error("strdup() failed for event string '%s'", string); free(event_ptr); return NULL; } } return event_ptr; } LADISH_PUBLIC void lash_event_destroy(lash_event_t * event_ptr) { free(event_ptr->string); if (event_ptr == &g_event) { event_ptr->type = 0; event_ptr->string = NULL; } else { free(event_ptr); } } LADISH_PUBLIC enum LASH_Event_Type lash_event_get_type(const lash_event_t * event_ptr) { return event_ptr->type; } LADISH_PUBLIC const char * lash_event_get_string(const lash_event_t * event_ptr) { return event_ptr->string; } LADISH_PUBLIC void lash_event_set_type(lash_event_t * event_ptr, enum LASH_Event_Type type) { event_ptr->type = type; } LADISH_PUBLIC void lash_event_set_string(lash_event_t * event_ptr, const char * string) { char * dup; if (string != NULL) { dup = strdup(string); if (dup == NULL) { log_error("strdup() failed for event string '%s'", string); ASSERT_NO_PASS; return; } } else { dup = NULL; } free(event_ptr->string); event_ptr->string = dup; } LADISH_PUBLIC const char * lash_event_get_project(const lash_event_t * UNUSED(event_ptr)) { /* Server interface - not implemented */ ASSERT_NO_PASS; /* lash_init() fails if LASH_Server_Interface is set */ return NULL; } LADISH_PUBLIC void lash_event_set_project(lash_event_t * UNUSED(event_ptr), const char * UNUSED(project)) { /* Server interface - not implemented */ ASSERT_NO_PASS; /* lash_init() fails if LASH_Server_Interface is set */ } LADISH_PUBLIC void lash_event_get_client_id(const lash_event_t * UNUSED(event_ptr), uuid_t UNUSED(id)) { /* Server interface - not implemented */ ASSERT_NO_PASS; /* lash_init() fails if LASH_Server_Interface is set */ } LADISH_PUBLIC void lash_event_set_client_id(lash_event_t * UNUSED(event_ptr), uuid_t UNUSED(id)) { /* Server interface - not implemented */ ASSERT_NO_PASS; /* lash_init() fails if LASH_Server_Interface is set */ } LADISH_PUBLIC unsigned char lash_event_get_alsa_client_id(const lash_event_t * UNUSED(event_ptr)) { /* Server interface - not implemented */ ASSERT_NO_PASS; /* lash_init() fails if LASH_Server_Interface is set */ return 0; } LADISH_PUBLIC unsigned char lash_str_get_alsa_client_id(const char * UNUSED(str)) { /* Server interface - not implemented */ ASSERT_NO_PASS; /* lash_init() fails if LASH_Server_Interface is set */ /* this is an undocumented function and probably internal one that sneaked to the public API */ return 0; } LADISH_PUBLIC void lash_jack_client_name(lash_client_t * UNUSED(client_ptr), const char * UNUSED(name)) { /* nothing to do, ladish detects jack client name through jack server */ } LADISH_PUBLIC void lash_str_set_alsa_client_id(char * UNUSED(str), unsigned char UNUSED(alsa_id)) { /* nothing to do, ladish detects alsa id through alsapid.so, jack and a2jmidid */ /* this is an undocumented function and probably internal one that sneaked to the public API */ } LADISH_PUBLIC void lash_event_set_alsa_client_id(lash_event_t * event_ptr, unsigned char UNUSED(alsa_id)) { /* set event type, so we can silently ignore the event, when sent */ lash_event_set_type(event_ptr, LASH_Alsa_Client_ID); } LADISH_PUBLIC void lash_alsa_client_id(lash_client_t * UNUSED(client), unsigned char UNUSED(id)) { /* nothing to do, ladish detects alsa id through alsapid.so, jack and a2jmidid */ } LADISH_PUBLIC lash_config_t * lash_config_new(void) { struct _lash_config * config_ptr; config_ptr = malloc(sizeof(struct _lash_config)); if (config_ptr == NULL) { log_error("malloc() failed to allocate lash event struct"); return NULL; } config_ptr->key = NULL; config_ptr->value = NULL; config_ptr->size = 0; return config_ptr; } LADISH_PUBLIC lash_config_t * lash_config_dup(const lash_config_t * src_ptr) { lash_config_t * dst_ptr; dst_ptr = lash_config_new(); if (dst_ptr == NULL) { return NULL; } ASSERT(src_ptr->key != NULL); dst_ptr->key = strdup(src_ptr->key); if (dst_ptr->key == NULL) { log_error("strdup() failed for config key '%s'", src_ptr->key); free(dst_ptr); return NULL; } if (dst_ptr->value != NULL) { dst_ptr->value = malloc(src_ptr->size); if (dst_ptr->value == NULL) { log_error("strdup() failed for config value with size %zu", src_ptr->size); free(dst_ptr->key); free(dst_ptr); return NULL; } memcpy(dst_ptr->value, src_ptr->value, src_ptr->size); dst_ptr->size = src_ptr->size; } return dst_ptr; } LADISH_PUBLIC lash_config_t * lash_config_new_with_key(const char * key) { lash_config_t * config_ptr; config_ptr = lash_config_new(); if (config_ptr == NULL) { return NULL; } config_ptr->key = strdup(key); if (config_ptr->key == NULL) { log_error("strdup() failed for config key '%s'", key); free(config_ptr); return NULL; } return config_ptr; } LADISH_PUBLIC void lash_config_destroy(lash_config_t * config_ptr) { free(config_ptr->key); free(config_ptr->value); free(config_ptr); } LADISH_PUBLIC const char * lash_config_get_key(const lash_config_t * config_ptr) { return config_ptr->key; } LADISH_PUBLIC const void * lash_config_get_value(const lash_config_t * config_ptr) { return config_ptr->value; } LADISH_PUBLIC size_t lash_config_get_value_size(const lash_config_t * config_ptr) { return config_ptr->size; } LADISH_PUBLIC void lash_config_set_key(lash_config_t * config_ptr, const char * key) { char * dup; ASSERT(key != NULL); dup = strdup(key); if (dup == NULL) { log_error("strdup() failed for config key '%s'", key); ASSERT_NO_PASS; return; } free(config_ptr->key); config_ptr->key = dup; } LADISH_PUBLIC void lash_config_set_value(lash_config_t * config_ptr, const void * value, size_t value_size) { void * buf; if (value != NULL) { buf = malloc(value_size); if (buf == NULL) { log_error("malloc() failed for config value with size %zu", value_size); ASSERT_NO_PASS; return; } memcpy(buf, value, value_size); } else { buf = NULL; value_size = 0; } free(config_ptr->value); config_ptr->value = buf; config_ptr->size = value_size; } LADISH_PUBLIC uint32_t lash_config_get_value_int(const lash_config_t * config_ptr) { ASSERT(lash_config_get_value_size(config_ptr) >= sizeof(uint32_t)); return ntohl(*(const uint32_t *)lash_config_get_value(config_ptr)); } LADISH_PUBLIC float lash_config_get_value_float(const lash_config_t * config_ptr) { ASSERT(lash_config_get_value_size(config_ptr) >= sizeof(float)); return *(const float *)lash_config_get_value(config_ptr); } LADISH_PUBLIC double lash_config_get_value_double(const lash_config_t * config_ptr) { ASSERT(lash_config_get_value_size(config_ptr) >= sizeof(double)); return *(const double *)lash_config_get_value(config_ptr); } LADISH_PUBLIC const char * lash_config_get_value_string(const lash_config_t * config_ptr) { const char * string; size_t len; void * ptr; string = lash_config_get_value(config_ptr); len = lash_config_get_value_size(config_ptr); ptr = memchr(string, 0, len); ASSERT(ptr != NULL); return string; } LADISH_PUBLIC void lash_config_set_value_int(lash_config_t * config_ptr, uint32_t value) { value = htonl(value); lash_config_set_value(config_ptr, &value, sizeof(uint32_t)); } LADISH_PUBLIC void lash_config_set_value_float(lash_config_t * config_ptr, float value) { lash_config_set_value(config_ptr, &value, sizeof(float)); } LADISH_PUBLIC void lash_config_set_value_double(lash_config_t * config_ptr, double value) { lash_config_set_value(config_ptr, &value, sizeof(double)); } LADISH_PUBLIC void lash_config_set_value_string(lash_config_t * config_ptr, const char * value) { lash_config_set_value(config_ptr, value, strlen(value) + 1); } LADISH_PUBLIC const char * lash_get_fqn(const char * dir, const char * file) { static char * fqn = NULL; if (fqn != NULL) { free(fqn); } fqn = catdup3(dir, "/", file); return fqn; } /***************************************************************************/ /* D-Bus interface implementation */ static void lash_quit(struct cdbus_method_call * UNUSED(call_ptr)) { log_debug("Quit command received through D-Bus"); g_quit = true; } static void lash_save(struct cdbus_method_call * call_ptr) { const char * dir; char * dup; int type; dbus_error_init(&cdbus_g_dbus_error); if (!dbus_message_get_args(call_ptr->message, &cdbus_g_dbus_error, DBUS_TYPE_STRING, &dir, DBUS_TYPE_INVALID)) { cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message); dbus_error_free(&cdbus_g_dbus_error); return; } log_debug("Save command received through D-Bus (%s)", dir); if (g_event.type != 0) { cdbus_error(call_ptr, LADISH_DBUS_ERROR_UNFINISHED_TASK, "App is busy processing event if type %d", g_event.type); return; } if (g_quit != 0) { cdbus_error(call_ptr, LADISH_DBUS_ERROR_UNFINISHED_TASK, "App is quitting", g_event.type); return; } if ((g_client.flags & LASH_Config_File) != 0) { type = LASH_Save_File; } else if ((g_client.flags & LASH_Config_Data_Set) != 0) { type = LASH_Save_Data_Set; } else { log_debug("App does not have internal state"); /* TODO: change status to "saved" */ return; } dup = strdup(dir); if (dup == NULL) { cdbus_error(call_ptr, DBUS_ERROR_FAILED, "strdup() failed for event string (dir) '%s'", dup); return; } ASSERT(g_event.string == NULL); g_event.string = dup; g_event.type = type; } static void lash_restore(struct cdbus_method_call * call_ptr) { const char * dir; char * dup; int type; dbus_error_init(&cdbus_g_dbus_error); if (!dbus_message_get_args(call_ptr->message, &cdbus_g_dbus_error, DBUS_TYPE_STRING, &dir, DBUS_TYPE_INVALID)) { cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message); dbus_error_free(&cdbus_g_dbus_error); return; } log_debug("Restore command received through D-Bus (%s)", dir); if (g_event.type != 0) { cdbus_error(call_ptr, LADISH_DBUS_ERROR_UNFINISHED_TASK, "App is busy processing event if type %d", g_event.type); return; } if (g_quit != 0) { cdbus_error(call_ptr, LADISH_DBUS_ERROR_UNFINISHED_TASK, "App is quitting", g_event.type); return; } if ((g_client.flags & LASH_Config_File) != 0) { type = LASH_Restore_File; } else if ((g_client.flags & LASH_Config_Data_Set) != 0) { type = LASH_Restore_Data_Set; } else { log_debug("App does not have internal state"); /* TODO: change status to "restored" */ return; } dup = strdup(dir); if (dup == NULL) { cdbus_error(call_ptr, DBUS_ERROR_FAILED, "strdup() failed for event string (dir) '%s'", dup); return; } ASSERT(g_event.string == NULL); g_event.string = dup; g_event.type = type; } CDBUS_METHOD_ARGS_BEGIN(Save, "Tell lash client to save") CDBUS_METHOD_ARG_DESCRIBE_IN("app_dir", "s", "Directory where app must save its internal state") CDBUS_METHOD_ARGS_END CDBUS_METHOD_ARGS_BEGIN(Restore, "Tell lash client to restore") CDBUS_METHOD_ARG_DESCRIBE_IN("app_dir", "s", "Directory from where app must load its internal state") CDBUS_METHOD_ARGS_END CDBUS_METHOD_ARGS_BEGIN(Quit, "Tell lash client to quit") CDBUS_METHOD_ARGS_END CDBUS_METHODS_BEGIN CDBUS_METHOD_DESCRIBE(Save, lash_save) CDBUS_METHOD_DESCRIBE(Restore, lash_restore) CDBUS_METHOD_DESCRIBE(Quit, lash_quit) CDBUS_METHODS_END CDBUS_INTERFACE_DEFAULT_HANDLER_METHODS_ONLY(g_interface, IFACE_LASH_CLIENT)