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