You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
877 lines
18 KiB
877 lines
18 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
// vp4mutex.cpp : Defines the entry point for the console application.
|
||
|
//
|
||
|
|
||
|
#define Error DbgError
|
||
|
|
||
|
#include "tier0/platform.h"
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <conio.h>
|
||
|
#include <vector> // SEE NOTES BELOW ABOUT REVERTING THIS IF REQUIRED
|
||
|
|
||
|
#undef Error
|
||
|
#undef Verify
|
||
|
|
||
|
#include "clientapi.h"
|
||
|
|
||
|
#include <time.h>
|
||
|
#include <ctype.h>
|
||
|
#include <windows.h>
|
||
|
|
||
|
#undef SetPort
|
||
|
|
||
|
#define RemoveAll clear
|
||
|
#define AddToTail push_back
|
||
|
#define Count size
|
||
|
|
||
|
#define CLIENTSPEC_BUFFER_SIZE (8 * 1024)
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// internal
|
||
|
//-----------------------------------------------------------------------------
|
||
|
ClientApi client;
|
||
|
ClientUser user;
|
||
|
|
||
|
//
|
||
|
// NOTE: All of this crap is here since we don't want to have the .exe depend on tier0 or vstdlib.dll. If we change that, this can go away and std::vector can go back
|
||
|
// to CUtlVector and CUtlSymbol can be used like it's supposed to be used....
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
|
||
|
static void Q_strncpy( char *pDest, char const *pSrc, int maxLen )
|
||
|
{
|
||
|
strncpy( pDest, pSrc, maxLen );
|
||
|
if ( maxLen > 0 )
|
||
|
{
|
||
|
pDest[maxLen-1] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int Q_strlen( const char *str )
|
||
|
{
|
||
|
return strlen( str );
|
||
|
}
|
||
|
|
||
|
|
||
|
#if defined( _WIN32 ) || defined( WIN32 )
|
||
|
#define PATHSEPARATOR(c) ((c) == '\\' || (c) == '/')
|
||
|
#else //_WIN32
|
||
|
#define PATHSEPARATOR(c) ((c) == '/')
|
||
|
#endif //_WIN32
|
||
|
|
||
|
static void Q_FileBase( const char *in, char *out, int maxlen )
|
||
|
{
|
||
|
if ( !in || !in[ 0 ] )
|
||
|
{
|
||
|
*out = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int len, start, end;
|
||
|
|
||
|
len = Q_strlen( in );
|
||
|
|
||
|
// scan backward for '.'
|
||
|
end = len - 1;
|
||
|
while ( end&& in[end] != '.' && !PATHSEPARATOR( in[end] ) )
|
||
|
{
|
||
|
end--;
|
||
|
}
|
||
|
|
||
|
if ( in[end] != '.' ) // no '.', copy to end
|
||
|
{
|
||
|
end = len-1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
end--; // Found ',', copy to left of '.'
|
||
|
}
|
||
|
|
||
|
// Scan backward for '/'
|
||
|
start = len-1;
|
||
|
while ( start >= 0 && !PATHSEPARATOR( in[start] ) )
|
||
|
{
|
||
|
start--;
|
||
|
}
|
||
|
|
||
|
if ( start < 0 || !PATHSEPARATOR( in[start] ) )
|
||
|
{
|
||
|
start = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
start++;
|
||
|
}
|
||
|
|
||
|
// Length of new sting
|
||
|
len = end - start + 1;
|
||
|
|
||
|
int maxcopy = min( len + 1, maxlen );
|
||
|
|
||
|
// Copy partial string
|
||
|
Q_strncpy( out, &in[start], maxcopy );
|
||
|
}
|
||
|
|
||
|
#define COPY_ALL_CHARACTERS -1
|
||
|
static char *Q_strncat(char *pDest, const char *pSrc, size_t destBufferSize, int max_chars_to_copy )
|
||
|
{
|
||
|
size_t charstocopy = (size_t)0;
|
||
|
|
||
|
size_t len = strlen(pDest);
|
||
|
size_t srclen = strlen( pSrc );
|
||
|
if ( max_chars_to_copy <= COPY_ALL_CHARACTERS )
|
||
|
{
|
||
|
charstocopy = srclen;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
charstocopy = (size_t)min( max_chars_to_copy, (int)srclen );
|
||
|
}
|
||
|
|
||
|
if ( len + charstocopy >= destBufferSize )
|
||
|
{
|
||
|
charstocopy = destBufferSize - len - 1;
|
||
|
}
|
||
|
|
||
|
if ( !charstocopy )
|
||
|
{
|
||
|
return pDest;
|
||
|
}
|
||
|
|
||
|
char *pOut = strncat( pDest, pSrc, charstocopy );
|
||
|
pOut[destBufferSize-1] = 0;
|
||
|
return pOut;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Finds a string in another string with a case insensitive test
|
||
|
//-----------------------------------------------------------------------------
|
||
|
static char const* Q_stristr( char const* pStr, char const* pSearch )
|
||
|
{
|
||
|
if (!pStr || !pSearch)
|
||
|
return 0;
|
||
|
|
||
|
char const* pLetter = pStr;
|
||
|
|
||
|
// Check the entire string
|
||
|
while (*pLetter != 0)
|
||
|
{
|
||
|
// Skip over non-matches
|
||
|
if (tolower((unsigned char)*pLetter) == tolower((unsigned char)*pSearch))
|
||
|
{
|
||
|
// Check for match
|
||
|
char const* pMatch = pLetter + 1;
|
||
|
char const* pTest = pSearch + 1;
|
||
|
while (*pTest != 0)
|
||
|
{
|
||
|
// We've run off the end; don't bother.
|
||
|
if (*pMatch == 0)
|
||
|
return 0;
|
||
|
|
||
|
if (tolower((unsigned char)*pMatch) != tolower((unsigned char)*pTest))
|
||
|
break;
|
||
|
|
||
|
++pMatch;
|
||
|
++pTest;
|
||
|
}
|
||
|
|
||
|
// Found a match!
|
||
|
if (*pTest == 0)
|
||
|
return pLetter;
|
||
|
}
|
||
|
|
||
|
++pLetter;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int Q_stricmp( const char *s1, const char *s2 )
|
||
|
{
|
||
|
return stricmp( s1, s2 );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: utility function to split a typical P4 line output into var and value
|
||
|
//-----------------------------------------------------------------------------
|
||
|
static void SplitP4Output(const_char *data, char *pszCmd, char *pszInfo, int bufLen)
|
||
|
{
|
||
|
Q_strncpy(pszCmd, data, bufLen);
|
||
|
|
||
|
char *mid = (char *)Q_stristr(pszCmd, " ");
|
||
|
if (mid)
|
||
|
{
|
||
|
*mid = 0;
|
||
|
Q_strncpy(pszInfo, data + (mid - pszCmd) + 1, bufLen);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pszInfo[0] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int Q_atoi (const char *str)
|
||
|
{
|
||
|
int val;
|
||
|
int sign;
|
||
|
int c;
|
||
|
|
||
|
if (*str == '-')
|
||
|
{
|
||
|
sign = -1;
|
||
|
str++;
|
||
|
}
|
||
|
else
|
||
|
sign = 1;
|
||
|
|
||
|
val = 0;
|
||
|
|
||
|
//
|
||
|
// check for hex
|
||
|
//
|
||
|
if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
|
||
|
{
|
||
|
str += 2;
|
||
|
while (1)
|
||
|
{
|
||
|
c = *str++;
|
||
|
if (c >= '0' && c <= '9')
|
||
|
val = (val<<4) + c - '0';
|
||
|
else if (c >= 'a' && c <= 'f')
|
||
|
val = (val<<4) + c - 'a' + 10;
|
||
|
else if (c >= 'A' && c <= 'F')
|
||
|
val = (val<<4) + c - 'A' + 10;
|
||
|
else
|
||
|
return val*sign;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// check for character
|
||
|
//
|
||
|
if (str[0] == '\'')
|
||
|
{
|
||
|
return sign * str[1];
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// assume decimal
|
||
|
//
|
||
|
while (1)
|
||
|
{
|
||
|
c = *str++;
|
||
|
if (c <'0' || c > '9')
|
||
|
return val*sign;
|
||
|
val = val*10 + c - '0';
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int Q_snprintf( char *pDest, int maxLen, char const *pFormat, ... )
|
||
|
{
|
||
|
va_list marker;
|
||
|
|
||
|
va_start( marker, pFormat );
|
||
|
#ifdef _WIN32
|
||
|
int len = _vsnprintf( pDest, maxLen, pFormat, marker );
|
||
|
#elif _LINUX
|
||
|
int len = vsnprintf( pDest, maxLen, pFormat, marker );
|
||
|
#else
|
||
|
#error "define vsnprintf type."
|
||
|
#endif
|
||
|
va_end( marker );
|
||
|
|
||
|
// Len < 0 represents an overflow
|
||
|
if( len < 0 )
|
||
|
{
|
||
|
len = maxLen;
|
||
|
pDest[maxLen-1] = 0;
|
||
|
}
|
||
|
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: base class for parse input from the P4 server
|
||
|
//-----------------------------------------------------------------------------
|
||
|
template< class T >
|
||
|
class CDataRetrievalUser : public ClientUser
|
||
|
{
|
||
|
public:
|
||
|
std::vector<T> &GetData()
|
||
|
{
|
||
|
return m_Data;
|
||
|
}
|
||
|
|
||
|
// call this to start retrieving data
|
||
|
void InitRetrievingData()
|
||
|
{
|
||
|
m_bAwaitingNewRecord = true;
|
||
|
m_Data.RemoveAll();
|
||
|
}
|
||
|
|
||
|
// implement this to parse out input from the server into the specified object
|
||
|
virtual void OutputRecord(T &obj, const char *pszVar, const char *pszInfo) = 0;
|
||
|
|
||
|
|
||
|
private:
|
||
|
bool m_bAwaitingNewRecord;
|
||
|
std::vector<T> m_Data;
|
||
|
|
||
|
|
||
|
virtual void OutputInfo(char level, const_char *data)
|
||
|
{
|
||
|
if (Q_strlen(data) < 1)
|
||
|
{
|
||
|
// end of a record, await the new one
|
||
|
m_bAwaitingNewRecord = true;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (m_bAwaitingNewRecord)
|
||
|
{
|
||
|
// add in the new record
|
||
|
T newRec;
|
||
|
m_Data.AddToTail( newRec );
|
||
|
|
||
|
T &record = m_Data[ m_Data.Count() - 1 ];
|
||
|
memset(&record, 0, sizeof(record));
|
||
|
m_bAwaitingNewRecord = false;
|
||
|
}
|
||
|
|
||
|
// parse
|
||
|
char szVar[_MAX_PATH];
|
||
|
char szInfo[_MAX_PATH];
|
||
|
SplitP4Output(data, szVar, szInfo, sizeof(szVar));
|
||
|
|
||
|
// emit
|
||
|
T &record = m_Data[m_Data.Count() - 1];
|
||
|
OutputRecord(record, szVar, szInfo);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class CP4Counter
|
||
|
{
|
||
|
public:
|
||
|
CP4Counter() :
|
||
|
m_nValue( 0 )
|
||
|
{
|
||
|
m_szName[ 0 ] = 0;
|
||
|
}
|
||
|
|
||
|
char const *GetCounterName() const
|
||
|
{
|
||
|
return m_szName;
|
||
|
}
|
||
|
|
||
|
int GetValue() const
|
||
|
{
|
||
|
return m_nValue;
|
||
|
}
|
||
|
|
||
|
void SetName( char const *name )
|
||
|
{
|
||
|
Q_strncpy( m_szName, name, sizeof( m_szName ) );
|
||
|
}
|
||
|
|
||
|
void SetValue( int value )
|
||
|
{
|
||
|
m_nValue = value;
|
||
|
}
|
||
|
private:
|
||
|
|
||
|
char m_szName[ 128 ];
|
||
|
int m_nValue;
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Retrieves a file list
|
||
|
//-----------------------------------------------------------------------------
|
||
|
class CCountersUser : public CDataRetrievalUser<CP4Counter>
|
||
|
{
|
||
|
public:
|
||
|
void RetrieveCounters()
|
||
|
{
|
||
|
// clear the list
|
||
|
InitRetrievingData();
|
||
|
|
||
|
client.Run("counters", this);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
virtual void OutputRecord(CP4Counter &counter, const char *szCmd, const char *szInfo)
|
||
|
{
|
||
|
if ( !Q_stricmp( szCmd, "counter" ) )
|
||
|
{
|
||
|
counter.SetName( szInfo );
|
||
|
}
|
||
|
else if ( !Q_stricmp( szCmd, "value" ) )
|
||
|
{
|
||
|
counter.SetValue( Q_atoi( szInfo ) );
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Retrieves a file list
|
||
|
//-----------------------------------------------------------------------------
|
||
|
class CSetCounterUser : public ClientUser
|
||
|
{
|
||
|
public:
|
||
|
void SetCounter( char *countername, int value )
|
||
|
{
|
||
|
char valuestr[ 32 ];
|
||
|
Q_snprintf( valuestr, sizeof( valuestr ), "%d", value );
|
||
|
char *argv[] = { countername, valuestr, NULL };
|
||
|
client.SetArgv( 2, argv );
|
||
|
client.Run("counter", this);
|
||
|
}
|
||
|
|
||
|
virtual void HandleError( Error *err )
|
||
|
{
|
||
|
}
|
||
|
virtual void Message( Error *err )
|
||
|
{
|
||
|
}
|
||
|
virtual void OutputError( const_char *errBuf )
|
||
|
{
|
||
|
}
|
||
|
virtual void OutputInfo( char level, const_char *data )
|
||
|
{
|
||
|
}
|
||
|
virtual void OutputBinary( const_char *data, int length )
|
||
|
{
|
||
|
}
|
||
|
virtual void OutputText( const_char *data, int length )
|
||
|
{
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static CSetCounterUser g_SetCounterUser;
|
||
|
static CCountersUser g_CountersUser;
|
||
|
|
||
|
typedef enum
|
||
|
{
|
||
|
MUTEX_QUERY = 0,
|
||
|
MUTEX_LOCK,
|
||
|
MUTEX_RELEASE,
|
||
|
} MUTEXACTION;
|
||
|
|
||
|
static void printusage( char const *basefile )
|
||
|
{
|
||
|
printf( "usage: %s \n\
|
||
|
\t< query | release | lock > <branchname> <sleepseconds> <clientname> [<ip:port>]\n\
|
||
|
\te.g.:\n\
|
||
|
\t%s query src_main 3 yahn\n\
|
||
|
\t%s query\n\
|
||
|
", basefile, basefile, basefile );
|
||
|
}
|
||
|
|
||
|
struct CUtlSymbol
|
||
|
{
|
||
|
public:
|
||
|
CUtlSymbol()
|
||
|
{
|
||
|
m_szValue[ 0 ] = 0;
|
||
|
}
|
||
|
|
||
|
CUtlSymbol& operator =( const char * lhs )
|
||
|
{
|
||
|
Q_strncpy( m_szValue, lhs, sizeof( m_szValue ) );
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
char const *String()
|
||
|
{
|
||
|
return m_szValue;
|
||
|
}
|
||
|
private:
|
||
|
char m_szValue[ 256 ];
|
||
|
};
|
||
|
|
||
|
int FindLockUsers( CCountersUser& counters, char const *branchspec, std::vector< CUtlSymbol >& users, std::vector< int >& locktimes, std::vector< CUtlSymbol >* branchnames = NULL )
|
||
|
{
|
||
|
users.RemoveAll();
|
||
|
|
||
|
char lockstr[ 256 ];
|
||
|
if ( branchspec != NULL )
|
||
|
{
|
||
|
Q_snprintf( lockstr, sizeof( lockstr ), "%s_lock_", branchspec );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Q_snprintf( lockstr, sizeof( lockstr ), "_lock_", branchspec );
|
||
|
}
|
||
|
|
||
|
counters.RetrieveCounters();
|
||
|
std::vector< CP4Counter >& list = counters.GetData();
|
||
|
int count = list.Count();
|
||
|
for ( int i = 0; i < count; ++i )
|
||
|
{
|
||
|
char const *name = list[ i ].GetCounterName();
|
||
|
int value = list[ i ].GetValue();
|
||
|
|
||
|
char const *p = Q_stristr( name, lockstr );
|
||
|
if ( !p )
|
||
|
continue;
|
||
|
|
||
|
if ( value != 0 )
|
||
|
{
|
||
|
CUtlSymbol sym;
|
||
|
sym = p + Q_strlen( lockstr );
|
||
|
|
||
|
users.AddToTail( sym );
|
||
|
locktimes.AddToTail( value );
|
||
|
|
||
|
if ( branchnames )
|
||
|
{
|
||
|
char branchname[ 512 ];
|
||
|
Q_strncpy( branchname, name, p - name + 1 );
|
||
|
CUtlSymbol sym;
|
||
|
sym = branchname;
|
||
|
branchnames->AddToTail( sym );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return users.Count();
|
||
|
}
|
||
|
|
||
|
static void GetHourMinuteSecondsString( int nInputSeconds, char *pOut, int outLen )
|
||
|
{
|
||
|
int nMinutes = nInputSeconds / 60;
|
||
|
int nSeconds = nInputSeconds - nMinutes * 60;
|
||
|
int nHours = nMinutes / 60;
|
||
|
nMinutes -= nHours * 60;
|
||
|
|
||
|
char *extra[2] = { "", "s" };
|
||
|
|
||
|
if ( nHours > 0 )
|
||
|
Q_snprintf( pOut, outLen, "%d hour%s, %d minute%s, %d second%s", nHours, extra[nHours != 1], nMinutes, extra[nMinutes != 1], nSeconds, extra[nSeconds != 1] );
|
||
|
else if ( nMinutes > 0 )
|
||
|
Q_snprintf( pOut, outLen, "%d minute%s, %d second%s", nMinutes, extra[nMinutes != 1], nSeconds, extra[nSeconds != 1] );
|
||
|
else
|
||
|
Q_snprintf( pOut, outLen, "%d second%s", nSeconds, extra[nSeconds != 1] );
|
||
|
}
|
||
|
|
||
|
static void ComputeHoldTime( int holdtime, char *buf, size_t bufsize )
|
||
|
{
|
||
|
buf[ 0 ] = 0;
|
||
|
|
||
|
if ( holdtime < 100 )
|
||
|
{
|
||
|
Q_snprintf( buf, bufsize, "UNKNOWN" );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Prepend the time.
|
||
|
time_t aclock = (time_t)holdtime;
|
||
|
struct tm *newtime = localtime( &aclock );
|
||
|
|
||
|
// Get rid of the \n.
|
||
|
Q_strncpy( buf, asctime( newtime ), bufsize );
|
||
|
char *pEnd = (char *)Q_stristr( buf, "\n" );
|
||
|
if ( pEnd )
|
||
|
{
|
||
|
*pEnd = 0;
|
||
|
}
|
||
|
|
||
|
time_t curtime;
|
||
|
time( &curtime );
|
||
|
|
||
|
int holdSeconds = curtime - holdtime;
|
||
|
if ( holdSeconds > 0 )
|
||
|
{
|
||
|
char durstring[ 256 ];
|
||
|
durstring[ 0 ] = 0;
|
||
|
GetHourMinuteSecondsString( holdSeconds, durstring, sizeof( durstring ) );
|
||
|
|
||
|
Q_strncat( buf, ", held for ", bufsize, COPY_ALL_CHARACTERS );
|
||
|
Q_strncat( buf, durstring, bufsize, COPY_ALL_CHARACTERS );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int _tmain(int argc, _TCHAR* argv[])
|
||
|
{
|
||
|
char basefile[ 256 ];
|
||
|
Q_FileBase( argv[ 0 ], basefile, sizeof( basefile ) );
|
||
|
|
||
|
bool validAction = false;
|
||
|
MUTEXACTION action = MUTEX_QUERY;
|
||
|
bool validBranch = false;
|
||
|
CUtlSymbol branchspec;
|
||
|
bool validSleepSeconds = false;
|
||
|
int sleepSeconds = 1;
|
||
|
bool validClient = false;
|
||
|
CUtlSymbol clientname;
|
||
|
bool validIP = false;
|
||
|
CUtlSymbol ipport;
|
||
|
|
||
|
for ( int i = 1; i < argc; ++i )
|
||
|
{
|
||
|
switch ( i )
|
||
|
{
|
||
|
default:
|
||
|
break;
|
||
|
case 1:
|
||
|
validAction = true;
|
||
|
if ( !Q_stricmp( argv[ i ], "query" ) )
|
||
|
{
|
||
|
action = MUTEX_QUERY;
|
||
|
}
|
||
|
else if ( !Q_stricmp( argv[ i ], "release" ) )
|
||
|
{
|
||
|
action = MUTEX_RELEASE;
|
||
|
}
|
||
|
else if ( !Q_stricmp( argv[ i ], "lock" ) )
|
||
|
{
|
||
|
action = MUTEX_LOCK;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
validAction = false;
|
||
|
}
|
||
|
break;
|
||
|
case 2:
|
||
|
{
|
||
|
validBranch = true;
|
||
|
branchspec = argv[ i ];
|
||
|
}
|
||
|
break;
|
||
|
case 3:
|
||
|
{
|
||
|
validSleepSeconds = true;
|
||
|
sleepSeconds = clamp( Q_atoi( argv[ i ] ), 0, 100 );
|
||
|
}
|
||
|
break;
|
||
|
case 4:
|
||
|
{
|
||
|
validClient = true;
|
||
|
clientname = argv[ i ];
|
||
|
}
|
||
|
break;
|
||
|
case 5:
|
||
|
{
|
||
|
validIP = true;
|
||
|
ipport = argv[ i ];
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool describeLocksOnly = false;
|
||
|
if ( !validBranch ||
|
||
|
!validSleepSeconds ||
|
||
|
!validClient ||
|
||
|
!validIP )
|
||
|
{
|
||
|
if ( !validAction || action != MUTEX_QUERY )
|
||
|
{
|
||
|
printusage( basefile );
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
describeLocksOnly = true;
|
||
|
sleepSeconds = 5;
|
||
|
}
|
||
|
|
||
|
// set the protocol return all data as key/value pairs
|
||
|
client.SetProtocol( "tag", "" );
|
||
|
|
||
|
// connect to the p4 server
|
||
|
Error e;
|
||
|
if ( ipport.String()[ 0 ] )
|
||
|
{
|
||
|
client.SetPort( ipport.String() );
|
||
|
}
|
||
|
if ( clientname.String()[ 0 ] )
|
||
|
{
|
||
|
client.SetUser( clientname.String() );
|
||
|
}
|
||
|
|
||
|
client.Init( &e );
|
||
|
bool connected = ( e.Test() == 0 ) ? true : false;
|
||
|
if ( !connected )
|
||
|
{
|
||
|
printf( "Unable to connect to perforce server\n" );
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if ( describeLocksOnly )
|
||
|
{
|
||
|
std::vector< CUtlSymbol > users;
|
||
|
std::vector< int > locktimes;
|
||
|
std::vector< CUtlSymbol > branchnames;
|
||
|
int holdCount = FindLockUsers( g_CountersUser, NULL, users, locktimes, &branchnames );
|
||
|
|
||
|
for ( int i = 0; i < holdCount; ++i )
|
||
|
{
|
||
|
char timestr[ 128 ];
|
||
|
ComputeHoldTime( locktimes[ i ], timestr, sizeof( timestr ) );
|
||
|
printf( "'%s' HELD by: %s\n\ttime: %s\n", branchnames[ i ].String(), users[ i ].String(), timestr );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
std::vector< CUtlSymbol > users;
|
||
|
std::vector< int > locktimes;
|
||
|
int holdCount = FindLockUsers( g_CountersUser, branchspec.String(), users, locktimes );
|
||
|
|
||
|
if ( holdCount >= 2 )
|
||
|
{
|
||
|
char userlist[ 1024 ];
|
||
|
userlist[ 0 ] = 0;
|
||
|
for ( int i = 0; i < (int)users.Count(); ++i )
|
||
|
{
|
||
|
Q_strncat( userlist, users[ i ].String(), sizeof( userlist ), COPY_ALL_CHARACTERS );
|
||
|
if ( i != users.Count() - 1 )
|
||
|
{
|
||
|
Q_strncat( userlist, ", ", sizeof( userlist ), COPY_ALL_CHARACTERS );
|
||
|
}
|
||
|
}
|
||
|
printf( "%s: ERROR, multiple users (%s) holding lock on '%s'\n", basefile, userlist, branchspec.String() );
|
||
|
printusage( basefile );
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
char setcountername[ 256 ];
|
||
|
Q_snprintf( setcountername, sizeof( setcountername ), "%s_lock_%s", branchspec.String(), clientname.String() );
|
||
|
|
||
|
switch ( action )
|
||
|
{
|
||
|
default:
|
||
|
break;
|
||
|
case MUTEX_QUERY:
|
||
|
{
|
||
|
if ( holdCount == 1 )
|
||
|
{
|
||
|
bool isHeldByLocal = false;
|
||
|
|
||
|
if ( !Q_stricmp( users[ 0 ].String(), clientname.String() ) )
|
||
|
{
|
||
|
isHeldByLocal = true;
|
||
|
}
|
||
|
|
||
|
char timestr[ 128 ];
|
||
|
ComputeHoldTime( locktimes[ 0 ], timestr, sizeof( timestr ) );
|
||
|
printf( "%s: '%s' lock on %s is HELD by: %s\nHOLD INFO: %s\n", basefile, branchspec.String(), ipport.String(), users[ 0 ].String(), timestr );
|
||
|
}
|
||
|
else if ( holdCount == 0 )
|
||
|
{
|
||
|
printf( "%s: '%s' lock on %s is FREE\n", basefile, branchspec.String(), ipport.String() );
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case MUTEX_LOCK:
|
||
|
{
|
||
|
printf( "%s: Attempting to lock the '%s' codeline for %s on %s\n\n", basefile, branchspec.String(), clientname.String(), ipport.String() );
|
||
|
|
||
|
// p4mutex: Attempting to lock the 'main_src' codeline for yahn on 207.173.178.12:1666.
|
||
|
|
||
|
if ( holdCount == 1 )
|
||
|
{
|
||
|
bool isHeldByLocal = false;
|
||
|
|
||
|
if ( !Q_stricmp( users[ 0 ].String(), clientname.String() ) )
|
||
|
{
|
||
|
isHeldByLocal = true;
|
||
|
}
|
||
|
|
||
|
char timestr[ 128 ];
|
||
|
ComputeHoldTime( locktimes[ 0 ], timestr, sizeof( timestr ) );
|
||
|
|
||
|
if ( isHeldByLocal )
|
||
|
{
|
||
|
// Success: You already have the 'main_src' codeline lock.
|
||
|
printf( "Success: You already have the '%s' codeline lock\nInfo: %s\n", branchspec.String(), timestr );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Failed: 'main_goldsrc' lock currently owned by alfred
|
||
|
printf( "Failed: '%s' lock currently owned by %s\nInfo: %s\n", branchspec.String(), users[ 0 ].String(), timestr );
|
||
|
}
|
||
|
}
|
||
|
else if ( holdCount == 0 )
|
||
|
{
|
||
|
// Success: 'main_src' codeline lock granted to yahn.
|
||
|
// Set the counter
|
||
|
time_t aclock;
|
||
|
time( &aclock );
|
||
|
g_SetCounterUser.SetCounter( setcountername, (int)aclock );
|
||
|
|
||
|
printf( "Success: '%s' codeline lock granted to %s\n", branchspec.String(), clientname.String() );
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case MUTEX_RELEASE:
|
||
|
{
|
||
|
printf( "%s: Attempting to release the '%s' codeline for %s on %s\n\n", basefile, branchspec.String(), clientname.String(), ipport.String() );
|
||
|
|
||
|
// p4mutex: Attempting to release the 'main_src' codeline for yahn on 207.173.178.12:1666.
|
||
|
|
||
|
if ( holdCount == 1 )
|
||
|
{
|
||
|
bool isHeldByLocal = false;
|
||
|
|
||
|
if ( !Q_stricmp( users[ 0 ].String(), clientname.String() ) )
|
||
|
{
|
||
|
isHeldByLocal = true;
|
||
|
}
|
||
|
|
||
|
char timestr[ 128 ];
|
||
|
ComputeHoldTime( locktimes[ 0 ], timestr, sizeof( timestr ) );
|
||
|
|
||
|
if ( isHeldByLocal )
|
||
|
{
|
||
|
// Success: 'main_src' codeline lock released.
|
||
|
|
||
|
// Set the counter
|
||
|
g_SetCounterUser.SetCounter( setcountername, 0 );
|
||
|
|
||
|
printf( "Success: '%s' codeline lock released.\n", branchspec.String() );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Failed: 'main_goldsrc' lock currently owned by alfred
|
||
|
printf( "Failed: '%s' lock currently owned by %s\nInfo: %s\n", branchspec.String(), users[ 0 ].String(), timestr );
|
||
|
}
|
||
|
}
|
||
|
else if ( holdCount == 0 )
|
||
|
{
|
||
|
// Success: The 'main_src' codeline lock is already free.
|
||
|
|
||
|
printf( "Success: The '%s' codeline lock is already free\n", branchspec.String() );
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( sleepSeconds > 0 )
|
||
|
{
|
||
|
time_t starttime;
|
||
|
time( &starttime );
|
||
|
|
||
|
int elapsed = 0;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
time_t curtime;
|
||
|
time( &curtime );
|
||
|
elapsed = curtime - starttime;
|
||
|
|
||
|
Sleep( 50 );
|
||
|
|
||
|
} while ( elapsed < sleepSeconds && !kbhit() );
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|