6 #include "stat_cache.h"
15 #include "inet_ntop_cache.h"
17 #include "sys-socket.h"
19 #include <sys/types.h>
34 # include <sys/wait.h>
37 #ifdef HAVE_SYS_FILIO_H
38 # include <sys/filio.h>
44 /* The newest modified time of included files for include statement */
45 static volatile time_t include_file_last_mtime
= 0;
47 /* init the plugin data */
48 INIT_FUNC(mod_ssi_init
) {
51 p
= calloc(1, sizeof(*p
));
53 p
->timefmt
= buffer_init();
54 p
->stat_fn
= buffer_init();
56 p
->ssi_vars
= array_init();
57 p
->ssi_cgi_env
= array_init();
62 /* detroy the plugin data */
63 FREE_FUNC(mod_ssi_free
) {
67 if (!p
) return HANDLER_GO_ON
;
69 if (p
->config_storage
) {
71 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
72 plugin_config
*s
= p
->config_storage
[i
];
74 if (NULL
== s
) continue;
76 array_free(s
->ssi_extension
);
77 buffer_free(s
->content_type
);
81 free(p
->config_storage
);
84 array_free(p
->ssi_vars
);
85 array_free(p
->ssi_cgi_env
);
87 pcre_free(p
->ssi_regex
);
89 buffer_free(p
->timefmt
);
90 buffer_free(p
->stat_fn
);
97 /* handle plugin config and check values */
99 SETDEFAULTS_FUNC(mod_ssi_set_defaults
) {
100 plugin_data
*p
= p_d
;
107 config_values_t cv
[] = {
108 { "ssi.extension", NULL
, T_CONFIG_ARRAY
, T_CONFIG_SCOPE_CONNECTION
}, /* 0 */
109 { "ssi.content-type", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 1 */
110 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
113 if (!p
) return HANDLER_ERROR
;
115 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
117 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
118 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
121 s
= calloc(1, sizeof(plugin_config
));
122 s
->ssi_extension
= array_init();
123 s
->content_type
= buffer_init();
125 cv
[0].destination
= s
->ssi_extension
;
126 cv
[1].destination
= s
->content_type
;
128 p
->config_storage
[i
] = s
;
130 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
131 return HANDLER_ERROR
;
137 if (NULL
== (p
->ssi_regex
= pcre_compile("<!--#([a-z]+)\\s+(?:([a-z]+)=\"(.*?)(?<!\\\\)\"\\s*)?(?:([a-z]+)=\"(.*?)(?<!\\\\)\"\\s*)?-->", 0, &errptr
, &erroff
, NULL
))) {
138 log_error_write(srv
, __FILE__
, __LINE__
, "sds",
141 return HANDLER_ERROR
;
144 log_error_write(srv
, __FILE__
, __LINE__
, "s",
145 "mod_ssi: pcre support is missing, please recompile with pcre support or remove mod_ssi from the list of modules");
146 return HANDLER_ERROR
;
149 return HANDLER_GO_ON
;
152 static int ssi_env_add(array
*env
, const char *key
, const char *val
) {
155 if (NULL
== (ds
= (data_string
*)array_get_unused_element(env
, TYPE_STRING
))) {
156 ds
= data_string_init();
158 buffer_copy_string(ds
->key
, key
);
159 buffer_copy_string(ds
->value
, val
);
161 array_insert_unique(env
, (data_unset
*)ds
);
168 * the next two functions are take from fcgi.c
172 static int ssi_env_add_request_headers(server
*srv
, connection
*con
, plugin_data
*p
) {
175 for (i
= 0; i
< con
->request
.headers
->used
; i
++) {
178 ds
= (data_string
*)con
->request
.headers
->data
[i
];
180 if (!buffer_is_empty(ds
->value
) && !buffer_is_empty(ds
->key
)) {
181 /* don't forward the Authorization: Header */
182 if (0 == strcasecmp(ds
->key
->ptr
, "AUTHORIZATION")) {
186 buffer_copy_string_encoded_cgi_varnames(srv
->tmp_buf
, CONST_BUF_LEN(ds
->key
), 1);
188 ssi_env_add(p
->ssi_cgi_env
, srv
->tmp_buf
->ptr
, ds
->value
->ptr
);
192 for (i
= 0; i
< con
->environment
->used
; i
++) {
195 ds
= (data_string
*)con
->environment
->data
[i
];
197 if (!buffer_is_empty(ds
->value
) && !buffer_is_empty(ds
->key
)) {
198 buffer_copy_string_encoded_cgi_varnames(srv
->tmp_buf
, CONST_BUF_LEN(ds
->key
), 0);
200 ssi_env_add(p
->ssi_cgi_env
, srv
->tmp_buf
->ptr
, ds
->value
->ptr
);
207 static int build_ssi_cgi_vars(server
*srv
, connection
*con
, plugin_data
*p
) {
208 char buf
[LI_ITOSTRING_LENGTH
];
210 server_socket
*srv_sock
= con
->srv_socket
;
213 char b2
[INET6_ADDRSTRLEN
+ 1];
216 #define CONST_STRING(x) \
219 array_reset(p
->ssi_cgi_env
);
221 ssi_env_add(p
->ssi_cgi_env
, CONST_STRING("SERVER_SOFTWARE"), PACKAGE_DESC
);
222 ssi_env_add(p
->ssi_cgi_env
, CONST_STRING("SERVER_NAME"),
224 inet_ntop(srv_sock
->addr
.plain
.sa_family
,
225 srv_sock
->addr
.plain
.sa_family
== AF_INET6
?
226 (const void *) &(srv_sock
->addr
.ipv6
.sin6_addr
) :
227 (const void *) &(srv_sock
->addr
.ipv4
.sin_addr
),
230 inet_ntoa(srv_sock
->addr
.ipv4
.sin_addr
)
233 ssi_env_add(p
->ssi_cgi_env
, CONST_STRING("GATEWAY_INTERFACE"), "CGI/1.1");
235 li_utostrn(buf
, sizeof(buf
),
237 ntohs(srv_sock
->addr
.plain
.sa_family
? srv_sock
->addr
.ipv6
.sin6_port
: srv_sock
->addr
.ipv4
.sin_port
)
239 ntohs(srv_sock
->addr
.ipv4
.sin_port
)
243 ssi_env_add(p
->ssi_cgi_env
, CONST_STRING("SERVER_PORT"), buf
);
245 ssi_env_add(p
->ssi_cgi_env
, CONST_STRING("REMOTE_ADDR"),
246 inet_ntop_cache_get_ip(srv
, &(con
->dst_addr
)));
248 if (con
->request
.content_length
> 0) {
249 /* CGI-SPEC 6.1.2 and FastCGI spec 6.3 */
251 li_itostrn(buf
, sizeof(buf
), con
->request
.content_length
);
252 ssi_env_add(p
->ssi_cgi_env
, CONST_STRING("CONTENT_LENGTH"), buf
);
256 * SCRIPT_NAME, PATH_INFO and PATH_TRANSLATED according to
257 * http://cgi-spec.golux.com/draft-coar-cgi-v11-03-clean.html
258 * (6.1.14, 6.1.6, 6.1.7)
261 ssi_env_add(p
->ssi_cgi_env
, CONST_STRING("SCRIPT_NAME"), con
->uri
.path
->ptr
);
262 ssi_env_add(p
->ssi_cgi_env
, CONST_STRING("PATH_INFO"), "");
265 * SCRIPT_FILENAME and DOCUMENT_ROOT for php. The PHP manual
266 * http://www.php.net/manual/en/reserved.variables.php
267 * treatment of PATH_TRANSLATED is different from the one of CGI specs.
268 * TODO: this code should be checked against cgi.fix_pathinfo php
272 if (!buffer_string_is_empty(con
->request
.pathinfo
)) {
273 ssi_env_add(p
->ssi_cgi_env
, CONST_STRING("PATH_INFO"), con
->request
.pathinfo
->ptr
);
276 ssi_env_add(p
->ssi_cgi_env
, CONST_STRING("SCRIPT_FILENAME"), con
->physical
.path
->ptr
);
277 ssi_env_add(p
->ssi_cgi_env
, CONST_STRING("DOCUMENT_ROOT"), con
->physical
.doc_root
->ptr
);
279 ssi_env_add(p
->ssi_cgi_env
, CONST_STRING("REQUEST_URI"), con
->request
.uri
->ptr
);
281 if (!buffer_string_is_empty(con
->uri
.scheme
)) {
282 ssi_env_add(p
->ssi_cgi_env
, CONST_STRING("REQUEST_SCHEME"), con
->uri
.scheme
->ptr
);
285 ssi_env_add(p
->ssi_cgi_env
, CONST_STRING("QUERY_STRING"), buffer_is_empty(con
->uri
.query
) ? "" : con
->uri
.query
->ptr
);
286 ssi_env_add(p
->ssi_cgi_env
, CONST_STRING("REQUEST_METHOD"), get_http_method_name(con
->request
.http_method
));
287 ssi_env_add(p
->ssi_cgi_env
, CONST_STRING("REDIRECT_STATUS"), "200");
288 ssi_env_add(p
->ssi_cgi_env
, CONST_STRING("SERVER_PROTOCOL"), get_http_version_name(con
->request
.http_version
));
290 ssi_env_add_request_headers(srv
, con
, p
);
295 static int process_ssi_stmt(server
*srv
, connection
*con
, plugin_data
*p
, const char **l
, size_t n
, stat_cache_entry
*sce
) {
296 size_t i
, ssicmd
= 0;
302 enum { SSI_UNSET
, SSI_ECHO
, SSI_FSIZE
, SSI_INCLUDE
, SSI_FLASTMOD
,
303 SSI_CONFIG
, SSI_PRINTENV
, SSI_SET
, SSI_IF
, SSI_ELIF
,
304 SSI_ELSE
, SSI_ENDIF
, SSI_EXEC
} type
;
306 { "echo", SSI_ECHO
},
307 { "include", SSI_INCLUDE
},
308 { "flastmod", SSI_FLASTMOD
},
309 { "fsize", SSI_FSIZE
},
310 { "config", SSI_CONFIG
},
311 { "printenv", SSI_PRINTENV
},
314 { "elif", SSI_ELIF
},
315 { "endif", SSI_ENDIF
},
316 { "else", SSI_ELSE
},
317 { "exec", SSI_EXEC
},
322 for (i
= 0; ssicmds
[i
].var
; i
++) {
323 if (0 == strcmp(l
[1], ssicmds
[i
].var
)) {
324 ssicmd
= ssicmds
[i
].type
;
334 const char *var_val
= NULL
;
342 SSI_ECHO_DOCUMENT_NAME
,
343 SSI_ECHO_DOCUMENT_URI
,
344 SSI_ECHO_LAST_MODIFIED
,
350 { "DATE_GMT", SSI_ECHO_DATE_GMT
},
351 { "DATE_LOCAL", SSI_ECHO_DATE_LOCAL
},
352 { "DOCUMENT_NAME", SSI_ECHO_DOCUMENT_NAME
},
353 { "DOCUMENT_URI", SSI_ECHO_DOCUMENT_URI
},
354 { "LAST_MODIFIED", SSI_ECHO_LAST_MODIFIED
},
355 { "USER_NAME", SSI_ECHO_USER_NAME
},
356 { "SCRIPT_URI", SSI_ECHO_SCRIPT_URI
},
357 { "SCRIPT_URL", SSI_ECHO_SCRIPT_URL
},
359 { NULL
, SSI_ECHO_UNSET
}
365 enum { SSI_ENC_UNSET, SSI_ENC_URL, SSI_ENC_NONE, SSI_ENC_ENTITY } type;
367 { "url", SSI_ENC_URL },
368 { "none", SSI_ENC_NONE },
369 { "entity", SSI_ENC_ENTITY },
371 { NULL, SSI_ENC_UNSET }
375 for (i
= 2; i
< n
; i
+= 2) {
376 if (0 == strcmp(l
[i
], "var")) {
381 for (j
= 0; echovars
[j
].var
; j
++) {
382 if (0 == strcmp(l
[i
+1], echovars
[j
].var
)) {
383 var
= echovars
[j
].type
;
387 } else if (0 == strcmp(l
[i
], "encoding")) {
391 for (j = 0; encvars[j].var; j++) {
392 if (0 == strcmp(l[i+1], encvars[j].var)) {
393 enc = encvars[j].type;
399 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
400 "ssi: unknow attribute for ",
405 if (p
->if_is_false
) break;
408 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
410 l
[1], "var is missing");
415 case SSI_ECHO_USER_NAME
: {
420 if (NULL
== (pw
= getpwuid(sce
->st
.st_uid
))) {
421 buffer_copy_int(b
, sce
->st
.st_uid
);
423 buffer_copy_string(b
, pw
->pw_name
);
426 buffer_copy_int(b
, sce
->st
.st_uid
);
428 chunkqueue_append_buffer(con
->write_queue
, b
);
432 case SSI_ECHO_LAST_MODIFIED
: {
433 time_t t
= sce
->st
.st_mtime
;
435 if (0 == strftime(buf
, sizeof(buf
), p
->timefmt
->ptr
, localtime(&t
))) {
436 chunkqueue_append_mem(con
->write_queue
, CONST_STR_LEN("(none)"));
438 chunkqueue_append_mem(con
->write_queue
, buf
, strlen(buf
));
442 case SSI_ECHO_DATE_LOCAL
: {
443 time_t t
= time(NULL
);
445 if (0 == strftime(buf
, sizeof(buf
), p
->timefmt
->ptr
, localtime(&t
))) {
446 chunkqueue_append_mem(con
->write_queue
, CONST_STR_LEN("(none)"));
448 chunkqueue_append_mem(con
->write_queue
, buf
, strlen(buf
));
452 case SSI_ECHO_DATE_GMT
: {
453 time_t t
= time(NULL
);
455 if (0 == strftime(buf
, sizeof(buf
), p
->timefmt
->ptr
, gmtime(&t
))) {
456 chunkqueue_append_mem(con
->write_queue
, CONST_STR_LEN("(none)"));
458 chunkqueue_append_mem(con
->write_queue
, buf
, strlen(buf
));
462 case SSI_ECHO_DOCUMENT_NAME
: {
465 if (NULL
== (sl
= strrchr(con
->physical
.path
->ptr
, '/'))) {
466 chunkqueue_append_mem(con
->write_queue
, CONST_BUF_LEN(con
->physical
.path
));
468 chunkqueue_append_mem(con
->write_queue
, sl
+ 1, strlen(sl
+ 1));
472 case SSI_ECHO_DOCUMENT_URI
: {
473 chunkqueue_append_mem(con
->write_queue
, CONST_BUF_LEN(con
->uri
.path
));
476 case SSI_ECHO_SCRIPT_URI
: {
477 if (!buffer_string_is_empty(con
->uri
.scheme
) && !buffer_string_is_empty(con
->uri
.authority
)) {
478 chunkqueue_append_mem(con
->write_queue
, CONST_BUF_LEN(con
->uri
.scheme
));
479 chunkqueue_append_mem(con
->write_queue
, CONST_STR_LEN("://"));
480 chunkqueue_append_mem(con
->write_queue
, CONST_BUF_LEN(con
->uri
.authority
));
481 chunkqueue_append_mem(con
->write_queue
, CONST_BUF_LEN(con
->request
.uri
));
482 if (!buffer_string_is_empty(con
->uri
.query
)) {
483 chunkqueue_append_mem(con
->write_queue
, CONST_STR_LEN("?"));
484 chunkqueue_append_mem(con
->write_queue
, CONST_BUF_LEN(con
->uri
.query
));
489 case SSI_ECHO_SCRIPT_URL
: {
490 chunkqueue_append_mem(con
->write_queue
, CONST_BUF_LEN(con
->request
.uri
));
491 if (!buffer_string_is_empty(con
->uri
.query
)) {
492 chunkqueue_append_mem(con
->write_queue
, CONST_STR_LEN("?"));
493 chunkqueue_append_mem(con
->write_queue
, CONST_BUF_LEN(con
->uri
.query
));
499 /* check if it is a cgi-var or a ssi-var */
501 if (NULL
!= (ds
= (data_string
*)array_get_element(p
->ssi_cgi_env
, var_val
)) ||
502 NULL
!= (ds
= (data_string
*)array_get_element(p
->ssi_vars
, var_val
))) {
503 chunkqueue_append_mem(con
->write_queue
, CONST_BUF_LEN(ds
->value
));
505 chunkqueue_append_mem(con
->write_queue
, CONST_STR_LEN("(none)"));
516 const char * file_path
= NULL
, *virt_path
= NULL
;
520 for (i
= 2; i
< n
; i
+= 2) {
521 if (0 == strcmp(l
[i
], "file")) {
523 } else if (0 == strcmp(l
[i
], "virtual")) {
526 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
527 "ssi: unknow attribute for ",
532 if (!file_path
&& !virt_path
) {
533 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
535 l
[1], "file or virtual are missing");
539 if (file_path
&& virt_path
) {
540 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
542 l
[1], "only one of file and virtual is allowed here");
547 if (p
->if_is_false
) break;
550 /* current doc-root */
551 if (NULL
== (sl
= strrchr(con
->physical
.path
->ptr
, '/'))) {
552 buffer_copy_string_len(p
->stat_fn
, CONST_STR_LEN("/"));
554 buffer_copy_string_len(p
->stat_fn
, con
->physical
.path
->ptr
, sl
- con
->physical
.path
->ptr
+ 1);
557 buffer_copy_string(srv
->tmp_buf
, file_path
);
558 buffer_urldecode_path(srv
->tmp_buf
);
559 buffer_path_simplify(srv
->tmp_buf
, srv
->tmp_buf
);
560 buffer_append_string_buffer(p
->stat_fn
, srv
->tmp_buf
);
564 if (virt_path
[0] == '/') {
565 buffer_copy_string(p
->stat_fn
, virt_path
);
567 /* there is always a / */
568 sl
= strrchr(con
->uri
.path
->ptr
, '/');
570 buffer_copy_string_len(p
->stat_fn
, con
->uri
.path
->ptr
, sl
- con
->uri
.path
->ptr
+ 1);
571 buffer_append_string(p
->stat_fn
, virt_path
);
574 buffer_urldecode_path(p
->stat_fn
);
575 buffer_path_simplify(srv
->tmp_buf
, p
->stat_fn
);
579 buffer_copy_buffer(p
->stat_fn
, con
->physical
.doc_root
);
580 buffer_append_string_buffer(p
->stat_fn
, srv
->tmp_buf
);
583 if (0 == stat(p
->stat_fn
->ptr
, &st
)) {
584 time_t t
= st
.st_mtime
;
591 const char *abr
[] = { " B", " kB", " MB", " GB", " TB", NULL
};
593 off_t s
= st
.st_size
;
595 for (j
= 0; s
> 1024 && abr
[j
+1]; s
/= 1024, j
++);
597 buffer_copy_int(b
, s
);
598 buffer_append_string(b
, abr
[j
]);
600 buffer_copy_int(b
, st
.st_size
);
602 chunkqueue_append_buffer(con
->write_queue
, b
);
606 if (0 == strftime(buf
, sizeof(buf
), p
->timefmt
->ptr
, localtime(&t
))) {
607 chunkqueue_append_mem(con
->write_queue
, CONST_STR_LEN("(none)"));
609 chunkqueue_append_mem(con
->write_queue
, buf
, strlen(buf
));
613 chunkqueue_append_file(con
->write_queue
, p
->stat_fn
, 0, st
.st_size
);
615 /* Keep the newest mtime of included files */
616 if (st
.st_mtime
> include_file_last_mtime
)
617 include_file_last_mtime
= st
.st_mtime
;
622 log_error_write(srv
, __FILE__
, __LINE__
, "sbs",
623 "ssi: stating failed ",
624 p
->stat_fn
, strerror(errno
));
629 const char *key
= NULL
, *val
= NULL
;
630 for (i
= 2; i
< n
; i
+= 2) {
631 if (0 == strcmp(l
[i
], "var")) {
633 } else if (0 == strcmp(l
[i
], "value")) {
636 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
637 "ssi: unknow attribute for ",
642 if (p
->if_is_false
) break;
647 if (NULL
== (ds
= (data_string
*)array_get_unused_element(p
->ssi_vars
, TYPE_STRING
))) {
648 ds
= data_string_init();
650 buffer_copy_string(ds
->key
, key
);
651 buffer_copy_string(ds
->value
, val
);
653 array_insert_unique(p
->ssi_vars
, (data_unset
*)ds
);
655 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
656 "ssi: var and value have to be set in",
662 if (p
->if_is_false
) break;
664 for (i
= 2; i
< n
; i
+= 2) {
665 if (0 == strcmp(l
[i
], "timefmt")) {
666 buffer_copy_string(p
->timefmt
, l
[i
+1]);
667 } else if (0 == strcmp(l
[i
], "sizefmt")) {
668 if (0 == strcmp(l
[i
+1], "abbrev")) {
670 } else if (0 == strcmp(l
[i
+1], "abbrev")) {
673 log_error_write(srv
, __FILE__
, __LINE__
, "sssss",
674 "ssi: unknow value for attribute '",
680 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
681 "ssi: unknow attribute for ",
687 if (p
->if_is_false
) break;
690 for (i
= 0; i
< p
->ssi_vars
->used
; i
++) {
691 data_string
*ds
= (data_string
*)p
->ssi_vars
->data
[p
->ssi_vars
->sorted
[i
]];
693 buffer_append_string_buffer(b
, ds
->key
);
694 buffer_append_string_len(b
, CONST_STR_LEN("="));
695 buffer_append_string_encoded(b
, CONST_BUF_LEN(ds
->value
), ENCODING_MINIMAL_XML
);
696 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
698 for (i
= 0; i
< p
->ssi_cgi_env
->used
; i
++) {
699 data_string
*ds
= (data_string
*)p
->ssi_cgi_env
->data
[p
->ssi_cgi_env
->sorted
[i
]];
701 buffer_append_string_buffer(b
, ds
->key
);
702 buffer_append_string_len(b
, CONST_STR_LEN("="));
703 buffer_append_string_encoded(b
, CONST_BUF_LEN(ds
->value
), ENCODING_MINIMAL_XML
);
704 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
706 chunkqueue_append_buffer(con
->write_queue
, b
);
711 const char *cmd
= NULL
;
713 int from_exec_fds
[2];
715 for (i
= 2; i
< n
; i
+= 2) {
716 if (0 == strcmp(l
[i
], "cmd")) {
719 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
720 "ssi: unknow attribute for ",
725 if (p
->if_is_false
) break;
727 /* create a return pipe and send output to the html-page
729 * as exec is assumed evil it is implemented synchronously
734 if (pipe(from_exec_fds
)) {
735 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
736 "pipe failed: ", strerror(errno
));
741 switch (pid
= fork()) {
743 /* move stdout to from_rrdtool_fd[1] */
744 close(STDOUT_FILENO
);
745 dup2(from_exec_fds
[1], STDOUT_FILENO
);
746 close(from_exec_fds
[1]);
748 close(from_exec_fds
[0]);
753 execl("/bin/sh", "sh", "-c", cmd
, (char *)NULL
);
755 log_error_write(srv
, __FILE__
, __LINE__
, "sss", "spawing exec failed:", strerror(errno
), cmd
);
763 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "fork failed:", strerror(errno
));
769 int was_interrupted
= 0;
771 close(from_exec_fds
[1]);
773 /* wait for the client to end */
776 * OpenBSD and Solaris send a EINTR on SIGCHILD even if we ignore it
779 if (-1 == waitpid(pid
, &status
, 0)) {
780 if (errno
== EINTR
) {
784 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "waitpid failed:", strerror(errno
));
786 } else if (WIFEXITED(status
)) {
788 /* read everything from client and paste it into the output */
792 if (ioctl(from_exec_fds
[0], FIONREAD
, &toread
)) {
793 log_error_write(srv
, __FILE__
, __LINE__
, "s",
794 "unexpected end-of-file (perhaps the ssi-exec process died)");
802 chunkqueue_get_memory(con
->write_queue
, &mem
, &mem_len
, 0, toread
);
803 r
= read(from_exec_fds
[0], mem
, mem_len
);
804 chunkqueue_use_memory(con
->write_queue
, r
> 0 ? r
: 0);
806 if (r
< 0) break; /* read failed */
813 log_error_write(srv
, __FILE__
, __LINE__
, "s", "process exited abnormally");
815 } while (was_interrupted
> 0 && was_interrupted
< 4); /* if waitpid() gets interrupted, retry, but max 4 times */
817 close(from_exec_fds
[0]);
830 const char *expr
= NULL
;
832 for (i
= 2; i
< n
; i
+= 2) {
833 if (0 == strcmp(l
[i
], "expr")) {
836 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
837 "ssi: unknow attribute for ",
843 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
845 l
[1], "expr missing");
849 if ((!p
->if_is_false
) &&
850 ((p
->if_is_false_level
== 0) ||
851 (p
->if_level
< p
->if_is_false_level
))) {
852 switch (ssi_eval_expr(srv
, con
, p
, expr
)) {
856 p
->if_is_false_level
= p
->if_level
;
871 if (p
->if_is_false
) {
872 if ((p
->if_level
== p
->if_is_false_level
) &&
873 (p
->if_is_false_endif
== 0)) {
879 p
->if_is_false_level
= p
->if_level
;
885 const char *expr
= NULL
;
886 for (i
= 2; i
< n
; i
+= 2) {
887 if (0 == strcmp(l
[i
], "expr")) {
890 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
891 "ssi: unknow attribute for ",
897 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
899 l
[1], "expr missing");
905 if (p
->if_level
== p
->if_is_false_level
) {
906 if ((p
->if_is_false
) &&
907 (p
->if_is_false_endif
== 0)) {
908 switch (ssi_eval_expr(srv
, con
, p
, expr
)) {
912 p
->if_is_false_level
= p
->if_level
;
920 p
->if_is_false_level
= p
->if_level
;
921 p
->if_is_false_endif
= 1;
932 if (p
->if_level
== p
->if_is_false_level
) {
934 p
->if_is_false_endif
= 0;
939 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
940 "ssi: unknow ssi-command:",
949 static int mod_ssi_handle_request(server
*srv
, connection
*con
, plugin_data
*p
) {
958 stat_cache_entry
*sce
= NULL
;
961 /* get a stream to the file */
963 array_reset(p
->ssi_vars
);
964 array_reset(p
->ssi_cgi_env
);
965 buffer_copy_string_len(p
->timefmt
, CONST_STR_LEN("%a, %d %b %Y %H:%M:%S %Z"));
967 build_ssi_cgi_vars(srv
, con
, p
);
970 /* Reset the modified time of included files */
971 include_file_last_mtime
= 0;
973 if (HANDLER_ERROR
== stat_cache_get_entry(srv
, con
, con
->physical
.path
, &sce
)) {
974 log_error_write(srv
, __FILE__
, __LINE__
, "SB", "stat_cache_get_entry failed: ", con
->physical
.path
);
978 if (-1 == stream_open(&s
, con
->physical
.path
)) {
979 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
980 "stream-open: ", con
->physical
.path
);
986 * <!--#element attribute=value attribute=value ... -->
994 * encoding -- missing
1030 * The current date in Greenwich Mean Time.
1032 * The current date in the local time zone.
1034 * The filename (excluding directories) of the document requested by the user.
1036 * The (%-decoded) URL path of the document requested by the user. Note that in the case of nested include files, this is not then URL for the current document.
1038 * The last modification date of the document requested by the user.
1040 * Contains the owner of the file which included it.
1044 for (i
= 0; (n
= pcre_exec(p
->ssi_regex
, NULL
, s
.start
, s
.size
, i
, 0, ovec
, N
* 3)) > 0; i
= ovec
[1]) {
1046 /* take everything from last offset to current match pos */
1048 if (!p
->if_is_false
) chunkqueue_append_file(con
->write_queue
, con
->physical
.path
, i
, ovec
[0] - i
);
1050 pcre_get_substring_list(s
.start
, ovec
, n
, &l
);
1051 process_ssi_stmt(srv
, con
, p
, l
, n
, sce
);
1052 pcre_free_substring_list(l
);
1056 case PCRE_ERROR_NOMATCH
:
1057 /* copy everything/the rest */
1058 chunkqueue_append_file(con
->write_queue
, con
->physical
.path
, i
, s
.size
- i
);
1062 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
1063 "execution error while matching: ", n
);
1071 con
->file_started
= 1;
1072 con
->file_finished
= 1;
1075 if (buffer_string_is_empty(p
->conf
.content_type
)) {
1076 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
1078 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p
->conf
.content_type
));
1082 /* Generate "ETag" & "Last-Modified" headers */
1084 buffer
*mtime
= NULL
;
1086 etag_mutate(con
->physical
.etag
, sce
->etag
);
1087 response_header_overwrite(srv
, con
, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con
->physical
.etag
));
1089 if (sce
->st
.st_mtime
> include_file_last_mtime
)
1090 lm_time
= sce
->st
.st_mtime
;
1092 lm_time
= include_file_last_mtime
;
1094 mtime
= strftime_cache_get(srv
, lm_time
);
1095 response_header_overwrite(srv
, con
, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime
));
1098 /* Reset the modified time of included files */
1099 include_file_last_mtime
= 0;
1101 /* reset physical.path */
1102 buffer_reset(con
->physical
.path
);
1109 static int mod_ssi_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
1111 plugin_config
*s
= p
->config_storage
[0];
1113 PATCH(ssi_extension
);
1114 PATCH(content_type
);
1116 /* skip the first, the global context */
1117 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
1118 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
1119 s
= p
->config_storage
[i
];
1121 /* condition didn't match */
1122 if (!config_check_cond(srv
, con
, dc
)) continue;
1125 for (j
= 0; j
< dc
->value
->used
; j
++) {
1126 data_unset
*du
= dc
->value
->data
[j
];
1128 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("ssi.extension"))) {
1129 PATCH(ssi_extension
);
1130 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("ssi.content-type"))) {
1131 PATCH(content_type
);
1140 URIHANDLER_FUNC(mod_ssi_physical_path
) {
1141 plugin_data
*p
= p_d
;
1144 if (con
->mode
!= DIRECT
) return HANDLER_GO_ON
;
1146 if (buffer_is_empty(con
->physical
.path
)) return HANDLER_GO_ON
;
1148 mod_ssi_patch_connection(srv
, con
, p
);
1150 for (k
= 0; k
< p
->conf
.ssi_extension
->used
; k
++) {
1151 data_string
*ds
= (data_string
*)p
->conf
.ssi_extension
->data
[k
];
1153 if (buffer_is_empty(ds
->value
)) continue;
1155 if (buffer_is_equal_right_len(con
->physical
.path
, ds
->value
, buffer_string_length(ds
->value
))) {
1156 /* handle ssi-request */
1158 if (mod_ssi_handle_request(srv
, con
, p
)) {
1160 con
->http_status
= 500;
1164 return HANDLER_FINISHED
;
1169 return HANDLER_GO_ON
;
1172 /* this function is called at dlopen() time and inits the callbacks */
1174 int mod_ssi_plugin_init(plugin
*p
);
1175 int mod_ssi_plugin_init(plugin
*p
) {
1176 p
->version
= LIGHTTPD_VERSION_ID
;
1177 p
->name
= buffer_init_string("ssi");
1179 p
->init
= mod_ssi_init
;
1180 p
->handle_subrequest_start
= mod_ssi_physical_path
;
1181 p
->set_defaults
= mod_ssi_set_defaults
;
1182 p
->cleanup
= mod_ssi_free
;