X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fmain.c;h=7738dacc12e1e6725e975cce0446dd97fa5f6fde;hb=36a1a8282f99bbfd905d97b44a091a37f768bf88;hp=a9d7141634e53d3ac86d8ab7509fec9d08e35b91;hpb=9d15a00ba83ccf0e5750f2d3b552c24188099edf;p=i3%2Fi3 diff --git a/src/main.c b/src/main.c index a9d71416..7738dacc 100644 --- a/src/main.c +++ b/src/main.c @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3 - an improved dynamic tiling window manager - * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) + * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) * * main.c: Initialization, main loop * @@ -12,10 +12,25 @@ #include #include #include +#include +#include +#include +#include #include "all.h" #include "sd-daemon.h" +/* The original value of RLIMIT_CORE when i3 was started. We need to restore + * this before starting any other process, since we set RLIMIT_CORE to + * RLIM_INFINITY for i3 debugging versions. */ +struct rlimit original_rlimit_core; + +/* Whether this version of i3 is a debug build or a release build. */ +bool debug_build = false; + +/** The number of file descriptors passed via socket activation. */ +int listen_fds; + static int xkb_event_base; int xkb_current_group; @@ -178,7 +193,7 @@ static void xkb_got_event(EV_P_ struct ev_io *w, int revents) { xcb_key_symbols_free(keysyms); keysyms = xcb_key_symbols_alloc(conn); - xcb_get_numlock_mask(conn); + xcb_numlock_mask = aio_get_mod_mask_for(XCB_NUM_LOCK, keysyms); ungrab_all_keys(conn); DLOG("Re-grabbing...\n"); @@ -198,6 +213,28 @@ static void i3_exit() { #if EV_VERSION_MAJOR >= 4 ev_loop_destroy(main_loop); #endif + + if (*shmlogname != '\0') { + fprintf(stderr, "Closing SHM log \"%s\"\n", shmlogname); + fflush(stderr); + shm_unlink(shmlogname); + } +} + +/* + * (One-shot) Handler for all signals with default action "Term", see signal(7) + * + * Unlinks the SHM log and re-raises the signal. + * + */ +static void handle_signal(int sig, siginfo_t *info, void *data) { + fprintf(stderr, "Received signal %d, terminating\n", sig); + if (*shmlogname != '\0') { + fprintf(stderr, "Closing SHM log \"%s\"\n", shmlogname); + shm_unlink(shmlogname); + } + fflush(stderr); + raise(sig); } int main(int argc, char *argv[]) { @@ -215,22 +252,43 @@ int main(int argc, char *argv[]) { {"layout", required_argument, 0, 'L'}, {"restart", required_argument, 0, 0}, {"force-xinerama", no_argument, 0, 0}, + {"force_xinerama", no_argument, 0, 0}, {"disable-signalhandler", no_argument, 0, 0}, + {"shmlog-size", required_argument, 0, 0}, + {"shmlog_size", required_argument, 0, 0}, {"get-socketpath", no_argument, 0, 0}, + {"get_socketpath", no_argument, 0, 0}, {0, 0, 0, 0} }; int option_index = 0, opt; setlocale(LC_ALL, ""); + /* Get the RLIMIT_CORE limit at startup time to restore this before + * starting processes. */ + getrlimit(RLIMIT_CORE, &original_rlimit_core); + /* Disable output buffering to make redirects in .xsession actually useful for debugging */ if (!isatty(fileno(stdout))) setbuf(stdout, NULL); srand(time(NULL)); + /* Init logging *before* initializing debug_build to guarantee early + * (file) logging. */ init_logging(); + /* I3_VERSION contains either something like this: + * "4.0.2 (2011-11-11, branch "release")". + * or: "4.0.2-123-gCOFFEEBABE (2011-11-11, branch "next")". + * + * So we check for the offset of the first opening round bracket to + * determine whether this is a git version or a release version. */ + debug_build = ((strchr(I3_VERSION, '(') - I3_VERSION) > 10); + + /* On non-release builds, disable SHM logging by default. */ + shmlog_size = (debug_build ? 25 * 1024 * 1024 : 0); + start_argv = argv; while ((opt = getopt_long(argc, argv, "c:CvaL:hld:V", long_options, &option_index)) != -1) { @@ -266,7 +324,8 @@ int main(int argc, char *argv[]) { /* DEPRECATED, ignored for the next 3 versions (3.e, 3.f, 3.g) */ break; case 0: - if (strcmp(long_options[option_index].name, "force-xinerama") == 0) { + if (strcmp(long_options[option_index].name, "force-xinerama") == 0 || + strcmp(long_options[option_index].name, "force_xinerama") == 0) { force_xinerama = true; ELOG("Using Xinerama instead of RandR. This option should be " "avoided at all cost because it does not refresh the list " @@ -277,14 +336,23 @@ int main(int argc, char *argv[]) { } else if (strcmp(long_options[option_index].name, "disable-signalhandler") == 0) { disable_signalhandler = true; break; - } else if (strcmp(long_options[option_index].name, "get-socketpath") == 0) { - char *socket_path = socket_path_from_x11(); + } else if (strcmp(long_options[option_index].name, "get-socketpath") == 0 || + strcmp(long_options[option_index].name, "get_socketpath") == 0) { + char *socket_path = root_atom_contents("I3_SOCKET_PATH"); if (socket_path) { printf("%s\n", socket_path); return 0; } return 1; + } else if (strcmp(long_options[option_index].name, "shmlog-size") == 0 || + strcmp(long_options[option_index].name, "shmlog_size") == 0) { + shmlog_size = atoi(optarg); + /* Re-initialize logging immediately to get as many + * logmessages as possible into the SHM log. */ + init_logging(); + LOG("Limiting SHM log size to %d bytes\n", shmlog_size); + break; } else if (strcmp(long_options[option_index].name, "restart") == 0) { FREE(layout_path); layout_path = sstrdup(optarg); @@ -311,6 +379,11 @@ int main(int argc, char *argv[]) { fprintf(stderr, "\t--get-socketpath\n" "\tRetrieve the i3 IPC socket path from X11, print it, then exit.\n"); fprintf(stderr, "\n"); + fprintf(stderr, "\t--shmlog-size \n" + "\tLimits the size of the i3 SHM log to bytes. Setting this\n" + "\tto 0 disables SHM logging entirely.\n" + "\tThe default is %d bytes.\n", shmlog_size); + fprintf(stderr, "\n"); fprintf(stderr, "If you pass plain text arguments, i3 will interpret them as a command\n" "to send to a currently running i3 (like i3-msg). This allows you to\n" "use nice and logical commands, such as:\n" @@ -346,7 +419,7 @@ int main(int argc, char *argv[]) { optind++; } LOG("Command is: %s (%d bytes)\n", payload, strlen(payload)); - char *socket_path = socket_path_from_x11(); + char *socket_path = root_atom_contents("I3_SOCKET_PATH"); if (!socket_path) { ELOG("Could not get i3 IPC socket path\n"); return 1; @@ -380,6 +453,30 @@ int main(int argc, char *argv[]) { return 0; } + /* Enable logging to handle the case when the user did not specify --shmlog-size */ + init_logging(); + + /* Try to enable core dumps by default when running a debug build */ + if (debug_build) { + struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY }; + setrlimit(RLIMIT_CORE, &limit); + + /* The following code is helpful, but not required. We thus don’t pay + * much attention to error handling, non-linux or other edge cases. */ + char cwd[PATH_MAX]; + LOG("CORE DUMPS: You are running a development version of i3, so coredumps were automatically enabled (ulimit -c unlimited).\n"); + if (getcwd(cwd, sizeof(cwd)) != NULL) + LOG("CORE DUMPS: Your current working directory is \"%s\".\n", cwd); + int patternfd; + if ((patternfd = open("/proc/sys/kernel/core_pattern", O_RDONLY)) >= 0) { + memset(cwd, '\0', sizeof(cwd)); + if (read(patternfd, cwd, sizeof(cwd)) > 0) + /* a trailing newline is included in cwd */ + LOG("CORE DUMPS: Your core_pattern is: %s", cwd); + close(patternfd); + } + } + LOG("i3 (tree) version " I3_VERSION " starting\n"); conn = xcb_connect(NULL, &conn_screen); @@ -503,20 +600,11 @@ int main(int argc, char *argv[]) { property_handlers_init(); - /* Set up the atoms we support */ - xcb_atom_t supported_atoms[] = { -#define xmacro(atom) A_ ## atom, -#include "atoms.xmacro" -#undef xmacro - }; - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 16, supported_atoms); - /* Set up the window manager’s name */ - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32, 1, &root); - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3"); + ewmh_setup_hints(); keysyms = xcb_key_symbols_alloc(conn); - xcb_get_numlock_mask(conn); + xcb_numlock_mask = aio_get_mod_mask_for(XCB_NUM_LOCK, keysyms); translate_keysyms(); grab_all_keys(conn, false); @@ -574,15 +662,29 @@ int main(int argc, char *argv[]) { ev_io_start(main_loop, ipc_io); } - /* Also handle the UNIX domain sockets passed via socket activation */ - int fds = sd_listen_fds(1); - if (fds < 0) + /* Also handle the UNIX domain sockets passed via socket activation. The + * parameter 1 means "remove the environment variables", we don’t want to + * pass these to child processes. */ + listen_fds = sd_listen_fds(0); + if (listen_fds < 0) ELOG("socket activation: Error in sd_listen_fds\n"); - else if (fds == 0) + else if (listen_fds == 0) DLOG("socket activation: no sockets passed\n"); else { - for (int fd = SD_LISTEN_FDS_START; fd < (SD_LISTEN_FDS_START + fds); fd++) { + int flags; + for (int fd = SD_LISTEN_FDS_START; + fd < (SD_LISTEN_FDS_START + listen_fds); + fd++) { DLOG("socket activation: also listening on fd %d\n", fd); + + /* sd_listen_fds() enables FD_CLOEXEC by default. + * However, we need to keep the file descriptors open for in-place + * restarting, therefore we explicitly disable FD_CLOEXEC. */ + if ((flags = fcntl(fd, F_GETFD)) < 0 || + fcntl(fd, F_SETFD, flags & ~FD_CLOEXEC) < 0) { + ELOG("Could not disable FD_CLOEXEC on fd %d\n", fd); + } + struct ev_io *ipc_io = scalloc(sizeof(struct ev_io)); ev_io_init(ipc_io, ipc_new_client, fd, EV_READ); ev_io_start(main_loop, ipc_io); @@ -619,8 +721,31 @@ int main(int argc, char *argv[]) { manage_existing_windows(root); + struct sigaction action; + + action.sa_sigaction = handle_signal; + action.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO; + sigemptyset(&action.sa_mask); + if (!disable_signalhandler) setup_signal_handler(); + else { + /* Catch all signals with default action "Core", see signal(7) */ + if (sigaction(SIGQUIT, &action, NULL) == -1 || + sigaction(SIGILL, &action, NULL) == -1 || + sigaction(SIGABRT, &action, NULL) == -1 || + sigaction(SIGFPE, &action, NULL) == -1 || + sigaction(SIGSEGV, &action, NULL) == -1) + ELOG("Could not setup signal handler"); + } + + /* Catch all signals with default action "Term", see signal(7) */ + if (sigaction(SIGHUP, &action, NULL) == -1 || + sigaction(SIGINT, &action, NULL) == -1 || + sigaction(SIGALRM, &action, NULL) == -1 || + sigaction(SIGUSR1, &action, NULL) == -1 || + sigaction(SIGUSR2, &action, NULL) == -1) + ELOG("Could not setup signal handler"); /* Ignore SIGPIPE to survive errors when an IPC client disconnects * while we are sending him a message */ @@ -631,7 +756,7 @@ int main(int argc, char *argv[]) { struct Autostart *exec; TAILQ_FOREACH(exec, &autostarts, autostarts) { LOG("auto-starting %s\n", exec->command); - start_application(exec->command); + start_application(exec->command, exec->no_startup_id); } } @@ -639,17 +764,18 @@ int main(int argc, char *argv[]) { struct Autostart *exec_always; TAILQ_FOREACH(exec_always, &autostarts_always, autostarts_always) { LOG("auto-starting (always!) %s\n", exec_always->command); - start_application(exec_always->command); + start_application(exec_always->command, exec_always->no_startup_id); } /* Start i3bar processes for all configured bars */ Barconfig *barconfig; TAILQ_FOREACH(barconfig, &barconfigs, configs) { char *command = NULL; - sasprintf(&command, "i3bar --bar_id=%s --socket=\"%s\"", - barconfig->id, current_socketpath); + sasprintf(&command, "%s --bar_id=%s --socket=\"%s\"", + barconfig->i3bar_command ? barconfig->i3bar_command : "i3bar", + barconfig->id, current_socketpath); LOG("Starting bar process: %s\n", command); - start_application(command); + start_application(command, true); free(command); }