Download | Plain Text | No Line Numbers


  1. This is proof of concept code to invalidate the opcode cache entries using
  2. fanotify events. This fails because fanotify doesn't support rename events.
  3. diff --git a/ZendAccelerator.c b/ZendAccelerator.c
  4. index 4406400..17f7cff 100644
  5. --- a/ZendAccelerator.c
  6. +++ b/ZendAccelerator.c
  7. @@ -29,6 +29,7 @@
  8. #include "zend_shared_alloc.h"
  9. #include "zend_accelerator_module.h"
  10. #include "zend_accelerator_blacklist.h"
  11. +#include "zend_accelerator_fsmonitor.h"
  12. #include "zend_list.h"
  13. #include "zend_execute.h"
  14. #include "main/SAPI.h"
  15. @@ -1048,32 +1049,21 @@ static inline char *accel_make_persistent_key(zend_file_handle *file_handle, int
  16. return accel_make_persistent_key_ex(file_handle, strlen(file_handle->filename), key_len TSRMLS_CC);
  17. }
  18.  
  19. -int zend_accel_invalidate(const char *filename, int filename_len, zend_bool force TSRMLS_DC)
  20. +int zend_accel_invalidate_real(char *filename, int filename_len, zend_bool force TSRMLS_DC)
  21. {
  22. - char *realpath;
  23. zend_persistent_script *persistent_script;
  24.  
  25. if (!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled) || accelerator_shm_read_lock(TSRMLS_C) != SUCCESS) {
  26. return FAILURE;
  27. }
  28.  
  29. -#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
  30. - realpath = accel_php_resolve_path(filename, filename_len, ZCG(include_path) TSRMLS_CC);
  31. -#else
  32. - realpath = accelerator_orig_zend_resolve_path(filename, filename_len TSRMLS_CC);
  33. -#endif
  34. -
  35. - if (!realpath) {
  36. - return FAILURE;
  37. - }
  38. -
  39. - persistent_script = zend_accel_hash_find(&ZCSG(hash), realpath, strlen(realpath) + 1);
  40. + persistent_script = zend_accel_hash_find(&ZCSG(hash), filename, filename_len + 1);
  41. if (persistent_script && !persistent_script->corrupted) {
  42. zend_file_handle file_handle;
  43.  
  44. file_handle.type = ZEND_HANDLE_FILENAME;
  45. - file_handle.filename = realpath;
  46. - file_handle.opened_path = realpath;
  47. + file_handle.filename = filename;
  48. + file_handle.opened_path = filename;
  49.  
  50. if (force ||
  51. !ZCG(accel_directives).validate_timestamps ||
  52. @@ -1096,11 +1086,34 @@ int zend_accel_invalidate(const char *filename, int filename_len, zend_bool forc
  53. }
  54.  
  55. accelerator_shm_read_unlock(TSRMLS_C);
  56. - efree(realpath);
  57.  
  58. return SUCCESS;
  59. }
  60.  
  61. +int zend_accel_invalidate(const char *filename, int filename_len, zend_bool force TSRMLS_DC)
  62. +{
  63. + char *realpath;
  64. + zend_persistent_script *persistent_script;
  65. +
  66. + if (!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled) || accelerator_shm_read_lock(TSRMLS_C) != SUCCESS) {
  67. + return FAILURE;
  68. + }
  69. +
  70. +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
  71. + realpath = accel_php_resolve_path(filename, filename_len, ZCG(include_path) TSRMLS_CC);
  72. +#else
  73. + realpath = accelerator_orig_zend_resolve_path(filename, filename_len TSRMLS_CC);
  74. +#endif
  75. +
  76. + if (!realpath) {
  77. + return FAILURE;
  78. + }
  79. +
  80. + zend_accel_invalidate_real(realpath, strlen(realpath), force);
  81. +
  82. + efree(realpath);
  83. +}
  84. +
  85. /* Adds another key for existing cached script */
  86. static void zend_accel_add_key(char *key, unsigned int key_length, zend_accel_hash_entry *bucket TSRMLS_DC)
  87. {
  88. @@ -2690,6 +2703,8 @@ static int accel_startup(zend_extension *extension)
  89. zend_accel_copy_internal_functions(TSRMLS_C);
  90. #endif
  91.  
  92. + zend_accel_fsmonitor_startup();
  93. +
  94. return SUCCESS;
  95. }
  96.  
  97. @@ -2706,6 +2721,8 @@ void accel_shutdown(TSRMLS_D)
  98. {
  99. zend_ini_entry *ini_entry;
  100.  
  101. + zend_accel_fsmonitor_signal();
  102. +
  103. zend_accel_blacklist_shutdown(&accel_blacklist);
  104.  
  105. if (!ZCG(enabled) || !accel_startup_ok) {
  106. diff --git a/ZendAccelerator.h b/ZendAccelerator.h
  107. index bba3631..a8bdf96 100644
  108. --- a/ZendAccelerator.h
  109. +++ b/ZendAccelerator.h
  110. @@ -332,6 +332,7 @@ void accel_shutdown(TSRMLS_D);
  111. void zend_accel_schedule_restart(zend_accel_restart_reason reason TSRMLS_DC);
  112. void zend_accel_schedule_restart_if_necessary(zend_accel_restart_reason reason TSRMLS_DC);
  113. int validate_timestamp_and_record(zend_persistent_script *persistent_script, zend_file_handle *file_handle TSRMLS_DC);
  114. +int zend_accel_invalidate_real(char *filename, int filename_len, zend_bool force TSRMLS_DC);
  115. int zend_accel_invalidate(const char *filename, int filename_len, zend_bool force TSRMLS_DC);
  116. int accelerator_shm_read_lock(TSRMLS_D);
  117. void accelerator_shm_read_unlock(TSRMLS_D);
  118. diff --git a/config.m4 b/config.m4
  119. index f6e6ca9..fd11e99 100644
  120. --- a/config.m4
  121. +++ b/config.m4
  122. @@ -366,6 +366,7 @@ fi
  123. PHP_NEW_EXTENSION(opcache,
  124. ZendAccelerator.c \
  125. zend_accelerator_blacklist.c \
  126. + zend_accelerator_fsmonitor.c \
  127. zend_accelerator_debug.c \
  128. zend_accelerator_hash.c \
  129. zend_accelerator_module.c \
  130. diff --git a/package.xml b/package.xml
  131. index bdd7891..3852e2c 100644
  132. --- a/package.xml
  133. +++ b/package.xml
  134. @@ -88,6 +88,7 @@
  135. <file role="src" name="shared_alloc_mmap.c"/>
  136. <file role="src" name="zend_accelerator_debug.c"/>
  137. <file role="src" name="zend_accelerator_blacklist.c"/>
  138. + <file role="src" name="zend_accelerator_fsmonitor.c"/>
  139. <file role="src" name="shared_alloc_shm.c"/>
  140. <file role="src" name="zend_accelerator_util_funcs.h"/>
  141. <file role="src" name="zend_accelerator_module.h"/>
  142. diff --git a/zend_accelerator_fsmonitor.c b/zend_accelerator_fsmonitor.c
  143. new file mode 100644
  144. index 0000000..d66228c
  145. --- /dev/null
  146. +++ b/zend_accelerator_fsmonitor.c
  147. @@ -0,0 +1,182 @@
  148. +/*
  149. + +----------------------------------------------------------------------+
  150. + | Zend OPcache |
  151. + +----------------------------------------------------------------------+
  152. + | Copyright (c) 1998-2014 The PHP Group |
  153. + +----------------------------------------------------------------------+
  154. + | This source file is subject to version 3.01 of the PHP license, |
  155. + | that is bundled with this package in the file LICENSE, and is |
  156. + | available through the world-wide-web at the following url: |
  157. + | http://www.php.net/license/3_01.txt |
  158. + | If you did not receive a copy of the PHP license and are unable to |
  159. + | obtain it through the world-wide-web, please send a note to |
  160. + | license@php.net so we can mail you a copy immediately. |
  161. + +----------------------------------------------------------------------+
  162. +*/
  163. +
  164. +#include "main/php.h"
  165. +#include "main/php_globals.h"
  166. +#include "ZendAccelerator.h"
  167. +#include "zend_accelerator_fsmonitor.h"
  168. +
  169. +#include <signal.h>
  170. +#include <fcntl.h>
  171. +#include <sys/fanotify.h>
  172. +
  173. +pid_t fsm_pid = 0;
  174. +int fsm_fd = -1;
  175. +
  176. +static void zend_accel_fsmonitor_shutdown()
  177. +{
  178. + zend_accel_error(ACCEL_LOG_DEBUG, "fsmonitor: shutdown");
  179. + close(fsm_fd);
  180. +}
  181. +
  182. +static void sig_handler(int signo)
  183. +{
  184. + int saved_errno = errno;
  185. + zend_accel_fsmonitor_shutdown();
  186. + errno = saved_errno;
  187. +}
  188. +
  189. +static int zend_accel_fsmonitor_init_signals()
  190. +{
  191. + struct sigaction act;
  192. +
  193. + memset(&act, 0, sizeof(act));
  194. + act.sa_handler = sig_handler;
  195. + sigfillset(&act.sa_mask);
  196. +
  197. + if (sigaction(SIGTERM, &act, 0) < 0) {
  198. + zend_accel_error(ACCEL_LOG_WARNING, "failed to init signals: sigaction()");
  199. + return FAILURE;
  200. + }
  201. +
  202. + return SUCCESS;
  203. +}
  204. +
  205. +static int zend_accel_fsmonitor_init()
  206. +{
  207. + zend_accel_error(ACCEL_LOG_DEBUG, "fsmonitor: init");
  208. + fsm_fd = fanotify_init(FAN_CLOEXEC, O_RDONLY | O_LARGEFILE);
  209. + if (fsm_fd == -1) {
  210. + zend_accel_error(ACCEL_LOG_WARNING, "Unable to initialize fsmonitor structure");
  211. + return FAILURE;
  212. + }
  213. +
  214. + //FIXME
  215. + if (fanotify_mark(fsm_fd, FAN_MARK_ADD | FAN_MARK_MOUNT, FAN_MODIFY, -1, "/var/www") == -1) {
  216. + zend_accel_error(ACCEL_LOG_WARNING, "Unable to watch filesystem");
  217. + close(fsm_fd);
  218. + return FAILURE;
  219. + }
  220. +
  221. + return zend_accel_fsmonitor_init_signals();
  222. +}
  223. +
  224. +static void zend_accel_fsmonitor_invalidate(char *filename, int filename_len)
  225. +{
  226. + /* only accept .php-files
  227. + * this is the full path so '.php' is not valid
  228. + */
  229. + if (filename_len <= 4 || strcmp(&filename[filename_len - 4], ".php") != 0)
  230. + return;
  231. + zend_accel_error(ACCEL_LOG_DEBUG, "file: %s", filename);
  232. + zend_accel_invalidate_real(filename, filename_len, 1);
  233. +}
  234. +
  235. +static int zend_accel_fsmonitor_read()
  236. +{
  237. + const struct fanotify_event_metadata *metadata;
  238. + struct fanotify_event_metadata buf[200];
  239. + char procfd_path[PATH_MAX], path[PATH_MAX];
  240. + ssize_t len, path_len;
  241. +
  242. + len = read(fsm_fd, (void *)&buf, sizeof(buf));
  243. + if (len == -1 && errno != EAGAIN) {
  244. + zend_accel_error(ACCEL_LOG_ERROR, "Read error: %s", strerror(errno));
  245. + return FAILURE;
  246. + }
  247. +
  248. + metadata = buf;
  249. + while (FAN_EVENT_OK(metadata, len)) {
  250. + if (metadata->vers != FANOTIFY_METADATA_VERSION) {
  251. + zend_accel_error(ACCEL_LOG_WARNING, "Mismatch of fanotify metadata version");
  252. + return FAILURE;
  253. + }
  254. +
  255. + /* metadata->fd contains either FAN_NOFD, indicating a
  256. + * queue overflow, or a file descriptor (a nonnegative
  257. + * integer). we simply ignore queue overflow.
  258. + */
  259. + if (metadata->fd >= 0) {
  260. + /* retrieve and print pathname of the accessed file */
  261. + snprintf(procfd_path, sizeof(procfd_path),
  262. + "/proc/self/fd/%d", metadata->fd);
  263. + path_len = readlink(procfd_path, path, sizeof(path) - 1);
  264. + if (path_len == -1) {
  265. + zend_accel_error(ACCEL_LOG_DEBUG, "readlink error");
  266. + return FAILURE;
  267. + }
  268. +
  269. + path[path_len] = '\0';
  270. + zend_accel_fsmonitor_invalidate(path, path_len);
  271. +
  272. + close(metadata->fd);
  273. + }
  274. +
  275. + /* advance to next event */
  276. + metadata = FAN_EVENT_NEXT(metadata, len);
  277. + }
  278. +
  279. + return SUCCESS;
  280. +}
  281. +
  282. +
  283. +void zend_accel_fsmonitor_startup()
  284. +{
  285. + if (fsm_pid > 0)
  286. + return;
  287. +
  288. + fsm_pid = fork();
  289. + switch (fsm_pid) {
  290. + case -1: /* error */
  291. + zend_accel_error(ACCEL_LOG_WARNING, "Unable to start filesystem monitoring process");
  292. + return;
  293. +
  294. + case 0: /* child */
  295. + if (zend_accel_fsmonitor_init() == SUCCESS) {
  296. + for(;;) {
  297. + if (zend_accel_fsmonitor_read() != SUCCESS) {
  298. + break;
  299. + }
  300. + }
  301. + zend_accel_fsmonitor_shutdown();
  302. + }
  303. + exit(0);
  304. + break;
  305. +
  306. + default: /* parent */
  307. + zend_accel_error(ACCEL_LOG_DEBUG, "I'm the parent. The childs pid is: %ld", fsm_pid);
  308. + break;
  309. + }
  310. +}
  311. +
  312. +void zend_accel_fsmonitor_signal()
  313. +{
  314. + pid_t w;
  315. + int status;
  316. +
  317. + if (fsm_pid) {
  318. + zend_accel_error(ACCEL_LOG_DEBUG, "Signaling child %ld", fsm_pid);
  319. + if (kill(fsm_pid, SIGTERM) == 0) {
  320. + sleep(1); //FIXME ugly
  321. + w = waitpid(fsm_pid, &status, WNOHANG | WUNTRACED);
  322. + if (w > 0 && !WIFEXITED(status) && !WIFSIGNALED(status)) {
  323. + zend_accel_error(ACCEL_LOG_WARNING, "Need to kill fsmonitor process...");
  324. + kill(fsm_pid, SIGKILL);
  325. + }
  326. + }
  327. + fsm_pid = 0;
  328. + }
  329. +}
  330. diff --git a/zend_accelerator_fsmonitor.h b/zend_accelerator_fsmonitor.h
  331. new file mode 100644
  332. index 0000000..6c292a7
  333. --- /dev/null
  334. +++ b/zend_accelerator_fsmonitor.h
  335. @@ -0,0 +1,23 @@
  336. +/*
  337. + +----------------------------------------------------------------------+
  338. + | Zend OPcache |
  339. + +----------------------------------------------------------------------+
  340. + | Copyright (c) 1998-2014 The PHP Group |
  341. + +----------------------------------------------------------------------+
  342. + | This source file is subject to version 3.01 of the PHP license, |
  343. + | that is bundled with this package in the file LICENSE, and is |
  344. + | available through the world-wide-web at the following url: |
  345. + | http://www.php.net/license/3_01.txt |
  346. + | If you did not receive a copy of the PHP license and are unable to |
  347. + | obtain it through the world-wide-web, please send a note to |
  348. + | license@php.net so we can mail you a copy immediately. |
  349. + +----------------------------------------------------------------------+
  350. +*/
  351. +
  352. +#ifndef ZEND_ACCELERATOR_FSMONITOR_H
  353. +#define ZEND_ACCELERATOR_FSMONTIOR_H
  354. +
  355. +void zend_accel_fsmonitor_startup();
  356. +void zend_accel_fsmonitor_signal();
  357. +
  358. +#endif /* ZEND_ACCELERATOR_FSMONITOR_H */
  359.