1 // Copyright 2005 Ben Hutchings <ben@decadentplace.org.uk>.
2 // See the file "COPYING" for licence details.
12 #include <netinet/in.h>
14 #include <sys/utsname.h>
18 #include "framebuffer.hpp"
19 #include "auto_fd.hpp"
23 int select_display_num()
25 // Minimum and maximum display numbers to use. Xvnc and ssh's
26 // proxies start at 10, so we'll follow that convention. We
27 // have to put a limit on iteration somewhere, and 100
28 // displays seems rather excessive so we'll stop there.
29 const int min_display_num = 10;
30 const int max_display_num = 99;
32 // Note that we have to leave it to the X server to create the
33 // lock file for the selected display number, which leaves a
34 // race condition. We could perhaps read its error stream to
35 // detect the case where another server grabs the display
37 char lock_file_name[20];
38 for (int display_num = min_display_num;
39 display_num <= max_display_num;
42 // Check whether a lock file exists for this display. Really we
43 // should also check for stale locks, but this will probably do.
44 std::sprintf(lock_file_name, "/tmp/.X%d-lock", display_num);
45 if (access(lock_file_name, 0) == -1 && errno == ENOENT)
49 throw std::runtime_error("did not find a free X display");
52 void get_random_bytes(unsigned char * buf, std::size_t len)
54 auto_fd random_fd(open("/dev/urandom", O_RDONLY));
55 if (random_fd.get() == -1 || read(random_fd.get(), buf, len) != len)
56 throw std::runtime_error(std::strerror(errno));
59 auto_temp_file create_temp_auth_file(int display_num)
61 char auth_file_name[] = "/tmp/Xvfb-auth-XXXXXX";
62 auto_fd auth_file_fd(mkstemp(auth_file_name));
63 if (auth_file_fd.get() == -1)
64 throw std::runtime_error(std::strerror(errno));
65 auto_temp_file auth_file(auth_file_name);
67 // mkstemp may use lax permissions, so fix that before writing
68 // the auth data to it.
69 fchmod(auth_file_fd.get(), S_IREAD|S_IWRITE);
70 ftruncate(auth_file_fd.get(), 0);
72 // An xauth entry consists of the following fields. All u16 fields
73 // are big-endian and unaligned. Character arrays are not null-
75 // u16 address family (= 256 for local socket)
76 // u16 length of address
77 // char[] address (= hostname)
78 // u16 length of display number
79 // char[] display number
80 // u16 auth type name length
81 // char[] auth type name (= "MIT-MAGIC-COOKIE-1")
82 // u16 length of auth data (= 16)
83 // char[] auth data (= random bytes)
84 uint16_t family = htons(0x100);
85 write(auth_file_fd.get(), &family, sizeof(family));
88 uint16_t len = htons(strlen(my_uname.nodename));
89 write(auth_file_fd.get(), &len, sizeof(len));
90 write(auth_file_fd.get(),
91 my_uname.nodename, strlen(my_uname.nodename));
93 std::sprintf(display, "%d", display_num);
94 len = htons(strlen(display));
95 write(auth_file_fd.get(), &len, sizeof(len));
96 write(auth_file_fd.get(), display, strlen(display));
97 static const char auth_type[] = "MIT-MAGIC-COOKIE-1";
98 len = htons(sizeof(auth_type) - 1);
99 write(auth_file_fd.get(), &len, sizeof(len));
100 write(auth_file_fd.get(), auth_type, sizeof(auth_type) - 1);
101 unsigned char auth_key[16];
102 get_random_bytes(auth_key, sizeof(auth_key));
103 len = htons(sizeof(auth_key));
104 write(auth_file_fd.get(), &len, sizeof(len));
105 write(auth_file_fd.get(), auth_key, sizeof(auth_key));
110 // Run the X server with the specified auth file, dimensions and
111 // assigned display number.
112 auto_kill_proc spawn_x_server(int display_num,
113 const std::string & auth_file_name,
114 int width, int height, int depth)
117 std::sprintf(display, ":%d", display_num);
118 const char * auth_file_c_str = auth_file_name.c_str();
120 auto_kill_proc server_proc(fork());
121 if (server_proc.get() == -1)
122 throw std::runtime_error(std::strerror(errno));
124 if (server_proc.get() == 0)
127 std::sprintf(dimensions, "%dx%dx%d", width, height, depth);
130 "-auth", auth_file_c_str,
131 "-screen", "0", dimensions,
137 // Wait for the lock file to appear or the server to exit. We can't
138 // really wait on both of these, so poll at 1-second intervals.
139 char lock_file_name[20];
140 std::sprintf(lock_file_name, "/tmp/.X%d-lock", display_num);
143 if (access(lock_file_name, 0) == 0)
145 if (errno != ENOENT) // huh?
146 throw std::runtime_error(std::strerror(errno));
147 if (waitpid(server_proc.get(), NULL, WNOHANG) == server_proc.get())
149 server_proc.release(); // pid is now invalid
150 // TODO: Get the exit status and decode it properly.
151 throw std::runtime_error("X server failed to create display");
160 FrameBuffer::FrameBuffer(int width, int height, int depth)
161 : display_num_(select_display_num()),
162 auth_file_(create_temp_auth_file(display_num_)),
163 server_proc_(spawn_x_server(display_num_,
165 width, height, depth))
168 std::string FrameBuffer::get_x_authority() const
170 return auth_file_.get();
173 std::string FrameBuffer::get_x_display() const
176 std::sprintf(display, ":%d", display_num_);