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