/***************************************** try-error_log_get.c ************************************ Try the AIX error_log_get() function, to retrieve entries from the AIX Error Log. COMPILATION: c89 -o try-error_log_get try-error_log_get.c -lc -ls -ldiag INVOCATION: "try-error_log_get" NOTES: - The AIX Error Log timestamp, in character form, is: MMDDHHMMYY (month, day, hour, minute, and year). HISTORY: 2000/11/24 Created. Richard Sims, Boston University OIT error_log_get Purpose Returns error-log entries. Syntax #include int error_log_get ( Option, Criteria, Err_data ) int Option; char *Criteria; struct errdata *Err_data; Description The error_log_get subroutine allows the Diagnostic Application (DA) to query the error log for entries. Parameters Option Describes the operation to be performed. The following values are defined: INIT Initializes error log retrieve, and return first entry, if any. Typically used with a "-s MMDDHHMMYY" time specification in the second operand to specify a starting time that is the time of last inspection, so that you see only new entries. SUBSEQ Gets next error-log entry. TERMI Ends error log retrieve. (Ostensibly, the remaining function args are not meaningful with this, though that is not documented.) NVRAMEL Use the NVRAM error log as the source for the error log retrieve. Only the following members of struct errdata are available when the error log is obtained from NVRAM: time_stamp err_id resource detail_data_len detail_data Criteria Used with the INIT option to specify which device to obtain the error log data for and how far back to search. This parameter can be set to any valid option used by the errpt command. When used with the NVRAMEL option, this can be either a list of resource names (with the -N switch) or an error ID (with the -j switch), but not both. Err_data (struct errdata) Data type that contains the following data filled in for use by the DA. struct errdata { unsigned sequence; /+ sequence number of entry +/ unsigned time_stamp; /+ entry timestamp +/ unsigned err_id; /+ error ID code +/ char *machine_id; /+ machine ID +/ char *node_id; /+ node ID +/ char *class; /+ H=hardware, S=software +/ char *type; /+ PERM,TEMP,PERF,PEND,UNKN +/ char *resource; /+ Configured device name. Often newline at end. +/ char *vpd_data; /+ VPD info +/ char *conn_where; /+ connwhere field of CuDv +/ char *location; /+ location field of CuDv +/ unsigned detail_data_len; /+ length of detail data +/ char *detail_data; /+ detail data +/ }; Return Value Return values are dependent on the option performed: INIT 0: No error, no new data available 1: Error-log entry available -1: Error obtaining data SUBSEQ 0: No more entries available 1: Error-log entry available TERMI 0: Terminate successful NVRAMEL 0: No entries matching criteria 1: Error-log entry available -1: Error accessing NVRAM -2: Invalid criteria ********************************************************************************************/ /* Include files, from /usr/include... */ #define _ALL_SOURCE /* For ANSI compilation. */ #include /* Contains errno. */ #include /* For open(). */ #include /* Defines stdin, stdout, stderr. */ #include #include #include #include #include /* Defines IOCPARM_MASK. */ #define _POSIX_SOURCE /* Assure getting STDIN_* in unistd.h. */ #include /* Defines STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO. */ #include #include #include #include #include /* For localtime(), tm struct. */ #include #ifndef LITLEN #define LITLEN(lit) (sizeof(lit) - 1) #endif /*********************************** Global variables ************************************/ char *Pgm_Name; /* The basename of this program, as obtained via argv[0]. */ /******************************************** dump() ******************************************** To display a sequence of character or binary bytes from memory to Stdout, as paired lines of up to 16 interpreted characters per line, with corresponding hex underneath each character, plus offset position and visual separation of character quads. Control characters are represented with a caret (e.g, "^A") and the Del character by "^?". Characters beyond the ASCII set of 128 are represented by "..". Sample: a b c d e f g h i j k l m n o p 0x012e3178 0x0000 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 q r s t u v w x y z A B C D E F 0x012e3188 0x0010 71 72 73 74 75 76 77 78 79 7a 41 42 43 44 45 46 G H I J K L M N O P Q R S T U V 0x012e3198 0x0020 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 W X Y Z ^@ ^A ^B ^C ^D ^? .. 0x012e31a8 0x0030 57 58 59 5a 00 01 02 03 04 7f bf INVOCATION: dump(UCHAR_PTR, UINT_LENGTH); RETURNS: nothing (void) HISTORY: 1995/09/06 Written by Richard Sims 1997/07/23 Greatly enhanced, with character interpretation and offset. RBS 1999/05/29 Show address as well as offset. RBS 1999/08/20 Arg pointer should be uchar, to handle chars > 127. RBS **************************************************************************************************/ #define FUNCTION_NAME "dump" #define RETURNED_VALUE_TYPE void #include RETURNED_VALUE_TYPE dump(unsigned char *arg_ucharptr, /* Address of the area to be displayed. */ unsigned int arg_length) /* The length of the area. */ { /*_________________________________________Function storage__________________________________________*/ /* Define variables as static to eliminate overhead of reconstructing across function calls. */ static unsigned char *cp; /* Pointer into caller-provided memory area. */ static unsigned char c; /* Character from caller-provided memory area. */ static int i, lo_nybble, hi_nybble; static char char_interp[80]; /* Where we construct character interpretations of * the given data. (Area is oversized.) */ static char *char_interp_ptr; /* To point to current position in char_interp. */ static char hex_interp[80]; /* Where we construct hexadecimal interpretations of * the given data. (Area is oversized.) */ static char *hex_interp_ptr; /* To point to current position in hex_interp. */ /*__________________________________Loop to interpret and dump to Stdout_____________________________________*/ cp = arg_ucharptr; /* Copy the caller pointer, to avoid disturbing it. */ char_interp_ptr = char_interp; hex_interp_ptr = hex_interp; for (i = 0; i < arg_length; ) { if (char_interp_ptr == char_interp) /* If starting next line, prep the leading portion... */ { strcpy(char_interp, " "); char_interp_ptr += 19; /* To lead the ASCII interp. line. */ sprintf(hex_interp, "0x%8.8x 0x%4.4x ", cp, i); hex_interp_ptr += 19; /* To display address and offset. */ } c = *cp++; /* Grab next character, advance pointer. */ /*printf("c = '%c'\n", c);*/ if (c < ' ') /* If a control char */ { *char_interp_ptr++ = '^'; *char_interp_ptr++ = (c & 0x1F) + 0x40; } else if (c == 0x7F) /* If the Delete char */ { *char_interp_ptr++ = '^'; *char_interp_ptr++ = '?'; } else if (c > 0x7F) /* If above ASCII's 128 chars */ { *char_interp_ptr++ = '.'; *char_interp_ptr++ = '.'; } else /* If an ordinary character. */ { *char_interp_ptr++ = ' '; *char_interp_ptr++ = c; } hi_nybble = (c & 0xf0) >> 4; /* Isolate hi nybble, shift to low position. */ if (hi_nybble < 10) *hex_interp_ptr++ = hi_nybble + '0'; /* If nybble value is 0-9, make it '0'-'9'... */ else *hex_interp_ptr++ = hi_nybble + 0x57; /* else make it 0x61-6f: 'a'-'f'. */ lo_nybble = c & 0x0f; /* Isolate low nybble. */ if (lo_nybble < 10) *hex_interp_ptr++ = lo_nybble + '0'; /* If nybble value is 0-9, make it '0'-'9'... */ else *hex_interp_ptr++ = lo_nybble + 0x57; /* else make it 0x61-6f: 'a'-'f'. */ *char_interp_ptr++ = ' '; *hex_interp_ptr++ = ' '; /* Space between each interpreted byte. */ i++; /* Increment counter here rather than in "for" statement so that * we don't induce printing when i == 0. */ /*_______________________________Watch for line filled or a quartet done_______________________________________*/ if (((i & 0xF) == 0) || i == arg_length) /* If 16 chars processed, time for new line... */ { /*__________________________The char and hex lines are filled - time to output_______________________________*/ *char_interp_ptr = '\0'; *hex_interp_ptr = '\0'; /* Mark the end of each string. */ puts(char_interp); puts(hex_interp); /* Convert line-end null to newline and emit to Stdout. */ char_interp_ptr = char_interp; /* Reset the pointers to start */ hex_interp_ptr = hex_interp; /* filling the output areas afresh. */ } else if ((i & 0x3) == 0) /* If 4 chars done, separate character quads with 2 spaces, for readability... */ { *char_interp_ptr++ = ' '; *hex_interp_ptr++ = ' '; /* 2 spaces between sets of four interpreted chars. */ } } /* bottom of for-loop to process the given memory area */ return; /* No return value - void function. */ } /* End of dump(). */ #undef FUNCTION_NAME #undef RETURNED_VALUE_TYPE /************************************ secs_to_date() ************************************** Convert a Unix seconds-since-1970 value to a date-time string. INVOCATION: see function prototype definition RETURNS: Date-time string of form "Thu Sep 29 09:30:10 1994". NOTES: - We employ an array of result strings because the caller may need to invoke this function a few times and need to depend upon having the results from the multiple calls all retain their original values - something which is not normally possible with traditional singular static storage areas. HISTORY: 1994/09/30 Written by Richard Sims 1999/01/13 Change arg from int to time_t; include time.h. RBS 2002/04/15 Employ rotating static storage areas for repeat calls. RBS ********************************************************************************************/ #define FUNCTION_NAME "secs_to_date" #define RETURNED_VALUE_TYPE char * #include /* For ctime() prototype. */ #include /* For timeval struct. */ #include #ifndef ELEMENTS #define ELEMENTS(x) (sizeof(x) / sizeof(x[0])) #endif char * secs_to_date(time_t arg_secs /* The seconds value. */ ) { /*______________________________Function storage____________________________________*/ static unsigned int ix = 0; /* To select the next element of * our rotational static areas. */ static char ctime_strings[8][100]; /*_________________________________Preliminaries_____________________________________*/ /* The following will rotationally use our static arrays... */ if (ix >= ELEMENTS(ctime_strings)) ix = 0; /*_________________________________Function logic_______________________________________*/ /* Convert time_t (long) time value to string, and eliminate newline. */ strcpy(ctime_strings[ix], ctime(&arg_secs)); /* Secure the value. (Subsequent calls to * system time functions will result in * replacement of the value held by them.) */ /* ctime() returns a string with a newline and a terminating null at the end. * We need to eliminate the newline. */ *(ctime_strings[ix] + strlen(ctime_strings[ix])-1) = '\0'; /* Overlay the newline. */ /* We now have a string containing a date time in the form: "Thu Sep 29 09:30:10 1994". */ return &ctime_strings[ix++][0]; /* Return results string and advance * indexer for next invocation. */ } #undef FUNCTION_NAME #undef RETURNED_VALUE_TYPE /*************************** secs_to_timestamp() ***************************** Given a time value in seconds since 1970, return the corresponding date-time, in format YYYY/MM/DD HH:MM:SS INVOCATION: secs_to_timestamp((time_t) SecondsSince1970) RETURNS: Date-time string of form "YYYY/MM/DD HH:MM:SS". Note that you can just get time time portion by referencing secs_to_timestamp(xxx)+11. NOTES: - We employ an array of result strings because the caller may need to invoke this function a few times and need to depend upon having the results from the multiple calls all retain their original values - something which is not normally possible with traditional singular static storage areas. HISTORY: 1996/10/02 Written by Richard Sims 1998/06/23 Employ rotating static storage areas for repeat calls. RBS *******************************************************************************/ #define FUNCTION_NAME "secs_to_timestamp" #define RETURNED_VALUE_TYPE char * #include /* For timeval struct. */ #include /* For tm struct. */ #ifndef ELEMENTS #define ELEMENTS(x) (sizeof(x) / sizeof(x[0])) #endif char * secs_to_timestamp ( time_t arg_time /* Given time, in seconds since 1970. */ ) { /*_____________________________Function storage_______________________________*/ /* Define variables as static to maximize performance. */ static int indexer = 0; /* To select the next element of * our rotational static areas. */ static struct tm *tm_struct_ptr; static char timestamp_strings[8][20]; /* Room for YYYY/MM/DD HH:MM:SS\0 */ static char *timestamp_string_ptr; /* Points into timestamp_strings. */ /*_____________________________Preliminaries_________________________________*/ /* The following will rotationally use our static arrays... */ if (indexer >= ELEMENTS(timestamp_strings)) indexer = 0; timestamp_string_ptr = timestamp_strings[indexer]; indexer++; /*___________________________Function logic_______________________________*/ tm_struct_ptr = localtime(&arg_time); sprintf(timestamp_string_ptr,"%04u/%02u/%02u %02u:%02u:%02u", tm_struct_ptr->tm_year+1900, tm_struct_ptr->tm_mon+1, tm_struct_ptr->tm_mday, tm_struct_ptr->tm_hour, tm_struct_ptr->tm_min, tm_struct_ptr->tm_sec ); return(timestamp_string_ptr); } /* bottom of secs_to_timestamp() */ #undef FUNCTION_NAME #undef RETURNED_VALUE_TYPE /******************************************** strctrl() **************************************************** Interpret a string's ASCII characters, converting control characters so that they are represented by a character doublet lead by a caret (^), as in "^M" for Carriage Return. The Delete character is represented by "^?". Char values above the standard ASCII 0-127 will be interpreted like: <80><81><82>. ENVIRONMENT: Any Unix INVOCATION: strctrl(String, Length) String Specifies the address of source characters to be translated. It is treated read-only. Note that this function accepts its input as unsigned chars: character values above the standard 127 for ASCII may be provided. Length Specifies the number of source characters to be translated, thus allowing nulls to be handled, which otherwise would mark the end of a string. RETURNS: Failure: 0, with errno set to an indicative value. Success: Pointer to interpreted character string, ordinary ASCII (char values 0-127). Errno = 0; HISTORY: 1996/12/29 Written by Richard Sims 1998/04/27 Add Length invocation parameter. RBS 2002/02/24 Improve prolog; return an errno; generally touch up. RBS ************************************************************************************************************/ #define FUNCTION_NAME "strctrl" #define RETURNED_VALUE_TYPE char * #define BAD_RC (RETURNED_VALUE_TYPE)0 RETURNED_VALUE_TYPE strctrl ( unsigned char *arg_string, /* Address of the characters to be translated. */ unsigned int arg_length /* Length of the characters to be translated. */ ) { static unsigned int ix; /* General indexer. */ static unsigned char *ip; /* Pointer into passed string, so as not to disturb caller * pointer. */ static char *op; /* Pointer into output string (interpreted_string). */ static int c; /* The current character being operated upon. */ static int x; /* The hex char translation of c. */ static int opos; /* Position number within output string. */ static char interpreted_string[4096]; /* Result area, reasonably sized to accommodate ordinary * strings. */ /*___________________________________________Preliminaries____________________________________________*/ /* Basic check to assure that input is not null and that interpreted result might fit into our output * work area. */ if (arg_string == 0) { errno = EFAULT; /* Indicative errno: Bad address - invalid pointer. */ return BAD_RC; /* Return failure indication. */ } if (arg_length == 0) { errno = EINVAL; /* Indicative errno: Invalid argument. */ return BAD_RC; /* Return failure indication. */ } if (arg_length > sizeof(interpreted_string)) { errno = E2BIG; /* Indicative errno: Too big. */ return BAD_RC; /* Return failure indication. */ } ip = arg_string; op = interpreted_string; opos = 1; /* We'll start the output position with a value of 1 such * that position comparisons below will always allow for * a final terminating null to fit into the output area. */ errno = 0; /*________________________________Loop to process all source chars____________________________________*/ for (ix = 0; ix < arg_length; ix++) { c = *ip++; /* Obtain next char from arg string and advance pointer within string. */ if (c < ' ') /* If a control character... */ { /*________________________________Translate to "^c" sequence___________________________________*/ /* Assure sufficient room in output area for 2 chars (plus ultimate string-end null)... */ if ((opos + 2) > sizeof(interpreted_string)) /* Watch out for output overflow. */ { fprintf(stderr, "%s: string too long to be interpreted.\n", FUNCTION_NAME); errno = E2BIG; /* Indicative errno: Too big. */ return BAD_RC; } *op++ = '^'; opos++; *op++ = (c & 0x1F) + 0x40; opos++; } else if (c == 0x7F) /* Delete character... */ { /*___________________________________Translate to "^?" sequence___________________________________*/ /* Assure sufficient room in output area for 2 chars (plus ultimate string-end null)... */ if ((opos + 2) > sizeof(interpreted_string)) /* Watch out for output overflow. */ { fprintf(stderr, "%s: string too long to be interpreted.\n", FUNCTION_NAME); errno = E2BIG; /* Indicative errno: Too big. */ return BAD_RC; } *op++ = '^'; opos++; *op++ = '?'; opos++; } else if (c > 0x7F) /* If byte is in the high range 0x80 - 0xff ... */ { /*___________________________________Translate to "" sequence___________________________________*/ /* Assure sufficient room in output area for 4 chars (plus ultimate string-end null)... */ if ((opos + 4) > sizeof(interpreted_string)) /* Watch out for output overflow. */ { fprintf(stderr, "%s: string too long to be interpreted.\n", FUNCTION_NAME); errno = E2BIG; /* Indicative errno: Too big. */ return BAD_RC; } /* Turn nybble values 0-9 into char '0'-'9'; turn nybble values 10-15 into char 'a'-'f' by adding 0x57. */ *op++ = '<'; *op++ = ((x = (c & 0xf0) >> 4) < 10) ? x + '0' : x + 0x57; opos++; *op++ = ((x = (c & 0x0f)) < 10) ? x + '0' : x + 0x57; opos++; *op++ = '>'; opos++; } else /* Ordinary character. */ { /*________________________________Simply transfer character as-is________________________________*/ /* Assure sufficient room in output area for 1 char (plus ultimate string-end null)... */ if ((opos + 1) > sizeof(interpreted_string)) /* Watch out for output overflow. */ { fprintf(stderr, "%s: string too long to be interpreted.\n", FUNCTION_NAME); errno = E2BIG; /* Indicative errno: Too big. */ return BAD_RC; } *op++ = c; opos++; } } /* bottom of for-loop to translate input character to output sequence. */ /*________________________________Complete the string_________________________________*/ /* Remember that opos comparisons above always accounted for the room needed to add * a final string-end null. */ *op = '\0'; /* Set output string-end null. */ return interpreted_string; } /* End of strctrl(). */ #undef BAD_RC #undef RETURNED_VALUE_TYPE #undef FUNCTION_NAME /****************************************** main() ********************************************/ main(int argc, /* Number of arguments passed. */ char * argv[], /* An array of pointers to the character * strings which comprise the arguments. */ char * envp[] /* An array of pointers to the character * strings which comprise the environmental * parameters. */ ) { /*________________________________________Main storage________________________________________*/ auto int rc; /* Return code from a library call. */ auto int n; static char * char_ptr; /* Pointer into char portion of output. */ struct errdata errdata_struct; struct tm *tm_struct_ptr; char errlog_timestamp[] = "MMDDHHMMYY"; int option; /*_______________________________Preliminaries____________________________________*/ Pgm_Name = ((char_ptr = strrchr(argv[0],'/')) != NULL) ? ++char_ptr : argv[0]; /* Get basename of invoked program name. */ /*____________________Process invocation parameters____________________*/ /* None. */ /*__________________________________Retrieve Error Log entries_______________________________________________*/ /* For Criteria (second operand), the most appropriate spec would be the next start date (probably one minute * later than the last invocation): * -s StartDate Specifies all records posted on and after the StartDate variable, where the StartDate variable * has the form mmddhhmmyy (month, day, hour, minute, and year). * Specify a null string to get all log entries. * Returns 1 if an error log entry available; else 0. * Entries will be returned in order from newest to oldest. */ option = INIT; /* To start. (INIT must be performed as part of the data * acquisition loop, as its first step.) */ while ((rc = error_log_get(option, /* Get next error-log entry. */ "-s 1114120700", /* Criteria. */ &errdata_struct) /* Output area. */) == 1 ) { /*____________________________error_log_get() succeeded in returning an entry________________________________*/ option = SUBSEQ; /* For all further get's. */ #if 0 if (rc == 0) { puts("error_log_get(SUBSEQ...) returned 0 - no problem"); } else if (rc == 1) { fprintf(stderr, "error_log_get(SUBSEQ...) returned 1 - Error-log entry available.\n"); } else if (rc == -1) { fprintf(stderr, "error_log_get(SUBSEQ...) returned -1 - Error obtaining data.\n"); } #endif puts("-------------------------------------------------------------"); printf("Sequence = %0#x (%d)\n", errdata_struct.sequence, errdata_struct.sequence); /* Note that the character Error Log timestamp is of the form: MMDDHHMMYY (no seconds). */ tm_struct_ptr = localtime((const long *)&errdata_struct.time_stamp); sprintf(errlog_timestamp, "%02u%02u%02u%02u%02u", tm_struct_ptr->tm_mon+1, tm_struct_ptr->tm_mday, tm_struct_ptr->tm_hour, tm_struct_ptr->tm_min, tm_struct_ptr->tm_year % 100 /* Want just right 2 digits of year. */); printf("Time stamp = %lu (%0#x: %s)\n", errdata_struct.time_stamp, errdata_struct.time_stamp, secs_to_date(errdata_struct.time_stamp)); #if 0 printf("Time stamp = %02u%02u%02u%02u%02u (%0#x: %s)\n", tm_struct_ptr->tm_mon+1, tm_struct_ptr->tm_mday, tm_struct_ptr->tm_hour, tm_struct_ptr->tm_min, tm_struct_ptr->tm_year % 100, /* Want just right 2 digits of year. */ errdata_struct.time_stamp, secs_to_date(errdata_struct.time_stamp)); #endif printf("Error ID = %x\n", errdata_struct.err_id); printf("Machine ID = %s", errdata_struct.machine_id); /* Carries its own newline. */ printf("Node ID = %s", errdata_struct.node_id); /* Carries its own newline. */ printf("Class = %s: %s\n", errdata_struct.class, *errdata_struct.class == 'H' ? "Hardware" : *errdata_struct.class == 'S' ? "Software" : "None" ); printf("Type = '%s'\n", errdata_struct.type); printf("Resource = '%s'\n", strctrl((unsigned char *)errdata_struct.resource, strlen(errdata_struct.resource)) ); /*____________________________Process the Vital Product Data (VPD)__________________________________________*/ /* I see that, for ssa0 for example, the raw data will look like: * VPD data = *PN^F 09L5632*FN^F 09L2090*SN^FS9145356*EC^G F24706*MF^EIBM053*R2500 0000*LL^C05 * *DD^C00*DSSA-ADAPTER *Z0^GSDRAM=0 64 *Z1^FCACHE=32*Z2^LUID=0000000629889C54^J * and the corresponding errpt entry looks like: * VPD: * Part Number................. 09L5632 * FRU Number.................. 09L2090 * Serial Number...............S9145356 * EC Level.................... F24706 * Manufacturer................IBM053 * ROS Level and ID............2500 0000 * Loadable Microcode Level....05 * Device Driver Level.........00 * Displayable Message.........SSA-ADAPTER * Device Specific.(Z0)........SDRAM=064 * Device Specific.(Z1)........CACHE=32 * Device Specific.(Z2)........UID=0000000629889C54 * Thus, '*' marks the start of a field, followed by a two-letter identifier (PN == Part Number). * Interpreted identifier codes: DD Device Driver Level * DS Displayable Message * EC EC Level * FN FRU Number * LL Loadable Microcode Level * MF Manufacturer * MN Model Number * PC Processor Componenet ID * PN Part Number * RL ROS Level and ID * SN Serial Number * Z0 Device Specific.(Z0) * Z1 Device Specific.(Z1) * Z2 Device Specific.(Z2) * The natural string length of the VPD is its full length. Oddly, however, what we get back may be * truncated, like "*MF^FEXABYTE *TM^J" though what 'errpt -a' reports is full VPD info. * ^J (Ctrl-J) marks the end of the VPD. * The significance of the binary byte following the "*__" is unknown: it is *not* a length. */ printf("VPD data = '%s'\n", strctrl((unsigned char *)errdata_struct.vpd_data, strlen(errdata_struct.vpd_data))); /*dump((unsigned char *)errdata_struct.vpd_data, strlen(errdata_struct.vpd_data));*/ if (strlen(errdata_struct.vpd_data)) { char *char_ptr; char *end_ptr; char *label = 0; char *value_ptr = 0; puts("Parsing the VPD..."); end_ptr = errdata_struct.vpd_data + strlen(errdata_struct.vpd_data); for (char_ptr = errdata_struct.vpd_data; char_ptr < end_ptr; char_ptr++) { if ((*char_ptr == '*') && (*(char_ptr + 1) >= 'A') && (*(char_ptr + 1) <= 'Z')) { /*_______________________At start of next field__________________________________*/ if (label && value_ptr) { printf(" %s = '%.*s'\n", label, char_ptr - value_ptr, value_ptr); label = value_ptr = 0; } char_ptr++; /* Step past '*' to the 2-letter VPD identifier. */ if (strncmp(char_ptr, "DD", 2) == 0) { label = "Device Driver Level"; } else if (strncmp(char_ptr, "DS", 2) == 0) { label = "Displayable Message"; } else if (strncmp(char_ptr, "EC", 2) == 0) { label = "EC Level"; } else if (strncmp(char_ptr, "FN", 2) == 0) { label = "FRU Number"; } else if (strncmp(char_ptr, "LL", 2) == 0) { label = "Loadable Microcode Level"; } else if (strncmp(char_ptr, "MF", 2) == 0) { label = "Manufacturer"; } else if (strncmp(char_ptr, "MN", 2) == 0) { label = "Model Number"; } else if (strncmp(char_ptr, "PC", 2) == 0) { label = "Processor Componenet ID"; } else if (strncmp(char_ptr, "PN", 2) == 0) { label = "Part Number"; } else if (strncmp(char_ptr, "RL", 2) == 0) { label = "ROS Level and ID"; } else if (strncmp(char_ptr, "SN", 2) == 0) { label = "Serial Number"; } else if (strncmp(char_ptr, "Z0", 2) == 0) { label = "Device Specific.(Z0)"; } else if (strncmp(char_ptr, "Z1", 2) == 0) { label = "Device Specific.(Z1)"; } else if (strncmp(char_ptr, "Z2", 2) == 0) { label = "Device Specific.(Z2)"; } else { label = "Unrecognized"; } char_ptr += 2; /* Step past the 2-letter VPD identifier, to control char. */ /* Watch out for premature end... */ if (*char_ptr == ('J' & 31)) /* If ^J (Ctrl-J)... */ { /* That's the end of the VPD. */ /*puts("Came upon Ctrl-J");*/ char_ptr--; /* Don't include the Ctrl-J. */ break; } char_ptr++; /* Step past the control char to the value. */ value_ptr = char_ptr; /*printf("char = '%c'\n", *char_ptr);*/ } else { /*_________________________Within a field_________________________________*/ if (*char_ptr == ('J' & 31)) /* If ^J (Ctrl-J)... */ { /* That's the end of the VPD. */ /*puts("Came upon Ctrl-J");*/ char_ptr--; /* Don't include the Ctrl-J. */ break; } /*printf("char = '%c'\n", *char_ptr);*/ /* Simply let the loop step us through it. */ } } /* bottom of for-loop */ /* Getting to the end of the string may leave residual data to report. */ if (label && value_ptr) { printf(" %s = '%.*s'\n", label, char_ptr - value_ptr, value_ptr); label = value_ptr = 0; } } /* bottom of reporting the VPD */ printf("Connwhere field of CuDv = %s", errdata_struct.conn_where); /* Carries its own newline. */ printf("Location field of CuDv = %s", errdata_struct.location); /* Carries its own newline. */ printf("Detail data length = %d\n", errdata_struct.detail_data_len); if (errdata_struct.detail_data_len) { puts("Detail data = "); dump((unsigned char *)errdata_struct.detail_data, errdata_struct.detail_data_len); } /*__________________________________Selective interpretation of the detail data___________________________________*/ if (errdata_struct.err_id == 0xfe2dee00) /* AIXIF_ARP_DUP_ADDR: DUPLICATE IP ADDRESS DETECTED IN THE NET */ { /* Note that this is a VERY BAD situation, as someone is likely subverting your network, taking over a host's * network address! */ printf(" IP addr = %s\n", inet_ntoa(*(struct in_addr *)errdata_struct.detail_data)); printf(" MAC addr = %02x:%02x:%02x:%02x:%02x:%02x\n", *(errdata_struct.detail_data + 4), *(errdata_struct.detail_data + 5), *(errdata_struct.detail_data + 6), *(errdata_struct.detail_data + 7), *(errdata_struct.detail_data + 8), *(errdata_struct.detail_data + 9)); } if (errdata_struct.err_id == ERRID_JFS_FS_FULL) /* AIX JFS file system full. */ { printf("Major/Minor device numbers: %u,%u (%s)\n", *((ushort *)errdata_struct.detail_data + 0), *((ushort *)errdata_struct.detail_data + 1), (char *)errdata_struct.detail_data + 4); } if ((strcmp(errdata_struct.type, "UNKN") == 0) && (strncmp(errdata_struct.resource, "lmcp", LITLEN("lmcp")) == 0)) { /* The detail data will be lead by a null-terminated string (followed by binary stuff) * explaining, like: "ERROR on OUR3494, volume 001017, ERA 6D Library Drive Not Unloaded" * or: "ERROR Sending to OUR3494: Connection timed out" . */ printf("UNKN error, lmcp, error ID %x, at %s (do 'errpt -al %u' for details)\n", errdata_struct.err_id, secs_to_timestamp(errdata_struct.time_stamp)+11 /* Just HH:MM:SS */, errdata_struct.sequence); } if (errdata_struct.err_id == ERRID_LVM_HWREL) { /* Detail data = ^@ ^M ^@ ^S ^@ ~ 9 .. ^@ ^@ ^@ ^@ ^@ ^@ ^@ ^@ 0xf01d4690 0x0000 00 0d 00 13 00 7e 39 ab 00 00 00 00 00 00 00 00 */ printf("UNKN hardware error, disk block relocation, error ID %x, at %s (do 'errpt -al %u' for details)\n", errdata_struct.err_id, secs_to_timestamp(errdata_struct.time_stamp)+11 /* Just HH:MM:SS */, errdata_struct.sequence); printf("Major/Minor device numbers: %u,%u Disk block %lu\n", *((ushort *)errdata_struct.detail_data + 0), *((ushort *)errdata_struct.detail_data + 1), *((unsigned int *)errdata_struct.detail_data + 1)); } if (errdata_struct.err_id == ERRID_JFS_FS_FRAGMENTED) { /* Detail data is the major/minor device numbers and file system names, like: * ^@ ( ^@ ^D / d e v / l v - h s m - * 0xf01d4690 0x0000 00 28 00 04 2f 64 65 76 2f 6c 76 2d 68 73 6d 2d * a r c h i v e u , / a r c h i * 0xf01d46a0 0x0010 61 72 63 68 69 76 65 75 2c 20 2f 61 72 63 68 69 * v e / a r c h i v e d - u s e r * 0xf01d46b0 0x0020 76 65 2f 61 72 63 68 69 76 65 64 2d 75 73 65 72 * s ^@ ^@ ^@ ^@ ^@ ^@ ^@ ^@ ^@ ^@ ^@ ^@ ^@ ^@ ^@ * 0xf01d46c0 0x0030 73 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ printf("JFS fragmented error, error ID %x, at %s (do 'errpt -al %u' for details)\n", errdata_struct.err_id, secs_to_timestamp(errdata_struct.time_stamp)+11 /* Just HH:MM:SS */, errdata_struct.sequence); printf("Major/Minor device numbers: %u,%u = %s\n", *((ushort *)errdata_struct.detail_data + 0), *((ushort *)errdata_struct.detail_data + 1), (unsigned char *)errdata_struct.detail_data + 4); } if (errdata_struct.err_id == ERRID_SSA_CACHE_BATTERY) { /* Detail data = ^D % , ^@ ^@ ^@ ^@ ^@ ^@ ^@ ^@ ^@ ^@ ^@ ^@ ^@ * 0xf01cde80 0x0000 04 25 2c 00 00 00 00 00 00 00 00 00 00 00 00 00 * and more zeroes, up to length of 128. */ printf("SSA_CACHE_BATTERY error on RAID-capable SSA adapter '%.*s' - the on-board lithium battery is depleted.\n", (*(errdata_struct.resource + (n = strlen(errdata_struct.resource) - 1)) == '\n') ? n : n + 1, errdata_struct.resource); printf("Fast writes are disabled - disk I/O must complete before acknowledgement of I/O completion.\n"); } #ifdef ERRID_MACHINE_CHECK_604 if (errdata_struct.err_id == ERRID_MACHINE_CHECK_604) /* 0xe9b202d0 Machine check interrupt (604) * Note: In AIX 4.1 was 0x5471966a */ #elif defined ERRID_MACHINE_CHK_604_620 if (errdata_struct.err_id == ERRID_MACHINE_CHK_604_620) /* 0x87f378ec Machine check interrupt (604 and 620) */ #endif { /* --------------------------------------------------------------------------- LABEL: MACHINE_CHECK_604 IDENTIFIER: E9B202D0 Date/Time: Thu Aug 8 06:05:25 Sequence Number: 27795 Machine Id: 00022486A400 Node Id: acs-mail Class: H Type: PEND Resource Name: sysplanar0 Resource Class: planar Resource Type: sysplanar_p Location: 00-00 VPD: EC Level....................E95393A FRU Number..................NMB Device Specific.(MN)........IBM053 Processor Component ID......6501001100020a060001021700010117000104 17000103170001047868c004c4b400 Part Number.................19H0260 Serial Number...............L119062061 Processor Identification....00022486 Machine Type and Model......7015R50 22486 Device Specific.(Y2)........ 22486 Device Specific.(MN)........IBM Device Specific.(Y3)........7fffff0095042000 ROS Level and ID............IPL, ROS Level and ID............OCS(31383032) ROS Level and ID............SEEDS(41393431) Description MACHINE CHECK Probable Causes MULTIPLE MEMORY PARITY ERROR DATA OR ADDRESS BUS PARITY ERROR INTERNAL CACHE PARITY ERROR Failure Causes MULTIPLE MEMORY PARITY ERROR DATA OR ADDRESS BUS PARITY ERROR INTERNAL CACHE PARITY ERROR Recommended Actions REPLACE DEFECTIVE SIMMS IF PARITY ERROR CALL SERVICE Detail Data TIME STAMP 3D52 3F9A MACHINE STATUS SAVE/RESTORE REGISTER 0 D005 B0C4 MACHINE STATUS SAVE/RESTORE REGISTER 1 0002 D030 CENTRAL PROCESSING UNIT 0000 0000 --------------------------------------------------------------------------- */ /* Detail data = = R ? .. .. ^E .. .. ^@ ^B .. 0 ^@ ^@ ^@ ^@ 0xf01ac8d8 0x0000 3d 52 3f 9a d0 05 b0 c4 00 02 d0 30 00 00 00 00 */ printf("PEND hardware error: Machine Check 604: error ID %x, at %s (do 'errpt -al %u' for details)\n", errdata_struct.err_id, secs_to_timestamp(errdata_struct.time_stamp)+11 /* Just HH:MM:SS */, errdata_struct.sequence); printf("Time stamp: %08x MachStatReg0: %08x MachStatReg1: %08x CPU: %x\n", *((unsigned int *)errdata_struct.detail_data + 0), *((unsigned int *)errdata_struct.detail_data + 1), *((unsigned int *)errdata_struct.detail_data + 2), *((unsigned int *)errdata_struct.detail_data + 3)); } } printf("error_log_get(SUBSEQ...) returned %d\n", rc); /*_____________________________Terminate Error Log retrieval____________________________________________*/ rc = error_log_get(TERMI, "", /* Criteria (none). */ &errdata_struct); /* Output area. */ /* Should only return 0. */ if (rc == 0) { puts("error_log_get(TERMI...) returned 0 - no problem"); } else { fprintf(stderr,"error_log_get(TERMI...) returned %d - Error-log entry available.\n", rc); } exit(EXIT_SUCCESS); } /* end of main() */