1 /*************************************************************************** 2 * 3 * probe-storage.c : Probe for storage devices 4 * 5 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 6 * Use is subject to license terms. 7 * 8 * Licensed under the Academic Free License version 2.1 9 * 10 **************************************************************************/ 11 12 #ifdef HAVE_CONFIG_H 13 # include <config.h> 14 #endif 15 16 #include <errno.h> 17 #include <string.h> 18 #include <strings.h> 19 #include <ctype.h> 20 #include <stdlib.h> 21 #include <stdio.h> 22 #include <sys/ioctl.h> 23 #include <sys/types.h> 24 #include <sys/stat.h> 25 #include <fcntl.h> 26 #include <unistd.h> 27 #include <sys/mnttab.h> 28 #include <sys/fdio.h> 29 #include <sys/scsi/scsi.h> 30 #include <sys/vtoc.h> 31 #include <sys/efi_partition.h> 32 #include <priv.h> 33 34 #include <libhal.h> 35 #include <cdutils.h> 36 #include <fsutils.h> 37 #include <logger.h> 38 39 /** Check if a filesystem on a special device file is mounted 40 * 41 * @param device_file Special device file, e.g. /dev/cdrom 42 * @return TRUE iff there is a filesystem system mounted 43 * on the special device file 44 */ 45 static dbus_bool_t 46 is_mounted (const char *device_file) 47 { 48 FILE *f; 49 dbus_bool_t rc = FALSE; 50 struct mnttab mp; 51 struct mnttab mpref; 52 53 if ((f = fopen ("/etc/mnttab", "r")) == NULL) 54 return rc; 55 56 bzero(&mp, sizeof (mp)); 57 bzero(&mpref, sizeof (mpref)); 58 mpref.mnt_special = (char *)device_file; 59 if (getmntany(f, &mp, &mpref) == 0) { 60 rc = TRUE; 61 } 62 63 fclose (f); 64 return rc; 65 } 66 67 static int 68 get_cdrom_properties_walker (void *arg, int profile, boolean_t is_current) 69 { 70 LibHalChangeSet *cs = (LibHalChangeSet *)arg; 71 72 switch (profile) { 73 case 0x09: 74 libhal_changeset_set_property_bool (cs, "storage.cdrom.cdr", TRUE); 75 break; 76 case 0x0a: 77 libhal_changeset_set_property_bool (cs, "storage.cdrom.cdrw", TRUE); 78 break; 79 case 0x10: 80 libhal_changeset_set_property_bool (cs, "storage.cdrom.dvd", TRUE); 81 break; 82 case 0x11: 83 libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdr", TRUE); 84 break; 85 case 0x12: 86 libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdram", TRUE); 87 break; 88 case 0x13: 89 libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdrw", TRUE); 90 break; 91 case 0x14: 92 libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdrw", TRUE); 93 break; 94 case 0x1a: 95 libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusrw", TRUE); 96 break; 97 case 0x1b: 98 libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusr", TRUE); 99 break; 100 case 0x2b: 101 libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusrdl", TRUE); 102 break; 103 case 0x40: 104 libhal_changeset_set_property_bool (cs, "storage.cdrom.bd", TRUE); 105 break; 106 case 0x41: 107 case 0x42: 108 libhal_changeset_set_property_bool (cs, "storage.cdrom.bdr", TRUE); 109 break; 110 case 0x43: 111 libhal_changeset_set_property_bool (cs, "storage.cdrom.bdre", TRUE); 112 break; 113 case 0x50: 114 libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvd", TRUE); 115 break; 116 case 0x51: 117 libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvdr", TRUE); 118 break; 119 case 0x52: 120 libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvdrw", TRUE); 121 break; 122 } 123 124 return CDUTIL_WALK_CONTINUE; 125 } 126 127 #define WSPLEN 64 128 129 static void 130 get_cdrom_properties (int fd, LibHalChangeSet *cs) 131 { 132 DBusError error; 133 int capabilities; 134 int read_speed, write_speed; 135 intlist_t *write_speeds, *write_speeds_mem, *sp; 136 int n_wspeeds; 137 char **wspeeds; 138 char *wspeeds_mem; 139 int i; 140 141 libhal_changeset_set_property_bool (cs, "storage.cdrom.cdr", FALSE); 142 libhal_changeset_set_property_bool (cs, "storage.cdrom.cdrw", FALSE); 143 libhal_changeset_set_property_bool (cs, "storage.cdrom.dvd", FALSE); 144 libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdr", FALSE); 145 libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdrw", FALSE); 146 libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdram", FALSE); 147 libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusr", FALSE); 148 libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusrw", FALSE); 149 libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusrdl", FALSE); 150 libhal_changeset_set_property_bool (cs, "storage.cdrom.bd", FALSE); 151 libhal_changeset_set_property_bool (cs, "storage.cdrom.bdr", FALSE); 152 libhal_changeset_set_property_bool (cs, "storage.cdrom.bdre", FALSE); 153 libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvd", FALSE); 154 libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvdr", FALSE); 155 libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvdrw", FALSE); 156 157 walk_profiles(fd, get_cdrom_properties_walker, cs); 158 159 /* XXX */ 160 libhal_changeset_set_property_bool (cs, "storage.cdrom.support_media_changed", TRUE); 161 162 get_read_write_speeds(fd, &read_speed, &write_speed, &write_speeds, &n_wspeeds, &write_speeds_mem); 163 164 libhal_changeset_set_property_int (cs, "storage.cdrom.read_speed", read_speed); 165 libhal_changeset_set_property_int (cs, "storage.cdrom.write_speed", write_speed); 166 167 if (n_wspeeds <= 0) { 168 wspeeds_mem = NULL; 169 libhal_changeset_set_property_strlist (cs, "storage.cdrom.write_speeds", (const char **)&wspeeds_mem); 170 return; 171 } 172 if ((wspeeds = (char **)calloc(n_wspeeds + 1, sizeof (char *))) == NULL) { 173 free (write_speeds_mem); 174 return; 175 } 176 if ((wspeeds_mem = (char *)calloc(n_wspeeds, WSPLEN)) == NULL) { 177 free (wspeeds); 178 free (write_speeds_mem); 179 return; 180 } 181 for (i = 0; i < n_wspeeds; i++) { 182 wspeeds[i] = &wspeeds_mem[i * WSPLEN]; 183 } 184 185 for (sp = write_speeds, i = 0; sp != NULL; sp = sp->next, i++) { 186 snprintf (wspeeds[i], WSPLEN, "%d", sp->val); 187 } 188 libhal_changeset_set_property_strlist (cs, "storage.cdrom.write_speeds", (const char **)wspeeds); 189 190 free (wspeeds); 191 free (wspeeds_mem); 192 free (write_speeds_mem); 193 } 194 195 /* 196 * Return a copy of a string without trailing spaces. If 'len' is non-zero, 197 * it specifies max length, otherwise the string must be null-terminated. 198 */ 199 char * 200 rtrim_copy(char *src, int len) 201 { 202 char *dst, *p; 203 204 if (len == 0) { 205 len = strlen(src); 206 } 207 if ((dst = calloc(1, len + 1)) != NULL) { 208 strncpy(dst, src, len); 209 p = dst + len - 1; 210 while ((p >= dst) && (isspace(*p))) { 211 *p-- = '\0'; 212 } 213 } 214 return (dst); 215 } 216 217 static void 218 get_disk_properties (int fd, LibHalChangeSet *cs) 219 { 220 struct scsi_inquiry inq; 221 struct uscsi_cmd ucmd; 222 union scsi_cdb cdb; 223 int status; 224 char *s; 225 226 /* INQUIRY */ 227 (void) memset((void *) &inq, 0, sizeof (inq)); 228 (void) memset((void *) &ucmd, 0, sizeof (ucmd)); 229 (void) memset((void *) &cdb, 0, sizeof (union scsi_cdb)); 230 cdb.scc_cmd = SCMD_INQUIRY; 231 FORMG0COUNT(&cdb, sizeof (inq)); 232 ucmd.uscsi_cdb = (caddr_t) & cdb; 233 ucmd.uscsi_cdblen = CDB_GROUP0; 234 ucmd.uscsi_bufaddr = (caddr_t) & inq; 235 ucmd.uscsi_buflen = sizeof (inq); 236 ucmd.uscsi_timeout = 30; 237 ucmd.uscsi_flags = USCSI_READ; 238 status = ioctl(fd, USCSICMD, &ucmd); 239 if (status || ucmd.uscsi_status) { 240 return; 241 } 242 243 if ((s = rtrim_copy(inq.inq_vid, sizeof (inq.inq_vid))) != NULL) { 244 libhal_changeset_set_property_string (cs, "storage.vendor", s); 245 free(s); 246 } 247 if ((s = rtrim_copy(inq.inq_pid, sizeof (inq.inq_pid))) != NULL) { 248 libhal_changeset_set_property_string (cs, "storage.model", s); 249 free(s); 250 } 251 if ((s = rtrim_copy(inq.inq_revision, sizeof (inq.inq_revision))) != NULL) { 252 libhal_changeset_set_property_string (cs, "storage.firmware_revision", s); 253 free(s); 254 } 255 if ((s = rtrim_copy(inq.inq_serial, sizeof (inq.inq_serial))) != NULL) { 256 libhal_changeset_set_property_string (cs, "storage.serial", s); 257 free(s); 258 } 259 } 260 261 /* 262 * returns TRUE if diskette is inserted. 263 * also returns write protection status. 264 */ 265 static dbus_bool_t 266 check_floppy(int fd, dbus_bool_t *wprot) 267 { 268 int chg; 269 270 if ((ioctl(fd, FDGETCHANGE, &chg) == 0) && !(chg & FDGC_CURRENT)) { 271 *wprot = ((chg & FDGC_CURWPROT) != NULL); 272 return (TRUE); 273 } else { 274 return (FALSE); 275 } 276 } 277 278 void 279 drop_privileges () 280 { 281 priv_set_t *pPrivSet = NULL; 282 priv_set_t *lPrivSet = NULL; 283 284 /* 285 * Start with the 'basic' privilege set and then remove any 286 * of the 'basic' privileges that will not be needed. 287 */ 288 if ((pPrivSet = priv_str_to_set("basic", ",", NULL)) == NULL) { 289 return; 290 } 291 292 /* Clear privileges we will not need from the 'basic' set */ 293 (void) priv_delset(pPrivSet, PRIV_FILE_LINK_ANY); 294 (void) priv_delset(pPrivSet, PRIV_PROC_INFO); 295 (void) priv_delset(pPrivSet, PRIV_PROC_SESSION); 296 (void) priv_delset(pPrivSet, PRIV_PROC_EXEC); 297 (void) priv_delset(pPrivSet, PRIV_PROC_FORK); 298 299 /* for uscsi */ 300 (void) priv_addset(pPrivSet, PRIV_SYS_DEVICES); 301 302 /* to open logindevperm'd devices */ 303 (void) priv_addset(pPrivSet, PRIV_FILE_DAC_READ); 304 305 /* Set the permitted privilege set. */ 306 if (setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) { 307 return; 308 } 309 310 /* Clear the limit set. */ 311 if ((lPrivSet = priv_allocset()) == NULL) { 312 return; 313 } 314 315 priv_emptyset(lPrivSet); 316 317 if (setppriv(PRIV_SET, PRIV_LIMIT, lPrivSet) != 0) { 318 return; 319 } 320 321 priv_freeset(lPrivSet); 322 } 323 324 int 325 main (int argc, char *argv[]) 326 { 327 int ret = 1; 328 int fd = -1; 329 int rfd = -1; 330 char *udi; 331 char *device_file; 332 char *raw_device_file; 333 LibHalContext *ctx = NULL; 334 DBusError error; 335 char *drive_type; 336 dbus_bool_t is_cdrom; 337 dbus_bool_t is_floppy; 338 struct dk_minfo minfo; 339 int rdonly; 340 unsigned int block_size = 512; 341 dbus_bool_t only_check_for_media; 342 int got_media = FALSE; 343 dbus_bool_t is_write_protected = FALSE; 344 dbus_bool_t is_mbr = FALSE; 345 dbus_bool_t is_smi = FALSE; 346 dbus_bool_t is_gpt = FALSE; 347 dbus_bool_t is_partitioned = FALSE; 348 dbus_bool_t vtoc_slices = FALSE; 349 int dos_cnt = 0; 350 const char *scheme = ""; 351 struct extvtoc vtoc; 352 dk_gpt_t *gpt; 353 LibHalChangeSet *cs = NULL; 354 355 if ((udi = getenv ("UDI")) == NULL) 356 goto out; 357 if ((device_file = getenv ("HAL_PROP_BLOCK_DEVICE")) == NULL) 358 goto out; 359 if ((raw_device_file = getenv ("HAL_PROP_BLOCK_SOLARIS_RAW_DEVICE")) == NULL) 360 goto out; 361 if ((drive_type = getenv ("HAL_PROP_STORAGE_DRIVE_TYPE")) == NULL) 362 goto out; 363 364 drop_privileges (); 365 366 setup_logger (); 367 368 if (argc == 2 && strcmp (argv[1], "--only-check-for-media") == 0) 369 only_check_for_media = TRUE; 370 else 371 only_check_for_media = FALSE; 372 373 is_cdrom = (strcmp (drive_type, "cdrom") == 0); 374 is_floppy = (strcmp (drive_type, "floppy") == 0); 375 376 dbus_error_init (&error); 377 if ((ctx = libhal_ctx_init_direct (&error)) == NULL) 378 goto out; 379 380 if ((cs = libhal_device_new_changeset (udi)) == NULL) { 381 HAL_DEBUG (("Cannot allocate changeset")); 382 goto out; 383 } 384 385 HAL_DEBUG (("Doing probe-storage for %s (drive_type %s) (udi=%s) (--only-check-for-media==%d)", 386 device_file, drive_type, udi, only_check_for_media)); 387 388 if ((rfd = open (raw_device_file, O_RDONLY | O_NONBLOCK)) < 0) { 389 HAL_DEBUG (("Cannot open %s: %s", raw_device_file, strerror (errno))); 390 goto out; 391 } 392 393 if (!only_check_for_media) { 394 if (strcmp (drive_type, "cdrom") == 0) { 395 get_cdrom_properties (rfd, cs); 396 } else if (strcmp (drive_type, "disk") == 0) { 397 get_disk_properties (rfd, cs); 398 } 399 } 400 401 ret = 0; 402 403 if (is_cdrom) { 404 HAL_DEBUG (("Checking for optical disc on %s", raw_device_file)); 405 got_media = get_media_info(rfd, &minfo); 406 if (!got_media) { 407 goto out_cs; 408 } 409 block_size = minfo.dki_lbsize; 410 /* XXX */ 411 is_write_protected = TRUE; 412 } else if (is_floppy) { 413 HAL_DEBUG (("Checking for floppy on %s", raw_device_file)); 414 if (check_floppy(rfd, &is_write_protected)) { 415 got_media = TRUE; 416 } 417 /* don't look for partitions on floppy */ 418 goto out_cs; 419 } else { 420 got_media = get_media_info(rfd, &minfo); 421 if (!got_media) { 422 goto out_cs; 423 } 424 block_size = minfo.dki_lbsize; 425 if ((ioctl(rfd, DKIOCREADONLY, &rdonly) == 0) && rdonly) { 426 is_write_protected = TRUE; 427 } 428 } 429 430 HAL_DEBUG (("Checking for partitions on %s", device_file)); 431 432 if ((fd = open (device_file, O_RDONLY | O_NONBLOCK)) < 0) { 433 HAL_DEBUG (("Cannot open %s: %s", device_file, strerror (errno))); 434 goto out_cs; 435 } 436 437 dos_cnt = get_num_dos_drives(fd, block_size); 438 is_mbr = (dos_cnt > 0); 439 if (is_mbr) { 440 scheme = "mbr"; 441 } 442 if (read_extvtoc(rfd, &vtoc) >= 0) { 443 if (!vtoc_one_slice_entire_disk(&vtoc)) { 444 is_smi = TRUE; 445 if (!is_mbr) { 446 /* smi within mbr partition is okay */ 447 scheme = "smi"; 448 } 449 vtoc_slices = TRUE; 450 } 451 } else if (!is_cdrom && (efi_alloc_and_read(rfd, &gpt) >= 0)) { 452 /* 453 * Note: for some reason efi_read takes very long on cdroms. 454 * Needs more investigation, skip gpt on cdrom for now. 455 */ 456 is_gpt = TRUE; 457 scheme = "gpt"; 458 efi_free(gpt); 459 } 460 461 out_cs: 462 is_partitioned = is_mbr || is_smi || is_gpt; 463 libhal_changeset_set_property_bool (cs, "storage.no_partitions_hint", !is_partitioned); 464 libhal_changeset_set_property_bool (cs, "block.no_partitions", !is_partitioned); 465 libhal_changeset_set_property_string (cs, "storage.partitioning_scheme", scheme); 466 libhal_changeset_set_property_bool (cs, "storage.solaris.vtoc_slices", vtoc_slices); 467 libhal_changeset_set_property_int (cs, "storage.solaris.num_dos_partitions", dos_cnt); 468 /* XXX should only set for removable drives */ 469 libhal_changeset_set_property_bool (cs, "storage.removable.media_available", got_media); 470 libhal_changeset_set_property_bool (cs, "storage.removable.solaris.read_only", is_write_protected); 471 472 libhal_device_commit_changeset (ctx, cs, &error); 473 474 out: 475 if (cs != NULL) { 476 libhal_device_free_changeset (cs); 477 } 478 if (fd >= 0) { 479 close (fd); 480 } 481 if (rfd >= 0) { 482 close (rfd); 483 } 484 if (ctx != NULL) { 485 if (dbus_error_is_set(&error)) { 486 dbus_error_free (&error); 487 } 488 libhal_ctx_shutdown (ctx, &error); 489 libhal_ctx_free (ctx); 490 } 491 492 return ret; 493 } 494