Home | History | Annotate | Download | only in libpackagelistparser
      1 /*
      2  * Copyright 2015, Intel Corporation
      3  * Copyright (C) 2015 The Android Open Source Project
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  *
     17  * Written by William Roberts <william.c.roberts (at) intel.com>
     18  *
     19  */
     20 
     21 #define LOG_TAG "packagelistparser"
     22 
     23 #include <errno.h>
     24 #include <stdbool.h>
     25 #include <stdio.h>
     26 #include <stdlib.h>
     27 #include <string.h>
     28 #include <sys/limits.h>
     29 
     30 #include <log/log.h>
     31 #include <packagelistparser/packagelistparser.h>
     32 
     33 #define CLOGE(fmt, ...) \
     34     do {\
     35         IF_ALOGE() {\
     36             ALOGE(fmt, ##__VA_ARGS__);\
     37         }\
     38     } while(0)
     39 
     40 static size_t get_gid_cnt(const char *gids)
     41 {
     42     size_t cnt;
     43 
     44     if (*gids == '\0') {
     45         return 0;
     46     }
     47 
     48     if (!strcmp(gids, "none")) {
     49         return 0;
     50     }
     51 
     52     for (cnt = 1; gids[cnt]; gids[cnt] == ',' ? cnt++ : *gids++)
     53         ;
     54 
     55     return cnt;
     56 }
     57 
     58 static bool parse_gids(char *gids, gid_t *gid_list, size_t *cnt)
     59 {
     60     gid_t gid;
     61     char* token;
     62     char *endptr;
     63     size_t cmp = 0;
     64 
     65     while ((token = strsep(&gids, ",\r\n"))) {
     66 
     67         if (cmp > *cnt) {
     68             return false;
     69         }
     70 
     71         gid = strtoul(token, &endptr, 10);
     72         if (*endptr != '\0') {
     73             return false;
     74         }
     75 
     76         /*
     77          * if unsigned long is greater than size of gid_t,
     78          * prevent a truncation based roll-over
     79          */
     80         if (gid > GID_MAX) {
     81             CLOGE("A gid in field \"gid list\" greater than GID_MAX");
     82             return false;
     83         }
     84 
     85         gid_list[cmp++] = gid;
     86     }
     87     return true;
     88 }
     89 
     90 extern bool packagelist_parse(pfn_on_package callback, void *userdata)
     91 {
     92 
     93     FILE *fp;
     94     char *cur;
     95     char *next;
     96     char *endptr;
     97     unsigned long tmp;
     98     ssize_t bytesread;
     99 
    100     bool rc = false;
    101     char *buf = NULL;
    102     size_t buflen = 0;
    103     unsigned long lineno = 1;
    104     const char *errmsg = NULL;
    105     struct pkg_info *pkg_info = NULL;
    106 
    107     fp = fopen(PACKAGES_LIST_FILE, "re");
    108     if (!fp) {
    109         CLOGE("Could not open: \"%s\", error: \"%s\"\n", PACKAGES_LIST_FILE,
    110                 strerror(errno));
    111         return false;
    112     }
    113 
    114     while ((bytesread = getline(&buf, &buflen, fp)) > 0) {
    115 
    116         pkg_info = calloc(1, sizeof(*pkg_info));
    117         if (!pkg_info) {
    118             goto err;
    119         }
    120 
    121         next = buf;
    122 
    123         cur = strsep(&next, " \t\r\n");
    124         if (!cur) {
    125             errmsg = "Could not get next token for \"package name\"";
    126             goto err;
    127         }
    128 
    129         pkg_info->name = strdup(cur);
    130         if (!pkg_info->name) {
    131             goto err;
    132         }
    133 
    134         cur = strsep(&next, " \t\r\n");
    135         if (!cur) {
    136             errmsg = "Could not get next token for field \"uid\"";
    137             goto err;
    138         }
    139 
    140         tmp = strtoul(cur, &endptr, 10);
    141         if (*endptr != '\0') {
    142             errmsg = "Could not convert field \"uid\" to integer value";
    143             goto err;
    144         }
    145 
    146         /*
    147          * if unsigned long is greater than size of uid_t,
    148          * prevent a truncation based roll-over
    149          */
    150         if (tmp > UID_MAX) {
    151             errmsg = "Field \"uid\" greater than UID_MAX";
    152             goto err;
    153         }
    154 
    155         pkg_info->uid = (uid_t) tmp;
    156 
    157         cur = strsep(&next, " \t\r\n");
    158         if (!cur) {
    159             errmsg = "Could not get next token for field \"debuggable\"";
    160             goto err;
    161         }
    162 
    163         tmp = strtoul(cur, &endptr, 10);
    164         if (*endptr != '\0') {
    165             errmsg = "Could not convert field \"debuggable\" to integer value";
    166             goto err;
    167         }
    168 
    169         /* should be a valid boolean of 1 or 0 */
    170         if (!(tmp == 0 || tmp == 1)) {
    171             errmsg = "Field \"debuggable\" is not 0 or 1 boolean value";
    172             goto err;
    173         }
    174 
    175         pkg_info->debuggable = (bool) tmp;
    176 
    177         cur = strsep(&next, " \t\r\n");
    178         if (!cur) {
    179             errmsg = "Could not get next token for field \"data dir\"";
    180             goto err;
    181         }
    182 
    183         pkg_info->data_dir = strdup(cur);
    184         if (!pkg_info->data_dir) {
    185             goto err;
    186         }
    187 
    188         cur = strsep(&next, " \t\r\n");
    189         if (!cur) {
    190             errmsg = "Could not get next token for field \"seinfo\"";
    191             goto err;
    192         }
    193 
    194         pkg_info->seinfo = strdup(cur);
    195         if (!pkg_info->seinfo) {
    196             goto err;
    197         }
    198 
    199         cur = strsep(&next, " \t\r\n");
    200         if (!cur) {
    201             errmsg = "Could not get next token for field \"gid(s)\"";
    202             goto err;
    203         }
    204 
    205         /*
    206          * Parse the gid list, could be in the form of none, single gid or list:
    207          * none
    208          * gid
    209          * gid, gid ...
    210          */
    211         pkg_info->gids.cnt = get_gid_cnt(cur);
    212         if (pkg_info->gids.cnt > 0) {
    213 
    214             pkg_info->gids.gids = calloc(pkg_info->gids.cnt, sizeof(gid_t));
    215             if (!pkg_info->gids.gids) {
    216                 goto err;
    217             }
    218 
    219             rc = parse_gids(cur, pkg_info->gids.gids, &pkg_info->gids.cnt);
    220             if (!rc) {
    221                 errmsg = "Could not parse field \"gid list\"";
    222                 goto err;
    223             }
    224         }
    225 
    226         rc = callback(pkg_info, userdata);
    227         if (rc == false) {
    228             /*
    229              * We do not log this as this can be intentional from
    230              * callback to abort processing. We go to out to not
    231              * free the pkg_info
    232              */
    233             rc = true;
    234             goto out;
    235         }
    236         lineno++;
    237     }
    238 
    239     rc = true;
    240 
    241 out:
    242     free(buf);
    243     fclose(fp);
    244     return rc;
    245 
    246 err:
    247     if (errmsg) {
    248         CLOGE("Error Parsing \"%s\" on line: %lu for reason: %s",
    249                 PACKAGES_LIST_FILE, lineno, errmsg);
    250     }
    251     rc = false;
    252     packagelist_free(pkg_info);
    253     goto out;
    254 }
    255 
    256 void packagelist_free(pkg_info *info)
    257 {
    258     if (info) {
    259         free(info->name);
    260         free(info->data_dir);
    261         free(info->seinfo);
    262         free(info->gids.gids);
    263         free(info);
    264     }
    265 }
    266