xref: /aosp_15_r20/external/sg3_utils/src/sg_start.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
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