diff options
author | netblue30 <netblue30@protonmail.com> | 2023-07-25 10:22:59 -0400 |
---|---|---|
committer | netblue30 <netblue30@protonmail.com> | 2023-07-25 10:22:59 -0400 |
commit | 8e4b847cdd9203227c003152d1ea4a2dd0f91664 (patch) | |
tree | aec337dd5faa357606ac06874f2168db6afe520e /src | |
parent | cleanup (diff) | |
download | firejail-8e4b847cdd9203227c003152d1ea4a2dd0f91664.tar.gz firejail-8e4b847cdd9203227c003152d1ea4a2dd0f91664.tar.zst firejail-8e4b847cdd9203227c003152d1ea4a2dd0f91664.zip |
split nettrace executable ^Cto netrace and netlock
Diffstat (limited to 'src')
-rw-r--r-- | src/firejail/netfilter.c | 4 | ||||
-rw-r--r-- | src/fnetlock/Makefile | 9 | ||||
-rw-r--r-- | src/fnetlock/fnetlock.h | 51 | ||||
-rw-r--r-- | src/fnetlock/main.c | 392 | ||||
-rw-r--r-- | src/fnetlock/tail.c | 63 |
5 files changed, 517 insertions, 2 deletions
diff --git a/src/firejail/netfilter.c b/src/firejail/netfilter.c index 458fb0dd1..e07776163 100644 --- a/src/firejail/netfilter.c +++ b/src/firejail/netfilter.c | |||
@@ -65,7 +65,7 @@ void netfilter_netlock(pid_t pid) { | |||
65 | umask(orig_umask); | 65 | umask(orig_umask); |
66 | 66 | ||
67 | char *cmd; | 67 | char *cmd; |
68 | if (asprintf(&cmd, "%s -e \"%s/firejail/fnettrace --tail --log=%s\"", terminal, LIBDIR, flog) == -1) | 68 | if (asprintf(&cmd, "%s -e \"%s/firejail/fnetlock --tail --log=%s\"", terminal, LIBDIR, flog) == -1) |
69 | errExit("asprintf"); | 69 | errExit("asprintf"); |
70 | int rv = system(cmd); | 70 | int rv = system(cmd); |
71 | (void) rv; | 71 | (void) rv; |
@@ -74,7 +74,7 @@ void netfilter_netlock(pid_t pid) { | |||
74 | } | 74 | } |
75 | 75 | ||
76 | char *cmd; | 76 | char *cmd; |
77 | if (asprintf(&cmd, "%s/firejail/fnettrace --netfilter --log=%s", LIBDIR, flog) == -1) | 77 | if (asprintf(&cmd, "%s/firejail/fnetlock --log=%s", LIBDIR, flog) == -1) |
78 | errExit("asprintf"); | 78 | errExit("asprintf"); |
79 | free(flog); | 79 | free(flog); |
80 | 80 | ||
diff --git a/src/fnetlock/Makefile b/src/fnetlock/Makefile new file mode 100644 index 000000000..789df06ac --- /dev/null +++ b/src/fnetlock/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | .SUFFIXES: | ||
2 | ROOT = ../.. | ||
3 | -include $(ROOT)/config.mk | ||
4 | |||
5 | MOD_DIR = src/fnetlock | ||
6 | PROG = fnetlock | ||
7 | TARGET = $(PROG) | ||
8 | |||
9 | include $(ROOT)/src/prog.mk | ||
diff --git a/src/fnetlock/fnetlock.h b/src/fnetlock/fnetlock.h new file mode 100644 index 000000000..018f58223 --- /dev/null +++ b/src/fnetlock/fnetlock.h | |||
@@ -0,0 +1,51 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2023 Firejail Authors | ||
3 | * | ||
4 | * This file is part of firejail project | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | #ifndef FNETLOCK_H | ||
21 | #define FNETLOCK_H | ||
22 | |||
23 | #include "../include/common.h" | ||
24 | #include <unistd.h> | ||
25 | #include <sys/stat.h> | ||
26 | #include <sys/types.h> | ||
27 | #include <sys/socket.h> | ||
28 | #include <netinet/in.h> | ||
29 | #include <time.h> | ||
30 | #include <stdarg.h> | ||
31 | #include <fcntl.h> | ||
32 | #include <sys/mman.h> | ||
33 | |||
34 | |||
35 | //#define DEBUG 1 | ||
36 | |||
37 | #define NETLOCK_INTERVAL 60 // seconds | ||
38 | |||
39 | static inline uint8_t hash(uint32_t ip) { | ||
40 | uint8_t *ptr = (uint8_t *) &ip; | ||
41 | // simple byte xor | ||
42 | return *ptr ^ *(ptr + 1) ^ *(ptr + 2) ^ *(ptr + 3); | ||
43 | } | ||
44 | |||
45 | // main.c | ||
46 | void logprintf(char* fmt, ...); | ||
47 | |||
48 | // tail.c | ||
49 | void tail(const char *logfile); | ||
50 | |||
51 | #endif | ||
diff --git a/src/fnetlock/main.c b/src/fnetlock/main.c new file mode 100644 index 000000000..b76be029d --- /dev/null +++ b/src/fnetlock/main.c | |||
@@ -0,0 +1,392 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2023 Firejail Authors | ||
3 | * | ||
4 | * This file is part of firejail project | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | #include "fnetlock.h" | ||
21 | #include <limits.h> | ||
22 | #include <sys/ioctl.h> | ||
23 | #include <sys/prctl.h> | ||
24 | #include <signal.h> | ||
25 | #define MAX_BUF_SIZE (64 * 1024) | ||
26 | |||
27 | static int arg_tail = 0; | ||
28 | static char *arg_log = NULL; | ||
29 | |||
30 | //***************************************************************** | ||
31 | // traffic trace storage - hash table for fast access + linked list for display purposes | ||
32 | //***************************************************************** | ||
33 | typedef struct hnode_t { | ||
34 | struct hnode_t *hnext; // used for hash table and unused linked list | ||
35 | struct hnode_t *dnext; // used to display streams on the screen | ||
36 | uint32_t ip_src; | ||
37 | uint16_t port_src; | ||
38 | uint8_t protocol; | ||
39 | |||
40 | // the firewall is build based on source address, and in the linked list | ||
41 | // we could have elements with the same address but different ports | ||
42 | uint8_t ip_instance; | ||
43 | } HNode; | ||
44 | |||
45 | // hash table | ||
46 | #define HMAX 256 | ||
47 | HNode *htable[HMAX] = {NULL}; | ||
48 | static int have_traffic = 0; | ||
49 | |||
50 | // using protocol 0 and port 0 for ICMP | ||
51 | static void hnode_add(uint32_t ip_src, uint8_t protocol, uint16_t port_src) { | ||
52 | uint8_t h = hash(ip_src); | ||
53 | int ip_instance = 0; | ||
54 | HNode *ptr = htable[h]; | ||
55 | while (ptr) { | ||
56 | if (ptr->ip_src == ip_src) { | ||
57 | ip_instance++; | ||
58 | if (ptr->port_src == port_src && ptr->protocol == protocol) | ||
59 | return; | ||
60 | } | ||
61 | ptr = ptr->hnext; | ||
62 | } | ||
63 | |||
64 | logprintf("netlock: adding %d.%d.%d.%d\n", PRINT_IP(ip_src)); | ||
65 | have_traffic = 1; | ||
66 | HNode *hnew = malloc(sizeof(HNode)); | ||
67 | assert(hnew); | ||
68 | hnew->ip_src = ip_src; | ||
69 | hnew->port_src = port_src; | ||
70 | hnew->protocol = protocol; | ||
71 | hnew->hnext = NULL; | ||
72 | hnew->ip_instance = ip_instance + 1; | ||
73 | if (htable[h] == NULL) | ||
74 | htable[h] = hnew; | ||
75 | else { | ||
76 | hnew->hnext = htable[h]; | ||
77 | htable[h] = hnew; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | |||
82 | |||
83 | |||
84 | // trace rx traffic coming in | ||
85 | static void run_trace(void) { | ||
86 | logprintf("netlock: accumulating traffic for %d seconds\n", NETLOCK_INTERVAL); | ||
87 | |||
88 | // trace only rx ipv4 tcp and upd | ||
89 | int s1 = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); | ||
90 | int s2 = socket(AF_INET, SOCK_RAW, IPPROTO_UDP); | ||
91 | if (s1 < 0 || s2 < 0) | ||
92 | errExit("socket"); | ||
93 | |||
94 | |||
95 | unsigned start = time(NULL); | ||
96 | unsigned char buf[MAX_BUF_SIZE]; | ||
97 | unsigned bw = 0; // bandwidth calculations | ||
98 | |||
99 | int printed = 0; | ||
100 | while (1) { | ||
101 | unsigned runtime = time(NULL) - start; | ||
102 | if ( runtime >= NETLOCK_INTERVAL) | ||
103 | break; | ||
104 | if (runtime % 10 == 0) { | ||
105 | if (!printed) | ||
106 | logprintf("netlock: %u seconds remaining\n", NETLOCK_INTERVAL - runtime); | ||
107 | printed = 1; | ||
108 | } | ||
109 | else | ||
110 | printed = 0; | ||
111 | |||
112 | fd_set rfds; | ||
113 | FD_ZERO(&rfds); | ||
114 | FD_SET(s1, &rfds); | ||
115 | FD_SET(s2, &rfds); | ||
116 | int maxfd = (s1 > s2) ? s1 : s2; | ||
117 | maxfd++; | ||
118 | |||
119 | struct timeval tv; | ||
120 | tv.tv_sec = 1; | ||
121 | tv.tv_usec = 0; | ||
122 | |||
123 | int rv = select(maxfd, &rfds, NULL, NULL, &tv); | ||
124 | if (rv < 0) | ||
125 | errExit("select"); | ||
126 | else if (rv == 0) | ||
127 | continue; | ||
128 | |||
129 | |||
130 | // rx tcp traffic by default | ||
131 | int sock = s1; | ||
132 | |||
133 | if (FD_ISSET(s2, &rfds)) | ||
134 | sock = s2; | ||
135 | |||
136 | unsigned bytes = recvfrom(sock, buf, MAX_BUF_SIZE, 0, NULL, NULL); | ||
137 | if (bytes >= 20) { // size of IP header | ||
138 | #ifdef DEBUG | ||
139 | { | ||
140 | uint32_t ip_src; | ||
141 | memcpy(&ip_src, buf + 12, 4); | ||
142 | ip_src = ntohl(ip_src); | ||
143 | |||
144 | uint32_t ip_dst; | ||
145 | memcpy(&ip_dst, buf + 16, 4); | ||
146 | ip_dst = ntohl(ip_dst); | ||
147 | printf("%d.%d.%d.%d -> %d.%d.%d.%d, %u bytes\n", PRINT_IP(ip_src), PRINT_IP(ip_dst), bytes); | ||
148 | } | ||
149 | #endif | ||
150 | // filter out loopback traffic | ||
151 | if (buf[12] != 127 && buf[16] != 127) { | ||
152 | bw += bytes + 14; // assume a 14 byte Ethernet layer | ||
153 | |||
154 | uint32_t ip_src; | ||
155 | memcpy(&ip_src, buf + 12, 4); | ||
156 | ip_src = ntohl(ip_src); | ||
157 | |||
158 | uint8_t hlen = (buf[0] & 0x0f) * 4; | ||
159 | uint16_t port_src = 0; | ||
160 | memcpy(&port_src, buf + hlen, 2); | ||
161 | port_src = ntohs(port_src); | ||
162 | |||
163 | uint8_t protocol = buf[9]; | ||
164 | hnode_add(ip_src, protocol, port_src); | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | |||
169 | close(s1); | ||
170 | close(s2); | ||
171 | } | ||
172 | |||
173 | static char *filter_start = | ||
174 | "*filter\n" | ||
175 | ":INPUT DROP [0:0]\n" | ||
176 | ":FORWARD DROP [0:0]\n" | ||
177 | ":OUTPUT DROP [0:0]\n"; | ||
178 | |||
179 | // return 1 if error | ||
180 | static int print_filter(FILE *fp) { | ||
181 | fprintf(fp, "%s\n", filter_start); | ||
182 | fprintf(fp, "-A INPUT -s 127.0.0.0/8 -j ACCEPT\n"); | ||
183 | fprintf(fp, "-A OUTPUT -d 127.0.0.0/8 -j ACCEPT\n"); | ||
184 | fprintf(fp, "\n"); | ||
185 | |||
186 | int i; | ||
187 | for (i = 0; i < HMAX; i++) { | ||
188 | HNode *ptr = htable[i]; | ||
189 | while (ptr) { | ||
190 | // filter rules are targeting ip address, the port number is disregarded, | ||
191 | // so we look only at the first instance of an address | ||
192 | if (ptr->ip_instance == 1) { | ||
193 | char *protocol = (ptr->protocol == 6) ? "tcp" : "udp"; | ||
194 | fprintf(fp, "-A INPUT -s %d.%d.%d.%d -p %s -j ACCEPT\n", | ||
195 | PRINT_IP(ptr->ip_src), | ||
196 | protocol); | ||
197 | fprintf(fp, "-A OUTPUT -d %d.%d.%d.%d -p %s -j ACCEPT\n", | ||
198 | PRINT_IP(ptr->ip_src), | ||
199 | protocol); | ||
200 | fprintf(fp, "\n"); | ||
201 | } | ||
202 | ptr = ptr->hnext; | ||
203 | } | ||
204 | } | ||
205 | fprintf(fp, "COMMIT\n"); | ||
206 | |||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | static char *flush_rules[] = { | ||
211 | "-P INPUT ACCEPT", | ||
212 | // "-P FORWARD DENY", | ||
213 | "-P OUTPUT ACCEPT", | ||
214 | "-F", | ||
215 | "-X", | ||
216 | // "-t nat -F", | ||
217 | // "-t nat -X", | ||
218 | // "-t mangle -F", | ||
219 | // "-t mangle -X", | ||
220 | // "iptables -t raw -F", | ||
221 | // "-t raw -X", | ||
222 | NULL | ||
223 | }; | ||
224 | |||
225 | static void deploy_netfilter(void) { | ||
226 | int rv; | ||
227 | char *cmd; | ||
228 | int i; | ||
229 | |||
230 | if (have_traffic == 0) { | ||
231 | logprintf("Sorry, no network traffic was detected. The firewall was not configured.\n"); | ||
232 | return; | ||
233 | } | ||
234 | // find iptables command | ||
235 | char *iptables = NULL; | ||
236 | char *iptables_restore = NULL; | ||
237 | if (access("/sbin/iptables", X_OK) == 0) { | ||
238 | iptables = "/sbin/iptables"; | ||
239 | iptables_restore = "/sbin/iptables-restore"; | ||
240 | } | ||
241 | else if (access("/usr/sbin/iptables", X_OK) == 0) { | ||
242 | iptables = "/usr/sbin/iptables"; | ||
243 | iptables_restore = "/usr/sbin/iptables-restore"; | ||
244 | } | ||
245 | if (iptables == NULL || iptables_restore == NULL) { | ||
246 | fprintf(stderr, "Error: iptables command not found, netfilter not configured\n"); | ||
247 | exit(1); | ||
248 | } | ||
249 | |||
250 | // flush all netfilter rules | ||
251 | i = 0; | ||
252 | while (flush_rules[i]) { | ||
253 | char *cmd; | ||
254 | if (asprintf(&cmd, "%s %s", iptables, flush_rules[i]) == -1) | ||
255 | errExit("asprintf"); | ||
256 | int rv = system(cmd); | ||
257 | (void) rv; | ||
258 | free(cmd); | ||
259 | i++; | ||
260 | } | ||
261 | |||
262 | // create temporary file | ||
263 | char fname[] = "/tmp/firejail-XXXXXX"; | ||
264 | int fd = mkstemp(fname); | ||
265 | if (fd == -1) { | ||
266 | fprintf(stderr, "Error: cannot create temporary configuration file\n"); | ||
267 | exit(1); | ||
268 | } | ||
269 | |||
270 | FILE *fp = fdopen(fd, "w"); | ||
271 | if (!fp) { | ||
272 | rv = unlink(fname); | ||
273 | (void) rv; | ||
274 | fprintf(stderr, "Error: cannot create temporary configuration file\n"); | ||
275 | exit(1); | ||
276 | } | ||
277 | print_filter(fp); | ||
278 | fclose(fp); | ||
279 | |||
280 | logprintf("\n\n"); | ||
281 | logprintf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); | ||
282 | if (asprintf(&cmd, "cat %s >> %s", fname, arg_log) == -1) | ||
283 | errExit("asprintf"); | ||
284 | rv = system(cmd); | ||
285 | (void) rv; | ||
286 | free(cmd); | ||
287 | |||
288 | if (asprintf(&cmd, "cat %s", fname) == -1) | ||
289 | errExit("asprintf"); | ||
290 | rv = system(cmd); | ||
291 | (void) rv; | ||
292 | free(cmd); | ||
293 | logprintf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); | ||
294 | |||
295 | |||
296 | // configuring | ||
297 | if (asprintf(&cmd, "%s %s", iptables_restore, fname) == -1) | ||
298 | errExit("asprintf"); | ||
299 | rv = system(cmd); | ||
300 | if (rv) | ||
301 | fprintf(stdout, "Warning: possible netfilter problem!"); | ||
302 | free(cmd); | ||
303 | |||
304 | rv = unlink(fname); | ||
305 | (void) rv; | ||
306 | logprintf("\nnetlock: firewall deployed\n"); | ||
307 | } | ||
308 | |||
309 | void logprintf(char *fmt, ...) { | ||
310 | if (!arg_log) | ||
311 | return; | ||
312 | |||
313 | FILE *fp = fopen(arg_log, "a"); | ||
314 | if (fp) { // disregard if error | ||
315 | va_list args; | ||
316 | va_start(args, fmt); | ||
317 | vfprintf(fp, fmt, args); | ||
318 | va_end(args); | ||
319 | fclose(fp); | ||
320 | } | ||
321 | |||
322 | va_list args; | ||
323 | va_start(args, fmt); | ||
324 | vfprintf(stdout, fmt, args); | ||
325 | va_end(args); | ||
326 | } | ||
327 | |||
328 | static const char *const usage_str = | ||
329 | "Usage: fnettrace [OPTIONS]\n" | ||
330 | "Options:\n" | ||
331 | " --help, -? - this help screen\n" | ||
332 | " --log=filename - netlocker logfile\n" | ||
333 | " --tail - \"tail -f\" functionality\n"; | ||
334 | |||
335 | static void usage(void) { | ||
336 | puts(usage_str); | ||
337 | } | ||
338 | |||
339 | int main(int argc, char **argv) { | ||
340 | int i; | ||
341 | |||
342 | for (i = 1; i < argc; i++) { | ||
343 | if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-?") == 0) { | ||
344 | usage(); | ||
345 | return 0; | ||
346 | } | ||
347 | else if (strcmp(argv[i], "--tail") == 0) | ||
348 | arg_tail = 1; | ||
349 | else if (strncmp(argv[i], "--log=", 6) == 0) | ||
350 | arg_log = argv[i] + 6; | ||
351 | else { | ||
352 | fprintf(stderr, "Error: invalid argument\n"); | ||
353 | return 1; | ||
354 | } | ||
355 | } | ||
356 | |||
357 | // tail | ||
358 | if (arg_tail) { | ||
359 | if (!arg_log) { | ||
360 | fprintf(stderr, "Error: no log file\n"); | ||
361 | usage(); | ||
362 | exit(1); | ||
363 | } | ||
364 | |||
365 | tail(arg_log); | ||
366 | sleep(5); | ||
367 | exit(0); | ||
368 | } | ||
369 | |||
370 | if (getuid() != 0) { | ||
371 | fprintf(stderr, "Error: you need to be root to run this program\n"); | ||
372 | return 1; | ||
373 | } | ||
374 | |||
375 | // kill the process if the parent died | ||
376 | prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); | ||
377 | |||
378 | logprintf("netlock: starting network lockdown\n"); | ||
379 | run_trace(); | ||
380 | |||
381 | // TCP path MTU discovery will not work properly since the firewall drops all ICMP packets | ||
382 | // Instead, we use iPacketization Layer PMTUD (RFC 4821) support in Linux kernel | ||
383 | int rv = system("echo 1 > /proc/sys/net/ipv4/tcp_mtu_probing"); | ||
384 | (void) rv; | ||
385 | |||
386 | deploy_netfilter(); | ||
387 | sleep(3); | ||
388 | if (arg_log) | ||
389 | unlink(arg_log); | ||
390 | |||
391 | return 0; | ||
392 | } | ||
diff --git a/src/fnetlock/tail.c b/src/fnetlock/tail.c new file mode 100644 index 000000000..e5d0367f6 --- /dev/null +++ b/src/fnetlock/tail.c | |||
@@ -0,0 +1,63 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2023 Firejail Authors | ||
3 | * | ||
4 | * This file is part of firejail project | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | #include "fnetlock.h" | ||
21 | |||
22 | void tail(const char *logfile) { | ||
23 | assert(logfile); | ||
24 | |||
25 | // wait for no more than 5 seconds for the logfile to appear in the filesystem | ||
26 | int cnt = 5; | ||
27 | while (access(logfile, R_OK) && cnt > 0) | ||
28 | cnt--; | ||
29 | if (cnt == 0) | ||
30 | exit(1); | ||
31 | |||
32 | off_t last_size = 0; | ||
33 | |||
34 | while (1) { | ||
35 | int fd = open(logfile, O_RDONLY); | ||
36 | if (fd == -1) | ||
37 | return; | ||
38 | |||
39 | off_t size = lseek(fd, 0, SEEK_END); | ||
40 | if (size < 0) { | ||
41 | close(fd); | ||
42 | return; | ||
43 | } | ||
44 | |||
45 | char *content = NULL; | ||
46 | int mmapped = 0; | ||
47 | if (size && size != last_size) { | ||
48 | content = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); | ||
49 | close(fd); | ||
50 | if (content != MAP_FAILED) | ||
51 | mmapped = 1; | ||
52 | } | ||
53 | |||
54 | if (mmapped) { | ||
55 | printf("%.*s", (int) (size - last_size), content + last_size); | ||
56 | fflush(0); | ||
57 | munmap(content, size); | ||
58 | last_size = size; | ||
59 | } | ||
60 | |||
61 | sleep(1); | ||
62 | } | ||
63 | } | ||