#include "places_config.h" #ifdef PLACES_HAVE_SYSTEMD #include #include #include "e_mod_main.h" #include "e_mod_places.h" /* NOTES - mounts in fstab seems to need the "auto" option to be seen over dbus :/ */ /* Systemd defines */ #define SYSTEMD_BUS "org.freedesktop.systemd1" #define SYSTEMD_PATH "/org/freedesktop/systemd1" #define SYSTEMD_MANAGER_IFACE "org.freedesktop.systemd1.Manager" #define SYSTEMD_UNIT_IFACE "org.freedesktop.systemd1.Unit" #define SYSTEMD_MOUNT_IFACE "org.freedesktop.systemd1.Mount" /* Local backend data */ typedef struct Places_Systemd_Backend_Data { Eldbus_Object *unit_obj; } Places_Systemd_Backend_Data; /* Local Function Prototypes */ static void _places_sd_name_start(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending); static void _places_sd_list_units_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending); static void _places_sd_unit_new_cb(void *data, const Eldbus_Message *msg); static void _places_sd_unit_removed_cb(void *data, const Eldbus_Message *msg); static void _places_sd_mount_props_all_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending); static void _places_sd_unit_props_all_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending); static void _places_sd_mount_props_changed_cb(void *data, const Eldbus_Message *msg); static void _places_sd_read_unit_properties(Volume *vol, const char *iface, Eldbus_Message_Iter *props); static Volume* _places_sd_volume_add(const char *obj_path, Eina_Bool first_time); static void _places_sd_volume_finalize(Volume *vol); static void _places_sd_mount_func(Volume *vol, Eina_List *opts); static void _places_sd_unmount_func(Volume *vol, Eina_List *opts); static void _places_sd_eject_func(Volume *vol, Eina_List *opts); static void _places_sd_volume_free_func(Volume *vol); /* Local Variables */ static Eldbus_Connection *_places_sd_conn = NULL; static Eldbus_Object *_places_sd_manager = NULL; Eina_Bool places_systemd_init(void) { printf("PLACES: systemd: init()\n"); EINA_SAFETY_ON_FALSE_RETURN_VAL(eldbus_init(), EINA_FALSE); _places_sd_conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SYSTEM); // _places_sd_conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION); if (!_places_sd_conn) { printf("PLACES: systemd: Error connecting to system bus.\n"); return EINA_FALSE; } eldbus_name_start(_places_sd_conn, SYSTEMD_BUS, 0, _places_sd_name_start, NULL); return EINA_TRUE; } void places_systemd_shutdown(void) { printf("PLACES: systemd: shutdown()\n"); E_FREE_FUNC(_places_sd_manager, eldbus_object_unref); E_FREE_FUNC(_places_sd_conn, eldbus_connection_unref); eldbus_shutdown(); } /* The service is up and running, setup the Manager object */ static void _places_sd_name_start(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) { Eldbus_Proxy *proxy; Eldbus_Message *meth; Eldbus_Message_Iter *iter, *array; PLACES_ON_MSG_ERROR_RETURN(msg); printf("PLACES: systemd Up and running\n"); // get the systemd Manager object _places_sd_manager = eldbus_object_get(_places_sd_conn, SYSTEMD_BUS, SYSTEMD_PATH); proxy = eldbus_proxy_get(_places_sd_manager, SYSTEMD_MANAGER_IFACE); // NOTE: proxy will be automatically deleted on obj deletion // call the ListUnitsByPatterns(as states, as patterns) on the Manager object meth = eldbus_proxy_method_call_new(proxy, "ListUnitsByPatterns"); iter = eldbus_message_iter_get(meth); eldbus_message_iter_arguments_append(iter, "as", &array); eldbus_message_iter_container_close(iter, array); eldbus_message_iter_arguments_append(iter, "as", &array); eldbus_message_iter_basic_append(array, 's', "*.mount"); // eldbus_message_iter_basic_append(array, 's', "*.device"); eldbus_message_iter_container_close(iter, array); eldbus_proxy_send(proxy, meth, _places_sd_list_units_cb, NULL, -1); // connect the Manager UnitNew / UnitRemoved signal handlers eldbus_proxy_signal_handler_add(proxy, "UnitNew", _places_sd_unit_new_cb, NULL); eldbus_proxy_signal_handler_add(proxy, "UnitRemoved", _places_sd_unit_removed_cb, NULL); } /* Callback for the Manager method: ListUnitsByPatterns() */ static void _places_sd_list_units_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) { Eldbus_Message_Iter *units, *unit; const char *name, *label; const char *load_state, *active_state, *sub_state; const char *followed, *obj_path; const char *job_type, *job_obj_path; unsigned int job_id; PLACES_ON_MSG_ERROR_RETURN(msg); EINA_SAFETY_ON_FALSE_RETURN( eldbus_message_arguments_get(msg, "a(ssssssouso)", &units) ); printf("PLACES: ListUnits\n"); while (eldbus_message_iter_get_and_next(units, 'r', &unit)) { if (eldbus_message_iter_arguments_get(unit, "ssssssouso", &name, &label, &load_state, &active_state, &sub_state, &followed, &obj_path, &job_id,&job_type, &job_obj_path)) { printf("******* %s\n", obj_path); printf(" name: %s\n", name); printf(" label: %s\n", label); printf(" load_state: %s\n", load_state); printf(" active_state: %s\n", active_state); printf(" sub_state: %s\n", sub_state); printf(" followed: %s\n", followed); printf(" job_id: %d\n", job_id); printf(" job_type: %s\n", job_type); printf(" job_obj_path: %s\n", job_obj_path); _places_sd_volume_add(obj_path, EINA_TRUE); } } } /* Callback for the Manager signal 'UnitNew' */ static void _places_sd_unit_new_cb(void *data, const Eldbus_Message *msg) { const char *id, *obj_path; PLACES_ON_MSG_ERROR_RETURN(msg); EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_arguments_get(msg, "so", &id, &obj_path)); // printf("PLACES: UnitNew(%s, %s)\n", id, obj_path); if (eina_str_has_suffix(id, ".mount") || eina_str_has_suffix(id, ".device")) { printf("PLACES: UnitNew(%s, %s)\n", id, obj_path); if (eina_str_has_suffix(id, ".mount")) { _places_sd_volume_add(obj_path, EINA_FALSE); } } } /* Callback for the Manager signal 'UnitRemoved' */ static void _places_sd_unit_removed_cb(void *data, const Eldbus_Message *msg) { const char *id, *obj_path; PLACES_ON_MSG_ERROR_RETURN(msg); EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_arguments_get(msg, "so", &id, &obj_path)); // printf("PLACES: UnitRemoved(%s, %s)\n", id, obj_path); if (eina_str_has_suffix(id, ".mount") || eina_str_has_suffix(id, ".device")) printf("PLACES: UnitRemoved(%s, %s)\n", id, obj_path); // TODO } static Volume* _places_sd_volume_add(const char *obj_path, Eina_Bool first_time) { Places_Systemd_Backend_Data *bdata; Volume *vol; // return a previously created Volume vol = places_volume_by_id_get(obj_path); if (vol) return vol; // create the backend data struct bdata = E_NEW(Places_Systemd_Backend_Data, 1); if (!bdata) return NULL; // create the Eldbus object for this unit bdata->unit_obj = eldbus_object_get(_places_sd_conn, SYSTEMD_BUS, obj_path); if (!bdata->unit_obj) return NULL; // create the places Volume vol = places_volume_add(obj_path, first_time); if (!vol) return NULL; vol->backend_data = bdata; vol->mount_func = _places_sd_mount_func; vol->unmount_func = _places_sd_unmount_func; vol->eject_func = _places_sd_eject_func; vol->free_func = _places_sd_volume_free_func; // request all the properties for the Mount & Unit ifaces Eldbus_Proxy *proxy; proxy = eldbus_proxy_get(bdata->unit_obj, SYSTEMD_MOUNT_IFACE); eldbus_proxy_property_get_all(proxy, _places_sd_mount_props_all_cb, vol); proxy = eldbus_proxy_get(bdata->unit_obj, SYSTEMD_UNIT_IFACE); eldbus_proxy_property_get_all(proxy, _places_sd_unit_props_all_cb, vol); // Get notifications on object properties change eldbus_object_signal_handler_add(bdata->unit_obj, ELDBUS_FDO_INTERFACE_PROPERTIES, "PropertiesChanged", _places_sd_mount_props_changed_cb, vol); return vol; } static void _places_sd_volume_free_func(Volume *vol) { Places_Systemd_Backend_Data *bdata = vol->backend_data; if (bdata) { E_FREE_FUNC(bdata->unit_obj, eldbus_object_unref); E_FREE(vol->backend_data); } } /* Callback for ALL properties of the Mount iface */ static void _places_sd_mount_props_all_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) { Volume *vol = data; Eldbus_Message_Iter *props; EINA_SAFETY_ON_NULL_RETURN(vol); PLACES_ON_MSG_ERROR_RETURN(msg); if (eldbus_message_arguments_get(msg, "a{sv}", &props)) { printf("PLACES: ALL Mount props for: %s\n", vol->id); _places_sd_read_unit_properties(vol, SYSTEMD_MOUNT_IFACE, props); } } /* Callback for ALL properties of the Unit iface */ static void _places_sd_unit_props_all_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) { Volume *vol = data; Eldbus_Message_Iter *props; EINA_SAFETY_ON_NULL_RETURN(vol); PLACES_ON_MSG_ERROR_RETURN(msg); if (eldbus_message_arguments_get(msg, "a{sv}", &props)) { printf("PLACES: ALL Unit props for: %s\n", vol->id); _places_sd_read_unit_properties(vol, SYSTEMD_UNIT_IFACE, props); } } /* Callback for DBUS signal "PropertiesChanged" on the unit objects */ static void _places_sd_mount_props_changed_cb(void *data, const Eldbus_Message *msg) { Volume *vol = data; Eldbus_Message_Iter *changed_props, *invalidated_props; const char *iface; EINA_SAFETY_ON_NULL_RETURN(vol); PLACES_ON_MSG_ERROR_RETURN(msg); if (eldbus_message_arguments_get(msg, "sa{sv}as", &iface, &changed_props, &invalidated_props)) { printf("PLACES: PropertiesChanged obj:%s - iface:%s\n", vol->id, iface); _places_sd_read_unit_properties(vol, iface, changed_props); } } /* Read the properties for a .mount object (Mount & Unit ifaces)*/ static void _places_sd_read_unit_properties(Volume *vol, const char *iface, Eldbus_Message_Iter *props) { Eldbus_Message_Iter *entry, *var; const char *key, *str_val; unsigned int changed = 0; Eina_Bool is_mount_iface = eina_streq(iface, SYSTEMD_MOUNT_IFACE); Eina_Bool is_unit_iface = eina_streq(iface, SYSTEMD_UNIT_IFACE); // NOTE props must be a{sv} EINA_SAFETY_ON_NULL_RETURN(vol); EINA_SAFETY_ON_NULL_RETURN(props); printf("PLACES: Properties obj:%s - iface:%s\n", vol->id, iface); // collect usefull props iterating over the dict while (eldbus_message_iter_get_and_next(props, 'e', &entry)) { if (!eldbus_message_iter_arguments_get(entry, "sv", &key, &var)) continue; // printf(" prop: %s\n", key); if (is_mount_iface) { if (eina_streq(key, "Where")) { eldbus_message_iter_arguments_get(var, "s", &str_val); if (eina_stringshare_replace(&vol->mount_point, str_val)) changed++; } else if (eina_streq(key, "What")) { eldbus_message_iter_arguments_get(var, "s", &str_val); if (eina_stringshare_replace(&vol->device, str_val)) changed++; } else if (eina_streq(key, "Type")) { eldbus_message_iter_arguments_get(var, "s", &str_val); if (eina_stringshare_replace(&vol->fstype, str_val)) changed++; } // Mount.ExecMount // Mount.ExecRemount // Mount.ExecUnmount } else if (is_unit_iface) { if (eina_streq(key, "SubState")) { eldbus_message_iter_arguments_get(var, "s", &str_val); if (eina_streq(str_val, "mounted")) { if (!vol->mounted) { vol->mounted = EINA_TRUE; changed++; } } else { if (vol->mounted) { vol->mounted = EINA_FALSE; changed++; } } } // Unit.CanStart bool // Unit.CanStop bool } } if (changed) _places_sd_volume_finalize(vol); } /* Called after all properties has been readed */ static void _places_sd_volume_finalize(Volume *vol) { Eina_Bool is_valid = EINA_FALSE; printf("PLACES: Validating %s\n", vol->id); // TODO array of know types if (eina_streq(vol->fstype, "ext4") || eina_streq(vol->fstype, "vfat") || eina_streq(vol->fstype, "nfs") || eina_streq(vol->fstype, "nfs3") || eina_streq(vol->fstype, "nfs4") || eina_streq(vol->fstype, "ntfs") || eina_streq(vol->fstype, "cifs")) is_valid = EINA_TRUE; else is_valid = EINA_FALSE; // choose a label if (vol->mount_point && vol->mount_point[0]) eina_stringshare_replace(&vol->label, vol->mount_point); else if (vol->device && vol->device[0]) eina_stringshare_replace(&vol->label, vol->device); // the update is always needed to trigger auto_mount/auto_open places_volume_update(vol); if (is_valid != vol->valid) { // trigger a full redraw, is the only way to show/hide a new device vol->valid = is_valid; places_update_all_gadgets(); } places_print_volume(vol); // TODO REMOVE ME } /* Callback for mount(), umont() and eject() calls */ static void _places_sd_task_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) { const char *str; Eina_Bool ret; if (eldbus_message_error_get(msg, NULL, NULL)) { ret = eldbus_message_arguments_get(msg, "s", &str); e_util_dialog_internal(D_("Operation failed"), ret ? str : D_("Unknown error")); } } static void _places_sd_mount_func(Volume *vol, Eina_List *opts) { Places_Systemd_Backend_Data *bdata = vol->backend_data; Eldbus_Message *msg; EINA_SAFETY_ON_FALSE_RETURN((bdata && bdata->unit_obj)); printf("PLACES: systemd Mount %s\n", vol->id); // Call the Start(s mode) method on the Unit iface msg = eldbus_object_method_call_new(bdata->unit_obj, SYSTEMD_UNIT_IFACE, "Start"); eldbus_message_arguments_append(msg, "s", "replace"); eldbus_object_send(bdata->unit_obj, msg, _places_sd_task_cb, vol, -1); } static void _places_sd_unmount_func(Volume *vol, Eina_List *opts) { Places_Systemd_Backend_Data *bdata = vol->backend_data; Eldbus_Message *msg; EINA_SAFETY_ON_FALSE_RETURN((bdata && bdata->unit_obj)); printf("PLACES: systemd Unmount %s\n", vol->id); // Call the Stop(s mode) method on the Unit iface msg = eldbus_object_method_call_new(bdata->unit_obj, SYSTEMD_UNIT_IFACE, "Stop"); eldbus_message_arguments_append(msg, "s", "replace"); eldbus_object_send(bdata->unit_obj, msg, _places_sd_task_cb, vol, -1); } static void _places_sd_eject_func(Volume *vol, Eina_List *opts) { // Places_Systemd_Backend_Data *bdata = vol->backend_data; printf("PLACES: TODO systemd Eject %s\n", vol->id); } #endif