fix: stop using "magic" numbers in string operations for the copyannofile2log feature
fix: When using the copylogfromannofile workaround, extract the build ID and build duration and add to the log as these are useful for analysis. The log should now be identical to the stdout file.
fix: Remove extra blank lines from output in copylogfromannofile mode.
/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
#include <unistd.h>
#include "talon_process.h"
#include "buffer.h"
#include <errno.h>
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
//#include <strsafe.h>
#include "log.h"
#define RETURN(x) { retval=x; goto cleanup; }
#define CLEANUP() cleanup:
#define READSIZE 4096
typedef struct ReadOpStruct {
HANDLE semaphore;
HANDLE thread;
DWORD timeout;
DWORD error;
HANDLE file;
BOOL success;
char *space;
DWORD nbytes;
int id;
struct ReadOpStruct *next;
} ReadOp;
typedef struct {
ReadOp *first;
ReadOp *last;
HANDLE semaphore;
} ReadOpQ;
proc *process_new(void)
{
proc *p = malloc(sizeof(proc));
p->output = buffer_new();
if (!p->output)
{
free(p);
return NULL;
}
p->starttime = 0;
p->endtime = 0;
p->returncode = 1;
p->pid = 0;
p->causeofdeath = PROC_NORMALDEATH;
return p;
}
#define TALONMAXERRSTR 1024
void printlasterror(void)
{
LPTSTR msg;
DWORD err = GetLastError();
char buf[1024];
msg=buf;
DEBUG(("error %d\n",err));
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, // lpSource
err, // dwMessageId,
0,
//MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), // dwLanguageId,
msg,
0,
NULL
);
DEBUG(("%s\n",msg));
//LocalFree(msg);
}
typedef struct
{
HANDLE read, write;
} tl_stream;
/* Because windows is d**b, there is no way to avoid blocking on an anonymous
* pipe. We can't use CancelIO to stop reads since that's only in newer
* versions of Win***ws. So what we are left with is putting the read operation
* into a thread, timing out in the main body and ignoring this thread if we
* feel we have to.
* */
DWORD readpipe_thread(void *param)
{
ReadOpQ *io_ops = (ReadOpQ *)param;
ReadOp *iopipe_op;
/* have our own buffer since we don't want to risk that the
* caller's buffer might have disappeared by the time
* our readfile unblocks.
*/
while (1)
{
DWORD waitres = WaitForSingleObject(io_ops->semaphore, INFINITE);
iopipe_op = io_ops->last;
DEBUG(("readpipe_thread: pre-ReadFile%d: %d \n", iopipe_op->id, iopipe_op->nbytes));
iopipe_op->success = ReadFile(iopipe_op->file, iopipe_op->space, iopipe_op->nbytes, &iopipe_op->nbytes, NULL);
iopipe_op->error = GetLastError();
DEBUG(("readpipe_thread: post-ReadFile%d: %d read, err %d\n", iopipe_op->id, iopipe_op->nbytes,iopipe_op->error));
ReleaseSemaphore(iopipe_op->semaphore, 1, NULL);
}
}
proc *process_run(char executable[], char *args[], int timeout)
{
proc *retval = NULL;
char *text;
int status;
tl_stream stdout_p;
tl_stream stdin_p;
SECURITY_ATTRIBUTES saAttr;
PROCESS_INFORMATION pi;
STARTUPINFO si;
BOOL createproc_success = FALSE;
BOOL timedout = FALSE;
TCHAR *commandline = NULL;
proc *p = process_new();
if (p == NULL)
return NULL;
/* Make sure pipe handles are inherited */
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
p->causeofdeath = PROC_PIPECREATE;
DEBUG(("making pipes \n"));
/* Child's Stdout */
if ( ! CreatePipe(&stdout_p.read, &stdout_p.write, &saAttr, 1) )
{
printlasterror();
RETURN(p);
}
DEBUG(("stdout done \n"));
/* read handle to the pipe for STDOUT is not inherited */
if ( ! SetHandleInformation(stdout_p.read, HANDLE_FLAG_INHERIT, 0) )
{
printlasterror();
RETURN(p);
}
DEBUG(("stdout noinherit \n"));
/* a pipe for the child process's STDIN */
if ( ! CreatePipe(&stdin_p.read, &stdin_p.write, &saAttr, 0) )
{
printlasterror();
RETURN(p);
}
DEBUG(("stdin done \n"));
/* write handle to the pipe for STDIN not inherited */
if ( ! SetHandleInformation(stdin_p.read, HANDLE_FLAG_INHERIT, 0) )
{
printlasterror();
RETURN(p);
}
DEBUG(("pipes done \n"));
p->causeofdeath = PROC_START;
ZeroMemory( &si, sizeof(STARTUPINFO) );
ZeroMemory( &pi, sizeof(PROCESS_INFORMATION) );
si.cb = sizeof(STARTUPINFO);
si.hStdError = stdout_p.write;
si.hStdOutput = stdout_p.write;
/*
Rather than use the stdin pipe, which would be
si.hStdInput = stdin_p.read;
Pass on talon's own standard input to the child process
This helps with programs like xcopy which demand that
they are attached to a console and not just any type of
input file.
*/
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.dwFlags |= STARTF_USESTDHANDLES;
DEBUG(("pre commandline \n"));
/* create the commandline string */
int len = 0;
int i = 0;
while (args[i] != NULL)
{
len += strlen(args[i++]) + 1;
}
len+=2;
commandline = malloc(len*2);
if (! commandline)
RETURN(p);
commandline[0] = '\0';
i = 0;
while (args[i] != NULL)
{
strcat(commandline, args[i]);
strcat(commandline, " ");
i++;
}
/* Get the read thread ready to go before creating
* the process.
*/
ReadOpQ *ropq = malloc(sizeof(ReadOpQ));
ropq->first=NULL;
ropq->last=NULL;
ropq->semaphore = CreateSemaphore(NULL, 0, 1, NULL);
DEBUG(("Creating read thread. \n"));
DWORD readpipe_threadid;
HANDLE h_readpipe_thread = CreateThread(NULL, 8192, (LPTHREAD_START_ROUTINE) readpipe_thread, (void*)ropq, 0, &readpipe_threadid);
/* ready to run the process */
DEBUG(("process commandline:\n%s \n", commandline));
DEBUG(("\n"));
createproc_success = CreateProcess(executable,
commandline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&si, // STARTUPINFO pointer
&pi); // receives PROCESS_INFORMATION
if (! createproc_success)
{
DEBUG(("Createprocess failed. \n"));
p->causeofdeath = PROC_SOMEODDDEATH;
RETURN(p);
}
int have_status = 0;
DEBUG(("Closing Handles. \n"));
if (!CloseHandle(stdout_p.write))
RETURN(p);
if (!CloseHandle(stdin_p.read))
RETURN(p);
DEBUG(("Closed Handles. \n"));
static int id=0;
do
{
char *space = buffer_makespace(p->output, READSIZE);
DWORD waitres;
ReadOp *iopipe_op = malloc(sizeof(ReadOp));
iopipe_op->semaphore = CreateSemaphore(NULL, 0, 1, NULL);
iopipe_op->thread = h_readpipe_thread;
iopipe_op->timeout = timeout;
iopipe_op->file = stdout_p.read;
iopipe_op->space = malloc(READSIZE);
iopipe_op->id = id++;
iopipe_op->nbytes = READSIZE;
iopipe_op->next = NULL;
if (!ropq->first)
{
ropq->first = iopipe_op;
ropq->last = iopipe_op;
} else {
ropq->last->next = iopipe_op;
ropq->last = iopipe_op;
}
ReleaseSemaphore(ropq->semaphore, 1, NULL);
DEBUG(("waiting for read %d\n", timeout));
waitres = WaitForSingleObject(iopipe_op->semaphore, timeout);
DEBUG(("read wait finished result= %d\n", waitres));
if (waitres != WAIT_OBJECT_0)
{
DEBUG(("timeout \n"));
timedout = TRUE;
break;
}
else
{
DEBUG(("read signalled: nbytes: %d \n", iopipe_op->nbytes));
if (iopipe_op->nbytes <= 0)
{
break;
}
memcpy(space, iopipe_op->space, iopipe_op->nbytes);
buffer_usespace(p->output, iopipe_op->nbytes);
DEBUG(("buffer took on nbytes: %d \n", iopipe_op->nbytes));
}
}
while (1);
if (timedout == FALSE)
{
DEBUG(("Wait for process exit\n"));
// Wait until child process exits.
WaitForSingleObject(pi.hProcess, INFINITE);
DEBUG(("Process exited\n"));
DWORD exitcode;
if (GetExitCodeProcess(pi.hProcess, &exitcode))
{
p->causeofdeath = PROC_NORMALDEATH;
p->returncode = exitcode;
DEBUG(("process exited normally = %d:\n", p->returncode));
RETURN(p);
} else {
p->causeofdeath = PROC_SOMEODDDEATH;
p->returncode = 128;
DEBUG(("process terminated \n"));
RETURN(p);
}
} else {
TerminateProcess(pi.hProcess,1);
p->causeofdeath = PROC_TIMEOUTDEATH;
p->returncode = 128;
DEBUG(("process timedout \n"));
RETURN(p);
}
/* Clean up the read operation queue
ReadOp *r = ropq.first;
do
{
CloseHandle(r->semaphore);
free(r->space);
free(r);
r = r->next;
} while (r != NULL); */
CLEANUP();
if (retval == NULL)
{
if (p)
process_free(&p);
}
if (commandline)
free(commandline);
return retval;
}
void process_free(proc **pp)
{
if (!pp)
return;
if (! *pp)
return;
if ((*pp)->output)
buffer_free(&((*pp)->output));
free(*pp);
*pp = NULL;
}