blob: f7ec9ef2f641d05bc0cb0dbbe63e74cff610b5fb [file] [log] [blame]
From 2648d4aee3aa7d473fad9b20c4a944cf865e4d80 Mon Sep 17 00:00:00 2001
From: David Valleau <valleau@chromium.org>
Date: Wed, 8 Nov 2017 11:22:05 -0800
Subject: [PATCH] Adding ippusb query path to cups
---
backend/ipp.c | 26 +++++++
cups/Makefile | 6 +-
cups/ippusb-private.h | 34 ++++++++
cups/ippusb.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++
systemv/lpadmin.c | 57 ++++++++++++--
5 files changed, 327 insertions(+), 7 deletions(-)
create mode 100644 cups/ippusb-private.h
create mode 100644 cups/ippusb.c
diff --git a/backend/ipp.c b/backend/ipp.c
index 69970a2..47e5faf 100644
--- a/backend/ipp.c
+++ b/backend/ipp.c
@@ -21,6 +21,7 @@
#include "backend-private.h"
#include <cups/array-private.h>
+#include <cups/ippusb-private.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -666,6 +667,31 @@ main(int argc, /* I - Number of command-line args */
update_reasons(NULL, "+connecting-to-device");
fprintf(stderr, "DEBUG: Looking up \"%s\"...\n", hostname);
+ // If the scheme is ippusb then a query is sent to the ippusb_manager service
+ // to check if the printer is currently connected and get the name of the
+ // socket used for communication with the printer.
+ if (!strcmp(scheme, "ippusb")) {
+ int sock = open_ippusb_manager_socket();
+ char* response = query_ippusb_manager(sock, hostname);
+
+ close(sock);
+
+ int ret = snprintf(hostname, sizeof(hostname), "/run/ippusb/%s", response);
+ if (ret < 0 || ret >= sizeof(hostname)) {
+ fprintf(stderr, "ERROR: Failed to overwrite hostname");
+ _exit(1);
+ }
+
+ free(response);
+
+ // Change the scheme back to ipp so that communications will be understood
+ // by the printer.
+ strcpy(scheme, "ipp");
+
+ // Wait a maximum of 3 seconds for the socket to be created.
+ wait_for_socket(hostname, 3);
+ }
+
while ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
{
_cupsLangPrintFilter(stderr, "INFO",
diff --git a/cups/Makefile b/cups/Makefile
index 7b65214..b9f1133 100644
--- a/cups/Makefile
+++ b/cups/Makefile
@@ -79,7 +79,8 @@ LIBOBJS = \
tls.o \
transcode.o \
usersys.o \
- util.o
+ util.o \
+ ippusb.o
TESTOBJS = \
testadmin.o \
testarray.o \
@@ -137,7 +138,8 @@ HEADERSPRIV = \
raster-private.h \
snmp-private.h \
string-private.h \
- thread-private.h
+ thread-private.h \
+ ippusb-private.h
#
diff --git a/cups/ippusb-private.h b/cups/ippusb-private.h
new file mode 100644
index 0000000..3eb890c
--- /dev/null
+++ b/cups/ippusb-private.h
@@ -0,0 +1,34 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef _CUPS_IPPUSB_H_
+#define _CUPS_IPPUSB_H_
+
+// Attempts to open the socket used to communicate with ippusb_manager, and if
+// successful returns a file descriptor of the socket.
+int open_ippusb_manager_socket(void);
+
+// Sends the provided message through the stream socket referred to by |fd|.
+void send_message(int fd, const char* msg);
+
+// Attempts to receive a message from the stream socket referred to by |fd|.
+char* get_message(int fd);
+
+// Sends a query message to the ippusb_manager through the socket |fd|, and
+// returns the response from ippusb_manager.
+char* query_ippusb_manager(int fd, const char* msg);
+
+// Verifies that the response string is not null and does not contain any
+// invalid characters.
+int valid_response(const char* response);
+
+// Returns a new a new uri based on the given |uri| that replaces the scheme
+// prefix with the given |scheme|.
+char* change_scheme(const char* uri, const char* scheme);
+
+// Waits for a maximum time of |timeout| until the socket at |filename| is ready
+// to accept connections.
+void wait_for_socket(const char* filename, long timeout);
+
+#endif /* _CUPS_IPPUSB_H_ */
diff --git a/cups/ippusb.c b/cups/ippusb.c
new file mode 100644
index 0000000..380f666
--- /dev/null
+++ b/cups/ippusb.c
@@ -0,0 +1,211 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ippusb-private.h"
+#include "language-private.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+int open_ippusb_manager_socket(void) {
+ int fd;
+
+ _cupsLangPrintf(stderr, _("Attempting to open socket"));
+
+ fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+ if (fd < 0) {
+ _cupsLangPrintf(stderr, _("Failed to open stream socket: %s"),
+ strerror(errno));
+ _exit(1);
+ }
+
+ _cupsLangPrintf(stderr, _("Attempting to connect to socket"));
+
+ struct sockaddr_un addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, "/run/ippusb/ippusb_manager.sock");
+
+ if (connect(fd, &addr, sizeof(struct sockaddr_un)) != 0) {
+ close(fd);
+ _cupsLangPrintf(stderr, _("Failed to connect to socket: %s"),
+ strerror(errno));
+ _exit(1);
+ }
+
+ return fd;
+}
+
+// Writes a message to the socket described by |fd| as a stream of bytes. The
+// first byte represents the length of the message, and the following bytes are
+// filled using |msg|.
+void send_message(int fd, const char* msg) {
+ size_t remaining = strlen(msg) + 1;
+ if (remaining > UINT8_MAX) {
+ _cupsLangPrintf(stderr, _("The message to be sent is too large"));
+ _exit(1);
+ }
+
+ // Send the length of the message.
+ uint8_t message_length = (uint8_t) remaining;
+ if (send(fd, &message_length, 1, 0) < 0) {
+ _cupsLangPrintf(stderr, _("Failed to send message length"));
+ _exit(1);
+ }
+
+ size_t total = 0;
+
+ while (remaining > 0) {
+ ssize_t sent = send(fd, msg + total, remaining, MSG_NOSIGNAL);
+
+ if (sent < 0) {
+ _cupsLangPrintf(stderr, _("Failed to send message"));
+ _exit(1);
+ }
+
+ total += sent;
+ if (sent >= remaining)
+ remaining = 0;
+ else
+ remaining -= sent;
+ }
+}
+
+char* get_message(int fd) {
+ // Poll the file descriptor first before trying to read. In the event that
+ // ippusb_manager exited unexpectedly before responding on the socket we want
+ // to be able to exit before blocking on read.
+ struct pollfd poll_fd;
+ poll_fd.fd = fd;
+ poll_fd.events = POLLIN;
+ int timeout = 1000;
+ if (poll(&poll_fd, 1, timeout) <= 0) {
+ _cupsLangPrintf(stderr, _("Failed to receive response"));
+ _exit(1);
+ }
+
+ // Get the first byte out of the stream which contains the length of the
+ // message.
+ uint8_t message_length;
+ if (recv(fd, &message_length, 1, 0) < 0) {
+ _cupsLangPrintf(stderr, _("Failed to get message length"));
+ _exit(1);
+ }
+
+ char* buf = (char*) malloc(sizeof(*buf) * message_length);
+ ssize_t gotten_size;
+ size_t total_size = 0;
+
+ while (total_size < message_length) {
+ gotten_size =
+ recv(fd, buf + total_size, message_length - total_size, 0);
+
+ if (gotten_size < 0) {
+ _cupsLangPrintf(stderr, _("Failed to receive message"));
+ _exit(1);
+ }
+
+ total_size += gotten_size;
+ }
+
+ return buf;
+}
+
+char* query_ippusb_manager(int fd, const char* msg) {
+ _cupsLangPrintf(stderr, _("Attempting to write to socket"));
+ send_message(fd, msg);
+
+ _cupsLangPrintf(stderr, _("Attempting to read response"));
+ char* response = get_message(fd);
+ _cupsLangPrintf(stderr, _("Finished reading response"));
+
+ if (!valid_response(response)) {
+ _cupsLangPrintf(stderr, _("Invalid response"));
+ _exit(1);
+ }
+
+ if (!strcasecmp(response, "device not found")) {
+ _cupsLangPrintf(stderr, _("Device not found"));
+ _exit(1);
+ }
+
+ return response;
+}
+
+int valid_response(const char* response) {
+ if (!response)
+ return 0;
+
+ const char* p = response;
+ while (*p) {
+ if (!isalpha(*p) && !isdigit(*p) && *p != '_' && *p != '.' && *p != ' ')
+ return 0;
+ ++p;
+ }
+
+ return 1;
+}
+
+char* change_scheme(const char* uri, const char* scheme) {
+ char* p = strchr(uri, ':');
+ if (!p)
+ return NULL;
+
+ char* updated_uri;
+ if (asprintf(&updated_uri, "%s%s", scheme, p) == -1)
+ return NULL;
+
+ return updated_uri;
+}
+
+void wait_for_socket(const char* filename, long timeout) {
+ int fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+ if (fd < 0) {
+ _cupsLangPrintf(stderr, _("Failed to create socket"));
+ _exit(1);
+ }
+
+ struct sockaddr_un addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, filename);
+
+ _cupsLangPrintf(stderr, _("Waiting for %s to be ready for connections"),
+ filename);
+
+ struct timespec start;
+ if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) {
+ _cupsLangPrintf(stderr, _("Failed to get clock time"));
+ _exit(1);
+ }
+
+ while (connect(fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
+ struct timespec current;
+ if (clock_gettime(CLOCK_MONOTONIC, &current) < 0) {
+ _cupsLangPrintf(stderr, _("Failed to get clock time"));
+ _exit(1);
+ }
+
+ if (current.tv_sec - start.tv_sec >= timeout) {
+ _cupsLangPrintf(stderr, _("Timed out waiting for socket %s"), filename);
+ _exit(1);
+ }
+ usleep(100);
+ }
+
+ _cupsLangPrintf(stderr, _("%s is now ready for connections"), filename);
+
+ close(fd);
+}
diff --git a/systemv/lpadmin.c b/systemv/lpadmin.c
index c181d2b..f0f9e33 100644
--- a/systemv/lpadmin.c
+++ b/systemv/lpadmin.c
@@ -20,6 +20,7 @@
#define _CUPS_NO_DEPRECATED
#define _PPD_DEPRECATED
#include <cups/cups-private.h>
+#include <cups/ippusb-private.h>
/*
@@ -1217,10 +1218,35 @@ get_printer_ppd(const char *uri, /* I - Printer URI */
return (NULL);
}
- http = httpConnect2(host, port, NULL, AF_UNSPEC, !strcmp(scheme, "ipps") ? HTTP_ENCRYPTION_ALWAYS : HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL);
- if (!http)
- {
- _cupsLangPrintf(stderr, _("%s: Unable to connect to \"%s:%d\": %s"), "lpadmin", host, port, cupsLastErrorString());
+ // If the scheme is ippusb then a query is sent to the ippusb_manager service
+ // to check if the printer is currently connected and get the name of the
+ // socket used for communication with the printer.
+ if (!strcmp(scheme, "ippusb")) {
+ int sock = open_ippusb_manager_socket();
+ char* response = query_ippusb_manager(sock, host);
+ _cupsLangPrintf(stderr, _("lpadmin: received response \"%s\""), response);
+
+ close(sock);
+
+ int ret = snprintf(host, sizeof(host), "/run/ippusb/%s", response);
+ if (ret < 0 || ret >= sizeof(host)) {
+ _cupsLangPrintf(stderr, _("lpadmin: Failed to overwrite host"));
+ _exit(1);
+ }
+
+ free(response);
+
+ // Wait a maximum of 3 seconds for the socket to be created.
+ wait_for_socket(host, 3);
+ }
+
+ http = httpConnect2(host, port, NULL, AF_UNSPEC,
+ !strcmp(scheme, "ipps") ? HTTP_ENCRYPTION_ALWAYS
+ : HTTP_ENCRYPTION_IF_REQUESTED,
+ 1, 30000, NULL);
+ if (!http) {
+ _cupsLangPrintf(stderr, _("%s: Unable to connect to \"%s:%d\": %s"),
+ "lpadmin", host, port, cupsLastErrorString());
return (NULL);
}
@@ -1229,7 +1255,26 @@ get_printer_ppd(const char *uri, /* I - Printer URI */
*/
request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
- ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+ if (!strcmp(scheme, "ippusb")) {
+ // Change the uri back to use the ipp scheme for communicating with the cups
+ // server and so that communications will be understood by the printer.
+ // We can't simply change the existing uri because we want lpadmin to save
+ // the printer in the system as "ippusb", but need the "ipp" scheme in
+ // |fixed_uri| in order to communicate with the printer.
+ char* fixed_uri = change_scheme(uri, "ipp");
+ if (!fixed_uri) {
+ _cupsLangPrintf(stderr, _("%s: Failed to change uri to %s"), "lpadmin",
+ "ipp");
+ _exit(1);
+ }
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
+ fixed_uri);
+ free(fixed_uri);
+ } else {
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
+ uri);
+ }
+
response = cupsDoRequest(http, request, resource);
if (!_ppdCreateFromIPP(buffer, bufsize, response))
@@ -1357,6 +1402,8 @@ set_printer_options(
else
request = ippNewRequest(IPP_OP_CUPS_ADD_MODIFY_PRINTER);
+ _cupsLangPrintf(stderr, _("lpadmin: printer uri %s"), uri);
+
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
--
2.16.0.rc0.223.g4a4ac83678-goog