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.
Completed
Push — master ( fcb0ee...48abf0 )
by Anton
03:54 queued 01:18
created

NativeSsh::run()   C

Complexity

Conditions 7
Paths 64

Size

Total Lines 44
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

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