#include #include #include #include #include #include #include #include #include #include #include #define ZEV_DEVICE "/devices/pseudo/zev@0:ctrl" static char *zev_device = ZEV_DEVICE; static char *zev_op_name[] = { "ERROR", "MARK", "ZFS_MOUNT", "ZFS_UMOUNT", "ZVOL_WRITE", "ZVOL_TRUNCATE", "ZNODE_CLOSE_AFTER_UPDATE", "ZNODE_CREATE", "ZNODE_MKDIR", "ZNODE_MAKE_XATTR_DIR", "ZNODE_REMOVE", "ZNODE_RMDIR", "ZNODE_LINK", "ZNODE_SYMLINK", "ZNODE_RENAME", "ZNODE_WRITE", "ZNODE_TRUNCATE", "ZNODE_SETATTR", "ZNODE_ACL", NULL }; static int verbose = 0; static int grep_friendly = 0; static void zpf(char *fmt, ...) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); if (grep_friendly) { printf(" "); } else { printf("\n"); } } static void znl(void) { if (grep_friendly) printf("\n"); } static int zev_statistics(int fd) { zev_statistics_t zs; if (ioctl(fd, ZEV_IOC_GET_GLOBAL_STATISTICS, &zs)) { perror("getting statistics data failed"); return (EXIT_FAILURE); } printf("ZEV module state:\n"); printf(" queue length in bytes : %lu\n", zs.zev_queue_len); printf(" queue length limit : %lu\n", zs.zev_max_queue_len); printf(" bytes read from device : %lu\n", zs.zev_bytes_read); printf(" module internal errors : %lu\n\n", zs.zev_cnt_errors); printf(" discarded events : %lu\n", zs.zev_cnt_discarded_events); printf(" discarded bytes : %lu\n\n", zs.zev_bytes_discarded); printf("ZFS event statistics:\n"); printf(" total ZFS events : %lu\n", zs.zev_cnt_total_events); printf(" ZFS mount : %lu\n", zs.zev_cnt_zfs_mount); printf(" ZFS umount : %lu\n", zs.zev_cnt_zfs_umount); printf(" ZVOL write : %lu\n", zs.zev_cnt_zvol_write); printf(" ZVOL truncate : %lu\n", zs.zev_cnt_zvol_truncate); printf(" ZNODE close after update: %lu\n", zs.zev_cnt_znode_close_after_update); printf(" ZNODE create : %lu\n", zs.zev_cnt_znode_create); printf(" ZNODE remove : %lu\n", zs.zev_cnt_znode_remove); printf(" ZNODE link : %lu\n", zs.zev_cnt_znode_link); printf(" ZNODE symlink : %lu\n", zs.zev_cnt_znode_symlink); printf(" ZNODE rename : %lu\n", zs.zev_cnt_znode_rename); printf(" ZNODE write : %lu\n", zs.zev_cnt_znode_write); printf(" ZNODE truncate : %lu\n", zs.zev_cnt_znode_truncate); printf(" ZNODE setattr : %lu\n", zs.zev_cnt_znode_setattr); printf(" ZNODE acl : %lu\n", zs.zev_cnt_znode_acl); return EXIT_SUCCESS; } static void zev_print_inode_info(char *name, zev_inode_info_t *info) { zpf(" %s.inode: %llu", name, info->ino); zpf(" %s.gen: %llu", name, info->gen); zpf(" %s.mtime: %llu", name, info->mtime); zpf(" %s.ctime: %llu", name, info->ctime); zpf(" %s.size: %llu", name, info->size); zpf(" %s.mode: %llo", name, info->mode); zpf(" %s.links: %llu", name, info->links); zpf(" %s.type: %lu", name, info->type); zpf(" %s.flags: %lu", name, info->flags); } static void zev_print_mark_payload(zev_mark_t *rec) { int i; int j; uint8_t *p; char c; zpf(" payload:"); p = (uint8_t *)ZEV_PAYLOAD(rec); for (i=0; ipayload_len; i+=16) { printf(" "); for (j=i; jpayload_len && jpayload_len && j= ' ' && p[j] <= '~') c = p[j]; printf("%c", c); if (j == i + 7) printf(" "); } printf("\n"); } } static void zev_print_error(char *buf) { zev_error_t *rec = (zev_error_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; if (verbose) { zpf("%s %s", ct, zev_op_name[rec->op - ZEV_OP_MIN]); zpf(" guid: %llu", rec->guid); zpf(" failed.op: %s", zev_op_name[rec->failed_op - ZEV_OP_MIN]); zpf(" message: %s", ZEV_ERRSTR(rec)); znl(); } else { printf("%s %s: failed_op=%s msg=%s\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], zev_op_name[rec->failed_op - ZEV_OP_MIN], ZEV_ERRSTR(rec)); } } static void zev_print_mark(char *buf) { zev_mark_t *rec = (zev_mark_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; if (verbose) { zpf("%s %s", ct, zev_op_name[rec->op - ZEV_OP_MIN]); zpf(" guid: %llu", rec->guid); zpf(" mark.id: %llu", rec->mark_id); zpf(" payload.len: %llu", rec->payload_len); if (rec->payload_len) zev_print_mark_payload(rec); znl(); } else { printf("%s %s: guid=%llu mark_id=%lld payload_len=%ld\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->guid, rec->mark_id, rec->payload_len); } } static void zev_print_zfs_mount(char *buf) { zev_zfs_mount_t *rec = (zev_zfs_mount_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; if (verbose) { zpf("%s %s", ct, zev_op_name[rec->op - ZEV_OP_MIN]); zpf(" guid: %llu", rec->guid); zpf(" dataset: %s", ZEV_DATASET(rec)); zpf(" mountpoint: %s", ZEV_MOUNTPOINT(rec)); zpf(" remount: %s", rec->remount ? "true" : "false"); zev_print_inode_info("root", &rec->root); znl(); } else { printf("%s %s: guid=%llu remount=%s dataset='%s' " "mountpoint='%s'\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->guid, rec->remount ? "true" : "false", ZEV_DATASET(rec), ZEV_MOUNTPOINT(rec)); } } static void zev_print_zfs_umount(char *buf) { zev_zfs_umount_t *rec = (zev_zfs_umount_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; if (verbose) { zpf("%s %s", ct, zev_op_name[rec->op - ZEV_OP_MIN]); zpf(" guid: %llu", rec->guid); znl(); } else { printf("%s %s: guid=%llu\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->guid); } } static void zev_print_zvol_truncate(char *buf) { zev_zvol_truncate_t *rec = (zev_zvol_truncate_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; if (verbose) { zpf("%s %s", ct, zev_op_name[rec->op - ZEV_OP_MIN]); zpf(" guid: %llu", rec->guid); zpf(" txg: %llu", rec->txg); zpf(" offset: %llu", rec->offset); zpf(" length: %llu", rec->length); znl(); } else { printf("%s %s: guid=%llu offset=%llu length=%llu\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->guid, rec->offset, rec->length); } } static void zev_print_zvol_write(char *buf) { zev_print_zvol_truncate(buf); } static void zev_print_znode_close_after_update(char *buf) { zev_znode_close_after_update_t *rec = (zev_znode_close_after_update_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; if (verbose) { zpf("%s %s", ct, zev_op_name[rec->op - ZEV_OP_MIN]); zpf(" guid: %llu", rec->guid); zev_print_inode_info("file", &rec->file); znl(); } else { printf("%s %s: guid=%llu file=%llu.%llu\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->guid, rec->file.ino, rec->file.gen); } } static void zev_print_znode_create(char *buf) { zev_znode_create_t *rec = (zev_znode_create_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; if (verbose) { zpf("%s %s", ct, zev_op_name[rec->op - ZEV_OP_MIN]); zpf(" guid: %llu", rec->guid); zpf(" txg: %llu", rec->txg); zpf(" name: '%s'", ZEV_NAME(rec)); zev_print_inode_info("file", &rec->file); zev_print_inode_info("parent", &rec->parent); znl(); } else { printf("%s %s: guid=%llu parent=%llu.%llu file=%llu.%llu " "file.mtime=%llu, parent.mtime=%llu, name='%s'\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->guid, rec->parent.ino, rec->parent.gen, rec->file.ino, rec->file.gen, rec->file.mtime, rec->parent.mtime, ZEV_NAME(rec)); } } static void zev_print_znode_mkdir(char *buf) { zev_print_znode_create(buf); } static void zev_print_znode_make_xattr_dir(char *buf) { zev_print_znode_create(buf); } static void zev_print_znode_remove(char *buf) { zev_znode_remove_t *rec = (zev_znode_remove_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; if (verbose) { zpf("%s %s", ct, zev_op_name[rec->op - ZEV_OP_MIN]); zpf(" guid: %llu", rec->guid); zpf(" txg: %llu", rec->txg); zpf(" file.name: '%s'", ZEV_NAME(rec)); zev_print_inode_info("file", &rec->file); zev_print_inode_info("parent", &rec->parent); znl(); } else { printf("%s %s: guid=%llu parent=%llu.%llu " "file.mtime=%llu name='%s'\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->guid, rec->parent.ino, rec->parent.gen, rec->file.mtime, ZEV_NAME(rec)); } } static void zev_print_znode_rmdir(char *buf) { zev_print_znode_remove(buf); } static void zev_print_znode_link(char *buf) { zev_znode_link_t *rec = (zev_znode_link_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; if (verbose) { zpf("%s %s", ct, zev_op_name[rec->op - ZEV_OP_MIN]); zpf(" guid: %llu", rec->guid); zpf(" txg: %llu", rec->txg); zpf(" link.name: '%s'", ZEV_NAME(rec)); zev_print_inode_info("file", &rec->file); zev_print_inode_info("parent", &rec->parent); znl(); } else { printf("%s %s: parent=%llu.%llu file=%llu.%llu " "file.ctime=%llu parent.ctime=%llu name='%s'\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->parent.ino, rec->parent.gen, rec->file.ino, rec->file.gen, rec->file.ctime, rec->parent.ctime, ZEV_NAME(rec)); } } static void zev_print_znode_symlink(char *buf) { zev_znode_symlink_t *rec = (zev_znode_symlink_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; if (verbose) { zpf("%s %s", ct, zev_op_name[rec->op - ZEV_OP_MIN]); zpf(" guid: %llu", rec->guid); zpf(" txg: %llu", rec->txg); zpf(" symlink.name: '%s'", ZEV_NAME(rec)); zpf(" symlink.link: '%s'", ZEV_LINK(rec)); zev_print_inode_info("file", &rec->file); zev_print_inode_info("parent", &rec->parent); znl(); } else { printf("%s %s: parent=%llu.%llu file=%llu.%llu " "name='%s' link='%s'\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->parent.ino, rec->parent.gen, rec->file.ino, rec->file.gen, ZEV_NAME(rec), ZEV_LINK(rec)); } } static void zev_print_znode_rename(char *buf) { zev_znode_rename_t *rec = (zev_znode_rename_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; if (verbose) { zpf("%s %s", ct, zev_op_name[rec->op - ZEV_OP_MIN]); zpf(" guid: %llu", rec->guid); zpf(" txg: %llu", rec->txg); zpf(" file.srcname: '%s'", ZEV_SRCNAME(rec)); zpf(" file.dstname: '%s'", ZEV_DSTNAME(rec)); zev_print_inode_info("file", &rec->file); zev_print_inode_info("srcdir", &rec->srcdir); zev_print_inode_info("dstdir", &rec->dstdir); znl(); } else { printf("%s %s: srcdir=%llu.%llu dstdir=%llu.%llu " "file=%llu.%llu file.mtime=%llu, file.ctime=%llu, " "srcdir.mtime=%llu, srcdir.ctime=%llu, " "dstdir.mtime=%llu, dstdir.ctime=%llu, " "srcname='%s' dstname='%s'\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->srcdir.ino, rec->srcdir.gen, rec->dstdir.ino, rec->dstdir.gen, rec->file.ino, rec->file.gen, rec->file.mtime, rec->file.ctime, rec->srcdir.mtime, rec->srcdir.ctime, rec->dstdir.mtime, rec->dstdir.ctime, ZEV_SRCNAME(rec), ZEV_DSTNAME(rec)); } } static void sig2hex_direct(const uint8_t *sig, char *hex) { int i; for (i = 0; i < SHA1_DIGEST_LENGTH; ++i) { sprintf(hex + 2 * i, "%02x", sig[i]); } hex[SHA1_DIGEST_LENGTH * 2] = '\0'; } static void zev_print_znode_write(char *buf) { zev_znode_write_t *rec = (zev_znode_write_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; zev_sig_t *sig; char sigval[(SHA1_DIGEST_LENGTH * 2) + 1]; int i; if (verbose) { zpf("%s %s", ct, zev_op_name[rec->op - ZEV_OP_MIN]); zpf(" guid: %llu", rec->guid); zpf(" txg: %llu", rec->txg); zpf(" offset: %llu", rec->offset); zpf(" length: %llu", rec->length); zev_print_inode_info("file", &rec->file); znl(); for (i=0; isignature_cnt; i++) { sig = (zev_sig_t *)ZEV_SIGNATURES(rec); sig += i; sig2hex_direct(sig->value, sigval); printf(" sig: level %d, offset %llu, value %s\n", sig->level, sig->block_offset, sigval); } } else { printf("%s %s: file=%llu.%llu offset=%llu length=%llu\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->file.ino, rec->file.gen, rec->offset, rec->length); } } static void zev_print_znode_truncate(char *buf) { zev_print_znode_write(buf); } static void zev_print_znode_setattr(char *buf) { zev_znode_setattr_t *rec = (zev_znode_setattr_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; if (verbose) { zpf("%s %s", ct, zev_op_name[rec->op - ZEV_OP_MIN]); zpf(" guid: %llu", rec->guid); zpf(" txg: %llu", rec->txg); zev_print_inode_info("file", &rec->file); znl(); } else { printf("%s %s: file=%llu.%llu mtime=%llu\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->file.ino, rec->file.gen, rec->file.mtime); } } static void zev_print_znode_acl(char *buf) { zev_print_znode_setattr(buf); } static void zev_print_event(char *buf, int len) { int record_len; int op; record_len = *(uint32_t *)buf; if (record_len != len) { fprintf(stderr, "record length mismatch: got %d, expected %d\n", record_len, len); exit(1); } op = *((uint32_t *)buf + 1); if (op < ZEV_OP_MIN || op > ZEV_OP_MAX) { fprintf(stderr, "unknown op code: %d\n", op); exit(1); } switch (op) { case ZEV_OP_ERROR: zev_print_error(buf); break; case ZEV_OP_MARK: zev_print_mark(buf); break; case ZEV_OP_ZFS_MOUNT: zev_print_zfs_mount(buf); break; case ZEV_OP_ZFS_UMOUNT: zev_print_zfs_umount(buf); break; case ZEV_OP_ZVOL_TRUNCATE: zev_print_zvol_truncate(buf); break; case ZEV_OP_ZVOL_WRITE: zev_print_zvol_write(buf); break; case ZEV_OP_ZNODE_CLOSE_AFTER_UPDATE: zev_print_znode_close_after_update(buf); break; case ZEV_OP_ZNODE_CREATE: zev_print_znode_create(buf); break; case ZEV_OP_ZNODE_MKDIR: zev_print_znode_mkdir(buf); break; case ZEV_OP_ZNODE_MAKE_XATTR_DIR: zev_print_znode_make_xattr_dir(buf); break; case ZEV_OP_ZNODE_REMOVE: zev_print_znode_remove(buf); break; case ZEV_OP_ZNODE_RMDIR: zev_print_znode_rmdir(buf); break; case ZEV_OP_ZNODE_LINK: zev_print_znode_link(buf); break; case ZEV_OP_ZNODE_SYMLINK: zev_print_znode_symlink(buf); break; case ZEV_OP_ZNODE_RENAME: zev_print_znode_rename(buf); break; case ZEV_OP_ZNODE_WRITE: zev_print_znode_write(buf); break; case ZEV_OP_ZNODE_TRUNCATE: zev_print_znode_truncate(buf); break; case ZEV_OP_ZNODE_SETATTR: zev_print_znode_setattr(buf); break; case ZEV_OP_ZNODE_ACL: zev_print_znode_acl(buf); break; default: fprintf(stderr, "unhandled op code: %d\n", op); exit(1); } } static int zev_poll_events(int fd, int create_tmp_queue) { struct pollfd pfd[1]; int ret; char buf[4096]; zev_event_t *ev; int off = 0; zev_ioctl_add_queue_t aq; int q_fd; if (create_tmp_queue) { aq.zev_max_queue_len = 0; aq.zev_flags = ZEV_FL_BLOCK_WHILE_QUEUE_FULL; snprintf(aq.zev_name, ZEV_MAX_QUEUE_NAME_LEN, "zevadm.%ld.%ld", time(NULL), getpid()); aq.zev_namelen = strlen(aq.zev_name); if (ioctl(fd, ZEV_IOC_ADD_QUEUE, &aq)) { perror("adding temporary queue failed"); return (EXIT_FAILURE); } snprintf(buf, sizeof(buf), "/devices/pseudo/zev@0:%s", aq.zev_name); q_fd = open(buf, O_RDONLY); if (q_fd < 0) { perror("opening queue device failed"); return (EXIT_FAILURE); } } else { q_fd = fd; } while (1) { pfd[0].fd = q_fd; pfd[0].events = POLLIN; ret = poll(pfd, 1, 1000); if (ret < 0) { perror("poll failed"); close(q_fd); return(EXIT_FAILURE); } if (!(pfd[0].revents & POLLIN)) continue; /* data available */ ret = read(q_fd, buf, sizeof(buf)); if (ret < 0) { perror("read failed"); close(q_fd); return(EXIT_FAILURE); } if (ret == 0) continue; while (ret > off) { ev = (zev_event_t *)(buf + off); zev_print_event(buf + off, ev->header.record_len); off += ev->header.record_len; } off = 0; } if (create_tmp_queue) close(q_fd); return EXIT_SUCCESS; } static void usage(char *progname) { fprintf(stderr, "usage: %s [-d ] [options]\n", progname); fprintf(stderr, "\n"); fprintf(stderr, " Status information:\n"); fprintf(stderr, " -s show zev statistics\n"); fprintf(stderr, " -p poll for ZFS events\n"); fprintf(stderr, " -D print zev module debug " "information\n"); fprintf(stderr, "\n"); fprintf(stderr, " Tune zev module settings:\n"); fprintf(stderr, " -Q set maximum event queue " "length\n"); fprintf(stderr, " -m mute pool, no events for " "this pool\n"); fprintf(stderr, " -M unmute pool\n"); fprintf(stderr, "\n"); fprintf(stderr, " Queue management:\n"); fprintf(stderr, " -l list queues\n"); fprintf(stderr, " -a add non-blocking queue\n"); fprintf(stderr, " -A add blocking queue\n"); fprintf(stderr, " -r remove queue\n"); fprintf(stderr, " -b make queue non-blocking " "(default)\n"); fprintf(stderr, " -B make queue block when full\n"); fprintf(stderr, " -P display queue properties\n"); fprintf(stderr, " -L set maximum event queue " "length\n"); fprintf(stderr, " -t set queue length poll " "throttle\n"); fprintf(stderr, "\n"); fprintf(stderr, " Other options:\n"); fprintf(stderr, " -d non-default device file. " "('%s')\n", ZEV_DEVICE); fprintf(stderr, " -q use device file for this " "queue name\n"); fprintf(stderr, " -k : queue mark event\n"); fprintf(stderr, " -c list file's content " "checksums\n"); fprintf(stderr, " -v verbose: additional output " "for some operations\n"); fprintf(stderr, " -g grep-friendly event output, " "one event per line\n"); exit (EXIT_FAILURE); } static int zev_add_queue(int fd, char *arg, int blocking) { zev_ioctl_add_queue_t aq; int namelen; namelen = strlen(arg); if (namelen > ZEV_MAX_QUEUE_NAME_LEN) { fprintf(stderr, "queue name too long: %s\n", arg); return (EXIT_FAILURE); } aq.zev_namelen = namelen; strcpy(aq.zev_name, arg); aq.zev_flags = ZEV_FL_PERSISTENT; if (blocking) { aq.zev_flags |= ZEV_FL_BLOCK_WHILE_QUEUE_FULL; aq.zev_max_queue_len = ZEV_MAX_QUEUE_LEN; } else { aq.zev_max_queue_len = (1024 * 1024); } if (ioctl(fd, ZEV_IOC_ADD_QUEUE, &aq)) { perror("adding queue failed"); return (EXIT_FAILURE); } return (0); } static int zev_remove_queue(int fd, char *arg) { zev_ioctl_remove_queue_t aq; int namelen; namelen = strlen(arg); if (namelen > ZEV_MAX_QUEUE_NAME_LEN) { fprintf(stderr, "queue name too long: %s\n", arg); return (EXIT_FAILURE); } aq.zev_queue_name.zev_namelen = namelen; strcpy(aq.zev_queue_name.zev_name, arg); if (ioctl(fd, ZEV_IOC_REMOVE_QUEUE, &aq)) { perror("removing queue failed"); return (EXIT_FAILURE); } return (0); } static int zev_set_global_max_queue_len(int fd, char *arg) { uint64_t maxqueuelen; errno = 0; maxqueuelen = strtol(arg, (char **)NULL, 10); if (errno) { fprintf(stderr, "invalid queue length parameter: %s\n", arg); return (EXIT_FAILURE); } if (ioctl(fd, ZEV_IOC_SET_MAX_QUEUE_LEN, &maxqueuelen)) { perror("setting max queue length failed"); return (EXIT_FAILURE); } return (0); } static int zev_mute_unmute_impl(int fd, char *poolname, int mute) { zev_ioctl_poolarg_t pa; int len; int op = mute ? ZEV_IOC_MUTE_POOL : ZEV_IOC_UNMUTE_POOL; len = strlen(poolname); if (len <= 0 || len >= sizeof(pa.zev_poolname)) { fprintf(stderr, "invalid poolname: %s\n", poolname); return (EXIT_FAILURE); } strcpy(pa.zev_poolname, poolname); pa.zev_poolname_len = len; if (ioctl(fd, op, &pa)) { perror("muting pool data failed"); return (EXIT_FAILURE); } return (0); } int zev_mute_pool(int fd, char *poolname) { return zev_mute_unmute_impl(fd, poolname, 1); } int zev_unmute_pool(int fd, char *poolname) { return zev_mute_unmute_impl(fd, poolname, 0); } static int zev_debug_info(int fd) { zev_ioctl_debug_info_t di; if (ioctl(fd, ZEV_IOC_GET_DEBUG_INFO, &di)) { perror("getting zev debug info failed"); return (EXIT_FAILURE); } printf("memory allocated: %llu bytes\n", di.zev_memory_allocated); printf("checksum cache size: %llu\n", di.zev_chksum_cache_size); printf("checksum cache hits: %llu\n", di.zev_chksum_cache_hits); printf("checksum cache misses: %llu\n", di.zev_chksum_cache_misses); return 0; } static int zev_mark(int fd, char *arg) { zev_ioctl_mark_t *mark; uint64_t guid; int len; char *p; p = strchr(arg, ':'); if (!p) { fprintf(stderr, "expected value is :, " "e.g. '123:hello'\n"); exit (EXIT_FAILURE); } *p = '\n'; p++; errno = 0; guid = strtoll(arg, (char **)NULL, 10); if (errno) { fprintf(stderr, "guid must be a number.\n"); exit (EXIT_FAILURE); } len = strlen(p); mark = malloc(sizeof(*mark) + len + 1); if (!mark) { fprintf(stderr, "can't allocate mark structure: %s\n", strerror(errno)); exit (EXIT_FAILURE); } mark->zev_guid = guid; mark->zev_mark_id = 0; mark->zev_payload_len = len; strcpy(ZEV_PAYLOAD(mark), p); if (ioctl(fd, ZEV_IOC_MARK, mark)) { perror("queueing mark failed"); return (EXIT_FAILURE); } printf("mark id: %lu\n", mark->zev_mark_id); return (0); } static int zev_queue_blocking(int fd, char *arg, int block) { zev_ioctl_get_queue_properties_t gqp; gqp.zev_queue_name.zev_namelen = strlen(arg); if (gqp.zev_queue_name.zev_namelen > ZEV_MAX_QUEUE_NAME_LEN) { fprintf(stderr, "queue name too long.\n"); return EXIT_FAILURE; } strcpy(gqp.zev_queue_name.zev_name, arg); if (ioctl(fd, ZEV_IOC_GET_QUEUE_PROPERTIES, &gqp)) { perror("getting queue properties failed"); return (EXIT_FAILURE); } if (block) { gqp.zev_flags |= ZEV_FL_BLOCK_WHILE_QUEUE_FULL; } else { gqp.zev_flags &= ~ZEV_FL_BLOCK_WHILE_QUEUE_FULL; } if (ioctl(fd, ZEV_IOC_SET_QUEUE_PROPERTIES, &gqp)) { perror("setting queue properties failed"); return (EXIT_FAILURE); } return (0); } static int zev_set_max_queue_len(int fd, char *arg, char *len) { zev_ioctl_get_queue_properties_t gqp; if (!len) { fprintf(stderr, "queue size parameter missing.\n"); return EXIT_FAILURE; } gqp.zev_queue_name.zev_namelen = strlen(arg); if (gqp.zev_queue_name.zev_namelen > ZEV_MAX_QUEUE_NAME_LEN) { fprintf(stderr, "queue name too long.\n"); return EXIT_FAILURE; } strcpy(gqp.zev_queue_name.zev_name, arg); if (ioctl(fd, ZEV_IOC_GET_QUEUE_PROPERTIES, &gqp)) { perror("getting queue properties failed"); return (EXIT_FAILURE); } gqp.zev_max_queue_len = atol(len); if (gqp.zev_max_queue_len == 0 && strcmp("0", len)) { fprintf(stderr, "queue size parameter garbled.\n"); return (EXIT_FAILURE); } if (gqp.zev_max_queue_len > ZEV_MAX_QUEUE_LEN) { fprintf(stderr, "queue size parameter out of bounds.\n"); return (EXIT_FAILURE); } if (ioctl(fd, ZEV_IOC_SET_QUEUE_PROPERTIES, &gqp)) { perror("setting queue properties failed"); return (EXIT_FAILURE); } return (0); } static int zev_set_poll_wakeup_queue_len(int fd, char *arg, char *len) { zev_ioctl_get_queue_properties_t gqp; if (!len) { fprintf(stderr, "poll throttle parameter missing.\n"); return EXIT_FAILURE; } gqp.zev_queue_name.zev_namelen = strlen(arg); if (gqp.zev_queue_name.zev_namelen > ZEV_MAX_QUEUE_NAME_LEN) { fprintf(stderr, "queue name too long.\n"); return EXIT_FAILURE; } strcpy(gqp.zev_queue_name.zev_name, arg); if (ioctl(fd, ZEV_IOC_GET_QUEUE_PROPERTIES, &gqp)) { perror("getting queue properties failed"); return (EXIT_FAILURE); } gqp.zev_poll_wakeup_threshold = atol(len); if (gqp.zev_poll_wakeup_threshold == 0 && strcmp("0", len)) { fprintf(stderr, "poll throttle parameter garbled.\n"); return (EXIT_FAILURE); } if (gqp.zev_poll_wakeup_threshold > ZEV_MAX_POLL_WAKEUP_QUEUE_LEN) { fprintf(stderr, "poll throttle parameter out of bounds.\n"); return (EXIT_FAILURE); } if (ioctl(fd, ZEV_IOC_SET_QUEUE_PROPERTIES, &gqp)) { perror("setting queue properties failed"); return (EXIT_FAILURE); } return (0); } static int zev_queue_properties(int fd, char *arg) { zev_ioctl_get_queue_properties_t gqp; gqp.zev_queue_name.zev_namelen = strlen(arg); if (gqp.zev_queue_name.zev_namelen > ZEV_MAX_QUEUE_NAME_LEN) { fprintf(stderr, "queue name too long.\n"); return EXIT_FAILURE; } strcpy(gqp.zev_queue_name.zev_name, arg); if (ioctl(fd, ZEV_IOC_GET_QUEUE_PROPERTIES, &gqp)) { perror("getting queue properties failed"); return (EXIT_FAILURE); } printf("queue : %s\n", arg); printf("max size : %" PRIu64 "\n", gqp.zev_max_queue_len); printf("poll throttle: %" PRIu64 "\n", gqp.zev_poll_wakeup_threshold); printf("persistent : %s\n", gqp.zev_flags & ZEV_FL_PERSISTENT ? "yes" : "no"); printf("blocking : %s\n", gqp.zev_flags & ZEV_FL_BLOCK_WHILE_QUEUE_FULL ? "yes" : "no"); return (0); } static int zev_list_queues(int fd) { zev_ioctl_get_queue_properties_t gqp; zev_ioctl_get_queue_list_t gql; zev_ioctl_get_queue_statistics_t gs; uint64_t i; char name[ZEV_MAX_QUEUE_NAME_LEN+1]; if (ioctl(fd, ZEV_IOC_GET_QUEUE_LIST, &gql)) { perror("getting queue list failed"); return (EXIT_FAILURE); } printf("Name Size " "Max Size Wakeup Per Block\n"); for (i=0; izev_fd = fd; gs->zev_bufsize = buf_size; off = 0; data = 0; while (1) { errno = 0; data = llseek(fd, off, SEEK_DATA); if (data < 0) { if (errno == ENXIO) /* no more data */ break; perror("llseek failed"); goto err; } data = P2ALIGN(data, ZEV_L1_SIZE); off = data + ZEV_L1_SIZE; gs->zev_offset = data; gs->zev_len = ZEV_L1_SIZE; if (ioctl(dev_fd, ZEV_IOC_GET_FILE_SIGNATURES, gs)) { perror("ioctl to get signatures failed"); goto err; } for (i=0; izev_signature_cnt; i++) { sig = (zev_sig_t *)ZEV_SIGNATURES(gs); sig += i; sig2hex_direct(sig->value, sigval); printf("level %d, offset %llu, value %s\n", sig->level, sig->block_offset, sigval); } } free(buf); close(fd); return 0; err: free(buf); close(fd); return (EXIT_FAILURE); } int main(int argc, char **argv) { int fd; int c; extern char *optarg; int create_tmp_queue = 1; char buf[MAXPATHLEN]; /* open device */ fd = open(zev_device, O_RDONLY); if (fd < 0) { perror("opening zev device failed"); return EXIT_FAILURE; } while ((c = getopt(argc, argv, "gvspc:d:Dlk:L:q:Qt:m:M:a:A:r:P:b:B:h?")) != -1){ switch(c) { case 'g': grep_friendly++; verbose++; break; case 'v': verbose++; break; case 's': return zev_statistics(fd); case 'p': return zev_poll_events(fd, create_tmp_queue); case 'c': return zev_checksum(fd, optarg); case 'D': return zev_debug_info(fd); case 'd': close(fd); zev_device = optarg; fd = open(zev_device, O_RDONLY); if (fd < 0) { perror("opening zev device failed"); return EXIT_FAILURE; } create_tmp_queue = 0; break; case 'q': snprintf(buf, sizeof(buf), "/devices/pseudo/zev@0:%s", optarg); close(fd); zev_device = buf; fd = open(zev_device, O_RDONLY); if (fd < 0) { perror("opening zev device failed"); return EXIT_FAILURE; } create_tmp_queue = 0; break; case 'l': return zev_list_queues(fd); case 'Q': return zev_set_global_max_queue_len(fd, optarg); case 'L': return zev_set_max_queue_len(fd, optarg, argv[optind]); case 't': return zev_set_poll_wakeup_queue_len(fd, optarg, argv[optind]); case 'm': return zev_mute_pool(fd, optarg); case 'M': return zev_unmute_pool(fd, optarg); case 'k': return zev_mark(fd, optarg); case 'a': return zev_add_queue(fd, optarg, 0); case 'A': return zev_add_queue(fd, optarg, 1); case 'r': return zev_remove_queue(fd, optarg); case 'b': return zev_queue_blocking(fd, optarg, 0); case 'B': return zev_queue_blocking(fd, optarg, 1); case 'P': return zev_queue_properties(fd, optarg); case 'h': case '?': default: usage(argv[0]); } } usage(argv[0]); close(fd); return EXIT_FAILURE; }