/*
 yawho 1.0 (Yet Another Who)
 Simple program similar to 'w'. I wrote this because sometimes it is 
 good to know which users uses ssh and which uses telnet.
 
 It shows you:
 	DAEMON - name of the daemon which was used to connect to your machine
	USER   
	TTY
	SHP (SHELL PID)
	       - pid of the login shell (just in case you're an admin and want
		 to kill someone's session ;-) 
	FROM
 	WHAT
	
 You have to have read access to utmp and proc/.
 
 compile: gcc whod.c -o whod
 
 author: Michal Suszycki, mike@wizard.ae.krakow.pl
 	 http://wizard.ae.krakow.pl/~mike
*/ 


#include <utmp.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/ioctl.h>

#ifndef UTMP_FILE
#define UTMP_FILE "/var/run/utmp"
#endif

// Find parent pid (in /proc/pid/stat)

int get_ppid(int pid)
{
    	static int ppid;
    	char buff[256];
    	FILE *f;
    	sprintf(buff,"/proc/%d/stat",pid);
    	if (!(f = fopen(buff,"rt")))
    		return (-1);
    	fscanf(f,"%*d %*s %*c %d",&ppid);
    	fclose(f);	
    	return ppid;
}

// For given pid find appropriate name (in /proc/pid/stat)

char *get_name(int pid)
{
    	static char buf[256];
    	FILE *f;
    	sprintf(buf,"/proc/%d/stat",pid);
    	if (!(f = fopen(buf,"rt")))
    		return "can't read";
    	fscanf(f,"%*d %s",buf);
	fclose(f);
	return buf;
}

char *get_cmdline(int pid)
{
        static char buff[512];
        FILE *f;
        int i = 0;
        sprintf(buff,"/proc/%d/cmdline",pid);
        if (!(f = fopen(buff,"rt")))
                return "-";
        while (fread(buff+i,1,1,f) == 1){
	        if (buff[i] == '\0') buff[i] = ' ';
                if (i == 512 - 2) break;
                i++;
        }
        buff[i] = '\0';
        fclose(f);
        return buff;
}

/* get process group ID of the process which currently owns the tty
   and return it's command line.
*/

char *get_command(int pid)
{
	static char buf[256];
	FILE *f;
	static int tpgid;
		
        sprintf(buf,"/proc/%d/stat",pid);
        if (!(f = fopen(buf,"r"))) return "-";
        fscanf(f,"%*d %*s %*c %*d %*d %*d %*d %d",&tpgid);
        fclose(f);
        return get_cmdline(tpgid);
}

struct utmp ent;

void main(int argc, char **argv)
{
	int all = 0 ,ssh_users , telnet_users, local_users;
    	int fd, pid, ppid, i, longopt = 0;
    	char login[UT_NAMESIZE + 1], *parent;    
	struct winsize win;
	
	void *tab[] = { 
		     "(sshd)",   	&ssh_users,
		     "(sshd1)", 	&ssh_users,
		     "(in.telnetd)", 	&telnet_users,
		     "(init)", 		&local_users
		    };
	if (argc == 2 && !strcmp(argv[1],"-l")) 
		longopt = 1;
	else if (argc >= 2){
		fprintf(stderr,"Unknown option.\nUsage: yawho [-l]\n"
			"-l\tLong display (don't cut processes' names)\n");
		exit (0);
	}
	if (ioctl(1,TIOCGWINSZ,&win) != -1 && win.ws_col < 80){
		fprintf(stderr,"\t####### WARNING #######\n");
		fprintf(stderr,"Not enough columns. Output could be unreadable.\n");
	}
	
	for (i = 0 ; i < 8 ; i += 2)
		*(int *)tab[i+1] = 0;
	
    	if ((fd = open(UTMP_FILE ,O_RDONLY)) == -1) {
		perror("open"); 
		exit (0);
	}

    	printf ("DAEMON        USER      TTY    SHP    FROM              WHAT\n");
    
    	while (read(fd,&ent,sizeof(ent))){
		strncpy(login,ent.ut_user,UT_NAMESIZE);
		login[UT_NAMESIZE] = '\0';	
		pid = ent.ut_pid;
	
		if (ent.ut_type == 7){
			ppid = get_ppid(pid);
			parent = get_name(ppid);
	    
			for (i = 0; i < 8 ; i += 2)
				if (!strcmp((char *) tab[i], parent))
					( *(int *)tab[i+1] )++;
					
			printf("%-13.13s %-9.9s %-6.6s %-6d %-17.17s ",
	    			parent,login,ent.ut_line,pid,ent.ut_host);
			if (longopt) 
				printf("%-24s\n",get_command(pid));
			else 
				printf("%-24.24s\n",get_command(pid));
	    		all++;
		}
    	}
	close(fd);
	printf("\n%d user%s",all,all?"s ":" ");
	printf ("(%d local, %d telnet, %d ssh, %d other)\n",
	local_users,telnet_users ,ssh_users, all-telnet_users-ssh_users-local_users);
}