I have production script that copies and renames files from one mounted network share to another (both CIFS), and sometimes the copy
function hangs indefinitely on read I/O dropping the process into uninterruptible sleep where it can only be killed with SIGKILL.
(Thankfully, TASK_KILLABLE, apparently.)
Since operation is blocked at the copy
call I can't gracefully handle the I/O failure, nor, more importantly, log it.
$res = copy("/path/to/mount/file.pdf", "/path/to/productionqueue/newfile.pdf");
//This doesn't run because of the process state.
if($res) {
//Report success to the log.
} else {
//Report failure to the log.
}
It's an environment, file-specific issue, and manual intervention can fix it, but I need to log the failure so an administrator can be notified about the condition. (Aside: I think it's a lock race condition. I can fix the problem by unmounting and remounting the source share, but reproducing it is a bit of an issue.)
Ideally, I'd like the copy
call to timeout after 10 seconds, so I can log the error, but there doesn't seem to be a way in PHP to do that.
What I'm considering is delegating copy or read operations to a subprocess monitored by the timeout
command and responding based on the exit codes, but is there a simpler way in a PHP script to handle this sort of scenario?
Everything I've found in my search suggests there's only runtime configuration available for socket/network timeouts, not filesystem calls.
The answer is no as far as I can tell - there's no built in way to timeout a file read operation in PHP. However, on systems where your I/O is TASK_KILLABLE
you can do the I/O operation in a subprocess and use timeout -s 9
to terminate it. timeout
can will then return an exit status that is non-zero.
For example, with the cp
utility:
$ssrc = escapeshellarg($src);
$sdst = escapeshellarg($dst);
//10 second timeout, send KILL
exec("timeout -s 9 10 cp $ssrc $sdst 2>&1", $output, $ret);
if($ret != 0) {
log("Failed copying $src to $dst, exit status: $ret");
}