xref: /openbmc/libcper/cli-app/cper-convert.c (revision cd9b1c5826323f8035a10513c832cc1a0d351247)
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