+++ /dev/null
-/*
- * Copyright (c) 2018 DDN Storage, Inc
- *
- * Author: Sebastien Buisson sbuisson@ddn.com
- */
-
-/*
- * lustre/utils/laudit-report.c
- * Report tool for audit.
- */
-#include <getopt.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <time.h>
-#include <dirent.h>
-
-#include <json-c/json.h>
-
-#include <lustre/lustreapi.h>
-
-#ifndef ARRAY_SIZE
-# define ARRAY_SIZE(a) ((sizeof(a)) / (sizeof((a)[0])))
-#endif /* !ARRAY_SIZE */
-
-#define MDD_NAME_LEN 16
-
-struct mdt_desc {
- char reader_id[8];
- char mdd_name[MDD_NAME_LEN + 1];
-};
-
-#define MAX_MDTS 256
-struct audit_config {
- char fs_name[9];
- char mount[PATH_MAX + 1];
- char dump_path[PATH_MAX + 1];
- int parse_interval_sec;
- int sync_every_n_entries;
- int max_syncs_before_sleep;
- int nb_mdts;
- struct mdt_desc mdts[MAX_MDTS];
-};
-
-static const char *progname;
-static struct audit_config cfg = { .mdts = { { .reader_id = "" } } };
-static bool config_init;
-static int verbose;
-static char config_file[PATH_MAX + 1];
-
-static unsigned int dir_level;
-static char *file_to_audit, *user_to_audit;
-static struct tm ts_before, ts_after;
-static bool check_before, check_after;
-
-void laudit_report_usage(void)
-{
- printf("usage: %s [options] <config file>\n\n", progname);
- printf("\toptions:\n");
- printf("\t\t-f|--file <file>: build report for given file\n");
- printf("\t\t\tYou can provide file path or FID, and also simple");
- printf("file or directory name.\n");
- printf("\t\t-u|--user <uid:gid>: build report for given user\n");
- printf("\t\t-b|--before <time>: only consider events occurring ");
- printf("before this time, included.\n");
- printf("\t\t-a|--after <time>: only consider events occurring ");
- printf("after this time, included.\n");
- printf("\t\t\tTime expected in the form 'yyyy.mm.dd HH:MM'.\n");
- printf("\t\t-v|--verbose: output some debug info; repeat for ");
- printf("more details.\n");
-}
-
-void printerr(char *format, ...)
-{
- va_list args;
- char *fullpath = getcwd(NULL, 0);
-
- va_start(args, format);
-
- fprintf(stderr, "%s in %s: ", progname, fullpath);
- vfprintf(stderr, format, args);
-
- va_end(args);
- free(fullpath);
-}
-
-void printinfo(char *format, ...)
-{
- va_list args;
-
- va_start(args, format);
-
- vfprintf(stdout, format, args);
-
- va_end(args);
-}
-
-/* config file is in JSON format, of the form:
- * {
- * "fs_name": "testfs",
- * "mount": "/mnt/lustre/testfs",
- * "dump_path": "/mnt/audit1",
- * "mdts": [
- * {"mdt_idx": "0", "reader_id": "cl1"},
- * {"mdt_idx": "1", "reader_id": "cl2"}
- * ],
- * "parse_interval_sec": 60,
- * "sync_every_n_entries": 500,
- * "max_syncs_before_sleep": 10
- * }
- */
-static int get_config(char *cfg_file)
-{
- struct json_object *root, *fs_name, *mount, *dump_path, *mdts;
- struct json_object *parse_interval_sec, *sync_every_n_entries;
- struct json_object *max_syncs_before_sleep;
- int i;
- int rc = -EINVAL;
-
- root = json_object_from_file(cfg_file);
- if (!root) {
- fprintf(stderr,
- "%s config error: %s is not in valid JSON format\n",
- progname, cfg_file);
- return -EINVAL;
- }
-
- rc = json_object_object_get_ex(root, "fs_name", &fs_name);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have a fs_name entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(fs_name, json_type_string)) {
- fprintf(stderr,
- "%s config error: fs_name is not a string\n",
- progname);
- goto out;
- }
- if (snprintf(cfg.fs_name, sizeof(cfg.fs_name), "%s",
- json_object_get_string(fs_name)) >= sizeof(cfg.fs_name)) {
- fprintf(stderr,
- "%s config error: fs_name is too long\n",
- progname);
- goto out;
- }
-
- rc = json_object_object_get_ex(root, "mount", &mount);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have a mount entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(mount, json_type_string)) {
- fprintf(stderr,
- "%s config error: mount is not a string\n",
- progname);
- goto out;
- }
- if (snprintf(cfg.mount, sizeof(cfg.mount), "%s",
- json_object_get_string(mount)) >= sizeof(cfg.mount)) {
- fprintf(stderr,
- "%s config error: mount is too long\n",
- progname);
- goto out;
- }
-
- rc = json_object_object_get_ex(root, "dump_path", &dump_path);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have a dump_path entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(dump_path, json_type_string)) {
- fprintf(stderr,
- "%s config error: dump_path is not a string\n",
- progname);
- goto out;
- }
- if (snprintf(cfg.dump_path, sizeof(cfg.dump_path), "%s",
- json_object_get_string(dump_path))
- >= sizeof(cfg.dump_path)) {
- fprintf(stderr,
- "%s config error: dump_path is too long\n",
- progname);
- goto out;
- }
-
- rc = json_object_object_get_ex(root, "mdts", &mdts);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have an mdts entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(mdts, json_type_array)) {
- fprintf(stderr,
- "%s config error: mdts is not an array\n",
- progname);
- goto out;
- }
-
- cfg.nb_mdts = json_object_array_length(mdts);
- if (cfg.nb_mdts > ARRAY_SIZE(cfg.mdts)) {
- fprintf(stderr,
- "%s error: mdts array is larger than %lu\n",
- progname,
- ARRAY_SIZE(cfg.mdts));
- goto out;
- }
-
- for (i = 0; i < cfg.nb_mdts; i++) {
- struct json_object *data, *mdt_idx, *reader_id;
- int mdt_id;
-
- data = json_object_array_get_idx(mdts, i);
- if (!data) {
- fprintf(stderr,
- "%s config error: mdt %d is not an object\n",
- progname, i);
- goto out;
- }
-
- rc = json_object_object_get_ex(data, "mdt_idx", &mdt_idx);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have an mdt_idx entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(mdt_idx, json_type_int)) {
- fprintf(stderr,
- "%s config error: mdt_idx is not an int\n",
- progname);
- goto out;
- }
- mdt_id = json_object_get_int(mdt_idx);
- if (mdt_id < 0 || mdt_id >= MAX_MDTS) {
- fprintf(stderr,
- "%s config error: mdt_idx %d out of range\n",
- progname, mdt_id);
- goto out;
- }
-
- rc = json_object_object_get_ex(data, "reader_id", &reader_id);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have a reader_id entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(reader_id, json_type_string)) {
- fprintf(stderr,
- "%s config error: reader_id is not a string\n",
- progname);
- goto out;
- }
- if (cfg.mdts[mdt_id].reader_id[0] != '\0') {
- fprintf(stderr,
- "%s config error: duplicate mdt index %d\n",
- progname, mdt_id);
- goto out;
- }
- if (snprintf(cfg.mdts[mdt_id].reader_id,
- sizeof(cfg.mdts[mdt_id].reader_id), "%s",
- json_object_get_string(reader_id)) >=
- sizeof(cfg.mdts[mdt_id].reader_id)) {
- fprintf(stderr,
- "%s config error: reader_id is too long\n",
- progname);
- goto out;
- }
- }
-
- rc = json_object_object_get_ex(root, "parse_interval_sec",
- &parse_interval_sec);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have a parse_interval_sec entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(parse_interval_sec, json_type_int)) {
- fprintf(stderr,
- "%s config error: parse_interval_sec is not an int\n",
- progname);
- goto out;
- }
- cfg.parse_interval_sec = json_object_get_int(parse_interval_sec);
-
- rc = json_object_object_get_ex(root, "sync_every_n_entries",
- &sync_every_n_entries);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have a sync_every_n_entries entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(sync_every_n_entries, json_type_int)) {
- fprintf(stderr,
- "%s config error: sync_every_n_entries is not an int\n",
- progname);
- goto out;
- }
- cfg.sync_every_n_entries = json_object_get_int(sync_every_n_entries);
-
- rc = json_object_object_get_ex(root, "max_syncs_before_sleep",
- &max_syncs_before_sleep);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have a max_syncs_before_sleep entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(max_syncs_before_sleep, json_type_int)) {
- fprintf(stderr,
- "%s config error: max_syncs_before_sleep is not an int\n",
- progname);
- goto out;
- }
-
- cfg.max_syncs_before_sleep =
- json_object_get_int(max_syncs_before_sleep);
-
- rc = 0;
-out:
- json_object_put(root);
- return rc;
-}
-
-static void dump_config(void)
-{
- int idx;
-
- if (!config_init)
- return;
-
- printf("FS name: %s\n", cfg.fs_name);
- printf("Mount path: %s\n", cfg.mount);
- printf("Dump path: %s\n", cfg.dump_path);
-
- for (idx = 0; idx < MAX_MDTS; idx++) {
- if (cfg.mdts[idx].reader_id[0] == '\0')
- continue;
- printf("MDT index: %d, reader ID %s\n",
- idx, cfg.mdts[idx].reader_id);
- }
-
- printf("Parse interval: %d\n", cfg.parse_interval_sec);
- printf("Sync: every %d entries\n", cfg.sync_every_n_entries);
- printf("Max syncs before sleep: %d\n", cfg.max_syncs_before_sleep);
-}
-
-static int sanity_config(void)
-{
- struct stat s;
- char path[PATH_MAX + 1];
- int rc;
-
- if (!config_init)
- return -1;
-
- /* check dump_path */
- rc = stat(cfg.dump_path, &s);
- if (rc == -1) {
- fprintf(stderr, "%s: cannot stat %s: %s\n", progname,
- cfg.dump_path, strerror(errno));
- return rc;
- }
- if (!S_ISDIR(s.st_mode)) {
- fprintf(stderr, "%s: dump path %s: %s\n",
- progname, cfg.dump_path, strerror(errno = ENOTDIR));
- return -1;
- }
-
- /* create /files and /users directories if necessary */
- rc = snprintf(path, sizeof(path), "%s/files", cfg.dump_path);
- if (rc >= sizeof(path))
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
- rc = mkdir(path, 0700);
- if (rc && errno != EEXIST) {
- fprintf(stderr, "%s cannot mkdir %s: %s\n", progname,
- path, strerror(errno));
- return rc;
- }
- rc = snprintf(path, sizeof(path), "%s/users", cfg.dump_path);
- if (rc >= sizeof(path))
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
- rc = mkdir(path, 0700);
- if (rc && errno != EEXIST) {
- fprintf(stderr, "%s cannot mkdir %s: %s\n", progname,
- path, strerror(errno));
- return rc;
- }
-
- /* check parse_interval_sec */
- if (cfg.parse_interval_sec <= 0) {
- fprintf(stderr, "%s: parse_interval_sec %d invalid\n",
- progname, cfg.parse_interval_sec);
- return -1;
- }
-
- /* sync_every_n_entries */
- if (cfg.sync_every_n_entries <= 0) {
- fprintf(stderr, "%s: sync_every_n_entries %d invalid\n",
- progname, cfg.sync_every_n_entries);
- return -1;
- }
-
- /* max_syncs_before_sleep */
- if (cfg.max_syncs_before_sleep <= 0) {
- fprintf(stderr, "%s: max_syncs_before_sleep %d invalid\n",
- progname, cfg.max_syncs_before_sleep);
- return -1;
- }
-
- return 0;
-}
-
-/* look for requested uid/gid in buffer */
-int filter_write(char *file_path, char *buf, size_t *buf_sz, char *uidgid)
-{
- char *nl = NULL, *user = NULL, *remainder = NULL;
- char *ptr = buf;
- size_t len;
- int rc;
-
- /* first, find end of line */
- while ((nl = memchr(ptr, '\n', *buf_sz)) != NULL) {
- /* then, find u=uid:gid */
- *nl = '\0';
- user = strstr(ptr, "u=");
- if (user == NULL) {
- printerr("malformed audit log in %s\n", file_path);
- return -EINVAL;
- }
- remainder = strchr(user + 2, ' ');
- if (remainder == NULL) {
- printerr("malformed audit log in %s\n", file_path);
- return -EINVAL;
- }
- *remainder = '\0';
- if (strcmp(user + 2, uidgid) == 0) {
- /* matches requested user, so dump entry
- * without uidgid info
- */
- *nl = '\n';
- len = user - ptr;
- rc = write(STDOUT_FILENO, ptr, len);
- if (rc != len) {
- printerr("cannot print %s content: %s\n",
- file_path, strerror(errno));
- return -errno;
- }
- len = nl - remainder;
- rc = write(STDOUT_FILENO, remainder + 1, len);
- if (rc != len) {
- printerr("cannot print %s content: %s\n",
- file_path, strerror(errno));
- return -errno;
- }
- }
- *buf_sz -= nl - ptr + 1;
- ptr = nl + 1;
- }
-
- /* copy end of buffer into buf for future use */
- if (*buf_sz != 0)
- memcpy(buf, ptr, *buf_sz);
-
- return 0;
-}
-
-int cat_file(char *file_path, char *uidgid)
-{
- int fd;
- char buf[4096];
- size_t read_sz = 0;
- int rc = 0;
-
- fd = open(file_path, O_RDONLY);
- if (fd == -1) {
- printerr("cannot open %s: %s\n", file_path, strerror(errno));
- return -1;
- }
-
- posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
-
- while (true) {
- rc = read(fd, buf + read_sz, sizeof(buf) - read_sz);
- if (rc == -1) {
- printerr("cannot read from file %s: %s\n",
- file_path, strerror(errno));
- rc = -errno;
- break;
- }
-
- if (rc == 0)
- /* end of file */
- break;
-
- read_sz += rc;
-
- if (uidgid) {
- /* filter based on uid/gid */
- rc = filter_write(file_path, buf, &read_sz, uidgid);
- if (rc)
- break;
- } else {
- /* bulk write */
- rc = write(STDOUT_FILENO, buf, read_sz);
- if (rc != read_sz) {
- printerr("cannot print %s content: %s\n",
- file_path, strerror(errno));
- rc = -errno;
- break;
- }
- read_sz = 0;
- }
- }
-
- close(fd);
- return rc;
-}
-
-int filter_out_files_for_time(const struct dirent *de)
-{
- int n, ret;
-
- ret = sscanf(de->d_name, "%02d", &n);
- if (ret < 0)
- return 0;
- return ret;
-}
-
-int browse_dir_depth_first(char *dir, char *uidgid)
-{
- struct dirent **namelist;
- struct stat s;
- int n, idx = 0;
- bool check_before_orig = check_before;
- bool check_after_orig = check_after;
- int rc = 0;
-
- if (chdir(dir)) {
- printerr("cannot chdir to %s: %s\n", dir, strerror(errno));
- rc = -errno;
- return rc;
- }
-
- n = scandir(".", &namelist, filter_out_files_for_time, alphasort);
- if (n < 0) {
- printerr("cannot scan dir %s: %s\n", dir, strerror(errno));
- rc = -errno;
- return rc;
- }
-
- dir_level++;
-
- for (idx = 0; idx < n; idx++) {
- /* In case of error, need to go through whole loop
- * to free memory.
- */
- if (rc)
- goto loop_free;
-
- rc = stat(namelist[idx]->d_name, &s);
- if (rc == -1) {
- printerr("cannot stat %s: %s\n", namelist[idx]->d_name,
- strerror(errno));
- rc = -errno;
- goto loop_free;
- }
-
- if (S_ISDIR(s.st_mode)) {
- if (check_before || check_after) {
- int val, val_before, val_after;
-
- val = atoi(namelist[idx]->d_name);
- switch (dir_level) {
- case 1:
- val_before = ts_before.tm_year + 1900;
- val_after = ts_after.tm_year + 1900;
- break;
- case 2:
- val_before = ts_before.tm_mon + 1;
- val_after = ts_after.tm_mon + 1;
- break;
- case 3:
- val_before = ts_before.tm_mday;
- val_after = ts_after.tm_mday;
- break;
- case 4:
- val_before = ts_before.tm_hour * 100 +
- ts_before.tm_min;
- val_after = ts_after.tm_hour * 100 +
- ts_after.tm_min;
- break;
- default:
- /* unknown dir level */
- printerr("unknown dir lvl %d for %s\n",
- dir_level,
- namelist[idx]->d_name);
- rc = -EINVAL;
- goto loop_free;
- }
-
- if (check_before) {
- if (val < val_before)
- /* time constraint is validated,
- * no need to check sub levels
- */
- check_before = false;
- else if (val > val_before)
- /* time does not fit constraint,
- * skip
- */
- goto loop_free;
- }
- if (check_after) {
- if (val > val_after)
- /* time constraint is validated,
- * no need to check sub levels
- */
- check_after = false;
- else if (val < val_after)
- /* time does not fit constraint,
- * skip
- */
- goto loop_free;
- }
- }
-
- /* recursively call browse_dir */
- rc = browse_dir_depth_first(namelist[idx]->d_name,
- uidgid);
- } else if (S_ISREG(s.st_mode)) {
- /* display content of file */
- rc = cat_file(namelist[idx]->d_name, uidgid);
- }
-
-loop_free:
- /* restore original check values */
- check_before = check_before_orig;
- check_after = check_after_orig;
-
- free(namelist[idx]);
- }
- free(namelist);
-
- if (!rc && chdir("..")) {
- printerr("cannot chdir to .. : %s\n", strerror(errno));
- rc = -errno;
- }
-
- dir_level--;
- return rc;
-}
-
-static int get_audit_for_file(char *fid, char *uidgid)
-{
- char main_dir[PATH_MAX + 1];
- struct stat s;
- int rc;
-
- /* directory structure for easy file history:
- * files/$FID/$YEAR/$MONTH/$DAY/$HOUR$MIN/$SECS
- */
- rc = snprintf(main_dir, sizeof(main_dir), "%s/files/%s",
- cfg.dump_path, fid);
- if (rc >= sizeof(main_dir))
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
-
- rc = stat(main_dir, &s);
- if (rc == -1) {
- fprintf(stderr, "%s: file %s does not exist in history\n",
- progname, fid);
- rc = -errno;
- return rc;
- }
-
- /* directory structure is like:
- * main_dir/$YEAR/$MONTH/$DAY/$HOUR$MIN/$SECS
- */
- printinfo("\nHistory of file %s", fid);
- if (uidgid)
- printinfo(" by user %s:\n", uidgid);
- else
- printinfo(":\n");
- return browse_dir_depth_first(main_dir, uidgid);
-}
-
-static int get_audit_for_user(char *uidgid)
-{
- char main_dir[PATH_MAX + 1];
- struct stat s;
- int rc;
-
- /* directory structure for easy file history:
- * files/$FID/$YEAR/$MONTH/$DAY/$HOUR$MIN/$SECS
- */
- rc = snprintf(main_dir, sizeof(main_dir), "%s/users/%s",
- cfg.dump_path, uidgid);
- if (rc >= sizeof(main_dir))
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
-
- rc = stat(main_dir, &s);
- if (rc == -1) {
- fprintf(stderr, "%s: user %s does not exist in history\n",
- progname, uidgid);
- rc = -errno;
- return rc;
- }
-
- /* directory structure is like:
- * main_dir/$YEAR/$MONTH/$DAY/$HOUR$MIN/$SECS
- */
- printinfo("\nHistory of user %s:\n", uidgid);
- return browse_dir_depth_first(main_dir, NULL);
-}
-
-int browse_files_fid(char *file_to_audit, bool use_as_basename)
-{
- char main_dir[PATH_MAX + 1];
- DIR *dp;
- struct dirent *ep;
- bool matches = false;
- FILE *fp;
- char *line = NULL;
- size_t len = 0;
- ssize_t read;
- int rc = 0;
-
- if (use_as_basename)
- printinfo("Possible matches:\n");
-
- rc = snprintf(main_dir, sizeof(main_dir), "%s/files", cfg.dump_path);
- if (rc >= sizeof(main_dir))
- rc = -ENAMETOOLONG;
- if (rc < 0) {
- printerr("error with snprintf: %d\n", strerror(-rc));
- return rc;
- }
- rc = 0;
-
- if (chdir(main_dir)) {
- printerr("cannot chdir to %s: %s\n", main_dir, strerror(errno));
- rc = errno;
- return rc;
- }
-
- dp = opendir(".");
- if (dp == NULL) {
- printerr("cannot open dir %s: %s\n", main_dir, strerror(errno));
- rc = errno;
- return rc;
- }
-
- while ((ep = readdir(dp)) != NULL) {
- if (strcmp(ep->d_name, ".") == 0 ||
- strcmp(ep->d_name, "..") == 0)
- continue;
-
- if (chdir(ep->d_name))
- continue;
-
- fp = fopen("paths", "r");
- if (fp == NULL) {
- chdir(main_dir);
- continue;
- }
-
- while ((read = getline(&line, &len, fp)) != -1) {
- if (line[read - 1] == '\n')
- line[read - 1] = '\0';
- if (strlen(line) == 0)
- continue;
-
- if (use_as_basename) {
- char *base = basename(line);
-
- if (strcmp(file_to_audit, base) == 0) {
- matches = true;
- printinfo("\t%s %s/%s\n", ep->d_name,
- cfg.mount, line);
- break;
- }
- } else {
- if (strcmp(line, file_to_audit +
- strlen(cfg.mount) + 1) == 0) {
- rc = get_audit_for_file(ep->d_name,
- user_to_audit);
- break;
- }
- }
- }
-
- fclose(fp);
- if (line) {
- free(line);
- line = NULL;
- }
-
- chdir(main_dir);
- }
- closedir(dp);
-
- if (use_as_basename && !matches)
- printinfo("\tNone\n");
-
- return rc;
-}
-
-/* need to resolve path to fid */
-/* strategy:
- * 1. resolve using path2fid: works for current path of
- * existing file
- * 2. look into 'paths' files under files/$FID/ : works
- * for path at create or mv or rm time
- * 3. look into 'paths' files for basename of file to find,
- * and display a list of possibilities with full path and FID
- */
-int find_fid_and_get_audit(char *file_to_audit, char *user_to_audit)
-{
- struct lu_fid fid;
- char fid_to_audit[PATH_MAX];
- char main_dir[PATH_MAX + 1];
- int rc = 0;
-
- rc = snprintf(main_dir, sizeof(main_dir), "%s/", cfg.mount);
- if (rc >= sizeof(main_dir))
- rc = -ENAMETOOLONG;
- if (rc < 0) {
- printerr("error with snprintf: %d\n", strerror(-rc));
- goto out_audit;
- }
-
- if (strlen(file_to_audit) >= strlen(main_dir) &&
- strncmp(file_to_audit, main_dir, strlen(main_dir)) == 0) {
- /* 1. resolve using path2fid */
- rc = llapi_path2fid(file_to_audit, &fid);
- if (rc == 0) {
- snprintf(fid_to_audit, sizeof(fid_to_audit), DFID,
- PFID(&fid));
- rc = get_audit_for_file(fid_to_audit, user_to_audit);
- if (rc)
- goto out_audit;
- }
-
- /* 2. look into 'paths' files under files/$FID/ */
- rc = browse_files_fid(file_to_audit, false);
- } else {
- if (file_to_audit[0] == '/') {
- printerr("Invalid parameter for '--file' option: %s\n",
- file_to_audit);
- rc = EINVAL;
- } else {
- /* 3. look into basename of 'paths' files for file to
- * find, and display a list of possibilities with
- * full path and FID
- */
- /* Consider file_to_audit as a filename or dirname
- * without path
- */
- rc = browse_files_fid(file_to_audit, true);
- }
- }
-
-out_audit:
- return rc;
-}
-
-int get_opts(int argc, char *const argv[])
-{
- static struct option long_opts[] = {
- { .val = 'f', .name = "file", .has_arg = required_argument},
- { .val = 'u', .name = "user", .has_arg = required_argument},
- { .val = 'b', .name = "before", .has_arg = required_argument},
- { .val = 'a', .name = "after", .has_arg = required_argument},
- { .val = 'v', .name = "verbose", .has_arg = no_argument},
- { .name = NULL } };
- char *short_opts = "f:u:b:a:v";
- int opt;
- int longidx;
- unsigned int uid, gid;
- char *time_before = NULL, *time_after = NULL;
-
- optind = 0;
- while ((opt = getopt_long(argc, argv, short_opts, long_opts,
- &longidx)) != EOF) {
- switch (opt) {
- case 'f':
- file_to_audit = optarg;
- break;
- case 'u':
- user_to_audit = optarg;
- break;
- case 'b':
- time_before = optarg;
- break;
- case 'a':
- time_after = optarg;
- break;
- case 'v':
- verbose++;
- break;
- default:
- if (opt != '?')
- fprintf(stderr, "Unknown option '%c'\n", opt);
- return -EINVAL;
- }
- }
-
- if (optind == argc ||
- (file_to_audit == NULL && user_to_audit == NULL)) {
- /* The user didn't specify config file */
- fprintf(stderr, "Not enough arguments\n");
- return -EINVAL;
- }
-
- /* The config file */
- strncpy(config_file, argv[argc - 1], sizeof(config_file) - 1);
- config_file[sizeof(config_file) - 1] = '\0';
-
- if (user_to_audit && sscanf(user_to_audit, "%u:%u", &uid, &gid) != 2) {
- /* incorrect uid:gid provided */
- fprintf(stderr, "Incorrect uid:gid provided\n");
- return -EINVAL;
- }
-
- if (time_before) {
- if (!strptime(time_before, "%Y.%m.%d %H:%M", &ts_before)) {
- /* incorrect 'before time' provided */
- fprintf(stderr, "Incorrect 'before time' provided\n");
- return -EINVAL;
- }
- check_before = true;
- }
-
- if (time_after) {
- if (!strptime(time_after, "%Y.%m.%d %H:%M", &ts_after)) {
- /* incorrect 'after time' provided */
- fprintf(stderr, "Incorrect 'after time' provided\n");
- return -EINVAL;
- }
- check_after = true;
- }
-
- if (time_before && time_after &&
- mktime(&ts_before) < mktime(&ts_after)) {
- /* incorrect time range provided */
- fprintf(stderr, "Incorrect time range provided\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-int main(int argc, char **argv)
-{
- int idx;
- int rc;
-
- progname = argv[0]; /* Used in error messages */
-
- if (argc < 3 || argv[argc - 1][0] == '-') {
- fprintf(stderr, "%s, missing options\n", argv[0]);
- laudit_report_usage();
- return -EINVAL;
- }
-
- setlinebuf(stdout);
-
- rc = get_opts(argc, argv);
- if (rc) {
- laudit_report_usage();
- goto out_main;
- }
-
- rc = get_config(config_file);
- if (rc)
- goto out_main;
- config_init = true;
-
- if (verbose)
- dump_config();
-
- rc = sanity_config();
- if (rc)
- goto out_main;
-
- for (idx = 0; idx < MAX_MDTS; idx++) {
- if (cfg.mdts[idx].reader_id[0] == '\0')
- continue;
- rc = snprintf(cfg.mdts[idx].mdd_name, MDD_NAME_LEN + 1,
- "%s-MDT%04x", cfg.fs_name, idx);
- if (rc >= MDD_NAME_LEN + 1) {
- rc = EINVAL;
- break;
- }
- if (rc < 0)
- break;
- }
-
- if (file_to_audit) {
- if (*file_to_audit == '[') {
- struct lu_fid fid;
-
- rc = sscanf(file_to_audit, "["SFID"]", RFID(&fid));
- if (rc != 3) {
- printerr("'%s' is not a valid FID\n",
- file_to_audit);
- rc = EINVAL;
- } else {
- rc = get_audit_for_file(file_to_audit,
- user_to_audit);
- }
- } else {
- rc = find_fid_and_get_audit(file_to_audit,
- user_to_audit);
- }
- } else if (user_to_audit) {
- rc = get_audit_for_user(user_to_audit);
- }
-
-out_main:
- return rc < 0 ? -rc : rc;
-}
+++ /dev/null
-/*
- * Copyright (c) 2018 DDN Storage, Inc
- *
- * Author: Sebastien Buisson sbuisson@ddn.com
- */
-
-/*
- * lustre/utils/laudit.c
- * Changelogs consumer for audit.
- */
-#include <getopt.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <time.h>
-#include <signal.h>
-#include <syslog.h>
-
-#include <json-c/json.h>
-
-#include <lustre/lustreapi.h>
-#ifdef HAVE_LNET_NIDSTR
-#include <lnet/nidstr.h>
-#elif defined(HAVE_LINUX_NIDSTR)
-#include <linux/lnet/nidstr.h>
-#endif
-
-#ifndef ARRAY_SIZE
-# define ARRAY_SIZE(a) ((sizeof(a)) / (sizeof((a)[0])))
-#endif /* !ARRAY_SIZE */
-
-#define MDD_NAME_LEN 16
-
-/* If we have include under $includedir/lnet, it means we are building against
- * Lustre 2.10 or earlier, so MDS_FMODE_{READ,WRITE} is not defined.
- */
-#ifdef HAVE_LNET_NIDSTR
-#define MDS_FMODE_READ FMODE_READ
-#define MDS_FMODE_WRITE FMODE_WRITE
-#endif
-
-struct mdt_desc {
- char reader_id[8];
- char mdd_name[MDD_NAME_LEN + 1];
-};
-
-#define MAX_MDTS 256
-struct audit_config {
- char fs_name[9];
- char mount[PATH_MAX + 1];
- char dump_path[PATH_MAX + 1];
- int parse_interval_sec;
- int sync_every_n_entries;
- int max_syncs_before_sleep;
- int nb_mdts;
- struct mdt_desc mdts[MAX_MDTS];
-};
-
-static const char *progname;
-static struct audit_config cfg = { .mdts = { { .reader_id = "" } } };
-static bool config_init;
-static int verbose;
-static char config_file[PATH_MAX + 1];
-static int daemon_mode;
-static bool path2fid;
-/* __atomic_always_lock_free returns true for bool */
-static bool stop_now;
-static bool warn_possible_fid2path_error;
-
-void laudit_usage(void)
-{
- printf("usage: %s [options] <config file>\n\n", progname);
- printf("\toptions:\n");
- printf("\t\t-d|--daemon: launch in daemon mode\n");
- printf("\t\t\tIf specified, parse Changelogs regularly in the ");
- printf("background,\n\t\t\taccording to the interval specified ");
- printf("in the config file.\n");
- printf("\t\t\tIf not specified, just parse Changelogs once in ");
- printf("the foreground.\n");
- printf("\t\t-p|--path: build path-to-fid info\n");
- printf("\t\t\tIf specified, resolve FID to path when parsing ");
- printf("Changelogs,\n\t\t\tand dump info along with entry.\n");
- printf("\t\t-v|--verbose: output some debug info; repeat for ");
- printf("more details.\n");
-}
-
-void printerr(char *format, ...)
-{
- va_list args;
-
- va_start(args, format);
-
- if (daemon_mode)
- vsyslog(LOG_ERR, format, args);
- else
- vfprintf(stderr, format, args);
-
- va_end(args);
-}
-
-void printinfo(char *format, ...)
-{
- va_list args;
-
- va_start(args, format);
-
- if (daemon_mode)
- vsyslog(LOG_INFO, format, args);
- else
- vfprintf(stdout, format, args);
-
- va_end(args);
-}
-
-int sync_audit(void)
-{
- int rc = 0;
- int root = open(cfg.dump_path, O_RDONLY);
-
- if (root == -1) {
- printerr("%s cannot open %s for sync: %s\n",
- progname, cfg.dump_path, strerror(errno));
- return -errno;
- }
-
- if (syncfs(root) < 0) {
- printerr("%s cannot sync %s: %s\n",
- progname, cfg.dump_path, strerror(errno));
- rc = -errno;
- }
-
- close(root);
- return rc;
-}
-
-int sync_n_clear(int idx, __u64 last_cr_index)
-{
- int rc;
-
- /* sanity check */
- if (cfg.mdts[idx].reader_id[0] == '\0')
- return 0;
-
- /* sync audit dump device */
- rc = sync_audit();
- if (rc) {
- printerr("%s error: cannot sync %s\n",
- progname, cfg.dump_path);
- return rc;
- }
-
- /* clear entry */
- rc = llapi_changelog_clear(cfg.mdts[idx].mdd_name,
- cfg.mdts[idx].reader_id,
- last_cr_index);
- if (rc == -EINVAL)
- printerr("%s: %s: record out of range: %u\n",
- progname, cfg.mdts[idx].mdd_name, 0);
- else if (rc == -ENOENT)
- printerr("%s: %s: no changelog user: %s\n",
- progname, cfg.mdts[idx].mdd_name,
- cfg.mdts[idx].reader_id);
- else if (rc)
- printerr("%s error: %s: %s\n", progname,
- cfg.mdts[idx].mdd_name, strerror(-rc));
-
- if (verbose)
- printinfo("%s cleared record %d on %s\n", progname,
- last_cr_index, cfg.mdts[idx].mdd_name);
- return rc;
-}
-
-void sig_ignore(int signal)
-{
- /* don't exit on signal */
- printinfo("%s %s received signal %d... Ignoring.\n", progname,
- (signal == SIGINT || signal == SIGTERM) ? "child" : "",
- signal);
-}
-
-void sig_stop_parsing(int signal)
-{
- /* just indicate we have to stop */
- printinfo("%s received signal %d, will stop after current entry is processed...\n",
- progname, signal);
-
- stop_now = true;
-}
-
-/* config file is in JSON format, of the form:
- * {
- * "fs_name": "mylustre",
- * "mount": "/mnt/lustre/testfs",
- * "dump_path": "/mnt/audit1",
- * "mdts": [
- * {"mdt_idx": "0", "reader_id": "cl1"},
- * {"mdt_idx": "1", "reader_id": "cl2"}
- * ],
- * "parse_interval_sec": 60,
- * "sync_every_n_entries": 500,
- * "max_syncs_before_sleep": 10
- * }
- */
-static int get_config(char *cfg_file)
-{
- struct json_object *root, *fs_name, *mount, *dump_path, *mdts;
- struct json_object *parse_interval_sec, *sync_every_n_entries;
- struct json_object *max_syncs_before_sleep;
- int i;
- int rc = -EINVAL;
-
- root = json_object_from_file(cfg_file);
- if (!root) {
- fprintf(stderr,
- "%s config error: %s is not in valid JSON format\n",
- progname, cfg_file);
- return -EINVAL;
- }
-
- rc = json_object_object_get_ex(root, "fs_name", &fs_name);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have a fs_name entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(fs_name, json_type_string)) {
- fprintf(stderr,
- "%s config error: fs_name is not a string\n",
- progname);
- goto out;
- }
- if (snprintf(cfg.fs_name, sizeof(cfg.fs_name), "%s",
- json_object_get_string(fs_name)) >= sizeof(cfg.fs_name)) {
- fprintf(stderr,
- "%s config error: fs_name is too long\n",
- progname);
- goto out;
- }
-
- rc = json_object_object_get_ex(root, "mount", &mount);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have a mount entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(mount, json_type_string)) {
- fprintf(stderr,
- "%s config error: mount is not a string\n",
- progname);
- goto out;
- }
- if (snprintf(cfg.mount, sizeof(cfg.mount), "%s",
- json_object_get_string(mount)) >= sizeof(cfg.mount)) {
- fprintf(stderr,
- "%s config error: mount is too long\n",
- progname);
- goto out;
- }
-
- rc = json_object_object_get_ex(root, "dump_path", &dump_path);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have a dump_path entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(dump_path, json_type_string)) {
- fprintf(stderr,
- "%s config error: dump_path is not a string\n",
- progname);
- goto out;
- }
- if (snprintf(cfg.dump_path, sizeof(cfg.dump_path), "%s",
- json_object_get_string(dump_path))
- >= sizeof(cfg.dump_path)) {
- fprintf(stderr,
- "%s config error: dump_path is too long\n",
- progname);
- goto out;
- }
-
- rc = json_object_object_get_ex(root, "mdts", &mdts);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have an mdts entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(mdts, json_type_array)) {
- fprintf(stderr,
- "%s config error: mdts is not an array\n",
- progname);
- goto out;
- }
-
- cfg.nb_mdts = json_object_array_length(mdts);
- if (cfg.nb_mdts > ARRAY_SIZE(cfg.mdts)) {
- fprintf(stderr,
- "%s error: mdts array is larger than %lu\n",
- progname,
- ARRAY_SIZE(cfg.mdts));
- goto out;
- }
-
- for (i = 0; i < cfg.nb_mdts; i++) {
- struct json_object *data, *mdt_idx, *reader_id;
- int mdt_id;
-
- data = json_object_array_get_idx(mdts, i);
- if (!data) {
- fprintf(stderr,
- "%s config error: mdt %d is not an object\n",
- progname, i);
- goto out;
- }
-
- rc = json_object_object_get_ex(data, "mdt_idx", &mdt_idx);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have an mdt_idx entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(mdt_idx, json_type_int)) {
- fprintf(stderr,
- "%s config error: mdt_idx is not an int\n",
- progname);
- goto out;
- }
- mdt_id = json_object_get_int(mdt_idx);
- if (mdt_id < 0 || mdt_id >= MAX_MDTS) {
- fprintf(stderr,
- "%s config error: mdt_idx %d out of range\n",
- progname, mdt_id);
- goto out;
- }
-
- rc = json_object_object_get_ex(data, "reader_id", &reader_id);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have a reader_id entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(reader_id, json_type_string)) {
- fprintf(stderr,
- "%s config error: reader_id is not a string\n",
- progname);
- goto out;
- }
- if (cfg.mdts[mdt_id].reader_id[0] != '\0') {
- fprintf(stderr,
- "%s config error: duplicate mdt index %d\n",
- progname, mdt_id);
- goto out;
- }
- if (snprintf(cfg.mdts[mdt_id].reader_id,
- sizeof(cfg.mdts[mdt_id].reader_id), "%s",
- json_object_get_string(reader_id)) >=
- sizeof(cfg.mdts[mdt_id].reader_id)) {
- fprintf(stderr,
- "%s config error: reader_id is too long\n",
- progname);
- goto out;
- }
- }
-
- rc = json_object_object_get_ex(root, "parse_interval_sec",
- &parse_interval_sec);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have a parse_interval_sec entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(parse_interval_sec, json_type_int)) {
- fprintf(stderr,
- "%s config error: parse_interval_sec is not an int\n",
- progname);
- goto out;
- }
- cfg.parse_interval_sec = json_object_get_int(parse_interval_sec);
-
- rc = json_object_object_get_ex(root, "sync_every_n_entries",
- &sync_every_n_entries);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have a sync_every_n_entries entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(sync_every_n_entries, json_type_int)) {
- fprintf(stderr,
- "%s config error: sync_every_n_entries is not an int\n",
- progname);
- goto out;
- }
- cfg.sync_every_n_entries = json_object_get_int(sync_every_n_entries);
-
- rc = json_object_object_get_ex(root, "max_syncs_before_sleep",
- &max_syncs_before_sleep);
- if (!rc) {
- fprintf(stderr,
- "%s config error: %s does not have a max_syncs_before_sleep entry\n",
- progname, cfg_file);
- goto out;
- }
- if (!json_object_is_type(max_syncs_before_sleep, json_type_int)) {
- fprintf(stderr,
- "%s config error: max_syncs_before_sleep is not an int\n",
- progname);
- goto out;
- }
- cfg.max_syncs_before_sleep =
- json_object_get_int(max_syncs_before_sleep);
-
- rc = 0;
-out:
- json_object_put(root);
- return rc;
-}
-
-static void dump_config(void)
-{
- int idx;
-
- if (!config_init)
- return;
-
- printf("FS name: %s\n", cfg.fs_name);
- printf("Mount path: %s\n", cfg.mount);
- printf("Dump path: %s\n", cfg.dump_path);
-
- for (idx = 0; idx < MAX_MDTS; idx++) {
- if (cfg.mdts[idx].reader_id[0] == '\0')
- continue;
- printf("MDT index: %d, reader ID %s\n",
- idx, cfg.mdts[idx].reader_id);
- }
-
- printf("Parse interval: %d\n", cfg.parse_interval_sec);
- printf("Sync: every %d entries\n", cfg.sync_every_n_entries);
- printf("Max syncs before sleep: %d\n", cfg.max_syncs_before_sleep);
-}
-
-static int sanity_config(void)
-{
- struct stat s;
- char path[PATH_MAX + 1];
- int rc;
-
- if (!config_init)
- return -1;
-
- /* check dump_path */
- rc = stat(cfg.dump_path, &s);
- if (rc == -1) {
- fprintf(stderr, "%s: cannot stat %s: %s\n", progname,
- cfg.dump_path, strerror(errno));
- return rc;
- }
- if (!S_ISDIR(s.st_mode)) {
- fprintf(stderr, "%s: dump path %s: %s\n",
- progname, cfg.dump_path, strerror(errno = ENOTDIR));
- return -1;
- }
-
- /* create /files and /users directories if necessary */
- rc = snprintf(path, sizeof(path), "%s/files", cfg.dump_path);
- if (rc >= sizeof(path))
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
- rc = mkdir(path, 0700);
- if (rc && errno != EEXIST) {
- fprintf(stderr, "%s cannot mkdir %s: %s\n", progname,
- path, strerror(errno));
- return rc;
- }
- rc = snprintf(path, sizeof(path), "%s/users", cfg.dump_path);
- if (rc >= sizeof(path))
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
- rc = mkdir(path, 0700);
- if (rc && errno != EEXIST) {
- fprintf(stderr, "%s cannot mkdir %s: %s\n", progname,
- path, strerror(errno));
- return rc;
- }
-
- /* create files/<FID> directory of root dir and associated name and
- * paths files
- */
- if (path2fid) {
- struct lu_fid fid;
- char rootpath[NAME_MAX + 2];
- int sz_max;
- FILE *fp;
-
- rc = snprintf(rootpath, sizeof(rootpath), "%s", cfg.mount);
- if (rc >= sizeof(rootpath))
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
- rc = llapi_path2fid(rootpath, &fid);
- if (rc) {
- fprintf(stderr, "%s path2fid failed for %s: %s\n",
- progname, rootpath, strerror(-rc));
- return rc;
- }
-
- rc = snprintf(path, sizeof(path), "%s/files/"DFID,
- cfg.dump_path, PFID(&fid));
- if (rc >= sizeof(path))
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
- rc = mkdir(path, 0700);
- if (rc && errno != EEXIST) {
- fprintf(stderr, "%s cannot mkdir %s: %s\n", progname,
- path, strerror(errno));
- return rc;
- }
-
- if (rc == 0) {
- sz_max = sizeof(path) - strlen(path);
- rc = snprintf(path + strlen(path), sz_max, "/name");
- if (rc >= sz_max)
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
- fp = fopen(path, "w");
- if (fp == NULL) {
- printerr("cannot open %s: %s\n",
- path, strerror(errno));
- return -errno;
- }
- rc = fprintf(fp, "/");
- fclose(fp);
- if (rc != 1) {
- printerr("cannot write name info in %s: %s\n",
- path, strerror(errno));
- return -errno;
- }
-
- rc = snprintf(path, sizeof(path),
- "%s/files/"DFID"/paths",
- cfg.dump_path, PFID(&fid));
- if (rc >= sizeof(path))
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
- fp = fopen(path, "w");
- if (fp == NULL) {
- printerr("cannot open %s: %s\n",
- path, strerror(errno));
- return -errno;
- }
- fclose(fp);
- }
- }
-
- /* check parse_interval_sec */
- if (cfg.parse_interval_sec <= 0) {
- fprintf(stderr, "%s: parse_interval_sec %d invalid\n",
- progname, cfg.parse_interval_sec);
- return -1;
- }
-
- /* sync_every_n_entries */
- if (cfg.sync_every_n_entries <= 0) {
- fprintf(stderr, "%s: sync_every_n_entries %d invalid\n",
- progname, cfg.sync_every_n_entries);
- return -1;
- }
-
- /* max_syncs_before_sleep */
- if (cfg.max_syncs_before_sleep <= 0) {
- fprintf(stderr, "%s: max_syncs_before_sleep %d invalid\n",
- progname, cfg.max_syncs_before_sleep);
- return -1;
- }
-
- return 0;
-}
-
-/* 3 objectives:
- * - write name in 'files/<tFID>/name' file
- * - write parent FID in 'files/<tFID>/pfid' file
- * - build full path info and put into pathinfo variable
- */
-static int get_path_info(char *name, int namelen, char *tfidstr, char *pfidstr,
- char *pathinfo, int pathinfo_sz, bool edit)
-{
- char path_pfid[PATH_MAX] = { 0 };
- char path_name[PATH_MAX] = { 0 };
- char pfid[PATH_MAX] = { 0 };
- char namestr[NAME_MAX] = { 0 };
- struct stat s;
- FILE *fp;
- int fd;
- int sz_max;
- int rc;
-
- rc = snprintf(path_pfid, sizeof(path_pfid), "%s/files/%s",
- cfg.dump_path, tfidstr);
- if (rc >= sizeof(path_pfid))
- rc = -ENAMETOOLONG;
- if (rc < 0) {
- printerr("%s: error with snprintf: %d\n",
- progname, strerror(-rc));
- return rc;
- }
-
- rc = mkdir(path_pfid, 0700);
- if (rc && errno != EEXIST) {
- printerr("%s cannot mkdir %s: %s\n", progname,
- path_pfid, strerror(errno));
- return rc;
- }
-
- sz_max = sizeof(path_pfid) - strlen(path_pfid);
- rc = snprintf(path_pfid + strlen(path_pfid), sz_max, "/pfid");
- if (rc >= sz_max)
- rc = -ENAMETOOLONG;
- if (rc < 0) {
- printerr("%s: error with snprintf: %d\n",
- progname, strerror(-rc));
- return rc;
- }
-
- rc = snprintf(path_name, sizeof(path_name), "%s/files/%s/name",
- cfg.dump_path, tfidstr);
- if (rc >= sizeof(path_name))
- rc = -ENAMETOOLONG;
- if (rc < 0) {
- printerr("%s: error with snprintf: %d\n",
- progname, strerror(-rc));
- return rc;
- }
-
- rc = stat(path_pfid, &s);
- if (rc == -1) {
- if (pfidstr != NULL) {
- fp = fopen(path_pfid, "w");
- if (fp == NULL) {
- printerr("cannot open %s: %s\n",
- path_pfid, strerror(errno));
- return -errno;
- }
- rc = fprintf(fp, "%s", pfidstr);
- fclose(fp);
- if (rc != strlen(pfidstr)) {
- printerr("cannot write pfid info in %s: %s\n",
- path_pfid, strerror(errno));
- return -errno;
- }
-
- /* what if path_name file already exists? */
- fp = fopen(path_name, "w");
- if (fp == NULL) {
- printerr("cannot open %s: %s\n",
- path_name, strerror(errno));
- return -errno;
- }
- rc = snprintf(namestr, sizeof(namestr), "%.*s",
- namelen, name);
- if (rc >= sizeof(namestr))
- rc = -ENAMETOOLONG;
- if (rc < 0) {
- printerr("%s: error with snprintf: %d\n",
- progname, strerror(-rc));
- return rc;
- }
- rc = fprintf(fp, "%s", namestr);
- fclose(fp);
- if (rc != strlen(namestr)) {
- printerr("cannot write name info in %s: %s\n",
- path_name, strerror(errno));
- return -errno;
- }
- } else {
- /* if 'name' file exists and contains '/', take it */
- fd = open(path_name, O_RDONLY);
- if (fd != -1) {
- rc = read(fd, namestr, sizeof(namestr));
- close(fd);
- if (rc == -1) {
- printerr("cannot read from file %s: %s\n",
- path_name, strerror(errno));
- return -errno;
- }
- namestr[rc] = '\0';
-
- if (strncmp(namestr, "/", strlen(namestr)) == 0)
- goto process;
-
- namestr[0] = '\0';
- }
-
- {
- char tfid_pathinfo[PATH_MAX];
- long long recno = -1;
- int linkno = 0;
- struct lu_fid parent_fid;
-
- /* no pfid file, and no pfidstr info provided
- * => means we are on a intermediate directory
- * in the path, and laudit was not run with '-p'
- * since beginning of Lustre fs life.
- * Solution is to rebuild info with
- * llapi_fid2path(tfidstr) and then
- * llapi_path2parent(newpath), and finally
- * putting parent_fid in 'pfid' file
- * and name in 'name' file.
- */
-
- /* But this resolution can be incorrect, eg:
- * # touch /<fs>/dirA/fileA
- * # mv /<fs>/dirA /<fs>/dirB
- * # laudit -p
- * => this will give wrong path (dirB/fileA) in
- * CREAT entry for fileA
- * But maybe this is better than nothing.
- * In this case, display a warning.
- */
- if (!warn_possible_fid2path_error) {
- printerr("%s: Warning! resolving path-to-fid with llapi_fid2path(). This may incur extra overhead, and possible incorrect path resolutions.\n",
- progname);
- warn_possible_fid2path_error = true;
- }
-
- rc = snprintf(tfid_pathinfo,
- sizeof(tfid_pathinfo),
- "%s/", cfg.mount);
- if (rc >= sizeof(tfid_pathinfo))
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
-
- rc = llapi_fid2path(cfg.fs_name, tfidstr,
- tfid_pathinfo +
- strlen(tfid_pathinfo),
- sizeof(tfid_pathinfo) -
- strlen(tfid_pathinfo),
- &recno, &linkno);
- if (rc) {
- printerr("%s cannot get path from FID %s: %s\n",
- progname, pfidstr,
- strerror(-rc));
- return rc;
- }
-
- rc = llapi_path2parent(tfid_pathinfo, linkno,
- &parent_fid, namestr,
- sizeof(namestr));
- if (rc) {
- printerr("%s cannot get parent FID from path %s: %s\n",
- progname,
- tfid_pathinfo, strerror(-rc));
- return rc;
- }
-
- sprintf(pfid, DFID, PFID(&parent_fid));
- pfidstr = pfid;
-
- fp = fopen(path_pfid, "w");
- if (fp == NULL) {
- printerr("cannot open %s: %s\n",
- path_pfid, strerror(errno));
- return -errno;
- }
- rc = fprintf(fp, "%s", pfidstr);
- fclose(fp);
- if (rc != strlen(pfidstr)) {
- printerr("cannot write pfid info in %s: %s\n",
- path_pfid, strerror(errno));
- return -errno;
- }
-
- fp = fopen(path_name, "w");
- if (fp == NULL) {
- printerr("cannot open %s: %s\n",
- path_name, strerror(errno));
- return -errno;
- }
- rc = fprintf(fp, "%s", namestr);
- fclose(fp);
- if (rc != strlen(namestr)) {
- printerr("cannot write name info in %s: %s\n",
- path_name, strerror(errno));
- return -errno;
- }
-
- }
- }
- } else {
- if (pfidstr != NULL) {
- if (edit) {
- /* this happens in case of rename */
- fp = fopen(path_pfid, "w");
- if (fp == NULL) {
- printerr("cannot open %s: %s\n",
- path_pfid, strerror(errno));
- return -errno;
- }
- rc = fprintf(fp, "%s", pfidstr);
- fclose(fp);
- if (rc != strlen(pfidstr)) {
- printerr("can't write pfid in %s: %s\n",
- path_pfid, strerror(errno));
- return -errno;
- }
- }
- } else {
- fd = open(path_pfid, O_RDONLY);
- if (fd == -1) {
- printerr("cannot open %s: %s\n",
- path_pfid, strerror(errno));
- return -errno;
- }
- rc = read(fd, pfid, sizeof(pfid));
- close(fd);
- if (rc == -1) {
- printerr("cannot read from file %s: %s\n",
- path_pfid, strerror(errno));
- return -errno;
- }
- pfid[rc] = '\0';
- pfidstr = pfid;
- }
-
- if (name != NULL) {
- rc = snprintf(namestr, sizeof(namestr), "%.*s",
- namelen, name);
- if (rc >= sizeof(namestr))
- rc = -ENAMETOOLONG;
- if (rc < 0) {
- printerr("%s: error with snprintf: %d\n",
- progname, strerror(-rc));
- return rc;
- }
-
- if (edit) {
- /* this happens in case of rename */
- fp = fopen(path_name, "w");
- if (fp == NULL) {
- printerr("cannot open %s: %s\n",
- path_name, strerror(errno));
- return -errno;
- }
-
- rc = fprintf(fp, "%s", namestr);
- fclose(fp);
- if (rc != strlen(namestr)) {
- printerr("can't write name in %s: %s\n",
- path_name, strerror(errno));
- return -errno;
- }
- }
- } else {
- fd = open(path_name, O_RDONLY);
- if (fd == -1) {
- printerr("cannot open %s: %s\n",
- path_name, strerror(errno));
- return -errno;
- }
- rc = read(fd, namestr, sizeof(namestr));
- close(fd);
- if (rc == -1) {
- printerr("cannot read from file %s: %s\n",
- path_name, strerror(errno));
- return -errno;
- }
- namestr[rc] = '\0';
- }
- }
-
-process:
- if (pfidstr != NULL) {
- get_path_info(NULL, 0, pfidstr, NULL, pathinfo, pathinfo_sz,
- false);
- if (pathinfo[0] == '/') {
- pathinfo[0] = '\0';
- } else {
- sz_max = pathinfo_sz - strlen(pathinfo);
- rc = snprintf(pathinfo + strlen(pathinfo), sz_max, "/");
- if (rc >= sz_max)
- rc = -ENAMETOOLONG;
- if (rc < 0) {
- printerr("%s: error with snprintf: %d\n",
- progname, strerror(-rc));
- return rc;
- }
- }
- } else {
- pathinfo[0] = '\0';
- }
-
- /* add file name after path of parent */
- sz_max = pathinfo_sz - strlen(pathinfo);
- rc = snprintf(pathinfo + strlen(pathinfo), sz_max, "%s",
- namestr);
- if (rc >= sz_max)
- rc = -ENAMETOOLONG;
- if (rc < 0) {
- printerr("%s: error with snprintf: %d\n",
- progname, strerror(-rc));
- return rc;
- }
-
- return 0;
-}
-
-/* If rec target fid is [0:0x0:0x0], make sure operation is 08RENME,
- * and then use source FID instead of target FID
- */
-static int tfid(struct changelog_rec *rec, char *tfidstr)
-{
- if (fid_is_zero(&rec->cr_tfid) &&
- rec->cr_type == CL_RENAME && rec->cr_flags & CLF_RENAME) {
- struct changelog_ext_rename *rnm = changelog_rec_rename(rec);
-
- if (fid_is_zero(&rnm->cr_sfid)) {
- printerr("cannot find usable fid in rec %d\n",
- rec->cr_index);
- return -errno;
- }
- sprintf(tfidstr, DFID, PFID(&rnm->cr_sfid));
- } else {
- sprintf(tfidstr, DFID, PFID(&rec->cr_tfid));
- }
-
- return 0;
-}
-
-static int get_record_info(struct changelog_rec *rec, struct tm ts,
- struct changelog_ext_uidgid **uidgid,
- char *tstamp, int tstamp_sz,
- char *rec_info, int rec_info_sz,
- char *pathinfo, int pathinfo_sz)
-{
- int sz_max;
- int rc;
-
- rec_info[0] = '\0';
- *uidgid = NULL;
-
- rc = snprintf(tstamp, tstamp_sz,
- "%04d.%02d.%02d %02d:%02d:%02d.%09d",
- ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday,
- ts.tm_hour, ts.tm_min, ts.tm_sec,
- (int)(rec->cr_time & ((1 << 30) - 1)));
- if (rc >= tstamp_sz)
- rc = -EAGAIN;
- if (rc < 0)
- goto printerr;
-
- if (rec->cr_flags & CLF_JOBID) {
- struct changelog_ext_jobid *jid = changelog_rec_jobid(rec);
-
- if (jid->cr_jobid[0] != '\0') {
- sz_max = rec_info_sz - strlen(rec_info);
- rc = snprintf(rec_info + strlen(rec_info), sz_max,
- " j=%s", jid->cr_jobid);
- if (rc >= sz_max)
- rc = -EAGAIN;
- if (rc < 0)
- goto printerr;
- }
- }
-
- if (rec->cr_flags & CLF_EXTRA_FLAGS) {
- struct changelog_ext_extra_flags *ef =
- changelog_rec_extra_flags(rec);
-
- if (ef->cr_extra_flags & CLFE_UIDGID)
- *uidgid = changelog_rec_uidgid(rec);
-
- if (ef->cr_extra_flags & CLFE_NID) {
- struct changelog_ext_nid *nid = changelog_rec_nid(rec);
-
- sz_max = rec_info_sz - strlen(rec_info);
- rc = snprintf(rec_info + strlen(rec_info), sz_max,
- " nid=%s", libcfs_nid2str(nid->cr_nid));
- if (rc >= sz_max)
- rc = -EAGAIN;
- if (rc < 0)
- goto printerr;
- }
-
- if (ef->cr_extra_flags & CLFE_OPEN) {
- struct changelog_ext_openmode *omd =
- changelog_rec_openmode(rec);
- char mode[] = "---";
-
- /* exec mode must be exclusive */
- if (omd->cr_openflags & MDS_FMODE_EXEC) {
- mode[2] = 'x';
- } else {
- if (omd->cr_openflags & MDS_FMODE_READ)
- mode[0] = 'r';
- if (omd->cr_openflags &
- (MDS_FMODE_WRITE |
- MDS_OPEN_TRUNC |
- MDS_OPEN_APPEND))
- mode[1] = 'w';
- }
-
- if (strcmp(mode, "---")) {
- sz_max = rec_info_sz - strlen(rec_info);
- rc = snprintf(rec_info + strlen(rec_info),
- sz_max, " m=%s", mode);
- if (rc >= sz_max)
- rc = -EAGAIN;
- if (rc < 0)
- goto printerr;
- }
- }
-
- if (ef->cr_extra_flags & CLFE_XATTR) {
- struct changelog_ext_xattr *xattr =
- changelog_rec_xattr(rec);
-
- if (xattr->cr_xattr[0] != '\0') {
- sz_max = rec_info_sz - strlen(rec_info);
- rc = snprintf(rec_info + strlen(rec_info),
- sz_max, " x=%s", xattr->cr_xattr);
- if (rc >= sz_max)
- rc = -EAGAIN;
- if (rc < 0)
- goto printerr;
- }
- }
- }
-
- if (rec->cr_namelen) {
- char fidstr[PATH_MAX];
-
- sprintf(fidstr, DFID, PFID(&rec->cr_pfid));
- if (path2fid) {
- char tfidstr[PATH_MAX];
-
- rc = tfid(rec, tfidstr);
- if (rc)
- goto out_noprint;
-
- rc = get_path_info(changelog_rec_name(rec),
- rec->cr_namelen, tfidstr, fidstr,
- pathinfo, pathinfo_sz, true);
- if (rc)
- goto out_noprint;
- }
-
- sz_max = rec_info_sz - strlen(rec_info);
- rc = snprintf(rec_info + strlen(rec_info), sz_max,
- " p=%s %.*s", fidstr,
- path2fid ? (int)strlen(pathinfo) : rec->cr_namelen,
- path2fid ? pathinfo : changelog_rec_name(rec));
- if (rc >= sz_max)
- rc = -EAGAIN;
- if (rc < 0)
- goto printerr;
- }
-
- if (rec->cr_flags & CLF_RENAME) {
- struct changelog_ext_rename *rnm = changelog_rec_rename(rec);
- char fidstr[PATH_MAX];
- char spathinfo[PATH_MAX];
-
- if (!fid_is_zero(&rnm->cr_sfid)) {
- sprintf(fidstr, DFID, PFID(&rnm->cr_spfid));
- if (path2fid) {
- char tfidstr[PATH_MAX];
-
- rc = tfid(rec, tfidstr);
- if (rc)
- goto out_noprint;
-
- rc = get_path_info(changelog_rec_sname(rec),
- (int)changelog_rec_snamelen(rec),
- tfidstr, fidstr, spathinfo,
- sizeof(spathinfo), false);
- if (rc)
- goto out_noprint;
- }
-
- sz_max = rec_info_sz - strlen(rec_info);
- rc = snprintf(rec_info + strlen(rec_info), sz_max,
- " s="DFID" sp=%s %.*s",
- PFID(&rnm->cr_sfid), fidstr,
- path2fid ? (int)strlen(spathinfo) :
- (int)changelog_rec_snamelen(rec),
- path2fid ? spathinfo :
- changelog_rec_sname(rec));
- if (rc >= sz_max)
- rc = -EAGAIN;
- if (rc < 0)
- goto printerr;
- }
- }
-
-printerr:
- if (rc < 0) {
- printerr("%s cannot get record info: %s\n",
- progname, strerror(-rc));
- } else if (uidgid == NULL) {
- printerr("%s cannot get uid/gid: aborting\n",
- progname);
- rc = -EINVAL;
- } else {
- rc = 0;
- }
-out_noprint:
- return rc;
-}
-
-/* When called with uidgid != NULL, put it into record; else put FID instead. */
-static int write_rec_file(char *main_dir, struct changelog_rec *rec,
- struct changelog_ext_uidgid *uidgid,
- struct tm ts, char *tstamp, char *rec_info)
-{
- /* directory structure is like:
- * main_dir/$YEAR/$MONTH/$DAY/$HOUR$MIN/$SECS
- */
-
- char path[PATH_MAX + 1];
- FILE *file;
- int sz_max;
- int rc;
-
- /* 1. always try to create main_dir directory */
- rc = mkdir(main_dir, 0700);
- if (rc && errno != EEXIST) {
- printerr("%s cannot mkdir %s: %s\n", progname,
- main_dir, strerror(errno));
- return rc;
- }
-
- /* 2. always try to create $YEAR/$MONTH/$DAY/$HOUR$MIN directory */
- sz_max = sizeof(path);
- goto hourmin;
-year:
- rc = snprintf(path, sz_max, "%s/%04d", main_dir, ts.tm_year + 1900);
- if (rc >= sz_max)
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
- rc = mkdir(path, 0700);
- if (rc && errno != EEXIST) {
- printerr("%s cannot mkdir %s: %s\n", progname,
- path, strerror(errno));
- return rc;
- }
-month:
- rc = snprintf(path, sz_max, "%s/%04d/%02d", main_dir,
- ts.tm_year + 1900, ts.tm_mon + 1);
- if (rc >= sz_max)
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
- rc = mkdir(path, 0700);
- if (rc) {
- if (errno == ENOENT) {
- goto year;
- } else if (errno != EEXIST) {
- printerr("%s cannot mkdir %s: %s\n", progname,
- path, strerror(errno));
- return rc;
- }
- }
-day:
- rc = snprintf(path, sz_max, "%s/%04d/%02d/%02d", main_dir,
- ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday);
- if (rc >= sz_max)
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
- rc = mkdir(path, 0700);
- if (rc) {
- if (errno == ENOENT) {
- goto month;
- } else if (errno != EEXIST) {
- printerr("%s cannot mkdir %s: %s\n", progname,
- path, strerror(errno));
- return rc;
- }
- }
-hourmin:
- rc = snprintf(path, sz_max, "%s/%04d/%02d/%02d/%02d%02d", main_dir,
- ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday,
- ts.tm_hour, ts.tm_min);
- if (rc >= sz_max)
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
- rc = mkdir(path, 0700);
- if (rc) {
- if (errno == ENOENT) {
- goto day;
- } else if (errno != EEXIST) {
- printerr("%s cannot mkdir %s: %s\n", progname,
- path, strerror(errno));
- return rc;
- }
- }
-
- /* 3. open $SECS file in append mode */
- sz_max = sizeof(path) - strlen(path);
- rc = snprintf(path + strlen(path), sz_max, "/%02d", ts.tm_sec);
- if (rc >= sz_max)
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
- file = fopen(path, "a");
- if (file == NULL) {
- printerr("%s cannot open %s: %s\n", progname,
- path, strerror(errno));
- return -1;
- }
-
- /* 4. write info */
- if (uidgid)
- rc = fprintf(file, "%s %02d%s u=%llu:%llu%s\n",
- tstamp, rec->cr_type,
- changelog_type2str(rec->cr_type), uidgid->cr_uid,
- uidgid->cr_gid, rec_info);
- else
- rc = fprintf(file, "%s %02d%s t="DFID"%s\n",
- tstamp, rec->cr_type,
- changelog_type2str(rec->cr_type),
- PFID(&rec->cr_tfid), rec_info);
-
- if (rc < 0)
- printerr("%s cannot write to file %s: %s\n",
- progname, path, strerror(-rc));
-
- /* 5. close file */
- fclose(file);
-
- return rc;
-}
-
-static int write_audit_files(struct changelog_rec *rec,
- struct changelog_ext_uidgid *uidgid,
- struct tm ts, char *tstamp,
- char *rec_info, char *pathinfo)
-{
- char main_dir[PATH_MAX + 1];
- char fidstr[PATH_MAX];
- FILE *fp;
- int rc;
-
- rc = tfid(rec, fidstr);
- if (rc)
- return rc;
-
- if (verbose > 1)
- printinfo("%s %02d%s t="DFID" u=%u:%u%s\n", tstamp,
- rec->cr_type, changelog_type2str(rec->cr_type),
- PFID(&rec->cr_tfid),
- uidgid->cr_uid, uidgid->cr_gid, rec_info);
-
- /* directory structure for easy file history:
- * files/$FID/$YEAR/$MONTH/$DAY/$HOUR$MIN/$SECS
- */
-
- rc = snprintf(main_dir, sizeof(main_dir), "%s/files/%s",
- cfg.dump_path, fidstr);
- if (rc >= sizeof(main_dir))
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
- rc = write_rec_file(main_dir, rec, uidgid, ts, tstamp, rec_info);
-
- if (rc < 0)
- return rc;
-
- /* If necessary, store path info in files/$FID/paths file.
- * Append new paths as file is moved.
- */
- if (path2fid && rec->cr_namelen) {
- rc = snprintf(main_dir + strlen(main_dir),
- sizeof(main_dir) - strlen(main_dir),
- "/paths");
- if (rc >= sizeof(main_dir))
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
-
- fp = fopen(main_dir, "a");
- if (fp == NULL) {
- printerr("cannot open %s: %s\n",
- main_dir, strerror(errno));
- return -errno;
- }
- rc = fprintf(fp, "%s\n", pathinfo);
- fclose(fp);
- if (rc != strlen(pathinfo) + 1) {
- printerr("cannot write path info in %s: %s\n",
- main_dir, strerror(errno));
- return -errno;
- }
- }
-
- /* directory structure for easy user history:
- * users/$UID:GID/$YEAR/$MONTH/$DAY/$HOUR$MIN/$SECS
- */
- rc = snprintf(main_dir, sizeof(main_dir), "%s/users/%llu:%llu",
- cfg.dump_path, uidgid->cr_uid, uidgid->cr_gid);
- if (rc >= sizeof(main_dir))
- rc = -ENAMETOOLONG;
- if (rc < 0)
- return rc;
- rc = write_rec_file(main_dir, rec, NULL, ts, tstamp, rec_info);
- return rc;
-}
-
-/* Important note about changelogs API implementation:
- * records that can be read with llapi_changelog_recv()
- * are determined by the time when llapi_changelog_start() is called.
- * So with llapi_changelog_clear(), it becomes possible to clear entries that
- * were not read, eg if they were added to the llogs AFTER the call to
- * llapi_changelog_start().
- */
-
-static int parse_changelog(int idx)
-{
- void *changelog_priv;
- struct changelog_rec *rec;
- time_t secs;
- struct tm ts;
- char tstamp[30];
- /* "j=jobid nid=nid m=openmode x=xattr p=PFID <name>
- * s=SFID sp=SPFID <sname>"
- * openmode: 3
- * FID: 42
- */
- char rec_info[LUSTRE_JOBID_SIZE + LNET_NIDSTR_SIZE + XATTR_NAME_MAX +
- 2*NAME_MAX + 153];
- char pathinfo[PATH_MAX];
- struct changelog_ext_uidgid *uidgid;
- __u64 last_cr_index = 0;
- int loop_count = 0, resume_count = 0;
- int rc = 0;
-
- if (stop_now)
- return -EINTR;
-
-resume:
- if (daemon_mode && ++resume_count > cfg.max_syncs_before_sleep)
- goto out_parse;
-
- rc = llapi_changelog_start(&changelog_priv,
- CHANGELOG_FLAG_BLOCK |
- CHANGELOG_FLAG_JOBID |
- CHANGELOG_FLAG_EXTRA_FLAGS,
- cfg.mdts[idx].mdd_name, 0);
- if (rc < 0) {
- printerr("%s cannot start changelog for %s: %s\n",
- progname, cfg.mdts[idx].mdd_name,
- strerror(errno = -rc));
- return rc;
- }
-
- rc = llapi_changelog_set_xflags(changelog_priv,
- CHANGELOG_EXTRA_FLAG_UIDGID |
- CHANGELOG_EXTRA_FLAG_NID |
- CHANGELOG_EXTRA_FLAG_OMODE |
- CHANGELOG_EXTRA_FLAG_XATTR);
- if (rc < 0) {
- printerr("%s cannot set xflags for changelog: %s\n",
- progname, strerror(errno = -rc));
- return rc;
- }
-
- while (!stop_now && rc >= 0 && loop_count < cfg.sync_every_n_entries) {
- rc = llapi_changelog_recv(changelog_priv, &rec);
- if (rc < 0) {
- printerr("%s cannot read changelog: %s\n",
- progname, strerror(errno = -rc));
- break;
- }
- if (rc)
- /* no more to read */
- break;
-
- secs = rec->cr_time >> 30;
- gmtime_r(&secs, &ts);
-
- /* get timestamp, uid/gid, nid and all relevant info */
- rc = get_record_info(rec, ts, &uidgid, tstamp, sizeof(tstamp),
- rec_info, sizeof(rec_info),
- pathinfo, sizeof(pathinfo));
-
- /* write record info to audit files */
- if (rc == 0) {
- rc = write_audit_files(rec, uidgid, ts, tstamp,
- rec_info, pathinfo);
- if (rc >= 0)
- last_cr_index = rec->cr_index;
- }
- llapi_changelog_free(&rec);
-
- if (rc < 0)
- break;
-
- loop_count++;
- }
- llapi_changelog_fini(&changelog_priv);
-
- /* do not waste time syncing if no new entry was written,
- * or do not clear if a problem occured when dumping
- */
- if (last_cr_index)
- rc = sync_n_clear(idx, last_cr_index);
-
- if (!stop_now && rc == 0 && loop_count == cfg.sync_every_n_entries) {
- last_cr_index = 0;
- loop_count = 0;
- goto resume;
- }
-
- if (stop_now && rc >= 0)
- rc = -EINTR;
-
-out_parse:
- return (rc == 1 ? 0 : rc);
-}
-
-int get_opts(int argc, char *const argv[])
-{
- static struct option long_opts[] = {
- { .val = 'd', .name = "daemon", .has_arg = no_argument},
- { .val = 'p', .name = "path", .has_arg = no_argument},
- { .val = 'v', .name = "verbose", .has_arg = no_argument},
- { .name = NULL } };
- char *short_opts = "dpv";
- int opt;
- int longidx;
-
- optind = 0;
- while ((opt = getopt_long(argc, argv, short_opts, long_opts,
- &longidx)) != EOF) {
- switch (opt) {
- case 'd':
- daemon_mode = 1;
- break;
- case 'p':
- path2fid = true;
- break;
- case 'v':
- verbose++;
- break;
- default:
- if (opt != '?')
- fprintf(stderr, "Unknown option '%c'\n", opt);
- return -EINVAL;
- }
- }
-
- if (optind == argc) {
- /* The user didn't specify config file */
- fprintf(stderr, "Not enough arguments - config file ");
- fprintf(stderr, "not specified.\n");
- return -EINVAL;
- }
-
- /* The config file */
- strncpy(config_file, argv[argc - 1], sizeof(config_file) - 1);
- config_file[sizeof(config_file) - 1] = '\0';
-
- return 0;
-}
-
-int main(int argc, char **argv)
-{
- struct sigaction stop_sigaction, ignore_sigaction;
- int idx;
- int rc;
-
- progname = argv[0]; /* Used in error messages */
-
- if (argc < 2 || argv[argc - 1][0] == '-') {
- fprintf(stderr, "%s, missing options\n", argv[0]);
- laudit_usage();
- return -EINVAL;
- }
-
- /* Ensure that liblustreapi constructor has run */
- if (!liblustreapi_initialized)
- fprintf(stderr, "liblustreapi was not properly initialized\n");
-
- setlinebuf(stdout);
-
- rc = get_opts(argc, argv);
- if (rc)
- goto out_main;
-
- rc = get_config(config_file);
- if (rc)
- goto out_main;
- config_init = true;
-
- if (verbose)
- dump_config();
-
- rc = sanity_config();
- if (rc)
- goto out_main;
-
- for (idx = 0; idx < MAX_MDTS; idx++) {
- if (cfg.mdts[idx].reader_id[0] == '\0')
- continue;
- rc = snprintf(cfg.mdts[idx].mdd_name, MDD_NAME_LEN + 1,
- "%s-MDT%04x", cfg.fs_name, idx);
- if (rc >= MDD_NAME_LEN + 1) {
- rc = EINVAL;
- break;
- }
- if (rc < 0)
- break;
- }
-
- switch (daemon_mode) {
- case 0:
- for (idx = 0; idx < MAX_MDTS; idx++) {
- if (cfg.mdts[idx].reader_id[0] == '\0')
- continue;
- rc = parse_changelog(idx);
- if (rc)
- break;
- }
- break;
- case 1:
- rc = daemon(0, 0);
- if (rc < 0) {
- printerr("%s could not daemonize: %s\n",
- progname, strerror(errno));
- break;
- }
-
- memset(&stop_sigaction, 0, sizeof(stop_sigaction));
- stop_sigaction.sa_handler = sig_stop_parsing;
- sigemptyset(&stop_sigaction.sa_mask);
- sigaction(SIGINT, &stop_sigaction, NULL);
- sigaction(SIGTERM, &stop_sigaction, NULL);
- memset(&ignore_sigaction, 0, sizeof(ignore_sigaction));
- ignore_sigaction.sa_handler = sig_ignore;
- sigemptyset(&ignore_sigaction.sa_mask);
- sigaction(SIGHUP, &ignore_sigaction, NULL);
-
- printinfo("%s commencing operation\n", progname);
-
- while (1) {
- rc = -EINTR;
- for (idx = 0; idx < MAX_MDTS; idx++) {
- if (cfg.mdts[idx].reader_id[0] == '\0')
- continue;
- rc = parse_changelog(idx);
- if (rc)
- break;
- }
- if (rc)
- break;
-
- sleep(cfg.parse_interval_sec);
- }
-
- printinfo("%s exiting\n", progname);
- break;
- }
-
-out_main:
- return rc < 0 ? -rc : rc;
-}