mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 14:23:28 +07:00
Linux file watcher: poll for missing roots
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -46,6 +46,7 @@ void array_put(array* a, int index, void* element);
|
||||
void* array_get(array* a, int index);
|
||||
void array_delete(array* a);
|
||||
void array_delete_vs_data(array* a);
|
||||
void array_delete_data(array* a);
|
||||
|
||||
|
||||
// poor man's hash table
|
||||
@@ -61,7 +62,8 @@ void table_delete(table* t);
|
||||
enum {
|
||||
ERR_IGNORE = -1,
|
||||
ERR_CONTINUE = -2,
|
||||
ERR_ABORT = -3
|
||||
ERR_ABORT = -3,
|
||||
ERR_MISSING = -4
|
||||
};
|
||||
|
||||
bool init_inotify();
|
||||
|
||||
@@ -113,7 +113,7 @@ inline int get_inotify_fd() {
|
||||
}
|
||||
|
||||
|
||||
#define EVENT_MASK IN_MODIFY | IN_ATTRIB | IN_CREATE | IN_DELETE | IN_MOVE | IN_DELETE_SELF
|
||||
#define EVENT_MASK IN_MODIFY | IN_ATTRIB | IN_CREATE | IN_DELETE | IN_MOVE | IN_DELETE_SELF | IN_MOVE_SELF
|
||||
|
||||
static int add_watch(const char* path, watch_node* parent) {
|
||||
int wd = inotify_add_watch(inotify_fd, path, EVENT_MASK);
|
||||
@@ -301,7 +301,7 @@ int watch(const char* root, array* mounts) {
|
||||
return ERR_IGNORE;
|
||||
}
|
||||
else if (errno == ENOENT) {
|
||||
return ERR_CONTINUE;
|
||||
return ERR_MISSING;
|
||||
}
|
||||
userlog(LOG_ERR, "stat(%s): %s", root, strerror(errno));
|
||||
return ERR_ABORT;
|
||||
@@ -342,14 +342,14 @@ static bool process_inotify_event(struct inotify_event* event) {
|
||||
strcat(path, event->name);
|
||||
}
|
||||
|
||||
if (is_dir && ((event->mask & IN_CREATE) == IN_CREATE || (event->mask & IN_MOVED_TO) == IN_MOVED_TO)) {
|
||||
if (is_dir && event->mask & (IN_CREATE | IN_MOVED_TO)) {
|
||||
int result = walk_tree(path, node, true, NULL);
|
||||
if (result < 0 && result != ERR_IGNORE && result != ERR_CONTINUE) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_dir && ((event->mask & IN_DELETE) == IN_DELETE || (event->mask & IN_MOVED_FROM) == IN_MOVED_FROM)) {
|
||||
if (is_dir && event->mask & (IN_DELETE | IN_MOVED_FROM)) {
|
||||
for (int i=0; i<array_size(node->kids); i++) {
|
||||
watch_node* kid = array_get(node->kids, i);
|
||||
if (kid != NULL && strcmp(kid->name, path) == 0) {
|
||||
@@ -363,6 +363,7 @@ static bool process_inotify_event(struct inotify_event* event) {
|
||||
if (callback != NULL) {
|
||||
(*callback)(path, event->mask);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <string.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/stat.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -56,9 +57,13 @@
|
||||
"The current <b>inotify</b>(7) watch limit is too low. " \
|
||||
"<a href=\"http://confluence.jetbrains.net/display/IDEADEV/Inotify+Watches+Limit\">More details.</a>\n"
|
||||
|
||||
#define MISSING_ROOT_TIMEOUT 1
|
||||
|
||||
#define UNFLATTEN(root) (root[0] == '|' ? root + 1 : root)
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
int id;
|
||||
char* path;
|
||||
int id; // negative value means missing root
|
||||
} watch_root;
|
||||
|
||||
static array* roots = NULL;
|
||||
@@ -76,6 +81,8 @@ static array* unwatchable_mounts();
|
||||
static void inotify_callback(char* path, int event);
|
||||
static void report_event(char* event, char* path);
|
||||
static void output(const char* format, ...);
|
||||
static void check_missing_roots();
|
||||
static void check_root_removal(char*);
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
@@ -208,13 +215,16 @@ static void main_loop() {
|
||||
int input_fd = fileno(stdin), inotify_fd = get_inotify_fd();
|
||||
int nfds = (inotify_fd > input_fd ? inotify_fd : input_fd) + 1;
|
||||
fd_set rfds;
|
||||
struct timeval timeout;
|
||||
bool go_on = true;
|
||||
|
||||
while (go_on) {
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(input_fd, &rfds);
|
||||
FD_SET(inotify_fd, &rfds);
|
||||
if (select(nfds, &rfds, NULL, NULL, NULL) < 0) {
|
||||
timeout = (struct timeval){MISSING_ROOT_TIMEOUT, 0};
|
||||
|
||||
if (select(nfds, &rfds, NULL, NULL, &timeout) < 0) {
|
||||
userlog(LOG_ERR, "select: %s", strerror(errno));
|
||||
go_on = false;
|
||||
}
|
||||
@@ -224,6 +234,9 @@ static void main_loop() {
|
||||
else if (FD_ISSET(inotify_fd, &rfds)) {
|
||||
go_on = process_inotify_input();
|
||||
}
|
||||
else {
|
||||
check_missing_roots();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,9 +324,9 @@ static bool update_roots(array* new_roots) {
|
||||
static void unregister_roots() {
|
||||
watch_root* root;
|
||||
while ((root = array_pop(roots)) != NULL) {
|
||||
userlog(LOG_INFO, "unregistering root: %s", root->name);
|
||||
userlog(LOG_INFO, "unregistering root: %s", root->path);
|
||||
unwatch(root->id);
|
||||
free(root->name);
|
||||
free(root->path);
|
||||
free(root);
|
||||
};
|
||||
}
|
||||
@@ -322,12 +335,11 @@ static void unregister_roots() {
|
||||
static bool register_roots(array* new_roots, array* unwatchable, array* mounts) {
|
||||
for (int i=0; i<array_size(new_roots); i++) {
|
||||
char* new_root = array_get(new_roots, i);
|
||||
char* unflattened = new_root;
|
||||
if (unflattened[0] == '|') ++unflattened;
|
||||
char* unflattened = UNFLATTEN(new_root);
|
||||
userlog(LOG_INFO, "registering root: %s", new_root);
|
||||
|
||||
if (unflattened[0] != '/') {
|
||||
userlog(LOG_WARNING, " ... not valid, skipped");
|
||||
userlog(LOG_WARNING, "invalid root: %s", new_root);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -357,12 +369,12 @@ static bool register_roots(array* new_roots, array* unwatchable, array* mounts)
|
||||
int id = watch(new_root, inner_mounts);
|
||||
array_delete(inner_mounts);
|
||||
|
||||
if (id >= 0) {
|
||||
if (id >= 0 || id == ERR_MISSING) {
|
||||
watch_root* root = malloc(sizeof(watch_root));
|
||||
CHECK_NULL(root, false);
|
||||
root->id = id;
|
||||
root->name = strdup(new_root);
|
||||
CHECK_NULL(root->name, false);
|
||||
root->path = strdup(new_root);
|
||||
CHECK_NULL(root->path, false);
|
||||
CHECK_NULL(array_push(roots, root), false);
|
||||
}
|
||||
else if (id == ERR_ABORT) {
|
||||
@@ -407,31 +419,25 @@ static array* unwatchable_mounts() {
|
||||
|
||||
|
||||
static void inotify_callback(char* path, int event) {
|
||||
if (event & IN_CREATE || event & IN_MOVED_TO) {
|
||||
if (event & (IN_CREATE | IN_MOVED_TO)) {
|
||||
report_event("CREATE", path);
|
||||
report_event("CHANGE", path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event & IN_MODIFY) {
|
||||
else if (event & IN_MODIFY) {
|
||||
report_event("CHANGE", path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event & IN_ATTRIB) {
|
||||
else if (event & IN_ATTRIB) {
|
||||
report_event("STATS", path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event & IN_DELETE || event & IN_MOVED_FROM) {
|
||||
else if (event & (IN_DELETE | IN_MOVED_FROM)) {
|
||||
report_event("DELETE", path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event & IN_UNMOUNT) {
|
||||
if (event & (IN_DELETE_SELF | IN_MOVE_SELF)) {
|
||||
check_root_removal(path);
|
||||
}
|
||||
else if (event & IN_UNMOUNT) {
|
||||
output("RESET\n");
|
||||
userlog(LOG_DEBUG, "RESET");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,3 +472,32 @@ static void output(const char* format, ...) {
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
|
||||
static void check_missing_roots() {
|
||||
struct stat st;
|
||||
for (int i=0; i<array_size(roots); i++) {
|
||||
watch_root* root = array_get(roots, i);
|
||||
if (root->id < 0) {
|
||||
char* unflattened = UNFLATTEN(root->path);
|
||||
if (stat(unflattened, &st) == 0) {
|
||||
root->id = watch(root->path, NULL);
|
||||
userlog(LOG_INFO, "root restored: %s\n", root->path);
|
||||
report_event("CREATE", unflattened);
|
||||
report_event("CHANGE", unflattened);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void check_root_removal(char* path) {
|
||||
for (int i=0; i<array_size(roots); i++) {
|
||||
watch_root* root = array_get(roots, i);
|
||||
if (root->id >= 0 && strcmp(path, UNFLATTEN(root->path)) == 0) {
|
||||
unwatch(root->id);
|
||||
root->id = -1;
|
||||
userlog(LOG_INFO, "root deleted: %s\n", root->path);
|
||||
report_event("DELETE", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,13 +107,20 @@ void array_delete(array* a) {
|
||||
}
|
||||
|
||||
void array_delete_vs_data(array* a) {
|
||||
if (a != NULL) {
|
||||
array_delete_data(a);
|
||||
array_delete(a);
|
||||
}
|
||||
}
|
||||
|
||||
void array_delete_data(array* a) {
|
||||
if (a != NULL) {
|
||||
for (int i=0; i<a->size; i++) {
|
||||
if (a->data[i] != NULL) {
|
||||
free(a->data[i]);
|
||||
}
|
||||
}
|
||||
array_delete(a);
|
||||
a->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ public class FileWatcher {
|
||||
private static boolean isUpToDate(File executable) {
|
||||
long length = SystemInfo.isWindows ? 70216 :
|
||||
SystemInfo.isMac ? 13924 :
|
||||
SystemInfo.isLinux ? SystemInfo.isAMD64 ? 29227 : 22734 :
|
||||
SystemInfo.isLinux ? SystemInfo.isAMD64 ? 29269 : 22768 :
|
||||
-1;
|
||||
return length < 0 || length == executable.length();
|
||||
}
|
||||
|
||||
@@ -147,12 +147,9 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
FileUtil.delete(file);
|
||||
assertEvent(VFileDeleteEvent.class, file.getAbsolutePath());
|
||||
|
||||
if (!SystemInfo.isLinux) {
|
||||
// todo[r.sh] fix Linux watcher
|
||||
myAccept = true;
|
||||
FileUtil.writeToFile(file, "re-creation");
|
||||
assertEvent(VFileCreateEvent.class, file.getAbsolutePath());
|
||||
}
|
||||
myAccept = true;
|
||||
FileUtil.writeToFile(file, "re-creation");
|
||||
assertEvent(VFileCreateEvent.class, file.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
unwatch(request);
|
||||
@@ -274,12 +271,6 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
}
|
||||
|
||||
public void testDirectoryNonExisting() throws Exception {
|
||||
if (SystemInfo.isLinux) {
|
||||
// todo[r.sh]: fix Linux watcher
|
||||
System.err.println("Ignored: to be fixed on Linux");
|
||||
return;
|
||||
}
|
||||
|
||||
File topDir = createTestDir("top");
|
||||
File subDir = new File(topDir, "subDir");
|
||||
File file = new File(subDir, "file.txt");
|
||||
@@ -493,12 +484,6 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
}
|
||||
|
||||
public void testWatchRootRecreation() throws Exception {
|
||||
if (SystemInfo.isLinux) {
|
||||
// todo[r.sh]: fix Linux watcher
|
||||
System.err.println("Ignored: to be fixed on Linux");
|
||||
return;
|
||||
}
|
||||
|
||||
File rootDir = createTestDir("root");
|
||||
File file1 = createTestFile(rootDir, "file1.txt", "abc");
|
||||
File file2 = createTestFile(rootDir, "file2.txt", "123");
|
||||
@@ -509,6 +494,7 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
myAccept = true;
|
||||
assertTrue(FileUtil.delete(rootDir));
|
||||
assertTrue(rootDir.mkdir());
|
||||
if (SystemInfo.isLinux) TimeoutUtil.sleep(1100); // implementation specific
|
||||
assertTrue(file1.createNewFile());
|
||||
assertTrue(file2.createNewFile());
|
||||
assertEvent(VFileContentChangeEvent.class, file1.getPath(), file2.getPath());
|
||||
@@ -520,12 +506,6 @@ public class FileWatcherTest extends PlatformLangTestCase {
|
||||
}
|
||||
|
||||
public void testWatchRootRenameRemove() throws Exception {
|
||||
if (SystemInfo.isLinux) {
|
||||
// todo[r.sh]: fix Linux watcher
|
||||
System.err.println("Ignored: to be fixed on Linux");
|
||||
return;
|
||||
}
|
||||
|
||||
File topDir = createTestDir("top");
|
||||
File rootDir = createTestDir(topDir, "root");
|
||||
File rootDir2 = new File(topDir, "_" + rootDir.getName());
|
||||
|
||||
Reference in New Issue
Block a user