Hi, > I'm sorry to write again, but I'm wrong again: There should have been an > `err` instead of `errx` after the asprintf(). And some formatting of > usage(), but as I said, it's breaking current behaviour anyway. the attached patch enables the user to change any field he is allowed to (or at least should be, according to the existing possibility to edit values in EDITOR). Regards, Julian
--- usr.bin/chpass/chpass.1 +++ usr.bin/chpass/chpass.1 @@ -46,10 +46,18 @@ .Nm chpass .Op Fl a Ar list .Op Fl s Ar newshell .Op Fl y .Op user +.Nm chpass +.Op Fl f Ar option=value +.Op Fl y +.Op user +.Nm chpass +.Op Fl f Ar option=value +.Op Fl l +.Op user .Sh DESCRIPTION .Nm allows editing of the user database information associated with .Ar user @@ -67,15 +75,25 @@ as an argument. This argument must be a colon .Pq Dq \&: separated list of all the user database fields, although they may be empty. +.It Fl f +Set the field given by +.Ar option +to value +.Ar value . +If there is a space in the name of the option, you can either enquote it in +brackets (i.e. passing still as one argument) or replace the space by an +underscore. .It Fl s The .Fl s option attempts to change the user's shell to .Ar newshell . +This is maintained for compataibility reasons, actually it does the same as +.Fl f Ar shell=newshell . .It Fl l This option causes the password to be updated only in the local password file. When changing only the local password, .Xr pwd_mkdb 8 @@ -238,10 +256,18 @@ .It Pa /tmp/pw.XXXXXX Temporary copy of the user passwd information .It Pa /etc/shells The list of approved shells .El +.Sh EXAMPLES +Set home directory for user +.Ar john +to +.Ar /home/john . +This can be achieved in two different ways +.D1 chpass Fl f Ar Home_Directory=/bin/sh Ar john +.D1 chpass Fl f Ar \*qHome Directory=/bin/sh\*q Ar john .Sh SEE ALSO .Xr finger 1 , .Xr login 1 , .Xr passwd 1 , .Xr pwhash 1 ,
--- usr.bin/chpass/chpass.c +++ usr.bin/chpass/chpass.c @@ -78,17 +78,17 @@ __dead static void usage(void); int main(int argc, char **argv) { - enum { NEWSH, LOADENTRY, EDITENTRY } op; + enum { LOADENTRY, EDITENTRY, SINGLEENTRY } op; struct passwd *pw, lpw, old_pw; - int ch, dfd, pfd, tfd; + int ch, dfd, pfd, tfd, i, ffound; #ifdef YP int yflag = 0; #endif - char *arg, *username = NULL; + char *arg, *opt, *username = NULL; #ifdef __GNUC__ pw = NULL; /* XXX gcc -Wuninitialized */ arg = NULL; #endif @@ -95,18 +95,23 @@ #ifdef YP use_yp = _yp_check(NULL); #endif op = EDITENTRY; - while ((ch = getopt(argc, argv, "a:s:ly")) != -1) + while ((ch = getopt(argc, argv, "f:a:s:ly")) != -1) switch (ch) { case 'a': op = LOADENTRY; arg = optarg; break; case 's': - op = NEWSH; + op = SINGLEENTRY; + if (asprintf(&arg, "shell:%s", optarg) <= 0) + errx(1, "asprintf"); + break; + case 'f': + op = SINGLEENTRY; arg = optarg; break; case 'l': use_yp = 0; break; @@ -180,11 +185,11 @@ if (op == LOADENTRY && use_yp) errx(1, "cannot load entry using YP.\n" "\tUse the -l flag to load local."); #endif - if (op == EDITENTRY || op == NEWSH) { + if (op == EDITENTRY || op == SINGLEENTRY) { if (username != NULL) { pw = getpwnam(username); if (pw == NULL) errx(1, "unknown user: %s", username); if (uid && uid != pw->pw_uid) @@ -202,16 +207,39 @@ err(1, "strdup"); /*NOTREACHED*/ } } - if (op == NEWSH) { - /* protect p_shell -- it thinks NULL is /bin/sh */ - if (!arg[0]) + if (op == SINGLEENTRY) { + /* Separate option and value. */ + opt = strsep(&arg, ":"); + if (opt == NULL || !strlen(opt) | !strlen(arg)) usage(); - if (p_shell(arg, pw, NULL)) - (*Pw_error)(NULL, 0, 1); + if (uid && uid != pw->pw_uid) + baduser(); + + /* The option name has a space replaced by underscore. */ + if (strchr(opt, '_')) + *(strchr(opt, '_')) = ' '; + + /* Search for the right field. */ + ffound = 0; + for (i = 0; list[i].prompt != NULL; i++) + if (!strcasecmp(opt, list[i].prompt)) { + if (list[i].restricted && uid) + baduser(); + if (!strcmp(list[i].prompt, "shell") && uid + && !ok_shell(pw->pw_shell)) + baduser(); + if (list[i].func(arg, pw, &list[i])) + (*Pw_error)(NULL, 0, 1); + ffound = 1; + break; + } + + if (!ffound) + errx(1, "invalid field specification: %s", opt); } if (op == LOADENTRY) { if (uid) baduser(); @@ -300,17 +328,20 @@ static void usage(void) { (void)fprintf(stderr, - "usage: %s [-a list] [-s shell] [-l] [user]\n" - " %s [-a list] [-s shell] [-y] [user]\n", - getprogname(), getprogname()); + "usage: %s [-a list] [-s shell] [-l] [user]\n" + " %s [-a list] [-s shell] [-y] [user]\n" + " %s [-f option:value] [-y] [user]\n" + " %s [-f option:value] [-l] [user]\n", + getprogname(), getprogname(), + getprogname(), getprogname()); exit(1); } static void cleanup(void) { (void)unlink(tempname); }
--- usr.bin/chpass/chpass.h +++ usr.bin/chpass/chpass.h @@ -33,12 +33,15 @@ struct passwd; typedef struct _entry { const char *prompt; - int (*func)(const char *, struct passwd *, struct _entry *), restricted, len; - const char *except, *save; + int (*func)(const char *, struct passwd *, struct _entry *); + int restricted; + int len; + const char *except; + const char *save; } ENTRY; extern int use_yp; /* Field numbers. */
--- usr.bin/chpass/table.c +++ usr.bin/chpass/table.c @@ -43,10 +43,13 @@ #include "chpass.h" char e1[] = ": "; char e2[] = ":,"; +/* Be careful when changing something here (though that might never happen...). + * The option names may not contain underscores, or the code in chpass.c needs + * to be changed! */ ENTRY list[] = { { "login", p_login, 1, 5, e1, NULL }, { "password", p_passwd, 1, 8, e1, NULL }, { "uid", p_uid, 1, 3, e1, NULL }, { "gid", p_gid, 1, 3, e1, NULL },
Attachment:
signature.asc
Description: PGP signature