summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorM4x1m3 <M4x1me@protonmail.com>2021-07-09 14:37:48 +0200
committerGunnar Beutner <gunnar@beutner.name>2021-07-15 11:16:58 +0200
commit21cb531be1c3b82c1ec196706c6c2155bb52bb3f (patch)
tree75dc42a40df5795b633ee368672e40e58c2b2efb /Userland
parent927f6ea2211d20b521bf35136c55c0147ce48a4f (diff)
downloadserenity-21cb531be1c3b82c1ec196706c6c2155bb52bb3f.zip
Utilities: Add groupadd
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Utilities/groupadd.cpp92
1 files changed, 92 insertions, 0 deletions
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 <jooster669@gmail.com>
+ * Copyright (c) 2021, Brandon Pruitt <brapru@pm.me>
+ * Copyright (c) 2021, Maxime Friess <M4x1me@pm.me>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibCore/ArgsParser.h>
+#include <ctype.h>
+#include <errno.h>
+#include <grp.h>
+#include <string.h>
+#include <unistd.h>
+
+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<uid_t>(gid))) {
+ warnln("gid {} already exists!", gid);
+ return 4;
+ }
+ } else {
+ for (gid = GROUPS_GID; getgrgid(static_cast<uid_t>(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<char*>(groupname);
+ g.gr_passwd = const_cast<char*>("x");
+ g.gr_gid = static_cast<gid_t>(gid);
+ g.gr_mem = nullptr;
+
+ if (putgrent(&g, grfile) < 0) {
+ perror("putpwent");
+ return 1;
+ }
+
+ fclose(grfile);
+
+ return 0;
+}