"Fossies" - the Fresh Open Source Software Archive 
Member "sshexport-2.5/sshexport" (20 Sep 2021, 90123 Bytes) of package /linux/privat/sshexport-2.5.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
See also the latest
Fossies "Diffs" side-by-side code changes report for "sshexport":
2.4_vs_2.5.
1 #!/usr/bin/env python3
2 #
3 # sshexport -- prepare a remote account for ssh access
4 #
5 # SPDX-License-Identifier: BSD-2-clause
6 #
7 # This code runs under both Python 2 and Python 3. Preserve this property!
8 from __future__ import print_function
9
10 """This script tries to export ssh public keys to a specified site. It will
11 walk you through generating key pairs if it doesn't find any to export.
12 It handles all the fiddly details like making sure local and remote
13 permissions are correct, and tells you what it's doing if it has to change
14 anything.
15
16 Options:
17 -h Print usage summary and exit
18 -c Check local and remote configuration only, don't try to fix problems.
19 -d Delete remote .ssh configuration.
20 -r Install keys on all sites in the saved list.
21 -v Report all commands sent to host and output received.
22 -p <port> Connect on a particular port in place of 22
23
24 Author: Eric S. Raymond <esr@thyrsus.com>"""
25
26 import os, stat, sys, getopt, getpass, tempfile
27
28 try:
29 import configparser
30 except ImportError:
31 import ConfigParser as configparser
32
33 ###############################################################################
34 #
35 # pexpect 2.3 is inlined here because it's not in the Python library yet.
36 #
37 # SPDX-License-Identifier: ISC
38
39 """Pexpect is a Python module for spawning child applications and controlling
40 them automatically. Pexpect can be used for automating interactive applications
41 such as ssh, ftp, passwd, telnet, etc. It can be used to a automate setup
42 scripts for duplicating software package installations on different servers. It
43 can be used for automated software testing. Pexpect is in the spirit of Don
44 Libes' Expect, but Pexpect is pure Python. Other Expect-like modules for Python
45 require TCL and Expect or require C extensions to be compiled. Pexpect does not
46 use C, Expect, or TCL extensions. It should work on any platform that supports
47 the standard Python pty module. The Pexpect interface focuses on ease of use so
48 that simple tasks are easy.
49
50 There are two main interfaces to Pexpect -- the function, run() and the class,
51 spawn. You can call the run() function to execute a command and return the
52 output. This is a handy replacement for os.system().
53
54 For example::
55
56 pexpect.run('ls -la')
57
58 The more powerful interface is the spawn class. You can use this to spawn an
59 external child command and then interact with the child by sending lines and
60 expecting responses.
61
62 For example::
63
64 child = pexpect.spawn('scp foo myname@host.example.com:.')
65 child.expect ('Password:')
66 child.sendline (mypassword)
67
68 This works even for commands that ask for passwords or other input outside of
69 the normal stdio streams.
70
71 Credits: Noah Spurrier, Richard Holden, Marco Molteni, Kimberley Burchett,
72 Robert Stone, Hartmut Goebel, Chad Schroeder, Erick Tryzelaar, Dave Kirby, Ids
73 vander Molen, George Todd, Noel Taylor, Nicolas D. Cesar, Alexander Gattin,
74 Geoffrey Marshall, Francisco Lourenco, Glen Mabey, Karthik Gurusamy, Fernando
75 Perez, Corey Minyard, Jon Cohen, Guillaume Chazarain, Andrew Ryan, Nick
76 Craig-Wood, Andrew Stone, Jorgen Grahn (Let me know if I forgot anyone.)
77
78 Free, open source, and all that good stuff.
79
80 Permission is hereby granted, free of charge, to any person obtaining a copy of
81 this software and associated documentation files (the "Software"), to deal in
82 the Software without restriction, including without limitation the rights to
83 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
84 of the Software, and to permit persons to whom the Software is furnished to do
85 so, subject to the following conditions:
86
87 The above copyright notice and this permission notice shall be included in all
88 copies or substantial portions of the Software.
89
90 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
91 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
92 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
93 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
94 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
95 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
96 SOFTWARE.
97
98 Pexpect Copyright (c) 2008 Noah Spurrier
99 http://pexpect.sourceforge.net/
100
101 $Id: pexpect.py 507 2007-12-27 02:40:52Z noah $
102 """
103
104 try:
105 import os, sys, time
106 import select
107 import string
108 import re
109 import struct
110 import resource
111 import types
112 import pty
113 import tty
114 import termios
115 import fcntl
116 import errno
117 import traceback
118 import signal
119 except ImportError as e:
120 raise ImportError(str(e) + """
121
122 A critical module was not found. Probably this operating system does not
123 support it. Pexpect is intended for UNIX-like operating systems.""")
124
125 __version__ = '2.3'
126 __revision__ = '$Revision: 399 $'
127 __all__ = ['ExceptionPexpect', 'EOF', 'TIMEOUT', 'spawn', 'run', 'which',
128 'split_command_line', '__version__', '__revision__']
129
130 # Exception classes used by this module.
131 class ExceptionPexpect(Exception):
132
133 """Base class for all exceptions raised by this module.
134 """
135
136 def __init__(self, value):
137
138 Exception.__init__(self)
139 self.value = value
140
141 def __str__(self):
142
143 return str(self.value)
144
145 def get_trace(self):
146
147 """This returns an abbreviated stack trace with lines that only concern
148 the caller. In other words, the stack trace inside the Pexpect module
149 is not included. """
150
151 tblist = traceback.extract_tb(sys.exc_info()[2])
152 #tblist = filter(self.__filter_not_pexpect, tblist)
153 tblist = [item for item in tblist if self.__filter_not_pexpect(item)]
154 tblist = traceback.format_list(tblist)
155 return ''.join(tblist)
156
157 def __filter_not_pexpect(self, trace_list_item):
158
159 """This returns True if list item 0 the string 'pexpect.py' in it. """
160
161 if trace_list_item[0].find('pexpect.py') == -1:
162 return True
163 else:
164 return False
165
166 class EOF(ExceptionPexpect):
167
168 """Raised when EOF is read from a child. This usually means the child has exited."""
169
170 class TIMEOUT(ExceptionPexpect):
171
172 """Raised when a read time exceeds the timeout. """
173
174 ##class TIMEOUT_PATTERN(TIMEOUT):
175 ## """Raised when the pattern match time exceeds the timeout.
176 ## This is different than a read TIMEOUT because the child process may
177 ## give output, thus never give a TIMEOUT, but the output
178 ## may never match a pattern.
179 ## """
180 ##class MAXBUFFER(ExceptionPexpect):
181 ## """Raised when a scan buffer fills before matching an expected pattern."""
182
183 def run(command, timeout=-1, withexitstatus=False, events=None, _extra_args=None, logfile=None, cwd=None, env=None):
184
185 """
186 This function runs the given command; waits for it to finish; then
187 returns all output as a string. STDERR is included in output. If the full
188 path to the command is not given then the path is searched.
189
190 Note that lines are terminated by CR/LF (\\r\\n) combination even on
191 UNIX-like systems because this is the standard for pseudo ttys. If you set
192 'withexitstatus' to true, then run will return a tuple of (command_output,
193 exitstatus). If 'withexitstatus' is false then this returns just
194 command_output.
195
196 The run() function can often be used instead of creating a spawn instance.
197 For example, the following code uses spawn::
198
199 from pexpect import *
200 child = spawn('scp foo myname@host.example.com:.')
201 child.expect ('(?i)password')
202 child.sendline (mypassword)
203
204 The previous code can be replace with the following::
205
206 from pexpect import *
207 run ('scp foo myname@host.example.com:.', events={'(?i)password': mypassword})
208
209 Examples
210 ========
211
212 Start the apache daemon on the local machine::
213
214 from pexpect import *
215 run ("/usr/local/apache/bin/apachectl start")
216
217 Check in a file using SVN::
218
219 from pexpect import *
220 run ("svn ci -m 'automatic commit' my_file.py")
221
222 Run a command and capture exit status::
223
224 from pexpect import *
225 (command_output, exitstatus) = run ('ls -l /bin', withexitstatus=1)
226
227 Tricky Examples
228 ===============
229
230 The following will run SSH and execute 'ls -l' on the remote machine. The
231 password 'secret' will be sent if the '(?i)password' pattern is ever seen::
232
233 run ("ssh username@machine.example.com 'ls -l'", events={'(?i)password':'secret\\n'})
234
235 This will start mencoder to rip a video from DVD. This will also display
236 progress ticks every 5 seconds as it runs. For example::
237
238 from pexpect import *
239 def print_ticks(d):
240 print d['event_count'],
241 run ("mencoder dvd://1 -o video.avi -oac copy -ovc copy", events={TIMEOUT:print_ticks}, timeout=5)
242
243 The 'events' argument should be a dictionary of patterns and responses.
244 Whenever one of the patterns is seen in the command out run() will send the
245 associated response string. Note that you should put newlines in your
246 string if Enter is necessary. The responses may also contain callback
247 functions. Any callback is function that takes a dictionary as an argument.
248 The dictionary contains all the locals from the run() function, so you can
249 access the child spawn object or any other variable defined in run()
250 (event_count, child, and extra_args are the most useful). A callback may
251 return True to stop the current run process otherwise run() continues until
252 the next event. A callback may also return a string which will be sent to
253 the child. 'extra_args' is not used by directly run(). It provides a way to
254 pass data to a callback function through run() through the locals
255 dictionary passed to a callback. """
256
257 if timeout == -1:
258 child = spawn(command, maxread=2000, logfile=logfile, cwd=cwd, env=env)
259 else:
260 child = spawn(command, timeout=timeout, maxread=2000, logfile=logfile, cwd=cwd, env=env)
261 if events is not None:
262 patterns = list(events.keys())
263 responses = list(events.values())
264 else:
265 patterns = None # We assume that EOF or TIMEOUT will save us.
266 responses = None
267 child_result_list = []
268 event_count = 0
269 while 1:
270 try:
271 index = child.expect(patterns)
272 if type(child.after) in str:
273 child_result_list.append(child.before + child.after)
274 else: # child.after may have been a TIMEOUT or EOF, so don't cat those.
275 child_result_list.append(child.before)
276 if type(responses[index]) in str:
277 child.send(responses[index])
278 elif type(responses[index]) is types.FunctionType:
279 callback_result = responses[index](locals())
280 sys.stdout.flush()
281 if type(callback_result) in str:
282 child.send(callback_result)
283 elif callback_result:
284 break
285 else:
286 raise TypeError('The callback must be a string or function type.')
287 event_count = event_count + 1
288 except TIMEOUT as e:
289 child_result_list.append(child.before)
290 break
291 except EOF as e:
292 child_result_list.append(child.before)
293 break
294 child_result = ''.join(child_result_list)
295 if withexitstatus:
296 child.close()
297 return (child_result, child.exitstatus)
298 else:
299 return child_result
300
301 class spawn(object):
302
303 """This is the main class interface for Pexpect. Use this class to start
304 and control child applications. """
305
306 def __init__(self, command, args=None, timeout=30, maxread=2000, searchwindowsize=None, logfile=None, cwd=None, env=None):
307
308 """This is the constructor. The command parameter may be a string that
309 includes a command and any arguments to the command. For example::
310
311 child = pexpect.spawn ('/usr/bin/ftp')
312 child = pexpect.spawn ('/usr/bin/ssh user@example.com')
313 child = pexpect.spawn ('ls -latr /tmp')
314
315 You may also construct it with a list of arguments like so::
316
317 child = pexpect.spawn ('/usr/bin/ftp', [])
318 child = pexpect.spawn ('/usr/bin/ssh', ['user@example.com'])
319 child = pexpect.spawn ('ls', ['-latr', '/tmp'])
320
321 After this the child application will be created and will be ready to
322 talk to. For normal use, see expect() and send() and sendline().
323
324 Remember that Pexpect does NOT interpret shell meta characters such as
325 redirect, pipe, or wild cards (>, |, or *). This is a common mistake.
326 If you want to run a command and pipe it through another command then
327 you must also start a shell. For example::
328
329 child = pexpect.spawn('/bin/bash -c "ls -l | grep LOG > log_list.txt"')
330 child.expect(pexpect.EOF)
331
332 The second form of spawn (where you pass a list of arguments) is useful
333 in situations where you wish to spawn a command and pass it its own
334 argument list. This can make syntax more clear. For example, the
335 following is equivalent to the previous example::
336
337 shell_cmd = 'ls -l | grep LOG > log_list.txt'
338 child = pexpect.spawn('/bin/bash', ['-c', shell_cmd])
339 child.expect(pexpect.EOF)
340
341 The maxread attribute sets the read buffer size. This is maximum number
342 of bytes that Pexpect will try to read from a TTY at one time. Setting
343 the maxread size to 1 will turn off buffering. Setting the maxread
344 value higher may help performance in cases where large amounts of
345 output are read back from the child. This feature is useful in
346 conjunction with searchwindowsize.
347
348 The searchwindowsize attribute sets the how far back in the incomming
349 seach buffer Pexpect will search for pattern matches. Every time
350 Pexpect reads some data from the child it will append the data to the
351 incomming buffer. The default is to search from the beginning of the
352 imcomming buffer each time new data is read from the child. But this is
353 very inefficient if you are running a command that generates a large
354 amount of data where you want to match The searchwindowsize does not
355 effect the size of the incomming data buffer. You will still have
356 access to the full buffer after expect() returns.
357
358 The logfile member turns on or off logging. All input and output will
359 be copied to the given file object. Set logfile to None to stop
360 logging. This is the default. Set logfile to sys.stdout to echo
361 everything to standard output. The logfile is flushed after each write.
362
363 Example log input and output to a file::
364
365 child = pexpect.spawn('some_command')
366 fout = file('mylog.txt','w')
367 child.logfile = fout
368
369 Example log to stdout::
370
371 child = pexpect.spawn('some_command')
372 child.logfile = sys.stdout
373
374 The logfile_read and logfile_send members can be used to separately log
375 the input from the child and output sent to the child. Sometimes you
376 don't want to see everything you write to the child. You only want to
377 log what the child sends back. For example::
378
379 child = pexpect.spawn('some_command')
380 child.logfile_read = sys.stdout
381
382 To separately log output sent to the child use logfile_send::
383
384 self.logfile_send = fout
385
386 The delaybeforesend helps overcome a weird behavior that many users
387 were experiencing. The typical problem was that a user would expect() a
388 "Password:" prompt and then immediately call sendline() to send the
389 password. The user would then see that their password was echoed back
390 to them. Passwords don't normally echo. The problem is caused by the
391 fact that most applications print out the "Password" prompt and then
392 turn off stdin echo, but if you send your password before the
393 application turned off echo, then you get your password echoed.
394 Normally this wouldn't be a problem when interacting with a human at a
395 real keyboard. If you introduce a slight delay just before writing then
396 this seems to clear up the problem. This was such a common problem for
397 many users that I decided that the default pexpect behavior should be
398 to sleep just before writing to the child application. 1/20th of a
399 second (50 ms) seems to be enough to clear up the problem. You can set
400 delaybeforesend to 0 to return to the old behavior. Most Linux machines
401 don't like this to be below 0.03. I don't know why.
402
403 Note that spawn is clever about finding commands on your path.
404 It uses the same logic that "which" uses to find executables.
405
406 If you wish to get the exit status of the child you must call the
407 close() method. The exit or signal status of the child will be stored
408 in self.exitstatus or self.signalstatus. If the child exited normally
409 then exitstatus will store the exit return code and signalstatus will
410 be None. If the child was terminated abnormally with a signal then
411 signalstatus will store the signal value and exitstatus will be None.
412 If you need more detail you can also read the self.status member which
413 stores the status returned by os.waitpid. You can interpret this using
414 os.WIFEXITED/os.WEXITSTATUS or os.WIFSIGNALED/os.TERMSIG. """
415
416 self.STDIN_FILENO = pty.STDIN_FILENO
417 self.STDOUT_FILENO = pty.STDOUT_FILENO
418 self.STDERR_FILENO = pty.STDERR_FILENO
419 self.stdin = sys.stdin
420 self.stdout = sys.stdout
421 self.stderr = sys.stderr
422
423 self.searcher = None
424 self.ignorecase = False
425 self.before = None
426 self.after = None
427 self.match = None
428 self.match_index = None
429 self.terminated = True
430 self.exitstatus = None
431 self.signalstatus = None
432 self.status = None # status returned by os.waitpid
433 self.flag_eof = False
434 self.pid = None
435 self.child_fd = -1 # initially closed
436 self.timeout = timeout
437 self.delimiter = EOF
438 self.logfile = logfile
439 self.logfile_read = None # input from child (read_nonblocking)
440 self.logfile_send = None # output to send (send, sendline)
441 self.maxread = maxread # max bytes to read at one time into buffer
442 self.buffer = '' # This is the read buffer. See maxread.
443 self.searchwindowsize = searchwindowsize # Anything before searchwindowsize point is preserved, but not searched.
444 # Most Linux machines don't like delaybeforesend to be below 0.03 (30 ms).
445 self.delaybeforesend = 0.05 # Sets sleep time used just before sending data to child. Time in seconds.
446 self.delayafterclose = 0.1 # Sets delay in close() method to allow kernel time to update process status. Time in seconds.
447 self.delayafterterminate = 0.1 # Sets delay in terminate() method to allow kernel time to update process status. Time in seconds.
448 self.softspace = False # File-like object.
449 self.name = '<' + repr(self) + '>' # File-like object.
450 self.encoding = None # File-like object.
451 self.closed = True # File-like object.
452 self.cwd = cwd
453 self.env = env
454 self.__irix_hack = (sys.platform.lower().find('irix') >= 0) # This flags if we are running on irix
455 # Solaris uses internal __fork_pty(). All others use pty.fork().
456 if (sys.platform.lower().find('solaris') >= 0) or (sys.platform.lower().find('sunos5') >= 0):
457 self.use_native_pty_fork = False
458 else:
459 self.use_native_pty_fork = True
460
461
462 # allow dummy instances for subclasses that may not use command or args.
463 if command is None:
464 self.command = None
465 self.args = None
466 self.name = '<pexpect factory incomplete>'
467 else:
468 self._spawn(command, args)
469
470 def __del__(self):
471
472 """This makes sure that no system resources are left open. Python only
473 garbage collects Python objects. OS file descriptors are not Python
474 objects, so they must be handled explicitly. If the child file
475 descriptor was opened outside of this class (passed to the constructor)
476 then this does not close it. """
477
478 if not self.closed:
479 # It is possible for __del__ methods to execute during the
480 # teardown of the Python VM itself. Thus self.close() may
481 # trigger an exception because os.close may be None.
482 # -- Fernando Perez
483 try:
484 self.close()
485 except AttributeError:
486 pass
487
488 def __str__(self):
489
490 """This returns a human-readable string that represents the state of
491 the object. """
492
493 s = []
494 s.append(repr(self))
495 s.append('version: ' + __version__ + ' (' + __revision__ + ')')
496 s.append('command: ' + str(self.command))
497 s.append('args: ' + str(self.args))
498 s.append('searcher: ' + str(self.searcher))
499 s.append('buffer (last 100 chars): ' + str(self.buffer)[-100:])
500 s.append('before (last 100 chars): ' + str(self.before)[-100:])
501 s.append('after: ' + str(self.after))
502 s.append('match: ' + str(self.match))
503 s.append('match_index: ' + str(self.match_index))
504 s.append('exitstatus: ' + str(self.exitstatus))
505 s.append('flag_eof: ' + str(self.flag_eof))
506 s.append('pid: ' + str(self.pid))
507 s.append('child_fd: ' + str(self.child_fd))
508 s.append('closed: ' + str(self.closed))
509 s.append('timeout: ' + str(self.timeout))
510 s.append('delimiter: ' + str(self.delimiter))
511 s.append('logfile: ' + str(self.logfile))
512 s.append('logfile_read: ' + str(self.logfile_read))
513 s.append('logfile_send: ' + str(self.logfile_send))
514 s.append('maxread: ' + str(self.maxread))
515 s.append('ignorecase: ' + str(self.ignorecase))
516 s.append('searchwindowsize: ' + str(self.searchwindowsize))
517 s.append('delaybeforesend: ' + str(self.delaybeforesend))
518 s.append('delayafterclose: ' + str(self.delayafterclose))
519 s.append('delayafterterminate: ' + str(self.delayafterterminate))
520 return '\n'.join(s)
521
522 def _spawn(self, command, args=None):
523
524 """This starts the given command in a child process. This does all the
525 fork/exec type of stuff for a pty. This is called by __init__. If args
526 is empty then command will be parsed (split on spaces) and args will be
527 set to parsed arguments. """
528
529 # The pid and child_fd of this object get set by this method.
530 # Note that it is difficult for this method to fail.
531 # You cannot detect if the child process cannot start.
532 # So the only way you can tell if the child process started
533 # or not is to try to read from the file descriptor. If you get
534 # EOF immediately then it means that the child is already dead.
535 # That may not necessarily be bad because you may haved spawned a child
536 # that performs some task; creates no stdout output; and then dies.
537
538 # If command is an int type then it may represent a file descriptor.
539 if type(command) == type(0):
540 raise ExceptionPexpect('Command is an int type. If this is a file descriptor then maybe you want to use fdpexpect.fdspawn which takes an existing file descriptor instead of a command string.')
541
542 # Avoids dangerous default
543 if args is None:
544 args = []
545 if type(args) != type([]):
546 raise TypeError('The argument, args, must be a list.')
547
548 if args == []:
549 self.args = split_command_line(command)
550 self.command = self.args[0]
551 else:
552 self.args = args[:] # work with a copy
553 self.args.insert(0, command)
554 self.command = command
555
556 command_with_path = which(self.command)
557 if command_with_path is None:
558 raise ExceptionPexpect('The command was not found or was not executable: %s.' % self.command)
559 self.command = command_with_path
560 self.args[0] = self.command
561
562 self.name = '<' + ' '.join(self.args) + '>'
563
564 assert self.pid is None, 'The pid member should be None.'
565 assert self.command is not None, 'The command member should not be None.'
566
567 if self.use_native_pty_fork:
568 try:
569 self.pid, self.child_fd = pty.fork()
570 except OSError as e:
571 raise ExceptionPexpect('Error! pty.fork() failed: ' + str(e))
572 else: # Use internal __fork_pty
573 self.pid, self.child_fd = self.__fork_pty()
574
575 if self.pid == 0: # Child
576 try:
577 self.child_fd = sys.stdout.fileno() # used by setwinsize()
578 self.setwinsize(24, 80)
579 except:
580 # Some platforms do not like setwinsize (Cygwin).
581 # This will cause problem when running applications that
582 # are very picky about window size.
583 # This is a serious limitation, but not a show stopper.
584 pass
585 # Do not allow child to inherit open file descriptors from parent.
586 max_fd = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
587 for i in range(3, max_fd):
588 try:
589 os.close(i)
590 except OSError:
591 pass
592
593 # I don't know why this works, but ignoring SIGHUP fixes a
594 # problem when trying to start a Java daemon with sudo
595 # (specifically, Tomcat).
596 signal.signal(signal.SIGHUP, signal.SIG_IGN)
597
598 if self.cwd is not None:
599 os.chdir(self.cwd)
600 if self.env is None:
601 os.execv(self.command, self.args)
602 else:
603 os.execvpe(self.command, self.args, self.env)
604
605 # Parent
606 self.terminated = False
607 self.closed = False
608
609 def __fork_pty(self):
610
611 """This implements a substitute for the forkpty system call. This
612 should be more portable than the pty.fork() function. Specifically,
613 this should work on Solaris.
614
615 Modified 10.06.05 by Geoff Marshall: Implemented __fork_pty() method to
616 resolve the issue with Python's pty.fork() not supporting Solaris,
617 particularly ssh. Based on patch to posixmodule.c authored by Noah
618 Spurrier::
619
620 http://mail.python.org/pipermail/python-dev/2003-May/035281.html
621
622 """
623
624 parent_fd, child_fd = os.openpty()
625 if parent_fd < 0 or child_fd < 0:
626 raise ExceptionPexpect("Error! Could not open pty with os.openpty().")
627
628 pid = os.fork()
629 if pid < 0:
630 raise ExceptionPexpect("Error! Failed os.fork().")
631 elif pid == 0:
632 # Child.
633 os.close(parent_fd)
634 self.__pty_make_controlling_tty(child_fd)
635
636 os.dup2(child_fd, 0)
637 os.dup2(child_fd, 1)
638 os.dup2(child_fd, 2)
639
640 if child_fd > 2:
641 os.close(child_fd)
642 else:
643 # Parent.
644 os.close(child_fd)
645
646 return pid, parent_fd
647
648 def __pty_make_controlling_tty(self, tty_fd):
649
650 """This makes the pseudo-terminal the controlling tty. This should be
651 more portable than the pty.fork() function. Specifically, this should
652 work on Solaris. """
653
654 child_name = os.ttyname(tty_fd)
655
656 # Disconnect from controlling tty if still connected.
657 fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY)
658 if fd >= 0:
659 os.close(fd)
660
661 os.setsid()
662
663 # Verify we are disconnected from controlling tty
664 try:
665 fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY)
666 if fd >= 0:
667 os.close(fd)
668 raise ExceptionPexpect("Error! We are not disconnected from a controlling tty.")
669 except:
670 # Good! We are disconnected from a controlling tty.
671 pass
672
673 # Verify we can open child pty.
674 fd = os.open(child_name, os.O_RDWR)
675 if fd < 0:
676 raise ExceptionPexpect("Error! Could not open child pty, " + child_name)
677 else:
678 os.close(fd)
679
680 # Verify we now have a controlling tty.
681 fd = os.open("/dev/tty", os.O_WRONLY)
682 if fd < 0:
683 raise ExceptionPexpect("Error! Could not open controlling tty, /dev/tty")
684 else:
685 os.close(fd)
686
687 def fileno(self): # File-like object.
688
689 """This returns the file descriptor of the pty for the child.
690 """
691
692 return self.child_fd
693
694 def close(self, force=True): # File-like object.
695
696 """This closes the connection with the child application. Note that
697 calling close() more than once is valid. This emulates standard Python
698 behavior with files. Set force to True if you want to make sure that
699 the child is terminated (SIGKILL is sent if the child ignores SIGHUP
700 and SIGINT). """
701
702 if not self.closed:
703 self.flush()
704 os.close(self.child_fd)
705 time.sleep(self.delayafterclose) # Give kernel time to update process status.
706 if self.isalive():
707 if not self.terminate(force):
708 raise ExceptionPexpect('close() could not terminate the child using terminate()')
709 self.child_fd = -1
710 self.closed = True
711 #self.pid = None
712
713 def flush(self): # File-like object.
714
715 """This does nothing. It is here to support the interface for a
716 File-like object. """
717
718 pass
719
720 def isatty(self): # File-like object.
721
722 """This returns True if the file descriptor is open and connected to a
723 tty(-like) device, else False. """
724
725 return os.isatty(self.child_fd)
726
727 def waitnoecho(self, timeout=-1):
728
729 """This waits until the terminal ECHO flag is set False. This returns
730 True if the echo mode is off. This returns False if the ECHO flag was
731 not set False before the timeout. This can be used to detect when the
732 child is waiting for a password. Usually a child application will turn
733 off echo mode when it is waiting for the user to enter a password. For
734 example, instead of expecting the "password:" prompt you can wait for
735 the child to set ECHO off::
736
737 p = pexpect.spawn ('ssh user@example.com')
738 p.waitnoecho()
739 p.sendline(mypassword)
740
741 If timeout is None then this method to block forever until ECHO flag is
742 False.
743
744 """
745
746 if timeout == -1:
747 timeout = self.timeout
748 if timeout is not None:
749 end_time = time.time() + timeout
750 while True:
751 if not self.getecho():
752 return True
753 if timeout < 0 and timeout is not None:
754 return False
755 if timeout is not None:
756 timeout = end_time - time.time()
757 time.sleep(0.1)
758
759 def getecho(self):
760
761 """This returns the terminal echo mode. This returns True if echo is
762 on or False if echo is off. Child applications that are expecting you
763 to enter a password often set ECHO False. See waitnoecho(). """
764
765 attr = termios.tcgetattr(self.child_fd)
766 if attr[3] & termios.ECHO:
767 return True
768 return False
769
770 def setecho(self, state):
771
772 """This sets the terminal echo mode on or off. Note that anything the
773 child sent before the echo will be lost, so you should be sure that
774 your input buffer is empty before you call setecho(). For example, the
775 following will work as expected::
776
777 p = pexpect.spawn('cat')
778 p.sendline ('1234') # We will see this twice (once from tty echo and again from cat).
779 p.expect (['1234'])
780 p.expect (['1234'])
781 p.setecho(False) # Turn off tty echo
782 p.sendline ('abcd') # We will set this only once (echoed by cat).
783 p.sendline ('wxyz') # We will set this only once (echoed by cat)
784 p.expect (['abcd'])
785 p.expect (['wxyz'])
786
787 The following WILL NOT WORK because the lines sent before the setecho
788 will be lost::
789
790 p = pexpect.spawn('cat')
791 p.sendline ('1234') # We will see this twice (once from tty echo and again from cat).
792 p.setecho(False) # Turn off tty echo
793 p.sendline ('abcd') # We will set this only once (echoed by cat).
794 p.sendline ('wxyz') # We will set this only once (echoed by cat)
795 p.expect (['1234'])
796 p.expect (['1234'])
797 p.expect (['abcd'])
798 p.expect (['wxyz'])
799 """
800
801 attr = termios.tcgetattr(self.child_fd)
802 if state:
803 attr[3] = attr[3] | termios.ECHO
804 else:
805 attr[3] = attr[3] & ~termios.ECHO
806 # I tried TCSADRAIN and TCSAFLUSH, but these were inconsistent
807 # and blocked on some platforms. TCSADRAIN is probably ideal if it worked.
808 termios.tcsetattr(self.child_fd, termios.TCSANOW, attr)
809
810 def read_nonblocking(self, size=1, timeout=-1):
811
812 """This reads at most size characters from the child application. It
813 includes a timeout. If the read does not complete within the timeout
814 period then a TIMEOUT exception is raised. If the end of file is read
815 then an EOF exception will be raised. If a log file was set using
816 setlog() then all data will also be written to the log file.
817
818 If timeout is None then the read may block indefinitely. If timeout is -1
819 then the self.timeout value is used. If timeout is 0 then the child is
820 polled and if there was no data immediately ready then this will raise
821 a TIMEOUT exception.
822
823 The timeout refers only to the amount of time to read at least one
824 character. This is not effected by the 'size' parameter, so if you call
825 read_nonblocking(size=100, timeout=30) and only one character is
826 available right away then one character will be returned immediately.
827 It will not wait for 30 seconds for another 99 characters to come in.
828
829 This is a wrapper around os.read(). It uses select.select() to
830 implement the timeout. """
831
832 if self.closed:
833 raise ValueError('I/O operation on closed file in read_nonblocking().')
834
835 if timeout == -1:
836 timeout = self.timeout
837
838 # Note that some systems such as Solaris do not give an EOF when
839 # the child dies. In fact, you can still try to read
840 # from the child_fd -- it will block forever or until TIMEOUT.
841 # For this case, I test isalive() before doing any reading.
842 # If isalive() is false, then I pretend that this is the same as EOF.
843 if not self.isalive():
844 r, w, e = self.__select([self.child_fd], [], [], 0) # timeout of 0 means "poll"
845 if not r:
846 self.flag_eof = True
847 raise EOF('End Of File (EOF) in read_nonblocking(). Braindead platform.')
848 elif self.__irix_hack:
849 # This is a hack for Irix. It seems that Irix requires a long delay before checking isalive.
850 # This adds a 2 second delay, but only when the child is terminated.
851 r, w, e = self.__select([self.child_fd], [], [], 2)
852 if not r and not self.isalive():
853 self.flag_eof = True
854 raise EOF('End Of File (EOF) in read_nonblocking(). Pokey platform.')
855
856 r, w, e = self.__select([self.child_fd], [], [], timeout)
857
858 if not r:
859 if not self.isalive():
860 # Some platforms, such as Irix, will claim that their processes are alive;
861 # then timeout on the select; and then finally admit that they are not alive.
862 self.flag_eof = True
863 raise EOF('End of File (EOF) in read_nonblocking(). Very pokey platform.')
864 else:
865 raise TIMEOUT('Timeout exceeded in read_nonblocking().')
866
867 if self.child_fd in r:
868 try:
869 s = os.read(self.child_fd, size)
870 except OSError as e: # Linux does this
871 self.flag_eof = True
872 raise EOF('End Of File (EOF) in read_nonblocking(). Exception style platform.')
873 if s == '': # BSD style
874 self.flag_eof = True
875 raise EOF('End Of File (EOF) in read_nonblocking(). Empty string style platform.')
876
877 if self.logfile is not None:
878 self.logfile.write(s)
879 self.logfile.flush()
880 if self.logfile_read is not None:
881 self.logfile_read.write(s)
882 self.logfile_read.flush()
883
884 return s
885
886 raise ExceptionPexpect('Reached an unexpected state in read_nonblocking().')
887
888 def read(self, size=-1): # File-like object.
889
890 """This reads at most "size" bytes from the file (less if the read hits
891 EOF before obtaining size bytes). If the size argument is negative or
892 omitted, read all data until EOF is reached. The bytes are returned as
893 a string object. An empty string is returned when EOF is encountered
894 immediately. """
895
896 if size == 0:
897 return ''
898 if size < 0:
899 self.expect(self.delimiter) # delimiter default is EOF
900 return self.before
901
902 # I could have done this more directly by not using expect(), but
903 # I deliberately decided to couple read() to expect() so that
904 # I would catch any bugs early and ensure consistant behavior.
905 # It's a little less efficient, but there is less for me to
906 # worry about if I have to later modify read() or expect().
907 # Note, it's OK if size==-1 in the regex. That just means it
908 # will never match anything in which case we stop only on EOF.
909 cre = re.compile('.{%d}' % size, re.DOTALL)
910 index = self.expect([cre, self.delimiter]) # delimiter default is EOF
911 if index == 0:
912 return self.after ### self.before should be ''. Should I assert this?
913 return self.before
914
915 def readline(self, size=-1): # File-like object.
916
917 """This reads and returns one entire line. A trailing newline is kept
918 in the string, but may be absent when a file ends with an incomplete
919 line. Note: This readline() looks for a \\r\\n pair even on UNIX
920 because this is what the pseudo tty device returns. So contrary to what
921 you may expect you will receive the newline as \\r\\n. An empty string
922 is returned when EOF is hit immediately. Currently, the size argument is
923 mostly ignored, so this behavior is not standard for a file-like
924 object. If size is 0 then an empty string is returned. """
925
926 if size == 0:
927 return ''
928 index = self.expect(['\r\n', self.delimiter]) # delimiter default is EOF
929 if index == 0:
930 return self.before + '\r\n'
931 else:
932 return self.before
933
934 def readlines(self, _sizehint=-1): # File-like object.
935
936 """This reads until EOF using readline() and returns a list containing
937 the lines thus read. The optional "sizehint" argument is ignored. """
938
939 lines = []
940 while True:
941 line = self.readline()
942 if not line:
943 break
944 lines.append(line)
945 return lines
946
947 def write(self, s): # File-like object.
948
949 """This is similar to send() except that there is no return value.
950 """
951
952 self.send(s)
953
954 def writelines(self, sequence): # File-like object.
955
956 """This calls write() for each element in the sequence. The sequence
957 can be any iterable object producing strings, typically a list of
958 strings. This does not add line separators There is no return value.
959 """
960
961 for s in sequence:
962 self.write(s)
963
964 def send(self, s):
965
966 """This sends a string to the child process. This returns the number of
967 bytes written. If a log file was set then the data is also written to
968 the log. """
969
970 time.sleep(self.delaybeforesend)
971 if self.logfile is not None:
972 self.logfile.write(s)
973 self.logfile.flush()
974 if self.logfile_send is not None:
975 self.logfile_send.write(s)
976 self.logfile_send.flush()
977 c = os.write(self.child_fd, s.encode('utf-8'))
978 return c
979
980 def sendline(self, s=''):
981
982 """This is like send(), but it adds a line feed (os.linesep). This
983 returns the number of bytes written. """
984
985 n = self.send(s)
986 n = n + self.send(os.linesep)
987 return n
988
989 def sendcontrol(self, char):
990
991 """This sends a control character to the child such as Ctrl-C or
992 Ctrl-D. For example, to send a Ctrl-G (ASCII 7)::
993
994 child.sendcontrol('g')
995
996 See also, sendintr() and sendeof().
997 """
998
999 char = char.lower()
1000 a = ord(char)
1001 if a >= 97 and a <= 122:
1002 a = a - ord('a') + 1
1003 return self.send(chr(a))
1004 d = {'@':0, '`':0,
1005 '[':27, '{':27,
1006 '\\':28, '|':28,
1007 ']':29, '}': 29,
1008 '^':30, '~':30,
1009 '_':31,
1010 '?':127}
1011 if char not in d:
1012 return 0
1013 return self.send(chr(d[char]))
1014
1015 def sendeof(self):
1016
1017 """This sends an EOF to the child. This sends a character which causes
1018 the pending parent output buffer to be sent to the waiting child
1019 program without waiting for end-of-line. If it is the first character
1020 of the line, the read() in the user program returns 0, which signifies
1021 end-of-file. This means to work as expected a sendeof() has to be
1022 called at the beginning of a line. This method does not send a newline.
1023 It is the responsibility of the caller to ensure the eof is sent at the
1024 beginning of a line. """
1025
1026 ### Hmmm... how do I send an EOF?
1027 ###C if ((m = write(pty, *buf, p - *buf)) < 0)
1028 ###C return (errno == EWOULDBLOCK) ? n : -1;
1029 #fd = sys.stdin.fileno()
1030 #old = termios.tcgetattr(fd) # remember current state
1031 #attr = termios.tcgetattr(fd)
1032 #attr[3] = attr[3] | termios.ICANON # ICANON must be set to recognize EOF
1033 #try: # use try/finally to ensure state gets restored
1034 # termios.tcsetattr(fd, termios.TCSADRAIN, attr)
1035 # if hasattr(termios, 'CEOF'):
1036 # os.write (self.child_fd, '%c' % termios.CEOF)
1037 # else:
1038 # # Silly platform does not define CEOF so assume CTRL-D
1039 # os.write (self.child_fd, '%c' % 4)
1040 #finally: # restore state
1041 # termios.tcsetattr(fd, termios.TCSADRAIN, old)
1042 if hasattr(termios, 'VEOF'):
1043 char = termios.tcgetattr(self.child_fd)[6][termios.VEOF]
1044 else:
1045 # platform does not define VEOF so assume CTRL-D
1046 char = chr(4)
1047 self.send(char)
1048
1049 def sendintr(self):
1050
1051 """This sends a SIGINT to the child. It does not require
1052 the SIGINT to be the first character on a line. """
1053
1054 if hasattr(termios, 'VINTR'):
1055 char = termios.tcgetattr(self.child_fd)[6][termios.VINTR]
1056 else:
1057 # platform does not define VINTR so assume CTRL-C
1058 char = chr(3)
1059 self.send(char)
1060
1061 def eof(self):
1062
1063 """This returns True if the EOF exception was ever raised.
1064 """
1065
1066 return self.flag_eof
1067
1068 def terminate(self, force=False):
1069
1070 """This forces a child process to terminate. It starts nicely with
1071 SIGHUP and SIGINT. If "force" is True then moves onto SIGKILL. This
1072 returns True if the child was terminated. This returns False if the
1073 child could not be terminated. """
1074
1075 if not self.isalive():
1076 return True
1077 try:
1078 self.kill(signal.SIGHUP)
1079 time.sleep(self.delayafterterminate)
1080 if not self.isalive():
1081 return True
1082 self.kill(signal.SIGCONT)
1083 time.sleep(self.delayafterterminate)
1084 if not self.isalive():
1085 return True
1086 self.kill(signal.SIGINT)
1087 time.sleep(self.delayafterterminate)
1088 if not self.isalive():
1089 return True
1090 if force:
1091 self.kill(signal.SIGKILL)
1092 time.sleep(self.delayafterterminate)
1093 if not self.isalive():
1094 return True
1095 else:
1096 return False
1097 return False
1098 except OSError as e:
1099 # I think there are kernel timing issues that sometimes cause
1100 # this to happen. I think isalive() reports True, but the
1101 # process is dead to the kernel.
1102 # Make one last attempt to see if the kernel is up to date.
1103 time.sleep(self.delayafterterminate)
1104 if not self.isalive():
1105 return True
1106 else:
1107 return False
1108
1109 def wait(self):
1110
1111 """This waits until the child exits. This is a blocking call. This will
1112 not read any data from the child, so this will block forever if the
1113 child has unread output and has terminated. In other words, the child
1114 may have printed output then called exit(); but, technically, the child
1115 is still alive until its output is read. """
1116
1117 if self.isalive():
1118 pid, status = os.waitpid(self.pid, 0)
1119 else:
1120 raise ExceptionPexpect('Cannot wait for dead child process.')
1121 self.exitstatus = os.WEXITSTATUS(status)
1122 if os.WIFEXITED(status):
1123 self.status = status
1124 self.exitstatus = os.WEXITSTATUS(status)
1125 self.signalstatus = None
1126 self.terminated = True
1127 elif os.WIFSIGNALED(status):
1128 self.status = status
1129 self.exitstatus = None
1130 self.signalstatus = os.WTERMSIG(status)
1131 self.terminated = True
1132 elif os.WIFSTOPPED(status):
1133 raise ExceptionPexpect('Wait was called for a child process that is stopped. This is not supported. Is some other process attempting job control with our child pid?')
1134 return self.exitstatus
1135
1136 def isalive(self):
1137
1138 """This tests if the child process is running or not. This is
1139 non-blocking. If the child was terminated then this will read the
1140 exitstatus or signalstatus of the child. This returns True if the child
1141 process appears to be running or False if not. It can take literally
1142 SECONDS for Solaris to return the right status. """
1143
1144 if self.terminated:
1145 return False
1146
1147 if self.flag_eof:
1148 # This is for Linux, which requires the blocking form of waitpid to get
1149 # status of a defunct process. This is super-lame. The flag_eof would have
1150 # been set in read_nonblocking(), so this should be safe.
1151 waitpid_options = 0
1152 else:
1153 waitpid_options = os.WNOHANG
1154
1155 try:
1156 pid, status = os.waitpid(self.pid, waitpid_options)
1157 except OSError as e: # No child processes
1158 if e.errno == errno.ECHILD:
1159 raise ExceptionPexpect('isalive() encountered condition where "terminated" is 0, but there was no child process. Did someone else call waitpid() on our process?')
1160 else:
1161 raise e
1162
1163 # I have to do this twice for Solaris. I can't even believe that I figured this out...
1164 # If waitpid() returns 0 it means that no child process wishes to
1165 # report, and the value of status is undefined.
1166 if pid == 0:
1167 try:
1168 pid, status = os.waitpid(self.pid, waitpid_options) ### os.WNOHANG) # Solaris!
1169 except OSError as e: # This should never happen...
1170 if e.errno == errno.ECHILD:
1171 raise ExceptionPexpect('isalive() encountered condition that should never happen. There was no child process. Did someone else call waitpid() on our process?')
1172 else:
1173 raise e
1174
1175 # If pid is still 0 after two calls to waitpid() then
1176 # the process really is alive. This seems to work on all platforms, except
1177 # for Irix which seems to require a blocking call on waitpid or select, so I let read_nonblocking
1178 # take care of this situation(unfortunately, this requires waiting through the timeout).
1179 if pid == 0:
1180 return True
1181
1182 if pid == 0:
1183 return True
1184
1185 if os.WIFEXITED(status):
1186 self.status = status
1187 self.exitstatus = os.WEXITSTATUS(status)
1188 self.signalstatus = None
1189 self.terminated = True
1190 elif os.WIFSIGNALED(status):
1191 self.status = status
1192 self.exitstatus = None
1193 self.signalstatus = os.WTERMSIG(status)
1194 self.terminated = True
1195 elif os.WIFSTOPPED(status):
1196 raise ExceptionPexpect('isalive() encountered condition where child process is stopped. This is not supported. Is some other process attempting job control with our child pid?')
1197 return False
1198
1199 def kill(self, sig):
1200
1201 """This sends the given signal to the child application. In keeping
1202 with UNIX tradition it has a misleading name. It does not necessarily
1203 kill the child unless you send the right signal. """
1204
1205 # Same as os.kill, but the pid is given for you.
1206 if self.isalive():
1207 os.kill(self.pid, sig)
1208
1209 def compile_pattern_list(self, patterns):
1210
1211 """This compiles a pattern-string or a list of pattern-strings.
1212 Patterns must be a StringType, EOF, TIMEOUT, SRE_Pattern, or a list of
1213 those. Patterns may also be None which results in an empty list (you
1214 might do this if waiting for an EOF or TIMEOUT condition without
1215 expecting any pattern).
1216
1217 This is used by expect() when calling expect_list(). Thus expect() is
1218 nothing more than::
1219
1220 cpl = self.compile_pattern_list(pl)
1221 return self.expect_list(cpl, timeout)
1222
1223 If you are using expect() within a loop it may be more
1224 efficient to compile the patterns first and then call expect_list().
1225 This avoid calls in a loop to compile_pattern_list()::
1226
1227 cpl = self.compile_pattern_list(my_pattern)
1228 while some_condition:
1229 ...
1230 i = self.expect_list(clp, timeout)
1231 ...
1232 """
1233
1234 if patterns is None:
1235 return []
1236 if type(patterns) is not list:
1237 patterns = [patterns]
1238
1239 compile_flags = re.DOTALL # Allow dot to match \n
1240 if self.ignorecase:
1241 compile_flags = compile_flags | re.IGNORECASE
1242 compiled_pattern_list = []
1243 for p in patterns:
1244 if type(p) == str:
1245 compiled_pattern_list.append(re.compile(p, compile_flags))
1246 elif p is EOF:
1247 compiled_pattern_list.append(EOF)
1248 elif p is TIMEOUT:
1249 compiled_pattern_list.append(TIMEOUT)
1250 elif type(p) is type(re.compile('')):
1251 compiled_pattern_list.append(p)
1252 else:
1253 raise TypeError('Argument must be one of StringTypes, EOF, TIMEOUT, SRE_Pattern, or a list of those type. %s' % str(type(p)))
1254
1255 return compiled_pattern_list
1256
1257 def expect(self, pattern, timeout=-1, searchwindowsize=None):
1258
1259 """This seeks through the stream until a pattern is matched. The
1260 pattern is overloaded and may take several types. The pattern can be a
1261 StringType, EOF, a compiled re, or a list of any of those types.
1262 Strings will be compiled to re types. This returns the index into the
1263 pattern list. If the pattern was not a list this returns index 0 on a
1264 successful match. This may raise exceptions for EOF or TIMEOUT. To
1265 avoid the EOF or TIMEOUT exceptions add EOF or TIMEOUT to the pattern
1266 list. That will cause expect to match an EOF or TIMEOUT condition
1267 instead of raising an exception.
1268
1269 If you pass a list of patterns and more than one matches, the first match
1270 in the stream is chosen. If more than one pattern matches at that point,
1271 the leftmost in the pattern list is chosen. For example::
1272
1273 # the input is 'foobar'
1274 index = p.expect(['bar', 'foo', 'foobar'])
1275 # returns 1 ('foo') even though 'foobar' is a "better" match
1276
1277 Please note, however, that buffering can affect this behavior, since
1278 input arrives in unpredictable chunks. For example::
1279
1280 # the input is 'foobar'
1281 index = p.expect(['foobar', 'foo'])
1282 # returns 0 ('foobar') if all input is available at once,
1283 # but returs 1 ('foo') if parts of the final 'bar' arrive late
1284
1285 After a match is found the instance attributes 'before', 'after' and
1286 'match' will be set. You can see all the data read before the match in
1287 'before'. You can see the data that was matched in 'after'. The
1288 re.MatchObject used in the re match will be in 'match'. If an error
1289 occurred then 'before' will be set to all the data read so far and
1290 'after' and 'match' will be None.
1291
1292 If timeout is -1 then timeout will be set to the self.timeout value.
1293
1294 A list entry may be EOF or TIMEOUT instead of a string. This will
1295 catch these exceptions and return the index of the list entry instead
1296 of raising the exception. The attribute 'after' will be set to the
1297 exception type. The attribute 'match' will be None. This allows you to
1298 write code like this::
1299
1300 index = p.expect(['good', 'bad', pexpect.EOF, pexpect.TIMEOUT])
1301 if index == 0:
1302 do_something()
1303 elif index == 1:
1304 do_something_else()
1305 elif index == 2:
1306 do_some_other_thing()
1307 elif index == 3:
1308 do_something_completely_different()
1309
1310 instead of code like this::
1311
1312 try:
1313 index = p.expect(['good', 'bad'])
1314 if index == 0:
1315 do_something()
1316 elif index == 1:
1317 do_something_else()
1318 except EOF:
1319 do_some_other_thing()
1320 except TIMEOUT:
1321 do_something_completely_different()
1322
1323 These two forms are equivalent. It all depends on what you want. You
1324 can also just expect the EOF if you are waiting for all output of a
1325 child to finish. For example::
1326
1327 p = pexpect.spawn('/bin/ls')
1328 p.expect(pexpect.EOF)
1329 print p.before
1330
1331 If you are trying to optimize for speed then see expect_list().
1332 """
1333
1334 compiled_pattern_list = self.compile_pattern_list(pattern)
1335 return self.expect_list(compiled_pattern_list, timeout, searchwindowsize)
1336
1337 def expect_list(self, pattern_list, timeout=-1, searchwindowsize=-1):
1338
1339 """This takes a list of compiled regular expressions and returns the
1340 index into the pattern_list that matched the child output. The list may
1341 also contain EOF or TIMEOUT (which are not compiled regular
1342 expressions). This method is similar to the expect() method except that
1343 expect_list() does not recompile the pattern list on every call. This
1344 may help if you are trying to optimize for speed, otherwise just use
1345 the expect() method. This is called by expect(). If timeout==-1 then
1346 the self.timeout value is used. If searchwindowsize==-1 then the
1347 self.searchwindowsize value is used. """
1348
1349 return self.expect_loop(searcher_re(pattern_list), timeout, searchwindowsize)
1350
1351 def expect_exact(self, pattern_list, timeout=-1, searchwindowsize=-1):
1352
1353 """This is similar to expect(), but uses plain string matching instead
1354 of compiled regular expressions in 'pattern_list'. The 'pattern_list'
1355 may be a string; a list or other sequence of strings; or TIMEOUT and
1356 EOF.
1357
1358 This call might be faster than expect() for two reasons: string
1359 searching is faster than RE matching and it is possible to limit the
1360 search to just the end of the input buffer.
1361
1362 This method is also useful when you don't want to have to worry about
1363 escaping regular expression characters that you want to match."""
1364
1365 if type(pattern_list) in str or pattern_list in (TIMEOUT, EOF):
1366 pattern_list = [pattern_list]
1367 return self.expect_loop(searcher_string(pattern_list), timeout, searchwindowsize)
1368
1369 def expect_loop(self, searcher, timeout=-1, searchwindowsize=-1):
1370
1371 """This is the common loop used inside expect. The 'searcher' should be
1372 an instance of searcher_re or searcher_string, which describes how and what
1373 to search for in the input.
1374
1375 See expect() for other arguments, return value and exceptions. """
1376
1377 self.searcher = searcher
1378
1379 if timeout == -1:
1380 timeout = self.timeout
1381 if timeout is not None:
1382 end_time = time.time() + timeout
1383 if searchwindowsize == -1:
1384 searchwindowsize = self.searchwindowsize
1385
1386 try:
1387 incoming = self.buffer
1388 freshlen = len(incoming)
1389 while True: # Keep reading until exception or return.
1390 index = searcher.search(incoming, freshlen, searchwindowsize)
1391 if index >= 0:
1392 self.buffer = incoming[searcher.end : ]
1393 self.before = incoming[ : searcher.start]
1394 self.after = incoming[searcher.start : searcher.end]
1395 self.match = searcher.match
1396 self.match_index = index
1397 return self.match_index
1398 # No match at this point
1399 if timeout < 0 and timeout is not None:
1400 raise TIMEOUT('Timeout exceeded in expect_any().')
1401 # Still have time left, so read more data
1402 c = self.read_nonblocking(self.maxread, timeout)
1403 freshlen = len(c)
1404 time.sleep(0.0001)
1405 incoming = incoming + c.decode('utf-8')
1406 if timeout is not None:
1407 timeout = end_time - time.time()
1408 except EOF as e:
1409 self.buffer = ''
1410 self.before = incoming
1411 self.after = EOF
1412 index = searcher.eof_index
1413 if index >= 0:
1414 self.match = EOF
1415 self.match_index = index
1416 return self.match_index
1417 else:
1418 self.match = None
1419 self.match_index = None
1420 raise EOF(str(e) + '\n' + str(self))
1421 except TIMEOUT as e:
1422 self.buffer = incoming
1423 self.before = incoming
1424 self.after = TIMEOUT
1425 index = searcher.timeout_index
1426 if index >= 0:
1427 self.match = TIMEOUT
1428 self.match_index = index
1429 return self.match_index
1430 else:
1431 self.match = None
1432 self.match_index = None
1433 raise TIMEOUT(str(e) + '\n' + str(self))
1434 except:
1435 self.before = incoming
1436 self.after = None
1437 self.match = None
1438 self.match_index = None
1439 raise
1440
1441 def getwinsize(self):
1442
1443 """This returns the terminal window size of the child tty. The return
1444 value is a tuple of (rows, cols). """
1445
1446 TIOCGWINSZ = getattr(termios, 'TIOCGWINSZ', 1074295912)
1447 s = struct.pack('HHHH', 0, 0, 0, 0)
1448 x = fcntl.ioctl(self.fileno(), TIOCGWINSZ, s)
1449 return struct.unpack('HHHH', x)[0:2]
1450
1451 def setwinsize(self, r, c):
1452
1453 """This sets the terminal window size of the child tty. This will cause
1454 a SIGWINCH signal to be sent to the child. This does not change the
1455 physical window size. It changes the size reported to TTY-aware
1456 applications like vi or curses -- applications that respond to the
1457 SIGWINCH signal. """
1458
1459 # Check for buggy platforms. Some Python versions on some platforms
1460 # (notably OSF1 Alpha and RedHat 7.1) truncate the value for
1461 # termios.TIOCSWINSZ. It is not clear why this happens.
1462 # These platforms don't seem to handle the signed int very well;
1463 # yet other platforms like OpenBSD have a large negative value for
1464 # TIOCSWINSZ and they don't have a truncate problem.
1465 # Newer versions of Linux have totally different values for TIOCSWINSZ.
1466 # Note that this fix is a hack.
1467 TIOCSWINSZ = getattr(termios, 'TIOCSWINSZ', -2146929561)
1468 if TIOCSWINSZ == 2148037735: # L is not required in Python >= 2.2.
1469 TIOCSWINSZ = -2146929561 # Same bits, but with sign.
1470 # Note, assume ws_xpixel and ws_ypixel are zero.
1471 s = struct.pack('HHHH', r, c, 0, 0)
1472 fcntl.ioctl(self.fileno(), TIOCSWINSZ, s)
1473
1474 def interact(self, escape_character=chr(29), input_filter=None, output_filter=None):
1475
1476 """This gives control of the child process to the interactive user (the
1477 human at the keyboard). Keystrokes are sent to the child process, and
1478 the stdout and stderr output of the child process is printed. This
1479 simply echos the child stdout and child stderr to the real stdout and
1480 it echos the real stdin to the child stdin. When the user types the
1481 escape_character this method will stop. The default for
1482 escape_character is ^]. This should not be confused with ASCII 27 --
1483 the ESC character. ASCII 29 was chosen for historical merit because
1484 this is the character used by 'telnet' as the escape character. The
1485 escape_character will not be sent to the child process.
1486
1487 You may pass in optional input and output filter functions. These
1488 functions should take a string and return a string. The output_filter
1489 will be passed all the output from the child process. The input_filter
1490 will be passed all the keyboard input from the user. The input_filter
1491 is run BEFORE the check for the escape_character.
1492
1493 Note that if you change the window size of the parent the SIGWINCH
1494 signal will not be passed through to the child. If you want the child
1495 window size to change when the parent's window size changes then do
1496 something like the following example::
1497
1498 import pexpect, struct, fcntl, termios, signal, sys
1499 def sigwinch_passthrough(sig, data):
1500 s = struct.pack("HHHH", 0, 0, 0, 0)
1501 a = struct.unpack('hhhh', fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ , s))
1502 global p
1503 p.setwinsize(a[0],a[1])
1504 p = pexpect.spawn('/bin/bash') # Note this is global and used in sigwinch_passthrough.
1505 signal.signal(signal.SIGWINCH, sigwinch_passthrough)
1506 p.interact()
1507 """
1508
1509 # Flush the buffer.
1510 self.stdout.write(self.buffer)
1511 self.stdout.flush()
1512 self.buffer = ''
1513 mode = tty.tcgetattr(self.STDIN_FILENO)
1514 tty.setraw(self.STDIN_FILENO)
1515 try:
1516 self.__interact_copy(escape_character, input_filter, output_filter)
1517 finally:
1518 tty.tcsetattr(self.STDIN_FILENO, tty.TCSAFLUSH, mode)
1519
1520 def __interact_writen(self, fd, data):
1521
1522 """This is used by the interact() method.
1523 """
1524
1525 while data != '' and self.isalive():
1526 n = os.write(fd, data)
1527 data = data[n:]
1528
1529 def __interact_read(self, fd):
1530
1531 """This is used by the interact() method.
1532 """
1533
1534 return os.read(fd, 1000)
1535
1536 def __interact_copy(self, escape_character=None, input_filter=None, output_filter=None):
1537
1538 """This is used by the interact() method.
1539 """
1540
1541 while self.isalive():
1542 r, w, e = self.__select([self.child_fd, self.STDIN_FILENO], [], [])
1543 if self.child_fd in r:
1544 data = self.__interact_read(self.child_fd)
1545 if output_filter:
1546 data = output_filter(data)
1547 if self.logfile is not None:
1548 self.logfile.write(data)
1549 self.logfile.flush()
1550 os.write(self.STDOUT_FILENO, data)
1551 if self.STDIN_FILENO in r:
1552 data = self.__interact_read(self.STDIN_FILENO)
1553 if input_filter:
1554 data = input_filter(data)
1555 i = data.rfind(escape_character)
1556 if i != -1:
1557 data = data[:i]
1558 self.__interact_writen(self.child_fd, data)
1559 break
1560 self.__interact_writen(self.child_fd, data)
1561
1562 def __select(self, iwtd, owtd, ewtd, timeout=None):
1563
1564 """This is a wrapper around select.select() that ignores signals. If
1565 select.select raises a select.error exception and errno is an EINTR
1566 error then it is ignored. Mainly this is used to ignore sigwinch
1567 (terminal resize). """
1568
1569 # if select() is interrupted by a signal (errno==EINTR) then
1570 # we loop back and enter the select() again.
1571 if timeout is not None:
1572 end_time = time.time() + timeout
1573 while True:
1574 try:
1575 return select.select(iwtd, owtd, ewtd, timeout)
1576 except select.error as e:
1577 if e.errno == errno.EINTR:
1578 # if we loop back we have to subtract the amount of time we already waited.
1579 if timeout is not None:
1580 timeout = end_time - time.time()
1581 if timeout < 0:
1582 return([], [], [])
1583 else: # something else caused the select.error, so this really is an exception
1584 raise
1585
1586 ##############################################################################
1587 # The following methods are no longer supported or allowed.
1588
1589 def setmaxread(self, _maxread):
1590
1591 """This method is no longer supported or allowed. I don't like getters
1592 and setters without a good reason. """
1593
1594 raise ExceptionPexpect('This method is no longer supported or allowed. Just assign a value to the maxread member variable.')
1595
1596 def setlog(self, _fileobject):
1597
1598 """This method is no longer supported or allowed.
1599 """
1600
1601 raise ExceptionPexpect('This method is no longer supported or allowed. Just assign a value to the logfile member variable.')
1602
1603 ##############################################################################
1604 # End of spawn class
1605 ##############################################################################
1606
1607 class searcher_string(object):
1608
1609 """This is a plain string search helper for the spawn.expect_any() method.
1610
1611 Attributes:
1612
1613 eof_index - index of EOF, or -1
1614 timeout_index - index of TIMEOUT, or -1
1615
1616 After a successful match by the search() method the following attributes
1617 are available:
1618
1619 start - index into the buffer, first byte of match
1620 end - index into the buffer, first byte after match
1621 match - the matching string itself
1622 """
1623
1624 def __init__(self, strings):
1625
1626 """This creates an instance of searcher_string. This argument 'strings'
1627 may be a list; a sequence of strings; or the EOF or TIMEOUT types. """
1628
1629 self.eof_index = -1
1630 self.timeout_index = -1
1631 self._strings = []
1632 for n, s in zip(list(range(len(strings))), strings):
1633 if s is EOF:
1634 self.eof_index = n
1635 continue
1636 if s is TIMEOUT:
1637 self.timeout_index = n
1638 continue
1639 self._strings.append((n, s))
1640 self.start = self.end = self.match = None
1641
1642 def __str__(self):
1643
1644 """This returns a human-readable string that represents the state of
1645 the object."""
1646
1647 ss = [(ns[0], ' %d: "%s"' % ns) for ns in self._strings]
1648 ss.append((-1, 'searcher_string:'))
1649 if self.eof_index >= 0:
1650 ss.append((self.eof_index, ' %d: EOF' % self.eof_index))
1651 if self.timeout_index >= 0:
1652 ss.append((self.timeout_index, ' %d: TIMEOUT' % self.timeout_index))
1653 ss.sort()
1654 ss = zip(*ss)[1]
1655 return '\n'.join(ss)
1656
1657 def search(self, sbuffer, freshlen, searchwindowsize=None):
1658
1659 """This searches 'sbuffer' for the first occurence of one of the search
1660 strings. 'freshlen' must indicate the number of bytes at the end of
1661 'sbuffer' which have not been searched before. It helps to avoid
1662 searching the same, possibly big, buffer over and over again.
1663
1664 See class spawn for the 'searchwindowsize' argument.
1665
1666 If there is a match this returns the index of that string, and sets
1667 'start', 'end' and 'match'. Otherwise, this returns -1. """
1668
1669 absurd_match = len(sbuffer)
1670 first_match = absurd_match
1671
1672 # 'freshlen' helps a lot here. Further optimizations could
1673 # possibly include:
1674 #
1675 # using something like the Boyer-Moore Fast String Searching
1676 # Algorithm; pre-compiling the search through a list of
1677 # strings into something that can scan the input once to
1678 # search for all N strings; realize that if we search for
1679 # ['bar', 'baz'] and the input is '...foo' we need not bother
1680 # rescanning until we've read three more bytes.
1681 #
1682 # Sadly, I don't know enough about this interesting topic. /grahn
1683 for index, s in self._strings:
1684 if searchwindowsize is None:
1685 # the match, if any, can only be in the fresh data,
1686 # or at the very end of the old data
1687 offset = -(freshlen+len(s))
1688 else:
1689 # better obey searchwindowsize
1690 offset = -searchwindowsize
1691 n = sbuffer.find(s, offset)
1692 if n >= 0 and n < first_match:
1693 first_match = n
1694 best_index, best_match = index, s
1695 if first_match == absurd_match:
1696 return -1
1697 self.match = best_match
1698 self.start = first_match
1699 self.end = self.start + len(self.match)
1700 return best_index
1701
1702 class searcher_re(object):
1703
1704 """This is regular expression string search helper for the
1705 spawn.expect_any() method.
1706
1707 Attributes:
1708
1709 eof_index - index of EOF, or -1
1710 timeout_index - index of TIMEOUT, or -1
1711
1712 After a successful match by the search() method the following attributes
1713 are available:
1714
1715 start - index into the buffer, first byte of match
1716 end - index into the buffer, first byte after match
1717 match - the re.match object returned by a succesful re.search
1718
1719 """
1720
1721 def __init__(self, patterns):
1722
1723 """This creates an instance that searches for 'patterns' Where
1724 'patterns' may be a list or other sequence of compiled regular
1725 expressions, or the EOF or TIMEOUT types."""
1726
1727 self.eof_index = -1
1728 self.timeout_index = -1
1729 self._searches = []
1730 self.start = self.end = self.match = None
1731 for n, s in zip(list(range(len(patterns))), patterns):
1732 if s is EOF:
1733 self.eof_index = n
1734 continue
1735 if s is TIMEOUT:
1736 self.timeout_index = n
1737 continue
1738 self._searches.append((n, s))
1739
1740 def __str__(self):
1741
1742 """This returns a human-readable string that represents the state of
1743 the object."""
1744
1745 ss = [(n, ' %d: re.compile("%s")' % (n, str(s.pattern))) for n, s in self._searches]
1746 ss.append((-1, 'searcher_re:'))
1747 if self.eof_index >= 0:
1748 ss.append((self.eof_index, ' %d: EOF' % self.eof_index))
1749 if self.timeout_index >= 0:
1750 ss.append((self.timeout_index, ' %d: TIMEOUT' % self.timeout_index))
1751 ss.sort()
1752 ss = zip(*ss)[1]
1753 return '\n'.join(ss)
1754
1755 def search(self, sbuffer, _freshlen, searchwindowsize=None):
1756
1757 """This searches 'sbuffer' for the first occurence of one of the regular
1758 expressions. 'freshlen' must indicate the number of bytes at the end of
1759 'sbuffer' which have not been searched before.
1760
1761 See class spawn for the 'searchwindowsize' argument.
1762
1763 If there is a match this returns the index of that string, and sets
1764 'start', 'end' and 'match'. Otherwise, returns -1."""
1765
1766 absurd_match = len(sbuffer)
1767 first_match = absurd_match
1768 # 'freshlen' doesn't help here -- we cannot predict the
1769 # length of a match, and the re module provides no help.
1770 if searchwindowsize is None:
1771 searchstart = 0
1772 else:
1773 searchstart = max(0, len(sbuffer)-searchwindowsize)
1774 for index, s in self._searches:
1775 match = s.search(sbuffer, searchstart)
1776 if match is None:
1777 continue
1778 n = match.start()
1779 if n < first_match:
1780 first_match = n
1781 the_match = match
1782 best_index = index
1783 if first_match == absurd_match:
1784 return -1
1785 self.start = first_match
1786 self.match = the_match
1787 self.end = self.match.end()
1788 return best_index
1789
1790 def which(filename):
1791
1792 """This takes a given filename; tries to find it in the environment path;
1793 then checks if it is executable. This returns the full path to the filename
1794 if found and executable. Otherwise this returns None."""
1795
1796 # Special case where filename already contains a path.
1797 if os.path.dirname(filename) != '':
1798 if os.access(filename, os.X_OK):
1799 return filename
1800
1801 if 'PATH' not in os.environ or os.environ['PATH'] == '':
1802 p = os.defpath
1803 else:
1804 p = os.environ['PATH']
1805
1806 pathlist = p.split(os.pathsep)
1807
1808 for path in pathlist:
1809 f = os.path.join(path, filename)
1810 if os.access(f, os.X_OK):
1811 return f
1812 return None
1813
1814 def split_command_line(command_line):
1815
1816 """This splits a command line into a list of arguments. It splits arguments
1817 on spaces, but handles embedded quotes, doublequotes, and escaped
1818 characters. It's impossible to do this with a regular expression, so I
1819 wrote a little state machine to parse the command line. """
1820
1821 arg_list = []
1822 arg = ''
1823
1824 # Constants to name the states we can be in.
1825 state_basic = 0
1826 state_esc = 1
1827 state_singlequote = 2
1828 state_doublequote = 3
1829 state_whitespace = 4 # The state of consuming whitespace between commands.
1830 state = state_basic
1831
1832 for c in command_line:
1833 if state == state_basic or state == state_whitespace:
1834 if c == '\\': # Escape the next character
1835 state = state_esc
1836 elif c == r"'": # Handle single quote
1837 state = state_singlequote
1838 elif c == r'"': # Handle double quote
1839 state = state_doublequote
1840 elif c.isspace():
1841 # Add arg to arg_list if we aren't in the middle of whitespace.
1842 if state == state_whitespace:
1843 pass # Do nothing.
1844 else:
1845 arg_list.append(arg)
1846 arg = ''
1847 state = state_whitespace
1848 else:
1849 arg = arg + c
1850 state = state_basic
1851 elif state == state_esc:
1852 arg = arg + c
1853 state = state_basic
1854 elif state == state_singlequote:
1855 if c == r"'":
1856 state = state_basic
1857 else:
1858 arg = arg + c
1859 elif state == state_doublequote:
1860 if c == r'"':
1861 state = state_basic
1862 else:
1863 arg = arg + c
1864
1865 if arg != '':
1866 arg_list.append(arg)
1867 return arg_list
1868
1869 #
1870 #
1871 ###############################################################################
1872
1873 version = 2.5
1874
1875 class ssh_session:
1876 "Session with extra state including the password to be used."
1877 # Jason Azze reported:
1878 # I notice that there is intermittently a very long delay (~30
1879 # seconds) between "debug2: we sent a publickey packet, wait for reply"
1880 # and the next debug line, "Authentications that can continue...".
1881 TIMEOUT = 45
1882 def __init__(self, user, port, host, password=None, verbose=0):
1883 self.user = user
1884 self.host = host
1885 self.verbose = verbose
1886 self.password = password
1887 self.port = port
1888 self.auth = ""
1889 def __exec(self, command):
1890 "Execute a command on the remote host. Return the output."
1891 child = spawn(command, timeout=ssh_session.TIMEOUT)
1892 if self.verbose:
1893 sys.stderr.write("-> " + command + "\n")
1894 while True:
1895 seen = child.expect([EOF,
1896 'assword:',
1897 'continue connecting (yes/no)?',
1898 'Permission denied',
1899 'refused',
1900 'not resolve'])
1901 if self.verbose:
1902 sys.stderr.write("<- " + child.before + "\n")
1903 if seen == 0:
1904 break
1905 elif seen == 1:
1906 if self.verbose:
1907 sys.stderr.write("Saw password prompt...\n")
1908 if not self.password:
1909 self.password = getpass.getpass('Remote password: ')
1910 child.sendline(self.password)
1911 elif seen == 2:
1912 if self.verbose:
1913 sys.stderr.write("Saw 'continue connecting' prompt...\n")
1914 child.sendline("yes")
1915 elif seen == 3:
1916 sys.stderr.write("FAILURE: check your password\n")
1917 raise RuntimeError
1918 elif seen == 4:
1919 sys.stderr.write("FAILURE: cannot connect to port " + str(self.port) + "\n")
1920 raise RuntimeError
1921 elif seen == 5:
1922 sys.stderr.write("FAILURE: check hostname\n")
1923 raise RuntimeError
1924 # Remote execution will fail noisily if the host key has changed.
1925 # Pass this message through to the user so there will be a chance
1926 # to recover.
1927 if '@@@' in child.before:
1928 sys.stdout.write(child.before)
1929 raise RuntimeError
1930 return child.before
1931 def ssh(self, command):
1932 "Execute a command on the remote host, returning its output."
1933 return self.__exec("ssh%s -p %s -l %s %s \"%s\"" % (self.auth,
1934 self.port,
1935 self.user,
1936 self.host,
1937 command))
1938 def scp(self, src, dst):
1939 "Copy a file to the remote host."
1940 return self.__exec("scp%s -P %s %s %s@%s:%s" % (self.auth,
1941 self.port,
1942 src,
1943 self.user,
1944 self.host,
1945 dst))
1946 def exists(self, fn):
1947 "Retrieve file permissions of specified remote file."
1948 seen = self.ssh("/bin/ls -ld %s" % fn)
1949 if not seen.strip():
1950 sys.stderr.write("Remote ls returned empty string, bailing out.\n")
1951 raise RuntimeError
1952 if self.verbose:
1953 sys.stderr.write("Existence check on %s gave %s\n" % (fn, repr(seen)))
1954 if seen.find("No such file") > -1:
1955 return None # File doesn't exist
1956 else:
1957 return seen.split()[0] # Return permission field of listing.
1958 def setauth(self, keylist):
1959 "Set non-default key locations."
1960 self.auth = " " + " ".join(["-i %s" % x for x in keylist])
1961
1962 class KeyConfig(configparser.RawConfigParser):
1963 "Store our state about remote keys."
1964 def __init__(self):
1965 configparser.RawConfigParser.__init__(self)
1966 newstyle = os.path.expanduser('~/.config/sshexport.rc')
1967 oldstyle = os.path.expanduser('~/.ssh/sshexport.rc')
1968 if os.path.exists(newstyle):
1969 self.configfile = newstyle
1970 elif os.path.exists(oldstyle):
1971 self.configfile = oldstyle
1972 else:
1973 self.configfile = None
1974 if self.configfile:
1975 if os.stat(self.configfile).st_mode & (stat.S_IROTH | stat.S_IWOTH | stat.S_IRGRP | stat.S_IWGRP):
1976 sys.stderr.write("sshexport: %s is not secured!\n" \
1977 % self.configfile)
1978 sys.exit(1)
1979 self.read(self.configfile)
1980 def save(self):
1981 if self.configfile:
1982 self.write(open(self.configfile, "w"))
1983 def remember(self, session):
1984 authorized = session.ssh("cat $HOME/.ssh/authorized_keys")
1985 keydict = {}
1986 for line in authorized.split("\n"):
1987 if line.strip():
1988 try:
1989 (keytype, content, keyid) = line.strip().split()
1990 if keyid not in keydict:
1991 keydict[keyid] = []
1992 keydict[keyid].append(keytype)
1993 except ValueError:
1994 print("unexpected format in key line '%s'." % repr(line))
1995 if keydict:
1996 role = session.user + "@" + session.host
1997 if not self.has_section(role):
1998 self.add_section(role)
1999 for (keyid, types) in list(keydict.items()):
2000 self.set(role, keyid, ",".join(types))
2001 if session.password is not None:
2002 self.set(role, "password", session.password)
2003 if session.port != 22:
2004 self.set("port", str(session.port))
2005 def forget(self, role):
2006 for (keyid, _types) in config.items(role):
2007 if "@" in keyid:
2008 self.remove_option(role, keyid)
2009
2010 def die(msg):
2011 "Die quietly."
2012 print(msg)
2013 print("Goodbye.")
2014 raise SystemExit(1)
2015
2016 def fixperms(session, fn, checkonly=False):
2017 "Force perms of remote file to user read on, group/other write off."
2018 perms = session.exists(fn)
2019 ok = (perms[1] == 'r' and perms[5] == '-' and perms[8] == '-')
2020 print("Remote %s permissions are %s which is %s." \
2021 % (fn, perms, ("wrong", "OK")[ok]))
2022 if not ok:
2023 print("User read should be on, Group and Other write permissions should be off.")
2024 if not checkonly:
2025 print("Correcting permissions...")
2026 session.ssh("/bin/chmod u+r,g-w,o-w %s" % fn)
2027 return ok
2028
2029 def forcefile(session, fn, command, checkonly=False):
2030 "Create file with command if it does not already exist."
2031 perms = session.exists(fn)
2032 if perms is None:
2033 print("Remote %s doesn't exist." % fn)
2034 if checkonly:
2035 die("Bailing out...")
2036 else:
2037 print("Creating remote %s" % fn)
2038 session.ssh(command)
2039 perms = session.exists(fn)
2040 else:
2041 print(fn, "already exists.")
2042 return perms
2043
2044 if __name__ == '__main__':
2045 # Process options
2046 try:
2047 (options, arguments) = getopt.getopt(sys.argv[1:], "rcdhvp:")
2048 except getopt.GetoptError as e:
2049 print(e)
2050 sys.exit(1)
2051 check = 0
2052 verbose = 0
2053 delete = 0
2054 port = 22
2055 rotate = 0
2056
2057 # Ignore DSA, it's weak and RSA support is baseline at this point
2058 supported_keytypes = ("rsa", "ed25519")
2059
2060 for (switch, val) in options:
2061 if switch == '-r':
2062 rotate = 1
2063 elif switch == '-c':
2064 check = 1
2065 elif switch == '-d':
2066 delete = 1
2067 elif switch == '-p':
2068 port = val
2069 elif switch == '-h':
2070 print(__doc__)
2071 raise SystemExit(0)
2072 elif switch == '-v':
2073 verbose = 1
2074
2075 config = KeyConfig()
2076
2077 # Verify that key pairs are present on the local side
2078 print("Checking your local configuration...")
2079 try:
2080 os.chdir(os.getenv("HOME"))
2081 except:
2082 die("Can't find your home directory!")
2083 if not os.access(".", os.R_OK):
2084 print("Your home directory is not readable by you. That's weird.")
2085 die("This probably means $HOME is incorrect.")
2086 if not os.path.exists(".ssh"):
2087 print("You have no .ssh directory.")
2088 if not check:
2089 print("Creating your .ssh directory.")
2090 os.mkdir(".ssh")
2091 else:
2092 print("Creation of .ssh suppressed.")
2093 if not os.access(".ssh", os.R_OK):
2094 print("Your .ssh directory is not readable by you. That's weird.")
2095 die("This probabably means $HOME is incorrect.")
2096 try:
2097 os.chdir(".ssh")
2098 except:
2099 die("Can't chdir into your .ssh directory!")
2100 public_keys = [f for f in ["id_%s.pub" % x for x in supported_keytypes] if os.access(f, os.R_OK)]
2101 if not public_keys:
2102 if check:
2103 supported = " or ".join([x.upper() for x in supported_keytypes])
2104 print("I don't see any %s key pairs. You need to generate some.")
2105 die("Try re-running this script without the check-only option.")
2106 public_keys = []
2107 # Some older versions of OpenSSH don't accept ed25519 keys, so
2108 # we generate and ship an RSA as well.
2109 for keytype in supported_keytypes:
2110 print("About to generate a %s key. You will be prompted for a passphrase," % keytype)
2111 print("Pressing enter at that prompt will create a key with no passphrase")
2112 os.system("ssh-keygen -t %s -f id_%s" % (keytype, keytype))
2113 public_keys.append("id_%s.pub" % keytype)
2114
2115 print("I see the following public keys:", ", ".join(public_keys))
2116 for filename in public_keys:
2117 private_key = filename[:-4]
2118 legend = private_key + " corresponding to " + filename + " "
2119 if os.access(private_key, os.R_OK):
2120 print(legend + "exists and is readable.")
2121 else:
2122 die(legend + "does not exist or is unreadable.")
2123
2124 print("Local configuration looks correct.")
2125
2126 if not arguments and not rotate:
2127 print("Neither remote host nor -r specified.")
2128 print(__doc__)
2129 raise SystemExit(0)
2130
2131 if rotate:
2132 arguments = config.sections()
2133
2134 # Proceed...
2135 try:
2136 keyfile = tempfile.mktemp()
2137 os.system("cat " + " ".join(public_keys) + " >" + keyfile)
2138 if verbose:
2139 print("Public keys consolidated in %s" % keyfile)
2140
2141 for host in arguments:
2142 login = os.environ.get("LOGNAME") or os.environ.get("USER")
2143 if "@" in host:
2144 (login, host) = host.split("@")
2145
2146 role = login + "@" + host
2147 if config.has_option(role, "port"):
2148 port = config.get(role, "port")
2149 if config.has_option(role, "password"):
2150 password = config.get(role, "password")
2151 else:
2152 password = None
2153
2154 try:
2155 # Get session to remote host.
2156 print("Starting session to %s..." % role)
2157 session = ssh_session(login, port, host,
2158 password=password, verbose=verbose)
2159 if not session.exists("$HOME"):
2160 print("Remote home directory does not exist, bailing out!")
2161 raise RuntimeError
2162
2163 if delete:
2164 session.ssh("rm -fr .ssh")
2165 print("Keys for %s@%s have been deleted." % (login, host))
2166 config.forget(role)
2167 else:
2168 # Permissions checks
2169 fixperms(session, "$HOME", check)
2170 forcefile(session, "$HOME/.ssh", "mkdir $HOME/.ssh", check)
2171 fixperms(session, "$HOME/.ssh", check)
2172
2173 # OK, install keys remotely if they don't exist
2174 if not rotate and session.exists("$HOME/.ssh/authorized_keys"):
2175 print("authorized_keys already exists on %s, cannot append to it." % host)
2176 config.remember(session)
2177 elif check:
2178 print("Check of %s is complete." % role)
2179 else:
2180 print("Copying your public keys to", host)
2181 session.scp(keyfile, "$HOME/.ssh/authorized_keys")
2182 fixperms(session, "$HOME/.ssh/authorized_keys", False)
2183 config.remember(session)
2184 print("Keys for %s have been installed." % role)
2185 config.save()
2186 if session.exists('/sbin/restorecon'):
2187 print("Root user on a system running selinux, fixing security context of authorized_keys file")
2188 session.ssh("/sbin/restorecon $HOME/.ssh/authorized_keys")
2189 except RuntimeError:
2190 pass
2191
2192 finally:
2193 # It's all over now...
2194 os.remove(keyfile)
2195
2196 # The following sets edit modes for GNU EMACS
2197 # Local Variables:
2198 # mode:python
2199 # End: