From: Michael Stapelberg Date: Fri, 6 Jan 2012 23:40:07 +0000 (+0000) Subject: shmlog: store meta information in the buffer itself, store path as X11 atom X-Git-Tag: 4.2~157 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=dee7c07ad27263e2f9b595c06d4c1f0394036e78;p=i3%2Fi3 shmlog: store meta information in the buffer itself, store path as X11 atom This makes i3-dump-log completely independent of a running i3 instance. --- diff --git a/i3-config-wizard/main.c b/i3-config-wizard/main.c index 11fec6ce..ac596024 100644 --- a/i3-config-wizard/main.c +++ b/i3-config-wizard/main.c @@ -410,7 +410,7 @@ int main(int argc, char *argv[]) { unlink(config_path); if (socket_path == NULL) - socket_path = socket_path_from_x11(); + socket_path = root_atom_contents("I3_SOCKET_PATH"); if (socket_path == NULL) socket_path = "/tmp/i3-ipc.sock"; diff --git a/i3-dump-log/main.c b/i3-dump-log/main.c index 68cbdb46..30dd514f 100644 --- a/i3-dump-log/main.c +++ b/i3-dump-log/main.c @@ -25,16 +25,14 @@ #include #include "libi3.h" +#include "shmlog.h" #include int main(int argc, char *argv[]) { - char *socket_path = getenv("I3SOCK"); int o, option_index = 0; - int message_type = I3_IPC_MESSAGE_TYPE_GET_LOG_MARKERS; bool verbose = false; static struct option long_options[] = { - {"socket", required_argument, 0, 's'}, {"version", no_argument, 0, 'v'}, {"verbose", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, @@ -44,11 +42,7 @@ int main(int argc, char *argv[]) { char *options_string = "s:vVh"; while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) { - if (o == 's') { - if (socket_path != NULL) - free(socket_path); - socket_path = sstrdup(optarg); - } else if (o == 'v') { + if (o == 'v') { printf("i3-dump-log " I3_VERSION "\n"); return 0; } else if (o == 'V') { @@ -60,86 +54,40 @@ int main(int argc, char *argv[]) { } } - if (socket_path == NULL) - socket_path = socket_path_from_x11(); - - /* Fall back to the default socket path */ - if (socket_path == NULL) - socket_path = sstrdup("/tmp/i3-ipc.sock"); - - int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0); - if (sockfd == -1) - err(EXIT_FAILURE, "Could not create socket"); - - struct sockaddr_un addr; - memset(&addr, 0, sizeof(struct sockaddr_un)); - addr.sun_family = AF_LOCAL; - strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); - if (connect(sockfd, (const struct sockaddr*)&addr, sizeof(struct sockaddr_un)) < 0) - err(EXIT_FAILURE, "Could not connect to i3"); - - if (ipc_send_message(sockfd, 0, message_type, NULL) == -1) - err(EXIT_FAILURE, "IPC: write()"); - - uint32_t reply_length; - uint8_t *reply; - int ret; - if ((ret = ipc_recv_message(sockfd, message_type, &reply_length, &reply)) != 0) { - if (ret == -1) - err(EXIT_FAILURE, "IPC: read()"); - exit(1); - } - char *buffer = NULL; - sasprintf(&buffer, "%.*s", reply_length, reply); - /* The reply will look like this: - * {"offset_next_write":1729,"offset_last_wrap":1996,"size":2048,"shmname":"/i3-log-399"} - * IMO, it’s not worth linking a JSON parser in just for this. If the - * structure changes in the future, this decision needs to be re-evaluated - * :). */ - int offset_next_write, offset_last_wrap, logbuffer_size; - char *next_write_str = strstr(buffer, "offset_next_write"), - *last_wrap_str = strstr(buffer, "offset_last_wrap"), - *size_str = strstr(buffer, "size"), - *shmname = strstr(buffer, "shmname"); - if (!next_write_str || - !last_wrap_str || - !size_str || - !shmname || - sscanf(next_write_str, "offset_next_write\":%d", &offset_next_write) != 1 || - sscanf(last_wrap_str, "offset_last_wrap\":%d", &offset_last_wrap) != 1 || - sscanf(size_str, "size\":%d", &logbuffer_size) != 1) - errx(EXIT_FAILURE, "invalid IPC reply: %s\n", buffer); - - shmname += strlen("shmname\":\""); - char *quote = strchr(shmname, '"'); - if (!quote) - errx(EXIT_FAILURE, "invalid IPC reply: %s\n", buffer); - *quote = '\0'; - - if (verbose) - printf("next_write = %d, last_wrap = %d, logbuffer_size = %d, shmname = %s\n", - offset_next_write, offset_last_wrap, logbuffer_size, shmname); + char *shmname = root_atom_contents("I3_SHMLOG_PATH"); + if (shmname == NULL) + errx(EXIT_FAILURE, "Cannot get I3_SHMLOG_PATH atom contents. Is i3 running on this display?"); if (*shmname == '\0') errx(EXIT_FAILURE, "Cannot dump log: SHM logging is disabled in i3."); + struct stat statbuf; + int logbuffer_shm = shm_open(shmname, O_RDONLY, 0); if (logbuffer_shm == -1) err(EXIT_FAILURE, "Could not shm_open SHM segment for the i3 log (%s)", shmname); - char *logbuffer = mmap(NULL, logbuffer_size, PROT_READ, MAP_SHARED, logbuffer_shm, 0); + if (fstat(logbuffer_shm, &statbuf) != 0) + err(EXIT_FAILURE, "stat(%s)", shmname); + + char *logbuffer = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, logbuffer_shm, 0); if (logbuffer == MAP_FAILED) err(EXIT_FAILURE, "Could not mmap SHM segment for the i3 log"); + i3_shmlog_header *header = (i3_shmlog_header*)logbuffer; + + if (verbose) + printf("next_write = %d, last_wrap = %d, logbuffer_size = %d, shmname = %s\n", + header->offset_next_write, header->offset_last_wrap, header->size, shmname); int chars; - char *walk = logbuffer + offset_next_write; + char *walk = logbuffer + header->offset_next_write; /* Skip the first line, it very likely is mangled. Not a problem, though, * the log is chatty enough to have plenty lines left. */ while (*walk != '\0') walk++; /* Print the oldest log lines. We use printf("%s") to stop on \0. */ - while (walk < (logbuffer + offset_last_wrap)) { + while (walk < (logbuffer + header->offset_last_wrap)) { chars = printf("%s", walk); /* Shortcut: If there are two consecutive \0 bytes, this part of the * buffer was never touched. To not call printf() for every byte of the @@ -150,8 +98,8 @@ int main(int argc, char *argv[]) { } /* Then start from the beginning and print the newer lines */ - walk = logbuffer; - while (walk < (logbuffer + offset_next_write)) { + walk = logbuffer + sizeof(i3_shmlog_header); + while (walk < (logbuffer + header->offset_next_write)) { chars = printf("%s", walk); walk += (chars > 0 ? chars : 1); } diff --git a/i3-input/main.c b/i3-input/main.c index 25ccceaa..b5709523 100644 --- a/i3-input/main.c +++ b/i3-input/main.c @@ -342,7 +342,7 @@ int main(int argc, char *argv[]) { printf("using format \"%s\"\n", format); if (socket_path == NULL) - socket_path = socket_path_from_x11(); + socket_path = root_atom_contents("I3_SOCKET_PATH"); if (socket_path == NULL) socket_path = "/tmp/i3-ipc.sock"; diff --git a/i3-msg/main.c b/i3-msg/main.c index e16f6943..ccf6e10f 100644 --- a/i3-msg/main.c +++ b/i3-msg/main.c @@ -73,11 +73,9 @@ int main(int argc, char *argv[]) { message_type = I3_IPC_MESSAGE_TYPE_GET_MARKS; else if (strcasecmp(optarg, "get_bar_config") == 0) message_type = I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG; - else if (strcasecmp(optarg, "get_log_markers") == 0) - message_type = I3_IPC_MESSAGE_TYPE_GET_LOG_MARKERS; else { printf("Unknown message type\n"); - printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_log_markers\n"); + printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config\n"); exit(EXIT_FAILURE); } } else if (o == 'q') { @@ -93,7 +91,7 @@ int main(int argc, char *argv[]) { } if (socket_path == NULL) - socket_path = socket_path_from_x11(); + socket_path = root_atom_contents("I3_SOCKET_PATH"); /* Fall back to the default socket path */ if (socket_path == NULL) diff --git a/include/atoms.xmacro b/include/atoms.xmacro index f08a90d5..b907f41e 100644 --- a/include/atoms.xmacro +++ b/include/atoms.xmacro @@ -26,3 +26,4 @@ xmacro(WM_WINDOW_ROLE) xmacro(I3_SOCKET_PATH) xmacro(I3_CONFIG_PATH) xmacro(I3_SYNC) +xmacro(I3_SHMLOG_PATH) diff --git a/include/i3/ipc.h b/include/i3/ipc.h index e8de2e1e..bfadf4cf 100644 --- a/include/i3/ipc.h +++ b/include/i3/ipc.h @@ -40,9 +40,6 @@ /** Request the configuration for a specific 'bar' */ #define I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG 6 -/** Request the SHM debug log start/wrap markers */ -#define I3_IPC_MESSAGE_TYPE_GET_LOG_MARKERS 7 - /* * Messages from i3 to clients * @@ -69,9 +66,6 @@ /** Bar config reply type */ #define I3_IPC_REPLY_TYPE_BAR_CONFIG 6 -/** Request the SHM debug log start/wrap markers */ -#define I3_IPC_REPLY_TYPE_LOG_MARKERS 7 - /* * Events from i3 to clients. Events have the first bit set high. * diff --git a/include/libi3.h b/include/libi3.h index b8ed6c31..973c885d 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -47,13 +47,14 @@ struct Font { #endif /** - * Try to get the socket path from X11 and return NULL if it doesn’t work. + * Try to get the contents of the given atom (for example I3_SOCKET_PATH) from + * the X11 root window and return NULL if it doesn’t work. * - * The memory for the socket path is dynamically allocated and has to be + * The memory for the contents is dynamically allocated and has to be * free()d by the caller. * */ -char *socket_path_from_x11(); +char *root_atom_contents(const char *atomname); /** * Safe-wrapper around malloc which exits if malloc returns NULL (meaning that diff --git a/include/log.h b/include/log.h index ef24bda8..0eb55744 100644 --- a/include/log.h +++ b/include/log.h @@ -37,13 +37,6 @@ void init_logging(); */ void add_loglevel(const char *level); -/** - * Returns the offsets for the next write and for the last wrap. - * Necessary to print the i3 SHM log in the correct order. - * - */ -void get_log_markers(int *offset_next_write, int *offset_last_wrap, int *size); - /** * Set verbosity of i3. If verbose is set to true, informative messages will * be printed to stdout. If verbose is set to false, only errors will be diff --git a/libi3/get_socket_path.c b/libi3/get_socket_path.c deleted file mode 100644 index d623b6c3..00000000 --- a/libi3/get_socket_path.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * vim:ts=4:sw=4:expandtab - * - * i3 - an improved dynamic tiling window manager - * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) - * - */ -#include -#include -#include -#include - -#include -#include - -#include "libi3.h" - -/* - * Try to get the socket path from X11 and return NULL if it doesn’t work. - * - * The memory for the socket path is dynamically allocated and has to be - * free()d by the caller. - * - */ -char *socket_path_from_x11() { - xcb_connection_t *conn; - xcb_intern_atom_cookie_t atom_cookie; - xcb_intern_atom_reply_t *atom_reply; - int screen; - char *socket_path; - - if ((conn = xcb_connect(NULL, &screen)) == NULL || - xcb_connection_has_error(conn)) - return NULL; - - atom_cookie = xcb_intern_atom(conn, 0, strlen("I3_SOCKET_PATH"), "I3_SOCKET_PATH"); - - xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screen); - xcb_window_t root = root_screen->root; - - atom_reply = xcb_intern_atom_reply(conn, atom_cookie, NULL); - if (atom_reply == NULL) - return NULL; - - xcb_get_property_cookie_t prop_cookie; - xcb_get_property_reply_t *prop_reply; - prop_cookie = xcb_get_property_unchecked(conn, false, root, atom_reply->atom, - XCB_GET_PROPERTY_TYPE_ANY, 0, PATH_MAX); - prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL); - if (prop_reply == NULL || xcb_get_property_value_length(prop_reply) == 0) - return NULL; - if (asprintf(&socket_path, "%.*s", xcb_get_property_value_length(prop_reply), - (char*)xcb_get_property_value(prop_reply)) == -1) - return NULL; - xcb_disconnect(conn); - return socket_path; -} - diff --git a/libi3/root_atom_contents.c b/libi3/root_atom_contents.c new file mode 100644 index 00000000..927cc5f8 --- /dev/null +++ b/libi3/root_atom_contents.c @@ -0,0 +1,59 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved dynamic tiling window manager + * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) + * + */ +#include +#include +#include +#include + +#include +#include + +#include "libi3.h" + +/* + * Try to get the contents of the given atom (for example I3_SOCKET_PATH) from + * the X11 root window and return NULL if it doesn’t work. + * + * The memory for the contents is dynamically allocated and has to be + * free()d by the caller. + * + */ +char *root_atom_contents(const char *atomname) { + xcb_connection_t *conn; + xcb_intern_atom_cookie_t atom_cookie; + xcb_intern_atom_reply_t *atom_reply; + int screen; + char *socket_path; + + if ((conn = xcb_connect(NULL, &screen)) == NULL || + xcb_connection_has_error(conn)) + return NULL; + + atom_cookie = xcb_intern_atom(conn, 0, strlen(atomname), atomname); + + xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screen); + xcb_window_t root = root_screen->root; + + atom_reply = xcb_intern_atom_reply(conn, atom_cookie, NULL); + if (atom_reply == NULL) + return NULL; + + xcb_get_property_cookie_t prop_cookie; + xcb_get_property_reply_t *prop_reply; + prop_cookie = xcb_get_property_unchecked(conn, false, root, atom_reply->atom, + XCB_GET_PROPERTY_TYPE_ANY, 0, PATH_MAX); + prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL); + if (prop_reply == NULL || xcb_get_property_value_length(prop_reply) == 0) + return NULL; + if (asprintf(&socket_path, "%.*s", xcb_get_property_value_length(prop_reply), + (char*)xcb_get_property_value(prop_reply)) == -1) + return NULL; + xcb_disconnect(conn); + return socket_path; +} + diff --git a/src/ipc.c b/src/ipc.c index 8a50761d..6eccbac7 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -643,48 +643,6 @@ IPC_HANDLER(get_bar_config) { y(free); } -/* - * Formats the reply message for a GET_LOG_MARKERS request and sends it to the - * client. - * - */ -IPC_HANDLER(get_log_markers) { -#if YAJL_MAJOR >= 2 - yajl_gen gen = yajl_gen_alloc(NULL); -#else - yajl_gen gen = yajl_gen_alloc(NULL, NULL); -#endif - - int offset_next_write, offset_last_wrap, logsize; - get_log_markers(&offset_next_write, &offset_last_wrap, &logsize); - - y(map_open); - ystr("offset_next_write"); - y(integer, offset_next_write); - - ystr("offset_last_wrap"); - y(integer, offset_last_wrap); - - ystr("shmname"); - ystr(shmlogname); - - ystr("size"); - y(integer, logsize); - - y(map_close); - - const unsigned char *payload; -#if YAJL_MAJOR >= 2 - size_t length; -#else - unsigned int length; -#endif - y(get_buf, &payload, &length); - - ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_LOG_MARKERS, payload); - y(free); -} - /* * Callback for the YAJL parser (will be called when a string is parsed). * @@ -770,7 +728,7 @@ IPC_HANDLER(subscribe) { /* The index of each callback function corresponds to the numeric * value of the message type (see include/i3/ipc.h) */ -handler_t handlers[8] = { +handler_t handlers[7] = { handle_command, handle_get_workspaces, handle_subscribe, @@ -778,7 +736,6 @@ handler_t handlers[8] = { handle_tree, handle_get_marks, handle_get_bar_config, - handle_get_log_markers }; /* diff --git a/src/log.c b/src/log.c index 14819e9e..7b7eca50 100644 --- a/src/log.c +++ b/src/log.c @@ -23,6 +23,7 @@ #include "log.h" #include "i3.h" #include "libi3.h" +#include "shmlog.h" /* loglevels.h is autogenerated at make time */ #include "loglevels.h" @@ -52,6 +53,20 @@ static int logbuffer_size; /* File descriptor for shm_open. */ static int logbuffer_shm; +/* + * Writes the offsets for the next write and for the last wrap to the + * shmlog_header. + * Necessary to print the i3 SHM log in the correct order. + * + */ +static void store_log_markers() { + i3_shmlog_header *header = (i3_shmlog_header*)logbuffer; + + header->offset_next_write = (logwalk - logbuffer); + header->offset_last_wrap = (loglastwrap - logbuffer); + header->size = logbuffer_size; +} + /* * Initializes logging by creating an error logfile in /tmp (or * XDG_RUNTIME_DIR, see get_process_filename()). @@ -103,8 +118,9 @@ void init_logging() { logbuffer = NULL; return; } - logwalk = logbuffer; + logwalk = logbuffer + sizeof(i3_shmlog_header); loglastwrap = logbuffer + logbuffer_size; + store_log_markers(); } } @@ -141,17 +157,6 @@ void add_loglevel(const char *level) { } } -/* - * Returns the offsets for the next write and for the last wrap. - * Necessary to print the i3 SHM log in the correct order. - * - */ -void get_log_markers(int *offset_next_write, int *offset_last_wrap, int *size) { - *offset_next_write = (logwalk - logbuffer); - *offset_last_wrap = (loglastwrap - logbuffer); - *size = logbuffer_size; -} - /* * Logs the given message to stdout (if print is true) while prefixing the * current time to it. Additionally, the message will be saved in the i3 SHM @@ -208,7 +213,7 @@ static void vlog(const bool print, const char *fmt, va_list args) { * beginning again. */ if ((len+1) >= (logbuffer_size - (logwalk - logbuffer))) { loglastwrap = logwalk; - logwalk = logbuffer; + logwalk = logbuffer + sizeof(i3_shmlog_header); } /* Copy the buffer, terminate it, move the write pointer to the byte after @@ -217,6 +222,8 @@ static void vlog(const bool print, const char *fmt, va_list args) { logwalk[len] = '\0'; logwalk += len + 1; + store_log_markers(); + if (print) fwrite(message, len, 1, stdout); } diff --git a/src/main.c b/src/main.c index 45cb162c..32073d22 100644 --- a/src/main.c +++ b/src/main.c @@ -335,7 +335,7 @@ int main(int argc, char *argv[]) { break; } else if (strcmp(long_options[option_index].name, "get-socketpath") == 0 || strcmp(long_options[option_index].name, "get_socketpath") == 0) { - char *socket_path = socket_path_from_x11(); + char *socket_path = root_atom_contents("I3_SOCKET_PATH"); if (socket_path) { printf("%s\n", socket_path); return 0; @@ -416,7 +416,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; diff --git a/src/x.c b/src/x.c index 6c5a11ec..e5853fb5 100644 --- a/src/x.c +++ b/src/x.c @@ -928,6 +928,8 @@ void x_set_i3_atoms() { current_socketpath); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_CONFIG_PATH, A_UTF8_STRING, 8, strlen(current_configpath), current_configpath); + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_SHMLOG_PATH, A_UTF8_STRING, 8, + strlen(shmlogname), shmlogname); } /*