#!/bin/ksh93 # $Id$ # /usr/share/mdadm/mkconf set -eu ME="${.sh.file##*/}" MDADM='/sbin/mdadm' CONFIG='/etc/mdadm/mdadm.conf' DEBIANCONFIG='/etc/default/mdadm' function addSparegroups { [[ -z $2 ]] && return typeset -n ARRAYS=$1 typeset -a KV=( ${2//,/ } ) MS unset MD2SG ; typeset -A MD2SG typeset X LINE (( ! ${#KV[@]} )) && return for X in "${KV[@]}" ; do [[ -z $X ]] && continue MS=( ${X//:/ } ) if (( ${#MS[@]} != 2 )) || [[ ${MS:0:7} != '/dev/md' ]]; then print -u2 "W: ${ME}: Invalid spare-group value '$X' ignored!" continue fi MD2SG["${MS[0]}"]="${MS[1]}" done (( ! ${#MD2SG[@]} )) && return typeset OLDIFS="${IFS}" IFS=$'\n' MS=( ${ARRAYS} ) IFS=${OLDIFS} integer C=0 ARRAYS="" for LINE in "${MS[@]}" ; do ARRAYS+="\n${LINE}" [[ "${LINE:0:13}" != 'ARRAY /dev/md' ]] && continue X="${LINE:6}" X="${X%%+(\s*)}" [[ -n ${MD2SG["${X}"]:-} ]] && ARRAYS+="\n spare-group=${MD2SG[${X}]}" done } function doMain { # initialize config variables in case the environment leaks unset CFG ; typeset -A CFG typeset KEY VAL TXT integer GENERATE=0 FORCE=0 COUNT=0 if [[ $1 == 'generate' ]]; then GENERATE=1 elif [[ $1 == 'force-generate' ]]; then GENERATE=1 FORCE=1 fi (( GENERATE )) && [[ -n $2 ]] && CONFIG="$2" # honor MAILADDR from the environment (from postinst) [[ -n ${MDADM_MAILADDR__:-} ]] && CFG['mail']="${MDADM_MAILADDR__}" # save existing values if (( ! FORCE )) && [[ -r ${CONFIG} ]]; then while read KEY VAL; do [[ -z ${KEY} || ${KEY:0:1} == '#' ]] && continue (( COUNT++ )) [[ ${KEY} =~ ^(DEVICE|CREATE|HOMEHOST|PROGRAM|MAILADDR)$ ]] && \ CFG["${KEY}"]="${VAL}" [[ ${KEY} == 'MAILADDR' ]] && (( COUNT-- )) done < "${CONFIG}" fi if [[ -r ${DEBIANCONFIG} ]]; then while read KEY ; do [[ ${KEY:0:12} == 'SPAREGROUPS=' ]] && CFG['SG']="${KEY:12}" done < "${DEBIANCONFIG}" fi if (( GENERATE )); then # only barf if the config file specifies anything else than MAILADDR if (( COUNT )); then print -u2 "E: ${ME}: ${CONFIG} already exists." exit 255 fi [[ ! -d "${CONFIG%/*}" ]] && mkdir --parent "${CONFIG%/*}" fi [[ -z ${CFG['DEVICE']:-} ]] && CFG['DEVICE']='partitions containers' [[ -z ${CFG['CREATE']:-} ]] && \ CFG['CREATE']='owner=root group=disk mode=0660 auto=yes' [[ -z ${CFG['HOMEHOST']:-} ]] && CFG['HOMEHOST']='' [[ -z ${CFG['MAILADDR']:-} ]] && CFG['MAILADDR']='root' TXT='# mdadm.conf # # Please refer to mdadm.conf(5) for information about this file. # # by default (built-in), scan all partitions (/proc/partitions) and all # containers for MD superblocks. alternatively, specify devices to scan, using # wildcards if desired. DEVICE '"${CFG['DEVICE']}"' # auto-create devices with Debian standard permissions CREATE '"${CFG['CREATE']}"' # automatically tag new arrays as belonging to the local system HOMEHOST '"${CFG['HOMEHOST']}"' # instruct the monitoring daemon where to send mail alerts MAILADDR '"${CFG['MAILADDR']}"' ' if [[ -n ${CFG['PROGRAM']:-} ]]; then TXT+=' # program to run when mdadm monitor detects potentially interesting events PROGRAM '"${CFG['PROGRAM']}"' ' fi COUNT=0 if [[ ! -r /proc/mdstat ]]; then print -u2 "W: ${ME}: MD subsystem is not loaded, thus I cannot scan for arrays." COUNT=1 elif [[ ! -r /proc/partitions ]]; then print -u2 "W: ${ME}: /proc/partitions cannot be read, thus I cannot scan for arrays." COUNT=2 else TXT+='# definitions of existing MD arrays\n' # needed to keep/honor DEVICE settings KEY=${ mktemp --tmpdir=/tmp mkconf.XXXXXX; } if [[ -z ${KEY} ]]; then print -u2 "E: ${ME}: Unable to create tempfile in /tmp/." COUNT=3 else print "${TXT}" >>"${KEY}" VAL=${ ${MDADM} --examine --scan --config=${KEY} || COUNT=$? ; } if (( COUNT )) then (( COUNT+=128 )) print -u2 "W: ${ME}: failed to scan for partitions." else if [[ -z ${VAL} ]]; then print -u2 "W: ${ME}: No arrays found!" else addSparegroups VAL "${CFG['SG']:-}" TXT+="${VAL}\n" fi if (( GENERATE )); then TXT+="# Auto-generated on ${ uname -n; } on ${ date -R;}\n" TXT+="# by ${ME} "'$Id$' fi fi rm -f "${KEY}" fi fi # finally write the config if (( DRY )); then print "${TXT}" else print "${TXT}" >"${CONFIG}" || COUNT=4 fi if (( COUNT )); then print "\n${CONFIG} would look like this:\n" print "${TXT}" elif (( GENERATE && ! DRY )); then KEY='/var/lib/mdadm' [[ ! -d ${KEY} ]] && mkdir -p "${KEY}" md5sum "${CONFIG}" > "${KEY}/mdadm.conf-generated" fi } function showUsage { getopts -a "${ME}" "${ print ${USAGE}; }" OPT --man } USAGE='[-?$Id$ ] [-copyright?Copyright (c) 2014 Jens Elkner. All rights reserved.] [-license?CDDL 1.0] [+NAME?'"${ME}"' - create a valid mdadm.conf] [+DESCRIPTION?This script can be used on \bLinux\b to generate valid \bmdadm\b(8) configuration file (default: \b'"${CONFIG}"'\b). Also \bupdate-initramfs\b(8) invokes this file to create or update the default configuration file unless it already exists and contains at least one \bARRAY\b definition.] [+?If the debian configuration file \b'"${DEBIANCONFIG}"'\b exists, it gets parsed for a \bSPAREGROUPS=\b\aVALUE\a definition. If found, the generated \bARRAY\b definitions are supplemented with the corresponding \bspare-group\b settings. \aVALUE\a is a comma separated list of \amd_dev\a\b:\b\asparegroup\a, whereby \amd_dev\a is the related \b/dev/md/\b\aN\a node (e.g. /dev/md/1) and \asparegroup\a the name of the spare-group to use for this array (whitespaces are not allowed here). E.g. SPAREGROUPS=/dev/md/2:big,/dev/md/5:big,/dev/md/3=small,/dev/md/4:small] [+?If \bgenerate\b or \bforce-generate\b is given, a MD5 checksum gets generated for the new file and will be stored in \b/var/lib/mdadm/\b. The difference between both is, that \bforce-generate\b will ignore the current config file copletely and generate it from the scratch using "\bpartitions container\b" as config parameter for \bmdadm\b(8).] [+?If \afile\a is given, this one would be used instead of the default configuration file.] [+FILES?]{ [+'"${CONFIG}"'?The default \bmdadm\b(8) configuration file.] [+'"${DEBIANCONFIG}"'?The debian configuration file used to obtain \bSPAREGROUPS\b definitions.] [+/usr/share/mdadm/mkconf?The script used by \bupdate-initramfs\b(8) to update the default configuration file.] } [+SEE ALSO?\bmdadm.conf\b(5), \bmdadm\b(8).] [+NOTE?This script ignores all \bmdadm.conf\b(5) keywords from the current configuration file except \bDEVICE\b, \bCREATE\b, \bHOMEHOST\b, \bPROGRAM\b and \bMAILADDR\b, i.e. other settings will be lost!] [+?If the user executing this script has no read privilege for the corresponding raw block devices, \bmdadm\b(8) cannot read the meta information from its superblock and thus will possibly not find all available arrays!] [h:help?Print this help and exit.] [F:functions?Print a list of all functions of this script.] [T:trace]:[functionList?A comma separated list of functions of this script to trace (convinience for troubleshooting).] [n:dry?Print the generated configuration to stdout, only.] \n\n[{\bgenerate\b|\bforce-generate\b} [\afile\a]] ' integer DRY=0 IDX while getopts -a "${ME}" "${ print ${USAGE}; }" OPT ; do case ${OPT} in h) showUsage ; exit 0 ;; F) typeset +f && exit 0 ;; T) typeset -ft ${OPTARG//,/ } ;; n) DRY=1 ;; esac done (( IDX=OPTIND-1 )) shift $IDX doMain "$@"