Whamcloud - gitweb
74936cecc7ae511240c489b4c1199f6d945a7346
[fs/lustre-release.git] / lustre / utils / liblustreapi_json.c
1 /*
2  * LGPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * (C) Copyright 2014 Intel Corporation.
7  *
8  * All rights reserved. This program and the accompanying materials
9  * are made available under the terms of the GNU Lesser General Public License
10  * (LGPL) version 2.1 or (at your discretion) any later version.
11  * (LGPL) version 2.1 accompanies this distribution, and is available at
12  * http://www.gnu.org/licenses/lgpl-2.1.html
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * LGPL HEADER END
20  */
21 /*
22  * lustre/utils/liblustreapi_json.c
23  *
24  * lustreapi library for json calls
25  *
26  * Author: Michael MacDonald <michael.macdonald@intel.com>
27  * Author: Bruno Faccini <bruno.faccini@intel.com>
28  */
29
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <stddef.h>
34 #include <malloc.h>
35 #include <errno.h>
36 #include <stdarg.h>
37 #include <sys/types.h>
38 #ifdef HAVE_LINUX_UNISTD_H
39 #include <linux/unistd.h>
40 #else
41 #include <unistd.h>
42 #endif
43
44 #include <libcfs/util/string.h>
45 #include <lustre/lustreapi.h>
46
47 /** Quick-n'-dirty JSON string escape routine.
48  * \param[out]  out_string      JSON-escaped string, allocated here
49  * \param[in]   in_string       Unescaped string
50  *
51  * \retval      0 on success.
52  * \retval      -errno on error.
53  *
54  * http://json.org/
55  * http://www.ietf.org/rfc/rfc4627.txt (section 2.5)
56  */
57 int llapi_json_escape_string(char **out_string, char *in_string)
58 {
59         int     i;
60         char    escape_chars[] = {'\b', '\f', '\n', '\r', '\t', '"', '\\',
61                                   '\0'};
62         char    *escaped_chars[] = {"\\\\b", "\\\\f", "\\\\n", "\\\\r",
63                                     "\\\\t", "\\\\\"", "\\\\\\\\"};
64         char    *src = in_string;
65         char    *idx, *dst, *tmp;
66         char    *escaped_string;
67         size_t  tmp_len, escaped_length = strlen(in_string);
68
69         /* add up the extra space needed for the escapes */
70         while (*src) {
71                 idx = strchr(escape_chars, *src);
72                 if (idx != NULL) {
73                         tmp = escaped_chars[idx - escape_chars];
74                         escaped_length += strlen(tmp);
75                 }
76                 src++;
77         }
78
79         escaped_string = calloc(1, escaped_length + 1);
80         if (escaped_string == NULL)
81                 return -ENOMEM;
82
83         src = in_string;
84         dst = escaped_string;
85         for (i = 0; *src && i <= escaped_length; i++) {
86                 idx = strchr(escape_chars, *src);
87                 if (idx != NULL) {
88                         tmp = escaped_chars[idx - escape_chars];
89                         tmp_len = strlen(tmp);
90                         memcpy(dst, tmp, tmp_len);
91                         dst += tmp_len;
92                         src++;
93                 } else {
94                         *dst = *src;
95                         src++;
96                         dst++;
97                 }
98         }
99
100         *dst = '\0';
101
102         *out_string = escaped_string;
103
104         return 0;
105 }
106
107 /** Write a list of JSON items to a filehandle.
108  * \param       json_items      list of JSON items to be written
109  * \param       fp              open filehandle to use for write
110  *
111  * \retval      0 on success.
112  * \retval      -errno on error.
113  */
114 int llapi_json_write_list(struct llapi_json_item_list **json_items, FILE *fp)
115 {
116         int                             i;
117         char                            *escaped_string = NULL;
118         struct llapi_json_item_list     *list;
119         struct llapi_json_item          *item;
120
121         if (json_items == NULL || *json_items == NULL)
122                 return -EINVAL;
123
124         list = *json_items;
125         item = list->ljil_items;
126
127         fprintf(fp, "{");
128         for (i = 0; i < list->ljil_item_count; i++) {
129                 if (item == NULL) {
130                         llapi_err_noerrno(LLAPI_MSG_ERROR,
131                                           "%d json items but %d is NULL!",
132                                           list->ljil_item_count, i);
133                         /* Don't bomb out here so that we still emit
134                          * valid JSON. */
135                         break;
136                 }
137
138                 fprintf(fp, "\"%s\": ", item->lji_key);
139                 switch (item->lji_type) {
140                 case LLAPI_JSON_INTEGER:
141                         fprintf(fp, "%d", item->lji_integer);
142                         break;
143                 case LLAPI_JSON_BIGNUM:
144                         fprintf(fp, LPU64, item->lji_u64);
145                         break;
146                 case LLAPI_JSON_REAL:
147                         fprintf(fp, "%f", item->lji_real);
148                         break;
149                 case LLAPI_JSON_STRING:
150                         if (llapi_json_escape_string(&escaped_string,
151                                                      item->lji_string) < 0) {
152                                 if (escaped_string != NULL)
153                                         free(escaped_string);
154                                 return -errno;
155                         }
156
157                         fprintf(fp, "\"%s\"", escaped_string);
158
159                         if (escaped_string != NULL)
160                                 free(escaped_string);
161                         break;
162                 default:
163                         llapi_err_noerrno(LLAPI_MSG_ERROR,
164                                     "Invalid item type: %d", item->lji_type);
165                         /* Ensure valid JSON */
166                         fprintf(fp, "\"\"");
167                         break;
168                 }
169
170                 if (i < list->ljil_item_count - 1)
171                         fprintf(fp, ", ");
172
173                 item = item->lji_next;
174         }
175         fprintf(fp, "}\n");
176
177         return 0;
178 }
179
180 /** Create a list to hold JSON items.
181  * \param[out]  json_items      Item list handle, allocated here
182  *
183  * \retval      0 on success.
184  * \retval      -errno on error.
185  */
186 int llapi_json_init_list(struct llapi_json_item_list **json_items)
187 {
188         struct llapi_json_item_list     *new_list;
189
190         new_list = calloc(1, sizeof(*new_list));
191         if (new_list == NULL)
192                 return -ENOMEM;
193
194         new_list->ljil_item_count = 0;
195
196         *json_items = new_list;
197
198         return 0;
199 }
200
201 /** Deallocate a list of JSON items.
202  * \param       json_items      Item list handle, deallocated here
203  *
204  * \retval      0 on success.
205  * \retval      -errno on error.
206  */
207 int llapi_json_destroy_list(struct llapi_json_item_list **json_items)
208 {
209         int                             i;
210         struct llapi_json_item_list     *list;
211         struct llapi_json_item          *cur_item;
212         struct llapi_json_item          *last_item;
213
214         if (json_items == NULL || *json_items == NULL)
215                 return -EINVAL;
216
217         list = *json_items;
218         cur_item = list->ljil_items;
219
220         for (i = 0; i < list->ljil_item_count; i++) {
221                 if (cur_item == NULL) {
222                         llapi_err_noerrno(LLAPI_MSG_ERROR,
223                                           "%d json items but %d is NULL!",
224                                           list->ljil_item_count, i);
225                         return -EINVAL;
226                 }
227
228                 if (cur_item->lji_key != NULL)
229                         free(cur_item->lji_key);
230
231                 if (cur_item->lji_type == LLAPI_JSON_STRING
232                     && cur_item->lji_string != NULL)
233                         free(cur_item->lji_string);
234
235                 last_item = cur_item;
236                 cur_item = last_item->lji_next;
237                 free(last_item);
238         }
239
240         free(list);
241         *json_items = NULL;
242
243         return 0;
244 }
245
246 /** Add an item to a list of JSON items.
247  * \param       json_items      Item list handle
248  * \param       key             Item key name
249  * \param       type            Item key type
250  * \param       val             Item key value
251  *
252  * \retval      0 on success.
253  * \retval      -errno on error.
254  */
255 int llapi_json_add_item(struct llapi_json_item_list **json_items,
256                         char *key, __u32 type, void *val)
257 {
258         struct llapi_json_item_list     *list;
259         struct llapi_json_item          *new_item;
260         size_t len;
261
262         if (json_items == NULL || *json_items == NULL)
263                 return -EINVAL;
264
265         if (val == NULL)
266                 return -EINVAL;
267
268         list = *json_items;
269
270         new_item = calloc(1, sizeof(*new_item));
271         if (new_item == NULL)
272                 return -ENOMEM;
273
274         len = strlen(key) + 1;
275         new_item->lji_key = calloc(len, sizeof(char));
276         if (new_item->lji_key == NULL)
277                 return -ENOMEM;
278
279         strlcpy(new_item->lji_key, key, len);
280         new_item->lji_type = type;
281         new_item->lji_next = NULL;
282
283         switch (new_item->lji_type) {
284         case LLAPI_JSON_INTEGER:
285                 new_item->lji_integer = *(int *)val;
286                 break;
287         case LLAPI_JSON_BIGNUM:
288                 new_item->lji_u64 = *(__u64 *)val;
289                 break;
290         case LLAPI_JSON_REAL:
291                 new_item->lji_real = *(double *)val;
292                 break;
293         case LLAPI_JSON_STRING:
294                 len = strlen((char *)val) + 1;
295                 new_item->lji_string = calloc(len, sizeof(char));
296                 if (new_item->lji_string == NULL)
297                         return -ENOMEM;
298                 strlcpy(new_item->lji_string, (char *)val, len);
299                 break;
300         default:
301                 llapi_err_noerrno(LLAPI_MSG_ERROR, "Unknown JSON type: %d",
302                                   new_item->lji_type);
303                 return -EINVAL;
304         }
305
306         if (list->ljil_item_count == 0) {
307                 list->ljil_items = new_item;
308         } else {
309                 new_item->lji_next = list->ljil_items;
310                 list->ljil_items = new_item;
311         }
312         list->ljil_item_count++;
313
314         return 0;
315 }