lighttpd 1.4.x https://www.lighttpd.net/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1170 lines
34 KiB

fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
7 years ago
fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
7 years ago
fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
7 years ago
fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
7 years ago
fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
7 years ago
fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
7 years ago
fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
7 years ago
fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
7 years ago
  1. #include "first.h"
  2. #include "base.h"
  3. #include "fdevent.h"
  4. #include "log.h"
  5. #include "buffer.h"
  6. #include "sock_addr.h"
  7. #include "plugin.h"
  8. #include <sys/types.h>
  9. #include <sys/stat.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <fcntl.h>
  13. #include <unistd.h>
  14. #include <errno.h>
  15. #include <time.h>
  16. #ifdef HAVE_SYSLOG_H
  17. # include <syslog.h>
  18. #endif
  19. typedef struct {
  20. char key;
  21. enum {
  22. FORMAT_UNSET,
  23. FORMAT_UNSUPPORTED,
  24. FORMAT_PERCENT,
  25. FORMAT_REMOTE_HOST,
  26. FORMAT_REMOTE_IDENT,
  27. FORMAT_REMOTE_USER,
  28. FORMAT_TIMESTAMP,
  29. FORMAT_REQUEST_LINE,
  30. FORMAT_STATUS,
  31. FORMAT_BYTES_OUT_NO_HEADER,
  32. FORMAT_HEADER,
  33. FORMAT_REMOTE_ADDR,
  34. FORMAT_LOCAL_ADDR,
  35. FORMAT_COOKIE,
  36. FORMAT_TIME_USED_US,
  37. FORMAT_ENV,
  38. FORMAT_FILENAME,
  39. FORMAT_REQUEST_PROTOCOL,
  40. FORMAT_REQUEST_METHOD,
  41. FORMAT_SERVER_PORT,
  42. FORMAT_QUERY_STRING,
  43. FORMAT_TIME_USED,
  44. FORMAT_URL,
  45. FORMAT_SERVER_NAME,
  46. FORMAT_HTTP_HOST,
  47. FORMAT_CONNECTION_STATUS,
  48. FORMAT_BYTES_IN,
  49. FORMAT_BYTES_OUT,
  50. FORMAT_KEEPALIVE_COUNT,
  51. FORMAT_RESPONSE_HEADER,
  52. FORMAT_NOTE
  53. } type;
  54. } format_mapping;
  55. /**
  56. *
  57. *
  58. * "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
  59. *
  60. */
  61. static const format_mapping fmap[] =
  62. {
  63. { '%', FORMAT_PERCENT },
  64. { 'h', FORMAT_REMOTE_HOST },
  65. { 'l', FORMAT_REMOTE_IDENT },
  66. { 'u', FORMAT_REMOTE_USER },
  67. { 't', FORMAT_TIMESTAMP },
  68. { 'r', FORMAT_REQUEST_LINE },
  69. { 's', FORMAT_STATUS },
  70. { 'b', FORMAT_BYTES_OUT_NO_HEADER },
  71. { 'i', FORMAT_HEADER },
  72. { 'a', FORMAT_REMOTE_ADDR },
  73. { 'A', FORMAT_LOCAL_ADDR },
  74. { 'B', FORMAT_BYTES_OUT_NO_HEADER },
  75. { 'C', FORMAT_COOKIE },
  76. { 'D', FORMAT_TIME_USED_US },
  77. { 'e', FORMAT_ENV },
  78. { 'f', FORMAT_FILENAME },
  79. { 'H', FORMAT_REQUEST_PROTOCOL },
  80. { 'k', FORMAT_KEEPALIVE_COUNT },
  81. { 'm', FORMAT_REQUEST_METHOD },
  82. { 'n', FORMAT_NOTE },
  83. { 'p', FORMAT_SERVER_PORT },
  84. { 'P', FORMAT_UNSUPPORTED }, /* we are only one process */
  85. { 'q', FORMAT_QUERY_STRING },
  86. { 'T', FORMAT_TIME_USED },
  87. { 'U', FORMAT_URL }, /* w/o querystring */
  88. { 'v', FORMAT_SERVER_NAME },
  89. { 'V', FORMAT_HTTP_HOST },
  90. { 'X', FORMAT_CONNECTION_STATUS },
  91. { 'I', FORMAT_BYTES_IN },
  92. { 'O', FORMAT_BYTES_OUT },
  93. { 'o', FORMAT_RESPONSE_HEADER },
  94. { '\0', FORMAT_UNSET }
  95. };
  96. enum e_optflags_time {
  97. /* format string is passed to strftime unless other format optflags set
  98. * (besides FORMAT_FLAG_TIME_BEGIN or FORMAT_FLAG_TIME_END) */
  99. FORMAT_FLAG_TIME_END = 0x00,/* use request end time (default) */
  100. FORMAT_FLAG_TIME_BEGIN = 0x01,/* use request start time */
  101. FORMAT_FLAG_TIME_SEC = 0x02,/* request time as num sec since epoch */
  102. FORMAT_FLAG_TIME_MSEC = 0x04,/* request time as num msec since epoch */
  103. FORMAT_FLAG_TIME_USEC = 0x08,/* request time as num usec since epoch */
  104. FORMAT_FLAG_TIME_NSEC = 0x10,/* request time as num nsec since epoch */
  105. FORMAT_FLAG_TIME_MSEC_FRAC = 0x20,/* request time msec fraction */
  106. FORMAT_FLAG_TIME_USEC_FRAC = 0x40,/* request time usec fraction */
  107. FORMAT_FLAG_TIME_NSEC_FRAC = 0x80 /* request time nsec fraction */
  108. };
  109. enum e_optflags_port {
  110. FORMAT_FLAG_PORT_LOCAL = 0x01,/* (default) */
  111. FORMAT_FLAG_PORT_REMOTE = 0x02
  112. };
  113. typedef struct {
  114. enum { FIELD_UNSET, FIELD_STRING, FIELD_FORMAT } type;
  115. buffer *string;
  116. int field;
  117. int opt;
  118. } format_field;
  119. typedef struct {
  120. format_field **ptr;
  121. size_t used;
  122. size_t size;
  123. } format_fields;
  124. typedef struct {
  125. buffer *access_logfile;
  126. int log_access_fd;
  127. buffer *access_logbuffer; /* each logfile has a separate buffer */
  128. unsigned short use_syslog; /* syslog has global buffer */
  129. unsigned short syslog_level;
  130. buffer *format;
  131. time_t last_generated_accesslog_ts;
  132. time_t *last_generated_accesslog_ts_ptr;
  133. buffer *ts_accesslog_str;
  134. format_fields *parsed_format;
  135. } plugin_config;
  136. typedef struct {
  137. PLUGIN_DATA;
  138. plugin_config **config_storage;
  139. plugin_config conf;
  140. buffer *syslog_logbuffer; /* syslog has global buffer. no caching, always written directly */
  141. } plugin_data;
  142. INIT_FUNC(mod_accesslog_init) {
  143. plugin_data *p;
  144. p = calloc(1, sizeof(*p));
  145. p->syslog_logbuffer = buffer_init();
  146. return p;
  147. }
  148. static void accesslog_write_all(server *srv, const buffer *filename, int fd, const void* buf, size_t count) {
  149. if (-1 == write_all(fd, buf, count)) {
  150. log_error_write(srv, __FILE__, __LINE__, "sbs",
  151. "writing access log entry failed:", filename, strerror(errno));
  152. }
  153. }
  154. static void accesslog_append_escaped(buffer *dest, buffer *str) {
  155. char *ptr, *start, *end;
  156. /* replaces non-printable chars with \xHH where HH is the hex representation of the byte */
  157. /* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */
  158. if (buffer_string_is_empty(str)) return;
  159. buffer_string_prepare_append(dest, buffer_string_length(str));
  160. for (ptr = start = str->ptr, end = str->ptr + buffer_string_length(str); ptr < end; ptr++) {
  161. unsigned char const c = (unsigned char) *ptr;
  162. if (c >= ' ' && c <= '~' && c != '"' && c != '\\') {
  163. /* nothing to change, add later as one block */
  164. } else {
  165. /* copy previous part */
  166. if (start < ptr) {
  167. buffer_append_string_len(dest, start, ptr - start);
  168. }
  169. start = ptr + 1;
  170. switch (c) {
  171. case '"':
  172. BUFFER_APPEND_STRING_CONST(dest, "\\\"");
  173. break;
  174. case '\\':
  175. BUFFER_APPEND_STRING_CONST(dest, "\\\\");
  176. break;
  177. case '\b':
  178. BUFFER_APPEND_STRING_CONST(dest, "\\b");
  179. break;
  180. case '\n':
  181. BUFFER_APPEND_STRING_CONST(dest, "\\n");
  182. break;
  183. case '\r':
  184. BUFFER_APPEND_STRING_CONST(dest, "\\r");
  185. break;
  186. case '\t':
  187. BUFFER_APPEND_STRING_CONST(dest, "\\t");
  188. break;
  189. case '\v':
  190. BUFFER_APPEND_STRING_CONST(dest, "\\v");
  191. break;
  192. default: {
  193. /* non printable char => \xHH */
  194. char hh[5] = {'\\','x',0,0,0};
  195. char h = c / 16;
  196. hh[2] = (h > 9) ? (h - 10 + 'A') : (h + '0');
  197. h = c % 16;
  198. hh[3] = (h > 9) ? (h - 10 + 'A') : (h + '0');
  199. buffer_append_string_len(dest, &hh[0], 4);
  200. }
  201. break;
  202. }
  203. }
  204. }
  205. if (start < end) {
  206. buffer_append_string_len(dest, start, end - start);
  207. }
  208. }
  209. static int accesslog_parse_format(server *srv, format_fields *fields, buffer *format) {
  210. size_t i, j, k = 0, start = 0;
  211. if (buffer_is_empty(format)) return -1;
  212. for (i = 0; i < buffer_string_length(format); i++) {
  213. switch(format->ptr[i]) {
  214. case '%':
  215. if (i > 0 && start != i) {
  216. /* copy the string before this % */
  217. if (fields->size == 0) {
  218. fields->size = 16;
  219. fields->used = 0;
  220. fields->ptr = malloc(fields->size * sizeof(format_field * ));
  221. } else if (fields->used == fields->size) {
  222. fields->size += 16;
  223. fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
  224. }
  225. fields->ptr[fields->used] = malloc(sizeof(format_field));
  226. fields->ptr[fields->used]->type = FIELD_STRING;
  227. fields->ptr[fields->used]->string = buffer_init();
  228. buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start);
  229. fields->used++;
  230. }
  231. /* we need a new field */
  232. if (fields->size == 0) {
  233. fields->size = 16;
  234. fields->used = 0;
  235. fields->ptr = malloc(fields->size * sizeof(format_field * ));
  236. } else if (fields->used == fields->size) {
  237. fields->size += 16;
  238. fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
  239. }
  240. /* search for the terminating command */
  241. switch (format->ptr[i+1]) {
  242. case '>':
  243. case '<':
  244. /* after the } has to be a character */
  245. if (format->ptr[i+2] == '\0') {
  246. log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a format-specifier");
  247. return -1;
  248. }
  249. for (j = 0; fmap[j].key != '\0'; j++) {
  250. if (fmap[j].key != format->ptr[i+2]) continue;
  251. /* found key */
  252. fields->ptr[fields->used] = malloc(sizeof(format_field));
  253. fields->ptr[fields->used]->type = FIELD_FORMAT;
  254. fields->ptr[fields->used]->field = fmap[j].type;
  255. fields->ptr[fields->used]->string = NULL;
  256. fields->ptr[fields->used]->opt = 0;
  257. fields->used++;
  258. break;
  259. }
  260. if (fmap[j].key == '\0') {
  261. log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a valid format-specifier");
  262. return -1;
  263. }
  264. start = i + 3;
  265. i = start - 1; /* skip the string */
  266. break;
  267. case '{':
  268. /* go forward to } */
  269. for (k = i+2; k < buffer_string_length(format); k++) {
  270. if (format->ptr[k] == '}') break;
  271. }
  272. if (k == buffer_string_length(format)) {
  273. log_error_write(srv, __FILE__, __LINE__, "s", "%{ has to be terminated by a }");
  274. return -1;
  275. }
  276. /* after the } has to be a character */
  277. if (format->ptr[k+1] == '\0') {
  278. log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a format-specifier");
  279. return -1;
  280. }
  281. if (k == i + 2) {
  282. log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to contain a string");
  283. return -1;
  284. }
  285. for (j = 0; fmap[j].key != '\0'; j++) {
  286. if (fmap[j].key != format->ptr[k+1]) continue;
  287. /* found key */
  288. fields->ptr[fields->used] = malloc(sizeof(format_field));
  289. fields->ptr[fields->used]->type = FIELD_FORMAT;
  290. fields->ptr[fields->used]->field = fmap[j].type;
  291. fields->ptr[fields->used]->string = buffer_init();
  292. fields->ptr[fields->used]->opt = 0;
  293. buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + i + 2, k - (i + 2));
  294. fields->used++;
  295. break;
  296. }
  297. if (fmap[j].key == '\0') {
  298. log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a valid format-specifier");
  299. return -1;
  300. }
  301. start = k + 2;
  302. i = start - 1; /* skip the string */
  303. break;
  304. default:
  305. /* after the % has to be a character */
  306. if (format->ptr[i+1] == '\0') {
  307. log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a format-specifier");
  308. return -1;
  309. }
  310. for (j = 0; fmap[j].key != '\0'; j++) {
  311. if (fmap[j].key != format->ptr[i+1]) continue;
  312. /* found key */
  313. fields->ptr[fields->used] = malloc(sizeof(format_field));
  314. fields->ptr[fields->used]->type = FIELD_FORMAT;
  315. fields->ptr[fields->used]->field = fmap[j].type;
  316. fields->ptr[fields->used]->string = NULL;
  317. fields->ptr[fields->used]->opt = 0;
  318. fields->used++;
  319. break;
  320. }
  321. if (fmap[j].key == '\0') {
  322. log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a valid format-specifier");
  323. return -1;
  324. }
  325. start = i + 2;
  326. i = start - 1; /* skip the string */
  327. break;
  328. }
  329. break;
  330. }
  331. }
  332. if (start < i) {
  333. /* copy the string */
  334. if (fields->size == 0) {
  335. fields->size = 16;
  336. fields->used = 0;
  337. fields->ptr = malloc(fields->size * sizeof(format_field * ));
  338. } else if (fields->used == fields->size) {
  339. fields->size += 16;
  340. fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
  341. }
  342. fields->ptr[fields->used] = malloc(sizeof(format_field));
  343. fields->ptr[fields->used]->type = FIELD_STRING;
  344. fields->ptr[fields->used]->string = buffer_init();
  345. buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start);
  346. fields->used++;
  347. }
  348. return 0;
  349. }
  350. FREE_FUNC(mod_accesslog_free) {
  351. plugin_data *p = p_d;
  352. size_t i;
  353. if (!p) return HANDLER_GO_ON;
  354. if (p->config_storage) {
  355. for (i = 0; i < srv->config_context->used; i++) {
  356. plugin_config *s = p->config_storage[i];
  357. if (NULL == s) continue;
  358. if (!buffer_string_is_empty(s->access_logbuffer)) {
  359. if (s->log_access_fd != -1) {
  360. accesslog_write_all(srv, s->access_logfile, s->log_access_fd, CONST_BUF_LEN(s->access_logbuffer));
  361. }
  362. }
  363. if (s->log_access_fd != -1) {
  364. if (buffer_string_is_empty(s->access_logfile) || *s->access_logfile->ptr != '|') {
  365. close(s->log_access_fd);
  366. } /*(else piped loggers closed in fdevent_close_logger_pipes())*/
  367. }
  368. buffer_free(s->ts_accesslog_str);
  369. buffer_free(s->access_logbuffer);
  370. buffer_free(s->format);
  371. buffer_free(s->access_logfile);
  372. if (s->parsed_format) {
  373. size_t j;
  374. for (j = 0; j < s->parsed_format->used; j++) {
  375. if (s->parsed_format->ptr[j]->string) buffer_free(s->parsed_format->ptr[j]->string);
  376. free(s->parsed_format->ptr[j]);
  377. }
  378. free(s->parsed_format->ptr);
  379. free(s->parsed_format);
  380. }
  381. free(s);
  382. }
  383. free(p->config_storage);
  384. }
  385. if (p->syslog_logbuffer) buffer_free(p->syslog_logbuffer);
  386. free(p);
  387. return HANDLER_GO_ON;
  388. }
  389. SETDEFAULTS_FUNC(log_access_open) {
  390. plugin_data *p = p_d;
  391. size_t i = 0;
  392. config_values_t cv[] = {
  393. { "accesslog.filename", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  394. { "accesslog.use-syslog", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },
  395. { "accesslog.format", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  396. { "accesslog.syslog-level", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },
  397. { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
  398. };
  399. if (!p) return HANDLER_ERROR;
  400. p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
  401. for (i = 0; i < srv->config_context->used; i++) {
  402. data_config const* config = (data_config const*)srv->config_context->data[i];
  403. plugin_config *s;
  404. s = calloc(1, sizeof(plugin_config));
  405. s->access_logfile = buffer_init();
  406. s->format = buffer_init();
  407. s->access_logbuffer = buffer_init();
  408. s->ts_accesslog_str = buffer_init();
  409. s->log_access_fd = -1;
  410. s->last_generated_accesslog_ts = 0;
  411. s->last_generated_accesslog_ts_ptr = &(s->last_generated_accesslog_ts);
  412. s->syslog_level = LOG_INFO;
  413. cv[0].destination = s->access_logfile;
  414. cv[1].destination = &(s->use_syslog);
  415. cv[2].destination = s->format;
  416. cv[3].destination = &(s->syslog_level);
  417. p->config_storage[i] = s;
  418. if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
  419. return HANDLER_ERROR;
  420. }
  421. if (i == 0 && buffer_string_is_empty(s->format)) {
  422. /* set a default logfile string */
  423. buffer_copy_string_len(s->format, CONST_STR_LEN("%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""));
  424. }
  425. /* parse */
  426. if (!buffer_is_empty(s->format)) {
  427. size_t j, tcount = 0;
  428. s->parsed_format = calloc(1, sizeof(*(s->parsed_format)));
  429. if (-1 == accesslog_parse_format(srv, s->parsed_format, s->format)) {
  430. log_error_write(srv, __FILE__, __LINE__, "sb",
  431. "parsing accesslog-definition failed:", s->format);
  432. return HANDLER_ERROR;
  433. }
  434. for (j = 0; j < s->parsed_format->used; ++j) {
  435. format_field * const f = s->parsed_format->ptr[j];
  436. if (FIELD_FORMAT != f->type) continue;
  437. if (FORMAT_TIMESTAMP == f->field) {
  438. if (!buffer_string_is_empty(f->string)) {
  439. const char *ptr = f->string->ptr;
  440. if (0 == strncmp(ptr, "begin:", sizeof("begin:")-1)) {
  441. f->opt |= FORMAT_FLAG_TIME_BEGIN;
  442. ptr += sizeof("begin:")-1;
  443. } else if (0 == strncmp(ptr, "end:", sizeof("end:")-1)) {
  444. f->opt |= FORMAT_FLAG_TIME_END;
  445. ptr += sizeof("end:")-1;
  446. }
  447. if (0 == strcmp(ptr, "sec")) f->opt |= FORMAT_FLAG_TIME_SEC;
  448. else if (0 == strcmp(ptr, "msec")) f->opt |= FORMAT_FLAG_TIME_MSEC;
  449. else if (0 == strcmp(ptr, "usec")) f->opt |= FORMAT_FLAG_TIME_USEC;
  450. else if (0 == strcmp(ptr, "nsec")) f->opt |= FORMAT_FLAG_TIME_NSEC;
  451. else if (0 == strcmp(ptr, "msec_frac")) f->opt |= FORMAT_FLAG_TIME_MSEC_FRAC;
  452. else if (0 == strcmp(ptr, "usec_frac")) f->opt |= FORMAT_FLAG_TIME_USEC_FRAC;
  453. else if (0 == strcmp(ptr, "nsec_frac")) f->opt |= FORMAT_FLAG_TIME_NSEC_FRAC;
  454. else if (NULL == strchr(ptr, '%')) {
  455. log_error_write(srv, __FILE__, __LINE__, "sb",
  456. "constant string for time format (misspelled token? or missing '%'):", s->format);
  457. return HANDLER_ERROR;
  458. }
  459. }
  460. /* make sure they didn't try to send the timestamp in twice
  461. * (would invalidate s->ts_accesslog_str cache of timestamp str) */
  462. if (!(f->opt & ~(FORMAT_FLAG_TIME_BEGIN|FORMAT_FLAG_TIME_END|FORMAT_FLAG_TIME_SEC)) && ++tcount > 1) {
  463. log_error_write(srv, __FILE__, __LINE__, "sb",
  464. "you may not use strftime timestamp format %{}t twice in the same access log:", s->format);
  465. return HANDLER_ERROR;
  466. }
  467. if (f->opt & FORMAT_FLAG_TIME_BEGIN) srv->srvconf.high_precision_timestamps = 1;
  468. } else if (FORMAT_TIME_USED_US == f->field) {
  469. f->opt |= FORMAT_FLAG_TIME_USEC;
  470. srv->srvconf.high_precision_timestamps = 1;
  471. } else if (FORMAT_TIME_USED == f->field) {
  472. if (buffer_string_is_empty(f->string)
  473. || buffer_is_equal_string(f->string, CONST_STR_LEN("s"))
  474. || buffer_is_equal_string(f->string, CONST_STR_LEN("sec"))) f->opt |= FORMAT_FLAG_TIME_SEC;
  475. else if (buffer_is_equal_string(f->string, CONST_STR_LEN("ms"))
  476. || buffer_is_equal_string(f->string, CONST_STR_LEN("msec"))) f->opt |= FORMAT_FLAG_TIME_MSEC;
  477. else if (buffer_is_equal_string(f->string, CONST_STR_LEN("us"))
  478. || buffer_is_equal_string(f->string, CONST_STR_LEN("usec"))) f->opt |= FORMAT_FLAG_TIME_USEC;
  479. else if (buffer_is_equal_string(f->string, CONST_STR_LEN("ns"))
  480. || buffer_is_equal_string(f->string, CONST_STR_LEN("nsec"))) f->opt |= FORMAT_FLAG_TIME_NSEC;
  481. else {
  482. log_error_write(srv, __FILE__, __LINE__, "sb",
  483. "invalid time unit in %{UNIT}T:", s->format);
  484. return HANDLER_ERROR;
  485. }
  486. if (f->opt & ~(FORMAT_FLAG_TIME_SEC)) srv->srvconf.high_precision_timestamps = 1;
  487. } else if (FORMAT_COOKIE == f->field) {
  488. if (buffer_string_is_empty(f->string)) f->type = FIELD_STRING; /*(blank)*/
  489. } else if (FORMAT_SERVER_PORT == f->field) {
  490. if (buffer_string_is_empty(f->string))
  491. f->opt |= FORMAT_FLAG_PORT_LOCAL;
  492. else if (buffer_is_equal_string(f->string, CONST_STR_LEN("canonical")))
  493. f->opt |= FORMAT_FLAG_PORT_LOCAL;
  494. else if (buffer_is_equal_string(f->string, CONST_STR_LEN("local")))
  495. f->opt |= FORMAT_FLAG_PORT_LOCAL;
  496. else if (buffer_is_equal_string(f->string, CONST_STR_LEN("remote")))
  497. f->opt |= FORMAT_FLAG_PORT_REMOTE;
  498. else {
  499. log_error_write(srv, __FILE__, __LINE__, "sb",
  500. "invalid format %{canonical,local,remote}p:", s->format);
  501. return HANDLER_ERROR;
  502. }
  503. }
  504. }
  505. #if 0
  506. /* debugging */
  507. for (j = 0; j < s->parsed_format->used; j++) {
  508. switch (s->parsed_format->ptr[j]->type) {
  509. case FIELD_FORMAT:
  510. log_error_write(srv, __FILE__, __LINE__, "ssds",
  511. "config:", "format", s->parsed_format->ptr[j]->field,
  512. s->parsed_format->ptr[j]->string ?
  513. s->parsed_format->ptr[j]->string->ptr : "" );
  514. break;
  515. case FIELD_STRING:
  516. log_error_write(srv, __FILE__, __LINE__, "ssbs", "config:", "string '", s->parsed_format->ptr[j]->string, "'");
  517. break;
  518. default:
  519. break;
  520. }
  521. }
  522. #endif
  523. }
  524. if (s->use_syslog) {
  525. /* ignore the next checks */
  526. continue;
  527. }
  528. if (buffer_string_is_empty(s->access_logfile)) continue;
  529. if (srv->srvconf.preflight_check) continue;
  530. if (-1 == (s->log_access_fd = fdevent_open_logger(s->access_logfile->ptr))) {
  531. log_error_write(srv, __FILE__, __LINE__, "SBSS",
  532. "opening log '", s->access_logfile,
  533. "' failed: ", strerror(errno));
  534. return HANDLER_ERROR;
  535. }
  536. }
  537. return HANDLER_GO_ON;
  538. }
  539. static void log_access_flush(server *srv, void *p_d) {
  540. plugin_data *p = p_d;
  541. size_t i;
  542. for (i = 0; i < srv->config_context->used; i++) {
  543. plugin_config *s = p->config_storage[i];
  544. if (!buffer_string_is_empty(s->access_logbuffer)) {
  545. if (s->log_access_fd != -1) {
  546. accesslog_write_all(srv, s->access_logfile, s->log_access_fd, CONST_BUF_LEN(s->access_logbuffer));
  547. }
  548. buffer_reset(s->access_logbuffer);
  549. }
  550. }
  551. }
  552. TRIGGER_FUNC(log_access_periodic_flush) {
  553. /* flush buffered access logs every 4 seconds */
  554. if (0 == (srv->cur_ts & 3)) log_access_flush(srv, p_d);
  555. return HANDLER_GO_ON;
  556. }
  557. SIGHUP_FUNC(log_access_cycle) {
  558. plugin_data *p = p_d;
  559. size_t i;
  560. if (!p->config_storage) return HANDLER_GO_ON;
  561. log_access_flush(srv, p);
  562. for (i = 0; i < srv->config_context->used; i++) {
  563. plugin_config *s = p->config_storage[i];
  564. if (s->use_syslog == 0
  565. && !buffer_string_is_empty(s->access_logfile)
  566. && s->access_logfile->ptr[0] != '|') {
  567. if (-1 == fdevent_cycle_logger(s->access_logfile->ptr, &s->log_access_fd)) {
  568. log_error_write(srv, __FILE__, __LINE__, "ss", "cycling access-log failed:", strerror(errno));
  569. return HANDLER_ERROR;
  570. }
  571. }
  572. }
  573. return HANDLER_GO_ON;
  574. }
  575. #define PATCH(x) \
  576. p->conf.x = s->x;
  577. static int mod_accesslog_patch_connection(server *srv, connection *con, plugin_data *p) {
  578. size_t i, j;
  579. plugin_config *s = p->config_storage[0];
  580. PATCH(access_logfile);
  581. PATCH(log_access_fd);
  582. PATCH(last_generated_accesslog_ts_ptr);
  583. PATCH(access_logbuffer);
  584. PATCH(ts_accesslog_str);
  585. PATCH(parsed_format);
  586. PATCH(use_syslog);
  587. PATCH(syslog_level);
  588. /* skip the first, the global context */
  589. for (i = 1; i < srv->config_context->used; i++) {
  590. data_config *dc = (data_config *)srv->config_context->data[i];
  591. s = p->config_storage[i];
  592. /* condition didn't match */
  593. if (!config_check_cond(srv, con, dc)) continue;
  594. /* merge config */
  595. for (j = 0; j < dc->value->used; j++) {
  596. data_unset *du = dc->value->data[j];
  597. if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.filename"))) {
  598. PATCH(access_logfile);
  599. PATCH(log_access_fd);
  600. PATCH(access_logbuffer);
  601. } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.format"))) {
  602. PATCH(parsed_format);
  603. PATCH(last_generated_accesslog_ts_ptr);
  604. PATCH(ts_accesslog_str);
  605. } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.use-syslog"))) {
  606. PATCH(use_syslog);
  607. } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.syslog-level"))) {
  608. PATCH(syslog_level);
  609. }
  610. }
  611. }
  612. return 0;
  613. }
  614. #undef PATCH
  615. REQUESTDONE_FUNC(log_access_write) {
  616. plugin_data *p = p_d;
  617. buffer *b;
  618. size_t j;
  619. int newts = 0;
  620. data_string *ds;
  621. struct timespec ts = { 0, 0 };
  622. mod_accesslog_patch_connection(srv, con, p);
  623. /* No output device, nothing to do */
  624. if (!p->conf.use_syslog && p->conf.log_access_fd == -1) return HANDLER_GO_ON;
  625. if (p->conf.use_syslog) {
  626. b = p->syslog_logbuffer;
  627. } else {
  628. b = p->conf.access_logbuffer;
  629. }
  630. if (buffer_is_empty(b)) {
  631. buffer_string_set_length(b, 0);
  632. }
  633. for (j = 0; j < p->conf.parsed_format->used; j++) {
  634. const format_field * const f = p->conf.parsed_format->ptr[j];
  635. switch(f->type) {
  636. case FIELD_STRING:
  637. buffer_append_string_buffer(b, f->string);
  638. break;
  639. case FIELD_FORMAT:
  640. switch(f->field) {
  641. case FORMAT_TIMESTAMP:
  642. if (f->opt & ~(FORMAT_FLAG_TIME_BEGIN|FORMAT_FLAG_TIME_END)) {
  643. if (f->opt & FORMAT_FLAG_TIME_SEC) {
  644. time_t t = (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) ? srv->cur_ts : con->request_start;
  645. buffer_append_int(b, (intmax_t)t);
  646. } else if (f->opt & (FORMAT_FLAG_TIME_MSEC|FORMAT_FLAG_TIME_USEC|FORMAT_FLAG_TIME_NSEC)) {
  647. off_t t; /*(expected to be 64-bit since large file support enabled)*/
  648. long ns;
  649. if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) {
  650. if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts);
  651. t = (off_t)ts.tv_sec;
  652. ns = ts.tv_nsec;
  653. } else {
  654. t = (off_t)con->request_start_hp.tv_sec;
  655. ns = con->request_start_hp.tv_nsec;
  656. }
  657. if (f->opt & FORMAT_FLAG_TIME_MSEC) {
  658. t *= 1000;
  659. t += (ns + 999999) / 1000000; /* ceil */
  660. } else if (f->opt & FORMAT_FLAG_TIME_USEC) {
  661. t *= 1000000;
  662. t += (ns + 999) / 1000; /* ceil */
  663. } else {/*(f->opt & FORMAT_FLAG_TIME_NSEC)*/
  664. t *= 1000000000;
  665. t += ns;
  666. }
  667. buffer_append_int(b, (intmax_t)t);
  668. } else { /*(FORMAT_FLAG_TIME_MSEC_FRAC|FORMAT_FLAG_TIME_USEC_FRAC|FORMAT_FLAG_TIME_NSEC_FRAC)*/
  669. long ns;
  670. char *ptr;
  671. if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) {
  672. if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts);
  673. ns = ts.tv_nsec;
  674. } else {
  675. ns = con->request_start_hp.tv_nsec;
  676. }
  677. /*assert(t < 1000000000);*/
  678. if (f->opt & FORMAT_FLAG_TIME_MSEC_FRAC) {
  679. ns += 999999; /* ceil */
  680. ns /= 1000000;
  681. buffer_append_string_len(b, CONST_STR_LEN("000"));
  682. } else if (f->opt & FORMAT_FLAG_TIME_USEC_FRAC) {
  683. ns += 999; /* ceil */
  684. ns /= 1000;
  685. buffer_append_string_len(b, CONST_STR_LEN("000000"));
  686. } else {/*(f->opt & FORMAT_FLAG_TIME_NSEC_FRAC)*/
  687. buffer_append_string_len(b, CONST_STR_LEN("000000000"));
  688. }
  689. for (ptr = b->ptr + buffer_string_length(b); ns > 0; ns /= 10)
  690. *--ptr = (ns % 10) + '0';
  691. }
  692. } else if (!(f->opt & FORMAT_FLAG_TIME_BEGIN) && srv->cur_ts == *(p->conf.last_generated_accesslog_ts_ptr)) {
  693. buffer_append_string_buffer(b, p->conf.ts_accesslog_str);
  694. } else {
  695. /* cache the generated timestamp (only if ! FORMAT_FLAG_TIME_BEGIN) */
  696. struct tm *tmptr;
  697. time_t t;
  698. #if defined(HAVE_STRUCT_TM_GMTOFF)
  699. # ifdef HAVE_LOCALTIME_R
  700. struct tm tm;
  701. # endif /* HAVE_LOCALTIME_R */
  702. #else /* HAVE_STRUCT_TM_GMTOFF */
  703. # ifdef HAVE_GMTIME_R
  704. struct tm tm;
  705. # endif /* HAVE_GMTIME_R */
  706. #endif /* HAVE_STRUCT_TM_GMTOFF */
  707. if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) {
  708. t = *(p->conf.last_generated_accesslog_ts_ptr) = srv->cur_ts;
  709. newts = 1;
  710. } else {
  711. t = con->request_start;
  712. }
  713. #if defined(HAVE_STRUCT_TM_GMTOFF)
  714. # ifdef HAVE_LOCALTIME_R
  715. tmptr = localtime_r(&t, &tm);
  716. # else /* HAVE_LOCALTIME_R */
  717. tmptr = localtime(&t);
  718. # endif /* HAVE_LOCALTIME_R */
  719. #else /* HAVE_STRUCT_TM_GMTOFF */
  720. # ifdef HAVE_GMTIME_R
  721. tmptr = gmtime_r(&t, &tm);
  722. # else /* HAVE_GMTIME_R */
  723. tmptr = gmtime(&t);
  724. # endif /* HAVE_GMTIME_R */
  725. #endif /* HAVE_STRUCT_TM_GMTOFF */
  726. buffer_string_prepare_copy(p->conf.ts_accesslog_str, 255);
  727. if (buffer_string_is_empty(f->string)) {
  728. #if defined(HAVE_STRUCT_TM_GMTOFF)
  729. long scd, hrs, min;
  730. buffer_append_strftime(p->conf.ts_accesslog_str, "[%d/%b/%Y:%H:%M:%S ", tmptr);
  731. buffer_append_string_len(p->conf.ts_accesslog_str, tmptr->tm_gmtoff >= 0 ? "+" : "-", 1);
  732. scd = labs(tmptr->tm_gmtoff);
  733. hrs = scd / 3600;
  734. min = (scd % 3600) / 60;
  735. /* hours */
  736. if (hrs < 10) buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("0"));
  737. buffer_append_int(p->conf.ts_accesslog_str, hrs);
  738. if (min < 10) buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("0"));
  739. buffer_append_int(p->conf.ts_accesslog_str, min);
  740. buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("]"));
  741. #else
  742. buffer_append_strftime(p->conf.ts_accesslog_str, "[%d/%b/%Y:%H:%M:%S +0000]", tmptr);
  743. #endif /* HAVE_STRUCT_TM_GMTOFF */
  744. } else {
  745. buffer_append_strftime(p->conf.ts_accesslog_str, f->string->ptr, tmptr);
  746. }
  747. buffer_append_string_buffer(b, p->conf.ts_accesslog_str);
  748. }
  749. break;
  750. case FORMAT_TIME_USED:
  751. case FORMAT_TIME_USED_US:
  752. if (f->opt & FORMAT_FLAG_TIME_SEC) {
  753. buffer_append_int(b, srv->cur_ts - con->request_start);
  754. } else {
  755. const struct timespec * const bs = &con->request_start_hp;
  756. off_t tdiff; /*(expected to be 64-bit since large file support enabled)*/
  757. if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts);
  758. tdiff = (off_t)(ts.tv_sec - bs->tv_sec)*1000000000 + (ts.tv_nsec - bs->tv_nsec);
  759. if (tdiff <= 0) {
  760. /* sanity check for time moving backwards
  761. * (daylight savings adjustment or leap seconds or ?) */
  762. tdiff = -1;
  763. } else if (f->opt & FORMAT_FLAG_TIME_MSEC) {
  764. tdiff += 999999; /* ceil */
  765. tdiff /= 1000000;
  766. } else if (f->opt & FORMAT_FLAG_TIME_USEC) {
  767. tdiff += 999; /* ceil */
  768. tdiff /= 1000;
  769. } /* else (f->opt & FORMAT_FLAG_TIME_NSEC) */
  770. buffer_append_int(b, (intmax_t)tdiff);
  771. }
  772. break;
  773. case FORMAT_REMOTE_ADDR:
  774. case FORMAT_REMOTE_HOST:
  775. buffer_append_string_buffer(b, con->dst_addr_buf);
  776. break;
  777. case FORMAT_REMOTE_IDENT:
  778. /* ident */
  779. buffer_append_string_len(b, CONST_STR_LEN("-"));
  780. break;
  781. case FORMAT_REMOTE_USER:
  782. if (NULL != (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER")) && !buffer_string_is_empty(ds->value)) {
  783. accesslog_append_escaped(b, ds->value);
  784. } else {
  785. buffer_append_string_len(b, CONST_STR_LEN("-"));
  786. }
  787. break;
  788. case FORMAT_REQUEST_LINE:
  789. if (!buffer_string_is_empty(con->request.request_line)) {
  790. accesslog_append_escaped(b, con->request.request_line);
  791. }
  792. break;
  793. case FORMAT_STATUS:
  794. buffer_append_int(b, con->http_status);
  795. break;
  796. case FORMAT_BYTES_OUT_NO_HEADER:
  797. if (con->bytes_written > 0) {
  798. buffer_append_int(b,
  799. con->bytes_written - con->bytes_header <= 0 ? 0 : con->bytes_written - con->bytes_header);
  800. } else {
  801. buffer_append_string_len(b, CONST_STR_LEN("-"));
  802. }
  803. break;
  804. case FORMAT_HEADER:
  805. if (NULL != (ds = (data_string *)array_get_element_klen(con->request.headers, CONST_BUF_LEN(f->string)))) {
  806. accesslog_append_escaped(b, ds->value);
  807. } else {
  808. buffer_append_string_len(b, CONST_STR_LEN("-"));
  809. }
  810. break;
  811. case FORMAT_RESPONSE_HEADER:
  812. if (NULL != (ds = (data_string *)array_get_element_klen(con->response.headers, CONST_BUF_LEN(f->string)))) {
  813. accesslog_append_escaped(b, ds->value);
  814. } else {
  815. buffer_append_string_len(b, CONST_STR_LEN("-"));
  816. }
  817. break;
  818. case FORMAT_ENV:
  819. case FORMAT_NOTE:
  820. if (NULL != (ds = (data_string *)array_get_element_klen(con->environment, CONST_BUF_LEN(f->string)))) {
  821. accesslog_append_escaped(b, ds->value);
  822. } else {
  823. buffer_append_string_len(b, CONST_STR_LEN("-"));
  824. }
  825. break;
  826. case FORMAT_FILENAME:
  827. if (!buffer_string_is_empty(con->physical.path)) {
  828. buffer_append_string_buffer(b, con->physical.path);
  829. } else {
  830. buffer_append_string_len(b, CONST_STR_LEN("-"));
  831. }
  832. break;
  833. case FORMAT_BYTES_OUT:
  834. if (con->bytes_written > 0) {
  835. buffer_append_int(b, con->bytes_written);
  836. } else {
  837. buffer_append_string_len(b, CONST_STR_LEN("-"));
  838. }
  839. break;
  840. case FORMAT_BYTES_IN:
  841. if (con->bytes_read > 0) {
  842. buffer_append_int(b, con->bytes_read);
  843. } else {
  844. buffer_append_string_len(b, CONST_STR_LEN("-"));
  845. }
  846. break;
  847. case FORMAT_SERVER_NAME:
  848. if (!buffer_string_is_empty(con->server_name)) {
  849. buffer_append_string_buffer(b, con->server_name);
  850. } else {
  851. buffer_append_string_len(b, CONST_STR_LEN("-"));
  852. }
  853. break;
  854. case FORMAT_HTTP_HOST:
  855. if (!buffer_string_is_empty(con->uri.authority)) {
  856. accesslog_append_escaped(b, con->uri.authority);
  857. } else {
  858. buffer_append_string_len(b, CONST_STR_LEN("-"));
  859. }
  860. break;
  861. case FORMAT_REQUEST_PROTOCOL:
  862. buffer_append_string_len(b,
  863. con->request.http_version == HTTP_VERSION_1_1 ? "HTTP/1.1" : "HTTP/1.0", 8);
  864. break;
  865. case FORMAT_REQUEST_METHOD:
  866. buffer_append_string(b, get_http_method_name(con->request.http_method));
  867. break;
  868. case FORMAT_PERCENT:
  869. buffer_append_string_len(b, CONST_STR_LEN("%"));
  870. break;
  871. case FORMAT_LOCAL_ADDR:
  872. {
  873. /* (perf: not using getsockname() and inet_ntop_cache_get_ip())
  874. * (still useful if admin has configured explicit listen IPs) */
  875. const char *colon;
  876. buffer *srvtoken = con->srv_socket->srv_token;
  877. if (srvtoken->ptr[0] == '[') {
  878. colon = strstr(srvtoken->ptr, "]:");
  879. } else {
  880. colon = strchr(srvtoken->ptr, ':');
  881. }
  882. if (colon) {
  883. buffer_append_string_len(b, srvtoken->ptr, (size_t)(colon - srvtoken->ptr));
  884. } else {
  885. buffer_append_string_buffer(b, srvtoken);
  886. }
  887. }
  888. break;
  889. case FORMAT_SERVER_PORT:
  890. if (f->opt & FORMAT_FLAG_PORT_REMOTE) {
  891. buffer_append_int(b, sock_addr_get_port(&con->dst_addr));
  892. } else { /* if (f->opt & FORMAT_FLAG_PORT_LOCAL) *//*(default)*/
  893. const char *colon;
  894. buffer *srvtoken = ((server_socket*)(con->srv_socket))->srv_token;
  895. if (srvtoken->ptr[0] == '[') {
  896. colon = strstr(srvtoken->ptr, "]:");
  897. } else {
  898. colon = strchr(srvtoken->ptr, ':');
  899. }
  900. if (colon) {
  901. buffer_append_string(b, colon+1);
  902. } else {
  903. buffer_append_int(b, srv->srvconf.port);
  904. }
  905. }
  906. break;
  907. case FORMAT_QUERY_STRING:
  908. accesslog_append_escaped(b, con->uri.query);
  909. break;
  910. case FORMAT_URL:
  911. accesslog_append_escaped(b, con->uri.path_raw);
  912. break;
  913. case FORMAT_CONNECTION_STATUS:
  914. if (con->state == CON_STATE_RESPONSE_END) {
  915. if (0 == con->keep_alive) {
  916. buffer_append_string_len(b, CONST_STR_LEN("-"));
  917. } else {
  918. buffer_append_string_len(b, CONST_STR_LEN("+"));
  919. }
  920. } else { /* CON_STATE_ERROR */
  921. buffer_append_string_len(b, CONST_STR_LEN("X"));
  922. }
  923. break;
  924. case FORMAT_KEEPALIVE_COUNT:
  925. if (con->request_count > 1) {
  926. buffer_append_int(b, (intmax_t)(con->request_count-1));
  927. } else {
  928. buffer_append_string_len(b, CONST_STR_LEN("0"));
  929. }
  930. break;
  931. case FORMAT_COOKIE:
  932. if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Cookie"))) {
  933. char *str = ds->value->ptr;
  934. size_t len = buffer_string_length(f->string);
  935. do {
  936. while (*str == ' ' || *str == '\t') ++str;
  937. if (0 == strncmp(str, f->string->ptr, len) && str[len] == '=') {
  938. char *v = str+len+1;
  939. buffer *bstr;
  940. for (str = v; *str != '\0' && *str != ';'; ++str) ;
  941. if (str == v) break;
  942. do { --str; } while (str > v && (*str == ' ' || *str == '\t'));
  943. bstr = buffer_init();
  944. buffer_copy_string_len(bstr, v, str - v + 1);
  945. accesslog_append_escaped(b, bstr);
  946. buffer_free(bstr);
  947. break;
  948. } else {
  949. do { ++str; } while (*str != ' ' && *str != '\t' && *str != '\0');
  950. }
  951. while (*str == ' ' || *str == '\t') ++str;
  952. } while (*str++ == ';');
  953. }
  954. break;
  955. default:
  956. break;
  957. }
  958. break;
  959. default:
  960. break;
  961. }
  962. }
  963. if (p->conf.use_syslog) { /* syslog doesn't cache */
  964. #ifdef HAVE_SYSLOG_H
  965. if (!buffer_string_is_empty(b)) {
  966. /*(syslog appends a \n on its own)*/
  967. syslog(p->conf.syslog_level, "%s", b->ptr);
  968. buffer_reset(b);
  969. }
  970. #endif
  971. return HANDLER_GO_ON;
  972. }
  973. buffer_append_string_len(b, CONST_STR_LEN("\n"));
  974. if ((!buffer_string_is_empty(p->conf.access_logfile) && p->conf.access_logfile->ptr[0] == '|') || /* pipes don't cache */
  975. newts ||
  976. buffer_string_length(b) >= BUFFER_MAX_REUSE_SIZE) {
  977. if (p->conf.log_access_fd >= 0) {
  978. accesslog_write_all(srv, p->conf.access_logfile, p->conf.log_access_fd, CONST_BUF_LEN(b));
  979. }
  980. buffer_reset(b);
  981. }
  982. return HANDLER_GO_ON;
  983. }
  984. int mod_accesslog_plugin_init(plugin *p);
  985. int mod_accesslog_plugin_init(plugin *p) {
  986. p->version = LIGHTTPD_VERSION_ID;
  987. p->name = buffer_init_string("accesslog");
  988. p->init = mod_accesslog_init;
  989. p->set_defaults= log_access_open;
  990. p->cleanup = mod_accesslog_free;
  991. p->handle_request_done = log_access_write;
  992. p->handle_trigger = log_access_periodic_flush;
  993. p->handle_sighup = log_access_cycle;
  994. p->data = NULL;
  995. return 0;
  996. }