Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ install-common:
install -d $(DESTDIR)/etc/qubes/post-install.d
install -m 0755 appvm-scripts/etc/qubes/post-install.d/20-qubes-guivm-gui-agent.sh \
$(DESTDIR)/etc/qubes/post-install.d/20-qubes-guivm-gui-agent.sh
install -m 0755 appvm-scripts/etc/qubes/post-install.d/20-qubes-gui-agent.sh \
$(DESTDIR)/etc/qubes/post-install.d/20-qubes-gui-agent.sh
install -D appvm-scripts/usrbin/qubes-session \
$(DESTDIR)/usr/bin/qubes-session
install -D appvm-scripts/usrbin/qubes-run-xorg \
Expand Down
4 changes: 4 additions & 0 deletions appvm-scripts/etc/qubes/post-install.d/20-qubes-gui-agent.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh

# announce support for late gui-daemon connection
qvm-features-request supported-feature.late-gui-daemon=1
9 changes: 8 additions & 1 deletion appvm-scripts/usrbin/qubes-run-xorg
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@

## This script is triggered by qubes-gui-agent systemd service.

#expects W, H, MEM and DEPTH env vars to be set by invoker
#expects W, H, MEM and DEPTH env vars to be set by invoker, but use default
# values if they aren't

[ -n "$W" ] || W=1920
[ -n "$H" ] || H=1080
[ -n "$MEM" ] || MEM=$(( W * H * 4 / 1024 ))
[ -n "$DEPTH" ] || DEPTH=24

DUMMY_MAX_CLOCK=300 #hardcoded in dummy_drv
PREFERRED_HSYNC=50
RES="$W"x"$H"
Expand Down
1 change: 1 addition & 0 deletions debian/qubes-gui-agent.install
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ etc/security/limits.d/90-qubes-gui.conf
etc/qubes-rpc/qubes.SetMonitorLayout
etc/qubes-rpc/qubes.GuiVMSession
etc/qubes/post-install.d/20-qubes-guivm-gui-agent.sh
etc/qubes/post-install.d/20-qubes-gui-agent.sh
etc/xdg/autostart/qubes-gtk4-workarounds.desktop
etc/xdg/autostart/qubes-icon-sender.desktop
etc/xdg/autostart/qubes-qrexec-fork-server.desktop
Expand Down
105 changes: 63 additions & 42 deletions gui-agent/vmside.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
}) + sizeof(x)/sizeof((x)[0]))
#define SOCKET_ADDRESS "/var/run/xf86-qubes-socket"

#define STATUS_FILE_PATH "/run/qubes/gui-agent.status"

/* Supported protocol version */

#define PROTOCOL_VERSION_MAJOR 1
Expand Down Expand Up @@ -2176,23 +2178,19 @@ static void handle_close(Ghandles * g, XID winid)

/* start X server, returns its PID
*/
static pid_t do_execute_xorg(
char *w_str, char *h_str, char *mem_str, char *depth_str,
char *gui_domid_str)
static pid_t do_execute_xorg(Ghandles *g)
{
pid_t pid;
int fd;
char gui_domid_str[12];

pid = fork();
switch (pid) {
case -1:
perror("fork");
return -1;
case 0:
setenv("W", w_str, 1);
setenv("H", h_str, 1);
setenv("MEM", mem_str, 1);
setenv("DEPTH", depth_str, 1);
snprintf(gui_domid_str, sizeof(gui_domid_str), "%u", g->domid);
setenv("GUI_DOMID", gui_domid_str, 1);
/* don't leak other FDs */
for (fd = 3; fd < 256; fd++)
Expand Down Expand Up @@ -2374,27 +2372,11 @@ static void handle_message(Ghandles * g)
}
}

static pid_t get_xconf_and_run_x(Ghandles *g)
{
struct msg_xconf xconf;
char w_str[12], h_str[12], mem_str[12], depth_str[12], gui_domid_str[12];
pid_t x_pid;
read_struct(g->vchan, xconf);
snprintf(w_str, sizeof(w_str), "%d", xconf.w);
snprintf(h_str, sizeof(h_str), "%d", xconf.h);
snprintf(mem_str, sizeof(mem_str), "%d", xconf.mem);
snprintf(depth_str, sizeof(depth_str), "%d", xconf.depth);
snprintf(gui_domid_str, sizeof(gui_domid_str), "%u", g->domid);
x_pid = do_execute_xorg(w_str, h_str, mem_str, depth_str, gui_domid_str);
if (x_pid == (pid_t)-1) {
errx(1, "X server startup failed");
}
return x_pid;
}

static void handshake(Ghandles *g)
{
uint32_t version = PROTOCOL_VERSION;
struct msg_xconf xconf;

write_struct(g->vchan, version);
version = 0;
read_struct(g->vchan, version);
Expand All @@ -2407,27 +2389,54 @@ static void handshake(Ghandles *g)
PROTOCOL_VERSION_MAJOR, PROTOCOL_VERSION_MINOR,
major_version, minor_version);
g->protocol_version = version;

/* discard */
read_struct(g->vchan, xconf);
}

static void write_status_file(const char *status)
{
int fd = open(STATUS_FILE_PATH, O_CREAT | O_WRONLY | O_NOFOLLOW | O_TRUNC, 0644);
if (fd < 0) {
perror("open status file");
return;
}
FILE *f = fdopen(fd, "w");
if (!f) {
perror("fdopen status file");
close(fd);
return;
}
if (fwrite(status, strlen(status), 1, f) != 1) {
perror("write error");
}
fclose(f);
close(fd);
}

static void cleanup_status_file(void)
{
unlink(STATUS_FILE_PATH);
}

static void handle_guid_disconnect(void)
{
Ghandles *g = ghandles_for_vchan_reinitialize;
struct msg_xconf xconf;

if (!ghandles_for_vchan_reinitialize) {
fprintf(stderr, "gui-daemon disconnected before fully initialized, "
"cannot reconnect, exiting!\n");
exit(1);
}
write_status_file("started\n");
libvchan_close(g->vchan);
g->vchan = libvchan_server_init(g->domid, 6000, 4096, 4096);
/* wait for gui daemon */
while (libvchan_is_open(g->vchan) == VCHAN_WAITING)
libvchan_wait(g->vchan);
handshake(g);
/* discard */
read_struct(g->vchan, xconf);
send_all_windows_info(g);
write_status_file("connected\n");
}

static _Noreturn void handle_sigterm(int UNUSED(sig),
Expand Down Expand Up @@ -2518,6 +2527,9 @@ int main(int argc, char **argv)
int xfd;
Ghandles g = { .x_pid = -1 };

write_status_file("starting\n");
atexit(cleanup_status_file);

g.created_input_device = access("/run/qubes-service/gui-agent-virtual-input-device", F_OK) == 0;

if(g.created_input_device) {
Expand Down Expand Up @@ -2571,7 +2583,6 @@ int main(int argc, char **argv)
g.last_known_modifier_states = 0;
}


parse_args(&g, argc, argv);

/* Clipboard wipe functionality is controlled by the
Expand All @@ -2580,18 +2591,8 @@ int main(int argc, char **argv)
g.clipboard_wipe =
access("/run/qubes-service/gui-agent-clipboard-wipe", F_OK) == 0;

g.vchan = libvchan_server_init(g.domid, 6000, 4096, 4096);
if (!g.vchan) {
fprintf(stderr, "vchan initialization failed\n");
exit(1);
}
/* wait for gui daemon */
while (libvchan_is_open(g.vchan) == VCHAN_WAITING)
libvchan_wait(g.vchan);
saved_argv = argv;
vchan_register_at_eof(handle_guid_disconnect);

ghandles_for_vchan_reinitialize = &g;

struct sigaction sigchld_handler = {
.sa_sigaction = handle_sigchld,
.sa_flags = SA_SIGINFO,
Expand All @@ -2607,8 +2608,10 @@ int main(int argc, char **argv)
if (sigaction(SIGTERM, &sigterm_handler, NULL))
err(1, "sigaction");

handshake(&g);
g.x_pid = get_xconf_and_run_x(&g);
g.x_pid = do_execute_xorg(&g);
if (g.x_pid == (pid_t)-1) {
errx(1, "X server startup failed");
}

mkghandles(&g);
/* Turn on Composite for all children of root window. This way X server
Expand Down Expand Up @@ -2677,6 +2680,24 @@ int main(int argc, char **argv)
fprintf(stderr,
"Acquired MANAGER selection for tray\n");
}

write_status_file("started\n");

g.vchan = libvchan_server_init(g.domid, 6000, 4096, 4096);
if (!g.vchan) {
fprintf(stderr, "vchan initialization failed\n");
exit(1);
}
/* wait for gui daemon */
while (libvchan_is_open(g.vchan) == VCHAN_WAITING)
libvchan_wait(g.vchan);
saved_argv = argv;
vchan_register_at_eof(handle_guid_disconnect);

handshake(&g);

write_status_file("connected\n");

xfd = ConnectionNumber(g.display);
struct pollfd fds[] = {
{ .fd = -1, .events = POLLIN | POLLHUP, .revents = 0 },
Expand Down
1 change: 1 addition & 0 deletions rpm_spec/gui-agent.spec.in
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ rm -f %{name}-%{version}
/etc/qubes-rpc/qubes.SetMonitorLayout
/etc/qubes-rpc/qubes.GuiVMSession
/etc/qubes/post-install.d/20-qubes-guivm-gui-agent.sh
/etc/qubes/post-install.d/20-qubes-gui-agent.sh
%if 0%{?is_opensuse}
%{_fillupdir}/desktop.qubes-gui-agent
%else
Expand Down
1 change: 1 addition & 0 deletions selinux/qubes-gui-agent.fc
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
/usr/bin/qubes-gui-runuser -- gen_context(system_u:object_r:xdm_exec_t,s0)
/usr/lib/qubes/qubes-xorg-wrapper -- gen_context(system_u:object_r:xserver_exec_t,s0)
/usr/bin/qubes-run-xephyr -- gen_context(system_u:object_r:xserver_exec_t,s0)
/run/qubes/gui-agent.status -- gen_context(system_u:object_r:qubes_gui_agent_state_file_t,s0)
8 changes: 8 additions & 0 deletions selinux/qubes-gui-agent.te
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ require {
class tcp_socket name_connect;
}

type qubes_gui_agent_state_file_t;
files_type(qubes_gui_agent_state_file_t)

allow xdm_t etc_t:file { create write };
allow xdm_t self:passwd rootok;
allow domain local_login_t:unix_stream_socket { rw_inherited_sock_file_perms ioctl };
Expand All @@ -16,6 +19,11 @@ init_domtrans_script(xdm_t)
type_transition xdm_t qubes_var_run_t:sock_file qubes_qrexec_socket_t;
manage_sock_files_pattern(xdm_t, qubes_var_run_t, qubes_qrexec_socket_t)
write_sock_files_pattern(xdm_t, qubes_var_run_t, qubes_var_run_t)

allow xdm_t qubes_gui_agent_state_file_t:file { create_file_perms write_file_perms delete_file_perms };
allow xdm_t qubes_var_run_t:dir { add_entry_dir_perms del_entry_dir_perms };
filetrans_pattern(xdm_t, qubes_var_run_t, qubes_gui_agent_state_file_t, file, "gui-agent.status")

optional {
pulseaudio_domtrans(xdm_t)
dev_rw_xen(pulseaudio_t)
Expand Down