1
2 """
3
4 @author: Fabio Erculiani <lxnay@sabayonlinux.org>
5 @contact: lxnay@sabayonlinux.org
6 @copyright: Fabio Erculiani
7 @license: GPL-2
8
9 B{EntropyTransceiver SSH URI Handler module}.
10
11 """
12 import os
13 import time
14 import tempfile
15
16 from entropy.output import brown, darkgreen
17 from entropy.i18n import _
18 from entropy.exceptions import ConnectionError
19 from entropy.transceivers.uri_handlers.skel import EntropyUriHandler
22
23 PLUGIN_API_VERSION = 0
24
25 """
26 EntropyUriHandler based SSH (with pubkey) transceiver plugin.
27 """
28
29 _DEFAULT_TIMEOUT = 60
30 _DEFAULT_PORT = 22
31 _TXC_CMD = "/usr/bin/scp"
32 _SSH_CMD = "/usr/bin/ssh"
33
34 @staticmethod
36 if uri.startswith("ssh://"):
37 return True
38 return False
39
40 @staticmethod
42 myuri = uri.split("/")[2:][0].split(":")[0]
43 myuri = myuri.split("@")[-1]
44 return myuri
45
46 @staticmethod
49
51 EntropyUriHandler.__init__(self, uri)
52
53 import socket, subprocess, pty
54 self._socket, self._subprocess = socket, subprocess
55 self._pty = pty
56 self.__host = EntropySshUriHandler.get_uri_name(self._uri)
57 self.__user, self.__port, self.__dir = self.__extract_scp_data(
58 self._uri)
59
60
61
62 self.__test_connection()
63
66
67 - def __exit__(self, exc_type, exc_value, traceback):
69
71 tries = 5
72 while tries:
73 tries -= 1
74 try:
75 sock = self._socket.create_connection(
76 (self.__host, self.__port), 5)
77 sock.close()
78 return
79 except self._socket.error:
80 time.sleep(1)
81 continue
82
83 raise ConnectionError("cannot connect to %s on port %s" % (
84 self.__host, self.__port,))
85
87
88 no_ssh_split = uri.split("ssh://")[-1]
89 user = ''
90 if "@" in no_ssh_split:
91 user = no_ssh_split.split("@")[0]
92
93 port = uri.split(":")[-1]
94 try:
95 port = int(port)
96 except ValueError:
97 port = EntropySshUriHandler._DEFAULT_PORT
98
99 sdir = '~/'
100 proposed_sdir = no_ssh_split.split(":", 1)[-1].split(":")[0]
101 if proposed_sdir:
102 sdir = proposed_sdir
103
104 return user, port, sdir
105
107
108 line_data = line.strip().split()
109 if len(line_data) < 5:
110
111 self.updateProgress(line.strip(), back = True)
112 return
113
114 percent = line_data[1]
115 tx_speed = line_data[3]
116 tx_size = line_data[2]
117 eta = line_data[4]
118
119
120 mytxt = _("Transfer status")
121 current_txt = brown(" <-> %s: " % (mytxt,)) + \
122 darkgreen(tx_size) + " " + \
123 brown("[") + str(percent) + brown("]") + \
124 " " + eta + " " + tx_speed
125
126 self.updateProgress(current_txt, back = True)
127
129 read_buf = ""
130 try:
131 char = std_r.read(1)
132 while char:
133 if (char == "\r") and read_buf:
134 self._parse_progress_line(read_buf)
135 read_buf = ""
136 elif (char != "\r"):
137 read_buf += char
138 char = std_r.read(1)
139 except IOError:
140 return
141
143
144 pid, fd = self._pty.fork()
145
146 if pid == 0:
147 proc = self._subprocess.Popen(args)
148 os._exit(proc.wait())
149 elif pid == -1:
150 raise ConnectionError("cannot forkpty()")
151 else:
152 dead = False
153 return_code = 1
154 std_r = os.fdopen(fd, "r")
155 while not dead:
156
157 try:
158 dead, return_code = os.waitpid(pid, os.WNOHANG)
159 except OSError as e:
160 if e.errno != 10:
161 raise
162 dead = True
163
164 time.sleep(0.5)
165 self._update_progress(std_r)
166 std_r.close()
167
168 return return_code
169
171
172 fd, tmp_path = tempfile.mkstemp()
173 fd_err, tmp_path_err = tempfile.mkstemp()
174 os.close(fd)
175 os.close(fd_err)
176
177 with open(tmp_path, "wb") as std_f:
178 with open(tmp_path_err, "wb") as std_f_err:
179 proc = self._subprocess.Popen(args, stdout = std_f,
180 stderr = std_f_err)
181 exec_rc = proc.wait()
182
183 std_f = open(tmp_path, "rb")
184 output = std_f.read()
185 std_f.close()
186 std_f = open(tmp_path_err, "rb")
187 error = std_f.read()
188 std_f.close()
189
190 os.remove(tmp_path)
191 os.remove(tmp_path_err)
192 return exec_rc, output, error
193
195 args = []
196 if self._speed_limit:
197 args += ["-l", str(self._speed_limit*8)]
198 remote_ptr = os.path.join(self.__dir, remote_path)
199 remote_str = ""
200 if self.__user:
201 remote_str += self.__user + "@"
202 remote_str += self.__host + ":" + remote_ptr
203
204 return args, remote_str
205
206 - def download(self, remote_path, save_path):
207 args = [EntropySshUriHandler._TXC_CMD]
208 c_args, remote_str = self._setup_common_args(remote_path)
209 args.extend(c_args)
210 args += ["-B", "-P", str(self.__port), remote_str, save_path]
211 return self._fork_cmd(args) == 0
212
213 - def upload(self, load_path, remote_path):
214 args = [EntropySshUriHandler._TXC_CMD]
215 c_args, remote_str = self._setup_common_args(remote_path)
216 args.extend(c_args)
217 args += ["-B", "-P", str(self.__port), load_path, remote_str]
218 return self._fork_cmd(args) == 0
219
221 args = [EntropySshUriHandler._SSH_CMD]
222 remote_str = ""
223 if self.__user:
224 remote_str += self.__user + "@"
225 remote_str += self.__host
226 return args, remote_str
227
228 - def rename(self, remote_path_old, remote_path_new):
229 args, remote_str = self._setup_fs_args()
230 remote_ptr_old = os.path.join(self.__dir, remote_path_old)
231 remote_ptr_new = os.path.join(self.__dir, remote_path_new)
232 args += [remote_str, "mv", remote_ptr_old, remote_ptr_new]
233 return self._exec_cmd(args)[0] == 0
234
235 - def delete(self, remote_path):
236 args, remote_str = self._setup_fs_args()
237 remote_ptr = os.path.join(self.__dir, remote_path)
238 args += [remote_str, "rm", remote_ptr]
239 return self._exec_cmd(args)[0] == 0
240
242 args, remote_str = self._setup_fs_args()
243 remote_ptr = os.path.join(self.__dir, remote_path)
244 args += [remote_str, "md5sum", remote_ptr]
245 exec_rc, output, error = self._exec_cmd(args)
246 if exec_rc:
247 return None
248 return output.strip().split()[0]
249
250 - def list_content(self, remote_path):
251 args, remote_str = self._setup_fs_args()
252 remote_ptr = os.path.join(self.__dir, remote_path)
253 args += [remote_str, "ls", "-1", remote_ptr]
254 exec_rc, output, error = self._exec_cmd(args)
255 if exec_rc:
256 return []
257 return [x for x in output.split("\n") if x]
258
260 args, remote_str = self._setup_fs_args()
261 remote_ptr = os.path.join(self.__dir, remote_path)
262 args += [remote_str, "ls", "-1lA", remote_ptr]
263 exec_rc, output, error = self._exec_cmd(args)
264 if exec_rc:
265 return []
266
267 data = []
268 for item in output.split("\n"):
269 item = item.strip().split()
270 if len(item) < 5:
271 continue
272 perms, owner, group, size, name = item[0], item[2], item[3], \
273 item[4], item[-1]
274 data.append((name, size, owner, group, perms,))
275 return data
276
277 - def is_dir(self, remote_path):
278 args, remote_str = self._setup_fs_args()
279 remote_ptr = os.path.join(self.__dir, remote_path)
280 args += [remote_str, "test", "-d", remote_ptr]
281 exec_rc, output, error = self._exec_cmd(args)
282 return exec_rc == 0
283
285 args, remote_str = self._setup_fs_args()
286 remote_ptr = os.path.join(self.__dir, remote_path)
287 args += [remote_str, "test", "-f", remote_ptr]
288 exec_rc, output, error = self._exec_cmd(args)
289 return exec_rc == 0
290
292 args, remote_str = self._setup_fs_args()
293 remote_ptr = os.path.join(self.__dir, remote_path)
294 args += [remote_str, "stat", remote_ptr]
295 exec_rc, output, error = self._exec_cmd(args)
296 return exec_rc == 0
297
299 args, remote_str = self._setup_fs_args()
300 remote_ptr = os.path.join(self.__dir, remote_path)
301 args += [remote_str, "mkdir", "-p", remote_ptr]
302 exec_rc, output, error = self._exec_cmd(args)
303 return exec_rc == 0
304
307
310