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
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.tools;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.io.IOException;
033    import java.io.OutputStream;
034    import java.io.PrintStream;
035    import java.text.SimpleDateFormat;
036    import java.util.ArrayList;
037    import java.util.Date;
038    import java.util.LinkedList;
039    import java.util.TimeZone;
040    import java.util.UUID;
041    import java.util.concurrent.atomic.AtomicInteger;
042    
043    import org.opends.server.controls.ProxiedAuthV2Control;
044    import org.opends.server.core.DirectoryServer;
045    import org.opends.server.core.LockFileManager;
046    import org.opends.server.protocols.asn1.ASN1Exception;
047    import org.opends.server.protocols.asn1.ASN1OctetString;
048    import org.opends.server.protocols.ldap.AddRequestProtocolOp;
049    import org.opends.server.protocols.ldap.AddResponseProtocolOp;
050    import org.opends.server.protocols.ldap.ExtendedResponseProtocolOp;
051    import org.opends.server.protocols.ldap.LDAPAttribute;
052    import org.opends.server.protocols.ldap.LDAPConstants;
053    import org.opends.server.protocols.ldap.LDAPControl;
054    import org.opends.server.protocols.ldap.LDAPMessage;
055    import org.opends.server.protocols.ldap.LDAPResultCode;
056    import org.opends.server.tasks.ShutdownTask;
057    import org.opends.server.tools.tasks.TaskTool;
058    import org.opends.server.types.Control;
059    import org.opends.server.types.LDAPException;
060    import org.opends.server.types.NullOutputStream;
061    import org.opends.server.types.RawAttribute;
062    import org.opends.server.util.args.Argument;
063    import org.opends.server.util.args.ArgumentException;
064    import org.opends.server.util.args.ArgumentParser;
065    import org.opends.server.util.args.BooleanArgument;
066    import org.opends.server.util.args.FileBasedArgument;
067    import org.opends.server.util.args.IntegerArgument;
068    import org.opends.server.util.args.LDAPConnectionArgumentParser;
069    import org.opends.server.util.args.StringArgument;
070    
071    import static org.opends.server.config.ConfigConstants.*;
072    import static org.opends.messages.ToolMessages.*;
073    import static org.opends.server.util.ServerConstants.*;
074    import static org.opends.server.util.StaticUtils.*;
075    import static org.opends.server.tools.ToolConstants.*;
076    
077    
078    
079    /**
080     * This class provides a tool that can send a request to the Directory Server
081     * that will cause it to shut down.
082     */
083    public class StopDS
084    {
085      /**
086       * The fully-qualified name of this class.
087       */
088      private static final String CLASS_NAME = "org.opends.server.tools.StopDS";
089    
090      /**
091       * Return codes used when the hidden option --checkStoppability is used.
092       * NOTE: when checkStoppability is specified is recommended not to allocate
093       * a lot of memory for the JVM (Using -Xms and -Xmx options) as there might
094       * be calls to Runtime.exec.
095       */
096      /**
097       * The server is already stopped.
098       */
099      private static int SERVER_ALREADY_STOPPED = 98;
100      /**
101       * The server must be started.
102       */
103      private static int START_SERVER = 99;
104      /**
105       * The server must be stopped using a system call.
106       */
107      private static int STOP_USING_SYSTEM_CALL = 100;
108      /**
109       * The server must be restarted using system calls.
110       */
111      private static int RESTART_USING_SYSTEM_CALL = 101;
112      /**
113       * The server must be stopped using protocol.
114       */
115      private static int STOP_USING_PROTOCOL = 102;
116      /**
117       * The server must be stopped as a window service.
118       */
119      private static int STOP_AS_WINDOW_SERVICE = 103;
120      /**
121       * The server must be restarted as a window service.
122       */
123      private static int RESTART_AS_WINDOW_SERVICE = 104;
124      /**
125       * The server must be started and it should use quiet mode.
126       */
127      private static int START_SERVER_QUIET = 105;
128      /**
129       * The server must be restarted using system calls and it should use quiet
130       * mode.
131       */
132      private static int RESTART_USING_SYSTEM_CALL_QUIET = 106;
133    
134      /**
135       * Invokes the <CODE>stopDS</CODE> method, passing it the provided command
136       * line arguments.  If the call to <CODE>stopDS</CODE> returns a nonzero
137       * value, then that will be used as the exit code for this program.
138       *
139       * @param  args  The command-line arguments provided to this program.
140       */
141      public static void main(String[] args)
142      {
143        int result = stopDS(args, System.out, System.err);
144    
145        if (result != LDAPResultCode.SUCCESS)
146        {
147          System.exit(filterExitCode(result));
148        }
149      }
150    
151    
152    
153      /**
154       * Parses the provided set of command-line arguments and attempts to contact
155       * the Directory Server in order to send it the shutdown request.
156       *
157       * @param  args  The command-line arguments provided to this program.
158       *
159       * @return  An integer value that indicates whether the shutdown request was
160       *          accepted by the Directory Server.  A nonzero value should be
161       *          interpreted as a failure of some kind.
162       */
163      public static int stopDS(String[] args)
164      {
165        return stopDS(args, System.out, System.err);
166      }
167    
168    
169    
170      /**
171       * Parses the provided set of command-line arguments and attempts to contact
172       * the Directory Server in order to send it the shutdown request.
173       *
174       * @param  args       The command-line arguments provided to this program.
175       * @param  outStream  The output stream to use for standard output, or
176       *                    <CODE>null</CODE> if standard output is not needed.
177       * @param  errStream  The output stream to use for standard error, or
178       *                    <CODE>null</CODE> if standard error is not needed.
179       *
180       * @return  An integer value that indicates whether the shutdown request was
181       *          accepted by the Directory Server.  A nonzero value should be
182       *          interpreted as a failure of some kind.
183       */
184      public static int stopDS(String[] args, OutputStream outStream,
185                               OutputStream errStream)
186      {
187        PrintStream out;
188        if (outStream == null)
189        {
190          out = NullOutputStream.printStream();
191        }
192        else
193        {
194          out = new PrintStream(outStream);
195        }
196    
197        PrintStream err;
198        if (errStream == null)
199        {
200          err = NullOutputStream.printStream();
201        }
202        else
203        {
204          err = new PrintStream(errStream);
205        }
206    
207    
208        // Define all the arguments that may be used with this program.
209        Message toolDescription = INFO_STOPDS_TOOL_DESCRIPTION.get();
210        ArgumentParser    argParser = new ArgumentParser(CLASS_NAME,
211                                                         toolDescription, false);
212        BooleanArgument   checkStoppability;
213        BooleanArgument   quietMode;
214        BooleanArgument   windowsNetStop;
215        BooleanArgument   restart;
216        BooleanArgument   showUsage;
217        BooleanArgument   trustAll;
218        BooleanArgument   useSSL;
219        BooleanArgument   useStartTLS;
220        FileBasedArgument bindPWFile;
221        FileBasedArgument keyStorePWFile;
222        FileBasedArgument trustStorePWFile;
223        IntegerArgument   port;
224        StringArgument    bindDN;
225        StringArgument    bindPW;
226        StringArgument    certNickname;
227        StringArgument    host;
228        StringArgument    keyStoreFile;
229        StringArgument    keyStorePW;
230        StringArgument    proxyAuthzID;
231        StringArgument    saslOption;
232        StringArgument    stopReason;
233        StringArgument    stopTimeStr;
234        StringArgument    trustStoreFile;
235        StringArgument    trustStorePW;
236        StringArgument    propertiesFileArgument;
237        BooleanArgument   noPropertiesFileArgument;
238    
239        try
240        {
241          propertiesFileArgument = new StringArgument("propertiesFilePath",
242              null, OPTION_LONG_PROP_FILE_PATH,
243              false, false, true, INFO_PROP_FILE_PATH_PLACEHOLDER.get(), null, null,
244              INFO_DESCRIPTION_PROP_FILE_PATH.get());
245          argParser.addArgument(propertiesFileArgument);
246          argParser.setFilePropertiesArgument(propertiesFileArgument);
247    
248          noPropertiesFileArgument = new BooleanArgument(
249              "noPropertiesFileArgument", null, OPTION_LONG_NO_PROP_FILE,
250              INFO_DESCRIPTION_NO_PROP_FILE.get());
251          argParser.addArgument(noPropertiesFileArgument);
252          argParser.setNoPropertiesFileArgument(noPropertiesFileArgument);
253    
254          host = new StringArgument("host", OPTION_SHORT_HOST,
255                                    OPTION_LONG_HOST, false, false, true,
256                                    INFO_HOST_PLACEHOLDER.get(), "127.0.0.1", null,
257                                    INFO_STOPDS_DESCRIPTION_HOST.get());
258          host.setPropertyName(OPTION_LONG_HOST);
259          argParser.addArgument(host);
260    
261          port = new IntegerArgument(
262                  "port", OPTION_SHORT_PORT,
263                  OPTION_LONG_PORT, false, false, true,
264                  INFO_PORT_PLACEHOLDER.get(), 389, null, true, 1,
265                  true, 65535, INFO_STOPDS_DESCRIPTION_PORT.get());
266          port.setPropertyName(OPTION_LONG_PORT);
267          argParser.addArgument(port);
268    
269          useSSL = new BooleanArgument("usessl", OPTION_SHORT_USE_SSL,
270                                       OPTION_LONG_USE_SSL,
271                                       INFO_STOPDS_DESCRIPTION_USESSL.get());
272          useSSL.setPropertyName(OPTION_LONG_USE_SSL);
273          argParser.addArgument(useSSL);
274    
275          useStartTLS = new BooleanArgument(
276                  "usestarttls", OPTION_SHORT_START_TLS,
277                  OPTION_LONG_START_TLS,
278                  INFO_STOPDS_DESCRIPTION_USESTARTTLS.get());
279          useStartTLS.setPropertyName(OPTION_LONG_START_TLS);
280          argParser.addArgument(useStartTLS);
281    
282          bindDN = new StringArgument("binddn", OPTION_SHORT_BINDDN,
283                                      OPTION_LONG_BINDDN, false, false, true,
284                                      INFO_BINDDN_PLACEHOLDER.get(), null, null,
285                                      INFO_STOPDS_DESCRIPTION_BINDDN.get());
286          bindDN.setPropertyName(OPTION_LONG_BINDDN);
287          argParser.addArgument(bindDN);
288    
289          bindPW = new StringArgument("bindpw", OPTION_SHORT_BINDPWD,
290                                      OPTION_LONG_BINDPWD, false, false,
291                                      true,
292                                      INFO_BINDPWD_PLACEHOLDER.get(), null, null,
293                                      INFO_STOPDS_DESCRIPTION_BINDPW.get());
294          bindPW.setPropertyName(OPTION_LONG_BINDPWD);
295          argParser.addArgument(bindPW);
296    
297          bindPWFile = new FileBasedArgument(
298                  "bindpwfile",
299                  OPTION_SHORT_BINDPWD_FILE,
300                  OPTION_LONG_BINDPWD_FILE,
301                  false, false,
302                  INFO_BINDPWD_FILE_PLACEHOLDER.get(),
303                  null, null,
304                  INFO_STOPDS_DESCRIPTION_BINDPWFILE.get());
305          bindPWFile.setPropertyName(OPTION_LONG_BINDPWD_FILE);
306          argParser.addArgument(bindPWFile);
307    
308          saslOption = new StringArgument(
309                  "sasloption", OPTION_SHORT_SASLOPTION,
310                  OPTION_LONG_SASLOPTION, false,
311                  true, true,
312                  INFO_SASL_OPTION_PLACEHOLDER.get(), null, null,
313                  INFO_STOPDS_DESCRIPTION_SASLOPTIONS.get());
314          saslOption.setPropertyName(OPTION_LONG_SASLOPTION);
315          argParser.addArgument(saslOption);
316    
317          proxyAuthzID = new StringArgument(
318                  "proxyauthzid",
319                  OPTION_SHORT_PROXYAUTHID,
320                  OPTION_LONG_PROXYAUTHID, false,
321                  false, true,
322                  INFO_PROXYAUTHID_PLACEHOLDER.get(), null,
323                  null,
324                  INFO_STOPDS_DESCRIPTION_PROXYAUTHZID.get());
325          proxyAuthzID.setPropertyName(OPTION_LONG_PROXYAUTHID);
326          argParser.addArgument(proxyAuthzID);
327    
328          stopReason = new StringArgument(
329                  "stopreason", 'r', "stopReason", false,
330                  false, true, INFO_STOP_REASON_PLACEHOLDER.get(), null, null,
331                  INFO_STOPDS_DESCRIPTION_STOP_REASON.get());
332          stopReason.setPropertyName("stopReason");
333          argParser.addArgument(stopReason);
334    
335          checkStoppability = new BooleanArgument("checkstoppability", null,
336                  "checkStoppability",
337                  INFO_STOPDS_CHECK_STOPPABILITY.get());
338          checkStoppability.setHidden(true);
339          argParser.addArgument(checkStoppability);
340    
341          windowsNetStop = new BooleanArgument("windowsnetstop", null,
342              "windowsNetStop", INFO_STOPDS_DESCRIPTION_WINDOWS_NET_STOP.get());
343          windowsNetStop.setHidden(true);
344          argParser.addArgument(windowsNetStop);
345    
346          restart = new BooleanArgument("restart", 'R', "restart",
347                                        INFO_STOPDS_DESCRIPTION_RESTART.get());
348          restart.setPropertyName("restart");
349          argParser.addArgument(restart);
350    
351          stopTimeStr = new StringArgument("stoptime", 't', "stopTime", false,
352                                           false, true,
353                                           INFO_STOP_TIME_PLACEHOLDER.get(), null,
354                                           null,
355                                           INFO_STOPDS_DESCRIPTION_STOP_TIME.get());
356          stopTimeStr.setPropertyName("stopTime");
357          argParser.addArgument(stopTimeStr);
358    
359          trustAll = new BooleanArgument("trustall", 'X', "trustAll",
360                                         INFO_STOPDS_DESCRIPTION_TRUST_ALL.get());
361          trustAll.setPropertyName("trustAll");
362          argParser.addArgument(trustAll);
363    
364          keyStoreFile = new StringArgument("keystorefile",
365                                            OPTION_SHORT_KEYSTOREPATH,
366                                            OPTION_LONG_KEYSTOREPATH,
367                                            false, false, true,
368                                            INFO_KEYSTOREPATH_PLACEHOLDER.get(),
369                                            null, null,
370                                            INFO_STOPDS_DESCRIPTION_KSFILE.get());
371          keyStoreFile.setPropertyName(OPTION_LONG_KEYSTOREPATH);
372          argParser.addArgument(keyStoreFile);
373    
374          keyStorePW = new StringArgument("keystorepw", OPTION_SHORT_KEYSTORE_PWD,
375                                          OPTION_LONG_KEYSTORE_PWD,
376                                          false, false, true,
377                                          INFO_KEYSTORE_PWD_PLACEHOLDER.get(),
378                                          null, null,
379                                          INFO_STOPDS_DESCRIPTION_KSPW.get());
380          keyStorePW.setPropertyName(OPTION_LONG_KEYSTORE_PWD);
381          argParser.addArgument(keyStorePW);
382    
383          keyStorePWFile = new FileBasedArgument(
384                  "keystorepwfile",
385                  OPTION_SHORT_KEYSTORE_PWD_FILE,
386                  OPTION_LONG_KEYSTORE_PWD_FILE,
387                  false, false,
388                  INFO_KEYSTORE_PWD_FILE_PLACEHOLDER.get(),
389                  null, null,
390                  INFO_STOPDS_DESCRIPTION_KSPWFILE.get());
391          keyStorePWFile.setPropertyName(OPTION_LONG_KEYSTORE_PWD_FILE);
392          argParser.addArgument(keyStorePWFile);
393    
394          certNickname = new StringArgument(
395                  "certnickname", 'N', "certNickname",
396                  false, false, true, INFO_NICKNAME_PLACEHOLDER.get(), null,
397                  null, INFO_DESCRIPTION_CERT_NICKNAME.get());
398          certNickname.setPropertyName("certNickname");
399          argParser.addArgument(certNickname);
400    
401          trustStoreFile = new StringArgument("truststorefile",
402                                              OPTION_SHORT_TRUSTSTOREPATH,
403                                              OPTION_LONG_TRUSTSTOREPATH,
404                                              false, false, true,
405                                              INFO_TRUSTSTOREPATH_PLACEHOLDER.get(),
406                                              null, null,
407                                              INFO_STOPDS_DESCRIPTION_TSFILE.get());
408          trustStoreFile.setPropertyName(OPTION_LONG_TRUSTSTOREPATH);
409          argParser.addArgument(trustStoreFile);
410    
411          trustStorePW = new StringArgument(
412                  "truststorepw", 'T',
413                  OPTION_LONG_TRUSTSTORE_PWD,
414                  false, false,
415                  true, INFO_TRUSTSTORE_PWD_PLACEHOLDER.get(), null,
416                  null, INFO_STOPDS_DESCRIPTION_TSPW.get());
417          trustStorePW.setPropertyName(OPTION_LONG_TRUSTSTORE_PWD);
418          argParser.addArgument(trustStorePW);
419    
420          trustStorePWFile = new FileBasedArgument("truststorepwfile",
421                                      OPTION_SHORT_TRUSTSTORE_PWD_FILE,
422                                      OPTION_LONG_TRUSTSTORE_PWD_FILE,
423                                      false, false,
424                                      INFO_TRUSTSTORE_PWD_FILE_PLACEHOLDER.get(),
425                                      null, null,
426                                      INFO_STOPDS_DESCRIPTION_TSPWFILE.get());
427          trustStorePWFile.setPropertyName(OPTION_LONG_TRUSTSTORE_PWD_FILE);
428          argParser.addArgument(trustStorePWFile);
429    
430          quietMode = new BooleanArgument("quiet", OPTION_SHORT_QUIET,
431                                          OPTION_LONG_QUIET,
432                                          INFO_DESCRIPTION_QUIET.get());
433          quietMode.setPropertyName(OPTION_LONG_QUIET);
434          argParser.addArgument(quietMode);
435    
436          showUsage = new BooleanArgument("showusage", OPTION_SHORT_HELP,
437                                          OPTION_LONG_HELP,
438                                          INFO_STOPDS_DESCRIPTION_SHOWUSAGE.get());
439          argParser.addArgument(showUsage);
440          argParser.setUsageArgument(showUsage, out);
441        }
442        catch (ArgumentException ae)
443        {
444          Message message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
445    
446          err.println(wrapText(message, MAX_LINE_WIDTH));
447          return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
448        }
449    
450    
451        // Parse the command-line arguments provided to the program.
452        try
453        {
454          argParser.parseArguments(args);
455        }
456        catch (ArgumentException ae)
457        {
458          Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage());
459    
460          err.println(wrapText(message, MAX_LINE_WIDTH));
461          err.println(argParser.getUsage());
462          return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
463        }
464    
465    
466        // If we should just display usage or version information,
467        // then exit because it will have already been done.
468        if (argParser.usageOrVersionDisplayed())
469        {
470          return LDAPResultCode.SUCCESS;
471        }
472    
473        if (quietMode.isPresent())
474        {
475          out = NullOutputStream.printStream();
476        }
477    
478        if (checkStoppability.isPresent())
479        {
480          System.exit(checkStoppability(argParser, out, err));
481        }
482    
483        // If both a bind password and bind password file were provided, then return
484        // an error.
485        if (bindPW.isPresent() && bindPWFile.isPresent())
486        {
487          Message message = ERR_STOPDS_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
488                  bindPW.getLongIdentifier(),
489                  bindPWFile.getLongIdentifier());
490          err.println(wrapText(message, MAX_LINE_WIDTH));
491          return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
492        }
493    
494    
495        // If both a key store password and key store password file were provided,
496        // then return an error.
497        if (keyStorePW.isPresent() && keyStorePWFile.isPresent())
498        {
499          Message message = ERR_STOPDS_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
500                  keyStorePW.getLongIdentifier(),
501                  keyStorePWFile.getLongIdentifier());
502          err.println(wrapText(message, MAX_LINE_WIDTH));
503          return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
504        }
505    
506    
507        // If both a trust store password and trust store password file were
508        // provided, then return an error.
509        if (trustStorePW.isPresent() && trustStorePWFile.isPresent())
510        {
511          Message message = ERR_STOPDS_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
512                  trustStorePW.getLongIdentifier(),
513                  trustStorePWFile.getLongIdentifier());
514          err.println(wrapText(message, MAX_LINE_WIDTH));
515          return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
516        }
517    
518    
519        // Make sure that we can decode the stop time string if one was provided.
520        Date stopTime = new Date();
521        if (stopTimeStr.isPresent())
522        {
523          String timeStr = stopTimeStr.getValue();
524          if (!TaskTool.NOW.equals(timeStr))
525          {
526            try
527            {
528              stopTime = parseDateTimeString(timeStr);
529            }
530            catch (Exception e)
531            {
532              Message message = ERR_STOPDS_CANNOT_DECODE_STOP_TIME.get();
533              err.println(wrapText(message, MAX_LINE_WIDTH));
534              return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
535            }
536          }
537        }
538    
539    
540        // Create the LDAP connection options object, which will be used to
541        // customize the way that we connect to the server and specify a set of
542        // basic defaults.
543        LDAPConnectionOptions connectionOptions = new LDAPConnectionOptions();
544        connectionOptions.setVersionNumber(3);
545    
546    
547        // See if we should use SSL or StartTLS when establishing the connection.
548        // If so, then make sure only one of them was specified.
549        if (useSSL.isPresent())
550        {
551          if (useStartTLS.isPresent())
552          {
553            Message message = ERR_STOPDS_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
554                    useSSL.getLongIdentifier(),
555                    useStartTLS.getLongIdentifier());
556            err.println(wrapText(message, MAX_LINE_WIDTH));
557            return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
558          }
559          else
560          {
561            connectionOptions.setUseSSL(true);
562          }
563        }
564        else if (useStartTLS.isPresent())
565        {
566          connectionOptions.setStartTLS(true);
567        }
568    
569    
570        // If we should blindly trust any certificate, then install the appropriate
571        // SSL connection factory.
572        if (useSSL.isPresent() || useStartTLS.isPresent())
573        {
574          try
575          {
576            String clientAlias;
577            if (certNickname.isPresent())
578            {
579              clientAlias = certNickname.getValue();
580            }
581            else
582            {
583              clientAlias = null;
584            }
585    
586            SSLConnectionFactory sslConnectionFactory = new SSLConnectionFactory();
587            sslConnectionFactory.init(trustAll.isPresent(), keyStoreFile.getValue(),
588                                      keyStorePW.getValue(), clientAlias,
589                                      trustStoreFile.getValue(),
590                                      trustStorePW.getValue());
591    
592            connectionOptions.setSSLConnectionFactory(sslConnectionFactory);
593          }
594          catch (SSLConnectionException sce)
595          {
596            Message message =
597                    ERR_STOPDS_CANNOT_INITIALIZE_SSL.get(sce.getMessage());
598            err.println(wrapText(message, MAX_LINE_WIDTH));
599            return LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR;
600          }
601        }
602    
603    
604        // If one or more SASL options were provided, then make sure that one of
605        // them was "mech" and specified a valid SASL mechanism.
606        if (saslOption.isPresent())
607        {
608          String             mechanism = null;
609          LinkedList<String> options   = new LinkedList<String>();
610    
611          for (String s : saslOption.getValues())
612          {
613            int equalPos = s.indexOf('=');
614            if (equalPos <= 0)
615            {
616              Message message = ERR_STOPDS_CANNOT_PARSE_SASL_OPTION.get(s);
617              err.println(wrapText(message, MAX_LINE_WIDTH));
618              return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
619            }
620            else
621            {
622              String name  = s.substring(0, equalPos);
623    
624              if (name.equalsIgnoreCase("mech"))
625              {
626                mechanism = s;
627              }
628              else
629              {
630                options.add(s);
631              }
632            }
633          }
634    
635          if (mechanism == null)
636          {
637            Message message = ERR_STOPDS_NO_SASL_MECHANISM.get();
638            err.println(wrapText(message, MAX_LINE_WIDTH));
639            return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
640          }
641    
642          connectionOptions.setSASLMechanism(mechanism);
643    
644          for (String option : options)
645          {
646            connectionOptions.addSASLProperty(option);
647          }
648        }
649    
650    
651        // Attempt to connect and authenticate to the Directory Server.
652        AtomicInteger nextMessageID = new AtomicInteger(1);
653        LDAPConnection connection;
654        try
655        {
656          connection = new LDAPConnection(host.getValue(), port.getIntValue(),
657                                          connectionOptions, out, err);
658          connection.connectToHost(bindDN.getValue(),
659              LDAPConnectionArgumentParser.getPasswordValue(bindPW, bindPWFile),
660              nextMessageID);
661        }
662        catch (ArgumentException ae)
663        {
664          Message message = ERR_STOPDS_CANNOT_DETERMINE_PORT.get(
665                  port.getLongIdentifier(),
666                  ae.getMessage());
667          err.println(wrapText(message, MAX_LINE_WIDTH));
668          return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
669        }
670        catch (LDAPConnectionException lce)
671        {
672          Message message = ERR_STOPDS_CANNOT_CONNECT.get(lce.getMessage());
673          err.println(wrapText(message, MAX_LINE_WIDTH));
674          return LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR;
675        }
676    
677        LDAPReader reader = connection.getLDAPReader();
678        LDAPWriter writer = connection.getLDAPWriter();
679    
680    
681        // Construct the add request to send to the server.
682        String taskID = UUID.randomUUID().toString();
683        ASN1OctetString entryDN =
684             new ASN1OctetString(ATTR_TASK_ID + "=" + taskID + "," +
685                                 SCHEDULED_TASK_BASE_RDN + "," + DN_TASK_ROOT);
686    
687        ArrayList<RawAttribute> attributes = new ArrayList<RawAttribute>();
688    
689        ArrayList<ASN1OctetString> ocValues = new ArrayList<ASN1OctetString>(3);
690        ocValues.add(new ASN1OctetString("top"));
691        ocValues.add(new ASN1OctetString("ds-task"));
692        ocValues.add(new ASN1OctetString("ds-task-shutdown"));
693        attributes.add(new LDAPAttribute(ATTR_OBJECTCLASS, ocValues));
694    
695        ArrayList<ASN1OctetString> taskIDValues = new ArrayList<ASN1OctetString>(1);
696        taskIDValues.add(new ASN1OctetString(taskID));
697        attributes.add(new LDAPAttribute(ATTR_TASK_ID, taskIDValues));
698    
699        ArrayList<ASN1OctetString> classValues = new ArrayList<ASN1OctetString>(1);
700        classValues.add(new ASN1OctetString(ShutdownTask.class.getName()));
701        attributes.add(new LDAPAttribute(ATTR_TASK_CLASS, classValues));
702    
703        if (restart.isPresent())
704        {
705          ArrayList<ASN1OctetString> restartValues =
706               new ArrayList<ASN1OctetString>(1);
707          restartValues.add(new ASN1OctetString("true"));
708          attributes.add(new LDAPAttribute(ATTR_RESTART_SERVER, restartValues));
709        }
710    
711        if (stopReason.isPresent())
712        {
713          ArrayList<ASN1OctetString> stopReasonValues =
714               new ArrayList<ASN1OctetString>(1);
715          stopReasonValues.add(new ASN1OctetString(stopReason.getValue()));
716          attributes.add(new LDAPAttribute(ATTR_SHUTDOWN_MESSAGE,
717                                           stopReasonValues));
718        }
719    
720        if (stopTime != null)
721        {
722          ArrayList<ASN1OctetString> stopTimeValues =
723               new ArrayList<ASN1OctetString>(1);
724    
725          SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME);
726          dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
727          stopTimeValues.add(new ASN1OctetString(dateFormat.format(stopTime)));
728          attributes.add(new LDAPAttribute(ATTR_TASK_SCHEDULED_START_TIME,
729                                           stopTimeValues));
730        }
731    
732        ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
733        if (proxyAuthzID.isPresent())
734        {
735          Control c = new ProxiedAuthV2Control(
736                               new ASN1OctetString(proxyAuthzID.getValue()));
737          controls.add(new LDAPControl(c));
738        }
739    
740        AddRequestProtocolOp addRequest = new AddRequestProtocolOp(entryDN,
741                                                                   attributes);
742        LDAPMessage requestMessage =
743             new LDAPMessage(nextMessageID.getAndIncrement(), addRequest, controls);
744    
745    
746        // Send the request to the server and read the response.
747        LDAPMessage responseMessage;
748        try
749        {
750          writer.writeMessage(requestMessage);
751    
752          responseMessage = reader.readMessage();
753          if (responseMessage == null)
754          {
755            Message message = ERR_STOPDS_UNEXPECTED_CONNECTION_CLOSURE.get();
756            err.println(wrapText(message, MAX_LINE_WIDTH));
757            return LDAPResultCode.CLIENT_SIDE_SERVER_DOWN;
758          }
759        }
760        catch (IOException ioe)
761        {
762          Message message = ERR_STOPDS_IO_ERROR.get(String.valueOf(ioe));
763          err.println(wrapText(message, MAX_LINE_WIDTH));
764          return LDAPResultCode.CLIENT_SIDE_SERVER_DOWN;
765        }
766        catch (ASN1Exception ae)
767        {
768          Message message = ERR_STOPDS_DECODE_ERROR.get(ae.getMessage());
769          err.println(wrapText(message, MAX_LINE_WIDTH));
770          return LDAPResultCode.CLIENT_SIDE_DECODING_ERROR;
771        }
772        catch (LDAPException le)
773        {
774          Message message = ERR_STOPDS_DECODE_ERROR.get(le.getMessage());
775          err.println(wrapText(message, MAX_LINE_WIDTH));
776          return LDAPResultCode.CLIENT_SIDE_DECODING_ERROR;
777        }
778    
779    
780        if (responseMessage.getProtocolOpType() !=
781            LDAPConstants.OP_TYPE_ADD_RESPONSE)
782        {
783          if (responseMessage.getProtocolOpType() ==
784              LDAPConstants.OP_TYPE_EXTENDED_RESPONSE)
785          {
786            // It's possible that this is a notice of disconnection, which we can
787            // probably interpret as a "success" in this case.
788            ExtendedResponseProtocolOp extendedResponse =
789                 responseMessage.getExtendedResponseProtocolOp();
790            String responseOID = extendedResponse.getOID();
791            if ((responseOID != null) &&
792                (responseOID.equals(LDAPConstants.OID_NOTICE_OF_DISCONNECTION)))
793            {
794              Message message = extendedResponse.getErrorMessage();
795              if (message != null)
796              {
797                err.println(wrapText(message, MAX_LINE_WIDTH));
798              }
799    
800              return extendedResponse.getResultCode();
801            }
802          }
803    
804    
805          Message message = ERR_STOPDS_INVALID_RESPONSE_TYPE.get(
806                  responseMessage.getProtocolOpName());
807          err.println(wrapText(message, MAX_LINE_WIDTH));
808          return LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR;
809        }
810    
811    
812        AddResponseProtocolOp addResponse =
813             responseMessage.getAddResponseProtocolOp();
814        Message errorMessage = addResponse.getErrorMessage();
815        if (errorMessage != null)
816        {
817          err.println(wrapText(errorMessage, MAX_LINE_WIDTH));
818        }
819    
820        return addResponse.getResultCode();
821      }
822    
823      /**
824       * Returns the error code that we return when we are checking the stoppability
825       * of the server.  This basically tells the invoker what must be done based
826       * on the different parameters passed.
827       * @param argParser the ArgumentParser with the arguments already parsed.
828       * @param out the print stream to use for standard output.
829       * @param err the print stream to use for standard error.
830       * @return the error code that we return when we are checking the stoppability
831       * of the server.
832       */
833      private static int checkStoppability(ArgumentParser argParser,
834                                           PrintStream out, PrintStream err)
835      {
836        int returnValue;
837        boolean isServerRunning;
838    
839        boolean quietMode = false;
840        Argument quietArg = argParser.getArgumentForLongID("quiet");
841        if ((quietArg != null) && quietArg.isPresent())
842        {
843          quietMode = true;
844        }
845    
846        BooleanArgument restart =
847          (BooleanArgument)argParser.getArgumentForLongID("restart");
848        boolean restartPresent = restart.isPresent();
849        BooleanArgument windowsNetStop =
850          (BooleanArgument)argParser.getArgumentForLongID("windowsnetstop");
851        boolean windowsNetStopPresent = windowsNetStop.isPresent();
852    
853        // Check if this is a stop through protocol.
854        LinkedList<Argument> list = argParser.getArgumentList();
855        boolean stopThroughProtocol = false;
856        for (Argument arg: list)
857        {
858          if (!"restart".equals(arg.getName()) &&
859              !"quiet".equals(arg.getName()) &&
860              !"showusage".equals(arg.getName()) &&
861              !"checkstoppability".equals(arg.getName()) &&
862              !"windowsnetstop".equals(arg.getName()))
863          {
864            stopThroughProtocol |= arg.isPresent();
865          }
866        }
867    
868        if (stopThroughProtocol)
869        {
870          // Assume that this is done on a remote server and do no more checks.
871          returnValue = STOP_USING_PROTOCOL;
872        }
873        else
874        {
875          String lockFile = LockFileManager.getServerLockFileName();
876          try
877          {
878            StringBuilder failureReason = new StringBuilder();
879            if (LockFileManager.acquireExclusiveLock(lockFile, failureReason))
880            {
881              // The server is not running: write a message informing of that
882              // in the standard out (this is not an error message).
883              Message message = INFO_STOPDS_SERVER_ALREADY_STOPPED.get();
884              out.println(message);
885              LockFileManager.releaseLock(lockFile, failureReason);
886              isServerRunning = false;
887            }
888            else
889            {
890              isServerRunning = true;
891            }
892          }
893          catch (Exception e)
894          {
895            // Assume that if we cannot acquire the lock file the server is
896            // running.
897            isServerRunning = true;
898          }
899    
900          boolean configuredAsService =
901              DirectoryServer.isRunningAsWindowsService();
902    
903          if (!isServerRunning)
904          {
905            if (configuredAsService && !windowsNetStopPresent)
906            {
907              if (restartPresent)
908              {
909                returnValue = RESTART_AS_WINDOW_SERVICE;
910              }
911              else
912              {
913                returnValue = STOP_AS_WINDOW_SERVICE;
914              }
915            }
916            else if (restartPresent)
917            {
918              if (quietMode)
919              {
920                returnValue = START_SERVER_QUIET;
921              }
922              else
923              {
924                returnValue = START_SERVER;
925              }
926            }
927            else
928            {
929              returnValue = SERVER_ALREADY_STOPPED;
930            }
931          }
932          else
933          {
934            if (configuredAsService)
935            {
936              if (windowsNetStopPresent)
937              {
938                // stop-ds.bat is being called through net stop, so return
939                // STOP_USING_SYSTEM_CALL or RESTART_USING_SYSTEM_CALL so that the
940                // batch file actually stops the server.
941                if (restartPresent)
942                {
943                  if (quietMode)
944                  {
945                    returnValue = RESTART_USING_SYSTEM_CALL_QUIET;
946                  }
947                  else
948                  {
949                    returnValue = RESTART_USING_SYSTEM_CALL;
950                  }
951                }
952                else
953                {
954                  returnValue = STOP_USING_SYSTEM_CALL;
955                }
956              }
957              else
958              {
959                if (restartPresent)
960                {
961                  returnValue = RESTART_AS_WINDOW_SERVICE;
962                }
963                else
964                {
965                  returnValue = STOP_AS_WINDOW_SERVICE;
966                }
967                // Display a message informing that we are going to the server.
968    
969                Message message = INFO_STOPDS_GOING_TO_STOP.get();
970                out.println(message);
971              }
972            }
973            else
974            {
975              // Display a message informing that we are going to the server.
976    
977              Message message = INFO_STOPDS_GOING_TO_STOP.get();
978              out.println(message);
979    
980              if (restartPresent)
981              {
982                if (quietMode)
983                {
984                  returnValue = RESTART_USING_SYSTEM_CALL_QUIET;
985                }
986                else
987                {
988                  returnValue = RESTART_USING_SYSTEM_CALL;
989                }
990              }
991              else
992              {
993                returnValue = STOP_USING_SYSTEM_CALL;
994              }
995            }
996          }
997        }
998        return returnValue;
999      }
1000    }
1001