Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
system_apply_metadata_schema.php
1 <?php
26 error_reporting(E_ALL);
27 if (ini_get('memory_limit') != '-1') ini_set('memory_limit', '-1');
28 if ((php_sapi_name() != 'cli')) {
29  trigger_error("You can only run this script from the command line\n", E_USER_ERROR);
30 }//end if
31 
32 $config = array();
33 
34 define('LOG_FILE_NAME', 'set_permissions.log');
35 
36 if (count($argv) < 3) {
37  usage();
38  exit();
39 }//end if
40 
41 // Get config from command line args.
42 process_args($config);
43 
44 
45 require_once $config['system_root'].'/core/include/init.inc';
46 
47 define('LOG_FILE', SQ_SYSTEM_ROOT.'/data/private/logs/'.LOG_FILE_NAME); // This is the log file
48 define('SYNCH_FILE', SQ_TEMP_PATH.'/set_metadata_schema.assetid'); // We need this file to store the assetids
49 
50 
51 // Replace space with empty string
52 $assetids = preg_replace('/[\s]*/', '', $config['assetids']);
53 
54 // Explode them so we have the list in array
55 $rootnodes = getRootNodes($assetids);
56 
57 _disconnectFromMatrixDatabase();
58 $pid_prepare = pcntl_fork();
59  switch ($pid_prepare) {
60  case -1:
61  break;
62  case 0:
63  // Connect to DB within the child process
64  _connectToMatrixDatabase();
65 
66  $root_user = $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('root_user');
67 
68  // This ridiculousness allows us to workaround Oracle, forking and CLOBs
69  // if a query is executed that returns more than 1 LOB before a fork occurs,
70  // the Oracle DB connection will be lost inside the fork.
71  // In this case, because a user asset has more than 1 attribute and custom_val in sq_ast_attr_val
72  // is of type CLOB, we attempt to check the root password inside our forked process.
73  // log in as root
74  if (!$GLOBALS['SQ_SYSTEM']->setCurrentUser($root_user)) {
75  echo "Failed logging in as root user\n";
76  exit(1);
77  }//end if
78 
79  // Check the schema exists
80  $schema_info = $GLOBALS['SQ_SYSTEM']->am->getAssetInfo($config['schemaid']);
81  if ($schema_info) {
82  if ($schema_info[$config['schemaid']]['type_code'] != 'metadata_schema') {
83  trigger_error("Asset {$config['schemaid']} is not a metadata schema\n", E_USER_ERROR);
84  }
85  } else {
86  trigger_error("Provided schemaid {$config['schemaid']} is not an asset\n", E_USER_ERROR);
87  }
88 
89  $children = $rootnodes; // Start off with the root nodes.
90  foreach ($rootnodes as $rootnode_id) {
91  $children += array_merge($children, array_keys(($GLOBALS['SQ_SYSTEM']->am->getChildren($rootnode_id))));
92 
93  }//end foreach
94 
95  // Save the list into a file so we can access the list from the parent process
96  file_put_contents(SYNCH_FILE, implode(',', $children));
97 
98  // Disconnect from DB
99  _disconnectFromMatrixDatabase();
100 
101  exit(0);
102  // waiting for child exit signal
103  $status = null;
104  pcntl_waitpid(-1, $status);
105 
106  break;
107  default:
108  $status = null;
109  pcntl_waitpid(-1, $status);
110  break;
111  }//end switch
112 
113 $children = Array();
114 if (file_exists(SYNCH_FILE)) {
115  $children_str = file_get_contents(SYNCH_FILE);
116 } else {
117  echo "Unable to find Synch File, probably because the root user was not able to log in, or the user executing this script does not have permission to write to this folder.\n";
118  exit(0);
119 }//end else
120 
121 $children = explode(',', $children_str);
122 $children = array_unique($children); // We are only generate metadata for each asset once, despite they might be linked in different plaecs
123 
124 // Chunk them up so we can process each batch when forking
125 $chunk_children = array_chunk($children, $config['batch_size']);
126 $current_child_list = Array();
127 
128 log_to_file('======================= Start Applying Metadata Schema'.date('d-m-Y h:i:s').' =======================', LOG_FILE);
129 log_to_file("Applying schema for: " . var_export(count($children),TRUE) . " assets \n", LOG_FILE);
130 
131  $fork_num = 0; // Determine how many child process we have forked
132  while (!empty($chunk_children)) {
133  $current_child_list = array_pop($chunk_children);
134  $current_remaining = count($current_child_list);
135  $pid = pcntl_fork();
136  $fork_num++;
137  switch ($pid) {
138  case -1:
139  trigger_error('Process failed to fork while regenerating metadata', E_USER_ERROR);
140  exit(1);
141  break;
142  case 0:
143  // Connect to DB within the child process
144  _connectToMatrixDatabase();
145  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
146 
147  $conn_id = MatrixDAL::getCurrentDbId();
148 
149 
150  $mm = $GLOBALS['SQ_SYSTEM']->getMetadataManager();
151 
152  foreach ($current_child_list as $child_assetid) {
153  $child_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($child_assetid);
154  if (!$GLOBALS['SQ_SYSTEM']->am->acquireLock($child_assetid, 'metadata')) {
155  log_to_file('Unable to acquire metadata lock for assetid ' .$child_assetid.'. Skipping this asset.', LOG_FILE);
156  continue;
157  }//end if
158 
159  if (!$child_asset->writeAccess('metadata')) {
160  log_to_file('Do not have write access for assetid ' .$child_assetid .'. Skipping this asset.', LOG_FILE);
161  $GLOBALS['SQ_SYSTEM']->am->releaseLock($child_assetid, 'metadata');
162  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($child_asset);
163  continue;
164  }//end if
165 
166  if ($config['delete'] === TRUE) {
167  // Delete schema
168  if (!$mm->deleteSchema($child_assetid, $config['schemaid'])) {
169  log_to_file('Failed deleting metadata schema for assetid ' . $child_assetid . '.', LOG_FILE);
170  continue;
171  }
172 
173  } else {
174  // Set Schema
175  if (!$mm->setSchema($child_assetid, $config['schemaid'], $config['access'], $config['cascades'], $config['force'])) {
176  log_to_file('Failed applying metadata schema for assetid ' .$child_assetid .'.', LOG_FILE);
177  continue;
178  }//end if
179 
180  log_to_file('Metadata schema '.$config['schemaid'].' applied for child assetid '.$child_assetid, LOG_FILE);
181  }
182 
183  if (!$mm->regenerateMetadata($child_assetid, NULL, $config['update_assets'])) {
184  log_to_file('Failed regenerating metadata for assetid ' .$child_assetid .'.', LOG_FILE);
185  $GLOBALS['SQ_SYSTEM']->am->releaseLock($child_assetid, 'metadata');
186  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($child_asset);
187  continue;
188  }//end if
189 
190  log_to_file('Regenerated Metadata for child assetid '.$child_assetid, LOG_FILE);
191 
192  $current_remaining--;
193  echo '-- Remaining: ' . (string) ($current_remaining + (count($chunk_children) * $config['batch_size'])) . "\n";
194 
195  $GLOBALS['SQ_SYSTEM']->am->releaseLock($child_assetid, 'metadata');
196  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($child_asset);
197  $child_asset = NULL;
198  unset($child_asset);
199 
200  }//end foreach
201 
202  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
203  // Disconnect from DB
204  _disconnectFromMatrixDatabase();
205 
206  exit(0);
207  // waiting for child exit signal
208  $status = null;
209  pcntl_waitpid(-1, $status);
210 
211  break;
212  default:
213  // We only want to fork a maximum number of child process, so if we've already reached the max num, sit and wait
214  if ($fork_num >= $config['max_threads']) {
215  $status = null;
216  pcntl_waitpid(-1, $status);
217  $fork_num--;
218  }//end if
219 
220  if (empty($chunk_children)) {
221  // We wait for all the fork child to finish
222  while ($fork_num > 0) {
223  $status = null;
224  pcntl_waitpid(-1, $status);
225  $fork_num--;
226  }//end
227  }//end if
228 
229  break;
230 
231  }//end switch & thread
232  //}//end foreach
233  }//end while
234 
235 
236  log_to_file('======================= Finished Applying Metadata Schema '.date('d-m-Y h:i:s').' =======================', LOG_FILE);
237  if (file_exists(SYNCH_FILE)) {
238  unlink(SYNCH_FILE);
239  }//end if
240  exit(0);
241 
242 
243 
244 
250 function usage() {
251  echo "\n";
252  echo "Usage: php {$_SERVER['argv'][0]} <system_root> <assetid[,assetid]> <schema>\n";
253  echo " php {$_SERVER['argv'][0]} <system_root> <assetid[,assetid]> <schema> [--access <granted|denied>] [-nc] [-d] [-f] [-u]\n";
254  echo " php {$_SERVER['argv'][0]} <system_root> <assetid[,assetid]> <schema> [--max-threads <max_threads>] [--batch-size <batch_size>]\n";
255  echo "\n";
256  echo " --access granted|denied Set the schema access. Default is 'granted'.\n";
257  echo " -nc No cascade. Don't set the cascade flag, which allows new assets to inherit the schema.\n";
258  echo " -d Delete schema. Ignores most other options and simply removes the schema.\n";
259  echo " -f Force application of the schema, even if the schema is already applied or granted/denied are in conflict.\n";
260  echo " -u Notify each asset that it has been updated.\n";
261  echo " --max-threads Maximum concurrency. Default is 3, max allowed is 5.\n";
262  echo " --batch-size Default is 50.\n";
263  echo "\n";
264  echo "Results are logged to data/private/logs/".LOG_FILE_NAME."\n";
265 
266 }//end usage()
267 
268 
275 function process_args(&$config) {
276 
277  $config['system_root'] = (isset($_SERVER['argv'][1])) ? $_SERVER['argv'][1] : '';
278  if (empty($config['system_root'])) {
279  echo "ERROR: You need to supply the path to the System Root as the first argument\n";
280  exit();
281  }
282 
283  if (!is_dir($config['system_root']) || !is_readable($config['system_root'].'/core/include/init.inc')) {
284  echo "ERROR: Path provided doesn't point to a Matrix installation's System Root. Please provide correct path and try again.\n";
285  exit();
286  }
287 
288  $config['assetids'] = (isset($_SERVER['argv'][2])) ? $_SERVER['argv'][2] : '';
289  if (empty($config['assetids'])) {
290  echo "ERROR: You need to specify the root nodes to apply the schema from as the second argument\n";
291  exit();
292  }//end if
293 
294  $config['schemaid'] = (isset($_SERVER['argv'][3])) ? $_SERVER['argv'][3] : '';
295  if (empty($config['schemaid'])) {
296  echo "ERROR: You need to specify a schema to apply as the third argument\n";
297  exit();
298  }//end if
299 
300  $config['access'] = get_parameterised_arg('--access', 'granted');
301  if ($config['access'] == 'denied') {
302  $config['granted'] = FALSE;
303  } else {
304  $config['granted'] = TRUE;
305  }//end if
306 
307  $config['cascades'] = !get_boolean_arg('-nc'); // Invert.. -nc means no cascade.
308  $config['force'] = get_boolean_arg('-f');
309  $config['update_assets'] = get_boolean_arg('-u');
310  $config['delete'] = get_boolean_arg('-d');
311 
312  $config['max_threads'] = (int) get_parameterised_arg('--max-threads', 3);
313  if ($config['max_threads'] > 5) {
314  $config['max_threads'] = 5;
315  } elseif ($config['max_threads'] < 1) {
316  $config['max_threads'] = 1;
317  }//end if
318 
319  $config['batch_size'] = (int) get_parameterised_arg('--batch-size', 50);
320  if ($config['batch_size'] < 5) {
321  $config['batch_size'] = 50;
322  }
323 
324  echo "\n";
325  if ($config['delete'] === TRUE) {
326  echo "Will attempt to delete schema {$config['schemaid']} from asset(s) {$config['assetids']}.\n\n";
327  echo " Update assets: {$config['update_assets']}\n";
328  echo " Max threads: {$config['max_threads']}\n";
329  echo " Batch size: {$config['batch_size']}\n";
330  } else {
331  echo "Will attempt to set schema {$config['schemaid']} to asset(s) {$config['assetids']}.\n\n";
332  echo " Access: {$config['access']}\n";
333  echo " Cascade: {$config['cascades']}\n";
334  echo " Force: {$config['force']}\n";
335  echo " Update assets: {$config['update_assets']}\n";
336  echo " Max threads: {$config['max_threads']}\n";
337  echo " Batch size: {$config['batch_size']}\n";
338  }
339  echo "\n";
340 
341  // Check for potential hazards
342  if (($config['access'] == 'denied') && ($config['force'] === TRUE)) {
343  echo "WARNING: You are attempting to 'force' application of a schema as 'denied', which can lead to unpredictable results.\n";
344  echo "A schema already applied with 'grant' cannot be subsequently 'denied', it must first be removed.\n";
345 
346  // ask for the root password for the system
347  echo 'Are you sure you want to continue? (y/N): ';
348  $force_denied = rtrim(fgets(STDIN, 4094));
349 
350  if (strtoupper($force_denied) != 'Y') {
351  exit;
352  }
353  }
354 
355 }//end function processArgs()
356 
357 
364 function get_boolean_arg($arg) {
365  $key = array_search($arg, $_SERVER['argv']);
366  if ($key) {
367  return TRUE;
368  } else {
369  return FALSE;
370  }//end if
371 
372 }//end get_boolean_arg()
373 
374 
382 function get_parameterised_arg($arg, $default) {
383  $key = array_search($arg, $_SERVER['argv']);
384  // Look for the parameter to this arg
385  if (($key) && (isset($_SERVER['argv'][$key + 1]))) {
386  return $_SERVER['argv'][$key + 1];
387  } else {
388  return $default;
389  }
390 
391 }//end get_parameterised_arg()
392 
393 
402 function get_line($prompt='')
403 {
404  echo $prompt;
405  // now get their entry and remove the trailing new line
406  return rtrim(fgets(STDIN, 4096));
407 
408 }//end get_line()
409 
410 
417 function _disconnectFromMatrixDatabase()
418 {
419  $conn_id = MatrixDAL::getCurrentDbId();
420  if (isset($conn_id) && !empty($conn_id)) {
422  MatrixDAL::dbClose($conn_id);
423  }//end if
424 
425 }//end _disconnectFromMatrixDatabase()
426 
427 
434 function _connectToMatrixDatabase()
435 {
436  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
437 
438 }//end _connectToMatrixDatabase()
439 
440 
445 function log_to_file($content, $file_name="set_metadata_schema.log")
446 {
447  file_put_contents($file_name, '['.date('d-m-Y h:i:s').'] '.$content."\n", FILE_APPEND);
448 
449 }//end log_to_file();
450 
451 
452 function getRootNodes($action)
453 {
454  $rootnodes = explode(',', $action);
455 
456  // Check if each of these rootnodes exists in the system
457  $rootnodes_exists = $GLOBALS['SQ_SYSTEM']->am->assetExists($rootnodes);
458 
459  $not_exists = array_diff($rootnodes, $rootnodes_exists);
460  if (!empty($not_exists)) {
461  $list_not_exists = implode(', ', $not_exists);
462  echo "These rootnode ids do not exists in the system: $list_not_exists \n";
463  exit();
464  }//end if
465 
466  return $rootnodes;
467 }//end getRootNodes()
468 
469 ?>