Merge branch 'logbuf' into next
This commit is contained in:
commit
6610215aaa
4
Makefile
4
Makefile
|
@ -18,7 +18,7 @@ else
|
||||||
UNUSED:=$(shell $(MAKE) loglevels.h)
|
UNUSED:=$(shell $(MAKE) loglevels.h)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
SUBDIRS:=i3-msg i3-input i3-nagbar i3-config-wizard i3bar
|
SUBDIRS:=i3-msg i3-input i3-nagbar i3-config-wizard i3bar i3-dump-log
|
||||||
|
|
||||||
# Depend on the specific file (.c for each .o) and on all headers
|
# Depend on the specific file (.c for each .o) and on all headers
|
||||||
src/%.o: src/%.c ${HEADERS}
|
src/%.o: src/%.c ${HEADERS}
|
||||||
|
@ -100,7 +100,7 @@ dist: distclean
|
||||||
[ ! -e i3-${VERSION}.tar.bz2 ] || rm i3-${VERSION}.tar.bz2
|
[ ! -e i3-${VERSION}.tar.bz2 ] || rm i3-${VERSION}.tar.bz2
|
||||||
mkdir i3-${VERSION}
|
mkdir i3-${VERSION}
|
||||||
cp i3-migrate-config-to-v4 i3-sensible-* i3.config.keycodes DEPENDS GOALS LICENSE PACKAGE-MAINTAINER RELEASE-NOTES-${VERSION} i3.config i3.desktop i3.welcome pseudo-doc.doxygen i3-wsbar Makefile i3-${VERSION}
|
cp i3-migrate-config-to-v4 i3-sensible-* i3.config.keycodes DEPENDS GOALS LICENSE PACKAGE-MAINTAINER RELEASE-NOTES-${VERSION} i3.config i3.desktop i3.welcome pseudo-doc.doxygen i3-wsbar Makefile i3-${VERSION}
|
||||||
cp -r src libi3 i3-msg i3-nagbar i3-config-wizard i3bar yajl-fallback include man i3-${VERSION}
|
cp -r src libi3 i3-msg i3-nagbar i3-config-wizard i3bar i3-dump-log yajl-fallback include man i3-${VERSION}
|
||||||
# Only copy toplevel documentation (important stuff)
|
# Only copy toplevel documentation (important stuff)
|
||||||
mkdir i3-${VERSION}/docs
|
mkdir i3-${VERSION}/docs
|
||||||
# Pre-generate documentation
|
# Pre-generate documentation
|
||||||
|
|
|
@ -68,6 +68,7 @@ CPPFLAGS += -DPCRE_HAS_UCP=1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LIBS += -lm
|
LIBS += -lm
|
||||||
|
LIBS += -lrt
|
||||||
LIBS += -L $(TOPDIR)/libi3 -li3
|
LIBS += -L $(TOPDIR)/libi3 -li3
|
||||||
LIBS += $(call ldflags_for_lib, xcb-event,xcb-event)
|
LIBS += $(call ldflags_for_lib, xcb-event,xcb-event)
|
||||||
LIBS += $(call ldflags_for_lib, xcb-keysyms,xcb-keysyms)
|
LIBS += $(call ldflags_for_lib, xcb-keysyms,xcb-keysyms)
|
||||||
|
|
60
docs/ipc
60
docs/ipc
|
@ -1,7 +1,7 @@
|
||||||
IPC interface (interprocess communication)
|
IPC interface (interprocess communication)
|
||||||
==========================================
|
==========================================
|
||||||
Michael Stapelberg <michael+i3@stapelberg.de>
|
Michael Stapelberg <michael+i3@stapelberg.de>
|
||||||
October 2011
|
December 2011
|
||||||
|
|
||||||
This document describes how to interface with i3 from a separate process. This
|
This document describes how to interface with i3 from a separate process. This
|
||||||
is useful for example to remote-control i3 (to write test cases for example) or
|
is useful for example to remote-control i3 (to write test cases for example) or
|
||||||
|
@ -68,6 +68,10 @@ GET_BAR_CONFIG (6)::
|
||||||
Gets the configuration (as JSON map) of the workspace bar with the
|
Gets the configuration (as JSON map) of the workspace bar with the
|
||||||
given ID. If no ID is provided, an array with all configured bar IDs is
|
given ID. If no ID is provided, an array with all configured bar IDs is
|
||||||
returned instead.
|
returned instead.
|
||||||
|
GET_LOG_MARKERS (7)::
|
||||||
|
Gets the SHM log markers for the current position, the last wrap, the
|
||||||
|
SHM segment name and segment size. This is necessary for tools like
|
||||||
|
i3-dump-log which want to display the SHM log.
|
||||||
|
|
||||||
So, a typical message could look like this:
|
So, a typical message could look like this:
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
@ -111,18 +115,20 @@ The following reply types are implemented:
|
||||||
|
|
||||||
COMMAND (0)::
|
COMMAND (0)::
|
||||||
Confirmation/Error code for the COMMAND message.
|
Confirmation/Error code for the COMMAND message.
|
||||||
GET_WORKSPACES (1)::
|
WORKSPACES (1)::
|
||||||
Reply to the GET_WORKSPACES message.
|
Reply to the GET_WORKSPACES message.
|
||||||
SUBSCRIBE (2)::
|
SUBSCRIBE (2)::
|
||||||
Confirmation/Error code for the SUBSCRIBE message.
|
Confirmation/Error code for the SUBSCRIBE message.
|
||||||
GET_OUTPUTS (3)::
|
OUTPUTS (3)::
|
||||||
Reply to the GET_OUTPUTS message.
|
Reply to the GET_OUTPUTS message.
|
||||||
GET_TREE (4)::
|
TREE (4)::
|
||||||
Reply to the GET_TREE message.
|
Reply to the GET_TREE message.
|
||||||
GET_MARKS (5)::
|
MARKS (5)::
|
||||||
Reply to the GET_MARKS message.
|
Reply to the GET_MARKS message.
|
||||||
GET_BAR_CONFIG (6)::
|
BAR_CONFIG (6)::
|
||||||
Reply to the GET_BAR_CONFIG message.
|
Reply to the GET_BAR_CONFIG message.
|
||||||
|
LOG_MARKERS (7)::
|
||||||
|
Reply to the GET_LOG_MARKERS message.
|
||||||
|
|
||||||
=== COMMAND reply
|
=== COMMAND reply
|
||||||
|
|
||||||
|
@ -134,7 +140,7 @@ property is +success (bool)+, but this will be expanded in future versions.
|
||||||
{ "success": true }
|
{ "success": true }
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
=== GET_WORKSPACES reply
|
=== WORKSPACES reply
|
||||||
|
|
||||||
The reply consists of a serialized list of workspaces. Each workspace has the
|
The reply consists of a serialized list of workspaces. Each workspace has the
|
||||||
following properties:
|
following properties:
|
||||||
|
@ -248,7 +254,7 @@ rect (map)::
|
||||||
]
|
]
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
=== GET_TREE reply
|
=== TREE reply
|
||||||
|
|
||||||
The reply consists of a serialized tree. Each node in the tree (representing
|
The reply consists of a serialized tree. Each node in the tree (representing
|
||||||
one container) has at least the properties listed below. While the nodes might
|
one container) has at least the properties listed below. While the nodes might
|
||||||
|
@ -431,7 +437,7 @@ JSON dump:
|
||||||
}
|
}
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
=== GET_MARKS reply
|
=== MARKS reply
|
||||||
|
|
||||||
The reply consists of a single array of strings for each container that has a
|
The reply consists of a single array of strings for each container that has a
|
||||||
mark. The order of that array is undefined. If more than one container has the
|
mark. The order of that array is undefined. If more than one container has the
|
||||||
|
@ -440,7 +446,7 @@ contents are not unique).
|
||||||
|
|
||||||
If no window has a mark the response will be the empty array [].
|
If no window has a mark the response will be the empty array [].
|
||||||
|
|
||||||
=== GET_BAR_CONFIG reply
|
=== BAR_CONFIG reply
|
||||||
|
|
||||||
This can be used by third-party workspace bars (especially i3bar, but others
|
This can be used by third-party workspace bars (especially i3bar, but others
|
||||||
are free to implement compatible alternatives) to get the +bar+ block
|
are free to implement compatible alternatives) to get the +bar+ block
|
||||||
|
@ -524,6 +530,40 @@ urgent_workspace_text/urgent_workspace_bar::
|
||||||
}
|
}
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
=== LOG_MARKERS reply
|
||||||
|
|
||||||
|
Gets the SHM log markers for the current position, the last wrap, the
|
||||||
|
SHM segment name and segment size. This is necessary for tools like
|
||||||
|
i3-dump-log which want to display the SHM log.
|
||||||
|
|
||||||
|
The reply is a JSON map with the following entries:
|
||||||
|
|
||||||
|
shmname (string)::
|
||||||
|
The name of the SHM segment, will be of the format +/i3-log-<pid>+.
|
||||||
|
size (integer)::
|
||||||
|
The size (in bytes) of the SHM segment. If this is 0, SHM logging is
|
||||||
|
disabled.
|
||||||
|
offset_next_write (integer)::
|
||||||
|
The offset in the SHM segment at which the next write will happen.
|
||||||
|
Tools should start printing lines from here, since the bytes following
|
||||||
|
this offset are the oldest log lines. However, the first line might be
|
||||||
|
garbled, so it makes sense to skip all bytes until the first \0.
|
||||||
|
offset_last_wrap (integer)::
|
||||||
|
The offset in the SHM segment at which the last wrap occured. i3 only
|
||||||
|
stores entire messages in the SHM log, so it might waste a few bytes at
|
||||||
|
the end to be more efficient. Tools should not print content after the
|
||||||
|
offset_last_wrap.
|
||||||
|
|
||||||
|
*Example*:
|
||||||
|
-----------------------------
|
||||||
|
{
|
||||||
|
"offset_next_write":132839,
|
||||||
|
"offset_last_wrap":26214400,
|
||||||
|
"shmname":"/i3-log-3392",
|
||||||
|
"size":26214400
|
||||||
|
}
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
== Events
|
== Events
|
||||||
|
|
||||||
[[events]]
|
[[events]]
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Default value so one can compile i3-dump-log standalone
|
||||||
|
TOPDIR=..
|
||||||
|
|
||||||
|
include $(TOPDIR)/common.mk
|
||||||
|
|
||||||
|
CFLAGS += -I$(TOPDIR)/include
|
||||||
|
|
||||||
|
# Depend on the object files of all source-files in src/*.c and on all header files
|
||||||
|
FILES=$(patsubst %.c,%.o,$(wildcard *.c))
|
||||||
|
HEADERS=$(wildcard *.h)
|
||||||
|
|
||||||
|
# Depend on the specific file (.c for each .o) and on all headers
|
||||||
|
%.o: %.c ${HEADERS}
|
||||||
|
echo "[i3-dump-log] CC $<"
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
all: i3-dump-log
|
||||||
|
|
||||||
|
i3-dump-log: ${FILES}
|
||||||
|
echo "[i3-dump-log] LINK i3-dump-log"
|
||||||
|
$(CC) $(LDFLAGS) -o i3-dump-log ${FILES} $(LIBS)
|
||||||
|
|
||||||
|
install: all
|
||||||
|
echo "[i3-dump-log] INSTALL"
|
||||||
|
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
|
||||||
|
$(INSTALL) -m 0755 i3-dump-log $(DESTDIR)$(PREFIX)/bin/
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
rm -f i3-dump-log
|
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
* vim:ts=4:sw=4:expandtab
|
||||||
|
*
|
||||||
|
* i3 - an improved dynamic tiling window manager
|
||||||
|
* © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
|
||||||
|
*
|
||||||
|
* i3-dump-log/main.c: Dumps the i3 SHM log to stdout.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "libi3.h"
|
||||||
|
#include <i3/ipc.h>
|
||||||
|
|
||||||
|
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'},
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
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') {
|
||||||
|
printf("i3-dump-log " I3_VERSION "\n");
|
||||||
|
return 0;
|
||||||
|
} else if (o == 'V') {
|
||||||
|
verbose = true;
|
||||||
|
} else if (o == 'h') {
|
||||||
|
printf("i3-dump-log " I3_VERSION "\n");
|
||||||
|
printf("i3-dump-log [-s <socket>]\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (*shmname == '\0')
|
||||||
|
errx(EXIT_FAILURE, "Cannot dump log: SHM logging is disabled in i3.");
|
||||||
|
|
||||||
|
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 (logbuffer == MAP_FAILED)
|
||||||
|
err(EXIT_FAILURE, "Could not mmap SHM segment for the i3 log");
|
||||||
|
|
||||||
|
int chars;
|
||||||
|
char *walk = logbuffer + 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)) {
|
||||||
|
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
|
||||||
|
* buffer, we directly exit the loop. */
|
||||||
|
if (*walk == '\0' && *(walk+1) == '\0')
|
||||||
|
break;
|
||||||
|
walk += (chars > 0 ? chars : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Then start from the beginning and print the newer lines */
|
||||||
|
walk = logbuffer;
|
||||||
|
while (walk < (logbuffer + offset_next_write)) {
|
||||||
|
chars = printf("%s", walk);
|
||||||
|
walk += (chars > 0 ? chars : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -73,9 +73,11 @@ int main(int argc, char *argv[]) {
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_MARKS;
|
message_type = I3_IPC_MESSAGE_TYPE_GET_MARKS;
|
||||||
else if (strcasecmp(optarg, "get_bar_config") == 0)
|
else if (strcasecmp(optarg, "get_bar_config") == 0)
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG;
|
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 {
|
else {
|
||||||
printf("Unknown message type\n");
|
printf("Unknown message type\n");
|
||||||
printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config\n");
|
printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_log_markers\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
} else if (o == 'q') {
|
} else if (o == 'q') {
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
* this before starting any other process, since we set RLIMIT_CORE to
|
* this before starting any other process, since we set RLIMIT_CORE to
|
||||||
* RLIM_INFINITY for i3 debugging versions. */
|
* RLIM_INFINITY for i3 debugging versions. */
|
||||||
extern struct rlimit original_rlimit_core;
|
extern struct rlimit original_rlimit_core;
|
||||||
|
/** Whether this version of i3 is a debug build or a release build. */
|
||||||
|
extern bool debug_build;
|
||||||
extern xcb_connection_t *conn;
|
extern xcb_connection_t *conn;
|
||||||
extern int conn_screen;
|
extern int conn_screen;
|
||||||
/** The last timestamp we got from X11 (timestamps are included in some events
|
/** The last timestamp we got from X11 (timestamps are included in some events
|
||||||
|
|
|
@ -40,6 +40,9 @@
|
||||||
/** Request the configuration for a specific 'bar' */
|
/** Request the configuration for a specific 'bar' */
|
||||||
#define I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG 6
|
#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
|
* Messages from i3 to clients
|
||||||
*
|
*
|
||||||
|
@ -66,6 +69,9 @@
|
||||||
/** Bar config reply type */
|
/** Bar config reply type */
|
||||||
#define I3_IPC_REPLY_TYPE_BAR_CONFIG 6
|
#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.
|
* Events from i3 to clients. Events have the first bit set high.
|
||||||
*
|
*
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
|
|
||||||
extern char *loglevels[];
|
extern char *loglevels[];
|
||||||
extern char *errorfilename;
|
extern char *errorfilename;
|
||||||
|
extern char *shmlogname;
|
||||||
|
extern int shmlog_size;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes logging by creating an error logfile in /tmp (or
|
* Initializes logging by creating an error logfile in /tmp (or
|
||||||
|
@ -35,6 +37,13 @@ void init_logging();
|
||||||
*/
|
*/
|
||||||
void add_loglevel(const char *level);
|
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
|
* 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
|
* be printed to stdout. If verbose is set to false, only errors will be
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
A2M:=a2x -f manpage --asciidoc-opts="-f asciidoc.conf"
|
A2M:=a2x -f manpage --asciidoc-opts="-f asciidoc.conf"
|
||||||
|
|
||||||
all: i3.1 i3-msg.1 i3-input.1 i3-nagbar.1 i3-wsbar.1 i3-config-wizard.1 i3-migrate-config-to-v4.1 i3-sensible-editor.1 i3-sensible-pager.1 i3-sensible-terminal.1
|
all: i3.1 i3-msg.1 i3-input.1 i3-nagbar.1 i3-wsbar.1 i3-config-wizard.1 i3-migrate-config-to-v4.1 i3-sensible-editor.1 i3-sensible-pager.1 i3-sensible-terminal.1 i3-dump-log.1
|
||||||
|
|
||||||
%.1: %.man asciidoc.conf
|
%.1: %.man asciidoc.conf
|
||||||
${A2M} $<
|
${A2M} $<
|
||||||
|
@ -9,7 +9,7 @@ i3-wsbar.1: ../i3-wsbar
|
||||||
pod2man $^ > $@
|
pod2man $^ > $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
for file in $$(echo i3 i3-msg i3-input i3-nagbar i3-wsbar i3-config-wizard i3-migrate-config-to-v4 i3-sensible-editor i3-sensible-pager i3-sensible-terminal); \
|
for file in $$(echo i3 i3-msg i3-input i3-nagbar i3-wsbar i3-config-wizard i3-migrate-config-to-v4 i3-sensible-editor i3-sensible-pager i3-sensible-terminal i3-dump-log); \
|
||||||
do \
|
do \
|
||||||
rm -f $${file}.1 $${file}.html $${file}.xml; \
|
rm -f $${file}.1 $${file}.html $${file}.xml; \
|
||||||
done
|
done
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
i3-dump-log(1)
|
||||||
|
==============
|
||||||
|
Michael Stapelberg <michael+i3@stapelberg.de>
|
||||||
|
v4.1, December 2011
|
||||||
|
|
||||||
|
== NAME
|
||||||
|
|
||||||
|
i3-dump-log - dumps the i3 SHM log
|
||||||
|
|
||||||
|
== SYNOPSIS
|
||||||
|
|
||||||
|
i3-dump-log [-s <socketpath>]
|
||||||
|
|
||||||
|
== DESCRIPTION
|
||||||
|
|
||||||
|
Debug versions of i3 automatically use 1% of your RAM (but 25 MiB max) to store
|
||||||
|
full debug loglevel log output. This is extremely helpful for bugreports and
|
||||||
|
figuring out what is going on, without permanently logging to a file.
|
||||||
|
|
||||||
|
With i3-dump-log, you can dump the SHM log to stdout.
|
||||||
|
|
||||||
|
== EXAMPLE
|
||||||
|
|
||||||
|
i3-dump-log | gzip -9 > /tmp/i3-log.gz
|
||||||
|
|
||||||
|
== SEE ALSO
|
||||||
|
|
||||||
|
i3(1)
|
||||||
|
|
||||||
|
== AUTHOR
|
||||||
|
|
||||||
|
Michael Stapelberg and contributors
|
47
src/ipc.c
47
src/ipc.c
|
@ -612,6 +612,48 @@ IPC_HANDLER(get_bar_config) {
|
||||||
y(free);
|
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).
|
* Callback for the YAJL parser (will be called when a string is parsed).
|
||||||
*
|
*
|
||||||
|
@ -697,14 +739,15 @@ IPC_HANDLER(subscribe) {
|
||||||
|
|
||||||
/* The index of each callback function corresponds to the numeric
|
/* The index of each callback function corresponds to the numeric
|
||||||
* value of the message type (see include/i3/ipc.h) */
|
* value of the message type (see include/i3/ipc.h) */
|
||||||
handler_t handlers[7] = {
|
handler_t handlers[8] = {
|
||||||
handle_command,
|
handle_command,
|
||||||
handle_get_workspaces,
|
handle_get_workspaces,
|
||||||
handle_subscribe,
|
handle_subscribe,
|
||||||
handle_get_outputs,
|
handle_get_outputs,
|
||||||
handle_tree,
|
handle_tree,
|
||||||
handle_get_marks,
|
handle_get_marks,
|
||||||
handle_get_bar_config
|
handle_get_bar_config,
|
||||||
|
handle_get_log_markers
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
159
src/log.c
159
src/log.c
|
@ -15,9 +15,14 @@
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "i3.h"
|
||||||
|
#include "libi3.h"
|
||||||
|
|
||||||
/* loglevels.h is autogenerated at make time */
|
/* loglevels.h is autogenerated at make time */
|
||||||
#include "loglevels.h"
|
#include "loglevels.h"
|
||||||
|
@ -27,21 +32,79 @@ static bool verbose = false;
|
||||||
static FILE *errorfile;
|
static FILE *errorfile;
|
||||||
char *errorfilename;
|
char *errorfilename;
|
||||||
|
|
||||||
|
/* SHM logging variables */
|
||||||
|
|
||||||
|
/* The name for the SHM (/i3-log-%pid). Will end up on /dev/shm on most
|
||||||
|
* systems. Global so that we can clean up at exit. */
|
||||||
|
char *shmlogname = "";
|
||||||
|
/* Size limit for the SHM log, by default 25 MiB. Can be overwritten using the
|
||||||
|
* flag --shmlog-size. */
|
||||||
|
int shmlog_size = 0;
|
||||||
|
/* If enabled, logbuffer will point to a memory mapping of the i3 SHM log. */
|
||||||
|
static char *logbuffer;
|
||||||
|
/* A pointer (within logbuffer) where data will be written to next. */
|
||||||
|
static char *logwalk;
|
||||||
|
/* A pointer to the byte where we last wrapped. Necessary to not print the
|
||||||
|
* left-overs at the end of the ringbuffer. */
|
||||||
|
static char *loglastwrap;
|
||||||
|
/* Size (in bytes) of the i3 SHM log. */
|
||||||
|
static int logbuffer_size;
|
||||||
|
/* File descriptor for shm_open. */
|
||||||
|
static int logbuffer_shm;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initializes logging by creating an error logfile in /tmp (or
|
* Initializes logging by creating an error logfile in /tmp (or
|
||||||
* XDG_RUNTIME_DIR, see get_process_filename()).
|
* XDG_RUNTIME_DIR, see get_process_filename()).
|
||||||
*
|
*
|
||||||
|
* Will be called twice if --shmlog-size is specified.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
void init_logging() {
|
void init_logging() {
|
||||||
errorfilename = get_process_filename("errorlog");
|
if (!errorfilename) {
|
||||||
if (errorfilename == NULL) {
|
if (!(errorfilename = get_process_filename("errorlog")))
|
||||||
ELOG("Could not initialize errorlog\n");
|
ELOG("Could not initialize errorlog\n");
|
||||||
return;
|
else {
|
||||||
|
errorfile = fopen(errorfilename, "w");
|
||||||
|
if (fcntl(fileno(errorfile), F_SETFD, FD_CLOEXEC)) {
|
||||||
|
ELOG("Could not set close-on-exec flag\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
errorfile = fopen(errorfilename, "w");
|
/* If this is a debug build (not a release version), we will enable SHM
|
||||||
if (fcntl(fileno(errorfile), F_SETFD, FD_CLOEXEC)) {
|
* logging by default, unless the user turned it off explicitly. */
|
||||||
ELOG("Could not set close-on-exec flag\n");
|
if (logbuffer == NULL && shmlog_size > 0) {
|
||||||
|
/* Reserve 1% of the RAM for the logfile, but at max 25 MiB.
|
||||||
|
* For 512 MiB of RAM this will lead to a 5 MiB log buffer.
|
||||||
|
* At the moment (2011-12-10), no testcase leads to an i3 log
|
||||||
|
* of more than ~ 600 KiB. */
|
||||||
|
long long physical_mem_bytes = (long long)sysconf(_SC_PHYS_PAGES) *
|
||||||
|
sysconf(_SC_PAGESIZE);
|
||||||
|
logbuffer_size = min(physical_mem_bytes * 0.01, shmlog_size);
|
||||||
|
sasprintf(&shmlogname, "/i3-log-%d", getpid());
|
||||||
|
logbuffer_shm = shm_open(shmlogname, O_RDWR | O_CREAT | O_TRUNC, S_IREAD | S_IWRITE);
|
||||||
|
if (logbuffer_shm == -1) {
|
||||||
|
ELOG("Could not shm_open SHM segment for the i3 log: %s\n", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ftruncate(logbuffer_shm, logbuffer_size) == -1) {
|
||||||
|
close(logbuffer_shm);
|
||||||
|
shm_unlink("/i3-log-");
|
||||||
|
ELOG("Could not ftruncate SHM segment for the i3 log: %s\n", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logbuffer = mmap(NULL, logbuffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, logbuffer_shm, 0);
|
||||||
|
if (logbuffer == MAP_FAILED) {
|
||||||
|
close(logbuffer_shm);
|
||||||
|
shm_unlink("/i3-log-");
|
||||||
|
ELOG("Could not mmap SHM segment for the i3 log: %s\n", strerror(errno));
|
||||||
|
logbuffer = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logwalk = logbuffer;
|
||||||
|
loglastwrap = logbuffer + logbuffer_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,28 +142,78 @@ void add_loglevel(const char *level) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Logs the given message to stdout while prefixing the current time to it.
|
* 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
|
||||||
|
* log if enabled.
|
||||||
* This is to be called by *LOG() which includes filename/linenumber/function.
|
* This is to be called by *LOG() which includes filename/linenumber/function.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void vlog(char *fmt, va_list args) {
|
static void vlog(const bool print, const char *fmt, va_list args) {
|
||||||
static char timebuf[64];
|
/* Precisely one page to not consume too much memory but to hold enough
|
||||||
|
* data to be useful. */
|
||||||
|
static char message[4096];
|
||||||
static struct tm result;
|
static struct tm result;
|
||||||
|
static time_t t;
|
||||||
|
static struct tm *tmp;
|
||||||
|
static size_t len;
|
||||||
|
|
||||||
/* Get current time */
|
/* Get current time */
|
||||||
time_t t = time(NULL);
|
t = time(NULL);
|
||||||
/* Convert time to local time (determined by the locale) */
|
/* Convert time to local time (determined by the locale) */
|
||||||
struct tm *tmp = localtime_r(&t, &result);
|
tmp = localtime_r(&t, &result);
|
||||||
/* Generate time prefix */
|
/* Generate time prefix */
|
||||||
strftime(timebuf, sizeof(timebuf), "%x %X - ", tmp);
|
len = strftime(message, sizeof(message), "%x %X - ", tmp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* logbuffer print
|
||||||
|
* ----------------
|
||||||
|
* true true format message, save, print
|
||||||
|
* true false format message, save
|
||||||
|
* false true print message only
|
||||||
|
* false false INVALID, never called
|
||||||
|
*/
|
||||||
|
if (!logbuffer) {
|
||||||
#ifdef DEBUG_TIMING
|
#ifdef DEBUG_TIMING
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
gettimeofday(&tv, NULL);
|
gettimeofday(&tv, NULL);
|
||||||
printf("%s%d.%d - ", timebuf, tv.tv_sec, tv.tv_usec);
|
printf("%s%d.%d - ", message, tv.tv_sec, tv.tv_usec);
|
||||||
#else
|
#else
|
||||||
printf("%s", timebuf);
|
printf("%s", message);
|
||||||
#endif
|
#endif
|
||||||
vprintf(fmt, args);
|
vprintf(fmt, args);
|
||||||
|
} else {
|
||||||
|
len += vsnprintf(message + len, sizeof(message) - len, fmt, args);
|
||||||
|
if (len == sizeof(message)) {
|
||||||
|
fprintf(stderr, "BUG: single log message > 4k\n");
|
||||||
|
}
|
||||||
|
/* If there is no space for the current message (plus trailing
|
||||||
|
* nullbyte) in the ringbuffer, we need to wrap and write to the
|
||||||
|
* beginning again. */
|
||||||
|
if ((len+1) >= (logbuffer_size - (logwalk - logbuffer))) {
|
||||||
|
loglastwrap = logwalk;
|
||||||
|
logwalk = logbuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy the buffer, terminate it, move the write pointer to the byte after
|
||||||
|
* our current message. */
|
||||||
|
strncpy(logwalk, message, len);
|
||||||
|
logwalk[len] = '\0';
|
||||||
|
logwalk += len + 1;
|
||||||
|
|
||||||
|
if (print)
|
||||||
|
fwrite(message, len, 1, stdout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -111,11 +224,11 @@ void vlog(char *fmt, va_list args) {
|
||||||
void verboselog(char *fmt, ...) {
|
void verboselog(char *fmt, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
if (!verbose)
|
if (!logbuffer && !verbose)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
vlog(fmt, args);
|
vlog(verbose, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +240,7 @@ void errorlog(char *fmt, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
vlog(fmt, args);
|
vlog(true, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
/* also log to the error logfile, if opened */
|
/* also log to the error logfile, if opened */
|
||||||
|
@ -146,10 +259,10 @@ void errorlog(char *fmt, ...) {
|
||||||
void debuglog(uint64_t lev, char *fmt, ...) {
|
void debuglog(uint64_t lev, char *fmt, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
if ((loglevel & lev) == 0)
|
if (!logbuffer && !(loglevel & lev))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
vlog(fmt, args);
|
vlog((loglevel & lev), fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
90
src/main.c
90
src/main.c
|
@ -14,6 +14,8 @@
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include "all.h"
|
#include "all.h"
|
||||||
|
|
||||||
#include "sd-daemon.h"
|
#include "sd-daemon.h"
|
||||||
|
@ -23,6 +25,9 @@
|
||||||
* RLIM_INFINITY for i3 debugging versions. */
|
* RLIM_INFINITY for i3 debugging versions. */
|
||||||
struct rlimit original_rlimit_core;
|
struct rlimit original_rlimit_core;
|
||||||
|
|
||||||
|
/* Whether this version of i3 is a debug build or a release build. */
|
||||||
|
bool debug_build = false;
|
||||||
|
|
||||||
static int xkb_event_base;
|
static int xkb_event_base;
|
||||||
|
|
||||||
int xkb_current_group;
|
int xkb_current_group;
|
||||||
|
@ -205,6 +210,28 @@ static void i3_exit() {
|
||||||
#if EV_VERSION_MAJOR >= 4
|
#if EV_VERSION_MAJOR >= 4
|
||||||
ev_loop_destroy(main_loop);
|
ev_loop_destroy(main_loop);
|
||||||
#endif
|
#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[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
@ -224,6 +251,8 @@ int main(int argc, char *argv[]) {
|
||||||
{"force-xinerama", no_argument, 0, 0},
|
{"force-xinerama", no_argument, 0, 0},
|
||||||
{"force_xinerama", no_argument, 0, 0},
|
{"force_xinerama", no_argument, 0, 0},
|
||||||
{"disable-signalhandler", 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},
|
||||||
{"get_socketpath", no_argument, 0, 0},
|
{"get_socketpath", no_argument, 0, 0},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
|
@ -242,8 +271,21 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
srand(time(NULL));
|
srand(time(NULL));
|
||||||
|
|
||||||
|
/* Init logging *before* initializing debug_build to guarantee early
|
||||||
|
* (file) logging. */
|
||||||
init_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;
|
start_argv = argv;
|
||||||
|
|
||||||
while ((opt = getopt_long(argc, argv, "c:CvaL:hld:V", long_options, &option_index)) != -1) {
|
while ((opt = getopt_long(argc, argv, "c:CvaL:hld:V", long_options, &option_index)) != -1) {
|
||||||
|
@ -300,6 +342,14 @@ int main(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
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) {
|
} else if (strcmp(long_options[option_index].name, "restart") == 0) {
|
||||||
FREE(layout_path);
|
FREE(layout_path);
|
||||||
layout_path = sstrdup(optarg);
|
layout_path = sstrdup(optarg);
|
||||||
|
@ -326,6 +376,11 @@ int main(int argc, char *argv[]) {
|
||||||
fprintf(stderr, "\t--get-socketpath\n"
|
fprintf(stderr, "\t--get-socketpath\n"
|
||||||
"\tRetrieve the i3 IPC socket path from X11, print it, then exit.\n");
|
"\tRetrieve the i3 IPC socket path from X11, print it, then exit.\n");
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
|
fprintf(stderr, "\t--shmlog-size <limit>\n"
|
||||||
|
"\tLimits the size of the i3 SHM log to <limit> 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"
|
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"
|
"to send to a currently running i3 (like i3-msg). This allows you to\n"
|
||||||
"use nice and logical commands, such as:\n"
|
"use nice and logical commands, such as:\n"
|
||||||
|
@ -395,13 +450,11 @@ int main(int argc, char *argv[]) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* I3_VERSION contains either something like this:
|
/* Enable logging to handle the case when the user did not specify --shmlog-size */
|
||||||
* "4.0.2 (2011-11-11, branch "release")".
|
init_logging();
|
||||||
* or: "4.0.2-123-gCOFFEEBABE (2011-11-11, branch "next")".
|
|
||||||
*
|
/* Try to enable core dumps by default when running a debug build */
|
||||||
* So we check for the offset of the first opening round bracket to
|
if (debug_build) {
|
||||||
* determine whether this is a git version or a release version. */
|
|
||||||
if ((strchr(I3_VERSION, '(') - I3_VERSION) > 10) {
|
|
||||||
struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY };
|
struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY };
|
||||||
setrlimit(RLIMIT_CORE, &limit);
|
setrlimit(RLIMIT_CORE, &limit);
|
||||||
|
|
||||||
|
@ -662,8 +715,31 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
manage_existing_windows(root);
|
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)
|
if (!disable_signalhandler)
|
||||||
setup_signal_handler();
|
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
|
/* Ignore SIGPIPE to survive errors when an IPC client disconnects
|
||||||
* while we are sending him a message */
|
* while we are sending him a message */
|
||||||
|
|
|
@ -199,8 +199,11 @@ void setup_signal_handler() {
|
||||||
action.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
|
action.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
|
||||||
sigemptyset(&action.sa_mask);
|
sigemptyset(&action.sa_mask);
|
||||||
|
|
||||||
if (sigaction(SIGSEGV, &action, NULL) == -1 ||
|
/* 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(SIGABRT, &action, NULL) == -1 ||
|
||||||
sigaction(SIGFPE, &action, NULL) == -1)
|
sigaction(SIGFPE, &action, NULL) == -1 ||
|
||||||
|
sigaction(SIGSEGV, &action, NULL) == -1)
|
||||||
ELOG("Could not setup signal handler");
|
ELOG("Could not setup signal handler");
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,8 @@ sub activate_i3 {
|
||||||
|
|
||||||
# Construct the command to launch i3. Use maximum debug level, disable
|
# Construct the command to launch i3. Use maximum debug level, disable
|
||||||
# the interactive signalhandler to make it crash immediately instead.
|
# the interactive signalhandler to make it crash immediately instead.
|
||||||
my $i3cmd = abs_path("../i3") . " -V -d all --disable-signalhandler";
|
# Also disable logging to SHM since we want to redirect the logs anyways.
|
||||||
|
my $i3cmd = abs_path("../i3") . " -V -d all --disable-signalhandler --shmlog-size=0";
|
||||||
|
|
||||||
# For convenience:
|
# For convenience:
|
||||||
my $outdir = $args{outdir};
|
my $outdir = $args{outdir};
|
||||||
|
|
Loading…
Reference in New Issue