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
is_mounted(const char * device_file)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
get_cdrom_properties_walker(void * arg,int profile,boolean_t is_current)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
get_cdrom_properties(int fd,LibHalChangeSet * cs)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 *
rtrim_copy(char * src,int len)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
get_disk_properties(int fd,LibHalChangeSet * cs)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
check_floppy(int fd,dbus_bool_t * wprot)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) != 0);
272 return (TRUE);
273 } else {
274 return (FALSE);
275 }
276 }
277
278 void
drop_privileges()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
main(int argc,char * argv[])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