// SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L #include #include #include #include #include "common/fd-util.h" #include "common/font.h" #include "common/spawn.h" #include "config/rcxml.h" #include "config/session.h" #include "labwc.h" #include "theme.h" #include "translate.h" #include "menu/menu.h" struct rcxml rc = { 0 }; static const struct option long_options[] = { {"config", required_argument, NULL, 'c'}, {"config-dir", required_argument, NULL, 'C'}, {"debug", no_argument, NULL, 'd'}, {"exit", no_argument, NULL, 'e'}, {"help", no_argument, NULL, 'h'}, {"merge-config", no_argument, NULL, 'm'}, {"reconfigure", no_argument, NULL, 'r'}, {"startup", required_argument, NULL, 's'}, {"session", required_argument, NULL, 'S'}, {"version", no_argument, NULL, 'v'}, {"verbose", no_argument, NULL, 'V'}, {0, 0, 0, 0} }; static const char labwc_usage[] = "Usage: labwc [options...]\n" " -c, --config Specify config file (with path)\n" " -C, --config-dir Specify config directory\n" " -d, --debug Enable full logging, including debug information\n" " -e, --exit Exit the compositor\n" " -h, --help Show help message and quit\n" " -m, --merge-config Merge user config files/theme in all XDG Base Dirs\n" " -r, --reconfigure Reload the compositor configuration\n" " -s, --startup Run command on startup\n" " -S, --session Run command on startup and terminate on exit\n" " -v, --version Show version number and quit\n" " -V, --verbose Enable more verbose logging\n"; static void usage(void) { printf("%s", labwc_usage); exit(0); } static void print_version(void) { #define FEATURE_ENABLED(feature) (HAVE_##feature ? "+" : "-") printf("labwc %s (%sxwayland %snls %srsvg %slibsfdo)\n", LABWC_VERSION, FEATURE_ENABLED(XWAYLAND), FEATURE_ENABLED(NLS), FEATURE_ENABLED(RSVG), FEATURE_ENABLED(LIBSFDO) ); #undef FEATURE_ENABLED } static void die_on_detecting_suid(void) { if (geteuid() != 0 && getegid() != 0) { return; } if (getuid() == geteuid() && getgid() == getegid()) { return; } wlr_log(WLR_ERROR, "SUID detected - aborting"); exit(EXIT_FAILURE); } static void die_on_no_fonts(void) { PangoContext *context = pango_font_map_create_context( pango_cairo_font_map_get_default()); PangoLayout *layout = pango_layout_new(context); pango_layout_set_text(layout, "abcdefg", -1); int nr_unknown_glyphs = pango_layout_get_unknown_glyphs_count(layout); g_object_unref(layout); g_object_unref(context); if (nr_unknown_glyphs > 0) { wlr_log(WLR_ERROR, "no fonts are available"); exit(EXIT_FAILURE); } /* * Make pango's dedicated thread exit. This prevents CI failures due to * SIGTERM delivered to the pango's thread. This kind of workaround is * not needed after we register our SIGTERM handler in * server_init() > wl_event_loop_add_signal(), which masks SIGTERM. */ pango_cairo_font_map_set_default(NULL); } static void send_signal_to_labwc_pid(int signal) { char *labwc_pid = getenv("LABWC_PID"); if (!labwc_pid) { wlr_log(WLR_ERROR, "LABWC_PID not set"); exit(EXIT_FAILURE); } int pid = atoi(labwc_pid); if (!pid) { wlr_log(WLR_ERROR, "should not send signal to pid 0"); exit(EXIT_FAILURE); } kill(pid, signal); } struct idle_ctx { struct server *server; const char *primary_client; const char *startup_cmd; }; static void idle_callback(void *data) { /* Idle callbacks destroy automatically once triggered */ struct idle_ctx *ctx = data; /* Start session-manager if one is specified by -S|--session */ if (ctx->primary_client) { ctx->server->primary_client_pid = spawn_primary_client(ctx->primary_client); if (ctx->server->primary_client_pid < 0) { wlr_log(WLR_ERROR, "fatal error starting primary client: %s", ctx->primary_client); wl_display_terminate(ctx->server->wl_display); return; } } session_autostart_init(ctx->server); if (ctx->startup_cmd) { spawn_async_no_shell(ctx->startup_cmd); } } int main(int argc, char *argv[]) { char *startup_cmd = NULL; char *primary_client = NULL; enum wlr_log_importance verbosity = WLR_ERROR; int c; while (1) { int index = 0; c = getopt_long(argc, argv, "c:C:dehmrs:S:vV", long_options, &index); if (c == -1) { break; } switch (c) { case 'c': rc.config_file = optarg; break; case 'C': rc.config_dir = optarg; break; case 'd': verbosity = WLR_DEBUG; break; case 'e': send_signal_to_labwc_pid(SIGTERM); exit(0); case 'm': rc.merge_config = true; break; case 'r': send_signal_to_labwc_pid(SIGHUP); exit(0); case 's': startup_cmd = optarg; break; case 'S': primary_client = optarg; break; case 'v': print_version(); exit(0); case 'V': verbosity = WLR_INFO; break; case 'h': default: usage(); } } if (optind < argc) { usage(); } wlr_log_init(verbosity, NULL); die_on_detecting_suid(); die_on_no_fonts(); session_environment_init(); #if HAVE_NLS /* Initialize locale after setting env vars */ setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); textdomain(GETTEXT_PACKAGE); #endif rcxml_read(rc.config_file); /* * Set environment variable LABWC_PID to the pid of the compositor * so that SIGHUP and SIGTERM can be sent to specific instances using * `kill -s ` rather than `killall -s labwc` */ char pid[32]; snprintf(pid, sizeof(pid), "%d", getpid()); if (setenv("LABWC_PID", pid, true) < 0) { wlr_log_errno(WLR_ERROR, "unable to set LABWC_PID"); } else { wlr_log(WLR_DEBUG, "LABWC_PID=%s", pid); } /* useful for helper programs */ if (setenv("LABWC_VER", LABWC_VERSION, true) < 0) { wlr_log_errno(WLR_ERROR, "unable to set LABWC_VER"); } else { wlr_log(WLR_DEBUG, "LABWC_VER=%s", LABWC_VERSION); } if (!getenv("XDG_RUNTIME_DIR")) { wlr_log(WLR_ERROR, "XDG_RUNTIME_DIR is unset"); exit(EXIT_FAILURE); } increase_nofile_limit(); struct server server = { 0 }; server_init(&server); server_start(&server); struct theme theme = { 0 }; theme_init(&theme, &server, rc.theme_name); rc.theme = &theme; server.theme = &theme; menu_init(&server); /* Delay startup of applications until the event loop is ready */ struct idle_ctx idle_ctx = { .server = &server, .primary_client = primary_client, .startup_cmd = startup_cmd }; wl_event_loop_add_idle(server.wl_event_loop, idle_callback, &idle_ctx); wl_display_run(server.wl_display); session_shutdown(&server); menu_finish(&server); theme_finish(&theme); rcxml_finish(); font_finish(); server_finish(&server); return 0; }