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

Generated on Sat Feb 7 22:11:47 2004 for HAL by doxygen 1.3.5