📄 scsi.c
字号:
if (SEND_COMMAND(sg_dev, cmd) == -1) { perror("failed on EVPD Page 0x83 INQUIRY"); return 1; } } while (scsi_retryable_error(sg_dev, cmd, FALSE, DEF_WAIT)); /* * Get the product ID */ if(WAS_OK(cmd)) { i = 4; /* location of the first descriptor */ while(i < cmd.buf[3]) { switch(cmd.buf[i + 1] & 0xf) { case 0: sg_dev->id_nonunique = malloc(cmd.buf[i + 3] + 1); if(sg_dev->id_nonunique != NULL) { memcpy(sg_dev->id_nonunique, &cmd.buf[i + 4], cmd.buf[i + 3]); sg_dev->id_nonunique[cmd.buf[i + 3]] = '\0'; } break; case 1: sg_dev->id_vendor = malloc(cmd.buf[i + 3] + 1); if(sg_dev->id_vendor != NULL) { memcpy(sg_dev->id_vendor, &cmd.buf[i + 4], cmd.buf[i + 3]); sg_dev->id_vendor[cmd.buf[i + 3]] = '\0'; } break; case 2: memcpy((void *)&sg_dev->id_eui64[0], &cmd.buf[i + 4], 8); break; case 3: memcpy((void *)&sg_dev->id_fcph[0], &cmd.buf[i + 4], 8); break; default: break; } i += cmd.buf[i + 3] + 4; } } else if (WAS_SENSE(cmd) && SENSE_KEY(cmd) == ILLEGAL_REQUEST) { /* the device doesn't support the page requested, don't give an error * since this is an optional feature */ return 1; } else { printf("%s: scsi error on EVPD Page 0x83 INQUIRY, status byte = 0x%x\n", sg_dev->name, STATUS(cmd)); if(WAS_SENSE(cmd)) { printf("%s: SENSE_KEY 0x%02x, ASC 0x%02x, ASQ 0x%02x\n", sg_dev->name, SENSE_KEY(cmd), ASC(cmd), ASQ(cmd)); } return 1; } return 0;}/* * Function: scsi_get_serial_number_page * * Inputs: * scsi_sg_dev_t * - The sg_dev we want to get size information for * * Outputs: * int - 0 on success, 1 on any error or failure. * * Purpose: * Used to provide a unique identifier to each device. This can then * be used to detect multipath drives on the fly. This is the * backup method we use when the device doesn't support the device * id page. This isn't quite as reliable as the device id page because * we have no assurance from the device that the number we get will * be unique, where as several of the options from the device id page * are assured of being unique. Once we get the serial number, which * is in ASCII form, we will concatenate it with the device Vendor * string, Model string, and Revision string to produce a rather longish, * but hopefully unique device id. It will then be stored in the * sg_dev->id_vendor spot. */static intscsi_get_serial_number_page(scsi_sg_dev_t *sg_dev){ scsi_send_command_t cmd; char *buffer; /* * Get the Serial Number page from another INQUIRY command */ do { cmd.outsize = 0; cmd.insize = 255; memset(cmd.buf, 0, 255); cmd.buf[0] = INQUIRY; cmd.buf[1] = 1; /* EVPD bit */ cmd.buf[2] = 0x80; /* Serial Number Page */ cmd.buf[4] = 255; if (SEND_COMMAND(sg_dev, cmd) == -1) { perror("failed on EVPD Page 0x80 INQUIRY"); return 1; } } while (scsi_retryable_error(sg_dev, cmd, FALSE, DEF_WAIT)); /* * Get the Serial Number and make a string out of it. */ if(WAS_OK(cmd)) { buffer = malloc(cmd.buf[3] + 1); if(buffer == NULL) return 1; strncpy(&buffer[0], &cmd.buf[4], cmd.buf[3]); buffer[cmd.buf[3]] = '\0'; sg_dev->serial_number = buffer; } else if (WAS_SENSE(cmd) && SENSE_KEY(cmd) == ILLEGAL_REQUEST) { /* the device doesn't support the page requested, don't give an error * since this is an optional feature */ return 1; } else { printf("%s: scsi error on EVPD Page 0x80 INQUIRY, status byte = 0x%x\n", sg_dev->name, STATUS(cmd)); if(WAS_SENSE(cmd)) { printf("%s: SENSE_KEY 0x%02x, ASC 0x%02x, ASQ 0x%02x\n", sg_dev->name, SENSE_KEY(cmd), ASC(cmd), ASQ(cmd)); } return 1; } return 0;}/* * Function: scsi_init_device_size * * Inputs: * scsi_sg_dev_t * - The sg_dev we want to get size information for * * Outputs: * int - 0 on success, 1 if the drive is currently reserved, 2 on any * other failure. * scsi_sg_dev_t * - the size information will be placed into the * scsi_sg_dev struct on success. * * Purpose: * This was part of scsi_init_sg_device(), but in order to make that * function more graceful in the event that the device is currently * reserved by another host at the time we are trying to initialize it, * we separated this function out. This is only really needed if you * plan to use extent based reservations on less than the whole drive * anyway, so it can be skipped entirely if you know that you will only * need to reserve whole drives. */intscsi_init_device_size(scsi_sg_dev_t * sg_dev){ scsi_send_command_t cmd; /* * Not everything we needed is part of an INQUIRY return. Next, we * want to establish the block size for any extent based reservations. * Try to get block_size and num_blocks first by using a READ_CAPACITY, * but since that command isn't mandatory in the spec, fall back to * a MODE_SENSE and general block descriptors if it isn't available. * If both methods give permanent failures, then assume 512 bytes per * block and ignore the num_blocks element. */ do { cmd.outsize = 0; cmd.insize = 16; memset(cmd.buf, 0, 16); cmd.buf[0] = READ_CAPACITY; if (SEND_COMMAND(sg_dev, cmd) == -1) { perror("failed on READ_CAPACITY"); return (2); } } while (scsi_retryable_error(sg_dev, cmd, FALSE, DEF_WAIT)); if (WAS_SENSE(cmd) && (SENSE_KEY(cmd) == ILLEGAL_REQUEST)) { do { cmd.outsize = 0; cmd.insize = 255; memset(cmd.buf, 0, 255); cmd.buf[0] = MODE_SENSE; cmd.buf[4] = 255; if (SEND_COMMAND(sg_dev, cmd) == -1) { perror("failed on MODE_SENSE"); return (2); } } while (scsi_retryable_error(sg_dev, cmd, FALSE, DEF_WAIT)); if (RES_CONFLICT(cmd)) { printf("%s: device is currently reserved.\n", sg_dev->name); return (1); } else if (!WAS_OK(cmd) || (cmd.buf[3] != 8)) { printf("%s: both MODE_SENSE and READ_CAPACITY failed " "to return a block size\n", sg_dev->name); printf("\tassuming 512 bytes per block\n"); sg_dev->block_size = 512; sg_dev->num_blocks = 0; return (2); } else { sg_dev->num_blocks = ((cmd.buf[4] << 24) | (cmd.buf[5] << 16) | (cmd.buf[6] << 8) | cmd.buf[7]); sg_dev->block_size = ((cmd.buf[9] << 16) | (cmd.buf[10] << 8) | cmd.buf[11]); } } else if (WAS_SENSE(cmd)) { printf("%s: failure on READ_CAPACITY, SENSE_KEY 0x%02x\n", sg_dev->name, SENSE_KEY(cmd)); return (2); } else if (RES_CONFLICT(cmd)) { printf("%s: the device is currently reserved.\n", sg_dev->name); return (1); } else if (!WAS_OK(cmd)) { printf("%s: failure on READ_CAPACITY, status byte 0x%02x\n", sg_dev->name, STATUS(cmd)); return (2); } else { sg_dev->num_blocks = 1 + ((cmd.buf[0] << 24) | (cmd.buf[1] << 16) | (cmd.buf[2] << 8) | cmd.buf[3]); sg_dev->block_size = (cmd.buf[4] << 24) | (cmd.buf[5] << 16) | (cmd. buf[6] << 8) | cmd.buf[7]; } return (0);}/* * Function: scsi_release_sg_device * * Inputs: * scsi_sg_dev_t * - Pointer to the sg_dev structure to be released. * * Outputs: * int - On success, 0. On failure, 1. * * Purpose: * This routine will free all current reservations on a device that * are listed in the reservations list. It will then free() any memory * that has been malloc()'ed for the sg_name item, close the file * descriptor that points to the sg device entry, and then free() the * sg_dev struct itself. The calling program is responsible for * closing the file descriptor to the block device (aka, sg_dev->disk_fd) * itself. Since scsi_init_sg_device() doesn't open that file * descriptor, but instead simply records the one that was passed in to * it, it was deemed inconsistent for this function to go around closing * file descriptors that the init function didn't open(). The calling * function is also responsible for calling free() on the sg_dev->name * buffer that was passed to scsi_init_sg_device() if appropriate. */intscsi_release_sg_device(scsi_sg_dev_t * sg_dev){ if (sg_dev == NULL) return (0); if (sg_dev->sg_name) free((void *)sg_dev->sg_name); if (sg_dev->id_nonunique) free((void *)sg_dev->id_nonunique); if (sg_dev->id_vendor) free((void *)sg_dev->id_vendor); if (sg_dev->serial_number) free((void *)sg_dev->serial_number); close(sg_dev->fd); free(sg_dev); return (0);}/* * Function: scsi_wait_device_ready * * Inputs: * scsi_sg_dev_t * - The device to wait on * int - Amount of time to wait (in seconds) before giving up. * * Outputs: * int - 0 if device is ready, 1 if we timed out, -1 on error. * * Purpose: * This function will spin in a loop waiting for a device to become * ready. It will return when either the device reports that it is * ready for use or the timeout has passed, whichever happens first. * */intscsi_wait_device_ready(scsi_sg_dev_t * sg_dev, int timeout){ scsi_send_command_t cmd; int i; if (timeout < 0 || timeout > 60) { printf("%s: wait timeout out of range (%d)\n", sg_dev->name, timeout); printf("Allowed timeout is between 0 and 60 seconds.\n"); return (-1); } i = 0; do { if (i) usleep(100); cmd.outsize = 0; cmd.insize = 16; memset(cmd.buf, 0, 16); cmd.buf[0] = TEST_UNIT_READY; cmd.buf[1] = sg_dev->scsi_dev_lun << 5; if (SEND_COMMAND(sg_dev, cmd) == -1) { perror("failure sending TEST_UNIT_READY"); return (-1); } if (!WAS_SENSE(cmd)) { break; } } while ((SENSE_KEY(cmd) != NO_SENSE) && (++i < (timeout * 10))); if (STATUS(cmd) == GOOD || SENSE_KEY(cmd) == NO_SENSE) return (0); else return (1);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -