/* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2000 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * * Portions of this software are based upon public domain software * originally written at the National Center for Supercomputing Applications, * University of Illinois, Urbana-Champaign. */ /* * This file will include OS specific functions which are not inlineable. * Any inlineable functions should be defined in os-inline.c instead. */ #include "httpd.h" #include "http_core.h" #include "os.h" #include "scoreboard.h" #include "http_log.h" #include "http_conf_globals.h" #ifdef __PIPE_ #include #include static TPF_FD_LIST *tpf_fds = NULL; #endif void *tpf_shm_static_ptr = NULL; unsigned short zinet_model; static FILE *sock_fp; int tpf_select(int maxfds, fd_set *reads, fd_set *writes, fd_set *excepts, struct timeval *tv) { /* We're going to force our way through select. We're only interested reads and TPF allows 2billion+ socket descriptors for we don't want an fd_set that big. Just assume that maxfds-1 contains the socket descriptor we're interested in. If it's 0, leave it alone. */ int sockets[1]; int no_reads = 0; int no_writes = 0; int no_excepts = 0; int timeout = 0; int rv = 0; if(maxfds) { if(tv) timeout = tv->tv_sec * 1000 + tv->tv_usec; sockets[0] = maxfds-1; no_reads++; } else sockets[0] = 0; ap_check_signals(); if ((no_reads + no_writes + no_excepts == 0) && (tv) && (tv->tv_sec + tv->tv_usec != 0)) { #ifdef TPF_HAVE_SAWNC /* TPF's select immediately returns if the sum of no_reads, no_writes, and no_excepts is zero. This means that the select calls in http_main.c for shutdown don't actually wait while killing children. The following code makes TPF's select work a little closer to everyone else's select: */ struct ev0bk evnblock; timeout = tv->tv_sec; if (tv->tv_usec) { timeout++; /* round up to seconds (like TPF's select does) */ } evnblock.evnpstinf.evnbkc1 = 1; /* nbr of posts needed */ evntc(&evnblock, EVENT_CNT, 'N', timeout, EVNTC_1052); tpf_sawnc(&evnblock, EVENT_CNT); #endif } else { rv = select(sockets, no_reads, no_writes, no_excepts, timeout); } ap_check_signals(); return rv; } int tpf_accept(int sockfd, struct sockaddr *peer, int *paddrlen) { int socks[1]; int rv; ap_check_signals(); socks[0] = sockfd; rv = select(socks, 1, 0, 0, 1000); errno = sock_errno(); if(rv>0) { ap_check_signals(); rv = accept(sockfd, peer, paddrlen); errno = sock_errno(); } return rv; } /* the getpass function is not usable on TPF */ char *getpass(const char* prompt) { errno = EIO; return((char *)NULL); } /* fork and exec functions are not defined on TPF due to the implementation of tpf_fork() */ pid_t fork(void) { errno = ENOSYS; return(-1); } int execl(const char *path, const char *arg0, ...) { errno = ENOSYS; return(-1); } int execle(const char *path, const char *arg0, ...) { errno = ENOSYS; return(-1); } int execve(const char *path, char *const argv[], char *const envp[]) { errno = ENOSYS; return(-1); } int execvp(const char *file, char *const argv[]) { errno = ENOSYS; return(-1); } int ap_tpf_spawn_child(pool *p, int (*func) (void *, child_info *), void *data, enum kill_conditions kill_how, int *pipe_in, int *pipe_out, int *pipe_err, int out_fds[], int in_fds[], int err_fds[]) { int i, temp_out, temp_in, temp_err, save_errno, pid, result=0; int fd_flags_out, fd_flags_in, fd_flags_err; struct tpf_fork_input fork_input; TPF_FORK_CHILD *cld = (TPF_FORK_CHILD *) data; array_header *env_arr = ap_table_elts ((array_header *) cld->subprocess_env); table_entry *elts = (table_entry *) env_arr->elts; #ifdef TPF_FORK_EXTENDED #define WHITE " \t\n" #define MAXARGC 49 char *arguments; char *args[MAXARGC + 1]; char **envp = NULL; pool *subpool = NULL; #include "util_script.h" #endif /* TPF_FORK_EXTENDED */ if (func) { if (result=func(data, NULL)) { return 0; /* error from child function */ } } if (pipe_out) { fd_flags_out = fcntl(out_fds[0], F_GETFD); fcntl(out_fds[0], F_SETFD, FD_CLOEXEC); temp_out = dup(STDOUT_FILENO); fcntl(temp_out, F_SETFD, FD_CLOEXEC); dup2(out_fds[1], STDOUT_FILENO); } if (pipe_in) { fd_flags_in = fcntl(in_fds[1], F_GETFD); fcntl(in_fds[1], F_SETFD, FD_CLOEXEC); temp_in = dup(STDIN_FILENO); fcntl(temp_in, F_SETFD, FD_CLOEXEC); dup2(in_fds[0], STDIN_FILENO); } if (pipe_err) { fd_flags_err = fcntl(err_fds[0], F_GETFD); fcntl(err_fds[0], F_SETFD, FD_CLOEXEC); temp_err = dup(STDERR_FILENO); fcntl(temp_err, F_SETFD, FD_CLOEXEC); dup2(err_fds[1], STDERR_FILENO); } /* set up environment variables for the tpf_fork */ if (cld->subprocess_env) { #ifdef TPF_FORK_EXTENDED /* with extended tpf_fork( ) we pass the pointer to a list of pointers */ /* that point to "key=value" strings for each env variable */ subpool = ap_make_sub_pool(p); envp = ap_create_environment(subpool, cld->subprocess_env); #else /* without extended tpf_fork( ) we setenv( ) each env variable */ /* so the child inherits them */ for (i = 0; i < env_arr->nelts; ++i) { if (!elts[i].key) continue; setenv (elts[i].key, elts[i].val, 1); } #endif /* TPF_FORK_EXTENDED */ } fork_input.program = (const char*) cld->filename; fork_input.prog_type = cld->prog_type; fork_input.istream = TPF_FORK_IS_BALANCE; fork_input.ebw_data_length = 0; fork_input.ebw_data = NULL; fork_input.parm_data = NULL; #ifdef TPF_FORK_EXTENDED /* use a copy of cld->filename because strtok is destructive */ arguments = ap_pstrdup(p, cld->filename); args[0] = strtok(arguments, WHITE); for (i = 0; i < MAXARGC && args[i] ; i++) { args[i + 1] = strtok(NULL, WHITE); } args[MAXARGC] = NULL; if ((pid = tpf_fork(&fork_input, (const char **)args, (const char **)envp)) < 0) { #else if ((pid = tpf_fork(&fork_input)) < 0) { #endif /* TPF_FORK_EXTENDED */ save_errno = errno; if (pipe_out) { close(out_fds[0]); } if (pipe_in) { close(in_fds[1]); } if (pipe_err) { close(err_fds[0]); } errno = save_errno; pid = 0; } #ifdef TPF_FORK_EXTENDED if (subpool) { ap_destroy_pool(subpool); } #else if (cld->subprocess_env) { for (i = 0; i < env_arr->nelts; ++i) { if (!elts[i].key) continue; unsetenv (elts[i].key); } } #endif /* TPF_FORK_EXTENDED */ if (pipe_out) { close(out_fds[1]); dup2(temp_out, STDOUT_FILENO); close(temp_out); fcntl(out_fds[0], F_SETFD, fd_flags_out); } if (pipe_in) { close(in_fds[0]); dup2(temp_in, STDIN_FILENO); close(temp_in); fcntl(in_fds[1], F_SETFD, fd_flags_in); } if (pipe_err) { close(err_fds[1]); dup2(temp_err, STDERR_FILENO); close(temp_err); fcntl(err_fds[0], F_SETFD, fd_flags_err); } if (pid) { ap_note_subprocess(p, pid, kill_how); if (pipe_out) { *pipe_out = out_fds[0]; } if (pipe_in) { *pipe_in = in_fds[1]; } if (pipe_err) { *pipe_err = err_fds[0]; } } return pid; } pid_t os_fork(server_rec *s, int slot) { struct tpf_fork_input fork_input; APACHE_TPF_INPUT input_parms; int count; listen_rec *lr; fflush(stdin); if (dup2(fileno(sock_fp), STDIN_FILENO) == -1) ap_log_error(APLOG_MARK, APLOG_CRIT, s, "unable to replace stdin with sock device driver"); fflush(stdout); if (dup2(fileno(sock_fp), STDOUT_FILENO) == -1) ap_log_error(APLOG_MARK, APLOG_CRIT, s, "unable to replace stdout with sock device driver"); input_parms.generation = ap_my_generation; #if defined(USE_TPF_SCOREBOARD) || defined(USE_SHMGET_SCOREBOARD) input_parms.scoreboard_heap = ap_scoreboard_image; #else input_parms.scoreboard_fd = scoreboard_fd; #endif lr = ap_listeners; count = 0; do { input_parms.listeners[count] = lr->fd; lr = lr->next; count++; } while(lr != ap_listeners); input_parms.slot = slot; input_parms.restart_time = ap_restart_time; input_parms.shm_static_ptr = tpf_shm_static_ptr; input_parms.tpf_fds = tpf_fds; fork_input.ebw_data = &input_parms; fork_input.program = ap_server_argv0; fork_input.prog_type = TPF_FORK_NAME; fork_input.istream = TPF_FORK_IS_BALANCE; fork_input.ebw_data_length = sizeof(input_parms); fork_input.parm_data = "-x"; #ifdef TPF_FORK_EXTENDED return tpf_fork(&fork_input, NULL, NULL); #else return tpf_fork(&fork_input); #endif /* TPF_FORK_EXTENDED */ } void ap_tpf_zinet_checks(int standalone, const char *servername, server_rec *s) { INETD_IDCT_ENTRY_PTR idct; /* explicitly disallow "ServerType inetd" on TPF */ if (!standalone) { ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, s, TPF_SERVERTYPE_MSG); exit(1); /* abort start-up of server */ } /* figure out zinet model for our server from the idct slot */ idct = inetd_getServer(servername); if (idct) { zinet_model = idct->model; free(idct); } else { ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, s, TPF_UNABLE_TO_DETERMINE_ZINET_MODEL); exit(1); /* abort start-up of server */ } /* check for valid zinet models */ if (zinet_model != INETD_IDCF_MODEL_DAEMON && zinet_model != INETD_IDCF_MODEL_NOLISTEN) { ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, s, TPF_STANDALONE_CONFLICT_MSG); exit(1); /* abort start-up of server */ } #ifdef TPF_NOLISTEN_WARNING /* nag about switching to DAEMON model */ if (zinet_model == INETD_IDCF_MODEL_NOLISTEN) { ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, s, TPF_NOLISTEN_WARNING); } #endif } int os_check_server(char *server) { int *current_acn; if (zinet_model == INETD_IDCF_MODEL_NOLISTEN) { /* if NOLISTEN model, check with ZINET for status */ if (inetd_getServerStatus(server) == INETD_SERVER_STATUS_INACTIVE) { return 1; } /* and check that program activation number hasn't changed */ current_acn = (int *)cinfc_fast(CINFC_CMMACNUM); if (ecbp2()->ce2acn != *current_acn) { return 1; } } else { /* if DAEMON model, just make sure parent is still around */ if (getppid() == 1) { return 1; } } return 0; } void os_note_additional_cleanups(pool *p, int sd) { char sockfilename[50]; /* write the socket to file so that TPF socket device driver will close socket in case we happen to abend. */ sprintf(sockfilename, "/dev/tpf.socket.file/%.8X", sd); sock_fp = fopen(sockfilename, "r+"); /* arrange to close on exec or restart */ ap_note_cleanups_for_file(p, sock_fp); fcntl(sd,F_SETFD,FD_CLOEXEC); } void os_tpf_child(APACHE_TPF_INPUT *input_parms) { tpf_child = 1; ap_my_generation = input_parms->generation; ap_restart_time = input_parms->restart_time; tpf_fds = input_parms->tpf_fds; tpf_shm_static_ptr = input_parms->shm_static_ptr; } #ifndef __PIPE_ int pipe(int fildes[2]) { errno = ENOSYS; return(-1); } API_EXPORT(piped_log *) ap_open_piped_log(pool *p, const char *program) { fprintf(stderr, "Pipes not supported on this TPF system\n"); exit (1); } #else void ap_tpf_detach_shared_mem(void *address) { if (*((void **)address)) { shmdt(*((void **)address)); *((void **)address) = NULL; } } static void *ap_tpf_get_shared_mem(size_t size) { key_t shmkey = IPC_PRIVATE; int shmid = -1; void *result; if ((shmid = shmget(shmkey, size, IPC_CREAT | SHM_R | SHM_W)) == -1) { perror("shmget failed in ap_tpf_get_shared_mem funciton"); exit(1); } #define BADSHMAT ((void *)(-1)) if ((result = shmat(shmid, 0, 0)) == BADSHMAT) { perror("shmat failed in ap_tpf_get_shared_mem"); } if (shmctl(shmid, IPC_RMID, NULL) != 0) { perror("shmctl(IPC_RMID) failed in ap_tpf_get_shared_mem"); } if (result == BADSHMAT) { /* now bailout */ exit(1); } return result; } int ap_tpf_fd_lookup(enum FILE_TYPE file_type, const char *fname) /* lookup a fd in the fd inheritance table */ { if (tpf_fds) { int i; TPF_FD_ITEM *fd_item = &tpf_fds->first_item; for (i = 1; i <= tpf_fds->nbr_of_items; i++, fd_item++) { /* check for an fd with the same type and name */ if ((file_type == fd_item->file_type) && (strcmp(fname, fd_item->fname) == 0) ) { /* we've got a match, check that fd is still open */ struct stat stbuf; if (fstat(fd_item->fd, &stbuf) == 0) { return(fd_item->fd); } else { /* fd is not open - the entire fd table is suspect */ fprintf(stderr, "fstat failed in ap_tpf_fd_lookup " "for fd %i (filename/pipe to %s): %s\n", fd_item->fd, fname, strerror(errno)); ap_tpf_detach_shared_mem(&tpf_fds); return(-1); } } } } return(-1); } void ap_tpf_add_fd(pool *p, int fd, enum FILE_TYPE file_type, const char *fname) /* add a newly opened fd to the fd inheritance table */ { int fname_size; if (tpf_child) { return; /* no kids allowed */ } if (tpf_fds == NULL) { /* get shared memory if necssary */ tpf_fds = ap_tpf_get_shared_mem((size_t)TPF_FD_LIST_SIZE); if (tpf_fds) { ap_register_cleanup(p, (void *)&tpf_fds, ap_tpf_detach_shared_mem, ap_null_cleanup); tpf_fds->nbr_of_items = 0; tpf_fds->next_avail_byte = &tpf_fds->first_item; tpf_fds->last_avail_byte = (char *)tpf_fds + TPF_FD_LIST_SIZE; } } /* add fd */ if (tpf_fds) { TPF_FD_ITEM *fd_item; /* make sure there's room */ fname_size = strlen(fname) + 1; if (sizeof(TPF_FD_ITEM) + fname_size > (char *)tpf_fds->last_avail_byte - (char *)tpf_fds->next_avail_byte) { fprintf(stderr, "fd inheritance table out of room, increase " "TPF_FD_LIST_SIZE in os.h and recompile Apache\n"); exit(1); } /* add the new item */ fd_item = tpf_fds->next_avail_byte; tpf_fds->next_avail_byte = fd_item + 1; tpf_fds->last_avail_byte = (char *)tpf_fds->last_avail_byte - fname_size; fd_item->fname = tpf_fds->last_avail_byte; strcpy(fd_item->fname, fname); fd_item->fd = fd; fd_item->file_type = file_type; tpf_fds->nbr_of_items++; } } API_EXPORT(piped_log *) ap_open_piped_log(pool *p, const char *program) { int log_fd; piped_log *pl; /* check fd inheritance table to see if this log is already open */ log_fd = ap_tpf_fd_lookup(PIPE_OUT, program); if (log_fd < 0) { /* this is a new log - open it */ FILE *dummy; TPF_FORK_CHILD cld; cld.filename = (char *)program; cld.subprocess_env = NULL; cld.prog_type = FORK_NAME; if (ap_spawn_child(p, NULL, &cld, kill_after_timeout, &dummy, NULL, NULL)) { log_fd = fileno(dummy); /* add this log to the fd inheritance table */ ap_tpf_add_fd(p, log_fd, PIPE_OUT, program); } else { perror("ap_spawn_child"); fprintf(stderr, "Couldn't fork child for piped log process\n"); exit (1); } } pl = ap_palloc(p, sizeof (*pl)); pl->p = p; pl->fds[1] = log_fd; return pl; } #endif /* __PIPE_ */ /* The following functions are used for the tpf specific module called mod_tpf_shm_static. This module is a clone of Apache's mod_mmap_static. Because TPF doesn't support the system call mmap(), it is replaced by shared memory, but uses the mmap directives, etc. */ union align{ /* Types which are likely to have the longest RELEVANT alignment * restrictions... */ char *cp; void (*f) (void); long l; FILE *fp; double d; }; #define CLICK_SZ (sizeof(union align)) union block_hdr { union align a; /* Actual header... */ struct { char *endp; union block_hdr *next; char *first_avail; #ifdef POOL_DEBUG union block_hdr *global_next; struct pool *owning_pool; #endif } h; }; struct pool { union block_hdr *first; union block_hdr *last; struct cleanup *cleanups; struct process_chain *subprocesses; struct pool *sub_pools; struct pool *sub_next; struct pool *sub_prev; struct pool *parent; char *free_first_avail; #ifdef ALLOC_USE_MALLOC void *allocation_list; #endif #ifdef POOL_DEBUG struct pool *joined; #endif }; #include "ap_alloc.h" #define POOL_HDR_CLICKS (1 + ((sizeof(struct pool) - 1) / CLICK_SZ)) #define POOL_HDR_BYTES (POOL_HDR_CLICKS * CLICK_SZ) pool * ap_get_shared_mem_pool(size_t size) { pool *new_pool; union block_hdr *blok; blok = (union block_hdr *) ap_tpf_get_shared_mem(size); /* if shm fails, it will exit blok will be valid here */ memset((char *) blok, '\0', size); blok->h.next = NULL; blok->h.first_avail = (char *) (blok + 1); blok->h.endp = size + blok->h.first_avail; new_pool = (pool *) blok->h.first_avail; blok->h.first_avail += POOL_HDR_BYTES; new_pool->free_first_avail = blok->h.first_avail; new_pool->first = new_pool->last = blok; return new_pool; } int ap_check_shm_space(struct pool *a, int size) { union block_hdr *blok = a->last; char *first_avail = blok->h.first_avail; char *new_first_avail; new_first_avail = first_avail + size; if (new_first_avail <= blok->h.endp) { return (1); } else return (0); } /* This function serves as an interim killpg for Apache shutdown purposes. TPF won't have an actual killpg for a very long time, if ever. (And kill with a negative pid doesn't work on TPF either.) */ int killpg(pid_t pgrp, int sig) { int i; ap_sync_scoreboard_image(); for (i = 0; i < HARD_SERVER_LIMIT; ++i) { int pid = ap_scoreboard_image->parent[i].pid; /* the pgrp check is so that we don't kill ourself: */ if (pid && pid != pgrp) { kill(pid, sig); } } return(0); } /* This function augments http_main's show_compile_settings function. This way definitions that are only shown on TPF won't clutter up main line code. */ void show_os_specific_compile_settings(void) { #ifdef USE_TPF_SCOREBOARD printf(" -D USE_TPF_SCOREBOARD\n"); #endif #ifdef TPF_FORK_EXTENDED printf(" -D TPF_FORK_EXTENDED\n"); #endif #ifdef TPF_HAVE_NONSOCKET_SELECT printf(" -D TPF_HAVE_NONSOCKET_SELECT\n"); #endif #ifdef TPF_NO_NONSOCKET_SELECT printf(" -D TPF_NO_NONSOCKET_SELECT\n"); #endif #ifdef TPF_HAVE_SAWNC printf(" -D TPF_HAVE_SAWNC\n"); #endif #ifdef TPF_NO_SAWNC printf(" -D TPF_NO_SAWNC\n"); #endif }