Whamcloud - gitweb
LU-14052 ofd: support for multiple access readers
[fs/lustre-release.git] / lustre / utils / ofd_access_log_reader.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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 version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  *
22  * Copyright 2020, DataDirect Networks Storage.
23  *
24  * This file is part of Lustre, http://www.lustre.org/
25  *
26  * Author: John L. Hammond <jhammond@whamcloud.com>
27  *
28  * lustre/utils/ofd_access_log_reader.c
29  *
30  * Sample utility to discover and read Lustre (ofd) access logs.
31  *
32  * This demonstrates the discovery and reading of Lustre access logs
33  * (see linux/lustre/lustre_access_log.h and
34  * lustre/ofd/ofd_access_log.c.). By default it opens the control
35  * device, discovers and opens all access log devices, and consumes
36  * all access log entries. If invoked with the --list option then it
37  * prints information about all available devices to stdout and exits.
38  *
39  * Structured trace points (when --trace is used) are added to permit
40  * testing of the access log functionality (see test_165* in
41  * lustre/tests/sanity.sh).
42  */
43 #include <stddef.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <assert.h>
47 #include <dirent.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <getopt.h>
51 #include <inttypes.h>
52 #include <limits.h>
53 #include <malloc.h>
54 #include <pthread.h>
55 #include <signal.h>
56 #include <string.h>
57 #include <unistd.h>
58 #include <sys/epoll.h>
59 #include <sys/ioctl.h>
60 #include <sys/signalfd.h>
61 #include <sys/stat.h>
62 #include <sys/sysmacros.h>
63 #include <sys/timerfd.h>
64 #include <sys/types.h>
65 #include <linux/types.h>
66 #include <linux/lustre/lustre_user.h>
67 #include <linux/lustre/lustre_access_log.h>
68 #include "ofd_access_batch.h"
69 #include "lstddef.h"
70
71 /* TODO fsname filter */
72
73 static FILE *debug_file;
74 static FILE *trace_file;
75
76 #define DEBUG(fmt, args...)                                             \
77         do {                                                            \
78                 if (debug_file != NULL)                                 \
79                         fprintf(debug_file, "DEBUG %s:%d: "fmt, __func__, __LINE__, ##args); \
80         } while (0)
81
82 #define TRACE(fmt, args...)                                             \
83         do {                                                            \
84                 if (trace_file != NULL)                                 \
85                         fprintf(trace_file, "TRACE "fmt, ##args);       \
86         } while (0)
87
88 #define DEBUG_D(x) DEBUG("%s = %"PRIdMAX"\n", #x, (intmax_t)x)
89 #define DEBUG_P(x) DEBUG("%s = %p\n", #x, x)
90 #define DEBUG_S(x) DEBUG("%s = '%s'\n", #x, x)
91 #define DEBUG_U(x) DEBUG("%s = %"PRIuMAX"\n", #x, (uintmax_t)x)
92
93 #define ERROR(fmt, args...) \
94         fprintf(stderr, "%s: "fmt, program_invocation_short_name, ##args)
95
96 #define FATAL(fmt, args...)                     \
97         do {                                    \
98                 ERROR("FATAL: "fmt, ##args);    \
99                 exit(EXIT_FAILURE);             \
100         } while (0)
101
102 enum {
103         ALR_EXIT_SUCCESS = INT_MIN + EXIT_SUCCESS,
104         ALR_EXIT_FAILURE = INT_MIN + EXIT_FAILURE,
105         ALR_ERROR = -1,
106         ALR_EOF = 0,
107         ALR_OK = 1,
108 };
109
110 struct alr_dev {
111         char *alr_name;
112         int (*alr_io)(int /* epoll_fd */, struct alr_dev * /* this */, unsigned int /* mask */);
113         void (*alr_destroy)(struct alr_dev *);
114         int alr_fd;
115 };
116
117 struct alr_log {
118         struct alr_dev alr_dev;
119         char *alr_buf;
120         size_t alr_buf_size;
121         size_t alr_entry_size;
122         size_t alr_read_count;
123         dev_t alr_rdev;
124 };
125
126 static struct alr_log *alr_log[1 << 20]; /* 20 == MINORBITS */
127 static int oal_version; /* FIXME ... major version, minor version */
128 static __u32 alr_filter = 0xffffffff; /* no filter by default */
129 static unsigned int oal_log_major;
130 static unsigned int oal_log_minor_max;
131 static struct alr_batch *alr_batch;
132 static FILE *alr_batch_file;
133 static pthread_mutex_t alr_batch_file_mutex = PTHREAD_MUTEX_INITIALIZER;
134 static const char *alr_batch_file_path;
135 static const char *alr_stats_file_path;
136 static int alr_print_fraction = 100;
137
138 #define D_ALR_DEV "%s %d"
139 #define P_ALR_DEV(ad) \
140         (ad)->alr_name, (ad)->alr_fd
141
142 #define D_ALR_LOG D_ALR_DEV" %u:%u"
143 #define P_ALR_LOG(al) \
144         P_ALR_DEV(&(al)->alr_dev), major((al)->alr_rdev), minor((al)->alr_rdev)
145
146 static void alr_dev_free(int epoll_fd, struct alr_dev *ad)
147 {
148         TRACE("alr_dev_free %s\n", ad->alr_name);
149
150         if (!(ad->alr_fd < 0))
151                 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, ad->alr_fd, NULL);
152
153         if (ad->alr_destroy != NULL)
154                 (*ad->alr_destroy)(ad);
155
156         if (!(ad->alr_fd < 0))
157                 close(ad->alr_fd);
158
159         free(ad->alr_name);
160         free(ad);
161 }
162
163 static struct alr_log **alr_log_lookup(dev_t rdev)
164 {
165         assert(major(rdev) == oal_log_major);
166
167         if (!(minor(rdev) < ARRAY_SIZE(alr_log)))
168                 return NULL;
169
170         return &alr_log[minor(rdev)];
171 }
172
173 static const char *alr_flags_to_str(unsigned int flags)
174 {
175         switch (flags & (OFD_ACCESS_READ | OFD_ACCESS_WRITE)) {
176         default:
177                 return "0";
178         case OFD_ACCESS_READ:
179                 return "r";
180         case OFD_ACCESS_WRITE:
181                 return "w";
182         case OFD_ACCESS_READ | OFD_ACCESS_WRITE:
183                 return "rw";
184         }
185 }
186
187 /* /dev/lustre-access-log/scratch-OST0000 device poll callback: read entries
188  * from log and print. */
189 static int alr_log_io(int epoll_fd, struct alr_dev *ad, unsigned int mask)
190 {
191         struct alr_log *al = container_of(ad, struct alr_log, alr_dev);
192         ssize_t i, count;
193
194         TRACE("alr_log_io %s\n", ad->alr_name);
195         DEBUG_U(mask);
196
197         assert(al->alr_entry_size != 0);
198         assert(al->alr_buf_size != 0);
199         assert(al->alr_buf != NULL);
200
201         count = read(ad->alr_fd, al->alr_buf, al->alr_buf_size);
202         if (count < 0) {
203                 ERROR("cannot read events from '%s': %s\n", ad->alr_name, strerror(errno));
204                 return ALR_ERROR;
205         }
206
207         if (count == 0) {
208                 TRACE("alr_log_eof %s\n", ad->alr_name);
209                 return ALR_EOF;
210         }
211
212         if (count % al->alr_entry_size != 0) {
213                 ERROR("invalid read from "D_ALR_LOG": entry_size = %zu, count = %zd\n",
214                         P_ALR_LOG(al), al->alr_entry_size, count);
215                 return ALR_ERROR;
216         }
217
218         DEBUG("read "D_ALR_LOG", count = %zd\n", P_ALR_LOG(al), count);
219
220         al->alr_read_count += count / al->alr_entry_size;
221
222         for (i = 0; i < count; i += al->alr_entry_size) {
223                 struct ofd_access_entry_v1 *oae =
224                         (struct ofd_access_entry_v1 *)&al->alr_buf[i];
225
226                 TRACE("alr_log_entry %s "DFID" %lu %lu %lu %u %u %s\n",
227                         ad->alr_name,
228                         PFID(&oae->oae_parent_fid),
229                         (unsigned long)oae->oae_begin,
230                         (unsigned long)oae->oae_end,
231                         (unsigned long)oae->oae_time,
232                         (unsigned int)oae->oae_size,
233                         (unsigned int)oae->oae_segment_count,
234                         alr_flags_to_str(oae->oae_flags));
235
236                 alr_batch_add(alr_batch, ad->alr_name, &oae->oae_parent_fid,
237                         oae->oae_time, oae->oae_begin, oae->oae_end,
238                         oae->oae_size, oae->oae_segment_count, oae->oae_flags);
239         }
240
241         return ALR_OK;
242 }
243
244 static void alr_log_destroy(struct alr_dev *ad)
245 {
246         struct alr_log *al = container_of(ad, struct alr_log, alr_dev);
247         struct alr_log **pal;
248
249         TRACE("alr_log_free %s\n", ad->alr_name);
250         assert(major(al->alr_rdev) == oal_log_major);
251
252         pal = alr_log_lookup(al->alr_rdev);
253         if (pal != NULL && *pal == al)
254                 *pal = NULL;
255
256         free(al->alr_buf);
257         al->alr_buf = NULL;
258         al->alr_buf_size = 0;
259 }
260
261 /* Add an access log (identified by path) to the epoll set. */
262 static int alr_log_add(int epoll_fd, const char *path)
263 {
264         struct alr_log **pal, *al = NULL;
265         struct stat st;
266         int fd = -1;
267         int rc;
268
269         DEBUG_S(path);
270
271         fd = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC);
272         if (fd < 0) {
273                 ERROR("cannot open device '%s': %s\n", path, strerror(errno));
274                 rc = (errno == ENOENT ? 0 : -1); /* Possible race. */
275                 goto out;
276         }
277
278         /* Revalidate rdev in case of race. */
279         rc = fstat(fd, &st);
280         if (rc < 0) {
281                 ERROR("cannot stat '%s': %s\n", path, strerror(errno));
282                 goto out;
283         }
284
285         if (major(st.st_rdev) != oal_log_major)
286                 goto out;
287
288         pal = alr_log_lookup(st.st_rdev);
289         if (pal == NULL) {
290                 ERROR("no device slot available for '%s' with minor %u\n",
291                         path, minor(st.st_rdev));
292                 goto out;
293         }
294
295         if (*pal != NULL)
296                 goto out; /* We already have this device. */
297
298         struct lustre_access_log_info_v1 lali;
299
300         memset(&lali, 0, sizeof(lali));
301
302         rc = ioctl(fd, LUSTRE_ACCESS_LOG_IOCTL_INFO, &lali);
303         if (rc < 0) {
304                 ERROR("cannot get info for device '%s': %s\n",
305                         path, strerror(errno));
306                 goto out;
307         }
308
309         if (lali.lali_type != LUSTRE_ACCESS_LOG_TYPE_OFD) {
310                 rc = 0;
311                 goto out;
312         }
313         rc = ioctl(fd, LUSTRE_ACCESS_LOG_IOCTL_FILTER, alr_filter);
314         if (rc < 0) {
315                 ERROR("cannot set filter '%s': %s\n",
316                         path, strerror(errno));
317                 goto out;
318         }
319
320         al = calloc(1, sizeof(*al));
321         if (al == NULL)
322                 FATAL("cannot allocate struct alr_dev of size %zu: %s\n",
323                         sizeof(*al), strerror(errno));
324
325         al->alr_dev.alr_io = &alr_log_io;
326         al->alr_dev.alr_destroy = &alr_log_destroy;
327         al->alr_dev.alr_fd = fd;
328         fd = -1;
329
330         al->alr_rdev = st.st_rdev;
331
332         al->alr_dev.alr_name = strdup(lali.lali_name);
333         if (al->alr_dev.alr_name == NULL)
334                 FATAL("cannot copy name of size %zu: %s\n",
335                         strlen(lali.lali_name), strerror(errno));
336
337         al->alr_buf_size = lali.lali_log_size;
338         al->alr_entry_size = lali.lali_entry_size;
339
340         if (al->alr_entry_size == 0) {
341                 ERROR("device '%s' has zero entry size\n", path);
342                 rc = -1;
343                 goto out;
344         }
345
346         if (al->alr_buf_size == 0)
347                 al->alr_buf_size = 1048576;
348
349         al->alr_buf_size = roundup(al->alr_buf_size, al->alr_entry_size);
350
351         al->alr_buf = malloc(al->alr_buf_size);
352         if (al->alr_buf == NULL)
353                 FATAL("cannot allocate log buffer for '%s' of size %zu: %s\n",
354                         path, al->alr_buf_size, strerror(errno));
355
356         struct epoll_event ev = {
357                 .events = EPOLLIN | EPOLLHUP,
358                 .data.ptr = &al->alr_dev,
359         };
360
361         rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, al->alr_dev.alr_fd, &ev);
362         if (rc < 0) {
363                 ERROR("cannot add device '%s' to epoll set: %s\n",
364                         path, strerror(errno));
365                 goto out;
366         }
367
368         TRACE("alr_log_add %s\n", al->alr_dev.alr_name);
369
370         if (oal_log_minor_max < minor(al->alr_rdev))
371                 oal_log_minor_max = minor(al->alr_rdev);
372
373         assert(*pal == NULL);
374         *pal = al;
375         al = NULL;
376         rc = 0;
377 out:
378         if (al != NULL)
379                 alr_dev_free(epoll_fd, &al->alr_dev);
380
381         if (!(fd < 0))
382                 close(fd);
383
384         return rc;
385 }
386
387 /* Call LUSTRE_ACCESS_LOG_IOCTL_INFO to get access log info and print
388  * YAML formatted info to stdout. */
389 static int alr_log_info(struct alr_log *al)
390 {
391         struct lustre_access_log_info_v1 lali;
392         int rc;
393
394         rc = ioctl(al->alr_dev.alr_fd, LUSTRE_ACCESS_LOG_IOCTL_INFO, &lali);
395         if (rc < 0) {
396                 ERROR("cannot get info for device '%s': %s\n",
397                         al->alr_dev.alr_name, strerror(errno));
398                 return -1;
399         }
400
401         printf("- name: %s\n"
402                "  version: %#x\n"
403                "  type: %#x\n"
404                "  log_size: %u\n"
405                "  entry_size: %u\n",
406                lali.lali_name,
407                lali.lali_version,
408                lali.lali_type,
409                lali.lali_log_size,
410                lali.lali_entry_size);
411
412         return 0;
413 }
414
415 static int alr_log_stats(FILE *file, struct alr_log *al)
416 {
417         struct lustre_access_log_info_v1 lali;
418         int rc;
419
420         rc = ioctl(al->alr_dev.alr_fd, LUSTRE_ACCESS_LOG_IOCTL_INFO, &lali);
421         if (rc < 0) {
422                 ERROR("cannot get info for device '%s': %s\n",
423                         al->alr_dev.alr_name, strerror(errno));
424                 return -1;
425         }
426
427 #define X(m) \
428         fprintf(file, "STATS %s %s %u\n", lali.lali_name, #m, lali.m)
429
430         X(_lali_head);
431         X(_lali_tail);
432         X(_lali_entry_space);
433         X(_lali_entry_count);
434         X(_lali_drop_count);
435         X(_lali_is_closed);
436 #undef X
437
438         fprintf(file, "STATS %s %s %zu\n",
439                 lali.lali_name, "alr_read_count", al->alr_read_count);
440
441         return 0;
442 }
443
444 static void alr_log_stats_all(void)
445 {
446         FILE *stats_file;
447         int m;
448
449         if (alr_stats_file_path == NULL) {
450                 stats_file = stderr;
451         } else if (strcmp(alr_stats_file_path, "-") == 0) {
452                 stats_file = stdout;
453         } else {
454                 stats_file = fopen(alr_stats_file_path, "a");
455                 if (stats_file == NULL) {
456                         ERROR("cannot open '%s': %s\n",
457                               alr_stats_file_path, strerror(errno));
458                         return;
459                 }
460         }
461
462         for (m = 0; m <= oal_log_minor_max; m++) {
463                 if (alr_log[m] == NULL)
464                         continue;
465
466                 alr_log_stats(stats_file, alr_log[m]);
467         }
468
469         if (stats_file == stdout || stats_file == stderr)
470                 fflush(stats_file);
471         else
472                 fclose(stats_file);
473 }
474
475 /* Scan /dev/lustre-access-log/ for new access log devices and add to
476  * epoll set. */
477 static int alr_scan(int epoll_fd)
478 {
479         const char dir_path[] = "/dev/"LUSTRE_ACCESS_LOG_DIR_NAME;
480         DIR *dir;
481         int dir_fd;
482         struct dirent *d;
483         int rc;
484
485         dir = opendir(dir_path);
486         if (dir == NULL) {
487                 ERROR("cannot open '%s' for scanning: %s\n", dir_path, strerror(errno));
488                 return ALR_EXIT_FAILURE;
489         }
490
491         dir_fd = dirfd(dir);
492
493         /* Scan /dev for devices with major equal to oal_log_major and add
494          * any new devices. */
495         while ((d = readdir(dir)) != NULL) {
496                 char path[6 + PATH_MAX];
497                 struct alr_log **pal;
498                 struct stat st;
499
500                 if (d->d_type != DT_CHR)
501                         continue;
502
503                 rc = fstatat(dir_fd, d->d_name, &st, 0);
504                 if (rc < 0) {
505                         ERROR("cannot stat '%s/%s' while scanning: %s\n",
506                                 dir_path, d->d_name, strerror(errno));
507                         continue;
508                 }
509
510                 if (!S_ISCHR(st.st_mode))
511                         continue;
512
513                 if (major(st.st_rdev) != oal_log_major)
514                         continue;
515
516                 pal = alr_log_lookup(st.st_rdev);
517                 if (pal == NULL) {
518                         ERROR("no device slot available for '%s/%s' with minor %u\n",
519                                 dir_path, d->d_name, minor(st.st_rdev));
520                         continue;
521                 }
522
523                 if (*pal != NULL)
524                         continue; /* We already have this device. */
525
526                 snprintf(path, sizeof(path), "%s/%s", dir_path, d->d_name);
527
528                 alr_log_add(epoll_fd, path);
529         }
530
531         closedir(dir);
532
533         return ALR_OK;
534 }
535
536 /* /dev/lustre-access-log/control device poll callback: call prescan
537  * ioctl and scan /dev/lustre-access-log/ for new access log
538  * devices. */
539 static int alr_ctl_io(int epoll_fd, struct alr_dev *cd, unsigned int mask)
540 {
541         int rc;
542
543         TRACE("%s\n", __func__);
544         DEBUG_U(mask);
545
546         if (mask & EPOLLERR)
547                 return ALR_EXIT_FAILURE;
548
549         if (mask & EPOLLHUP)
550                 return ALR_EXIT_SUCCESS;
551
552         rc = ioctl(cd->alr_fd, LUSTRE_ACCESS_LOG_IOCTL_PRESCAN);
553         if (rc < 0) {
554                 ERROR("cannot start scanning: %s\n", strerror(errno));
555                 return ALR_EXIT_FAILURE;
556         }
557
558         return alr_scan(epoll_fd);
559 }
560
561 /* signalfd epoll callback. Handle SIGINT and SIGTERM by breaking from
562  * the epoll loop and exiting normally.*/
563 static int alr_signal_io(int epoll_fd, struct alr_dev *sd, unsigned int mask)
564 {
565         struct signalfd_siginfo ssi;
566         ssize_t rc;
567
568         TRACE("%s\n", __func__);
569         DEBUG_U(mask);
570
571         rc = read(sd->alr_fd, &ssi, sizeof(ssi));
572         if (rc <= 0)
573                 return ALR_OK;
574
575         DEBUG_U(ssi.ssi_signo);
576         switch (ssi.ssi_signo) {
577         case SIGINT:
578         case SIGTERM:
579                 return ALR_EXIT_SUCCESS;
580         case SIGUSR1:
581                 alr_log_stats_all();
582
583                 return ALR_OK;
584         case SIGUSR2:
585                 if (debug_file == NULL)
586                         debug_file = stderr;
587
588                 if (trace_file == NULL)
589                         trace_file = stderr;
590
591                 return ALR_OK;
592         default:
593                 return ALR_OK;
594         }
595 }
596
597 /* batching timerfd epoll callback. Print batched access entries to
598  * alr_batch_file. */
599 static int alr_batch_timer_io(int epoll_fd, struct alr_dev *td, unsigned int mask)
600 {
601         time_t now = time(NULL);
602         uint64_t expire_count;
603         ssize_t rc;
604
605         TRACE("%s\n", __func__);
606         DEBUG_D(now);
607         DEBUG_U(mask);
608
609         rc = read(td->alr_fd, &expire_count, sizeof(expire_count));
610         if (rc <= 0)
611                 return ALR_OK;
612
613         DEBUG_U(expire_count);
614
615         rc = alr_batch_print(alr_batch, alr_batch_file, &alr_batch_file_mutex,
616                              alr_print_fraction);
617         if (rc < 0) {
618                 ERROR("cannot write to '%s': %s\n",
619                         alr_batch_file_path, strerror(errno));
620                 goto out;
621         }
622 out:
623         /* Failed writes will leave alr_batch_file (pipe) in a
624          * weird state so make that fatal. */
625         return (rc < 0) ? ALR_EXIT_FAILURE : ALR_OK;
626 }
627
628 static struct alr_dev *alr_dev_create(int epoll_fd, int fd, const char *name,
629                         int (*io)(int, struct alr_dev *, unsigned int),
630                         void (*destroy)(struct alr_dev *))
631 {
632         struct alr_dev *alr;
633         int rc;
634
635         alr = calloc(1, sizeof(*alr));
636         if (alr == NULL)
637                 return NULL;
638
639         alr->alr_name = strdup(name);
640         if (alr->alr_name == NULL) {
641                 free(alr);
642                 return NULL;
643         }
644         alr->alr_io = io;
645         alr->alr_destroy = destroy;
646         alr->alr_fd = fd;
647
648         struct epoll_event event = {
649                 .events = EPOLLIN | EPOLLHUP,
650                 .data.ptr = alr,
651         };
652
653         rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, alr->alr_fd, &event);
654         if (rc < 0) {
655                 free(alr);
656                 return NULL;
657         }
658
659         return alr;
660 }
661
662 void usage(void)
663 {
664         printf("Usage: %s: [OPTION]...\n"
665 "Discover, read, batch, and write Lustre access logs\n"
666 "\n"
667 "Mandatory arguments to long options are mandatory for short options too.\n"
668 "  -f, --batch-file=FILE          print batch to file (default stdout)\n"
669 "  -F, --batch-fraction=P         set batch printing fraction to P/100\n"
670 "  -i, --batch-interval=INTERVAL  print batch every INTERVAL seconds\n"
671 "  -o, --batch-offset=OFFSET      print batch at OFFSET seconds\n"
672 "  -I, --mdt-index-filter=INDEX   set log MDT index filter to INDEX\n"
673 "  -h, --help                     display this help and exit\n"
674 "  -l, --list                     print YAML list of available access logs\n"
675 "  -d, --debug[=FILE]             print debug messages to FILE (stderr)\n"
676 "  -s, --stats=FILE               print stats messages to FILE (stderr)\n"
677 "  -t, --trace[=FILE]             print trace messages to FILE (stderr)\n",
678                 program_invocation_short_name);
679 }
680
681 int main(int argc, char *argv[])
682 {
683         const char ctl_path[] = "/dev/"LUSTRE_ACCESS_LOG_DIR_NAME"/control";
684         struct alr_dev *alr_signal = NULL;
685         struct alr_dev *alr_batch_timer = NULL;
686         struct alr_dev *alr_ctl = NULL;
687         time_t batch_interval = 0;
688         time_t batch_offset = 0;
689         unsigned int m;
690         int list_info = 0;
691         int epoll_fd = -1;
692         int exit_status;
693         int rc;
694         int c;
695
696         static struct option options[] = {
697                 { .name = "batch-file", .has_arg = required_argument, .val = 'f', },
698                 { .name = "batch-fraction", .has_arg = required_argument, .val = 'F', },
699                 { .name = "batch-interval", .has_arg = required_argument, .val = 'i', },
700                 { .name = "batch-offset", .has_arg = required_argument, .val = 'o', },
701                 { .name = "mdt-index-filter", .has_arg = required_argument, .val = 'I' },
702                 { .name = "debug", .has_arg = optional_argument, .val = 'd', },
703                 { .name = "help", .has_arg = no_argument, .val = 'h', },
704                 { .name = "list", .has_arg = no_argument, .val = 'l', },
705                 { .name = "stats", .has_arg = required_argument, .val = 's', },
706                 { .name = "trace", .has_arg = optional_argument, .val = 't', },
707                 { .name = NULL, },
708         };
709
710         while ((c = getopt_long(argc, argv, "d::f:F:hi:I:ls:t::", options, NULL)) != -1) {
711                 switch (c) {
712                 case 'f':
713                         alr_batch_file_path = optarg;
714                         break;
715                 case 'i':
716                         errno = 0;
717                         batch_interval = strtoll(optarg, NULL, 0);
718                         if (batch_interval < 0 || batch_interval >= 1048576 ||
719                             errno != 0)
720                                 FATAL("invalid batch interval '%s'\n", optarg);
721                         break;
722                 case 'o':
723                         errno = 0;
724                         batch_offset = strtoll(optarg, NULL, 0);
725                         if (batch_offset < 0 || batch_offset >= 1048576 ||
726                             errno != 0)
727                                 FATAL("invalid batch offset '%s'\n", optarg);
728                         break;
729                 case 'd':
730                         if (optarg == NULL) {
731                                 debug_file = stderr;
732                         } else if (strcmp(optarg, "-") == 0) {
733                                 debug_file = stdout;
734                         } else {
735                                 debug_file = fopen(optarg, "a");
736                                 if (debug_file == NULL)
737                                         FATAL("cannot open debug file '%s': %s\n",
738                                                 optarg, strerror(errno));
739                         }
740
741                         break;
742                 case 'h':
743                         usage();
744                         exit(EXIT_SUCCESS);
745                 case 'F':
746                         alr_print_fraction = strtoll(optarg, NULL, 0);
747                         if (alr_print_fraction < 1 || alr_print_fraction > 100)
748                                 FATAL("invalid batch offset '%s'\n", optarg);
749                         break;
750                 case 'I':
751                         alr_filter = strtoll(optarg, NULL, 0);
752                         break;
753                 case 'l':
754                         list_info = 1;
755                         break;
756                 case 's':
757                         alr_stats_file_path = optarg;
758                         break;
759                 case 't':
760                         if (optarg == NULL) {
761                                 trace_file = stderr;
762                         } else if (strcmp(optarg, "-") == 0) {
763                                 trace_file = stdout;
764                         } else {
765                                 trace_file = fopen(optarg, "a");
766                                 if (debug_file == NULL)
767                                         FATAL("cannot open debug file '%s': %s\n",
768                                                 optarg, strerror(errno));
769                         }
770
771                         break;
772                 case '?':
773                         fprintf(stderr, "Try '%s --help' for more information.\n",
774                                 program_invocation_short_name);
775                         exit(EXIT_FAILURE);
776                 }
777         }
778
779         if (batch_interval > 0) {
780                 alr_batch = alr_batch_create(-1);
781                 if (alr_batch == NULL)
782                         FATAL("cannot create batch struct: %s\n",
783                                 strerror(errno));
784         }
785
786         if (alr_batch_file_path != NULL) {
787                 alr_batch_file = fopen(alr_batch_file_path, "w");
788                 if (alr_batch_file == NULL)
789                         FATAL("cannot open batch file '%s': %s\n",
790                                 alr_batch_file_path, strerror(errno));
791         } else {
792                 alr_batch_file_path = "stdout";
793                 alr_batch_file = stdout;
794         }
795
796         epoll_fd = epoll_create1(EPOLL_CLOEXEC);
797         if (epoll_fd < 0)
798                 FATAL("cannot create epoll set: %s\n", strerror(errno));
799
800         /* Setup signal FD and add to epoll set. */
801         sigset_t signal_mask;
802         sigemptyset(&signal_mask);
803         sigaddset(&signal_mask, SIGINT);
804         sigaddset(&signal_mask, SIGTERM);
805         sigaddset(&signal_mask, SIGUSR1);
806         sigaddset(&signal_mask, SIGUSR2);
807         rc = sigprocmask(SIG_BLOCK, &signal_mask, NULL);
808         if (rc < 0)
809                 FATAL("cannot set process signal mask: %s\n", strerror(errno));
810
811         int signal_fd = signalfd(-1, &signal_mask, SFD_NONBLOCK|SFD_CLOEXEC);
812         if (signal_fd < 0)
813                 FATAL("cannot create signalfd: %s\n", strerror(errno));
814
815         alr_signal = alr_dev_create(epoll_fd, signal_fd, "signal", &alr_signal_io, NULL);
816         if (alr_signal == NULL)
817                 FATAL("cannot register signalfd: %s\n", strerror(errno));
818
819         signal_fd = -1;
820
821         /* Setup batch timer FD and add to epoll set. */
822         struct timespec now;
823         rc = clock_gettime(CLOCK_REALTIME, &now);
824         if (rc < 0)
825                 FATAL("cannot read realtime clock: %s\n", strerror(errno));
826
827         int timer_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
828         if (timer_fd < 0)
829                 FATAL("cannot create batch timerfd: %s\n", strerror(errno));
830
831         struct itimerspec it = {
832                 .it_value.tv_sec = (batch_interval > 0) ?
833                                    roundup(now.tv_sec, batch_interval) +
834                                    (batch_offset % batch_interval) :
835                                    0,
836                 .it_interval.tv_sec = batch_interval,
837         };
838
839         DEBUG_D(it.it_value.tv_sec);
840
841         rc = timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &it, NULL);
842         if (rc < 0)
843                 FATAL("cannot arm timerfd: %s\n", strerror(errno));
844
845         alr_batch_timer = alr_dev_create(epoll_fd, timer_fd, "batch_timer",
846                                         &alr_batch_timer_io, NULL);
847         if (alr_batch_timer == NULL)
848                 FATAL("cannot register batch timerfd: %s\n", strerror(errno));
849
850         timer_fd = -1;
851
852         /* Open control device. */
853         int ctl_fd = open(ctl_path, O_RDONLY|O_NONBLOCK|O_CLOEXEC);
854         if (ctl_fd < 0)
855                 FATAL("cannot open '%s': %s\n", ctl_path, strerror(errno));
856
857         /* Get and print interface version. */
858         oal_version = ioctl(ctl_fd, LUSTRE_ACCESS_LOG_IOCTL_VERSION);
859         if (oal_version < 0)
860                 FATAL("cannot get ofd access log interface version: %s\n", strerror(errno));
861
862         DEBUG_D(oal_version);
863
864         /* Get and print device major used for access log devices. */
865         oal_log_major = ioctl(ctl_fd, LUSTRE_ACCESS_LOG_IOCTL_MAJOR);
866         if (oal_log_major < 0)
867                 FATAL("cannot get ofd access log major: %s\n", strerror(errno));
868
869         DEBUG_D(oal_log_major);
870
871         /* Add control device to epoll set. */
872         alr_ctl = alr_dev_create(epoll_fd, ctl_fd, "control", &alr_ctl_io, NULL);
873         if (alr_ctl == NULL)
874                 FATAL("cannot register control device: %s\n", strerror(errno));
875
876         ctl_fd = -1;
877
878         do {
879                 struct epoll_event ev[32];
880                 int timeout = (list_info ? 0 : -1);
881                 int i, ev_count;
882
883                 ev_count = epoll_wait(epoll_fd, ev, ARRAY_SIZE(ev), timeout);
884                 if (ev_count < 0) {
885                         if (errno == EINTR) /* Signal or timeout. */
886                                 continue;
887
888                         ERROR("cannot wait on epoll set: %s\n", strerror(errno));
889                         exit_status = EXIT_FAILURE;
890                         goto out;
891                 }
892
893                 DEBUG_D(ev_count);
894
895                 for (i = 0; i < ev_count; i++) {
896                         struct alr_dev *ad = ev[i].data.ptr;
897                         unsigned int mask = ev[i].events;
898
899                         rc = (*ad->alr_io)(epoll_fd, ad, mask);
900                         switch (rc) {
901                         case ALR_EXIT_FAILURE:
902                                 exit_status = EXIT_FAILURE;
903                                 goto out;
904                         case ALR_EXIT_SUCCESS:
905                                 exit_status = EXIT_SUCCESS;
906                                 goto out;
907                         case ALR_ERROR:
908                         case ALR_EOF:
909                                 alr_dev_free(epoll_fd, ad);
910                                 break;
911                         case ALR_OK:
912                         default:
913                                 break;
914                         }
915                 }
916         } while (!list_info);
917
918         exit_status = EXIT_SUCCESS;
919 out:
920         assert(oal_log_minor_max < ARRAY_SIZE(alr_log));
921
922         for (m = 0; m <= oal_log_minor_max; m++) {
923                 if (alr_log[m] == NULL)
924                         continue;
925
926                 if (list_info) {
927                         rc = alr_log_info(alr_log[m]);
928                         if (rc < 0)
929                                 exit_status = EXIT_FAILURE;
930                 }
931
932                 alr_dev_free(epoll_fd, &alr_log[m]->alr_dev);
933         }
934
935         alr_dev_free(epoll_fd, alr_ctl);
936         alr_dev_free(epoll_fd, alr_signal);
937         alr_dev_free(epoll_fd, alr_batch_timer);
938         close(epoll_fd);
939
940         alr_batch_destroy(alr_batch);
941
942         DEBUG_D(exit_status);
943
944         return exit_status;
945 }