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