xref: /freebsd/usr.sbin/pw/pw.c (revision bbe09b2e1933b78f507c6173e0960c045c0aae69)
1d6f907dcSJoerg Wunsch /*-
2ad7cf975SJoerg Wunsch  * Copyright (C) 1996
3ad7cf975SJoerg Wunsch  *	David L. Nugent.  All rights reserved.
4d6f907dcSJoerg Wunsch  *
5d6f907dcSJoerg Wunsch  * Redistribution and use in source and binary forms, with or without
6d6f907dcSJoerg Wunsch  * modification, are permitted provided that the following conditions
7d6f907dcSJoerg Wunsch  * are met:
8d6f907dcSJoerg Wunsch  * 1. Redistributions of source code must retain the above copyright
9ad7cf975SJoerg Wunsch  *    notice, this list of conditions and the following disclaimer.
10d6f907dcSJoerg Wunsch  * 2. Redistributions in binary form must reproduce the above copyright
11d6f907dcSJoerg Wunsch  *    notice, this list of conditions and the following disclaimer in the
12d6f907dcSJoerg Wunsch  *    documentation and/or other materials provided with the distribution.
13d6f907dcSJoerg Wunsch  *
14ad7cf975SJoerg Wunsch  * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
15d6f907dcSJoerg Wunsch  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16d6f907dcSJoerg Wunsch  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17ad7cf975SJoerg Wunsch  * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
18d6f907dcSJoerg Wunsch  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19d6f907dcSJoerg Wunsch  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20d6f907dcSJoerg Wunsch  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21d6f907dcSJoerg Wunsch  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22d6f907dcSJoerg Wunsch  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23d6f907dcSJoerg Wunsch  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24d6f907dcSJoerg Wunsch  * SUCH DAMAGE.
25d6f907dcSJoerg Wunsch  */
26d6f907dcSJoerg Wunsch 
271dcc6ec7SPhilippe Charnier #ifndef lint
281dcc6ec7SPhilippe Charnier static const char rcsid[] =
2997d92980SPeter Wemm   "$FreeBSD$";
301dcc6ec7SPhilippe Charnier #endif /* not lint */
311dcc6ec7SPhilippe Charnier 
321dcc6ec7SPhilippe Charnier #include <err.h>
335e91a8acSNate Williams #include <fcntl.h>
34e7161f36SAndrey A. Chernov #include <locale.h>
35f1d684faSDavid Nugent #include <paths.h>
362cc63cd1SBaptiste Daroussin #include <stdbool.h>
37f1d684faSDavid Nugent #include <sys/wait.h>
385f12594aSDavid Nugent #include "pw.h"
39d6f907dcSJoerg Wunsch 
40923dc0b2SDavid Nugent #if !defined(_PATH_YP)
41923dc0b2SDavid Nugent #define	_PATH_YP	"/var/yp/"
42923dc0b2SDavid Nugent #endif
432399cd14SDavid Nugent const char     *Modes[] = {
442399cd14SDavid Nugent   "add", "del", "mod", "show", "next",
452399cd14SDavid Nugent   NULL};
46d6f907dcSJoerg Wunsch const char     *Which[] = {"user", "group", NULL};
4748aee7f3SJoerg Wunsch static const char *Combo1[] = {
4848aee7f3SJoerg Wunsch   "useradd", "userdel", "usermod", "usershow", "usernext",
492399cd14SDavid Nugent   "lock", "unlock",
5048aee7f3SJoerg Wunsch   "groupadd", "groupdel", "groupmod", "groupshow", "groupnext",
51d6f907dcSJoerg Wunsch   NULL};
5248aee7f3SJoerg Wunsch static const char *Combo2[] = {
5348aee7f3SJoerg Wunsch   "adduser", "deluser", "moduser", "showuser", "nextuser",
542399cd14SDavid Nugent   "lock", "unlock",
5548aee7f3SJoerg Wunsch   "addgroup", "delgroup", "modgroup", "showgroup", "nextgroup",
56d6f907dcSJoerg Wunsch   NULL};
57d6f907dcSJoerg Wunsch 
585f12594aSDavid Nugent struct pwf PWF =
595f12594aSDavid Nugent {
60ac72be28SBaptiste Daroussin 	PWF_REGULAR,
615f12594aSDavid Nugent 	setpwent,
625f12594aSDavid Nugent 	endpwent,
635f12594aSDavid Nugent 	getpwent,
645f12594aSDavid Nugent 	getpwuid,
655f12594aSDavid Nugent 	getpwnam,
665f12594aSDavid Nugent 	setgrent,
675f12594aSDavid Nugent 	endgrent,
685f12594aSDavid Nugent 	getgrent,
695f12594aSDavid Nugent 	getgrgid,
705f12594aSDavid Nugent 	getgrnam,
715f12594aSDavid Nugent 
725f12594aSDavid Nugent };
735f12594aSDavid Nugent struct pwf VPWF =
745f12594aSDavid Nugent {
75ac72be28SBaptiste Daroussin 	PWF_ALT,
765f12594aSDavid Nugent 	vsetpwent,
775f12594aSDavid Nugent 	vendpwent,
785f12594aSDavid Nugent 	vgetpwent,
795f12594aSDavid Nugent 	vgetpwuid,
805f12594aSDavid Nugent 	vgetpwnam,
815f12594aSDavid Nugent 	vsetgrent,
825f12594aSDavid Nugent 	vendgrent,
835f12594aSDavid Nugent 	vgetgrent,
845f12594aSDavid Nugent 	vgetgrgid,
855f12594aSDavid Nugent 	vgetgrnam,
865f12594aSDavid Nugent };
875f12594aSDavid Nugent 
882cc63cd1SBaptiste Daroussin struct pwconf conf;
892cc63cd1SBaptiste Daroussin 
90d6f907dcSJoerg Wunsch static struct cargs arglist;
91d6f907dcSJoerg Wunsch 
92d6f907dcSJoerg Wunsch static int      getindex(const char *words[], const char *word);
93d6f907dcSJoerg Wunsch static void     cmdhelp(int mode, int which);
94d6f907dcSJoerg Wunsch 
95d6f907dcSJoerg Wunsch 
96d6f907dcSJoerg Wunsch int
97d6f907dcSJoerg Wunsch main(int argc, char *argv[])
98d6f907dcSJoerg Wunsch {
99d6f907dcSJoerg Wunsch 	int             ch;
100d6f907dcSJoerg Wunsch 	int             mode = -1;
101d6f907dcSJoerg Wunsch 	int             which = -1;
1025f12594aSDavid Nugent 	char		*config = NULL;
10390edef4fSBaptiste Daroussin 	struct stat	st;
104ac72be28SBaptiste Daroussin 	char		arg;
10582a3c75aSBaptiste Daroussin 	bool		relocated, nis;
106d6f907dcSJoerg Wunsch 
107d6f907dcSJoerg Wunsch 	static const char *opts[W_NUM][M_NUM] =
108d6f907dcSJoerg Wunsch 	{
10948aee7f3SJoerg Wunsch 		{ /* user */
110ac72be28SBaptiste Daroussin 			"R:V:C:qn:u:c:d:e:p:g:G:mM:k:s:oL:i:w:h:H:Db:NPy:Y",
111ac72be28SBaptiste Daroussin 			"R:V:C:qn:u:rY",
112ac72be28SBaptiste Daroussin 			"R:V:C:qn:u:c:d:e:p:g:G:mM:l:k:s:w:L:h:H:FNPY",
113ac72be28SBaptiste Daroussin 			"R:V:C:qn:u:FPa7",
114ac72be28SBaptiste Daroussin 			"R:V:C:q",
115ac72be28SBaptiste Daroussin 			"R:V:C:q",
116ac72be28SBaptiste Daroussin 			"R:V:C:q"
11748aee7f3SJoerg Wunsch 		},
11848aee7f3SJoerg Wunsch 		{ /* grp  */
119ac72be28SBaptiste Daroussin 			"R:V:C:qn:g:h:H:M:opNPY",
120ac72be28SBaptiste Daroussin 			"R:V:C:qn:g:Y",
121ac72be28SBaptiste Daroussin 			"R:V:C:qn:d:g:l:h:H:FM:m:NPY",
122ac72be28SBaptiste Daroussin 			"R:V:C:qn:g:FPa",
123ac72be28SBaptiste Daroussin 			"R:V:C:q"
12448aee7f3SJoerg Wunsch 		 }
125d6f907dcSJoerg Wunsch 	};
126d6f907dcSJoerg Wunsch 
1272cc63cd1SBaptiste Daroussin 	static int      (*funcs[W_NUM]) (int _mode, struct cargs * _args) =
128d6f907dcSJoerg Wunsch 	{			/* Request handlers */
129d6f907dcSJoerg Wunsch 		pw_user,
130d6f907dcSJoerg Wunsch 		pw_group
131d6f907dcSJoerg Wunsch 	};
132d6f907dcSJoerg Wunsch 
13382a3c75aSBaptiste Daroussin 	relocated = nis = false;
134*bbe09b2eSBaptiste Daroussin 	memset(&conf, 0, sizeof(conf));
1352cc63cd1SBaptiste Daroussin 	strlcpy(conf.etcpath, _PATH_PWD, sizeof(conf.etcpath));
1362cc63cd1SBaptiste Daroussin 
137d6f907dcSJoerg Wunsch 	LIST_INIT(&arglist);
138d6f907dcSJoerg Wunsch 
13939610c72SAndrey A. Chernov 	(void)setlocale(LC_ALL, "");
140e7161f36SAndrey A. Chernov 
141d6f907dcSJoerg Wunsch 	/*
142d6f907dcSJoerg Wunsch 	 * Break off the first couple of words to determine what exactly
143d6f907dcSJoerg Wunsch 	 * we're being asked to do
144d6f907dcSJoerg Wunsch 	 */
1455f12594aSDavid Nugent 	while (argc > 1) {
146d6f907dcSJoerg Wunsch 		int             tmp;
147d6f907dcSJoerg Wunsch 
1485f12594aSDavid Nugent 		if (*argv[1] == '-') {
1495f12594aSDavid Nugent 			/*
1505f12594aSDavid Nugent 			 * Special case, allow pw -V<dir> <operation> [args] for scripts etc.
1515f12594aSDavid Nugent 			 */
152ac72be28SBaptiste Daroussin 			arg = argv[1][1];
153ac72be28SBaptiste Daroussin 			if (arg == 'V' || arg == 'R') {
1542cc63cd1SBaptiste Daroussin 				if (relocated)
1552cc63cd1SBaptiste Daroussin 					errx(EXIT_FAILURE, "Both '-R' and '-V' "
1562cc63cd1SBaptiste Daroussin 					    "specified, only one accepted");
1572cc63cd1SBaptiste Daroussin 				relocated = true;
1585f12594aSDavid Nugent 				optarg = &argv[1][2];
1595f12594aSDavid Nugent 				if (*optarg == '\0') {
16090edef4fSBaptiste Daroussin 					if (stat(argv[2], &st) != 0)
16190edef4fSBaptiste Daroussin 						errx(EX_OSFILE, \
16290edef4fSBaptiste Daroussin 						    "no such directory `%s'",
16390edef4fSBaptiste Daroussin 						    argv[2]);
16490edef4fSBaptiste Daroussin 					if (!S_ISDIR(st.st_mode))
16590edef4fSBaptiste Daroussin 						errx(EX_OSFILE, "`%s' not a "
16690edef4fSBaptiste Daroussin 						    "directory", argv[2]);
1675f12594aSDavid Nugent 					optarg = argv[2];
1685f12594aSDavid Nugent 					++argv;
1695f12594aSDavid Nugent 					--argc;
1705f12594aSDavid Nugent 				}
1712cc63cd1SBaptiste Daroussin 				memcpy(&PWF, &VPWF, sizeof PWF);
1722cc63cd1SBaptiste Daroussin 				if (arg == 'R') {
1732cc63cd1SBaptiste Daroussin 					strlcpy(conf.rootdir, optarg,
1742cc63cd1SBaptiste Daroussin 					    sizeof(conf.rootdir));
1752cc63cd1SBaptiste Daroussin 					PWF._altdir = PWF_ROOTDIR;
1762cc63cd1SBaptiste Daroussin 				}
1772cc63cd1SBaptiste Daroussin 				snprintf(conf.etcpath, sizeof(conf.etcpath),
1782cc63cd1SBaptiste Daroussin 				    "%s%s", optarg, arg == 'R' ? "/etc" : "");
179e3921b27SDavid Nugent 			} else
1805f12594aSDavid Nugent 				break;
1815f12594aSDavid Nugent 		}
182c4e667b9SDavid Nugent 		else if (mode == -1 && (tmp = getindex(Modes, argv[1])) != -1)
183d6f907dcSJoerg Wunsch 			mode = tmp;
184c4e667b9SDavid Nugent 		else if (which == -1 && (tmp = getindex(Which, argv[1])) != -1)
185d6f907dcSJoerg Wunsch 			which = tmp;
186c4e667b9SDavid Nugent 		else if ((mode == -1 && which == -1) &&
187c4e667b9SDavid Nugent 			 ((tmp = getindex(Combo1, argv[1])) != -1 ||
188c4e667b9SDavid Nugent 			  (tmp = getindex(Combo2, argv[1])) != -1)) {
189d6f907dcSJoerg Wunsch 			which = tmp / M_NUM;
190d6f907dcSJoerg Wunsch 			mode = tmp % M_NUM;
191c4e667b9SDavid Nugent 		} else if (strcmp(argv[1], "help") == 0 && argv[2] == NULL)
192d6f907dcSJoerg Wunsch 			cmdhelp(mode, which);
1937291e217SDavid Nugent 		else if (which != -1 && mode != -1)
194d6f907dcSJoerg Wunsch 			addarg(&arglist, 'n', argv[1]);
195d6f907dcSJoerg Wunsch 		else
1961dcc6ec7SPhilippe Charnier 			errx(EX_USAGE, "unknown keyword `%s'", argv[1]);
197d6f907dcSJoerg Wunsch 		++argv;
198d6f907dcSJoerg Wunsch 		--argc;
199d6f907dcSJoerg Wunsch 	}
200d6f907dcSJoerg Wunsch 
201d6f907dcSJoerg Wunsch 	/*
202d6f907dcSJoerg Wunsch 	 * Bail out unless the user is specific!
203d6f907dcSJoerg Wunsch 	 */
204d6f907dcSJoerg Wunsch 	if (mode == -1 || which == -1)
205d6f907dcSJoerg Wunsch 		cmdhelp(mode, which);
206d6f907dcSJoerg Wunsch 
207d6f907dcSJoerg Wunsch 	/*
208d6f907dcSJoerg Wunsch 	 * We know which mode we're in and what we're about to do, so now
209d6f907dcSJoerg Wunsch 	 * let's dispatch the remaining command line args in a genric way.
210d6f907dcSJoerg Wunsch 	 */
211d6f907dcSJoerg Wunsch 	optarg = NULL;
212d6f907dcSJoerg Wunsch 
213d6f907dcSJoerg Wunsch 	while ((ch = getopt(argc, argv, opts[which][mode])) != -1) {
21482a3c75aSBaptiste Daroussin 		switch (ch) {
21582a3c75aSBaptiste Daroussin 		case '?':
216923dc0b2SDavid Nugent 			errx(EX_USAGE, "unknown switch");
21782a3c75aSBaptiste Daroussin 			break;
21882a3c75aSBaptiste Daroussin 		case 'C':
21982a3c75aSBaptiste Daroussin 			config = optarg;
22082a3c75aSBaptiste Daroussin 			break;
221363cefdbSBaptiste Daroussin 		case 'N':
222363cefdbSBaptiste Daroussin 			conf.dryrun = true;
223363cefdbSBaptiste Daroussin 			break;
2242166b4d1SBaptiste Daroussin 		case 'P':
2252166b4d1SBaptiste Daroussin 			conf.pretty = true;
2262166b4d1SBaptiste Daroussin 			break;
22782a3c75aSBaptiste Daroussin 		case 'Y':
22882a3c75aSBaptiste Daroussin 			nis = true;
22982a3c75aSBaptiste Daroussin 			break;
23082a3c75aSBaptiste Daroussin 		default:
231d6f907dcSJoerg Wunsch 			addarg(&arglist, ch, optarg);
23282a3c75aSBaptiste Daroussin 			break;
23382a3c75aSBaptiste Daroussin 		}
234d6f907dcSJoerg Wunsch 		optarg = NULL;
235d6f907dcSJoerg Wunsch 	}
236d6f907dcSJoerg Wunsch 
237d6f907dcSJoerg Wunsch 	/*
23848aee7f3SJoerg Wunsch 	 * Must be root to attempt an update
23948aee7f3SJoerg Wunsch 	 */
240363cefdbSBaptiste Daroussin 	if (geteuid() != 0 && mode != M_PRINT && mode != M_NEXT && !conf.dryrun)
2411dcc6ec7SPhilippe Charnier 		errx(EX_NOPERM, "you must be root to run this program");
24248aee7f3SJoerg Wunsch 
24348aee7f3SJoerg Wunsch 	/*
244d6f907dcSJoerg Wunsch 	 * We should immediately look for the -q 'quiet' switch so that we
245d6f907dcSJoerg Wunsch 	 * don't bother with extraneous errors
246d6f907dcSJoerg Wunsch 	 */
247d6f907dcSJoerg Wunsch 	if (getarg(&arglist, 'q') != NULL)
2481a37aa56SDavid E. O'Brien 		freopen(_PATH_DEVNULL, "w", stderr);
249d6f907dcSJoerg Wunsch 
250d6f907dcSJoerg Wunsch 	/*
2515f12594aSDavid Nugent 	 * Set our base working path if not overridden
2525f12594aSDavid Nugent 	 */
2535f12594aSDavid Nugent 
2545f12594aSDavid Nugent 	if (config == NULL) {	/* Only override config location if -C not specified */
2552cc63cd1SBaptiste Daroussin 		asprintf(&config, "%s/pw.conf", conf.etcpath);
25619741915SBaptiste Daroussin 		if (config == NULL)
25719741915SBaptiste Daroussin 			errx(EX_OSERR, "out of memory");
2585f12594aSDavid Nugent 	}
2595f12594aSDavid Nugent 
2605f12594aSDavid Nugent 	/*
261d6f907dcSJoerg Wunsch 	 * Now, let's do the common initialisation
262d6f907dcSJoerg Wunsch 	 */
2632cc63cd1SBaptiste Daroussin 	conf.userconf = read_userconfig(config);
2645e91a8acSNate Williams 
2652cc63cd1SBaptiste Daroussin 	ch = funcs[which] (mode, &arglist);
266f1d684faSDavid Nugent 
267f1d684faSDavid Nugent 	/*
268f1d684faSDavid Nugent 	 * If everything went ok, and we've been asked to update
269f1d684faSDavid Nugent 	 * the NIS maps, then do it now
270f1d684faSDavid Nugent 	 */
27182a3c75aSBaptiste Daroussin 	if (ch == EXIT_SUCCESS && nis) {
272f1d684faSDavid Nugent 		pid_t	pid;
273f1d684faSDavid Nugent 
274f1d684faSDavid Nugent 		fflush(NULL);
275f1d684faSDavid Nugent 		if (chdir(_PATH_YP) == -1)
2761dcc6ec7SPhilippe Charnier 			warn("chdir(" _PATH_YP ")");
277f1d684faSDavid Nugent 		else if ((pid = fork()) == -1)
2781dcc6ec7SPhilippe Charnier 			warn("fork()");
279f1d684faSDavid Nugent 		else if (pid == 0) {
280f1d684faSDavid Nugent 			/* Is make anywhere else? */
2817bc6d015SBrian Somers 			execlp("/usr/bin/make", "make", (char *)NULL);
282f1d684faSDavid Nugent 			_exit(1);
283f1d684faSDavid Nugent 		} else {
284f1d684faSDavid Nugent 			int   i;
285f1d684faSDavid Nugent 			waitpid(pid, &i, 0);
286f1d684faSDavid Nugent 			if ((i = WEXITSTATUS(i)) != 0)
2871dcc6ec7SPhilippe Charnier 				errx(ch, "make exited with status %d", i);
288f1d684faSDavid Nugent 			else
2892cc63cd1SBaptiste Daroussin 				pw_log(conf.userconf, mode, which, "NIS maps updated");
290f1d684faSDavid Nugent 		}
291f1d684faSDavid Nugent 	}
292f1d684faSDavid Nugent 	return ch;
293d6f907dcSJoerg Wunsch }
294d6f907dcSJoerg Wunsch 
2955e91a8acSNate Williams 
296d6f907dcSJoerg Wunsch static int
297d6f907dcSJoerg Wunsch getindex(const char *words[], const char *word)
298d6f907dcSJoerg Wunsch {
299d6f907dcSJoerg Wunsch 	int             i = 0;
300d6f907dcSJoerg Wunsch 
301d6f907dcSJoerg Wunsch 	while (words[i]) {
302d6f907dcSJoerg Wunsch 		if (strcmp(words[i], word) == 0)
303d6f907dcSJoerg Wunsch 			return i;
304d6f907dcSJoerg Wunsch 		i++;
305d6f907dcSJoerg Wunsch 	}
306d6f907dcSJoerg Wunsch 	return -1;
307d6f907dcSJoerg Wunsch }
308d6f907dcSJoerg Wunsch 
309d6f907dcSJoerg Wunsch 
310d6f907dcSJoerg Wunsch /*
311d6f907dcSJoerg Wunsch  * This is probably an overkill for a cmdline help system, but it reflects
312d6f907dcSJoerg Wunsch  * the complexity of the command line.
313d6f907dcSJoerg Wunsch  */
314d6f907dcSJoerg Wunsch 
315d6f907dcSJoerg Wunsch static void
316d6f907dcSJoerg Wunsch cmdhelp(int mode, int which)
317d6f907dcSJoerg Wunsch {
318d6f907dcSJoerg Wunsch 	if (which == -1)
3192399cd14SDavid Nugent 		fprintf(stderr, "usage:\n  pw [user|group|lock|unlock] [add|del|mod|show|next] [help|switches/values]\n");
320d6f907dcSJoerg Wunsch 	else if (mode == -1)
3212399cd14SDavid Nugent 		fprintf(stderr, "usage:\n  pw %s [add|del|mod|show|next] [help|switches/values]\n", Which[which]);
322d6f907dcSJoerg Wunsch 	else {
323d6f907dcSJoerg Wunsch 
324d6f907dcSJoerg Wunsch 		/*
325d6f907dcSJoerg Wunsch 		 * We need to give mode specific help
326d6f907dcSJoerg Wunsch 		 */
327d6f907dcSJoerg Wunsch 		static const char *help[W_NUM][M_NUM] =
328d6f907dcSJoerg Wunsch 		{
329d6f907dcSJoerg Wunsch 			{
3301dcc6ec7SPhilippe Charnier 				"usage: pw useradd [name] [switches]\n"
3315f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
332ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
333d6f907dcSJoerg Wunsch 				"\t-C config      configuration file\n"
334d6f907dcSJoerg Wunsch 				"\t-q             quiet operation\n"
335d6f907dcSJoerg Wunsch 				"  Adding users:\n"
336d6f907dcSJoerg Wunsch 				"\t-n name        login name\n"
337d6f907dcSJoerg Wunsch 				"\t-u uid         user id\n"
338d6f907dcSJoerg Wunsch 				"\t-c comment     user name/comment\n"
339d6f907dcSJoerg Wunsch 				"\t-d directory   home directory\n"
340d6f907dcSJoerg Wunsch 				"\t-e date        account expiry date\n"
341d6f907dcSJoerg Wunsch 				"\t-p date        password expiry date\n"
342d6f907dcSJoerg Wunsch 				"\t-g grp         initial group\n"
343d6f907dcSJoerg Wunsch 				"\t-G grp1,grp2   additional groups\n"
344d6f907dcSJoerg Wunsch 				"\t-m [ -k dir ]  create and set up home\n"
3451dd634b0SLukas Ertl 				"\t-M mode        home directory permissions\n"
346d6f907dcSJoerg Wunsch 				"\t-s shell       name of login shell\n"
347d6f907dcSJoerg Wunsch 				"\t-o             duplicate uid ok\n"
348d6f907dcSJoerg Wunsch 				"\t-L class       user class\n"
349d6f907dcSJoerg Wunsch 				"\t-h fd          read password on fd\n"
35087d6b5caSIan Dowse 				"\t-H fd          read encrypted password on fd\n"
351f1d684faSDavid Nugent 				"\t-Y             update NIS maps\n"
35248aee7f3SJoerg Wunsch 				"\t-N             no update\n"
353d6f907dcSJoerg Wunsch 				"  Setting defaults:\n"
3545f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
355ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
356d6f907dcSJoerg Wunsch 			        "\t-D             set user defaults\n"
357d6f907dcSJoerg Wunsch 				"\t-b dir         default home root dir\n"
358d6f907dcSJoerg Wunsch 				"\t-e period      default expiry period\n"
359d6f907dcSJoerg Wunsch 				"\t-p period      default password change period\n"
360d6f907dcSJoerg Wunsch 				"\t-g group       default group\n"
361d6f907dcSJoerg Wunsch 				"\t-G grp1,grp2   additional groups\n"
362d6f907dcSJoerg Wunsch 				"\t-L class       default user class\n"
363d6f907dcSJoerg Wunsch 				"\t-k dir         default home skeleton\n"
36485204142SLukas Ertl 				"\t-M mode        home directory permissions\n"
365d6f907dcSJoerg Wunsch 				"\t-u min,max     set min,max uids\n"
366d6f907dcSJoerg Wunsch 				"\t-i min,max     set min,max gids\n"
367d6f907dcSJoerg Wunsch 				"\t-w method      set default password method\n"
368f1d684faSDavid Nugent 				"\t-s shell       default shell\n"
369f1d684faSDavid Nugent 				"\t-y path        set NIS passwd file path\n",
3701dcc6ec7SPhilippe Charnier 				"usage: pw userdel [uid|name] [switches]\n"
3715f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
372ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
373d6f907dcSJoerg Wunsch 				"\t-n name        login name\n"
374d6f907dcSJoerg Wunsch 				"\t-u uid         user id\n"
375f1d684faSDavid Nugent 				"\t-Y             update NIS maps\n"
376d6f907dcSJoerg Wunsch 				"\t-r             remove home & contents\n",
3771dcc6ec7SPhilippe Charnier 				"usage: pw usermod [uid|name] [switches]\n"
3785f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
379ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
380d6f907dcSJoerg Wunsch 				"\t-C config      configuration file\n"
381d6f907dcSJoerg Wunsch 				"\t-q             quiet operation\n"
382d6f907dcSJoerg Wunsch 				"\t-F             force add if no user\n"
383d6f907dcSJoerg Wunsch 				"\t-n name        login name\n"
384d6f907dcSJoerg Wunsch 				"\t-u uid         user id\n"
385d6f907dcSJoerg Wunsch 				"\t-c comment     user name/comment\n"
386d6f907dcSJoerg Wunsch 				"\t-d directory   home directory\n"
387d6f907dcSJoerg Wunsch 				"\t-e date        account expiry date\n"
388d6f907dcSJoerg Wunsch 				"\t-p date        password expiry date\n"
389d6f907dcSJoerg Wunsch 				"\t-g grp         initial group\n"
390d6f907dcSJoerg Wunsch 				"\t-G grp1,grp2   additional groups\n"
391d6f907dcSJoerg Wunsch 				"\t-l name        new login name\n"
392d6f907dcSJoerg Wunsch 				"\t-L class       user class\n"
393d6f907dcSJoerg Wunsch 				"\t-m [ -k dir ]  create and set up home\n"
3941dd634b0SLukas Ertl 				"\t-M mode        home directory permissions\n"
395d6f907dcSJoerg Wunsch 				"\t-s shell       name of login shell\n"
39648aee7f3SJoerg Wunsch 				"\t-w method      set new password using method\n"
39748aee7f3SJoerg Wunsch 				"\t-h fd          read password on fd\n"
39887d6b5caSIan Dowse 				"\t-H fd          read encrypted password on fd\n"
399f1d684faSDavid Nugent 				"\t-Y             update NIS maps\n"
40048aee7f3SJoerg Wunsch 				"\t-N             no update\n",
4011dcc6ec7SPhilippe Charnier 				"usage: pw usershow [uid|name] [switches]\n"
4025f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
403ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
404d6f907dcSJoerg Wunsch 				"\t-n name        login name\n"
405d6f907dcSJoerg Wunsch 				"\t-u uid         user id\n"
406d6f907dcSJoerg Wunsch 				"\t-F             force print\n"
40748aee7f3SJoerg Wunsch 				"\t-P             prettier format\n"
408f3522722SDavid Nugent 				"\t-a             print all users\n"
409f3522722SDavid Nugent 				"\t-7             print in v7 format\n",
4101dcc6ec7SPhilippe Charnier 				"usage: pw usernext [switches]\n"
4115f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
412ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
41348aee7f3SJoerg Wunsch 				"\t-C config      configuration file\n"
414ed6fd55aSDima Dorfman 				"\t-q             quiet operation\n",
415ed6fd55aSDima Dorfman 				"usage pw: lock [switches]\n"
416ed6fd55aSDima Dorfman 				"\t-V etcdir      alternate /etc locations\n"
417ed6fd55aSDima Dorfman 				"\t-C config      configuration file\n"
418ed6fd55aSDima Dorfman 				"\t-q             quiet operation\n",
419ed6fd55aSDima Dorfman 				"usage pw: unlock [switches]\n"
420ed6fd55aSDima Dorfman 				"\t-V etcdir      alternate /etc locations\n"
421ed6fd55aSDima Dorfman 				"\t-C config      configuration file\n"
422ed6fd55aSDima Dorfman 				"\t-q             quiet operation\n"
423d6f907dcSJoerg Wunsch 			},
424d6f907dcSJoerg Wunsch 			{
4251dcc6ec7SPhilippe Charnier 				"usage: pw groupadd [group|gid] [switches]\n"
4265f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
427ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
428d6f907dcSJoerg Wunsch 				"\t-C config      configuration file\n"
429d6f907dcSJoerg Wunsch 				"\t-q             quiet operation\n"
430d6f907dcSJoerg Wunsch 				"\t-n group       group name\n"
431d6f907dcSJoerg Wunsch 				"\t-g gid         group id\n"
43248aee7f3SJoerg Wunsch 				"\t-M usr1,usr2   add users as group members\n"
43348aee7f3SJoerg Wunsch 				"\t-o             duplicate gid ok\n"
434f1d684faSDavid Nugent 				"\t-Y             update NIS maps\n"
43548aee7f3SJoerg Wunsch 				"\t-N             no update\n",
4361dcc6ec7SPhilippe Charnier 				"usage: pw groupdel [group|gid] [switches]\n"
4375f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
438ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
439d6f907dcSJoerg Wunsch 				"\t-n name        group name\n"
440f1d684faSDavid Nugent 				"\t-g gid         group id\n"
441f1d684faSDavid Nugent 				"\t-Y             update NIS maps\n",
4421dcc6ec7SPhilippe Charnier 				"usage: pw groupmod [group|gid] [switches]\n"
4435f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
444ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
445d6f907dcSJoerg Wunsch 				"\t-C config      configuration file\n"
446d6f907dcSJoerg Wunsch 				"\t-q             quiet operation\n"
447d6f907dcSJoerg Wunsch 				"\t-F             force add if not exists\n"
448d6f907dcSJoerg Wunsch 				"\t-n name        group name\n"
449d6f907dcSJoerg Wunsch 				"\t-g gid         group id\n"
45048aee7f3SJoerg Wunsch 				"\t-M usr1,usr2   replaces users as group members\n"
45148aee7f3SJoerg Wunsch 				"\t-m usr1,usr2   add users as group members\n"
452bc991a6dSSean Farley 				"\t-d usr1,usr2   delete users as group members\n"
45348aee7f3SJoerg Wunsch 				"\t-l name        new group name\n"
454f1d684faSDavid Nugent 				"\t-Y             update NIS maps\n"
45548aee7f3SJoerg Wunsch 				"\t-N             no update\n",
4561dcc6ec7SPhilippe Charnier 				"usage: pw groupshow [group|gid] [switches]\n"
4575f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
458ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
459d6f907dcSJoerg Wunsch 				"\t-n name        group name\n"
460d6f907dcSJoerg Wunsch 				"\t-g gid         group id\n"
461d6f907dcSJoerg Wunsch 				"\t-F             force print\n"
46248aee7f3SJoerg Wunsch 				"\t-P             prettier format\n"
46348aee7f3SJoerg Wunsch 				"\t-a             print all accounting groups\n",
4641dcc6ec7SPhilippe Charnier 				"usage: pw groupnext [switches]\n"
4655f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
466ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
46748aee7f3SJoerg Wunsch 				"\t-C config      configuration file\n"
468ed6fd55aSDima Dorfman 				"\t-q             quiet operation\n"
469d6f907dcSJoerg Wunsch 			}
470d6f907dcSJoerg Wunsch 		};
471d6f907dcSJoerg Wunsch 
472cab0fb4eSKris Kennaway 		fprintf(stderr, "%s", help[which][mode]);
473d6f907dcSJoerg Wunsch 	}
47448aee7f3SJoerg Wunsch 	exit(EXIT_FAILURE);
475d6f907dcSJoerg Wunsch }
476d6f907dcSJoerg Wunsch 
477d6f907dcSJoerg Wunsch struct carg    *
478d6f907dcSJoerg Wunsch getarg(struct cargs * _args, int ch)
479d6f907dcSJoerg Wunsch {
480d0d78e13SBen Smithurst 	struct carg    *c = LIST_FIRST(_args);
481d6f907dcSJoerg Wunsch 
482d6f907dcSJoerg Wunsch 	while (c != NULL && c->ch != ch)
483d0d78e13SBen Smithurst 		c = LIST_NEXT(c, list);
484d6f907dcSJoerg Wunsch 	return c;
485d6f907dcSJoerg Wunsch }
486d6f907dcSJoerg Wunsch 
487d6f907dcSJoerg Wunsch struct carg    *
488d6f907dcSJoerg Wunsch addarg(struct cargs * _args, int ch, char *argstr)
489d6f907dcSJoerg Wunsch {
490d6f907dcSJoerg Wunsch 	struct carg    *ca = malloc(sizeof(struct carg));
491d6f907dcSJoerg Wunsch 
492d6f907dcSJoerg Wunsch 	if (ca == NULL)
4931dcc6ec7SPhilippe Charnier 		errx(EX_OSERR, "out of memory");
494d6f907dcSJoerg Wunsch 	ca->ch = ch;
495d6f907dcSJoerg Wunsch 	ca->val = argstr;
496d6f907dcSJoerg Wunsch 	LIST_INSERT_HEAD(_args, ca, list);
497d6f907dcSJoerg Wunsch 	return ca;
498d6f907dcSJoerg Wunsch }
499