Whamcloud - gitweb
e4548379496beb50336a1f7854cda056c477c277
[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 <libcfs/libcfs.h>
46 #include <lustre/lustreapi.h>
47
48 /** Quick-n'-dirty JSON string escape routine.
49  * \param[out]  out_string      JSON-escaped string, allocated here
50  * \param[in]   in_string       Unescaped string
51  *
52  * \retval      0 on success.
53  * \retval      -errno on error.
54  *
55  * http://json.org/
56  * http://www.ietf.org/rfc/rfc4627.txt (section 2.5)
57  */
58 int llapi_json_escape_string(char **out_string, char *in_string)
59 {
60         int     i;
61         char    escape_chars[] = {'\b', '\f', '\n', '\r', '\t', '"', '\\',
62                                   '\0'};
63         char    *escaped_chars[] = {"\\\\b", "\\\\f", "\\\\n", "\\\\r",
64                                     "\\\\t", "\\\\\"", "\\\\\\\\"};
65         char    *src = in_string;
66         char    *idx, *dst, *tmp;
67         char    *escaped_string;
68         size_t  tmp_len, escaped_length = strlen(in_string);
69
70         /* add up the extra space needed for the escapes */
71         while (*src) {
72                 idx = strchr(escape_chars, *src);
73                 if (idx != NULL) {
74                         tmp = escaped_chars[idx - escape_chars];
75                         escaped_length += strlen(tmp);
76                 }
77                 src++;
78         }
79
80         escaped_string = calloc(1, escaped_length + 1);
81         if (escaped_string == NULL)
82                 return -ENOMEM;
83
84         src = in_string;
85         dst = escaped_string;
86         for (i = 0; *src && i <= escaped_length; i++) {
87                 idx = strchr(escape_chars, *src);
88                 if (idx != NULL) {
89                         tmp = escaped_chars[idx - escape_chars];
90                         tmp_len = strlen(tmp);
91                         memcpy(dst, tmp, tmp_len);
92                         dst += tmp_len;
93                         src++;
94                 } else {
95                         *dst = *src;
96                         src++;
97                         dst++;
98                 }
99         }
100
101         *dst = '\0';
102
103         *out_string = escaped_string;
104
105         return 0;
106 }
107
108 /** Write a list of JSON items to a filehandle.
109  * \param       json_items      list of JSON items to be written
110  * \param       fp              open filehandle to use for write
111  *
112  * \retval      0 on success.
113  * \retval      -errno on error.
114  */
115 int llapi_json_write_list(struct llapi_json_item_list **json_items, FILE *fp)
116 {
117         int                             i;
118         char                            *escaped_string = NULL;
119         struct llapi_json_item_list     *list;
120         struct llapi_json_item          *item;
121
122         if (json_items == NULL || *json_items == NULL)
123                 return -EINVAL;
124
125         list = *json_items;
126         item = list->ljil_items;
127
128         fprintf(fp, "{");
129         for (i = 0; i < list->ljil_item_count; i++) {
130                 if (item == NULL) {
131                         llapi_err_noerrno(LLAPI_MSG_ERROR,
132                                           "%d json items but %d is NULL!",
133                                           list->ljil_item_count, i);
134                         /* Don't bomb out here so that we still emit
135                          * valid JSON. */
136                         break;
137                 }
138
139                 fprintf(fp, "\"%s\": ", item->lji_key);
140                 switch (item->lji_type) {
141                 case LLAPI_JSON_INTEGER:
142                         fprintf(fp, "%d", item->lji_integer);
143                         break;
144                 case LLAPI_JSON_BIGNUM:
145                         fprintf(fp, LPU64, item->lji_u64);
146                         break;
147                 case LLAPI_JSON_REAL:
148                         fprintf(fp, "%f", item->lji_real);
149                         break;
150                 case LLAPI_JSON_STRING:
151                         if (llapi_json_escape_string(&escaped_string,
152                                                      item->lji_string) < 0) {
153                                 if (escaped_string != NULL)
154                                         free(escaped_string);
155                                 return -errno;
156                         }
157
158                         fprintf(fp, "\"%s\"", escaped_string);
159
160                         if (escaped_string != NULL)
161                                 free(escaped_string);
162                         break;
163                 default:
164                         llapi_err_noerrno(LLAPI_MSG_ERROR,
165                                     "Invalid item type: %d", item->lji_type);
166                         /* Ensure valid JSON */
167                         fprintf(fp, "\"\"");
168                         break;
169                 }
170
171                 if (i < list->ljil_item_count - 1)
172                         fprintf(fp, ", ");
173
174                 item = item->lji_next;
175         }
176         fprintf(fp, "}\n");
177
178         return 0;
179 }
180
181 /** Create a list to hold JSON items.
182  * \param[out]  json_items      Item list handle, allocated here
183  *
184  * \retval      0 on success.
185  * \retval      -errno on error.
186  */
187 int llapi_json_init_list(struct llapi_json_item_list **json_items)
188 {
189         struct llapi_json_item_list     *new_list;
190
191         new_list = calloc(1, sizeof(*new_list));
192         if (new_list == NULL)
193                 return -ENOMEM;
194
195         new_list->ljil_item_count = 0;
196
197         *json_items = new_list;
198
199         return 0;
200 }
201
202 /** Deallocate a list of JSON items.
203  * \param       json_items      Item list handle, deallocated here
204  *
205  * \retval      0 on success.
206  * \retval      -errno on error.
207  */
208 int llapi_json_destroy_list(struct llapi_json_item_list **json_items)
209 {
210         int                             i;
211         struct llapi_json_item_list     *list;
212         struct llapi_json_item          *cur_item;
213         struct llapi_json_item          *last_item;
214
215         if (json_items == NULL || *json_items == NULL)
216                 return -EINVAL;
217
218         list = *json_items;
219         cur_item = list->ljil_items;
220
221         for (i = 0; i < list->ljil_item_count; i++) {
222                 if (cur_item == NULL) {
223                         llapi_err_noerrno(LLAPI_MSG_ERROR,
224                                           "%d json items but %d is NULL!",
225                                           list->ljil_item_count, i);
226                         return -EINVAL;
227                 }
228
229                 if (cur_item->lji_key != NULL)
230                         free(cur_item->lji_key);
231
232                 if (cur_item->lji_type == LLAPI_JSON_STRING
233                     && cur_item->lji_string != NULL)
234                         free(cur_item->lji_string);
235
236                 last_item = cur_item;
237                 cur_item = last_item->lji_next;
238                 free(last_item);
239         }
240
241         free(list);
242         *json_items = NULL;
243
244         return 0;
245 }
246
247 /** Add an item to a list of JSON items.
248  * \param       json_items      Item list handle
249  * \param       key             Item key name
250  * \param       type            Item key type
251  * \param       val             Item key value
252  *
253  * \retval      0 on success.
254  * \retval      -errno on error.
255  */
256 int llapi_json_add_item(struct llapi_json_item_list **json_items,
257                         char *key, __u32 type, void *val)
258 {
259         struct llapi_json_item_list     *list;
260         struct llapi_json_item          *new_item;
261         size_t len;
262
263         if (json_items == NULL || *json_items == NULL)
264                 return -EINVAL;
265
266         if (val == NULL)
267                 return -EINVAL;
268
269         list = *json_items;
270
271         new_item = calloc(1, sizeof(*new_item));
272         if (new_item == NULL)
273                 return -ENOMEM;
274
275         len = strlen(key) + 1;
276         new_item->lji_key = calloc(len, sizeof(char));
277         if (new_item->lji_key == NULL)
278                 return -ENOMEM;
279
280         strlcpy(new_item->lji_key, key, len);
281         new_item->lji_type = type;
282         new_item->lji_next = NULL;
283
284         switch (new_item->lji_type) {
285         case LLAPI_JSON_INTEGER:
286                 new_item->lji_integer = *(int *)val;
287                 break;
288         case LLAPI_JSON_BIGNUM:
289                 new_item->lji_u64 = *(__u64 *)val;
290                 break;
291         case LLAPI_JSON_REAL:
292                 new_item->lji_real = *(double *)val;
293                 break;
294         case LLAPI_JSON_STRING:
295                 len = strlen((char *)val) + 1;
296                 new_item->lji_string = calloc(len, sizeof(char));
297                 if (new_item->lji_string == NULL)
298                         return -ENOMEM;
299                 strlcpy(new_item->lji_string, (char *)val, len);
300                 break;
301         default:
302                 llapi_err_noerrno(LLAPI_MSG_ERROR, "Unknown JSON type: %d",
303                                   new_item->lji_type);
304                 return -EINVAL;
305         }
306
307         if (list->ljil_item_count == 0) {
308                 list->ljil_items = new_item;
309         } else {
310                 new_item->lji_next = list->ljil_items;
311                 list->ljil_items = new_item;
312         }
313         list->ljil_item_count++;
314
315         return 0;
316 }