provision.inc

The provisioning framework API.

API functions that are used by the provisioning framework to provide structure to the provisioning modules.

Functions

Namesort descending Description
provision_api_version Aegir API implemented by this backend
provision_autoload Return an instance of the provision autoloader.
provision_autoload_register_prefix Register a PECL style prefix with the provision autoloader.
provision_backend_invoke Execute a command against a specific context object.
provision_class_directory Return the directory containing the file a class is defined in.
provision_class_file Return the file a class is defined in.
provision_current_user Find the username of the current running procses
provision_fqdn return the FQDN of the machine or provided host
provision_get_base_url Retrieve a base_url for the currently active site.
provision_is_local_host Make a determination whether or not the given host is local or not.
provision_normalise_context_name Normalise a context name, ensuring that it starts with one '@'.
provision_password Generate a random alphanumeric password.
provision_posix_groupname Return the valid system groupname for $group.
provision_posix_username Return the valid system username for $user.
provision_save_platform_data Save modified options to the drushrc.php file
provision_save_server_data Save modified options to the drushrc.php file
provision_save_site_data
provision_user_in_group Check whether a user is a member of a group.
provision_version the aegir version of the backend
provision_version_parts The different parts of the version number
_provision_file_check_location Convenience copy of Drupal 6's file_check_location()
_provision_recursive_delete Remove files or directories, recursively
_scrub_object This is a helper function which changes deeply nested objects into arrays

Classes

Namesort descending Description
provision Provision class.
provisionException
provisionException_continue Signal for parent to continue processing.

File

provision.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * The provisioning framework API.
  5. *
  6. * API functions that are used by the provisioning framework to provide
  7. * structure to the provisioning modules.
  8. */
  9. drush_errors_on();
  10. /**
  11. * Return an instance of the provision autoloader.
  12. *
  13. * This will instiatate an instance if it needs to.
  14. */
  15. function provision_autoload() {
  16. static $instance = NULL;
  17. if (is_null($instance)) {
  18. require_once dirname(__FILE__) . '/Symfony/Component/ClassLoader/UniversalClassLoader.php';
  19. $instance = new UniversalClassLoader();
  20. // Activate the autoloader.
  21. $instance->register();
  22. }
  23. return $instance;
  24. }
  25. /**
  26. * Register a PECL style prefix with the provision autoloader.
  27. *
  28. * @param string $prefix
  29. * The class prefix to register.
  30. * @param string $dir
  31. * The directory to search for the classes in.
  32. * @param bool $prepend
  33. * If the directory should be searched first for the classes in the given
  34. * prefix, set this to TRUE, otherwise, the default, FALSE, is fine.
  35. */
  36. function provision_autoload_register_prefix($prefix, $dir, $prepend = FALSE) {
  37. // Get any current directories set for this prefix.
  38. $current_prefixes = provision_autoload()->getPrefixes();
  39. if (isset($current_prefixes[$prefix])) {
  40. $dirs = $current_prefixes[$prefix];
  41. }
  42. else {
  43. $dirs = array();
  44. }
  45. // Now add the new one.
  46. if ($prepend) {
  47. array_unshift($dirs, $dir);
  48. }
  49. else {
  50. array_push($dirs, $dir);
  51. }
  52. // Set the prefixes.
  53. provision_autoload()->registerPrefix($prefix, $dirs);
  54. }
  55. // Add our prefix to the autoloader.
  56. provision_autoload_register_prefix('Provision_', dirname(__FILE__));
  57. /**
  58. * Return the directory containing the file a class is defined in.
  59. *
  60. * @param string $class_name
  61. * The class name to search for.
  62. *
  63. * @return string
  64. * A directory if the class can be found or an empty string if not.
  65. */
  66. function provision_class_directory($class_name) {
  67. return dirname(provision_class_file($class_name));
  68. }
  69. /**
  70. * Return the file a class is defined in.
  71. *
  72. * @param string $class_name
  73. * The class name to search for.
  74. *
  75. * @return string
  76. * A file if the class can be found or an empty string if not.
  77. */
  78. function provision_class_file($class_name) {
  79. if (class_exists($class_name)) {
  80. $reflect = new reflectionClass($class_name);
  81. return $reflect->getFilename();
  82. }
  83. return '';
  84. }
  85. /**
  86. * @defgroup sitedata Site data management utility functions.
  87. * @{
  88. * The provision framework maintains a site.php file in the sites directory, to maintain additional
  89. * information from the front end, as well as providing a change history of setting changes.
  90. *
  91. * These functions load, save and manage changes made to the site data. This data has diagnostic and infrastructure
  92. * values, that allow sites to be more easily moved between different provisioned platforms.
  93. */
  94. /**
  95. * Make a determination whether or not the given host is local or not.
  96. *
  97. * We needed to fork this from drush core to handle the case sensitivity in host names.
  98. *
  99. * @param host
  100. * A hostname, 'localhost' or '127.0.0.1'.
  101. *
  102. * @return
  103. * True if the host is local.
  104. */
  105. function provision_is_local_host($host) {
  106. $host = strtolower($host);
  107. // In order for this to work right, you must use 'localhost' or '127.0.0.1'
  108. // or the machine returned by 'uname -n' for your 'remote-host' entry in
  109. // your site alias. Note that sometimes 'uname -n' does not return the
  110. // correct value. To fix it, put the correct hostname in /etc/hostname
  111. // and then run 'hostname -F /etc/hostname'.
  112. return ($host == 'localhost') ||
  113. ($host == '127.0.0.1') ||
  114. (gethostbyname($host) == '127.0.0.1') ||
  115. (gethostbyname($host) == '127.0.1.1') || // common setting on
  116. // ubuntu and friends
  117. ($host == strtolower(php_uname('n'))) ||
  118. ($host == provision_fqdn());
  119. }
  120. /**
  121. * return the FQDN of the machine or provided host
  122. *
  123. * this replicates hostname -f, which is not portable
  124. */
  125. function provision_fqdn($host = NULL) {
  126. if (is_null($host)) {
  127. $host = php_uname('n');
  128. }
  129. return strtolower(gethostbyaddr(gethostbyname($host)));
  130. }
  131. /**
  132. * Retrieve a base_url for the currently active site.
  133. *
  134. * TODO: when we actually support HTTPS, do this correctly.
  135. */
  136. function provision_get_base_url() {
  137. $base_url = 'http://' . d()->uri;
  138. $http_port = d()->server->http_port;
  139. if (!is_null($http_port) && ($http_port != 80)) {
  140. $base_url .= ':' . $http_port;
  141. }
  142. return $base_url;
  143. }
  144. /**
  145. * Save modified options to the drushrc.php file
  146. */
  147. function provision_save_server_data() {
  148. if (!drush_get_error()) {
  149. $config = new Provision_Config_Drushrc_Server(d()->name);
  150. $config->write();
  151. }
  152. }
  153. function provision_save_site_data() {
  154. if (!drush_get_error()) {
  155. $config = new Provision_Config_Drushrc_Site(d()->name);
  156. $config->write();
  157. provision_drupal_push_site();
  158. }
  159. }
  160. /**
  161. * Save modified options to the drushrc.php file
  162. */
  163. function provision_save_platform_data() {
  164. if (!drush_get_error()) {
  165. $config = new Provision_Config_Drushrc_Platform(d()->name);
  166. $config->write();
  167. provision_drupal_push_site();
  168. }
  169. }
  170. /**
  171. * @} End of "defgroup sitedata".
  172. */
  173. /**
  174. * Remove files or directories, recursively
  175. *
  176. * This was taken from Drupal 7's file.inc, with slight modifications:
  177. * * carry error codes along the way (returns TRUE only if all operations return TRUE)
  178. * * remove any type of files encountered (not files and directories)
  179. * * do not follow symlink directories
  180. *
  181. * @see file_unmanaged_delete_recursive()
  182. */
  183. function _provision_recursive_delete($path) {
  184. $ret = 1;
  185. // is_dir() follows symlinks, so it can return true on a symlink
  186. if (is_dir($path) && !is_link($path)) {
  187. $d = dir($path);
  188. if (!empty($d)) {
  189. while (($entry = $d->read()) !== FALSE) {
  190. if ($entry == '.' || $entry == '..') {
  191. continue;
  192. }
  193. $entry_path = $path . '/' . $entry;
  194. $ret &= _provision_recursive_delete($entry_path);
  195. }
  196. $d->close();
  197. }
  198. $rm = provision_file()->rmdir($path)
  199. ->fail('Deleting @path directory failed.')
  200. ->status();
  201. $ret = $ret && $rm;
  202. }
  203. else {
  204. $rm = provision_file()->unlink($path)
  205. ->fail('Deleting @path file failed.')
  206. ->status();
  207. $ret = $ret && $rm;
  208. }
  209. return $ret;
  210. }
  211. /**
  212. * Convenience copy of Drupal 6's file_check_location()
  213. *
  214. * Check if a file is really located inside $directory. Should be used to make
  215. * sure a file specified is really located within the directory to prevent
  216. * exploits.
  217. *
  218. * @code
  219. * // Returns FALSE:
  220. * file_check_location('/www/example.com/files/../../../etc/passwd', '/www/example.com/files');
  221. * @endcode
  222. *
  223. * @param $source A string set to the file to check.
  224. * @param $directory A string where the file should be located.
  225. * @return 0 for invalid path or the real path of the source.
  226. *
  227. * @see file_check_location()
  228. */
  229. function _provision_file_check_location($source, $directory = '') {
  230. $check = realpath($source);
  231. if ($check) {
  232. $source = $check;
  233. }
  234. else {
  235. // This file does not yet exist
  236. $source = realpath(dirname($source)) .'/'. basename($source);
  237. }
  238. $directory = realpath($directory);
  239. if ($directory && strpos($source, $directory) !== 0) {
  240. return 0;
  241. }
  242. return $source;
  243. }
  244. /**
  245. * Find the username of the current running procses
  246. *
  247. * This will return the username of the current running user (as seen
  248. * from posix_geteuid()) and should be used instead of
  249. * get_current_user() (which looks at the file owner instead).
  250. *
  251. * @see get_current_user()
  252. * @see posix_geteuid()
  253. *
  254. * @return
  255. * String. The username.
  256. */
  257. function provision_current_user() {
  258. return provision_posix_username(posix_geteuid());
  259. }
  260. /**
  261. * Check whether a user is a member of a group.
  262. *
  263. * @param user
  264. * username or user id of user.
  265. * @param group
  266. * groupname or group id of group.
  267. *
  268. * @return
  269. * Boolean. True if user does belong to group,
  270. * and FALSE if the user does not belong to the group, or either the user or group do not exist.
  271. */
  272. function provision_user_in_group($user, $group) {
  273. // TODO: make these singletons with static variables for caching.
  274. $user = provision_posix_username($user);
  275. $group = provision_posix_groupname($group);
  276. if ($user && $group) {
  277. $info = posix_getgrnam($group);
  278. if (in_array($user, $info['members'])) {
  279. return TRUE;
  280. }
  281. }
  282. return FALSE;
  283. }
  284. /**
  285. * Return the valid system username for $user.
  286. *
  287. * @return
  288. * Returns the username if found, otherwise returns FALSE
  289. */
  290. function provision_posix_username($user) {
  291. // TODO: make these singletons with static variables for caching.
  292. // we do this both ways, so that the function returns NULL if no such user was found.
  293. if (is_numeric($user)) {
  294. $info = posix_getpwuid($user);
  295. $user = $info['name'];
  296. }
  297. else {
  298. $info = posix_getpwnam($user);
  299. $user = $info['name'];
  300. }
  301. return $user;
  302. }
  303. /**
  304. * Return the valid system groupname for $group.
  305. *
  306. * @return
  307. * Returns the groupname if found, otherwise returns FALSE
  308. */
  309. function provision_posix_groupname($group) {
  310. // TODO: make these singletons with static variables for caching.
  311. // we do this both ways, so that the function returns NULL if no such user was found.
  312. if (is_numeric($group)) {
  313. $info = posix_getgrgid($group);
  314. $group = $info['name'];
  315. }
  316. else {
  317. $info = posix_getgrnam($group);
  318. $group = $info['name'];
  319. }
  320. return $group;
  321. }
  322. /**
  323. * Generate a random alphanumeric password.
  324. *
  325. * This is a copy of Drupal core's user_password() function. We keep it
  326. * here in case we need this and don't have a bootstrapped Drupal
  327. * around.
  328. *
  329. * @see user_password()
  330. */
  331. function provision_password($length = 10) {
  332. // This variable contains the list of allowable characters for the
  333. // password. Note that the number 0 and the letter 'O' have been
  334. // removed to avoid confusion between the two. The same is true
  335. // of 'I', 1, and 'l'.
  336. $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
  337. // Zero-based count of characters in the allowable list:
  338. $len = strlen($allowable_characters) - 1;
  339. // Declare the password as a blank string.
  340. $pass = '';
  341. // Loop the number of times specified by $length.
  342. for ($i = 0; $i < $length; $i++) {
  343. // Each iteration, pick a random character from the
  344. // allowable string and append it to the password:
  345. $pass .= $allowable_characters[mt_rand(0, $len)];
  346. }
  347. return $pass;
  348. }
  349. /**
  350. * This is a helper function which changes deeply nested objects into arrays
  351. *
  352. * This helps get past the face that objects are not simple to work with, or
  353. * save in context files.
  354. *
  355. * This function 'misuses' a side effect of the json_decode function's second
  356. * parameter. As this is done in C, and the structures we are manipulating
  357. * aren't that large, it should be performant enough.
  358. */
  359. function _scrub_object($input) {
  360. return json_decode(json_encode($input), TRUE);
  361. }
  362. /**
  363. * Execute a command against a specific context object.
  364. *
  365. * @param $target
  366. * the context to operate on, @ prefix is optional.
  367. * @param $command
  368. * drush command passed to drush_invoke_process().
  369. * @param $arguments
  370. * drush arguments passed to drush_invoke_process().
  371. * @param $data
  372. * drush data passed to drush_invoke_process().
  373. * @param $mode
  374. * drush IPC mode (GET/POST) passed to drush_invoke_process().
  375. *
  376. * @see drush_invoke_process()
  377. */
  378. function provision_backend_invoke($target, $command, $arguments = array(), $data = array(), $mode = 'GET') {
  379. $context = '@' . ltrim($target, '@');
  380. return drush_invoke_process($context, $command, $arguments, $data, array('method' => $mode, 'integrate' => TRUE, 'dispatch-using-alias' => TRUE));
  381. }
  382. /**
  383. * the aegir version of the backend
  384. *
  385. * @return string
  386. * the aegir version as stored in the .info file, potentially
  387. * including the 6.x- prefix. to get a cleaned up version, use
  388. * provision_version_parts()
  389. *
  390. * @see provision_version_parts()
  391. */
  392. function provision_version() {
  393. $ini = parse_ini_file(dirname(__FILE__) . '/provision.info');
  394. return $ini['version'];
  395. }
  396. /**
  397. * Aegir API implemented by this backend
  398. *
  399. * This is the major release number, the first part of the version
  400. * stored in the info file
  401. *
  402. * @return int
  403. * a number greater than zero, 1 for 1.0 or 1.0-rc2, 2 for 2.0, etc.
  404. *
  405. * @see provision_version_parts()
  406. */
  407. function provision_api_version() {
  408. $parts = provision_version_parts();
  409. return $parts[0];
  410. }
  411. /**
  412. * The different parts of the version number
  413. *
  414. * This cleans up the version number by removing the Drupal version
  415. * (6.x-...) and splits the remaining version on dots.
  416. *
  417. * @return array
  418. * the major and minor version numbers, e.g. array(1, 0-rc3) for
  419. * 1.0-rc3 or array(1, 2) for 1.2
  420. */
  421. function provision_version_parts() {
  422. $version = preg_replace('/^[^-]*-/', '', provision_version()); // remove "6.x-"
  423. return explode('.', $version);
  424. }
  425. /**
  426. * Normalise a context name, ensuring that it starts with one '@'.
  427. *
  428. * @param $name
  429. * The context name to normalise.
  430. *
  431. * @return
  432. * The normalised context name.
  433. */
  434. function provision_normalise_context_name($name) {
  435. return '@' . ltrim($name, '@');
  436. }
  437. // Base class for provision exceptions.
  438. class provisionException extends Exception {
  439. }
  440. /**
  441. * Signal for parent to continue processing.
  442. *
  443. * The primary use for this class is for the config
  444. * classes to be able to signal to it's caller, that
  445. * the configuration file was not needed, and to
  446. * continue on.
  447. */
  448. class provisionException_continue extends provisionException {
  449. }
  450. /**
  451. * Provision class.
  452. *
  453. * This is just a container for some useful static methods.
  454. */
  455. class provision {
  456. /**
  457. * The actual body of the method_invoke function.
  458. *
  459. * This is a static method so it can be re-used by some other classes
  460. * that aren't contexts. (notably services and configs).
  461. */
  462. static function method_invoke($object, $func, $args = array()) {
  463. if (method_exists($object, $func)) {
  464. return call_user_func_array(array($object, $func), $args);
  465. }
  466. }
  467. }
  468. include_once('provision.context.inc');
  469. include_once('provision.service.inc');
  470. include_once('provision.file.inc');