* Tools code) that provides some useful VMX interaction capability, e.g.
* logging to the VM's VMX log, querying config variables, etc.
*
- * NB: This library is *NOT* threadsafe, so if you want to avoid
- * corrupting your log statements or other screwups, add your own
- * locking around calls to RpcVMX_Log. The exception is
- * RpcVMX_LogVWithBuffer which does not use any global state but
- * requires the caller to manage its own buffers.
+ * Thread safety:
+ * 1. This library in general is *NOT* threadsafe. Users of
+ * RpcVMX_Log/RpcVMX_LogV and RpcVMX_LogSetPrefix should protect
+ * this usage externally with their own locking, and any other
+ * requirements for thread safety should be carefully audited.
+ *
+ * 2. If thread safety around logging is required at a finer-grained
+ * level than a single external lock, callers should allocate
+ * buffers externally and then initialize them with
+ * RpcVMX_InitLogBackingBuffer. These buffers should be externally
+ * protected in some way such that they are only used by one thread
+ * at a time, and passed into RpcVMX_LogVWithBuffer at logging
+ * time.
*/
#ifndef _RPCVMX_H_
#include "vm_basic_types.h"
#include "rpcvmxext.h"
-#define RPCVMX_MAX_LOG_LEN (2048) /* 2kb max - make it dynamic? */
-#define RPCVMX_LOG_BUFSIZE (RPCVMX_MAX_LOG_LEN + sizeof "log")
+#define RPCVMX_DEFAULT_LOG_BUFSIZE (2048 + sizeof "log")
-typedef struct RpcVMXState {
- char logBuf[RPCVMX_LOG_BUFSIZE];
+typedef struct RpcVMXLogBuffer {
+ char * logBuf;
+ unsigned int logBufSizeBytes;
unsigned int logOffset;
-} RpcVMXState;
-
+} RpcVMXLogBuffer;
/*
* Set a prefix to prepend to any future log statements.
*/
const char *RpcVMX_LogGetPrefix(const char *prefix);
+/*
+ * Initialize the given log buffer struct with the given caller-allocated
+ * backing buffer and prefix string.
+ */
+Bool RpcVMX_InitLogBackingBuffer(RpcVMXLogBuffer *bufferOut,
+ char *logBuf,
+ unsigned int logBufSizeBytes,
+ const char *prefix);
+
/*
* Same as RpcVMX_Log but takes a va_list instead of inline arguments.
*/
/*
* Same as RpcVMX_LogV but uses the caller-specified buffer to back the log.
*/
-void RpcVMX_LogVWithBuffer(RpcVMXState *rpcBuffer,
+void RpcVMX_LogVWithBuffer(RpcVMXLogBuffer *rpcBuffer,
const char *fmt,
va_list args);
#include "rpcvmx.h"
-static RpcVMXState RpcVMX = { "log ", sizeof "log" };
+/*
+ * Global shared buffer state used to back RPCI log calls made through
+ * RpcVMX_Log and RpcVMX_LogV.
+ */
+static char gRpcVmxLogBackingBuffer[RPCVMX_DEFAULT_LOG_BUFSIZE] = "log ";
+static RpcVMXLogBuffer gRpcVmxLog = { &gRpcVmxLogBackingBuffer[0],
+ RPCVMX_DEFAULT_LOG_BUFSIZE,
+ sizeof "log" };
+
+static Bool RpcVMXBufferSetPrefix(char *logBackingBuffer,
+ unsigned int logBackingBufferSizeBytes,
+ const char *prefix,
+ unsigned int *logOffsetOut);
/*
*
* RpcVMX_LogSetPrefix
*
- * Allows callers to set a prefix to prepend to the log output. If the
- * prefix overflows the (static) prefix space available, it is rejected
- * and the prefix is reset to nothing. Each call to VMXLog_SetPrefix
- * replaces the previously set prefix.
+ * Allows callers to set a prefix to prepend to the log output, for calls
+ * to RpcVMX_Log and RpcVMX_LogV. If the prefix overflows the (static)
+ * prefix space available, it is rejected and the prefix is reset to
+ * nothing. Each call to VMXLog_SetPrefix replaces the previously set
+ * prefix.
*
* Results:
- * TRUE if the prefix was accepted, FALSE otherwise.
+ * None.
*
* Side effects:
- * All subsequent calls to RpcVMX_Log() will have the prefix string
- * prepended.
+ * All subsequent calls to RpcVMX_Log() and RpcVMX_LogV() will have the
+ * prefix string prepended.
*
*----------------------------------------------------------------------------
*/
void
RpcVMX_LogSetPrefix(const char *prefix)
{
- size_t prefixLen = strlen(prefix);
-
- if (prefixLen + sizeof "log" >= sizeof RpcVMX.logBuf - 1) {
- /*
- * Somebody passed a huge prefix. Don't do that!
- */
- RpcVMX.logOffset = sizeof "log";
- return;
- }
- Str_Strcpy(RpcVMX.logBuf + sizeof "log",
- prefix,
- sizeof RpcVMX.logBuf - sizeof "log");
-
- RpcVMX.logOffset = (unsigned int)(sizeof "log" + prefixLen);
+ RpcVMXBufferSetPrefix(gRpcVmxLog.logBuf, gRpcVmxLog.logBufSizeBytes, prefix,
+ &gRpcVmxLog.logOffset);
}
{
UNUSED_VARIABLE(prefix);
- RpcVMX.logBuf[RpcVMX.logOffset] = '\0';
- return RpcVMX.logBuf + sizeof "log";
+ gRpcVmxLog.logBuf[gRpcVmxLog.logOffset] = '\0';
+ return gRpcVmxLog.logBuf + sizeof "log";
}
+/*
+ *---------------------------------------------------------------------------
+ *
+ * RpcVMXBufferSetPrefix --
+ *
+ * Internal helper function to set the prefix string for a log buffer.
+ *
+ * Results:
+ * Returns TRUE iff the prefix was successfully set.
+ *
+ * On success, writes the buffer index immediately following "log {PREFIX}"
+ * to *logOffsetOut.
+ *
+ * Side effects:
+ * All subsequent calls to the RpcVMX_Log* functions using the new
+ * backing buffer will have the prefix string prepended.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static Bool
+RpcVMXBufferSetPrefix(char *logBackingBuffer, // OUT
+ unsigned int logBackingBufferSizeBytes, // IN
+ const char *prefix, // IN
+ unsigned int *logOffsetOut) // OUT
+{
+ size_t prefixLen;
+
+ if (logBackingBuffer == NULL || prefix == NULL || logOffsetOut == NULL) {
+ return FALSE;
+ }
+
+ *logOffsetOut = 0;
+
+ prefixLen = strlen(prefix);
+
+ if (prefixLen + sizeof "log" >= logBackingBufferSizeBytes - 1) {
+ return FALSE;
+ }
+
+ Str_Strcpy(logBackingBuffer + sizeof "log", prefix,
+ logBackingBufferSizeBytes - sizeof "log");
+
+ *logOffsetOut = (unsigned int)(sizeof "log" + prefixLen);
+
+ return TRUE;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * RpcVMX_InitLogBackingBuffer --
+ *
+ * Initialize the given log buffer struct with the given caller-allocated
+ * backing buffer and prefix string.
+ *
+ * Results:
+ * Returns TRUE if the provided RpcVMXBuffer was initialized successfully,
+ * or FALSE if initialization failed and the RpcVMXBuffer should not be
+ * used.
+ *
+ * Side effects:
+ * All subsequent calls to the RpcVMX_Log* functions using the new
+ * backing buffer will have the prefix string prepended.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+Bool
+RpcVMX_InitLogBackingBuffer(RpcVMXLogBuffer *bufferOut, // OUT
+ char *logBuf, // IN
+ unsigned int logBufSizeBytes, // IN
+ const char *prefix) // IN
+{
+ unsigned int prefixLogOffset = 0;
+
+ if (bufferOut == NULL || logBuf == NULL || prefix == NULL ||
+ logBufSizeBytes < sizeof "log ") {
+ return FALSE;
+ }
+
+ bufferOut->logBuf = logBuf;
+ bufferOut->logBufSizeBytes = logBufSizeBytes;
+
+ memset(bufferOut->logBuf, 0, bufferOut->logBufSizeBytes);
+
+ /*
+ * Copy in the RPCI command prefix "log ".
+ */
+ Str_Strcpy(bufferOut->logBuf, "log ",
+ logBufSizeBytes - sizeof "log ");
+ bufferOut->logOffset = sizeof "log";
+
+ /*
+ * Copy in the provided logging prefix after the initial "log ".
+ */
+ if (RpcVMXBufferSetPrefix(bufferOut->logBuf, bufferOut->logBufSizeBytes,
+ prefix, &prefixLogOffset)) {
+ bufferOut->logOffset = prefixLogOffset;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+
/*
*----------------------------------------------------------------------------
*
int payloadLen;
char receiveBuffer[16];
- payloadLen = Str_Vsnprintf(RpcVMX.logBuf + RpcVMX.logOffset,
- sizeof RpcVMX.logBuf - RpcVMX.logOffset,
+ payloadLen = Str_Vsnprintf(gRpcVmxLog.logBuf + gRpcVmxLog.logOffset,
+ gRpcVmxLog.logBufSizeBytes - gRpcVmxLog.logOffset,
fmt, args);
if (payloadLen < 1) {
* Overflow. We need more space in the buffer. Just set the length to
* the buffer size and send the (truncated) log message.
*/
- payloadLen = sizeof RpcVMX.logBuf - RpcVMX.logOffset;
+ payloadLen = gRpcVmxLog.logBufSizeBytes - gRpcVmxLog.logOffset;
}
/*
* returns two character strings "1 " on success and "0 " on
* failure, so we don't need a sizeable buffer.
*/
- RpcOut_SendOneRawPreallocated(RpcVMX.logBuf,
- (size_t)RpcVMX.logOffset + payloadLen,
+ RpcOut_SendOneRawPreallocated(gRpcVmxLog.logBuf,
+ (size_t)gRpcVmxLog.logOffset + payloadLen,
receiveBuffer, sizeof receiveBuffer);
}
* argument list, then send it to the VMX using the RPCI "log" command.
*
* Uses the caller-provided buffer to back the log, rather than the
- * global RpcVMXState.
+ * global gRpcVmxLog.
*
* Results:
* None.
*/
void
-RpcVMX_LogVWithBuffer(RpcVMXState *rpcBuffer, // IN/OUT
+RpcVMX_LogVWithBuffer(RpcVMXLogBuffer *rpcBuffer, // IN/OUT
const char *fmt, // IN
va_list args) // IN
{
return;
}
- if (rpcBuffer->logOffset >= sizeof rpcBuffer->logBuf) {
+ if (rpcBuffer->logOffset >= rpcBuffer->logBufSizeBytes) {
/*
- * The RpcVMXState is not valid, because the prefix is taking up the
+ * The RpcVMXLogBuffer is not valid, because the prefix is taking up the
* entire buffer. Since we can't log any actual message, silently fail.
*/
return;
}
payloadLen = Str_Vsnprintf(rpcBuffer->logBuf + rpcBuffer->logOffset,
- sizeof rpcBuffer->logBuf - rpcBuffer->logOffset,
+ rpcBuffer->logBufSizeBytes - rpcBuffer->logOffset,
fmt, args);
if (payloadLen < 1) {
* Overflow. We need more space in the buffer. Just set the length to
* the buffer size and send the (truncated) log message.
*/
- payloadLen = sizeof rpcBuffer->logBuf - rpcBuffer->logOffset;
+ payloadLen = rpcBuffer->logBufSizeBytes - rpcBuffer->logOffset;
}
/*