1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "apr_arch_threadproc.h" 18#include "apr_arch_file_io.h" 19#include "apr_strings.h" 20#include "apr_portable.h" 21 22#include <proc.h> 23 24/* Heavy on no'ops, here's what we want to pass if there is APR_NO_FILE 25 * requested for a specific child handle; 26 */ 27static apr_file_t no_file = { NULL, -1, }; 28 29static apr_status_t apr_netware_proc_cleanup(void *theproc) 30{ 31 apr_proc_t *proc = theproc; 32 int exit_int; 33 int waitpid_options = WUNTRACED | WNOHANG; 34 35 if (proc->pid > 0) { 36 waitpid(proc->pid, &exit_int, waitpid_options); 37 } 38 39/* NXVmDestroy(proc->pid); */ 40 return APR_SUCCESS; 41} 42 43APR_DECLARE(apr_status_t) apr_procattr_create(apr_procattr_t **new,apr_pool_t *pool) 44{ 45 (*new) = (apr_procattr_t *)apr_pcalloc(pool, sizeof(apr_procattr_t)); 46 47 if ((*new) == NULL) { 48 return APR_ENOMEM; 49 } 50 (*new)->pool = pool; 51 (*new)->cmdtype = APR_PROGRAM; 52 /* Default to a current path since NetWare doesn't handle it very well */ 53 apr_filepath_get(&((*new)->currdir), APR_FILEPATH_NATIVE, pool); 54 (*new)->detached = 1; 55 return APR_SUCCESS; 56 57} 58 59APR_DECLARE(apr_status_t) apr_procattr_io_set(apr_procattr_t *attr, 60 apr_int32_t in, 61 apr_int32_t out, 62 apr_int32_t err) 63{ 64 apr_status_t rv; 65 66 if ((in != APR_NO_PIPE) && (in != APR_NO_FILE)) { 67 /* APR_CHILD_BLOCK maps to APR_WRITE_BLOCK, while 68 * APR_PARENT_BLOCK maps to APR_READ_BLOCK, so transpose 69 * the CHILD/PARENT blocking flags for the stdin pipe. 70 * stdout/stderr map to the correct mode by default. 71 */ 72 if (in == APR_CHILD_BLOCK) 73 in = APR_READ_BLOCK; 74 else if (in == APR_PARENT_BLOCK) 75 in = APR_WRITE_BLOCK; 76 77 if ((rv = apr_file_pipe_create_ex(&attr->child_in, &attr->parent_in, 78 in, attr->pool)) == APR_SUCCESS) 79 rv = apr_file_inherit_unset(attr->parent_in); 80 if (rv != APR_SUCCESS) 81 return rv; 82 } 83 else if (in == APR_NO_FILE) 84 attr->child_in = &no_file; 85 86 if ((out != APR_NO_PIPE) && (out != APR_NO_FILE)) { 87 if ((rv = apr_file_pipe_create_ex(&attr->parent_out, &attr->child_out, 88 out, attr->pool)) == APR_SUCCESS) 89 rv = apr_file_inherit_unset(attr->parent_out); 90 if (rv != APR_SUCCESS) 91 return rv; 92 } 93 else if (out == APR_NO_FILE) 94 attr->child_out = &no_file; 95 96 if ((err != APR_NO_PIPE) && (err != APR_NO_FILE)) { 97 if ((rv = apr_file_pipe_create_ex(&attr->parent_err, &attr->child_err, 98 err, attr->pool)) == APR_SUCCESS) 99 rv = apr_file_inherit_unset(attr->parent_err); 100 if (rv != APR_SUCCESS) 101 return rv; 102 } 103 else if (err == APR_NO_FILE) 104 attr->child_err = &no_file; 105 106 return APR_SUCCESS; 107} 108 109 110APR_DECLARE(apr_status_t) apr_procattr_child_in_set(apr_procattr_t *attr, apr_file_t *child_in, 111 apr_file_t *parent_in) 112{ 113 apr_status_t rv = APR_SUCCESS; 114 115 if (attr->child_in == NULL && attr->parent_in == NULL 116 && child_in == NULL && parent_in == NULL) 117 if ((rv = apr_file_pipe_create(&attr->child_in, &attr->parent_in, 118 attr->pool)) == APR_SUCCESS) 119 rv = apr_file_inherit_unset(attr->parent_in); 120 121 if (child_in != NULL && rv == APR_SUCCESS) { 122 if (attr->child_in && (attr->child_in->filedes != -1)) 123 rv = apr_file_dup2(attr->child_in, child_in, attr->pool); 124 else { 125 attr->child_in = NULL; 126 if ((rv = apr_file_dup(&attr->child_in, child_in, attr->pool)) 127 == APR_SUCCESS) 128 rv = apr_file_inherit_set(attr->child_in); 129 } 130 } 131 132 if (parent_in != NULL && rv == APR_SUCCESS) { 133 rv = apr_file_dup(&attr->parent_in, parent_in, attr->pool); 134 } 135 136 return rv; 137} 138 139 140APR_DECLARE(apr_status_t) apr_procattr_child_out_set(apr_procattr_t *attr, apr_file_t *child_out, 141 apr_file_t *parent_out) 142{ 143 apr_status_t rv = APR_SUCCESS; 144 145 if (attr->child_out == NULL && attr->parent_out == NULL 146 && child_out == NULL && parent_out == NULL) 147 if ((rv = apr_file_pipe_create(&attr->parent_out, &attr->child_out, 148 attr->pool)) == APR_SUCCESS) 149 rv = apr_file_inherit_unset(attr->parent_out); 150 151 if (child_out != NULL && rv == APR_SUCCESS) { 152 if (attr->child_out && (attr->child_out->filedes != -1)) 153 rv = apr_file_dup2(attr->child_out, child_out, attr->pool); 154 else { 155 attr->child_out = NULL; 156 if ((rv = apr_file_dup(&attr->child_out, child_out, attr->pool)) 157 == APR_SUCCESS) 158 rv = apr_file_inherit_set(attr->child_out); 159 } 160 } 161 162 if (parent_out != NULL && rv == APR_SUCCESS) { 163 rv = apr_file_dup(&attr->parent_out, parent_out, attr->pool); 164 } 165 166 return rv; 167} 168 169 170APR_DECLARE(apr_status_t) apr_procattr_child_err_set(apr_procattr_t *attr, apr_file_t *child_err, 171 apr_file_t *parent_err) 172{ 173 apr_status_t rv = APR_SUCCESS; 174 175 if (attr->child_err == NULL && attr->parent_err == NULL 176 && child_err == NULL && parent_err == NULL) 177 if ((rv = apr_file_pipe_create(&attr->parent_err, &attr->child_err, 178 attr->pool)) == APR_SUCCESS) 179 rv = apr_file_inherit_unset(attr->parent_err); 180 181 if (child_err != NULL && rv == APR_SUCCESS) { 182 if (attr->child_err && (attr->child_err->filedes != -1)) 183 rv = apr_file_dup2(attr->child_err, child_err, attr->pool); 184 else { 185 attr->child_err = NULL; 186 if ((rv = apr_file_dup(&attr->child_err, child_err, attr->pool)) 187 == APR_SUCCESS) 188 rv = apr_file_inherit_set(attr->child_err); 189 } 190 } 191 192 if (parent_err != NULL && rv == APR_SUCCESS) { 193 rv = apr_file_dup(&attr->parent_err, parent_err, attr->pool); 194 } 195 196 return rv; 197} 198 199 200APR_DECLARE(apr_status_t) apr_procattr_dir_set(apr_procattr_t *attr, 201 const char *dir) 202{ 203 return apr_filepath_merge(&attr->currdir, NULL, dir, 204 APR_FILEPATH_NATIVE, attr->pool); 205} 206 207APR_DECLARE(apr_status_t) apr_procattr_cmdtype_set(apr_procattr_t *attr, 208 apr_cmdtype_e cmd) 209{ 210 /* won't ever be called on this platform, so don't save the function pointer */ 211 return APR_SUCCESS; 212} 213 214APR_DECLARE(apr_status_t) apr_procattr_detach_set(apr_procattr_t *attr, apr_int32_t detach) 215{ 216 attr->detached = detach; 217 return APR_SUCCESS; 218} 219 220#if APR_HAS_FORK 221APR_DECLARE(apr_status_t) apr_proc_fork(apr_proc_t *proc, apr_pool_t *pool) 222{ 223 int pid; 224 225 if ((pid = fork()) < 0) { 226 return errno; 227 } 228 else if (pid == 0) { 229 proc->pid = pid; 230 proc->in = NULL; 231 proc->out = NULL; 232 proc->err = NULL; 233 return APR_INCHILD; 234 } 235 proc->pid = pid; 236 proc->in = NULL; 237 proc->out = NULL; 238 proc->err = NULL; 239 return APR_INPARENT; 240} 241#endif 242 243static apr_status_t limit_proc(apr_procattr_t *attr) 244{ 245#if APR_HAVE_STRUCT_RLIMIT && APR_HAVE_SETRLIMIT 246#ifdef RLIMIT_CPU 247 if (attr->limit_cpu != NULL) { 248 if ((setrlimit(RLIMIT_CPU, attr->limit_cpu)) != 0) { 249 return errno; 250 } 251 } 252#endif 253#ifdef RLIMIT_NPROC 254 if (attr->limit_nproc != NULL) { 255 if ((setrlimit(RLIMIT_NPROC, attr->limit_nproc)) != 0) { 256 return errno; 257 } 258 } 259#endif 260#if defined(RLIMIT_AS) 261 if (attr->limit_mem != NULL) { 262 if ((setrlimit(RLIMIT_AS, attr->limit_mem)) != 0) { 263 return errno; 264 } 265 } 266#elif defined(RLIMIT_DATA) 267 if (attr->limit_mem != NULL) { 268 if ((setrlimit(RLIMIT_DATA, attr->limit_mem)) != 0) { 269 return errno; 270 } 271 } 272#elif defined(RLIMIT_VMEM) 273 if (attr->limit_mem != NULL) { 274 if ((setrlimit(RLIMIT_VMEM, attr->limit_mem)) != 0) { 275 return errno; 276 } 277 } 278#endif 279#else 280 /* 281 * Maybe make a note in error_log that setrlimit isn't supported?? 282 */ 283 284#endif 285 return APR_SUCCESS; 286} 287 288APR_DECLARE(apr_status_t) apr_procattr_child_errfn_set(apr_procattr_t *attr, 289 apr_child_errfn_t *errfn) 290{ 291 /* won't ever be called on this platform, so don't save the function pointer */ 292 return APR_SUCCESS; 293} 294 295APR_DECLARE(apr_status_t) apr_procattr_error_check_set(apr_procattr_t *attr, 296 apr_int32_t chk) 297{ 298 /* won't ever be used on this platform, so don't save the flag */ 299 return APR_SUCCESS; 300} 301 302APR_DECLARE(apr_status_t) apr_procattr_addrspace_set(apr_procattr_t *attr, 303 apr_int32_t addrspace) 304{ 305 attr->addrspace = addrspace; 306 return APR_SUCCESS; 307} 308 309APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *newproc, 310 const char *progname, 311 const char * const *args, 312 const char * const *env, 313 apr_procattr_t *attr, 314 apr_pool_t *pool) 315{ 316 wiring_t wire; 317 int addr_space; 318 319 wire.infd = attr->child_in 320 ? (attr->child_in->filedes != -1 ? attr->child_in->filedes 321 : FD_UNUSED) 322 : fileno(stdin); 323 wire.outfd = attr->child_out 324 ? (attr->child_out->filedes != -1 ? attr->child_out->filedes 325 : FD_UNUSED) 326 : fileno(stdout); 327 wire.errfd = attr->child_err 328 ? (attr->child_err->filedes != -1 ? attr->child_err->filedes 329 : FD_UNUSED) 330 : fileno(stderr); 331 332 newproc->in = attr->parent_in; 333 newproc->out = attr->parent_out; 334 newproc->err = attr->parent_err; 335 336 /* attr->detached and PROC_DETACHED do not mean the same thing. attr->detached means 337 * start the NLM in a separate address space. PROC_DETACHED means don't wait for the 338 * NLM to unload by calling wait() or waitpid(), just clean up */ 339 addr_space = PROC_LOAD_SILENT | (attr->addrspace ? 0 : PROC_CURRENT_SPACE); 340 addr_space |= (attr->detached ? PROC_DETACHED : 0); 341 342 if (attr->currdir) { 343 char *fullpath = NULL; 344 apr_status_t rv; 345 346 if ((rv = apr_filepath_merge(&fullpath, attr->currdir, progname, 347 APR_FILEPATH_NATIVE, pool)) != APR_SUCCESS) { 348 return rv; 349 } 350 progname = fullpath; 351 } 352 353 if ((newproc->pid = procve(progname, addr_space, (const char**)env, &wire, 354 NULL, NULL, 0, NULL, (const char **)args)) == -1) { 355 return errno; 356 } 357 358 if (attr->child_in && (attr->child_in->filedes != -1)) { 359 apr_pool_cleanup_kill(apr_file_pool_get(attr->child_in), 360 attr->child_in, apr_unix_file_cleanup); 361 apr_file_close(attr->child_in); 362 } 363 if (attr->child_out && (attr->child_out->filedes != -1)) { 364 apr_pool_cleanup_kill(apr_file_pool_get(attr->child_out), 365 attr->child_out, apr_unix_file_cleanup); 366 apr_file_close(attr->child_out); 367 } 368 if (attr->child_err && (attr->child_err->filedes != -1)) { 369 apr_pool_cleanup_kill(apr_file_pool_get(attr->child_err), 370 attr->child_err, apr_unix_file_cleanup); 371 apr_file_close(attr->child_err); 372 } 373 374 apr_pool_cleanup_register(pool, (void *)newproc, apr_netware_proc_cleanup, 375 apr_pool_cleanup_null); 376 377 return APR_SUCCESS; 378} 379 380APR_DECLARE(apr_status_t) apr_proc_wait_all_procs(apr_proc_t *proc, 381 int *exitcode, 382 apr_exit_why_e *exitwhy, 383 apr_wait_how_e waithow, 384 apr_pool_t *p) 385{ 386 proc->pid = -1; 387 return apr_proc_wait(proc, exitcode, exitwhy, waithow); 388} 389 390APR_DECLARE(apr_status_t) apr_proc_wait(apr_proc_t *proc, 391 int *exitcode, apr_exit_why_e *exitwhy, 392 apr_wait_how_e waithow) 393{ 394 pid_t pstatus; 395 int waitpid_options = WUNTRACED; 396 int exit_int; 397 int ignore; 398 apr_exit_why_e ignorewhy; 399 400 if (exitcode == NULL) { 401 exitcode = &ignore; 402 } 403 404 if (exitwhy == NULL) { 405 exitwhy = &ignorewhy; 406 } 407 408 if (waithow != APR_WAIT) { 409 waitpid_options |= WNOHANG; 410 } 411 412 /* If the pid is 0 then the process was started detached. There 413 is no need to wait since there is nothing to wait for on a 414 detached process. Starting a process as non-detached and 415 then calling wait or waitpid could cause the thread to hang. 416 The reason for this is because NetWare does not have a way 417 to kill or even signal a process to be killed. Starting 418 all processes as detached avoids the possibility of a 419 thread hanging. */ 420 if (proc->pid == 0) { 421 *exitwhy = APR_PROC_EXIT; 422 *exitcode = 0; 423 return APR_CHILD_DONE; 424 } 425 426 if ((pstatus = waitpid(proc->pid, &exit_int, waitpid_options)) > 0) { 427 proc->pid = pstatus; 428 429 if (WIFEXITED(exit_int)) { 430 *exitwhy = APR_PROC_EXIT; 431 *exitcode = WEXITSTATUS(exit_int); 432 } 433 else if (WIFSIGNALED(exit_int)) { 434 *exitwhy = APR_PROC_SIGNAL; 435 *exitcode = WIFTERMSIG(exit_int); 436 } 437 else { 438 /* unexpected condition */ 439 return APR_EGENERAL; 440 } 441 442 return APR_CHILD_DONE; 443 } 444 else if (pstatus == 0) { 445 return APR_CHILD_NOTDONE; 446 } 447 448 return errno; 449} 450 451#if APR_HAVE_STRUCT_RLIMIT 452APR_DECLARE(apr_status_t) apr_procattr_limit_set(apr_procattr_t *attr, 453 apr_int32_t what, 454 struct rlimit *limit) 455{ 456 switch(what) { 457 case APR_LIMIT_CPU: 458#ifdef RLIMIT_CPU 459 attr->limit_cpu = limit; 460 break; 461#else 462 return APR_ENOTIMPL; 463#endif 464 465 case APR_LIMIT_MEM: 466#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) 467 attr->limit_mem = limit; 468 break; 469#else 470 return APR_ENOTIMPL; 471#endif 472 473 case APR_LIMIT_NPROC: 474#ifdef RLIMIT_NPROC 475 attr->limit_nproc = limit; 476 break; 477#else 478 return APR_ENOTIMPL; 479#endif 480 481 case APR_LIMIT_NOFILE: 482#ifdef RLIMIT_NOFILE 483 attr->limit_nofile = limit; 484 break; 485#else 486 return APR_ENOTIMPL; 487#endif 488 489 } 490 return APR_SUCCESS; 491} 492#endif /* APR_HAVE_STRUCT_RLIMIT */ 493 494APR_DECLARE(apr_status_t) apr_procattr_user_set(apr_procattr_t *attr, 495 const char *username, 496 const char *password) 497{ 498 /* Always return SUCCESS because NetWare threads don't run as a user */ 499 return APR_SUCCESS; 500} 501 502APR_DECLARE(apr_status_t) apr_procattr_group_set(apr_procattr_t *attr, 503 const char *groupname) 504{ 505 /* Always return SUCCESS because NetWare threads don't run within a group */ 506 return APR_SUCCESS; 507} 508