/* CS305 ASN 2 * Dan J. Fraser (1219229) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include <../src/kernel/const.h> #include <../src/kernel/type.h> #include <../src/kernel/proc.h> #undef printf /* kernel's const.h defined this */ #include <../src/mm/mproc.h> #include <../src/fs/fproc.h> #include <../src/fs/const.h> #undef printf /* fs's const.h defined this */ /* ------- INCLUDES FOR WHO COMMAND ------- */ /* - also, includes for the uptime syscall */ #define nil 0 #include #include #include /* needed for uptime syscall */ #include /* needed for uptime syscall */ /* -------- GLOBALS FOR WHO COMMAND ------------*/ char PATH_UTMP[] = "/etc/utmp"; /* Macro to convert memory offsets to rounded kilo-units */ #define off_to_k(off) ((unsigned) (((off) + 512) / 1024)) /* Number of tasks and processes. */ int nr_tasks, nr_procs; /* Process tables of the kernel, MM, and FS. */ struct proc *ps_proc; struct mproc *ps_mproc; struct fproc *ps_fproc; /* Where is INIT? */ int init_proc_nr; #define low_user init_proc_nr #define Z_STATE 1 #define KMEM_PATH "/dev/kmem" /* opened for kernel proc table */ #define MEM_PATH "/dev/mem" /* opened for mm/fs + user processes */ int kmemfd, memfd; /* file descriptors of [k]mem */ struct pstat { /* structure filled by pstat() */ dev_t ps_dev; /* major/minor of controlling tty */ uid_t ps_ruid; /* real uid */ uid_t ps_euid; /* effective uid */ pid_t ps_pid; /* process id */ pid_t ps_ppid; /* parent process id */ int ps_pgrp; /* process group id */ int ps_flags; /* kernel flags */ int ps_mflags; /* mm flags */ int ps_ftask; /* (possibly pseudo) fs suspend task */ vir_bytes ps_tsize; /* text size (in bytes) */ vir_bytes ps_dsize; /* data size (in bytes) */ vir_bytes ps_ssize; /* stack size (in bytes) */ phys_bytes ps_vtext; /* virtual text offset */ phys_bytes ps_vdata; /* virtual data offset */ phys_bytes ps_vstack; /* virtual stack offset */ phys_bytes ps_text; /* physical text offset */ phys_bytes ps_data; /* physical data offset */ phys_bytes ps_stack; /* physical stack offset */ int ps_state; int ps_recv; /* process number to receive from */ time_t ps_utime; /* accumulated user time */ time_t ps_stime; /* accumulated system time */ char *ps_args; /* concatenated argument string */ vir_bytes ps_procargs; /* initial stack frame from MM */ }; _PROTOTYPE(int main, (int argc, char *argv [])); _PROTOTYPE(char *get_args, (struct pstat *bufp )); _PROTOTYPE(int pstat, (int p_nr, struct pstat *bufp )); _PROTOTYPE(int addrread, (int fd, phys_clicks base, vir_bytes addr, char *buf, int nbytes )); _PROTOTYPE(void usage, (char *pname )); _PROTOTYPE(void err, (char *s )); /* Main interprets arguments, gets system addresses, opens [k]mem, reads in * process tables from kernel/mm/fs and calls pstat() for relevant entries. */ int main(argc, argv) int argc; char *argv[]; { int i,j; /* loop counters */ struct pstat buf; int db_fd; int uid = getuid(); /* real uid of caller */ char *ke_path; /* paths of kernel, */ char *mm_path; /* mm, */ char *fs_path; /* and fs used in ps -U */ struct psinfo psinfo; /* WHO GLOBALS! */ char *tmp= PATH_UTMP; /* path to utmp file */ FILE *f; struct utmp ut; /* structure for utmp data */ struct tm *tm; /* structure for time information from utmp */ int slot, wtmp= 0, once= 0; /* Dan's variables */ pid_t current_parent; /* keep track of who's who */ int final_child, status; /* flags for process search */ long uptime; /* shows how long the system has been up */ int num_users; /* used to count the number of users */ int days, minutes, hours; /* these make it easier to pretty-print the uptime */ char pretty_time[100]; /* ditto, a temp string */ time_t tp; /* time structure used for time of day */ static message uptime_mess; /* stores result from TIMES syscall */ /* Open memory devices and get PS info from the kernel */ if ((kmemfd = open(KMEM_PATH, O_RDONLY)) == -1) err(KMEM_PATH); if ((memfd = open(MEM_PATH, O_RDONLY)) == -1) err(MEM_PATH); if (ioctl(memfd, MIOCGPSINFO, (void *) &psinfo) == -1) err("can't get PS info from kernel"); nr_tasks = psinfo.nr_tasks; nr_procs = psinfo.nr_procs; /* Allocate memory for process tables */ ps_proc = (struct proc *) malloc((nr_tasks + nr_procs) * sizeof(ps_proc[0])); ps_mproc = (struct mproc *) malloc(nr_procs * sizeof(ps_mproc[0])); ps_fproc = (struct fproc *) malloc(nr_procs * sizeof(ps_fproc[0])); if (ps_proc == NULL || ps_mproc == NULL || ps_fproc == NULL) err("Out of memory"); /* Get kernel process table */ if (addrread(kmemfd, (phys_clicks) 0, psinfo.proc, (char *) ps_proc, (nr_tasks + nr_procs) * sizeof(ps_proc[0])) != (nr_tasks + nr_procs) * sizeof(ps_proc[0])) err("Can't get kernel proc table from /dev/kmem"); /* Get mm/fs process tables */ if (addrread(memfd, ps_proc[nr_tasks + MM_PROC_NR].p_map[D].mem_phys, psinfo.mproc, (char *) ps_mproc, nr_procs * sizeof(ps_mproc[0])) != nr_procs * sizeof(ps_mproc[0])) err("Can't get mm proc table from /dev/mem"); if (addrread(memfd, ps_proc[nr_tasks + FS_PROC_NR].p_map[D].mem_phys, psinfo.fproc, (char *) ps_fproc, nr_procs * sizeof(ps_fproc[0])) != nr_procs * sizeof(ps_fproc[0])) err("Can't get fs proc table from /dev/mem"); /* We need to know where INIT hangs out. */ for (i = FS_PROC_NR; i < nr_procs; i++) { if (strcmp(ps_proc[nr_tasks + i].p_name, "INIT") == 0) break; } init_proc_nr = i; /************ * This is where we find the number of users logged into * the system at this time. just iterate through utmp and * count. ************/ /* open utmp for reading... */ if ((f= fopen(tmp, "r")) == nil) { fprintf(stderr, "w: can't open %s\n", tmp); exit(1); } num_users = 0; /* loop over the contents and count the number of real users */ while (fread((char *) &ut, sizeof(ut), 1, f) == 1) { if (!wtmp && ut.ut_name[0] == 0) continue; num_users++; } fclose(f); /* and close */ /**************** * This is where we figure out the important times: boot time, * current time. ****************/ tp = time(0); /* get current time of day */ strftime(pretty_time,sizeof(pretty_time), "%I:%M%p", localtime(&tp)); /* 0:00AM */ status = _syscall(FS,TIMES,&uptime_mess); /* ask for TIMES info from the kernel */ uptime = uptime_mess.m4_l5 / HZ; /* it just happens to have BOOT_TICKS in there. */ days = (int) uptime / (60*60*24); uptime = uptime % (60*60*24); hours = (int) uptime / (60*60); uptime = uptime % (60*60); minutes = (int) uptime / 60; /* print the information line */ printf("%8s up %d day(s), %2d:%02d, %d users.\n",pretty_time,days, hours, minutes ,num_users); /***************** * This is the meat. We re-open (I know, I know) utmp for read, * we iterate through it. For each real user entry, we figure out * when he logged on, and what he's running. *****************/ if ((f= fopen(tmp, "r")) == nil) { fprintf(stderr, "w: can't open %s\n", tmp); exit(1); } /* print the header */ printf("User tty login@ idle CPU what\n"); while (fread((char *) &ut, sizeof(ut), 1, f) == 1) { if (!wtmp && ut.ut_name[0] == 0) continue; /* Now loop through process table and handle each entry */ final_child=0; /* flag for loop exit */ current_parent = 1; /* everybody's parent is INIT. */ while (!final_child) { /* if we're not finished...*/ final_child = 1; /* if nothing happens to change this, we're done */ for (i = -nr_tasks; i < nr_procs; i++) { /* loop over process table */ if (pstat(i, &buf) != -1 ) { /* and if we have a valid process */ if (ut.ut_pid == buf.ps_pgrp) { /* check to see if it's our users' */ if (buf.ps_ppid == current_parent) { /* if it is, and it's a child */ current_parent = buf.ps_pid; /* set out new parent */ j = i; /* make a note */ final_child = 0; /* and go again! */ } } } } } if (pstat(j, &buf) != -1) { struct tm *login_time; pretty_time[0]=0; /* pretty-format the login time */ strftime(pretty_time,sizeof(pretty_time), "%I:%M%p", localtime(&ut.ut_time)); /* print out the users' line */ printf("%-8s %-11s %8s %5s %2d:%02d %s\n", ut.ut_user, /* username */ ut.ut_line, /* terminal */ pretty_time, /* login time */ "?:??", /* idle time */ (buf.ps_utime + buf.ps_stime) / HZ / 60, /* cpu minutes */ (buf.ps_utime + buf.ps_stime) / HZ % 60, /* cpu seconds */ buf.ps_args); } else printf("something's odd with pstat\n"); } return(0); } char *get_args(bufp) struct pstat *bufp; { int nargv; int cnt; /* # of bytes read from stack frame */ int neos; /* # of '\0's seen in argv string space */ phys_bytes iframe; long l; char *cp, *args; static union stack { vir_bytes stk_i; char *stk_cp; char stk_c; } stk[ARG_MAX / sizeof(char *)]; union stack *sp; /* Phys address of the original stack frame. */ iframe = bufp->ps_procargs - bufp->ps_vstack + bufp->ps_stack; /* Calculate the number of bytes to read from user stack */ l = (phys_bytes) bufp->ps_ssize - (iframe - bufp->ps_stack); if (l > ARG_MAX) l = ARG_MAX; cnt = l; /* Get cnt bytes from user initial stack to local stack buffer */ if (lseek(memfd, (off_t) iframe, 0) < 0) return NULL; if ( read(memfd, (char *)stk, cnt) != cnt ) return NULL; sp = stk; nargv = (int) sp[0].stk_i; /* number of argv arguments */ /* See if argv[0] is with the bytes we read in */ l = (long) sp[1].stk_cp - (long) bufp->ps_procargs; if ( ( l < 0 ) || ( l > cnt ) ) return NULL; /* l is the offset of the argv[0] argument */ /* change for concatenation the '\0' to space, for nargv elements */ args = &((char *) stk)[(int)l]; neos = 0; for (cp = args; cp < &((char *) stk)[cnt]; cp++) if (*cp == '\0') if (++neos >= nargv) break; else *cp = ' '; if (cp == args) return NULL; *cp = '\0'; return args; } /* Pstat collects info on process number p_nr and returns it in buf. * It is assumed that tasks do not have entries in fproc/mproc. */ int pstat(p_nr, bufp) int p_nr; struct pstat *bufp; { int p_ki = p_nr + nr_tasks; /* kernel proc index */ if (p_nr < -nr_tasks || p_nr >= nr_procs) return -1; if ((ps_proc[p_ki].p_flags & P_SLOT_FREE) && !(ps_mproc[p_nr].mp_flags & IN_USE)) return -1; bufp->ps_flags = ps_proc[p_ki].p_flags; if (p_nr >= low_user) { bufp->ps_dev = ps_fproc[p_nr].fp_tty; bufp->ps_ftask = ps_fproc[p_nr].fp_task; } else { bufp->ps_dev = 0; bufp->ps_ftask = 0; } if (p_nr >= low_user) { bufp->ps_ruid = ps_mproc[p_nr].mp_realuid; bufp->ps_euid = ps_mproc[p_nr].mp_effuid; bufp->ps_pid = ps_mproc[p_nr].mp_pid; bufp->ps_ppid = ps_mproc[ps_mproc[p_nr].mp_parent].mp_pid; bufp->ps_pgrp = ps_mproc[p_nr].mp_procgrp; bufp->ps_mflags = ps_mproc[p_nr].mp_flags; } else { bufp->ps_pid = bufp->ps_ppid = 0; bufp->ps_ruid = bufp->ps_euid = 0; bufp->ps_pgrp = 0; bufp->ps_mflags = 0; } if (p_nr >= low_user) { if (ps_mproc[p_nr].mp_flags & HANGING) { bufp->ps_state = Z_STATE; /* zombie */ } else bufp->ps_state = 0; } bufp->ps_tsize = (size_t) ps_proc[p_ki].p_map[T].mem_len << CLICK_SHIFT; bufp->ps_dsize = (size_t) ps_proc[p_ki].p_map[D].mem_len << CLICK_SHIFT; bufp->ps_ssize = (size_t) ps_proc[p_ki].p_map[S].mem_len << CLICK_SHIFT; bufp->ps_vtext = (off_t) ps_proc[p_ki].p_map[T].mem_vir << CLICK_SHIFT; bufp->ps_vdata = (off_t) ps_proc[p_ki].p_map[D].mem_vir << CLICK_SHIFT; bufp->ps_vstack = (off_t) ps_proc[p_ki].p_map[S].mem_vir << CLICK_SHIFT; bufp->ps_text = (off_t) ps_proc[p_ki].p_map[T].mem_phys << CLICK_SHIFT; bufp->ps_data = (off_t) ps_proc[p_ki].p_map[D].mem_phys << CLICK_SHIFT; bufp->ps_stack = (off_t) ps_proc[p_ki].p_map[S].mem_phys << CLICK_SHIFT; bufp->ps_recv = ps_proc[p_ki].p_getfrom; bufp->ps_utime = ps_proc[p_ki].user_time; bufp->ps_stime = ps_proc[p_ki].sys_time; bufp->ps_procargs = ps_mproc[p_nr].mp_procargs; if (bufp->ps_state == Z_STATE) bufp->ps_args = ""; else if (p_nr > init_proc_nr) bufp->ps_args = get_args(bufp); return 0; } /* Addrread reads nbytes from offset addr to click base of fd into buf. */ int addrread(fd, base, addr, buf, nbytes) int fd; phys_clicks base; vir_bytes addr; char *buf; int nbytes; { #if (MACHINE == SUN) base = 0; /* Addresses in kernel/mm/fs are ALWAYS physical in smx */ #endif if (lseek(fd, ((off_t) base << CLICK_SHIFT) + addr, 0) < 0) return -1; return read(fd, buf, nbytes); } void usage(pname) char *pname; { fprintf(stderr, "Usage: %s [-][alx]\n", pname); exit(1); } void err(s) char *s; { extern int errno; if (errno == 0) fprintf(stderr, "who: %s\n", s); else fprintf(stderr, "who: %s: %s\n", s, strerror(errno)); exit(2); }