forked from openkylin/ukui-search
490 lines
16 KiB
C++
490 lines
16 KiB
C++
#include "inotify-watch.h"
|
|
#include "dir-watcher.h"
|
|
#include <sys/ioctl.h>
|
|
#include <malloc.h>
|
|
#include <errno.h>
|
|
using namespace UkuiSearch;
|
|
static InotifyWatch* global_instance_InotifyWatch = nullptr;
|
|
|
|
UkuiSearch::InotifyWatch *UkuiSearch::InotifyWatch::getInstance()
|
|
{
|
|
if(!global_instance_InotifyWatch) {
|
|
global_instance_InotifyWatch = new InotifyWatch();
|
|
}
|
|
return global_instance_InotifyWatch;
|
|
}
|
|
|
|
UkuiSearch::InotifyWatch::InotifyWatch(): Traverse_BFS(), m_semaphore(INDEX_SEM, 0, QSystemSemaphore::AccessMode::Open)
|
|
{
|
|
qDebug() << "setInotifyMaxUserWatches start";
|
|
UkuiSearchQDBus usQDBus;
|
|
usQDBus.setInotifyMaxUserWatches();
|
|
qDebug() << "setInotifyMaxUserWatches end";
|
|
m_sharedMemory = new QSharedMemory("ukui-search-shared-map", this);
|
|
}
|
|
|
|
InotifyWatch::~InotifyWatch()
|
|
{
|
|
if(m_notifier)
|
|
delete m_notifier;
|
|
m_notifier = nullptr;
|
|
}
|
|
|
|
bool InotifyWatch::addWatch(const QString &path)
|
|
{
|
|
int ret = inotify_add_watch(m_inotifyFd, path.toStdString().c_str(), (IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE | IN_DELETE | IN_MODIFY));
|
|
if(ret == -1) {
|
|
qWarning() << "AddWatch error:" << path;
|
|
return false;
|
|
}
|
|
m_pathMap[ret] = path;
|
|
// qDebug() << "Watch: " << path << "ret: " << ret;
|
|
return true;
|
|
}
|
|
|
|
bool InotifyWatch::removeWatch(const QString &path, bool removeFromDatabase)
|
|
{
|
|
inotify_rm_watch(m_inotifyFd, m_pathMap.key(path));
|
|
|
|
if(removeFromDatabase) {
|
|
for(QMap<int, QString>::Iterator i = m_pathMap.begin(); i != m_pathMap.end();) {
|
|
// qDebug() << i.value();
|
|
// if(i.value().length() > path.length()) {
|
|
if(FileUtils::isOrUnder(i.value(), path)) {
|
|
qDebug() << "remove path: " << i.value();
|
|
inotify_rm_watch(m_inotifyFd, m_pathMap.key(path));
|
|
PendingFile f(i.value());
|
|
f.setDeleted();
|
|
f.setIsDir();
|
|
PendingFileQueue::getInstance()->enqueue(f);
|
|
m_pathMap.erase(i++);
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
} else {
|
|
for(QMap<int, QString>::Iterator i = m_pathMap.begin(); i != m_pathMap.end();) {
|
|
// qDebug() << i.value();
|
|
if(i.value().length() > path.length()) {
|
|
if(FileUtils::isOrUnder(i.value(), path)) {
|
|
// if(i.value().startsWith(path + "/")) {
|
|
// qDebug() << "remove path: " << i.value();
|
|
inotify_rm_watch(m_inotifyFd, m_pathMap.key(path));
|
|
m_pathMap.erase(i++);
|
|
} else {
|
|
i++;
|
|
}
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
m_pathMap.remove(m_pathMap.key(path));
|
|
return true;
|
|
}
|
|
|
|
void InotifyWatch::work(const QFileInfo &info)
|
|
{
|
|
qDebug() << info.fileName() << "-------" << info.absoluteFilePath();
|
|
if(info.isDir() && (!info.isSymLink())) {
|
|
this->addWatch(info.absoluteFilePath());
|
|
}
|
|
PendingFile f(info.absoluteFilePath());
|
|
if(info.isDir()) {
|
|
f.setIsDir();
|
|
}
|
|
PendingFileQueue::getInstance()->enqueue(f);
|
|
}
|
|
|
|
void InotifyWatch::firstTraverse(QStringList pathList, QStringList blockList)
|
|
{
|
|
if(pathList.isEmpty()) {
|
|
pathList = m_pathList;
|
|
}
|
|
if(blockList.isEmpty()) {
|
|
blockList = m_blockList;
|
|
}
|
|
|
|
QQueue<QString> bfs;
|
|
for(QString blockPath : blockList) {
|
|
for(QString path : pathList) {
|
|
if(FileUtils::isOrUnder(path, blockPath)) {
|
|
pathList.removeOne(path);
|
|
}
|
|
}
|
|
}
|
|
for(QString path : pathList) {
|
|
addWatch(path);
|
|
bfs.enqueue(path);
|
|
}
|
|
|
|
QFileInfoList list;
|
|
QDir dir;
|
|
QStringList tmpList = blockList;
|
|
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) {
|
|
bool isBlocked = false;
|
|
for(QString path : tmpList) {
|
|
if(i.absoluteFilePath() == path) {
|
|
isBlocked = true;
|
|
tmpList.removeOne(path);
|
|
break;
|
|
}
|
|
}
|
|
if(isBlocked)
|
|
continue;
|
|
|
|
if(i.isDir() && (!(i.isSymLink()))) {
|
|
addWatch(i.absoluteFilePath());
|
|
bfs.enqueue(i.absoluteFilePath());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void InotifyWatch::addIndexPath(const QString path, const QStringList blockList)
|
|
{
|
|
this->firstTraverse(QStringList() << path, blockList);
|
|
}
|
|
|
|
void InotifyWatch::removeIndexPath(QString &path)
|
|
{
|
|
|
|
}
|
|
|
|
void InotifyWatch::stopWatch()
|
|
{
|
|
// if(this->isRunning()) {
|
|
// this->quit();
|
|
// if(m_notifier)
|
|
// delete m_notifier;
|
|
// m_notifier = nullptr;
|
|
// removeWatch(QStandardPaths::writableLocation(QStandardPaths::HomeLocation), false);
|
|
// }
|
|
|
|
// IndexStatusRecorder::getInstance()->setStatus(INOTIFY_NORMAL_EXIT, "3");
|
|
}
|
|
|
|
void InotifyWatch::run()
|
|
{
|
|
m_inotifyFd = inotify_init();
|
|
if (m_inotifyFd > 0) {
|
|
qDebug()<<"Inotify init success!";
|
|
} else {
|
|
qWarning() << "Inotify init fail! Now try add inotify_user_instances.";
|
|
UkuiSearchQDBus usQDBus;
|
|
usQDBus.addInotifyUserInstances(128);
|
|
m_inotifyFd = inotify_init();
|
|
if (m_inotifyFd > 0) {
|
|
qDebug()<<"Inotify init success!";
|
|
} else {
|
|
printf("errno=%d\n",errno);
|
|
printf("Mesg:%s\n",strerror(errno));
|
|
Q_ASSERT_X(0, "InotifyWatch", "Failed to initialize inotify");
|
|
}
|
|
}
|
|
|
|
setPath(DirWatcher::getDirWatcher()->currentIndexableDir());
|
|
setBlockPath(DirWatcher::getDirWatcher()->currentBlackListOfIndex());
|
|
firstTraverse();
|
|
|
|
while(FileUtils::SearchMethod::INDEXSEARCH == FileUtils::searchMethod) {
|
|
fd_set fds;
|
|
FD_ZERO(&fds);
|
|
FD_SET(m_inotifyFd, &fds);
|
|
int rc;
|
|
rc = select(m_inotifyFd + 1, &fds, NULL, NULL, NULL);
|
|
if(rc > 0) {
|
|
++FileUtils::indexStatus;
|
|
int avail;
|
|
if (ioctl(m_inotifyFd, FIONREAD, &avail) == EINVAL) {
|
|
qWarning() << "Did not receive an entire inotify event.";
|
|
--FileUtils::indexStatus;
|
|
return;
|
|
}
|
|
|
|
char* buf = (char*)malloc(avail);
|
|
memset(buf, 0x00, avail);
|
|
|
|
const ssize_t len = read(m_inotifyFd, buf, avail);
|
|
if(len != avail) {
|
|
qWarning()<<"read event error";
|
|
// IndexStatusRecorder::getInstance()->setStatus(INOTIFY_NORMAL_EXIT, "1");
|
|
}
|
|
|
|
int i = 0;
|
|
while (i < len) {
|
|
const struct inotify_event* event = (struct inotify_event*)&buf[i];
|
|
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;
|
|
}
|
|
i += sizeof(struct inotify_event) + event->len;
|
|
}
|
|
if(i < len ) {
|
|
qDebug() << "fork";
|
|
slotEvent(buf, len);
|
|
free(buf);
|
|
}
|
|
--FileUtils::indexStatus;
|
|
} else if(rc < 0) {
|
|
// error
|
|
qWarning() << "select result < 0, error!";
|
|
IndexStatusRecorder::getInstance()->setStatus(INOTIFY_NORMAL_EXIT, "1");
|
|
assert(false);
|
|
}
|
|
}
|
|
qDebug() << "Leave watch loop";
|
|
if(FileUtils::SearchMethod::DIRECTSEARCH == FileUtils::searchMethod) {
|
|
IndexStatusRecorder::getInstance()->setStatus(INOTIFY_NORMAL_EXIT, "3");
|
|
for(QString path : m_pathMap) {
|
|
inotify_rm_watch(m_inotifyFd, m_pathMap.key(path));
|
|
}
|
|
m_pathMap.clear();
|
|
}
|
|
close(m_inotifyFd);
|
|
// fcntl(m_inotifyFd, F_SETFD, FD_CLOEXEC);
|
|
// m_notifier = new QSocketNotifier(m_inotifyFd, QSocketNotifier::Read);
|
|
// connect(m_notifier, &QSocketNotifier::activated, this, &InotifyWatch::slotEvent, Qt::DirectConnection);
|
|
// exec();
|
|
}
|
|
|
|
void InotifyWatch::slotEvent(char *buf, ssize_t len)
|
|
{
|
|
// eventProcess(socket);
|
|
if(FileUtils::SearchMethod::INDEXSEARCH == FileUtils::searchMethod) {
|
|
pid_t pid;
|
|
pid = fork();
|
|
if(pid == 0) {
|
|
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
|
prctl(PR_SET_NAME, "inotify-index");
|
|
this->eventProcess(buf, len);
|
|
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_inotifyFd, &read_fds);
|
|
rc = select(m_inotifyFd + 1, &read_fds, NULL, NULL, read_timeout);
|
|
if(rc < 0) {
|
|
// error
|
|
qWarning() << "fork select result < 0, error!";
|
|
IndexStatusRecorder::getInstance()->setStatus(INOTIFY_NORMAL_EXIT, "1");
|
|
assert(false);
|
|
} else if(rc == 0) {
|
|
qDebug() << "select timeout!";
|
|
::free(read_timeout);
|
|
|
|
QBuffer buffer;
|
|
QDataStream out(&buffer);
|
|
if (m_sharedMemory->isAttached()) {
|
|
m_sharedMemory->detach();
|
|
}
|
|
buffer.open(QBuffer::ReadWrite);
|
|
out << m_pathMap;
|
|
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();
|
|
PendingFileQueue::getInstance()->forceFinish();
|
|
PendingFileQueue::getInstance()->~PendingFileQueue();
|
|
::_exit(0);
|
|
} else {
|
|
// qDebug() << "Select remain:" <<read_timeout->tv_sec;
|
|
this->eventProcess(m_inotifyFd);
|
|
// qDebug() << "Select remain:" <<read_timeout->tv_sec;
|
|
}
|
|
}
|
|
} else if(pid > 0) {
|
|
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();
|
|
m_pathMap = pathMap;
|
|
}
|
|
} else {
|
|
assert(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
char * InotifyWatch::filter()
|
|
{
|
|
int avail;
|
|
if (ioctl(m_inotifyFd, FIONREAD, &avail) == EINVAL) {
|
|
qWarning() << "Did not receive an entire inotify event.";
|
|
return NULL;
|
|
}
|
|
|
|
char* buffer = (char*)malloc(avail);
|
|
memset(buffer, 0x00, avail);
|
|
|
|
const int len = read(m_inotifyFd, buffer, avail);
|
|
if(len != avail) {
|
|
qWarning()<<"read event error";
|
|
// IndexStatusRecorder::getInstance()->setStatus(INOTIFY_NORMAL_EXIT, "1");
|
|
}
|
|
|
|
int i = 0;
|
|
while (i < len) {
|
|
const struct inotify_event* event = (struct inotify_event*)&buffer[i];
|
|
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);
|
|
i += sizeof(struct inotify_event) + event->len;
|
|
return buffer;
|
|
}
|
|
}
|
|
free(buffer);
|
|
return NULL;
|
|
}
|
|
void InotifyWatch::eventProcess(int socket)
|
|
{
|
|
// qDebug()<< "Enter eventProcess!";
|
|
int avail;
|
|
if (ioctl(socket, FIONREAD, &avail) == EINVAL) {
|
|
qWarning() << "Did not receive an entire inotify event.";
|
|
return;
|
|
}
|
|
|
|
char* buffer = (char*)malloc(avail);
|
|
memset(buffer, 0x00, avail);
|
|
|
|
const ssize_t len = read(socket, buffer, avail);
|
|
if(len != avail) {
|
|
qWarning()<<"read event error";
|
|
// IndexStatusRecorder::getInstance()->setStatus(INOTIFY_NORMAL_EXIT, "1");
|
|
}
|
|
int i = 0;
|
|
while (i < len) {
|
|
const struct inotify_event* event = (struct inotify_event*)&buffer[i];
|
|
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;
|
|
}
|
|
i += sizeof(struct inotify_event) + event->len;
|
|
}
|
|
if(i >= len) {
|
|
qDebug() << "There is nothing to do!";
|
|
return;
|
|
}
|
|
eventProcess(buffer, len);
|
|
free(buffer);
|
|
}
|
|
|
|
void InotifyWatch::eventProcess(const char *buffer, ssize_t len)
|
|
{
|
|
// qDebug()<< "Begin eventProcess! len:" << len;
|
|
|
|
char * p = const_cast<char*>(buffer);
|
|
while (p < buffer + len) {
|
|
const struct inotify_event* event = reinterpret_cast<inotify_event *>(p);
|
|
// qDebug() << "Read Event: " << currentPath[event->wd] << QString(event->name) << event->cookie << event->wd << event->mask;
|
|
// qDebug("mask:0x%x,",event->mask);
|
|
if(event->name[0] != '.') {
|
|
QString path = m_pathMap[event->wd] + '/' + event->name;
|
|
|
|
//过滤黑名单下的信号
|
|
for(QString i : m_blockList) {
|
|
if(FileUtils::isOrUnder(path, i))
|
|
goto next;
|
|
}
|
|
|
|
//Create top dir first, traverse it last.
|
|
if(event->mask & IN_CREATE) {
|
|
// qDebug() << "IN_CREATE";
|
|
PendingFile f(path);
|
|
if(event->mask & IN_ISDIR) {
|
|
f.setIsDir();
|
|
}
|
|
PendingFileQueue::getInstance()->enqueue(f);
|
|
|
|
if(event->mask & IN_ISDIR) {
|
|
if(!QFileInfo(path).isSymLink()){
|
|
addWatch(path);
|
|
setPath(QStringList() << path);
|
|
Traverse();
|
|
}
|
|
}
|
|
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(path);
|
|
} else {
|
|
PendingFile f(path);
|
|
f.setDeleted();
|
|
PendingFileQueue::getInstance()->enqueue(f);
|
|
}
|
|
p += sizeof(struct inotify_event) + event->len;
|
|
continue;
|
|
}
|
|
if(event->mask & IN_MODIFY) {
|
|
// qDebug() << "IN_MODIFY";
|
|
if(!(event->mask & IN_ISDIR)) {
|
|
PendingFileQueue::getInstance()->enqueue(PendingFile(path));
|
|
}
|
|
goto next;
|
|
|
|
}
|
|
|
|
if(event->mask & IN_MOVED_TO) {
|
|
qDebug() << "IN_MOVED_TO";
|
|
if(event->mask & IN_ISDIR) {
|
|
removeWatch(path);
|
|
|
|
PendingFile f(path);
|
|
f.setIsDir();
|
|
PendingFileQueue::getInstance()->enqueue(f);
|
|
|
|
if(!QFileInfo(path).isSymLink()){
|
|
addWatch(path);
|
|
setPath(QStringList() << path);
|
|
Traverse();
|
|
}
|
|
} else {
|
|
//Enqueue a deleted file to merge.
|
|
PendingFile f(path);
|
|
f.setDeleted();
|
|
PendingFileQueue::getInstance()->enqueue(f);
|
|
//Enqueue a new one.
|
|
PendingFileQueue::getInstance()->enqueue(PendingFile(path));
|
|
}
|
|
goto next;
|
|
}
|
|
}
|
|
next:
|
|
p += sizeof(struct inotify_event) + event->len;
|
|
}
|
|
// qDebug()<< "Finish eventProcess!";
|
|
}
|
|
|
|
|