Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8c711a53 authored by Elliott Hughes's avatar Elliott Hughes Committed by Gerrit Code Review
Browse files

Merge "Rewrite libpackagelistparser."

parents a22599df d32e7e2c
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -18,6 +18,9 @@
    {
      "name": "libbase_test"
    },
    {
      "name": "libpackagelistparser_test"
    },
    {
      "name": "libprocinfo_test"
    },
+1 −0
Original line number Diff line number Diff line
../.clang-format-2
 No newline at end of file
+11 −6
Original line number Diff line number Diff line
cc_library {

    name: "libpackagelistparser",
    recovery_available: true,
    srcs: ["packagelistparser.c"],
    cflags: [
        "-Wall",
        "-Werror",
    ],
    srcs: ["packagelistparser.cpp"],
    shared_libs: ["liblog"],
    local_include_dirs: ["include"],
    export_include_dirs: ["include"],
@@ -15,3 +10,13 @@ cc_library {
        misc_undefined: ["integer"],
    },
}

cc_test {
    name: "libpackagelistparser_test",
    srcs: ["packagelistparser_test.cpp"],
    shared_libs: [
        "libbase",
        "libpackagelistparser",
    ],
    test_suites: ["device-tests"],
}
+52 −65
Original line number Diff line number Diff line
/*
 * Copyright 2015, Intel Corporation
 * Copyright (C) 2015 The Android Open Source Project
 * 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.
@@ -13,82 +12,70 @@
 * 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>
 *
 * This is a parser library for parsing the packages.list file generated
 * by PackageManager service.
 *
 * This simple parser is sensitive to format changes in
 * frameworks/base/services/core/java/com/android/server/pm/Settings.java
 * A dependency note has been added to that file to correct
 * this parser.
 */

#ifndef PACKAGELISTPARSER_H_
#define PACKAGELISTPARSER_H_
#pragma once

#include <stdbool.h>
#include <sys/cdefs.h>
#include <sys/types.h>

__BEGIN_DECLS

/** The file containing the list of installed packages on the system */
#define PACKAGES_LIST_FILE  "/data/system/packages.list"

typedef struct pkg_info pkg_info;
typedef struct gid_list gid_list;

struct gid_list {
typedef struct gid_list {
  /** Number of gids. */
  size_t cnt;

  /** Array of gids. */
  gid_t* gids;
};
} gid_list;

struct pkg_info {
typedef struct pkg_info {
  /** Package name like "com.android.blah". */
  char* name;

  /** Package uid like 10014. */
  uid_t uid;

  /** Package's AndroidManifest.xml debuggable flag. */
  bool debuggable;

  /** Package data directory like "/data/user/0/com.android.blah" */
  char* data_dir;

  /** Package SELinux info. */
  char* seinfo;

  /** Package's list of gids. */
  gid_list gids;

  /** Spare pointer for the caller to stash extra data off. */
  void* private_data;

  /** Package's AndroidManifest.xml profileable flag. */
  bool profileable_from_shell;

  /** Package's AndroidManifest.xml version code. */
  long version_code;
};
} pkg_info;

/**
 * Callback function to be used by packagelist_parse() routine.
 * @param info
 *  The parsed package information
 * @param userdata
 *  The supplied userdata pointer to packagelist_parse()
 * @return
 *  true to keep processing, false to stop.
 * Parses the system's default package list.
 * Invokes `callback` once for each package.
 * The callback owns the `pkg_info*` and should call packagelist_free().
 * The callback should return `false` to exit early or `true` to continue.
 */
typedef bool (*pfn_on_package)(pkg_info *info, void *userdata);
bool packagelist_parse(bool (*callback)(pkg_info* info, void* user_data), void* user_data);

/**
 * Parses the file specified by PACKAGES_LIST_FILE and invokes the callback on
 * each entry found. Once the callback is invoked, ownership of the pkg_info pointer
 * is passed to the callback routine, thus they are required to perform any cleanup
 * desired.
 * @param callback
 *  The callback function called on each parsed line of the packages list.
 * @param userdata
 *  An optional userdata supplied pointer to pass to the callback function.
 * @return
 *  true on success false on failure.
 * Parses the given package list.
 * Invokes `callback` once for each package.
 * The callback owns the `pkg_info*` and should call packagelist_free().
 * The callback should return `false` to exit early or `true` to continue.
 */
extern bool packagelist_parse(pfn_on_package callback, void *userdata);
bool packagelist_parse_file(const char* path, bool (*callback)(pkg_info* info, void* user_data),
                            void* user_data);

/**
 * Frees a pkg_info structure.
 * @param info
 *  The struct to free
 */
extern void packagelist_free(pkg_info *info);
/** Frees the given `pkg_info`. */
void packagelist_free(pkg_info* info);

__END_DECLS

#endif /* PACKAGELISTPARSER_H_ */
+0 −291
Original line number Diff line number Diff line
/*
 * 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);
    }
}
Loading