#include #include #include #include #include #include #include "sg_lib.h" #include "sg_cmds_basic.h" /* * Copyright (C) 1999-2007 D. Gilbert * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. Start/Stop parameter by Kurt Garloff , 6/2000 Sync cache parameter by Kurt Garloff , 1/2001 Guard block device answering sg's ioctls. 12/2002 Convert to SG_IO ioctl so can use sg or block devices in 2.6.* 3/2003 This utility was written for the Linux 2.4 kernel series. It should now build for the Linux 2.6 kernel series and various other Operating Systems. */ static char * version_str = "0.55 20070128"; static struct option long_options[] = { {"eject", 0, 0, 'e'}, {"fl", 1, 0, 'f'}, {"help", 0, 0, 'h'}, {"immed", 0, 0, 'i'}, {"load", 0, 0, 'l'}, {"loej", 0, 0, 'L'}, {"new", 0, 0, 'N'}, {"old", 0, 0, 'O'}, {"pc", 1, 0, 'p'}, {"start", 0, 0, 's'}, {"stop", 0, 0, 'S'}, {"verbose", 0, 0, 'v'}, {"version", 0, 0, 'V'}, {0, 0, 0, 0}, }; struct opts_t { int do_eject; int do_fl; int do_help; int do_immed; int do_load; int do_loej; int do_pc; int do_start; int do_stop; int do_verbose; int do_version; const char * device_name; int opt_new; }; void usage() { fprintf(stderr, "Usage: sg_start [--eject] [--fl=FL] [--help] " "[--immed] [--load] [--loej]\n" " [--pc=PC] [--start] [--stop] " "[--verbose] [--version]\n" " DEVICE\n" " where:\n" " --eject|-e stop unit then eject the medium\n" " --fl=FL|-f FL format layer number (mmc5)\n" " --help|-h print usage message then exit\n" " --immed|-i device should return control after " "receiving cdb,\n" " default action is to wait until action " "is complete\n" " --load|-l load medium then start the unit\n" " --loej|-L load or eject, corresponds to LOEJ bit " "in cdb;\n" " load when START bit also set, else " "eject\n" " --pc=PC|-p PC power conditions: 0 (default) -> no " "power condition,\n" " 1 -> active, 2 -> idle, 3 -> standby, " "5 -> sleep (MMC)\n" " --start|-s start unit, corresponds to START bit " "in cdb,\n" " default (START=1) if no other options " "given\n" " --stop|-S stop unit (e.g. spin down disk)\n" " --verbose|-v increase verbosity\n" " --version|-V print version string then exit\n\n" " Example: 'sg_start --stop /dev/sdb' stops unit\n" " 'sg_start --eject /dev/scd0' stops unit and " "ejects medium\n\n" "Performs a SCSI START STOP UNIT command\n" ); } void usage_old() { fprintf(stderr, "Usage: sg_start [0] [1] [--eject] [--fl=FL] " "[-i] [--imm=0|1]\n" " [--load] [--loej] [--pc=PC] [--start] " "[--stop] [-v] [-V]\n" " DEVICE\n" " where:\n" " 0 stop unit (e.g. spin down a disk or a " "cd/dvd)\n" " 1 start unit (e.g. spin up a disk or a " "cd/dvd)\n" " --eject stop then eject the medium\n" " --fl=FL format layer number (mmc5)\n" " -i return immediately (same as '--imm=1')\n" " --imm=0|1 0->await completion(def), 1->return " "immediately\n" " --load load then start the medium\n" " --loej load the medium if '-start' option is " "also given\n" " or stop unit and eject\n" " --pc=PC power conditions (in hex, default 0 -> no " "power condition)\n" " 1 -> active, 2 -> idle, 3 -> standby, " "5 -> sleep (MMC)\n" " --start start unit (same as '1'), default " "action\n" " --stop stop unit (same as '0')\n" " -v verbose (print out SCSI commands)\n" " -V print version string then exit\n\n" " Example: 'sg_start --stop /dev/sdb' stops unit\n" " 'sg_start --eject /dev/scd0' stops unit and " "ejects medium\n\n" "Performs a SCSI START STOP UNIT command\n" ); } static int process_cl_new(struct opts_t * optsp, int argc, char * argv[]) { int c, n, err; while (1) { int option_index = 0; c = getopt_long(argc, argv, "ef:hilLNOp:sSvV", long_options, &option_index); if (c == -1) break; switch (c) { case 'e': ++optsp->do_eject; ++optsp->do_loej; break; case 'f': n = sg_get_num(optarg); if ((n < 0) || (n > 3)) { fprintf(stderr, "bad argument to '--fl='\n"); usage(); return SG_LIB_SYNTAX_ERROR; } ++optsp->do_loej; ++optsp->do_start; optsp->do_fl = n; break; case 'h': case '?': ++optsp->do_help; break; case 'i': ++optsp->do_immed; break; case 'l': ++optsp->do_load; ++optsp->do_loej; break; case 'L': ++optsp->do_loej; break; case 'N': break; /* ignore */ case 'O': optsp->opt_new = 0; return 0; case 'p': n = sg_get_num(optarg); if ((n < 0) || (n > 15)) { fprintf(stderr, "bad argument to '--pc='\n"); usage(); return SG_LIB_SYNTAX_ERROR; } optsp->do_pc = n; break; case 's': ++optsp->do_start; break; case 'S': ++optsp->do_stop; break; case 'v': ++optsp->do_verbose; break; case 'V': ++optsp->do_version; break; default: fprintf(stderr, "unrecognised switch code %c [0x%x]\n", c, c); if (optsp->do_help) break; usage(); return SG_LIB_SYNTAX_ERROR; } } err = 0; for (; optind < argc; ++optind) { if (1 == strlen(argv[optind])) { if (0 == strcmp("0", argv[optind])) { ++optsp->do_stop; continue; } else if (0 == strcmp("1", argv[optind])) { ++optsp->do_start; continue; } } if (NULL == optsp->device_name) optsp->device_name = argv[optind]; else { fprintf(stderr, "Unexpected extra argument: %s\n", argv[optind]); ++err; } } if (err) { usage(); return SG_LIB_SYNTAX_ERROR; } else return 0; } static int process_cl_old(struct opts_t * optsp, int argc, char * argv[]) { int k, jmp_out, plen, num; int ambigu = 0; int startstop = -1; unsigned int u; const char * cp; for (k = 1; k < argc; ++k) { cp = argv[k]; plen = strlen(cp); if (plen <= 0) continue; if ('-' == *cp) { for (--plen, ++cp, jmp_out = 0; plen > 0; --plen, ++cp) { switch (*cp) { case 'i': if ('\0' == *(cp + 1)) optsp->do_immed = 1; else jmp_out = 1; break; case 'v': ++optsp->do_verbose; break; case 'V': ++optsp->do_version; break; case 'h': case '?': ++optsp->do_help; break; case 'N': optsp->opt_new = 1; return 0; case 'O': break; case '-': ++cp; --plen; jmp_out = 1; break; default: jmp_out = 1; break; } if (jmp_out) break; } if (plen <= 0) continue; if (0 == strncmp(cp, "eject", 5)) { optsp->do_loej = 1; if (startstop == 1) ambigu = 1; else startstop = 0; } else if (0 == strncmp("fl=", cp, 3)) { num = sscanf(cp + 3, "%x", &u); if (1 != num) { fprintf(stderr, "Bad value after 'fl=' option\n"); usage_old(); return SG_LIB_SYNTAX_ERROR; } startstop = 1; optsp->do_loej = 1; optsp->do_fl = u; } else if (0 == strncmp("imm=", cp, 4)) { num = sscanf(cp + 4, "%x", &u); if ((1 != num) || (u > 1)) { fprintf(stderr, "Bad value after 'imm=' option\n"); usage_old(); return SG_LIB_SYNTAX_ERROR; } optsp->do_immed = u; } else if (0 == strncmp(cp, "load", 4)) { optsp->do_loej = 1; if (startstop == 0) ambigu = 1; else startstop = 1; } else if (0 == strncmp(cp, "loej", 4)) optsp->do_loej = 1; else if (0 == strncmp("pc=", cp, 3)) { num = sscanf(cp + 3, "%x", &u); if ((1 != num) || (u > 15)) { fprintf(stderr, "Bad value after after 'pc=' option\n"); usage_old(); return SG_LIB_SYNTAX_ERROR; } optsp->do_pc = u; } else if (0 == strncmp(cp, "start", 5)) { if (startstop == 0) ambigu = 1; else startstop = 1; } else if (0 == strncmp(cp, "stop", 4)) { if (startstop == 1) ambigu = 1; else startstop = 0; } else if (0 == strncmp(cp, "old", 3)) ; else if (jmp_out) { fprintf(stderr, "Unrecognized option: %s\n", cp); usage_old(); return SG_LIB_SYNTAX_ERROR; } } else if (0 == strcmp("0", cp)) { if (1 == startstop) ambigu = 1; else startstop = 0; } else if (0 == strcmp("1", cp)) { if (0 == startstop) ambigu = 1; else startstop = 1; } else if (0 == optsp->device_name) optsp->device_name = cp; else { fprintf(stderr, "too many arguments, got: %s, not " "expecting: %s\n", optsp->device_name, cp); usage_old(); return SG_LIB_SYNTAX_ERROR; } if (ambigu) { fprintf(stderr, "please, only one of 0, 1, --eject, " "--load, --start or --stop\n"); usage_old(); return SG_LIB_SYNTAX_ERROR; } else if (0 == startstop) ++optsp->do_stop; else if (1 == startstop) ++optsp->do_start; } return 0; } static int process_cl(struct opts_t * optsp, int argc, char * argv[]) { int res; char * cp; cp = getenv("SG3_UTILS_OLD_OPTS"); if (cp) { optsp->opt_new = 0; res = process_cl_old(optsp, argc, argv); if ((0 == res) && optsp->opt_new) res = process_cl_new(optsp, argc, argv); } else { optsp->opt_new = 1; res = process_cl_new(optsp, argc, argv); if ((0 == res) && (0 == optsp->opt_new)) res = process_cl_old(optsp, argc, argv); } return res; } int main(int argc, char * argv[]) { int fd, res; int ret = 0; struct opts_t opts; memset(&opts, 0, sizeof(opts)); opts.do_fl = -1; /* only when >= 0 set FL bit */ res = process_cl(&opts, argc, argv); if (res) return SG_LIB_SYNTAX_ERROR; if (opts.do_help) { if (opts.opt_new) usage(); else usage_old(); return 0; } if (opts.do_version) { fprintf(stderr, "Version string: %s\n", version_str); return 0; } if (opts.do_start && opts.do_stop) { fprintf(stderr, "Ambiguous to give both '--start' and '--stop'\n"); return SG_LIB_SYNTAX_ERROR; } if (opts.do_load && opts.do_eject) { fprintf(stderr, "Ambiguous to give both '--load' and '--eject'\n"); return SG_LIB_SYNTAX_ERROR; } if (opts.do_load) opts.do_start = 1; else if ((opts.do_eject) || (opts.do_stop)) opts.do_start = 0; else if (opts.opt_new && opts.do_loej && (0 == opts.do_start)) opts.do_start = 1; /* --loej alone in new interface is load */ else if ((0 == opts.do_loej) && (-1 == opts.do_fl) && (0 == opts.do_pc)) opts.do_start = 1; /* default action is to start when no other active options */ if (0 == opts.device_name) { fprintf(stderr, "No DEVICE argument given\n"); if (opts.opt_new) usage(); else usage_old(); return SG_LIB_SYNTAX_ERROR; } if (opts.do_fl >= 0) { if (opts.do_start == 0) { fprintf(stderr, "Giving '--fl=FL' with '--stop' (or " "'--eject') is invalid\n"); return SG_LIB_SYNTAX_ERROR; } if (opts.do_pc > 0) { fprintf(stderr, "Giving '--fl=FL' with '--pc=PC' " "when PC is non-zero is invalid\n"); return SG_LIB_SYNTAX_ERROR; } } fd = sg_cmds_open_device(opts.device_name, 0 /* rw */, opts.do_verbose); if (fd < 0) { fprintf(stderr, "Error trying to open %s: %s\n", opts.device_name, safe_strerror(-fd)); return SG_LIB_FILE_ERROR; } res = 0; if (opts.do_fl >= 0) res = sg_ll_start_stop_unit(fd, opts.do_immed, opts.do_fl, 0 /* pc */, 1 /* fl */, 1 /* loej */, 1 /*start */, 1 /* noisy */, opts.do_verbose); else if (opts.do_pc > 0) res = sg_ll_start_stop_unit(fd, opts.do_immed, 0, opts.do_pc, 0, 0, 0, 1, opts.do_verbose); else res = sg_ll_start_stop_unit(fd, opts.do_immed, 0, 0, 0, opts.do_loej, opts.do_start, 1, opts.do_verbose); ret = res; if (res) { if (opts.do_verbose < 2) { if (SG_LIB_CAT_INVALID_OP == res) fprintf(stderr, "command not supported\n"); else if (SG_LIB_CAT_NOT_READY == res) fprintf(stderr, "device not ready\n"); else if (SG_LIB_CAT_UNIT_ATTENTION == res) fprintf(stderr, "unit attention\n"); else if (SG_LIB_CAT_ABORTED_COMMAND == res) fprintf(stderr, "aborted command\n"); else if (SG_LIB_CAT_ILLEGAL_REQ == res) fprintf(stderr, "invalid field in cdb\n"); } fprintf(stderr, "START STOP UNIT command failed\n"); } res = sg_cmds_close_device(fd); if ((res < 0) && (0 == ret)) return SG_LIB_FILE_ERROR; return (ret >= 0) ? ret : SG_LIB_CAT_OTHER; }