includes/SystemHelper.php
<?php
/*
Copyright 2009-2014 Guillaume Boudreau
This file is part of Greyhole.
Greyhole is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Greyhole is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Greyhole. If not, see <http://www.gnu.org/licenses/>.
*/
final class SystemHelper {
public static function is_amahi() {
return file_exists('/usr/bin/hda-ctl');
}
public static function directory_uuid($dir) {
$dev = exec('df ' . escapeshellarg($dir) . ' 2> /dev/null | grep \'/dev\' | awk \'{print $1}\'');
if (!is_dir($dir)) {
return FALSE;
}
if (empty($dev) || strpos($dev, '/dev/') !== 0) {
// ZFS pool maybe?
if (file_exists('/sbin/zpool')) {
$dataset = exec('df ' . escapeshellarg($dir) . ' 2> /dev/null | awk \'{print $1}\'');
if (strpos($dataset, '/') !== FALSE) {
$is_zfs = exec('mount | grep ' . escapeshellarg("$dataset .*zfs") . ' 2> /dev/null | wc -l');
if ($is_zfs == 1) {
$p = explode('/', $dataset);
$pool = $p[0];
$dev_name = exec('/sbin/zpool list -v ' . escapeshellarg($pool) . ' 2> /dev/null | awk \'{print $1}\' | tail -n 1');
if (!empty($dev_name)) {
$dev = exec("ls -l /dev/disk/*/$dev_name | awk '{print \$(NF-2)}'");
if (empty($dev) && file_exists("/dev/$dev_name")) {
$dev = '/dev/$dev_name';
Log::info("Found a ZFS pool ($pool) that uses a device name in /dev/ ($dev). That is a bad idea, since those can easily change, which would prevent this pool from mounting automatically. You should use any of the /dev/disk/*/ links instead. For example, you could do: zpool export $pool && zpool import -d /dev/disk/by-id/ $pool. More details at http://zfsonlinux.org/faq.html#WhatDevNamesShouldIUseWhenCreatingMyPool");
}
}
if (empty($dev)) {
Log::warn("Warning! Couldn't find the device used by your ZFS pool name $pool. That pool will never be used.", Log::EVENT_CODE_ZFS_UNKNOWN_DEVICE);
return FALSE;
}
}
}
}
if (empty($dev)) {
return 'remote';
}
}
$uuid = trim(exec('/sbin/blkid '.$dev.' | awk -F\'UUID="\' \'{print $2}\' | awk -F\'"\' \'{print $1}\''));
if (empty($uuid)) {
return 'remote';
}
return $uuid;
}
}
$use_alt_symlinks_creation = FALSE;
function gh_symlink($target, $link) {
global $use_alt_symlinks_creation;
$success = !$use_alt_symlinks_creation && symlink($target, $link);
if (!$success) {
exec("ln -s " . escapeshellarg($target) . " " . escapeshellarg($link));
$success = gh_is_file($link);
if ($success) {
if (!$use_alt_symlinks_creation) {
Log::info("Will use exec() instead of symlink() to create all symlinks.");
}
$use_alt_symlinks_creation = TRUE;
}
}
return $success;
}
function gh_mkdir($directory, $original_directory, $dir_permissions = NULL) {
$directory = clean_dir($directory);
$original_directory = clean_dir($original_directory);
if (is_dir($directory)) {
if (empty($dir_permissions)) {
$dir_permissions = StorageFile::get_file_permissions($original_directory);
//Log::debug("Folder '$directory' already exists; copying ownership + permission from '$original_directory': $dir_permissions->fileowner:$dir_permissions->filegroup $dir_permissions->fileperms");
} else {
//Log::debug("Folder '$directory' already exists; applying ownership $dir_permissions->fileowner:$dir_permissions->filegroup + permission $dir_permissions->fileperms");
}
if (!chown($directory, $dir_permissions->fileowner)) {
Log::warn(" Failed to chown directory '$directory'", Log::EVENT_CODE_MKDIR_CHOWN_FAILED);
}
if (!chgrp($directory, $dir_permissions->filegroup)) {
Log::warn(" Failed to chgrp directory '$directory'", Log::EVENT_CODE_MKDIR_CHGRP_FAILED);
}
if (!chmod($directory, $dir_permissions->fileperms)) {
Log::warn(" Failed to chmod directory '$directory'", Log::EVENT_CODE_MKDIR_CHMOD_FAILED);
}
} else {
// Need to mkdir & chown/chgrp all dirs that don't exist, up to the full path ($directory)
$dir_parts = explode('/', $directory);
$dir_parts_orig = explode('/', $original_directory);
$i = 0;
$parent_directory = clean_dir('/' . $dir_parts[$i++]);
while (is_dir($parent_directory) && $i < count($dir_parts)) {
//Log::debug("Parent folder already exists: '$parent_directory'. Skipping.");
$parent_directory = clean_dir($parent_directory . '/' . $dir_parts[$i++]);
}
while ($i <= count($dir_parts)) {
if (!empty($dir_permissions)) {
$new_dir_permissions = $dir_permissions;
} else {
// Need to find the corresponding folder in $original_directory, to copy the ownership/permissions from
// eg. for mkdir /mnt/hdd1/gh/ShareName/Dir1/Dir2, with original directory /opt/ShareName/Dir1/Dir2
// - mkdir /mnt/hdd1/gh/ShareName and copy permission from /opt/ShareName
// - mkdir /mnt/hdd1/gh/ShareName/Dir1 and copy permission from /opt/ShareName/Dir1
// - mkdir /mnt/hdd1/gh/ShareName/Dir1/Dir2 and copy permission from /opt/ShareName/Dir1/Dir2
$new_original_directory = '';
for ($j=0; $j<count($dir_parts_orig) - (count($dir_parts) - $i); $j++) {
$new_original_directory = clean_dir($new_original_directory . '/' . $dir_parts_orig[$j]);
}
$new_dir_permissions = StorageFile::get_file_permissions($new_original_directory);
}
if (!is_dir($parent_directory) && !@mkdir($parent_directory, $new_dir_permissions->fileperms)) {
if (gh_is_file($parent_directory)) {
gh_rename($parent_directory, "$parent_directory (file copy)");
}
if (!@mkdir($parent_directory, $new_dir_permissions->fileperms)) {
// Even if mkdir return false, the folder might have been correctly created... who would think...
if (!is_dir($parent_directory)) {
// Try NFC form [http://en.wikipedia.org/wiki/Unicode_equivalence#Normalization]
if (is_dir(normalize_utf8_characters($parent_directory))) {
// Bingo!
$parent_directory = normalize_utf8_characters($parent_directory);
} else {
Log::warn(" Failed to create directory $parent_directory", Log::EVENT_CODE_MKDIR_FAILED);
return FALSE;
}
}
}
}
//Log::debug("Folder '$parent_directory' created with permissions $new_dir_permissions->fileperms; " . (isset($new_original_directory) ? "copying ownership from '$new_original_directory': " : "ownership ") . "$new_dir_permissions->fileowner:$new_dir_permissions->filegroup");
if (!chown($parent_directory, $new_dir_permissions->fileowner)) {
Log::warn(" Failed to chown directory '$parent_directory'", Log::EVENT_CODE_MKDIR_CHOWN_FAILED);
}
if (!chgrp($parent_directory, $new_dir_permissions->filegroup)) {
Log::warn(" Failed to chgrp directory '$parent_directory'", Log::EVENT_CODE_MKDIR_CHGRP_FAILED);
}
if (!isset($dir_parts[$i])) {
break;
}
$parent_directory = clean_dir($parent_directory . '/' . $dir_parts[$i++]);
}
}
return TRUE;
}
function gh_file_exists($real_path, $log_message = NULL) {
clearstatcache();
if (!file_exists($real_path)) {
if (!empty($log_message)) {
eval('$log_message = "' . str_replace('"', '\"', $log_message) . '";');
Log::info($log_message);
}
return FALSE;
}
return TRUE;
}
function gh_is_file_locked($real_fullpath) {
if (is_link($real_fullpath)) {
$real_fullpath = readlink($real_fullpath);
}
$result = exec("lsof -n -P -l " . escapeshellarg($real_fullpath) . " 2> /dev/null");
if (string_contains($result, $real_fullpath)) {
if (preg_match('/^([^\s]+)\s+(\d+)\s+(\d+)\s+([^\s]+)\s/U', $result, $re)) {
// Columns are: COMMAND PID USER FD
if (substr($re[4], -1) == 'r') {
// Open mode = read access; i.e. not locked
return FALSE;
}
return "$re[1], PID $re[2]";
}
return $result;
}
return FALSE;
}
// Get CPU architecture (x86_64 or i386 or armv6l or armv5*)
$arch = exec('uname -m');
if ($arch != 'x86_64') {
function gh_filesize($filename) {
$result = exec("stat -c %s ".escapeshellarg($filename)." 2>/dev/null");
if (empty($result)) {
return FALSE;
}
return (float) $result;
}
function gh_fileowner($filename) {
$result = exec("stat -c %u ".escapeshellarg($filename)." 2>/dev/null");
if (empty($result)) {
return FALSE;
}
return (int) $result;
}
function gh_filegroup($filename) {
$result = exec("stat -c %g ".escapeshellarg($filename)." 2>/dev/null");
if (empty($result)) {
return FALSE;
}
return (int) $result;
}
function gh_fileperms($filename) {
$result = exec("stat -c %a ".escapeshellarg($filename)." 2>/dev/null");
if (empty($result)) {
return FALSE;
}
return "0" . $result;
}
function gh_is_file($filename) {
exec('[ -f '.escapeshellarg($filename).' ]', $tmp, $result);
return $result === 0;
}
function gh_fileinode($filename) {
// This function returns deviceid_inode to make sure this value will be different for files on different devices.
$result = exec("stat -c '%d_%i' ".escapeshellarg($filename)." 2>/dev/null");
if (empty($result)) {
return FALSE;
}
return (string) $result;
}
function gh_file_deviceid($filename) {
$result = exec("stat -c '%d' ".escapeshellarg($filename)." 2>/dev/null");
if (empty($result)) {
return FALSE;
}
return (string) $result;
}
function gh_rename($filename, $target_filename) {
exec("mv ".escapeshellarg($filename)." ".escapeshellarg($target_filename)." 2>/dev/null", $output, $result);
return $result === 0;
}
} else {
function gh_filesize(&$filename) {
$size = @filesize($filename);
// Try NFC form [http://en.wikipedia.org/wiki/Unicode_equivalence#Normalization]
if ($size === FALSE) {
// Try NFC form [http://en.wikipedia.org/wiki/Unicode_equivalence#Normalization]
$size = @filesize(normalize_utf8_characters($filename));
if ($size !== FALSE) {
// Bingo!
$filename = normalize_utf8_characters($filename);
}
}
return $size;
}
function gh_fileowner($filename) {
return fileowner($filename);
}
function gh_filegroup($filename) {
return filegroup($filename);
}
function gh_fileperms($filename) {
return mb_substr(decoct(fileperms($filename)), -4);
}
function gh_is_file($filename) {
return is_file($filename);
}
function gh_fileinode($filename) {
// This function returns deviceid_inode to make sure this value will be different for files on different devices.
$stat = @stat($filename);
if ($stat === FALSE) {
return FALSE;
}
return $stat['dev'] . '_' . $stat['ino'];
}
function gh_file_deviceid($filename) {
$stat = @stat($filename);
if ($stat === FALSE) {
return FALSE;
}
return $stat['dev'];
}
function gh_rename($filename, $target_filename) {
return @rename($filename, $target_filename);
}
}
?>