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

Commit 1ab8f553 authored by Tom Cherry's avatar Tom Cherry
Browse files

ueventd: convert platform_names from C list to std::vector

Also simplify this code a bit.

There's only one consumer that removes the /devices/platform prefix,
so have them handle it instead of storing two copies of the string.

Remove an unneeded search for '/' in get_character_device_symlinks()
as a / will always be the next character after a parent path, by
nature of FindPlatformDevice().

Test: boot bullhead
Test: init unit tests

Change-Id: I9d0482d137b1342ae7509ae993ff99198be814f0
parent e3e48214
Loading
Loading
Loading
Loading
+41 −93
Original line number Diff line number Diff line
@@ -76,16 +76,8 @@ struct perm_node {
    struct listnode plist;
};

struct platform_node {
    char *name;
    char *path;
    int path_len;
    struct listnode list;
};

static list_declare(sys_perms);
static list_declare(dev_perms);
static list_declare(platform_names);

int add_dev_perms(const char *name, const char *attr,
                  mode_t perm, unsigned int uid, unsigned int gid,
@@ -270,74 +262,25 @@ out:
    }
}

void add_platform_device(const char* path) {
    int path_len = strlen(path);
    struct platform_node *bus;
    const char *name = path;

    if (!strncmp(path, "/devices/", 9)) {
        name += 9;
        if (!strncmp(name, "platform/", 9))
            name += 9;
    }

    LOG(VERBOSE) << "adding platform device " << name << " (" << path << ")";

    bus = (platform_node*) calloc(1, sizeof(struct platform_node));
    bus->path = strdup(path);
    bus->path_len = path_len;
    bus->name = bus->path + (name - path);
    list_add_tail(&platform_names, &bus->list);
}
// TODO: Move this to be a member variable of a future devices class.
std::vector<std::string> platform_devices;

/*
 * given a path that may start with a platform device, find the length of the
 * platform device prefix.  If it doesn't start with a platform device, return
 * 0.
 */
static struct platform_node* find_platform_device(const char* path) {
    int path_len = strlen(path);
    struct listnode *node;
    struct platform_node *bus;

    list_for_each_reverse(node, &platform_names) {
        bus = node_to_item(node, struct platform_node, list);
        if ((bus->path_len < path_len) &&
                (path[bus->path_len] == '/') &&
                !strncmp(path, bus->path, bus->path_len))
            return bus;
    }

    return NULL;
}

void remove_platform_device(const char* path) {
    struct listnode *node;
    struct platform_node *bus;

    list_for_each_reverse(node, &platform_names) {
        bus = node_to_item(node, struct platform_node, list);
        if (!strcmp(path, bus->path)) {
            LOG(INFO) << "removing platform device " << bus->name;
            free(bus->path);
            list_remove(node);
            free(bus);
            return;
        }
    }
// Given a path that may start with a platform device, find the length of the
// platform device prefix.  If it doesn't start with a platform device, return false
bool find_platform_device(const std::string& path, std::string* out_path) {
    out_path->clear();
    // platform_devices is searched backwards, since parents are added before their children,
    // and we want to match as deep of a child as we can.
    for (auto it = platform_devices.rbegin(); it != platform_devices.rend(); ++it) {
        auto platform_device_path_length = it->length();
        if (platform_device_path_length < path.length() &&
            path[platform_device_path_length] == '/' &&
            android::base::StartsWith(path, it->c_str())) {
            *out_path = *it;
            return true;
        }

static void destroy_platform_devices() {
    struct listnode* node;
    struct listnode* n;
    struct platform_node* bus;

    list_for_each_safe(node, n, &platform_names) {
        list_remove(node);
        bus = node_to_item(node, struct platform_node, list);
        free(bus->path);
        free(bus);
    }
    return false;
}

/* Given a path that may start with a PCI device, populate the supplied buffer
@@ -442,16 +385,13 @@ void parse_event(const char* msg, uevent* uevent) {
}

std::vector<std::string> get_character_device_symlinks(uevent* uevent) {
    platform_node* pdev = find_platform_device(uevent->path.c_str());
    if (!pdev) return {};

    /* skip "/devices/platform/<driver>" */
    auto parent_start = uevent->path.find('/', pdev->path_len);
    if (parent_start == std::string::npos) return {};
    std::string parent_device;
    if (!find_platform_device(uevent->path, &parent_device)) return {};

    std::string parent = uevent->path.substr(parent_start);
    // skip path to the parent driver
    std::string path = uevent->path.substr(parent_device.length());

    if (!android::base::StartsWith(parent, "/usb")) return {};
    if (!android::base::StartsWith(path, "/usb")) return {};

    // skip root hub name and device. use device interface
    // skip 3 slashes, including the first / by starting the search at the 1st character, not 0th.
@@ -459,13 +399,13 @@ std::vector<std::string> get_character_device_symlinks(uevent* uevent) {
    // e.g. "/usb/usb_device/name/tty2-1:1.0" -> "name"

    std::string::size_type start = 0;
    start = parent.find('/', start + 1);
    start = path.find('/', start + 1);
    if (start == std::string::npos) return {};

    start = parent.find('/', start + 1);
    start = path.find('/', start + 1);
    if (start == std::string::npos) return {};

    auto end = parent.find('/', start + 1);
    auto end = path.find('/', start + 1);
    if (end == std::string::npos) return {};

    start++;  // Skip the first '/'
@@ -473,7 +413,7 @@ std::vector<std::string> get_character_device_symlinks(uevent* uevent) {
    auto length = end - start;
    if (length == 0) return {};

    auto name_string = parent.substr(start, length);
    auto name_string = path.substr(start, length);

    std::vector<std::string> links;
    links.emplace_back("/dev/usb/" + uevent->subsystem + name_string);
@@ -502,12 +442,19 @@ void sanitize_partition_name(std::string* string) {

std::vector<std::string> get_block_device_symlinks(uevent* uevent) {
    std::string device;
    struct platform_node* pdev;
    std::string type;

    pdev = find_platform_device(uevent->path.c_str());
    if (pdev) {
        device = pdev->name;
    if (find_platform_device(uevent->path, &device)) {
        // Skip /devices/platform or /devices/ if present
        static const std::string devices_platform_prefix = "/devices/platform/";
        static const std::string devices_prefix = "/devices/";

        if (android::base::StartsWith(device, devices_platform_prefix.c_str())) {
            device = device.substr(devices_platform_prefix.length());
        } else if (android::base::StartsWith(device, devices_prefix.c_str())) {
            device = device.substr(devices_prefix.length());
        }

        type = "platform";
    } else if (find_pci_device_prefix(uevent->path, &device)) {
        type = "pci";
@@ -575,11 +522,12 @@ static void handle_device(const std::string& action, const std::string& devpath,
    }
}

static void handle_platform_device_event(uevent* uevent) {
void handle_platform_device_event(uevent* uevent) {
    if (uevent->action == "add") {
        add_platform_device(uevent->path.c_str());
        platform_devices.emplace_back(uevent->path);
    } else if (uevent->action == "remove") {
        remove_platform_device(uevent->path.c_str());
        auto it = std::find(platform_devices.begin(), platform_devices.end(), uevent->path);
        if (it != platform_devices.end()) platform_devices.erase(it);
    }
}

@@ -920,7 +868,7 @@ void device_init(const char* path, coldboot_callback fn) {
}

void device_close() {
    destroy_platform_devices();
    platform_devices.clear();
    device_fd.reset();
    selinux_status_close();
}
+3 −2
Original line number Diff line number Diff line
@@ -59,10 +59,11 @@ extern int add_dev_perms(const char *name, const char *attr,
int get_device_fd();

// Exposed for testing
void add_platform_device(const char* path);
void remove_platform_device(const char* path);
extern std::vector<std::string> platform_devices;
bool find_platform_device(const std::string& path, std::string* out_path);
std::vector<std::string> get_character_device_symlinks(uevent* uevent);
std::vector<std::string> get_block_device_symlinks(uevent* uevent);
void sanitize_partition_name(std::string* string);
void handle_platform_device_event(uevent* uevent);

#endif /* _INIT_DEVICES_H */
+46 −2
Original line number Diff line number Diff line
@@ -22,12 +22,26 @@
#include <android-base/scopeguard.h>
#include <gtest/gtest.h>

void add_platform_device(const std::string& path) {
    uevent uevent = {
        .action = "add", .subsystem = "platform", .path = path,
    };
    handle_platform_device_event(&uevent);
}

void remove_platform_device(const std::string& path) {
    uevent uevent = {
        .action = "remove", .subsystem = "platform", .path = path,
    };
    handle_platform_device_event(&uevent);
}

template <std::vector<std::string> (*Function)(uevent*)>
void test_get_symlinks(const std::string& platform_device_name, uevent* uevent,
                       const std::vector<std::string> expected_links) {
    add_platform_device(platform_device_name.c_str());
    add_platform_device(platform_device_name);
    auto platform_device_remover = android::base::make_scope_guard(
        [&platform_device_name]() { remove_platform_device(platform_device_name.c_str()); });
        [&platform_device_name]() { remove_platform_device(platform_device_name); });

    std::vector<std::string> result = Function(uevent);

@@ -41,6 +55,36 @@ void test_get_symlinks(const std::string& platform_device_name, uevent* uevent,
    }
}

TEST(devices, handle_platform_device_event) {
    platform_devices.clear();
    add_platform_device("/devices/platform/some_device_name");
    ASSERT_EQ(1U, platform_devices.size());
    remove_platform_device("/devices/platform/some_device_name");
    ASSERT_EQ(0U, platform_devices.size());
}

TEST(devices, find_platform_device) {
    platform_devices.clear();
    add_platform_device("/devices/platform/some_device_name");
    add_platform_device("/devices/platform/some_device_name/longer");
    add_platform_device("/devices/platform/other_device_name");
    EXPECT_EQ(3U, platform_devices.size());

    std::string out_path;
    EXPECT_FALSE(find_platform_device("/devices/platform/not_found", &out_path));
    EXPECT_EQ("", out_path);

    EXPECT_FALSE(
        find_platform_device("/devices/platform/some_device_name_with_same_prefix", &out_path));

    EXPECT_TRUE(
        find_platform_device("/devices/platform/some_device_name/longer/longer_child", &out_path));
    EXPECT_EQ("/devices/platform/some_device_name/longer", out_path);

    EXPECT_TRUE(find_platform_device("/devices/platform/some_device_name/other_child", &out_path));
    EXPECT_EQ("/devices/platform/some_device_name", out_path);
}

TEST(devices, get_character_device_symlinks_success) {
    const char* platform_device = "/devices/platform/some_device_name";
    uevent uevent = {