winlauncher.c revision 1300
826N/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*
961N/A* Portions Copyright 2007 Sun Microsystems, Inc.
961N/A*/
961N/A
826N/A#include "winlauncher.h"
826N/A
826N/A// ----------------------------------------------------
826N/A// Generates the pid file name for a given instanceDir.
826N/A// Returns TRUE if the command name could be initiated and
826N/A// FALSE otherwise (buffer overflow because the resulting
826N/A// string is bigger than maxSize).
826N/A// ----------------------------------------------------
826N/ABOOL getPidFile(const char* instanceDir, char* pidFile, unsigned int maxSize)
826N/A{
961N/A BOOL returnValue;
961N/A char* relativePath = "\\logs\\server.pid";
1298N/A
1298N/A debug("Attempting to get the PID file for instanceDir='%s'", instanceDir);
1298N/A
961N/A if ((strlen(relativePath) + strlen(instanceDir)) < maxSize)
961N/A {
1300N/A sprintf(pidFile, "%s\\logs\\server.pid", instanceDir);
961N/A returnValue = TRUE;
1298N/A debug("PID file name is '%s'.", pidFile);
961N/A }
961N/A else
961N/A {
1298N/A debugError("Unable to get the PID file name because the path was too long.");
961N/A returnValue = FALSE;
961N/A }
961N/A return returnValue;
826N/A} // getPidFile
826N/A
826N/A
826N/A// ----------------------------------------------------
826N/A// Tells whether a file exists or not. If the file exists
826N/A// returns TRUE and FALSE otherwise.
826N/A// ----------------------------------------------------
826N/ABOOL fileExists(const char *fileName)
826N/A{
961N/A struct stat finfo;
961N/A BOOL returnValue = FALSE;
961N/A
961N/A if(stat(fileName, &finfo) < 0)
961N/A {
961N/A returnValue = FALSE;
961N/A }
961N/A else
961N/A {
961N/A returnValue = TRUE;
961N/A }
1298N/A
1298N/A debug("File '%s' does%s exist.", fileName, (returnValue ? "" : " not"));
1298N/A
961N/A return returnValue;
826N/A} // fileExists
826N/A
826N/A
826N/A// ----------------------------------------------------
826N/A// Deletes the pid file for a given instance directory.
826N/A// If the file could be deleted (or it does not exist)
826N/A// returns TRUE and FALSE otherwise.
826N/A// ----------------------------------------------------
826N/ABOOL deletePidFile(const char* instanceDir)
826N/A{
961N/A BOOL returnValue = FALSE;
961N/A char pidFile[PATH_SIZE];
961N/A int nTries = 10;
961N/A
1298N/A debug("Attempting to delete the PID file from instanceDir='%s'.", instanceDir);
961N/A // Sometimes the lock on the system in windows takes time to be released.
961N/A if (getPidFile(instanceDir, pidFile, PATH_SIZE))
961N/A {
961N/A while (fileExists(pidFile) && (nTries > 0) && !returnValue)
826N/A {
1298N/A debug("PID file '%s' exists, attempting to remove it.", instanceDir);
961N/A if (remove(pidFile) == 0)
961N/A {
1298N/A debug("Successfully removed PID file: '%s'.", pidFile);
961N/A returnValue = TRUE;
961N/A }
961N/A else
961N/A {
1298N/A nTries--;
1298N/A debug("Failed to remove the PID file. Sleeping for a bit. Will try %d more time(s).", nTries);
961N/A Sleep(500);
961N/A }
826N/A }
961N/A }
961N/A
1298N/A debug("deletePidFile('%s') returning %d.", instanceDir, returnValue);
961N/A return returnValue;
826N/A} // deletePidFile
826N/A
826N/A
826N/A// ----------------------------------------------------
826N/A// Returns the pid stored in the pid file for a given server
826N/A// instance directory. If the pid could not be retrieved
826N/A// it returns 0.
826N/A// ----------------------------------------------------
826N/Aint getPid(const char* instanceDir)
826N/A{
961N/A int returnValue;
961N/A char pidFile[PATH_SIZE];
961N/A FILE *f;
961N/A char buf[BUF_SIZE];
961N/A int read;
961N/A
1298N/A debug("Attempting to get the PID for the server rooted at '%s'.", instanceDir);
961N/A if (getPidFile(instanceDir, pidFile, PATH_SIZE))
961N/A {
961N/A if ((f = fopen(pidFile, "r")) != NULL)
826N/A {
961N/A read = fread(buf, 1, sizeof(buf),f);
1298N/A debug("Read '%s' from the PID file '%s'.", buf, pidFile);
961N/A }
961N/A
961N/A if (f != NULL)
961N/A {
961N/A fclose(f);
961N/A returnValue = (int)strtol(buf, (char **)NULL, 10);
826N/A }
826N/A else
826N/A {
1298N/A char * msg = "File %s could not be opened.\nMost likely the server has already stopped.\n\n";
1298N/A debug(msg, pidFile);
1298N/A fprintf(stderr, msg, pidFile);
961N/A returnValue = 0;
826N/A }
961N/A }
961N/A else
961N/A {
961N/A returnValue = 0;
961N/A }
1298N/A debug("getPid('%s') returning %d.", instanceDir, returnValue);
961N/A return returnValue;
826N/A} // getPid
826N/A
826N/A
826N/A// ----------------------------------------------------
826N/A// Kills the process associated with the provided pid.
826N/A// Returns TRUE if the process could be killed or the
826N/A// process did not exist and false otherwise.
826N/A// ----------------------------------------------------
826N/ABOOL killProcess(int pid)
826N/A{
961N/A BOOL processDead;
1298N/A HANDLE procHandle;
1298N/A
1298N/A debug("killProcess(pid=%d)", pid);
1298N/A
1298N/A debug("Opening process with pid=%d.", pid);
1298N/A procHandle = OpenProcess(
961N/A PROCESS_TERMINATE // to terminate the process
961N/A | PROCESS_QUERY_INFORMATION, // to get exit code
961N/A FALSE, // handle is not inheritable
961N/A pid
961N/A );
961N/A
961N/A if (procHandle == NULL)
961N/A {
1298N/A debug("The process with pid=%d has already terminated.", pid);
961N/A // process already dead
961N/A processDead = TRUE;
961N/A }
961N/A else
961N/A {
961N/A if (!TerminateProcess(procHandle, 0))
826N/A {
1298N/A debugError("Failed to terminate process (pid=%d) lastError=%d.", pid, GetLastError());
961N/A // failed to terminate the process
961N/A processDead = FALSE;
826N/A }
826N/A else
826N/A {
961N/A DWORD exitCode;
961N/A int nTries = 20;
1298N/A
1298N/A debug("Successfully began termination process for (pid=%d).", pid);
1298N/A // wait for the process to end.
961N/A
961N/A processDead = FALSE;
961N/A while ((nTries > 0) && !processDead)
961N/A {
961N/A GetExitCodeProcess(procHandle, &exitCode);
961N/A if (exitCode == STILL_ACTIVE)
961N/A {
961N/A // process is still alive, let's wait 1 sec and loop again
1298N/A nTries--;
1298N/A debug("Process (pid=%d) has not yet exited. Sleeping for 1 second and will try %d more time(s).", pid, nTries);
961N/A Sleep(1000);
961N/A }
961N/A else
961N/A {
1298N/A debug("Process (pid=%d) has exited with exit code %d.", pid, exitCode);
961N/A processDead = TRUE;
961N/A }
961N/A }
826N/A }
961N/A CloseHandle(procHandle);
961N/A }
1298N/A
1298N/A debug("killProcess(pid=%d) returning %d", pid, processDead);
961N/A return processDead;
826N/A} // killProcess
826N/A
826N/A// ----------------------------------------------------
826N/A// Creates the pid file for a given instance directory.
826N/A// and a given pid.
826N/A// If the file could be created returns TRUE and FALSE
826N/A// otherwise.
826N/A// ----------------------------------------------------
826N/ABOOL createPidFile(const char* instanceDir, int pid)
1298N/A{
961N/A BOOL returnValue = FALSE;
961N/A char pidFile[PATH_SIZE];
961N/A FILE *f;
961N/A
1298N/A debug("createPidFile(instanceDir='%s',pid=%d)", instanceDir, pid);
1298N/A
961N/A if (getPidFile(instanceDir, pidFile, PATH_SIZE))
961N/A {
961N/A if ((f = fopen(pidFile, "w")) != NULL)
826N/A {
961N/A fprintf(f, "%d", pid);
961N/A fclose (f);
961N/A returnValue = TRUE;
1298N/A debug("Successfully put pid=%d in the pid file '%s'.", pid, pidFile);
826N/A }
826N/A else
826N/A {
1298N/A debugError("Couldn't create the pid file '%s' because the file could not be opened.", pidFile);
961N/A returnValue = FALSE;
826N/A }
961N/A }
961N/A else
961N/A {
1298N/A debugError("Couldn't create the pid file because the pid file name could not be constructed.");
961N/A returnValue = FALSE;
961N/A }
961N/A
961N/A return returnValue;
826N/A} // createPidFile
829N/A
826N/A
826N/A// ----------------------------------------------------
826N/A// Elaborate the command line: "cmd arg1 arg2..."
826N/A// If an arg contains white space(s) then add " " to protect them
826N/A// but don't do it for option (an option starts with -).
826N/A// Returns TRUE if the command name could be initiated and
826N/A// FALSE otherwise (buffer overflow because the resulting
826N/A// string is bigger than maxSize).
826N/A// ----------------------------------------------------
826N/ABOOL getCommandLine(const char* argv[], char* command, unsigned int maxSize)
826N/A{
961N/A int curCmdInd = 0;
961N/A int i = 0;
961N/A BOOL overflow = FALSE;
1298N/A
1298N/A debug("Constructing full command line from arguments:");
1298N/A for (i = 0; (argv[i] != NULL); i++)
1298N/A {
1298N/A debug(" argv[%d]: %s", i, argv[i]);
1298N/A }
961N/A
1298N/A i = 0;
961N/A while ((argv[i] != NULL) && !overflow)
961N/A {
961N/A const char* curarg = argv[i++];
961N/A if (i > 1)
961N/A {
961N/A if (curCmdInd + strlen(" ") < maxSize)
961N/A {
961N/A sprintf (&command[curCmdInd], " ");
961N/A curCmdInd = strlen(command);
961N/A }
961N/A else
961N/A {
961N/A overflow = TRUE;
961N/A }
961N/A }
961N/A
961N/A if (curarg[0] != '\0')
961N/A {
961N/A int argInd = 0;
961N/A
961N/A if (curarg[0] == '"')
961N/A {
961N/A // there is a quote: no need to add extra quotes
961N/A }
961N/A else
826N/A {
961N/A while (curarg[argInd] != ' '
961N/A && curarg[argInd] != '\0'
961N/A && curarg[argInd] != '\n')
961N/A {
961N/A argInd++;
961N/A }
961N/A }
961N/A if (curarg[0] != '"' && curarg[argInd] == ' ')
961N/A {
961N/A if (curCmdInd + strlen("\"\"") + strlen(curarg) < maxSize)
961N/A {
961N/A // no begining quote and white space inside => add quotes
961N/A sprintf (&command[curCmdInd], "\"%s\"", curarg);
961N/A curCmdInd = strlen (command);
961N/A }
961N/A else
961N/A {
961N/A overflow = TRUE;
961N/A }
961N/A }
961N/A else
961N/A {
961N/A if (curCmdInd + strlen(curarg) < maxSize)
961N/A {
961N/A // no white space or quotes detected, keep the arg as is
961N/A sprintf (&command[curCmdInd], "%s", curarg);
961N/A curCmdInd = strlen (command);
961N/A }
961N/A else
961N/A {
961N/A overflow = TRUE;
961N/A }
826N/A }
826N/A
961N/A } else {
961N/A if (curCmdInd + strlen("\"\"") < maxSize)
826N/A {
961N/A sprintf (&command[curCmdInd], "\"\"");
961N/A curCmdInd = strlen (command);
826N/A }
961N/A else
961N/A {
961N/A overflow = TRUE;
961N/A }
961N/A }
961N/A }
1298N/A
1298N/A if (overflow)
1298N/A {
1298N/A debugError("Failed to construct the full commandline because the buffer wasn't big enough.");
1298N/A }
1298N/A else
1298N/A {
1298N/A debug("The full commandline is '%s'.", command);
1298N/A }
961N/A
961N/A return !overflow;
826N/A} // getCommandLine
826N/A
826N/A// ----------------------------------------------------
826N/A// Function called when we want to start the server.
826N/A// This function expects the following parameter to be passed:
826N/A// the directory of the server we want to start and the
826N/A// command line (and its argumets) that we want to execute (basically the java
826N/A// command that we want to start the server). The main reasons
826N/A// to have the command line passed are:
826N/A// 1. Keep the native code as minimal as possible.
826N/A// 2. Allow the administrator some flexibility in the way the
826N/A// server is started by leaving most of the logic in the command-line.
826N/A//
826N/A// This approach makes things to be closer between what is proposed
826N/A// in windows and in UNIX systems.
826N/A//
826N/A// If the instance could be started the code will write the pid of the process
826N/A// of the server in file that can be used for instance to stop the server
826N/A// (see stop.c).
826N/A//
826N/A// Returns the pid of the process of the instance if it could be started and -1
826N/A// otherwise.
826N/A// ----------------------------------------------------
826N/Aint start(const char* instanceDir, char* argv[])
826N/A{
961N/A int returnValue;
961N/A int childPid;
961N/A
961N/A char command[COMMAND_SIZE];
1298N/A
961N/A if (getCommandLine(argv, command, COMMAND_SIZE))
961N/A {
961N/A childPid = spawn(command, TRUE);
826N/A
961N/A if (childPid > 0)
826N/A {
961N/A createPidFile(instanceDir, childPid);
961N/A returnValue = childPid;
826N/A }
826N/A else
826N/A {
1298N/A debugError("Couldn't start the child process because the spawn failed.");
961N/A returnValue = -1;
826N/A }
961N/A }
961N/A else
961N/A {
1298N/A debugError("Couldn't start the child process because the full command line could not be constructed.");
961N/A returnValue = -1;
961N/A }
961N/A
961N/A return returnValue;
826N/A} // start
826N/A
826N/A
826N/A// ----------------------------------------------------
826N/A// Function called when we want to stop the server.
826N/A// This code is called by the stop-ds.bat batch file to stop the server
826N/A// in windows.
826N/A// This function expects just one parameter to be passed
826N/A// to the executable: the directory of the server we want
826N/A// to stop.
826N/A//
826N/A// If the instance could be stopped the pid file
826N/A// is removed. This is done for security reasons: if we do
826N/A// not delete the pid file and the old pid of the process
826N/A// is used by a new process, when we call again this executable
826N/A// the new process will be killed.
826N/A// Note: even if the code in the class org.opends.server.core.DirectoryServer
826N/A// sets the pid file to be deleted on the exit of the process
826N/A// the file is not always deleted.
826N/A//
826N/A// Returns 0 if the instance could be stopped using the
826N/A// pid stored in a file of the server installation and
826N/A// -1 otherwise.
826N/A// ----------------------------------------------------
826N/Aint stop(const char* instanceDir)
826N/A{
961N/A int returnCode = -1;
961N/A
961N/A int childPid;
961N/A
1298N/A debug("Attempting to stop the server running at root '%s'.", instanceDir);
1298N/A
961N/A childPid = getPid(instanceDir);
961N/A
961N/A if (childPid != 0)
961N/A {
961N/A if (killProcess(childPid))
826N/A {
961N/A returnCode = 0;
961N/A deletePidFile(instanceDir);
826N/A }
961N/A }
1298N/A else
1298N/A {
1298N/A debug("Could not stop the server running at root '%s' because the pid could not be located.", instanceDir);
1298N/A }
1298N/A
961N/A return returnCode;
826N/A} // stop
826N/A
826N/A
826N/A// ----------------------------------------------------
826N/A// Function called when we want to launch simply a process without attaching
826N/A// it to any command prompt (the difference with start is basically that here
826N/A// we create no pid file).
826N/A// This code is called for instance by the statuspanel.bat batch file to launch
826N/A// the status panel on windows.
826N/A
826N/A// The goal of these methods is:
826N/A// Be able to launch batch files with double-click and not having a
826N/A// prompt-window associated with it.
826N/A// Launch batch files from the prompt that generate a java process that does not
826N/A// block the prompt and that keeps running even if the prompt window is closed.
826N/A//
826N/A// This function expects the following parameter to be passed:
826N/A// the directory of the server we want to start and the
826N/A// command line that we want to execute (basically the java
826N/A// command that we want to display the status panel). The main reasons
826N/A// to have the command line passed are:
826N/A// 1. Keep the native code as minimal as possible.
826N/A// 2. Allow the administrator some flexibility in the way the
826N/A// server is started by leaving most of the logic in the command-line.
826N/A//
826N/A// Returns the pid of the process associated with the command if it could be
826N/A// launched and -1 otherwise.
826N/A// ----------------------------------------------------
826N/Aint launch(char* argv[])
826N/A{
961N/A int returnValue;
961N/A
961N/A char command[COMMAND_SIZE];
961N/A
961N/A if (getCommandLine(argv, command, COMMAND_SIZE))
961N/A {
961N/A returnValue = spawn(command, TRUE);
1298N/A if (returnValue <= 0)
1298N/A {
1298N/A debugError("Failed to launch the child process '%s'.", command);
1298N/A }
1298N/A else
1298N/A {
1298N/A debug("Successfully launched the child process '%s'.", command);
1298N/A }
961N/A }
961N/A else
961N/A {
1298N/A debugError("Couldn't launch the child process because the full command line could not be constructed.");
961N/A returnValue = -1;
961N/A }
961N/A
961N/A return returnValue;
826N/A} // launch
826N/A
826N/A// ----------------------------------------------------
826N/A// main function called by the executable. This code is
826N/A// called by the start-ds.bat, stop-ds.bat and statuspanel.bat batch files.
826N/A//
826N/A// The code assumes that the first passed argument is the subcommand to be
826N/A// executed and the second argument the directory of the server. The rest
826N/A// of the arguments are the arguments specific to each subcommand (see the
826N/A// comments for the functions start, stop and launch).
826N/A// ----------------------------------------------------
826N/Aint main(int argc, char* argv[])
826N/A{
961N/A int returnCode;
1298N/A char* subcommand = NULL;
1298N/A char* instanceDir = NULL;
1298N/A int i;
961N/A
1298N/A debug("main called.");
1298N/A for (i = 0; i < argc; i++) {
1298N/A debug(" argv[%d] = '%s'", i, argv[i]);
1298N/A }
1298N/A
1298N/A if (argc < 3) {
1298N/A char * msg = "Expected command line args of [subcommand] [server directory], but got %d arguments.\n";
1298N/A debugError(msg, argc - 1);
1298N/A fprintf(stderr, msg, argc - 1);
1298N/A return -1;
1298N/A }
1298N/A
1298N/A subcommand = argv[1];
1298N/A instanceDir = argv[2];
1298N/A
961N/A argv += 3;
961N/A
961N/A if (strcmp(subcommand, "start") == 0)
961N/A {
961N/A returnCode = start(instanceDir, argv);
961N/A }
961N/A else if (strcmp(subcommand, "stop") == 0)
961N/A {
961N/A returnCode = stop(instanceDir);
961N/A }
961N/A else if (strcmp(subcommand, "launch") == 0)
961N/A {
961N/A returnCode = launch(argv);
961N/A }
961N/A else
961N/A {
1298N/A char * msg = "Unknown subcommand: [%s]\n";
1300N/A debugError(msg, subcommand);
1298N/A fprintf(stderr, msg, subcommand);
961N/A returnCode = -1;
961N/A }
1298N/A debug("main finished. Returning %d", returnCode);
961N/A return returnCode;
826N/A}
961N/A