1/*
2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include <errno.h>
7#include <getopt.h>
8#include <grp.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <unistd.h>
13
14#include <set>
15#include <string>
16
17#include <OS.h>
18
19#include <RegistrarDefs.h>
20#include <user_group.h>
21#include <util/KMessage.h>
22
23#include "multiuser_utils.h"
24
25
26extern const char *__progname;
27
28
29static const char* kUsage =
30	"Usage: %s [ <options> ] <group name>\n"
31	"Creates a new group <group name>.\n"
32	"\n"
33	"Options:\n"
34	"  -A, --add-user <user>\n"
35	"    Add the user <user> to the group.\n"
36	"  -h, --help\n"
37	"    Print usage info.\n"
38	"  -R, --remove-user <user>\n"
39	"    Remove the user <user> from the group.\n"
40	;
41
42static void
43print_usage_and_exit(bool error)
44{
45	fprintf(error ? stderr : stdout, kUsage, __progname);
46	exit(error ? 1 : 0);
47}
48
49
50int
51main(int argc, const char* const* argv)
52{
53	typedef std::set<std::string> StringSet;
54
55	StringSet usersToAdd;
56	StringSet usersToRemove;
57
58	while (true) {
59		static struct option sLongOptions[] = {
60			{ "add-user", required_argument, 0, 'A' },
61			{ "help", no_argument, 0, 'h' },
62			{ "remove-user", required_argument, 0, 'A' },
63			{ 0, 0, 0, 0 }
64		};
65
66		opterr = 0; // don't print errors
67		int c = getopt_long(argc, (char**)argv, "A:hR:", sLongOptions, NULL);
68		if (c == -1)
69			break;
70
71
72		switch (c) {
73			case 'A':
74				usersToAdd.insert(optarg);
75				break;
76
77			case 'h':
78				print_usage_and_exit(false);
79				break;
80
81			case 'R':
82				usersToRemove.insert(optarg);
83				break;
84
85			default:
86				print_usage_and_exit(true);
87				break;
88		}
89	}
90
91	if (optind != argc - 1)
92		print_usage_and_exit(true);
93
94	const char* group = argv[optind];
95
96	if (geteuid() != 0) {
97		fprintf(stderr, "Error: Only root may modify groups.\n");
98		exit(1);
99	}
100
101	// get the group
102	struct group* groupInfo = getgrnam(group);
103	if (groupInfo == NULL) {
104		fprintf(stderr, "Error: Group \"%s\" doesn't exist.\n", group);
105		exit(1);
106	}
107
108	// check, if anything needs to be done
109	if (usersToAdd.empty() && usersToRemove.empty()) {
110		fprintf(stderr, "Error: No modification specified.\n");
111		exit(1);
112	}
113
114	// prepare request for the registrar
115	KMessage message(BPrivate::B_REG_UPDATE_GROUP);
116	if (message.AddInt32("gid", groupInfo->gr_gid) != B_OK
117		|| message.AddString("name", group) != B_OK
118		|| message.AddString("password", groupInfo->gr_passwd) != B_OK
119		|| message.AddBool("add group", false) != B_OK) {
120		fprintf(stderr, "Error: Out of memory!\n");
121		exit(1);
122	}
123
124	for (int32 i = 0; const char* user = groupInfo->gr_mem[i]; i++) {
125		if (usersToRemove.erase(user) > 0)
126			continue;
127
128		usersToAdd.insert(user);
129	}
130
131	if (!usersToRemove.empty()) {
132		fprintf(stderr, "Error: \"%s\" is not a member of group \"%s\"\n",
133			usersToRemove.begin()->c_str(), group);
134		exit(1);
135	}
136
137	// If the group doesn't have any more members, insert an empty string as an
138	// indicator for the registrar to remove all members.
139	if (usersToAdd.empty())
140		usersToAdd.insert("");
141
142	for (StringSet::const_iterator it = usersToAdd.begin();
143		it != usersToAdd.end(); ++it) {
144		if (message.AddString("members", it->c_str()) != B_OK) {
145			fprintf(stderr, "Error: Out of memory!\n");
146			exit(1);
147		}
148	}
149
150	// send the request
151	KMessage reply;
152	status_t error = send_authentication_request_to_registrar(message, reply);
153	if (error != B_OK) {
154		fprintf(stderr, "Error: Failed to create group: %s\n", strerror(error));
155		exit(1);
156	}
157
158	return 0;
159}
160