ppdmgr revision c81d47afd05baeb768e2f032636019b717899efd
#!/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 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
#
#
# 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=$(/bin/mktemp -p "${ppdmgrtmpdir}" \
tmpgoldencache.XXXXXX 2>/dev/null)
/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=$(/bin/mktemp -p "${ppdmgrtmpdir}" \
unsortedcache.XXXXXX 2>/dev/null)
#
# 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=$(/bin/mktemp -p "${ppdmgrtmpdir}" \
tmpcachepath.XXXXXX 2>/dev/null)
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
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 "-g <ppd_filename_path> [ -L <label> ]\n" 1>&2
# gettext "\t\t[ -R <ppd_repository> ]\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"
# Deliveries
SYSTEM=system
VENDOR=vendor
ADMIN=admin
USER=user
# Sytem 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=/tmp/ppdmgr.$$
/bin/mkdir "${ppdmgrtmpdir}" >/dev/null 2>&1
set_perms ${DIRMODE} ${DIROWNER} "${ppdmgrtmpdir}"
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}