/* * w.c v1.4 * * An alternative "w" program for Linux. * Shows users and their processes. * * Copyright (c) Dec 1993, Oct 1994 Steve "Mr. Bassman" Bryant * bassman@hpbbi30.bbn.hp.com (Old address) * bassman@muttley.soc.staffs.ac.uk * * Info: * I starting writing as an improvement of the w program included * with linux. The idea was to add in some extra functionality to the * program, and see if I could fix a couple of bugs which seemed to * occur. * Mr. Bassman, 10/94 * * Acknowledgments: * * The original version of w: * Copyright (c) 1993 Larry Greenfield (greenfie@gauss.rutgers.edu) * * Uptime routine and w mods: * Michael K. Johnson (johnsonm@stolaf.edu) * * * Distribution: * This program is freely distributable under the terms of copyleft. * No warranty, no support, use at your own risk etc. * * Compilation: * gcc -O -o w sysinfo.c whattime.c w.c * * Usage: * w [-hfusd] [user] * * * $Log: tmp-junk.c,v $ * Revision 1.1 2002/02/01 22:46:37 csmall * Initial revision * * Revision 1.5 1994/10/26 17:57:35 bassman * Loads of stuff - see comments. * * Revision 1.4 1994/01/01 12:57:21 johnsonm * Added RCS, and some other fixes. * * Revision history: * Jan 01, 1994 (mkj): Eliminated GCC warnings, took out unnecessary * dead variables in fscanf, replacing them with * *'d format qualifiers. Also added RCS stuff. * Oct 26, 1994 (bass): Tidied up the code, fixed bug involving corrupt * utmp records. Added switch for From field; * default is compile-time set. Added -d option * as a remnant from BSD 'w'. Fixed bug so it now * behaves if the first process on a tty isn't owned * by the person first logged in on that tty, and * also detects su'd users. Changed the tty format * to the short one. */ #include #include #include #include #include #include #include #include #include #include #include #include "proc/whattime.h" #define TRUE 1 #define FALSE 0 /* * Default setting for whether to have a From field. The -f switch * toggles this - if the default is to have it, using -f will turn * it off; if the default is not to have it, the -f switch will put * it in. Possible values are TRUE (to have the field by default), * and FALSE. */ #define DEFAULT_FROM TRUE #define ZOMBIE "" void put_syntax(); char *idletime(); char *logintime(); static char rcsid[]="$Id: tmp-junk.c,v 1.1 2002/02/01 22:46:37 csmall Exp $"; void main (argc, argv) int argc; char *argv[]; { int header=TRUE, long_format=TRUE, ignore_user=TRUE, from_switch=DEFAULT_FROM, show_pid=FALSE, line_length; int i, j; struct utmp *utmp_rec; struct stat stat_rec; struct passwd *passwd_entry; uid_t uid; char username[9], tty[13], rhost[17], login_time[27]; char idle_time[7], what[1024], pid[10]; char out_line[1024], file_name[256]; char search_name[9]; int jcpu, pcpu, tpgid, curr_pid, utime, stime, cutime, cstime; char /*ch,*/ state, comm[1024], *columns_ptr; FILE *fp; search_name[0] = '\0'; /* * Process the command line */ if (argc > 1) { /* * Args that start with '-' */ for (i = 1; ((i < argc) && (argv[i][0] == '-')); i ++) { for (j = 1; argv[i][j] != '\0'; j++) { switch (argv[i][j]) { case 'h': header = FALSE; break; case 's': long_format = FALSE; break; case 'u': ignore_user = FALSE; break; case 'd': show_pid = TRUE; break; case 'f': if (DEFAULT_FROM == TRUE) from_switch = FALSE; else from_switch = TRUE; break; default: fprintf (stderr, "w: unknown option: '%c'\n", argv[i][j]); put_syntax (); break; } } } /* * Check for arg not starting with '-' (ie: username) */ if (argc > i) { strncpy (search_name, argv[i], 8); search_name[8] = '\0'; i ++; if (argc > i) { fprintf (stderr, "w: syntax error\n"); put_syntax (); } } } /* * Check that /proc is actually there, or else we can't * get all the information. */ if (chdir ("/proc")) { fprintf (stderr, "w: fatal error: cannot access /proc\n"); perror (strerror(errno)); exit (-1); } /* * Find out our screen width from $COLUMNS */ columns_ptr = getenv ("COLUMNS"); if (columns_ptr == NULL) { struct winsize window; /* * Try getting it directly */ if ((ioctl (1, TIOCGWINSZ, &window) != 1) && (window.ws_col > 0)) line_length = window.ws_col; else line_length = 80; /* Default length assumed */ } else line_length = atoi (columns_ptr); /* * Maybe we should check whether there is enough space on * the lines for the options selected... */ if (line_length < 60) long_format = FALSE; line_length --; /* * Print whatever headers */ if (header == TRUE) { /* * uptime: from MKJ's uptime routine, * found in whattime.c */ print_uptime(); /* * Print relevant header bits */ printf ("User tty "); if (long_format == TRUE) { if (from_switch == TRUE) printf ("From "); printf (" login@ idle JCPU PCPU "); if (show_pid == TRUE) printf (" PID "); printf ("what\n"); } else { printf (" idle "); if (show_pid == TRUE) printf (" PID "); printf ("what\n"); } } /* * Process user information. */ while ((utmp_rec = getutent())) { /* * Check we actually want to see this record. * It must be a valid active user process, * and match a specified search name. */ if ( (utmp_rec->ut_type == USER_PROCESS) && (strcmp(utmp_rec->ut_user, "")) && ( (search_name[0] == '\0') || ( (search_name[0] != '\0') && !strncmp(search_name, utmp_rec->ut_user, 8) ) ) ) { /* * Get the username */ strncpy (username, utmp_rec->ut_user, 8); username[8] = '\0'; /* Set end terminator */ /* * Find out the uid of that user (from their * passwd entry) */ uid = -1; if ((passwd_entry = getpwnam (username)) != NULL) { uid = passwd_entry->pw_uid; } /* * Get (and clean up) the tty line */ for (i = 0; (utmp_rec->ut_line[i] > 32) && (i < 6); i ++) tty[i] = utmp_rec->ut_line[i]; utmp_rec->ut_line[i] = '\0'; tty[i] = '\0'; /* * Don't bother getting info if it's not asked for */ if (long_format == TRUE) { /* * Get the remote hostname; this can be up to 16 chars, * but if any chars are invalid (ie: [^a-zA-Z0-9\.]) * then the char is changed to a string terminator. */ if (from_switch == TRUE) { strncpy (rhost, utmp_rec->ut_host, 16); rhost[16] = '\0'; } /* * Get the login time * (Calculated by LG's routine, below) */ strcpy (login_time, logintime(utmp_rec->ut_time)); } /* * Get the idle time. * (Calculated by LG's routine, below) */ strcpy (idle_time, idletime (tty)); /* * That's all the info out of /etc/utmp. * The rest is more difficult. We use the pid from * utmp_rec->ut_pid to look in /proc for the info. * NOTE: This is not necessarily the active pid, so we chase * down the path of parent -> child pids until we find it, * according to the information given in /proc//stat. */ sprintf (pid, "%d", utmp_rec->ut_pid); what[0] = '\0'; strcpy (file_name, pid); strcat (file_name, "/stat"); jcpu = 0; pcpu = 0; if ((fp = fopen(file_name, "r"))) { while (what[0] == '\0') { /* * Check /proc//stat to see if the process * controlling the tty is the current one */ fscanf (fp, "%d %s %c %*d %*d %*d %*d %d " "%*u %*u %*u %*u %*u %d %d %d %d", &curr_pid, comm, &state, &tpgid, &utime, &stime, &cutime, &cstime); fclose (fp); if (comm[0] == '\0') strcpy (comm, "-"); /* * Calculate jcpu and pcpu. * JCPU is the time used by all processes and their * children, attached to the tty. * PCPU is the time used by the current process * (calculated once after the loop, using last * obtained values). */ if (!jcpu) jcpu = cutime + cstime; /* * Check for a zombie first... */ if (state == 'Z') strcpy (what, ZOMBIE); else if (curr_pid == tpgid) { /* * If it is the current process, read cmdline * If that's empty, then the process is swapped out, * or is a zombie, so we use the command given in stat * which is in normal round brackets, ie: "()". */ strcpy (file_name, pid); strcat (file_name, "/cmdline"); if ((fp = fopen(file_name, "r"))) { i = 0; j = fgetc (fp); while ((j != EOF) && (i < 256)) { if (j == '\0') j = ' '; what[i] = j; i++; j = fgetc (fp); } what[i] = '\0'; fclose (fp); } if (what[0] == '\0') strcpy (what, comm); } else { /* * Check out the next process * If we can't open it, use info from this process, * so we have to check out cmdline first. * * If we're not using "-u" then should we just * say "-" (or "-su") instead of a command line ? * If so, we should strpcy(what, "-"); when we * fclose() in the if after the stat() below. */ strcpy (file_name, pid); strcat (file_name, "/cmdline"); if ((fp = fopen (file_name, "r"))) { i = 0; j = fgetc (fp); while ((j != EOF) && (i < 256)) { if (j == '\0') j = ' '; what[i] = j; i++; j = fgetc (fp); } what[i] = '\0'; fclose (fp); } if (what[0] == '\0') strcpy (what, comm); /* * Now we have something in the what variable, * in case we can't open the next process. */ sprintf (pid, "%d", tpgid); strcpy (file_name, pid); strcat (file_name, "/stat"); fp = fopen (file_name, "r"); if (fp && (ignore_user == FALSE)) { /* * We don't necessarily go onto the next process, * unless we are either ignoring who the effective * user is, or it's the same uid */ stat (file_name, &stat_rec); /* * If the next process is not owned by this * user finish the loop. */ if (stat_rec.st_uid != uid) { fclose (fp); strcpy (what, "-su"); /* * See comment above somewhere; I've used * "-su" here, as the next process is owned * by someone else; this is generally * because the user has done an "su" which * then exec'd something else. */ } else what[0] = '\0'; } else if (fp) /* else we are ignoring uid's */ what[0] = '\0'; } } } else /* Could not open first process for user */ strcpy (what, "?"); /* * There is a bug somewhere in my version of linux * which means that utmp records are not cleaned * up properly when users log out. However, we * can detect this, by the users first process * not being there when we look in /proc. */ /* * Don't output a line for "dead" users. * This gets round a bug which doesn't update utmp/wtmp * when users log out. */ if (what[0] != '?') { #ifdef 0 /* This makes unix98 pty's not line up, so has been disabled - JEH. */ /* * Remove the letters 'tty' from the tty id */ if (!strncmp (tty, "tty", 3)) { for (i = 3; tty[i - 1] != '\0'; i ++) tty[i - 3] = tty[i]; } #endif /* * Common fields */ sprintf (out_line, "%-9.8s%-6.7s ", username, tty); /* * Format the line for output */ if (long_format == TRUE) { /* * Calculate CPU usage */ pcpu = utime + stime; jcpu /= 100; pcpu /= 100; if (from_switch == TRUE) sprintf (out_line, "%s %-16.15s", out_line, rhost); sprintf (out_line, "%s%8.8s ", out_line, login_time); } sprintf (out_line, "%s%6s", out_line, idle_time); if (long_format == TRUE) { if (!jcpu) strcat (out_line, " "); else if (jcpu/60) sprintf (out_line, "%s%3d:%02d", out_line, jcpu/60, jcpu%60); else sprintf (out_line, "%s %2d", out_line, jcpu); if (!pcpu) strcat (out_line, " "); else if (pcpu/60) sprintf (out_line, "%s%3d:%02d", out_line, pcpu/60, pcpu%60); else sprintf (out_line, "%s %2d", out_line, pcpu); } if (show_pid == TRUE) sprintf (out_line, "%s %5.5s", out_line, pid); strcat (out_line, " "); strcat (out_line, what); /* * Try not to exceed the line length */ out_line[line_length] = '\0'; printf ("%s\n", out_line); } } } } /* * put_syntax() * * Routine to print the correct syntax to call this program, * and then exit out appropriately */ void put_syntax () { fprintf (stderr, "usage: w [-hfsud] [user]\n"); exit (-1); } /* * idletime() * * Routine which returns a string containing * the idle time of a given user. * * This routine was lifted from the original w program * by Larry Greenfield (greenfie@gauss.rutgers.edu) * Copyright (c) 1993 Larry Greenfield * */ char *idletime (tty) char *tty; { struct stat terminfo; unsigned long idle; char ttytmp[40]; static char give[20]; time_t curtime; curtime = time (NULL); sprintf (ttytmp, "/dev/%s", tty); stat (ttytmp, &terminfo); idle = (unsigned long) curtime - (unsigned long) terminfo.st_atime; if (idle >= (60 * 60)) /* more than an hour */ { if (idle >= (60 * 60 * 48)) /* more than two days */ sprintf (give, "%2ludays", idle / (60 * 60 * 24)); else sprintf (give, " %2lu:%02u", idle / (60 * 60), (unsigned) ((idle / 60) % 60)); } else { if (idle / 60) sprintf (give, "%6lu", idle / 60); else give[0]=0; } return give; } /* * logintime() * * Returns the time given in a suitable format * * This routine was lifted from the original w program * by Larry Greenfield (greenfie@gauss.rutgers.edu) * Copyright (c) 1993 Larry Greenfield * */ #undef ut_time char *logintime(ut_time) time_t ut_time; { time_t curtime; struct tm *logintime, *curtm; int hour, am, curday, logday; static char give[20]; static char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; curtime = time(NULL); curtm = localtime(&curtime); curday = curtm->tm_yday; logintime = localtime(&ut_time); hour = logintime->tm_hour; logday = logintime->tm_yday; am = (hour < 12); if (!am) hour -= 12; if (hour == 0) hour = 12; /* * This is a newer behavior: it waits 12 hours and the next day, and then * goes to the 2nd time format. This should reduce confusion. * It then waits only 6 days (not till the last moment) to go the last * time format. */ if ((curtime > (ut_time + (60 * 60 * 12))) && (logday != curday)) { if (curtime > (ut_time + (60 * 60 * 24 * 6))) sprintf(give, "%2d%3s%2d", logintime->tm_mday, month[logintime->tm_mon], (logintime->tm_year % 100)); else sprintf(give, "%*s%2d%s", 3, weekday[logintime->tm_wday], hour, am ? "am" : "pm"); } else sprintf(give, "%2d:%02d%s", hour, logintime->tm_min, am ? "am" : "pm"); return give; }