#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mnttab.h>
#include <sys/dkio.h>
#include <priv.h>
#include <libsysevent.h>
#include <sys/sysevent/dev.h>
#include <libhal.h>
#include "../../hald/logger.h"
#define SLEEP_PERIOD 5
static char *udi;
static char *devfs_path;
LibHalContext *ctx = NULL;
static sysevent_handle_t *shp = NULL;
static void sysevent_dev_handler(sysevent_t *);
static void
my_dbus_error_free(DBusError *error)
{
if (dbus_error_is_set(error)) {
dbus_error_free(error);
}
}
static void
sysevent_init ()
{
const char *subcl[1];
shp = sysevent_bind_handle (sysevent_dev_handler);
if (shp == NULL) {
HAL_DEBUG (("sysevent_bind_handle failed %d", errno));
return;
}
subcl[0] = ESC_DEV_EJECT_REQUEST;
if (sysevent_subscribe_event (shp, EC_DEV_STATUS, subcl, 1) != 0) {
HAL_INFO (("subscribe(dev_status) failed %d", errno));
sysevent_unbind_handle (shp);
return;
}
}
static void
sysevent_fini ()
{
if (shp != NULL) {
sysevent_unbind_handle (shp);
shp = NULL;
}
}
static void
sysevent_dev_handler (sysevent_t *ev)
{
char *class;
char *subclass;
nvlist_t *attr_list;
char *phys_path, *path;
char *p;
int len;
DBusError error;
if ((class = sysevent_get_class_name (ev)) == NULL)
return;
if ((subclass = sysevent_get_subclass_name (ev)) == NULL)
return;
if ((strcmp (class, EC_DEV_STATUS) != 0) ||
(strcmp (subclass, ESC_DEV_EJECT_REQUEST) != 0))
return;
if (sysevent_get_attr_list (ev, &attr_list) != 0)
return;
if (nvlist_lookup_string (attr_list, DEV_PHYS_PATH, &phys_path) != 0) {
goto out;
}
if (strncmp (phys_path, "/devices", sizeof ("/devices") - 1) == 0)
path = phys_path + sizeof ("/devices") - 1;
else
path = phys_path;
if ((p = strrchr (path, ':')) == NULL)
goto out;
len = (uintptr_t)p - (uintptr_t)path;
if (strncmp (path, devfs_path, len) != 0)
goto out;
HAL_DEBUG (("sysevent_dev_handler %s %s", subclass, phys_path));
dbus_error_init (&error);
libhal_device_emit_condition (ctx, udi, "EjectPressed", "", &error);
dbus_error_free (&error);
out:
nvlist_free(attr_list);
}
static void
force_unmount (LibHalContext *ctx, const char *udi)
{
DBusError error;
DBusMessage *msg = NULL;
DBusMessage *reply = NULL;
char **options = NULL;
unsigned int num_options = 0;
DBusConnection *dbus_connection;
char *device_file;
dbus_error_init (&error);
dbus_connection = libhal_ctx_get_dbus_connection (ctx);
msg = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
"org.freedesktop.Hal.Device.Volume",
"Unmount");
if (msg == NULL) {
HAL_DEBUG (("Could not create dbus message for %s", udi));
goto out;
}
options = calloc (1, sizeof (char *));
if (options == NULL) {
HAL_DEBUG (("Could not allocate options array"));
goto out;
}
device_file = libhal_device_get_property_string (ctx, udi, "block.device", &error);
if (device_file != NULL) {
libhal_free_string (device_file);
}
dbus_error_free (&error);
if (!dbus_message_append_args (msg,
DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, num_options,
DBUS_TYPE_INVALID)) {
HAL_DEBUG (("Could not append args to dbus message for %s", udi));
goto out;
}
if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, msg, -1, &error))) {
HAL_DEBUG (("Unmount failed for %s: %s : %s\n", udi, error.name, error.message));
goto out;
}
if (dbus_error_is_set (&error)) {
HAL_DEBUG (("Unmount failed for %s\n%s : %s\n", udi, error.name, error.message));
goto out;
}
HAL_DEBUG (("Succesfully unmounted udi '%s'", udi));
out:
dbus_error_free (&error);
if (options != NULL)
free (options);
if (msg != NULL)
dbus_message_unref (msg);
if (reply != NULL)
dbus_message_unref (reply);
}
static void
unmount_childs (LibHalContext *ctx, const char *udi)
{
DBusError error;
int num_volumes;
char **volumes;
dbus_error_init (&error);
if ((volumes = libhal_manager_find_device_string_match (
ctx, "block.storage_device", udi, &num_volumes, &error)) != NULL) {
dbus_error_free (&error);
int i;
for (i = 0; i < num_volumes; i++) {
char *vol_udi;
vol_udi = volumes[i];
if (libhal_device_get_property_bool (ctx, vol_udi, "block.is_volume", &error)) {
dbus_error_free (&error);
if (libhal_device_get_property_bool (ctx, vol_udi, "volume.is_mounted", &error)) {
dbus_error_free (&error);
HAL_DEBUG (("Forcing unmount of child '%s'", vol_udi));
force_unmount (ctx, vol_udi);
}
}
}
libhal_free_string_array (volumes);
}
my_dbus_error_free (&error);
}
static dbus_bool_t
is_mounted (const char *device_file)
{
FILE *f;
dbus_bool_t rc = FALSE;
struct mnttab mp;
struct mnttab mpref;
if ((f = fopen ("/etc/mnttab", "r")) == NULL)
return rc;
bzero(&mp, sizeof (mp));
bzero(&mpref, sizeof (mpref));
mpref.mnt_special = (char *)device_file;
if (getmntany(f, &mp, &mpref) == 0) {
rc = TRUE;
}
fclose (f);
return rc;
}
void
close_device (int *fd)
{
if (*fd > 0) {
close (*fd);
*fd = -1;
}
}
void
drop_privileges ()
{
priv_set_t *pPrivSet = NULL;
priv_set_t *lPrivSet = NULL;
if ((pPrivSet = priv_str_to_set("basic", ",", NULL)) == NULL) {
return;
}
(void) priv_delset(pPrivSet, PRIV_FILE_LINK_ANY);
(void) priv_delset(pPrivSet, PRIV_PROC_INFO);
(void) priv_delset(pPrivSet, PRIV_PROC_SESSION);
(void) priv_addset(pPrivSet, PRIV_FILE_DAC_READ);
(void) priv_addset(pPrivSet, PRIV_SYS_CONFIG);
if (setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) {
return;
}
if ((lPrivSet = priv_allocset()) == NULL) {
return;
}
priv_emptyset(lPrivSet);
if (setppriv(PRIV_SET, PRIV_LIMIT, lPrivSet) != 0) {
return;
}
priv_freeset(lPrivSet);
}
int
main (int argc, char *argv[])
{
char *device_file, *raw_device_file;
DBusError error;
char *bus;
char *drive_type;
int state, last_state;
int fd = -1;
if ((udi = getenv ("UDI")) == NULL)
goto out;
if ((device_file = getenv ("HAL_PROP_BLOCK_DEVICE")) == NULL)
goto out;
if ((raw_device_file = getenv ("HAL_PROP_BLOCK_SOLARIS_RAW_DEVICE")) == NULL)
goto out;
if ((bus = getenv ("HAL_PROP_STORAGE_BUS")) == NULL)
goto out;
if ((drive_type = getenv ("HAL_PROP_STORAGE_DRIVE_TYPE")) == NULL)
goto out;
if ((devfs_path = getenv ("HAL_PROP_SOLARIS_DEVFS_PATH")) == NULL)
goto out;
drop_privileges ();
setup_logger ();
sysevent_init ();
dbus_error_init (&error);
if ((ctx = libhal_ctx_init_direct (&error)) == NULL) {
goto out;
}
my_dbus_error_free (&error);
if (!libhal_device_addon_is_ready (ctx, udi, &error)) {
goto out;
}
my_dbus_error_free (&error);
printf ("Doing addon-storage for %s (bus %s) (drive_type %s) (udi %s)\n", device_file, bus, drive_type, udi);
last_state = state = DKIO_NONE;
for (;;) {
if (is_mounted (device_file)) {
close_device (&fd);
sleep (SLEEP_PERIOD);
} else if ((fd < 0) && ((fd = open (raw_device_file, O_RDONLY | O_NONBLOCK)) < 0)) {
HAL_DEBUG (("open failed for %s: %s", raw_device_file, strerror (errno)));
sleep (SLEEP_PERIOD);
} else {
if (ioctl (fd, DKIOCSTATE, &state) == 0) {
if (state == last_state) {
HAL_DEBUG (("state has not changed %d %s", state, device_file));
continue;
} else {
HAL_DEBUG (("new state %d %s", state, device_file));
}
switch (state) {
case DKIO_EJECTED:
HAL_DEBUG (("Media removal detected on %s", device_file));
last_state = state;
libhal_device_set_property_bool (ctx, udi, "storage.removable.media_available", FALSE, &error);
my_dbus_error_free (&error);
unmount_childs (ctx, udi);
libhal_device_rescan (ctx, udi, &error);
my_dbus_error_free (&error);
break;
case DKIO_INSERTED:
HAL_DEBUG (("Media insertion detected on %s", device_file));
last_state = state;
libhal_device_set_property_bool (ctx, udi, "storage.removable.media_available", TRUE, &error);
my_dbus_error_free (&error);
libhal_device_rescan (ctx, udi, &error);
my_dbus_error_free (&error);
break;
case DKIO_DEV_GONE:
HAL_DEBUG (("Device gone detected on %s", device_file));
last_state = state;
unmount_childs (ctx, udi);
close_device (&fd);
goto out;
case DKIO_NONE:
default:
break;
}
} else {
HAL_DEBUG (("DKIOCSTATE failed: %s\n", strerror(errno)));
sleep (SLEEP_PERIOD);
}
}
}
out:
sysevent_fini ();
if (ctx != NULL) {
my_dbus_error_free (&error);
libhal_ctx_shutdown (ctx, &error);
libhal_ctx_free (ctx);
}
return 0;
}