1 /***
2 *
3 */
4 package de.cohesion.bssh;
5
6 import java.io.File;
7 import java.io.IOException;
8 import java.io.PrintStream;
9 import java.util.Collections;
10 import java.util.HashMap;
11 import java.util.LinkedList;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Set;
15 import java.util.concurrent.TimeUnit;
16
17 import ch.ethz.ssh2.Connection;
18 import de.cohesion.bssh.impl.CommandProcessor;
19 import de.cohesion.bssh.impl.ExecuteSSHCommand;
20 import de.cohesion.bssh.impl.FileMemberSource;
21 import de.cohesion.bssh.impl.PBEPasswordStore;
22 import de.cohesion.bssh.impl.SSHCommand;
23 import de.cohesion.bssh.impl.SSHConnectionFactory;
24 import de.cohesion.bssh.impl.io.PasswordHelper;
25 import de.cohesion.bssh.impl.util.Cache;
26 import de.cohesion.bssh.impl.util.Cache.ReplacementStrategy;
27
28 /***
29 * @author schulzs
30 */
31 public class BatchSSH {
32
33 private enum ReturnCodes {
34 OK("Success"), INVALID_OPTION("Invalid option"), UNRECOGNIZED_OPTION(
35 "Unrecognized option"), GROUP_FILE_ERROR(
36 "Error opening the group file"), PASSPHRASE_ERROR(
37 "Error reading passphrase"), PASSWORD_STORE_ERROR(
38 "Failed to open password store");
39
40 private String message;
41
42 ReturnCodes(final String message) {
43 this.message = message;
44 }
45
46 public String getMessage() {
47 return message;
48 }
49
50 public void exit() {
51 exit(null, null);
52 }
53
54 public void exit(final String msg) {
55 exit(msg, null);
56 }
57
58 public void exit(final Throwable t) {
59 exit(null, t);
60 }
61
62 public void exit(final String msg, final Throwable t) {
63 PrintStream ps = null;
64 if (this == OK) {
65 ps = System.out;
66 } else {
67 ps = System.err;
68 }
69 ps.print(getMessage() + " (code: " + ordinal());
70 if (msg != null) {
71 ps.print(", message: " + msg);
72 }
73 if (t != null) {
74 ps.print(", cause: " + t.getMessage());
75 }
76 ps.println(").");
77 System.exit(ordinal());
78 }
79
80 }
81
82 private static Cache<Member, Connection> ccache;
83
84 private static final Map<String, Option> OPTIONS = new HashMap<String, Option>();
85 static {
86 for (Option o : new Option[] { new Concurrency(), new Help(),
87 new Timeout(), new Password() }) {
88 OPTIONS.put(o.getKey(), o);
89 }
90 }
91
92 private static char[] getPassphrase() {
93 Password p = (Password) OPTIONS.get(Password.KEY);
94 if (p.isActive()) {
95 return p.getPassword();
96 } else {
97 try {
98 return PasswordHelper.getPassword(System.in, "Password: ");
99 } catch (IOException ioe) {
100 ReturnCodes.PASSPHRASE_ERROR.exit(ioe);
101 }
102 }
103 return null;
104 }
105
106 public static void main(final String[] args) {
107
108 if (args.length == 0) {
109 showHelp();
110 }
111
112 int offset = parseOptions(args);
113
114
115 MemberSource source = null;
116 try {
117 source = new FileMemberSource(new File(args[offset]));
118 } catch (IOException ioe) {
119 ReturnCodes.GROUP_FILE_ERROR.exit(ioe);
120 }
121 Set<Member> members = source.getMembers();
122
123
124 StringBuilder b = new StringBuilder();
125 for (int i = 1; i < args.length - offset; i++) {
126 b.append(args[offset + i]);
127 b.append(" ");
128 }
129
130
131 char[] passphrase = getPassphrase();
132
133 PasswordStore store = null;
134 try {
135 store = new PBEPasswordStore(new File(System
136 .getProperty("user.home")
137 + "/.bssh"), passphrase);
138 } catch (IOException ioe) {
139 ReturnCodes.PASSWORD_STORE_ERROR.exit(ioe);
140 }
141
142
143 ccache = new Cache<Member, Connection>(1024, new SSHConnectionFactory(
144 3, store), ReplacementStrategy.LRU);
145
146 SSHCommand command = new ExecuteSSHCommand(b.toString(), ccache);
147
148 CommandProcessor processor = new CommandProcessor(Collections
149 .singleton(command), members, Timeout.class.cast(
150 OPTIONS.get(Timeout.KEY)).getTimeout());
151 processor.setConcurrency(Concurrency.class.cast(
152 OPTIONS.get(Concurrency.KEY)).getLevel());
153
154
155 long start = System.nanoTime();
156 List<Result> results = new LinkedList<Result>();
157 showProgress(System.out, members.size(), 0);
158 for (Result r : processor.getResults()) {
159 results.add(r);
160 showProgress(System.out, members.size(), results.size()
161 / (float) members.size());
162 }
163 long end = System.nanoTime();
164 System.out.println("\n");
165
166
167 ResultFormat rf = ResultFormat.SIMPLE;
168 for (Result r : results) {
169 System.out.println(rf.format(r) + "\n");
170 }
171
172 long diff = end - start;
173 System.out.println("Total execution time: "
174 + TimeUnit.SECONDS.convert(diff, TimeUnit.NANOSECONDS) + "s "
175 + TimeUnit.MILLISECONDS.convert(diff, TimeUnit.NANOSECONDS)
176 % 1000 + "ms");
177
178 ccache.invalidate();
179
180 System.exit(0);
181
182 }
183
184 private static int parseOptions(final String[] args) {
185
186 int offset = 0;
187 for (int i = 0; i < args.length;) {
188 String arg = args[i];
189
190
191 Option o = null;
192 if (arg.startsWith("--")) {
193 o = OPTIONS.get(arg.substring(2));
194 } else if (arg.startsWith("-")) {
195 for (String key : OPTIONS.keySet()) {
196 if (key.startsWith(arg.substring(1, 2))) {
197 o = OPTIONS.get(key);
198 break;
199 }
200 }
201 } else {
202 break;
203 }
204
205 if (o == null) {
206 ReturnCodes.UNRECOGNIZED_OPTION.exit(arg);
207 }
208
209 assert o != null;
210
211
212 StringBuilder b = new StringBuilder();
213 int j = 0;
214 while (j <= o.getArity()) {
215 if (j > 0 && args[i + j].startsWith("-")) {
216 break;
217 }
218 b.append(args[i + j]);
219 b.append(' ');
220 j++;
221 }
222 i += j;
223
224
225 try {
226 o.parse(b.toString());
227 } catch (ParseException pe) {
228 ReturnCodes.INVALID_OPTION.exit(o.getKey(), pe);
229 }
230
231 offset = i;
232 }
233
234
235 for (Option o : OPTIONS.values()) {
236 if (o.isActive()) {
237 if (o instanceof Help) {
238 showHelp();
239 }
240 }
241 }
242
243 return offset;
244 }
245
246 private static void showHelp() {
247 StringBuilder b = new StringBuilder();
248 b.append("usage: bssh [-options] <group-file> <command>\n\n");
249 b.append("where options include:\n");
250 for (Option o : OPTIONS.values()) {
251 b.append(" -" + o.getKey().charAt(0) + ", --" + o.getKey() + " ");
252 b.append(o.getHelpText() + "\n");
253 }
254 System.out.println(b.toString());
255 ReturnCodes.OK.exit();
256 }
257
258 private static void showProgress(final PrintStream p, int width, float ratio) {
259 p.print("\015" + "[");
260 for (int i = 0; i < (int) Math.floor(width * ratio); i++) {
261 p.print("#");
262 }
263 for (int i = (int) Math.floor(width * ratio); i < width; i++) {
264 p.print("-");
265 }
266 p.print("]");
267 }
268
269 }