/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ /*--------------------------------------------------------------------------- * Module: SYSCommon.c *-------------------------------------------------------------------------*/ #include #include "SYSCommon.h" #include #include #include #include #include #ifndef WIN32 #include #endif #ifdef WIN32 #include #include /* for malloc, calloc, and free */ #elif defined K_LINUX_PLATFORM #include /* it includes usleep(us) */ #include #include #else /* * Directory traversal code is not yet available for Solaris. * If such code will need to be written, then it will probably use ftw.h. */ #endif #ifdef K_SOLARIS_PLATFORM /* For K_AdjustLocalClock */ #include /* For K_SetRootPassword */ #define __EXTENSIONS__ /* to expose flockfile and friends in stdio.h */ #include #include #include #include #include #include #include #include #include #include #include #include #endif #ifdef K_LINUX_PLATFORM extern int pthread_mutexattr_settype __P ((pthread_mutexattr_t *__attr, int __kind)); #endif #ifdef K_HPUX_PLATFORM int64 atoll(const char *str) { int64 tmp = 0; sscanf(str, "%lld", &tmp); return tmp; } #endif /*--------------------------------------------------------------------------- * Function: K_CreateThread * * Description: * Thread creation function "CreateThread" takes a thread function * and its parameter to create a thread. It also has a Boolean * parameter to indicate if the thread is detached or joinable. * A new thread's handle is returned through the output parameter. * * Input * ----- * i_pFunc Function pointer of the thread function * i_pvData The point of the parameter passed to the thread function * i_bIsDetached The thread is detached or not * (Note: It is not supported on Win32) * * Output * ------ * o_pNewThread The Thread handle * * Return value Error code * *--------------------------------------------------------------------------*/ int K_CreateThread(K_ThreadFunc i_pFunc, void *i_pvData, int i_bIsDetached, K_THREAD_HANDLE *o_pNewThread) { int iOK = K_SYS_OK; int iReturn = 0; #ifdef WIN32 { unsigned id; *o_pNewThread = (HANDLE)_beginthreadex(NULL, 0, (int (_stdcall *) (void *vpData))i_pFunc, i_pvData, 0, &id); if(*o_pNewThread == 0) { #ifdef SYS_DEBUG printf(" (%s, %d): error creating pthread, error = %d\n", __FILE__, __LINE__, iReturn); #endif return K_SYS_ERR_CREATE_THREAD; } return K_SYS_OK; } #else pthread_attr_t attr; iReturn = pthread_attr_init(&attr); if ( iReturn == 0 ) { iReturn = pthread_attr_setdetachstate(&attr, (i_bIsDetached) ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE); } #ifdef UNIX if ( iReturn == 0 ) { iReturn = pthread_attr_setstacksize(&attr, 1024*1024); } #endif if ( iReturn == 0 ) { iReturn = pthread_create(o_pNewThread, &attr, (void *(*)(void *)) i_pFunc, i_pvData); } if ( iReturn == 0 ) { iReturn = pthread_attr_destroy(&attr); } // TODO: Log error? if ( iReturn ) { #ifdef SYS_DEBUG printf(" (%s, %d): error creating pthread, error = %d\n", __FILE__, __LINE__, iReturn); #endif iOK = K_SYS_ERR_CREATE_THREAD; } return iOK; #endif } /*--------------------------------------------------------------------------- * Function: K_JoinThread * * Description: * Thread joining function is called when the current thread * waits another thread to terminate. * * Input * ----- * i_hThread The thread handle of the to-be-joined thread * * Output * ------ * (none) * * Return value Error code * *--------------------------------------------------------------------------*/ int K_JoinThread(K_THREAD_HANDLE i_hThread) { int iOK = K_SYS_OK; #ifdef WIN32 WaitForSingleObject(i_hThread, INFINITE); #else { int iReturn; iReturn = pthread_join(i_hThread, NULL); if ( iReturn ) { #ifdef SYS_DEBUG printf(" (%s, %d): error creating pthread, error = %d\n", __FILE__, __LINE__, iReturn); #endif iOK = K_SYS_ERR_JOIN_THREAD; } } #endif return iOK; } /*--------------------------------------------------------------------------- * Function: K_GetCurrentThreadId * * Description: * Returns the thread ID of the current thread. * * Input * ----- * (none) * * Output * ------ * (none) * * Return value The thread ID * *--------------------------------------------------------------------------*/ int K_GetCurrentThreadId() { #ifdef WIN32 return GetCurrentThreadId(); #else return pthread_self(); #endif } /*--------------------------------------------------------------------------- * Function: K_CreateMutex * * Description: * The mutex creation function creates a mutex according to the given * mutex type, and returns the mutex handle to the output parameter. * * Input * ----- * i_bIsRecursive Indication whether the mutex can be entered recursively * * Output * ------ * o_phandle the handle pointer to the mutex * * Return value Error Code * *--------------------------------------------------------------------------*/ int K_CreateMutex( K_MUTEX_HANDLE *o_phandle ) { int iOK = K_SYS_OK; BOOL bIsRecursive = 1; // this used to be an input -- but why do we want this to be optional? #ifdef WIN32 { *o_phandle = (WIN32Mutex *)malloc(sizeof(WIN32Mutex)); if(*o_phandle == NULL) { return K_SYS_ERR_NO_MEMORY; } (*o_phandle)->m_bIsRecursive = bIsRecursive; if(bIsRecursive) { InitializeCriticalSection(&((*o_phandle)->m_stCriticalSection)); } else { (*o_phandle)->m_handle = CreateMutex(NULL, FALSE, NULL); } } #else { int iType; pthread_mutexattr_t attr; if ( pthread_mutexattr_init(&attr) ) { return K_SYS_ERR_COND; } if(bIsRecursive) { iType = #ifdef K_LINUX_PLATFORM PTHREAD_MUTEX_RECURSIVE_NP; #else PTHREAD_MUTEX_RECURSIVE; #endif if ( pthread_mutexattr_settype(&attr, iType) ) { return K_SYS_ERR_COND; } } *o_phandle = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); if(*o_phandle == NULL) { return K_SYS_ERR_NO_MEMORY; } if ( pthread_mutex_init(*o_phandle, &attr) ) { return K_SYS_ERR_COND; } if ( pthread_mutexattr_destroy(&attr) ) { return K_SYS_ERR_COND; } } #endif return iOK; } /*--------------------------------------------------------------------------- * Function: K_LockMutex * * Description: * K_LockMutex is used to lock the mutex, and K_UnlockMutex is * used to unlock it. * * Input * ----- * i_handle the mutex handle * * Output * ------ * (none) * * Return value Error Code * *--------------------------------------------------------------------------*/ int K_LockMutex(K_MUTEX_HANDLE i_handle) { int iOK = K_SYS_OK; #ifdef WIN32 if(i_handle->m_bIsRecursive) { EnterCriticalSection(&(i_handle->m_stCriticalSection)); } else { WaitForSingleObject(i_handle->m_handle, INFINITE); } #else if ( pthread_mutex_lock(i_handle) ) { return K_SYS_ERR_COND; } #endif return iOK; // TODO: better error handling } /*--------------------------------------------------------------------------- * Function: K_UnlockMutex * * Description: * K_UnlockMutex is used to unlock the lock. * * Input * ----- * i_handle the mutex handle * * Output * ------ * (none) * * Return value Error Code * *--------------------------------------------------------------------------*/ int K_UnlockMutex(K_MUTEX_HANDLE i_handle) { int iOK = K_SYS_OK; #ifdef WIN32 if(i_handle->m_bIsRecursive) { LeaveCriticalSection(&(i_handle->m_stCriticalSection)); } else { ReleaseMutex(i_handle->m_handle); } #else if ( pthread_mutex_unlock(i_handle) ) { return K_SYS_ERR_COND; } #endif return iOK; // TODO: better error handling } /*--------------------------------------------------------------------------- * Function: K_DestroyMutex * * Description: * When a mutex is no longer needed, K_DestroyMutex must be called * to destroy it. * * Input * ----- * i_handle the mutex handle * Output * ------ * (none) * * Return value Error Code * *--------------------------------------------------------------------------*/ int K_DestroyMutex(K_MUTEX_HANDLE i_handle) { int iOK = K_SYS_OK; #ifdef WIN32 if(i_handle->m_bIsRecursive) { DeleteCriticalSection(&(i_handle->m_stCriticalSection)); } else { CloseHandle(i_handle->m_handle); } free(i_handle); #else pthread_mutex_destroy(i_handle); free(i_handle); #endif return iOK; // TODO: better error handling } /*--------------------------------------------------------------------------- * Function: K_InitConditionalVariable * * Description: * This function initializes a conditional variable. Upon successful * completion, the new condition variable is returned via the condition * parameter, and 0 is returned. Otherwise, an error code is returned. * * Input * ----- * i_pCond the pointer to the conditional variable which is to be * initialized * * Output * ------ * (none) * * Return value Error Code * *--------------------------------------------------------------------------*/ int K_InitConditionalVariable (K_ConditionalVariable * i_pCond) { int iOK = K_SYS_OK; #ifdef WIN32 i_pCond->m_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); i_pCond->m_hMutex = CreateMutex(NULL, FALSE, NULL); i_pCond->m_iSignalAll = 0; i_pCond->m_iNumWaiting = 0; #else if ( pthread_cond_init(i_pCond, NULL) ) { return K_SYS_ERR_COND; } #endif return iOK; } /*--------------------------------------------------------------------------- * Function: K_DestroyConditionalVariable * * Description: * This function destroys a conditional variable. Upon successful * completion, the condition variable is destroyed, and 0 is returned. * Otherwise, an error code is returned. * After deletion of the condition variable, the condition parameter * is not valid until it is initialized again by a call to the * K_InitConditionalVariable subroutine. * * Input * ----- * i_pCond the pointer to the conditional variable which is to be * destroyed * Output * ------ * (none) * * Return value Error Code * *--------------------------------------------------------------------------*/ int K_DestroyConditionalVariable(K_ConditionalVariable * i_pCond) { int iOK = K_SYS_OK; #ifdef WIN32 CloseHandle(i_pCond->m_hMutex); CloseHandle(i_pCond->m_hEvent); #else if ( pthread_cond_destroy(i_pCond) ) { return K_SYS_ERR_COND; } #endif return iOK; } /*--------------------------------------------------------------------------- * Function: K_WaitConditionalVariable * * Description: * This function is used to block on a condition variable. * They are called with mutex locked by the calling thread or undefined * behaviour will result. * * Input * ----- * i_pCond the pointer to the conditional variable * i_handle the companion mutex handle * * Output * ------ * (none) * * Return value Error Code * *--------------------------------------------------------------------------*/ int K_WaitConditionalVariable(K_ConditionalVariable * i_pCond, K_MUTEX_HANDLE i_handle) { int iOK = K_SYS_OK; #ifdef WIN32 DWORD res; while (1) { iOK = WaitForSingleObject(i_pCond->m_hMutex, INFINITE); if (iOK != WAIT_OBJECT_0) { return K_SYS_ERR_COND; } i_pCond->m_iNumWaiting++; ReleaseMutex(i_pCond->m_hMutex); K_UnlockMutex(i_handle); res = WaitForSingleObject(i_pCond->m_hEvent, INFINITE); i_pCond->m_iNumWaiting--; if (res != WAIT_OBJECT_0) { ReleaseMutex(i_pCond->m_hMutex); return K_SYS_ERR_COND; } if (i_pCond->m_iSignalAll) { if (i_pCond->m_iNumWaiting == 0) { ResetEvent(i_pCond->m_hEvent); } break; } if (i_pCond->m_iSignalled) { i_pCond->m_iSignalled = 0; ResetEvent(i_pCond->m_hEvent); break; } ReleaseMutex(i_pCond->m_hMutex); } K_LockMutex(i_handle); return K_SYS_OK; #else if ( pthread_cond_wait(i_pCond, i_handle) ) { return K_SYS_ERR_COND; } #endif return iOK; // TODO: better error handling } /*--------------------------------------------------------------------------- * Function: K_SignalConditionalVariable * * Description: * This function is used to restart one of the threads that are waiting on * the condition variable. If no threads are waiting on it, nothing happens. * If several threads are waiting on it, exactly one is restarted. * * Input * ----- * i_pCond the pointer to the conditional variable * * Output * ------ * (none) * * Return value Error Code * *--------------------------------------------------------------------------*/ int K_SignalConditionalVariable(K_ConditionalVariable * i_pCond) { int iOK = K_SYS_OK; #ifdef WIN32 int iReturn; iReturn = WaitForSingleObject(i_pCond->m_hMutex, INFINITE); if (iReturn != WAIT_OBJECT_0) { return K_SYS_ERR_COND; } i_pCond->m_iSignalled = 1; iReturn = SetEvent(i_pCond->m_hEvent); if (iReturn == 0) { iOK = K_SYS_ERR_COND; } ReleaseMutex(i_pCond->m_hMutex); return iOK; #else if ( pthread_cond_signal(i_pCond) ) { return K_SYS_ERR_COND; } #endif return iOK; } /*--------------------------------------------------------------------------- * Function: K_BroadcastConditionalVariable * * Description: * This function is used to restart all threads that are waiting on * the condition variable. * * Input * ----- * i_pCond the pointer to the conditional variable * * Output * ------ * (none) * * Return value Error Code * *--------------------------------------------------------------------------*/ int K_BroadcastConditionalVariable(K_ConditionalVariable * i_pCond) { int iOK = K_SYS_OK; #ifdef WIN32 int iReturn; iReturn = WaitForSingleObject(i_pCond->m_hMutex, INFINITE); if (iReturn != WAIT_OBJECT_0) { return K_SYS_ERR_COND; } i_pCond->m_iSignalled = 1; i_pCond->m_iSignalAll = 1; iReturn = SetEvent(i_pCond->m_hEvent); if (iReturn == 0) { iOK = K_SYS_ERR_COND; } ReleaseMutex(i_pCond->m_hMutex); return iOK; #else if ( pthread_cond_broadcast(i_pCond) ) { return K_SYS_ERR_COND; } #endif return iOK; } /*--------------------------------------------------------------------------- * Function: K_Sleep * * Description: * Sleep for a given period in given milliseconds. * * Input * ----- * i_ms milliseconds * * Output * ------ * (none) * * Return value (none) * *--------------------------------------------------------------------------*/ void K_Sleep(int i_ms) { #ifdef WIN32 Sleep(i_ms); #else usleep(i_ms * 1000); #endif } /*--------------------------------------------------------------------------- * Function: K_GetTickCount * * Description: * The K_GetTickCount function retrieves the number of * milliseconds that have elapsed since the system was started. * * Input * ----- * (none) * * Output * ------ * (none) * * Return value the elasped milliseconds since the system was started * *--------------------------------------------------------------------------*/ unsigned int K_GetTickCount() { #ifdef WIN32 return (unsigned int)GetTickCount(); #else { struct timeval tv; gettimeofday( &tv, NULL ); /* this will rollover ~ every 49.7 days dont surprise when it returns negative values, since we are only interested in using sth like "tickCount2 - tickCount1" to get the time interval */ return ( tv.tv_sec * 1000 ) + ( tv.tv_usec / 1000 ); } #endif } /*--------------------------------------------------------------------------- * Function: K_AdjustClock * * Description: * The K_AdjustClock function immediately adjusts the system clock by * the given number of seconds. A positive number adjusts the system * clock forward; a negative number adjusts the system clock backward. * * Input * ----- * i_iAdjustmentInSeconds Number of seconds by which to adjust the * system clock * Output * ------ * (none) * * Return value 1 if successful, 0 on error * *--------------------------------------------------------------------------*/ int K_AdjustClock( long i_iAdjustmentInSeconds ) { #ifndef WIN32 struct timeval stDateTime; if ( 0 != gettimeofday(&stDateTime, NULL) ) { return FALSE; } stDateTime.tv_sec += i_iAdjustmentInSeconds; if ( 0 != settimeofday(&stDateTime, NULL) ) { return FALSE; } #else // TODO: implement for Windows return FALSE; #endif return TRUE; } /*--------------------------------------------------------------------------- * Function: K_IsLittleEndian * * Description: * Checks to see whether this platform uses little endian integer * representation. * * Input * ----- * (none) * * Output * ------ * (none) * * Return value 1 for little endian * *--------------------------------------------------------------------------*/ int K_IsLittleEndian() { short iWord = 0x4321; return ((*(unsigned char*)&iWord) == 0x21); } /*--------------------------------------------------------------------------- * Function: K_FileLength32 * * Description: * Gets the size in bytes of the file associated with the given FILE pointer. * * Input * ----- * i_fpFile File handle * * Output * ------ * (none) * * Return value File size in bytes, or -1L on error * *--------------------------------------------------------------------------*/ long K_FileLength32( FILE* i_fpFile ) { #ifdef WIN32 int iFileDescriptor = _fileno( i_fpFile ); struct _stat stStat; if ( _fstat(iFileDescriptor, &stStat) != 0) { // error return -1L; } #else int iFileDescriptor = fileno( i_fpFile ); struct stat stStat; if ( fstat(iFileDescriptor, &stStat) != 0) { // error return -1L; } #endif return stStat.st_size; } /*--------------------------------------------------------------------------- * Function: K_StringCompareNoCase * * Description: * Compares the two given strings insensitive to case. * * Input * ----- * i_sString1 First string * i_sString2 Second string * * Output * ------ * (none) * * Return value 0 if identical, -1 if first string is less than second * string, or 1 if first string is greater than second * *--------------------------------------------------------------------------*/ int K_StringCompareNoCase( const char* i_sString1, const char* i_sString2 ) { #ifdef WIN32 return _stricmp( i_sString1, i_sString2 ); #else return strcasecmp( i_sString1, i_sString2 ); #endif } /*--------------------------------------------------------------------------- * Function: K_StringCompareNoCaseWide * * Description: * Compares the two given wide strings insensitive to case. * * Input * ----- * i_wsString1 First wide string * i_wsString2 Second wide string * * Output * ------ * (none) * * Return value 0 if identical, -1 if first string is less than second * string, or 1 if first string is greater than second * *--------------------------------------------------------------------------*/ int K_StringCompareNoCaseWide( const wchar_t* i_wsString1, const wchar_t* i_wsString2 ) { #ifdef WIN32 return _wcsicmp( i_wsString1, i_wsString2 ); #elif defined K_SOLARIS_PLATFORM return wscasecmp( i_wsString1, i_wsString2 ); #else return wcscasecmp( i_wsString1, i_wsString2 ); #endif } /*--------------------------------------------------------------------------- * Function: K_CreateDirectory * * Description: * Creates a directory with the given path name. * * Input * ----- * i_sDirectoryName Directory name * * Output * ------ * (none) * * Return value 0 on success, -1 on failure * *--------------------------------------------------------------------------*/ int K_CreateDirectory( const char* i_sDirectoryName ) { // TODO: make this build all parent directories as well. #ifdef WIN32 if ( CreateDirectoryA( i_sDirectoryName, NULL ) ) { return 0; } else { DWORD dwError = GetLastError(); return ( dwError == ERROR_ALREADY_EXISTS ) ? 0 : (dwError ? dwError : -1); } #else if ( mkdir( i_sDirectoryName, S_IRWXU ) == 0 ) { return 0; } else { return ( errno == EEXIST ) ? 0 : (errno ? errno : -1); } #endif } /*--------------------------------------------------------------------------- * Function: K_DeleteFile * * Description: * Deletes the given file. * * Input * ----- * i_sFilename Name of file to delete * * Output * ------ * (none) * * Return value 0 on success, errno on failure * *--------------------------------------------------------------------------*/ int K_DeleteFile( const char* i_sFilename ) { int bSuccess = 0; bSuccess = #ifdef WIN32 _unlink( #else unlink( #endif i_sFilename ) == 0; return bSuccess ? 0 : errno; } /*--------------------------------------------------------------------------- * Function: K_ReadFile * * Description: * Reads from the given file and passes the bytes read back to the output * parameter. The caller must deallocate o_ppFileData using free(). * * Input * ----- * i_sFilename Name of file from which to read * * Output * ------ * o_ppFileData Pointer to bytes read * * Return value Number of bytes read on success, -1 on failure * *--------------------------------------------------------------------------*/ int K_ReadFile( const char* i_sFilename, unsigned char** o_ppFileData ) { FILE* pFile = 0; long iFileSize = 0; if ( !i_sFilename || (strlen(i_sFilename) <= 0) || !o_ppFileData ) { return -1; } *o_ppFileData = 0; // Open the file pFile = fopen( i_sFilename, "rb" ); if ( !pFile ) { return -1; } // Determine the file size if ( fseek( pFile, 0, SEEK_END ) ) { (void) fclose( pFile ); return -1; } iFileSize = ftell( pFile ); if ( iFileSize < 0 ) { (void) fclose( pFile ); return -1; } else if ( iFileSize == 0 ) { (void) fclose( pFile ); return 0; } if ( fseek( pFile, 0, SEEK_SET ) ) { (void) fclose( pFile ); return -1; } *o_ppFileData = (unsigned char*)malloc( iFileSize ); if ( !*o_ppFileData ) { // Out of memory. (void) fclose( pFile ); return -1; } if ( iFileSize != (long)fread( *o_ppFileData, 1, iFileSize, pFile ) ) { free( *o_ppFileData ); *o_ppFileData = 0; (void) fclose( pFile ); return -1; } (void) fclose( pFile ); return iFileSize; } /*--------------------------------------------------------------------------- * Function: K_ReadFileString * * Description: * Reads from the given file and passes the bytes read back to the output * parameter, appending these bytes with a null terminator. There is no * guarantee that there are no non-text characters in the returned "string". * The caller must deallocate o_ppFileData using free(). * * Input * ----- * i_sFilename Name of file from which to read * * Output * ------ * o_psFileDataString Pointer to bytes read * * Return value Number of bytes read (including null terminator) on * success (0 if file is empty), -1 on failure * *--------------------------------------------------------------------------*/ int K_ReadFileString( const char* i_sFilename, char** o_psFileDataString ) { unsigned char* pFileData = 0; int iFileSize = 0; *o_psFileDataString = 0; iFileSize = K_ReadFile( i_sFilename, &pFileData ); if ( iFileSize <= 0 ) { return iFileSize; } *o_psFileDataString = (char*)malloc( iFileSize+1 ); if ( !*o_psFileDataString ) { // Out of memory. if ( pFileData ) { free( pFileData ); } return -1; } memcpy( *o_psFileDataString, pFileData, iFileSize ); (*o_psFileDataString)[iFileSize] = '\0'; if ( pFileData ) { free( pFileData ); } return iFileSize+1; } /*--------------------------------------------------------------------------- * Function: K_WriteFile * * Description: * Writes the given bytes to the given file. * * Input * ----- * i_sFilename Name of file to which to write * i_pFileData Bytes to write * i_iFileDataSize Number of bytes to write * * Output * ------ * (none) * * Return value 0 on success, errno or -1 (generic error) on failure * *--------------------------------------------------------------------------*/ int K_WriteFile( const char* i_sFilename, const unsigned char* i_pFileData, int i_iFileDataSize ) { FILE* pFile = 0; if ( !i_sFilename || (strlen(i_sFilename) <= 0) || (!i_pFileData && (i_iFileDataSize > 0)) || (i_iFileDataSize < 0) ) { return -1; } pFile = fopen( i_sFilename, "wb" ); if ( !pFile ) { int iError = errno; return (iError != 0) ? iError : -1; } if ( i_iFileDataSize > 0 ) { if ( i_iFileDataSize != (int)fwrite( i_pFileData, 1, i_iFileDataSize, pFile ) ) { int iError = ferror( pFile ); (void) fclose( pFile ); return (iError != 0) ? iError : -1; } } (void) fclose( pFile ); return 0; } /*--------------------------------------------------------------------------- * Function: K_WriteFileString * * Description: * Writes the given null-terminated bytes to the given file. The null * terminator itself is not written to the file. * * Input * ----- * i_sFilename Name of file to which to write * i_sFileData Bytes to write * * Output * ------ * (none) * * Return value 0 on success, errno or -1 (generic error) on failure * *--------------------------------------------------------------------------*/ int K_WriteFileString( const char* i_sFilename, const char* i_sFileData ) { if ( !i_sFilename || (strlen(i_sFilename) <= 0) || !i_sFileData || (strlen(i_sFileData) <= 0) ) { return -1; } return K_WriteFile( i_sFilename, (const unsigned char*)i_sFileData, strlen(i_sFileData) ); } /*--------------------------------------------------------------------------- * Function: K_FileExists * * Description: * Checks to see whehter the given file exists. * * Input * ----- * i_sFilename Name of file to check * * Output * ------ * (none) * * Return value 1 if file exists, 0 if not, -1 on failure * *--------------------------------------------------------------------------*/ int K_FileExists( const char* i_sFilename ) { FILE* pFile = 0; if ( !i_sFilename || (strlen(i_sFilename) <= 0) ) { return -1; } pFile = fopen( i_sFilename, "r+" ); if ( !pFile ) { if ( errno == ENOENT ) { return 0; } return -1; } (void) fclose( pFile ); return 1; } /*--------------------------------------------------------------------------- * Function: K_CopyFile * * Description: * Reads from the given source file and writes these bytes to the given * destination file. * * Input * ----- * i_sSrcFilename Name of file from which to read * i_sDestFilename Name of file to which to write * * Output * ------ * o_pbFileExists Non-zero if the destination file already exists * * Return value 0 on success, errno or -1 (generic error) on failure * *--------------------------------------------------------------------------*/ int K_CopyFile( const char* i_sSrcFilename, const char* i_sDestFilename, int* o_pbFileExists ) { unsigned char* pFileData = 0; int iFileSize = 0; int iError, iFileExists; if ( !i_sSrcFilename || (strlen(i_sSrcFilename) <= 0) || !i_sDestFilename || (strlen(i_sDestFilename) <= 0) || !o_pbFileExists ) { return -1; } *o_pbFileExists = 0; iFileExists = K_FileExists( i_sDestFilename ); if ( iFileExists < 0 ) { iError = errno; return (iError == 0) ? -1 : iError; } else if ( iFileExists > 0 ) { *o_pbFileExists = 1; return -1; } iFileSize = K_ReadFile( i_sSrcFilename, &pFileData ); if ( iFileSize < 0 ) { iError = errno; return (iError == 0) ? -1 : iError; } iError = K_WriteFile( i_sDestFilename, pFileData, iFileSize ); if ( pFileData ) { free( pFileData ); } return iError; } #ifdef K_LINUX_PLATFORM static int fts_compare( const FTSENT** i_ppF1, const FTSENT** i_ppF2 ) { return strcmp( (*i_ppF1)->fts_name, (*i_ppF2)->fts_name ); } #else /* * Directory traversal code is not yet available for Solaris. * If such code will need to be written, then it will probably use ftw.h. */ #endif /* * TODO: Set up functions for platform-specific find-file operations to * help clean up the code below. */ typedef struct K_FindInfo { #ifdef WIN32 struct _finddata_t m_stFindData; long m_hFile; #elif defined K_LINUX_PLATFORM FTS* m_pFTS; FTSENT* m_pFTSENT; #else /* * Directory traversal code is not yet available for Solaris. * If such code will need to be written, then it will probably use ftw.h. */ int unused; #endif } K_FindInfo; // Memory for filename is held in i_pFindInfo. const char* K_GetFilenameFromInfo( const K_FindInfo* i_pFindInfo ) { if( !i_pFindInfo ) { return 0; } #ifdef WIN32 return i_pFindInfo->m_stFindData.name; #elif defined K_LINUX_PLATFORM return i_pFindInfo->m_pFTSENT->fts_name; #else /* * Directory traversal code is not yet available for Solaris. * If such code will need to be written, then it will probably use ftw.h. */ FATAL_ASSERT( 0 ); return 0; #endif } // Forward declarations int K_FindFileNext( K_FindInfo* io_pFindInfo ); void K_FindFileClose( K_FindInfo* io_pFindInfo ); // Returns 0 if successful, 1 if not found, -1 if error. // If not error, K_FindFileClose must be called. // o_pFindInfo must not be null. int K_FindFileFirst( const char* i_sDirectoryName, K_FindInfo* o_pFindInfo ) { #ifdef WIN32 char* sSearchString = 0; int iSearchStringIndex = 0; #endif if ( !i_sDirectoryName || (strlen(i_sDirectoryName) <= 0) || !o_pFindInfo ) { return -1; } #ifdef WIN32 memset( o_pFindInfo, 0, sizeof(K_FindInfo) ); iSearchStringIndex = strlen(i_sDirectoryName); if ( i_sDirectoryName[iSearchStringIndex-1] == PATH_SEPARATOR ) { iSearchStringIndex += 2; } else { iSearchStringIndex += 3; } sSearchString = (char*)calloc( iSearchStringIndex, 1 ); if ( !sSearchString ) { return -1; } strcpy( sSearchString, i_sDirectoryName ); iSearchStringIndex--; sSearchString[iSearchStringIndex] = '\0'; iSearchStringIndex--; sSearchString[iSearchStringIndex] = '*'; iSearchStringIndex--; sSearchString[iSearchStringIndex] = PATH_SEPARATOR; o_pFindInfo->m_hFile = _findfirst( sSearchString, &o_pFindInfo->m_stFindData ); free( sSearchString ); if ( o_pFindInfo->m_hFile == -1 ) { if ( errno == ENOENT ) { return 1; } else { return -1; } } #elif defined K_LINUX_PLATFORM memset( o_pFindInfo, 0, sizeof(K_FindInfo) ); o_pFindInfo->m_pFTS = fts_open( aPath, FTS_PHYSICAL | FTS_NOSTAT, fts_compare ); if ( !o_pFindInfo->m_pFTS ) { return -1; } o_pFindInfo->m_pFTSENT = fts_read( o_pFindInfo->m_pFTS ); if ( !o_pFindInfo->m_pFTSENT ) { if ( errno == 0 ) { return 1; } else { fts_close( o_pFindInfo->m_pFTS ); return -1; } } #else /* * Directory traversal code is not yet available for Solaris. * If such code will need to be written, then it will probably use ftw.h. */ #endif // If what we found is not actually a file, get the next hit. #ifdef WIN32 if ( (o_pFindInfo->m_stFindData.attrib & _A_SUBDIR) ) #elif defined K_LINUX_PLATFORM if ( !(o_pFindInfo->m_pFTSENT->fts_info & FTS_F) ) #else /* * Directory traversal code is not yet available for Solaris. * If such code will need to be written, then it will probably use ftw.h. */ #endif { int iNextReturn = K_FindFileNext( o_pFindInfo ); if ( iNextReturn < 0 ) { K_FindFileClose( o_pFindInfo ); return -1; } else { return iNextReturn; } } #if defined(WIN32) || defined(K_LINUX_PLATFORM) return 0; #endif } // Returns 0 if successful, 1 if not found, -1 if error. int K_FindFileNext( K_FindInfo* io_pFindInfo ) { if ( !io_pFindInfo ) { return -1; } #ifdef WIN32 if ( _findnext( io_pFindInfo->m_hFile, &io_pFindInfo->m_stFindData ) != 0 ) { return (errno == ENOENT) ? 1 : -1; } #elif defined K_LINUX_PLATFORM io_pFindInfo->m_pFTSENT = fts_read( io_pFindInfo->m_pFTS ); if ( !io_pFindInfo->m_pFTSENT ) { return (errno == 0) ? 1 : -1; } #else /* * Directory traversal code is not yet available for Solaris. * If such code will need to be written, then it will probably use ftw.h. */ #endif // If what we found is not actually a file, get the next hit. #ifdef WIN32 if ( (io_pFindInfo->m_stFindData.attrib & _A_SUBDIR) ) #elif defined K_LINUX_PLATFORM if ( !(io_pFindInfo->m_pFTSENT->fts_info & FTS_F) ) #else /* * Directory traversal code is not yet available for Solaris. * If such code will need to be written, then it will probably use ftw.h. */ #endif { return K_FindFileNext( io_pFindInfo ); } #if defined(WIN32) || defined(K_LINUX_PLATFORM) return 0; #endif } void K_FindFileClose( K_FindInfo* io_pFindInfo ) { if ( !io_pFindInfo ) { return; } #ifdef WIN32 _findclose( io_pFindInfo->m_hFile ); #elif defined K_LINUX_PLATFORM fts_close( io_pFindInfo->m_pFTS ); #else /* * Directory traversal code is not yet available for Solaris. * If such code will need to be written, then it will probably use ftw.h. */ #endif } /*--------------------------------------------------------------------------- * Function: K_GetFilenamesInDirectoryCount * * Description: * Reads the given directory and returns the number of files that it contains. * * Input * ----- * i_sDirectoryName Name of directory * * Output * ------ * (none) * * Return value Number of files on success, -1 on failure * *--------------------------------------------------------------------------*/ int K_GetFilenamesInDirectoryCount( const char* i_sDirectoryName ) { K_FindInfo stFindInfo; int iCurrentFile = 0; int iError = 0; if ( !i_sDirectoryName || (strlen(i_sDirectoryName) <= 0) ) { return -1; } iError = K_FindFileFirst( i_sDirectoryName, &stFindInfo ); if ( iError < 0 ) { // error return -1; } else if ( iError > 0 ) { // no files found K_FindFileClose( &stFindInfo ); return 0; } while ( 1 ) { iCurrentFile++; iError = K_FindFileNext( &stFindInfo ); if ( iError < 0 ) { // error K_FindFileClose( &stFindInfo ); return -1; } else if ( iError > 0 ) { // no more files found break; } } K_FindFileClose( &stFindInfo ); return iCurrentFile; } /*--------------------------------------------------------------------------- * Function: K_GetFilenamesInDirectory * * Description: * Reads the given directory and returns an array of names of files that it * contains. A null pointer appears at the last item in the array. The * caller must deallocate o_pasFilenames by using K_FreeFilenames or by * calling free() for each file name and then calling free() on the array * itself. * * Input * ----- * i_sDirectoryName Name of directory * * Output * ------ * o_pasFilenames Array of names of files found in this directory * * Return value Number of files on success, -1 on failure * *--------------------------------------------------------------------------*/ int K_GetFilenamesInDirectory( const char* i_sDirectoryName, char*** o_pasFilenames ) { // Note that we iterate through the filenames twice -- once to get the count // (K_GetFilenamesInDirectoryCount) and then once to get all the names. But // it may happen that the count changes between these calls. So we'll retrieve // at most the number of files that's returned in the first pass. K_FindInfo stFindInfo; int iFilenameCount = 0, iCurrentFile = 0; int iError = 0; if ( !i_sDirectoryName || (strlen(i_sDirectoryName) <= 0) || !o_pasFilenames ) { return -1; } *o_pasFilenames = 0; iFilenameCount = K_GetFilenamesInDirectoryCount( i_sDirectoryName ); if ( iFilenameCount < 0 ) { return -1; } iError = K_FindFileFirst( i_sDirectoryName, &stFindInfo ); if ( iError < 0 ) { // error return -1; } else if ( iError > 0 ) { // No files found K_FindFileClose( &stFindInfo ); return 0; } *o_pasFilenames = (char**)calloc( (iFilenameCount+1), sizeof(char*) ); // +1 for the null last one if ( !*o_pasFilenames ) { // Out of memory K_FindFileClose( &stFindInfo ); return -1; } while ( 1 ) { const char* sFilename = K_GetFilenameFromInfo( &stFindInfo ); size_t iFilenameLength = sFilename ? strlen( sFilename ) : 0; if ( iFilenameLength <= 0 ) { K_FreeFilenames( *o_pasFilenames ); K_FindFileClose( &stFindInfo ); return -1; } (*o_pasFilenames)[iCurrentFile] = (char*)calloc( (iFilenameLength+1), sizeof(char) ); if ( !(*o_pasFilenames)[iCurrentFile] ) { K_FreeFilenames( *o_pasFilenames ); K_FindFileClose( &stFindInfo ); return -1; } strncpy( (*o_pasFilenames)[iCurrentFile], sFilename, iFilenameLength ); (*o_pasFilenames)[iCurrentFile][iFilenameLength] = '\0'; iCurrentFile++; if ( iCurrentFile >= iFilenameCount ) { break; } iError = K_FindFileNext( &stFindInfo ); if ( iError < 0 ) { // error K_FindFileClose( &stFindInfo ); return -1; } else if ( iError > 0 ) { // no more files found break; } } K_FindFileClose( &stFindInfo ); return iCurrentFile; } /*--------------------------------------------------------------------------- * Function: K_FreeFilenames * * Description: * Deallocates the memory allocated in a successful call to * K_GetFilenamesInDirectory. * * Input * ----- * i_asFilenames Array of names of files * * Output * ------ * (none) * * Return value (none) * *--------------------------------------------------------------------------*/ void K_FreeFilenames( char** i_asFilenames ) { int i; if ( !i_asFilenames ) { return; } for ( i = 0; (i_asFilenames[i] != 0); i++ ) { free( i_asFilenames[i] ); i_asFilenames[i] = 0; } free( i_asFilenames ); } /*--------------------------------------------------------------------------- * Function: K_AdjustLocalClock * * Description: * The K_AdjustLocalClock function gradually adjusts the system clock by * the given number of seconds. A positive number adjusts the system * clock forward; a negative number adjusts the system clock backward. * * Input * ----- * i_iAdjustmentInSeconds Number of seconds by which to adjust the * system clock * Output * ------ * (none) * * Return value 1 if successful, 0 on error * *--------------------------------------------------------------------------*/ int K_AdjustLocalClock( int i_iNumberOfSeconds ) { struct timeval delta, lastchange; #ifndef K_SOLARIS_PLATFORM /* Only supported/tested on Solaris at the moment */ return -1; #else /* WARNING: uses standard C time functions with Year 2038 limitations */ time_t now; if ( (now = time(NULL)) == ((time_t)-1) ) { return -1; } delta.tv_sec = i_iNumberOfSeconds; delta.tv_usec = 0; return adjtime(&delta, &lastchange); #endif } #ifdef K_SOLARIS_PLATFORM static int pam_tty_conv( int num_msg, struct pam_message** mess, struct pam_response** resp, void* my_data) { // Following code implements a console-based PAM "conversation" function // (based sample code from Solaris 10 Software Developer Collection >> // Solaris Security for Developers Guide >> // 3. Writing PAM Applications and Services) struct pam_message* m = *mess; struct pam_response* r; int i, j; const char* sPassword = (const char*)my_data; int error = PAM_CONV_ERR; if (num_msg <= 0 || num_msg >= PAM_MAX_NUM_MSG) { (void) fprintf(stderr, "PAM error: bad number of messages"); *resp = NULL; return (PAM_CONV_ERR); } if ((*resp = r = calloc(num_msg, sizeof (struct pam_response))) == NULL) { return (PAM_BUF_ERR); } // Loop through messages for (i = 0; i < num_msg; i++) { // bad message from service module if (m->msg == NULL) { (void) fprintf(stderr, "PAM error: bad message"); goto err; } // fix up final newline: removed for prompts, added back for messages if (m->msg[strlen(m->msg)] == '\n') { m->msg[strlen(m->msg)] = '\0'; } // Since the KMA has its own password prompts and enforces its own rule checks, we already have the // new password in memory. So instead of displaying PAM prompts and collecting user responses, we // "automate" by assuming that the prompts correspond to the standard sequence of "New password:" // followed by "Confirm password:" and so in each case we immediately return the password we already // have in memory. This violates the PAM "conversation" function instructions (which say, basically, // not to assume any particular sequence of prompts since there could be any number of underlying // password managers), but since the KMA is running on an appliance with a fixed password manager, // our assumptions should hold. r->resp = NULL; r->resp_retcode = 0; switch (m->msg_style) { case PAM_PROMPT_ECHO_OFF: case PAM_PROMPT_ECHO_ON: // Assume the prompt asked for New/Confirm password, so return password. if ( (r->resp = strdup(sPassword)) == NULL ) { error = PAM_BUF_ERR; goto err; } break; case PAM_ERROR_MSG: // Assuming the system is configured properly and the KMA password prompts enforce password strength rules, // there should not be errors because of weak passwords, etc. Still, print errors so users/support can // diagnose problems. (void) fputs(m->msg, stderr); (void) fputc('\n', stderr); break; case PAM_TEXT_INFO: // Supress prompts (again, making assumptions). break; default: (void) fprintf(stderr, "PAM error: unknown message"); goto err; } if (errno == EINTR) { goto err; } // next message/response m++; r++; } return (PAM_SUCCESS); err: // Service modules do not clean up responses if an error is returned. // Free responses here. for (j = 0; j < i; j++, r++) { if (r->resp) { // clear before freeing -- may be a password bzero(r->resp, strlen(r->resp)); free(r->resp); r->resp = NULL; } } free(r); *resp = NULL; return error; } #endif /*--------------------------------------------------------------------------- * Function: K_SetRootPassword * * Description: * The K_SetRootPassword function sets the password for the root user via * Pluggable Authentication Module (PAM). This function is interactive. * * Input * ----- * i_sPassword Password to set * * Output * ------ * (none) * * Return value 0 if successful, -1 on error * *--------------------------------------------------------------------------*/ int K_SetRootPassword( const char* i_sPassword ) { // Only supported/tested on Solaris at the moment #ifndef K_SOLARIS_PLATFORM return -1; #else // Based on sample code from Solaris 10 Software Developer Collection >> // Solaris Security for Developers Guide >> // 3. Writing PAM Applications and Services // TODO: Return PAM error codes (to be logged) instead of emitting // messages to screen? struct pam_conv conv; pam_handle_t *pamh; int err; conv.conv = pam_tty_conv; conv.appdata_ptr = (void*)i_sPassword; // Initialize PAM framework err = pam_start("KeyMgr", "root", &conv, &pamh); if (err != PAM_SUCCESS) { fprintf(stderr, "PAM error: %s\n", pam_strerror(pamh, err)); return -1; } // Change password err = pam_chauthtok(pamh, 0); if (err != PAM_SUCCESS) { fprintf(stderr, "PAM error: %s\n", pam_strerror(pamh, err)); // fall through to cleanup } // Cleanup session pam_end(pamh, 0); return (err == PAM_SUCCESS) ? 0 : -1; #endif } /*--------------------------------------------------------------------------- * Function: K_Alarm * * Description: * Calls alarm(2) on Unix in order to cause the operating system to generate * a SIGALRM signal for this process after the given number of real-time * seconds. Does nothing on Windows. * * Input * ----- * i_iSeconds Number of seconds after which to generate a SIGALRM * signal * * Output * ------ * (none) * * Return value If a previous alarm request is pending, then it returns * the number of seconds until this previous request would * have generated a SIGALRM signal. Otherwise, returns 0. * *--------------------------------------------------------------------------*/ unsigned int K_Alarm( unsigned int i_iSeconds ) { #ifndef WIN32 return alarm( i_iSeconds ); #else return 0; #endif } /*--------------------------------------------------------------------------- * Function: K_GetExtendedVersionFromBase * * Description: * This KMS-specific function prepends the timestamp value to the specified * base replication schema version and returns this value as an extended * replication schema version. * * Input * ----- * i_iBaseSchemaVersion Base replication schema version * * Output * ------ * (none) * * Return value Extended replication schema version * *--------------------------------------------------------------------------*/ unsigned int K_GetExtendedVersionFromBase( unsigned int i_iBaseSchemaVersion ) { // seconds since 1970, force to 32-bit #ifdef WIN32 INT32 iTimeStamp = (INT32) time(NULL); #else int32_t iTimeStamp = (int32_t) time(NULL); #endif // minutes since 1970 iTimeStamp = iTimeStamp / 60; // minutes since 2000 (approximately) iTimeStamp -= (30*365*24*60); // shift 8 bits to clear out room for schema version # iTimeStamp = iTimeStamp << 8; // add schema version # to lower end iTimeStamp |= i_iBaseSchemaVersion; return (unsigned int) iTimeStamp; } /*--------------------------------------------------------------------------- * Function: K_ParseTimestampFromExtendedVersion * * Description: * This KMS-specific function parses the timestamp value from the given * extended replication schema version and returns this timestamp value. * * Input * ----- * i_iExtendedSchemaVersion Extended replication schema version * * Output * ------ * (none) * * Return value Timestamp value * *--------------------------------------------------------------------------*/ unsigned int K_ParseTimestampFromExtendedVersion( unsigned int i_iExtendedSchemaVersion ) { unsigned int iTimeStamp = i_iExtendedSchemaVersion >> 8; return iTimeStamp; } /*--------------------------------------------------------------------------- * Function: K_ParseBaseFromExtendedVersion * * Description: * This KMS-specific function parses the base replication schema value from * the given extended replication schema version and returns this base value. * * Input * ----- * i_iExtendedSchemaVersion Extended replication schema version * * Output * ------ * (none) * * Return value Base replication schema value * *--------------------------------------------------------------------------*/ unsigned int K_ParseBaseFromExtendedVersion( unsigned int i_iExtendedSchemaVersion ) { unsigned int iBaseSchemaVersion = i_iExtendedSchemaVersion & 0x000000FF; return iBaseSchemaVersion; } /*--------------------------------------------------------------------------- * Function: K_System * * Description: * This function is a thread-safe replacement for the unsafe system(3C) call. * See the popen(3C) man page for more information. * * Input * ----- * i_sCmd Command to execute * * Output * ------ * (none) * * Return value Termination status of the command language interpreter * if successful, -1 on failure * *--------------------------------------------------------------------------*/ int K_System( const char *i_sCmd ) { #ifndef WIN32 FILE *p; int rc; struct sigaction sOldAction; // Save signal handler sigaction( SIGCHLD, NULL, &sOldAction ); // Use default child signal handler sigset( SIGCHLD, SIG_DFL ); p = popen( i_sCmd, "w" ); if ( p == NULL ) { rc = -1; } else { rc = pclose( p ); } // Reset signal handler sigset( SIGCHLD, sOldAction.sa_handler ); return rc; #else return system( i_sCmd ); #endif }