pw.c revision 44386
1130803Smarcel/*- 246283Sdfr * Copyright (C) 1996 3130803Smarcel * David L. Nugent. All rights reserved. 4130803Smarcel * 5130803Smarcel * Redistribution and use in source and binary forms, with or without 698944Sobrien * modification, are permitted provided that the following conditions 746283Sdfr * are met: 898944Sobrien * 1. Redistributions of source code must retain the above copyright 998944Sobrien * notice, this list of conditions and the following disclaimer. 1098944Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1198944Sobrien * notice, this list of conditions and the following disclaimer in the 1246283Sdfr * documentation and/or other materials provided with the distribution. 1398944Sobrien * 1498944Sobrien * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND 1598944Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1698944Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1746283Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE 1898944Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1998944Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2098944Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2198944Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2246283Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2346283Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2446283Sdfr * SUCH DAMAGE. 2546283Sdfr */ 2646283Sdfr 2746283Sdfr#ifndef lint 2846283Sdfrstatic const char rcsid[] = 2946283Sdfr "$Id: pw.c,v 1.14 1999/02/23 11:01:50 davidn Exp $"; 3046283Sdfr#endif /* not lint */ 3146283Sdfr 3246283Sdfr#include <err.h> 3346283Sdfr#include <fcntl.h> 3446283Sdfr#include <paths.h> 35130803Smarcel#include <sys/wait.h> 36130803Smarcel#include "pw.h" 37130803Smarcel 3846283Sdfrconst char *Modes[] = {"add", "del", "mod", "show", "next", NULL}; 3998944Sobrienconst char *Which[] = {"user", "group", NULL}; 4098944Sobrienstatic const char *Combo1[] = { 4198944Sobrien "useradd", "userdel", "usermod", "usershow", "usernext", 4298944Sobrien "groupadd", "groupdel", "groupmod", "groupshow", "groupnext", 4398944Sobrien NULL}; 4446283Sdfrstatic const char *Combo2[] = { 4546283Sdfr "adduser", "deluser", "moduser", "showuser", "nextuser", 4646283Sdfr "addgroup", "delgroup", "modgroup", "showgroup", "nextgroup", 4746283SdfrNULL}; 4846283Sdfr 4946283Sdfrstruct pwf PWF = 5046283Sdfr{ 5146283Sdfr 0, 5246283Sdfr setpwent, 5398944Sobrien endpwent, 5498944Sobrien getpwent, 5546283Sdfr getpwuid, 5646283Sdfr getpwnam, 5746283Sdfr pwdb, 5846283Sdfr setgrent, 5946283Sdfr endgrent, 6046283Sdfr getgrent, 6146283Sdfr getgrgid, 6246283Sdfr getgrnam, 6346283Sdfr grdb 6498944Sobrien 6598944Sobrien}; 6698944Sobrienstruct pwf VPWF = 6746283Sdfr{ 6898944Sobrien 1, 6946283Sdfr vsetpwent, 7098944Sobrien vendpwent, 7198944Sobrien vgetpwent, 7298944Sobrien vgetpwuid, 7398944Sobrien vgetpwnam, 7446283Sdfr vpwdb, 7546283Sdfr vsetgrent, 7698944Sobrien vendgrent, 7798944Sobrien vgetgrent, 7898944Sobrien vgetgrgid, 7998944Sobrien vgetgrnam, 8098944Sobrien vgrdb 8198944Sobrien}; 8246283Sdfr 8346283Sdfrstatic struct cargs arglist; 8498944Sobrien 8598944Sobrienstatic int getindex(const char *words[], const char *word); 8698944Sobrienstatic void cmdhelp(int mode, int which); 8746283Sdfr 8898944Sobrien 8998944Sobrienint 9098944Sobrienmain(int argc, char *argv[]) 9198944Sobrien{ 9298944Sobrien int ch; 9398944Sobrien int mode = -1; 9498944Sobrien int which = -1; 9598944Sobrien char *config = NULL; 9698944Sobrien struct userconf *cnf; 9798944Sobrien 9898944Sobrien static const char *opts[W_NUM][M_NUM] = 9998944Sobrien { 10098944Sobrien { /* user */ 10198944Sobrien "V:C:qn:u:c:d:e:p:g:G:mk:s:oL:i:w:h:Db:NPy:Y", 10298944Sobrien "V:C:qn:u:rY", 10398944Sobrien "V:C:qn:u:c:d:e:p:g:G:ml:k:s:w:L:h:FNPY", 10498944Sobrien "V:C:qn:u:FPa7", 10598944Sobrien "V:C:q" 10698944Sobrien }, 10798944Sobrien { /* grp */ 10898944Sobrien "V:C:qn:g:h:M:pNPY", 10998944Sobrien "V:C:qn:g:Y", 11098944Sobrien "V:C:qn:g:l:h:FM:m:NPY", 11198944Sobrien "V:C:qn:g:FPa", 11298944Sobrien "V:C:q" 11398944Sobrien } 11498944Sobrien }; 11598944Sobrien 11698944Sobrien static int (*funcs[W_NUM]) (struct userconf * _cnf, int _mode, struct cargs * _args) = 11798944Sobrien { /* Request handlers */ 11898944Sobrien pw_user, 11998944Sobrien pw_group 12098944Sobrien }; 12198944Sobrien 12298944Sobrien umask(0); /* We wish to handle this manually */ 12398944Sobrien LIST_INIT(&arglist); 12446283Sdfr 12598944Sobrien /* 12698944Sobrien * Break off the first couple of words to determine what exactly 12798944Sobrien * we're being asked to do 12898944Sobrien */ 12998944Sobrien while (argc > 1) { 13098944Sobrien int tmp; 13198944Sobrien 13298944Sobrien if (*argv[1] == '-') { 13398944Sobrien /* 13498944Sobrien * Special case, allow pw -V<dir> <operation> [args] for scripts etc. 13598944Sobrien */ 13646283Sdfr if (argv[1][1] == 'V') { 13798944Sobrien optarg = &argv[1][2]; 13898944Sobrien if (*optarg == '\0') { 13946283Sdfr optarg = argv[2]; 14046283Sdfr ++argv; 14146283Sdfr --argc; 14246283Sdfr } 14346283Sdfr addarg(&arglist, 'V', optarg); 14446283Sdfr } else 14546283Sdfr break; 14646283Sdfr } 14746283Sdfr else if ((tmp = getindex(Modes, argv[1])) != -1) 14846283Sdfr mode = tmp; 14946283Sdfr else if ((tmp = getindex(Which, argv[1])) != -1) 15046283Sdfr which = tmp; 15146283Sdfr else if ((tmp = getindex(Combo1, argv[1])) != -1 || (tmp = getindex(Combo2, argv[1])) != -1) { 15246283Sdfr which = tmp / M_NUM; 15346283Sdfr mode = tmp % M_NUM; 15446283Sdfr } else if (strcmp(argv[1], "help") == 0) 15546283Sdfr cmdhelp(mode, which); 15646283Sdfr else if (which != -1 && mode != -1) 15746283Sdfr addarg(&arglist, 'n', argv[1]); 15846283Sdfr else 15946283Sdfr errx(EX_USAGE, "unknown keyword `%s'", argv[1]); 16046283Sdfr ++argv; 16198944Sobrien --argc; 16246283Sdfr } 16346283Sdfr 16446283Sdfr /* 16546283Sdfr * Bail out unless the user is specific! 16646283Sdfr */ 16746283Sdfr if (mode == -1 || which == -1) 16846283Sdfr cmdhelp(mode, which); 16946283Sdfr 17046283Sdfr /* 17198944Sobrien * We know which mode we're in and what we're about to do, so now 17246283Sdfr * let's dispatch the remaining command line args in a genric way. 17346283Sdfr */ 17446283Sdfr optarg = NULL; 17546283Sdfr 17646283Sdfr while ((ch = getopt(argc, argv, opts[which][mode])) != -1) { 17746283Sdfr if (ch == '?') 17846283Sdfr errx(EX_USAGE, NULL); 17946283Sdfr else 18046283Sdfr addarg(&arglist, ch, optarg); 18146283Sdfr optarg = NULL; 18246283Sdfr } 18346283Sdfr 18498944Sobrien /* 18546283Sdfr * Must be root to attempt an update 18646283Sdfr */ 18746283Sdfr if (geteuid() != 0 && mode != M_PRINT && mode != M_NEXT && getarg(&arglist, 'N')==NULL) 18846283Sdfr errx(EX_NOPERM, "you must be root to run this program"); 18946283Sdfr 19046283Sdfr /* 19146283Sdfr * We should immediately look for the -q 'quiet' switch so that we 19246283Sdfr * don't bother with extraneous errors 19346283Sdfr */ 19446283Sdfr if (getarg(&arglist, 'q') != NULL) 19546283Sdfr freopen("/dev/null", "w", stderr); 19646283Sdfr 19746283Sdfr /* 19846283Sdfr * Set our base working path if not overridden 19946283Sdfr */ 20046283Sdfr 20146283Sdfr config = getarg(&arglist, 'C') ? getarg(&arglist, 'C')->val : NULL; 20246283Sdfr 20346283Sdfr if (getarg(&arglist, 'V') != NULL) { 20446283Sdfr char * etcpath = getarg(&arglist, 'V')->val; 20546283Sdfr if (*etcpath) { 20698944Sobrien if (config == NULL) { /* Only override config location if -C not specified */ 20746283Sdfr config = malloc(MAXPATHLEN); 20846283Sdfr snprintf(config, MAXPATHLEN, "%s/pw.conf", etcpath); 20946283Sdfr } 21046283Sdfr memcpy(&PWF, &VPWF, sizeof PWF); 21146283Sdfr setpwdir(etcpath); 21246283Sdfr setgrdir(etcpath); 21346283Sdfr } 21446283Sdfr } 21546283Sdfr 21646283Sdfr /* 21746283Sdfr * Now, let's do the common initialisation 21846283Sdfr */ 21946283Sdfr cnf = read_userconfig(config); 22046283Sdfr 22146283Sdfr ch = funcs[which] (cnf, mode, &arglist); 22246283Sdfr 22346283Sdfr /* 22446283Sdfr * If everything went ok, and we've been asked to update 22598944Sobrien * the NIS maps, then do it now 22646283Sdfr */ 22746283Sdfr if (ch == EXIT_SUCCESS && getarg(&arglist, 'Y') != NULL) { 22846283Sdfr pid_t pid; 22946283Sdfr 23046283Sdfr fflush(NULL); 23146283Sdfr if (chdir(_PATH_YP) == -1) 23246283Sdfr warn("chdir(" _PATH_YP ")"); 23398944Sobrien else if ((pid = fork()) == -1) 23446283Sdfr warn("fork()"); 23546283Sdfr else if (pid == 0) { 23698944Sobrien /* Is make anywhere else? */ 23746283Sdfr execlp("/usr/bin/make", "make", NULL); 23846283Sdfr _exit(1); 23946283Sdfr } else { 24046283Sdfr int i; 24146283Sdfr waitpid(pid, &i, 0); 24246283Sdfr if ((i = WEXITSTATUS(i)) != 0) 24346283Sdfr errx(ch, "make exited with status %d", i); 24446283Sdfr else 24598944Sobrien pw_log(cnf, mode, which, "NIS maps updated"); 24698944Sobrien } 24798944Sobrien } 24898944Sobrien return ch; 24998944Sobrien} 25046283Sdfr 25146283Sdfr 25298944Sobrienstatic int 25398944Sobriengetindex(const char *words[], const char *word) 25498944Sobrien{ 25598944Sobrien int i = 0; 25698944Sobrien 25746283Sdfr while (words[i]) { 25846283Sdfr if (strcmp(words[i], word) == 0) 25998944Sobrien return i; 26046283Sdfr i++; 26146283Sdfr } 26298944Sobrien return -1; 26398944Sobrien} 26498944Sobrien 26546283Sdfr 26646283Sdfr/* 26798944Sobrien * This is probably an overkill for a cmdline help system, but it reflects 26898944Sobrien * the complexity of the command line. 26946283Sdfr */ 27046283Sdfr 27198944Sobrienstatic void 27246283Sdfrcmdhelp(int mode, int which) 27398944Sobrien{ 27498944Sobrien if (which == -1) 27598944Sobrien fprintf(stderr, "usage: pw [user|group] [add|del|mod|show|next] [ help | switches/values ]\n"); 27698944Sobrien else if (mode == -1) 27798944Sobrien fprintf(stderr, "usage: pw %s [add|del|mod|show|next] [ help | switches/values ]\n", Which[which]); 27898944Sobrien else { 27998944Sobrien 28098944Sobrien /* 28146283Sdfr * We need to give mode specific help 28246283Sdfr */ 28346283Sdfr static const char *help[W_NUM][M_NUM] = 28446283Sdfr { 28546283Sdfr { 28646283Sdfr "usage: pw useradd [name] [switches]\n" 28746283Sdfr "\t-V etcdir alternate /etc location\n" 28846283Sdfr "\t-C config configuration file\n" 28946283Sdfr "\t-q quiet operation\n" 29046283Sdfr " Adding users:\n" 29146283Sdfr "\t-n name login name\n" 29246283Sdfr "\t-u uid user id\n" 29346283Sdfr "\t-c comment user name/comment\n" 29446283Sdfr "\t-d directory home directory\n" 29546283Sdfr "\t-e date account expiry date\n" 29646283Sdfr "\t-p date password expiry date\n" 29746283Sdfr "\t-g grp initial group\n" 29846283Sdfr "\t-G grp1,grp2 additional groups\n" 29946283Sdfr "\t-m [ -k dir ] create and set up home\n" 30046283Sdfr "\t-s shell name of login shell\n" 30146283Sdfr "\t-o duplicate uid ok\n" 30246283Sdfr "\t-L class user class\n" 30346283Sdfr "\t-h fd read password on fd\n" 30446283Sdfr "\t-Y update NIS maps\n" 30546283Sdfr "\t-N no update\n" 30646283Sdfr " Setting defaults:\n" 30746283Sdfr "\t-V etcdir alternate /etc location\n" 30846283Sdfr "\t-D set user defaults\n" 30946283Sdfr "\t-b dir default home root dir\n" 31046283Sdfr "\t-e period default expiry period\n" 31198944Sobrien "\t-p period default password change period\n" 31246283Sdfr "\t-g group default group\n" 31346283Sdfr "\t-G grp1,grp2 additional groups\n" 31446283Sdfr "\t-L class default user class\n" 31546283Sdfr "\t-k dir default home skeleton\n" 31646283Sdfr "\t-u min,max set min,max uids\n" 31746283Sdfr "\t-i min,max set min,max gids\n" 31898944Sobrien "\t-w method set default password method\n" 31946283Sdfr "\t-s shell default shell\n" 32046283Sdfr "\t-y path set NIS passwd file path\n", 32146283Sdfr "usage: pw userdel [uid|name] [switches]\n" 32246283Sdfr "\t-V etcdir alternate /etc location\n" 32346283Sdfr "\t-n name login name\n" 32446283Sdfr "\t-u uid user id\n" 32546283Sdfr "\t-Y update NIS maps\n" 32646283Sdfr "\t-r remove home & contents\n", 32746283Sdfr "usage: pw usermod [uid|name] [switches]\n" 32846283Sdfr "\t-V etcdir alternate /etc location\n" 32946283Sdfr "\t-C config configuration file\n" 33046283Sdfr "\t-q quiet operation\n" 33146283Sdfr "\t-F force add if no user\n" 33246283Sdfr "\t-n name login name\n" 33346283Sdfr "\t-u uid user id\n" 33498944Sobrien "\t-c comment user name/comment\n" 33546283Sdfr "\t-d directory home directory\n" 33646283Sdfr "\t-e date account expiry date\n" 33746283Sdfr "\t-p date password expiry date\n" 33846283Sdfr "\t-g grp initial group\n" 33946283Sdfr "\t-G grp1,grp2 additional groups\n" 34046283Sdfr "\t-l name new login name\n" 34146283Sdfr "\t-L class user class\n" 34246283Sdfr "\t-m [ -k dir ] create and set up home\n" 34346283Sdfr "\t-s shell name of login shell\n" 34446283Sdfr "\t-w method set new password using method\n" 34546283Sdfr "\t-h fd read password on fd\n" 34646283Sdfr "\t-Y update NIS maps\n" 34746283Sdfr "\t-N no update\n", 34898944Sobrien "usage: pw usershow [uid|name] [switches]\n" 34946283Sdfr "\t-V etcdir alternate /etc location\n" 35046283Sdfr "\t-n name login name\n" 35146283Sdfr "\t-u uid user id\n" 35246283Sdfr "\t-F force print\n" 35346283Sdfr "\t-P prettier format\n" 35446283Sdfr "\t-a print all users\n" 35546283Sdfr "\t-7 print in v7 format\n", 35698944Sobrien "usage: pw usernext [switches]\n" 35746283Sdfr "\t-V etcdir alternate /etc location\n" 35846283Sdfr "\t-C config configuration file\n" 35998944Sobrien }, 360130803Smarcel { 36146283Sdfr "usage: pw groupadd [group|gid] [switches]\n" 36246283Sdfr "\t-V etcdir alternate /etc location\n" 36346283Sdfr "\t-C config configuration file\n" 36446283Sdfr "\t-q quiet operation\n" 36546283Sdfr "\t-n group group name\n" 36646283Sdfr "\t-g gid group id\n" 36746283Sdfr "\t-M usr1,usr2 add users as group members\n" 36898944Sobrien "\t-o duplicate gid ok\n" 36946283Sdfr "\t-Y update NIS maps\n" 370130803Smarcel "\t-N no update\n", 37146283Sdfr "usage: pw groupdel [group|gid] [switches]\n" 37246283Sdfr "\t-V etcdir alternate /etc location\n" 37346283Sdfr "\t-n name group name\n" 37446283Sdfr "\t-g gid group id\n" 37546283Sdfr "\t-Y update NIS maps\n", 37646283Sdfr "usage: pw groupmod [group|gid] [switches]\n" 37746283Sdfr "\t-V etcdir alternate /etc location\n" 37846283Sdfr "\t-C config configuration file\n" 37946283Sdfr "\t-q quiet operation\n" 38098944Sobrien "\t-F force add if not exists\n" 38146283Sdfr "\t-n name group name\n" 38246283Sdfr "\t-g gid group id\n" 38346283Sdfr "\t-M usr1,usr2 replaces users as group members\n" 38446283Sdfr "\t-m usr1,usr2 add users as group members\n" 38546283Sdfr "\t-l name new group name\n" 38646283Sdfr "\t-Y update NIS maps\n" 38746283Sdfr "\t-N no update\n", 388130803Smarcel "usage: pw groupshow [group|gid] [switches]\n" 38946283Sdfr "\t-V etcdir alternate /etc location\n" 39046283Sdfr "\t-n name group name\n" 39146283Sdfr "\t-g gid group id\n" 39246283Sdfr "\t-F force print\n" 39346283Sdfr "\t-P prettier format\n" 39446283Sdfr "\t-a print all accounting groups\n", 39546283Sdfr "usage: pw groupnext [switches]\n" 396130803Smarcel "\t-V etcdir alternate /etc location\n" 39746283Sdfr "\t-C config configuration file\n" 39898944Sobrien } 39998944Sobrien }; 40098944Sobrien 40198944Sobrien fprintf(stderr, help[which][mode]); 40298944Sobrien } 40398944Sobrien exit(EXIT_FAILURE); 40498944Sobrien} 40598944Sobrien 40698944Sobrienstruct carg * 40798944Sobriengetarg(struct cargs * _args, int ch) 40898944Sobrien{ 40998944Sobrien struct carg *c = _args->lh_first; 41046283Sdfr 41146283Sdfr while (c != NULL && c->ch != ch) 41246283Sdfr c = c->list.le_next; 41346283Sdfr return c; 41446283Sdfr} 41546283Sdfr 41698944Sobrienstruct carg * 41798944Sobrienaddarg(struct cargs * _args, int ch, char *argstr) 41846283Sdfr{ 41946283Sdfr struct carg *ca = malloc(sizeof(struct carg)); 42046283Sdfr 42146283Sdfr if (ca == NULL) 42246283Sdfr errx(EX_OSERR, "out of memory"); 42346283Sdfr ca->ch = ch; 42446283Sdfr ca->val = argstr; 42598944Sobrien LIST_INSERT_HEAD(_args, ca, list); 42698944Sobrien return ca; 42798944Sobrien} 42898944Sobrien