1 /** 2 * A user-space application linking to the CPER-JSON conversion library which allows for easy 3 * conversion between CPER and CPER-JSON formats. 4 * 5 * Author: Lawrence.Tang@arm.com 6 **/ 7 8 #include <stdio.h> 9 #include <string.h> 10 #include <libgen.h> 11 #include <limits.h> 12 #include <json.h> 13 #include <libcper/log.h> 14 #include <libcper/cper-parse.h> 15 #include <libcper/json-schema.h> 16 #include <libcper/Cper.h> 17 #include <libcper/base64.h> 18 19 void cper_to_json(char *in_file, char *out_file, int is_single_section); 20 void json_to_cper(const char *in_file, const char *out_file); 21 void print_help(void); 22 23 int main(int argc, char *argv[]) 24 { 25 cper_set_log_stdio(); 26 //Print help if requested. 27 if (argc == 2 && strcmp(argv[1], "--help") == 0) { 28 print_help(); 29 return 0; 30 } 31 32 //Ensure at least two arguments are present. 33 if (argc < 3) { 34 printf("Invalid number of arguments. See 'cper-convert --help' for command information.\n"); 35 return -1; 36 } 37 38 //Parse the command line arguments. 39 char *input_file = argv[2]; 40 char *output_file = NULL; 41 char *specification_file = NULL; 42 int no_validate = 0; 43 int debug = 0; 44 for (int i = 3; i < argc; i++) { 45 if (strcmp(argv[i], "--out") == 0 && i < argc - 1) { 46 //Output file. 47 output_file = argv[i + 1]; 48 i++; 49 } else if (strcmp(argv[i], "--specification") == 0 && 50 i < argc - 1) { 51 //Specification file. 52 specification_file = argv[i + 1]; 53 i++; 54 } else if (strcmp(argv[i], "--no-validate") == 0) { 55 //No validation to be used. 56 //Invalidates specification file. 57 specification_file = NULL; 58 no_validate = 1; 59 } else if (strcmp(argv[i], "--debug") == 0) { 60 //Debug output on. 61 debug = 1; 62 } else { 63 printf("Unrecognised argument '%s'. See 'cper-convert --help' for command information.\n", 64 argv[i]); 65 } 66 } 67 68 // Debug is not used at the moment. Leave for compatibility. 69 (void)debug; 70 (void)no_validate; 71 (void)specification_file; 72 //Run the requested command. 73 if (strcmp(argv[1], "to-json") == 0) { 74 cper_to_json(input_file, output_file, 0); 75 } else if (strcmp(argv[1], "to-json-section") == 0) { 76 cper_to_json(input_file, output_file, 1); 77 } else if (strcmp(argv[1], "to-cper") == 0) { 78 json_to_cper(input_file, output_file); 79 } else { 80 printf("Unrecognised argument '%s'. See 'cper-convert --help' for command information.\n", 81 argv[1]); 82 return -1; 83 } 84 85 return 0; 86 } 87 88 //Command for converting a provided CPER log file or CPER single section file into JSON. 89 void cper_to_json(char *in_file, char *out_file, int is_single_section) 90 { 91 //Get a handle for the log file. 92 FILE *cper_file = fopen(in_file, "r"); 93 if (cper_file == NULL) { 94 printf("Could not open provided CPER file '%s', file handle returned null.\n", 95 in_file); 96 return; 97 } 98 99 fseek(cper_file, 0, SEEK_END); 100 long fsize = ftell(cper_file); 101 fseek(cper_file, 0, SEEK_SET); 102 103 char *fbuff = malloc(fsize); 104 size_t readsize = fread(fbuff, 1, (long)fsize, cper_file); 105 if (readsize != (size_t)fsize) { 106 printf("Could not read CPER file '%s', read returned %zu bytes.\n", 107 in_file, readsize); 108 return; 109 } 110 111 if (!header_valid(fbuff, readsize)) { 112 // Check if it's base64 encoded 113 int32_t decoded_len = 0; 114 UINT8 *decoded = base64_decode(fbuff, readsize, &decoded_len); 115 if (decoded == NULL) { 116 printf("base64 decode failed for CPER file '%s'.\n", 117 in_file); 118 free(fbuff); 119 free(decoded); 120 return; 121 } 122 if (!header_valid((const char *)decoded, decoded_len)) { 123 printf("Invalid CPER file '%s'.\n", in_file); 124 free(fbuff); 125 free(decoded); 126 return; 127 } 128 // Swap the buffer to the base64 decoded buffer. 129 free(fbuff); 130 fbuff = (char *)decoded; 131 132 fsize = decoded_len; 133 decoded = NULL; 134 } 135 136 //Convert. 137 json_object *ir; 138 if (is_single_section) { 139 ir = cper_buf_single_section_to_ir((UINT8 *)fbuff, readsize); 140 } else { 141 ir = cper_buf_to_ir((UINT8 *)fbuff, fsize); 142 } 143 fclose(cper_file); 144 145 //Output to string. 146 const char *json_output = 147 json_object_to_json_string_ext(ir, JSON_C_TO_STRING_PRETTY); 148 149 //Check whether there is a "--out" argument, if there is, then output to file instead. 150 //Otherwise, just send to console. 151 if (out_file == NULL) { 152 printf("%s\n", json_output); 153 return; 154 } 155 156 //Try to open a file handle to the desired output file. 157 FILE *json_file = fopen(out_file, "w"); 158 if (json_file == NULL) { 159 printf("Could not get a handle for output file '%s', file handle returned null.\n", 160 out_file); 161 return; 162 } 163 164 //Write out to file. 165 fwrite(json_output, strlen(json_output), 1, json_file); 166 fclose(json_file); 167 } 168 169 //Command for converting a provided CPER-JSON JSON file to CPER binary. 170 void json_to_cper(const char *in_file, const char *out_file) 171 { 172 //Verify output file exists. 173 if (out_file == NULL) { 174 printf("No output file provided for 'to-cper'. See 'cper-convert --help' for command information.\n"); 175 return; 176 } 177 178 //Read JSON IR from file. 179 json_object *ir = json_object_from_file(in_file); 180 if (ir == NULL) { 181 printf("Could not read JSON from file '%s', import returned null.\n", 182 in_file); 183 return; 184 } 185 186 //Open a read for the output file. 187 FILE *cper_file = fopen(out_file, "w"); 188 if (cper_file == NULL) { 189 printf("Could not open output file '%s', file handle returned null.\n", 190 out_file); 191 json_object_put(ir); 192 return; 193 } 194 195 //Detect the type of CPER (full log, single section) from the IR given. 196 //Run the converter accordingly. 197 if (json_object_object_get(ir, "header") != NULL) { 198 ir_to_cper(ir, cper_file); 199 } else { 200 ir_single_section_to_cper(ir, cper_file); 201 } 202 fclose(cper_file); 203 json_object_put(ir); 204 } 205 206 //Command for printing help information. 207 void print_help(void) 208 { 209 printf(":: to-json cper.file [--out file.name]\n"); 210 printf("\tConverts the provided CPER log file into JSON, by default writing to stdout. If '--out' is specified,\n"); 211 printf("\tThe outputted JSON will be written to the provided file name instead.\n"); 212 printf("\n:: to-json-section cper.section.file [--out file.name]\n"); 213 printf("\tConverts the provided single CPER section descriptor & section file into JSON, by default writing to stdout.\n"); 214 printf("\tOtherwise behaves the same as 'to-json'.\n"); 215 printf("\n:: to-cper cper.json --out file.name [--no-validate] [--debug] [--specification some/spec/path.json]\n"); 216 printf("\tConverts the provided CPER-JSON JSON file into CPER binary. An output file must be specified with '--out'.\n"); 217 printf("\tWill automatically detect whether the JSON passed is a single section, or a whole file,\n"); 218 printf("\tand output binary accordingly.\n\n"); 219 printf("\tBy default, the provided JSON will try to be validated against a specification. If no specification file path\n"); 220 printf("\tis provided with '--specification', then it will default to 'argv[0] + /specification/cper-json.json'.\n"); 221 printf("\tIf the '--no-validate' argument is set, then the provided JSON will not be validated. Be warned, this may cause\n"); 222 printf("\tpremature exit/unexpected behaviour in CPER output.\n\n"); 223 printf("\tIf '--debug' is set, then debug output for JSON specification parsing will be printed to stdout.\n"); 224 printf("\n:: --help\n"); 225 printf("\tDisplays help information to the console.\n"); 226 } 227