~ubuntu-branches/debian/jessie/systemd/jessie

« back to all changes in this revision

Viewing changes to src/readahead-collect.c

  • Committer: Package Import Robot
  • Author(s): Tollef Fog Heen, Tollef Fog Heen, Michael Biebl
  • Date: 2012-04-03 19:59:17 UTC
  • mfrom: (1.1.10) (6.1.3 experimental)
  • Revision ID: package-import@ubuntu.com-20120403195917-l532urrbg4pkreas
Tags: 44-1
[ Tollef Fog Heen ]
* New upstream version.
  - Backport 3492207: journal: PAGE_SIZE is not known on ppc and other
    archs
  - Backport 5a2a2a1: journal: react with immediate rotation to a couple
    of more errors
  - Backport 693ce21: util: never follow symlinks in rm_rf_children()
    Fixes CVE-2012-1174, closes: #664364
* Drop output message from init-functions hook, it's pointless.
* Only rmdir /lib/init/rw if it exists.
* Explicitly order debian-fixup before sysinit.target to prevent a
  possible race condition with the creation of sockets.  Thanks to
  Michael Biebl for debugging this.
* Always restart the initctl socket on upgrades, to mask sysvinit
  removing it.

[ Michael Biebl ]
* Remove workaround for non-interactive sessions from pam config again.
* Create compat /dev/initctl symlink in case we are upgrading from a system
  running a newer version of sysvinit (using /run/initctl) and sysvinit is
  replaced with systemd-sysv during the upgrade. Closes: #663219
* Install new man pages.
* Build-Depend on valac (>= 0.12) instead of valac-0.12. Closes: #663323

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
 
 
3
 
/***
4
 
  This file is part of systemd.
5
 
 
6
 
  Copyright 2010 Lennart Poettering
7
 
 
8
 
  systemd is free software; you can redistribute it and/or modify it
9
 
  under the terms of the GNU General Public License as published by
10
 
  the Free Software Foundation; either version 2 of the License, or
11
 
  (at your option) any later version.
12
 
 
13
 
  systemd is distributed in the hope that it will be useful, but
14
 
  WITHOUT ANY WARRANTY; without even the implied warranty of
15
 
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
 
  General Public License for more details.
17
 
 
18
 
  You should have received a copy of the GNU General Public License
19
 
  along with systemd; If not, see <http://www.gnu.org/licenses/>.
20
 
***/
21
 
 
22
 
#include <errno.h>
23
 
#include <inttypes.h>
24
 
#include <fcntl.h>
25
 
#include <linux/limits.h>
26
 
#include <stdbool.h>
27
 
#include <stdio.h>
28
 
#include <stdlib.h>
29
 
#include <string.h>
30
 
#include <sys/select.h>
31
 
#include <sys/time.h>
32
 
#include <sys/types.h>
33
 
#include <sys/stat.h>
34
 
#include <unistd.h>
35
 
#include <linux/fanotify.h>
36
 
#include <sys/signalfd.h>
37
 
#include <sys/poll.h>
38
 
#include <sys/mman.h>
39
 
#include <linux/fs.h>
40
 
#include <linux/fiemap.h>
41
 
#include <sys/ioctl.h>
42
 
#include <sys/vfs.h>
43
 
#include <getopt.h>
44
 
#include <sys/inotify.h>
45
 
 
46
 
#include "missing.h"
47
 
#include "util.h"
48
 
#include "set.h"
49
 
#include "sd-daemon.h"
50
 
#include "ioprio.h"
51
 
#include "readahead-common.h"
52
 
#include "virt.h"
53
 
 
54
 
/* fixme:
55
 
 *
56
 
 * - detect ssd on btrfs/lvm...
57
 
 * - read ahead directories
58
 
 * - gzip?
59
 
 * - remount rw?
60
 
 * - handle files where nothing is in mincore
61
 
 * - does ioprio_set work with fadvise()?
62
 
 */
63
 
 
64
 
static unsigned arg_files_max = 16*1024;
65
 
static off_t arg_file_size_max = READAHEAD_FILE_SIZE_MAX;
66
 
static usec_t arg_timeout = 2*USEC_PER_MINUTE;
67
 
 
68
 
static ReadaheadShared *shared = NULL;
69
 
 
70
 
/* Avoid collisions with the NULL pointer */
71
 
#define SECTOR_TO_PTR(s) ULONG_TO_PTR((s)+1)
72
 
#define PTR_TO_SECTOR(p) (PTR_TO_ULONG(p)-1)
73
 
 
74
 
static int btrfs_defrag(int fd) {
75
 
        struct btrfs_ioctl_vol_args data;
76
 
 
77
 
        zero(data);
78
 
        data.fd = fd;
79
 
 
80
 
        return ioctl(fd, BTRFS_IOC_DEFRAG, &data);
81
 
}
82
 
 
83
 
static int pack_file(FILE *pack, const char *fn, bool on_btrfs) {
84
 
        struct stat st;
85
 
        void *start = MAP_FAILED;
86
 
        uint8_t *vec;
87
 
        uint32_t b, c;
88
 
        size_t l, pages;
89
 
        bool mapped;
90
 
        int r = 0, fd = -1, k;
91
 
 
92
 
        assert(pack);
93
 
        assert(fn);
94
 
 
95
 
        if ((fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW)) < 0) {
96
 
 
97
 
                if (errno == ENOENT)
98
 
                        return 0;
99
 
 
100
 
                if (errno == EPERM || errno == EACCES)
101
 
                        return 0;
102
 
 
103
 
                log_warning("open(%s) failed: %m", fn);
104
 
                r = -errno;
105
 
                goto finish;
106
 
        }
107
 
 
108
 
        if ((k = file_verify(fd, fn, arg_file_size_max, &st)) <= 0) {
109
 
                r = k;
110
 
                goto finish;
111
 
        }
112
 
 
113
 
        if (on_btrfs)
114
 
                btrfs_defrag(fd);
115
 
 
116
 
        l = PAGE_ALIGN(st.st_size);
117
 
        if ((start = mmap(NULL, l, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
118
 
                log_warning("mmap(%s) failed: %m", fn);
119
 
                r = -errno;
120
 
                goto finish;
121
 
        }
122
 
 
123
 
        pages = l / page_size();
124
 
 
125
 
        vec = alloca(pages);
126
 
        memset(vec, 0, pages);
127
 
        if (mincore(start, l, vec) < 0) {
128
 
                log_warning("mincore(%s) failed: %m", fn);
129
 
                r = -errno;
130
 
                goto finish;
131
 
        }
132
 
 
133
 
        fputs(fn, pack);
134
 
        fputc('\n', pack);
135
 
 
136
 
        mapped = false;
137
 
        for (c = 0; c < pages; c++) {
138
 
                bool new_mapped = !!(vec[c] & 1);
139
 
 
140
 
                if (!mapped && new_mapped)
141
 
                        b = c;
142
 
                else if (mapped && !new_mapped) {
143
 
                        fwrite(&b, sizeof(b), 1, pack);
144
 
                        fwrite(&c, sizeof(c), 1, pack);
145
 
 
146
 
                        log_debug("%s: page %u to %u", fn, b, c);
147
 
                }
148
 
 
149
 
                mapped = new_mapped;
150
 
        }
151
 
 
152
 
        /* We don't write any range data if we should read the entire file */
153
 
        if (mapped && b > 0) {
154
 
                fwrite(&b, sizeof(b), 1, pack);
155
 
                fwrite(&c, sizeof(c), 1, pack);
156
 
 
157
 
                log_debug("%s: page %u to %u", fn, b, c);
158
 
        }
159
 
 
160
 
        /* End marker */
161
 
        b = 0;
162
 
        fwrite(&b, sizeof(b), 1, pack);
163
 
        fwrite(&b, sizeof(b), 1, pack);
164
 
 
165
 
finish:
166
 
        if (start != MAP_FAILED)
167
 
                munmap(start, l);
168
 
 
169
 
        if (fd >= 0)
170
 
                close_nointr_nofail(fd);
171
 
 
172
 
        return r;
173
 
}
174
 
 
175
 
static unsigned long fd_first_block(int fd) {
176
 
        struct {
177
 
                struct fiemap fiemap;
178
 
                struct fiemap_extent extent;
179
 
        } data;
180
 
 
181
 
        zero(data);
182
 
        data.fiemap.fm_length = ~0ULL;
183
 
        data.fiemap.fm_extent_count = 1;
184
 
 
185
 
        if (ioctl(fd, FS_IOC_FIEMAP, &data) < 0)
186
 
                return 0;
187
 
 
188
 
        if (data.fiemap.fm_mapped_extents <= 0)
189
 
                return 0;
190
 
 
191
 
        if (data.fiemap.fm_extents[0].fe_flags & FIEMAP_EXTENT_UNKNOWN)
192
 
                return 0;
193
 
 
194
 
        return (unsigned long) data.fiemap.fm_extents[0].fe_physical;
195
 
}
196
 
 
197
 
struct item {
198
 
        const char *path;
199
 
        unsigned long block;
200
 
};
201
 
 
202
 
static int qsort_compare(const void *a, const void *b) {
203
 
        const struct item *i, *j;
204
 
 
205
 
        i = a;
206
 
        j = b;
207
 
 
208
 
        if (i->block < j->block)
209
 
                return -1;
210
 
        if (i->block > j->block)
211
 
                return 1;
212
 
 
213
 
        return strcmp(i->path, j->path);
214
 
}
215
 
 
216
 
static int collect(const char *root) {
217
 
        enum {
218
 
                FD_FANOTIFY,  /* Get the actual fs events */
219
 
                FD_SIGNAL,
220
 
                FD_INOTIFY,   /* We get notifications to quit early via this fd */
221
 
                _FD_MAX
222
 
        };
223
 
        struct pollfd pollfd[_FD_MAX];
224
 
        int fanotify_fd = -1, signal_fd = -1, inotify_fd = -1, r = 0;
225
 
        pid_t my_pid;
226
 
        Hashmap *files = NULL;
227
 
        Iterator i;
228
 
        char *p, *q;
229
 
        sigset_t mask;
230
 
        FILE *pack = NULL;
231
 
        char *pack_fn_new = NULL, *pack_fn = NULL;
232
 
        bool on_ssd, on_btrfs;
233
 
        struct statfs sfs;
234
 
        usec_t not_after;
235
 
 
236
 
        assert(root);
237
 
 
238
 
        write_one_line_file("/proc/self/oom_score_adj", "1000");
239
 
 
240
 
        if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0)) < 0)
241
 
                log_warning("Failed to set IDLE IO priority class: %m");
242
 
 
243
 
        assert_se(sigemptyset(&mask) == 0);
244
 
        sigset_add_many(&mask, SIGINT, SIGTERM, -1);
245
 
        assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
246
 
 
247
 
        if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
248
 
                log_error("signalfd(): %m");
249
 
                r = -errno;
250
 
                goto finish;
251
 
        }
252
 
 
253
 
        if (!(files = hashmap_new(string_hash_func, string_compare_func))) {
254
 
                log_error("Failed to allocate set.");
255
 
                r = -ENOMEM;
256
 
                goto finish;
257
 
        }
258
 
 
259
 
        if ((fanotify_fd = fanotify_init(FAN_CLOEXEC|FAN_NONBLOCK, O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_NOATIME)) < 0)  {
260
 
                log_error("Failed to create fanotify object: %m");
261
 
                r = -errno;
262
 
                goto finish;
263
 
        }
264
 
 
265
 
        if (fanotify_mark(fanotify_fd, FAN_MARK_ADD|FAN_MARK_MOUNT, FAN_OPEN, AT_FDCWD, root) < 0) {
266
 
                log_error("Failed to mark %s: %m", root);
267
 
                r = -errno;
268
 
                goto finish;
269
 
        }
270
 
 
271
 
        if ((inotify_fd = open_inotify()) < 0) {
272
 
                r = inotify_fd;
273
 
                goto finish;
274
 
        }
275
 
 
276
 
        not_after = now(CLOCK_MONOTONIC) + arg_timeout;
277
 
 
278
 
        my_pid = getpid();
279
 
 
280
 
        zero(pollfd);
281
 
        pollfd[FD_FANOTIFY].fd = fanotify_fd;
282
 
        pollfd[FD_FANOTIFY].events = POLLIN;
283
 
        pollfd[FD_SIGNAL].fd = signal_fd;
284
 
        pollfd[FD_SIGNAL].events = POLLIN;
285
 
        pollfd[FD_INOTIFY].fd = inotify_fd;
286
 
        pollfd[FD_INOTIFY].events = POLLIN;
287
 
 
288
 
        sd_notify(0,
289
 
                  "READY=1\n"
290
 
                  "STATUS=Collecting readahead data");
291
 
 
292
 
        log_debug("Collecting...");
293
 
 
294
 
        if (access("/run/systemd/readahead/cancel", F_OK) >= 0) {
295
 
                log_debug("Collection canceled");
296
 
                r = -ECANCELED;
297
 
                goto finish;
298
 
        }
299
 
 
300
 
        if (access("/run/systemd/readahead/done", F_OK) >= 0) {
301
 
                log_debug("Got termination request");
302
 
                goto done;
303
 
        }
304
 
 
305
 
        for (;;) {
306
 
                union {
307
 
                        struct fanotify_event_metadata metadata;
308
 
                        char buffer[4096];
309
 
                } data;
310
 
                ssize_t n;
311
 
                struct fanotify_event_metadata *m;
312
 
                usec_t t;
313
 
                int h;
314
 
 
315
 
                if (hashmap_size(files) > arg_files_max) {
316
 
                        log_debug("Reached maximum number of read ahead files, ending collection.");
317
 
                        break;
318
 
                }
319
 
 
320
 
                t = now(CLOCK_MONOTONIC);
321
 
                if (t >= not_after) {
322
 
                        log_debug("Reached maximum collection time, ending collection.");
323
 
                        break;
324
 
                }
325
 
 
326
 
                if ((h = poll(pollfd, _FD_MAX, (int) ((not_after - t) / USEC_PER_MSEC))) < 0) {
327
 
 
328
 
                        if (errno == EINTR)
329
 
                                continue;
330
 
 
331
 
                        log_error("poll(): %m");
332
 
                        r = -errno;
333
 
                        goto finish;
334
 
                }
335
 
 
336
 
                if (h == 0) {
337
 
                        log_debug("Reached maximum collection time, ending collection.");
338
 
                        break;
339
 
                }
340
 
 
341
 
                if (pollfd[FD_SIGNAL].revents) {
342
 
                        log_debug("Got signal.");
343
 
                        break;
344
 
                }
345
 
 
346
 
                if (pollfd[FD_INOTIFY].revents) {
347
 
                        uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
348
 
                        struct inotify_event *e;
349
 
 
350
 
                        if ((n = read(inotify_fd, &inotify_buffer, sizeof(inotify_buffer))) < 0) {
351
 
                                if (errno == EINTR || errno == EAGAIN)
352
 
                                        continue;
353
 
 
354
 
                                log_error("Failed to read inotify event: %m");
355
 
                                r = -errno;
356
 
                                goto finish;
357
 
                        }
358
 
 
359
 
                        e = (struct inotify_event*) inotify_buffer;
360
 
                        while (n > 0) {
361
 
                                size_t step;
362
 
 
363
 
                                if ((e->mask & IN_CREATE) && streq(e->name, "cancel")) {
364
 
                                        log_debug("Collection canceled");
365
 
                                        r = -ECANCELED;
366
 
                                        goto finish;
367
 
                                }
368
 
 
369
 
                                if ((e->mask & IN_CREATE) && streq(e->name, "done")) {
370
 
                                        log_debug("Got termination request");
371
 
                                        goto done;
372
 
                                }
373
 
 
374
 
                                step = sizeof(struct inotify_event) + e->len;
375
 
                                assert(step <= (size_t) n);
376
 
 
377
 
                                e = (struct inotify_event*) ((uint8_t*) e + step);
378
 
                                n -= step;
379
 
                        }
380
 
                }
381
 
 
382
 
                if ((n = read(fanotify_fd, &data, sizeof(data))) < 0) {
383
 
 
384
 
                        if (errno == EINTR || errno == EAGAIN)
385
 
                                continue;
386
 
 
387
 
                        /* fanotify sometimes returns EACCES on read()
388
 
                         * where it shouldn't. For now let's just
389
 
                         * ignore it here (which is safe), but
390
 
                         * eventually this should be
391
 
                         * dropped when the kernel is fixed.
392
 
                         *
393
 
                         * https://bugzilla.redhat.com/show_bug.cgi?id=707577 */
394
 
                        if (errno == EACCES)
395
 
                                continue;
396
 
 
397
 
                        log_error("Failed to read event: %m");
398
 
                        r = -errno;
399
 
                        goto finish;
400
 
                }
401
 
 
402
 
                for (m = &data.metadata; FAN_EVENT_OK(m, n); m = FAN_EVENT_NEXT(m, n)) {
403
 
                        char fn[PATH_MAX];
404
 
                        int k;
405
 
 
406
 
                        if (m->fd < 0)
407
 
                                goto next_iteration;
408
 
 
409
 
                        if (m->pid == my_pid)
410
 
                                goto next_iteration;
411
 
 
412
 
                        __sync_synchronize();
413
 
                        if (m->pid == shared->replay)
414
 
                                goto next_iteration;
415
 
 
416
 
                        snprintf(fn, sizeof(fn), "/proc/self/fd/%i", m->fd);
417
 
                        char_array_0(fn);
418
 
 
419
 
                        if ((k = readlink_malloc(fn, &p)) >= 0) {
420
 
                                if (startswith(p, "/tmp") ||
421
 
                                    endswith(p, " (deleted)") ||
422
 
                                    hashmap_get(files, p))
423
 
                                        /* Not interesting, or
424
 
                                         * already read */
425
 
                                        free(p);
426
 
                                else {
427
 
                                        unsigned long ul;
428
 
 
429
 
                                        ul = fd_first_block(m->fd);
430
 
 
431
 
                                        if ((k = hashmap_put(files, p, SECTOR_TO_PTR(ul))) < 0) {
432
 
                                                log_warning("set_put() failed: %s", strerror(-k));
433
 
                                                free(p);
434
 
                                        }
435
 
                                }
436
 
 
437
 
                        } else
438
 
                                log_warning("readlink(%s) failed: %s", fn, strerror(-k));
439
 
 
440
 
                next_iteration:
441
 
                        if (m->fd)
442
 
                                close_nointr_nofail(m->fd);
443
 
                }
444
 
        }
445
 
 
446
 
done:
447
 
        if (fanotify_fd >= 0) {
448
 
                close_nointr_nofail(fanotify_fd);
449
 
                fanotify_fd = -1;
450
 
        }
451
 
 
452
 
        log_debug("Writing Pack File...");
453
 
 
454
 
        on_ssd = fs_on_ssd(root) > 0;
455
 
        log_debug("On SSD: %s", yes_no(on_ssd));
456
 
 
457
 
        on_btrfs = statfs(root, &sfs) >= 0 && (long) sfs.f_type == (long) BTRFS_SUPER_MAGIC;
458
 
        log_debug("On btrfs: %s", yes_no(on_btrfs));
459
 
 
460
 
        asprintf(&pack_fn, "%s/.readahead", root);
461
 
        asprintf(&pack_fn_new, "%s/.readahead.new", root);
462
 
 
463
 
        if (!pack_fn || !pack_fn_new) {
464
 
                log_error("Out of memory");
465
 
                r = -ENOMEM;
466
 
                goto finish;
467
 
        }
468
 
 
469
 
        if (!(pack = fopen(pack_fn_new, "we"))) {
470
 
                log_error("Failed to open pack file: %m");
471
 
                r = -errno;
472
 
                goto finish;
473
 
        }
474
 
 
475
 
        fputs(CANONICAL_HOST "\n", pack);
476
 
        putc(on_ssd ? 'S' : 'R', pack);
477
 
 
478
 
        if (on_ssd || on_btrfs) {
479
 
 
480
 
                /* On SSD or on btrfs, just write things out in the
481
 
                 * order the files were accessed. */
482
 
 
483
 
                HASHMAP_FOREACH_KEY(q, p, files, i)
484
 
                        pack_file(pack, p, on_btrfs);
485
 
        } else {
486
 
                struct item *ordered, *j;
487
 
                unsigned k, n;
488
 
 
489
 
                /* On rotating media, order things by the block
490
 
                 * numbers */
491
 
 
492
 
                log_debug("Ordering...");
493
 
 
494
 
                n = hashmap_size(files);
495
 
                if (!(ordered = new(struct item, n))) {
496
 
                        log_error("Out of memory");
497
 
                        r = -ENOMEM;
498
 
                        goto finish;
499
 
                }
500
 
 
501
 
                j = ordered;
502
 
                HASHMAP_FOREACH_KEY(q, p, files, i) {
503
 
                        j->path = p;
504
 
                        j->block = PTR_TO_SECTOR(q);
505
 
                        j++;
506
 
                }
507
 
 
508
 
                assert(ordered + n == j);
509
 
 
510
 
                qsort(ordered, n, sizeof(struct item), qsort_compare);
511
 
 
512
 
                for (k = 0; k < n; k++)
513
 
                        pack_file(pack, ordered[k].path, on_btrfs);
514
 
 
515
 
                free(ordered);
516
 
        }
517
 
 
518
 
        log_debug("Finalizing...");
519
 
 
520
 
        fflush(pack);
521
 
 
522
 
        if (ferror(pack)) {
523
 
                log_error("Failed to write pack file.");
524
 
                r = -EIO;
525
 
                goto finish;
526
 
        }
527
 
 
528
 
        if (rename(pack_fn_new, pack_fn) < 0) {
529
 
                log_error("Failed to rename readahead file: %m");
530
 
                r = -errno;
531
 
                goto finish;
532
 
        }
533
 
 
534
 
        fclose(pack);
535
 
        pack = NULL;
536
 
 
537
 
        log_debug("Done.");
538
 
 
539
 
finish:
540
 
        if (fanotify_fd >= 0)
541
 
                close_nointr_nofail(fanotify_fd);
542
 
 
543
 
        if (signal_fd >= 0)
544
 
                close_nointr_nofail(signal_fd);
545
 
 
546
 
        if (inotify_fd >= 0)
547
 
                close_nointr_nofail(inotify_fd);
548
 
 
549
 
        if (pack) {
550
 
                fclose(pack);
551
 
                unlink(pack_fn_new);
552
 
        }
553
 
 
554
 
        free(pack_fn_new);
555
 
        free(pack_fn);
556
 
 
557
 
        while ((p = hashmap_steal_first_key(files)))
558
 
                free(p);
559
 
 
560
 
        hashmap_free(files);
561
 
 
562
 
        return r;
563
 
}
564
 
 
565
 
static int help(void) {
566
 
 
567
 
        printf("%s [OPTIONS...] [DIRECTORY]\n\n"
568
 
               "Collect read-ahead data on early boot.\n\n"
569
 
               "  -h --help                 Show this help\n"
570
 
               "     --max-files=INT        Maximum number of files to read ahead\n"
571
 
               "     --max-file-size=BYTES  Maximum size of files to read ahead\n"
572
 
               "     --timeout=USEC         Maximum time to spend collecting data\n",
573
 
               program_invocation_short_name);
574
 
 
575
 
        return 0;
576
 
}
577
 
 
578
 
static int parse_argv(int argc, char *argv[]) {
579
 
 
580
 
        enum {
581
 
                ARG_FILES_MAX = 0x100,
582
 
                ARG_FILE_SIZE_MAX,
583
 
                ARG_TIMEOUT
584
 
        };
585
 
 
586
 
        static const struct option options[] = {
587
 
                { "help",          no_argument,       NULL, 'h'                },
588
 
                { "files-max",     required_argument, NULL, ARG_FILES_MAX      },
589
 
                { "file-size-max", required_argument, NULL, ARG_FILE_SIZE_MAX  },
590
 
                { "timeout",       required_argument, NULL, ARG_TIMEOUT        },
591
 
                { NULL,            0,                 NULL, 0                  }
592
 
        };
593
 
 
594
 
        int c;
595
 
 
596
 
        assert(argc >= 0);
597
 
        assert(argv);
598
 
 
599
 
        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
600
 
 
601
 
                switch (c) {
602
 
 
603
 
                case 'h':
604
 
                        help();
605
 
                        return 0;
606
 
 
607
 
                case ARG_FILES_MAX:
608
 
                        if (safe_atou(optarg, &arg_files_max) < 0 || arg_files_max <= 0) {
609
 
                                log_error("Failed to parse maximum number of files %s.", optarg);
610
 
                                return -EINVAL;
611
 
                        }
612
 
                        break;
613
 
 
614
 
                case ARG_FILE_SIZE_MAX: {
615
 
                        unsigned long long ull;
616
 
 
617
 
                        if (safe_atollu(optarg, &ull) < 0 || ull <= 0) {
618
 
                                log_error("Failed to parse maximum file size %s.", optarg);
619
 
                                return -EINVAL;
620
 
                        }
621
 
 
622
 
                        arg_file_size_max = (off_t) ull;
623
 
                        break;
624
 
                }
625
 
 
626
 
                case ARG_TIMEOUT:
627
 
                        if (parse_usec(optarg, &arg_timeout) < 0 || arg_timeout <= 0) {
628
 
                                log_error("Failed to parse timeout %s.", optarg);
629
 
                                return -EINVAL;
630
 
                        }
631
 
 
632
 
                        break;
633
 
 
634
 
                case '?':
635
 
                        return -EINVAL;
636
 
 
637
 
                default:
638
 
                        log_error("Unknown option code %c", c);
639
 
                        return -EINVAL;
640
 
                }
641
 
        }
642
 
 
643
 
        if (optind != argc &&
644
 
            optind != argc-1) {
645
 
                help();
646
 
                return -EINVAL;
647
 
        }
648
 
 
649
 
        return 1;
650
 
}
651
 
 
652
 
int main(int argc, char *argv[]) {
653
 
        int r;
654
 
        const char *root;
655
 
 
656
 
        log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
657
 
        log_parse_environment();
658
 
        log_open();
659
 
 
660
 
        umask(0022);
661
 
 
662
 
        if ((r = parse_argv(argc, argv)) <= 0)
663
 
                return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
664
 
 
665
 
        root = optind < argc ? argv[optind] : "/";
666
 
 
667
 
        if (fs_on_read_only(root) > 0) {
668
 
                log_info("Disabling readahead collector due to read-only media.");
669
 
                return 0;
670
 
        }
671
 
 
672
 
        if (!enough_ram()) {
673
 
                log_info("Disabling readahead collector due to low memory.");
674
 
                return 0;
675
 
        }
676
 
 
677
 
        if (detect_virtualization(NULL) > 0) {
678
 
                log_info("Disabling readahead collector due to execution in virtualized environment.");
679
 
                return 0;
680
 
        }
681
 
 
682
 
        if (!(shared = shared_get()))
683
 
                return 1;
684
 
 
685
 
        shared->collect = getpid();
686
 
        __sync_synchronize();
687
 
 
688
 
        if (collect(root) < 0)
689
 
                return 1;
690
 
 
691
 
        return 0;
692
 
}