961N/A/*
961N/A* CDDL HEADER START
961N/A*
961N/A* The contents of this file are subject to the terms of the
961N/A* Common Development and Distribution License, Version 1.0 only
961N/A* (the "License"). You may not use this file except in compliance
961N/A* with the License.
961N/A*
961N/A* You can obtain a copy of the license at
961N/A* trunk/opends/resource/legal-notices/OpenDS.LICENSE
961N/A* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
961N/A* See the License for the specific language governing permissions
961N/A* and limitations under the License.
961N/A*
961N/A* When distributing Covered Code, include this CDDL HEADER in each
961N/A* file and include the License file at
961N/A* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
961N/A* add the following below this CDDL HEADER, with the fields enclosed
961N/A* by brackets "[]" replaced with your own identifying * information:
961N/A* Portions Copyright [yyyy] [name of copyright owner]
961N/A*
961N/A* CDDL HEADER END
961N/A*
961N/A*
5072N/A* Copyright 2008-2010 Sun Microsystems, Inc.
6332N/A* Portions Copyright 2013 ForgeRock AS
961N/A*/
961N/A
961N/A#include "common.h"
5072N/A#include "service.h"
1298N/A#include <errno.h>
1298N/A#include <fcntl.h>
1298N/A#include <io.h>
1298N/A#include <stdio.h>
1298N/A#include <sys/locking.h>
1298N/A#include <time.h>
1298N/A
1298N/ABOOL DEBUG = TRUE;
1298N/Achar * DEBUG_LOG_NAME = "native-windows.out";
1298N/ADWORD MAX_DEBUG_LOG_SIZE = 500 * 1000;
1298N/Achar * getDebugLogFileName();
1298N/Avoid debugInner(BOOL isError, const char *msg, va_list ap);
1298N/Avoid deleteIfLargerThan(char * fileName, DWORD maxSize);
6332N/ABOOL isExistingDirectory(char * fileName);
961N/A
961N/A// ----------------------------------------------------
961N/A// Function used to create a process with the given command.
961N/A// The information about the process is stored in procInfo.
961N/A// The function returns TRUE if the process could be created
961N/A// and FALSE otherwise.
961N/A// ----------------------------------------------------
961N/ABOOL createChildProcess(char* command, BOOL background,
961N/APROCESS_INFORMATION* procInfo)
961N/A{
961N/A BOOL createOk;
961N/A STARTUPINFO startInfo; // info to pass to the new process
961N/A DWORD processFlag; // background process flag
3765N/A HANDLE hStdin; /* stdin */
3765N/A HANDLE hStdout; /* stdout */
3765N/A HANDLE hStderr; /* stderr */
961N/A
5072N/A debug("createChildProcess: Attempting to create child process '%s' background=%d.",
5072N/A command,
1878N/A background);
1298N/A
961N/A // reset process info first
961N/A ZeroMemory(procInfo, sizeof(PROCESS_INFORMATION));
961N/A
961N/A // initialize handles to pass to the child process
961N/A ZeroMemory(&startInfo, sizeof(STARTUPINFO));
961N/A startInfo.cb = sizeof(STARTUPINFO);
961N/A startInfo.dwFlags |= STARTF_USESTDHANDLES; // use handles above
3765N/A
3765N/A hStdin= GetStdHandle(STD_INPUT_HANDLE);
3765N/A SetHandleInformation (hStdin, HANDLE_FLAG_INHERIT, FALSE);
3765N/A hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
3765N/A SetHandleInformation (hStdout, HANDLE_FLAG_INHERIT, FALSE);
3765N/A hStderr = GetStdHandle(STD_ERROR_HANDLE);
3765N/A SetHandleInformation (hStderr, HANDLE_FLAG_INHERIT, FALSE);
961N/A
961N/A // Create the child process
961N/A processFlag = background == TRUE ? DETACHED_PROCESS : 0;
961N/A createOk = CreateProcess(
1298N/A NULL, // application name
1298N/A command, // command line
1298N/A NULL, // process security attributes
1298N/A NULL, // primary thread security attributes
1298N/A TRUE, // handles are inherited
1298N/A processFlag, // creation flags
1298N/A NULL, // use parent's environment
1298N/A NULL, // use parent's current directory
1298N/A &startInfo, // STARTUPINFO pointer
1298N/A procInfo // receives PROCESS_INFORMATION
961N/A );
961N/A
1298N/A if (createOk)
1298N/A {
5072N/A debug("createChildProcess: Successfully created child process '%s'.", command);
1298N/A }
1298N/A else
1298N/A {
5072N/A debugError(
5072N/A "createChildProcess: Failed to create child process '%s'. Last error = %d.",
1878N/A command, GetLastError());
1298N/A }
1298N/A
961N/A return createOk;
961N/A} // createChildProcess
961N/A
5072N/ABOOL createBatchFileChildProcess(char* batchFile, BOOL background,
5072N/APROCESS_INFORMATION* procInfo)
5072N/A{
5072N/A BOOL createOk;
5072N/A STARTUPINFO startInfo; // info to pass to the new process
5072N/A DWORD processFlag; // background process flag
5072N/A HANDLE hStdin; /* stdin */
5072N/A HANDLE hStdout; /* stdout */
5072N/A HANDLE hStderr; /* stderr */
5072N/A char command[COMMAND_SIZE]; // full command line
5072N/A
5072N/A if (strlen(batchFile) + 3 >= COMMAND_SIZE)
5072N/A {
5072N/A debug("createBatchFileChildProcess: the batch file path is too long.");
5072N/A return FALSE;
5072N/A }
5072N/A sprintf(command, "/c %s", batchFile);
5072N/A debug("createBatchFileChildProcess: Attempting to create child process '%s' background=%d.",
5072N/A command,
5072N/A background);
5072N/A
5072N/A // reset process info first
5072N/A ZeroMemory(procInfo, sizeof(PROCESS_INFORMATION));
5072N/A
5072N/A // initialize handles to pass to the child process
5072N/A ZeroMemory(&startInfo, sizeof(STARTUPINFO));
5072N/A startInfo.cb = sizeof(STARTUPINFO);
5072N/A startInfo.dwFlags |= STARTF_USESTDHANDLES; // use handles above
5072N/A
5072N/A hStdin= GetStdHandle(STD_INPUT_HANDLE);
5072N/A SetHandleInformation (hStdin, HANDLE_FLAG_INHERIT, FALSE);
5072N/A hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
5072N/A SetHandleInformation (hStdout, HANDLE_FLAG_INHERIT, FALSE);
5072N/A hStderr = GetStdHandle(STD_ERROR_HANDLE);
5072N/A SetHandleInformation (hStderr, HANDLE_FLAG_INHERIT, FALSE);
5072N/A
5072N/A // Create the child process
5072N/A processFlag = background == TRUE ? DETACHED_PROCESS : 0;
5072N/A createOk = CreateProcess(
5072N/A "cmd.exe", // application name
5072N/A command, // command line
5072N/A NULL, // process security attributes
5072N/A NULL, // primary thread security attributes
5072N/A TRUE, // handles are inherited
5072N/A processFlag, // creation flags
5072N/A NULL, // use parent's environment
5072N/A NULL, // use parent's current directory
5072N/A &startInfo, // STARTUPINFO pointer
5072N/A procInfo // receives PROCESS_INFORMATION
5072N/A );
5072N/A
5072N/A if (createOk)
5072N/A {
5072N/A debug("createBatchFileChildProcess: Successfully created child process '%s'.", command);
5072N/A }
5072N/A else
5072N/A {
5072N/A debugError("createBatchFileChildProcess: Failed to create child process '%s'. Last error = %d.",
5072N/A command, GetLastError());
5072N/A }
5072N/A
5072N/A return createOk;
5072N/A} // createChildProcess
5072N/A
5072N/A
961N/A// ----------------------------------------------------
961N/A// Function used to launch a process for the given command
961N/A// If the process could be created it returns the pid of
961N/A// the created process and -1 otherwise.
961N/A// ----------------------------------------------------
5072N/Aint spawn(char* command, BOOL background)
961N/A{
1298N/A DWORD childPid = -1; // child's pid
961N/A PROCESS_INFORMATION procInfo; // info on the new process
961N/A BOOL createOk;
961N/A
5072N/A createOk = createChildProcess(command, background, &procInfo);
961N/A
961N/A if(createOk)
961N/A {
961N/A childPid = procInfo.dwProcessId;
961N/A }
961N/A
961N/A if (childPid != -1)
961N/A {
1298N/A debug("The PID of the spawned process is %d.", childPid);
961N/A return childPid;
961N/A }
961N/A else
961N/A {
1298N/A debugError("Could not get the PID of the spawned process.");
961N/A return -1;
961N/A }
961N/A} // spawn
961N/A
5072N/A// ----------------------------------------------------
5072N/A// Function used to wait for a process.
5072N/A// The passed waitTime parameter is maximum the time in milliseconds to wait.
5072N/A// Returns TRUE if the process ended and updates the exitCode
5072N/A// parameter with the return value of the process.
5072N/A// Returns FALSE if the process did not end with the provided
5072N/A// timeout and the error code returned by WaitForSingleObject
5072N/A// in the provided exitCode value.
5072N/A// ----------------------------------------------------
5072N/ABOOL waitForProcess(PROCESS_INFORMATION* procInfo, DWORD waitTime,
5072N/A DWORD* exitCode)
5072N/A{
5072N/A BOOL returnValue = TRUE;
5072N/A DWORD waitForSingleCode;
5072N/A debug("waitForProcess: wait time is: %d", waitTime);
5072N/A waitForSingleCode = WaitForSingleObject (procInfo->hProcess, waitTime);
5072N/A if (waitForSingleCode == WAIT_OBJECT_0)
5072N/A {
5072N/A debug("waitForProcess: was successful");
5072N/A GetExitCodeProcess(procInfo->hProcess, exitCode);
5072N/A debug("waitForProcess exitCode: %d", *exitCode);
5072N/A }
5072N/A else
5072N/A {
5072N/A returnValue = FALSE;
5072N/A switch (waitForSingleCode)
5072N/A {
5072N/A case WAIT_FAILED:
5072N/A debug("waitForProcess: Wait for process failed: %d", GetLastError());
5072N/A break;
5072N/A case WAIT_TIMEOUT:
5072N/A debug("waitForProcess: Process timed out.");
5072N/A break;
5072N/A default:
5072N/A debug("waitForProcess: WaitForSingleObject returned %d", waitForSingleCode);
5072N/A }
5072N/A *exitCode = waitForSingleCode;
5072N/A }
5072N/A return returnValue;
5072N/A}
1298N/A// ---------------------------------------------------
1298N/A// Debug utility.
1298N/A// ---------------------------------------------------
1298N/Avoid debug(const char *msg, ...)
1298N/A{
1298N/A va_list ap;
1298N/A va_start (ap, msg);
1298N/A debugInner(FALSE, msg, ap);
1298N/A va_end (ap);
1298N/A}
1298N/A
1298N/Avoid debugError(const char *msg, ...)
1298N/A{
1298N/A va_list ap;
1298N/A va_start (ap, msg);
1298N/A debugInner(TRUE, msg, ap);
1298N/A va_end (ap);
1298N/A}
1298N/A
1298N/Avoid debugInner(BOOL isError, const char *msg, va_list ap)
1298N/A{
1298N/A static DWORD currentProcessPid = 0;
1298N/A static BOOL noMessageLogged = TRUE;
1298N/A
1298N/A // The file containing the log.
1298N/A char * logFile;
1298N/A FILE *fp;
1298N/A time_t rawtime;
1298N/A struct tm * timeinfo;
1298N/A char formattedTime[100];
1298N/A
1298N/A if (noMessageLogged)
1298N/A {
1298N/A currentProcessPid = GetCurrentProcessId();
1298N/A noMessageLogged = FALSE;
1878N/A debug("--------------- FIRST LOG MESSAGE FROM '%s' ---------------",
1878N/A _pgmptr);
1298N/A }
1298N/A
1298N/A // Time-stamp
1298N/A time(&rawtime);
1298N/A timeinfo = localtime(&rawtime);
1298N/A strftime(formattedTime, 100, "%Y/%m/%d %H:%M:%S", timeinfo);
1298N/A
1298N/A logFile = getDebugLogFileName();
1298N/A deleteIfLargerThan(logFile, MAX_DEBUG_LOG_SIZE);
1298N/A if ((fp = fopen(logFile, "a")) != NULL)
1298N/A {
1298N/A fprintf(fp, "%s: (pid=%d) ", formattedTime, currentProcessPid);
1298N/A if (isError)
1298N/A {
1298N/A fprintf(fp, "ERROR: ");
1298N/A // It would be nice to echo to stderr, but that doesn't appear to work.
1298N/A }
1298N/A
1298N/A vfprintf(fp, msg, ap);
6332N/A
6332N/A fprintf(fp, "\n");
1298N/A fclose(fp);
1298N/A }
1298N/A else
1298N/A {
1298N/A fprintf(stdout, "Could not create log file.\n");
1298N/A }
1298N/A}
1298N/A
1298N/A// ---------------------------------------------------------------
1298N/A// Get the fully-qualified debug log file name. The logic in this
1298N/A// method assumes that the executable of this process is in a
1298N/A// direct subdirectory of the instance root.
1298N/A// ---------------------------------------------------------------
1298N/A
1298N/Achar * getDebugLogFileName()
1298N/A{
1298N/A static char * logFile = NULL;
1298N/A char path [MAX_PATH];
1298N/A char execName [MAX_PATH];
1298N/A char * lastSlash;
6332N/A char logpath[MAX_PATH];
6332N/A char * temp;
6332N/A FILE *file;
1298N/A
1298N/A if (logFile != NULL)
1298N/A {
1298N/A return logFile;
1298N/A }
6332N/A temp = getenv("TEMP");
6332N/A
1298N/A // Get the name of the executable.
1298N/A GetModuleFileName (
1298N/A NULL,
1298N/A execName,
1298N/A MAX_PATH
1298N/A );
1298N/A
1878N/A // Cut everything after the last slash, twice. This will take us back to the
1878N/A // instance root.
1298N/A // This logic assumes that we are in a directory above the instance root.
1298N/A lastSlash = strrchr(execName, '\\');
1298N/A lastSlash[0] = '\0';
1298N/A lastSlash = strrchr(execName, '\\');
1298N/A lastSlash[0] = '\0';
1298N/A
6332N/A // Instance root is in execName (eg. C:\opendj
6332N/A // and adds the log's folder name to it
6332N/A strcpy(logpath, execName);
6332N/A strcat(logpath, "\\logs\\");
6332N/A
6332N/A // If the log folder doesn's exist in the instance path
6332N/A // we create the log file in the temp directory.
6332N/A if (isExistingDirectory(logpath))
6332N/A {
6332N/A sprintf(path, "%s\\logs\\%s", execName, DEBUG_LOG_NAME);
6332N/A } else {
6332N/A strcat(temp, "\\logs\\");
6332N/A mkdir(temp);
6332N/A strcat(temp, DEBUG_LOG_NAME);
6332N/A file = fopen(temp,"a+");
6332N/A fclose(file);
6332N/A sprintf(path, "%s", temp);
6332N/A }
6332N/A
1298N/A logFile = _strdup(path);
1298N/A return logFile;
1298N/A}
1298N/A
1298N/A// ---------------------------------------------------------------
1298N/A// Function called to know if the --debug option was passed
1298N/A// when calling this executable or not. The DEBUG variable is
1298N/A// updated accordingly.
1298N/A// ---------------------------------------------------------------
1298N/A
1298N/Avoid updateDebugFlag(char* argv[], int argc)
1298N/A{
1298N/A int i;
1298N/A DEBUG = FALSE;
1298N/A for (i=1; (i<argc) && !DEBUG; i++)
1298N/A {
1298N/A if (strcmp(argv[i], "--debug") == 0)
1298N/A {
1298N/A DEBUG = TRUE;
1298N/A }
1298N/A }
1298N/A}
1298N/A
1298N/A// ---------------------------------------------------------------
1298N/A// Deletes a file if it's larger than the given maximum size.
1298N/A// ---------------------------------------------------------------
1298N/A
1298N/Avoid deleteIfLargerThan(char * fileName, DWORD maxSize)
1298N/A{
1298N/A DWORD fileSize = 0;
1298N/A HANDLE fileHandle = CreateFile(
1298N/A fileName,
1298N/A 0,
1298N/A FILE_SHARE_READ | FILE_SHARE_WRITE,
1298N/A NULL,
1298N/A OPEN_EXISTING,
1298N/A 0,
1298N/A NULL
1298N/A );
1298N/A
1298N/A if (fileHandle == INVALID_HANDLE_VALUE)
1298N/A {
1298N/A return;
1298N/A }
1298N/A
1298N/A fileSize = GetFileSize(fileHandle, NULL);
1298N/A
1298N/A CloseHandle(fileHandle);
1298N/A
1298N/A if (fileSize > maxSize)
1298N/A {
1298N/A DeleteFile(fileName);
1298N/A }
1298N/A}
6332N/A
6332N/A// ---------------------------------------------------------------
6332N/A// Checks if the specifed directory exist.
6332N/A// ---------------------------------------------------------------
6332N/ABOOL isExistingDirectory(char * fileName)
6332N/A{
6332N/A DWORD str = GetFileAttributes(fileName);
6332N/A
6332N/A return (str != INVALID_FILE_ATTRIBUTES &&
6332N/A (str & FILE_ATTRIBUTE_DIRECTORY));
6332N/A}