/* * 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> * */ #define LOG_TAG "packagelistparser" #include <errno.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/limits.h> #include <log/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; } } cur = strsep(&next, " \t\r\n"); if (cur) { tmp = strtoul(cur, &endptr, 10); if (*endptr != '\0') { errmsg = "Could not convert field \"profileable_from_shell\" to integer value"; goto err; } /* should be a valid boolean of 1 or 0 */ if (!(tmp == 0 || tmp == 1)) { errmsg = "Field \"profileable_from_shell\" is not 0 or 1 boolean value"; goto err; } pkg_info->profileable_from_shell = (bool)tmp; } cur = strsep(&next, " \t\r\n"); if (cur) { tmp = strtoul(cur, &endptr, 10); if (*endptr != '\0') { errmsg = "Could not convert field \"versionCode\" to integer value"; goto err; } pkg_info->version_code = tmp; } 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); } }