1 #define _POSIX_C_SOURCE 200809L
2 #include <signal.h>
3 #include <stdbool.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <sys/socket.h>
7 #include <sys/types.h>
8 #include <sys/wait.h>
9 #include <unistd.h>
10 #include "log.h"
11 #include "sway/server.h"
12 #include "sway/swaynag.h"
13 #include "util.h"
14 
handle_swaynag_client_destroy(struct wl_listener * listener,void * data)15 static void handle_swaynag_client_destroy(struct wl_listener *listener,
16 		void *data) {
17 	struct swaynag_instance *swaynag =
18 		wl_container_of(listener, swaynag, client_destroy);
19 	wl_list_remove(&swaynag->client_destroy.link);
20 	wl_list_init(&swaynag->client_destroy.link);
21 	swaynag->client = NULL;
22 }
23 
swaynag_spawn(const char * swaynag_command,struct swaynag_instance * swaynag)24 bool swaynag_spawn(const char *swaynag_command,
25 		struct swaynag_instance *swaynag) {
26 	if (swaynag->client != NULL) {
27 		wl_client_destroy(swaynag->client);
28 	}
29 
30 	if (!swaynag_command) {
31 		return true;
32 	}
33 
34 	if (swaynag->detailed) {
35 		if (pipe(swaynag->fd) != 0) {
36 			sway_log(SWAY_ERROR, "Failed to create pipe for swaynag");
37 			return false;
38 		}
39 		if (!sway_set_cloexec(swaynag->fd[1], true)) {
40 			goto failed;
41 		}
42 	}
43 
44 	int sockets[2];
45 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) {
46 		sway_log_errno(SWAY_ERROR, "socketpair failed");
47 		goto failed;
48 	}
49 	if (!sway_set_cloexec(sockets[0], true) || !sway_set_cloexec(sockets[1], true)) {
50 		goto failed;
51 	}
52 
53 	swaynag->client = wl_client_create(server.wl_display, sockets[0]);
54 	if (swaynag->client == NULL) {
55 		sway_log_errno(SWAY_ERROR, "wl_client_create failed");
56 		goto failed;
57 	}
58 
59 	swaynag->client_destroy.notify = handle_swaynag_client_destroy;
60 	wl_client_add_destroy_listener(swaynag->client, &swaynag->client_destroy);
61 
62 	pid_t pid = fork();
63 	if (pid < 0) {
64 		sway_log(SWAY_ERROR, "Failed to create fork for swaynag");
65 		goto failed;
66 	} else if (pid == 0) {
67 		pid = fork();
68 		if (pid < 0) {
69 			sway_log_errno(SWAY_ERROR, "fork failed");
70 			_exit(EXIT_FAILURE);
71 		} else if (pid == 0) {
72 			if (!sway_set_cloexec(sockets[1], false)) {
73 				_exit(EXIT_FAILURE);
74 			}
75 
76 			if (swaynag->detailed) {
77 				close(swaynag->fd[1]);
78 				dup2(swaynag->fd[0], STDIN_FILENO);
79 				close(swaynag->fd[0]);
80 			}
81 
82 			char wayland_socket_str[16];
83 			snprintf(wayland_socket_str, sizeof(wayland_socket_str),
84 					"%d", sockets[1]);
85 			setenv("WAYLAND_SOCKET", wayland_socket_str, true);
86 
87 			size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2;
88 			char *cmd = malloc(length);
89 			snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args);
90 			execl("/bin/sh", "/bin/sh", "-c", cmd, NULL);
91 			sway_log_errno(SWAY_ERROR, "execl failed");
92 			_exit(EXIT_FAILURE);
93 		}
94 		_exit(EXIT_SUCCESS);
95 	}
96 
97 	if (swaynag->detailed) {
98 		if (close(swaynag->fd[0]) != 0) {
99 			sway_log_errno(SWAY_ERROR, "close failed");
100 			return false;
101 		}
102 	}
103 
104 	if (close(sockets[1]) != 0) {
105 		sway_log_errno(SWAY_ERROR, "close failed");
106 		return false;
107 	}
108 
109 	if (waitpid(pid, NULL, 0) < 0) {
110 		sway_log_errno(SWAY_ERROR, "waitpid failed");
111 		return false;
112 	}
113 
114 	return true;
115 
116 failed:
117 	if (swaynag->detailed) {
118 		if (close(swaynag->fd[0]) != 0) {
119 			sway_log_errno(SWAY_ERROR, "close failed");
120 			return false;
121 		}
122 		if (close(swaynag->fd[1]) != 0) {
123 			sway_log_errno(SWAY_ERROR, "close failed");
124 		}
125 	}
126 	return false;
127 }
128 
swaynag_log(const char * swaynag_command,struct swaynag_instance * swaynag,const char * fmt,...)129 void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag,
130 		const char *fmt, ...) {
131 	if (!swaynag_command) {
132 		return;
133 	}
134 
135 	if (!swaynag->detailed) {
136 		sway_log(SWAY_ERROR, "Attempting to write to non-detailed swaynag inst");
137 		return;
138 	}
139 
140 	if (swaynag->client == NULL && !swaynag_spawn(swaynag_command, swaynag)) {
141 		return;
142 	}
143 
144 	va_list args;
145 	va_start(args, fmt);
146 	size_t length = vsnprintf(NULL, 0, fmt, args) + 1;
147 	va_end(args);
148 
149 	char *temp = malloc(length + 1);
150 	if (!temp) {
151 		sway_log(SWAY_ERROR, "Failed to allocate buffer for swaynag log entry.");
152 		return;
153 	}
154 
155 	va_start(args, fmt);
156 	vsnprintf(temp, length, fmt, args);
157 	va_end(args);
158 
159 	write(swaynag->fd[1], temp, length);
160 
161 	free(temp);
162 }
163 
swaynag_show(struct swaynag_instance * swaynag)164 void swaynag_show(struct swaynag_instance *swaynag) {
165 	if (swaynag->detailed && swaynag->client != NULL) {
166 		close(swaynag->fd[1]);
167 	}
168 }
169 
170