xref: /illumos-gate/usr/src/cmd/print/scripts/lpadmin (revision ae389aa988ce154c9f8d5d4dbe4a63c3744339f4)
1#!/bin/ksh
2#
3# CDDL HEADER START
4#
5# The contents of this file are subject to the terms of the
6# Common Development and Distribution License (the "License").
7# You may not use this file except in compliance with the License.
8#
9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10# or http://www.opensolaris.org/os/licensing.
11# See the License for the specific language governing permissions
12# and limitations under the License.
13#
14# When distributing Covered Code, include this CDDL HEADER in each
15# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16# If applicable, add the following below this CDDL HEADER, with the
17# fields enclosed by brackets "[]" replaced with your own identifying
18# information: Portions Copyright [yyyy] [name of copyright owner]
19#
20# CDDL HEADER END
21#
22#
23# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24# Use is subject to license terms.
25#
26#
27
28PATH=/bin:/usr/bin:/usr/sbin export PATH
29
30TEXTDOMAIN="SUNW_OST_OSCMD"
31export TEXTDOMAIN
32
33PFEXEC=/usr/bin/pfexec
34LPSET=/usr/bin/lpset
35LPGET=/usr/bin/lpget
36LPSTAT=/usr/bin/lpstat
37LPADMIN=/usr/lib/lp/local/lpadmin
38LPFILTER=/usr/sbin/lpfilter
39COMM=/usr/bin/comm
40PPDMGR=/usr/sbin/ppdmgr
41MKTEMP="/usr/bin/mktemp -t"
42
43HOST=$(/bin/uname -n)
44exit_code=0
45
46usage() {
47	gettext "Usage:\n" 1>&2
48	gettext "	lpadmin -p (printer) (options)\n" 1>&2
49	gettext "	lpadmin -x (dest)\n" 1>&2
50	gettext "	lpadmin -d (dest)\n" 1>&2
51	gettext "	lpadmin -S print-wheel -A alert-type [ -W minutes ]\n" 1>&2
52	gettext "		[ -Q requests ]\n" 1>&2
53	gettext "	lpadmin -M -f form-name [ -a [ -o filebreak ]\n" 1>&2
54	gettext "		[ -t tray-number ]]\n" 1>&2
55	exit 1
56}
57
58# create a filter table for LP service
59lp_config_filters() {
60	if [[ ! -f /etc/lp/filter.table ]] ; then
61		cd /etc/lp/fd ; for filter in *.fd ; do
62			${PFEXEC} ${LPFILTER} \
63				-f $(/usr/bin/basename $filter .fd) \
64				-F $filter
65		done
66	fi
67}
68
69# enable/disable LP related service(s)
70lp_config_service() {	# (enable | disable)
71	svcadm ${1} -s svc:/application/print/server:default
72	# svcadm ${1} -s svc:/application/print/rfc1179:default
73}
74
75# synchronize printers.conf with LP configuration changes
76lp_config_sync_pconf() {	# (pre) (post)
77	ADDED=$(${COMM} -13 ${1} ${2})
78	REMOVED=$(${COMM} -23 ${1} ${2})
79
80	lp_server=${server:-${HOST}}
81	for DEST in ${ADDED} ; do
82		lp_uri="ipp://${lp_server}/printers/${DEST}"
83		lp_bsdaddr="${lp_server},${DEST},Solaris"
84		${LPSET} -n system \
85			-a "printer-uri-supported=${lp_uri}" \
86			-a "bsdaddr=${lp_bsdaddr}" \
87		 	${DEST} 2>/dev/null
88	done
89
90	for DEST in ${REMOVED} ; do
91		${LPSET} -n system -x ${DEST} 2>/dev/null
92	done
93}
94
95# Delete all destinations in printers.conf
96delete_all() {
97	for DEST in $(lpget -n system list | egrep -e '.+:$' | sed -e 's/://')
98	do
99		${LPSET} -n system -x ${DEST}
100		status=$?
101	done
102}
103
104# Call the ppdmgr utility to add a new PPD file to the system.
105#
106# $1  - path to PPD file
107# $2  - label name (optional)
108add_new_ppd_file() {
109	# Add new ppd file and echo full path it was actually saved to
110	ppdmgrcmd="${PFEXEC} ${PPDMGR} -a ${1} -w"
111
112	ppderrfile=$(${MKTEMP} lpadminerror.XXXXXX)
113	if [[ -z "${ppderrfile}" ]] ; then
114		gettext "lpadmin: System error; cannot create temporary file\n" 1>&2
115		exit 2
116	fi
117	ppd_file=$(${ppdmgrcmd} 2>${ppderrfile})
118	ppdmgrrc=$?
119	if [[ -s "${ppderrfile}" ]] ; then
120		print -n "lpadmin: " 1>&2
121		cat ${ppderrfile} 1>&2
122		rm -f ${ppderrfile} >/dev/null 2>&1
123		if [[ ${ppdmgrrc} -ne 0 ]] ; then
124			exit 1
125		fi
126	fi
127	rm -f ${ppderrfile} >/dev/null 2>&1
128}
129
130#
131# Execution begins here
132#
133
134# be sure that we can run lpset and lpget
135if [[ ! -x ${LPSET} || ! -x ${LPGET} ]] ; then
136	gettext "lpadmin: System error; cannot set default printer\n" 1>&2
137	exit 2
138fi
139
140if [[ $# -lt 1 ]] ; then
141	usage
142	exit 1
143fi
144
145# Deal with the -d option independently since getopts does not handle
146# options that may or may not have arguments
147#
148if [[ ${1} = "-d" ]] ; then
149	if [[ $# -eq 1 ]] ; then	# remove the "default"
150		${LPGET} -n system _default >/dev/null 2>&1
151		exit_code=$?
152
153		if [[ ${exit_code} -eq 0 ]] ; then
154			${LPSET} -n system -x _default
155			exit_code=$?
156		else	# no default, nothing to do
157			exit_code=0
158		fi
159	elif [[ $# -eq 2 ]] ; then	# add/change the "default"
160		${LPGET} -k bsdaddr ${2} >/dev/null 2>&1
161		exit_code=$?
162
163		if [[ $exit_code -eq 0 ]] ; then
164			${LPSET} -n system -a "use=${2}" _default
165			exit_code=$?
166		else	# can't set default to an unconfigured printer
167			gettext "${2}: undefined printer\n" 1>&1
168		fi
169	else				# invalid usage
170		usage
171		exit 1
172	fi
173
174	exit ${exit_code}
175fi
176
177#		Strip off legal options
178while getopts "A:ac:D:e:f:F:H:hi:I:lm:Mn:o:p:Q:r:S:s:T:u:U:v:W:x:t:P:" arg
179do
180	case $arg in
181	D)
182		description="${OPTARG}"
183	;;
184	n)
185		ppd_file="${OPTARG}"
186	;;
187	p)
188		if [[ -n "${delete}" ]] ; then
189			usage
190		fi
191		printer=${OPTARG}
192	;;
193	s)
194		server=${OPTARG}
195	;;
196	v|U)
197		device=${OPTARG}
198		if [[ ! -n "${server}" ]] ; then
199			server=${HOST}
200		fi
201		local="true"
202	;;
203	x)
204		if [[ -n "${printer}" || -n "${server}" || \
205		     -n "${device}" || -n "${description}" ]] ; then
206			usage
207		fi
208		delete=${OPTARG}
209		printer=${OPTARG}
210		if [[ ${printer} = "all" ]] ; then
211			local="true"
212		fi
213	;;
214	S|M|A)
215		local="true"
216	;;
217	c)
218		class=${OPTARG}
219		local="true"
220		if [[ ! -f ${LPGET} ]] ; then
221			gettext "lpadmin: System error; cannot set class\n " 1>&2
222			exit 2
223		fi
224
225		${LPGET} "${class}" > /dev/null 2>&1
226		lpget_class=$?
227		if [[ ${lpget_class} -eq 0 && ! -r /etc/lp/classes/"${class}" ]] ; then
228			gettext "lpadmin: ERROR: Can't create class ${class}.\n" 1>&2
229			gettext "           TO FIX: This is an existing printer name;\n" 1>&2
230			gettext "                   choose another name.\n" 1>&2
231			exit 1
232		fi
233	;;
234	r)
235		local="true"
236	;;
237	esac
238done
239
240#
241# We don't have anything to do; let user know and bail
242#
243if [[ ! -n "${printer}" && ! -n "${delete}" && ! -n "${local}" ]] ; then
244	gettext "lpadmin: ERROR: Nothing to do.\n" 1>&2
245	gettext "        TO FIX: You must give one of these options:\n" 1>&2
246	gettext "		      -p, -d, -x -S\n" 1>&2
247	exit 1
248fi
249
250#
251#       Printer does not exist
252#       To be consistent with 2.5, assume adding local printer
253#
254if [[ ! -n "${device}" && ! -n "${server}" && ! -n "${delete}" && \
255	  ! -n "${local}" ]] ; then
256	${LPGET} "${printer}" > /dev/null 2>&1
257	lpget_stat=$?
258	if [[ ${lpget_stat} -ne 0 ]] ; then
259		gettext "lpadmin: ERROR: Missing -U or -v option.\n" 1>&2
260		gettext "           TO FIX: Local printers must have\n" 1>&2
261		gettext "                   a port defined (-v option) or\n" 1>&2
262		gettext "                   have dial-out instructions (-U option).\n" 1>&2
263		exit 1
264	fi
265fi
266
267#	process the "server" value
268#	It can be a hostname, UUCP form (server!queue), RCMD form(queue@server),
269#	or in URI form ({scheme}://{endpoint})
270#
271case "${server}" in
272	*://*)	# URI form
273		uri=${server}
274		rem_printer=$(expr "${server}" : ".*://.*/\([^/]*\)")
275		server=$(expr "${server}" : ".*://\([^/]*\)/.*")
276		;;
277	*@*)	# RCMD form
278		rem_printer=$(expr "${server}" : "\(.*\)@.*")
279		server=$(expr "${server}" : ".*@\(.*\)")
280		;;
281	*!*)	# UUCP form
282		rem_printer=$(expr "${server}" : ".*!\(.*\)")
283		server=$(expr "${server}" : "\(.*\)!.*")
284		;;
285	*)	# hostname
286		rem_printer=${printer}
287		;;
288esac
289
290# if there is a "device" or LP configuration, it's local
291if [[ -n "${device}" || -f /etc/lp/printers/${printer}/configuration || \
292      -f /etc/lp/classes/${printer} ]] ; then
293	local="true"
294fi
295
296# Do the LP configuration for a local printer served by lpsched
297if [[ -x ${LPADMIN} && -n "${local}" ]] ; then
298	# enumerate LP configured printers before modification
299	PRE=$(${MKTEMP} lpadmin-pre.XXXXXX)
300	if [[ -z "${PRE}" ]] ; then
301		gettext "lpadmin: System error; cannot create temporary file\n" 1>&2
302		exit 2
303	fi
304
305	(/bin/ls /etc/lp/printers 2>/dev/null ; /bin/ls /etc/lp/classes \
306		2>/dev/null) >${PRE}
307
308	# if there are no printers configured, enable LP service(s)
309	[[ ! -s "${PRE}" ]] && lp_config_service enable
310
311	# add filters to LP service
312	lp_config_filters
313
314	# add new ppd file to PPD file repositories
315	if [[ -n "${ppd_file}" && -x ${PPDMGR} ]] ; then
316		add_new_ppd_file "${ppd_file}"
317	fi
318
319	# modify LP destination(s)
320	CMD="${PFEXEC} ${LPADMIN}"
321	while [[ -n "$*" ]] ; do	# to deal with multi-word arguments
322		CMD="$CMD \"$1\""
323		# replace the ppd_file originally specified with the -n option
324		# with the one returned from call to ppdmgr
325		if [[ "${1}" = "-n" ]] ; then
326			CMD="$CMD \"${ppd_file}\""
327			shift
328		fi
329		shift
330	done
331	case "$CMD" in
332		*\"-D\")
333			CMD="$CMD \"\""
334		;;
335	esac
336
337	# execute the LP lpadmin command
338	eval $CMD
339	exit_code=$?
340
341	# enumerate LP configured printers after modification
342	POST=$(${MKTEMP} lpadmin-post.XXXXXX)
343	if [[ -z "${POST}" ]] ; then
344		gettext "lpadmin: System error; cannot create temporary file\n" 1>&2
345		/bin/rm -f ${PRE} >/dev/null 2>&1
346		exit 2
347	fi
348
349	(/bin/ls /etc/lp/printers 2>/dev/null ; /bin/ls /etc/lp/classes \
350		2>/dev/null) >${POST}
351
352	# if there are no destinations, disable the service(s)
353	[[ ! -s "${POST}" ]] && lp_config_service disable
354
355	# sync printers.conf with LP configuration
356	lp_config_sync_pconf "${PRE}" "${POST}"
357
358	/bin/rm -f ${PRE} ${POST}
359fi
360
361# Do any printers.conf configuration that is required
362if [[ -n "${delete}" ]] ; then
363	if [[ "${delete}" = "all" ]] ; then
364		[[ $exit_code -eq 0 ]] && delete_all
365   	elif [[ -z "${local}" ]] ; then
366   		${LPSET} -n system -x ${delete}
367   		exit_code=$?
368   	fi
369else
370	if [[ -z "${local}" ]] ; then
371		# if we need a uri, find the "best" one.
372		if [[ -z "${uri}" ]] ; then
373			uri="ipp://${server}/printers/${rem_printer}"
374			${LPSTAT} -p ${uri} >/dev/null 2>&1
375			if [[ $? -ne 0 ]] ; then
376				uri="lpd://${server}/printers/${rem_printer}#Solaris"
377			fi
378		fi
379		# set the bsdaddr
380		bsdaddr="${server},${rem_printer},Solaris"
381
382		if [[ -n "${printer}" && -n "${server}" ]] ; then
383			${LPSET} -n system \
384				-a "printer-uri-supported=${uri}" \
385				-a "bsdaddr=${bsdaddr}" ${printer}
386			exit_code=$?
387		fi
388
389	fi
390
391	if [[ -n "${printer}" && -n "${description}" ]] ; then
392		${LPSET} -n system \
393			-a "description=${description}" ${printer}
394		exit_code=$?
395	fi
396fi
397
398# if the "default" doesn't resolve a "bsdaddr", the printer is gone, remove it
399${LPGET} -n system -k bsdaddr _default >/dev/null 2>&1 ||
400	${LPSET} -n system -x _default >/dev/null 2>&1
401
402exit $exit_code
403