diff options
author | Kenneth Myhra <kennethmyhra@gmail.com> | 2022-01-02 22:41:29 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-01-16 11:19:07 +0100 |
commit | a99b50ce8c790e0ba394ef7ed580bfc414f4af4c (patch) | |
tree | 6f369a3e67827d4e549e4967804bbbdafa45da83 /Userland/Libraries/LibCore | |
parent | adfdb63e0207afdb43a9d7e505502ee544ce7ab5 (diff) | |
download | serenity-a99b50ce8c790e0ba394ef7ed580bfc414f4af4c.zip |
LibCore: Add Core::Group abstraction for group management :^)
This adds the Core::Group C++ abstraction to ease interaction with the
group entry database, as well as represent the Group entry.
Core::Group abstraction currently contains the following functionality:
- Add a group entry - 'Core::Group::add_group()'
Diffstat (limited to 'Userland/Libraries/LibCore')
-rw-r--r-- | Userland/Libraries/LibCore/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibCore/Group.cpp | 103 | ||||
-rw-r--r-- | Userland/Libraries/LibCore/Group.h | 45 |
3 files changed, 149 insertions, 0 deletions
diff --git a/Userland/Libraries/LibCore/CMakeLists.txt b/Userland/Libraries/LibCore/CMakeLists.txt index a7eb071d8a..c3eecb178d 100644 --- a/Userland/Libraries/LibCore/CMakeLists.txt +++ b/Userland/Libraries/LibCore/CMakeLists.txt @@ -13,6 +13,7 @@ set(SOURCES File.cpp FilePermissionsMask.cpp GetPassword.cpp + Group.cpp IODevice.cpp LocalServer.cpp LocalSocket.cpp diff --git a/Userland/Libraries/LibCore/Group.cpp b/Userland/Libraries/LibCore/Group.cpp new file mode 100644 index 0000000000..586dca33e1 --- /dev/null +++ b/Userland/Libraries/LibCore/Group.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2022, Kenneth Myhra <kennethmyhra@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <AK/CharacterTypes.h> +#include <LibCore/Group.h> +#include <LibCore/System.h> + +namespace Core { + +#ifndef AK_OS_MACOS +ErrorOr<void> Group::add_group(Group& group) +{ + if (group.name().is_empty()) + return Error::from_string_literal("Group name can not be empty."); + + // A quick sanity check on group name + if (strpbrk(group.name().characters(), "\\/!@#$%^&*()~+=`:\n")) + return Error::from_string_literal("Group name has invalid characters."); + + // Disallow names starting with '_', '-' or other non-alpha characters. + if (group.name().starts_with('_') || group.name().starts_with('-') || !is_ascii_alpha(group.name().characters()[0])) + return Error::from_string_literal("Group name has invalid characters."); + + // Verify group name does not already exist + if (TRY(name_exists(group.name()))) + return Error::from_string_literal("Group name already exists."); + + // Sort out the group id for the group + if (group.id() > 0) { + if (TRY(id_exists(group.id()))) + return Error::from_string_literal("Group ID already exists."); + } else { + gid_t group_id = 100; + while (true) { + if (!TRY(id_exists(group_id))) + break; + group_id++; + } + group.set_group_id(group_id); + } + + auto gr = TRY(group.to_libc_group()); + + FILE* file = fopen("/etc/group", "a"); + if (!file) + return Error::from_errno(errno); + + if (putgrent(&gr, file) < 0) + return Error::from_errno(errno); + + fclose(file); + + return {}; +} +#endif + +Group::Group(String name, gid_t id, Vector<String> members) + : m_name(move(name)) + , m_id(id) + , m_members(move(members)) +{ +} + +ErrorOr<bool> Group::name_exists(StringView name) +{ + return TRY(Core::System::getgrnam(name)).has_value(); +} + +ErrorOr<bool> Group::id_exists(gid_t id) +{ + return TRY(Core::System::getgrgid(id)).has_value(); +} + +// NOTE: struct group returned from this function cannot outlive an instance of Group. +ErrorOr<struct group> Group::to_libc_group() +{ + struct group gr; + gr.gr_name = const_cast<char*>(m_name.characters()); + gr.gr_passwd = const_cast<char*>("x"); + gr.gr_gid = m_id; + gr.gr_mem = nullptr; + + // FIXME: A better solution would surely be not using a static here + // NOTE: This now means that there cannot be multiple struct groups at the same time, because only one gr.gr_mem can ever be valid at the same time. + // NOTE: Not using a static here would result in gr.gr_mem being freed up on exit from this function. + static Vector<char*> members; + members.clear_with_capacity(); + if (m_members.size() > 0) { + TRY(members.try_ensure_capacity(m_members.size() + 1)); + for (auto member : m_members) + members.unchecked_append(const_cast<char*>(member.characters())); + members.unchecked_append(nullptr); + + gr.gr_mem = const_cast<char**>(members.data()); + } + + return gr; +} + +} diff --git a/Userland/Libraries/LibCore/Group.h b/Userland/Libraries/LibCore/Group.h new file mode 100644 index 0000000000..8cd4602984 --- /dev/null +++ b/Userland/Libraries/LibCore/Group.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022, Kenneth Myhra <kennethmyhra@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/Error.h> +#include <AK/String.h> +#include <AK/Vector.h> +#include <grp.h> + +namespace Core { + +class Group { +public: +#ifndef AK_OS_MACOS + static ErrorOr<void> add_group(Group& group); +#endif + + Group() = default; + Group(String name, gid_t id = 0, Vector<String> members = {}); + + ~Group() = default; + + String const& name() const { return m_name; } + void set_name(String const& name) { m_name = name; } + + gid_t id() const { return m_id; } + void set_group_id(gid_t const id) { m_id = id; } + + Vector<String>& members() { return m_members; } + +private: + static ErrorOr<bool> name_exists(StringView name); + static ErrorOr<bool> id_exists(gid_t id); + ErrorOr<struct group> to_libc_group(); + + String m_name; + gid_t m_id { 0 }; + Vector<String> m_members; +}; + +} |