1519bb60aSIngo Weinhold/*
2519bb60aSIngo Weinhold * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3519bb60aSIngo Weinhold * Distributed under the terms of the MIT License.
4519bb60aSIngo Weinhold */
5519bb60aSIngo Weinhold
6519bb60aSIngo Weinhold#include <errno.h>
7519bb60aSIngo Weinhold#include <getopt.h>
8519bb60aSIngo Weinhold#include <grp.h>
9519bb60aSIngo Weinhold#include <stdio.h>
10519bb60aSIngo Weinhold#include <stdlib.h>
11519bb60aSIngo Weinhold#include <string.h>
12519bb60aSIngo Weinhold#include <unistd.h>
13519bb60aSIngo Weinhold
14519bb60aSIngo Weinhold#include <set>
15519bb60aSIngo Weinhold#include <string>
16519bb60aSIngo Weinhold
17519bb60aSIngo Weinhold#include <OS.h>
18519bb60aSIngo Weinhold
19519bb60aSIngo Weinhold#include <RegistrarDefs.h>
20519bb60aSIngo Weinhold#include <user_group.h>
21519bb60aSIngo Weinhold#include <util/KMessage.h>
22519bb60aSIngo Weinhold
23519bb60aSIngo Weinhold#include "multiuser_utils.h"
24519bb60aSIngo Weinhold
25519bb60aSIngo Weinhold
26519bb60aSIngo Weinholdextern const char *__progname;
27519bb60aSIngo Weinhold
28519bb60aSIngo Weinhold
29519bb60aSIngo Weinholdstatic const char* kUsage =
30519bb60aSIngo Weinhold	"Usage: %s [ <options> ] <group name>\n"
31519bb60aSIngo Weinhold	"Creates a new group <group name>.\n"
32519bb60aSIngo Weinhold	"\n"
33519bb60aSIngo Weinhold	"Options:\n"
34519bb60aSIngo Weinhold	"  -A, --add-user <user>\n"
35519bb60aSIngo Weinhold	"    Add the user <user> to the group.\n"
36519bb60aSIngo Weinhold	"  -h, --help\n"
37519bb60aSIngo Weinhold	"    Print usage info.\n"
38519bb60aSIngo Weinhold	"  -R, --remove-user <user>\n"
39519bb60aSIngo Weinhold	"    Remove the user <user> from the group.\n"
40519bb60aSIngo Weinhold	;
41519bb60aSIngo Weinhold
42519bb60aSIngo Weinholdstatic void
43519bb60aSIngo Weinholdprint_usage_and_exit(bool error)
44519bb60aSIngo Weinhold{
45519bb60aSIngo Weinhold	fprintf(error ? stderr : stdout, kUsage, __progname);
46519bb60aSIngo Weinhold	exit(error ? 1 : 0);
47519bb60aSIngo Weinhold}
48519bb60aSIngo Weinhold
49519bb60aSIngo Weinhold
50519bb60aSIngo Weinholdint
51519bb60aSIngo Weinholdmain(int argc, const char* const* argv)
52519bb60aSIngo Weinhold{
53519bb60aSIngo Weinhold	typedef std::set<std::string> StringSet;
54519bb60aSIngo Weinhold
55519bb60aSIngo Weinhold	StringSet usersToAdd;
56519bb60aSIngo Weinhold	StringSet usersToRemove;
57519bb60aSIngo Weinhold
58519bb60aSIngo Weinhold	while (true) {
59519bb60aSIngo Weinhold		static struct option sLongOptions[] = {
60519bb60aSIngo Weinhold			{ "add-user", required_argument, 0, 'A' },
61519bb60aSIngo Weinhold			{ "help", no_argument, 0, 'h' },
62519bb60aSIngo Weinhold			{ "remove-user", required_argument, 0, 'A' },
63519bb60aSIngo Weinhold			{ 0, 0, 0, 0 }
64519bb60aSIngo Weinhold		};
65519bb60aSIngo Weinhold
66519bb60aSIngo Weinhold		opterr = 0; // don't print errors
67519bb60aSIngo Weinhold		int c = getopt_long(argc, (char**)argv, "A:hR:", sLongOptions, NULL);
68519bb60aSIngo Weinhold		if (c == -1)
69519bb60aSIngo Weinhold			break;
70519bb60aSIngo Weinhold
71519bb60aSIngo Weinhold
72519bb60aSIngo Weinhold		switch (c) {
73519bb60aSIngo Weinhold			case 'A':
74519bb60aSIngo Weinhold				usersToAdd.insert(optarg);
75519bb60aSIngo Weinhold				break;
76519bb60aSIngo Weinhold
77519bb60aSIngo Weinhold			case 'h':
78519bb60aSIngo Weinhold				print_usage_and_exit(false);
79519bb60aSIngo Weinhold				break;
80519bb60aSIngo Weinhold
81519bb60aSIngo Weinhold			case 'R':
82519bb60aSIngo Weinhold				usersToRemove.insert(optarg);
83519bb60aSIngo Weinhold				break;
84519bb60aSIngo Weinhold
85519bb60aSIngo Weinhold			default:
86519bb60aSIngo Weinhold				print_usage_and_exit(true);
87519bb60aSIngo Weinhold				break;
88519bb60aSIngo Weinhold		}
89519bb60aSIngo Weinhold	}
90519bb60aSIngo Weinhold
91519bb60aSIngo Weinhold	if (optind != argc - 1)
92519bb60aSIngo Weinhold		print_usage_and_exit(true);
93519bb60aSIngo Weinhold
94519bb60aSIngo Weinhold	const char* group = argv[optind];
95519bb60aSIngo Weinhold
96519bb60aSIngo Weinhold	if (geteuid() != 0) {
97519bb60aSIngo Weinhold		fprintf(stderr, "Error: Only root may modify groups.\n");
98519bb60aSIngo Weinhold		exit(1);
99519bb60aSIngo Weinhold	}
100519bb60aSIngo Weinhold
101519bb60aSIngo Weinhold	// get the group
102519bb60aSIngo Weinhold	struct group* groupInfo = getgrnam(group);
103519bb60aSIngo Weinhold	if (groupInfo == NULL) {
104519bb60aSIngo Weinhold		fprintf(stderr, "Error: Group \"%s\" doesn't exist.\n", group);
105519bb60aSIngo Weinhold		exit(1);
106519bb60aSIngo Weinhold	}
107519bb60aSIngo Weinhold
108519bb60aSIngo Weinhold	// check, if anything needs to be done
109519bb60aSIngo Weinhold	if (usersToAdd.empty() && usersToRemove.empty()) {
110519bb60aSIngo Weinhold		fprintf(stderr, "Error: No modification specified.\n");
111519bb60aSIngo Weinhold		exit(1);
112519bb60aSIngo Weinhold	}
113519bb60aSIngo Weinhold
114519bb60aSIngo Weinhold	// prepare request for the registrar
115519bb60aSIngo Weinhold	KMessage message(BPrivate::B_REG_UPDATE_GROUP);
116519bb60aSIngo Weinhold	if (message.AddInt32("gid", groupInfo->gr_gid) != B_OK
117519bb60aSIngo Weinhold		|| message.AddString("name", group) != B_OK
118519bb60aSIngo Weinhold		|| message.AddString("password", groupInfo->gr_passwd) != B_OK
119519bb60aSIngo Weinhold		|| message.AddBool("add group", false) != B_OK) {
120519bb60aSIngo Weinhold		fprintf(stderr, "Error: Out of memory!\n");
121519bb60aSIngo Weinhold		exit(1);
122519bb60aSIngo Weinhold	}
123519bb60aSIngo Weinhold
124519bb60aSIngo Weinhold	for (int32 i = 0; const char* user = groupInfo->gr_mem[i]; i++) {
125519bb60aSIngo Weinhold		if (usersToRemove.erase(user) > 0)
126519bb60aSIngo Weinhold			continue;
127519bb60aSIngo Weinhold
128519bb60aSIngo Weinhold		usersToAdd.insert(user);
129519bb60aSIngo Weinhold	}
130519bb60aSIngo Weinhold
131519bb60aSIngo Weinhold	if (!usersToRemove.empty()) {
132519bb60aSIngo Weinhold		fprintf(stderr, "Error: \"%s\" is not a member of group \"%s\"\n",
133519bb60aSIngo Weinhold			usersToRemove.begin()->c_str(), group);
134519bb60aSIngo Weinhold		exit(1);
135519bb60aSIngo Weinhold	}
136519bb60aSIngo Weinhold
137519bb60aSIngo Weinhold	// If the group doesn't have any more members, insert an empty string as an
138519bb60aSIngo Weinhold	// indicator for the registrar to remove all members.
139519bb60aSIngo Weinhold	if (usersToAdd.empty())
140519bb60aSIngo Weinhold		usersToAdd.insert("");
141519bb60aSIngo Weinhold
142519bb60aSIngo Weinhold	for (StringSet::const_iterator it = usersToAdd.begin();
143519bb60aSIngo Weinhold		it != usersToAdd.end(); ++it) {
144519bb60aSIngo Weinhold		if (message.AddString("members", it->c_str()) != B_OK) {
145519bb60aSIngo Weinhold			fprintf(stderr, "Error: Out of memory!\n");
146519bb60aSIngo Weinhold			exit(1);
147519bb60aSIngo Weinhold		}
148519bb60aSIngo Weinhold	}
149519bb60aSIngo Weinhold
150519bb60aSIngo Weinhold	// send the request
151519bb60aSIngo Weinhold	KMessage reply;
152519bb60aSIngo Weinhold	status_t error = send_authentication_request_to_registrar(message, reply);
153519bb60aSIngo Weinhold	if (error != B_OK) {
154519bb60aSIngo Weinhold		fprintf(stderr, "Error: Failed to create group: %s\n", strerror(error));
155519bb60aSIngo Weinhold		exit(1);
156519bb60aSIngo Weinhold	}
157519bb60aSIngo Weinhold
158519bb60aSIngo Weinhold	return 0;
159519bb60aSIngo Weinhold}