5 * Jörg Wunsch. All rights reserved.
7 * The basic structure has been taken from tcpip.c, which is:
10 * Gary J Palmer. All rights reserved.
11 * Jordan K Hubbard. All rights reserved.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer,
18 * verbatim and that no modifications are made prior to this
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
30 * OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
32 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 #include "sysinstall.h"
40 #include <sys/param.h>
43 /* The help file for the user mgmt screen */
44 #define USER_HELPFILE "usermgmt"
46 /* XXX should they be moved out to sysinstall.h? */
47 #define GNAME_FIELD_LEN 32
48 #define GID_FIELD_LEN 10
49 #define GMEMB_FIELD_LEN 64
51 #define UID_FIELD_LEN 10
52 #define UGROUP_FIELD_LEN GNAME_FIELD_LEN
53 #define GECOS_FIELD_LEN 64
54 #define UMEMB_FIELD_LEN GMEMB_FIELD_LEN
55 #define HOMEDIR_FIELD_LEN 48
56 #define SHELL_FIELD_LEN 48
57 #define PASSWD_FIELD_LEN 32
59 /* These are nasty, but they make the layout structure a lot easier ... */
61 static char gname[GNAME_FIELD_LEN],
63 gmemb[GMEMB_FIELD_LEN],
64 uname[UT_NAMESIZE + 1],
65 passwd[PASSWD_FIELD_LEN],
67 ugroup[UGROUP_FIELD_LEN],
68 gecos[GECOS_FIELD_LEN],
69 umemb[UMEMB_FIELD_LEN],
70 homedir[HOMEDIR_FIELD_LEN],
71 shell[SHELL_FIELD_LEN];
72 #define CLEAR(v) memset(v, 0, sizeof v)
74 static int okbutton, cancelbutton;
77 /* What the screen size is meant to be */
78 #define USER_DIALOG_Y 0
79 #define USER_DIALOG_X 8
80 #define USER_DIALOG_WIDTH COLS - 16
81 #define USER_DIALOG_HEIGHT LINES - 2
83 /* The group configuration menu. */
84 static Layout groupLayout[] = {
85 #define LAYOUT_GNAME 0
86 { 4, 10, 20, GNAME_FIELD_LEN - 1,
87 "Group name:", "The alphanumeric name of the new group (mandatory)",
88 gname, STRINGOBJ, NULL },
90 { 4, 38, 10, GID_FIELD_LEN - 1,
91 "GID:", "The numerical ID for this group (leave blank for automatic choice)",
92 gid, STRINGOBJ, NULL },
93 #define LAYOUT_GMEMB 2
94 { 11, 10, 40, GMEMB_FIELD_LEN - 1,
95 "Group members:", "Who belongs to this group (i.e., gets access rights for it)",
96 gmemb, STRINGOBJ, NULL },
97 #define LAYOUT_OKBUTTON 3
99 "OK", "Select this if you are happy with these settings",
100 &okbutton, BUTTONOBJ, NULL },
101 #define LAYOUT_CANCELBUTTON 4
103 "CANCEL", "Select this if you wish to cancel this screen",
104 &cancelbutton, BUTTONOBJ, NULL },
108 /* The user configuration menu. */
109 static Layout userLayout[] = {
110 #define LAYOUT_UNAME 0
111 { 3, 6, UT_NAMESIZE, UT_NAMESIZE + 1,
112 "Login ID:", "The login name of the new user (mandatory)",
113 uname, STRINGOBJ, NULL },
115 { 3, 23, 8, UID_FIELD_LEN - 1,
116 "UID:", "The numerical ID for this user (leave blank for automatic choice)",
117 uid, STRINGOBJ, NULL },
118 #define LAYOUT_UGROUP 2
119 { 3, 33, 8, UGROUP_FIELD_LEN - 1,
120 "Group:", "The login group name for this user (leave blank for automatic choice)",
121 ugroup, STRINGOBJ, NULL },
122 #define LAYOUT_PASSWD 3
123 { 3, 43, 15, PASSWD_FIELD_LEN - 1,
124 "Password:", "The password for this user (enter this field with care!)",
125 passwd, NO_ECHO_OBJ(STRINGOBJ), NULL },
126 #define LAYOUT_GECOS 4
127 { 8, 6, 33, GECOS_FIELD_LEN - 1,
128 "Full name:", "The user's full name (comment)",
129 gecos, STRINGOBJ, NULL },
130 #define LAYOUT_UMEMB 5
131 { 8, 43, 15, UMEMB_FIELD_LEN - 1,
132 "Member groups:", "The groups this user belongs to (i.e. gets access rights for)",
133 umemb, STRINGOBJ, NULL },
134 #define LAYOUT_HOMEDIR 6
135 { 13, 6, 20, HOMEDIR_FIELD_LEN - 1,
136 "Home directory:", "The user's home directory (leave blank for default)",
137 homedir, STRINGOBJ, NULL },
138 #define LAYOUT_SHELL 7
139 { 13, 29, 29, SHELL_FIELD_LEN - 1,
140 "Login shell:", "The user's login shell (leave blank for default)",
141 shell, STRINGOBJ, NULL },
142 #define LAYOUT_U_OKBUTTON 8
144 "OK", "Select this if you are happy with these settings",
145 &okbutton, BUTTONOBJ, NULL },
146 #define LAYOUT_U_CANCELBUTTON 9
148 "CANCEL", "Select this if you wish to cancel this screen",
149 &cancelbutton, BUTTONOBJ, NULL },
161 /* Check for the settings on the screen. */
164 verifyGroupSettings(void)
169 if (strlen(gname) == 0) {
170 feepout("The group name field must not be empty!");
173 snprintf(tmp, 256, "pw group show -q -n %s > /dev/null", gname);
174 if (vsystem("%s", tmp) == 0) {
175 feepout("This group name is already in use.");
178 if (strlen(gid) > 0) {
179 lgid = strtol(gid, &cp, 10);
180 if (lgid < 0 || lgid >= 65536 || (*cp != '\0' && !isspace(*cp))) {
181 feepout("The GID must be a number between 1 and 65535.");
185 if (strlen(gmemb) > 0) {
186 if (strpbrk(gmemb, " \t") != NULL) {
187 feepout("The group member list must not contain any whitespace;\n"
188 "use commas to separate the names.");
191 #ifndef notyet /* XXX */
192 feepout("Sorry, the group member list feature\n"
193 "is currently not yet implemented.");
202 * Ask pw(8) to fill in the blanks for us.
203 * Works solely on the global variables.
216 "pw", "group", "next", 0
220 if ((pid = fork()) == 0)
225 for (i = getdtablesize(); i > 2; i--)
228 execv("/usr/sbin/pw", vec);
229 msgDebug("Cannot execv() /usr/sbin/pw.\n");
238 while((l = read(pfd[0], &tmp[i], amnt)) > 0)
251 if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
254 if ((cp = strchr(tmp, '\n')) != NULL)
256 strncpy(gid, tmp, sizeof gid);
261 addGroup(WINDOW *ds_win)
270 "pw", "group", "add", "-n", 0, "-g", 0, 0
275 msgNotify("Adding group \"%s\"...", gname);
278 if ((pid = fork()) == 0)
283 for (i = getdtablesize(); i > 2; i--)
286 vec[VEC_GNAME] = gname;
291 vec[VEC_GID - 1] = 0;
293 execv("/usr/sbin/pw", vec);
294 msgDebug("Cannot execv() /usr/sbin/pw.\n");
303 while((l = read(pfd[0], &tmp[i], amnt)) > 0)
317 msgDebug("pw(8) exited with signal %d.\n", WTERMSIG(i));
318 else if(WEXITSTATUS(i))
321 if(strncmp(tmp, "pw: ", 4) == 0)
323 tmp[sizeof tmp - 1] = '\0'; /* sanity */
324 msgConfirm("The `pw' command exited with an error status.\n"
325 "Its error message was:\n\n%s",
334 userAddGroup(dialogMenuItem *self)
336 WINDOW *ds_win, *save;
337 ComposeObj *obj = NULL;
338 int n = 0, cancel = FALSE, ret;
339 int max, firsttime = TRUE;
341 if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
342 msgConfirm("This option may only be used after the system is installed, sorry!");
343 return DITEM_FAILURE;
347 dialog_clear_norefresh();
348 /* We need a curses window */
349 if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
350 USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
352 msgConfirm("Cannot open addgroup dialog window!!");
353 return(DITEM_FAILURE);
356 /* Draw a group entry box */
357 draw_box(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 8, USER_DIALOG_HEIGHT - 8,
358 USER_DIALOG_WIDTH - 17, dialog_attr, border_attr);
359 wattrset(ds_win, dialog_attr);
360 mvwaddstr(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 22, " Add a new group ");
366 /* Some more initialisation before we go into the main input loop */
367 obj = initLayoutDialog(ds_win, groupLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
370 cancelbutton = okbutton = 0;
372 /* fill in the blanks, well, just the GID */
374 RefreshStringObj(groupLayout[LAYOUT_GID].obj);
378 while (layoutDialogLoop(ds_win, groupLayout, &obj, &n, max, &cancelbutton, &cancel));
380 if (!cancel && !verifyGroupSettings())
383 /* Clear this crap off the screen */
385 dialog_clear_norefresh();
398 /* Check for the settings on the screen. */
401 verifyUserSettings(WINDOW *ds_win)
408 if (strlen(uname) == 0) {
409 feepout("The user name field must not be empty!");
412 snprintf(tmp, 256, "pw user show -q -n %s > /dev/null", uname);
413 if (vsystem("%s", tmp) == 0) {
414 feepout("This user name is already in use.");
417 if (strlen(uid) > 0) {
418 luid = strtol(uid, &cp, 10);
419 if (luid < 0 || luid >= 65536 || (*cp != '\0' && !isspace(*cp))) {
420 feepout("The UID must be a number between 1 and 65535.");
424 if ((homedir[0]!=0) && (homedir[0]!='/')) {
425 feepout("The pathname for home directories must begin with a '/'.");
428 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();