This is proof of concept code to invalidate the opcode cache entries using
fanotify events. This fails because fanotify doesn't support rename events.
diff --git a/ZendAccelerator.c b/ZendAccelerator.c
index 4406400..17f7cff 100644
--- a/ZendAccelerator.c
+++ b/ZendAccelerator.c
@@ -29,6 +29,7 @@
#include "zend_shared_alloc.h"
#include "zend_accelerator_module.h"
#include "zend_accelerator_blacklist.h"
+#include "zend_accelerator_fsmonitor.h"
#include "zend_list.h"
#include "zend_execute.h"
#include "main/SAPI.h"
@@ -1048,32 +1049,21 @@ static inline char *accel_make_persistent_key(zend_file_handle *file_handle, int
return accel_make_persistent_key_ex(file_handle, strlen(file_handle->filename), key_len TSRMLS_CC);
}
-int zend_accel_invalidate(const char *filename, int filename_len, zend_bool force TSRMLS_DC)
+int zend_accel_invalidate_real(char *filename, int filename_len, zend_bool force TSRMLS_DC)
{
- char *realpath;
zend_persistent_script *persistent_script;
if (!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled) || accelerator_shm_read_lock(TSRMLS_C) != SUCCESS) {
return FAILURE;
}
-#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
- realpath = accel_php_resolve_path(filename, filename_len, ZCG(include_path) TSRMLS_CC);
-#else
- realpath = accelerator_orig_zend_resolve_path(filename, filename_len TSRMLS_CC);
-#endif
-
- if (!realpath) {
- return FAILURE;
- }
-
- persistent_script = zend_accel_hash_find(&ZCSG(hash), realpath, strlen(realpath) + 1);
+ persistent_script = zend_accel_hash_find(&ZCSG(hash), filename, filename_len + 1);
if (persistent_script && !persistent_script->corrupted) {
zend_file_handle file_handle;
file_handle.type = ZEND_HANDLE_FILENAME;
- file_handle.filename = realpath;
- file_handle.opened_path = realpath;
+ file_handle.filename = filename;
+ file_handle.opened_path = filename;
if (force ||
!ZCG(accel_directives).validate_timestamps ||
@@ -1096,11 +1086,34 @@ int zend_accel_invalidate(const char *filename, int filename_len, zend_bool forc
}
accelerator_shm_read_unlock(TSRMLS_C);
- efree(realpath);
return SUCCESS;
}
+int zend_accel_invalidate(const char *filename, int filename_len, zend_bool force TSRMLS_DC)
+{
+ char *realpath;
+ zend_persistent_script *persistent_script;
+
+ if (!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled) || accelerator_shm_read_lock(TSRMLS_C) != SUCCESS) {
+ return FAILURE;
+ }
+
+#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
+ realpath = accel_php_resolve_path(filename, filename_len, ZCG(include_path) TSRMLS_CC);
+#else
+ realpath = accelerator_orig_zend_resolve_path(filename, filename_len TSRMLS_CC);
+#endif
+
+ if (!realpath) {
+ return FAILURE;
+ }
+
+ zend_accel_invalidate_real(realpath, strlen(realpath), force);
+
+ efree(realpath);
+}
+
/* Adds another key for existing cached script */
static void zend_accel_add_key(char *key, unsigned int key_length, zend_accel_hash_entry *bucket TSRMLS_DC)
{
@@ -2690,6 +2703,8 @@ static int accel_startup(zend_extension *extension)
zend_accel_copy_internal_functions(TSRMLS_C);
#endif
+ zend_accel_fsmonitor_startup();
+
return SUCCESS;
}
@@ -2706,6 +2721,8 @@ void accel_shutdown(TSRMLS_D)
{
zend_ini_entry *ini_entry;
+ zend_accel_fsmonitor_signal();
+
zend_accel_blacklist_shutdown(&accel_blacklist);
if (!ZCG(enabled) || !accel_startup_ok) {
diff --git a/ZendAccelerator.h b/ZendAccelerator.h
index bba3631..a8bdf96 100644
--- a/ZendAccelerator.h
+++ b/ZendAccelerator.h
@@ -332,6 +332,7 @@ void accel_shutdown(TSRMLS_D);
void zend_accel_schedule_restart(zend_accel_restart_reason reason TSRMLS_DC);
void zend_accel_schedule_restart_if_necessary(zend_accel_restart_reason reason TSRMLS_DC);
int validate_timestamp_and_record(zend_persistent_script *persistent_script, zend_file_handle *file_handle TSRMLS_DC);
+int zend_accel_invalidate_real(char *filename, int filename_len, zend_bool force TSRMLS_DC);
int zend_accel_invalidate(const char *filename, int filename_len, zend_bool force TSRMLS_DC);
int accelerator_shm_read_lock(TSRMLS_D);
void accelerator_shm_read_unlock(TSRMLS_D);
diff --git a/config.m4 b/config.m4
index f6e6ca9..fd11e99 100644
--- a/config.m4
+++ b/config.m4
@@ -366,6 +366,7 @@ fi
PHP_NEW_EXTENSION(opcache,
ZendAccelerator.c \
zend_accelerator_blacklist.c \
+ zend_accelerator_fsmonitor.c \
zend_accelerator_debug.c \
zend_accelerator_hash.c \
zend_accelerator_module.c \
diff --git a/package.xml b/package.xml
index bdd7891..3852e2c 100644
--- a/package.xml
+++ b/package.xml
@@ -88,6 +88,7 @@
+
diff --git a/zend_accelerator_fsmonitor.c b/zend_accelerator_fsmonitor.c
new file mode 100644
index 0000000..d66228c
--- /dev/null
+++ b/zend_accelerator_fsmonitor.c
@@ -0,0 +1,182 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+*/
+
+#include "main/php.h"
+#include "main/php_globals.h"
+#include "ZendAccelerator.h"
+#include "zend_accelerator_fsmonitor.h"
+
+#include
+#include
+#include
+
+pid_t fsm_pid = 0;
+int fsm_fd = -1;
+
+static void zend_accel_fsmonitor_shutdown()
+{
+ zend_accel_error(ACCEL_LOG_DEBUG, "fsmonitor: shutdown");
+ close(fsm_fd);
+}
+
+static void sig_handler(int signo)
+{
+ int saved_errno = errno;
+ zend_accel_fsmonitor_shutdown();
+ errno = saved_errno;
+}
+
+static int zend_accel_fsmonitor_init_signals()
+{
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = sig_handler;
+ sigfillset(&act.sa_mask);
+
+ if (sigaction(SIGTERM, &act, 0) < 0) {
+ zend_accel_error(ACCEL_LOG_WARNING, "failed to init signals: sigaction()");
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+
+static int zend_accel_fsmonitor_init()
+{
+ zend_accel_error(ACCEL_LOG_DEBUG, "fsmonitor: init");
+ fsm_fd = fanotify_init(FAN_CLOEXEC, O_RDONLY | O_LARGEFILE);
+ if (fsm_fd == -1) {
+ zend_accel_error(ACCEL_LOG_WARNING, "Unable to initialize fsmonitor structure");
+ return FAILURE;
+ }
+
+ //FIXME
+ if (fanotify_mark(fsm_fd, FAN_MARK_ADD | FAN_MARK_MOUNT, FAN_MODIFY, -1, "/var/www") == -1) {
+ zend_accel_error(ACCEL_LOG_WARNING, "Unable to watch filesystem");
+ close(fsm_fd);
+ return FAILURE;
+ }
+
+ return zend_accel_fsmonitor_init_signals();
+}
+
+static void zend_accel_fsmonitor_invalidate(char *filename, int filename_len)
+{
+ /* only accept .php-files
+ * this is the full path so '.php' is not valid
+ */
+ if (filename_len <= 4 || strcmp(&filename[filename_len - 4], ".php") != 0)
+ return;
+ zend_accel_error(ACCEL_LOG_DEBUG, "file: %s", filename);
+ zend_accel_invalidate_real(filename, filename_len, 1);
+}
+
+static int zend_accel_fsmonitor_read()
+{
+ const struct fanotify_event_metadata *metadata;
+ struct fanotify_event_metadata buf[200];
+ char procfd_path[PATH_MAX], path[PATH_MAX];
+ ssize_t len, path_len;
+
+ len = read(fsm_fd, (void *)&buf, sizeof(buf));
+ if (len == -1 && errno != EAGAIN) {
+ zend_accel_error(ACCEL_LOG_ERROR, "Read error: %s", strerror(errno));
+ return FAILURE;
+ }
+
+ metadata = buf;
+ while (FAN_EVENT_OK(metadata, len)) {
+ if (metadata->vers != FANOTIFY_METADATA_VERSION) {
+ zend_accel_error(ACCEL_LOG_WARNING, "Mismatch of fanotify metadata version");
+ return FAILURE;
+ }
+
+ /* metadata->fd contains either FAN_NOFD, indicating a
+ * queue overflow, or a file descriptor (a nonnegative
+ * integer). we simply ignore queue overflow.
+ */
+ if (metadata->fd >= 0) {
+ /* retrieve and print pathname of the accessed file */
+ snprintf(procfd_path, sizeof(procfd_path),
+ "/proc/self/fd/%d", metadata->fd);
+ path_len = readlink(procfd_path, path, sizeof(path) - 1);
+ if (path_len == -1) {
+ zend_accel_error(ACCEL_LOG_DEBUG, "readlink error");
+ return FAILURE;
+ }
+
+ path[path_len] = '\0';
+ zend_accel_fsmonitor_invalidate(path, path_len);
+
+ close(metadata->fd);
+ }
+
+ /* advance to next event */
+ metadata = FAN_EVENT_NEXT(metadata, len);
+ }
+
+ return SUCCESS;
+}
+
+
+void zend_accel_fsmonitor_startup()
+{
+ if (fsm_pid > 0)
+ return;
+
+ fsm_pid = fork();
+ switch (fsm_pid) {
+ case -1: /* error */
+ zend_accel_error(ACCEL_LOG_WARNING, "Unable to start filesystem monitoring process");
+ return;
+
+ case 0: /* child */
+ if (zend_accel_fsmonitor_init() == SUCCESS) {
+ for(;;) {
+ if (zend_accel_fsmonitor_read() != SUCCESS) {
+ break;
+ }
+ }
+ zend_accel_fsmonitor_shutdown();
+ }
+ exit(0);
+ break;
+
+ default: /* parent */
+ zend_accel_error(ACCEL_LOG_DEBUG, "I'm the parent. The childs pid is: %ld", fsm_pid);
+ break;
+ }
+}
+
+void zend_accel_fsmonitor_signal()
+{
+ pid_t w;
+ int status;
+
+ if (fsm_pid) {
+ zend_accel_error(ACCEL_LOG_DEBUG, "Signaling child %ld", fsm_pid);
+ if (kill(fsm_pid, SIGTERM) == 0) {
+ sleep(1); //FIXME ugly
+ w = waitpid(fsm_pid, &status, WNOHANG | WUNTRACED);
+ if (w > 0 && !WIFEXITED(status) && !WIFSIGNALED(status)) {
+ zend_accel_error(ACCEL_LOG_WARNING, "Need to kill fsmonitor process...");
+ kill(fsm_pid, SIGKILL);
+ }
+ }
+ fsm_pid = 0;
+ }
+}
diff --git a/zend_accelerator_fsmonitor.h b/zend_accelerator_fsmonitor.h
new file mode 100644
index 0000000..6c292a7
--- /dev/null
+++ b/zend_accelerator_fsmonitor.h
@@ -0,0 +1,23 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_ACCELERATOR_FSMONITOR_H
+#define ZEND_ACCELERATOR_FSMONTIOR_H
+
+void zend_accel_fsmonitor_startup();
+void zend_accel_fsmonitor_signal();
+
+#endif /* ZEND_ACCELERATOR_FSMONITOR_H */