From e22999a35a46a9436d16699bd1f2e3bcb242af51 Mon Sep 17 00:00:00 2001 From: Tim Waugh Date: Sun, 26 Jul 2009 18:43:24 +0000 Subject: [PATCH] - Split out D-Bus service for udev helper. --- system-config-printer-525e996.patch | 2568 +++++++++++++++++++++++++++ system-config-printer-a05bd9c.patch | 322 ---- system-config-printer.spec | 21 +- 3 files changed, 2585 insertions(+), 326 deletions(-) create mode 100644 system-config-printer-525e996.patch delete mode 100644 system-config-printer-a05bd9c.patch diff --git a/system-config-printer-525e996.patch b/system-config-printer-525e996.patch new file mode 100644 index 0000000..7b9a8b8 --- /dev/null +++ b/system-config-printer-525e996.patch @@ -0,0 +1,2568 @@ +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 . ++# ++# 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 .])], ++ [$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 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +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 @@ ++ ++ ++ ++ ++ ++ ++ The PrinterConfig service is available via the ++ system message bus. To access the service, use the ++ com.redhat.PrinterConfig interface ++ on the /com/redhat/PrinterConfig ++ object on the D-Bus system bus service with the well-known ++ name ++ com.redhat.PrinterConfig. ++ ++ It is intended to be used by udev when USB printers ++ are connected and disconnected. ++ ++ ++ ++ ++ ++ ++ ++ ++ The udev devpath of the device with subsystem ++ usb and devtype usb_device referring to the USB printer ++ device. ++ ++ ++ ++ ++ ++ ++ ++ The IEEE 1284 Device ID (not including length ++ field) of the device. ++ ++ ++ ++ ++ ++ ++ This method informs the PrinterConfig service that ++ a USB printer is now connected. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ The udev devpath of the device with subsystem ++ usb and devtype usb_device referring to the USB printer ++ device. ++ ++ ++ ++ ++ ++ ++ This method informs the PrinterConfig service that ++ a USB printer has been disconnected. ++ ++ ++ ++ ++ +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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ * ++ * 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 ++#include ++ ++#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 ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define DBUS_API_SUBJECT_TO_CHANGE ++#include ++#include ++ ++#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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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; ++} diff --git a/system-config-printer-a05bd9c.patch b/system-config-printer-a05bd9c.patch deleted file mode 100644 index 6d7e2ca..0000000 --- a/system-config-printer-a05bd9c.patch +++ /dev/null @@ -1,322 +0,0 @@ -diff --git a/udev/udev-add-printer b/udev/udev-add-printer -index 03d301e..2e4d933 100755 ---- a/udev/udev-add-printer -+++ b/udev/udev-add-printer -@@ -27,13 +27,11 @@ import sys - import traceback - from syslog import * - --def create_queue (c, name, device_uri, ppdname, info): -+def create_queue (c, printernames, name, device_uri, ppdname, info): - # Make sure the name is unique. -- printers = cupshelpers.getPrinters (c) -- names = printers.keys () -- if name in names: -+ if name in printernames: - suffix = 2 -- while (name + "-" + str (suffix)) in names: -+ while (name + "-" + str (suffix)) in printernames: - suffix += 1 - if suffix == 100: - break -@@ -83,7 +81,10 @@ def add_queue (device_id, device_uris, is_fax=False): - name = name.replace ("/", "-") - name = name.replace ("#", "-") - -- create_queue (c, name, device_uris[0], ppdname, -+ printers = cupshelpers.getPrinters (c) -+ printernames = printers.keys () -+ -+ create_queue (c, printernames, name, device_uris[0], ppdname, - "%s %s" % (id_dict["MFG"], id_dict["MDL"])) - - if not is_fax: -@@ -93,24 +94,8 @@ def add_queue (device_id, device_uris, is_fax=False): - # 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,8 +104,20 @@ def add_queue (device_id, device_uris, is_fax=False): - # 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 -+ -+ devices = c.getDevices (include_schemes=[scheme]) -+ device_dict = devices.get (uri) -+ if device_dict == None: -+ break -+ - add_queue (device_dict.get ("device-id", ""), - [uri], is_fax=True) - else: -diff --git a/udev/udev-configure-printer.c b/udev/udev-configure-printer.c -index a3c87dc..37a8682 100644 ---- a/udev/udev-configure-printer.c -+++ b/udev/udev-configure-printer.c -@@ -708,7 +708,7 @@ cupsDoRequestOrDie (http_t *http, - - static int - find_matching_device_uris (struct device_id *id, -- const char *serial, -+ const char *usbserial, - struct device_uris *uris, - const char *devpath) - { -@@ -716,21 +716,34 @@ find_matching_device_uris (struct device_id *id, - 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 = 0; -- uris->uri = NULL; -- -- uris_noserial.n_uris = 0; -- uris_noserial.uri = NULL; -+ 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, "include-schemes", -- sizeof (device_uri_types) / sizeof(device_uri_types[0]), -- NULL, device_uri_types); -+ ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "exclude-schemes", -+ sizeof (exclude_schemes) / sizeof(exclude_schemes[0]), -+ NULL, exclude_schemes); - - answer = cupsDoRequestOrDie (cups, request, "/"); - httpClose (cups); -@@ -757,6 +770,28 @@ find_matching_device_uris (struct device_id *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)) -@@ -803,11 +838,11 @@ find_matching_device_uris (struct device_id *id, - match = 1; - } - -- if (!match && strlen (serial) >= 12) -+ if (!match && usbserial[0] != '\0') - { - if (!id->sern) - { -- if (this_id.sern && !strcmp (serial, this_id.sern)) -+ if (this_id.sern && !strcmp (usbserial, this_id.sern)) - { - syslog (LOG_DEBUG, - "SERN field matches USB serial number"); -@@ -819,10 +854,11 @@ find_matching_device_uris (struct device_id *id, - { - char *saveptr, *uri = strdup (device_uri); - const char *token; -- for (token = strtok_r (uri, "?=&", &saveptr); -+ const char *sep = "?=&/"; -+ for (token = strtok_r (uri, sep, &saveptr); - token; -- token = strtok_r (NULL, "?=&", &saveptr)) -- if (!strcmp (token, serial)) -+ token = strtok_r (NULL, sep, &saveptr)) -+ if (!strcmp (token, usbserial)) - { - syslog (LOG_DEBUG, "URI contains USB serial number"); - match = 1; -@@ -886,14 +922,56 @@ find_matching_device_uris (struct device_id *id, - uris->uri = old; - else - { -- size_t i; - 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) - { - struct usb_uri_map *map = read_usb_uri_map (); -@@ -1048,12 +1126,12 @@ do_add (const char *cmd, const char *devpath) - struct device_id id; - struct device_uris device_uris; - char *usb_device_devpath; -- char serial[256]; -+ char usbserial[256]; - - syslog (LOG_DEBUG, "add %s", devpath); - - usb_device_devpath = device_id_from_devpath (devpath, &id, -- serial, sizeof (serial)); -+ usbserial, sizeof (usbserial)); - if (!id.mfg || !id.mdl) - { - syslog (LOG_ERR, "invalid or missing IEEE 1284 Device ID%s%s", -@@ -1063,28 +1141,31 @@ do_add (const char *cmd, const char *devpath) - } - - syslog (LOG_DEBUG, "MFG:%s MDL:%s SERN:%s serial:%s", id.mfg, id.mdl, -- id.sern ? id.sern : "-", serial); -- -- if ((pid = fork ()) == -1) -- syslog (LOG_ERR, "Failed to fork process"); -- else if (pid != 0) -- /* Parent. */ -- exit (0); -- -- close (STDIN_FILENO); -- close (STDOUT_FILENO); -- close (STDERR_FILENO); -- f = open ("/dev/null", O_RDWR); -- if (f != STDIN_FILENO) -- dup2 (f, STDIN_FILENO); -- if (f != STDOUT_FILENO) -- dup2 (f, STDOUT_FILENO); -- if (f != STDERR_FILENO) -- dup2 (f, STDERR_FILENO); -- -- setsid (); -- -- find_matching_device_uris (&id, serial, &device_uris, usb_device_devpath); -+ id.sern ? id.sern : "-", usbserial); -+ -+ if (getenv ("DEBUG") == NULL) -+ { -+ if ((pid = fork ()) == -1) -+ syslog (LOG_ERR, "Failed to fork process"); -+ else if (pid != 0) -+ /* Parent. */ -+ exit (0); -+ -+ close (STDIN_FILENO); -+ close (STDOUT_FILENO); -+ close (STDERR_FILENO); -+ f = open ("/dev/null", O_RDWR); -+ if (f != STDIN_FILENO) -+ dup2 (f, STDIN_FILENO); -+ if (f != STDOUT_FILENO) -+ dup2 (f, STDOUT_FILENO); -+ if (f != STDERR_FILENO) -+ dup2 (f, STDERR_FILENO); -+ -+ setsid (); -+ } -+ -+ find_matching_device_uris (&id, usbserial, &device_uris, usb_device_devpath); - free (usb_device_devpath); - if (device_uris.n_uris == 0) - { diff --git a/system-config-printer.spec b/system-config-printer.spec index 3a46505..ed1f981 100644 --- a/system-config-printer.spec +++ b/system-config-printer.spec @@ -7,14 +7,14 @@ Summary: A printer administration tool Name: system-config-printer Version: 1.1.10 -Release: 3%{?dist} +Release: 4%{?dist} License: GPLv2+ URL: http://cyberelk.net/tim/software/system-config-printer/ Group: System Environment/Base Source0: http://cyberelk.net/tim/data/system-config-printer/1.1/system-config-printer-%{version}.tar.bz2 Source1: http://cyberelk.net/tim/data/pycups/pycups-%{pycups_version}.tar.bz2 Source2: http://cyberelk.net/tim/data/pysmbc/pysmbc-%{pysmbc_version}.tar.bz2 -Patch1: system-config-printer-a05bd9c.patch +Patch1: system-config-printer-525e996.patch BuildRequires: cups-devel >= 1.2 BuildRequires: python-devel >= 2.4 @@ -25,6 +25,9 @@ BuildRequires: intltool BuildRequires: libusb-devel, libudev-devel BuildRequires: xmlto BuildRequires: epydoc + +BuildRequires: automake, autoconf + BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Requires: pygtk2 >= 2.4.0, pygtk2-libglade @@ -76,7 +79,9 @@ printers. %prep %setup -q -a 1 -a 2 -%patch1 -p1 -b .a05bd9c +%patch1 -p1 -b .525e996 +automake --copy --add-missing +autoconf %build %configure --with-udev-rules @@ -127,7 +132,12 @@ rm -rf %buildroot %files udev %defattr(-,root,root,-) %{_sysconfdir}/udev/rules.d/*.rules -/lib/udev/* +%{_sysconfdir}/dbus-1/system.d/*.conf +%{_libexecdir}/printer-config-daemon +%{_libexecdir}/udev-add-printer +%{_datadir}/dbus-1/interfaces/*.xml +%{_datadir}/dbus-1/system-services/*.service +/lib/udev/udev-usb-printer %files %defattr(-,root,root,-) @@ -184,6 +194,9 @@ rm -rf %buildroot exit 0 %changelog +* Sun Jul 26 2009 Tim Waugh 1.1.10-4 +- Split out D-Bus service for udev helper. + * Fri Jul 24 2009 Tim Waugh 1.1.10-3 - Removed gnome-packagekit dependency. The presence of gpk-install-package-name is detected at run-time, and the program