aboutsummaryrefslogtreecommitdiffstats
path: root/sway/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/main.c')
-rw-r--r--sway/main.c254
1 files changed, 93 insertions, 161 deletions
diff --git a/sway/main.c b/sway/main.c
index cc9147b8..efb674b6 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -1,58 +1,46 @@
1#define _XOPEN_SOURCE 700 1#define _XOPEN_SOURCE 700
2#define _POSIX_C_SOURCE 200112L 2#define _POSIX_C_SOURCE 200112L
3#include <stdio.h> 3#include <getopt.h>
4#include <stdlib.h> 4#include <signal.h>
5#include <stdbool.h> 5#include <stdbool.h>
6#include <wlc/wlc.h> 6#include <stdlib.h>
7#include <sys/wait.h> 7#include <stdio.h>
8#include <sys/types.h> 8#include <string.h>
9#include <sys/stat.h> 9#include <sys/stat.h>
10#include <sys/types.h>
11#include <sys/wait.h>
10#include <sys/un.h> 12#include <sys/un.h>
11#include <signal.h>
12#include <unistd.h> 13#include <unistd.h>
13#include <getopt.h>
14#ifdef __linux__ 14#ifdef __linux__
15#include <sys/capability.h> 15#include <sys/capability.h>
16#include <sys/prctl.h> 16#include <sys/prctl.h>
17#endif 17#endif
18#include "sway/extensions.h" 18#include <wlr/util/log.h>
19#include "sway/layout.h"
20#include "sway/config.h" 19#include "sway/config.h"
21#include "sway/security.h" 20#include "sway/debug.h"
22#include "sway/handlers.h" 21#include "sway/server.h"
23#include "sway/input.h" 22#include "sway/tree/layout.h"
24#include "sway/ipc-server.h" 23#include "sway/ipc-server.h"
25#include "ipc-client.h" 24#include "ipc-client.h"
26#include "readline.h" 25#include "readline.h"
27#include "stringop.h" 26#include "stringop.h"
28#include "sway.h"
29#include "log.h"
30#include "util.h" 27#include "util.h"
31 28
32static bool terminate_request = false; 29static bool terminate_request = false;
33static int exit_value = 0; 30static int exit_value = 0;
31struct sway_server server;
34 32
35void sway_terminate(int exit_code) { 33void sway_terminate(int exit_code) {
36 terminate_request = true; 34 terminate_request = true;
37 exit_value = exit_code; 35 exit_value = exit_code;
38 wlc_terminate(); 36 wl_display_terminate(server.wl_display);
39} 37}
40 38
41void sig_handler(int signal) { 39void sig_handler(int signal) {
42 close_views(&root_container); 40 //close_views(&root_container);
43 sway_terminate(EXIT_SUCCESS); 41 sway_terminate(EXIT_SUCCESS);
44} 42}
45 43
46static void wlc_log_handler(enum wlc_log_type type, const char *str) {
47 if (type == WLC_LOG_ERROR) {
48 sway_log(L_ERROR, "[wlc] %s", str);
49 } else if (type == WLC_LOG_WARN) {
50 sway_log(L_INFO, "[wlc] %s", str);
51 } else {
52 sway_log(L_DEBUG, "[wlc] %s", str);
53 }
54}
55
56void detect_raspi() { 44void detect_raspi() {
57 bool raspi = false; 45 bool raspi = false;
58 FILE *f = fopen("/sys/firmware/devicetree/base/model", "r"); 46 FILE *f = fopen("/sys/firmware/devicetree/base/model", "r");
@@ -98,23 +86,16 @@ void detect_proprietary() {
98 if (!f) { 86 if (!f) {
99 return; 87 return;
100 } 88 }
101 bool nvidia = false, nvidia_modeset = false, nvidia_uvm = false, nvidia_drm = false;
102 while (!feof(f)) { 89 while (!feof(f)) {
103 char *line; 90 char *line;
104 if (!(line = read_line(f))) { 91 if (!(line = read_line(f))) {
105 break; 92 break;
106 } 93 }
107 if (strstr(line, "nvidia")) { 94 if (strstr(line, "nvidia")) {
108 nvidia = true; 95 fprintf(stderr, "\x1B[1;31mWarning: Proprietary Nvidia drivers are "
109 } 96 "NOT supported. Use Nouveau.\x1B[0m\n");
110 if (strstr(line, "nvidia_modeset")) { 97 free(line);
111 nvidia_modeset = true; 98 break;
112 }
113 if (strstr(line, "nvidia_uvm")) {
114 nvidia_uvm = true;
115 }
116 if (strstr(line, "nvidia_drm")) {
117 nvidia_drm = true;
118 } 99 }
119 if (strstr(line, "fglrx")) { 100 if (strstr(line, "fglrx")) {
120 fprintf(stderr, "\x1B[1;31mWarning: Proprietary AMD drivers do " 101 fprintf(stderr, "\x1B[1;31mWarning: Proprietary AMD drivers do "
@@ -125,52 +106,6 @@ void detect_proprietary() {
125 free(line); 106 free(line);
126 } 107 }
127 fclose(f); 108 fclose(f);
128 if (nvidia) {
129 fprintf(stderr, "\x1B[1;31mWarning: Proprietary nvidia driver support "
130 "is considered experimental. Nouveau is strongly recommended."
131 "\x1B[0m\n");
132 if (!nvidia_modeset || !nvidia_uvm || !nvidia_drm) {
133 fprintf(stderr, "\x1B[1;31mWarning: You do not have all of the "
134 "necessary kernel modules loaded for nvidia support. "
135 "You need nvidia, nvidia_modeset, nvidia_uvm, and nvidia_drm."
136 "\x1B[0m\n");
137 }
138#ifdef __linux__
139 f = fopen("/sys/module/nvidia_drm/parameters/modeset", "r");
140 if (f) {
141 char *line = read_line(f);
142 if (line && strstr(line, "Y")) {
143 // nvidia-drm.modeset is set to 0
144 fprintf(stderr, "\x1B[1;31mWarning: You must load "
145 "nvidia-drm with the modeset option on to use "
146 "the proprietary driver. Consider adding "
147 "nvidia-drm.modeset=1 to your kernel command line "
148 "parameters.\x1B[0m\n");
149 }
150 fclose(f);
151 free(line);
152 } else {
153 // nvidia-drm.modeset is not set
154 fprintf(stderr, "\x1B[1;31mWarning: You must load "
155 "nvidia-drm with the modeset option on to use "
156 "the proprietary driver. Consider adding "
157 "nvidia-drm.modeset=1 to your kernel command line "
158 "parameters.\x1B[0m\n");
159 }
160#else
161 f = fopen("/proc/cmdline", "r");
162 if (f) {
163 char *line = read_line(f);
164 if (line && !strstr(line, "nvidia-drm.modeset=1")) {
165 fprintf(stderr, "\x1B[1;31mWarning: You must add "
166 "nvidia-drm.modeset=1 to your kernel command line to use "
167 "the proprietary driver.\x1B[0m\n");
168 }
169 fclose(f);
170 free(line);
171 }
172#endif
173 }
174} 109}
175 110
176void run_as_ipc_client(char *command, char *socket_path) { 111void run_as_ipc_client(char *command, char *socket_path) {
@@ -184,27 +119,15 @@ void run_as_ipc_client(char *command, char *socket_path) {
184static void log_env() { 119static void log_env() {
185 const char *log_vars[] = { 120 const char *log_vars[] = {
186 "PATH", 121 "PATH",
187 "LD_LOAD_PATH", 122 "LD_LIBRARY_PATH",
188 "LD_PRELOAD_PATH", 123 "LD_PRELOAD_PATH",
189 "LD_LIBRARY_PATH", 124 "LD_LIBRARY_PATH",
190 "SWAY_CURSOR_THEME", 125 "SWAY_CURSOR_THEME",
191 "SWAY_CURSOR_SIZE", 126 "SWAY_CURSOR_SIZE",
192 "SWAYSOCK", 127 "SWAYSOCK"
193 "WLC_DRM_DEVICE",
194 "WLC_SHM",
195 "WLC_OUTPUTS",
196 "WLC_XWAYLAND",
197 "WLC_LIBINPUT",
198 "WLC_REPEAT_DELAY",
199 "WLC_REPEAT_RATE",
200 "XKB_DEFAULT_RULES",
201 "XKB_DEFAULT_MODEL",
202 "XKB_DEFAULT_LAYOUT",
203 "XKB_DEFAULT_VARIANT",
204 "XKB_DEFAULT_OPTIONS",
205 }; 128 };
206 for (size_t i = 0; i < sizeof(log_vars) / sizeof(char *); ++i) { 129 for (size_t i = 0; i < sizeof(log_vars) / sizeof(char *); ++i) {
207 sway_log(L_INFO, "%s=%s", log_vars[i], getenv(log_vars[i])); 130 wlr_log(L_INFO, "%s=%s", log_vars[i], getenv(log_vars[i]));
208 } 131 }
209} 132}
210 133
@@ -219,14 +142,14 @@ static void log_distro() {
219 for (size_t i = 0; i < sizeof(paths) / sizeof(char *); ++i) { 142 for (size_t i = 0; i < sizeof(paths) / sizeof(char *); ++i) {
220 FILE *f = fopen(paths[i], "r"); 143 FILE *f = fopen(paths[i], "r");
221 if (f) { 144 if (f) {
222 sway_log(L_INFO, "Contents of %s:", paths[i]); 145 wlr_log(L_INFO, "Contents of %s:", paths[i]);
223 while (!feof(f)) { 146 while (!feof(f)) {
224 char *line; 147 char *line;
225 if (!(line = read_line(f))) { 148 if (!(line = read_line(f))) {
226 break; 149 break;
227 } 150 }
228 if (*line) { 151 if (*line) {
229 sway_log(L_INFO, "%s", line); 152 wlr_log(L_INFO, "%s", line);
230 } 153 }
231 free(line); 154 free(line);
232 } 155 }
@@ -236,9 +159,10 @@ static void log_distro() {
236} 159}
237 160
238static void log_kernel() { 161static void log_kernel() {
162 return;
239 FILE *f = popen("uname -a", "r"); 163 FILE *f = popen("uname -a", "r");
240 if (!f) { 164 if (!f) {
241 sway_log(L_INFO, "Unable to determine kernel version"); 165 wlr_log(L_INFO, "Unable to determine kernel version");
242 return; 166 return;
243 } 167 }
244 while (!feof(f)) { 168 while (!feof(f)) {
@@ -247,7 +171,7 @@ static void log_kernel() {
247 break; 171 break;
248 } 172 }
249 if (*line) { 173 if (*line) {
250 sway_log(L_INFO, "%s", line); 174 wlr_log(L_INFO, "%s", line);
251 } 175 }
252 free(line); 176 free(line);
253 } 177 }
@@ -258,14 +182,14 @@ static void security_sanity_check() {
258 // TODO: Notify users visually if this has issues 182 // TODO: Notify users visually if this has issues
259 struct stat s; 183 struct stat s;
260 if (stat("/proc", &s)) { 184 if (stat("/proc", &s)) {
261 sway_log(L_ERROR, 185 wlr_log(L_ERROR,
262 "!! DANGER !! /proc is not available - sway CANNOT enforce security rules!"); 186 "!! DANGER !! /proc is not available - sway CANNOT enforce security rules!");
263 } 187 }
264#ifdef __linux__ 188#ifdef __linux__
265 cap_flag_value_t v; 189 cap_flag_value_t v;
266 cap_t cap = cap_get_proc(); 190 cap_t cap = cap_get_proc();
267 if (!cap || cap_get_flag(cap, CAP_SYS_PTRACE, CAP_PERMITTED, &v) != 0 || v != CAP_SET) { 191 if (!cap || cap_get_flag(cap, CAP_SYS_PTRACE, CAP_PERMITTED, &v) != 0 || v != CAP_SET) {
268 sway_log(L_ERROR, 192 wlr_log(L_ERROR,
269 "!! DANGER !! Sway does not have CAP_SYS_PTRACE and cannot enforce security rules for processes running as other users."); 193 "!! DANGER !! Sway does not have CAP_SYS_PTRACE and cannot enforce security rules for processes running as other users.");
270 } 194 }
271 if (cap) { 195 if (cap) {
@@ -281,13 +205,13 @@ static void executable_sanity_check() {
281 stat(exe, &sb); 205 stat(exe, &sb);
282 // We assume that cap_get_file returning NULL implies ENODATA 206 // We assume that cap_get_file returning NULL implies ENODATA
283 if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) { 207 if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) {
284 sway_log(L_ERROR, 208 wlr_log(L_ERROR,
285 "sway executable has both the s(g)uid bit AND file caps set."); 209 "sway executable has both the s(g)uid bit AND file caps set.");
286 sway_log(L_ERROR, 210 wlr_log(L_ERROR,
287 "This is strongly discouraged (and completely broken)."); 211 "This is strongly discouraged (and completely broken).");
288 sway_log(L_ERROR, 212 wlr_log(L_ERROR,
289 "Please clear one of them (either the suid bit, or the file caps)."); 213 "Please clear one of them (either the suid bit, or the file caps).");
290 sway_log(L_ERROR, 214 wlr_log(L_ERROR,
291 "If unsure, strip the file caps."); 215 "If unsure, strip the file caps.");
292 exit(EXIT_FAILURE); 216 exit(EXIT_FAILURE);
293 } 217 }
@@ -295,6 +219,37 @@ static void executable_sanity_check() {
295#endif 219#endif
296} 220}
297 221
222static void drop_permissions(bool keep_caps) {
223 if (getuid() != geteuid() || getgid() != getegid()) {
224 if (setgid(getgid()) != 0) {
225 wlr_log(L_ERROR, "Unable to drop root");
226 exit(EXIT_FAILURE);
227 }
228 if (setuid(getuid()) != 0) {
229 wlr_log(L_ERROR, "Unable to drop root");
230 exit(EXIT_FAILURE);
231 }
232 }
233 if (setuid(0) != -1) {
234 wlr_log(L_ERROR, "Root privileges can be restored.");
235 exit(EXIT_FAILURE);
236 }
237#ifdef __linux__
238 if (keep_caps) {
239 // Drop every cap except CAP_SYS_PTRACE
240 cap_t caps = cap_init();
241 cap_value_t keep = CAP_SYS_PTRACE;
242 wlr_log(L_INFO, "Dropping extra capabilities");
243 if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) ||
244 cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) ||
245 cap_set_proc(caps)) {
246 wlr_log(L_ERROR, "Failed to drop extra capabilities");
247 exit(EXIT_FAILURE);
248 }
249 }
250#endif
251}
252
298int main(int argc, char **argv) { 253int main(int argc, char **argv) {
299 static int verbose = 0, debug = 0, validate = 0; 254 static int verbose = 0, debug = 0, validate = 0;
300 255
@@ -334,7 +289,7 @@ int main(int argc, char **argv) {
334 int c; 289 int c;
335 while (1) { 290 while (1) {
336 int option_index = 0; 291 int option_index = 0;
337 c = getopt_long(argc, argv, "hCdvVc:", long_options, &option_index); 292 c = getopt_long(argc, argv, "hCdDvVc:", long_options, &option_index);
338 if (c == -1) { 293 if (c == -1) {
339 break; 294 break;
340 } 295 }
@@ -352,6 +307,9 @@ int main(int argc, char **argv) {
352 case 'd': // debug 307 case 'd': // debug
353 debug = 1; 308 debug = 1;
354 break; 309 break;
310 case 'D': // extended debug options
311 enable_debug_tree = true;
312 break;
355 case 'v': // version 313 case 'v': // version
356 fprintf(stdout, "sway version " SWAY_VERSION "\n"); 314 fprintf(stdout, "sway version " SWAY_VERSION "\n");
357 exit(EXIT_SUCCESS); 315 exit(EXIT_SUCCESS);
@@ -374,37 +332,24 @@ int main(int argc, char **argv) {
374 } 332 }
375 } 333 }
376 334
377 // we need to setup logging before wlc_init in case it fails. 335 // TODO: switch logging over to wlroots?
378 if (debug) { 336 if (debug) {
379 init_log(L_DEBUG); 337 wlr_log_init(L_DEBUG, NULL);
380 } else if (verbose || validate) { 338 } else if (verbose || validate) {
381 init_log(L_INFO); 339 wlr_log_init(L_INFO, NULL);
382 } else { 340 } else {
383 init_log(L_ERROR); 341 wlr_log_init(L_ERROR, NULL);
384 } 342 }
385 343
386 if (optind < argc) { // Behave as IPC client 344 if (optind < argc) { // Behave as IPC client
387 if(optind != 1) { 345 if(optind != 1) {
388 sway_log(L_ERROR, "Don't use options with the IPC client"); 346 wlr_log(L_ERROR, "Don't use options with the IPC client");
389 exit(EXIT_FAILURE);
390 }
391 if (getuid() != geteuid() || getgid() != getegid()) {
392 if (setgid(getgid()) != 0) {
393 sway_log(L_ERROR, "Unable to drop root");
394 exit(EXIT_FAILURE);
395 }
396 if (setuid(getuid()) != 0) {
397 sway_log(L_ERROR, "Unable to drop root");
398 exit(EXIT_FAILURE);
399 }
400 }
401 if (setuid(0) != -1) {
402 sway_log(L_ERROR, "Root privileges can be restored.");
403 exit(EXIT_FAILURE); 347 exit(EXIT_FAILURE);
404 } 348 }
349 drop_permissions(false);
405 char *socket_path = getenv("SWAYSOCK"); 350 char *socket_path = getenv("SWAYSOCK");
406 if (!socket_path) { 351 if (!socket_path) {
407 sway_log(L_ERROR, "Unable to retrieve socket path"); 352 wlr_log(L_ERROR, "Unable to retrieve socket path");
408 exit(EXIT_FAILURE); 353 exit(EXIT_FAILURE);
409 } 354 }
410 char *command = join_args(argv + optind, argc - optind); 355 char *command = join_args(argv + optind, argc - optind);
@@ -413,49 +358,25 @@ int main(int argc, char **argv) {
413 } 358 }
414 359
415 executable_sanity_check(); 360 executable_sanity_check();
416#ifdef __linux__
417 bool suid = false; 361 bool suid = false;
362#ifdef __linux__
418 if (getuid() != geteuid() || getgid() != getegid()) { 363 if (getuid() != geteuid() || getgid() != getegid()) {
419 // Retain capabilities after setuid() 364 // Retain capabilities after setuid()
420 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) { 365 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
421 sway_log(L_ERROR, "Cannot keep caps after setuid()"); 366 wlr_log(L_ERROR, "Cannot keep caps after setuid()");
422 exit(EXIT_FAILURE); 367 exit(EXIT_FAILURE);
423 } 368 }
424 suid = true; 369 suid = true;
425 } 370 }
426#endif 371#endif
427 372
428 wlc_log_set_handler(wlc_log_handler);
429 log_kernel(); 373 log_kernel();
430 log_distro(); 374 log_distro();
431 log_env();
432 detect_proprietary(); 375 detect_proprietary();
433 detect_raspi(); 376 detect_raspi();
434 377
435 input_devices = create_list();
436
437 /* Changing code earlier than this point requires detailed review */
438 /* (That code runs as root on systems without logind, and wlc_init drops to
439 * another user.) */
440 register_wlc_handlers();
441 if (!wlc_init()) {
442 return 1;
443 }
444 register_extensions();
445
446#ifdef __linux__ 378#ifdef __linux__
447 if (suid) { 379 drop_permissions(suid);
448 // Drop every cap except CAP_SYS_PTRACE
449 cap_t caps = cap_init();
450 cap_value_t keep = CAP_SYS_PTRACE;
451 sway_log(L_INFO, "Dropping extra capabilities");
452 if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) ||
453 cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) ||
454 cap_set_proc(caps)) {
455 sway_log(L_ERROR, "Failed to drop extra capabilities");
456 exit(EXIT_FAILURE);
457 }
458 }
459#endif 380#endif
460 // handle SIGTERM signals 381 // handle SIGTERM signals
461 signal(SIGTERM, sig_handler); 382 signal(SIGTERM, sig_handler);
@@ -463,11 +384,16 @@ int main(int argc, char **argv) {
463 // prevent ipc from crashing sway 384 // prevent ipc from crashing sway
464 signal(SIGPIPE, SIG_IGN); 385 signal(SIGPIPE, SIG_IGN);
465 386
466 sway_log(L_INFO, "Starting sway version " SWAY_VERSION "\n"); 387 wlr_log(L_INFO, "Starting sway version " SWAY_VERSION);
388
389 layout_init();
467 390
468 init_layout(); 391 if (!server_init(&server)) {
392 return 1;
393 }
469 394
470 ipc_init(); 395 ipc_init(&server);
396 log_env();
471 397
472 if (validate) { 398 if (validate) {
473 bool valid = load_main_config(config_path, false); 399 bool valid = load_main_config(config_path, false);
@@ -484,11 +410,17 @@ int main(int argc, char **argv) {
484 410
485 security_sanity_check(); 411 security_sanity_check();
486 412
413 // TODO: wait for server to be ready
414 // TODO: consume config->cmd_queue
415 config->active = true;
416
487 if (!terminate_request) { 417 if (!terminate_request) {
488 wlc_run(); 418 server_run(&server);
489 } 419 }
490 420
491 list_free(input_devices); 421 wlr_log(L_INFO, "Shutting down sway");
422
423 server_fini(&server);
492 424
493 ipc_terminate(); 425 ipc_terminate();
494 426