diff --git a/Base/usr/share/man/man8/groupadd.md b/Base/usr/share/man/man8/groupadd.md new file mode 100644 index 0000000000..6a7c781b15 --- /dev/null +++ b/Base/usr/share/man/man8/groupadd.md @@ -0,0 +1,38 @@ +## Name + +groupadd - add a new group to the system group file + +## Synopsis + +```**sh +# groupadd [options] +``` + +## Description + +This program adds a new group to the system. + +This program must be run as root. + +## Options + +* `-g`, `--gid` _gid_: The group identifier for the new group. If not specified, an unused GID above `100` will be auto-generated. + +## Exit Values + +* 0 - Success +* 1 - Couldn't update the group file +* 3 - Invalid argument to option +* 4 - GID already in use + +## Files + +* `/etc/group` - new group information (such GID) is appended to this file. + +## Examples + +```sh +# groupadd -g 110 contributors +# groupadd mainteners +``` + diff --git a/Userland/Utilities/groupadd.cpp b/Userland/Utilities/groupadd.cpp new file mode 100644 index 0000000000..3c8eab8047 --- /dev/null +++ b/Userland/Utilities/groupadd.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019-2020, Jesse Buhagiar + * Copyright (c) 2021, Brandon Pruitt + * Copyright (c) 2021, Maxime Friess + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +constexpr gid_t GROUPS_GID = 100; + +int main(int argc, char** argv) +{ + if (pledge("stdio wpath rpath cpath chown", nullptr) < 0) { + perror("pledge"); + return 1; + } + + int gid = 0; + char const* groupname = nullptr; + + Core::ArgsParser args_parser; + args_parser.add_option(gid, "Group ID (gid) for the new group", "gid", 'g', "gid"); + args_parser.add_positional_argument(groupname, "Name of the group (groupname)", "group"); + + args_parser.parse(argc, argv); + + // Let's run a quick sanity check on groupname + if (strpbrk(groupname, "\\/!@#$%^&*()~+=`:\n")) { + warnln("invalid character in groupname, {}", groupname); + return 1; + } + + // Disallow names starting with _ and - + if (groupname[0] == '_' || groupname[0] == '-' || !isalpha(groupname[0])) { + warnln("invalid groupname, {}", groupname); + return 1; + } + + if (getgrnam(groupname)) { + warnln("Group {} already exists!", groupname); + return 1; + } + + if (gid < 0) { + warnln("invalid gid {}!", gid); + return 3; + } + + // First, let's sort out the gid for the group + if (gid > 0) { + if (getgrgid(static_cast(gid))) { + warnln("gid {} already exists!", gid); + return 4; + } + } else { + for (gid = GROUPS_GID; getgrgid(static_cast(gid)); gid++) { + } + } + + if (gid < 0) { + warnln("invalid gid {}", gid); + return 3; + } + + FILE* grfile = fopen("/etc/group", "a"); + if (!grfile) { + perror("failed to open /etc/group"); + return 1; + } + + struct group g; + g.gr_name = const_cast(groupname); + g.gr_passwd = const_cast("x"); + g.gr_gid = static_cast(gid); + g.gr_mem = nullptr; + + if (putgrent(&g, grfile) < 0) { + perror("putpwent"); + return 1; + } + + fclose(grfile); + + return 0; +}