Main Page | Modules | Data Structures | File List | Data Fields | Related Pages

block_class_device.c

00001 /***************************************************************************
00002  * CVSID: $Id: block_class_device.c,v 1.14 2004/04/12 20:11:51 joe Exp $
00003  *
00004  * Block device class
00005  *
00006  * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
00007  *
00008  * Licensed under the Academic Free License version 2.0
00009  *
00010  * This program is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00023  *
00024  **************************************************************************/
00025 
00026 #ifdef HAVE_CONFIG_H
00027 #  include <config.h>
00028 #endif
00029 
00030 #define _GNU_SOURCE 1
00031 
00032 #include <ctype.h>
00033 #include <stdio.h>
00034 #include <stdlib.h>
00035 #include <string.h>
00036 #include <getopt.h>
00037 #include <assert.h>
00038 #include <unistd.h>
00039 #include <stdarg.h>
00040 #include <limits.h>
00041 #include <errno.h>
00042 #include <signal.h>
00043 #include <fcntl.h>
00044 #include <sys/types.h>
00045 #include <sys/stat.h>
00046 #include <sys/ioctl.h>
00047 #include <linux/kdev_t.h>
00048 #include <linux/cdrom.h>
00049 #include <linux/fs.h>
00050 #include <glib.h>
00051  
00052 #include "../hald.h"
00053 #include "../hald_dbus.h"
00054 #include "../logger.h"
00055 #include "../device_store.h"
00056 #include "class_device.h"
00057 #include "common.h"
00058  
00059 #include "linux_dvd_rw_utils.h"
00060 
00068 typedef struct {
00069     HalDevice *device;
00070     ClassDeviceHandler *handler;
00071 } AsyncInfo;
00072 
00073 static void
00074 block_class_visit (ClassDeviceHandler *self,
00075            const char *path,
00076            struct sysfs_class_device *class_device,
00077            dbus_bool_t is_probing)
00078 {
00079     HalDevice *d;
00080     char *parent_sysfs_path;
00081     AsyncInfo *ai;
00082 
00083     /* only care about given sysfs class name */
00084     if (strcmp (class_device->classname, "block") != 0)
00085         return;
00086 
00087     d = hal_device_new ();
00088     hal_device_store_add (hald_get_tdl (), d);
00089     hal_device_property_set_string (d, "info.bus", self->hal_class_name);
00090     hal_device_property_set_string (d, "linux.sysfs_path", path);
00091     hal_device_property_set_string (d, "linux.sysfs_path_device", path);
00092 
00093     if (class_device->sysdevice == NULL) {
00094         parent_sysfs_path = get_parent_sysfs_path (path);
00095         hal_device_property_set_bool (d, "block.is_volume", TRUE);
00096     } else {
00097         parent_sysfs_path = class_device->sysdevice->path;
00098         hal_device_property_set_bool (d, "block.is_volume", FALSE);
00099     }
00100 
00101     /* temporary property used for _udev_event() */
00102     hal_device_property_set_string (d, ".udev.sysfs_path", path);
00103     hal_device_property_set_string (d, ".udev.class_name", "block");
00104 
00105     /* Property name we should store the device file in */
00106     hal_device_property_set_string (d, ".target_dev", "block.device");
00107 
00108     /* Ask udev about the device file if we are probing */
00109     if (self->require_device_file && is_probing) {
00110         char dev_file[SYSFS_PATH_MAX];
00111 
00112         if (!class_device_get_device_file (path, dev_file, 
00113                            SYSFS_PATH_MAX)) {
00114             HAL_WARNING (("Couldn't get device file for class "
00115                       "device with sysfs path", path));
00116             return;
00117         }
00118 
00119         /* If we are not probing this function will be called upon
00120          * receiving a dbus event */
00121         self->udev_event (self, d, dev_file);
00122     }
00123 
00124     /* Now find the parent device; this happens asynchronously as it
00125      * might be added later. */
00126     ai = g_new0 (AsyncInfo, 1);
00127     ai->device = d;
00128     ai->handler = self;
00129 
00130     hal_device_store_match_key_value_string_async (
00131         hald_get_gdl (),
00132         "linux.sysfs_path_device",
00133         parent_sysfs_path,
00134         class_device_got_parent_device, ai,
00135         is_probing ? 0 : HAL_LINUX_HOTPLUG_TIMEOUT);
00136 }
00137 
00138 
00139 static char *
00140 strip_space (char *str)
00141 {
00142     int i, len;
00143 
00144     len = strlen (str);
00145     for (i = len - 1; i > 0 && isspace (str[i]); --i)
00146         str[i] = '\0';
00147 
00148     return str;
00149 }
00150 
00151 
00152 static void
00153 cdrom_check(HalDevice *d, const char *device_file)
00154 {
00155     int fd, capabilities;
00156     int read_speed, write_speed;
00157 
00158     /* Check handling */
00159     fd = open (device_file, O_RDONLY | O_NONBLOCK);
00160     if (fd < 0)
00161         return;
00162         
00163     ioctl (fd, CDROM_SET_OPTIONS, CDO_USE_FFLAGS);
00164         
00165     capabilities = ioctl (fd, CDROM_GET_CAPABILITY, 0);
00166     if (capabilities < 0) {
00167         close(fd);
00168         return;
00169     }
00170 
00171     if (capabilities & CDC_CD_R) {
00172         hal_device_add_capability (d, "storage.cdr");
00173         hal_device_property_set_bool (d, "storage.cdr", TRUE);
00174     }
00175     
00176     if (capabilities & CDC_CD_RW) {
00177         hal_device_add_capability (d, "storage.cdrw");
00178         hal_device_property_set_bool (d, "storage.cdrw", TRUE);
00179     }
00180     if (capabilities & CDC_DVD) {
00181         int profile;
00182         
00183         hal_device_add_capability (d, "storage.dvd");
00184         hal_device_property_set_bool (d, "storage.dvd", TRUE);
00185         
00186         profile = get_dvd_r_rw_profile (fd);
00187         HAL_INFO (("profile %d\n", profile));
00188         if (profile == 2) {
00189             hal_device_add_capability (d, "storage.dvdplusr");
00190             hal_device_property_set_bool (d, "storage.dvdplusr", TRUE);
00191             hal_device_add_capability (d, "storage.dvdplusrw");
00192             hal_device_property_set_bool (d, "storage.dvdplusrw", TRUE);
00193         } else if (profile == 0) {
00194             hal_device_add_capability(d, "storage.dvdplusr");
00195             hal_device_property_set_bool(d, "storage.dvdplusr",
00196                          TRUE);
00197         } else if (profile == 1) {
00198             hal_device_add_capability (d, "storage.dvdplusrw");
00199             hal_device_property_set_bool (d, "storage.dvdplusrw", TRUE);
00200         }
00201     }
00202     if (capabilities & CDC_DVD_R) {
00203         hal_device_add_capability (d, "storage.dvdr");
00204         hal_device_property_set_bool (d, "storage.dvdr", TRUE);
00205     }
00206     if (capabilities & CDC_DVD_RAM) {
00207         hal_device_add_capability (d, "storage.dvdram");
00208         hal_device_property_set_bool (d, "storage.dvdram", TRUE);
00209     }
00210     
00211     /* while we're at it, check if we support media changed */
00212     if (ioctl (fd, CDROM_MEDIA_CHANGED) >= 0) {
00213         hal_device_property_set_bool (d, "storage.cdrom.support_media_changed", TRUE);
00214     }
00215     
00216     if (get_read_write_speed(fd, &read_speed, &write_speed) >= 0) {
00217         hal_device_property_set_int (d, "storage.cdrom.read_speed", read_speed);
00218         if (write_speed > 0)
00219             hal_device_property_set_int(d, "storage.cdrom.write_speed", write_speed);
00220     }
00221 
00222     close (fd);
00223 }
00224 
00230 static void
00231 force_unmount (HalDevice * d)
00232 {
00233     const char *device_file;
00234     const char *device_mount_point;
00235     const char *umount_argv[4] = { "/bin/umount", "-l", NULL, NULL };
00236     char *umount_stdout;
00237     char *umount_stderr;
00238     int umount_exitcode;
00239 
00240     device_file = hal_device_property_get_string (d, "block.device");
00241     device_mount_point =
00242         hal_device_property_get_string (d, "block.mount_point");
00243 
00244     umount_argv[2] = device_file;
00245 
00246     if (hal_device_has_property (d, "block.is_volume") &&
00247         hal_device_property_get_bool (d, "block.is_volume") &&
00248         device_mount_point != NULL &&
00249         strlen (device_mount_point) > 0) {
00250         HAL_INFO (("attempting /bin/umount -l %s", device_file));
00251 
00252         /* invoke umount */
00253         if (g_spawn_sync ("/",
00254                   (char **) umount_argv,
00255                   NULL,
00256                   0,
00257                   NULL,
00258                   NULL,
00259                   &umount_stdout,
00260                   &umount_stderr,
00261                   &umount_exitcode, NULL) != TRUE) {
00262             HAL_ERROR (("Couldn't invoke /bin/umount"));
00263         }
00264 
00265         if (umount_exitcode != 0) {
00266             HAL_INFO (("/bin/umount returned %d",
00267                    umount_exitcode));
00268         } else {
00269             /* Tell clients we are going to unmount so they close
00270              * can files - otherwise this unmount is going to stall
00271              *
00272              * One candidate for catching this would be FAM - the
00273              * File Alteration Monitor
00274              *
00275              * Lazy unmount been in Linux since 2.4.11, so we're
00276              * homefree (but other OS'es might not support this)
00277              */
00278             HAL_INFO (("Goint to emit BlockForcedUnmountPartition('%s', '%s', TRUE)", device_file, device_mount_point));
00279             device_send_signal_condition (d,
00280                               "BlockForcedUnmountPartition",
00281                               DBUS_TYPE_STRING,
00282                               device_file,
00283                               DBUS_TYPE_STRING,
00284                               device_mount_point,
00285                               DBUS_TYPE_BOOLEAN,
00286                               TRUE,
00287                               DBUS_TYPE_INVALID);
00288 
00289             /* Woohoo, have to change block.mount_point *afterwards*, other
00290              * wise device_mount_point points to garbage and D-BUS throws
00291              * us off the bus, in fact it's doing exiting with code 1
00292              * for us - not nice
00293              */
00294             device_property_atomic_update_begin ();
00295             hal_device_property_set_string (d, "block.mount_point",
00296                         "");
00297             hal_device_property_set_string (d, "block.fstype", "");
00298             hal_device_property_set_bool (d, "block.is_mounted",
00299                           FALSE);
00300             device_property_atomic_update_end ();
00301         }
00302     }
00303 }
00304 
00310 static void
00311 force_unmount_of_all_childs (HalDevice * d)
00312 {
00313     int fd;
00314     const char *device_file;
00315     GSList *children;
00316 
00317     device_file = hal_device_property_get_string (d, "block.device");
00318 
00319     children = hal_device_store_match_multiple_key_value_string (
00320         hald_get_gdl (),
00321         "info.parent",
00322         hal_device_get_udi (d));
00323 
00324     if (children != NULL) {
00325         GSList *iter;
00326 
00327         for (iter = children; iter != NULL; iter = iter->next) {
00328             HalDevice *child = HAL_DEVICE (iter->data);
00329 
00330             force_unmount (child);
00331 
00332         }       /* for all children */
00333 
00334         g_slist_free (children);
00335 
00336         HAL_INFO (("Rereading partition table for %s",
00337                device_file));
00338         fd = open (device_file, O_RDONLY | O_NONBLOCK);
00339         if (fd != -1) {
00340             ioctl (fd, BLKRRPART);
00341         }
00342         close (fd);
00343 
00344         /* All this work should generate hotplug events to actually
00345          * remove the child devices 
00346          */
00347 
00348         /* Finally, send a single signal on the device - this
00349          * is useful for desktop policy clients such as g-v-m
00350          * such that only a single annoying "dude, you need to
00351          * *stop* the device before pulling it out" popup is
00352          * displayed */
00353         HAL_INFO (("Goint to emit BlockForcedUnmount('%s')",
00354                device_file));
00355         device_send_signal_condition (d, "BlockForcedUnmount",
00356                           DBUS_TYPE_STRING, device_file,
00357                           DBUS_TYPE_INVALID);
00358 
00359     }           /* childs!=NULL */
00360 }
00361 
00362 
00371 static dbus_bool_t
00372 detect_media (HalDevice * d)
00373 {
00374     int fd;
00375     dbus_bool_t is_cdrom;
00376     const char *device_file;
00377     HalDevice *child;
00378 
00379     /* need to be in GDL */
00380     if (!hal_device_store_find (hald_get_gdl (),
00381                     hal_device_get_udi (d)))
00382         return FALSE;
00383         
00384     /* need to have a device and not be a volume */
00385     if (!hal_device_has_property (d, "block.is_volume") ||
00386         !hal_device_has_property (d, "block.device") ||
00387         hal_device_property_get_bool (d, "block.is_volume"))
00388         return FALSE;
00389 
00390     device_file = hal_device_property_get_string (d, "block.device");
00391     if (device_file == NULL)
00392         return FALSE;
00393 
00394     /* we do special treatment for optical discs */
00395     is_cdrom = hal_device_has_property (d, "storage.media") &&
00396         strcmp (hal_device_property_get_string (d, "storage.media"),
00397             "cdrom") == 0
00398         && hal_device_property_get_bool (d,
00399                      "storage.cdrom.support_media_changed");
00400 
00401     if (!is_cdrom) {
00402         fd = open (device_file, O_RDONLY);
00403 
00404         if (fd == -1) {
00405             /* open failed */
00406             HAL_WARNING (("open(\"%s\", O_RDONLY) failed, "
00407                       "errno=%d", device_file, errno));
00408 
00409             if (errno == ENOMEDIUM) {
00410                 force_unmount_of_all_childs (d);
00411             }
00412 
00413         }
00414 
00415     } /* device is not an optical drive */
00416     else {
00417         int drive;
00418         dbus_bool_t got_disc = FALSE;
00419 
00420         fd = open (device_file, O_RDONLY | O_NONBLOCK | O_EXCL);
00421 
00422         if (fd == -1) {
00423             /* open failed */
00424             HAL_WARNING (("open(\"%s\", O_RDONLY|O_NONBLOCK|O_EXCL) failed, " "errno=%d", device_file, errno));
00425             return FALSE;
00426         }
00427 
00428         drive = ioctl (fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
00429         switch (drive) {
00430             /* explicit fallthrough */
00431         case CDS_NO_INFO:
00432         case CDS_NO_DISC:
00433         case CDS_TRAY_OPEN:
00434         case CDS_DRIVE_NOT_READY:
00435             break;
00436 
00437         case CDS_DISC_OK:
00438             got_disc = TRUE;
00439             break;
00440 
00441         default:
00442             break;
00443         }
00444 
00445         if (!got_disc) {
00446             /* we get to here if there is no disc in the drive */
00447             child = hal_device_store_match_key_value_string (
00448                 hald_get_gdl (), "info.parent",
00449                 hal_device_get_udi (d));
00450 
00451             if (child != NULL) {
00452                 HAL_INFO (("Removing volume for optical device %s", device_file));
00453                 hal_device_store_remove (hald_get_gdl (), child);
00454                 g_object_unref (child);
00455 
00456                 close (fd);
00457 
00458                 /* GDL was modified */
00459                 return TRUE;
00460             }
00461 
00462             close (fd);
00463             return FALSE;
00464         }
00465 
00466 
00467         /* got a disc in drive, */
00468 
00469         /* disc in drive; check if the HAL device representing
00470          * the optical drive already got a child (it can have
00471          * only one child)
00472          */
00473 
00474         child = hal_device_store_match_key_value_string (
00475             hald_get_gdl (), "info.parent",
00476             hal_device_get_udi (d));
00477 
00478         if (child == NULL) {
00479             int type;
00480             char udi[256];
00481 
00482             /* nope, add child */
00483             HAL_INFO (("Adding volume for optical device %s",
00484                    device_file));
00485 
00486             child = hal_device_new ();
00487 
00488             /* copy from parent */
00489             hal_device_merge (child, d);
00490 
00491             /* modify some properties */
00492             hal_device_property_set_string (child, "info.parent",
00493                         d->udi);
00494             hal_device_property_set_bool (child, "block.is_volume",
00495                           TRUE);
00496             hal_device_property_set_string (child, "info.capabilities",
00497                         "block volume");
00498             hal_device_property_set_string (child, "info.category",
00499                         "volume");
00500             hal_device_property_set_string (child, "info.product",
00501                         "Disc");
00502 
00503             /* set UDI as appropriate */
00504             strncpy (udi,
00505                  hal_device_property_get_string (d, "info.udi"),
00506                  256);
00507             strncat (udi, "-disc", 256);
00508             hal_device_property_set_string (child, "info.udi", udi);
00509             hal_device_set_udi (child, udi);
00510 
00511             /* set disc media type as appropriate */
00512             type = ioctl (fd, CDROM_DISC_STATUS, CDSL_CURRENT);
00513             close(fd);
00514             switch (type) {
00515             case CDS_AUDIO:     /* audio CD */
00516                 hal_device_property_set_string (child,
00517                         "storage.cdrom.media_type",
00518                         "audio");
00519                 break;
00520             case CDS_MIXED:     /* mixed mode CD */
00521                 hal_device_property_set_string (child,
00522                         "storage.cdrom.media_type",
00523                         "mixed");
00524                 break;
00525             case CDS_DATA_1:    /* data CD */
00526             case CDS_DATA_2:
00527             case CDS_XA_2_1:
00528             case CDS_XA_2_2:
00529                 hal_device_property_set_string (child,
00530                         "storage.cdrom.media_type",
00531                         "data");
00532                 break;
00533             case CDS_NO_INFO:   /* blank or invalid CD */
00534                 hal_device_property_set_string (child,
00535                         "storage.cdrom.media_type",
00536                         "blank");
00537                 break;
00538 
00539             default:        /* should never see this */
00540                 hal_device_property_set_string (child,
00541                         "storage.cdrom.media_type",
00542                         "unknown");
00543                 break;
00544             }
00545 
00546 
00547             /* add new device */
00548             hal_device_store_add (hald_get_gdl (), child);
00549             g_object_unref (child);
00550 
00551             /* GDL was modified */
00552             return TRUE;
00553         }
00554 
00555     }           /* if( is optical drive ) */
00556 
00557     close (fd);
00558 
00559     return FALSE;
00560 }
00561 
00562 static void
00563 block_class_got_udi (ClassDeviceHandler *self,
00564              HalDevice *d,
00565              const char *udi)
00566 {
00567     const char *stordev_udi;
00568     char temp_prefix[] = "/org/freedesktop/Hal/devices/temp";
00569 
00570     /* fixup from setting block.storage_device below to a temporary 
00571      * device */
00572     stordev_udi = hal_device_property_get_string (d, 
00573                               "block.storage_device");
00574 
00575     if (strncmp (stordev_udi, temp_prefix, strlen(temp_prefix)) == 0) {
00576         hal_device_property_set_string (d,
00577                         "block.storage_device",
00578                         udi);
00579     }
00580 }
00581 
00582 static dbus_bool_t
00583 detect_fs_fat (HalDevice *d)
00584 {
00585     int i, len;
00586     int fd;
00587     const char *device_file;
00588     unsigned char data[512];
00589     char label[12];
00590     dbus_bool_t matched = FALSE;
00591 
00592     /* See http://www.win.tue.nl/~aeb/linux/fs/fat/fat-1.html for
00593      * more information
00594      */
00595 
00596     device_file = hal_device_property_get_string (d, "block.device");
00597 
00598     fd = open (device_file, O_RDONLY);
00599     if (fd < 0)
00600         return FALSE;
00601 
00602     if (512 != read (fd, data, 512))
00603         goto out;
00604 
00605     /* signature must be 0x55aa on the last two bytes of the first 512
00606      * byte sector */
00607     if (data[510] != 0x55 && 
00608         data[511] != 0xaa)
00609         goto out;
00610 
00611     memset (label, 0, 12);
00612 
00613     if (data[82] == 'F' &&
00614         data[83] == 'A' &&
00615         data[84] == 'T' &&
00616         data[85] == '3' &&
00617         data[86] == '2' ) {
00618         /* FAT32 */
00619         memcpy (label, data+71, 11);
00620         hal_device_property_set_string (d, "block.fstype", "vfat");
00621         matched = TRUE;
00622     } else if (data[54] == 'F' &&
00623            data[55] == 'A' &&
00624            data[56] == 'T' ) {
00625         /* FAT12/FAT16/FAT */
00626         memcpy (label, data+43, 11);
00627         hal_device_property_set_string (d, "block.fstype", "vfat");
00628         matched = TRUE;
00629     }
00630 
00631     len = strlen (label);
00632     for (i=len-1; i>=0 && isspace (label[i]); --i)
00633         label[i] = '\0';
00634     hal_device_property_set_string (d, "block.volume_label", label);
00635     
00636 out:
00637     close (fd);
00638     return matched;
00639 }
00640 
00641 static void
00642 detect_fs (HalDevice *d)
00643 {
00644     if (detect_fs_fat(d))
00645         return;
00646 }
00647 
00648 static void 
00649 block_class_pre_process (ClassDeviceHandler *self,
00650              HalDevice *d,
00651              const char *sysfs_path,
00652              struct sysfs_class_device *class_device)
00653 {
00654     int major, minor;
00655     HalDevice *parent;
00656     HalDevice *stordev = NULL;
00657     HalDevice *physdev = NULL;
00658     HalDevice *scsidev = NULL;
00659     const char *stordev_udi;
00660     const char *device_file;
00661     dbus_bool_t has_removable_media = FALSE;
00662     dbus_bool_t is_hotpluggable = FALSE;
00663 
00664     parent = hal_device_store_find (hald_get_gdl (),
00665                     hal_device_property_get_string (
00666                         d, "info.parent"));
00667     assert (parent != NULL);
00668 
00669     /* add capabilities for device */
00670     hal_device_property_set_string (d, "info.category", "block");
00671     hal_device_add_capability (d, "block");
00672 
00673     class_device_get_major_minor (sysfs_path, &major, &minor);
00674     hal_device_property_set_int (d, "block.major", major);
00675     hal_device_property_set_int (d, "block.minor", minor);
00676 
00677     device_file = hal_device_property_get_string (d, "block.device");
00678 
00679     /* Determine physical device that is backing this block device */
00680     if (hal_device_property_get_bool (d, "block.is_volume")) {
00681         /* Take the block parent */
00682         stordev_udi = hal_device_property_get_string (
00683             parent, "block.storage_device");
00684         stordev = hal_device_store_find (hald_get_gdl (), stordev_udi);
00685     } else {
00686         const char *udi_it;
00687         const char *physdev_udi = NULL;
00688 
00689         /* Set ourselves to be the storage.* keeper */
00690         stordev_udi = d->udi;
00691         stordev = d;
00692 
00693         /* Default */
00694         hal_device_property_set_string (
00695             stordev, "storage.bus", "unknown");
00696 
00697 
00698         /* walk up the device chain to find the physical device, 
00699          * start with our parent. On the way, optionally pick up
00700          * the scsi_device if it exists */
00701         udi_it = parent->udi;
00702 
00703         while (udi_it != NULL) {
00704             HalDevice *d_it;
00705             const char *bus;
00706 
00707             /* Find device */
00708             d_it = hal_device_store_find (hald_get_gdl (), udi_it);
00709             assert (d_it != NULL);
00710 
00711             /* Check info.bus */
00712             bus = hal_device_property_get_string (d_it,"info.bus");
00713 
00714             if (strcmp (bus, "scsi_device") == 0) {
00715                 scsidev = d_it;
00716             }
00717 
00718             if (strcmp (bus, "usb") == 0) {
00719                 physdev = d_it;
00720                 physdev_udi = udi_it;
00721                 is_hotpluggable = TRUE;
00722                 hal_device_property_set_string (
00723                     stordev, "storage.bus", "usb");
00724                                 
00725                 break;
00726             } else if (strcmp (bus, "ieee1394") == 0) {
00727                 physdev = d_it;
00728                 physdev_udi = udi_it;
00729                 is_hotpluggable = TRUE;
00730                 hal_device_property_set_string (
00731                     stordev, "storage.bus", "ieee1394");
00732                 break;
00733             } else if (strcmp (bus, "ide") == 0) {
00734                 physdev = d_it;
00735                 physdev_udi = udi_it;
00736                 hal_device_property_set_string (
00737                     stordev, "storage.bus", "ide");
00738                 break;
00739             }
00740 
00741             /* Go to parent */
00742             udi_it = hal_device_property_get_string (
00743                 d_it, "info.parent");
00744         }
00745 
00746         if (physdev_udi != NULL) {
00747             hal_device_property_set_string (
00748                 stordev, 
00749                 "storage.physical_device",
00750                 physdev_udi);
00751         }
00752     }
00753 
00754     hal_device_property_set_string (d, "block.storage_device",
00755                     stordev_udi);
00756 
00757     if (hal_device_property_get_bool (d, "block.is_volume")) {
00758         /* We are a volume */
00759         find_and_set_physical_device (d);
00760         hal_device_property_set_bool (d, "info.virtual", TRUE);
00761         hal_device_add_capability (d, "volume");
00762         hal_device_property_set_string (d, "info.category", "volume");
00763 
00764         /* block device that is a partition; e.g. a storage volume */
00765 
00767         hal_device_property_set_string (d, "info.product", "Volume");
00768         
00769         /* Detect filesystem and volume label */
00770         detect_fs (d);
00771 
00772         /* Not much to do for volumes; our parent already set the
00773          * appropriate values for the storage device backing us */
00774 
00775         return;
00776     } 
00777 
00778     /* be pessimistic */
00779     hal_device_property_set_bool (stordev, "storage.cdr", FALSE);
00780     hal_device_property_set_bool (stordev, "storage.cdrw", FALSE);
00781     hal_device_property_set_bool (stordev, "storage.dvd", FALSE);
00782     hal_device_property_set_bool (stordev, "storage.dvdr", FALSE);
00783     hal_device_property_set_bool (stordev, "storage.dvdram", FALSE);
00784 
00785     /* We are a disk or cdrom drive; maybe we even offer 
00786      * removable media 
00787      */
00788     hal_device_property_set_string (d, "info.category", "block");
00789 
00790     HAL_INFO (("Bus type is %s!",
00791            hal_device_property_get_string (parent, "info.bus")));
00792 
00793     if (strcmp (hal_device_property_get_string (parent, "info.bus"), 
00794                 "ide") == 0) {
00795         const char *ide_name;
00796         char *model;
00797         char *media;
00798 
00799         ide_name = get_last_element (hal_device_property_get_string
00800                          (d, "linux.sysfs_path"));
00801 
00802         model = read_single_line ("/proc/ide/%s/model", ide_name);
00803         if (model != NULL) {
00804             hal_device_property_set_string (stordev, 
00805                             "storage.model", 
00806                             model);
00807             hal_device_property_set_string (d, 
00808                             "info.product",
00809                             model);
00810         }
00811 
00812 
00813         /* According to the function proc_ide_read_media() in 
00814          * drivers/ide/ide-proc.c in the Linux sources, media
00815          * can only assume "disk", "cdrom", "tape", "floppy", 
00816          * "UNKNOWN"
00817          */
00818         
00823         media = read_single_line ("/proc/ide/%s/media",
00824                       ide_name);
00825         if (media != NULL) {
00826             hal_device_property_set_string (stordev, 
00827                             "storage.media",
00828                             media);
00829             
00830             /* Set for removable media */
00831             if (strcmp (media, "disk") == 0) {
00832                 /* left blank */
00833             } else if (strcmp (media, "cdrom") == 0) {
00834                 has_removable_media = TRUE;
00835             } else if (strcmp (media, "floppy") == 0) {
00836                 has_removable_media = TRUE;
00837             } else if (strcmp (media, "tape") == 0) {
00838                 has_removable_media = TRUE;
00839             }
00840             
00841         }
00842         
00843     } else if (strcmp (hal_device_property_get_string (parent, 
00844                              "info.bus"),
00845                "scsi_device") == 0) {
00846         const char *sysfs_path;
00847         char attr_path[SYSFS_PATH_MAX];
00848         struct sysfs_attribute *attr;
00849         
00850         sysfs_path = hal_device_property_get_string (
00851             d, "linux.sysfs_path");
00852         
00853         snprintf (attr_path, SYSFS_PATH_MAX,
00854               "%s/device/vendor", sysfs_path);
00855         attr = sysfs_open_attribute (attr_path);
00856         if (sysfs_read_attribute (attr) >= 0) {
00857             hal_device_property_set_string (d, "info.vendor",
00858                             strip_space (attr->
00859                                      value));
00860             sysfs_close_attribute (attr);
00861         }
00862         
00863         snprintf (attr_path, SYSFS_PATH_MAX,
00864               "%s/device/model", sysfs_path);
00865         attr = sysfs_open_attribute (attr_path);
00866         if (sysfs_read_attribute (attr) >= 0) {
00867             strip_space (attr->value);
00868             hal_device_property_set_string (d, 
00869                             "info.product",
00870                             attr->value);
00871             hal_device_property_set_string (stordev,
00872                             "storage.model",
00873                             attr->value);
00874             sysfs_close_attribute (attr);
00875         }
00876         
00877         snprintf (attr_path, SYSFS_PATH_MAX,
00878               "%s/device/type", sysfs_path);
00879         attr = sysfs_open_attribute (attr_path);
00880         if (sysfs_read_attribute (attr) >= 0) {
00881             int type = parse_dec (attr->value);
00882             switch (type) {
00883             case 0: /* Disk */
00884                 hal_device_property_set_string (
00885                     stordev, 
00886                     "storage.media", 
00887                     "disk");
00888                 break;
00889             case 1: /* Tape */
00890                 has_removable_media = TRUE;
00891                 hal_device_property_set_string (
00892                     stordev,
00893                     "storage.media", 
00894                     "tape");
00895                 has_removable_media = TRUE;
00896                 break;
00897             case 5: /* CD-ROM */
00898                 hal_device_property_set_string (
00899                     stordev, 
00900                     "storage.media", 
00901                     "cdrom");
00902                 has_removable_media = TRUE;
00903                 break;
00904             default:
00906                 HAL_WARNING (("Don't know how to "
00907                           "handle SCSI type %d", 
00908                           type));
00909             }
00910         }
00911     } else {
00918         hal_device_property_set_string (
00919             stordev, 
00920             "storage.media",
00921             "flash");
00922         
00923         /* guestimate product name */
00924         hal_device_property_set_string (d, "info.product", "Disk");
00925         
00926     }
00927     
00928     
00929     hal_device_property_set_bool (
00930         stordev, 
00931         "storage.removable", 
00932         has_removable_media);
00933     
00934     if (has_removable_media) {
00935         hal_device_add_capability (
00936             stordev, 
00937             "storage.removable");
00938     }
00939 
00940 
00941     if (hal_device_has_property (stordev, "storage.media") &&
00942         strcmp (hal_device_property_get_string (stordev, "storage.media"), 
00943             "cdrom") == 0) {
00944         cdrom_check (stordev, device_file);
00945     }
00946 
00947     hal_device_property_set_string (stordev, "info.category", "storage");
00948     hal_device_add_capability (stordev, "storage");
00949 
00950     hal_device_property_set_bool (stordev, "storage.hotpluggable",
00951                       is_hotpluggable);
00952     if (is_hotpluggable) {
00953         hal_device_add_capability (stordev, "storage.hotpluggable");
00954     }
00955 
00956 
00957 
00958     /* FINALLY, merge information derived from a .fdi file, from the 
00959      * physical device that is backing this block device.
00960      *
00961      * - physdev is either NULL or the physical device (ide,
00962      *   usb, iee1394 etc.) of which we are the offspring
00963      *
00964      * - scsidev is either NULL or the SCSI device inbetween
00965      *   us and the physical device 
00966      */
00967     
00968     /* Merge storage.lun%d.* to storage.* from physical device for the
00969      * appropriate LUN */
00970     if (physdev != NULL) {
00971 
00972         /* Merge storage.* from physdev to stordev */
00973         hal_device_merge_with_rewrite (stordev, physdev, 
00974                            "storage.", "storage.");
00975 
00976         /* If there's a scsi device inbetween merge all 
00977          * storage.lun%d.* properties */
00978         if (scsidev != NULL) {
00979             int lun;
00980             char propname[64];
00981         
00982             lun = hal_device_property_get_int (
00983                 scsidev, "scsi_device.lun");
00984         
00985             /* See 6in1-card-reader.fdi for an example */
00986                     
00987             snprintf (propname, sizeof (propname), 
00988                   "storage_lun%d.", lun);
00989 
00990             hal_device_merge_with_rewrite (stordev, physdev, 
00991                                "storage.", propname);
00992         }
00993     }
00994 
00995     /* check for media on the device */
00996     detect_media (d);
00997 }
00998 
00999 static char *
01000 block_class_compute_udi (HalDevice * d, int append_num)
01001 {
01002         char *format;
01003         static char buf[256];
01004  
01005         if (append_num == -1)
01006                 format = "/org/freedesktop/Hal/devices/block_%d_%d";
01007         else
01008                 format = "/org/freedesktop/Hal/devices/block_%d_%d-%d";
01009  
01010         snprintf (buf, 256, format,
01011                   hal_device_property_get_int (d, "block.major"),
01012                   hal_device_property_get_int (d, "block.minor"), append_num);
01013  
01014         return buf;
01015 }
01016 
01017 
01018 
01019 #define MOUNT_POINT_MAX 256
01020 #define MOUNT_POINT_STRING_SIZE 128
01021 
01023 struct mount_point_s {
01024     int major;                 
01025     int minor;                 
01026     char device[MOUNT_POINT_STRING_SIZE];  
01027     char mount_point[MOUNT_POINT_STRING_SIZE];
01029     char fs_type[MOUNT_POINT_STRING_SIZE]; 
01030 };
01031 
01033 static struct mount_point_s mount_points[MOUNT_POINT_MAX];
01034 
01036 static int num_mount_points;
01037 
01038 static int etc_fd = -1;
01039 
01040 
01046 static void
01047 etc_mtab_process_line (char *s)
01048 {
01049     int i;
01050     char *p;
01051     char *delim = " \t\n";
01052     char buf[256];
01053     char *bufp = buf;
01054     struct stat stat_buf;
01055     int major = 0;
01056     int minor = 0;
01057     char *device = NULL;
01058     char *mount_point = NULL;
01059     char *fs_type = NULL;
01060 
01061     i = 0;
01062     p = strtok_r (s, delim, &bufp);
01063     while (p != NULL) {
01064         /*printf("token: '%s'\n", p); */
01065         switch (i) {
01066         case 0:
01067             if (strcmp (p, "none") == 0)
01068                 return;
01069             if (p[0] != '/')
01070                 return;
01071             device = p;
01072             /* Find major/minor for this device */
01073 
01074             if (stat (p, &stat_buf) != 0) {
01075                 return;
01076             }
01077             major = MAJOR (stat_buf.st_rdev);
01078             minor = MINOR (stat_buf.st_rdev);
01079             break;
01080 
01081         case 1:
01082             mount_point = p;
01083             break;
01084 
01085         case 2:
01086             fs_type = p;
01087             break;
01088 
01089         case 3:
01090             break;
01091 
01092         case 4:
01093             break;
01094 
01095         case 5:
01096             break;
01097         }
01098 
01099         p = strtok_r (NULL, delim, &bufp);
01100         i++;
01101     }
01102 
01106     if (num_mount_points == MOUNT_POINT_MAX)
01107         return;
01108 
01109     mount_points[num_mount_points].major = major;
01110     mount_points[num_mount_points].minor = minor;
01111     strncpy (mount_points[num_mount_points].device, device,
01112          MOUNT_POINT_STRING_SIZE);
01113     strncpy (mount_points[num_mount_points].mount_point, mount_point,
01114          MOUNT_POINT_STRING_SIZE);
01115     strncpy (mount_points[num_mount_points].fs_type, fs_type,
01116          MOUNT_POINT_STRING_SIZE);
01117 
01118     num_mount_points++;
01119 }
01120 
01122 static time_t etc_mtab_mtime = 0;
01123 
01124 
01134 static dbus_bool_t
01135 read_etc_mtab (dbus_bool_t force)
01136 {
01137     int fd;
01138     char buf[256];
01139     FILE *f;
01140     struct stat stat_buf;
01141 
01142     num_mount_points = 0;
01143 
01144     fd = open ("/etc/mtab", O_RDONLY);
01145 
01146     if (fd == -1) {
01147         HAL_ERROR (("Cannot open /etc/mtab"));
01148         return FALSE;
01149     }
01150 
01151     if (fstat (fd, &stat_buf) != 0) {
01152         HAL_ERROR (("Cannot fstat /etc/mtab fd, errno=%d", errno));
01153         return FALSE;
01154     }
01155 
01156     if (!force && etc_mtab_mtime == stat_buf.st_mtime) {
01157         /*printf("No modification, etc_mtab_mtime=%d\n", etc_mtab_mtime); */
01158         return FALSE;
01159     }
01160 
01161     etc_mtab_mtime = stat_buf.st_mtime;
01162 
01163     /*printf("Modification, etc_mtab_mtime=%d\n", etc_mtab_mtime); */
01164 
01165     f = fdopen (fd, "r");
01166 
01167     if (f == NULL) {
01168         HAL_ERROR (("Cannot fdopen /etc/mtab fd"));
01169         return FALSE;
01170     }
01171 
01172     while (!feof (f)) {
01173         if (fgets (buf, 256, f) == NULL)
01174             break;
01175         /*printf("got line: '%s'\n", buf); */
01176         etc_mtab_process_line (buf);
01177     }
01178 
01179     fclose (f);
01180 
01181     close (fd);
01182 
01183     return TRUE;
01184 }
01185 
01186 static void sigio_handler (int sig);
01187 
01189 static dbus_bool_t have_setup_watcher = FALSE;
01190 
01191 static gboolean
01192 foreach_block_device (HalDeviceStore *store, HalDevice *d,
01193               gpointer user_data)
01194 {
01195     const char *bus;
01196     int major, minor;
01197     dbus_bool_t found_mount_point;
01198     struct mount_point_s *mp;
01199     int i;
01200 
01201     bus = hal_device_property_get_string (d, "info.bus");
01202     if (bus == NULL ||
01203         strncmp (bus, "block", 5) != 0 ||
01204         !hal_device_property_get_bool (d, "block.is_volume"))
01205         return TRUE;
01206 
01207     major = hal_device_property_get_int (d, "block.major");
01208     minor = hal_device_property_get_int (d, "block.minor");
01209 
01210     /* Search all mount points */
01211     found_mount_point = FALSE;
01212     for (i = 0; i < num_mount_points; i++) {
01213         mp = &mount_points[i];
01214             
01215         if (mp->major == major && mp->minor == minor) {
01216             const char *existing_block_device;
01217             dbus_bool_t was_mounted;
01218 
01219             HAL_INFO (("%s mounted at %s, major:minor=%d:%d, fstype=%s, udi=%s", mp->device, mp->mount_point, mp->major, mp->minor, mp->fs_type, d->udi));
01220 
01221             device_property_atomic_update_begin ();
01222 
01223             existing_block_device =
01224                 hal_device_property_get_string (d,
01225                                 "block.device");
01226 
01227             was_mounted =
01228                 hal_device_property_get_bool (d,
01229                                   "block.is_mounted");
01230 
01231             /* Yay! Found a mount point; set properties accordingly */
01232             hal_device_property_set_string (d,
01233                             "block.mount_point",
01234                             mp->mount_point);
01235             hal_device_property_set_string (d, "block.fstype",
01236                             mp->fs_type);
01237             hal_device_property_set_bool (d,
01238                               "block.is_mounted",
01239                               TRUE);
01240 
01241             /* only overwrite block.device if it's not set */
01242             if (existing_block_device == NULL ||
01243                 (existing_block_device != NULL &&
01244                  strcmp (existing_block_device,
01245                      "") == 0)) {
01246                 hal_device_property_set_string (d,
01247                                 "block.device",
01248                                 mp->
01249                                 device);
01250             }
01251 
01252             device_property_atomic_update_end ();
01253 
01254             if (!was_mounted) {
01255                 device_send_signal_condition (
01256                     d,
01257                     "BlockMountEvent",
01258                     DBUS_TYPE_STRING,
01259                     hal_device_property_get_string (
01260                         d,
01261                         "block.device"),
01262                     DBUS_TYPE_STRING,
01263                     mp->mount_point,
01264                     DBUS_TYPE_STRING,
01265                     mp->fs_type,
01266                     DBUS_TYPE_INVALID);
01267             }
01268 
01269             found_mount_point = TRUE;
01270             break;
01271         }
01272     }
01273 
01274     /* No mount point found; (possibly) remove all information */
01275     if (!found_mount_point) {
01276         dbus_bool_t was_mounted;
01277 
01278         device_property_atomic_update_begin ();
01279         
01280         was_mounted =
01281             hal_device_property_get_bool (d, "block.is_mounted");
01282 
01283         hal_device_property_set_bool (d, "block.is_mounted",
01284                           FALSE);
01285         hal_device_property_set_string (d, "block.mount_point",
01286                         "");
01287         hal_device_property_set_string (d, "block.fstype", "");
01288 
01289         device_property_atomic_update_end ();
01290 
01291         if (was_mounted) {
01292             device_send_signal_condition (
01293                 d, "BlockUnmountEvent",
01294                 DBUS_TYPE_STRING,
01295                 hal_device_property_get_string (
01296                     d,
01297                     "block.device"),
01298                 DBUS_TYPE_INVALID);
01299         }
01300         
01301     }
01302 
01303     return TRUE;
01304 }
01305 
01312 static void
01313 etc_mtab_process_all_block_devices (dbus_bool_t force)
01314 {
01315     /* Start or continue watching /etc */
01316     if (!have_setup_watcher) {
01317         have_setup_watcher = TRUE;
01318 
01319         signal (SIGIO, sigio_handler);
01320         etc_fd = open ("/etc", O_RDONLY);
01321         fcntl (etc_fd, F_NOTIFY, DN_MODIFY | DN_MULTISHOT);
01322     }
01323 
01324     /* Just return if /etc/mtab wasn't modified */
01325     if (!read_etc_mtab (force))
01326         return;
01327 
01328     HAL_INFO (("/etc/mtab changed, processing all block devices"));
01329 
01330     hal_device_store_foreach (hald_get_gdl (), foreach_block_device, NULL);
01331 }
01332 
01333 
01335 static dbus_bool_t sigio_etc_changed = FALSE;
01336 
01341 static void
01342 sigio_handler (int sig)
01343 {
01344     /* Set a variable instead of handling it now - this is *much* safer
01345      * since this handler must be very careful - man signal for more 
01346      * information 
01347      */
01348 
01349     sigio_etc_changed = TRUE;
01350 }
01351 
01352 
01353 static void
01354 block_class_removed (ClassDeviceHandler* self, 
01355               const char *sysfs_path, 
01356               HalDevice *d)
01357 {
01358     if (hal_device_has_property (d, "block.is_volume")) {
01359         if (hal_device_property_get_bool (d, "block.is_volume")) {
01360             force_unmount (d);
01361         } else {
01362             force_unmount_of_all_childs (d);
01363         }
01364     }
01365 }
01366 
01367 
01368 static gboolean
01369 foreach_detect_media (HalDeviceStore *store, HalDevice *device,
01370               gpointer user_data)
01371 {
01375         if (detect_media (device))
01376             return FALSE;
01377         else
01378             return TRUE;
01379 }
01380 
01381 static void
01382 block_class_tick (ClassDeviceHandler *self)
01383 {
01384     /*HAL_INFO(("entering")); */
01385 
01386     hal_device_store_foreach (hald_get_gdl (), foreach_detect_media, NULL);
01387 
01388     /* check if the SIGIO signal handler delivered something to us */
01389     if (sigio_etc_changed) {
01390         /* acknowledge we got it */
01391         sigio_etc_changed = FALSE;
01392 
01393         HAL_INFO (("Directory /etc changed"));
01394         /* don't force reloading of /etc/mtab */
01395         etc_mtab_process_all_block_devices (FALSE);
01396     }
01397 
01398     /* HAL_INFO (("exiting")); */
01399 }
01400 
01401 static void
01402 block_class_detection_done (ClassDeviceHandler *self)
01403 {
01404     etc_mtab_process_all_block_devices (TRUE);
01405 }
01406 
01408 ClassDeviceHandler block_class_handler = {
01409     class_device_init,                  
01410     block_class_detection_done,         
01411     class_device_shutdown,              
01412     block_class_tick,                   
01413     class_device_accept,                
01414     block_class_visit,                  
01415     block_class_removed,                
01416     class_device_udev_event,            
01417     class_device_get_device_file_target,
01418     block_class_pre_process,            
01419     class_device_post_merge,            
01420     block_class_got_udi,                
01421     block_class_compute_udi,            
01422     "block",                            
01423     "block",                            
01424     TRUE,                               
01425     FALSE                               
01426 };
01427 

Generated on Sat Apr 24 19:57:44 2004 for HAL by doxygen 1.3.6-20040222