2 * $FreeBSD: src/release/sysinstall/user.c,v 1.15.2.2 2001/07/19 01:33:02 kris Exp $
3 * $DragonFly: src/release/sysinstall/Attic/user.c,v 1.2 2003/06/17 04:27:21 dillon Exp $
6 * Jörg Wunsch. All rights reserved.
8 * The basic structure has been taken from tcpip.c, which is:
11 * Gary J Palmer. All rights reserved.
12 * Jordan K Hubbard. All rights reserved.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer,
19 * verbatim and that no modifications are made prior to this
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
31 * OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
32 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 #include "sysinstall.h"
41 #include <sys/param.h>
44 /* The help file for the user mgmt screen */
45 #define USER_HELPFILE "usermgmt"
47 /* XXX should they be moved out to sysinstall.h? */
48 #define GNAME_FIELD_LEN 32
49 #define GID_FIELD_LEN 10
50 #define GMEMB_FIELD_LEN 64
52 #define UID_FIELD_LEN 10
53 #define UGROUP_FIELD_LEN GNAME_FIELD_LEN
54 #define GECOS_FIELD_LEN 64
55 #define UMEMB_FIELD_LEN GMEMB_FIELD_LEN
56 #define HOMEDIR_FIELD_LEN 48
57 #define SHELL_FIELD_LEN 48
58 #define PASSWD_FIELD_LEN 32
60 /* These are nasty, but they make the layout structure a lot easier ... */
62 static char gname[GNAME_FIELD_LEN],
64 gmemb[GMEMB_FIELD_LEN],
65 uname[UT_NAMESIZE + 1],
66 passwd[PASSWD_FIELD_LEN],
68 ugroup[UGROUP_FIELD_LEN],
69 gecos[GECOS_FIELD_LEN],
70 umemb[UMEMB_FIELD_LEN],
71 homedir[HOMEDIR_FIELD_LEN],
72 shell[SHELL_FIELD_LEN];
73 #define CLEAR(v) memset(v, 0, sizeof v)
75 static int okbutton, cancelbutton;
78 /* What the screen size is meant to be */
79 #define USER_DIALOG_Y 0
80 #define USER_DIALOG_X 8
81 #define USER_DIALOG_WIDTH COLS - 16
82 #define USER_DIALOG_HEIGHT LINES - 2
84 /* The group configuration menu. */
85 static Layout groupLayout[] = {
86 #define LAYOUT_GNAME 0
87 { 4, 10, 20, GNAME_FIELD_LEN - 1,
88 "Group name:", "The alphanumeric name of the new group (mandatory)",
89 gname, STRINGOBJ, NULL },
91 { 4, 38, 10, GID_FIELD_LEN - 1,
92 "GID:", "The numerical ID for this group (leave blank for automatic choice)",
93 gid, STRINGOBJ, NULL },
94 #define LAYOUT_GMEMB 2
95 { 11, 10, 40, GMEMB_FIELD_LEN - 1,
96 "Group members:", "Who belongs to this group (i.e., gets access rights for it)",
97 gmemb, STRINGOBJ, NULL },
98 #define LAYOUT_OKBUTTON 3
100 "OK", "Select this if you are happy with these settings",
101 &okbutton, BUTTONOBJ, NULL },
102 #define LAYOUT_CANCELBUTTON 4
104 "CANCEL", "Select this if you wish to cancel this screen",
105 &cancelbutton, BUTTONOBJ, NULL },
109 /* The user configuration menu. */
110 static Layout userLayout[] = {
111 #define LAYOUT_UNAME 0
112 { 3, 6, UT_NAMESIZE, UT_NAMESIZE + 1,
113 "Login ID:", "The login name of the new user (mandatory)",
114 uname, STRINGOBJ, NULL },
116 { 3, 23, 8, UID_FIELD_LEN - 1,
117 "UID:", "The numerical ID for this user (leave blank for automatic choice)",
118 uid, STRINGOBJ, NULL },
119 #define LAYOUT_UGROUP 2
120 { 3, 33, 8, UGROUP_FIELD_LEN - 1,
121 "Group:", "The login group name for this user (leave blank for automatic choice)",
122 ugroup, STRINGOBJ, NULL },
123 #define LAYOUT_PASSWD 3
124 { 3, 43, 15, PASSWD_FIELD_LEN - 1,
125 "Password:", "The password for this user (enter this field with care!)",
126 passwd, NO_ECHO_OBJ(STRINGOBJ), NULL },
127 #define LAYOUT_GECOS 4
128 { 8, 6, 33, GECOS_FIELD_LEN - 1,
129 "Full name:", "The user's full name (comment)",
130 gecos, STRINGOBJ, NULL },
131 #define LAYOUT_UMEMB 5
132 { 8, 43, 15, UMEMB_FIELD_LEN - 1,
133 "Member groups:", "The groups this user belongs to (i.e. gets access rights for)",
134 umemb, STRINGOBJ, NULL },
135 #define LAYOUT_HOMEDIR 6
136 { 13, 6, 20, HOMEDIR_FIELD_LEN - 1,
137 "Home directory:", "The user's home directory (leave blank for default)",
138 homedir, STRINGOBJ, NULL },
139 #define LAYOUT_SHELL 7
140 { 13, 29, 29, SHELL_FIELD_LEN - 1,
141 "Login shell:", "The user's login shell (leave blank for default)",
142 shell, STRINGOBJ, NULL },
143 #define LAYOUT_U_OKBUTTON 8
145 "OK", "Select this if you are happy with these settings",
146 &okbutton, BUTTONOBJ, NULL },
147 #define LAYOUT_U_CANCELBUTTON 9
149 "CANCEL", "Select this if you wish to cancel this screen",
150 &cancelbutton, BUTTONOBJ, NULL },
162 /* Check for the settings on the screen. */
165 verifyGroupSettings(void)
170 if (strlen(gname) == 0) {
171 feepout("The group name field must not be empty!");
174 snprintf(tmp, 256, "pw group show -q -n %s > /dev/null", gname);
175 if (vsystem("%s", tmp) == 0) {
176 feepout("This group name is already in use.");
179 if (strlen(gid) > 0) {
180 lgid = strtol(gid, &cp, 10);
181 if (lgid < 0 || lgid >= 65536 || (*cp != '\0' && !isspace(*cp))) {
182 feepout("The GID must be a number between 1 and 65535.");
186 if (strlen(gmemb) > 0) {
187 if (strpbrk(gmemb, " \t") != NULL) {
188 feepout("The group member list must not contain any whitespace;\n"
189 "use commas to separate the names.");
192 #ifndef notyet /* XXX */
193 feepout("Sorry, the group member list feature\n"
194 "is currently not yet implemented.");
203 * Ask pw(8) to fill in the blanks for us.
204 * Works solely on the global variables.
217 "pw", "group", "next", 0
221 if ((pid = fork()) == 0)
226 for (i = getdtablesize(); i > 2; i--)
229 execv("/usr/sbin/pw", vec);
230 msgDebug("Cannot execv() /usr/sbin/pw.\n");
239 while((l = read(pfd[0], &tmp[i], amnt)) > 0)
252 if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
255 if ((cp = strchr(tmp, '\n')) != NULL)
257 strncpy(gid, tmp, sizeof gid);
262 addGroup(WINDOW *ds_win)
271 "pw", "group", "add", "-n", 0, "-g", 0, 0
276 msgNotify("Adding group \"%s\"...", gname);
279 if ((pid = fork()) == 0)
284 for (i = getdtablesize(); i > 2; i--)
287 vec[VEC_GNAME] = gname;
292 vec[VEC_GID - 1] = 0;
294 execv("/usr/sbin/pw", vec);
295 msgDebug("Cannot execv() /usr/sbin/pw.\n");
304 while((l = read(pfd[0], &tmp[i], amnt)) > 0)
318 msgDebug("pw(8) exited with signal %d.\n", WTERMSIG(i));
319 else if(WEXITSTATUS(i))
322 if(strncmp(tmp, "pw: ", 4) == 0)
324 tmp[sizeof tmp - 1] = '\0'; /* sanity */
325 msgConfirm("The `pw' command exited with an error status.\n"
326 "Its error message was:\n\n%s",
335 userAddGroup(dialogMenuItem *self)
337 WINDOW *ds_win, *save;
338 ComposeObj *obj = NULL;
339 int n = 0, cancel = FALSE, ret;
340 int max, firsttime = TRUE;
342 if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
343 msgConfirm("This option may only be used after the system is installed, sorry!");
344 return DITEM_FAILURE;
348 dialog_clear_norefresh();
349 /* We need a curses window */
350 if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
351 USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
353 msgConfirm("Cannot open addgroup dialog window!!");
354 return(DITEM_FAILURE);
357 /* Draw a group entry box */
358 draw_box(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 8, USER_DIALOG_HEIGHT - 8,
359 USER_DIALOG_WIDTH - 17, dialog_attr, border_attr);
360 wattrset(ds_win, dialog_attr);
361 mvwaddstr(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 22, " Add a new group ");
367 /* Some more initialisation before we go into the main input loop */
368 obj = initLayoutDialog(ds_win, groupLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
371 cancelbutton = okbutton = 0;
373 /* fill in the blanks, well, just the GID */
375 RefreshStringObj(groupLayout[LAYOUT_GID].obj);
379 while (layoutDialogLoop(ds_win, groupLayout, &obj, &n, max, &cancelbutton, &cancel));
381 if (!cancel && !verifyGroupSettings())
384 /* Clear this crap off the screen */
386 dialog_clear_norefresh();
399 /* Check for the settings on the screen. */
402 verifyUserSettings(WINDOW *ds_win)
409 if (strlen(uname) == 0) {
410 feepout("The user name field must not be empty!");
413 snprintf(tmp, 256, "pw user show -q -n %s > /dev/null", uname);
414 if (vsystem("%s", tmp) == 0) {
415 feepout("This user name is already in use.");
418 if (strlen(uid) > 0) {
419 luid = strtol(uid, &cp, 10);
420 if (luid < 0 || luid >= 65536 || (*cp != '\0' && !isspace(*cp))) {
421 feepout("The UID must be a number between 1 and 65535.");
425 if ((homedir[0]!=0) && (homedir[0]!='/')) {
426 feepout("The pathname for home directories must begin with a '/'.");
429 if (strlen(shell) > 0) {
430 while((cp = getusershell()) != NULL)
431 if (strcmp(cp, shell) == 0)
436 rv = msgYesNo("Warning:\n\n"
437 "The requested shell \"%s\" is not\n"
438 "a valid user shell.\n\n"
439 "Use it anyway?\n", shell);
442 if (rv != DITEM_SUCCESS)
448 if (strlen(umemb) > 0) {
449 if (strpbrk(umemb, " \t") != NULL) {
450 feepout("The member groups list must not contain any whitespace;\n"
451 "use commas to separate the names.");
460 * Ask pw(8) to fill in the blanks for us.
461 * Works solely on the global variables.
468 char tmp[256], *cp, *cp2;
474 "pw", "user", "add", "-N", "-n", 0, 0
479 if ((pid = fork()) == 0)
484 for (i = getdtablesize(); i > 2; i--)
487 vec[VEC_UNAME] = uname;
489 execv("/usr/sbin/pw", vec);
490 msgDebug("Cannot execv() /usr/sbin/pw.\n");
499 while((l = read(pfd[0], &tmp[i], amnt)) > 0)
512 if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
515 if ((cp = strchr(tmp, '\n')) != NULL)
517 if ((cp = strchr(tmp, ':')) == NULL || (cp = strchr(++cp, ':')) == NULL)
520 if ((cp2 = strchr(cp, ':')) == NULL)
523 strncpy(uid, cp, sizeof uid);
525 if ((cp2 = strchr(cp, ':')) == NULL)
528 #ifdef notyet /* XXX pw user add -g doesn't accept a numerical GID */
529 strncpy(ugroup, cp, sizeof ugroup);
532 if ((cp2 = strchr(cp, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL ||
533 (cp = cp2 = strchr(++cp2, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL)
537 strncpy(gecos, cp, sizeof gecos);
539 if ((cp2 = strchr(cp, ':')) == NULL)
543 strncpy(shell, cp2, sizeof shell);
549 addUser(WINDOW *ds_win)
552 int pfd[2], ipfd[2], i, j;
558 * pw user add -m -n uname -g grp -u uid -c comment -d homedir -s shell -G grplist -h 0
562 "pw", "user", "add", "-m", "-n", /* ... */
566 msgNotify("Adding user \"%s\"...", uname);
570 if ((pid = fork()) == 0)
576 for (i = getdtablesize(); i > 2; i--)
579 vec[i = VEC_UNAME] = uname;
581 #define ADDVEC(var, option) do { if (strlen(var) > 0) { vec[i++] = option; vec[i++] = var; } } while (0)
582 ADDVEC(ugroup, "-g");
585 ADDVEC(homedir, "-d");
594 execv("/usr/sbin/pw", vec);
595 msgDebug("Cannot execv() /usr/sbin/pw.\n");
605 write(ipfd[1], passwd, strlen(passwd));
609 while((l = read(pfd[0], &tmp[i], amnt)) > 0)
625 msg = "The `pw' command exited with signal %d.\n";
628 else if((j = WEXITSTATUS(i)))
631 if(strncmp(tmp, "pw: ", 4) == 0)
633 tmp[sizeof tmp - 1] = '\0'; /* sanity */
634 if (j == EX_DATAERR || j == EX_NOUSER || j == EX_SOFTWARE)
635 msgConfirm("The `pw' command exited with an error status.\n"
636 "Its error message was:\n\n%s",
640 msg = "The `pw' command exited with unexpected status %d.\n";
643 msgDebug("Command stdout and stderr was:\n\n%s", tmp);
648 msgConfirm("You will need to enter a password for this user\n"
649 "later, using the passwd(1) command from the shell.\n\n"
650 "The account for `%s' is currently still disabled.",
658 userAddUser(dialogMenuItem *self)
660 WINDOW *ds_win, *save;
661 ComposeObj *obj = NULL;
662 int n = 0, cancel = FALSE, ret;
663 int max, firsttime = TRUE, filled=0;
665 if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
666 msgConfirm("This option may only be used after the system is installed, sorry!");
667 return DITEM_FAILURE;
671 dialog_clear_norefresh();
673 /* We need a curses window */
674 if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
675 USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
677 msgConfirm("Cannot open adduser dialog window!!");
678 return(DITEM_FAILURE);
681 /* Draw a user entry box */
682 draw_box(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 3, USER_DIALOG_HEIGHT - 6,
683 USER_DIALOG_WIDTH - 6, dialog_attr, border_attr);
684 wattrset(ds_win, dialog_attr);
685 mvwaddstr(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 22, " Add a new user ");
696 /* Some more initialisation before we go into the main input loop */
697 obj = initLayoutDialog(ds_win, userLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
700 cancelbutton = okbutton = 0;
702 /* fill in the blanks, well, just the GID */
704 RefreshStringObj(userLayout[LAYOUT_UID].obj);
705 RefreshStringObj(userLayout[LAYOUT_UGROUP].obj);
706 RefreshStringObj(userLayout[LAYOUT_GECOS].obj);
707 RefreshStringObj(userLayout[LAYOUT_UMEMB].obj);
708 RefreshStringObj(userLayout[LAYOUT_HOMEDIR].obj);
709 RefreshStringObj(userLayout[LAYOUT_SHELL].obj);
713 while (layoutDialogLoop(ds_win, userLayout, &obj, &n, max, &cancelbutton, &cancel)) {
714 /* Prevent this from being irritating if user really means NO */
716 if ((uname[0]) && !homedir[0]) {
717 SAFE_STRCPY(homedir,"/home/");
718 strcat(homedir,uname);
719 RefreshStringObj(userLayout[LAYOUT_HOMEDIR].obj);
725 if (!cancel && !verifyUserSettings(ds_win))
728 /* Clear this crap off the screen */
730 dialog_clear_norefresh();