View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.geronimo.gshell.commands.ssh;
21  
22  import com.google.code.sshd.ClientChannel;
23  import com.google.code.sshd.ClientSession;
24  import com.google.code.sshd.SshClient;
25  import com.google.code.sshd.common.util.NoCloseInputStream;
26  import com.google.code.sshd.common.util.NoCloseOutputStream;
27  import org.apache.geronimo.gshell.clp.Argument;
28  import org.apache.geronimo.gshell.clp.Option;
29  import org.apache.geronimo.gshell.command.CommandAction;
30  import org.apache.geronimo.gshell.command.CommandContext;
31  import org.apache.geronimo.gshell.i18n.MessageSource;
32  import org.apache.geronimo.gshell.io.IO;
33  import org.apache.geronimo.gshell.io.PromptReader;
34  import org.apache.geronimo.gshell.spring.BeanContainer;
35  import org.apache.geronimo.gshell.spring.BeanContainerAware;
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  
39  /**
40   * Connect to a SSH server.
41   *
42   * @version $Rev: 721244 $ $Date: 2008-11-27 18:19:56 +0100 (Thu, 27 Nov 2008) $
43   */
44  public class SshAction
45      implements CommandAction, BeanContainerAware
46  {
47      private final Logger log = LoggerFactory.getLogger(getClass());
48  
49      @Option(name="-l", aliases={"--username"})
50      private String username;
51  
52      @Option(name="-P", aliases={"--password"})
53      private String password;
54  
55      @Argument(required=true)
56      private String hostname;
57  
58      @Option(name="-p", aliases={"--port"})
59      private int port = 22;
60  
61      private BeanContainer container;
62  
63      public void setBeanContainer(final BeanContainer container) {
64          assert container != null;
65          this.container = container;
66      }
67  
68      /**
69       * Helper to validate that prompted username or password is not null or empty.
70       */
71      private class UsernamePasswordValidator
72          implements PromptReader.Validator
73      {
74          private String type;
75  
76          private int count = 0;
77  
78          private int max = 3;
79  
80          public UsernamePasswordValidator(final String type) {
81              assert type != null;
82  
83              this.type = type;
84          }
85  
86          public boolean isValid(final String value) {
87              count++;
88  
89              if (value != null && value.trim().length() > 0) {
90                  return true;
91              }
92  
93              if (count >= max) {
94                  throw new RuntimeException("Too many attempts; failed to prompt user for " + type + " after " + max + " tries");
95              }
96  
97              return false;
98          }
99      }
100 
101     public Object execute(final CommandContext context) throws Exception {
102         assert context != null;
103         IO io = context.getIo();
104         MessageSource messages = context.getCommand().getMessages();
105 
106         //
107         // TODO: Parse hostname for <username>@<hostname>
108         //
109         
110         io.info(messages.format("info.connecting", hostname, port));
111 
112         // If the username/password was not configured via cli, then prompt the user for the values
113         if (username == null || password == null) {
114             PromptReader prompter = new PromptReader(io);
115             String text;
116 
117             log.debug("Prompting user for credentials");
118 
119             if (username == null) {
120                 text = messages.getMessage("prompt.username");
121                 username = prompter.readLine(text + ": ", new UsernamePasswordValidator(text));
122             }
123 
124             if (password == null) {
125                 text = messages.getMessage("prompt.password");
126                 password = prompter.readPassword(text + ": ", new UsernamePasswordValidator(text));
127             }
128         }
129 
130         // Create the client from prototype
131         SshClient client = container.getBean(SshClient.class);
132         log.debug("Created client: {}", client);
133         client.start();;
134 
135         try {
136             ClientSession session = client.connect(hostname, port);
137             try {
138                 io.info(messages.getMessage("info.connected"));
139 
140                 session.authPassword(username, password);
141                 int ret = session.waitFor(ClientSession.WAIT_AUTH | ClientSession.CLOSED | ClientSession.AUTHED, 0);
142                 if ((ret & ClientSession.AUTHED) == 0) {
143                     io.err.println("Authentication failed");
144                     return Result.FAILURE;
145                 }
146 
147                 ClientChannel channel = session.createChannel("shell");
148                 channel.setIn(new NoCloseInputStream(io.inputStream));
149                 channel.setOut(new NoCloseOutputStream(io.outputStream));
150                 channel.setErr(new NoCloseOutputStream(io.errorStream));
151                 channel.open();
152                 channel.waitFor(ClientChannel.CLOSED, 0);
153             } finally {
154                 session.close();
155             }
156         } finally {
157             client.stop();
158         }
159 
160         io.verbose(messages.getMessage("verbose.disconnected"));
161 
162         return Result.SUCCESS;
163     }
164 }