X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=framebuffer.cpp;fp=framebuffer.cpp;h=0000000000000000000000000000000000000000;hb=0acb5f1329d294faf42e247f8c2daf68d82150f6;hp=2ae42cde48addaf3f758e77af7d7d652b2c883cd;hpb=1b6026c7baa122b99011f760857b80b7f253dfbb;p=videolink.git diff --git a/framebuffer.cpp b/framebuffer.cpp deleted file mode 100644 index 2ae42cd..0000000 --- a/framebuffer.cpp +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright 2005 Ben Hutchings . -// See the file "COPYING" for licence details. - -#include "framebuffer.hpp" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "auto_fd.hpp" -#include "auto_handle.hpp" -#include "temp_file.hpp" - -namespace -{ - struct addrinfo_factory - { - addrinfo * operator()() const { return NULL; } - }; - struct addrinfo_closer - { - void operator()(addrinfo * addr_list) const - { - if (addr_list) - freeaddrinfo(addr_list); - } - }; - typedef auto_handle - auto_addrinfo; - - int select_display_num(auto_fd & tcp4_socket, auto_fd & tcp6_socket) - { - // Minimum and maximum display numbers to use. Xvnc and ssh's - // proxies start at 10, so we'll follow that convention. We - // have to put a limit on iteration somewhere, and 100 - // displays seems rather excessive so we'll stop there. - const int min_display_num = 10; - const int max_display_num = 99; - - for (int display_num = min_display_num; - display_num <= max_display_num; - ++display_num) - { - // Check that there's no lock file for the local socket - // for this display. We could also check for stale locks, - // but this will probably do. - char lock_file_name[20]; - std::sprintf(lock_file_name, "/tmp/.X%d-lock", display_num); - if (!(access(lock_file_name, 0) == -1 && errno == ENOENT)) - continue; - - // Attempt to create TCP socket(s) and bind them to the - // appropriate port number. We won't set the X server to - // listen on a TCP socket but this does ensure that ssh - // isn't using and won't use this display number. This is - // roughly based on the x11_create_display_inet function - // in OpenSSH. - - auto_addrinfo addr_list; - - { - addrinfo hints = {}; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - char port_str[5 + 1]; - std::sprintf(port_str, "%d", 6000 + display_num); - addrinfo * addr_list_temp; - int error = getaddrinfo(NULL, port_str, &hints, - &addr_list_temp); - if (error != 0) - throw std::runtime_error( - std::string("getaddrinfo: ") - .append(gai_strerror(error))); - addr_list.reset(addr_list_temp); - } - - const addrinfo * addr; - - for (addr = addr_list.get(); addr != NULL; addr = addr->ai_next) - { - // We're only interested in TCPv4 and TCPv6. - if (addr->ai_family != AF_INET && addr->ai_family != AF_INET6) - continue; - auto_fd & tcp_socket = - (addr->ai_family == AF_INET) ? tcp4_socket : tcp6_socket; - - tcp_socket.reset(socket(addr->ai_family, - SOCK_STREAM, - addr->ai_protocol)); - if (tcp_socket.get() < 0) - { - // If the family is unsupported, no-one can bind - // to this address, so this is not a problem. - if (errno == EAFNOSUPPORT -# ifdef EPFNOSUPPORT - || errno == EPFNOSUPPORT -# endif - ) - continue; - throw std::runtime_error( - std::string("socket: ").append(strerror(errno))); - } - - // Don't let TCPv6 sockets interfere with TCPv4 sockets. -# ifdef IPV6_V6ONLY - if (addr->ai_family == AF_INET6) - { - int on = 1; - if (setsockopt(tcp_socket.get(), IPPROTO_IPV6, IPV6_V6ONLY, - &on, sizeof(on)) != 0) - { - throw std::runtime_error( - std::string("setsockopt IPV6_V6ONLY: ") - .append(strerror(errno))); - } - } -# endif - - if (bind(tcp_socket.get(), addr->ai_addr, addr->ai_addrlen) - != 0) - break; - } - - // If we reached the end of the address list, we've - // successfully bound to all appropriate addresses for - // this display number, so we can use it. - if (addr == NULL) - return display_num; - } - - throw std::runtime_error("did not find a free X display"); - } - - void get_random_bytes(unsigned char * buf, int len) - { - assert(len > 0); - auto_fd random_fd(open("/dev/urandom", O_RDONLY)); - if (random_fd.get() == -1 || read(random_fd.get(), buf, len) != len) - throw std::runtime_error(std::strerror(errno)); - } - - std::auto_ptr create_temp_auth_file(int display_num) - { - std::auto_ptr auth_file(new temp_file("Xvfb-auth-")); - - // An xauth entry consists of the following fields. All u16 fields - // are big-endian and unaligned. Character arrays are not null- - // terminated. - // u16 address family (= 256 for local socket) - // u16 length of address - // char[] address (= hostname) - // u16 length of display number - // char[] display number - // u16 auth type name length - // char[] auth type name (= "MIT-MAGIC-COOKIE-1") - // u16 length of auth data (= 16) - // char[] auth data (= random bytes) - uint16_t family = htons(0x100); - write(auth_file->get_fd(), &family, sizeof(family)); - utsname my_uname; - uname(&my_uname); - uint16_t len = htons(strlen(my_uname.nodename)); - write(auth_file->get_fd(), &len, sizeof(len)); - write(auth_file->get_fd(), - my_uname.nodename, strlen(my_uname.nodename)); - char display[15]; - std::sprintf(display, "%d", display_num); - len = htons(strlen(display)); - write(auth_file->get_fd(), &len, sizeof(len)); - write(auth_file->get_fd(), display, strlen(display)); - static const char auth_type[] = "MIT-MAGIC-COOKIE-1"; - len = htons(sizeof(auth_type) - 1); - write(auth_file->get_fd(), &len, sizeof(len)); - write(auth_file->get_fd(), auth_type, sizeof(auth_type) - 1); - unsigned char auth_key[16]; - get_random_bytes(auth_key, sizeof(auth_key)); - len = htons(sizeof(auth_key)); - write(auth_file->get_fd(), &len, sizeof(len)); - write(auth_file->get_fd(), auth_key, sizeof(auth_key)); - - return auth_file; - } - - // Run the X server with the specified auth file, dimensions and - // assigned display number. - auto_kill_proc spawn_x_server(int display_num, - const std::string & auth_file_name, - int width, int height, int depth) - { - char display[15]; - std::sprintf(display, ":%d", display_num); - const char * auth_file_c_str = auth_file_name.c_str(); - std::fflush(NULL); - auto_kill_proc server_proc(fork()); - if (server_proc.get() == -1) - throw std::runtime_error(std::strerror(errno)); - - if (server_proc.get() == 0) - { - char dimensions[40]; - std::sprintf(dimensions, "%dx%dx%d", width, height, depth); - execlp("Xvfb", - "Xvfb", - "-auth", auth_file_c_str, - "-nolisten", "tcp", - "-screen", "0", dimensions, - "-terminate", - display, - NULL); - _exit(128 + errno); - } - - // Wait for the lock file to appear or the server to exit. We can't - // really wait on both of these, so poll at 1-second intervals. - char lock_file_name[20]; - std::sprintf(lock_file_name, "/tmp/.X%d-lock", display_num); - for (;;) - { - if (access(lock_file_name, 0) == 0) - break; - if (errno != ENOENT) // huh? - throw std::runtime_error(std::strerror(errno)); - if (waitpid(server_proc.get(), NULL, WNOHANG) == server_proc.get()) - { - server_proc.release(); // pid is now invalid - // TODO: Get the exit status and decode it properly. - throw std::runtime_error("X server failed to create display"); - } - sleep(1); - } - - return server_proc; - } -} - -FrameBuffer::FrameBuffer(int width, int height, int depth) - : display_num_(select_display_num(tcp4_socket_, tcp6_socket_)), - auth_file_(create_temp_auth_file(display_num_)), - server_proc_(spawn_x_server(display_num_, - get_x_authority(), - width, height, depth)) -{} - -std::string FrameBuffer::get_x_authority() const -{ - return auth_file_->get_name(); -} - -std::string FrameBuffer::get_x_display() const -{ - char display[15]; - std::sprintf(display, ":%d", display_num_); - return display; -}