#include "protocol.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern char **environ; struct view { pid_t pid; int control; int frames; int shared_fd; struct owb_shared_frames *shared; size_t shared_size; }; static bool write_all(int fd, const void *data, size_t size) { const uint8_t *cursor = data; while (size) { ssize_t count = write(fd, cursor, size); if (count < 0 && errno == EINTR) continue; if (count <= 0) return false; cursor += (size_t)count; size -= (size_t)count; } return true; } static void serve_pages(int server) { static const char page[] = "" ""; char response[2048]; int length = snprintf(response, sizeof(response), "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nCache-Control: no-store\r\n" "Connection: close\r\nContent-Length: %zu\r\n\r\n%s", strlen(page), page); for (;;) { int client = accept(server, NULL, NULL); if (client < 0 && errno == EINTR) continue; if (client < 0) _exit(0); char request[1024]; ssize_t ignored = read(client, request, sizeof(request)); (void)ignored; write_all(client, response, (size_t)length); close(client); } } static bool start_view(struct view *view, const char *renderer, const char *url) { memset(view, 0, sizeof(*view)); view->pid = 0; view->control = view->frames = view->shared_fd = -1; int control[2] = {-1, -1}; int frames[2] = {-1, -1}; if (socketpair(AF_UNIX, SOCK_STREAM, 0, control) != 0 || pipe(frames) != 0) return false; const uint32_t width = 64, height = 64; view->shared_size = owb_shared_size(width, height); view->shared_fd = memfd_create("shared-profile-test", MFD_CLOEXEC); if (view->shared_fd < 0 || ftruncate(view->shared_fd, (off_t)view->shared_size) != 0) return false; view->shared = mmap(NULL, view->shared_size, PROT_READ | PROT_WRITE, MAP_SHARED, view->shared_fd, 0); if (view->shared == MAP_FAILED) return false; memset(view->shared, 0, sizeof(*view->shared)); view->shared->magic = OWB_SHARED_MAGIC; view->shared->version = OWB_PROTOCOL_VERSION; view->shared->width = width; view->shared->height = height; view->shared->stride = width * 4; view->shared->slot_count = OWB_SHARED_FRAME_SLOTS; posix_spawn_file_actions_t actions; posix_spawn_file_actions_init(&actions); posix_spawn_file_actions_adddup2(&actions, control[1], STDIN_FILENO); posix_spawn_file_actions_adddup2(&actions, frames[1], STDOUT_FILENO); posix_spawn_file_actions_adddup2(&actions, view->shared_fd, OWB_SHARED_FRAME_FD); char *arguments[] = {(char *)renderer, NULL}; int error = posix_spawn(&view->pid, renderer, &actions, NULL, arguments, environ); posix_spawn_file_actions_destroy(&actions); close(control[1]); close(frames[1]); if (error) return false; view->control = control[0]; view->frames = frames[0]; struct owb_config_header config = { .magic = OWB_CONFIG_MAGIC, .version = OWB_PROTOCOL_VERSION, .width = width, .height = height, .fps = 20, .transparent = 0, .zoom_percent = 100, .hardware_acceleration = 1, .url_length = (uint32_t)strlen(url), .css_length = 0, }; return write_all(view->control, &config, sizeof(config)) && write_all(view->control, url, strlen(url)); } static int stop_view(struct view *view) { int status = 0; if (view->control >= 0) { shutdown(view->control, SHUT_RDWR); close(view->control); } if (view->frames >= 0) close(view->frames); if (view->shared && view->shared != MAP_FAILED) munmap(view->shared, view->shared_size); if (view->shared_fd >= 0) close(view->shared_fd); if (view->pid > 0) { kill(view->pid, SIGTERM); while (waitpid(view->pid, &status, 0) < 0 && errno == EINTR) { } } return status; } int main(int argc, char **argv) { if (argc != 2) return 2; int server = socket(AF_INET, SOCK_STREAM, 0); int enabled = 1; setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &enabled, sizeof(enabled)); struct sockaddr_in address = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)}; if (server < 0 || bind(server, (struct sockaddr *)&address, sizeof(address)) != 0 || listen(server, 16) != 0) return 77; socklen_t address_size = sizeof(address); if (getsockname(server, (struct sockaddr *)&address, &address_size) != 0) return 2; pid_t http_server = fork(); if (http_server == 0) serve_pages(server); close(server); char writer_url[256], reader_url[256]; unsigned port = ntohs(address.sin_port); snprintf(writer_url, sizeof(writer_url), "http://127.0.0.1:%u/writer?token=%ld", port, (long)getpid()); snprintf(reader_url, sizeof(reader_url), "http://127.0.0.1:%u/reader?token=%ld", port, (long)getpid()); struct view writer, reader; bool started_writer = start_view(&writer, argv[1], writer_url); bool started_reader = started_writer && start_view(&reader, argv[1], reader_url); bool shared = false; for (unsigned attempt = 0; started_reader && attempt < 200; attempt++) { struct pollfd item = {.fd = reader.frames, .events = POLLIN}; if (poll(&item, 1, 100) <= 0) continue; uint8_t slot; if (read(reader.frames, &slot, sizeof(slot)) != 1 || slot >= OWB_SHARED_FRAME_SLOTS) continue; uint8_t *frame = owb_shared_slot(reader.shared, slot); size_t center = ((size_t)32 * 64 + 32) * 4; uint8_t *bgra = frame + center; if (bgra[0] < 55 && bgra[1] > 210 && bgra[2] < 35 && bgra[3] == 255) { shared = true; break; } } int reader_status = started_reader ? stop_view(&reader) : 0; int writer_status = started_writer ? stop_view(&writer) : 0; kill(http_server, SIGTERM); waitpid(http_server, NULL, 0); if (!started_writer || !started_reader) return 2; if ((WIFEXITED(reader_status) && WEXITSTATUS(reader_status) == 5) || (WIFEXITED(writer_status) && WEXITSTATUS(writer_status) == 5)) return 77; if (!shared) { fprintf(stderr, "two renderer views did not share localStorage and BroadcastChannel\n"); return 1; } return 0; }