xref: /freebsd/usr.sbin/pw/pw.c (revision 1b56bb4ca7afcf0f76feac28ddb97f5b78923913)
1 /*-
2  * Copyright (C) 1996
3  *	David L. Nugent.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *	$Id: pw.c,v 1.1.1.2 1996/12/09 23:55:20 joerg Exp $
27  */
28 
29 #include "pw.h"
30 
31 static char    *progname = "pw";
32 
33 const char     *Modes[] = {"add", "del", "mod", "show", "next", NULL};
34 const char     *Which[] = {"user", "group", NULL};
35 static const char *Combo1[] = {
36   "useradd", "userdel", "usermod", "usershow", "usernext",
37   "groupadd", "groupdel", "groupmod", "groupshow", "groupnext",
38   NULL};
39 static const char *Combo2[] = {
40   "adduser", "deluser", "moduser", "showuser", "nextuser",
41   "addgroup", "delgroup", "modgroup", "showgroup", "nextgroup",
42 NULL};
43 
44 static struct cargs arglist;
45 
46 static int      getindex(const char *words[], const char *word);
47 static void     cmdhelp(int mode, int which);
48 
49 
50 int
51 main(int argc, char *argv[])
52 {
53 	int             ch;
54 	int             mode = -1;
55 	int             which = -1;
56 	struct userconf *cnf;
57 
58 	static const char *opts[W_NUM][M_NUM] =
59 	{
60 		{ /* user */
61 			"C:qn:u:c:d:e:p:g:G:mk:s:oL:i:w:h:Db:NP",
62 			"C:qn:u:r",
63 			"C:qn:u:c:d:e:p:g:G:mk:s:w:L:h:FNP",
64 			"C:qn:u:FPa",
65 			"C:q"
66 		},
67 		{ /* grp  */
68 			"C:qn:g:h:M:pNP",
69 			"C:qn:g:",
70 			"C:qn:g:l:h:FM:m:NP",
71 			"C:qn:g:FPa",
72 			"C:q"
73 		 }
74 	};
75 
76 	static int      (*funcs[W_NUM]) (struct userconf * _cnf, int _mode, struct cargs * _args) =
77 	{			/* Request handlers */
78 		pw_user,
79 		pw_group
80 	};
81 
82 	umask(0);		/* We wish to handle this manually */
83 	progname = strrchr(argv[0], '/');
84 	if (progname != NULL)
85 		++progname;
86 	else
87 		progname = argv[0];
88 
89 	LIST_INIT(&arglist);
90 
91 	/*
92 	 * Break off the first couple of words to determine what exactly
93 	 * we're being asked to do
94 	 */
95 	while (argc > 1 && *argv[1] != '-') {
96 		int             tmp;
97 
98 		if ((tmp = getindex(Modes, argv[1])) != -1)
99 			mode = tmp;
100 		else if ((tmp = getindex(Which, argv[1])) != -1)
101 			which = tmp;
102 		else if ((tmp = getindex(Combo1, argv[1])) != -1 || (tmp = getindex(Combo2, argv[1])) != -1) {
103 			which = tmp / M_NUM;
104 			mode = tmp % M_NUM;
105 		} else if (strcmp(argv[1], "help") == 0)
106 			cmdhelp(mode, which);
107 		else if (which != -1 && mode != -1 && arglist.lh_first == NULL)
108 			addarg(&arglist, 'n', argv[1]);
109 		else
110 			cmderr(EX_USAGE, "Unknown keyword `%s'\n", argv[1]);
111 		++argv;
112 		--argc;
113 	}
114 
115 	/*
116 	 * Bail out unless the user is specific!
117 	 */
118 	if (mode == -1 || which == -1)
119 		cmdhelp(mode, which);
120 
121 	/*
122 	 * We know which mode we're in and what we're about to do, so now
123 	 * let's dispatch the remaining command line args in a genric way.
124 	 */
125 	argv[0] = progname;	/* Preserve this */
126 	optarg = NULL;
127 
128 	while ((ch = getopt(argc, argv, opts[which][mode])) != -1) {
129 		if (ch == '?')
130 			cmderr(EX_USAGE, NULL);
131 		else
132 			addarg(&arglist, ch, optarg);
133 		optarg = NULL;
134 	}
135 
136 	/*
137 	 * Must be root to attempt an update
138 	 */
139 	if (getuid() != 0 && mode != M_PRINT && mode != M_NEXT && getarg(&arglist, 'N')==NULL)
140 		cmderr(EX_NOPERM, "you must be root to run this program\n");
141 
142 	/*
143 	 * We should immediately look for the -q 'quiet' switch so that we
144 	 * don't bother with extraneous errors
145 	 */
146 	if (getarg(&arglist, 'q') != NULL)
147 		freopen("/dev/null", "w", stderr);
148 
149 	/*
150 	 * Now, let's do the common initialisation
151 	 */
152 	cnf = read_userconfig(getarg(&arglist, 'C') ? getarg(&arglist, 'C')->val : NULL);
153 	return funcs[which] (cnf, mode, &arglist);
154 }
155 
156 static int
157 getindex(const char *words[], const char *word)
158 {
159 	int             i = 0;
160 
161 	while (words[i]) {
162 		if (strcmp(words[i], word) == 0)
163 			return i;
164 		i++;
165 	}
166 	return -1;
167 }
168 
169 
170 /*
171  * This is probably an overkill for a cmdline help system, but it reflects
172  * the complexity of the command line.
173  */
174 
175 static void
176 banner(void)
177 {
178 	fprintf(stderr, "%s: ", progname);
179 }
180 
181 void
182 cmderr(int ec, char const * fmt,...)
183 {
184 	if (fmt != NULL) {
185 		va_list         argp;
186 
187 		banner();
188 		va_start(argp, fmt);
189 		vfprintf(stderr, fmt, argp);
190 		va_end(argp);
191 	}
192 	exit(ec);
193 }
194 
195 static void
196 cmdhelp(int mode, int which)
197 {
198 	banner();
199 	if (which == -1)
200 		fprintf(stderr, "usage: %s [user|group] [add|del|mod|show|next] [ help | switches/values ]\n", progname);
201 	else if (mode == -1)
202 		fprintf(stderr, "usage: %s %s [add|del|mod|show|next] [ help | switches/values ]\n", progname, Which[which]);
203 	else {
204 
205 		/*
206 		 * We need to give mode specific help
207 		 */
208 		static const char *help[W_NUM][M_NUM] =
209 		{
210 			{
211 				"usage: %s useradd [name] [switches]\n"
212 				"\t-C config      configuration file\n"
213 				"\t-q             quiet operation\n"
214 				"  Adding users:\n"
215 				"\t-n name        login name\n"
216 				"\t-u uid         user id\n"
217 				"\t-c comment     user name/comment\n"
218 				"\t-d directory   home directory\n"
219 				"\t-e date        account expiry date\n"
220 				"\t-p date        password expiry date\n"
221 				"\t-g grp         initial group\n"
222 				"\t-G grp1,grp2   additional groups\n"
223 				"\t-m [ -k dir ]  create and set up home\n"
224 				"\t-s shell       name of login shell\n"
225 				"\t-o             duplicate uid ok\n"
226 				"\t-L class       user class\n"
227 				"\t-h fd          read password on fd\n"
228 				"\t-N             no update\n"
229 				"  Setting defaults:\n"
230 				"\t-D             set user defaults\n"
231 				"\t-b dir         default home root dir\n"
232 				"\t-e period      default expiry period\n"
233 				"\t-p period      default password change period\n"
234 				"\t-g group       default group\n"
235 				"\t-G grp1,grp2   additional groups\n"
236 				"\t-L class       default user class\n"
237 				"\t-k dir         default home skeleton\n"
238 				"\t-u min,max     set min,max uids\n"
239 				"\t-i min,max     set min,max gids\n"
240 				"\t-w method      set default password method\n"
241 				"\t-s shell       default shell\n",
242 				"usage: %s userdel [uid|name] [switches]\n"
243 				"\t-n name        login name\n"
244 				"\t-u uid         user id\n"
245 				"\t-r             remove home & contents\n",
246 				"usage: %s usermod [uid|name] [switches]\n"
247 				"\t-C config      configuration file\n"
248 				"\t-q             quiet operation\n"
249 				"\t-F             force add if no user\n"
250 				"\t-n name        login name\n"
251 				"\t-u uid         user id\n"
252 				"\t-c comment     user name/comment\n"
253 				"\t-d directory   home directory\n"
254 				"\t-e date        account expiry date\n"
255 				"\t-p date        password expiry date\n"
256 				"\t-g grp         initial group\n"
257 				"\t-G grp1,grp2   additional groups\n"
258 				"\t-l name        new login name\n"
259 				"\t-L class       user class\n"
260 				"\t-m [ -k dir ]  create and set up home\n"
261 				"\t-s shell       name of login shell\n"
262 				"\t-w method      set new password using method\n"
263 				"\t-h fd          read password on fd\n"
264 				"\t-N             no update\n",
265 				"usage: %s usershow [uid|name] [switches]\n"
266 				"\t-n name        login name\n"
267 				"\t-u uid         user id\n"
268 				"\t-F             force print\n"
269 				"\t-P             prettier format\n"
270 				"\t-a             print all users\n",
271 				"usage: %s usernext [switches]\n"
272 				"\t-C config      configuration file\n"
273 			},
274 			{
275 				"usage: %s groupadd [group|gid] [switches]\n"
276 				"\t-C config      configuration file\n"
277 				"\t-q             quiet operation\n"
278 				"\t-n group       group name\n"
279 				"\t-g gid         group id\n"
280 				"\t-M usr1,usr2   add users as group members\n"
281 				"\t-o             duplicate gid ok\n"
282 				"\t-N             no update\n",
283 				"usage: %s groupdel [group|gid] [switches]\n"
284 				"\t-n name        group name\n"
285 				"\t-g gid         group id\n",
286 				"usage: %s groupmod [group|gid] [switches]\n"
287 				"\t-C config      configuration file\n"
288 				"\t-q             quiet operation\n"
289 				"\t-F             force add if not exists\n"
290 				"\t-n name        group name\n"
291 				"\t-g gid         group id\n"
292 				"\t-M usr1,usr2   replaces users as group members\n"
293 				"\t-m usr1,usr2   add users as group members\n"
294 				"\t-l name        new group name\n"
295 				"\t-N             no update\n",
296 				"usage: %s groupshow [group|gid] [switches]\n"
297 				"\t-n name        group name\n"
298 				"\t-g gid         group id\n"
299 				"\t-F             force print\n"
300 				"\t-P             prettier format\n"
301 				"\t-a             print all accounting groups\n",
302 				"usage: %s groupnext [switches]\n"
303 				"\t-C config      configuration file\n"
304 			}
305 		};
306 
307 		fprintf(stderr, help[which][mode], progname);
308 	}
309 	exit(EXIT_FAILURE);
310 }
311 
312 struct carg    *
313 getarg(struct cargs * _args, int ch)
314 {
315 	struct carg    *c = _args->lh_first;
316 
317 	while (c != NULL && c->ch != ch)
318 		c = c->list.le_next;
319 	return c;
320 }
321 
322 struct carg    *
323 addarg(struct cargs * _args, int ch, char *argstr)
324 {
325 	struct carg    *ca = malloc(sizeof(struct carg));
326 
327 	if (ca == NULL)
328 		cmderr(EX_OSERR, "Abort - out of memory\n");
329 	ca->ch = ch;
330 	ca->val = argstr;
331 	LIST_INSERT_HEAD(_args, ca, list);
332 	return ca;
333 }
334