WLC_API bool wlc_init(const struct wlc_interface *interface, int argc, char *argv[]) { assert(interface); if (!interface) die("no wlc_interface was given"); if (wlc.display) return true; memset(&wlc, 0, sizeof(wlc)); wl_log_set_handler_server(wl_cb_log); for (int i = 1; i < argc; ++i) { if (chck_cstreq(argv[i], "--log")) { if (i + 1 >= argc) die("--log takes an argument (filename)"); wlc_set_log_file(fopen(argv[++i], "a")); } } unsetenv("TERM"); const char *x11display = getenv("DISPLAY"); bool privilidged = false; const bool has_logind = wlc_logind_available(); if (getuid() != geteuid() || getgid() != getegid()) { wlc_log(WLC_LOG_INFO, "Doing work on SUID/SGID side and dropping permissions"); privilidged = true; } else if (getuid() == 0) { die("Do not run wlc compositor as root"); } else if (!x11display && !has_logind && access("/dev/input/event0", R_OK | W_OK) != 0) { die("Not running from X11 and no access to /dev/input/event0 or logind available"); } #ifndef NDEBUG { struct sigaction action = { .sa_handler = backtrace }; sigaction(SIGABRT, &action, NULL); sigaction(SIGSEGV, &action, NULL); // XXX: Some weird sigfpes seems to come when running // wlc compositor inside wlc compositor (X11 backend). // Seems to be caused by resolution changes and mouse clicks. // Gather more information about this later and see what's going on. if (!getenv("WAYLAND_DISPLAY")) fpesetup(&action); } #endif int vt = 0; #ifdef HAS_LOGIND // Init logind if we are not running as SUID. // We need event loop for logind to work, and thus we won't allow it on SUID process. if (!privilidged && !x11display && has_logind) { if (!(wlc.display = wl_display_create())) die("Failed to create wayland display"); if (!(vt = wlc_logind_init("seat0"))) die("Failed to init logind"); } #else (void)privilidged; #endif if (!x11display) wlc_tty_init(vt); // -- we open tty before dropping permissions // so the fd process can also handle cleanup in case of crash // if logind initialized correctly, fd process does nothing but handle crash. { struct wl_display *display = wlc.display; wlc.display = NULL; wlc_fd_init(argc, argv, (vt != 0)); wlc.display = display; } // -- permissions are now dropped wl_signal_init(&wlc.signals.terminate); wl_signal_init(&wlc.signals.activate); wl_signal_init(&wlc.signals.compositor); wl_signal_init(&wlc.signals.focus); wl_signal_init(&wlc.signals.surface); wl_signal_init(&wlc.signals.input); wl_signal_init(&wlc.signals.output); wl_signal_init(&wlc.signals.render); wl_signal_init(&wlc.signals.xwayland); wl_signal_add(&wlc.signals.compositor, &compositor_listener); if (!wlc_resources_init()) die("Failed to init resource manager"); if (!wlc.display && !(wlc.display = wl_display_create())) die("Failed to create wayland display"); const char *socket_name; if (!(socket_name = wl_display_add_socket_auto(wlc.display))) die("Failed to add socket to wayland display"); if (socket_name) // shut up static analyze setenv("WAYLAND_DISPLAY", socket_name, true); if (wl_display_init_shm(wlc.display) != 0) die("Failed to init shm"); if (!wlc_udev_init()) die("Failed to init udev"); const char *libinput = getenv("WLC_LIBINPUT"); if (!x11display || (libinput && !chck_cstreq(libinput, "0"))) { if (!wlc_input_init()) die("Failed to init input"); } memcpy(&wlc.interface, interface, sizeof(wlc.interface)); if (!wlc_compositor(&wlc.compositor)) die("Failed to init compositor"); const char *xwayland = getenv("WLC_XWAYLAND"); if (!xwayland || !chck_cstreq(xwayland, "0")) { if (!(wlc_xwayland_init())) die("Failed to init xwayland"); } else { wlc.set_ready_on_run = true; } wlc_set_active(true); return wlc_compositor_is_good(&wlc.compositor); }
int main(void) { // TEST: Basic source and handle creation { assert(wlc_resources_init()); struct wlc_source source; assert(wlc_source(&source, "test", constructor, destructor, 1, sizeof(struct wlc_resource))); struct wlc_resource *ptr; assert(!constructor_called); assert((ptr = wlc_handle_create(&source))); assert(constructor_called); assert(source.pool.items.count == 1); wlc_handle handle; #pragma GCC diagnostic ignored "-Wpointer-arith" assert(!convert_to_wlc_handle(NULL)); #pragma GCC diagnostic warning "-Wpointer-arith" assert((handle = convert_to_wlc_handle(ptr))); assert(!convert_from_wlc_handle(handle, "invalid")); assert(convert_from_wlc_handle(handle, "test") == ptr); const char *test = "foobar"; wlc_handle_set_user_data(handle, test); assert(wlc_handle_get_user_data(handle) == test); assert(!destructor_called); wlc_handle_release(handle); assert(destructor_called); assert(!convert_from_wlc_handle(handle, "invalid")); assert(!convert_from_wlc_handle(handle, "test")); assert(!wlc_handle_get_user_data(handle)); assert(source.pool.items.count == 0); wlc_source_release(&source); wlc_resources_terminate(); } // TEST: Handle invalidation on source release { assert(wlc_resources_init()); struct wlc_source source; assert(wlc_source(&source, "test", constructor, destructor, 1, sizeof(struct wlc_resource))); struct wlc_resource *ptr; assert((ptr = wlc_handle_create(&source))); assert(source.pool.items.count == 1); wlc_handle handle; assert((handle = convert_to_wlc_handle(ptr))); assert(!(destructor_called = false)); wlc_source_release(&source); assert(destructor_called); assert(source.pool.items.count == 0); assert(!convert_from_wlc_handle(handle, "test")); wlc_resources_terminate(); } // TEST: Handle invalidation on resources termination { assert(wlc_resources_init()); struct wlc_source source; assert(wlc_source(&source, "test", constructor, destructor, 1, sizeof(struct wlc_resource))); struct wlc_resource *ptr; assert((ptr = wlc_handle_create(&source))); assert(source.pool.items.count == 1); wlc_handle handle; assert((handle = convert_to_wlc_handle(ptr))); assert(!(destructor_called = false)); wlc_resources_terminate(); assert(destructor_called); assert(!convert_from_wlc_handle(handle, "test")); wlc_source_release(&source); } // TEST: Relocation of source inside container of handle, when the handle's source changes location { assert(wlc_resources_init()); struct wlc_source source; assert(wlc_source(&source, "test", constructor2, destructor2, 1, sizeof(struct contains_source))); struct contains_source *ptr; assert((ptr = wlc_handle_create(&source))); assert(source.pool.items.count == 1); void *original_source = &ptr->source; wlc_handle handle; assert((handle = convert_to_wlc_handle(ptr))); struct wlc_resource *ptr2; assert((ptr2 = wlc_handle_create(&ptr->source))); wlc_handle handle2; assert((handle2 = convert_to_wlc_handle(ptr2))); assert(convert_from_wlc_handle(handle2, "test2") == ptr2); // Play with heap until realloc does not return linear memory anymore { void *original = source.pool.items.buffer; do { void *garbage; assert((garbage = malloc(1024))); assert(wlc_handle_create(&source)); free(garbage); } while (original == source.pool.items.buffer); } // So this should be true assert(ptr = convert_from_wlc_handle(handle, "test")); assert(original_source != &ptr->source); wlc_resources_terminate(); assert(!convert_from_wlc_handle(handle2, "test2")); wlc_source_release(&source); } // TEST: Benchmark (many insertions, and removal expanding from center) { assert(wlc_resources_init()); struct container { wlc_handle self; }; struct wlc_source source; assert(wlc_source(&source, "test", constructor, destructor, 1024, sizeof(struct container))); wlc_handle first = 0; const uint32_t iters = 0xFFFFF; for (uint32_t i = 0; i < iters; ++i) { struct container *ptr = wlc_handle_create(&source); ptr->self = convert_to_wlc_handle(ptr); if (!first) first = ptr->self; assert(convert_from_wlc_handle(first, "test")); } assert(source.pool.items.count == iters); for (uint32_t i = iters / 2, d = iters / 2; i < iters; ++i, --d) { assert(((struct container*)convert_from_wlc_handle(i + 1, "test"))->self == i + 1); assert(((struct container*)convert_from_wlc_handle(d + 1, "test"))->self == d + 1); wlc_handle_release(i + 1); wlc_handle_release(d + 1); } assert(source.pool.items.count == 0); assert(!convert_from_wlc_handle(first, "test")); wlc_source_release(&source); wlc_resources_terminate(); } // TODO: Needs test for wlc_resource. // For this we need to start compositor and some clients, or dummy the wl_resource struct. // (Latter probably better) return EXIT_SUCCESS; }
WLC_API bool wlc_init(const struct wlc_interface *interface, int argc, char *argv[]) { assert(interface); if (!interface) die("no wlc_interface was given"); if (wlc.display) return true; // reset wlc state, but keep log function void *log_fun = wlc.log_fun; memset(&wlc, 0, sizeof(wlc)); wlc.log_fun = log_fun; wl_log_set_handler_server(wl_cb_log); unsetenv("TERM"); const char *x11display = getenv("DISPLAY"); bool privileged = false; const bool has_logind = wlc_logind_available(); if (getuid() != geteuid() || getgid() != getegid()) { wlc_log(WLC_LOG_INFO, "Doing work on SUID/SGID side and dropping permissions"); privileged = true; } else if (getuid() == 0) { die("Do not run wlc compositor as root"); } else if (!x11display && !has_logind && access("/dev/input/event0", R_OK | W_OK) != 0) { die("Not running from X11 and no access to /dev/input/event0 or logind available"); } int vt = 0; #ifdef HAS_LOGIND // Init logind if we are not running as SUID. // We need event loop for logind to work, and thus we won't allow it on SUID process. if (!privileged && !x11display && has_logind) { if (!(wlc.display = wl_display_create())) die("Failed to create wayland display"); if (!(vt = wlc_logind_init("seat0"))) die("Failed to init logind"); } #else (void)privileged; #endif if (!x11display) wlc_tty_init(vt); // -- we open tty before dropping permissions // so the fd process can also handle cleanup in case of crash // if logind initialized correctly, fd process does nothing but handle crash. { struct wl_display *display = wlc.display; wlc.display = NULL; wlc_fd_init(argc, argv, (vt != 0)); wlc.display = display; } // -- permissions are now dropped wl_signal_init(&wlc.signals.terminate); wl_signal_init(&wlc.signals.activate); wl_signal_init(&wlc.signals.compositor); wl_signal_init(&wlc.signals.focus); wl_signal_init(&wlc.signals.surface); wl_signal_init(&wlc.signals.input); wl_signal_init(&wlc.signals.output); wl_signal_init(&wlc.signals.render); wl_signal_init(&wlc.signals.xwayland); wl_signal_add(&wlc.signals.compositor, &compositor_listener); if (!wlc_resources_init()) die("Failed to init resource manager"); if (!wlc.display && !(wlc.display = wl_display_create())) die("Failed to create wayland display"); const char *socket_name; if (!(socket_name = wl_display_add_socket_auto(wlc.display))) die("Failed to add socket to wayland display"); if (socket_name) // shut up static analyze setenv("WAYLAND_DISPLAY", socket_name, true); if (wl_display_init_shm(wlc.display) != 0) die("Failed to init shm"); if (!wlc_udev_init()) die("Failed to init udev"); const char *libinput = getenv("WLC_LIBINPUT"); if (!x11display || (libinput && !chck_cstreq(libinput, "0"))) { if (!wlc_input_init()) die("Failed to init input"); } if (!wlc_compositor(&wlc.compositor)) die("Failed to init compositor"); memcpy(&wlc.interface, interface, sizeof(wlc.interface)); return true; }