xref: /freebsd/usr.sbin/pw/pw.c (revision a70fbf7ef512e8541d8817383687e8981158dfd1)
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;
102a9237189SBaptiste Daroussin 	long		id = -1;
1035f12594aSDavid Nugent 	char		*config = NULL;
10490edef4fSBaptiste Daroussin 	struct stat	st;
105a9237189SBaptiste Daroussin 	const char	*errstr;
106a9237189SBaptiste Daroussin 	char		arg, *name;
10782a3c75aSBaptiste Daroussin 	bool		relocated, nis;
108d6f907dcSJoerg Wunsch 
109d6f907dcSJoerg Wunsch 	static const char *opts[W_NUM][M_NUM] =
110d6f907dcSJoerg Wunsch 	{
11148aee7f3SJoerg Wunsch 		{ /* user */
112ac72be28SBaptiste Daroussin 			"R:V:C:qn:u:c:d:e:p:g:G:mM:k:s:oL:i:w:h:H:Db:NPy:Y",
113ac72be28SBaptiste Daroussin 			"R:V:C:qn:u:rY",
114ac72be28SBaptiste Daroussin 			"R:V:C:qn:u:c:d:e:p:g:G:mM:l:k:s:w:L:h:H:FNPY",
115ac72be28SBaptiste Daroussin 			"R:V:C:qn:u:FPa7",
1163bfc59e8SBaptiste Daroussin 			"R:V:C:q",
117ac72be28SBaptiste Daroussin 			"R:V:C:q",
118ac72be28SBaptiste Daroussin 			"R:V:C:q"
11948aee7f3SJoerg Wunsch 		},
12048aee7f3SJoerg Wunsch 		{ /* grp  */
121ac72be28SBaptiste Daroussin 			"R:V:C:qn:g:h:H:M:opNPY",
122ac72be28SBaptiste Daroussin 			"R:V:C:qn:g:Y",
123ac72be28SBaptiste Daroussin 			"R:V:C:qn:d:g:l:h:H:FM:m:NPY",
124ac72be28SBaptiste Daroussin 			"R:V:C:qn:g:FPa",
1253bfc59e8SBaptiste Daroussin 			"R:V:C:q"
12648aee7f3SJoerg Wunsch 		 }
127d6f907dcSJoerg Wunsch 	};
128d6f907dcSJoerg Wunsch 
129a9237189SBaptiste Daroussin 	static int      (*funcs[W_NUM]) (int _mode, char *_name, long _id,
130a9237189SBaptiste Daroussin 	    struct cargs * _args) =
131d6f907dcSJoerg Wunsch 	{			/* Request handlers */
132d6f907dcSJoerg Wunsch 		pw_user,
133d6f907dcSJoerg Wunsch 		pw_group
134d6f907dcSJoerg Wunsch 	};
135d6f907dcSJoerg Wunsch 
136a9237189SBaptiste Daroussin 	name = NULL;
13782a3c75aSBaptiste Daroussin 	relocated = nis = false;
138bbe09b2eSBaptiste Daroussin 	memset(&conf, 0, sizeof(conf));
13965730d93SBaptiste Daroussin 	strlcpy(conf.rootdir, "/", sizeof(conf.rootdir));
1402cc63cd1SBaptiste Daroussin 	strlcpy(conf.etcpath, _PATH_PWD, sizeof(conf.etcpath));
14111621f96SBaptiste Daroussin 	conf.fd = -1;
142*a70fbf7eSBaptiste Daroussin 	conf.checkduplicate = true;
1432cc63cd1SBaptiste Daroussin 
144d6f907dcSJoerg Wunsch 	LIST_INIT(&arglist);
145d6f907dcSJoerg Wunsch 
14639610c72SAndrey A. Chernov 	(void)setlocale(LC_ALL, "");
147e7161f36SAndrey A. Chernov 
148d6f907dcSJoerg Wunsch 	/*
149d6f907dcSJoerg Wunsch 	 * Break off the first couple of words to determine what exactly
150d6f907dcSJoerg Wunsch 	 * we're being asked to do
151d6f907dcSJoerg Wunsch 	 */
1525f12594aSDavid Nugent 	while (argc > 1) {
153d6f907dcSJoerg Wunsch 		int             tmp;
154d6f907dcSJoerg Wunsch 
1555f12594aSDavid Nugent 		if (*argv[1] == '-') {
1565f12594aSDavid Nugent 			/*
1575f12594aSDavid Nugent 			 * Special case, allow pw -V<dir> <operation> [args] for scripts etc.
1585f12594aSDavid Nugent 			 */
159ac72be28SBaptiste Daroussin 			arg = argv[1][1];
160ac72be28SBaptiste Daroussin 			if (arg == 'V' || arg == 'R') {
1612cc63cd1SBaptiste Daroussin 				if (relocated)
1622cc63cd1SBaptiste Daroussin 					errx(EXIT_FAILURE, "Both '-R' and '-V' "
1632cc63cd1SBaptiste Daroussin 					    "specified, only one accepted");
1642cc63cd1SBaptiste Daroussin 				relocated = true;
1655f12594aSDavid Nugent 				optarg = &argv[1][2];
1665f12594aSDavid Nugent 				if (*optarg == '\0') {
16790edef4fSBaptiste Daroussin 					if (stat(argv[2], &st) != 0)
16890edef4fSBaptiste Daroussin 						errx(EX_OSFILE, \
16990edef4fSBaptiste Daroussin 						    "no such directory `%s'",
17090edef4fSBaptiste Daroussin 						    argv[2]);
17190edef4fSBaptiste Daroussin 					if (!S_ISDIR(st.st_mode))
17290edef4fSBaptiste Daroussin 						errx(EX_OSFILE, "`%s' not a "
17390edef4fSBaptiste Daroussin 						    "directory", argv[2]);
1745f12594aSDavid Nugent 					optarg = argv[2];
1755f12594aSDavid Nugent 					++argv;
1765f12594aSDavid Nugent 					--argc;
1775f12594aSDavid Nugent 				}
1782cc63cd1SBaptiste Daroussin 				memcpy(&PWF, &VPWF, sizeof PWF);
1792cc63cd1SBaptiste Daroussin 				if (arg == 'R') {
1802cc63cd1SBaptiste Daroussin 					strlcpy(conf.rootdir, optarg,
1812cc63cd1SBaptiste Daroussin 					    sizeof(conf.rootdir));
1822cc63cd1SBaptiste Daroussin 					PWF._altdir = PWF_ROOTDIR;
1832cc63cd1SBaptiste Daroussin 				}
1842cc63cd1SBaptiste Daroussin 				snprintf(conf.etcpath, sizeof(conf.etcpath),
1852cc63cd1SBaptiste Daroussin 				    "%s%s", optarg, arg == 'R' ? "/etc" : "");
186e3921b27SDavid Nugent 			} else
1875f12594aSDavid Nugent 				break;
1885f12594aSDavid Nugent 		}
189c4e667b9SDavid Nugent 		else if (mode == -1 && (tmp = getindex(Modes, argv[1])) != -1)
190d6f907dcSJoerg Wunsch 			mode = tmp;
191c4e667b9SDavid Nugent 		else if (which == -1 && (tmp = getindex(Which, argv[1])) != -1)
192d6f907dcSJoerg Wunsch 			which = tmp;
193c4e667b9SDavid Nugent 		else if ((mode == -1 && which == -1) &&
194c4e667b9SDavid Nugent 			 ((tmp = getindex(Combo1, argv[1])) != -1 ||
195c4e667b9SDavid Nugent 			  (tmp = getindex(Combo2, argv[1])) != -1)) {
196d6f907dcSJoerg Wunsch 			which = tmp / M_NUM;
197d6f907dcSJoerg Wunsch 			mode = tmp % M_NUM;
198c4e667b9SDavid Nugent 		} else if (strcmp(argv[1], "help") == 0 && argv[2] == NULL)
199d6f907dcSJoerg Wunsch 			cmdhelp(mode, which);
200a9237189SBaptiste Daroussin 		else if (which != -1 && mode != -1) {
201a9237189SBaptiste Daroussin 			if (strspn(argv[1], "0123456789") == strlen(argv[1])) {
202a9237189SBaptiste Daroussin 				id = strtonum(argv[1], 0, LONG_MAX, &errstr);
203a9237189SBaptiste Daroussin 				if (errstr != NULL)
204a9237189SBaptiste Daroussin 					errx(EX_USAGE, "Bad id '%s': %s",
205a9237189SBaptiste Daroussin 					    argv[1], errstr);
206a9237189SBaptiste Daroussin 			} else
207a9237189SBaptiste Daroussin 				name = argv[1];
208a9237189SBaptiste Daroussin 		} else
2091dcc6ec7SPhilippe Charnier 			errx(EX_USAGE, "unknown keyword `%s'", argv[1]);
210d6f907dcSJoerg Wunsch 		++argv;
211d6f907dcSJoerg Wunsch 		--argc;
212d6f907dcSJoerg Wunsch 	}
213d6f907dcSJoerg Wunsch 
214d6f907dcSJoerg Wunsch 	/*
215d6f907dcSJoerg Wunsch 	 * Bail out unless the user is specific!
216d6f907dcSJoerg Wunsch 	 */
217d6f907dcSJoerg Wunsch 	if (mode == -1 || which == -1)
218d6f907dcSJoerg Wunsch 		cmdhelp(mode, which);
219d6f907dcSJoerg Wunsch 
22065730d93SBaptiste Daroussin 	conf.rootfd = open(conf.rootdir, O_DIRECTORY|O_CLOEXEC);
22165730d93SBaptiste Daroussin 	if (conf.rootfd == -1)
22265730d93SBaptiste Daroussin 		errx(EXIT_FAILURE, "Unable to open '%s'", conf.rootdir);
2230fc58d14SBaptiste Daroussin 	conf.which = which;
224d6f907dcSJoerg Wunsch 	/*
225d6f907dcSJoerg Wunsch 	 * We know which mode we're in and what we're about to do, so now
226d6f907dcSJoerg Wunsch 	 * let's dispatch the remaining command line args in a genric way.
227d6f907dcSJoerg Wunsch 	 */
228d6f907dcSJoerg Wunsch 	optarg = NULL;
229d6f907dcSJoerg Wunsch 
230d6f907dcSJoerg Wunsch 	while ((ch = getopt(argc, argv, opts[which][mode])) != -1) {
23182a3c75aSBaptiste Daroussin 		switch (ch) {
23282a3c75aSBaptiste Daroussin 		case '?':
233923dc0b2SDavid Nugent 			errx(EX_USAGE, "unknown switch");
23482a3c75aSBaptiste Daroussin 			break;
235c86f7ad5SBaptiste Daroussin 		case '7':
236c86f7ad5SBaptiste Daroussin 			conf.v7 = true;
237c86f7ad5SBaptiste Daroussin 			break;
23882a3c75aSBaptiste Daroussin 		case 'C':
239a68fbadbSBaptiste Daroussin 			conf.config = optarg;
240a68fbadbSBaptiste Daroussin 			config = conf.config;
24182a3c75aSBaptiste Daroussin 			break;
242ae73dd9fSBaptiste Daroussin 		case 'F':
243ae73dd9fSBaptiste Daroussin 			conf.force = true;
244ae73dd9fSBaptiste Daroussin 			break;
245363cefdbSBaptiste Daroussin 		case 'N':
246363cefdbSBaptiste Daroussin 			conf.dryrun = true;
247363cefdbSBaptiste Daroussin 			break;
248bae068d2SBaptiste Daroussin 		case 'l':
249bae068d2SBaptiste Daroussin 			if (strlen(optarg) >= MAXLOGNAME)
250bae068d2SBaptiste Daroussin 				errx(EX_USAGE, "new name too long: %s", optarg);
251bae068d2SBaptiste Daroussin 			conf.newname = optarg;
252bae068d2SBaptiste Daroussin 			break;
2532166b4d1SBaptiste Daroussin 		case 'P':
2542166b4d1SBaptiste Daroussin 			conf.pretty = true;
2552166b4d1SBaptiste Daroussin 			break;
25682a3c75aSBaptiste Daroussin 		case 'Y':
25782a3c75aSBaptiste Daroussin 			nis = true;
25882a3c75aSBaptiste Daroussin 			break;
259ae73dd9fSBaptiste Daroussin 		case 'a':
260ae73dd9fSBaptiste Daroussin 			conf.all = true;
261ae73dd9fSBaptiste Daroussin 			break;
26249c195e6SBaptiste Daroussin 		case 'c':
26349c195e6SBaptiste Daroussin 			conf.gecos = pw_checkname(optarg, 1);
26449c195e6SBaptiste Daroussin 			break;
265a9237189SBaptiste Daroussin 		case 'g':
266a9237189SBaptiste Daroussin 			if (which == 0) { /* for user* */
267a9237189SBaptiste Daroussin 				addarg(&arglist, 'g', optarg);
268a9237189SBaptiste Daroussin 				break;
269a9237189SBaptiste Daroussin 			}
270a9237189SBaptiste Daroussin 			if (strspn(optarg, "0123456789") != strlen(optarg))
2711718a38fSBaptiste Daroussin 				errx(EX_USAGE, "-g expects a number");
2721718a38fSBaptiste Daroussin 			id = strtonum(optarg, 0, LONG_MAX, &errstr);
2731718a38fSBaptiste Daroussin 			if (errstr != NULL)
2741718a38fSBaptiste Daroussin 				errx(EX_USAGE, "Bad id '%s': %s", optarg,
2751718a38fSBaptiste Daroussin 				    errstr);
2761718a38fSBaptiste Daroussin 			break;
2771718a38fSBaptiste Daroussin 		case 'u':
2781718a38fSBaptiste Daroussin 			if (strspn(optarg, "0123456789,") != strlen(optarg))
2791718a38fSBaptiste Daroussin 				errx(EX_USAGE, "-u expects a number");
2801718a38fSBaptiste Daroussin 			if (strchr(optarg, ',') != NULL) {
2811718a38fSBaptiste Daroussin 				addarg(&arglist, 'u', optarg);
2821718a38fSBaptiste Daroussin 				break;
2831718a38fSBaptiste Daroussin 			}
284a9237189SBaptiste Daroussin 			id = strtonum(optarg, 0, LONG_MAX, &errstr);
285a9237189SBaptiste Daroussin 			if (errstr != NULL)
286a9237189SBaptiste Daroussin 				errx(EX_USAGE, "Bad id '%s': %s", optarg,
287a9237189SBaptiste Daroussin 				    errstr);
288a9237189SBaptiste Daroussin 			break;
289a9237189SBaptiste Daroussin 		case 'n':
290a9237189SBaptiste Daroussin 			if (strspn(optarg, "0123456789") != strlen(optarg)) {
291a9237189SBaptiste Daroussin 				name = optarg;
292a9237189SBaptiste Daroussin 				break;
293a9237189SBaptiste Daroussin 			}
294a9237189SBaptiste Daroussin 			id = strtonum(optarg, 0, LONG_MAX, &errstr);
295a9237189SBaptiste Daroussin 			if (errstr != NULL)
296a9237189SBaptiste Daroussin 				errx(EX_USAGE, "Bad id '%s': %s", optarg,
297a9237189SBaptiste Daroussin 				    errstr);
29891860967SBaptiste Daroussin 			break;
29911621f96SBaptiste Daroussin 		case 'H':
30011621f96SBaptiste Daroussin 			if (conf.fd != -1)
30111621f96SBaptiste Daroussin 				errx(EX_USAGE, "'-h' and '-H' are mutually "
30211621f96SBaptiste Daroussin 				    "exclusive options");
30311621f96SBaptiste Daroussin 			conf.precrypted = true;
30411621f96SBaptiste Daroussin 			if (strspn(optarg, "0123456789") != strlen(optarg))
30511621f96SBaptiste Daroussin 				errx(EX_USAGE, "'-H' expects a file descriptor");
30611621f96SBaptiste Daroussin 
30711621f96SBaptiste Daroussin 			conf.fd = strtonum(optarg, 0, INT_MAX, &errstr);
30811621f96SBaptiste Daroussin 			if (errstr != NULL)
30911621f96SBaptiste Daroussin 				errx(EX_USAGE, "Bad file descriptor '%s': %s",
31011621f96SBaptiste Daroussin 				    optarg, errstr);
31111621f96SBaptiste Daroussin 			break;
31211621f96SBaptiste Daroussin 		case 'h':
31311621f96SBaptiste Daroussin 			if (conf.fd != -1)
31411621f96SBaptiste Daroussin 				errx(EX_USAGE, "'-h' and '-H' are mutually "
31511621f96SBaptiste Daroussin 				    "exclusive options");
31611621f96SBaptiste Daroussin 
31711621f96SBaptiste Daroussin 			if (strcmp(optarg, "-") == 0)
31811621f96SBaptiste Daroussin 				conf.fd = '-';
31911621f96SBaptiste Daroussin 			else if (strspn(optarg, "0123456789") == strlen(optarg)) {
32011621f96SBaptiste Daroussin 				conf.fd = strtonum(optarg, 0, INT_MAX, &errstr);
32111621f96SBaptiste Daroussin 				if (errstr != NULL)
32211621f96SBaptiste Daroussin 					errx(EX_USAGE, "'-h' expects a "
32311621f96SBaptiste Daroussin 					    "file descriptor or '-'");
32411621f96SBaptiste Daroussin 			} else
32511621f96SBaptiste Daroussin 				errx(EX_USAGE, "'-h' expects a file "
32611621f96SBaptiste Daroussin 				    "descriptor or '-'");
32711621f96SBaptiste Daroussin 			break;
32891860967SBaptiste Daroussin 		case 'o':
329a46045bbSBaptiste Daroussin 			conf.checkduplicate = false;
33091860967SBaptiste Daroussin 			break;
3318daa3a67SBaptiste Daroussin 		case 'q':
3328daa3a67SBaptiste Daroussin 			conf.quiet = true;
3338daa3a67SBaptiste Daroussin 			break;
334d14c4847SBaptiste Daroussin 		case 'r':
335d14c4847SBaptiste Daroussin 			conf.deletehome = true;
336d14c4847SBaptiste Daroussin 			break;
33782a3c75aSBaptiste Daroussin 		default:
338d6f907dcSJoerg Wunsch 			addarg(&arglist, ch, optarg);
33982a3c75aSBaptiste Daroussin 			break;
34082a3c75aSBaptiste Daroussin 		}
341d6f907dcSJoerg Wunsch 		optarg = NULL;
342d6f907dcSJoerg Wunsch 	}
343d6f907dcSJoerg Wunsch 
344a9237189SBaptiste Daroussin 	if (name != NULL && strlen(name) >= MAXLOGNAME)
345a9237189SBaptiste Daroussin 		errx(EX_USAGE, "name too long: %s", name);
346a9237189SBaptiste Daroussin 
347d6f907dcSJoerg Wunsch 	/*
34848aee7f3SJoerg Wunsch 	 * Must be root to attempt an update
34948aee7f3SJoerg Wunsch 	 */
350363cefdbSBaptiste Daroussin 	if (geteuid() != 0 && mode != M_PRINT && mode != M_NEXT && !conf.dryrun)
3511dcc6ec7SPhilippe Charnier 		errx(EX_NOPERM, "you must be root to run this program");
35248aee7f3SJoerg Wunsch 
35348aee7f3SJoerg Wunsch 	/*
354d6f907dcSJoerg Wunsch 	 * We should immediately look for the -q 'quiet' switch so that we
355d6f907dcSJoerg Wunsch 	 * don't bother with extraneous errors
356d6f907dcSJoerg Wunsch 	 */
3578daa3a67SBaptiste Daroussin 	if (conf.quiet)
3581a37aa56SDavid E. O'Brien 		freopen(_PATH_DEVNULL, "w", stderr);
359d6f907dcSJoerg Wunsch 
360d6f907dcSJoerg Wunsch 	/*
3615f12594aSDavid Nugent 	 * Set our base working path if not overridden
3625f12594aSDavid Nugent 	 */
3635f12594aSDavid Nugent 
3645f12594aSDavid Nugent 	if (config == NULL) {	/* Only override config location if -C not specified */
3652cc63cd1SBaptiste Daroussin 		asprintf(&config, "%s/pw.conf", conf.etcpath);
36619741915SBaptiste Daroussin 		if (config == NULL)
36719741915SBaptiste Daroussin 			errx(EX_OSERR, "out of memory");
3685f12594aSDavid Nugent 	}
3695f12594aSDavid Nugent 
3705f12594aSDavid Nugent 	/*
371d6f907dcSJoerg Wunsch 	 * Now, let's do the common initialisation
372d6f907dcSJoerg Wunsch 	 */
3732cc63cd1SBaptiste Daroussin 	conf.userconf = read_userconfig(config);
3745e91a8acSNate Williams 
375a9237189SBaptiste Daroussin 	ch = funcs[which] (mode, name, id, &arglist);
376f1d684faSDavid Nugent 
377f1d684faSDavid Nugent 	/*
378f1d684faSDavid Nugent 	 * If everything went ok, and we've been asked to update
379f1d684faSDavid Nugent 	 * the NIS maps, then do it now
380f1d684faSDavid Nugent 	 */
38182a3c75aSBaptiste Daroussin 	if (ch == EXIT_SUCCESS && nis) {
382f1d684faSDavid Nugent 		pid_t	pid;
383f1d684faSDavid Nugent 
384f1d684faSDavid Nugent 		fflush(NULL);
385f1d684faSDavid Nugent 		if (chdir(_PATH_YP) == -1)
3861dcc6ec7SPhilippe Charnier 			warn("chdir(" _PATH_YP ")");
387f1d684faSDavid Nugent 		else if ((pid = fork()) == -1)
3881dcc6ec7SPhilippe Charnier 			warn("fork()");
389f1d684faSDavid Nugent 		else if (pid == 0) {
390f1d684faSDavid Nugent 			/* Is make anywhere else? */
3917bc6d015SBrian Somers 			execlp("/usr/bin/make", "make", (char *)NULL);
392f1d684faSDavid Nugent 			_exit(1);
393f1d684faSDavid Nugent 		} else {
394f1d684faSDavid Nugent 			int   i;
395f1d684faSDavid Nugent 			waitpid(pid, &i, 0);
396f1d684faSDavid Nugent 			if ((i = WEXITSTATUS(i)) != 0)
3971dcc6ec7SPhilippe Charnier 				errx(ch, "make exited with status %d", i);
398f1d684faSDavid Nugent 			else
3992cc63cd1SBaptiste Daroussin 				pw_log(conf.userconf, mode, which, "NIS maps updated");
400f1d684faSDavid Nugent 		}
401f1d684faSDavid Nugent 	}
402f1d684faSDavid Nugent 	return ch;
403d6f907dcSJoerg Wunsch }
404d6f907dcSJoerg Wunsch 
4055e91a8acSNate Williams 
406d6f907dcSJoerg Wunsch static int
407d6f907dcSJoerg Wunsch getindex(const char *words[], const char *word)
408d6f907dcSJoerg Wunsch {
409d6f907dcSJoerg Wunsch 	int             i = 0;
410d6f907dcSJoerg Wunsch 
411d6f907dcSJoerg Wunsch 	while (words[i]) {
412d6f907dcSJoerg Wunsch 		if (strcmp(words[i], word) == 0)
413d6f907dcSJoerg Wunsch 			return i;
414d6f907dcSJoerg Wunsch 		i++;
415d6f907dcSJoerg Wunsch 	}
416d6f907dcSJoerg Wunsch 	return -1;
417d6f907dcSJoerg Wunsch }
418d6f907dcSJoerg Wunsch 
419d6f907dcSJoerg Wunsch 
420d6f907dcSJoerg Wunsch /*
421d6f907dcSJoerg Wunsch  * This is probably an overkill for a cmdline help system, but it reflects
422d6f907dcSJoerg Wunsch  * the complexity of the command line.
423d6f907dcSJoerg Wunsch  */
424d6f907dcSJoerg Wunsch 
425d6f907dcSJoerg Wunsch static void
426d6f907dcSJoerg Wunsch cmdhelp(int mode, int which)
427d6f907dcSJoerg Wunsch {
428d6f907dcSJoerg Wunsch 	if (which == -1)
4292399cd14SDavid Nugent 		fprintf(stderr, "usage:\n  pw [user|group|lock|unlock] [add|del|mod|show|next] [help|switches/values]\n");
430d6f907dcSJoerg Wunsch 	else if (mode == -1)
4312399cd14SDavid Nugent 		fprintf(stderr, "usage:\n  pw %s [add|del|mod|show|next] [help|switches/values]\n", Which[which]);
432d6f907dcSJoerg Wunsch 	else {
433d6f907dcSJoerg Wunsch 
434d6f907dcSJoerg Wunsch 		/*
435d6f907dcSJoerg Wunsch 		 * We need to give mode specific help
436d6f907dcSJoerg Wunsch 		 */
437d6f907dcSJoerg Wunsch 		static const char *help[W_NUM][M_NUM] =
438d6f907dcSJoerg Wunsch 		{
439d6f907dcSJoerg Wunsch 			{
4401dcc6ec7SPhilippe Charnier 				"usage: pw useradd [name] [switches]\n"
4415f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
442ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
443d6f907dcSJoerg Wunsch 				"\t-C config      configuration file\n"
444d6f907dcSJoerg Wunsch 				"\t-q             quiet operation\n"
445d6f907dcSJoerg Wunsch 				"  Adding users:\n"
446d6f907dcSJoerg Wunsch 				"\t-n name        login name\n"
447d6f907dcSJoerg Wunsch 				"\t-u uid         user id\n"
448d6f907dcSJoerg Wunsch 				"\t-c comment     user name/comment\n"
449d6f907dcSJoerg Wunsch 				"\t-d directory   home directory\n"
450d6f907dcSJoerg Wunsch 				"\t-e date        account expiry date\n"
451d6f907dcSJoerg Wunsch 				"\t-p date        password expiry date\n"
452d6f907dcSJoerg Wunsch 				"\t-g grp         initial group\n"
453d6f907dcSJoerg Wunsch 				"\t-G grp1,grp2   additional groups\n"
454d6f907dcSJoerg Wunsch 				"\t-m [ -k dir ]  create and set up home\n"
4551dd634b0SLukas Ertl 				"\t-M mode        home directory permissions\n"
456d6f907dcSJoerg Wunsch 				"\t-s shell       name of login shell\n"
457d6f907dcSJoerg Wunsch 				"\t-o             duplicate uid ok\n"
458d6f907dcSJoerg Wunsch 				"\t-L class       user class\n"
459d6f907dcSJoerg Wunsch 				"\t-h fd          read password on fd\n"
46087d6b5caSIan Dowse 				"\t-H fd          read encrypted password on fd\n"
461f1d684faSDavid Nugent 				"\t-Y             update NIS maps\n"
46248aee7f3SJoerg Wunsch 				"\t-N             no update\n"
463d6f907dcSJoerg Wunsch 				"  Setting defaults:\n"
4645f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
465ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
466d6f907dcSJoerg Wunsch 			        "\t-D             set user defaults\n"
467d6f907dcSJoerg Wunsch 				"\t-b dir         default home root dir\n"
468d6f907dcSJoerg Wunsch 				"\t-e period      default expiry period\n"
469d6f907dcSJoerg Wunsch 				"\t-p period      default password change period\n"
470d6f907dcSJoerg Wunsch 				"\t-g group       default group\n"
471d6f907dcSJoerg Wunsch 				"\t-G grp1,grp2   additional groups\n"
472d6f907dcSJoerg Wunsch 				"\t-L class       default user class\n"
473d6f907dcSJoerg Wunsch 				"\t-k dir         default home skeleton\n"
47485204142SLukas Ertl 				"\t-M mode        home directory permissions\n"
475d6f907dcSJoerg Wunsch 				"\t-u min,max     set min,max uids\n"
476d6f907dcSJoerg Wunsch 				"\t-i min,max     set min,max gids\n"
477d6f907dcSJoerg Wunsch 				"\t-w method      set default password method\n"
478f1d684faSDavid Nugent 				"\t-s shell       default shell\n"
479f1d684faSDavid Nugent 				"\t-y path        set NIS passwd file path\n",
4801dcc6ec7SPhilippe Charnier 				"usage: pw userdel [uid|name] [switches]\n"
4815f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
482ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
483d6f907dcSJoerg Wunsch 				"\t-n name        login name\n"
484d6f907dcSJoerg Wunsch 				"\t-u uid         user id\n"
485f1d684faSDavid Nugent 				"\t-Y             update NIS maps\n"
486d6f907dcSJoerg Wunsch 				"\t-r             remove home & contents\n",
4871dcc6ec7SPhilippe Charnier 				"usage: pw usermod [uid|name] [switches]\n"
4885f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
489ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
490d6f907dcSJoerg Wunsch 				"\t-C config      configuration file\n"
491d6f907dcSJoerg Wunsch 				"\t-q             quiet operation\n"
492d6f907dcSJoerg Wunsch 				"\t-F             force add if no user\n"
493d6f907dcSJoerg Wunsch 				"\t-n name        login name\n"
494d6f907dcSJoerg Wunsch 				"\t-u uid         user id\n"
495d6f907dcSJoerg Wunsch 				"\t-c comment     user name/comment\n"
496d6f907dcSJoerg Wunsch 				"\t-d directory   home directory\n"
497d6f907dcSJoerg Wunsch 				"\t-e date        account expiry date\n"
498d6f907dcSJoerg Wunsch 				"\t-p date        password expiry date\n"
499d6f907dcSJoerg Wunsch 				"\t-g grp         initial group\n"
500d6f907dcSJoerg Wunsch 				"\t-G grp1,grp2   additional groups\n"
501d6f907dcSJoerg Wunsch 				"\t-l name        new login name\n"
502d6f907dcSJoerg Wunsch 				"\t-L class       user class\n"
503d6f907dcSJoerg Wunsch 				"\t-m [ -k dir ]  create and set up home\n"
5041dd634b0SLukas Ertl 				"\t-M mode        home directory permissions\n"
505d6f907dcSJoerg Wunsch 				"\t-s shell       name of login shell\n"
50648aee7f3SJoerg Wunsch 				"\t-w method      set new password using method\n"
50748aee7f3SJoerg Wunsch 				"\t-h fd          read password on fd\n"
50887d6b5caSIan Dowse 				"\t-H fd          read encrypted password on fd\n"
509f1d684faSDavid Nugent 				"\t-Y             update NIS maps\n"
51048aee7f3SJoerg Wunsch 				"\t-N             no update\n",
5111dcc6ec7SPhilippe Charnier 				"usage: pw usershow [uid|name] [switches]\n"
5125f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
513ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
514d6f907dcSJoerg Wunsch 				"\t-n name        login name\n"
515d6f907dcSJoerg Wunsch 				"\t-u uid         user id\n"
516d6f907dcSJoerg Wunsch 				"\t-F             force print\n"
51748aee7f3SJoerg Wunsch 				"\t-P             prettier format\n"
518f3522722SDavid Nugent 				"\t-a             print all users\n"
519f3522722SDavid Nugent 				"\t-7             print in v7 format\n",
5201dcc6ec7SPhilippe Charnier 				"usage: pw usernext [switches]\n"
5215f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
522ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
5233bfc59e8SBaptiste Daroussin 				"\t-C config      configuration file\n"
5243bfc59e8SBaptiste Daroussin 				"\t-q             quiet operation\n",
525ed6fd55aSDima Dorfman 				"usage pw: lock [switches]\n"
526ed6fd55aSDima Dorfman 				"\t-V etcdir      alternate /etc locations\n"
527ed6fd55aSDima Dorfman 				"\t-C config      configuration file\n"
528ed6fd55aSDima Dorfman 				"\t-q             quiet operation\n",
529ed6fd55aSDima Dorfman 				"usage pw: unlock [switches]\n"
530ed6fd55aSDima Dorfman 				"\t-V etcdir      alternate /etc locations\n"
531ed6fd55aSDima Dorfman 				"\t-C config      configuration file\n"
532ed6fd55aSDima Dorfman 				"\t-q             quiet operation\n"
533d6f907dcSJoerg Wunsch 			},
534d6f907dcSJoerg Wunsch 			{
5351dcc6ec7SPhilippe Charnier 				"usage: pw groupadd [group|gid] [switches]\n"
5365f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
537ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
538d6f907dcSJoerg Wunsch 				"\t-C config      configuration file\n"
539d6f907dcSJoerg Wunsch 				"\t-q             quiet operation\n"
540d6f907dcSJoerg Wunsch 				"\t-n group       group name\n"
541d6f907dcSJoerg Wunsch 				"\t-g gid         group id\n"
54248aee7f3SJoerg Wunsch 				"\t-M usr1,usr2   add users as group members\n"
54348aee7f3SJoerg Wunsch 				"\t-o             duplicate gid ok\n"
544f1d684faSDavid Nugent 				"\t-Y             update NIS maps\n"
54548aee7f3SJoerg Wunsch 				"\t-N             no update\n",
5461dcc6ec7SPhilippe Charnier 				"usage: pw groupdel [group|gid] [switches]\n"
5475f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
548ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
549d6f907dcSJoerg Wunsch 				"\t-n name        group name\n"
550f1d684faSDavid Nugent 				"\t-g gid         group id\n"
551f1d684faSDavid Nugent 				"\t-Y             update NIS maps\n",
5521dcc6ec7SPhilippe Charnier 				"usage: pw groupmod [group|gid] [switches]\n"
5535f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
554ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
555d6f907dcSJoerg Wunsch 				"\t-C config      configuration file\n"
556d6f907dcSJoerg Wunsch 				"\t-q             quiet operation\n"
557d6f907dcSJoerg Wunsch 				"\t-F             force add if not exists\n"
558d6f907dcSJoerg Wunsch 				"\t-n name        group name\n"
559d6f907dcSJoerg Wunsch 				"\t-g gid         group id\n"
56048aee7f3SJoerg Wunsch 				"\t-M usr1,usr2   replaces users as group members\n"
56148aee7f3SJoerg Wunsch 				"\t-m usr1,usr2   add users as group members\n"
562bc991a6dSSean Farley 				"\t-d usr1,usr2   delete users as group members\n"
56348aee7f3SJoerg Wunsch 				"\t-l name        new group name\n"
564f1d684faSDavid Nugent 				"\t-Y             update NIS maps\n"
56548aee7f3SJoerg Wunsch 				"\t-N             no update\n",
5661dcc6ec7SPhilippe Charnier 				"usage: pw groupshow [group|gid] [switches]\n"
5675f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
568ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
569d6f907dcSJoerg Wunsch 				"\t-n name        group name\n"
570d6f907dcSJoerg Wunsch 				"\t-g gid         group id\n"
571d6f907dcSJoerg Wunsch 				"\t-F             force print\n"
57248aee7f3SJoerg Wunsch 				"\t-P             prettier format\n"
57348aee7f3SJoerg Wunsch 				"\t-a             print all accounting groups\n",
5741dcc6ec7SPhilippe Charnier 				"usage: pw groupnext [switches]\n"
5755f12594aSDavid Nugent 				"\t-V etcdir      alternate /etc location\n"
576ac72be28SBaptiste Daroussin 				"\t-R rootir      alternate root directory\n"
57748aee7f3SJoerg Wunsch 				"\t-C config      configuration file\n"
5783bfc59e8SBaptiste Daroussin 				"\t-q             quiet operation\n"
579d6f907dcSJoerg Wunsch 			}
580d6f907dcSJoerg Wunsch 		};
581d6f907dcSJoerg Wunsch 
582cab0fb4eSKris Kennaway 		fprintf(stderr, "%s", help[which][mode]);
583d6f907dcSJoerg Wunsch 	}
58448aee7f3SJoerg Wunsch 	exit(EXIT_FAILURE);
585d6f907dcSJoerg Wunsch }
586d6f907dcSJoerg Wunsch 
587d6f907dcSJoerg Wunsch struct carg    *
588d6f907dcSJoerg Wunsch getarg(struct cargs * _args, int ch)
589d6f907dcSJoerg Wunsch {
5908a2ace2aSBaptiste Daroussin 	struct carg    *c;
5918a2ace2aSBaptiste Daroussin 
5928a2ace2aSBaptiste Daroussin 	if (_args == NULL)
5938a2ace2aSBaptiste Daroussin 		return (NULL);
5948a2ace2aSBaptiste Daroussin 
5958a2ace2aSBaptiste Daroussin 	c = LIST_FIRST(_args);
596d6f907dcSJoerg Wunsch 
597d6f907dcSJoerg Wunsch 	while (c != NULL && c->ch != ch)
598d0d78e13SBen Smithurst 		c = LIST_NEXT(c, list);
599d6f907dcSJoerg Wunsch 	return c;
600d6f907dcSJoerg Wunsch }
601d6f907dcSJoerg Wunsch 
602d6f907dcSJoerg Wunsch struct carg    *
603d6f907dcSJoerg Wunsch addarg(struct cargs * _args, int ch, char *argstr)
604d6f907dcSJoerg Wunsch {
605d6f907dcSJoerg Wunsch 	struct carg    *ca = malloc(sizeof(struct carg));
606d6f907dcSJoerg Wunsch 
607d6f907dcSJoerg Wunsch 	if (ca == NULL)
6081dcc6ec7SPhilippe Charnier 		errx(EX_OSERR, "out of memory");
609d6f907dcSJoerg Wunsch 	ca->ch = ch;
610d6f907dcSJoerg Wunsch 	ca->val = argstr;
611d6f907dcSJoerg Wunsch 	LIST_INSERT_HEAD(_args, ca, list);
612d6f907dcSJoerg Wunsch 	return ca;
613d6f907dcSJoerg Wunsch }
614