blob: ff5f7878bac622fdcc73887ffdd133d2a2754b86 [file] [log] [blame]
From bc619954ef85cc77b1d863a301d9ebc9ff8b398a Mon Sep 17 00:00:00 2001
From: DavieV <davidvalleau@gmail.com>
Date: Fri, 20 Oct 2017 11:33:18 -0700
Subject: [PATCH] Adding support for unix domain sockets to the ippusbxd daemon
Adds support for unix sockets to the ippusbxd program. Ippusbxd is used for
ipp-over-usb printing, but the upstream version only supports network sockets.
Support for unix domain sockets has been added so that ipp-over-usb printing
on Chrome OS will be more secure by settings the correct file permissions on
the sockets used for communication during printing.
Since this change required some Chrome OS specific changes in order for it to
work properly, the author has decided that they would rather not upstream any
of these changes.
Here is a link to the pull request from the original patch:
https://github.com/tillkamppeter/ippusbxd/pull/12
---
src/CMakeLists.txt | 1 +
src/ippusbxd.c | 208 ++++++++++++++++++++++++++++++++-------------
src/logging.h | 9 +-
src/options.h | 4 +
src/uds.c | 204 ++++++++++++++++++++++++++++++++++++++++++++
src/uds.h | 46 ++++++++++
src/usb.c | 4 +
7 files changed, 417 insertions(+), 59 deletions(-)
create mode 100644 src/uds.c
create mode 100644 src/uds.h
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e6809b5..b59f3da 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -37,6 +37,7 @@ usb.c
logging.c
options.c
dnssd.c
+uds.c
)
target_link_libraries(ippusbxd ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(ippusbxd ${LIBUSB_LIBRARIES})
diff --git a/src/ippusbxd.c b/src/ippusbxd.c
index b0fae90..3ef91ad 100644
--- a/src/ippusbxd.c
+++ b/src/ippusbxd.c
@@ -19,6 +19,7 @@
#include <string.h>
#include <signal.h>
+#include <sys/time.h>
#include <unistd.h>
#include <getopt.h>
#include <pthread.h>
@@ -32,9 +33,11 @@
#include "tcp.h"
#include "usb.h"
#include "dnssd.h"
+#include "uds.h"
struct service_thread_param {
struct tcp_conn_t *tcp;
+ struct uds_conn_t *uds;
struct usb_sock_t *usb_sock;
struct usb_conn_t *usb_conn;
pthread_t thread_handle;
@@ -46,6 +49,7 @@ struct libusb_callback_data {
int *read_inflight;
uint32_t thread_num;
struct tcp_conn_t *tcp;
+ struct uds_conn_t *uds;
struct http_packet_t *pkt;
pthread_mutex_t *read_inflight_mutex;
pthread_cond_t *read_inflight_cond;
@@ -83,6 +87,8 @@ static pthread_mutex_t thread_register_mutex;
static struct service_thread_param **service_threads = NULL;
static uint32_t num_service_threads = 0;
+static struct timeval start_time;
+
static void sigterm_handler(int sig)
{
/* Flag that we should stop and return... */
@@ -185,6 +191,37 @@ cleanup_handler(void *arg_void)
pthread_mutex_unlock(&thread_register_mutex);
}
+static void check_timeout(void)
+{
+ if (num_service_threads == 0 && !g_options.measuring_timeout) {
+ if (gettimeofday(&start_time, NULL) < 0) {
+ ERR("Failed to get time");
+ g_options.terminate = 1;
+ return;
+ }
+ g_options.measuring_timeout = 1;
+ } else if (num_service_threads > 0) {
+ g_options.measuring_timeout = 0;
+ }
+
+ if (g_options.measuring_timeout) {
+ struct timeval current_time;
+ if (gettimeofday(&current_time, NULL)) {
+ ERR("Failed to get time");
+ g_options.terminate = 1;
+ return;
+ }
+
+ time_t seconds = current_time.tv_sec - start_time.tv_sec;
+
+ NOTE("Elapsed time: %lld seconds", (long long)seconds);
+ if (seconds > 10) {
+ NOTE("Timeout has been reached - shutting down");
+ g_options.terminate = 1;
+ }
+ }
+}
+
static void read_transfer_callback(struct libusb_transfer *transfer)
{
struct libusb_callback_data *user_data =
@@ -199,13 +236,17 @@ static void read_transfer_callback(struct libusb_transfer *transfer)
user_data->pkt->filled_size = transfer->actual_length;
if (transfer->actual_length) {
- NOTE("Thread #%u: Pkt from %s (buffer size: %zu)\n===\n%s===", thread_num,
- "usb", user_data->pkt->filled_size,
+ NOTE("Thread #%u: Pkt from %s (buffer size: %zu)\n===\n%s===",
+ thread_num, "usb", user_data->pkt->filled_size,
hexdump(user_data->pkt->buffer, (int)user_data->pkt->filled_size));
- tcp_packet_send(user_data->tcp, user_data->pkt);
- /* Mark the tcp socket as active. */
- set_is_active(user_data->tcp, 1);
+ if (g_options.unix_socket_mode) {
+ uds_packet_send(user_data->uds, user_data->pkt);
+ } else {
+ tcp_packet_send(user_data->tcp, user_data->pkt);
+ /* Mark the tcp socket as active. */
+ set_is_active(user_data->tcp, 1);
+ }
}
break;
@@ -214,8 +255,7 @@ static void read_transfer_callback(struct libusb_transfer *transfer)
g_options.terminate = 1;
break;
case LIBUSB_TRANSFER_TIMED_OUT:
- NOTE(
- "Thread #%u: The transfer timed out before it could be completed: "
+ ERR("Thread #%u: The transfer timed out before it could be completed: "
"Received %u bytes",
thread_num, transfer->actual_length);
break;
@@ -270,6 +310,8 @@ static void *service_connection(void *params_void)
(struct service_thread_param *)params_void;
uint32_t thread_num = params->thread_num;
+ NOTE("Thread #%u: Setting up both ends for communication", thread_num);
+
/* Detach this thread so that the main thread does not need to join this
thread after termination for clean-up. */
pthread_detach(pthread_self());
@@ -299,6 +341,8 @@ static void *service_connection(void *params_void)
printer_params->thread_num += 1;
/* Attempt to start the printer's end of the communication. */
+ NOTE("Thread #%u: Attempting to register thread %u", thread_num,
+ thread_num + 1);
if (setup_communication_thread(&service_printer_connection, printer_params))
goto cleanup;
@@ -330,7 +374,10 @@ cleanup:
NOTE("Thread #%u: closing, %s", thread_num,
g_options.terminate ? "shutdown requested"
: "communication thread terminated");
- tcp_conn_close(params->tcp);
+ if (g_options.unix_socket_mode)
+ uds_conn_close(params->uds);
+ else
+ tcp_conn_close(params->tcp);
/* Execute clean-up handler. */
pthread_cleanup_pop(1);
@@ -342,29 +389,28 @@ static void service_socket_connection(struct service_thread_param *params)
{
uint32_t thread_num = params->thread_num;
+ NOTE("Thread #%u: Starting on socket end", thread_num);
+
+ struct http_packet_t *pkt = NULL;
+
while (is_socket_open(params) && !g_options.terminate) {
- int result = poll_tcp_socket(params->tcp);
- if (result < 0 || !is_socket_open(params)) {
- NOTE("Thread #%u: Client closed connection", thread_num);
- return;
- } else if (result == 0) {
- continue;
- }
+ if (g_options.unix_socket_mode)
+ pkt = uds_packet_get(params->uds);
+ else
+ pkt = tcp_packet_get(params->tcp);
- struct http_packet_t *pkt = tcp_packet_get(params->tcp);
if (pkt == NULL) {
- NOTE("Thread #%u: There was an error reading from the socket",
- thread_num);
- return;
- }
-
- if (!is_socket_open(params)) {
- NOTE("Thread #%u: Client closed connection", thread_num);
+ if (!is_socket_open(params))
+ NOTE("Thread: #%u: Client closed connection", thread_num);
+ else
+ NOTE("Thread: #%u: There was an error reading from the socket",
+ thread_num);
return;
}
- NOTE("Thread #%u: Pkt from tcp (buffer size: %zu)\n===\n%s===", thread_num,
- pkt->filled_size, hexdump(pkt->buffer, (int)pkt->filled_size));
+ NOTE("Thread #%u: Pkt from %s (buffer size: %zu)\n===\n%s===", thread_num,
+ g_options.unix_socket_mode ? "uds" : "tcp", pkt->filled_size,
+ hexdump(pkt->buffer, (int)pkt->filled_size));
/* Send pkt to printer. */
usb_conn_packet_send(params->usb_conn, pkt);
@@ -402,6 +448,8 @@ static void *service_printer_connection(void *params_void)
(struct service_thread_param *)params_void;
uint32_t thread_num = params->thread_num;
+ NOTE("Thread #%u: Starting on printer end", thread_num);
+
/* Register clean-up handler. */
pthread_cleanup_push(cleanup_handler, &thread_num);
@@ -426,6 +474,7 @@ static void *service_printer_connection(void *params_void)
if (!is_socket_open(params) || g_options.terminate)
break;
+ NOTE("Thread #%u: No read in flight, starting a new one", thread_num);
struct http_packet_t *pkt = packet_new();
if (pkt == NULL) {
ERR("Thread #%u: Failed to allocate packet", thread_num);
@@ -518,14 +567,17 @@ static uint16_t open_tcp_socket(void)
return desired_port;
}
-/* Attempts to allocate space for a tcp socket. If the allocation is
- successful then a value of 0 is returned, otherwise a non-zero value is
- returned. */
+/* Attempts to allocate space for either a uds socket or a tcp socket depending
+ on the value of |g_options.unix_socket_mode|. If the allocation is successful
+ then a value of 0 is returned, otherwise a non-zero value is returned. */
static int allocate_socket_connection(struct service_thread_param *param)
{
- param->tcp = calloc(1, sizeof(*param->tcp));
+ if (g_options.unix_socket_mode)
+ param->uds = calloc(1, sizeof(*param->uds));
+ else
+ param->tcp = calloc(1, sizeof(*param->tcp));
- if (param->tcp == NULL) {
+ if (param->uds == NULL && param->tcp == NULL) {
ERR("Preparing thread #%u: Failed to allocate space for cups connection",
param->thread_num);
return -1;
@@ -534,14 +586,26 @@ static int allocate_socket_connection(struct service_thread_param *param)
return 0;
}
-/* Attempts to setup a connection for to a tcp socket. Returns a 0 value on
- success and a non-zero value if something went wrong attempting to establish
- the connection. */
+/* Attempts to setup a connection to either the uds or tcp socket depending on
+ the value of |g_options.unix_socket_mode|. Returns a 0 value on success and a
+ non-zero value if something went wrong attempting to establish the
+ connection. */
static int setup_socket_connection(struct service_thread_param *param)
{
- param->tcp = tcp_conn_select(g_options.tcp_socket, g_options.tcp6_socket);
- if (g_options.terminate || param->tcp == NULL)
- return -1;
+ if (g_options.unix_socket_mode) {
+ int poll_status = 0;
+ while (!g_options.terminate && poll_status == 0) {
+ check_timeout();
+ poll_status = uds_connect(g_options.uds_socket, param->uds);
+ }
+ if (g_options.terminate || poll_status < 0)
+ return -1;
+ } else {
+ param->tcp = tcp_conn_select(g_options.tcp_socket, g_options.tcp6_socket);
+ if (g_options.terminate || param->tcp == NULL)
+ return -1;
+ }
+
return 0;
}
@@ -602,12 +666,18 @@ static struct libusb_callback_data *setup_libusb_callback_data(
data->thread_num = thread_param->thread_num;
data->read_inflight_mutex = read_inflight_mutex;
data->read_inflight_cond = thread_param->cond;
- data->tcp = thread_param->tcp;
+
+ if (g_options.unix_socket_mode)
+ data->uds = thread_param->uds;
+ else
+ data->tcp = thread_param->tcp;
return data;
}
static int is_socket_open(const struct service_thread_param *param) {
+ if (g_options.unix_socket_mode)
+ return !param->uds->is_closed;
return !param->tcp->is_closed;
}
@@ -622,26 +692,35 @@ static void start_daemon()
usb_sock = usb_open();
if (usb_sock == NULL) goto cleanup_usb;
- /* Capture a socket */
- uint16_t desired_port = open_tcp_socket();
- if (g_options.tcp_socket == NULL && g_options.tcp6_socket == NULL)
- goto cleanup_tcp;
+ if (g_options.unix_socket_mode) {
+ g_options.uds_socket = uds_open(g_options.unix_socket_path);
+ if (g_options.uds_socket == NULL)
+ goto cleanup_connections;
+ NOTE("Opened unix socket %s", g_options.unix_socket_path);
+ } else {
+ /* Capture a socket */
+ uint16_t desired_port = open_tcp_socket();
+ if (g_options.tcp_socket == NULL && g_options.tcp6_socket == NULL)
+ goto cleanup_connections;
+
+ if (g_options.tcp_socket)
+ g_options.real_port = tcp_port_number_get(g_options.tcp_socket);
+ else
+ g_options.real_port = tcp_port_number_get(g_options.tcp6_socket);
+
+ if (desired_port != 0 && g_options.only_desired_port == 1 &&
+ desired_port != g_options.real_port) {
+ ERR("Received port number did not match requested port number."
+ " The requested port number may be too high.");
+ goto cleanup_connections;
+ }
+ printf("%u|", g_options.real_port);
+ fflush(stdout);
- if (g_options.tcp_socket)
- g_options.real_port = tcp_port_number_get(g_options.tcp_socket);
- else
- g_options.real_port = tcp_port_number_get(g_options.tcp6_socket);
- if (desired_port != 0 && g_options.only_desired_port == 1 &&
- desired_port != g_options.real_port) {
- ERR("Received port number did not match requested port number."
- " The requested port number may be too high.");
- goto cleanup_tcp;
+ NOTE("Port: %d, IPv4 %savailable, IPv6 %savailable", g_options.real_port,
+ g_options.tcp_socket ? "" : "not ",
+ g_options.tcp6_socket ? "" : "not ");
}
- printf("%u|", g_options.real_port);
- fflush(stdout);
-
- NOTE("Port: %d, IPv4 %savailable, IPv6 %savailable",
- g_options.real_port, g_options.tcp_socket ? "" : "not ", g_options.tcp6_socket ? "" : "not ");
/* Lose connection to caller */
uint16_t pid;
@@ -682,7 +761,7 @@ static void start_daemon()
that cups-browsed and ippfind will discover it */
if (g_options.nobroadcast == 0) {
if (dnssd_init() == -1)
- goto cleanup_tcp;
+ goto cleanup_connections;
}
/* Main loop */
@@ -698,7 +777,8 @@ static void start_daemon()
args->thread_num = i;
args->usb_sock = usb_sock;
- /* Allocate space for a tcp socket to be used for communication. */
+ /* Allocate space for either a uds or tcp socket to be used for
+ communication. */
if (allocate_socket_connection(args))
goto cleanup_thread;
@@ -719,12 +799,14 @@ static void start_daemon()
if (args != NULL) {
if (args->tcp != NULL)
tcp_conn_close(args->tcp);
+ if (args->uds != NULL)
+ uds_conn_close(args->uds);
free(args);
}
break;
}
- cleanup_tcp:
+ cleanup_connections:
/* Stop DNS-SD advertising of the printer */
if (g_options.dnssd_data != NULL)
dnssd_shutdown();
@@ -751,6 +833,10 @@ static void start_daemon()
if (g_options.tcp6_socket!= NULL)
tcp_close(g_options.tcp6_socket);
+ /* UDS clean-up */
+ if (g_options.uds_socket != NULL)
+ uds_close(g_options.uds_socket);
+
cleanup_usb:
/* USB clean-up and final reset of the printer */
if (usb_sock != NULL)
@@ -788,6 +874,7 @@ int main(int argc, char *argv[])
{"from-port", required_argument, 0, 'P' },
{"only-port", required_argument, 0, 'p' },
{"interface", required_argument, 0, 'i' },
+ {"uds-path", required_argument, 0, 'U' },
{"logging", no_argument, 0, 'l' },
{"debug", no_argument, 0, 'd' },
{"verbose", no_argument, 0, 'q' },
@@ -805,6 +892,7 @@ int main(int argc, char *argv[])
g_options.product_id = 0;
g_options.bus = 0;
g_options.device = 0;
+ g_options.measuring_timeout = 0;
while ((c = getopt_long(argc, argv, "qnhdp:P:i:s:lv:m:B",
long_options, &option_index)) != -1) {
@@ -882,6 +970,10 @@ int main(int argc, char *argv[])
case 'B':
g_options.nobroadcast = 1;
break;
+ case 'U':
+ g_options.unix_socket_mode = 1;
+ g_options.unix_socket_path = strdup(optarg);
+ break;
}
}
diff --git a/src/logging.h b/src/logging.h
index 1f584ca..d6a2db5 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -16,6 +16,7 @@
#include <pthread.h> /* For pthread_self() */
#include "options.h"
#include "dnssd.h"
+#include "uds.h"
#define TID() (pthread_self())
enum log_level {
@@ -49,7 +50,13 @@ enum log_level {
#define CONF_1(msg) BASE_LOG(LOGGING_CONFORMANCE, "<%d>Standard Conformance Failure: " msg "\n", TID())
#define CONF_2(msg, ...) BASE_LOG(LOGGING_CONFORMANCE, "<%d>Standard Conformance Failure: " msg "\n", TID(), __VA_ARGS__)
-#define ERR_AND_EXIT(...) do { ERR(__VA_ARGS__); if (g_options.dnssd_data != NULL) dnssd_shutdown(g_options.dnssd_data); exit(-1);} while (0)
+#define ERR_AND_EXIT(...) \
+ do { \
+ ERR(__VA_ARGS__); \
+ if (g_options.dnssd_data != NULL) dnssd_shutdown(g_options.dnssd_data); \
+ if (g_options.uds_socket != NULL) uds_close(g_options.uds_socket); \
+ exit(-1); \
+ } while (0)
void BASE_LOG(enum log_level, const char *, ...);
char* hexdump (void *addr, int len);
diff --git a/src/options.h b/src/options.h
index e167514..99cadd4 100644
--- a/src/options.h
+++ b/src/options.h
@@ -29,6 +29,7 @@ struct options {
int only_desired_port;
uint16_t real_port;
char *interface;
+ char *unix_socket_path;
enum log_target log_destination;
/* Behavior */
@@ -36,6 +37,7 @@ struct options {
int verbose_mode;
int nofork_mode;
int nobroadcast;
+ int unix_socket_mode;
/* Printer identity */
unsigned char *serial_num;
@@ -47,10 +49,12 @@ struct options {
/* Global variables */
int terminate;
+ int measuring_timeout;
dnssd_t *dnssd_data;
pthread_t usb_event_thread_handle;
struct tcp_sock_t *tcp_socket;
struct tcp_sock_t *tcp6_socket;
+ struct uds_sock_t *uds_socket;
};
extern struct options g_options;
diff --git a/src/uds.c b/src/uds.c
new file mode 100644
index 0000000..be2dab0
--- /dev/null
+++ b/src/uds.c
@@ -0,0 +1,204 @@
+// 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "http.h"
+#include "logging.h"
+#include "options.h"
+#include "uds.h"
+
+struct uds_sock_t *uds_open(const char *path)
+{
+ struct uds_sock_t *sock = calloc(1, sizeof(*sock));
+ if (sock == NULL) {
+ ERR("UDS: Allocating memory for socket failed");
+ goto error;
+ }
+
+ if ((sock->fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0)) < 0) {
+ ERR("UDS: Opening socket failed");
+ goto error;
+ }
+
+ int val = 1;
+ if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
+ ERR("UDS: Setting socket options failed");
+ goto error;
+ }
+
+ // Configure socket parameters.
+ struct sockaddr_un addr;
+ memset(&addr, 0, sizeof(addr));
+
+ addr.sun_family = AF_UNIX;
+
+ // Check that the length of the socket path is not too long. The maximum size
+ // of a path in a sockaddr_un is 108 characters.
+ if (strlen(path) >= sizeof(addr.sun_path)) {
+ ERR("UDS: Provided socket path %s is too long", path);
+ goto error;
+ }
+
+ strcpy(addr.sun_path, path);
+
+ NOTE("UDS: Binding to %s", path);
+
+ if (bind(sock->fd, (struct sockaddr *)&addr, sizeof(addr))) {
+ ERR("UDS: Binding to socket failed - %s", strerror(errno));
+ goto error;
+ }
+
+ if (listen(sock->fd, UDS_MAX_PENDING_CONNS)) {
+ ERR("UDS: Listen on socket failed");
+ goto error;
+ }
+
+ sock->addr = addr;
+
+ return sock;
+
+error:
+ if (sock != NULL) {
+ if (sock->fd != -1) {
+ close(sock->fd);
+ unlink(sock->addr.sun_path);
+ }
+ free(sock);
+ }
+ return NULL;
+}
+
+void uds_close(struct uds_sock_t *sock)
+{
+ close(sock->fd);
+ unlink(sock->addr.sun_path);
+ free(sock);
+}
+
+int uds_connect(struct uds_sock_t *sock, struct uds_conn_t *conn)
+{
+ if (sock == NULL) {
+ ERR("UDS: No valid unix socket provided");
+ return -1;
+ }
+
+ struct pollfd poll_fd;
+ poll_fd.fd = sock->fd;
+ poll_fd.events = POLLIN;
+ int timeout = 1000;
+
+ int retval = poll(&poll_fd, 1, timeout);
+
+ if (g_options.terminate)
+ return -1;
+
+ if (retval < 0) {
+ ERR("Something went wrong when polling the uds socket");
+ return -1;
+ }
+
+ if (retval == 0) {
+ return 0;
+ }
+
+ if ((conn->fd = accept(sock->fd, NULL, NULL)) < 0) {
+ ERR("Failed to connect to uds socket");
+ return -1;
+ }
+
+ NOTE("Successfully connected to socket");
+
+ return 1;
+}
+
+void uds_conn_close(struct uds_conn_t *conn)
+{
+ shutdown(conn->fd, SHUT_RDWR);
+ close(conn->fd);
+ free(conn);
+}
+
+struct http_packet_t *uds_packet_get(struct uds_conn_t *conn) {
+ struct http_packet_t *pkt = packet_new();
+ if (pkt == NULL) {
+ ERR("UDS: Allocating memory for incoming uds message failed");
+ goto error;
+ }
+
+ struct timeval tv;
+ tv.tv_sec = 3;
+ tv.tv_usec = 0;
+ if (setsockopt(conn->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) {
+ ERR("UDS: Setting options for uds socket failed");
+ goto error;
+ }
+
+ ssize_t gotten_size = recv(conn->fd, pkt->buffer, pkt->buffer_capacity, 0);
+
+ if (gotten_size < 0) {
+ int errno_saved = errno;
+ ERR("UDS: recv failed with err %d:%s", errno_saved, strerror(errno_saved));
+ conn->is_closed = 1;
+ goto error;
+ }
+
+ if (gotten_size == 0) {
+ conn->is_closed = 1;
+ goto error;
+ }
+
+ pkt->filled_size = gotten_size;
+ return pkt;
+
+error:
+ if (pkt != NULL) packet_free(pkt);
+ return NULL;
+}
+
+int uds_packet_send(struct uds_conn_t *conn, struct http_packet_t *pkt)
+{
+ size_t remaining = pkt->filled_size;
+ size_t total = 0;
+
+ while (remaining > 0 && !g_options.terminate) {
+ ssize_t sent = send(conn->fd, pkt->buffer + total, remaining, MSG_NOSIGNAL);
+
+ if (sent < 0) {
+ if (errno == EPIPE) {
+ conn->is_closed = 1;
+ return 0;
+ }
+ int errno_saved = errno;
+ ERR("UDS: send failed with error %d:%s", errno_saved,
+ strerror(errno_saved));
+ return -1;
+ }
+
+ total += sent;
+ if (sent >= remaining)
+ remaining = 0;
+ else
+ remaining -= sent;
+ }
+
+ NOTE("UDS: sent %zu bytes", total);
+ return 0;
+}
+
+int uds_poll_connection(struct uds_conn_t *conn) {
+ struct pollfd poll_fd;
+ poll_fd.fd = conn->fd;
+ poll_fd.events = POLLIN;
+ static int timeout = 1000;
+
+ return poll(&poll_fd, 1, timeout);
+}
diff --git a/src/uds.h b/src/uds.h
new file mode 100644
index 0000000..b79c3c8
--- /dev/null
+++ b/src/uds.h
@@ -0,0 +1,46 @@
+// 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.
+
+#pragma once
+#include <stdint.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "http.h"
+
+// In order for CUPS to determine whether or not the socket is ready it first
+// attempts to open a dummy connection. By the time CUPS attempts to establish
+// the real connection the dummy connection has still not been fully removed by
+// the system, so we must allow for some pending connections.
+#define UDS_MAX_PENDING_CONNS 4
+
+struct uds_sock_t {
+ int fd;
+ struct sockaddr_un addr;
+ socklen_t info_size;
+};
+
+struct uds_conn_t {
+ int fd;
+ int is_closed;
+};
+
+struct uds_sock_t *uds_open(const char *path);
+void uds_close(struct uds_sock_t *sock);
+
+// Polls the unix socket described by |sock| to see if there is data to be read.
+// If there is then it opens a new connection and stores it in |conn|. This
+// function returns 1 if the connection opens sucessfuly, 0 if the socket is not
+// currently ready, and -1 if there was an error.
+int uds_connect(struct uds_sock_t *sock, struct uds_conn_t *conn);
+
+void uds_conn_close(struct uds_conn_t *conn);
+
+struct http_packet_t *uds_packet_get(struct uds_conn_t *conn);
+
+int uds_packet_send(struct uds_conn_t *conn, struct http_packet_t *pkt);
+
+int uds_poll_connection(struct uds_conn_t *conn);
diff --git a/src/usb.c b/src/usb.c
index fa8ce59..ffa3e80 100644
--- a/src/usb.c
+++ b/src/usb.c
@@ -555,6 +555,10 @@ static int LIBUSB_CALL usb_exit_on_unplug(libusb_context *context,
if (g_options.tcp6_socket!= NULL)
tcp_close(g_options.tcp6_socket);
+ /* UDS clean-up */
+ if (g_options.uds_socket != NULL)
+ uds_close(g_options.uds_socket);
+
exit(0);
}
--
2.19.1.568.g152ad8e336-goog