diff options
Diffstat (limited to 'libpackagelistparser/packagelistparser.c')
-rw-r--r-- | libpackagelistparser/packagelistparser.c | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/libpackagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c new file mode 100644 index 000000000..16052e299 --- /dev/null +++ b/libpackagelistparser/packagelistparser.c @@ -0,0 +1,266 @@ +/* + * Copyright 2015, Intel Corporation + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Written by William Roberts <william.c.roberts@intel.com> + * + */ + +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sys/limits.h> + +#define LOG_TAG "packagelistparser" +#include <utils/Log.h> + +#include <packagelistparser/packagelistparser.h> + +#define CLOGE(fmt, ...) \ + do {\ + IF_ALOGE() {\ + ALOGE(fmt, ##__VA_ARGS__);\ + }\ + } while(0) + +static size_t get_gid_cnt(const char *gids) +{ + size_t cnt; + + if (*gids == '\0') { + return 0; + } + + if (!strcmp(gids, "none")) { + return 0; + } + + for (cnt = 1; gids[cnt]; gids[cnt] == ',' ? cnt++ : *gids++) + ; + + return cnt; +} + +static bool parse_gids(char *gids, gid_t *gid_list, size_t *cnt) +{ + gid_t gid; + char* token; + char *endptr; + size_t cmp = 0; + + while ((token = strsep(&gids, ",\r\n"))) { + + if (cmp > *cnt) { + return false; + } + + gid = strtoul(token, &endptr, 10); + if (*endptr != '\0') { + return false; + } + + /* + * if unsigned long is greater than size of gid_t, + * prevent a truncation based roll-over + */ + if (gid > GID_MAX) { + CLOGE("A gid in field \"gid list\" greater than GID_MAX"); + return false; + } + + gid_list[cmp++] = gid; + } + return true; +} + +extern bool packagelist_parse(pfn_on_package callback, void *userdata) +{ + + FILE *fp; + char *cur; + char *next; + char *endptr; + unsigned long tmp; + ssize_t bytesread; + + bool rc = false; + char *buf = NULL; + size_t buflen = 0; + unsigned long lineno = 1; + const char *errmsg = NULL; + struct pkg_info *pkg_info = NULL; + + fp = fopen(PACKAGES_LIST_FILE, "re"); + if (!fp) { + CLOGE("Could not open: \"%s\", error: \"%s\"\n", PACKAGES_LIST_FILE, + strerror(errno)); + return false; + } + + while ((bytesread = getline(&buf, &buflen, fp)) > 0) { + + pkg_info = calloc(1, sizeof(*pkg_info)); + if (!pkg_info) { + goto err; + } + + next = buf; + + cur = strsep(&next, " \t\r\n"); + if (!cur) { + errmsg = "Could not get next token for \"package name\""; + goto err; + } + + pkg_info->name = strdup(cur); + if (!pkg_info->name) { + goto err; + } + + cur = strsep(&next, " \t\r\n"); + if (!cur) { + errmsg = "Could not get next token for field \"uid\""; + goto err; + } + + tmp = strtoul(cur, &endptr, 10); + if (*endptr != '\0') { + errmsg = "Could not convert field \"uid\" to integer value"; + goto err; + } + + /* + * if unsigned long is greater than size of uid_t, + * prevent a truncation based roll-over + */ + if (tmp > UID_MAX) { + errmsg = "Field \"uid\" greater than UID_MAX"; + goto err; + } + + pkg_info->uid = (uid_t) tmp; + + cur = strsep(&next, " \t\r\n"); + if (!cur) { + errmsg = "Could not get next token for field \"debuggable\""; + goto err; + } + + tmp = strtoul(cur, &endptr, 10); + if (*endptr != '\0') { + errmsg = "Could not convert field \"debuggable\" to integer value"; + goto err; + } + + /* should be a valid boolean of 1 or 0 */ + if (!(tmp == 0 || tmp == 1)) { + errmsg = "Field \"debuggable\" is not 0 or 1 boolean value"; + goto err; + } + + pkg_info->debuggable = (bool) tmp; + + cur = strsep(&next, " \t\r\n"); + if (!cur) { + errmsg = "Could not get next token for field \"data dir\""; + goto err; + } + + pkg_info->data_dir = strdup(cur); + if (!pkg_info->data_dir) { + goto err; + } + + cur = strsep(&next, " \t\r\n"); + if (!cur) { + errmsg = "Could not get next token for field \"seinfo\""; + goto err; + } + + pkg_info->seinfo = strdup(cur); + if (!pkg_info->seinfo) { + goto err; + } + + cur = strsep(&next, " \t\r\n"); + if (!cur) { + errmsg = "Could not get next token for field \"gid(s)\""; + goto err; + } + + /* + * Parse the gid list, could be in the form of none, single gid or list: + * none + * gid + * gid, gid ... + */ + pkg_info->gids.cnt = get_gid_cnt(cur); + if (pkg_info->gids.cnt > 0) { + + pkg_info->gids.gids = calloc(pkg_info->gids.cnt, sizeof(gid_t)); + if (!pkg_info->gids.gids) { + goto err; + } + + rc = parse_gids(cur, pkg_info->gids.gids, &pkg_info->gids.cnt); + if (!rc) { + errmsg = "Could not parse field \"gid list\""; + goto err; + } + } + + rc = callback(pkg_info, userdata); + if (rc == false) { + /* + * We do not log this as this can be intentional from + * callback to abort processing. We go to out to not + * free the pkg_info + */ + rc = true; + goto out; + } + lineno++; + } + + rc = true; + +out: + free(buf); + fclose(fp); + return rc; + +err: + if (errmsg) { + CLOGE("Error Parsing \"%s\" on line: %lu for reason: %s", + PACKAGES_LIST_FILE, lineno, errmsg); + } + rc = false; + packagelist_free(pkg_info); + goto out; +} + +void packagelist_free(pkg_info *info) +{ + if (info) { + free(info->name); + free(info->data_dir); + free(info->seinfo); + free(info->gids.gids); + free(info); + } +} |