| /* | |
| * 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); | |
| } | |
| } |