510 lines
15 KiB
C
510 lines
15 KiB
C
|
|
#include "places_config.h"
|
|
|
|
|
|
#ifdef PLACES_HAVE_SYSTEMD
|
|
|
|
#include <e.h>
|
|
#include <Eldbus.h>
|
|
#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
|