forked from openkylin/ukui-search
419 lines
16 KiB
C++
419 lines
16 KiB
C++
/*
|
||
* Copyright (C) 2020, KylinSoft Co., Ltd.
|
||
*
|
||
* This program is free software: you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation, either version 3 of the License, or
|
||
* (at your option) any later version.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
*
|
||
* Authors: zhangzihao <zhangzihao@kylinos.cn>
|
||
*
|
||
*/
|
||
#include "inotify-index.h"
|
||
#include <QDataStream>
|
||
|
||
#define CREATE_FILE_NAME_INDEX \
|
||
indexQueue->enqueue(QVector<QString>() << QString(event->name) << QString(currentPath[event->wd] + '/' + event->name) << QString((event->mask & IN_ISDIR) ? "1" : "0")); \
|
||
IndexGenerator::getInstance()->creatAllIndex(indexQueue); \
|
||
indexQueue->clear();
|
||
|
||
#define CREATE_FILE_CONTENT_INDEX \
|
||
if ((!QString(event->name).split(".").isEmpty()) && (true == this->targetFileTypeMap[QString(event->name).split(".").last()])) { \
|
||
contentIndexQueue->enqueue(QString(currentPath[event->wd] + '/' + event->name)); \
|
||
IndexGenerator::getInstance()->creatAllIndex(contentIndexQueue); \
|
||
contentIndexQueue->clear(); \
|
||
break; \
|
||
}
|
||
|
||
#define TRAVERSE_DIR \
|
||
QString tmp = currentPath[event->wd] + '/' + event->name; \
|
||
QFileInfo fi(tmp); \
|
||
if(!fi.isSymLink()){ \
|
||
AddWatch(tmp); \
|
||
setPath(tmp); \
|
||
Traverse(); \
|
||
}
|
||
|
||
|
||
#define CREATE_FILE \
|
||
CREATE_FILE_NAME_INDEX \
|
||
CREATE_FILE_CONTENT_INDEX
|
||
using namespace Zeeker;
|
||
InotifyIndex::InotifyIndex(const QString& path) : Traverse_BFS(path) {
|
||
qDebug() << "setInotifyMaxUserWatches start";
|
||
UkuiSearchQDBus usQDBus;
|
||
usQDBus.setInotifyMaxUserWatches();
|
||
qDebug() << "setInotifyMaxUserWatches end";
|
||
m_sharedMemory = new QSharedMemory("ukui-search-shared-map", this);
|
||
}
|
||
|
||
InotifyIndex::~InotifyIndex() {
|
||
IndexGenerator::getInstance()->~IndexGenerator();
|
||
qWarning() << "~InotifyIndex";
|
||
}
|
||
|
||
void InotifyIndex::firstTraverse() {
|
||
QQueue<QString> bfs;
|
||
bfs.enqueue(this->path);
|
||
QFileInfoList list;
|
||
QDir dir;
|
||
dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
|
||
dir.setSorting(QDir::DirsFirst);
|
||
while(!bfs.empty()) {
|
||
dir.setPath(bfs.dequeue());
|
||
list = dir.entryInfoList();
|
||
for(auto i : list) {
|
||
if(i.isDir() && (!(i.isSymLink()))) {
|
||
this->AddWatch(i.absoluteFilePath());
|
||
bfs.enqueue(i.absoluteFilePath());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void InotifyIndex::DoSomething(const QFileInfo& fileInfo) {
|
||
qDebug() << fileInfo.fileName() << "-------" << fileInfo.absoluteFilePath();
|
||
if(fileInfo.isDir() && (!fileInfo.isSymLink())) {
|
||
this->AddWatch(fileInfo.absoluteFilePath());
|
||
}
|
||
QQueue<QVector<QString> > tempFile;
|
||
tempFile.enqueue(QVector<QString>() << fileInfo.fileName() << fileInfo.absoluteFilePath() << QString((fileInfo.isDir() && (!fileInfo.isSymLink())) ? "1" : "0"));
|
||
IndexGenerator::getInstance()->creatAllIndex(&tempFile);
|
||
if((fileInfo.fileName().split(".", QString::SkipEmptyParts).length() > 1) && (true == targetFileTypeMap[fileInfo.fileName().split(".").last()])) {
|
||
QQueue<QString> tmp;
|
||
tmp.enqueue(fileInfo.absoluteFilePath());
|
||
IndexGenerator::getInstance()->creatAllIndex(&tmp);
|
||
}
|
||
}
|
||
|
||
bool InotifyIndex::AddWatch(const QString &path) {
|
||
int ret = inotify_add_watch(m_fd, path.toStdString().c_str(), (IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE | IN_DELETE | IN_MODIFY));
|
||
if(ret == -1) {
|
||
qDebug() << "AddWatch error:" << path;
|
||
return false;
|
||
}
|
||
// Q_ASSERT(ret != -1);
|
||
assert(ret != -1);
|
||
currentPath[ret] = path;
|
||
// qDebug() << "Watch: " << path << "ret: " << ret;
|
||
return true;
|
||
}
|
||
|
||
bool InotifyIndex::RemoveWatch(const QString &path, bool removeFromDatabase) {
|
||
int ret = inotify_rm_watch(m_fd, currentPath.key(path));
|
||
if(ret) {
|
||
qDebug() << "remove path error";
|
||
return false;
|
||
}
|
||
// Q_ASSERT(ret == 0);
|
||
assert(ret == 0);
|
||
|
||
if(removeFromDatabase) {
|
||
for(QMap<int, QString>::Iterator i = currentPath.begin(); i != currentPath.end();) {
|
||
// qDebug() << i.value();
|
||
if(i.value().length() > path.length()) {
|
||
// if (i.value().mid(0, path.length()) == path){
|
||
// if (path.startsWith(i.value())){
|
||
if(i.value().startsWith(path)) {
|
||
qDebug() << "remove path: " << i.value();
|
||
ret = inotify_rm_watch(m_fd, currentPath.key(path));
|
||
if(ret) {
|
||
qDebug() << "remove path error";
|
||
// return false;
|
||
}
|
||
// assert(ret == 0);
|
||
/*--------------------------------*/
|
||
//在此调用删除索引
|
||
qDebug() << i.value();
|
||
IndexGenerator::getInstance()->deleteAllIndex(new QStringList(i.value()));
|
||
/*--------------------------------*/
|
||
currentPath.erase(i++);
|
||
// i++;
|
||
} else {
|
||
i++;
|
||
}
|
||
} else {
|
||
i++;
|
||
}
|
||
}
|
||
} else {
|
||
for(QMap<int, QString>::Iterator i = currentPath.begin(); i != currentPath.end();) {
|
||
// qDebug() << i.value();
|
||
if(i.value().length() > path.length()) {
|
||
// if (i.value().mid(0, path.length()) == path){
|
||
// if (path.startsWith(i.value())){
|
||
if(i.value().startsWith(path)) {
|
||
qDebug() << "remove path: " << i.value();
|
||
ret = inotify_rm_watch(m_fd, currentPath.key(path));
|
||
if(ret) {
|
||
qDebug() << "remove path error";
|
||
// return false;
|
||
}
|
||
// assert(ret == 0);
|
||
currentPath.erase(i++);
|
||
// i++;
|
||
} else {
|
||
i++;
|
||
}
|
||
} else {
|
||
i++;
|
||
}
|
||
}
|
||
}
|
||
// qDebug() << path;
|
||
//这个貌似不用删,先mark一下
|
||
// currentPath.remove(currentPath.key(path));
|
||
return true;
|
||
}
|
||
|
||
void InotifyIndex::eventProcess(const char* buf, ssize_t tmp) {
|
||
QQueue<QVector<QString>>* indexQueue = new QQueue<QVector<QString>>();
|
||
QQueue<QString>* contentIndexQueue = new QQueue<QString>();
|
||
|
||
ssize_t numRead = 0;
|
||
numRead = tmp;
|
||
char * p = const_cast<char*>(buf);
|
||
IndexStatusRecorder::getInstance()->setStatus(INOTIFY_NORMAL_EXIT, "0");
|
||
for(; p < buf + numRead;) {
|
||
struct inotify_event * event = reinterpret_cast<inotify_event *>(p);
|
||
if(event->name[0] != '.') {
|
||
|
||
qDebug() << "Read Event event->wd: " << event->wd;
|
||
qDebug() << "Read Event: " << currentPath[event->wd] << QString(event->name) << event->cookie << event->wd << event->mask;
|
||
|
||
qDebug() << QString(currentPath[event->wd] + '/' + event->name);
|
||
// switch (event->mask) {
|
||
if(event->mask & IN_CREATE) {
|
||
|
||
//Create top dir first, traverse it last.
|
||
qDebug() << "IN_CREATE";
|
||
CREATE_FILE
|
||
if(event->mask & IN_ISDIR) {
|
||
TRAVERSE_DIR
|
||
}
|
||
goto next;
|
||
}
|
||
|
||
if((event->mask & IN_DELETE) | (event->mask & IN_MOVED_FROM)) {
|
||
qDebug() << "IN_DELETE or IN_MOVED_FROM";
|
||
if(event->mask & IN_ISDIR) {
|
||
RemoveWatch(currentPath[event->wd] + '/' + event->name);
|
||
}
|
||
//delete once more
|
||
IndexGenerator::getInstance()->deleteAllIndex(new QStringList(currentPath[event->wd] + '/' + event->name));
|
||
goto next;
|
||
}
|
||
|
||
if(event->mask & IN_MODIFY) {
|
||
qDebug() << "IN_MODIFY";
|
||
if(!(event->mask & IN_ISDIR)) {
|
||
// IndexGenerator::getInstance()->deleteAllIndex(new QStringList(currentPath[event->wd] + '/' + event->name));
|
||
CREATE_FILE
|
||
}
|
||
goto next;
|
||
}
|
||
|
||
if(event->mask & IN_MOVED_TO) {
|
||
qDebug() << "IN_MOVED_TO";
|
||
if(event->mask & IN_ISDIR) {
|
||
RemoveWatch(currentPath[event->wd] + '/' + event->name);
|
||
// IndexGenerator::getInstance()->deleteAllIndex(new QStringList(currentPath[event->wd] + '/' + event->name));
|
||
CREATE_FILE
|
||
TRAVERSE_DIR
|
||
} else {
|
||
IndexGenerator::getInstance()->deleteAllIndex(new QStringList(currentPath[event->wd] + '/' + event->name));
|
||
CREATE_FILE
|
||
}
|
||
goto next;
|
||
}
|
||
}
|
||
next:
|
||
p += sizeof(struct inotify_event) + event->len;
|
||
}
|
||
IndexStatusRecorder::getInstance()->setStatus(INOTIFY_NORMAL_EXIT, "2");
|
||
delete indexQueue;
|
||
indexQueue = nullptr;
|
||
delete contentIndexQueue;
|
||
contentIndexQueue = nullptr;
|
||
}
|
||
|
||
void InotifyIndex::run() {
|
||
m_fd = inotify_init();
|
||
qDebug() << "m_fd----------->" << m_fd;
|
||
|
||
this->AddWatch(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
|
||
this->setPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
|
||
this->firstTraverse();
|
||
|
||
int fifo_fd;
|
||
char buffer[2];
|
||
memset(buffer, 0, sizeof(buffer));
|
||
fifo_fd = open(UKUI_SEARCH_PIPE_PATH, O_RDWR);
|
||
if(fifo_fd == -1) {
|
||
perror("open fifo error\n");
|
||
assert(false);
|
||
}
|
||
int retval = read(fifo_fd, buffer, sizeof(buffer));
|
||
if(retval == -1) {
|
||
perror("read error\n");
|
||
assert(false);
|
||
}
|
||
printf("read fifo=[%s]\n", buffer);
|
||
|
||
printf("read data ok\n");
|
||
close(fifo_fd);
|
||
if(buffer[0] & 0x1) {
|
||
printf("data confirmed\n");
|
||
}
|
||
unlink(UKUI_SEARCH_PIPE_PATH);
|
||
|
||
char buf[BUF_LEN] __attribute__((aligned(8)));
|
||
|
||
ssize_t numRead;
|
||
|
||
while(FileUtils::SearchMethod::INDEXSEARCH == FileUtils::searchMethod) {
|
||
// for (;;) { /* Read events forever */
|
||
memset(buf, 0x00, BUF_LEN);
|
||
numRead = read(m_fd, buf, BUF_LEN);
|
||
|
||
if(numRead == -1) {
|
||
printf("\033[1;31;40mread event error\033[0m\n");
|
||
IndexStatusRecorder::getInstance()->setStatus(INOTIFY_NORMAL_EXIT, "1");
|
||
fflush(stdout);
|
||
assert(false);
|
||
}
|
||
|
||
//TODO: Merge multiple signals.
|
||
char * tmp = const_cast<char*>(buf);
|
||
|
||
for(; tmp < buf + numRead;) {
|
||
struct inotify_event * event = reinterpret_cast<inotify_event *>(tmp);
|
||
if(event->name[0] != '.') {
|
||
// qDebug() << "Read Event: " << currentPath[event->wd] << QString(event->name) << event->cookie << event->wd << event->mask;
|
||
// qDebug("mask:0x%x,",event->mask);
|
||
break;
|
||
}
|
||
tmp += sizeof(struct inotify_event) + event->len;
|
||
}
|
||
if(tmp >= buf + numRead) {
|
||
continue;
|
||
}
|
||
|
||
++FileUtils::_index_status;
|
||
|
||
pid_t pid;
|
||
pid = fork();
|
||
if(pid == 0) {
|
||
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
||
prctl(PR_SET_NAME, "inotify-index");
|
||
if(numRead == 0) {
|
||
qDebug() << "read() from inotify fd returned 0!";
|
||
}
|
||
if(numRead == -1) {
|
||
qDebug() << "read";
|
||
}
|
||
eventProcess(buf, numRead);
|
||
|
||
fd_set read_fds;
|
||
int rc;
|
||
timeval* read_timeout = (timeval*)malloc(sizeof(timeval));
|
||
|
||
read_timeout->tv_sec = 40;
|
||
read_timeout->tv_usec = 0;
|
||
for(;;) {
|
||
FD_ZERO(&read_fds);
|
||
FD_SET(m_fd, &read_fds);
|
||
qDebug() << read_timeout->tv_sec;
|
||
rc = select(m_fd + 1, &read_fds, NULL, NULL, read_timeout);
|
||
if(rc < 0) {
|
||
// error
|
||
qWarning() << "select result < 0, error!";
|
||
IndexStatusRecorder::getInstance()->setStatus(INOTIFY_NORMAL_EXIT, "1");
|
||
assert(false);
|
||
} else if(rc == 0) {
|
||
qDebug() << "select timeout!";
|
||
::free(read_timeout);
|
||
IndexGenerator::getInstance()->~IndexGenerator();
|
||
QBuffer buffer;
|
||
QDataStream out(&buffer);
|
||
if (m_sharedMemory->isAttached()) {
|
||
m_sharedMemory->detach();
|
||
}
|
||
buffer.open(QBuffer::ReadWrite);
|
||
out << currentPath;
|
||
int size = buffer.size();
|
||
if (!m_sharedMemory->create(size)) {
|
||
qDebug() << "Create sharedMemory Error: " << m_sharedMemory->errorString();
|
||
} else {
|
||
m_sharedMemory->lock();
|
||
char *to = static_cast<char *>(m_sharedMemory->data());
|
||
const char *from = buffer.data().constData();
|
||
memcpy(to, from, qMin(size, m_sharedMemory->size()));
|
||
m_sharedMemory->unlock();
|
||
}
|
||
// GlobalSettings::getInstance()->forceSync();
|
||
::_exit(0);
|
||
} else {
|
||
memset(buf, 0x00, BUF_LEN);
|
||
numRead = read(m_fd, buf, BUF_LEN);
|
||
if(numRead == -1) {
|
||
printf("\033[1;31;40mread event error\033[0m\n");
|
||
fflush(stdout);
|
||
assert(false);
|
||
}
|
||
|
||
//TODO: Merge multiple signals.
|
||
char * tmp = const_cast<char*>(buf);
|
||
|
||
for(; tmp < buf + numRead; ) {
|
||
struct inotify_event * event = reinterpret_cast<inotify_event *>(tmp);
|
||
if(event->name[0] != '.') {
|
||
break;
|
||
}
|
||
tmp += sizeof(struct inotify_event) + event->len;
|
||
}
|
||
if(tmp >= buf + numRead) {
|
||
continue;
|
||
}
|
||
|
||
qDebug() << "Read " << numRead << " bytes from inotify fd";
|
||
this->eventProcess(buf, numRead);
|
||
}
|
||
}
|
||
} else if(pid > 0) {
|
||
memset(buf, 0x00, BUF_LEN);
|
||
waitpid(pid, NULL, 0);
|
||
if (!m_sharedMemory->attach()) {
|
||
qDebug() << "SharedMemory attach Error: " << m_sharedMemory->errorString();
|
||
} else {
|
||
QBuffer buffer;
|
||
QDataStream in(&buffer);
|
||
QMap<int, QString> pathMap;
|
||
m_sharedMemory->lock();
|
||
buffer.setData(static_cast<const char *>(m_sharedMemory->constData()), m_sharedMemory->size());
|
||
buffer.open(QBuffer::ReadWrite);
|
||
in >> pathMap;
|
||
m_sharedMemory->unlock();
|
||
m_sharedMemory->detach();
|
||
currentPath = pathMap;
|
||
}
|
||
--FileUtils::_index_status;
|
||
} else {
|
||
assert(false);
|
||
}
|
||
}
|
||
|
||
if(FileUtils::SearchMethod::DIRECTSEARCH == FileUtils::searchMethod) {
|
||
IndexStatusRecorder::getInstance()->setStatus(INOTIFY_NORMAL_EXIT, "3");
|
||
RemoveWatch(QStandardPaths::writableLocation(QStandardPaths::HomeLocation), false);
|
||
}
|
||
}
|