Apache HTTPD
cgi_common.h
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "apr.h"
18#include "apr_strings.h"
19#include "apr_buckets.h"
20#include "apr_lib.h"
21#include "apr_poll.h"
22
23#define APR_WANT_STRFUNC
24#define APR_WANT_MEMFUNC
25#include "apr_want.h"
26
27#include "httpd.h"
28#include "util_filter.h"
29#include "util_script.h"
30
33
34/* These functions provided by mod_cgi.c/mod_cgid.c still. */
35static int log_script(request_rec *r, cgi_server_conf * conf, int ret,
36 char *dbuf, const char *sbuf, apr_bucket_brigade *bb,
39 apr_bucket_brigade *bb, char *s);
41 apr_bucket_brigade *bb, const char *command);
42
43/* Read and discard all output from the brigade. Note that with the
44 * CGI bucket, the brigade will become empty once the script's stdout
45 * is closed (or on error/timeout), but the stderr output may not have
46 * been entirely captured at this point. */
48{
50 const char *buf;
52
53 for (e = APR_BRIGADE_FIRST(bb);
55 e = APR_BRIGADE_FIRST(bb))
56 {
58 break;
59 }
61 }
62}
63
65 apr_status_t rv, const char *logno,
66 const char *error)
67{
68 apr_file_t *f = NULL;
69 apr_finfo_t finfo;
70 char time_str[APR_CTIME_LEN];
71
72 /* Intentional no APLOGNO */
73 /* Callee provides APLOGNO in error text */
75 "%sstderr from %s: %s", logno ? logno : "", r->filename, error);
76
77 /* XXX Very expensive mainline case! Open, then getfileinfo! */
78 if (!conf->logname ||
79 ((apr_stat(&finfo, conf->logname,
81 (finfo.size > conf->logbytes)) ||
82 (apr_file_open(&f, conf->logname,
84 r->pool) != APR_SUCCESS)) {
85 return ret;
86 }
87
88 /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
89 apr_ctime(time_str, apr_time_now());
90 apr_file_printf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri,
91 r->args ? "?" : "", r->args ? r->args : "", r->protocol);
92 /* "%% 500 /usr/local/apache/cgi-bin */
93 apr_file_printf(f, "%%%% %d %s\n", ret, r->filename);
94
95 apr_file_printf(f, "%%error\n%s\n", error);
96
98 return ret;
99}
100
101/* Soak up stderr from a script and redirect it to the error log.
102 */
104{
106 char *newline;
107 apr_status_t rv;
109
111 script_err)) == APR_SUCCESS) {
112
113 newline = strchr(argsbuffer, '\n');
114 if (newline) {
115 char *prev = newline - 1;
116 if (prev >= argsbuffer && *prev == '\r') {
117 newline = prev;
118 }
119
120 *newline = '\0';
121 }
122 log_scripterror(r, conf, r->status, 0, APLOGNO(01215), argsbuffer);
123 }
124
125 return rv;
126}
127
130{
131 char *tag = NULL;
132 char *tag_val = NULL;
133 request_rec *r = f->r;
134 char *file = r->filename;
136
137 if (!ctx->argc) {
139 (ctx->flags & SSI_FLAG_PRINTING)
141 0, r, APLOGNO(03195)
142 "missing argument for exec element in %s", r->filename);
143 }
144
145 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
146 return APR_SUCCESS;
147 }
148
149 if (!ctx->argc) {
151 return APR_SUCCESS;
152 }
153
154 if (ctx->flags & SSI_FLAG_NO_EXEC) {
155 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01228) "exec used but not allowed "
156 "in %s", r->filename);
158 return APR_SUCCESS;
159 }
160
161 while (1) {
163 if (!tag || !tag_val) {
164 break;
165 }
166
167 if (!strcmp(tag, "cmd")) {
168 apr_status_t rv;
169
172
173 rv = include_cmd(ctx, f, bb, parsed_string);
174 if (rv != APR_SUCCESS) {
175 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01229) "execution failure "
176 "for parameter \"%s\" to tag exec in file %s",
177 tag, r->filename);
179 break;
180 }
181 }
182 else if (!strcmp(tag, "cgi")) {
183 apr_status_t rv;
184
187
188 rv = include_cgi(ctx, f, bb, parsed_string);
189 if (rv != APR_SUCCESS) {
190 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01230) "invalid CGI ref "
191 "\"%s\" in %s", tag_val, file);
193 break;
194 }
195 }
196 else {
197 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01231) "unknown parameter "
198 "\"%s\" to tag exec in %s", tag, file);
200 break;
201 }
202 }
203
204 return APR_SUCCESS;
205}
206
207/* Hook to register exec= handling with mod_include. */
208static void cgi_optfns_retrieve(void)
209{
211
215
217 /* Required by mod_include filter. This is how mod_cgi registers
218 * with mod_include to provide processing of the exec directive.
219 */
221 }
222}
223
224#ifdef WANT_CGI_BUCKET
225/* A CGI bucket type is needed to catch any output to stderr from the
226 * script; see PR 22030. */
228
229struct cgi_bucket_data {
231 request_rec *r;
233};
234
235/* Create a CGI bucket using pipes from script stdout 'out'
236 * and stderr 'err', for request 'r'. */
241{
242 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
243 apr_status_t rv;
245 struct cgi_bucket_data *data = apr_palloc(r->pool, sizeof *data);
246
247 /* Disable APR timeout handling since we'll use poll() entirely. */
250
252 b->free = apr_bucket_free;
253 b->list = list;
254 b->type = &bucket_type_cgi;
255 b->length = (apr_size_t)(-1);
256 b->start = -1;
257
258 /* Create the pollset */
259 rv = apr_pollset_create(&data->pollset, 2, r->pool, 0);
260 if (rv != APR_SUCCESS) {
262 "apr_pollset_create(); check system or user limits");
263 return NULL;
264 }
265
266 fd.desc_type = APR_POLL_FILE;
267 fd.reqevents = APR_POLLIN;
268 fd.p = r->pool;
269 fd.desc.f = out; /* script's stdout */
270 fd.client_data = (void *)1;
271 rv = apr_pollset_add(data->pollset, &fd);
272 if (rv != APR_SUCCESS) {
274 "apr_pollset_add(); check system or user limits");
275 return NULL;
276 }
277
278 fd.desc.f = err; /* script's stderr */
279 fd.client_data = (void *)2;
280 rv = apr_pollset_add(data->pollset, &fd);
281 if (rv != APR_SUCCESS) {
283 "apr_pollset_add(); check system or user limits");
284 return NULL;
285 }
286
287 data->r = r;
288 data->timeout = timeout;
289 b->data = data;
290 return b;
291}
292
293/* Create a duplicate CGI bucket using given bucket data */
296{
297 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
299 b->free = apr_bucket_free;
300 b->list = list;
301 b->type = &bucket_type_cgi;
302 b->length = (apr_size_t)(-1);
303 b->start = -1;
304 b->data = data;
305 return b;
306}
307
308/* Handle stdout from CGI child. Duplicate of logic from the _read
309 * method of the real APR pipe bucket implementation. */
311 const char **str, apr_size_t *len)
312{
313 char *buf;
314 apr_status_t rv;
315
316 *str = NULL;
318 buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */
319
320 rv = apr_file_read(out, buf, len);
321
322 if (rv != APR_SUCCESS && rv != APR_EOF) {
324 return rv;
325 }
326
327 if (*len > 0) {
328 struct cgi_bucket_data *data = a->data;
330
331 /* Change the current bucket to refer to what we read */
333 h = a->data;
334 h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */
335 *str = buf;
337 }
338 else {
340 a = apr_bucket_immortal_make(a, "", 0);
341 *str = a->data;
342 }
343 return rv;
344}
345
346/* Read method of CGI bucket: polls on stderr and stdout of the child,
347 * sending any stderr output immediately away to the error log. */
348static apr_status_t cgi_bucket_read(apr_bucket *b, const char **str,
350{
351 struct cgi_bucket_data *data = b->data;
353 apr_status_t rv;
354 int gotdata = 0;
355
356 if (block != APR_NONBLOCK_READ) {
357 timeout = data->timeout > 0 ? data->timeout : data->r->server->timeout;
358 }
359
360 do {
361 const apr_pollfd_t *results;
363
364 rv = apr_pollset_poll(data->pollset, timeout, &num, &results);
365 if (APR_STATUS_IS_TIMEUP(rv)) {
366 if (timeout) {
368 "Timeout waiting for output from CGI script %s",
369 data->r->filename);
370 return rv;
371 }
372 else {
373 return APR_EAGAIN;
374 }
375 }
376 else if (APR_STATUS_IS_EINTR(rv)) {
377 continue;
378 }
379 else if (rv != APR_SUCCESS) {
381 "poll failed waiting for CGI child");
382 return rv;
383 }
384
385 for (; num; num--, results++) {
386 if (results[0].client_data == (void *)1) {
387 /* stdout */
388 rv = cgi_read_stdout(b, results[0].desc.f, str, len);
389 if (APR_STATUS_IS_EOF(rv)) {
390 rv = APR_SUCCESS;
391 }
392 gotdata = 1;
393 } else {
394 /* stderr */
395 apr_status_t rv2 = log_script_err(data->r, results[0].desc.f);
396 if (APR_STATUS_IS_EOF(rv2)) {
397 apr_pollset_remove(data->pollset, &results[0]);
398 }
399 }
400 }
401
402 } while (!gotdata);
403
404 return rv;
405}
406
407static const apr_bucket_type_t bucket_type_cgi = {
408 "CGI", 5, APR_BUCKET_DATA,
414};
415
416#endif /* WANT_CGI_BUCKET */
417
418/* Handle the CGI response output, having set up the brigade with the
419 * CGI or PIPE bucket as appropriate. */
423{
424 apr_status_t rv;
425
426 /* Handle script return... */
427 if (!nph) {
428 const char *location;
429 char sbuf[MAX_STRING_LEN];
430 int ret;
431
434
435 /* xCGI has its own body framing mechanism which we don't
436 * match against any provided Content-Length, so let the
437 * core determine C-L vs T-E based on what's actually sent.
438 */
440 apr_table_unset(r->headers_out, "Content-Length");
441 apr_table_unset(r->headers_out, "Transfer-Encoding");
442
443 if (ret != OK) {
444 /* In the case of a timeout reading script output, clear
445 * the brigade to avoid a second attempt to read the
446 * output. */
447 if (ret == HTTP_GATEWAY_TIME_OUT) {
449 }
450
451 ret = log_script(r, conf, ret, logdata, sbuf, bb, script_err);
452
453 /*
454 * ret could be HTTP_NOT_MODIFIED in the case that the CGI script
455 * does not set an explicit status and ap_meets_conditions, which
456 * is called by ap_scan_script_header_err_brigade, detects that
457 * the conditions of the requests are met and the response is
458 * not modified.
459 * In this case set r->status and return OK in order to prevent
460 * running through the error processing stack as this would
461 * break with mod_cache, if the conditions had been set by
462 * mod_cache itself to validate a stale entity.
463 * BTW: We circumvent the error processing stack anyway if the
464 * CGI script set an explicit status code (whatever it is) and
465 * the only possible values for ret here are:
466 *
467 * HTTP_NOT_MODIFIED (set by ap_meets_conditions)
468 * HTTP_PRECONDITION_FAILED (set by ap_meets_conditions)
469 * HTTP_INTERNAL_SERVER_ERROR (if something went wrong during the
470 * processing of the response of the CGI script, e.g broken headers
471 * or a crashed CGI process).
472 */
473 if (ret == HTTP_NOT_MODIFIED) {
474 r->status = ret;
475 return OK;
476 }
477
478 return ret;
479 }
480
481 location = apr_table_get(r->headers_out, "Location");
482
483 if (location && r->status == 200) {
484 /* For a redirect whether internal or not, discard any
485 * remaining stdout from the script, and log any remaining
486 * stderr output, as normal. */
489
490 if (script_err) {
493 }
494 }
495
496 if (location && location[0] == '/' && r->status == 200) {
497 /* This redirect needs to be a GET no matter what the original
498 * method was.
499 */
500 r->method = "GET";
502
503 /* We already read the message body (if any), so don't allow
504 * the redirected request to think it has one. We can ignore
505 * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR.
506 */
507 apr_table_unset(r->headers_in, "Content-Length");
508
510 return OK;
511 }
512 else if (location && r->status == 200) {
513 /* XXX: Note that if a script wants to produce its own Redirect
514 * body, it now has to explicitly *say* "Status: 302"
515 */
519 }
520
522 }
523 else /* nph */ {
524 struct ap_filter_t *cur;
525
526 /* get rid of all filters up through protocol... since we
527 * haven't parsed off the headers, there is no way they can
528 * work
529 */
530
532 while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION) {
533 cur = cur->next;
534 }
536
538 }
539
540 /* don't soak up script output if errors occurred writing it
541 * out... otherwise, we prolong the life of the script when the
542 * connection drops or we stopped sending output for some other
543 * reason */
544 if (script_err && rv == APR_SUCCESS && !r->connection->aborted) {
547 }
548
550
551 return OK; /* NOT r->status, even if it has changed. */
552}
553
554/* Read the request body and write it to fd 'script_out', using 'bb'
555 * as temporary bucket brigade. If 'logbuf' is non-NULL, the first
556 * logbufbytes of stdout are stored in logbuf. */
560{
561 int seen_eos = 0;
562 int child_stopped_reading = 0;
563 apr_status_t rv;
564 int dbpos = 0;
565
566 do {
567 apr_bucket *bucket;
568
571
572 if (rv != APR_SUCCESS) {
573 return rv;
574 }
575
576 for (bucket = APR_BRIGADE_FIRST(bb);
577 bucket != APR_BRIGADE_SENTINEL(bb);
578 bucket = APR_BUCKET_NEXT(bucket))
579 {
580 const char *data;
582
583 if (APR_BUCKET_IS_EOS(bucket)) {
584 seen_eos = 1;
585 break;
586 }
587
588 /* We can't do much with this. */
589 if (APR_BUCKET_IS_FLUSH(bucket)) {
590 continue;
591 }
592
593 /* If the child stopped, we still must read to EOS. */
595 continue;
596 }
597
598 /* read */
599 rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
600 if (rv) {
601 return rv;
602 }
603
604 if (logbufbytes && dbpos < logbufbytes) {
605 int cursize;
606
607 if ((dbpos + len) > logbufbytes) {
609 }
610 else {
611 cursize = len;
612 }
614 dbpos += cursize;
615 }
616
617 /* Keep writing data to the child until done or too much time
618 * elapses with no progress or an error occurs.
619 */
621
622 if (rv != APR_SUCCESS) {
623 /* silly script stopped reading, soak up remaining message */
626 "Error writing request body to script %s",
627 r->filename);
628 }
629 }
631 }
632 while (!seen_eos);
633
634 if (logbuf) {
635 logbuf[dbpos] = '\0';
636 }
637
638 return APR_SUCCESS;
639}
const char apr_size_t len
Definition ap_regex.h:187
APR-UTIL Buckets/Bucket Brigades.
APR general purpose library routines.
APR Poll interface.
APR Strings library.
APR Standard Headers Support.
static void cgi_optfns_retrieve(void)
Definition cgi_common.h:208
static int cgi_handle_response(request_rec *r, int nph, apr_bucket_brigade *bb, apr_interval_time_t timeout, cgi_server_conf *conf, char *logdata, apr_file_t *script_err)
Definition cgi_common.h:420
static int log_scripterror(request_rec *r, cgi_server_conf *conf, int ret, apr_status_t rv, const char *logno, const char *error)
Definition cgi_common.h:64
static int log_script(request_rec *r, cgi_server_conf *conf, int ret, char *dbuf, const char *sbuf, apr_bucket_brigade *bb, apr_file_t *script_err)
static apr_status_t log_script_err(request_rec *r, apr_file_t *script_err)
Definition cgi_common.h:103
static apr_status_t cgi_handle_exec(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb)
Definition cgi_common.h:128
static apr_status_t include_cmd(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb, const char *command)
static apr_status_t include_cgi(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb, char *s)
static void discard_script_output(apr_bucket_brigade *bb)
Definition cgi_common.h:47
static apr_status_t cgi_handle_request(request_rec *r, apr_file_t *script_out, apr_bucket_brigade *bb, char *logbuf, apr_size_t logbufbytes)
Definition cgi_common.h:557
#define ap_get_module_config(v, m)
request_rec * r
#define HUGE_STRING_LEN
Definition httpd.h:303
#define MAX_STRING_LEN
Definition httpd.h:300
#define OK
Definition httpd.h:456
apr_status_t ap_pass_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket)
apr_status_t ap_get_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes)
@ AP_FTYPE_CONNECTION
#define APLOGNO(n)
Definition http_log.h:117
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_WARNING
Definition http_log.h:68
#define APLOG_MODULE_INDEX
Definition http_log.h:168
const unsigned char * buf
Definition util_md5.h:50
void ap_internal_redirect_handler(const char *new_uri, request_rec *r)
int ap_scan_script_header_err_brigade_ex(request_rec *r, apr_bucket_brigade *bb, char *buffer, int module_index)
#define AP_TRUST_CGILIKE_CL_ENVVAR
#define APR_EAGAIN
Definition apr_errno.h:730
#define APR_EOF
Definition apr_errno.h:461
#define APR_STATUS_IS_EINTR(s)
Definition apr_errno.h:1281
#define APR_STATUS_IS_TIMEUP(s)
Definition apr_errno.h:534
#define APR_STATUS_IS_EOF(s)
Definition apr_errno.h:567
#define APR_BUCKET_INIT(e)
apr_file_t * f
#define APR_BUCKET_INSERT_AFTER(a, b)
#define APR_BUCKET_IS_FLUSH(e)
#define APR_BUCKET_NEXT(e)
apr_read_type_e
Definition apr_buckets.h:57
apr_bucket * e
#define APR_BUCKET_BUFF_SIZE
Definition apr_buckets.h:54
#define APR_BRIGADE_SENTINEL(b)
#define apr_bucket_delete(e)
#define APR_BUCKET_IS_EOS(e)
apr_brigade_flush void * ctx
apr_bucket apr_bucket_brigade * a
#define APR_BRIGADE_FIRST(b)
apr_file_t * fd
#define apr_bucket_read(e, str, len, block)
@ APR_BLOCK_READ
Definition apr_buckets.h:58
@ APR_NONBLOCK_READ
Definition apr_buckets.h:59
apr_pool_t const char apr_dbd_t const char ** error
Definition apr_dbd.h:143
#define APR_RETRIEVE_OPTIONAL_FN(name)
#define APR_OPTIONAL_FN_TYPE(name)
#define HTTP_NOT_MODIFIED
Definition httpd.h:504
#define HTTP_MOVED_TEMPORARILY
Definition httpd.h:502
#define HTTP_GATEWAY_TIME_OUT
Definition httpd.h:539
#define SSI_FLAG_PRINTING
Definition mod_include.h:56
#define SSI_EXPAND_LEAVE_NAME
Definition mod_include.h:41
#define SSI_EXPAND_DROP_NAME
Definition mod_include.h:42
#define SSI_CREATE_ERROR_BUCKET(ctx, f, bb)
Definition mod_include.h:48
#define SSI_VALUE_DECODED
Definition mod_include.h:35
#define SSI_FLAG_NO_EXEC
Definition mod_include.h:59
#define M_GET
Definition httpd.h:592
apr_size_t size
const apr_array_header_t * list
Definition apr_cstr.h:105
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
void * data
const char apr_file_t * file
#define APR_APPEND
Definition apr_file_io.h:96
#define APR_WRITE
Definition apr_file_io.h:94
#define APR_CREATE
Definition apr_file_io.h:95
#define APR_OS_DEFAULT
#define APR_FINFO_SIZE
const apr_hash_t * h
Definition apr_hash.h:97
apr_interval_time_t apr_int32_t * num
Definition apr_poll.h:273
@ APR_POLL_FILE
Definition apr_poll.h:94
apr_pool_t * b
Definition apr_pools.h:529
const char * s
Definition apr_strings.h:95
apr_int32_t apr_int32_t apr_int32_t err
#define APR_CTIME_LEN
Definition apr_time.h:198
apr_int64_t apr_interval_time_t
Definition apr_time.h:55
#define APR_POLLIN
Definition apr_poll.h:49
HTTP Daemon routines.
#define cgi_module
Definition mod_cgid.c:218
static void ap_register_include_handler(char *tag, include_handler_fn_t *func)
static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag, char **tag_val, int dodecode)
static char * ap_ssi_parse_string(include_ctx_t *ctx, const char *in, char *out, apr_size_t length, int leave_name)
static apr_file_t * out
Definition mod_info.c:85
return NULL
Definition mod_so.c:359
static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc, step_vars_storage *step_vars)
Definition sed1.c:766
The representation of a filter chain.
ap_filter_t * next
apr_bucket_alloc_t * list
void * data
apr_off_t size
const char * logname
Definition mod_cgi.c:86
unsigned aborted
Definition httpd.h:1219
A structure that represents the current request.
Definition httpd.h:845
int status
Definition httpd.h:891
char * uri
Definition httpd.h:1016
struct ap_filter_t * output_filters
Definition httpd.h:1070
int method_number
Definition httpd.h:898
apr_pool_t * pool
Definition httpd.h:847
char * filename
Definition httpd.h:1018
conn_rec * connection
Definition httpd.h:849
struct ap_filter_t * proto_output_filters
Definition httpd.h:1076
struct ap_filter_t * input_filters
Definition httpd.h:1072
apr_table_t * headers_in
Definition httpd.h:976
char * protocol
Definition httpd.h:879
apr_table_t * subprocess_env
Definition httpd.h:983
server_rec * server
Definition httpd.h:851
const char * method
Definition httpd.h:900
char * args
Definition httpd.h:1026
apr_table_t * headers_out
Definition httpd.h:978
struct ap_conf_vector_t * module_config
Definition httpd.h:1341
static apr_pollset_t * pollset
Definition testpoll.c:41
apr_status_t apr_ctime(char *date_str, apr_time_t t)
Definition timestr.c:90
#define str
Apache filter library.
@ AP_MODE_READBYTES
Definition util_filter.h:43
Apache script tools.
IN ULONG IN INT timeout