]> git.sur5r.net Git - i3/i3/blobdiff - i3-nagbar/main.c
i3-nagbar: take our terminal execution kludge to the next level
[i3/i3] / i3-nagbar / main.c
index e700e2e9259a299564cd7208a6470807d613c4ef..8f28de16a815072efd590ac896b8b3ccd9798cf6 100644 (file)
@@ -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-2013 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * i3-nagbar is a utility which displays a nag message, for example in the case
  * when the user has an error in his configuration file.
@@ -30,6 +30,8 @@
 #include "libi3.h"
 #include "i3-nagbar.h"
 
+static char *argv0 = NULL;
+
 typedef struct {
     i3String *label;
     char *action;
@@ -148,7 +150,7 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
      * */
     char *script_path = get_process_filename("nagbar-cmd");
 
-    int fd = open(script_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR);
+    int fd = open(script_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
     if (fd == -1) {
         warn("Could not create temporary script to store the nagbar command");
         return;
@@ -162,11 +164,18 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
     /* Also closes fd */
     fclose(script);
 
+    char *link_path;
+    sasprintf(&link_path, "%s.nagbar_cmd", script_path);
+    symlink(get_exe_path(argv0), link_path);
+
     char *terminal_cmd;
-    sasprintf(&terminal_cmd, "i3-sensible-terminal -e %s", script_path);
+    sasprintf(&terminal_cmd, "i3-sensible-terminal -e %s", link_path);
+    printf("argv0 = %s\n", argv0);
+    printf("terminal_cmd = %s\n", terminal_cmd);
 
     start_application(terminal_cmd);
 
+    free(link_path);
     free(terminal_cmd);
     free(script_path);
 
@@ -269,6 +278,41 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
 }
 
 int main(int argc, char *argv[]) {
+    /* The following lines are a terribly horrible kludge. Because terminal
+     * emulators have different ways of interpreting the -e command line
+     * argument (some need -e "less /etc/fstab", others need -e less
+     * /etc/fstab), we need to write commands to a script and then just run
+     * that script. However, since on some machines, $XDG_RUNTIME_DIR and
+     * $TMPDIR are mounted with noexec, we cannot directly execute the script
+     * either.
+     *
+     * Initially, we tried to pass the command via the environment variable
+     * _I3_NAGBAR_CMD. But turns out that some terminal emulators such as
+     * xfce4-terminal run all windows from a single master process and only
+     * pass on the command (not the environment) to that master process.
+     *
+     * Therefore, we symlink i3-nagbar (which MUST reside on an executable
+     * filesystem) with a special name and run that symlink. When i3-nagbar
+     * recognizes it’s started as a binary ending in .nagbar_cmd, it strips off
+     * the .nagbar_cmd suffix and runs /bin/sh on argv[0]. That way, we can run
+     * a shell script on a noexec filesystem.
+     *
+     * From a security point of view, i3-nagbar is just an alias to /bin/sh in
+     * certain circumstances. This should not open any new security issues, I
+     * hope. */
+    char *cmd = NULL;
+    const size_t argv0_len = strlen(argv[0]);
+    if (argv0_len > strlen(".nagbar_cmd") &&
+        strcmp(argv[0] + argv0_len - strlen(".nagbar_cmd"), ".nagbar_cmd") == 0) {
+        unlink(argv[0]);
+        cmd = strdup(argv[0]);
+        *(cmd + argv0_len - strlen(".nagbar_cmd")) = '\0';
+        execl("/bin/sh", "/bin/sh", cmd, NULL);
+        err(EXIT_FAILURE, "execv(/bin/sh, /bin/sh, %s)", cmd);
+    }
+
+    argv0 = argv[0];
+
     char *pattern = sstrdup("-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1");
     int o, option_index = 0;
     enum { TYPE_ERROR = 0, TYPE_WARNING = 1 } bar_type = TYPE_ERROR;