LCOV - code coverage report
Current view: directory - nsprpub/pr/src/md/unix - uxproces.c (source / functions) Found Hit Coverage
Test: app.info Lines: 315 198 62.9 %
Date: 2012-06-02 Functions: 15 13 86.7 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is the Netscape Portable Runtime (NSPR).
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998-2000
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      26                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : #include "primpl.h"
      39                 : 
      40                 : #include <sys/types.h>
      41                 : #include <unistd.h>
      42                 : #include <fcntl.h>
      43                 : #include <signal.h>
      44                 : #include <sys/wait.h>
      45                 : #include <string.h>
      46                 : #if defined(AIX)
      47                 : #include <dlfcn.h>  /* For dlopen, dlsym, dlclose */
      48                 : #endif
      49                 : 
      50                 : #if defined(DARWIN)
      51                 : #if defined(HAVE_CRT_EXTERNS_H)
      52                 : #include <crt_externs.h>
      53                 : #endif
      54                 : #else
      55                 : PR_IMPORT_DATA(char **) environ;
      56                 : #endif
      57                 : 
      58                 : /*
      59                 :  * HP-UX 9 doesn't have the SA_RESTART flag.
      60                 :  */
      61                 : #ifndef SA_RESTART
      62                 : #define SA_RESTART 0
      63                 : #endif
      64                 : 
      65                 : /*
      66                 :  **********************************************************************
      67                 :  *
      68                 :  * The Unix process routines
      69                 :  *
      70                 :  **********************************************************************
      71                 :  */
      72                 : 
      73                 : #define _PR_SIGNALED_EXITSTATUS 256
      74                 : 
      75                 : typedef enum pr_PidState {
      76                 :     _PR_PID_DETACHED,
      77                 :     _PR_PID_REAPED,
      78                 :     _PR_PID_WAITING
      79                 : } pr_PidState;
      80                 : 
      81                 : typedef struct pr_PidRecord {
      82                 :     pid_t pid;
      83                 :     int exitStatus;
      84                 :     pr_PidState state;
      85                 :     PRCondVar *reapedCV;
      86                 :     struct pr_PidRecord *next;
      87                 : } pr_PidRecord;
      88                 : 
      89                 : /*
      90                 :  * Irix sprocs and LinuxThreads are actually a kind of processes
      91                 :  * that can share the virtual address space and file descriptors.
      92                 :  */
      93                 : #if (defined(IRIX) && !defined(_PR_PTHREADS)) \
      94                 :         || ((defined(LINUX) || defined(__GNU__) || defined(__GLIBC__)) \
      95                 :         && defined(_PR_PTHREADS))
      96                 : #define _PR_SHARE_CLONES
      97                 : #endif
      98                 : 
      99                 : /*
     100                 :  * The macro _PR_NATIVE_THREADS indicates that we are
     101                 :  * using native threads only, so waitpid() blocks just the
     102                 :  * calling thread, not the process.  In this case, the waitpid
     103                 :  * daemon thread can safely block in waitpid().  So we don't
     104                 :  * need to catch SIGCHLD, and the pipe to unblock PR_Poll() is
     105                 :  * also not necessary.
     106                 :  */
     107                 : 
     108                 : #if defined(_PR_GLOBAL_THREADS_ONLY) \
     109                 :         || (defined(_PR_PTHREADS) \
     110                 :         && !defined(LINUX) && !defined(__GNU__) && !defined(__GLIBC__))
     111                 : #define _PR_NATIVE_THREADS
     112                 : #endif
     113                 : 
     114                 : /*
     115                 :  * All the static variables used by the Unix process routines are
     116                 :  * collected in this structure.
     117                 :  */
     118                 : 
     119                 : static struct {
     120                 :     PRCallOnceType once;
     121                 :     PRThread *thread;
     122                 :     PRLock *ml;
     123                 : #if defined(_PR_NATIVE_THREADS)
     124                 :     PRInt32 numProcs;
     125                 :     PRCondVar *cv;
     126                 : #else
     127                 :     int pipefd[2];
     128                 : #endif
     129                 :     pr_PidRecord **pidTable;
     130                 : 
     131                 : #ifdef _PR_SHARE_CLONES
     132                 :     struct pr_CreateProcOp *opHead, *opTail;
     133                 : #endif
     134                 : 
     135                 : #ifdef AIX
     136                 :     pid_t (*forkptr)(void);  /* Newer versions of AIX (starting in 4.3.2)
     137                 :                               * have f_fork, which is faster than the
     138                 :                               * regular fork in a multithreaded process
     139                 :                               * because it skips calling the fork handlers.
     140                 :                               * So we look up the f_fork symbol to see if
     141                 :                               * it's available and fall back on fork.
     142                 :                               */
     143                 : #endif /* AIX */
     144                 : } pr_wp;
     145                 : 
     146                 : #ifdef _PR_SHARE_CLONES
     147                 : static int pr_waitpid_daemon_exit;
     148                 : 
     149                 : void
     150               0 : _MD_unix_terminate_waitpid_daemon(void)
     151                 : {
     152               0 :     if (pr_wp.thread) {
     153               0 :         pr_waitpid_daemon_exit = 1;
     154               0 :         write(pr_wp.pipefd[1], "", 1);
     155               0 :         PR_JoinThread(pr_wp.thread);
     156                 :     }
     157               0 : }
     158                 : #endif
     159                 : 
     160                 : static PRStatus _MD_InitProcesses(void);
     161                 : #if !defined(_PR_NATIVE_THREADS)
     162                 : static void pr_InstallSigchldHandler(void);
     163                 : #endif
     164                 : 
     165                 : static PRProcess *
     166            1042 : ForkAndExec(
     167                 :     const char *path,
     168                 :     char *const *argv,
     169                 :     char *const *envp,
     170                 :     const PRProcessAttr *attr)
     171                 : {
     172                 :     PRProcess *process;
     173                 :     int nEnv, idx;
     174                 :     char *const *childEnvp;
     175            1042 :     char **newEnvp = NULL;
     176                 :     int flags;
     177                 : 
     178            1042 :     process = PR_NEW(PRProcess);
     179            1042 :     if (!process) {
     180               0 :         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
     181               0 :         return NULL;
     182                 :     }
     183                 : 
     184            1042 :     childEnvp = envp;
     185            1042 :     if (attr && attr->fdInheritBuffer) {
     186               0 :         PRBool found = PR_FALSE;
     187                 : 
     188               0 :         if (NULL == childEnvp) {
     189                 : #ifdef DARWIN
     190                 : #ifdef HAVE_CRT_EXTERNS_H
     191                 :             childEnvp = *(_NSGetEnviron());
     192                 : #else
     193                 :             /* _NSGetEnviron() is not available on iOS. */
     194                 :             PR_DELETE(process);
     195                 :             PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
     196                 :             return NULL;
     197                 : #endif
     198                 : #else
     199               0 :             childEnvp = environ;
     200                 : #endif
     201                 :         }
     202                 : 
     203               0 :         for (nEnv = 0; childEnvp[nEnv]; nEnv++) {
     204                 :         }
     205               0 :         newEnvp = (char **) PR_MALLOC((nEnv + 2) * sizeof(char *));
     206               0 :         if (NULL == newEnvp) {
     207               0 :             PR_DELETE(process);
     208               0 :             PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
     209               0 :             return NULL;
     210                 :         }
     211               0 :         for (idx = 0; idx < nEnv; idx++) {
     212               0 :             newEnvp[idx] = childEnvp[idx];
     213               0 :             if (!found && !strncmp(newEnvp[idx], "NSPR_INHERIT_FDS=", 17)) {
     214               0 :                 newEnvp[idx] = attr->fdInheritBuffer;
     215               0 :                 found = PR_TRUE;
     216                 :             }
     217                 :         }
     218               0 :         if (!found) {
     219               0 :             newEnvp[idx++] = attr->fdInheritBuffer;
     220                 :         }
     221               0 :         newEnvp[idx] = NULL;
     222               0 :         childEnvp = newEnvp;
     223                 :     }
     224                 : 
     225                 : #ifdef AIX
     226                 :     process->md.pid = (*pr_wp.forkptr)();
     227                 : #elif defined(NTO) || defined(SYMBIAN)
     228                 :     /*
     229                 :      * fork() & exec() does not work in a multithreaded process.
     230                 :      * Use spawn() instead.
     231                 :      */
     232                 :     {
     233                 :         int fd_map[3] = { 0, 1, 2 };
     234                 : 
     235                 :         if (attr) {
     236                 :             if (attr->stdinFd && attr->stdinFd->secret->md.osfd != 0) {
     237                 :                 fd_map[0] = dup(attr->stdinFd->secret->md.osfd);
     238                 :                 flags = fcntl(fd_map[0], F_GETFL, 0);
     239                 :                 if (flags & O_NONBLOCK)
     240                 :                     fcntl(fd_map[0], F_SETFL, flags & ~O_NONBLOCK);
     241                 :             }
     242                 :             if (attr->stdoutFd && attr->stdoutFd->secret->md.osfd != 1) {
     243                 :                 fd_map[1] = dup(attr->stdoutFd->secret->md.osfd);
     244                 :                 flags = fcntl(fd_map[1], F_GETFL, 0);
     245                 :                 if (flags & O_NONBLOCK)
     246                 :                     fcntl(fd_map[1], F_SETFL, flags & ~O_NONBLOCK);
     247                 :             }
     248                 :             if (attr->stderrFd && attr->stderrFd->secret->md.osfd != 2) {
     249                 :                 fd_map[2] = dup(attr->stderrFd->secret->md.osfd);
     250                 :                 flags = fcntl(fd_map[2], F_GETFL, 0);
     251                 :                 if (flags & O_NONBLOCK)
     252                 :                     fcntl(fd_map[2], F_SETFL, flags & ~O_NONBLOCK);
     253                 :             }
     254                 : 
     255                 :             PR_ASSERT(attr->currentDirectory == NULL);  /* not implemented */
     256                 :         }
     257                 : 
     258                 : #ifdef SYMBIAN
     259                 :         /* In Symbian OS, we use posix_spawn instead of fork() and exec() */
     260                 :         posix_spawn(&(process->md.pid), path, NULL, NULL, argv, childEnvp);
     261                 : #else
     262                 :         process->md.pid = spawn(path, 3, fd_map, NULL, argv, childEnvp);
     263                 : #endif
     264                 : 
     265                 :         if (fd_map[0] != 0)
     266                 :             close(fd_map[0]);
     267                 :         if (fd_map[1] != 1)
     268                 :             close(fd_map[1]);
     269                 :         if (fd_map[2] != 2)
     270                 :             close(fd_map[2]);
     271                 :     }
     272                 : #else
     273            1042 :     process->md.pid = fork();
     274                 : #endif
     275            2009 :     if ((pid_t) -1 == process->md.pid) {
     276               0 :         PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, errno);
     277               0 :         PR_DELETE(process);
     278               0 :         if (newEnvp) {
     279               0 :             PR_DELETE(newEnvp);
     280                 :         }
     281               0 :         return NULL;
     282            2009 :     } else if (0 == process->md.pid) {  /* the child process */
     283                 :         /*
     284                 :          * If the child process needs to exit, it must call _exit().
     285                 :          * Do not call exit(), because exit() will flush and close
     286                 :          * the standard I/O file descriptors, and hence corrupt
     287                 :          * the parent process's standard I/O data structures.
     288                 :          */
     289                 : 
     290                 : #if !defined(NTO) && !defined(SYMBIAN)
     291             967 :         if (attr) {
     292                 :             /* the osfd's to redirect stdin, stdout, and stderr to */
     293              12 :             int in_osfd = -1, out_osfd = -1, err_osfd = -1;
     294                 : 
     295              12 :             if (attr->stdinFd
     296              12 :                     && attr->stdinFd->secret->md.osfd != 0) {
     297              12 :                 in_osfd = attr->stdinFd->secret->md.osfd;
     298              12 :                 if (dup2(in_osfd, 0) != 0) {
     299               0 :                     _exit(1);  /* failed */
     300                 :                 }
     301              12 :                 flags = fcntl(0, F_GETFL, 0);
     302              12 :                 if (flags & O_NONBLOCK) {
     303              12 :                     fcntl(0, F_SETFL, flags & ~O_NONBLOCK);
     304                 :                 }
     305                 :             }
     306               0 :             if (attr->stdoutFd
     307              12 :                     && attr->stdoutFd->secret->md.osfd != 1) {
     308              12 :                 out_osfd = attr->stdoutFd->secret->md.osfd;
     309              12 :                 if (dup2(out_osfd, 1) != 1) {
     310               0 :                     _exit(1);  /* failed */
     311                 :                 }
     312              12 :                 flags = fcntl(1, F_GETFL, 0);
     313              12 :                 if (flags & O_NONBLOCK) {
     314              12 :                     fcntl(1, F_SETFL, flags & ~O_NONBLOCK);
     315                 :                 }
     316                 :             }
     317               0 :             if (attr->stderrFd
     318              12 :                     && attr->stderrFd->secret->md.osfd != 2) {
     319              12 :                 err_osfd = attr->stderrFd->secret->md.osfd;
     320              12 :                 if (dup2(err_osfd, 2) != 2) {
     321               0 :                     _exit(1);  /* failed */
     322                 :                 }
     323              12 :                 flags = fcntl(2, F_GETFL, 0);
     324              12 :                 if (flags & O_NONBLOCK) {
     325              12 :                     fcntl(2, F_SETFL, flags & ~O_NONBLOCK);
     326                 :                 }
     327                 :             }
     328               0 :             if (in_osfd != -1) {
     329              12 :                 close(in_osfd);
     330                 :             }
     331               0 :             if (out_osfd != -1 && out_osfd != in_osfd) {
     332              12 :                 close(out_osfd);
     333                 :             }
     334               0 :             if (err_osfd != -1 && err_osfd != in_osfd
     335              12 :                     && err_osfd != out_osfd) {
     336              12 :                 close(err_osfd);
     337                 :             }
     338               0 :             if (attr->currentDirectory) {
     339               0 :                 if (chdir(attr->currentDirectory) < 0) {
     340               0 :                     _exit(1);  /* failed */
     341                 :                 }
     342                 :             }
     343                 :         }
     344                 : 
     345               0 :         if (childEnvp) {
     346               0 :             (void)execve(path, argv, childEnvp);
     347                 :         } else {
     348                 :             /* Inherit the environment of the parent. */
     349               0 :             (void)execv(path, argv);
     350                 :         }
     351                 :         /* Whoops! It returned. That's a bad sign. */
     352               0 :         _exit(1);
     353                 : #endif /* !NTO */
     354                 :     }
     355                 : 
     356            1042 :     if (newEnvp) {
     357               0 :         PR_DELETE(newEnvp);
     358                 :     }
     359                 : 
     360                 : #if defined(_PR_NATIVE_THREADS)
     361                 :     PR_Lock(pr_wp.ml);
     362                 :     if (0 == pr_wp.numProcs++) {
     363                 :         PR_NotifyCondVar(pr_wp.cv);
     364                 :     }
     365                 :     PR_Unlock(pr_wp.ml);
     366                 : #endif
     367            1042 :     return process;
     368                 : }
     369                 : 
     370                 : #ifdef _PR_SHARE_CLONES
     371                 : 
     372                 : struct pr_CreateProcOp {
     373                 :     const char *path;
     374                 :     char *const *argv;
     375                 :     char *const *envp;
     376                 :     const PRProcessAttr *attr;
     377                 :     PRProcess *process;
     378                 :     PRErrorCode prerror;
     379                 :     PRInt32 oserror;
     380                 :     PRBool done;
     381                 :     PRCondVar *doneCV;
     382                 :     struct pr_CreateProcOp *next;
     383                 : };
     384                 : 
     385                 : PRProcess *
     386            1042 : _MD_CreateUnixProcess(
     387                 :     const char *path,
     388                 :     char *const *argv,
     389                 :     char *const *envp,
     390                 :     const PRProcessAttr *attr)
     391                 : {
     392                 :     struct pr_CreateProcOp *op;
     393                 :     PRProcess *proc;
     394                 :     int rv;
     395                 : 
     396            1042 :     if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) {
     397               0 :         return NULL;
     398                 :     }
     399                 : 
     400            1042 :     op = PR_NEW(struct pr_CreateProcOp);
     401            1042 :     if (NULL == op) {
     402               0 :         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
     403               0 :         return NULL;
     404                 :     }
     405            1042 :     op->path = path;
     406            1042 :     op->argv = argv;
     407            1042 :     op->envp = envp;
     408            1042 :     op->attr = attr;
     409            1042 :     op->done = PR_FALSE;
     410            1042 :     op->doneCV = PR_NewCondVar(pr_wp.ml);
     411            1042 :     if (NULL == op->doneCV) {
     412               0 :         PR_DELETE(op);
     413               0 :         return NULL;
     414                 :     }
     415            1042 :     PR_Lock(pr_wp.ml);
     416                 : 
     417                 :     /* add to the tail of op queue */
     418            1042 :     op->next = NULL;
     419            1042 :     if (pr_wp.opTail) {
     420               0 :         pr_wp.opTail->next = op;
     421               0 :         pr_wp.opTail = op;
     422                 :     } else {
     423            1042 :         PR_ASSERT(NULL == pr_wp.opHead);
     424            1042 :         pr_wp.opHead = pr_wp.opTail = op;
     425                 :     }
     426                 : 
     427                 :     /* wake up the daemon thread */
     428                 :     do {
     429            1042 :         rv = write(pr_wp.pipefd[1], "", 1);
     430            1042 :     } while (-1 == rv && EINTR == errno);
     431                 : 
     432            3126 :     while (op->done == PR_FALSE) {
     433            1042 :         PR_WaitCondVar(op->doneCV, PR_INTERVAL_NO_TIMEOUT);
     434                 :     }
     435            1042 :     PR_Unlock(pr_wp.ml);
     436            1042 :     PR_DestroyCondVar(op->doneCV);
     437            1042 :     proc = op->process;
     438            1042 :     if (!proc) {
     439               0 :         PR_SetError(op->prerror, op->oserror);
     440                 :     }
     441            1042 :     PR_DELETE(op);
     442            1042 :     return proc;
     443                 : }
     444                 : 
     445                 : #else  /* ! _PR_SHARE_CLONES */
     446                 : 
     447                 : PRProcess *
     448                 : _MD_CreateUnixProcess(
     449                 :     const char *path,
     450                 :     char *const *argv,
     451                 :     char *const *envp,
     452                 :     const PRProcessAttr *attr)
     453                 : {
     454                 :     if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) {
     455                 :         return NULL;
     456                 :     }
     457                 :     return ForkAndExec(path, argv, envp, attr);
     458                 : }  /* _MD_CreateUnixProcess */
     459                 : 
     460                 : #endif  /* _PR_SHARE_CLONES */
     461                 : 
     462                 : /*
     463                 :  * The pid table is a hashtable.
     464                 :  *
     465                 :  * The number of buckets in the hashtable (NBUCKETS) must be a power of 2.
     466                 :  */
     467                 : #define NBUCKETS_LOG2 6
     468                 : #define NBUCKETS (1 << NBUCKETS_LOG2)
     469                 : #define PID_HASH_MASK ((pid_t) (NBUCKETS - 1))
     470                 : 
     471                 : static pr_PidRecord *
     472            2084 : FindPidTable(pid_t pid)
     473                 : {
     474                 :     pr_PidRecord *pRec;
     475            2084 :     int keyHash = (int) (pid & PID_HASH_MASK);
     476                 : 
     477            2084 :     pRec =  pr_wp.pidTable[keyHash];
     478            4168 :     while (pRec) {
     479            1042 :         if (pRec->pid == pid) {
     480            1042 :             break;
     481                 :         }
     482               0 :         pRec = pRec->next;
     483                 :     }
     484            2084 :     return pRec;
     485                 : }
     486                 : 
     487                 : static void
     488            1042 : InsertPidTable(pr_PidRecord *pRec)
     489                 : {
     490            1042 :     int keyHash = (int) (pRec->pid & PID_HASH_MASK);
     491                 : 
     492            1042 :     pRec->next = pr_wp.pidTable[keyHash];
     493            1042 :     pr_wp.pidTable[keyHash] = pRec;
     494            1042 : }
     495                 : 
     496                 : static void
     497            1042 : DeletePidTable(pr_PidRecord *pRec)
     498                 : {
     499            1042 :     int keyHash = (int) (pRec->pid & PID_HASH_MASK);
     500                 : 
     501            1042 :     if (pr_wp.pidTable[keyHash] == pRec) {
     502            1042 :         pr_wp.pidTable[keyHash] = pRec->next;
     503                 :     } else {
     504                 :         pr_PidRecord *pred, *cur;  /* predecessor and current */
     505                 : 
     506               0 :         pred = pr_wp.pidTable[keyHash];
     507               0 :         cur = pred->next;
     508               0 :         while (cur) {
     509               0 :             if (cur == pRec) {
     510               0 :                 pred->next = cur->next;
     511               0 :                 break;
     512                 :             }
     513               0 :             pred = cur;
     514               0 :             cur = cur->next;
     515                 :         }
     516               0 :         PR_ASSERT(cur != NULL);
     517                 :     }
     518            1042 : }
     519                 : 
     520                 : static int
     521            1042 : ExtractExitStatus(int rawExitStatus)
     522                 : {
     523                 :     /*
     524                 :      * We did not specify the WCONTINUED and WUNTRACED options
     525                 :      * for waitpid, so these two events should not be reported.
     526                 :      */
     527            1042 :     PR_ASSERT(!WIFSTOPPED(rawExitStatus));
     528                 : #ifdef WIFCONTINUED
     529            1042 :     PR_ASSERT(!WIFCONTINUED(rawExitStatus));
     530                 : #endif
     531            1042 :     if (WIFEXITED(rawExitStatus)) {
     532              29 :         return WEXITSTATUS(rawExitStatus);
     533                 :     } else {
     534            1013 :         PR_ASSERT(WIFSIGNALED(rawExitStatus));
     535            1013 :         return _PR_SIGNALED_EXITSTATUS;
     536                 :     }
     537                 : }
     538                 : 
     539                 : static void
     540            1042 : ProcessReapedChildInternal(pid_t pid, int status)
     541                 : {
     542                 :     pr_PidRecord *pRec;
     543                 : 
     544            1042 :     pRec = FindPidTable(pid);
     545            1042 :     if (NULL == pRec) {
     546               4 :         pRec = PR_NEW(pr_PidRecord);
     547               4 :         pRec->pid = pid;
     548               4 :         pRec->state = _PR_PID_REAPED;
     549               4 :         pRec->exitStatus = ExtractExitStatus(status);
     550               4 :         pRec->reapedCV = NULL;
     551               4 :         InsertPidTable(pRec);
     552                 :     } else {
     553            1038 :         PR_ASSERT(pRec->state != _PR_PID_REAPED);
     554            1038 :         if (_PR_PID_DETACHED == pRec->state) {
     555               0 :             PR_ASSERT(NULL == pRec->reapedCV);
     556               0 :             DeletePidTable(pRec);
     557               0 :             PR_DELETE(pRec);
     558                 :         } else {
     559            1038 :             PR_ASSERT(_PR_PID_WAITING == pRec->state);
     560            1038 :             PR_ASSERT(NULL != pRec->reapedCV);
     561            1038 :             pRec->exitStatus = ExtractExitStatus(status);
     562            1038 :             pRec->state = _PR_PID_REAPED;
     563            1038 :             PR_NotifyCondVar(pRec->reapedCV);
     564                 :         }
     565                 :     }
     566            1042 : }
     567                 : 
     568                 : #if defined(_PR_NATIVE_THREADS)
     569                 : 
     570                 : /*
     571                 :  * If all the threads are native threads, the daemon thread is
     572                 :  * simpler.  We don't need to catch the SIGCHLD signal.  We can
     573                 :  * just have the daemon thread block in waitpid().
     574                 :  */
     575                 : 
     576                 : static void WaitPidDaemonThread(void *unused)
     577                 : {
     578                 :     pid_t pid;
     579                 :     int status;
     580                 : 
     581                 :     while (1) {
     582                 :         PR_Lock(pr_wp.ml);
     583                 :         while (0 == pr_wp.numProcs) {
     584                 :             PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT);
     585                 :         }
     586                 :         PR_Unlock(pr_wp.ml);
     587                 : 
     588                 :         while (1) {
     589                 :             do {
     590                 :                 pid = waitpid((pid_t) -1, &status, 0);
     591                 :             } while ((pid_t) -1 == pid && EINTR == errno);
     592                 : 
     593                 :             /*
     594                 :              * waitpid() cannot return 0 because we did not invoke it
     595                 :              * with the WNOHANG option.
     596                 :              */ 
     597                 :             PR_ASSERT(0 != pid);
     598                 : 
     599                 :             /*
     600                 :              * The only possible error code is ECHILD.  But if we do
     601                 :              * our accounting correctly, we should only call waitpid()
     602                 :              * when there is a child process to wait for.
     603                 :              */
     604                 :             PR_ASSERT((pid_t) -1 != pid);
     605                 :             if ((pid_t) -1 == pid) {
     606                 :                 break;
     607                 :             }
     608                 : 
     609                 :             PR_Lock(pr_wp.ml);
     610                 :             ProcessReapedChildInternal(pid, status);
     611                 :             pr_wp.numProcs--;
     612                 :             while (0 == pr_wp.numProcs) {
     613                 :                 PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT);
     614                 :             }
     615                 :             PR_Unlock(pr_wp.ml);
     616                 :         }
     617                 :     }
     618                 : }
     619                 : 
     620                 : #else /* _PR_NATIVE_THREADS */
     621                 : 
     622              13 : static void WaitPidDaemonThread(void *unused)
     623                 : {
     624                 :     PRPollDesc pd;
     625                 :     PRFileDesc *fd;
     626                 :     int rv;
     627                 :     char buf[128];
     628                 :     pid_t pid;
     629                 :     int status;
     630                 : #ifdef _PR_SHARE_CLONES
     631                 :     struct pr_CreateProcOp *op;
     632                 : #endif
     633                 : 
     634                 : #ifdef _PR_SHARE_CLONES
     635              13 :     pr_InstallSigchldHandler();
     636                 : #endif
     637                 : 
     638              13 :     fd = PR_ImportFile(pr_wp.pipefd[0]);
     639              13 :     PR_ASSERT(NULL != fd);
     640              13 :     pd.fd = fd;
     641              13 :     pd.in_flags = PR_POLL_READ;
     642                 : 
     643                 :     while (1) {
     644            1992 :         rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
     645            1979 :         PR_ASSERT(1 == rv);
     646                 : 
     647                 : #ifdef _PR_SHARE_CLONES
     648            1979 :         if (pr_waitpid_daemon_exit) {
     649                 :             return;
     650                 :         }
     651            1979 :         PR_Lock(pr_wp.ml);
     652                 : #endif
     653                 :             
     654                 :         do {
     655            1979 :             rv = read(pr_wp.pipefd[0], buf, sizeof(buf));
     656            1979 :         } while (sizeof(buf) == rv || (-1 == rv && EINTR == errno));
     657                 : 
     658                 : #ifdef _PR_SHARE_CLONES
     659            1979 :         PR_Unlock(pr_wp.ml);
     660            5000 :         while ((op = pr_wp.opHead) != NULL) {
     661            1042 :             op->process = ForkAndExec(op->path, op->argv,
     662                 :                     op->envp, op->attr);
     663            1042 :             if (NULL == op->process) {
     664               0 :                 op->prerror = PR_GetError();
     665               0 :                 op->oserror = PR_GetOSError();
     666                 :             }
     667            1042 :             PR_Lock(pr_wp.ml);
     668            1042 :             pr_wp.opHead = op->next;
     669            1042 :             if (NULL == pr_wp.opHead) {
     670            1042 :                 pr_wp.opTail = NULL;
     671                 :             }
     672            1042 :             op->done = PR_TRUE;
     673            1042 :             PR_NotifyCondVar(op->doneCV);
     674            1042 :             PR_Unlock(pr_wp.ml);
     675                 :         }
     676                 : #endif
     677                 : 
     678                 :         while (1) {
     679                 :             do {
     680            3021 :                 pid = waitpid((pid_t) -1, &status, WNOHANG);
     681            3021 :             } while ((pid_t) -1 == pid && EINTR == errno);
     682            3021 :             if (0 == pid) break;
     683            2093 :             if ((pid_t) -1 == pid) {
     684                 :                 /* must be because we have no child processes */
     685            1051 :                 PR_ASSERT(ECHILD == errno);
     686            1051 :                 break;
     687                 :             }
     688                 : 
     689            1042 :             PR_Lock(pr_wp.ml);
     690            1042 :             ProcessReapedChildInternal(pid, status);
     691            1042 :             PR_Unlock(pr_wp.ml);
     692            1042 :         }
     693            1979 :     }
     694                 : }
     695                 : 
     696            1042 : static void pr_SigchldHandler(int sig)
     697                 : {
     698                 :     int errnoCopy;
     699                 :     int rv;
     700                 : 
     701            1042 :     errnoCopy = errno;
     702                 : 
     703                 :     do {
     704            1042 :         rv = write(pr_wp.pipefd[1], "", 1);
     705            1042 :     } while (-1 == rv && EINTR == errno);
     706                 : 
     707                 : #ifdef DEBUG
     708            1042 :     if (-1 == rv && EAGAIN != errno && EWOULDBLOCK != errno) {
     709               0 :         char *msg = "cannot write to pipe\n";
     710               0 :         write(2, msg, strlen(msg) + 1);
     711               0 :         _exit(1);
     712                 :     }
     713                 : #endif
     714                 : 
     715            1042 :     errno = errnoCopy;
     716            1042 : }
     717                 : 
     718              13 : static void pr_InstallSigchldHandler()
     719                 : {
     720                 : #if defined(HPUX) && defined(_PR_DCETHREADS)
     721                 : #error "HP-UX DCE threads have their own SIGCHLD handler"
     722                 : #endif
     723                 : 
     724                 :     struct sigaction act, oact;
     725                 :     int rv;
     726                 : 
     727              13 :     act.sa_handler = pr_SigchldHandler;
     728              13 :     sigemptyset(&act.sa_mask);
     729              13 :     act.sa_flags = SA_NOCLDSTOP | SA_RESTART;
     730              13 :     rv = sigaction(SIGCHLD, &act, &oact);
     731              13 :     PR_ASSERT(0 == rv);
     732                 :     /* Make sure we are not overriding someone else's SIGCHLD handler */
     733                 : #ifndef _PR_SHARE_CLONES
     734                 :     PR_ASSERT(oact.sa_handler == SIG_DFL);
     735                 : #endif
     736              13 : }
     737                 : 
     738                 : #endif  /* !defined(_PR_NATIVE_THREADS) */
     739                 : 
     740              13 : static PRStatus _MD_InitProcesses(void)
     741                 : {
     742                 : #if !defined(_PR_NATIVE_THREADS)
     743                 :     int rv;
     744                 :     int flags;
     745                 : #endif
     746                 : #ifdef SUNOS4
     747                 : #define _PR_NBIO_FLAG FNDELAY
     748                 : #else
     749                 : #define _PR_NBIO_FLAG O_NONBLOCK
     750                 : #endif
     751                 : 
     752                 : #ifdef AIX
     753                 :     {
     754                 :         void *handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
     755                 :         pr_wp.forkptr = (pid_t (*)(void)) dlsym(handle, "f_fork");
     756                 :         if (!pr_wp.forkptr) {
     757                 :             pr_wp.forkptr = fork;
     758                 :         }
     759                 :         dlclose(handle);
     760                 :     }
     761                 : #endif /* AIX */
     762                 : 
     763              13 :     pr_wp.ml = PR_NewLock();
     764              13 :     PR_ASSERT(NULL != pr_wp.ml);
     765                 : 
     766                 : #if defined(_PR_NATIVE_THREADS)
     767                 :     pr_wp.numProcs = 0;
     768                 :     pr_wp.cv = PR_NewCondVar(pr_wp.ml);
     769                 :     PR_ASSERT(NULL != pr_wp.cv);
     770                 : #else
     771              13 :     rv = pipe(pr_wp.pipefd);
     772              13 :     PR_ASSERT(0 == rv);
     773              13 :     flags = fcntl(pr_wp.pipefd[0], F_GETFL, 0);
     774              13 :     fcntl(pr_wp.pipefd[0], F_SETFL, flags | _PR_NBIO_FLAG);
     775              13 :     flags = fcntl(pr_wp.pipefd[1], F_GETFL, 0);
     776              13 :     fcntl(pr_wp.pipefd[1], F_SETFL, flags | _PR_NBIO_FLAG);
     777                 : 
     778                 : #ifndef _PR_SHARE_CLONES
     779                 :     pr_InstallSigchldHandler();
     780                 : #endif
     781                 : #endif  /* !_PR_NATIVE_THREADS */
     782                 : 
     783              13 :     pr_wp.thread = PR_CreateThread(PR_SYSTEM_THREAD,
     784                 :             WaitPidDaemonThread, NULL, PR_PRIORITY_NORMAL,
     785                 : #ifdef _PR_SHARE_CLONES
     786                 :             PR_GLOBAL_THREAD,
     787                 : #else
     788                 :             PR_LOCAL_THREAD,
     789                 : #endif
     790                 :             PR_JOINABLE_THREAD, 0);
     791              13 :     PR_ASSERT(NULL != pr_wp.thread);
     792                 : 
     793              13 :     pr_wp.pidTable = (pr_PidRecord**)PR_CALLOC(NBUCKETS * sizeof(pr_PidRecord *));
     794              13 :     PR_ASSERT(NULL != pr_wp.pidTable);
     795              13 :     return PR_SUCCESS;
     796                 : }
     797                 : 
     798               0 : PRStatus _MD_DetachUnixProcess(PRProcess *process)
     799                 : {
     800               0 :     PRStatus retVal = PR_SUCCESS;
     801                 :     pr_PidRecord *pRec;
     802                 : 
     803               0 :     PR_Lock(pr_wp.ml);
     804               0 :     pRec = FindPidTable(process->md.pid);
     805               0 :     if (NULL == pRec) {
     806               0 :         pRec = PR_NEW(pr_PidRecord);
     807               0 :         if (NULL == pRec) {
     808               0 :             PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
     809               0 :             retVal = PR_FAILURE;
     810               0 :             goto done;
     811                 :         }
     812               0 :         pRec->pid = process->md.pid;
     813               0 :         pRec->state = _PR_PID_DETACHED;
     814               0 :         pRec->reapedCV = NULL;
     815               0 :         InsertPidTable(pRec);
     816                 :     } else {
     817               0 :         PR_ASSERT(_PR_PID_REAPED == pRec->state);
     818               0 :         if (_PR_PID_REAPED != pRec->state) {
     819               0 :             PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
     820               0 :             retVal = PR_FAILURE;
     821                 :         } else {
     822               0 :             DeletePidTable(pRec);
     823               0 :             PR_ASSERT(NULL == pRec->reapedCV);
     824               0 :             PR_DELETE(pRec);
     825                 :         }
     826                 :     }
     827               0 :     PR_DELETE(process);
     828                 : 
     829                 : done:
     830               0 :     PR_Unlock(pr_wp.ml);
     831               0 :     return retVal;
     832                 : }
     833                 : 
     834            1042 : PRStatus _MD_WaitUnixProcess(
     835                 :     PRProcess *process,
     836                 :     PRInt32 *exitCode)
     837                 : {
     838                 :     pr_PidRecord *pRec;
     839            1042 :     PRStatus retVal = PR_SUCCESS;
     840            1042 :     PRBool interrupted = PR_FALSE;
     841                 : 
     842            1042 :     PR_Lock(pr_wp.ml);
     843            1042 :     pRec = FindPidTable(process->md.pid);
     844            1042 :     if (NULL == pRec) {
     845            1038 :         pRec = PR_NEW(pr_PidRecord);
     846            1038 :         if (NULL == pRec) {
     847               0 :             PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
     848               0 :             retVal = PR_FAILURE;
     849               0 :             goto done;
     850                 :         }
     851            1038 :         pRec->pid = process->md.pid;
     852            1038 :         pRec->state = _PR_PID_WAITING;
     853            1038 :         pRec->reapedCV = PR_NewCondVar(pr_wp.ml);
     854            1038 :         if (NULL == pRec->reapedCV) {
     855               0 :             PR_DELETE(pRec);
     856               0 :             retVal = PR_FAILURE;
     857               0 :             goto done;
     858                 :         }
     859            1038 :         InsertPidTable(pRec);
     860            3114 :         while (!interrupted && _PR_PID_REAPED != pRec->state) {
     861            1038 :             if (PR_WaitCondVar(pRec->reapedCV,
     862                 :                     PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE
     863               0 :                     && PR_GetError() == PR_PENDING_INTERRUPT_ERROR) {
     864               0 :                 interrupted = PR_TRUE;
     865                 :             }
     866                 :         }
     867            1038 :         if (_PR_PID_REAPED == pRec->state) {
     868            1038 :             if (exitCode) {
     869            1038 :                 *exitCode = pRec->exitStatus;
     870                 :             }
     871                 :         } else {
     872               0 :             PR_ASSERT(interrupted);
     873               0 :             retVal = PR_FAILURE;
     874                 :         }
     875            1038 :         DeletePidTable(pRec);
     876            1038 :         PR_DestroyCondVar(pRec->reapedCV);
     877            1038 :         PR_DELETE(pRec);
     878                 :     } else {
     879               4 :         PR_ASSERT(_PR_PID_REAPED == pRec->state);
     880               4 :         PR_ASSERT(NULL == pRec->reapedCV);
     881               4 :         DeletePidTable(pRec);
     882               4 :         if (exitCode) {
     883               4 :             *exitCode = pRec->exitStatus;
     884                 :         }
     885               4 :         PR_DELETE(pRec);
     886                 :     }
     887            1042 :     PR_DELETE(process);
     888                 : 
     889                 : done:
     890            1042 :     PR_Unlock(pr_wp.ml);
     891            1042 :     return retVal;
     892                 : }  /* _MD_WaitUnixProcess */
     893                 : 
     894            1003 : PRStatus _MD_KillUnixProcess(PRProcess *process)
     895                 : {
     896                 :     PRErrorCode prerror;
     897                 :     PRInt32 oserror;
     898                 : 
     899                 : #ifdef SYMBIAN
     900                 :     /* In Symbian OS, we can not kill other process with Open C */
     901                 :     PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, oserror);
     902                 :     return PR_FAILURE;
     903                 : #else
     904            1003 :     if (kill(process->md.pid, SIGKILL) == 0) {
     905            1003 :         return PR_SUCCESS;
     906                 :     }
     907               0 :     oserror = errno;
     908               0 :     switch (oserror) {
     909                 :         case EPERM:
     910               0 :             prerror = PR_NO_ACCESS_RIGHTS_ERROR;
     911               0 :             break;
     912                 :         case ESRCH:
     913               0 :             prerror = PR_INVALID_ARGUMENT_ERROR;
     914               0 :             break;
     915                 :         default:
     916               0 :             prerror = PR_UNKNOWN_ERROR;
     917               0 :             break;
     918                 :     }
     919               0 :     PR_SetError(prerror, oserror);
     920               0 :     return PR_FAILURE;
     921                 : #endif
     922                 : }  /* _MD_KillUnixProcess */

Generated by: LCOV version 1.7