#!/bin/sh # # This is a shell library containing utility functions the scripts in the # desktop-profiles package. This library currently contains the following # functions (see the comments with each function for more info): # - test_requirement: takes a requirement and (optionally) a username, # exit code indicates wether the requirement is met # - test_profile_requirements: takes a profile's line (from the .listing # file), and (optionally) a username, exit # code indicates wether the requirements are # met # - for_each_requirement: first argument is a list of requirements, second # argument is a command to be executed once for # each requirement (with requirement as argument) # - list_listings: returns a space separated list of all .listing files # found in the directories contained in $LISTINGS_DIRS # - filter_listings: returns matching profiles from the available listing # files (output influenced by a number of environment # variables, see below for a list) # # See desktop-profiles (7) for more information about using profiles through # desktop-profiles and the format of the .listing files # # (c) 2004 Bart Cornelis ############################################################################### ############################################################################### # test_requirement () - test wether the given requirement is fulfilled for a # given user # # Note: won't work for not-current-user when command-requirments depend on # the user's environment settings. # # $1 = requirement # $2 = username (defaults to current user if absent) # # Each requirement is of one of the following forms: # requirement | meaning # -------------------------- # | $USER is a member of # ! | $USER must not be member of group # ! | always false (i.e. deactivate profile) # $(command) | (shell) command exits succesfully # # returns succesfully ($?=0) if requirement is met # returns 1 otherwise ############################################################################### test_requirement(){ # if no requirement (given) -> then it's met if (test "$1"x = x); then exit; fi; # initialize needed variables. Do not give argument to groups when looking up the current # user, to avoid expensive lookup with LDAP NSS. if (test "$2"x = x) || (test "$USER" = "$2") ; then OUR_USER="$USER" OUR_GROUPS="`id -Gn`" else OUR_USER="$2" OUR_GROUPS="`id -Gn -- $OUR_USER`" fi # !... requirement if (echo "$1" | grep '^!' > /dev/null) ; then GROUP=`echo "$1" | sed 's/^!//'`; # deactivated profile if (test "$GROUP"x = x); then exit 1; fi; # user is not a member of given group if (echo $OUR_GROUPS | grep -v $GROUP > /dev/null); then exit;# success fi; # given command must exit succesfully elif (echo "$1" | grep '^\$(.*)' > /dev/null); then COMMAND="`echo "$1" | sed -e 's/^\$(//' -e 's/)$//'`"; sh -c "$COMMAND" > /dev/null; exit $?; # user is a member of given group else if (echo $OUR_GROUPS | grep $1 > /dev/null); then exit;# success fi; fi; # if we get here the requirement was not met exit 1; } ################################################################ # $1 = list of requirements # $2 = '$2 $REQUIREMENT' will be executed for each REQUIREMENT # $3- ... = extra arguments to pass to each $2 call ################################################################ for_each_requirement(){ PROFILE_REQUIREMENTS="$1";shift COMMAND="$1";shift EXTRA_ARGS="$@"; # requirements -> check one by one while (test "$PROFILE_REQUIREMENTS"x != x); do # attempt to get first (remaining) REQUIREMENT C=1; REQUIREMENT=`echo "$PROFILE_REQUIREMENTS" | cut --fields 1 --delimiter " "`; # if command requirement if (echo "$REQUIREMENT" | grep "^\$(" > /dev/null); then #make sure we have the whole command (with params) while (echo "$REQUIREMENT" | grep -v ')$' > /dev/null); do C=`expr $C + 1`; REQUIREMENT=`echo $PROFILE_REQUIREMENTS | cut --fields -$C --delimiter " "`; done; # prepare loop for next iteration C=`expr $C + 1`; PROFILE_REQUIREMENTS=`echo $PROFILE_REQUIREMENTS | cut --fields $C- --delimiter " " `; else # prepare loop for next iteration PROFILE_REQUIREMENTS=`echo $PROFILE_REQUIREMENTS | sed -e "s/^$REQUIREMENT//" -e "s/^ *//"`; fi; "$COMMAND" "$REQUIREMENT" "$EXTRA_ARGS" done; } ############################################################################### # test_profile_requirements() - test wether the given profile's requirements # are met for a given user. # # Note: won't work for not-current-user when command-requirments depend on # the user's environment settings. # # $1 = the profile line from the listing file # $2 = username (defaults to current user if absent) # # returns succesfully ($?=0) if requirement is met # returns 1 otherwise ############################################################################### test_profile_requirements() { PROFILE_REQUIREMENTS="$(echo $@ | cut --fields 5 --delimiter ";")"; # no requirements -> met if (test "$PROFILE_REQUIREMENTS"x = x); then exit; fi; # requirements -> check one by one for_each_requirement "$PROFILE_REQUIREMENTS" test_requirement $2; # all requirements are met (or we wouldn't get here) exit; } ################################################################################ # outputs a space separated list of all .listing files found in the directories # contained in $LISTINGS_DIRS ################################################################################ list_listings () { # Make sure the variable we need are initialized LISTINGS_DIRS=${LISTINGS_DIRS:-'/etc/desktop-profiles'} for DIR in $LISTINGS_DIRS; do echo -n $(ls -1 $DIR/*.listing); echo -n " "; done; echo; } ############################################################################### # filter_listings() - filters the profiles in the .listing files # (criteria and output-format are set through a number of # environment variables, as listed below) # # The following environment variables _may_ be used: # - NAME_FILTER, LOCATION_FILTER, REQUIREMENT_FILTER, # KIND_FILTER, and DESCRIPTION_FILTER: contain the regexp filter to be used # on the corresponding field of the profile-line # - PRECEDENCE_FILTER contains the second half of an expression to be passed to # the test program (e.g. '-gt 0', '-ge 0', or '-lt 50') # - OUR_USER: requirements need to be met for this user # - FORMAT: don't just echo the profile-line from the .listing file, but use # the specified format (may use the variables NAME, LOCATION, PRECEDENCE, # REQUIREMENT, KIND, DESCRIPTION, FILE variables. First 6 refer to the # the respective fields for that profile, FILE refers to the file the profile # is listed in. # NOTE: characters interpreted specially by the shell (such as ';') need # to be escaped. # - SORT_KEY: sort on field (NOTE: numeric) # - SORT_ARGS: extra arguments to be given to the sort command (e.g. when # sorting on the precedence field (3) you probably want to set this to # '--general-numeric-sort --reverse') # - LISTINGS_DIRS: the directory containing the .listing files to include # (defaults to '/etc/desktop-profiles', probably shouldn't be changed ever) # # In absence of any set variables it will just output all available profiles # sorted by name. # # The list-desktop-profile script from the desktop-profiles package offers an # example of how to use this function. ############################################################################### filter_listings () { # Make sure the variable we need are initialized SORT_KEY=${SORT_KEY:-1} SORT_ARGS=${SORT_ARGS:-''} NAME_FILTER=${NAME_FILTER:-''} LOCATION_FILTER=${LOCATION_FILTER:-''} PRECEDENCE_FILTER=${PRECEDENCE_FILTER:-''} REQUIREMENT_FILTER=${REQUIREMENT_FILTER:-''} KIND_FILTER=${KIND_FILTER:-''} DESCRIPTION_FILTER=${DESCRIPTION_FILTER:-''} OUR_USER=${OUR_USER:-''} FORMAT=${FORMAT:-'$NAME\;$KIND\;$LOCATION\;$PRECEDENCE\;$REQUIREMENTS\;$DESCRIPTION'}; LISTINGS_LIST=$(list_listings) # do the filtering cat $LISTINGS_LIST | grep -v -e "^[[:space:]]*#" -e "^[[:space:]]*$" | sort $SORT_ARGS --key="$SORT_KEY" --field-separator=';' | \ while read PROFILE; do # split fields export NAME="`echo $PROFILE | cut --delimiter ';' --fields 1`"; export KIND="`echo $PROFILE | cut --delimiter ';' --fields 2`"; export LOCATION="`echo $PROFILE | cut --delimiter ';' --fields 3`"; export PRECEDENCE="`echo $PROFILE | cut --delimiter ';' --fields 4`"; export REQUIREMENTS="`echo $PROFILE | cut --delimiter ';' --fields 5`"; export DESCRIPTION="`echo $PROFILE | cut --delimiter ';' --fields 6`"; export FILE=`grep -l "^$NAME;" $LISTINGS_LIST`; if (test "$PRECEDENCE"x = x); then #unset = lower then anything, so set to insanely low value NORM_PRECEDENCE='-999999999999999999'; else NORM_PRECEDENCE=$PRECEDENCE; fi; # if filters don't match -> go to next profile if ( (test "${NAME_FILTER:-''}" != '') && (echo "$NAME" | grep -v "$NAME_FILTER" > /dev/null) ) || ( (test "${LOCATION_FILTER:-''}" != '') && (echo "$LOCATION" | grep -v "$LOCATION_FILTER" > /dev/null) ) || ( (test "${PRECEDENCE_FILTER:-''}" != '') && !(test "$NORM_PRECEDENCE" $PRECEDENCE_FILTER) ) || ( (test "${REQUIREMENT_FILTER:-''}" != '') && (echo "$REQUIREMENTS" | grep -v "$REQUIREMENT_FILTER" > /dev/null) ) || ( (test "${KIND_FILTER:-''}" != '') && (echo "$KIND" | grep -v "$KIND_FILTER" > /dev/null) ) || ( (test "${DESCRIPTION_FILTER:-''}" != '') && (echo "$DESCRIPTION" | grep -v "$DESCRIPTION_FILTER" > /dev/null) ); then continue; fi; # if we have a username to match for, and requirements are not met if (test "$OUR_USER" != '') && !(test_profile_requirements "$PROFILE" "$OUR_USER"); then # -> go to next profile continue; fi; # if we get here output the profile's information in the requested format echo $(sh -c "echo $FORMAT"); done; }