diff options
Diffstat (limited to 'pw/pw.c')
-rw-r--r-- | pw/pw.c | 310 |
1 files changed, 271 insertions, 39 deletions
@@ -32,12 +32,14 @@ static const char rcsid[] = #include <err.h> #include <fcntl.h> #include <locale.h> -#include <string.h> -#include <sysexits.h> -#include <unistd.h> - +#include <paths.h> +#include <stdbool.h> +#include <sys/wait.h> #include "pw.h" +#if !defined(_PATH_YP) +#define _PATH_YP "/var/yp/" +#endif const char *Modes[] = { "add", "del", "mod", "show", "next", NULL}; @@ -83,39 +85,55 @@ struct pwf VPWF = vgetgrnam, }; -static int (*cmdfunc[W_NUM][M_NUM])(int argc, char **argv, char *_name) = { - { /* user */ - pw_user_add, - pw_user_del, - pw_user_mod, - pw_user_show, - pw_user_next, - pw_user_lock, - pw_user_unlock, - }, - { /* group */ - pw_group_add, - pw_group_del, - pw_group_mod, - pw_group_show, - pw_group_next, - } -}; - struct pwconf conf; -static int getindex(const char *words[], const char *word); -static void cmdhelp(int mode, int which); +static struct cargs arglist; + +static int getindex(const char *words[], const char *word); +static void cmdhelp(int mode, int which); + int main(int argc, char *argv[]) { - int mode = -1, which = -1, tmp; + int ch; + int mode = -1; + int which = -1; + long id = -1; + char *config = NULL; struct stat st; - char arg, *arg1; + const char *errstr; + char arg, *name; bool relocated, nis; - arg1 = NULL; + static const char *opts[W_NUM][M_NUM] = + { + { /* user */ + "R:V:C:qn:u:c:d:e:p:g:G:mM:k:s:oL:i:w:h:H:Db:NPy:Y", + "R:V:C:qn:u:rY", + "R:V:C:qn:u:c:d:e:p:g:G:mM:l:k:s:w:L:h:H:FNPY", + "R:V:C:qn:u:FPa7", + "R:V:C:q", + "R:V:C:q", + "R:V:C:q" + }, + { /* grp */ + "R:V:C:qn:g:h:H:M:opNPY", + "R:V:C:qn:g:Y", + "R:V:C:qn:d:g:l:h:H:FM:m:NPY", + "R:V:C:qn:g:FPa", + "R:V:C:q" + } + }; + + static int (*funcs[W_NUM]) (int _mode, char *_name, long _id, + struct cargs * _args) = + { /* Request handlers */ + pw_user, + pw_group + }; + + name = NULL; relocated = nis = false; memset(&conf, 0, sizeof(conf)); strlcpy(conf.rootdir, "/", sizeof(conf.rootdir)); @@ -123,13 +141,17 @@ main(int argc, char *argv[]) conf.fd = -1; conf.checkduplicate = true; - setlocale(LC_ALL, ""); + LIST_INIT(&arglist); + + (void)setlocale(LC_ALL, ""); /* * Break off the first couple of words to determine what exactly * we're being asked to do */ while (argc > 1) { + int tmp; + if (*argv[1] == '-') { /* * Special case, allow pw -V<dir> <operation> [args] for scripts etc. @@ -175,9 +197,15 @@ main(int argc, char *argv[]) mode = tmp % M_NUM; } else if (strcmp(argv[1], "help") == 0 && argv[2] == NULL) cmdhelp(mode, which); - else if (which != -1 && mode != -1) - arg1 = argv[1]; - else + else if (which != -1 && mode != -1) { + if (strspn(argv[1], "0123456789") == strlen(argv[1])) { + id = strtonum(argv[1], 0, LONG_MAX, &errstr); + if (errstr != NULL) + errx(EX_USAGE, "Bad id '%s': %s", + argv[1], errstr); + } else + name = argv[1]; + } else errx(EX_USAGE, "unknown keyword `%s'", argv[1]); ++argv; --argc; @@ -192,22 +220,200 @@ main(int argc, char *argv[]) conf.rootfd = open(conf.rootdir, O_DIRECTORY|O_CLOEXEC); if (conf.rootfd == -1) errx(EXIT_FAILURE, "Unable to open '%s'", conf.rootdir); + conf.which = which; + /* + * We know which mode we're in and what we're about to do, so now + * let's dispatch the remaining command line args in a genric way. + */ + optarg = NULL; + + while ((ch = getopt(argc, argv, opts[which][mode])) != -1) { + switch (ch) { + case '?': + errx(EX_USAGE, "unknown switch"); + break; + case '7': + conf.v7 = true; + break; + case 'C': + conf.config = optarg; + config = conf.config; + break; + case 'F': + conf.force = true; + break; + case 'N': + conf.dryrun = true; + break; + case 'l': + if (strlen(optarg) >= MAXLOGNAME) + errx(EX_USAGE, "new name too long: %s", optarg); + conf.newname = optarg; + break; + case 'P': + conf.pretty = true; + break; + case 'Y': + nis = true; + break; + case 'a': + conf.all = true; + break; + case 'c': + conf.gecos = pw_checkname(optarg, 1); + break; + case 'g': + if (which == 0) { /* for user* */ + addarg(&arglist, 'g', optarg); + break; + } + if (strspn(optarg, "0123456789") != strlen(optarg)) + errx(EX_USAGE, "-g expects a number"); + id = strtonum(optarg, 0, LONG_MAX, &errstr); + if (errstr != NULL) + errx(EX_USAGE, "Bad id '%s': %s", optarg, + errstr); + break; + case 'u': + if (strspn(optarg, "0123456789,") != strlen(optarg)) + errx(EX_USAGE, "-u expects a number"); + if (strchr(optarg, ',') != NULL) { + addarg(&arglist, 'u', optarg); + break; + } + id = strtonum(optarg, 0, LONG_MAX, &errstr); + if (errstr != NULL) + errx(EX_USAGE, "Bad id '%s': %s", optarg, + errstr); + break; + case 'n': + if (strspn(optarg, "0123456789") != strlen(optarg)) { + name = optarg; + break; + } + id = strtonum(optarg, 0, LONG_MAX, &errstr); + if (errstr != NULL) + errx(EX_USAGE, "Bad id '%s': %s", optarg, + errstr); + break; + case 'H': + if (conf.fd != -1) + errx(EX_USAGE, "'-h' and '-H' are mutually " + "exclusive options"); + conf.precrypted = true; + if (strspn(optarg, "0123456789") != strlen(optarg)) + errx(EX_USAGE, "'-H' expects a file descriptor"); + + conf.fd = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) + errx(EX_USAGE, "Bad file descriptor '%s': %s", + optarg, errstr); + break; + case 'h': + if (conf.fd != -1) + errx(EX_USAGE, "'-h' and '-H' are mutually " + "exclusive options"); - return (cmdfunc[which][mode](argc, argv, arg1)); + if (strcmp(optarg, "-") == 0) + conf.fd = '-'; + else if (strspn(optarg, "0123456789") == strlen(optarg)) { + conf.fd = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) + errx(EX_USAGE, "'-h' expects a " + "file descriptor or '-'"); + } else + errx(EX_USAGE, "'-h' expects a file " + "descriptor or '-'"); + break; + case 'o': + conf.checkduplicate = false; + break; + case 'q': + conf.quiet = true; + break; + case 'r': + conf.deletehome = true; + break; + default: + addarg(&arglist, ch, optarg); + break; + } + optarg = NULL; + } + + if (name != NULL && strlen(name) >= MAXLOGNAME) + errx(EX_USAGE, "name too long: %s", name); + + /* + * Must be root to attempt an update + */ + if (geteuid() != 0 && mode != M_PRINT && mode != M_NEXT && !conf.dryrun) + errx(EX_NOPERM, "you must be root to run this program"); + + /* + * We should immediately look for the -q 'quiet' switch so that we + * don't bother with extraneous errors + */ + if (conf.quiet) + freopen(_PATH_DEVNULL, "w", stderr); + + /* + * Set our base working path if not overridden + */ + + if (config == NULL) { /* Only override config location if -C not specified */ + asprintf(&config, "%s/pw.conf", conf.etcpath); + if (config == NULL) + errx(EX_OSERR, "out of memory"); + } + + /* + * Now, let's do the common initialisation + */ + conf.userconf = read_userconfig(config); + + ch = funcs[which] (mode, name, id, &arglist); + + /* + * If everything went ok, and we've been asked to update + * the NIS maps, then do it now + */ + if (ch == EXIT_SUCCESS && nis) { + pid_t pid; + + fflush(NULL); + if (chdir(_PATH_YP) == -1) + warn("chdir(" _PATH_YP ")"); + else if ((pid = fork()) == -1) + warn("fork()"); + else if (pid == 0) { + /* Is make anywhere else? */ + execlp("/usr/bin/make", "make", (char *)NULL); + _exit(1); + } else { + int i; + waitpid(pid, &i, 0); + if ((i = WEXITSTATUS(i)) != 0) + errx(ch, "make exited with status %d", i); + else + pw_log(conf.userconf, mode, which, "NIS maps updated"); + } + } + return ch; } static int getindex(const char *words[], const char *word) { - int i = 0; + int i = 0; while (words[i]) { if (strcmp(words[i], word) == 0) - return (i); + return i; i++; } - return (-1); + return -1; } @@ -257,7 +463,7 @@ cmdhelp(int mode, int which) " Setting defaults:\n" "\t-V etcdir alternate /etc location\n" "\t-R rootir alternate root directory\n" - "\t-D set user defaults\n" + "\t-D set user defaults\n" "\t-b dir default home root dir\n" "\t-e period default expiry period\n" "\t-p period default password change period\n" @@ -277,7 +483,6 @@ cmdhelp(int mode, int which) "\t-n name login name\n" "\t-u uid user id\n" "\t-Y update NIS maps\n" - "\t-y path set NIS passwd file path\n" "\t-r remove home & contents\n", "usage: pw usermod [uid|name] [switches]\n" "\t-V etcdir alternate /etc location\n" @@ -302,7 +507,6 @@ cmdhelp(int mode, int which) "\t-h fd read password on fd\n" "\t-H fd read encrypted password on fd\n" "\t-Y update NIS maps\n" - "\t-y path set NIS passwd file path\n" "\t-N no update\n", "usage: pw usershow [uid|name] [switches]\n" "\t-V etcdir alternate /etc location\n" @@ -379,3 +583,31 @@ cmdhelp(int mode, int which) } exit(EXIT_FAILURE); } + +struct carg * +getarg(struct cargs * _args, int ch) +{ + struct carg *c; + + if (_args == NULL) + return (NULL); + + c = LIST_FIRST(_args); + + while (c != NULL && c->ch != ch) + c = LIST_NEXT(c, list); + return c; +} + +struct carg * +addarg(struct cargs * _args, int ch, char *argstr) +{ + struct carg *ca = malloc(sizeof(struct carg)); + + if (ca == NULL) + errx(EX_OSERR, "out of memory"); + ca->ch = ch; + ca->val = argstr; + LIST_INSERT_HEAD(_args, ca, list); + return ca; +} |