| /* Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "../../APR/PerlIO/modperl_apr_perlio.h" |
| |
| #ifndef MP_SOURCE_SCAN |
| #include "apr_optional.h" |
| |
| static APR_OPTIONAL_FN_TYPE(modperl_apr_perlio_apr_file_to_glob) *apr_file_to_glob; |
| #endif |
| |
| /* XXX: probably needs a lot more error checkings */ |
| |
| typedef struct { |
| apr_int32_t in_pipe; |
| apr_int32_t out_pipe; |
| apr_int32_t err_pipe; |
| apr_cmdtype_e cmd_type; |
| } exec_info; |
| |
| #undef FAILED /* win32 defines a macro with this name */ |
| #define FAILED(command) ((rc = command) != APR_SUCCESS) |
| |
| #define SET_TIMEOUT(fp) \ |
| apr_file_pipe_timeout_set(fp, \ |
| (int)(apr_time_from_sec(r->server->timeout))) |
| |
| static int modperl_spawn_proc_prog(pTHX_ |
| request_rec *r, |
| const char *command, |
| const char ***argv, |
| apr_file_t **script_in, |
| apr_file_t **script_out, |
| apr_file_t **script_err) |
| { |
| exec_info e_info; |
| apr_pool_t *p; |
| const char * const *env; |
| |
| apr_procattr_t *procattr; |
| apr_proc_t *procnew; |
| apr_status_t rc = APR_SUCCESS; |
| |
| e_info.in_pipe = APR_CHILD_BLOCK; |
| e_info.out_pipe = APR_CHILD_BLOCK; |
| e_info.err_pipe = APR_CHILD_BLOCK; |
| e_info.cmd_type = APR_PROGRAM; |
| |
| p = r->main ? r->main->pool : r->pool; |
| |
| *script_out = *script_in = *script_err = NULL; |
| |
| env = (const char * const *)ap_create_environment(p, r->subprocess_env); |
| |
| if (FAILED(apr_procattr_create(&procattr, p)) || |
| FAILED(apr_procattr_io_set(procattr, e_info.in_pipe, |
| e_info.out_pipe, e_info.err_pipe)) || |
| FAILED(apr_procattr_dir_set(procattr, |
| ap_make_dirstr_parent(r->pool, |
| r->filename))) || |
| FAILED(apr_procattr_cmdtype_set(procattr, e_info.cmd_type))) |
| { |
| /* Something bad happened, tell the world. */ |
| ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, |
| "couldn't set child process attributes: %s", |
| r->filename); |
| return rc; |
| } |
| |
| procnew = apr_pcalloc(p, sizeof(*procnew)); |
| if (FAILED(ap_os_create_privileged_process(r, procnew, command, |
| *argv, env, procattr, p))) |
| { |
| /* Bad things happened. Everyone should have cleaned up. */ |
| ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, |
| "couldn't create child process: %d: %s", |
| rc, r->filename); |
| return rc; |
| } |
| |
| apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT); |
| |
| if (!(*script_in = procnew->in)) { |
| Perl_croak(aTHX_ "broken program-in stream"); |
| return APR_EBADF; |
| } |
| SET_TIMEOUT(*script_in); |
| |
| if (!(*script_out = procnew->out)) { |
| Perl_croak(aTHX_ "broken program-out stream"); |
| return APR_EBADF; |
| } |
| SET_TIMEOUT(*script_out); |
| |
| if (!(*script_err = procnew->err)) { |
| Perl_croak(aTHX_ "broken program-err stream"); |
| return APR_EBADF; |
| } |
| SET_TIMEOUT(*script_err); |
| |
| return rc; |
| } |
| |
| #define PUSH_FILE_GLOB(fp, type) \ |
| PUSHs(apr_file_to_glob(aTHX_ fp, r->pool, type)) |
| |
| #define PUSH_FILE_GLOB_READ(fp) \ |
| PUSH_FILE_GLOB(fp, MODPERL_APR_PERLIO_HOOK_READ) |
| |
| #define PUSH_FILE_GLOB_WRITE(fp) \ |
| PUSH_FILE_GLOB(fp, MODPERL_APR_PERLIO_HOOK_WRITE) |
| |
| #define CLOSE_SCRIPT_STD(stream) \ |
| rc = apr_file_close(stream); \ |
| if (rc != APR_SUCCESS) { \ |
| XSRETURN_UNDEF; \ |
| } |
| |
| MP_STATIC XS(MPXS_modperl_spawn_proc_prog) |
| { |
| dXSARGS; |
| const char *usage = "Usage: spawn_proc_prog($r, $command, [\\@argv])"; |
| |
| if (items < 2) { |
| Perl_croak(aTHX_ "%s", usage); |
| } |
| |
| SP -= items; |
| { |
| apr_file_t *script_in, *script_out, *script_err; |
| apr_status_t rc; |
| const char **argv; |
| int i=0; |
| AV *av_argv = (AV *)NULL; |
| I32 len=-1, av_items=0; |
| request_rec *r = modperl_xs_sv2request_rec(aTHX_ ST(0), NULL, cv); |
| const char *command = (const char *)SvPV_nolen(ST(1)); |
| |
| if (items == 3) { |
| if (SvROK(ST(2)) && SvTYPE(SvRV(ST(2))) == SVt_PVAV) { |
| av_argv = (AV*)SvRV(ST(2)); |
| len = AvFILLp(av_argv); |
| av_items = len+1; |
| } |
| else { |
| Perl_croak(aTHX_ "%s", usage); |
| } |
| } |
| |
| /* ap_os_create_privileged_process expects ARGV as char |
| * **argv, with terminating NULL and the program itself as a |
| * first item. |
| */ |
| argv = apr_palloc(r->pool, (av_items + 2) * sizeof(char *)); |
| argv[0] = command; |
| if (av_argv) { |
| for (i = 0; i <= len; i++) { |
| argv[i+1] = (const char *)SvPV_nolen(AvARRAY(av_argv)[i]); |
| } |
| } |
| argv[i+1] = NULL; |
| #if 0 |
| for (i=0; i<=len+2; i++) { |
| Perl_warn(aTHX_ "arg: %d %s\n", |
| i, argv[i] ? argv[i] : "NULL"); |
| } |
| #endif |
| rc = modperl_spawn_proc_prog(aTHX_ r, command, &argv, |
| &script_in, &script_out, |
| &script_err); |
| |
| if (rc == APR_SUCCESS) { |
| /* XXX: apr_file_to_glob should be set once in the BOOT: section */ |
| apr_file_to_glob = |
| APR_RETRIEVE_OPTIONAL_FN(modperl_apr_perlio_apr_file_to_glob); |
| |
| if (GIMME_V == G_VOID) { |
| CLOSE_SCRIPT_STD(script_in); |
| CLOSE_SCRIPT_STD(script_out); |
| CLOSE_SCRIPT_STD(script_err); |
| XSRETURN_EMPTY; |
| } |
| else if (GIMME_V == G_SCALAR) { |
| /* XXX: need to do lots of error checking before |
| * putting the object on the stack |
| */ |
| EXTEND(SP, 1); |
| PUSH_FILE_GLOB_READ(script_out); |
| CLOSE_SCRIPT_STD(script_in); |
| CLOSE_SCRIPT_STD(script_err); |
| } |
| else { |
| EXTEND(SP, 3); |
| PUSH_FILE_GLOB_WRITE(script_in); |
| PUSH_FILE_GLOB_READ(script_out); |
| PUSH_FILE_GLOB_READ(script_err); |
| } |
| } |
| else { |
| XSRETURN_UNDEF; |
| } |
| } |
| |
| PUTBACK; |
| } |
| |
| /* |
| * Local Variables: |
| * c-basic-offset: 4 |
| * indent-tabs-mode: nil |
| * End: |
| */ |