3 require_once("openmediavault/object.inc");
4 require_once("openmediavault/config.inc");
5 require_once("openmediavault/error.inc");
6 require_once("openmediavault/util.inc");
7 require_once("openmediavault/rpcservice.inc");
8 require_once("openmediavault/notify.inc");
9 require_once("zfs/Utils.php");
10 require_once("zfs/Dataset.php");
11 require_once("zfs/Snapshot.php");
12 require_once("zfs/Zvol.php");
13 require_once("zfs/Zpool.php");
14 require_once("zfs/NotifyListener.php");
16 class OMVRpcServiceZFS extends OMVRpcServiceAbstract {
17 public function getName() {
18 return "ZFS"; // RPC Service name. Same as in .js files
21 /* Initialize the RPC service. Different methods of the RPC service are declared here*/
22 public function initialize() {
23 $this->registerMethod("addPool");
24 $this->registerMethod("getObjectTree");
25 $this->registermethod("passParam");
26 $this->registermethod("addObject");
27 $this->registermethod("deleteObject");
28 $this->registermethod("getProperties");
29 $this->registermethod("setProperties");
30 $this->registermethod("inherit");
31 $this->registermethod("getSharedParams");
32 $this->registermethod("createShare");
33 $this->registermethod("getObjectDetails");
34 $this->registermethod("expandPool");
37 public function addPool($params, $context) {
38 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
39 // Validate the parameters of the RPC service method.
40 $this->validateMethodParams($params, '{
43 "pooltype":{"type":"string","enum":["basic","mirror",' .
44 '"raidz1","raidz2","raidz3"]},
45 "force":{"type":"boolean"},
46 "mountpoint":{"type":"string"},
47 "name":{"type":"string"},
48 "devices":{"type":"string"},
49 "diskpath":{"type":"boolean"}
52 switch ($params['pooltype']) {
54 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSPLAIN;
57 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSMIRROR;
60 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ1;
63 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ2;
66 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ3;
69 throw new OMVModuleZFSException("Incorrect pool type specified");
72 //Check for user supplied options
74 if ($params['force']) {
77 if (strlen($params['mountpoint']) > 0) {
78 $opts .= "-m " . $params['mountpoint'] . " ";
81 $disks = preg_split("/[,;]/", $params['devices']);
82 foreach ($disks as $disk) {
83 OMVModuleZFSUtil::setGPTLabel($disk);
85 //Use /dev/disk/by-path as suggested in ZoL FAQ.
86 if ($params['diskpath']) {
88 if (file_exists("/dev/disk/by-path/")) {
90 foreach ($disks as $disk) {
91 $tmp_disks[] = OMVModuleZFSUtil::getDiskPath($disk);
95 } catch (OMVModuleZFSException $e) {
96 //Do nothing if an excpetion is thrown
100 $vdev = new OMVModuleZFSVdev($params['name'], $pooltype, $disks);
101 $pool = new OMVModuleZFSZpool($vdev, $opts);
102 //Ugly fix to solve the problem of blkid not displaying info on newly created pools
104 $pool->import($pool->getName());
107 public function getObjectTree($params, $context) {
108 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
109 $objects = OMVModuleZFSUtil::getZFSFlatArray();
111 foreach ($objects as $a){
112 $new[$a['parentid']][] = $a;
114 $tree = OMVModuleZFSUtil::createTree($new, $new['root']);
115 OMVModuleZFSUtil::addMissingOMVMntEnt(); //Adds missing ZFS filesystems to the OMV core
119 public function passParam($params, $context) {
120 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
121 // Validate the parameters of the RPC service method.
122 $this->validateMethodParams($params, '{
125 "key":{"type":"string"},
126 "value":{"type":"string"}
129 return array($params['key'] => $params['value']);
132 public function addObject($params, $context) {
133 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
134 // Validate the parameters of the RPC service method.
135 $this->validateMethodParams($params, '{
138 "type":{"type":"string","enum":["filesystem","snapshot",' .
140 "path":{"type":"string"},
141 "name":{"type":"string"},
142 "size":{"type":"string"},
143 "clonename":{"type":"string"},
144 "mountpoint":{"type":"string"}
147 switch ($params['type']) {
149 $tmp = new OMVModuleZFSSnapshot($params['path']);
150 $tmp->clonesnap($params['clonename']);
153 $name = $params['path'] . "/" . $params['name'];
154 $tmp = new OMVModuleZFSDataset($name);
155 if (strlen($params['mountpoint']) > 0) {
156 $properties = array("mountpoint"=>$params['mountpoint']);
157 $tmp->setProperties($properties);
161 $name = $params['path'] . "@" . $params['name'];
162 $tmp = new OMVModuleZFSSnapshot($name);
165 $name = $params['path'] . "/" . $params['name'];
166 $tmp = new OMVModuleZFSZvol($name);
167 $tmp->create($params['size']);
170 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
175 public function deleteObject($params, $context) {
176 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
177 // Validate the parameters of the RPC service method.
178 $this->validateMethodParams($params, '{
181 "type":{"type":"string","enum":["Filesystem","Snapshot",' .
183 "name":{"type":"string"}
187 $name = $params['name'];
188 switch ($params['type']) {
190 OMVModuleZFSUtil::deleteShares($name);
191 $tmp = new OMVModuleZFSDataset($name);
195 $tmp = new OMVModuleZFSSnapshot($name);
199 $tmp = new OMVModuleZFSZvol($name);
203 $disks = OMVModuleZFSUtil::getDevDisksByPool($name);
204 $pooluuid = OMVModuleZFSUtil::getUUIDbyName($name);
205 $tmp = new OMVModuleZFSZpool($name);
207 $xpath = "//system/fstab/mntent[fsname='" . $pooluuid . "' and type='zfs']";
208 $object = $xmlConfig->get($xpath);
209 $xmlConfig->delete($xpath);
210 OMVModuleZFSUtil::clearZFSLabel($disks);
213 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
218 public function getProperties($params, $context) {
219 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
220 // Validate the parameters of the RPC service method.
221 $this->validateMethodParams($params, '{
224 "type":{"type":"string"},
225 "name":{"type":"string"},
226 "start":{"type":"integer"},
227 "limit":{'.$GLOBALS['OMV_JSONSCHEMA_COUNTFIELD'].'},
228 "sortfield":{'.$GLOBALS['OMV_JSONSCHEMA_SORTFIELD'].'},
229 "sortdir":{'.$GLOBALS['OMV_JSONSCHEMA_SORTDIR'].'}
233 $name = $params['name'];
234 switch ($params['type']) {
236 $tmp = new OMVModuleZFSDataset($name);
239 $tmp = new OMVModuleZFSSnapshot($name);
242 $tmp = new OMVModuleZFSZvol($name);
245 $tmp = new OMVModuleZFSZpool($name);
248 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
251 $properties = $tmp->getProperties();
252 foreach ($properties as $propertyk => $propertyv) {
253 if (!(strcmp($propertyv['source'], "-") == 0)) {
254 $objects[] = array('property' => $propertyk,
255 'value' => $propertyv['value'],
256 'source' => $propertyv['source'],
257 'modified' => "false");
263 public function setProperties($params, $context) {
264 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
265 // Validate the parameters of the RPC service method.
266 $this->validateMethodParams($params, '{
269 "type":{"type":"string","enum":["Filesystem","Snapshot",' .
271 "name":{"type":"string"},
272 "properties":{"type":"array","items":{
275 "property":{"type":"string"},
276 "value":{"type":"string"}}}}
280 switch ($params['type']) {
282 $tmp = new OMVModuleZFSDataset($params['name']);
285 $tmp = new OMVModuleZFSSnapshot($params['name']);
288 $tmp = new OMVModuleZFSZvol($params['name']);
291 $tmp = new OMVModuleZFSZpool($params['name']);
294 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
297 foreach ($params['properties'] as $property) {
300 $objects[$property['property']] = $property['value'];
301 $tmp->setProperties($objects);
302 if ((strcmp($property['property'], "mountpoint") === 0) && (strcmp($params['type'], "Filesystem") === 0)) {
303 OMVModuleZFSUtil::relocateFilesystem($params['name']);
308 public function inherit($params, $context) {
309 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
310 // Validate the parameters of the RPC service method.
311 $this->validateMethodParams($params, '{
314 "type":{"type":"string","enum":["Filesystem","Snapshot",' .
316 "name":{"type":"string"},
317 "property":{"type":"string"}
320 // Create a background process.
321 $bgStatusFilename = $this->createBgProcStatus();
322 $pid = $this->fork();
323 if($pid > 0) { // Parent process.
324 $this->initializeBgProcStatus($bgStatusFilename, $pid);
325 return $bgStatusFilename;
329 $bgOutputFilename = $this->createBgProcOutput();
330 $this->updateBgProcStatus($bgStatusFilename, "outputfilename", $bgOutputFilename);
331 switch ($params['type']) {
333 $tmp = new OMVModuleZFSDataset($params['name']);
336 $tmp = new OMVModuleZFSSnapshot($params['name']);
339 $tmp = new OMVModuleZFSZvol($params['name']);
342 $tmp = new OMVModuleZFSZpool($params['name']);
345 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
348 $tmp->inherit($params['property']);
349 $this->finalizeBgProcStatus($bgStatusFilename, $output);
351 } catch(Exception $e) {
352 $this->finalizeBgProcStatus($bgStatusFilename, "", $e);
357 public function getSharedParams($params, $context) {
358 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
359 // Validate the parameters of the RPC service method.
360 $this->validateMethodParams($params, '{
363 "type":{"type":"string"},
364 "name":{"type":"string"}
368 //$ds = new OMVModuleZFSDataset($params['name']);
369 //$mountpoint = $ds->getMountPoint();
371 //"mountpoint" => $mountpoint,
372 "name" => $params['name'],
373 "type" => $params['type']);
376 public function createShare($params, $context) {
378 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
379 // Validate the parameters of the RPC service method.
380 $this->validateMethodParams($params, '{
383 "name":{"type":"string"},
384 "type":{"type":"string","enum":["Filesystem"]},
385 "sharename":{'.$GLOBALS['OMV_JSONSCHEMA_SHARENAME'].'},
386 "comment":{"type":"string"},
387 "mode":{"type":"string","enum":["700","750","755",'.
388 '"770","775","777"],"optional":true},
389 "mountpoint":{"type":"string"}
393 // The field 'reldirpath' may not contain the characters '..'. This
394 // is because of security reasons: the given canonicalized absolute
395 // path MUST be below the given mount point.
396 if(1 == preg_match("/\.\./", $params['mountpoint'])) {
397 throw new OMVException(OMVErrorMsg::E_RPC_SERVICE_METHOD_INVALID_PARAMS,
398 sprintf(gettext("The field '%s' contains forbidden two-dot symbols"), "mountpoint"));
401 // Prepare the configuration object. Use the name of the shared
402 // folder as the relative directory name of the share.
403 switch ($params['type']) {
405 $tmp = new OMVModuleZFSDataset($params['name']);
408 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
412 $poolname = OMVModuleZFSUtil::getPoolname($params['name']);
413 $pooluuid = OMVModuleZFSUtil::getUUIDbyName($poolname);
414 $dir = $tmp->getMountPoint();
416 //Get the mntent object and fetch it's uuid.
417 $xpath = "//system/fstab/mntent[fsname='" . $pooluuid . "' and dir='" . $dir . "' and type='zfs']";
418 $mountpoint = $xmlConfig->get($xpath);
419 $mntentref = $mountpoint['uuid'];
421 $uuid = OMVUtil::uuid();
422 $pathName = $dir . "/" . trim($params['mountpoint'], "/");
423 $reldirpath = trim($params['mountpoint'], "/");
426 "name" => $params['sharename'],
427 "comment" => $params['comment'] . "*** ZFS share on " . $params['name'] . " ***",
428 "mntentref" => $mntentref,
429 "reldirpath" => $reldirpath
432 // Set the configuration object.
434 // Check uniqueness. The share name must be global unique because
435 // the name is also used when exporting a shared folder via NFS for
437 $xpath = sprintf("//system/shares/sharedfolder[name='%s']",
439 if(TRUE === $xmlConfig->exists($xpath)) {
440 throw new OMVException(OMVErrorMsg::E_CONFIG_OBJECT_UNIQUENESS,
441 gettext("A shared folder with the given name already exists"));
444 // Add empty list of privileges per default.
445 $object['privileges'] = array();
447 // Append object to configuration.
448 $success = $xmlConfig->set("//system/shares",
449 array("sharedfolder" => $object));
450 if(FALSE === $success) {
451 throw new OMVException(OMVErrorMsg::E_CONFIG_SET_OBJECT_FAILED);
454 // Append the file mode field to the notification object if set.
456 $object['mode'] = "775";
457 if(array_key_exists("mode", $params)) {
458 $object['mode'] = $params['mode'];
461 // Create the shared folder directory if necessary.
462 if(FALSE === file_exists($pathName)) {
463 // Create the directory. Note, the function seems to have a bug
464 // when using the mask parameter. E.g. octdec("777") does not
465 // create the correct permissions as expected, thus change the
467 if(FALSE === mkdir($pathName, 0700, TRUE)) {
468 throw new OMVException(OMVErrorMsg::E_MISC_FAILURE,
469 sprintf("Failed to create the directory '%s'", $pathName));
471 // Change the directory mode.
472 if(FALSE === chmod($pathName, octdec($object['mode']))) {
473 throw new OMVException(OMVErrorMsg::E_MISC_FAILURE,
474 sprintf("Failed to set file mode to '%s' for '%s'",
475 $object['mode'], $pathName));
479 // Change group owner of directory to configured default group,
481 if(FALSE === chgrp($pathName, $GLOBALS['OMV_USERMGMT_DEFAULT_GROUP'])) {
482 throw new OMVException(OMVErrorMsg::E_MISC_FAILURE,
483 sprintf("Failed to set file group to '%s' for '%s'",
484 $GLOBALS['OMV_USERMGMT_DEFAULT_GROUP'], $pathName));
487 // Set the setgid bit. Setting this permission means that all files
488 // created in the folder will inherit the group of the folder rather
489 // than the primary group of the user who creates the file.
490 $mode = fileperms($pathName) | 02000;
491 if(FALSE === chmod($pathName, $mode)) {
492 throw new OMVException(OMVErrorMsg::E_MISC_FAILURE,
493 sprintf("Failed to set file mode to '%o' for '%s'",
497 // Notify configuration changes.
498 $dispatcher = &OMVNotifyDispatcher::getInstance();
499 $dispatcher->notify(OMV_NOTIFY_CREATE,"org.openmediavault.system.shares.sharedfolder", $object);
500 // Return the configuration object.
504 public function getObjectDetails($params, $context) {
505 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
506 // Validate the parameters of the RPC service method.
507 $this->validateMethodParams($params, '{
510 "name":{"type":"string"},
511 "type":{"type":"string"}
515 switch ($params['type']) {
517 $output .= "Filesystem details (zfs get all):\n\r\n\r";
518 $cmd = "zfs get all {$params['name']}";
521 $output .= "Volume details (zfs get all):\n\r\n\r";
522 $cmd = "zfs get all {$params['name']}";
525 $output .= "Snapshot details (zfs get all):\n\r\n\r";
526 $cmd = "zfs get all {$params['name']}";
529 $output .= "Pool status (zpool status):\n\r\n\r";
530 $cmd = "zpool status {$params['name']}";
531 OMVModuleZFSUtil::exec($cmd,$out,$res);
532 $output .= implode("\n\r", $out);
534 $output .= "\n\r\n\rPool details (zpool get all):\n\r\n\r";
535 $cmd = "zpool get all {$params['name']}";
536 OMVModuleZFSUtil::exec($cmd,$out,$res);
537 $output .= implode("\n\r", $out);
539 $output .= "\n\r\n\rPool filesystem details (zfs get all):\n\r\n\r";
540 $cmd = "zfs get all {$params['name']}";
543 throw new OMVModuleZFSException("Incorrect type provided");
545 OMVModuleZFSUtil::exec($cmd,$out,$res);
546 $output .= implode("\n\r", $out);
547 return array("details" => $output);
550 public function expandPool($params, $context) {
551 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
552 // Validate the parameters of the RPC service method.
553 $this->validateMethodParams($params, '{
556 "vdevtype":{"type":"string","enum":["basic","mirror",' .
557 '"raidz1","raidz2","raidz3"]},
558 "name":{"type":"string"},
559 "devices":{"type":"string"},
560 "force":{"type":"boolean"},
561 "diskpath":{"type":"boolean"}
564 $pool = new OMVModuleZFSZpool($params['name']);
565 switch ($params['vdevtype']) {
567 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSPLAIN;
570 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSMIRROR;
573 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ1;
576 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ2;
579 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ3;
582 throw new OMVModuleZFSException("Incorrect pool type specified");
585 if ($params['force']) {
588 $disks = preg_split("/[,;]/", $params['devices']);
589 foreach ($disks as $disk) {
590 OMVModuleZFSUtil::setGPTLabel($disk);
592 //Use /dev/disk/by-path as suggested in ZoL FAQ.
593 if ($params['diskpath']) {
595 if (file_exists("/dev/disk/by-path/")) {
596 $tmp_disks = array();
597 foreach ($disks as $disk) {
598 $tmp_disks[] = OMVModuleZFSUtil::getDiskPath($disk);
602 } catch (OMVModuleZFSException $e) {
603 //Do nothing if an exception is thrown
606 $vdev[] = new OMVModuleZFSVdev($params['name'], $pooltype, $disks);
607 $pool->addVdev($vdev, $opts);
608 //Ugly fix to solve the problem of blkid not displaying info on newly created pools
610 $pool->import($pool->getName());
614 // Register the RPC service.
615 $rpcServiceMgr = &OMVRpcServiceMgr::getInstance(); // Get the "root" instance for the Services
616 $rpcServiceMgr->registerService(new OMVRpcServiceZFS()); // Register a new instance of the RPC service described above