2 * Copyright (c) 1984 through 2008, William LeFebvre
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
16 * * Neither the name of William LeFebvre nor the names of other
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * top - a top users display for Unix
36 * SYNOPSIS: 2.x with thread eliding
39 * This is the machine-dependent module for Linux 2.x that elides threads
42 * CFLAGS: -DHAVE_GETOPT -DHAVE_STRERROR -DORDER
46 * AUTHOR: Richard Henderson <rth@tamu.edu>
47 * Order support added by Alexey Klimkin <kad@klon.tme.mcst.ru>
48 * Ported to 2.4 by William LeFebvre
49 * Thread eliding by William LeFebvre
54 #include <sys/types.h>
68 #include <sys/param.h> /* for HZ */
69 #include <asm/page.h> /* for PAGE_SHIFT */
72 #include <linux/proc_fs.h> /* for PROC_SUPER_MAGIC */
74 #define PROC_SUPER_MAGIC 0x9fa0
81 #define PROCFS "/proc"
84 /*=PROCESS INFORMATION==================================================*/
93 unsigned long size
, rss
; /* in k */
96 unsigned long start_time
;
98 unsigned long start_code
;
99 unsigned long end_code
;
100 unsigned long start_stack
;
101 unsigned int threads
;
103 struct top_proc
*next
;
107 /*=STATE IDENT STRINGS==================================================*/
109 #define NPROCSTATES 7
110 static char *state_abbrev
[NPROCSTATES
+1] =
112 "", "run", "sleep", "disk", "zomb", "stop", "swap",
116 static char *procstatenames
[NPROCSTATES
+1] =
118 "", " running, ", " sleeping, ", " uninterruptable, ",
119 " zombie, ", " stopped, ", " swapping, ",
124 static char *cpustatenames
[NCPUSTATES
+1] =
126 "user", "nice", "system", "idle",
136 static char *memorynames
[NMEMSTATS
+1] =
138 "K used, ", "K free, ", "K shared, ", "K buffers, ", "K cached",
146 static char *swapnames
[NSWAPSTATS
+1] =
148 "K used, ", "K free, ", "K cached",
152 static char fmt_header
[] =
153 " PID X THR PRI NICE SIZE RES STATE TIME CPU COMMAND";
155 /* these are names given to allowed sorting orders -- first is default */
157 {"cpu", "size", "res", "time", "command", NULL
};
159 /* forward definitions for comparison functions */
166 int (*proc_compares
[])() = {
174 /*=SYSTEM STATE INFO====================================================*/
176 /* these are for calculating cpu state percentages */
178 static long cp_time
[NCPUSTATES
];
179 static long cp_old
[NCPUSTATES
];
180 static long cp_diff
[NCPUSTATES
];
182 /* for calculating the exponential average */
184 static struct timeval lasttime
;
186 /* these are for keeping track of processes */
188 #define HASH_SIZE (1003)
189 #define INITIAL_ACTIVE_SIZE (256)
190 #define PROCBLOCK_SIZE (32)
191 static struct top_proc
*ptable
[HASH_SIZE
];
192 static struct top_proc
**pactive
;
193 static struct top_proc
**nextactive
;
194 static unsigned int activesize
= 0;
195 static time_t boottime
= -1;
197 /* these are for passing data back to the machine independant portion */
199 static int cpu_states
[NCPUSTATES
];
200 static int process_states
[NPROCSTATES
];
201 static long memory_stats
[NMEMSTATS
];
202 static long swap_stats
[NSWAPSTATS
];
205 #define bytetok(x) (((x) + 512) >> 10)
206 #define pagetok(x) ((x) << (PAGE_SHIFT - 10))
207 #define HASH(x) (((x) * 1686629713U) % HASH_SIZE)
209 /*======================================================================*/
212 skip_ws(const char *p
)
214 while (isspace(*p
)) p
++;
219 skip_token(const char *p
)
221 while (isspace(*p
)) p
++;
222 while (*p
&& !isspace(*p
)) p
++;
227 xfrm_cmdline(char *p
, int len
)
240 update_procname(struct top_proc
*proc
, char *cmd
)
245 if (proc
->name
== NULL
)
247 proc
->name
= strdup(cmd
);
249 else if (strcmp(proc
->name
, cmd
) != 0)
252 proc
->name
= strdup(cmd
);
260 * Process structures are allocated and freed as needed. Here we
261 * keep big pools of them, adding more pool as needed. When a
262 * top_proc structure is freed, it is added to a freelist and reused.
265 static struct top_proc
*freelist
= NULL
;
266 static struct top_proc
*procblock
= NULL
;
267 static struct top_proc
*procmax
= NULL
;
269 static struct top_proc
*
277 freelist
= freelist
->next
;
282 if (++procblock
>= procmax
)
289 p
= procblock
= (struct top_proc
*)calloc(PROCBLOCK_SIZE
,
290 sizeof(struct top_proc
));
291 procmax
= procblock
++ + PROCBLOCK_SIZE
;
305 free_proc(struct top_proc
*proc
)
307 proc
->next
= freelist
;
313 machine_init(struct statics
*statics
)
316 /* make sure the proc filesystem is mounted */
319 if (statfs(PROCFS
, &sb
) < 0 || sb
.f_type
!= PROC_SUPER_MAGIC
)
321 fprintf(stderr
, "%s: proc filesystem not mounted on " PROCFS
"\n",
327 /* chdir to the proc filesystem to make things easier */
335 unsigned long uptime
;
338 if ((fd
= open("uptime", 0)) != -1)
340 if (read(fd
, buff
, sizeof(buff
)) > 0)
342 uptime
= strtoul(buff
, &p
, 10);
343 gettimeofday(&tv
, 0);
344 boottime
= tv
.tv_sec
- uptime
;
350 /* fill in the statics information */
351 statics
->procstate_names
= procstatenames
;
352 statics
->cpustate_names
= cpustatenames
;
353 statics
->memory_names
= memorynames
;
354 statics
->swap_names
= swapnames
;
355 statics
->order_names
= ordernames
;
356 statics
->boottime
= boottime
;
357 statics
->flags
.fullcmds
= 1;
358 statics
->flags
.warmup
= 1;
360 /* allocate needed space */
361 pactive
= (struct top_proc
**)malloc(sizeof(struct top_proc
*) * INITIAL_ACTIVE_SIZE
);
362 activesize
= INITIAL_ACTIVE_SIZE
;
364 /* make sure the hash table is empty */
365 memset(ptable
, 0, HASH_SIZE
* sizeof(struct top_proc
*));
373 get_system_info(struct system_info
*info
)
380 /* get load averages */
382 if ((fd
= open("loadavg", O_RDONLY
)) != -1)
384 if ((len
= read(fd
, buffer
, sizeof(buffer
)-1)) > 0)
388 info
->load_avg
[0] = strtod(buffer
, &p
);
389 info
->load_avg
[1] = strtod(p
, &p
);
390 info
->load_avg
[2] = strtod(p
, &p
);
391 p
= skip_token(p
); /* skip running/tasks */
395 info
->last_pid
= atoi(p
);
405 /* get the cpu time info */
406 if ((fd
= open("stat", O_RDONLY
)) != -1)
408 if ((len
= read(fd
, buffer
, sizeof(buffer
)-1)) > 0)
411 p
= skip_token(buffer
); /* "cpu" */
412 cp_time
[0] = strtoul(p
, &p
, 0);
413 cp_time
[1] = strtoul(p
, &p
, 0);
414 cp_time
[2] = strtoul(p
, &p
, 0);
415 cp_time
[3] = strtoul(p
, &p
, 0);
417 /* convert cp_time counts to percentages */
418 percentages(4, cpu_states
, cp_time
, cp_old
, cp_diff
);
423 /* get system wide memory usage */
424 if ((fd
= open("meminfo", O_RDONLY
)) != -1)
429 unsigned long memtotal
= 0;
430 unsigned long memfree
= 0;
431 unsigned long swaptotal
= 0;
433 if ((len
= read(fd
, buffer
, sizeof(buffer
)-1)) > 0)
438 /* iterate thru the lines */
442 if (p
[0] == ' ' || p
[0] == '\t')
446 else if (strncmp(p
, "Mem:", 4) == 0)
448 p
= skip_token(p
); /* "Mem:" */
449 p
= skip_token(p
); /* total memory */
450 memory_stats
[MEMUSED
] = strtoul(p
, &p
, 10);
451 memory_stats
[MEMFREE
] = strtoul(p
, &p
, 10);
452 memory_stats
[MEMSHARED
] = strtoul(p
, &p
, 10);
453 memory_stats
[MEMBUFFERS
] = strtoul(p
, &p
, 10);
454 memory_stats
[MEMCACHED
] = strtoul(p
, &p
, 10);
455 memory_stats
[MEMUSED
] = bytetok(memory_stats
[MEMUSED
]);
456 memory_stats
[MEMFREE
] = bytetok(memory_stats
[MEMFREE
]);
457 memory_stats
[MEMSHARED
] = bytetok(memory_stats
[MEMSHARED
]);
458 memory_stats
[MEMBUFFERS
] = bytetok(memory_stats
[MEMBUFFERS
]);
459 memory_stats
[MEMCACHED
] = bytetok(memory_stats
[MEMCACHED
]);
462 else if (strncmp(p
, "Swap:", 5) == 0)
464 p
= skip_token(p
); /* "Swap:" */
465 p
= skip_token(p
); /* total swap */
466 swap_stats
[SWAPUSED
] = strtoul(p
, &p
, 10);
467 swap_stats
[SWAPFREE
] = strtoul(p
, &p
, 10);
468 swap_stats
[SWAPUSED
] = bytetok(swap_stats
[SWAPUSED
]);
469 swap_stats
[SWAPFREE
] = bytetok(swap_stats
[SWAPFREE
]);
472 else if (!mem
&& strncmp(p
, "MemTotal:", 9) == 0)
475 memtotal
= strtoul(p
, &p
, 10);
477 else if (!mem
&& memtotal
> 0 && strncmp(p
, "MemFree:", 8) == 0)
480 memfree
= strtoul(p
, &p
, 10);
481 memory_stats
[MEMUSED
] = memtotal
- memfree
;
482 memory_stats
[MEMFREE
] = memfree
;
484 else if (!mem
&& strncmp(p
, "MemShared:", 10) == 0)
487 memory_stats
[MEMSHARED
] = strtoul(p
, &p
, 10);
489 else if (!mem
&& strncmp(p
, "Buffers:", 8) == 0)
492 memory_stats
[MEMBUFFERS
] = strtoul(p
, &p
, 10);
494 else if (!mem
&& strncmp(p
, "Cached:", 7) == 0)
497 memory_stats
[MEMCACHED
] = strtoul(p
, &p
, 10);
499 else if (!swap
&& strncmp(p
, "SwapTotal:", 10) == 0)
502 swaptotal
= strtoul(p
, &p
, 10);
504 else if (!swap
&& swaptotal
> 0 && strncmp(p
, "SwapFree:", 9) == 0)
507 memfree
= strtoul(p
, &p
, 10);
508 swap_stats
[SWAPUSED
] = swaptotal
- memfree
;
509 swap_stats
[SWAPFREE
] = memfree
;
511 else if (!mem
&& strncmp(p
, "SwapCached:", 11) == 0)
514 swap_stats
[SWAPCACHED
] = strtoul(p
, &p
, 10);
517 /* move to the next line */
524 /* set arrays and strings */
525 info
->cpustates
= cpu_states
;
526 info
->memory
= memory_stats
;
527 info
->swap
= swap_stats
;
532 read_one_proc_stat(pid_t pid
, struct top_proc
*proc
, struct process_select
*sel
)
534 char buffer
[4096], *p
, *q
;
538 /* if anything goes wrong, we return with proc->state == 0 */
541 /* full cmd handling */
542 fullcmd
= sel
->fullcmd
;
545 sprintf(buffer
, "%d/cmdline", pid
);
546 if ((fd
= open(buffer
, O_RDONLY
)) != -1)
548 /* read command line data */
549 /* (theres no sense in reading more than we can fit) */
550 if ((len
= read(fd
, buffer
, MAX_COLS
)) > 1)
553 xfrm_cmdline(buffer
, len
);
554 update_procname(proc
, buffer
);
568 /* grab the proc stat info in one go */
569 sprintf(buffer
, "%d/stat", pid
);
571 fd
= open(buffer
, O_RDONLY
);
572 len
= read(fd
, buffer
, sizeof(buffer
)-1);
577 proc
->uid
= (uid_t
)proc_owner((int)pid
);
579 /* parse out the status */
581 /* skip pid and locate command, which is in parentheses */
582 if ((p
= strchr(buffer
, '(')) == NULL
)
586 if ((q
= strrchr(++p
, ')')) == NULL
)
591 /* set the procname */
595 update_procname(proc
, p
);
598 /* scan the rest of the line */
601 switch (*p
++) /* state */
603 case 'R': proc
->state
= 1; break;
604 case 'S': proc
->state
= 2; break;
605 case 'D': proc
->state
= 3; break;
606 case 'Z': proc
->state
= 4; break;
607 case 'T': proc
->state
= 5; break;
608 case 'W': proc
->state
= 6; break;
612 proc
->ppid
= strtoul(p
, &p
, 10); /* ppid */
613 p
= skip_token(p
); /* skip pgrp */
614 p
= skip_token(p
); /* skip session */
615 p
= skip_token(p
); /* skip tty */
616 p
= skip_token(p
); /* skip tty pgrp */
617 p
= skip_token(p
); /* skip flags */
618 p
= skip_token(p
); /* skip min flt */
619 p
= skip_token(p
); /* skip cmin flt */
620 p
= skip_token(p
); /* skip maj flt */
621 p
= skip_token(p
); /* skip cmaj flt */
623 proc
->time
= strtoul(p
, &p
, 10); /* utime */
624 proc
->time
+= strtoul(p
, &p
, 10); /* stime */
626 p
= skip_token(p
); /* skip cutime */
627 p
= skip_token(p
); /* skip cstime */
629 proc
->pri
= strtol(p
, &p
, 10); /* priority */
630 proc
->nice
= strtol(p
, &p
, 10); /* nice */
632 p
= skip_token(p
); /* skip timeout */
633 p
= skip_token(p
); /* skip it_real_val */
634 proc
->start_time
= strtoul(p
, &p
, 10); /* start_time */
636 proc
->size
= bytetok(strtoul(p
, &p
, 10)); /* vsize */
637 proc
->rss
= pagetok(strtoul(p
, &p
, 10)); /* rss */
639 p
= skip_token(p
); /* skip rlim */
640 proc
->start_code
= strtoul(p
, &p
, 10); /* start_code */
641 proc
->end_code
= strtoul(p
, &p
, 10); /* end_code */
642 proc
->start_stack
= strtoul(p
, &p
, 10); /* start_stack */
644 /* for the record, here are the rest of the fields */
646 p
= skip_token(p
); /* skip sp */
647 p
= skip_token(p
); /* skip pc */
648 p
= skip_token(p
); /* skip signal */
649 p
= skip_token(p
); /* skip sigblocked */
650 p
= skip_token(p
); /* skip sigignore */
651 p
= skip_token(p
); /* skip sigcatch */
652 p
= skip_token(p
); /* skip wchan */
658 get_process_info(struct system_info
*si
,
659 struct process_select
*sel
,
662 struct timeval thistime
;
663 double timediff
, alpha
, beta
;
664 struct top_proc
*proc
;
667 unsigned long elapsed
;
670 /* calculate the time difference since our last check */
671 gettimeofday(&thistime
, 0);
674 timediff
= ((thistime
.tv_sec
- lasttime
.tv_sec
) +
675 (thistime
.tv_usec
- lasttime
.tv_usec
) * 1e-6);
683 /* round current time to a second */
684 now
= (unsigned long)thistime
.tv_sec
;
685 if (thistime
.tv_usec
>= 500000)
690 /* calculate constants for the exponental average */
691 if (timediff
> 0.0 && timediff
< 30.0)
693 alpha
= 0.5 * (timediff
/ 30.0);
698 timediff
*= HZ
; /* convert to ticks */
700 /* mark all hash table entries as not seen */
701 for (i
= 0; i
< HASH_SIZE
; ++i
)
703 for (proc
= ptable
[i
]; proc
; proc
= proc
->next
)
709 /* read the process information */
711 DIR *dir
= opendir(".");
714 struct top_proc
**active
;
716 int show_idle
= sel
->idle
;
717 int show_uid
= sel
->uid
!= -1;
719 memset(process_states
, 0, sizeof(process_states
));
721 while ((ent
= readdir(dir
)) != NULL
)
725 if (!isdigit(ent
->d_name
[0]))
728 pid
= atoi(ent
->d_name
);
730 /* look up hash table entry */
731 proc
= pp
= ptable
[HASH(pid
)];
732 while (proc
&& proc
->pid
!= pid
)
737 /* if we came up empty, create a new entry */
743 ptable
[HASH(pid
)] = proc
;
744 proc
->time
= proc
->otime
= 0;
747 read_one_proc_stat(pid
, proc
, sel
);
750 if (proc
->state
== 0)
754 process_states
[proc
->state
]++;
756 /* calculate cpu percentage */
759 if ((proc
->pcpu
= (proc
->time
- proc
->otime
) / timediff
) < 0.0001)
764 else if ((elapsed
= (now
- boottime
)*HZ
- proc
->start_time
) > 0)
766 if ((proc
->pcpu
= (double)proc
->time
/ (double)elapsed
) < 0.0001)
776 /* remember time for next time */
777 proc
->otime
= proc
->time
;
781 /* make sure we have enough slots for the active procs */
782 if (activesize
< total_procs
)
784 pactive
= (struct top_proc
**)realloc(pactive
,
785 sizeof(struct top_proc
*) * total_procs
);
786 activesize
= total_procs
;
789 /* set up the active procs and flush dead entries */
790 /* also coalesce threads */
792 for (i
= 0; i
< HASH_SIZE
; i
++)
794 struct top_proc
*last
;
795 struct top_proc
*ptmp
;
796 struct top_proc
*parent
;
802 if (proc
->state
== 0)
807 proc
= last
->next
= proc
->next
;
811 proc
= ptable
[i
] = proc
->next
;
817 /* look up hash table entry for parent */
821 parent
= ptable
[HASH(pid
)];
822 while (parent
&& parent
->pid
!= pid
)
824 parent
= parent
->next
;
826 } while (parent
&& parent
->state
== 0);
828 /* does this look like a thread of its parent? */
829 if (parent
&& proc
->size
== parent
->size
&&
830 proc
->rss
== parent
->rss
&&
831 proc
->start_code
== parent
->start_code
&&
832 proc
->end_code
== parent
->end_code
&&
833 proc
->start_stack
== parent
->start_stack
)
835 /* yes it does: roll up the cumulative numbers */
836 parent
->threads
+= proc
->threads
;
837 parent
->time
+= proc
->time
;
838 parent
->pcpu
+= proc
->pcpu
;
840 /* mark this process as dead (undisplayable) */
843 else if ((show_idle
|| proc
->state
== 1 || proc
->pcpu
) &&
844 (!show_uid
|| proc
->uid
== sel
->uid
))
854 si
->p_active
= active
- pactive
;
855 si
->p_total
= total_procs
;
856 si
->procstates
= process_states
;
859 /* if requested, sort the "active" procs */
861 qsort(pactive
, si
->p_active
, sizeof(struct top_proc
*),
862 proc_compares
[compare_index
]);
864 /* don't even pretend that the return value thing here isn't bogus */
865 nextactive
= pactive
;
871 format_header(char *uname_field
)
874 int uname_len
= strlen(uname_field
);
878 memcpy(strchr(fmt_header
, 'X'), uname_field
, uname_len
);
885 format_next_process(caddr_t handle
, char *(*get_userid
)(int))
888 static char fmt
[MAX_COLS
]; /* static area where result is built */
889 struct top_proc
*p
= *nextactive
++;
891 snprintf(fmt
, sizeof(fmt
),
892 "%5d %-8.8s %3d %3d %4d %5s %5s %-5s %6s %5.2f%% %s",
894 (*get_userid
)(p
->uid
),
896 p
->pri
< -99 ? -99 : p
->pri
,
900 state_abbrev
[p
->state
],
901 format_time(p
->time
/ HZ
),
905 /* return the result */
909 /* comparison routines for qsort */
912 * There are currently four possible comparison routines. main selects
913 * one of these by indexing in to the array proc_compares.
915 * Possible keys are defined as macros below. Currently these keys are
916 * defined: percent cpu, cpu ticks, process state, resident set size,
917 * total virtual memory usage. The process states are ordered as follows
918 * (from least to most important): WAIT, zombie, sleep, stop, start, run.
919 * The array declaration below maps a process state index into a number
920 * that reflects this ordering.
923 /* First, the possible comparison keys. These are defined in such a way
924 that they can be merely listed in the source code to define the actual
928 #define ORDERKEY_PCTCPU if (dresult = p2->pcpu - p1->pcpu,\
929 (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
930 #define ORDERKEY_CPTICKS if ((result = (long)p2->time - (long)p1->time) == 0)
931 #define ORDERKEY_STATE if ((result = (sort_state[p2->state] - \
932 sort_state[p1->state])) == 0)
933 #define ORDERKEY_PRIO if ((result = p2->pri - p1->pri) == 0)
934 #define ORDERKEY_RSSIZE if ((result = p2->rss - p1->rss) == 0)
935 #define ORDERKEY_MEM if ((result = p2->size - p1->size) == 0)
936 #define ORDERKEY_NAME if ((result = strcmp(p1->name, p2->name)) == 0)
938 /* Now the array that maps process state to a weight */
940 unsigned char sort_state
[] =
952 /* compare_cpu - the comparison function for sorting by cpu percentage */
956 struct top_proc
**pp1
,
957 struct top_proc
**pp2
)
959 register struct top_proc
*p1
;
960 register struct top_proc
*p2
;
961 register long result
;
964 /* remove one level of indirection */
976 return result
== 0 ? 0 : result
< 0 ? -1 : 1;
979 /* compare_size - the comparison function for sorting by total memory usage */
983 struct top_proc
**pp1
,
984 struct top_proc
**pp2
)
986 register struct top_proc
*p1
;
987 register struct top_proc
*p2
;
988 register long result
;
991 /* remove one level of indirection */
1003 return result
== 0 ? 0 : result
< 0 ? -1 : 1;
1006 /* compare_res - the comparison function for sorting by resident set size */
1010 struct top_proc
**pp1
,
1011 struct top_proc
**pp2
)
1013 register struct top_proc
*p1
;
1014 register struct top_proc
*p2
;
1015 register long result
;
1018 /* remove one level of indirection */
1030 return result
== 0 ? 0 : result
< 0 ? -1 : 1;
1033 /* compare_time - the comparison function for sorting by total cpu time */
1037 struct top_proc
**pp1
,
1038 struct top_proc
**pp2
)
1040 register struct top_proc
*p1
;
1041 register struct top_proc
*p2
;
1042 register long result
;
1045 /* remove one level of indirection */
1057 return result
== 0 ? 0 : result
< 0 ? -1 : 1;
1060 /* compare_cmd - the comparison function for sorting by command name */
1064 struct top_proc
**pp1
,
1065 struct top_proc
**pp2
)
1067 register struct top_proc
*p1
;
1068 register struct top_proc
*p2
;
1069 register long result
;
1072 /* remove one level of indirection */
1085 return result
== 0 ? 0 : result
< 0 ? -1 : 1;
1090 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
1091 * the process does not exist.
1092 * It is EXTREMLY IMPORTANT that this function work correctly.
1093 * If top runs setuid root (as in SVR4), then this function
1094 * is the only thing that stands in the way of a serious
1095 * security problem. It validates requests for the "kill"
1096 * and "renice" commands.
1105 sprintf(buffer
, "%d", pid
);
1107 if (stat(buffer
, &sb
) < 0)
1110 return (int)sb
.st_uid
;