GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Pull Request — master (#1061)
by Maxim
04:23 queued 01:35
created

NativeSsh::getConnectionHash()   C

Complexity

Conditions 8
Paths 7

Size

Total Lines 34
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
cc 8
eloc 29
nc 7
nop 0
dl 0
loc 34
ccs 0
cts 32
cp 0
crap 72
rs 5.3846
c 0
b 0
f 0
1
<?php
2
/* (c) Anton Medvedev <[email protected]>
3
 *
4
 * For the full copyright and license information, please view the LICENSE
5
 * file that was distributed with this source code.
6
 */
7
8
namespace Deployer\Server\Remote;
9
10
use Deployer\Server\Configuration;
11
use Deployer\Server\ServerInterface;
12
use Symfony\Component\Process\Exception\ProcessFailedException;
13
use Symfony\Component\Process\Process;
14
15
class NativeSsh implements ServerInterface
16
{
17
    const UNIX_SOCKET_MAX_LENGTH = 104;
18
19
    /**
20
     * @var array
21
     */
22
    private $mkdirs = [];
23
24
    /**
25
     * @var Configuration
26
     */
27
    private $configuration;
28
29
    /**
30
     * @param Configuration $configuration
31
     */
32
    public function __construct(Configuration $configuration)
33
    {
34
        $this->configuration = $configuration;
35
    }
36
37
    /**
38
     * {@inheritdoc}
39
     */
40
    public function connect()
41
    {
42
        /* No persistent connection is used */
43
    }
44
45
    /**
46
     * {@inheritdoc}
47
     */
48
    public function run($command)
49
    {
50
        $serverConfig = $this->getConfiguration();
51
        $sshOptions = [
52
            '-A',
53
            '-o UserKnownHostsFile=/dev/null',
54
            '-o StrictHostKeyChecking=no'
55
        ];
56
57
        if (\Deployer\get('ssh_multiplexing', false)) {
58
            $this->initMultiplexing();
59
            $sshOptions = array_merge($sshOptions, $this->getMultiplexingSshOptions());
60
        }
61
62
        $username = $serverConfig->getUser() ? $serverConfig->getUser() : null;
63
        if (!empty($username)) {
64
            $username = $username . '@';
65
        }
66
        $hostname = $serverConfig->getHost();
67
68
        if ($serverConfig->getConfigFile()) {
69
            $sshOptions[] = '-F ' . escapeshellarg($serverConfig->getConfigFile());
70
        }
71
72
        if ($serverConfig->getPort()) {
73
            $sshOptions[] = '-p ' . escapeshellarg($serverConfig->getPort());
74
        }
75
76 View Code Duplication
        if ($serverConfig->getPrivateKey()) {
77
            $sshOptions[] = '-i ' . escapeshellarg($serverConfig->getPrivateKey());
78
        } elseif ($serverConfig->getPemFile()) {
79
            $sshOptions[] = '-i ' . escapeshellarg($serverConfig->getPemFile());
80
        }
81
82
        if ($serverConfig->getPty()) {
83
            $sshOptions[] = '-t';
84
        }
85
86
        $sshCommand = 'ssh ' . implode(' ', $sshOptions) . ' ' . escapeshellarg($username . $hostname) . ' ' . escapeshellarg($command);
87
88
        try {
89
            $process = new Process($sshCommand);
90
            $process
91
                ->setPty($serverConfig->getPty())
92
                ->setTimeout(null)
93
                ->setIdleTimeout(null)
94
                ->mustRun();
95
        } catch (ProcessFailedException $exception) {
96
            $errorMessage = \Deployer\isDebug() ? $exception->getMessage() : $process->getErrorOutput();
97
            throw new \RuntimeException($errorMessage);
98
        }
99
100
        return $process->getOutput();
101
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106
    public function upload($local, $remote)
107
    {
108
        $local = str_replace(' ', '\ ', $local);
109
        $remote = str_replace(' ', '\ ', $remote);
110
111
        $serverConfig = $this->getConfiguration();
112
113
        $username = $serverConfig->getUser() ? $serverConfig->getUser() : null;
114
        $hostname = $serverConfig->getHost();
115
116
        $dir = dirname($remote);
117
118
        if (!in_array($dir, $this->mkdirs)) {
119
            $this->run('mkdir -p ' . escapeshellarg($dir));
120
            $this->mkdirs[] = $dir;
121
        }
122
123
        return $this->scpCopy($local, (!empty($username) ? $username . '@' : '') . $hostname . ':' . $remote);
124
    }
125
126
    /**
127
     * {@inheritdoc}
128
     */
129
    public function download($local, $remote)
130
    {
131
        $serverConfig = $this->getConfiguration();
132
133
        $username = $serverConfig->getUser() ? $serverConfig->getUser() : null;
134
        $hostname = $serverConfig->getHost();
135
136
        return $this->scpCopy((!empty($username) ? $username . '@' : '') . $hostname . ':' . $remote, $local);
137
    }
138
139
    /**
140
     * Copy file from target1 to target 2 via scp
141
     * @param string $target
142
     * @param string $target2
143
     * @return string
144
     */
145
    public function scpCopy($target, $target2)
146
    {
147
        $serverConfig = $this->getConfiguration();
148
149
        $scpOptions = [];
150
151
        if ($serverConfig->getConfigFile()) {
152
            $scpOptions[] = '-F ' . escapeshellarg($serverConfig->getConfigFile());
153
        }
154
155
        if ($serverConfig->getPort()) {
156
            $scpOptions[] = '-P ' . escapeshellarg($serverConfig->getPort());
157
        }
158
159
        if ($serverConfig->getPrivateKey()) {
160
            $scpOptions[] = '-i ' . escapeshellarg($serverConfig->getPrivateKey());
161
        }
162
163
        if (\Deployer\get('ssh_multiplexing', false)) {
164
            $this->initMultiplexing();
165
            $scpOptions = array_merge($scpOptions, $this->getMultiplexingSshOptions());
166
        }
167
168
        $scpCommand = 'scp ' . implode(' ', $scpOptions) . ' ' . escapeshellarg($target) . ' ' . escapeshellarg($target2);
169
170
        try {
171
            $process = new Process($scpCommand);
172
            $process
173
                ->setTimeout(null)
174
                ->setIdleTimeout(null)
175
                ->mustRun();
176
        } catch (ProcessFailedException $exception) {
177
            $errorMessage = \Deployer\isDebug() ? $exception->getMessage() : $process->getErrorOutput();
178
            throw new \RuntimeException($errorMessage);
179
        }
180
181
        return $process->getOutput();
182
    }
183
184
    /**
185
     * @return Configuration
186
     */
187
    public function getConfiguration()
188
    {
189
        return $this->configuration;
190
    }
191
192
    /**
193
     * Return ssh multiplexing socket name
194
     *
195
     * When $connectionHash is longer than 104 chars we can get "SSH Error: unix_listener: too long for Unix domain socket".
196
     * https://github.com/ansible/ansible/issues/11536
197
     * So try to get as descriptive hash as possible.
198
     * %C is creating hash out of connection attributes.
199
     *
200
     * @return string Ssh multiplexing socket name
201
     */
202
    protected function getConnectionHash()
203
    {
204
        $serverConfig = $this->getConfiguration();
205
        $connectionData = "{$serverConfig->getUser()}@{$serverConfig->getHost()}:{$serverConfig->getPort()}";
206
        $tryLongestPossibleSocketName = 0;
207
208
        $connectionHash = '';
209
        do {
210
            switch ($tryLongestPossibleSocketName) {
211
                case 1:
212
                    $connectionHash = "~/.ssh/deployer_mux_" . $connectionData;
213
                    break;
214
                case 2:
215
                    $connectionHash = "~/.ssh/deployer_mux_%C";
216
                    break;
217
                case 3:
218
                    $connectionHash = "~/deployer_mux_$connectionData";
219
                    break;
220
                case 4:
221
                    $connectionHash = "~/deployer_mux_%C";
222
                    break;
223
                case 5:
224
                    $connectionHash = "~/mux_%C";
225
                    break;
226
                case 6:
227
                    throw new \RuntimeException("The multiplexing socket name is too long. Socket name is:" . $connectionHash);
228
                default:
229
                    $connectionHash = "~/.ssh/deployer_mux_$connectionData";
230
            }
231
            $tryLongestPossibleSocketName++;
232
        } while (strlen($connectionHash) > self::UNIX_SOCKET_MAX_LENGTH);
233
234
        return $connectionHash;
235
    }
236
237
238
    /**
239
     * Return ssh options for multiplexing
240
     *
241
     * @return string[]
242
     */
243
    protected function getMultiplexingSshOptions()
244
    {
245
        return [
246
            '-o ControlMaster=auto',
247
            '-o ControlPersist=5',
248
            '-o ControlPath=\'' . $this->getConnectionHash() . '\'',
249
        ];
250
    }
251
252
253
    /**
254
     * Init multiplexing with exec() command
255
     *
256
     * Background: Symfony Process hangs on creating multiplex connection
257
     * but after mux is created with exec() then Symfony Process
258
     * can work with it.
259
     */
260
    public function initMultiplexing()
261
    {
262
        $serverConfig = $this->getConfiguration();
263
        $username = $serverConfig->getUser() ? $serverConfig->getUser() . '@' : null;
264
        $hostname = $serverConfig->getHost();
265
266
        $sshOptions = [];
267
        if ($serverConfig->getConfigFile()) {
268
            $sshOptions[] = '-F ' . escapeshellarg($serverConfig->getConfigFile());
269
        }
270
        if ($serverConfig->getPort()) {
271
            $sshOptions[] = '-p ' . escapeshellarg($serverConfig->getPort());
272
        }
273 View Code Duplication
        if ($serverConfig->getPrivateKey()) {
274
            $sshOptions[] = '-i ' . escapeshellarg($serverConfig->getPrivateKey());
275
        } elseif ($serverConfig->getPemFile()) {
276
            $sshOptions[] = '-i ' . escapeshellarg($serverConfig->getPemFile());
277
        }
278
        $sshOptions = array_merge($sshOptions, $this->getMultiplexingSshOptions());
279
280
        exec('ssh ' . implode(' ', $sshOptions) . ' -O check -S ' . $this->getConnectionHash() . ' ' . escapeshellarg($username . $hostname) . ' 2>&1', $checkifMuxActive);
281
        if (!preg_match('/Master running/', $checkifMuxActive[0])) {
282
            if (\Deployer\isVerbose()) {
283
                \Deployer\writeln('  SSH multiplexing initialization');
284
            }
285
            exec('ssh ' . implode(' ', $sshOptions) . ' ' . escapeshellarg($username . $hostname) . "  'echo \"SSH multiplexing initialization\"' ");
286
        }
287
    }
288
}
289