Time out inactive peers after 3 minutes.
[doldaconnect.git] / daemon / client.c
CommitLineData
d3372da9 1/*
2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18*/
19#include <stdlib.h>
20#include <stdio.h>
21#include <malloc.h>
22#include <wchar.h>
23#include <string.h>
24#include <errno.h>
25#include <dirent.h>
26#include <sys/stat.h>
27#include <unistd.h>
28#include <fcntl.h>
29#include <signal.h>
30
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34#include "client.h"
35#include "conf.h"
36#include "log.h"
37#include "utils.h"
38#include "module.h"
39#include "tiger.h"
40#include "net.h"
41#include "sysevents.h"
42
43struct scanstate
44{
45 struct scanstate *next;
46 struct sharecache *node;
47 DIR *dd;
48};
49
50struct scanqueue
51{
52 struct scanqueue *next;
53 struct scanstate *state;
54};
55
56static int conf_share(int argc, wchar_t **argv);
57static void freecache(struct sharecache *node);
58static void checkhashes(void);
59
60static struct configvar myvars[] =
61{
62 {CONF_VAR_STRING, "defnick", {.str = L"DoldaConnect user"}},
63 {CONF_VAR_INT, "scanfilemask", {.num = 0004}},
64 {CONF_VAR_INT, "scandirmask", {.num = 0005}},
65 {CONF_VAR_STRING, "hashcache", {.str = L"dc-hashcache"}},
66 {CONF_VAR_END}
67};
68
69static struct configcmd mycmds[] =
70{
71 {"share", conf_share},
72 {NULL}
73};
74
75static struct scanstate *scanjob = NULL;
76static struct scanqueue *scanqueue = NULL;
77static struct sharepoint *shares = NULL;
78static struct hashcache *hashcache = NULL;
79static pid_t hashjob = 0;
80struct sharecache *shareroot = NULL;
81unsigned long long sharesize = 0;
82GCBCHAIN(sharechangecb, unsigned long long);
83
84static int conf_share(int argc, wchar_t **argv)
85{
86 struct sharepoint *share;
87 char *b;
88
89 if(argc < 3)
90 {
91 flog(LOG_WARNING, "not enough arguments given for share command");
92 return(1);
93 }
94 if((b = icwcstombs(argv[2], NULL)) == NULL)
95 {
96 flog(LOG_WARNING, "could not convert wcs path (%ls) to current locale's charset: %s", argv[2], strerror(errno));
97 return(1);
98 }
99 for(share = shares; share != NULL; share = share->next)
100 {
101 if(!strcmp(share->path, b) && !wcscmp(share->name, argv[1]))
102 {
103 share->delete = 0;
104 free(b);
105 return(0);
106 }
107 }
108 share = smalloc(sizeof(*share));
109 share->path = b;
110 share->delete = 0;
111 share->name = swcsdup(argv[1]);
112 share->next = shares;
113 share->prev = NULL;
114 if(shares != NULL)
115 shares->prev = share;
116 shares = share;
117 return(0);
118}
119
120static void dumpsharecache(struct sharecache *node, int l)
121{
122 int i;
123
124 for(; node != NULL; node = node->next)
125 {
126 for(i = 0; i < l; i++)
127 putc('\t', stdout);
128 printf("%ls\n", node->name);
129 if(node->f.b.type == FILE_DIR)
130 dumpsharecache(node->child, l + 1);
131 }
132}
133
a55f78c6 134struct hash *newhash(wchar_t *algo, size_t len, char *buf)
135{
136 struct hash *ret;
137
138 ret = smalloc(sizeof(*ret));
139 memset(ret, 0, sizeof(*ret));
140 ret->algo = swcsdup(algo);
141 ret->len = len;
142 ret->buf = memcpy(smalloc(len), buf, len);
143 return(ret);
144}
145
146void freehash(struct hash *hash)
147{
148 free(hash->algo);
149 free(hash->buf);
150 free(hash);
151}
152
153struct hash *duphash(struct hash *hash)
154{
155 return(newhash(hash->algo, hash->len, hash->buf));
156}
157
158struct hash *parsehash(wchar_t *text)
159{
160 wchar_t *p;
161 char *mbsbuf, *decbuf;
162 size_t buflen;
163 struct hash *ret;
164
165 if((p = wcschr(text, L':')) == NULL)
166 return(NULL);
167 *(p++) = L'\0';
168 if((mbsbuf = icwcstombs(p, "US-ASCII")) == NULL)
169 return(NULL);
170 decbuf = base64decode(mbsbuf, &buflen);
171 free(mbsbuf);
172 if(decbuf == NULL)
173 return(NULL);
174 ret = newhash(text, buflen, decbuf);
175 free(decbuf);
176 return(ret);
177}
178
179wchar_t *unparsehash(struct hash *hash)
180{
2eaefd31 181 static wchar_t *buf = NULL;
182 wchar_t *whbuf;
a55f78c6 183 char *hbuf;
184 size_t bufsize, bufdata;
185
2eaefd31 186 if(buf != NULL)
187 free(buf);
a55f78c6 188 buf = NULL;
189 bufsize = bufdata = 0;
190 hbuf = base64encode(hash->buf, hash->len);
191 if((whbuf = icmbstowcs(hbuf, "US-ASCII")) == NULL)
192 {
193 flog(LOG_CRIT, "bug! could not convert base64 from us-ascii: %s", strerror(errno));
194 abort();
195 }
196 free(hbuf);
197 bufcat(buf, hash->algo, wcslen(hash->algo));
198 addtobuf(buf, ':');
199 bufcat(buf, whbuf, wcslen(whbuf));
200 free(whbuf);
2b77e3c4 201 addtobuf(buf, 0);
a55f78c6 202 return(buf);
203}
204
4f8fc795 205int hashcmp(struct hash *h1, struct hash *h2)
206{
207 if(wcscmp(h1->algo, h2->algo))
208 return(0);
209 if(h1->len != h2->len)
210 return(0);
211 if(memcmp(h1->buf, h2->buf, h1->len))
212 return(0);
213 return(1);
214}
215
d3372da9 216static struct hashcache *newhashcache(void)
217{
218 struct hashcache *new;
219
220 new = smalloc(sizeof(*new));
221 memset(new, 0, sizeof(*new));
222 new->next = hashcache;
223 new->prev = NULL;
224 if(hashcache != NULL)
225 hashcache->prev = new;
226 hashcache = new;
227 return(new);
228}
229
230static void freehashcache(struct hashcache *hc)
231{
232 if(hc->next != NULL)
233 hc->next->prev = hc->prev;
234 if(hc->prev != NULL)
235 hc->prev->next = hc->next;
236 if(hc == hashcache)
237 hashcache = hc->next;
238 free(hc);
239}
240
241static char *findhashcachefile(int filldef)
242{
243 static char ret[128];
244 char *hcname;
245
246 if(getenv("HOME") != NULL)
247 {
248 snprintf(ret, sizeof(ret), "%s/.dc-hashcache", getenv("HOME"));
249 if(!access(ret, R_OK))
250 return(ret);
251 }
252 if((hcname = icswcstombs(confgetstr("cli", "hashcache"), NULL, NULL)) == NULL)
253 {
254 flog(LOG_WARNING, "could not convert hash cache name into local charset: %s", strerror(errno));
255 return(NULL);
256 }
257 if(strchr(hcname, '/') != NULL)
258 {
259 if(!access(hcname, R_OK))
260 {
261 strcpy(ret, hcname);
262 return(ret);
263 }
264 } else {
265 snprintf(ret, sizeof(ret), "/etc/%s", hcname);
266 if(!access(ret, R_OK))
267 return(ret);
268 snprintf(ret, sizeof(ret), "/usr/etc/%s", hcname);
269 if(!access(ret, R_OK))
270 return(ret);
271 snprintf(ret, sizeof(ret), "/usr/local/etc/%s", hcname);
272 if(!access(ret, R_OK))
273 return(ret);
274 }
275 if(filldef)
276 {
277 if(getenv("HOME") != NULL)
278 snprintf(ret, sizeof(ret), "%s/.dc-hashcache", getenv("HOME"));
279 else
280 snprintf(ret, sizeof(ret), "/etc/%s", hcname);
281 return(ret);
282 } else {
283 return(NULL);
284 }
285}
286
287static struct hashcache *findhashcache(dev_t dev, ino_t inode)
288{
289 struct hashcache *hc;
290
291 for(hc = hashcache; hc != NULL; hc = hc->next)
292 {
293 if((hc->dev == dev) && (hc->inode == inode))
294 return(hc);
295 }
296 return(NULL);
297}
298
299static void readhashcache(void)
300{
301 int i, wc, line;
302 char *hcname;
303 FILE *stream;
304 char linebuf[256];
305 char *p, *p2, *wv[32], *hash;
306 struct hashcache *hc;
307 size_t len;
308
309 if((hcname = findhashcachefile(0)) == NULL)
310 return;
311 if((stream = fopen(hcname, "r")) == NULL)
312 {
313 flog(LOG_WARNING, "could not open hash cache %s: %s", hcname, strerror(errno));
314 return;
315 }
316 while(hashcache != NULL)
317 freehashcache(hashcache);
318 line = 0;
319 while(!feof(stream))
320 {
321 fgets(linebuf, sizeof(linebuf), stream);
322 line++;
323 for(p = linebuf; *p; p++)
324 {
325 if(*p == '\n')
326 *p = ' ';
327 }
328 if(linebuf[0] == '#')
329 continue;
330 for(wc = 0, p = linebuf; (wc < 32) && ((p2 = strchr(p, ' ')) != NULL); p = p2 + 1)
331 {
332 if(p2 == p)
333 continue;
334 *p2 = 0;
335 wv[wc++] = p;
336 }
337 if(wc < 3)
338 continue;
339 hc = newhashcache();
340 hc->dev = strtoll(wv[0], NULL, 10);
341 hc->inode = strtoll(wv[1], NULL, 10);
342 hc->mtime = strtoll(wv[2], NULL, 10);
343 for(i = 3; i < wc; i++)
344 {
345 if(!strcmp(wv[i], "tth"))
346 {
347 if(++i >= wc)
348 continue;
349 hash = base64decode(wv[i], &len);
350 if(len != 24)
351 {
352 free(hash);
353 continue;
354 }
355 memcpy(hc->tth, hash, 24);
356 free(hash);
357 }
358 }
359 }
360 fclose(stream);
361}
362
363static void writehashcache(void)
364{
365 char *buf;
366 char *hcname;
367 FILE *stream;
368 struct hashcache *hc;
369
370 hcname = findhashcachefile(1);
371 if((stream = fopen(hcname, "w")) == NULL)
372 {
373 flog(LOG_WARNING, "could not write hash cache %s: %s", hcname, strerror(errno));
374 return;
375 }
376 fprintf(stream, "# Dolda Connect hash cache file\n");
377 fprintf(stream, "# Generated automatically, do not edit\n");
378 fprintf(stream, "# Format: DEVICE INODE MTIME [HASH...]\n");
379 fprintf(stream, "# HASH := HASHTYPE HASHVAL\n");
380 fprintf(stream, "# HASHTYPE can currently only be `tth'\n");
381 for(hc = hashcache; hc != NULL; hc = hc->next)
382 {
383 buf = base64encode(hc->tth, 24);
384 fprintf(stream, "%lli %lli %li tth %s\n", hc->dev, (long long)hc->inode, hc->mtime, buf);
385 free(buf);
386 }
387 fclose(stream);
388}
389
390static void hashread(struct socket *sk, void *uudata)
391{
392 static char *hashbuf;
393 static size_t hashbufsize = 0, hashbufdata = 0;
394 char *buf, *p, *p2, *lp;
395 size_t bufsize;
396 char *wv[32];
397 int wc;
398 dev_t dev;
399 ino_t inode;
400 time_t mtime;
401 struct hashcache *hc;
402
403 if((buf = sockgetinbuf(sk, &bufsize)) == NULL)
404 return;
405 bufcat(hashbuf, buf, bufsize);
406 free(buf);
407 while((lp = memchr(hashbuf, '\n', hashbufdata)) != NULL)
408 {
409 *(lp++) = 0;
410 wc = 0;
411 p = hashbuf;
412 while(1)
413 {
414 while((p2 = strchr(p, ' ')) == p)
415 p++;
416 wv[wc++] = p;
417 if(p2 == NULL)
418 {
419 break;
420 } else {
421 *p2 = 0;
422 p = p2 + 1;
423 }
424 }
425 if(wc != 4)
426 {
427 flog(LOG_ERR, "BUG: unexpected number of words (%i) arrived from hashing process", wc);
428 } else {
429 dev = strtoll(wv[0], NULL, 10);
430 inode = strtoll(wv[1], NULL, 10);
431 mtime = strtol(wv[2], NULL, 10);
432 if((hc = findhashcache(dev, inode)) == NULL)
433 {
434 hc = newhashcache();
435 hc->dev = dev;
436 hc->inode = inode;
437 }
438 hc->mtime = mtime;
439 buf = base64decode(wv[3], NULL);
440 memcpy(hc->tth, buf, 24);
441 free(buf);
442 writehashcache();
443 }
444 memmove(hashbuf, lp, hashbufdata -= (lp - hashbuf));
445 }
446}
447
448static void hashexit(pid_t pid, int status, void *uudata)
449{
450 if(pid != hashjob)
451 flog(LOG_ERR, "BUG: hashing process changed PID?! old: %i new %i", hashjob, pid);
452 if(status)
453 flog(LOG_WARNING, "hashing process exited with non-zero status: %i", status);
454 hashjob = 0;
455 checkhashes();
456}
457
458static int hashfile(char *path)
459{
460 int i, ret;
461 int fd;
462 int pfd[2];
463 char buf[4096];
464 struct stat sb;
465 struct tigertreehash tth;
466 char digest[24];
467 struct socket *outsock;
468
469 if((fd = open(path, O_RDONLY)) < 0)
470 {
471 flog(LOG_WARNING, "could not open %s for hashing: %s", path, strerror(errno));
472 return(1);
473 }
474 if(fstat(fd, &sb) < 0)
475 {
476 flog(LOG_WARNING, "could not stat %s while hashing: %s", path, strerror(errno));
477 close(fd);
478 return(1);
479 }
480 if(pipe(pfd) < 0)
481 {
482 flog(LOG_WARNING, "could not create pipe(!): %s", strerror(errno));
483 close(fd);
484 return(1);
485 }
486 hashjob = fork();
487 if(hashjob < 0)
488 {
489 flog(LOG_WARNING, "could not fork(!) hashing process: %s", strerror(errno));
490 close(fd);
491 close(pfd[0]);
492 close(pfd[1]);
493 return(1);
494 }
495 if(hashjob == 0)
496 {
497 nice(10);
498 signal(SIGHUP, SIG_DFL);
499 fd = dup2(fd, 4);
500 pfd[1] = dup2(pfd[1], 3);
501 dup2(fd, 0);
502 dup2(pfd[1], 1);
503 for(i = 3; i < FD_SETSIZE; i++)
504 close(i);
505 initlog();
506 inittigertree(&tth);
507 while((ret = read(0, buf, 4096)) > 0)
508 dotigertree(&tth, buf, ret);
509 if(ret < 0)
510 {
511 flog(LOG_WARNING, "could not read from %s while hashing: %s", path, strerror(errno));
512 exit(1);
513 }
514 synctigertree(&tth);
515 restigertree(&tth, digest);
516 ret = snprintf(buf, sizeof(buf), "%lli %lli %li %s\n", sb.st_dev, (long long)sb.st_ino, sb.st_mtime, base64encode(digest, 24));
517 write(1, buf, ret);
518 exit(0);
519 }
520 close(fd);
521 close(pfd[1]);
522 outsock = wrapsock(pfd[0]);
523 outsock->readcb = hashread;
524 childcallback(hashjob, hashexit, NULL);
525 return(0);
526}
527
528/*
529 * Call only when hashjob == 0
530 */
531static void checkhashes(void)
532{
533 struct sharecache *node;
534 struct hashcache *hc;
535 char *path;
536
537 node = shareroot->child;
538 while(1)
539 {
540 if(node->child != NULL)
541 {
542 node = node->child;
543 continue;
544 }
545 if(!node->f.b.hastth)
546 {
547 if((hc = findhashcache(node->dev, node->inode)) != NULL)
548 {
549 memcpy(node->hashtth, hc->tth, 24);
550 node->f.b.hastth = 1;
551 GCBCHAINDOCB(sharechangecb, sharesize);
552 } else {
553 path = getfspath(node);
554 if(hashfile(path))
555 {
556 flog(LOG_WARNING, "could not hash %s, unsharing it", path);
557 freecache(node);
558 }
559 free(path);
560 return;
561 }
562 }
563 while(node->next == NULL)
564 {
565 if((node = node->parent) == shareroot)
566 break;
567 }
568 if(node == shareroot)
569 break;
570 node = node->next;
571 }
572}
573
574struct sharecache *nextscnode(struct sharecache *node)
575{
576 if(node->child != NULL)
577 return(node->child);
578 while(node->next == NULL)
579 {
580 node = node->parent;
581 if(node == shareroot)
582 return(NULL);
583 }
584 return(node->next);
585}
586
587static void freescan(struct scanstate *job)
588{
589 if(job->dd != NULL)
590 closedir(job->dd);
591 free(job);
592}
593
594/* No need for optimization; lookup isn't really that common */
595struct sharecache *findcache(struct sharecache *parent, wchar_t *name)
596{
597 struct sharecache *node;
598
599 for(node = parent->child; node != NULL; node = node->next)
600 {
601 if(!wcscmp(node->name, name))
602 return(node);
603 }
604 return(NULL);
605}
606
607static void attachcache(struct sharecache *parent, struct sharecache *node)
608{
609 node->parent = parent;
610 node->next = parent->child;
611 if(parent->child != NULL)
612 parent->child->prev = node;
613 parent->child = node;
614}
615
616static void detachcache(struct sharecache *node)
617{
618 if(node->next != NULL)
619 node->next->prev = node->prev;
620 if(node->prev != NULL)
621 node->prev->next = node->next;
622 if((node->parent != NULL) && (node->parent->child == node))
623 node->parent->child = node->next;
624 node->parent = NULL;
625 node->next = NULL;
626 node->prev = NULL;
627}
628
629static void freecache(struct sharecache *node)
630{
631 struct sharecache *cur, *next;
632 struct scanqueue *q, *nq, **fq;
633
634 detachcache(node);
635 fq = &scanqueue;
636 for(q = scanqueue; q != NULL; q = nq)
637 {
638 nq = q->next;
639 if(q->state->node == node)
640 {
641 flog(LOG_DEBUG, "freed node %ls cancelled queued scan", node->name);
642 freescan(q->state);
643 *fq = q->next;
644 free(q);
645 continue;
646 }
647 fq = &q->next;
648 }
649 if(node->child != NULL)
650 {
651 for(cur = node->child; cur != NULL; cur = next)
652 {
653 next = cur->next;
654 freecache(cur);
655 }
656 }
657 CBCHAINDOCB(node, share_delete, node);
658 CBCHAINFREE(node, share_delete);
659 sharesize -= node->size;
660 if(node->path != NULL)
661 free(node->path);
662 if(node->name != NULL)
663 free(node->name);
664 free(node);
665}
666
667static void freesharepoint(struct sharepoint *share)
668{
669 struct sharecache *node;
670
671 if(share->next != NULL)
672 share->next->prev = share->prev;
673 if(share->prev != NULL)
674 share->prev->next = share->next;
675 if(share == shares)
676 shares = share->next;
677 if((node = findcache(shareroot, share->name)) != NULL)
678 freecache(node);
679 free(share->path);
680 free(share->name);
681 free(share);
682}
683
684static struct sharecache *newcache(void)
685{
686 struct sharecache *new;
687
688 new = smalloc(sizeof(*new));
689 memset(new, 0, sizeof(*new));
690 CBCHAININIT(new, share_delete);
691 return(new);
692}
693
694char *getfspath(struct sharecache *node)
695{
696 char *buf, *mbsname;
697 size_t bufsize;
698
699 buf = smalloc(bufsize = 64);
700 *buf = 0;
701 while(node != NULL)
702 {
703 if(node->path != NULL)
704 {
705 if(bufsize < strlen(node->path) + strlen(buf) + 1)
706 buf = srealloc(buf, strlen(node->path) + strlen(buf) + 1);
707 memmove(buf + strlen(node->path), buf, strlen(buf) + 1);
708 memcpy(buf, node->path, strlen(node->path));
709 return(buf);
710 }
711 if((mbsname = icwcstombs(node->name, NULL)) == NULL)
712 {
713 flog(LOG_WARNING, "could not map unicode share name (%ls) into filesystem charset: %s", node->name, strerror(errno));
714 free(buf);
715 return(NULL);
716 }
717 while(bufsize < strlen(mbsname) + 1 + strlen(buf) + 1)
718 buf = srealloc(buf, bufsize *= 2);
719 memmove(buf + strlen(mbsname) + 1, buf, strlen(buf) + 1);
720 memcpy(buf + 1, mbsname, strlen(mbsname));
721 *buf = '/';
722 free(mbsname);
723 node = node->parent;
724 }
725 buf = srealloc(buf, strlen(buf) + 1);
726 return(buf);
727}
728
729static int checknode(struct sharecache *node)
730{
731 char *path;
732 struct stat sb;
733
734 if(node->parent == NULL)
735 {
736 return(1);
737 } else {
738 if(!checknode(node->parent))
739 return(0);
740 path = getfspath(node);
741 if(stat(path, &sb) < 0)
742 {
743 flog(LOG_INFO, "%s was found to be broken (%s); scheduling rescan of parent", path, strerror(errno));
744 queuescan(node->parent);
745 return(0);
746 } else {
747 return(1);
748 }
749 }
750}
751
752int opensharecache(struct sharecache *node)
753{
754 char *path;
755 int fd, errbak;
756
757 path = getfspath(node);
758 fd = open(path, O_RDONLY);
759 errbak = errno;
760 if(fd < 0)
761 {
762 flog(LOG_WARNING, "could not open %s: %s", path, strerror(errbak));
763 checknode(node);
764 }
765 free(path);
766 errno = errbak;
767 return(fd);
768}
769
770static struct scanstate *newscan(struct sharecache *node)
771{
772 struct scanstate *new;
773
774 new = smalloc(sizeof(*new));
775 new->next = NULL;
776 new->node = node;
777 new->dd = NULL;
778 return(new);
779}
780
781void queuescan(struct sharecache *node)
782{
783 struct scanqueue *new;
784
785 new = smalloc(sizeof(*new));
786 new->state = newscan(node);
787 new->next = scanqueue;
788 scanqueue = new;
789}
790
791/* For internal use in doscan() */
792static void removestale(struct sharecache *node)
793{
794 struct sharecache *cur, *next;
795
796 for(cur = node->child; cur != NULL; cur = next)
797 {
798 next = cur->next;
799 if(!cur->f.b.found)
800 freecache(cur);
801 }
802}
803
804/* For internal use in doscan() */
805static void jobdone(void)
806{
807 struct scanstate *jbuf;
808
809 jbuf = scanjob;
810 scanjob = jbuf->next;
811 freescan(jbuf);
812 if(scanjob != NULL)
813 fchdir(dirfd(scanjob->dd));
814}
815
816int doscan(int quantum)
817{
818 char *path;
819 wchar_t *wcs;
820 int type;
821 struct sharecache *n;
822 struct scanstate *jbuf;
823 struct scanqueue *qbuf;
824 struct dirent *de;
825 struct stat sb;
826 struct hashcache *hc;
827 int dmask, fmask;
828 static int busybefore = 0;
829
830 dmask = confgetint("cli", "scandirmask");
831 fmask = confgetint("cli", "scanfilemask");
832 if((scanjob != NULL) && (scanjob->dd != NULL))
833 {
834 while(fchdir(dirfd(scanjob->dd)) < 0)
835 {
836 flog(LOG_WARNING, "could not fchdir to fd %i: %s", dirfd(scanjob->dd), strerror(errno));
837 removestale(scanjob->node);
838 jobdone();
839 }
840 }
841 while(quantum-- > 0)
842 {
843 if(scanjob != NULL)
844 {
845 busybefore = 1;
846 } else {
847 while(scanjob == NULL)
848 {
849 if(scanqueue == NULL)
850 {
851 if(busybefore)
852 {
853 flog(LOG_INFO, "sharing %lli bytes", sharesize);
854 busybefore = 0;
855 GCBCHAINDOCB(sharechangecb, sharesize);
856 if(hashjob == 0)
857 checkhashes();
858 }
859 return(0);
860 }
861 busybefore = 1;
862 scanjob = scanqueue->state;
863 qbuf = scanqueue;
864 scanqueue = qbuf->next;
865 free(qbuf);
866 for(n = scanjob->node->child; n != NULL; n = n->next)
867 n->f.b.found = 0;
868 }
869 }
870 if(scanjob->dd == NULL)
871 {
872 path = getfspath(scanjob->node);
873 if((scanjob->dd = opendir(path)) == NULL)
874 {
875 flog(LOG_WARNING, "cannot open directory %s for scanning: %s, deleting from share", path, strerror(errno));
876 freecache(scanjob->node);
877 free(path);
878 jobdone();
879 continue;
880 }
881 free(path);
882 if(fchdir(dirfd(scanjob->dd)) < 0)
883 {
884 flog(LOG_WARNING, "could not fchdir to fd %i: %s", dirfd(scanjob->dd), strerror(errno));
885 jobdone();
886 continue;
887 }
888 }
889 if((de = readdir(scanjob->dd)) == NULL)
890 {
891 removestale(scanjob->node);
892 jobdone();
893 continue;
894 }
895 if(*de->d_name == '.')
896 continue;
897 if((wcs = icmbstowcs(de->d_name, NULL)) == NULL)
898 {
899 flog(LOG_WARNING, "file name %s has cannot be converted to wchar: %s", de->d_name, strerror(errno));
900 continue;
901 }
902 n = findcache(scanjob->node, wcs);
903 if(stat(de->d_name, &sb) < 0)
904 {
905 free(wcs);
906 if(n != NULL)
907 {
908 flog(LOG_WARNING, "could not stat %s: %s, deleting from share", de->d_name, strerror(errno));
909 freecache(n);
910 } else {
911 flog(LOG_WARNING, "could not stat %s: %s", de->d_name, strerror(errno));
912 }
913 continue;
914 }
915 if(S_ISDIR(sb.st_mode))
916 {
917 if(~sb.st_mode & dmask)
918 {
919 free(wcs);
920 continue;
921 }
922 type = FILE_DIR;
923 } else if(S_ISREG(sb.st_mode)) {
924 if(~sb.st_mode & fmask)
925 {
926 free(wcs);
927 continue;
928 }
929 type = FILE_REG;
930 } else {
931 flog(LOG_WARNING, "unhandled file type: %i", sb.st_mode);
932 free(wcs);
933 continue;
934 }
935 if(n != NULL)
936 {
937 if((n->f.b.type != type) || (n->mtime != sb.st_mtime) || ((type == FILE_REG) && (n->size != sb.st_size)))
938 {
939 freecache(n);
940 n = NULL;
941 }
942 }
943 if(n == NULL)
944 {
945 n = newcache();
946 n->name = wcs;
947 if(S_ISREG(sb.st_mode))
948 {
949 sharesize += (n->size = sb.st_size);
950 } else {
951 n->size = 0;
952 }
953 n->mtime = sb.st_mtime;
954 n->dev = sb.st_dev;
955 n->inode = sb.st_ino;
956 n->f.b.type = type;
957 attachcache(scanjob->node, n);
958 } else {
959 free(wcs);
960 }
961 n->f.b.found = 1;
962 if(n->f.b.type == FILE_DIR)
963 {
964 jbuf = newscan(n);
965 jbuf->next = scanjob;
966 scanjob = jbuf;
967 } else if(n->f.b.type == FILE_REG) {
968 if(n->f.b.hastth && (n->mtime != sb.st_mtime))
969 n->f.b.hastth = 0;
970 if(!n->f.b.hastth)
971 {
972 if((hc = findhashcache(sb.st_dev, sb.st_ino)) != NULL)
973 {
974 if(hc->mtime == n->mtime)
975 {
976 n->f.b.hastth = 1;
977 memcpy(n->hashtth, hc->tth, 24);
978 } else {
979 freehashcache(hc);
980 }
981 }
982 }
983 }
984 }
985 return(1);
986}
987
988void scanshares(void)
989{
990 struct sharepoint *cur;
991 struct sharecache *node;
992 struct stat sb;
993
994 for(cur = shares; cur != NULL; cur = cur->next)
995 {
996 if((node = findcache(shareroot, cur->name)) == NULL)
997 {
998 if(stat(cur->path, &sb))
999 {
1000 flog(LOG_WARNING, "could not stat share \"%ls\": %s", cur->name, strerror(errno));
1001 continue;
1002 }
1003 if(!S_ISDIR(sb.st_mode))
1004 {
1005 flog(LOG_WARNING, "%s is not a directory; won't share it", cur->path);
1006 continue;
1007 }
1008 node = newcache();
1009 node->name = swcsdup(cur->name);
1010 node->path = sstrdup(cur->path);
1011 if(node->path[strlen(node->path) - 1] == '/')
1012 node->path[strlen(node->path) - 1] = 0;
1013 node->f.b.type = FILE_DIR;
1014 attachcache(shareroot, node);
1015 }
1016 queuescan(node);
1017 }
1018}
1019
1020static void preinit(int hup)
1021{
1022 struct sharepoint *cur;
1023
1024 if(hup)
1025 {
1026 for(cur = shares; cur != NULL; cur = cur->next)
1027 cur->delete = 1;
1028 } else {
1029 shareroot = newcache();
1030 shareroot->name = swcsdup(L"");
1031 shareroot->f.b.type = FILE_DIR;
1032 }
1033}
1034
1035static int init(int hup)
1036{
1037 struct sharepoint *cur, *next;
1038
1039 readhashcache();
1040 for(cur = shares; cur != NULL; cur = next)
1041 {
1042 next = cur->next;
1043 if(cur->delete)
1044 freesharepoint(cur);
1045 }
1046 scanshares();
1047 if(!hup)
1048 while(doscan(100));
1049 return(0);
1050}
1051
1052static int run(void)
1053{
1054 return(doscan(10));
1055}
1056
1057static void terminate(void)
1058{
1059 if(hashjob != 0)
1060 kill(hashjob, SIGHUP);
1061 while(shares != NULL)
1062 freesharepoint(shares);
1063 freecache(shareroot);
1064}
1065
1066static struct module me =
1067{
1068 .name = "cli",
1069 .conf =
1070 {
1071 .vars = myvars,
1072 .cmds = mycmds
1073 },
1074 .preinit = preinit,
1075 .init = init,
1076 .run = run,
1077 .terminate = terminate
1078};
1079
1080MODULE(me)