1086de764009cca84a27a5ff581716ffbe398718
[tridge/junkcode.git] / tsums / tsums.c
1 /* 
2    Trivial tripwire-like system
3
4    Copyright (C) Andrew Tridgell 2001
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
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "tsums.h"
22
23 struct sum_struct {
24         time_t mtime;
25         time_t ctime;
26         mode_t mode;
27         uid_t uid;
28         gid_t gid;
29         char sum[16];
30         ino_t inode;
31         dev_t device;
32         dev_t rdev;
33         nlink_t nlink;
34         size_t size;
35 };
36
37 struct ignore {
38         struct ignore *next;
39         char *pattern;
40 };
41
42 static TDB_CONTEXT *tdb;
43 static int do_update;
44 static int do_ignore;
45 static int do_quick;
46 static int verbose;
47 static int recurse=1;
48 static struct ignore *ignore_list;
49
50 static void tsums_dir(const char *dname);
51
52 static void fatal(const char *format, ...)
53 {
54         va_list ap;
55         char *ptr = NULL;
56
57         va_start(ap, format);
58         vasprintf(&ptr, format, ap);
59         va_end(ap);
60         
61         if (!ptr || !*ptr) return;
62
63         fprintf(stderr, "%s", ptr);
64
65         free(ptr);
66         exit(1);
67 }
68
69
70 static void dump_ignored(void)
71 {
72         struct ignore *ign;
73         for (ign=ignore_list; ign; ign=ign->next) {
74                 printf("Ignoring %s\n", ign->pattern);
75         }
76 }
77
78 static void flush_ignored(void)
79 {
80         struct ignore *ign;
81         for (ign=ignore_list; ign; ign=ign->next) {
82                 char *keystr;
83                 TDB_DATA key;
84                 asprintf(&keystr, "IGNORE:%s", ign->pattern);
85                 key.dptr = keystr;
86                 key.dsize = strlen(keystr)+1;
87                 tdb_delete(tdb, key);
88                 free(keystr);
89         }
90 }
91
92
93 static void report_difference(const char *fname, 
94                               struct sum_struct *sum2, 
95                               struct sum_struct *sum1)
96 {
97         
98         printf("(%c%c%c%c%c%c%c%c%c%c%c)\t%s\n", 
99                sum1->ctime==sum2->ctime?' ':'c',
100                sum1->mtime==sum2->mtime?' ':'m',
101                sum1->mode==sum2->mode?' ':'p',
102                sum1->uid==sum2->uid?' ':'u',
103                sum1->gid==sum2->gid?' ':'g',
104                sum1->inode==sum2->inode?' ':'i',
105                sum1->device==sum2->device?' ':'d',
106                sum1->rdev==sum2->rdev?' ':'r',
107                sum1->nlink==sum2->nlink?' ':'l',
108                sum1->size==sum2->size?' ':'s',
109                do_quick || memcmp(sum1->sum, sum2->sum, 16)==0?' ':'4',
110                fname);
111 }
112
113
114 static int link_checksum(const char *fname, char sum[16])
115 {
116         struct mdfour md;
117         char lname[1024];
118         int n;
119
120         n = readlink(fname, lname, sizeof(lname));
121         if (n == -1) return -1;
122
123         mdfour_begin(&md);
124         mdfour_update(&md, lname, n);
125         mdfour_result(&md, sum);
126         return 0;
127 }
128
129
130 static int file_checksum(const char *fname, char sum[16])
131 {
132         int fd;
133         struct mdfour md;
134         unsigned char buf[64*1024];
135         
136         fd = open(fname,O_RDONLY);
137         if (fd == -1) return -1;
138         
139         mdfour_begin(&md);
140
141         while (1) {
142                 int n = read(fd, buf, sizeof(buf));
143                 if (n <= 0) break;
144                 mdfour_update(&md, buf, n);
145         }
146
147         close(fd);
148
149         mdfour_result(&md, sum);
150         return 0;
151 }
152
153 static void ignore_file(const char *fname)
154 {
155         TDB_DATA key, data;
156         char *keystr=NULL;
157         int one=1;
158
159         asprintf(&keystr, "IGNORE:%s", fname);
160         key.dptr = keystr;
161         key.dsize = strlen(keystr)+1;
162         data.dptr = (void *)&one;
163         data.dsize = sizeof(one);
164
165         tdb_store(tdb, key, data, TDB_REPLACE);
166
167         printf("Added %s\n", keystr);
168         free(keystr);
169 }
170
171 static int is_ignored(const char *fname)
172 {
173         struct ignore *ign;
174         for (ign=ignore_list; ign; ign=ign->next) {
175                 if (fnmatch(ign->pattern, fname, 0) == 0) return 1;
176         }
177         return 0;
178 }
179
180
181 static void tsums_file(const char *fname)
182 {
183         struct stat st;
184         struct sum_struct sum;
185         struct sum_struct old;
186         TDB_DATA key, data;
187         char *keystr=NULL;
188
189         if (lstat(fname, &st) != 0) return;
190
191         if (is_ignored(fname)) goto next;
192
193         bzero(&sum, sizeof(sum));
194         sum.mtime = st.st_mtime;
195         sum.ctime = st.st_ctime;
196         sum.mode = st.st_mode;
197         sum.uid = st.st_uid;
198         sum.gid = st.st_gid;
199         sum.device = st.st_dev;
200         sum.inode = st.st_ino;
201         sum.nlink = st.st_nlink;
202         sum.size = st.st_size;
203         if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
204                 sum.rdev = st.st_rdev;
205         }
206
207         if (S_ISLNK(st.st_mode)) {
208                 link_checksum(fname, &sum.sum[0]);
209         } else if (S_ISREG(st.st_mode) && !do_quick) {
210                 file_checksum(fname, &sum.sum[0]);
211         }
212
213         asprintf(&keystr, "FILE:%s", fname);
214
215         key.dptr = (void *)keystr;
216         key.dsize = strlen(keystr)+1;
217         data = tdb_fetch(tdb, key);
218
219         if (!data.dptr) goto update;
220         
221         if (data.dsize==sizeof(sum) &&
222             memcmp(&sum, data.dptr, sizeof(sum)) == 0) {
223                 /* nothings changed */
224                 goto next;
225         }
226
227         if (memcmp(&sum, data.dptr, MIN(data.dsize,sizeof(sum))) == 0) {
228                 /* data structure extended */
229                 if (verbose) printf("old record size (%d/%d) - updating\n",
230                                     data.dsize, sizeof(sum));
231                 goto update;
232         }
233         
234         bzero(&old, sizeof(old));
235         memcpy(&old, data.dptr, MIN(sizeof(old), data.dsize));
236
237         if (do_quick &&
238             old.ctime == sum.ctime &&
239             old.mtime == sum.mtime &&
240             old.uid == sum.uid &&
241             old.gid == sum.gid &&
242             old.mode == sum.mode &&
243             old.device == sum.device &&
244             old.inode == sum.inode &&
245             old.rdev == sum.rdev &&
246             old.nlink == sum.nlink &&
247             old.size == sum.size) {
248                 /* quick properties are the same */
249                 goto next;
250         }
251
252         report_difference(fname, &sum, &old);
253
254         if (!do_update) {
255                 goto next;
256         }
257
258 update:
259         if (do_quick) file_checksum(fname, &sum.sum[0]);
260         if (data.dptr) free(data.dptr);
261         data.dptr = (void *)&sum;
262         data.dsize = sizeof(sum);
263         tdb_store(tdb, key, data, TDB_REPLACE);
264
265 next:
266         if (keystr) free(keystr);
267
268         if (recurse && S_ISDIR(st.st_mode)) {
269                 tsums_dir(fname);
270         }
271 }
272
273 static void tsums_dir(const char *dname)
274 {
275         DIR *d;
276         struct dirent *de;
277
278         d = opendir(dname);
279         if (!d) return;
280
281         for (de=readdir(d); de; de=readdir(d)) {
282                 char *s=NULL;
283                 if (strcmp(de->d_name, ".") == 0 ||
284                     strcmp(de->d_name, "..") == 0) continue;
285                 asprintf(&s, "%s/%s", dname, de->d_name);
286                 tsums_file(s);
287                 free(s);
288         }
289         closedir(d);
290 }
291
292
293 static int load_ignore(TDB_CONTEXT *db, TDB_DATA key, TDB_DATA data, 
294                         void *state)
295 {
296         struct ignore *ignore;
297
298         if (strncmp(key.dptr, "IGNORE:", 7) != 0) return 0;
299
300         ignore = (struct ignore *)malloc(sizeof(*ignore));
301         if (!ignore) fatal("out of memory in load_ignore\n");
302         ignore->pattern = strdup(key.dptr+7);
303         ignore->next = ignore_list;
304         ignore_list = ignore;
305         return 0;
306 }
307
308 static void process_one(char *fname)
309 {
310         if (verbose) {
311                 printf("%s\n", fname);
312         }
313         if (do_ignore) {
314                 ignore_file(fname);
315         } else {
316                 tsums_file(fname);
317         }
318 }
319
320 static int process_fn(TDB_CONTEXT *db, TDB_DATA key, TDB_DATA data, 
321                       void *state)
322 {
323         if (strncmp(key.dptr, "FILE:", 5) != 0) return 0;
324
325         process_one(key.dptr + 5);
326         return 0;
327 }
328
329 static void usage(void)
330 {
331         printf("
332 tsums maintains signatures of files on a system. Similar to tripwire.
333 Copyright (C) Andrew Tridgell (tridge@samba.org)
334
335 Usage: tsums [options] <files|dirs...>
336
337 Options:
338   -a          use all existing files
339   -q          quick mode (don't checksum)
340   -h          this help
341   -u          update sums
342   -f <DB>     database name
343   -i          add listed files to ignore list
344   -d          dump the ignored list
345   -F          flush the ignored list
346 ");
347         exit(1);
348 }
349
350 int main(int argc, char *argv[])
351 {
352         int i;
353         char *db_name = "tsums.tdb";
354         extern char *optarg;
355         extern int optind;
356         int c;
357         int do_dump = 0;
358         int use_all = 0;
359         int do_flush_ignore=0;
360
361         while ((c = getopt(argc, argv, "qhuf:idavF")) != -1){
362                 switch (c) {
363                 case 'h':
364                         usage();
365                         break;
366                 case 'a':
367                         use_all = 1;
368                         break;
369                 case 'v':
370                         verbose++;
371                         break;
372                 case 'q':
373                         do_quick = 1;
374                         break;
375                 case 'u':
376                         do_update = 1;
377                         break;
378                 case 'd':
379                         do_dump = 1;
380                         break;
381                 case 'i':
382                         do_ignore = 1;
383                         break;
384                 case 'f':
385                         db_name = optarg;
386                         break;
387                 case 'F':
388                         do_flush_ignore = 1;
389                         break;
390                 default:
391                         usage();
392                         break;
393                 }
394         }
395         
396
397         argc -= optind;
398         argv += optind;
399
400         tdb = tdb_open(db_name, 1000, 0, O_CREAT|O_RDWR, 0600);
401         
402         if (!tdb) {
403                 fatal("can't open tdb\n");
404         }
405
406         tdb_traverse(tdb, load_ignore, NULL);
407
408         if (do_dump) {
409                 dump_ignored();
410                 goto finish;
411         }
412
413         if (do_flush_ignore) {
414                 flush_ignored();
415                 goto finish;
416         }
417
418         if (use_all) {
419                 recurse = 0;
420                 tdb_traverse(tdb, process_fn, NULL);
421                 goto finish;
422         } 
423
424         if (argc == 0) usage();
425
426         for (i=0;i<argc;i++) {
427                 process_one(argv[i]);
428         }
429
430 finish:
431         tdb_close(tdb);
432
433         return 0;
434 }