#!/bin/ksh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # Description: Script to generate the Solaris printmgr 'ppdcache' file from the # ppd files installed in the given ppd database directory # # ppdmgr -a <ppd_filename_path> [ -L <label> ] [-w] # ppdmgr -g <ppd_filename_path> [ -L <label> ] [ -R <ppd_repository> ] # ppdmgr -r [ -L <label> ] [ -R <ppd_repository> ] # ppdmgr -u [ -L <label> ] [ -R <ppd_repository> ] # # Options: # -a <ppd_filename_path> - Add a new PPD file to the specified # label in the "user" repository, and # updates to the "user" repository # in the ppdcache. # -g <ppd_filename_path> - Generate a cache file entry # for the specified PPD file # on standard out. # -L <label> - Label name. <label> # can be any characters from the # portable character set, however # may not contain a semi-colon (':'). # The following are the defaults # for <label> for each option: # OPTION DEFAULT LABEL # ------ ------------- # -a <label> from <ppd_filename_path> # if <ppd_filename_path> # is from a known repository, # otherwise defaults to "user". # -g <label> from <ppd_filename_path> # if <ppd_filename_path> # is from a known repository, # otherwise defaults to "user". # -r all # -u all # The following are reserved labels: # caches - may never be specified # ppdcache - may never be specified # manufaliases - may never be specified # all - applies specified # action to all labels # in a repository. # Can only be specified # with -r or -u. # SUNW* - anything starting with # SUNW is reserved for # use by Sun, but not # prohibited. # -r - Rebuild the cache information for the # specified label in the specified # repository. Similar to -u, however, # the cache file is removed to force an # update to the ppdcache. # -R <ppd_repository> - PPD repository name. # Defaults to "user". # The following are the possible # values for <ppd_repository> and # location in the system: # REP LOCATION # --- -------- # user /var/lp/ppd # admin /usr/local/share/ppd # vendor /opt/share/ppd # system /usr/share/ppd # all all repositories # # Note: When specified with the -a option # only "user" and "admin" are valid. # "vendor", "system", and "all" will be # considered reserved. # -u - Update the PPD cache information # for the specified label in the specified # repository if needed. If the cache # update was required, then the updated # cache information is reflected in # the ppdcache. # -w - Display full path of where the # ppd file is located on the system. # Only valid with -a, otherwise the # option is ignored. # # If -a, -g, -r, or -u are specified on the command line, only the last action # specified will be performed. # # Cache file entry format: # <ModifiedManufacturerName>:<Model>:<NickName>:<1284DeviceIDManufacturer>:<1284DeviceIDModel>:<FullPPDFilePath> # HP:HP DeskJet 450:Foomatic/hpijs (recommended):dj450:hp:/usr/share/ppd/HP/HP-DeskJet_450-hpijs.ppd.gz # PATH=/bin:/usr/bin:/usr/sbin export PATH set -o noclobber TEXTDOMAIN="SUNW_OST_OSCMD" export TEXTDOMAIN # # Generates debug output for calling routine. # If calling routine's name is passed in, then # will also generate the name of the calling routine. # # $1 - Name of calling routine debugger() { [[ ${debug} -eq 1 ]] || return 1 if [[ -n "${1}" ]] ; then echo "In ${1}..." 1>&2 fi return 0 } # # Set the ownership and permissions on a file. # # $1 - Mode # $2 - Owner:Group # $3 - Full path to file # set_perms() { /bin/chmod -f ${1} "${3}" >/dev/null 2>&1 /bin/chown -f ${2} "${3}" >/dev/null 2>&1 } # # Create administrator repository directories, /usr/local/share/ppd, # if needed. This is a special case a Solaris doesn't deliver # /usr/local/share and it has different permissions than the # user repository. # # $1 - destination repository name # create_adminrep_dirs() { if debugger "check_adminrep_dirs" ; then set -x fi # Only create administrator repository directories, if needed. [[ "${1}" = "${ADMIN}" ]] || return 0 # Check /usr/local/share/ppd [[ ! -d "${ADMINREP}" ]] || return 0 # Check /usr/local/share admpar=$(/bin/dirname "${ADMINREP}") if [[ ! -d "${admpar}" ]] ; then # Check /usr/local admppar=$(/bin/dirname "${admpar}") if [[ ! -d "${admppar}" ]] ; then make_dir ${DIRMODE} ${ADMINOWNER} "${admppar}" || \ return 1 fi make_dir ${DIRMODE} ${ADMINOWNER} "${admpar}" || return 1 fi make_dir ${DIRMODE} ${ADMINOWNER} ${ADMINREP} || return 1 return 0 } # # Returns full path to PPD file that was added to the system. # # $1 - Full path to source PPD file # $2 - PPD file name # $3 - Full path to repository # $4 - Repository name # $5 - Label name # # Return codes: # 0 - File successfully added # 1 - Error # 2 - Duplicate file already exists # add_ppd() { if debugger ; then set -x fi verify_ppd_file "${1}" if [[ $? -ne 0 ]] ; then gettext "invalid PPD file: ${1}" 2>/dev/null return 3 fi # The destination path can now be set dstlabelpath="${3}/${5}" dstmanufpath="${dstlabelpath}/${modmanuf}" dstpath="${dstmanufpath}/${2}" # # If a version (either compressed or not compressed) of the PPD # file exists in the destination in the label/repository, # then just return as there no work to be done. dst_copy_path=$(variant_copy "${1}" "${dstpath}" "${6}" "${ppdfname}") ap_rc=$? if [[ ${ap_rc} -ne 0 ]] ; then echo "${dst_copy_path}" return ${ap_rc} fi # # Can only add a PPD file to the "user" or "admin" repository. # Note: this check is here instead of at the top of this # function as we don't want to cause an error if a user # specifies the same repository and label as a the specified # ppd file and the repository of the specified ppd file # exists in a known repository. # if [[ "${4}" != "${USER}" && "${4}" != "${ADMIN}" ]] ; then gettext "invalid PPD file repository name: ${4}" 2>/dev/null return 3 fi # Ensure destination directories exist if ! create_adminrep_dirs ${4} ${DIRMODE} ${ADMINOWNER} || \ ! make_dir ${DIRMODE} ${DIROWNER} "${3}" || \ ! make_dir ${DIRMODE} ${DIROWNER} "${dstlabelpath}" || \ ! make_dir ${DIRMODE} ${DIROWNER} "${dstmanufpath}" ; then gettext "unable to create destination directories" 2>/dev/null return 3 fi # Copy source PPD file, and compress if needed, to destination if [[ "${ppdfileext}" = "${PEXT}" ]] ; then ${GZIP} "${1}" >"${dst_copy_path}" 2>/dev/null if [[ $? -eq 1 ]] ; then gettext "unable to copy PPD file " 2>/dev/null gettext "to destination" 2>/dev/null return 3 fi else /bin/cp -f "${1}" "${dst_copy_path}" >/dev/null 2>&1 if [[ $? -ne 0 ]] ; then gettext "unable to copy PPD file " 2>/dev/null gettext "to destination" 2>/dev/null return 3 fi fi set_perms ${FILEMODE} ${FILEOWNER} "${dst_copy_path}" echo "${dst_copy_path}" return 0 } # # Returns 0 if the cache needs to be modified, otherwise # returns 1. # # $1 - Full path to cache # $2 - Full path to cache replacement candidate # changes_in_cache() { if debugger "changes_in_cache" ; then set -x fi if [[ "${action}" = "${REBUILD}" ]] ; then return 0 fi [[ "${2}" -nt "${1}" ]] || return 1 if $(${CMP} "${1}" "${2}" >/dev/null 2>&1) ; then # No differences. Just update timestamp /bin/touch -r "${2}" "${1}" >/dev/null 2>&1 return 1 else return 0 fi } # # Generate a new golden cache file (/var/lp/ppd/ppdcache), by # concatenating and sorting all existing cache files in /var/lp/ppd/caches. # # If there are difference between the newly generated golden cache file and # the existing one (if it exists) then the newly generated one replaces the # existing one at /var/lp/ppd/ppdcache. # update_golden_cache() { if debugger "update_golden_cache" ; then set -x fi # # Remove any cache files that don't have an associated # label. # for cname in $(/bin/ls ${VARCACHES} 2>/dev/null) ; do repname="${cname%%:*}" cfile="${cname#*:}" checkdir="$(get_rep_path ${repname})/${cfile}" remove_unassociated_cache "${checkdir}" "${cname}" done # # Combine the contents of all cache files into a # temporary golden cache file. # tmpgoldencache=$ppdmgrtmpdir/tmpgoldencache /bin/sort "${VARCACHES}"/* >>"${tmpgoldencache}" 2>/dev/null if [[ ! -s "${tmpgoldencache}" ]] ; then # No cache files. Remove golden cache. /bin/rm -f "${GOLDCACHE}" >/dev/null 2>&1 /bin/rm -f "${tmpgoldencache}" >/dev/null 2>&1 elif [[ -e "${GOLDCACHE}" ]] ; then # # Use the newly generated "temporary" golden cache file if there # differences between the current and newly generated ppdcache # or if a rebuild is being performed. # if [[ "${VARCACHES}" -nt "${GOLDCACHE}" ]] || \ changes_in_cache "${GOLDCACHE}" "${tmpgoldencache}" ; then set_perms ${FILEMODE} ${FILEOWNER} "${tmpgoldencache}" /bin/mv -f "${tmpgoldencache}" \ "${GOLDCACHE}" >/dev/null 2>&1 else /bin/rm -f "${tmpgoldencache}" >/dev/null 2>&1 fi else # There wasn't an existing ppdcache. Install the newly # generated ppdcache file to the golden ppdcache. set_perms ${FILEMODE} ${FILEOWNER} "${tmpgoldencache}" /bin/mv -f "${tmpgoldencache}" "${GOLDCACHE}" >/dev/null 2>&1 fi } # # Returns a list of PPD files that exist. # # $1 - Full path to cache file # remove_invalid_cache_entries() { if debugger ; then set -x fi [[ -s "${1}" ]] || return IFS="$NoSpaceTabIFS" for centry in $(/bin/cat "${1}" 2>/dev/null) ; do IFS="$SaveIFS" # # Keep the entry from the ppd cache if it still # exists and there haven't been any modifications # since the last update to the cache. # if [[ -n "${centry}" ]] ; then ppdfile="${centry##*:}" if [[ -n "${ppdfile}" && -e "${ppdfile}" && "${1}" -nt "${ppdfile}" ]] ; then echo "${centry}" fi fi IFS="$NoSpaceTabIFS" done IFS="$SaveIFS" } # # Returns 0 if the path to the PPD is as follows: # <PPD file repository>/<label>/<manufacturer>/<PPD file> # otherwise, returns 1 # # $1 Full path to PPD file # verify_ppd_location() { if debugger ; then set -x fi # # Strip off what should be <label>/<manufacturer>/<PPD file> # and verify the PPD file repository matches one of the # known PPD file repositories. # ppd_file_repository=${1%/*/*/*} found=1 for repository in ${REPOSITORIES} ; do if [[ "${repository}" = "${ppd_file_repository}" ]] ; then found=0 break fi done return ${found} } # # Generate, and sort, cache entries for each PPD files in the specified # list to the specified file. # # $1 - List of full paths to PPD files # $2 - Full path to current cache file # $3 - Full path to label # $4 - Full path to new cache file to generate # # Return code: # 0 success # 1 unsuccessful # generate_label_cache_file() { if debugger ; then set -x fi # # Generate a cache file containing cache entries for # all files in the label. # ucfile=$ppdmgrtmpdir/unsortedcache # # Before processing new files, remove any cache entries # which may be invalid. # valid_files= if [[ -e "${2}" && "${action}" != "${REBUILD}" ]] ; then valid_files=$(remove_invalid_cache_entries "${2}") if [[ -n "${valid_files}" ]] ; then echo "${valid_files}" >>${ucfile} fi fi # # If there are no valid PPD files in the current cache file, # and there are no new PPD files to process, the only thing # left to do is to remove the current cache file. # if [[ -z "${valid_files}" && -z "${1}" ]] ; then /bin/rm -f "${2}" >/dev/null 2>&1 /bin/rm -f "${ucfile}" >/dev/null 2>&1 return 0 fi # # For each of the label's PPD files, generate # a cache file entry and add it to the cache file. # vpl_rc=0 vpf_rc=0 vpl_msg= vpf_msg= IFS="$NoSpaceTabIFS" for fname in ${1} ; do IFS="$SaveIFS" if [[ -n "${fname}" ]] ; then verify_ppd_location "${fname}" vpl_rc=$? if [[ ${vpl_rc} -ne 0 ]] ; then vpl_msg="${vpl_msg}\t${fname}\n" fi verify_ppd_file "${fname}" vpf_rc=$? if [[ ${vpf_rc} -ne 0 ]] ; then vpf_msg="${vpf_msg}\t${fname}\n" fi if [[ ${vpl_rc} -eq 0 && ${vpf_rc} -eq 0 ]] ; then echo "$(generate_cache_file_entry \ "${modmanuf}" "${model}" "${nickn}" \ "${devidmfg}" "${devidmdl}" "${fname}")" fi fi IFS="$NoSpaceTabIFS" done >>"${ucfile}" IFS="$SaveIFS" /bin/sort -u "${ucfile}" >>"${4}" 2>/dev/null /bin/rm -f "${ucfile}" >/dev/null 2>&1 [[ -n "${vpl_msg}" || -n "${vpf_msg}" ]] || return 0 if [[ -n ${vpl_msg} ]] ; then gettext " PPD file(s) not in valid location\n" 2>/dev/null gettext \ " (<repository>/<label>/<manufacturer>/<PPD file>):\n" 2>/dev/null echo "${vpl_msg}" fi if [[ -n ${vpf_msg} ]] ; then gettext " invalid PPD file(s):\n" 2>/dev/null echo "${vpf_msg}" fi return 1 } # # Update current cache file with candidate cache file if there are # differences. # # $1 - Current cache file # $2 - Candidate cache file to update # $3 - Repository name # update_current_cache_file() { if debugger "update_current_cache_file" ; then set -x fi if [[ ! -s "${2}" ]] ; then # # Candidate cache has zero size (label # directory with no PPD files under it). # Delete the empty candidate cache # file and delete the current cache # file. # /bin/rm -f "${1}" >/dev/null 2>&1 /bin/rm -f "${2}" >/dev/null 2>&1 elif [[ -e "${1}" ]] ; then # # If there are differences between the current # cache file and the newly generated one, then # replace the current one with the new one, and # set the flag to update the golden ppdcache # file. # if changes_in_cache "${1}" "${2}" ; then set_perms ${FILEMODE} ${FILEOWNER} "${2}" /bin/mv -f "${2}" "${1}" >/dev/null 2>&1 else /bin/rm -f "${2}" >/dev/null 2>&1 fi else # # There is no current cache file. Move the candidate # to the caches directory. # set_perms ${FILEMODE} ${FILEOWNER} "${2}" /bin/mv -f "${2}" "${1}" >/dev/null 2>&1 fi } # # Returns 0 if there are files in $1 with newer timestamp # than $2 or if deletions have occurred under $1, # otherwise returns 1. # # $1 - Full path to the destination label # $2 - Full path to label cache file # changes_under_label() { if debugger ; then set -x fi # First check for newer files in the directory if [[ -e "${2}" && "${action}" != "${REBUILD}" ]] ; then newfiles=$(/bin/find "${1}" -type f -newer "${2}") else newfiles=$(/bin/find "${1}" -type f) fi echo "${newfiles}" [[ -z "${newfiles}" ]] || return 0 # # Need to detect if PPD files have been deleted by checking # timestamps on label and manufacturer directories. # [[ ! "${1}" -nt "${2}" ]] || return 0 /bin/find "${1}" -type d -newer "${2}" >/dev/null 2>&1 || return 1 return 0 } # # If -R was specified, or the timestamp on the specified label's # directory or any of the PPD files under the specified label in # the specified PPD file respository is newer than the cache file # associated with the label, then generate a new sorted cache file. # # The new cache will replace the existing one (if any) only if there # are differences. Note: if -r was specified, then a new cache file # file will always be installed at # /var/lp/ppd/caches/<PPD file repository name>-<label name> # # $1 - Full path of the destination PPD file repository # $2 - Destination PPD file repository name # $3 - Destination label name # update_label_cache() { if debugger ; then set -x fi dstlabelpath="${1}/${3}" replabelcachepath="${1}/${CACHES}/${3}" varlabelcachepath="${VARCACHES}/${2}${SEP}${3}" ulc_rc=0 if [[ -d "${dstlabelpath}" ]] ; then # # If the cache doesn't exist for a label, # or if there were any changes under a label # (i.e., the timestamp on the label directory or any # of the PPD files under it is newer than the # existing cache file), then generate a new cache file. # tmpcachepath=$ppdmgrtmpdir/tmpcachepath # if this is a system repository, check for a prepopulated cache if [[ "${2}" = "${SYSTEM}" && -e ${FOOCACHEDIR}/${3}.cache ]] ; then # copy prepopulated cache /bin/cp -f ${FOOCACHEDIR}/${3}.cache ${tmpcachepath} else newfileslist=$(changes_under_label "${dstlabelpath}" \ "${varlabelcachepath}") if [[ $? -eq 0 ]] ; then err_files=$(generate_label_cache_file \ "${newfileslist}" "${varlabelcachepath}" \ "${dstlabelpath}" "${tmpcachepath}") if [[ $? -ne 0 ]] ; then # # At least one PPD file was invalid. # Don't return yet, as the cache info # for the valid PPD files can still be # used to generate a cache file. # echo "${err_files}" ulc_rc=1 fi fi fi if [[ -e "${tmpcachepath}" ]] ; then update_current_cache_file \ "${varlabelcachepath}" "${tmpcachepath}" "${2}" /bin/rm -f "${tmpcachepath}" >/dev/null 2>&1 fi else # # If there is a cache file in /var/lp/ppd/caches associated # with the label which no longer exists, remove it. # /bin/rm -f "${varlabelcachepath}" >/dev/null 2>&1 fi return ${ulc_rc} } # # Returns the alias for the specified real manufacturer's name. # # $1 - Real manufacturer's name # $2 - File containing list of files that have manufacturers aliases # manuf_name_alias() { if debugger ; then set -x fi # # Found a couple of PPD files which had special characters # in the Manufacturer name (i.e, the following is the Manufacturer # entry: # *Manufacturer: "Canon Inc. (Kosugi Offic" # We'll only search the alias file for "Canon Inc." # tmpmanuf="${1% *\(*}" # Search alias files for a match on the real manufacturer name if [[ -s "${2}" ]] ; then # # Check the manufacturer aliases file for case # insensitive match of the Manufacturer entry # from the PPD file. If a match is found, # then modify the manufacturer entry to # be that of the specified alias. # manufaliases=$(/bin/egrep -i \ "^${tmpmanuf}:|:${tmpmanuf}:|:${tmpmanuf}$" "${2}") if [[ -n "${manufaliases}" ]] ; then echo "${manufaliases%%:*}" break else echo "${tmpmanuf}" fi else echo "${tmpmanuf}" fi } # # Returns 0 if the extension to the specified PPD file is a known # extension, otherwise returns 1. # # $1 - Full path to PPD file # # Set upon return: # ppdfileext - PPD file ext (.ppd or .ppd.gz) # verify_file_ext() { if debugger ; then set -x fi if [[ "${1%.gz}".gz = "${1}" ]] ; then ppdfileext=${GEXT} elif [[ "${1%.ppd}".ppd = "${1}" ]] ; then ppdfileext=${PEXT} else # invalid PPD file name extension return 1 fi return 0 } # # Return the lines from the specified PPD file matching the specified # spec items. # # $1 - spec entries from PPD file # $2 - spec item # # $1 example - 1 string with substrings separated by newline: # *PPD-Adobe: "4.3" # *Manufacturer: "HP" # *Product: "(officejet 4200 series)" # *ModelName: "HP OfficeJet 4200" # *NickName: "HP OfficeJet 4200 Foomatic/hpijs (recommended)" # $2 example: # ^\*Manufacturer # spec_entry() { if debugger ; then set -x fi item=$(echo "${1}" | /bin/grep ${2}) # Remove everything up to and including the first quote item=${item#*\"} # Remove the end quote echo "${item%\"}" } # # Return the lines from the specified PPD file matching the specified # spec items. # # Note: this is similar to spec_entry() except the tokens in the # spec entry are different. # # $1 - spec entries from PPD file # $2 - spec item # devid_spec_entry() { if debugger ; then set -x fi item=$(echo "${1}" | /bin/grep ${2}) # Remove everything up to and including the first semi-colon item=${item#*\:} # Remove the end quote echo ${item%\;} } # # Verifies that the specified PPD file # - has a valid extension # - has the following required spec file entries: # *PPD-Adobe: "4.3" # Manufacturer # Product # ModelName # NickName # # In addition, the manufacture and model from the IEEE1284 device id # information will be gathered here, although it's not an error that # it isn't in the PPD file as many don't contain the IEEE1284 info. # # $1 - Full path to PPD file # # Return codes: # 0 success # 1 invalid PPD file # verify_ppd_file() { if debugger ; then set -x fi ADOBESPEC="PPD-Adobe" MANUF="Manufacturer" PRODUCT="Product" MODEL="ModelName" NICKNAME="NickName" DEVID="1284DeviceID" # Verify the PPD file extension verify_file_ext "${1}" || return 1 # Query for the required spec items searchentries="^\*${ADOBESPEC}:|^\*${MANUF}:|^\*${PRODUCT}:" searchentries="${searchentries}|^\*${MODEL}:|^\*${NICKNAME}:" searchentries="${searchentries}|^\*${DEVID}:" ppd_info="$(/bin/gzgrep -e "${searchentries}" "${1}")" # # Process the query results to verify each of the required spec # file items appears in the PPD file. # for spec_item in ${ADOBESPEC} ${MANUF} ${PRODUCT} ${MODEL} \ ${NICKNAME} ; do entry=$(spec_entry "${ppd_info}" "^\*${spec_item}:") [[ ! -z "${entry}" ]] || return 1 case ${spec_item} in ${MANUF}) realmanuf="${entry}" ;; ${PRODUCT}) product="${entry}" ;; ${MODEL}) model="${entry}" ;; ${NICKNAME}) # # Remove the model and any commas and spaces # which appear before the driver # nickn="${entry#$model[, ]*}" ;; esac done # Save IEEE1284 device id information if $(echo "${ppd_info}" | grep "${DEVID}" >/dev/null 2>&1) ; then DMDL="MDL" DMFG="MFG" devid="$(/bin/gzgrep -e "^[ ]*${DMDL}:|^[ ]*${DMFG}:" "${1}")" devidmdl="$(devid_spec_entry "${devid}" "${DMDL}")" devidmfg="$(devid_spec_entry "${devid}" "${DMFG}")" else devidmdl= devidmfg= fi modmanuf=$(manuf_name_alias "${realmanuf}" ${aliasfile}) return 0 } # # generate_cache_file_entry() # # Returns a cache file entry for the specified PPD file. # # $1 - modmanuf # $2 - model # $3 - nickn # $4 - devidmfg # $5 - devidmdl # $6 - Full path to the specified PPD file # generate_cache_file_entry() { if debugger "generate_cache_file_entry" ; then set -x fi echo "${1}":"${2}":"${3}":"${4}":"${5}":"${6}" } # # Expand specified file to the full path. # # $1 - File path to expand # # Return code set to 0 if expanded successfully, otherwise set to 1. # ppd_pathname() { if debugger ; then set -x fi if [[ -f "${1}" && -s "${1}" ]] ; then (cd "$(/bin/dirname "${1}")" ; \ echo "$(/bin/pwd)/$(/bin/basename "${1}")") || return 1 return 0 else return 1 fi } # # Returns the PPD repsitory path associated with the specified # PPD repository name. # # $1 - Repository name # get_rep_path() { if debugger ; then set -x fi case ${1} in ${SYSTEM}) echo "${SYSTEMREP}" ;; ${VENDOR}) echo "${VENDORREP}" ;; ${ADMIN}) echo "${ADMINREP}" ;; ${USER}) echo "${USERREP}" ;; *) echo "${UNSET}" ;; esac } # # Returns the PPD respository name from the repository path # # $1 - PPD repository path # get_rep_name() { if debugger ; then set -x fi case ${1} in ${SYSTEMREP}) echo "${SYSTEM}" ;; ${VENDORREP}) echo "${VENDOR}" ;; ${ADMINREP}) echo "${ADMIN}" ;; ${USERREP}) echo "${USER}" ;; "all") echo "all" ;; *) echo "${UNSET}" ;; esac } # # Returns 0 if a matching label name is found in the specified repository, # otherwise returns 1. # # $1 - repository path # $2 - label name # label_path_in_repository() { if debugger "label_path_in_repository" ; then set -x fi [[ "${1}" != "" && "${2}" != "" ]] || return 1 lpir_rc=1 for repository in ${REPOSITORIES} ; do if [[ "${repository}" = "${1}" && -d "${1}/${2}" ]] ; then lpir_rc=0 break fi done return ${lpir_rc} } # # Returns 0 if the source label path is the same # as the destination label path, otherwise returns 1. # # $1 - full path to source PPD file (source label path) # $2 - destination repository path # $3 - destination label name # label_path_match() { if debugger "label_path_match" ; then set -x fi # dest repository not specified if [[ "${2}" = "${UNSET}" ]] ; then # dest label not specified if [[ "${3}" = "${UNSET}" ]] ; then # # We've found a match if the label path is in a known # repository. # lpath="${1%/*/*}" label_path_in_repository \ "${1%/*/*/*}" "${lpath##*/}" || return 1 else # # If the source label path exists in the # in a known repository, and the destination # label is the same as the source label, # then we'll assume the default destination # repository is the same as the source # destination repository. # [[ "${1%/*/*}" = "${1%/*/*/*}/${3}" ]] || return 1 label_path_in_repository "${1%/*/*/*}" "${3}" || \ return 1 fi # dest repository specified, dest label not specified elif [[ "${3}" = "${UNSET}" ]] ; then # # If the destination repository path is the same as the # source repository, and if the source label exists in the # destination repository path, then we'll assume the default # destination label is the same as the source label. # [[ "${2}" = "${1%/*/*/*}" ]] || return 1 lpath="${1%/*/*}" label_path_in_repository "${2}" "${lpath##*/}" || return 1 # dest repository and dest label specified. else # # We've found a match if the destination and label # match those of the source label path, and the source # label path is in a known repository. # [[ "${1%/*/*}" = "${2}/${3}" ]] || return 1 label_path_in_repository "${2}" "${3}" || return 1 fi return 0 } # # Returns 0 if specified label name is a reserved label, otherwise # returns 1. # # $1 - label name # reserved_label() { if debugger ; then set -x fi rl_rc=1 for labelname in ${RESERVEDLABELS} ; do if [[ "${1}" = "${labelname}" ]] ; then rl_rc=0 break fi done return ${rl_rc} } # # Returns a list of all labels that exist in a repository that are # not reserved labels. # # $1 - Full path of repository # $2 - Repository name # get_rep_label_list() { if debugger ; then set -x fi # # Get a list of all labels that exist in all of the # PPD file repository. # for lname in $(/bin/ls "${1}" 2>/dev/null) ; do if [[ -d "${1}/${lname}" ]] ; then if ! reserved_label "${lname}" ; then echo "${lname} " fi fi done } # # Returns a valid PPD label. # # Verifies the specified PPD label is a valid label. If the # label is not set, then it is set to a default value. # # Return code set to 0 if the specified PPD label is valid, otherwise 1. # # $1 - PPD label # valid_specified_label() { if debugger ; then set -x fi # Verify the specified label vsl_rc=0 case "${1}" in "all") # Reserved label name with -a or -g options if [[ "${action}" = "${ADD}" || \ "${action}" = "${GENERATEENTRY}" ]] ; then print -n "$myprog: " 1>&2 gettext "reserved PPD label name: ${1}\n" 1>&2 vsl_rc=1 else echo "${1}" fi ;; "ppdcache" | "caches" | "manufaliases") # Reserved label names with any option print -n "$myprog: " 1>&2 gettext "reserved PPD label name: ${1}\n" 1>&2 vsl_rc=1 ;; "" | "${UNSET}") # Label name not specified. Set the default label name. # For -g and -a, default is "user", otherwise, default # is "all". if [[ "${action}" = "${ADD}" || \ "${action}" = "${GENERATEENTRY}" ]] ; then echo "${USER}" else echo "all" fi ;; *) # label cannot be "." or ".." if [[ "${1}" = "." || "${1}" = ".." ]] ; then print -n "$myprog: " 1>&2 gettext "PPD label name cannot be " 1>&2 gettext "\".\" or \"..\"\n" 1>&2 vsl_rc=1 fi # Label name cannot contain special characters echo "${1}" | /bin/egrep "${SPECIALCHARS}" >/dev/null if [[ $? -eq 0 ]] ; then print -n "$myprog: " 1>&2 gettext "PPD label name contains " 1>&2 gettext "an invalid character: ${1}\n" 1>&2 vsl_rc=1 else echo "${1}" fi ;; esac return ${vsl_rc} } # # Returns the full path of any variant copy of the source file in # the destination label/repository. # # $1 - Full path to source PPD file # $2 - Full path to destination PPD file # # Return code set to # 0 - Copy doesn't exist # 1 - Duplicate copy exists # 2 - Variant copy exists # variant_copy() { if debugger ; then set -x fi # # First make sure there is not a .ppd and a .ppd.gz version # of the destination file; users should know not to do this. # if [[ -e "${2%.gz}" && -e "${2%.gz}.gz" ]] ; then /bin/rm -f "${2%.gz}" >/dev/null 2>&1 fi # Use gzcmp to compare PPD files as it can deal with # gzipped or regular files. if $(${GZCMP} "${1}" "${2}"* >/dev/null 2>&1) ; then echo "${2}"* return 1 elif [[ -e "${2%.gz}" ]] ; then echo "${2%.gz}" return 2 elif [[ -e "${2%.gz}.gz" ]] ; then echo "${2%.gz}.gz" return 2 else # # A PPD file doesn't exist in the destination # repository under the destination label. # Just display the source PPD file, ensuring # it has a gzip extension as we will always # try to gzip the copy in the destination. # if [[ "${1#*.ppd}" = ".gz" ]] ; then echo "${2}" else echo "${2}.gz" fi return 0 fi } # # $1 - Directory mode # $2 - Directory owner (i.e., root:lp) # $3 - Directory to create # make_dir() { if debugger "make_dir" ; then set -x fi [[ ! -d "${3}" ]] || return 0 /bin/mkdir "${3}" >/dev/null 2>&1 || return 1 set_perms ${1} ${2} "${3}" return 0 } # # Remove a ppdmgr generated cache (in /var/lp/ppd/cache) # if it doesn't have an associated label in the repository. # # $1 - Full path to label # $2 - Cache name # remove_unassociated_cache() { if debugger "remove_unassociated_cache" ; then set -x fi if [[ "${1}" != "${UNSET}" ]] ; then if [[ -n "${1}" && ! -d "${1}" ]] ; then # # The label doesn't exist, so delete # the associated cache file. # /bin/rm -f "${VARCACHES}/${2}" >/dev/null 2>&1 fi fi } # # Sorted copies of cache files for each label in each PPD repository # are maintained in /var/lp/ppd/caches/<PPD respository>-<label>. # This is done so that changes in delivered cache files can be # detected. If a difference in cache files is detected, or a # cache file is either added or removed, then we know that # the ppdcache file needs to be updated. # # Get a list of all cache files and compare against the list # of labels in all of the PPD file repositories. They should # be the same. If there is a label in one of the PPD file # repositories that doesn't have an associated cache file, then # we don't worry about it now, as that will be resolved when # we update the cache for that label. However, if there is # a cache file associated with a label that no longer exists, then # remove the cache file. # # $1 - Full path to repository (or "all") # $2 - Label name # update_cache() { if debugger ; then set -x fi # # Determine which labels in which PPD repository the # cache file will be updated for. # if [[ "${1}" = "all" ]] ; then rname="${REPOSITORIES}" else rname="${1}" fi uc_rc=0 for dstreppath in ${rname} ; do labellist= if [[ "${2}" = "all" ]] ; then dstrepname=$(get_rep_name "${dstreppath}") labellist=$(get_rep_label_list "${dstreppath}" \ "${dstrepname}") else # Ensure the label exists in the PPD file repository. if [[ -d "${dstreppath}/${2}" ]] ; then labellist="${2}" fi fi # # Update the cache for each label in the PPD repository # for dstlabel in ${labellist} ; do ulc_msg=$(update_label_cache "${dstreppath}" \ "${dstrepname}" "${dstlabel}") if [[ $? -ne 0 ]] ; then echo "${ulc_msg}" uc_rc=1 fi done done # Update the golden cache file. update_golden_cache return ${uc_rc} } # $1 - exit status ppdmgr_exit() { if debugger "ppdmgr_exit" ; then set -x fi /bin/rm -rf "${ppdmgrtmpdir}" >/dev/null 2>&1 exit ${1} } usage() { gettext "usage:\n" 1>&2 print -n "\t$myprog: " 1>&2 gettext "-a <ppd_filename_path> [ -L <label> ]\n" 1>&2 gettext "\t\t[ -R <ppd_repository> ] [-w]\n" 1>&2 print -n "\t$myprog: " 1>&2 gettext "-r [ -L <label> ] [ -R <ppd_repository> ]\n" 1>&2 print -n "\t$myprog: " 1>&2 gettext "-u [ -L <label> ] [ -R <ppd_repository> ]\n" 1>&2 ppdmgr_exit ${FAIL} } ########################################################################## # main ########################################################################## myprog=$(/bin/basename $0) SaveIFS="$IFS" NoSpaceTabIFS=' ' # Updatable PPD repository VARDIR=/var/lp/ppd # Delivered PPD respository SYSTEMREP=/usr/share/ppd ADMINREP=/usr/local/share/ppd VENDORREP=/opt/share/ppd USERREP=${VARDIR} RESERVEDREPS="${SYSTEMREP} ${ADMINREP} ${VENDORREP}" REPOSITORIES="${USERREP} ${RESERVEDREPS}" RESERVEDLABELS="all caches ppdcache manufaliases" # Directory where system:SUNWfoomatic is delivered FOOCACHEDIR=/usr/lib/lp/caches # Deliveries SYSTEM=system VENDOR=vendor ADMIN=admin USER=user # System PPD cache name used by printmgr GOLDCACHE=${USERREP}/ppdcache # Delivered caches directory CACHES=caches MANUFALIASES=manufaliases # Updated caches directory VARCACHES=${VARDIR}/${CACHES} # valid PPD file name extensions PEXT=ppd GEXT=gz FILEEXTS=".${PEXT} .${PEXT}.${GEXT}" # Default modes and owners DIRMODE=755 DIROWNER=root:lp ADMINOWNER=root:root FILEMODE=444 FILEOWNER=root:lp # ppdmgr actions ADD=add GENERATEENTRY=generateentry UPDATE=update REBUILD=rebuild SUCCESS=0 FAIL=1 WARN=2 MAXLABELNAME=256 GZIP="/bin/gzip -c" GZCMP="/bin/gzcmp -s" CMP="/bin/cmp -s" SPECIALCHARS=":" SEP=":" debug=0 wflag=0 status=${SUCCESS} UNSET="" ppdlabel=${UNSET} ppdrepname=${UNSET} ppdreppath=${UNSET} modmanuf= model= nickn= devidmdl= devidmfg= ppdmgrtmpdir=$(/usr/bin/mktemp -t -d ppdmgr.XXXXXX) if [ -z "$ppdmgrtmpdir" ] ; then print -n "$myprog: " 1>&2 gettext "Fatal error: could not create temporary directory\n" 1>&2 exit 1 fi aliasfile=${USERREP}/manufaliases tmpfilepath= OPTS=a:g:L:rR:uwZ while getopts "$OPTS" arg ; do case ${arg} in a) # add PPD file action=${ADD} origsrcppdpath=${OPTARG} ;; g) # create cache entry action=${GENERATEENTRY} origsrcppdpath=${OPTARG} ;; L) # PPD label name ppdlabel=${OPTARG} ;; r) # rebuild cache action=${REBUILD} ;; R) # PPD file repository to use ppdrepname=${OPTARG} ;; u) # update cache action=${UPDATE} ;; w) # display PPD file path wflag=1 ;; Z) # debug debug=1 ;; ?) usage ;; esac done if debugger "Main" ; then set -x fi if [[ $# -lt 1 || -z "${action}" ]] ; then usage fi # ignore wflag unless specified with -a if [[ ${wflag} -eq 1 && "${action}" != ${ADD} ]] ; then wflag=0 fi # # Ensure the destination PPD repository directory is set # to match the specified repository. If the # destination PPD file repository was specified, then # it must be one of the following: # "user" # "admin" # "vendor" # "system" # "all" # case "${ppdrepname}" in "${SYSTEM}") ppdreppath="${SYSTEMREP}" ;; "${ADMIN}") ppdreppath="${ADMINREP}" ;; "${VENDOR}") ppdreppath="${VENDORREP}" ;; "${USER}") ppdreppath="${USERREP}" ;; "all") if [[ "${action}" = "${ADD}" || \ "${action}" = "${GENERATEENTRY}" ]] ; then print -n "$myprog: " 1>&2 gettext "reserved PPD repository name: " 1>&2 gettext "${ppdrepname}\n" 1>&2 ppdmgr_exit ${FAIL} fi ppdreppath="all" ;; "${UNSET}"|"") ppdreppath="${UNSET}" ;; *) print -n "$myprog: " 1>&2 gettext "invalid PPD repository name: ${ppdrepname}\n" 1>&2 ppdmgr_exit ${FAIL} ;; esac # # When a source PPD file's path is from a known repository, the # destination repository and desination label are assumed to be the # same as the source PPD file's unless a differing repository or label # was specified. # if [[ "${action}" = "${ADD}" || "${action}" = "${GENERATEENTRY}" ]] ; then srcppdpath=$(ppd_pathname "${origsrcppdpath}") ppd_pathname_rc=$? if [[ ${ppd_pathname_rc} -ne 0 ]] ; then print -n "$myprog: " 1>&2 gettext "invalid PPD file: ${origsrcppdpath}\n" 1>&2 ppdmgr_exit ${ppd_pathname_rc} fi # Path cannot contain special characters echo "${srcppdpath}" | /bin/egrep "${SPECIALCHARS}" >/dev/null if [[ $? -eq 0 ]] ; then print -n "$myprog: " 1>&2 gettext "PPD path contains " 1>&2 gettext "an invalid character: ${ppd_pathname}\n" 1>&2 ppdmgr_exit ${FAIL} fi ppdfname=$(/bin/basename "${origsrcppdpath}") # # Check to see if there's any work to be done. If the source file # is already in the destination repository under the destination # label, then there's nothing left to do. We exit rather than # going on to do an update on the label in the repository as # it could possible take a long time to update. If an add was # requested, it could have come from an application, so we want # to return quickly. # if label_path_match "${srcppdpath}" "${ppdreppath}" "${ppdlabel}" ; then if [[ ${wflag} -eq 1 || \ "${action}" = "${GENERATEENTRY}" ]] ; then echo "${srcppdpath}" fi ppdmgr_exit ${SUCCESS} fi fi ppdlabel=$(valid_specified_label "${ppdlabel}") if [[ $? -ne 0 ]] ; then ppdmgr_exit ${FAIL} fi if [[ "${ppdreppath}" = "${UNSET}" ]] ; then ppdreppath="${USERREP}" fi dstrepname=$(get_rep_name "${ppdreppath}") case "${action}" in "${ADD}") # # Attempt to add the PPD file to the repository under the # specified label. If any errors occur, final_dst_ppd_path # will contain the error message rather than the path to the # PPD file. # final_dst_ppd_path=$(add_ppd "${srcppdpath}" "${ppdfname}" \ "${ppdreppath}" "${dstrepname}" "${ppdlabel}") add_ppd_rc=$? case ${add_ppd_rc} in 0) # # The PPD file was added. Update the specified # cache associated with the label if the PPD file # was added successfully and was not a duplicate. # Ensure any changes are also reflected in the # golden cache. # add_ppd_msg=$(update_label_cache "${ppdreppath}" \ "${dstrepname}" "${ppdlabel}") apm_rc=$? echo "${add_ppd_msg}" | /bin/grep "${final_dst_ppd_path}" path_in_msg=$? # # Only report cache update errors if the file that was # added was one that was reported as not being added # to the cache. This really should happen as the file # was verified during the add. # if [[ ${apm_rc} -ne 0 && ${path_in_msg} -eq 0 ]] ; then print -n "$myprog: " 1>&2 gettext "printer information does not reflect " 1>&2 gettext "the\nfollowing PPD file(s):\n" 1>&2 print "${add_ppd_msg}" 1>&2 status=${FAIL} else update_golden_cache # # Display the full path to the added PPD file, # if requested (-w). # if [[ ${wflag} -eq 1 ]] ; then print "${final_dst_ppd_path}" fi fi ;; 1) # Duplicate copy exists if [[ ${wflag} -eq 1 ]] ; then print "${final_dst_ppd_path}" fi ;; 2) # Varying copy exists print -n "$myprog: " 1>&2 gettext "differing variant of source PPD file " 1>&2 gettext "already exists at\n" 1>&2 gettext "${final_dst_ppd_path}\n" 1>&2 status=${FAIL} ;; *) # The PPD file was not added as a problem occurred. # Display the error message. print -n "$myprog: " 1>&2 print "${final_dst_ppd_path}" 1>&2 status=${FAIL} ;; esac ;; "${GENERATEENTRY}") # # Create a cache file entry for the specified PPD file and # display it on standard out. # verify_ppd_file "${srcppdpath}" if [[ $? -eq 0 ]] ; then dstdir="${ppdreppath}/${ppdlabel}/${modmanuf}" final_dst_path="${dstdir}/$(/bin/basename ${srcppdpath})" verify_ppd_location "${final_dst_path}" if [[ $? -eq 0 ]] ; then # Generate the cache file entry print "$(generate_cache_file_entry "${modmanuf}" \ "${model}" "${nickn}" "${devidmfg}" "${devidmdl}" \ "${final_dst_path}")" else print -n "$myprog: " 1>&2 gettext "PPD file not in valid location\n" 1>&2 gettext \ "(<repository>/<label>/<manufacturer>/<PPD file>):\n\t${1}\n" 1>&2 status=${FAIL} fi else print -n "$myprog: " 1>&2 gettext "invalid PPD file: ${1}\n" 1>&2 status=${FAIL} fi ;; "${REBUILD}" | "${UPDATE}") update_msg=$(update_cache "${ppdreppath}" "${ppdlabel}") if [[ $? -ne 0 ]] ; then print -n "$myprog: " 1>&2 gettext "printer information does not reflect " 1>&2 gettext "the\nfollowing PPD file(s):\n" 1>&2 print "${update_msg}" 1>&2 status=${WARN} fi ;; *) usage ;; esac ppdmgr_exit ${status}