X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=extras%2Fcdrom_id%2Fcdrom_id.c;h=664a00d2c79ed19f329b5c2296132fceda1d09ce;hb=6ac6c3a04ca47df501ba29e4f20337719ad0a776;hp=d59991c6b53a755ef7216aa3feea33e97c269ad7;hpb=24af530a543c70ac59fdd244377d648ef77a0e21;p=elogind.git diff --git a/extras/cdrom_id/cdrom_id.c b/extras/cdrom_id/cdrom_id.c index d59991c6b..664a00d2c 100644 --- a/extras/cdrom_id/cdrom_id.c +++ b/extras/cdrom_id/cdrom_id.c @@ -41,7 +41,7 @@ #include "libudev.h" #include "libudev-private.h" -static int debug; +static bool debug; static void log_fn(struct udev *udev, int priority, const char *file, int line, const char *fn, @@ -156,13 +156,11 @@ struct scsi_cmd { struct sg_io_hdr sg_io; }; -static void scsi_cmd_init(struct udev *udev, struct scsi_cmd *cmd, unsigned char *buf, size_t bufsize) +static void scsi_cmd_init(struct udev *udev, struct scsi_cmd *cmd) { memset(cmd, 0x00, sizeof(struct scsi_cmd)); - memset(buf, 0x00, bufsize); cmd->cgc.quiet = 1; cmd->cgc.sense = &cmd->_sense.s; - memset(&cmd->sg_io, 0, sizeof(cmd->sg_io)); cmd->sg_io.interface_id = 'S'; cmd->sg_io.mx_sb_len = sizeof(cmd->_sense); cmd->sg_io.cmdp = cmd->cgc.cmd; @@ -182,9 +180,13 @@ static int scsi_cmd_run(struct udev *udev, struct scsi_cmd *cmd, int fd, unsigne { int ret = 0; - cmd->sg_io.dxferp = buf; - cmd->sg_io.dxfer_len = bufsize; - cmd->sg_io.dxfer_direction = SG_DXFER_FROM_DEV; + if (bufsize > 0) { + cmd->sg_io.dxferp = buf; + cmd->sg_io.dxfer_len = bufsize; + cmd->sg_io.dxfer_direction = SG_DXFER_FROM_DEV; + } else { + cmd->sg_io.dxfer_direction = SG_DXFER_NONE; + } if (ioctl(fd, SG_IO, &cmd->sg_io)) return -1; @@ -200,6 +202,39 @@ static int scsi_cmd_run(struct udev *udev, struct scsi_cmd *cmd, int fd, unsigne return ret; } +static int media_lock(struct udev *udev, int fd, bool lock) +{ + int err; + + /* disable the kernel's lock logic */ + err = ioctl(fd, CDROM_CLEAR_OPTIONS, CDO_LOCK); + if (err < 0) + info(udev, "CDROM_CLEAR_OPTIONS, CDO_LOCK failed\n"); + + err = ioctl(fd, CDROM_LOCKDOOR, lock ? 1 : 0); + if (err < 0) + info(udev, "CDROM_LOCKDOOR failed\n"); + + return err; +} + +static int media_eject(struct udev *udev, int fd) +{ + struct scsi_cmd sc; + int err; + + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x1b); + scsi_cmd_set(udev, &sc, 4, 0x02); + scsi_cmd_set(udev, &sc, 5, 0); + err = scsi_cmd_run(udev, &sc, fd, NULL, 0); + if ((err != 0)) { + info_scsi_cmd_err(udev, "START_STOP_UNIT", err); + return -1; + } + return 0; +} + static int cd_capability_compat(struct udev *udev, int fd) { int capability; @@ -237,12 +272,13 @@ static int cd_media_compat(struct udev *udev, int fd) return 0; } -static int cd_inquiry(struct udev *udev, int fd) { +static int cd_inquiry(struct udev *udev, int fd) +{ struct scsi_cmd sc; unsigned char inq[128]; int err; - scsi_cmd_init(udev, &sc, inq, sizeof(inq)); + scsi_cmd_init(udev, &sc); scsi_cmd_set(udev, &sc, 0, 0x12); scsi_cmd_set(udev, &sc, 4, 36); scsi_cmd_set(udev, &sc, 5, 0); @@ -261,6 +297,115 @@ static int cd_inquiry(struct udev *udev, int fd) { return 0; } +static void feature_profile_media(struct udev *udev, int cur_profile) +{ + switch (cur_profile) { + case 0x03: + case 0x04: + case 0x05: + info(udev, "profile 0x%02x \n", cur_profile); + cd_media = 1; + cd_media_mo = 1; + break; + case 0x08: + info(udev, "profile 0x%02x media_cd_rom\n", cur_profile); + cd_media = 1; + cd_media_cd_rom = 1; + break; + case 0x09: + info(udev, "profile 0x%02x media_cd_r\n", cur_profile); + cd_media = 1; + cd_media_cd_r = 1; + break; + case 0x0a: + info(udev, "profile 0x%02x media_cd_rw\n", cur_profile); + cd_media = 1; + cd_media_cd_rw = 1; + break; + case 0x10: + info(udev, "profile 0x%02x media_dvd_ro\n", cur_profile); + cd_media = 1; + cd_media_dvd_rom = 1; + break; + case 0x11: + info(udev, "profile 0x%02x media_dvd_r\n", cur_profile); + cd_media = 1; + cd_media_dvd_r = 1; + break; + case 0x12: + info(udev, "profile 0x%02x media_dvd_ram\n", cur_profile); + cd_media = 1; + cd_media_dvd_ram = 1; + break; + case 0x13: + info(udev, "profile 0x%02x media_dvd_rw_ro\n", cur_profile); + cd_media = 1; + cd_media_dvd_rw = 1; + cd_media_dvd_rw_ro = 1; + break; + case 0x14: + info(udev, "profile 0x%02x media_dvd_rw_seq\n", cur_profile); + cd_media = 1; + cd_media_dvd_rw = 1; + cd_media_dvd_rw_seq = 1; + break; + case 0x1B: + info(udev, "profile 0x%02x media_dvd_plus_r\n", cur_profile); + cd_media = 1; + cd_media_dvd_plus_r = 1; + break; + case 0x1A: + info(udev, "profile 0x%02x media_dvd_plus_rw\n", cur_profile); + cd_media = 1; + cd_media_dvd_plus_rw = 1; + break; + case 0x2A: + info(udev, "profile 0x%02x media_dvd_plus_rw_dl\n", cur_profile); + cd_media = 1; + cd_media_dvd_plus_rw_dl = 1; + break; + case 0x2B: + info(udev, "profile 0x%02x media_dvd_plus_r_dl\n", cur_profile); + cd_media = 1; + cd_media_dvd_plus_r_dl = 1; + break; + case 0x40: + info(udev, "profile 0x%02x media_bd\n", cur_profile); + cd_media = 1; + cd_media_bd = 1; + break; + case 0x41: + case 0x42: + info(udev, "profile 0x%02x media_bd_r\n", cur_profile); + cd_media = 1; + cd_media_bd_r = 1; + break; + case 0x43: + info(udev, "profile 0x%02x media_bd_re\n", cur_profile); + cd_media = 1; + cd_media_bd_re = 1; + break; + case 0x50: + info(udev, "profile 0x%02x media_hddvd\n", cur_profile); + cd_media = 1; + cd_media_hddvd = 1; + break; + case 0x51: + info(udev, "profile 0x%02x media_hddvd_r\n", cur_profile); + cd_media = 1; + cd_media_hddvd_r = 1; + break; + case 0x52: + info(udev, "profile 0x%02x media_hddvd_rw\n", cur_profile); + cd_media = 1; + cd_media_hddvd_rw = 1; + break; + default: + info(udev, "profile 0x%02x \n", cur_profile); + break; + } +} + static int feature_profiles(struct udev *udev, const unsigned char *profiles, size_t size) { unsigned int i; @@ -297,14 +442,9 @@ static int feature_profiles(struct udev *udev, const unsigned char *profiles, si cd_dvd_ram = 1; break; case 0x13: - info(udev, "profile 0x%02x media_dvd_rw\n", profile); - cd_media_dvd_rw = 1; - cd_media_dvd_rw_ro = 1; - break; case 0x14: info(udev, "profile 0x%02x dvd_rw\n", profile); cd_dvd_rw = 1; - cd_media_dvd_rw_seq = 1; break; case 0x1B: info(udev, "profile 0x%02x dvd_plus_r\n", profile); @@ -355,6 +495,7 @@ static int feature_profiles(struct udev *udev, const unsigned char *profiles, si return 0; } +/* returns 0 if media was detected */ static int cd_profiles_old_mmc(struct udev *udev, int fd) { struct scsi_cmd sc; @@ -362,15 +503,21 @@ static int cd_profiles_old_mmc(struct udev *udev, int fd) unsigned char header[32]; - scsi_cmd_init(udev, &sc, header, sizeof(header)); + scsi_cmd_init(udev, &sc); scsi_cmd_set(udev, &sc, 0, 0x51); scsi_cmd_set(udev, &sc, 8, sizeof(header)); scsi_cmd_set(udev, &sc, 9, 0); err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); if ((err != 0)) { info_scsi_cmd_err(udev, "READ DISC INFORMATION", err); - info(udev, "no current profile, assuming no media\n"); - return -1; + if (cd_media == 1) { + info(udev, "no current profile, but disc is present; assuming CD-ROM\n"); + cd_media_cd_rom = 1; + return 0; + } else { + info(udev, "no current profile, assuming no media\n"); + return -1; + } }; cd_media = 1; @@ -388,6 +535,7 @@ static int cd_profiles_old_mmc(struct udev *udev, int fd) return 0; } +/* returns 0 if media was detected */ static int cd_profiles(struct udev *udev, int fd) { struct scsi_cmd sc; @@ -396,24 +544,59 @@ static int cd_profiles(struct udev *udev, int fd) unsigned int len; unsigned int i; int err; + int ret; + + ret = -1; - scsi_cmd_init(udev, &sc, features, sizeof(features)); + /* First query the current profile */ + scsi_cmd_init(udev, &sc); scsi_cmd_set(udev, &sc, 0, 0x46); - scsi_cmd_set(udev, &sc, 7, (sizeof(features) >> 8) & 0xff); - scsi_cmd_set(udev, &sc, 8, sizeof(features) & 0xff); + scsi_cmd_set(udev, &sc, 8, 8); scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, features, sizeof(features)); + err = scsi_cmd_run(udev, &sc, fd, features, 8); if ((err != 0)) { info_scsi_cmd_err(udev, "GET CONFIGURATION", err); /* handle pre-MMC2 drives which do not support GET CONFIGURATION */ if (SK(err) == 0x5 && ASC(err) == 0x20) { info(udev, "drive is pre-MMC2 and does not support 46h get configuration command\n"); info(udev, "trying to work around the problem\n"); - return cd_profiles_old_mmc(udev, fd); + ret = cd_profiles_old_mmc(udev, fd); } + goto out; + } + + cur_profile = features[6] << 8 | features[7]; + if (cur_profile > 0) { + info(udev, "current profile 0x%02x\n", cur_profile); + feature_profile_media (udev, cur_profile); + ret = 0; /* we have media */ + } else { + info(udev, "no current profile, assuming no media\n"); + } + + len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; + info(udev, "GET CONFIGURATION: size of features buffer 0x%04x\n", len); + + if (len > sizeof(features)) { + info(udev, "can not get features in a single query, truncating\n"); + len = sizeof(features); + } else if (len <= 8) { + len = sizeof(features); + } + + /* Now get the full feature buffer */ + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x46); + scsi_cmd_set(udev, &sc, 7, ( len >> 8 ) & 0xff); + scsi_cmd_set(udev, &sc, 8, len & 0xff); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, features, len); + if ((err != 0)) { + info_scsi_cmd_err(udev, "GET CONFIGURATION", err); return -1; } + /* parse the length once more, in case the drive decided to have other features suddenly :) */ len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; info(udev, "GET CONFIGURATION: size of features buffer 0x%04x\n", len); @@ -438,115 +621,8 @@ static int cd_profiles(struct udev *udev, int fd) break; } } - - cur_profile = features[6] << 8 | features[7]; - if (cur_profile > 0) { - info(udev, "current profile 0x%02x\n", cur_profile); - } else { - info(udev, "no current profile, assuming no media\n"); - return -1; - } - - switch (cur_profile) { - case 0x03: - case 0x04: - case 0x05: - info(udev, "profile 0x%02x \n", cur_profile); - cd_media = 1; - cd_media_mo = 1; - break; - case 0x08: - info(udev, "profile 0x%02x media_cd_rom\n", cur_profile); - cd_media = 1; - cd_media_cd_rom = 1; - break; - case 0x09: - info(udev, "profile 0x%02x media_cd_r\n", cur_profile); - cd_media = 1; - cd_media_cd_r = 1; - break; - case 0x0a: - info(udev, "profile 0x%02x media_cd_rw\n", cur_profile); - cd_media = 1; - cd_media_cd_rw = 1; - break; - case 0x10: - info(udev, "profile 0x%02x media_dvd_ro\n", cur_profile); - cd_media = 1; - cd_media_dvd_rom = 1; - break; - case 0x11: - info(udev, "profile 0x%02x media_dvd_r\n", cur_profile); - cd_media = 1; - cd_media_dvd_r = 1; - break; - case 0x12: - info(udev, "profile 0x%02x media_dvd_ram\n", cur_profile); - cd_media = 1; - cd_media_dvd_ram = 1; - break; - case 0x13: - case 0x14: - info(udev, "profile 0x%02x media_dvd_rw\n", cur_profile); - cd_media = 1; - cd_media_dvd_rw = 1; - break; - case 0x1B: - info(udev, "profile 0x%02x media_dvd_plus_r\n", cur_profile); - cd_media = 1; - cd_media_dvd_plus_r = 1; - break; - case 0x1A: - info(udev, "profile 0x%02x media_dvd_plus_rw\n", cur_profile); - cd_media = 1; - cd_media_dvd_plus_rw = 1; - break; - case 0x2A: - info(udev, "profile 0x%02x media_dvd_plus_rw_dl\n", cur_profile); - cd_media = 1; - cd_media_dvd_plus_rw_dl = 1; - break; - case 0x2B: - info(udev, "profile 0x%02x media_dvd_plus_r_dl\n", cur_profile); - cd_media = 1; - cd_media_dvd_plus_r_dl = 1; - break; - case 0x40: - info(udev, "profile 0x%02x media_bd\n", cur_profile); - cd_media = 1; - cd_media_bd = 1; - break; - case 0x41: - case 0x42: - info(udev, "profile 0x%02x media_bd_r\n", cur_profile); - cd_media = 1; - cd_media_bd_r = 1; - break; - case 0x43: - info(udev, "profile 0x%02x media_bd_re\n", cur_profile); - cd_media = 1; - cd_media_bd_re = 1; - break; - case 0x50: - info(udev, "profile 0x%02x media_hddvd\n", cur_profile); - cd_media = 1; - cd_media_hddvd = 1; - break; - case 0x51: - info(udev, "profile 0x%02x media_hddvd_r\n", cur_profile); - cd_media = 1; - cd_media_hddvd_r = 1; - break; - case 0x52: - info(udev, "profile 0x%02x media_hddvd_rw\n", cur_profile); - cd_media = 1; - cd_media_hddvd_rw = 1; - break; - default: - info(udev, "profile 0x%02x \n", cur_profile); - break; - } - return 0; +out: + return ret; } static int cd_media_info(struct udev *udev, int fd) @@ -561,7 +637,7 @@ static int cd_media_info(struct udev *udev, int fd) }; int err; - scsi_cmd_init(udev, &sc, header, sizeof(header)); + scsi_cmd_init(udev, &sc); scsi_cmd_set(udev, &sc, 0, 0x51); scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff); scsi_cmd_set(udev, &sc, 9, 0); @@ -589,7 +665,7 @@ static int cd_media_info(struct udev *udev, int fd) * always "complete", DVD-RAM are "other" or "complete" if the disc is * write protected; we need to check the contents if it is blank */ if ((cd_media_dvd_rw_ro || cd_media_dvd_plus_rw || cd_media_dvd_plus_rw_dl || cd_media_dvd_ram) && (header[2] & 3) > 1) { - unsigned char buffer[17 * 2048]; + unsigned char buffer[32 * 2048]; unsigned char result, len; int block, offset; @@ -599,7 +675,7 @@ static int cd_media_info(struct udev *udev, int fd) unsigned char dvdstruct[8]; unsigned char format[12]; - scsi_cmd_init(udev, &sc, dvdstruct, sizeof(dvdstruct)); + scsi_cmd_init(udev, &sc); scsi_cmd_set(udev, &sc, 0, 0xAD); scsi_cmd_set(udev, &sc, 7, 0xC0); scsi_cmd_set(udev, &sc, 9, sizeof(dvdstruct)); @@ -616,7 +692,7 @@ static int cd_media_info(struct udev *udev, int fd) } /* let's make sure we don't try to read unformatted media */ - scsi_cmd_init(udev, &sc, format, sizeof(format)); + scsi_cmd_init(udev, &sc); scsi_cmd_set(udev, &sc, 0, 0x23); scsi_cmd_set(udev, &sc, 8, sizeof(format)); scsi_cmd_set(udev, &sc, 9, 0); @@ -657,13 +733,14 @@ static int cd_media_info(struct udev *udev, int fd) * has "blank" status", DVD-RAM was examined earlier) and check * for ISO and UDF PVDs or a fs superblock presence and do it * in one ioctl (we need just sectors 0 and 16) */ - scsi_cmd_init(udev, &sc, buffer, sizeof(buffer)); + scsi_cmd_init(udev, &sc); scsi_cmd_set(udev, &sc, 0, 0x28); scsi_cmd_set(udev, &sc, 5, 0); - scsi_cmd_set(udev, &sc, 8, 17); + scsi_cmd_set(udev, &sc, 8, 32); scsi_cmd_set(udev, &sc, 9, 0); err = scsi_cmd_run(udev, &sc, fd, buffer, sizeof(buffer)); if ((err != 0)) { + cd_media = 0; info_scsi_cmd_err(udev, "READ FIRST 32 BLOCKS", err); return -1; } @@ -690,8 +767,9 @@ static int cd_media_info(struct udev *udev, int fd) } determined: - /* "other" is e. g. DVD-RAM, can't append sessions there either */ - if ((header[2] & 3) < 2) + /* "other" is e. g. DVD-RAM, can't append sessions there; DVDs in + * restricted overwrite mode can never append, only in sequential mode */ + if ((header[2] & 3) < 2 && !cd_media_dvd_rw_ro) cd_media_session_next = header[10] << 8 | header[5]; cd_media_session_count = header[9] << 8 | header[4]; cd_media_track_count = header[11] << 8 | header[6]; @@ -703,12 +781,12 @@ static int cd_media_toc(struct udev *udev, int fd) { struct scsi_cmd sc; unsigned char header[12]; - unsigned char toc[2048]; - unsigned int len, i; + unsigned char toc[65536]; + unsigned int len, i, num_tracks; unsigned char *p; int err; - scsi_cmd_init(udev, &sc, header, sizeof(header)); + scsi_cmd_init(udev, &sc); scsi_cmd_set(udev, &sc, 0, 0x43); scsi_cmd_set(udev, &sc, 6, 1); scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff); @@ -720,17 +798,19 @@ static int cd_media_toc(struct udev *udev, int fd) } len = (header[0] << 8 | header[1]) + 2; - info(udev, "READ TOC: len: %d\n", len); + info(udev, "READ TOC: len: %d, start track: %d, end track: %d\n", len, header[2], header[3]); if (len > sizeof(toc)) return -1; if (len < 2) return -1; + /* 2: first track, 3: last track */ + num_tracks = header[3] - header[2] + 1; /* empty media has no tracks */ if (len < 8) return 0; - scsi_cmd_init(udev, &sc, toc, sizeof(toc)); + scsi_cmd_init(udev, &sc); scsi_cmd_set(udev, &sc, 0, 0x43); scsi_cmd_set(udev, &sc, 6, header[2]); /* First Track/Session Number */ scsi_cmd_set(udev, &sc, 7, (len >> 8) & 0xff); @@ -742,7 +822,10 @@ static int cd_media_toc(struct udev *udev, int fd) return -1; } - for (p = toc+4, i = 4; i < len-8; i += 8, p += 8) { + /* Take care to not iterate beyond the last valid track as specified in + * the TOC, but also avoid going beyond the TOC length, just in case + * the last track number is invalidly large */ + for (p = toc+4, i = 4; i < len-8 && num_tracks > 0; i += 8, p += 8, --num_tracks) { unsigned int block; unsigned int is_data_track; @@ -758,7 +841,7 @@ static int cd_media_toc(struct udev *udev, int fd) cd_media_track_count_audio++; } - scsi_cmd_init(udev, &sc, header, sizeof(header)); + scsi_cmd_init(udev, &sc); scsi_cmd_set(udev, &sc, 0, 0x43); scsi_cmd_set(udev, &sc, 2, 1); /* Session Info */ scsi_cmd_set(udev, &sc, 8, sizeof(header)); @@ -778,13 +861,17 @@ int main(int argc, char *argv[]) { struct udev *udev; static const struct option options[] = { - { "export", no_argument, NULL, 'x' }, + { "lock-media", no_argument, NULL, 'l' }, + { "unlock-media", no_argument, NULL, 'u' }, + { "eject-media", no_argument, NULL, 'e' }, { "debug", no_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, {} }; + bool eject = false; + bool lock = false; + bool unlock = false; const char *node = NULL; - int export = 0; int fd = -1; int cnt; int rc = 0; @@ -799,22 +886,30 @@ int main(int argc, char *argv[]) while (1) { int option; - option = getopt_long(argc, argv, "dxh", options, NULL); + option = getopt_long(argc, argv, "deluh", options, NULL); if (option == -1) break; switch (option) { + case 'l': + lock = true; + break; + case 'u': + unlock = true; + break; + case 'e': + eject = true; + break; case 'd': - debug = 1; + debug = true; if (udev_get_log_priority(udev) < LOG_INFO) udev_set_log_priority(udev, LOG_INFO); break; - case 'x': - export = 1; - break; case 'h': printf("Usage: cdrom_id [options] \n" - " --export export key/value pairs\n" + " --lock-media lock the media (to enable eject request events)\n" + " --unlock-media unlock the media\n" + " --eject-media eject the media\n" " --debug debug to stderr\n" " --help print this help text\n\n"); goto exit; @@ -863,11 +958,13 @@ int main(int argc, char *argv[]) /* check if drive talks MMC */ if (cd_inquiry(udev, fd) < 0) - goto print; + goto work; /* read drive and possibly current profile */ - if (cd_profiles(udev, fd) < 0) - goto print; + if (cd_profiles(udev, fd) != 0) + goto work; + + /* at this point we are guaranteed to have media in the drive - find out more about it */ /* get session/track info */ cd_media_toc(udev, fd); @@ -875,7 +972,25 @@ int main(int argc, char *argv[]) /* get writable media state */ cd_media_info(udev, fd); -print: +work: + /* lock the media, so we enable eject button events */ + if (lock && cd_media) { + info(udev, "PREVENT_ALLOW_MEDIUM_REMOVAL (lock)\n"); + media_lock(udev, fd, true); + } + + if (unlock && cd_media) { + info(udev, "PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)\n"); + media_lock(udev, fd, false); + } + + if (eject) { + info(udev, "PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)\n"); + media_lock(udev, fd, false); + info(udev, "START_STOP_UNIT (eject)\n"); + media_eject(udev, fd); + } + printf("ID_CDROM=1\n"); if (cd_cd_rom) printf("ID_CDROM_CD=1\n"); @@ -982,4 +1097,3 @@ exit: udev_log_close(); return rc; } -