1N/A/***************************************************************************
1N/A *
1N/A * probe-storage.c : Probe for storage devices
1N/A *
1N/A * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
1N/A *
1N/A * Licensed under the Academic Free License version 2.1
1N/A *
1N/A **************************************************************************/
1N/A
1N/A#ifdef HAVE_CONFIG_H
1N/A# include <config.h>
1N/A#endif
1N/A
1N/A#include <errno.h>
1N/A#include <string.h>
1N/A#include <strings.h>
1N/A#include <ctype.h>
1N/A#include <stdlib.h>
1N/A#include <stdio.h>
1N/A#include <sys/ioctl.h>
1N/A#include <sys/types.h>
1N/A#include <sys/stat.h>
1N/A#include <fcntl.h>
1N/A#include <unistd.h>
1N/A#include <sys/mnttab.h>
1N/A#include <sys/scsi/scsi.h>
1N/A#include <sys/vtoc.h>
1N/A#include <sys/efi_partition.h>
1N/A#include <priv.h>
1N/A
1N/A#include <libhal.h>
1N/A#include <cdutils.h>
1N/A#include <fsutils.h>
1N/A#include <logger.h>
1N/A
1N/A/** Check if a filesystem on a special device file is mounted
1N/A *
1N/A * @param device_file Special device file, e.g. /dev/cdrom
1N/A * @return TRUE iff there is a filesystem system mounted
1N/A * on the special device file
1N/A */
1N/Astatic dbus_bool_t
1N/Ais_mounted (const char *device_file)
1N/A{
1N/A FILE *f;
1N/A dbus_bool_t rc = FALSE;
1N/A struct mnttab mp;
1N/A struct mnttab mpref;
1N/A
1N/A if ((f = fopen ("/etc/mnttab", "r")) == NULL)
1N/A return rc;
1N/A
1N/A bzero(&mp, sizeof (mp));
1N/A bzero(&mpref, sizeof (mpref));
1N/A mpref.mnt_special = (char *)device_file;
1N/A if (getmntany(f, &mp, &mpref) == 0) {
1N/A rc = TRUE;
1N/A }
1N/A
1N/A fclose (f);
1N/A return rc;
1N/A}
1N/A
1N/Astatic int
1N/Aget_cdrom_properties_walker (void *arg, int profile, boolean_t is_current)
1N/A{
1N/A LibHalChangeSet *cs = (LibHalChangeSet *)arg;
1N/A
1N/A switch (profile) {
1N/A case 0x09:
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.cdr", TRUE);
1N/A break;
1N/A case 0x0a:
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.cdrw", TRUE);
1N/A break;
1N/A case 0x10:
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.dvd", TRUE);
1N/A break;
1N/A case 0x11:
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdr", TRUE);
1N/A break;
1N/A case 0x12:
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdram", TRUE);
1N/A break;
1N/A case 0x13:
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdrw", TRUE);
1N/A break;
1N/A case 0x14:
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdrw", TRUE);
1N/A break;
1N/A case 0x1a:
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusrw", TRUE);
1N/A break;
1N/A case 0x1b:
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusr", TRUE);
1N/A break;
1N/A case 0x2b:
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusrdl", TRUE);
1N/A break;
1N/A case 0x40:
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.bd", TRUE);
1N/A break;
1N/A case 0x41:
1N/A case 0x42:
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.bdr", TRUE);
1N/A break;
1N/A case 0x43:
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.bdre", TRUE);
1N/A break;
1N/A case 0x50:
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvd", TRUE);
1N/A break;
1N/A case 0x51:
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvdr", TRUE);
1N/A break;
1N/A case 0x52:
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvdrw", TRUE);
1N/A break;
1N/A }
1N/A
1N/A return CDUTIL_WALK_CONTINUE;
1N/A}
1N/A
1N/A#define WSPLEN 64
1N/A
1N/Astatic void
1N/Aget_cdrom_properties (int fd, LibHalChangeSet *cs)
1N/A{
1N/A DBusError error;
1N/A int capabilities;
1N/A int read_speed, write_speed;
1N/A intlist_t *write_speeds, *write_speeds_mem, *sp;
1N/A int n_wspeeds;
1N/A char **wspeeds;
1N/A char *wspeeds_mem;
1N/A int i;
1N/A
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.cdr", FALSE);
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.cdrw", FALSE);
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.dvd", FALSE);
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdr", FALSE);
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdrw", FALSE);
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdram", FALSE);
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusr", FALSE);
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusrw", FALSE);
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusrdl", FALSE);
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.bd", FALSE);
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.bdr", FALSE);
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.bdre", FALSE);
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvd", FALSE);
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvdr", FALSE);
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvdrw", FALSE);
1N/A
1N/A walk_profiles(fd, get_cdrom_properties_walker, cs);
1N/A
1N/A /* XXX */
1N/A libhal_changeset_set_property_bool (cs, "storage.cdrom.support_media_changed", TRUE);
1N/A
1N/A get_read_write_speeds(fd, &read_speed, &write_speed, &write_speeds, &n_wspeeds, &write_speeds_mem);
1N/A
1N/A libhal_changeset_set_property_int (cs, "storage.cdrom.read_speed", read_speed);
1N/A libhal_changeset_set_property_int (cs, "storage.cdrom.write_speed", write_speed);
1N/A
1N/A if (n_wspeeds <= 0) {
1N/A wspeeds_mem = NULL;
1N/A libhal_changeset_set_property_strlist (cs, "storage.cdrom.write_speeds", (const char **)&wspeeds_mem);
1N/A return;
1N/A }
1N/A if ((wspeeds = (char **)calloc(n_wspeeds + 1, sizeof (char *))) == NULL) {
1N/A free (write_speeds_mem);
1N/A return;
1N/A }
1N/A if ((wspeeds_mem = (char *)calloc(n_wspeeds, WSPLEN)) == NULL) {
1N/A free (wspeeds);
1N/A free (write_speeds_mem);
1N/A return;
1N/A }
1N/A for (i = 0; i < n_wspeeds; i++) {
1N/A wspeeds[i] = &wspeeds_mem[i * WSPLEN];
1N/A }
1N/A
1N/A for (sp = write_speeds, i = 0; sp != NULL; sp = sp->next, i++) {
1N/A snprintf (wspeeds[i], WSPLEN, "%d", sp->val);
1N/A }
1N/A libhal_changeset_set_property_strlist (cs, "storage.cdrom.write_speeds", (const char **)wspeeds);
1N/A
1N/A free (wspeeds);
1N/A free (wspeeds_mem);
1N/A free (write_speeds_mem);
1N/A}
1N/A
1N/A/*
1N/A * Return a copy of a string without trailing spaces. If 'len' is non-zero,
1N/A * it specifies max length, otherwise the string must be null-terminated.
1N/A */
1N/Achar *
1N/Artrim_copy(char *src, int len)
1N/A{
1N/A char *dst, *p;
1N/A
1N/A if (len == 0) {
1N/A len = strlen(src);
1N/A }
1N/A if ((dst = calloc(1, len + 1)) != NULL) {
1N/A strncpy(dst, src, len);
1N/A p = dst + len - 1;
1N/A while ((p >= dst) && (isspace(*p))) {
1N/A *p-- = '\0';
1N/A }
1N/A }
1N/A return (dst);
1N/A}
1N/A
1N/Astatic void
1N/Aget_disk_properties (int fd, LibHalChangeSet *cs)
1N/A{
1N/A struct scsi_inquiry inq;
1N/A struct uscsi_cmd ucmd;
1N/A union scsi_cdb cdb;
1N/A int status;
1N/A char *s;
1N/A
1N/A /* INQUIRY */
1N/A (void) memset((void *) &inq, 0, sizeof (inq));
1N/A (void) memset((void *) &ucmd, 0, sizeof (ucmd));
1N/A (void) memset((void *) &cdb, 0, sizeof (union scsi_cdb));
1N/A cdb.scc_cmd = SCMD_INQUIRY;
1N/A FORMG0COUNT(&cdb, sizeof (inq));
1N/A ucmd.uscsi_cdb = (caddr_t) & cdb;
1N/A ucmd.uscsi_cdblen = CDB_GROUP0;
1N/A ucmd.uscsi_bufaddr = (caddr_t) & inq;
1N/A ucmd.uscsi_buflen = sizeof (inq);
1N/A ucmd.uscsi_timeout = 30;
1N/A ucmd.uscsi_flags = USCSI_READ;
1N/A status = ioctl(fd, USCSICMD, &ucmd);
1N/A if (status || ucmd.uscsi_status) {
1N/A return;
1N/A }
1N/A
1N/A if ((s = rtrim_copy(inq.inq_vid, sizeof (inq.inq_vid))) != NULL) {
1N/A libhal_changeset_set_property_string (cs, "storage.vendor", s);
1N/A free(s);
1N/A }
1N/A if ((s = rtrim_copy(inq.inq_pid, sizeof (inq.inq_pid))) != NULL) {
1N/A libhal_changeset_set_property_string (cs, "storage.model", s);
1N/A free(s);
1N/A }
1N/A if ((s = rtrim_copy(inq.inq_revision, sizeof (inq.inq_revision))) != NULL) {
1N/A libhal_changeset_set_property_string (cs, "storage.firmware_revision", s);
1N/A free(s);
1N/A }
1N/A if ((s = rtrim_copy(inq.inq_serial, sizeof (inq.inq_serial))) != NULL) {
1N/A libhal_changeset_set_property_string (cs, "storage.serial", s);
1N/A free(s);
1N/A }
1N/A}
1N/A
1N/Avoid
1N/Adrop_privileges ()
1N/A{
1N/A priv_set_t *pPrivSet = NULL;
1N/A priv_set_t *lPrivSet = NULL;
1N/A
1N/A /*
1N/A * Start with the 'basic' privilege set and then remove any
1N/A * of the 'basic' privileges that will not be needed.
1N/A */
1N/A if ((pPrivSet = priv_str_to_set("basic", ",", NULL)) == NULL) {
1N/A return;
1N/A }
1N/A
1N/A /* Clear privileges we will not need from the 'basic' set */
1N/A (void) priv_delset(pPrivSet, PRIV_FILE_LINK_ANY);
1N/A (void) priv_delset(pPrivSet, PRIV_PROC_INFO);
1N/A (void) priv_delset(pPrivSet, PRIV_PROC_SESSION);
1N/A (void) priv_delset(pPrivSet, PRIV_PROC_EXEC);
1N/A (void) priv_delset(pPrivSet, PRIV_PROC_FORK);
1N/A
1N/A /* for uscsi */
1N/A (void) priv_addset(pPrivSet, PRIV_SYS_DEVICES);
1N/A
1N/A /* to open logindevperm'd devices */
1N/A (void) priv_addset(pPrivSet, PRIV_FILE_DAC_READ);
1N/A
1N/A /* Set the permitted privilege set. */
1N/A if (setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) {
1N/A return;
1N/A }
1N/A
1N/A /* Clear the limit set. */
1N/A if ((lPrivSet = priv_allocset()) == NULL) {
1N/A return;
1N/A }
1N/A
1N/A priv_emptyset(lPrivSet);
1N/A
1N/A if (setppriv(PRIV_SET, PRIV_LIMIT, lPrivSet) != 0) {
1N/A return;
1N/A }
1N/A
1N/A priv_freeset(lPrivSet);
1N/A}
1N/A
1N/Aint
1N/Amain (int argc, char *argv[])
1N/A{
1N/A int ret = 1;
1N/A int fd = -1;
1N/A int rfd = -1;
1N/A char *udi;
1N/A char *device_file;
1N/A char *raw_device_file;
1N/A LibHalContext *ctx = NULL;
1N/A DBusError error;
1N/A char *drive_type;
1N/A dbus_bool_t is_cdrom;
1N/A struct dk_minfo minfo;
1N/A int rdonly;
1N/A unsigned int block_size = 512;
1N/A dbus_bool_t only_check_for_media;
1N/A int got_media = FALSE;
1N/A dbus_bool_t is_write_protected = FALSE;
1N/A dbus_bool_t is_mbr = FALSE;
1N/A dbus_bool_t is_smi = FALSE;
1N/A dbus_bool_t is_gpt = FALSE;
1N/A dbus_bool_t is_partitioned = FALSE;
1N/A dbus_bool_t vtoc_slices = FALSE;
1N/A int dos_cnt = 0;
1N/A const char *scheme = "";
1N/A struct extvtoc vtoc;
1N/A dk_gpt_t *gpt;
1N/A LibHalChangeSet *cs = NULL;
1N/A
1N/A if ((udi = getenv ("UDI")) == NULL)
1N/A goto out;
1N/A if ((device_file = getenv ("HAL_PROP_BLOCK_DEVICE")) == NULL)
1N/A goto out;
1N/A if ((raw_device_file = getenv ("HAL_PROP_BLOCK_SOLARIS_RAW_DEVICE")) == NULL)
1N/A goto out;
1N/A if ((drive_type = getenv ("HAL_PROP_STORAGE_DRIVE_TYPE")) == NULL)
1N/A goto out;
1N/A
1N/A drop_privileges ();
1N/A
1N/A setup_logger ();
1N/A
1N/A if (argc == 2 && strcmp (argv[1], "--only-check-for-media") == 0)
1N/A only_check_for_media = TRUE;
1N/A else
1N/A only_check_for_media = FALSE;
1N/A
1N/A is_cdrom = (strcmp (drive_type, "cdrom") == 0);
1N/A
1N/A dbus_error_init (&error);
1N/A if ((ctx = libhal_ctx_init_direct (&error)) == NULL)
1N/A goto out;
1N/A
1N/A if ((cs = libhal_device_new_changeset (udi)) == NULL) {
1N/A HAL_DEBUG (("Cannot allocate changeset"));
1N/A goto out;
1N/A }
1N/A
1N/A HAL_DEBUG (("Doing probe-storage for %s (drive_type %s) (udi=%s) (--only-check-for-media==%d)",
1N/A device_file, drive_type, udi, only_check_for_media));
1N/A
1N/A if ((rfd = open (raw_device_file, O_RDONLY | O_NONBLOCK)) < 0) {
1N/A HAL_DEBUG (("Cannot open %s: %s", raw_device_file, strerror (errno)));
1N/A goto out;
1N/A }
1N/A
1N/A if (!only_check_for_media) {
1N/A if (strcmp (drive_type, "cdrom") == 0) {
1N/A get_cdrom_properties (rfd, cs);
1N/A } else if (strcmp (drive_type, "disk") == 0) {
1N/A get_disk_properties (rfd, cs);
1N/A }
1N/A }
1N/A
1N/A ret = 0;
1N/A
1N/A if (is_cdrom) {
1N/A HAL_DEBUG (("Checking for optical disc on %s", raw_device_file));
1N/A got_media = get_media_info(rfd, &minfo);
1N/A if (!got_media) {
1N/A goto out_cs;
1N/A }
1N/A block_size = minfo.dki_lbsize;
1N/A /* XXX */
1N/A is_write_protected = TRUE;
1N/A } else {
1N/A got_media = get_media_info(rfd, &minfo);
1N/A if (!got_media) {
1N/A goto out_cs;
1N/A }
1N/A block_size = minfo.dki_lbsize;
1N/A if ((ioctl(rfd, DKIOCREADONLY, &rdonly) == 0) && rdonly) {
1N/A is_write_protected = TRUE;
1N/A }
1N/A }
1N/A
1N/A HAL_DEBUG (("Checking for partitions on %s", device_file));
1N/A
1N/A if ((fd = open (device_file, O_RDONLY | O_NONBLOCK)) < 0) {
1N/A HAL_DEBUG (("Cannot open %s: %s", device_file, strerror (errno)));
1N/A goto out_cs;
1N/A }
1N/A
1N/A dos_cnt = get_num_dos_drives(fd, block_size);
1N/A is_mbr = (dos_cnt > 0);
1N/A if (is_mbr) {
1N/A scheme = "mbr";
1N/A }
1N/A if (read_extvtoc(rfd, &vtoc) >= 0) {
1N/A if (!vtoc_one_slice_entire_disk(&vtoc)) {
1N/A is_smi = TRUE;
1N/A if (!is_mbr) {
1N/A /* smi within mbr partition is okay */
1N/A scheme = "smi";
1N/A }
1N/A vtoc_slices = TRUE;
1N/A }
1N/A } else if (!is_cdrom && (efi_alloc_and_read(rfd, &gpt) >= 0)) {
1N/A /*
1N/A * Note: for some reason efi_read takes very long on cdroms.
1N/A * Needs more investigation, skip gpt on cdrom for now.
1N/A */
1N/A is_gpt = TRUE;
1N/A scheme = "gpt";
1N/A efi_free(gpt);
1N/A }
1N/A
1N/Aout_cs:
1N/A is_partitioned = is_mbr || is_smi || is_gpt;
1N/A libhal_changeset_set_property_bool (cs, "storage.no_partitions_hint", !is_partitioned);
1N/A libhal_changeset_set_property_bool (cs, "block.no_partitions", !is_partitioned);
1N/A libhal_changeset_set_property_string (cs, "storage.partitioning_scheme", scheme);
1N/A libhal_changeset_set_property_bool (cs, "storage.solaris.vtoc_slices", vtoc_slices);
1N/A libhal_changeset_set_property_int (cs, "storage.solaris.num_dos_partitions", dos_cnt);
1N/A /* XXX should only set for removable drives */
1N/A libhal_changeset_set_property_bool (cs, "storage.removable.media_available", got_media);
1N/A libhal_changeset_set_property_bool (cs, "storage.removable.solaris.read_only", is_write_protected);
1N/A
1N/A libhal_device_commit_changeset (ctx, cs, &error);
1N/A
1N/Aout:
1N/A if (cs != NULL) {
1N/A libhal_device_free_changeset (cs);
1N/A }
1N/A if (fd >= 0) {
1N/A close (fd);
1N/A }
1N/A if (rfd >= 0) {
1N/A close (rfd);
1N/A }
1N/A if (ctx != NULL) {
1N/A if (dbus_error_is_set(&error)) {
1N/A dbus_error_free (&error);
1N/A }
1N/A libhal_ctx_shutdown (ctx, &error);
1N/A libhal_ctx_free (ctx);
1N/A }
1N/A
1N/A return ret;
1N/A}