From 33929c79234f299c2c45f4278f4f6194993341f1 Mon Sep 17 00:00:00 2001 From: dteske Date: Tue, 1 Apr 2014 00:19:13 +0000 Subject: MFC revisions 260894,260899,262895-262902,262904,262908-262910,262982, 262984,263133-263137,263139,263141,263144-263150, and (partially) 263249 (31 revisions; summarized below)... r260894: Optimize f_expand_number(), improving performance r260899: s/__number/__num/ in f_expand_number() r262895: Allow dispatched reswords to carry arguments r262896: Add missing local declaration r262897: Fix a typo in a comment r262898: Fix incorrect return status if var_to_set and var_to_get are same r262899: Make f_show_err non-fatal r262900: Centralize function name; Update a comment while here r262901: s/__num/__number/ in f_expand_number() r262902: Comment to go with NL global introduced by previous commit r262904: Rewrite groupmgmt -- hooking it into the scripting system r262908: Change dispatch words from group* to *Group for backward compat r262909: Fix copy/paste error in a comment r262910: Take a group name on the command-line if available r262982: Whitespace r262984: Remove vestigial global, no longer used since r262904 r263133: Remove indexfile from debug statement (already logged) r263134: Add debug statement just before attempting to exec a module r263135: Comments r263136: Update copyright r263137: Fix future namespace issues for functions taking $var_to_set r263139: Remove useless NULL string in compound strings r263141: Pointy hat! Fix a broken f_isinteger() r263144: Fix a code-typo that prevented auto-sizing of a dialog r263145: Fix comments and whitespace r263146: Reduce the sleep cycle when using dialog(1) [infobox] to 1-second r263147: Fix a bug preventing errors from pw(8) from appearing r263148: For non-interactive scripts, forgot to check argument r263149: Add protection against input containing single-quotes r263150: Rewrite usermgmt r263249: (partial) Add more obsolete files --- ObsoleteFiles.inc | 4 + usr.sbin/bsdconfig/bsdconfig | 8 +- usr.sbin/bsdconfig/share/common.subr | 13 +- usr.sbin/bsdconfig/share/device.subr | 10 +- usr.sbin/bsdconfig/share/dialog.subr | 74 +- usr.sbin/bsdconfig/share/keymap.subr | 2 +- usr.sbin/bsdconfig/share/script.subr | 36 +- usr.sbin/bsdconfig/share/strings.subr | 41 +- usr.sbin/bsdconfig/share/variable.subr | 21 +- usr.sbin/bsdconfig/timezone/share/menus.subr | 4 +- usr.sbin/bsdconfig/usermgmt/Makefile | 3 +- usr.sbin/bsdconfig/usermgmt/groupadd | 19 +- usr.sbin/bsdconfig/usermgmt/groupdel | 16 +- usr.sbin/bsdconfig/usermgmt/groupedit | 18 +- usr.sbin/bsdconfig/usermgmt/groupinput | 293 ----- usr.sbin/bsdconfig/usermgmt/include/messages.subr | 17 +- usr.sbin/bsdconfig/usermgmt/share/Makefile | 2 +- usr.sbin/bsdconfig/usermgmt/share/group.subr | 518 ++++++++ usr.sbin/bsdconfig/usermgmt/share/group_input.subr | 480 ++++--- usr.sbin/bsdconfig/usermgmt/share/user.subr | 1184 +++++++++++++++++ usr.sbin/bsdconfig/usermgmt/share/user_input.subr | 1345 ++++++++++++-------- usr.sbin/bsdconfig/usermgmt/useradd | 19 +- usr.sbin/bsdconfig/usermgmt/userdel | 16 +- usr.sbin/bsdconfig/usermgmt/useredit | 18 +- usr.sbin/bsdconfig/usermgmt/userinput | 508 -------- 25 files changed, 3089 insertions(+), 1580 deletions(-) delete mode 100755 usr.sbin/bsdconfig/usermgmt/groupinput create mode 100644 usr.sbin/bsdconfig/usermgmt/share/group.subr create mode 100644 usr.sbin/bsdconfig/usermgmt/share/user.subr delete mode 100755 usr.sbin/bsdconfig/usermgmt/userinput diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc index a6b7aa2..6a7020c 100644 --- a/ObsoleteFiles.inc +++ b/ObsoleteFiles.inc @@ -75,6 +75,10 @@ OLD_FILES+=usr/include/clang/3.3/xopintrin.h OLD_FILES+=usr/share/man/man1/llvm-prof.1.gz OLD_FILES+=usr/share/man/man1/llvm-ranlib.1.gz OLD_DIRS+=usr/include/clang/3.3 +# 20140314: bsdconfig usermgmt rewrite +OLD_FILES+=usr/libexec/bsdconfig/070.usermgmt/userinput +# 20140307: bsdconfig groupmgmt rewrite +OLD_FILES+=usr/libexec/bsdconfig/070.usermgmt/groupinput # 20131109: extattr(2) mlinks fixed OLD_FILES+=usr/share/man/man2/extattr_delete_list.2.gz OLD_FILES+=usr/share/man/man2/extattr_get_list.2.gz diff --git a/usr.sbin/bsdconfig/bsdconfig b/usr.sbin/bsdconfig/bsdconfig index c158bfa..62699f2 100755 --- a/usr.sbin/bsdconfig/bsdconfig +++ b/usr.sbin/bsdconfig/bsdconfig @@ -1,7 +1,7 @@ #!/bin/sh #- # Copyright (c) 2012 Ron McDowell -# Copyright (c) 2012-2013 Devin Teske +# Copyright (c) 2012-2014 Devin Teske # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -315,8 +315,7 @@ if [ "$pgm" != "bsdconfig" ]; then if indexfile=$( f_index_file "$pgm" ) && cmd=$( f_index_menusel_command "$indexfile" "$pgm" ) then - f_dprintf "pgm=[%s] indexfile=[%s] cmd=[%s]" \ - "$pgm" "$indexfile" "$cmd" + f_dprintf "pgm=[%s] cmd=[%s] *=[%s]" "$pgm" "$cmd" "$*" exec "$cmd" "$@" || exit 1 else f_include $BSDCFG_SHARE/script.subr @@ -324,7 +323,7 @@ if [ "$pgm" != "bsdconfig" ]; then [ "$pgm" = "$resword" ] || continue # Found a match f_dprintf "pgm=[%s] A valid resWord!" "$pgm" - f_dispatch $resword + f_dispatch $resword $resword "$@" exit $? done fi @@ -382,6 +381,7 @@ if [ "$1" ]; then # Not reached fi + f_dprintf "cmd=[%s] *=[%s]" "$cmd" "$*" shift exec $cmd ${USE_XDIALOG:+-X} "$@" || exit 1 # Not reached diff --git a/usr.sbin/bsdconfig/share/common.subr b/usr.sbin/bsdconfig/share/common.subr index 94b5cfd..7c3379e 100644 --- a/usr.sbin/bsdconfig/share/common.subr +++ b/usr.sbin/bsdconfig/share/common.subr @@ -1,7 +1,7 @@ if [ ! "$_COMMON_SUBR" ]; then _COMMON_SUBR=1 # # Copyright (c) 2012 Ron McDowell -# Copyright (c) 2012-2013 Devin Teske +# Copyright (c) 2012-2014 Devin Teske # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -153,7 +153,7 @@ f_debug_init() # Process stored command-line arguments # set -- $ARGV - local OPTIND + local OPTIND flag f_dprintf "f_debug_init: ARGV=[%s] GETOPTS_STDARGS=[%s]" \ "$ARGV" "$GETOPTS_STDARGS" while getopts "$GETOPTS_STDARGS$GETOPTS_EXTRA$GETOPTS_ALLFLAGS" flag \ @@ -270,9 +270,9 @@ f_getvar() { local __var_to_get="$1" __var_to_set="$2" [ "$__var_to_set" ] || local value - eval ${__var_to_set:-value}=\"\${$__var_to_get}\" eval [ \"\${$__var_to_get+set}\" ] local __retval=$? + eval ${__var_to_set:-value}=\"\${$__var_to_get}\" eval f_dprintf '"f_getvar: var=[%s] value=[%s] r=%u"' \ \"\$__var_to_get\" \"\$${__var_to_set:-value}\" \$__retval [ "$__var_to_set" ] || { [ "$value" ] && echo "$value"; } @@ -368,13 +368,10 @@ f_show_msg() # f_show_err $format [$arguments ...] # # Display a message in a dialog box with ``Error'' i18n title (overridden by -# setting msg_error) using printf(1) syntax. If running non-interactively, -# the process will terminate (using [above] f_die()). +# setting msg_error) using printf(1) syntax. # f_show_err() { - [ "$nonInteractive" ] && f_die - local msg msg=$( printf "$@" ) @@ -523,7 +520,7 @@ f_include_lang() # f_usage $file "FOO" "BAR" # # Will cause instances of "@FOO@" appearing in $file to be replaced with the -# text "BAR" before bering printed to the screen. +# text "BAR" before being printed to the screen. # # This function is a two-parter. Below is the awk(1) portion of the function, # afterward is the sh(1) function which utilizes the below awk script. diff --git a/usr.sbin/bsdconfig/share/device.subr b/usr.sbin/bsdconfig/share/device.subr index e42751e..9201b95 100644 --- a/usr.sbin/bsdconfig/share/device.subr +++ b/usr.sbin/bsdconfig/share/device.subr @@ -1,6 +1,6 @@ if [ ! "$_DEVICE_SUBR" ]; then _DEVICE_SUBR=1 # -# Copyright (c) 2012-2013 Devin Teske +# Copyright (c) 2012-2014 Devin Teske # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -170,11 +170,10 @@ f_device_reset() for dev in $DEVICES; do f_device_shutdown $dev - # - # XXX this potentially leaks $dev->private if it's being + # XXX This potentially leaks $dev->private if it's being # used to point to something dynamic, but you're not supposed # to call this routine at such times that some open instance - # has its private member pointing somewhere anyway. XXX + # has its private member pointing somewhere anyway. # f_struct_free device_$dev done @@ -325,8 +324,7 @@ f_device_get_all() case "$diskname" in cd*) - # XXX - # Due to unknown reasons, kern.disks returns SCSI + # XXX Due to unknown reasons, kern.disks returns SCSI # CDROM as a valid disk. This will prevent bsdconfig # from presenting SCSI CDROMs as available disks in # various menus. Why GEOM treats SCSI CDROM as a disk diff --git a/usr.sbin/bsdconfig/share/dialog.subr b/usr.sbin/bsdconfig/share/dialog.subr index d05d4fb..e0dfe80 100644 --- a/usr.sbin/bsdconfig/share/dialog.subr +++ b/usr.sbin/bsdconfig/share/dialog.subr @@ -1,6 +1,6 @@ if [ ! "$_DIALOG_SUBR" ]; then _DIALOG_SUBR=1 # -# Copyright (c) 2006-2013 Devin Teske +# Copyright (c) 2006-2014 Devin Teske # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -469,16 +469,17 @@ f_dialog_menu_constrain() # Print debug warnings if any given (non-NULL) argument are invalid # NOTE: Don't change the name of $__{var,min,}{height,width,rows} # - local __height __width __rows + local __height_menu_constrain __width_menu_constrain + local __rows_menu_constrain local __arg __cp __fname=f_dialog_menu_constrain for __arg in height width rows; do debug= f_getvar __var_$__arg __cp [ "$__cp" ] || continue - if ! debug= f_getvar "$__cp" __$__arg; then + if ! debug= f_getvar "$__cp" __${__arg}_menu_constrain; then f_dprintf "%s: var_%s variable \`%s' not set" \ $__fname $__arg "$__cp" __retval=$FAILURE - elif ! eval f_isinteger \$__$__arg; then + elif ! eval f_isinteger \$__${__arg}_menu_constrain; then f_dprintf "%s: var_%s variable value not a number" \ $__fname $__arg __retval=$FAILURE @@ -502,9 +503,11 @@ f_dialog_menu_constrain() # Adjust height if desired if [ "$__var_height" ]; then - if [ $__height -lt ${__min_height:-0} ]; then + if [ $__height_menu_constrain -lt ${__min_height:-0} ]; then setvar "$__var_height" $__min_height - elif [ $__height -gt $__max_height_menu_constrain ]; then + elif [ $__height_menu_constrain -gt \ + $__max_height_menu_constrain ] + then setvar "$__var_height" $__max_height_menu_constrain fi fi @@ -516,9 +519,11 @@ f_dialog_menu_constrain() else : ${__min_width:=${DIALOG_MIN_WIDTH:-24}} fi - if [ $__width -lt $__min_width ]; then + if [ $__width_menu_constrain -lt $__min_width ]; then setvar "$__var_width" $__min_width - elif [ $__width -gt $__max_width_menu_constrain ]; then + elif [ $__width_menu_constrain -gt \ + $__max_width_menu_constrain ] + then setvar "$__var_width" $__max_width_menu_constrain fi fi @@ -531,16 +536,20 @@ f_dialog_menu_constrain() : ${__min_rows:=0} fi - local __max_rows=$(( $__max_height_menu_constrain - 7 )) + local __max_rows_menu_constrain=$(( + $__max_height_menu_constrain - 7 + )) # If prompt_len is zero (no prompt), bump the max-rows by 1 # Default assumption is (if no argument) that there's no prompt - [ ${__prompt_len:-0} -gt 0 ] || - __max_rows=$(( $__max_rows + 1 )) + [ ${__prompt_len:-0} -gt 0 ] || __max_rows_menu_constrain=$(( + $__max_rows_menu_constrain + 1 + )) - if [ $__rows -lt $__min_rows ]; then + if [ $__rows_menu_constrain -lt $__min_rows ]; then setvar "$__var_rows" $__min_rows - elif [ $__rows -gt $__max_rows ]; then - setvar "$__var_rows" $__max_rows + elif [ $__rows_menu_constrain -gt $__max_rows_menu_constrain ] + then + setvar "$__var_rows" $__max_rows_menu_constrain fi fi @@ -1100,19 +1109,20 @@ f_dialog_radiolist_size() # longest item-length (both used to bump the width), and the number of # rows (used to bump the height). # - local __longest_tag=0 __longest_item=0 __rows=0 + local __longest_tag=0 __longest_item=0 __rows_rlist_size=0 while [ $# -ge 3 ]; do local __tag="$1" __item="$2" shift 3 # tag/item/status [ ${#__tag} -gt $__longest_tag ] && __longest_tag=${#__tag} [ ${#__item} -gt $__longest_item ] && __longest_item=${#__item} - __rows=$(( $__rows + 1 )) + __rows_rlist_size=$(( $__rows_rlist_size + 1 )) done # Adjust rows early (for up-coming height calculation) if [ "$__var_height" -o "$__var_rows" ]; then # Add a row for visual aid if using Xdialog(1) - [ "$USE_XDIALOG" ] && __rows=$(( $__rows + 1 )) + [ "$USE_XDIALOG" ] && + __rows_rlist_size=$(( $__rows_rlist_size + 1 )) fi # Adjust height if desired @@ -1120,10 +1130,12 @@ f_dialog_radiolist_size() # Add rows to height if [ "$USE_XDIALOG" ]; then __height_rlist_size=$(( - $__height_rlist_size + $__rows + 7 )) + $__height_rlist_size + $__rows_rlist_size + 7 + )) else __height_rlist_size=$(( - $__height_rlist_size + $__rows + 4 )) + $__height_rlist_size + $__rows_rlist_size + 4 + )) fi setvar "$__var_height" $__height_rlist_size fi @@ -1140,7 +1152,7 @@ f_dialog_radiolist_size() fi # Store adjusted rows if desired - [ "$__var_rows" ] && setvar "$__var_rows" $__rows + [ "$__var_rows" ] && setvar "$__var_rows" $__rows_rlist_size # Constrain height, width, and rows to sensible minimum/maximum values # Return success if no-constrain, else return status from constrain @@ -1220,20 +1232,26 @@ f_dialog_radiolist_with_help_size() # all used to bump the width -- and the number of rows (used to bump # the height). # - local __longest_tag=0 __longest_item=0 __longest_help=0 __rows=0 + local __longest_tag=0 __longest_item=0 __longest_help=0 + local __rows_rlist_with_help_size=0 while [ $# -ge 4 ]; do local __tag="$1" __item="$2" __status="$3" __help="$4" shift 4 # tag/item/status/help [ ${#__tag} -gt $__longest_tag ] && __longest_tag=${#__tag} [ ${#__item} -gt $__longest_item ] && __longest_item=${#__item} [ ${#__help} -gt $__longest_help ] && __longest_help=${#__help} - __rows=$(( $__rows + 1 )) + __rows_rlist_with_help_size=$(( + $__rows_rlist_with_help_size + 1 + )) done # Adjust rows early (for up-coming height calculation) if [ "$__var_height" -o "$__var_rows" ]; then # Add a row for visual aid if using Xdialog(1) - [ "$USE_XDIALOG" ] && __rows=$(( $__rows + 1 )) + [ "$USE_XDIALOG" ] && + __rows_rlist_with_help_size=$(( + $__rows_rlist_with_help_size + 1 + )) fi # Adjust height if desired @@ -1241,10 +1259,14 @@ f_dialog_radiolist_with_help_size() # Add rows to height if [ "$USE_XDIALOG" ]; then __height_rlist_with_help_size=$(( - $__height_rlist_with_help_size + $__rows + 7 )) + $__height_rlist_with_help_size + + $__rows_rlist_with_help_size + 7 + )) else __height_rlist_with_help_size=$(( - $__height_rlist_with_help_size + $__rows + 4 )) + $__height_rlist_with_help_size + + $__rows_rlist_with_help_size + 4 + )) fi setvar "$__var_height" $__height fi @@ -1270,7 +1292,7 @@ f_dialog_radiolist_with_help_size() fi # Store adjusted rows if desired - [ "$__var_rows" ] && setvar "$__var_rows" $__rows + [ "$__var_rows" ] && setvar "$__var_rows" $__rows_rlist_with_help_size # Constrain height, width, and rows to sensible minimum/maximum values # Return success if no-constrain, else return status from constrain diff --git a/usr.sbin/bsdconfig/share/keymap.subr b/usr.sbin/bsdconfig/share/keymap.subr index 39b6b54..d4f391c 100644 --- a/usr.sbin/bsdconfig/share/keymap.subr +++ b/usr.sbin/bsdconfig/share/keymap.subr @@ -165,7 +165,7 @@ f_keymap_get_all() marks[keym] = mark name = keym gsub(/[^[:alnum:]_]/, "_", name) - gsub(/'\''/, "'\''\\'\'''\''", desc); + gsub(/'\''/, "'\''\\'\'\''", desc); printf "f_keymap_checkfile %s && " \ "f_keymap_register %s '\'%s\'' %s %u\n", keym, name, desc, keym, mark diff --git a/usr.sbin/bsdconfig/share/script.subr b/usr.sbin/bsdconfig/share/script.subr index a227419..b562e99 100644 --- a/usr.sbin/bsdconfig/share/script.subr +++ b/usr.sbin/bsdconfig/share/script.subr @@ -1,6 +1,6 @@ if [ ! "$_SCRIPT_SUBR" ]; then _SCRIPT_SUBR=1 # -# Copyright (c) 2012-2013 Devin Teske +# Copyright (c) 2012-2014 Devin Teske # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -37,6 +37,8 @@ f_include $BSDCFG_SHARE/media/tcpip.subr f_include $BSDCFG_SHARE/mustberoot.subr f_include $BSDCFG_SHARE/networking/services.subr f_include $BSDCFG_SHARE/packages/packages.subr +f_include $BSDCFG_SHARE/usermgmt/group.subr +f_include $BSDCFG_SHARE/usermgmt/user.subr f_include $BSDCFG_SHARE/variable.subr ############################################################ GLOBALS @@ -50,25 +52,23 @@ RESWORDS= # Create a new `reserved' word for scripting purposes. Reswords call pre- # defined functions but differ from those functions in the following ways: # -# + Reswords do not take arguments but instead get all their data from -# the environment variable namespace. # + Unless noError is set (must be non-NULL), if calling the resword # results in failure, the application will terminate prematurely. # + noError is unset after each/every resword is called. # # Reswords should not be used in bsdconfig itself (hence the name `reserved -# word') but instead only in scripts loaded through f_script_load()). +# word') but instead only in scripts loaded through f_script_load(). # f_resword_new() { local resword="$1" func="$2" [ "$resword" ] || return $FAILURE f_dprintf "script.subr: New resWord %s -> %s" "$resword" "$func" - eval $resword\(\){ f_dispatch $func $resword\; } + eval $resword\(\){ f_dispatch $func $resword \"\$@\"\; } RESWORDS="$RESWORDS${RESWORDS:+ }$resword" } -# f_dispatch $func [$resword] +# f_dispatch $func $resword # # Wrapper function used by `reserved words' (reswords) to call other functions. # If $noError is set and non-NULL, a failure result from $func is ignored, @@ -78,9 +78,10 @@ f_resword_new() # f_dispatch() { - local func="$1" resword="${2:-$1}" + local func="$1" resword="$2" + shift 2 # func resword f_dprintf "f_dispatch: calling resword \`%s'" "$resword" - eval $func + eval $func "$@" local retval=$? if [ $retval -ne $SUCCESS ]; then local _ignore_this_error @@ -96,7 +97,7 @@ f_dispatch() # Load a script (usually filled with reswords). If $file is missing or NULL, # use one of the following instead (in order): # -# $configFile +# $configFile (global) # install.cfg # /stand/install.fg # /tmp/install.cfg @@ -108,9 +109,10 @@ f_dispatch() # f_script_load() { + local funcname=f_script_load local script="$1" config_file retval=$SUCCESS - f_dprintf "f_script_load: script=[%s]" "$script" + f_dprintf "$funcname: script=[%s]" "$script" if [ ! "$script" ]; then f_getvar $VAR_CONFIG_FILE config_file for script in \ @@ -130,11 +132,11 @@ f_script_load() setvar $VAR_NONINTERACTIVE yes if [ "$script" = "-" ]; then - f_dprintf "f_script_load: Loading script from stdin" + f_dprintf "$funcname: Loading script from stdin" eval "$( cat )" retval=$? else - f_dprintf "f_script_load: Loading script \`%s'" "$script" + f_dprintf "$funcname: Loading script \`%s'" "$script" if [ ! -e "$script" ]; then f_show_msg "$msg_unable_to_open" "$script" return $FAILURE @@ -198,6 +200,16 @@ f_resword_new packageAdd f_package_add f_resword_new packageDelete f_package_delete f_resword_new packageReinstall f_package_reinstall +# usermgmt/group.subr +f_resword_new addGroup f_group_add +f_resword_new deleteGroup f_group_delete +f_resword_new editGroup f_group_edit + +# usermgmt/user.subr +f_resword_new addUser f_user_add +f_resword_new deleteUser f_user_delete +f_resword_new editUser f_user_edit + # variable.subr f_resword_new installVarDefaults f_variable_set_defaults f_resword_new dumpVariables f_dump_variables diff --git a/usr.sbin/bsdconfig/share/strings.subr b/usr.sbin/bsdconfig/share/strings.subr index 9b0014f..487e061 100644 --- a/usr.sbin/bsdconfig/share/strings.subr +++ b/usr.sbin/bsdconfig/share/strings.subr @@ -34,6 +34,12 @@ BSDCFG_SHARE="/usr/share/bsdconfig" ############################################################ GLOBALS # +# A Literal newline (for use with f_replace_all(), or IFS, or whatever) +# +NL=" +" # END-QUOTE + +# # Valid characters that can appear in an sh(1) variable name # # Please note that the character ranges A-Z and a-z should be avoided because @@ -189,7 +195,7 @@ f_number_of_lines() f_isinteger() { local arg="${1#-}" - [ "${arg:-x}" = "${arg#[!0-9]*}" ] + [ "${arg:-x}" = "${arg%[!0-9]*}" ] } # f_uriencode [$text] @@ -265,6 +271,9 @@ f_uridecode() # capturing in a sub-shell (which is less recommended due to performance # degradation). # +# To replace newlines or a sequence containing the newline character, use $NL +# as `\n' is not supported. +# f_replaceall() { local __left="" __right="$1" @@ -372,14 +381,13 @@ f_expand_number() local __cp __num __bshift __maxinput # Remove any leading non-digits - while :; do - __cp="$__string" - __string="${__cp#[!0-9]}" - [ "$__string" = "$__cp" ] && break - done + __string="${__string#${__string%%[0-9]*}}" + + # Store the numbers (no trailing suffix) + __num="${__string%%[!0-9]*}" # Produce `-1' if string didn't contain any digits - if [ ! "$__string" ]; then + if [ ! "$__num" ]; then if [ "$__var_to_set" ]; then setvar "$__var_to_set" -1 else @@ -388,25 +396,8 @@ f_expand_number() return 1 # 1 = "Given $string contains no digits" fi - # Store the numbers - __num="${__string%%[!0-9]*}" - - # Shortcut - if [ $__num -eq 0 ]; then - if [ "$__var_to_set" ]; then - setvar "$__var_to_set" 0 - else - echo 0 - fi - return $SUCCESS - fi - # Remove all the leading numbers from the string to get at the prefix - while :; do - __cp="$__string" - __string="${__cp#[0-9]}" - [ "$__string" = "$__cp" ] && break - done + __string="${__string#"$__num"}" # # Test for invalid prefix (and determine bitshift length) diff --git a/usr.sbin/bsdconfig/share/variable.subr b/usr.sbin/bsdconfig/share/variable.subr index 55bc850..c453f67 100644 --- a/usr.sbin/bsdconfig/share/variable.subr +++ b/usr.sbin/bsdconfig/share/variable.subr @@ -1,6 +1,6 @@ if [ ! "$_VARIABLE_SUBR" ]; then _VARIABLE_SUBR=1 # -# Copyright (c) 2012-2013 Devin Teske +# Copyright (c) 2012-2014 Devin Teske # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -241,6 +241,10 @@ f_variable_new VAR_FTP_PORT ftpPort f_variable_new VAR_FTP_STATE ftpState f_variable_new VAR_FTP_USER ftpUser f_variable_new VAR_GATEWAY defaultrouter +f_variable_new VAR_GROUP group +f_variable_new VAR_GROUP_GID groupGid +f_variable_new VAR_GROUP_MEMBERS groupMembers +f_variable_new VAR_GROUP_PASSWORD groupPassword f_variable_new VAR_HOSTNAME hostname f_variable_new VAR_HTTP_DIR httpDirectory f_variable_new VAR_HTTP_FTP_MODE httpFtpMode @@ -279,6 +283,21 @@ f_variable_new VAR_SLOW_ETHER slowEthernetCard f_variable_new VAR_TRY_DHCP tryDHCP f_variable_new VAR_TRY_RTSOL tryRTSOL f_variable_new VAR_UFS_PATH ufs +f_variable_new VAR_USER user +f_variable_new VAR_USER_ACCOUNT_EXPIRE userAccountExpire +f_variable_new VAR_USER_DOTFILES_CREATE userDotfilesCreate +f_variable_new VAR_USER_GECOS userGecos +f_variable_new VAR_USER_GID userGid +f_variable_new VAR_USER_GROUPS userGroups +f_variable_new VAR_USER_GROUP_DELETE userGroupDelete +f_variable_new VAR_USER_HOME userHome +f_variable_new VAR_USER_HOME_CREATE userHomeCreate +f_variable_new VAR_USER_HOME_DELETE userHomeDelete +f_variable_new VAR_USER_LOGIN_CLASS userLoginClass +f_variable_new VAR_USER_PASSWORD userPassword +f_variable_new VAR_USER_PASSWORD_EXPIRE userPasswordExpire +f_variable_new VAR_USER_SHELL userShell +f_variable_new VAR_USER_UID userUid f_variable_new VAR_ZFSINTERACTIVE zfsInteractive # diff --git a/usr.sbin/bsdconfig/timezone/share/menus.subr b/usr.sbin/bsdconfig/timezone/share/menus.subr index 2028bf3..ef3979b 100644 --- a/usr.sbin/bsdconfig/timezone/share/menus.subr +++ b/usr.sbin/bsdconfig/timezone/share/menus.subr @@ -104,7 +104,7 @@ f_make_menus_awk=' function add_zone_n_to_country_menu(tlc, n) { zone_title = ENVIRON["country_" tlc "_descr_" n] - gsub(/'\''/, "'\''\\'\'''\''", zone_title) + gsub(/'\''/, "'\''\\'\'\''", zone_title) country_menu_list[tlc] = country_menu_list[tlc] \ ( length(country_menu_list[tlc]) > 0 ? "\n" : "" ) \ n " '\''" zone_title "'\''" @@ -121,7 +121,7 @@ BEGIN { { tlc = countries[cp] title = ENVIRON["country_" tlc "_name"] - gsub(/'\''/, "'\''\\'\'''\''", title) + gsub(/'\''/, "'\''\\'\'\''", title) nzones = ENVIRON["country_" tlc "_nzones"] if (!nzones) { diff --git a/usr.sbin/bsdconfig/usermgmt/Makefile b/usr.sbin/bsdconfig/usermgmt/Makefile index 910d380..5211c3c 100644 --- a/usr.sbin/bsdconfig/usermgmt/Makefile +++ b/usr.sbin/bsdconfig/usermgmt/Makefile @@ -8,8 +8,7 @@ FILESDIR= ${LIBEXECDIR}/bsdconfig/070.usermgmt FILES= INDEX USAGE SCRIPTSDIR= ${FILESDIR} -SCRIPTS= groupadd groupdel groupedit groupinput \ - useradd userdel useredit userinput usermgmt +SCRIPTS= groupadd groupdel groupedit useradd userdel useredit usermgmt beforeinstall: mkdir -p ${DESTDIR}${FILESDIR} diff --git a/usr.sbin/bsdconfig/usermgmt/groupadd b/usr.sbin/bsdconfig/usermgmt/groupadd index 62f062d..cba0f2b 100755 --- a/usr.sbin/bsdconfig/usermgmt/groupadd +++ b/usr.sbin/bsdconfig/usermgmt/groupadd @@ -1,7 +1,7 @@ #!/bin/sh #- # Copyright (c) 2012 Ron McDowell -# Copyright (c) 2012-2013 Devin Teske +# Copyright (c) 2012-2014 Devin Teske # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -33,8 +33,11 @@ BSDCFG_SHARE="/usr/share/bsdconfig" . $BSDCFG_SHARE/common.subr || exit 1 f_dprintf "%s: loading includes..." "$0" f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/usermgmt/group.subr BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr f_index_menusel_keyword $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ipgm && pgm="${ipgm:-$pgm}" @@ -55,9 +58,19 @@ done shift $(( $OPTIND - 1 )) # -# Chain-load to groupinput to centralize code and minimize duplication +# Initialize # -$BSDCFG_LIBE/$APP_DIR/groupinput ${USE_XDIALOG:+-X} mode="Add" +f_dialog_title "$msg_add $msg_group" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Add a group +# +# NB: If given an argument on the command-line use it; otherwise fall-back to +# environment variable $group (handle $VAR_GROUP). +# +f_group_add ${1:+"$1"} ################################################################################ # END diff --git a/usr.sbin/bsdconfig/usermgmt/groupdel b/usr.sbin/bsdconfig/usermgmt/groupdel index aa1c305..ea55489 100755 --- a/usr.sbin/bsdconfig/usermgmt/groupdel +++ b/usr.sbin/bsdconfig/usermgmt/groupdel @@ -1,7 +1,7 @@ #!/bin/sh #- # Copyright (c) 2012 Ron McDowell -# Copyright (c) 2012-2013 Devin Teske +# Copyright (c) 2012-2014 Devin Teske # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -34,6 +34,7 @@ BSDCFG_SHARE="/usr/share/bsdconfig" f_dprintf "%s: loading includes..." "$0" f_include $BSDCFG_SHARE/dialog.subr f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/usermgmt/group.subr f_include $BSDCFG_SHARE/usermgmt/group_input.subr BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" @@ -65,9 +66,17 @@ f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" f_mustberoot_init # +# If given a group name, operate on it and exit +# +if [ "$1" ]; then + f_group_delete "$1" + exit $SUCCESS +fi + +# # Loop until the user Exits, Cancels or presses ESC # -defaultitem="" +defaultitem= while :; do f_dialog_menu_group_list "$defaultitem" retval=$? @@ -81,8 +90,7 @@ while :; do # Anything else is a group name - $BSDCFG_LIBE/$APP_DIR/groupinput \ - ${USE_XDIALOG:+-X} mode="Delete" group="$mtag" + f_group_delete "$mtag" done exit $SUCCESS diff --git a/usr.sbin/bsdconfig/usermgmt/groupedit b/usr.sbin/bsdconfig/usermgmt/groupedit index 953f4f5..2338d57 100755 --- a/usr.sbin/bsdconfig/usermgmt/groupedit +++ b/usr.sbin/bsdconfig/usermgmt/groupedit @@ -1,7 +1,7 @@ #!/bin/sh #- # Copyright (c) 2012 Ron McDowell -# Copyright (c) 2012-2013 Devin Teske +# Copyright (c) 2012-2014 Devin Teske # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -34,6 +34,7 @@ BSDCFG_SHARE="/usr/share/bsdconfig" f_dprintf "%s: loading includes..." "$0" f_include $BSDCFG_SHARE/dialog.subr f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/usermgmt/group.subr f_include $BSDCFG_SHARE/usermgmt/group_input.subr BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" @@ -65,9 +66,17 @@ f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" f_mustberoot_init # -# Loop until the user Exits, Cancels or presses ESC +# If given a group name, operate on it and exit # -defaultitem="" +if [ "$1" ]; then + f_group_edit "$1" + exit $SUCCESS +fi + +# +# Present a list of groups and loop until user Exits, Cancels or presses ESC +# +defaultitem= while :; do f_dialog_menu_group_list "$defaultitem" retval=$? @@ -81,8 +90,7 @@ while :; do # Anything else is a group name - $BSDCFG_LIBE/$APP_DIR/groupinput \ - ${USE_XDIALOG:+-X} mode="Edit/View" group="$mtag" + f_group_edit "$mtag" done exit $SUCCESS diff --git a/usr.sbin/bsdconfig/usermgmt/groupinput b/usr.sbin/bsdconfig/usermgmt/groupinput deleted file mode 100755 index f98e890..0000000 --- a/usr.sbin/bsdconfig/usermgmt/groupinput +++ /dev/null @@ -1,293 +0,0 @@ -#!/bin/sh -#- -# Copyright (c) 2012 Ron McDowell -# Copyright (c) 2012-2013 Devin Teske -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_SHARE="/usr/share/bsdconfig" -. $BSDCFG_SHARE/common.subr || exit 1 -f_dprintf "%s: loading includes..." "$0" -f_include $BSDCFG_SHARE/dialog.subr -f_include $BSDCFG_SHARE/mustberoot.subr -f_include $BSDCFG_SHARE/usermgmt/group_input.subr - -BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" -f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr - -f_index_menusel_keyword $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ipgm && - pgm="${ipgm:-$pgm}" - -############################################################ CONFIGURATION - -# set some reasonable defaults if /etc/adduser.conf does not exist. -[ -f /etc/adduser.conf ] && f_include /etc/adduser.conf -: ${passwdtype:="yes"} - -############################################################ FUNCTIONS - -# save_changes -# -# Save any/all settings (actions performed depend on $mode value). -# -save_changes() -{ - local funcname=save_changes - - case "$mode" in - Delete) - f_eval_catch $funcname pw 'pw groupdel "%s"' "$group_name" || - return $? - f_show_msg "$msg_group_deleted" - ;; - Add) - local cmd="pw groupadd -n '$group_name'" - [ "$group_gid" ] && cmd="$cmd -g '$group_gid'" - [ "$group_members" != "$cur_group_members" ] && - cmd="$cmd -M '$group_members'" - if [ "$pw_group_password_disable" ]; then - f_eval_catch $funcname pw '%s -h -' "$cmd" - elif [ "$group_password" ]; then - echo "$group_password" | - f_eval_catch $funcname pw '%s -h 0' "$cmd" - else - f_eval_catch $funcname pw '%s' "$cmd" - fi || return $? - f_show_msg "$msg_group_added" - ;; - Edit/View) - local cmd="pw groupmod -n '$group_name'" - [ "$group_gid" ] && cmd="$cmd -g '$group_gid'" - [ "$group_members" != "$cur_group_members" ] && - cmd="$cmd -M '$group_members'" - if [ "$pw_group_password_disable" ]; then - f_eval_catch $funcname pw '%s -h -' "$cmd" - elif [ "$group_password" ]; then - echo "$group_password" | - f_eval_catch $funcname pw '%s -h 0' "$cmd" - else - f_eval_catch $funcname pw '%s' "$cmd" - fi || return $? - f_show_msg "$msg_group_updated" - ;; - esac - - save_flag= - return $SUCCESS -} - -# dialog_title_update $mode -# -# Set the title based on the given $mode. -# -dialog_title_update() -{ - local mode="$1" - case "$mode" in - Add) f_dialog_title "$msg_add $msg_group" ;; - Edit/View) f_dialog_title "$msg_edit_view $msg_group: $group" ;; - Delete) f_dialog_title "$msg_delete $msg_group: $group" ;; - esac -} - -############################################################ MAIN - -# Incorporate rc-file if it exists -[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" - -# -# Process command-line arguments -# -while [ $# -gt 0 ]; do - key="${1%%=*}" - value="${1#*=}" - f_dprintf "key=[%s] value=[%s]" "$key" "$value" - case "$key" in - mode) mode="$value" ;; - group) group="$value" ;; - esac - shift -done -f_dprintf "mode=[%s] group=[%s]" "$mode" "$group" - -# -# Initialize -# -dialog_title_update "$mode" -f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" -f_mustberoot_init -menu_text= -save_flag= -hline="$hline_arrows_tab_enter" - -if [ "$mode" = "Add" ]; then - f_dialog_input_group_name || exit 0 - - f_dialog_noyes "$msg_use_default_values_for_all_account_details" - retval=$? - - if [ $retval -eq $DIALOG_ESC ]; then - exit $SUCCESS - elif [ $retval -ne $DIALOG_OK ]; then - # - # Ask a series of questions to pre-fill the editor screen. - # - # The defaults used in each dialog should allow the user to - # simply hit ENTER to proceed, because cancelling a single - # dialog will cause them to be returned to the main groupmenu. - # - - [ "$passwdtype" = "yes" ] && - { f_dialog_input_group_password || exit $SUCCESS; } - f_dialog_input_group_gid || exit $SUCCESS - f_dialog_input_group_members || exit $SUCCESS - fi -fi - -if [ "$mode" = "Edit/View" -o "$mode" = "Delete" ]; then - f_input_group "$group" || f_die 1 "$msg_group_not_found" -fi - -cur_group_name="$group_name" -cur_group_password="$group_password" -cur_group_gid="$group_gid" -cur_group_members="$group_members" - -[ "$mode" = "Delete" ] && save_flag=1 - -# -# Loop until the user decides to Exit, Cancel, or presses ESC -# -while :; do - dialog_title_update "$mode" - - menu_text= - menu_exit="$msg_exit" - if [ "$save_flag" ]; then - if [ "$mode" = "Delete" ]; then - menu_exit="$msg_delete/$msg_exit" - menu_text="$msg_delete_exit_or_cancel" - else - menu_exit="$msg_save/$msg_exit" - menu_text="$msg_save_exit_or_cancel" - fi - fi - - case "$mode" in - Delete) - menu_items=" - 'X' '$menu_exit' - '1' '$msg_group: $group_name' - '-' '$msg_password: -----' - '-' '$msg_group_id: $group_gid' - '-' '$msg_group_members: $group_members' - " # END-QUOTE - ;; - *) - menu_items=" - 'X' '$menu_exit' - '1' '$msg_group: $group_name' - '2' '$msg_password: -----' - '3' '$msg_group_id: $group_gid' - '4' '$msg_group_members: $group_members' - " # END-QUOTE - esac - - eval f_dialog_menu_size height width rows \ - \"\$DIALOG_TITLE\" \ - \"\$DIALOG_BACKTITLE\" \ - \"\$menu_text\" \ - \"\$hline\" \ - $menu_items - - f_dialog_default_fetch defaultitem - mtag=$( eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --default-item \"\$defaultitem\" \ - --menu \"\$menu_text\" \ - $height $width $rows \ - $menu_items \ - 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD - ) - retval=$? - f_dialog_data_sanitize mtag - f_dialog_default_store "$mtag" - f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" - - # Exit if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $DIALOG_OK ] || f_die - - case "$mtag" in - X) # Exit - [ "$save_flag" ] && { save_changes || continue; } - break - ;; - 1) # Group Name - case "$mode" in - Add) f_dialog_input_group_name "$group_name" ;; - Edit/View|Delete) - f_dialog_menu_group_list "$group_name" - retval=$? - f_dialog_menutag_fetch mtag - f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" - - # Loop if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $DIALOG_OK ] || continue - - [ "$mtag" = "X $msg_exit" ] && continue - - group="$mtag" - f_input_group "$group" || f_die 1 "$msg_group_not_found" - cur_group_name="$group_name" - cur_group_password="$group_password" - cur_group_gid="$group_gid" - cur_group_members="$group_members" - [ "$mode" != "Delete" ] && save_flag= - esac - ;; - 2) # Password - f_dialog_input_group_password - ;; - 3) # GID - f_dialog_input_group_gid "$group_gid" - ;; - 4) # Users in Group - f_dialog_input_group_members "$group_members" - ;; - esac - -done - -exit $SUCCESS - -################################################################################ -# END -################################################################################ diff --git a/usr.sbin/bsdconfig/usermgmt/include/messages.subr b/usr.sbin/bsdconfig/usermgmt/include/messages.subr index e139f32..f56b844 100644 --- a/usr.sbin/bsdconfig/usermgmt/include/messages.subr +++ b/usr.sbin/bsdconfig/usermgmt/include/messages.subr @@ -1,5 +1,5 @@ # Copyright (c) 2012 Ron McDowell -# Copyright (c) 2012-2013 Devin Teske +# Copyright (c) 2012-2014 Devin Teske # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -33,7 +33,7 @@ hline_arrows_tab_enter="Press arrows, TAB or ENTER" hline_num_arrows_tab_enter="Use numbers, arrows, TAB or ENTER" hline_num_tab_enter="Use numbers, TAB or ENTER" msg_account_does_not_expire="Account does not expire" -msg_account_expires_in_how_many_days="Account expires in how many days?" +msg_account_expire_manual_edit="Enter account expiration time. Format is one of:\n\n a) decimal for UNIX time since %s\n b) dd-mmm-yy[yy] for day, month, and 2- or 4-digit year\n c) +n[mhdwoy] for relative time from current date\n\nNOTE: Value of zero disables expiration." msg_account_expires_on="Account Expires on" msg_add="Add" msg_add_group="Add Group" @@ -56,6 +56,7 @@ msg_edit_group="Edit/View Group" msg_edit_login="Edit/View Login" msg_edit_view="Edit/View" msg_enter_group_members_manually="Enter Group Members manually" +msg_enter_groups_manually="Enter Groups manually" msg_enter_number_of_days_into_the_future="Enter number of days into the future" msg_enter_value_manually="Edit value manually" msg_error="ERROR!" @@ -74,9 +75,8 @@ msg_group_not_found="%s: Group not found." msg_group_password="Group Password" msg_group_passwords_do_not_match="Group Passwords do not match." msg_group_updated="Group Updated" +msg_groups="Groups" msg_home_directory="Home Directory" -msg_invalid_number_of_days="Invalid number of days." -msg_invalid_number_of_seconds="Invalid number of seconds." msg_login="Login" msg_login_added="Login Added" msg_login_already_used="%s: Login is already used." @@ -85,23 +85,28 @@ msg_login_deleted="Login Deleted" msg_login_is_empty="Login is empty." msg_login_management="Login/Group Management" msg_login_must_start_with_letter="Login must start with a letter." -msg_login_not_found="Login not found." +msg_login_not_found="%s: Login not found." msg_login_updated="Login Updated" msg_member_of_groups="Member of Groups" msg_n_a="N/A" msg_no="No" +msg_no_group_specified="No group specified!" +msg_no_user_specified="No user specified!" msg_number_of_seconds_since_epoch="Number of seconds since the Epoch\n(1 = %s)\nNULL or zero to disable:" msg_ok="OK" msg_password="Password" msg_password_does_not_expire="Password does not expire" -msg_password_expires_in_how_many_days="Password expires in how many days?" +msg_password_expire_manual_edit="Enter password expiration time. Format is one of:\n\n a) decimal for UNIX time since %s\n b) dd-mmm-yy[yy] for day, month, and 2- or 4-digit year\n c) +n[mhdwoy] for relative time from current date\n\nNOTE: Value of zero disables expiration." msg_password_expires_on="Password Expires on" msg_passwords_do_not_match="Passwords do not match." +msg_please_enter_a_group_name="Please enter a group name!" +msg_please_enter_a_user_name="Please enter a user name!" msg_reenter_group_password="Re-enter Group Password" msg_reenter_password="Re-enter Password" msg_save="Save" msg_save_exit_or_cancel="Choose Save/Exit when finished or Cancel." msg_select_group_members_from_list="Select Group Members from a list" +msg_select_groups_from_list="Select Groups from a list" msg_select_login_shell="Select Login Shell" msg_separated_by_commas="Separated by commas" msg_shell="Shell" diff --git a/usr.sbin/bsdconfig/usermgmt/share/Makefile b/usr.sbin/bsdconfig/usermgmt/share/Makefile index d6b9e3a..7f27c92 100644 --- a/usr.sbin/bsdconfig/usermgmt/share/Makefile +++ b/usr.sbin/bsdconfig/usermgmt/share/Makefile @@ -3,7 +3,7 @@ NO_OBJ= FILESDIR= ${SHAREDIR}/bsdconfig/usermgmt -FILES= group_input.subr user_input.subr +FILES= group.subr group_input.subr user.subr user_input.subr beforeinstall: mkdir -p ${DESTDIR}${FILESDIR} diff --git a/usr.sbin/bsdconfig/usermgmt/share/group.subr b/usr.sbin/bsdconfig/usermgmt/share/group.subr new file mode 100644 index 0000000..fb320d1 --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/share/group.subr @@ -0,0 +1,518 @@ +if [ ! "$_USERMGMT_GROUP_SUBR" ]; then _USERMGMT_GROUP_SUBR=1 +# +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012-2014 Devin Teske +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_dprintf "%s: loading includes..." usermgmt/group.subr +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/usermgmt/group_input.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ CONFIGURATION + +# set some reasonable defaults if /etc/adduser.conf does not exist. +[ -f /etc/adduser.conf ] && f_include /etc/adduser.conf +: ${passwdtype:="yes"} + +############################################################ FUNCTIONS + +# f_group_add [$group] +# +# Add a group. If both $group (as a first argument) and $VAR_GROUP are unset +# or NULL and we are running interactively, prompt the user to enter the name +# of a new group and (if $VAR_NO_CONFIRM is unset or NULL) prompt the user to +# answer some questions about the new group. Variables that can be used to +# script user input: +# +# VAR_GROUP [Optional if running interactively] +# The group to add. Ignored if given non-NULL first-argument. +# VAR_GROUP_GID [Optional] +# Numerical group ID to use. If NULL or unset, the group ID is +# automatically chosen. +# VAR_GROUP_MEMBERS [Optional] +# Comma separated list of users that are a member of this group. +# VAR_GROUP_PASSWORD [Optional] +# newgrp(1) password to set for the group. Default if NULL or +# unset is to disable newgrp(1) password authentication. +# +# Returns success if the group was successfully added. +# +f_group_add() +{ + local funcname=f_group_add + local title # Calculated below + local alert=f_show_msg no_confirm= + + f_getvar $VAR_NO_CONFIRM no_confirm + [ "$no_confirm" ] && alert=f_show_info + + local input + f_getvar 3:-\$$VAR_GROUP input "$1" + + # + # NB: pw(8) has a ``feature'' wherein `-n name' can be taken as GID + # instead of name. Work-around is to also pass `-g GID' at the same + # time (any GID will do; but `-1' is appropriate for this context). + # + if [ "$input" ] && f_quietly pw groupshow -n "$input" -g -1; then + f_show_err "$msg_group_already_used" "$input" + return $FAILURE + fi + + local group_name="$input" + while f_interactive && [ ! "$group_name" ]; do + f_dialog_input_group_name group_name "$group_name" || + return $SUCCESS + [ "$group_name" ] || + f_show_err "$msg_please_enter_a_group_name" + done + if [ ! "$group_name" ]; then + f_show_err "$msg_no_group_specified" + return $FAILURE + fi + + local group_password group_gid group_members + f_getvar $VAR_GROUP_PASSWORD group_password + f_getvar $VAR_GROUP_GID group_gid + f_getvar $VAR_GROUP_MEMBERS group_members + + local group_password_disable= + f_interactive || [ "$group_password" ] || group_password_disable=1 + + if f_interactive && [ ! "$no_confirm" ]; then + f_dialog_noyes \ + "$msg_use_default_values_for_all_account_details" + retval=$? + if [ $retval -eq $DIALOG_ESC ]; then + return $SUCCESS + elif [ $retval -ne $DIALOG_OK ]; then + # + # Ask series of questions to pre-fill the editor screen + # + # Defaults used in each dialog should allow the user to + # simply hit ENTER to proceed and cancelling a single + # dialog cause them to return to the previous menu. + # + + if [ "$passwdtype" = "yes" ]; then + f_dialog_input_group_password group_password \ + group_password_disable || + return $FAILURE + fi + f_dialog_input_group_gid group_gid "$group_gid" || + return $FAILURE + f_dialog_input_group_members group_members \ + "$group_members" || return $FAILURE + fi + fi + + # + # Loop until the user decides to Exit, Cancel, or presses ESC + # + title="$msg_add $msg_group: $group_name" + if f_interactive; then + local mtag retval defaultitem= + while :; do + f_dialog_title "$title" + f_dialog_menu_group_add "$defaultitem" + retval=$? + f_dialog_title_restore + f_dialog_menutag_fetch mtag + f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" + defaultitem="$mtag" + + # Return if user either pressed ESC or chose Cancel/No + [ $retval -eq $DIALOG_OK ] || return $FAILURE + + case "$mtag" in + X) # Add/Exit + local var + for var in gid members name; do + local _group_$var + eval f_shell_escape \ + \"\$group_$var\" _group_$var + done + + local cmd="pw groupadd -n '$_group_name'" + [ "$group_gid" ] && cmd="$cmd -g '$_group_gid'" + [ "$group_members" ] && + cmd="$cmd -M '$_group_members'" + + # Execute the command (break on success) + if [ "$group_password_disable" ]; then + f_eval_catch $funcname pw '%s -h -' "$cmd" + elif [ "$group_password" ]; then + echo "$group_password" | + f_eval_catch $funcname \ + pw '%s -h 0' "$cmd" + else + f_eval_catch $funcname pw '%s' "$cmd" + fi && break + ;; + 1) # Group Name (prompt for new group name) + f_dialog_input_group_name input "$group_name" || + continue + if f_quietly pw groupshow -n "$input" -g -1; then + f_show_err "$msg_group_already_used" "$input" + continue + fi + group_name="$input" + title="$msg_add $msg_group: $group_name" + ;; + 2) # Password + f_dialog_input_group_password group_password \ + group_password_disable + ;; + 3) # Group ID + f_dialog_input_group_gid group_gid "$group_gid" + ;; + 4) # Group Members + f_dialog_input_group_members group_members \ + "$group_members" + ;; + esac + done + else + local var + for var in gid members name; do + local _group_$var + eval f_shell_escape \"\$group_$var\" _group_$var + done + + # Form the command + local cmd="pw groupadd -n '$_group_name'" + [ "$group_gid" ] && cmd="$cmd -g '$_group_gid'" + [ "$group_members" ] && cmd="$cmd -M '$_group_members'" + + # Execute the command + local retval err + if [ "$group_password_disable" ]; then + f_eval_catch -k err $funcname pw '%s -h -' "$cmd" + elif [ "$group_password" ]; then + err=$( echo "$group_password" | f_eval_catch -de \ + $funcname pw '%s -h 0' "$cmd" 2>&1 ) + else + f_eval_catch -k err $funcname pw '%s' "$cmd" + fi + retval=$? + if [ $retval -ne $SUCCESS ]; then + f_show_err "%s" "$err" + return $retval + fi + fi + + f_dialog_title "$title" + $alert "$msg_group_added" + f_dialog_title_restore + [ "$no_confirm" -a "$USE_DIALOG" ] && sleep 1 + + return $SUCCESS +} + +# f_group_delete [$group] +# +# Delete a group. If both $group (as a first argument) and $VAR_GROUP are unset +# or NULL and we are running interactively, prompt the user to select a group +# from a list of available groups. Variables that can be used to script user +# input: +# +# VAR_GROUP [Optional if running interactively] +# The group to delete. Ignored if given non-NULL first-argument. +# +# Returns success if the group was successfully deleted. +# +f_group_delete() +{ + local funcname=f_group_delete + local title # Calculated below + local alert=f_show_msg no_confirm= + + f_getvar $VAR_NO_CONFIRM no_confirm + [ "$no_confirm" ] && alert=f_show_info + + local input + f_getvar 3:-\$$VAR_GROUP input "$1" + + local group_name group_password group_gid group_members + if [ "$input" ] && ! f_input_group "$input"; then + f_show_err "$msg_group_not_found" "$input" + return $FAILURE + fi + + # + # Loop until the user decides to Exit, Cancel, or presses ESC + # + title="$msg_delete $msg_group: $group_name" + if f_interactive; then + local mtag retval defaultitem= + while :; do + f_dialog_title "$title" + f_dialog_menu_group_delete "$group_name" "$defaultitem" + retval=$? + f_dialog_title_restore + f_dialog_menutag_fetch mtag + f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" + defaultitem="$mtag" + + # Return if user either pressed ESC or chose Cancel/No + [ $retval -eq $DIALOG_OK ] || return $FAILURE + + case "$mtag" in + X) # Delete/Exit + local _group_name + f_shell_escape "$group_name" _group_name + f_eval_catch $funcname pw 'pw groupdel "%s"' \ + "$_group_name" && break + ;; + 1) # Group Name (select different group from list) + f_dialog_menu_group_list "$group_name" || continue + f_dialog_menutag_fetch mtag + + [ "$mtag" = "X $msg_exit" ] && continue + + if ! f_input_group "$mtag"; then + f_show_err "$msg_group_not_found" "$mtag" + # Attempt to fall back to previous selection + f_input_group "$input" || return $FAILURE + else + input="$mtag" + fi + ;; + esac + done + else + local retval err _group_name + f_shell_escape "$group_name" _group_name + f_eval_catch -k err $funcname pw \ + "pw groupdel '%s'" "$_group_name" + retval=$? + if [ $retval -ne $SUCCESS ]; then + f_show_err "%s" "$err" + return $retval + fi + fi + + f_dialog_title "$title" + $alert "$msg_group_deleted" + f_dialog_title_restore + [ "$no_confirm" -a "$USE_DIALOG" ] && sleep 1 + + return $SUCCESS +} + +# f_group_edit [$group] +# +# Modify a group. If both $group (as a first argument) and $VAR_GROUP are unset +# or NULL and we are running interactively, prompt the user to select a group +# from a list of available groups. Variables that can be used to script user +# input: +# +# VAR_GROUP [Optional if running interactively] +# The group to modify. Ignored if given non-NULL first-argument. +# VAR_GROUP_GID [Optional] +# Numerical group ID to set. If NULL or unset, the group ID is +# unchanged. +# VAR_GROUP_MEMBERS [Optional] +# Comma separated list of users that are a member of this group. +# If set but NULL, group memberships are reset (no users will be +# a member of this group). If unset, group membership is +# unmodified. +# VAR_GROUP_PASSWORD [Optional] +# newgrp(1) password to set for the group. If unset, the password +# is unmodified. If NULL, the newgrp(1) password is disabled. +# +# Returns success if the group was successfully modified. +# +f_group_edit() +{ + local funcname=f_group_edit + local title # Calculated below + local alert=f_show_msg no_confirm= + + f_getvar $VAR_NO_CONFIRM no_confirm + [ "$no_confirm" ] && alert=f_show_info + + local input + f_getvar 3:-\$$VAR_GROUP input "$1" + + # + # NB: pw(8) has a ``feature'' wherein `-n name' can be taken as GID + # instead of name. Work-around is to also pass `-g GID' at the same + # time (any GID will do; but `-1' is appropriate for this context). + # + if [ "$input" ] && ! f_quietly pw groupshow -n "$input" -g -1; then + f_show_err "$msg_group_not_found" "$input" + return $FAILURE + fi + + if f_interactive && [ ! "$input" ]; then + f_dialog_menu_group_list || return $SUCCESS + f_dialog_menutag_fetch input + [ "$input" = "X $msg_exit" ] && return $SUCCESS + elif [ ! "$input" ]; then + f_show_err "$msg_no_group_specified" + return $FAILURE + fi + + local group_name group_password group_gid group_members + if ! f_input_group "$input"; then + f_show_err "$msg_group_not_found" "$input" + return $FAILURE + fi + + f_isset $VAR_GROUP_GID && f_getvar $VAR_GROUP_GID group_gid + local null_members= + if f_isset $VAR_GROUP_MEMBERS; then + f_getvar $VAR_GROUP_MEMBERS group_members + [ "$group_members" ] || null_members=1 + fi + local group_password_disable= + if f_isset $VAR_GROUP_PASSWORD; then + f_getvar $VAR_GROUP_PASSWORD group_password + [ "$group_password" ] || group_password_disable=1 + fi + + # + # Loop until the user decides to Exit, Cancel, or presses ESC + # + title="$msg_edit_view $msg_group: $group_name" + if f_interactive; then + local mtag retval defaultitem= + while :; do + f_dialog_title "$title" + f_dialog_menu_group_edit "$defaultitem" + retval=$? + f_dialog_title_restore + f_dialog_menutag_fetch mtag + f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" + defaultitem="$mtag" + + # Return if user either pressed ESC or chose Cancel/No + [ $retval -eq $DIALOG_OK ] || return $FAILURE + + case "$mtag" in + X) # Save/Exit + local var + for var in gid members name; do + local _group_$var + eval f_shell_escape \ + \"\$group_$var\" _group_$var + done + + local cmd="pw groupmod -n '$_group_name'" + [ "$group_gid" ] && cmd="$cmd -g '$_group_gid'" + [ "$group_members" -o "$null_members" ] && + cmd="$cmd -M '$_group_members'" + + # Execute the command (break on success) + if [ "$group_password_disable" ]; then + f_eval_catch $funcname pw '%s -h -' "$cmd" + elif [ "$group_password" ]; then + echo "$group_password" | f_eval_catch \ + $funcname pw '%s -h 0' "$cmd" + else + f_eval_catch $funcname pw '%s' "$cmd" + fi && break + ;; + 1) # Group Name (select different group from list) + f_dialog_menu_group_list "$group_name" || continue + f_dialog_menutag_fetch mtag + + [ "$mtag" = "X $msg_exit" ] && continue + + if ! f_input_group "$mtag"; then + f_show_err "$msg_group_not_found" "$mtag" + # Attempt to fall back to previous selection + f_input_group "$input" || return $FAILURE + else + input="$mtag" + fi + title="$msg_edit_view $msg_group: $group_name" + ;; + 2) # Password + f_dialog_input_group_password group_password \ + group_password_disable + ;; + 3) # Group ID + f_dialog_input_group_gid group_gid "$group_gid" + ;; + 4) # Group Members + f_dialog_input_group_members group_members \ + "$group_members" && [ ! "$group_members" ] && + null_members=1 + ;; + esac + done + else + local var + for var in gid members name; do + local _group_$var + eval f_shell_escape \"\$group_$var\" _group_$var + done + + # Form the command + local cmd="pw groupmod -n '$_group_name'" + [ "$group_gid" ] && cmd="$cmd -g '$_group_gid'" + [ "$group_members" -o "$null_members" ] && + cmd="$cmd -M '$_group_members'" + + # Execute the command + local retval err + if [ "$group_password_disable" ]; then + f_eval_catch -k err $funcname pw '%s -h -' "$cmd" + elif [ "$group_password" -o "$null_password" ]; then + err=$( echo "$group_password" | f_eval_catch -de \ + $funcname pw '%s -h 0' "$cmd" 2>&1 ) + else + f_eval_catch -k err $funcname pw '%s' "$cmd" + fi + retval=$? + if [ $retval -ne $SUCCESS ]; then + f_show_err "%s" "$err" + return $retval + fi + fi + + f_dialog_title "$title" + $alert "$msg_group_updated" + f_dialog_title_restore + [ "$no_confirm" -a "$USE_DIALOG" ] && sleep 1 + + return $SUCCESS +} + +############################################################ MAIN + +f_dprintf "%s: Successfully loaded." usermgmt/group.subr + +fi # ! $_USERMGMT_GROUP_SUBR diff --git a/usr.sbin/bsdconfig/usermgmt/share/group_input.subr b/usr.sbin/bsdconfig/usermgmt/share/group_input.subr index e0f47f7..2e8c086 100644 --- a/usr.sbin/bsdconfig/usermgmt/share/group_input.subr +++ b/usr.sbin/bsdconfig/usermgmt/share/group_input.subr @@ -1,7 +1,7 @@ if [ ! "$_USERMGMT_GROUP_INPUT_SUBR" ]; then _USERMGMT_GROUP_INPUT_SUBR=1 # # Copyright (c) 2012 Ron McDowell -# Copyright (c) 2012-2013 Devin Teske +# Copyright (c) 2012-2014 Devin Teske # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -48,14 +48,24 @@ f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr # f_input_group() { - eval $( pw groupshow "$1" | awk -F: ' + local funcname=f_input_group + local group="$1" + + f_dprintf "$funcname: Getting info for group \`%s'" "$group" + eval "$( pw groupshow "$group" 2> /dev/null | awk -F: ' + function set_value(var, value) { + gsub(/'\''/, "'\''\\'\'\''", value) + printf "group_%s='\'%s\''\n", var, value + } { - printf "group_name='\'%s\''\n", $1 - printf "group_password=\n" - printf "group_gid='\'%s\''\n", $3 - printf "group_members='\'%s\''\n", $4 + found = $1 != "" + set_value("name", $1) + set_value("password", "") + set_value("gid", $3) + set_value("members", $4) exit - }' ) + } + END { if (!found) print "false" }' )" } # f_dialog_menu_group_list [$default] @@ -74,10 +84,13 @@ f_dialog_menu_group_list() # Add groups from group(5) menu_list="$menu_list $( pw groupshow -a | awk -F: ' - !/^[[:space:]]*(#|$)/ { - printf "'\'%s\'\ \'%s\''\n", $1, $1 - }' - )" + function mprint(tag, item) { + gsub(/'\''/, "'\''\\'\'\''", tag) + gsub(/'\''/, "'\''\\'\'\''", item) + printf "'\'%s\'\ \'%s\''\n", tag, item + } + !/^[[:space:]]*(#|$)/ { mprint($1, $1) } + ' )" local height width rows eval f_dialog_menu_size height width rows \ @@ -105,300 +118,477 @@ f_dialog_menu_group_list() return $retval } -# f_dialog_input_group_name [$group_name] +# f_dialog_input_group_name $var_to_set [$group_name] # -# Allows the user to enter a new groupname for a given group. If the user does -# not cancel or press ESC, the $group_name variable will hold the -# newly-configured value upon return. -# -# If $cur_group_name is defined, the user can enter that and by-pass error- -# checking (allowing the user to "revert" to an old value without, for example, -# being told that the groupname already exists). +# Allows the user to enter a name for a new group. If the user does not cancel +# or press ESC, the $var_to_set variable will hold the newly-configured value +# upon return. # f_dialog_input_group_name() { + local __var_to_set="$1" __name="$2" + # # Loop until the user provides taint-free/valid input # - local _name="$1" _input="$1" + local __input="$__name" while :; do - # Return if user has either pressed ESC or chosen Cancel/No - f_dialog_input _input "$msg_group" "$_input" \ + f_dialog_input __input "$msg_group" "$__input" \ "$hline_alnum_tab_enter" || return $? # Check for no-change - [ "$_input" = "$_name" ] && return $DIALOG_OK - - # Check for reversion - if [ "$_input" = "$cur_group_name" ]; then - group_name="$cur_group_name" + if [ "$__input" = "$__name" ]; then + setvar "$__var_to_set" "$__input" return $DIALOG_OK fi # Check for NULL entry - if [ ! "$_input" ]; then + if [ ! "$__input" ]; then f_show_msg "$msg_group_is_empty" continue fi # Check for invalid entry - if ! echo "$_input" | grep -q "^[[:alpha:]]"; then + case "$__input" in [!a-zA-Z]*) f_show_msg "$msg_group_must_start_with_letter" continue - fi + esac # Check for duplicate entry - if f_quietly pw groupshow -n "$_input"; then - f_show_msg "$msg_group_already_used" "$_input" + if f_quietly pw groupshow -n "$__input"; then + f_show_msg "$msg_group_already_used" "$__input" continue fi - group_name="$_input" + setvar "$__var_to_set" "$__input" break done - save_flag=1 - - f_dprintf "group_name: [%s]->[%s]" "$cur_group_name" "$group_name" return $DIALOG_OK } -# f_dialog_input_group_password +# f_dialog_input_group_password $var_to_set $dvar_to_set # -# Prompt the user to enter a password (twice). +# Prompt the user to enter a password (twice). If the user does not cancel or +# press ESC, $var_to_set will hold the confirmed user entry. Otherwise, if the +# user cancels or enters a NULL password (twice), they are given the choice to +# disable password authentication for the given group, wherein $dvar_to_set has +# a value of 1 to indicate password authentication should be disabled. # f_dialog_input_group_password() { - local prompt1="$msg_group_password" - local prompt2="$msg_reenter_group_password" - local hline="$hline_alnum_punc_tab_enter" + local __var_to_set="$1" __dvar_to_set="$2" + local __prompt1="$msg_group_password" + local __prompt2="$msg_reenter_group_password" + local __hline="$hline_alnum_punc_tab_enter" - local height1 width1 - f_dialog_inputbox_size height1 width1 \ + local __height1 __width1 + f_dialog_inputbox_size __height1 __width1 \ "$DIALOG_TITLE" \ "$DIALOG_BACKTITLE" \ - "$prompt1" \ + "$__prompt1" \ "" \ - "$hline" + "$__hline" - local height2 width2 - f_dialog_inputbox_size height2 width2 \ + local __height2 __width2 + f_dialog_inputbox_size __height2 __width2 \ "$DIALOG_TITLE" \ "$DIALOG_BACKTITLE" \ - "$prompt2" \ + "$__prompt2" \ "" \ - "$hline" + "$__hline" # # Loop until the user provides taint-free/valid input # - local retval _password1 _password2 + local __retval __password1 __password2 while :; do - _password1=$( $DIALOG \ + __password1=$( $DIALOG \ --title "$DIALOG_TITLE" \ --backtitle "$DIALOG_BACKTITLE" \ - --hline "$hline" \ + --hline "$__hline" \ --ok-label "$msg_ok" \ --cancel-label "$msg_cancel" \ --insecure \ - --passwordbox "$prompt1" \ - $height1 $width1 \ + --passwordbox "$__prompt1" \ + $__height1 $__width1 \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) - retval=$? - debug= f_dialog_line_sanitize _password1 + __retval=$? + debug= f_dialog_line_sanitize __password1 # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $DIALOG_OK ] || return $retval + [ $__retval -eq $DIALOG_OK ] || return $__retval - _password2=$( $DIALOG \ + __password2=$( $DIALOG \ --title "$DIALOG_TITLE" \ --backtitle "$DIALOG_BACKTITLE" \ - --hline "$hline" \ + --hline "$__hline" \ --ok-label "$msg_ok" \ --cancel-label "$msg_cancel" \ --insecure \ - --passwordbox "$prompt2" \ - $height2 $width2 \ + --passwordbox "$__prompt2" \ + $__height2 $__width2 \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) - retval=$? - debug= f_dialog_line_sanitize _password2 + __retval=$? + debug= f_dialog_line_sanitize __password2 # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $DIALOG_OK ] || return $retval + [ $__retval -eq $DIALOG_OK ] || return $__retval # Check for password mismatch - if [ "$_password1" != "$_password2" ]; then + if [ "$__password1" != "$__password2" ]; then f_show_msg "$msg_group_passwords_do_not_match" continue fi # Check for NULL entry - if [ ! "$_password1" ]; then + if [ ! "$__password1" ]; then f_dialog_yesno "$msg_disable_password_auth_for_group" - local retval=$? - if [ $retval -eq $DIALOG_ESC ]; then - return $retval - elif [ $retval -eq $DIALOG_OK ]; then - pw_group_password_disable=1 + __retval=$? + if [ $__retval -eq $DIALOG_ESC ]; then + return $__retval + elif [ $__retval -eq $DIALOG_OK ]; then + setvar "$__dvar_to_set" 1 else continue # back to password prompt fi else - pw_group_password_disable= + setvar "$__dvar_to_set" "" fi - group_password="$_password1" + setvar "$__var_to_set" "$__password1" break done - save_flag=1 - - f_dprintf "group_password: [%s]->[%s]" \ - "$cur_group_password" "$group_password" return $DIALOG_OK } -# f_dialog_input_group_gid [$group_gid] +# f_dialog_input_group_gid $var_to_set [$group_gid] # # Allow the user to enter a new GID for a given group. If the user does not -# cancel or press ESC, the $group_gid variable will hold the newly-configured +# cancel or press ESC, the $var_to_set variable will hold the newly-configured # value upon return. # f_dialog_input_group_gid() { - local _input="$1" + local __var_to_set="$1" __input="$2" # Return if user has either pressed ESC or chosen Cancel/No - f_dialog_input _input "$msg_group_id_leave_empty_for_default" \ - "$_input" "$hline_num_tab_enter" || return $? - - group_gid="$_input" - save_flag=1 - - f_dprintf "group_gid: [%s]->[%s]" "$cur_group_gid" "$group_gid" + f_dialog_input __input "$msg_group_id_leave_empty_for_default" \ + "$__input" "$hline_num_tab_enter" || return $? + setvar "$__var_to_set" "$__input" return $DIALOG_OK } -# f_dialog_input_group_members [$group_members] +# f_dialog_input_group_members $var_to_set [$group_members] # # Allow the user to modify a list of members for a given group. If the user -# does not cancel or press ESC, the $group_members variable will hold the -# newly-configured value upon return. +# does not cancel or press ESC, the $var_to_set variable will hold the newly- +# configured value upon return. # f_dialog_input_group_members() { - local _input="$1" - local prompt="$msg_group_members:" - local menu_list=" + local __var_to_set="$1" __input="$2" + local __prompt="$msg_group_members:" + local __menu_list=" 'X' '$msg_continue' '1' '$msg_select_group_members_from_list' '2' '$msg_enter_group_members_manually' " # END-QUOTE - local defaultitem= - local hline="$hline_num_arrows_tab_enter" + local __defaultitem= + local __hline="$hline_num_arrows_tab_enter" - local mheight mwidth mrows - eval f_dialog_menu_size mheight mwidth mrows \ + local __mheight __mwidth __mrows + eval f_dialog_menu_size __mheight __mwidth __mrows \ \"\$DIALOG_TITLE\" \ \"\$DIALOG_BACKTITLE\" \ - \"\$prompt\" \ - \"\$hline\" \ - $menu_list + \"\$__prompt\" \ + \"\$__hline\" \ + $__menu_list - local menu_choice retval + local __menu_choice __retval while :; do - menu_choice=$( eval $DIALOG \ + __menu_choice=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ + --hline \"\$__hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ - --default-item \"\$defaultitem\" \ - --menu \"\$prompt\" \ - $mheight $mwidth $mrows \ - $menu_list \ + --default-item \"\$__defaultitem\" \ + --menu \"\$__prompt\" \ + $__mheight $__mwidth $__mrows \ + $__menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) - retval=$? - f_dialog_data_sanitize menu_choice - defaultitem="$menu_choice" - f_dprintf "retval=%u menu_choice=[%s]" $retval "$menu_choice" + __retval=$? + f_dialog_data_sanitize __menu_choice + __defaultitem="$__menu_choice" + f_dprintf "retval=%u menu_choice=[%s]" \ + $__retval "$__menu_choice" # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $DIALOG_OK ] || return $retval + [ $__retval -eq $DIALOG_OK ] || return $__retval - local _group_members - case "$menu_choice" in + local __group_members + case "$__menu_choice" in X) # Exit break ;; 1) # Select Group Members from a list - local user check_list= - for user in $( pw usershow -a | - awk -F: '!/^[[:space:]]*(#|$)/{print $1}' - ); do + local __check_list= # Calculated below + local __user_list __u __user __length=0 + __user_list=$( pw usershow -a | + awk -F: '!/^[[:space:]]*(#|$)/{print $1}' ) + while [ $__length -ne ${#__user_list} ]; do + __u="${__user_list%%$NL*}" # First line + f_shell_escape "$__u" __user + # Format of a checklist entry: tag item status - if echo "$_input" | grep -q "\<$user\>"; then - check_list="$check_list $user '' on" - else - check_list="$check_list $user '' off" - fi + __check_list="$__check_list '$__user' ''" + case "$__input" in + "$__u"|"$__u",*|*,"$__u",*|*,"$__u") + __check_list="$__check_list on" ;; + *) + __check_list="$__check_list off" + esac + + __length=${#__user_list} + __user_list="${__user_list#*$NL}" # Kill line done - local cheight cwidth crows - eval f_dialog_checklist_size cheight cwidth crows \ - \"\$DIALOG_TITLE\" \ - \"\$DIALOG_BACKTITLE\" \ - \"\$prompt\" \ - \"\$hline\" \ - $check_list - _group_members=$( eval $DIALOG \ + local __cheight __cwidth __crows + eval f_dialog_checklist_size \ + __cheight __cwidth __crows \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$__prompt\" \ + \"\$__hline\" \ + $__check_list + __group_members=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --separate-output \ - --hline \"\$hline\" \ + --hline \"\$__hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ - --checklist \"\$prompt\" \ - $cheight $cwidth $crows \ - $check_list \ + --checklist \"\$__prompt\" \ + $__cheight $__cwidth $__crows \ + $__check_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) || continue # Return to previous menu if user either # pressed ESC or chose Cancel/No - f_dialog_data_sanitize _group_members + f_dialog_data_sanitize __group_members # # Convert the newline separated list into a comma- # separated one so that if the user switches over to # manual editing, list reflects checklist selections # - f_replaceall "$_group_members" "[$IFS]" "," _input + f_replaceall "$__group_members" "[$NL]" "," __input ;; 2) # Enter Group Members manually - local p="$msg_group_members ($msg_separated_by_commas)" + local __prompt2="$msg_group_members" + __prompt2="$__prompt2 ($msg_separated_by_commas)" - f_dialog_input _group_members "$p" "$_input" \ + f_dialog_input __group_members \ + "$__prompt2" "$__input" \ "$hline_num_tab_enter" || continue # Return to previous menu if user either # pressed ESC or chose Cancel/No - _input="$_group_members" + __input="$__group_members" ;; esac done - group_members="$_input" - save_flag=1 - f_dprintf "group_members: [%s]->[%s]" \ - "$cur_group_members" "$group_members" - + setvar "$__var_to_set" "$__input" return $DIALOG_OK } +# f_dialog_menu_group_add [$defaultitem] +# +# Present a menu detailing the properties of a group that is about to be added. +# The user's menu choice is available using f_dialog_menutag_fetch(). Returns +# success unless the user chose Cancel or pressed ESC. Data to display is taken +# from environment variables group_name, group_gid, and group_members. If +# $defaultitem is present and non-NULL, initially highlight the item in the +# menu. +# +f_dialog_menu_group_add() +{ + local prompt="$msg_save_exit_or_cancel" + local menu_list # Calculated below + local defaultitem="$1" + local hline="$hline_arrows_tab_enter" + + # Localize potentially hostile variables and escape their values + # to the local variable (see f_shell_escape() of `strings.subr') + local var + for var in gid members name; do + local _group_$var + eval f_shell_escape \"\$group_$var\" _group_$var + done + + menu_list=" + 'X' '$msg_add/$msg_exit' + '1' '$msg_group: $_group_name' + '2' '$msg_password: -----' + '3' '$msg_group_id: $_group_gid' + '4' '$msg_group_members: $_group_members' + " # END-QUOTE + + local height width rows + eval f_dialog_menu_size height width rows \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list + + local menu_choice + menu_choice=$( eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --default-item \"\$defaultitem\" \ + --menu \"\$prompt\" \ + $height $width $rows \ + $menu_list \ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + f_dialog_data_sanitize menu_choice + f_dialog_menutag_store "$menu_choice" + return $retval +} + +# f_dialog_menu_group_delete $group [$defaultitem] +# +# Present a menu detailing the properties of a group that is about to be +# deleted. The user's menu choice is available using f_dialog_menutag_fetch(). +# Returns success unless the user chose Cancel or pressed ESC. Data to display +# is populated automatically from the system accounting database for the given +# $group argument. If $defaultitem is present and non-NULL, initially highlight +# the item in the menu. +# +f_dialog_menu_group_delete() +{ + local prompt="$msg_delete_exit_or_cancel" + local menu_list # Calculated below + local defaultitem="$2" + local hline="$hline_arrows_tab_enter" + + local group_name group_password group_gid group_members + f_input_group "$1" + + # Localize potentially hostile variables and escape their values + # to the local variable (see f_shell_escape() of `strings.subr') + local var + for var in gid members name; do + local _group_$var + eval f_shell_escape \"\$group_$var\" _group_$var + done + + menu_list=" + 'X' '$msg_delete/$msg_exit' + '1' '$msg_group: $_group_name' + '-' '$msg_password: -----' + '-' '$msg_group_id: $_group_gid' + '-' '$msg_group_members: $_group_members' + " # END-QUOTE + + local height width rows + eval f_dialog_menu_size height width rows \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list + + local menu_choice + menu_choice=$( eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --default-item \"\$defaultitem\" \ + --menu \"\$prompt\" \ + $height $width $rows \ + $menu_list \ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + f_dialog_data_sanitize menu_choice + f_dialog_menutag_store "$menu_choice" + return $retval +} + +# f_dialog_menu_group_edit [$defaultitem] +# +# Present a menu detailing the properties of a group that is about to be +# modified. The user's menu choice is available using f_dialog_menutag_fetch(). +# Returns success unless the user chose Cancel or pressed ESC. Data to display +# is taken from environment variables group_name, group_gid, and group_members. +# If $defaultitem is present and non-NULL, initially highlight the item in the +# menu. +# +f_dialog_menu_group_edit() +{ + local prompt="$msg_save_exit_or_cancel" + local menu_list # Calculated below + local defaultitem="$1" + local hline="$hline_arrows_tab_enter" + + # Localize potentially hostile variables and escape their values + # to the local variable (see f_shell_escape() of `strings.subr') + local var + for var in gid members name; do + local _group_$var + eval f_shell_escape \"\$group_$var\" _group_$var + done + + menu_list=" + 'X' '$msg_save/$msg_exit' + '1' '$msg_group: $_group_name' + '2' '$msg_password: -----' + '3' '$msg_group_id: $_group_gid' + '4' '$msg_group_members: $_group_members' + " # END-QUOTE + + local height width rows + eval f_dialog_menu_size height width rows \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list + + local menu_choice + menu_choice=$( eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --default-item \"\$defaultitem\" \ + --menu \"\$prompt\" \ + $height $width $rows \ + $menu_list \ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + f_dialog_data_sanitize menu_choice + f_dialog_menutag_store "$menu_choice" + return $retval +} + ############################################################ MAIN f_dprintf "%s: Successfully loaded." usermgmt/group_input.subr diff --git a/usr.sbin/bsdconfig/usermgmt/share/user.subr b/usr.sbin/bsdconfig/usermgmt/share/user.subr new file mode 100644 index 0000000..5fd65fb --- /dev/null +++ b/usr.sbin/bsdconfig/usermgmt/share/user.subr @@ -0,0 +1,1184 @@ +if [ ! "$_USERMGMT_USER_SUBR" ]; then _USERMGMT_USER_SUBR=1 +# +# Copyright (c) 2012 Ron McDowell +# Copyright (c) 2012-2014 Devin Teske +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_dprintf "%s: loading includes..." usermgmt/user.subr +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/strings.subr +f_include $BSDCFG_SHARE/usermgmt/group_input.subr +f_include $BSDCFG_SHARE/usermgmt/user_input.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ CONFIGURATION + +# set some reasonable defaults if /etc/adduser.conf does not exist. +[ -f /etc/adduser.conf ] && f_include /etc/adduser.conf +: ${defaultclass:=""} +: ${defaultshell:="/bin/sh"} +: ${homeprefix:="/home"} +: ${passwdtype:="yes"} +: ${udotdir:="/usr/share/skel"} +: ${uexpire:=""} + # Default account expire time. Format is similar to upwexpire variable. +: ${ugecos:="User &"} +: ${upwexpire:=""} + # The default password expiration time. Format of the date is either a + # UNIX time in decimal, or a date in dd-mmm-yy[yy] format, where dd is + # the day, mmm is the month in either numeric or alphabetic format, and + # yy[yy] is either a two or four digit year. This variable also accepts + # a relative date in the form of n[mhdwoy] where n is a decimal, octal + # (leading 0) or hexadecimal (leading 0x) digit followed by the number + # of Minutes, Hours, Days, Weeks, Months or Years from the current date + # at which the expiration time is to be set. + +# +# uexpire and upwexpire from adduser.conf(5) differ only slightly from what +# pw(8) accepts as `date' argument(s); pw(8) requires a leading `+' for the +# relative date syntax (n[mhdwoy]). +# +case "$uexpire" in *[mhdwoy]) + f_isinteger "${uexpire%[mhdwoy]}" && uexpire="+$uexpire" +esac +case "$upwexpire" in *[mhdwoy]) + f_isinteger "${upwexpire%[mhdwoy]}" && upwexpire="+$upwexpire" +esac + +############################################################ FUNCTIONS + +# f_user_create_homedir $user +# +# Create home directory for $user. +# +f_user_create_homedir() +{ + local funcname=f_user_create_homedir + local user="$1" + + [ "$user" ] || return $FAILURE + + local user_account_expire user_class user_gecos user_gid user_home_dir + local user_member_groups user_name user_password user_password_expire + local user_shell user_uid # Variables created by f_input_user() below + f_input_user "$user" || return $FAILURE + + f_dprintf "Creating home directory \`%s' for user \`%s'" \ + "$user_home_dir" "$user" + + local _user_gid _user_home_dir _user_uid + f_shell_escape "$user_gid" _user_gid + f_shell_escape "$user_home_dir" _user_home_dir + f_shell_escape "$user_uid" _user_uid + f_eval_catch $funcname mkdir "mkdir -p '%s'" "$_user_home_dir" || + return $FAILURE + f_eval_catch $funcname chown "chown '%i:%i' '%s'" \ + "$_user_uid" "$_user_gid" "$_user_home_dir" || return $FAILURE +} + +# f_user_copy_dotfiles $user +# +# Copy `skel' dot-files from $udotdir (global inherited from /etc/adduser.conf) +# to the home-directory of $user. Attempts to create the home-directory first +# if it doesn't exist. +# +f_user_copy_dotfiles() +{ + local funcname=f_user_copy_dotfiles + local user="$1" + + [ "$udotdir" ] || return $FAILURE + [ "$user" ] || return $FAILURE + + local user_account_expire user_class user_gecos user_gid user_home_dir + local user_member_groups user_name user_password user_password_expire + local user_shell user_uid # Variables created by f_input_user() below + f_input_user "$user" || return $FAILURE + + f_dprintf "Copying dot-files from \`%s' to \`%s'" \ + "$udotdir" "$user_home_dir" + + # Attempt to create the home directory if it doesn't exist + [ -d "$user_home_dir" ] || + f_user_create_homedir "$user" || return $FAILURE + + local _user_gid _user_home_dir _user_uid + f_shell_escape "$user_gid" _user_gid + f_shell_escape "$user_home_dir" _user_home_dir + f_shell_escape "$user_uid" _user_uid + + local - # Localize `set' to this function + set +f # Enable glob pattern-matching for paths + cd "$udotdir" || return $FAILURE + + local _file file retval + for file in dot.*; do + [ -e "$file" ] || continue # no-match + + f_shell_escape "$file" "_file" + f_eval_catch $funcname cp "cp -n '%s' '%s'" \ + "$_file" "$_user_home_dir/${_file#dot}" + retval=$? + [ $retval -eq $SUCCESS ] || break + f_eval_catch $funcname chown \ + "chown -h '%i:%i' '%s'" \ + "$_user_uid" "$_user_gid" \ + "$_user_home_dir/${_file#dot}" + retval=$? + [ $retval -eq $SUCCESS ] || break + done + + cd - + return $retval +} + +# f_user_add [$user] +# +# Create a login account. If both $user (as a first argument) and $VAR_USER are +# unset or NULL and we are running interactively, prompt the end-user to enter +# the name of a new login account and (if $VAR_NO_CONFIRM is unset or NULL) +# prompt the end-user to answer some questions about the new account. Variables +# that can be used to script user input: +# +# VAR_USER [Optional if running interactively] +# The login to add. Ignored if given non-NULL first-argument. +# VAR_USER_ACCOUNT_EXPIRE [Optional] +# The account expiration time. Format is similar to +# VAR_USER_PASSWORD_EXPIRE variable below. Default is to never +# expire the account. +# VAR_USER_DOTFILES_CREATE [Optional] +# If non-NULL, populate the user's home directory with the +# template files found in $udotdir (`/usr/share/skel' default). +# VAR_USER_GECOS [Optional] +# Often the full name of the account holder. Default is NULL. +# VAR_USER_GID [Optional] +# Numerical primary-group ID to use. If NULL or unset, the group +# ID is automatically chosen. +# VAR_USER_GROUPS [Optional] +# Comma-separated list of additional groups to which the user is +# a member of. Default is NULL (no additional groups). +# VAR_USER_HOME [Optional] +# The home directory to set. If NULL or unset, the home directory +# is automatically calculated. +# VAR_USER_HOME_CREATE [Optional] +# If non-NULL, create the user's home directory if it doesn't +# already exist. +# VAR_USER_LOGIN_CLASS [Optional] +# Login class to use when creating the login. Default is NULL. +# VAR_USER_PASSWORD [Optional] +# Unencrypted password to use. If unset or NULL, password +# authentication for the login is disabled. +# VAR_USER_PASSWORD_EXPIRE [Optional] +# The password expiration time. Format of the date is either a +# UNIX time in decimal, or a date in dd-mmm-yy[yy] format, where +# dd is the day, mmm is the month in either numeric or alphabetic +# format, and yy[yy] is either a two or four digit year. This +# variable also accepts a relative date in the form of +n[mhdwoy] +# where n is a decimal, octal (leading 0) or hexadecimal (leading +# 0x) digit followed by the number of Minutes, Hours, Days, +# Weeks, Months or Years from the current date at which the +# expiration time is to be set. Default is to never expire the +# account password. +# VAR_USER_SHELL [Optional] +# Path to login shell to use. Default is `/bin/sh'. +# VAR_USER_UID [Optional] +# Numerical user ID to use. If NULL or unset, the user ID is +# automatically chosen. +# +# Returns success if the user account was successfully created. +# +f_user_add() +{ + local funcname=f_user_add + local title # Calculated below + local alert=f_show_msg no_confirm= + + f_getvar $VAR_NO_CONFIRM no_confirm + [ "$no_confirm" ] && alert=f_show_info + + local input + f_getvar 3:-\$$VAR_USER input "$1" + + # + # NB: pw(8) has a ``feature'' wherein `-n name' can be taken as UID + # instead of name. Work-around is to also pass `-u UID' at the same + # time (any UID will do; but `-1' is appropriate for this context). + # + if [ "$input" ] && f_quietly pw usershow -n "$input" -u -1; then + f_show_err "$msg_login_already_used" "$input" + return $FAILURE + fi + + local user_name="$input" + while f_interactive && [ ! "$user_name" ]; do + f_dialog_input_name user_name "$user_name" || + return $SUCCESS + [ "$user_name" ] || + f_show_err "$msg_please_enter_a_user_name" + done + if [ ! "$user_name" ]; then + f_show_err "$msg_no_user_specified" + return $FAILURE + fi + + local user_account_expire user_class user_gecos user_gid user_home_dir + local user_member_groups user_password user_password_expire user_shell + local user_uid user_dotfiles_create= user_home_create= + f_getvar $VAR_USER_ACCOUNT_EXPIRE-\$uexpire user_account_expire + f_getvar $VAR_USER_DOTFILES_CREATE:+\$msg_yes user_dotfiles_create + f_getvar $VAR_USER_GECOS-\$ugecos user_gecos + f_getvar $VAR_USER_GID user_gid + f_getvar $VAR_USER_GROUPS user_member_groups + f_getvar $VAR_USER_HOME:-\${homeprefix%/}/\$user_name \ + user_home_dir + f_getvar $VAR_USER_HOME_CREATE:+\$msg_yes user_home_create + f_getvar $VAR_USER_LOGIN_CLASS-\$defaultclass user_class + f_getvar $VAR_USER_PASSWORD user_password + f_getvar $VAR_USER_PASSWORD_EXPIRE-\$upwexpire user_password_expire + f_getvar $VAR_USER_SHELL-\$defaultshell user_shell + f_getvar $VAR_USER_UID user_uid + + # Create home-dir if no script-override and does not exist + f_isset $VAR_USER_HOME_CREATE || [ -d "$user_home_dir" ] || + user_home_create="$msg_yes" + # Copy dotfiles if home-dir creation is desired, does not yet exist, + # and no script-override has been set + f_isset $VAR_USER_DOTFILES_CREATE || + [ "$user_home_create" != "$msg_yes" ] || + [ -d "$user_home_dir" ] || user_dotfiles_create="$msg_yes" + # Create home-dir if copying dotfiles but home-dir does not exist + [ "$user_dotfiles_create" -a ! -d "$user_home_dir" ] && + user_home_create="$msg_yes" + + # Set flags for meaningful NULL values if-provided + local no_account_expire= no_password_expire= null_gecos= null_members= + local user_password_disable= + f_isset $VAR_USER_ACCOUNT_EXPIRE && + [ ! "$user_account_expire" ] && no_account_expire=1 + f_isset $VAR_USER_GECOS && + [ ! "$user_gecos" ] && null_gecos=1 + f_isset $VAR_USER_GROUPS && + [ ! "$user_member_groups" ] && null_members=1 + f_isset $VAR_USER_PASSWORD && + [ ! "$user_password" ] && user_password_disable=1 + f_isset $VAR_USER_PASSWORD_EXPIRE && + [ ! "$user_password_expire" ] && no_password_expire=1 + + if f_interactive && [ ! "$no_confirm" ]; then + f_dialog_noyes \ + "$msg_use_default_values_for_all_account_details" + retval=$? + if [ $retval -eq $DIALOG_ESC ]; then + return $SUCCESS + elif [ $retval -ne $DIALOG_OK ]; then + # + # Ask series of questions to pre-fill the editor screen + # + # Defaults used in each dialog should allow the user to + # simply hit ENTER to proceed, because cancelling any + # single dialog will cause them to be returned to the + # previous menu. + # + + f_dialog_input_gecos user_gecos "$user_gecos" || + return $FAILURE + if [ "$passwdtype" = "yes" ]; then + f_dialog_input_password user_password \ + user_password_disable || + return $FAILURE + fi + f_dialog_input_uid user_uid "$user_uid" || + return $FAILURE + f_dialog_input_gid user_gid "$user_gid" || + return $FAILURE + f_dialog_input_member_groups user_member_groups \ + "$user_member_groups" || return $FAILURE + f_dialog_input_class user_class "$user_class" || + return $FAILURE + f_dialog_input_expire_password user_password_expire \ + "$user_password_expire" || return $FAILURE + f_dialog_input_expire_account user_account_expire \ + "$user_account_expire" || return $FAILURE + f_dialog_input_home_dir user_home_dir \ + "$user_home_dir" || return $FAILURE + if [ ! -d "$user_home_dir" ]; then + f_dialog_input_home_create user_home_create || + return $FAILURE + if [ "$user_home_create" = "$msg_yes" ]; then + f_dialog_input_dotfiles_create \ + user_dotfiles_create || + return $FAILURE + fi + fi + f_dialog_input_shell user_shell "$user_shell" || + return $FAILURE + fi + fi + + # + # Loop until the user decides to Exit, Cancel, or presses ESC + # + title="$msg_add $msg_user: $user_name" + if f_interactive; then + local mtag retval defaultitem= + while :; do + f_dialog_title "$title" + f_dialog_menu_user_add "$defaultitem" + retval=$? + f_dialog_title_restore + f_dialog_menutag_fetch mtag + f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" + defaultitem="$mtag" + + # Return if user either pressed ESC or chose Cancel/No + [ $retval -eq $DIALOG_OK ] || return $FAILURE + + case "$mtag" in + X) # Add/Exit + local var + for var in account_expire class gecos gid home_dir \ + member_groups name password_expire shell uid \ + ; do + local _user_$var + eval f_shell_escape \"\$user_$var\" _user_$var + done + + local cmd="pw useradd -n '$_user_name'" + [ "$user_gid" ] && cmd="$cmd -g '$_user_gid'" + [ "$user_shell" ] && cmd="$cmd -s '$_user_shell'" + [ "$user_uid" ] && cmd="$cmd -u '$_user_uid'" + [ "$user_account_expire" -o \ + "$no_account_expire" ] && + cmd="$cmd -e '$_user_account_expire'" + [ "$user_class" -o "$null_class" ] && + cmd="$cmd -L '$_user_class'" + [ "$user_gecos" -o "$null_gecos" ] && + cmd="$cmd -c '$_user_gecos'" + [ "$user_home_dir" ] && + cmd="$cmd -d '$_user_home_dir'" + [ "$user_member_groups" ] && + cmd="$cmd -G '$_user_member_groups'" + [ "$user_password_expire" -o \ + "$no_password_expire" ] && + cmd="$cmd -p '$_user_password_expire'" + + # Execute the command + if [ "$user_password_disable" ]; then + f_eval_catch $funcname pw '%s -h -' "$cmd" + elif [ "$user_password" ]; then + echo "$user_password" | f_eval_catch \ + $funcname pw '%s -h 0' "$cmd" + else + f_eval_catch $funcname pw '%s' "$cmd" + fi || continue + + # Create home directory if desired + [ "${user_home_create:-$msg_no}" != "$msg_no" ] && + f_user_create_homedir "$user_name" + + # Copy dotfiles if desired + [ "${user_dotfiles_create:-$msg_no}" != \ + "$msg_no" ] && f_user_copy_dotfiles "$user_name" + + break # to success + ;; + 1) # Login (prompt for new login name) + f_dialog_input_name input "$user_name" || + continue + if f_quietly pw usershow -n "$input" -u -1; then + f_show_err "$msg_login_already_used" "$input" + continue + fi + user_name="$input" + title="$msg_add $msg_user: $user_name" + user_home_dir="${homeprefix%/}/$user_name" + ;; + 2) # Full Name + f_dialog_input_gecos user_gecos "$user_gecos" && + [ ! "$user_gecos" ] && null_gecos=1 ;; + 3) # Password + f_dialog_input_password \ + user_password user_password_disable ;; + 4) # User ID + f_dialog_input_uid user_uid "$user_uid" ;; + 5) # Group ID + f_dialog_input_gid user_gid "$user_gid" ;; + 6) # Member of Groups + f_dialog_input_member_groups \ + user_member_groups "$user_member_groups" && + [ ! "$user_member_groups" ] && + null_members=1 ;; + 7) # Login Class + f_dialog_input_class user_class "$user_class" && + [ ! "$user_class" ] && null_class=1 ;; + 8) # Password Expires On + f_dialog_input_expire_password \ + user_password_expire "$user_password_expire" && + [ ! "$user_password_expire" ] && + no_password_expire=1 ;; + 9) # Account Expires On + f_dialog_input_expire_account \ + user_account_expire "$user_account_expire" && + [ ! "$user_account_expire" ] && + no_account_expire=1 ;; + A) # Home Directory + f_dialog_input_home_dir \ + user_home_dir "$user_home_dir" ;; + B) # Shell + f_dialog_input_shell user_shell "$user_shell" ;; + C) # Create Home Directory? + if [ "${user_home_create:-$msg_no}" != "$msg_no" ] + then + user_home_create="$msg_no" + else + user_home_create="$msg_yes" + fi ;; + D) # Create Dotfiles? + if [ "${user_dotfiles_create:-$msg_no}" != \ + "$msg_no" ] + then + user_dotfiles_create="$msg_no" + else + user_dotfiles_create="$msg_yes" + fi ;; + esac + done + else + local var + for var in account_expire class gecos gid home_dir \ + member_groups name password_expire shell uid \ + ; do + local _user_$var + eval f_shell_escape \"\$user_$var\" _user_$var + done + + # Form the command + local cmd="pw useradd -n '$_user_name'" + [ "$user_gid" ] && cmd="$cmd -g '$_user_gid'" + [ "$user_home_dir" ] && cmd="$cmd -d '$_user_home_dir'" + [ "$user_shell" ] && cmd="$cmd -s '$_user_shell'" + [ "$user_uid" ] && cmd="$cmd -u '$_user_uid'" + [ "$user_account_expire" -o "$no_account_expire" ] && + cmd="$cmd -e '$_user_account_expire'" + [ "$user_class" -o "$null_class" ] && + cmd="$cmd -L '$_user_class'" + [ "$user_gecos" -o "$null_gecos" ] && + cmd="$cmd -c '$_user_gecos'" + [ "$user_member_groups" -o "$null_members" ] && + cmd="$cmd -G '$_user_member_groups'" + [ "$user_password_expire" -o "$no_password_expire" ] && + cmd="$cmd -p '$_user_password_expire'" + + # Execute the command + local retval err + if [ "$user_password_disable" ]; then + f_eval_catch -k err $funcname pw '%s -h -' "$cmd" + elif [ "$user_password" ]; then + err=$( echo "$user_password" | f_eval_catch -de \ + $funcname pw '%s -h 0' "$cmd" 2>&1 ) + else + f_eval_catch -k err $funcname pw '%s' "$cmd" + fi + retval=$? + if [ $retval -ne $SUCCESS ]; then + f_show_err "%s" "$err" + return $retval + fi + + # Create home directory if desired + [ "${user_home_create:-$msg_no}" != "$msg_no" ] && + f_user_create_homedir "$user_name" + + # Copy dotfiles if desired + [ "${user_dotfiles_create:-$msg_no}" != "$msg_no" ] && + f_user_copy_dotfiles "$user_name" + fi + + f_dialog_title "$title" + $alert "$msg_login_added" + f_dialog_title_restore + [ "$no_confirm" -a "$USE_DIALOG" ] && sleep 1 + + return $SUCCESS +} + +# f_user_delete [$user] +# +# Delete a user. If both $user (as a first argument) and $VAR_USER are unset or +# NULL and we are running interactively, prompt the end-user to select a user +# account from a list of those available. Variables that can be used to script +# user input: +# +# VAR_USER [Optional if running interactively] +# The user to delete. Ignored if given non-NULL first-argument. +# +# Returns success if the user account was successfully deleted. +# +f_user_delete() +{ + local funcname=f_user_delete + local title # Calculated below + local alert=f_show_msg no_confirm= + + f_getvar $VAR_NO_CONFIRM no_confirm + [ "$no_confirm" ] && alert=f_show_info + + local input + f_getvar 3:-\$$VAR_USER input "$1" + + if f_interactive && [ ! "$input" ]; then + f_dialog_menu_user_list || return $SUCCESS + f_dialog_menutag_fetch input + [ "$input" = "X $msg_exit" ] && return $SUCCESS + elif [ ! "$input" ]; then + f_show_err "$msg_no_user_specified" + return $FAILURE + fi + + local user_account_expire user_class user_gecos user_gid user_home_dir + local user_member_groups user_name user_password user_password_expire + local user_shell user_uid # Variables created by f_input_user() below + if [ "$input" ] && ! f_input_user "$input"; then + f_show_err "$msg_login_not_found" "$input" + return $FAILURE + fi + + local user_group_delete= user_home_delete= + f_getvar $VAR_USER_GROUP_DELETE:-\$msg_no user_group_delete + f_getvar $VAR_USER_HOME_DELETE:-\$msg_no user_home_delete + + # Attempt to translate user GID into a group name + local user_group + if user_group=$( pw groupshow -g "$user_gid" 2> /dev/null ); then + user_group="${user_group%%:*}" + # Default to delete the primary group if no script-override and + # exists with same name as the user (same logic used by pw(8)) + f_isset $VAR_USER_GROUP_DELETE || + [ "$user_group" != "$user_name" ] || + user_group_delete="$msg_yes" + fi + + # + # Loop until the user decides to Exit, Cancel, or presses ESC + # + title="$msg_delete $msg_user: $user_name" + if f_interactive; then + local mtag retval defaultitem= + while :; do + f_dialog_title "$title" + f_dialog_menu_user_delete "$user_name" "$defaultitem" + retval=$? + f_dialog_title_restore + f_dialog_menutag_fetch mtag + f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" + defaultitem="$mtag" + + # Return if user either pressed ESC or chose Cancel/No + [ $retval -eq $DIALOG_OK ] || return $FAILURE + + case "$mtag" in + X) # Delete/Exit + f_shell_escape "$user_uid" _user_uid + + # Save group information in case pw(8) deletes it + # and we wanted to keep it (to be restored below) + if [ "${user_group_delete:-$msg_no}" = "$msg_no" ] + then + local v vars="gid members name password" + for v in $vars; do local group_$var; done + f_input_group "$user_group" + + # Remove user-to-delete from group members + # NB: Otherwise group restoration could fail + local name length=0 _members= + while [ $length -ne ${#group_members} ]; do + name="${group_members%%,*}" + [ "$name" != "$user_name" ] && + _members="$_members,$name" + length=${#group_members} + group_members="${group_members#*,}" + done + group_members="${_members#,}" + + # Create escaped variables for f_eval_catch() + for v in $vars; do + local _group_$v + eval f_shell_escape \ + \"\$group_$v\" _group_$v + done + fi + + # Delete the user (if asked to delete home directory + # display [X]dialog notification to show activity) + local cmd="pw userdel -u '$_user_uid'" + if [ "$user_home_delete" = "$msg_yes" -a \ + "$USE_XDIALOG" ] + then + local err + err=$( + exec 9>&1 + f_eval_catch -e $funcname pw \ + "%s -r" "$cmd" \ + >&$DIALOG_TERMINAL_PASSTHRU_FD 2>&9 | + f_xdialog_info \ + "$msg_deleting_home_directory" + ) + [ ! "$err" ] + elif [ "$user_home_delete" = "$msg_yes" ]; then + f_dialog_info "$msg_deleting_home_directory" + f_eval_catch $funcname pw '%s -r' "$cmd" + else + f_eval_catch $funcname pw '%s' "$cmd" + fi || continue + + # + # pw(8) may conditionally delete the primary group, + # which may not be what is desired. + # + # If we've been asked to delete the group and pw(8) + # chose not to, delete it. Otherwise, if we're told + # to NOT delete the group, we may need to restore it + # since pw(8) doesn't have a flag to tell `userdel' + # to not delete the group. + # + # NB: If primary group and user have different names + # the group may not have been deleted (again, see PR + # 169471 and SVN r263114 for details). + # + if [ "${user_group_delete:-$msg_no}" != "$msg_no" ] + then + f_quietly pw groupshow -g "$user_gid" && + f_eval_catch $funcname pw \ + "pw groupdel -g '%s'" "$_user_gid" + elif ! f_quietly pw groupshow -g "$group_gid" && + [ "$group_name" -a "$group_gid" ] + then + # Group deleted by pw(8), so restore it + local cmd="pw groupadd -n '$_group_name'" + cmd="$cmd -g '$_group_gid'" + cmd="$cmd -M '$_group_members'" + + # Get the group password (pw(8) groupshow does + # NOT provide this (even if running privileged) + local group_password_enc + group_password_enc=$( getent group | awk -F: ' + !/^[[:space:]]*(#|$)/ && \ + $1 == ENVIRON["group_name"] && \ + $3 == ENVIRON["group_gid"] && \ + $4 == ENVIRON["group_members"] \ + { print $2; exit } + ' ) + if [ "$group_password_enc" ]; then + echo "$group_password_enc" | + f_eval_catch $funcname \ + pw '%s -H 0' "$cmd" + else + f_eval_catch $funcname \ + pw '%s -h -' "$cmd" + fi + fi + + break # to success + ;; + 1) # Login (select different login from list) + f_dialog_menu_user_list "$user_name" || continue + f_dialog_menutag_fetch mtag + + [ "$mtag" = "X $msg_exit" ] && continue + + if ! f_input_user "$mtag"; then + f_show_err "$msg_login_not_found" "$mtag" + # Attempt to fall back to previous selection + f_input_user "$input" || return $FAILURE + else + input="$mtag" + fi + title="$msg_delete $msg_user: $user_name" + ;; + C) # Delete Primary Group? + if [ "${user_group_delete:-$msg_no}" != "$msg_no" ] + then + user_group_delete="$msg_no" + else + user_group_delete="$msg_yes" + fi ;; + D) # Delete Home Directory? + if [ "${user_home_delete:-$msg_no}" != "$msg_no" ] + then + user_home_delete="$msg_no" + else + user_home_delete="$msg_yes" + fi ;; + esac + done + else + f_shell_escape "$user_uid" _user_uid + + # Save group information in case pw(8) deletes it + # and we wanted to keep it (to be restored below) + if [ "${user_group_delete:-$msg_no}" = "$msg_no" ]; then + local v vars="gid members name password" + for v in $vars; do local group_$v; done + f_input_group "$user_group" + + # Remove user we're about to delete from group members + # NB: Otherwise group restoration could fail + local name length=0 _members= + while [ $length -ne ${#group_members} ]; do + name="${group_members%%,*}" + [ "$name" != "$user_name" ] && + _members="$_members,$name" + length=${#group_members} + group_members="${group_members#*,}" + done + group_members="${_members#,}" + + # Create escaped variables for later f_eval_catch() + for v in $vars; do + local _group_$v + eval f_shell_escape \"\$group_$v\" _group_$v + done + fi + + # Delete the user (if asked to delete home directory + # display [X]dialog notification to show activity) + local err cmd="pw userdel -u '$_user_uid'" + if [ "$user_home_delete" = "$msg_yes" -a "$USE_XDIALOG" ]; then + err=$( + exec 9>&1 + f_eval_catch -de $funcname pw \ + '%s -r' "$cmd" 2>&9 | f_xdialog_info \ + "$msg_deleting_home_directory" + ) + [ ! "$err" ] + elif [ "$user_home_delete" = "$msg_yes" ]; then + f_dialog_info "$msg_deleting_home_directory" + f_eval_catch -k err $funcname pw '%s -r' "$cmd" + else + f_eval_catch -k err $funcname pw '%s' "$cmd" + fi + local retval=$? + if [ $retval -ne $SUCCESS ]; then + f_show_err "%s" "$err" + return $retval + fi + + # + # pw(8) may conditionally delete the primary group, which may + # not be what is desired. + # + # If we've been asked to delete the group and pw(8) chose not + # to, delete it. Otherwise, if we're told to NOT delete the + # group, we may need to restore it since pw(8) doesn't have a + # flag to tell `userdel' to not delete the group. + # + # NB: If primary group and user have different names the group + # may not have been deleted (again, see PR 169471 and SVN + # r263114 for details). + # + if [ "${user_group_delete:-$msg_no}" != "$msg_no" ] + then + f_quietly pw groupshow -g "$user_gid" && + f_eval_catch $funcname pw \ + "pw groupdel -g '%s'" "$_user_gid" + elif ! f_quietly pw groupshow -g "$group_gid" && + [ "$group_name" -a "$group_gid" ] + then + # Group deleted by pw(8), so restore it + local cmd="pw groupadd -n '$_group_name'" + cmd="$cmd -g '$_group_gid'" + cmd="$cmd -M '$_group_members'" + local group_password_enc + group_password_enc=$( getent group | awk -F: ' + !/^[[:space:]]*(#|$)/ && \ + $1 == ENVIRON["group_name"] && \ + $3 == ENVIRON["group_gid"] && \ + $4 == ENVIRON["group_members"] \ + { print $2; exit } + ' ) + if [ "$group_password_enc" ]; then + echo "$group_password_enc" | + f_eval_catch $funcname \ + pw '%s -H 0' "$cmd" + else + f_eval_catch $funcname \ + pw '%s -h -' "$cmd" + fi + fi + fi + + f_dialog_title "$title" + $alert "$msg_login_deleted" + f_dialog_title_restore + [ "$no_confirm" -a "$USE_DIALOG" ] && sleep 1 + + return $SUCCESS +} + +# f_user_edit [$user] +# +# Modify a login account. If both $user (as a first argument) and $VAR_USER are +# unset or NULL and we are running interactively, prompt the end-user to select +# a login account from a list of those available. Variables that can be used to +# script user input: +# +# VAR_USER [Optional if running interactively] +# The login to modify. Ignored if given non-NULL first-argument. +# VAR_USER_ACCOUNT_EXPIRE [Optional] +# The account expiration time. Format is similar to +# VAR_USER_PASSWORD_EXPIRE variable below. If unset, account +# expiry is unchanged. If set but NULL, account expiration is +# disabled (same as setting a value of `0'). +# VAR_USER_DOTFILES_CREATE [Optional] +# If non-NULL, re-populate the user's home directory with the +# template files found in $udotdir (`/usr/share/skel' default). +# VAR_USER_GECOS [Optional] +# Often the full name of the account holder. If unset, the GECOS +# field is unmodified. If set but NULL, the field is blanked. +# VAR_USER_GID [Optional] +# Numerical primary-group ID to set. If NULL or unset, the group +# ID is unchanged. +# VAR_USER_GROUPS [Optional] +# Comma-separated list of additional groups to which the user is +# a member of. If set but NULL, group memberships are reset (this +# login will not be a member of any additional groups besides the +# primary group). If unset, group membership is unmodified. +# VAR_USER_HOME [Optional] +# The home directory to set. If NULL or unset, the home directory +# is unchanged. +# VAR_USER_HOME_CREATE [Optional] +# If non-NULL, create the user's home directory if it doesn't +# already exist. +# VAR_USER_LOGIN_CLASS [Optional] +# Login class to set. If unset, the login class is unchanged. If +# set but NULL, the field is blanked. +# VAR_USER_PASSWORD [Optional] +# Unencrypted password to set. If unset, the login password is +# unmodified. If set but NULL, password authentication for the +# login is disabled. +# VAR_USER_PASSWORD_EXPIRE [Optional] +# The password expiration time. Format of the date is either a +# UNIX time in decimal, or a date in dd-mmm-yy[yy] format, where +# dd is the day, mmm is the month in either numeric or alphabetic +# format, and yy[yy] is either a two or four digit year. This +# variable also accepts a relative date in the form of +n[mhdwoy] +# where n is a decimal, octal (leading 0) or hexadecimal (leading +# 0x) digit followed by the number of Minutes, Hours, Days, +# Weeks, Months or Years from the current date at which the +# expiration time is to be set. If unset, password expiry is +# unchanged. If set but NULL, password expiration is disabled +# (same as setting a value of `0'). +# VAR_USER_SHELL [Optional] +# Path to login shell to set. If NULL or unset, the shell is +# unchanged. +# VAR_USER_UID [Optional] +# Numerical user ID to set. If NULL or unset, the user ID is +# unchanged. +# +# Returns success if the user account was successfully modified. +# +f_user_edit() +{ + local funcname=f_user_edit + local title # Calculated below + local alert=f_show_msg no_confirm= + + f_getvar $VAR_NO_CONFIRM no_confirm + [ "$no_confirm" ] && alert=f_show_info + + local input + f_getvar 3:-\$$VAR_USER input "$1" + + # + # NB: pw(8) has a ``feature'' wherein `-n name' can be taken as UID + # instead of name. Work-around is to also pass `-u UID' at the same + # time (any UID will do; but `-1' is appropriate for this context). + # + if [ "$input" ] && ! f_quietly pw usershow -n "$input" -u -1; then + f_show_err "$msg_login_not_found" "$input" + return $FAILURE + fi + + if f_interactive && [ ! "$input" ]; then + f_dialog_menu_user_list || return $SUCCESS + f_dialog_menutag_fetch input + [ "$input" = "X $msg_exit" ] && return $SUCCESS + elif [ ! "$input" ]; then + f_show_err "$msg_no_user_specified" + return $FAILURE + fi + + local user_account_expire user_class user_gecos user_gid user_home_dir + local user_member_groups user_name user_password user_password_expire + local user_shell user_uid # Variables created by f_input_user() below + if ! f_input_user "$input"; then + f_show_err "$msg_login_not_found" "$input" + return $FAILURE + fi + + # + # Override values probed by f_input_user() with desired values + # + f_isset $VAR_USER_GID && f_getvar $VAR_USER_GID user_gid + f_isset $VAR_USER_HOME && f_getvar $VAR_USER_HOME user_home_dir + f_isset $VAR_USER_SHELL && f_getvar $VAR_USER_SHELL user_shell + f_isset $VAR_USER_UID && f_getvar $VAR_USER_UID user_uid + local user_dotfiles_create= user_home_create= + f_getvar $VAR_USER_DOTFILES_CREATE:+\$msg_yes user_dotfiles_create + f_getvar $VAR_USER_HOME_CREATE:+\$msg_yes user_home_create + local no_account_expire= + if f_isset $VAR_USER_ACCOUNT_EXPIRE; then + f_getvar $VAR_USER_ACCOUNT_EXPIRE user_account_expire + [ "$user_account_expire" ] || no_account_expire=1 + fi + local null_gecos= + if f_isset $VAR_USER_GECOS; then + f_getvar $VAR_USER_GECOS user_gecos + [ "$user_gecos" ] || null_gecos=1 + fi + local null_members= + if f_isset $VAR_USER_GROUPS; then + f_getvar $VAR_USER_GROUPS user_member_groups + [ "$user_member_groups" ] || null_members=1 + fi + local null_class= + if f_isset $VAR_USER_LOGIN_CLASS; then + f_getvar $VAR_USER_LOGIN_CLASS user_class + [ "$user_class" ] || null_class=1 + fi + local user_password_disable= + if f_isset $VAR_USER_PASSWORD; then + f_getvar $VAR_USER_PASSWORD user_password + [ "$user_password" ] || user_password_disable=1 + fi + local no_password_expire= + if f_isset $VAR_USER_PASSWORD_EXPIRE; then + f_getvar $VAR_USER_PASSWORD_EXPIRE user_password_expire + [ "$user_password_expire" ] || no_password_expire=1 + fi + + # + # Loop until the user decides to Exit, Cancel, or presses ESC + # + title="$msg_edit_view $msg_user: $user_name" + if f_interactive; then + local mtag retval defaultitem= + while :; do + f_dialog_title "$title" + f_dialog_menu_user_edit "$defaultitem" + retval=$? + f_dialog_title_restore + f_dialog_menutag_fetch mtag + f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" + defaultitem="$mtag" + + # Return if user either pressed ESC or chose Cancel/No + [ $retval -eq $DIALOG_OK ] || return $FAILURE + + case "$mtag" in + X) # Save/Exit + local var + for var in account_expire class gecos gid home_dir \ + member_groups name password_expire shell uid \ + ; do + local _user_$var + eval f_shell_escape \"\$user_$var\" _user_$var + done + + local cmd="pw usermod -n '$_user_name'" + [ "$user_gid" ] && cmd="$cmd -g '$_user_gid'" + [ "$user_shell" ] && cmd="$cmd -s '$_user_shell'" + [ "$user_uid" ] && cmd="$cmd -u '$_user_uid'" + [ "$user_account_expire" -o \ + "$no_account_expire" ] && + cmd="$cmd -e '$_user_account_expire'" + [ "$user_class" -o "$null_class" ] && + cmd="$cmd -L '$_user_class'" + [ "$user_gecos" -o "$null_gecos" ] && + cmd="$cmd -c '$_user_gecos'" + [ "$user_home_dir" ] && + cmd="$cmd -d '$_user_home_dir'" + [ "$user_member_groups" -o "$null_members" ] && + cmd="$cmd -G '$_user_member_groups'" + [ "$user_password_expire" -o \ + "$no_password_expire" ] && + cmd="$cmd -p '$_user_password_expire'" + + # Execute the command + if [ "$user_password_disable" ]; then + f_eval_catch $funcname pw '%s -h -' "$cmd" + elif [ "$user_password" ]; then + echo "$user_password" | f_eval_catch \ + $funcname pw '%s -h 0' "$cmd" + else + f_eval_catch $funcname pw '%s' "$cmd" + fi || continue + + # Create home directory if desired + [ "${user_home_create:-$msg_no}" != "$msg_no" ] && + f_user_create_homedir "$user_name" + + # Copy dotfiles if desired + [ "${user_dotfiles_create:-$msg_no}" != \ + "$msg_no" ] && f_user_copy_dotfiles "$user_name" + + break # to success + ;; + 1) # Login (select different login from list) + f_dialog_menu_user_list "$user_name" || continue + f_dialog_menutag_fetch mtag + + [ "$mtag" = "X $msg_exit" ] && continue + + if ! f_input_user "$mtag"; then + f_show_err "$msg_login_not_found" "$mtag" + # Attempt to fall back to previous selection + f_input_user "$input" || return $FAILURE + else + input="$mtag" + fi + title="$msg_edit_view $msg_user: $user_name" + ;; + 2) # Full Name + f_dialog_input_gecos user_gecos "$user_gecos" && + [ ! "$user_gecos" ] && null_gecos=1 ;; + 3) # Password + f_dialog_input_password \ + user_password user_password_disable ;; + 4) # User ID + f_dialog_input_uid user_uid "$user_uid" ;; + 5) # Group ID + f_dialog_input_gid user_gid "$user_gid" ;; + 6) # Member of Groups + f_dialog_input_member_groups \ + user_member_groups "$user_member_groups" && + [ ! "$user_member_groups" ] && + null_members=1 ;; + 7) # Login Class + f_dialog_input_class user_class "$user_class" && + [ ! "$user_class" ] && null_class=1 ;; + 8) # Password Expires On + f_dialog_input_expire_password \ + user_password_expire "$user_password_expire" && + [ ! "$user_password_expire" ] && + no_password_expire=1 ;; + 9) # Account Expires On + f_dialog_input_expire_account \ + user_account_expire "$user_account_expire" && + [ ! "$user_account_expire" ] && + no_account_expire=1 ;; + A) # Home Directory + f_dialog_input_home_dir \ + user_home_dir "$user_home_dir" ;; + B) # Shell + f_dialog_input_shell user_shell "$user_shell" ;; + C) # Create Home Directory? + if [ "${user_home_create:-$msg_no}" != "$msg_no" ] + then + user_home_create="$msg_no" + else + user_home_create="$msg_yes" + fi ;; + D) # Create Dotfiles? + if [ "${user_dotfiles_create:-$msg_no}" != \ + "$msg_no" ] + then + user_dotfiles_create="$msg_no" + else + user_dotfiles_create="$msg_yes" + fi ;; + esac + done + else + local var + for var in account_expire class gecos gid home_dir \ + member_groups name password_expire shell uid \ + ; do + local _user_$var + eval f_shell_escape \"\$user_$var\" _user_$var + done + + # Form the command + local cmd="pw usermod -n '$_user_name'" + [ "$user_gid" ] && cmd="$cmd -g '$_user_gid'" + [ "$user_home_dir" ] && cmd="$cmd -d '$_user_home_dir'" + [ "$user_shell" ] && cmd="$cmd -s '$_user_shell'" + [ "$user_uid" ] && cmd="$cmd -u '$_user_uid'" + [ "$user_account_expire" -o "$no_account_expire" ] && + cmd="$cmd -e '$_user_account_expire'" + [ "$user_class" -o "$null_class" ] && + cmd="$cmd -L '$_user_class'" + [ "$user_gecos" -o "$null_gecos" ] && + cmd="$cmd -c '$_user_gecos'" + [ "$user_member_groups" -o "$null_members" ] && + cmd="$cmd -G '$_user_member_groups'" + [ "$user_password_expire" -o "$no_password_expire" ] && + cmd="$cmd -p '$_user_password_expire'" + + # Execute the command + local retval err + if [ "$user_password_disable" ]; then + f_eval_catch -k err $funcname pw '%s -h -' "$cmd" + elif [ "$user_password" ]; then + err=$( echo "$user_password" | f_eval_catch -de \ + $funcname pw '%s -h 0' "$cmd" 2>&1 ) + else + f_eval_catch -k err $funcname pw '%s' "$cmd" + fi + retval=$? + if [ $retval -ne $SUCCESS ]; then + f_show_err "%s" "$err" + return $retval + fi + + # Create home directory if desired + [ "${user_home_create:-$msg_no}" != "$msg_no" ] && + f_user_create_homedir "$user_name" + + # Copy dotfiles if desired + [ "${user_dotfiles_create:-$msg_no}" != "$msg_no" ] && + f_user_copy_dotfiles "$user_name" + fi + + f_dialog_title "$title" + $alert "$msg_login_updated" + f_dialog_title_restore + [ "$no_confirm" -a "$USE_DIALOG" ] && sleep 1 + + return $SUCCESS +} + +############################################################ MAIN + +f_dprintf "%s: Successfully loaded." usermgmt/user.subr + +fi # ! $_USERMGMT_USER_SUBR diff --git a/usr.sbin/bsdconfig/usermgmt/share/user_input.subr b/usr.sbin/bsdconfig/usermgmt/share/user_input.subr index c95685c..39578c8 100644 --- a/usr.sbin/bsdconfig/usermgmt/share/user_input.subr +++ b/usr.sbin/bsdconfig/usermgmt/share/user_input.subr @@ -1,7 +1,7 @@ if [ ! "$_USERMGMT_USER_INPUT_SUBR" ]; then _USERMGMT_USER_INPUT_SUBR=1 # # Copyright (c) 2012 Ron McDowell -# Copyright (c) 2012-2013 Devin Teske +# Copyright (c) 2012-2014 Devin Teske # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -48,39 +48,57 @@ f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ############################################################ FUNCTIONS -# f_get_member_groups $user +# f_get_member_groups $var_to_set $user # # Get a list of additional groups $user is a member of in group(5). # f_get_member_groups() { - echo $( pw groupshow -a | awk -F: "/[:,]$1(,|\$)/{print \$1}" ) + f_replaceall "$( pw groupshow -a | awk -F: -v user="$2" '{ + if (!split($4, users, /,/)) next + for (u in users) if (users[u] == user) { print $1; next } + }' )" "[$NL]" "," "$1" } # f_input_user $user # -# Given $user name or id, create the environment variables pw_name, pw_uid, -# pw_gid, pw_class, pw_password_expire, pw_account_expire, pw_gecos, -# pw_home_dir, pw_shell, and pw_member_groups (and pw_password is reset to -# NULL). +# Given $user name or id, create the environment variables user_name, user_uid, +# user_gid, user_class, user_password_expire, user_account_expire, user_gecos, +# user_home_dir, user_shell, and user_member_groups (and user_password is reset +# to NULL). # f_input_user() { + local funcname=f_input_user local user="$1" - eval $( pw usershow "$user" | awk -F: ' + + f_dprintf "$funcname: Getting info for user \`%s'" "$user" + eval "$( pw usershow "$user" 2> /dev/null | awk -F: ' + function set_value(var, value) { + gsub(/'\''/, "'\''\\'\'\''", value) + printf "user_%s='\'%s\''\n", var, value + } { - printf "pw_name='\'%s\''\n", $1 - printf "pw_password=\n" - printf "pw_uid='\'%s\''\n", $3 - printf "pw_gid='\'%s\''\n", $4 - printf "pw_class='\'%s\''\n", $5 - printf "pw_password_expire='\'%s\''\n", $6 - printf "pw_account_expire='\'%s\''\n", $7 - printf "pw_gecos='\'%s\''\n", $8 - printf "pw_home_dir='\'%s\''\n", $9 - printf "pw_shell='\'%s\''\n", $10 - }' ) - pw_member_groups=$( f_get_member_groups "$user" ) + found = $1 != "" + set_value("name", $1 ) + set_value("password", "" ) + set_value("uid", $3 ) + set_value("gid", $4 ) + set_value("class", $5 ) + set_value("password_expire", $6 ) + set_value("account_expire", $7 ) + set_value("gecos", $8 ) + set_value("home_dir", $9 ) + set_value("shell", $10) + exit + } + END { if (!found) print "false" }' )" + local retval=$? + + f_dprintf "$funcname: Getting group memberships for user \`%s'" "$user" + f_get_member_groups user_member_groups "$user" + + return $retval } # f_dialog_menu_user_list [$default] @@ -99,10 +117,13 @@ f_dialog_menu_user_list() # Add users from passwd(5) menu_list="$menu_list $( pw usershow -a | awk -F: ' - !/^[[:space:]]*(#|$)/ { - printf "'\'%s\'\ \'%s\''\n", $1, $8 - }' - )" + function mprint(tag, item) { + gsub(/'\''/, "'\''\\'\'\''", tag) + gsub(/'\''/, "'\''\\'\'\''", item) + printf "'\'%s\'\ \'%s\''\n", tag, item + } + !/^[[:space:]]*(#|$)/ { mprint($1, $8) } + ' )" local height width rows eval f_dialog_menu_size height width rows \ @@ -130,760 +151,710 @@ f_dialog_menu_user_list() return $retval } -# f_dialog_input_member_groups [$member_groups] +# f_dialog_input_member_groups $var_to_set [$member_groups] # # Allows the user to edit group memberships for a given user. If the user does -# not cancel or press ESC, the $pw_member_groups variable will hold the newly- +# not cancel or press ESC, the $var_to_set variable will hold the newly- # configured value upon return. # f_dialog_input_member_groups() { - local _member_groups="$1" - local prompt="$msg_member_of_groups" - local check_list= # Calculated below - local hline="$hline_alnum_space_tab_enter" - local group + local __var_to_set="$1" __input="$2" + local __prompt="$msg_member_of_groups" + local __menu_list=" + 'X' '$msg_continue' + '1' '$msg_select_groups_from_list' + '2' '$msg_enter_groups_manually' + " # END-QUOTE + local __defaultitem= + local __hline="$hline_alnum_space_tab_enter" - # - # Generate the checklist menu - # - for group in $( - pw groupshow -a | awk -F: '!/^[[:space:]]*(#|$)/{print $1}' - ); do - # Format of a checklist menu entry is "tag item status" - # (setting both tag and item to the group name below). - if echo "$_member_groups" | grep -q "\<$group\>"; then - check_list="$check_list $group $group on" - else - check_list="$check_list $group $group off" - fi - done + local __mheight __mwidth __mrows + eval f_dialog_menu_size __mheight __mwidth __mrows \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$__prompt\" \ + \"\$__hline\" \ + $__menu_list - # - # Loop until the user provides taint-free/valid input - # - local height width rows + local __menu_choice __retval while :; do - eval f_dialog_checklist_size height width rows \ - \"\$DIALOG_TITLE\" \ - \"\$DIALOG_BACKTITLE\" \ - \"\$prompt\" \ - \"\$hline\" \ - $check_list - _member_groups=$( eval $DIALOG \ + __menu_choice=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ - --separate-output \ - --hline \"\$hline\" \ + --hline \"\$__hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ - --checklist \"\$prompt\" \ - $height $width $rows \ - $check_list \ + --default-item \"\$__defaultitem\" \ + --menu \"\$__prompt\" \ + $__mheight $__mwidth $__mrows \ + $__menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD - ) || return $? - # Return if user either pressed ESC or chose Cancel/No - f_dialog_data_sanitize _member_groups - - # - # Validate each of the groups the user has entered - # - local all_groups_valid=1 - for group in $_member_groups; do - if ! f_quietly pw groupshow -n "$group"; then - f_show_msg "$msg_group_not_found" "$group" - all_groups_valid= - break - fi - done - [ "$all_groups_valid" ] || continue + ) + __retval=$? + f_dialog_data_sanitize __menu_choice + __defaultitem="$__menu_choice" + f_dprintf "retval=%u menu_choice=[%s]" \ + $__retval "$__menu_choice" - pw_member_groups="$_member_groups" - break - done - save_flag=1 + # Return if user has either pressed ESC or chosen Cancel/No + [ $__retval -eq $DIALOG_OK ] || return $__retval - f_dprintf "pw_member_groups: [%s]->[%s]" \ - "$cur_pw_member_groups" "$pw_member_groups" + local __member_groups + case "$__menu_choice" in + X) # Exit + break ;; + 1) # Select Groups from a list + local __check_list= # Calculated below + local __group_list __g __grp __length=0 + __group_list=$( pw groupshow -a | + awk -F: '!/^[[:space:]]*(#|$)/{print $1}' ) + while [ $__length -ne ${#__group_list} ]; do + __g="${__group_list%%$NL*}" # First line + f_shell_escape "$__g" __grp + + # Format of a checklist entry: tag item status + # NB: Setting both tag/item to group name below + __check_list="$__check_list '$__grp' '$__grp'" + case "$__input" in + "$__g"|"$__g",*|*,"$__g",*|*,"$__g") + __check_list="$__check_list on" ;; + *) + __check_list="$__check_list off" + esac + + __length=${#__group_list} + __group_list="${__group_list#*$NL}" # Kill line + done + + local __cheight __cwidth __crows + + eval f_dialog_checklist_size \ + __cheight __cwidth __crows \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$__prompt\" \ + \"\$__hline\" \ + $__check_list + __member_groups=$( eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --separate-output \ + --hline \"\$__hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --checklist \"\$__prompt\" \ + $__cheight $__cwidth $__crows \ + $__check_list \ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) || continue + # Return to previous menu if user either + # pressed ESC or chose Cancel/No + f_dialog_data_sanitize __member_groups + + # + # Convert the newline separated list into a comma- + # separated one so that if the user switches over to + # manual editing, list reflects checklist selections + # + f_replaceall "$__member_groups" "[$NL]" "," __input + ;; + 2) # Enter Groups manually + local __prompt2="$msg_groups" + __prompt2="$__prompt2 ($msg_separated_by_commas)" + + f_dialog_input __member_groups \ + "$__prompt2" "$__input" \ + "$hline_num_tab_enter" || continue + # Return to previous menu if user either + # pressed ESC or chose Cancel/No + + # + # Validate each of the groups the user has entered + # + local __all_groups_valid=1 __grp __grp_list + f_replaceall "$__member_groups" "," " " __grp_list + for __grp in $__grp_list; do + if ! f_quietly pw groupshow -n "$__grp"; then + f_show_msg "$msg_group_not_found" \ + "$__grp" + __all_groups_valid= + break + fi + done + [ "$__all_groups_valid" ] || continue + + __input="$__member_groups" + ;; + esac + done + setvar "$__var_to_set" "$__input" return $DIALOG_OK } -# f_dialog_input_name [$name] +# f_dialog_input_name $var_to_set [$name] # # Allows the user to enter a new username for a given user. If the user does -# not cancel or press ESC, the $pw_name variable will hold the newly-configured -# value upon return. -# -# If $cur_pw_name is defined, the user can enter that and by-pass error- -# checking (allowing the user to "revert" to an old value without, for example, -# being told that the username already exists). +# not cancel or press ESC, the $var_to_set variable will hold the newly- +# configured value upon return. # f_dialog_input_name() { + local __var_to_set="$1" __name="$2" + # # Loop until the user provides taint-free/valid input # - local _name="$1" _input="$1" + local __input="$__name" while :; do - # Return if user has either pressed ESC or chosen Cancel/No - f_dialog_input _input "$msg_login" "$_input" \ + f_dialog_input __input "$msg_login" "$__input" \ "$hline_alnum_tab_enter" || return $? # Check for no-change - [ "$_input" = "$_name" ] && return $DIALOG_OK - - # Check for reversion - if [ "$_input" = "$cur_pw_name" ]; then - pw_name="$cur_pw_name" + if [ "$__input" = "$__name" ]; then + setvar "$__var_to_set" "$__input" return $DIALOG_OK fi # Check for NULL entry - if [ ! "$_input" ]; then + if [ ! "$__input" ]; then f_show_msg "$msg_login_is_empty" continue fi # Check for invalid entry - if ! echo "$_input" | grep -q "^[[:alpha:]]"; then + case "$__input" in [!a-zA-Z]*) f_show_msg "$msg_login_must_start_with_letter" continue - fi + esac # Check for duplicate entry - if f_quietly pw usershow -n "$_input"; then - f_show_msg "$msg_login_already_used" "$_input" + if f_quietly pw usershow -n "$__input"; then + f_show_msg "$msg_login_already_used" "$__input" continue fi - pw_name="$_input" + setvar "$__var_to_set" "$__input" break done - save_flag=1 - - f_dprintf "pw_name: [%s]->[%s]" "$cur_pw_name" "$pw_name" return $DIALOG_OK } -# f_dialog_input_password +# f_dialog_input_password $var_to_set $dvar_to_set # -# Prompt the user to enter a password (twice). +# Prompt the user to enter a password (twice). If the user does not cancel or +# press ESC, $var_to_set will hold the confirmed user entry. Otherwise, if the +# user cancels or enters a NULL password (twice), they are given the choice to +# disable password authentication for the given login, wherein $dvar_to_set has +# a value of 1 to indicate password authentication should be disabled. # f_dialog_input_password() { - local prompt1="$msg_password" - local prompt2="$msg_reenter_password" - local hline="$hline_alnum_punc_tab_enter" + local __var_to_set="$1" __dvar_to_set="$2" + local __prompt1="$msg_password" + local __prompt2="$msg_reenter_password" + local __hline="$hline_alnum_punc_tab_enter" - local height1 width1 - f_dialog_inputbox_size height1 width1 \ + local __height1 __width1 + f_dialog_inputbox_size __height1 __width1 \ "$DIALOG_TITLE" \ "$DIALOG_BACKTITLE" \ - "$prompt1" \ + "$__prompt1" \ "" \ - "$hline" - local height2 width2 - f_dialog_inputbox_size height2 width2 \ + "$__hline" + local __height2 __width2 + f_dialog_inputbox_size __height2 __width2 \ "$DIALOG_TITLE" \ "$DIALOG_BACKTITLE" \ - "$prompt2" \ + "$__prompt2" \ "" \ - "$hline" + "$__hline" # # Loop until the user provides taint-free/valid input # - local _password1 _password2 + local __retval __password1 __password2 while :; do - _password1=$( $DIALOG \ + __password1=$( $DIALOG \ --title "$DIALOG_TITLE" \ --backtitle "$DIALOG_BACKTITLE" \ - --hline "$hline" \ + --hline "$__hline" \ --ok-label "$msg_ok" \ --cancel-label "$msg_cancel" \ --insecure \ - --passwordbox "$prompt1" \ - $height1 $width1 \ + --passwordbox "$__prompt1" \ + $__height1 $__width1 \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) || return $? # Return if user either pressed ESC or chose Cancel/No - debug= f_dialog_line_sanitize _password1 + debug= f_dialog_line_sanitize __password1 - _password2=$( $DIALOG \ + __password2=$( $DIALOG \ --title "$DIALOG_TITLE" \ --backtitle "$DIALOG_BACKTITLE" \ - --hline "$hline" \ + --hline "$__hline" \ --ok-label "$msg_ok" \ --cancel-label "$msg_cancel" \ --insecure \ - --passwordbox "$prompt2" \ - $height2 $width2 \ + --passwordbox "$__prompt2" \ + $__height2 $__width2 \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) || return $? # Return if user either pressed ESC or chose Cancel/No - debug= f_dialog_line_sanitize _password2 + debug= f_dialog_line_sanitize __password2 # Check for password mismatch - if [ "$_password1" != "$_password2" ]; then + if [ "$__password1" != "$__password2" ]; then f_show_msg "$msg_passwords_do_not_match" continue fi # Check for NULL entry - if [ ! "$_password1" ]; then + if [ ! "$__password1" ]; then f_dialog_yesno "$msg_disable_password_auth_for_account" - local retval=$? - if [ $retval -eq $DIALOG_ESC ]; then - return $retval - elif [ $retval -eq $DIALOG_OK ]; then - pw_password_disable=1 + __retval=$? + if [ $__retval -eq $DIALOG_ESC ]; then + return $__retval + elif [ $__retval -eq $DIALOG_OK ]; then + setvar "$__dvar_to_set" 1 else continue # back to password prompt fi else - pw_password_disable= + setvar "$__dvar_to_set" "" fi - pw_password="$_password1" + setvar "$__var_to_set" "$__password1" break done - save_flag=1 - - f_dprintf "pw_password: [%s]->[%s]" "$cur_pw_password" "$pw_password" return $DIALOG_OK } -# f_dialog_input_gecos [$gecos] +# f_dialog_input_gecos $var_to_set [$gecos] # # Allow the user to enter new GECOS information for a given user. This # information is commonly used to store the ``Full Name'' of the user. If the -# user does not cancel or press ESC, the $pw_gecos variable will hold the +# user does not cancel or press ESC, the $var_to_set variable will hold the # newly-configured value upon return. # f_dialog_input_gecos() { - local _input="$1" + local __var_to_set="$1" __input="$2" # Return if user has either pressed ESC or chosen Cancel/No - f_dialog_input _input "$msg_full_name" "$_input" \ + f_dialog_input __input "$msg_full_name" "$__input" \ "$hline_alnum_punc_tab_enter" || return $? - pw_gecos="$_input" - save_flag=1 - - f_dprintf "pw_gecos: [%s]->[%s]" "$cur_pw_gecos" "$pw_gecos" - + setvar "$__var_to_set" "$__input" return $DIALOG_OK } -# f_dialog_input_uid [$uid] +# f_dialog_input_uid $var_to_set [$uid] # # Allow the user to enter a new UID for a given user. If the user does not -# cancel or press ESC, the $pw_uid variable will hold the newly-configured +# cancel or press ESC, the $var_to_set variable will hold the newly-configured # value upon return. # f_dialog_input_uid() { - local _input="$1" + local __var_to_set="$1" __input="$2" # Return if user has either pressed ESC or chosen Cancel/No - f_dialog_input _input "$msg_user_id_leave_empty_for_default" \ - "$_input" "$hline_num_tab_enter" || return $? - - pw_uid="$_input" - save_flag=1 - - f_dprintf "pw_uid: [%s]->[%s]" "$cur_pw_uid" "$pw_uid" + f_dialog_input __input "$msg_user_id_leave_empty_for_default" \ + "$__input" "$hline_num_tab_enter" || return $? + setvar "$__var_to_set" "$__input" return $DIALOG_OK } -# f_dialog_input_gid [$gid] +# f_dialog_input_gid $var_to_set [$gid] # # Allow the user to enter a new primary GID for a given user. If the user does -# not cancel or press ESC, the $pw_gid variable will hold the newly-configured -# value upon return. +# not cancel or press ESC, the $var_to_set variable will hold the newly- +# configured value upon return. # f_dialog_input_gid() { - local _input="$1" + local __var_to_set="$1" __input="$2" # Return if user has either pressed ESC or chosen Cancel/No - f_dialog_input _input "$msg_group_id_leave_empty_for_default" \ - "$_input" "$hline_num_tab_enter" || return $? - - pw_gid="$_input" - save_flag=1 - - f_dprintf "pw_gid: [%s]->[%s]" "$cur_pw_gid" "$pw_gid" + f_dialog_input __input "$msg_group_id_leave_empty_for_default" \ + "$__input" "$hline_num_tab_enter" || return $? + setvar "$__var_to_set" "$__input" return $DIALOG_OK } -# f_dialog_input_class [$class] +# f_dialog_input_class $var_to_set [$class] # # Allow the user to enter a new login class for a given user. If the user does -# not cancel or press ESC, the $pw_class variable will hold the newly- +# not cancel or press ESC, the $var_to_set variable will hold the newly- # configured value upon return. # f_dialog_input_class() { - local _input="$1" + local __var_to_set="$1" __input="$2" # Return if user has either pressed ESC or chosen Cancel/No - f_dialog_input _input "$msg_login_class" "$_input" \ + f_dialog_input __input "$msg_login_class" "$__input" \ "$hline_alnum_tab_enter" || return $? - pw_class="$_input" - save_flag=1 - - f_dprintf "pw_class: [%s]->[%s]" "$cur_pw_class" "$pw_class" - + setvar "$__var_to_set" "$__input" return $DIALOG_OK } -# f_dialog_input_expire_password [$seconds] +# f_dialog_input_expire_password $var_to_set [$seconds] # # Allow the user to enter a date/time (in number-of-seconds since the `epoch') # for when a given user's password must be changed. If the user does not cancel -# or press ESC, the $pw_password_expire variable will hold the newly- -# configured value upon return. +# or press ESC, the $var_to_set variable will hold the newly-configured value +# upon return. # f_dialog_input_expire_password() { - local prompt="$msg_password_expires_on" - local menu_list=" + local __var_to_set="$1" __input="$2" + local __prompt="$msg_password_expires_on" + local __menu_list=" '1' '$msg_password_does_not_expire' '2' '$msg_edit_date_time_with_a_calendar' - '3' '$msg_enter_number_of_days_into_the_future' - '4' '$msg_enter_value_manually' + '3' '$msg_enter_value_manually' " # END-QUOTE - local hline="$hline_num_arrows_tab_enter" - local retval _input="$1" + local __defaultitem= # Calculated below + local __hline="$hline_num_arrows_tab_enter" - local mheight mwidth mrows - eval f_dialog_menu_size mheight mwidth mrows \ + local __mheight __mwidth __mrows + eval f_dialog_menu_size __mheight __mwidth __mrows \ \"\$DIALOG_TITLE\" \ \"\$DIALOG_BACKTITLE\" \ - \"\$prompt\" \ - \"\$hline\" \ - $menu_list - local cheight cwidth - f_dialog_calendar_size cheight cwidth \ + \"\$__prompt\" \ + \"\$__hline\" \ + $__menu_list + local __cheight __cwidth + f_dialog_calendar_size __cheight __cwidth \ "$DIALOG_TITLE" \ "$DIALOG_BACKTITLE" \ - "$prompt" \ - "$hline" - local theight twidth - f_dialog_timebox_size theight twidth \ + "$__prompt" \ + "$__hline" + local __theight __twidth + f_dialog_timebox_size __theight __twidth \ "$DIALOG_TITLE" \ "$DIALOG_BACKTITLE" \ - "$prompt" \ - "$hline" + "$__prompt" \ + "$__hline" # # Loop until the user provides taint-free/cancellation-free input # - local date_type defaultitem= + local __retval __date_type while :; do - date_type=$( eval $DIALOG \ + __date_type=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --default-item \"\$defaultitem\" \ + --hline \"\$__hline\" \ + --default-item \"\$__defaultitem\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ - --menu \"\$prompt\" \ - $mheight $mwidth $mrows \ - $menu_list \ + --menu \"\$__prompt\" \ + $__mheight $__mwidth $__mrows \ + $__menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) - retval=$? - f_dialog_data_sanitize date_type - defaultitem="$date_type" - f_dprintf "retval=%u date_type=[%s]" $retval "$date_type" + __retval=$? + f_dialog_data_sanitize __date_type + __defaultitem="$__date_type" + f_dprintf "retval=%u date_type=[%s]" $__retval "$__date_type" # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $DIALOG_OK ] || return $retval + [ $__retval -eq $DIALOG_OK ] || return $__retval - case "$date_type" in + case "$__date_type" in 1) # Password does not expire - _input="" - break ;; + __input= break ;; 2) # Edit date/time with a calendar - local _input_date _input_time ret_date ret_time + local __input_date __input_time __ret_date __ret_time - local secs="$_input" - { f_isinteger "$secs" && [ $secs -gt 0 ]; } || secs= - _input_date=$( date -j -f "%s" -- "$secs" \ + local __seconds="$__input" + { f_isinteger "$__seconds" && [ $__seconds -gt 0 ]; } || + __seconds= + __input_date=$( date -j -f "%s" -- "$__seconds" \ "+%d %m %Y" 2> /dev/null ) - ret_date=$( eval $DIALOG \ + __ret_date=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ + --hline \"\$__hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ - --calendar \"\$prompt\" \ - $cheight $cwidth \ - $_input_date \ + --calendar \"\$__prompt\" \ + $__cheight $__cwidth \ + $__input_date \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) - retval=$? - f_dialog_data_sanitize ret_date - f_dprintf "retval=%u ret_date=[%s]" $retval "$ret_date" + __retval=$? + f_dialog_data_sanitize __ret_date + f_dprintf "retval=%u ret_date=[%s]" \ + $__retval "$__ret_date" # Return to menu if either ESC or Cancel/No - [ $retval -eq $DIALOG_OK ] || continue + [ $__retval -eq $DIALOG_OK ] || continue - _input_time= - [ "$secs" ] && _input_time=$( date -j \ - -f %s -- "$_input" "+%H %M %S" 2> /dev/null ) - ret_time=$( eval $DIALOG \ + __input_time= + [ "$__seconds" ] && __input_time=$( date -j \ + -f %s -- "$__input" "+%H %M %S" 2> /dev/null ) + __ret_time=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ + --hline \"\$__hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ - --timebox \"\$prompt\" \ - $theight $twidth \ - $_input_time \ + --timebox \"\$__prompt\" \ + $__theight $__twidth \ + $__input_time \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) - retval=$? - f_dialog_data_sanitize ret_time - f_dprintf "retval=%u ret_time=[%s]" $retval "$ret_time" + __retval=$? + f_dialog_data_sanitize __ret_time + f_dprintf "retval=%u ret_time=[%s]" \ + $__retval "$__ret_time" # Return to menu if either ESC or Cancel/No - [ $retval -eq $DIALOG_OK ] || continue + [ $__retval -eq $DIALOG_OK ] || continue - _input=$( date \ - -j -f "%d/%m/%Y %T" \ - -- "$ret_date $ret_time" \ - +%s 2> /dev/null ) - f_dprintf "_input=[%s]" "$_input" + __input=$( date -j -f "%d/%m/%Y %T" -- \ + "$__ret_date $__ret_time" +%s 2> /dev/null ) + f_dprintf "input=[%s]" "$__input" break ;; - 3) # Enter number of days into the future - local ret_days seconds="$( date +%s )" - - f_isinteger "$_input" || _input=0 - [ $_input -gt 0 -a $_input -gt $seconds ] && - ret_days=$(( ( $_input - $seconds ) / 86400 )) - f_isinteger "$ret_days" && - ret_days=$(( $ret_days + 1 )) + 3) # Enter value manually + local __msg __new_input + f_sprintf __msg "$msg_password_expire_manual_edit" \ + "$( date -r 0 "+%c %Z" )" # Return to menu if either ESC or Cancel/No - f_dialog_input ret_days \ - "$msg_password_expires_in_how_many_days" \ - "$ret_days" "$hline" || continue - - # Taint-check the user's input - if ! f_isinteger "$ret_days"; then - f_show_msg "$msg_invalid_number_of_days" - continue - fi + f_dialog_input __new_input \ + "$__msg" "$__input" "$__hline" || continue - f_dprintf "ret_days=[%s]" "$ret_days" - case "$ret_days" in - [-+]*) _input=$( date -v${ret_days}d +%s ) ;; - 0) _input=$( date +%s ) ;; - *) _input=$( date -v+${ret_days}d +%s ) ;; - esac - f_dprintf "_input=[%s]" "$_input" - break ;; - - 4) # Enter value manually - local msg ret_secs - f_sprintf msg "$msg_number_of_seconds_since_epoch" \ - "$( date -r 1 "+%c %Z" )" - - # Return to menu if either ESC or Cancel/No - f_dialog_input ret_secs \ - "$msg" "$_input" "$hline" || continue - - _input="$ret_secs" - - # Taint-check the user's input - if ! f_isinteger "${_input:-0}"; then - f_show_msg "$msg_invalid_number_of_seconds" - continue - fi - - f_dprintf "_input=[%s]" "$_input" + __input="$__new_input" + f_dprintf "input=[%s]" "$__input" break ;; esac done # Loop forever - pw_password_expire="$_input" - save_flag=1 - - f_dprintf "pw_password_expire: [%s]->[%s]" \ - "$cur_pw_password_expire" "$pw_password_expire" - + setvar "$__var_to_set" "$__input" return $DIALOG_OK } -# f_dialog_input_expire_account [$seconds] +# f_dialog_input_expire_account $var_to_set [$seconds] # # Allow the user to enter a date/time (in number-of-seconds since the `epoch') # for when a given user's account should become expired. If the user does not -# cancel or press ESC, the $pw_account_expire variable will hold the newly- -# configured value upon return. +# cancel or press ESC, the $var_to_set variable will hold the newly-configured +# value upon return. # f_dialog_input_expire_account() { - local prompt="$msg_account_expires_on" - local menu_list=" + local __var_to_set="$1" __input="$2" + local __prompt="$msg_account_expires_on" + local __menu_list=" '1' '$msg_account_does_not_expire' '2' '$msg_edit_date_time_with_a_calendar' - '3' '$msg_enter_number_of_days_into_the_future' - '4' '$msg_enter_value_manually' + '3' '$msg_enter_value_manually' " # END-QUOTE - local hline="$hline_num_arrows_tab_enter" - local retval _input="$1" + local __defaultitem= # Calculated below + local __hline="$hline_num_arrows_tab_enter" - local mheight mwidth mrows - eval f_dialog_menu_size mheight mwidth mrows \ + local __mheight __mwidth __mrows + eval f_dialog_menu_size __mheight __mwidth __mrows \ \"\$DIALOG_TITLE\" \ \"\$DIALOG_BACKTITLE\" \ - \"\$prompt\" \ - \"\$hline\" \ - $menu_list - local cheight cwidth - f_dialog_calendar_size cheight cwidth \ + \"\$__prompt\" \ + \"\$__hline\" \ + $__menu_list + local __cheight __cwidth + f_dialog_calendar_size __cheight __cwidth \ "$DIALOG_TITLE" \ "$DIALOG_BACKTITLE" \ - "$prompt" \ - "$hline" - local theight twidth - f_dialog_timebox_size theight twidth \ + "$__prompt" \ + "$__hline" + local __theight __twidth + f_dialog_timebox_size __theight __twidth \ "$DIALOG_TITLE" \ "$DIALOG_BACKTITLE" \ - "$prompt" \ - "$hline" + "$__prompt" \ + "$__hline" # # Loop until the user provides taint-free/cancellation-free input # - local date_type defaultitem= + local __retval __date_type while :; do - date_type=$( eval $DIALOG \ + __date_type=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --default-item \"\$defaultitem\" \ + --hline \"\$__hline\" \ + --default-item \"\$__defaultitem\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ - --menu \"\$prompt\" \ - $mheight $mwidth $mrows \ - $menu_list \ + --menu \"\$__prompt\" \ + $__mheight $__mwidth $__mrows \ + $__menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) - retval=$? - f_dialog_data_sanitize date_type - defaultitem="$date_type" - f_dprintf "retval=%u date_type=[%s]" $retval "$date_type" + __retval=$? + f_dialog_data_sanitize __date_type + __defaultitem="$__date_type" + f_dprintf "retval=%u date_type=[%s]" $__retval "$__date_type" # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $DIALOG_OK ] || return $retval + [ $__retval -eq $DIALOG_OK ] || return $__retval - case "$date_type" in + case "$__date_type" in 1) # Account does not expire - _input="" - break ;; + __input= break ;; 2) # Edit date/time with a calendar - local _input_date _input_time ret_date ret_time + local __input_date __input_time __ret_date __ret_time - local secs="$_input" - { f_isinteger "$secs" && [ $secs -gt 0 ]; } || secs= - _input_date=$( date -j -f "%s" -- "$secs" \ + local __seconds="$__input" + { f_isinteger "$__seconds" && [ $__seconds -gt 0 ]; } || + __seconds= + __input_date=$( date -j -f "%s" -- "$__seconds" \ "+%d %m %Y" 2> /dev/null ) - ret_date=$( eval $DIALOG \ + __ret_date=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ + --hline \"\$__hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ - --calendar \"\$prompt\" \ - $cheight $cwidth \ - $_input_date \ + --calendar \"\$__prompt\" \ + $__cheight $__cwidth \ + $__input_date \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) - retval=$? - f_dialog_data_sanitize ret_date - f_dprintf "retval=%u ret_date=[%s]" $retval "$ret_date" + __retval=$? + f_dialog_data_sanitize __ret_date + f_dprintf "retval=%u ret_date=[%s]" \ + $__retval "$__ret_date" # Return to menu if either ESC or Cancel/No - [ $retval -eq $DIALOG_OK ] || continue + [ $__retval -eq $DIALOG_OK ] || continue - _input_time= - [ "$secs" ] && _input_time=$( date -j \ - -f %s -- "$_input" "+%H %M %S" 2> /dev/null ) - ret_time=$( eval $DIALOG \ + __input_time= + [ "$__seconds" ] && __input_time=$( date -j \ + -f %s -- "$__input" "+%H %M %S" 2> /dev/null ) + __ret_time=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ + --hline \"\$__hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ - --timebox \"\$prompt\" \ - $theight $twidth \ - $_input_time \ + --timebox \"\$__prompt\" \ + $__theight $__twidth \ + $__input_time \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) - retval=$? - f_dialog_data_sanitize ret_time - f_dprintf "retval=%u ret_time=[%s]" $retval "$ret_time" - - # Return to menu if either ESC or Cancel/No - [ $retval -eq $DIALOG_OK ] || continue - - _input=$( date \ - -j -f "%d/%m/%Y %T" \ - -- "$ret_date $ret_time" \ - +%s 2> /dev/null ) - f_dprintf "_input=[%s]" "$_input" - break ;; - - 3) # Enter number of days into the future - local ret_days seconds="$( date +%s )" - - f_isinteger "$_input" || _input=0 - [ $_input -gt 0 -a $_input -gt $seconds ] && - ret_days=$(( ( $_input - $seconds ) / 86400 )) - f_isinteger "$ret_days" && - ret_days=$(( $ret_days + 1 )) + __retval=$? + f_dialog_data_sanitize __ret_time + f_dprintf "retval=%u ret_time=[%s]" \ + $__retval "$__ret_time" # Return to menu if either ESC or Cancel/No - f_dialog_input ret_days \ - "$msg_account_expires_in_how_many_days" \ - "$ret_days" "$hline" || continue - - # Taint-check the user's input - if ! f_isinteger "$ret_days"; then - f_show_msg "$msg_invalid_number_of_days" - continue - fi + [ $__retval -eq $DIALOG_OK ] || continue - f_dprintf "ret_days=[%s]" "$ret_days" - case "$ret_days" in - [-+]*) _input=$( date -v${ret_days}d +%s ) ;; - 0) _input=$( date +%s ) ;; - *) _input=$( date -v+${ret_days}d +%s ) ;; - esac - f_dprintf "_input=[%s]" "$_input" + __input=$( date -j -f "%d/%m/%Y %T" -- \ + "$ret_date $ret_time" +%s 2> /dev/null ) + f_dprintf "input=[%s]" "$__input" break ;; - 4) # Enter value manually - local msg ret_secs - f_sprintf msg "$msg_number_of_seconds_since_epoch" \ - "$( date -r 1 "+%c %Z" )" + 3) # Enter value manually + local __msg __new_input + f_sprintf __msg "$msg_account_expire_manual_edit" \ + "$( date -r 0 "+%c %Z" )" # Return to menu if either ESC or Cancel/No - f_dialog_input ret_secs "$msg" \ - "$_input" "$hline" || continue - - _input="$ret_secs" - - # Taint-check the user's input - if ! f_isinteger "${_input:-0}"; then - f_show_msg "$msg_invalid_number_of_seconds" - continue - fi + f_dialog_input __new_input \ + "$__msg" "$__input" "$__hline" || continue - f_dprintf "_input=[%s]" "$_input" + __input="$__new_input" + f_dprintf "input=[%s]" "$__input" break ;; esac done # Loop forever - pw_account_expire="$_input" - save_flag=1 - - f_dprintf "pw_account_expire: [%s]->[%s]" \ - "$cur_pw_account_expire" "$pw_account_expire" - + setvar "$__var_to_set" "$__input" return $DIALOG_OK } -# f_dialog_input_home_dir [$home_dir] +# f_dialog_input_home_dir $var_to_set [$home_dir] # -# Allow the user to enter a new home directory for a given user. If the user -# does not cancel or press ESC, the $pw_home_dir variable will hold the newly- +# Allow the user to enter a new home directory for a given login. If the user +# does not cancel or press ESC, the $var_to_set variable will hold the newly- # configured value upon return. # f_dialog_input_home_dir() { - local _input="$1" + local __var_to_set="$1" __input="$2" # Return if user has either pressed ESC or chosen Cancel/No - f_dialog_input _input "$msg_home_directory" "$_input" \ + f_dialog_input __input "$msg_home_directory" "$__input" \ "$hline_alnum_punc_tab_enter" || return $? - pw_home_dir="$_input" - save_flag=1 - - f_dprintf "pw_home_dir: [%s]->[%s]" "$cur_pw_home_dir" "$pw_home_dir" - + setvar "$__var_to_set" "$__input" return $DIALOG_OK } -# f_dialog_input_home_create +# f_dialog_input_home_create $var_to_set # -# Prompt the user to confirm creation of a given user's home directory. If the -# user does not cancel (by choosing "No") or press ESC, the $pw_home_create +# Prompt the user to confirm creation of a given login's home directory. If the +# user does not cancel (by choosing "No") or press ESC, the $var_to_set # variable will hold $msg_yes upon return, otherwise $msg_no. Use these return # variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible. # f_dialog_input_home_create() { - local retval + local __var_to_set="$1" f_dialog_yesno "$msg_create_home_directory" - retval=$? + local __retval=$? - if [ $retval -eq $DIALOG_OK ]; then - pw_home_create="$msg_yes" + if [ $__retval -eq $DIALOG_OK ]; then + setvar "$__var_to_set" "$msg_yes" else - pw_home_create="$msg_no" + setvar "$__var_to_set" "$msg_no" fi - save_flag=1 - f_dprintf "pw_home_create: [%s]->[%s]" \ - "$cur_pw_home_create" "$pw_home_create" - - [ $retval -ne $DIALOG_ESC ] # return failure if user pressed ESC + [ $__retval -ne $DIALOG_ESC ] # return failure if user pressed ESC } -# f_dialog_input_group_delete +# f_dialog_input_group_delete $var_to_set [$group] # -# Prompt the user to confirm deletion of a given user's primary group. If the -# user does not cancel (by choosing "No") or press ESC, the $pw_group_delete +# Prompt the user to confirm deletion of a given login's primary group. If the +# user does not cancel (by choosing "No") or press ESC, the $var_to_set # variable will hold $msg_yes upon return, otherwise $msg_no. Use these return # variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible. # f_dialog_input_group_delete() { - local retval + local __var_to_set="$1" __group="$2" - if f_isinteger "$pw_gid"; then - if [ $pw_gid -lt 1000 ]; then + if f_isinteger "$__group"; then + if [ $__group -lt 1000 ]; then f_dialog_noyes "$msg_delete_primary_group" else f_dialog_yesno "$msg_delete_primary_group" fi - elif [ "$pw_gid" ]; then - local gid=0 - gid=$( pw groupshow "$pw_gid" | awk -F: '{print $3}' ) - if f_isinteger "$gid" && [ $gid -lt 1000 ]; then + elif [ "$__group" ]; then + local __gid=0 + __gid=$( pw groupshow "$__group" | awk -F: '{print $3}' ) + if f_isinteger "$__gid" && [ $__gid -lt 1000 ]; then f_dialog_noyes "$msg_delete_primary_group" else f_dialog_yesno "$msg_delete_primary_group" @@ -891,131 +862,473 @@ f_dialog_input_group_delete() else f_dialog_yesno "$msg_delete_primary_group" fi - retval=$? + local __retval=$? - if [ $retval -eq $DIALOG_OK ]; then - pw_group_delete="$msg_yes" + if [ $__retval -eq $DIALOG_OK ]; then + setvar "$__var_to_set" "$msg_yes" else - pw_group_delete="$msg_no" + setvar "$__var_to_set" "$msg_no" fi - save_flag=1 - - f_dprintf "pw_group_delete: [%s]->[%s]" \ - "$cur_pw_group_delete" "$pw_group_delete" - [ $retval -ne $DIALOG_ESC ] # return failure if user pressed ESC + [ $__retval -ne $DIALOG_ESC ] # return failure if user pressed ESC } -# f_dialog_input_home_delete +# f_dialog_input_home_delete $var_to_set # -# Prompt the user to confirm deletion of a given user's home directory. If the -# user does not cancel (by choosing "No") or press ESC, the $pw_home_delete +# Prompt the user to confirm deletion of a given login's home directory. If the +# user does not cancel (by choosing "No") or press ESC, the $var_to_set # variable will hold $msg_yes upon return, otherwise $msg_no. Use these return # variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible. # f_dialog_input_home_delete() { - local retval + local __var_to_set="$1" f_dialog_yesno "$msg_delete_home_directory" - retval=$? + local __retval=$? - if [ $retval -eq $DIALOG_OK ]; then - pw_home_delete="$msg_yes" + if [ $__retval -eq $DIALOG_OK ]; then + setvar "$__var_to_set" "$msg_yes" else - pw_home_delete="$msg_no" + setvar "$__var_to_set" "$msg_no" fi - save_flag=1 - f_dprintf "pw_home_delete: [%s]->[%s]" \ - "$cur_pw_home_delete" "$pw_home_delete" - - [ $retval -ne $DIALOG_ESC ] # return failure if user pressed ESC + [ $__retval -ne $DIALOG_ESC ] # return failure if user pressed ESC } -# f_dialog_input_dotfiles_create +# f_dialog_input_dotfiles_create $var_to_set # -# Prompt the user to confirm population of a given user's home directory with +# Prompt the user to confirm population of a given login's home directory with # sample dotfiles. If the user does not cancel (by choosing "No") or press ESC, -# the $pw_dotfiles_create variable will hold $msg_yes upon return, otherwise -# $msg_no. Use these return variables ($msg_yes and $msg_no) for comparison to -# be i18n-compatible. +# the $var_to_set variable will hold $msg_yes upon return, otherwise $msg_no. +# Use these return variables ($msg_yes and $msg_no) for comparison to be i18n- +# compatible. # f_dialog_input_dotfiles_create() { - local retval + local __var_to_set="$1" f_dialog_yesno "$msg_create_dotfiles" - retval=$? + local __retval=$? - if [ $retval -eq $DIALOG_OK ]; then - pw_dotfiles_create="$msg_yes" + if [ $__retval -eq $DIALOG_OK ]; then + setvar "$__var_to_set" "$msg_yes" else - pw_dotfiles_create="$msg_no" + setvar "$__var_to_set" "$msg_no" fi - save_flag=1 - - f_dprintf "pw_dotfiles_create: [%s]->[%s]" \ - "$cur_pw_dotfiles_create" "$pw_dotfiles_create" - [ $retval -ne $DIALOG_ESC ] # return failure if user pressed ESC + [ $__retval -ne $DIALOG_ESC ] # return failure if user pressed ESC } -# f_dialog_input_shell [$shell] -# -# Allow the user to select a new login shell for a given user. If the user does -# not cancel or press ESC, the $pw_home_dir variable will hold the newly- -# configured value upon return. +# f_dialog_input_shell $var_to_set [$shell] # +# Allow the user to select a new shell for a given login. If the user does not +# cancel or press ESC, the $var_to_set variable will hold the newly-configured +# value upon return. # f_dialog_input_shell() { - local _input="$1" - local prompt="$msg_select_login_shell" - local radio_list= # Calculated below - local hline="$hline_arrows_space_tab_enter" + local __funcname=f_dialog_input_shell + local __var_to_set="$1" __input="$2" + local __prompt="$msg_select_login_shell" + local __radio_list= # Calculated below + local __defaultitem="$2" + local __hline="$hline_arrows_space_tab_enter" # # Generate the radiolist of shells # - local shell - for shell in $( awk '!/^[[:space:]]*(#|$)/{print}' "$ETC_SHELLS" ); do - # Format of a radiolist menu entry is "tag item status" - if [ "$shell" = "$_input" ]; then - radio_list="$radio_list '$shell' '' 'on'" + local __shell_list __s __shell __length=0 + f_eval_catch -k __shell_list $__funcname awk "awk '%s' \"%s\"" \ + '!/^[[:space:]]*(#|$)/{print}' "$ETC_SHELLS" || return $FAILURE + while [ $__length -ne ${#__shell_list} ]; do + __s="${__shell_list%%$NL*}" # First line + f_shell_escape "$__s" __shell + + # Format of a radiolist entry: tag item status + if [ "$__s" = "$__input" ]; then + __radio_list="$__radio_list '$__shell' '' 'on'" else - radio_list="$radio_list '$shell' '' 'off'" + __radio_list="$__radio_list '$__shell' '' 'off'" fi + + __length=${#__shell_list} + __shell_list="${__shell_list#*$NL}" # Kill line done - local height width rows - eval f_dialog_radiolist_size height width rows \ + local __height __width __rows + eval f_dialog_radiolist_size __height __width __rows \ \"\$DIALOG_TITLE\" \ \"\$DIALOG_BACKTITLE\" \ - \"\$prompt\" \ - \"\$hline\" \ - $radio_list + \"\$__prompt\" \ + \"\$__hline\" \ + $__radio_list - _input=$( eval $DIALOG \ + __input=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ + --hline \"\$__hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ - --radiolist \"\$prompt\" \ - $height $width $rows \ - $radio_list \ + --default-item \"\$__defaultitem\" \ + --radiolist \"\$__prompt\" \ + $__height $__width $__rows \ + $__radio_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) || return $? # Return if user either pressed ESC or chose Cancel/No - f_dialog_data_sanitize _input + f_dialog_data_sanitize __input - pw_shell="$_input" - save_flag=1 + setvar "$__var_to_set" "$__input" + return $DIALOG_OK +} - f_dprintf "pw_shell: [%s]->[%s]" "$cur_pw_shell" "$pw_shell" +# f_dialog_menu_user_add [$defaultitem] +# +# Present a menu detailing the properties of a login that is about to be added. +# The user's menu choice is available using f_dialog_menutag_fetch(). Returns +# success unless the user chose Cancel or pressed ESC. Data to display is taken +# from environment variables user_account_expire, user_class, +# user_dotfiles_create, user_gecos, user_gid, user_home_create, user_home_dir, +# user_member_groups, user_name, user_password_expire, user_shell, and +# user_uid. If $defaultitem is present and non-NULL, initially highlight the +# item in the menu. +# +f_dialog_menu_user_add() +{ + local funcname=f_dialog_menu_user_add + local prompt="$msg_save_exit_or_cancel" + local menu_list # Calculated below + local defaultitem="$1" + local hline="$hline_arrows_tab_enter" - return $DIALOG_OK + # Attempt to convert numeric UNIX time to calendar date/time + local user_account_expires_on= + if f_isinteger "$user_account_expire"; then + [ "$user_account_expire" -ne 0 ] && user_account_expires_on=$( + date -r "$user_account_expire" "+%F %T %Z" + ) + else + user_account_expires_on="$user_account_expire" + fi + local user_password_expires_on= + if f_isinteger "$user_password_expire"; then + [ $user_password_expire -ne 0 ] && user_password_expires_on=$( + date -r "$user_password_expire" "+%F %T %Z" + ) + else + user_password_expires_on="$user_password_expire" + fi + + # Attempt to translate a numeric GID into `number (name)' + if f_isinteger "$user_gid"; then + local user_group + user_group=$( pw groupshow -g "$user_gid" 2> /dev/null ) && + user_gid="$user_gid (${user_group%%:*})" + fi + + # Localize potentially hostile variables and escape their values + # to the local variable (see f_shell_escape() of `strings.subr') + local var + for var in account_expires_on class dotfiles_create gecos gid \ + home_create home_dir member_groups name password_expires_on \ + shell uid \ + ; do + local _user_$var + eval f_shell_escape \"\$user_$var\" _user_$var + done + + menu_list=" + 'X' '$msg_add/$msg_exit' + '1' '$msg_login: $_user_name' + '2' '$msg_full_name: $_user_gecos' + '3' '$msg_password: -----' + '4' '$msg_user_id: $_user_uid' + '5' '$msg_group_id: $_user_gid' + '6' '$msg_member_of_groups: $_user_member_groups' + '7' '$msg_login_class: $_user_class' + '8' '$msg_password_expires_on: $_user_password_expires_on' + '9' '$msg_account_expires_on: $_user_account_expires_on' + 'A' '$msg_home_directory: $_user_home_dir' + 'B' '$msg_shell: $_user_shell' + " # END-QUOTE + case "$user_home_dir" in + /|/nonexistent|/var/empty) menu_list="$menu_list + '-' '$msg_create_home_directory: $msg_n_a' + '-' '$msg_create_dotfiles: $msg_n_a' + " # END-QUOTE + ;; + *) if [ -d "$user_home_dir" ]; then menu_list="$menu_list + '-' '$msg_create_home_directory: $msg_n_a' + 'D' '$msg_create_dotfiles: ${_user_dotfiles_create:-$msg_no}' + " # END-QUOTE + else menu_list="$menu_list + 'C' '$msg_create_home_directory: ${_user_home_create:-$msg_no}' + 'D' '$msg_create_dotfiles: ${_user_dotfiles_create:-$msg_no}' + " # END-QUOTE + fi + esac + + local height width rows + eval f_dialog_menu_size height width rows \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list + + local menu_choice + menu_choice=$( eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --default-item \"\$defaultitem\" \ + --keep-tite \ + --menu \"\$prompt\" \ + $height $width $rows \ + $menu_list \ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + f_dialog_data_sanitize menu_choice + f_dialog_menutag_store "$menu_choice" + return $retval +} + +# f_dialog_menu_user_delete $user [$defaultitem] +# +# Present a menu detailing the properties of a login that is about to be +# deleted. The user's menu choice is available using f_dialog_menutag_fetch(). +# Returns success unless the user chose Cancel or pressed ESC. Data to display +# is populated automatically from the system accounting database for the given +# $user argument with the exception of two environment variables: +# user_group_delete and user_home_delete. If $defaultitem is present and non- +# NULL, initially highlight the item in the menu. +# +f_dialog_menu_user_delete() +{ + local prompt="$msg_delete_exit_or_cancel" + local menu_list # Calculated below + local defaultitem="$2" + local hline="$hline_arrows_tab_enter" + + local user_name user_password user_uid user_gid user_class + local user_password_expire user_account_expire user_gecos + local user_home_dir user_shell user_member_groups + f_input_user "$1" + + # Attempt to convert numeric UNIX time to calendar date/time + local user_account_expires_on= + if f_isinteger "$user_account_expire"; then + [ "$user_account_expire" -ne 0 ] && user_account_expires_on=$( + date -r "$user_account_expire" "+%F %T %Z" + ) + else + user_account_expires_on="$user_account_expire" + fi + local user_password_expires_on= + if f_isinteger "$user_password_expire"; then + [ $user_password_expire -ne 0 ] && user_password_expires_on=$( + date -r "$user_password_expire" "+%F %T %Z" + ) + else + user_password_expires_on="$user_password_expire" + fi + + # Attempt to translate a numeric GID into `number (name)' + if f_isinteger "$user_gid"; then + local user_group + user_group=$( pw groupshow -g "$user_gid" 2> /dev/null ) && + user_gid="$user_gid (${user_group%%:*})" + fi + + # Localize potentially hostile variables and escape their values + # to the local variable (see f_shell_escape() of `strings.subr') + local var + for var in account_expires_on class gecos gid group_delete \ + home_delete home_dir member_groups name password_expires_on \ + shell uid \ + ; do + local _user_$var + eval f_shell_escape \"\$user_$var\" _user_$var + done + + menu_list=" + 'X' '$msg_delete/$msg_exit' + '1' '$msg_login: $_user_name' + '-' '$msg_full_name: $_user_gecos' + '-' '$msg_password: -----' + '-' '$msg_user_id: $_user_uid' + '-' '$msg_group_id: $_user_gid' + '-' '$msg_group_members: $_user_member_groups' + '-' '$msg_login_class: $_user_class' + '-' '$msg_password_expires_on: $_user_password_expires_on' + '-' '$msg_account_expires_on: $_user_account_expires_on' + '-' '$msg_home_directory: $_user_home_dir' + '-' '$msg_shell: $_user_shell' + " # END-QUOTE + if f_quietly pw groupshow -g "$user_gid"; then menu_list="$menu_list + 'C' '$msg_delete_primary_group: ${_user_group_delete:-$msg_no}' + " # END-QUOTE + else menu_list="$menu_list + '-' '$msg_delete_primary_group: $msg_n_a' + " # END-QUOTE + fi + case "$user_home_dir" in + /|/nonexistent|/var/empty) menu_list="$menu_list + '-' '$msg_delete_home_directory: $msg_n_a' + " # END-QUOTE + ;; + *) if [ -d "$user_home_dir" ]; then menu_list="$menu_list + 'D' '$msg_delete_home_directory: ${_user_home_delete:-$msg_no}' + " # END-QUOTE + else menu_list="$menu_list + '-' '$msg_delete_home_directory: $msg_n_a' + " # END-QUOTE + fi + esac + + local height width rows + eval f_dialog_menu_size height width rows \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list + + local menu_choice + menu_choice=$( eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --default-item \"\$defaultitem\" \ + --keep-tite \ + --menu \"\$prompt\" \ + $height $width $rows \ + $menu_list \ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + f_dialog_data_sanitize menu_choice + f_dialog_menutag_store "$menu_choice" + return $retval +} + +# f_dialog_menu_user_edit [$defaultitem] +# +# Present a menu detailing the properties of a login that is about to be +# modified. The user's menu choice is available using f_dialog_menutag_fetch(). +# Returns success unless the user chose Cancel or pressed ESC. Data to display +# is taken from environment variables user_account_expire, user_class, +# user_dotfiles_create, user_gecos, user_gid, user_home_create, user_home_dir, +# user_member_groups, user_name, user_password_expire, user_shell, and +# user_uid. If $defaultitem is present and non-NULL, initially highlight the +# item in the menu. +# +f_dialog_menu_user_edit() +{ + local prompt="$msg_save_exit_or_cancel" + local menu_list # Calculated below + local defaultitem="$1" + local hline="$hline_arrows_tab_enter" + + # Attempt to convert numeric UNIX time to calendar date/time + local user_account_expires_on= + if f_isinteger "$user_account_expire"; then + [ "$user_account_expire" -ne 0 ] && user_account_expires_on=$( + date -r "$user_account_expire" "+%F %T %Z" + ) + else + user_account_expires_on="$user_account_expire" + fi + local user_password_expires_on= + if f_isinteger "$user_password_expire"; then + [ $user_password_expire -ne 0 ] && user_password_expires_on=$( + date -r "$user_password_expire" "+%F %T %Z" + ) + else + user_password_expires_on="$user_password_expire" + fi + + # Attempt to translate a numeric GID into `number (name)' + if f_isinteger "$user_gid"; then + local user_group + user_group=$( pw groupshow -g "$user_gid" 2> /dev/null ) && + user_gid="$user_gid (${user_group%%:*})" + fi + + # Localize potentially hostile variables and escape their values + # to the local variable (see f_shell_escape() of `strings.subr') + local var + for var in account_expires_on class dotfiles_create gecos gid \ + home_create home_dir member_groups name password_expires_on \ + shell uid \ + ; do + local _user_$var + eval f_shell_escape \"\$user_$var\" _user_$var + done + + menu_list=" + 'X' '$msg_save/$msg_exit' + '1' '$msg_login: $_user_name' + '2' '$msg_full_name: $_user_gecos' + '3' '$msg_password: -----' + '4' '$msg_user_id: $_user_uid' + '5' '$msg_group_id: $_user_gid' + '6' '$msg_member_of_groups: $_user_member_groups' + '7' '$msg_login_class: $_user_class' + '8' '$msg_password_expires_on: $_user_password_expires_on' + '9' '$msg_account_expires_on: $_user_account_expires_on' + 'A' '$msg_home_directory: $_user_home_dir' + 'B' '$msg_shell: $_user_shell' + " # END-QUOTE + case "$user_home_dir" in + /|/nonexistent|/var/empty) menu_list="$menu_list + '-' '$msg_create_home_directory: $msg_n_a' + '-' '$msg_create_dotfiles: $msg_n_a' + " # END-QUOTE + ;; + *) if [ -d "$user_home_dir" ]; then menu_list="$menu_list + '-' '$msg_create_home_directory: $msg_n_a' + 'D' '$msg_create_dotfiles: ${_user_dotfiles_create:-$msg_no}' + " # END-QUOTE + else menu_list="$menu_list + 'C' '$msg_create_home_directory: ${_user_home_create:-$msg_no}' + 'D' '$msg_create_dotfiles: ${_user_dotfiles_create:-$msg_no}' + " # END-QUOTE + fi + esac + + local height width rows + eval f_dialog_menu_size height width rows \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list + + local menu_choice + menu_choice=$( eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --default-item \"\$defaultitem\" \ + --keep-tite \ + --menu \"\$prompt\" \ + $height $width $rows \ + $menu_list \ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + f_dialog_data_sanitize menu_choice + f_dialog_menutag_store "$menu_choice" + return $retval } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/usermgmt/useradd b/usr.sbin/bsdconfig/usermgmt/useradd index 2d6b4d0..d372be4 100755 --- a/usr.sbin/bsdconfig/usermgmt/useradd +++ b/usr.sbin/bsdconfig/usermgmt/useradd @@ -1,7 +1,7 @@ #!/bin/sh #- # Copyright (c) 2012 Ron McDowell -# Copyright (c) 2012-2013 Devin Teske +# Copyright (c) 2012-2014 Devin Teske # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -33,8 +33,11 @@ BSDCFG_SHARE="/usr/share/bsdconfig" . $BSDCFG_SHARE/common.subr || exit 1 f_dprintf "%s: loading includes..." "$0" f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/usermgmt/user.subr BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr f_index_menusel_keyword $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ipgm && pgm="${ipgm:-$pgm}" @@ -55,9 +58,19 @@ done shift $(( $OPTIND - 1 )) # -# Chain-load to userinput to centralize code and minimize duplication +# Initialize # -$BSDCFG_LIBE/$APP_DIR/userinput ${USE_XDIALOG:+-X} mode="Add" +f_dialog_title "$msg_add $msg_user" +f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" +f_mustberoot_init + +# +# Add a user +# +# NB: If given an argument on the command-line use it; otherwise fall-back to +# environment variable $user (handle $VAR_USER). +# +f_user_add ${1:+"$1"} ################################################################################ # END diff --git a/usr.sbin/bsdconfig/usermgmt/userdel b/usr.sbin/bsdconfig/usermgmt/userdel index 33aa062..9425c7b 100755 --- a/usr.sbin/bsdconfig/usermgmt/userdel +++ b/usr.sbin/bsdconfig/usermgmt/userdel @@ -1,7 +1,7 @@ #!/bin/sh #- # Copyright (c) 2012 Ron McDowell -# Copyright (c) 2012-2013 Devin Teske +# Copyright (c) 2012-2014 Devin Teske # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -34,6 +34,7 @@ BSDCFG_SHARE="/usr/share/bsdconfig" f_dprintf "%s: loading includes..." "$0" f_include $BSDCFG_SHARE/dialog.subr f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/usermgmt/user.subr f_include $BSDCFG_SHARE/usermgmt/user_input.subr BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" @@ -65,9 +66,17 @@ f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" f_mustberoot_init # +# If given a user name, operate on it and exit +# +if [ "$1" ]; then + f_user_delete "$1" + exit $SUCCESS +fi + +# # Loop until the user Exits, Cancels or presses ESC # -defaultitem="" +defaultitem= while :; do f_dialog_menu_user_list "$defaultitem" retval=$? @@ -81,8 +90,7 @@ while :; do # Anything else is a userid - $BSDCFG_LIBE/$APP_DIR/userinput \ - ${USE_XDIALOG:+-X} mode="Delete" user="$mtag" + f_user_delete "$mtag" done exit $SUCCESS diff --git a/usr.sbin/bsdconfig/usermgmt/useredit b/usr.sbin/bsdconfig/usermgmt/useredit index 30ee578..612f3a6 100755 --- a/usr.sbin/bsdconfig/usermgmt/useredit +++ b/usr.sbin/bsdconfig/usermgmt/useredit @@ -1,7 +1,7 @@ #!/bin/sh #- # Copyright (c) 2012 Ron McDowell -# Copyright (c) 2012-2013 Devin Teske +# Copyright (c) 2012-2014 Devin Teske # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -34,6 +34,7 @@ BSDCFG_SHARE="/usr/share/bsdconfig" f_dprintf "%s: loading includes..." "$0" f_include $BSDCFG_SHARE/dialog.subr f_include $BSDCFG_SHARE/mustberoot.subr +f_include $BSDCFG_SHARE/usermgmt/user.subr f_include $BSDCFG_SHARE/usermgmt/user_input.subr BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" @@ -65,9 +66,17 @@ f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" f_mustberoot_init # -# Loop until the user Exits, Cancels or presses ESC +# If given a user name, operate on it and exit # -defaultitem="" +if [ "$1" ]; then + f_user_edit "$1" + exit $SUCCESS +fi + +# +# Present a list of users and loop until user Exits, Cancels or presses ESC +# +defaultitem= while :; do f_dialog_menu_user_list "$defaultitem" retval=$? @@ -81,8 +90,7 @@ while :; do # Anything else is a userid - $BSDCFG_LIBE/$APP_DIR/userinput \ - ${USE_XDIALOG:+-X} mode="Edit/View" user="$mtag" + f_user_edit "$mtag" done exit $SUCCESS diff --git a/usr.sbin/bsdconfig/usermgmt/userinput b/usr.sbin/bsdconfig/usermgmt/userinput deleted file mode 100755 index 5cf5554..0000000 --- a/usr.sbin/bsdconfig/usermgmt/userinput +++ /dev/null @@ -1,508 +0,0 @@ -#!/bin/sh -#- -# Copyright (c) 2012 Ron McDowell -# Copyright (c) 2012-2013 Devin Teske -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# -############################################################ INCLUDES - -BSDCFG_SHARE="/usr/share/bsdconfig" -. $BSDCFG_SHARE/common.subr || exit 1 -f_dprintf "%s: loading includes..." "$0" -f_include $BSDCFG_SHARE/dialog.subr -f_include $BSDCFG_SHARE/mustberoot.subr -f_include $BSDCFG_SHARE/usermgmt/user_input.subr - -BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt" -f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr - -f_index_menusel_keyword $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ipgm && - pgm="${ipgm:-$pgm}" - -############################################################ CONFIGURATION - -# set some reasonable defaults if /etc/adduser.conf does not exist. -[ -f /etc/adduser.conf ] && f_include /etc/adduser.conf -: ${passwdtype:="yes"} -: ${homeprefix:="/home"} -: ${defaultshell:="/bin/sh"} -: ${udotdir:="/usr/share/skel"} - -############################################################ FUNCTIONS - -# copy_dotfiles -# -# Copy `skel' dot-files to a new home directory. -# -copy_dotfiles() -{ - ( # Operate within sub-shell to protect CWD/glob of parent - cd "$udotdir" || exit $? - set +f # glob - for file in dot.*; do - cp -n "$file" "$pw_home_dir/${file#dot}" || exit $? - done - ) -} - -# save_changes -# -# Save any/all settings (actions performed depend on $mode value). -# -save_changes() -{ - local funcname=save_changes - - case "$mode" in - Delete) - f_eval_catch $funcname pw 'pw userdel -u "%s"' "$pw_uid" || - return $? - f_show_msg "$msg_login_deleted" - - if [ "$pw_group_delete" = "$msg_yes" ] && - f_quietly pw groupshow -g "$pw_gid" - then - f_eval_catch $funcname pw \ - 'pw groupdel -g "%s"' "$pw_gid" - fi - - if [ "$pw_home_delete" = "$msg_yes" ]; then - f_dialog_info "$msg_deleting_home_directory" - f_eval_catch $funcname rm 'rm -Rf "%s"' "$pw_home_dir" - fi - ;; - Add) - local cmd="pw useradd -n '$pw_name'" - [ "$pw_member_groups" ] && cmd="$cmd -G '$pw_member_groups'" - [ "$pw_class" ] && cmd="$cmd -L '$pw_class'" - [ "$pw_gecos" ] && cmd="$cmd -c '$pw_gecos'" - [ "$pw_home_dir" ] && cmd="$cmd -d '$pw_home_dir'" - [ "$pw_account_expire" ] && cmd="$cmd -e '$pw_account_expire'" - [ "$pw_gid" ] && cmd="$cmd -g '$pw_gid'" - [ "$pw_password_expire" ] && cmd="$cmd -p '$pw_password_expire'" - [ "$pw_shell" ] && cmd="$cmd -s '$pw_shell'" - [ "$pw_uid" ] && cmd="$cmd -u '$pw_uid'" - if [ "$pw_password_disable" ]; then - f_eval_catch $funcname pw '%s -h -' "$cmd" - elif [ "$pw_password" ]; then - echo "$pw_password" | - f_eval_catch $funcname pw '%s -h 0' "$cmd" - else - f_eval_catch $funcname pw '%s' "$cmd" - fi || return $? - f_show_msg "$msg_login_added" - - if [ "$pw_home_create" = "$msg_yes" ]; then - if f_eval_catch $funcname mkdir \ - 'mkdir -p "%s"' "$pw_home_dir" - then - f_eval_catch $funcname chown \ - 'chown -R "%s" "%s"' \ - "$pw_uid:$pw_gid" "$pw_home_dir" - fi - fi - - [ "$pw_dotfiles_create" = "$msg_yes" ] && - f_eval_catch $funcname copy_dotfiles copy_dotfiles - - user="$pw_name" - f_quietly pw usershow -n "$pw_name" && - mode="Edit/View" # Change mode - ;; - Edit/View) - local cmd="pw usermod -n '$pw_name'" - [ "$pw_member_groups" ] && cmd="$cmd -G '$pw_member_groups'" - [ "$pw_class" ] && cmd="$cmd -L '$pw_class'" - [ "$pw_gecos" ] && cmd="$cmd -c '$pw_gecos'" - [ "$pw_home_dir" ] && cmd="$cmd -d '$pw_home_dir'" - [ "$pw_account_expire" ] && cmd="$cmd -e '$pw_account_expire'" - [ "$pw_gid" ] && cmd="$cmd -g '$pw_gid'" - [ "$pw_password_expire" ] && cmd="$cmd -p '$pw_password_expire'" - [ "$pw_shell" ] && cmd="$cmd -s '$pw_shell'" - [ "$pw_uid" ] && cmd="$cmd -u '$pw_uid'" - if [ "$pw_password_disable" ]; then - f_eval_catch $funcname pw '%s -h -' "$cmd" - elif [ "$pw_password" ]; then - echo "$pw_password" | - f_eval_catch $funcname pw '%s -h 0' "$cmd" - else - f_eval_catch $funcname pw '%s' "$cmd" - fi || return $? - f_show_msg "$msg_login_updated" - - if [ "$pw_home_create" = "$msg_yes" ]; then - if f_eval_catch $funcname mkdir \ - 'mkdir -p "%s"' "$pw_home_dir" - then - f_eval_catch $funcname chown \ - 'chown -R "%i:%i" "%s"' \ - "$pw_uid" "$pw_gid" "$pw_home_dir" - fi - fi - - [ "$pw_dotfiles_create" = "$msg_yes" ] && - f_eval_catch $funcname copy_dotfiles copy_dotfiles - ;; - esac - - save_flag= - return $SUCCESS -} - -# dialog_title_update $mode -# -# Set the title based on the given $mode. -# -dialog_title_update() -{ - local mode="$1" - case "$mode" in - Add) f_dialog_title "$msg_add $msg_user" ;; - Edit/View) f_dialog_title "$msg_edit_view $msg_user: $user" ;; - Delete) f_dialog_title "$msg_delete $msg_user: $user" ;; - esac -} - -############################################################ MAIN - -# Incorporate rc-file if it exists -[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" - -# -# Process command-line arguments -# -while [ $# -gt 0 ]; do - key="${1%%=*}" - value="${1#*=}" - f_dprintf "key=[%s] value=[%s]" "$key" "$value" - case "$key" in - mode) mode="$value" ;; - user) user="$value" ;; - esac - shift -done -f_dprintf "mode=[%s] user=[%s]" "$mode" "$user" - -# -# Initialize -# -dialog_title_update "$mode" -f_dialog_backtitle "${ipgm:+bsdconfig }$pgm" -f_mustberoot_init -menu_text= -save_flag= -hline="$hline_arrows_tab_enter" - -if [ "$mode" = "Add" ]; then - f_dialog_input_name || exit 0 - - # - # Set some sensible defaults for account attributes - # - pw_gecos="${pw_gecos-$pw_name}" - pw_home_dir="${pw_home_dir:-$homeprefix/$pw_name}" - if [ -d "$pw_home_dir" ]; then - pw_home_create="${pw_home_create:-$msg_no}" - pw_dotfiles_create="${pw_dotfiles_create:-$msg_no}" - else - pw_home_create="${pw_home_create:-$msg_yes}" - pw_dotfiles_create="${pw_dotfiles_create:-$msg_yes}" - fi - pw_shell="${pw_shell:-$defaultshell}" - - f_dialog_noyes "$msg_use_default_values_for_all_account_details" - retval=$? - - if [ $retval -eq $DIALOG_ESC ]; then - exit $SUCCESS - elif [ $retval -ne $DIALOG_OK ]; then - # - # Ask a series of questions to pre-fill the editor screen. - # - # The defaults used in each dialog should allow the user to - # simply hit ENTER to proceed, because cancelling a single - # dialog will cause them to be returned to the main usermenu. - # - - f_dialog_input_gecos "$pw_gecos" || exit 0 - [ "$passwdtype" = "yes" ] && - { f_dialog_input_password || exit 0; } - f_dialog_input_uid || exit 0 - f_dialog_input_gid || exit 0 - f_dialog_input_member_groups || exit 0 - f_dialog_input_class || exit 0 - f_dialog_input_expire_password || exit 0 - f_dialog_input_expire_account || exit 0 - f_dialog_input_home_dir "$pw_home_dir" || exit 0 - if [ ! -d "$pw_home_dir" ]; then - f_dialog_input_home_create || exit 0 - [ "$pw_home_create" = "$msg_yes" ] && - { f_dialog_input_dotfiles_create || exit 0; } - fi - f_dialog_input_shell "$pw_shell" || exit 0 - fi -fi - -if [ "$mode" = "Edit/View" -o "$mode" = "Delete" ]; then - f_input_user "$user" || f_die 1 "$msg_login_not_found" -fi - -if [ "$mode" = "Edit/View" ]; then - [ -d "$pw_home_dir" ] || pw_home_create="$msg_no" - pw_dotfiles_create="$msg_no" -fi - -if [ "$mode" = "Delete" ]; then - f_dialog_input_group_delete || exit 0 - pw_home_delete="$msg_no" - [ -d "$pw_home_dir" ] && - { f_dialog_input_home_delete || exit 0; } -fi - -cur_pw_name="$pw_name" -cur_pw_password="$pw_password" -cur_pw_uid="$pw_uid" -cur_pw_gid="$pw_gid" -cur_pw_member_groups="$pw_member_groups" -cur_pw_class="$pw_class" -cur_pw_password_expire="$pw_password_expire" -cur_pw_account_expire="$pw_account_expire" -cur_pw_gecos="$pw_gecos" -cur_pw_home_dir="$pw_home_dir" -cur_pw_shell="$pw_shell" -cur_pw_group_delete="$pw_group_delete" -cur_pw_home_create="$pw_home_create" -cur_pw_home_delete="$pw_home_delete" -cur_pw_dotfiles_create="$pw_dotfiles_create" - -[ "$mode" = "Delete" ] && save_flag=1 - -# -# Loop until the user decides to Exit, Cancel, or presses ESC -# -while :; do - dialog_title_update "$mode" - - menu_text= - menu_exit="$msg_exit" - if [ "$save_flag" ]; then - if [ "$mode" = "Delete" ]; then - menu_exit="$msg_delete/$msg_exit" - menu_text="$msg_delete_exit_or_cancel" - else - menu_exit="$msg_save/$msg_exit" - menu_text="$msg_save_exit_or_cancel" - fi - fi - - pw_password_expires_on="$pw_password_expire" - f_isinteger "$pw_password_expire" && [ $pw_password_expire -ne 0 ] && - pw_password_expires_on=$( - date -r "$pw_password_expire" "+%F %T %Z" - ) - pw_account_expires_on="$pw_account_expire" - f_isinteger "$pw_account_expire" && [ "$pw_account_expire" -ne 0 ] && - pw_account_expires_on=$( - date -r "$pw_account_expire" "+%F %T %Z" - ) - - case "$mode" in - Delete) - menu_items=" - 'X' '$menu_exit' - '1' '$msg_login: $pw_name' - '-' '$msg_full_name: $pw_gecos' - '-' '$msg_password: -----' - '-' '$msg_user_id: $pw_uid' - '-' '$msg_group_id: $pw_gid' - '-' '$msg_member_of_groups: $pw_member_groups' - '-' '$msg_login_class: $pw_class' - '-' '$msg_password_expires_on: $pw_password_expires_on' - '-' '$msg_account_expires_on: $pw_account_expires_on' - '-' '$msg_home_directory: $pw_home_dir' - '-' '$msg_shell: $pw_shell' - " # END-QUOTE - ;; - *) - menu_items=" - 'X' '$menu_exit' - '1' '$msg_login: $pw_name' - '2' '$msg_full_name: $pw_gecos' - '3' '$msg_password: -----' - '4' '$msg_user_id: $pw_uid' - '5' '$msg_group_id: $pw_gid' - '6' '$msg_member_of_groups: $pw_member_groups' - '7' '$msg_login_class: $pw_class' - '8' '$msg_password_expires_on: $pw_password_expires_on' - '9' '$msg_account_expires_on: $pw_account_expires_on' - 'A' '$msg_home_directory: $pw_home_dir' - 'B' '$msg_shell: $pw_shell' - " # END-QUOTE - esac - - case "$mode" in - Add|Edit/View) - if [ -d "$pw_home_dir" ]; then menu_items="$menu_items - '-' '$msg_create_home_directory: $msg_n_a' - 'D' '$msg_create_dotfiles: $pw_dotfiles_create' - "; else menu_items="$menu_items - 'C' '$msg_create_home_directory: $pw_home_create' - 'D' '$msg_create_dotfiles: $pw_dotfiles_create' - "; fi - ;; - Delete) - if [ -d "$pw_home_dir" ]; then menu_items="$menu_items - 'C' '$msg_delete_primary_group: $pw_group_delete' - 'D' '$msg_delete_home_directory: $pw_home_delete' - "; else menu_items="$menu_items - 'C' '$msg_delete_primary_group: $pw_group_delete' - '-' '$msg_delete_home_directory: $msg_n_a' - "; fi - ;; - esac - - eval f_dialog_menu_size height width rows \ - \"\$DIALOG_TITLE\" \ - \"\$DIALOG_BACKTITLE\" \ - \"\$menu_text\" \ - \"\$hline\" \ - $menu_items - - f_dialog_default_fetch defaultitem - mtag=$( eval $DIALOG \ - --title \"\$DIALOG_TITLE\" \ - --backtitle \"\$DIALOG_BACKTITLE\" \ - --hline \"\$hline\" \ - --ok-label \"\$msg_ok\" \ - --cancel-label \"\$msg_cancel\" \ - --default-item \"\$defaultitem\" \ - --menu \"\$menu_text\" \ - $height $width $rows \ - $menu_items \ - 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD - ) - retval=$? - f_dialog_data_sanitize mtag - f_dialog_default_store "$mtag" - f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" - - # Exit if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $DIALOG_OK ] || f_die - - case "$mtag" in - X) # Exit - if [ "$save_flag" ]; then - save_changes || continue - fi - break - ;; - 1) # Login - case "$mode" in - Add) f_dialog_input_name "$pw_name" ;; - Edit/View|Delete) - f_dialog_menu_user_list "$pw_name" - retval=$? - f_dialog_menutag_fetch mtag - f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" - - # Loop if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $DIALOG_OK ] || continue - - [ "$mtag" = "X $msg_exit" ] && continue - - user="$mtag" - f_input_user "$user" || f_die 1 "$msg_login_not_found" - cur_pw_name="$pw_name" - cur_pw_password="$pw_password" - cur_pw_uid="$pw_uid" - cur_pw_gid="$pw_gid" - cur_pw_member_groups="$pw_member_groups" - cur_pw_class="$pw_class" - cur_pw_password_expire="$pw_password_expire" - cur_pw_account_expire="$pw_account_expire" - cur_pw_gecos="$pw_gecos" - cur_pw_home_dir="$pw_home_dir" - cur_pw_shell="$pw_shell" - cur_pw_group_delete="$pw_group_delete" - cur_pw_home_create="$pw_home_create" - cur_pw_home_delete="$pw_home_delete" - cur_pw_dotfiles_create="$pw_dotfiles_create" - [ "$mode" != "Delete" ] && save_flag= - esac - ;; - 2) # Full Name - f_dialog_input_gecos "$pw_gecos" ;; - 3) # Password - f_dialog_input_password ;; - 4) # UID - f_dialog_input_uid "$pw_uid" ;; - 5) # Default Group - f_dialog_input_gid "$pw_gid" ;; - 6) # Member of Groups - f_dialog_input_member_groups "$pw_member_groups" ;; - 7) # Login Class - f_dialog_input_class "$pw_class" ;; - 8) # Password Expire on - f_dialog_input_expire_password "$pw_password_expire" ;; - 9) # Account Expire on - f_dialog_input_expire_account "$pw_account_expire" ;; - A) # Home Directory - f_dialog_input_home_dir "$pw_home_dir" ;; - B) # Shell - f_dialog_input_shell "$pw_shell" ;; - esac - - case "$mode" in - Delete) - case "$mtag" in - C) # Delete Primary Group - f_dialog_input_group_delete ;; - D) # Delete Home Directory - f_dialog_input_home_delete ;; - esac - ;; - Add|Edit/View) - case "$mtag" in - C) # Create Home Directory - f_dialog_input_home_create - [ "$pw_home_create" = "$msg_no" ] && - pw_dotfiles_create="$msg_no" - ;; - D) # Create Dotfiles - f_dialog_input_dotfiles_create - [ "$pw_dotfiles_create" = "$msg_yes" ] && - pw_home_create="$msg_yes" - ;; - esac - ;; - esac -done - -exit $SUCCESS - -################################################################################ -# END -################################################################################ -- cgit v1.1