tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / bin / multiuser / useradd.cpp
blob476e6fae55c6e0dacbe59498da6981e6eae05e92
1 /*
2 * Copyright 2008-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
6 #include <errno.h>
7 #include <getopt.h>
8 #include <pwd.h>
9 #include <shadow.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <termios.h>
14 #include <time.h>
15 #include <unistd.h>
17 #include <OS.h>
18 #include <parsedate.h>
20 #include <RegistrarDefs.h>
21 #include <user_group.h>
22 #include <util/KMessage.h>
24 #include <AutoDeleter.h>
26 #include "multiuser_utils.h"
29 extern const char *__progname;
32 static const char* kUsage =
33 "Usage: %s [ <options> ] <user name>\n"
34 "Creates a new user <user name>.\n"
35 "\n"
36 "Options:\n"
37 " -d <home>\n"
38 " Specifies the home directory for the new user.\n"
39 " -e <expiration>\n"
40 " Specifies the expiration date for the new user's account.\n"
41 " -f <inactive>\n"
42 " Specifies the number of days after the expiration of the new user's "
43 "password\n"
44 " until the account expires.\n"
45 " -g <gid>\n"
46 " Specifies the new user's primary group by ID or name.\n"
47 " -h, --help\n"
48 " Print usage info.\n"
49 " -s <shell>\n"
50 " Specifies the new user's login shell.\n"
51 " -n <real name>\n"
52 " Specifies the new user's real name.\n"
55 static void
56 print_usage_and_exit(bool error)
58 fprintf(error ? stderr : stdout, kUsage, __progname);
59 exit(error ? 1 : 0);
63 int
64 main(int argc, const char* const* argv)
66 const char* home = "/boot/home";
67 int expiration = 99999;
68 int inactive = -1;
69 const char* group = NULL;
70 const char* shell = "/bin/sh";
71 const char* realName = "";
73 int min = -1;
74 int max = -1;
75 int warn = 7;
77 while (true) {
78 static struct option sLongOptions[] = {
79 { "help", no_argument, 0, 'h' },
80 { 0, 0, 0, 0 }
83 opterr = 0; // don't print errors
84 int c = getopt_long(argc, (char**)argv, "d:e:f:g:hn:s:", sLongOptions,
85 NULL);
86 if (c == -1)
87 break;
90 switch (c) {
91 case 'd':
92 home = optarg;
93 break;
95 case 'e':
96 expiration = parsedate(optarg, time(NULL)) / (3600 * 24);
97 break;
99 case 'f':
100 inactive = atoi(optarg);
101 break;
103 case 'g':
105 group = optarg;
106 break;
109 case 'h':
110 print_usage_and_exit(false);
111 break;
113 case 'n':
114 realName = optarg;
115 break;
117 case 's':
118 shell = optarg;
119 break;
121 default:
122 print_usage_and_exit(true);
123 break;
127 if (optind != argc - 1)
128 print_usage_and_exit(true);
130 const char* user = argv[optind];
132 if (geteuid() != 0) {
133 fprintf(stderr, "Error: Only root may add users.\n");
134 exit(1);
137 // check, if user already exists
138 if (getpwnam(user) != NULL) {
139 fprintf(stderr, "Error: User \"%s\" already exists.\n", user);
140 exit(1);
143 // get group ID
144 gid_t gid = 100;
145 if (group != NULL) {
146 char* end;
147 gid = strtol(group, &end, 0);
148 if (*end == '\0') {
149 // seems to be a number
150 if (gid < 1) {
151 fprintf(stderr, "Error: Invalid group ID \"%s\".\n",
152 group);
153 exit(1);
155 } else {
156 // must be a group name -- get it
157 char* buffer = NULL;
158 ssize_t bufferSize = sysconf(_SC_GETGR_R_SIZE_MAX);
159 if (bufferSize <= 0)
160 bufferSize = 256;
161 for (;;) {
162 buffer = (char*)realloc(buffer, bufferSize);
163 if (buffer == NULL) {
164 fprintf(stderr, "Error: Out of memory!\n");
165 exit(1);
168 struct group groupBuffer;
169 struct group* groupFound;
170 int error = getgrnam_r(group, &groupBuffer, buffer, bufferSize,
171 &groupFound);
172 if (error == ERANGE) {
173 bufferSize *= 2;
174 continue;
177 if (error != 0) {
178 fprintf(stderr, "Error: Failed to get info for group "
179 "\"%s\".\n", group);
180 exit(1);
182 if (groupFound == NULL) {
183 fprintf(stderr, "Error: Specified group \"%s\" doesn't "
184 "exist.\n", group);
185 exit(1);
188 gid = groupFound->gr_gid;
189 break;
194 // find an unused UID
195 uid_t uid = 1000;
196 while (getpwuid(uid) != NULL)
197 uid++;
199 // prepare request for the registrar
200 KMessage message(BPrivate::B_REG_UPDATE_USER);
201 if (message.AddInt32("uid", uid) != B_OK
202 || message.AddInt32("gid", gid) != B_OK
203 || message.AddString("name", user) != B_OK
204 || message.AddString("password", "x") != B_OK
205 || message.AddString("home", home) != B_OK
206 || message.AddString("shell", shell) != B_OK
207 || message.AddString("real name", realName) != B_OK
208 || message.AddString("shadow password", "!") != B_OK
209 || message.AddInt32("last changed", time(NULL)) != B_OK
210 || message.AddInt32("min", min) != B_OK
211 || message.AddInt32("max", max) != B_OK
212 || message.AddInt32("warn", warn) != B_OK
213 || message.AddInt32("inactive", inactive) != B_OK
214 || message.AddInt32("expiration", expiration) != B_OK
215 || message.AddInt32("flags", 0) != B_OK
216 || message.AddBool("add user", true) != B_OK) {
217 fprintf(stderr, "Error: Out of memory!\n");
218 exit(1);
221 // send the request
222 KMessage reply;
223 status_t error = send_authentication_request_to_registrar(message, reply);
224 if (error != B_OK) {
225 fprintf(stderr, "Error: Failed to create user: %s\n", strerror(error));
226 exit(1);
229 return 0;