Whamcloud - gitweb
5bbc20ad6a4d80f5766f09625a5448bdbaedfa8e
[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         dev_t alr_rdev;
123 };
124
125 static struct alr_log *alr_log[1 << 20]; /* 20 == MINORBITS */
126 static int oal_version; /* FIXME ... major version, minor version */
127 static unsigned int oal_log_major;
128 static unsigned int oal_log_minor_max;
129 static struct alr_batch *alr_batch;
130 static FILE *alr_batch_file;
131 static pthread_mutex_t alr_batch_file_mutex = PTHREAD_MUTEX_INITIALIZER;
132 static const char *alr_batch_file_path;
133 static int alr_print_fraction = 100;
134
135 #define D_ALR_DEV "%s %d"
136 #define P_ALR_DEV(ad) \
137         (ad)->alr_name, (ad)->alr_fd
138
139 #define D_ALR_LOG D_ALR_DEV" %u:%u"
140 #define P_ALR_LOG(al) \
141         P_ALR_DEV(&(al)->alr_dev), major((al)->alr_rdev), minor((al)->alr_rdev)
142
143 static void alr_dev_free(int epoll_fd, struct alr_dev *ad)
144 {
145         TRACE("alr_dev_free %s\n", ad->alr_name);
146
147         if (!(ad->alr_fd < 0))
148                 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, ad->alr_fd, NULL);
149
150         if (ad->alr_destroy != NULL)
151                 (*ad->alr_destroy)(ad);
152
153         if (!(ad->alr_fd < 0))
154                 close(ad->alr_fd);
155
156         free(ad->alr_name);
157         free(ad);
158 }
159
160 static struct alr_log **alr_log_lookup(dev_t rdev)
161 {
162         assert(major(rdev) == oal_log_major);
163
164         if (!(minor(rdev) < ARRAY_SIZE(alr_log)))
165                 return NULL;
166
167         return &alr_log[minor(rdev)];
168 }
169
170 static const char *alr_flags_to_str(unsigned int flags)
171 {
172         switch (flags & (OFD_ACCESS_READ | OFD_ACCESS_WRITE)) {
173         default:
174                 return "0";
175         case OFD_ACCESS_READ:
176                 return "r";
177         case OFD_ACCESS_WRITE:
178                 return "w";
179         case OFD_ACCESS_READ | OFD_ACCESS_WRITE:
180                 return "rw";
181         }
182 }
183
184 /* /dev/lustre-access-log/scratch-OST0000 device poll callback: read entries
185  * from log and print. */
186 static int alr_log_io(int epoll_fd, struct alr_dev *ad, unsigned int mask)
187 {
188         struct alr_log *al = container_of(ad, struct alr_log, alr_dev);
189         ssize_t i, count;
190
191         TRACE("alr_log_io %s\n", ad->alr_name);
192         DEBUG_U(mask);
193
194         assert(al->alr_entry_size != 0);
195         assert(al->alr_buf_size != 0);
196         assert(al->alr_buf != NULL);
197
198         count = read(ad->alr_fd, al->alr_buf, al->alr_buf_size);
199         if (count < 0) {
200                 ERROR("cannot read events from '%s': %s\n", ad->alr_name, strerror(errno));
201                 return ALR_ERROR;
202         }
203
204         if (count == 0) {
205                 TRACE("alr_log_eof %s\n", ad->alr_name);
206                 return ALR_EOF;
207         }
208
209         if (count % al->alr_entry_size != 0) {
210                 ERROR("invalid read from "D_ALR_LOG": entry_size = %zu, count = %zd\n",
211                         P_ALR_LOG(al), al->alr_entry_size, count);
212                 return ALR_ERROR;
213         }
214
215         DEBUG("read "D_ALR_LOG", count = %zd\n", P_ALR_LOG(al), count);
216
217         for (i = 0; i < count; i += al->alr_entry_size) {
218                 struct ofd_access_entry_v1 *oae =
219                         (struct ofd_access_entry_v1 *)&al->alr_buf[i];
220
221                 TRACE("alr_log_entry %s "DFID" %lu %lu %lu %u %u %s\n",
222                         ad->alr_name,
223                         PFID(&oae->oae_parent_fid),
224                         (unsigned long)oae->oae_begin,
225                         (unsigned long)oae->oae_end,
226                         (unsigned long)oae->oae_time,
227                         (unsigned int)oae->oae_size,
228                         (unsigned int)oae->oae_segment_count,
229                         alr_flags_to_str(oae->oae_flags));
230
231                 alr_batch_add(alr_batch, ad->alr_name, &oae->oae_parent_fid,
232                         oae->oae_time, oae->oae_begin, oae->oae_end,
233                         oae->oae_size, oae->oae_segment_count, oae->oae_flags);
234         }
235
236         return ALR_OK;
237 }
238
239 static void alr_log_destroy(struct alr_dev *ad)
240 {
241         struct alr_log *al = container_of(ad, struct alr_log, alr_dev);
242         struct alr_log **pal;
243
244         TRACE("alr_log_free %s\n", ad->alr_name);
245         assert(major(al->alr_rdev) == oal_log_major);
246
247         pal = alr_log_lookup(al->alr_rdev);
248         if (pal != NULL && *pal == al)
249                 *pal = NULL;
250
251         free(al->alr_buf);
252         al->alr_buf = NULL;
253         al->alr_buf_size = 0;
254 }
255
256 /* Add an access log (identified by path) to the epoll set. */
257 static int alr_log_add(int epoll_fd, const char *path)
258 {
259         struct alr_log **pal, *al = NULL;
260         struct stat st;
261         int fd = -1;
262         int rc;
263
264         DEBUG_S(path);
265
266         fd = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC);
267         if (fd < 0) {
268                 ERROR("cannot open device '%s': %s\n", path, strerror(errno));
269                 rc = (errno == ENOENT ? 0 : -1); /* Possible race. */
270                 goto out;
271         }
272
273         /* Revalidate rdev in case of race. */
274         rc = fstat(fd, &st);
275         if (rc < 0) {
276                 ERROR("cannot stat '%s': %s\n", path, strerror(errno));
277                 goto out;
278         }
279
280         if (major(st.st_rdev) != oal_log_major)
281                 goto out;
282
283         pal = alr_log_lookup(st.st_rdev);
284         if (pal == NULL) {
285                 ERROR("no device slot available for '%s' with minor %u\n",
286                         path, minor(st.st_rdev));
287                 goto out;
288         }
289
290         if (*pal != NULL)
291                 goto out; /* We already have this device. */
292
293         struct lustre_access_log_info_v1 lali;
294
295         memset(&lali, 0, sizeof(lali));
296
297         rc = ioctl(fd, LUSTRE_ACCESS_LOG_IOCTL_INFO, &lali);
298         if (rc < 0) {
299                 ERROR("cannot get info for device '%s': %s\n",
300                         path, strerror(errno));
301                 goto out;
302         }
303
304         if (lali.lali_type != LUSTRE_ACCESS_LOG_TYPE_OFD) {
305                 rc = 0;
306                 goto out;
307         }
308
309         al = calloc(1, sizeof(*al));
310         if (al == NULL)
311                 FATAL("cannot allocate struct alr_dev of size %zu: %s\n",
312                         sizeof(*al), strerror(errno));
313
314         al->alr_dev.alr_io = &alr_log_io;
315         al->alr_dev.alr_destroy = &alr_log_destroy;
316         al->alr_dev.alr_fd = fd;
317         fd = -1;
318
319         al->alr_rdev = st.st_rdev;
320
321         al->alr_dev.alr_name = strdup(lali.lali_name);
322         if (al->alr_dev.alr_name == NULL)
323                 FATAL("cannot copy name of size %zu: %s\n",
324                         strlen(lali.lali_name), strerror(errno));
325
326         al->alr_buf_size = lali.lali_log_size;
327         al->alr_entry_size = lali.lali_entry_size;
328
329         if (al->alr_entry_size == 0) {
330                 ERROR("device '%s' has zero entry size\n", path);
331                 rc = -1;
332                 goto out;
333         }
334
335         if (al->alr_buf_size == 0)
336                 al->alr_buf_size = 1048576;
337
338         al->alr_buf_size = roundup(al->alr_buf_size, al->alr_entry_size);
339
340         al->alr_buf = malloc(al->alr_buf_size);
341         if (al->alr_buf == NULL)
342                 FATAL("cannot allocate log buffer for '%s' of size %zu: %s\n",
343                         path, al->alr_buf_size, strerror(errno));
344
345         struct epoll_event ev = {
346                 .events = EPOLLIN | EPOLLHUP,
347                 .data.ptr = &al->alr_dev,
348         };
349
350         rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, al->alr_dev.alr_fd, &ev);
351         if (rc < 0) {
352                 ERROR("cannot add device '%s' to epoll set: %s\n",
353                         path, strerror(errno));
354                 goto out;
355         }
356
357         TRACE("alr_log_add %s\n", al->alr_dev.alr_name);
358
359         if (oal_log_minor_max < minor(al->alr_rdev))
360                 oal_log_minor_max = minor(al->alr_rdev);
361
362         assert(*pal == NULL);
363         *pal = al;
364         al = NULL;
365         rc = 0;
366 out:
367         if (al != NULL)
368                 alr_dev_free(epoll_fd, &al->alr_dev);
369
370         if (!(fd < 0))
371                 close(fd);
372
373         return rc;
374 }
375
376 /* Scan /dev/lustre-access-log/ for new access log devices and add to
377  * epoll set. */
378 static int alr_scan(int epoll_fd)
379 {
380         const char dir_path[] = "/dev/"LUSTRE_ACCESS_LOG_DIR_NAME;
381         DIR *dir;
382         int dir_fd;
383         struct dirent *d;
384         int rc;
385
386         dir = opendir(dir_path);
387         if (dir == NULL) {
388                 ERROR("cannot open '%s' for scanning: %s\n", dir_path, strerror(errno));
389                 return ALR_EXIT_FAILURE;
390         }
391
392         dir_fd = dirfd(dir);
393
394         /* Scan /dev for devices with major equal to oal_log_major and add
395          * any new devices. */
396         while ((d = readdir(dir)) != NULL) {
397                 char path[6 + PATH_MAX];
398                 struct alr_log **pal;
399                 struct stat st;
400
401                 if (d->d_type != DT_CHR)
402                         continue;
403
404                 rc = fstatat(dir_fd, d->d_name, &st, 0);
405                 if (rc < 0) {
406                         ERROR("cannot stat '%s/%s' while scanning: %s\n",
407                                 dir_path, d->d_name, strerror(errno));
408                         continue;
409                 }
410
411                 if (!S_ISCHR(st.st_mode))
412                         continue;
413
414                 if (major(st.st_rdev) != oal_log_major)
415                         continue;
416
417                 pal = alr_log_lookup(st.st_rdev);
418                 if (pal == NULL) {
419                         ERROR("no device slot available for '%s/%s' with minor %u\n",
420                                 dir_path, d->d_name, minor(st.st_rdev));
421                         continue;
422                 }
423
424                 if (*pal != NULL)
425                         continue; /* We already have this device. */
426
427                 snprintf(path, sizeof(path), "%s/%s", dir_path, d->d_name);
428
429                 alr_log_add(epoll_fd, path);
430         }
431
432         closedir(dir);
433
434         return ALR_OK;
435 }
436
437 /* /dev/lustre-access-log/control device poll callback: call prescan
438  * ioctl and scan /dev/lustre-access-log/ for new access log
439  * devices. */
440 static int alr_ctl_io(int epoll_fd, struct alr_dev *cd, unsigned int mask)
441 {
442         int rc;
443
444         TRACE("%s\n", __func__);
445         DEBUG_U(mask);
446
447         if (mask & EPOLLERR)
448                 return ALR_EXIT_FAILURE;
449
450         if (mask & EPOLLHUP)
451                 return ALR_EXIT_SUCCESS;
452
453         rc = ioctl(cd->alr_fd, LUSTRE_ACCESS_LOG_IOCTL_PRESCAN);
454         if (rc < 0) {
455                 ERROR("cannot start scanning: %s\n", strerror(errno));
456                 return ALR_EXIT_FAILURE;
457         }
458
459         return alr_scan(epoll_fd);
460 }
461
462 /* signalfd epoll callback. Handle SIGINT and SIGTERM by breaking from
463  * the epoll loop and exiting normally.*/
464 static int alr_signal_io(int epoll_fd, struct alr_dev *sd, unsigned int mask)
465 {
466         struct signalfd_siginfo ssi;
467         ssize_t rc;
468
469         TRACE("%s\n", __func__);
470         DEBUG_U(mask);
471
472         rc = read(sd->alr_fd, &ssi, sizeof(ssi));
473         if (rc <= 0)
474                 return ALR_OK;
475
476         DEBUG_U(ssi.ssi_signo);
477         switch (ssi.ssi_signo) {
478         case SIGINT:
479         case SIGTERM:
480                 return ALR_EXIT_SUCCESS;
481         default:
482                 return ALR_OK;
483         }
484 }
485
486 /* batching timerfd epoll callback. Print batched access entries to
487  * alr_batch_file. */
488 static int alr_batch_timer_io(int epoll_fd, struct alr_dev *td, unsigned int mask)
489 {
490         time_t now = time(NULL);
491         uint64_t expire_count;
492         ssize_t rc;
493
494         TRACE("%s\n", __func__);
495         DEBUG_D(now);
496         DEBUG_U(mask);
497
498         rc = read(td->alr_fd, &expire_count, sizeof(expire_count));
499         if (rc <= 0)
500                 return ALR_OK;
501
502         DEBUG_U(expire_count);
503
504         rc = alr_batch_print(alr_batch, alr_batch_file, &alr_batch_file_mutex,
505                              alr_print_fraction);
506         if (rc < 0) {
507                 ERROR("cannot write to '%s': %s\n",
508                         alr_batch_file_path, strerror(errno));
509                 goto out;
510         }
511
512         /* FIXME: blocking write to batch file. */
513         rc = fflush(alr_batch_file);
514         if (rc < 0) {
515                 ERROR("cannot write to '%s': %s\n",
516                         alr_batch_file_path, strerror(errno));
517                 goto out;
518         }
519 out:
520         /* Failed writes will leave alr_batch_file (pipe) in a
521          * weird state so make that fatal. */
522         return (rc < 0) ? ALR_EXIT_FAILURE : ALR_OK;
523 }
524
525 /* Call LUSTRE_ACCESS_LOG_IOCTL_INFO to get access log info and print
526  * YAML formatted info to stdout. */
527 static int alr_log_info(struct alr_log *al)
528 {
529         struct lustre_access_log_info_v1 lali;
530         int rc;
531
532         rc = ioctl(al->alr_dev.alr_fd, LUSTRE_ACCESS_LOG_IOCTL_INFO, &lali);
533         if (rc < 0) {
534                 ERROR("cannot get info for device '%s': %s\n",
535                         al->alr_dev.alr_name, strerror(errno));
536                 return -1;
537         }
538
539         printf("- name: %s\n"
540                "  version: %#x\n"
541                "  type: %#x\n"
542                "  log_size: %u\n"
543                "  entry_size: %u\n"
544                "  _head: %u\n"
545                "  _tail: %u\n"
546                "  _entry_space: %u\n"
547                "  _entry_count: %u\n"
548                "  _drop_count: %u\n"
549                "  _is_closed: %u\n",
550                lali.lali_name,
551                lali.lali_version,
552                lali.lali_type,
553                lali.lali_log_size,
554                lali.lali_entry_size,
555                lali._lali_head,
556                lali._lali_tail,
557                lali._lali_entry_space,
558                lali._lali_entry_count,
559                lali._lali_drop_count,
560                lali._lali_is_closed);
561
562         return 0;
563 }
564
565 static struct alr_dev *alr_dev_create(int epoll_fd, int fd, const char *name,
566                         int (*io)(int, struct alr_dev *, unsigned int),
567                         void (*destroy)(struct alr_dev *))
568 {
569         struct alr_dev *alr;
570         int rc;
571
572         alr = calloc(1, sizeof(*alr));
573         if (alr == NULL)
574                 return NULL;
575
576         alr->alr_name = strdup(name);
577         if (alr->alr_name == NULL) {
578                 free(alr);
579                 return NULL;
580         }
581         alr->alr_io = io;
582         alr->alr_destroy = destroy;
583         alr->alr_fd = fd;
584
585         struct epoll_event event = {
586                 .events = EPOLLIN | EPOLLHUP,
587                 .data.ptr = alr,
588         };
589
590         rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, alr->alr_fd, &event);
591         if (rc < 0) {
592                 free(alr);
593                 return NULL;
594         }
595
596         return alr;
597 }
598
599 void usage(void)
600 {
601         printf("Usage: %s: [OPTION]...\n"
602 "Discover, read, batch, and write Lustre access logs\n"
603 "\n"
604 "Mandatory arguments to long options are mandatory for short options too.\n"
605 "  -f, --batch-file=FILE          print batch to file (default stdout)\n"
606 "  -i, --batch-interval=INTERVAL  print batch every INTERVAL seconds\n"
607 "  -o, --batch-offset=OFFSET      print batch at OFFSET seconds\n"
608 "  -d, --debug[=FILE]             print debug messages to FILE (stderr)\n"
609 "  -h, --help                     display this help and exit\n"
610 "  -l, --list                     print YAML list of available access logs\n"
611 "  -t, --trace[=FILE]             print trace messages to FILE (stderr)\n",
612                 program_invocation_short_name);
613 }
614
615 int main(int argc, char *argv[])
616 {
617         const char ctl_path[] = "/dev/"LUSTRE_ACCESS_LOG_DIR_NAME"/control";
618         struct alr_dev *alr_signal = NULL;
619         struct alr_dev *alr_batch_timer = NULL;
620         struct alr_dev *alr_ctl = NULL;
621         time_t batch_interval = 0;
622         time_t batch_offset = 0;
623         unsigned int m;
624         int list_info = 0;
625         int epoll_fd = -1;
626         int exit_status;
627         int rc;
628         int c;
629
630         static struct option options[] = {
631                 { .name = "batch-file", .has_arg = required_argument, .val = 'f', },
632                 { .name = "batch-interval", .has_arg = required_argument, .val = 'i', },
633                 { .name = "batch-offset", .has_arg = required_argument, .val = 'o', },
634                 { .name = "debug", .has_arg = optional_argument, .val = 'd', },
635                 { .name = "help", .has_arg = no_argument, .val = 'h', },
636                 { .name = "fraction", .has_arg = required_argument, .val = 'F', },
637                 { .name = "list", .has_arg = no_argument, .val = 'l', },
638                 { .name = "trace", .has_arg = optional_argument, .val = 't', },
639                 { .name = NULL, },
640         };
641
642         while ((c = getopt_long(argc, argv, "d::f:F:hi:lt::", options, NULL)) != -1) {
643                 switch (c) {
644                 case 'f':
645                         alr_batch_file_path = optarg;
646                         break;
647                 case 'i':
648                         errno = 0;
649                         batch_interval = strtoll(optarg, NULL, 0);
650                         if (batch_interval < 0 || batch_interval >= 1048576 ||
651                             errno != 0)
652                                 FATAL("invalid batch interval '%s'\n", optarg);
653                         break;
654                 case 'o':
655                         errno = 0;
656                         batch_offset = strtoll(optarg, NULL, 0);
657                         if (batch_offset < 0 || batch_offset >= 1048576 ||
658                             errno != 0)
659                                 FATAL("invalid batch offset '%s'\n", optarg);
660                         break;
661                 case 'd':
662                         if (optarg == NULL) {
663                                 debug_file = stderr;
664                         } else if (strcmp(optarg, "-") == 0) {
665                                 debug_file = stdout;
666                         } else {
667                                 debug_file = fopen(optarg, "a");
668                                 if (debug_file == NULL)
669                                         FATAL("cannot open debug file '%s': %s\n",
670                                                 optarg, strerror(errno));
671                         }
672
673                         break;
674                 case 'h':
675                         usage();
676                         exit(EXIT_SUCCESS);
677                 case 'F':
678                         alr_print_fraction = strtoll(optarg, NULL, 0);
679                         if (alr_print_fraction < 1 || alr_print_fraction > 100)
680                                 FATAL("invalid batch offset '%s'\n", optarg);
681                         break;
682                 case 'l':
683                         list_info = 1;
684                         break;
685                 case 't':
686                         if (optarg == NULL) {
687                                 trace_file = stderr;
688                         } else if (strcmp(optarg, "-") == 0) {
689                                 trace_file = stdout;
690                         } else {
691                                 trace_file = fopen(optarg, "a");
692                                 if (debug_file == NULL)
693                                         FATAL("cannot open debug file '%s': %s\n",
694                                                 optarg, strerror(errno));
695                         }
696
697                         break;
698                 case '?':
699                         fprintf(stderr, "Try '%s --help' for more information.\n",
700                                 program_invocation_short_name);
701                         exit(EXIT_FAILURE);
702                 }
703         }
704
705         if (batch_interval > 0) {
706                 alr_batch = alr_batch_create(-1);
707                 if (alr_batch == NULL)
708                         FATAL("cannot create batch struct: %s\n",
709                                 strerror(errno));
710         }
711
712         if (alr_batch_file_path != NULL) {
713                 alr_batch_file = fopen(alr_batch_file_path, "w");
714                 if (alr_batch_file == NULL)
715                         FATAL("cannot open batch file '%s': %s\n",
716                                 alr_batch_file_path, strerror(errno));
717         } else {
718                 alr_batch_file_path = "stdout";
719                 alr_batch_file = stdout;
720         }
721
722         epoll_fd = epoll_create1(EPOLL_CLOEXEC);
723         if (epoll_fd < 0)
724                 FATAL("cannot create epoll set: %s\n", strerror(errno));
725
726         /* Setup signal FD and add to epoll set. */
727         sigset_t signal_mask;
728         sigemptyset(&signal_mask);
729         sigaddset(&signal_mask, SIGINT);
730         sigaddset(&signal_mask, SIGTERM);
731         rc = sigprocmask(SIG_BLOCK, &signal_mask, NULL);
732         if (rc < 0)
733                 FATAL("cannot set process signal mask: %s\n", strerror(errno));
734
735         int signal_fd = signalfd(-1, &signal_mask, SFD_NONBLOCK|SFD_CLOEXEC);
736         if (signal_fd < 0)
737                 FATAL("cannot create signalfd: %s\n", strerror(errno));
738
739         alr_signal = alr_dev_create(epoll_fd, signal_fd, "signal", &alr_signal_io, NULL);
740         if (alr_signal == NULL)
741                 FATAL("cannot register signalfd: %s\n", strerror(errno));
742
743         signal_fd = -1;
744
745         /* Setup batch timer FD and add to epoll set. */
746         struct timespec now;
747         rc = clock_gettime(CLOCK_REALTIME, &now);
748         if (rc < 0)
749                 FATAL("cannot read realtime clock: %s\n", strerror(errno));
750
751         int timer_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
752         if (timer_fd < 0)
753                 FATAL("cannot create batch timerfd: %s\n", strerror(errno));
754
755         struct itimerspec it = {
756                 .it_value.tv_sec = (batch_interval > 0) ?
757                                    roundup(now.tv_sec, batch_interval) +
758                                    (batch_offset % batch_interval) :
759                                    0,
760                 .it_interval.tv_sec = batch_interval,
761         };
762
763         DEBUG_D(it.it_value.tv_sec);
764
765         rc = timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &it, NULL);
766         if (rc < 0)
767                 FATAL("cannot arm timerfd: %s\n", strerror(errno));
768
769         alr_batch_timer = alr_dev_create(epoll_fd, timer_fd, "batch_timer",
770                                         &alr_batch_timer_io, NULL);
771         if (alr_batch_timer == NULL)
772                 FATAL("cannot register batch timerfd: %s\n", strerror(errno));
773
774         timer_fd = -1;
775
776         /* Open control device. */
777         int ctl_fd = open(ctl_path, O_RDONLY|O_NONBLOCK|O_CLOEXEC);
778         if (ctl_fd < 0)
779                 FATAL("cannot open '%s': %s\n", ctl_path, strerror(errno));
780
781         /* Get and print interface version. */
782         oal_version = ioctl(ctl_fd, LUSTRE_ACCESS_LOG_IOCTL_VERSION);
783         if (oal_version < 0)
784                 FATAL("cannot get ofd access log interface version: %s\n", strerror(errno));
785
786         DEBUG_D(oal_version);
787
788         /* Get and print device major used for access log devices. */
789         oal_log_major = ioctl(ctl_fd, LUSTRE_ACCESS_LOG_IOCTL_MAJOR);
790         if (oal_log_major < 0)
791                 FATAL("cannot get ofd access log major: %s\n", strerror(errno));
792
793         DEBUG_D(oal_log_major);
794
795         /* Add control device to epoll set. */
796         alr_ctl = alr_dev_create(epoll_fd, ctl_fd, "control", &alr_ctl_io, NULL);
797         if (alr_ctl == NULL)
798                 FATAL("cannot register control device: %s\n", strerror(errno));
799
800         ctl_fd = -1;
801
802         do {
803                 struct epoll_event ev[32];
804                 int timeout = (list_info ? 0 : -1);
805                 int i, ev_count;
806
807                 ev_count = epoll_wait(epoll_fd, ev, ARRAY_SIZE(ev), timeout);
808                 if (ev_count < 0) {
809                         if (errno == EINTR) /* Signal or timeout. */
810                                 continue;
811
812                         ERROR("cannot wait on epoll set: %s\n", strerror(errno));
813                         exit_status = EXIT_FAILURE;
814                         goto out;
815                 }
816
817                 DEBUG_D(ev_count);
818
819                 for (i = 0; i < ev_count; i++) {
820                         struct alr_dev *ad = ev[i].data.ptr;
821                         unsigned int mask = ev[i].events;
822
823                         rc = (*ad->alr_io)(epoll_fd, ad, mask);
824                         switch (rc) {
825                         case ALR_EXIT_FAILURE:
826                                 exit_status = EXIT_FAILURE;
827                                 goto out;
828                         case ALR_EXIT_SUCCESS:
829                                 exit_status = EXIT_SUCCESS;
830                                 goto out;
831                         case ALR_ERROR:
832                         case ALR_EOF:
833                                 alr_dev_free(epoll_fd, ad);
834                                 break;
835                         case ALR_OK:
836                         default:
837                                 break;
838                         }
839                 }
840         } while (!list_info);
841
842         exit_status = EXIT_SUCCESS;
843 out:
844         assert(oal_log_minor_max < ARRAY_SIZE(alr_log));
845
846         for (m = 0; m <= oal_log_minor_max; m++) {
847                 if (alr_log[m] == NULL)
848                         continue;
849
850                 if (list_info) {
851                         rc = alr_log_info(alr_log[m]);
852                         if (rc < 0)
853                                 exit_status = EXIT_FAILURE;
854                 }
855
856                 alr_dev_free(epoll_fd, &alr_log[m]->alr_dev);
857         }
858
859         alr_dev_free(epoll_fd, alr_ctl);
860         alr_dev_free(epoll_fd, alr_signal);
861         alr_dev_free(epoll_fd, alr_batch_timer);
862         close(epoll_fd);
863
864         alr_batch_destroy(alr_batch);
865
866         DEBUG_D(exit_status);
867
868         return exit_status;
869 }