#include "clusterautoconfig.h"
/**
 * glocktop.c - list/print the top GFS2 glock waiters
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <limits.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <dirent.h>
#include <curses.h>
#include <term.h>
#include <sys/time.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <ctype.h>
#include <errno.h>
#include <libgfs2.h>

#define MAX_GLOCKS 20
#define MAX_LINES 6000
#define MAX_FILES 512
#define MAX_CALLTRACE_LINES 4
#define TITLE1 "glocktop - GFS2 glock monitor"
#define TITLE2 "Press <ctrl-c> or <escape> to exit"

#define COLOR_TITLE     1
#define COLOR_NORMAL    2
#define COLOR_INVERSE   3
#define COLOR_SPECIAL   4
#define COLOR_HIGHLIGHT 5
#define COLOR_OFFSETS   6
#define COLOR_CONTENTS  7
#define COLOR_HELD      8

/*	init_pair(COLOR_TITLE, COLOR_BLACK,  COLOR_CYAN);
	init_pair(COLOR_INVERSE, COLOR_BLACK,  COLOR_WHITE);
	init_pair(COLOR_NORMAL, COLOR_WHITE,  COLOR_BLACK);
	init_pair(COLOR_SPECIAL, COLOR_MAGENTA, COLOR_WHITE);
	init_pair(COLOR_HIGHLIGHT, COLOR_WHITE, COLOR_BLUE);
	init_pair(COLOR_OFFSETS, COLOR_CYAN,   COLOR_WHITE);
	init_pair(COLOR_CONTENTS, COLOR_BLUE, COLOR_WHITE);
	init_pair(COLOR_HELD, COLOR_CYAN, COLOR_BLACK);
*/

#define STR_BLACK "[\033[0;30m]"
#define STR_RED "[\033[0;31m]"
#define STR_GREEN "[\033[0;32m]"
#define STR_YELLOW "[\033[0;33m]"
#define STR_BLUE "[\033[0;34m]"
#define STR_MAGENTA "[\033[0;35m]"
#define STR_CYAN "[\033[0;36m]"
#define STR_WHITE "[\033[0;37m]"

#define BOLD_WHITE "[\033[1;37m]"

#define BKG_CYAN "[\033[46m]"
#define BKG_WHITE "[\033[47m]"
#define BKG_BLUE "[\033[44m]"

#define REFRESH_TIME    30
#define COLORS_TITLE    \
	do {						   \
		if (termlines)				   \
			attrset(COLOR_PAIR(COLOR_TITLE));  \
		else 					   \
			printf(BKG_CYAN);		   \
	} while (0)
#define COLORS_NORMAL_BOLD    \
	do {						   \
		if (termlines) {			   \
			attrset(COLOR_PAIR(COLOR_NORMAL)); \
			attron(A_BOLD);			   \
		} else {				   \
			printf(BOLD_WHITE);		   \
		}					   \
	} while (0)
#define COLORS_NORMAL    \
	do {						   \
		if (termlines) {			   \
			attrset(COLOR_PAIR(COLOR_NORMAL)); \
		} else {				   \
			printf(STR_WHITE);		   \
		}					   \
	} while (0)
#define COLORS_INVERSE_BOLD   \
	do {						    \
		if (termlines) {			    \
			attrset(COLOR_PAIR(COLOR_INVERSE)); \
			attron(A_BOLD);			    \
		} else {				    \
			printf(BKG_WHITE);		    \
		}					    \
	} while (0)
#define COLORS_INVERSE   \
	do {						    \
		if (termlines) {			    \
			attrset(COLOR_PAIR(COLOR_INVERSE)); \
		} else {				    \
			printf(BKG_WHITE);		    \
		}					    \
	} while (0)
#define COLORS_HELD   \
	do {						    \
		if (termlines) {			    \
			attrset(COLOR_PAIR(COLOR_HELD));    \
		} else {				    \
			printf(STR_CYAN);		    \
		}					    \
	} while (0)
#define COLORS_HIGHLIGHT   \
	do {						    \
		if (termlines) {			    \
			attrset(COLOR_PAIR(COLOR_HIGHLIGHT));	\
		} else {				    \
			printf(BKG_BLUE);		    \
		}					    \
	} while (0)
#define DLM_DIRTBL "/sys/kernel/config/dlm/cluster/dirtbl_size"
#define DLM_RSBTBL "/sys/kernel/config/dlm/cluster/rsbtbl_size"
#define DLM_LKBTBL "/sys/kernel/config/dlm/cluster/lkbtbl_size"

#define GFS2_MAX_META_HEIGHT	10

#define DETAILS  0x00000001
#define FRIENDLY 0x00000002

enum summary_types {
	all = 0,
	locked = 1,
	held_ex = 2,
	held_sh = 3,
	held_df = 4,
	has_waiter = 5,
	tot_waiters = 6,
	stypes = 7,
};

char *debugfs;
int termcols = 80, termlines = 30, done = 0;
unsigned glocks = 0;
const char *termtype;
WINDOW *wind;
int bufsize = 4 * 1024 * 1024;
char *glock[MAX_GLOCKS];
int iterations = 0, show_reservations = 0, iters_done = 0;
struct mount_point {
	struct mount_point *next;
	char *device;
	char *dir;
	int fd;
	struct gfs2_sb sb;
};
struct mount_point *mounts;
char dlmwlines[100][96]; /* waiters lines */
char dlmglines[MAX_LINES][97]; /* granted lines */
char contended_filenames[MAX_FILES][PATH_MAX];
unsigned long long contended_blocks[MAX_FILES];
int contended_count = 0;
int line = 0;
const char *prog_name;
char dlm_dirtbl_size[32], dlm_rsbtbl_size[32], dlm_lkbtbl_size[32];
int bsize = 0;
char print_dlm_grants = 1;
char *gbuf = NULL; /* glocks buffer */
char *gpos = NULL;
char *gnextpos = NULL;
int gmaxpos = 0;

char *dbuf = NULL; /* dlm locks buffer */
char *dpos = NULL;
char *dnextpos = NULL;
int dmaxpos = 0;
char hostname[256];

/*
 * init_colors
 */
static void init_colors(void)
{
	init_pair(COLOR_TITLE, COLOR_BLACK,  COLOR_CYAN);
	init_pair(COLOR_INVERSE, COLOR_BLACK,  COLOR_WHITE);
	init_pair(COLOR_NORMAL, COLOR_WHITE,  COLOR_BLACK);
	init_pair(COLOR_SPECIAL, COLOR_MAGENTA, COLOR_WHITE);
	init_pair(COLOR_HIGHLIGHT, COLOR_WHITE, COLOR_BLUE);
	init_pair(COLOR_OFFSETS, COLOR_CYAN,   COLOR_WHITE);
	init_pair(COLOR_CONTENTS, COLOR_BLUE, COLOR_WHITE);
	init_pair(COLOR_HELD, COLOR_CYAN, COLOR_BLACK);
}

/*
 * UpdateSize - screen size changed, so update it
 */
static void UpdateSize(int sig)
{
	static char term_buffer[2048];
	int rc;

	if (termlines) {
		termlines = 30;
		termtype = getenv("TERM");
		if (termtype == NULL)
			return;
		rc=tgetent(term_buffer,termtype);
		if (rc >= 0) {
			termlines = tgetnum((char *)"li");
			if (termlines < 10)
				termlines = 30;
			termcols = tgetnum((char *)"co");
			if (termcols < 80)
				termcols = 80;
		} else
			perror("Error: tgetent failed.");
		termlines--; /* last line is number of lines -1 */
	}
	signal(SIGWINCH, UpdateSize);
}

static int read_superblock(int fd, struct gfs2_sb *sb)
{
	ssize_t r;
	char *buf;

	ioctl(fd, BLKFLSBUF, 0);
	buf = calloc(1, GFS2_BASIC_BLOCK);
	if (buf == NULL) {
		perror("Failed to read superblock");
		return -1;
	}
	r = pread(fd, buf, GFS2_BASIC_BLOCK, GFS2_BASIC_BLOCK * GFS2_SB_ADDR);
	if (r != GFS2_BASIC_BLOCK) {
		perror("Failed to read superblock");
		free(buf);
		return -1;
	}
	gfs2_sb_in(sb, buf);
	free(buf);

	bsize = sb->sb_bsize;
	if (!bsize)
		bsize = 4096;
	return 0;
}

static void free_mounts(void)
{
	struct mount_point *mntp = mounts;

	while (mntp != NULL) {
		struct mount_point *nextp = mntp->next;

		close(mntp->fd);
		free(mntp);
		mntp = nextp;
	}
	mounts = NULL;
}

static int parse_mounts(void)
{
	struct mount_point **mntp = &mounts;
	struct mntent *mnt;
	FILE *fp;

	fp = setmntent("/proc/mounts", "r");
	if (fp == NULL) {
		perror("/proc/mounts");
		return 1;
	}
	while ((mnt = getmntent(fp)) != NULL) {
		size_t devlen = strlen(mnt->mnt_fsname);
		size_t dirlen = strlen(mnt->mnt_dir);
		struct mount_point *newmnt;

		if (strcmp(mnt->mnt_type, "debugfs") == 0) {
			if (debugfs == NULL)
				debugfs = strdup(mnt->mnt_dir);
			continue;
		}
		if (strcmp(mnt->mnt_type, "gfs2") != 0)
			continue;

		newmnt = calloc(1, sizeof(*newmnt) + devlen + 1 + dirlen + 1);
		if (newmnt == NULL) {
			perror("Failed to gather mount points");
			return 1;
		}
		newmnt->device = (char *)(newmnt + 1);
		newmnt->dir = newmnt->device + devlen + 1;
		strcpy(newmnt->device, mnt->mnt_fsname);
		strcpy(newmnt->dir, mnt->mnt_dir);
		newmnt->fd = open(newmnt->device, O_RDONLY);

		if (newmnt->fd < 0) {
			fprintf(stderr, "Failed to open gfs2 filesystem '%s': %s\n",
			        newmnt->device, strerror(errno));
			free(newmnt);
			continue;
		}
		if (read_superblock(newmnt->fd, &newmnt->sb) != 0) {
			free(newmnt);
			continue;
		}
		*mntp = newmnt;
		mntp = &newmnt->next;
	}
	endmntent(fp);
	if (debugfs == NULL) {
		debugfs = strdup("/sys/kernel/debug");
		if (!debugfs || mount("debugfs", "/sys/kernel/debug", "debugfs", 0, NULL)) {
			perror("debugfs not mounted and an attempt to mount it failed");
			free(debugfs);
			free_mounts();
			return 1;
		}
	}
	return 0;
}

/*
 * display_title_lines
 */
static void display_title_lines(void)
{
	if (termlines) {
		clear(); /* don't use Erase */
		COLORS_TITLE;
		attron(A_BOLD);
		move(0, 0);
		printw("%-80s", TITLE1);
		move(termlines, 0);
		printw("%-79s", TITLE2);
		COLORS_NORMAL_BOLD;
		move(1, 0);
	} else {
		printf("\n");
	}
	line = 1;
}

/*
 * bobgets - get a string
 * returns: 1 if user exited by hitting enter
 *          0 if user exited by hitting escape
 */
static int bobgets(char string[], int x, int y, int sz, int *ch)
{
	int finished,runningy,rc;

	if (!termlines)
		return 0;
	move(x,y);
	finished=FALSE;
	COLORS_INVERSE_BOLD;
	move(x,y);
	addstr(string);
	move(x,y);
	curs_set(2);
	refresh();
	runningy=y;
	rc=0;
	while (!finished) {
		*ch = getch();

		if(*ch < 0x0100 && isprint(*ch)) {
			char *p=string+strlen(string); // end of the string

			*(p+1)='\0';
			string[runningy-y]=*ch;
			runningy++;
			move(x,y);
			addstr(string);
			if (runningy-y >= sz) {
				rc=1;
				*ch = KEY_RIGHT;
				finished = TRUE;
			}
		}
		else {
			// special character, is it one we recognize?
			switch(*ch)
			{
			case(KEY_ENTER):
			case('\n'):
			case('\r'):
				rc=1;
				finished=TRUE;
				string[runningy-y] = '\0';
				break;
			case(KEY_CANCEL):
			case(0x01B):
				rc=0;
				finished=TRUE;
				break;
			case(KEY_DC):
			case(0x07F):
				if (runningy>=y) {
					char *p;
					p = &string[runningy - y];
					while (*p) {
						*p = *(p + 1);
						p++;
					}
					*p = '\0';
					runningy--;
					// remove the character from the string
					move(x,y);
					addstr(string);
					COLORS_NORMAL_BOLD;
					addstr(" ");
					COLORS_INVERSE_BOLD;
					runningy++;
				}
				break;
			case(KEY_BACKSPACE):
				if (runningy>y) {
					char *p;

					p = &string[runningy - y - 1];
					while (*p) {
						*p = *(p + 1);
						p++;
					}
					*p='\0';
					runningy--;
					// remove the character from the string
					move(x,y);
					addstr(string);
					COLORS_NORMAL_BOLD;
					addstr(" ");
					COLORS_INVERSE_BOLD;
				}
				break;
			default:
				move(0,70);
				printw("%08x", *ch);
				// ignore all other characters
				break;
			} // end switch on non-printable character
		} // end non-printable character
		move(line, runningy);
		refresh();
	} // while !finished
	if (sz>0)
		string[sz]='\0';
	COLORS_NORMAL_BOLD;
	return rc;
}/* bobgets */

static char *bufgets(int fd, char *bigbuf, char **nextpos, char **pos,
		     int *maxpos)
{
	if (*nextpos == NULL) {
		*maxpos = read(fd, bigbuf, bufsize - 1);
		bigbuf[bufsize - 1] = '\0';
		if (*maxpos == 0)
			return NULL;
		*pos = bigbuf;
	} else
		*pos = *nextpos;

	*nextpos = memchr(*pos, '\n', (bigbuf + *maxpos) - *pos);
	while (*nextpos && (**nextpos == '\n' || **nextpos == '\r') &&
	       *nextpos < bigbuf + (bufsize - 1)) {
		**nextpos = '\0';
		(*nextpos)++;
	}
	if (*nextpos >= bigbuf + *maxpos)
		*nextpos = NULL;
	return *pos;
}

static char *glock_number(const char *str)
{
	const char *glockid;
	char *p;
	static char id[32];

	glockid = strchr(str, '/');
	if (glockid == NULL)
		return NULL;
	glockid++;
	strncpy(id, glockid, sizeof(id));
	id[31] = '\0';
	p = strchr(id, ' ');
	if (p)
		*p = '\0';
	return id;
}

static int this_glock_requested(const char *str)
{
	const char *glockid;
	int i;

	if (!glocks)
		return 0;

	glockid = glock_number(str);
	if (glockid == NULL)
		return 0;
	for (i = 0; i < glocks; i++)
		if (!strcmp(glockid, glock[i]))
			return 1;
	return 0;
}

static int is_iopen(const char *str)
{
	char *p;

	p = strchr(str, '/');
	if (p == NULL)
		return 0;
	p--;
	if (*p == '5')
		return 1;
	return 0;
}

static int this_lkb_requested(const char *str)
{
	int i;

	if (!glocks)
		return 1;

	for (i = 0; i < glocks; i++) {
		if (strstr(str, glock[i]))
			return 1;
	}
	return 0;
}

static void eol(int col) /* end of line */
{
	if (termlines) {
		line++;
		move(line, col);
	} else {
		printf("\n");
		for (; col > 0; col--)
			printf(" ");
	}
}

void print_it(const char *label, const char *fmt, const char *fmt2, ...)
{
	va_list args;
	char tmp_string[128];

	if (!termlines || line < termlines) {
		va_start(args, fmt2);
		vsnprintf(tmp_string, 127, fmt, args);
		tmp_string[127] = '\0';

		if (termlines) {
			printw("%s", tmp_string);
			refresh();
		} else {
			printf("%s", tmp_string);
			fflush(stdout);
		}
	}
	va_end(args);
}

static void display_filename(int fd, unsigned long long block,
			     unsigned long long dirarray[256], int subdepth)
{
	struct mount_point *mp;
	int i, subs;
	char blk[32];
	DIR *dir = NULL;
	struct dirent *dent;

	for (mp = mounts; mp != NULL; mp = mp->next) {
		if (fd == mp->fd)
			break;
	}
	if (mp == NULL)
		return;
	for (i = 0; i < contended_count; i++) {
		if (contended_blocks[i] == block) {
			break;
		}
	}
	sprintf(blk, "%lld", block);
	if (i >= contended_count) {
		memset(contended_filenames[i], 0, PATH_MAX);
		strcat(contended_filenames[i], mp->dir);
		for (subs = subdepth - 2; subs >= 0; subs--) {
			dir = opendir(contended_filenames[i]);
			while ((dent = readdir(dir))) {
				if (dent->d_ino == dirarray[subs]) {
					strcat(contended_filenames[i], "/");
					strcat(contended_filenames[i],
					       dent->d_name);
					break;
				}
			}
			closedir(dir);
		}
	}

	print_it(NULL, "%s", NULL, contended_filenames[i]);
	eol(0);
}

static const char *show_inode(const char *id, int fd, unsigned long long block)
{
	struct gfs2_inode *ip;
	const char *inode_type = NULL;
	struct gfs2_sbd sbd = { .device_fd = fd, .bsize = bsize };

	ip = lgfs2_inode_read(&sbd, block);
	if (S_ISDIR(ip->i_di.di_mode)) {
		struct gfs2_inode *parent;
		unsigned long long dirarray[256];
		int subdepth = 0, error;

		inode_type = "directory ";
		dirarray[0] = block;
		subdepth++;
		/* Backtrack the directory to its source */
		while (1) {
			error = gfs2_lookupi(ip, "..", 2, &parent);
			if (error)
				break;
			/* Stop at the root inode */
			if (ip->i_di.di_num.no_addr ==
			    parent->i_di.di_num.no_addr) {
				inode_put(&parent);
				break;
			}
			inode_put(&ip);
			ip = parent;
			dirarray[subdepth++] = parent->i_di.di_num.no_addr;
		}
		display_filename(fd, block, dirarray, subdepth);
	} else if (S_ISREG(ip->i_di.di_mode)) {
		inode_type = "file ";
	} else if (S_ISLNK(ip->i_di.di_mode)) {
		inode_type = "link ";
	} else if (S_ISCHR(ip->i_di.di_mode)) {
		inode_type = "char device ";
	} else if (S_ISBLK(ip->i_di.di_mode)) {
		inode_type = "block device ";
	} else if (S_ISFIFO(ip->i_di.di_mode)) {
		inode_type = "fifo ";
	} else if (S_ISSOCK(ip->i_di.di_mode)) {
		inode_type = "socket ";
	} else
		inode_type = "file? ";
	inode_put(&ip);
	return inode_type;
}

static const char *show_details(const char *id, const char *fsname, int btype,
				int trace_dir_path)
{
	unsigned long long block = 0;
	const char *blk_type = NULL;
	struct mount_point *mp;
	FILE *dlmf;

	/* Figure out which mount point corresponds to this debugfs id */
	for (mp = mounts; mp != NULL; mp = mp->next) {
		char *p;

		p = strchr(mp->sb.sb_locktable, ':');
		if (!p)
			continue;
		p++;
		if (!strcmp(p, fsname))
			break;
	}
	if (mp == NULL)
		return "unknown";
	memset(dlm_dirtbl_size, 0, sizeof(dlm_dirtbl_size));
	memset(dlm_rsbtbl_size, 0, sizeof(dlm_rsbtbl_size));
	memset(dlm_lkbtbl_size, 0, sizeof(dlm_lkbtbl_size));
	if (!strcmp(mp->sb.sb_lockproto, "lock_dlm")) {
		char *sp;
		char *p;

		dlmf = fopen(DLM_DIRTBL, "rt");
		if (dlmf) {
			sp = fgets(dlm_dirtbl_size, sizeof(dlm_dirtbl_size), dlmf);
			if (sp == NULL)
				goto out_err;
			p = strchr(dlm_dirtbl_size, '\n');
			if (p)
				*p = '\0';
			fclose(dlmf);
		} else {
			strcpy(dlm_dirtbl_size, " ");
		}
		dlmf = fopen(DLM_RSBTBL, "rt");
		if (dlmf) {
			sp = fgets(dlm_rsbtbl_size, sizeof(dlm_rsbtbl_size), dlmf);
			if (sp == NULL)
				goto out_err;
			p = strchr(dlm_rsbtbl_size, '\n');
			if (p)
				*p = '\0';
			fclose(dlmf);
		} else {
			strcpy(dlm_rsbtbl_size, " ");
		}
		dlmf = fopen(DLM_LKBTBL, "rt");
		if (dlmf) {
			sp = fgets(dlm_lkbtbl_size, sizeof(dlm_lkbtbl_size), dlmf);
			if (sp == NULL)
				goto out_err;
			p = strchr(dlm_lkbtbl_size, '\n');
			if (p)
				*p = '\0';
			fclose(dlmf);
		} else {
			strcpy(dlm_lkbtbl_size, " ");
		}
	} else {
		strcpy(dlm_dirtbl_size, "nolock");
		strcpy(dlm_lkbtbl_size, "nolock");
		strcpy(dlm_lkbtbl_size, "nolock");
	}

	/* Read the inode in so we can see its type. */
	sscanf(id, "%llx", &block);
	if (block) {
		if (btype == 2)
			if (trace_dir_path)
				blk_type = show_inode(id, mp->fd, block);
			else
				blk_type = "";
		else
			blk_type = "";
	}
	return blk_type;
out_err:
	fclose(dlmf);
	return "error";
}

static int is_dlm_waiting(int dlmwaiters, int locktype, char *id)
{
	int i;
	int dlmid, wait_type, nodeid, type;
	char locknum[32];

	for (i = 0; i < dlmwaiters && i < 100; i++) {
		sscanf(dlmwlines[i], "%x %d %d        %d         %s",
		       &dlmid, &wait_type, &nodeid, &type, locknum);
		if ((type == locktype) && (!strcmp(locknum, id)))
			return 1;
	}
	return 0;
}

static const char *friendly_state(const char *glock_line, const char *search)
{
	const char *p;

	p = strstr(glock_line, search);

	if (p == NULL)
		return "Dazed";

	p += 2;
	if (*p == 'E')
		return "Exclusive";
	else if (*p == 'S')
		return "Shared";
	else if (*p == 'U')
		return "Unlocked";
	else if (*p == 'D')
		return "Deferred";
	else
		return "Confused";
}

static const char *friendly_gflags(const char *glock_line)
{
	static char flagout[PATH_MAX];
	const char *p;

	memset(flagout, 0, sizeof(flagout));

	p = strstr(glock_line, "f:");
	if (!p)
		return " ";
	p += 2;
	strcpy(flagout, "[");
	while (*p != ' ') {
		switch (*p) {
		case 'l':
			/*strcat(flagout, "Locked");*/
			break;
		case 'D':
			strcat(flagout, "Demoting");
			break;
		case 'd':
			strcat(flagout, "Demote pending");
			break;
		case 'p':
			strcat(flagout, "Demote in progress");
			break;
		case 'y':
			strcat(flagout, "Dirty");
			break;
		case 'f':
			strcat(flagout, "Flush");
			break;
		case 'i':
			strcat(flagout, "Invalidating");
			break;
		case 'r':
			strcat(flagout, "Reply pending");
			break;
		case 'I':
			/*strcat(flagout, "Initial");*/
			break;
		case 'F':
			strcat(flagout, "Frozen");
			break;
		case 'q':
			strcat(flagout, "Queued");
			break;
		case 'L':
			strcat(flagout, "LRU");
			break;
		case 'o':
			/*strcat(flagout, "Object present");*/
			break;
		case 'b':
			strcat(flagout, "Blocking");
			break;
		default:
			strcat(flagout, "Unknown");
			break;
		}
		if ((strlen(flagout)) > 1 && (!strchr(" lIo", *(p + 1))))
			strcat(flagout, ", ");
		p++;
	}
	strcat(flagout, "]");
	return flagout;
}

static const char *friendly_glock(const char *glock_line, char prefix)
{
	static char gline[PATH_MAX];

	if (prefix == 'W')
		sprintf(gline, "Is:%s, Want:%s   %s",
			friendly_state(glock_line, "s:"),
			friendly_state(glock_line, "t:"),
			friendly_gflags(glock_line));
	else
		sprintf(gline, "Held:%s   %s",
			friendly_state(glock_line, "s:"),
			friendly_gflags(glock_line));
	return gline;
}

static const char *dlm_grtype(int grmode)
{
	const char *dlm_types[8] = {"NL", "CR", "CW", "PR", "PW", "EX",
				    "NA", "NA"};

	if (grmode < 0)
		return "-1";
	return dlm_types[grmode & 0x07];
}

static const char *dlm_status(int status)
{
	const char *dlm_statuses[4] = {"Unknown", "Waiting", "Granted",
				       "Converting"};
	if (status < 0)
		return "unknown";
	return dlm_statuses[status & 0x03];
}

static const char *dlm_nodeid(int lkbnodeid)
{
	static char nodeid[16];

	if (lkbnodeid == 0)
		return "this node";
	sprintf(nodeid, "node %d", lkbnodeid);
	return nodeid;
}

static const char *getprocname(int ownpid)
{
	char fn[1024];
	static char str[80];
	const char *procname;
	FILE *fp;

	sprintf(fn, "/proc/%d/status", ownpid);
	fp = fopen(fn, "r");
	if (fp == NULL)
		return "ended";

	if (fgets(str, 80, fp) != NULL) {
		char *p;

		procname = str + 6;
		p = strchr(procname, '\n');
		if (p)
			*p = '\0';
	} else
		procname = "unknown";

	fclose(fp);
	return procname;
}

static void show_dlm_grants(int locktype, const char *g_line, int dlmgrants,
			    int summary)
{
	int i;
	char dlm_resid[75];
	unsigned int lkb_id, lkbnodeid, remid, ownpid, exflags, flags, status;
	unsigned int grmode, rqmode, nodeid, length;
	unsigned long long xid, us;
	char trgt_res_name[64], res_name[64], *p1, *p2;
	const char *procname;

	p1 = strchr(g_line, '/');
	if (!p1)
		return;
	p1++;
	p2 = strchr(p1, ' ');
	if (!p2)
		return;
	memset(trgt_res_name, 0, sizeof(trgt_res_name));
	memcpy(trgt_res_name, p1, p2 - p1);
	sprintf(dlm_resid, "%8d%16s", locktype, trgt_res_name);
	for (i = 0; i < dlmgrants; i++) {
/*
lkb_id  n   remid  pid x e f s g rq u n ln res_name 1234567890123456
1100003 1 2ae0006 8954 0 0 0 2 5 -1 0 1 24 "       2           102ab"
2a20001 1 30d0001 8934 0 0 0 2 3 -1 0 1 24 "       5           102ab"
  b0001 2  860001 8868 0 0 10000 2 3 -1 0 0 24 "       1               2"
2450001 2 1be0002 8962 0 0 10000 1 -1 5 12214 0 24 "       2           102ab"
*/
		p1 = strchr(dlmglines[i], '\"');
		if (!p1)
			continue;
		p1++;
		if (strncmp(dlm_resid, p1, 24))
			continue;

		sscanf(dlmglines[i], "%x %d %x %u %llu %x %x %d %d %d %llu "
		       "%u %d \"%24s\"\n",
		       &lkb_id, &lkbnodeid, &remid, &ownpid, &xid, &exflags,
		       &flags, &status, &grmode, &rqmode, &us, &nodeid,
		       &length, res_name);
		if (status == 1) { /* Waiting */
			if (!lkbnodeid)
				procname = getprocname(ownpid);
			else
				procname = "";
			if (summary)
				print_it(NULL, " (", NULL);
			else
				print_it(NULL, "  D: ", NULL);
			print_it(NULL, "%s for %s, pid %d %s", NULL,
				 dlm_status(status), dlm_nodeid(lkbnodeid),
				 ownpid, procname);
			if (summary)
				print_it(NULL, ")", NULL);
		} else if (grmode == 0) {
			continue; /* ignore "D: Granted NL on node X" */
		} else {
			procname = getprocname(ownpid);
			if (summary)
				print_it(NULL, " (", NULL);
			else
				print_it(NULL, "  D: ", NULL);
			print_it(NULL, "%s %s on %s to pid %d %s", NULL,
				 dlm_status(status), dlm_grtype(grmode),
				 dlm_nodeid(lkbnodeid), ownpid, procname);
			if (summary)
				print_it(NULL, ")", NULL);
		}
		if (!summary)
			eol(0);
	}
}

static void print_call_trace(const char *hline)
{
	char *p, *pid, tmp[32], stackfn[64], str[96];
	FILE *fp;
	int i;

	p = strchr(hline, 'p');
	if (!p)
		return;
	pid = p + 2;
	p = strchr(pid, ' ');
	if (!p)
		return;
	memset(tmp, 0, sizeof(tmp));
	memcpy(tmp, pid, p - pid);
	sprintf(stackfn, "/proc/%s/stack", tmp);
	fp = fopen(stackfn, "rt");
	if (fp == NULL)
		return;
	for (i = 0; i < MAX_CALLTRACE_LINES; i++) {
		if (fgets(str, sizeof(str) - 1, fp) == NULL)
			break;
		if (strstr(str, "gfs2_glock_")) { /* skip lines we don't
						      care about*/
			i--;
			continue;
		}
		p = strchr(str, '\n');
		if (p)
			*p = '\0';
		p = strchr(str, ']');
		if (p)
			p += 2;
		else
			p = str;
		print_it(NULL, "  C:              %s ", NULL, p);
		eol(0);
	}
	fclose(fp);
}

static int is_ex(const char *hline)
{
	if (strncmp(hline, " H: s:EX ", 9) == 0)
		return 1;
	return 0;
}

static int has_holder_flag(const char *hline, char flag)
{
	const char *p;

	p = strchr(hline, 'f');
	if (p == NULL)
		return 0;
	p++;
	if (*p != ':')
		return 0;
	p++;
	while (*p != '\0') {
		if (*p == ' ')
			return 0;
		if (*p == flag)
			return 1;
		p++;
	}
	return 0;
}

static int is_holder(const char *hline)
{
	return has_holder_flag(hline, 'H');
}

static int is_waiter(const char *hline)
{
	return has_holder_flag(hline, 'W');
}

static int get_lock_type(const char *str)
{
	const char *p;

	p = strchr(str, '/');
	return (p ? (*(p - 1)) - '0' : 0);
}

static long long get_demote_time(const char *str)
{
	char *p;
	char tmp[80];

	p = strchr(str, '/');
	if (p == NULL)
		return 0;
	p++;
	p = strchr(p, '/');
	if (p == NULL)
		return 0;
	p++;
	strncpy(tmp, p, 79);
	tmp[79] = '\0';
	p = strchr(tmp, ' ');
	if (p == NULL)
		return 0;
	*p = '\0';
	return atoll(tmp);
}

static const char *pid_string(const char *str)
{
	char *p;
	static char pidstr[80];

	memset(pidstr, 0, sizeof(pidstr));
	p = strchr(str, 'p');
	if (p) {
		strncpy(pidstr, p + 2, sizeof(pidstr));
		pidstr[79] = '\0';
		p = strchr(pidstr, ']');
		if (p) {
			p++;
			*p = '\0';
		}
	}
	return pidstr;
}

/* If this glock is relevant, return 0, else the reason it's irrelevant */
static int irrelevant(const char *holder, const char *glockstr)
{
	int lock_type = get_lock_type(glockstr);

	/* Exclude shared and locks */
	if (!is_ex(holder))
		return 1;
	/* Exclude locks held at mount time: statfs*/
	if (strstr(holder, "init_per_node"))
		return 2;
	if (strstr(holder, "init_journal"))
		return 3;
	if (strstr(holder, "init_inodes"))
		return 4;
	if (strstr(holder, "fill_super"))
		return 5;
	if (lock_type == 9) /* Exclude journal locks */
		return 6;
	return 0;
}

static const char *reason(int why)
{
	const char *reasons[] = {"(N/A:------)",  /* 0 */
				 "(N/A:Not EX)",  /* 1 */
				 "(N/A:System)",  /* 2 */
				 "(N/A:journl)",  /* 3 */
				 "(N/A:System)",  /* 4 */
				 "(N/A:System)",  /* 5 */
				 "(N/A:Journl)"}; /* 6 */

	return reasons[why];
}

static void print_friendly_prefix(char one_glocks_lines[MAX_LINES][97])
{
	int why = irrelevant(one_glocks_lines[1], one_glocks_lines[0]);

	if (why)
		print_it(NULL, "  U: %s ", NULL, reason(why));
	else
		print_it(NULL, "  U: ", NULL);
}

static void show_glock(char one_glocks_lines[MAX_LINES][97], int gline,
		       const char *fsname, int dlmwaiters, int dlmgrants,
		       int trace_dir_path, int prev_had_waiter, int flags,
		       int summary)
{
	int i, locktype = 0;
	char id[33], *p;
	char extras[80], prefix = '\0';
	long long demote_time = 0;
	const char *ltype[] = {"N/A", "non-disk", "inode", "rgrp", "meta",
			       "i_open", "flock", "posix lock", "quota",
			       "journal"};

	if (termlines) {
		if (irrelevant(one_glocks_lines[1], one_glocks_lines[0]))
			COLORS_HELD;
		else
			COLORS_NORMAL;
	}
	if (!gline)
		return;

	memset(extras, 0, sizeof(extras));
	p = strchr(one_glocks_lines[0], '/');
	memset(id, 0, sizeof(id));

	if (p) {
		locktype = get_lock_type(one_glocks_lines[0]);
		demote_time = get_demote_time(one_glocks_lines[0]);
		p++;
		strncpy(id, p, sizeof(id) - 1);
		id[sizeof(id) - 1] = '\0';
		p = strchr(id, ' ');
		if (p)
			*p = '\0';

		if (locktype != 2) {
			strncpy(extras, ltype[locktype], 79);
			extras[79] = '\0';
		} else {
			const char *i_type = show_details(id, fsname, 2,
							  trace_dir_path);
			sprintf(extras, "%sinode", i_type);
		}
	}
	if (flags & DETAILS) {
		print_it(NULL, " %s ", NULL, one_glocks_lines[0]);
		print_it(NULL, "(%s)", NULL, extras);
		if (demote_time)
			print_it(NULL, " ** demote time is greater than 0 **",
				 NULL);
		eol(0);
		if (dlmgrants)
			show_dlm_grants(locktype, one_glocks_lines[0],
					dlmgrants, 0);
	}
	if (flags & FRIENDLY) {
		print_friendly_prefix(one_glocks_lines);
		for (i = 1; i < gline; i++) {
			if (one_glocks_lines[i][0] == ' ' &&
			    one_glocks_lines[i][1] == 'H' &&
			    prefix != 'W')
				prefix = (is_holder(one_glocks_lines[i]) ?
					  'H' : 'W');
		}
		print_it(NULL, " %c %-10.10s %-9.9s %s", NULL, prefix,
			 extras, id, friendly_glock(one_glocks_lines[0],
						    prefix));
		eol(0);
	}
	for (i = 1; i < gline; i++) {
		if (!show_reservations &&
		    one_glocks_lines[i][0] == ' ' &&
		    one_glocks_lines[i][2] == 'B' &&
		    one_glocks_lines[i][3] == ':')
			continue;

		if (flags & DETAILS) {
			print_it(NULL, " %-80.80s", NULL, one_glocks_lines[i]);
			eol(0);
			continue;
		}
		if ((flags & FRIENDLY) &&
		    one_glocks_lines[i][1] == 'H')
			print_friendly_prefix(one_glocks_lines);

		if (one_glocks_lines[i][0] == ' ' &&
		    one_glocks_lines[i][1] == 'H') {
			print_it(NULL, " %c ---> %s pid %s ", NULL,
				 prefix, (is_holder(one_glocks_lines[i]) ?
					  "held by" : "waiting"),
				 pid_string(one_glocks_lines[i]));
			if (demote_time)
				print_it(NULL, "** demote time is non-"
					 "zero ** ", NULL);
			if (is_dlm_waiting(dlmwaiters, locktype, id)) {
				print_it(NULL, "***** DLM is in a "
					 "comm wait for this lock "
					 "***** ", NULL);
			}
			show_dlm_grants(locktype, one_glocks_lines[0],
					dlmgrants, 1);
			eol(0);
			print_call_trace(one_glocks_lines[i]);
		}
	}
}

static int parse_dlm_waiters(FILE *dlm, const char *fsname)
{
	int dlml = 0;

	memset(dlmwlines, 0, sizeof(dlmwlines));
	while (fgets(dlmwlines[dlml], 80, dlm))
		dlml++;

	return dlml;
}

static int parse_dlm_grants(int dlmfd, const char *fsname)
{
	int dlml = 0;
	char *dlmline;

	memset(dlmglines, 0, sizeof(dlmglines));
	dnextpos = NULL;
	while ((dlmline = bufgets(dlmfd, dbuf, &dnextpos, &dpos, &dmaxpos))) {
		if (!this_lkb_requested(dlmline))
			continue;
		strncpy(dlmglines[dlml], dlmline, 96);
		dlmglines[dlml][96] = '\0';
		dlml++;
		if (dlml >= MAX_LINES)
			break;
	}
	return dlml;
}

static void print_summary(int total_glocks[11][stypes], int dlmwaiters)
{
	int i;
	int total_unlocked = 0;
	const struct {
		const char *name;
		const int width;
	} column[] = {
		{ "unknown", 7 }, { "nondisk", 7}, { "inode", 8 }, { "rgrp", 7 },
		{ "meta", 4 }, { "iopen", 7 }, { "flock", 7 }, { "p", 1 },
		{ "quota", 5 }, { "jrnl", 4 }, { "Total", 8 }
	};
	const int ncols = sizeof(column) / sizeof(column[0]);

	/* Print column headers */
	print_it(NULL, "S     glocks ", NULL);
	for (i = 1; i < ncols; i++)
		if (i != 7 && i != 4) /* Ignore plock and meta */
			print_it(NULL, "%*s ", NULL, column[i].width, column[i].name);
	eol(0);
	print_it(NULL, "S  --------- ", NULL);
	for (i = 1; i < ncols; i++)
		if (i != 7 && i != 4) /* Ignore plock and meta */
			print_it(NULL, "%*s ", NULL, column[i].width, "--------");
	eol(0);

	/* Print rows */
	print_it(NULL, "S  Unlocked: ", NULL);
	for (i = 1; i < (ncols - 1); i++) {
		if (i != 7 && i != 4) /* Ignore plock and meta */
			print_it(NULL, "%*d ", NULL, column[i].width,
				 total_glocks[i][all] - total_glocks[i][locked]);
		total_unlocked += total_glocks[i][all] -
			total_glocks[i][locked];
	}
	print_it(NULL, "%*d ", NULL, column[i].width, total_unlocked);
	eol(0);
	print_it(NULL, "S    Locked: ", NULL);
	for (i = 1; i < ncols; i++) {
		if (i != 7 && i != 4) /* Ignore plock and meta */
			print_it(NULL, "%*d ", NULL, column[i].width,
				 total_glocks[i][locked]);
		total_glocks[10][locked] += total_glocks[i][locked];
	}
	eol(0);
	print_it(NULL, "S     Total: ", NULL);
	for (i = 1; i < ncols; i++) {
		if (i != 7 && i != 4) /* Ignore plock and meta */
			print_it(NULL, "%*d ", NULL, column[i].width,
				 total_glocks[i][all]);
		total_glocks[10][all] += total_glocks[i][all];
	}
	eol(0);
	print_it(NULL, "S", NULL);
	eol(0);
	print_it(NULL, "S   Held EX: ", NULL);
	for (i = 1; i < ncols; i++) {
		if (i != 7 && i != 4) /* Ignore plock and meta */
			print_it(NULL, "%*d ", NULL, column[i].width,
				 total_glocks[i][held_ex]);
		total_glocks[10][held_ex] += total_glocks[i][held_ex];
	}
	eol(0);
	print_it(NULL, "S   Held SH: ", NULL);
	for (i = 1; i < ncols; i++) {
		if (i != 7 && i != 4) /* Ignore plock and meta */
			print_it(NULL, "%*d ", NULL, column[i].width,
				 total_glocks[i][held_sh]);
		total_glocks[10][held_sh] += total_glocks[i][held_sh];
	}
	eol(0);
	print_it(NULL, "S   Held DF: ", NULL);
	for (i = 1; i < ncols; i++) {
		if (i != 7 && i != 4) /* Ignore plock and meta */
			print_it(NULL, "%*d ", NULL, column[i].width,
				 total_glocks[i][held_df]);
		total_glocks[10][held_df] += total_glocks[i][held_df];
	}
	eol(0);
	print_it(NULL, "S G Waiting: ", NULL);
	for (i = 1; i < ncols; i++) {
		if (i != 7 && i != 4) /* Ignore plock and meta */
			print_it(NULL, "%*d ", NULL, column[i].width,
				 total_glocks[i][has_waiter]);
		total_glocks[10][has_waiter] += total_glocks[i][has_waiter];
	}
	eol(0);
	print_it(NULL, "S P Waiting: ", NULL);
	for (i = 1; i < ncols; i++) {
		if (i != 7 && i != 4) /* Ignore plock and meta */
			print_it(NULL, "%*d ", NULL, column[i].width,
				 total_glocks[i][tot_waiters]);
		total_glocks[10][tot_waiters] += total_glocks[i][tot_waiters];
	}
	eol(0);
	print_it(NULL, "S  DLM wait: %7d", NULL, dlmwaiters);
	eol(0);
	eol(0);
}

/* flags = DETAILS || FRIENDLY or both */
static void glock_details(int fd, const char *fsname, int dlmwaiters,
			  int dlmgrants, int trace_dir_path, int show_held,
			  int summary)
{
	char *ln, *p;
	char one_glocks_lines[MAX_LINES][97];
	int gline = 0;
	int show_prev_glock = 0, prev_had_waiter = 0;
	int total_glocks[11][stypes], locktype = 0;
	int holders_this_glock_ex = 0;
	int holders_this_glock_sh = 0;
	int holders_this_glock_df = 0;
	int waiters_this_glock = 0;

	memset(total_glocks, 0, sizeof(total_glocks));
	gnextpos = NULL;
	while ((ln = bufgets(fd, gbuf, &gnextpos, &gpos, &gmaxpos))) {
		if (ln[0] == ' ' && ln[1] == ' ' && ln[2] == ' ')
			continue;
		if (ln[0] == 'G') {
			/* Summary stuff------------------------------------ */
			if (waiters_this_glock) {
				total_glocks[locktype][tot_waiters] +=
					waiters_this_glock;
				total_glocks[locktype][has_waiter]++;
			}
			if (holders_this_glock_ex)
				total_glocks[locktype][held_ex]++;
			if (holders_this_glock_sh)
				total_glocks[locktype][held_sh]++;
			if (holders_this_glock_df)
				total_glocks[locktype][held_df]++;
			locktype = get_lock_type(ln);
			p = ln + 6;
			if (*p != 'U' || *(p + 1) != 'N')
				total_glocks[locktype][locked]++;
			total_glocks[locktype][all]++;
			holders_this_glock_ex = 0;
			holders_this_glock_sh = 0;
			holders_this_glock_df = 0;
			waiters_this_glock = 0;
			/* Detail stuff------------------------------------- */
			if (show_prev_glock) {
				show_glock(one_glocks_lines, gline, fsname,
					   dlmwaiters, dlmgrants,
					   trace_dir_path, prev_had_waiter,
					   DETAILS, summary);
				show_glock(one_glocks_lines, gline, fsname,
					   dlmwaiters, dlmgrants,
					   trace_dir_path, prev_had_waiter,
					   FRIENDLY, summary);
				memset(one_glocks_lines, 0,
				       sizeof(one_glocks_lines));
				show_prev_glock = 0;
			}
			prev_had_waiter = 0;
			gline = 0;
			if (this_glock_requested(ln))
				show_prev_glock = 1;
		} else if (ln[0] == ' ' && ln[1] == 'H') {
			char *flag = strchr(ln, 'f');
			char *mode = strchr(ln, 's');

			/* Summary stuff------------------------------------ */
			while (flag) {
				flag++;
				switch (*flag) {
				case ':':
					break;
				case 'W':
					waiters_this_glock++;
					flag = NULL;
					break;
				case 'H':
					flag = NULL;
					if (mode == NULL)
						holders_this_glock_df++;
					else if (*(mode + 1) == ':' &&
						 *(mode + 2) == 'E' &&
						 *(mode + 3) == 'X')
						holders_this_glock_ex++;
					else if (*(mode + 1) == ':' &&
						 *(mode + 2) == 'S' &&
						 *(mode + 3) == 'H')
						holders_this_glock_sh++;
					else
						holders_this_glock_df++;
					break;
				case ' ':
					flag = NULL;
					break;
				default:
					break;
				};
			}
			/* Detail stuff------------------------------------- */
			if (!glocks) {
				int haswaiter = is_waiter(ln);

				if (haswaiter) {
					show_prev_glock = 1;
					prev_had_waiter = 1;
				} else if (show_held && is_holder(ln) &&
					   !is_iopen(one_glocks_lines[0])) {
					show_prev_glock = 1;
				} else if (!irrelevant(ln, one_glocks_lines[0])) {
					show_prev_glock = 1;
				}
			}
		}
		/* Detail stuff--------------------------------------------- */
		strncpy(one_glocks_lines[gline], ln, 96);
		one_glocks_lines[gline][96] = '\0';
		gline++;
		if (gline >= MAX_LINES)
			break;
		if (termlines && line >= termlines)
			break;
	}
	/* Detail stuff----------------------------------------------------- */
	if (show_prev_glock && gline < MAX_LINES &&
	    (!termlines || line < termlines)) {
		show_glock(one_glocks_lines, gline, fsname, dlmwaiters,
			   dlmgrants, trace_dir_path, prev_had_waiter,
			   DETAILS, summary);
		show_glock(one_glocks_lines, gline, fsname, dlmwaiters,
			   dlmgrants, trace_dir_path, prev_had_waiter,
			   FRIENDLY, summary);
	}
	if (!summary || ((iters_done % summary) != 0))
		return;

	print_summary(total_glocks, dlmwaiters);
}

static void show_help(int help)
{
	if (help == 1) {
		COLORS_NORMAL;
		eol(0);
		print_it(NULL, "  Glock flags:                 ", NULL);
		eol(0);
		print_it(NULL, "    l - Locked                 ", NULL);
		print_it(NULL, "    r - Reply pending          ", NULL);
		eol(0);
		print_it(NULL, "    d - Demote pending         ", NULL);
		print_it(NULL, "    I - Initial                ", NULL);
		eol(0);
		print_it(NULL, "    D - Demote requested       ", NULL);
		print_it(NULL, "    F - Frozen                 ", NULL);
		eol(0);
		print_it(NULL, "    p - Demote in progress     ", NULL);
		print_it(NULL, "    q - Queued holder          ", NULL);
		eol(0);
		print_it(NULL, "    y - Dirty data             ", NULL);
		print_it(NULL, "    L - LRU                    ", NULL);
		eol(0);
		print_it(NULL, "    f - Flush                  ", NULL);
		print_it(NULL, "    o - Object present         ", NULL);
		eol(0);
		print_it(NULL, "    i - Invalidating           ", NULL);
		print_it(NULL, "    b - Blocking request       ", NULL);
		eol(0);
	} else if (help == 2) {
		COLORS_NORMAL;
		eol(0);
		print_it(NULL, "  Holder flags:                ", NULL);
		eol(0);
		print_it(NULL, "    t - Try (non-blocking)     ", NULL);
		print_it(NULL, "    E - Exact lock             ", NULL);
		eol(0);
		print_it(NULL, "    T - Try with callback      ", NULL);
		print_it(NULL, "    c - No Cache lock          ", NULL);
		eol(0);
		print_it(NULL, "    e - No exp                 ", NULL);
		print_it(NULL, "    H - Held (locked)          ", NULL);
		eol(0);
		print_it(NULL, "    A - Any lock               ", NULL);
		print_it(NULL, "    W - Waiting for lock       ", NULL);
		eol(0);
		print_it(NULL, "    p - Priority lock          ", NULL);
		print_it(NULL, "    a - Asynchronous lock      ", NULL);
		eol(0);
		print_it(NULL, "    F - First                  ", NULL);
		eol(0);
	}
}

/* flags = DETAILS || FRIENDLY or both */
static void parse_glocks_file(int fd, const char *fsname, int dlmwaiters,
			      int dlmgrants, int trace_dir_path,
			      int show_held, int help, int summary)
{
	char fstitle[96], fsdlm[105];
	char ctimestr[64];
	time_t t;
	int i;

	tzset();
	t = time(NULL);
	strftime(ctimestr, 64, "%a %b %d %T %Y", localtime(&t));
	ctimestr[63] = '\0';
	memset(fstitle, 0, sizeof(fstitle));
	memset(fsdlm, 0, sizeof(fsdlm));
	sprintf(fstitle, "@ %.22s       %s ", fsname, ctimestr);
	if (dlmwaiters) {
		sprintf(fsdlm, "dlm: %s/%s/%s [", dlm_dirtbl_size,
			dlm_rsbtbl_size, dlm_lkbtbl_size);
		for (i = 0; i < dlmwaiters; i++)
			strcat(fsdlm, "*");
		for (; i < 10; i++)
			strcat(fsdlm, " ");
		strcat(fsdlm, "]");
	}
	attron(A_BOLD);
	print_it(NULL, "%s @%s %s", NULL, fstitle, hostname, fsdlm);
	eol(0);
	attroff(A_BOLD);
	glock_details(fd, fsname, dlmwaiters, dlmgrants, trace_dir_path,
		      show_held, summary);

	show_help(help);
	if (termlines)
		refresh();
}

static void usage(void)
{
	printf("Usage:\n");
	printf("glocktop [-i] [-d <delay sec>] [-n <iter>] [-sX] [-c] [-D] [-H] [-r] [-t]\n");
	printf("\n");
	printf("-i : Runs glocktop in interactive mode.\n");
	printf("-d : delay between refreshes, in seconds (default: %d).\n", REFRESH_TIME);
	printf("-n : stop after <iter> refreshes.\n");
	printf("-H : don't show Held glocks, even if not waited on, excluding "
	       "iopen\n");
	printf("-r : show reservations when rgrp glocks are displayed\n");
	printf("-s : show glock summary information every X iterations\n");
	printf("-t : trace directory glocks back\n");
	printf("-D : don't show DLM lock status\n");
	printf("\n");
	fflush(stdout);
	exit(0);
}

int main(int argc, char **argv)
{
	int fd;
	DIR *dir = NULL;
	char *fn;
	struct dirent *dent;
	int retval;
	int refresh_time = REFRESH_TIME;
	fd_set readfds;
	char string[96];
	int ch, dlmwaiters = 0, dlmgrants = 0;
	int cont = TRUE, optchar;
	int trace_dir_path = 0;
	int show_held = 1, help = 0;
	int interactive = 0;
	int summary = 10;
	int nfds = STDIN_FILENO + 1;

	prog_name = argv[0];
	memset(glock, 0, sizeof(glock));
	memset(contended_filenames, 0, sizeof(contended_filenames));
	memset(contended_blocks, 0, sizeof(contended_blocks));
	UpdateSize(0);
	/* decode command line arguments */
	while (cont) {
		optchar = getopt(argc, argv, "-d:Dn:rs:thHi");

		switch (optchar) {
		case 'd':
			refresh_time = atoi(optarg);
			if (refresh_time < 1) {
				fprintf(stderr, "Error: delay %d too small; "
					"must be at least 1\n", refresh_time);
				exit(-1);
			}
			break;
		case 'D':
			print_dlm_grants = 0;
			break;
		case 'n':
			iterations = atoi(optarg);
			break;
		case 'r':
			show_reservations = 1;
			break;
		case 's':
			summary = atoi(optarg);
			break;
		case 't':
			trace_dir_path = 1;
			break;
		case 'h':
			usage();
			break;
		case 'H':
			show_held = 0; /* held, but not iopen held */
			break;
		case 'i':
			interactive = 1;
			break;
		case EOF:
			cont = FALSE;
			break;
		case 1:
			if  (optarg && glocks < MAX_GLOCKS)
				glock[glocks++] = optarg;
			break;

		default:
			fprintf(stderr, "unknown option: %c\n", optchar);
			exit(-1);
		};
	}

	if (interactive) {
		printf("Initializing. Please wait...");
		fflush(stdout);
	}
	if (gethostname(hostname, sizeof(hostname))) {
		fprintf(stderr, "Error: unable to determine host name.\n");
		exit(-1);
	}
	if (parse_mounts())
		exit(-1);

	if (interactive && (wind = initscr()) == NULL) {
		fprintf(stderr, "Error: unable to initialize screen.\n");
		exit(-1);
	}

	if (interactive) {
		/* Do our initial screen stuff: */
		signal(SIGWINCH, UpdateSize); /* handle term resize signal */
		UpdateSize(0); /* update screen size based on term settings */
		clear(); /* don't use Erase */
		start_color();
		noecho();
		keypad(stdscr, TRUE);
		raw();
		curs_set(0);
		init_colors();
	} else {
		termlines = 0;
	}
	while (!gbuf) {
		gbuf = malloc(bufsize);
		if (gbuf) {
			/*printf("bufsize=%dK\n", bufsize / 1024);*/
			break;
		}
		bufsize /= 2;
	}
	while (!dbuf) {
		dbuf = malloc(bufsize);
		if (dbuf) {
			/*printf("bufsize=%dK\n", bufsize / 1024);*/
			break;
		}
		bufsize /= 2;
	}

	while (!done) {
		struct timeval tv;

		if (asprintf(&fn, "%s/gfs2/", debugfs) == -1) {
			perror(argv[0]);
			exit(-1);
		}
		dir = opendir(fn);
		free(fn);

		if (!dir) {
			if (interactive) {
				refresh();
				endwin();
			}
			fprintf(stderr, "Unable to open gfs2 debugfs directory.\n");
			fprintf(stderr, "Check if debugfs and gfs2 are mounted.\n");
			exit(-1);
		}
		display_title_lines();
		while ((dent = readdir(dir))) {
			const char *fsname;
			char *dlm_fn;
			FILE *dlmf;
			int dlmfd;

			if (!strcmp(dent->d_name, "."))
				continue;
			if (!strcmp(dent->d_name, ".."))
				continue;

			fsname = strchr(dent->d_name, ':');
			if (fsname)
				fsname++;
			else
				fsname = dent->d_name;

			if (asprintf(&dlm_fn, "%s/dlm/%s_waiters", debugfs, fsname) == -1) {
				perror("Failed to construct dlm waiters debugfs path");
				exit(-1);
			}
			dlmf = fopen(dlm_fn, "rt");
			if (dlmf) {
				dlmwaiters = parse_dlm_waiters(dlmf, fsname);
				fclose(dlmf);
			}
			free(dlm_fn);

			if (print_dlm_grants) {
				if (asprintf(&dlm_fn, "%s/dlm/%s_locks", debugfs, fsname) == -1) {
					perror("Failed to construct dlm locks debugfs path");
					exit(-1);
				}
				dlmfd = open(dlm_fn, O_RDONLY);
				if (dlmfd > 0) {
					dlmgrants = parse_dlm_grants(dlmfd,
								     fsname);
					close(dlmfd);
				}
				free(dlm_fn);
			}

			if (asprintf(&fn, "%s/gfs2/%s/glocks", debugfs, dent->d_name) == -1) {
				perror(argv[0]);
				exit(-1);
			}
			fd = open(fn, O_RDONLY);
			if (fd < 0) {
				if (interactive) {
					refresh();
					endwin();
				}
				perror(fn);
				free(fn);
				exit(-1);
			}
			free(fn);
			parse_glocks_file(fd, fsname, dlmwaiters, dlmgrants,
					  trace_dir_path, show_held, help,
					  summary);
			close(fd);
		}
		closedir(dir);
		tv.tv_sec = refresh_time;
		tv.tv_usec = 0;
		FD_ZERO(&readfds);
		if (nfds != 0)
			FD_SET(STDIN_FILENO, &readfds);
		retval = select(nfds, &readfds, NULL, NULL, &tv);
		if (retval) {
			if (interactive)
				ch = getch();
			else
				ch = getchar();
			switch (ch) {
			case 0x1b: /* mount wheel? */
			case 0x03:
			case 'q':
				done = 1;
				break;
			case 'h':
				help = (help + 1) % 3;
				break;
			case 's':
				if (!interactive)
					break;
				move(1, 0);
				printw("Change delay from %d to: ",
				       refresh_time);
				if (bobgets(string, 1, 25, 5, &ch) == 1)
					refresh_time = atoi(string);
				if (refresh_time < 1)
					refresh_time = 1;
				break;
			/* When we get EOF on stdin, remove it from the fd_set
			   to avoid shorting out the select() */
			case EOF:
				nfds = 0;
				break;
			}
		}
		iters_done++;
		if (iterations && iters_done >= iterations)
			break;
	}
	free_mounts();
	free(gbuf);
	free(dbuf);
	free(debugfs);
	if (interactive) {
		refresh();
		endwin();
	}
	exit(0);
}
