blob: 0617d2c5fc643e1116e5efac702977820f1f4183 [file] [log] [blame]
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.
---
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 413be5c..6aac59e 100644
--- a/src/ippusbxd.c
+++ b/src/ippusbxd.c
@@ -25,6 +25,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/time.h>
#include <unistd.h>
#include "dnssd.h"
@@ -32,6 +33,7 @@
#include "logging.h"
#include "options.h"
#include "tcp.h"
+#include "uds.h"
#include "usb.h"
/* Global variables */
@@ -39,6 +41,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... */
@@ -148,6 +152,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 =
@@ -165,9 +200,13 @@ static void read_transfer_callback(struct libusb_transfer *transfer)
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);
+ }
} else {
/* Set that we received an empty response from the printer. */
*user_data->empty_response = 1;
@@ -179,8 +218,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;
@@ -226,6 +264,8 @@ 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());
@@ -255,6 +295,8 @@ 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;
@@ -286,7 +328,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);
@@ -297,29 +342,46 @@ 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) {
+ int poll_result = uds_poll_connection(params->uds->fd);
+ if (poll_result < 0) {
+ ERR("Thread #%u: Failed to poll the uds socket");
+ params->uds->is_closed = 1;
+ } else if (poll_result == 0) {
+ continue;
+ }
+ // Only attempt to read a packet if the poll result indicates that there
+ // is data to be read.
+ pkt = uds_packet_get(params->uds);
+ } else {
+ int poll_result = poll_tcp_socket(params->tcp);
+ if (poll_result < 0 || !is_socket_open(params)) {
+ NOTE("Thread #%u: Client closed connection", thread_num);
+ return;
+ } else if (poll_result == 0) {
+ continue;
+ }
- 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;
+ pkt = tcp_packet_get(params->tcp);
}
- if (!is_socket_open(params)) {
- NOTE("Thread #%u: Client closed connection", thread_num);
+ if (pkt == NULL) {
+ 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);
@@ -334,6 +396,8 @@ 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);
@@ -475,9 +539,12 @@ static uint16_t open_tcp_socket(void)
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;
@@ -488,9 +555,20 @@ int allocate_socket_connection(struct service_thread_param *param)
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;
}
@@ -546,7 +624,11 @@ 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;
}
@@ -568,6 +650,8 @@ void set_read_inflight(int val, pthread_mutex_t *mtx, int *read_inflight)
}
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;
}
@@ -591,26 +675,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;
@@ -651,7 +744,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 */
@@ -667,7 +760,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;
@@ -688,12 +782,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();
@@ -720,6 +816,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)
@@ -757,6 +857,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' },
@@ -774,6 +875,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) {
@@ -851,6 +953,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/ippusbxd.h b/src/ippusbxd.h
index 098a45a..69461fe 100644
--- a/src/ippusbxd.h
+++ b/src/ippusbxd.h
@@ -10,6 +10,7 @@
struct service_thread_param {
/* Connection to the device issuing requests to the printer. */
struct tcp_conn_t *tcp;
+ struct uds_conn_t *uds;
/* Socket which holds the context for the bound USB printer. */
struct usb_sock_t *usb_sock;
/* Represents a connection to a specific USB interface. */
@@ -31,6 +32,7 @@ struct libusb_callback_data {
int *empty_response;
uint32_t thread_num;
struct tcp_conn_t *tcp;
+ struct uds_conn_t *uds;
/* The contents of the response from the printer. */
struct http_packet_t *pkt;
pthread_mutex_t *read_inflight_mutex;
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 7cb25d3..dc93bcf 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,6 +49,7 @@ 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;
@@ -54,6 +57,8 @@ struct options {
/* Scanner presence */
int scanner_present;
+
+ 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..de349db
--- /dev/null
+++ b/src/uds.c
@@ -0,0 +1,200 @@
+// 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;
+ }
+
+ int poll_result = uds_poll_connection(sock->fd);
+
+ if (g_options.terminate)
+ return -1;
+
+ if (poll_result < 0) {
+ ERR("Something went wrong when polling the uds socket");
+ return -1;
+ }
+
+ // There is no data to be read on the socket.
+ if (poll_result == 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;
+}
+
+// Polls the UDS connection in |conn| to see if there is any data to be read.
+int uds_poll_connection(int fd) {
+ struct pollfd poll_fd;
+ poll_fd.fd = 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..67c13f9
--- /dev/null
+++ b/src/uds.h
@@ -0,0 +1,51 @@
+// 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);
+
+// Polls the given file descriptor |fd| to see if there is any data to be read.
+// Return values:
+// 1 - there is data to be read.
+// 0 - there is not any data to be read.
+// -1 - there was an error.
+int uds_poll_connection(int fd);
diff --git a/src/usb.c b/src/usb.c
index a47aa98..21c84d4 100644
--- a/src/usb.c
+++ b/src/usb.c
@@ -599,6 +599,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.25.0.341.g760bfbb309-goog