File: w.bassman.c

package info (click to toggle)
w-bassman 1.0-17
  • links: PTS
  • area: main
  • in suites: woody
  • size: 68 kB
  • ctags: 11
  • sloc: ansic: 370; makefile: 40; sh: 14
file content (736 lines) | stat: -rw-r--r-- 16,078 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
/*
 * 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: w2.c,v $
 * 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <time.h>
#include <utmp.h>
#include <unistd.h>
#include <errno.h>
#include <pwd.h>
#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		"<zombie>"


void put_syntax();
char *idletime();
char *logintime();

static char rcsid[]="$Id: w.c,v 1.4 1994/10/26 17:57:35 bassman Exp $";


int 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[UT_LINESIZE], 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 < UT_LINESIZE); 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)
	     */
	    if (strchr(tty,':')) {	/* idle unknown for xdm logins */
		    strcpy(idle_time," ?xdm? ");
		    /* 
		     * Also, trim the hostname down to just what's before 
		     * the first '.'
		     */
		    if (strchr(tty,'.')) {
			    *(strchr(tty,'.')) = '\0';
		    }
	    }
	    else
		    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/<pid>/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/<pid>/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] != '?')
	    {
#if 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.6s ", 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;
}