summaryrefslogtreecommitdiffstats
path: root/pw/pw.c
diff options
context:
space:
mode:
Diffstat (limited to 'pw/pw.c')
-rw-r--r--pw/pw.c310
1 files changed, 271 insertions, 39 deletions
diff --git a/pw/pw.c b/pw/pw.c
index 1f42101..5ad2511 100644
--- a/pw/pw.c
+++ b/pw/pw.c
@@ -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;
+}