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

linux_class_block.c

00001 /***************************************************************************
00002  * CVSID: $Id: linux_class_block.c,v 1.26 2004/03/03 17:56:56 david Exp $
00003  *
00004  * linux_class_block.c : Block device handling on Linux 2.6
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 #include <ctype.h>
00031 #include <stdio.h>
00032 #include <stdlib.h>
00033 #include <string.h>
00034 #include <getopt.h>
00035 #include <assert.h>
00036 #include <unistd.h>
00037 #include <stdarg.h>
00038 #include <errno.h>
00039 #include <sys/types.h>
00040 #include <sys/stat.h>
00041 #include <signal.h>
00042 
00043 #include <glib.h>
00044 
00045 #define _GNU_SOURCE 1
00046 #include <linux/fcntl.h>
00047 #include <linux/kdev_t.h>
00048 #include <linux/cdrom.h>
00049 #include <linux/fs.h>
00050 
00051 #include "../logger.h"
00052 #include "../device_store.h"
00053 #include "../hald.h"
00054 #include "linux_class_block.h"
00055 #include "linux_dvd_rw_utils.h"
00056 
00057 /* fwd decl */
00058 static void etc_mtab_process_all_block_devices (dbus_bool_t force);
00059 static dbus_bool_t detect_media (HalDevice * d);
00060 
00077 static char *
00078 block_compute_udi (HalDevice * d, int append_num)
00079 {
00080     char *format;
00081     static char buf[256];
00082 
00083     if (append_num == -1)
00084         format = "/org/freedesktop/Hal/devices/block_%d_%d";
00085     else
00086         format = "/org/freedesktop/Hal/devices/block_%d_%d-%d";
00087 
00088     snprintf (buf, 256, format,
00089           ds_property_get_int (d, "block.major"),
00090           ds_property_get_int (d, "block.minor"), append_num);
00091 
00092     return buf;
00093 }
00094 
00095 /* fwd decl */
00096 static void visit_class_device_block_got_parent (HalDevice * parent,
00097                          void *data1, void *data2);
00098 
00107 void
00108 visit_class_device_block (const char *path,
00109               struct sysfs_class_device *class_device)
00110 {
00111     HalDevice *d;
00112     char *parent_sysfs_path;
00113     char attr_name[SYSFS_NAME_LEN];
00114     struct sysfs_attribute *cur;
00115     dbus_bool_t is_disk = FALSE;
00116     dbus_bool_t not_partition = FALSE;
00117 
00118     if (sysfs_get_classdev_attr (class_device, "dev") == NULL) {
00119         /* Must have major:minor number before we are interested */
00120         /*HAL_INFO(("Block device with sysfs path %s doesn't have major:minor",
00121            path)); */
00122         return;
00123     }
00124 
00125     d = ds_device_new ();
00126     ds_property_set_string (d, "info.bus", "block");
00127     ds_property_set_string (d, "linux.sysfs_path", path);
00128     ds_property_set_string (d, "linux.sysfs_path_device", path);
00129 
00130     ds_add_capability (d, "block");
00131 
00132     dlist_for_each_data (sysfs_get_classdev_attributes (class_device),
00133                  cur, struct sysfs_attribute) {
00134         if (sysfs_get_name_from_path
00135             (cur->path, attr_name, SYSFS_NAME_LEN) != 0)
00136             continue;
00137 
00138         if (strcmp (attr_name, "dev") == 0) {
00139             int major, minor;
00140             if (sscanf (cur->value, "%d:%d", &major, &minor) ==
00141                 2) {
00142                 is_disk = TRUE;
00143                 ds_property_set_int (d, "block.major",
00144                              major);
00145                 ds_property_set_int (d, "block.minor",
00146                              minor);
00147             }
00148         } else if (strcmp (attr_name, "size") == 0) {
00149             ds_property_set_int (d, "block.size",
00150                          parse_dec (cur->value));
00151         } else if (strcmp (attr_name, "start") == 0) {
00152             ds_property_set_int (d, "block.start",
00153                          parse_dec (cur->value));
00154         } else if (strcmp (attr_name, "range") == 0) {
00155             not_partition = TRUE;
00156         }
00157     }
00158     ds_property_set_bool (d, "block.is_volume", !not_partition);
00159 
00161     ds_property_set_int (d, "block.block_size", 512);
00162 
00163     if (class_device->sysdevice == NULL) {
00164         /* there is no sys device corresponding to us.. this means we
00165          * must be the child of another block device ie. a partition 
00166          */
00167 
00168         /* check if our parent is there; example: if we are sda1 then
00169          * our parent is sda
00170          */
00171 
00172         parent_sysfs_path = get_parent_sysfs_path (path);
00173     } else {
00174         /* There is a sys device corresponding to us; This is normally
00175          * an IDE interface or a SCSI device */
00176 
00177         /* Now find the corresponding physical device */
00178 
00179         parent_sysfs_path = class_device->sysdevice->path;
00180     }
00181 
00182 
00183     /* Find parent; this happens asynchronously as our parent might
00184      * be added later. If we are probing this can't happen so the
00185      * timeout is set to zero in that event..
00186      */
00187     ds_device_async_find_by_key_value_string
00188         ("linux.sysfs_path_device", parent_sysfs_path, TRUE,
00189          visit_class_device_block_got_parent, (void *) d,
00190          /*(void*) parent_sysfs_path */ NULL,
00191          is_probing ? 0 : HAL_LINUX_HOTPLUG_TIMEOUT);
00192 
00193     /*HAL_INFO(("*** finding parent_sysfs_path=%s, 0x%08x, callback=0x%08x", 
00194        parent_sysfs_path, parent_sysfs_path, 
00195        visit_class_device_block_got_parent));
00196      */
00197 }
00198 
00199 static char *
00200 strip_space (char *str)
00201 {
00202     int i, len;
00203 
00204     len = strlen (str);
00205     for (i = len - 1; i > 0 && isspace (str[i]); --i)
00206         str[i] = '\0';
00207 
00208     return str;
00209 }
00210 
00218 static void
00219 visit_class_device_block_got_parent (HalDevice * parent,
00220                      void *data1, void *data2)
00221 {
00222     char *new_udi = NULL;
00223     HalDevice *new_d = NULL;
00224     HalDevice *d = (HalDevice *) data1;
00225 
00226     HAL_INFO (("data2=0x%08x, d=0x%08x, d->udi=%s, parent->udi=%s, parent->in_gdl=%d", data2, d, d->udi, parent != NULL ? parent->udi : "no parent", parent != NULL ? parent->in_gdl : 42));
00227     HAL_INFO (("d: linux.sysfs_path=%s",
00228            ds_property_get_string (d, "linux.sysfs_path")));
00229     HAL_INFO (("d: linux.sysfs_path_device=%s",
00230            ds_property_get_string (d, "linux.sysfs_path_device")));
00231 
00232     if (parent == NULL) {
00233         HAL_WARNING (("No parent for block device!"));
00234         ds_device_destroy (d);
00235         return;
00236     }
00237     ds_property_set_string (d, "info.parent", parent->udi);
00238 
00239 
00240     /* Ask udev about the device file if we are probing.. otherwise 
00241      * we'll just receive a dbus message from udev later */
00242     if (is_probing) {
00243         int i;
00244         const char *path;
00245         int sysfs_mount_path_len;
00246         char sysfs_path_trunc[SYSFS_NAME_LEN];
00247         char *udev_argv[7] =
00248             { udevinfo_path (), "-r", "-q", "name", "-p",
00249             sysfs_path_trunc, NULL
00250         };
00251         char *udev_stdout;
00252         char *udev_stderr;
00253         int udev_exitcode;
00254 
00255         path = ds_property_get_string (d, "linux.sysfs_path");
00256 
00257         /* compute truncated sysfs path */
00258         sysfs_mount_path_len = strlen (sysfs_mount_path);
00259         if (strlen (path) > sysfs_mount_path_len) {
00260             strncpy (sysfs_path_trunc,
00261                  path + sysfs_mount_path_len,
00262                  SYSFS_NAME_LEN);
00263         }
00264         HAL_INFO (("*** sysfs_path_trunc = '%s'",
00265                sysfs_path_trunc));
00266 
00267         /* Now invoke udevinfo */
00268         if (udev_argv[0] == NULL || g_spawn_sync ("/",
00269                               udev_argv,
00270                               NULL,
00271                               0,
00272                               NULL,
00273                               NULL,
00274                               &udev_stdout,
00275                               &udev_stderr,
00276                               &udev_exitcode,
00277                               NULL) != TRUE) {
00278             HAL_ERROR (("Couldn't invoke udevinfo"));
00279             goto error;
00280         }
00281 
00282         if (udev_exitcode != 0) {
00283             HAL_ERROR (("%s returned %d", udevinfo_path (),
00284                     udev_exitcode));
00285             goto error;
00286         }
00287 
00288         /* sanitize string returned by udev */
00289         for (i = 0; udev_stdout[i] != 0; i++) {
00290             if (udev_stdout[i] == '\r'
00291                 || udev_stdout[i] == '\n') {
00292                 udev_stdout[i] = 0;
00293                 break;
00294             }
00295         }
00296 
00297         HAL_INFO (("device file = '%s'", udev_stdout));
00298 
00299         ds_property_set_string (d, "block.device", udev_stdout);
00300 
00302     } else {
00303     error:
00304         /* woow, be careful not to overwrite */
00305         if (!ds_property_exists (d, "block.device")) {
00306             ds_property_set_string (d, "block.device", "");
00307         }
00308     }
00309 
00310 
00311 
00312     if (ds_property_get_bool (d, "block.is_volume")) {
00313         /* We are a volume */
00314         find_and_set_physical_device (d);
00315         ds_property_set_bool (d, "info.virtual", TRUE);
00316         ds_add_capability (d, "volume");
00317         ds_property_set_string (d, "info.category", "volume");
00318 
00319         /* block device that is a partition; e.g. a storage volume */
00320 
00322         ds_property_set_string (d, "info.product", "Volume");
00323 
00324         /* update storage.removable.media_inserted if applicable */
00325 
00326 /*
00327         if( ds_property_get_bool(parent, "storage.removable") )
00328         {
00329             ds_property_set_bool(parent, "storage.removable.media_inserted", 
00330                                  TRUE)
00331         }
00332 */
00333 
00334     } else {
00335         dbus_bool_t removable_media = FALSE;
00336 
00337         /* be pessimistic */
00338         ds_property_set_bool (d, "storage.cdr", FALSE);
00339         ds_property_set_bool (d, "storage.cdrw", FALSE);
00340         ds_property_set_bool (d, "storage.dvd", FALSE);
00341         ds_property_set_bool (d, "storage.dvdr", FALSE);
00342         ds_property_set_bool (d, "storage.dvdram", FALSE);
00343 
00344         /* We are a disk or cdrom drive; maybe we even offer 
00345          * removable media 
00346          */
00347         ds_property_set_string (d, "info.category", "block");
00348 
00349         HAL_INFO (("Bus type is %s!",
00350                ds_property_get_string (parent, "info.bus")));
00351 
00352         if (strcmp
00353             (ds_property_get_string (parent, "info.bus"),
00354              "ide") == 0) {
00355             const char *ide_name;
00356             char *model;
00357             char *media;
00358 
00359             ide_name =
00360                 get_last_element (ds_property_get_string
00361                           (d, "linux.sysfs_path"));
00362 
00363             model =
00364                 read_single_line ("/proc/ide/%s/model",
00365                           ide_name);
00366             if (model != NULL) {
00367                 ds_property_set_string (d, "storage.model",
00368                             model);
00369                 ds_property_set_string (d, "info.product",
00370                             model);
00371             }
00372 
00373 
00374             /* According to the function proc_ide_read_media() in 
00375              * drivers/ide/ide-proc.c in the Linux sources, media
00376              * can only assume "disk", "cdrom", "tape", "floppy", 
00377              * "UNKNOWN"
00378              */
00379 
00384             media =
00385                 read_single_line ("/proc/ide/%s/media",
00386                           ide_name);
00387             if (media != NULL) {
00388                 ds_property_set_string (d, "storage.media",
00389                             media);
00390 
00391                 /* Set for removable media */
00392                 if (strcmp (media, "disk") == 0) {
00393                     ds_add_capability (d, "storage");
00394                     ds_property_set_string (d,
00395                                    "info.category",
00396                                 "storage");
00397                 } else if (strcmp (media, "cdrom") == 0) {
00398                     ds_add_capability (d, "storage");
00399                     ds_add_capability (d,
00400                                "storage.removable");
00401                     ds_property_set_string (d,
00402                                   "info.category",
00403                               "storage.removable");
00404 
00405                     removable_media = TRUE;
00406                 } else if (strcmp (media, "floppy") == 0) {
00407                     ds_add_capability (d, "storage");
00408                     ds_add_capability (d,
00409                               "storage.removable");
00410                     ds_property_set_string (d,
00411                                 "info.category",
00412                                 "storage.removable");
00413                     removable_media = TRUE;
00414                 } else if (strcmp (media, "tape") == 0) {
00415                     ds_add_capability (d, "storage");
00416                     ds_add_capability (d,
00417                               "storage.removable");
00418                     ds_property_set_string (d,
00419                                   "info.category",
00420                               "storage.removable");
00421                     removable_media = TRUE;
00422                 }
00423 
00424             }
00425 
00426         } 
00427         else if (strcmp (ds_property_get_string (parent,"info.bus"),
00428                  "scsi_device") == 0) {
00429             const char *sysfs_path;
00430             char attr_path[SYSFS_PATH_MAX];
00431             struct sysfs_attribute *attr;
00432             
00433             sysfs_path = ds_property_get_string (
00434                 d, 
00435                 "linux.sysfs_path");
00436             
00437             snprintf (attr_path, SYSFS_PATH_MAX,
00438                   "%s/device/vendor", sysfs_path);
00439             
00440             attr = sysfs_open_attribute (attr_path);
00441             
00442             if (sysfs_read_attribute (attr) >= 0) {
00443                 ds_property_set_string (d, "info.vendor",
00444                             strip_space (attr->
00445                                      value));
00446                 sysfs_close_attribute (attr);
00447             }
00448             
00449             snprintf (attr_path, SYSFS_PATH_MAX,
00450                   "%s/device/model", sysfs_path);
00451             
00452             attr = sysfs_open_attribute (attr_path);
00453             
00454             if (sysfs_read_attribute (attr) >= 0) {
00455                 ds_property_set_string (d, "info.product",
00456                             strip_space (attr->
00457                                      value));
00458                 sysfs_close_attribute (attr);
00459             }
00460 
00461             snprintf (attr_path, SYSFS_PATH_MAX,
00462                   "%s/device/type", sysfs_path);
00463 
00464             attr = sysfs_open_attribute (attr_path);
00465             
00466             if (sysfs_read_attribute (attr) >= 0) {
00467                 int type = parse_dec (attr->value);
00468 
00469                 switch (type) {
00470                 case 0: /* Disk */
00471                     ds_add_capability (d, "storage");
00472                     ds_property_set_string (
00473                         d, "info.category", "storage");
00474                     ds_property_set_string (
00475                         d, "storage.media", "disk");
00476                     break;
00477                 case 1: /* Tape */
00478                     ds_add_capability (d, "storage");
00479                     ds_add_capability (
00480                         d, "storage.removable");
00481                     ds_property_set_string (
00482                         d, "info.category",
00483                         "storage.removable");
00484                     ds_property_set_string (
00485                         d,
00486                         "storage.media", "tape");
00487                     removable_media = TRUE;
00488                     break;
00489                 case 5: /* CD-ROM */
00490                     ds_add_capability (d, "storage");
00491                     ds_add_capability (
00492                         d, "storage.removable");
00493                     ds_property_set_string (
00494                         d, "storage.media", "cdrom");
00495                     ds_property_set_string (
00496                         d, "info.category",
00497                         "storage.removable");
00498                     removable_media = TRUE;
00499                     break;
00500                 default:
00502                     HAL_WARNING (("Don't know how to "
00503                               "handle SCSI type %d", 
00504                               type));
00505                 }
00506             }
00507         } else {
00515             ds_property_set_string (d, "storage.media",
00516                         "flash");
00517             
00518             ds_add_capability (d, "storage");
00519             ds_property_set_string (d, "info.category",
00520                         "storage");
00521             
00522             /* guestimate product name */
00523             ds_property_set_string (d, "info.product", "Disk");
00524             
00525         }
00526 
00527         ds_property_set_bool (d, "storage.removable",
00528                       removable_media);
00529 /*
00530         if( removable_media )
00531         {
00532             ds_property_set_bool(d, "storage.removable.media_inserted", 
00533                                  FALSE);
00534         }
00535 */
00536     }
00537 
00538     /* check /etc/mtab, forces reload of the file */
00539     etc_mtab_process_all_block_devices (TRUE);
00540     
00541     new_udi = rename_and_merge (d, block_compute_udi, "block");
00542     if (new_udi != NULL) {
00543         new_d = ds_device_find (new_udi);
00544         if (new_d != NULL) {
00545             linux_class_block_check_if_ready_to_add (new_d);
00546         }
00547     }
00548 }
00549 
00555 void
00556 linux_class_block_check_if_ready_to_add (HalDevice * d)
00557 {
00558     const char *parent;
00559     const char *device_file;
00560 
00561     /* we know, a'priori, that the only thing we possibly get later
00562      * is block.device, so just check for the presence of this
00563      */
00564 
00565     /* but do check that we already got our parent sorted out to avoid
00566      * a race between udev add and find parent */
00567     parent = ds_property_get_string (d, "info.parent");
00568     if (parent == NULL)
00569         return;
00570 
00571     device_file = ds_property_get_string (d, "block.device");
00572     HAL_INFO (("Entering, udi=%s, device_file=%s", d->udi,
00573            device_file));
00574     
00575     if (device_file != NULL && strcmp (device_file, "") != 0) {
00576         const char *media;
00577         
00578         /* now we have block.device (and storage.media since got_parent
00579          * was called) 
00580          */
00581         media = ds_property_get_string (d, "storage.media");
00582         if (media != NULL && strcmp (media, "cdrom") == 0) {
00583             int fd, capabilities;
00584 
00585             /* Check handling */
00586             fd = open (device_file, O_RDONLY | O_NONBLOCK);
00587 
00588             ioctl (fd, CDROM_SET_OPTIONS, CDO_USE_FFLAGS);
00589 
00590             if (fd >= 0) {
00591                 capabilities =
00592                     ioctl (fd, CDROM_GET_CAPABILITY, 0);
00593 
00594                 if (capabilities >= 0) {
00595                     int read_speed, write_speed;
00596 
00597                     if (capabilities & CDC_CD_R) {
00598                         ds_add_capability (
00599                             d,
00600                             "storage.cdr");
00601                         ds_property_set_bool (
00602                             d,
00603                             "storage.cdr", TRUE);
00604                     }
00605                     if (capabilities & CDC_CD_RW) {
00606                         ds_add_capability (
00607                             d,
00608                             "storage.cdrw");
00609                         ds_property_set_bool (
00610                             d,
00611                             "storage.cdrw", TRUE);
00612                     }
00613                     if (capabilities & CDC_DVD) {
00614                         int profile;
00615 
00616                         ds_add_capability (
00617                             d,
00618                             "storage.dvd");
00619                         ds_property_set_bool (
00620                             d,
00621                             "storage.dvd", TRUE);
00622 
00623                         profile =
00624                             get_dvd_r_rw_profile (fd);
00625                         HAL_WARNING (("profile %d\n", profile));
00626                         if (profile == 2) {
00627                             ds_add_capability
00628                                 (d, "storage.dvdplusr");
00629                             ds_property_set_bool
00630                                 (d, "storage.dvdplusr",
00631                                  TRUE);
00632                             ds_add_capability
00633                                 (d, "storage.dvdplusrw");
00634                             ds_property_set_bool
00635                                 (d, "storage.dvdplusrw",
00636                                  TRUE);
00637                         } else if (profile == 0) {
00638                             ds_add_capability
00639                                 (d, "storage.dvdplusr");
00640                             ds_property_set_bool
00641                                 (d, "storage.dvdplusr",
00642                                  TRUE);
00643                         } else if (profile == 1) {
00644                             ds_add_capability
00645                                 (d, "storage.dvdplusrw");
00646                             ds_property_set_bool
00647                                 (d, "storage.dvdplusrw",
00648                                  TRUE);
00649                         }
00650                     }
00651                     if (capabilities & CDC_DVD_R) {
00652                         ds_add_capability (d, "storage.dvdr");
00653                         ds_property_set_bool (d, "storage.dvdr",
00654                                       TRUE);
00655                     }
00656                     if (capabilities & CDC_DVD_RAM) {
00657                         ds_add_capability (d, "storage.dvdram");
00658                         ds_property_set_bool (d, "storage.dvdram",
00659                                       TRUE);
00660                     }
00661 
00662                     /* while we're at it, check if we support media changed */
00663                     if (ioctl (fd, CDROM_MEDIA_CHANGED)
00664                         >= 0) {
00665                         ds_property_set_bool (d, "storage.cdrom.support_media_changed",
00666                                       TRUE);
00667                     }
00668 
00669                     if (get_read_write_speed
00670                         (fd, &read_speed,
00671                          &write_speed) >= 0) {
00672                         ds_property_set_int (d, "storage.cdrom.read_speed",
00673                                      read_speed);
00674                         if (write_speed > 0)
00675                             ds_property_set_int
00676                                 (d, "storage.cdrom.write_speed",
00677                                  write_speed);
00678                     }
00679                 }
00680                 close (fd);
00681             }
00682         }
00683 
00684         if (ds_property_get_bool (d, "block.is_volume")) {
00685             /* the parent block device always carries storage.* */
00686             ds_property_set_string (d, "block.storage_device",
00687                         parent);
00688         } else {
00689             /* if we are not a volume we are the top block device and
00690              * thus also carry the storage properties so we point to
00691              * ourselves (this may change later; e.g. we want the
00692              * physical USB device to carry the storage properties)
00693              */
00694             ds_property_set_string (d, "block.storage_device",
00695                         d->udi);
00696         }
00697 
00698 
00699         ds_gdl_add (d);
00700 
00701         /* check for media on the device */
00702         detect_media (d);
00703     }
00704 }
00705 
00706 
00707 
00708 #define MOUNT_POINT_MAX 256
00709 #define MOUNT_POINT_STRING_SIZE 128
00710 
00712 struct mount_point_s {
00713     int major;                 
00714     int minor;                 
00715     char device[MOUNT_POINT_STRING_SIZE];  
00716     char mount_point[MOUNT_POINT_STRING_SIZE];
00718     char fs_type[MOUNT_POINT_STRING_SIZE]; 
00719 };
00720 
00722 static struct mount_point_s mount_points[MOUNT_POINT_MAX];
00723 
00725 static int num_mount_points;
00726 
00727 static int etc_fd = -1;
00728 
00729 
00735 static void
00736 etc_mtab_process_line (char *s)
00737 {
00738     int i;
00739     char *p;
00740     char *delim = " \t\n";
00741     char buf[256];
00742     char *bufp = buf;
00743     struct stat stat_buf;
00744     int major = 0;
00745     int minor = 0;
00746     char *device = NULL;
00747     char *mount_point = NULL;
00748     char *fs_type = NULL;
00749 
00750     i = 0;
00751     p = strtok_r (s, delim, &bufp);
00752     while (p != NULL) {
00753         /*printf("token: '%s'\n", p); */
00754         switch (i) {
00755         case 0:
00756             if (strcmp (p, "none") == 0)
00757                 return;
00758             if (p[0] != '/')
00759                 return;
00760             device = p;
00761             /* Find major/minor for this device */
00762 
00763             if (stat (p, &stat_buf) != 0) {
00764                 return;
00765             }
00766             major = MAJOR (stat_buf.st_rdev);
00767             minor = MINOR (stat_buf.st_rdev);
00768             break;
00769 
00770         case 1:
00771             mount_point = p;
00772             break;
00773 
00774         case 2:
00775             fs_type = p;
00776             break;
00777 
00778         case 3:
00779             break;
00780 
00781         case 4:
00782             break;
00783 
00784         case 5:
00785             break;
00786         }
00787 
00788         p = strtok_r (NULL, delim, &bufp);
00789         i++;
00790     }
00791 
00795     if (num_mount_points == MOUNT_POINT_MAX)
00796         return;
00797 
00798     mount_points[num_mount_points].major = major;
00799     mount_points[num_mount_points].minor = minor;
00800     strncpy (mount_points[num_mount_points].device, device,
00801          MOUNT_POINT_STRING_SIZE);
00802     strncpy (mount_points[num_mount_points].mount_point, mount_point,
00803          MOUNT_POINT_STRING_SIZE);
00804     strncpy (mount_points[num_mount_points].fs_type, fs_type,
00805          MOUNT_POINT_STRING_SIZE);
00806 
00807     num_mount_points++;
00808 }
00809 
00811 static time_t etc_mtab_mtime = 0;
00812 
00813 
00823 static dbus_bool_t
00824 read_etc_mtab (dbus_bool_t force)
00825 {
00826     int fd;
00827     char buf[256];
00828     FILE *f;
00829     struct stat stat_buf;
00830 
00831     num_mount_points = 0;
00832 
00833     fd = open ("/etc/mtab", O_RDONLY);
00834 
00835     if (fd == -1) {
00836         HAL_ERROR (("Cannot open /etc/mtab"));
00837         return FALSE;
00838     }
00839 
00840     if (fstat (fd, &stat_buf) != 0) {
00841         HAL_ERROR (("Cannot fstat /etc/mtab fd, errno=%d", errno));
00842         return FALSE;
00843     }
00844 
00845     if (!force && etc_mtab_mtime == stat_buf.st_mtime) {
00846         /*printf("No modification, etc_mtab_mtime=%d\n", etc_mtab_mtime); */
00847         return FALSE;
00848     }
00849 
00850     etc_mtab_mtime = stat_buf.st_mtime;
00851 
00852     /*printf("Modification, etc_mtab_mtime=%d\n", etc_mtab_mtime); */
00853 
00854     f = fdopen (fd, "r");
00855 
00856     if (f == NULL) {
00857         HAL_ERROR (("Cannot fdopen /etc/mtab fd"));
00858         return FALSE;
00859     }
00860 
00861     while (!feof (f)) {
00862         if (fgets (buf, 256, f) == NULL)
00863             break;
00864         /*printf("got line: '%s'\n", buf); */
00865         etc_mtab_process_line (buf);
00866     }
00867 
00868     fclose (f);
00869 
00870     close (fd);
00871 
00872     return TRUE;
00873 }
00874 
00875 static void sigio_handler (int sig);
00876 
00878 static dbus_bool_t have_setup_watcher = FALSE;
00879 
00886 static void
00887 etc_mtab_process_all_block_devices (dbus_bool_t force)
00888 {
00889     int i;
00890     const char *bus;
00891     HalDevice *d;
00892     int major, minor;
00893     dbus_bool_t found_mount_point;
00894     struct mount_point_s *mp;
00895     HalDeviceIterator diter;
00896 
00897     /* Start or continue watching /etc */
00898     if (!have_setup_watcher) {
00899         have_setup_watcher = TRUE;
00900 
00901         signal (SIGIO, sigio_handler);
00902         etc_fd = open ("/etc", O_RDONLY);
00903         fcntl (etc_fd, F_NOTIFY, DN_MODIFY | DN_MULTISHOT);
00904     }
00905 
00906     /* Just return if /etc/mtab wasn't modified */
00907     if (!read_etc_mtab (force))
00908         return;
00909 
00910     HAL_INFO (("/etc/mtab changed, processing all block devices"));
00911 
00912     /* Iterate over all HAL devices */
00913     for (ds_device_iter_begin (&diter);
00914          ds_device_iter_has_more (&diter);
00915          ds_device_iter_next (&diter)) {
00916 
00917         d = ds_device_iter_get (&diter);
00918 
00919         bus = ds_property_get_string (d, "info.bus");
00920         if (bus == NULL || strcmp (bus, "block") != 0 ||
00921             !ds_property_get_bool (d, "block.is_volume"))
00922             continue;
00923 
00924         major = ds_property_get_int (d, "block.major");
00925         minor = ds_property_get_int (d, "block.minor");
00926 
00927         /* Search all mount points */
00928         found_mount_point = FALSE;
00929         for (i = 0; i < num_mount_points; i++) {
00930             mp = &mount_points[i];
00931 
00932             if (mp->major == major && mp->minor == minor) {
00933                 const char *existing_block_device;
00934                 dbus_bool_t was_mounted;
00935 
00936                 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));
00937 
00938                 property_atomic_update_begin ();
00939 
00940                 existing_block_device =
00941                     ds_property_get_string (d,
00942                                 "block.device");
00943 
00944                 was_mounted =
00945                     ds_property_get_bool (d,
00946                               "block.is_mounted");
00947 
00948                 /* Yay! Found a mount point; set properties accordingly */
00949                 ds_property_set_string (d,
00950                             "block.mount_point",
00951                             mp->mount_point);
00952                 ds_property_set_string (d, "block.fstype",
00953                             mp->fs_type);
00954                 ds_property_set_bool (d,
00955                               "block.is_mounted",
00956                               TRUE);
00957 
00958                 /* only overwrite block.device if it's not set */
00959                 if (existing_block_device == NULL ||
00960                     (existing_block_device != NULL &&
00961                      strcmp (existing_block_device,
00962                          "") == 0)) {
00963                     ds_property_set_string (d,
00964                                 "block.device",
00965                                 mp->
00966                                 device);
00967                 }
00968 
00969                 property_atomic_update_end ();
00970 
00971                 if (!was_mounted) {
00972                     emit_condition (d,
00973                             "BlockMountEvent",
00974                             DBUS_TYPE_STRING,
00975                             ds_property_get_string
00976                             (d,
00977                              "block.device"),
00978                             DBUS_TYPE_STRING,
00979                             mp->mount_point,
00980                             DBUS_TYPE_STRING,
00981                             mp->fs_type,
00982                             DBUS_TYPE_INVALID);
00983                 }
00984 
00985                 found_mount_point = TRUE;
00986                 break;
00987             }
00988         }
00989 
00990         /* No mount point found; (possibly) remove all information */
00991         if (!found_mount_point) {
00992             dbus_bool_t was_mounted;
00993 
00994             property_atomic_update_begin ();
00995 
00996             was_mounted =
00997                 ds_property_get_bool (d, "block.is_mounted");
00998 
00999             ds_property_set_bool (d, "block.is_mounted",
01000                           FALSE);
01001             ds_property_set_string (d, "block.mount_point",
01002                         "");
01003             ds_property_set_string (d, "block.fstype", "");
01004 
01005             property_atomic_update_end ();
01006 
01007             if (was_mounted) {
01008                 emit_condition (d, "BlockUnmountEvent",
01009                         DBUS_TYPE_STRING,
01010                         ds_property_get_string (d,
01011                                     "block.device"),
01012                         DBUS_TYPE_INVALID);
01013             }
01014 
01015         }
01016     }
01017 }
01018 
01019 
01021 static dbus_bool_t sigio_etc_changed = FALSE;
01022 
01027 static void
01028 sigio_handler (int sig)
01029 {
01030     /* Set a variable instead of handling it now - this is *much* safer
01031      * since this handler must be very careful - man signal for more 
01032      * information 
01033      */
01034 
01035     sigio_etc_changed = TRUE;
01036 }
01037 
01041 void
01042 linux_class_block_init ()
01043 {
01044 }
01045 
01051 static void
01052 force_unmount (HalDevice * d)
01053 {
01054     const char *device_file;
01055     const char *device_mount_point;
01056     const char *umount_argv[4] = { "/bin/umount", "-l", NULL, NULL };
01057     char *umount_stdout;
01058     char *umount_stderr;
01059     int umount_exitcode;
01060 
01061     device_file = ds_property_get_string (d, "block.device");
01062     device_mount_point =
01063         ds_property_get_string (d, "block.mount_point");
01064 
01065     umount_argv[2] = device_file;
01066 
01067     if (ds_property_exists (d, "block.is_volume") &&
01068         ds_property_get_bool (d, "block.is_volume") &&
01069         device_mount_point != NULL &&
01070         strlen (device_mount_point) > 0) {
01071         HAL_INFO (("attempting /bin/umount -l %s", device_file));
01072 
01073         /* invoke umount */
01074         if (g_spawn_sync ("/",
01075                   (char **) umount_argv,
01076                   NULL,
01077                   0,
01078                   NULL,
01079                   NULL,
01080                   &umount_stdout,
01081                   &umount_stderr,
01082                   &umount_exitcode, NULL) != TRUE) {
01083             HAL_ERROR (("Couldn't invoke /bin/umount"));
01084         }
01085 
01086         if (umount_exitcode != 0) {
01087             HAL_INFO (("/bin/umount returned %d",
01088                    umount_exitcode));
01089         } else {
01090             /* Tell clients we are going to unmount so they close
01091              * can files - otherwise this unmount is going to stall
01092              *
01093              * One candidate for catching this would be FAM - the
01094              * File Alteration Monitor
01095              *
01096              * Lazy unmount been in Linux since 2.4.11, so we're
01097              * homefree (but other OS'es might not support this)
01098              */
01099             HAL_INFO (("Goint to emit BlockForcedUnmountPartition('%s', '%s', TRUE)", device_file, device_mount_point));
01100             emit_condition (d, "BlockForcedUnmountPartition",
01101                     DBUS_TYPE_STRING, device_file,
01102                     DBUS_TYPE_STRING,
01103                     device_mount_point,
01104                     DBUS_TYPE_BOOLEAN, TRUE,
01105                     DBUS_TYPE_INVALID);
01106 
01107             /* Woohoo, have to change block.mount_point *afterwards*, other
01108              * wise device_mount_point points to garbage and D-BUS throws
01109              * us off the bus, in fact it's doing exiting with code 1
01110              * for us - not nice
01111              */
01112             property_atomic_update_begin ();
01113             ds_property_set_string (d, "block.mount_point",
01114                         "");
01115             ds_property_set_string (d, "block.fstype", "");
01116             ds_property_set_bool (d, "block.is_mounted",
01117                           FALSE);
01118             property_atomic_update_end ();
01119         }
01120     }
01121 }
01122 
01128 static void
01129 force_unmount_of_all_childs (HalDevice * d)
01130 {
01131     int fd;
01132     int num_childs;
01133     const char *device_file;
01134     HalDevice *child;
01135     HalDevice **childs;
01136 
01137     device_file = ds_property_get_string (d, "block.device");
01138 
01139     childs =
01140         ds_device_find_multiple_by_key_value_string ("info.parent",
01141                              d->udi, TRUE,
01142                              &num_childs);
01143     if (childs != NULL) {
01144         int n;
01145 
01146         for (n = 0; n < num_childs; n++) {
01147             child = childs[n];
01148 
01149             force_unmount (child);
01150 
01151         }       /* for all childs */
01152 
01153         free (childs);
01154 
01155         HAL_INFO (("Rereading partition table for %s",
01156                device_file));
01157         fd = open (device_file, O_RDONLY | O_NONBLOCK);
01158         if (fd != -1) {
01159             ioctl (fd, BLKRRPART);
01160         }
01161         close (fd);
01162 
01163         /* All this work should generate hotplug events to actually
01164          * remove the child devices 
01165          */
01166 
01167         /* Finally, send a single signal on the device - this
01168          * is useful for desktop policy clients such as g-v-m
01169          * such that only a single annoying "dude, you need to
01170          * *stop* the device before pulling it out" popup is
01171          * displayed */
01172         HAL_INFO (("Goint to emit BlockForcedUnmount('%s')",
01173                device_file));
01174         emit_condition (d, "BlockForcedUnmount",
01175                 DBUS_TYPE_STRING, device_file,
01176                 DBUS_TYPE_INVALID);
01177 
01178     }           /* childs!=NULL */
01179 }
01180 
01181 
01182 
01187 void
01188 linux_class_block_removed (HalDevice * d)
01189 {
01190     if (ds_property_exists (d, "block.is_volume")) {
01191         if (ds_property_get_bool (d, "block.is_volume")) {
01192             force_unmount (d);
01193         } else {
01194             force_unmount_of_all_childs (d);
01195         }
01196     }
01197 }
01198 
01207 static dbus_bool_t
01208 detect_media (HalDevice * d)
01209 {
01210     int fd;
01211     dbus_bool_t is_cdrom;
01212     const char *device_file;
01213     HalDevice *child;
01214 
01215     /* need to be in GDL, need to have block.deve and 
01216      * have block.is_volume==FALSE 
01217      */
01218     if (!d->in_gdl ||
01219         (!ds_property_exists (d, "block.is_volume")) ||
01220         (!ds_property_exists (d, "block.device")) ||
01221         ds_property_get_bool (d, "block.is_volume"))
01222         return FALSE;
01223 
01224     device_file = ds_property_get_string (d, "block.device");
01225     if (device_file == NULL)
01226         return FALSE;
01227 
01228     /* we do special treatment for optical discs */
01229     is_cdrom = ds_property_exists (d, "storage.media") &&
01230         strcmp (ds_property_get_string (d, "storage.media"),
01231             "cdrom") == 0
01232         && ds_property_get_bool (d,
01233                      "storage.cdrom.support_media_changed");
01234 
01235     if (!is_cdrom) {
01236         fd = open (device_file, O_RDONLY);
01237 
01238         if (fd == -1) {
01239             /* open failed */
01240             HAL_WARNING (("open(\"%s\", O_RDONLY) failed, "
01241                       "errno=%d", device_file, errno));
01242 
01243             if (errno == ENOMEDIUM) {
01244                 force_unmount_of_all_childs (d);
01245             }
01246 
01247         }
01248 
01249     } /* device is not an optical drive */
01250     else {
01251         int drive;
01252         dbus_bool_t got_disc = FALSE;
01253 
01254         fd = open (device_file, O_RDONLY | O_NONBLOCK | O_EXCL);
01255 
01256         if (fd == -1) {
01257             /* open failed */
01258             HAL_WARNING (("open(\"%s\", O_RDONLY|O_NONBLOCK|O_EXCL) failed, " "errno=%d", device_file, errno));
01259             return FALSE;
01260         }
01261 
01262         drive = ioctl (fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
01263         switch (drive) {
01264             /* explicit fallthrough */
01265         case CDS_NO_INFO:
01266         case CDS_NO_DISC:
01267         case CDS_TRAY_OPEN:
01268         case CDS_DRIVE_NOT_READY:
01269             break;
01270 
01271         case CDS_DISC_OK:
01272             got_disc = TRUE;
01273             break;
01274 
01275         default:
01276             break;
01277         }
01278 
01279         if (!got_disc) {
01280             /* we get to here if there is no disc in the drive */
01281             child =
01282                 ds_device_find_by_key_value_string
01283                 ("info.parent", d->udi, TRUE);
01284 
01285             if (child != NULL) {
01286                 HAL_INFO (("Removing volume for optical device %s", device_file));
01287                 ds_device_destroy (child);
01288 
01289                 close (fd);
01290 
01291                 /* GDL was modified */
01292                 return TRUE;
01293             }
01294 
01295             close (fd);
01296             return FALSE;
01297         }
01298 
01299 
01300         /* got a disc in drive, */
01301 
01302         /* disc in drive; check if the HAL device representing
01303          * the optical drive already got a child (it can have
01304          * only one child)
01305          */
01306 
01307         close (fd);
01308 
01309         child = ds_device_find_by_key_value_string ("info.parent",
01310                                 d->udi, TRUE);
01311         if (child == NULL) {
01312             char udi[256];
01313 
01314             /* nope, add child */
01315             HAL_INFO (("Adding volume for optical device %s",
01316                    device_file));
01317 
01318             child = ds_device_new ();
01319 
01320             /* copy from parent */
01321             ds_device_merge (child, d);
01322 
01323             /* modify some properties */
01324             ds_property_set_string (child, "info.parent",
01325                         d->udi);
01326             ds_property_set_bool (child, "block.is_volume",
01327                           TRUE);
01328             ds_property_set_string (child, "info.capabilities",
01329                         "block volume");
01330             ds_property_set_string (child, "info.category",
01331                         "volume");
01332             ds_property_set_string (child, "info.product",
01333                         "Disc");
01334 
01335             /* set UDI as appropriate */
01336             strncpy (udi,
01337                  ds_property_get_string (d, "info.udi"),
01338                  256);
01339             strncat (udi, "-disc", 256);
01340             ds_property_set_string (child, "info.udi", udi);
01341             ds_device_set_udi (child, udi);
01342 
01343             /* add new device */
01344             ds_gdl_add (child);
01345 
01346             /* GDL was modified */
01347             return TRUE;
01348         }
01349 
01350     }           /* if( is optical drive ) */
01351 
01352     close (fd);
01353 
01354     return FALSE;
01355 }
01356 
01362 static gboolean
01363 media_detect_timer_handler (gpointer data)
01364 {
01365     int fd;
01366     const char *device_file;
01367     HalDevice *d;
01368     HalDeviceIterator iter;
01369 
01370     /*HAL_INFO(("entering")); */
01371 
01372     /* Iterate all devices */
01373     for (ds_device_iter_begin (&iter);
01374          ds_device_iter_has_more (&iter);
01375          ds_device_iter_next (&iter)) {
01376         d = ds_device_iter_get (&iter);
01377 
01381         if (detect_media (d))
01382             break;
01383     }
01384 
01385     /* check if the SIGIO signal handler delivered something to us */
01386     if (sigio_etc_changed) {
01387         /* acknowledge we got it */
01388         sigio_etc_changed = FALSE;
01389 
01390         HAL_INFO (("Directory /etc changed"));
01391         /* don't force reloading of /etc/mtab */
01392         etc_mtab_process_all_block_devices (FALSE);
01393     }
01394 
01395     HAL_INFO (("exiting"));
01396 
01397     return TRUE;
01398 }
01399 
01400 
01405 void
01406 linux_class_block_detection_done ()
01407 {
01408     g_timeout_add (2000, media_detect_timer_handler, NULL);
01409 
01410     etc_mtab_process_all_block_devices (TRUE);
01411 }
01412 
01416 void
01417 linux_class_block_shutdown ()
01418 {
01419 
01420 }

Generated on Thu Mar 11 21:32:22 2004 for HAL by doxygen 1.3.6-20040222