/* lsmodules
 * ------------------------------------
 * shows some informations about loaded
 * modules.
 *
 * usage:  ./lsm [-i module_name|*] [-h]
 *
 *		    -( trace@dump.cz )-
 */

#include <stdio.h>
#include <linux/module.h>

#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

#include <string.h>
#include <stdlib.h>

#include <errno.h>


#define PAGESIZE	4096
#define SYS_GETPID	20
#define EM		"\e[1m"
#define NORM		"\e[0m"

int rkm(int fd, void *data, unsigned long offset, int size)
{
	if (lseek(fd, offset, SEEK_SET) != offset) return 0;
	if (read(fd, data, size) != size) return 0;
	return size;
}

void showflags(unsigned long flag) 
{
	char *name = malloc(14);
	bzero(name, 14);

	if (flag & MOD_UNINITIALIZED)
		printf("UNINITIALIZED  ");
	if (flag & MOD_RUNNING)
		printf("RUNNING  ");
	if (flag & MOD_DELETED)
		printf("DELETED  ");
	if (flag & MOD_INITIALIZING)
		printf("INITIALIZING  ");
	if (flag & MOD_JUST_FREED)
		printf("JUST_FREED  ");
	if (flag & MOD_AUTOCLEAN)
		printf("AUTOCLEAN  ");
	if (flag & MOD_AUTOCLEAN)
		printf("VISITED  ");
	if (flag & MOD_AUTOCLEAN)
		printf("RUN_ONCE  ");
	printf("\n");
}

/* checks if module flags is valid.. return value 1 means true, 0 false */
int check_flags(unsigned long flags) 
{
	int ret = 1;	
	return ret;
}

int main (int argc, char **argv) 
{
	int kmem;
	char info = 0;
	unsigned addr;
	unsigned long num;
	char name[128] = {0};
	char modname[126] = {0};
	char last[128] = {0};
	struct module mod;
	int opt;

	while((opt = getopt(argc, argv, "i:h")) != -1) {
		switch (opt) {
			case 'i':
				info = 1;
				snprintf(modname, sizeof(modname), "%s", optarg);
				break;
			case 'h':
				printf("Usage:  ./lsm [-i module_name] [-h]\n");
				break;
		}
	}
	
	addr = (0xc4000000 / PAGESIZE) * PAGESIZE;
	kmem = open("/dev/kmem", O_RDWR);

	if (!info)
		printf(EM"%-15s used     address    size  "NORM"\n", "name");

	while (addr < 0xe0000000) { 
		rkm(kmem, &num, addr, sizeof(num));
		if (num == sizeof(struct module)) {
			rkm(kmem, &mod, addr, sizeof(mod));
			if (!check_flags(mod.flags)) continue;
			if (rkm(kmem, &name, (unsigned)mod.name, sizeof(name)) != sizeof(name)) 
				continue;
			if (!strcmp(last, name)) break;
		
			if (!info)
				printf("%-15s%4d   0x%x   %5u\n", name, mod.uc.usecount.counter, addr, mod.size);
			else if (!strcmp(name, modname) || *modname == '*') {
				struct module_symbol ms;
				printf("-[ "EM"%s"NORM" ]----------------\n", name);
				printf("Address\t\t0x%x\n", addr);
				printf("Size\t\t%d\n", mod.size);
				printf("Used by\t\t%d\n", mod.uc.usecount.counter);
				printf("Flags\t\t");
				showflags(mod.flags);
				rkm(kmem, &ms, (unsigned)mod.syms, sizeof(ms));
				rkm(kmem, &name, (unsigned)ms.name, sizeof(name));
				printf("First symbol\t`%s' at 0x%x\n", name, ms.value);
				printf("Number of exported symbols: %d\n\n", mod.nsyms);
			}
			
			memcpy(last, name, sizeof(last));
			bzero(name, sizeof(name));
		}
		addr += PAGESIZE;
	}
	
	close(kmem);				
	return 0;
}

