APIE process_set_identity(uid_t uid, gid_t gid) { APIE error_code; struct passwd *pw; gid_t groups[128]; // FIXME: maybe allocate this dynamically int length = sizeof(groups) / sizeof(gid_t); // get username pw = getpwuid(uid); if (pw == NULL) { error_code = api_get_error_code_from_errno(); log_error("Could not get passwd entry for user ID %u: %s (%d)", uid, get_errno_name(errno), errno); return error_code; } // get (secondary) groups if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &length) < 0) { error_code = api_get_error_code_from_errno(); log_error("Could not get secondary groups for user ID %u: %s (%d)", uid, get_errno_name(errno), errno); return error_code; } // set (primary) group if (setregid(gid, gid) < 0) { error_code = api_get_error_code_from_errno(); log_error("Could not change real and effective group ID to %u: %s (%d)", gid, get_errno_name(errno), errno); return error_code; } // set (secondary) groups if (setgroups(length, groups) < 0) { error_code = api_get_error_code_from_errno(); log_error("Could not set secondary group IDs of user ID %u: %s (%d)", uid, get_errno_name(errno), errno); return error_code; } // set user if (setreuid(uid, uid) < 0) { error_code = api_get_error_code_from_errno(); log_error("Could not change real and effective user ID to %u: %s (%d)", uid, get_errno_name(errno), errno); return error_code; } return API_E_SUCCESS; }
static void handle_device_event(DWORD event_type) { uint8_t byte = 0; switch (event_type) { case DBT_DEVICEARRIVAL: log_debug("Received device notification (type: arrival)"); if (pipe_write(_notification_pipe[1], &byte, sizeof(byte)) < 0) { log_error("Could not write to notification pipe: %s (%d)", get_errno_name(errno), errno); } break; case DBT_DEVICEREMOVECOMPLETE: log_debug("Received device notification (type: removal)"); if (pipe_write(_notification_pipe[1], &byte, sizeof(byte)) < 0) { log_error("Could not write to notification pipe: %s (%d)", get_errno_name(errno), errno); } break; } }
int service_is_running(void) { SC_HANDLE service_control_manager; int rc; SC_HANDLE service; SERVICE_STATUS service_status; // open service control manager service_control_manager = OpenSCManager(0, 0, SC_MANAGER_CONNECT); if (service_control_manager == NULL) { rc = ERRNO_WINAPI_OFFSET + GetLastError(); log_error("Could not open service control manager: %s (%d)", get_errno_name(rc), rc); return -1; } // open service service = OpenService(service_control_manager, _service_name, SERVICE_QUERY_STATUS); if (service == NULL) { rc = GetLastError(); if (rc == ERROR_SERVICE_DOES_NOT_EXIST) { CloseServiceHandle(service_control_manager); return 0; } rc += ERRNO_WINAPI_OFFSET; log_error("Could not open '%s' service: %s (%d)", _service_name, get_errno_name(rc), rc); CloseServiceHandle(service_control_manager); return -1; } // get service status if (!QueryServiceStatus(service, &service_status)) { rc = ERRNO_WINAPI_OFFSET + GetLastError(); log_error("Could not query status of '%s' service: %s (%d)", _service_name, get_errno_name(rc), rc); CloseServiceHandle(service); CloseServiceHandle(service_control_manager); return -1; } CloseServiceHandle(service); CloseServiceHandle(service_control_manager); return service_status.dwCurrentState != SERVICE_STOPPED ? 1 : 0; }
// TODO: If we want "real parallel accessibility" of the EEPROM we need to // lock a mutex in the init function and unlock it in the release function int i2c_eeprom_create(I2CEEPROM *i2c_eeprom, int extension) { GPIOREDPin pullup = {GPIO_RED_PORT_B, GPIO_RED_PIN_6}; log_debug("Initializing I2C EEPROM for extension %d", extension); if (i2c_eeprom == NULL || extension < 0 || extension > 1) { log_error("Initialization of I2C EEPROM for extension %d failed (malformed parameters)", extension); return -1; } // Enable pullups gpio_red_mux_configure(pullup, GPIO_RED_MUX_OUTPUT); gpio_red_output_clear(pullup); // Initialize I2C EEPROM structure i2c_eeprom->extension = extension; switch (extension) { case 0: i2c_eeprom->address_pin.port_index = GPIO_RED_PORT_G; i2c_eeprom->address_pin.pin_index = GPIO_RED_PIN_9; break; case 1: i2c_eeprom->address_pin.port_index = GPIO_RED_PORT_G; i2c_eeprom->address_pin.pin_index = GPIO_RED_PIN_13; break; } // enable I2C bus with GPIO_RED gpio_red_mux_configure(i2c_eeprom->address_pin, GPIO_RED_MUX_OUTPUT); i2c_eeprom_deselect(i2c_eeprom); i2c_eeprom->file = open(I2C_EEPROM_BUS, O_RDWR); if (i2c_eeprom->file < 0) { log_error("Initialization of I2C EEPROM for extension %d failed (Unable to open I2C bus: %s (%d))", extension, get_errno_name(errno), errno); return -1; } if (ioctl(i2c_eeprom->file, I2C_SLAVE, I2C_EEPROM_DEVICE_ADDRESS) < 0) { log_error("Initialization of I2C EEPROM for extension %d failed (Unable to access I2C device on the bus: %s (%d))", extension, get_errno_name(errno), errno); i2c_eeprom_destroy(i2c_eeprom); return -1; } return 0; }
static void network_handle_accept(void *opaque) { Socket *server_socket = opaque; Socket *client_socket; struct sockaddr_storage address; socklen_t length = sizeof(address); char hostname[NI_MAXHOST]; char port[NI_MAXSERV]; char buffer[NI_MAXHOST + NI_MAXSERV + 4]; // 4 == strlen("[]:") + 1 char *name = "<unknown>"; Client *client; // accept new client socket client_socket = socket_accept(server_socket, (struct sockaddr *)&address, &length); if (client_socket == NULL) { if (!errno_interrupted()) { log_error("Could not accept new client socket: %s (%d)", get_errno_name(errno), errno); } return; } if (socket_address_to_hostname((struct sockaddr *)&address, length, hostname, sizeof(hostname), port, sizeof(port)) < 0) { log_warn("Could not get hostname and port of client (socket: %d): %s (%d)", client_socket->base.handle, get_errno_name(errno), errno); } else { if (address.ss_family == AF_INET6) { snprintf(buffer, sizeof(buffer), "[%s]:%s", hostname, port); } else { snprintf(buffer, sizeof(buffer), "%s:%s", hostname, port); } name = buffer; } // create new client client = network_create_client(name, &client_socket->base); if (client == NULL) { socket_destroy(client_socket); free(client_socket); return; } #ifdef BRICKD_WITH_RED_BRICK client_send_red_brick_enumerate(client, ENUMERATION_TYPE_CONNECTED); #endif }
void mesh_handle_accept(void *opaque) { char port[NI_MAXSERV]; char *name = "<unknown>"; char hostname[NI_MAXHOST]; Socket *mesh_client_socket; // Socket that is created to the root node of a mesh network. struct sockaddr_storage address; socklen_t length = sizeof(address); Socket *mesh_listen_socket = opaque; char buffer[NI_MAXHOST + NI_MAXSERV + 4]; log_info("New connection on mesh port"); // Accept new mesh client socket. mesh_client_socket = socket_accept(mesh_listen_socket, (struct sockaddr *)&address, &length); if (mesh_client_socket == NULL) { if (!errno_interrupted()) { log_error("Failed to accept new mesh client connection: %s (%d)", get_errno_name(errno), errno); } return; } if (socket_address_to_hostname((struct sockaddr *)&address, length, hostname, sizeof(hostname), port, sizeof(port)) < 0) { log_warn("Could not get hostname and port of mesh client (socket: %d): %s (%d)", mesh_client_socket->base.handle, get_errno_name(errno), errno); } else { snprintf(buffer, sizeof(buffer), "%s:%s", hostname, port); name = buffer; } /* * Allocate and initialise a new mesh stack. Note that in this stage the stack * is not added to brickd's central list of stacks yet. */ if (mesh_stack_create(name, &mesh_client_socket->base) < 0) { log_error("Could not create new mesh stack"); } else { log_info("New mesh stack created"); } }
// public API APIE process_kill(Process *process, ProcessSignal signal) { int rc; APIE error_code; // FIXME: here is a race condition, because the child process might already // be dead at this point, but the process state didn't get updated // yet. this can result in trying to kill a process that's not // existing anymore. or even worse, the process ID has already been // reused and an unrelated process gets killed here if (!process_is_alive(process)) { log_warn("Cannot send signal (number: %d) to an already dead child process (executable: %s)", signal, process->executable->buffer); return API_E_INVALID_OPERATION; } rc = kill(process->pid, signal); if (rc < 0) { error_code = api_get_error_code_from_errno(); log_error("Could not send signal (number: %d) to child process (executable: %s, pid: %u): %s (%d)", signal, process->executable->buffer, process->pid, get_errno_name(errno), errno); return error_code; } return API_E_SUCCESS; }
APIE inventory_add_object(Object *object) { Object **object_ptr; APIE error_code; error_code = inventory_get_next_object_id(&object->id); if (error_code != API_E_SUCCESS) { log_warn("Cannot add new %s object, all object IDs are in use", object_get_type_name(object->type)); return error_code; } object_ptr = array_append(&_objects[object->type]); if (object_ptr == NULL) { error_code = api_get_error_code_from_errno(); log_error("Could not append to %s object array: %s (%d)", object_get_type_name(object->type), get_errno_name(errno), errno); return error_code; } *object_ptr = object; log_object_debug("Added %s object (id: %u)", object_get_type_name(object->type), object->id); return API_E_SUCCESS; }
APIE inventory_add_session(Session *session) { Session **session_ptr; APIE error_code; error_code = inventory_get_next_session_id(&session->id); if (error_code != API_E_SUCCESS) { log_warn("Cannot add new session, all session IDs are in use"); return error_code; } session_ptr = array_append(&_sessions); if (session_ptr == NULL) { error_code = api_get_error_code_from_errno(); log_error("Could not append to session array: %s (%d)", get_errno_name(errno), errno); return error_code; } *session_ptr = session; log_object_debug("Added session (id: %u)", session->id); return API_E_SUCCESS; }
int mesh_init(void) { uint16_t mesh_listen_port = \ (uint16_t)config_get_option_value("listen.mesh_port")->integer; if (mesh_listen_port == 0) { log_info("Mesh support is disabled"); return 0; } log_info("Initializing mesh subsystem"); if (mesh_start_listening(&mesh_listen_socket, mesh_listen_port, socket_create_allocated) >= 0) { is_mesh_listen_socket_open = true; } if (!is_mesh_listen_socket_open) { log_error("Could not open mesh listen socket"); return -1; } // Create mesh stack array. if (array_create(&mesh_stacks, MAX_MESH_STACKS, sizeof(MeshStack), false) < 0) { log_error("Could not create mesh stack array: %s (%d)", get_errno_name(errno), errno); return -1; } return 0; }
static void handle_sighup(void) { FILE *log_file = log_get_file(); if (log_file != NULL) { if (fileno(log_file) == STDOUT_FILENO || fileno(log_file) == STDERR_FILENO) { return; // don't close stdout or stderr } fclose(log_file); } log_file = fopen(_log_filename, "a+"); if (log_file == NULL) { log_set_file(stderr); log_error("Could not reopen log file '%s': %s (%d)", _log_filename, get_errno_name(errno), errno); return; } log_set_file(log_file); log_info("Reopened log file '%s'", _log_filename); }
Client *network_create_client(const char *name, IO *io) { Client *client; // append to client array client = array_append(&_clients); if (client == NULL) { log_error("Could not append to client array: %s (%d)", get_errno_name(errno), errno); return NULL; } // create new client that takes ownership of the I/O object if (client_create(client, name, io, _next_authentication_nonce++, NULL) < 0) { array_remove(&_clients, _clients.count - 1, NULL); return NULL; } log_info("Added new client ("CLIENT_SIGNATURE_FORMAT")", client_expand_signature(client)); return client; }
int service_init(LPHANDLER_FUNCTION_EX handler) { int rc; _service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; _service_status.dwCurrentState = SERVICE_STOPPED; _service_status.dwControlsAccepted = 0; _service_status.dwWin32ExitCode = NO_ERROR; _service_status.dwServiceSpecificExitCode = NO_ERROR; _service_status.dwCheckPoint = 0; _service_status.dwWaitHint = 0; _service_status_handle = RegisterServiceCtrlHandlerEx(_service_name, handler, NULL); if (_service_status_handle == 0) { rc = ERRNO_WINAPI_OFFSET + GetLastError(); log_error("Could not register service control handler: %s (%d)", get_errno_name(rc), rc); return -1; } return 0; }
static int i2c_eeprom_set_pointer(I2CEEPROM *i2c_eeprom, uint8_t *eeprom_memory_address) { int bytes_written = 0; if (i2c_eeprom == NULL || i2c_eeprom->file < 0) { log_error("I2C EEPROM structure uninitialized"); return -1; } bytes_written = robust_write(i2c_eeprom->file, eeprom_memory_address, 2); if (bytes_written != 2) { // We only use debug here to not spam the log with errors. // This is the expected case if an extension is not present. log_debug("Error setting EEPROM address pointer: %s (%d)", get_errno_name(errno), errno); i2c_eeprom_destroy(i2c_eeprom); return -1; } return bytes_written; }
static int service_run(int log_to_file, int debug) { SERVICE_TABLE_ENTRY service_table[2]; int rc; service_table[0].lpServiceName = service_get_name(); service_table[0].lpServiceProc = service_main; service_table[1].lpServiceName = NULL; service_table[1].lpServiceProc = NULL; if (!StartServiceCtrlDispatcher(service_table)) { rc = ERRNO_WINAPI_OFFSET + GetLastError(); if (rc == ERRNO_WINAPI_OFFSET + ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { log_info("Could not start as service, starting as console application"); _run_as_service = 0; _pause_before_exit = started_by_explorer(); return generic_main(log_to_file, debug); } else { log_error("Could not start service control dispatcher: %s (%d)", get_errno_name(rc), rc); log_exit(); config_exit(); return EXIT_FAILURE; } } return EXIT_SUCCESS; }
static LRESULT CALLBACK message_pump_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { int rc; switch (msg) { case WM_USER: log_debug("Destroying message pump window"); if (!DestroyWindow(hwnd)) { rc = ERRNO_WINAPI_OFFSET + GetLastError(); log_warn("Could not destroy message pump window: %s (%d)", get_errno_name(rc), rc); } return 0; case WM_DESTROY: log_debug("Posting quit message message loop"); PostQuitMessage(0); return 0; case WM_DEVICECHANGE: handle_device_event(wparam); return TRUE; } return DefWindowProc(hwnd, msg, wparam, lparam); }
static int get_process_image_name(PROCESSENTRY32 entry, char *buffer, DWORD length) { int rc; HANDLE handle = NULL; QUERYFULLPROCESSIMAGENAME query_full_process_image_name = NULL; handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, entry.th32ProcessID); if (handle == NULL && GetLastError() == ERROR_ACCESS_DENIED) { handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, entry.th32ProcessID); } if (handle == NULL) { rc = ERRNO_WINAPI_OFFSET + GetLastError(); log_warn("Could not open process with ID %u: %s (%d)", (uint32_t)entry.th32ProcessID, get_errno_name(rc), rc); return -1; } query_full_process_image_name = (QUERYFULLPROCESSIMAGENAME)GetProcAddress(GetModuleHandle("kernel32"), "QueryFullProcessImageNameA"); if (query_full_process_image_name != NULL) { if (query_full_process_image_name(handle, 0, buffer, &length) == 0) { rc = ERRNO_WINAPI_OFFSET + GetLastError(); log_warn("Could not get image name of process with ID %u: %s (%d)", (uint32_t)entry.th32ProcessID, get_errno_name(rc), rc); return -1; } } else { memcpy(buffer, entry.szExeFile, length); buffer[length - 1] = '\0'; } CloseHandle(handle); return 0; }
static void program_scheduler_handle_process_state_change(void *opaque) { ProgramScheduler *program_scheduler = opaque; Program *program = containerof(program_scheduler, Program, scheduler); bool spawn = false; if (program_scheduler->state != PROGRAM_SCHEDULER_STATE_RUNNING) { return; } if (program_scheduler->last_spawned_process->state == PROCESS_STATE_EXITED) { if (program_scheduler->last_spawned_process->exit_code == 0) { if (program->config.start_mode == PROGRAM_START_MODE_ALWAYS) { spawn = true; } } else { if (program->config.continue_after_error) { if (program->config.start_mode == PROGRAM_START_MODE_ALWAYS) { spawn = true; } } else { program_scheduler_stop(program_scheduler, NULL); } } } else if (program_scheduler->last_spawned_process->state == PROCESS_STATE_ERROR || program_scheduler->last_spawned_process->state == PROCESS_STATE_KILLED) { if (program->config.continue_after_error) { if (program->config.start_mode == PROGRAM_START_MODE_ALWAYS) { spawn = true; } } else { program_scheduler_stop(program_scheduler, NULL); } } if (spawn) { // delay next process spawn by 1 second to avoid running into a tight // loop of process spawn and process exit events which would basically // stop redapid from doing anything else. also if the throughput between // redapid and brickv is low then sending to many program-process-spawned // callbacks might force brickv into doing nothing else but updating // the last-spawned-program-process information if (timer_configure(&program_scheduler->timer, 1000000, 0) < 0) { program_scheduler_handle_error(program_scheduler, false, "Could not start timer: %s (%d)", get_errno_name(errno), errno); return; } log_debug("Started timer for program object (identifier: %s)", program->identifier->buffer); program_scheduler->timer_active = true; } }
// Get "red_stack_dispatch_from_spi" called from main brickd event thread static int red_stack_spi_request_dispatch_response_event(void) { eventfd_t ev = 1; if (eventfd_write(_red_stack_notification_event, ev) < 0) { log_error("Could not write to red stack spi notification event: %s (%d)", get_errno_name(errno), errno); return -1; } return 0; }
int event_stop_platform(void) { uint8_t byte = 0; if (pipe_write(&_stop_pipe, &byte, sizeof(byte)) < 0) { log_error("Could not write to stop pipe: %s (%d)", get_errno_name(errno), errno); return -1; } return 0; }
int client_create(Client *client, EventHandle socket, struct sockaddr_in *address, socklen_t length) { log_debug("Creating client from socket (handle: %d)", socket); client->socket = socket; client->packet_used = 0; // create pending request array if (array_create(&client->pending_requests, 32, sizeof(PacketHeader), 1) < 0) { log_error("Could not create pending request array: %s (%d)", get_errno_name(errno), errno); return -1; } // get peer name client->peer = resolve_address(address, length); if (client->peer == NULL) { log_warn("Could not get peer name of client (socket: %d): %s (%d)", socket, get_errno_name(errno), errno); client->peer = (char *)_unknown_peer_name; } // add socket as event source if (event_add_source(client->socket, EVENT_SOURCE_TYPE_GENERIC, EVENT_READ, client_handle_receive, client) < 0) { if (client->peer != _unknown_peer_name) { free(client->peer); } array_destroy(&client->pending_requests, NULL); return -1; } return 0; }
void log_init_platform(void) { int rc; _event_log = RegisterEventSource(NULL, "Brick Daemon"); if (_event_log == NULL) { rc = ERRNO_WINAPI_OFFSET + GetLastError(); // this will go to the logfile if it is enabled via --debug log_error("Could not open Windows event log: %s (%d)", get_errno_name(rc), rc); } }
int hardware_init(void) { log_debug("Initializing hardware subsystem"); // create stack array if (array_create(&_stacks, 32, sizeof(Stack *), true) < 0) { log_error("Could not create stack array: %s (%d)", get_errno_name(errno), errno); return -1; } return 0; }
static void iokit_forward_notifications(void *opaque) { uint8_t byte; (void)opaque; if (pipe_read(&_notification_pipe, &byte, sizeof(byte)) < 0) { log_error("Could not read from notification pipe: %s (%d)", get_errno_name(errno), errno); return; } usb_reopen(); }
int hardware_add_stack(Stack *stack) { Stack **new_stack = array_append(&_stacks); if (new_stack == NULL) { log_error("Could not append to stack array: %s (%d)", get_errno_name(errno), errno); return -1; } *new_stack = stack; return 0; }
int client_dispatch_packet(Client *client, Packet *packet, int force) { int i; Packet *pending_request; int found = -1; int rc = -1; if (!force) { for (i = 0; i < client->pending_requests.count; ++i) { pending_request = array_get(&client->pending_requests, i); if (pending_request->header.uid == packet->header.uid && pending_request->header.function_id == packet->header.function_id && pending_request->header.sequence_number == packet->header.sequence_number) { found = i; break; } } } if (force || found >= 0) { if (socket_send(client->socket, packet, packet->header.length) < 0) { log_error("Could not send response to client (socket: %d, peer: %s): %s (%d)", client->socket, client->peer, get_errno_name(errno), errno); goto cleanup; } if (force) { log_debug("Forced to sent response to client (socket: %d, peer: %s)", client->socket, client->peer); } else { log_debug("Sent response to client (socket: %d, peer: %s)", client->socket, client->peer); } } rc = 0; cleanup: if (found >= 0) { array_remove(&client->pending_requests, found, NULL); if (rc == 0) { rc = 1; } } return rc; }
int libusb_submit_transfer(struct libusb_transfer *transfer) { usbi_transfer *itransfer = (usbi_transfer *)transfer; libusb_device_handle *dev_handle = transfer->dev_handle; libusb_context *ctx = dev_handle->dev->ctx; usbfs_urb *urb = &itransfer->urb; int rc; if (transfer->type != LIBUSB_TRANSFER_TYPE_BULK || transfer->timeout != 0 || transfer->callback == NULL) { return LIBUSB_ERROR_INVALID_PARAM; } if (itransfer->submitted) { return LIBUSB_ERROR_BUSY; } libusb_ref_device(dev_handle->dev); itransfer->submitted = true; urb->status = INT32_MIN; urb->endpoint = transfer->endpoint; urb->buffer = transfer->buffer; urb->buffer_length = transfer->length; rc = ioctl(dev_handle->pollfd.fd, IOCTL_USBFS_SUBMITURB, urb); if (rc < 0) { if (errno == ENODEV) { rc = LIBUSB_ERROR_NO_DEVICE; } else { rc = LIBUSB_ERROR_IO; } itransfer->submitted = false; libusb_unref_device(dev_handle->dev); usbi_log_error(ctx, "Could not submit %s transfer %p (length: %d): %s (%d)", (LIBUSB_ENDPOINT_IN & transfer->endpoint) != 0 ? "read" : "write", transfer, transfer->length, get_errno_name(errno), errno); return rc; } node_insert_before(&dev_handle->itransfer_sentinel, &itransfer->node); return LIBUSB_SUCCESS; }
static void socat_handle_receive(void *opaque) { Socat *socat = opaque; int length; length = socket_receive(socat->socket, (uint8_t *)&socat->notification + socat->notification_used, sizeof(CronNotification) - socat->notification_used); if (length == 0) { log_debug("Socat (handle: %d) disconnected by peer", socat->socket->handle); socat->disconnected = true; return; } if (length < 0) { if (errno_interrupted()) { log_debug("Receiving from socat (handle: %d) was interrupted, retrying", socat->socket->handle); } else if (errno_would_block()) { log_debug("Receiving from socat (handle: %d) Daemon would block, retrying", socat->socket->handle); } else { log_error("Could not receive from socat (handle: %d), disconnecting socat: %s (%d)", socat->socket->handle, get_errno_name(errno), errno); socat->disconnected = true; } return; } socat->notification_used += length; if (socat->notification_used < (int)sizeof(CronNotification)) { // wait for complete request return; } cron_handle_notification(&socat->notification); log_debug("Socat (handle: %d) received complete request, disconnecting socat", socat->socket->handle); socat->disconnected = true; }
// New packet from SPI stack is send into brickd event loop static void red_stack_dispatch_from_spi(void *opaque) { eventfd_t ev; (void)opaque; if (eventfd_read(_red_stack_notification_event, &ev) < 0) { log_error("Could not read from SPI notification event: %s (%d)", get_errno_name(errno), errno); return; } // Send message into brickd dispatcher // and allow SPI thread to run again. network_dispatch_response(&_red_stack.packet_from_spi); semaphore_release(&_red_stack_dispatch_packet_from_spi_semaphore); }
int libusb_release_interface(libusb_device_handle *dev_handle, int interface_number) { libusb_context *ctx = dev_handle->dev->ctx; int rc = ioctl(dev_handle->pollfd.fd, IOCTL_USBFS_RELEASEINTF, &interface_number); if (rc < 0) { if (errno == ENODEV) { return LIBUSB_ERROR_NO_DEVICE; } else { usbi_log_error(ctx, "Could not release interface %d: %s (%d)", interface_number, get_errno_name(errno), errno); return LIBUSB_ERROR_OTHER; } } return LIBUSB_SUCCESS; }