diff --git a/qse/cmd/http/httpd.c b/qse/cmd/http/httpd.c index 9ce91cb9..ca79c6e5 100644 --- a/qse/cmd/http/httpd.c +++ b/qse/cmd/http/httpd.c @@ -357,6 +357,33 @@ static int daemonize (int devnull) /* --------------------------------------------------------------------- */ +int xxxx (void* ctx, qse_env_char_t** envir) +{ + extern char** environ; +char buf[1000]; +char* cl; +int cl_i = 0; + environ = envir; + printf ("Content-Type: text/html\n\n"); + printf ("
\n");
+	system ("ls -laF /tmp");
+	printf ("--------------------\n");
+	system ("printenv");
+ cl = getenv("CONTENT_LENGTH");
+if (cl) cl_i = atoi(cl);
+//if (cl_i)
+//{
+while (fgets (buf, sizeof(buf), stdin) != NULL)
+{
+printf ("%s", buf);
+}
+//}
+//	system ("while read xxx; do echo $xxx; done; echo 123 456 789");
+	printf ("
\n"); + + return 0; +} + static int make_resource ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req, qse_httpd_rsrc_t* rsrc) @@ -406,7 +433,7 @@ static loccfg_t* find_loccfg ( for (loccfg = hostcfg->loccfg; loccfg; loccfg = loccfg->next) { QSE_ASSERT (loccfg->locname.len > 0); - if (qse_mbsbeg (qpath, loccfg->locname.ptr) && + if (qse_mbsbeg (qpath, loccfg->locname.ptr) && (loccfg->locname.ptr[loccfg->locname.len - 1] == QSE_MT('/') || qpath[loccfg->locname.len] == QSE_MT('/') || qpath[loccfg->locname.len] == QSE_MT('\0'))) @@ -708,10 +735,10 @@ found: if (qse_mbscmp (qse_htre_getqpath(qinfo->req), QSE_MT("/version")) == 0) { /* return static text without inspecting further */ - ((qse_httpd_serverstd_root_t*)result)->type = QSE_HTTPD_SERVERSTD_ROOT_TEXT; + + /*((qse_httpd_serverstd_root_t*)result)->type = QSE_HTTPD_SERVERSTD_ROOT_TEXT; ((qse_httpd_serverstd_root_t*)result)->u.text.ptr = QSE_MT(QSE_PACKAGE_NAME " " QSE_PACKAGE_VERSION); - ((qse_httpd_serverstd_root_t*)result)->u.text.mime = QSE_MT("text/plain"); - } + ((qse_httpd_serverstd_root_t*)result)->u.text.mime = QSE_MT("text/plain");*/ else #endif @@ -724,7 +751,10 @@ found: ((qse_httpd_serverstd_realm_t*)result)->name = loccfg->xcfg[XCFG_REALM]; + /* qinfo->xpath is not available for the REALM query in the std implementation. + * let me check if it's available in case the implementation changes */ apath = qinfo->xpath? qinfo->xpath: qse_htre_getqpath (qinfo->req); +printf ("apth in READM QUERY [%s]\n", apath); if (apath) { const qse_mchar_t* base; @@ -803,25 +833,44 @@ found: qse_size_t i; qse_httpd_serverstd_cgi_t* scgi; const qse_mchar_t* xpath_base; + qse_mchar_t* qpath; - xpath_base = qse_mbsbasename (qinfo->xpath); + qpath = qse_htre_getqpath(qinfo->req); scgi = (qse_httpd_serverstd_cgi_t*)result; qse_memset (scgi, 0, QSE_SIZEOF(*scgi)); - for (i = 0; i < QSE_COUNTOF(loccfg->cgi); i++) +#if 1 +printf ("qinfo->xpath ####### [%s] %d [%s]\n", qinfo->xpath, qinfo->xpath_nx, qpath); + //if (qse_mbscmp (qinfo->xpath, QSE_MT("/tmp/version.cgi")) == 0) + if (qse_mbscmp (qpath, QSE_MT("/local/version.cgi")) == 0) { - struct cgi_t* cgi; - for (cgi = loccfg->cgi[i].head; cgi; cgi = cgi->next) + scgi->cgi = 1; + scgi->nph = 0; + scgi->fncptr = xxxx; + scgi->shebang = QSE_NULL; + return 0; + } +#endif + + + if (!qinfo->xpath_nx) + { + xpath_base = qse_mbsbasename (qinfo->xpath); + for (i = 0; i < QSE_COUNTOF(loccfg->cgi); i++) { - if ((cgi->type == CGI_PREFIX && qse_mbsbeg (xpath_base, cgi->spec)) || - (cgi->type == CGI_SUFFIX && qse_mbsend (xpath_base, cgi->spec)) || - (cgi->type == CGI_NAME && qse_mbscmp (xpath_base, cgi->spec) == 0)) + struct cgi_t* cgi; + for (cgi = loccfg->cgi[i].head; cgi; cgi = cgi->next) { - scgi->cgi = 1; - scgi->nph = cgi->nph; - scgi->shebang = cgi->shebang; - return 0; + if ((cgi->type == CGI_PREFIX && qse_mbsbeg (xpath_base, cgi->spec)) || + (cgi->type == CGI_SUFFIX && qse_mbsend (xpath_base, cgi->spec)) || + (cgi->type == CGI_NAME && qse_mbscmp (xpath_base, cgi->spec) == 0)) + { + scgi->cgi = 1; + scgi->nph = cgi->nph; + scgi->shebang = cgi->shebang; + return 0; + } } } } @@ -874,7 +923,7 @@ found: id = (code == QSE_HTTPD_SERVERSTD_DIRACC)? 0: 1; xpath_base = qse_mbsbasename (qinfo->xpath); - + *(int*)result = 200; for (i = 0; i < QSE_COUNTOF(loccfg->access[id]); i++) { diff --git a/qse/include/qse/cmn/pio.h b/qse/include/qse/cmn/pio.h index 4f845c3d..77e0a8fc 100644 --- a/qse/include/qse/cmn/pio.h +++ b/qse/include/qse/cmn/pio.h @@ -59,6 +59,10 @@ enum qse_pio_flag_t * them to prevent inheritance. */ QSE_PIO_NOCLOEXEC = (1 << 5), + /** indidate that the command to qse_pio_open() is a pointer to + * #qse_pio_fnc_t. supported on unix/linux only */ + QSE_PIO_FNCCMD = (1 << 6), + /** write to stdin of a child process */ QSE_PIO_WRITEIN = (1 << 8), /** read stdout of a child process */ @@ -113,6 +117,16 @@ enum qse_pio_hid_t }; typedef enum qse_pio_hid_t qse_pio_hid_t; + +typedef int (*qse_pio_fncptr_t) (void* ctx, qse_env_char_t** envir); + +typedef struct qse_pio_fnc_t qse_pio_fnc_t; +struct qse_pio_fnc_t +{ + qse_pio_fncptr_t ptr; + void* ctx; +}; + /** * The qse_pio_errnum_t type defines error numbers. */ @@ -177,6 +191,7 @@ struct qse_pio_pin_t qse_pio_t* self; }; + /** * The qse_pio_t type defines a structure to store status for piped I/O * to a child process. The qse_pio_xxx() funtions are written around this diff --git a/qse/include/qse/http/httpd.h b/qse/include/qse/http/httpd.h index 431a9e12..1b3a1906 100644 --- a/qse/include/qse/http/httpd.h +++ b/qse/include/qse/http/httpd.h @@ -28,6 +28,8 @@ #include #include #include +#include + typedef struct qse_httpd_t qse_httpd_t; typedef struct qse_httpd_mate_t qse_httpd_mate_t; @@ -689,6 +691,19 @@ struct qse_httpd_urs_t /* -------------------------------------------------------------------------- */ +/* ensure to define qse_httpd_fncptr_t to the same as + * qse_pio_fncptr_t in */ +typedef int (*qse_httpd_fncptr_t) (void* ctx, qse_env_char_t** envir); + +typedef struct qse_httpd_fnc_t qse_httpd_fnc_t; +struct qse_httpd_fnc_t +{ + qse_httpd_fncptr_t ptr; + void* ctx; +}; + +/* -------------------------------------------------------------------------- */ + /** * The qse_httpd_rsrc_type_t defines the resource type than can * be entasked with qse_httpd_entaskrsrc(). @@ -702,7 +717,6 @@ enum qse_httpd_rsrc_type_t QSE_HTTPD_RSRC_FILE, QSE_HTTPD_RSRC_PROXY, QSE_HTTPD_RSRC_RELOC, - QSE_HTTPD_RSRC_REDIR, QSE_HTTPD_RSRC_TEXT }; typedef enum qse_httpd_rsrc_type_t qse_httpd_rsrc_type_t; @@ -711,16 +725,39 @@ enum qse_httpd_rsrc_flag_t { QSE_HTTPD_RSRC_100_CONTINUE = (1 << 0) }; +typedef enum qse_httpd_rsrc_flag_t qse_httpd_rsrc_flag_t; + +enum qse_httpd_rsrc_cgi_flag_t +{ + /* non-parsed header */ + QSE_HTTPD_RSRC_CGI_NPH = (1 << 0), + + /* 'path' points to qse_httpd_fncptr_t && 'shebang' points to its context */ + QSE_HTTPD_RSRC_CGI_FNC = (1 << 1) +}; +typedef enum qse_httpd_rsrc_cgi_flag_t qse_httpd_rsrc_cgi_flag_t; + typedef struct qse_httpd_rsrc_cgi_t qse_httpd_rsrc_cgi_t; struct qse_httpd_rsrc_cgi_t { - const qse_mchar_t* path; - const qse_mchar_t* script; - const qse_mchar_t* suffix; + /* bitwised-ORed of #qse_httpd_rsrc_cgi_flag_t */ + int flags; + + /* script path resolved against file system */ + const qse_mchar_t* path; + + /* script path as in qpath */ + const qse_mchar_t* script; + + /* trailing part of qpath excluding the script path. + * for a qpath of /tmp/abc.cgi/a/b/c, if /tmp/abc.cgi is a script path, + * /a/b/c forms the suffix.*/ + const qse_mchar_t* suffix; + const qse_mchar_t* root; + const qse_mchar_t* shebang; - int nph; }; enum qse_httpd_rsrc_proxy_flag_t @@ -733,6 +770,7 @@ enum qse_httpd_rsrc_proxy_flag_t QSE_HTTPD_RSRC_PROXY_DNS_SERVER = (1 << 5), /* dns address specified */ QSE_HTTPD_RSRC_PROXY_URS_SERVER = (1 << 6), /* urs address specified */ }; +typedef enum qse_httpd_rsrc_proxy_flag_t qse_httpd_rsrc_proxy_flag_t; typedef struct qse_httpd_rsrc_proxy_t qse_httpd_rsrc_proxy_t; struct qse_httpd_rsrc_proxy_t @@ -774,6 +812,21 @@ struct qse_httpd_rsrc_dir_t const qse_mchar_t* foot; }; +enum qse_httpd_rsrc_reloc_flag_t +{ + QSE_HTTPD_RSRC_RELOC_PERMANENT = (1 << 0), + QSE_HTTPD_RSRC_RELOC_KEEPMETHOD = (1 << 1), + QSE_HTTPD_RSRC_RELOC_APPENDSLASH = (1 << 2) +}; +typedef enum qse_httpd_rsrc_reloc_flag_t qse_httpd_rsrc_reloc_flag_t; + +typedef struct qse_httpd_rsrc_reloc_t qse_httpd_rsrc_reloc_t; +struct qse_httpd_rsrc_reloc_t +{ + int flags; + const qse_mchar_t* dst; +}; + typedef struct qse_httpd_rsrc_t qse_httpd_rsrc_t; struct qse_httpd_rsrc_t { @@ -802,15 +855,7 @@ struct qse_httpd_rsrc_t qse_httpd_rsrc_proxy_t proxy; - struct - { - const qse_mchar_t* dst; - } reloc; - - struct - { - const qse_mchar_t* dst; - } redir; + qse_httpd_rsrc_reloc_t reloc; struct { @@ -1071,22 +1116,13 @@ QSE_EXPORT qse_httpd_task_t* qse_httpd_entaskauth ( ); QSE_EXPORT qse_httpd_task_t* qse_httpd_entaskreloc ( - qse_httpd_t* httpd, - qse_httpd_client_t* client, - qse_httpd_task_t* pred, - const qse_mchar_t* dst, - qse_htre_t* req + qse_httpd_t* httpd, + qse_httpd_client_t* client, + qse_httpd_task_t* pred, + const qse_httpd_rsrc_reloc_t* reloc, + qse_htre_t* req ); -QSE_EXPORT qse_httpd_task_t* qse_httpd_entaskredir ( - qse_httpd_t* httpd, - qse_httpd_client_t* client, - qse_httpd_task_t* pred, - const qse_mchar_t* dst, - qse_htre_t* req -); - - QSE_EXPORT qse_httpd_task_t* qse_httpd_entasknomod ( qse_httpd_t* httpd, qse_httpd_client_t* client, diff --git a/qse/include/qse/http/stdhttpd.h b/qse/include/qse/http/stdhttpd.h index 3e8ff971..2de606e3 100644 --- a/qse/include/qse/http/stdhttpd.h +++ b/qse/include/qse/http/stdhttpd.h @@ -90,9 +90,17 @@ struct qse_httpd_serverstd_auth_t typedef struct qse_httpd_serverstd_cgi_t qse_httpd_serverstd_cgi_t; struct qse_httpd_serverstd_cgi_t { - int cgi: 1; - int nph: 1; - const qse_mchar_t* shebang; /* optional, can be #QSE_NULL */ + unsigned int cgi: 1; + unsigned int nph: 1; + + /* optional, can be #QSE_NULL. */ + qse_httpd_fncptr_t fncptr; + + /* optional, can be #QSE_NULL. if fncptr not #QSE_NULL, shebang is + * interpreted as void* and used as a context pointer to fnc. + * if fncptr is #QSE_NULL, it provides a pointer to the path + * to the program interpreter for executing a cgi script. */ + const qse_mchar_t* shebang; }; typedef struct qse_httpd_serverstd_index_t qse_httpd_serverstd_index_t; @@ -135,7 +143,22 @@ struct qse_httpd_serverstd_query_info_t { qse_httpd_client_t* client; qse_htre_t* req; - qse_mchar_t* xpath; /* query path combined with document root */ + + /** + * set to a query path combined with document root for these query types. + * - #QSE_HTTPD_SERVERSTD_CGI + * - #QSE_HTTPD_SERVERSTD_MIME + * - #QSE_HTTPD_SERVERSTD_DIRACC + * - #QSE_HTTPD_SERVERSTD_FILEACC + * - #QSE_HTTPD_SERVERSTD_DIRHEAD + * - #QSE_HTTPD_SERVERSTD_DIRFOOT + * + * set to #QSE_NULL for other query types. + */ + qse_mchar_t* xpath; + + /** indiates that stat() failed over xpath when it's not #QSE_NULL. */ + int xpath_nx; }; typedef struct qse_httpd_serverstd_query_info_t qse_httpd_serverstd_query_info_t; diff --git a/qse/lib/cmn/env.c b/qse/lib/cmn/env.c index d9eddceb..91a6fe24 100644 --- a/qse/lib/cmn/env.c +++ b/qse/lib/cmn/env.c @@ -437,7 +437,7 @@ static qse_wchar_t* get_env (qse_env_t* env, const qse_wchar_t* name, int* free) *free = 0; return eq + 1; } - p++; + p++; } } */ @@ -464,10 +464,10 @@ static qse_wchar_t* get_env (qse_env_t* env, const qse_wchar_t* name, int* free) QSE_MMGR_FREE (env->mmgr, dup); - p++; + p++; } } - + return 0; } @@ -494,10 +494,10 @@ static qse_mchar_t* get_env (qse_env_t* env, const qse_mchar_t* name, int* free) *free = 0; return eq + 1; } - p++; + p++; } } - + return 0; } #endif @@ -587,14 +587,14 @@ static int load_curenv (qse_env_t* env) if (*envstr != QSE_WT('=') && add_envstrw (env, envstr) <= -1) { ret = -1; goto done; } envstr += qse_wcslen (envstr) + 1; - } + } #else while (*envstr != QSE_MT('\0')) { if (*envstr != QSE_MT('=') && add_envstrm (env, envstr) <= -1) { ret = -1; goto done; } envstr += qse_mbslen (envstr) + 1; - } + } #endif done: @@ -614,7 +614,7 @@ done: while (*p) { if (add_envstrw (env, *p) <= -1) return -1; - p++; + p++; } } */ @@ -635,11 +635,10 @@ done: QSE_MMGR_FREE (env->mmgr, dup); if (n <= -1) return -1; - p++; + p++; } } - - + return 0; #else @@ -656,10 +655,10 @@ done: while (*p) { if (add_envstrm (env, *p) <= -1) return -1; - p++; + p++; } } - + return 0; #endif } diff --git a/qse/lib/cmn/pio.c b/qse/lib/cmn/pio.c index cc7f2a5a..0df45700 100644 --- a/qse/lib/cmn/pio.c +++ b/qse/lib/cmn/pio.c @@ -376,8 +376,8 @@ static int close_unneeded_fds_using_proc (int* excepts, qse_size_t count) qse_mchar_t buf[64]; qse_mbsxfmt (buf, QSE_COUNTOF(buf), QSE_MT("/proc/%d/fd"), QSE_GETPID()); d = QSE_OPENDIR (buf); + if (!d) d = QSE_OPENDIR (QSE_MT("/dev/fd")); /* Darwin, FreeBSD */ } -/* TODO: try /dev/fd - FreeBSD, Darwin, OS X*/ if (d) { @@ -438,10 +438,9 @@ static int get_highest_fd (void) qse_mchar_t buf[64]; qse_mbsxfmt (buf, QSE_COUNTOF(buf), QSE_MT("/proc/%d/fd"), QSE_GETPID()); d = QSE_OPENDIR (buf); + if (!d) d = QSE_OPENDIR (QSE_MT("/dev/fd")); /* Darwin, FreeBSD */ } -/* TODO: try /dev/fd - FreeBSD, Darwin, OS X*/ - if (d) { int maxfd = -1; @@ -476,7 +475,7 @@ static int get_highest_fd (void) #endif } else fd = rlim.rlim_max; - if (fd == -1) fd = 1024; /* fallback */ + if (fd <= -1) fd = 1024; /* fallback */ /* F_MAXFD is the highest fd. but RLIMIT_NOFILE and * _SC_OPEN_MAX returnes the maximum number of file @@ -486,6 +485,152 @@ static int get_highest_fd (void) return fd; } + +static qse_pio_pid_t standard_fork_and_exec (qse_pio_t* pio, int pipes[], param_t* param, qse_env_t* env) +{ + qse_pio_pid_t pid; + +#if defined(HAVE_CRT_EXTERNS_H) +# define environ (*(_NSGetEnviron())) +#else + extern char** environ; +#endif + + pid = QSE_FORK(); + if (pid <= -1) + { + pio->errnum = QSE_PIO_EINVAL; + return -1; + } + + if (pid == 0) + { + /* child */ + qse_pio_hnd_t devnull = -1; + + if (!(pio->flags & QSE_PIO_NOCLOEXEC)) + { + if (close_unneeded_fds_using_proc (pipes, 6) <= -1) + { + int fd = get_highest_fd (); + + /* close all other unknown open handles except + * stdin/out/err and the pipes. */ + while (fd > 2) + { + if (fd != pipes[0] && fd != pipes[1] && + fd != pipes[2] && fd != pipes[3] && + fd != pipes[4] && fd != pipes[5]) + { + QSE_CLOSE (fd); + } + fd--; + } + } + } + + if (pio->flags & QSE_PIO_WRITEIN) + { + /* child should read */ + QSE_CLOSE (pipes[1]); + pipes[1] = QSE_PIO_HND_NIL; + if (QSE_DUP2 (pipes[0], 0) <= -1) goto child_oops; + QSE_CLOSE (pipes[0]); + pipes[0] = QSE_PIO_HND_NIL; + } + + if (pio->flags & QSE_PIO_READOUT) + { + /* child should write */ + QSE_CLOSE (pipes[2]); + pipes[2] = QSE_PIO_HND_NIL; + if (QSE_DUP2 (pipes[3], 1) <= -1) goto child_oops; + + if (pio->flags & QSE_PIO_ERRTOOUT) + { + if (QSE_DUP2 (pipes[3], 2) <= -1) goto child_oops; + } + + QSE_CLOSE (pipes[3]); + pipes[3] = QSE_PIO_HND_NIL; + } + + if (pio->flags & QSE_PIO_READERR) + { + /* child should write */ + QSE_CLOSE (pipes[4]); + pipes[4] = QSE_PIO_HND_NIL; + if (QSE_DUP2 (pipes[5], 2) <= -1) goto child_oops; + + if (pio->flags & QSE_PIO_OUTTOERR) + { + if (QSE_DUP2 (pipes[5], 1) <= -1) goto child_oops; + } + + QSE_CLOSE (pipes[5]); + pipes[5] = QSE_PIO_HND_NIL; + } + + if ((pio->flags & QSE_PIO_INTONUL) || + (pio->flags & QSE_PIO_OUTTONUL) || + (pio->flags & QSE_PIO_ERRTONUL)) + { + #if defined(O_LARGEFILE) + devnull = QSE_OPEN (QSE_MT("/dev/null"), O_RDWR|O_LARGEFILE, 0); + #else + devnull = QSE_OPEN (QSE_MT("/dev/null"), O_RDWR, 0); + #endif + if (devnull <= -1) goto child_oops; + } + + if ((pio->flags & QSE_PIO_INTONUL) && + QSE_DUP2(devnull,0) <= -1) goto child_oops; + if ((pio->flags & QSE_PIO_OUTTONUL) && + QSE_DUP2(devnull,1) <= -1) goto child_oops; + if ((pio->flags & QSE_PIO_ERRTONUL) && + QSE_DUP2(devnull,2) <= -1) goto child_oops; + + if ((pio->flags & QSE_PIO_INTONUL) || + (pio->flags & QSE_PIO_OUTTONUL) || + (pio->flags & QSE_PIO_ERRTONUL)) + { + QSE_CLOSE (devnull); + devnull = -1; + } + + if (pio->flags & QSE_PIO_DROPIN) QSE_CLOSE(0); + if (pio->flags & QSE_PIO_DROPOUT) QSE_CLOSE(1); + if (pio->flags & QSE_PIO_DROPERR) QSE_CLOSE(2); + + + if (pio->flags & QSE_PIO_FNCCMD) + { + /* ----------------------------------------------- + * the function pointer to execute has been given. + * -----------------------------------------------*/ + qse_pio_fnc_t* fnc = (qse_pio_fnc_t*)param; + int retx; + + retx = fnc->ptr (fnc->ctx, (env? qse_env_getarr(env): environ)); + if (devnull >= 0) QSE_CLOSE (devnull); + QSE_EXIT (retx); + } + else + { + QSE_EXECVE (param->argv[0], param->argv, (env? qse_env_getarr(env): environ)); + + /* if exec fails, free 'param' parameter which is an inherited pointer */ + free_param (pio, param); + } + + child_oops: + if (devnull >= 0) QSE_CLOSE (devnull); + QSE_EXIT (128); + } + + return pid; +} + #endif static int set_pipe_nonblock (qse_pio_t* pio, qse_pio_hnd_t fd, int enabled) @@ -1006,7 +1151,7 @@ create_process: DosClose (old_out); old_out = QSE_PIO_HND_NIL; DosClose (old_in); old_in = QSE_PIO_HND_NIL; goto oops; - } + } /* we must not let our own stdin/out/err duplicated * into old_in/out/err be inherited */ @@ -1200,462 +1345,11 @@ create_process: pio->child = child_rc.codeTerminate; #elif defined(__DOS__) - + /* DOS not multi-processed. can't support pio */ pio->errnum = QSE_PIO_ENOIMPL; return -1; -#elif defined(HAVE_POSIX_SPAWN) && !(defined(QSE_SYSCALL0) && defined(SYS_vfork)) - if (flags & QSE_PIO_WRITEIN) - { - if (QSE_PIPE(&handle[0]) <= -1) - { - pio->errnum = syserr_to_errnum (errno); - goto oops; - } - minidx = 0; maxidx = 1; - } - - if (flags & QSE_PIO_READOUT) - { - if (QSE_PIPE(&handle[2]) <= -1) - { - pio->errnum = syserr_to_errnum (errno); - goto oops; - } - if (minidx == -1) minidx = 2; - maxidx = 3; - } - - if (flags & QSE_PIO_READERR) - { - if (QSE_PIPE(&handle[4]) <= -1) - { - pio->errnum = syserr_to_errnum (errno); - goto oops; - } - if (minidx == -1) minidx = 4; - maxidx = 5; - } - - if (maxidx == -1) - { - pio->errnum = QSE_PIO_EINVAL; - goto oops; - } - - if ((pserr = posix_spawn_file_actions_init (&fa)) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - fa_inited = 1; - - if (flags & QSE_PIO_WRITEIN) - { - /* child should read */ - if ((pserr = posix_spawn_file_actions_addclose (&fa, handle[1])) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - if ((pserr = posix_spawn_file_actions_adddup2 (&fa, handle[0], 0)) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - if ((pserr = posix_spawn_file_actions_addclose (&fa, handle[0])) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - } - - if (flags & QSE_PIO_READOUT) - { - /* child should write */ - if ((pserr = posix_spawn_file_actions_addclose (&fa, handle[2])) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - if ((pserr = posix_spawn_file_actions_adddup2 (&fa, handle[3], 1)) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - if ((flags & QSE_PIO_ERRTOOUT) && - (pserr = posix_spawn_file_actions_adddup2 (&fa, handle[3], 2)) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - if ((pserr = posix_spawn_file_actions_addclose (&fa, handle[3])) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - } - - if (flags & QSE_PIO_READERR) - { - /* child should write */ - if ((pserr = posix_spawn_file_actions_addclose (&fa, handle[4])) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - if ((pserr = posix_spawn_file_actions_adddup2 (&fa, handle[5], 2)) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - if ((flags & QSE_PIO_OUTTOERR) && - (pserr = posix_spawn_file_actions_adddup2 (&fa, handle[5], 1)) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - if ((pserr = posix_spawn_file_actions_addclose (&fa, handle[5])) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - } - - { - int oflags = O_RDWR; - #if defined(O_LARGEFILE) - oflags |= O_LARGEFILE; - #endif - - if ((flags & QSE_PIO_INTONUL) && - (pserr = posix_spawn_file_actions_addopen (&fa, 0, QSE_MT("/dev/null"), oflags, 0)) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - if ((flags & QSE_PIO_OUTTONUL) && - (pserr = posix_spawn_file_actions_addopen (&fa, 1, QSE_MT("/dev/null"), oflags, 0)) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - if ((flags & QSE_PIO_ERRTONUL) && - (pserr = posix_spawn_file_actions_addopen (&fa, 2, QSE_MT("/dev/null"), oflags, 0)) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - } - - /* there remains the chance of race condition that - * 0, 1, 2 can be closed between addclose() and posix_spawn(). - * so checking the file descriptors with is_fd_valid() is - * just on the best-effort basis. - */ - if ((flags & QSE_PIO_DROPIN) && is_fd_valid(0) && - (pserr = posix_spawn_file_actions_addclose (&fa, 0)) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - if ((flags & QSE_PIO_DROPOUT) && is_fd_valid(1) && - (pserr = posix_spawn_file_actions_addclose (&fa, 1)) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - if ((flags & QSE_PIO_DROPERR) && is_fd_valid(2) && - (pserr = posix_spawn_file_actions_addclose (&fa, 2)) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - - if (!(flags & QSE_PIO_NOCLOEXEC)) - { - int fd = get_highest_fd (); - while (fd > 2) - { - if (fd != handle[0] && fd != handle[1] && - fd != handle[2] && fd != handle[3] && - fd != handle[4] && fd != handle[5]) - { - /* closing attempt on a best-effort basis. - * posix_spawn() fails if the file descriptor added - * with addclose() is closed before posix_spawn(). - * addclose() if no FD_CLOEXEC is set or it's unknown. */ - if (is_fd_valid_and_nocloexec(fd) && - (pserr = posix_spawn_file_actions_addclose (&fa, fd)) != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - } - fd--; - } - } - - if (make_param (pio, cmd, flags, ¶m) <= -1) goto oops; - - /* check if the command(the command requested or /bin/sh) is - * exectuable to return an error without trying to execute it - * though this check alone isn't sufficient */ - if (assert_executable (pio, param.argv[0]) <= -1) - { - free_param (pio, ¶m); - goto oops; - } - - posix_spawnattr_init (&psattr); -#if defined(__linux) -#if !defined(POSIX_SPAWN_USEVFORK) -# define POSIX_SPAWN_USEVFORK 0x40 -#endif - posix_spawnattr_setflags (&psattr, POSIX_SPAWN_USEVFORK); -#endif - - pserr = posix_spawn( - &pid, param.argv[0], &fa, &psattr, param.argv, - (env? qse_env_getarr(env): environ)); - -#if defined(__linux) - posix_spawnattr_destroy (&psattr); -#endif - - free_param (pio, ¶m); - if (fa_inited) - { - posix_spawn_file_actions_destroy (&fa); - fa_inited = 0; - } - if (pserr != 0) - { - pio->errnum = syserr_to_errnum (pserr); - goto oops; - } - - pio->child = pid; - if (flags & QSE_PIO_WRITEIN) - { - QSE_CLOSE (handle[0]); - handle[0] = QSE_PIO_HND_NIL; - } - if (flags & QSE_PIO_READOUT) - { - QSE_CLOSE (handle[3]); - handle[3] = QSE_PIO_HND_NIL; - } - if (flags & QSE_PIO_READERR) - { - QSE_CLOSE (handle[5]); - handle[5] = QSE_PIO_HND_NIL; - } - -#elif defined(QSE_SYSCALL0) && defined(SYS_vfork) - if (flags & QSE_PIO_WRITEIN) - { - if (QSE_PIPE(&handle[0]) <= -1) - { - pio->errnum = syserr_to_errnum (errno); - goto oops; - } - minidx = 0; maxidx = 1; - } - - if (flags & QSE_PIO_READOUT) - { - if (QSE_PIPE(&handle[2]) <= -1) - { - pio->errnum = syserr_to_errnum (errno); - goto oops; - } - if (minidx == -1) minidx = 2; - maxidx = 3; - } - - if (flags & QSE_PIO_READERR) - { - if (QSE_PIPE(&handle[4]) <= -1) - { - pio->errnum = syserr_to_errnum (errno); - goto oops; - } - if (minidx == -1) minidx = 4; - maxidx = 5; - } - - if (maxidx == -1) - { - pio->errnum = QSE_PIO_EINVAL; - goto oops; - } - - if (make_param (pio, cmd, flags, ¶m) <= -1) goto oops; - - /* check if the command(the command requested or /bin/sh) is - * exectuable to return an error without trying to execute it - * though this check alone isn't sufficient */ - if (assert_executable (pio, param.argv[0]) <= -1) - { - free_param (pio, ¶m); - goto oops; - } - - /* prepare some data before vforking for vfork limitation. - * the child in vfork should not make function calls or - * change data shared with the parent. */ - if (!(flags & QSE_PIO_NOCLOEXEC)) highest_fd = get_highest_fd (); - envarr = env? qse_env_getarr(env): environ; - - QSE_SYSCALL0 (pid, SYS_vfork); - if (pid <= -1) - { - pio->errnum = QSE_PIO_EINVAL; - free_param (pio, ¶m); - goto oops; - } - - if (pid == 0) - { - /* the child after vfork should not make function calls. - * since the system call like close() are also normal - * functions, i have to use assembly macros to make - * system calls. */ - - qse_pio_hnd_t devnull = -1; - - if (!(flags & QSE_PIO_NOCLOEXEC)) - { - /* cannot call close_unneeded_fds_using_proc() in the vfork() context */ - - int fd = highest_fd; - - /* close all other unknown open handles except - * stdin/out/err and the pipes. */ - while (fd > 2) - { - if (fd != handle[0] && fd != handle[1] && - fd != handle[2] && fd != handle[3] && - fd != handle[4] && fd != handle[5]) - { - QSE_SYSCALL1 (dummy, SYS_close, fd); - } - fd--; - } - } - - if (flags & QSE_PIO_WRITEIN) - { - /* child should read */ - QSE_SYSCALL1 (dummy, SYS_close, handle[1]); - QSE_SYSCALL2 (dummy, SYS_dup2, handle[0], 0); - if (dummy <= -1) goto child_oops; - QSE_SYSCALL1 (dummy, SYS_close, handle[0]); - } - - if (flags & QSE_PIO_READOUT) - { - /* child should write */ - QSE_SYSCALL1 (dummy, SYS_close, handle[2]); - QSE_SYSCALL2 (dummy, SYS_dup2, handle[3], 1); - if (dummy <= -1) goto child_oops; - - if (flags & QSE_PIO_ERRTOOUT) - { - QSE_SYSCALL2 (dummy, SYS_dup2, handle[3], 2); - if (dummy <= -1) goto child_oops; - } - - QSE_SYSCALL1 (dummy, SYS_close, handle[3]); - } - - if (flags & QSE_PIO_READERR) - { - /* child should write */ - QSE_SYSCALL1 (dummy, SYS_close, handle[4]); - QSE_SYSCALL2 (dummy, SYS_dup2, handle[5], 2); - if (dummy <= -1) goto child_oops; - - if (flags & QSE_PIO_OUTTOERR) - { - QSE_SYSCALL2 (dummy, SYS_dup2, handle[5], 1); - if (dummy <= -1) goto child_oops; - } - - QSE_SYSCALL1 (dummy, SYS_close, handle[5]); - } - - if ((flags & QSE_PIO_INTONUL) || - (flags & QSE_PIO_OUTTONUL) || - (flags & QSE_PIO_ERRTONUL)) - { - #if defined(O_LARGEFILE) - QSE_SYSCALL3 (devnull, SYS_open, QSE_MT("/dev/null"), O_RDWR|O_LARGEFILE, 0); - #else - QSE_SYSCALL3 (devnull, SYS_open, QSE_MT("/dev/null"), O_RDWR, 0); - #endif - if (devnull <= -1) goto child_oops; - } - - if (flags & QSE_PIO_INTONUL) - { - QSE_SYSCALL2 (dummy, SYS_dup2, devnull, 0); - if (dummy <= -1) goto child_oops; - } - if (flags & QSE_PIO_OUTTONUL) - { - QSE_SYSCALL2 (dummy, SYS_dup2, devnull, 1); - if (dummy <= -1) goto child_oops; - } - if (flags & QSE_PIO_ERRTONUL) - { - QSE_SYSCALL2 (dummy, SYS_dup2, devnull, 2); - if (dummy <= -1) goto child_oops; - } - - if ((flags & QSE_PIO_INTONUL) || - (flags & QSE_PIO_OUTTONUL) || - (flags & QSE_PIO_ERRTONUL)) - { - QSE_SYSCALL1 (dummy, SYS_close, devnull); - devnull = -1; - } - - if (flags & QSE_PIO_DROPIN) QSE_SYSCALL1 (dummy, SYS_close, 0); - if (flags & QSE_PIO_DROPOUT) QSE_SYSCALL1 (dummy, SYS_close, 1); - if (flags & QSE_PIO_DROPERR) QSE_SYSCALL1 (dummy, SYS_close, 2); - - QSE_SYSCALL3 (dummy, SYS_execve, param.argv[0], param.argv, envarr); - /*free_param (pio, ¶m); don't free this in the vfork version */ - - child_oops: - if (devnull >= 0) QSE_SYSCALL1 (dummy, SYS_close, devnull); - QSE_SYSCALL1 (dummy, SYS_exit, 128); - } - - /* parent */ - free_param (pio, ¶m); - pio->child = pid; - - if (flags & QSE_PIO_WRITEIN) - { - QSE_CLOSE (handle[0]); - handle[0] = QSE_PIO_HND_NIL; - } - - if (flags & QSE_PIO_READOUT) - { - QSE_CLOSE (handle[3]); - handle[3] = QSE_PIO_HND_NIL; - } - - if (flags & QSE_PIO_READERR) - { - QSE_CLOSE (handle[5]); - handle[5] = QSE_PIO_HND_NIL; - } #else if (flags & QSE_PIO_WRITEIN) @@ -1696,137 +1390,385 @@ create_process: goto oops; } - if (make_param (pio, cmd, flags, ¶m) <= -1) goto oops; - - /* check if the command(the command requested or /bin/sh) is - * exectuable to return an error without trying to execute it - * though this check alone isn't sufficient */ - if (assert_executable (pio, param.argv[0]) <= -1) + if (pio->flags & QSE_PIO_FNCCMD) { - free_param (pio, ¶m); - goto oops; + /* i know i'm abusing typecasting here. + * cmd is supposed to be qse_pio_fnc_t*, anyway */ + pid = standard_fork_and_exec (pio, handle, (param_t*)cmd, env); + if (pid <= -1) goto oops; + pio->child = pid; } - - pid = QSE_FORK(); - if (pid <= -1) + else { - pio->errnum = QSE_PIO_EINVAL; - free_param (pio, ¶m); - goto oops; - } + #if defined(HAVE_POSIX_SPAWN) && !(defined(QSE_SYSCALL0) && defined(SYS_vfork)) - if (pid == 0) - { - /* child */ - qse_pio_hnd_t devnull = -1; + if ((pserr = posix_spawn_file_actions_init (&fa)) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + fa_inited = 1; + + if (flags & QSE_PIO_WRITEIN) + { + /* child should read */ + if ((pserr = posix_spawn_file_actions_addclose (&fa, handle[1])) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + if ((pserr = posix_spawn_file_actions_adddup2 (&fa, handle[0], 0)) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + if ((pserr = posix_spawn_file_actions_addclose (&fa, handle[0])) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + } + + if (flags & QSE_PIO_READOUT) + { + /* child should write */ + if ((pserr = posix_spawn_file_actions_addclose (&fa, handle[2])) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + if ((pserr = posix_spawn_file_actions_adddup2 (&fa, handle[3], 1)) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + if ((flags & QSE_PIO_ERRTOOUT) && + (pserr = posix_spawn_file_actions_adddup2 (&fa, handle[3], 2)) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + if ((pserr = posix_spawn_file_actions_addclose (&fa, handle[3])) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + } + + if (flags & QSE_PIO_READERR) + { + /* child should write */ + if ((pserr = posix_spawn_file_actions_addclose (&fa, handle[4])) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + if ((pserr = posix_spawn_file_actions_adddup2 (&fa, handle[5], 2)) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + if ((flags & QSE_PIO_OUTTOERR) && + (pserr = posix_spawn_file_actions_adddup2 (&fa, handle[5], 1)) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + if ((pserr = posix_spawn_file_actions_addclose (&fa, handle[5])) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + } + + { + int oflags = O_RDWR; + #if defined(O_LARGEFILE) + oflags |= O_LARGEFILE; + #endif + + if ((flags & QSE_PIO_INTONUL) && + (pserr = posix_spawn_file_actions_addopen (&fa, 0, QSE_MT("/dev/null"), oflags, 0)) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + if ((flags & QSE_PIO_OUTTONUL) && + (pserr = posix_spawn_file_actions_addopen (&fa, 1, QSE_MT("/dev/null"), oflags, 0)) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + if ((flags & QSE_PIO_ERRTONUL) && + (pserr = posix_spawn_file_actions_addopen (&fa, 2, QSE_MT("/dev/null"), oflags, 0)) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + } + + /* there remains the chance of race condition that + * 0, 1, 2 can be closed between addclose() and posix_spawn(). + * so checking the file descriptors with is_fd_valid() is + * just on the best-effort basis. + */ + if ((flags & QSE_PIO_DROPIN) && is_fd_valid(0) && + (pserr = posix_spawn_file_actions_addclose (&fa, 0)) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + if ((flags & QSE_PIO_DROPOUT) && is_fd_valid(1) && + (pserr = posix_spawn_file_actions_addclose (&fa, 1)) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + if ((flags & QSE_PIO_DROPERR) && is_fd_valid(2) && + (pserr = posix_spawn_file_actions_addclose (&fa, 2)) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } if (!(flags & QSE_PIO_NOCLOEXEC)) { - if (close_unneeded_fds_using_proc (handle, 6) <= -1) + int fd = get_highest_fd (); + while (fd > 2) { - int fd = get_highest_fd (); + if (fd != handle[0] && fd != handle[1] && + fd != handle[2] && fd != handle[3] && + fd != handle[4] && fd != handle[5]) + { + /* closing attempt on a best-effort basis. + * posix_spawn() fails if the file descriptor added + * with addclose() is closed before posix_spawn(). + * addclose() if no FD_CLOEXEC is set or it's unknown. */ + if (is_fd_valid_and_nocloexec(fd) && + (pserr = posix_spawn_file_actions_addclose (&fa, fd)) != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + } + fd--; + } + } + + if (make_param (pio, cmd, flags, ¶m) <= -1) goto oops; + + /* check if the command(the command requested or /bin/sh) is + * exectuable to return an error without trying to execute it + * though this check alone isn't sufficient */ + if (assert_executable (pio, param.argv[0]) <= -1) + { + free_param (pio, ¶m); + goto oops; + } + + posix_spawnattr_init (&psattr); + + #if defined(__linux) + #if !defined(POSIX_SPAWN_USEVFORK) + # define POSIX_SPAWN_USEVFORK 0x40 + #endif + posix_spawnattr_setflags (&psattr, POSIX_SPAWN_USEVFORK); + #endif + + pserr = posix_spawn( + &pid, param.argv[0], &fa, &psattr, param.argv, + (env? qse_env_getarr(env): environ)); + + #if defined(__linux) + posix_spawnattr_destroy (&psattr); + #endif + + free_param (pio, ¶m); + if (fa_inited) + { + posix_spawn_file_actions_destroy (&fa); + fa_inited = 0; + } + if (pserr != 0) + { + pio->errnum = syserr_to_errnum (pserr); + goto oops; + } + + pio->child = pid; + + #elif defined(QSE_SYSCALL0) && defined(SYS_vfork) + + if (make_param (pio, cmd, flags, ¶m) <= -1) goto oops; + + /* check if the command(the command requested or /bin/sh) is + * exectuable to return an error without trying to execute it + * though this check alone isn't sufficient */ + if (assert_executable (pio, param.argv[0]) <= -1) + { + free_param (pio, ¶m); + goto oops; + } + + /* prepare some data before vforking for vfork limitation. + * the child in vfork should not make function calls or + * change data shared with the parent. */ + if (!(flags & QSE_PIO_NOCLOEXEC)) highest_fd = get_highest_fd (); + envarr = env? qse_env_getarr(env): environ; + + QSE_SYSCALL0 (pid, SYS_vfork); + if (pid <= -1) + { + pio->errnum = QSE_PIO_EINVAL; + free_param (pio, ¶m); + goto oops; + } + + if (pid == 0) + { + /* the child after vfork should not make function calls. + * since the system call like close() are also normal + * functions, i have to use assembly macros to make + * system calls. */ + + qse_pio_hnd_t devnull = -1; + + if (!(flags & QSE_PIO_NOCLOEXEC)) + { + /* cannot call close_unneeded_fds_using_proc() in the vfork() context */ + + int fd = highest_fd; /* close all other unknown open handles except * stdin/out/err and the pipes. */ while (fd > 2) { if (fd != handle[0] && fd != handle[1] && - fd != handle[2] && fd != handle[3] && - fd != handle[4] && fd != handle[5]) + fd != handle[2] && fd != handle[3] && + fd != handle[4] && fd != handle[5]) { - QSE_CLOSE (fd); + QSE_SYSCALL1 (dummy, SYS_close, fd); } fd--; } } - } - if (flags & QSE_PIO_WRITEIN) - { - /* child should read */ - QSE_CLOSE (handle[1]); - handle[1] = QSE_PIO_HND_NIL; - if (QSE_DUP2 (handle[0], 0) <= -1) goto child_oops; - QSE_CLOSE (handle[0]); - handle[0] = QSE_PIO_HND_NIL; - } - - if (flags & QSE_PIO_READOUT) - { - /* child should write */ - QSE_CLOSE (handle[2]); - handle[2] = QSE_PIO_HND_NIL; - if (QSE_DUP2 (handle[3], 1) <= -1) goto child_oops; - - if (flags & QSE_PIO_ERRTOOUT) + if (flags & QSE_PIO_WRITEIN) { - if (QSE_DUP2 (handle[3], 2) <= -1) goto child_oops; + /* child should read */ + QSE_SYSCALL1 (dummy, SYS_close, handle[1]); + QSE_SYSCALL2 (dummy, SYS_dup2, handle[0], 0); + if (dummy <= -1) goto child_oops; + QSE_SYSCALL1 (dummy, SYS_close, handle[0]); } - QSE_CLOSE (handle[3]); - handle[3] = QSE_PIO_HND_NIL; - } - - if (flags & QSE_PIO_READERR) - { - /* child should write */ - QSE_CLOSE (handle[4]); - handle[4] = QSE_PIO_HND_NIL; - if (QSE_DUP2 (handle[5], 2) <= -1) goto child_oops; - - if (flags & QSE_PIO_OUTTOERR) + if (flags & QSE_PIO_READOUT) { - if (QSE_DUP2 (handle[5], 1) <= -1) goto child_oops; + /* child should write */ + QSE_SYSCALL1 (dummy, SYS_close, handle[2]); + QSE_SYSCALL2 (dummy, SYS_dup2, handle[3], 1); + if (dummy <= -1) goto child_oops; + + if (flags & QSE_PIO_ERRTOOUT) + { + QSE_SYSCALL2 (dummy, SYS_dup2, handle[3], 2); + if (dummy <= -1) goto child_oops; + } + + QSE_SYSCALL1 (dummy, SYS_close, handle[3]); } - QSE_CLOSE (handle[5]); - handle[5] = QSE_PIO_HND_NIL; + if (flags & QSE_PIO_READERR) + { + /* child should write */ + QSE_SYSCALL1 (dummy, SYS_close, handle[4]); + QSE_SYSCALL2 (dummy, SYS_dup2, handle[5], 2); + if (dummy <= -1) goto child_oops; + + if (flags & QSE_PIO_OUTTOERR) + { + QSE_SYSCALL2 (dummy, SYS_dup2, handle[5], 1); + if (dummy <= -1) goto child_oops; + } + + QSE_SYSCALL1 (dummy, SYS_close, handle[5]); + } + + if (flags & (QSE_PIO_INTONUL | QSE_PIO_OUTTONUL | QSE_PIO_ERRTONUL)) + { + #if defined(O_LARGEFILE) + QSE_SYSCALL3 (devnull, SYS_open, QSE_MT("/dev/null"), O_RDWR|O_LARGEFILE, 0); + #else + QSE_SYSCALL3 (devnull, SYS_open, QSE_MT("/dev/null"), O_RDWR, 0); + #endif + if (devnull <= -1) goto child_oops; + } + + if (flags & QSE_PIO_INTONUL) + { + QSE_SYSCALL2 (dummy, SYS_dup2, devnull, 0); + if (dummy <= -1) goto child_oops; + } + if (flags & QSE_PIO_OUTTONUL) + { + QSE_SYSCALL2 (dummy, SYS_dup2, devnull, 1); + if (dummy <= -1) goto child_oops; + } + if (flags & QSE_PIO_ERRTONUL) + { + QSE_SYSCALL2 (dummy, SYS_dup2, devnull, 2); + if (dummy <= -1) goto child_oops; + } + + if (flags & (QSE_PIO_INTONUL | QSE_PIO_OUTTONUL | QSE_PIO_ERRTONUL)) + { + QSE_SYSCALL1 (dummy, SYS_close, devnull); + devnull = -1; + } + + if (flags & QSE_PIO_DROPIN) QSE_SYSCALL1 (dummy, SYS_close, 0); + if (flags & QSE_PIO_DROPOUT) QSE_SYSCALL1 (dummy, SYS_close, 1); + if (flags & QSE_PIO_DROPERR) QSE_SYSCALL1 (dummy, SYS_close, 2); + + QSE_SYSCALL3 (dummy, SYS_execve, param.argv[0], param.argv, envarr); + /*free_param (pio, ¶m); don't free this in the vfork version */ + + child_oops: + if (devnull >= 0) QSE_SYSCALL1 (dummy, SYS_close, devnull); + QSE_SYSCALL1 (dummy, SYS_exit, 128); } - if ((flags & QSE_PIO_INTONUL) || - (flags & QSE_PIO_OUTTONUL) || - (flags & QSE_PIO_ERRTONUL)) + /* parent */ + free_param (pio, ¶m); + pio->child = pid; + + #else + + if (make_param (pio, cmd, flags, ¶m) <= -1) goto oops; + + /* check if the command(the command requested or /bin/sh) is + * exectuable to return an error without trying to execute it + * though this check alone isn't sufficient */ + if (assert_executable (pio, param.argv[0]) <= -1) { - #if defined(O_LARGEFILE) - devnull = QSE_OPEN (QSE_MT("/dev/null"), O_RDWR|O_LARGEFILE, 0); - #else - devnull = QSE_OPEN (QSE_MT("/dev/null"), O_RDWR, 0); - #endif - if (devnull <= -1) goto child_oops; + free_param (pio, ¶m); + goto oops; } - if ((flags & QSE_PIO_INTONUL) && - QSE_DUP2(devnull,0) <= -1) goto child_oops; - if ((flags & QSE_PIO_OUTTONUL) && - QSE_DUP2(devnull,1) <= -1) goto child_oops; - if ((flags & QSE_PIO_ERRTONUL) && - QSE_DUP2(devnull,2) <= -1) goto child_oops; - - if ((flags & QSE_PIO_INTONUL) || - (flags & QSE_PIO_OUTTONUL) || - (flags & QSE_PIO_ERRTONUL)) + pid = standard_fork_and_exec (pio, handle, ¶m, env); + if (pid <= -1) { - QSE_CLOSE (devnull); - devnull = -1; + free_param (pio, ¶m); + goto oops; } - if (flags & QSE_PIO_DROPIN) QSE_CLOSE(0); - if (flags & QSE_PIO_DROPOUT) QSE_CLOSE(1); - if (flags & QSE_PIO_DROPERR) QSE_CLOSE(2); + /* parent */ + free_param (pio, ¶m); + pio->child = pid; + #endif - /*if (make_param (pio, cmd, flags, ¶m) <= -1) goto child_oops;*/ - QSE_EXECVE (param.argv[0], param.argv, (env? qse_env_getarr(env): environ)); - free_param (pio, ¶m); - - child_oops: - if (devnull >= 0) QSE_CLOSE (devnull); - QSE_EXIT (128); } - /* parent */ - free_param (pio, ¶m); - pio->child = pid; - if (flags & QSE_PIO_WRITEIN) { /* @@ -1862,7 +1804,6 @@ create_process: QSE_CLOSE (handle[5]); handle[5] = QSE_PIO_HND_NIL; } - #endif if (((flags & QSE_PIO_INNOBLOCK) && set_pipe_nonblock(pio, handle[1], 1) <= -1) || @@ -1957,7 +1898,7 @@ oops: #elif defined(__OS2__) for (i = minidx; i < maxidx; i++) { - if (handle[i] != QSE_PIO_HND_NIL) DosClose (handle[i]); + if (handle[i] != QSE_PIO_HND_NIL) DosClose (handle[i]); } #elif defined(__DOS__) diff --git a/qse/lib/cmn/task.c b/qse/lib/cmn/task.c index c7cc027c..add98bb2 100644 --- a/qse/lib/cmn/task.c +++ b/qse/lib/cmn/task.c @@ -316,7 +316,7 @@ qse_task_slice_t* qse_task_create ( tmp = ((qse_uint8_t*)(slice + 1)) + stksize - QSE_SIZEOF(void*); tmp = (qse_uint8_t*)tmp - QSE_SIZEOF(void*); - *(void**)tmp = NULL; /* t1 */ + *(void**)tmp = QSE_NULL; /* t1 */ tmp = (qse_uint8_t*)tmp - QSE_SIZEOF(void*); *(void**)tmp = slice; /* t2 */ diff --git a/qse/lib/http/http.c b/qse/lib/http/http.c index 50a5c1c6..472a25d2 100644 --- a/qse/lib/http/http.c +++ b/qse/lib/http/http.c @@ -56,6 +56,7 @@ const qse_mchar_t* qse_httpstatustombs (int code) case 304: msg = QSE_MT("Not Modified"); break; case 305: msg = QSE_MT("Use Proxy"); break; case 307: msg = QSE_MT("Temporary Redirect"); break; + case 308: msg = QSE_MT("Permanent Redirect"); break; case 400: msg = QSE_MT("Bad Request"); break; case 401: msg = QSE_MT("Unauthorized"); break; diff --git a/qse/lib/http/httpd-cgi.c b/qse/lib/http/httpd-cgi.c index f2ffc69c..430c3fc4 100644 --- a/qse/lib/http/httpd-cgi.c +++ b/qse/lib/http/httpd-cgi.c @@ -44,6 +44,7 @@ struct task_cgi_arg_t qse_mcstr_t root; qse_mcstr_t shebang; int nph; + qse_httpd_fnc_t fnc; qse_htre_t* req; }; @@ -63,6 +64,7 @@ struct task_cgi_t qse_http_version_t version; int keepalive; /* taken from the request */ int nph; + qse_pio_fnc_t fnc; qse_htrd_t* script_htrd; qse_env_t* env; @@ -705,7 +707,7 @@ static int task_init_cgi ( qse_size_t len; const qse_mchar_t* ptr; const qse_htre_hdrval_t* tmp; - + cgi = (task_cgi_t*)qse_httpd_gettaskxtn (httpd, task); arg = (task_cgi_arg_t*)task->ctx; @@ -733,6 +735,12 @@ static int task_init_cgi ( cgi->keepalive = (arg->req->flags & QSE_HTRE_ATTR_KEEPALIVE); cgi->nph = arg->nph; cgi->req = QSE_NULL; + if (arg->fnc.ptr) + { + /* the function pointer is set */ + cgi->fnc.ptr = arg->fnc.ptr; + cgi->fnc.ctx = arg->fnc.ctx; + } content_length = 0; if (arg->req->state & QSE_HTRE_DISCARDED) goto done; @@ -1432,6 +1440,7 @@ static int task_main_cgi ( cgi_script_htrd_xtn_t* xtn; cgi->script_htrd = qse_htrd_open (httpd->mmgr, QSE_SIZEOF(cgi_script_htrd_xtn_t)); if (cgi->script_htrd == QSE_NULL) goto oops; + xtn = (cgi_script_htrd_xtn_t*) qse_htrd_getxtn (cgi->script_htrd); xtn->cgi = cgi; xtn->task = task; @@ -1460,22 +1469,30 @@ static int task_main_cgi ( if (httpd->opt.trait & QSE_HTTPD_CGINOCLOEXEC) pio_options |= QSE_PIO_NOCLOEXEC; - if (cgi->shebang[0] != QSE_MT('\0')) + if (cgi->fnc.ptr) { - const qse_mchar_t* tmp[4]; - tmp[0] = cgi->shebang; - tmp[1] = QSE_MT(" "); - tmp[2] = cgi->path; - tmp[3] = QSE_NULL; - xpath = qse_mbsadup (tmp, QSE_NULL, httpd->mmgr); - if (xpath == QSE_NULL) goto oops; + xpath = (qse_mchar_t*)&cgi->fnc; + pio_options |= QSE_PIO_FNCCMD; + } + else + { + if (cgi->shebang[0] != QSE_MT('\0')) + { + const qse_mchar_t* tmp[4]; + tmp[0] = cgi->shebang; + tmp[1] = QSE_MT(" "); + tmp[2] = cgi->path; + tmp[3] = QSE_NULL; + xpath = qse_mbsadup (tmp, QSE_NULL, httpd->mmgr); + if (xpath == QSE_NULL) goto oops; + } + else xpath = cgi->path; } - else xpath = cgi->path; x = qse_pio_init ( &cgi->pio, httpd->mmgr, (const qse_char_t*)xpath, cgi->env, pio_options); - if (xpath != cgi->path) QSE_MMGR_FREE (httpd->mmgr, xpath); + if (xpath != cgi->path && xpath != &cgi->fnc) QSE_MMGR_FREE (httpd->mmgr, xpath); if (x <= -1) { @@ -1539,6 +1556,12 @@ static int task_main_cgi ( } } } + else + { + /* no forwarding buffer. the request should not send any contents + * to the cgi script. close the input to the script */ + qse_pio_end (&cgi->pio, QSE_PIO_IN); + } task->main = cgi->nph? task_main_cgi_4_nph: task_main_cgi_2; return 1; @@ -1575,11 +1598,35 @@ qse_httpd_task_t* qse_httpd_entaskcgi ( task_cgi_arg_t arg; qse_httpd_rsrc_cgi_t rsrc; + QSE_MEMSET (&arg, 0, QSE_SIZEOF(arg)); + rsrc = *cgi; + if (rsrc.flags & QSE_HTTPD_RSRC_CGI_FNC) + { + /* rsrc.script must carry a pointer to qse_pio_fnc_t */ + if (rsrc.script == QSE_NULL || ((qse_pio_fnc_t*)rsrc.script)->ptr == QSE_NULL) + { + httpd->errnum = QSE_HTTPD_EINVAL; + return QSE_NULL; + } + + arg.fnc.ptr = (qse_httpd_fncptr_t)rsrc.path; + arg.fnc.ctx = (void*)rsrc.shebang; + + /* reset the script to an empty string for less interference + * with code handling normal script */ + rsrc.path = QSE_MT(""); + rsrc.shebang = QSE_MT(""); + } + else + { + QSE_ASSERT (rsrc.path != QSE_NULL); + if (rsrc.shebang == QSE_NULL) rsrc.shebang = QSE_MT(""); + } + if (rsrc.script == QSE_NULL) rsrc.script = qse_htre_getqpath(req); if (rsrc.suffix == QSE_NULL) rsrc.suffix = QSE_MT(""); if (rsrc.root == QSE_NULL) rsrc.root = QSE_MT(""); - if (rsrc.shebang == QSE_NULL) rsrc.shebang = QSE_MT(""); arg.path.ptr = (qse_mchar_t*)rsrc.path; arg.path.len = qse_mbslen(rsrc.path); @@ -1589,7 +1636,7 @@ qse_httpd_task_t* qse_httpd_entaskcgi ( arg.suffix.len = qse_mbslen(rsrc.suffix); arg.root.ptr = (qse_mchar_t*)rsrc.root; arg.root.len = qse_mbslen(rsrc.root); - arg.nph = rsrc.nph; + arg.nph = ((rsrc.flags & QSE_HTTPD_RSRC_CGI_NPH) != 0); arg.shebang.ptr = (qse_mchar_t*)rsrc.shebang; arg.shebang.len = qse_mbslen(rsrc.shebang); arg.req = req; diff --git a/qse/lib/http/httpd-proxy.c b/qse/lib/http/httpd-proxy.c index ccd0d62b..a2cf7c69 100644 --- a/qse/lib/http/httpd-proxy.c +++ b/qse/lib/http/httpd-proxy.c @@ -1983,7 +1983,7 @@ printf ("XXXXXXXXXXXXXXXXXXXXXXXXXX URL REWRITTEN TO [%s].....\n", new_url); { /* check if it begins with redirection code followed by a colon */ int redir_code = 0; - qse_httpd_status_reloc_t reloc; + qse_httpd_rsrc_reloc_t reloc; const qse_mchar_t* nuptr = new_url; do { @@ -1996,11 +1996,14 @@ printf ("XXXXXXXXXXXXXXXXXXXXXXXXXX URL REWRITTEN TO [%s].....\n", new_url); /* no colon is found after digits. it's probably a normal url */ goto normal_url; } - if (redir_code != 301 && redir_code != 302 && redir_code != 307) redir_code = 301; + if (redir_code != 301 && redir_code != 302 && redir_code != 303 && + redir_code != 307 && redir_code != 308) redir_code = 302; nuptr++; + /* relocation code is given explictly, no slash appending is needed. + * use qse_httpd_entask_status() rather than qse_httpd_entaskreloc(). */ + reloc.flags = 0; reloc.dst = nuptr; - reloc.redir = 0; /* don't want to append extra / */ if (qse_httpd_entask_status ( httpd, proxy->client, proxy->task, redir_code, &reloc, diff --git a/qse/lib/http/httpd-std.c b/qse/lib/http/httpd-std.c index 59ece36f..3fe3752b 100644 --- a/qse/lib/http/httpd-std.c +++ b/qse/lib/http/httpd-std.c @@ -1547,7 +1547,7 @@ static int file_stat ( * for a file. it is mainly used to get a file size and timestamps * of a regular file. so it should fail for a non-regular file. * note that STAT_REG is passed to stat_file for it */ - return stat_file (httpd, path, hst, STAT_REG); + return stat_file (httpd, path, hst, STAT_REG); } static int file_purge (qse_httpd_t* httpd, const qse_mchar_t* path) @@ -2444,10 +2444,13 @@ static void free_resource ( QSE_MMGR_FREE (httpd->mmgr, (qse_mchar_t*)target->u.cgi.suffix); if (target->u.cgi.script != qpath) QSE_MMGR_FREE (httpd->mmgr, (qse_mchar_t*)target->u.cgi.script); - if (target->u.cgi.path != qpath) - QSE_MMGR_FREE (httpd->mmgr, (qse_mchar_t*)target->u.cgi.path); - if (target->u.cgi.shebang) - QSE_MMGR_FREE (httpd->mmgr, (qse_mchar_t*)target->u.cgi.shebang); + if (!(target->u.cgi.flags & QSE_HTTPD_RSRC_CGI_FNC)) + { + if (target->u.cgi.path != qpath) + QSE_MMGR_FREE (httpd->mmgr, (qse_mchar_t*)target->u.cgi.path); + if (target->u.cgi.shebang) + QSE_MMGR_FREE (httpd->mmgr, (qse_mchar_t*)target->u.cgi.shebang); + } break; @@ -2457,8 +2460,8 @@ static void free_resource ( break; case QSE_HTTPD_RSRC_FILE: - if (target->u.cgi.path != qpath) - QSE_MMGR_FREE (httpd->mmgr, (qse_mchar_t*)target->u.cgi.path); + if (target->u.file.path != qpath) + QSE_MMGR_FREE (httpd->mmgr, (qse_mchar_t*)target->u.file.path); break; case QSE_HTTPD_RSRC_RELOC: @@ -2466,11 +2469,6 @@ static void free_resource ( QSE_MMGR_FREE (httpd->mmgr, (qse_mchar_t*)target->u.reloc.dst); break; - case QSE_HTTPD_RSRC_REDIR: - if (target->u.redir.dst != qpath) - QSE_MMGR_FREE (httpd->mmgr, (qse_mchar_t*)target->u.redir.dst); - break; - default: /* nothing to do */ break; @@ -2573,22 +2571,19 @@ static int attempt_cgi ( #endif /* create a relocation resource */ + QSE_MEMSET (target, 0, QSE_SIZEOF(*target)); target->type = QSE_HTTPD_RSRC_RELOC; target->u.reloc.dst = merge_paths (httpd, tmp->qpath, tmp->idxfile); if (target->u.reloc.dst == QSE_NULL) goto oops; + /* free tmp->xpath here upon success since it's not used for relocation. - * it is freed by the called upon failure so the 'oops' part don't free it */ + * upon failure, it is freed by the caller. so the 'oops' part + * of this function doesn't free it. */ QSE_MMGR_FREE (httpd->mmgr, tmp->xpath); return 1; } else script = (qse_mchar_t*)tmp->qpath; - if (cgi.shebang) - { - shebang = qse_mbsdup (cgi.shebang, httpd->mmgr); - if (shebang == QSE_NULL) goto oops; - } - goto bingo; } } @@ -2619,14 +2614,13 @@ static int attempt_cgi ( */ merge_paths_to_buf (httpd, tmp->root.u.path.val, tmp->qpath_rp, slash - tmp->qpath_rp, tmp->xpath); xpath_changed = 1; - + stx = stat_file (httpd, tmp->xpath, &st, 0); if (stx <= -1) { - /* stop at the current segment if stating fails. - * if the current semgment can't be stat-ed, it's not likely that - * the next segment can be successfully stat-ed */ - break; + /* instead of stopping here, let's give a non-existent + * segment to be a virtual cgi script(function pointer). */ + st.isdir = 0; } if (!st.isdir) @@ -2636,9 +2630,16 @@ static int attempt_cgi ( QSE_MEMSET (&qinfo, 0, QSE_SIZEOF(qinfo)); qinfo.req = req; qinfo.xpath = tmp->xpath; + qinfo.xpath_nx = (stx <= -1); if (server_xtn->query (httpd, client->server, QSE_HTTPD_SERVERSTD_CGI, &qinfo, &cgi) >= 0 && cgi.cgi) { + if (cgi.fncptr == QSE_NULL && stx <= -1) + { + /* normal cgi script must exist. */ + break; + } + /* the script name is composed of the orginal query path. * the pointer held in 'slash' is valid for tmp->qpath as * tmp->qpath_rp is at most the tail part of tmp->qpath. */ @@ -2646,23 +2647,57 @@ static int attempt_cgi ( suffix = qse_mbsdup (slash, httpd->mmgr); if (!script || !suffix) goto oops; - if (cgi.shebang) - { - shebang = qse_mbsdup (cgi.shebang, httpd->mmgr); - if (shebang == QSE_NULL) goto oops; - } - goto bingo; } } + + if (stx <= -1) + { + /* stop at the current segment if stat() fails. + * if the current segment can't be stat-ed, it's not likely that + * the next segment can be stat-ed successfully */ + break; + } } ptr = slash + 1; } else { - /* no more slash is found. the last segement doesn't have to be checked - * here since it's attempted by the caller. */ + /* no more slash is found. this is the last segment. + * the caller has called stat() against the last segment + * before having called this function. so it's known + * that the path disn't exist. + * + * however, a virtual cgi script may not exist. a check + * for it is still required here */ + + qse_httpd_serverstd_query_info_t qinfo; + + if (xpath_changed) + { + /* restore the tmp->xpath to the original value by + * combining the full path with the document root. */ + merge_paths_to_buf (httpd, tmp->root.u.path.val, tmp->qpath_rp, (qse_size_t)-1, tmp->xpath); + xpath_changed = 0; + } + + QSE_MEMSET (&qinfo, 0, QSE_SIZEOF(qinfo)); + qinfo.req = req; + qinfo.xpath = tmp->xpath; + qinfo.xpath_nx = 1; + + if (server_xtn->query (httpd, client->server, QSE_HTTPD_SERVERSTD_CGI, &qinfo, &cgi) >= 0 && cgi.cgi && cgi.fncptr) + { + /* virtual cgi script */ + script = qse_mbsdup (tmp->qpath, httpd->mmgr); + if (!script) goto oops; + suffix = QSE_NULL; + + goto bingo; + } + + /* not a virtual cgi script. just break */ break; } } @@ -2675,12 +2710,32 @@ static int attempt_cgi ( bingo: target->type = QSE_HTTPD_RSRC_CGI; - target->u.cgi.nph = cgi.nph; - target->u.cgi.path = tmp->xpath; + target->u.cgi.flags = 0; + if (cgi.nph) target->u.cgi.flags |= QSE_HTTPD_RSRC_CGI_NPH; + + if (cgi.fncptr) + { + /* the type casting here is guly */ + target->u.cgi.path = (qse_mchar_t*)cgi.fncptr; + target->u.cgi.shebang = cgi.shebang; + target->u.cgi.flags |= QSE_HTTPD_RSRC_CGI_FNC; + } + else + { + if (cgi.shebang) + { + shebang = qse_mbsdup (cgi.shebang, httpd->mmgr); + if (shebang == QSE_NULL) goto oops; + } + + target->u.cgi.path = tmp->xpath; + target->u.cgi.shebang = shebang; + } + target->u.cgi.script = script; target->u.cgi.suffix = suffix; target->u.cgi.root = tmp->root.u.path.val; - target->u.cgi.shebang = shebang; + return 1; oops: @@ -2739,6 +2794,13 @@ static int make_resource ( /* handle the request locally */ QSE_ASSERT (tmp.root.type == QSE_HTTPD_SERVERSTD_ROOT_PATH); +/***************************************************************************** + * BUG BUG BUG !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1 + * TODO: calling the realm query here is wrong especially if the prefix path is resolved to a cgi. + * for example, /abc/def/test.cgi/x/y/z, + * when this function queries for REALM, it's not known that /abc/def/test.cgi is a cgi script. + + *****************************************************************************/ if (server_xtn->query (httpd, client->server, QSE_HTTPD_SERVERSTD_REALM, &qinfo, &tmp.realm) <= -1 || server_xtn->query (httpd, client->server, QSE_HTTPD_SERVERSTD_INDEX, &qinfo, &tmp.index) <= -1) { @@ -2877,7 +2939,7 @@ auth_ok: tmp.xpath = tpath; tmp.idxfile = ptr; goto attempt_file; - } + } QSE_MMGR_FREE (httpd->mmgr, tpath); } @@ -2898,8 +2960,9 @@ auth_ok: { /* the query path doesn't end with a slash. so redirect it */ qse_htre_discardcontent (req); - target->type = QSE_HTTPD_RSRC_REDIR; - target->u.redir.dst = tmp.qpath; + target->type = QSE_HTTPD_RSRC_RELOC; + target->u.reloc.flags = QSE_HTTPD_RSRC_RELOC_APPENDSLASH | QSE_HTTPD_RSRC_RELOC_PERMANENT; + target->u.reloc.dst = tmp.qpath; /* free xpath since it won't be used */ QSE_MMGR_FREE (httpd->mmgr, tmp.xpath); } @@ -2929,7 +2992,16 @@ auth_ok: QSE_MMGR_FREE (httpd->mmgr, tmp.xpath); return -1; } - if (n >= 1) return 0; + if (n >= 1) + { + if (target->u.cgi.flags & QSE_HTTPD_RSRC_CGI_FNC) + { + /* tmp.xpath is not set to target->u.cgi.path when + * this flag is set. it must be deallocated */ + QSE_MMGR_FREE (httpd->mmgr, tmp.xpath); + } + return 0; + } qinfo.xpath = tmp.xpath; @@ -3015,8 +3087,8 @@ struct cgi_tab_t }; static struct cgi_tab_t cgitab[] = { - { QSE_MT(".cgi"), { 1, 0, QSE_NULL } }, - { QSE_MT(".nph"), { 1, 1, QSE_NULL } }, + { QSE_MT(".cgi"), { 1, 0, QSE_NULL, QSE_NULL } }, + { QSE_MT(".nph"), { 1, 1, QSE_NULL, QSE_NULL } }, }; static int query_server ( @@ -3071,12 +3143,18 @@ static int query_server ( case QSE_HTTPD_SERVERSTD_CGI: { qse_httpd_serverstd_cgi_t* cgi = (qse_httpd_serverstd_cgi_t*)result; - for (i = 0; i < QSE_COUNTOF(cgitab); i++) + + if (!qinfo->xpath_nx) { - if (qse_mbsend (qinfo->xpath, cgitab[i].suffix)) + /* this standard implementation supports a normal cgi script only */ + + for (i = 0; i < QSE_COUNTOF(cgitab); i++) { - QSE_MEMCPY (cgi, &cgitab[i].cgi, QSE_SIZEOF(*cgi)); - return 0; + if (qse_mbsend (qinfo->xpath, cgitab[i].suffix)) + { + QSE_MEMCPY (cgi, &cgitab[i].cgi, QSE_SIZEOF(*cgi)); + return 0; + } } } diff --git a/qse/lib/http/httpd-task.c b/qse/lib/http/httpd-task.c index ca92d501..b533b500 100644 --- a/qse/lib/http/httpd-task.c +++ b/qse/lib/http/httpd-task.c @@ -178,14 +178,16 @@ qse_httpd_task_t* qse_httpd_entask_status ( msg = qse_httpstatustombs (code); switch (code) { - case 301: - case 302: - case 307: + case 301: /* Moved Permanently */ + case 302: /* Found */ + case 303: /* See Other (since HTTP/1.1) */ + case 307: /* Temporary Redirect (since HTTP/1.1) */ + case 308: /* Permanent Redirect (Experimental RFC; RFC 7238) */ { - qse_httpd_status_reloc_t* reloc; - reloc = (qse_httpd_status_reloc_t*)extra; + qse_httpd_rsrc_reloc_t* reloc; + reloc = (qse_httpd_rsrc_reloc_t*)extra; extrapre = QSE_MT("Location: "); - extrapst = reloc->redir? QSE_MT("/\r\n"): QSE_MT("\r\n"); + extrapst = (reloc->flags & QSE_HTTPD_RSRC_RELOC_APPENDSLASH)? QSE_MT("/\r\n"): QSE_MT("\r\n"); extraval = reloc->dst; break; } @@ -276,31 +278,22 @@ qse_httpd_task_t* qse_httpd_entaskauth ( qse_httpd_task_t* qse_httpd_entaskreloc ( qse_httpd_t* httpd, qse_httpd_client_t* client, - qse_httpd_task_t* pred, const qse_mchar_t* dst, qse_htre_t* req) + qse_httpd_task_t* pred, const qse_httpd_rsrc_reloc_t* reloc, qse_htre_t* req) { - qse_httpd_status_reloc_t reloc; + int code; - reloc.dst = dst; - reloc.redir = 0; + if (reloc->flags & QSE_HTTPD_RSRC_RELOC_KEEPMETHOD) + { + code = (reloc->flags & QSE_HTTPD_RSRC_RELOC_PERMANENT)? 308: 307; + } + else + { + /* NOTE: 302 can be 303 for HTTP/1.1 */ + code = (reloc->flags & QSE_HTTPD_RSRC_RELOC_PERMANENT)? 301: 302; + } return qse_httpd_entask_status ( - httpd, client, pred, 301, (void*)&reloc, - qse_htre_getqmethodtype(req), - qse_htre_getversion(req), - (req->flags & QSE_HTRE_ATTR_KEEPALIVE)); -} - -qse_httpd_task_t* qse_httpd_entaskredir ( - qse_httpd_t* httpd, qse_httpd_client_t* client, - qse_httpd_task_t* pred, const qse_mchar_t* dst, qse_htre_t* req) -{ - qse_httpd_status_reloc_t reloc; - - reloc.dst = dst; - reloc.redir = 1; - - return qse_httpd_entask_status ( - httpd, client, pred, 301, (void*)&reloc, + httpd, client, pred, code, (void*)reloc, qse_htre_getqmethodtype(req), qse_htre_getversion(req), (req->flags & QSE_HTRE_ATTR_KEEPALIVE)); @@ -403,11 +396,7 @@ qse_httpd_task_t* qse_httpd_entaskrsrc ( break; case QSE_HTTPD_RSRC_RELOC: - task = qse_httpd_entaskreloc (httpd, client, pred, rsrc->u.reloc.dst, req); - break; - - case QSE_HTTPD_RSRC_REDIR: - task = qse_httpd_entaskredir (httpd, client, pred, rsrc->u.redir.dst, req); + task = qse_httpd_entaskreloc (httpd, client, pred, &rsrc->u.reloc, req); break; case QSE_HTTPD_RSRC_TEXT: diff --git a/qse/lib/http/httpd.h b/qse/lib/http/httpd.h index 9e25f093..6770c200 100644 --- a/qse/lib/http/httpd.h +++ b/qse/lib/http/httpd.h @@ -100,13 +100,6 @@ struct qse_httpd_real_task_t qse_httpd_real_task_t* next; }; -typedef struct qse_httpd_status_reloc_t qse_httpd_status_reloc_t; -struct qse_httpd_status_reloc_t -{ - const qse_mchar_t* dst; - int redir; -}; - #define MAX_SEND_SIZE 4096 #define MAX_RECV_SIZE 4096