xref: /openbmc/libcper/cli-app/cper-convert.c (revision 044afd01)
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 "../cper-parse.h"
14 #include "../json-schema.h"
15 
16 void cper_to_json(char *in_file, char *out_file, int is_single_section);
17 void json_to_cper(char *in_file, char *out_file, char *specification_file,
18 		  char *program_dir, int no_validate, int debug);
19 void print_help(void);
20 
21 int main(int argc, char *argv[])
22 {
23 	//Print help if requested.
24 	if (argc == 2 && strcmp(argv[1], "--help") == 0) {
25 		print_help();
26 		return 0;
27 	}
28 
29 	//Ensure at least two arguments are present.
30 	if (argc < 3) {
31 		printf("Invalid number of arguments. See 'cper-convert --help' for command information.\n");
32 		return -1;
33 	}
34 
35 	//Parse the command line arguments.
36 	char *input_file = argv[2];
37 	char *output_file = NULL;
38 	char *specification_file = NULL;
39 	int no_validate = 0;
40 	int debug = 0;
41 	for (int i = 3; i < argc; i++) {
42 		if (strcmp(argv[i], "--out") == 0 && i < argc - 1) {
43 			//Output file.
44 			output_file = argv[i + 1];
45 			i++;
46 		} else if (strcmp(argv[i], "--specification") == 0 &&
47 			   i < argc - 1) {
48 			//Specification file.
49 			specification_file = argv[i + 1];
50 			i++;
51 		} else if (strcmp(argv[i], "--no-validate") == 0) {
52 			//No validation to be used.
53 			//Invalidates specification file.
54 			specification_file = NULL;
55 			no_validate = 1;
56 		} else if (strcmp(argv[i], "--debug") == 0) {
57 			//Debug output on.
58 			debug = 1;
59 		} else {
60 			printf("Unrecognised argument '%s'. See 'cper-convert --help' for command information.\n",
61 			       argv[i]);
62 		}
63 	}
64 
65 	//Run the requested command.
66 	if (strcmp(argv[1], "to-json") == 0) {
67 		cper_to_json(input_file, output_file, 0);
68 	} else if (strcmp(argv[1], "to-json-section") == 0) {
69 		cper_to_json(input_file, output_file, 1);
70 	} else if (strcmp(argv[1], "to-cper") == 0) {
71 		json_to_cper(input_file, output_file, specification_file,
72 			     argv[0], no_validate, debug);
73 	} else {
74 		printf("Unrecognised argument '%s'. See 'cper-convert --help' for command information.\n",
75 		       argv[1]);
76 		return -1;
77 	}
78 
79 	return 0;
80 }
81 
82 //Command for converting a provided CPER log file or CPER single section file into JSON.
83 void cper_to_json(char *in_file, char *out_file, int is_single_section)
84 {
85 	//Get a handle for the log file.
86 	FILE *cper_file = fopen(in_file, "r");
87 	if (cper_file == NULL) {
88 		printf("Could not open provided CPER file '%s', file handle returned null.\n",
89 		       in_file);
90 		return;
91 	}
92 
93 	//Convert.
94 	json_object *ir;
95 	if (is_single_section)
96 		ir = cper_single_section_to_ir(cper_file);
97 	else
98 		ir = cper_to_ir(cper_file);
99 	fclose(cper_file);
100 
101 	//Output to string.
102 	const char *json_output =
103 		json_object_to_json_string_ext(ir, JSON_C_TO_STRING_PRETTY);
104 
105 	//Check whether there is a "--out" argument, if there is, then output to file instead.
106 	//Otherwise, just send to console.
107 	if (out_file == NULL) {
108 		printf("%s\n", json_output);
109 		return;
110 	}
111 
112 	//Try to open a file handle to the desired output file.
113 	FILE *json_file = fopen(out_file, "w");
114 	if (json_file == NULL) {
115 		printf("Could not get a handle for output file '%s', file handle returned null.\n",
116 		       out_file);
117 		return;
118 	}
119 
120 	//Write out to file.
121 	fwrite(json_output, strlen(json_output), 1, json_file);
122 	fclose(json_file);
123 }
124 
125 //Command for converting a provided CPER-JSON JSON file to CPER binary.
126 void json_to_cper(char *in_file, char *out_file, char *specification_file,
127 		  char *program_dir, int no_validate, int debug)
128 {
129 	//Verify output file exists.
130 	if (out_file == NULL) {
131 		printf("No output file provided for 'to-cper'. See 'cper-convert --help' for command information.\n");
132 		return;
133 	}
134 
135 	//Read JSON IR from file.
136 	json_object *ir = json_object_from_file(in_file);
137 	if (ir == NULL) {
138 		printf("Could not read JSON from file '%s', import returned null.\n",
139 		       in_file);
140 		return;
141 	}
142 
143 	//Validate the JSON against specification, unless otherwise specified.
144 	if (!no_validate) {
145 		int using_default_spec_path = 0;
146 
147 		//Is there a specification file path?
148 		if (specification_file == NULL) {
149 			using_default_spec_path = 1;
150 
151 			//Make the specification path the assumed default (application directory + specification/cper-json.json).
152 			specification_file = malloc(PATH_MAX);
153 			char *dir = dirname(program_dir);
154 			strcpy(specification_file, dir);
155 			strcat(specification_file,
156 			       "/specification/cper-json.json");
157 		}
158 
159 		//Enable debug mode if indicated.
160 		if (debug)
161 			validate_schema_debug_enable();
162 
163 		//Attempt to verify with the the specification.
164 		char *error_message = malloc(JSON_ERROR_MSG_MAX_LEN);
165 		int success = validate_schema_from_file(specification_file, ir,
166 							error_message);
167 
168 		//Free specification path (if necessary).
169 		if (using_default_spec_path)
170 			free(specification_file);
171 
172 		//If failed, early exit before conversion.
173 		if (!success) {
174 			printf("JSON format validation failed: %s\n",
175 			       error_message);
176 			free(error_message);
177 			return;
178 		}
179 		free(error_message);
180 	}
181 
182 	//Open a read for the output file.
183 	FILE *cper_file = fopen(out_file, "w");
184 	if (cper_file == NULL) {
185 		printf("Could not open output file '%s', file handle returned null.\n",
186 		       out_file);
187 		return;
188 	}
189 
190 	//Detect the type of CPER (full log, single section) from the IR given.
191 	//Run the converter accordingly.
192 	if (json_object_object_get(ir, "header") != NULL)
193 		ir_to_cper(ir, cper_file);
194 	else
195 		ir_single_section_to_cper(ir, cper_file);
196 	fclose(cper_file);
197 }
198 
199 //Command for printing help information.
200 void print_help(void)
201 {
202 	printf(":: to-json cper.file [--out file.name]\n");
203 	printf("\tConverts the provided CPER log file into JSON, by default writing to stdout. If '--out' is specified,\n");
204 	printf("\tThe outputted JSON will be written to the provided file name instead.\n");
205 	printf("\n:: to-json-section cper.section.file [--out file.name]\n");
206 	printf("\tConverts the provided single CPER section descriptor & section file into JSON, by default writing to stdout.\n");
207 	printf("\tOtherwise behaves the same as 'to-json'.\n");
208 	printf("\n:: to-cper cper.json --out file.name [--no-validate] [--debug] [--specification some/spec/path.json]\n");
209 	printf("\tConverts the provided CPER-JSON JSON file into CPER binary. An output file must be specified with '--out'.\n");
210 	printf("\tWill automatically detect whether the JSON passed is a single section, or a whole file,\n");
211 	printf("\tand output binary accordingly.\n\n");
212 	printf("\tBy default, the provided JSON will try to be validated against a specification. If no specification file path\n");
213 	printf("\tis provided with '--specification', then it will default to 'argv[0] + /specification/cper-json.json'.\n");
214 	printf("\tIf the '--no-validate' argument is set, then the provided JSON will not be validated. Be warned, this may cause\n");
215 	printf("\tpremature exit/unexpected behaviour in CPER output.\n\n");
216 	printf("\tIf '--debug' is set, then debug output for JSON specification parsing will be printed to stdout.\n");
217 	printf("\n:: --help\n");
218 	printf("\tDisplays help information to the console.\n");
219 }