📄 stafprocess.cpp
字号:
/*****************************************************************************//* Software Testing Automation Framework (STAF) *//* (C) Copyright IBM Corp. 2001 *//* *//* This software is licensed under the Common Public License (CPL) V1.0. *//*****************************************************************************/#include "STAF.h"#include "STAF_iostream.h"#include <sys/types.h>#include <unistd.h>#include <sys/stat.h>#include <sys/wait.h>#include <fcntl.h>#include <signal.h>#include <ctype.h>#include <deque>#include <map>#include <list>#include "STAFProcess.h"#include "STAFMutexSem.h"#include "STAFEventSem.h"#include "STAFUtil.h"#include "STAFUtilUnix.h"#include "STAFThreadManager.h"#include "STAFTrace.h"#include "STAFInternalProcess.h"#ifdef STAF_OS_NAME_ZOS#include <spawn.h>extern char **environ;#endifextern "C"{// XXX: What's up with these #define's?#ifndef _XOPEN_SOURCE#define _XOPEN_SOURCE#endif // _XOPEN_SOURCE#include <pwd.h>}#define MONITOR_SLEEP_SECONDS 1// Static data used to start processstruct ProcessCreateInfo{ ProcessCreateInfo(STAFProcessCommandType_t theCommandType = kSTAFProcessCommand, const STAFStringBufferPtr &cmd = STAFStringBufferPtr(), char **arg = 0, char **env = 0, STAFUserID_t UID = getuid(), STAFGroupID_t GID = getgid(), const STAFStringBufferPtr &username = STAFStringBufferPtr(), const STAFStringBufferPtr &dir = STAFStringBufferPtr(), STAFProcessRedirectedIOMode_t theStdinMode = kSTAFProcessIONoRedirect, int stdinFD = dup(0), STAFProcessRedirectedIOMode_t theStdoutMode = kSTAFProcessIONoRedirect, int stdoutFD = dup(1), STAFProcessRedirectedIOMode_t theStderrMode = kSTAFProcessIONoRedirect, int stderrFD = dup(2), STAFProcessEndCallbackLevel1 cback = STAFProcessEndCallbackLevel1()) : commandType(theCommandType), command(cmd), argv(arg), envp(env), workdir(dir), uid(UID), gid(GID), userName(username), stdinMode(theStdinMode), child_stdin(stdinFD), stdoutMode(theStdoutMode), child_stdout(stdoutFD), stderrMode(theStderrMode), child_stderr(stderrFD), callback(cback) { /* Do nothing */ } int pid; STAFUserID_t uid; STAFGroupID_t gid; STAFStringBufferPtr userName; char **argv; char **envp; STAFProcessCommandType_t commandType; STAFStringBufferPtr command; STAFStringBufferPtr workdir; STAFProcessRedirectedIOMode_t stdinMode; int child_stdin; STAFProcessRedirectedIOMode_t stdoutMode; int child_stdout; STAFProcessRedirectedIOMode_t stderrMode; int child_stderr; STAFProcessEndCallbackLevel1 callback;};// Data passed to the callback threadstruct ProcessMonitorCallbackInfo{ ProcessMonitorCallbackInfo(STAFProcessEndCallbackLevel1 theCallback, STAFProcessID_t thePID, STAFProcessHandle_t theHandle, unsigned int theRC) : callback(theCallback), pid(thePID), handle(theHandle), rc(theRC) { /* Do Nothing */ } STAFProcessEndCallbackLevel1 callback; STAFProcessID_t pid; STAFProcessHandle_t handle; unsigned int rc;};struct ProcessMonitorInfo{ ProcessMonitorInfo (STAFProcessHandle_t aHandle = 0, STAFProcessID_t aPID = 0, STAFProcessEndCallbackLevel1 aCallback = STAFProcessEndCallbackLevel1()) : handle(aHandle), pid(aPID), callback(aCallback) { /* Do nothing */ } STAFProcessHandle_t handle; STAFProcessID_t pid; STAFProcessEndCallbackLevel1 callback;};typedef std::deque<ProcessMonitorInfo> ProcessMonitorList;typedef std::map<STAFProcessID_t, ProcessMonitorList> ProcessMonitorMap;// Perform OS Specific User Authenticationstatic unsigned int UserAuthenticate(STAFUserID_t &uid, STAFGroupID_t &gid, const STAFString &username, const STAFString &password, bool mustValidate, unsigned int *osRC);static unsigned int ProcessMonitorCallbackThread(void *data);static unsigned int ProcessMonitorThread(void *);static int ParseCommandParms(STAFString &, char ***);static ProcessCreateInfo sProcessCreateInfo;static STAFMutexSem sMonitorDataSem;static ProcessMonitorMap sMonitorMap; static STAFUserID_t sOurUID;static STAFGroupID_t sOurGID;static STAFMutexSem sCreateProcessSem;static STAFEventSem sProcessCreated;static STAFEventSem sProcessThread;// private prototypes for user authentication functionsstatic unsigned int sAuthNone(const char *, const char *);static unsigned int sAuthPam(const char *, const char *);// private function pointer must be initialized to whatever fAuthMode isstatic unsigned int (*sAuthenticateFuncPtr)(const char *username, const char *password) = sAuthNone;// Substitution characters valid for a shell command on Unixchar *gSTAFProcessShellSubstitutionChars = "cCpPtTuUwWxX%";static STAFThreadManager &getProcessThreadManager(){ static STAFThreadManager theThreadManager(1); return theThreadManager;}// XXX: This function needs to be called in a number of different places.// Check in windows version also.static void InitProcessManager(){ static STAFMutexSem initSem; static bool alreadyInited = false; if (alreadyInited) return; STAFMutexSemLock initLock(initSem); if (alreadyInited) return; sOurUID = getuid(); sOurGID = getgid(); getProcessThreadManager().dispatch(ProcessMonitorThread, 0); alreadyInited = true;}unsigned int ProcessMonitorCallbackThread(void *data){ ProcessMonitorCallbackInfo *pInfo = static_cast<ProcessMonitorCallbackInfo *>(data); pInfo->callback.callback(pInfo->pid, pInfo->handle, pInfo->rc, pInfo->callback.data); delete pInfo; return 0;}unsigned int ProcessMonitorThread(void *){ // Monitor infinetely until STAF finishes. this thread will clean both // processes registered from outside of STAF (outsiders) and processes // registered from inside of STAF (insiders). // Due to the linux threading model, we have changed this code so that // processes get started from the thread that monitors which processes have // terminated. This is b/c of a bug on waitpid() which does not work if a // process gets started from another thread, which was the case before. std::list<STAFProcessID_t> fgPIDList; int theTTY = open("/dev/tty", O_RDONLY); if (theTTY < 0) { STAFString errorMessage("STAFProcess::processMonitorThread: " "Error opening /dev/tty, errno: "); errorMessage += errno; STAFTrace::trace(kSTAFTraceError, errorMessage); } while (1) { int foundProcess = 0; try { // NOTE: waitpid() returns -1 if no more child processes exist, // returns 0 if no child processes have ended, or returns // pid of process that has sent a stop or terminate signal. // Outsider processes are NOT considered child processes. ProcessMonitorList processMonitorList; int retCode = 0; int childId = 0; { // Acquire lock STAFMutexSemLock lock(sMonitorDataSem); // Note: the waitpid function with WNOHANG does NOT block, so // the lock will NOT be held for long periods of time. // waitpid() is protected to avoid the following situation: // assume no insiders exist (ie. no children) and 0 or more // outsiders exist. then waitpid would return -1 (childId = -1) // and set retCode to 0. This thread gets preempted by // startProcess2() and an internal process is added to the front // of the list terminating quickly after being added. The // thread then gets control back. The first condition fails // since childId < 0, and the second condition succeeds since // childId < 0 and the process has terminated. This makes the // return code of the process to be a fake 0 rather than its // real return code. Locking before calling waitpid() solves // this problem. childId = waitpid(-1, &retCode, WNOHANG | WUNTRACED); // if the childId > 0 then we look for it in the list, else // if the childId <= 0, we look for a victim outsider process // that has terminated. // Note: For outsider processes, we have to check that kill() // didn't set errno to EPERM because that means it does // not have permission to send the signal to the // process pid (e.g. could have been started by another // user), not that the process is terminated. ProcessMonitorMap::iterator iter = sMonitorMap.begin(); while (iter != sMonitorMap.end()) { if (((childId > 0) && (iter->first == childId) && (WIFEXITED(retCode) || WIFSIGNALED(retCode))) || ((childId <= 0) && (kill(iter->first, 0) == -1) && (errno != EPERM))) { // If the child is no longer running, then remove it // from the list of foreground waiters if (childId > 0) fgPIDList.remove(childId); // If the child was in the foreground then put STAF // back in the foreground. Farther below, we check for // other children who want the foreground. if ((childId > 0) && (tcgetpgrp(theTTY) == childId) && (tcsetpgrp(theTTY, getpgrp()) < 0)) { STAFString errorMessage("STAFProcess::" "processMonitorThread: " "Error on tcsetpgrp(), " "errno: "); errorMessage += errno; STAFTrace::trace(kSTAFTraceError, errorMessage); } processMonitorList = iter->second; sMonitorMap.erase(iter->first); foundProcess = 1; break; } iter++; } } // release lock if (foundProcess) { // Note: The callback needs to be executed by a separate // thread to avoid deadlock with STAFProcessService when // started process finishes before it's added to the list // and attempts to call it's callback function. for (ProcessMonitorList::iterator monitorIter = processMonitorList.begin(); (monitorIter != processMonitorList.end()) && (monitorIter->callback.callback != 0); ++monitorIter) { // XXX: It appears we aren't removing any entries from the // list. Is this correct? getProcessThreadManager().dispatch( ProcessMonitorCallbackThread, new ProcessMonitorCallbackInfo(monitorIter->callback, monitorIter->pid, monitorIter->handle, WIFEXITED(retCode) ? WEXITSTATUS(retCode) : WTERMSIG(retCode))); } } else if (!foundProcess && (childId > 0)) { if (WIFSTOPPED(retCode)) { fgPIDList.push_back(childId); } else { STAFTrace::trace(kSTAFTraceError, "STAFProcess::" "processMonitorThread: Signaled process " "(PID: " + STAFString(childId) + ") not in process monitor's thread list"); } } // If STAF is in the foreground and the list of children wanting // the foreground is not empty, then put the first one in the list // in the foreground if (!fgPIDList.empty() && (getpgrp() == tcgetpgrp(theTTY))) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -