Prevent buffer overflow on reading value
[platform/core/api/system-info.git] / src / init_db / system_info_db_init.c
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17
18 #include <errno.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <ctype.h>
24 #include <getopt.h>
25 #include <fcntl.h>
26 #include <sys/stat.h>
27 #include <dlog.h>
28
29 #include <system_info.h>
30 #include <iniparser.h>
31 #include <libxml/xmlmemory.h>
32 #include <libxml/parser.h>
33 #include <libxml/tree.h>
34
35 #include "system_info_private.h"
36
37 #ifdef LOG_TAG
38 #undef LOG_TAG
39 #endif
40
41 #define LOG_TAG "SYSTEM_INFO"
42 #define _D(fmt, args...)   SLOGD(fmt, ##args)
43 #define _E(fmt, args...)   SLOGE(fmt, ##args)
44 #define _I(fmt, args...)   SLOGI(fmt, ##args)
45
46 #define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0]))
47
48 #define KEY_MAX 256
49
50 #define MODEL_CONFIG_TAG "model-config"
51 #define KEY_PREFIX "http://"
52
53 extern const struct runtime runtime[LANG_MAX];
54
55 static int db_set_value(const char *db_path, char *tag, char *name, char *type, char *value, int val_len)
56 {
57         int ret;
58         char key_internal[KEY_MAX];
59         size_t key_internal_len;
60         char file_path[PATH_MAX];
61         char file_path_new[PATH_MAX * 2];
62         char buf[PATH_MAX];
63         char *ptr;
64         FILE *fp = NULL;
65         FILE *fp_new = NULL;
66         int key_idx;
67
68         if (!db_path || !tag || !name || !type || !value)
69                 return -EINVAL;
70
71         if (name == strstr(name, KEY_PREFIX))
72                 snprintf(key_internal, sizeof(key_internal), "%s:%s:%s", tag, name + strlen(KEY_PREFIX), type);
73         else
74                 snprintf(key_internal, sizeof(key_internal), "%s:%s:%s", tag, name, type);
75         snprintf(file_path, sizeof(file_path), "%s/%lu", db_path, simple_hash(key_internal));
76
77         fp = fopen(file_path, "r+");
78         if (!fp) {
79                 _E("fopen for %s failed (%d)", file_path, errno);
80                 return -errno;
81         }
82
83         key_internal_len = strlen(key_internal);
84         key_idx = 0;
85         while ((ptr = fgets(buf, sizeof(buf), fp))) {
86                 if (!strncmp(buf, key_internal, key_internal_len) && buf[key_internal_len] == ' ')
87                         break;
88                 key_idx++;
89         }
90
91         // 1. Insert new value
92         if (!ptr) {
93                 ret = fprintf(fp, "%s %s\n", key_internal, value);
94                 fclose(fp);
95
96                 if (ret < 2) {
97                         _E("fprintf for %s failed (%d)", file_path, errno);
98                         return -errno;
99                 }
100
101                 _I("DB: value (key:%s,value:%s) is stored", name, value);
102                 return 0;
103         }
104
105         // 2. Make new file to update value
106         snprintf(file_path_new, sizeof(file_path_new), "%s_tmp", file_path);
107         fp_new = fopen(file_path_new, "w");
108         if (!fp_new) {
109                 _E("fopen for %s failed (%d)", file_path_new, errno);
110                 ret = -errno;
111                 goto clean;
112         }
113
114         // 2-1. Copy prefix
115         rewind(fp);
116         for (int i = 0; i < key_idx; i++) {
117                 if (!fgets(buf, sizeof(buf), fp)) {
118                         _E("fgets for %s failed", file_path);
119                         ret = -EIO;
120                         goto clean;
121                 }
122                 if (fputs(buf, fp_new) == EOF) {
123                         _E("fputs for %s failed", file_path_new);
124                         ret = -EIO;
125                         goto clean;
126                 }
127         }
128
129         // 2-2. Insert new value
130         ret = fprintf(fp_new, "%s %s\n", key_internal, value);
131         if (ret < 2) {
132                 _E("fprintf for %s failed (%d)", file_path_new, errno);
133                 ret = -errno;
134                 goto clean;
135         }
136
137         // To move position indicator
138         if (!fgets(buf, sizeof(buf), fp)) {
139                 _E("fgets for %s failed", file_path);
140                 ret = -EIO;
141                 goto clean;
142         }
143
144         // 2-3. Copy suffix
145         while (fgets(buf, sizeof(buf), fp)) {
146                 if (fputs(buf, fp_new) == EOF) {
147                         _E("fputs for %s failed", file_path_new);
148                         ret = -EIO;
149                         goto clean;
150                 }
151         }
152
153         // 2-4. Overwrite file
154         ret = rename(file_path_new, file_path);
155         if (ret != 0) {
156                 _E("rename for %s failed (%d)", file_path_new, errno);
157                 ret = -errno;
158                 goto clean;
159         }
160
161         _I("DB: value (key:%s,value:%s) is updated", name, value);
162         ret = 0;
163
164 clean:
165         if (fp_new)
166                 fclose(fp_new);
167         if (fp)
168                 fclose(fp);
169
170         return ret;
171 }
172
173 static int db_get_value(const char *db_path, char *tag, char *name, char *type, char *value, int val_len)
174 {
175         char key_internal[KEY_MAX];
176         size_t key_internal_len;
177         char buf[PATH_MAX];
178         size_t value_internal_len;
179         char *ptr;
180         FILE *fp = NULL;
181
182         if (!db_path || !tag || !name || !type || !value)
183                 return -EINVAL;
184
185         if (name == strstr(name, KEY_PREFIX))
186                 snprintf(key_internal, sizeof(key_internal), "%s:%s:%s", tag, name + strlen(KEY_PREFIX), type);
187         else
188                 snprintf(key_internal, sizeof(key_internal), "%s:%s:%s", tag, name, type);
189         snprintf(buf, sizeof(buf), "%s/%lu", db_path, simple_hash(key_internal));
190
191         fp = fopen(buf, "r");
192         if (!fp) {
193                 _E("fopen for %s failed (%d)", buf, errno);
194                 return -errno;
195         }
196
197         key_internal_len = strlen(key_internal);
198         while ((ptr = fgets(buf, sizeof(buf), fp))) {
199                 if (!strncmp(buf, key_internal, key_internal_len) && buf[key_internal_len] == ' ') {
200                         value_internal_len = strcspn(buf + key_internal_len + 1, "\n") + 1;
201                         snprintf(value, val_len < value_internal_len ? val_len : value_internal_len,
202                                         "%s", buf + key_internal_len + 1);
203                         break;
204                 }
205         }
206         fclose(fp);
207
208         if (!ptr) {
209                 _E("fgets for %s failed", key_internal);
210                 return -EIO;
211         }
212
213         _I("DB: value (key:%s,value:%s) is fetched", name, value);
214
215         return 0;
216 }
217
218 static int db_set_value_specific_runtime(const char *db_path, char *tag, char *name, char *type, char *value, int lang)
219 {
220         char value_intg[LANG_MAX + 1] = {0};
221         int ret;
222
223         ret = db_get_value(db_path, tag, name, type, value_intg, LANG_MAX + 1);
224         if (ret != 0)
225                 return ret;
226
227         value_intg[lang] = (value[0] == 't' ? 'T' : 'F');
228         ret = db_set_value(db_path, tag, name, type, value_intg, LANG_MAX);
229
230         return ret;
231 }
232
233 static int db_set_value_foreach_runtime(const char *db_path, xmlNode *node,
234                                                 char *tag, char *name, char *type, char *value)
235 {
236         int rt;
237         xmlChar *prop_val;
238         char value_intg[LANG_MAX + 1] = {0};
239         int ret;
240
241         memset(value_intg, strncmp(value, "true", 4) ? 'F' : 'T', LANG_MAX);
242
243         for (rt = 0; rt < LANG_MAX; rt++) {
244                 if (!runtime[rt].xml_prop)
245                         break;
246
247                 prop_val = xmlGetProp(node, (xmlChar *)(runtime[rt].xml_prop));
248                 if (!prop_val)
249                         continue;
250
251                 value_intg[runtime[rt].lang] = xmlStrcmp(prop_val, (xmlChar *)"on") ? 'F' : 'T';
252
253                 xmlFree(prop_val);
254         }
255
256         ret = db_set_value(db_path, tag, name, type, value_intg, LANG_MAX);
257
258         return ret;
259 }
260
261 static int system_info_get_values_config_xml(const char *db_path, const char *path)
262 {
263         xmlDocPtr doc;
264         xmlNodePtr cur;
265         xmlNode *cur_node, *tag_node;
266         char *tag, *name, *type, *value;
267         int ret;
268
269         if (!db_path)
270                 return -EINVAL;
271
272         doc = xmlParseFile(path);
273         if (!doc) {
274                 _E("cannot file open %s file!!!", path);
275                 return SYSTEM_INFO_ERROR_IO_ERROR;
276         }
277
278         cur = xmlDocGetRootElement(doc);
279         if (!cur) {
280                 _E("empty document %s file!!!", path);
281                 ret = -ENOENT;
282                 goto out;
283         }
284
285         for (cur_node = cur; cur_node ; cur_node = cur_node->next) {
286                 if (!xmlStrcmp(cur->name, (const xmlChar*)MODEL_CONFIG_TAG))
287                         break;
288         }
289         if (!cur_node) {
290                 _E("cannot find %s root element file!!!", MODEL_CONFIG_TAG);
291                 ret = -ENOENT;
292                 goto out;
293         }
294
295         cur = cur_node->xmlChildrenNode;
296         for (tag_node = cur; tag_node; tag_node = tag_node->next) {
297                 if (!xmlStrcmp(tag_node->name, (const xmlChar *)TAG_TYPE_PLATFORM_STR))
298                         tag = TAG_TYPE_PLATFORM_STR;
299                 else if (!xmlStrcmp(tag_node->name, (const xmlChar *)TAG_TYPE_CUSTOM_STR))
300                         tag = TAG_TYPE_CUSTOM_STR;
301                 else
302                         continue;
303
304                 cur = tag_node->xmlChildrenNode;
305                 for (cur_node = cur; cur_node ; cur_node = cur_node->next) {
306                         if (cur_node->type != XML_ELEMENT_NODE)
307                                 continue;
308
309                         name = (char *)xmlGetProp(cur_node, (const xmlChar*)"name");
310                         if (!name)
311                                 continue;
312
313                         type = (char *)xmlGetProp(cur_node, (const xmlChar*)"type");
314                         if (!type) {
315                                 xmlFree(name);
316                                 continue;
317                         }
318
319                         value = (char *)xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1);
320                         if (!value) {
321                                 xmlFree(name);
322                                 xmlFree(type);
323                                 continue;
324                         }
325
326                         if (!strncmp(type, "bool", 4))
327                                 ret = db_set_value_foreach_runtime(db_path, cur_node, tag, name, type, value);
328                         else
329                                 ret = db_set_value(db_path, tag, name, type, value, strlen(value));
330
331                         if (ret < 0)
332                                 _E("Failed to set value (%d)", ret);
333
334                         xmlFree(name);
335                         xmlFree(type);
336                         xmlFree(value);
337                 }
338         }
339
340         ret = 0;
341
342 out:
343         if (doc)
344                 xmlFreeDoc(doc);
345         return ret;
346 }
347
348 static struct build_ini_keys {
349         char *info;
350         char *key;
351 } ini_keys[] = {
352         { "version:model",   "http://tizen.org/system/build.model"   },
353         { "version:build",   "http://tizen.org/system/build.string"  },
354         { "version:release", "http://tizen.org/system/build.release" },
355         { "build:type",      "http://tizen.org/system/build.type"    },
356         { "build:date",      "http://tizen.org/system/build.date"    },
357         { "build:time",      "http://tizen.org/system/build.time"    },
358         { "build:variant",   "http://tizen.org/system/build.variant" },
359         { "build:id",        "http://tizen.org/system/build.id"      },
360 };
361
362 static int system_info_get_values_ini(const char *db_path)
363 {
364         dictionary *ini;
365         int i, ret;
366         char *value;
367
368         if (!db_path)
369                 return -EINVAL;
370
371         ini = iniparser_load(INFO_FILE_PATH);
372         if (!ini) {
373                 _E("cannot file open %s file!!!", INFO_FILE_PATH);
374                 return -ENOENT;
375         }
376
377         for (i = 0 ; i < ARRAY_SIZE(ini_keys) ; i++) {
378                 value = iniparser_getstring(ini, ini_keys[i].info, NULL);
379                 if (!value) {
380                         _E("NOT found %s", ini_keys[i].info);
381                         continue;
382                 }
383
384                 ret = db_set_value(db_path, TAG_TYPE_PLATFORM_STR, ini_keys[i].key, STR_TYPE, value, strlen(value));
385                 if (ret < 0)
386                         _E("Failed to set value (%d)", ret);
387         }
388
389         iniparser_freedict(ini);
390
391         return 0;
392 }
393
394 static int system_info_create_db(const char *conf_path, char *db_path)
395 {
396         int ret;
397         char file_path[PATH_MAX];
398         FILE *fp = NULL;
399
400         if (conf_path == NULL)
401                 conf_path = MODEL_CONFIG_RO_PATH;
402
403         if (db_path == NULL)
404                 db_path = SYSTEM_INFO_DB_RO_PATH;
405
406         ret = mkdir(db_path, 0555);
407         if (ret != 0 && errno != EEXIST) {
408                 _E("mkdir for %s failed (%d)", db_path, errno);
409                 return -errno;
410         }
411
412         for (int i = 0; i < NUM_HASH_FILE; i++) {
413                 snprintf(file_path, sizeof(file_path), "%s/%d", db_path, i);
414                 fp = fopen(file_path, "w");
415                 if (!fp) {
416                         _E("fopen for %s failed (%d)", file_path, errno);
417                         return -errno;
418                 }
419                 fclose(fp);
420         }
421
422         ret = system_info_get_values_config_xml(db_path, conf_path);
423         if (ret < 0)
424                 _E("Failed to get keys and values from xml(%d)", ret);
425
426         ret = system_info_get_values_ini(db_path);
427         if (ret < 0)
428                 _E("Failed to get keys and values from ini(%d)", ret);
429
430         return 0;
431 }
432
433 static void show_help(void)
434 {
435         printf("system_info_init_db [OPTIONS]\n");
436         printf("  -h --help         Show this help\n");
437         printf("  -k --key=KEY      System info key to update\n");
438         printf("  -t --type=TYPE    System info type to update (int/bool/double/string)\n");
439         printf("  -l --lang=LANG    System info specific language target (capi/webapi/csapi)\n");
440         printf("  -g --tag=TAG      System info tag to update (platform/custom)\n");
441         printf("  -v --value=VALUE  System info value to update\n");
442         printf("  -i --input=PATH   System info get input argument from several source\n");
443         printf("  -o --output=PATH  System info get output argument from several source\n");
444 }
445
446 static int system_info_update_db(int argc, char *argv[])
447 {
448         int ret;
449         int opt;
450         bool failed = false;
451         char key[KEY_MAX] = {0};
452         char type[KEY_MAX] = {0};
453         char tag[KEY_MAX] = {0};
454         char value[KEY_MAX] = {0};
455         char value_bool[LANG_MAX + 1] = {0};
456         char conf_path[KEY_MAX] = {0};
457         char db_path[KEY_MAX] = {0};
458         enum language lang = LANG_MAX;
459         int rt;
460
461         struct option long_options[] = {
462                 { "key",   required_argument, 0, 0 },
463                 { "type",  required_argument, 0, 0 },
464                 { "tag",   required_argument, 0, 0 },
465                 { "value", required_argument, 0, 0 },
466                 { "input", required_argument, 0, 0 },
467                 { "output", required_argument, 0, 0 },
468                 { "help",  no_argument,       0, 0 },
469                 { 0,       0,                 0, 0 },
470         };
471
472         while (1) {
473                 opt = getopt_long(argc, argv, "k:t:g:v:l:i:o:h",
474                                 long_options, NULL);
475                 if (opt < 0)
476                         break;
477                 switch (opt) {
478                 case 'k':
479                         snprintf(key, sizeof(key), "%s", optarg);
480                         break;
481                 case 't':
482                         snprintf(type, sizeof(type), "%s", optarg);
483                         break;
484                 case 'g':
485                         snprintf(tag, sizeof(tag), "%s", optarg);
486                         break;
487                 case 'v':
488                         snprintf(value, sizeof(value), "%s", optarg);
489                         break;
490                 case 'i':
491                         snprintf(conf_path, sizeof(conf_path), "%s", optarg);
492                         break;
493                 case 'o':
494                         snprintf(db_path, sizeof(db_path), "%s", optarg);
495                         break;
496                 case 'l':
497                         for (rt = 0; rt < LANG_MAX; rt++) {
498                                 if (!runtime[rt].xml_prop)
499                                         break;
500                                 if (!strncmp(optarg, runtime[rt].xml_prop, RT_PREFIX)) {
501                                         lang = runtime[rt].lang;
502                                         break;
503                                 }
504                         }
505                         if (rt == LANG_MAX || !runtime[rt].xml_prop) {
506                                 printf("Invalid language (%s)\n", optarg);
507                                 return 0;
508                         }
509                         break;
510                 case 'h':
511                 default:
512                         show_help();
513                         return 0;
514                 }
515         }
516
517         if (conf_path[0] != '\0' && db_path[0] != '\0') {
518                 printf("Make system info db(%s) by %s\n", db_path, conf_path);
519                 return system_info_create_db(conf_path, db_path);
520         }
521
522         failed = false;
523         if (key[0] == '\0') {
524                 printf("Invalid Parameter: no key\n");
525                 failed = true;
526         }
527         if (type[0] == '\0') {
528                 printf("Invalid Parameter: no type\n");
529                 failed = true;
530         }
531         if (tag[0] == '\0') {
532                 printf("Invalid Parameter: no tag\n");
533                 failed = true;
534         }
535         if (value[0] == '\0') {
536                 printf("Invalid Parameter: no value\n");
537                 failed = true;
538         }
539         if ((lang != LANG_MAX) && (strncmp(type, "bool", 4))) {
540                 printf("Invalid Parameter: lang parameter supports for just bool type\n");
541                 failed = true;
542         }
543
544         if (failed)
545                 return -EINVAL;
546
547         if (lang == LANG_MAX)
548                 _I("Request to update: key(%s), type(%s), tag(%s), value(%s)",
549                                 key, type, tag, value);
550         else
551                 _I("Request to update for specific lang(%s): key(%s), type(%s), tag(%s), value(%s)",
552                                 runtime[lang].xml_prop, key, type, tag, value);
553
554         if (!strncmp(type, "bool", 4)) {
555                 if (lang == LANG_MAX) {
556                         memset(value_bool, value[0] == 't' ? 'T' : 'F', LANG_MAX);
557                         ret = db_set_value(SYSTEM_INFO_DB_RO_PATH, tag, key, type, value_bool, LANG_MAX);
558                 } else
559                         ret = db_set_value_specific_runtime(SYSTEM_INFO_DB_RO_PATH, tag, key, type, value, lang);
560         } else
561                 ret = db_set_value(SYSTEM_INFO_DB_RO_PATH, tag, key, type, value, strlen(value));
562
563         if (ret != 0)
564                 _E("Failed to set value (%d)", ret);
565
566         return ret;
567 }
568
569 int main(int argc, char *argv[])
570 {
571         umask(0222);
572
573         if (argc == 1)
574                 return system_info_create_db(NULL, NULL);
575
576         return system_info_update_db(argc, argv);
577 }