/* * Copyright © 2020 Microsoft * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rdp.h" static int cached_tm_mday = -1; void rdp_debug_print(struct weston_log_scope *log_scope, bool cont, char *fmt, ...) { char timestr[128]; int len_va; char *str; if (!log_scope || !weston_log_scope_is_enabled(log_scope)) return; va_list ap; va_start(ap, fmt); if (cont) { weston_log_scope_vprintf(log_scope, fmt, ap); goto end; } weston_log_timestamp(timestr, sizeof(timestr), &cached_tm_mday); len_va = vasprintf(&str, fmt, ap); if (len_va >= 0) { weston_log_scope_printf(log_scope, "%s %s", timestr, str); free(str); } else { const char *oom = "Out of memory"; weston_log_scope_printf(log_scope, "%s %s", timestr, oom); } end: va_end(ap); } #ifdef ENABLE_RDP_THREAD_CHECK void assert_compositor_thread(struct rdp_backend *b) { assert(b->compositor_tid == gettid()); } void assert_not_compositor_thread(struct rdp_backend *b) { assert(b->compositor_tid != gettid()); } #endif /* ENABLE_RDP_THREAD_CHECK */ bool rdp_event_loop_add_fd(struct wl_event_loop *loop, int fd, uint32_t mask, wl_event_loop_fd_func_t func, void *data, struct wl_event_source **event_source) { *event_source = wl_event_loop_add_fd(loop, fd, 0, func, data); if (!*event_source) { weston_log("%s: wl_event_loop_add_fd failed.\n", __func__); return false; } wl_event_source_fd_update(*event_source, mask); return true; } void rdp_dispatch_task_to_display_loop(RdpPeerContext *peerCtx, rdp_loop_task_func_t func, struct rdp_loop_task *task) { /* this function is ONLY used to queue the task from FreeRDP thread, * and the task to be processed at wayland display loop thread. */ ASSERT_NOT_COMPOSITOR_THREAD(peerCtx->rdpBackend); task->peerCtx = peerCtx; task->func = func; pthread_mutex_lock(&peerCtx->loop_task_list_mutex); /* this inserts at head */ wl_list_insert(&peerCtx->loop_task_list, &task->link); pthread_mutex_unlock(&peerCtx->loop_task_list_mutex); eventfd_write(peerCtx->loop_task_event_source_fd, 1); } static int rdp_dispatch_task(int fd, uint32_t mask, void *arg) { RdpPeerContext *peerCtx = (RdpPeerContext *)arg; struct rdp_loop_task *task, *tmp; eventfd_t dummy; /* this must be called back at wayland display loop thread */ ASSERT_COMPOSITOR_THREAD(peerCtx->rdpBackend); eventfd_read(peerCtx->loop_task_event_source_fd, &dummy); pthread_mutex_lock(&peerCtx->loop_task_list_mutex); /* dequeue the first task which is at last, so use reverse. */ assert(!wl_list_empty(&peerCtx->loop_task_list)); wl_list_for_each_reverse_safe(task, tmp, &peerCtx->loop_task_list, link) { wl_list_remove(&task->link); break; } pthread_mutex_unlock(&peerCtx->loop_task_list_mutex); /* Dispatch and task will be freed by caller. */ task->func(false, task); return 0; } bool rdp_initialize_dispatch_task_event_source(RdpPeerContext *peerCtx) { struct rdp_backend *b = peerCtx->rdpBackend; struct wl_event_loop *loop; bool ret; if (pthread_mutex_init(&peerCtx->loop_task_list_mutex, NULL) == -1) { weston_log("%s: pthread_mutex_init failed. %s\n", __func__, strerror(errno)); goto error_mutex; } assert(peerCtx->loop_task_event_source_fd == -1); peerCtx->loop_task_event_source_fd = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC); if (peerCtx->loop_task_event_source_fd == -1) { weston_log("%s: eventfd(EFD_SEMAPHORE) failed. %s\n", __func__, strerror(errno)); goto error_event_source_fd; } assert(wl_list_empty(&peerCtx->loop_task_list)); loop = wl_display_get_event_loop(b->compositor->wl_display); assert(peerCtx->loop_task_event_source == NULL); ret = rdp_event_loop_add_fd(loop, peerCtx->loop_task_event_source_fd, WL_EVENT_READABLE, rdp_dispatch_task, peerCtx, &peerCtx->loop_task_event_source); if (!ret) goto error_event_loop_add_fd; return true; error_event_loop_add_fd: close(peerCtx->loop_task_event_source_fd); peerCtx->loop_task_event_source_fd = -1; error_event_source_fd: pthread_mutex_destroy(&peerCtx->loop_task_list_mutex); error_mutex: return false; } void rdp_destroy_dispatch_task_event_source(RdpPeerContext *peerCtx) { struct rdp_loop_task *task, *tmp; /* This function must be called all virtual channel thread at FreeRDP is terminated, * that ensures no more incoming tasks. */ if (peerCtx->loop_task_event_source) { wl_event_source_remove(peerCtx->loop_task_event_source); peerCtx->loop_task_event_source = NULL; } wl_list_for_each_reverse_safe(task, tmp, &peerCtx->loop_task_list, link) { wl_list_remove(&task->link); /* inform caller task is not really scheduled prior to context destruction, * inform them to clean them up. */ task->func(true /* freeOnly */, task); } assert(wl_list_empty(&peerCtx->loop_task_list)); if (peerCtx->loop_task_event_source_fd != -1) { close(peerCtx->loop_task_event_source_fd); peerCtx->loop_task_event_source_fd = -1; } pthread_mutex_destroy(&peerCtx->loop_task_list_mutex); } /* This is a little tricky - it makes sure there's always at least * one spare byte in the array in case the caller needs to add a * null terminator to it. We can't just null terminate the array * here, because some callers won't want that - and some won't * like having an odd number of bytes. */ int rdp_wl_array_read_fd(struct wl_array *array, int fd) { int len, size; char *data; /* Make sure we have at least 1024 bytes of space left */ if (array->alloc - array->size < 1024) { if (!wl_array_add(array, 1024)) { errno = ENOMEM; return -1; } array->size -= 1024; } data = (char *)array->data + array->size; /* Leave one char in case the caller needs space for a * null terminator */ size = array->alloc - array->size - 1; do { len = read(fd, data, size); } while (len == -1 && errno == EINTR); if (len == -1) return -1; array->size += len; return len; }