1*18c2aff7Sartem /*************************************************************************** 2*18c2aff7Sartem * 3*18c2aff7Sartem * addon-storage.c : watch removable media state changes 4*18c2aff7Sartem * 5*18c2aff7Sartem * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 6*18c2aff7Sartem * Use is subject to license terms. 7*18c2aff7Sartem * 8*18c2aff7Sartem * Licensed under the Academic Free License version 2.1 9*18c2aff7Sartem * 10*18c2aff7Sartem **************************************************************************/ 11*18c2aff7Sartem 12*18c2aff7Sartem #pragma ident "%Z%%M% %I% %E% SMI" 13*18c2aff7Sartem 14*18c2aff7Sartem #ifdef HAVE_CONFIG_H 15*18c2aff7Sartem # include <config.h> 16*18c2aff7Sartem #endif 17*18c2aff7Sartem 18*18c2aff7Sartem #include <errno.h> 19*18c2aff7Sartem #include <string.h> 20*18c2aff7Sartem #include <strings.h> 21*18c2aff7Sartem #include <stdlib.h> 22*18c2aff7Sartem #include <stdio.h> 23*18c2aff7Sartem #include <sys/ioctl.h> 24*18c2aff7Sartem #include <sys/types.h> 25*18c2aff7Sartem #include <sys/stat.h> 26*18c2aff7Sartem #include <sys/types.h> 27*18c2aff7Sartem #include <sys/wait.h> 28*18c2aff7Sartem #include <fcntl.h> 29*18c2aff7Sartem #include <unistd.h> 30*18c2aff7Sartem #include <sys/mnttab.h> 31*18c2aff7Sartem #include <sys/dkio.h> 32*18c2aff7Sartem #include <priv.h> 33*18c2aff7Sartem 34*18c2aff7Sartem #include <libhal.h> 35*18c2aff7Sartem 36*18c2aff7Sartem #include "../../hald/logger.h" 37*18c2aff7Sartem 38*18c2aff7Sartem #define SLEEP_PERIOD 5 39*18c2aff7Sartem 40*18c2aff7Sartem static void 41*18c2aff7Sartem my_dbus_error_free(DBusError *error) 42*18c2aff7Sartem { 43*18c2aff7Sartem if (dbus_error_is_set(error)) { 44*18c2aff7Sartem dbus_error_free(error); 45*18c2aff7Sartem } 46*18c2aff7Sartem } 47*18c2aff7Sartem 48*18c2aff7Sartem static void 49*18c2aff7Sartem force_unmount (LibHalContext *ctx, const char *udi) 50*18c2aff7Sartem { 51*18c2aff7Sartem DBusError error; 52*18c2aff7Sartem DBusMessage *msg = NULL; 53*18c2aff7Sartem DBusMessage *reply = NULL; 54*18c2aff7Sartem char **options = NULL; 55*18c2aff7Sartem unsigned int num_options = 0; 56*18c2aff7Sartem DBusConnection *dbus_connection; 57*18c2aff7Sartem char *device_file; 58*18c2aff7Sartem 59*18c2aff7Sartem dbus_error_init (&error); 60*18c2aff7Sartem 61*18c2aff7Sartem dbus_connection = libhal_ctx_get_dbus_connection (ctx); 62*18c2aff7Sartem 63*18c2aff7Sartem msg = dbus_message_new_method_call ("org.freedesktop.Hal", udi, 64*18c2aff7Sartem "org.freedesktop.Hal.Device.Volume", 65*18c2aff7Sartem "Unmount"); 66*18c2aff7Sartem if (msg == NULL) { 67*18c2aff7Sartem HAL_DEBUG (("Could not create dbus message for %s", udi)); 68*18c2aff7Sartem goto out; 69*18c2aff7Sartem } 70*18c2aff7Sartem 71*18c2aff7Sartem 72*18c2aff7Sartem options = calloc (1, sizeof (char *)); 73*18c2aff7Sartem if (options == NULL) { 74*18c2aff7Sartem HAL_DEBUG (("Could not allocate options array")); 75*18c2aff7Sartem goto out; 76*18c2aff7Sartem } 77*18c2aff7Sartem 78*18c2aff7Sartem device_file = libhal_device_get_property_string (ctx, udi, "block.device", &error); 79*18c2aff7Sartem if (device_file != NULL) { 80*18c2aff7Sartem libhal_free_string (device_file); 81*18c2aff7Sartem } 82*18c2aff7Sartem dbus_error_free (&error); 83*18c2aff7Sartem 84*18c2aff7Sartem if (!dbus_message_append_args (msg, 85*18c2aff7Sartem DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, num_options, 86*18c2aff7Sartem DBUS_TYPE_INVALID)) { 87*18c2aff7Sartem HAL_DEBUG (("Could not append args to dbus message for %s", udi)); 88*18c2aff7Sartem goto out; 89*18c2aff7Sartem } 90*18c2aff7Sartem 91*18c2aff7Sartem if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, msg, -1, &error))) { 92*18c2aff7Sartem HAL_DEBUG (("Unmount failed for %s: %s : %s\n", udi, error.name, error.message)); 93*18c2aff7Sartem goto out; 94*18c2aff7Sartem } 95*18c2aff7Sartem 96*18c2aff7Sartem if (dbus_error_is_set (&error)) { 97*18c2aff7Sartem HAL_DEBUG (("Unmount failed for %s\n%s : %s\n", udi, error.name, error.message)); 98*18c2aff7Sartem goto out; 99*18c2aff7Sartem } 100*18c2aff7Sartem 101*18c2aff7Sartem HAL_DEBUG (("Succesfully unmounted udi '%s'", udi)); 102*18c2aff7Sartem 103*18c2aff7Sartem out: 104*18c2aff7Sartem dbus_error_free (&error); 105*18c2aff7Sartem if (options != NULL) 106*18c2aff7Sartem free (options); 107*18c2aff7Sartem if (msg != NULL) 108*18c2aff7Sartem dbus_message_unref (msg); 109*18c2aff7Sartem if (reply != NULL) 110*18c2aff7Sartem dbus_message_unref (reply); 111*18c2aff7Sartem } 112*18c2aff7Sartem 113*18c2aff7Sartem static void 114*18c2aff7Sartem unmount_childs (LibHalContext *ctx, const char *udi) 115*18c2aff7Sartem { 116*18c2aff7Sartem DBusError error; 117*18c2aff7Sartem int num_volumes; 118*18c2aff7Sartem char **volumes; 119*18c2aff7Sartem 120*18c2aff7Sartem dbus_error_init (&error); 121*18c2aff7Sartem 122*18c2aff7Sartem /* need to force unmount all partitions */ 123*18c2aff7Sartem if ((volumes = libhal_manager_find_device_string_match ( 124*18c2aff7Sartem ctx, "block.storage_device", udi, &num_volumes, &error)) != NULL) { 125*18c2aff7Sartem dbus_error_free (&error); 126*18c2aff7Sartem int i; 127*18c2aff7Sartem 128*18c2aff7Sartem for (i = 0; i < num_volumes; i++) { 129*18c2aff7Sartem char *vol_udi; 130*18c2aff7Sartem 131*18c2aff7Sartem vol_udi = volumes[i]; 132*18c2aff7Sartem if (libhal_device_get_property_bool (ctx, vol_udi, "block.is_volume", &error)) { 133*18c2aff7Sartem dbus_error_free (&error); 134*18c2aff7Sartem if (libhal_device_get_property_bool (ctx, vol_udi, "volume.is_mounted", &error)) { 135*18c2aff7Sartem dbus_error_free (&error); 136*18c2aff7Sartem HAL_DEBUG (("Forcing unmount of child '%s'", vol_udi)); 137*18c2aff7Sartem force_unmount (ctx, vol_udi); 138*18c2aff7Sartem } 139*18c2aff7Sartem } 140*18c2aff7Sartem } 141*18c2aff7Sartem libhal_free_string_array (volumes); 142*18c2aff7Sartem } 143*18c2aff7Sartem my_dbus_error_free (&error); 144*18c2aff7Sartem } 145*18c2aff7Sartem 146*18c2aff7Sartem /** Check if a filesystem on a special device file is mounted 147*18c2aff7Sartem * 148*18c2aff7Sartem * @param device_file Special device file, e.g. /dev/cdrom 149*18c2aff7Sartem * @return TRUE iff there is a filesystem system mounted 150*18c2aff7Sartem * on the special device file 151*18c2aff7Sartem */ 152*18c2aff7Sartem static dbus_bool_t 153*18c2aff7Sartem is_mounted (const char *device_file) 154*18c2aff7Sartem { 155*18c2aff7Sartem FILE *f; 156*18c2aff7Sartem dbus_bool_t rc = FALSE; 157*18c2aff7Sartem struct mnttab mp; 158*18c2aff7Sartem struct mnttab mpref; 159*18c2aff7Sartem 160*18c2aff7Sartem if ((f = fopen ("/etc/mnttab", "r")) == NULL) 161*18c2aff7Sartem return rc; 162*18c2aff7Sartem 163*18c2aff7Sartem bzero(&mp, sizeof (mp)); 164*18c2aff7Sartem bzero(&mpref, sizeof (mpref)); 165*18c2aff7Sartem mpref.mnt_special = (char *)device_file; 166*18c2aff7Sartem if (getmntany(f, &mp, &mpref) == 0) { 167*18c2aff7Sartem rc = TRUE; 168*18c2aff7Sartem } 169*18c2aff7Sartem 170*18c2aff7Sartem fclose (f); 171*18c2aff7Sartem return rc; 172*18c2aff7Sartem } 173*18c2aff7Sartem 174*18c2aff7Sartem void 175*18c2aff7Sartem close_device (int *fd) 176*18c2aff7Sartem { 177*18c2aff7Sartem if (*fd > 0) { 178*18c2aff7Sartem close (*fd); 179*18c2aff7Sartem *fd = -1; 180*18c2aff7Sartem } 181*18c2aff7Sartem } 182*18c2aff7Sartem 183*18c2aff7Sartem void 184*18c2aff7Sartem drop_privileges () 185*18c2aff7Sartem { 186*18c2aff7Sartem priv_set_t *pPrivSet = NULL; 187*18c2aff7Sartem priv_set_t *lPrivSet = NULL; 188*18c2aff7Sartem 189*18c2aff7Sartem /* 190*18c2aff7Sartem * Start with the 'basic' privilege set and then remove any 191*18c2aff7Sartem * of the 'basic' privileges that will not be needed. 192*18c2aff7Sartem */ 193*18c2aff7Sartem if ((pPrivSet = priv_str_to_set("basic", ",", NULL)) == NULL) { 194*18c2aff7Sartem return; 195*18c2aff7Sartem } 196*18c2aff7Sartem 197*18c2aff7Sartem /* Clear privileges we will not need from the 'basic' set */ 198*18c2aff7Sartem (void) priv_delset(pPrivSet, PRIV_FILE_LINK_ANY); 199*18c2aff7Sartem (void) priv_delset(pPrivSet, PRIV_PROC_INFO); 200*18c2aff7Sartem (void) priv_delset(pPrivSet, PRIV_PROC_SESSION); 201*18c2aff7Sartem 202*18c2aff7Sartem /* to open logindevperm'd devices */ 203*18c2aff7Sartem (void) priv_addset(pPrivSet, PRIV_FILE_DAC_READ); 204*18c2aff7Sartem 205*18c2aff7Sartem /* Set the permitted privilege set. */ 206*18c2aff7Sartem if (setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) { 207*18c2aff7Sartem return; 208*18c2aff7Sartem } 209*18c2aff7Sartem 210*18c2aff7Sartem /* Clear the limit set. */ 211*18c2aff7Sartem if ((lPrivSet = priv_allocset()) == NULL) { 212*18c2aff7Sartem return; 213*18c2aff7Sartem } 214*18c2aff7Sartem 215*18c2aff7Sartem priv_emptyset(lPrivSet); 216*18c2aff7Sartem 217*18c2aff7Sartem if (setppriv(PRIV_SET, PRIV_LIMIT, lPrivSet) != 0) { 218*18c2aff7Sartem return; 219*18c2aff7Sartem } 220*18c2aff7Sartem 221*18c2aff7Sartem priv_freeset(lPrivSet); 222*18c2aff7Sartem } 223*18c2aff7Sartem 224*18c2aff7Sartem int 225*18c2aff7Sartem main (int argc, char *argv[]) 226*18c2aff7Sartem { 227*18c2aff7Sartem char *udi; 228*18c2aff7Sartem char *device_file, *raw_device_file; 229*18c2aff7Sartem LibHalContext *ctx = NULL; 230*18c2aff7Sartem DBusError error; 231*18c2aff7Sartem char *bus; 232*18c2aff7Sartem char *drive_type; 233*18c2aff7Sartem int state, last_state; 234*18c2aff7Sartem char *support_media_changed_str; 235*18c2aff7Sartem int support_media_changed; 236*18c2aff7Sartem int fd = -1; 237*18c2aff7Sartem 238*18c2aff7Sartem if ((udi = getenv ("UDI")) == NULL) 239*18c2aff7Sartem goto out; 240*18c2aff7Sartem if ((device_file = getenv ("HAL_PROP_BLOCK_DEVICE")) == NULL) 241*18c2aff7Sartem goto out; 242*18c2aff7Sartem if ((raw_device_file = getenv ("HAL_PROP_BLOCK_SOLARIS_RAW_DEVICE")) == NULL) 243*18c2aff7Sartem goto out; 244*18c2aff7Sartem if ((bus = getenv ("HAL_PROP_STORAGE_BUS")) == NULL) 245*18c2aff7Sartem goto out; 246*18c2aff7Sartem if ((drive_type = getenv ("HAL_PROP_STORAGE_DRIVE_TYPE")) == NULL) 247*18c2aff7Sartem goto out; 248*18c2aff7Sartem 249*18c2aff7Sartem drop_privileges (); 250*18c2aff7Sartem 251*18c2aff7Sartem setup_logger (); 252*18c2aff7Sartem 253*18c2aff7Sartem support_media_changed_str = getenv ("HAL_PROP_STORAGE_CDROM_SUPPORT_MEDIA_CHANGED"); 254*18c2aff7Sartem if (support_media_changed_str != NULL && strcmp (support_media_changed_str, "true") == 0) 255*18c2aff7Sartem support_media_changed = TRUE; 256*18c2aff7Sartem else 257*18c2aff7Sartem support_media_changed = FALSE; 258*18c2aff7Sartem 259*18c2aff7Sartem dbus_error_init (&error); 260*18c2aff7Sartem 261*18c2aff7Sartem if ((ctx = libhal_ctx_init_direct (&error)) == NULL) { 262*18c2aff7Sartem goto out; 263*18c2aff7Sartem } 264*18c2aff7Sartem my_dbus_error_free (&error); 265*18c2aff7Sartem 266*18c2aff7Sartem if (!libhal_device_addon_is_ready (ctx, udi, &error)) { 267*18c2aff7Sartem goto out; 268*18c2aff7Sartem } 269*18c2aff7Sartem my_dbus_error_free (&error); 270*18c2aff7Sartem 271*18c2aff7Sartem printf ("Doing addon-storage for %s (bus %s) (drive_type %s) (udi %s)\n", device_file, bus, drive_type, udi); 272*18c2aff7Sartem 273*18c2aff7Sartem last_state = state = DKIO_NONE; 274*18c2aff7Sartem 275*18c2aff7Sartem /* Linux version of this addon attempts to re-open the device O_EXCL 276*18c2aff7Sartem * every 2 seconds, trying to figure out if some other app, 277*18c2aff7Sartem * like a cd burner, is using the device. Aside from questionable 278*18c2aff7Sartem * value of this (apps should use HAL's locked property or/and 279*18c2aff7Sartem * Solaris in_use facility), but also frequent opens/closes 280*18c2aff7Sartem * keeps media constantly spun up. All this needs more thought. 281*18c2aff7Sartem */ 282*18c2aff7Sartem for (;;) { 283*18c2aff7Sartem if (is_mounted (device_file)) { 284*18c2aff7Sartem close_device (&fd); 285*18c2aff7Sartem sleep (SLEEP_PERIOD); 286*18c2aff7Sartem } else if ((fd < 0) && ((fd = open (raw_device_file, O_RDONLY | O_NONBLOCK)) < 0)) { 287*18c2aff7Sartem HAL_DEBUG (("open failed for %s: %s", raw_device_file, strerror (errno))); 288*18c2aff7Sartem sleep (SLEEP_PERIOD); 289*18c2aff7Sartem } else { 290*18c2aff7Sartem /* Check if a disc is in the drive */ 291*18c2aff7Sartem /* XXX initial call always returns inserted 292*18c2aff7Sartem * causing unnecessary rescan - optimize? 293*18c2aff7Sartem */ 294*18c2aff7Sartem if (ioctl (fd, DKIOCSTATE, &state) == 0) { 295*18c2aff7Sartem if (state == last_state) { 296*18c2aff7Sartem HAL_DEBUG (("state has not changed %d %s", state, device_file)); 297*18c2aff7Sartem continue; 298*18c2aff7Sartem } else { 299*18c2aff7Sartem HAL_DEBUG (("new state %d %s", state, device_file)); 300*18c2aff7Sartem } 301*18c2aff7Sartem 302*18c2aff7Sartem switch (state) { 303*18c2aff7Sartem case DKIO_EJECTED: 304*18c2aff7Sartem HAL_DEBUG (("Media removal detected on %s", device_file)); 305*18c2aff7Sartem last_state = state; 306*18c2aff7Sartem 307*18c2aff7Sartem libhal_device_set_property_bool (ctx, udi, "storage.removable.media_available", FALSE, &error); 308*18c2aff7Sartem my_dbus_error_free (&error); 309*18c2aff7Sartem 310*18c2aff7Sartem /* attempt to unmount all childs */ 311*18c2aff7Sartem unmount_childs (ctx, udi); 312*18c2aff7Sartem 313*18c2aff7Sartem /* could have a fs on the main block device; do a rescan to remove it */ 314*18c2aff7Sartem libhal_device_rescan (ctx, udi, &error); 315*18c2aff7Sartem my_dbus_error_free (&error); 316*18c2aff7Sartem break; 317*18c2aff7Sartem 318*18c2aff7Sartem case DKIO_INSERTED: 319*18c2aff7Sartem HAL_DEBUG (("Media insertion detected on %s", device_file)); 320*18c2aff7Sartem last_state = state; 321*18c2aff7Sartem 322*18c2aff7Sartem libhal_device_set_property_bool (ctx, udi, "storage.removable.media_available", TRUE, &error); 323*18c2aff7Sartem my_dbus_error_free (&error); 324*18c2aff7Sartem 325*18c2aff7Sartem /* could have a fs on the main block device; do a rescan to add it */ 326*18c2aff7Sartem libhal_device_rescan (ctx, udi, &error); 327*18c2aff7Sartem my_dbus_error_free (&error); 328*18c2aff7Sartem break; 329*18c2aff7Sartem 330*18c2aff7Sartem case DKIO_DEV_GONE: 331*18c2aff7Sartem HAL_DEBUG (("Device gone detected on %s", device_file)); 332*18c2aff7Sartem last_state = state; 333*18c2aff7Sartem 334*18c2aff7Sartem unmount_childs (ctx, udi); 335*18c2aff7Sartem close_device (&fd); 336*18c2aff7Sartem goto out; 337*18c2aff7Sartem 338*18c2aff7Sartem case DKIO_NONE: 339*18c2aff7Sartem default: 340*18c2aff7Sartem break; 341*18c2aff7Sartem } 342*18c2aff7Sartem } else { 343*18c2aff7Sartem HAL_DEBUG (("DKIOCSTATE failed: %s\n", strerror(errno))); 344*18c2aff7Sartem sleep (SLEEP_PERIOD); 345*18c2aff7Sartem } 346*18c2aff7Sartem } 347*18c2aff7Sartem } 348*18c2aff7Sartem 349*18c2aff7Sartem out: 350*18c2aff7Sartem if (ctx != NULL) { 351*18c2aff7Sartem my_dbus_error_free (&error); 352*18c2aff7Sartem libhal_ctx_shutdown (ctx, &error); 353*18c2aff7Sartem libhal_ctx_free (ctx); 354*18c2aff7Sartem } 355*18c2aff7Sartem 356*18c2aff7Sartem return 0; 357*18c2aff7Sartem } 358