system-config-printer/system-config-printer-525e996.patch
2009-07-26 18:43:24 +00:00

2569 lines
72 KiB
Diff

diff -up system-config-printer-1.1.10/aclocal.m4.525e996 system-config-printer-1.1.10/aclocal.m4
--- system-config-printer-1.1.10/aclocal.m4.525e996 2009-07-22 13:54:02.000000000 +0100
+++ system-config-printer-1.1.10/aclocal.m4 2009-07-26 19:01:45.683356020 +0100
@@ -1867,6 +1867,162 @@ AC_DEFUN([AM_NLS],
AC_SUBST(USE_NLS)
])
+# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
+#
+# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# PKG_PROG_PKG_CONFIG([MIN-VERSION])
+# ----------------------------------
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+ AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+ _pkg_min_version=m4_default([$1], [0.9.0])
+ AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+ if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ PKG_CONFIG=""
+ fi
+
+fi[]dnl
+])# PKG_PROG_PKG_CONFIG
+
+# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+#
+# Check to see whether a particular set of modules exists. Similar
+# to PKG_CHECK_MODULES(), but does not set variables or print errors.
+#
+#
+# Similar to PKG_CHECK_MODULES, make sure that the first instance of
+# this or PKG_CHECK_MODULES is called, or make sure to call
+# PKG_CHECK_EXISTS manually
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+ m4_ifval([$2], [$2], [:])
+m4_ifvaln([$3], [else
+ $3])dnl
+fi])
+
+
+# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+# ---------------------------------------------
+m4_define([_PKG_CONFIG],
+[if test -n "$$1"; then
+ pkg_cv_[]$1="$$1"
+ elif test -n "$PKG_CONFIG"; then
+ PKG_CHECK_EXISTS([$3],
+ [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
+ [pkg_failed=yes])
+ else
+ pkg_failed=untried
+fi[]dnl
+])# _PKG_CONFIG
+
+# _PKG_SHORT_ERRORS_SUPPORTED
+# -----------------------------
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi[]dnl
+])# _PKG_SHORT_ERRORS_SUPPORTED
+
+
+# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+# [ACTION-IF-NOT-FOUND])
+#
+#
+# Note that if there is a possibility the first call to
+# PKG_CHECK_MODULES might not happen, you should be sure to include an
+# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+#
+#
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+ _PKG_SHORT_ERRORS_SUPPORTED
+ if test $_pkg_short_errors_supported = yes; then
+ $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$2" 2>&1`
+ else
+ $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors "$2" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+ ifelse([$4], , [AC_MSG_ERROR(dnl
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT
+])],
+ [AC_MSG_RESULT([no])
+ $4])
+elif test $pkg_failed = untried; then
+ ifelse([$4], , [AC_MSG_FAILURE(dnl
+[The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])],
+ [$4])
+else
+ $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+ $1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+ AC_MSG_RESULT([yes])
+ ifelse([$3], , :, [$3])
+fi[]dnl
+])# PKG_CHECK_MODULES
+
# po.m4 serial 15 (gettext-0.17)
dnl Copyright (C) 1995-2007 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
diff -up system-config-printer-1.1.10/configure.in.525e996 system-config-printer-1.1.10/configure.in
--- system-config-printer-1.1.10/configure.in.525e996 2009-07-22 13:53:25.000000000 +0100
+++ system-config-printer-1.1.10/configure.in 2009-07-26 19:01:45.685355364 +0100
@@ -40,6 +40,9 @@ AC_ARG_WITH(udev-rules,
AM_CONDITIONAL([UDEV_RULES], [test x$with_udev_rules != xno])
if test x$with_udev_rules != xno; then
+ PKG_CHECK_MODULES(DBUS_GLIB, [dbus-glib-1 >= 0.76])
+ AC_SUBST(DBUS_GLIB_CFLAGS)
+ AC_SUBST(DBUS_GLIB_LIBS)
AM_PROG_CC_C_O
fi
diff -up system-config-printer-1.1.10/cupshelpers/ppds.py.525e996 system-config-printer-1.1.10/cupshelpers/ppds.py
--- system-config-printer-1.1.10/cupshelpers/ppds.py.525e996 2009-07-22 13:22:49.000000000 +0100
+++ system-config-printer-1.1.10/cupshelpers/ppds.py 2009-07-26 19:01:45.686355890 +0100
@@ -159,29 +159,32 @@ def ppdMakeModelSplit (ppd_make_and_mode
# HP PPDs give NickNames like:
# *NickName: "HP LaserJet 4 Plus v2013.111 Postscript (recommended)"
# Find the version number.
- v = model.find (" v")
+ modell = model.lower ()
+ v = modell.find (" v")
if v != -1 and (model[v + 2].isdigit () or
(model[v + 2] == '.' and
model[v + 3].isdigit ())):
# Truncate at that point.
model = model[:v]
+ modell = modell[:v]
for suffix in [" hpijs",
- " Foomatic/",
+ " foomatic/",
" - ",
" w/",
" (",
- " PostScript",
- " PS",
- " PS1",
- " PS2",
- " PS3",
- " PXL",
- " series"
+ " postscript",
+ " ps",
+ " ps1",
+ " ps2",
+ " ps3",
+ " pxl",
+ " series",
","]:
- s = model.find (suffix)
+ s = modell.find (suffix)
if s != -1:
model = model[:s]
+ modell = modell[:s]
if makel == "hp":
modelnames = {"dj": "DeskJet",
@@ -190,10 +193,10 @@ def ppdMakeModelSplit (ppd_make_and_mode
"color lj": "Color LaserJet",
"ps ": "PhotoSmart",
"hp ": ""}
- modell = model.lower ()
for (name, fullname) in modelnames.iteritems ():
if modell.startswith (name):
model = fullname + model[len (name):]
+ modell = model.lower ()
for mfr in [ "Apple", "Canon", "Epson", "Lexmark", "Oki" ]:
if makel == mfr.lower ():
diff -up system-config-printer-1.1.10/Makefile.am.525e996 system-config-printer-1.1.10/Makefile.am
--- system-config-printer-1.1.10/Makefile.am.525e996 2009-07-22 15:11:41.000000000 +0100
+++ system-config-printer-1.1.10/Makefile.am 2009-07-26 19:01:45.680355779 +0100
@@ -152,14 +152,65 @@ bin_SCRIPTS=\
if UDEV_RULES
udevrulesdir=$(sysconfdir)/udev/rules.d
udevrules_DATA=udev/70-printers.rules
-udev_udev_configure_printer_SOURCES=\
- udev/udev-configure-printer.c
-udev_udev_configure_printer_LDADD=-lcups -ludev -lusb
+
+udev_printer_config_daemon_SOURCES= \
+ udev/printer-config-daemon.c \
+ udev/printer-config-main.c \
+ udev/printer-config.h
+udev_printer_config_daemon_DEPENDENCIES= \
+ udev/printer-config-server-bindings.h
+udev_printer_config_daemon_CPPFLAGS= \
+ $(DBUS_GLIB_CFLAGS)
+udev_printer_config_daemon_LDADD= \
+ $(DBUS_GLIB_LIBS) \
+ -lcups -ludev -lusb
+
+udev_udev_usb_printer_SOURCES= \
+ udev/udev-usb-printer.c
+udev_udev_usb_printer_DEPENDENCIES= \
+ udev/printer-config-client-bindings.h
+udev_udev_usb_printer_CPPFLAGS= \
+ $(DBUS_GLIB_CFLAGS)
+udev_udev_usb_printer_LDADD= \
+ $(DBUS_GLIB_LIBS) \
+ -ludev -lusb
udevhelperdir=/lib/udev
udevhelper_PROGRAMS=\
- udev/udev-configure-printer
-udevhelper_SCRIPTS=\
+ udev/udev-usb-printer
+
+udev/printer-config-server-bindings.h: udev/com.redhat.PrinterConfig.xml \
+ Makefile
+ dbus-binding-tool --prefix=printer_config_daemon \
+ --mode=glib-server \
+ --output=$@ $<
+
+udev/printer-config-client-bindings.h: udev/com.redhat.PrinterConfig.xml \
+ Makefile
+ dbus-binding-tool --prefix=printer_config_daemon \
+ --mode=glib-client \
+ --output=$@ $<
+
+BUILT_SOURCES= \
+ udev/printer-config-server-bindings.h \
+ udev/printer-config-client-bindings.h
+
+libexec_PROGRAMS=\
+ udev/printer-config-daemon
+libexec_SCRIPTS=\
udev/udev-add-printer
+
+dbusifdir = $(datadir)/dbus-1/interfaces
+dbusif_DATA = udev/com.redhat.PrinterConfig.xml
+
+servicedir = $(datadir)/dbus-1/system-services
+service_in_files = udev/com.redhat.PrinterConfig.service.in
+service_DATA = $(service_in_files:.service.in=.service)
+
+$(service_DATA): $(service_in_files) Makefile
+ @sed -e "s,\@libexecdir\@,$(libexecdir)," $< > $@
+
+dbusconfdir = $(sysconfdir)/dbus-1/system.d
+dbusconf_DATA = udev/com.redhat.PrinterConfig.conf
endif
man_MANS= \
@@ -238,7 +289,10 @@ EXTRA_DIST=\
intltool-merge.in \
intltool-update.in \
config.py.in \
- udev/70-printers.rules
+ udev/70-printers.rules \
+ udev/com.redhat.PrinterConfig.xml \
+ udev/com.redhat.PrinterConfig.service.in \
+ udev/com.redhat.PrinterConfig.conf
desktop_in_files = $(desktop_DATA:.desktop=.desktop.in)
@@ -293,12 +347,17 @@ test-ppd-module.sh:
TESTS = test-ppd-module.sh
+CLEANFILES = \
+ $(BUILT_SOURCES) \
+ udev/*.o
+
DISTCLEANFILES=*.pyc *.pyo *~ *.bak \
troubleshoot/*.pyc troubleshoot/*.pyo troubleshoot/*~ \
intltool-extract intltool-merge intltool-update \
*.desktop man/*.1 \
test-ppd-module.sh pickled-ppds \
- config.py
+ config.py \
+ $(service_DATA)
distclean-local:
rm -rf html
diff -up system-config-printer-1.1.10/system-config-printer.py.525e996 system-config-printer-1.1.10/system-config-printer.py
--- system-config-printer-1.1.10/system-config-printer.py.525e996 2009-07-21 15:16:35.000000000 +0100
+++ system-config-printer-1.1.10/system-config-printer.py 2009-07-26 19:01:45.690356122 +0100
@@ -3377,7 +3377,7 @@ class GUI(GtkGUI, monitor.Watcher):
name = name.replace ("#", "-")
if not self.checkNPName (name):
suffix=2
- while not self.checkNPName (name + str (suffix)):
+ while not self.checkNPName (name + "-" + str (suffix)):
suffix += 1
if suffix == 100:
break
diff -up system-config-printer-1.1.10/udev/70-printers.rules.525e996 system-config-printer-1.1.10/udev/70-printers.rules
--- system-config-printer-1.1.10/udev/70-printers.rules.525e996 2009-07-22 13:22:49.000000000 +0100
+++ system-config-printer-1.1.10/udev/70-printers.rules 2009-07-26 19:01:45.698481110 +0100
@@ -1,7 +1,7 @@
# Low-level USB device add trigger
-ACTION=="add", SUBSYSTEM=="usb", ATTR{bInterfaceClass}=="07", ATTR{bInterfaceSubClass}=="01", RUN+="udev-configure-printer add %p"
+ACTION=="add", SUBSYSTEM=="usb", ATTR{bInterfaceClass}=="07", ATTR{bInterfaceSubClass}=="01", RUN+="udev-usb-printer add %p"
# usblp device add trigger (needed when usblp is already loaded)
-ACTION=="add", KERNEL=="lp*", RUN+="udev-configure-printer add %p"
+ACTION=="add", KERNEL=="lp*", RUN+="udev-usb-printer add %p"
# Low-level USB device remove trigger
-ACTION=="remove", SUBSYSTEM=="usb", ENV{ID_USB_INTERFACES}=="*:0701*:*", RUN+="udev-configure-printer remove %p"
+ACTION=="remove", SUBSYSTEM=="usb", ENV{ID_USB_INTERFACES}=="*:0701*:*", RUN+="udev-usb-printer remove %p"
diff -up /dev/null system-config-printer-1.1.10/udev/com.redhat.PrinterConfig.conf
--- /dev/null 2009-07-26 12:31:15.305032486 +0100
+++ system-config-printer-1.1.10/udev/com.redhat.PrinterConfig.conf 2009-07-26 19:01:45.698481110 +0100
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
+
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <!-- Only root can own the service -->
+ <policy user="root">
+ <allow own="com.redhat.PrinterConfig"/>
+ <allow send_destination="com.redhat.PrinterConfig"/>
+ </policy>
+
+ <policy context="default">
+ <deny send_destination="com.redhat.PrinterConfig"/>
+ </policy>
+</busconfig>
diff -up /dev/null system-config-printer-1.1.10/udev/com.redhat.PrinterConfig.service.in
--- /dev/null 2009-07-26 12:31:15.305032486 +0100
+++ system-config-printer-1.1.10/udev/com.redhat.PrinterConfig.service.in 2009-07-26 19:01:45.699481020 +0100
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=com.redhat.PrinterConfig
+Exec=@libexecdir@/printer-config-daemon
+User=root
diff -up /dev/null system-config-printer-1.1.10/udev/com.redhat.PrinterConfig.xml
--- /dev/null 2009-07-26 12:31:15.305032486 +0100
+++ system-config-printer-1.1.10/udev/com.redhat.PrinterConfig.xml 2009-07-26 19:01:45.699481020 +0100
@@ -0,0 +1,71 @@
+<!DOCTYPE node PUBLIC
+"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/com/redhat/PrinterConfig" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+
+ <interface name="com.redhat.PrinterConfig">
+ <doc:doc>
+ <doc:description>
+ <doc:para>The PrinterConfig service is available via the
+ system message bus. To access the service, use the
+ <doc:tt>com.redhat.PrinterConfig</doc:tt> interface
+ on the <doc:tt>/com/redhat/PrinterConfig</doc:tt>
+ object on the D-Bus system bus service with the well-known
+ name
+ <doc:tt>com.redhat.PrinterConfig</doc:tt>.</doc:para>
+
+ <doc:para>It is intended to be used by udev when USB printers
+ are connected and disconnected.</doc:para>
+ </doc:description>
+ </doc:doc>
+
+ <method name="UsbPrinterAdd">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="devpath" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ <doc:para>The udev devpath of the device with subsystem
+ usb and devtype usb_device referring to the USB printer
+ device.</doc:para>
+ </doc:summary>
+ </doc:doc>
+ </arg>
+
+ <arg name="deviceid" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ <doc:para>The IEEE 1284 Device ID (not including length
+ field) of the device.</doc:para>
+ </doc:summary>
+ </doc:doc>
+ </arg>
+
+ <doc:doc>
+ <doc:description>
+ <doc:para>This method informs the PrinterConfig service that
+ a USB printer is now connected.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+
+ <method name="UsbPrinterRemove">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="devpath" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ <doc:para>The udev devpath of the device with subsystem
+ usb and devtype usb_device referring to the USB printer
+ device.</doc:para>
+ </doc:summary>
+ </doc:doc>
+ </arg>
+
+ <doc:doc>
+ <doc:description>
+ <doc:para>This method informs the PrinterConfig service that
+ a USB printer has been disconnected.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+ </interface>
+</node>
diff -up /dev/null system-config-printer-1.1.10/udev/printer-config-daemon.c
--- /dev/null 2009-07-26 12:31:15.305032486 +0100
+++ system-config-printer-1.1.10/udev/printer-config-daemon.c 2009-07-26 19:01:45.701480595 +0100
@@ -0,0 +1,1248 @@
+/* -*- Mode: C; c-file-style: "gnu" -*-
+ * printer-config-daemon - a D-Bus service for configuring printers
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Author: Tim Waugh <twaugh@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE 1
+#include "printer-config.h"
+#include "printer-config-server-bindings.h"
+#include <cups/cups.h>
+#include <cups/http.h>
+#include <dbus/dbus-glib-bindings.h>
+#include <fcntl.h>
+#include <libudev.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <usb.h>
+
+#define DISABLED_REASON "Unplugged or turned off"
+#define MATCH_ONLY_DISABLED 1
+#define USB_URI_MAP "/var/run/udev-configure-printer/usb-uris"
+
+struct children
+{
+ struct children *next;
+ GPid pid;
+};
+
+struct device_uris
+{
+ size_t n_uris;
+ char **uri;
+};
+
+struct usb_uri_map_entry
+{
+ struct usb_uri_map_entry *next;
+
+ /* The devpath of the ("usb","usb_device") device. */
+ char *devpath;
+
+ /* List of matching device URIs. */
+ struct device_uris uris;
+};
+
+struct usb_uri_map
+{
+ struct usb_uri_map_entry *entries;
+
+ /* Open file descriptor for the map, or -1 if it has already been
+ * written. */
+ int fd;
+};
+
+struct device_id
+{
+ char *full_device_id;
+ char *mfg;
+ char *mdl;
+ char *sern;
+};
+
+/* Device URI schemes in decreasing order of preference. */
+static const char *device_uri_types[] =
+ {
+ "hp",
+ "usb",
+ };
+
+static int
+device_uri_type (const char *uri)
+{
+ int slen = strcspn (uri, ":");
+ int i;
+ int n = sizeof (device_uri_types) / sizeof (device_uri_types[0]);
+ for (i = 0; i < n; i++)
+ if (!strncmp (uri, device_uri_types[i], slen) &&
+ device_uri_types[i][slen] == '\0')
+ break;
+
+ return i;
+}
+
+static void
+add_device_uri (struct device_uris *uris,
+ const char *uri)
+{
+ char *uri_copy = strdup (uri);
+ if (!uri_copy)
+ {
+ syslog (LOG_ERR, "out of memory");
+ return;
+ }
+
+ if (uris->n_uris == 0)
+ {
+ uris->uri = malloc (sizeof (char *));
+ if (uris->uri)
+ {
+ uris->n_uris = 1;
+ uris->uri[0] = uri_copy;
+ }
+ }
+ else
+ {
+ char **old = uris->uri;
+ if (++uris->n_uris < UINT_MAX / sizeof (char *))
+ {
+ uris->uri = realloc (uris->uri,
+ sizeof (char *) * uris->n_uris);
+ if (uris->uri)
+ uris->uri[uris->n_uris - 1] = uri_copy;
+ else
+ {
+ uris->uri = old;
+ uris->n_uris--;
+ free (uri_copy);
+ }
+ }
+ else
+ {
+ uris->n_uris--;
+ free (uri_copy);
+ }
+ }
+}
+
+static void
+free_device_uris (struct device_uris *uris)
+{
+ size_t i;
+ for (i = 0; i < uris->n_uris; i++)
+ free (uris->uri[i]);
+ free (uris->uri);
+}
+
+static void
+add_usb_uri_mapping (struct usb_uri_map **map,
+ const char *devpath,
+ const struct device_uris *uris)
+{
+ struct usb_uri_map_entry *entry, **prev;
+ size_t i;
+ prev = &(*map)->entries;
+ while (*prev)
+ prev = &((*prev)->next);
+
+ entry = malloc (sizeof (struct usb_uri_map_entry));
+ if (!entry)
+ {
+ syslog (LOG_ERR, "out of memory");
+ return;
+ }
+
+ entry->devpath = strdup (devpath);
+ entry->uris.n_uris = uris->n_uris;
+ entry->uris.uri = malloc (sizeof (char *) * uris->n_uris);
+ for (i = 0; i < uris->n_uris; i++)
+ entry->uris.uri[i] = strdup (uris->uri[i]);
+ entry->next = NULL;
+ *prev = entry;
+}
+
+static struct usb_uri_map *
+read_usb_uri_map (void)
+{
+ int fd = open (USB_URI_MAP, O_RDWR);
+ struct usb_uri_map *map = NULL;
+ struct flock lock;
+ struct stat st;
+ char *buf, *line;
+
+ if (fd == -1)
+ {
+ char dir[] = USB_URI_MAP;
+ char *p = strrchr (dir, '/');
+ if (p)
+ {
+ *p = '\0';
+ mkdir (dir, 0755);
+ fd = open (USB_URI_MAP, O_RDWR | O_TRUNC | O_CREAT, 0644);
+ if (fd == -1)
+ {
+ syslog (LOG_ERR, "failed to create " USB_URI_MAP);
+ return NULL;
+ }
+ }
+ }
+
+ map = malloc (sizeof (struct usb_uri_map));
+ if (!map)
+ {
+ close (fd);
+ syslog (LOG_ERR, "out of memory");
+ return NULL;
+ }
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+ if (fcntl (fd, F_SETLKW, &lock) == -1)
+ {
+ close (fd);
+ free (map);
+ syslog (LOG_ERR, "failed to lock " USB_URI_MAP);
+ return NULL;
+ }
+
+ map->entries = NULL;
+ map->fd = fd;
+ if (fstat (fd, &st) == -1)
+ {
+ close (fd);
+ free (map);
+ syslog (LOG_ERR, "failed to fstat " USB_URI_MAP " (fd %d)", fd);
+ return NULL;
+ }
+
+ /* Read the entire file into memory. */
+ buf = malloc (1 + (sizeof (char) * st.st_size));
+ if (!buf)
+ {
+ close (fd);
+ free (map);
+ syslog (LOG_ERR, "out of memory");
+ return NULL;
+ }
+
+ if (read (fd, buf, st.st_size) < 0)
+ {
+ close (fd);
+ free (map);
+ free (buf);
+ syslog (LOG_ERR, "failed to read " USB_URI_MAP);
+ return NULL;
+ }
+
+ buf[st.st_size] = '\0';
+ line = buf;
+ while (line)
+ {
+ char *saveptr = NULL;
+ const char *devpath, *uri;
+ struct device_uris uris;
+ char *nextline = strchr (line, '\n');
+ if (!nextline)
+ break;
+
+ *nextline++ = '\0';
+ if (nextline >= buf + st.st_size)
+ nextline = NULL;
+
+ devpath = strtok_r (line, "\t", &saveptr);
+ uri = strtok_r (NULL, "\t", &saveptr);
+ if (!devpath || !uri)
+ {
+ syslog (LOG_DEBUG, "Incorrect line in " USB_URI_MAP ": %s",
+ line);
+ continue;
+ }
+
+ uris.n_uris = 1;
+ uris.uri = malloc (sizeof (char *));
+ if (uris.uri == NULL)
+ break;
+
+ uris.uri[0] = strdup (uri);
+ while ((uri = strtok_r (NULL, "\t", &saveptr)) != NULL)
+ add_device_uri (&uris, uri);
+
+ add_usb_uri_mapping (&map, devpath, &uris);
+
+ line = nextline;
+ }
+
+ free (buf);
+ return map;
+}
+
+static void
+write_usb_uri_map (struct usb_uri_map *map)
+{
+ struct usb_uri_map_entry *entry;
+ int fd = map->fd;
+ FILE *f;
+
+ lseek (fd, SEEK_SET, 0);
+ ftruncate (fd, 0);
+ f = fdopen (fd, "w");
+ if (!f)
+ {
+ syslog (LOG_ERR, "failed to fdopen " USB_URI_MAP " (fd %d)", fd);
+ close (fd);
+ map->fd = -1;
+ return;
+ }
+
+ for (entry = map->entries; entry; entry = entry->next)
+ {
+ size_t i;
+ fprintf (f, "%s\t%s", entry->devpath, entry->uris.uri[0]);
+ for (i = 1; i < entry->uris.n_uris; i++)
+ fprintf (f, "\t%s", entry->uris.uri[i]);
+ fwrite ("\n", 1, 1, f);
+ }
+
+ fclose (f);
+ map->fd = -1;
+}
+
+static void
+free_usb_uri_map (struct usb_uri_map *map)
+{
+ struct usb_uri_map_entry *entry, *next;
+ for (entry = map->entries; entry; entry = next)
+ {
+ next = entry->next;
+ free (entry->devpath);
+ free_device_uris (&entry->uris);
+ free (entry);
+ }
+
+ if (map->fd != -1)
+ close (map->fd);
+
+ free (map);
+}
+
+static void
+free_device_id (struct device_id *id)
+{
+ free (id->full_device_id);
+ free (id->mfg);
+ free (id->mdl);
+ free (id->sern);
+}
+
+static void
+parse_device_id (const char *device_id,
+ struct device_id *id)
+{
+ char *fieldname;
+ char *start, *end;
+ size_t len;
+
+ len = strlen (device_id);
+ if (len == 0)
+ return;
+
+ if (device_id[len - 1] == '\n')
+ len--;
+
+ id->full_device_id = malloc (len + 1);
+ fieldname = malloc (len + 1);
+ if (!id->full_device_id || !fieldname)
+ {
+ syslog (LOG_ERR, "out of memory");
+ return;
+ }
+
+ memcpy (id->full_device_id, device_id, len);
+ id->full_device_id[len] = '\0';
+ fieldname[0] = '\0';
+ start = id->full_device_id;
+ while (*start != '\0')
+ {
+ /* New field. */
+
+ end = start;
+ while (*end != '\0' && *end != ':')
+ end++;
+
+ if (*end == '\0')
+ break;
+
+ len = end - start;
+ memcpy (fieldname, start, len);
+ fieldname[len] = '\0';
+
+ start = end + 1;
+ while (*end != '\0' && *end != ';')
+ end++;
+
+ len = end - start;
+
+ if (!id->mfg &&
+ (!strncasecmp (fieldname, "MANUFACTURER", 12) ||
+ !strncasecmp (fieldname, "MFG", 3)))
+ id->mfg = strndup (start, len);
+ else if (!id->mdl &&
+ (!strncasecmp (fieldname, "MODEL", 5) ||
+ !strncasecmp (fieldname, "MDL", 3)))
+ id->mdl = strndup (start, len);
+ else if (!id->sern &&
+ (!strncasecmp (fieldname, "SERIALNUMBER", 12) ||
+ !strncasecmp (fieldname, "SERN", 4) ||
+ !strncasecmp (fieldname, "SN", 2)))
+ id->sern = strndup (start, len);
+
+ if (*end != '\0')
+ start = end + 1;
+ }
+
+ free (fieldname);
+}
+
+static const char *
+no_password (const char *prompt)
+{
+ return "";
+}
+
+static http_t *
+cups_connection (void)
+{
+ http_t *cups = NULL;
+ static int first_time = 1;
+
+ if (first_time)
+ {
+ cupsSetPasswordCB (no_password);
+ first_time = 0;
+ }
+
+ cups = httpConnectEncrypt ("localhost", 631,
+ HTTP_ENCRYPT_IF_REQUESTED);
+ if (cups == NULL)
+ {
+ /* Don't bother retrying here. Instead, the CUPS initscript
+ should run these commands after cupsd is started:
+
+ rmmod usblp
+ udevadm trigger --subsystem-match=usb \
+ --attr-match=bInterfaceClass=07 \
+ --attr-match=bInterfaceSubClass=01
+ */
+
+ syslog (LOG_DEBUG, "failed to connect to CUPS server; giving up");
+ return NULL;
+ }
+
+ return cups;
+}
+
+static int
+find_matching_device_uris (struct device_id *id,
+ const char *usbserial,
+ struct device_uris *uris,
+ const char *devpath,
+ struct usb_uri_map *map)
+{
+ http_t *cups;
+ ipp_t *request, *answer;
+ ipp_attribute_t *attr;
+ struct device_uris uris_noserial;
+ struct device_uris all_uris;
+ size_t i, n;
+ const char *exclude_schemes[] = {
+ "beh",
+ "bluetooth",
+ "http",
+ "https",
+ "ipp",
+ "lpd",
+ "ncp",
+ "parallel",
+ "scsi",
+ "smb",
+ "snmp",
+ "socket",
+ };
+
+ uris->n_uris = uris_noserial.n_uris = all_uris.n_uris = 0;
+ uris->uri = uris_noserial.uri = all_uris.uri = NULL;
+
+ /* Leave the bus to settle. */
+ sleep (1);
+
+ cups = cups_connection ();
+ request = ippNewRequest (CUPS_GET_DEVICES);
+ ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "exclude-schemes",
+ sizeof (exclude_schemes) / sizeof(exclude_schemes[0]),
+ NULL, exclude_schemes);
+
+ answer = cupsDoRequest (cups, request, "/");
+ httpClose (cups);
+
+ if (answer == NULL)
+ {
+ syslog (LOG_ERR, "failed to send IPP request %d",
+ request->request.op.operation_id);
+ return 0;
+ }
+
+ if (answer->request.status.status_code > IPP_OK_CONFLICT)
+ {
+ syslog (LOG_ERR, "IPP request %d failed (%d)",
+ request->request.op.operation_id,
+ answer->request.status.status_code);
+ return 0;
+ }
+
+ for (attr = answer->attrs; attr; attr = attr->next)
+ {
+ const char *device_uri = NULL;
+ struct device_id this_id;
+ this_id.full_device_id = this_id.mfg = this_id.mdl = this_id.sern = NULL;
+
+ while (attr && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (!attr)
+ break;
+
+ for (; attr && attr->group_tag == IPP_TAG_PRINTER; attr = attr->next)
+ {
+ if (attr->value_tag == IPP_TAG_URI &&
+ !strcmp (attr->name, "device-uri"))
+ device_uri = attr->values[0].string.text;
+ else if (attr->value_tag == IPP_TAG_TEXT &&
+ !strcmp (attr->name, "device-id"))
+ parse_device_id (attr->values[0].string.text, &this_id);
+ }
+
+ /* Only use device schemes in our preference order for matching
+ * against the IEEE 1284 Device ID. */
+
+ for (i = 0;
+ device_uri &&
+ i < sizeof (device_uri_types) / sizeof (device_uri_types[0]);
+ i++)
+ {
+ size_t len = strlen (device_uri_types[i]);
+ if (!strncmp (device_uri_types[i], device_uri, len) &&
+ device_uri[len] == ':')
+ break;
+ }
+
+ if (device_uri)
+ add_device_uri (&all_uris, device_uri);
+
+ if (i == sizeof (device_uri_types) / sizeof (device_uri_types[0]))
+ /* Not what we want to match against. Ignore this one. */
+ device_uri = NULL;
+
+ /* Now check the manufacturer and model names. */
+ if (device_uri && this_id.mfg && this_id.mdl &&
+ !strcmp (this_id.mfg, id->mfg) &&
+ !strcmp (this_id.mdl, id->mdl))
+ {
+ /* We've checked everything except the serial numbers. This
+ * is more complicated. Some devices include a serial
+ * number (SERN) field in their IEEE 1284 Device ID. Others
+ * don't -- this was not a mandatory field in the
+ * specification.
+ *
+ * If the device includes SERN field in its, it must match
+ * what the device-id attribute has.
+ *
+ * Otherwise, the only means we have of knowing which device
+ * is meant is the USB serial number.
+ *
+ * CUPS backends may choose to insert the USB serial number
+ * into the SERN field when reporting a device-id attribute.
+ * HPLIP does this, and it seems not to stray too far from
+ * the intent of that field. We accommodate this.
+ *
+ * Alternatively, CUPS backends may include the USB serial
+ * number somewhere in their reported device-uri attributes.
+ * For instance, the CUPS 1.4 usb backend, when compiled
+ * with libusb support, gives device URIs containing the USB
+ * serial number for devices without a SERN field, like
+ * this: usb://HP/DESKJET%20990C?serial=US05M1D20CIJ
+ *
+ * To accommodate this we examine tokens between '?', '='
+ * and '&' delimiters to check for USB serial number
+ * matches.
+ *
+ * CUPS 1.3, and CUPS 1.4 without libusb support, doesn't do this.
+ * As a result we also need to deal with devices that don't report a
+ * SERN field where the backends that don't add a SERN field from
+ * the USB serial number and also don't include the USB serial
+ * number in the URI.
+ */
+
+ int match = 0;
+ if ((id->sern && this_id.sern && !strcmp (id->sern, this_id.sern)))
+ {
+ syslog (LOG_DEBUG, "SERN fields match");
+ match = 1;
+ }
+
+ if (!match && usbserial[0] != '\0')
+ {
+ if (!id->sern)
+ {
+ if (this_id.sern && !strcmp (usbserial, this_id.sern))
+ {
+ syslog (LOG_DEBUG,
+ "SERN field matches USB serial number");
+ match = 1;
+ }
+ }
+
+ if (!match)
+ {
+ char *saveptr, *uri = strdup (device_uri);
+ const char *token;
+ const char *sep = "?=&/";
+ for (token = strtok_r (uri, sep, &saveptr);
+ token;
+ token = strtok_r (NULL, sep, &saveptr))
+ if (!strcmp (token, usbserial))
+ {
+ syslog (LOG_DEBUG, "URI contains USB serial number");
+ match = 1;
+ break;
+ }
+
+ free (uri);
+ }
+ }
+
+ if (match)
+ {
+ syslog (LOG_DEBUG, "URI match: %s", device_uri);
+ add_device_uri (uris, device_uri);
+ }
+ else if (!id->sern)
+ {
+ syslog (LOG_DEBUG, "URI matches without serial number: %s",
+ device_uri);
+ add_device_uri (&uris_noserial, device_uri);
+ }
+ else
+ syslog (LOG_DEBUG, "No match: %s", device_uri);
+ }
+
+ if (!attr)
+ break;
+ }
+
+ ippDelete (answer);
+
+ /* Decide what to do about device URIs that did not match a serial
+ * number. The device had no SERN field, and the USB serial number
+ * was nowhere to be found from the device URI or device-id field.
+ *
+ * Device URIs with no reference to serial number can only each ever
+ * work when only one printer of that model is connected.
+ * Accordingly, it is safe to disable queues using such URIs, as we
+ * know the removed/added device is that lone printer.
+ *
+ * When adding queues it is best to avoid URIs that don't
+ * distinguish serial numbers.
+ *
+ * What we'll do, then, is concatenate the list of "non-serial" URIs
+ * onto the end of the list of "serial" URIs.
+ */
+
+ if (uris->n_uris == 0 && uris_noserial.n_uris > 0)
+ {
+ syslog (LOG_DEBUG, "No serial number URI matches so using those without");
+ uris->n_uris = uris_noserial.n_uris;
+ uris->uri = uris_noserial.uri;
+ uris_noserial.n_uris = 0;
+ uris_noserial.uri = NULL;
+ }
+ else if (uris_noserial.n_uris > 0)
+ {
+ char **old = uris->uri;
+ uris->uri = realloc (uris->uri,
+ sizeof (char *) * (uris->n_uris +
+ uris_noserial.n_uris));
+ if (!uris->uri)
+ uris->uri = old;
+ else
+ {
+ for (i = 0; i < uris_noserial.n_uris; i++)
+ uris->uri[uris->n_uris + i] = uris_noserial.uri[i];
+ uris->n_uris += uris_noserial.n_uris;
+ }
+
+ uris_noserial.n_uris = 0;
+ uris_noserial.uri = NULL;
+ }
+
+ free_device_uris (&uris_noserial);
+
+ /* Having decided which device URIs match based on IEEE 1284 Device
+ * ID, we now need to look for "paired" URIs for other functions of
+ * a multi-function device. This are the same except for the
+ * scheme. */
+
+ n = uris->n_uris;
+ for (i = 0; i < n; i++)
+ {
+ size_t j;
+ char *me = uris->uri[i];
+ char *my_rest = strchr (me, ':');
+ size_t my_schemelen;
+ if (!my_rest)
+ continue;
+
+ my_schemelen = my_rest - me;
+ for (j = 0; j < all_uris.n_uris; j++)
+ {
+ char *twin = all_uris.uri[j];
+ char *twin_rest = strchr (twin, ':');
+ size_t twin_schemelen;
+ if (!twin_rest)
+ continue;
+
+ twin_schemelen = twin_rest - twin;
+ if (my_schemelen == twin_schemelen &&
+ !strncmp (me, twin, my_schemelen))
+ /* This is the one we are looking for the twin of. */
+ continue;
+
+ if (!strcmp (my_rest, twin_rest))
+ {
+ syslog (LOG_DEBUG, "%s twinned with %s", me, twin);
+ add_device_uri (uris, twin);
+ }
+ }
+ }
+
+ free_device_uris (&all_uris);
+ if (uris->n_uris > 0)
+ {
+ add_usb_uri_mapping (&map, devpath, uris);
+ write_usb_uri_map (map);
+ free_usb_uri_map (map);
+ }
+
+ return uris->n_uris;
+}
+
+/* Call a function for each queue with the given device-uri and printer-state.
+ * Returns the number of queues with a matching device-uri. */
+static size_t
+for_each_matching_queue (struct device_uris *device_uris,
+ int flags,
+ void (*fn) (const char *, void *),
+ void *context)
+{
+ size_t matched = 0;
+ http_t *cups = cups_connection ();
+ ipp_t *request, *answer;
+ ipp_attribute_t *attr;
+ const char *attributes[] = {
+ "printer-uri-supported",
+ "device-uri",
+ "printer-state",
+ "printer-state-message",
+ };
+
+ request = ippNewRequest (CUPS_GET_PRINTERS);
+ ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes",
+ sizeof (attributes) / sizeof (attributes[0]),
+ NULL, attributes);
+ answer = cupsDoRequest (cups, request, "/");
+ httpClose (cups);
+ if (answer == NULL)
+ {
+ syslog (LOG_ERR, "failed to send CUPS-Get-Printers request");
+ return 0;
+ }
+
+ if (answer->request.status.status_code > IPP_OK_CONFLICT)
+ {
+ if (answer->request.status.status_code == IPP_NOT_FOUND)
+ {
+ /* No printer queues configured. */
+ ippDelete (answer);
+ return 0;
+ }
+
+ syslog (LOG_ERR, "CUPS-Get-Printers request failed (%d)",
+ answer->request.status.status_code);
+ return 0;
+ }
+
+ for (attr = answer->attrs; attr; attr = attr->next)
+ {
+ const char *this_printer_uri = NULL;
+ const char *this_device_uri = NULL;
+ const char *printer_state_message = NULL;
+ int state = 0;
+ size_t i;
+
+ while (attr && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (!attr)
+ break;
+
+ for (; attr && attr->group_tag == IPP_TAG_PRINTER; attr = attr->next)
+ {
+ if (attr->value_tag == IPP_TAG_URI)
+ {
+ if (!strcmp (attr->name, "device-uri"))
+ this_device_uri = attr->values[0].string.text;
+ else if (!strcmp (attr->name, "printer-uri-supported"))
+ this_printer_uri = attr->values[0].string.text;
+ }
+ else if (attr->value_tag == IPP_TAG_TEXT &&
+ !strcmp (attr->name, "printer-state-message"))
+ printer_state_message = attr->values[0].string.text;
+ else if (attr->value_tag == IPP_TAG_ENUM &&
+ !strcmp (attr->name, "printer-state"))
+ state = attr->values[0].integer;
+ }
+
+ for (i = 0; i < device_uris->n_uris; i++)
+ if (!strcmp (device_uris->uri[i], this_device_uri))
+ {
+ matched++;
+ if (((flags & MATCH_ONLY_DISABLED) &&
+ state == IPP_PRINTER_STOPPED &&
+ !strcmp (printer_state_message, DISABLED_REASON)) ||
+ (flags & MATCH_ONLY_DISABLED) == 0)
+ {
+ syslog (LOG_DEBUG ,"Queue %s has matching device URI",
+ this_printer_uri);
+ (*fn) (this_printer_uri, context);
+ }
+ }
+
+ if (!attr)
+ break;
+ }
+
+ ippDelete (answer);
+ return matched;
+}
+
+static void
+enable_queue (const char *printer_uri, void *context)
+{
+ /* Disable it. */
+ http_t *cups = cups_connection ();
+ ipp_t *request, *answer;
+ request = ippNewRequest (IPP_RESUME_PRINTER);
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, printer_uri);
+ answer = cupsDoRequest (cups, request, "/admin/");
+ if (!answer)
+ {
+ syslog (LOG_ERR, "Failed to send IPP-Resume-Printer request");
+ httpClose (cups);
+ return;
+ }
+
+ if (answer->request.status.status_code > IPP_OK_CONFLICT)
+ syslog (LOG_ERR, "IPP-Resume-Printer request failed");
+ else
+ syslog (LOG_INFO, "Re-enabled printer %s", printer_uri);
+
+ ippDelete (answer);
+ httpClose (cups);
+}
+
+static void
+disable_queue (const char *printer_uri, void *context)
+{
+ /* Disable it. */
+ http_t *cups = cups_connection ();
+ ipp_t *request, *answer;
+ request = ippNewRequest (IPP_PAUSE_PRINTER);
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, printer_uri);
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
+ "printer-state-message", NULL, DISABLED_REASON);
+ answer = cupsDoRequest (cups, request, "/admin/");
+ if (!answer)
+ {
+ syslog (LOG_ERR, "Failed to send IPP-Pause-Printer request");
+ httpClose (cups);
+ return;
+ }
+
+ if (answer->request.status.status_code > IPP_OK_CONFLICT)
+ syslog (LOG_ERR, "IPP-Pause-Printer request failed");
+ else
+ syslog (LOG_INFO, "Disabled printer %s as the corresponding device "
+ "was unplugged or turned off", printer_uri);
+
+ ippDelete (answer);
+ httpClose (cups);
+}
+
+static char *
+syspath_from_devpath (struct udev *udev, const char *devpath)
+{
+ const char *sys;
+ char *syspath;
+ size_t syslen, devpathlen = strlen (devpath);
+ sys = udev_get_sys_path (udev);
+ syslen = strlen (sys);
+ syspath = malloc (syslen + devpathlen + 1);
+ if (syspath == NULL)
+ return NULL;
+
+ memcpy (syspath, sys, syslen);
+ memcpy (syspath + syslen, devpath, devpathlen);
+ syspath[syslen + devpathlen] = '\0';
+ return syspath;
+}
+
+static void
+reap_child (GPid pid, gint status, gpointer context)
+{
+ PrinterConfigDaemon *self = context;
+ struct children *child, **prev = &self->children;
+ g_debug ("PID %d has exited", pid);
+ for (child = self->children; child; prev = &child->next, child = child->next)
+ if (child->pid == pid)
+ {
+ *prev = child->next;
+ free (child);
+ g_debug ("self->children is now %p", self->children);
+ break;
+ }
+}
+
+G_DEFINE_TYPE (PrinterConfigDaemon, printer_config_daemon, G_TYPE_OBJECT)
+
+static gboolean
+kill_timeout (gpointer context)
+{
+ PrinterConfigDaemon *self = context;
+ if (self->children == NULL)
+ {
+ g_debug ("Time to go");
+ main_quit ();
+ return FALSE;
+ }
+
+ g_debug ("children is %p", self->children);
+ return TRUE;
+}
+
+static gboolean
+reset_killtimer (PrinterConfigDaemon *self)
+{
+ if (self->killtimer != 0)
+ {
+ g_debug ("Remove killtimer %d", self->killtimer);
+ g_source_remove (self->killtimer);
+ }
+
+ self->killtimer = g_timeout_add (1000, kill_timeout, self);
+ g_debug ("Set killtimer %d", self->killtimer);
+ return TRUE;
+}
+
+static void
+printer_config_daemon_dispose (GObject *gobject)
+{
+ PrinterConfigDaemon *self = PRINTER_CONFIG_DAEMON (gobject);
+ g_debug ("dispose %p", self);
+ G_OBJECT_CLASS (printer_config_daemon_parent_class)->dispose (gobject);
+}
+
+static void
+printer_config_daemon_finalize (GObject *gobject)
+{
+ PrinterConfigDaemon *self = PRINTER_CONFIG_DAEMON (gobject);
+ g_debug ("finalize %p", self);
+ if (self->killtimer != 0)
+ {
+ g_debug ("Remove killtimer %d", self->killtimer);
+ g_source_remove (self->killtimer);
+ }
+
+ G_OBJECT_CLASS (printer_config_daemon_parent_class)->finalize (gobject);
+}
+
+static void
+printer_config_daemon_class_init (PrinterConfigDaemonClass *klass)
+{
+ GError *error = NULL;
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->dispose = printer_config_daemon_dispose;
+ gobject_class->finalize = printer_config_daemon_finalize;
+ g_debug ("class init");
+ klass->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (klass->connection == NULL)
+ {
+ g_warning ("Unable to connect to D-Bus: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ dbus_g_object_type_install_info(PRINTER_CONFIG_TYPE_DAEMON,
+ &dbus_glib_printer_config_daemon_object_info);
+}
+
+static void
+printer_config_daemon_init (PrinterConfigDaemon *self)
+{
+ DBusGProxy *driver_proxy;
+ GError *error = NULL;
+ PrinterConfigDaemonClass *klass = PRINTER_CONFIG_DAEMON_GET_CLASS (self);
+ guint request_ret;
+
+ dbus_g_connection_register_g_object (klass->connection,
+ "/com/redhat/PrinterConfig",
+ G_OBJECT (self));
+ driver_proxy = dbus_g_proxy_new_for_name (klass->connection,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS);
+
+ g_debug ("daemon init %p", self);
+ if (!org_freedesktop_DBus_request_name (driver_proxy,
+ "com.redhat.PrinterConfig",
+ 0, &request_ret,
+ &error))
+ {
+ g_warning ("Unable to register service: %s", error->message);
+ g_error_free (error);
+ }
+
+ self->children = NULL;
+ g_object_unref (driver_proxy);
+}
+
+gboolean
+printer_config_daemon_usb_printer_add (PrinterConfigDaemon *self,
+ const char *usb_device_devpath,
+ const char *deviceid,
+ DBusGMethodInvocation *context)
+{
+ struct device_id id;
+ struct device_uris device_uris;
+ struct usb_uri_map *map;
+ struct usb_uri_map_entry *entry;
+ struct udev *udev;
+ struct udev_device *dev;
+ const char *usbserial;
+ char *syspath;
+
+ syslog (LOG_DEBUG, "add %s", usb_device_devpath);
+ dbus_g_method_return (context);
+
+ reset_killtimer (self);
+ udev = udev_new ();
+ if (!udev)
+ {
+ syslog (LOG_ERR, "failed to init libudev");
+ return FALSE;
+ }
+
+ map = read_usb_uri_map ();
+ if (!map)
+ return TRUE;
+
+ for (entry = map->entries; entry; entry = entry->next)
+ if (!strcmp (entry->devpath, usb_device_devpath))
+ break;
+
+ if (entry != NULL)
+ /* The map already had an entry so has already been dealt
+ * with. This can happen because there are two "add"
+ * triggers: one for the usb_device device and the other for
+ * the usblp device. We have most likely been triggered by
+ * the usblp device, so the usb_device rule got there before
+ * us and succeeded.
+ *
+ * Pretend we didn't find any device URIs that matched, and
+ * exit.
+ */
+ return TRUE;
+
+ id.full_device_id = id.mfg = id.mdl = id.sern = NULL;
+ parse_device_id (deviceid, &id);
+ if (!id.mfg || !id.mdl)
+ {
+ syslog (LOG_ERR, "invalid IEEE 1284 Device ID %s",
+ id.full_device_id);
+ return FALSE;
+ }
+
+ syspath = syspath_from_devpath (udev, usb_device_devpath);
+ if (!syspath)
+ {
+ syslog (LOG_ERR, "unable to get syspath from devpath");
+ return FALSE;
+ }
+
+ dev = udev_device_new_from_syspath (udev, syspath);
+ if (!dev)
+ {
+ udev_device_unref (dev);
+ udev_unref (udev);
+ syslog (LOG_ERR, "unable to access %s", syspath);
+ free (syspath);
+ return TRUE;
+ }
+
+ free (syspath);
+ usbserial = udev_device_get_sysattr_value (dev, "serial");
+ syslog (LOG_DEBUG, "MFG:%s MDL:%s SERN:%s serial:%s", id.mfg, id.mdl,
+ id.sern ? id.sern : "-", usbserial ? usbserial : "-");
+
+ find_matching_device_uris (&id, usbserial, &device_uris, usb_device_devpath,
+ map);
+ udev_device_unref (dev);
+ udev_unref (udev);
+ if (device_uris.n_uris == 0)
+ {
+ free_device_id (&id);
+ return TRUE;
+ }
+
+ /* Re-enable any queues we'd previously disabled. */
+ if (for_each_matching_queue (&device_uris, MATCH_ONLY_DISABLED,
+ enable_queue, NULL) == 0)
+ {
+ size_t i;
+ int type;
+ GPid child_pid;
+ GError *error = NULL;
+ char **argv = malloc (sizeof (char *) * (3 + device_uris.n_uris));
+
+ /* No queue is configured for this device yet.
+ Decide on a URI to use. */
+ type = device_uri_type (device_uris.uri[0]);
+ for (i = 1; i < device_uris.n_uris; i++)
+ {
+ int new_type = device_uri_type (device_uris.uri[i]);
+ if (new_type < type)
+ {
+ char *swap = device_uris.uri[0];
+ device_uris.uri[0] = device_uris.uri[i];
+ device_uris.uri[i] = swap;
+ type = new_type;
+ }
+ }
+
+ argv[0] = "/usr/libexec/udev-add-printer";
+ argv[1] = id.full_device_id;
+ for (i = 0; i < device_uris.n_uris; i++)
+ argv[i + 2] = device_uris.uri[i];
+ argv[i + 2] = NULL;
+
+ syslog (LOG_DEBUG, "About to add queue for %s", argv[2]);
+ if (g_spawn_async ("/", argv, NULL,
+ G_SPAWN_STDOUT_TO_DEV_NULL |
+ G_SPAWN_STDERR_TO_DEV_NULL |
+ G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL, NULL,
+ &child_pid,
+ &error) == FALSE)
+ syslog (LOG_ERR, "Failed to execute %s", argv[0]);
+ else
+ {
+ struct children *child = malloc (sizeof (struct children));
+ child->next = self->children;
+ child->pid = child_pid;
+ self->children = child;
+ g_child_watch_add (child_pid, reap_child, self);
+ }
+ }
+
+ free_device_id (&id);
+ free_device_uris (&device_uris);
+ return TRUE;
+}
+
+gboolean
+printer_config_daemon_usb_printer_remove (PrinterConfigDaemon *self,
+ const char *devpath,
+ DBusGMethodInvocation *context)
+{
+ struct usb_uri_map *map;
+ struct usb_uri_map_entry *entry, **prev;
+ struct device_uris *uris = NULL;
+
+ syslog (LOG_DEBUG, "remove %s", devpath);
+ dbus_g_method_return (context);
+
+ reset_killtimer (self);
+ map = read_usb_uri_map ();
+ if (!map)
+ return TRUE;
+
+ prev = &map->entries;
+ for (entry = map->entries; entry; entry = entry->next)
+ {
+ if (!strcmp (entry->devpath, devpath))
+ {
+ uris = &entry->uris;
+ break;
+ }
+
+ prev = &(entry->next);
+ }
+
+ if (uris)
+ {
+ /* Find the relevant queues and disable them if they are enabled. */
+ for_each_matching_queue (uris, 0, disable_queue, NULL);
+ *prev = entry->next;
+ write_usb_uri_map (map);
+ }
+
+ free_usb_uri_map (map);
+ return TRUE;
+}
+
+PrinterConfigDaemon *
+printer_config_daemon_new (void)
+{
+ PrinterConfigDaemon *self;
+ self = PRINTER_CONFIG_DAEMON (g_object_new (PRINTER_CONFIG_TYPE_DAEMON,
+ NULL));
+ self->children = NULL;
+ self->killtimer = 0;
+ g_debug ("New daemon %p", self);
+ return self;
+}
diff -up /dev/null system-config-printer-1.1.10/udev/printer-config.h
--- /dev/null 2009-07-26 12:31:15.305032486 +0100
+++ system-config-printer-1.1.10/udev/printer-config.h 2009-07-26 19:01:45.703480670 +0100
@@ -0,0 +1,76 @@
+/* -*- Mode: C; c-file-style: "gnu" -*-
+ * printer-config - a D-Bus service for configuring printers
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Author: Tim Waugh <twaugh@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef PRINTER_CONFIG_H
+#define PRINTER_CONFIG_H
+
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+
+#define PRINTER_CONFIG_TYPE_DAEMON (printer_config_daemon_get_type ())
+#define PRINTER_CONFIG_DAEMON(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ PRINTER_CONFIG_TYPE_DAEMON, \
+ PrinterConfigDaemon))
+#define PRINTER_CONFIG_IS_DAEMON_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), PRINTER_CONFIG_TYPE_DAEMON, \
+ PrinterConfigDaemonClass))
+#define PRINTER_CONFIG_DAEMON_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), PRINTER_CONFIG_TYPE_DAEMON, \
+ PrinterConfigDaemonClass))
+
+typedef struct _PrinterConfigDaemon PrinterConfigDaemon;
+typedef struct _PrinterConfigDaemonClass PrinterConfigDaemonClass;
+
+struct children;
+struct _PrinterConfigDaemon
+{
+ GObject parent_instance;
+
+ /* instance members */
+ struct children *children;
+ guint killtimer;
+};
+
+struct _PrinterConfigDaemonClass
+{
+ GObjectClass parent_class;
+
+ /* class members */
+ DBusGConnection *connection;
+};
+
+/* used by PRINTER_CONFIG_TYPE_DAEMON */
+GType printer_config_daemon_get_type (void);
+
+PrinterConfigDaemon *printer_config_daemon_new (void);
+gboolean printer_config_daemon_usb_printer_add (PrinterConfigDaemon *d,
+ const char *devpath,
+ const char *deviceid,
+ DBusGMethodInvocation *ctx);
+
+gboolean printer_config_daemon_usb_printer_remove (PrinterConfigDaemon *d,
+ const char *devpath,
+ DBusGMethodInvocation *ctx);
+
+void main_quit (void);
+
+#endif /* PRINTER_CONFIG_H */
diff -up /dev/null system-config-printer-1.1.10/udev/printer-config-main.c
--- /dev/null 2009-07-26 12:31:15.305032486 +0100
+++ system-config-printer-1.1.10/udev/printer-config-main.c 2009-07-26 19:01:45.702480595 +0100
@@ -0,0 +1,159 @@
+/* -*- Mode: C; c-file-style: "gnu" -*-
+ *
+ * Copyright (C) 2007 David Zeuthen <davidz@redhat.com>
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "printer-config.h"
+
+#define NAME_TO_CLAIM "com.redhat.PrinterConfig"
+
+static gboolean
+acquire_name_on_proxy (DBusGProxy *bus_proxy)
+{
+ GError *error;
+ guint result;
+ gboolean res;
+ gboolean ret;
+
+ ret = FALSE;
+
+ if (bus_proxy == NULL) {
+ goto out;
+ }
+
+ error = NULL;
+ res = dbus_g_proxy_call (bus_proxy,
+ "RequestName",
+ &error,
+ G_TYPE_STRING, NAME_TO_CLAIM,
+ G_TYPE_UINT, 0,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, &result,
+ G_TYPE_INVALID);
+ if (! res) {
+ if (error != NULL) {
+ g_warning ("Failed to acquire %s: %s", NAME_TO_CLAIM, error->message);
+ g_error_free (error);
+ } else {
+ g_warning ("Failed to acquire %s", NAME_TO_CLAIM);
+ }
+ goto out;
+ }
+
+ if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+ if (error != NULL) {
+ g_warning ("Failed to acquire %s: %s", NAME_TO_CLAIM, error->message);
+ g_error_free (error);
+ } else {
+ g_warning ("Failed to acquire %s", NAME_TO_CLAIM);
+ }
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ return ret;
+}
+
+static GMainLoop *loop;
+
+void main_quit (void);
+
+void
+main_quit (void)
+{
+ g_main_loop_quit (loop);
+}
+
+int
+main (int argc, char **argv)
+{
+ PrinterConfigDaemon *daemon;
+ GError *error;
+ DBusGProxy *bus_proxy;
+ DBusGConnection *bus;
+ int ret = 1;
+
+ g_type_init ();
+
+ error = NULL;
+ bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (bus == NULL) {
+ g_warning ("Couldn't connect to system bus: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ bus_proxy = dbus_g_proxy_new_for_name (bus,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS);
+ if (bus_proxy == NULL) {
+ g_warning ("Could not construct bus_proxy object; bailing out");
+ goto out;
+ }
+
+ if (!acquire_name_on_proxy (bus_proxy) ) {
+ g_warning ("Could not acquire name; bailing out");
+ goto out;
+ }
+
+ g_debug ("Starting printer-config-daemon version %s", VERSION);
+
+ daemon = printer_config_daemon_new ();
+
+ if (daemon == NULL) {
+ goto out;
+ }
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ g_main_loop_run (loop);
+
+ g_object_unref (daemon);
+ g_main_loop_unref (loop);
+ ret = 0;
+
+ out:
+ return ret;
+}
diff -up system-config-printer-1.1.10/udev/udev-add-printer.525e996 system-config-printer-1.1.10/udev/udev-add-printer
--- system-config-printer-1.1.10/udev/udev-add-printer.525e996 2009-07-22 13:22:49.000000000 +0100
+++ system-config-printer-1.1.10/udev/udev-add-printer 2009-07-26 19:01:45.703480670 +0100
@@ -27,17 +27,28 @@ import sys
import traceback
from syslog import *
-def create_queue (c, name, device_uri, ppdname, info):
+def create_queue (c, printers, name, device_uri, ppdname, info):
# Make sure the name is unique.
- printers = cupshelpers.getPrinters (c)
- names = printers.keys ()
- if name in names:
- suffix = 2
- while (name + "-" + str (suffix)) in names:
+ namel = unicode (name.lower ())
+ unique = False
+ suffix = 1
+ while not unique:
+ unique = True
+ for printer in printers.values ():
+ if (not printer.discovered and
+ ((suffix == 1 and printer.name.lower () == namel) or
+ (suffix > 1 and
+ printer.name.lower () == namel + "-" + str (suffix)))):
+ unique = False
+ break
+
+ if not unique:
suffix += 1
if suffix == 100:
break
- name += str (suffix)
+
+ if suffix > 1:
+ name += "-" + str (suffix)
c.addPrinter (name,
device=device_uri,
@@ -45,14 +56,19 @@ def create_queue (c, name, device_uri, p
info=info,
location=os.uname ()[1])
cupshelpers.activateNewPrinter (c, name)
+ return name
-def add_queue (device_id, device_uris, is_fax=False):
+def add_queue (device_id, device_uris, fax_basename=False):
"""
Create a CUPS queue.
+
+ device_id: the IEEE 1284 Device ID of the device to add a queue for.
+ device_uris: device URIs, best first, for this device
+ fax_basename: False if this is not a fax queue, else name prefix
"""
syslog (LOG_DEBUG, "add_queue: URIs=%s" % device_uris)
- if is_fax:
+ if fax_basename != False:
notification = None
else:
try:
@@ -83,34 +99,22 @@ def add_queue (device_id, device_uris, i
name = name.replace ("/", "-")
name = name.replace ("#", "-")
- create_queue (c, name, device_uris[0], ppdname,
- "%s %s" % (id_dict["MFG"], id_dict["MDL"]))
+ if fax_basename != False:
+ name = fax_basename + "-" + name
- if not is_fax:
+ printers = cupshelpers.getPrinters (c)
+ uniquename = create_queue (c, printers, name, device_uris[0], ppdname,
+ "%s %s" % (id_dict["MFG"], id_dict["MDL"]))
+
+ if fax_basename == False:
# Look for a corresponding fax queue. We can only
# identify these by looking for device URIs that are the
# same as this one but with a different scheme. If we
# find one whose scheme ends in "fax", use that as a fax
# queue. Note that the HPLIP backends do follow this
# pattern (hp and hpfax).
- not_fax_schemes=["beh",
- "bluetooth",
- "http",
- "https",
- "ipp",
- "lpd",
- "parallel",
- "serial",
- "smb",
- "snmp",
- "socket",
- "scsi",
- "usb"]
- devices = c.getDevices (exclude_schemes=not_fax_schemes)
- for uri, device_dict in devices.iteritems ():
- if uri in device_uris:
- continue
-
+ used_uris = map (lambda x: x.device_uri, printers.values ())
+ for uri in device_uris[1:]:
if uri.find (":") == -1:
continue
@@ -119,17 +123,34 @@ def add_queue (device_id, device_uris, i
# Now see if the non-scheme parts of the URI match
# any of the URIs we were given.
for each_uri in device_uris:
+ if each_uri == uri:
+ continue
(s, device_uri_rest) = each_uri.split (":", 1)
if rest == device_uri_rest:
+ # This one matches. Check there is not
+ # already a queue using this URI.
+ if uri in used_uris:
+ break
+
+ try:
+ devices = c.getDevices(include_schemes=[scheme])
+ except TypeError:
+ # include_schemes requires pycups 1.9.46
+ devices = c.getDevices ()
+
+ device_dict = devices.get (uri)
+ if device_dict == None:
+ break
+
add_queue (device_dict.get ("device-id", ""),
- [uri], is_fax=True)
+ [uri], fax_basename=uniquename)
else:
# Not an exact match.
- name = device_uris[0]
+ uniquename = device_uris[0]
if notification:
try:
- notification.NewPrinter (status, name, id_dict["MFG"],
+ notification.NewPrinter (status, uniquename, id_dict["MFG"],
id_dict["MDL"], id_dict["DES"],
reduce(lambda x, y: x + ',' + y,
id_dict["CMD"]))
diff -up system-config-printer-1.1.10/udev/udev-configure-printer.c.525e996 system-config-printer-1.1.10/udev/udev-configure-printer.c
diff -up /dev/null system-config-printer-1.1.10/udev/udev-usb-printer.c
--- /dev/null 2009-07-26 12:31:15.305032486 +0100
+++ system-config-printer-1.1.10/udev/udev-usb-printer.c 2009-07-26 19:01:45.711481093 +0100
@@ -0,0 +1,454 @@
+/* -*- Mode: C; c-file-style: "gnu" -*-
+ * udev-usb-printer - a udev callout to configure print queues
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Author: Tim Waugh <twaugh@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * The protocol for this program is:
+ *
+ * udev-usb-printer add {DEVPATH}
+ * udev-usb-printer remove {DEVPATH}
+ *
+ * where DEVPATH is the path (%p) of the device
+ */
+
+#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE 1
+
+#include <dbus/dbus-glib-bindings.h>
+#include <fcntl.h>
+#include <libudev.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <usb.h>
+
+#include "printer-config-client-bindings.h"
+
+struct device_id
+{
+ char *full_device_id;
+ char *mfg;
+ char *mdl;
+ char *sern;
+};
+
+static void
+free_device_id (struct device_id *id)
+{
+ free (id->full_device_id);
+ free (id->mfg);
+ free (id->mdl);
+ free (id->sern);
+}
+
+static void
+parse_device_id (const char *device_id,
+ struct device_id *id)
+{
+ char *fieldname;
+ char *start, *end;
+ size_t len;
+
+ len = strlen (device_id);
+ if (len == 0)
+ return;
+
+ if (device_id[len - 1] == '\n')
+ len--;
+
+ id->full_device_id = malloc (len + 1);
+ fieldname = malloc (len + 1);
+ if (!id->full_device_id || !fieldname)
+ {
+ syslog (LOG_ERR, "out of memory");
+ exit (1);
+ }
+
+ memcpy (id->full_device_id, device_id, len);
+ id->full_device_id[len] = '\0';
+ fieldname[0] = '\0';
+ start = id->full_device_id;
+ while (*start != '\0')
+ {
+ /* New field. */
+
+ end = start;
+ while (*end != '\0' && *end != ':')
+ end++;
+
+ if (*end == '\0')
+ break;
+
+ len = end - start;
+ memcpy (fieldname, start, len);
+ fieldname[len] = '\0';
+
+ start = end + 1;
+ while (*end != '\0' && *end != ';')
+ end++;
+
+ len = end - start;
+
+ if (!id->mfg &&
+ (!strncasecmp (fieldname, "MANUFACTURER", 12) ||
+ !strncasecmp (fieldname, "MFG", 3)))
+ id->mfg = strndup (start, len);
+ else if (!id->mdl &&
+ (!strncasecmp (fieldname, "MODEL", 5) ||
+ !strncasecmp (fieldname, "MDL", 3)))
+ id->mdl = strndup (start, len);
+ else if (!id->sern &&
+ (!strncasecmp (fieldname, "SERIALNUMBER", 12) ||
+ !strncasecmp (fieldname, "SERN", 4) ||
+ !strncasecmp (fieldname, "SN", 2)))
+ id->sern = strndup (start, len);
+
+ if (*end != '\0')
+ start = end + 1;
+ }
+
+ free (fieldname);
+}
+
+static char *
+syspath_from_devpath (struct udev *udev, const char *devpath)
+{
+ const char *sys;
+ char *syspath;
+ size_t syslen, devpathlen = strlen (devpath);
+ sys = udev_get_sys_path (udev);
+ syslen = strlen (sys);
+ syspath = malloc (syslen + devpathlen + 1);
+ if (syspath == NULL)
+ return NULL;
+
+ memcpy (syspath, sys, syslen);
+ memcpy (syspath + syslen, devpath, devpathlen);
+ syspath[syslen + devpathlen] = '\0';
+ return syspath;
+}
+
+static char *
+device_id_from_devpath (const char *devpath,
+ struct device_id *id,
+ char *usbserial, size_t usbseriallen)
+{
+ struct udev *udev;
+ struct udev_device *dev, *parent_dev = NULL;
+ const char *idVendorStr, *idProductStr, *serial;
+ char *end;
+ unsigned long idVendor, idProduct;
+ struct usb_bus *bus;
+ struct usb_dev_handle *handle = NULL;
+ char ieee1284_id[1024];
+ const char *device_id = NULL;
+ char *syspath;
+ int conf = 0, iface = 0;
+ int got = 0;
+ char *usb_device_devpath;
+
+ id->full_device_id = id->mfg = id->mdl = id->sern = NULL;
+
+ udev = udev_new ();
+ if (udev == NULL)
+ {
+ syslog (LOG_ERR, "udev_new failed");
+ exit (1);
+ }
+
+ syspath = syspath_from_devpath (udev, devpath);
+ if (syspath == NULL)
+ {
+ udev_unref (udev);
+ exit (1);
+ }
+
+ dev = udev_device_new_from_syspath (udev, syspath);
+ if (dev == NULL)
+ {
+ udev_device_unref (dev);
+ udev_unref (udev);
+ syslog (LOG_ERR, "unable to access %s", syspath);
+ free (syspath);
+ exit (1);
+ }
+
+ free (syspath);
+ parent_dev = udev_device_get_parent_with_subsystem_devtype (dev,
+ "usb",
+ "usb_device");
+ if (!parent_dev)
+ {
+ udev_unref (udev);
+ syslog (LOG_ERR, "Failed to get parent");
+ exit (1);
+ }
+
+ usb_device_devpath = strdup (udev_device_get_devpath (parent_dev));
+ syslog (LOG_DEBUG, "parent devpath is %s", usb_device_devpath);
+
+ serial = udev_device_get_sysattr_value (parent_dev, "serial");
+ if (serial)
+ {
+ strncpy (usbserial, serial, usbseriallen);
+ usbserial[usbseriallen - 1] = '\0';
+ }
+ else
+ usbserial[0] = '\0';
+
+ /* See if we were triggered by a usblp add event. */
+ device_id = udev_device_get_sysattr_value (dev, "device/ieee1284_id");
+ if (device_id)
+ {
+ got = 1;
+ goto got_deviceid;
+ }
+
+ /* This is a low-level USB device. Use libusb to fetch the Device ID. */
+ idVendorStr = udev_device_get_sysattr_value (parent_dev, "idVendor");
+ idProductStr = udev_device_get_sysattr_value (parent_dev, "idProduct");
+
+ if (!idVendorStr || !idProductStr)
+ {
+ udev_device_unref (dev);
+ udev_unref (udev);
+ syslog (LOG_ERR, "Missing sysattr %s",
+ idVendorStr ?
+ (idProductStr ? "serial" : "idProduct") : "idVendor");
+ exit (1);
+ }
+
+ idVendor = strtoul (idVendorStr, &end, 16);
+ if (end == idVendorStr)
+ {
+ syslog (LOG_ERR, "Invalid idVendor: %s", idVendorStr);
+ exit (1);
+ }
+
+ idProduct = strtoul (idProductStr, &end, 16);
+ if (end == idProductStr)
+ {
+ syslog (LOG_ERR, "Invalid idProduct: %s", idProductStr);
+ exit (1);
+ }
+
+ syslog (LOG_DEBUG, "Device vendor/product is %04zX:%04zX",
+ idVendor, idProduct);
+
+ usb_init ();
+ usb_find_busses ();
+ usb_find_devices ();
+ for (bus = usb_get_busses (); bus && !got; bus = bus->next)
+ {
+ struct usb_device *device;
+ for (device = bus->devices; device && !got; device = device->next)
+ {
+ struct usb_config_descriptor *confptr;
+ if (device->descriptor.idVendor != idVendor ||
+ device->descriptor.idProduct != idProduct ||
+ !device->config)
+ continue;
+
+ conf = 0;
+ for (confptr = device->config;
+ conf < device->descriptor.bNumConfigurations && !got;
+ conf++, confptr++)
+ {
+ struct usb_interface *ifaceptr;
+ iface = 0;
+ for (ifaceptr = confptr->interface;
+ iface < confptr->bNumInterfaces && !got;
+ iface++, ifaceptr++)
+ {
+ struct usb_interface_descriptor *altptr;
+ int altset = 0;
+ for (altptr = ifaceptr->altsetting;
+ altset < ifaceptr->num_altsetting && !got;
+ altset++, altptr++)
+ {
+ if (altptr->bInterfaceClass == USB_CLASS_PRINTER &&
+ altptr->bInterfaceSubClass == 1)
+ {
+ int n;
+ handle = usb_open (device);
+ if (!handle)
+ {
+ syslog (LOG_DEBUG, "failed to open device");
+ continue;
+ }
+
+ n = altptr->bInterfaceNumber;
+ if (usb_claim_interface (handle, n) < 0)
+ {
+ usb_close (handle);
+ handle = NULL;
+ syslog (LOG_DEBUG, "failed to claim interface");
+ continue;
+ }
+
+ if (n != 0 && usb_claim_interface (handle, 0) < 0)
+ {
+ usb_close (handle);
+ handle = NULL;
+ syslog (LOG_DEBUG, "failed to claim interface 0");
+ continue;
+ }
+
+ n = altptr->bAlternateSetting;
+ if (usb_set_altinterface (handle, n) < 0)
+ {
+ usb_close (handle);
+ handle = NULL;
+ syslog (LOG_DEBUG, "failed set altinterface");
+ continue;
+ }
+
+ memset (ieee1284_id, '\0', sizeof (ieee1284_id));
+ if (usb_control_msg (handle,
+ USB_TYPE_CLASS |
+ USB_ENDPOINT_IN |
+ USB_RECIP_INTERFACE,
+ 0, conf, iface,
+ ieee1284_id,
+ sizeof (ieee1284_id),
+ 5000) < 0)
+ {
+ usb_close (handle);
+ handle = NULL;
+ syslog (LOG_ERR, "Failed to fetch Device ID");
+ continue;
+ }
+
+ got = 1;
+ usb_close (handle);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ got_deviceid:
+ if (got)
+ {
+ if (!device_id)
+ device_id = ieee1284_id + 2;
+ parse_device_id (device_id, id);
+ }
+
+ udev_device_unref (dev);
+ udev_unref (udev);
+ return usb_device_devpath;
+}
+
+static int
+do_add (DBusGProxy *proxy, const char *devpath)
+{
+ GError *error = NULL;
+ struct device_id id;
+ char *usb_device_devpath;
+ char usbserial[256];
+
+ syslog (LOG_DEBUG, "add %s", devpath);
+
+ usb_device_devpath = device_id_from_devpath (devpath, &id,
+ usbserial, sizeof (usbserial));
+
+ if (!id.mfg || !id.mdl)
+ {
+ syslog (LOG_ERR, "invalid or missing IEEE 1284 Device ID%s%s",
+ id.full_device_id ? " " : "",
+ id.full_device_id ? id.full_device_id : "");
+ exit (1);
+ }
+
+ syslog (LOG_DEBUG, "MFG:%s MDL:%s SERN:%s serial:%s", id.mfg, id.mdl,
+ id.sern ? id.sern : "-", usbserial);
+
+ com_redhat_PrinterConfig_usb_printer_add (proxy,
+ usb_device_devpath,
+ id.full_device_id,
+ &error);
+ free (usb_device_devpath);
+ free_device_id (&id);
+ return 0;
+}
+
+static int
+do_remove (DBusGProxy *proxy, const char *devpath)
+{
+ GError *error = NULL;
+ syslog (LOG_DEBUG, "remove %s", devpath);
+
+ com_redhat_PrinterConfig_usb_printer_remove (proxy,
+ devpath,
+ &error);
+ return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+ DBusGConnection *connection;
+ DBusGProxy *proxy;
+ GError *error = NULL;
+ int add;
+
+ g_type_init ();
+
+ if (argc != 3 ||
+ !((add = !strcmp (argv[1], "add")) ||
+ !strcmp (argv[1], "remove")))
+ {
+ fprintf (stderr,
+ "Syntax: %s add {USB device path}\n"
+ " %s remove {USB device path}\n",
+ argv[0], argv[0]);
+ return 1;
+ }
+
+ openlog ("udev-usb-printer", 0, LOG_LPR);
+
+ connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (connection == NULL)
+ {
+ syslog (LOG_ERR, "unable to connect to D-Bus: %s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ proxy = dbus_g_proxy_new_for_name (connection,
+ "com.redhat.PrinterConfig",
+ "/com/redhat/PrinterConfig",
+ "com.redhat.PrinterConfig");
+
+ if (add)
+ do_add (proxy, argv[2]);
+ else
+ do_remove (proxy, argv[2]);
+
+ g_object_unref (proxy);
+ return 0;
+}