winlauncher.c revision f71f7a61dec7c9089378d14493ad564a1dedf0b5
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Portions Copyright 2007 Sun Microsystems, Inc.
*/
#include "winlauncher.h"
// ----------------------------------------------------
// Generates the pid file name for a given instanceDir.
// Returns TRUE if the command name could be initiated and
// FALSE otherwise (buffer overflow because the resulting
// string is bigger than maxSize).
// ----------------------------------------------------
BOOL getPidFile(const char* instanceDir, char* pidFile, unsigned int maxSize)
{
BOOL returnValue;
char* relativePath = "\\logs\\server.pid";
if ((strlen(relativePath) + strlen(instanceDir)) < maxSize)
{
sprintf(pidFile, "%s\\logs\\server.pid", instanceDir);
returnValue = TRUE;
}
else
{
returnValue = FALSE;
}
return returnValue;
} // getPidFile
// ----------------------------------------------------
// Tells whether a file exists or not. If the file exists
// returns TRUE and FALSE otherwise.
// ----------------------------------------------------
BOOL fileExists(const char *fileName)
{
struct stat finfo;
BOOL returnValue = FALSE;
if(stat(fileName, &finfo) < 0)
{
returnValue = FALSE;
}
else
{
returnValue = TRUE;
}
return returnValue;
} // fileExists
// ----------------------------------------------------
// Deletes the pid file for a given instance directory.
// If the file could be deleted (or it does not exist)
// returns TRUE and FALSE otherwise.
// ----------------------------------------------------
BOOL deletePidFile(const char* instanceDir)
{
BOOL returnValue = FALSE;
char pidFile[PATH_SIZE];
int nTries = 10;
// Sometimes the lock on the system in windows takes time to be released.
if (getPidFile(instanceDir, pidFile, PATH_SIZE))
{
while (fileExists(pidFile) && (nTries > 0) && !returnValue)
{
if (remove(pidFile) == 0)
{
returnValue = TRUE;
}
else
{
Sleep(500);
nTries--;
}
}
}
return returnValue;
} // deletePidFile
// ----------------------------------------------------
// Returns the pid stored in the pid file for a given server
// instance directory. If the pid could not be retrieved
// it returns 0.
// ----------------------------------------------------
int getPid(const char* instanceDir)
{
int returnValue;
char pidFile[PATH_SIZE];
FILE *f;
char buf[BUF_SIZE];
int read;
if (getPidFile(instanceDir, pidFile, PATH_SIZE))
{
if ((f = fopen(pidFile, "r")) != NULL)
{
read = fread(buf, 1, sizeof(buf),f);
}
fclose(f);
returnValue = (int)strtol(buf, (char **)NULL, 10);
}
else
{
returnValue = 0;
}
return returnValue;
} // getPid
// ----------------------------------------------------
// Kills the process associated with the provided pid.
// Returns TRUE if the process could be killed or the
// process did not exist and false otherwise.
// ----------------------------------------------------
BOOL killProcess(int pid)
{
BOOL processDead;
HANDLE procHandle = OpenProcess(
PROCESS_TERMINATE // to terminate the process
| PROCESS_QUERY_INFORMATION, // to get exit code
FALSE, // handle is not inheritable
pid
);
if (procHandle == NULL)
{
// process already dead
processDead = TRUE;
}
else
{
if (!TerminateProcess(procHandle, 0))
{
// failed to terminate the process
processDead = FALSE;
}
else
{
// wait for the process to end.
DWORD exitCode;
int nTries = 20;
processDead = FALSE;
while ((nTries > 0) && !processDead)
{
GetExitCodeProcess(procHandle, &exitCode);
if (exitCode == STILL_ACTIVE)
{
// process is still alive, let's wait 1 sec and loop again
Sleep(1000);
nTries--;
}
else
{
processDead = TRUE;
}
}
}
CloseHandle(procHandle);
}
return processDead;
} // killProcess
// ----------------------------------------------------
// Creates the pid file for a given instance directory.
// and a given pid.
// If the file could be created returns TRUE and FALSE
// otherwise.
// ----------------------------------------------------
BOOL createPidFile(const char* instanceDir, int pid)
{
BOOL returnValue = FALSE;
char pidFile[PATH_SIZE];
FILE *f;
if (getPidFile(instanceDir, pidFile, PATH_SIZE))
{
if ((f = fopen(pidFile, "w")) != NULL)
{
fprintf(f, "%d", pid);
fclose (f);
returnValue = TRUE;
}
else
{
returnValue = FALSE;
}
}
else
{
returnValue = FALSE;
}
return returnValue;
} // createPidFile
// ----------------------------------------------------
// Function used to create a process with the given command.
// The information about the process is stored in procInfo.
// The function returns TRUE if the process could be created
// and FALSE otherwise.
// ----------------------------------------------------
BOOL createChildProcess(char* command, BOOL background,
PROCESS_INFORMATION* procInfo)
{
BOOL createOk;
STARTUPINFO startInfo; // info to pass to the new process
DWORD processFlag; // background process flag
// reset process info first
ZeroMemory(procInfo, sizeof(PROCESS_INFORMATION));
// initialize handles to pass to the child process
ZeroMemory(&startInfo, sizeof(STARTUPINFO));
startInfo.cb = sizeof(STARTUPINFO);
startInfo.dwFlags |= STARTF_USESTDHANDLES; // use handles above
// Create the child process
processFlag = background == TRUE ? DETACHED_PROCESS : 0;
createOk = CreateProcess(
NULL, // application name
command, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
processFlag, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&startInfo, // STARTUPINFO pointer
procInfo // receives PROCESS_INFORMATION
);
if (!createOk)
{
fprintf(stderr, "Failed to create child process [%s]\n", command);
}
return createOk;
} // createChildProcess
// ----------------------------------------------------
// Function used to launch a process for the given command
// If the process could be created it returns the pid of
// the created process and -1 otherwise.
// ----------------------------------------------------
int spawn(const char* command)
{
DWORD childPid; // child's pid
PROCESS_INFORMATION procInfo; // info on the new process
BOOL createOk;
createOk = createChildProcess((char*)command, TRUE, &procInfo);
if(createOk)
{
childPid = procInfo.dwProcessId;
}
if (childPid != -1)
{
return childPid;
}
else
{
return -1;
}
} // spawn
// ----------------------------------------------------
// Elaborate the command line: "cmd arg1 arg2..."
// If an arg contains white space(s) then add " " to protect them
// but don't do it for option (an option starts with -).
// Returns TRUE if the command name could be initiated and
// FALSE otherwise (buffer overflow because the resulting
// string is bigger than maxSize).
// ----------------------------------------------------
BOOL getCommandLine(const char* argv[], char* command, unsigned int maxSize)
{
int curCmdInd = 0;
int i = 0;
BOOL overflow = FALSE;
while ((argv[i] != NULL) && !overflow)
{
const char* curarg = argv[i++];
if (i > 1)
{
if (curCmdInd + strlen(" ") < maxSize)
{
sprintf (&command[curCmdInd], " ");
curCmdInd = strlen(command);
}
else
{
overflow = TRUE;
}
}
if (curarg[0] != '\0')
{
int argInd = 0;
if (curarg[0] == '"')
{
// there is a quote: no need to add extra quotes
}
else
{
while (curarg[argInd] != ' '
&& curarg[argInd] != '\0'
&& curarg[argInd] != '\n')
{
argInd++;
}
}
if (curarg[0] != '"' && curarg[argInd] == ' ')
{
if (curCmdInd + strlen("\"\"") + strlen(curarg) < maxSize)
{
// no begining quote and white space inside => add quotes
sprintf (&command[curCmdInd], "\"%s\"", curarg);
curCmdInd = strlen (command);
}
else
{
overflow = TRUE;
}
}
else
{
if (curCmdInd + strlen(curarg) < maxSize)
{
// no white space or quotes detected, keep the arg as is
sprintf (&command[curCmdInd], "%s", curarg);
curCmdInd = strlen (command);
}
else
{
overflow = TRUE;
}
}
} else {
if (curCmdInd + strlen("\"\"") < maxSize)
{
sprintf (&command[curCmdInd], "\"\"");
curCmdInd = strlen (command);
}
else
{
overflow = TRUE;
}
}
}
return !overflow;
} // getCommandLine
// ----------------------------------------------------
// Function called when we want to start the server.
// This function expects the following parameter to be passed:
// the directory of the server we want to start and the
// command line (and its argumets) that we want to execute (basically the java
// command that we want to start the server). The main reasons
// to have the command line passed are:
// 1. Keep the native code as minimal as possible.
// 2. Allow the administrator some flexibility in the way the
// server is started by leaving most of the logic in the command-line.
//
// This approach makes things to be closer between what is proposed
// in windows and in UNIX systems.
//
// If the instance could be started the code will write the pid of the process
// of the server in file that can be used for instance to stop the server
// (see stop.c).
//
// Returns the pid of the process of the instance if it could be started and -1
// otherwise.
// ----------------------------------------------------
int start(const char* instanceDir, char* argv[])
{
int returnValue;
int childPid;
char command[COMMAND_SIZE];
if (getCommandLine(argv, command, COMMAND_SIZE))
{
childPid = spawn(command);
if (childPid > 0)
{
createPidFile(instanceDir, childPid);
returnValue = childPid;
}
else
{
returnValue = -1;
}
}
else
{
returnValue = -1;
}
return returnValue;
} // start
// ----------------------------------------------------
// Function called when we want to stop the server.
// This code is called by the stop-ds.bat batch file to stop the server
// in windows.
// This function expects just one parameter to be passed
// to the executable: the directory of the server we want
// to stop.
//
// If the instance could be stopped the pid file
// is removed. This is done for security reasons: if we do
// not delete the pid file and the old pid of the process
// is used by a new process, when we call again this executable
// the new process will be killed.
// Note: even if the code in the class org.opends.server.core.DirectoryServer
// sets the pid file to be deleted on the exit of the process
// the file is not always deleted.
//
// Returns 0 if the instance could be stopped using the
// pid stored in a file of the server installation and
// -1 otherwise.
// ----------------------------------------------------
int stop(const char* instanceDir)
{
int returnCode = -1;
int childPid;
childPid = getPid(instanceDir);
if (childPid != 0)
{
if (killProcess(childPid))
{
returnCode = 0;
deletePidFile(instanceDir);
}
}
return returnCode;
} // stop
// ----------------------------------------------------
// Function called when we want to launch simply a process without attaching
// it to any command prompt (the difference with start is basically that here
// we create no pid file).
// This code is called for instance by the statuspanel.bat batch file to launch
// the status panel on windows.
// The goal of these methods is:
// Be able to launch batch files with double-click and not having a
// prompt-window associated with it.
// Launch batch files from the prompt that generate a java process that does not
// block the prompt and that keeps running even if the prompt window is closed.
//
// This function expects the following parameter to be passed:
// the directory of the server we want to start and the
// command line that we want to execute (basically the java
// command that we want to display the status panel). The main reasons
// to have the command line passed are:
// 1. Keep the native code as minimal as possible.
// 2. Allow the administrator some flexibility in the way the
// server is started by leaving most of the logic in the command-line.
//
// Returns the pid of the process associated with the command if it could be
// launched and -1 otherwise.
// ----------------------------------------------------
int launch(char* argv[])
{
int returnValue;
char command[COMMAND_SIZE];
if (getCommandLine(argv, command, COMMAND_SIZE))
{
returnValue = spawn(command);
}
else
{
returnValue = -1;
}
return returnValue;
} // launch
// ----------------------------------------------------
// main function called by the executable. This code is
// called by the start-ds.bat, stop-ds.bat and statuspanel.bat batch files.
//
// The code assumes that the first passed argument is the subcommand to be
// executed and the second argument the directory of the server. The rest
// of the arguments are the arguments specific to each subcommand (see the
// comments for the functions start, stop and launch).
// ----------------------------------------------------
int main(int argc, char* argv[])
{
int returnCode;
char* subcommand = argv[1];
char* instanceDir = argv[2];
argv += 3;
if (strcmp(subcommand, "start") == 0)
{
returnCode = start(instanceDir, argv);
}
else if (strcmp(subcommand, "stop") == 0)
{
returnCode = stop(instanceDir);
}
else if (strcmp(subcommand, "launch") == 0)
{
returnCode = launch(argv);
}
else
{
fprintf(stderr, "Unknown subcommand: [%s]", subcommand);
returnCode = -1;
}
return returnCode;
}