001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS.
026 */
027package org.opends.server.tools;
028
029import static com.forgerock.opendj.cli.Utils.*;
030import static com.forgerock.opendj.util.OperatingSystem.*;
031
032import static org.opends.messages.ToolMessages.*;
033
034import java.io.BufferedReader;
035import java.io.File;
036import java.io.IOException;
037import java.io.InputStreamReader;
038import java.io.OutputStream;
039import java.io.PrintStream;
040
041import org.forgerock.i18n.LocalizableMessage;
042import org.opends.guitools.controlpanel.util.Utilities;
043import org.opends.quicksetup.util.Utils;
044import org.opends.server.core.DirectoryServer;
045import org.opends.server.loggers.JDKLogging;
046import org.opends.server.types.NullOutputStream;
047import org.opends.server.util.DynamicConstants;
048import org.opends.server.util.SetupUtils;
049
050import com.forgerock.opendj.cli.ArgumentException;
051import com.forgerock.opendj.cli.ArgumentParser;
052import com.forgerock.opendj.cli.BooleanArgument;
053import com.forgerock.opendj.cli.CommonArguments;
054import com.forgerock.opendj.cli.StringArgument;
055
056/**
057  * This class is used to configure the Windows service for this instance on
058  * this machine.
059  * This tool allows to enable and disable OpenDJ to run as a Windows service
060  * and allows to know if OpenDJ is running as a Windows service or not.
061  *
062  * Some comments about Vista:
063  * In Vista, when we launch the subcommands that require administrator
064  * privileges (enable, disable and cleanup) we cannot use the administrator
065  * launcher binary directly from Java (see
066  * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6410605) so we use
067  * winlauncher.exe.
068  * When we launch subcommands that required administrator privileges
069  * we must launch a binary containing the manifest that specifies that we
070  * require administrator privileges (requireAdministrator value): if UAC is
071  * enabled, the user will be asked for confirmation.
072  * To minimize the number of confirmation that the user must provide when
073  * launching the state subcommand we will use a binary whose manifest does
074  * not contain the requireAdministrator value.
075  *
076  * See the files under src/build-tools/windows for more details.
077  */
078public class ConfigureWindowsService
079{
080  /** The fully-qualified name of this class. */
081  private static final String CLASS_NAME = "org.opends.server.tools.ConfigureWindowsService";
082
083  private static final String DEBUG_OPTION = "--debug";
084  /** Option to be used when calling the launchers. */
085  public static final String LAUNCHER_OPTION = "run";
086
087  private static final int SUCCESS = 0;
088  private static final int ERROR = 1;
089
090  /** Return codes for the method enableService. */
091  /** The service was successfully enabled. */
092  public static final int SERVICE_ENABLE_SUCCESS = 0;
093  /** The service was already enabled. */
094  public static final int SERVICE_ALREADY_ENABLED = 1;
095  /** The service name was already in use. */
096  public static final int SERVICE_NAME_ALREADY_IN_USE = 2;
097  /** An error occurred enabling the service. */
098  public static final int SERVICE_ENABLE_ERROR = 3;
099
100  /** Return codes for the method disableService. */
101  /** The service was successfully disabled. */
102  public static final int SERVICE_DISABLE_SUCCESS = 0;
103  /** The service was already disabled. */
104  public static final int SERVICE_ALREADY_DISABLED = 1;
105  /** The service is marked for deletion. */
106  public static final int SERVICE_MARKED_FOR_DELETION = 2;
107  /** An error occurred disabling the service. */
108  public static final int SERVICE_DISABLE_ERROR = 3;
109
110  /** Return codes for the method serviceState. */
111  /** The service is enabled. */
112  public static final int SERVICE_STATE_ENABLED = 0;
113  /** The service is disabled. */
114  public static final int SERVICE_STATE_DISABLED = 1;
115  /** An error occurred checking the service state. */
116  public static final int SERVICE_STATE_ERROR = 2;
117
118  /** Return codes for the method cleanupService. */
119  /** The service cleanup worked. */
120  public static final int SERVICE_CLEANUP_SUCCESS = 0;
121  /** The service could not be found. */
122  public static final int SERVICE_NOT_FOUND = 1;
123  /** An error occurred cleaning up the service. */
124  public static final int SERVICE_CLEANUP_ERROR = 2;
125  /** The service is marked for deletion. */
126  public static final int SERVICE_CLEANUP_MARKED_FOR_DELETION = 3;
127
128  /**
129   * Configures the Windows service for this instance on this machine. This tool
130   * allows to enable and disable OpenDJ to run as a Windows service and allows
131   * to know if OpenDJ is running as a Windows service or not.
132   *
133   * @param args
134   *          The command-line arguments provided to this program.
135   */
136  public static void main(String[] args)
137  {
138    int result = configureWindowsService(args, System.out, System.err);
139
140    System.exit(filterExitCode(result));
141  }
142
143  /**
144   * Configures the Windows service for this instance on this machine. This tool
145   * allows to enable and disable OpenDJ to run as a Windows service and allows
146   * to know if OpenDJ is running as a Windows service or not.
147   *
148   * @param args
149   *          The command-line arguments provided to this program.
150   * @param outStream
151   *          the stream used to write the standard output.
152   * @param errStream
153   *          the stream used to write the error output.
154   * @return the integer code describing if the operation could be completed or
155   *         not.
156   */
157  public static int configureWindowsService(String[] args, OutputStream outStream, OutputStream errStream)
158  {
159    PrintStream out = NullOutputStream.wrapOrNullStream(outStream);
160    PrintStream err = NullOutputStream.wrapOrNullStream(errStream);
161    JDKLogging.disableLogging();
162
163    //  Define all the arguments that may be used with this program.
164    LocalizableMessage toolDescription = INFO_CONFIGURE_WINDOWS_SERVICE_TOOL_DESCRIPTION.get();
165    ArgumentParser argParser = new ArgumentParser(CLASS_NAME, toolDescription, false);
166    argParser.setShortToolDescription(REF_SHORT_DESC_WINDOWS_SERVICE.get());
167    BooleanArgument enableService = null;
168    BooleanArgument disableService = null;
169    BooleanArgument serviceState = null;
170    StringArgument cleanupService = null;
171    BooleanArgument showUsage = null;
172
173    try
174    {
175      enableService = new BooleanArgument("enableservice", 'e', "enableService",
176          INFO_CONFIGURE_WINDOWS_SERVICE_DESCRIPTION_ENABLE.get());
177      argParser.addArgument(enableService);
178
179      disableService = new BooleanArgument("disableservice", 'd', "disableService",
180          INFO_CONFIGURE_WINDOWS_SERVICE_DESCRIPTION_DISABLE.get());
181      argParser.addArgument(disableService);
182
183      serviceState = new BooleanArgument("servicestate", 's', "serviceState",
184          INFO_CONFIGURE_WINDOWS_SERVICE_DESCRIPTION_STATE.get());
185      argParser.addArgument(serviceState);
186
187      cleanupService = new StringArgument("cleanupservice", 'c', "cleanupService", false, false, true,
188          INFO_SERVICE_NAME_PLACEHOLDER.get(), null, null,
189          INFO_CONFIGURE_WINDOWS_SERVICE_DESCRIPTION_CLEANUP.get());
190      argParser.addArgument(cleanupService);
191
192      showUsage = CommonArguments.getShowUsage();
193      argParser.addArgument(showUsage);
194      argParser.setUsageArgument(showUsage, out);
195    }
196    catch (ArgumentException ae)
197    {
198      printWrappedText(err, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
199      return ERROR;
200    }
201
202    try
203    {
204      argParser.parseArguments(args);
205    }
206    catch (ArgumentException ae)
207    {
208      argParser.displayMessageAndUsageReference(err, ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
209      return ERROR;
210    }
211
212    // If we should just display usage or version information,
213    // then it is already done
214    if (!argParser.usageOrVersionDisplayed())
215    {
216      /* Check that the user only asked for one argument */
217      int nArgs = 0;
218      if (enableService.isPresent())
219      {
220        nArgs++;
221      }
222      if (disableService.isPresent())
223      {
224        nArgs++;
225      }
226      if (serviceState.isPresent())
227      {
228        nArgs++;
229      }
230      if (cleanupService.isPresent())
231      {
232        nArgs++;
233      }
234      if (nArgs != 1)
235      {
236        argParser.displayMessageAndUsageReference(err, nArgs == 0 ? ERR_CONFIGURE_WINDOWS_SERVICE_TOO_FEW_ARGS.get()
237                                                                  : ERR_CONFIGURE_WINDOWS_SERVICE_TOO_MANY_ARGS.get());
238        return ERROR;
239      }
240    }
241
242    if (argParser.usageOrVersionDisplayed())
243    {
244      return SUCCESS;
245    }
246
247    if (enableService.isPresent())
248    {
249      return enableService(out, err);
250    }
251    else if (disableService.isPresent())
252    {
253      return disableService(out, err);
254    }
255    else if (serviceState.isPresent())
256    {
257      return serviceState(out, err);
258    }
259
260    return cleanupService(cleanupService.getValue(), out, err);
261  }
262
263  /**
264   * Returns the service name associated with OpenDJ or null if no service name
265   * could be found.
266   *
267   * @return the service name associated with OpenDJ or null if no service name
268   *         could be found.
269   */
270  static String getServiceName()
271  {
272    String serverRoot = getServerRoot();
273    String[] cmd = { getBinaryFullPath(), "state", serverRoot };
274    try
275    {
276      String serviceName = null;
277      Process p = Runtime.getRuntime().exec(cmd);
278      BufferedReader stdout = new BufferedReader(new InputStreamReader(p.getInputStream()));
279      boolean processDone = false;
280      String s;
281      while (!processDone)
282      {
283        try
284        {
285          p.exitValue();
286          processDone = true;
287        }
288        catch (Throwable t)
289        {
290        }
291        while ((s = stdout.readLine()) != null)
292        {
293          serviceName = s;
294          if (serviceName.trim().length() == 0)
295          {
296            serviceName = null;
297          }
298        }
299      }
300      return serviceName;
301    }
302    catch (Throwable t)
303    {
304      return null;
305    }
306  }
307
308  /**
309   * Enables OpenDJ to run as a windows service.
310   *
311   * @param out
312   *          the stream used to write the standard output.
313   * @param err
314   *          the stream used to write the error output.
315   * @return <CODE>SERVICE_ENABLE_SUCCESS</CODE>,
316   *         <CODE>SERVICE_ENABLE_ERROR</CODE>,
317   *         <CODE>SERVICE_NAME_ALREADY_IN_USE</CODE> or
318   *         <CODE>SERVICE_ALREADY_ENABLED</CODE> depending on whether the
319   *         service could be enabled or not.
320   */
321  public static int enableService(PrintStream out, PrintStream err)
322  {
323    LocalizableMessage serviceName = Utils.getCustomizedObject(
324        "INFO_WINDOWS_SERVICE_NAME",
325        INFO_WINDOWS_SERVICE_NAME.get(DynamicConstants.PRODUCT_NAME),
326        LocalizableMessage.class);
327    LocalizableMessage serviceDescription = Utils.getCustomizedObject(
328        "INFO_WINDOWS_SERVICE_DESCRIPTION",
329        INFO_WINDOWS_SERVICE_DESCRIPTION.get(getServerRoot()), LocalizableMessage.class);
330    return enableService(out, err, serviceName.toString(), serviceDescription.toString());
331  }
332
333  /**
334   * Enables OpenDJ to run as a windows service.
335   *
336   * @param out
337   *          the stream used to write the standard output.
338   * @param err
339   *          the stream used to write the error output.
340   * @param serviceName
341   *          the name of the service as it will appear in the registry.
342   * @param serviceDescription
343   *          the description of the service as it will appear in the registry.
344   * @return <CODE>SERVICE_ENABLE_SUCCESS</CODE>,
345   *         <CODE>SERVICE_ENABLE_ERROR</CODE>,
346   *         <CODE>SERVICE_NAME_ALREADY_IN_USE</CODE> or
347   *         <CODE>SERVICE_ALREADY_ENABLED</CODE> depending on whether the
348   *         service could be enabled or not.
349   */
350  public static int enableService(PrintStream out, PrintStream err, String serviceName, String serviceDescription)
351  {
352    String serverRoot = getServerRoot();
353    String[] cmd;
354
355    if (hasUAC())
356    {
357      cmd = new String[] {
358          getLauncherBinaryFullPath(),
359          LAUNCHER_OPTION,
360          getLauncherAdministratorBinaryFullPath(),
361          LAUNCHER_OPTION,
362          getBinaryFullPath(),
363          "create",
364          serverRoot,
365          serviceName,
366          serviceDescription,
367          DEBUG_OPTION
368      };
369    }
370    else
371    {
372      cmd = new String[] {
373          getBinaryFullPath(),
374          "create",
375          serverRoot,
376          serviceName,
377          serviceDescription,
378          DEBUG_OPTION
379      };
380    }
381
382    try
383    {
384      boolean isServerRunning = Utilities.isServerRunning(new File(serverRoot));
385
386      int resultCode = Runtime.getRuntime().exec(cmd).waitFor();
387      switch (resultCode)
388      {
389      case 0:
390        if (isServerRunning)
391        {
392          // We have to launch the windows service.  The service code already
393          // handles this case (the service binary is executed when the server
394          // already runs).
395          final int returnValue = StartWindowsService.startWindowsService(out, err);
396          if (returnValue == 0)
397          {
398            printWrappedText(out, INFO_WINDOWS_SERVICE_SUCCESSULLY_ENABLED.get());
399            return SERVICE_ENABLE_SUCCESS;
400          }
401          else
402          {
403            printWrappedText(err, ERR_WINDOWS_SERVICE_ENABLING_ERROR_STARTING_SERVER.get(SERVICE_ENABLE_ERROR));
404            return SERVICE_ENABLE_ERROR;
405          }
406        }
407        else
408        {
409          printWrappedText(out, INFO_WINDOWS_SERVICE_SUCCESSULLY_ENABLED.get());
410          return SERVICE_ENABLE_SUCCESS;
411        }
412      case 1:
413        printWrappedText(out, INFO_WINDOWS_SERVICE_ALREADY_ENABLED.get());
414        return SERVICE_ALREADY_ENABLED;
415      case 2:
416        printWrappedText(err, ERR_WINDOWS_SERVICE_NAME_ALREADY_IN_USE.get());
417        return SERVICE_NAME_ALREADY_IN_USE;
418      case 3:
419        printWrappedText(err, ERR_WINDOWS_SERVICE_ENABLE_ERROR.get());
420        return SERVICE_ENABLE_ERROR;
421      default:
422        printWrappedText(err, ERR_WINDOWS_SERVICE_ENABLE_ERROR.get());
423        return SERVICE_ENABLE_ERROR;
424      }
425    }
426    catch (Throwable t)
427    {
428      err.println("Unexpected throwable: "+t);
429      t.printStackTrace();
430      printWrappedText(err, ERR_WINDOWS_SERVICE_ENABLE_ERROR.get());
431      return SERVICE_ENABLE_ERROR;
432    }
433  }
434
435  /**
436   * Disables OpenDJ to run as a windows service.
437   *
438   * @param out
439   *          the stream used to write the standard output.
440   * @param err
441   *          the stream used to write the error output.
442   * @return <CODE>SERVICE_DISABLE_SUCCESS</CODE>,
443   *         <CODE>SERVICE_DISABLE_ERROR</CODE>,
444   *         <CODE>SERVICE_MARKED_FOR_DELETION</CODE> or
445   *         <CODE>SERVICE_ALREADY_DISABLED</CODE> depending on whether the
446   *         service could be disabled or not.
447   */
448  public static int disableService(PrintStream out, PrintStream err)
449  {
450    String serverRoot = getServerRoot();
451    String[] cmd;
452    if (hasUAC())
453    {
454      cmd = new String[] {
455          getLauncherBinaryFullPath(),
456          LAUNCHER_OPTION,
457          getLauncherAdministratorBinaryFullPath(),
458          LAUNCHER_OPTION,
459          getBinaryFullPath(),
460          "remove",
461          serverRoot,
462          DEBUG_OPTION
463      };
464    }
465    else
466    {
467      cmd = new String[] {
468        getBinaryFullPath(),
469        "remove",
470        serverRoot,
471        DEBUG_OPTION
472        };
473    }
474    try
475    {
476      int resultCode = Runtime.getRuntime().exec(cmd).waitFor();
477      switch (resultCode)
478      {
479      case 0:
480        printWrappedText(out, INFO_WINDOWS_SERVICE_SUCCESSULLY_DISABLED.get());
481        return SERVICE_DISABLE_SUCCESS;
482      case 1:
483        printWrappedText(out, INFO_WINDOWS_SERVICE_ALREADY_DISABLED.get());
484        return SERVICE_ALREADY_DISABLED;
485      case 2:
486        printWrappedText(out, WARN_WINDOWS_SERVICE_MARKED_FOR_DELETION.get());
487        return SERVICE_MARKED_FOR_DELETION;
488      case 3:
489        printWrappedText(err, ERR_WINDOWS_SERVICE_DISABLE_ERROR.get());
490        return SERVICE_DISABLE_ERROR;
491      default:
492        printWrappedText(err, ERR_WINDOWS_SERVICE_DISABLE_ERROR.get());
493        return SERVICE_DISABLE_ERROR;
494      }
495    }
496    catch (Throwable t)
497    {
498      t.printStackTrace();
499      printWrappedText(err, ERR_WINDOWS_SERVICE_DISABLE_ERROR.get());
500      return SERVICE_DISABLE_ERROR;
501    }
502  }
503
504  /**
505   * Cleans up a service for a given service name.
506   *
507   * @param serviceName
508   *          the service name to be cleaned up.
509   * @param out
510   *          the stream used to write the standard output.
511   * @param err
512   *          the stream used to write the error output.
513   * @return <CODE>SERVICE_CLEANUP_SUCCESS</CODE>,
514   *         <CODE>SERVICE_NOT_FOUND</CODE>,
515   *         <CODE>SERVICE_MARKED_FOR_DELETION</CODE> or
516   *         <CODE>SERVICE_CLEANUP_ERROR</CODE> depending on whether the service
517   *         could be found or not.
518   */
519  public static int cleanupService(String serviceName, PrintStream out,
520      PrintStream err)
521  {
522    String[] cmd;
523    if (hasUAC())
524    {
525      cmd = new String[] {
526          getLauncherBinaryFullPath(),
527          LAUNCHER_OPTION,
528          getLauncherAdministratorBinaryFullPath(),
529          LAUNCHER_OPTION,
530          getBinaryFullPath(),
531          "cleanup",
532          serviceName,
533          DEBUG_OPTION
534      };
535    }
536    else
537    {
538      cmd = new String[] {
539          getBinaryFullPath(),
540          "cleanup",
541          serviceName,
542          DEBUG_OPTION
543      };
544    }
545    try
546    {
547      int resultCode = Runtime.getRuntime().exec(cmd).waitFor();
548      switch (resultCode)
549      {
550      case 0:
551        printWrappedText(out, INFO_WINDOWS_SERVICE_CLEANUP_SUCCESS.get(serviceName));
552        return SERVICE_CLEANUP_SUCCESS;
553      case 1:
554        printWrappedText(err, ERR_WINDOWS_SERVICE_CLEANUP_NOT_FOUND.get(serviceName));
555        return SERVICE_NOT_FOUND;
556      case 2:
557        printWrappedText(out, WARN_WINDOWS_SERVICE_CLEANUP_MARKED_FOR_DELETION.get(serviceName));
558        return SERVICE_CLEANUP_MARKED_FOR_DELETION;
559      case 3:
560        printWrappedText(err, ERR_WINDOWS_SERVICE_CLEANUP_ERROR.get(serviceName));
561        return SERVICE_CLEANUP_ERROR;
562      default:
563        printWrappedText(err, ERR_WINDOWS_SERVICE_CLEANUP_ERROR.get(serviceName));
564        return SERVICE_CLEANUP_ERROR;
565      }
566    }
567    catch (Throwable t)
568    {
569      err.println(ERR_WINDOWS_SERVICE_CLEANUP_ERROR.get(serviceName));
570      printWrappedText(err, "Exception:" + t);
571      return SERVICE_CLEANUP_ERROR;
572    }
573  }
574
575  /**
576   * Checks if OpenDJ is enabled as a windows service.
577   *
578   * @return <CODE>SERVICE_STATE_ENABLED</CODE>,
579   *         <CODE>SERVICE_STATE_DISABLED</CODE> or
580   *         <CODE>SERVICE_STATE_ERROR</CODE> depending on the state of the
581   *         service.
582   */
583  public static int serviceState()
584  {
585    return serviceState(NullOutputStream.printStream(), NullOutputStream.printStream());
586  }
587
588  /**
589   * Checks if OpenDJ is enabled as a windows service and if it is write the
590   * serviceName in the output stream (if it is not null).
591   *
592   * @param out
593   *          the stream used to write the standard output.
594   * @param err
595   *          the stream used to write the error output.
596   * @return <CODE>SERVICE_STATE_ENABLED</CODE>,
597   *         <CODE>SERVICE_STATE_DISABLED</CODE> or
598   *         <CODE>SERVICE_STATE_ERROR</CODE> depending on the state of the
599   *         service.
600   */
601  public static int serviceState(PrintStream out, PrintStream err)
602  {
603    String serviceName = null;
604
605    String serverRoot = getServerRoot();
606    String[] cmd = new String[] {
607        getBinaryFullPath(),
608        "state",
609        serverRoot,
610        DEBUG_OPTION
611    };
612
613    try
614    {
615      int resultCode = -1;
616      Process process = new ProcessBuilder(cmd).start();
617      BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream()));
618
619      boolean processDone = false;
620      String s;
621      while (!processDone)
622      {
623        try
624        {
625          resultCode = process.exitValue();
626          processDone = true;
627        }
628        catch (Throwable t)
629        {
630        }
631        while ((s = stdout.readLine()) != null)
632        {
633          if (s.trim().length() != 0)
634          {
635            serviceName = s;
636          }
637        }
638      }
639      switch (resultCode)
640      {
641      case 0:
642        printWrappedText(out, INFO_WINDOWS_SERVICE_ENABLED.get(serviceName));
643        return SERVICE_STATE_ENABLED;
644      case 1:
645        printWrappedText(out, INFO_WINDOWS_SERVICE_DISABLED.get());
646        return SERVICE_STATE_DISABLED;
647      case 2:
648        printWrappedText(err, ERR_WINDOWS_SERVICE_STATE_ERROR.get());
649        return SERVICE_STATE_ERROR;
650      default:
651        printWrappedText(err, ERR_WINDOWS_SERVICE_STATE_ERROR.get());
652        return SERVICE_STATE_ERROR;
653      }
654    }
655    catch (Throwable t)
656    {
657      printWrappedText(err, ERR_WINDOWS_SERVICE_STATE_ERROR.get());
658      printWrappedText(err, t.toString());
659      return SERVICE_STATE_ERROR;
660    }
661  }
662
663  /**
664   * Returns the Directory Server installation path in a user friendly
665   * representation.
666   *
667   * @return the Directory Server installation path in a user friendly
668   *         representation.
669   */
670  private static String getServerRoot()
671  {
672    String serverRoot = DirectoryServer.getServerRoot();
673    File f = new File(serverRoot);
674    try
675    {
676      /*
677       * Do a best effort to avoid having a relative representation (for
678       * instance to avoid having ../../../).
679       */
680      f = f.getCanonicalFile();
681    }
682    catch (IOException ioe)
683    {
684      /* This is a best effort to get the best possible representation of the
685       * file: reporting the error is not necessary.
686       */
687    }
688    serverRoot = f.toString();
689    if (serverRoot.endsWith(File.separator))
690    {
691      serverRoot = serverRoot.substring(0, serverRoot.length() - 1);
692    }
693    return serverRoot;
694  }
695
696  /**
697   * Returns the full path of the executable used by this class to perform
698   * operations related to the service. This binaries file has the asInvoker
699   * value in its manifest.
700   *
701   * @return the full path of the executable used by this class to perform
702   *         operations related to the service.
703   */
704  private static String getBinaryFullPath()
705  {
706    return SetupUtils.getScriptPath(getServerRoot() + "\\lib\\opendj_service.exe");
707  }
708
709  /**
710   * Returns the full path of the executable that has a manifest requiring
711   * administrator privileges used by this class to perform operations related
712   * to the service.
713   *
714   * @return the full path of the executable that has a manifest requiring
715   *         administrator privileges used by this class to perform operations
716   *         related to the service.
717   */
718  public static String getLauncherAdministratorBinaryFullPath()
719  {
720    return getServerRoot() + "\\lib\\launcher_administrator.exe";
721  }
722
723  /**
724   * Returns the full path of the executable that has a manifest requiring
725   * administrator privileges used by this class to perform operations related
726   * to the service.
727   *
728   * @return the full path of the executable that has a manifest requiring
729   *         administrator privileges used by this class to perform operations
730   *         related to the service.
731   */
732  public static String getLauncherBinaryFullPath()
733  {
734    return getServerRoot() + "\\lib\\winlauncher.exe";
735  }
736}