1 /*
2 * Copyright (C) 1999-2020 D. Gilbert
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2, or (at your option)
6 * any later version.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9
10 Start/Stop parameter by Kurt Garloff, 6/2000
11 Sync cache parameter by Kurt Garloff, 1/2001
12 Guard block device answering sg's ioctls.
13 <dgilbert at interlog dot com> 12/2002
14 Convert to SG_IO ioctl so can use sg or block devices in 2.6.* 3/2003
15
16 This utility was written for the Linux 2.4 kernel series. It now
17 builds for the Linux 2.6 and 3 kernel series and various other
18 Operating Systems.
19
20 */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <stdbool.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <fcntl.h>
29 #include <getopt.h>
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 #include "sg_lib.h"
35 #include "sg_cmds_basic.h"
36 #include "sg_pr2serr.h"
37
38
39 static const char * version_str = "0.67 20200930"; /* sbc3r14; mmc6r01a */
40
41 static struct option long_options[] = {
42 {"eject", no_argument, 0, 'e'},
43 {"fl", required_argument, 0, 'f'},
44 {"help", no_argument, 0, 'h'},
45 {"immed", no_argument, 0, 'i'},
46 {"load", no_argument, 0, 'l'},
47 {"loej", no_argument, 0, 'L'},
48 {"mod", required_argument, 0, 'm'},
49 {"noflush", no_argument, 0, 'n'},
50 {"new", no_argument, 0, 'N'},
51 {"old", no_argument, 0, 'O'},
52 {"pc", required_argument, 0, 'p'},
53 {"readonly", no_argument, 0, 'r'},
54 {"start", no_argument, 0, 's'},
55 {"stop", no_argument, 0, 'S'},
56 {"verbose", no_argument, 0, 'v'},
57 {"version", no_argument, 0, 'V'},
58 {0, 0, 0, 0},
59 };
60
61 struct opts_t {
62 bool do_eject;
63 bool do_immed;
64 bool do_load;
65 bool do_loej;
66 bool do_noflush;
67 bool do_readonly;
68 bool do_start;
69 bool do_stop;
70 bool opt_new;
71 bool verbose_given;
72 bool version_given;
73 int do_fl;
74 int do_help;
75 int do_mod;
76 int do_pc;
77 int verbose;
78 const char * device_name;
79 };
80
81 static void
usage()82 usage()
83 {
84 pr2serr("Usage: sg_start [--eject] [--fl=FL] [--help] "
85 "[--immed] [--load] [--loej]\n"
86 " [--mod=PC_MOD] [--noflush] [--pc=PC] "
87 "[--readonly]\n"
88 " [--start] [--stop] [--verbose] "
89 "[--version] DEVICE\n"
90 " where:\n"
91 " --eject|-e stop unit then eject the medium\n"
92 " --fl=FL|-f FL format layer number (mmc5)\n"
93 " --help|-h print usage message then exit\n"
94 " --immed|-i device should return control after "
95 "receiving cdb,\n"
96 " default action is to wait until action "
97 "is complete\n"
98 " --load|-l load medium then start the unit\n"
99 " --loej|-L load or eject, corresponds to LOEJ bit "
100 "in cdb;\n"
101 " load when START bit also set, else "
102 "eject\n"
103 " --mod=PC_MOD|-m PC_MOD power condition modifier "
104 "(def: 0) (sbc)\n"
105 " --noflush|-n no flush prior to operation that limits "
106 "access (sbc)\n"
107 " --pc=PC|-p PC power condition: 0 (default) -> no "
108 "power condition,\n"
109 " 1 -> active, 2 -> idle, 3 -> standby, "
110 "5 -> sleep (mmc)\n"
111 " --readonly|-r open DEVICE read-only (def: read-write)\n"
112 " recommended if DEVICE is ATA disk\n"
113 " --start|-s start unit, corresponds to START bit "
114 "in cdb,\n"
115 " default (START=1) if no other options "
116 "given\n"
117 " --stop|-S stop unit (e.g. spin down disk)\n"
118 " --verbose|-v increase verbosity\n"
119 " --old|-O use old interface (use as first option)\n"
120 " --version|-V print version string then exit\n\n"
121 " Example: 'sg_start --stop /dev/sdb' stops unit\n"
122 " 'sg_start --eject /dev/scd0' stops unit and "
123 "ejects medium\n\n"
124 "Performs a SCSI START STOP UNIT command\n"
125 );
126 }
127
128 static void
usage_old()129 usage_old()
130 {
131 pr2serr("Usage: sg_start [0] [1] [--eject] [--fl=FL] "
132 "[-i] [--imm=0|1]\n"
133 " [--load] [--loej] [--mod=PC_MOD] "
134 "[--noflush] [--pc=PC]\n"
135 " [--readonly] [--start] [--stop] [-v] [-V]\n"
136 " DEVICE\n"
137 " where:\n"
138 " 0 stop unit (e.g. spin down a disk or a "
139 "cd/dvd)\n"
140 " 1 start unit (e.g. spin up a disk or a "
141 "cd/dvd)\n"
142 " --eject stop then eject the medium\n"
143 " --fl=FL format layer number (mmc5)\n"
144 " -i return immediately (same as '--imm=1')\n"
145 " --imm=0|1 0->await completion(def), 1->return "
146 "immediately\n"
147 " --load load then start the medium\n"
148 " --loej load the medium if '-start' option is "
149 "also given\n"
150 " or stop unit and eject\n"
151 " --mod=PC_MOD power condition modifier "
152 "(def: 0) (sbc)\n"
153 " --noflush no flush prior to operation that limits "
154 "access (sbc)\n"
155 " --pc=PC power condition (in hex, default 0 -> no "
156 "power condition)\n"
157 " 1 -> active, 2 -> idle, 3 -> standby, "
158 "5 -> sleep (mmc)\n"
159 " --readonly|-r open DEVICE read-only (def: read-write)\n"
160 " recommended if DEVICE is ATA disk\n"
161 " --start start unit (same as '1'), default "
162 "action\n"
163 " --stop stop unit (same as '0')\n"
164 " -v verbose (print out SCSI commands)\n"
165 " --new|-N use new interface\n"
166 " -V print version string then exit\n\n"
167 " Example: 'sg_start --stop /dev/sdb' stops unit\n"
168 " 'sg_start --eject /dev/scd0' stops unit and "
169 "ejects medium\n\n"
170 "Performs a SCSI START STOP UNIT command\n"
171 );
172 }
173
174 static int
new_parse_cmd_line(struct opts_t * op,int argc,char * argv[])175 new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
176 {
177 int c, n, err;
178
179 while (1) {
180 int option_index = 0;
181
182 c = getopt_long(argc, argv, "ef:hilLm:nNOp:rsSvV", long_options,
183 &option_index);
184 if (c == -1)
185 break;
186
187 switch (c) {
188 case 'e':
189 op->do_eject = true;
190 op->do_loej = true;
191 break;
192 case 'f':
193 n = sg_get_num(optarg);
194 if ((n < 0) || (n > 3)) {
195 pr2serr("bad argument to '--fl='\n");
196 usage();
197 return SG_LIB_SYNTAX_ERROR;
198 }
199 op->do_loej = true;
200 op->do_start = true;
201 op->do_fl = n;
202 break;
203 case 'h':
204 case '?':
205 ++op->do_help;
206 break;
207 case 'i':
208 op->do_immed = true;
209 break;
210 case 'l':
211 op->do_load = true;
212 op->do_loej = true;
213 break;
214 case 'L':
215 op->do_loej = true;
216 break;
217 case 'm':
218 n = sg_get_num(optarg);
219 if ((n < 0) || (n > 15)) {
220 pr2serr("bad argument to '--mod='\n");
221 usage();
222 return SG_LIB_SYNTAX_ERROR;
223 }
224 op->do_mod = n;
225 break;
226 case 'n':
227 op->do_noflush = true;
228 break;
229 case 'N':
230 break; /* ignore */
231 case 'O':
232 op->opt_new = false;
233 return 0;
234 case 'p':
235 n = sg_get_num(optarg);
236 if ((n < 0) || (n > 15)) {
237 pr2serr("bad argument to '--pc='\n");
238 usage();
239 return SG_LIB_SYNTAX_ERROR;
240 }
241 op->do_pc = n;
242 break;
243 case 'r':
244 op->do_readonly = true;
245 break;
246 case 's':
247 op->do_start = true;
248 break;
249 case 'S':
250 op->do_stop = true;
251 break;
252 case 'v':
253 op->verbose_given = true;
254 ++op->verbose;
255 break;
256 case 'V':
257 op->version_given = true;
258 break;
259 default:
260 pr2serr("unrecognised option code %c [0x%x]\n", c, c);
261 if (op->do_help)
262 break;
263 usage();
264 return SG_LIB_SYNTAX_ERROR;
265 }
266 }
267 err = 0;
268 for (; optind < argc; ++optind) {
269 if (1 == strlen(argv[optind])) {
270 if (0 == strcmp("0", argv[optind])) {
271 op->do_stop = true;
272 continue;
273 } else if (0 == strcmp("1", argv[optind])) {
274 op->do_start = true;
275 continue;
276 }
277 }
278 if (NULL == op->device_name)
279 op->device_name = argv[optind];
280 else {
281 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
282 ++err;
283 }
284 }
285 if (err) {
286 usage();
287 return SG_LIB_SYNTAX_ERROR;
288 } else
289 return 0;
290 }
291
292 static int
old_parse_cmd_line(struct opts_t * op,int argc,char * argv[])293 old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
294 {
295 bool ambigu = false;
296 bool jmp_out;
297 bool startstop = false;
298 bool startstop_set = false;
299 int k, plen, num;
300 unsigned int u;
301 const char * cp;
302
303 for (k = 1; k < argc; ++k) {
304 cp = argv[k];
305 plen = strlen(cp);
306 if (plen <= 0)
307 continue;
308 if ('-' == *cp) {
309 for (--plen, ++cp, jmp_out = false; plen > 0;
310 --plen, ++cp) {
311 switch (*cp) {
312 case 'i':
313 if ('\0' == *(cp + 1))
314 op->do_immed = true;
315 else
316 jmp_out = true;
317 break;
318 case 'r':
319 op->do_readonly = true;
320 break;
321 case 'v':
322 op->verbose_given = true;
323 ++op->verbose;
324 break;
325 case 'V':
326 op->version_given = true;
327 break;
328 case 'h':
329 case '?':
330 ++op->do_help;
331 break;
332 case 'N':
333 op->opt_new = true;
334 return 0;
335 case 'O':
336 break;
337 case '-':
338 ++cp;
339 --plen;
340 jmp_out = true;
341 break;
342 default:
343 jmp_out = true;
344 break;
345 }
346 if (jmp_out)
347 break;
348 }
349 if (plen <= 0)
350 continue;
351
352 if (0 == strncmp(cp, "eject", 5)) {
353 op->do_loej = true;
354 if (startstop_set && startstop)
355 ambigu = true;
356 else {
357 startstop = false;
358 startstop_set = true;
359 }
360 } else if (0 == strncmp("fl=", cp, 3)) {
361 num = sscanf(cp + 3, "%x", &u);
362 if (1 != num) {
363 pr2serr("Bad value after 'fl=' option\n");
364 usage_old();
365 return SG_LIB_SYNTAX_ERROR;
366 }
367 startstop = true;
368 startstop_set = true;
369 op->do_loej = true;
370 op->do_fl = u;
371 } else if (0 == strncmp("imm=", cp, 4)) {
372 num = sscanf(cp + 4, "%x", &u);
373 if ((1 != num) || (u > 1)) {
374 pr2serr("Bad value after 'imm=' option\n");
375 usage_old();
376 return SG_LIB_SYNTAX_ERROR;
377 }
378 op->do_immed = !! u;
379 } else if (0 == strncmp(cp, "load", 4)) {
380 op->do_loej = true;
381 if (startstop_set && (! startstop))
382 ambigu = true;
383 else {
384 startstop = true;
385 startstop_set = true;
386 }
387 } else if (0 == strncmp(cp, "loej", 4))
388 op->do_loej = true;
389 else if (0 == strncmp("pc=", cp, 3)) {
390 num = sscanf(cp + 3, "%x", &u);
391 if ((1 != num) || (u > 15)) {
392 pr2serr("Bad value after after 'pc=' option\n");
393 usage_old();
394 return SG_LIB_SYNTAX_ERROR;
395 }
396 op->do_pc = u;
397 } else if (0 == strncmp("mod=", cp, 4)) {
398 num = sscanf(cp + 3, "%x", &u);
399 if (1 != num) {
400 pr2serr("Bad value after 'mod=' option\n");
401 usage_old();
402 return SG_LIB_SYNTAX_ERROR;
403 }
404 op->do_mod = u;
405 } else if (0 == strncmp(cp, "noflush", 7)) {
406 op->do_noflush = true;
407 } else if (0 == strncmp(cp, "start", 5)) {
408 if (startstop_set && (! startstop))
409 ambigu = true;
410 else {
411 startstop = true;
412 startstop_set = true;
413 }
414 } else if (0 == strncmp(cp, "stop", 4)) {
415 if (startstop_set && startstop)
416 ambigu = true;
417 else {
418 startstop = false;
419 startstop_set = true;
420 }
421 } else if (0 == strncmp(cp, "old", 3))
422 ;
423 else if (jmp_out) {
424 pr2serr("Unrecognized option: %s\n", cp);
425 usage_old();
426 return SG_LIB_SYNTAX_ERROR;
427 }
428 } else if (0 == strcmp("0", cp)) {
429 if (startstop_set && startstop)
430 ambigu = true;
431 else {
432 startstop = false;
433 startstop_set = true;
434 }
435 } else if (0 == strcmp("1", cp)) {
436 if (startstop_set && (! startstop))
437 ambigu = true;
438 else {
439 startstop = true;
440 startstop_set = true;
441 }
442 } else if (0 == op->device_name)
443 op->device_name = cp;
444 else {
445 pr2serr("too many arguments, got: %s, not "
446 "expecting: %s\n", op->device_name, cp);
447 usage_old();
448 return SG_LIB_SYNTAX_ERROR;
449 }
450 if (ambigu) {
451 pr2serr("please, only one of 0, 1, --eject, "
452 "--load, --start or --stop\n");
453 usage_old();
454 return SG_LIB_CONTRADICT;
455 } else if (startstop_set) {
456 if (startstop)
457 op->do_start = true;
458 else
459 op->do_stop = true;
460 }
461 }
462 return 0;
463 }
464
465 static int
parse_cmd_line(struct opts_t * op,int argc,char * argv[])466 parse_cmd_line(struct opts_t * op, int argc, char * argv[])
467 {
468 int res;
469 char * cp;
470
471 cp = getenv("SG3_UTILS_OLD_OPTS");
472 if (cp) {
473 op->opt_new = false;
474 res = old_parse_cmd_line(op, argc, argv);
475 if ((0 == res) && op->opt_new)
476 res = new_parse_cmd_line(op, argc, argv);
477 } else {
478 op->opt_new = true;
479 res = new_parse_cmd_line(op, argc, argv);
480 if ((0 == res) && (! op->opt_new))
481 res = old_parse_cmd_line(op, argc, argv);
482 }
483 return res;
484 }
485
486
487 int
main(int argc,char * argv[])488 main(int argc, char * argv[])
489 {
490 int res;
491 int sg_fd = -1;
492 int ret = 0;
493 struct opts_t opts;
494 struct opts_t * op;
495
496 op = &opts;
497 memset(op, 0, sizeof(opts));
498 op->do_fl = -1; /* only when >= 0 set FL bit */
499 res = parse_cmd_line(op, argc, argv);
500 if (res)
501 return res;
502 if (op->do_help) {
503 if (op->opt_new)
504 usage();
505 else
506 usage_old();
507 return 0;
508 }
509
510 #ifdef DEBUG
511 pr2serr("In DEBUG mode, ");
512 if (op->verbose_given && op->version_given) {
513 pr2serr("but override: '-vV' given, zero verbose and continue\n");
514 op->verbose_given = false;
515 op->version_given = false;
516 op->verbose = 0;
517 } else if (! op->verbose_given) {
518 pr2serr("set '-vv'\n");
519 op->verbose = 2;
520 } else
521 pr2serr("keep verbose=%d\n", op->verbose);
522 #else
523 if (op->verbose_given && op->version_given)
524 pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
525 #endif
526 if (op->version_given) {
527 pr2serr("Version string: %s\n", version_str);
528 return 0;
529 }
530
531 if (op->do_start && op->do_stop) {
532 pr2serr("Ambiguous to give both '--start' and '--stop'\n");
533 return SG_LIB_CONTRADICT;
534 }
535 if (op->do_load && op->do_eject) {
536 pr2serr("Ambiguous to give both '--load' and '--eject'\n");
537 return SG_LIB_CONTRADICT;
538 }
539 if (op->do_load)
540 op->do_start = true;
541 else if ((op->do_eject) || op->do_stop)
542 op->do_start = false;
543 else if (op->opt_new && op->do_loej && (! op->do_start))
544 op->do_start = true; /* --loej alone in new interface is load */
545 else if ((! op->do_loej) && (-1 == op->do_fl) && (0 == op->do_pc))
546 op->do_start = true;
547 /* default action is to start when no other active options */
548
549 if (0 == op->device_name) {
550 pr2serr("No DEVICE argument given\n");
551 if (op->opt_new)
552 usage();
553 else
554 usage_old();
555 return SG_LIB_SYNTAX_ERROR;
556 }
557
558 if (op->do_fl >= 0) {
559 if (! op->do_start) {
560 pr2serr("Giving '--fl=FL' with '--stop' (or '--eject') is "
561 "invalid\n");
562 return SG_LIB_CONTRADICT;
563 }
564 if (op->do_pc > 0) {
565 pr2serr("Giving '--fl=FL' with '--pc=PC' when PC is non-zero "
566 "is invalid\n");
567 return SG_LIB_CONTRADICT;
568 }
569 }
570
571 sg_fd = sg_cmds_open_device(op->device_name, op->do_readonly,
572 op->verbose);
573 if (sg_fd < 0) {
574 if (op->verbose)
575 pr2serr("Error trying to open %s: %s\n", op->device_name,
576 safe_strerror(-sg_fd));
577 ret = sg_convert_errno(-sg_fd);
578 goto fini;
579 }
580
581 if (op->do_fl >= 0)
582 res = sg_ll_start_stop_unit(sg_fd, op->do_immed, op->do_fl, 0 /* pc */,
583 true /* fl */, true /* loej */,
584 true /*start */, true /* noisy */,
585 op->verbose);
586 else if (op->do_pc > 0)
587 res = sg_ll_start_stop_unit(sg_fd, op->do_immed, op->do_mod,
588 op->do_pc, op->do_noflush, false, false,
589 true, op->verbose);
590 else
591 res = sg_ll_start_stop_unit(sg_fd, op->do_immed, 0, false,
592 op->do_noflush, op->do_loej,
593 op->do_start, true, op->verbose);
594 ret = res;
595 if (res) {
596 if (op->verbose < 2) {
597 char b[80];
598
599 sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
600 pr2serr("%s\n", b);
601 }
602 pr2serr("START STOP UNIT command failed\n");
603 }
604 fini:
605 if (sg_fd >= 0) {
606 res = sg_cmds_close_device(sg_fd);
607 if (res < 0) {
608 if (0 == ret)
609 ret = sg_convert_errno(-res);
610 }
611 }
612 if (0 == op->verbose) {
613 if (! sg_if_can2stderr("sg_start failed: ", ret))
614 pr2serr("Some error occurred, try again with '-v' "
615 "or '-vv' for more information\n");
616 }
617 return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
618 }
619