Implement shmlog command
Patch status: merged
Patch by Alexander Berntsen
Long description:
Add shmlog command that takes <size>|toggle|on|off. Separate logbuffer management into open_logbuffer() and close_logbuffer(). Make t/187-commands-parser.t expect 'shmlog'. Add update_shmlog_atom() to update the SHMLOG_PATH. Document the shmlog command in userguide.
To apply this patch, use:
curl http://cr.i3wm.org/patch/179/raw.patch | git am
b/docs/userguide
| 25 | @@ -1762,6 +1762,22 @@ stack-limit rows 5 | 
| 26 | image:stacklimit.png[Container limited to two columns] | 
| 27 | /////////////////////////////////////////////////////////////////////////////// | 
| 28 |  | 
| 29 | +== Starting/stopping/changing the size of the shm log | 
| 30 | + | 
| 31 | +You may start or stop the shm log with +shmlog+, or change the size of the log. | 
| 32 | +If you pass a size to the shmlog command, it will change the running log's | 
| 33 | +size, or, if the log is not running, start the log with the provided size. You | 
| 34 | +may also toggle the log. This is useful if you want to bind the command to a | 
| 35 | +key. | 
| 36 | + | 
| 37 | +*Examples*: | 
| 38 | +--------------- | 
| 39 | +shmlog 26214400 | 
| 40 | +shmlog toggle | 
| 41 | +shmlog on | 
| 42 | +shmlog off | 
| 43 | +--------------- | 
| 44 | + | 
| 45 | === Reloading/Restarting/Exiting | 
| 46 |  | 
| 47 | You can make i3 reload its configuration file with +reload+. You can also | 
b/include/commands.h
| 52 | @@ -271,4 +271,10 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name); | 
| 53 | */ | 
| 54 | void cmd_bar(I3_CMD, char *bar_type, char *bar_value, char *bar_id); | 
| 55 |  | 
| 56 | +/* | 
| 57 | + * Implementation of 'shmlog <size>|toggle|on|off' | 
| 58 | + * | 
| 59 | + */ | 
| 60 | +void cmd_shmlog(I3_CMD, char *argument); | 
| 61 | + | 
| 62 | #endif | 
b/include/log.h
| 67 | @@ -39,6 +39,18 @@ extern int shmlog_size; | 
| 68 | void init_logging(void); | 
| 69 |  | 
| 70 | /** | 
| 71 | + * Opens the logbuffer. | 
| 72 | + * | 
| 73 | + */ | 
| 74 | +void open_logbuffer(void); | 
| 75 | + | 
| 76 | +/** | 
| 77 | + * Closes the logbuffer. | 
| 78 | + * | 
| 79 | + */ | 
| 80 | +void close_logbuffer(void); | 
| 81 | + | 
| 82 | +/** | 
| 83 | * Set debug logging. | 
| 84 | * | 
| 85 | */ | 
b/include/x.h
| 90 | @@ -109,6 +109,12 @@ void x_raise_con(Con *con, bool above_all); | 
| 91 | void x_set_name(Con *con, const char *name); | 
| 92 |  | 
| 93 | /** | 
| 94 | + * Set up the SHMLOG_PATH atom. | 
| 95 | + * | 
| 96 | + */ | 
| 97 | +void update_shmlog_atom(void); | 
| 98 | + | 
| 99 | +/** | 
| 100 | * Sets up i3 specific atoms (I3_SOCKET_PATH and I3_CONFIG_PATH) | 
| 101 | * | 
| 102 | */ | 
b/parser-specs/commands.spec
| 107 | @@ -19,6 +19,7 @@ state INITIAL: | 
| 108 | 'exit' -> call cmd_exit() | 
| 109 | 'restart' -> call cmd_restart() | 
| 110 | 'reload' -> call cmd_reload() | 
| 111 | + 'shmlog' -> SHMLOG | 
| 112 | 'border' -> BORDER | 
| 113 | 'layout' -> LAYOUT | 
| 114 | 'append_layout' -> APPEND_LAYOUT | 
| 115 | @@ -62,6 +63,12 @@ state EXEC: | 
| 116 | command = string | 
| 117 | -> call cmd_exec($nosn, $command) | 
| 118 |  | 
| 119 | +# shmlog <size>|toggle|on|off | 
| 120 | +state SHMLOG: | 
| 121 | + # argument may be a number | 
| 122 | + argument = string | 
| 123 | + -> call cmd_shmlog($argument) | 
| 124 | + | 
| 125 | # border normal|none|1pixel|toggle|1pixel | 
| 126 | state BORDER: | 
| 127 | border_style = 'normal', 'pixel' | 
b/src/commands.c
| 132 | @@ -13,6 +13,7 @@ | 
| 133 | #include <stdarg.h> | 
| 134 |  | 
| 135 | #include "all.h" | 
| 136 | +#include "shmlog.h" | 
| 137 |  | 
| 138 | // Macros to make the YAJL API a bit easier to use. | 
| 139 | #define y(x, ...) yajl_gen_ ## x (cmd_output->json_gen, ##__VA_ARGS__) | 
| 140 | @@ -2027,3 +2028,34 @@ void cmd_bar(I3_CMD, char *bar_type, char *bar_value, char *bar_id) { | 
| 141 |  | 
| 142 | update_barconfig(); | 
| 143 | } | 
| 144 | + | 
| 145 | +/* | 
| 146 | + * Implementation of 'shmlog <size>|toggle|on|off' | 
| 147 | + * | 
| 148 | + */ | 
| 149 | +void cmd_shmlog(I3_CMD, char *argument) { | 
| 150 | + if (!strcmp(argument,"toggle")) | 
| 151 | + /* Toggle shm log, if size is not 0. If it is 0, set it to default. */ | 
| 152 | + shmlog_size = shmlog_size ? -shmlog_size : default_shmlog_size; | 
| 153 | + else if (!strcmp(argument, "on")) | 
| 154 | + shmlog_size = default_shmlog_size; | 
| 155 | + else if (!strcmp(argument, "off")) | 
| 156 | + shmlog_size = 0; | 
| 157 | +    else { | 
| 158 | + /* If shm logging now, restart logging with the new size. */ | 
| 159 | +        if (shmlog_size > 0) { | 
| 160 | + shmlog_size = 0; | 
| 161 | +            LOG("Restarting shm logging...\n"); | 
| 162 | + init_logging(); | 
| 163 | + } | 
| 164 | + shmlog_size = atoi(argument); | 
| 165 | + /* Make a weakly attempt at ensuring the argument is valid. */ | 
| 166 | + if (shmlog_size <= 0) | 
| 167 | + shmlog_size = default_shmlog_size; | 
| 168 | + } | 
| 169 | +    LOG("%s shm logging\n", shmlog_size > 0 ? "Enabling" : "Disabling"); | 
| 170 | + init_logging(); | 
| 171 | + update_shmlog_atom(); | 
| 172 | + // XXX: default reply for now, make this a better reply | 
| 173 | + ysuccess(true); | 
| 174 | +} | 
b/src/log.c
| 179 | @@ -89,10 +89,21 @@ void init_logging(void) { | 
| 180 | } | 
| 181 | } | 
| 182 | } | 
| 183 | + /* Start SHM logging if shmlog_size is > 0. shmlog_size is SHMLOG_SIZE by | 
| 184 | + * default on development versions, and 0 on release versions. If it is | 
| 185 | + * not > 0, the user has turned it off, so let's close the logbuffer. */ | 
| 186 | + if (shmlog_size > 0 && logbuffer == NULL) | 
| 187 | + open_logbuffer(); | 
| 188 | + else if (shmlog_size <= 0 && logbuffer) | 
| 189 | + close_logbuffer(); | 
| 190 | + atexit(purge_zerobyte_logfile); | 
| 191 | +} | 
| 192 |  | 
| 193 | - /* If this is a debug build (not a release version), we will enable SHM | 
| 194 | - * logging by default, unless the user turned it off explicitly. */ | 
| 195 | -    if (logbuffer == NULL && shmlog_size > 0) { | 
| 196 | +/* | 
| 197 | + * Opens the logbuffer. | 
| 198 | + * | 
| 199 | + */ | 
| 200 | +void open_logbuffer(void) { | 
| 201 | /* Reserve 1% of the RAM for the logfile, but at max 25 MiB. | 
| 202 | * For 512 MiB of RAM this will lead to a 5 MiB log buffer. | 
| 203 | * At the moment (2011-12-10), no testcase leads to an i3 log | 
| 204 | @@ -127,10 +138,8 @@ void init_logging(void) { | 
| 205 |  | 
| 206 | logbuffer = mmap(NULL, logbuffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, logbuffer_shm, 0); | 
| 207 |          if (logbuffer == MAP_FAILED) { | 
| 208 | - close(logbuffer_shm); | 
| 209 | - shm_unlink(shmlogname); | 
| 210 | + close_logbuffer(); | 
| 211 | fprintf(stderr, "Could not mmap SHM segment for the i3 log: %s\n", strerror(errno)); | 
| 212 | - logbuffer = NULL; | 
| 213 | return; | 
| 214 | } | 
| 215 |  | 
| 216 | @@ -148,8 +157,16 @@ void init_logging(void) { | 
| 217 | logwalk = logbuffer + sizeof(i3_shmlog_header); | 
| 218 | loglastwrap = logbuffer + logbuffer_size; | 
| 219 | store_log_markers(); | 
| 220 | - } | 
| 221 | - atexit(purge_zerobyte_logfile); | 
| 222 | +} | 
| 223 | + | 
| 224 | +/* | 
| 225 | + * Closes the logbuffer. | 
| 226 | + * | 
| 227 | + */ | 
| 228 | +void close_logbuffer(void) { | 
| 229 | + close(logbuffer_shm); | 
| 230 | + shm_unlink(shmlogname); | 
| 231 | + logbuffer = NULL; | 
| 232 | } | 
| 233 |  | 
| 234 | /* | 
b/src/x.c
| 239 | @@ -1064,6 +1064,16 @@ void x_set_name(Con *con, const char *name) { | 
| 240 | } | 
| 241 |  | 
| 242 | /* | 
| 243 | + * Set up the I3_SHMLOG_PATH atom. | 
| 244 | + * | 
| 245 | + */ | 
| 246 | +void update_shmlog_atom() { | 
| 247 | + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, | 
| 248 | + A_I3_SHMLOG_PATH, A_UTF8_STRING, 8, | 
| 249 | + strlen(shmlogname), shmlogname); | 
| 250 | +} | 
| 251 | + | 
| 252 | +/* | 
| 253 | * Sets up i3 specific atoms (I3_SOCKET_PATH and I3_CONFIG_PATH) | 
| 254 | * | 
| 255 | */ | 
| 256 | @@ -1075,8 +1085,7 @@ void x_set_i3_atoms(void) { | 
| 257 | xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_PID, XCB_ATOM_CARDINAL, 32, 1, &pid); | 
| 258 | xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_CONFIG_PATH, A_UTF8_STRING, 8, | 
| 259 | strlen(current_configpath), current_configpath); | 
| 260 | - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_SHMLOG_PATH, A_UTF8_STRING, 8, | 
| 261 | - strlen(shmlogname), shmlogname); | 
| 262 | + update_shmlog_atom(); | 
| 263 | } | 
| 264 |  | 
| 265 | /* | 
b/testcases/t/187-commands-parser.t
| 270 | @@ -144,7 +144,7 @@ is(parser_calls("\nworkspace test"), | 
| 271 | ################################################################################ | 
| 272 |  | 
| 273 |  is(parser_calls('unknown_literal'), | 
| 274 | - "ERROR: Expected one of these tokens: <end>, '[', 'move', 'exec', 'exit', 'restart', 'reload', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'split', 'floating', 'mark', 'resize', 'rename', 'nop', 'scratchpad', 'mode', 'bar'\n" . | 
| 275 | + "ERROR: Expected one of these tokens: <end>, '[', 'move', 'exec', 'exit', 'restart', 'reload', 'shmlog', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'split', 'floating', 'mark', 'resize', 'rename', 'nop', 'scratchpad', 'mode', 'bar'\n" . | 
| 276 | "ERROR: Your command: unknown_literal\n" . | 
| 277 | "ERROR: ^^^^^^^^^^^^^^^", | 
| 278 | 'error for unknown literal ok'); |