1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#ifndef INOTIFY_LINUX_H
6#define INOTIFY_LINUX_H
7
8#include "inotify_hook.h"
9
10#include <QDebug>
11
12#include <poll.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <sys/inotify.h>
16#include <unistd.h>
17#include <string.h>
18
19class InotifyLinux : public InotifyHook
20{
21 Q_OBJECT
22 int inotifyFD {0};
23 struct pollfd pfdCache {};
24 QHash<Type, int> typeMapping
25 {
26 { Type::ACCESS, IN_ACCESS },
27 { Type::MODIFY, IN_MODIFY },
28 { Type::ATTRIB, IN_ATTRIB },
29 { Type::CLOSE_WRITE, IN_CLOSE_WRITE },
30 { Type::CLOSE_NOWRITE, IN_CLOSE_NOWRITE },
31 { Type::CLOSE, IN_CLOSE },
32 { Type::OPEN, IN_OPEN },
33 { Type::MOVED_FROM, IN_MOVED_FROM },
34 { Type::MOVED_TO, IN_MOVED_TO },
35 { Type::MOVE, IN_MOVE },
36 { Type::CREATE, IN_CREATE },
37 { Type::DELETE, IN_DELETE },
38 { Type::DELETE_SELF, IN_DELETE_SELF },
39 { Type::MOVE_SELF, IN_MOVE_SELF },
40 };
41 QHash<int, QString> watchPaths {};
42 QReadWriteLock rwLock {};
43
44public:
45 InotifyLinux()
46 {
47 inotifyFD = inotify_init1(IN_NONBLOCK);
48 if (inotifyFD == -1) {
49 qCritical() << "Failed, create inotify fd";
50 }
51 pfdCache = {inotifyFD, POLLIN, 0};
52 }
53
54 ~InotifyLinux() override
55 {
56 if (inotifyFD > 0) {
57 for(auto wd : watchPaths.keys()) {
58 inotify_rm_watch(inotifyFD, wd);
59 }
60 close(inotifyFD);
61 }
62 }
63
64 virtual void addPath(const QString &path) override
65 {
66 if (inotifyFD == -1)
67 return;
68
69 QWriteLocker lock(&rwLock);
70 int watcherID = inotify_add_watch(inotifyFD, path.toLatin1(),
71 IN_MODIFY| IN_OPEN| IN_CLOSE| IN_CREATE|
72 IN_MOVED_TO| IN_MOVED_FROM| IN_MOVE_SELF| IN_MOVE|
73 IN_DELETE| IN_DELETE_SELF);
74
75 if (watcherID == -1) {
76 qCritical() << "Failed, Create watcher from called inotify_add_watch";
77 return;
78 }
79
80 watchPaths[watcherID] = path;
81 }
82
83 virtual void removePath(const QString &path) override
84 {
85 QWriteLocker lock(&rwLock);
86
87 int wd = watchPaths.key(path);
88 if (wd < 0)
89 return;
90
91 inotify_rm_watch(inotifyFD, wd);
92 watchPaths.remove(wd);
93 }
94
95 virtual void run() override
96 {
97 if (inotifyFD == -1) {
98 return;
99 }
100
101 while(!stopFlag) {
102 int poll_num = -1;
103 poll_num = poll(&pfdCache, 1, 15);
104 if (poll_num == -1) {
105 if (errno == EINTR)
106 continue;
107 qCritical() << "Failed, Create poll instance from called poll()";
108 return;
109 }
110 if (poll_num > 0) {
111 if (pfdCache.revents & POLLIN) {
112 char buf[4096] __attribute__ ((aligned(__alignof__(struct inotify_event))));
113 const struct inotify_event *event;
114 for (;;) {
115 ssize_t len = read(inotifyFD, buf, sizeof(buf));
116 if (len == -1 && errno != EAGAIN) {
117 continue;
118 }
119 if (len <= 0)
120 break;
121 for (char *ptr = buf; ptr < buf + len; ptr += sizeof(struct inotify_event) + event->len) {
122
123 event = (const struct inotify_event *) ptr;
124
125 QString filePath = watchPaths.value(event->wd);
126 if (filePath.isEmpty())
127 continue;
128
129 for (auto val : typeMapping.values()) {
130 if (event->mask & val) {
131 auto type = typeMapping.key(val);
132 emit inotifyEvent(type, filePath);
133 break;
134 }
135 }
136 }
137 } // for read event
138 }
139 }
140 }
141 }
142};
143
144#endif // INOTIFY_LINUX_H
145