📄 labview_test_controller.cpp
字号:
// $Id: labview_test_controller.cpp 81007 2008-03-18 20:47:11Z shuston $
//
// Defines the entry point for the LabVIEW RT test controller DLL application.
// This DLL is loaded at system boot by LabVIEW RT. The controller waits for
// TCP connections from the ACE+TAO test scripts. The test scripts will direct
// operation of the tests via commands sent over TCP. In order to be ready for
// connections without intervention via VI, the initial load will spawn a
// thread that sets up the listening socket.
#include "stdafx.h"
#include <errno.h>
#include <fcntl.h>
#include <io.h>
#include <memory.h>
#include <process.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <Winsock2.h>
// NULL is the documented way to check DLL handles, and this is plain
// Windows code, not ACE, so we stick to the Microsoft way...
// FUZZ: disable check_for_NULL
// This is plain Windows code, not ACE. Therefore we disable
// the check for ACE_OS
// FUZZ: disable check_for_lack_ACE_OS
// TEST_FUNC is the prototype for the called test's main entrypoint. It's
// the normal C main.
typedef int (*TEST_FUNC) (int argc, char *argv[]);
// Thread entrypoints
static unsigned int __stdcall test_control (void *param);
static unsigned int __stdcall peer_svc (void *peer_p);
static unsigned int __stdcall run_test (void *test_p);
static const char *format_errmsg (unsigned int errcode, const char *prefix);
// Logging information
static const char *LogName = "acetao.log";
static HANDLE logf = INVALID_HANDLE_VALUE;
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
return (0 != _beginthreadex (0, // security
8 * 1024, // stack size
test_control, // entrypoint
0, // param
0, // creation flags
0)); // ptr to thread id
}
return TRUE;
}
class Test
{
public:
Test () : dll_handle_ (NULL),
thr_handle_ (0),
entry_ (0),
running_ (false),
status_ (-1)
{}
~Test ();
HANDLE handle (void) { return this->thr_handle_; }
int run (void);
const char *start (const char *name);
bool status (int *exit_status);
int wait (void);
void kill (void);
// Clean up remnants of a test run.
void cleanup (void);
private:
HMODULE dll_handle_;
HANDLE thr_handle_;
TEST_FUNC entry_;
bool running_;
int status_;
enum { CMDLINE_LEN = 1024, ARGV_SIZE = 100 };
char name_[CMDLINE_LEN];
char cmdline_[CMDLINE_LEN];
int argc_;
char *argv_[ARGV_SIZE];
};
class Peer
{
public:
Peer (SOCKET h) : handle_ (h) {}
// Run the Peer's session; intended to be called from a new thread devoted
// to this peer's session.
int svc (void);
private:
Peer () {};
// Process command input from socket.
int command (void);
// Send a reply string to the peer.
int reply (const char *msg);
SOCKET handle_;
Test test_;
};
// Run a peer session; assume there's a thread for each session, so this
// object has all it needs for context located in 'this' object, and it can
// block at any point as long as one remembers that there is one or more
// test threads running and some attention must be paid to the encapsulated
// socket handle over which this object receives control commands from the
// host test driver.
int
Peer::svc (void)
{
// Read commands until EOF (peer closed) or protocol error
while (0 == this->command ())
;
closesocket (this->handle_);
this->handle_ = INVALID_SOCKET;
return 0;
}
int
Peer::command (void)
{
// The protocol exchanges with the peer are execpted to be lock-step
// request-reply command lines, so we can make assumptions about a complete
// line being available to make life easier.
const int MAX_RECV = 1024;
char line[MAX_RECV], *p;
p = &line[0];
int count = 0, len = 0;
while ((count = recv (this->handle_, p, MAX_RECV - len, 0)) > 0)
{
p[count] = '\0';
len += count;
p += count;
char *nl;
if ((nl = strchr (line, '\n')) == 0)
continue;
// At this point we have a 0-terminated string with a newline ending
// the command line. Break out and process the command.
break;
}
if (count <= 0)
return -1; // Relay closed/error socket to caller
char *cmd = strtok (line, "\t \n\r");
if (cmd == 0)
{
char err[1024];
sprintf (err, "Can't parse input: %s\n", line);
this->reply (err);
return -1;
}
// Which command is it? These commands are known:
//
// run <test-dll> [args]
// Run test in the named test-dll; respond with "OK" or an error string.
// status
// If test still running return "RUNNING" else return exit status.
// wait
// Wait for test to exit; return "OK"
// kill
// Kill the thread with the most recent test; return "OK".
// snaplog
// Take a snapshot of the current stdout/stderr log to a new file
// name and reset the stdout/stderr log.
if (strcmp ("run", cmd) == 0)
{
char *test = strtok (0, "\t \n\r");
if (test == 0)
{
this->reply ("Malformed run command\n");
return -1;
}
// start() pulls apart the rest of the command line...
const char *errmsg = this->test_.start (test);
if (errmsg == 0)
this->reply ("OK\n");
else
this->reply (errmsg);
}
else if (strcmp ("status", cmd) == 0)
{
int status;
if (this->test_.status (&status))
{
char retvalmsg[64];
sprintf (retvalmsg, "%d\n", status);
this->reply (retvalmsg);
}
else
this->reply ("RUNNING\n");
}
else if (strcmp ("wait", cmd) == 0)
{
int status = this->test_.wait ();
char retvalmsg[64];
sprintf (retvalmsg, "%d\n", status);
this->reply (retvalmsg);
}
else if (strcmp ("kill", cmd) == 0)
{
// Killing things is bad... say we can't and the host should reboot us.
this->reply ("NO - please reboot me\n");
}
else if (strcmp ("waitforfile", cmd) == 0)
{
char *name = strtok (0, "\t \n\r");
if (name == 0)
{
this->reply ("Malformed waitforfile command\n");
return -1;
}
char *secs_s = strtok (0, "\t \n\r");
int secs = 0;
if (secs_s == 0 || (secs = atoi (secs_s)) <= 0)
{
this->reply ("Malformed waitforfile command\n");
return -1;
}
struct _stat info;
const char *msg = 0;
bool found = false;
while (secs > 0)
{
if (_stat (name, &info) == -1) // No file yet
{
if (errno != ENOENT)
{
// Something more serious than no file yet; bail out.
msg = format_errmsg (errno, name);
break;
}
}
else
{
if (info.st_size > 0)
{
found = true;
break;
}
}
// Either no file yet, or it's there but with no content yet.
Sleep (1 * 1000); // arg is in msec
--secs;
}
if (found)
this->reply ("OK\n");
else if (secs == 0)
this->reply ("TIMEOUT\n");
else
this->reply (msg);
}
else if (strcmp ("snaplog", cmd) == 0)
{
if (logf == INVALID_HANDLE_VALUE)
{
this->reply ("NONE\n");
}
else
{
CloseHandle (logf);
if (0 == rename (LogName, "snapshot.txt"))
{
char abspath[1024];
if (_fullpath (abspath, "snapshot.txt", 1024))
{
strcat (abspath, "\n");
this->reply (abspath);
}
else
{
// Last ditch effort to get a name back to the client
this->reply ("\\ni-rt\\system\\snapshot.txt\n");
}
}
else
{
this->reply ("NONE\n");
}
// Reset stdout/stderr to a new file
logf = CreateFile (LogName,
FILE_ALL_ACCESS,
FILE_SHARE_READ,
0, // security
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
0);
SetStdHandle (STD_OUTPUT_HANDLE, logf);
SetStdHandle (STD_ERROR_HANDLE, logf);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -