Added access controls for encounter categories
[openemr.git] / library / ajax / execute_background_services.php
blob65233afe515fa788475e0bc4f4db2cb2e9bac5e7
1 <?php
2 /**
3 * Manage background operations that should be executed at intervals.
5 * This script may be executed by a suitable Ajax request, by a cron job, or both.
7 * When called from cron, optinal args are [site] [service] [force]
8 * @param site to specify a specific site, 'default' used if omitted
9 * @param service to specify a specific service, 'all' used if omitted
10 * @param force '1' to ignore specified wait interval, '0' to honor wait interval
12 * The same parameters can be accessed via Ajax using the $_POST variables
13 * 'site', 'background_service', and 'background_force', respectively.
15 * For both calling methods, this script guarantees that each active
16 * background service function: (1) will not be called again before it has completed,
17 * and (2) will not be called any more frequently than at the specified interval
18 * (unless the force execution flag is used). A service function that is already running
19 * will not be called a second time even if the force execution flag is used.
21 * Notes for the default background behavior:
22 * 1. If the Ajax method is used, services will only be checked while
23 * Ajax requests are being received, which is currently only when users are
24 * logged in.
25 * 2. All services are checked and called sequentially in the order specified
26 * by the sort_order field in the background_services table. Service calls that are "slow"
27 * should be given a higher sort_order value.
28 * 3. The actual interval between two calls to a given background service may be
29 * as long as the time to complete that service plus the interval between
30 * n+1 calls to this script where n is the number of other services preceding it
31 * in the array, even if the specified minimum interval is shorter, so plan
32 * accordingly. Example: with a 5 min cron interval, the 4th service on the list
33 * may not be started again for up to 20 minutes after it has completed if
34 * services 1, 2, and 3 take more than 15, 10, and 5 minutes to complete,
35 * respectively.
37 * Copyright (C) 2013 EMR Direct <http://www.emrdirect.com/>
39 * LICENSE: This program is free software; you can redistribute it and/or
40 * modify it under the terms of the GNU General Public License
41 * as published by the Free Software Foundation; either version 2
42 * of the License, or (at your option) any later version.
43 * This program is distributed in the hope that it will be useful,
44 * but WITHOUT ANY WARRANTY; without even the implied warranty of
45 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
46 * GNU General Public License for more details.
47 * You should have received a copy of the GNU General Public License
48 * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>;.
50 * @package OpenEMR
51 * @author EMR Direct <http://www.emrdirect.com/>
52 * @link http://www.open-emr.org
57 //ajax param should be set by calling ajax scripts
58 $isAjaxCall = isset($_POST['ajax']);
60 //if false, we may assume this is a cron job and set up accordingly
61 if (!$isAjaxCall) {
62 $ignoreAuth = 1;
63 //process optional arguments when called from cron
64 $_GET['site'] = (isset($argv[1])) ? $argv[1] : 'default';
65 if (isset($argv[2]) && $argv[2]!='all') $_GET['background_service'] = $argv[2];
66 if (isset($argv[3]) && $argv[3]=='1') $_GET['background_force'] = 1;
69 //an additional require file can be specified for each service in the background_services table
70 require_once(dirname(__FILE__) . "/../../interface/globals.php");
72 //Remove time limit so script doesn't time out
73 set_time_limit(0);
75 //Release session lock to prevent freezing of other scripts
76 session_write_close();
78 //Safety in case one of the background functions tries to output data
79 ignore_user_abort(1);
81 /**
82 * Execute background services
83 * This function reads a list of available services from the background_services table
84 * For each service that is not already running and is due for execution, the associated
85 * background function is run.
87 * Note: Each service must do its own logging, as appropriate, and should disable itself
88 * to prevent continued service calls if an error condition occurs which requires
89 * administrator intervention. Any service function return values and output are ignored.
92 function execute_background_service_calls()
94 /**
95 * Note: The global $service_name below is set to the name of the service currently being
96 * processed before the actual service function call, and is unset after normal
97 * completion of the loop. If the script exits abnormally, the shutdown_function
98 * uses the value of $service_name to do any required clean up.
100 global $service_name;
102 $single_service = isset($_REQUEST['background_service']) ? $_REQUEST['background_service'] : '';
103 $force = (isset($_REQUEST['background_force']) && $_REQUEST['background_force']);
105 $sql = 'SELECT * FROM background_services WHERE ' . ($force ? '1' : 'execute_interval > 0');
106 if ($single_service!="")
107 $services = sqlStatementNoLog($sql.' AND name=?',array($single_service));
108 else
109 $services = sqlStatementNoLog($sql.' ORDER BY sort_order');
111 while($service = sqlFetchArray($services)){
112 $service_name = $service['name'];
113 if(!$service['active'] || $service['running'] == 1) continue;
114 $interval=(int)$service['execute_interval'];
116 //leverage locking built-in to UPDATE to prevent race conditions
117 //will need to assess performance in high concurrency setting at some point
118 $sql='UPDATE background_services SET running = 1, next_run = NOW()+ INTERVAL ?'
119 . ' MINUTE WHERE running < 1 ' . ($force ? '' : 'AND NOW() > next_run ') . 'AND name = ?';
120 if(sqlStatementNoLog($sql,array($interval,$service_name))===false) continue;
121 $acquiredLock = generic_sql_affected_rows();
122 if($acquiredLock<1) continue; //service is already running or not due yet
124 if ($service['require_once'])
125 require_once($GLOBALS['fileroot'] . $service['require_once']);
126 if (!function_exists($service['function'])) continue;
128 //use try/catch in case service functions throw an unexpected Exception
129 try {
130 $service['function']();
131 } catch (Exception $e) {
132 //do nothing
135 $sql = 'UPDATE background_services SET running = 0 WHERE name = ?';
136 $res = sqlStatementNoLog($sql, array($service_name));
142 * Catch unexpected failures.
144 * if the global $service_name is still set, then a die() or exit() occurred during the execution
145 * of that service's function call, and we did not complete the foreach loop properly,
146 * so we need to reset the is_running flag for that service before quitting
149 function background_shutdown()
151 global $service_name;
152 if (isset($service_name)) {
154 $sql = 'UPDATE background_services SET running = 0 WHERE name = ?';
155 $res = sqlStatementNoLog($sql, array($service_name));
160 register_shutdown_function('background_shutdown');
161 execute_background_service_calls();
162 unset($service_name);