| 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(¤t_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 |
| |