├── demo.gif
├── .travis.yml
├── Makefile
├── LICENSE
├── README.md
└── main.c
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeplea/fsghost/HEAD/demo.gif
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: c
2 |
3 | compiler:
4 | - clang
5 | - gcc
6 |
7 | script: make
8 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | cc ?= gcc
2 |
3 | ccflags = -Wall -Wshadow -O2
4 | lflags =
5 |
6 |
7 | all: fsghost
8 |
9 |
10 | fsghost: main.o
11 | $(cc) $(ccflags) -o $@ $^ $(lflags)
12 |
13 |
14 | .c.o:
15 | $(cc) -c $(ccflags) $< -o $@
16 |
17 |
18 | clean:
19 | rm *.o
20 | rm *.exe
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | zlib License
2 |
3 | Copyright (C) 2016 Lewis Van Winkle
4 |
5 | This software is provided 'as-is', without any express or implied
6 | warranty. In no event will the authors be held liable for any damages
7 | arising from the use of this software.
8 |
9 | Permission is granted to anyone to use this software for any purpose,
10 | including commercial applications, and to alter it and redistribute it
11 | freely, subject to the following restrictions:
12 |
13 | 1. The origin of this software must not be misrepresented; you must not
14 | claim that you wrote the original software. If you use this software
15 | in a product, an acknowledgement in the product documentation would be
16 | appreciated but is not required.
17 | 2. Altered source versions must be plainly marked as such, and must not be
18 | misrepresented as being the original software.
19 | 3. This notice may not be removed or altered from any source distribution.
20 |
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/codeplea/fsghost)
2 |
3 | # FsGhost - Cross-Platform File Change Monitoring in C
4 |
5 |
6 |
7 | FsGhost is a *tiny* command-line tool to monitor and notify of file changes in a
8 | given directory. FsGhost works natively on both Windows and Linux and is
9 | self-contained in only 1 C file. Its only dependencies are the operating system
10 | headers for your platform.
11 |
12 | ## Features
13 |
14 | - **ANSI C with no external dependencies**.
15 | - Contained in a single source file.
16 | - Simple.
17 | - Released under the zlib license - free for nearly any use.
18 |
19 |
20 | ## Example Usage
21 |
22 | Call `fsghost` with a single argument, the path of the directory you'd like to monitor.
23 |
24 | ```
25 | mkdir test-dir
26 | fsghost test-dir
27 | ```
28 |
29 | FsGhost will then monitor that directory. If any files are created, written, or
30 | deleted, FsGhost will write that filename to the standard output.
31 |
32 |
33 | 
34 |
35 | ## Building
36 | You can probably build by simply running `make`. If that doesn't work, try to compile
37 | `main.c`. It should be pretty easy.
38 |
39 | ## FAQ
40 | ### Does FsGhost do polling.
41 | No. FsGhost uses the proper file-system APIs on each platform to get notified when changes take place. This means
42 | that it uses very few system resources when running, and will not bog down your machine. It's made to be run in the background
43 | for long periods of time.
44 |
45 | For Windows, FsGhost uses the `ReadDirectoryChangesW` API. For Linux, it uses the `inotify` API.
46 |
--------------------------------------------------------------------------------
/main.c:
--------------------------------------------------------------------------------
1 | /*
2 | * FsGhost - Simple file change monitoring tool
3 | *
4 | * Copyright (c) 2016 Lewis Van Winkle
5 | *
6 | * http://CodePlea.com
7 | *
8 | * This software is provided 'as-is', without any express or implied
9 | * warranty. In no event will the authors be held liable for any damages
10 | * arising from the use of this software.
11 | *
12 | * Permission is granted to anyone to use this software for any purpose,
13 | * including commercial applications, and to alter it and redistribute it
14 | * freely, subject to the following restrictions:
15 | *
16 | * 1. The origin of this software must not be misrepresented; you must not
17 | * claim that you wrote the original software. If you use this software
18 | * in a product, an acknowledgement in the product documentation would be
19 | * appreciated but is not required.
20 | * 2. Altered source versions must be plainly marked as such, and must not be
21 | * misrepresented as being the original software.
22 | * 3. This notice may not be removed or altered from any source distribution.
23 | *
24 | */
25 |
26 | #include
27 | #include
28 |
29 | #ifdef _WIN32
30 |
31 | #define WIN32_LEAN_AND_MEAN
32 | #include
33 |
34 | int watch(const char *dir) {
35 |
36 | HANDLE hdir = CreateFile(
37 | dir,
38 | FILE_LIST_DIRECTORY,
39 | FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
40 | 0,
41 | OPEN_EXISTING,
42 | FILE_FLAG_BACKUP_SEMANTICS,
43 | 0);
44 |
45 | if (hdir == INVALID_HANDLE_VALUE) {
46 | DWORD err = GetLastError();
47 | printf("Error - CreateFile %ld\n", err);
48 | return 1;
49 | }
50 |
51 |
52 |
53 | const int buffer_size = 4096;
54 | void *buffer = malloc(buffer_size);
55 |
56 | if (!buffer) {
57 | printf("Error - Couldn't allocate %d bytes.\n", buffer_size);
58 | return 1;
59 | }
60 |
61 |
62 | while (1) {
63 | DWORD rsize;
64 | int rd = ReadDirectoryChangesW(
65 | hdir,
66 | buffer,
67 | buffer_size,
68 | 0,
69 | FILE_NOTIFY_CHANGE_FILE_NAME |
70 | FILE_NOTIFY_CHANGE_DIR_NAME |
71 | FILE_NOTIFY_CHANGE_ATTRIBUTES |
72 | FILE_NOTIFY_CHANGE_SIZE |
73 | FILE_NOTIFY_CHANGE_LAST_WRITE |
74 | FILE_NOTIFY_CHANGE_SECURITY,
75 | &rsize,
76 | 0,
77 | 0);
78 |
79 | if (!rd) {
80 | printf("Error - ReadDirectoryChangesW %d\n", rd);
81 | return 1;
82 | }
83 |
84 |
85 | FILE_NOTIFY_INFORMATION *p = buffer;
86 |
87 | while (rsize > 0) {
88 |
89 | DWORD action = p->Action;
90 | DWORD len = p->FileNameLength;
91 | WCHAR *fname = p->FileName;
92 |
93 | switch (action) {
94 | case FILE_ACTION_ADDED: printf("added "); break;
95 | case FILE_ACTION_REMOVED: printf("removed "); break;
96 | case FILE_ACTION_MODIFIED: printf("modified "); break;
97 | case FILE_ACTION_RENAMED_OLD_NAME: printf("removed "); break;
98 | case FILE_ACTION_RENAMED_NEW_NAME: printf("added "); break;
99 | default: printf("error "); break;
100 | }
101 |
102 | printf("%.*S\n", (int)(len/2), fname);
103 |
104 | if (p->NextEntryOffset) {
105 | p = (void*)p + p->NextEntryOffset;
106 | } else {
107 | break;
108 | }
109 | }
110 | }
111 |
112 |
113 | return 1;
114 | }
115 |
116 | #elif __linux__
117 |
118 | #include
119 | #include
120 | #include
121 | #include
122 | #include
123 |
124 |
125 | int watch(const char *dir) {
126 |
127 | int fd = inotify_init();
128 | if (fd < 0) {
129 | printf("Error - inotify_init failed.\n");
130 | return 1;
131 | }
132 |
133 | const int buffer_size = 4096;
134 | char *buffer = malloc(buffer_size);
135 |
136 | if (!buffer) {
137 | printf("Error - Couldn't allocate %d bytes.\n", buffer_size);
138 | return 1;
139 | }
140 |
141 | int wd = inotify_add_watch(fd, dir, IN_ALL_EVENTS);
142 | if (wd < 0) {
143 | printf("Error - inotify_add_watch failed.\n");
144 | return 1;
145 | }
146 |
147 | while (1) {
148 | int len = read(fd, buffer, buffer_size);
149 |
150 | if (len < 0) {
151 | printf("Error - read failed.\n");
152 | return 1;
153 | }
154 |
155 |
156 | char *p = buffer;
157 | while(p < buffer + len) {
158 | struct inotify_event *evt = (struct inotify_event *)buffer;
159 | if (evt->len) {
160 | if (evt->mask & (IN_CREATE | IN_MOVED_TO)) {
161 | printf("added %s\n", evt->name);
162 | } else if (evt->mask & (IN_DELETE | IN_MOVED_FROM)) {
163 | printf("removed %s\n", evt->name);
164 | } else if (evt->mask & IN_MODIFY) {
165 | printf("modified %s\n", evt->name);
166 | }
167 | }
168 |
169 | p += (sizeof(struct inotify_event) + evt->len);
170 | }
171 | }
172 |
173 |
174 | return 1;
175 | }
176 |
177 |
178 | #elif __APPLE__
179 | /* TODO */
180 | #error("APPLE IS UNSUPPORTED PLATFORM")
181 | #else
182 | #error("UNSUPPORTED PLATFORM")
183 | #endif
184 |
185 | int main(int argc, char *argv[]) {
186 | if (argc != 2) {
187 | printf("Usage: fsghost directory\n");
188 | return 0;
189 | }
190 |
191 |
192 | return watch(argv[1]);
193 | }
194 |
--------------------------------------------------------------------------------