1 // Copyright 2005 Ben Hutchings <ben@decadentplace.org.uk>.
2 // See the file "COPYING" for licence details.
4 #include "framebuffer.hpp"
11 #include <sys/types.h>
14 #include <netinet/in.h>
16 #include <sys/utsname.h>
20 #include "auto_fd.hpp"
21 #include "temp_file.hpp"
25 int select_display_num()
27 // Minimum and maximum display numbers to use. Xvnc and ssh's
28 // proxies start at 10, so we'll follow that convention. We
29 // have to put a limit on iteration somewhere, and 100
30 // displays seems rather excessive so we'll stop there.
31 const int min_display_num = 10;
32 const int max_display_num = 99;
34 // Note that we have to leave it to the X server to create the
35 // lock file for the selected display number, which leaves a
36 // race condition. We could perhaps read its error stream to
37 // detect the case where another server grabs the display
39 char lock_file_name[20];
40 for (int display_num = min_display_num;
41 display_num <= max_display_num;
44 // Check whether a lock file exists for this display. Really we
45 // should also check for stale locks, but this will probably do.
46 std::sprintf(lock_file_name, "/tmp/.X%d-lock", display_num);
47 if (access(lock_file_name, 0) == -1 && errno == ENOENT)
51 throw std::runtime_error("did not find a free X display");
54 void get_random_bytes(unsigned char * buf, std::size_t len)
56 auto_fd random_fd(open("/dev/urandom", O_RDONLY));
57 if (random_fd.get() == -1 || read(random_fd.get(), buf, len) != len)
58 throw std::runtime_error(std::strerror(errno));
61 std::auto_ptr<temp_file> create_temp_auth_file(int display_num)
63 std::auto_ptr<temp_file> auth_file(new temp_file("Xvfb-auth-"));
65 // An xauth entry consists of the following fields. All u16 fields
66 // are big-endian and unaligned. Character arrays are not null-
68 // u16 address family (= 256 for local socket)
69 // u16 length of address
70 // char[] address (= hostname)
71 // u16 length of display number
72 // char[] display number
73 // u16 auth type name length
74 // char[] auth type name (= "MIT-MAGIC-COOKIE-1")
75 // u16 length of auth data (= 16)
76 // char[] auth data (= random bytes)
77 uint16_t family = htons(0x100);
78 write(auth_file->get_fd(), &family, sizeof(family));
81 uint16_t len = htons(strlen(my_uname.nodename));
82 write(auth_file->get_fd(), &len, sizeof(len));
83 write(auth_file->get_fd(),
84 my_uname.nodename, strlen(my_uname.nodename));
86 std::sprintf(display, "%d", display_num);
87 len = htons(strlen(display));
88 write(auth_file->get_fd(), &len, sizeof(len));
89 write(auth_file->get_fd(), display, strlen(display));
90 static const char auth_type[] = "MIT-MAGIC-COOKIE-1";
91 len = htons(sizeof(auth_type) - 1);
92 write(auth_file->get_fd(), &len, sizeof(len));
93 write(auth_file->get_fd(), auth_type, sizeof(auth_type) - 1);
94 unsigned char auth_key[16];
95 get_random_bytes(auth_key, sizeof(auth_key));
96 len = htons(sizeof(auth_key));
97 write(auth_file->get_fd(), &len, sizeof(len));
98 write(auth_file->get_fd(), auth_key, sizeof(auth_key));
103 // Run the X server with the specified auth file, dimensions and
104 // assigned display number.
105 auto_kill_proc spawn_x_server(int display_num,
106 const std::string & auth_file_name,
107 int width, int height, int depth)
110 std::sprintf(display, ":%d", display_num);
111 const char * auth_file_c_str = auth_file_name.c_str();
113 auto_kill_proc server_proc(fork());
114 if (server_proc.get() == -1)
115 throw std::runtime_error(std::strerror(errno));
117 if (server_proc.get() == 0)
120 std::sprintf(dimensions, "%dx%dx%d", width, height, depth);
123 "-auth", auth_file_c_str,
124 "-screen", "0", dimensions,
130 // Wait for the lock file to appear or the server to exit. We can't
131 // really wait on both of these, so poll at 1-second intervals.
132 char lock_file_name[20];
133 std::sprintf(lock_file_name, "/tmp/.X%d-lock", display_num);
136 if (access(lock_file_name, 0) == 0)
138 if (errno != ENOENT) // huh?
139 throw std::runtime_error(std::strerror(errno));
140 if (waitpid(server_proc.get(), NULL, WNOHANG) == server_proc.get())
142 server_proc.release(); // pid is now invalid
143 // TODO: Get the exit status and decode it properly.
144 throw std::runtime_error("X server failed to create display");
153 FrameBuffer::FrameBuffer(int width, int height, int depth)
154 : display_num_(select_display_num()),
155 auth_file_(create_temp_auth_file(display_num_)),
156 server_proc_(spawn_x_server(display_num_,
158 width, height, depth))
161 std::string FrameBuffer::get_x_authority() const
163 return auth_file_->get_name();
166 std::string FrameBuffer::get_x_display() const
169 std::sprintf(display, ":%d", display_num_);