Home | History | Annotate | Download | only in sg_write_buffer
      1 /*
      2  * Copyright (c) 2006-2018 Luben Tuikov and Douglas Gilbert.
      3  * All rights reserved.
      4  * Use of this source code is governed by a BSD-style
      5  * license that can be found in the BSD_LICENSE file.
      6  */
      7 
      8 #include <unistd.h>
      9 #include <fcntl.h>
     10 #include <stdio.h>
     11 #include <stdlib.h>
     12 #include <stdarg.h>
     13 #include <stdbool.h>
     14 #include <ctype.h>
     15 #include <string.h>
     16 #include <getopt.h>
     17 #define __STDC_FORMAT_MACROS 1
     18 #include <inttypes.h>
     19 
     20 #ifdef HAVE_CONFIG_H
     21 #include "config.h"
     22 #endif
     23 #include "sg_lib.h"
     24 #include "sg_cmds_basic.h"
     25 #include "sg_cmds_extra.h"
     26 #include "sg_unaligned.h"
     27 #include "sg_pr2serr.h"
     28 
     29 #ifdef SG_LIB_WIN32
     30 #ifdef SG_LIB_WIN32_DIRECT
     31 #include "sg_pt.h"      /* needed for scsi_pt_win32_direct() */
     32 #endif
     33 #endif
     34 
     35 /*
     36  * This utility issues the SCSI WRITE BUFFER command to the given device.
     37  */
     38 
     39 static const char * version_str = "1.24 20180111";    /* spc5r18 */
     40 
     41 #define ME "sg_write_buffer: "
     42 #define DEF_XFER_LEN (8 * 1024 * 1024)
     43 #define EBUFF_SZ 256
     44 
     45 #define WRITE_BUFFER_CMD 0x3b
     46 #define WRITE_BUFFER_CMDLEN 10
     47 #define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
     48 #define DEF_PT_TIMEOUT 300      /* 300 seconds, 5 minutes */
     49 
     50 static struct option long_options[] = {
     51         {"bpw", required_argument, 0, 'b'},
     52         {"dry-run", no_argument, 0, 'd'},
     53         {"dry_run", no_argument, 0, 'd'},
     54         {"help", no_argument, 0, 'h'},
     55         {"id", required_argument, 0, 'i'},
     56         {"in", required_argument, 0, 'I'},
     57         {"length", required_argument, 0, 'l'},
     58         {"mode", required_argument, 0, 'm'},
     59         {"offset", required_argument, 0, 'o'},
     60         {"read-stdin", no_argument, 0, 'r'},
     61         {"read_stdin", no_argument, 0, 'r'},
     62         {"raw", no_argument, 0, 'r'},
     63         {"skip", required_argument, 0, 's'},
     64         {"specific", required_argument, 0, 'S'},
     65         {"timeout", required_argument, 0, 't' },
     66         {"verbose", no_argument, 0, 'v'},
     67         {"version", no_argument, 0, 'V'},
     68         {0, 0, 0, 0},
     69 };
     70 
     71 
     72 static void
     73 usage()
     74 {
     75     pr2serr("Usage: "
     76             "sg_write_buffer [--bpw=CS] [--dry-run] [--help] [--id=ID] "
     77             "[--in=FILE]\n"
     78             "                       [--length=LEN] [--mode=MO] "
     79             "[--offset=OFF]\n"
     80             "                       [--read-stdin] [--skip=SKIP] "
     81             "[--specific=MS]\n"
     82             "                       [--timeout=TO] [--verbose] [--version] "
     83             "DEVICE\n"
     84             "  where:\n"
     85             "    --bpw=CS|-b CS         CS is chunk size: bytes per write "
     86             "buffer\n"
     87             "                           command (def: 0 -> as many as "
     88             "possible)\n"
     89             "    --dry-run|-d           skip WRITE BUFFER commands, do "
     90             "everything else\n"
     91             "    --help|-h              print out usage message then exit\n"
     92             "    --id=ID|-i ID          buffer identifier (0 (default) to "
     93             "255)\n"
     94             "    --in=FILE|-I FILE      read from FILE ('-I -' read "
     95             "from stdin)\n"
     96             "    --length=LEN|-l LEN    length in bytes to write; may be "
     97             "deduced from\n"
     98             "                           FILE\n"
     99             "    --mode=MO|-m MO        write buffer mode, MO is number or "
    100             "acronym\n"
    101             "                           (def: 0 -> 'combined header and "
    102             "data' (obs))\n"
    103             "    --offset=OFF|-o OFF    buffer offset (unit: bytes, def: 0)\n"
    104             "    --read-stdin|-r        read from stdin (same as '-I -')\n"
    105             "    --skip=SKIP|-s SKIP    bytes in file FILE to skip before "
    106             "reading\n"
    107             "    --specific=MS|-S MS    mode specific value; 3 bit field "
    108             "(0 to 7)\n"
    109             "    --timeout=TO|-t TO     command timeout in seconds (def: "
    110             "300)\n"
    111             "    --verbose|-v           increase verbosity\n"
    112             "    --version|-V           print version string and exit\n\n"
    113             "Performs one or more SCSI WRITE BUFFER commands. Use '-m xxx' "
    114             "to list\navailable modes. A chunk size of 4 KB ('--bpw=4k') "
    115             "seems to work well.\nExample: sg_write_buffer -b 4k -I xxx.lod "
    116             "-m 7 /dev/sg3\n"
    117           );
    118 
    119 }
    120 
    121 #define MODE_HEADER_DATA        0
    122 #define MODE_VENDOR             1
    123 #define MODE_DATA               2
    124 #define MODE_DNLD_MC            4
    125 #define MODE_DNLD_MC_SAVE       5
    126 #define MODE_DNLD_MC_OFFS       6
    127 #define MODE_DNLD_MC_OFFS_SAVE  7
    128 #define MODE_ECHO_BUFFER        0x0A
    129 #define MODE_DNLD_MC_EV_OFFS_DEFER 0x0D
    130 #define MODE_DNLD_MC_OFFS_DEFER 0x0E
    131 #define MODE_ACTIVATE_MC        0x0F
    132 #define MODE_EN_EX_ECHO         0x1A
    133 #define MODE_DIS_EX             0x1B
    134 #define MODE_DNLD_ERR_HISTORY   0x1C
    135 
    136 
    137 struct mode_s {
    138         const char *mode_string;
    139         int   mode;
    140         const char *comment;
    141 };
    142 
    143 static struct mode_s mode_arr[] = {
    144         {"hd",         MODE_HEADER_DATA, "combined header and data "
    145                 "(obsolete)"},
    146         {"vendor",     MODE_VENDOR,    "vendor specific"},
    147         {"data",       MODE_DATA,      "data"},
    148         {"dmc",        MODE_DNLD_MC,   "download microcode and activate"},
    149         {"dmc_save",   MODE_DNLD_MC_SAVE, "download microcode, save and "
    150                 "activate"},
    151         {"dmc_offs",   MODE_DNLD_MC_OFFS, "download microcode with offsets "
    152                 "and activate"},
    153         {"dmc_offs_save", MODE_DNLD_MC_OFFS_SAVE, "download microcode with "
    154                 "offsets, save and\n\t\t\t\tactivate"},
    155         {"echo",       MODE_ECHO_BUFFER, "write data to echo buffer"},
    156         {"dmc_offs_ev_defer", MODE_DNLD_MC_EV_OFFS_DEFER, "download "
    157                 "microcode with offsets, select\n\t\t\t\tactivation event, "
    158                 "save and defer activation"},
    159         {"dmc_offs_defer", MODE_DNLD_MC_OFFS_DEFER, "download microcode "
    160                 "with offsets, save and\n\t\t\t\tdefer activation"},
    161         {"activate_mc", MODE_ACTIVATE_MC, "activate deferred microcode"},
    162         {"en_ex",      MODE_EN_EX_ECHO, "enable expander communications "
    163                 "protocol and\n\t\t\t\techo buffer (obsolete)"},
    164         {"dis_ex",     MODE_DIS_EX, "disable expander communications "
    165                 "protocol\n\t\t\t\t(obsolete)"},
    166         {"deh",        MODE_DNLD_ERR_HISTORY, "download application client "
    167                 "error history "},
    168         {NULL, 0, NULL},
    169 };
    170 
    171 static void
    172 print_modes(void)
    173 {
    174     const struct mode_s * mp;
    175 
    176     pr2serr("The modes parameter argument can be numeric (hex or decimal)\n"
    177             "or symbolic:\n");
    178     for (mp = mode_arr; mp->mode_string; ++mp) {
    179         pr2serr(" %2d (0x%02x)  %-18s%s\n", mp->mode, mp->mode,
    180                 mp->mode_string, mp->comment);
    181     }
    182     pr2serr("\nAdditionally '--bpw=<val>,act' does a activate deferred "
    183             "microcode after\nsuccessful dmc_offs_defer and "
    184             "dmc_offs_ev_defer mode downloads.\n");
    185 }
    186 
    187 
    188 int
    189 main(int argc, char * argv[])
    190 {
    191     bool bpw_then_activate = false;
    192     bool dry_run = false;
    193     bool got_stdin = false;
    194     bool wb_len_given = false;
    195     int sg_fd, infd, res, c, len, k, n;
    196     int bpw = 0;
    197     int do_help = 0;
    198     int ret = 0;
    199     int verbose = 0;
    200     int wb_id = 0;
    201     int wb_len = 0;
    202     int wb_mode = 0;
    203     int wb_offset = 0;
    204     int wb_skip = 0;
    205     int wb_timeout = DEF_PT_TIMEOUT;
    206     int wb_mspec = 0;
    207     const char * device_name = NULL;
    208     const char * file_name = NULL;
    209     unsigned char * dop = NULL;
    210     char * cp;
    211     const struct mode_s * mp;
    212     char ebuff[EBUFF_SZ];
    213 
    214     while (1) {
    215         int option_index = 0;
    216 
    217         c = getopt_long(argc, argv, "b:dhi:I:l:m:o:rs:S:t:vV", long_options,
    218                         &option_index);
    219         if (c == -1)
    220             break;
    221 
    222         switch (c) {
    223         case 'b':
    224             bpw = sg_get_num(optarg);
    225             if (bpw < 0) {
    226                 pr2serr("argument to '--bpw' should be in a positive "
    227                         "number\n");
    228                 return SG_LIB_SYNTAX_ERROR;
    229             }
    230             if ((cp = strchr(optarg, ','))) {
    231                 if (0 == strncmp("act", cp + 1, 3))
    232                     bpw_then_activate = true;
    233             }
    234             break;
    235         case 'd':
    236             dry_run = true;
    237             break;
    238         case 'h':
    239         case '?':
    240             ++do_help;
    241             break;
    242         case 'i':
    243             wb_id = sg_get_num(optarg);
    244             if ((wb_id < 0) || (wb_id > 255)) {
    245                 pr2serr("argument to '--id' should be in the range 0 to "
    246                         "255\n");
    247                 return SG_LIB_SYNTAX_ERROR;
    248             }
    249             break;
    250         case 'I':
    251             file_name = optarg;
    252             break;
    253         case 'l':
    254             wb_len = sg_get_num(optarg);
    255             if (wb_len < 0) {
    256                 pr2serr("bad argument to '--length'\n");
    257                 return SG_LIB_SYNTAX_ERROR;
    258              }
    259              wb_len_given = true;
    260              break;
    261         case 'm':
    262             if (isdigit(*optarg)) {
    263                 wb_mode = sg_get_num(optarg);
    264                 if ((wb_mode < 0) || (wb_mode > 31)) {
    265                     pr2serr("argument to '--mode' should be in the range 0 "
    266                             "to 31\n");
    267                     return SG_LIB_SYNTAX_ERROR;
    268                 }
    269             } else {
    270                 len = strlen(optarg);
    271                 for (mp = mode_arr; mp->mode_string; ++mp) {
    272                     if (0 == strncmp(mp->mode_string, optarg, len)) {
    273                         wb_mode = mp->mode;
    274                         break;
    275                     }
    276                 }
    277                 if (! mp->mode_string) {
    278                     print_modes();
    279                     return SG_LIB_SYNTAX_ERROR;
    280                 }
    281             }
    282             break;
    283         case 'o':
    284            wb_offset = sg_get_num(optarg);
    285            if (wb_offset < 0) {
    286                 pr2serr("bad argument to '--offset'\n");
    287                 return SG_LIB_SYNTAX_ERROR;
    288             }
    289             break;
    290         case 'r':       /* --read-stdin and --raw (previous name) */
    291             file_name = "-";
    292             break;
    293         case 's':
    294            wb_skip = sg_get_num(optarg);
    295            if (wb_skip < 0) {
    296                 pr2serr("bad argument to '--skip'\n");
    297                 return SG_LIB_SYNTAX_ERROR;
    298             }
    299             break;
    300         case 'S':
    301             wb_mspec = sg_get_num(optarg);
    302             if ((wb_mspec < 0) || (wb_mspec > 7)) {
    303                 pr2serr("expected argument to '--specific' to be 0 to 7\n");
    304                 return SG_LIB_SYNTAX_ERROR;
    305             }
    306             break;
    307         case 't':
    308             wb_timeout = sg_get_num(optarg);
    309             if (wb_timeout < 0) {
    310                 pr2serr("Invalid argument to '--timeout'\n");
    311                 return SG_LIB_SYNTAX_ERROR;
    312             }
    313             break;
    314         case 'v':
    315             ++verbose;
    316             break;
    317         case 'V':
    318             pr2serr(ME "version: %s\n", version_str);
    319             return 0;
    320         default:
    321             pr2serr("unrecognised option code 0x%x ??\n", c);
    322             usage();
    323             return SG_LIB_SYNTAX_ERROR;
    324         }
    325     }
    326     if (do_help) {
    327         if (do_help > 1) {
    328             usage();
    329             pr2serr("\n");
    330             print_modes();
    331         } else
    332             usage();
    333         return 0;
    334     }
    335     if (optind < argc) {
    336         if (NULL == device_name) {
    337             device_name = argv[optind];
    338             ++optind;
    339         }
    340         if (optind < argc) {
    341             for (; optind < argc; ++optind)
    342                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
    343             usage();
    344             return SG_LIB_SYNTAX_ERROR;
    345         }
    346     }
    347 
    348     if (NULL == device_name) {
    349         pr2serr("missing device name!\n");
    350         usage();
    351         return SG_LIB_SYNTAX_ERROR;
    352     }
    353 
    354     if ((wb_len > 0) && (bpw > wb_len)) {
    355         pr2serr("trim chunk size (CS) to be the same as LEN\n");
    356         bpw = wb_len;
    357     }
    358 
    359 #ifdef SG_LIB_WIN32
    360 #ifdef SG_LIB_WIN32_DIRECT
    361     if (verbose > 4)
    362         pr2serr("Initial win32 SPT interface state: %s\n",
    363                 scsi_pt_win32_spt_state() ? "direct" : "indirect");
    364     scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */);
    365 #endif
    366 #endif
    367 
    368     sg_fd = sg_cmds_open_device(device_name, false /* rw */, verbose);
    369     if (sg_fd < 0) {
    370         pr2serr(ME "open error: %s: %s\n", device_name,
    371                 safe_strerror(-sg_fd));
    372         return SG_LIB_FILE_ERROR;
    373     }
    374     if (file_name || (wb_len > 0)) {
    375         if (0 == wb_len)
    376             wb_len = DEF_XFER_LEN;
    377         if (NULL == (dop = (unsigned char *)malloc(wb_len))) {
    378             pr2serr(ME "out of memory\n");
    379             ret = SG_LIB_SYNTAX_ERROR;
    380             goto err_out;
    381         }
    382         memset(dop, 0xff, wb_len);
    383         if (file_name) {
    384             got_stdin = (0 == strcmp(file_name, "-"));
    385             if (got_stdin) {
    386                 if (wb_skip > 0) {
    387                     pr2serr("Can't skip on stdin\n");
    388                     ret = SG_LIB_FILE_ERROR;
    389                     goto err_out;
    390                 }
    391                 infd = STDIN_FILENO;
    392             } else {
    393                 if ((infd = open(file_name, O_RDONLY)) < 0) {
    394                     snprintf(ebuff, EBUFF_SZ,
    395                              ME "could not open %s for reading", file_name);
    396                     perror(ebuff);
    397                     ret = SG_LIB_FILE_ERROR;
    398                     goto err_out;
    399                 } else if (sg_set_binary_mode(infd) < 0)
    400                     perror("sg_set_binary_mode");
    401                 if (wb_skip > 0) {
    402                     if (lseek(infd, wb_skip, SEEK_SET) < 0) {
    403                         snprintf(ebuff,  EBUFF_SZ, ME "couldn't skip to "
    404                                  "required position on %s", file_name);
    405                         perror(ebuff);
    406                         close(infd);
    407                         ret = SG_LIB_FILE_ERROR;
    408                         goto err_out;
    409                     }
    410                 }
    411             }
    412             res = read(infd, dop, wb_len);
    413             if (res < 0) {
    414                 snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s",
    415                          file_name);
    416                 perror(ebuff);
    417                 if (! got_stdin)
    418                     close(infd);
    419                 ret = SG_LIB_FILE_ERROR;
    420                 goto err_out;
    421             }
    422             if (res < wb_len) {
    423                 if (wb_len_given) {
    424                     pr2serr("tried to read %d bytes from %s, got %d bytes\n",
    425                             wb_len, file_name, res);
    426                     pr2serr("pad with 0xff bytes and continue\n");
    427                 } else {
    428                     if (verbose) {
    429                         pr2serr("tried to read %d bytes from %s, got %d "
    430                                 "bytes\n", wb_len, file_name, res);
    431                         pr2serr("will write %d bytes", res);
    432                         if ((bpw > 0) && (bpw < wb_len))
    433                             pr2serr(", %d bytes per WRITE BUFFER command\n",
    434                                     bpw);
    435                         else
    436                             pr2serr("\n");
    437                     }
    438                     wb_len = res;
    439                 }
    440             }
    441             if (! got_stdin)
    442                 close(infd);
    443         }
    444     }
    445 
    446     res = 0;
    447     if (bpw > 0) {
    448         for (k = 0; k < wb_len; k += n) {
    449             n = wb_len - k;
    450             if (n > bpw)
    451                 n = bpw;
    452             if (verbose)
    453                 pr2serr("sending write buffer, mode=0x%x, mspec=%d, id=%d, "
    454                         " offset=%d, len=%d\n", wb_mode, wb_mspec, wb_id,
    455                         wb_offset + k, n);
    456             if (dry_run) {
    457                 if (verbose)
    458                     pr2serr("skipping WRITE BUFFER command due to "
    459                             "--dry-run\n");
    460                 res = 0;
    461             } else
    462                 res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id,
    463                                             wb_offset + k, dop + k, n,
    464                                             wb_timeout, true, verbose);
    465             if (res)
    466                 break;
    467         }
    468         if (bpw_then_activate) {
    469             if (verbose)
    470                 pr2serr("sending Activate deferred microcode [0xf]\n");
    471             if (dry_run) {
    472                 if (verbose)
    473                     pr2serr("skipping WRITE BUFFER(ACTIVATE) command due to "
    474                             "--dry-run\n");
    475                 res = 0;
    476             } else
    477                 res = sg_ll_write_buffer_v2(sg_fd, MODE_ACTIVATE_MC,
    478                                             0 /* buffer_id */,
    479                                             0 /* buffer_offset */, 0,
    480                                             NULL, 0, wb_timeout, true,
    481                                             verbose);
    482         }
    483     } else {
    484         if (verbose)
    485             pr2serr("sending single write buffer, mode=0x%x, mpsec=%d, "
    486                     "id=%d, offset=%d, len=%d\n", wb_mode, wb_mspec, wb_id,
    487                     wb_offset, wb_len);
    488         if (dry_run) {
    489             if (verbose)
    490                 pr2serr("skipping WRITE BUFFER(all in one) command due to "
    491                         "--dry-run\n");
    492             res = 0;
    493         } else
    494             res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id,
    495                                         wb_offset, dop, wb_len, wb_timeout,
    496                                         true, verbose);
    497     }
    498     if (0 != res) {
    499         char b[80];
    500 
    501         ret = res;
    502         sg_get_category_sense_str(res, sizeof(b), b, verbose);
    503         pr2serr("Write buffer failed: %s\n", b);
    504     }
    505 
    506 err_out:
    507     if (dop)
    508         free(dop);
    509     res = sg_cmds_close_device(sg_fd);
    510     if (res < 0) {
    511         pr2serr("close error: %s\n", safe_strerror(-res));
    512         if (0 == ret)
    513             return SG_LIB_FILE_ERROR;
    514     }
    515     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
    516 }
    517