120253Sjoerg/*- 220302Sjoerg * Copyright (C) 1996 320302Sjoerg * David L. Nugent. All rights reserved. 420253Sjoerg * 520253Sjoerg * Redistribution and use in source and binary forms, with or without 620253Sjoerg * modification, are permitted provided that the following conditions 720253Sjoerg * are met: 820253Sjoerg * 1. Redistributions of source code must retain the above copyright 920302Sjoerg * notice, this list of conditions and the following disclaimer. 1020253Sjoerg * 2. Redistributions in binary form must reproduce the above copyright 1120253Sjoerg * notice, this list of conditions and the following disclaimer in the 1220253Sjoerg * documentation and/or other materials provided with the distribution. 1320253Sjoerg * 1420302Sjoerg * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND 1520253Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1620253Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1720302Sjoerg * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE 1820253Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1920253Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2020253Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2120253Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2220253Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2320253Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2420253Sjoerg * SUCH DAMAGE. 2520253Sjoerg */ 2620253Sjoerg 2730259Scharnier#ifndef lint 2830259Scharnierstatic const char rcsid[] = 2950479Speter "$FreeBSD$"; 3030259Scharnier#endif /* not lint */ 3130259Scharnier 3230259Scharnier#include <err.h> 3338112Snate#include <fcntl.h> 3461957Sache#include <locale.h> 35287084Sbapt#include <string.h> 36287084Sbapt#include <sysexits.h> 37287084Sbapt#include <unistd.h> 38287084Sbapt 3944229Sdavidn#include "pw.h" 4020253Sjoerg 4152512Sdavidnconst char *Modes[] = { 4252512Sdavidn "add", "del", "mod", "show", "next", 4352512Sdavidn NULL}; 4420253Sjoergconst char *Which[] = {"user", "group", NULL}; 4520267Sjoergstatic const char *Combo1[] = { 4620267Sjoerg "useradd", "userdel", "usermod", "usershow", "usernext", 4752512Sdavidn "lock", "unlock", 4820267Sjoerg "groupadd", "groupdel", "groupmod", "groupshow", "groupnext", 4920267Sjoerg NULL}; 5020267Sjoergstatic const char *Combo2[] = { 5120267Sjoerg "adduser", "deluser", "moduser", "showuser", "nextuser", 5252512Sdavidn "lock", "unlock", 5320267Sjoerg "addgroup", "delgroup", "modgroup", "showgroup", "nextgroup", 5452512Sdavidn NULL}; 5520253Sjoerg 5644229Sdavidnstruct pwf PWF = 5744229Sdavidn{ 58285092Sbapt PWF_REGULAR, 5944229Sdavidn setpwent, 6044229Sdavidn endpwent, 6144229Sdavidn getpwent, 6244229Sdavidn getpwuid, 6344229Sdavidn getpwnam, 6444229Sdavidn setgrent, 6544229Sdavidn endgrent, 6644229Sdavidn getgrent, 6744229Sdavidn getgrgid, 6844229Sdavidn getgrnam, 6944229Sdavidn 7044229Sdavidn}; 7144229Sdavidnstruct pwf VPWF = 7244229Sdavidn{ 73285092Sbapt PWF_ALT, 7444229Sdavidn vsetpwent, 7544229Sdavidn vendpwent, 7644229Sdavidn vgetpwent, 7744229Sdavidn vgetpwuid, 7844229Sdavidn vgetpwnam, 7944229Sdavidn vsetgrent, 8044229Sdavidn vendgrent, 8144229Sdavidn vgetgrent, 8244229Sdavidn vgetgrgid, 8344229Sdavidn vgetgrnam, 8444229Sdavidn}; 8544229Sdavidn 86287084Sbaptstatic int (*cmdfunc[W_NUM][M_NUM])(int argc, char **argv, char *_name) = { 87287084Sbapt { /* user */ 88287084Sbapt pw_user_add, 89287084Sbapt pw_user_del, 90287084Sbapt pw_user_mod, 91287084Sbapt pw_user_show, 92287084Sbapt pw_user_next, 93287084Sbapt pw_user_lock, 94287084Sbapt pw_user_unlock, 95287084Sbapt }, 96287084Sbapt { /* group */ 97287084Sbapt pw_group_add, 98287084Sbapt pw_group_del, 99287084Sbapt pw_group_mod, 100287084Sbapt pw_group_show, 101287084Sbapt pw_group_next, 102287084Sbapt } 103287084Sbapt}; 104287084Sbapt 105285092Sbaptstruct pwconf conf; 106285092Sbapt 107287084Sbaptstatic int getindex(const char *words[], const char *word); 108287084Sbaptstatic void cmdhelp(int mode, int which); 10920253Sjoerg 11020253Sjoergint 11120253Sjoergmain(int argc, char *argv[]) 11220253Sjoerg{ 113287084Sbapt int mode = -1, which = -1, tmp; 114285092Sbapt struct stat st; 115287084Sbapt char arg, *arg1; 116285092Sbapt bool relocated, nis; 11720253Sjoerg 118287084Sbapt arg1 = NULL; 119285092Sbapt relocated = nis = false; 120285092Sbapt memset(&conf, 0, sizeof(conf)); 121287084Sbapt strlcpy(conf.rootdir, "/", sizeof(conf.rootdir)); 122285092Sbapt strlcpy(conf.etcpath, _PATH_PWD, sizeof(conf.etcpath)); 123285536Sbapt conf.checkduplicate = true; 124287084Sbapt conf.fd = -1; 125285092Sbapt 126287084Sbapt setlocale(LC_ALL, ""); 12720253Sjoerg 12820253Sjoerg /* 12920253Sjoerg * Break off the first couple of words to determine what exactly 13020253Sjoerg * we're being asked to do 13120253Sjoerg */ 13244229Sdavidn while (argc > 1) { 13344229Sdavidn if (*argv[1] == '-') { 13444229Sdavidn /* 13544229Sdavidn * Special case, allow pw -V<dir> <operation> [args] for scripts etc. 13644229Sdavidn */ 137285092Sbapt arg = argv[1][1]; 138285092Sbapt if (arg == 'V' || arg == 'R') { 139285092Sbapt if (relocated) 140285092Sbapt errx(EXIT_FAILURE, "Both '-R' and '-V' " 141285092Sbapt "specified, only one accepted"); 142285092Sbapt relocated = true; 14344229Sdavidn optarg = &argv[1][2]; 14444229Sdavidn if (*optarg == '\0') { 145285092Sbapt if (stat(argv[2], &st) != 0) 146285092Sbapt errx(EX_OSFILE, \ 147285092Sbapt "no such directory `%s'", 148285092Sbapt argv[2]); 149285092Sbapt if (!S_ISDIR(st.st_mode)) 150285092Sbapt errx(EX_OSFILE, "`%s' not a " 151285092Sbapt "directory", argv[2]); 15244229Sdavidn optarg = argv[2]; 15344229Sdavidn ++argv; 15444229Sdavidn --argc; 15544229Sdavidn } 156285092Sbapt memcpy(&PWF, &VPWF, sizeof PWF); 157285092Sbapt if (arg == 'R') { 158285092Sbapt strlcpy(conf.rootdir, optarg, 159285092Sbapt sizeof(conf.rootdir)); 160285092Sbapt PWF._altdir = PWF_ROOTDIR; 161285092Sbapt } 162285092Sbapt snprintf(conf.etcpath, sizeof(conf.etcpath), 163285092Sbapt "%s%s", optarg, arg == 'R' ? "/etc" : ""); 16444231Sdavidn } else 16544231Sdavidn break; 16644229Sdavidn } 16761760Sdavidn else if (mode == -1 && (tmp = getindex(Modes, argv[1])) != -1) 16820253Sjoerg mode = tmp; 16961760Sdavidn else if (which == -1 && (tmp = getindex(Which, argv[1])) != -1) 17020253Sjoerg which = tmp; 17161760Sdavidn else if ((mode == -1 && which == -1) && 17261760Sdavidn ((tmp = getindex(Combo1, argv[1])) != -1 || 17361760Sdavidn (tmp = getindex(Combo2, argv[1])) != -1)) { 17420253Sjoerg which = tmp / M_NUM; 17520253Sjoerg mode = tmp % M_NUM; 17661760Sdavidn } else if (strcmp(argv[1], "help") == 0 && argv[2] == NULL) 17720253Sjoerg cmdhelp(mode, which); 178287084Sbapt else if (which != -1 && mode != -1) 179287084Sbapt arg1 = argv[1]; 180287084Sbapt else 18130259Scharnier errx(EX_USAGE, "unknown keyword `%s'", argv[1]); 18220253Sjoerg ++argv; 18320253Sjoerg --argc; 18420253Sjoerg } 18520253Sjoerg 18620253Sjoerg /* 18720253Sjoerg * Bail out unless the user is specific! 18820253Sjoerg */ 18920253Sjoerg if (mode == -1 || which == -1) 19020253Sjoerg cmdhelp(mode, which); 19120253Sjoerg 192287084Sbapt conf.rootfd = open(conf.rootdir, O_DIRECTORY|O_CLOEXEC); 193287084Sbapt if (conf.rootfd == -1) 194287084Sbapt errx(EXIT_FAILURE, "Unable to open '%s'", conf.rootdir); 19520253Sjoerg 196287084Sbapt return (cmdfunc[which][mode](argc, argv, arg1)); 19720253Sjoerg} 19820253Sjoerg 19938112Snate 20020253Sjoergstatic int 20120253Sjoerggetindex(const char *words[], const char *word) 20220253Sjoerg{ 203287084Sbapt int i = 0; 20420253Sjoerg 20520253Sjoerg while (words[i]) { 20620253Sjoerg if (strcmp(words[i], word) == 0) 207287084Sbapt return (i); 20820253Sjoerg i++; 20920253Sjoerg } 210287084Sbapt return (-1); 21120253Sjoerg} 21220253Sjoerg 21320253Sjoerg 21420253Sjoerg/* 21520253Sjoerg * This is probably an overkill for a cmdline help system, but it reflects 21620253Sjoerg * the complexity of the command line. 21720253Sjoerg */ 21820253Sjoerg 21920253Sjoergstatic void 22020253Sjoergcmdhelp(int mode, int which) 22120253Sjoerg{ 22220253Sjoerg if (which == -1) 22352512Sdavidn fprintf(stderr, "usage:\n pw [user|group|lock|unlock] [add|del|mod|show|next] [help|switches/values]\n"); 22420253Sjoerg else if (mode == -1) 22552512Sdavidn fprintf(stderr, "usage:\n pw %s [add|del|mod|show|next] [help|switches/values]\n", Which[which]); 22620253Sjoerg else { 22720253Sjoerg 22820253Sjoerg /* 22920253Sjoerg * We need to give mode specific help 23020253Sjoerg */ 23120253Sjoerg static const char *help[W_NUM][M_NUM] = 23220253Sjoerg { 23320253Sjoerg { 23430259Scharnier "usage: pw useradd [name] [switches]\n" 23544229Sdavidn "\t-V etcdir alternate /etc location\n" 236289436Swblock "\t-R rootdir alternate root directory\n" 23720253Sjoerg "\t-C config configuration file\n" 23820253Sjoerg "\t-q quiet operation\n" 23920253Sjoerg " Adding users:\n" 24020253Sjoerg "\t-n name login name\n" 24120253Sjoerg "\t-u uid user id\n" 24220253Sjoerg "\t-c comment user name/comment\n" 24320253Sjoerg "\t-d directory home directory\n" 24420253Sjoerg "\t-e date account expiry date\n" 24520253Sjoerg "\t-p date password expiry date\n" 24620253Sjoerg "\t-g grp initial group\n" 24720253Sjoerg "\t-G grp1,grp2 additional groups\n" 24820253Sjoerg "\t-m [ -k dir ] create and set up home\n" 249168043Sle "\t-M mode home directory permissions\n" 25020253Sjoerg "\t-s shell name of login shell\n" 25120253Sjoerg "\t-o duplicate uid ok\n" 25220253Sjoerg "\t-L class user class\n" 25320253Sjoerg "\t-h fd read password on fd\n" 254124382Siedowse "\t-H fd read encrypted password on fd\n" 25521330Sdavidn "\t-Y update NIS maps\n" 25620267Sjoerg "\t-N no update\n" 25720253Sjoerg " Setting defaults:\n" 25844229Sdavidn "\t-V etcdir alternate /etc location\n" 259289436Swblock "\t-R rootdir alternate root directory\n" 260287084Sbapt "\t-D set user defaults\n" 26120253Sjoerg "\t-b dir default home root dir\n" 26220253Sjoerg "\t-e period default expiry period\n" 26320253Sjoerg "\t-p period default password change period\n" 26420253Sjoerg "\t-g group default group\n" 26520253Sjoerg "\t-G grp1,grp2 additional groups\n" 26620253Sjoerg "\t-L class default user class\n" 26720253Sjoerg "\t-k dir default home skeleton\n" 268168044Sle "\t-M mode home directory permissions\n" 26920253Sjoerg "\t-u min,max set min,max uids\n" 27020253Sjoerg "\t-i min,max set min,max gids\n" 27120253Sjoerg "\t-w method set default password method\n" 27221330Sdavidn "\t-s shell default shell\n" 27321330Sdavidn "\t-y path set NIS passwd file path\n", 27430259Scharnier "usage: pw userdel [uid|name] [switches]\n" 27544229Sdavidn "\t-V etcdir alternate /etc location\n" 276289436Swblock "\t-R rootdir alternate root directory\n" 27720253Sjoerg "\t-n name login name\n" 27820253Sjoerg "\t-u uid user id\n" 27921330Sdavidn "\t-Y update NIS maps\n" 280287084Sbapt "\t-y path set NIS passwd file path\n" 28120253Sjoerg "\t-r remove home & contents\n", 28230259Scharnier "usage: pw usermod [uid|name] [switches]\n" 28344229Sdavidn "\t-V etcdir alternate /etc location\n" 284289436Swblock "\t-R rootdir alternate root directory\n" 28520253Sjoerg "\t-C config configuration file\n" 28620253Sjoerg "\t-q quiet operation\n" 28720253Sjoerg "\t-F force add if no user\n" 28820253Sjoerg "\t-n name login name\n" 28920253Sjoerg "\t-u uid user id\n" 29020253Sjoerg "\t-c comment user name/comment\n" 29120253Sjoerg "\t-d directory home directory\n" 29220253Sjoerg "\t-e date account expiry date\n" 29320253Sjoerg "\t-p date password expiry date\n" 29420253Sjoerg "\t-g grp initial group\n" 29520253Sjoerg "\t-G grp1,grp2 additional groups\n" 29620253Sjoerg "\t-l name new login name\n" 29720253Sjoerg "\t-L class user class\n" 29820253Sjoerg "\t-m [ -k dir ] create and set up home\n" 299168043Sle "\t-M mode home directory permissions\n" 30020253Sjoerg "\t-s shell name of login shell\n" 30120267Sjoerg "\t-w method set new password using method\n" 30220267Sjoerg "\t-h fd read password on fd\n" 303124382Siedowse "\t-H fd read encrypted password on fd\n" 30421330Sdavidn "\t-Y update NIS maps\n" 305287084Sbapt "\t-y path set NIS passwd file path\n" 30620267Sjoerg "\t-N no update\n", 30730259Scharnier "usage: pw usershow [uid|name] [switches]\n" 30844229Sdavidn "\t-V etcdir alternate /etc location\n" 309289436Swblock "\t-R rootdir alternate root directory\n" 31020253Sjoerg "\t-n name login name\n" 31120253Sjoerg "\t-u uid user id\n" 31220253Sjoerg "\t-F force print\n" 31320267Sjoerg "\t-P prettier format\n" 31444386Sdavidn "\t-a print all users\n" 31544386Sdavidn "\t-7 print in v7 format\n", 31630259Scharnier "usage: pw usernext [switches]\n" 31744229Sdavidn "\t-V etcdir alternate /etc location\n" 318289436Swblock "\t-R rootdir alternate root directory\n" 31920267Sjoerg "\t-C config configuration file\n" 32074226Sdd "\t-q quiet operation\n", 32174226Sdd "usage pw: lock [switches]\n" 32274226Sdd "\t-V etcdir alternate /etc locations\n" 32374226Sdd "\t-C config configuration file\n" 32474226Sdd "\t-q quiet operation\n", 32574226Sdd "usage pw: unlock [switches]\n" 32674226Sdd "\t-V etcdir alternate /etc locations\n" 32774226Sdd "\t-C config configuration file\n" 32874226Sdd "\t-q quiet operation\n" 32920253Sjoerg }, 33020253Sjoerg { 33130259Scharnier "usage: pw groupadd [group|gid] [switches]\n" 33244229Sdavidn "\t-V etcdir alternate /etc location\n" 333289436Swblock "\t-R rootdir alternate root directory\n" 33420253Sjoerg "\t-C config configuration file\n" 33520253Sjoerg "\t-q quiet operation\n" 33620253Sjoerg "\t-n group group name\n" 33720253Sjoerg "\t-g gid group id\n" 33820267Sjoerg "\t-M usr1,usr2 add users as group members\n" 33920267Sjoerg "\t-o duplicate gid ok\n" 34021330Sdavidn "\t-Y update NIS maps\n" 34120267Sjoerg "\t-N no update\n", 34230259Scharnier "usage: pw groupdel [group|gid] [switches]\n" 34344229Sdavidn "\t-V etcdir alternate /etc location\n" 344289436Swblock "\t-R rootdir alternate root directory\n" 34520253Sjoerg "\t-n name group name\n" 34621330Sdavidn "\t-g gid group id\n" 34721330Sdavidn "\t-Y update NIS maps\n", 34830259Scharnier "usage: pw groupmod [group|gid] [switches]\n" 34944229Sdavidn "\t-V etcdir alternate /etc location\n" 350289436Swblock "\t-R rootdir alternate root directory\n" 35120253Sjoerg "\t-C config configuration file\n" 35220253Sjoerg "\t-q quiet operation\n" 35320253Sjoerg "\t-F force add if not exists\n" 35420253Sjoerg "\t-n name group name\n" 35520253Sjoerg "\t-g gid group id\n" 35620267Sjoerg "\t-M usr1,usr2 replaces users as group members\n" 35720267Sjoerg "\t-m usr1,usr2 add users as group members\n" 358176474Sscf "\t-d usr1,usr2 delete users as group members\n" 35920267Sjoerg "\t-l name new group name\n" 36021330Sdavidn "\t-Y update NIS maps\n" 36120267Sjoerg "\t-N no update\n", 36230259Scharnier "usage: pw groupshow [group|gid] [switches]\n" 36344229Sdavidn "\t-V etcdir alternate /etc location\n" 364289436Swblock "\t-R rootdir alternate root directory\n" 36520253Sjoerg "\t-n name group name\n" 36620253Sjoerg "\t-g gid group id\n" 36720253Sjoerg "\t-F force print\n" 36820267Sjoerg "\t-P prettier format\n" 36920267Sjoerg "\t-a print all accounting groups\n", 37030259Scharnier "usage: pw groupnext [switches]\n" 37144229Sdavidn "\t-V etcdir alternate /etc location\n" 372289436Swblock "\t-R rootdir alternate root directory\n" 37320267Sjoerg "\t-C config configuration file\n" 37474226Sdd "\t-q quiet operation\n" 37520253Sjoerg } 37620253Sjoerg }; 37720253Sjoerg 37879292Skris fprintf(stderr, "%s", help[which][mode]); 37920253Sjoerg } 38020267Sjoerg exit(EXIT_FAILURE); 38120253Sjoerg} 382