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