I'm using the following command to open a temporary ssh tunnel for making a mysql connection:
exec('ssh -f -L 3400:127.0.0.1:3306 user@example.com sleep 1 > /dev/null');
$connection = @new \mysqli(127.0.0.1, $username, $password, $database, 3400);
This works splendidly. However, once in a while there may be another process using that port in which case it fails.
bind [127.0.0.1]:3400: Address already in use channel_setup_fwd_listener_tcpip: cannot listen to port: 3401 Could not request local forwarding.
What I'd like to do is capture the error output of exec()
so that I can retry using a different port. If I add 2>&1
to my command the error output just goes nowhere since stdout is already being piped to /dev/null
.
One solution I've come up with is to pipe output to a file instead of /dev/null
:
exec('ssh -f -L 3400:127.0.0.1:3306 user@example.com sleep 1 >temp.log 2>&1');
$output = file_get_contents('temp.log');
This works, but it feels messy. I'd prefer not to use the filesystem just to get the error response. Is there a way to capture the error output of this command without piping it to a file?
UPDATE: For the sake of clarity:
(a) Capturing result code using the second argument of exec()
does not work in this case. Don't ask me why - but it will always return 0 (success)
(b) stdout must be redirected somewhere or php will not treat it as a background process and script execution will stop until it completes. (https://www.php.net/manual/en/function.exec.php#refsect1-function.exec-notes)
If a program is started with this function, in order for it to continue running in the background, the output of the program must be redirected to a file or another output stream. Failing to do so will cause PHP to hang until the execution of the program ends.