I originally asked this question on Ask Differently, but the administrator said it was "too broad" for that group and that I should ask it here.
I am trying to create a PHP program which will run on localhost and needs to take snapshots from attached USB camera devices (the camera array peripheral that I am developing).
I am doing development on Mac OS X Mojave running on MacBook Pro. I ultimately will deploys to kiosks which have either an embedded Mac Mini running Mac OS X, or an embedded Raspberry Pi running Linux. The custom camera array HW I am developing is also embedded in this kiosk.
I am currently using imagesnap on the Mac and plan to us fswebcam on Linux to take pictures programmatically.
I've set up the local Apache Web Server that I access as localhost. I also have PHP 7.1.23 installed.
I have written a PHP program (see below) that should list the available cameras connected to the computer using imagesnap -l and then it should take an image with each listed camera using imagesnap -d.
If I run this PHP program in CLI mode from the terminal under my own userID, it it listed the camera and took and saved the image just fine.
However, my intended deployment will be in a kiosk, with a webpage interface that calls this PHP program when images are to be taken.
Unfortunately there's a problem. The symptoms are clear:
When the PHP program is running in CLI mode with my userID it takes images fine and writes them to the appropriate subdirectory of the web directory.
% php ../imagesnaptest.php
imagesnap -l RETURNS 'Video Devices:
<AVCaptureDALDevice: 0x7fda89438500 [FaceTime HD Camera][DJH6095K5ZEG1HPBB]>
'
FaceTime HD Camera is a valid camera.
camerasArray =
array(1) {
[0]=>
string(18) "FaceTime HD Camera"
}
imagesnap -d 'FaceTime HD Camera' /Users/myiuserid/Sites/sonascan/scans/img5cd115d73d2b5.jpg RETURNS 'Capturing image from device "<AVCaptureDALDevice: 0x7fb27b32dbb0 [FaceTime HD Camera][DJH6095K5ZEG1HPBB]>"...'
Captured 1 images from cameras.
IMAGING COMPLETE.
But when that php program is invoked with an HTTP call from a browser, it runs as _www and can't seem to access the cameras and no images are saved to the web directory. But there is no error message explaining why imagesnap failed (or even acknowledging that it had).
% imagesnap -l RETURNS ''
camerasArray =
array(0) { }
No valid cameras were found!
I've opened up the permissions of my web directory to be rwx by all, and this difference in behaviors remained unchanged.
It was suggested to me that my problem is a permissions misconfiguration issue because normally the _www webserver user doesn't have access to attached USB devices like cameras. However, no one suggested how I could change that.
In exploring the suggestions I have been given to date, I experimented with making imagesnap owned by _www, or even root, and even setting the setuid bit on the program. That did not resolve the problem. Instead it introduced a new error which generated the error message:
*2019-05-06 19:56:44.081 imagesnap[60072:14580598] The application with bundle ID (null) is running setugid(), which is not allowed*
.
commenter @nohillside wrote:
"This can be a simple access permission issue, this can be an Apache misconfiguration, this can be a problem coming from SIP (System Integrity Protection), sandboxing or full disk access permissions, this can be something else. Without more details about the setup and some digging on your part (eg if you replace imagesnap with a shell script which just logs itself, does it work) any answer can only be guesswork."
Following up on @nohillside's suggestion I replaced imagesnap with a program that doesn't access the camera ("/usr/local/bin/python3 -h" and there is no error when the program is changed. So I am thinking it has something to do with the camera access that imagesnap needs.
It makes sense to me that this might have something to do with SIP or sandboxing, but I have no idea how to change and test alternative SIP or sandbox configurations. If you are knowledgable about how to configure these and have suggestions for me to test, Please describe more precisely how to change this configuration.
How can I learn more about how to configure my web server to allow it to access my camera in this way? And where can I learn more about how SIP and sandboxing security normally work, and how I should adjust them for situations like I've just described.
<?php
if (php_sapi_name() == "cli") { $nl= PHP_EOL; } //PHP is running in command line
else { $nl= "<br />"; } // PHP is running in browser }
function getListOfCameras()
{
global $nl;
$imagesnaplist = shell_exec("/usr/local/bin/imagesnap -l");
echo "imagesnap -l RETURNS '" . $imagesnaplist ."' $nl";
$cameraDeviceArray = explode(PHP_EOL,$imagesnaplist );
unset ($cameraDeviceArray[0]); // Throw away "Video Devices:" line in element [0]
$cameraDeviceArray = array_values($cameraDeviceArray); // Reassign elements in array to start at [0] again
$matchedCameras = 0;
$camerasArray =array();
foreach ($cameraDeviceArray as $key => $value)
{
preg_match("/\[(.*?)\]/", $value, $matches);
if ($matches[1] != "")
{
$camerasArray[$matchedCameras] = $matches[1];
echo $camerasArray[$matchedCameras]. " is a valid camera. $nl";
$matchedCameras++;
}
}
return $camerasArray;
}
function takeAPhoto($camera, $filename)
{
global $nl;
$status = shell_exec("/usr/local/bin/imagesnap -d '$camera' $filename ");
echo "imagesnap -d '$camera' $filename RETURNS '" . $status . "'$nl";
return $status;
}
function takeAllPhotos($camerasArray)
{
global $nl;
$numberTaken = 0;
if (count($camerasArray) > 0)
{
foreach ($camerasArray as $camera)
{
$cameraNumber = $numberTaken;
$filename = "/Users/myiuserid/Sites/sonascan/scans/img" . uniqid() . ".jpg";
$status = takeAPhoto($camera, $filename);
$numberTaken++;
}
return $numberTaken;
} else
{
die("No valid cameras were found!$nl");
}
}
$camerasArray = getListOfCameras();
echo "camerasArray = $nl";
var_dump($camerasArray);
echo "$nl";
$numberTaken = takeAllPhotos($camerasArray);
echo "$nl" . "Captured $numberTaken images from $numberOfCameras cameras.$nl";
echo "IMAGING COMPLETE.$nl";
?>