pw.c revision 30259
1133359Sobrien/*-
2133359Sobrien * Copyright (C) 1996
3133359Sobrien *	David L. Nugent.  All rights reserved.
4133359Sobrien *
5133359Sobrien * Redistribution and use in source and binary forms, with or without
6133359Sobrien * modification, are permitted provided that the following conditions
7133359Sobrien * are met:
8133359Sobrien * 1. Redistributions of source code must retain the above copyright
9133359Sobrien *    notice, this list of conditions and the following disclaimer.
10133359Sobrien * 2. Redistributions in binary form must reproduce the above copyright
11133359Sobrien *    notice, this list of conditions and the following disclaimer in the
12133359Sobrien *    documentation and/or other materials provided with the distribution.
13133359Sobrien *
14133359Sobrien * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
15133359Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16133359Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17133359Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
18133359Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19133359Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20133359Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21133359Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22133359Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23133359Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24133359Sobrien * SUCH DAMAGE.
25133359Sobrien */
26133359Sobrien
2768349Sobrien#ifndef lint
2868349Sobrienstatic const char rcsid[] =
29191771Sobrien	"$Id$";
30234449Sobrien#endif /* not lint */
31191771Sobrien
32191771Sobrien#include "pw.h"
3368349Sobrien#include <err.h>
3468349Sobrien#include <paths.h>
3568349Sobrien#include <sys/wait.h>
3668349Sobrien
3768349Sobrienconst char     *Modes[] = {"add", "del", "mod", "show", "next", NULL};
3868349Sobrienconst char     *Which[] = {"user", "group", NULL};
3968349Sobrienstatic const char *Combo1[] = {
4068349Sobrien  "useradd", "userdel", "usermod", "usershow", "usernext",
4168349Sobrien  "groupadd", "groupdel", "groupmod", "groupshow", "groupnext",
42186691Sobrien  NULL};
4368349Sobrienstatic const char *Combo2[] = {
4468349Sobrien  "adduser", "deluser", "moduser", "showuser", "nextuser",
45169942Sobrien  "addgroup", "delgroup", "modgroup", "showgroup", "nextgroup",
46169942SobrienNULL};
4768349Sobrien
48169942Sobrienstatic struct cargs arglist;
49186691Sobrien
50234449Sobrienstatic int      getindex(const char *words[], const char *word);
51234449Sobrienstatic void     cmdhelp(int mode, int which);
52186691Sobrien
53159764Sobrien
5468349Sobrienint
55133359Sobrienmain(int argc, char *argv[])
5668349Sobrien{
57159764Sobrien	int             ch;
58159764Sobrien	int             mode = -1;
59133359Sobrien	int             which = -1;
60133359Sobrien	struct userconf *cnf;
61133359Sobrien
62133359Sobrien	static const char *opts[W_NUM][M_NUM] =
63275671Sdelphij	{
64275671Sdelphij		{ /* user */
65275671Sdelphij			"C:qn:u:c:d:e:p:g:G:mk:s:oL:i:w:h:Db:NPy:Y",
66275671Sdelphij			"C:qn:u:rY",
67275671Sdelphij			"C:qn:u:c:d:e:p:g:G:ml:k:s:w:L:h:FNPY",
68275671Sdelphij			"C:qn:u:FPa",
69275671Sdelphij			"C:q"
70275671Sdelphij		},
71275671Sdelphij		{ /* grp  */
72275671Sdelphij			"C:qn:g:h:M:pNPY",
73275671Sdelphij			"C:qn:g:Y",
74275671Sdelphij			"C:qn:g:l:h:FM:m:NPY",
75133359Sobrien			"C:qn:g:FPa",
76103373Sobrien			"C:q"
7768349Sobrien		 }
7868349Sobrien	};
7968349Sobrien
8068349Sobrien	static int      (*funcs[W_NUM]) (struct userconf * _cnf, int _mode, struct cargs * _args) =
8168349Sobrien	{			/* Request handlers */
8268349Sobrien		pw_user,
8368349Sobrien		pw_group
8468349Sobrien	};
8568349Sobrien
8668349Sobrien	umask(0);		/* We wish to handle this manually */
8768349Sobrien	LIST_INIT(&arglist);
8868349Sobrien
8968349Sobrien	/*
9068349Sobrien	 * Break off the first couple of words to determine what exactly
9168349Sobrien	 * we're being asked to do
9268349Sobrien	 */
9368349Sobrien	while (argc > 1 && *argv[1] != '-') {
94133359Sobrien		int             tmp;
95103373Sobrien
9668349Sobrien		if ((tmp = getindex(Modes, argv[1])) != -1)
9768349Sobrien			mode = tmp;
9868349Sobrien		else if ((tmp = getindex(Which, argv[1])) != -1)
9968349Sobrien			which = tmp;
10068349Sobrien		else if ((tmp = getindex(Combo1, argv[1])) != -1 || (tmp = getindex(Combo2, argv[1])) != -1) {
10168349Sobrien			which = tmp / M_NUM;
10268349Sobrien			mode = tmp % M_NUM;
10368349Sobrien		} else if (strcmp(argv[1], "help") == 0)
10468349Sobrien			cmdhelp(mode, which);
10568349Sobrien		else if (which != -1 && mode != -1 && arglist.lh_first == NULL)
10668349Sobrien			addarg(&arglist, 'n', argv[1]);
10768349Sobrien		else
10868349Sobrien			errx(EX_USAGE, "unknown keyword `%s'", argv[1]);
10968349Sobrien		++argv;
11068349Sobrien		--argc;
11168349Sobrien	}
11268349Sobrien
11368349Sobrien	/*
11468349Sobrien	 * Bail out unless the user is specific!
115133359Sobrien	 */
116103373Sobrien	if (mode == -1 || which == -1)
11768349Sobrien		cmdhelp(mode, which);
11868349Sobrien
11968349Sobrien	/*
12068349Sobrien	 * We know which mode we're in and what we're about to do, so now
12168349Sobrien	 * let's dispatch the remaining command line args in a genric way.
12268349Sobrien	 */
12368349Sobrien	optarg = NULL;
12468349Sobrien
12568349Sobrien	while ((ch = getopt(argc, argv, opts[which][mode])) != -1) {
12668349Sobrien		if (ch == '?')
12768349Sobrien			errx(EX_USAGE, NULL);
12868349Sobrien		else
12968349Sobrien			addarg(&arglist, ch, optarg);
13068349Sobrien		optarg = NULL;
13168349Sobrien	}
13268349Sobrien
13368349Sobrien	/*
13468349Sobrien	 * Must be root to attempt an update
13568349Sobrien	 */
13668349Sobrien	if (geteuid() != 0 && mode != M_PRINT && mode != M_NEXT && getarg(&arglist, 'N')==NULL)
13768349Sobrien		errx(EX_NOPERM, "you must be root to run this program");
13868349Sobrien
13968349Sobrien	/*
140186691Sobrien	 * We should immediately look for the -q 'quiet' switch so that we
141186691Sobrien	 * don't bother with extraneous errors
142159764Sobrien	 */
143159764Sobrien	if (getarg(&arglist, 'q') != NULL)
144186691Sobrien		freopen("/dev/null", "w", stderr);
145186691Sobrien
146159764Sobrien	/*
147159764Sobrien	 * Now, let's do the common initialisation
148159764Sobrien	 */
149159764Sobrien	cnf = read_userconfig(getarg(&arglist, 'C') ? getarg(&arglist, 'C')->val : NULL);
150186691Sobrien	ch = funcs[which] (cnf, mode, &arglist);
151186691Sobrien
15268349Sobrien	/*
153186691Sobrien	 * If everything went ok, and we've been asked to update
154186691Sobrien	 * the NIS maps, then do it now
155111658Sobrien	 */
156186691Sobrien	if (ch == EXIT_SUCCESS && getarg(&arglist, 'Y') != NULL) {
157186691Sobrien		pid_t	pid;
158186691Sobrien
159234449Sobrien		fflush(NULL);
160186691Sobrien		if (chdir(_PATH_YP) == -1)
161186691Sobrien			warn("chdir(" _PATH_YP ")");
162186691Sobrien		else if ((pid = fork()) == -1)
163186691Sobrien			warn("fork()");
164186691Sobrien		else if (pid == 0) {
165186691Sobrien			/* Is make anywhere else? */
166186691Sobrien			execlp("/usr/bin/make", "make", NULL);
16768349Sobrien			_exit(1);
168186691Sobrien		} else {
169186691Sobrien			int   i;
170111658Sobrien			waitpid(pid, &i, 0);
171186691Sobrien			if ((i = WEXITSTATUS(i)) != 0)
172186691Sobrien				errx(ch, "make exited with status %d", i);
173186691Sobrien			else
174186691Sobrien				pw_log(cnf, mode, which, "NIS maps updated");
175186691Sobrien		}
176186691Sobrien	}
177186691Sobrien	return ch;
178186691Sobrien}
179186691Sobrien
180186691Sobrienstatic int
181186691Sobriengetindex(const char *words[], const char *word)
182186691Sobrien{
183186691Sobrien	int             i = 0;
184186691Sobrien
185186691Sobrien	while (words[i]) {
186186691Sobrien		if (strcmp(words[i], word) == 0)
187159764Sobrien			return i;
188186691Sobrien		i++;
189186691Sobrien	}
190186691Sobrien	return -1;
191186691Sobrien}
192186691Sobrien
193133359Sobrien
194186691Sobrien/*
195186691Sobrien * This is probably an overkill for a cmdline help system, but it reflects
196186691Sobrien * the complexity of the command line.
197186691Sobrien */
198186691Sobrien
199186691Sobrienstatic void
200186691Sobriencmdhelp(int mode, int which)
201186691Sobrien{
202186691Sobrien	if (which == -1)
203186691Sobrien		fprintf(stderr, "usage: pw [user|group] [add|del|mod|show|next] [ help | switches/values ]\n");
204186691Sobrien	else if (mode == -1)
20568349Sobrien		fprintf(stderr, "usage: pw %s [add|del|mod|show|next] [ help | switches/values ]\n", Which[which]);
206186691Sobrien	else {
207186691Sobrien
208186691Sobrien		/*
209186691Sobrien		 * We need to give mode specific help
210186691Sobrien		 */
211186691Sobrien		static const char *help[W_NUM][M_NUM] =
212186691Sobrien		{
213186691Sobrien			{
214186691Sobrien				"usage: pw useradd [name] [switches]\n"
215186691Sobrien				"\t-C config      configuration file\n"
216186691Sobrien				"\t-q             quiet operation\n"
217186691Sobrien				"  Adding users:\n"
21868349Sobrien				"\t-n name        login name\n"
21968349Sobrien				"\t-u uid         user id\n"
220186691Sobrien				"\t-c comment     user name/comment\n"
221186691Sobrien				"\t-d directory   home directory\n"
222186691Sobrien				"\t-e date        account expiry date\n"
223186691Sobrien				"\t-p date        password expiry date\n"
224186691Sobrien				"\t-g grp         initial group\n"
225186691Sobrien				"\t-G grp1,grp2   additional groups\n"
226186691Sobrien				"\t-m [ -k dir ]  create and set up home\n"
227186691Sobrien				"\t-s shell       name of login shell\n"
228186691Sobrien				"\t-o             duplicate uid ok\n"
229186691Sobrien				"\t-L class       user class\n"
230186691Sobrien				"\t-h fd          read password on fd\n"
231186691Sobrien				"\t-Y             update NIS maps\n"
232186691Sobrien				"\t-N             no update\n"
233186691Sobrien				"  Setting defaults:\n"
234186691Sobrien				"\t-D             set user defaults\n"
235186691Sobrien				"\t-b dir         default home root dir\n"
23668349Sobrien				"\t-e period      default expiry period\n"
23768349Sobrien				"\t-p period      default password change period\n"
23868349Sobrien				"\t-g group       default group\n"
239186691Sobrien				"\t-G grp1,grp2   additional groups\n"
240186691Sobrien				"\t-L class       default user class\n"
241186691Sobrien				"\t-k dir         default home skeleton\n"
242186691Sobrien				"\t-u min,max     set min,max uids\n"
243186691Sobrien				"\t-i min,max     set min,max gids\n"
244186691Sobrien				"\t-w method      set default password method\n"
245186691Sobrien				"\t-s shell       default shell\n"
246186691Sobrien				"\t-y path        set NIS passwd file path\n",
247186691Sobrien				"usage: pw userdel [uid|name] [switches]\n"
248186691Sobrien				"\t-n name        login name\n"
249186691Sobrien				"\t-u uid         user id\n"
250186691Sobrien				"\t-Y             update NIS maps\n"
251159764Sobrien				"\t-r             remove home & contents\n",
25268349Sobrien				"usage: pw usermod [uid|name] [switches]\n"
25368349Sobrien				"\t-C config      configuration file\n"
25468349Sobrien				"\t-q             quiet operation\n"
25568349Sobrien				"\t-F             force add if no user\n"
25668349Sobrien				"\t-n name        login name\n"
257186691Sobrien				"\t-u uid         user id\n"
25868349Sobrien				"\t-c comment     user name/comment\n"
25968349Sobrien				"\t-d directory   home directory\n"
26068349Sobrien				"\t-e date        account expiry date\n"
26168349Sobrien				"\t-p date        password expiry date\n"
26268349Sobrien				"\t-g grp         initial group\n"
26368349Sobrien				"\t-G grp1,grp2   additional groups\n"
26468349Sobrien				"\t-l name        new login name\n"
26568349Sobrien				"\t-L class       user class\n"
26668349Sobrien				"\t-m [ -k dir ]  create and set up home\n"
26768349Sobrien				"\t-s shell       name of login shell\n"
26868349Sobrien				"\t-w method      set new password using method\n"
26968349Sobrien				"\t-h fd          read password on fd\n"
27068349Sobrien				"\t-Y             update NIS maps\n"
271186691Sobrien				"\t-N             no update\n",
272186691Sobrien				"usage: pw usershow [uid|name] [switches]\n"
273186691Sobrien				"\t-n name        login name\n"
274186691Sobrien				"\t-u uid         user id\n"
275186691Sobrien				"\t-F             force print\n"
276186691Sobrien				"\t-P             prettier format\n"
277186691Sobrien				"\t-a             print all users\n",
278186691Sobrien				"usage: pw usernext [switches]\n"
27968349Sobrien				"\t-C config      configuration file\n"
28068349Sobrien			},
28168349Sobrien			{
28268349Sobrien				"usage: pw groupadd [group|gid] [switches]\n"
28368349Sobrien				"\t-C config      configuration file\n"
28468349Sobrien				"\t-q             quiet operation\n"
28568349Sobrien				"\t-n group       group name\n"
28668349Sobrien				"\t-g gid         group id\n"
287103373Sobrien				"\t-M usr1,usr2   add users as group members\n"
288103373Sobrien				"\t-o             duplicate gid ok\n"
289103373Sobrien				"\t-Y             update NIS maps\n"
290103373Sobrien				"\t-N             no update\n",
291103373Sobrien				"usage: pw groupdel [group|gid] [switches]\n"
292186691Sobrien				"\t-n name        group name\n"
293103373Sobrien				"\t-g gid         group id\n"
294103373Sobrien				"\t-Y             update NIS maps\n",
295103373Sobrien				"usage: pw groupmod [group|gid] [switches]\n"
296103373Sobrien				"\t-C config      configuration file\n"
297103373Sobrien				"\t-q             quiet operation\n"
298234449Sobrien				"\t-F             force add if not exists\n"
299234449Sobrien				"\t-n name        group name\n"
300234449Sobrien				"\t-g gid         group id\n"
301234449Sobrien				"\t-M usr1,usr2   replaces users as group members\n"
302234449Sobrien				"\t-m usr1,usr2   add users as group members\n"
303159764Sobrien				"\t-l name        new group name\n"
304133359Sobrien				"\t-Y             update NIS maps\n"
305186691Sobrien				"\t-N             no update\n",
306169942Sobrien				"usage: pw groupshow [group|gid] [switches]\n"
30768349Sobrien				"\t-n name        group name\n"
30868349Sobrien				"\t-g gid         group id\n"
30968349Sobrien				"\t-F             force print\n"
310133359Sobrien				"\t-P             prettier format\n"
311133359Sobrien				"\t-a             print all accounting groups\n",
312133359Sobrien				"usage: pw groupnext [switches]\n"
31368349Sobrien				"\t-C config      configuration file\n"
314159764Sobrien			}
315133359Sobrien		};
316133359Sobrien
317133359Sobrien		fprintf(stderr, help[which][mode]);
318133359Sobrien	}
319159764Sobrien	exit(EXIT_FAILURE);
32068349Sobrien}
32168349Sobrien
32268349Sobrienstruct carg    *
32368349Sobriengetarg(struct cargs * _args, int ch)
324234449Sobrien{
325133359Sobrien	struct carg    *c = _args->lh_first;
326133359Sobrien
327133359Sobrien	while (c != NULL && c->ch != ch)
328159764Sobrien		c = c->list.le_next;
329133359Sobrien	return c;
330133359Sobrien}
331133359Sobrien
332234449Sobrienstruct carg    *
333234449Sobrienaddarg(struct cargs * _args, int ch, char *argstr)
334169942Sobrien{
335234449Sobrien	struct carg    *ca = malloc(sizeof(struct carg));
336169942Sobrien
337169942Sobrien	if (ca == NULL)
338169942Sobrien		errx(EX_OSERR, "out of memory");
339159764Sobrien	ca->ch = ch;
34068349Sobrien	ca->val = argstr;
34168349Sobrien	LIST_INSERT_HEAD(_args, ca, list);
34268349Sobrien	return ca;
34368349Sobrien}
34468349Sobrien