]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - usr.sbin/bsdconfig/usermgmt/share/user.subr
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / usr.sbin / bsdconfig / usermgmt / share / user.subr
1 if [ ! "$_USERMGMT_USER_SUBR" ]; then _USERMGMT_USER_SUBR=1
2 #
3 # Copyright (c) 2012 Ron McDowell
4 # Copyright (c) 2012-2015 Devin Teske
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
10 # 1. Redistributions of source code must retain the above copyright
11 #    notice, this list of conditions and the following disclaimer.
12 # 2. Redistributions in binary form must reproduce the above copyright
13 #    notice, this list of conditions and the following disclaimer in the
14 #    documentation and/or other materials provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 # ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 # SUCH DAMAGE.
27 #
28 # $FreeBSD$
29 #
30 ############################################################ INCLUDES
31
32 BSDCFG_SHARE="/usr/share/bsdconfig"
33 . $BSDCFG_SHARE/common.subr || exit 1
34 f_dprintf "%s: loading includes..." usermgmt/user.subr
35 f_include $BSDCFG_SHARE/dialog.subr
36 f_include $BSDCFG_SHARE/strings.subr
37 f_include $BSDCFG_SHARE/usermgmt/group_input.subr
38 f_include $BSDCFG_SHARE/usermgmt/user_input.subr
39
40 BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt"
41 f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr
42
43 ############################################################ CONFIGURATION
44
45 # set some reasonable defaults if /etc/adduser.conf does not exist.
46 [ -f /etc/adduser.conf ] && f_include /etc/adduser.conf
47 : ${defaultclass:=""}
48 : ${defaultshell:="/bin/sh"}
49 : ${homeprefix:="/home"}
50 : ${passwdtype:="yes"}
51 : ${udotdir:="/usr/share/skel"}
52 : ${uexpire:=""}
53         # Default account expire time. Format is similar to upwexpire variable.
54 : ${ugecos:="User &"}
55 : ${upwexpire:=""}
56         # The default password expiration time. Format of the date is either a
57         # UNIX time in decimal, or a date in dd-mmm-yy[yy] format, where dd is
58         # the day, mmm is the month in either numeric or alphabetic format, and
59         # yy[yy] is either a two or four digit year. This variable also accepts
60         # a relative date in the form of n[mhdwoy] where n is a decimal, octal
61         # (leading 0) or hexadecimal (leading 0x) digit followed by the number
62         # of Minutes, Hours, Days, Weeks, Months or Years from the current date
63         # at which the expiration time is to be set.
64
65 #
66 # uexpire and upwexpire from adduser.conf(5) differ only slightly from what
67 # pw(8) accepts as `date' argument(s); pw(8) requires a leading `+' for the
68 # relative date syntax (n[mhdwoy]).
69 #
70 case "$uexpire" in *[mhdwoy])
71         f_isinteger "${uexpire%[mhdwoy]}" && uexpire="+$uexpire"
72 esac
73 case "$upwexpire" in *[mhdwoy])
74         f_isinteger "${upwexpire%[mhdwoy]}" && upwexpire="+$upwexpire"
75 esac
76
77 ############################################################ FUNCTIONS
78
79 # f_user_create_homedir $user
80 #
81 # Create home directory for $user.
82 #
83 f_user_create_homedir()
84 {
85         local funcname=f_user_create_homedir
86         local user="$1"
87
88         [ "$user" ] || return $FAILURE
89
90         local user_account_expire user_class user_gecos user_gid user_home_dir
91         local user_member_groups user_name user_password user_password_expire
92         local user_shell user_uid # Variables created by f_input_user() below
93         f_input_user "$user" || return $FAILURE
94
95         f_dprintf "Creating home directory \`%s' for user \`%s'" \
96                   "$user_home_dir" "$user"
97
98         local _user_gid _user_home_dir _user_uid
99         f_shell_escape "$user_gid"      _user_gid
100         f_shell_escape "$user_home_dir" _user_home_dir
101         f_shell_escape "$user_uid"      _user_uid
102         f_eval_catch $funcname mkdir "mkdir -p '%s'" "$_user_home_dir" ||
103                 return $FAILURE
104         f_eval_catch $funcname chown "chown '%i:%i' '%s'" \
105                 "$_user_uid" "$_user_gid" "$_user_home_dir" || return $FAILURE
106 }
107
108 # f_user_copy_dotfiles $user
109 #
110 # Copy `skel' dot-files from $udotdir (global inherited from /etc/adduser.conf)
111 # to the home-directory of $user. Attempts to create the home-directory first
112 # if it doesn't exist.
113 #
114 f_user_copy_dotfiles()
115 {
116         local funcname=f_user_copy_dotfiles
117         local user="$1"
118
119         [ "$udotdir" ] || return $FAILURE
120         [ "$user"    ] || return $FAILURE
121
122         local user_account_expire user_class user_gecos user_gid user_home_dir
123         local user_member_groups user_name user_password user_password_expire
124         local user_shell user_uid # Variables created by f_input_user() below
125         f_input_user "$user" || return $FAILURE
126
127         f_dprintf "Copying dot-files from \`%s' to \`%s'" \
128                   "$udotdir" "$user_home_dir"
129
130         # Attempt to create the home directory if it doesn't exist
131         [ -d "$user_home_dir" ] ||
132                 f_user_create_homedir "$user" || return $FAILURE
133
134         local _user_gid _user_home_dir _user_uid
135         f_shell_escape "$user_gid"      _user_gid
136         f_shell_escape "$user_home_dir" _user_home_dir
137         f_shell_escape "$user_uid"      _user_uid
138
139         local - # Localize `set' to this function
140         set +f # Enable glob pattern-matching for paths
141         cd "$udotdir" || return $FAILURE
142
143         local _file file retval
144         for file in dot.*; do
145                 [ -e "$file" ] || continue # no-match
146
147                 f_shell_escape "$file" "_file"
148                 f_eval_catch $funcname cp "cp -n '%s' '%s'" \
149                         "$_file" "$_user_home_dir/${_file#dot}"
150                 retval=$?
151                 [ $retval -eq $SUCCESS ] || break
152                 f_eval_catch $funcname chown \
153                         "chown -h '%i:%i' '%s'" \
154                         "$_user_uid" "$_user_gid" \
155                         "$_user_home_dir/${_file#dot}"
156                 retval=$?
157                 [ $retval -eq $SUCCESS ] || break
158         done
159
160         cd -
161         return $retval
162 }
163
164 # f_user_add [$user]
165 #
166 # Create a login account. If both $user (as a first argument) and $VAR_USER are
167 # unset or NULL and we are running interactively, prompt the end-user to enter
168 # the name of a new login account and (if $VAR_NO_CONFIRM is unset or NULL)
169 # prompt the end-user to answer some questions about the new account. Variables
170 # that can be used to script user input:
171 #
172 #       VAR_USER [Optional if running interactively]
173 #               The login to add. Ignored if given non-NULL first-argument.
174 #       VAR_USER_ACCOUNT_EXPIRE [Optional]
175 #               The account expiration time. Format is similar to
176 #               VAR_USER_PASSWORD_EXPIRE variable below. Default is to never
177 #               expire the account.
178 #       VAR_USER_DOTFILES_CREATE [Optional]
179 #               If non-NULL, populate the user's home directory with the
180 #               template files found in $udotdir (`/usr/share/skel' default).
181 #       VAR_USER_GECOS [Optional]
182 #               Often the full name of the account holder. Default is NULL.
183 #       VAR_USER_GID [Optional]
184 #               Numerical primary-group ID to use. If NULL or unset, the group
185 #               ID is automatically chosen.
186 #       VAR_USER_GROUPS [Optional]
187 #               Comma-separated list of additional groups to which the user is
188 #               a member of. Default is NULL (no additional groups).
189 #       VAR_USER_HOME [Optional]
190 #               The home directory to set. If NULL or unset, the home directory
191 #               is automatically calculated.
192 #       VAR_USER_HOME_CREATE [Optional]
193 #               If non-NULL, create the user's home directory if it doesn't
194 #               already exist.
195 #       VAR_USER_LOGIN_CLASS [Optional]
196 #               Login class to use when creating the login. Default is NULL.
197 #       VAR_USER_PASSWORD [Optional]
198 #               Unencrypted password to use. If unset or NULL, password
199 #               authentication for the login is disabled.
200 #       VAR_USER_PASSWORD_EXPIRE [Optional]
201 #               The password expiration time. Format of the date is either a
202 #               UNIX time in decimal, or a date in dd-mmm-yy[yy] format, where
203 #               dd is the day, mmm is the month in either numeric or alphabetic
204 #               format, and yy[yy] is either a two or four digit year. This
205 #               variable also accepts a relative date in the form of +n[mhdwoy]
206 #               where n is a decimal, octal (leading 0) or hexadecimal (leading
207 #               0x) digit followed by the number of Minutes, Hours, Days,
208 #               Weeks, Months or Years from the current date at which the
209 #               expiration time is to be set. Default is to never expire the
210 #               account password.
211 #       VAR_USER_SHELL [Optional]
212 #               Path to login shell to use. Default is `/bin/sh'.
213 #       VAR_USER_UID [Optional]
214 #               Numerical user ID to use. If NULL or unset, the user ID is
215 #               automatically chosen.
216 #
217 # Returns success if the user account was successfully created.
218 #
219 f_user_add()
220 {
221         local funcname=f_user_add
222         local title # Calculated below
223         local alert=f_show_msg no_confirm=
224
225         f_getvar $VAR_NO_CONFIRM no_confirm
226         [ "$no_confirm" ] && alert=f_show_info
227
228         local input
229         f_getvar 3:-\$$VAR_USER input "$1"
230
231         #
232         # NB: pw(8) has a ``feature'' wherein `-n name' can be taken as UID
233         # instead of name. Work-around is to also pass `-u UID' at the same
234         # time (the UID is ignored in this case, so any UID will do).
235         #
236         if [ "$input" ] && f_quietly pw usershow -n "$input" -u 1337; then
237                 f_show_err "$msg_login_already_used" "$input"
238                 return $FAILURE
239         fi
240
241         local user_name="$input"
242         while f_interactive && [ ! "$user_name" ]; do
243                 f_dialog_input_name user_name "$user_name" ||
244                         return $SUCCESS
245                 [ "$user_name" ] ||
246                         f_show_err "$msg_please_enter_a_user_name"
247         done
248         if [ ! "$user_name" ]; then
249                 f_show_err "$msg_no_user_specified"
250                 return $FAILURE
251         fi
252
253         local user_account_expire user_class user_gecos user_gid user_home_dir
254         local user_member_groups user_password user_password_expire user_shell
255         local user_uid user_dotfiles_create= user_home_create=
256         f_getvar $VAR_USER_ACCOUNT_EXPIRE-\$uexpire    user_account_expire
257         f_getvar $VAR_USER_DOTFILES_CREATE:+\$msg_yes  user_dotfiles_create
258         f_getvar $VAR_USER_GECOS-\$ugecos              user_gecos
259         f_getvar $VAR_USER_GID                         user_gid
260         f_getvar $VAR_USER_GROUPS                      user_member_groups
261         f_getvar $VAR_USER_HOME:-\${homeprefix%/}/\$user_name \
262                                                        user_home_dir
263         f_getvar $VAR_USER_HOME_CREATE:+\$msg_yes      user_home_create
264         f_getvar $VAR_USER_LOGIN_CLASS-\$defaultclass  user_class
265         f_getvar $VAR_USER_PASSWORD                    user_password
266         f_getvar $VAR_USER_PASSWORD_EXPIRE-\$upwexpire user_password_expire
267         f_getvar $VAR_USER_SHELL-\$defaultshell        user_shell
268         f_getvar $VAR_USER_UID                         user_uid
269
270         # Create home-dir if no script-override and does not exist
271         f_isset $VAR_USER_HOME_CREATE || [ -d "$user_home_dir" ] ||
272                 user_home_create="$msg_yes"
273         # Copy dotfiles if home-dir creation is desired, does not yet exist,
274         # and no script-override has been set
275         f_isset $VAR_USER_DOTFILES_CREATE ||
276                 [ "$user_home_create" != "$msg_yes" ] ||
277                 [ -d "$user_home_dir" ] || user_dotfiles_create="$msg_yes"
278         # Create home-dir if copying dotfiles but home-dir does not exist
279         [ "$user_dotfiles_create" -a ! -d "$user_home_dir" ] &&
280                 user_home_create="$msg_yes"
281
282         # Set flags for meaningful NULL values if-provided
283         local no_account_expire= no_password_expire= null_gecos= null_members=
284         local user_password_disable=
285         f_isset $VAR_USER_ACCOUNT_EXPIRE &&
286                 [ ! "$user_account_expire"  ] && no_account_expire=1
287         f_isset $VAR_USER_GECOS &&
288                 [ ! "$user_gecos"           ] && null_gecos=1
289         f_isset $VAR_USER_GROUPS &&
290                 [ ! "$user_member_groups"   ] && null_members=1
291         f_isset $VAR_USER_PASSWORD &&
292                 [ ! "$user_password"        ] && user_password_disable=1
293         f_isset $VAR_USER_PASSWORD_EXPIRE &&
294                 [ ! "$user_password_expire" ] && no_password_expire=1
295
296         if f_interactive && [ ! "$no_confirm" ]; then
297                 f_dialog_noyes \
298                         "$msg_use_default_values_for_all_account_details"
299                 retval=$?
300                 if [ $retval -eq $DIALOG_ESC ]; then
301                         return $SUCCESS
302                 elif [ $retval -ne $DIALOG_OK ]; then
303                         #
304                         # Ask series of questions to pre-fill the editor screen
305                         #
306                         # Defaults used in each dialog should allow the user to
307                         # simply hit ENTER to proceed, because cancelling any
308                         # single dialog will cause them to be returned to the
309                         # previous menu.
310                         #
311
312                         f_dialog_input_gecos user_gecos "$user_gecos" ||
313                                 return $FAILURE
314                         if [ "$passwdtype" = "yes" ]; then
315                                 f_dialog_input_password user_password \
316                                         user_password_disable ||
317                                         return $FAILURE
318                         fi
319                         f_dialog_input_uid user_uid "$user_uid" ||
320                                 return $FAILURE
321                         f_dialog_input_gid user_gid "$user_gid" ||
322                                 return $FAILURE
323                         f_dialog_input_member_groups user_member_groups \
324                                 "$user_member_groups" || return $FAILURE
325                         f_dialog_input_class user_class "$user_class" ||
326                                 return $FAILURE
327                         f_dialog_input_expire_password user_password_expire \
328                                 "$user_password_expire" || return $FAILURE
329                         f_dialog_input_expire_account user_account_expire \
330                                 "$user_account_expire" || return $FAILURE
331                         f_dialog_input_home_dir user_home_dir \
332                                 "$user_home_dir" || return $FAILURE
333                         if [ ! -d "$user_home_dir" ]; then
334                                 f_dialog_input_home_create user_home_create ||
335                                         return $FAILURE
336                                 if [ "$user_home_create" = "$msg_yes" ]; then
337                                         f_dialog_input_dotfiles_create \
338                                                 user_dotfiles_create ||
339                                                 return $FAILURE
340                                 fi
341                         fi
342                         f_dialog_input_shell user_shell "$user_shell" ||
343                                 return $FAILURE
344                 fi
345         fi
346
347         #
348         # Loop until the user decides to Exit, Cancel, or presses ESC
349         #
350         title="$msg_add $msg_user: $user_name"
351         if f_interactive; then
352                 local mtag retval defaultitem=
353                 while :; do
354                         f_dialog_title "$title"
355                         f_dialog_menu_user_add "$defaultitem"
356                         retval=$?
357                         f_dialog_title_restore
358                         f_dialog_menutag_fetch mtag
359                         f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
360                         defaultitem="$mtag"
361
362                         # Return if user either pressed ESC or chose Cancel/No
363                         [ $retval -eq $DIALOG_OK ] || return $FAILURE
364
365                         case "$mtag" in
366                         X) # Add/Exit
367                            local var
368                            for var in account_expire class gecos gid home_dir \
369                                 member_groups name password_expire shell uid \
370                            ; do
371                                 local _user_$var
372                                 eval f_shell_escape \"\$user_$var\" _user_$var
373                            done
374
375                            local cmd="pw useradd -n '$_user_name'"
376                            [ "$user_gid"   ] && cmd="$cmd -g '$_user_gid'"
377                            [ "$user_shell" ] && cmd="$cmd -s '$_user_shell'"
378                            [ "$user_uid"   ] && cmd="$cmd -u '$_user_uid'"
379                            [ "$user_account_expire" -o \
380                              "$no_account_expire" ] &&
381                                 cmd="$cmd -e '$_user_account_expire'"
382                            [ "$user_class" -o "$null_class" ] &&
383                                 cmd="$cmd -L '$_user_class'"
384                            [ "$user_gecos" -o "$null_gecos" ] &&
385                                 cmd="$cmd -c '$_user_gecos'"
386                            [ "$user_home_dir" ] &&
387                                 cmd="$cmd -d '$_user_home_dir'"
388                            [ "$user_member_groups" ] &&
389                                 cmd="$cmd -G '$_user_member_groups'"
390                            [ "$user_password_expire" -o \
391                              "$no_password_expire" ] &&
392                                 cmd="$cmd -p '$_user_password_expire'"
393
394                            # Execute the command
395                            if [ "$user_password_disable" ]; then
396                                 f_eval_catch $funcname pw '%s -h -' "$cmd"
397                            elif [ "$user_password" ]; then
398                                 echo "$user_password" | f_eval_catch \
399                                         $funcname pw '%s -h 0' "$cmd"
400                            else
401                                 f_eval_catch $funcname pw '%s' "$cmd"
402                            fi || continue
403
404                            # Create home directory if desired
405                            [ "${user_home_create:-$msg_no}" != "$msg_no" ] &&
406                                 f_user_create_homedir "$user_name"
407
408                            # Copy dotfiles if desired
409                            [ "${user_dotfiles_create:-$msg_no}" != \
410                              "$msg_no" ] && f_user_copy_dotfiles "$user_name"
411
412                            break # to success
413                            ;;
414                         1) # Login (prompt for new login name)
415                            f_dialog_input_name input "$user_name" ||
416                                 continue
417                            if f_quietly pw usershow -n "$input" -u 1337; then
418                                 f_show_err "$msg_login_already_used" "$input"
419                                 continue
420                            fi
421                            user_name="$input"
422                            title="$msg_add $msg_user: $user_name"
423                            user_home_dir="${homeprefix%/}/$user_name"
424                            ;;
425                         2) # Full Name
426                            f_dialog_input_gecos user_gecos "$user_gecos" &&
427                                 [ ! "$user_gecos" ] && null_gecos=1 ;;
428                         3) # Password
429                            f_dialog_input_password \
430                                 user_password user_password_disable ;;
431                         4) # User ID
432                            f_dialog_input_uid user_uid "$user_uid" ;;
433                         5) # Group ID
434                            f_dialog_input_gid user_gid "$user_gid" ;;
435                         6) # Member of Groups
436                            f_dialog_input_member_groups \
437                                 user_member_groups "$user_member_groups" &&
438                                 [ ! "$user_member_groups" ] &&
439                                 null_members=1 ;;
440                         7) # Login Class
441                            f_dialog_input_class user_class "$user_class" &&
442                                 [ ! "$user_class" ] && null_class=1 ;;
443                         8) # Password Expires On
444                            f_dialog_input_expire_password \
445                                 user_password_expire "$user_password_expire" &&
446                                 [ ! "$user_password_expire" ] &&
447                                 no_password_expire=1 ;;
448                         9) # Account Expires On
449                            f_dialog_input_expire_account \
450                                 user_account_expire "$user_account_expire" &&
451                                 [ ! "$user_account_expire" ] &&
452                                 no_account_expire=1 ;;
453                         A) # Home Directory
454                            f_dialog_input_home_dir \
455                                 user_home_dir "$user_home_dir" ;;
456                         B) # Shell
457                            f_dialog_input_shell user_shell "$user_shell" ;;
458                         C) # Create Home Directory?
459                            if [ "${user_home_create:-$msg_no}" != "$msg_no" ]
460                            then
461                                 user_home_create="$msg_no"
462                            else
463                                 user_home_create="$msg_yes"
464                            fi ;;
465                         D) # Create Dotfiles?
466                            if [ "${user_dotfiles_create:-$msg_no}" != \
467                                 "$msg_no" ]
468                            then
469                                 user_dotfiles_create="$msg_no"
470                            else
471                                 user_dotfiles_create="$msg_yes"
472                            fi ;;
473                         esac
474                 done
475         else
476                 local var
477                 for var in account_expire class gecos gid home_dir \
478                         member_groups name password_expire shell uid \
479                 ; do
480                         local _user_$var
481                         eval f_shell_escape \"\$user_$var\" _user_$var
482                 done
483
484                 # Form the command
485                 local cmd="pw useradd -n '$_user_name'"
486                 [ "$user_gid"      ] && cmd="$cmd -g '$_user_gid'"
487                 [ "$user_home_dir" ] && cmd="$cmd -d '$_user_home_dir'"
488                 [ "$user_shell"    ] && cmd="$cmd -s '$_user_shell'"
489                 [ "$user_uid"      ] && cmd="$cmd -u '$_user_uid'"
490                 [ "$user_account_expire" -o "$no_account_expire" ] &&
491                         cmd="$cmd -e '$_user_account_expire'"
492                 [ "$user_class" -o "$null_class" ] &&
493                         cmd="$cmd -L '$_user_class'"
494                 [ "$user_gecos" -o "$null_gecos" ] &&
495                         cmd="$cmd -c '$_user_gecos'"
496                 [ "$user_member_groups" -o "$null_members" ] &&
497                         cmd="$cmd -G '$_user_member_groups'"
498                 [ "$user_password_expire" -o "$no_password_expire" ] &&
499                         cmd="$cmd -p '$_user_password_expire'"
500
501                 # Execute the command
502                 local retval err
503                 if [ "$user_password_disable" ]; then
504                         f_eval_catch -k err $funcname pw '%s -h -' "$cmd"
505                 elif [ "$user_password" ]; then
506                         err=$( echo "$user_password" | f_eval_catch -de \
507                                 $funcname pw '%s -h 0' "$cmd" 2>&1 )
508                 else
509                         f_eval_catch -k err $funcname pw '%s' "$cmd"
510                 fi
511                 retval=$?
512                 if [ $retval -ne $SUCCESS ]; then
513                         f_show_err "%s" "$err"
514                         return $retval
515                 fi
516
517                 # Create home directory if desired
518                 [ "${user_home_create:-$msg_no}" != "$msg_no" ] &&
519                         f_user_create_homedir "$user_name"
520
521                 # Copy dotfiles if desired
522                 [ "${user_dotfiles_create:-$msg_no}" != "$msg_no" ] &&
523                         f_user_copy_dotfiles "$user_name"
524         fi
525
526         f_dialog_title "$title"
527         $alert "$msg_login_added"
528         f_dialog_title_restore
529         [ "$no_confirm" -a "$USE_DIALOG" ] && sleep 1
530
531         return $SUCCESS
532 }
533
534 # f_user_delete [$user]
535 #
536 # Delete a user. If both $user (as a first argument) and $VAR_USER are unset or
537 # NULL and we are running interactively, prompt the end-user to select a user
538 # account from a list of those available. Variables that can be used to script
539 # user input:
540 #
541 #       VAR_USER [Optional if running interactively]
542 #               The user to delete. Ignored if given non-NULL first-argument.
543 #
544 # Returns success if the user account was successfully deleted.
545 #
546 f_user_delete()
547 {
548         local funcname=f_user_delete
549         local title # Calculated below
550         local alert=f_show_msg no_confirm=
551
552         f_getvar $VAR_NO_CONFIRM no_confirm
553         [ "$no_confirm" ] && alert=f_show_info
554
555         local input
556         f_getvar 3:-\$$VAR_USER input "$1"
557
558         if f_interactive && [ ! "$input" ]; then
559                 f_dialog_menu_user_list || return $SUCCESS
560                 f_dialog_menutag_fetch input
561                 [ "$input" = "X $msg_exit" ] && return $SUCCESS
562         elif [ ! "$input" ]; then
563                 f_show_err "$msg_no_user_specified"
564                 return $FAILURE
565         fi
566
567         local user_account_expire user_class user_gecos user_gid user_home_dir
568         local user_member_groups user_name user_password user_password_expire
569         local user_shell user_uid # Variables created by f_input_user() below
570         if [ "$input" ] && ! f_input_user "$input"; then
571                 f_show_err "$msg_login_not_found" "$input"
572                 return $FAILURE
573         fi
574
575         local user_group_delete= user_home_delete=
576         f_getvar $VAR_USER_GROUP_DELETE:-\$msg_no user_group_delete
577         f_getvar $VAR_USER_HOME_DELETE:-\$msg_no  user_home_delete
578
579         # Attempt to translate user GID into a group name
580         local user_group
581         if user_group=$( pw groupshow -g "$user_gid" 2> /dev/null ); then
582                 user_group="${user_group%%:*}"
583                 # Default to delete the primary group if no script-override and
584                 # exists with same name as the user (same logic used by pw(8))
585                 f_isset $VAR_USER_GROUP_DELETE ||
586                         [ "$user_group" != "$user_name" ] ||
587                         user_group_delete="$msg_yes"
588         fi
589
590         #
591         # Loop until the user decides to Exit, Cancel, or presses ESC
592         #
593         title="$msg_delete $msg_user: $user_name"
594         if f_interactive; then
595                 local mtag retval defaultitem=
596                 while :; do
597                         f_dialog_title "$title"
598                         f_dialog_menu_user_delete "$user_name" "$defaultitem"
599                         retval=$?
600                         f_dialog_title_restore
601                         f_dialog_menutag_fetch mtag
602                         f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
603                         defaultitem="$mtag"
604
605                         # Return if user either pressed ESC or chose Cancel/No
606                         [ $retval -eq $DIALOG_OK ] || return $FAILURE
607
608                         case "$mtag" in
609                         X) # Delete/Exit
610                            f_shell_escape "$user_uid" _user_uid
611
612                            # Save group information in case pw(8) deletes it
613                            # and we wanted to keep it (to be restored below)
614                            if [ "${user_group_delete:-$msg_no}" = "$msg_no" ]
615                            then
616                                 local v vars="gid members name password"
617                                 for v in $vars; do local group_$var; done
618                                 f_input_group "$user_group"
619
620                                 # Remove user-to-delete from group members
621                                 # NB: Otherwise group restoration could fail
622                                 local name length=0 _members=
623                                 while [ $length -ne ${#group_members} ]; do
624                                         name="${group_members%%,*}"
625                                         [ "$name" != "$user_name" ] &&
626                                                 _members="$_members,$name"
627                                         length=${#group_members}
628                                         group_members="${group_members#*,}"
629                                 done
630                                 group_members="${_members#,}"
631
632                                 # Create escaped variables for f_eval_catch()
633                                 for v in $vars; do
634                                         local _group_$v
635                                         eval f_shell_escape \
636                                                 \"\$group_$v\" _group_$v
637                                 done
638                            fi
639
640                            # Delete the user (if asked to delete home directory
641                            # display [X]dialog notification to show activity)
642                            local cmd="pw userdel -u '$_user_uid'"
643                            if [ "$user_home_delete" = "$msg_yes" -a \
644                                 "$USE_XDIALOG" ]
645                            then
646                                 local err
647                                 err=$(
648                                         exec 9>&1
649                                         f_eval_catch -e $funcname pw \
650                                           "%s -r" "$cmd" \
651                                           >&$DIALOG_TERMINAL_PASSTHRU_FD 2>&9 |
652                                           f_xdialog_info \
653                                                 "$msg_deleting_home_directory"
654                                 )
655                                 [ ! "$err" ]
656                            elif [ "$user_home_delete" = "$msg_yes" ]; then
657                                 f_dialog_info "$msg_deleting_home_directory"
658                                 f_eval_catch $funcname pw '%s -r' "$cmd"
659                            else
660                                 f_eval_catch $funcname pw '%s' "$cmd"
661                            fi || continue
662
663                            #
664                            # pw(8) may conditionally delete the primary group,
665                            # which may not be what is desired.
666                            #
667                            # If we've been asked to delete the group and pw(8)
668                            # chose not to, delete it. Otherwise, if we're told
669                            # to NOT delete the group, we may need to restore it
670                            # since pw(8) doesn't have a flag to tell `userdel'
671                            # to not delete the group.
672                            # 
673                            # NB: If primary group and user have different names
674                            # the group may not have been deleted (again, see PR
675                            # 169471 and SVN r263114 for details).
676                            #
677                            if [ "${user_group_delete:-$msg_no}" != "$msg_no" ]
678                            then
679                                 f_quietly pw groupshow -g "$user_gid" &&
680                                 f_eval_catch $funcname pw \
681                                         "pw groupdel -g '%s'" "$_user_gid"
682                            elif ! f_quietly pw groupshow -g "$group_gid" &&
683                                 [ "$group_name" -a "$group_gid" ]
684                            then
685                                 # Group deleted by pw(8), so restore it
686                                 local cmd="pw groupadd -n '$_group_name'"
687                                 cmd="$cmd -g '$_group_gid'"
688                                 cmd="$cmd -M '$_group_members'"
689
690                                 # Get the group password (pw(8) groupshow does
691                                 # NOT provide this (even if running privileged)
692                                 local group_password_enc
693                                 group_password_enc=$( getent group | awk -F: '
694                                         !/^[[:space:]]*(#|$)/ && \
695                                             $1 == ENVIRON["group_name"] && \
696                                             $3 == ENVIRON["group_gid"] && \
697                                             $4 == ENVIRON["group_members"] \
698                                         { print $2; exit }
699                                 ' )
700                                 if [ "$group_password_enc" ]; then
701                                         echo "$group_password_enc" |
702                                                 f_eval_catch $funcname \
703                                                         pw '%s -H 0' "$cmd"
704                                 else
705                                         f_eval_catch $funcname \
706                                                 pw '%s -h -' "$cmd"
707                                 fi
708                            fi
709
710                            break # to success
711                            ;;
712                         1) # Login (select different login from list)
713                            f_dialog_menu_user_list "$user_name" || continue
714                            f_dialog_menutag_fetch mtag
715
716                            [ "$mtag" = "X $msg_exit" ] && continue
717
718                            if ! f_input_user "$mtag"; then
719                                 f_show_err "$msg_login_not_found" "$mtag"
720                                 # Attempt to fall back to previous selection
721                                 f_input_user "$input" || return $FAILURE
722                            else
723                                 input="$mtag"
724                            fi
725                            title="$msg_delete $msg_user: $user_name"
726                            ;;
727                         C) # Delete Primary Group?
728                            if [ "${user_group_delete:-$msg_no}" != "$msg_no" ]
729                            then
730                                 user_group_delete="$msg_no"
731                            else
732                                 user_group_delete="$msg_yes"
733                            fi ;;
734                         D) # Delete Home Directory?
735                            if [ "${user_home_delete:-$msg_no}" != "$msg_no" ]
736                            then
737                                 user_home_delete="$msg_no"
738                            else
739                                 user_home_delete="$msg_yes"
740                            fi ;;
741                         esac
742                 done
743         else
744                 f_shell_escape "$user_uid" _user_uid
745
746                 # Save group information in case pw(8) deletes it
747                 # and we wanted to keep it (to be restored below)
748                 if [ "${user_group_delete:-$msg_no}" = "$msg_no" ]; then
749                         local v vars="gid members name password"
750                         for v in $vars; do local group_$v; done
751                         f_input_group "$user_group"
752
753                         # Remove user we're about to delete from group members
754                         # NB: Otherwise group restoration could fail
755                         local name length=0 _members=
756                         while [ $length -ne ${#group_members} ]; do
757                                 name="${group_members%%,*}"
758                                 [ "$name" != "$user_name" ] &&
759                                         _members="$_members,$name"
760                                 length=${#group_members}
761                                 group_members="${group_members#*,}"
762                         done
763                         group_members="${_members#,}"
764
765                         # Create escaped variables for later f_eval_catch()
766                         for v in $vars; do
767                                 local _group_$v
768                                 eval f_shell_escape \"\$group_$v\" _group_$v
769                         done
770                 fi
771
772                 # Delete the user (if asked to delete home directory
773                 # display [X]dialog notification to show activity)
774                 local err cmd="pw userdel -u '$_user_uid'"
775                 if [ "$user_home_delete" = "$msg_yes" -a "$USE_XDIALOG" ]; then
776                         err=$(
777                                 exec 9>&1
778                                 f_eval_catch -de $funcname pw \
779                                         '%s -r' "$cmd" 2>&9 | f_xdialog_info \
780                                         "$msg_deleting_home_directory"
781                         )
782                         [ ! "$err" ]
783                 elif [ "$user_home_delete" = "$msg_yes" ]; then
784                         f_dialog_info "$msg_deleting_home_directory"
785                         f_eval_catch -k err $funcname pw '%s -r' "$cmd"
786                 else
787                         f_eval_catch -k err $funcname pw '%s' "$cmd"
788                 fi
789                 local retval=$?
790                 if [ $retval -ne $SUCCESS ]; then
791                         f_show_err "%s" "$err"
792                         return $retval
793                 fi
794
795                 #
796                 # pw(8) may conditionally delete the primary group, which may
797                 # not be what is desired.
798                 #
799                 # If we've been asked to delete the group and pw(8) chose not
800                 # to, delete it. Otherwise, if we're told to NOT delete the
801                 # group, we may need to restore it since pw(8) doesn't have a
802                 # flag to tell `userdel' to not delete the group.
803                 # 
804                 # NB: If primary group and user have different names the group
805                 # may not have been deleted (again, see PR 169471 and SVN
806                 # r263114 for details).
807                 #
808                 if [ "${user_group_delete:-$msg_no}" != "$msg_no" ]
809                 then
810                         f_quietly pw groupshow -g "$user_gid" &&
811                         f_eval_catch $funcname pw \
812                                 "pw groupdel -g '%s'" "$_user_gid"
813                 elif ! f_quietly pw groupshow -g "$group_gid" &&
814                      [ "$group_name" -a "$group_gid" ]
815                 then
816                         # Group deleted by pw(8), so restore it
817                         local cmd="pw groupadd -n '$_group_name'"
818                         cmd="$cmd -g '$_group_gid'"
819                         cmd="$cmd -M '$_group_members'"
820                         local group_password_enc
821                         group_password_enc=$( getent group | awk -F: '
822                                 !/^[[:space:]]*(#|$)/ && \
823                                     $1 == ENVIRON["group_name"] && \
824                                     $3 == ENVIRON["group_gid"] && \
825                                     $4 == ENVIRON["group_members"] \
826                                 { print $2; exit }
827                         ' )
828                         if [ "$group_password_enc" ]; then
829                                 echo "$group_password_enc" |
830                                         f_eval_catch $funcname \
831                                                 pw '%s -H 0' "$cmd"
832                         else
833                                 f_eval_catch $funcname pw '%s -h -' "$cmd"
834                         fi
835                 fi
836         fi
837
838         f_dialog_title "$title"
839         $alert "$msg_login_deleted"
840         f_dialog_title_restore
841         [ "$no_confirm" -a "$USE_DIALOG" ] && sleep 1
842
843         return $SUCCESS
844 }
845
846 # f_user_edit [$user]
847 #
848 # Modify a login account. If both $user (as a first argument) and $VAR_USER are
849 # unset or NULL and we are running interactively, prompt the end-user to select
850 # a login account from a list of those available. Variables that can be used to
851 # script user input:
852 #
853 #       VAR_USER [Optional if running interactively]
854 #               The login to modify. Ignored if given non-NULL first-argument.
855 #       VAR_USER_ACCOUNT_EXPIRE [Optional]
856 #               The account expiration time. Format is similar to
857 #               VAR_USER_PASSWORD_EXPIRE variable below. If unset, account
858 #               expiry is unchanged. If set but NULL, account expiration is
859 #               disabled (same as setting a value of `0').
860 #       VAR_USER_DOTFILES_CREATE [Optional]
861 #               If non-NULL, re-populate the user's home directory with the
862 #               template files found in $udotdir (`/usr/share/skel' default).
863 #       VAR_USER_GECOS [Optional]
864 #               Often the full name of the account holder. If unset, the GECOS
865 #               field is unmodified. If set but NULL, the field is blanked.
866 #       VAR_USER_GID [Optional]
867 #               Numerical primary-group ID to set. If NULL or unset, the group
868 #               ID is unchanged.
869 #       VAR_USER_GROUPS [Optional]
870 #               Comma-separated list of additional groups to which the user is
871 #               a member of. If set but NULL, group memberships are reset (this
872 #               login will not be a member of any additional groups besides the
873 #               primary group). If unset, group membership is unmodified.
874 #       VAR_USER_HOME [Optional]
875 #               The home directory to set. If NULL or unset, the home directory
876 #               is unchanged.
877 #       VAR_USER_HOME_CREATE [Optional]
878 #               If non-NULL, create the user's home directory if it doesn't
879 #               already exist.
880 #       VAR_USER_LOGIN_CLASS [Optional]
881 #               Login class to set. If unset, the login class is unchanged. If
882 #               set but NULL, the field is blanked.
883 #       VAR_USER_PASSWORD [Optional]
884 #               Unencrypted password to set. If unset, the login password is
885 #               unmodified. If set but NULL, password authentication for the
886 #               login is disabled.
887 #       VAR_USER_PASSWORD_EXPIRE [Optional]
888 #               The password expiration time. Format of the date is either a
889 #               UNIX time in decimal, or a date in dd-mmm-yy[yy] format, where
890 #               dd is the day, mmm is the month in either numeric or alphabetic
891 #               format, and yy[yy] is either a two or four digit year. This
892 #               variable also accepts a relative date in the form of +n[mhdwoy]
893 #               where n is a decimal, octal (leading 0) or hexadecimal (leading
894 #               0x) digit followed by the number of Minutes, Hours, Days,
895 #               Weeks, Months or Years from the current date at which the
896 #               expiration time is to be set. If unset, password expiry is
897 #               unchanged. If set but NULL, password expiration is disabled
898 #               (same as setting a value of `0').
899 #       VAR_USER_SHELL [Optional]
900 #               Path to login shell to set. If NULL or unset, the shell is
901 #               unchanged.
902 #       VAR_USER_UID [Optional]
903 #               Numerical user ID to set. If NULL or unset, the user ID is
904 #               unchanged.
905 #
906 # Returns success if the user account was successfully modified.
907 #
908 f_user_edit()
909 {
910         local funcname=f_user_edit
911         local title # Calculated below
912         local alert=f_show_msg no_confirm=
913
914         f_getvar $VAR_NO_CONFIRM no_confirm
915         [ "$no_confirm" ] && alert=f_show_info
916
917         local input
918         f_getvar 3:-\$$VAR_USER input "$1"
919
920         #
921         # NB: pw(8) has a ``feature'' wherein `-n name' can be taken as UID
922         # instead of name. Work-around is to also pass `-u UID' at the same
923         # time (the UID is ignored in this case, so any UID will do).
924         #
925         if [ "$input" ] && ! f_quietly pw usershow -n "$input" -u 1337; then
926                 f_show_err "$msg_login_not_found" "$input"
927                 return $FAILURE
928         fi
929
930         if f_interactive && [ ! "$input" ]; then
931                 f_dialog_menu_user_list || return $SUCCESS
932                 f_dialog_menutag_fetch input
933                 [ "$input" = "X $msg_exit" ] && return $SUCCESS
934         elif [ ! "$input" ]; then
935                 f_show_err "$msg_no_user_specified"
936                 return $FAILURE
937         fi
938
939         local user_account_expire user_class user_gecos user_gid user_home_dir
940         local user_member_groups user_name user_password user_password_expire
941         local user_shell user_uid # Variables created by f_input_user() below
942         if ! f_input_user "$input"; then
943                 f_show_err "$msg_login_not_found" "$input"
944                 return $FAILURE
945         fi
946
947         #
948         # Override values probed by f_input_user() with desired values
949         #
950         f_isset $VAR_USER_GID   && f_getvar $VAR_USER_GID   user_gid
951         f_isset $VAR_USER_HOME  && f_getvar $VAR_USER_HOME  user_home_dir
952         f_isset $VAR_USER_SHELL && f_getvar $VAR_USER_SHELL user_shell
953         f_isset $VAR_USER_UID   && f_getvar $VAR_USER_UID   user_uid
954         local user_dotfiles_create= user_home_create=
955         f_getvar $VAR_USER_DOTFILES_CREATE:+\$msg_yes user_dotfiles_create
956         f_getvar $VAR_USER_HOME_CREATE:+\$msg_yes     user_home_create
957         local no_account_expire=
958         if f_isset $VAR_USER_ACCOUNT_EXPIRE; then
959                 f_getvar $VAR_USER_ACCOUNT_EXPIRE user_account_expire
960                 [ "$user_account_expire" ] || no_account_expire=1
961         fi
962         local null_gecos=
963         if f_isset $VAR_USER_GECOS; then
964                 f_getvar $VAR_USER_GECOS user_gecos
965                 [ "$user_gecos" ] || null_gecos=1
966         fi
967         local null_members=
968         if f_isset $VAR_USER_GROUPS; then
969                 f_getvar $VAR_USER_GROUPS user_member_groups
970                 [ "$user_member_groups" ] || null_members=1
971         fi
972         local null_class=
973         if f_isset $VAR_USER_LOGIN_CLASS; then
974                 f_getvar $VAR_USER_LOGIN_CLASS user_class
975                 [ "$user_class" ] || null_class=1
976         fi
977         local user_password_disable=
978         if f_isset $VAR_USER_PASSWORD; then
979                 f_getvar $VAR_USER_PASSWORD user_password
980                 [ "$user_password" ] || user_password_disable=1
981         fi
982         local no_password_expire=
983         if f_isset $VAR_USER_PASSWORD_EXPIRE; then
984                 f_getvar $VAR_USER_PASSWORD_EXPIRE user_password_expire
985                 [ "$user_password_expire" ] || no_password_expire=1
986         fi
987
988         #
989         # Loop until the user decides to Exit, Cancel, or presses ESC
990         #
991         title="$msg_edit_view $msg_user: $user_name"
992         if f_interactive; then
993                 local mtag retval defaultitem=
994                 while :; do
995                         f_dialog_title "$title"
996                         f_dialog_menu_user_edit "$defaultitem"
997                         retval=$?
998                         f_dialog_title_restore
999                         f_dialog_menutag_fetch mtag
1000                         f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
1001                         defaultitem="$mtag"
1002
1003                         # Return if user either pressed ESC or chose Cancel/No
1004                         [ $retval -eq $DIALOG_OK ] || return $FAILURE
1005
1006                         case "$mtag" in
1007                         X) # Save/Exit
1008                            local var
1009                            for var in account_expire class gecos gid home_dir \
1010                                 member_groups name password_expire shell uid \
1011                            ; do
1012                                 local _user_$var
1013                                 eval f_shell_escape \"\$user_$var\" _user_$var
1014                            done
1015
1016                            local cmd="pw usermod -n '$_user_name'"
1017                            [ "$user_gid"   ] && cmd="$cmd -g '$_user_gid'"
1018                            [ "$user_shell" ] && cmd="$cmd -s '$_user_shell'"
1019                            [ "$user_uid"   ] && cmd="$cmd -u '$_user_uid'"
1020                            [ "$user_account_expire" -o \
1021                              "$no_account_expire" ] &&
1022                                 cmd="$cmd -e '$_user_account_expire'"
1023                            [ "$user_class" -o "$null_class" ] &&
1024                                 cmd="$cmd -L '$_user_class'"
1025                            [ "$user_gecos" -o "$null_gecos" ] &&
1026                                 cmd="$cmd -c '$_user_gecos'"
1027                            [ "$user_home_dir"  ] &&
1028                                 cmd="$cmd -d '$_user_home_dir'"
1029                            [ "$user_member_groups" -o "$null_members" ] &&
1030                                 cmd="$cmd -G '$_user_member_groups'"
1031                            [ "$user_password_expire" -o \
1032                              "$no_password_expire" ] &&
1033                                 cmd="$cmd -p '$_user_password_expire'"
1034
1035                            # Execute the command
1036                            if [ "$user_password_disable" ]; then
1037                                 f_eval_catch $funcname pw '%s -h -' "$cmd"
1038                            elif [ "$user_password" ]; then
1039                                 echo "$user_password" | f_eval_catch \
1040                                         $funcname pw '%s -h 0' "$cmd"
1041                            else
1042                                 f_eval_catch $funcname pw '%s' "$cmd"
1043                            fi || continue
1044
1045                            # Create home directory if desired
1046                            [ "${user_home_create:-$msg_no}" != "$msg_no" ] &&
1047                                 f_user_create_homedir "$user_name"
1048
1049                            # Copy dotfiles if desired
1050                            [ "${user_dotfiles_create:-$msg_no}" != \
1051                              "$msg_no" ] && f_user_copy_dotfiles "$user_name"
1052
1053                            break # to success
1054                            ;;
1055                         1) # Login (select different login from list)
1056                            f_dialog_menu_user_list "$user_name" || continue
1057                            f_dialog_menutag_fetch mtag
1058
1059                            [ "$mtag" = "X $msg_exit" ] && continue
1060
1061                            if ! f_input_user "$mtag"; then
1062                                 f_show_err "$msg_login_not_found" "$mtag"
1063                                 # Attempt to fall back to previous selection
1064                                 f_input_user "$input" || return $FAILURE
1065                            else
1066                                 input="$mtag"
1067                            fi
1068                            title="$msg_edit_view $msg_user: $user_name"
1069                            ;;
1070                         2) # Full Name
1071                            f_dialog_input_gecos user_gecos "$user_gecos" &&
1072                                 [ ! "$user_gecos" ] && null_gecos=1 ;;
1073                         3) # Password
1074                            f_dialog_input_password \
1075                                 user_password user_password_disable ;;
1076                         4) # User ID
1077                            f_dialog_input_uid user_uid "$user_uid" ;;
1078                         5) # Group ID
1079                            f_dialog_input_gid user_gid "$user_gid" ;;
1080                         6) # Member of Groups
1081                            f_dialog_input_member_groups \
1082                                 user_member_groups "$user_member_groups" &&
1083                                 [ ! "$user_member_groups" ] &&
1084                                 null_members=1 ;;
1085                         7) # Login Class
1086                            f_dialog_input_class user_class "$user_class" &&
1087                                 [ ! "$user_class" ] && null_class=1 ;;
1088                         8) # Password Expires On
1089                            f_dialog_input_expire_password \
1090                                 user_password_expire "$user_password_expire" &&
1091                                 [ ! "$user_password_expire" ] &&
1092                                 no_password_expire=1 ;;
1093                         9) # Account Expires On
1094                            f_dialog_input_expire_account \
1095                                 user_account_expire "$user_account_expire" &&
1096                                 [ ! "$user_account_expire" ] &&
1097                                 no_account_expire=1 ;;
1098                         A) # Home Directory
1099                            f_dialog_input_home_dir \
1100                                 user_home_dir "$user_home_dir" ;;
1101                         B) # Shell
1102                            f_dialog_input_shell user_shell "$user_shell" ;;
1103                         C) # Create Home Directory?
1104                            if [ "${user_home_create:-$msg_no}" != "$msg_no" ]
1105                            then
1106                                 user_home_create="$msg_no"
1107                            else
1108                                 user_home_create="$msg_yes"
1109                            fi ;;
1110                         D) # Create Dotfiles?
1111                            if [ "${user_dotfiles_create:-$msg_no}" != \
1112                                 "$msg_no" ]
1113                            then
1114                                 user_dotfiles_create="$msg_no"
1115                            else
1116                                 user_dotfiles_create="$msg_yes"
1117                            fi ;;
1118                         esac
1119                 done
1120         else
1121                 local var
1122                 for var in account_expire class gecos gid home_dir \
1123                         member_groups name password_expire shell uid \
1124                 ; do
1125                         local _user_$var
1126                         eval f_shell_escape \"\$user_$var\" _user_$var
1127                 done
1128
1129                 # Form the command
1130                 local cmd="pw usermod -n '$_user_name'"
1131                 [ "$user_gid"      ] && cmd="$cmd -g '$_user_gid'"
1132                 [ "$user_home_dir" ] && cmd="$cmd -d '$_user_home_dir'"
1133                 [ "$user_shell"    ] && cmd="$cmd -s '$_user_shell'"
1134                 [ "$user_uid"      ] && cmd="$cmd -u '$_user_uid'"
1135                 [ "$user_account_expire" -o "$no_account_expire" ] &&
1136                         cmd="$cmd -e '$_user_account_expire'"
1137                 [ "$user_class" -o "$null_class" ] &&
1138                         cmd="$cmd -L '$_user_class'"
1139                 [ "$user_gecos" -o "$null_gecos" ] &&
1140                         cmd="$cmd -c '$_user_gecos'"
1141                 [ "$user_member_groups" -o "$null_members" ] &&
1142                         cmd="$cmd -G '$_user_member_groups'"
1143                 [ "$user_password_expire" -o "$no_password_expire" ] &&
1144                         cmd="$cmd -p '$_user_password_expire'"
1145
1146                 # Execute the command
1147                 local retval err
1148                 if [ "$user_password_disable" ]; then
1149                         f_eval_catch -k err $funcname pw '%s -h -' "$cmd"
1150                 elif [ "$user_password" ]; then
1151                         err=$( echo "$user_password" | f_eval_catch -de \
1152                                 $funcname pw '%s -h 0' "$cmd" 2>&1 )
1153                 else
1154                         f_eval_catch -k err $funcname pw '%s' "$cmd"
1155                 fi
1156                 retval=$?
1157                 if [ $retval -ne $SUCCESS ]; then
1158                         f_show_err "%s" "$err"
1159                         return $retval
1160                 fi
1161
1162                 # Create home directory if desired
1163                 [ "${user_home_create:-$msg_no}" != "$msg_no" ] &&
1164                         f_user_create_homedir "$user_name"
1165
1166                 # Copy dotfiles if desired
1167                 [ "${user_dotfiles_create:-$msg_no}" != "$msg_no" ] &&
1168                         f_user_copy_dotfiles "$user_name"
1169         fi
1170
1171         f_dialog_title "$title"
1172         $alert "$msg_login_updated"
1173         f_dialog_title_restore
1174         [ "$no_confirm" -a "$USE_DIALOG" ] && sleep 1
1175
1176         return $SUCCESS
1177 }
1178
1179 ############################################################ MAIN
1180
1181 f_dprintf "%s: Successfully loaded." usermgmt/user.subr
1182
1183 fi # ! $_USERMGMT_USER_SUBR