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