diff options
author | Didier Raboud <odyx@debian.org> | 2012-10-25 21:01:59 +0200 |
---|---|---|
committer | Didier Raboud <odyx@debian.org> | 2012-10-25 21:01:59 +0200 |
commit | b1469ea9a3a5b5b652dcd733f7d419dd1fdfe844 (patch) | |
tree | 5531f93b7c293790552944e6981a1745baaf7226 /backend | |
parent | 4ddc0bb6de85d409501de745bf1d3df896b15c02 (diff) | |
download | cups-b1469ea9a3a5b5b652dcd733f7d419dd1fdfe844.tar.gz |
Imported Upstream version 1.5.3upstream/1.5.3
Diffstat (limited to 'backend')
-rw-r--r-- | backend/Dependencies | 110 | ||||
-rw-r--r-- | backend/Makefile | 10 | ||||
-rw-r--r-- | backend/dnssd.c | 23 | ||||
-rw-r--r-- | backend/ipp.c | 411 | ||||
-rw-r--r-- | backend/lpd.c | 104 | ||||
-rw-r--r-- | backend/runloop.c | 34 | ||||
-rw-r--r-- | backend/snmp-supplies.c | 58 | ||||
-rw-r--r-- | backend/usb-libusb.c | 1094 | ||||
-rw-r--r-- | backend/usb.c | 10 |
9 files changed, 1375 insertions, 479 deletions
diff --git a/backend/Dependencies b/backend/Dependencies index 4f99c17e..afc21fa9 100644 --- a/backend/Dependencies +++ b/backend/Dependencies @@ -3,77 +3,83 @@ ipp.o: backend-private.h ../cups/cups-private.h ../cups/cups.h ../cups/file.h ipp.o: ../cups/versioning.h ../cups/ipp.h ../cups/http.h ../cups/array.h ipp.o: ../cups/language.h ../cups/string-private.h ../config.h -ipp.o: ../cups/debug-private.h ../cups/ppd-private.h ../cups/ppd.h -ipp.o: ../cups/cups.h ../cups/pwg-private.h ../cups/http-private.h -ipp.o: ../cups/http.h ../cups/md5-private.h ../cups/ipp-private.h -ipp.o: ../cups/ipp.h ../cups/language-private.h ../cups/transcode.h -ipp.o: ../cups/thread-private.h ../cups/snmp-private.h ../cups/backend.h -ipp.o: ../cups/sidechannel.h ../cups/array-private.h +ipp.o: ../cups/debug-private.h ../cups/versioning.h ../cups/ppd-private.h +ipp.o: ../cups/ppd.h ../cups/cups.h ../cups/pwg-private.h +ipp.o: ../cups/http-private.h ../cups/http.h ../cups/md5-private.h +ipp.o: ../cups/ipp-private.h ../cups/ipp.h ../cups/language-private.h +ipp.o: ../cups/transcode.h ../cups/thread-private.h ../cups/snmp-private.h +ipp.o: ../cups/backend.h ../cups/sidechannel.h ../cups/array-private.h +ipp.o: ../cups/array.h lpd.o: ../cups/http-private.h ../config.h ../cups/http.h lpd.o: ../cups/md5-private.h ../cups/ipp-private.h ../cups/ipp.h lpd.o: backend-private.h ../cups/cups-private.h ../cups/cups.h ../cups/file.h lpd.o: ../cups/versioning.h ../cups/ipp.h ../cups/http.h ../cups/array.h lpd.o: ../cups/language.h ../cups/string-private.h ../cups/debug-private.h -lpd.o: ../cups/ppd-private.h ../cups/ppd.h ../cups/cups.h -lpd.o: ../cups/pwg-private.h ../cups/http-private.h +lpd.o: ../cups/versioning.h ../cups/ppd-private.h ../cups/ppd.h +lpd.o: ../cups/cups.h ../cups/pwg-private.h ../cups/http-private.h lpd.o: ../cups/language-private.h ../cups/transcode.h lpd.o: ../cups/thread-private.h ../cups/snmp-private.h ../cups/backend.h lpd.o: ../cups/sidechannel.h dnssd.o: backend-private.h ../cups/cups-private.h ../cups/cups.h dnssd.o: ../cups/file.h ../cups/versioning.h ../cups/ipp.h ../cups/http.h dnssd.o: ../cups/array.h ../cups/language.h ../cups/string-private.h -dnssd.o: ../config.h ../cups/debug-private.h ../cups/ppd-private.h -dnssd.o: ../cups/ppd.h ../cups/cups.h ../cups/pwg-private.h -dnssd.o: ../cups/http-private.h ../cups/http.h ../cups/md5-private.h -dnssd.o: ../cups/ipp-private.h ../cups/ipp.h ../cups/language-private.h -dnssd.o: ../cups/transcode.h ../cups/thread-private.h ../cups/snmp-private.h -dnssd.o: ../cups/backend.h ../cups/sidechannel.h ../cups/array.h +dnssd.o: ../config.h ../cups/debug-private.h ../cups/versioning.h +dnssd.o: ../cups/ppd-private.h ../cups/ppd.h ../cups/cups.h +dnssd.o: ../cups/pwg-private.h ../cups/http-private.h ../cups/http.h +dnssd.o: ../cups/md5-private.h ../cups/ipp-private.h ../cups/ipp.h +dnssd.o: ../cups/language-private.h ../cups/transcode.h +dnssd.o: ../cups/thread-private.h ../cups/snmp-private.h ../cups/backend.h +dnssd.o: ../cups/sidechannel.h ../cups/array.h parallel.o: backend-private.h ../cups/cups-private.h ../cups/cups.h parallel.o: ../cups/file.h ../cups/versioning.h ../cups/ipp.h ../cups/http.h parallel.o: ../cups/array.h ../cups/language.h ../cups/string-private.h -parallel.o: ../config.h ../cups/debug-private.h ../cups/ppd-private.h -parallel.o: ../cups/ppd.h ../cups/cups.h ../cups/pwg-private.h -parallel.o: ../cups/http-private.h ../cups/http.h ../cups/md5-private.h -parallel.o: ../cups/ipp-private.h ../cups/ipp.h ../cups/language-private.h -parallel.o: ../cups/transcode.h ../cups/thread-private.h -parallel.o: ../cups/snmp-private.h ../cups/backend.h ../cups/sidechannel.h +parallel.o: ../config.h ../cups/debug-private.h ../cups/versioning.h +parallel.o: ../cups/ppd-private.h ../cups/ppd.h ../cups/cups.h +parallel.o: ../cups/pwg-private.h ../cups/http-private.h ../cups/http.h +parallel.o: ../cups/md5-private.h ../cups/ipp-private.h ../cups/ipp.h +parallel.o: ../cups/language-private.h ../cups/transcode.h +parallel.o: ../cups/thread-private.h ../cups/snmp-private.h ../cups/backend.h +parallel.o: ../cups/sidechannel.h serial.o: backend-private.h ../cups/cups-private.h ../cups/cups.h serial.o: ../cups/file.h ../cups/versioning.h ../cups/ipp.h ../cups/http.h serial.o: ../cups/array.h ../cups/language.h ../cups/string-private.h -serial.o: ../config.h ../cups/debug-private.h ../cups/ppd-private.h -serial.o: ../cups/ppd.h ../cups/cups.h ../cups/pwg-private.h -serial.o: ../cups/http-private.h ../cups/http.h ../cups/md5-private.h -serial.o: ../cups/ipp-private.h ../cups/ipp.h ../cups/language-private.h -serial.o: ../cups/transcode.h ../cups/thread-private.h ../cups/snmp-private.h -serial.o: ../cups/backend.h ../cups/sidechannel.h +serial.o: ../config.h ../cups/debug-private.h ../cups/versioning.h +serial.o: ../cups/ppd-private.h ../cups/ppd.h ../cups/cups.h +serial.o: ../cups/pwg-private.h ../cups/http-private.h ../cups/http.h +serial.o: ../cups/md5-private.h ../cups/ipp-private.h ../cups/ipp.h +serial.o: ../cups/language-private.h ../cups/transcode.h +serial.o: ../cups/thread-private.h ../cups/snmp-private.h ../cups/backend.h +serial.o: ../cups/sidechannel.h snmp.o: backend-private.h ../cups/cups-private.h ../cups/cups.h snmp.o: ../cups/file.h ../cups/versioning.h ../cups/ipp.h ../cups/http.h snmp.o: ../cups/array.h ../cups/language.h ../cups/string-private.h -snmp.o: ../config.h ../cups/debug-private.h ../cups/ppd-private.h -snmp.o: ../cups/ppd.h ../cups/cups.h ../cups/pwg-private.h -snmp.o: ../cups/http-private.h ../cups/http.h ../cups/md5-private.h -snmp.o: ../cups/ipp-private.h ../cups/ipp.h ../cups/language-private.h -snmp.o: ../cups/transcode.h ../cups/thread-private.h ../cups/snmp-private.h -snmp.o: ../cups/backend.h ../cups/sidechannel.h ../cups/array.h -snmp.o: ../cups/file.h ../cups/http-private.h +snmp.o: ../config.h ../cups/debug-private.h ../cups/versioning.h +snmp.o: ../cups/ppd-private.h ../cups/ppd.h ../cups/cups.h +snmp.o: ../cups/pwg-private.h ../cups/http-private.h ../cups/http.h +snmp.o: ../cups/md5-private.h ../cups/ipp-private.h ../cups/ipp.h +snmp.o: ../cups/language-private.h ../cups/transcode.h +snmp.o: ../cups/thread-private.h ../cups/snmp-private.h ../cups/backend.h +snmp.o: ../cups/sidechannel.h ../cups/array.h ../cups/file.h +snmp.o: ../cups/http-private.h socket.o: ../cups/http-private.h ../config.h ../cups/http.h socket.o: ../cups/md5-private.h ../cups/ipp-private.h ../cups/ipp.h socket.o: backend-private.h ../cups/cups-private.h ../cups/cups.h socket.o: ../cups/file.h ../cups/versioning.h ../cups/ipp.h ../cups/http.h socket.o: ../cups/array.h ../cups/language.h ../cups/string-private.h -socket.o: ../cups/debug-private.h ../cups/ppd-private.h ../cups/ppd.h -socket.o: ../cups/cups.h ../cups/pwg-private.h ../cups/http-private.h -socket.o: ../cups/language-private.h ../cups/transcode.h -socket.o: ../cups/thread-private.h ../cups/snmp-private.h ../cups/backend.h -socket.o: ../cups/sidechannel.h +socket.o: ../cups/debug-private.h ../cups/versioning.h ../cups/ppd-private.h +socket.o: ../cups/ppd.h ../cups/cups.h ../cups/pwg-private.h +socket.o: ../cups/http-private.h ../cups/language-private.h +socket.o: ../cups/transcode.h ../cups/thread-private.h ../cups/snmp-private.h +socket.o: ../cups/backend.h ../cups/sidechannel.h test1284.o: ../cups/string-private.h ../config.h ieee1284.c backend-private.h test1284.o: ../cups/cups-private.h ../cups/cups.h ../cups/file.h test1284.o: ../cups/versioning.h ../cups/ipp.h ../cups/http.h ../cups/array.h test1284.o: ../cups/language.h ../cups/string-private.h -test1284.o: ../cups/debug-private.h ../cups/ppd-private.h ../cups/ppd.h -test1284.o: ../cups/cups.h ../cups/pwg-private.h ../cups/http-private.h -test1284.o: ../cups/http.h ../cups/md5-private.h ../cups/ipp-private.h -test1284.o: ../cups/ipp.h ../cups/language-private.h ../cups/transcode.h +test1284.o: ../cups/debug-private.h ../cups/versioning.h +test1284.o: ../cups/ppd-private.h ../cups/ppd.h ../cups/cups.h +test1284.o: ../cups/pwg-private.h ../cups/http-private.h ../cups/http.h +test1284.o: ../cups/md5-private.h ../cups/ipp-private.h ../cups/ipp.h +test1284.o: ../cups/language-private.h ../cups/transcode.h test1284.o: ../cups/thread-private.h ../cups/snmp-private.h ../cups/backend.h test1284.o: ../cups/sidechannel.h testbackend.o: ../cups/string-private.h ../config.h ../cups/cups.h @@ -84,18 +90,18 @@ testsupplies.o: backend-private.h ../cups/cups-private.h ../cups/cups.h testsupplies.o: ../cups/file.h ../cups/versioning.h ../cups/ipp.h testsupplies.o: ../cups/http.h ../cups/array.h ../cups/language.h testsupplies.o: ../cups/string-private.h ../config.h ../cups/debug-private.h -testsupplies.o: ../cups/ppd-private.h ../cups/ppd.h ../cups/cups.h -testsupplies.o: ../cups/pwg-private.h ../cups/http-private.h ../cups/http.h -testsupplies.o: ../cups/md5-private.h ../cups/ipp-private.h ../cups/ipp.h -testsupplies.o: ../cups/language-private.h ../cups/transcode.h +testsupplies.o: ../cups/versioning.h ../cups/ppd-private.h ../cups/ppd.h +testsupplies.o: ../cups/cups.h ../cups/pwg-private.h ../cups/http-private.h +testsupplies.o: ../cups/http.h ../cups/md5-private.h ../cups/ipp-private.h +testsupplies.o: ../cups/ipp.h ../cups/language-private.h ../cups/transcode.h testsupplies.o: ../cups/thread-private.h ../cups/snmp-private.h testsupplies.o: ../cups/backend.h ../cups/sidechannel.h usb.o: backend-private.h ../cups/cups-private.h ../cups/cups.h ../cups/file.h usb.o: ../cups/versioning.h ../cups/ipp.h ../cups/http.h ../cups/array.h usb.o: ../cups/language.h ../cups/string-private.h ../config.h -usb.o: ../cups/debug-private.h ../cups/ppd-private.h ../cups/ppd.h -usb.o: ../cups/cups.h ../cups/pwg-private.h ../cups/http-private.h -usb.o: ../cups/http.h ../cups/md5-private.h ../cups/ipp-private.h -usb.o: ../cups/ipp.h ../cups/language-private.h ../cups/transcode.h -usb.o: ../cups/thread-private.h ../cups/snmp-private.h ../cups/backend.h -usb.o: ../cups/sidechannel.h +usb.o: ../cups/debug-private.h ../cups/versioning.h ../cups/ppd-private.h +usb.o: ../cups/ppd.h ../cups/cups.h ../cups/pwg-private.h +usb.o: ../cups/http-private.h ../cups/http.h ../cups/md5-private.h +usb.o: ../cups/ipp-private.h ../cups/ipp.h ../cups/language-private.h +usb.o: ../cups/transcode.h ../cups/thread-private.h ../cups/snmp-private.h +usb.o: ../cups/backend.h ../cups/sidechannel.h diff --git a/backend/Makefile b/backend/Makefile index 47feac9e..643ee0cd 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -1,9 +1,9 @@ # -# "$Id: Makefile 9737 2011-05-04 04:28:00Z mike $" +# "$Id: Makefile 10425 2012-04-23 17:42:12Z mike $" # # Backend makefile for CUPS. # -# Copyright 2007-2011 by Apple Inc. +# Copyright 2007-2012 by Apple Inc. # Copyright 1997-2007 by Easy Software Products, all rights reserved. # # These coded instructions, statements, and computer programs are the @@ -98,7 +98,7 @@ install-exec: $(INSTALLXPC) $(RM) $(SERVERBIN)/backend/$$file; \ $(LN) ipp $(SERVERBIN)/backend/$$file; \ done - if test "x$(DNSSD_BACKEND)" != x; then \ + if test "x$(DNSSD_BACKEND)" != x -a `uname` = Darwin; then \ $(RM) $(SERVERBIN)/backend/mdns; \ $(LN) $(DNSSD_BACKEND) $(SERVERBIN)/backend/mdns; \ fi @@ -268,7 +268,7 @@ socket: socket.o ../cups/$(LIBCUPS) libbackend.a usb: usb.o ../cups/$(LIBCUPS) libbackend.a echo Linking $@... - $(CC) $(LDFLAGS) -o usb usb.o libbackend.a $(LIBUSB) \ + $(CC) $(ARCHFLAGS) $(LDFLAGS) -o usb usb.o libbackend.a $(LIBUSB) \ $(BACKLIBS) $(LIBS) usb.o: usb.c usb-darwin.c usb-libusb.c usb-unix.c @@ -281,5 +281,5 @@ include Dependencies # -# End of "$Id: Makefile 9737 2011-05-04 04:28:00Z mike $". +# End of "$Id: Makefile 10425 2012-04-23 17:42:12Z mike $". # diff --git a/backend/dnssd.c b/backend/dnssd.c index f37e8123..02c78754 100644 --- a/backend/dnssd.c +++ b/backend/dnssd.c @@ -1,9 +1,9 @@ /* - * "$Id: dnssd.c 10017 2011-09-26 18:46:46Z mike $" + * "$Id: dnssd.c 10379 2012-03-23 22:16:22Z mike $" * * DNS-SD discovery backend for CUPS. * - * Copyright 2008-2011 by Apple Inc. + * Copyright 2008-2012 by Apple Inc. * * These coded instructions, statements, and computer programs are the * property of Apple Inc. and are protected by Federal copyright @@ -43,8 +43,8 @@ typedef enum { CUPS_DEVICE_PRINTER = 0, /* lpd://... */ - CUPS_DEVICE_IPP, /* ipp://... */ CUPS_DEVICE_IPPS, /* ipps://... */ + CUPS_DEVICE_IPP, /* ipp://... */ CUPS_DEVICE_FAX_IPP, /* ipp://... */ CUPS_DEVICE_PDL_DATASTREAM, /* socket://... */ CUPS_DEVICE_RIOUSBPRINT /* riousbprint://... */ @@ -84,7 +84,8 @@ static void browse_callback(DNSServiceRef sdRef, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, - const char *replyDomain, void *context); + const char *replyDomain, void *context) + __attribute__((nonnull(1,5,6,7,8))); static void browse_local_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, @@ -92,13 +93,15 @@ static void browse_local_callback(DNSServiceRef sdRef, const char *serviceName, const char *regtype, const char *replyDomain, - void *context); + void *context) + __attribute__((nonnull(1,5,6,7,8))); static int compare_devices(cups_device_t *a, cups_device_t *b); static void exec_backend(char **argv); static cups_device_t *get_device(cups_array_t *devices, const char *serviceName, const char *regtype, - const char *replyDomain); + const char *replyDomain) + __attribute__((nonnull(1,2,3,4))); static void query_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, @@ -106,9 +109,11 @@ static void query_callback(DNSServiceRef sdRef, const char *fullName, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, - void *context); + void *context) + __attribute__((nonnull(1,5,9,11))); static void sigterm_handler(int sig); -static void unquote(char *dst, const char *src, size_t dstsize); +static void unquote(char *dst, const char *src, size_t dstsize) + __attribute__((nonnull(1,2))); /* @@ -961,5 +966,5 @@ unquote(char *dst, /* I - Destination buffer */ /* - * End of "$Id: dnssd.c 10017 2011-09-26 18:46:46Z mike $". + * End of "$Id: dnssd.c 10379 2012-03-23 22:16:22Z mike $". */ diff --git a/backend/ipp.c b/backend/ipp.c index 23dab618..e27f38ee 100644 --- a/backend/ipp.c +++ b/backend/ipp.c @@ -1,9 +1,9 @@ /* - * "$Id: ipp.c 10112 2011-11-07 06:08:44Z mike $" + * "$Id: ipp.c 10452 2012-05-04 23:00:01Z mike $" * * IPP backend for CUPS. * - * Copyright 2007-2011 by Apple Inc. + * Copyright 2007-2012 by Apple Inc. * Copyright 1997-2007 by Easy Software Products, all rights reserved. * * These coded instructions, statements, and computer programs are the @@ -63,6 +63,7 @@ typedef struct _cups_monitor_s /**** Monitoring data ****/ int port, /* Port number */ version, /* IPP version */ job_id; /* Job ID for submitted job */ + const char *job_name; /* Job name for submitted job */ http_encryption_t encryption; /* Use encryption? */ ipp_jstate_t job_state; /* Current job state */ ipp_pstate_t printer_state; /* Current printer state */ @@ -82,12 +83,16 @@ static const char * const jattrs[] = /* Job attributes we want */ { "job-impressions-completed", "job-media-sheets-completed", + "job-name", + "job-originating-user-name", "job-state", "job-state-reasons" }; static int job_canceled = 0; /* Job cancelled? */ -static char *password = NULL; +static char username[256] = "", + /* Username for device URI */ + *password = NULL; /* Password for device URI */ static int password_tries = 0; /* Password tries */ @@ -186,7 +191,6 @@ main(int argc, /* I - Number of command-line args */ const char *device_uri; /* Device URI */ char scheme[255], /* Scheme in URI */ hostname[1024], /* Hostname */ - username[255], /* Username info */ resource[1024], /* Resource info (printer name) */ addrname[256], /* Address name */ *optptr, /* Pointer to URI options */ @@ -205,6 +209,7 @@ main(int argc, /* I - Number of command-line args */ int port; /* Port number (not used) */ char portname[255]; /* Port name */ char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */ + char print_job_name[1024]; /* Update job-name for Print-Job */ http_status_t http_status; /* Status of HTTP request */ ipp_status_t ipp_status; /* Status of IPP request */ http_t *http; /* HTTP connection */ @@ -231,7 +236,9 @@ main(int argc, /* I - Number of command-line args */ ipp_attribute_t *doc_handling_sup; /* multiple-document-handling-supported */ ipp_attribute_t *printer_state; /* printer-state attribute */ ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs */ - int validate_job; /* Does printer support Validate-Job? */ + int create_job = 0, /* Does printer support Create-Job? */ + send_document = 0, /* Does printer support Send-Document? */ + validate_job = 0; /* Does printer support Validate-Job? */ int copies, /* Number of copies for job */ copies_remaining; /* Number of copies remaining */ const char *content_type, /* CONTENT_TYPE environment variable */ @@ -604,7 +611,10 @@ main(int argc, /* I - Number of command-line args */ const char *ptr = getenv("AUTH_USERNAME"); if (ptr) + { + strlcpy(username, ptr, sizeof(username)); cupsSetUser(ptr); + } password = getenv("AUTH_PASSWORD"); } @@ -789,7 +799,6 @@ main(int argc, /* I - Number of command-line args */ supported = NULL; operations_sup = NULL; doc_handling_sup = NULL; - validate_job = 0; do { @@ -834,7 +843,9 @@ main(int argc, /* I - Number of command-line args */ fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n", ippErrorString(ipp_status), cupsLastErrorString()); - if (ipp_status > IPP_OK_CONFLICT) + if (ipp_status <= IPP_OK_CONFLICT) + password_tries = 0; + else { fprintf(stderr, "DEBUG: Get-Printer-Attributes returned %s.\n", ippErrorString(ipp_status)); @@ -891,16 +902,21 @@ main(int argc, /* I - Number of command-line args */ return (CUPS_BACKEND_STOP); } - else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN) + else if (ipp_status == IPP_FORBIDDEN || + ipp_status == IPP_AUTHENTICATION_CANCELED) { - if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE), - "Negotiate", 9)) + const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE); + /* WWW-Authenticate field value */ + + if (!strncmp(www_auth, "Negotiate", 9)) auth_info_required = "negotiate"; + else if (www_auth[0]) + auth_info_required = "username,password"; fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required); return (CUPS_BACKEND_AUTH_REQUIRED); } - else + else if (ipp_status != IPP_NOT_AUTHORIZED) { _cupsLangPrintFilter(stderr, "ERROR", _("Unable to get printer status.")); @@ -1042,11 +1058,21 @@ main(int argc, /* I - Number of command-line args */ "cups-ipp-missing-get-printer-attributes"); for (i = 0; i < operations_sup->num_values; i ++) + { if (operations_sup->values[i].integer == IPP_VALIDATE_JOB) - { validate_job = 1; - break; - } + else if (operations_sup->values[i].integer == IPP_CREATE_JOB) + create_job = 1; + else if (operations_sup->values[i].integer == IPP_SEND_DOCUMENT) + send_document = 1; + } + + if (!send_document) + { + fputs("DEBUG: Printer supports Create-Job but not Send-Document.\n", + stderr); + create_job = 0; + } if (!validate_job) update_reasons(NULL, "+cups-ipp-conformance-failure-report," @@ -1116,7 +1142,7 @@ main(int argc, /* I - Number of command-line args */ { copies_remaining = 1; - if (argc < 7 && !send_options) + if (argc < 7 && !_cups_strncasecmp(final_content_type, "image/", 6)) copies = 1; } else @@ -1153,7 +1179,8 @@ main(int argc, /* I - Number of command-line args */ if (format_sup != NULL) { for (i = 0; i < format_sup->num_values; i ++) - if (!_cups_strcasecmp(final_content_type, format_sup->values[i].string.text)) + if (!_cups_strcasecmp(final_content_type, + format_sup->values[i].string.text)) { document_format = final_content_type; break; @@ -1163,7 +1190,7 @@ main(int argc, /* I - Number of command-line args */ { for (i = 0; i < format_sup->num_values; i ++) if (!_cups_strcasecmp("application/octet-stream", - format_sup->values[i].string.text)) + format_sup->values[i].string.text)) { document_format = "application/octet-stream"; break; @@ -1171,6 +1198,9 @@ main(int argc, /* I - Number of command-line args */ } } + fprintf(stderr, "DEBUG: final_content_type=\"%s\", document_format=\"%s\"\n", + final_content_type, document_format ? document_format : "(null)"); + /* * If the printer does not support HTTP/1.1 (which IPP requires), copy stdin * to a temporary file so that we can do a HTTP/1.0 submission... @@ -1188,8 +1218,17 @@ main(int argc, /* I - Number of command-line args */ _cupsLangPrintFilter(stderr, "INFO", _("Copying print data.")); - compatsize = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0, - backendNetworkSideCB); + if ((compatsize = write(fd, buffer, bytes)) < 0) + { + perror("DEBUG: Unable to write temporary file"); + return (CUPS_BACKEND_FAILED); + } + + if ((bytes = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0, + backendNetworkSideCB)) < 0) + return (CUPS_BACKEND_FAILED); + + compatsize += bytes; close(fd); @@ -1220,6 +1259,17 @@ main(int argc, /* I - Number of command-line args */ monitor.job_state = IPP_JOB_PENDING; monitor.printer_state = IPP_PRINTER_IDLE; + if (create_job) + { + monitor.job_name = argv[3]; + } + else + { + snprintf(print_job_name, sizeof(print_job_name), "%s - %s", argv[1], + argv[3]); + monitor.job_name = print_job_name; + } + _cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor); /* @@ -1228,8 +1278,8 @@ main(int argc, /* I - Number of command-line args */ while (!job_canceled && validate_job) { - request = new_request(IPP_VALIDATE_JOB, version, uri, argv[2], argv[3], - num_options, options, compression, + request = new_request(IPP_VALIDATE_JOB, version, uri, argv[2], + monitor.job_name, num_options, options, compression, copies_sup ? copies : 1, document_format, pc, media_col_sup, doc_handling_sup); @@ -1248,26 +1298,16 @@ main(int argc, /* I - Number of command-line args */ _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy.")); sleep(10); } - else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN || + else if (ipp_status == IPP_FORBIDDEN || ipp_status == IPP_AUTHENTICATION_CANCELED) { - /* - * Update auth-info-required as needed... - */ - - fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n", - httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE)); - - /* - * Normal authentication goes through the password callback, which sets - * auth_info_required to "username,password". Kerberos goes directly - * through GSSAPI, so look for Negotiate in the WWW-Authenticate header - * here and set auth_info_required as needed... - */ + const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE); + /* WWW-Authenticate field value */ - if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE), - "Negotiate", 9)) + if (!strncmp(www_auth, "Negotiate", 9)) auth_info_required = "negotiate"; + else if (www_auth[0]) + auth_info_required = "username,password"; goto cleanup; } @@ -1281,7 +1321,8 @@ main(int argc, /* I - Number of command-line args */ "cups-ipp-missing-validate-job"); break; } - else if (ipp_status < IPP_REDIRECTION_OTHER_SITE) + else if (ipp_status < IPP_REDIRECTION_OTHER_SITE || + ipp_status == IPP_BAD_REQUEST) break; } @@ -1306,16 +1347,17 @@ main(int argc, /* I - Number of command-line args */ if (job_canceled) break; - request = new_request(num_files > 1 ? IPP_CREATE_JOB : IPP_PRINT_JOB, - version, uri, argv[2], argv[3], num_options, options, - compression, copies_sup ? copies : 1, document_format, - pc, media_col_sup, doc_handling_sup); + request = new_request((num_files > 1 || create_job) ? IPP_CREATE_JOB : + IPP_PRINT_JOB, + version, uri, argv[2], monitor.job_name, num_options, + options, compression, copies_sup ? copies : 1, + document_format, pc, media_col_sup, doc_handling_sup); /* * Do the request... */ - if (num_files > 1) + if (num_files > 1 || create_job) response = cupsDoRequest(http, request, resource); else { @@ -1333,7 +1375,13 @@ main(int argc, /* I - Number of command-line args */ if (http_status == HTTP_CONTINUE && request->state == IPP_DATA) { if (num_files == 1) - fd = open(files[0], O_RDONLY); + { + if ((fd = open(files[0], O_RDONLY)) < 0) + { + _cupsLangPrintError("ERROR", _("Unable to open print file")); + return (CUPS_BACKEND_FAILED); + } + } else { fd = 0; @@ -1382,7 +1430,7 @@ main(int argc, /* I - Number of command-line args */ ipp_status = cupsLastError(); fprintf(stderr, "DEBUG: %s: %s (%s)\n", - num_files > 1 ? "Create-Job" : "Print-Job", + (num_files > 1 || create_job) ? "Create-Job" : "Print-Job", ippErrorString(ipp_status), cupsLastErrorString()); if (ipp_status > IPP_OK_CONFLICT) @@ -1411,6 +1459,8 @@ main(int argc, /* I - Number of command-line args */ } else if (ipp_status == IPP_ERROR_JOB_CANCELED) goto cleanup; + else if (ipp_status == IPP_NOT_AUTHORIZED) + continue; else { /* @@ -1420,21 +1470,24 @@ main(int argc, /* I - Number of command-line args */ _cupsLangPrintFilter(stderr, "ERROR", _("Print file was not accepted.")); - if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN) + if (ipp_status == IPP_FORBIDDEN || + ipp_status == IPP_AUTHENTICATION_CANCELED) { - fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n", - httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE)); + const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE); + /* WWW-Authenticate field value */ - /* - * Normal authentication goes through the password callback, which sets - * auth_info_required to "username,password". Kerberos goes directly - * through GSSAPI, so look for Negotiate in the WWW-Authenticate header - * here and set auth_info_required as needed... + if (!strncmp(www_auth, "Negotiate", 9)) + auth_info_required = "negotiate"; + else if (www_auth[0]) + auth_info_required = "username,password"; + } + else if (ipp_status == IPP_REQUEST_VALUE) + { + /* + * Print file is too large, abort this job... */ - if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE), - "Negotiate", 9)) - auth_info_required = "negotiate"; + goto cleanup; } else sleep(10); @@ -1461,19 +1514,21 @@ main(int argc, /* I - Number of command-line args */ } else { + password_tries = 0; monitor.job_id = job_id = job_id_attr->values[0].integer; _cupsLangPrintFilter(stderr, "INFO", _("Print file accepted - job ID %d."), job_id); } + fprintf(stderr, "DEBUG: job-id=%d\n", job_id); ippDelete(response); if (job_canceled) break; - if (job_id && num_files > 1) + if (job_id && (num_files > 1 || create_job)) { - for (i = 0; i < num_files; i ++) + for (i = 0; num_files == 0 || i < num_files; i ++) { /* * Check for side-channel requests... @@ -1499,21 +1554,41 @@ main(int argc, /* I - Number of command-line args */ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, argv[2]); - if ((i + 1) == num_files) + if ((i + 1) >= num_files) ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, - "document-format", NULL, content_type); + if (document_format) + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, + "document-format", NULL, document_format); fprintf(stderr, "DEBUG: Sending file %d using chunking...\n", i + 1); http_status = cupsSendRequest(http, request, resource, 0); - if (http_status == HTTP_CONTINUE && request->state == IPP_DATA && - (fd = open(files[i], O_RDONLY)) >= 0) + if (http_status == HTTP_CONTINUE && request->state == IPP_DATA) { - while (!job_canceled && + if (num_files == 0) + { + fd = 0; + http_status = cupsWriteRequestData(http, buffer, bytes); + } + else + { + if ((fd = open(files[i], O_RDONLY)) < 0) + { + _cupsLangPrintError("ERROR", _("Unable to open print file")); + return (CUPS_BACKEND_FAILED); + } + } + } + else + fd = -1; + + if (fd >= 0) + { + while (!job_canceled && http_status == HTTP_CONTINUE && (bytes = read(fd, buffer, sizeof(buffer))) > 0) { - if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE) + if ((http_status = cupsWriteRequestData(http, buffer, bytes)) + != HTTP_CONTINUE) break; else { @@ -1525,7 +1600,8 @@ main(int argc, /* I - Number of command-line args */ } } - close(fd); + if (fd > 0) + close(fd); } ippDelete(cupsGetResponse(http, resource)); @@ -1542,6 +1618,13 @@ main(int argc, /* I - Number of command-line args */ _("Unable to add document to print job.")); break; } + else + { + password_tries = 0; + + if (num_files == 0 || fd < 0) + break; + } } } @@ -1554,6 +1637,14 @@ main(int argc, /* I - Number of command-line args */ ipp_status == IPP_NOT_POSSIBLE || ipp_status == IPP_PRINTER_BUSY) continue; + else if (ipp_status == IPP_REQUEST_VALUE) + { + /* + * Print file is too large, abort this job... + */ + + goto cleanup; + } else copies_remaining --; @@ -1621,16 +1712,16 @@ main(int argc, /* I - Number of command-line args */ fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n", ippErrorString(ipp_status), cupsLastErrorString()); - if (ipp_status > IPP_OK_CONFLICT) + if (ipp_status <= IPP_OK_CONFLICT) + password_tries = 0; + else { if (ipp_status != IPP_SERVICE_UNAVAILABLE && ipp_status != IPP_NOT_POSSIBLE && ipp_status != IPP_PRINTER_BUSY) { ippDelete(response); - - _cupsLangPrintFilter(stderr, "ERROR", - _("Unable to get print job status.")); + ipp_status = IPP_OK; break; } } @@ -1777,6 +1868,11 @@ main(int argc, /* I - Number of command-line args */ else if (ipp_status == IPP_DOCUMENT_FORMAT || ipp_status == IPP_CONFLICT) return (CUPS_BACKEND_FAILED); + else if (ipp_status == IPP_REQUEST_VALUE) + { + _cupsLangPrintFilter(stderr, "ERROR", _("Print job too large.")); + return (CUPS_BACKEND_CANCEL); + } else if (ipp_status > IPP_OK_CONFLICT && ipp_status != IPP_ERROR_JOB_CANCELED) return (CUPS_BACKEND_RETRY_CURRENT); else @@ -1879,6 +1975,9 @@ check_printer_state( fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n", ippErrorString(cupsLastError()), cupsLastErrorString()); + if (cupsLastError() <= IPP_OK_CONFLICT) + password_tries = 0; + /* * Return the printer-state value... */ @@ -1972,6 +2071,11 @@ monitor_printer( ipp_attribute_t *attr; /* Attribute in response */ int delay, /* Current delay */ prev_delay; /* Previous delay */ + ipp_op_t job_op; /* Operation to use */ + int job_id; /* Job ID */ + const char *job_name; /* Job name */ + ipp_jstate_t job_state; /* Job state */ + const char *job_user; /* Job originating user name */ /* @@ -1981,6 +2085,8 @@ monitor_printer( http = _httpCreate(monitor->hostname, monitor->port, NULL, monitor->encryption, AF_UNSPEC); httpSetTimeout(http, 30.0, timeout_cb, NULL); + if (username[0]) + cupsSetUser(username); cupsSetPasswordCB(password_cb); /* @@ -2006,47 +2112,99 @@ monitor_printer( monitor->user, monitor->version); - if (monitor->job_id > 0) - { - /* - * Check the status of the job itself... - */ + /* + * Check the status of the job itself... + */ - request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES); - request->request.op.version[0] = monitor->version / 10; - request->request.op.version[1] = monitor->version % 10; + job_op = monitor->job_id > 0 ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS; + request = ippNewRequest(job_op); + request->request.op.version[0] = monitor->version / 10; + request->request.op.version[1] = monitor->version % 10; - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", - NULL, monitor->uri); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", + NULL, monitor->uri); + if (job_op == IPP_GET_JOB_ATTRIBUTES) ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", - monitor->job_id); + monitor->job_id); - if (monitor->user && monitor->user[0]) - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, monitor->user); + if (monitor->user && monitor->user[0]) + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, + "requesting-user-name", NULL, monitor->user); - ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "requested-attributes", - (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs); + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, + "requested-attributes", + (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs); - /* - * Do the request... - */ + /* + * Do the request... + */ - response = cupsDoRequest(http, request, monitor->resource); + response = cupsDoRequest(http, request, monitor->resource); - fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n", - ippErrorString(cupsLastError()), cupsLastErrorString()); + fprintf(stderr, "DEBUG: %s: %s (%s)\n", ippOpString(job_op), + ippErrorString(cupsLastError()), cupsLastErrorString()); + if (cupsLastError() <= IPP_OK_CONFLICT) + password_tries = 0; + + if (job_op == IPP_GET_JOB_ATTRIBUTES) + { if ((attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM)) != NULL) monitor->job_state = (ipp_jstate_t)attr->values[0].integer; else monitor->job_state = IPP_JOB_COMPLETED; + } + else if (response) + { + for (attr = response->attrs; attr; attr = attr->next) + { + job_id = 0; + job_name = NULL; + job_state = IPP_JOB_PENDING; + job_user = NULL; - ippDelete(response); + while (attr && attr->group_tag != IPP_TAG_JOB) + attr = attr->next; + + if (!attr) + break; + + while (attr && attr->group_tag == IPP_TAG_JOB) + { + if (!strcmp(attr->name, "job-id") && + attr->value_tag == IPP_TAG_INTEGER) + job_id = attr->values[0].integer; + else if (!strcmp(attr->name, "job-name") && + (attr->value_tag == IPP_TAG_NAME || + attr->value_tag == IPP_TAG_NAMELANG)) + job_name = attr->values[0].string.text; + else if (!strcmp(attr->name, "job-state") && + attr->value_tag == IPP_TAG_ENUM) + job_state = attr->values[0].integer; + else if (!strcmp(attr->name, "job-originating-user-name") && + (attr->value_tag == IPP_TAG_NAME || + attr->value_tag == IPP_TAG_NAMELANG)) + job_user = attr->values[0].string.text; + + attr = attr->next; + } + + if (job_id > 0 && job_name && !strcmp(job_name, monitor->job_name) && + job_user && monitor->user && !strcmp(job_user, monitor->user)) + { + monitor->job_id = job_id; + monitor->job_state = job_state; + break; + } + + if (!attr) + break; + } } + ippDelete(response); + /* * Disconnect from the printer - we'll reconnect on the next poll... */ @@ -2064,6 +2222,15 @@ monitor_printer( } /* + * Cancel the job if necessary... + */ + + if (job_canceled && monitor->job_id > 0) + if (!httpReconnect(http)) + cancel_job(http, monitor->uri, monitor->job_id, monitor->resource, + monitor->user, monitor->version); + + /* * Cleanup and return... */ @@ -2088,7 +2255,7 @@ new_request( cups_option_t *options, /* I - Options to send */ const char *compression, /* I - compression value or NULL */ int copies, /* I - copies value or 0 */ - const char *format, /* I - documet-format value or NULL */ + const char *format, /* I - document-format value or NULL */ _ppd_cache_t *pc, /* I - PPD cache and mapping data */ ipp_attribute_t *media_col_sup, /* I - media-col-supported values */ ipp_attribute_t *doc_handling_sup) /* I - multiple-document-handling-supported values */ @@ -2163,6 +2330,9 @@ new_request( { if (pc) { + int num_finishings = 0, /* Number of finishing values */ + finishings[10]; /* Finishing enum values */ + /* * Send standard IPP attributes... */ @@ -2284,6 +2454,7 @@ new_request( } if (doc_handling_sup && + (!format || _cups_strncasecmp(format, "image/", 6)) && (keyword = cupsGetOption("collate", num_options, options)) != NULL) { if (!_cups_strcasecmp(keyword, "true")) @@ -2299,6 +2470,43 @@ new_request( break; } } + + /* + * Map finishing options... + */ + + num_finishings = _ppdCacheGetFinishingValues(pc, num_options, options, + (int)(sizeof(finishings) / + sizeof(finishings[0])), + finishings); + if (num_finishings > 0) + ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings", + num_finishings, finishings); + + /* + * Map FaxOut options... + */ + + if ((keyword = cupsGetOption("phone", num_options, options)) != NULL) + { + ipp_t *destination; /* destination collection */ + char tel_uri[1024]; /* tel: URI */ + + destination = ippNew(); + + httpAssembleURI(HTTP_URI_CODING_ALL, tel_uri, sizeof(tel_uri), "tel", + NULL, NULL, 0, keyword); + ippAddString(destination, IPP_TAG_JOB, IPP_TAG_URI, "destination-uri", + NULL, tel_uri); + + if ((keyword = cupsGetOption("faxPrefix", num_options, + options)) != NULL && *keyword) + ippAddString(destination, IPP_TAG_JOB, IPP_TAG_TEXT, + "pre-dial-string", NULL, keyword); + + ippAddCollection(request, IPP_TAG_JOB, "destination-uris", destination); + ippDelete(destination); + } } else { @@ -2324,6 +2532,9 @@ new_request( static const char * /* O - Password */ password_cb(const char *prompt) /* I - Prompt (not used) */ { + fprintf(stderr, "DEBUG: password_cb(prompt=\"%s\"), password=%p, " + "password_tries=%d\n", prompt, password, password_tries); + (void)prompt; /* @@ -2386,17 +2597,23 @@ report_attr(ipp_attribute_t *attr) /* I - Attribute */ case IPP_TAG_TEXT : case IPP_TAG_NAME : case IPP_TAG_KEYWORD : + *valptr++ = '\''; *valptr++ = '\"'; for (attrptr = attr->values[i].string.text; *attrptr && valptr < (value + sizeof(value) - 10); attrptr ++) { - if (*attrptr == '\\' || *attrptr == '\"') + if (*attrptr == '\\' || *attrptr == '\"' || *attrptr == '\'') + { + *valptr++ = '\\'; *valptr++ = '\\'; + *valptr++ = '\\'; + } *valptr++ = *attrptr; } *valptr++ = '\"'; + *valptr++ = '\''; break; default : @@ -2988,5 +3205,5 @@ update_reasons(ipp_attribute_t *attr, /* I - printer-state-reasons or NULL */ } /* - * End of "$Id: ipp.c 10112 2011-11-07 06:08:44Z mike $". + * End of "$Id: ipp.c 10452 2012-05-04 23:00:01Z mike $". */ diff --git a/backend/lpd.c b/backend/lpd.c index cd2edc1f..7837b344 100644 --- a/backend/lpd.c +++ b/backend/lpd.c @@ -1,9 +1,9 @@ /* - * "$Id: lpd.c 9793 2011-05-20 03:49:49Z mike $" + * "$Id: lpd.c 10265 2012-02-12 07:20:10Z mike $" * * Line Printer Daemon backend for CUPS. * - * Copyright 2007-2011 by Apple Inc. + * Copyright 2007-2012 by Apple Inc. * Copyright 1997-2007 by Easy Software Products, all rights reserved. * * These coded instructions, statements, and computer programs are the @@ -19,7 +19,6 @@ * main() - Send a file to the printer or server. * lpd_command() - Send an LPR command sequence and wait for a reply. * lpd_queue() - Queue a file using the Line Printer Daemon protocol. - * lpd_timeout() - Handle timeout alarms... * lpd_write() - Write a buffer of data to an LPD server. * rresvport_af() - A simple implementation of rresvport_af(). * sigterm_handler() - Handle 'terminate' signals that stop the backend. @@ -87,14 +86,13 @@ static int abort_job = 0; /* Non-zero if we get SIGTERM */ * Local functions... */ -static int lpd_command(int lpd_fd, int timeout, char *format, ...); +static int lpd_command(int lpd_fd, char *format, ...); static int lpd_queue(const char *hostname, http_addrlist_t *addrlist, const char *printer, int print_fd, int snmp_fd, int mode, const char *user, const char *title, int copies, int banner, int format, int order, int reserve, int manual_copies, int timeout, int contimeout); -static void lpd_timeout(int sig); static int lpd_write(int lpd_fd, char *buffer, int length); #ifndef HAVE_RRESVPORT_AF static int rresvport_af(int *port, int family); @@ -564,7 +562,6 @@ main(int argc, /* I - Number of command-line arguments (6 or 7) */ static int /* O - Status of command */ lpd_command(int fd, /* I - Socket connection to LPD host */ - int timeout, /* I - Seconds to wait for a response */ char *format, /* I - printf()-style format string */ ...) /* I - Additional args as necessary */ { @@ -609,18 +606,12 @@ lpd_command(int fd, /* I - Socket connection to LPD host */ fputs("DEBUG: Reading command status...\n", stderr); - alarm(timeout); - if (recv(fd, &status, 1, 0) < 1) { - _cupsLangPrintFilter(stderr, "WARNING", - _("Printer did not respond after %d seconds."), - timeout); + _cupsLangPrintFilter(stderr, "WARNING", _("Printer did not respond.")); status = errno; } - alarm(0); - fprintf(stderr, "DEBUG: lpd_command returning %d\n", status); return (status); @@ -666,26 +657,12 @@ lpd_queue(const char *hostname, /* I - Host to connect to */ size_t nbytes; /* Number of bytes written */ off_t tbytes; /* Total bytes written */ char buffer[32768]; /* Output buffer */ -#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) - struct sigaction action; /* Actions for POSIX signals */ -#endif /* HAVE_SIGACTION && !HAVE_SIGSET */ - - - /* - * Setup an alarm handler for timeouts... - */ - -#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */ - sigset(SIGALRM, lpd_timeout); -#elif defined(HAVE_SIGACTION) - memset(&action, 0, sizeof(action)); - - sigemptyset(&action.sa_mask); - action.sa_handler = lpd_timeout; - sigaction(SIGALRM, &action, NULL); +#ifdef WIN32 + DWORD tv; /* Timeout in milliseconds */ #else - signal(SIGALRM, lpd_timeout); -#endif /* HAVE_SIGSET */ + struct timeval tv; /* Timeout in secs and usecs */ +#endif /* WIN32 */ + /* * Remember when we started trying to connect to the printer... @@ -864,6 +841,23 @@ lpd_queue(const char *hostname, /* I - Host to connect to */ } } + /* + * Set the timeout... + */ + +#ifdef WIN32 + tv = (DWORD)(timeout * 1000); + + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv)); +#else + tv.tv_sec = timeout; + tv.tv_usec = 0; + + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); +#endif /* WIN32 */ + fputs("STATE: -connecting-to-device\n", stderr); _cupsLangPrintFilter(stderr, "INFO", _("Connected to printer.")); @@ -926,7 +920,7 @@ lpd_queue(const char *hostname, /* I - Host to connect to */ * literal output... */ - if (lpd_command(fd, timeout, "\002%s\n", + if (lpd_command(fd, "\002%s\n", printer)) /* Receive print job(s) */ { close(fd); @@ -978,7 +972,7 @@ lpd_queue(const char *hostname, /* I - Host to connect to */ * Send the control file... */ - if (lpd_command(fd, timeout, "\002%d cfA%03.3d%.15s\n", strlen(control), + if (lpd_command(fd, "\002%d cfA%03.3d%.15s\n", strlen(control), (int)getpid() % 1000, localhost)) { close(fd); @@ -997,17 +991,12 @@ lpd_queue(const char *hostname, /* I - Host to connect to */ } else { - alarm(timeout); - if (read(fd, &status, 1) < 1) { _cupsLangPrintFilter(stderr, "WARNING", - _("Printer did not respond after %d seconds."), - timeout); + _("Printer did not respond.")); status = errno; } - - alarm(0); } if (status != 0) @@ -1033,7 +1022,7 @@ lpd_queue(const char *hostname, /* I - Host to connect to */ * Send the print file... */ - if (lpd_command(fd, timeout, "\003" CUPS_LLFMT " dfA%03.3d%.15s\n", + if (lpd_command(fd, "\003" CUPS_LLFMT " dfA%03.3d%.15s\n", CUPS_LLCAST filestats.st_size, (int)getpid() % 1000, localhost)) { @@ -1084,17 +1073,12 @@ lpd_queue(const char *hostname, /* I - Host to connect to */ * want to requeue it over and over... */ - alarm(timeout); - if (recv(fd, &status, 1, 0) < 1) { _cupsLangPrintFilter(stderr, "WARNING", - _("Printer did not respond after %d seconds."), - timeout); + _("Printer did not respond.")); status = 0; } - - alarm(0); } } else @@ -1121,7 +1105,7 @@ lpd_queue(const char *hostname, /* I - Host to connect to */ * Send control file... */ - if (lpd_command(fd, timeout, "\002%d cfA%03.3d%.15s\n", strlen(control), + if (lpd_command(fd, "\002%d cfA%03.3d%.15s\n", strlen(control), (int)getpid() % 1000, localhost)) { close(fd); @@ -1139,17 +1123,12 @@ lpd_queue(const char *hostname, /* I - Host to connect to */ } else { - alarm(timeout); - if (read(fd, &status, 1) < 1) { _cupsLangPrintFilter(stderr, "WARNING", - _("Printer did not respond after %d seconds."), - timeout); + _("Printer did not respond.")); status = errno; } - - alarm(0); } if (status != 0) @@ -1193,21 +1172,6 @@ lpd_queue(const char *hostname, /* I - Host to connect to */ /* - * 'lpd_timeout()' - Handle timeout alarms... - */ - -static void -lpd_timeout(int sig) /* I - Signal number */ -{ - (void)sig; - -#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION) - signal(SIGALRM, lpd_timeout); -#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */ -} - - -/* * 'lpd_write()' - Write a buffer of data to an LPD server. */ @@ -1338,5 +1302,5 @@ sigterm_handler(int sig) /* I - Signal */ /* - * End of "$Id: lpd.c 9793 2011-05-20 03:49:49Z mike $". + * End of "$Id: lpd.c 10265 2012-02-12 07:20:10Z mike $". */ diff --git a/backend/runloop.c b/backend/runloop.c index 21cf45df..6cb41783 100644 --- a/backend/runloop.c +++ b/backend/runloop.c @@ -1,9 +1,9 @@ /* - * "$Id: runloop.c 9820 2011-06-10 22:06:26Z mike $" + * "$Id: runloop.c 10369 2012-03-21 04:31:19Z mike $" * * Common run loop APIs for CUPS backends. * - * Copyright 2007-2011 by Apple Inc. + * Copyright 2007-2012 by Apple Inc. * Copyright 2006-2007 by Easy Software Products, all rights reserved. * * These coded instructions, statements, and computer programs are the @@ -434,9 +434,11 @@ backendWaitLoop( int use_bc, /* I - Use back-channel? */ _cups_sccb_t side_cb) /* I - Side-channel callback */ { - fd_set input; /* Input set for reading */ - time_t curtime, /* Current time */ - snmp_update = 0; /* Last SNMP status update */ + int nfds; /* Number of file descriptors */ + fd_set input; /* Input set for reading */ + time_t curtime, /* Current time */ + snmp_update = 0;/* Last SNMP status update */ + struct timeval timeout; /* Timeout for select() */ fprintf(stderr, "DEBUG: backendWaitLoop(snmp_fd=%d, addr=%p, side_cb=%p)\n", @@ -446,6 +448,9 @@ backendWaitLoop( * Now loop until we receive data from stdin... */ + if (snmp_fd >= 0) + snmp_update = time(NULL) + 5; + for (;;) { /* @@ -457,7 +462,18 @@ backendWaitLoop( if (side_cb) FD_SET(CUPS_SC_FD, &input); - if (select(CUPS_SC_FD + 1, &input, NULL, NULL, NULL) < 0) + if (snmp_fd >= 0) + { + curtime = time(NULL); + timeout.tv_sec = curtime >= snmp_update ? 0 : snmp_update - curtime; + timeout.tv_usec = 0; + + nfds = select(CUPS_SC_FD + 1, &input, NULL, NULL, &timeout); + } + else + nfds = select(CUPS_SC_FD + 1, &input, NULL, NULL, NULL); + + if (nfds < 0) { /* * Pause printing to clear any pending errors... @@ -501,10 +517,10 @@ backendWaitLoop( * Do SNMP updates periodically... */ - if (snmp_fd >= 0 && time(&curtime) >= snmp_update) + if (snmp_fd >= 0 && curtime >= snmp_update) { if (backendSNMPSupplies(snmp_fd, addr, NULL, NULL)) - snmp_update = INT_MAX; + snmp_fd = -1; else snmp_update = curtime + 5; } @@ -519,5 +535,5 @@ backendWaitLoop( /* - * End of "$Id: runloop.c 9820 2011-06-10 22:06:26Z mike $". + * End of "$Id: runloop.c 10369 2012-03-21 04:31:19Z mike $". */ diff --git a/backend/snmp-supplies.c b/backend/snmp-supplies.c index 6d6bae8a..dc2b3f1a 100644 --- a/backend/snmp-supplies.c +++ b/backend/snmp-supplies.c @@ -1,9 +1,9 @@ /* - * "$Id: snmp-supplies.c 10064 2011-10-07 21:41:07Z mike $" + * "$Id: snmp-supplies.c 10262 2012-02-12 05:48:09Z mike $" * * SNMP supplies functions for CUPS. * - * Copyright 2008-2011 by Apple Inc. + * Copyright 2008-2012 by Apple Inc. * * These coded instructions, statements, and computer programs are the * property of Apple Inc. and are protected by Federal copyright @@ -408,7 +408,7 @@ backend_init_supplies( cachefilename[1024], /* Cache filename */ description[CUPS_SNMP_MAX_STRING], /* Device description string */ - value[CUPS_MAX_SUPPLIES * (CUPS_SNMP_MAX_STRING * 2 + 3)], + value[CUPS_MAX_SUPPLIES * (CUPS_SNMP_MAX_STRING * 4 + 3)], /* Value string */ *ptr, /* Pointer into value string */ *name_ptr; /* Pointer into name string */ @@ -659,7 +659,8 @@ backend_init_supplies( fprintf(stderr, "ATTR: marker-colors=%s\n", value); /* - * Output the marker-names attribute... + * Output the marker-names attribute (the double quoting is necessary to deal + * with embedded quotes and commas in the marker names...) */ for (i = 0, ptr = value; i < num_supplies; i ++) @@ -667,15 +668,21 @@ backend_init_supplies( if (i) *ptr++ = ','; + *ptr++ = '\''; *ptr++ = '\"'; for (name_ptr = supplies[i].name; *name_ptr;) { - if (*name_ptr == '\\' || *name_ptr == '\"') + if (*name_ptr == '\\' || *name_ptr == '\"' || *name_ptr == '\'') + { + *ptr++ = '\\'; + *ptr++ = '\\'; *ptr++ = '\\'; + } *ptr++ = *name_ptr++; } *ptr++ = '\"'; + *ptr++ = '\''; } *ptr = '\0'; @@ -712,16 +719,33 @@ backend_walk_cb(cups_snmp_t *packet, /* I - SNMP packet */ void *data) /* I - User data (unused) */ { int i, j, k; /* Looping vars */ - static const char * const colors[8][2] = + static const char * const colors[][2] = { /* Standard color names */ - { "black", "#000000" }, - { "blue", "#0000FF" }, - { "cyan", "#00FFFF" }, - { "green", "#00FF00" }, - { "magenta", "#FF00FF" }, - { "red", "#FF0000" }, - { "white", "#FFFFFF" }, - { "yellow", "#FFFF00" } + { "black", "#000000" }, + { "blue", "#0000FF" }, + { "brown", "#A52A2A" }, + { "cyan", "#00FFFF" }, + { "dark-gray", "#404040" }, + { "dark gray", "#404040" }, + { "dark-yellow", "#FFCC00" }, + { "dark yellow", "#FFCC00" }, + { "gold", "#FFD700" }, + { "gray", "#808080" }, + { "green", "#00FF00" }, + { "light-black", "#606060" }, + { "light black", "#606060" }, + { "light-cyan", "#E0FFFF" }, + { "light cyan", "#E0FFFF" }, + { "light-gray", "#D3D3D3" }, + { "light gray", "#D3D3D3" }, + { "light-magenta", "#FF77FF" }, + { "light magenta", "#FF77FF" }, + { "magenta", "#FF00FF" }, + { "orange", "#FFA500" }, + { "red", "#FF0000" }, + { "silver", "#C0C0C0" }, + { "white", "#FFFFFF" }, + { "yellow", "#FFFF00" } }; @@ -743,7 +767,8 @@ backend_walk_cb(cups_snmp_t *packet, /* I - SNMP packet */ if (supplies[j].colorant == i) { for (k = 0; k < (int)(sizeof(colors) / sizeof(colors[0])); k ++) - if (!strcmp(colors[k][0], (char *)packet->object_value.string.bytes)) + if (!_cups_strcasecmp(colors[k][0], + (char *)packet->object_value.string.bytes)) { strcpy(supplies[j].color, colors[k][1]); break; @@ -834,7 +859,6 @@ backend_walk_cb(cups_snmp_t *packet, /* I - SNMP packet */ { char *src, *dst; /* Pointers into strings */ - /* * Loop safe because both the object_value and supplies char arrays * are CUPS_SNMP_MAX_STRING elements long. @@ -982,5 +1006,5 @@ utf16_to_utf8( /* - * End of "$Id: snmp-supplies.c 10064 2011-10-07 21:41:07Z mike $". + * End of "$Id: snmp-supplies.c 10262 2012-02-12 05:48:09Z mike $". */ diff --git a/backend/usb-libusb.c b/backend/usb-libusb.c index 7f23ed83..e3a02be9 100644 --- a/backend/usb-libusb.c +++ b/backend/usb-libusb.c @@ -1,7 +1,7 @@ /* - * "$Id: usb-libusb.c 10198 2012-01-27 16:48:43Z mike $" + * "$Id: usb-libusb.c 10267 2012-02-12 08:35:28Z mike $" * - * Libusb interface code for CUPS. + * LIBUSB interface code for CUPS. * * Copyright 2007-2012 by Apple Inc. * @@ -13,25 +13,43 @@ * * Contents: * - * list_devices() - List the available printers. - * print_device() - Print a file to a USB device. - * close_device() - Close the connection to the USB printer. - * find_device() - Find or enumerate USB printers. - * get_device_id() - Get the IEEE-1284 device ID for the printer. - * list_cb() - List USB printers for discovery. - * make_device_uri() - Create a device URI for a USB printer. - * open_device() - Open a connection to the USB printer. - * print_cb() - Find a USB printer for printing. - * side_cb() - Handle side-channel requests. + * list_devices() - List the available printers. + * print_device() - Print a file to a USB device. + * close_device() - Close the connection to the USB printer. + * find_device() - Find or enumerate USB printers. + * get_device_id() - Get the IEEE-1284 device ID for the printer. + * list_cb() - List USB printers for discovery. + * make_device_uri() - Create a device URI for a USB printer. + * open_device() - Open a connection to the USB printer. + * print_cb() - Find a USB printer for printing. + * read_thread() - Thread to read the backchannel data on. + * sidechannel_thread() - Handle side-channel requests. + * soft_reset() - Send a soft reset to the device. */ /* * Include necessary headers... */ -#include <usb.h> -#include <poll.h> +#include <libusb.h> #include <cups/cups-private.h> +#include <pthread.h> +#include <sys/select.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <unistd.h> + + +/* + * WAIT_EOF_DELAY is number of seconds we'll wait for responses from + * the printer after we've finished sending all the data + */ + +#define WAIT_EOF 0 +#define WAIT_EOF_DELAY 7 +#define WAIT_SIDE_DELAY 3 +#define DEFAULT_TIMEOUT 5000L /* @@ -40,18 +58,55 @@ typedef struct usb_printer_s /**** USB Printer Data ****/ { - struct usb_device *device; /* Device info */ + struct libusb_device *device; /* Device info */ int conf, /* Configuration */ iface, /* Interface */ altset, /* Alternate setting */ write_endp, /* Write endpoint */ - read_endp; /* Read endpoint */ - struct usb_dev_handle *handle; /* Open handle to device */ + read_endp, /* Read endpoint */ + protocol, /* Protocol: 1 = Uni-di, 2 = Bi-di. */ + usblp_attached; /* Is the "usblp" kernel module + attached? */ + struct libusb_device_handle *handle; /* Open handle to device */ } usb_printer_t; typedef int (*usb_cb_t)(usb_printer_t *, const char *, const char *, const void *); +typedef struct usb_globals_s +{ + usb_printer_t *printer; /* Printer */ + + pthread_mutex_t read_thread_mutex; + pthread_cond_t read_thread_cond; + int read_thread_stop; + int read_thread_done; + + pthread_mutex_t readwrite_lock_mutex; + pthread_cond_t readwrite_lock_cond; + int readwrite_lock; + + int print_fd; /* File descriptor to print */ + ssize_t print_bytes; /* Print bytes read */ + + int wait_eof; + int drain_output; /* Drain all pending output */ + int bidi_flag; /* 0=unidirectional, 1=bidirectional */ + + pthread_mutex_t sidechannel_thread_mutex; + pthread_cond_t sidechannel_thread_cond; + int sidechannel_thread_stop; + int sidechannel_thread_done; +} usb_globals_t; + + +/* + * Globals... + */ + +usb_globals_t g = { 0 }; /* Globals */ +libusb_device **list; /* List of connected USB devices */ + /* * Local functions... @@ -69,7 +124,9 @@ static char *make_device_uri(usb_printer_t *printer, static int open_device(usb_printer_t *printer, int verbose); static int print_cb(usb_printer_t *printer, const char *device_uri, const char *device_id, const void *data); -static ssize_t side_cb(usb_printer_t *printer, int print_fd); +static void *read_thread(void *reference); +static void *sidechannel_thread(void *reference); +static void soft_reset(void); /* @@ -98,27 +155,46 @@ print_device(const char *uri, /* I - Device URI */ int argc, /* I - Number of command-line arguments (6 or 7) */ char *argv[]) /* I - Command-line arguments */ { - usb_printer_t *printer; /* Printer */ - ssize_t bytes, /* Bytes read/written */ - tbytes; /* Total bytes written */ - char buffer[512]; /* Print data buffer */ + int bytes; /* Bytes written */ + ssize_t total_bytes; /* Total bytes written */ struct sigaction action; /* Actions for POSIX signals */ - struct pollfd pfds[2]; /* Poll descriptors */ + int status = CUPS_BACKEND_OK, + /* Function results */ + iostatus; /* Current IO status */ + pthread_t read_thread_id, /* Read thread */ + sidechannel_thread_id; /* Side-channel thread */ + int have_sidechannel = 0; /* Was the side-channel thread started? */ + struct stat sidechannel_info; /* Side-channel file descriptor info */ + unsigned char print_buffer[8192], /* Print data buffer */ + *print_ptr; /* Pointer into print data buffer */ + fd_set input_set; /* Input set for select() */ + int nfds; /* Number of file descriptors */ + struct timeval *timeout, /* Timeout pointer */ + tv; /* Time value */ + struct timespec cond_timeout; /* pthread condition timeout */ + + + /* + * See if the side-channel descriptor is valid... + */ + have_sidechannel = !fstat(CUPS_SC_FD, &sidechannel_info) && + S_ISSOCK(sidechannel_info.st_mode); - fputs("DEBUG: print_device\n", stderr); + g.wait_eof = WAIT_EOF; /* * Connect to the printer... */ - while ((printer = find_device(print_cb, uri)) == NULL) + while ((g.printer = find_device(print_cb, uri)) == NULL) { _cupsLangPrintFilter(stderr, "INFO", _("Waiting for printer to become available.")); sleep(5); } + g.print_fd = print_fd; /* * If we are printing data from a print driver on stdin, ignore SIGTERM @@ -136,74 +212,371 @@ print_device(const char *uri, /* I - Device URI */ sigaction(SIGTERM, &action, NULL); } - tbytes = 0; + /* + * Start the side channel thread if the descriptor is valid... + */ - pfds[0].fd = print_fd; - pfds[0].events = POLLIN; - pfds[1].fd = CUPS_SC_FD; - pfds[1].events = POLLIN; + pthread_mutex_init(&g.readwrite_lock_mutex, NULL); + pthread_cond_init(&g.readwrite_lock_cond, NULL); + g.readwrite_lock = 1; - while (copies > 0 && tbytes >= 0) + if (have_sidechannel) { - copies --; + g.sidechannel_thread_stop = 0; + g.sidechannel_thread_done = 0; + + pthread_cond_init(&g.sidechannel_thread_cond, NULL); + pthread_mutex_init(&g.sidechannel_thread_mutex, NULL); - if (print_fd != 0) + if (pthread_create(&sidechannel_thread_id, NULL, sidechannel_thread, NULL)) + { + fprintf(stderr, "DEBUG: Fatal USB error.\n"); + _cupsLangPrintFilter(stderr, "ERROR", + _("There was an unrecoverable USB error.")); + fputs("DEBUG: Couldn't create side-channel thread.\n", stderr); + close_device(g.printer); + return (CUPS_BACKEND_STOP); + } + } + + /* + * Get the read thread going... + */ + + g.read_thread_stop = 0; + g.read_thread_done = 0; + + pthread_cond_init(&g.read_thread_cond, NULL); + pthread_mutex_init(&g.read_thread_mutex, NULL); + + if (pthread_create(&read_thread_id, NULL, read_thread, NULL)) + { + fprintf(stderr, "DEBUG: Fatal USB error.\n"); + _cupsLangPrintFilter(stderr, "ERROR", + _("There was an unrecoverable USB error.")); + fputs("DEBUG: Couldn't create read thread.\n", stderr); + close_device(g.printer); + return (CUPS_BACKEND_STOP); + } + + /* + * The main thread sends the print file... + */ + + g.drain_output = 0; + g.print_bytes = 0; + total_bytes = 0; + print_ptr = print_buffer; + + while (status == CUPS_BACKEND_OK && copies-- > 0) + { + _cupsLangPrintFilter(stderr, "INFO", _("Sending data to printer.")); + + if (print_fd != STDIN_FILENO) { fputs("PAGE: 1 1\n", stderr); lseek(print_fd, 0, SEEK_SET); } - /* - * TODO: Add back-channel support, along with better write error handling. - */ - - while (poll(pfds, 2, -1) > 0) + while (status == CUPS_BACKEND_OK) { + FD_ZERO(&input_set); + + if (!g.print_bytes) + FD_SET(print_fd, &input_set); + + /* + * Calculate select timeout... + * If we have data waiting to send timeout is 100ms. + * else if we're draining print_fd timeout is 0. + * else we're waiting forever... + */ + + if (g.print_bytes) + { + tv.tv_sec = 0; + tv.tv_usec = 100000; /* 100ms */ + timeout = &tv; + } + else if (g.drain_output) + { + tv.tv_sec = 0; + tv.tv_usec = 0; + timeout = &tv; + } + else + timeout = NULL; + + /* + * I/O is unlocked around select... + */ + + pthread_mutex_lock(&g.readwrite_lock_mutex); + g.readwrite_lock = 0; + pthread_cond_signal(&g.readwrite_lock_cond); + pthread_mutex_unlock(&g.readwrite_lock_mutex); + + nfds = select(print_fd + 1, &input_set, NULL, NULL, timeout); + + /* + * Reacquire the lock... + */ + + pthread_mutex_lock(&g.readwrite_lock_mutex); + while (g.readwrite_lock) + pthread_cond_wait(&g.readwrite_lock_cond, &g.readwrite_lock_mutex); + g.readwrite_lock = 1; + pthread_mutex_unlock(&g.readwrite_lock_mutex); + + if (nfds < 0) + { + if (errno == EINTR && total_bytes == 0) + { + fputs("DEBUG: Received an interrupt before any bytes were " + "written, aborting.\n", stderr); + close_device(g.printer); + return (CUPS_BACKEND_OK); + } + else if (errno != EAGAIN && errno != EINTR) + { + _cupsLangPrintFilter(stderr, "ERROR", + _("Unable to read print data.")); + perror("DEBUG: select"); + close_device(g.printer); + return (CUPS_BACKEND_FAILED); + } + } + /* - * CUPS STR #3318: USB process hangs on end-of-file, making further - * printing impossible - * - * From a strict interpretation of POSIX poll(), POLLHUP should never be - * set without POLLIN, since POLLIN is the event you request. That said, - * it appears that some versions of Linux break this. + * If drain output has finished send a response... */ - if (pfds[0].revents & (POLLIN | POLLHUP)) + if (g.drain_output && !nfds && !g.print_bytes) { - if ((bytes = read(print_fd, buffer, sizeof(buffer))) > 0) + /* Send a response... */ + cupsSideChannelWrite(CUPS_SC_CMD_DRAIN_OUTPUT, CUPS_SC_STATUS_OK, NULL, 0, 1.0); + g.drain_output = 0; + } + + /* + * Check if we have print data ready... + */ + + if (FD_ISSET(print_fd, &input_set)) + { + g.print_bytes = read(print_fd, print_buffer, sizeof(print_buffer)); + + if (g.print_bytes < 0) { - if (usb_bulk_write(printer->handle, printer->write_endp, buffer, - bytes, 3600000) < 0) + /* + * Read error - bail if we don't see EAGAIN or EINTR... + */ + + if (errno != EAGAIN && errno != EINTR) { _cupsLangPrintFilter(stderr, "ERROR", - _("Unable to send data to printer.")); - tbytes = -1; - break; + _("Unable to read print data.")); + perror("DEBUG: read"); + close_device(g.printer); + return (CUPS_BACKEND_FAILED); } - tbytes += bytes; + g.print_bytes = 0; } - else if (bytes == 0 || (bytes < 0 && errno != EAGAIN && errno != EINTR)) + else if (g.print_bytes == 0) + { + /* + * End of file, break out of the loop... + */ + + break; + } + + print_ptr = print_buffer; + + fprintf(stderr, "DEBUG: Read %d bytes of print data...\n", + (int)g.print_bytes); + } + + if (g.print_bytes) + { + iostatus = libusb_bulk_transfer(g.printer->handle, + g.printer->write_endp, + print_buffer, g.print_bytes, + &bytes, 60000); + /* + * Ignore timeout errors, but retain the number of bytes written to + * avoid sending duplicate data... + */ + + if (iostatus == LIBUSB_ERROR_TIMEOUT) + { + fputs("DEBUG: Got USB transaction timeout during write.\n", stderr); + iostatus = 0; + } + + /* + * If we've stalled, retry the write... + */ + + else if (iostatus == LIBUSB_ERROR_PIPE) + { + fputs("DEBUG: Got USB pipe stalled during write.\n", stderr); + + iostatus = libusb_bulk_transfer(g.printer->handle, + g.printer->write_endp, + print_buffer, g.print_bytes, + &bytes, 60000); + } + + /* + * Retry a write after an aborted write since we probably just got + * SIGTERM... + */ + + else if (iostatus == LIBUSB_ERROR_INTERRUPTED) + { + fputs("DEBUG: Got USB return aborted during write.\n", stderr); + + iostatus = libusb_bulk_transfer(g.printer->handle, + g.printer->write_endp, + print_buffer, g.print_bytes, + &bytes, 60000); + } + + if (iostatus) + { + /* + * Write error - bail if we don't see an error we can retry... + */ + + _cupsLangPrintFilter(stderr, "ERROR", + _("Unable to send data to printer.")); + fprintf(stderr, "DEBUG: libusb write operation returned %x.\n", + iostatus); + + status = CUPS_BACKEND_FAILED; + break; + } + else if (bytes > 0) + { + fprintf(stderr, "DEBUG: Wrote %d bytes of print data...\n", + (int)bytes); + + g.print_bytes -= bytes; + print_ptr += bytes; + total_bytes += bytes; + } + } + + if (print_fd != 0 && status == CUPS_BACKEND_OK) + fprintf(stderr, "DEBUG: Sending print file, " CUPS_LLFMT " bytes...\n", + CUPS_LLCAST total_bytes); + } + } + + fprintf(stderr, "DEBUG: Sent " CUPS_LLFMT " bytes...\n", + CUPS_LLCAST total_bytes); + + /* + * Signal the side channel thread to exit... + */ + + if (have_sidechannel) + { + close(CUPS_SC_FD); + pthread_mutex_lock(&g.readwrite_lock_mutex); + g.readwrite_lock = 0; + pthread_cond_signal(&g.readwrite_lock_cond); + pthread_mutex_unlock(&g.readwrite_lock_mutex); + + g.sidechannel_thread_stop = 1; + pthread_mutex_lock(&g.sidechannel_thread_mutex); + + if (!g.sidechannel_thread_done) + { + gettimeofday(&tv, NULL); + cond_timeout.tv_sec = tv.tv_sec + WAIT_SIDE_DELAY; + cond_timeout.tv_nsec = tv.tv_usec * 1000; + + while (!g.sidechannel_thread_done) + { + if (pthread_cond_timedwait(&g.sidechannel_thread_cond, + &g.sidechannel_thread_mutex, + &cond_timeout) != 0) break; } + } + + pthread_mutex_unlock(&g.sidechannel_thread_mutex); + } + + /* + * Signal the read thread to exit then wait 7 seconds for it to complete... + */ + + g.read_thread_stop = 1; + + pthread_mutex_lock(&g.read_thread_mutex); + + if (!g.read_thread_done) + { + fputs("DEBUG: Waiting for read thread to exit...\n", stderr); + + gettimeofday(&tv, NULL); + cond_timeout.tv_sec = tv.tv_sec + WAIT_EOF_DELAY; + cond_timeout.tv_nsec = tv.tv_usec * 1000; + + while (!g.read_thread_done) + { + if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex, + &cond_timeout) != 0) + break; + } + + /* + * If it didn't exit abort the pending read and wait an additional second... + */ + + if (!g.read_thread_done) + { + fputs("DEBUG: Read thread still active, aborting the pending read...\n", + stderr); - if (pfds[1].revents & (POLLIN | POLLHUP)) + g.wait_eof = 0; + + gettimeofday(&tv, NULL); + cond_timeout.tv_sec = tv.tv_sec + 1; + cond_timeout.tv_nsec = tv.tv_usec * 1000; + + while (!g.read_thread_done) { - if ((bytes = side_cb(printer, print_fd)) < 0) - pfds[1].events = 0; /* Filter has gone away... */ - else - tbytes += bytes; + if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex, + &cond_timeout) != 0) + break; } } } + pthread_mutex_unlock(&g.read_thread_mutex); + + if (print_fd) + close(print_fd); + /* - * Close our connection and return... + * Close the connection and input file and general clean up... */ - close_device(printer); + close_device(g.printer); - return (CUPS_BACKEND_OK); + /* + * Clean up .... + */ + + libusb_free_device_list(list, 1); + libusb_exit(NULL); + + return (status); } @@ -214,6 +587,12 @@ print_device(const char *uri, /* I - Device URI */ static int /* I - 0 on success, -1 on failure */ close_device(usb_printer_t *printer) /* I - Printer */ { + struct libusb_device_descriptor devdesc; + /* Current device descriptor */ + struct libusb_config_descriptor *confptr; + /* Pointer to current configuration */ + + if (printer->handle) { /* @@ -221,19 +600,36 @@ close_device(usb_printer_t *printer) /* I - Printer */ * to the device... */ - int number = printer->device->config[printer->conf]. - interface[printer->iface]. - altsetting[printer->altset].bInterfaceNumber; - usb_release_interface(printer->handle, number); + int number; /* Interface number */ + libusb_get_device_descriptor (printer->device, &devdesc); + libusb_get_config_descriptor (printer->device, printer->conf, &confptr); + number = confptr->interface[printer->iface]. + altsetting[printer->altset].bInterfaceNumber; + libusb_release_interface(printer->handle, number); if (number != 0) - usb_release_interface(printer->handle, 0); + libusb_release_interface(printer->handle, 0); + + /* + * Re-attach "usblp" kernel module if it was attached before using this + * device + */ + + if (printer->usblp_attached == 1) + { + if (libusb_attach_kernel_driver(printer->handle, printer->iface) < 0) + fprintf(stderr, + "DEBUG: Failed to re-attach \"usblp\" kernel module to " + "%04x:%04x\n", devdesc.idVendor, devdesc.idProduct); + } + + libusb_free_config_descriptor(confptr); /* * Close the interface and return... */ - usb_close(printer->handle); + libusb_close(printer->handle); printer->handle = NULL; } @@ -249,15 +645,21 @@ static usb_printer_t * /* O - Found printer */ find_device(usb_cb_t cb, /* I - Callback function */ const void *data) /* I - User data for callback */ { - struct usb_bus *bus; /* Current bus */ - struct usb_device *device; /* Current device */ - struct usb_config_descriptor *confptr;/* Pointer to current configuration */ - struct usb_interface *ifaceptr; /* Pointer to current interface */ - struct usb_interface_descriptor *altptr; + libusb_device **list; /* List of connected USB devices */ + libusb_device *device = NULL; /* Current device */ + struct libusb_device_descriptor devdesc; + /* Current device descriptor */ + struct libusb_config_descriptor *confptr = NULL; + /* Pointer to current configuration */ + const struct libusb_interface *ifaceptr = NULL; + /* Pointer to current interface */ + const struct libusb_interface_descriptor *altptr = NULL; /* Pointer to current alternate setting */ - struct usb_endpoint_descriptor *endpptr; + const struct libusb_endpoint_descriptor *endpptr = NULL; /* Pointer to current endpoint */ - int conf, /* Current configuration */ + ssize_t numdevs, /* number of connected devices */ + i = 0; + uint8_t conf, /* Current configuration */ iface, /* Current interface */ altset, /* Current alternate setting */ protocol, /* Current protocol */ @@ -274,29 +676,34 @@ find_device(usb_cb_t cb, /* I - Callback function */ * Initialize libusb... */ - usb_init(); - fprintf(stderr, "DEBUG: usb_find_busses=%d\n", usb_find_busses()); - fprintf(stderr, "DEBUG: usb_find_devices=%d\n", usb_find_devices()); + libusb_init(NULL); + numdevs = libusb_get_device_list(NULL, &list); + fprintf(stderr, "DEBUG: libusb_get_device_list=%d\n", (int)numdevs); /* * Then loop through the devices it found... */ - for (bus = usb_get_busses(); bus; bus = bus->next) - for (device = bus->devices; device; device = device->next) + if (numdevs > 0) + for (i = 0; i < numdevs; i++) { + device = list[i]; + /* * Ignore devices with no configuration data and anything that is not * a printer... */ - if (!device->config || !device->descriptor.idVendor || - !device->descriptor.idProduct) + libusb_get_device_descriptor (device, &devdesc); + + if (!devdesc.bNumConfigurations || !devdesc.idVendor || + !devdesc.idProduct) continue; - for (conf = 0, confptr = device->config; - conf < device->descriptor.bNumConfigurations; - conf ++, confptr ++) + for (conf = 0; conf < devdesc.bNumConfigurations; conf ++) + { + if (libusb_get_config_descriptor (device, conf, &confptr) < 0) + continue; for (iface = 0, ifaceptr = confptr->interface; iface < confptr->bNumInterfaces; iface ++, ifaceptr ++) @@ -317,7 +724,7 @@ find_device(usb_cb_t cb, /* I - Callback function */ * 1284.4 (packet mode) protocol as well. */ - if (altptr->bInterfaceClass != USB_CLASS_PRINTER || + if (altptr->bInterfaceClass != LIBUSB_CLASS_PRINTER || altptr->bInterfaceSubClass != 1 || (altptr->bInterfaceProtocol != 1 && /* Unidirectional */ altptr->bInterfaceProtocol != 2) || /* Bidirectional */ @@ -330,10 +737,10 @@ find_device(usb_cb_t cb, /* I - Callback function */ for (endp = 0, endpptr = altptr->endpoint; endp < altptr->bNumEndpoints; endp ++, endpptr ++) - if ((endpptr->bmAttributes & USB_ENDPOINT_TYPE_MASK) == - USB_ENDPOINT_TYPE_BULK) + if ((endpptr->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == + LIBUSB_TRANSFER_TYPE_BULK) { - if (endpptr->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + if (endpptr->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) read_endp = endp; else write_endp = endp; @@ -354,38 +761,37 @@ find_device(usb_cb_t cb, /* I - Callback function */ if (protocol > 0) { - printer.device = device; - printer.conf = conf; - printer.iface = iface; - printer.handle = NULL; + printer.device = device; + printer.conf = conf; + printer.iface = iface; + printer.protocol = protocol; + printer.handle = NULL; if (!open_device(&printer, data != NULL)) { - if (!get_device_id(&printer, device_id, sizeof(device_id))) - { - make_device_uri(&printer, device_id, device_uri, - sizeof(device_uri)); + get_device_id(&printer, device_id, sizeof(device_id)); + make_device_uri(&printer, device_id, device_uri, + sizeof(device_uri)); - if ((*cb)(&printer, device_uri, device_id, data)) - { - printer.read_endp = printer.device->config[printer.conf]. - interface[printer.iface]. + if ((*cb)(&printer, device_uri, device_id, data)) + { + printer.read_endp = confptr->interface[printer.iface]. altsetting[printer.altset]. endpoint[printer.read_endp]. bEndpointAddress; - printer.write_endp = printer.device->config[printer.conf]. - interface[printer.iface]. + printer.write_endp = confptr->interface[printer.iface]. altsetting[printer.altset]. endpoint[printer.write_endp]. bEndpointAddress; - return (&printer); - } + return (&printer); } close_device(&printer); } } } + libusb_free_config_descriptor(confptr); + } } /* @@ -393,6 +799,13 @@ find_device(usb_cb_t cb, /* I - Callback function */ * to print to... */ + /* + * Clean up .... + */ + + libusb_free_device_list(list, 1); + libusb_exit(NULL); + return (NULL); } @@ -409,10 +822,12 @@ get_device_id(usb_printer_t *printer, /* I - Printer */ int length; /* Length of device ID */ - if (usb_control_msg(printer->handle, - USB_TYPE_CLASS | USB_ENDPOINT_IN | USB_RECIP_INTERFACE, - 0, printer->conf, (printer->iface << 8) | printer->altset, - buffer, bufsize, 5000) < 0) + if (libusb_control_transfer(printer->handle, + LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_ENDPOINT_IN | + LIBUSB_RECIPIENT_INTERFACE, + 0, printer->conf, + (printer->iface << 8) | printer->altset, + (unsigned char *)buffer, bufsize, 5000) < 0) { *buffer = '\0'; return (-1); @@ -482,7 +897,8 @@ list_cb(usb_printer_t *printer, /* I - Printer */ * Get the device URI and make/model strings... */ - backendGetMakeModel(device_id, make_model, sizeof(make_model)); + if (backendGetMakeModel(device_id, make_model, sizeof(make_model))) + strlcpy(make_model, "Unknown", sizeof(make_model)); /* * Report the printer... @@ -510,12 +926,14 @@ make_device_uri( char *uri, /* I - Device URI buffer */ size_t uri_size) /* I - Size of device URI buffer */ { + struct libusb_device_descriptor devdesc; + /* Current device descriptor */ char options[1024]; /* Device URI options */ int num_values; /* Number of 1284 parameters */ cups_option_t *values; /* 1284 parameters */ const char *mfg, /* Manufacturer */ *mdl, /* Model */ - *des, /* Description */ + *des = NULL, /* Description */ *sern; /* Serial number */ size_t mfglen; /* Length of manufacturer string */ char tempmfg[256], /* Temporary manufacturer string */ @@ -532,16 +950,18 @@ make_device_uri( if ((sern = cupsGetOption("SERIALNUMBER", num_values, values)) == NULL) if ((sern = cupsGetOption("SERN", num_values, values)) == NULL) if ((sern = cupsGetOption("SN", num_values, values)) == NULL && - printer->device->descriptor.iSerialNumber) + ((libusb_get_device_descriptor (printer->device, &devdesc) >= 0) && + devdesc.iSerialNumber)) { /* * Try getting the serial number from the device itself... */ - int length = usb_get_string_simple(printer->handle, - printer->device->descriptor. - iSerialNumber, - tempsern, sizeof(tempsern) - 1); + int length = + libusb_get_string_descriptor_ascii(printer->handle, + devdesc.iSerialNumber, + (unsigned char *)tempsern, + sizeof(tempsern) - 1); if (length > 0) { tempsern[length] = '\0'; @@ -555,15 +975,6 @@ make_device_uri( if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL) mdl = cupsGetOption("MDL", num_values, values); -#ifdef __APPLE__ - /* - * To maintain compatibility with the original IOKit-based backend on Mac OS X, - * don't map manufacturer names... - */ - - if (!mfg) - -#else /* * To maintain compatibility with the original character device backend on * Linux and *BSD, map manufacturer names... @@ -577,7 +988,6 @@ make_device_uri( mfg = "Lexmark"; } else -#endif /* __APPLE__ */ { /* * No manufacturer? Use the model string or description... @@ -597,6 +1007,19 @@ make_device_uri( mfg = tempmfg; } + if (!mdl) + { + /* + * No model? Use description... + */ + if (des) + mdl = des; /* We remove the manufacturer name below */ + else if (!strncasecmp(mfg, "Unknown", 7)) + mdl = "Printer"; + else + mdl = "Unknown Model"; + } + mfglen = strlen(mfg); if (!strncasecmp(mdl, mfg, mfglen) && _cups_isspace(mdl[mfglen])) @@ -642,7 +1065,13 @@ static int /* O - 0 on success, -1 on error */ open_device(usb_printer_t *printer, /* I - Printer */ int verbose) /* I - Update connecting-to-device state? */ { - int number; /* Configuration/interface/altset numbers */ + struct libusb_device_descriptor devdesc; + /* Current device descriptor */ + struct libusb_config_descriptor *confptr = NULL; + /* Pointer to current configuration */ + int number1 = -1, /* Configuration/interface/altset */ + number2 = -1, /* numbers */ + errcode = 0; char current; /* Current configuration */ @@ -657,7 +1086,7 @@ open_device(usb_printer_t *printer, /* I - Printer */ * Try opening the printer... */ - if ((printer->handle = usb_open(printer->device)) == NULL) + if (libusb_open(printer->device, &printer->handle) < 0) return (-1); if (verbose) @@ -665,20 +1094,25 @@ open_device(usb_printer_t *printer, /* I - Printer */ /* * Set the desired configuration, but only if it needs changing. Some - * printers (e.g., Samsung) don't like usb_set_configuration. It will succeed, - * but the following print job is sometimes silently lost by the printer. + * printers (e.g., Samsung) don't like libusb_set_configuration. It will + * succeed, but the following print job is sometimes silently lost by the + * printer. */ - if (usb_control_msg(printer->handle, - USB_TYPE_STANDARD | USB_ENDPOINT_IN | USB_RECIP_DEVICE, - 8, /* GET_CONFIGURATION */ - 0, 0, ¤t, 1, 5000) != 1) + if (libusb_control_transfer(printer->handle, + LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_ENDPOINT_IN | + LIBUSB_RECIPIENT_DEVICE, + 8, /* GET_CONFIGURATION */ + 0, 0, (unsigned char *)¤t, 1, 5000) < 0) current = 0; /* Assume not configured */ - number = printer->device->config[printer->conf].bConfigurationValue; - if (number != current) + libusb_get_device_descriptor (printer->device, &devdesc); + libusb_get_config_descriptor (printer->device, printer->conf, &confptr); + number1 = confptr->bConfigurationValue; + + if (number1 != current) { - if (usb_set_configuration(printer->handle, number) < 0) + if ((errcode = libusb_set_configuration(printer->handle, number1)) < 0) { /* * If the set fails, chances are that the printer only supports a @@ -686,26 +1120,52 @@ open_device(usb_printer_t *printer, /* I - Printer */ * the USB printer specification, but otherwise they'll work... */ - if (errno != EBUSY) + if (errcode != LIBUSB_ERROR_BUSY) fprintf(stderr, "DEBUG: Failed to set configuration %d for %04x:%04x\n", - number, printer->device->descriptor.idVendor, - printer->device->descriptor.idProduct); + number1, devdesc.idVendor, devdesc.idProduct); + } + } + + /* + * Get the "usblp" kernel module out of the way. This backend only + * works without the module attached. + */ + + errcode = libusb_kernel_driver_active(printer->handle, printer->iface); + if (errcode == 0) + printer->usblp_attached = 0; + else if (errcode == 1) + { + printer->usblp_attached = 1; + if ((errcode = + libusb_detach_kernel_driver(printer->handle, printer->iface)) < 0) + { + fprintf(stderr, "DEBUG: Failed to detach \"usblp\" module from %04x:%04x\n", + devdesc.idVendor, devdesc.idProduct); + goto error; } } + else + { + printer->usblp_attached = 0; + fprintf(stderr, "DEBUG: Failed to check whether %04x:%04x has the \"usblp\" kernel module attached\n", + devdesc.idVendor, devdesc.idProduct); + goto error; + } /* * Claim interfaces as needed... */ - number = printer->device->config[printer->conf].interface[printer->iface]. - altsetting[printer->altset].bInterfaceNumber; - while (usb_claim_interface(printer->handle, number) < 0) + number1 = confptr->interface[printer->iface]. + altsetting[printer->altset].bInterfaceNumber; + + while ((errcode = libusb_claim_interface(printer->handle, number1)) < 0) { - if (errno != EBUSY) + if (errcode != LIBUSB_ERROR_BUSY) fprintf(stderr, "DEBUG: Failed to claim interface %d for %04x:%04x: %s\n", - number, printer->device->descriptor.idVendor, - printer->device->descriptor.idProduct, strerror(errno)); + number1, devdesc.idVendor, devdesc.idProduct, strerror(errno)); goto error; } @@ -715,24 +1175,29 @@ open_device(usb_printer_t *printer, /* I - Printer */ * printers (e.g., Samsung) don't like usb_set_altinterface. */ - if (printer->device->config[printer->conf].interface[printer->iface]. - num_altsetting > 1) + if (confptr->interface[printer->iface].num_altsetting > 1) { - number = printer->device->config[printer->conf].interface[printer->iface]. + number1 = confptr->interface[printer->iface]. + altsetting[printer->altset].bInterfaceNumber; + number2 = confptr->interface[printer->iface]. altsetting[printer->altset].bAlternateSetting; - while (usb_set_altinterface(printer->handle, number) < 0) + while ((errcode = + libusb_set_interface_alt_setting(printer->handle, number1, number2)) + < 0) { - if (errno != EBUSY) + if (errcode != LIBUSB_ERROR_BUSY) fprintf(stderr, "DEBUG: Failed to set alternate interface %d for %04x:%04x: " - "%s\n", number, printer->device->descriptor.idVendor, - printer->device->descriptor.idProduct, strerror(errno)); + "%s\n", + number2, devdesc.idVendor, devdesc.idProduct, strerror(errno)); goto error; } } + libusb_free_config_descriptor(confptr); + if (verbose) fputs("STATE: -connecting-to-device\n", stderr); @@ -747,7 +1212,7 @@ open_device(usb_printer_t *printer, /* I - Printer */ if (verbose) fputs("STATE: -connecting-to-device\n", stderr); - usb_close(printer->handle); + libusb_close(printer->handle); printer->handle = NULL; return (-1); @@ -849,94 +1314,293 @@ print_cb(usb_printer_t *printer, /* I - Printer */ /* - * 'side_cb()' - Handle side-channel requests. + * 'read_thread()' - Thread to read the backchannel data on. + */ + +static void *read_thread(void *reference) +{ + unsigned char readbuffer[512]; + int rbytes; + int readstatus; + struct timeval now, + delay, + end, + timeleft; + + + (void)reference; + + /* + * Read frequency: once every 250 milliseconds. + */ + + delay.tv_sec = 0; + delay.tv_usec = 250000; + + do + { + /* + * Remember when we started so we can throttle the loop after the read + * call... + */ + + gettimeofday(&now, NULL); + + /* + * Calculate what 250 milliSeconds are in absolute time... + */ + + timeradd(&now, &delay, &end); + + rbytes = sizeof(readbuffer); + readstatus = libusb_bulk_transfer(g.printer->handle, + g.printer->read_endp, + readbuffer, rbytes, + &rbytes, 60000); + if (readstatus == LIBUSB_SUCCESS && rbytes > 0) + { + fprintf(stderr, "DEBUG: Read %d bytes of back-channel data...\n", + (int)rbytes); + cupsBackChannelWrite((const char *)readbuffer, rbytes, 1.0); + } + else if (readstatus == LIBUSB_ERROR_TIMEOUT) + fputs("DEBUG: Got USB transaction timeout during read.\n", stderr); + else if (readstatus == LIBUSB_ERROR_PIPE) + fputs("DEBUG: Got USB pipe stalled during read.\n", stderr); + else if (readstatus == LIBUSB_ERROR_INTERRUPTED) + fputs("DEBUG: Got USB return aborted during read.\n", stderr); + + /* + * Make sure this loop executes no more than once every 250 miliseconds... + */ + + if ((readstatus != LIBUSB_SUCCESS || rbytes == 0) && + (g.wait_eof || !g.read_thread_stop)) + { + gettimeofday(&now, NULL); + if (timercmp(&now, &end, <)) + { + timersub(&end, &now, &timeleft); + usleep(1000000 * timeleft.tv_sec + timeleft.tv_usec); + } + } + } while (g.wait_eof || !g.read_thread_stop); + + /* + * Let the main thread know that we have completed the read thread... + */ + + pthread_mutex_lock(&g.read_thread_mutex); + g.read_thread_done = 1; + pthread_cond_signal(&g.read_thread_cond); + pthread_mutex_unlock(&g.read_thread_mutex); + + return (NULL); +} + + +/* + * 'sidechannel_thread()' - Handle side-channel requests. */ -static ssize_t /* O - Number of bytes written */ -side_cb(usb_printer_t *printer, /* I - Printer */ - int print_fd) /* I - File to print */ +static void* +sidechannel_thread(void *reference) { - ssize_t bytes, /* Bytes read/written */ - tbytes; /* Total bytes written */ - char buffer[512]; /* Print data buffer */ - struct pollfd pfd; /* Poll descriptor */ cups_sc_command_t command; /* Request command */ cups_sc_status_t status; /* Request/response status */ char data[2048]; /* Request/response data */ int datalen; /* Request/response data size */ - tbytes = 0; - datalen = sizeof(data); + (void)reference; - if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0)) - return (-1); - - switch (command) + do { - case CUPS_SC_CMD_DRAIN_OUTPUT : - pfd.fd = print_fd; - pfd.events = POLLIN; + datalen = sizeof(data); - while (poll(&pfd, 1, 1000) > 0) - { - if ((bytes = read(print_fd, buffer, sizeof(buffer))) > 0) - { - while (usb_bulk_write(printer->handle, printer->write_endp, buffer, - bytes, 5000) < 0) - { - _cupsLangPrintFilter(stderr, "ERROR", - _("Unable to send data to printer.")); - tbytes = -1; - break; - } + if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0)) + { + if (status == CUPS_SC_STATUS_TIMEOUT) + continue; + else + break; + } + + switch (command) + { + case CUPS_SC_CMD_SOFT_RESET: /* Do a soft reset */ + fputs("DEBUG: CUPS_SC_CMD_SOFT_RESET received from driver...\n", + stderr); + + soft_reset(); + cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, NULL, 0, 1.0); + fputs("DEBUG: Returning status CUPS_STATUS_OK with no bytes...\n", + stderr); + break; - tbytes += bytes; + case CUPS_SC_CMD_DRAIN_OUTPUT: /* Drain all pending output */ + fputs("DEBUG: CUPS_SC_CMD_DRAIN_OUTPUT received from driver...\n", + stderr); + + g.drain_output = 1; + break; + + case CUPS_SC_CMD_GET_BIDI: /* Is the connection bidirectional? */ + fputs("DEBUG: CUPS_SC_CMD_GET_BIDI received from driver...\n", + stderr); + + data[0] = (g.printer->protocol >= 2 ? 1 : 0); + cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, 1, 1.0); + + fprintf(stderr, + "DEBUG: Returned CUPS_SC_STATUS_OK with 1 byte (%02X)...\n", + data[0]); + break; + + case CUPS_SC_CMD_GET_DEVICE_ID: /* Return IEEE-1284 device ID */ + fputs("DEBUG: CUPS_SC_CMD_GET_DEVICE_ID received from driver...\n", + stderr); + + datalen = sizeof(data); + if (get_device_id(g.printer, data, sizeof(data))) + { + status = CUPS_SC_STATUS_IO_ERROR; + datalen = 0; } - else if (bytes < 0 && errno != EAGAIN && errno != EINTR) - break; - } + else + { + status = CUPS_SC_STATUS_OK; + datalen = strlen(data); + } + cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, datalen, 1.0); - if (tbytes < 0) - status = CUPS_SC_STATUS_IO_ERROR; - else - status = CUPS_SC_STATUS_OK; + if (datalen < sizeof(data)) + data[datalen] = '\0'; + else + data[sizeof(data) - 1] = '\0'; - datalen = 0; - break; + fprintf(stderr, + "DEBUG: Returning CUPS_SC_STATUS_OK with %d bytes (%s)...\n", + datalen, data); + break; - case CUPS_SC_CMD_GET_BIDI : - status = CUPS_SC_STATUS_OK; - data[0] = 0; /* TODO: Change to 1 when read supported */ - datalen = 1; - break; + case CUPS_SC_CMD_GET_STATE: /* Return device state */ + fputs("DEBUG: CUPS_SC_CMD_GET_STATE received from driver...\n", + stderr); - case CUPS_SC_CMD_GET_DEVICE_ID : - if (get_device_id(printer, data, sizeof(data))) - { - status = CUPS_SC_STATUS_IO_ERROR; - datalen = 0; - } - else - { - status = CUPS_SC_STATUS_OK; - datalen = strlen(data); - } - break; + data[0] = CUPS_SC_STATE_ONLINE; + cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, 1, 1.0); + + fprintf(stderr, + "DEBUG: Returned CUPS_SC_STATUS_OK with 1 byte (%02X)...\n", + data[0]); + break; + + case CUPS_SC_CMD_GET_CONNECTED: /* Return whether device is + connected */ + fputs("DEBUG: CUPS_SC_CMD_GET_CONNECTED received from driver...\n", + stderr); - default : - status = CUPS_SC_STATUS_NOT_IMPLEMENTED; - datalen = 0; + data[0] = (g.printer->handle ? 1 : 0); + cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, 1, 1.0); + + fprintf(stderr, + "DEBUG: Returned CUPS_SC_STATUS_OK with 1 byte (%02X)...\n", + data[0]); + break; + + default: + fprintf(stderr, "DEBUG: Unknown side-channel command (%d) received " + "from driver...\n", command); + + cupsSideChannelWrite(command, CUPS_SC_STATUS_NOT_IMPLEMENTED, + NULL, 0, 1.0); + + fputs("DEBUG: Returned CUPS_SC_STATUS_NOT_IMPLEMENTED with no bytes...\n", + stderr); + break; + } + } + while (!g.sidechannel_thread_stop); + + pthread_mutex_lock(&g.sidechannel_thread_mutex); + g.sidechannel_thread_done = 1; + pthread_cond_signal(&g.sidechannel_thread_cond); + pthread_mutex_unlock(&g.sidechannel_thread_mutex); + + return (NULL); +} + + +/* + * 'soft_reset()' - Send a soft reset to the device. + */ + +static void soft_reset(void) +{ + fd_set input_set; /* Input set for select() */ + struct timeval tv; /* Time value */ + char buffer[2048]; /* Buffer */ + struct timespec cond_timeout; /* pthread condition timeout */ + + /* + * Send an abort once a second until the I/O lock is released by the main + * thread... + */ + + pthread_mutex_lock(&g.readwrite_lock_mutex); + while (g.readwrite_lock) + { + gettimeofday(&tv, NULL); + cond_timeout.tv_sec = tv.tv_sec + 1; + cond_timeout.tv_nsec = tv.tv_usec * 1000; + + while (g.readwrite_lock) + { + if (pthread_cond_timedwait(&g.readwrite_lock_cond, + &g.readwrite_lock_mutex, + &cond_timeout) != 0) break; + } } - cupsSideChannelWrite(command, status, data, datalen, 1.0); + g.readwrite_lock = 1; + pthread_mutex_unlock(&g.readwrite_lock_mutex); + + /* + * Flush bytes waiting on print_fd... + */ + + g.print_bytes = 0; + + FD_ZERO(&input_set); + FD_SET(g.print_fd, &input_set); + + tv.tv_sec = 0; + tv.tv_usec = 0; + + while (select(g.print_fd+1, &input_set, NULL, NULL, &tv) > 0) + if (read(g.print_fd, buffer, sizeof(buffer)) <= 0) + break; + + /* + * Send the reset... + */ + + libusb_reset_device (g.printer->handle); + + /* + * Release the I/O lock... + */ - return (tbytes); + pthread_mutex_lock(&g.readwrite_lock_mutex); + g.readwrite_lock = 0; + pthread_cond_signal(&g.readwrite_lock_cond); + pthread_mutex_unlock(&g.readwrite_lock_mutex); } /* - * End of "$Id: usb-libusb.c 10198 2012-01-27 16:48:43Z mike $". + * End of "$Id: usb-libusb.c 10267 2012-02-12 08:35:28Z mike $". */ diff --git a/backend/usb.c b/backend/usb.c index 81736a1f..2cacb744 100644 --- a/backend/usb.c +++ b/backend/usb.c @@ -1,9 +1,9 @@ /* - * "$Id: usb.c 9771 2011-05-12 05:21:56Z mike $" + * "$Id: usb.c 10265 2012-02-12 07:20:10Z mike $" * * USB port backend for CUPS. * - * Copyright 2007-2011 by Apple Inc. + * Copyright 2007-2012 by Apple Inc. * Copyright 1997-2007 by Easy Software Products, all rights reserved. * * These coded instructions, statements, and computer programs are the @@ -55,7 +55,7 @@ int print_device(const char *uri, const char *hostname, * Include the vendor-specific USB implementation... */ -#ifdef HAVE_USB_H +#ifdef HAVE_LIBUSB # include "usb-libusb.c" #elif defined(__APPLE__) # include "usb-darwin.c" @@ -118,7 +118,7 @@ print_device(const char *uri, /* I - Device URI */ return (CUPS_BACKEND_FAILED); } -#endif /* __APPLE__ */ +#endif /* HAVE_LIBUSB */ /* @@ -260,5 +260,5 @@ main(int argc, /* I - Number of command-line arguments (6 or 7) */ /* - * End of "$Id: usb.c 9771 2011-05-12 05:21:56Z mike $". + * End of "$Id: usb.c 10265 2012-02-12 07:20:10Z mike $". */ |