1 # This program is free software; you can redistribute it and/or modify
2 # it under the terms of the GNU General Public License as published by
3 # the Free Software Foundation; either version 2 of the License, or
4 # (at your option) any later version.
6 # This program is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # GNU Library General Public License for more details.
11 # You should have received a copy of the GNU General Public License
12 # along with this program; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 # See the COPYING file for license information.
17 # Copyright (c) 2006, 2007, 2008 Guillaume Chazarain <guichaz@gmail.com>
23 from gsh
.control_commands_helpers
import complete_shells
, selected_shells
24 from gsh
.control_commands_helpers
import list_control_commands
25 from gsh
.control_commands_helpers
import get_control_command
, toggle_shells
26 from gsh
.control_commands_helpers
import expand_local_path
27 from gsh
.completion
import complete_local_absolute_path
28 from gsh
.console
import console_output
29 from gsh
import dispatchers
30 from gsh
import remote_dispatcher
32 from gsh
import file_transfer
34 def complete_help(line
, text
):
35 colon
= text
.startswith(':')
36 text
= text
.lstrip(':')
37 res
= [cmd
+ ' ' for cmd
in list_control_commands() if \
38 cmd
.startswith(text
) and ' ' + cmd
+ ' ' not in line
]
40 res
= [':' + cmd
for cmd
in res
]
45 Usage: :help [COMMAND]
46 List control commands or show their documentations.
48 command
= command
.strip()
51 for name
in command
.split():
53 cmd
= get_control_command(name
.lstrip(':'))
54 except AttributeError:
55 console_output('Unknown control command: %s\n' % name
)
57 doc
= [d
.strip() for d
in cmd
.__doc
__.split('\n') if d
.strip()]
58 texts
.append('\n'.join(doc
))
60 console_output('\n\n'.join(texts
))
63 names
= list_control_commands()
64 max_name_len
= max(map(len, names
))
65 for i
in xrange(len(names
)):
67 txt
= (max_name_len
- len(name
)) * ' ' + ':' + name
+ ' - '
68 doc
= get_control_command(name
).__doc
__
69 txt
+= doc
.split('\n')[2].strip() + '\n'
72 def complete_list(line
, text
):
73 return complete_shells(line
, text
)
77 Usage: :list [SHELLS...]
78 List remote shells and their states.
79 The special characters * ? and [] work as expected.
81 nr_active
= nr_dead
= 0
83 for i
in selected_shells(command
):
84 instances
.append(i
.get_info())
89 dispatchers
.format_info(instances
)
90 console_output('%s\n\n%d active shells, %d dead shells, total: %d\n' % \
91 ('\n'.join(instances
), nr_active
, nr_dead
, nr_active
+ nr_dead
))
98 raise asyncore
.ExitNow(0)
100 def complete_chdir(line
, text
):
101 return [p
+ '/' for p
in glob
.glob(expand_local_path(text
) + '*') if
104 def do_chdir(command
):
107 Change the current directory of gsh (not the remote shells).
110 os
.chdir(expand_local_path(command
))
112 console_output('%s\n' % str(e
))
114 def complete_send_ctrl(line
, text
):
115 if len(line
[:-1].split()) >= 2:
116 # Control letter already given in command line
117 return complete_shells(line
, text
, lambda i
: i
.enabled
)
118 if text
in ('c', 'd', 'z'):
120 return ['c ', 'd ', 'z ']
122 def do_send_ctrl(command
):
124 Usage: :send_ctrl LETTER [SHELLS...]
125 Send a control character to remote shells.
126 The first argument is the control character to send: c, d or z.
127 The remaining optional arguments are the destination shells.
128 The special characters * ? and [] work as expected.
130 split
= command
.split()
132 console_output('Expected at least a letter\n')
136 console_output('Expected a single letter, got: %s\n' % letter
)
138 control_letter
= chr(ord(letter
.lower()) - ord('a') + 1)
139 for i
in selected_shells(' '.join(split
[1:])):
141 i
.dispatch_write(control_letter
)
143 def complete_reset_prompt(line
, text
):
144 return complete_shells(line
, text
, lambda i
: i
.enabled
)
146 def do_reset_prompt(command
):
148 Usage: :reset_prompt [SHELLS...]
149 Change the prompt to be recognized by gsh.
150 The special characters * ? and [] work as expected.
152 for i
in selected_shells(command
):
153 i
.dispatch_command(i
.init_string
)
155 def complete_enable(line
, text
):
156 return complete_shells(line
, text
, lambda i
: i
.active
and not i
.enabled
)
158 def do_enable(command
):
160 Usage: :enable [SHELLS...]
161 Enable sending commands to remote shells.
162 The special characters * ? and [] work as expected.
164 toggle_shells(command
, True)
166 def complete_disable(line
, text
):
167 return complete_shells(line
, text
, lambda i
: i
.enabled
)
169 def do_disable(command
):
171 Usage: :disable [SHELLS...]
172 Disable sending commands to remote shells.
173 The special characters * ? and [] work as expected.
175 toggle_shells(command
, False)
177 def complete_reconnect(line
, text
):
178 return complete_shells(line
, text
, lambda i
: not i
.active
)
180 def do_reconnect(command
):
182 Usage: :reconnect [SHELLS...]
183 Try to reconnect to disconnected remote shells.
184 The special characters * ? and [] work as expected.
186 for i
in selected_shells(command
):
193 Add one or many remote shells.
195 for host
in command
.split():
196 remote_dispatcher
.remote_dispatcher(host
)
198 def complete_purge(line
, text
):
199 return complete_shells(line
, text
, lambda i
: not i
.enabled
)
201 def do_purge(command
):
203 Usage: :purge [SHELLS...]
204 Delete disabled remote shells.
205 This helps to have a shorter list.
206 The special characters * ? and [] work as expected.
209 for i
in selected_shells(command
):
216 def do_rename(command
):
218 Usage: :rename [NEW_NAME]
219 Rename all enabled remote shells with the argument.
220 The argument will be shell expanded on the remote processes. With no
221 argument, the original hostname will be restored as the displayed name.
223 for i
in dispatchers
.all_instances():
227 def do_hide_password(command
):
229 Usage: :hide_password
230 Do not echo the next typed line.
231 This is useful when entering password. If debugging or logging is enabled,
232 it will be disabled to avoid displaying a password.
235 for i
in dispatchers
.all_instances():
236 if i
.enabled
and i
.debug
:
239 console_output('Debugging disabled to avoid displaying '
242 stdin
.set_echo(False)
244 if remote_dispatcher
.options
.log_file
:
245 console_output('Logging disabled to avoid writing passwords\n')
246 remote_dispatcher
.options
.log_file
= None
248 def complete_set_debug(line
, text
):
249 if len(line
[:-1].split()) >= 2:
250 # Debug value already given in command line
251 return complete_shells(line
, text
)
252 if text
.lower() in ('y', 'n'):
256 def do_set_debug(command
):
258 Usage: :set_debug y|n [SHELLS...]
259 Enable or disable debugging output for remote shells.
260 The first argument is 'y' to enable the debugging output, 'n' to
262 The remaining optional arguments are the selected shells.
263 The special characters * ? and [] work as expected.
265 split
= command
.split()
267 console_output('Expected at least a letter\n')
269 letter
= split
[0].lower()
270 if letter
not in ('y', 'n'):
271 console_output("Expected 'y' or 'n', got: %s\n" % split
[0])
273 debug
= letter
== 'y'
274 for i
in selected_shells(' '.join(split
[1:])):
277 def complete_replicate(line
, text
):
279 return [c
[:-1] + ':' for c
in complete_shells(line
, text
)]
280 shell
, path
= text
.split(':')
281 return [shell
+ ':' + p
for p
in complete_local_absolute_path(path
)]
283 def do_replicate(command
):
285 Usage: :replicate SHELL:path
286 Copy a path from one remote shell to all others
288 if ':' not in command
:
289 console_output('Usage: :replicate SHELL:path\n')
291 shell_name
, path
= command
.split(':', 1)
292 for shell
in dispatchers
.all_instances():
293 if shell
.display_name
== shell_name
:
294 if not shell
.enabled
:
295 console_output('%s is not enabled\n' % shell_name
)
299 console_output('%s not found\n' % shell_name
)
301 file_transfer
.replicate(shell
, path
)
303 def do_export_rank(command
):
306 Set GSH_RANK and GSH_NR_SHELLS on enabled remote shells.
307 The GSH_RANK shell variable uniquely identifies each shell with a number
308 between 0 and GSH_NR_SHELLS - 1. GSH_NR_SHELLS is the total number of
312 for shell
in dispatchers
.all_instances():
314 shell
.dispatch_command('export GSH_RANK=%d\n' % rank
)
317 for shell
in dispatchers
.all_instances():
319 shell
.dispatch_command('export GSH_NR_SHELLS=%d\n' % rank
)
321 def complete_log_output(line
, text
):
322 return [p
for p
in glob
.glob(expand_local_path(text
or './') + '*')]
324 def do_log_output(command
):
326 Usage: :log_output [PATH]
327 Duplicate every console output into the given local file.
328 If PATH is not given, restore the default behaviour of not logging the
333 remote_dispatcher
.options
.log_file
= file(command
, 'a')
335 console_output('%s\n' % str(e
))
338 remote_dispatcher
.options
.log_file
= None
339 console_output('Logging disabled\n')
343 Output a help text of each control command suitable for the man page
345 for name
in list_control_commands():
347 unstripped
= get_control_command(name
).__doc
__.split('\n')
348 lines
= [l
.strip() for l
in unstripped
]
349 usage
= lines
[1].strip()
350 print '<fB>%s<fR>' % usage
[7:]
351 help_text
= ' '.join(lines
[2:]).replace('gsh', '<fI>gsh<fR>').strip()
354 if __name__
== '__main__':