Whamcloud - gitweb
be9a1e89271f2f14feff946966973efb66f36cf6
[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 <liblustre.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         if (fprintf(fp, "{") < 0)
128                 return -errno;
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                 if (fprintf(fp, "\"%s\": ", item->lji_key) < 0)
140                         return -errno;
141                 switch (item->lji_type) {
142                 case LLAPI_JSON_INTEGER:
143                         if (fprintf(fp, "%d", item->lji_integer) < 0)
144                                 return -errno;
145                         break;
146                 case LLAPI_JSON_BIGNUM:
147                         if (fprintf(fp, LPU64, item->lji_u64) < 0)
148                                 return -errno;
149                         break;
150                 case LLAPI_JSON_REAL:
151                         if (fprintf(fp, "%f", item->lji_real) < 0)
152                                 return -errno;
153                         break;
154                 case LLAPI_JSON_STRING:
155                         if (llapi_json_escape_string(&escaped_string,
156                                                         item->lji_string) < 0) {
157                                 if (escaped_string != NULL)
158                                         free(escaped_string);
159                                 return -errno;
160                         }
161
162                         if (fprintf(fp, "\"%s\"", escaped_string) < 0) {
163                                 if (escaped_string != NULL)
164                                         free(escaped_string);
165                                 return -errno;
166                         }
167
168                         if (escaped_string != NULL)
169                                 free(escaped_string);
170                         break;
171                 default:
172                         llapi_err_noerrno(LLAPI_MSG_ERROR,
173                                     "Invalid item type: %d", item->lji_type);
174                         /* Ensure valid JSON */
175                         if (fprintf(fp, "\"\"") < 0)
176                                 return -errno;
177                         break;
178                 }
179
180                 if (i < list->ljil_item_count - 1)
181                         if (fprintf(fp, ", ") < 0)
182                                 return -errno;
183
184                 item = item->lji_next;
185         }
186         if (fprintf(fp, "}\n") < 0)
187                 return -errno;
188
189         return 0;
190 }
191
192 /** Create a list to hold JSON items.
193  * \param[out]  json_items      Item list handle, allocated here
194  *
195  * \retval      0 on success.
196  * \retval      -errno on error.
197  */
198 int llapi_json_init_list(struct llapi_json_item_list **json_items)
199 {
200         struct llapi_json_item_list     *new_list;
201
202         new_list = calloc(1, sizeof(*new_list));
203         if (new_list == NULL)
204                 return -ENOMEM;
205
206         new_list->ljil_item_count = 0;
207
208         *json_items = new_list;
209
210         return 0;
211 }
212
213 /** Deallocate a list of JSON items.
214  * \param       json_items      Item list handle, deallocated here
215  *
216  * \retval      0 on success.
217  * \retval      -errno on error.
218  */
219 int llapi_json_destroy_list(struct llapi_json_item_list **json_items)
220 {
221         int                             i;
222         struct llapi_json_item_list     *list;
223         struct llapi_json_item          *cur_item;
224         struct llapi_json_item          *last_item;
225
226         if (json_items == NULL || *json_items == NULL)
227                 return -EINVAL;
228
229         list = *json_items;
230         cur_item = list->ljil_items;
231
232         for (i = 0; i < list->ljil_item_count; i++) {
233                 if (cur_item == NULL) {
234                         llapi_err_noerrno(LLAPI_MSG_ERROR,
235                                           "%d json items but %d is NULL!",
236                                           list->ljil_item_count, i);
237                         return -EINVAL;
238                 }
239
240                 if (cur_item->lji_key != NULL)
241                         free(cur_item->lji_key);
242
243                 if (cur_item->lji_type == LLAPI_JSON_STRING
244                     && cur_item->lji_string != NULL)
245                         free(cur_item->lji_string);
246
247                 last_item = cur_item;
248                 cur_item = last_item->lji_next;
249                 free(last_item);
250         }
251
252         free(list);
253         *json_items = NULL;
254
255         return 0;
256 }
257
258 /** Add an item to a list of JSON items.
259  * \param       json_items      Item list handle
260  * \param       key             Item key name
261  * \param       type            Item key type
262  * \param       val             Item key value
263  *
264  * \retval      0 on success.
265  * \retval      -errno on error.
266  */
267 int llapi_json_add_item(struct llapi_json_item_list **json_items,
268                         char *key, __u32 type, void *val)
269 {
270         struct llapi_json_item_list     *list;
271         struct llapi_json_item          *new_item;
272
273         if (json_items == NULL || *json_items == NULL)
274                 return -EINVAL;
275
276         if (val == NULL)
277                 return -EINVAL;
278
279         list = *json_items;
280
281         new_item = calloc(1, sizeof(*new_item));
282         if (new_item == NULL)
283                 return -ENOMEM;
284
285         new_item->lji_key = calloc(1, strlen(key) + 1);
286         if (new_item->lji_key == NULL)
287                 return -ENOMEM;
288
289         strncpy(new_item->lji_key, key, strlen(key));
290         new_item->lji_type = type;
291         new_item->lji_next = NULL;
292
293         switch (new_item->lji_type) {
294         case LLAPI_JSON_INTEGER:
295                 new_item->lji_integer = *(int *)val;
296                 break;
297         case LLAPI_JSON_BIGNUM:
298                 new_item->lji_u64 = *(__u64 *)val;
299                 break;
300         case LLAPI_JSON_REAL:
301                 new_item->lji_real = *(double *)val;
302                 break;
303         case LLAPI_JSON_STRING:
304                 new_item->lji_string = calloc(1, strlen((char *)val) + 1);
305                 if (new_item->lji_string == NULL)
306                         return -ENOMEM;
307                 strncpy(new_item->lji_string,
308                         (char *)val, strlen((char *)val));
309                 break;
310         default:
311                 llapi_err_noerrno(LLAPI_MSG_ERROR, "Unknown JSON type: %d",
312                                   new_item->lji_type);
313                 return -EINVAL;
314         }
315
316         if (list->ljil_item_count == 0) {
317                 list->ljil_items = new_item;
318         } else {
319                 new_item->lji_next = list->ljil_items;
320                 list->ljil_items = new_item;
321         }
322         list->ljil_item_count++;
323
324         return 0;
325 }