summaryrefslogtreecommitdiff
path: root/libpackagelistparser/packagelistparser.cpp
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2019-08-12 19:26:12 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2019-08-12 19:26:12 +0000
commit8c711a5384eb9aa620ede7e953fa5caa7d72d97d (patch)
tree2b485cbd0b9bf8e1881b3c901c2b7c6662aa1a15 /libpackagelistparser/packagelistparser.cpp
parenta22599df5872aeec9053a4f434107bff8f5cf257 (diff)
parentd32e7e2c05694a677e1e6514c0b9c7d7ae081186 (diff)
Merge "Rewrite libpackagelistparser."
Diffstat (limited to 'libpackagelistparser/packagelistparser.cpp')
-rw-r--r--libpackagelistparser/packagelistparser.cpp143
1 files changed, 143 insertions, 0 deletions
diff --git a/libpackagelistparser/packagelistparser.cpp b/libpackagelistparser/packagelistparser.cpp
new file mode 100644
index 000000000..ddf558b5b
--- /dev/null
+++ b/libpackagelistparser/packagelistparser.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define LOG_TAG "packagelistparser"
+
+#include <packagelistparser/packagelistparser.h>
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/limits.h>
+
+#include <memory>
+
+#include <log/log.h>
+
+static bool parse_gids(const char* path, size_t line_number, const char* gids, pkg_info* info) {
+ // Nothing to do?
+ if (!gids || !strcmp(gids, "none")) return true;
+
+ // How much space do we need?
+ info->gids.cnt = 1;
+ for (const char* p = gids; *p; ++p) {
+ if (*p == ',') ++info->gids.cnt;
+ }
+
+ // Allocate the space.
+ info->gids.gids = new gid_t[info->gids.cnt];
+ if (!info->gids.gids) return false;
+
+ // And parse the individual gids.
+ size_t i = 0;
+ while (true) {
+ char* end;
+ unsigned long gid = strtoul(gids, &end, 10);
+ if (gid > GID_MAX) {
+ ALOGE("%s:%zu: gid %lu > GID_MAX", path, line_number, gid);
+ return false;
+ }
+
+ if (i >= info->gids.cnt) return false;
+ info->gids.gids[i++] = gid;
+
+ if (*end == '\0') return true;
+ if (*end != ',') return false;
+ gids = end + 1;
+ }
+ return true;
+}
+
+static bool parse_line(const char* path, size_t line_number, const char* line, pkg_info* info) {
+ unsigned long uid;
+ int debuggable;
+ char* gid_list;
+ int profileable_from_shell = 0;
+
+ int fields =
+ sscanf(line, "%ms %lu %d %ms %ms %ms %d %ld", &info->name, &uid, &debuggable, &info->data_dir,
+ &info->seinfo, &gid_list, &profileable_from_shell, &info->version_code);
+
+ // Handle the more complicated gids field and free the temporary string.
+ bool gids_okay = parse_gids(path, line_number, gid_list, info);
+ free(gid_list);
+ if (!gids_okay) return false;
+
+ // Did we see enough fields to be getting on with?
+ // The final fields are optional (and not usually present).
+ if (fields < 6) {
+ ALOGE("%s:%zu: too few fields in line", path, line_number);
+ return false;
+ }
+
+ // Extra validation.
+ if (uid > UID_MAX) {
+ ALOGE("%s:%zu: uid %lu > UID_MAX", path, line_number, uid);
+ return false;
+ }
+ info->uid = uid;
+
+ // Integer to bool conversions.
+ if (debuggable != 0 && debuggable != 1) return false;
+ info->debuggable = debuggable;
+
+ if (profileable_from_shell != 0 && profileable_from_shell != 1) return false;
+ info->profileable_from_shell = profileable_from_shell;
+
+ return true;
+}
+
+bool packagelist_parse_file(const char* path, bool (*callback)(pkg_info*, void*), void* user_data) {
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(path, "re"), &fclose);
+ if (!fp) {
+ ALOGE("couldn't open '%s': %s", path, strerror(errno));
+ return false;
+ }
+
+ size_t line_number = 0;
+ char* line = nullptr;
+ size_t allocated_length = 0;
+ while (getline(&line, &allocated_length, fp.get()) > 0) {
+ ++line_number;
+ std::unique_ptr<pkg_info, decltype(&packagelist_free)> info(
+ static_cast<pkg_info*>(calloc(1, sizeof(pkg_info))), &packagelist_free);
+ if (!info) {
+ ALOGE("%s:%zu: couldn't allocate pkg_info", path, line_number);
+ return false;
+ }
+
+ if (!parse_line(path, line_number, line, info.get())) return false;
+
+ if (!callback(info.release(), user_data)) break;
+ }
+ free(line);
+ return true;
+}
+
+bool packagelist_parse(bool (*callback)(pkg_info*, void*), void* user_data) {
+ return packagelist_parse_file("/data/system/packages.list", callback, user_data);
+}
+
+void packagelist_free(pkg_info* info) {
+ if (!info) return;
+
+ free(info->name);
+ free(info->data_dir);
+ free(info->seinfo);
+ delete[] info->gids.gids;
+ free(info);
+}