/* Editor Settings: expandtabs and use 4 spaces for indentation * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: * * -*- mode: c, c-basic-offset: 4 -*- */ /* * Copyright Likewise Software 2004-2008 * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. You should have received a copy of the GNU General * Public License along with this program. If not, see * . * * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU * GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT * license@likewisesoftware.com */ /* * Copyright (C) Likewise Software. All rights reserved. * * Module Name: * * main.c * * Abstract: * * Likewise Security and Authentication Subsystem (LSASS) * * Test Program for stress testing AD Provider * * Authors: Sriram Nambakam (snambakam@likewisesoftware.com) * */ typedef void* (*PFN_LADS_THR_ROUTINE)(void* pData); #define _POSIX_PTHREAD_SEMANTICS 1 #include "includes.h" static void LADSInterruptHandler( int Signal ) { if (Signal == SIGINT) { raise(SIGTERM); } } int main( int argc, char* argv[] ) { DWORD dwError = 0; PSTR pszConfigFilePath = NULL; LsaLogLevel maxAllowedLogLevel = LSA_LOG_LEVEL_ERROR; dwError = LADSInitGlobals(); BAIL_ON_LSA_ERROR(dwError); dwError = LADSParseArgs( argc, argv, &pszConfigFilePath, &maxAllowedLogLevel); BAIL_ON_LSA_ERROR(dwError); dwError = LsaInitLogging_r( LADSGetProgramName(argv[0]), LSA_LOG_TARGET_CONSOLE, maxAllowedLogLevel, NULL); BAIL_ON_LSA_ERROR(dwError); dwError = LADSParseConfig( pszConfigFilePath); BAIL_ON_LSA_ERROR(dwError); if (LW_IS_NULL_OR_EMPTY_STR(gpszProviderLibPath)) { fprintf(stderr, "Error: The stress config file does not have a valid provider library path\n"); dwError = EINVAL; BAIL_ON_LSA_ERROR(dwError); } if (LW_IS_NULL_OR_EMPTY_STR(gpszProviderConfigFilePath)) { fprintf(stderr, "Error: The stress config file does not have a valid provider config file path\n"); dwError = EINVAL; BAIL_ON_LSA_ERROR(dwError); } gpAuthProvider->pszProviderLibpath = gpszProviderLibPath; dwError = LWNetExtendEnvironmentForKrb5Affinity(TRUE); BAIL_ON_LSA_ERROR(dwError); dwError = LADSBlockSelectedSignals(); BAIL_ON_LSA_ERROR(dwError); dwError = LADSInitAuthProvider( gpszProviderConfigFilePath, gpAuthProvider); BAIL_ON_LSA_ERROR(dwError); dwError = LADSStartWorkers(); BAIL_ON_LSA_ERROR(dwError); // Handle signals, blocking until we are supposed to exit. dwError = LADSHandleSignals(); BAIL_ON_LSA_ERROR(dwError); cleanup: LADSStopWorkers(); LADSShutdownAuthProvider(gpAuthProvider); LsaShutdownLogging_r(); LADSShutdownGlobals(); LW_SAFE_FREE_STRING(pszConfigFilePath); return (dwError); error: LSA_LOG_ERROR("Failed in stress-testing ad-provider [error code: %d]", dwError); goto cleanup; } DWORD LADSInitGlobals( VOID ) { DWORD dwError = 0; memset(&gLADSStressData[0], 0, sizeof(gLADSStressData)); return dwError; } PSTR LADSGetProgramName( PSTR pszFullProgramPath ) { if (pszFullProgramPath == NULL || *pszFullProgramPath == '\0') { return NULL; } // start from end of the string PSTR pszNameStart = pszFullProgramPath + strlen(pszFullProgramPath); do { if (*(pszNameStart - 1) == '/') { break; } pszNameStart--; } while (pszNameStart != pszFullProgramPath); return pszNameStart; } DWORD LADSShutdownGlobals( VOID ) { DWORD dwError = 0; DWORD iType = 0; for (iType = 0; iType < LADS_SENTINEL; iType++) { switch (iType) { case LADS_FIND_USER_BY_NAME: case LADS_FIND_GROUP_BY_NAME: if (gLADSStressData[iType].data.ppszNames) { DWORD i = 0; for (i = 0; i < gLADSStressData[iType].dwNumItems; i++) { LW_SAFE_FREE_STRING(gLADSStressData[iType].data.ppszNames[i]); } LwFreeMemory(gLADSStressData[iType].data.ppszNames); } break; case LADS_FIND_USER_BY_ID: LW_SAFE_FREE_MEMORY(gLADSStressData[iType].data.pUidArray); case LADS_FIND_GROUP_BY_ID: LW_SAFE_FREE_MEMORY(gLADSStressData[iType].data.pGidArray); default: break; } } LW_SAFE_FREE_STRING(gpszProviderLibPath); LW_SAFE_FREE_STRING(gpszProviderConfigFilePath); return dwError; } DWORD LADSParseArgs( int argc, char* argv[], PSTR* ppszConfigFilePath, LsaLogLevel* pMaxAllowedLogLevel ) { typedef enum { OpenMode = 0, ConfigFile, LogLevel, Done } ParseMode; DWORD dwError = 0; int iArg = 1; PSTR pszArg = NULL; ParseMode parse_mode = OpenMode; PSTR pszConfigFilePath = NULL; LsaLogLevel maxAllowedLogLevel = LSA_LOG_LEVEL_ERROR; do { pszArg = argv[iArg++]; if (pszArg == NULL || *pszArg == '\0') { break; } switch (parse_mode) { case OpenMode: { if ((strcmp(pszArg, "--help") == 0) || (strcmp(pszArg, "-h") == 0)) { ShowUsage(); exit(0); } else if (!strcasecmp(pszArg, "--config")) { parse_mode = ConfigFile; } else if (!strcmp(pszArg, "--loglevel")) { parse_mode = LogLevel; } else { parse_mode = Done; } break; } case ConfigFile: dwError = LwAllocateString( pszArg, &pszConfigFilePath); BAIL_ON_LSA_ERROR(dwError); parse_mode = OpenMode; break; case LogLevel: if (!strcasecmp(pszArg, "error")) { maxAllowedLogLevel = LSA_LOG_LEVEL_ERROR; } else if (!strcasecmp(pszArg, "warning")) { maxAllowedLogLevel = LSA_LOG_LEVEL_WARNING; } else if (!strcasecmp(pszArg, "info")) { maxAllowedLogLevel = LSA_LOG_LEVEL_INFO; } else if (!strcasecmp(pszArg, "verbose")) { maxAllowedLogLevel = LSA_LOG_LEVEL_VERBOSE; } else if (!strcasecmp(pszArg, "debug")) { maxAllowedLogLevel = LSA_LOG_LEVEL_DEBUG; } else { fprintf(stderr, "Error: Invalid log level [%s]", pszArg); ShowUsage(); exit(1); } break; case Done: break; } } while ((parse_mode != Done) && (iArg < argc)); if (LW_IS_NULL_OR_EMPTY_STR(pszConfigFilePath)) { ShowUsage(); exit(1); } *ppszConfigFilePath = pszConfigFilePath; *pMaxAllowedLogLevel = maxAllowedLogLevel; cleanup: return dwError; error: *ppszConfigFilePath = NULL; LW_SAFE_FREE_STRING(pszConfigFilePath); goto cleanup; } void ShowUsage( VOID ) { printf("Usage: test_stress_ad_provider --config \n"); } DWORD LADSBlockSelectedSignals( VOID ) { DWORD dwError = 0; sigset_t default_signal_mask; sigset_t old_signal_mask; sigemptyset(&default_signal_mask); sigaddset(&default_signal_mask, SIGINT); sigaddset(&default_signal_mask, SIGTERM); sigaddset(&default_signal_mask, SIGHUP); sigaddset(&default_signal_mask, SIGQUIT); sigaddset(&default_signal_mask, SIGPIPE); dwError = pthread_sigmask(SIG_BLOCK, &default_signal_mask, &old_signal_mask); BAIL_ON_LSA_ERROR(dwError); cleanup: return dwError; error: goto cleanup; } DWORD LADSHandleSignals( VOID ) { DWORD dwError = 0; struct sigaction action; sigset_t catch_signal_mask; sigset_t old_signal_mask; int which_signal = 0; BOOLEAN bWaitForSignals = TRUE; int sysRet = 0; // After starting up threads, we now want to handle SIGINT async // instead of using sigwait() on it. The reason for this is so // that a debugger (such as gdb) can break in properly. // See http://sourceware.org/ml/gdb/2007-03/msg00145.html and // http://bugzilla.kernel.org/show_bug.cgi?id=9039. memset(&action, 0, sizeof(action)); action.sa_handler = LADSInterruptHandler; sysRet = sigaction(SIGINT, &action, NULL); dwError = (sysRet != 0) ? errno : 0; BAIL_ON_LSA_ERROR(dwError); // Unblock SIGINT sigemptyset(&catch_signal_mask); sigaddset(&catch_signal_mask, SIGINT); dwError = pthread_sigmask(SIG_UNBLOCK, &catch_signal_mask, NULL); BAIL_ON_LSA_ERROR(dwError); // These should already be blocked... sigemptyset(&catch_signal_mask); sigaddset(&catch_signal_mask, SIGTERM); sigaddset(&catch_signal_mask, SIGHUP); sigaddset(&catch_signal_mask, SIGQUIT); sigaddset(&catch_signal_mask, SIGPIPE); dwError = pthread_sigmask(SIG_BLOCK, &catch_signal_mask, &old_signal_mask); BAIL_ON_LSA_ERROR(dwError); while (bWaitForSignals) { /* wait for a signal to arrive */ sigwait(&catch_signal_mask, &which_signal); LSA_LOG_WARNING("Received signal [%d]", which_signal); switch (which_signal) { case SIGINT: case SIGTERM: case SIGQUIT: { LADSStopProcess(); bWaitForSignals = FALSE; break; } case SIGPIPE: { LSA_LOG_DEBUG("Handled SIGPIPE"); break; } } } error: return dwError; } DWORD LADSInitAuthProvider( PCSTR pszConfigFilePath, PTEST_AUTH_PROVIDER pProvider ) { DWORD dwError = 0; PFNINITIALIZEPROVIDER pfnInitProvider = NULL; PCSTR pszError = NULL; PSTR pszProviderLibpath = NULL; if (LW_IS_NULL_OR_EMPTY_STR(pProvider->pszProviderLibpath)) { dwError = LW_ERROR_INVALID_AUTH_PROVIDER; BAIL_ON_LSA_ERROR(dwError); } pszProviderLibpath = pProvider->pszProviderLibpath; dlerror(); pProvider->pLibHandle = dlopen(pszProviderLibpath, RTLD_NOW | RTLD_GLOBAL); if (!pProvider->pLibHandle) { LSA_LOG_ERROR("Failed to open auth provider at path '%s'", pszProviderLibpath); pszError = dlerror(); if (!LW_IS_NULL_OR_EMPTY_STR(pszError)) { LSA_LOG_ERROR("%s", pszError); } dwError = LW_ERROR_INVALID_AUTH_PROVIDER; BAIL_ON_LSA_ERROR(dwError); } dlerror(); pfnInitProvider = (PFNINITIALIZEPROVIDER)dlsym( pProvider->pLibHandle, LSA_SYMBOL_NAME_INITIALIZE_PROVIDER); if (!pfnInitProvider) { LSA_LOG_ERROR("Ignoring invalid auth provider at path '%s'", pszProviderLibpath); pszError = dlerror(); if (!LW_IS_NULL_OR_EMPTY_STR(pszError)) { LSA_LOG_ERROR("%s", pszError); } dwError = LW_ERROR_INVALID_AUTH_PROVIDER; BAIL_ON_LSA_ERROR(dwError); } dwError = pfnInitProvider( &pProvider->pszName, &pProvider->pFnTable); BAIL_ON_LSA_ERROR(dwError); dwError = LADSValidateProvider(pProvider); BAIL_ON_LSA_ERROR(dwError); cleanup: return dwError; error: goto cleanup; } DWORD LADSValidateProvider( PTEST_AUTH_PROVIDER pProvider ) { if (!pProvider || !pProvider->pFnTable || !pProvider->pFnTable->pfnShutdownProvider || !pProvider->pFnTable->pfnOpenHandle || !pProvider->pFnTable->pfnCloseHandle || !pProvider->pFnTable->pfnServicesDomain || !pProvider->pFnTable->pfnAuthenticateUser || !pProvider->pFnTable->pfnValidateUser || !pProvider->pFnTable->pfnLookupUserByName || !pProvider->pFnTable->pfnLookupUserById || !pProvider->pFnTable->pfnBeginEnumUsers || !pProvider->pFnTable->pfnEnumUsers || !pProvider->pFnTable->pfnEndEnumUsers || !pProvider->pFnTable->pfnLookupGroupByName || !pProvider->pFnTable->pfnLookupGroupById || !pProvider->pFnTable->pfnGetGroupsForUser || !pProvider->pFnTable->pfnBeginEnumGroups || !pProvider->pFnTable->pfnEnumGroups || !pProvider->pFnTable->pfnEndEnumGroups || !pProvider->pFnTable->pfnChangePassword || !pProvider->pFnTable->pfnAddUser || !pProvider->pFnTable->pfnModifyUser || !pProvider->pFnTable->pfnDeleteUser || !pProvider->pFnTable->pfnAddGroup || !pProvider->pFnTable->pfnDeleteGroup || !pProvider->pFnTable->pfnOpenSession || !pProvider->pFnTable->pfnCloseSession || !pProvider->pFnTable->pfnGetNamesBySidList || !pProvider->pFnTable->pfnBeginEnumNSSArtefacts || !pProvider->pFnTable->pfnEnumNSSArtefacts || !pProvider->pFnTable->pfnEndEnumNSSArtefacts || !pProvider->pFnTable->pfnGetStatus || !pProvider->pFnTable->pfnFreeStatus ) { return LW_ERROR_INVALID_AUTH_PROVIDER; } return 0; } DWORD LADSStartWorkers( VOID ) { DWORD dwError = 0; DWORD iType = 0; for (iType = LADS_FIND_USER_BY_NAME; iType < LADS_SENTINEL; iType++) { DWORD iThread = 0; PFN_LADS_THR_ROUTINE pFnThrRoutine = NULL; if (!gLADSStressData[iType].dwNumThreads) { continue; } dwError = LwAllocateMemory( sizeof(pthread_t) * gLADSStressData[iType].dwNumThreads, (PVOID*)&gLADSStressData[iType].pThreadArray); BAIL_ON_LSA_ERROR(dwError); switch (gLADSStressData[iType].type) { case LADS_FIND_USER_BY_NAME: pFnThrRoutine = &LADSFindUserByName; break; case LADS_FIND_USER_BY_ID: pFnThrRoutine = &LADSFindUserById; break; case LADS_ENUM_USERS: pFnThrRoutine = &LADSEnumUsers; break; case LADS_FIND_GROUP_BY_ID: pFnThrRoutine = &LADSFindGroupById; break; case LADS_FIND_GROUP_BY_NAME: pFnThrRoutine = &LADSFindGroupByName; break; case LADS_ENUM_GROUPS: pFnThrRoutine = &LADSEnumGroups; break; default: break; } for (iThread = 0; iThread < gLADSStressData[iType].dwNumThreads; iThread++) { dwError = pthread_create( &gLADSStressData[iType].pThreadArray[iThread], NULL, pFnThrRoutine, NULL); BAIL_ON_LSA_ERROR(dwError); } } cleanup: return dwError; error: goto cleanup; } DWORD LADSStopWorkers( VOID ) { DWORD dwError = 0; DWORD iType = 0; LADSStopProcess(); for (iType = LADS_FIND_USER_BY_NAME; iType < LADS_SENTINEL; iType++) { DWORD iThread = 0; if (!gLADSStressData[iType].dwNumThreads) { continue; } for (iThread = 0; iThread < gLADSStressData[iType].dwNumThreads; iThread++) { PVOID pThreadResult = NULL; if (gLADSStressData[iType].pThreadArray[iThread]) { pthread_join(gLADSStressData[iType].pThreadArray[iThread], &pThreadResult); } } LwFreeMemory(gLADSStressData[iType].pThreadArray); } return dwError; } DWORD LADSShutdownAuthProvider( PTEST_AUTH_PROVIDER pProvider ) { if (pProvider) { if (pProvider->pFnTable && pProvider->pFnTable->pfnShutdownProvider) { pProvider->pFnTable->pfnShutdownProvider(); } if (pProvider->pLibHandle) { dlclose(pProvider->pLibHandle); } } return 0; } VOID LADSStopProcess( VOID ) { pthread_mutex_lock(&gExitLock); gbExit = TRUE; pthread_mutex_unlock(&gExitLock); } BOOLEAN LADSProcessShouldStop( VOID ) { BOOLEAN bValue = FALSE; pthread_mutex_lock(&gExitLock); bValue = gbExit; pthread_mutex_unlock(&gExitLock); return bValue; }