~ubuntu-branches/ubuntu/oneiric/monodevelop/oneiric

« back to all changes in this revision

Viewing changes to contrib/NGit/NGit.Transport/JschSession.cs

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2011-06-27 17:03:13 UTC
  • mto: (1.8.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 54.
  • Revision ID: james.westby@ubuntu.com-20110627170313-6cvz3s19x6e9hqe9
ImportĀ upstreamĀ versionĀ 2.5.92+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
This code is derived from jgit (http://eclipse.org/jgit).
 
3
Copyright owners are documented in jgit's IP log.
 
4
 
 
5
This program and the accompanying materials are made available
 
6
under the terms of the Eclipse Distribution License v1.0 which
 
7
accompanies this distribution, is reproduced below, and is
 
8
available at http://www.eclipse.org/org/documents/edl-v10.php
 
9
 
 
10
All rights reserved.
 
11
 
 
12
Redistribution and use in source and binary forms, with or
 
13
without modification, are permitted provided that the following
 
14
conditions are met:
 
15
 
 
16
- Redistributions of source code must retain the above copyright
 
17
  notice, this list of conditions and the following disclaimer.
 
18
 
 
19
- Redistributions in binary form must reproduce the above
 
20
  copyright notice, this list of conditions and the following
 
21
  disclaimer in the documentation and/or other materials provided
 
22
  with the distribution.
 
23
 
 
24
- Neither the name of the Eclipse Foundation, Inc. nor the
 
25
  names of its contributors may be used to endorse or promote
 
26
  products derived from this software without specific prior
 
27
  written permission.
 
28
 
 
29
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 
30
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 
31
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
32
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
33
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 
34
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
35
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
36
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 
37
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 
38
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 
39
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
40
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 
41
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
42
*/
 
43
 
 
44
using System;
 
45
using NGit.Errors;
 
46
using NGit.Transport;
 
47
using NGit.Util.IO;
 
48
using NSch;
 
49
using Sharpen;
 
50
 
 
51
namespace NGit.Transport
 
52
{
 
53
        /// <summary>Run remote commands using Jsch.</summary>
 
54
        /// <remarks>
 
55
        /// Run remote commands using Jsch.
 
56
        /// <p>
 
57
        /// This class is the default session implementation using Jsch. Note that
 
58
        /// <see cref="JschConfigSessionFactory">JschConfigSessionFactory</see>
 
59
        /// is used to create the actual session passed
 
60
        /// to the constructor.
 
61
        /// </remarks>
 
62
        public class JschSession : RemoteSession
 
63
        {
 
64
                private readonly Session sock;
 
65
 
 
66
                private readonly URIish uri;
 
67
 
 
68
                /// <summary>
 
69
                /// Create a new session object by passing the real Jsch session and the URI
 
70
                /// information.
 
71
                /// </summary>
 
72
                /// <remarks>
 
73
                /// Create a new session object by passing the real Jsch session and the URI
 
74
                /// information.
 
75
                /// </remarks>
 
76
                /// <param name="session">the real Jsch session created elsewhere.</param>
 
77
                /// <param name="uri">the URI information for the remote connection</param>
 
78
                public JschSession(Session session, URIish uri)
 
79
                {
 
80
                        sock = session;
 
81
                        this.uri = uri;
 
82
                }
 
83
 
 
84
                /// <exception cref="System.IO.IOException"></exception>
 
85
                public virtual SystemProcess Exec(string command, int timeout)
 
86
                {
 
87
                        return new JschSession.JschProcess(this, command, timeout);
 
88
                }
 
89
 
 
90
                public virtual void Disconnect()
 
91
                {
 
92
                        if (sock.IsConnected())
 
93
                        {
 
94
                                sock.Disconnect();
 
95
                        }
 
96
                }
 
97
 
 
98
                /// <summary>
 
99
                /// A kludge to allow
 
100
                /// <see cref="TransportSftp">TransportSftp</see>
 
101
                /// to get an Sftp channel from Jsch.
 
102
                /// Ideally, this method would be generic, which would require implementing
 
103
                /// generic Sftp channel operations in the RemoteSession class.
 
104
                /// </summary>
 
105
                /// <returns>a channel suitable for Sftp operations.</returns>
 
106
                /// <exception cref="NSch.JSchException">on problems getting the channel.</exception>
 
107
                public virtual Channel GetSftpChannel()
 
108
                {
 
109
                        return sock.OpenChannel("sftp");
 
110
                }
 
111
 
 
112
                /// <summary>Implementation of Process for running a single command using Jsch.</summary>
 
113
                /// <remarks>
 
114
                /// Implementation of Process for running a single command using Jsch.
 
115
                /// <p>
 
116
                /// Uses the Jsch session to do actual command execution and manage the
 
117
                /// execution.
 
118
                /// </remarks>
 
119
                internal class JschProcess : SystemProcess
 
120
                {
 
121
                        private ChannelExec channel;
 
122
 
 
123
                        private readonly int timeout;
 
124
 
 
125
                        private InputStream inputStream;
 
126
 
 
127
                        private OutputStream outputStream;
 
128
 
 
129
                        private InputStream errStream;
 
130
 
 
131
                        /// <summary>
 
132
                        /// Opens a channel on the session ("sock") for executing the given
 
133
                        /// command, opens streams, and starts command execution.
 
134
                        /// </summary>
 
135
                        /// <remarks>
 
136
                        /// Opens a channel on the session ("sock") for executing the given
 
137
                        /// command, opens streams, and starts command execution.
 
138
                        /// </remarks>
 
139
                        /// <param name="commandName">the command to execute</param>
 
140
                        /// <param name="tms">the timeout value, in seconds, for the command.</param>
 
141
                        /// <exception cref="NGit.Errors.TransportException">
 
142
                        /// on problems opening a channel or connecting to the remote
 
143
                        /// host
 
144
                        /// </exception>
 
145
                        /// <exception cref="System.IO.IOException">on problems opening streams</exception>
 
146
                        public JschProcess(JschSession _enclosing, string commandName, int tms)
 
147
                        {
 
148
                                this._enclosing = _enclosing;
 
149
                                this.timeout = tms;
 
150
                                try
 
151
                                {
 
152
                                        this.channel = (ChannelExec)this._enclosing.sock.OpenChannel("exec");
 
153
                                        this.channel.SetCommand(commandName);
 
154
                                        this.SetupStreams();
 
155
                                        this.channel.Connect(this.timeout > 0 ? this.timeout * 1000 : 0);
 
156
                                        if (!this.channel.IsConnected())
 
157
                                        {
 
158
                                                throw new TransportException(this._enclosing.uri, "connection failed");
 
159
                                        }
 
160
                                }
 
161
                                catch (JSchException e)
 
162
                                {
 
163
                                        throw new TransportException(this._enclosing.uri, e.Message, e);
 
164
                                }
 
165
                        }
 
166
 
 
167
                        /// <exception cref="System.IO.IOException"></exception>
 
168
                        private void SetupStreams()
 
169
                        {
 
170
                                this.inputStream = this.channel.GetInputStream();
 
171
                                // JSch won't let us interrupt writes when we use our InterruptTimer
 
172
                                // to break out of a long-running write operation. To work around
 
173
                                // that we spawn a background thread to shuttle data through a pipe,
 
174
                                // as we can issue an interrupted write out of that. Its slower, so
 
175
                                // we only use this route if there is a timeout.
 
176
                                OutputStream @out = this.channel.GetOutputStream();
 
177
                                if (this.timeout <= 0)
 
178
                                {
 
179
                                        this.outputStream = @out;
 
180
                                }
 
181
                                else
 
182
                                {
 
183
                                        PipedInputStream pipeIn = new PipedInputStream();
 
184
                                        StreamCopyThread copier = new StreamCopyThread(pipeIn, @out);
 
185
                                        PipedOutputStream pipeOut = new _PipedOutputStream_173(this, copier, pipeIn);
 
186
                                        // Just wake early, the thread will terminate
 
187
                                        // anyway.
 
188
                                        copier.Start();
 
189
                                        this.outputStream = pipeOut;
 
190
                                }
 
191
                                this.errStream = this.channel.GetErrStream();
 
192
                        }
 
193
 
 
194
                        private sealed class _PipedOutputStream_173 : PipedOutputStream
 
195
                        {
 
196
                                public _PipedOutputStream_173(JschProcess _enclosing, StreamCopyThread copier, PipedInputStream
 
197
                                         baseArg1) : base(baseArg1)
 
198
                                {
 
199
                                        this._enclosing = _enclosing;
 
200
                                        this.copier = copier;
 
201
                                }
 
202
 
 
203
                                /// <exception cref="System.IO.IOException"></exception>
 
204
                                public override void Flush()
 
205
                                {
 
206
                                        base.Flush();
 
207
                                        copier.Flush();
 
208
                                }
 
209
 
 
210
                                /// <exception cref="System.IO.IOException"></exception>
 
211
                                public override void Close()
 
212
                                {
 
213
                                        base.Close();
 
214
                                        try
 
215
                                        {
 
216
                                                copier.Join(this._enclosing.timeout * 1000);
 
217
                                        }
 
218
                                        catch (Exception)
 
219
                                        {
 
220
                                        }
 
221
                                }
 
222
 
 
223
                                private readonly JschProcess _enclosing;
 
224
 
 
225
                                private readonly StreamCopyThread copier;
 
226
                        }
 
227
 
 
228
                        public override InputStream GetInputStream()
 
229
                        {
 
230
                                return this.inputStream;
 
231
                        }
 
232
 
 
233
                        public override OutputStream GetOutputStream()
 
234
                        {
 
235
                                return this.outputStream;
 
236
                        }
 
237
 
 
238
                        public override InputStream GetErrorStream()
 
239
                        {
 
240
                                return this.errStream;
 
241
                        }
 
242
 
 
243
                        public override int ExitValue()
 
244
                        {
 
245
                                if (this.IsRunning())
 
246
                                {
 
247
                                        throw new InvalidOperationException();
 
248
                                }
 
249
                                return this.channel.GetExitStatus();
 
250
                        }
 
251
 
 
252
                        private bool IsRunning()
 
253
                        {
 
254
                                return this.channel.GetExitStatus() < 0 && this.channel.IsConnected();
 
255
                        }
 
256
 
 
257
                        public override void Destroy()
 
258
                        {
 
259
                                if (this.channel.IsConnected())
 
260
                                {
 
261
                                        this.channel.Disconnect();
 
262
                                }
 
263
                        }
 
264
 
 
265
                        /// <exception cref="System.Exception"></exception>
 
266
                        public override int WaitFor()
 
267
                        {
 
268
                                while (this.IsRunning())
 
269
                                {
 
270
                                        Sharpen.Thread.Sleep(100);
 
271
                                }
 
272
                                return this.ExitValue();
 
273
                        }
 
274
 
 
275
                        private readonly JschSession _enclosing;
 
276
                }
 
277
        }
 
278
}