/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* SES Log reader library
*
* This library is responsible for accessing the SES log at the target address,
* formatting and returning any log entries found.
*
* The data will be returned in an nvlist_t structure allocated here.
*/
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <libseslog.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <libscf.h>
/*
* open the device with given device name
*/
static int
{
int fd;
if (fd < 0)
return (fd);
}
/*
* Initialize scsi struct
*/
static void
{
}
/*
* set control cdb of scsi structure
*/
static void
int cdb_len)
{
}
/*
* initialize sense data
*/
static void
int max_sense_len)
{
}
/*
* Initialize data going to device
*/
static void
int dxfer_len)
{
if (dxfer_len > 0) {
}
}
/*
* Executes SCSI command(or at least forwards it to lower layers).
*/
static int
{
if (time_secs > 0)
/* Took an error */
return (errno);
}
return (0);
}
/*
* Read log from device
* Invokes a SCSI LOG SENSE command.
* Return:
* 0 -> success
* SG_LIB_CAT_INVALID_OP -> Log Sense not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
* SG_LIB_CAT_NOT_READY -> device not ready,
* -1 -> other failure
*/
static int
{
{SCMD_LOG_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
if (mx_resp_len > 0xffff) {
return (-1);
}
logsCmdBlk[1] = 0;
/* pc = 1, pg_code = 0x7 (logs page) */
/* (((pc << 6) & 0xc0) | (pg_code & 0x3f)) = 0x47; */
/* pc = 1 current values */
logsCmdBlk[6] = 0;
if (res) {
} else {
}
return (ret);
}
/*
* Save the logs by walking through the entries in the response buffer.
*
* resp buffer looks like:
*
* +=====-========-========-========-========-========-========-========-=====+
* | Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* |Byte | | | | | | | | |
* |=====+====================================================================|
* | 0 | reserved | page code |
* |-----+--------------------------------------------------------------------|
* | 1 | Reserved |
* |-----+--------------------------------------------------------------------|
* | 2 |(MSB) Page Length(n-3) |
* | -- | |
* | 3 | (LSB) |
* |-----+--------------------------------------------------------------------|
* | 4 | Log Parameter (First)(Length X) |
* | -- | |
* | x+3 | |
* |-----+--------------------------------------------------------------------|
* |n-y+1| Log Parameter (Last)(Length y) |
* | -- | |
* | n | |
* +==========================================================================+
*
* Log parameter field looks like:
*
* +=====-========-========-========-========-========-========-========-=====+
* | Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* |Byte | | | | | | | | |
* |=====+====================================================================|
* | 0 |(MSB) Parameter Code |
* | -- | |
* | 1 | (LSB) |
* |-----+--------------------------------------------------------------------|
* | 2 | DU | DS | TSD | ETC | TMC | LBIN | LP |
* |-----+--------------------------------------------------------------------|
* | 3 | Parameter Length(n-3) |
* |-----+--------------------------------------------------------------------|
* | 4 | Parameter Values |
* | -- | |
* | n | |
* |-----+--------------------------------------------------------------------|
*/
static int
{
int k;
int match_found = 0;
char entry_added = 0;
int all_log_data_len;
/*
* Bytes 2 and 3 of response buffer contain the page length of
* the log entries returned.
*/
/*
* Initialize log parameter pointer to point to first log entry.
* The resp includes 4 bytes of header info and then log entries
*/
/*
* If multiple heads are reading the logs, it is possible that we
* could be re-reading some of the same log entries plus some
* new additional entries. Check to see if any entries in this read
* contain the same log entry as the last entry we read last time.
*/
/*
* We have a valid log entry from a previous read log
* operation.
*/
/*
* Start walking each log entry in response buffer looking for
* a duplicate entry.
*/
for (k = 0; k < all_log_data_len; k += param_len) {
/*
* Calculate log entry length
* Log param ptr [3] contains the log length minus the
* header info which is 4 bytes so add that in.
*/
if (param_len <= 4) {
/*
* Only header information in this entry
* process next log entry
*/
continue;
}
/*
* initialize log_str_ptr to point to string info
* returned by expander
* first 4 bytes of log parameter contains
* 2 bytes of parameter code, 1 byte of Control data
* and 1 byte for parameter length. Log string begins
* after that so add 4 to log param ptr.
*/
/*
* Check to see if this is the
* same line
*/
SES_LOG_VALID_LOG_SIZE) == 0) {
/* Found an exact match */
k += param_len;
match_found = 1;
break;
}
}
}
if (!match_found) {
k = 0;
}
if (k == all_log_data_len) {
/*
* Either there was no log data or we have
* already read these log entries.
* Just return.
*/
return (0);
}
/* Grab memory to return logs with */
/* Couldn't alloc memory for nvlist */
return (SES_LOG_FAILED_NVLIST_CREATE);
}
/*
* Start saving new log entries
* Walk the log data adding any new entries
*/
for (; k < all_log_data_len; k += param_len) {
/*
* Calculate log entry length
* Log ptr [3] contains the log length minus the header info
* which is 4 bytes so add that in
*/
if (param_len <= 4) {
/* Only header information in this entry */
/* process next log entry */
continue;
}
/*
* initialize log_str_ptr to point to string info of the log
* entry. First 4 bytes of log entry contains param code,
* control byte, and length. Log string starts after that.
*/
/*
* Format of log str is as follows
* "%8x %8x %8x %8x %8x %8x %8x %8x",
* log_entry.log_word0, log_entry.ts_u, log_entry.ts_l,
* log_entry.seq_num, log_entry.log_code, log_entry.log_word2,
* log_entry.log_word3, log_entry.log_word4
* following example has extra spaces removed to fit in 80 char
* 40004 0 42d5f5fe 185b 630002 fd0800 50800207 e482813
*/
(void) strncpy(save_buffer,
(const char *)log_str_ptr,
(const char *) log_str_ptr +
/* Add this entry to the nvlist log data */
/* Couldn't alloc space, return error */
return (SES_LOG_FAILED_NV_UNIQUE);
}
/* Error adding string, return error */
return (SES_LOG_FAILED_NV_LOG);
}
/* Error adding string, return error */
return (SES_LOG_FAILED_NV_CODE);
}
/* Error adding srtring, return error */
return (SES_LOG_FAILED_NV_SEV);
}
/* Error adding nvlist, return error */
return (SES_LOG_FAILED_NV_ENTRY);
}
entry_added = 1;
(data->number_log_entries)++;
}
if (entry_added) {
/* Update the last log entry string with last one read */
}
return (0);
}
/* Setup struct to send command to device */
static void
int dxfer_len)
{
if (dxfer_len > 0) {
}
}
/*
* Invokes a SCSI MODE SENSE(10) command.
* Return:
* 0 for success
* SG_LIB_CAT_INVALID_OP -> invalid opcode
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb
* SG_LIB_CAT_NOT_READY -> device not ready
* -1 -> other failure
*/
static int
{
{SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
modesCmdBlk[1] = 0;
modesCmdBlk[3] = 0;
if (res) {
} else {
}
return (ret);
}
/*
* Invokes a SCSI MODE SELECT(10) command.
* Return:
* 0 for success.
* SG_LIB_CAT_INVALID_OP for invalid opcode
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
* SG_LIB_CAT_NOT_READY -> device not ready,
* -1 -> other failure
*/
static int
{
{SCMD_MODE_SELECT_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
modesCmdBlk[1] = 0;
/*
* modesCmdBlk 2 equal 0 PC 0 return current page code 0 return
* vendor specific
*/
if (res) {
} else {
}
return (ret);
}
/*
* MODE SENSE 10 commands yield a response that has block descriptors followed
* by mode pages. In most cases users are interested in the first mode page.
* This function returns the(byte) offset of the start of the first mode page.
* Returns >= 0 is successful or -1 if failure. If there is a failure
* a message is written to err_buff.
*/
/*
* return data looks like:
* Table 92 - Mode parameter header(10)
* Bit
* Byte
* 7 6 5 4 3 2 1 0
* ----------------------------------------------------------
* 0 MSB Data length
* 1 LSB Data length
* ----------------------------------------------------------
* 2 Medium type
* ----------------------------------------------------------
* 3 Device-specific parameter
* ----------------------------------------------------------
* 4 Reserved
* ----------------------------------------------------------
* 5 Reserved
* ----------------------------------------------------------
* 6 MSB block descriptor length
* 7 LSB block descriptor length
* ----------------------------------------------------------
* block desciptors....
* -----------------------
* mode sense page:
* 0 : ps Reserved : page Code
* 1 : Page Length(n-1)
* 2-N Mode parameters
*/
static int
{
int bd_len;
int calc_len;
int offset;
/* Too short of a response buffer */
return (-1);
}
/* LongLBA doesn't change this calculation */
/* Given response length to small */
offset = -1;
/* Calculated response length too small */
offset = -1;
}
return (offset);
}
/* fill in unique host id */
static void
{
char *str;
long myhostid;
/*
* The appliance kit contains all the information for the AK systems.
* To see the values run:
* Looking for this:
*/
/* Worked okay */
return;
}
}
}
/* If here, didn't get unique host id from AK */
/* Use host id */
}
/*
* Clear logs
*/
static int
{
int md_len;
int read_in_len = 0;
long poll_time;
unsigned long seq_num = 0;
clear_data.subpage_code = 0;
/*
* convert nanosecond time to seconds
*/
/* Add 5 minutes to poll time to allow for data retrieval time */
/*
* retrieve the last read sequence number from the last
* log entry read.
*/
/*
* We have a valid log entry from a previous read log
* operation.
*/
(void) strncpy(seq_num_str,
(const char *) data->last_log_entry +
}
read_in_len = sizeof (clear_data);
/* do MODE SENSE to fetch current values */
if (0 != res) {
/* Error during mode sense */
return (SES_LOG_FAILED_MODE_SENSE);
}
/* Setup mode Select to clear logs */
if (off < 0) {
/* Mode page offset error */
return (SES_LOG_FAILED_MODE_SENSE_OFFSET);
}
ref_md[0] = 0;
ref_md[1] = 0;
/* Data length to large */
return (SES_LOG_FAILED_BAD_DATA_LEN);
}
/* Content length not correct */
return (SES_LOG_FAILED_BAD_CONTENT_LEN);
}
/* reference model doesn't have use subpage format bit set */
/* Even though it should have */
/* don't send the command */
return (SES_LOG_FAILED_FORMAT_PAGE_ERR);
}
sizeof (clear_data));
if (res != 0) {
return (SES_LOG_FAILED_MODE_SELECT);
}
return (0);
}
/*
* Gather data from given device.
*/
static int
{
int sg_fd;
int error;
/* Open device */
/* Failed to open device */
return (SES_LOG_FAILED_TO_OPEN_DEVICE);
}
/* Read the logs */
if (res != 0) {
/* Some sort of Error during read of logs */
return (SES_LOG_FAILED_TO_READ_DEVICE);
}
/* Save the logs */
if (error != 0) {
return (error);
}
/* Clear the logs */
return (error);
}
/*
* Access the SES target identified by the indicated path. Read the logs
* and return them in a nvlist.
*/
int
{
int error;
/* Initialize return data */
data->number_log_entries = 0;
data->size_of_log_entries = 0;
/* NULL Target path, return error */
return (SES_LOG_FAILED_NULL_TARGET_PATH);
}
/* Try to find a valid path */
data->target_path);
data->target_path);
/* Couldn't find a path that exists */
return (SES_LOG_FAILED_BAD_TARGET_PATH);
}
}
/* Update the size of log entries being returned */
return (error);
}