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