Model::CanHandleResult Model::CanHandleDrops() const { if (IsDirectory()) // directories take anything // resolve permissions here return kCanHandle; if (IsSymLink()) { // descend into symlink and try again on it's target BEntry entry(&fEntryRef, true); if (entry.InitCheck() != B_OK) return kCannotHandle; if (entry == BEntry(EntryRef())) // self-referencing link, avoid infinite recursion return kCannotHandle; Model model(&entry); if (model.InitCheck() != B_OK) return kCannotHandle; return model.CanHandleDrops(); } if (IsExecutable()) return kNeedToCheckType; return kCannotHandle; }
static bool TwinExists(void) { char twinfilename[CF_BUFSIZE]; struct stat sb; snprintf(twinfilename, CF_BUFSIZE, "%s/%s", CFWORKDIR, TwinFilename()); MapName(twinfilename); return (stat(twinfilename, &sb) == 0) && (IsExecutable(twinfilename)); }
static void VerifyProcessOp(EvalContext *ctx, Item *procdata, Attributes a, Promise *pp) { int matches = 0, do_signals = true, out_of_range, killed = 0, need_to_restart = true; Item *killlist = NULL; matches = FindPidMatches(ctx, procdata, &killlist, a, pp->promiser); /* promise based on number of matches */ if (a.process_count.min_range != CF_NOINT) /* if a range is specified */ { if ((matches < a.process_count.min_range) || (matches > a.process_count.max_range)) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_CHANGE, pp, a, "Process count for '%s' was out of promised range (%d found)", pp->promiser, matches); for (const Rlist *rp = a.process_count.out_of_range_define; rp != NULL; rp = rp->next) { if (!EvalContextHeapContainsSoft(ctx, rp->item)) { EvalContextHeapAddSoft(ctx, rp->item, PromiseGetNamespace(pp)); } } out_of_range = true; } else { for (const Rlist *rp = a.process_count.in_range_define; rp != NULL; rp = rp->next) { if (!EvalContextHeapContainsSoft(ctx, rp->item)) { EvalContextHeapAddSoft(ctx, rp->item, PromiseGetNamespace(pp)); } } cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Process promise for '%s' is kept", pp->promiser); out_of_range = false; } } else { out_of_range = true; } if (!out_of_range) { return; } if (a.transaction.action == cfa_warn) { do_signals = false; } else { do_signals = true; } /* signal/kill promises for existing matches */ if (do_signals && (matches > 0)) { if (a.process_stop != NULL) { if (DONTDO) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a, "Need to keep process-stop promise for '%s', but only a warning is promised", pp->promiser); } else { if (IsExecutable(CommandArg0(a.process_stop))) { ShellCommandReturnsZero(a.process_stop, SHELL_TYPE_NONE); } else { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_FAIL, pp, a, "Process promise to stop '%s' could not be kept because '%s' the stop operator failed", pp->promiser, a.process_stop); DeleteItemList(killlist); return; } } } killed = DoAllSignals(ctx, killlist, a, pp); } /* delegated promise to restart killed or non-existent entries */ need_to_restart = (a.restart_class != NULL) && (killed || (matches == 0)); DeleteItemList(killlist); if (!need_to_restart) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "No restart promised for %s", pp->promiser); return; } else { if (a.transaction.action == cfa_warn) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a, "Need to keep restart promise for '%s', but only a warning is promised", pp->promiser); } else { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "Making a one-time restart promise for '%s'", pp->promiser); EvalContextHeapAddSoft(ctx, a.restart_class, PromiseGetNamespace(pp)); } } }
static PromiseResult VerifyProcessOp(EvalContext *ctx, Item *procdata, Attributes a, const Promise *pp) { bool do_signals = true; int out_of_range; int killed = 0; bool need_to_restart = true; Item *killlist = NULL; int matches = FindPidMatches(procdata, &killlist, a, pp->promiser); /* promise based on number of matches */ PromiseResult result = PROMISE_RESULT_NOOP; if (a.process_count.min_range != CF_NOINT) /* if a range is specified */ { if ((matches < a.process_count.min_range) || (matches > a.process_count.max_range)) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_CHANGE, pp, a, "Process count for '%s' was out of promised range (%d found)", pp->promiser, matches); result = PromiseResultUpdate(result, PROMISE_RESULT_CHANGE); for (const Rlist *rp = a.process_count.out_of_range_define; rp != NULL; rp = rp->next) { ClassRef ref = ClassRefParse(RlistScalarValue(rp)); EvalContextClassPutSoft(ctx, RlistScalarValue(rp), CONTEXT_SCOPE_NAMESPACE, "source=promise"); ClassRefDestroy(ref); } out_of_range = true; } else { for (const Rlist *rp = a.process_count.in_range_define; rp != NULL; rp = rp->next) { ClassRef ref = ClassRefParse(RlistScalarValue(rp)); EvalContextClassPutSoft(ctx, RlistScalarValue(rp), CONTEXT_SCOPE_NAMESPACE, "source=promise"); ClassRefDestroy(ref); } cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Process promise for '%s' is kept", pp->promiser); out_of_range = false; } } else { out_of_range = true; } if (!out_of_range) { DeleteItemList(killlist); return result; } if (a.transaction.action == cfa_warn) { do_signals = false; result = PromiseResultUpdate(result, PROMISE_RESULT_WARN); } else { do_signals = true; } /* signal/kill promises for existing matches */ if (do_signals && (matches > 0)) { if (a.process_stop != NULL) { if (DONTDO) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a, "Need to keep process-stop promise for '%s', but only a warning is promised", pp->promiser); result = PromiseResultUpdate(result, PROMISE_RESULT_WARN); } else { if (IsExecutable(CommandArg0(a.process_stop))) { ShellCommandReturnsZero(a.process_stop, SHELL_TYPE_NONE); } else { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Process promise to stop '%s' could not be kept because '%s' the stop operator failed", pp->promiser, a.process_stop); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); DeleteItemList(killlist); return result; } } } killed = DoAllSignals(ctx, killlist, a, pp, &result); } /* delegated promise to restart killed or non-existent entries */ need_to_restart = (a.restart_class != NULL) && (killed || (matches == 0)); DeleteItemList(killlist); if (!need_to_restart) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "No restart promised for %s", pp->promiser); return result; } else { if (a.transaction.action == cfa_warn) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a, "Need to keep restart promise for '%s', but only a warning is promised", pp->promiser); result = PromiseResultUpdate(result, PROMISE_RESULT_WARN); } else { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "Making a one-time restart promise for '%s'", pp->promiser); result = PromiseResultUpdate(result, PROMISE_RESULT_CHANGE); EvalContextClassPutSoft(ctx, a.restart_class, CONTEXT_SCOPE_NAMESPACE, "source=promise"); } } return result; }
bool IsNeedCmd(BOOL bRootCmd, LPCWSTR asCmdLine, CEStr &szExe, LPCWSTR* rsArguments /*= NULL*/, BOOL* rpbNeedCutStartEndQuot /*= NULL*/, BOOL* rpbRootIsCmdExe /*= NULL*/, BOOL* rpbAlwaysConfirmExit /*= NULL*/, BOOL* rpbAutoDisableConfirmExit /*= NULL*/) { bool rbNeedCutStartEndQuot = false; bool rbRootIsCmdExe = true; bool rbAlwaysConfirmExit = false; bool rbAutoDisableConfirmExit = false; wchar_t *pwszEndSpace; if (rsArguments) *rsArguments = NULL; bool lbRc = false; int iRc = 0; BOOL lbFirstWasGot = FALSE; LPCWSTR pwszCopy; int nLastChar; #ifdef _DEBUG CEStr szDbgFirst; #endif if (!asCmdLine || !*asCmdLine) { _ASSERTE(asCmdLine && *asCmdLine); goto wrap; } #ifdef _DEBUG // Это минимальные проверки, собственно к коду - не относятся bool bIsBatch = false; { LPCWSTR psz = asCmdLine; NextArg(&psz, szDbgFirst); psz = PointToExt(szDbgFirst); if (lstrcmpi(psz, L".cmd")==0 || lstrcmpi(psz, L".bat")==0) bIsBatch = true; } #endif if (!szExe.GetBuffer(MAX_PATH)) { _ASSERTE(FALSE && "Failed to allocate MAX_PATH"); lbRc = true; goto wrap; } szExe.Empty(); if (!asCmdLine || *asCmdLine == 0) { _ASSERTE(asCmdLine && *asCmdLine); lbRc = true; goto wrap; } pwszCopy = asCmdLine; // cmd /c ""c:\program files\arc\7z.exe" -?" // да еще и внутри могут быть двойными... // cmd /c "dir c:\" nLastChar = lstrlenW(pwszCopy) - 1; if (pwszCopy[0] == L'"' && pwszCopy[nLastChar] == L'"') { //if (pwszCopy[1] == L'"' && pwszCopy[2]) //{ // pwszCopy ++; // Отбросить первую кавычку в командах типа: ""c:\program files\arc\7z.exe" -?" // if (rbNeedCutStartEndQuot) *rbNeedCutStartEndQuot = TRUE; //} //else // глючила на ""F:\VCProject\FarPlugin\#FAR180\far.exe -new_console"" //if (wcschr(pwszCopy+1, L'"') == (pwszCopy+nLastChar)) { // LPCWSTR pwszTemp = pwszCopy; // // Получим первую команду (исполняемый файл?) // if ((iRc = NextArg(&pwszTemp, szArg)) != 0) { // //Parsing command line failed // lbRc = true; goto wrap; // } // pwszCopy ++; // Отбросить первую кавычку в командах типа: "c:\arc\7z.exe -?" // lbFirstWasGot = TRUE; // if (rbNeedCutStartEndQuot) *rbNeedCutStartEndQuot = TRUE; //} else { // Will be dequoted in 'NextArg' function. Examples // "C:\GCC\msys\bin\make.EXE -f "makefile" COMMON="../../../plugins/common"" // ""F:\VCProject\FarPlugin\#FAR180\far.exe -new_console"" // ""cmd"" // cmd /c ""c:\program files\arc\7z.exe" -?" // да еще и внутри могут быть двойными... // cmd /c "dir c:\" LPCWSTR pwszTemp = pwszCopy; // Получим первую команду (исполняемый файл?) if ((iRc = NextArg(&pwszTemp, szExe)) != 0) { //Parsing command line failed #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } if (lstrcmpiW(szExe, L"start") == 0) { // Команду start обрабатывает только процессор #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } LPCWSTR pwszQ = pwszCopy + 1 + lstrlen(szExe); wchar_t* pszExpand = NULL; if (*pwszQ != L'"' && IsExecutable(szExe, &pszExpand)) { pwszCopy ++; // отбрасываем lbFirstWasGot = TRUE; if (pszExpand) { szExe.Set(pszExpand); SafeFree(pszExpand); if (rsArguments) *rsArguments = pwszQ; } rbNeedCutStartEndQuot = true; } } } // Получим первую команду (исполняемый файл?) if (!lbFirstWasGot) { szExe.Empty(); // `start` command must be processed by processor itself if ((lstrcmpni(pwszCopy, L"start", 5) == 0) && (!pwszCopy[5] || isSpace(pwszCopy[5]))) { #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } // 17.10.2010 - support executable file path without parameters, but with spaces in its path // 22.11.2015 - or some weirdness, like `C:\Program Files\CB/cb_console_runner.exe "C:\sources\app.exe"` LPCWSTR pchEnd = wcschr(pwszCopy, L' '); if (!pchEnd) pchEnd = pwszCopy + lstrlenW(pwszCopy); CEStr szTemp; DWORD nTempSize; while (pchEnd) { szTemp.Set(pwszCopy, (pchEnd - pwszCopy)); _ASSERTE(szTemp[(pchEnd - pwszCopy)] == 0); // If this is a full path without environment variables if (((IsFilePath(szTemp, true) && !wcschr(szTemp, L'%')) // or file/dir may be found via env.var. substitution or searching in %PATH% || FileExistsSearch((LPCWSTR)szTemp, szTemp)) // Than check if it is a FILE (not a directory) && FileExists(szTemp, &nTempSize) && nTempSize) { // OK, it an our executable? if (rsArguments) *rsArguments = pchEnd; szExe.Set(szTemp); break; } _ASSERTE(*pchEnd == 0 || *pchEnd == L' '); if (!*pchEnd) break; // Find next space after nonspace while (*(pchEnd) == L' ') pchEnd++; // If quoted string starts from here - it's supposed to be an argument if (*pchEnd == L'"') { // And we must not get here, because the executable must be already processed above // _ASSERTE(*pchEnd != L'"'); break; } pchEnd = wcschr(pchEnd, L' '); if (!pchEnd) pchEnd = pwszCopy + lstrlenW(pwszCopy); } if (szExe[0] == 0) { if ((iRc = NextArg(&pwszCopy, szExe)) != 0) { //Parsing command line failed #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } _ASSERTE(lstrcmpiW(szExe, L"start") != 0); // Обработка переменных окружения и поиск в PATH if (FileExistsSearch((LPCWSTR)szExe, szExe)) { if (rsArguments) *rsArguments = pwszCopy; } } } if (!*szExe) { _ASSERTE(szExe[0] != 0); } else { // Если юзеру нужен редирект - то он должен явно указать ком.процессор // Иначе нереально (или достаточно сложно) определить, является ли символ // редиректом, или это просто один из аргументов команды... // "Левые" символы в имени файла (а вот в "первом аргументе" все однозначно) if (wcspbrk(szExe, L"?*<>|")) { rbRootIsCmdExe = TRUE; // запуск через "процессор" lbRc = true; goto wrap; // добавить "cmd.exe" } // если "путь" не указан if (wcschr(szExe, L'\\') == NULL) { bool bHasExt = (wcschr(szExe, L'.') != NULL); // Проверим, может это команда процессора (типа "DIR")? if (!bHasExt) { bool bIsCommand = false; wchar_t* pszUppr = lstrdup(szExe); if (pszUppr) { // избежать линковки на user32.dll //CharUpperBuff(pszUppr, lstrlen(pszUppr)); for (wchar_t* p = pszUppr; *p; p++) { if (*p >= L'a' && *p <= 'z') *p -= 0x20; } const wchar_t* pszFind = gsInternalCommands; while (*pszFind) { if (lstrcmp(pszUppr, pszFind) == 0) { bIsCommand = true; break; } pszFind += lstrlen(pszFind)+1; } free(pszUppr); } if (bIsCommand) { #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif rbRootIsCmdExe = TRUE; // запуск через "процессор" lbRc = true; goto wrap; // добавить "cmd.exe" } } // Try to find executable in %PATH% { #ifndef CONEMU_MINIMAL MWow64Disable wow; wow.Disable(); // Disable Wow64 file redirector #endif apiSearchPath(NULL, szExe, bHasExt ? NULL : L".exe", szExe); } } // end: if (wcschr(szExe, L'\\') == NULL) } // Если szExe не содержит путь к файлу - запускаем через cmd // "start "" C:\Utils\Files\Hiew32\hiew32.exe C:\00\Far.exe" if (!IsFilePath(szExe)) { #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif rbRootIsCmdExe = TRUE; // запуск через "процессор" lbRc = true; goto wrap; // добавить "cmd.exe" } //pwszCopy = wcsrchr(szArg, L'\\'); if (!pwszCopy) pwszCopy = szArg; else pwszCopy ++; pwszCopy = PointToName(szExe); //2009-08-27 pwszEndSpace = szExe.ms_Val + lstrlenW(szExe) - 1; while ((*pwszEndSpace == L' ') && (pwszEndSpace > szExe)) { *(pwszEndSpace--) = 0; } #ifndef __GNUC__ #pragma warning( push ) #pragma warning(disable : 6400) #endif if (lstrcmpiW(pwszCopy, L"cmd")==0 || lstrcmpiW(pwszCopy, L"cmd.exe")==0 || lstrcmpiW(pwszCopy, L"tcc")==0 || lstrcmpiW(pwszCopy, L"tcc.exe")==0) { rbRootIsCmdExe = TRUE; // уже должен быть выставлен, но проверим rbAlwaysConfirmExit = TRUE; rbAutoDisableConfirmExit = FALSE; _ASSERTE(!bIsBatch); lbRc = false; goto wrap; // уже указан командный процессор, cmd.exe в начало добавлять не нужно } // Issue 1211: Decide not to do weird heuristic. // If user REALLY needs redirection (root command, huh?) // - he must call "cmd /c ..." directly // Если есть одна из команд перенаправления, или слияния - нужен CMD.EXE if (!bRootCmd) { if (wcschr(asCmdLine, L'&') || wcschr(asCmdLine, L'>') || wcschr(asCmdLine, L'<') || wcschr(asCmdLine, L'|') || wcschr(asCmdLine, L'^') // или экранирования ) { #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } } //if (lstrcmpiW(pwszCopy, L"far")==0 || lstrcmpiW(pwszCopy, L"far.exe")==0) if (IsFarExe(pwszCopy)) { bool bFound = (wcschr(pwszCopy, L'.') != NULL); // Если указали при запуске просто "far" - это может быть батник, расположенный в %PATH% if (!bFound) { CEStr szSearch; if (apiSearchPath(NULL, pwszCopy, L".exe", szSearch)) { if (lstrcmpi(PointToExt(szSearch), L".exe") == 0) bFound = true; } } if (bFound) { rbAutoDisableConfirmExit = TRUE; rbRootIsCmdExe = FALSE; // FAR! _ASSERTE(!bIsBatch); lbRc = false; goto wrap; // уже указан исполняемый файл, cmd.exe в начало добавлять не нужно } } if (IsExecutable(szExe)) { rbRootIsCmdExe = FALSE; // Для других программ - буфер не включаем _ASSERTE(!bIsBatch); lbRc = false; goto wrap; // Запускается конкретная консольная программа. cmd.exe не требуется } //Можно еще Доделать поиски с: SearchPath, GetFullPathName, добавив расширения .exe & .com //хотя фар сам формирует полные пути к командам, так что можно не заморачиваться #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif rbRootIsCmdExe = TRUE; #ifndef __GNUC__ #pragma warning( pop ) #endif lbRc = true; wrap: if (rpbNeedCutStartEndQuot) *rpbNeedCutStartEndQuot = rbNeedCutStartEndQuot; if (rpbRootIsCmdExe) *rpbRootIsCmdExe = rbRootIsCmdExe; if (rpbAlwaysConfirmExit) *rpbAlwaysConfirmExit = rbAlwaysConfirmExit; if (rpbAutoDisableConfirmExit) *rpbAutoDisableConfirmExit = rbAutoDisableConfirmExit; return lbRc; }
// Function checks, if we need drop first and last quotation marks // Example: ""7z.exe" /?" // Using cmd.exe rules bool IsNeedDequote(LPCWSTR asCmdLine, bool abFromCmdCK, LPCWSTR* rsEndQuote/*=NULL*/) { if (rsEndQuote) *rsEndQuote = NULL; if (!asCmdLine) return false; bool bDeQu = false; LPCWSTR pszQE, pszSP; if (asCmdLine[0] == L'"') { bDeQu = (asCmdLine[1] == L'"'); // Всегда - нельзя. Иначе парсинг строки запуска некорректно идет // L"\"C:\\ConEmu\\ConEmuC64.exe\" /PARENTFARPID=1 /C \"C:\\GIT\\cmdw\\ad.cmd CE12.sln & ci -m \"Solution debug build properties\"\"" if (!bDeQu) { size_t nLen = lstrlen(asCmdLine); if (abFromCmdCK) { bDeQu = ((asCmdLine[nLen-1] == L'"') && (asCmdLine[nLen-2] == L'"')); } if (!bDeQu && (asCmdLine[nLen-1] == L'"')) { pszSP = wcschr(asCmdLine+1, L' '); pszQE = wcschr(asCmdLine+1, L'"'); if (pszSP && pszQE && (pszSP < pszQE) && ((pszSP - asCmdLine) < MAX_PATH)) { CEStr lsTmp; lsTmp.Set(asCmdLine+1, pszSP-asCmdLine-1); bDeQu = (IsFilePath(lsTmp, true) && IsExecutable(lsTmp)); } } } } if (!bDeQu) return false; // Don't dequote? pszQE = wcsrchr(asCmdLine+2, L'"'); if (!pszQE) return false; #if 0 LPCWSTR pszQ1 = wcschr(asCmdLine+2, L'"'); if (!pszQ1) return false; LPCWSTR pszQE = wcsrchr(pszQ1, L'"'); // Only TWO quotes in asCmdLine? if (pszQE == pszQ1) { // Doesn't contains special symbols? if (!wcspbrk(asCmdLine+1, L"&<>()@^|")) { // Must contains spaces (doubt?) if (wcschr(asCmdLine+1, L' ')) { // Cmd also checks this for executable file name. Skip this check? return false; } } } #endif // Well, we get here _ASSERTE(asCmdLine[0]==L'"' && pszQE && *pszQE==L'"' && !wcschr(pszQE+1,L'"')); // Dequote it! if (rsEndQuote) *rsEndQuote = pszQE; return true; }
static void VerifyExec(Attributes a, Promise *pp) { CfLock thislock; char unsafeLine[CF_BUFSIZE], line[sizeof(unsafeLine) * 2], eventname[CF_BUFSIZE]; char comm[20]; char execstr[CF_EXPANDSIZE]; int outsourced, count = 0; mode_t maskval = 0; FILE *pfp; char cmdOutBuf[CF_BUFSIZE]; int cmdOutBufPos = 0; int lineOutLen; if (!IsExecutable(GetArg0(pp->promiser))) { cfPS(cf_error, CF_FAIL, "", pp, a, "%s promises to be executable but isn't\n", pp->promiser); if (strchr(pp->promiser, ' ')) { CfOut(cf_verbose, "", "Paths with spaces must be inside escaped quoutes (e.g. \\\"%s\\\")", pp->promiser); } return; } else { CfOut(cf_verbose, "", " -> Promiser string contains a valid executable (%s) - ok\n", GetArg0(pp->promiser)); } DeleteScalar("this", "promiser"); NewScalar("this", "promiser", pp->promiser, cf_str); if (a.args) { snprintf(execstr, CF_EXPANDSIZE - 1, "%s %s", pp->promiser, a.args); } else { strncpy(execstr, pp->promiser, CF_BUFSIZE); } thislock = AcquireLock(execstr, VUQNAME, CFSTARTTIME, a, pp, false); if (thislock.lock == NULL) { return; } PromiseBanner(pp); CfOut(cf_inform, "", " -> Executing \'%s\' ...(timeout=%d,owner=%ju,group=%ju)\n", execstr, a.contain.timeout, (uintmax_t)a.contain.owner, (uintmax_t)a.contain.group); BeginMeasure(); if (DONTDO && !a.contain.preview) { CfOut(cf_error, "", "-> Would execute script %s\n", execstr); } else if (a.transaction.action != cfa_fix) { cfPS(cf_error, CF_WARN, "", pp, a, " !! Command \"%s\" needs to be executed, but only warning was promised", execstr); } else { CommPrefix(execstr, comm); if (a.transaction.background) { #ifdef MINGW outsourced = true; #else CfOut(cf_verbose, "", " -> Backgrounding job %s\n", execstr); outsourced = fork(); #endif } else { outsourced = false; } if (outsourced || !a.transaction.background) // work done here: either by child or non-background parent { if (a.contain.timeout != CF_NOINT) { SetTimeOut(a.contain.timeout); } #ifndef MINGW CfOut(cf_verbose, "", " -> (Setting umask to %jo)\n", (uintmax_t)a.contain.umask); maskval = umask(a.contain.umask); if (a.contain.umask == 0) { CfOut(cf_verbose, "", " !! Programming %s running with umask 0! Use umask= to set\n", execstr); } #endif /* NOT MINGW */ if (a.contain.useshell) { pfp = cf_popen_shsetuid(execstr, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, a.transaction.background); } else { pfp = cf_popensetuid(execstr, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, a.transaction.background); } if (pfp == NULL) { cfPS(cf_error, CF_FAIL, "cf_popen", pp, a, "!! Couldn't open pipe to command %s\n", execstr); YieldCurrentLock(thislock); return; } while (!feof(pfp)) { if (ferror(pfp)) /* abortable */ { cfPS(cf_error, CF_TIMEX, "ferror", pp, a, "!! Command pipe %s\n", execstr); cf_pclose(pfp); YieldCurrentLock(thislock); return; } CfReadLine(unsafeLine, CF_BUFSIZE - 1, pfp); ReplaceStr(unsafeLine, line, sizeof(line), "%", "%%"); // escape format char if (strstr(line, "cfengine-die")) { break; } if (ferror(pfp)) /* abortable */ { cfPS(cf_error, CF_TIMEX, "ferror", pp, a, "!! Command pipe %s\n", execstr); cf_pclose(pfp); YieldCurrentLock(thislock); return; } if (a.contain.preview) { PreviewProtocolLine(line, execstr); } if (a.module) { ModuleProtocol(execstr, line, !a.contain.nooutput); } else if (!a.contain.nooutput && NonEmptyLine(line)) { lineOutLen = strlen(comm) + strlen(line) + 12; // if buffer is to small for this line, output it directly if (lineOutLen > sizeof(cmdOutBuf)) { CfOut(cf_cmdout, "", "Q: \"...%s\": %s\n", comm, line); } else { if (cmdOutBufPos + lineOutLen > sizeof(cmdOutBuf)) { CfOut(cf_cmdout, "", "%s", cmdOutBuf); cmdOutBufPos = 0; } sprintf(cmdOutBuf + cmdOutBufPos, "Q: \"...%s\": %s\n", comm, line); cmdOutBufPos += (lineOutLen - 1); } count++; } } #ifdef MINGW if (outsourced) // only get return value if we waited for command execution { cf_pclose(pfp); } else { cf_pclose_def(pfp, a, pp); } #else /* NOT MINGW */ cf_pclose_def(pfp, a, pp); #endif } if (count) { if (cmdOutBufPos) { CfOut(cf_cmdout, "", "%s", cmdOutBuf); } CfOut(cf_cmdout, "", "I: Last %d quoted lines were generated by promiser \"%s\"\n", count, execstr); } if (a.contain.timeout != CF_NOINT) { alarm(0); signal(SIGALRM, SIG_DFL); } CfOut(cf_inform, "", " -> Completed execution of %s\n", execstr); #ifndef MINGW umask(maskval); #endif YieldCurrentLock(thislock); snprintf(eventname, CF_BUFSIZE - 1, "Exec(%s)", execstr); #ifndef MINGW if (a.transaction.background && outsourced) { CfOut(cf_verbose, "", " -> Backgrounded command (%s) is done - exiting\n", execstr); exit(0); } #endif /* NOT MINGW */ } }
static ActionResult RepairExec(EvalContext *ctx, Attributes a, Promise *pp) { char line[CF_BUFSIZE], eventname[CF_BUFSIZE]; char cmdline[CF_BUFSIZE]; char comm[20]; int outsourced, count = 0; #if !defined(__MINGW32__) mode_t maskval = 0; #endif FILE *pfp; char cmdOutBuf[CF_BUFSIZE]; int cmdOutBufPos = 0; int lineOutLen; if (IsAbsoluteFileName(CommandArg0(pp->promiser)) || a.contain.shelltype == SHELL_TYPE_NONE) { if (!IsExecutable(CommandArg0(pp->promiser))) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "'%s' promises to be executable but isn't", pp->promiser); if (strchr(pp->promiser, ' ')) { Log(LOG_LEVEL_VERBOSE, "Paths with spaces must be inside escaped quoutes (e.g. \\\"%s\\\")", pp->promiser); } return ACTION_RESULT_FAILED; } else { Log(LOG_LEVEL_VERBOSE, "Promiser string contains a valid executable '%s' - ok", CommandArg0(pp->promiser)); } } char timeout_str[CF_BUFSIZE]; if (a.contain.timeout == CF_NOINT) { snprintf(timeout_str, CF_BUFSIZE, "no timeout"); } else { snprintf(timeout_str, CF_BUFSIZE, "timeout=%ds", a.contain.timeout); } char owner_str[CF_BUFSIZE] = ""; if (a.contain.owner != -1) { snprintf(owner_str, CF_BUFSIZE, ",uid=%ju", (uintmax_t)a.contain.owner); } char group_str[CF_BUFSIZE] = ""; if (a.contain.group != -1) { snprintf(group_str, CF_BUFSIZE, ",gid=%ju", (uintmax_t)a.contain.group); } snprintf(cmdline, CF_BUFSIZE, "%s%s%s", pp->promiser, a.args ? " " : "", a.args ? a.args : ""); Log(LOG_LEVEL_INFO, "Executing '%s%s%s' ... '%s'", timeout_str, owner_str, group_str, cmdline); BeginMeasure(); if (DONTDO && (!a.contain.preview)) { Log(LOG_LEVEL_ERR, "Would execute script '%s'", cmdline); return ACTION_RESULT_OK; } if (a.transaction.action != cfa_fix) { Log(LOG_LEVEL_ERR, "Command '%s' needs to be executed, but only warning was promised", cmdline); return ACTION_RESULT_OK; } CommandPrefix(cmdline, comm); if (a.transaction.background) { #ifdef __MINGW32__ outsourced = true; #else Log(LOG_LEVEL_VERBOSE, "Backgrounding job '%s'", cmdline); outsourced = fork(); #endif } else { outsourced = false; } if (outsourced || (!a.transaction.background)) // work done here: either by child or non-background parent { if (a.contain.timeout != CF_NOINT) { SetTimeOut(a.contain.timeout); } #ifndef __MINGW32__ Log(LOG_LEVEL_VERBOSE, "(Setting umask to %jo)", (uintmax_t)a.contain.umask); maskval = umask(a.contain.umask); if (a.contain.umask == 0) { Log(LOG_LEVEL_VERBOSE, "Programming '%s' running with umask 0! Use umask= to set", cmdline); } #endif /* !__MINGW32__ */ if (a.contain.shelltype == SHELL_TYPE_POWERSHELL) { #ifdef __MINGW32__ pfp = cf_popen_powershell_setuid(cmdline, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, a.transaction.background); #else // !__MINGW32__ Log(LOG_LEVEL_ERR, "Powershell is only supported on Windows"); return ACTION_RESULT_FAILED; #endif // !__MINGW32__ } else if (a.contain.shelltype == SHELL_TYPE_USE) { pfp = cf_popen_shsetuid(cmdline, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, a.transaction.background); } else { pfp = cf_popensetuid(cmdline, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, a.transaction.background); } if (pfp == NULL) { Log(LOG_LEVEL_ERR, "Couldn't open pipe to command '%s'. (cf_popen: %s)", cmdline, GetErrorStr()); return ACTION_RESULT_FAILED; } for (;;) { ssize_t res = CfReadLine(line, CF_BUFSIZE, pfp); if (res == 0) { break; } if (res == -1) { Log(LOG_LEVEL_ERR, "Unable to read output from command '%s'. (fread: %s)", cmdline, GetErrorStr()); cf_pclose(pfp); return ACTION_RESULT_FAILED; } if (strstr(line, "cfengine-die")) { break; } if (a.contain.preview) { PreviewProtocolLine(line, cmdline); } if (a.module) { ModuleProtocol(ctx, cmdline, line, !a.contain.nooutput, PromiseGetNamespace(pp)); } else if ((!a.contain.nooutput) && (!EmptyString(line))) { lineOutLen = strlen(comm) + strlen(line) + 12; // if buffer is to small for this line, output it directly if (lineOutLen > sizeof(cmdOutBuf)) { Log(LOG_LEVEL_NOTICE, "Q: '%s': %s", comm, line); } else { if (cmdOutBufPos + lineOutLen > sizeof(cmdOutBuf)) { Log(LOG_LEVEL_NOTICE, "%s", cmdOutBuf); cmdOutBufPos = 0; } sprintf(cmdOutBuf + cmdOutBufPos, "Q: \"...%s\": %s\n", comm, line); cmdOutBufPos += (lineOutLen - 1); } count++; } } #ifdef __MINGW32__ if (outsourced) // only get return value if we waited for command execution { cf_pclose(pfp); } else #endif /* __MINGW32__ */ { int ret = cf_pclose(pfp); if (ret == -1) { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, "Finished script '%s' - failed (abnormal termination)", pp->promiser); } else { VerifyCommandRetcode(ctx, ret, true, a, pp); } } } if (count) { if (cmdOutBufPos) { Log(LOG_LEVEL_NOTICE, "%s", cmdOutBuf); } Log(LOG_LEVEL_INFO, "Last %d quoted lines were generated by promiser '%s'", count, cmdline); } if (a.contain.timeout != CF_NOINT) { alarm(0); signal(SIGALRM, SIG_DFL); } Log(LOG_LEVEL_INFO, "Completed execution of '%s'", cmdline); #ifndef __MINGW32__ umask(maskval); #endif snprintf(eventname, CF_BUFSIZE - 1, "Exec(%s)", cmdline); #ifndef __MINGW32__ if ((a.transaction.background) && outsourced) { Log(LOG_LEVEL_VERBOSE, "Backgrounded command '%s' is done - exiting", cmdline); exit(0); } #endif /* !__MINGW32__ */ return ACTION_RESULT_OK; }
bool IsNeedCmd(BOOL bRootCmd, LPCWSTR asCmdLine, CmdArg &szExe, LPCWSTR* rsArguments /*= NULL*/, BOOL* rpbNeedCutStartEndQuot /*= NULL*/, BOOL* rpbRootIsCmdExe /*= NULL*/, BOOL* rpbAlwaysConfirmExit /*= NULL*/, BOOL* rpbAutoDisableConfirmExit /*= NULL*/) { _ASSERTE(asCmdLine && *asCmdLine); bool rbNeedCutStartEndQuot = false; bool rbRootIsCmdExe = true; bool rbAlwaysConfirmExit = false; bool rbAutoDisableConfirmExit = false; if (rsArguments) *rsArguments = NULL; bool lbRc = false; int iRc = 0; BOOL lbFirstWasGot = FALSE; LPCWSTR pwszCopy; int nLastChar; #ifdef _DEBUG // Это минимальные проверки, собственно к коду - не относятся CmdArg szDbgFirst; bool bIsBatch = false; { LPCWSTR psz = asCmdLine; NextArg(&psz, szDbgFirst); psz = PointToExt(szDbgFirst); if (lstrcmpi(psz, L".cmd")==0 || lstrcmpi(psz, L".bat")==0) bIsBatch = true; } #endif if (!szExe.GetBuffer(MAX_PATH)) { _ASSERTE(FALSE && "Failed to allocate MAX_PATH"); lbRc = true; goto wrap; } szExe.Empty(); if (!asCmdLine || *asCmdLine == 0) { _ASSERTE(asCmdLine && *asCmdLine); lbRc = true; goto wrap; } pwszCopy = asCmdLine; // cmd /c ""c:\program files\arc\7z.exe" -?" // да еще и внутри могут быть двойными... // cmd /c "dir c:\" nLastChar = lstrlenW(pwszCopy) - 1; if (pwszCopy[0] == L'"' && pwszCopy[nLastChar] == L'"') { //if (pwszCopy[1] == L'"' && pwszCopy[2]) //{ // pwszCopy ++; // Отбросить первую кавычку в командах типа: ""c:\program files\arc\7z.exe" -?" // if (rbNeedCutStartEndQuot) *rbNeedCutStartEndQuot = TRUE; //} //else // глючила на ""F:\VCProject\FarPlugin\#FAR180\far.exe -new_console"" //if (wcschr(pwszCopy+1, L'"') == (pwszCopy+nLastChar)) { // LPCWSTR pwszTemp = pwszCopy; // // Получим первую команду (исполняемый файл?) // if ((iRc = NextArg(&pwszTemp, szArg)) != 0) { // //Parsing command line failed // lbRc = true; goto wrap; // } // pwszCopy ++; // Отбросить первую кавычку в командах типа: "c:\arc\7z.exe -?" // lbFirstWasGot = TRUE; // if (rbNeedCutStartEndQuot) *rbNeedCutStartEndQuot = TRUE; //} else { // Will be dequoted in 'NextArg' function. Examples // "C:\GCC\msys\bin\make.EXE -f "makefile" COMMON="../../../plugins/common"" // ""F:\VCProject\FarPlugin\#FAR180\far.exe -new_console"" // ""cmd"" // cmd /c ""c:\program files\arc\7z.exe" -?" // да еще и внутри могут быть двойными... // cmd /c "dir c:\" LPCWSTR pwszTemp = pwszCopy; // Получим первую команду (исполняемый файл?) if ((iRc = NextArg(&pwszTemp, szExe)) != 0) { //Parsing command line failed #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } if (lstrcmpiW(szExe, L"start") == 0) { // Команду start обрабатывает только процессор #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } LPCWSTR pwszQ = pwszCopy + 1 + lstrlen(szExe); wchar_t* pszExpand = NULL; if (*pwszQ != L'"' && IsExecutable(szExe, &pszExpand)) { pwszCopy ++; // отбрасываем lbFirstWasGot = TRUE; if (pszExpand) { szExe.Set(pszExpand); SafeFree(pszExpand); if (rsArguments) *rsArguments = pwszQ; } rbNeedCutStartEndQuot = true; } } } // Получим первую команду (исполняемый файл?) if (!lbFirstWasGot) { szExe.Empty(); // 17.10.2010 - поддержка переданного исполняемого файла без параметров, но с пробелами в пути LPCWSTR pchEnd = pwszCopy + lstrlenW(pwszCopy); while (pchEnd > pwszCopy && *(pchEnd-1) == L' ') pchEnd--; if ((pchEnd - pwszCopy) < MAX_PATH) { szExe.Set(pwszCopy, (pchEnd - pwszCopy)); _ASSERTE(szExe[(pchEnd - pwszCopy)] == 0); if (lstrcmpiW(szExe, L"start") == 0) { // Команду start обрабатывает только процессор #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } // Обработка переменных окружения и поиск в PATH if (FileExistsSearch((LPCWSTR)szExe, szExe)) { if (rsArguments) *rsArguments = pchEnd; } else { szExe.Empty(); } } if (szExe[0] == 0) { if ((iRc = NextArg(&pwszCopy, szExe)) != 0) { //Parsing command line failed #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } if (lstrcmpiW(szExe, L"start") == 0) { // Команду start обрабатывает только процессор #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } // Обработка переменных окружения и поиск в PATH if (FileExistsSearch((LPCWSTR)szExe, szExe)) { if (rsArguments) *rsArguments = pwszCopy; } } } if (!*szExe) { _ASSERTE(szExe[0] != 0); } else { // Если юзеру нужен редирект - то он должен явно указать ком.процессор // Иначе нереально (или достаточно сложно) определить, является ли символ // редиректом, или это просто один из аргументов команды... // "Левые" символы в имени файла (а вот в "первом аргументе" все однозначно) if (wcspbrk(szExe, L"?*<>|")) { rbRootIsCmdExe = TRUE; // запуск через "процессор" lbRc = true; goto wrap; // добавить "cmd.exe" } // если "путь" не указан if (wcschr(szExe, L'\\') == NULL) { bool bHasExt = (wcschr(szExe, L'.') != NULL); // Проверим, может это команда процессора (типа "DIR")? if (!bHasExt) { bool bIsCommand = false; wchar_t* pszUppr = lstrdup(szExe); if (pszUppr) { // избежать линковки на user32.dll //CharUpperBuff(pszUppr, lstrlen(pszUppr)); for (wchar_t* p = pszUppr; *p; p++) { if (*p >= L'a' && *p <= 'z') *p -= 0x20; } const wchar_t* pszFind = gsInternalCommands; while (*pszFind) { if (lstrcmp(pszUppr, pszFind) == 0) { bIsCommand = true; break; } pszFind += lstrlen(pszFind)+1; } free(pszUppr); } if (bIsCommand) { #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif rbRootIsCmdExe = TRUE; // запуск через "процессор" lbRc = true; goto wrap; // добавить "cmd.exe" } } // Пробуем найти "по путям" соответствующий exe-шник. DWORD nCchMax = szExe.mn_MaxLen; // выделить память, длинее чем szExe вернуть не сможем wchar_t* pszSearch = (wchar_t*)malloc(nCchMax*sizeof(wchar_t)); if (pszSearch) { #ifndef CONEMU_MINIMAL MWow64Disable wow; wow.Disable(); // Отключить редиректор! #endif wchar_t *pszName = NULL; DWORD nRc = SearchPath(NULL, szExe, bHasExt ? NULL : L".exe", nCchMax, pszSearch, &pszName); if (nRc && (nRc < nCchMax)) { // Нашли, возвращаем что нашли szExe.Set(pszSearch); } free(pszSearch); } } // end: if (wcschr(szExe, L'\\') == NULL) } // Если szExe не содержит путь к файлу - запускаем через cmd // "start "" C:\Utils\Files\Hiew32\hiew32.exe C:\00\Far.exe" if (!IsFilePath(szExe)) { #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif rbRootIsCmdExe = TRUE; // запуск через "процессор" lbRc = true; goto wrap; // добавить "cmd.exe" } //pwszCopy = wcsrchr(szArg, L'\\'); if (!pwszCopy) pwszCopy = szArg; else pwszCopy ++; pwszCopy = PointToName(szExe); //2009-08-27 wchar_t *pwszEndSpace = szExe.ms_Arg + lstrlenW(szExe) - 1; while ((*pwszEndSpace == L' ') && (pwszEndSpace > szExe)) { *(pwszEndSpace--) = 0; } #ifndef __GNUC__ #pragma warning( push ) #pragma warning(disable : 6400) #endif if (lstrcmpiW(pwszCopy, L"cmd")==0 || lstrcmpiW(pwszCopy, L"cmd.exe")==0 || lstrcmpiW(pwszCopy, L"tcc")==0 || lstrcmpiW(pwszCopy, L"tcc.exe")==0) { rbRootIsCmdExe = TRUE; // уже должен быть выставлен, но проверим rbAlwaysConfirmExit = TRUE; rbAutoDisableConfirmExit = FALSE; _ASSERTE(!bIsBatch); lbRc = false; goto wrap; // уже указан командный процессор, cmd.exe в начало добавлять не нужно } // Issue 1211: Decide not to do weird heuristic. // If user REALLY needs redirection (root command, huh?) // - he must call "cmd /c ..." directly // Если есть одна из команд перенаправления, или слияния - нужен CMD.EXE if (!bRootCmd) { if (wcschr(asCmdLine, L'&') || wcschr(asCmdLine, L'>') || wcschr(asCmdLine, L'<') || wcschr(asCmdLine, L'|') || wcschr(asCmdLine, L'^') // или экранирования ) { #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif lbRc = true; goto wrap; } } //if (lstrcmpiW(pwszCopy, L"far")==0 || lstrcmpiW(pwszCopy, L"far.exe")==0) if (IsFarExe(pwszCopy)) { bool bFound = (wcschr(pwszCopy, L'.') != NULL); // Если указали при запуске просто "far" - это может быть батник, расположенный в %PATH% if (!bFound) { wchar_t szSearch[MAX_PATH+1], *pszPart = NULL; DWORD n = SearchPath(NULL, pwszCopy, L".exe", countof(szSearch), szSearch, &pszPart); if (n && (n < countof(szSearch))) { if (lstrcmpi(PointToExt(pszPart), L".exe") == 0) bFound = true; } } if (bFound) { rbAutoDisableConfirmExit = TRUE; rbRootIsCmdExe = FALSE; // FAR! _ASSERTE(!bIsBatch); lbRc = false; goto wrap; // уже указан исполняемый файл, cmd.exe в начало добавлять не нужно } } if (IsExecutable(szExe)) { rbRootIsCmdExe = FALSE; // Для других программ - буфер не включаем _ASSERTE(!bIsBatch); lbRc = false; goto wrap; // Запускается конкретная консольная программа. cmd.exe не требуется } //Можно еще Доделать поиски с: SearchPath, GetFullPathName, добавив расширения .exe & .com //хотя фар сам формирует полные пути к командам, так что можно не заморачиваться #ifdef WARN_NEED_CMD _ASSERTE(FALSE); #endif rbRootIsCmdExe = TRUE; #ifndef __GNUC__ #pragma warning( pop ) #endif lbRc = true; wrap: if (rpbNeedCutStartEndQuot) *rpbNeedCutStartEndQuot = rbNeedCutStartEndQuot; if (rpbRootIsCmdExe) *rpbRootIsCmdExe = rbRootIsCmdExe; if (rpbAlwaysConfirmExit) *rpbAlwaysConfirmExit = rbAlwaysConfirmExit; if (rpbAutoDisableConfirmExit) *rpbAutoDisableConfirmExit = rbAutoDisableConfirmExit; return lbRc; }
void RunShell() { int running = 1; char* str; char** argv; InitBackgroundProcessing(); while (running) { CheckProcessQueue(); PrintPrompt(); str = ReadInput(); argv = ParseInput(str); if (argv[0] == NULL) { // do nothing if empty arguments } else if (CheckForIOandPipeErrors(argv)) { // do nothing if IO or pipe error } else if (CheckForBackgroundErrors(argv)) { // do nothing if background processing format error } else if (strcmp(argv[0], "exit") == 0) { BigFree(argv); printf("Exiting Shell...\n"); OnExitProcessQueueWait(); exit(0); } else if (strcmp(argv[0], "cd") == 0) { if (ArraySize(argv) <= 2) { if (ArraySize(argv) == 2) ChangeDirectory(argv[1]); else ChangeDirectory(getenv("HOME")); } else { printf("Too many arguments...\n"); } } else if (strcmp(argv[0], "limits") == 0) { if (ArraySize(argv) > 1) { argv = ArrayRemoveElement(argv, 0); Limits(argv); } } else if (strcmp(argv[0], "etime") == 0) { if (ArraySize(argv) > 1) { argv = ArrayRemoveElement(argv, 0); ETime(argv); } } else if (IsExecutable(argv[0])) { int background = VecContainsStr(argv, "&"); int I_loc = VecContainsStr(argv, "<"); int O_loc = VecContainsStr(argv, ">"); int pipe_count = ArgvCountSymbol(argv, "|"); if (I_loc != -1) { argv = ExecuteExternalWithInput(argv, I_loc, background); } else if (O_loc != -1) { argv = ExecuteExternalWithOutput(argv, O_loc, background); } else if (pipe_count > 0) { argv = ExecuteExternalWithPipe(argv, pipe_count, background); } else { char* cmd = ArgvToString(argv); if (background != -1) { argv = ArrayRemoveElement(argv, background); } ExecuteExternal(argv, background, cmd); free(cmd); } } BigFree(argv); } }
static void VerifyProcessOp(Item *procdata, Attributes a, Promise *pp) { int matches = 0, do_signals = true, out_of_range, killed = 0, need_to_restart = true; Item *killlist = NULL; CfDebug("VerifyProcessOp\n"); matches = FindPidMatches(procdata, &killlist, a, pp); /* promise based on number of matches */ if (a.process_count.min_range != CF_NOINT) /* if a range is specified */ { if (matches < a.process_count.min_range || matches > a.process_count.max_range) { cfPS(cf_error, CF_CHG, "", pp, a, " !! Process count for \'%s\' was out of promised range (%d found)\n", pp->promiser, matches); AddEphemeralClasses(a.process_count.out_of_range_define); out_of_range = true; } else { AddEphemeralClasses(a.process_count.in_range_define); cfPS(cf_verbose, CF_NOP, "", pp, a, " -> Process promise for %s is kept", pp->promiser); out_of_range = false; } } else { out_of_range = true; } if (!out_of_range) { return; } if (a.transaction.action == cfa_warn) { do_signals = false; } else { do_signals = true; } /* signal/kill promises for existing matches */ if (do_signals && matches > 0) { if (a.process_stop != NULL) { if (DONTDO) { cfPS(cf_error, CF_WARN, "", pp, a, " -- Need to keep process-stop promise for %s, but only a warning is promised", pp->promiser); } else { if (IsExecutable(GetArg0(a.process_stop))) { ShellCommandReturnsZero(a.process_stop, false); } else { cfPS(cf_verbose, CF_FAIL, "", pp, a, "Process promise to stop %s could not be kept because %s the stop operator failed", pp->promiser, a.process_stop); DeleteItemList(killlist); return; } } } killed = DoAllSignals(killlist, a, pp); } /* delegated promise to restart killed or non-existent entries */ need_to_restart = (a.restart_class != NULL) && (killed || matches == 0); DeleteItemList(killlist); if (!need_to_restart) { cfPS(cf_verbose, CF_NOP, "", pp, a, " -> No restart promised for %s\n", pp->promiser); return; } else { if (a.transaction.action == cfa_warn) { cfPS(cf_error, CF_WARN, "", pp, a, " -- Need to keep restart promise for %s, but only a warning is promised", pp->promiser); } else { cfPS(cf_inform, CF_CHG, "", pp, a, " -> Making a one-time restart promise for %s", pp->promiser); NewClass(a.restart_class); } } }
static Item *MonReSample(EvalContext *ctx, int slot, Attributes a, const Promise *pp, PromiseResult *result) { CfLock thislock; char eventname[CF_BUFSIZE]; char comm[20]; struct timespec start; FILE *fin = NULL; mode_t maskval = 0; if (a.measure.stream_type && strcmp(a.measure.stream_type, "pipe") == 0) { if (!IsExecutable(CommandArg0(pp->promiser))) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "%s promises to be executable but isn't\n", pp->promiser); *result = PromiseResultUpdate(*result, PROMISE_RESULT_FAIL); return NULL; } else { Log(LOG_LEVEL_VERBOSE, "Promiser string contains a valid executable (%s) - ok", CommandArg0(pp->promiser)); } } TransactionContext tc = { .expireafter = a.transaction.expireafter, .ifelapsed = MONITOR_RESTARTED ? 0 : a.transaction.ifelapsed, // Force a measurement if restarted }; CFSTARTTIME = time(NULL); thislock = AcquireLock(ctx, pp->promiser, VUQNAME, CFSTARTTIME, tc, pp, false); if (thislock.lock == NULL) { if (a.measure.history_type && (strcmp(a.measure.history_type, "log") == 0)) { DeleteItemList(ENTERPRISE_DATA[slot].output); ENTERPRISE_DATA[slot].output = NULL; } else { /* If static or time-series, and too soon or busy then use a cached value to avoid artificial gaps in the history */ } MONITOR_RESTARTED = false; return ENTERPRISE_DATA[slot].output; } else { DeleteItemList(ENTERPRISE_DATA[slot].output); ENTERPRISE_DATA[slot].output = NULL; Log(LOG_LEVEL_INFO, "Sampling \'%s\' ...(timeout=%d,owner=%ju,group=%ju)", pp->promiser, a.contain.timeout, (uintmax_t)a.contain.owner, (uintmax_t)a.contain.group); start = BeginMeasure(); CommandPrefix(pp->promiser, comm); if (a.contain.timeout != 0) { SetTimeOut(a.contain.timeout); } /* Stream types */ if (a.measure.stream_type && strcmp(a.measure.stream_type, "file") == 0) { long filepos = 0; struct stat sb; Log(LOG_LEVEL_VERBOSE, "Stream \"%s\" is a plain file", pp->promiser); if (stat(pp->promiser, &sb) == -1) { Log(LOG_LEVEL_INFO, "Unable to find stream '%s'. (stat: %s)", pp->promiser, GetErrorStr()); YieldCurrentLock(thislock); MONITOR_RESTARTED = false; return NULL; } fin = safe_fopen(pp->promiser, "r"); if (a.measure.growing) { filepos = Mon_RestoreFilePosition(pp->promiser); if (sb.st_size >= filepos) { fseek(fin, filepos, SEEK_SET); } } } else if (a.measure.stream_type && strcmp(a.measure.stream_type, "pipe") == 0) { Log(LOG_LEVEL_VERBOSE, "(Setting pipe umask to %jo)", (uintmax_t)a.contain.umask); maskval = umask(a.contain.umask); if (a.contain.umask == 0) { Log(LOG_LEVEL_VERBOSE, "Programming %s running with umask 0! Use umask= to set", pp->promiser); } // Mark: This is strange that we used these wrappers. Currently no way of setting these a.contain.owner = -1; a.contain.group = -1; a.contain.chdir = NULL; a.contain.chroot = NULL; // Mark: they were unset, and would fail for non-root(!) if (a.contain.shelltype == SHELL_TYPE_POWERSHELL) { #ifdef __MINGW32__ fin = cf_popen_powershell_setuid(pp->promiser, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, false); #else // !__MINGW32__ Log(LOG_LEVEL_ERR, "Powershell is only supported on Windows"); YieldCurrentLock(thislock); MONITOR_RESTARTED = false; return NULL; #endif // !__MINGW32__ } else if (a.contain.shelltype == SHELL_TYPE_USE) { fin = cf_popen_shsetuid(pp->promiser, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, false); } else { fin = cf_popensetuid(pp->promiser, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, false); } } /* generic file stream */ if (fin == NULL) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Couldn't open pipe to command '%s'. (cf_popen: %s)", pp->promiser, GetErrorStr()); *result = PromiseResultUpdate(*result, PROMISE_RESULT_FAIL); YieldCurrentLock(thislock); MONITOR_RESTARTED = false; return ENTERPRISE_DATA[slot].output; } size_t line_size = CF_BUFSIZE; char *line = xmalloc(line_size); for (;;) { ssize_t res = CfReadLine(&line, &line_size, fin); if (res == -1) { if (!feof(fin)) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_TIMEOUT, pp, a, "Sample stream '%s'. (fread: %s)", pp->promiser, GetErrorStr()); *result = PromiseResultUpdate(*result, PROMISE_RESULT_TIMEOUT); YieldCurrentLock(thislock); free(line); return ENTERPRISE_DATA[slot].output; } else { break; } } AppendItem(&(ENTERPRISE_DATA[slot].output), line, NULL); } free(line); if (a.measure.stream_type && strcmp(a.measure.stream_type, "file") == 0) { long fileptr = ftell(fin); fclose(fin); Mon_SaveFilePosition(pp->promiser, fileptr); } else if (a.measure.stream_type && strcmp(a.measure.stream_type, "pipe") == 0) { cf_pclose(fin); } } if (a.contain.timeout != 0) { alarm(0); signal(SIGALRM, SIG_DFL); } Log(LOG_LEVEL_INFO, "Collected sample of %s", pp->promiser); umask(maskval); YieldCurrentLock(thislock); MONITOR_RESTARTED = false; snprintf(eventname, CF_BUFSIZE - 1, "Sample(%s)", pp->promiser); EndMeasure(eventname, start); return ENTERPRISE_DATA[slot].output; } /************************************************************************************/ void HistoryUpdate(EvalContext *ctx, Averages newvals) { CfLock thislock; time_t now = time(NULL); /* We do this only once per hour - this should not be changed */ Log(LOG_LEVEL_VERBOSE, "(Updating long-term history database)"); Policy *history_db_policy = PolicyNew(); Promise *pp = NULL; Bundle *bp = PolicyAppendBundle(history_db_policy, NamespaceDefault(), "history_db_bundle", "agent", NULL, NULL); PromiseType *tp = BundleAppendPromiseType(bp, "history_db"); pp = PromiseTypeAppendPromise(tp, "the long term memory", (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, NULL); assert(pp); TransactionContext tc = { .expireafter = 0, .ifelapsed = 59 }; thislock = AcquireLock(ctx, pp->promiser, VUQNAME, now, tc, pp, false); if (thislock.lock == NULL) { PolicyDestroy(history_db_policy); return; } /* Refresh the class context of the agent */ EvalContextClear(ctx); DetectEnvironment(ctx); time_t t = SetReferenceTime(); UpdateTimeClasses(ctx, t); EvalContextHeapPersistentLoadAll(ctx); LoadSystemConstants(ctx); YieldCurrentLock(thislock); PolicyDestroy(history_db_policy); Mon_HistoryUpdate(CFSTARTTIME, &newvals); Mon_DumpSlowlyVaryingObservations(); } /************************************************************************************/ static Item *MonGetMeasurementStream(EvalContext *ctx, Attributes a, const Promise *pp, PromiseResult *result) { int i; for (i = 0; i < CF_DUNBAR_WORK; i++) { if (ENTERPRISE_DATA[i].path == NULL) { break; } if (strcmp(ENTERPRISE_DATA[i].path, pp->promiser) == 0) { ENTERPRISE_DATA[i].output = MonReSample(ctx, i, a, pp, result); return ENTERPRISE_DATA[i].output; } } ENTERPRISE_DATA[i].path = xstrdup(pp->promiser); ENTERPRISE_DATA[i].output = MonReSample(ctx, i, a, pp, result); return ENTERPRISE_DATA[i].output; }