From 30d479c28c831a0d4f1fdb54a9e346b0fc176be1 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 08:23:09 +0200 Subject: Adding upstream version 1.7.2. Signed-off-by: Daniel Baumann --- threadproc/beos/apr_proc_stub.c | 76 +++ threadproc/beos/proc.c | 454 ++++++++++++++ threadproc/beos/thread.c | 237 +++++++ threadproc/beos/threadpriv.c | 180 ++++++ threadproc/beos/threadproc_common.c | 21 + threadproc/netware/proc.c | 515 ++++++++++++++++ threadproc/netware/procsup.c | 102 +++ threadproc/netware/signals.c | 81 +++ threadproc/netware/thread.c | 254 ++++++++ threadproc/netware/threadpriv.c | 102 +++ threadproc/os2/proc.c | 672 ++++++++++++++++++++ threadproc/os2/signals.c | 1 + threadproc/os2/thread.c | 260 ++++++++ threadproc/os2/threadpriv.c | 88 +++ threadproc/unix/proc.c | 739 ++++++++++++++++++++++ threadproc/unix/procsup.c | 104 ++++ threadproc/unix/signals.c | 481 +++++++++++++++ threadproc/unix/thread.c | 334 ++++++++++ threadproc/unix/threadpriv.c | 129 ++++ threadproc/win32/proc.c | 1158 +++++++++++++++++++++++++++++++++++ threadproc/win32/signals.c | 67 ++ threadproc/win32/thread.c | 281 +++++++++ threadproc/win32/threadpriv.c | 101 +++ 23 files changed, 6437 insertions(+) create mode 100644 threadproc/beos/apr_proc_stub.c create mode 100644 threadproc/beos/proc.c create mode 100644 threadproc/beos/thread.c create mode 100644 threadproc/beos/threadpriv.c create mode 100644 threadproc/beos/threadproc_common.c create mode 100644 threadproc/netware/proc.c create mode 100644 threadproc/netware/procsup.c create mode 100644 threadproc/netware/signals.c create mode 100644 threadproc/netware/thread.c create mode 100644 threadproc/netware/threadpriv.c create mode 100644 threadproc/os2/proc.c create mode 100644 threadproc/os2/signals.c create mode 100644 threadproc/os2/thread.c create mode 100644 threadproc/os2/threadpriv.c create mode 100644 threadproc/unix/proc.c create mode 100644 threadproc/unix/procsup.c create mode 100644 threadproc/unix/signals.c create mode 100644 threadproc/unix/thread.c create mode 100644 threadproc/unix/threadpriv.c create mode 100644 threadproc/win32/proc.c create mode 100644 threadproc/win32/signals.c create mode 100644 threadproc/win32/thread.c create mode 100644 threadproc/win32/threadpriv.c (limited to 'threadproc') diff --git a/threadproc/beos/apr_proc_stub.c b/threadproc/beos/apr_proc_stub.c new file mode 100644 index 0000000..011d793 --- /dev/null +++ b/threadproc/beos/apr_proc_stub.c @@ -0,0 +1,76 @@ +/* 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 +#include +#include +#include + +struct pipefd { + int in; + int out; + int err; +}; + +int main(int argc, char *argv[]) { +/* we expect the following... + * + * argv[0] = this stub + * argv[1] = directory to run in... + * argv[2] = progname to execute + * rest of arguments to be passed to program + */ + char *progname = argv[2]; + char *directory = argv[1]; + struct pipefd *pfd; + thread_id sender; + void *buffer; + char ** newargs; + int i = 0; + + newargs = (char**)malloc(sizeof(char*) * (argc - 1)); + + buffer = (void*)malloc(sizeof(struct pipefd)); + /* this will block until we get the data */ + receive_data(&sender, buffer, sizeof(struct pipefd)); + pfd = (struct pipefd*)buffer; + + if (pfd->in > STDERR_FILENO) { + if (dup2(pfd->in, STDIN_FILENO) != STDIN_FILENO) return (-1); + close (pfd->in); + } + if (pfd->out > STDERR_FILENO) { + if (dup2(pfd->out, STDOUT_FILENO) != STDOUT_FILENO) return (-1); + close (pfd->out); + } + if (pfd->err > STDERR_FILENO) { + if (dup2(pfd->err, STDERR_FILENO) != STDERR_FILENO) return (-1); + close (pfd->err); + } + + for (i=3;i<=argc;i++){ + newargs[i-3] = argv[i]; + } + + /* tell the caller we're OK to start */ + send_data(sender,1,NULL,0); + + if (directory != NULL) + chdir(directory); + execve (progname, newargs, environ); + + return (-1); +} diff --git a/threadproc/beos/proc.c b/threadproc/beos/proc.c new file mode 100644 index 0000000..e369808 --- /dev/null +++ b/threadproc/beos/proc.c @@ -0,0 +1,454 @@ +/* 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_arch_threadproc.h" +#include "apr_strings.h" + +/* Heavy on no'ops, here's what we want to pass if there is APR_NO_FILE + * requested for a specific child handle; + */ +static apr_file_t no_file = { NULL, -1, }; + +struct send_pipe { + int in; + int out; + int err; +}; + +APR_DECLARE(apr_status_t) apr_procattr_create(apr_procattr_t **new, apr_pool_t *pool) +{ + (*new) = (apr_procattr_t *)apr_palloc(pool, + sizeof(apr_procattr_t)); + + if ((*new) == NULL) { + return APR_ENOMEM; + } + (*new)->pool = pool; + (*new)->parent_in = NULL; + (*new)->child_in = NULL; + (*new)->parent_out = NULL; + (*new)->child_out = NULL; + (*new)->parent_err = NULL; + (*new)->child_err = NULL; + (*new)->currdir = NULL; + (*new)->cmdtype = APR_PROGRAM; + (*new)->detached = 0; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_io_set(apr_procattr_t *attr, + apr_int32_t in, + apr_int32_t out, + apr_int32_t err) +{ + apr_status_t rv; + + if ((in != APR_NO_PIPE) && (in != APR_NO_FILE)) { + /* APR_CHILD_BLOCK maps to APR_WRITE_BLOCK, while + * APR_PARENT_BLOCK maps to APR_READ_BLOCK, so transpose + * the CHILD/PARENT blocking flags for the stdin pipe. + * stdout/stderr map to the correct mode by default. + */ + if (in == APR_CHILD_BLOCK) + in = APR_READ_BLOCK; + else if (in == APR_PARENT_BLOCK) + in = APR_WRITE_BLOCK; + + if ((rv = apr_file_pipe_create_ex(&attr->child_in, &attr->parent_in, + in, attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_in); + if (rv != APR_SUCCESS) + return rv; + } + else if (in == APR_NO_FILE) + attr->child_in = &no_file; + + if ((out != APR_NO_PIPE) && (out != APR_NO_FILE)) { + if ((rv = apr_file_pipe_create_ex(&attr->parent_out, &attr->child_out, + out, attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_out); + if (rv != APR_SUCCESS) + return rv; + } + else if (out == APR_NO_FILE) + attr->child_out = &no_file; + + if ((err != APR_NO_PIPE) && (err != APR_NO_FILE)) { + if ((rv = apr_file_pipe_create_ex(&attr->parent_err, &attr->child_err, + err, attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_err); + if (rv != APR_SUCCESS) + return rv; + } + else if (err == APR_NO_FILE) + attr->child_err = &no_file; + + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_dir_set(apr_procattr_t *attr, + const char *dir) +{ + char * cwd; + if (dir[0] != '/') { + cwd = (char*)malloc(sizeof(char) * PATH_MAX); + getcwd(cwd, PATH_MAX); + attr->currdir = (char *)apr_pstrcat(attr->pool, cwd, "/", dir, NULL); + free(cwd); + } else { + attr->currdir = (char *)apr_pstrdup(attr->pool, dir); + } + if (attr->currdir) { + return APR_SUCCESS; + } + return APR_ENOMEM; +} + +APR_DECLARE(apr_status_t) apr_procattr_cmdtype_set(apr_procattr_t *attr, + apr_cmdtype_e cmd) +{ + attr->cmdtype = cmd; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_detach_set(apr_procattr_t *attr, apr_int32_t detach) +{ + attr->detached = detach; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_proc_fork(apr_proc_t *proc, apr_pool_t *pool) +{ + int pid; + + if ((pid = fork()) < 0) { + return errno; + } + else if (pid == 0) { + /* This is really ugly... + * The semantics of BeOS's fork() are that areas (used for shared + * memory) get COW'd :-( The only way we can make shared memory + * work across fork() is therefore to find any areas that have + * been created and then clone them into our address space. + * Thankfully only COW'd areas have the lock variable set at + * anything but 0, so we can use that to find the areas we need to + * copy. Of course what makes it even worse is that the loop through + * the area's will go into an infinite loop, eating memory and then + * eventually segfault unless we know when we reach then end of the + * "original" areas and stop. Why? Well, we delete the area and then + * add another to the end of the list... + */ + area_info ai; + int32 cookie = 0; + area_id highest = 0; + + while (get_next_area_info(0, &cookie, &ai) == B_OK) + if (ai.area > highest) + highest = ai.area; + cookie = 0; + while (get_next_area_info(0, &cookie, &ai) == B_OK) { + if (ai.area > highest) + break; + if (ai.lock > 0) { + area_id original = find_area(ai.name); + delete_area(ai.area); + clone_area(ai.name, &ai.address, B_CLONE_ADDRESS, + ai.protection, original); + } + } + + proc->pid = pid; + proc->in = NULL; + proc->out = NULL; + proc->err = NULL; + return APR_INCHILD; + } + proc->pid = pid; + proc->in = NULL; + proc->out = NULL; + proc->err = NULL; + return APR_INPARENT; +} + +APR_DECLARE(apr_status_t) apr_procattr_child_errfn_set(apr_procattr_t *attr, + apr_child_errfn_t *errfn) +{ + /* won't ever be called on this platform, so don't save the function pointer */ + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_error_check_set(apr_procattr_t *attr, + apr_int32_t chk) +{ + /* won't ever be used on this platform, so don't save the flag */ + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_addrspace_set(apr_procattr_t *attr, + apr_int32_t addrspace) +{ + /* won't ever be used on this platform, so don't save the flag */ + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *new, const char *progname, + const char * const *args, + const char * const *env, + apr_procattr_t *attr, + apr_pool_t *pool) +{ + int i=0,nargs=0; + char **newargs = NULL; + thread_id newproc, sender; + struct send_pipe *sp; + char * dir = NULL; + + sp = (struct send_pipe *)apr_palloc(pool, sizeof(struct send_pipe)); + + new->in = attr->parent_in; + new->err = attr->parent_err; + new->out = attr->parent_out; + sp->in = attr->child_in ? attr->child_in->filedes : FILENO_STDIN; + sp->out = attr->child_out ? attr->child_out->filedes : FILENO_STDOUT; + sp->err = attr->child_err ? attr->child_err->filedes : FILENO_STDERR; + + i = 0; + while (args && args[i]) { + i++; + } + + newargs = (char**)malloc(sizeof(char *) * (i + 4)); + newargs[0] = strdup("/boot/home/config/bin/apr_proc_stub"); + if (attr->currdir == NULL) { + /* we require the directory , so use a temp. variable */ + dir = malloc(sizeof(char) * PATH_MAX); + getcwd(dir, PATH_MAX); + newargs[1] = strdup(dir); + free(dir); + } else { + newargs[1] = strdup(attr->currdir); + } + newargs[2] = strdup(progname); + i=0;nargs = 3; + + while (args && args[i]) { + newargs[nargs] = strdup(args[i]); + i++;nargs++; + } + newargs[nargs] = NULL; + + /* ### we should be looking at attr->cmdtype in here... */ + + newproc = load_image(nargs, (const char**)newargs, (const char**)env); + + /* load_image copies the data so now we can free it... */ + while (--nargs >= 0) + free (newargs[nargs]); + free(newargs); + + if (newproc < B_NO_ERROR) { + return errno; + } + + resume_thread(newproc); + + if (attr->child_in && (attr->child_in->filedes != -1)) { + apr_file_close(attr->child_in); + } + if (attr->child_out && (attr->child_in->filedes != -1)) { + apr_file_close(attr->child_out); + } + if (attr->child_err && (attr->child_in->filedes != -1)) { + apr_file_close(attr->child_err); + } + + send_data(newproc, 0, (void*)sp, sizeof(struct send_pipe)); + new->pid = newproc; + + /* before we go charging on we need the new process to get to a + * certain point. When it gets there it'll let us know and we + * can carry on. */ + receive_data(&sender, (void*)NULL,0); + + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_proc_wait_all_procs(apr_proc_t *proc, + int *exitcode, + apr_exit_why_e *exitwhy, + apr_wait_how_e waithow, + apr_pool_t *p) +{ + proc->pid = -1; + return apr_proc_wait(proc, exitcode, exitwhy, waithow); +} + +APR_DECLARE(apr_status_t) apr_proc_wait(apr_proc_t *proc, + int *exitcode, + apr_exit_why_e *exitwhy, + apr_wait_how_e waithow) +{ + pid_t pstatus; + int waitpid_options = WUNTRACED; + int exit_int; + int ignore; + apr_exit_why_e ignorewhy; + + if (exitcode == NULL) { + exitcode = &ignore; + } + if (exitwhy == NULL) { + exitwhy = &ignorewhy; + } + + if (waithow != APR_WAIT) { + waitpid_options |= WNOHANG; + } + + if ((pstatus = waitpid(proc->pid, &exit_int, waitpid_options)) > 0) { + proc->pid = pstatus; + if (WIFEXITED(exit_int)) { + *exitwhy = APR_PROC_EXIT; + *exitcode = WEXITSTATUS(exit_int); + } + else if (WIFSIGNALED(exit_int)) { + *exitwhy = APR_PROC_SIGNAL; + *exitcode = WTERMSIG(exit_int); + } + else { + + /* unexpected condition */ + return APR_EGENERAL; + } + return APR_CHILD_DONE; + } + else if (pstatus == 0) { + return APR_CHILD_NOTDONE; + } + + return errno; +} + +APR_DECLARE(apr_status_t) apr_procattr_child_in_set(apr_procattr_t *attr, apr_file_t *child_in, + apr_file_t *parent_in) +{ + apr_status_t rv; + + if (attr->child_in == NULL && attr->parent_in == NULL + && child_in == NULL && parent_in == NULL) + if ((rv = apr_file_pipe_create(&attr->child_in, &attr->parent_in, + attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_in); + + if (child_in != NULL && rv == APR_SUCCESS) { + if (attr->child_in && (attr->child_in->filedes != -1)) + rv = apr_file_dup2(attr->child_in, child_in, attr->pool); + else { + attr->child_in = NULL; + if ((rv = apr_file_dup(&attr->child_in, child_in, attr->pool)) + == APR_SUCCESS) + rv = apr_file_inherit_set(attr->child_in); + } + } + + if (parent_in != NULL && rv == APR_SUCCESS) + rv = apr_file_dup(&attr->parent_in, parent_in, attr->pool); + + return rv; +} + +APR_DECLARE(apr_status_t) apr_procattr_child_out_set(apr_procattr_t *attr, apr_file_t *child_out, + apr_file_t *parent_out) +{ + apr_status_t rv; + + if (attr->child_out == NULL && attr->parent_out == NULL + && child_out == NULL && parent_out == NULL) + if ((rv = apr_file_pipe_create(&attr->parent_out, &attr->child_out, + attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_out); + + if (child_out != NULL && rv == APR_SUCCESS) { + if (attr->child_out && (attr->child_out->filedes != -1)) + rv = apr_file_dup2(attr->child_out, child_out, attr->pool); + else { + attr->child_out = NULL; + if ((rv = apr_file_dup(&attr->child_out, child_out, attr->pool)) + == APR_SUCCESS) + rv = apr_file_inherit_set(attr->child_out); + } + } + + if (parent_out != NULL && rv == APR_SUCCESS) + rv = apr_file_dup(&attr->parent_out, parent_out, attr->pool); + + return rv; +} + +APR_DECLARE(apr_status_t) apr_procattr_child_err_set(apr_procattr_t *attr, apr_file_t *child_err, + apr_file_t *parent_err) +{ + apr_status_t rv; + + if (attr->child_err == NULL && attr->parent_err == NULL + && child_err == NULL && parent_err == NULL) + if ((rv = apr_file_pipe_create(&attr->parent_err, &attr->child_err, + attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_err); + + if (child_err != NULL && rv == APR_SUCCESS) { + if (attr->child_err && (attr->child_err->filedes != -1)) + rv = apr_file_dup2(attr->child_err, child_err, attr->pool); + else { + attr->child_err = NULL; + if ((rv = apr_file_dup(&attr->child_err, child_err, attr->pool)) + == APR_SUCCESS) + rv = apr_file_inherit_set(attr->child_err); + } + } + + if (parent_err != NULL && rv == APR_SUCCESS) + rv = apr_file_dup(&attr->parent_err, parent_err, attr->pool); + + return rv; +} + +APR_DECLARE(apr_status_t) apr_procattr_limit_set(apr_procattr_t *attr, apr_int32_t what, + void *limit) +{ + return APR_ENOTIMPL; +} + +APR_DECLARE(apr_status_t) apr_procattr_user_set(apr_procattr_t *attr, + const char *username, + const char *password) +{ + return APR_ENOTIMPL; +} + +APR_DECLARE(apr_status_t) apr_procattr_group_set(apr_procattr_t *attr, + const char *groupname) +{ + return APR_ENOTIMPL; +} + +APR_DECLARE(apr_status_t) apr_procattr_perms_set_register(apr_procattr_t *attr, + apr_perms_setfn_t *perms_set_fn, + void *data, + apr_fileperms_t perms) +{ + return APR_ENOTIMPL; +} diff --git a/threadproc/beos/thread.c b/threadproc/beos/thread.c new file mode 100644 index 0000000..8d83839 --- /dev/null +++ b/threadproc/beos/thread.c @@ -0,0 +1,237 @@ +/* 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_arch_threadproc.h" +#include "apr_portable.h" + +APR_DECLARE(apr_status_t) apr_threadattr_create(apr_threadattr_t **new, apr_pool_t *pool) +{ + (*new) = (apr_threadattr_t *)apr_palloc(pool, + sizeof(apr_threadattr_t)); + + if ((*new) == NULL) { + return APR_ENOMEM; + } + + (*new)->pool = pool; + (*new)->attr = (int32)B_NORMAL_PRIORITY; + + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_threadattr_detach_set(apr_threadattr_t *attr, apr_int32_t on) +{ + if (on == 1){ + attr->detached = 1; + } else { + attr->detached = 0; + } + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_threadattr_detach_get(apr_threadattr_t *attr) +{ + if (attr->detached == 1){ + return APR_DETACH; + } + return APR_NOTDETACH; +} + +APR_DECLARE(apr_status_t) apr_threadattr_stacksize_set(apr_threadattr_t *attr, + apr_size_t stacksize) +{ + return APR_ENOTIMPL; +} + +APR_DECLARE(apr_status_t) apr_threadattr_guardsize_set(apr_threadattr_t *attr, + apr_size_t size) +{ + return APR_ENOTIMPL; +} + +static void *dummy_worker(void *opaque) +{ + apr_thread_t *thd = (apr_thread_t*)opaque; + return thd->func(thd, thd->data); +} + +APR_DECLARE(apr_status_t) apr_thread_create(apr_thread_t **new, apr_threadattr_t *attr, + apr_thread_start_t func, void *data, + apr_pool_t *pool) +{ + int32 temp; + apr_status_t stat; + + (*new) = (apr_thread_t *)apr_palloc(pool, sizeof(apr_thread_t)); + if ((*new) == NULL) { + return APR_ENOMEM; + } + + (*new)->data = data; + (*new)->func = func; + (*new)->exitval = -1; + + /* First we create the new thread...*/ + if (attr) + temp = attr->attr; + else + temp = B_NORMAL_PRIORITY; + + stat = apr_pool_create(&(*new)->pool, pool); + if (stat != APR_SUCCESS) { + return stat; + } + + (*new)->td = spawn_thread((thread_func)dummy_worker, + "apr thread", + temp, + (*new)); + + /* Now we try to run it...*/ + if (resume_thread((*new)->td) == B_NO_ERROR) { + return APR_SUCCESS; + } + else { + return errno; + } +} + +APR_DECLARE(apr_os_thread_t) apr_os_thread_current(void) +{ + return find_thread(NULL); +} + +int apr_os_thread_equal(apr_os_thread_t tid1, apr_os_thread_t tid2) +{ + return tid1 == tid2; +} + +APR_DECLARE(apr_status_t) apr_thread_exit(apr_thread_t *thd, apr_status_t retval) +{ + apr_pool_destroy(thd->pool); + thd->exitval = retval; + exit_thread ((status_t)(retval)); + /* This will never be reached... */ + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_thread_join(apr_status_t *retval, apr_thread_t *thd) +{ + status_t rv = 0, ret; + ret = wait_for_thread(thd->td, &rv); + if (ret == B_NO_ERROR) { + *retval = rv; + return APR_SUCCESS; + } + else { + /* if we've missed the thread's death, did we set an exit value prior + * to it's demise? If we did return that. + */ + if (thd->exitval != -1) { + *retval = thd->exitval; + return APR_SUCCESS; + } else + return ret; + } +} + +APR_DECLARE(apr_status_t) apr_thread_detach(apr_thread_t *thd) +{ + if (suspend_thread(thd->td) == B_NO_ERROR){ + return APR_SUCCESS; + } + else { + return errno; + } +} + +void apr_thread_yield() +{ +} + +APR_DECLARE(apr_status_t) apr_thread_data_get(void **data, const char *key, apr_thread_t *thread) +{ + return apr_pool_userdata_get(data, key, thread->pool); +} + +APR_DECLARE(apr_status_t) apr_thread_data_set(void *data, const char *key, + apr_status_t (*cleanup) (void *), + apr_thread_t *thread) +{ + return apr_pool_userdata_set(data, key, cleanup, thread->pool); +} + +APR_DECLARE(apr_status_t) apr_os_thread_get(apr_os_thread_t **thethd, apr_thread_t *thd) +{ + *thethd = &thd->td; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_os_thread_put(apr_thread_t **thd, apr_os_thread_t *thethd, + apr_pool_t *pool) +{ + if (pool == NULL) { + return APR_ENOPOOL; + } + if ((*thd) == NULL) { + (*thd) = (apr_thread_t *)apr_pcalloc(pool, sizeof(apr_thread_t)); + (*thd)->pool = pool; + } + (*thd)->td = *thethd; + return APR_SUCCESS; +} + +static apr_status_t thread_once_cleanup(void *vcontrol) +{ + apr_thread_once_t *control = (apr_thread_once_t *)vcontrol; + + if (control->sem) { + release_sem(control->sem); + delete_sem(control->sem); + } + + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_thread_once_init(apr_thread_once_t **control, + apr_pool_t *p) +{ + int rc; + *control = (apr_thread_once_t *)apr_pcalloc(p, sizeof(apr_thread_once_t)); + (*control)->hit = 0; /* we haven't done it yet... */ + rc = ((*control)->sem = create_sem(1, "thread_once")); + if (rc < 0) + return rc; + + apr_pool_cleanup_register(p, control, thread_once_cleanup, apr_pool_cleanup_null); + return APR_SUCCESS; +} + + + +APR_DECLARE(apr_status_t) apr_thread_once(apr_thread_once_t *control, + void (*func)(void)) +{ + if (!control->hit) { + if (acquire_sem(control->sem) == B_OK) { + control->hit = 1; + func(); + } + } + return APR_SUCCESS; +} + +APR_POOL_IMPLEMENT_ACCESSOR(thread) diff --git a/threadproc/beos/threadpriv.c b/threadproc/beos/threadpriv.c new file mode 100644 index 0000000..442235f --- /dev/null +++ b/threadproc/beos/threadpriv.c @@ -0,0 +1,180 @@ +/* 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_arch_threadproc.h" + +static struct beos_key key_table[BEOS_MAX_DATAKEYS]; +static struct beos_private_data *beos_data[BEOS_MAX_DATAKEYS]; +static sem_id lock; + +APR_DECLARE(apr_status_t) apr_threadkey_private_create(apr_threadkey_t **key, + void (*dest)(void *), apr_pool_t *pool) +{ + (*key) = (apr_threadkey_t *)apr_palloc(pool, sizeof(apr_threadkey_t)); + if ((*key) == NULL) { + return APR_ENOMEM; + } + + (*key)->pool = pool; + + acquire_sem(lock); + for ((*key)->key=0; (*key)->key < BEOS_MAX_DATAKEYS; (*key)->key++){ + if (key_table[(*key)->key].assigned == 0){ + key_table[(*key)->key].assigned = 1; + key_table[(*key)->key].destructor = dest; + release_sem(lock); + return APR_SUCCESS; + } + + } + release_sem(lock); + return APR_ENOMEM; +} + +APR_DECLARE(apr_status_t) apr_threadkey_private_get(void **new, apr_threadkey_t *key) +{ + thread_id tid; + int i, index=0; + tid = find_thread(NULL); + for (i=0;idata){ + /* it's been used */ + if (beos_data[i]->td == tid){ + index = i; + } + } + } + if (index == 0){ + /* no storage for thread so we can't get anything... */ + return APR_ENOMEM; + } + + if ((key->key < BEOS_MAX_DATAKEYS) && (key_table)){ + acquire_sem(key_table[key->key].lock); + if (key_table[key->key].count){ + (*new) = (void*)beos_data[index]->data[key->key]; + } else { + (*new) = NULL; + } + release_sem(key_table[key->key].lock); + } else { + (*new) = NULL; + } + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_threadkey_private_set(void *priv, apr_threadkey_t *key) +{ + thread_id tid; + int i,index = 0, ret = 0; + + tid = find_thread(NULL); + for (i=0; i < BEOS_MAX_DATAKEYS; i++){ + if (beos_data[i]->data){ + if (beos_data[i]->td == tid){index = i;} + } + } + if (index==0){ + /* not yet been allocated */ + for (i=0; i< BEOS_MAX_DATAKEYS; i++){ + if (! beos_data[i]->data){ + /* we'll take this one... */ + index = i; + beos_data[i]->data = (const void **)malloc(sizeof(void *) * BEOS_MAX_DATAKEYS); + memset((void *)beos_data[i]->data, 0, sizeof(void *) * BEOS_MAX_DATAKEYS); + beos_data[i]->count = (int)malloc(sizeof(int)); + beos_data[i]->td = (thread_id)malloc(sizeof(thread_id)); + beos_data[i]->td = tid; + } + } + } + if (index == 0){ + /* we're out of luck.. */ + return APR_ENOMEM; + } + if ((key->key < BEOS_MAX_DATAKEYS) && (key_table)){ + acquire_sem(key_table[key->key].lock); + if (key_table[key->key].count){ + if (beos_data[index]->data[key->key] == NULL){ + if (priv != NULL){ + beos_data[index]->count++; + key_table[key->key].count++; + } + } else { + if (priv == NULL){ + beos_data[index]->count--; + key_table[key->key].count--; + } + } + beos_data[index]->data[key->key] = priv; + ret = 1; + } else { + ret = 0; + } + release_sem(key_table[key->key].lock); + } + if (ret) + return APR_SUCCESS; + return APR_ENOMEM; +} + +APR_DECLARE(apr_status_t) apr_threadkey_private_delete(apr_threadkey_t *key) +{ + if (key->key < BEOS_MAX_DATAKEYS){ + acquire_sem(key_table[key->key].lock); + if (key_table[key->key].count == 1){ + key_table[key->key].destructor = NULL; + key_table[key->key].count = 0; + } + release_sem(key_table[key->key].lock); + } else { + return APR_ENOMEM; + } + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_threadkey_data_get(void **data, const char *key, + apr_threadkey_t *threadkey) +{ + return apr_pool_userdata_get(data, key, threadkey->pool); +} + +APR_DECLARE(apr_status_t) apr_threadkey_data_set(void *data, const char *key, + apr_status_t (*cleanup) (void *), + apr_threadkey_t *threadkey) +{ + return apr_pool_userdata_set(data, key, cleanup, threadkey->pool); +} + +APR_DECLARE(apr_status_t) apr_os_threadkey_get(apr_os_threadkey_t *thekey, apr_threadkey_t *key) +{ + *thekey = key->key; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_os_threadkey_put(apr_threadkey_t **key, + apr_os_threadkey_t *thekey, apr_pool_t *pool) +{ + if (pool == NULL) { + return APR_ENOPOOL; + } + if ((*key) == NULL) { + (*key) = (apr_threadkey_t *)apr_pcalloc(pool, sizeof(apr_threadkey_t)); + (*key)->pool = pool; + } + (*key)->key = *thekey; + return APR_SUCCESS; +} diff --git a/threadproc/beos/threadproc_common.c b/threadproc/beos/threadproc_common.c new file mode 100644 index 0000000..95e1625 --- /dev/null +++ b/threadproc/beos/threadproc_common.c @@ -0,0 +1,21 @@ +/* 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. + */ + +/* As the signal code is identical, use the unix version to reduce + code duplication */ +#include "../unix/signals.c" +#include "../unix/procsup.c" + diff --git a/threadproc/netware/proc.c b/threadproc/netware/proc.c new file mode 100644 index 0000000..e5306f9 --- /dev/null +++ b/threadproc/netware/proc.c @@ -0,0 +1,515 @@ +/* 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_arch_threadproc.h" +#include "apr_arch_file_io.h" +#include "apr_strings.h" +#include "apr_portable.h" + +#include + +/* Heavy on no'ops, here's what we want to pass if there is APR_NO_FILE + * requested for a specific child handle; + */ +static apr_file_t no_file = { NULL, -1, }; + +static apr_status_t apr_netware_proc_cleanup(void *theproc) +{ + apr_proc_t *proc = theproc; + int exit_int; + int waitpid_options = WUNTRACED | WNOHANG; + + if (proc->pid > 0) { + waitpid(proc->pid, &exit_int, waitpid_options); + } + +/* NXVmDestroy(proc->pid); */ + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_create(apr_procattr_t **new,apr_pool_t *pool) +{ + (*new) = (apr_procattr_t *)apr_pcalloc(pool, sizeof(apr_procattr_t)); + + if ((*new) == NULL) { + return APR_ENOMEM; + } + (*new)->pool = pool; + (*new)->cmdtype = APR_PROGRAM; + /* Default to a current path since NetWare doesn't handle it very well */ + apr_filepath_get(&((*new)->currdir), APR_FILEPATH_NATIVE, pool); + (*new)->detached = 1; + return APR_SUCCESS; + +} + +APR_DECLARE(apr_status_t) apr_procattr_io_set(apr_procattr_t *attr, + apr_int32_t in, + apr_int32_t out, + apr_int32_t err) +{ + apr_status_t rv; + + if ((in != APR_NO_PIPE) && (in != APR_NO_FILE)) { + /* APR_CHILD_BLOCK maps to APR_WRITE_BLOCK, while + * APR_PARENT_BLOCK maps to APR_READ_BLOCK, so transpose + * the CHILD/PARENT blocking flags for the stdin pipe. + * stdout/stderr map to the correct mode by default. + */ + if (in == APR_CHILD_BLOCK) + in = APR_READ_BLOCK; + else if (in == APR_PARENT_BLOCK) + in = APR_WRITE_BLOCK; + + if ((rv = apr_file_pipe_create_ex(&attr->child_in, &attr->parent_in, + in, attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_in); + if (rv != APR_SUCCESS) + return rv; + } + else if (in == APR_NO_FILE) + attr->child_in = &no_file; + + if ((out != APR_NO_PIPE) && (out != APR_NO_FILE)) { + if ((rv = apr_file_pipe_create_ex(&attr->parent_out, &attr->child_out, + out, attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_out); + if (rv != APR_SUCCESS) + return rv; + } + else if (out == APR_NO_FILE) + attr->child_out = &no_file; + + if ((err != APR_NO_PIPE) && (err != APR_NO_FILE)) { + if ((rv = apr_file_pipe_create_ex(&attr->parent_err, &attr->child_err, + err, attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_err); + if (rv != APR_SUCCESS) + return rv; + } + else if (err == APR_NO_FILE) + attr->child_err = &no_file; + + return APR_SUCCESS; +} + + +APR_DECLARE(apr_status_t) apr_procattr_child_in_set(apr_procattr_t *attr, apr_file_t *child_in, + apr_file_t *parent_in) +{ + apr_status_t rv = APR_SUCCESS; + + if (attr->child_in == NULL && attr->parent_in == NULL + && child_in == NULL && parent_in == NULL) + if ((rv = apr_file_pipe_create(&attr->child_in, &attr->parent_in, + attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_in); + + if (child_in != NULL && rv == APR_SUCCESS) { + if (attr->child_in && (attr->child_in->filedes != -1)) + rv = apr_file_dup2(attr->child_in, child_in, attr->pool); + else { + attr->child_in = NULL; + if ((rv = apr_file_dup(&attr->child_in, child_in, attr->pool)) + == APR_SUCCESS) + rv = apr_file_inherit_set(attr->child_in); + } + } + + if (parent_in != NULL && rv == APR_SUCCESS) { + rv = apr_file_dup(&attr->parent_in, parent_in, attr->pool); + } + + return rv; +} + + +APR_DECLARE(apr_status_t) apr_procattr_child_out_set(apr_procattr_t *attr, apr_file_t *child_out, + apr_file_t *parent_out) +{ + apr_status_t rv = APR_SUCCESS; + + if (attr->child_out == NULL && attr->parent_out == NULL + && child_out == NULL && parent_out == NULL) + if ((rv = apr_file_pipe_create(&attr->parent_out, &attr->child_out, + attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_out); + + if (child_out != NULL && rv == APR_SUCCESS) { + if (attr->child_out && (attr->child_out->filedes != -1)) + rv = apr_file_dup2(attr->child_out, child_out, attr->pool); + else { + attr->child_out = NULL; + if ((rv = apr_file_dup(&attr->child_out, child_out, attr->pool)) + == APR_SUCCESS) + rv = apr_file_inherit_set(attr->child_out); + } + } + + if (parent_out != NULL && rv == APR_SUCCESS) { + rv = apr_file_dup(&attr->parent_out, parent_out, attr->pool); + } + + return rv; +} + + +APR_DECLARE(apr_status_t) apr_procattr_child_err_set(apr_procattr_t *attr, apr_file_t *child_err, + apr_file_t *parent_err) +{ + apr_status_t rv = APR_SUCCESS; + + if (attr->child_err == NULL && attr->parent_err == NULL + && child_err == NULL && parent_err == NULL) + if ((rv = apr_file_pipe_create(&attr->parent_err, &attr->child_err, + attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_err); + + if (child_err != NULL && rv == APR_SUCCESS) { + if (attr->child_err && (attr->child_err->filedes != -1)) + rv = apr_file_dup2(attr->child_err, child_err, attr->pool); + else { + attr->child_err = NULL; + if ((rv = apr_file_dup(&attr->child_err, child_err, attr->pool)) + == APR_SUCCESS) + rv = apr_file_inherit_set(attr->child_err); + } + } + + if (parent_err != NULL && rv == APR_SUCCESS) { + rv = apr_file_dup(&attr->parent_err, parent_err, attr->pool); + } + + return rv; +} + + +APR_DECLARE(apr_status_t) apr_procattr_dir_set(apr_procattr_t *attr, + const char *dir) +{ + return apr_filepath_merge(&attr->currdir, NULL, dir, + APR_FILEPATH_NATIVE, attr->pool); +} + +APR_DECLARE(apr_status_t) apr_procattr_cmdtype_set(apr_procattr_t *attr, + apr_cmdtype_e cmd) +{ + /* won't ever be called on this platform, so don't save the function pointer */ + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_detach_set(apr_procattr_t *attr, apr_int32_t detach) +{ + attr->detached = detach; + return APR_SUCCESS; +} + +#if APR_HAS_FORK +APR_DECLARE(apr_status_t) apr_proc_fork(apr_proc_t *proc, apr_pool_t *pool) +{ + int pid; + + if ((pid = fork()) < 0) { + return errno; + } + else if (pid == 0) { + proc->pid = pid; + proc->in = NULL; + proc->out = NULL; + proc->err = NULL; + return APR_INCHILD; + } + proc->pid = pid; + proc->in = NULL; + proc->out = NULL; + proc->err = NULL; + return APR_INPARENT; +} +#endif + +static apr_status_t limit_proc(apr_procattr_t *attr) +{ +#if APR_HAVE_STRUCT_RLIMIT && APR_HAVE_SETRLIMIT +#ifdef RLIMIT_CPU + if (attr->limit_cpu != NULL) { + if ((setrlimit(RLIMIT_CPU, attr->limit_cpu)) != 0) { + return errno; + } + } +#endif +#ifdef RLIMIT_NPROC + if (attr->limit_nproc != NULL) { + if ((setrlimit(RLIMIT_NPROC, attr->limit_nproc)) != 0) { + return errno; + } + } +#endif +#if defined(RLIMIT_AS) + if (attr->limit_mem != NULL) { + if ((setrlimit(RLIMIT_AS, attr->limit_mem)) != 0) { + return errno; + } + } +#elif defined(RLIMIT_DATA) + if (attr->limit_mem != NULL) { + if ((setrlimit(RLIMIT_DATA, attr->limit_mem)) != 0) { + return errno; + } + } +#elif defined(RLIMIT_VMEM) + if (attr->limit_mem != NULL) { + if ((setrlimit(RLIMIT_VMEM, attr->limit_mem)) != 0) { + return errno; + } + } +#endif +#else + /* + * Maybe make a note in error_log that setrlimit isn't supported?? + */ + +#endif + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_child_errfn_set(apr_procattr_t *attr, + apr_child_errfn_t *errfn) +{ + /* won't ever be called on this platform, so don't save the function pointer */ + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_error_check_set(apr_procattr_t *attr, + apr_int32_t chk) +{ + /* won't ever be used on this platform, so don't save the flag */ + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_addrspace_set(apr_procattr_t *attr, + apr_int32_t addrspace) +{ + attr->addrspace = addrspace; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *newproc, + const char *progname, + const char * const *args, + const char * const *env, + apr_procattr_t *attr, + apr_pool_t *pool) +{ + wiring_t wire; + int addr_space; + + wire.infd = attr->child_in + ? (attr->child_in->filedes != -1 ? attr->child_in->filedes + : FD_UNUSED) + : fileno(stdin); + wire.outfd = attr->child_out + ? (attr->child_out->filedes != -1 ? attr->child_out->filedes + : FD_UNUSED) + : fileno(stdout); + wire.errfd = attr->child_err + ? (attr->child_err->filedes != -1 ? attr->child_err->filedes + : FD_UNUSED) + : fileno(stderr); + + newproc->in = attr->parent_in; + newproc->out = attr->parent_out; + newproc->err = attr->parent_err; + + /* attr->detached and PROC_DETACHED do not mean the same thing. attr->detached means + * start the NLM in a separate address space. PROC_DETACHED means don't wait for the + * NLM to unload by calling wait() or waitpid(), just clean up */ + addr_space = PROC_LOAD_SILENT | (attr->addrspace ? 0 : PROC_CURRENT_SPACE); + addr_space |= (attr->detached ? PROC_DETACHED : 0); + + if (attr->currdir) { + char *fullpath = NULL; + apr_status_t rv; + + if ((rv = apr_filepath_merge(&fullpath, attr->currdir, progname, + APR_FILEPATH_NATIVE, pool)) != APR_SUCCESS) { + return rv; + } + progname = fullpath; + } + + if ((newproc->pid = procve(progname, addr_space, (const char**)env, &wire, + NULL, NULL, 0, NULL, (const char **)args)) == -1) { + return errno; + } + + if (attr->child_in && (attr->child_in->filedes != -1)) { + apr_pool_cleanup_kill(apr_file_pool_get(attr->child_in), + attr->child_in, apr_unix_file_cleanup); + apr_file_close(attr->child_in); + } + if (attr->child_out && (attr->child_out->filedes != -1)) { + apr_pool_cleanup_kill(apr_file_pool_get(attr->child_out), + attr->child_out, apr_unix_file_cleanup); + apr_file_close(attr->child_out); + } + if (attr->child_err && (attr->child_err->filedes != -1)) { + apr_pool_cleanup_kill(apr_file_pool_get(attr->child_err), + attr->child_err, apr_unix_file_cleanup); + apr_file_close(attr->child_err); + } + + apr_pool_cleanup_register(pool, (void *)newproc, apr_netware_proc_cleanup, + apr_pool_cleanup_null); + + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_proc_wait_all_procs(apr_proc_t *proc, + int *exitcode, + apr_exit_why_e *exitwhy, + apr_wait_how_e waithow, + apr_pool_t *p) +{ + proc->pid = -1; + return apr_proc_wait(proc, exitcode, exitwhy, waithow); +} + +APR_DECLARE(apr_status_t) apr_proc_wait(apr_proc_t *proc, + int *exitcode, apr_exit_why_e *exitwhy, + apr_wait_how_e waithow) +{ + pid_t pstatus; + int waitpid_options = WUNTRACED; + int exit_int; + int ignore; + apr_exit_why_e ignorewhy; + + if (exitcode == NULL) { + exitcode = &ignore; + } + + if (exitwhy == NULL) { + exitwhy = &ignorewhy; + } + + if (waithow != APR_WAIT) { + waitpid_options |= WNOHANG; + } + + /* If the pid is 0 then the process was started detached. There + is no need to wait since there is nothing to wait for on a + detached process. Starting a process as non-detached and + then calling wait or waitpid could cause the thread to hang. + The reason for this is because NetWare does not have a way + to kill or even signal a process to be killed. Starting + all processes as detached avoids the possibility of a + thread hanging. */ + if (proc->pid == 0) { + *exitwhy = APR_PROC_EXIT; + *exitcode = 0; + return APR_CHILD_DONE; + } + + if ((pstatus = waitpid(proc->pid, &exit_int, waitpid_options)) > 0) { + proc->pid = pstatus; + + if (WIFEXITED(exit_int)) { + *exitwhy = APR_PROC_EXIT; + *exitcode = WEXITSTATUS(exit_int); + } + else if (WIFSIGNALED(exit_int)) { + *exitwhy = APR_PROC_SIGNAL; + *exitcode = WIFTERMSIG(exit_int); + } + else { + /* unexpected condition */ + return APR_EGENERAL; + } + + return APR_CHILD_DONE; + } + else if (pstatus == 0) { + return APR_CHILD_NOTDONE; + } + + return errno; +} + +#if APR_HAVE_STRUCT_RLIMIT +APR_DECLARE(apr_status_t) apr_procattr_limit_set(apr_procattr_t *attr, + apr_int32_t what, + struct rlimit *limit) +{ + switch(what) { + case APR_LIMIT_CPU: +#ifdef RLIMIT_CPU + attr->limit_cpu = limit; + break; +#else + return APR_ENOTIMPL; +#endif + + case APR_LIMIT_MEM: +#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) + attr->limit_mem = limit; + break; +#else + return APR_ENOTIMPL; +#endif + + case APR_LIMIT_NPROC: +#ifdef RLIMIT_NPROC + attr->limit_nproc = limit; + break; +#else + return APR_ENOTIMPL; +#endif + + case APR_LIMIT_NOFILE: +#ifdef RLIMIT_NOFILE + attr->limit_nofile = limit; + break; +#else + return APR_ENOTIMPL; +#endif + + } + return APR_SUCCESS; +} +#endif /* APR_HAVE_STRUCT_RLIMIT */ + +APR_DECLARE(apr_status_t) apr_procattr_user_set(apr_procattr_t *attr, + const char *username, + const char *password) +{ + /* Always return SUCCESS because NetWare threads don't run as a user */ + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_group_set(apr_procattr_t *attr, + const char *groupname) +{ + /* Always return SUCCESS because NetWare threads don't run within a group */ + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_perms_set_register(apr_procattr_t *attr, + apr_perms_setfn_t *perms_set_fn, + void *data, + apr_fileperms_t perms) +{ + return APR_ENOTIMPL; +} diff --git a/threadproc/netware/procsup.c b/threadproc/netware/procsup.c new file mode 100644 index 0000000..72fa1d9 --- /dev/null +++ b/threadproc/netware/procsup.c @@ -0,0 +1,102 @@ +/* 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_arch_threadproc.h" + +apr_status_t apr_proc_detach(int daemonize) +{ +#if 0 + int x; + pid_t pgrp; + + chdir("/"); +#if !defined(MPE) && !defined(OS2) && !defined(TPF) && !defined(BEOS) +/* Don't detach for MPE because child processes can't survive the death of + the parent. */ + if ((x = fork()) > 0) + exit(0); + else if (x == -1) { + perror("fork"); + fprintf(stderr, "unable to fork new process\n"); + exit(1); /* we can't do anything here, so just exit. */ + } +/* RAISE_SIGSTOP(DETACH);*/ +#endif +#if APR_HAVE_SETSID + if ((pgrp = setsid()) == -1) { + return errno; + } +#elif defined(NEXT) || defined(NEWSOS) + if (setpgrp(0, getpid()) == -1 || (pgrp = getpgrp(0)) == -1) { + return errno; + } +#elif defined(OS2) || defined(TPF) + /* OS/2 don't support process group IDs */ + pgrp = getpid(); +#elif defined(MPE) + /* MPE uses negative pid for process group */ + pgrp = -getpid(); +#else + if ((pgrp = setpgid(0, 0)) == -1) { + return errno; + } +#endif + + /* close out the standard file descriptors */ + if (freopen("/dev/null", "r", stdin) == NULL) { + return errno; + /* continue anyhow -- note we can't close out descriptor 0 because we + * have nothing to replace it with, and if we didn't have a descriptor + * 0 the next file would be created with that value ... leading to + * havoc. + */ + } + if (freopen("/dev/null", "w", stdout) == NULL) { + return errno; + } + /* We are going to reopen this again in a little while to the error + * log file, but better to do it twice and suffer a small performance + * hit for consistancy than not reopen it here. + */ + if (freopen("/dev/null", "w", stderr) == NULL) { + return errno; + } +#endif + return APR_SUCCESS; +} + +#if 0 +#if (!HAVE_WAITPID) +/* From ikluft@amdahl.com + * this is not ideal but it works for SVR3 variants + * Modified by dwd@bell-labs.com to call wait3 instead of wait because + * apache started to use the WNOHANG option. + */ +int waitpid(pid_t pid, int *statusp, int options) +{ + int tmp_pid; + if (kill(pid, 0) == -1) { + errno = ECHILD; + return -1; + } + while (((tmp_pid = wait3(statusp, options, 0)) != pid) && + (tmp_pid != -1) && (tmp_pid != 0) && (pid != -1)) + ; + return tmp_pid; +} +#endif +#endif + diff --git a/threadproc/netware/signals.c b/threadproc/netware/signals.c new file mode 100644 index 0000000..c744da5 --- /dev/null +++ b/threadproc/netware/signals.c @@ -0,0 +1,81 @@ +/* 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_arch_threadproc.h" +#include "apr_private.h" +#include "apr_pools.h" +#include "apr_signal.h" +#include "apr_strings.h" + +#include +#if APR_HAS_THREADS && APR_HAVE_PTHREAD_H +#include +#endif + +APR_DECLARE(apr_status_t) apr_proc_kill(apr_proc_t *proc, int signum) +{ + return APR_ENOTIMPL; +} + + +void apr_signal_init(apr_pool_t *pglobal) +{ +} + +const char *apr_signal_description_get(int signum) +{ + switch (signum) + { + case SIGABRT: + return "Abort"; + case SIGFPE: + return "Arithmetic exception"; + case SIGILL: + return "Illegal instruction"; + case SIGINT: + return "Interrupt"; + case SIGSEGV: + return "Segmentation fault"; + case SIGTERM: + return "Terminated"; + case SIGPOLL: + return "Pollable event occurred"; + default: + return "unknown signal (not supported)"; + } +} + +static void *signal_thread_func(void *signal_handler) +{ + return NULL; +} + +#if (APR_HAVE_SIGWAIT || APR_HAVE_SIGSUSPEND) +APR_DECLARE(apr_status_t) apr_setup_signal_thread(void) +{ + return 0; +} +#endif /* (APR_HAVE_SIGWAIT || APR_HAVE_SIGSUSPEND) */ + +APR_DECLARE(apr_status_t) apr_signal_block(int signum) +{ + return APR_ENOTIMPL; +} + +APR_DECLARE(apr_status_t) apr_signal_unblock(int signum) +{ + return APR_ENOTIMPL; +} diff --git a/threadproc/netware/thread.c b/threadproc/netware/thread.c new file mode 100644 index 0000000..f983668 --- /dev/null +++ b/threadproc/netware/thread.c @@ -0,0 +1,254 @@ +/* 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.h" +#include "apr_portable.h" +#include "apr_strings.h" +#include "apr_arch_threadproc.h" + +static int thread_count = 0; + +apr_status_t apr_threadattr_create(apr_threadattr_t **new, + apr_pool_t *pool) +{ + (*new) = (apr_threadattr_t *)apr_palloc(pool, + sizeof(apr_threadattr_t)); + + if ((*new) == NULL) { + return APR_ENOMEM; + } + + (*new)->pool = pool; + (*new)->stack_size = APR_DEFAULT_STACK_SIZE; + (*new)->detach = 0; + (*new)->thread_name = NULL; + return APR_SUCCESS; +} + +apr_status_t apr_threadattr_detach_set(apr_threadattr_t *attr,apr_int32_t on) +{ + attr->detach = on; + return APR_SUCCESS; +} + +apr_status_t apr_threadattr_detach_get(apr_threadattr_t *attr) +{ + if (attr->detach == 1) + return APR_DETACH; + return APR_NOTDETACH; +} + +APR_DECLARE(apr_status_t) apr_threadattr_stacksize_set(apr_threadattr_t *attr, + apr_size_t stacksize) +{ + attr->stack_size = stacksize; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_threadattr_guardsize_set(apr_threadattr_t *attr, + apr_size_t size) +{ + return APR_ENOTIMPL; +} + +static void *dummy_worker(void *opaque) +{ + apr_thread_t *thd = (apr_thread_t *)opaque; + return thd->func(thd, thd->data); +} + +apr_status_t apr_thread_create(apr_thread_t **new, + apr_threadattr_t *attr, + apr_thread_start_t func, + void *data, + apr_pool_t *pool) +{ + apr_status_t stat; + unsigned long flags = NX_THR_BIND_CONTEXT; + char threadName[NX_MAX_OBJECT_NAME_LEN+1]; + size_t stack_size = APR_DEFAULT_STACK_SIZE; + + if (attr && attr->thread_name) { + strncpy (threadName, attr->thread_name, NX_MAX_OBJECT_NAME_LEN); + } + else { + sprintf(threadName, "APR_thread %04ld", ++thread_count); + } + + /* An original stack size of 0 will allow NXCreateThread() to + * assign a default system stack size. An original stack + * size of less than 0 will assign the APR default stack size. + * anything else will be taken as is. + */ + if (attr && (attr->stack_size >= 0)) { + stack_size = attr->stack_size; + } + + (*new) = (apr_thread_t *)apr_palloc(pool, sizeof(apr_thread_t)); + + if ((*new) == NULL) { + return APR_ENOMEM; + } + + (*new)->data = data; + (*new)->func = func; + (*new)->thread_name = (char*)apr_pstrdup(pool, threadName); + + stat = apr_pool_create(&(*new)->pool, pool); + if (stat != APR_SUCCESS) { + return stat; + } + + if (attr && attr->detach) { + flags |= NX_THR_DETACHED; + } + + (*new)->ctx = NXContextAlloc( + /* void(*start_routine)(void *arg) */ (void (*)(void *)) dummy_worker, + /* void *arg */ (*new), + /* int priority */ NX_PRIO_MED, + /* size_t stackSize */ stack_size, + /* unsigned long flags */ NX_CTX_NORMAL, + /* int *error */ &stat); + + stat = NXContextSetName( + /* NXContext_t ctx */ (*new)->ctx, + /* const char *name */ threadName); + + stat = NXThreadCreate( + /* NXContext_t context */ (*new)->ctx, + /* unsigned long flags */ flags, + /* NXThreadId_t *thread_id */ &(*new)->td); + + if (stat == 0) + return APR_SUCCESS; + + return(stat); /* if error */ +} + +apr_os_thread_t apr_os_thread_current() +{ + return NXThreadGetId(); +} + +int apr_os_thread_equal(apr_os_thread_t tid1, apr_os_thread_t tid2) +{ + return (tid1 == tid2); +} + +void apr_thread_yield() +{ + NXThreadYield(); +} + +apr_status_t apr_thread_exit(apr_thread_t *thd, + apr_status_t retval) +{ + thd->exitval = retval; + apr_pool_destroy(thd->pool); + NXThreadExit(NULL); + return APR_SUCCESS; +} + +apr_status_t apr_thread_join(apr_status_t *retval, + apr_thread_t *thd) +{ + apr_status_t stat; + NXThreadId_t dthr; + + if ((stat = NXThreadJoin(thd->td, &dthr, NULL)) == 0) { + *retval = thd->exitval; + return APR_SUCCESS; + } + else { + return stat; + } +} + +apr_status_t apr_thread_detach(apr_thread_t *thd) +{ + return APR_SUCCESS; +} + +apr_status_t apr_thread_data_get(void **data, const char *key, + apr_thread_t *thread) +{ + if (thread != NULL) { + return apr_pool_userdata_get(data, key, thread->pool); + } + else { + data = NULL; + return APR_ENOTHREAD; + } +} + +apr_status_t apr_thread_data_set(void *data, const char *key, + apr_status_t (*cleanup) (void *), + apr_thread_t *thread) +{ + if (thread != NULL) { + return apr_pool_userdata_set(data, key, cleanup, thread->pool); + } + else { + data = NULL; + return APR_ENOTHREAD; + } +} + +APR_DECLARE(apr_status_t) apr_os_thread_get(apr_os_thread_t **thethd, + apr_thread_t *thd) +{ + if (thd == NULL) { + return APR_ENOTHREAD; + } + *thethd = &(thd->td); + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_os_thread_put(apr_thread_t **thd, + apr_os_thread_t *thethd, + apr_pool_t *pool) +{ + if (pool == NULL) { + return APR_ENOPOOL; + } + if ((*thd) == NULL) { + (*thd) = (apr_thread_t *)apr_palloc(pool, sizeof(apr_thread_t)); + (*thd)->pool = pool; + } + (*thd)->td = *thethd; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_thread_once_init(apr_thread_once_t **control, + apr_pool_t *p) +{ + (*control) = apr_pcalloc(p, sizeof(**control)); + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_thread_once(apr_thread_once_t *control, + void (*func)(void)) +{ + if (!atomic_xchg(&control->value, 1)) { + func(); + } + return APR_SUCCESS; +} + +APR_POOL_IMPLEMENT_ACCESSOR(thread) + + diff --git a/threadproc/netware/threadpriv.c b/threadproc/netware/threadpriv.c new file mode 100644 index 0000000..54680a5 --- /dev/null +++ b/threadproc/netware/threadpriv.c @@ -0,0 +1,102 @@ +/* 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_portable.h" +#include "apr_arch_threadproc.h" + +apr_status_t apr_threadkey_private_create(apr_threadkey_t **key, + void (*dest)(void *), apr_pool_t *pool) +{ + apr_status_t stat; + + (*key) = (apr_threadkey_t *)apr_palloc(pool, sizeof(apr_threadkey_t)); + if ((*key) == NULL) { + return APR_ENOMEM; + } + + (*key)->pool = pool; + + if ((stat = NXKeyCreate(NULL, dest, &(*key)->key)) == 0) { + return stat; + } + return stat; +} + +apr_status_t apr_threadkey_private_get(void **new, apr_threadkey_t *key) +{ + apr_status_t stat; + + if ((stat = NXKeyGetValue(key->key, new)) == 0) { + return APR_SUCCESS; + } + else { + return stat; + } +} + +apr_status_t apr_threadkey_private_set(void *priv, apr_threadkey_t *key) +{ + apr_status_t stat; + if ((stat = NXKeySetValue(key->key, priv)) == 0) { + return APR_SUCCESS; + } + else { + return stat; + } +} + +apr_status_t apr_threadkey_private_delete(apr_threadkey_t *key) +{ + apr_status_t stat; + if ((stat = NXKeyDelete(key->key)) == 0) { + return APR_SUCCESS; + } + return stat; +} + +apr_status_t apr_threadkey_data_get(void **data, const char *key, apr_threadkey_t *threadkey) +{ + return apr_pool_userdata_get(data, key, threadkey->pool); +} + +apr_status_t apr_threadkey_data_set(void *data, + const char *key, apr_status_t (*cleanup) (void *), + apr_threadkey_t *threadkey) +{ + return apr_pool_userdata_set(data, key, cleanup, threadkey->pool); +} + +apr_status_t apr_os_threadkey_get(apr_os_threadkey_t *thekey, + apr_threadkey_t *key) +{ + thekey = &(key->key); + return APR_SUCCESS; +} + +apr_status_t apr_os_threadkey_put(apr_threadkey_t **key, + apr_os_threadkey_t *thekey, apr_pool_t *pool) +{ + if (pool == NULL) { + return APR_ENOPOOL; + } + if ((*key) == NULL) { + (*key) = (apr_threadkey_t *)apr_palloc(pool, sizeof(apr_threadkey_t)); + (*key)->pool = pool; + } + (*key)->key = *thekey; + return APR_SUCCESS; +} + diff --git a/threadproc/os2/proc.c b/threadproc/os2/proc.c new file mode 100644 index 0000000..96f76d6 --- /dev/null +++ b/threadproc/os2/proc.c @@ -0,0 +1,672 @@ +/* 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. + */ + +#define INCL_DOS +#define INCL_DOSERRORS + +#include "apr_arch_threadproc.h" +#include "apr_arch_file_io.h" +#include "apr_private.h" +#include "apr_thread_proc.h" +#include "apr_file_io.h" +#include "apr_general.h" +#include "apr_lib.h" +#include "apr_portable.h" +#include "apr_strings.h" +#include "apr_signal.h" +#include +#include +#include +#include +#include +#include + +/* Heavy on no'ops, here's what we want to pass if there is APR_NO_FILE + * requested for a specific child handle; + */ +static apr_file_t no_file = { NULL, -1, }; + +APR_DECLARE(apr_status_t) apr_procattr_create(apr_procattr_t **new, apr_pool_t *pool) +{ + (*new) = (apr_procattr_t *)apr_palloc(pool, + sizeof(apr_procattr_t)); + + if ((*new) == NULL) { + return APR_ENOMEM; + } + (*new)->pool = pool; + (*new)->parent_in = NULL; + (*new)->child_in = NULL; + (*new)->parent_out = NULL; + (*new)->child_out = NULL; + (*new)->parent_err = NULL; + (*new)->child_err = NULL; + (*new)->currdir = NULL; + (*new)->cmdtype = APR_PROGRAM; + (*new)->detached = FALSE; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_io_set(apr_procattr_t *attr, + apr_int32_t in, + apr_int32_t out, + apr_int32_t err) +{ + apr_status_t rv; + + if ((in != APR_NO_PIPE) && (in != APR_NO_FILE)) { + /* APR_CHILD_BLOCK maps to APR_WRITE_BLOCK, while + * APR_PARENT_BLOCK maps to APR_READ_BLOCK, so transpose + * the CHILD/PARENT blocking flags for the stdin pipe. + * stdout/stderr map to the correct mode by default. + */ + if (in == APR_CHILD_BLOCK) + in = APR_READ_BLOCK; + else if (in == APR_PARENT_BLOCK) + in = APR_WRITE_BLOCK; + + if ((rv = apr_file_pipe_create_ex(&attr->child_in, &attr->parent_in, + in, attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_in); + if (rv != APR_SUCCESS) + return rv; + } + else if (in == APR_NO_FILE) + attr->child_in = &no_file; + + if ((out != APR_NO_PIPE) && (out != APR_NO_FILE)) { + if ((rv = apr_file_pipe_create_ex(&attr->parent_out, &attr->child_out, + out, attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_out); + if (rv != APR_SUCCESS) + return rv; + } + else if (out == APR_NO_FILE) + attr->child_out = &no_file; + + if ((err != APR_NO_PIPE) && (err != APR_NO_FILE)) { + if ((rv = apr_file_pipe_create_ex(&attr->parent_err, &attr->child_err, + err, attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_err); + if (rv != APR_SUCCESS) + return rv; + } + else if (err == APR_NO_FILE) + attr->child_err = &no_file; + + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_child_in_set(apr_procattr_t *attr, apr_file_t *child_in, + apr_file_t *parent_in) +{ + apr_status_t rv; + + if (attr->child_in == NULL && attr->parent_in == NULL + && child_in == NULL && parent_in == NULL) + if ((rv = apr_file_pipe_create(&attr->child_in, &attr->parent_in, + attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_in); + + if (child_in != NULL && rv == APR_SUCCESS) { + if (attr->child_in && (attr->child_in->filedes != -1)) + rv = apr_file_dup2(attr->child_in, child_in, attr->pool); + else { + attr->child_in = NULL; + if ((rv = apr_file_dup(&attr->child_in, child_in, attr->pool)) + == APR_SUCCESS) + rv = apr_file_inherit_set(attr->child_in); + } + } + + if (parent_in != NULL && rv == APR_SUCCESS) { + rv = apr_file_dup(&attr->parent_in, parent_in, attr->pool); + } + + return rv; +} + +APR_DECLARE(apr_status_t) apr_procattr_child_out_set(apr_procattr_t *attr, apr_file_t *child_out, + apr_file_t *parent_out) +{ + apr_status_t rv; + + if (attr->child_out == NULL && attr->parent_out == NULL + && child_out == NULL && parent_out == NULL) + if ((rv = apr_file_pipe_create(&attr->parent_out, &attr->child_out, + attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_out); + + if (child_out != NULL && rv == APR_SUCCESS) { + if (attr->child_out && (attr->child_out->filedes != -1)) + rv = apr_file_dup2(attr->child_out, child_out, attr->pool); + else { + attr->child_out = NULL; + if ((rv = apr_file_dup(&attr->child_out, child_out, attr->pool)) + == APR_SUCCESS) + rv = apr_file_inherit_set(attr->child_out); + } + } + + if (parent_out != NULL && rv == APR_SUCCESS) { + rv = apr_file_dup(&attr->parent_out, parent_out, attr->pool); + } + + return rv; +} + +APR_DECLARE(apr_status_t) apr_procattr_child_err_set(apr_procattr_t *attr, apr_file_t *child_err, + apr_file_t *parent_err) +{ + apr_status_t rv; + + if (attr->child_err == NULL && attr->parent_err == NULL + && child_err == NULL && parent_err == NULL) + if ((rv = apr_file_pipe_create(&attr->parent_err, &attr->child_err, + attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_err); + + if (child_err != NULL && rv == APR_SUCCESS) { + if (attr->child_err && (attr->child_err->filedes != -1)) + rv = apr_file_dup2(attr->child_err, child_err, attr->pool); + else { + attr->child_err = NULL; + if ((rv = apr_file_dup(&attr->child_err, child_err, attr->pool)) + == APR_SUCCESS) + rv = apr_file_inherit_set(attr->child_err); + } + } + + if (parent_err != NULL && rv == APR_SUCCESS) { + rv = apr_file_dup(&attr->parent_err, parent_err, attr->pool); + } + + return rv; +} + +APR_DECLARE(apr_status_t) apr_procattr_dir_set(apr_procattr_t *attr, const char *dir) +{ + attr->currdir = apr_pstrdup(attr->pool, dir); + if (attr->currdir) { + return APR_SUCCESS; + } + return APR_ENOMEM; +} + +APR_DECLARE(apr_status_t) apr_procattr_cmdtype_set(apr_procattr_t *attr, + apr_cmdtype_e cmd) +{ + attr->cmdtype = cmd; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_detach_set(apr_procattr_t *attr, apr_int32_t detach) +{ + attr->detached = detach; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_proc_fork(apr_proc_t *proc, apr_pool_t *pool) +{ + int pid; + + if ((pid = fork()) < 0) { + return errno; + } + else if (pid == 0) { + proc->pid = pid; + proc->in = NULL; + proc->out = NULL; + proc->err = NULL; + return APR_INCHILD; + } + proc->pid = pid; + proc->in = NULL; + proc->out = NULL; + proc->err = NULL; + return APR_INPARENT; +} + + + +/* quotes in the string are doubled up. + * Used to escape quotes in args passed to OS/2's cmd.exe + */ +static char *double_quotes(apr_pool_t *pool, const char *str) +{ + int num_quotes = 0; + int len = 0; + char *quote_doubled_str, *dest; + + while (str[len]) { + num_quotes += str[len++] == '\"'; + } + + quote_doubled_str = apr_palloc(pool, len + num_quotes + 1); + dest = quote_doubled_str; + + while (*str) { + if (*str == '\"') + *(dest++) = '\"'; + *(dest++) = *(str++); + } + + *dest = 0; + return quote_doubled_str; +} + + + +APR_DECLARE(apr_status_t) apr_procattr_child_errfn_set(apr_procattr_t *attr, + apr_child_errfn_t *errfn) +{ + /* won't ever be called on this platform, so don't save the function pointer */ + return APR_SUCCESS; +} + + + +APR_DECLARE(apr_status_t) apr_procattr_error_check_set(apr_procattr_t *attr, + apr_int32_t chk) +{ + /* won't ever be used on this platform, so don't save the flag */ + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_addrspace_set(apr_procattr_t *attr, + apr_int32_t addrspace) +{ + /* won't ever be used on this platform, so don't save the flag */ + return APR_SUCCESS; +} + + + +APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *proc, const char *progname, + const char * const *args, + const char * const *env, + apr_procattr_t *attr, apr_pool_t *pool) +{ + int i, arg, numargs, cmdlen; + apr_status_t status; + const char **newargs; + char savedir[300]; + HFILE save_in, save_out, save_err, dup; + int criticalsection = FALSE; + char *extension, *newprogname, *extra_arg = NULL, *cmdline, *cmdline_pos; + char interpreter[1024]; + char error_object[260]; + apr_file_t *progfile; + int env_len, e; + char *env_block, *env_block_pos; + RESULTCODES rescodes; + + proc->in = attr->parent_in; + proc->err = attr->parent_err; + proc->out = attr->parent_out; + + /* Prevent other threads from running while these process-wide resources are modified */ + if (attr->child_in || attr->child_out || attr->child_err || attr->currdir) { + criticalsection = TRUE; + DosEnterCritSec(); + } + + if (attr->child_in) { + save_in = -1; + DosDupHandle(STDIN_FILENO, &save_in); + dup = STDIN_FILENO; + if (attr->child_in->filedes == -1) + DosClose(dup); + else + DosDupHandle(attr->child_in->filedes, &dup); + } + + if (attr->child_out) { + save_out = -1; + DosDupHandle(STDOUT_FILENO, &save_out); + dup = STDOUT_FILENO; + if (attr->child_out->filedes == -1) + DosClose(dup); + else + DosDupHandle(attr->child_out->filedes, &dup); + } + + if (attr->child_err) { + save_err = -1; + DosDupHandle(STDERR_FILENO, &save_err); + dup = STDERR_FILENO; + if (attr->child_err->filedes == -1) + DosClose(dup); + else + DosDupHandle(attr->child_err->filedes, &dup); + } + + apr_signal(SIGCHLD, SIG_DFL); /*not sure if this is needed or not */ + + if (attr->currdir != NULL) { + _getcwd2(savedir, sizeof(savedir)); + + if (_chdir2(attr->currdir) < 0) { + if (criticalsection) + DosExitCritSec(); + return errno; + } + } + + interpreter[0] = 0; + extension = strrchr(progname, '.'); + + if (extension == NULL || strchr(extension, '/') || strchr(extension, '\\')) + extension = ""; + + /* ### how to handle APR_PROGRAM_ENV and APR_PROGRAM_PATH? */ + + if (attr->cmdtype == APR_SHELLCMD || + attr->cmdtype == APR_SHELLCMD_ENV || + strcasecmp(extension, ".cmd") == 0) { + strcpy(interpreter, "#!" SHELL_PATH); + extra_arg = "/C"; + } else if (stricmp(extension, ".exe") != 0) { + status = apr_file_open(&progfile, progname, APR_READ|APR_BUFFERED, 0, pool); + + if (status != APR_SUCCESS && APR_STATUS_IS_ENOENT(status)) { + progname = apr_pstrcat(pool, progname, ".exe", NULL); + } + + if (status == APR_SUCCESS) { + status = apr_file_gets(interpreter, sizeof(interpreter), progfile); + + if (status == APR_SUCCESS) { + if (interpreter[0] == '#' && interpreter[1] == '!') { + /* delete CR/LF & any other whitespace off the end */ + int end = strlen(interpreter) - 1; + + while (end >= 0 && apr_isspace(interpreter[end])) { + interpreter[end] = '\0'; + end--; + } + + if (interpreter[2] != '/' && interpreter[2] != '\\' && interpreter[3] != ':') { + char buffer[300]; + + if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) { + strcpy(interpreter+2, buffer); + } else { + strcat(interpreter, ".exe"); + if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) { + strcpy(interpreter+2, buffer); + } + } + } + } else { + interpreter[0] = 0; + } + } + + apr_file_close(progfile); + } + } + + i = 0; + + while (args && args[i]) { + i++; + } + + newargs = (const char **)apr_palloc(pool, sizeof (char *) * (i + 4)); + numargs = 0; + + if (interpreter[0]) + newargs[numargs++] = interpreter + 2; + if (extra_arg) + newargs[numargs++] = "/c"; + + newargs[numargs++] = newprogname = apr_pstrdup(pool, progname); + arg = 1; + + while (args && args[arg]) { + newargs[numargs++] = args[arg++]; + } + + newargs[numargs] = NULL; + + for (i=0; newprogname[i]; i++) + if (newprogname[i] == '/') + newprogname[i] = '\\'; + + cmdlen = 0; + + for (i=0; i\" ")) + a = apr_pstrcat(pool, "\"", double_quotes(pool, a), "\"", NULL); + + if (i) + *(cmdline_pos++) = ' '; + + strcpy(cmdline_pos, a); + cmdline_pos += strlen(cmdline_pos); + } + + *(++cmdline_pos) = 0; /* Add required second terminator */ + cmdline_pos = strchr(cmdline, ' '); + + if (cmdline_pos) { + *cmdline_pos = 0; + cmdline_pos++; + } + + /* Create environment block from list of envariables */ + if (env) { + for (env_len=1, e=0; env[e]; e++) + env_len += strlen(env[e]) + 1; + + env_block = apr_palloc(pool, env_len); + env_block_pos = env_block; + + for (e=0; env[e]; e++) { + strcpy(env_block_pos, env[e]); + env_block_pos += strlen(env_block_pos) + 1; + } + + *env_block_pos = 0; /* environment block is terminated by a double null */ + } else + env_block = NULL; + + status = DosExecPgm(error_object, sizeof(error_object), + attr->detached ? EXEC_BACKGROUND : EXEC_ASYNCRESULT, + cmdline, env_block, &rescodes, cmdline); + + proc->pid = rescodes.codeTerminate; + + if (attr->currdir != NULL) { + chdir(savedir); + } + + if (attr->child_in) { + if (attr->child_in->filedes != -1) { + apr_file_close(attr->child_in); + } + + dup = STDIN_FILENO; + DosDupHandle(save_in, &dup); + DosClose(save_in); + } + + if (attr->child_out) { + if (attr->child_out->filedes != -1) { + apr_file_close(attr->child_out); + } + + dup = STDOUT_FILENO; + DosDupHandle(save_out, &dup); + DosClose(save_out); + } + + if (attr->child_err) { + if (attr->child_err->filedes != -1) { + apr_file_close(attr->child_err); + } + + dup = STDERR_FILENO; + DosDupHandle(save_err, &dup); + DosClose(save_err); + } + + if (criticalsection) + DosExitCritSec(); + + return status; +} + + + +static void proces_result_codes(RESULTCODES codes, + int *exitcode, + apr_exit_why_e *exitwhy) +{ + int result = 0; + apr_exit_why_e why = APR_PROC_EXIT; + + switch (codes.codeTerminate) { + case TC_EXIT: /* Normal exit */ + why = APR_PROC_EXIT; + result = codes.codeResult; + break; + + case TC_HARDERROR: /* Hard error halt */ + why = APR_PROC_SIGNAL; + result = SIGSYS; + break; + + case TC_KILLPROCESS: /* Was killed by a DosKillProcess() */ + why = APR_PROC_SIGNAL; + result = SIGKILL; + break; + + case TC_TRAP: /* TRAP in 16 bit code */ + case TC_EXCEPTION: /* Threw an exception (32 bit code) */ + why = APR_PROC_SIGNAL; + + switch (codes.codeResult | XCPT_FATAL_EXCEPTION) { + case XCPT_ACCESS_VIOLATION: + result = SIGSEGV; + break; + + case XCPT_ILLEGAL_INSTRUCTION: + result = SIGILL; + break; + + case XCPT_FLOAT_DIVIDE_BY_ZERO: + case XCPT_INTEGER_DIVIDE_BY_ZERO: + result = SIGFPE; + break; + + default: + result = codes.codeResult; + break; + } + } + + if (exitcode) { + *exitcode = result; + } + + if (exitwhy) { + *exitwhy = why; + } +} + + + +APR_DECLARE(apr_status_t) apr_proc_wait_all_procs(apr_proc_t *proc, + int *exitcode, + apr_exit_why_e *exitwhy, + apr_wait_how_e waithow, + apr_pool_t *p) +{ + RESULTCODES codes; + ULONG rc; + PID pid; + + rc = DosWaitChild(DCWA_PROCESSTREE, waithow == APR_WAIT ? DCWW_WAIT : DCWW_NOWAIT, &codes, &pid, 0); + + if (rc == 0) { + proc->pid = pid; + proces_result_codes(codes, exitcode, exitwhy); + return APR_CHILD_DONE; + } else if (rc == ERROR_CHILD_NOT_COMPLETE) { + return APR_CHILD_NOTDONE; + } + + return APR_OS2_STATUS(rc); +} + + + +APR_DECLARE(apr_status_t) apr_proc_wait(apr_proc_t *proc, + int *exitcode, apr_exit_why_e *exitwhy, + apr_wait_how_e waithow) +{ + RESULTCODES codes; + ULONG rc; + PID pid; + rc = DosWaitChild(DCWA_PROCESS, waithow == APR_WAIT ? DCWW_WAIT : DCWW_NOWAIT, &codes, &pid, proc->pid); + + if (rc == 0) { + proces_result_codes(codes, exitcode, exitwhy); + return APR_CHILD_DONE; + } else if (rc == ERROR_CHILD_NOT_COMPLETE) { + return APR_CHILD_NOTDONE; + } + + return APR_OS2_STATUS(rc); +} + + + +APR_DECLARE(apr_status_t) apr_proc_detach(int daemonize) +{ + return APR_ENOTIMPL; +} + +APR_DECLARE(apr_status_t) apr_procattr_user_set(apr_procattr_t *attr, + const char *username, + const char *password) +{ + return APR_ENOTIMPL; +} + +APR_DECLARE(apr_status_t) apr_procattr_group_set(apr_procattr_t *attr, + const char *groupname) +{ + return APR_ENOTIMPL; +} + +APR_DECLARE(apr_status_t) apr_procattr_perms_set_register(apr_procattr_t *attr, + apr_perms_setfn_t *perms_set_fn, + void *data, + apr_fileperms_t perms) +{ + return APR_ENOTIMPL; +} diff --git a/threadproc/os2/signals.c b/threadproc/os2/signals.c new file mode 100644 index 0000000..e172712 --- /dev/null +++ b/threadproc/os2/signals.c @@ -0,0 +1 @@ +#include "../unix/signals.c" diff --git a/threadproc/os2/thread.c b/threadproc/os2/thread.c new file mode 100644 index 0000000..00ec4eb --- /dev/null +++ b/threadproc/os2/thread.c @@ -0,0 +1,260 @@ +/* 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. + */ + +#define INCL_DOSERRORS +#define INCL_DOS +#include "apr_arch_threadproc.h" +#include "apr_thread_proc.h" +#include "apr_general.h" +#include "apr_lib.h" +#include "apr_portable.h" +#include "apr_arch_file_io.h" +#include + +APR_DECLARE(apr_status_t) apr_threadattr_create(apr_threadattr_t **new, apr_pool_t *pool) +{ + (*new) = (apr_threadattr_t *)apr_palloc(pool, sizeof(apr_threadattr_t)); + + if ((*new) == NULL) { + return APR_ENOMEM; + } + + (*new)->pool = pool; + (*new)->attr = 0; + (*new)->stacksize = 0; + return APR_SUCCESS; +} + + + +APR_DECLARE(apr_status_t) apr_threadattr_detach_set(apr_threadattr_t *attr, apr_int32_t on) +{ + attr->attr |= APR_THREADATTR_DETACHED; + return APR_SUCCESS; +} + + + +APR_DECLARE(apr_status_t) apr_threadattr_detach_get(apr_threadattr_t *attr) +{ + return (attr->attr & APR_THREADATTR_DETACHED) ? APR_DETACH : APR_NOTDETACH; +} + +APR_DECLARE(apr_status_t) apr_threadattr_stacksize_set(apr_threadattr_t *attr, + apr_size_t stacksize) +{ + attr->stacksize = stacksize; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_threadattr_guardsize_set(apr_threadattr_t *attr, + apr_size_t size) +{ + return APR_ENOTIMPL; +} + +static void apr_thread_begin(void *arg) +{ + apr_thread_t *thread = (apr_thread_t *)arg; + thread->exitval = thread->func(thread, thread->data); +} + + + +APR_DECLARE(apr_status_t) apr_thread_create(apr_thread_t **new, apr_threadattr_t *attr, + apr_thread_start_t func, void *data, + apr_pool_t *pool) +{ + apr_status_t stat; + apr_thread_t *thread; + + thread = (apr_thread_t *)apr_palloc(pool, sizeof(apr_thread_t)); + *new = thread; + + if (thread == NULL) { + return APR_ENOMEM; + } + + thread->attr = attr; + thread->func = func; + thread->data = data; + stat = apr_pool_create(&thread->pool, pool); + + if (stat != APR_SUCCESS) { + return stat; + } + + if (attr == NULL) { + stat = apr_threadattr_create(&thread->attr, thread->pool); + + if (stat != APR_SUCCESS) { + return stat; + } + } + + thread->tid = _beginthread(apr_thread_begin, NULL, + thread->attr->stacksize > 0 ? + thread->attr->stacksize : APR_THREAD_STACKSIZE, + thread); + + if (thread->tid < 0) { + return errno; + } + + return APR_SUCCESS; +} + + + +APR_DECLARE(apr_os_thread_t) apr_os_thread_current() +{ + PIB *ppib; + TIB *ptib; + DosGetInfoBlocks(&ptib, &ppib); + return ptib->tib_ptib2->tib2_ultid; +} + + + +APR_DECLARE(apr_status_t) apr_thread_exit(apr_thread_t *thd, apr_status_t retval) +{ + thd->exitval = retval; + _endthread(); + return -1; /* If we get here something's wrong */ +} + + + +APR_DECLARE(apr_status_t) apr_thread_join(apr_status_t *retval, apr_thread_t *thd) +{ + ULONG rc; + TID waittid = thd->tid; + + if (thd->attr->attr & APR_THREADATTR_DETACHED) + return APR_EINVAL; + + rc = DosWaitThread(&waittid, DCWW_WAIT); + + if (rc == ERROR_INVALID_THREADID) + rc = 0; /* Thread had already terminated */ + + *retval = thd->exitval; + return APR_OS2_STATUS(rc); +} + + + +APR_DECLARE(apr_status_t) apr_thread_detach(apr_thread_t *thd) +{ + thd->attr->attr |= APR_THREADATTR_DETACHED; + return APR_SUCCESS; +} + + + +void apr_thread_yield() +{ + DosSleep(0); +} + + + +APR_DECLARE(apr_status_t) apr_os_thread_get(apr_os_thread_t **thethd, apr_thread_t *thd) +{ + *thethd = &thd->tid; + return APR_SUCCESS; +} + + + +APR_DECLARE(apr_status_t) apr_os_thread_put(apr_thread_t **thd, apr_os_thread_t *thethd, + apr_pool_t *pool) +{ + if ((*thd) == NULL) { + (*thd) = (apr_thread_t *)apr_pcalloc(pool, sizeof(apr_thread_t)); + (*thd)->pool = pool; + } + (*thd)->tid = *thethd; + return APR_SUCCESS; +} + + + +int apr_os_thread_equal(apr_os_thread_t tid1, apr_os_thread_t tid2) +{ + return tid1 == tid2; +} + + + +APR_DECLARE(apr_status_t) apr_thread_data_get(void **data, const char *key, apr_thread_t *thread) +{ + return apr_pool_userdata_get(data, key, thread->pool); +} + + + +APR_DECLARE(apr_status_t) apr_thread_data_set(void *data, const char *key, + apr_status_t (*cleanup) (void *), + apr_thread_t *thread) +{ + return apr_pool_userdata_set(data, key, cleanup, thread->pool); +} + +APR_POOL_IMPLEMENT_ACCESSOR(thread) + + + +static apr_status_t thread_once_cleanup(void *vcontrol) +{ + apr_thread_once_t *control = (apr_thread_once_t *)vcontrol; + + if (control->sem) { + DosCloseEventSem(control->sem); + } + + return APR_SUCCESS; +} + + + +APR_DECLARE(apr_status_t) apr_thread_once_init(apr_thread_once_t **control, + apr_pool_t *p) +{ + ULONG rc; + *control = (apr_thread_once_t *)apr_pcalloc(p, sizeof(apr_thread_once_t)); + rc = DosCreateEventSem(NULL, &(*control)->sem, 0, TRUE); + apr_pool_cleanup_register(p, control, thread_once_cleanup, apr_pool_cleanup_null); + return APR_FROM_OS_ERROR(rc); +} + + + +APR_DECLARE(apr_status_t) apr_thread_once(apr_thread_once_t *control, + void (*func)(void)) +{ + if (!control->hit) { + ULONG count, rc; + rc = DosResetEventSem(control->sem, &count); + + if (rc == 0 && count) { + control->hit = 1; + func(); + } + } + + return APR_SUCCESS; +} diff --git a/threadproc/os2/threadpriv.c b/threadproc/os2/threadpriv.c new file mode 100644 index 0000000..107ec10 --- /dev/null +++ b/threadproc/os2/threadpriv.c @@ -0,0 +1,88 @@ +/* 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_arch_threadproc.h" +#include "apr_thread_proc.h" +#include "apr_portable.h" +#include "apr_general.h" +#include "apr_errno.h" +#include "apr_lib.h" +#include "apr_arch_file_io.h" + +APR_DECLARE(apr_status_t) apr_threadkey_private_create(apr_threadkey_t **key, + void (*dest)(void *), + apr_pool_t *pool) +{ + (*key) = (apr_threadkey_t *)apr_palloc(pool, sizeof(apr_threadkey_t)); + + if ((*key) == NULL) { + return APR_ENOMEM; + } + + (*key)->pool = pool; + return APR_OS2_STATUS(DosAllocThreadLocalMemory(1, &((*key)->key))); +} + +APR_DECLARE(apr_status_t) apr_threadkey_private_get(void **new, apr_threadkey_t *key) +{ + (*new) = (void *)*(key->key); + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_threadkey_private_set(void *priv, apr_threadkey_t *key) +{ + *(key->key) = (ULONG)priv; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_threadkey_private_delete(apr_threadkey_t *key) +{ + return APR_OS2_STATUS(DosFreeThreadLocalMemory(key->key)); +} + +APR_DECLARE(apr_status_t) apr_threadkey_data_get(void **data, const char *key, + apr_threadkey_t *threadkey) +{ + return apr_pool_userdata_get(data, key, threadkey->pool); +} + +APR_DECLARE(apr_status_t) apr_threadkey_data_set(void *data, const char *key, + apr_status_t (*cleanup) (void *), + apr_threadkey_t *threadkey) +{ + return apr_pool_userdata_set(data, key, cleanup, threadkey->pool); +} + +APR_DECLARE(apr_status_t) apr_os_threadkey_get(apr_os_threadkey_t *thekey, apr_threadkey_t *key) +{ + *thekey = key->key; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_os_threadkey_put(apr_threadkey_t **key, + apr_os_threadkey_t *thekey, + apr_pool_t *pool) +{ + if (pool == NULL) { + return APR_ENOPOOL; + } + if ((*key) == NULL) { + (*key) = (apr_threadkey_t *)apr_pcalloc(pool, sizeof(apr_threadkey_t)); + (*key)->pool = pool; + } + (*key)->key = *thekey; + return APR_SUCCESS; +} diff --git a/threadproc/unix/proc.c b/threadproc/unix/proc.c new file mode 100644 index 0000000..004772f --- /dev/null +++ b/threadproc/unix/proc.c @@ -0,0 +1,739 @@ +/* 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_arch_threadproc.h" +#include "apr_strings.h" +#include "apr_portable.h" +#include "apr_signal.h" +#include "apr_random.h" + +/* Heavy on no'ops, here's what we want to pass if there is APR_NO_FILE + * requested for a specific child handle; + */ +static apr_file_t no_file = { NULL, -1, }; + +APR_DECLARE(apr_status_t) apr_procattr_create(apr_procattr_t **new, + apr_pool_t *pool) +{ + (*new) = (apr_procattr_t *)apr_pcalloc(pool, sizeof(apr_procattr_t)); + + if ((*new) == NULL) { + return APR_ENOMEM; + } + (*new)->pool = pool; + (*new)->cmdtype = APR_PROGRAM; + (*new)->uid = (*new)->gid = -1; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_io_set(apr_procattr_t *attr, + apr_int32_t in, + apr_int32_t out, + apr_int32_t err) +{ + apr_status_t rv; + + if ((in != APR_NO_PIPE) && (in != APR_NO_FILE)) { + /* APR_CHILD_BLOCK maps to APR_WRITE_BLOCK, while + * APR_PARENT_BLOCK maps to APR_READ_BLOCK, so transpose + * the CHILD/PARENT blocking flags for the stdin pipe. + * stdout/stderr map to the correct mode by default. + */ + if (in == APR_CHILD_BLOCK) + in = APR_READ_BLOCK; + else if (in == APR_PARENT_BLOCK) + in = APR_WRITE_BLOCK; + + if ((rv = apr_file_pipe_create_ex(&attr->child_in, &attr->parent_in, + in, attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_in); + if (rv != APR_SUCCESS) + return rv; + } + else if (in == APR_NO_FILE) + attr->child_in = &no_file; + + if ((out != APR_NO_PIPE) && (out != APR_NO_FILE)) { + if ((rv = apr_file_pipe_create_ex(&attr->parent_out, &attr->child_out, + out, attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_out); + if (rv != APR_SUCCESS) + return rv; + } + else if (out == APR_NO_FILE) + attr->child_out = &no_file; + + if ((err != APR_NO_PIPE) && (err != APR_NO_FILE)) { + if ((rv = apr_file_pipe_create_ex(&attr->parent_err, &attr->child_err, + err, attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_err); + if (rv != APR_SUCCESS) + return rv; + } + else if (err == APR_NO_FILE) + attr->child_err = &no_file; + + return APR_SUCCESS; +} + + +APR_DECLARE(apr_status_t) apr_procattr_child_in_set(apr_procattr_t *attr, + apr_file_t *child_in, + apr_file_t *parent_in) +{ + apr_status_t rv = APR_SUCCESS; + + if (attr->child_in == NULL && attr->parent_in == NULL + && child_in == NULL && parent_in == NULL) + if ((rv = apr_file_pipe_create(&attr->child_in, &attr->parent_in, + attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_in); + + if (child_in != NULL && rv == APR_SUCCESS) { + if (attr->child_in && (attr->child_in->filedes != -1)) + rv = apr_file_dup2(attr->child_in, child_in, attr->pool); + else { + attr->child_in = NULL; + if ((rv = apr_file_dup(&attr->child_in, child_in, attr->pool)) + == APR_SUCCESS) + rv = apr_file_inherit_set(attr->child_in); + } + } + + if (parent_in != NULL && rv == APR_SUCCESS) { + if (attr->parent_in) + rv = apr_file_dup2(attr->parent_in, parent_in, attr->pool); + else + rv = apr_file_dup(&attr->parent_in, parent_in, attr->pool); + } + + return rv; +} + + +APR_DECLARE(apr_status_t) apr_procattr_child_out_set(apr_procattr_t *attr, + apr_file_t *child_out, + apr_file_t *parent_out) +{ + apr_status_t rv = APR_SUCCESS; + + if (attr->child_out == NULL && attr->parent_out == NULL + && child_out == NULL && parent_out == NULL) + if ((rv = apr_file_pipe_create(&attr->parent_out, &attr->child_out, + attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_out); + + if (child_out != NULL && rv == APR_SUCCESS) { + if (attr->child_out && (attr->child_out->filedes != -1)) + rv = apr_file_dup2(attr->child_out, child_out, attr->pool); + else { + attr->child_out = NULL; + if ((rv = apr_file_dup(&attr->child_out, child_out, attr->pool)) + == APR_SUCCESS) + rv = apr_file_inherit_set(attr->child_out); + } + } + + if (parent_out != NULL && rv == APR_SUCCESS) { + if (attr->parent_out) + rv = apr_file_dup2(attr->parent_out, parent_out, attr->pool); + else + rv = apr_file_dup(&attr->parent_out, parent_out, attr->pool); + } + + return rv; +} + + +APR_DECLARE(apr_status_t) apr_procattr_child_err_set(apr_procattr_t *attr, + apr_file_t *child_err, + apr_file_t *parent_err) +{ + apr_status_t rv = APR_SUCCESS; + + if (attr->child_err == NULL && attr->parent_err == NULL + && child_err == NULL && parent_err == NULL) + if ((rv = apr_file_pipe_create(&attr->parent_err, &attr->child_err, + attr->pool)) == APR_SUCCESS) + rv = apr_file_inherit_unset(attr->parent_err); + + if (child_err != NULL && rv == APR_SUCCESS) { + if (attr->child_err && (attr->child_err->filedes != -1)) + rv = apr_file_dup2(attr->child_err, child_err, attr->pool); + else { + attr->child_err = NULL; + if ((rv = apr_file_dup(&attr->child_err, child_err, attr->pool)) + == APR_SUCCESS) + rv = apr_file_inherit_set(attr->child_err); + } + } + if (parent_err != NULL && rv == APR_SUCCESS) { + if (attr->parent_err) + rv = apr_file_dup2(attr->parent_err, parent_err, attr->pool); + else + rv = apr_file_dup(&attr->parent_err, parent_err, attr->pool); + } + + return rv; +} + + +APR_DECLARE(apr_status_t) apr_procattr_dir_set(apr_procattr_t *attr, + const char *dir) +{ + attr->currdir = apr_pstrdup(attr->pool, dir); + if (attr->currdir) { + return APR_SUCCESS; + } + + return APR_ENOMEM; +} + +APR_DECLARE(apr_status_t) apr_procattr_cmdtype_set(apr_procattr_t *attr, + apr_cmdtype_e cmd) +{ + attr->cmdtype = cmd; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_detach_set(apr_procattr_t *attr, + apr_int32_t detach) +{ + attr->detached = detach; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_proc_fork(apr_proc_t *proc, apr_pool_t *pool) +{ + int pid; + + memset(proc, 0, sizeof(apr_proc_t)); + + if ((pid = fork()) < 0) { + return errno; + } + else if (pid == 0) { + proc->pid = getpid(); + + apr_random_after_fork(proc); + + return APR_INCHILD; + } + + proc->pid = pid; + + return APR_INPARENT; +} + +static apr_status_t limit_proc(apr_procattr_t *attr) +{ +#if APR_HAVE_STRUCT_RLIMIT && APR_HAVE_SETRLIMIT +#ifdef RLIMIT_CPU + if (attr->limit_cpu != NULL) { + if ((setrlimit(RLIMIT_CPU, attr->limit_cpu)) != 0) { + return errno; + } + } +#endif +#ifdef RLIMIT_NPROC + if (attr->limit_nproc != NULL) { + if ((setrlimit(RLIMIT_NPROC, attr->limit_nproc)) != 0) { + return errno; + } + } +#endif +#ifdef RLIMIT_NOFILE + if (attr->limit_nofile != NULL) { + if ((setrlimit(RLIMIT_NOFILE, attr->limit_nofile)) != 0) { + return errno; + } + } +#endif +#if defined(RLIMIT_AS) + if (attr->limit_mem != NULL) { + if ((setrlimit(RLIMIT_AS, attr->limit_mem)) != 0) { + return errno; + } + } +#elif defined(RLIMIT_DATA) + if (attr->limit_mem != NULL) { + if ((setrlimit(RLIMIT_DATA, attr->limit_mem)) != 0) { + return errno; + } + } +#elif defined(RLIMIT_VMEM) + if (attr->limit_mem != NULL) { + if ((setrlimit(RLIMIT_VMEM, attr->limit_mem)) != 0) { + return errno; + } + } +#endif +#else + /* + * Maybe make a note in error_log that setrlimit isn't supported?? + */ + +#endif + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_child_errfn_set(apr_procattr_t *attr, + apr_child_errfn_t *errfn) +{ + attr->errfn = errfn; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_error_check_set(apr_procattr_t *attr, + apr_int32_t chk) +{ + attr->errchk = chk; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_addrspace_set(apr_procattr_t *attr, + apr_int32_t addrspace) +{ + /* won't ever be used on this platform, so don't save the flag */ + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_user_set(apr_procattr_t *attr, + const char *username, + const char *password) +{ + apr_status_t rv; + apr_gid_t gid; + + if ((rv = apr_uid_get(&attr->uid, &gid, username, + attr->pool)) != APR_SUCCESS) { + attr->uid = -1; + return rv; + } + + /* Use default user group if not already set */ + if (attr->gid == -1) { + attr->gid = gid; + } + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_group_set(apr_procattr_t *attr, + const char *groupname) +{ + apr_status_t rv; + + if ((rv = apr_gid_get(&attr->gid, groupname, attr->pool)) != APR_SUCCESS) + attr->gid = -1; + return rv; +} + +APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *new, + const char *progname, + const char * const *args, + const char * const *env, + apr_procattr_t *attr, + apr_pool_t *pool) +{ + int i; + const char * const empty_envp[] = {NULL}; + + if (!env) { /* Specs require an empty array instead of NULL; + * Purify will trigger a failure, even if many + * implementations don't. + */ + env = empty_envp; + } + + new->in = attr->parent_in; + new->err = attr->parent_err; + new->out = attr->parent_out; + + if (attr->errchk) { + if (attr->currdir) { + if (access(attr->currdir, X_OK) == -1) { + /* chdir() in child wouldn't have worked */ + return errno; + } + } + + if (attr->cmdtype == APR_PROGRAM || + attr->cmdtype == APR_PROGRAM_ENV || + *progname == '/') { + /* for both of these values of cmdtype, caller must pass + * full path, so it is easy to check; + * caller can choose to pass full path for other + * values of cmdtype + */ + if (access(progname, X_OK) == -1) { + /* exec*() in child wouldn't have worked */ + return errno; + } + } + else { + /* todo: search PATH for progname then try to access it */ + } + } + + if ((new->pid = fork()) < 0) { + return errno; + } + else if (new->pid == 0) { + /* child process */ + + /* + * If we do exec cleanup before the dup2() calls to set up pipes + * on 0-2, we accidentally close the pipes used by programs like + * mod_cgid. + * + * If we do exec cleanup after the dup2() calls, cleanup can accidentally + * close our pipes which replaced any files which previously had + * descriptors 0-2. + * + * The solution is to kill the cleanup for the pipes, then do + * exec cleanup, then do the dup2() calls. + */ + + if (attr->child_in) { + apr_pool_cleanup_kill(apr_file_pool_get(attr->child_in), + attr->child_in, apr_unix_file_cleanup); + } + + if (attr->child_out) { + apr_pool_cleanup_kill(apr_file_pool_get(attr->child_out), + attr->child_out, apr_unix_file_cleanup); + } + + if (attr->child_err) { + apr_pool_cleanup_kill(apr_file_pool_get(attr->child_err), + attr->child_err, apr_unix_file_cleanup); + } + + apr_pool_cleanup_for_exec(); + + if ((attr->child_in) && (attr->child_in->filedes == -1)) { + close(STDIN_FILENO); + } + else if (attr->child_in && + attr->child_in->filedes != STDIN_FILENO) { + dup2(attr->child_in->filedes, STDIN_FILENO); + apr_file_close(attr->child_in); + } + + if ((attr->child_out) && (attr->child_out->filedes == -1)) { + close(STDOUT_FILENO); + } + else if (attr->child_out && + attr->child_out->filedes != STDOUT_FILENO) { + dup2(attr->child_out->filedes, STDOUT_FILENO); + apr_file_close(attr->child_out); + } + + if ((attr->child_err) && (attr->child_err->filedes == -1)) { + close(STDERR_FILENO); + } + else if (attr->child_err && + attr->child_err->filedes != STDERR_FILENO) { + dup2(attr->child_err->filedes, STDERR_FILENO); + apr_file_close(attr->child_err); + } + + apr_signal(SIGCHLD, SIG_DFL); /* not sure if this is needed or not */ + + if (attr->currdir != NULL) { + if (chdir(attr->currdir) == -1) { + if (attr->errfn) { + attr->errfn(pool, errno, "change of working directory failed"); + } + _exit(-1); /* We have big problems, the child should exit. */ + } + } + if (!geteuid()) { + apr_procattr_pscb_t *c = attr->perms_set_callbacks; + + while (c) { + apr_status_t r; + r = (*c->perms_set_fn)((void *)c->data, c->perms, + attr->uid, attr->gid); + if (r != APR_SUCCESS && r != APR_ENOTIMPL) { + _exit(-1); + } + c = c->next; + } + } + /* Only try to switch if we are running as root */ + if (attr->gid != -1 && !geteuid()) { + if (setgid(attr->gid)) { + if (attr->errfn) { + attr->errfn(pool, errno, "setting of group failed"); + } + _exit(-1); /* We have big problems, the child should exit. */ + } + } + + if (attr->uid != -1 && !geteuid()) { + if (setuid(attr->uid)) { + if (attr->errfn) { + attr->errfn(pool, errno, "setting of user failed"); + } + _exit(-1); /* We have big problems, the child should exit. */ + } + } + + if (limit_proc(attr) != APR_SUCCESS) { + if (attr->errfn) { + attr->errfn(pool, errno, "setting of resource limits failed"); + } + _exit(-1); /* We have big problems, the child should exit. */ + } + + if (attr->cmdtype == APR_SHELLCMD || + attr->cmdtype == APR_SHELLCMD_ENV) { + int onearg_len = 0; + const char *newargs[4]; + + newargs[0] = SHELL_PATH; + newargs[1] = "-c"; + + i = 0; + while (args[i]) { + onearg_len += strlen(args[i]); + onearg_len++; /* for space delimiter */ + i++; + } + + switch(i) { + case 0: + /* bad parameters; we're doomed */ + break; + case 1: + /* no args, or caller already built a single string from + * progname and args + */ + newargs[2] = args[0]; + break; + default: + { + char *ch, *onearg; + + ch = onearg = apr_palloc(pool, onearg_len); + i = 0; + while (args[i]) { + size_t len = strlen(args[i]); + + memcpy(ch, args[i], len); + ch += len; + *ch = ' '; + ++ch; + ++i; + } + --ch; /* back up to trailing blank */ + *ch = '\0'; + newargs[2] = onearg; + } + } + + newargs[3] = NULL; + + if (attr->detached) { + apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); + } + + if (attr->cmdtype == APR_SHELLCMD) { + execve(SHELL_PATH, (char * const *) newargs, (char * const *)env); + } + else { + execv(SHELL_PATH, (char * const *)newargs); + } + } + else if (attr->cmdtype == APR_PROGRAM) { + if (attr->detached) { + apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); + } + + execve(progname, (char * const *)args, (char * const *)env); + } + else if (attr->cmdtype == APR_PROGRAM_ENV) { + if (attr->detached) { + apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); + } + + execv(progname, (char * const *)args); + } + else { + /* APR_PROGRAM_PATH */ + if (attr->detached) { + apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); + } + + execvp(progname, (char * const *)args); + } + if (attr->errfn) { + char *desc; + + desc = apr_psprintf(pool, "exec of '%s' failed", + progname); + attr->errfn(pool, errno, desc); + } + + _exit(-1); /* if we get here, there is a problem, so exit with an + * error code. */ + } + + /* Parent process */ + if (attr->child_in && (attr->child_in->filedes != -1)) { + apr_file_close(attr->child_in); + } + + if (attr->child_out && (attr->child_out->filedes != -1)) { + apr_file_close(attr->child_out); + } + + if (attr->child_err && (attr->child_err->filedes != -1)) { + apr_file_close(attr->child_err); + } + + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_proc_wait_all_procs(apr_proc_t *proc, + int *exitcode, + apr_exit_why_e *exitwhy, + apr_wait_how_e waithow, + apr_pool_t *p) +{ + proc->pid = -1; + return apr_proc_wait(proc, exitcode, exitwhy, waithow); +} + +APR_DECLARE(apr_status_t) apr_proc_wait(apr_proc_t *proc, + int *exitcode, apr_exit_why_e *exitwhy, + apr_wait_how_e waithow) +{ + pid_t pstatus; + int waitpid_options = WUNTRACED; + int exit_int; + int ignore; + apr_exit_why_e ignorewhy; + + if (exitcode == NULL) { + exitcode = &ignore; + } + + if (exitwhy == NULL) { + exitwhy = &ignorewhy; + } + + if (waithow != APR_WAIT) { + waitpid_options |= WNOHANG; + } + + do { + pstatus = waitpid(proc->pid, &exit_int, waitpid_options); + } while (pstatus < 0 && errno == EINTR); + + if (pstatus > 0) { + proc->pid = pstatus; + + if (WIFEXITED(exit_int)) { + *exitwhy = APR_PROC_EXIT; + *exitcode = WEXITSTATUS(exit_int); + } + else if (WIFSIGNALED(exit_int)) { + *exitwhy = APR_PROC_SIGNAL; + +#ifdef WCOREDUMP + if (WCOREDUMP(exit_int)) { + *exitwhy |= APR_PROC_SIGNAL_CORE; + } +#endif + + *exitcode = WTERMSIG(exit_int); + } + else { + /* unexpected condition */ + return APR_EGENERAL; + } + + return APR_CHILD_DONE; + } + else if (pstatus == 0) { + return APR_CHILD_NOTDONE; + } + + return errno; +} + +#if APR_HAVE_STRUCT_RLIMIT +APR_DECLARE(apr_status_t) apr_procattr_limit_set(apr_procattr_t *attr, + apr_int32_t what, + struct rlimit *limit) +{ + switch(what) { + case APR_LIMIT_CPU: +#ifdef RLIMIT_CPU + attr->limit_cpu = limit; + break; +#else + return APR_ENOTIMPL; +#endif + + case APR_LIMIT_MEM: +#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) + attr->limit_mem = limit; + break; +#else + return APR_ENOTIMPL; +#endif + + case APR_LIMIT_NPROC: +#ifdef RLIMIT_NPROC + attr->limit_nproc = limit; + break; +#else + return APR_ENOTIMPL; +#endif + + case APR_LIMIT_NOFILE: +#ifdef RLIMIT_NOFILE + attr->limit_nofile = limit; + break; +#else + return APR_ENOTIMPL; +#endif + + } + + return APR_SUCCESS; +} +#endif /* APR_HAVE_STRUCT_RLIMIT */ + +APR_DECLARE(apr_status_t) apr_procattr_perms_set_register(apr_procattr_t *attr, + apr_perms_setfn_t *perms_set_fn, + void *data, + apr_fileperms_t perms) +{ + apr_procattr_pscb_t *c; + + c = apr_palloc(attr->pool, sizeof(apr_procattr_pscb_t)); + c->data = data; + c->perms = perms; + c->perms_set_fn = perms_set_fn; + c->next = attr->perms_set_callbacks; + attr->perms_set_callbacks = c; + + return APR_SUCCESS; +} diff --git a/threadproc/unix/procsup.c b/threadproc/unix/procsup.c new file mode 100644 index 0000000..94177f9 --- /dev/null +++ b/threadproc/unix/procsup.c @@ -0,0 +1,104 @@ +/* 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_arch_threadproc.h" + +APR_DECLARE(apr_status_t) apr_proc_detach(int daemonize) +{ + if (chdir("/") == -1) { + return errno; + } + +#if !defined(MPE) && !defined(OS2) && !defined(TPF) && !defined(BEOS) + /* Don't detach for MPE because child processes can't survive the death of + * the parent. */ + if (daemonize) { + int x; + + if ((x = fork()) > 0) { + exit(0); + } + else if (x == -1) { + perror("fork"); + fprintf(stderr, "unable to fork new process\n"); + exit(1); /* we can't do anything here, so just exit. */ + } + /* RAISE_SIGSTOP(DETACH); */ + } +#endif + +#ifdef HAVE_SETSID + /* A setsid() failure is not fatal if we didn't just fork(). + * The calling process may be the process group leader, in + * which case setsid() will fail with EPERM. + */ + if (setsid() == -1 && daemonize) { + return errno; + } +#elif defined(NEXT) || defined(NEWSOS) + if (setpgrp(0, getpid()) == -1) { + return errno; + } +#elif defined(OS2) || defined(TPF) || defined(MPE) + /* do nothing */ +#else + if (setpgid(0, 0) == -1) { + return errno; + } +#endif + + /* close out the standard file descriptors */ + if (freopen("/dev/null", "r", stdin) == NULL) { + return errno; + /* continue anyhow -- note we can't close out descriptor 0 because we + * have nothing to replace it with, and if we didn't have a descriptor + * 0 the next file would be created with that value ... leading to + * havoc. + */ + } + if (freopen("/dev/null", "w", stdout) == NULL) { + return errno; + } + /* We are going to reopen this again in a little while to the error + * log file, but better to do it twice and suffer a small performance + * hit for consistancy than not reopen it here. + */ + if (freopen("/dev/null", "w", stderr) == NULL) { + return errno; + } + return APR_SUCCESS; +} + +#if (!HAVE_WAITPID) +/* From ikluft@amdahl.com + * this is not ideal but it works for SVR3 variants + * Modified by dwd@bell-labs.com to call wait3 instead of wait because + * apache started to use the WNOHANG option. + */ +int waitpid(pid_t pid, int *statusp, int options) +{ + int tmp_pid; + if (kill(pid, 0) == -1) { + errno = ECHILD; + return -1; + } + while (((tmp_pid = wait3(statusp, options, 0)) != pid) && + (tmp_pid != -1) && (tmp_pid != 0) && (pid != -1)) + ; + return tmp_pid; +} +#endif + diff --git a/threadproc/unix/signals.c b/threadproc/unix/signals.c new file mode 100644 index 0000000..c735eab --- /dev/null +++ b/threadproc/unix/signals.c @@ -0,0 +1,481 @@ +/* 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. + */ + +#define INCL_DOSEXCEPTIONS /* for OS2 */ +#include "apr_arch_threadproc.h" +#include "apr_private.h" +#include "apr_pools.h" +#include "apr_signal.h" +#include "apr_strings.h" + +#include +#if APR_HAS_THREADS && APR_HAVE_PTHREAD_H +#include +#endif + +#ifdef SIGWAIT_TAKES_ONE_ARG +#define apr_sigwait(a,b) ((*(b)=sigwait((a)))<0?-1:0) +#else +#define apr_sigwait(a,b) sigwait((a),(b)) +#endif + +APR_DECLARE(apr_status_t) apr_proc_kill(apr_proc_t *proc, int signum) +{ +#ifdef OS2 + /* SIGTERM's don't work too well in OS/2 (only affects other EMX + * programs). CGIs may not be, esp. REXX scripts, so use a native + * call instead + */ + if (signum == SIGTERM) { + return APR_OS2_STATUS(DosSendSignalException(proc->pid, + XCPT_SIGNAL_BREAK)); + } +#endif /* OS2 */ + + if (kill(proc->pid, signum) == -1) { + return errno; + } + + return APR_SUCCESS; +} + + +#if APR_HAVE_SIGACTION + +#if defined(__NetBSD__) || defined(DARWIN) +static void avoid_zombies(int signo) +{ + int exit_status; + + while (waitpid(-1, &exit_status, WNOHANG) > 0) { + /* do nothing */ + } +} +#endif /* DARWIN */ + +/* + * Replace standard signal() with the more reliable sigaction equivalent + * from W. Richard Stevens' "Advanced Programming in the UNIX Environment" + * (the version that does not automatically restart system calls). + */ +APR_DECLARE(apr_sigfunc_t *) apr_signal(int signo, apr_sigfunc_t * func) +{ + struct sigaction act, oact; + + act.sa_handler = func; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; +#ifdef SA_INTERRUPT /* SunOS */ + act.sa_flags |= SA_INTERRUPT; +#endif +#if defined(__osf__) && defined(__alpha) + /* XXX jeff thinks this should be enabled whenever SA_NOCLDWAIT is defined */ + + /* this is required on Tru64 to cause child processes to + * disappear gracefully - XPG4 compatible + */ + if ((signo == SIGCHLD) && (func == SIG_IGN)) { + act.sa_flags |= SA_NOCLDWAIT; + } +#endif +#if defined(__NetBSD__) || defined(DARWIN) + /* ignoring SIGCHLD or leaving the default disposition doesn't avoid zombies, + * and there is no SA_NOCLDWAIT flag, so catch the signal and reap status in + * the handler to avoid zombies + */ + if ((signo == SIGCHLD) && (func == SIG_IGN)) { + act.sa_handler = avoid_zombies; + } +#endif + if (sigaction(signo, &act, &oact) < 0) + return SIG_ERR; + return oact.sa_handler; +} + +#endif /* HAVE_SIGACTION */ + +/* AC_DECL_SYS_SIGLIST defines either of these symbols depending + * on the version of autoconf used. */ +#if defined(SYS_SIGLIST_DECLARED) || HAVE_DECL_SYS_SIGLIST + +void apr_signal_init(apr_pool_t *pglobal) +{ +} +const char *apr_signal_description_get(int signum) +{ + return (signum >= 0) ? sys_siglist[signum] : "unknown signal (number)"; +} + +#else /* !(SYS_SIGLIST_DECLARED || HAVE_DECL_SYS_SIGLIST) */ + +/* we need to roll our own signal description stuff */ + +#if defined(NSIG) +#define APR_NUMSIG NSIG +#elif defined(_NSIG) +#define APR_NUMSIG _NSIG +#elif defined(__NSIG) +#define APR_NUMSIG __NSIG +#else +#define APR_NUMSIG 33 /* breaks on OS/390 with < 33; 32 is o.k. for most */ +#endif + +static const char *signal_description[APR_NUMSIG]; + +#define store_desc(index, string) \ + do { \ + if (index >= APR_NUMSIG) { \ + assert(index < APR_NUMSIG); \ + } \ + else { \ + signal_description[index] = string; \ + } \ + } while (0) + +void apr_signal_init(apr_pool_t *pglobal) +{ + int sig; + + store_desc(0, "Signal 0"); + +#ifdef SIGHUP + store_desc(SIGHUP, "Hangup"); +#endif +#ifdef SIGINT + store_desc(SIGINT, "Interrupt"); +#endif +#ifdef SIGQUIT + store_desc(SIGQUIT, "Quit"); +#endif +#ifdef SIGILL + store_desc(SIGILL, "Illegal instruction"); +#endif +#ifdef SIGTRAP + store_desc(SIGTRAP, "Trace/BPT trap"); +#endif +#ifdef SIGIOT + store_desc(SIGIOT, "IOT instruction"); +#endif +#ifdef SIGABRT + store_desc(SIGABRT, "Abort"); +#endif +#ifdef SIGEMT + store_desc(SIGEMT, "Emulator trap"); +#endif +#ifdef SIGFPE + store_desc(SIGFPE, "Arithmetic exception"); +#endif +#ifdef SIGKILL + store_desc(SIGKILL, "Killed"); +#endif +#ifdef SIGBUS + store_desc(SIGBUS, "Bus error"); +#endif +#ifdef SIGSEGV + store_desc(SIGSEGV, "Segmentation fault"); +#endif +#ifdef SIGSYS + store_desc(SIGSYS, "Bad system call"); +#endif +#ifdef SIGPIPE + store_desc(SIGPIPE, "Broken pipe"); +#endif +#ifdef SIGALRM + store_desc(SIGALRM, "Alarm clock"); +#endif +#ifdef SIGTERM + store_desc(SIGTERM, "Terminated"); +#endif +#ifdef SIGUSR1 + store_desc(SIGUSR1, "User defined signal 1"); +#endif +#ifdef SIGUSR2 + store_desc(SIGUSR2, "User defined signal 2"); +#endif +#ifdef SIGCLD + store_desc(SIGCLD, "Child status change"); +#endif +#ifdef SIGCHLD + store_desc(SIGCHLD, "Child status change"); +#endif +#ifdef SIGPWR + store_desc(SIGPWR, "Power-fail restart"); +#endif +#ifdef SIGWINCH + store_desc(SIGWINCH, "Window changed"); +#endif +#ifdef SIGURG + store_desc(SIGURG, "urgent socket condition"); +#endif +#ifdef SIGPOLL + store_desc(SIGPOLL, "Pollable event occurred"); +#endif +#ifdef SIGIO + store_desc(SIGIO, "socket I/O possible"); +#endif +#ifdef SIGSTOP + store_desc(SIGSTOP, "Stopped (signal)"); +#endif +#ifdef SIGTSTP + store_desc(SIGTSTP, "Stopped"); +#endif +#ifdef SIGCONT + store_desc(SIGCONT, "Continued"); +#endif +#ifdef SIGTTIN + store_desc(SIGTTIN, "Stopped (tty input)"); +#endif +#ifdef SIGTTOU + store_desc(SIGTTOU, "Stopped (tty output)"); +#endif +#ifdef SIGVTALRM + store_desc(SIGVTALRM, "virtual timer expired"); +#endif +#ifdef SIGPROF + store_desc(SIGPROF, "profiling timer expired"); +#endif +#ifdef SIGXCPU + store_desc(SIGXCPU, "exceeded cpu limit"); +#endif +#ifdef SIGXFSZ + store_desc(SIGXFSZ, "exceeded file size limit"); +#endif + + for (sig = 0; sig < APR_NUMSIG; ++sig) + if (signal_description[sig] == NULL) + signal_description[sig] = apr_psprintf(pglobal, "signal #%d", sig); +} + +const char *apr_signal_description_get(int signum) +{ + return + (signum >= 0 && signum < APR_NUMSIG) + ? signal_description[signum] + : "unknown signal (number)"; +} + +#endif /* SYS_SIGLIST_DECLARED || HAVE_DECL_SYS_SIGLIST */ + +#if APR_HAS_THREADS && (HAVE_SIGSUSPEND || APR_HAVE_SIGWAIT) && !defined(OS2) + +static void remove_sync_sigs(sigset_t *sig_mask) +{ +#ifdef SIGABRT + sigdelset(sig_mask, SIGABRT); +#endif +#ifdef SIGBUS + sigdelset(sig_mask, SIGBUS); +#endif +#ifdef SIGEMT + sigdelset(sig_mask, SIGEMT); +#endif +#ifdef SIGFPE + sigdelset(sig_mask, SIGFPE); +#endif +#ifdef SIGILL + sigdelset(sig_mask, SIGILL); +#endif +#ifdef SIGIOT + sigdelset(sig_mask, SIGIOT); +#endif +#ifdef SIGPIPE + sigdelset(sig_mask, SIGPIPE); +#endif +#ifdef SIGSEGV + sigdelset(sig_mask, SIGSEGV); +#endif +#ifdef SIGSYS + sigdelset(sig_mask, SIGSYS); +#endif +#ifdef SIGTRAP + sigdelset(sig_mask, SIGTRAP); +#endif + +/* the rest of the signals removed from the mask in this function + * absolutely must be removed; you cannot block synchronous signals + * (requirement of pthreads API) + */ +} + +APR_DECLARE(apr_status_t) apr_signal_thread(int(*signal_handler)(int signum)) +{ + sigset_t sig_mask; +#if APR_HAVE_SIGWAIT + int (*sig_func)(int signum) = (int (*)(int))signal_handler; +#endif + + /* This thread will be the one responsible for handling signals */ + sigfillset(&sig_mask); + + /* On certain platforms, sigwait() returns EINVAL if any of various + * unblockable signals are included in the mask. This was first + * observed on AIX and Tru64. + */ +#ifdef SIGKILL + sigdelset(&sig_mask, SIGKILL); +#endif +#ifdef SIGSTOP + sigdelset(&sig_mask, SIGSTOP); +#endif +#ifdef SIGCONT + sigdelset(&sig_mask, SIGCONT); +#endif +#ifdef SIGWAITING + sigdelset(&sig_mask, SIGWAITING); +#endif + + /* no synchronous signals should be in the mask passed to sigwait() */ + remove_sync_sigs(&sig_mask); + + /* On AIX (4.3.3, at least), sigwait() won't wake up if the high- + * order bit of the second word of flags is turned on. sigdelset() + * returns an error when trying to turn this off, so we'll turn it + * off manually. + * + * Note that the private fields differ between 32-bit and 64-bit + * and even between _ALL_SOURCE and !_ALL_SOURCE. Except that on + * AIX 4.3 32-bit builds and 64-bit builds use the same definition. + * + * Applicable AIX fixes such that this is no longer needed: + * + * APAR IY23096 for AIX 51B, fix included in AIX 51C, and + * APAR IY24162 for 43X. + */ +#if defined(_AIX) +#if defined(__64BIT__) && defined(_AIXVERSION_510) +#ifdef _ALL_SOURCE + sig_mask.ss_set[3] &= 0x7FFFFFFF; +#else /* not _ALL_SOURCE */ + sig_mask.__ss_set[3] &= 0x7FFFFFFF; +#endif +#else /* not 64-bit build, or 64-bit build on 4.3 */ +#ifdef _ALL_SOURCE + sig_mask.hisigs &= 0x7FFFFFFF; +#else /* not _ALL_SOURCE */ + sig_mask.__hisigs &= 0x7FFFFFFF; +#endif +#endif +#endif /* _AIX */ + + while (1) { +#if APR_HAVE_SIGWAIT + int signal_received; + + if (apr_sigwait(&sig_mask, &signal_received) != 0) + { + /* handle sigwait() error here */ + } + + if (sig_func(signal_received) == 1) { + return APR_SUCCESS; + } +#elif HAVE_SIGSUSPEND + sigsuspend(&sig_mask); +#else +#error No apr_sigwait() and no sigsuspend() +#endif + } +} + +APR_DECLARE(apr_status_t) apr_setup_signal_thread(void) +{ + sigset_t sig_mask; + int rv; + + /* All threads should mask out signals to be handled by + * the thread doing sigwait(). + * + * No thread should ever block synchronous signals. + * See the Solaris man page for pthread_sigmask() for + * some information. Solaris chooses to knock out such + * processes when a blocked synchronous signal is + * delivered, skipping any registered signal handler. + * AIX doesn't call a signal handler either. At least + * one level of linux+glibc does call the handler even + * when the synchronous signal is blocked. + */ + sigfillset(&sig_mask); + remove_sync_sigs(&sig_mask); + +#if defined(SIGPROCMASK_SETS_THREAD_MASK) || ! APR_HAS_THREADS + if ((rv = sigprocmask(SIG_SETMASK, &sig_mask, NULL)) != 0) { + rv = errno; + } +#else + if ((rv = pthread_sigmask(SIG_SETMASK, &sig_mask, NULL)) != 0) { +#ifdef HAVE_ZOS_PTHREADS + rv = errno; +#endif + } +#endif + return rv; +} + +#endif /* APR_HAS_THREADS && ... */ + +APR_DECLARE(apr_status_t) apr_signal_block(int signum) +{ +#if APR_HAVE_SIGACTION + sigset_t sig_mask; + int rv; + + sigemptyset(&sig_mask); + + sigaddset(&sig_mask, signum); + +#if defined(SIGPROCMASK_SETS_THREAD_MASK) || ! APR_HAS_THREADS + if ((rv = sigprocmask(SIG_BLOCK, &sig_mask, NULL)) != 0) { + rv = errno; + } +#else + if ((rv = pthread_sigmask(SIG_BLOCK, &sig_mask, NULL)) != 0) { +#ifdef HAVE_ZOS_PTHREADS + rv = errno; +#endif + } +#endif + return rv; +#else + return APR_ENOTIMPL; +#endif +} + +APR_DECLARE(apr_status_t) apr_signal_unblock(int signum) +{ +#if APR_HAVE_SIGACTION + sigset_t sig_mask; + int rv; + + sigemptyset(&sig_mask); + + sigaddset(&sig_mask, signum); + +#if defined(SIGPROCMASK_SETS_THREAD_MASK) || ! APR_HAS_THREADS + if ((rv = sigprocmask(SIG_UNBLOCK, &sig_mask, NULL)) != 0) { + rv = errno; + } +#else + if ((rv = pthread_sigmask(SIG_UNBLOCK, &sig_mask, NULL)) != 0) { +#ifdef HAVE_ZOS_PTHREADS + rv = errno; +#endif + } +#endif + return rv; +#else + return APR_ENOTIMPL; +#endif +} diff --git a/threadproc/unix/thread.c b/threadproc/unix/thread.c new file mode 100644 index 0000000..6d060be --- /dev/null +++ b/threadproc/unix/thread.c @@ -0,0 +1,334 @@ +/* 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.h" +#include "apr_portable.h" +#include "apr_arch_threadproc.h" + +#if APR_HAS_THREADS + +#if APR_HAVE_PTHREAD_H + +/* Destroy the threadattr object */ +static apr_status_t threadattr_cleanup(void *data) +{ + apr_threadattr_t *attr = data; + apr_status_t rv; + + rv = pthread_attr_destroy(&attr->attr); +#ifdef HAVE_ZOS_PTHREADS + if (rv) { + rv = errno; + } +#endif + return rv; +} + +APR_DECLARE(apr_status_t) apr_threadattr_create(apr_threadattr_t **new, + apr_pool_t *pool) +{ + apr_status_t stat; + + (*new) = apr_palloc(pool, sizeof(apr_threadattr_t)); + (*new)->pool = pool; + stat = pthread_attr_init(&(*new)->attr); + + if (stat == 0) { + apr_pool_cleanup_register(pool, *new, threadattr_cleanup, + apr_pool_cleanup_null); + return APR_SUCCESS; + } +#ifdef HAVE_ZOS_PTHREADS + stat = errno; +#endif + + return stat; +} + +#if defined(PTHREAD_CREATE_DETACHED) +#define DETACH_ARG(v) ((v) ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE) +#else +#define DETACH_ARG(v) ((v) ? 1 : 0) +#endif + +APR_DECLARE(apr_status_t) apr_threadattr_detach_set(apr_threadattr_t *attr, + apr_int32_t on) +{ + apr_status_t stat; +#ifdef HAVE_ZOS_PTHREADS + int arg = DETACH_ARG(on); + + if ((stat = pthread_attr_setdetachstate(&attr->attr, &arg)) == 0) { +#else + if ((stat = pthread_attr_setdetachstate(&attr->attr, + DETACH_ARG(on))) == 0) { +#endif + return APR_SUCCESS; + } + else { +#ifdef HAVE_ZOS_PTHREADS + stat = errno; +#endif + + return stat; + } +} + +APR_DECLARE(apr_status_t) apr_threadattr_detach_get(apr_threadattr_t *attr) +{ + int state; + +#ifdef PTHREAD_ATTR_GETDETACHSTATE_TAKES_ONE_ARG + state = pthread_attr_getdetachstate(&attr->attr); +#else + pthread_attr_getdetachstate(&attr->attr, &state); +#endif + if (state == DETACH_ARG(1)) + return APR_DETACH; + return APR_NOTDETACH; +} + +APR_DECLARE(apr_status_t) apr_threadattr_stacksize_set(apr_threadattr_t *attr, + apr_size_t stacksize) +{ + int stat; + + stat = pthread_attr_setstacksize(&attr->attr, stacksize); + if (stat == 0) { + return APR_SUCCESS; + } +#ifdef HAVE_ZOS_PTHREADS + stat = errno; +#endif + + return stat; +} + +APR_DECLARE(apr_status_t) apr_threadattr_guardsize_set(apr_threadattr_t *attr, + apr_size_t size) +{ +#ifdef HAVE_PTHREAD_ATTR_SETGUARDSIZE + apr_status_t rv; + + rv = pthread_attr_setguardsize(&attr->attr, size); + if (rv == 0) { + return APR_SUCCESS; + } +#ifdef HAVE_ZOS_PTHREADS + rv = errno; +#endif + return rv; +#else + return APR_ENOTIMPL; +#endif +} + +static void *dummy_worker(void *opaque) +{ + apr_thread_t *thread = (apr_thread_t*)opaque; + return thread->func(thread, thread->data); +} + +APR_DECLARE(apr_status_t) apr_thread_create(apr_thread_t **new, + apr_threadattr_t *attr, + apr_thread_start_t func, + void *data, + apr_pool_t *pool) +{ + apr_status_t stat; + pthread_attr_t *temp; + + (*new) = (apr_thread_t *)apr_pcalloc(pool, sizeof(apr_thread_t)); + + if ((*new) == NULL) { + return APR_ENOMEM; + } + + (*new)->td = (pthread_t *)apr_pcalloc(pool, sizeof(pthread_t)); + + if ((*new)->td == NULL) { + return APR_ENOMEM; + } + + (*new)->data = data; + (*new)->func = func; + + if (attr) + temp = &attr->attr; + else + temp = NULL; + + stat = apr_pool_create(&(*new)->pool, pool); + if (stat != APR_SUCCESS) { + return stat; + } + + if ((stat = pthread_create((*new)->td, temp, dummy_worker, (*new))) == 0) { + return APR_SUCCESS; + } + else { +#ifdef HAVE_ZOS_PTHREADS + stat = errno; +#endif + + return stat; + } +} + +APR_DECLARE(apr_os_thread_t) apr_os_thread_current(void) +{ + return pthread_self(); +} + +APR_DECLARE(int) apr_os_thread_equal(apr_os_thread_t tid1, + apr_os_thread_t tid2) +{ + return pthread_equal(tid1, tid2); +} + +APR_DECLARE(apr_status_t) apr_thread_exit(apr_thread_t *thd, + apr_status_t retval) +{ + thd->exitval = retval; + apr_pool_destroy(thd->pool); + pthread_exit(NULL); + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_thread_join(apr_status_t *retval, + apr_thread_t *thd) +{ + apr_status_t stat; + apr_status_t *thread_stat; + + if ((stat = pthread_join(*thd->td,(void *)&thread_stat)) == 0) { + *retval = thd->exitval; + return APR_SUCCESS; + } + else { +#ifdef HAVE_ZOS_PTHREADS + stat = errno; +#endif + + return stat; + } +} + +APR_DECLARE(apr_status_t) apr_thread_detach(apr_thread_t *thd) +{ + apr_status_t stat; + +#ifdef HAVE_ZOS_PTHREADS + if ((stat = pthread_detach(thd->td)) == 0) { +#else + if ((stat = pthread_detach(*thd->td)) == 0) { +#endif + + return APR_SUCCESS; + } + else { +#ifdef HAVE_ZOS_PTHREADS + stat = errno; +#endif + + return stat; + } +} + +APR_DECLARE(void) apr_thread_yield(void) +{ +#ifdef HAVE_PTHREAD_YIELD +#ifdef HAVE_ZOS_PTHREADS + pthread_yield(NULL); +#else + pthread_yield(); +#endif /* HAVE_ZOS_PTHREADS */ +#else +#ifdef HAVE_SCHED_YIELD + sched_yield(); +#endif +#endif +} + +APR_DECLARE(apr_status_t) apr_thread_data_get(void **data, const char *key, + apr_thread_t *thread) +{ + return apr_pool_userdata_get(data, key, thread->pool); +} + +APR_DECLARE(apr_status_t) apr_thread_data_set(void *data, const char *key, + apr_status_t (*cleanup)(void *), + apr_thread_t *thread) +{ + return apr_pool_userdata_set(data, key, cleanup, thread->pool); +} + +APR_DECLARE(apr_status_t) apr_os_thread_get(apr_os_thread_t **thethd, + apr_thread_t *thd) +{ + *thethd = thd->td; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_os_thread_put(apr_thread_t **thd, + apr_os_thread_t *thethd, + apr_pool_t *pool) +{ + if (pool == NULL) { + return APR_ENOPOOL; + } + + if ((*thd) == NULL) { + (*thd) = (apr_thread_t *)apr_pcalloc(pool, sizeof(apr_thread_t)); + (*thd)->pool = pool; + } + + (*thd)->td = thethd; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_thread_once_init(apr_thread_once_t **control, + apr_pool_t *p) +{ + static const pthread_once_t once_init = PTHREAD_ONCE_INIT; + + *control = apr_palloc(p, sizeof(**control)); + (*control)->once = once_init; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_thread_once(apr_thread_once_t *control, + void (*func)(void)) +{ + return pthread_once(&control->once, func); +} + +APR_POOL_IMPLEMENT_ACCESSOR(thread) + +#endif /* HAVE_PTHREAD_H */ +#endif /* APR_HAS_THREADS */ + +#if !APR_HAS_THREADS + +/* avoid warning for no prototype */ +APR_DECLARE(apr_status_t) apr_os_thread_get(void); + +APR_DECLARE(apr_status_t) apr_os_thread_get(void) +{ + return APR_ENOTIMPL; +} + +#endif diff --git a/threadproc/unix/threadpriv.c b/threadproc/unix/threadpriv.c new file mode 100644 index 0000000..c278520 --- /dev/null +++ b/threadproc/unix/threadpriv.c @@ -0,0 +1,129 @@ +/* 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.h" +#include "apr_portable.h" +#include "apr_arch_threadproc.h" + +#if APR_HAS_THREADS + +#if APR_HAVE_PTHREAD_H +APR_DECLARE(apr_status_t) apr_threadkey_private_create(apr_threadkey_t **key, + void (*dest)(void *), + apr_pool_t *pool) +{ + (*key) = (apr_threadkey_t *)apr_pcalloc(pool, sizeof(apr_threadkey_t)); + + if ((*key) == NULL) { + return APR_ENOMEM; + } + + (*key)->pool = pool; + + return pthread_key_create(&(*key)->key, dest); + +} + +APR_DECLARE(apr_status_t) apr_threadkey_private_get(void **new, + apr_threadkey_t *key) +{ +#ifdef PTHREAD_GETSPECIFIC_TAKES_TWO_ARGS + if (pthread_getspecific(key->key,new)) + *new = NULL; +#else + (*new) = pthread_getspecific(key->key); +#endif + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_threadkey_private_set(void *priv, + apr_threadkey_t *key) +{ + apr_status_t stat; + + if ((stat = pthread_setspecific(key->key, priv)) == 0) { + return APR_SUCCESS; + } + else { + return stat; + } +} + +APR_DECLARE(apr_status_t) apr_threadkey_private_delete(apr_threadkey_t *key) +{ +#ifdef HAVE_PTHREAD_KEY_DELETE + apr_status_t stat; + + if ((stat = pthread_key_delete(key->key)) == 0) { + return APR_SUCCESS; + } + + return stat; +#else + return APR_ENOTIMPL; +#endif +} + +APR_DECLARE(apr_status_t) apr_threadkey_data_get(void **data, const char *key, + apr_threadkey_t *threadkey) +{ + return apr_pool_userdata_get(data, key, threadkey->pool); +} + +APR_DECLARE(apr_status_t) apr_threadkey_data_set(void *data, const char *key, + apr_status_t (*cleanup)(void *), + apr_threadkey_t *threadkey) +{ + return apr_pool_userdata_set(data, key, cleanup, threadkey->pool); +} + +APR_DECLARE(apr_status_t) apr_os_threadkey_get(apr_os_threadkey_t *thekey, + apr_threadkey_t *key) +{ + *thekey = key->key; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_os_threadkey_put(apr_threadkey_t **key, + apr_os_threadkey_t *thekey, + apr_pool_t *pool) +{ + if (pool == NULL) { + return APR_ENOPOOL; + } + + if ((*key) == NULL) { + (*key) = (apr_threadkey_t *)apr_pcalloc(pool, sizeof(apr_threadkey_t)); + (*key)->pool = pool; + } + + (*key)->key = *thekey; + return APR_SUCCESS; +} +#endif /* APR_HAVE_PTHREAD_H */ +#endif /* APR_HAS_THREADS */ + +#if !APR_HAS_THREADS + +/* avoid warning for no prototype */ +APR_DECLARE(apr_status_t) apr_os_threadkey_get(void); + +APR_DECLARE(apr_status_t) apr_os_threadkey_get(void) +{ + return APR_ENOTIMPL; +} + +#endif diff --git a/threadproc/win32/proc.c b/threadproc/win32/proc.c new file mode 100644 index 0000000..b4dfd5f --- /dev/null +++ b/threadproc/win32/proc.c @@ -0,0 +1,1158 @@ +/* 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_arch_threadproc.h" +#include "apr_arch_file_io.h" + +#include "apr_thread_proc.h" +#include "apr_file_io.h" +#include "apr_general.h" +#include "apr_strings.h" +#include "apr_portable.h" +#include "apr_lib.h" +#include +#if APR_HAVE_SIGNAL_H +#include +#endif +#include +#if APR_HAVE_PROCESS_H +#include +#endif + +/* Heavy on no'ops, here's what we want to pass if there is APR_NO_FILE + * requested for a specific child handle; + */ +static apr_file_t no_file = { NULL, INVALID_HANDLE_VALUE, }; + +/* We have very carefully excluded volumes of definitions from the + * Microsoft Platform SDK, which kill the build time performance. + * These the sole constants we borrow from WinBase.h and WinUser.h + */ +#ifndef LOGON32_LOGON_NETWORK +#define LOGON32_LOGON_NETWORK 3 +#endif + +#ifdef _WIN32_WCE +#ifndef DETACHED_PROCESS +#define DETACHED_PROCESS 0 +#endif +#ifndef CREATE_UNICODE_ENVIRONMENT +#define CREATE_UNICODE_ENVIRONMENT 0 +#endif +#ifndef STARTF_USESHOWWINDOW +#define STARTF_USESHOWWINDOW 0 +#endif +#ifndef SW_HIDE +#define SW_HIDE 0 +#endif +#endif + +/* + * some of the ideas expressed herein are based off of Microsoft + * Knowledge Base article: Q190351 + * + */ +APR_DECLARE(apr_status_t) apr_procattr_create(apr_procattr_t **new, + apr_pool_t *pool) +{ + (*new) = (apr_procattr_t *)apr_pcalloc(pool, sizeof(apr_procattr_t)); + (*new)->pool = pool; + (*new)->cmdtype = APR_PROGRAM; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_io_set(apr_procattr_t *attr, + apr_int32_t in, + apr_int32_t out, + apr_int32_t err) +{ + apr_status_t stat = APR_SUCCESS; + + if (in) { + /* APR_CHILD_BLOCK maps to APR_WRITE_BLOCK, while + * APR_PARENT_BLOCK maps to APR_READ_BLOCK, so transpose + * the CHILD/PARENT blocking flags for the stdin pipe. + * stdout/stderr map to the correct mode by default. + */ + if (in == APR_CHILD_BLOCK) + in = APR_READ_BLOCK; + else if (in == APR_PARENT_BLOCK) + in = APR_WRITE_BLOCK; + + if (in == APR_NO_FILE) + attr->child_in = &no_file; + else { + stat = apr_file_pipe_create_ex(&attr->child_in, &attr->parent_in, + in, attr->pool); + } + if (stat == APR_SUCCESS) + stat = apr_file_inherit_unset(attr->parent_in); + } + if (out && stat == APR_SUCCESS) { + if (out == APR_NO_FILE) + attr->child_out = &no_file; + else { + stat = apr_file_pipe_create_ex(&attr->parent_out, &attr->child_out, + out, attr->pool); + } + if (stat == APR_SUCCESS) + stat = apr_file_inherit_unset(attr->parent_out); + } + if (err && stat == APR_SUCCESS) { + if (err == APR_NO_FILE) + attr->child_err = &no_file; + else { + stat = apr_file_pipe_create_ex(&attr->parent_err, &attr->child_err, + err, attr->pool); + } + if (stat == APR_SUCCESS) + stat = apr_file_inherit_unset(attr->parent_err); + } + return stat; +} + +APR_DECLARE(apr_status_t) apr_procattr_child_in_set(apr_procattr_t *attr, + apr_file_t *child_in, + apr_file_t *parent_in) +{ + apr_status_t rv = APR_SUCCESS; + + if (child_in) { + if ((attr->child_in == NULL) || (attr->child_in == &no_file)) + rv = apr_file_dup(&attr->child_in, child_in, attr->pool); + else + rv = apr_file_dup2(attr->child_in, child_in, attr->pool); + + if (rv == APR_SUCCESS) + rv = apr_file_inherit_set(attr->child_in); + } + + if (parent_in && rv == APR_SUCCESS) { + if (attr->parent_in == NULL) + rv = apr_file_dup(&attr->parent_in, parent_in, attr->pool); + else + rv = apr_file_dup2(attr->parent_in, parent_in, attr->pool); + } + + return rv; +} + +APR_DECLARE(apr_status_t) apr_procattr_child_out_set(apr_procattr_t *attr, + apr_file_t *child_out, + apr_file_t *parent_out) +{ + apr_status_t rv = APR_SUCCESS; + + if (child_out) { + if ((attr->child_out == NULL) || (attr->child_out == &no_file)) + rv = apr_file_dup(&attr->child_out, child_out, attr->pool); + else + rv = apr_file_dup2(attr->child_out, child_out, attr->pool); + + if (rv == APR_SUCCESS) + rv = apr_file_inherit_set(attr->child_out); + } + + if (parent_out && rv == APR_SUCCESS) { + if (attr->parent_out == NULL) + rv = apr_file_dup(&attr->parent_out, parent_out, attr->pool); + else + rv = apr_file_dup2(attr->parent_out, parent_out, attr->pool); + } + + return rv; +} + +APR_DECLARE(apr_status_t) apr_procattr_child_err_set(apr_procattr_t *attr, + apr_file_t *child_err, + apr_file_t *parent_err) +{ + apr_status_t rv = APR_SUCCESS; + + if (child_err) { + if ((attr->child_err == NULL) || (attr->child_err == &no_file)) + rv = apr_file_dup(&attr->child_err, child_err, attr->pool); + else + rv = apr_file_dup2(attr->child_err, child_err, attr->pool); + + if (rv == APR_SUCCESS) + rv = apr_file_inherit_set(attr->child_err); + } + + if (parent_err && rv == APR_SUCCESS) { + if (attr->parent_err == NULL) + rv = apr_file_dup(&attr->parent_err, parent_err, attr->pool); + else + rv = apr_file_dup2(attr->parent_err, parent_err, attr->pool); + } + + return rv; +} + +APR_DECLARE(apr_status_t) apr_procattr_dir_set(apr_procattr_t *attr, + const char *dir) +{ + /* curr dir must be in native format, there are all sorts of bugs in + * the NT library loading code that flunk the '/' parsing test. + */ + return apr_filepath_merge(&attr->currdir, NULL, dir, + APR_FILEPATH_NATIVE, attr->pool); +} + +APR_DECLARE(apr_status_t) apr_procattr_cmdtype_set(apr_procattr_t *attr, + apr_cmdtype_e cmd) +{ + attr->cmdtype = cmd; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_detach_set(apr_procattr_t *attr, + apr_int32_t det) +{ + attr->detached = det; + return APR_SUCCESS; +} + +#ifndef _WIN32_WCE +static apr_status_t attr_cleanup(void *theattr) +{ + apr_procattr_t *attr = (apr_procattr_t *)theattr; + if (attr->user_token) + CloseHandle(attr->user_token); + attr->user_token = NULL; + return APR_SUCCESS; +} +#endif + +APR_DECLARE(apr_status_t) apr_procattr_user_set(apr_procattr_t *attr, + const char *username, + const char *password) +{ +#ifdef _WIN32_WCE + return APR_ENOTIMPL; +#else + HANDLE user; + apr_wchar_t *wusername = NULL; + apr_wchar_t *wpassword = NULL; + apr_status_t rv; + apr_size_t len, wlen; + + if (apr_os_level >= APR_WIN_NT_4) + { + if (attr->user_token) { + /* Cannot set that twice */ + if (attr->errfn) { + attr->errfn(attr->pool, 0, + apr_pstrcat(attr->pool, + "function called twice" + " on username: ", username, NULL)); + } + return APR_EINVAL; + } + len = strlen(username) + 1; + wlen = len; + wusername = apr_palloc(attr->pool, wlen * sizeof(apr_wchar_t)); + if ((rv = apr_conv_utf8_to_ucs2(username, &len, wusername, &wlen)) + != APR_SUCCESS) { + if (attr->errfn) { + attr->errfn(attr->pool, rv, + apr_pstrcat(attr->pool, + "utf8 to ucs2 conversion failed" + " on username: ", username, NULL)); + } + return rv; + } + if (password) { + len = strlen(password) + 1; + wlen = len; + wpassword = apr_palloc(attr->pool, wlen * sizeof(apr_wchar_t)); + if ((rv = apr_conv_utf8_to_ucs2(password, &len, wpassword, &wlen)) + != APR_SUCCESS) { + if (attr->errfn) { + attr->errfn(attr->pool, rv, + apr_pstrcat(attr->pool, + "utf8 to ucs2 conversion failed" + " on password: ", password, NULL)); + } + return rv; + } + } + if (!LogonUserW(wusername, + NULL, + wpassword ? wpassword : L"", + LOGON32_LOGON_NETWORK, + LOGON32_PROVIDER_DEFAULT, + &user)) { + /* Logon Failed */ + return apr_get_os_error(); + } + if (wpassword) + memset(wpassword, 0, wlen * sizeof(apr_wchar_t)); + /* Get the primary token for user */ + if (!DuplicateTokenEx(user, + TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, + NULL, + SecurityImpersonation, + TokenPrimary, + &(attr->user_token))) { + /* Failed to duplicate the user token */ + rv = apr_get_os_error(); + CloseHandle(user); + return rv; + } + CloseHandle(user); + + attr->sd = apr_pcalloc(attr->pool, SECURITY_DESCRIPTOR_MIN_LENGTH); + InitializeSecurityDescriptor(attr->sd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(attr->sd, -1, 0, 0); + attr->sa = apr_palloc(attr->pool, sizeof(SECURITY_ATTRIBUTES)); + attr->sa->nLength = sizeof (SECURITY_ATTRIBUTES); + attr->sa->lpSecurityDescriptor = attr->sd; + attr->sa->bInheritHandle = FALSE; + + /* register the cleanup */ + apr_pool_cleanup_register(attr->pool, (void *)attr, + attr_cleanup, + apr_pool_cleanup_null); + return APR_SUCCESS; + } + else + return APR_ENOTIMPL; +#endif +} + +APR_DECLARE(apr_status_t) apr_procattr_group_set(apr_procattr_t *attr, + const char *groupname) +{ + /* Always return SUCCESS cause groups are irrelevant */ + return APR_SUCCESS; +} + +static const char* has_space(const char *str) +{ + const char *ch; + for (ch = str; *ch; ++ch) { + if (apr_isspace(*ch)) { + return ch; + } + } + return NULL; +} + +static char *apr_caret_escape_args(apr_pool_t *p, const char *str) +{ + char *cmd; + unsigned char *d; + const unsigned char *s; + + cmd = apr_palloc(p, 2 * strlen(str) + 1); /* Be safe */ + d = (unsigned char *)cmd; + s = (const unsigned char *)str; + for (; *s; ++s) { + + /* + * Newlines to Win32/OS2 CreateProcess() are ill advised. + * Convert them to spaces since they are effectively white + * space to most applications + */ + if (*s == '\r' || *s == '\n') { + *d++ = ' '; + continue; + } + + if (IS_SHCHAR(*s)) { + *d++ = '^'; + } + *d++ = *s; + } + *d = '\0'; + + return cmd; +} + +APR_DECLARE(apr_status_t) apr_procattr_child_errfn_set(apr_procattr_t *attr, + apr_child_errfn_t *errfn) +{ + attr->errfn = errfn; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_error_check_set(apr_procattr_t *attr, + apr_int32_t chk) +{ + attr->errchk = chk; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_procattr_addrspace_set(apr_procattr_t *attr, + apr_int32_t addrspace) +{ + /* won't ever be used on this platform, so don't save the flag */ + return APR_SUCCESS; +} + +#if APR_HAS_UNICODE_FS && !defined(_WIN32_WCE) + +/* Used only for the NT code path, a critical section is the fastest + * implementation available. + */ +static CRITICAL_SECTION proc_lock; + +static apr_status_t threadproc_global_cleanup(void *ignored) +{ + DeleteCriticalSection(&proc_lock); + return APR_SUCCESS; +} + +/* Called from apr_initialize, we need a critical section to handle + * the pipe inheritance on win32. This will mutex any process create + * so as we change our inherited pipes, we prevent another process from + * also inheriting those alternate handles, and prevent the other process + * from failing to inherit our standard handles. + */ +apr_status_t apr_threadproc_init(apr_pool_t *pool) +{ + IF_WIN_OS_IS_UNICODE + { + InitializeCriticalSection(&proc_lock); + /* register the cleanup */ + apr_pool_cleanup_register(pool, &proc_lock, + threadproc_global_cleanup, + apr_pool_cleanup_null); + } + return APR_SUCCESS; +} + +#else /* !APR_HAS_UNICODE_FS || defined(_WIN32_WCE) */ + +apr_status_t apr_threadproc_init(apr_pool_t *pool) +{ + return APR_SUCCESS; +} + +#endif + +APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *new, + const char *progname, + const char * const *args, + const char * const *env, + apr_procattr_t *attr, + apr_pool_t *pool) +{ + apr_status_t rv; + apr_size_t i; + const char *argv0; + char *cmdline; + char *pEnvBlock; + PROCESS_INFORMATION pi; + DWORD dwCreationFlags = 0; + + new->in = attr->parent_in; + new->out = attr->parent_out; + new->err = attr->parent_err; + + if (attr->detached) { + /* If we are creating ourselves detached, then we should hide the + * window we are starting in. And we had better redefine our + * handles for STDIN, STDOUT, and STDERR. Do not set the + * detached attribute for Win9x. We have found that Win9x does + * not manage the stdio handles properly when running old 16 + * bit executables if the detached attribute is set. + */ + if (apr_os_level >= APR_WIN_NT) { + /* + * XXX DETACHED_PROCESS won't on Win9x at all; on NT/W2K + * 16 bit executables fail (MS KB: Q150956) + */ + dwCreationFlags |= DETACHED_PROCESS; + } + } + + /* progname must be unquoted, in native format, as there are all sorts + * of bugs in the NT library loader code that fault when parsing '/'. + * XXX progname must be NULL if this is a 16 bit app running in WOW + */ + if (progname[0] == '\"') { + progname = apr_pstrmemdup(pool, progname + 1, strlen(progname) - 2); + } + + if (attr->cmdtype == APR_PROGRAM || attr->cmdtype == APR_PROGRAM_ENV) { + char *fullpath = NULL; + if ((rv = apr_filepath_merge(&fullpath, attr->currdir, progname, + APR_FILEPATH_NATIVE, pool)) != APR_SUCCESS) { + if (attr->errfn) { + attr->errfn(pool, rv, + apr_pstrcat(pool, "filepath_merge failed.", + " currdir: ", attr->currdir, + " progname: ", progname, NULL)); + } + return rv; + } + progname = fullpath; + } + else { + /* Do not fail if the path isn't parseable for APR_PROGRAM_PATH + * or APR_SHELLCMD. We only invoke apr_filepath_merge (with no + * left hand side expression) in order to correct the path slash + * delimiters. But the filename doesn't need to be in the CWD, + * nor does it need to be a filename at all (it could be a + * built-in shell command.) + */ + char *fullpath = NULL; + if ((rv = apr_filepath_merge(&fullpath, "", progname, + APR_FILEPATH_NATIVE, pool)) == APR_SUCCESS) { + progname = fullpath; + } + } + + if (has_space(progname)) { + argv0 = apr_pstrcat(pool, "\"", progname, "\"", NULL); + } + else { + argv0 = progname; + } + + /* Handle the args, seperate from argv0 */ + cmdline = ""; + for (i = 1; args && args[i]; ++i) { + if (has_space(args[i]) || !args[i][0]) { + cmdline = apr_pstrcat(pool, cmdline, " \"", args[i], "\"", NULL); + } + else { + cmdline = apr_pstrcat(pool, cmdline, " ", args[i], NULL); + } + } + +#ifndef _WIN32_WCE + if (attr->cmdtype == APR_SHELLCMD || attr->cmdtype == APR_SHELLCMD_ENV) { + char *shellcmd = getenv("COMSPEC"); + if (!shellcmd) { + if (attr->errfn) { + attr->errfn(pool, APR_EINVAL, "COMSPEC envar is not set"); + } + return APR_EINVAL; + } + if (shellcmd[0] == '"') { + progname = apr_pstrmemdup(pool, shellcmd + 1, strlen(shellcmd) - 2); + } + else { + progname = shellcmd; + if (has_space(shellcmd)) { + shellcmd = apr_pstrcat(pool, "\"", shellcmd, "\"", NULL); + } + } + /* Command.com does not support a quoted command, while cmd.exe demands one. + */ + i = strlen(progname); + if (i >= 11 && strcasecmp(progname + i - 11, "command.com") == 0) { + cmdline = apr_pstrcat(pool, shellcmd, " /C ", argv0, cmdline, NULL); + } + else { + cmdline = apr_pstrcat(pool, shellcmd, " /C \"", argv0, cmdline, "\"", NULL); + } + } + else +#endif + { +#if defined(_WIN32_WCE) + { +#else + /* Win32 is _different_ than unix. While unix will find the given + * program since it's already chdir'ed, Win32 cannot since the parent + * attempts to open the program with it's own path. + * ###: This solution isn't much better - it may defeat path searching + * when the path search was desired. Open to further discussion. + */ + i = strlen(progname); + if (i >= 4 && (strcasecmp(progname + i - 4, ".bat") == 0 + || strcasecmp(progname + i - 4, ".cmd") == 0)) + { + char *shellcmd = getenv("COMSPEC"); + if (!shellcmd) { + if (attr->errfn) { + attr->errfn(pool, APR_EINVAL, "COMSPEC envar is not set"); + } + return APR_EINVAL; + } + if (shellcmd[0] == '"') { + progname = apr_pstrmemdup(pool, shellcmd + 1, strlen(shellcmd) - 2); + } + else { + progname = shellcmd; + if (has_space(shellcmd)) { + shellcmd = apr_pstrcat(pool, "\"", shellcmd, "\"", NULL); + } + } + i = strlen(progname); + if (i >= 11 && strcasecmp(progname + i - 11, "command.com") == 0) { + /* XXX: Still insecure - need doubled-quotes on each individual + * arg of cmdline. Suspect we need to postpone cmdline parsing + * until this moment in all four code paths, with some flags + * to toggle 'which flavor' is needed. + */ + cmdline = apr_pstrcat(pool, shellcmd, " /C ", argv0, cmdline, NULL); + } + else { + /* We must protect the cmdline args from any interpolation - this + * is not a shellcmd, and the source of argv[] is untrusted. + * Notice we escape ALL the cmdline args, including the quotes + * around the individual args themselves. No sense in allowing + * the shift-state to be toggled, and the application will + * not see the caret escapes. + */ + cmdline = apr_caret_escape_args(pool, cmdline); + /* + * Our app name must always be quoted so the quotes surrounding + * the entire /c "command args" are unambigious. + */ + if (*argv0 != '"') { + cmdline = apr_pstrcat(pool, shellcmd, " /C \"\"", argv0, "\"", cmdline, "\"", NULL); + } + else { + cmdline = apr_pstrcat(pool, shellcmd, " /C \"", argv0, cmdline, "\"", NULL); + } + } + } + else { +#endif + /* A simple command we are directly invoking. Do not pass + * the first arg to CreateProc() for APR_PROGRAM_PATH + * invocation, since it would need to be a literal and + * complete file path. That is; "c:\bin\aprtest.exe" + * would succeed, but "c:\bin\aprtest" or "aprtest.exe" + * can fail. + */ + cmdline = apr_pstrcat(pool, argv0, cmdline, NULL); + + if (attr->cmdtype == APR_PROGRAM_PATH) { + progname = NULL; + } + } + } + + if (!env || attr->cmdtype == APR_PROGRAM_ENV || + attr->cmdtype == APR_SHELLCMD_ENV) { + pEnvBlock = NULL; + } + else { + apr_size_t iEnvBlockLen; + /* + * Win32's CreateProcess call requires that the environment + * be passed in an environment block, a null terminated block of + * null terminated strings. + */ + i = 0; + iEnvBlockLen = 1; + while (env[i]) { + iEnvBlockLen += strlen(env[i]) + 1; + i++; + } + if (!i) + ++iEnvBlockLen; + +#if APR_HAS_UNICODE_FS + IF_WIN_OS_IS_UNICODE + { + apr_wchar_t *pNext; + pEnvBlock = (char *)apr_palloc(pool, iEnvBlockLen * 2); + dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT; + + i = 0; + pNext = (apr_wchar_t*)pEnvBlock; + while (env[i]) { + apr_size_t in = strlen(env[i]) + 1; + if ((rv = apr_conv_utf8_to_ucs2(env[i], &in, + pNext, &iEnvBlockLen)) + != APR_SUCCESS) { + if (attr->errfn) { + attr->errfn(pool, rv, + apr_pstrcat(pool, + "utf8 to ucs2 conversion failed" + " on this string: ", env[i], NULL)); + } + return rv; + } + pNext = wcschr(pNext, L'\0') + 1; + i++; + } + if (!i) + *(pNext++) = L'\0'; + *pNext = L'\0'; + } +#endif /* APR_HAS_UNICODE_FS */ +#if APR_HAS_ANSI_FS + ELSE_WIN_OS_IS_ANSI + { + char *pNext; + pEnvBlock = (char *)apr_palloc(pool, iEnvBlockLen); + + i = 0; + pNext = pEnvBlock; + while (env[i]) { + strcpy(pNext, env[i]); + pNext = strchr(pNext, '\0') + 1; + i++; + } + if (!i) + *(pNext++) = '\0'; + *pNext = '\0'; + } +#endif /* APR_HAS_ANSI_FS */ + } + + new->invoked = cmdline; + +#if APR_HAS_UNICODE_FS + IF_WIN_OS_IS_UNICODE + { + STARTUPINFOW si; + DWORD stdin_reset = 0; + DWORD stdout_reset = 0; + DWORD stderr_reset = 0; + apr_wchar_t *wprg = NULL; + apr_wchar_t *wcmd = NULL; + apr_wchar_t *wcwd = NULL; + + if (progname) { + apr_size_t nprg = strlen(progname) + 1; + apr_size_t nwprg = nprg + 6; + wprg = apr_palloc(pool, nwprg * sizeof(wprg[0])); + if ((rv = apr_conv_utf8_to_ucs2(progname, &nprg, wprg, &nwprg)) + != APR_SUCCESS) { + if (attr->errfn) { + attr->errfn(pool, rv, + apr_pstrcat(pool, + "utf8 to ucs2 conversion failed" + " on progname: ", progname, NULL)); + } + return rv; + } + } + + if (cmdline) { + apr_size_t ncmd = strlen(cmdline) + 1; + apr_size_t nwcmd = ncmd; + wcmd = apr_palloc(pool, nwcmd * sizeof(wcmd[0])); + if ((rv = apr_conv_utf8_to_ucs2(cmdline, &ncmd, wcmd, &nwcmd)) + != APR_SUCCESS) { + if (attr->errfn) { + attr->errfn(pool, rv, + apr_pstrcat(pool, + "utf8 to ucs2 conversion failed" + " on cmdline: ", cmdline, NULL)); + } + return rv; + } + } + + if (attr->currdir) + { + apr_size_t ncwd = strlen(attr->currdir) + 1; + apr_size_t nwcwd = ncwd; + wcwd = apr_palloc(pool, ncwd * sizeof(wcwd[0])); + if ((rv = apr_conv_utf8_to_ucs2(attr->currdir, &ncwd, + wcwd, &nwcwd)) + != APR_SUCCESS) { + if (attr->errfn) { + attr->errfn(pool, rv, + apr_pstrcat(pool, + "utf8 to ucs2 conversion failed" + " on currdir: ", attr->currdir, NULL)); + } + return rv; + } + } + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + + if (attr->detached) { + si.dwFlags |= STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + } + +#ifndef _WIN32_WCE + /* LOCK CRITICAL SECTION + * before we begin to manipulate the inherited handles + */ + EnterCriticalSection(&proc_lock); + + if ((attr->child_in && attr->child_in->filehand) + || (attr->child_out && attr->child_out->filehand) + || (attr->child_err && attr->child_err->filehand)) + { + si.dwFlags |= STARTF_USESTDHANDLES; + + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + if (attr->child_in && attr->child_in->filehand) + { + if (GetHandleInformation(si.hStdInput, + &stdin_reset) + && (stdin_reset &= HANDLE_FLAG_INHERIT)) + SetHandleInformation(si.hStdInput, + HANDLE_FLAG_INHERIT, 0); + + if ( (si.hStdInput = attr->child_in->filehand) + != INVALID_HANDLE_VALUE ) + SetHandleInformation(si.hStdInput, HANDLE_FLAG_INHERIT, + HANDLE_FLAG_INHERIT); + } + + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + if (attr->child_out && attr->child_out->filehand) + { + if (GetHandleInformation(si.hStdOutput, + &stdout_reset) + && (stdout_reset &= HANDLE_FLAG_INHERIT)) + SetHandleInformation(si.hStdOutput, + HANDLE_FLAG_INHERIT, 0); + + if ( (si.hStdOutput = attr->child_out->filehand) + != INVALID_HANDLE_VALUE ) + SetHandleInformation(si.hStdOutput, HANDLE_FLAG_INHERIT, + HANDLE_FLAG_INHERIT); + } + + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + if (attr->child_err && attr->child_err->filehand) + { + if (GetHandleInformation(si.hStdError, + &stderr_reset) + && (stderr_reset &= HANDLE_FLAG_INHERIT)) + SetHandleInformation(si.hStdError, + HANDLE_FLAG_INHERIT, 0); + + if ( (si.hStdError = attr->child_err->filehand) + != INVALID_HANDLE_VALUE ) + SetHandleInformation(si.hStdError, HANDLE_FLAG_INHERIT, + HANDLE_FLAG_INHERIT); + } + } + if (attr->user_token) { + /* XXX: for terminal services, handles can't be cannot be + * inherited across sessions. This process must be created + * in our existing session. lpDesktop assignment appears + * to be wrong according to these rules. + */ + si.lpDesktop = L"Winsta0\\Default"; + if (!ImpersonateLoggedOnUser(attr->user_token)) { + /* failed to impersonate the logged user */ + rv = apr_get_os_error(); + CloseHandle(attr->user_token); + attr->user_token = NULL; + LeaveCriticalSection(&proc_lock); + return rv; + } + rv = CreateProcessAsUserW(attr->user_token, + wprg, wcmd, + attr->sa, + NULL, + TRUE, + dwCreationFlags, + pEnvBlock, + wcwd, + &si, &pi); + + RevertToSelf(); + } + else { + rv = CreateProcessW(wprg, wcmd, /* Executable & Command line */ + NULL, NULL, /* Proc & thread security attributes */ + TRUE, /* Inherit handles */ + dwCreationFlags, /* Creation flags */ + pEnvBlock, /* Environment block */ + wcwd, /* Current directory name */ + &si, &pi); + } + + if ((attr->child_in && attr->child_in->filehand) + || (attr->child_out && attr->child_out->filehand) + || (attr->child_err && attr->child_err->filehand)) + { + if (stdin_reset) + SetHandleInformation(GetStdHandle(STD_INPUT_HANDLE), + stdin_reset, stdin_reset); + + if (stdout_reset) + SetHandleInformation(GetStdHandle(STD_OUTPUT_HANDLE), + stdout_reset, stdout_reset); + + if (stderr_reset) + SetHandleInformation(GetStdHandle(STD_ERROR_HANDLE), + stderr_reset, stderr_reset); + } + /* RELEASE CRITICAL SECTION + * The state of the inherited handles has been restored. + */ + LeaveCriticalSection(&proc_lock); + +#else /* defined(_WIN32_WCE) */ + rv = CreateProcessW(wprg, wcmd, /* Executable & Command line */ + NULL, NULL, /* Proc & thread security attributes */ + FALSE, /* must be 0 */ + dwCreationFlags, /* Creation flags */ + NULL, /* Environment block must be NULL */ + NULL, /* Current directory name must be NULL*/ + NULL, /* STARTUPINFO not supported */ + &pi); +#endif + } +#endif /* APR_HAS_UNICODE_FS */ +#if APR_HAS_ANSI_FS + ELSE_WIN_OS_IS_ANSI + { + STARTUPINFOA si; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + + if (attr->detached) { + si.dwFlags |= STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + } + + if ((attr->child_in && attr->child_in->filehand) + || (attr->child_out && attr->child_out->filehand) + || (attr->child_err && attr->child_err->filehand)) + { + si.dwFlags |= STARTF_USESTDHANDLES; + + si.hStdInput = (attr->child_in) + ? attr->child_in->filehand + : GetStdHandle(STD_INPUT_HANDLE); + + si.hStdOutput = (attr->child_out) + ? attr->child_out->filehand + : GetStdHandle(STD_OUTPUT_HANDLE); + + si.hStdError = (attr->child_err) + ? attr->child_err->filehand + : GetStdHandle(STD_ERROR_HANDLE); + } + + rv = CreateProcessA(progname, cmdline, /* Command line */ + NULL, NULL, /* Proc & thread security attributes */ + TRUE, /* Inherit handles */ + dwCreationFlags, /* Creation flags */ + pEnvBlock, /* Environment block */ + attr->currdir, /* Current directory name */ + &si, &pi); + } +#endif /* APR_HAS_ANSI_FS */ + + /* Check CreateProcess result + */ + if (!rv) + return apr_get_os_error(); + + /* XXX Orphaned handle warning - no fix due to broken apr_proc_t api. + */ + new->hproc = pi.hProcess; + new->pid = pi.dwProcessId; + + if ((attr->child_in) && (attr->child_in != &no_file)) { + apr_file_close(attr->child_in); + } + if ((attr->child_out) && (attr->child_out != &no_file)) { + apr_file_close(attr->child_out); + } + if ((attr->child_err) && (attr->child_err != &no_file)) { + apr_file_close(attr->child_err); + } + CloseHandle(pi.hThread); + + return APR_SUCCESS; +} + +static apr_exit_why_e why_from_exit_code(DWORD exit) { + /* See WinNT.h STATUS_ACCESS_VIOLATION and family for how + * this class of failures was determined + */ + if (((exit & 0xC0000000) == 0xC0000000) + && !(exit & 0x3FFF0000)) + return APR_PROC_SIGNAL; + else + return APR_PROC_EXIT; + + /* ### No way to tell if Dr Watson grabbed a core, AFAICT. */ +} + +APR_DECLARE(apr_status_t) apr_proc_wait_all_procs(apr_proc_t *proc, + int *exitcode, + apr_exit_why_e *exitwhy, + apr_wait_how_e waithow, + apr_pool_t *p) +{ +#if APR_HAS_UNICODE_FS +#ifndef _WIN32_WCE + IF_WIN_OS_IS_UNICODE + { + DWORD dwId = GetCurrentProcessId(); + DWORD i; + DWORD nChilds = 0; + DWORD nActive = 0; + HANDLE ps32; + PROCESSENTRY32W pe32; + BOOL bHasMore = FALSE; + DWORD dwFlags = PROCESS_QUERY_INFORMATION; + apr_status_t rv = APR_EGENERAL; + + if (waithow == APR_WAIT) + dwFlags |= SYNCHRONIZE; + if (!(ps32 = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0))) { + return apr_get_os_error(); + } + pe32.dwSize = sizeof(PROCESSENTRY32W); + if (!Process32FirstW(ps32, &pe32)) { + if (GetLastError() == ERROR_NO_MORE_FILES) + return APR_EOF; + else + return apr_get_os_error(); + } + do { + DWORD dwRetval = 0; + DWORD nHandles = 0; + HANDLE hProcess = NULL; + HANDLE pHandles[MAXIMUM_WAIT_OBJECTS]; + do { + if (pe32.th32ParentProcessID == dwId) { + nChilds++; + if ((hProcess = OpenProcess(dwFlags, FALSE, + pe32.th32ProcessID)) != NULL) { + if (GetExitCodeProcess(hProcess, &dwRetval)) { + if (dwRetval == STILL_ACTIVE) { + nActive++; + if (waithow == APR_WAIT) + pHandles[nHandles++] = hProcess; + else + CloseHandle(hProcess); + } + else { + /* Process has exited. + * No need to wait for its termination. + */ + CloseHandle(hProcess); + if (exitcode) + *exitcode = dwRetval; + if (exitwhy) + *exitwhy = why_from_exit_code(dwRetval); + proc->pid = pe32.th32ProcessID; + } + } + else { + /* Unexpected error code. + * Cleanup and return; + */ + rv = apr_get_os_error(); + CloseHandle(hProcess); + for (i = 0; i < nHandles; i++) + CloseHandle(pHandles[i]); + return rv; + } + } + else { + /* This is our child, so it shouldn't happen + * that we cannot open our child's process handle. + * However if the child process increased the + * security token it might fail. + */ + } + } + } while ((bHasMore = Process32NextW(ps32, &pe32)) && + nHandles < MAXIMUM_WAIT_OBJECTS); + if (nHandles) { + /* Wait for all collected processes to finish */ + DWORD waitStatus = WaitForMultipleObjects(nHandles, pHandles, + TRUE, INFINITE); + for (i = 0; i < nHandles; i++) + CloseHandle(pHandles[i]); + if (waitStatus == WAIT_OBJECT_0) { + /* Decrease active count by the number of awaited + * processes. + */ + nActive -= nHandles; + } + else { + /* Broken from the infinite loop */ + break; + } + } + } while (bHasMore); + CloseHandle(ps32); + if (waithow != APR_WAIT) { + if (nChilds && nChilds == nActive) { + /* All child processes are running */ + rv = APR_CHILD_NOTDONE; + proc->pid = -1; + } + else { + /* proc->pid contains the pid of the + * exited processes + */ + rv = APR_CHILD_DONE; + } + } + if (nActive == 0) { + rv = APR_CHILD_DONE; + proc->pid = -1; + } + return rv; + } +#endif /* _WIN32_WCE */ +#endif /* APR_HAS_UNICODE_FS */ + return APR_ENOTIMPL; +} + +APR_DECLARE(apr_status_t) apr_proc_wait(apr_proc_t *proc, + int *exitcode, apr_exit_why_e *exitwhy, + apr_wait_how_e waithow) +{ + DWORD stat; + DWORD time; + + if (waithow == APR_WAIT) + time = INFINITE; + else + time = 0; + + if ((stat = WaitForSingleObject(proc->hproc, time)) == WAIT_OBJECT_0) { + if (GetExitCodeProcess(proc->hproc, &stat)) { + if (exitcode) + *exitcode = stat; + if (exitwhy) + *exitwhy = why_from_exit_code(stat); + CloseHandle(proc->hproc); + proc->hproc = NULL; + return APR_CHILD_DONE; + } + } + else if (stat == WAIT_TIMEOUT) { + return APR_CHILD_NOTDONE; + } + return apr_get_os_error(); +} + +APR_DECLARE(apr_status_t) apr_proc_detach(int daemonize) +{ + return APR_ENOTIMPL; +} + +APR_DECLARE(apr_status_t) apr_procattr_perms_set_register(apr_procattr_t *attr, + apr_perms_setfn_t *perms_set_fn, + void *data, + apr_fileperms_t perms) +{ + return APR_ENOTIMPL; +} diff --git a/threadproc/win32/signals.c b/threadproc/win32/signals.c new file mode 100644 index 0000000..48676d8 --- /dev/null +++ b/threadproc/win32/signals.c @@ -0,0 +1,67 @@ +/* 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_arch_threadproc.h" +#include "apr_arch_file_io.h" +#include "apr_thread_proc.h" +#include "apr_signal.h" +#include "apr_file_io.h" +#include "apr_general.h" +#if APR_HAVE_SIGNAL_H +#include +#endif +#include +#if APR_HAVE_SYS_WAIT +#include +#endif + +/* Windows only really support killing process, but that will do for now. + * + * ### Actually, closing the input handle to the proc should also do fine + * for most console apps. This definitely needs improvement... + */ +APR_DECLARE(apr_status_t) apr_proc_kill(apr_proc_t *proc, int signal) +{ + if (proc->hproc != NULL) { + if (TerminateProcess(proc->hproc, signal) == 0) { + return apr_get_os_error(); + } + /* On unix, SIGKILL leaves a apr_proc_wait()able pid lying around, + * so we will leave hproc alone until the app calls apr_proc_wait(). + */ + return APR_SUCCESS; + } + return APR_EPROC_UNKNOWN; +} + +void apr_signal_init(apr_pool_t *pglobal) +{ +} + +APR_DECLARE(const char *) apr_signal_description_get(int signum) +{ + return "unknown signal (not supported)"; +} + +APR_DECLARE(apr_status_t) apr_signal_block(int signum) +{ + return APR_ENOTIMPL; +} + +APR_DECLARE(apr_status_t) apr_signal_unblock(int signum) +{ + return APR_ENOTIMPL; +} diff --git a/threadproc/win32/thread.c b/threadproc/win32/thread.c new file mode 100644 index 0000000..2503457 --- /dev/null +++ b/threadproc/win32/thread.c @@ -0,0 +1,281 @@ +/* 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_private.h" +#include "apr_arch_threadproc.h" +#include "apr_thread_proc.h" +#include "apr_general.h" +#include "apr_lib.h" +#include "apr_portable.h" +#if APR_HAVE_PROCESS_H +#include +#endif +#include "apr_arch_misc.h" + +/* Chosen for us by apr_initialize */ +DWORD tls_apr_thread = 0; + +APR_DECLARE(apr_status_t) apr_threadattr_create(apr_threadattr_t **new, + apr_pool_t *pool) +{ + (*new) = (apr_threadattr_t *)apr_palloc(pool, + sizeof(apr_threadattr_t)); + + if ((*new) == NULL) { + return APR_ENOMEM; + } + + (*new)->pool = pool; + (*new)->detach = 0; + (*new)->stacksize = 0; + + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_threadattr_detach_set(apr_threadattr_t *attr, + apr_int32_t on) +{ + attr->detach = on; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_threadattr_detach_get(apr_threadattr_t *attr) +{ + if (attr->detach == 1) + return APR_DETACH; + return APR_NOTDETACH; +} + +APR_DECLARE(apr_status_t) apr_threadattr_stacksize_set(apr_threadattr_t *attr, + apr_size_t stacksize) +{ + attr->stacksize = stacksize; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_threadattr_guardsize_set(apr_threadattr_t *attr, + apr_size_t size) +{ + return APR_ENOTIMPL; +} + +static void *dummy_worker(void *opaque) +{ + apr_thread_t *thd = (apr_thread_t *)opaque; + TlsSetValue(tls_apr_thread, thd->td); + return thd->func(thd, thd->data); +} + +APR_DECLARE(apr_status_t) apr_thread_create(apr_thread_t **new, + apr_threadattr_t *attr, + apr_thread_start_t func, + void *data, apr_pool_t *pool) +{ + apr_status_t stat; + unsigned temp; + HANDLE handle; + + (*new) = (apr_thread_t *)apr_palloc(pool, sizeof(apr_thread_t)); + + if ((*new) == NULL) { + return APR_ENOMEM; + } + + (*new)->data = data; + (*new)->func = func; + (*new)->td = NULL; + stat = apr_pool_create(&(*new)->pool, pool); + if (stat != APR_SUCCESS) { + return stat; + } + + /* Use 0 for default Thread Stack Size, because that will + * default the stack to the same size as the calling thread. + */ +#ifndef _WIN32_WCE + if ((handle = (HANDLE)_beginthreadex(NULL, + (DWORD) (attr ? attr->stacksize : 0), + (unsigned int (APR_THREAD_FUNC *)(void *))dummy_worker, + (*new), 0, &temp)) == 0) { + return APR_FROM_OS_ERROR(_doserrno); + } +#else + if ((handle = CreateThread(NULL, + attr && attr->stacksize > 0 ? attr->stacksize : 0, + (unsigned int (APR_THREAD_FUNC *)(void *))dummy_worker, + (*new), 0, &temp)) == 0) { + return apr_get_os_error(); + } +#endif + if (attr && attr->detach) { + CloseHandle(handle); + } + else + (*new)->td = handle; + + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_thread_exit(apr_thread_t *thd, + apr_status_t retval) +{ + thd->exitval = retval; + apr_pool_destroy(thd->pool); + thd->pool = NULL; +#ifndef _WIN32_WCE + _endthreadex(0); +#else + ExitThread(0); +#endif + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_thread_join(apr_status_t *retval, + apr_thread_t *thd) +{ + apr_status_t rv = APR_SUCCESS; + + if (!thd->td) { + /* Can not join on detached threads */ + return APR_DETACH; + } + rv = WaitForSingleObject(thd->td, INFINITE); + if ( rv == WAIT_OBJECT_0 || rv == WAIT_ABANDONED) { + /* If the thread_exit has been called */ + if (!thd->pool) + *retval = thd->exitval; + else + rv = APR_INCOMPLETE; + } + else + rv = apr_get_os_error(); + CloseHandle(thd->td); + thd->td = NULL; + + return rv; +} + +APR_DECLARE(apr_status_t) apr_thread_detach(apr_thread_t *thd) +{ + if (thd->td && CloseHandle(thd->td)) { + thd->td = NULL; + return APR_SUCCESS; + } + else { + return apr_get_os_error(); + } +} + +APR_DECLARE(void) apr_thread_yield() +{ + /* SwitchToThread is not supported on Win9x, but since it's + * primarily a noop (entering time consuming code, therefore + * providing more critical threads a bit larger timeslice) + * we won't worry too much if it's not available. + */ +#ifndef _WIN32_WCE + if (apr_os_level >= APR_WIN_NT) { + SwitchToThread(); + } +#endif +} + +APR_DECLARE(apr_status_t) apr_thread_data_get(void **data, const char *key, + apr_thread_t *thread) +{ + return apr_pool_userdata_get(data, key, thread->pool); +} + +APR_DECLARE(apr_status_t) apr_thread_data_set(void *data, const char *key, + apr_status_t (*cleanup) (void *), + apr_thread_t *thread) +{ + return apr_pool_userdata_set(data, key, cleanup, thread->pool); +} + + +APR_DECLARE(apr_os_thread_t) apr_os_thread_current(void) +{ + HANDLE hthread = (HANDLE)TlsGetValue(tls_apr_thread); + HANDLE hproc; + + if (hthread) { + return hthread; + } + + hproc = GetCurrentProcess(); + hthread = GetCurrentThread(); + if (!DuplicateHandle(hproc, hthread, + hproc, &hthread, 0, FALSE, + DUPLICATE_SAME_ACCESS)) { + return NULL; + } + TlsSetValue(tls_apr_thread, hthread); + return hthread; +} + +APR_DECLARE(apr_status_t) apr_os_thread_get(apr_os_thread_t **thethd, + apr_thread_t *thd) +{ + if (thd == NULL) { + return APR_ENOTHREAD; + } + *thethd = thd->td; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_os_thread_put(apr_thread_t **thd, + apr_os_thread_t *thethd, + apr_pool_t *pool) +{ + if (pool == NULL) { + return APR_ENOPOOL; + } + if ((*thd) == NULL) { + (*thd) = (apr_thread_t *)apr_palloc(pool, sizeof(apr_thread_t)); + (*thd)->pool = pool; + } + (*thd)->td = thethd; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_thread_once_init(apr_thread_once_t **control, + apr_pool_t *p) +{ + (*control) = apr_pcalloc(p, sizeof(**control)); + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_thread_once(apr_thread_once_t *control, + void (*func)(void)) +{ + if (!InterlockedExchange(&control->value, 1)) { + func(); + } + return APR_SUCCESS; +} + +APR_DECLARE(int) apr_os_thread_equal(apr_os_thread_t tid1, + apr_os_thread_t tid2) +{ + /* Since the only tid's we support our are own, and + * apr_os_thread_current returns the identical handle + * to the one we created initially, the test is simple. + */ + return (tid1 == tid2); +} + +APR_POOL_IMPLEMENT_ACCESSOR(thread) diff --git a/threadproc/win32/threadpriv.c b/threadproc/win32/threadpriv.c new file mode 100644 index 0000000..787c142 --- /dev/null +++ b/threadproc/win32/threadpriv.c @@ -0,0 +1,101 @@ +/* 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_arch_threadproc.h" +#include "apr_thread_proc.h" +#include "apr_general.h" +#include "apr_lib.h" +#include "apr_errno.h" +#include "apr_portable.h" + +APR_DECLARE(apr_status_t) apr_threadkey_private_create(apr_threadkey_t **key, + void (*dest)(void *), + apr_pool_t *pool) +{ + (*key) = (apr_threadkey_t *)apr_palloc(pool, sizeof(apr_threadkey_t)); + if ((*key) == NULL) { + return APR_ENOMEM; + } + + (*key)->pool = pool; + + if (((*key)->key = TlsAlloc()) != 0xFFFFFFFF) { + return APR_SUCCESS; + } + return apr_get_os_error(); +} + +APR_DECLARE(apr_status_t) apr_threadkey_private_get(void **new, + apr_threadkey_t *key) +{ + if (((*new) = TlsGetValue(key->key))) { + return APR_SUCCESS; + } + return apr_get_os_error(); +} + +APR_DECLARE(apr_status_t) apr_threadkey_private_set(void *priv, + apr_threadkey_t *key) +{ + if (TlsSetValue(key->key, priv)) { + return APR_SUCCESS; + } + return apr_get_os_error(); +} + +APR_DECLARE(apr_status_t) apr_threadkey_private_delete(apr_threadkey_t *key) +{ + if (TlsFree(key->key)) { + return APR_SUCCESS; + } + return apr_get_os_error(); +} + +APR_DECLARE(apr_status_t) apr_threadkey_data_get(void **data, const char *key, + apr_threadkey_t *threadkey) +{ + return apr_pool_userdata_get(data, key, threadkey->pool); +} + +APR_DECLARE(apr_status_t) apr_threadkey_data_set(void *data, const char *key, + apr_status_t (*cleanup)(void *), + apr_threadkey_t *threadkey) +{ + return apr_pool_userdata_set(data, key, cleanup, threadkey->pool); +} + +APR_DECLARE(apr_status_t) apr_os_threadkey_get(apr_os_threadkey_t *thekey, + apr_threadkey_t *key) +{ + *thekey = key->key; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_os_threadkey_put(apr_threadkey_t **key, + apr_os_threadkey_t *thekey, + apr_pool_t *pool) +{ + if (pool == NULL) { + return APR_ENOPOOL; + } + if ((*key) == NULL) { + (*key) = (apr_threadkey_t *)apr_palloc(pool, sizeof(apr_threadkey_t)); + (*key)->pool = pool; + } + (*key)->key = *thekey; + return APR_SUCCESS; +} + -- cgit v1.2.3