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