273 Commits

Author SHA1 Message Date
a4f4ef5b63 Tag 3.0.0beta5 2018-08-24 22:55:24 -03:00
8c43218aa0 Introduce screen tabs 2018-08-24 22:53:07 -03:00
e77a16f4ae Linux: proper sorting of CMAJFILT and CMINFILT 2018-08-24 22:45:36 -03:00
a49853543e Change to previous screen using Shift-Tab 2018-08-24 19:30:54 -03:00
28a5859fe8 Settings: fix storage of screens in htoprc 2018-08-24 18:39:00 -03:00
0939e5cb41 Settings: fix default and storage of sort keys 2018-08-24 18:39:00 -03:00
e1c2dc56fd Linux: fix CPU count 2018-08-24 18:39:00 -03:00
e9f95da559 Linux: remove warnings of unused variables 2018-08-24 18:39:00 -03:00
5a0a4e1adb Improve Catalan translation for desktop file (#828) 2018-08-24 18:39:00 -03:00
f3b47c46e6 Improve htop.desktop file (#609)
- sort entries according to the spec
- add to the `Monitor` category
2018-08-24 16:01:50 -03:00
34b147e14e Linux: add process->starttime and use it for STARTTIME column (#700)
this way a remount of /proc will not reset starttimes
and we can also see startup times for processes started before the mount
of /proc

also record btime (boot time in seconds since epoch) as Linux semi-global
2018-08-24 16:01:43 -03:00
03e0a73d5b OpenBSD: make the STARTTIME column display correctly (#815) 2018-08-24 16:01:38 -03:00
794419e6c5 OpenBSD: add environment reading support (#819) 2018-08-24 16:01:31 -03:00
7ec5312974 Fix process name updates for shorter strings (#812)
When a process name changes from a long string to a short string,
truncate instead of just overwriting the beginning.
2018-08-24 16:01:25 -03:00
af92f0b8d6 Fix virtualization color in help screen
Closes #785.
2018-08-24 16:01:19 -03:00
22e9d09723 Unstage/gitignore INSTALL since it's regenerated by autogen.sh
As noted by @marcelpaulo. I've had ugly diffs that fiddled with
this file in the past, so it's good to see it gone.

Closes #800.
2018-08-24 16:01:12 -03:00
fda1475a10 Mention the "c" key (tag subtree) in the man
This was reported by @agguser.
2018-08-24 16:01:06 -03:00
ebf3cff1ac Disable 'make dist' when pkg.m4 is unused in configure
This would prevent a careless future package maintainer from creating a
release tarball with a defective configure script. :)

Also, add a warning in the autogen.sh phase if pkg.m4 is unused.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2018-08-24 16:00:59 -03:00
7bbb3600fc fix option string
This broke with commit db05ba6106.
2018-08-24 16:00:52 -03:00
4092c90bc3 Update ChangeLog 2018-08-24 16:00:23 -03:00
7742e17cc0 Add tree view flag to man page (#777) 2018-08-24 16:00:01 -03:00
2dffee8de9 Require pkg-config as an optional build dependency on Linux (#775)
With this commit:

* if pkgconfig is installed, it will expand the code inside the shell if, adding the pkgconfig-based tests for the dependencies of Linux delay accounting.
* if pkgconfig is not installed, it will add an error message inside the test of Linux delay accounting, telling the user to install pkgconfig and rebuild the configure script if they want to use Linux delay accounting.

The end result is:

* people running Linux
  * will not need pkgconfig when not using delay accounting
  * will need pkgconfig when using delay accounting
    * if they don't have it
      * they are told by configure they need to install it and re-run autogen.sh when running from Git
      * they are told by configure they need to install it and re-run configure when running from the tarball
* people not running Linux
  * will never need pkgconfig

...and in none of the above scenarios the generated configure script produces unexpanded macros for users checking out the sources from Git.
2018-08-24 15:59:40 -03:00
791aae87c4 MakeHeader.py: Fix for non-utf8 environments (#770)
Header creation fails with non-utf8 locale and python3.
Simply set LC_ALL="C" and use python3 to reproduce the issue.

env LC_ALL="C" ./scripts/MakeHeader.py MetersPanel.c
Traceback (most recent call last):
  File "./scripts/MakeHeader.py", line 32, in <module>
    for line in file.readlines():
  File "/usr/lib64/python3.5/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 956: ordinal not in range(128)

This changes is python2 and python3 compatible

cStringIO.StringIO module is removed because it is not able to accept unicode strings
https://docs.python.org/2/library/stringio.html#cStringIO.StringIO
2018-08-24 15:58:12 -03:00
505fa6b517 Solaris: grammatical whoops in configure.ac 2018-05-22 23:17:30 -03:00
f94c54eb5a Solaris: add check for missing err.h 2018-05-22 23:17:30 -03:00
9710ce6c08 Solaris: Make last change to Platform.h persistent by adding to Platform.c 2018-05-22 23:17:30 -03:00
f78f658eed dragonflybsd, freebsd, openbsd: fixes for htop 3.0 branch 2018-04-10 10:22:44 -03:00
b8bfe60d2b Solaris: fix build on htop 3.0 branch 2018-04-10 10:21:29 -03:00
5c6d7cca3e Bump version to 3.0.0beta4 2018-04-06 12:43:24 -03:00
6fe06fb7e5 Portability: make list of default screens per-platform 2018-04-06 12:41:36 -03:00
657836a2ae Minor style fixes. 2018-04-06 12:40:51 -03:00
63e1417b8c Update some field accesses to new structures 2018-04-06 12:34:57 -03:00
9b4bdfcb3e Add -t command-line flag for tree view 2018-04-06 11:14:09 -03:00
7a71f9cf32 macOS: fix the switched version test (#772) 2018-04-06 11:14:09 -03:00
c91b0938d5 Solaris: update proc state letters to reflect Solaris usage 2018-04-06 11:14:09 -03:00
4f934c977d Solaris: bump copyright in Platform.{c,h} 2018-04-06 11:14:09 -03:00
d1696b5d5d Solaris: fix a memory leak caused by calling ProcessList_getProcess twice for each LWP 2018-04-06 11:14:09 -03:00
beb47cbb12 Solaris: Implement process environment listing 2018-04-06 11:14:09 -03:00
677cac9fab Solaris: add placeholder message about environment listing 2018-04-06 11:14:09 -03:00
6790e004cd Solaris: showing a dash for the top-level process is no longer necessary 2018-04-06 11:14:09 -03:00
8677162836 Solaris: add warning about proc_walk_f callback function 2018-04-06 11:14:09 -03:00
475798e8ab Solaris: condense separate process vs lwp handling down to a single workflow 2018-04-06 11:14:09 -03:00
7689c5c3b7 Solaris: get completely out of the file handling business using libproc 2018-04-06 11:14:09 -03:00
c2ab3ac422 Solaris: Condense conditional blocks for new vs old LWPs and procs 2018-04-06 11:14:09 -03:00
61bb649e0a Solaris: remove unneeded accumulators for process and thread counting 2018-04-06 11:14:09 -03:00
b8ec1c0337 Solaris: Assorted post-LWP code cleanup 2018-04-06 11:14:09 -03:00
499af738e7 Solaris: Implement kernel thread counting 2018-04-06 11:14:09 -03:00
416ef48a62 Solaris: If a process has a running LWP, then the process is by definition running 2018-04-06 11:14:09 -03:00
b9e0da9200 Collapse current subtree pressing Backspace 2018-04-06 11:14:09 -03:00
509303323f Solaris: Implement LWP enumeration (#768)
Squashed the following commits:

* Solaris: Get LWP enumeration working
* Solaris: Make showing and hiding of kernel threads behave
* Solaris: remove usage of lwpstatus that is no longer needed
* Solaris: no discrete access to parent proc structure needed
* Solaris: Restore runtime MaxPid detection after LWP changes
* Solaris: Workaround virtual PID signal issue by shadowing kill() with a macro
* Solaris: Fix unintention double-shifting of virtual PID for LWP enumeration
* Solaris: Add LWPID to default display since LWP enumeration is also default
* Solaris: use PAGE_SIZE_KB from Process.h instead of custom definition
* Solaris: stop LWP enumeration at 1023 LWPs per proc since that is all we can handle in the virtual PID
2018-04-06 11:14:09 -03:00
421a31d6f1 OpenBSD: read Battery data
Signed-off-by: Hisham Muhammad <hisham@gobolinux.org>
2018-04-06 11:14:09 -03:00
d4c4c02b97 macOS: keep scanning thread for versions before High Sierra (#728)
Keep scanning threads for versions before High Sierra 13.0.0 and after 13.3.0.
2018-04-06 11:14:09 -03:00
1ef20d1f14 Solaris: Fix virt and resident memory sizes. Was using KiB, needed pages. 2018-04-06 11:14:09 -03:00
1ab5afa1fe New makefile targets to rebuild and clean htop headers.
`make htop-headers` will regenerate all '.h' headers in htop source for
all platforms.
`make clean-htop-headers` will delete all generated htop headers.

Because of the introduction of these two targets, I slightly changed
the style of platform-specific portions of makefile rules.
Please comment if you accept such a style, or need me to revert to old
style.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2018-04-06 11:14:04 -03:00
00d1fb019a Linux: change how kernel threads are detected
Use the same method that ps and top use to determine if a
process is a kernel thread on Linux: check if cmdline is empty.

Thanks to @wangqr's investigation reported here:
https://github.com/hishamhm/htop/issues/761#issuecomment-375306069

Fixes #761.
2018-04-06 11:13:30 -03:00
dd175b6881 Fix overflow for signals >= 100.
Thanks to @gzip4 for tracking this down.

Closes #764.
2018-04-06 11:13:30 -03:00
7ad9701a12 strace: increase string length 2018-04-06 11:13:30 -03:00
881fe9631a Solaris: code indentation fix 2018-04-06 11:13:30 -03:00
0de77c7075 Solaris: enough changes made to justify a copyright bump to 2018 2018-04-06 11:13:30 -03:00
0f71db9d82 Solaris: fix memory allocation for usernames (some empty usernames in 32-bit builds) 2018-04-06 11:13:30 -03:00
da4010783e Solaris: fix malloc() / free() issues with zone name handling 2018-04-06 11:13:30 -03:00
3f7622a302 Solaris: Link against libmalloc to fix various crashes 2018-04-06 11:13:30 -03:00
df5d81a881 Solaris: Import backtrace-on-abort from Linux, with minor modification for Solaris 2018-04-06 11:13:30 -03:00
6c2b3b546d Use fork/exec instead of popen to run lsof (#757)
Fixes #675
2018-04-06 11:13:30 -03:00
f48f5a10ca Use AM_CFLAGS and AM_LDFLAGS in Makefile.am (#760)
This reduces generated Makefile.in size by 74%.
(217319 bytes -> 56326 bytes)

Automake considers that <prog>_CFLAGS and <prog>_LDFLAGS are
program-specific build rules, and when such are specified, Automake
will generate additional code just to avoid the "generic" and
package-wide AM_CFLAGS or AM_LDFLAGS. (Especially for <prog>_CFLAGS,
Automake will rename generated object files to become "prog-foo.o" and
such, and it's _a lot_ of code to achieve this in Makefile.)

There's no reason for htop to rename intermediate object files. It's
better to make things simpler.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2018-04-06 11:13:30 -03:00
1aa23925c9 Import Solaris support (#741)
This commit adds support for Solaris, squashed from PR #741:

Summary of additions:

* Initial setup of Solaris platform directory
* Add Solaris platform into autoconf template
* Uptime and load averages
* Add dependency on libkstat
* Basic process listing
* Zone name display
* CPU detection
* Per-process memory and CPU usage parsed correctly
* Uses sysconf to discover number of CPUs, instead of more complex libkstat code
* Simple memory display working
* Reduce repetitive calls to the PAGE_SIZE macro when reading memory info
* Add Project, Contract, Task, and Pool into process properties
* Use system major()/minor() implementations and remove extraneous definition of mkdev()
* Get the STARTTIME column working properly, using the Linux implementation as a guide
2018-04-06 11:13:30 -03:00
2e1f56d934 Changed type of some integer variables to avoid overflows 2018-03-16 12:01:24 -03:00
6ee99566cd Bump version to 3.0.0beta3 2018-02-26 20:13:09 -03:00
dc6bb069f0 Update generated header 2018-02-26 20:13:09 -03:00
0169577019 Fix inttypes.h header 2018-02-26 20:13:09 -03:00
0e38be9ee7 Darwin: expose LAST_PROCESSFIELD like the other platforms 2018-02-26 20:13:09 -03:00
8e6c1e1bac Add more default screens 2018-02-26 20:13:09 -03:00
709619800f Only compute counters is process is shown 2018-02-26 20:13:09 -03:00
a72439c9b7 Implemented various performance counters 2018-02-26 20:13:09 -03:00
61e94c1b5b Add IPC performance counter for Linux 2018-02-26 20:13:09 -03:00
b9f5892593 Add perf counter object 2018-02-26 20:13:09 -03:00
267d03b6d8 configure.ac: add --enable-perfcounters 2018-02-26 20:13:09 -03:00
3b819daf82 Set default sort keys in default screens 2018-02-26 20:13:09 -03:00
d9f8cdf0a6 Add make symbols target 2018-02-26 20:13:09 -03:00
59982a188c Store .sort_key as a string 2018-02-26 20:13:09 -03:00
0800424fe6 Match iotop's screen configuration 2018-02-26 20:13:09 -03:00
b4a8f048d1 Use screen's flags when reading process data 2018-02-26 20:13:09 -03:00
2df1f61d77 Screens: Fix "New Screen" option 2018-02-26 20:13:09 -03:00
e6c98b6e8e htoprc: store screen 0's setup for improved compatibility 2018-02-26 20:13:09 -03:00
b815e4c7a3 Add support for multiple screens, switchable using Tab 2018-02-26 20:13:09 -03:00
4791050cea Begin add supporting for multiple screens 2018-02-26 20:13:09 -03:00
1edcfad874 Move responsibility for cursor placement to Panels 2018-02-26 20:13:09 -03:00
d4ea7cd65c Fix bashisms (#749)
The configure script relied on bash-specific extensions to shell syntax
and behavior, causing build failures on systems with other /bin/sh
implementations. This commit replaces those with equivalent constructs
that should work in all POSIX shells.
2018-02-26 20:07:52 -03:00
9ca1c993ac Add Contributing Guide! 2018-02-26 11:45:53 -03:00
ccd156f8ba Updates to generated header files 2018-02-26 11:44:46 -03:00
858af2505f Protect against overflows in RichString_setAttrn 2018-02-26 11:05:12 -03:00
655c7293d2 Update ChangeLog 2018-02-26 10:54:01 -03:00
bc5d46982f use CFLAGS from ncurses*-config, if present (#745)
Fixes #695.
2018-02-26 10:19:01 -03:00
c01f40eb3e Fix build failure ('major' undefined) in glibc 2.28. (#746)
glibc 2.28 no longer defines 'major' and 'minor' in <sys/types.h> and
requires us to include <sys/sysmacros.h>. (glibc 2.25 starts
deprecating the macros in <sys/types.h>.) Now do include the latter if
found on the system.

At the moment, let's also utilize AC_HEADER_MAJOR in configure script.
However as Autoconf 2.69 has not yet updated the AC_HEADER_MAJOR macro
to reflect the glibc change [1], so add a workaround code.

Fixes #663. Supersedes pull request #729.

Reference:
[1] https://git.savannah.gnu.org/gitweb/?p=autoconf.git;a=commit;h=e17a30e987d7ee695fb4294a82d987ec3dc9b974

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2018-02-26 10:15:05 -03:00
eed18dd107 Remove unused function from unsupported/ 2018-02-18 21:18:53 -03:00
f914617508 Make settings file finding sequence more straightforward
Avoid unnecessary access() call and make code read more linearly.
2018-02-18 20:42:17 -03:00
03b2581745 Only consider a read successful when the file seems valid
Require at least the `fields` entry to be present,
so we can have a decent guess that it was indeed a settings file.
2018-02-18 20:35:23 -03:00
8c653212c0 Replace size_t with int/void* union
I was occasionally passing negative values to size_t.
Plus, this better reflects the intent of the variant argument.

Reported by Coverity:
https://scan8.coverity.com/reports.htm#v13253/p10402/fileInstanceId=22093891&defectInstanceId=7543346&mergedDefectId=174179&fileStart=251&fileEnd=500
2018-02-18 10:38:49 -03:00
b064d501ae linux/Battery.c: make sure fd is always closed
Detected by Coverity:
https://scan8.coverity.com/reports.htm#v13252/p10402/fileInstanceId=22093957&defectInstanceId=7543348&mergedDefectId=174180
2018-02-18 10:21:22 -03:00
ff78a1bfce Fix out-of-bounds read
Detected by Coverity:
https://scan8.coverity.com/reports.htm#v13252/p10402/fileInstanceId=22093847&defectInstanceId=7543344&mergedDefectId=174181
2018-02-18 10:17:56 -03:00
f4f35da7e0 Fix indentation 2018-02-18 10:17:29 -03:00
76366be3f1 Update ChangeLog 2018-02-17 21:11:54 -02:00
6dda8d2586 linux/LinuxProcessList.c: Fix indentation. 2018-02-17 20:52:46 -02:00
5fca258f33 call clear() function when SIGWINCH is received. (#660) 2018-02-17 16:25:57 -02:00
70ed51a303 linux/LinuxProcessList: fix reading of number of read syscalls of process
The "if" tests if the character at index "5" is 'r', as a first quick
check. However at index "5" will always be a colon ":". This patch fixes
the off-by-one error. htop now shows proper values in the RD_SYSC
column.

Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
2018-02-17 16:14:34 -02:00
df9922a67e Fix preservation of LDFLAGS value during configure script
Fixes #738.
2018-02-17 14:50:55 -02:00
fcdff59f4c Update ChangeLog 2018-02-17 14:50:42 -02:00
b544c22c72 Fix issue with small terminals.
Fixes #733.
2018-02-13 06:41:44 -02:00
f37a050d3d Optimize Vector_size on non-debug builds 2018-02-05 11:01:35 +01:00
03f17688ad Handle unexpected values for character passed to isalnum
It seems that certain negative integer values can crash isalnum().
Let's protect against those.

Fixes #711.
2018-02-05 10:59:20 +01:00
a32d7528f6 Check for pkgconfig's presence before using it.
Fixes #710.
2018-02-05 10:22:16 +01:00
ac2dff2881 Fix color behavior on some terminals.
Fixes #635.
2018-02-05 07:20:27 +01:00
c50440f165 Bump version to 2.1.0 2018-02-04 20:13:55 +01:00
b0588d90ff parseBatInfo: check line for NULL before passing it to String_getToken() 2018-02-04 17:04:47 +01:00
b84ebfd4e8 Clarify we are looking for the null termination
Not for a comparison to zero
2018-02-04 17:02:30 +01:00
b86e14d3cf Typo in man page
*hightlight*
2018-02-04 17:01:39 +01:00
b34d76cd41 Fix: infinite loop in tree view on macOS
Fixes #688, the bug regressed on 584a9bc.

On Mac OS X 10.11.6, all processes have their parents since there's a
special process named "kernel_task", whose PID and PPID are 0. As a
result, `this->processes` is never changed causing infinite `while`.
2018-02-04 16:51:06 +01:00
87be623eac Add support for Linux TASK_IDLE
Linux commit 06eb61844d841d0032a9950ce7f8e783ee49c0d0 ("sched/debug:
Add explicit TASK_IDLE printing") exposes kthreads idling using
TASK_IDLE in procfs as "I (idle)".

Until now, when sorting the STATE ("S") column, htop used the raw
value of the state character for comparison, however that led to the
undesirable effect of TASK_IDLE ('I') tasks being sorted above tasks
that were running ('R').

Thus, explicitly recognize the idle process state, and sort it below
others.
2018-02-04 16:44:21 +01:00
b27712181a Darwin: disable thread reading due to bug in macOS High Sierra 2018-02-04 08:59:29 +01:00
ad99187680 htop scans /proc to determine how many running tasks exist. Since that
operation is not possible to be conducted in an atomic fashion, task
scheduling effects can lead to a count greater than the number of actual
processors; this is more easily noticed on machines with several CPUs
and under heavy workload.
This patch simply adds an upper bound on cpuCount to guarantee
consistent reports of the number of running tasks at any given time.
2018-01-23 14:11:47 -02:00
b7b66b76a5 Adds support for linux delay accounting (#667)
Adds support for showing columns with linux delay accounting.

This information can be read from the netlink interface, and thus we set up a socket to read from that when initializing the LinuxProcessList (LinuxProcessList_initNetlinkSocket). After that, for each process we call LinuxProcessList_readDelayAcctData, which sends a message thru the socket after setting up a callback to get the answer from the Kernel. That callback sets the process total delay time attribute. We then set the delay percent as the percentage of time process cpu time since last scan.
2017-12-04 00:15:29 -02:00
52831955c7 fix fallthough comments for GCC 7.x
GCC 7.x does some extended checks on fallthough for switch/case
statement. The warning looks like this:

warning: this statement may fall through [-Wimplicit-fallthrough=]

It can be told about implicit fallthough, however it does not
recognize comments within blocks, so move the comments outside.
2017-10-23 14:27:33 -04:00
ef34a83c54 Merge pull request #678 from evelikov/remove-libtool
Remove libtool references
2017-10-13 13:55:43 -07:00
4c1230b03b Remove libtool references
The project builds a single standalone binary.

There are no libraries created - be that static or shared ones.
Thus there's no need for libtool.

Signed-off-by: Emil Velikov <emil.l.velikov@gmail.com>
2017-09-22 16:53:26 +01:00
e3f65c8ec2 Make 'c' key work with threads as well. 2017-09-14 17:10:39 -03:00
b9934ffa08 minor style tweak 2017-09-04 13:53:03 -03:00
65ec0bd6a6 Merge pull request #676 from wangqr/master
Find roots when constructing process tree, fix #587
2017-09-01 19:56:55 -03:00
584a9bceab Find roots when constructing process tree, fix #587 2017-09-01 21:34:05 +08:00
9487bda330 Do not use xSnprintf when the result is used. Fixes #662. 2017-08-01 15:48:43 -07:00
4300a19592 Merge pull request #659 from guoci/patch-1
make script version agnostic
2017-07-28 21:07:54 -03:00
60acda0bce make script version agnostic 2017-07-27 17:02:01 -04:00
09e241fb12 Security review: check results of snprintf.
Calls marked with xSnprintf shouldn't fail.
Abort program cleanly if any of them does.
2017-07-27 16:07:50 -03:00
3975e9ce5c Merge branch 'master' of https://github.com/hishamhm/htop 2017-07-26 16:16:10 -03:00
821d50f0b4 Merge pull request #651 from Explorer09/graph-mode-draw
Round values in graph drawing (instead of implicit truncate)
2017-07-26 16:15:59 -03:00
543d65c6ab Security review: make privilege dropping-restoring optional.
This is/was necessary only on macOS, because you needed root in order
to read the process list. This was never necessary on Linux, and
it also raises security concerns, so now it needs to be enabled
explicitly at build time.
2017-07-26 15:40:55 -03:00
f205f7004c Use regular readdir since readdir_r is deprecated and newer GCC complains. 2017-07-26 15:35:39 -03:00
ef7817c17a Merge pull request #656 from rsaxvc/master
more const usage
2017-07-26 11:24:51 -03:00
33fda93271 Merge pull request #657 from kalbasit/remove_whitespace_from_config_file
Settings: do not emit trailing whitespace
2017-07-25 13:57:55 -03:00
ce0cf3c457 Settings: do not emit trailing whitespace 2017-07-24 16:36:27 -07:00
d5faf64374 Mark some things as const
Several string pointer arrays pointed to const strings
but were not const themselves.

A few various structures and arrays were also marked const.
2017-07-22 22:34:30 -05:00
18b3e5d255 .gitignore vim & nano swp files 2017-07-22 22:34:30 -05:00
143a7de6b2 Round values in graph drawing (instead of implicit truncate) 2017-07-15 22:14:44 +08:00
e940aecfb9 Add "no perm" status when other fields fail due to lack of permission.
Thanks @Sworddragon for the heads up.
See #88.
2017-07-10 20:57:34 -03:00
1a178ad581 Merge branch 'master' of https://github.com/hishamhm/htop 2017-07-10 20:14:53 -03:00
c39f18a7be Add a clear warning about unsupported platforms.
Closes #648.
2017-07-10 20:14:25 -03:00
978c9e1698 Merge pull request #647 from Explorer09/dragonfly-pid-max
DragonFlyBSD PID_MAX is 999999.
2017-07-10 10:19:26 -03:00
e70f447d54 DragonFlyBSD PID_MAX is 999999.
See DragonFlyBSD source "sys/sys/proc.h".

Fixes #646
2017-07-07 20:38:04 +08:00
71785e2ded Set idle I/O prio to 0x6007, like ionice.
As suggested by @wolfgang42 in #100.
2017-07-05 15:20:48 -03:00
e9ecbd05bc Use class value only to display idle I/O priority.
As suggested by @wolfgang42. Fixes #100.
2017-07-05 15:18:02 -03:00
89d15399c5 Merge branch 'master' into fixedgray 2017-06-06 15:27:02 -03:00
e03e45d819 Merge pull request #624 from mklein-de/xterm-256color
make special keys also work with TERM=xterm-256color
2017-06-06 15:23:58 -03:00
45f3769887 make special keys also work with TERM=xterm-256color 2017-06-01 12:52:14 +02:00
2d8dd0b29e Work around the strange behavior of gray. 2017-05-23 14:02:34 -03:00
b1028e03c5 Merge pull request #414 from Explorer09/meters-panel-edit
Mouse-friendly functions bar for meters panel
2017-04-24 23:48:13 -03:00
0c168471b6 F6 is actually used for both actions, depending on context. 2017-04-24 23:38:28 -03:00
68b8efb83f Merge pull request #612 from ryenus/keymap
update keymap in help
2017-04-24 23:35:38 -03:00
418fea33f4 Merge pull request #613 from dkgroot/master
Initial addition of dragonflybsd (based on FreeBSD)
2017-04-24 23:34:27 -03:00
975e22144e Merge pull request #614 from dkgroot/EnhanceMakeHeader
Enh: scripts/MakeHeader script
2017-04-24 23:30:42 -03:00
50f03f5950 Enh: scripts/MakeHeader script
Only write a new .h file if something changed for the header file being created.
This prevents a lot of recompilation during development
2017-04-21 16:39:03 +02:00
cb7a06379d Enh: Add support for backtrace using execinfo 2017-04-21 16:36:19 +02:00
cba695961a Fix: TreeView 2017-04-21 16:34:40 +02:00
49af12e7c6 implement: readJailName
Note: dragonflybsd does not have 'jail_get' like freebsd does.
It does however provide a sysctl "jail.list" which returns a list of all jails.
2017-04-20 15:14:33 +02:00
b258d6e53e Initial addition of dragonflybsd (based on FreeBSD) 2017-04-19 16:19:32 +02:00
e0e84401e4 update keymap in help 2017-04-19 18:20:42 +08:00
5570748dd2 Merge pull request #610 from coypoop/patch-1
Add missing include
2017-03-28 23:56:13 -03:00
3c09082422 Merge pull request #608 from giwhub/giwhub-patch-1
Add Comment[zh_CN] and GenericName[zh_CN] into htop.desktop
2017-03-28 23:24:03 -03:00
53d7c66ac3 Add missing include
sys/time.h for struct timeval
2017-03-25 14:12:26 +03:00
3b1260ac3d Add Comment[zh_CN] and GenericName[zh_CN] into htop.desktop 2017-03-24 01:45:10 +08:00
fa30938247 Merge branch 'master' of https://github.com/hishamhm/htop 2017-02-15 22:49:13 -02:00
bb8dec1582 Cap battery at 100%.
Apparently invalid results can be returned by buggy drivers in old laptops,
as reported by @thukydides. See #596.
2017-02-15 22:47:03 -02:00
aa813c7561 Merge pull request #593 from kamyarrasta/issue/502sessionid
Issue #502 update Session ID column
2017-02-04 21:46:29 -02:00
3f6d1262c0 Issue #502 fix SID colunm header width 2017-02-05 00:10:29 +01:00
84bc00a275 Issue #502 update Session ID column 2017-02-01 00:03:55 +01:00
6141edc74b Merge pull request #584 from EliteTK/buffer-lengths
Replace all uses of sprintf with snprintf
2016-12-30 19:28:38 -02:00
2b5c1b4b13 Replace all uses of sprintf with snprintf
In all the cases where sprintf was being used within htop, snprintf
could have been used. This patch replaces all uses of sprintf with
snprintf which makes sure that if a buffer is too small to hold the
resulting string, the string is simply cut short instead of causing
a buffer overflow which leads to undefined behaviour.

`sizeof(variable)` was used in these cases, as opposed to `sizeof
variable` which is my personal preference because `sizeof(variable)`
was already used in one way or another in other parts of the code.
2016-12-29 19:42:41 +01:00
8af4d9f453 Interpret TTY_NR column on Linux,
translate dev_t to major:minor on other platforms.
Closes #316.
2016-10-01 03:09:04 -03:00
8a147dd5b4 Merge branch 'master' of https://github.com/hishamhm/htop 2016-09-16 13:37:44 -03:00
5df425867c Protect against strange values of SIGRTMIN and SIGRTMAX,
and change alignment of numbers.
2016-09-16 13:37:07 -03:00
46d8f2eef2 Merge pull request #551 from Explorer09/rt-signals
Real-time signals support (kill command)
2016-09-16 13:36:58 -03:00
8fd6228bc1 Merge pull request #551 from Explorer09/rt-signals
Real-time signals support (kill command)
2016-09-16 13:03:46 -03:00
0ce4835f95 Real-time signals support (for kill command)
SignalsPanel_new now fetches SIGRTMIN and SIGRTMAX and generates real-
time signals entries at runtime.

All signals between SIGRTMIN and SIGRTMAX are written in "SIGRTMIN+n"
notation, per discussion in pull request #551.

Signed-off-by: Kang-Che Sung <explorer09 @ gmail.com>
2016-09-14 21:47:24 +08:00
823481ae22 Merge branch 'master' of https://github.com/hishamhm/htop 2016-09-11 23:29:21 -03:00
96157870af Downgrade requirement to Libtool 2.4.0.
Courtesy to OpenWRT environments as requested by @champtar in #540.
2016-09-11 23:28:17 -03:00
5a5a7c8d6b Merge pull request #555 from ivan/master
Fix column misalignment for priority -101 threads
2016-09-06 15:08:26 -03:00
07086fcf77 Fix column misalignment for priority -101 threads
BFS-patched kernels can have kernel threads with priority -101.
This change makes priority -101 display as "RT", just like priority -100.

Related: https://github.com/hishamhm/htop/issues/314
2016-09-06 09:22:38 +00:00
6d4df3d138 Merge branch 'master' of https://github.com/hishamhm/htop 2016-08-30 12:38:12 -03:00
d4a8023b72 Fix typos, by @Gelma.
Closes #546.
2016-08-30 12:37:31 -03:00
99f7ff9bd2 Merge pull request #550 from Explorer09/signals-tweaks
Mark signal tables 'const'
2016-08-30 12:17:20 -03:00
1f3d85b617 Mark signal tables 'const'
Specifically, Platform_signals[] and Platform_numberOfSignals. Both are
not supposed to be mutable. Marking them 'const' puts them into rodata
sections in binary. And for Platform_numberOfSignals, this aids
optimization (aids only Link Time Optimization for now). :)

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2016-08-30 20:41:17 +08:00
cceab15b9d free(NULL) is a valid no-op, so let's make String_freeArray(NULL) valid too. 2016-08-24 18:12:35 -03:00
bd5d37f297 Return when reading cmdline fails (e.g. zombie process) 2016-08-24 18:11:10 -03:00
16406ea330 Point users to the official release tarballs. 2016-08-23 15:43:27 -03:00
d3f575264b Merge pull request #537 from Explorer09/string-utils
Optimize Strings_startWith()
2016-08-11 00:44:42 -03:00
bf35921abb Optimize Strings_startWith()
Use strncmp() combined with a strlen() will give better performance
than a strstr in worst case. Especially when the match prefix is a
constant and not a variable.

While we are at it, replace the match() function in linux/Battery.c,
which uses a naive algorithm, with a macro that does better job by
utilizing Strings_startWith().

    $ gcc --version | head -n 1
    gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
    $ uname -m
    x86_64
    $ size htop.old htop.new
       text   data    bss    dec    hex filename
     137929  15112   3776 156817  26491 htop.old
     137784  15104   3776 156664  263f8 htop.new

Signed-off-by: Kang-Che Sung <explorer09 @ gmail.com>
2016-08-11 10:49:35 +08:00
b269eb24b0 Merge pull request #534 from Explorer09/issue-532
Change scrolling behavior to make it more similar to other applications.
2016-08-09 13:06:33 -03:00
3d9868833e Fix scrolling behavior change caused by 759caf0f
Commit "Make PgDown behavior more usual." 759caf0f8f
silently changes the PageDown scrolling behavior that, instead of
scrolling one window down until the end of the window touches the end
of the list, the window simply repositions itself in a way that the
selected item always become the last item in the new window.

The commit reverts the behavior, and also fixes sanity conditionals
so that the scrollV variable will _never_ become negative or out-of-
bound.

Fixes issue #532. Also keep the problem #480 addressed.

Signed-off-by: Kang-Che Sung <explorer09 @ gmail.com>
2016-08-07 07:49:37 +08:00
3cd0339423 Merge branch 'master' of https://github.com/hishamhm/htop 2016-07-28 11:39:15 -03:00
ef879b4a22 'Follow' only if element is found in search 2016-07-28 11:37:44 -03:00
f161365d72 Merge pull request #526 from Explorer09/bar-tweaks
Let BarMeterMode_characters[] be const array.
2016-07-23 13:48:50 -03:00
6b9b6db655 Let BarMeterMode_characters[] be const array. 2016-07-22 14:58:29 +08:00
f80e577c59 Changelog for htop 2.0.2 2016-07-21 17:12:45 -03:00
81552d4ab5 Preparing release 2.0.2, a minor bugfix release. 2016-07-21 16:54:41 -03:00
0108117d0b Mouse-friendly functions bar for meters panel
Before:
SpaceStyle EnterMove  DelDeleteEscDone  |
~~~~~      ~~~~~      ~~~      ~~~      |
UpUp     DnDown  LtLeft  RtRight EnterConfirmDelDeleteEscDone  |
~~       ~~      ~~      ~~      ~~~~~       ~~~      ~~~      |

After:
SpaceStyle EnterMove                                           DelDeleteF10Done
~~~~~      ~~~~~      ~~                                       ~~~      ~~~
SpaceStyle EnterLock  UpUp    DnDown  <-Left  ->Right          DelDeleteF10Done
~~~~~      ~~~~~      ~~      ~~      ~~      ~~      ~~       ~~~      ~~~

* Align 'Delete' and 'Done' to the right to match functions on other
  screens. (Accidental clicking is avoided as a side benefit.)
* You could change meter type while in moving mode. New bar now hints
  this.
* Two Enter key functions are put in the same place and so mouse clicks
  there act like functions toggle. (The wording change to 'Lock' is
  also to reflect this.)
* '<-' and '->' instead of 'Lt' and 'Rt' abbreviation as the latter is
  not widely seen and arrows shapes are obvious. :)
* 'Esc' key for 'Done' in this context may not be intuitive, comparing
  to 'F10'. While I wish there be a Cancel/Undo function for 'Esc', it
  wouldn't hurt if we write 'F10' for 'Done' on functions bar for now.
2016-07-16 21:13:46 +08:00
a41c9b3925 Reword 'Type' to 'Style' for meters.
Per @hishamhm's suggestion. This is UI change only.
In code the class names and symbols still refer them as "modes".
2016-07-16 21:13:46 +08:00
265d04821a Merge pull request #524 from ricardo0y/cross_compile_with_ncurses_config
Allow to override ncurses*-config path
2016-07-12 10:02:51 -03:00
78b82d0fdc Allow to override ncurses*-config path
This will be used when cross-compiling with ncurses*-config generated for the
target, using constructs like
HTOP_NCURSES_CONFIG_SCRIPT=/path/to/ncurses5-config

Signed-off-by: Ricardo Martincoski <ricardo.martincoski@gmail.com>
2016-07-11 20:17:13 -03:00
7f9c82f28d Refresh whole panel on Ctrl+L in infoscreen.
Closes #520.
2016-06-23 13:25:58 -03:00
7d5ef246f1 Let's keep it simple then! 2016-06-20 12:11:49 -03:00
0fa03322a9 Dynamically adjust the size of line reads
* Dynamically adjust the size of line reads.
* Remove some more uses of fgets with arbitrary sizes.
* Fix reading of lines and width of n column.

Fixes #514.
2016-06-19 18:55:35 -03:00
52f814481c While we're at it, get rid of another sprintf. 2016-06-19 18:30:20 -03:00
b139671cbd Moving left and right needs a full redraw. 2016-06-19 18:05:18 -03:00
adcc944e35 Use "-" as the Ctrl and Alt joiner. 2016-06-17 22:14:07 -03:00
a5ef374bb8 Merge pull request #511 from Explorer09/man-page
Document Alt+{h,j,k,l} and Ctrl+{A,E} into man page
2016-06-17 11:50:15 -03:00
6dd32d3604 Document Alt+{h,j,k,l} and Ctrl+{A,E} into man page
Rewrite the scrolling part in the man page so that each key become clearer on
what it does. Also officially document the Alt+{h,j,k,l} key alternatives and
Ctrl+A, Ctrl+E, '^', '$' keys (see issue #508).
2016-06-17 10:19:19 +08:00
8cd045cc79 Merge branch 'master' of https://github.com/hishamhm/htop 2016-06-15 12:46:34 -03:00
0128d222b9 Added Ctrl+A and Ctrl+E to go to beginning and end of line.
(Also, '^' and '$')
Closes #508.
2016-06-15 12:45:23 -03:00
1a13b4d0f4 Don't store invisible trailing whitespace 2016-06-15 12:41:50 -03:00
9ec41d1b65 Merge pull request #505 from Explorer09/xStrdup
Fix xStrdup debug build failure & allow Clang to use ((nonnull))
2016-06-04 11:45:29 -03:00
c0e37bc5f5 Fix xStrdup debug build failure & allow Clang to use ((nonnull)) 2016-06-03 10:14:27 +08:00
f9c1db514d Merge pull request #504 from Explorer09/xStrdup
Fixes upon xStrdup(NULL) problem
2016-06-02 17:29:14 -03:00
3297616efa Add assert and __attribute__((nonnull)) on xStrdup
__attribute__((nonnull)) will help catching "calling with NULL" mistake on
compile time.

I also convert xStrdup into a macro, that will do assert() inline when the
code is *not* built with -DNDEBUG . For release builds (with -DNDEBUG),
preprocessor trick will ensure that generated code remains the same.
2016-06-02 20:38:24 +08:00
e288f690af Don't check if (!str) in xStrdup
This effectively reverts "Stricter strdup." 4674b4a732

If str is NULL upon the calling of strdup(), it should crash with SIGSEGV.
Just let it crash. Adding the "if (!str) fail();" code serves nothing but
bloat.
2016-06-02 18:30:18 +08:00
ea9dc4ab74 Merge branch 'master' of https://github.com/hishamhm/htop 2016-05-30 15:06:52 -03:00
112db9a609 Handle SIGQUIT. Closes #503. 2016-05-30 15:06:22 -03:00
5db1b0e9a0 Remove extra checks made redundant by XAlloc. 2016-05-30 12:22:33 -03:00
4674b4a732 Stricter strdup. 2016-05-30 12:22:22 -03:00
7ededce9b5 Silence cast warning. 2016-05-30 12:22:07 -03:00
3126cfce6c Merge pull request #500 from Explorer09/meter-tweaks
Remove redundant is-null checks on free(Meter.drawData)
2016-05-27 16:30:45 -03:00
313b3d3752 Remove redundant is-null checks on free(Meter.drawData) 2016-05-27 17:11:54 +08:00
fa0c637c55 Silence warnings about seteuid return value.
Closes #483.
2016-05-25 21:37:07 -03:00
b7ac416634 Fixes #498. 2016-05-25 21:29:06 -03:00
6cc0a8c820 Make sure a pointer fits in the argument! 2016-05-25 16:46:47 -03:00
645057d81a Use set_escdelay() to avoid problems with ESCDELAY as a macro. 2016-05-19 16:09:47 -03:00
6c1be63291 Fail early if libtool is not present. 2016-05-19 16:04:04 -03:00
642ccb49ca Merge pull request #445 from Explorer09/configure-ac
configure.ac fixes
2016-05-19 15:57:31 -03:00
6028e1b4c4 Merge pull request #496 from tcreech/lwp_hack
FreeBSD: fix multithreaded CPU% in process list
2016-05-19 15:44:57 -03:00
95d1984339 Merge pull request #485 from Cubox-/master
Fix FreeBSD CPU bars calculation
2016-05-16 19:19:36 -03:00
c0df404701 Update INSTALL text from autoconf-2.69 2016-05-08 14:35:20 +08:00
b71b07f5e0 Reorder configure macros to avoid "missing script" warning.
3 effects in this commit, with the first being the main one:

1. Fix the "`missing' script is too old or missing" warning. See:
   <https://lists.gnu.org/archive/html/automake/2010-08/msg00108.html>

2. By moving AC_CANONICAL_TARGET down in order, we are now able to
   set the directory for auxiliary scripts. For now it's still './'.
   I added the line "AC_CONFIG_AUX_DIR([.])" to show that the directory
   change is possible.

3. AC_USE_SYSTEM_EXTENSIONS includes checks from AC_PROG_CC, by moving
   the former macro down, we can save size in 'configure' by not
   generating repeated checks.
2016-05-08 14:35:20 +08:00
f0df28a470 Replace deprecated autoconf macros.
AC_HELP_STRING -> AS_HELP_STRING
AC_TRY_COMPILE -> AC_COMPILE_IFELSE([AC_LANG_PROGRAM([...])],...)
AC_CONFIG_HEADER -> AC_CONFIG_HEADERS
AC_PROG_LIBTOOL -> LT_INIT

Note: There might be more deprecated macros that I haven't noticed.
I just wish to avoid painful highlighting from my text editor (gedit)
that complains about them. :)
2016-05-08 14:35:20 +08:00
7d72715a6b Merge pull request #490 from Explorer09/macro-fixes
Fix macro Header_forEachColumn
2016-05-07 14:34:50 -03:00
54621e8b8f Fix macro Header_forEachColumn
The (this_) token was not expanded properly, but the bug was not caught
because all uses of this macro specifies (this_)=this .

Also parenthesize macro tokens to prevent further problems.
2016-05-07 14:57:51 +08:00
572546f806 Auto-follow process after a search.
See #237.
2016-05-05 10:30:06 -03:00
d464be13db Merge branch 'master' of https://github.com/hishamhm/htop 2016-05-04 22:43:03 -03:00
759caf0f8f Make PgDown behavior more usual.
Closes #480.
2016-05-04 22:41:52 -03:00
cdc91b0b33 Merge pull request #472 from jrtc27/hurd
Use Linux backend on the Hurd
2016-05-04 15:42:16 -03:00
1754a1cd48 Merge pull request #473 from fasterit/upstream/typo1
Fix a typo in the man page (Debian Lintian spelling-error-in-manpage)
2016-05-04 15:40:31 -03:00
8e6ffdb0ab Merge pull request #475 from fasterit/upstream/desktop-keywords
Add Keywords entry to .desktop file (Debian Lintian warning desktop-e…
2016-05-04 15:40:16 -03:00
c37be409a9 Improve reproducible builds.
Use a SOURCE_DATA_EPOCH friendly date.
Suggested by @fasterit in #476.
2016-05-04 15:34:49 -03:00
fa1b5d1e2e Fix a small undefined behavior detected by libubsan. 2016-05-04 15:34:22 -03:00
19f0f4db27 Merge pull request #488 from Explorer09/func-naming
Rename Meter.setValues() functions to updateValues()
2016-05-04 15:18:52 -03:00
9dea20e068 Rename Meter.setValues() functions to updateValues()
Rationale (copied from htop issue #471):
The function name "setValues" is misleading. For most OOP (object-
oriented programming) contexts, setXXX functions mean they will change
some member variables of an object into something specified in
function arguments. But in the *Meter_setValues() case, the new values
are not from the arguments, but from a hard-coded source. The caller
is not supposed to change the values[] to anything it likes, but
rather to "update" the values from the source. Hence, updateValues is
a better name for this family of functions.
2016-05-04 13:39:26 +08:00
2ea4bee66d Merge pull request #486 from mmcco/null-check
Remove needless allocation error conditions
2016-04-29 23:01:32 -03:00
1809f4be98 Remove needless allocation error conditions
These allocations were converted to use xMalloc et al. and no longer
need error checks.
2016-04-29 21:10:05 -04:00
5776cb56b4 Revert "Fix FreeBSD CPU% calculation"
This reverts commit f554f08fa9.
2016-04-28 21:42:18 +02:00
74f8b31040 Add Keywords entry to .desktop file (Debian Lintian warning desktop-entry-lacks-keywords-entry)
Debian patch from
https://anonscm.debian.org/cgit/collab-maint/htop.git/tree/debian/patches/002-lintian-warning-fix-desktop-keywords.patch
2016-04-19 19:06:03 +02:00
4d3f483b96 Fix a typo in the man page (Debian Lintian spelling-error-in-manpage)
Debian patch from
https://anonscm.debian.org/cgit/collab-maint/htop.git/tree/debian/patches/001-lintian-warning-fix-man-typo.patch
2016-04-19 18:32:39 +02:00
2de52862a6 Use Linux backend on the Hurd 2016-04-18 23:57:30 +01:00
a9508275cc Use $target_os instead of $target in configure.ac 2016-04-18 23:55:55 +01:00
306c5443ae Update header. 2016-03-31 11:01:23 -03:00
d15555ed2c Merge branch 'master' of https://github.com/hishamhm/htop 2016-03-31 00:19:59 -03:00
d64f2bdd56 If task_for_pid fails, stop trying. 2016-03-31 00:18:42 -03:00
6c2f698a47 Merge pull request #458 from Explorer09/bar-tweaks
BarMeterMode_draw() minor code improvement
2016-03-25 01:57:10 -03:00
7b3c8bc77a BarMeterMode_draw minor code improvement
Removed a loop that sets the bar[] buffer with spaces and merged that
task to the snprintf() call just below. No need for the barOffset
variable. Display behavior is unchanged.

Size comparision (when compiled on Ubuntu 14.04 64-bit):

    $ size htop.old htop.new
       text    data     bss     dec     hex filename
     137312   15112    3776  156200   26228 htop.old
     137216   15112    3776  156104   261c8 htop.new
2016-03-22 14:52:31 +08:00
3a4c0fa2d6 Merge pull request #452 from Explorer09/cpu-meter-tweaks
Assert (Platform_meterTypes[0] == &CPUMeter_class)
2016-03-20 05:02:07 -03:00
328de35623 Assert (Platform_meterTypes[0]==&CPUMeter_class)
Just assume Platform_meterTypes[0] is always &CPUMeter_class for every
platform. This removes a conditional in AvailableMetersPanel_new().

Also adds some comments about the logic here. Without assuming
Platform_meterTypes[0], the (int i=1) clause in this for loop will not
make sense. (I.e. Why not (int i=0)? )

Also replaced a sprintf() call with safer snprintf() in code further
below.
2016-03-19 15:01:13 +08:00
c8a735e471 Merge pull request #444 from Explorer09/meter-maxitems
Explicit "maxItems" property of meters
2016-03-13 20:57:08 -03:00
99fb3070a2 Explicit "maxItems" property of meters
Two changes in this commit:
- All meters now explicitly specify "maxItems" property, even for just
  1 item. (Exception is "container" CPU meter classes, which use
  CUSTOM_METERMODE.)
- "maxItems" being 0 is now allowed. This will let bar meters and graph
  meters render an empty meter.
2016-03-11 10:54:34 +08:00
7d3f67e822 Revert 5c593fae42 (xCalloc)
calloc() allows 'nmemb' or 'size' to be zero, in which case NULL may be
returned. Letting htop die because of either argument being zero doesn't
make sense.

As a side note: As size_t is unsigned, compiler should be able to optimize
conditional (nmemb > 0 && size > 0) to (nmemb && size). This theorically
shouldn't increase code size too much.
2016-03-11 10:43:31 +08:00
3283c6d23c Merge pull request #441 from Explorer09/issue-438
Avoid overlapping key values defined by curses (Real fix).
2016-03-09 02:03:11 -03:00
8a928c8b89 Avoid overlapping key values defined by curses (Real fix).
Real fix for issue #438.
2016-03-09 10:16:34 +08:00
f295a52ed9 Avoid overlapping key values defined by curses.
Closes #438.
2016-03-08 12:23:18 -03:00
cc8375f9ea FreeBSD: use KERN_PROC_PROC with kvm_getprocs to avoid erroneously combining LWPs 2016-02-28 22:41:50 -05:00
124 changed files with 6124 additions and 1444 deletions

9
.gitignore vendored
View File

@ -4,6 +4,12 @@ htop
# all object files
*.o
# skip all backups
*.bak
*~
.*.sw?
# skip coverage files
*.gcda
*/*.gcda
*.gcno
@ -14,6 +20,7 @@ htop
.deps/
Makefile
Makefile.in
INSTALL
aclocal.m4
autom4te.cache/
compile
@ -22,6 +29,7 @@ config.h
config.h.in
config.log
config.status
config.cache
config.sub
configure
depcomp
@ -32,4 +40,3 @@ ltmain.sh
m4/
missing
stamp-h1

114
Action.c
View File

@ -26,6 +26,7 @@ in the source distribution for its full text.
#include <math.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <sys/param.h>
#include <sys/time.h>
@ -80,7 +81,7 @@ Object* Action_pickFromVector(State* st, Panel* list, int x) {
header->pl->following = pid;
unfollow = true;
}
ScreenManager_run(scr, &panelFocus, &ch);
ScreenManager_run(scr, &panelFocus, &ch, NULL);
if (unfollow) {
header->pl->following = -1;
}
@ -106,7 +107,7 @@ static void Action_runSetup(Settings* settings, const Header* header, ProcessLis
CategoriesPanel_makeMetersPage(panelCategories);
Panel* panelFocus;
int ch;
ScreenManager_run(scr, &panelFocus, &ch);
ScreenManager_run(scr, &panelFocus, &ch, "Setup");
ScreenManager_delete(scr);
if (settings->changed) {
Header_writeBackToSettings(header);
@ -115,7 +116,7 @@ static void Action_runSetup(Settings* settings, const Header* header, ProcessLis
static bool changePriority(MainPanel* panel, int delta) {
bool anyTagged;
bool ok = MainPanel_foreachProcess(panel, (MainPanel_ForeachProcessFn) Process_changePriorityBy, delta, &anyTagged);
bool ok = MainPanel_foreachProcess(panel, (MainPanel_ForeachProcessFn) Process_changePriorityBy, (Arg){ .i = delta }, &anyTagged);
if (!ok)
beep();
return anyTagged;
@ -142,7 +143,7 @@ static void tagAllChildren(Panel* panel, Process* parent) {
pid_t ppid = parent->pid;
for (int i = 0; i < Panel_size(panel); i++) {
Process* p = (Process*) Panel_get(panel, i);
if (!p->tag && p->ppid == ppid) {
if (!p->tag && Process_isChildOf(p, ppid)) {
tagAllChildren(panel, p);
}
}
@ -155,10 +156,26 @@ static bool expandCollapse(Panel* panel) {
return true;
}
static bool collapseIntoParent(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return false;
pid_t ppid = Process_getParentPid(p);
for (int i = 0; i < Panel_size(panel); i++) {
Process* q = (Process*) Panel_get(panel, i);
if (q->pid == ppid) {
q->showChildren = false;
Panel_setSelected(panel, i);
return true;
}
}
return false;
}
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
settings->sortKey = sortKey;
settings->direction = 1;
settings->treeView = false;
ScreenSettings* ss = settings->ss;
ss->sortKey = sortKey;
ss->direction = 1;
ss->treeView = false;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
}
@ -166,11 +183,12 @@ static Htop_Reaction sortBy(State* st) {
Htop_Reaction reaction = HTOP_OK;
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Sort ", "Cancel "));
Panel_setHeader(sortPanel, "Sort by");
ProcessField* fields = st->settings->fields;
ScreenSettings* ss = st->settings->ss;
ProcessField* fields = ss->fields;
for (int i = 0; fields[i]; i++) {
char* name = String_trim(Process_fields[fields[i]].name);
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
if (fields[i] == st->settings->sortKey)
if (fields[i] == ss->sortKey)
Panel_setSelected(sortPanel, i);
free(name);
}
@ -185,6 +203,7 @@ static Htop_Reaction sortBy(State* st) {
// ----------------------------------------
static Htop_Reaction actionResize(State* st) {
clear();
Panel_resize(st->panel, COLS, LINES-(st->panel->y)-1);
return HTOP_REDRAW_BAR;
}
@ -218,8 +237,9 @@ static Htop_Reaction actionToggleProgramPath(State* st) {
}
static Htop_Reaction actionToggleTreeView(State* st) {
st->settings->treeView = !st->settings->treeView;
if (st->settings->treeView) st->settings->direction = 1;
ScreenSettings* ss = st->settings->ss;
ss->treeView = !ss->treeView;
if (ss->treeView) ss->direction = 1;
ProcessList_expandTree(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
@ -247,7 +267,7 @@ static Htop_Reaction actionLowerPriority(State* st) {
}
static Htop_Reaction actionInvertSortOrder(State* st) {
Settings_invertSortOrder(st->settings);
ScreenSettings_invertSortOrder(st->settings->ss);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
}
@ -260,14 +280,61 @@ static Htop_Reaction actionExpandOrCollapse(State* st) {
return changed ? HTOP_RECALCULATE : HTOP_OK;
}
static Htop_Reaction actionCollapseIntoParent(State* st) {
if (!st->settings->ss->treeView) {
return HTOP_OK;
}
bool changed = collapseIntoParent(st->panel);
return changed ? HTOP_RECALCULATE : HTOP_OK;
}
static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) {
return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
return st->settings->ss->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
}
static Htop_Reaction actionQuit() {
return HTOP_QUIT;
}
static Htop_Reaction actionNextScreen(State* st) {
Settings* settings = st->settings;
settings->ssIndex++;
if (settings->ssIndex == settings->nScreens) {
settings->ssIndex = 0;
}
settings->ss = settings->screens[settings->ssIndex];
return HTOP_REFRESH;
}
static Htop_Reaction actionPrevScreen(State* st) {
Settings* settings = st->settings;
if (settings->ssIndex == 0) {
settings->ssIndex = settings->nScreens - 1;
} else {
settings->ssIndex--;
}
settings->ss = settings->screens[settings->ssIndex];
return HTOP_REFRESH;
}
Htop_Reaction Action_setScreenTab(Settings* settings, int x) {
int s = 2;
for (unsigned int i = 0; i < settings->nScreens; i++) {
if (x < s) {
return 0;
}
const char* name = settings->screens[i]->name;
int len = strlen(name);
if (x <= s + len + 1) {
settings->ssIndex = i;
settings->ss = settings->screens[i];
return HTOP_REFRESH;
}
s += len + 3;
}
return 0;
}
static Htop_Reaction actionSetAffinity(State* st) {
if (st->pl->cpuCount == 1)
return HTOP_OK;
@ -284,7 +351,7 @@ static Htop_Reaction actionSetAffinity(State* st) {
void* set = Action_pickFromVector(st, affinityPanel, 15);
if (set) {
Affinity* affinity = AffinityPanel_getAffinity(affinityPanel, st->pl);
bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (size_t) affinity, NULL);
bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (Arg){ .v = affinity }, NULL);
if (!ok) beep();
Affinity_delete(affinity);
}
@ -301,7 +368,7 @@ static Htop_Reaction actionKill(State* st) {
Panel_setHeader(st->panel, "Sending...");
Panel_draw(st->panel, true);
refresh();
MainPanel_foreachProcess((MainPanel*)st->panel, (MainPanel_ForeachProcessFn) Process_sendSignal, (size_t) sgn->key, NULL);
MainPanel_foreachProcess((MainPanel*)st->panel, (MainPanel_ForeachProcessFn) Process_sendSignal, (Arg){ .i = sgn->key }, NULL);
napms(500);
}
}
@ -328,7 +395,7 @@ static Htop_Reaction actionFilterByUser(State* st) {
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionFollow(State* st) {
Htop_Reaction Action_follow(State* st) {
st->pl->following = MainPanel_selectedPid((MainPanel*)st->panel);
Panel_setSelectionColor(st->panel, CRT_colors[PANEL_SELECTION_FOLLOW]);
return HTOP_KEEP_FOLLOWING;
@ -381,7 +448,7 @@ static Htop_Reaction actionRedraw() {
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
static struct { const char* key; const char* info; } helpLeft[] = {
static const struct { const char* key; const char* info; } helpLeft[] = {
{ .key = " Arrows: ", .info = "scroll process list" },
{ .key = " Digits: ", .info = "incremental PID search" },
{ .key = " F3 /: ", .info = "incremental name search" },
@ -395,11 +462,11 @@ static struct { const char* key; const char* info; } helpLeft[] = {
{ .key = " F6 + -: ", .info = "expand/collapse tree" },
{ .key = " P M T: ", .info = "sort by CPU%, MEM% or TIME" },
{ .key = " I: ", .info = "invert sort order" },
{ .key = " F6 >: ", .info = "select sort column" },
{ .key = " F6 > .: ", .info = "select sort column" },
{ .key = NULL, .info = NULL }
};
static struct { const char* key; const char* info; } helpRight[] = {
static const struct { const char* key; const char* info; } helpRight[] = {
{ .key = " Space: ", .info = "tag process" },
{ .key = " c: ", .info = "tag process and its children" },
{ .key = " U: ", .info = "untag all processes" },
@ -414,7 +481,7 @@ static struct { const char* key; const char* info; } helpRight[] = {
{ .key = " l: ", .info = "list open files with lsof" },
{ .key = " s: ", .info = "trace syscalls with strace" },
{ .key = " ", .info = "" },
{ .key = " F2 S: ", .info = "setup" },
{ .key = " F2 C S: ", .info = "setup" },
{ .key = " F1 h: ", .info = "show this help screen" },
{ .key = " F10 q: ", .info = "quit" },
{ .key = NULL, .info = NULL }
@ -450,7 +517,7 @@ static Htop_Reaction actionHelp(State* st) {
addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/");
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_STEAL], "virtualiz");
addattrstr(CRT_colors[CPU_GUEST], "virtualiz");
addattrstr(CRT_colors[BAR_SHADOW], " used%");
}
addattrstr(CRT_colors[BAR_BORDER], "]");
@ -556,8 +623,9 @@ void Action_setBindings(Htop_Action* keys) {
keys['+'] = actionExpandOrCollapse;
keys['='] = actionExpandOrCollapse;
keys['-'] = actionExpandOrCollapse;
keys['\177'] = actionCollapseIntoParent;
keys['u'] = actionFilterByUser;
keys['F'] = actionFollow;
keys['F'] = Action_follow;
keys['S'] = actionSetup;
keys['C'] = actionSetup;
keys[KEY_F(2)] = actionSetup;
@ -571,5 +639,7 @@ void Action_setBindings(Htop_Action* keys) {
keys['U'] = actionUntagAll;
keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen;
keys['\t'] = actionNextScreen;
keys[KEY_SHIFT_TAB] = actionPrevScreen;
}

View File

@ -49,6 +49,10 @@ Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey);
// ----------------------------------------
Htop_Reaction Action_setScreenTab(Settings* settings, int x);
Htop_Reaction Action_follow(State* st);
void Action_setBindings(Htop_Action* keys);

View File

@ -52,7 +52,7 @@ Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity) {
int curCpu = 0;
for (int i = 0; i < pl->cpuCount; i++) {
char number[10];
snprintf(number, 9, "%d", Settings_cpuId(pl->settings, i));
xSnprintf(number, 9, "%d", Settings_cpuId(pl->settings, i));
bool mode;
if (curCpu < affinity->used && affinity->cpus[curCpu] == i) {
mode = true;

View File

@ -26,7 +26,7 @@ typedef struct AvailableColumnsPanel_ {
}*/
static const char* AvailableColumnsFunctions[] = {" ", " ", " ", " ", "Add ", " ", " ", " ", " ", "Done ", NULL};
static const char* const AvailableColumnsFunctions[] = {" ", " ", " ", " ", "Add ", " ", " ", " ", " ", "Done ", NULL};
static void AvailableColumnsPanel_delete(Object* object) {
Panel* super = (Panel*) object;
@ -81,7 +81,7 @@ AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns) {
for (int i = 1; i < Platform_numberOfFields; i++) {
if (i != COMM && Process_fields[i].description) {
char description[256];
snprintf(description, sizeof(description), "%s - %s", Process_fields[i].name, Process_fields[i].description);
xSnprintf(description, sizeof(description), "%s - %s", Process_fields[i].name, Process_fields[i].description);
Panel_add(super, (Object*) ListItem_new(description, i));
}
}

View File

@ -112,20 +112,22 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade
this->scr = scr;
Panel_setHeader(super, "Available meters");
// Platform_meterTypes[0] should be always (&CPUMeter_class), which we will
// handle separately in the code below.
for (int i = 1; Platform_meterTypes[i]; i++) {
MeterClass* type = Platform_meterTypes[i];
if (type != &CPUMeter_class) {
const char* label = type->description ? type->description : type->uiName;
Panel_add(super, (Object*) ListItem_new(label, i << 16));
}
assert(type != &CPUMeter_class);
const char* label = type->description ? type->description : type->uiName;
Panel_add(super, (Object*) ListItem_new(label, i << 16));
}
// Handle (&CPUMeter_class)
MeterClass* type = &CPUMeter_class;
int cpus = pl->cpuCount;
if (cpus > 1) {
Panel_add(super, (Object*) ListItem_new("CPU average", 0));
for (int i = 1; i <= cpus; i++) {
char buffer[50];
sprintf(buffer, "%s %d", type->uiName, i);
xSnprintf(buffer, 50, "%s %d", type->uiName, i);
Panel_add(super, (Object*) ListItem_new(buffer, i));
}
} else {

View File

@ -32,7 +32,7 @@ int BatteryMeter_attributes[] = {
BATTERY
};
static void BatteryMeter_setValues(Meter * this, char *buffer, int len) {
static void BatteryMeter_updateValues(Meter * this, char *buffer, int len) {
ACPresence isOnAC;
double percent;
@ -40,7 +40,7 @@ static void BatteryMeter_setValues(Meter * this, char *buffer, int len) {
if (percent == -1) {
this->values[0] = 0;
snprintf(buffer, len, "n/a");
xSnprintf(buffer, len, "n/a");
return;
}
@ -58,11 +58,11 @@ static void BatteryMeter_setValues(Meter * this, char *buffer, int len) {
}
if (isOnAC == AC_PRESENT) {
snprintf(buffer, len, onAcText, percent);
xSnprintf(buffer, len, onAcText, percent);
} else if (isOnAC == AC_ABSENT) {
snprintf(buffer, len, onBatteryText, percent);
xSnprintf(buffer, len, onBatteryText, percent);
} else {
snprintf(buffer, len, unknownText, percent);
xSnprintf(buffer, len, unknownText, percent);
}
return;
@ -73,8 +73,9 @@ MeterClass BatteryMeter_class = {
.extends = Class(Meter),
.delete = Meter_delete
},
.setValues = BatteryMeter_setValues,
.updateValues = BatteryMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 100.0,
.attributes = BatteryMeter_attributes,
.name = "Battery",

56
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,56 @@
Contributing Guide
==================
Hello, and thank you so much for taking your time to contribute in any way to
htop! There are many ways to contribute, and I'll try to list them below. The
support from the free software community has been amazing over the years and
it is the number one thing that keeps me going, maintaining and improving
something that started as a tiny pet project back in 2004 and that nowadays is
a piece of software used all over the world, in both reality [and
fiction!](http://hisham.hm/htop/index.php?page=sightings). Cheers!
-- Hisham Muhammad
Bug Reports
-----------
Bug reports should be posted in the [Github issue
tracker](http://github.com/hishamhm/htop/issues). (I reply to them all, but I
usually do it in batches! :) ) Bug reports are extremely important since it's
impossible for me to test htop in every possible system, distribution and
scenario. Your feedback is what keeps the tool stable and always improving!
Thank you!
Pull Requests
-------------
Code contributions are most welcome! Just [fork the
repo](http://github.com/hishamhm/htop) and send a [pull
request](https://github.com/hishamhm/htop/pulls). Help is especially
appreciated for support of platforms other than Linux. If proposing new
features, please be mindful that htop is a system tool that needs to keep a
small footprint and perform well on systems under stress -- so unfortunately I
can't accept every new feature proposed, as I need to keep the tool slim and
maintainable. Great ideas backed by a PR are always carefully considered for
inclusion, though! Also, PRs containing bug fixes and portability tweaks are a
no-brainer, please send those in!
Feature Requests
----------------
Back when htop was hosted in SourceForge, there used to be separate Bug
Tracker and Feature Request pages. These go all lumped together under "Issues"
in Github, which is a bit confusing. For this reason, I close Feature Requests
and file them with the [`feature
request`](https://github.com/hishamhm/htop/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22feature+request%22+)
label, where they remain accessible, but not mixed with actual bug reports.
This doesn't mean I'm dismissing or ignoring feature requests right away! It's
just an organizational issue (with Github, really!).
Donations
---------
If you like htop, feel free to [buy the author a
beer](http://hisham.hm/htop/index.php?page=donate). :-)

View File

@ -48,22 +48,22 @@ static void CPUMeter_init(Meter* this) {
int cpu = this->param;
if (this->pl->cpuCount > 1) {
char caption[10];
sprintf(caption, "%-3d", Settings_cpuId(this->pl->settings, cpu - 1));
xSnprintf(caption, sizeof(caption), "%-3d", Settings_cpuId(this->pl->settings, cpu - 1));
Meter_setCaption(this, caption);
}
if (this->param == 0)
Meter_setCaption(this, "Avg");
}
static void CPUMeter_setValues(Meter* this, char* buffer, int size) {
static void CPUMeter_updateValues(Meter* this, char* buffer, int size) {
int cpu = this->param;
if (cpu > this->pl->cpuCount) {
snprintf(buffer, size, "absent");
xSnprintf(buffer, size, "absent");
return;
}
memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT);
double percent = Platform_setCPUValues(this, cpu);
snprintf(buffer, size, "%5.1f%%", percent);
xSnprintf(buffer, size, "%5.1f%%", percent);
}
static void CPUMeter_display(Object* cast, RichString* out) {
@ -74,44 +74,44 @@ static void CPUMeter_display(Object* cast, RichString* out) {
RichString_append(out, CRT_colors[METER_TEXT], "absent");
return;
}
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_NORMAL]);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NORMAL]);
RichString_append(out, CRT_colors[METER_TEXT], ":");
RichString_append(out, CRT_colors[CPU_NORMAL], buffer);
if (this->pl->settings->detailedCPUTime) {
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_KERNEL]);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
RichString_append(out, CRT_colors[METER_TEXT], "sy:");
RichString_append(out, CRT_colors[CPU_KERNEL], buffer);
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_NICE]);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
RichString_append(out, CRT_colors[METER_TEXT], "ni:");
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer);
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_IRQ]);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "hi:");
RichString_append(out, CRT_colors[CPU_IRQ], buffer);
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "si:");
RichString_append(out, CRT_colors[CPU_SOFTIRQ], buffer);
if (this->values[CPU_METER_STEAL]) {
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_STEAL]);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_STEAL]);
RichString_append(out, CRT_colors[METER_TEXT], "st:");
RichString_append(out, CRT_colors[CPU_STEAL], buffer);
}
if (this->values[CPU_METER_GUEST]) {
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_GUEST]);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_GUEST]);
RichString_append(out, CRT_colors[METER_TEXT], "gu:");
RichString_append(out, CRT_colors[CPU_GUEST], buffer);
}
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_IOWAIT]);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IOWAIT]);
RichString_append(out, CRT_colors[METER_TEXT], "wa:");
RichString_append(out, CRT_colors[CPU_IOWAIT], buffer);
} else {
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_KERNEL]);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
RichString_append(out, CRT_colors[METER_TEXT], "sys:");
RichString_append(out, CRT_colors[CPU_KERNEL], buffer);
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_NICE]);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
RichString_append(out, CRT_colors[METER_TEXT], "low:");
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer);
if (this->values[CPU_METER_IRQ]) {
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_IRQ]);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "vir:");
RichString_append(out, CRT_colors[CPU_GUEST], buffer);
}
@ -215,7 +215,7 @@ MeterClass CPUMeter_class = {
.delete = Meter_delete,
.display = CPUMeter_display
},
.setValues = CPUMeter_setValues,
.updateValues = CPUMeter_updateValues,
.defaultMode = BAR_METERMODE,
.maxItems = CPU_METER_ITEMCOUNT,
.total = 100.0,
@ -312,8 +312,8 @@ MeterClass LeftCPUs2Meter_class = {
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "LeftCPUs2",
.description = "CPUs (1&2/4): first half in 2 shorter columns",
.uiName = "CPUs (1&2/4)",
.description = "CPUs (1&2/4): first half in 2 shorter columns",
.caption = "CPU",
.draw = DualColCPUsMeter_draw,
.init = AllCPUsMeter_init,

138
CRT.c
View File

@ -5,6 +5,7 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h"
#include "CRT.h"
#include "StringUtils.h"
@ -17,8 +18,14 @@ in the source distribution for its full text.
#include <string.h>
#include <locale.h>
#include <langinfo.h>
#if HAVE_SETUID_ENABLED
#include <unistd.h>
#include <sys/types.h>
#endif
#define ColorPair(i,j) COLOR_PAIR((7-i)*8+j)
#define ColorIndex(i,j) ((7-i)*8+j)
#define ColorPair(i,j) COLOR_PAIR(ColorIndex(i,j))
#define Black COLOR_BLACK
#define Red COLOR_RED
@ -29,9 +36,13 @@ in the source distribution for its full text.
#define Cyan COLOR_CYAN
#define White COLOR_WHITE
#define ColorPairGrayBlack ColorPair(Magenta,Magenta)
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta)
#define KEY_WHEELUP KEY_F(20)
#define KEY_WHEELDOWN KEY_F(21)
#define KEY_RECLICK KEY_F(22)
#define KEY_SHIFT_TAB KEY_F(23)
//#link curses
@ -118,6 +129,11 @@ typedef enum ColorElements_ {
CPU_SOFTIRQ,
CPU_STEAL,
CPU_GUEST,
PANEL_EDIT,
SCREENS_OTH_BORDER,
SCREENS_OTH_TEXT,
SCREENS_CUR_BORDER,
SCREENS_CUR_TEXT,
LAST_COLORELEMENT
} ColorElements;
@ -125,7 +141,7 @@ void CRT_fatalError(const char* note) __attribute__ ((noreturn));
void CRT_handleSIGSEGV(int sgn);
#define KEY_ALT(x) KEY_F(60) + (x - 'A')
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
}*/
@ -183,7 +199,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LED_COLOR] = ColorPair(Green,Black),
[TASKS_RUNNING] = A_BOLD | ColorPair(Green,Black),
[PROCESS] = A_NORMAL,
[PROCESS_SHADOW] = A_BOLD | ColorPair(Black,Black),
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
[PROCESS_TAG] = A_BOLD | ColorPair(Yellow,Black),
[PROCESS_MEGABYTES] = ColorPair(Cyan,Black),
[PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan,Black),
@ -195,7 +211,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_THREAD] = ColorPair(Green,Black),
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green,Black),
[BAR_BORDER] = A_BOLD,
[BAR_SHADOW] = A_BOLD | ColorPair(Black,Black),
[BAR_SHADOW] = A_BOLD | ColorPairGrayBlack,
[SWAP] = ColorPair(Red,Black),
[GRAPH_1] = A_BOLD | ColorPair(Cyan,Black),
[GRAPH_2] = ColorPair(Cyan,Black),
@ -222,6 +238,11 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Magenta,Black),
[CPU_STEAL] = ColorPair(Cyan,Black),
[CPU_GUEST] = ColorPair(Cyan,Black),
[PANEL_EDIT] = ColorPair(White,Blue),
[SCREENS_OTH_BORDER] = ColorPair(Blue,Blue),
[SCREENS_OTH_TEXT] = ColorPair(Black,Blue),
[SCREENS_CUR_BORDER] = ColorPair(Green,Green),
[SCREENS_CUR_TEXT] = ColorPair(Black,Green),
},
[COLORSCHEME_MONOCHROME] = {
[RESET_COLOR] = A_NORMAL,
@ -281,6 +302,11 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = A_BOLD,
[CPU_STEAL] = A_REVERSE,
[CPU_GUEST] = A_REVERSE,
[PANEL_EDIT] = A_BOLD,
[SCREENS_OTH_BORDER] = A_DIM,
[SCREENS_OTH_TEXT] = A_DIM,
[SCREENS_CUR_BORDER] = A_REVERSE,
[SCREENS_CUR_TEXT] = A_REVERSE,
},
[COLORSCHEME_BLACKONWHITE] = {
[RESET_COLOR] = ColorPair(Black,White),
@ -340,6 +366,11 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue,White),
[CPU_STEAL] = ColorPair(Cyan,White),
[CPU_GUEST] = ColorPair(Cyan,White),
[PANEL_EDIT] = ColorPair(White,Blue),
[SCREENS_OTH_BORDER] = A_BOLD | ColorPair(Black,White),
[SCREENS_OTH_TEXT] = A_BOLD | ColorPair(Black,White),
[SCREENS_CUR_BORDER] = ColorPair(Green,Green),
[SCREENS_CUR_TEXT] = ColorPair(Black,Green),
},
[COLORSCHEME_LIGHTTERMINAL] = {
[RESET_COLOR] = ColorPair(Black,Black),
@ -360,7 +391,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LED_COLOR] = ColorPair(Green,Black),
[TASKS_RUNNING] = ColorPair(Green,Black),
[PROCESS] = ColorPair(Black,Black),
[PROCESS_SHADOW] = A_BOLD | ColorPair(Black,Black),
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
[PROCESS_TAG] = ColorPair(White,Blue),
[PROCESS_MEGABYTES] = ColorPair(Blue,Black),
[PROCESS_BASENAME] = ColorPair(Green,Black),
@ -372,7 +403,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_THREAD] = ColorPair(Blue,Black),
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue,Black),
[BAR_BORDER] = ColorPair(Blue,Black),
[BAR_SHADOW] = ColorPair(Black,Black),
[BAR_SHADOW] = ColorPairGrayBlack,
[SWAP] = ColorPair(Red,Black),
[GRAPH_1] = A_BOLD | ColorPair(Cyan,Black),
[GRAPH_2] = ColorPair(Cyan,Black),
@ -399,6 +430,11 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue,Black),
[CPU_STEAL] = ColorPair(Black,Black),
[CPU_GUEST] = ColorPair(Black,Black),
[PANEL_EDIT] = ColorPair(White,Blue),
[SCREENS_OTH_BORDER] = ColorPair(Blue,Black),
[SCREENS_OTH_TEXT] = ColorPair(Blue,Black),
[SCREENS_CUR_BORDER] = ColorPair(Green,Green),
[SCREENS_CUR_TEXT] = ColorPair(Black,Green),
},
[COLORSCHEME_MIDNIGHT] = {
[RESET_COLOR] = ColorPair(White,Blue),
@ -458,6 +494,11 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Black,Blue),
[CPU_STEAL] = ColorPair(White,Blue),
[CPU_GUEST] = ColorPair(White,Blue),
[PANEL_EDIT] = ColorPair(White,Blue),
[SCREENS_OTH_BORDER] = A_BOLD | ColorPair(Yellow,Blue),
[SCREENS_OTH_TEXT] = ColorPair(Cyan,Blue),
[SCREENS_CUR_BORDER] = ColorPair(Cyan,Cyan),
[SCREENS_CUR_TEXT] = ColorPair(Black,Cyan),
},
[COLORSCHEME_BLACKNIGHT] = {
[RESET_COLOR] = ColorPair(Cyan,Black),
@ -478,7 +519,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LED_COLOR] = ColorPair(Green,Black),
[TASKS_RUNNING] = A_BOLD | ColorPair(Green,Black),
[PROCESS] = ColorPair(Cyan,Black),
[PROCESS_SHADOW] = A_BOLD | ColorPair(Black,Black),
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
[PROCESS_TAG] = A_BOLD | ColorPair(Yellow,Black),
[PROCESS_MEGABYTES] = A_BOLD | ColorPair(Green,Black),
[PROCESS_BASENAME] = A_BOLD | ColorPair(Green,Black),
@ -517,12 +558,15 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue,Black),
[CPU_STEAL] = ColorPair(Cyan,Black),
[CPU_GUEST] = ColorPair(Cyan,Black),
[PANEL_EDIT] = ColorPair(White,Cyan),
[SCREENS_OTH_BORDER] = ColorPair(White,Black),
[SCREENS_OTH_TEXT] = ColorPair(Cyan,Black),
[SCREENS_CUR_BORDER] = A_BOLD | ColorPair(White,Black),
[SCREENS_CUR_TEXT] = A_BOLD | ColorPair(Green,Black),
},
[COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated.
};
int CRT_cursorX = 0;
int CRT_scrollHAmount = 5;
int CRT_scrollWheelVAmount = 10;
@ -541,6 +585,48 @@ static void CRT_handleSIGTERM(int sgn) {
exit(0);
}
#if HAVE_SETUID_ENABLED
static int CRT_euid = -1;
static int CRT_egid = -1;
#define DIE(msg) do { CRT_done(); fprintf(stderr, msg); exit(1); } while(0)
void CRT_dropPrivileges() {
CRT_egid = getegid();
CRT_euid = geteuid();
if (setegid(getgid()) == -1) {
DIE("Fatal error: failed dropping group privileges.\n");
}
if (seteuid(getuid()) == -1) {
DIE("Fatal error: failed dropping user privileges.\n");
}
}
void CRT_restorePrivileges() {
if (CRT_egid == -1 || CRT_euid == -1) {
DIE("Fatal error: internal inconsistency.\n");
}
if (setegid(CRT_egid) == -1) {
DIE("Fatal error: failed restoring group privileges.\n");
}
if (seteuid(CRT_euid) == -1) {
DIE("Fatal error: failed restoring user privileges.\n");
}
}
#else
/* Turn setuid operations into NOPs */
#ifndef CRT_dropPrivileges
#define CRT_dropPrivileges()
#define CRT_restorePrivileges()
#endif
#endif
// TODO: pass an instance of Settings instead.
void CRT_init(int delay, int colorScheme) {
@ -555,7 +641,7 @@ void CRT_init(int delay, int colorScheme) {
for (int i = 0; i < LAST_COLORELEMENT; i++) {
unsigned int color = CRT_colorSchemes[COLORSCHEME_DEFAULT][i];
CRT_colorSchemes[COLORSCHEME_BROKENGRAY][i] = color == (A_BOLD | ColorPair(Black,Black)) ? ColorPair(White,Black) : color;
CRT_colorSchemes[COLORSCHEME_BROKENGRAY][i] = color == (A_BOLD | ColorPairGrayBlack) ? ColorPair(White,Black) : color;
}
halfdelay(CRT_delay);
@ -575,7 +661,7 @@ void CRT_init(int delay, int colorScheme) {
CRT_scrollHAmount = 20;
else
CRT_scrollHAmount = 5;
if (String_eq(CRT_termType, "xterm") || String_eq(CRT_termType, "xterm-color") || String_eq(CRT_termType, "vt220")) {
if (String_startsWith(CRT_termType, "xterm") || String_eq(CRT_termType, "vt220")) {
define_key("\033[H", KEY_HOME);
define_key("\033[F", KEY_END);
define_key("\033[7~", KEY_HOME);
@ -589,16 +675,21 @@ void CRT_init(int delay, int colorScheme) {
define_key("\033[13~", KEY_F(3));
define_key("\033[14~", KEY_F(4));
define_key("\033[17;2~", KEY_F(18));
define_key("\033[Z", KEY_SHIFT_TAB);
char sequence[3] = "\033a";
for (char c = 'a'; c <= 'z'; c++) {
sequence[1] = c;
define_key(sequence, KEY_ALT('A' + (c - 'a')));
}
}
if (String_startsWith(CRT_termType, "rxvt")) {
define_key("\033[Z", KEY_SHIFT_TAB);
}
#ifndef DEBUG
signal(11, CRT_handleSIGSEGV);
#endif
signal(SIGTERM, CRT_handleSIGTERM);
signal(SIGQUIT, CRT_handleSIGTERM);
use_default_colors();
if (!has_colors())
CRT_colorScheme = 1;
@ -661,14 +752,23 @@ void CRT_enableDelay() {
void CRT_setColors(int colorScheme) {
CRT_colorScheme = colorScheme;
if (colorScheme == COLORSCHEME_BLACKNIGHT) {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
init_pair((7-i)*8+j, i, j);
} else {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
init_pair((7-i)*8+j, i, (j==0?-1:j));
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (ColorIndex(i,j) != ColorPairGrayBlack) {
int bg = (colorScheme != COLORSCHEME_BLACKNIGHT)
? (j==0 ? -1 : j)
: j;
init_pair(ColorIndex(i,j), i, bg);
}
}
}
int grayBlackFg = COLORS > 8 ? 8 : 0;
int grayBlackBg = (colorScheme != COLORSCHEME_BLACKNIGHT)
? -1
: 0;
init_pair(ColorIndexGrayBlack, grayBlackFg, grayBlackBg);
CRT_colors = CRT_colorSchemes[colorScheme];
}

39
CRT.h
View File

@ -9,7 +9,12 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#define ColorPair(i,j) COLOR_PAIR((7-i)*8+j)
#if HAVE_SETUID_ENABLED
#endif
#define ColorIndex(i,j) ((7-i)*8+j)
#define ColorPair(i,j) COLOR_PAIR(ColorIndex(i,j))
#define Black COLOR_BLACK
#define Red COLOR_RED
@ -20,9 +25,13 @@ in the source distribution for its full text.
#define Cyan COLOR_CYAN
#define White COLOR_WHITE
#define ColorPairGrayBlack ColorPair(Magenta,Magenta)
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta)
#define KEY_WHEELUP KEY_F(20)
#define KEY_WHEELDOWN KEY_F(21)
#define KEY_RECLICK KEY_F(22)
#define KEY_SHIFT_TAB KEY_F(23)
//#link curses
@ -108,6 +117,11 @@ typedef enum ColorElements_ {
CPU_SOFTIRQ,
CPU_STEAL,
CPU_GUEST,
PANEL_EDIT,
SCREENS_OTH_BORDER,
SCREENS_OTH_TEXT,
SCREENS_CUR_BORDER,
SCREENS_CUR_TEXT,
LAST_COLORELEMENT
} ColorElements;
@ -115,7 +129,7 @@ void CRT_fatalError(const char* note) __attribute__ ((noreturn));
void CRT_handleSIGSEGV(int sgn);
#define KEY_ALT(x) KEY_F(60) + (x - 'A')
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
extern const char *CRT_treeStrAscii[TREE_STR_COUNT];
@ -136,8 +150,6 @@ int* CRT_colors;
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
extern int CRT_cursorX;
extern int CRT_scrollHAmount;
extern int CRT_scrollWheelVAmount;
@ -150,6 +162,25 @@ extern int CRT_colorScheme;
void *backtraceArray[128];
#if HAVE_SETUID_ENABLED
#define DIE(msg) do { CRT_done(); fprintf(stderr, msg); exit(1); } while(0)
void CRT_dropPrivileges();
void CRT_restorePrivileges();
#else
/* Turn setuid operations into NOPs */
#ifndef CRT_dropPrivileges
#define CRT_dropPrivileges()
#define CRT_restorePrivileges()
#endif
#endif
// TODO: pass an instance of Settings instead.
void CRT_init(int delay, int colorScheme);

View File

@ -10,7 +10,7 @@ in the source distribution for its full text.
#include "AvailableMetersPanel.h"
#include "MetersPanel.h"
#include "DisplayOptionsPanel.h"
#include "ColumnsPanel.h"
#include "ScreensPanel.h"
#include "ColorsPanel.h"
#include "AvailableColumnsPanel.h"
@ -34,7 +34,7 @@ typedef struct CategoriesPanel_ {
}*/
static const char* CategoriesFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static const char* const CategoriesFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static void CategoriesPanel_delete(Object* object) {
Panel* super = (Panel*) object;
@ -64,9 +64,11 @@ static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) {
ScreenManager_add(this->scr, colors, -1);
}
static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) {
Panel* columns = (Panel*) ColumnsPanel_new(this->settings);
static void CategoriesPanel_makeScreensPage(CategoriesPanel* this) {
Panel* screens = (Panel*) ScreensPanel_new(this->settings);
Panel* columns = (Panel*) ((ScreensPanel*)screens)->columns;
Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns);
ScreenManager_add(this->scr, screens, 20);
ScreenManager_add(this->scr, columns, 20);
ScreenManager_add(this->scr, availableColumns, -1);
}
@ -82,9 +84,9 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
result = HANDLED;
break;
case KEY_UP:
case KEY_CTRLP:
case KEY_CTRL('P'):
case KEY_DOWN:
case KEY_CTRLN:
case KEY_CTRL('N'):
case KEY_NPAGE:
case KEY_PPAGE:
case KEY_HOME:
@ -118,7 +120,7 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
CategoriesPanel_makeColorsPage(this);
break;
case 3:
CategoriesPanel_makeColumnsPage(this);
CategoriesPanel_makeScreensPage(this);
break;
}
}
@ -143,10 +145,10 @@ CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Hea
this->settings = settings;
this->header = header;
this->pl = pl;
Panel_setHeader(super, "Setup");
Panel_setHeader(super, "Categories");
Panel_add(super, (Object*) ListItem_new("Meters", 0));
Panel_add(super, (Object*) ListItem_new("Display options", 0));
Panel_add(super, (Object*) ListItem_new("Colors", 0));
Panel_add(super, (Object*) ListItem_new("Columns", 0));
Panel_add(super, (Object*) ListItem_new("Screens", 0));
return this;
}

View File

@ -1,3 +1,83 @@
What's new in version 2.2.0
* Solaris/Illumos/OpenIndiana support
(thanks to Guy M. Broome)
* -t/--tree flag for starting in tree-view mode
(thanks to Daniel Flanagan)
* macOS: detects High Sierra version to avoid OS bug
(thanks to Pierre Malhaire)
* OpenBSD: read battery data
(thanks to @nerd972)
* Various automake and build improvements
(thanks to Kang-Che Sung)
* Check for pkg-config when building with --enable-delayacct
(thanks to @florian2833z for the report)
* Avoid some bashisms in configure script
(thanks to Jesin)
* Use CFLAGS from ncurses*-config if present
(thanks to Michael Klein)
* Header generator supports non-UTF-8 environments
(thanks to @volkov-am)
* Linux: changed detection of kernel threads
* Collapse current subtree pressing Backspace
* BUGFIX: fix behavior of SYSCR column
(thanks to Marc Kleine-Budde)
* BUGFIX: obtain exit code of lsof correctly
(thanks to @wangqr)
* BUGFIX: fix crash with particular keycodes
(thanks to Wellington Torrejais da Silva for the report)
* BUGFIX: fix issue with small terminals
(thanks to Daniel Elf for the report)
* BUGFIX: fix terminal color issues
(thanks to Kang-Che Sung for the report)
* BUGFIX: preserve LDFLAGS when building
(thanks to Lance Frederickson for the report)
* BUGFIX: fixed overflow for systems with >= 100 signals
What's new in version 2.1.0
* Linux: Delay accounting metrics
(thanks to André Carvalho)
* DragonFlyBSD support
(thanks to Diederik de Groot)
* Support for real-time signals
(thanks to Kang-Che Sung)
* 'c' key now works with threads as well
* Session column renamed from SESN to SID
(thanks to Kamyar Rasta)
* Improved UI for meter style selection
(thanks to Kang-Che Sung)
* Improved code for constructing process tree
(thanks to wangqr)
* Compile-time option to disable setuid
* Error checking of various standard library operations
* Replacement of sprintf with snprintf
(thanks to Tomasz Kramkowski)
* Linux: performance improvements in battery meter
* Linux: update process TTY device
* Linux: add support for sorting TASK_IDLE
(thanks to Vladimir Panteleev)
* Linux: add upper-bound to running process counter
(thanks to Lucas Correia Villa Real)
* BUGFIX: avoid crash when battery is removed
(thanks to Jan Chren)
* BUGFIX: macOS: fix infinite loop in tree view
(thanks to Wataru Ashihara)
What's new in version 2.0.2
* Mac OS X: stop trying when task_for_pid fails for a process,
stops spamming logs with errors.
* Add Ctrl+A and Ctrl+E to go to beginning and end of line
* FreeBSD: fixes for CPU calculation
(thanks to Tim Creech, Andy Pilate)
* Usability: auto-follow process after a search.
* Use Linux backend on GNU Hurd
* Improvement for reproducible builds.
* BUGFIX: Fix behavior of Alt-key combinations
(thanks to Kang-Che Sung)
* Various code tweaks and cleanups
(thanks to Kang-Che Sung)
What's new in version 2.0.1

View File

@ -19,7 +19,7 @@ int ClockMeter_attributes[] = {
CLOCK
};
static void ClockMeter_setValues(Meter* this, char* buffer, int size) {
static void ClockMeter_updateValues(Meter* this, char* buffer, int size) {
time_t t = time(NULL);
struct tm result;
struct tm *lt = localtime_r(&t, &result);
@ -32,8 +32,9 @@ MeterClass ClockMeter_class = {
.extends = Class(Meter),
.delete = Meter_delete
},
.setValues = ClockMeter_setValues,
.updateValues = ClockMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 1440, /* 24*60 */
.attributes = ClockMeter_attributes,
.name = "Clock",

View File

@ -34,9 +34,9 @@ typedef struct ColorsPanel_ {
}*/
static const char* ColorsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static const char* const ColorsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static const char* ColorSchemeNames[] = {
static const char* const ColorSchemeNames[] = {
"Default",
"Monochromatic",
"Black on White",
@ -78,6 +78,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
this->settings->changed = true;
const Header* header = this->scr->header;
CRT_setColors(mark);
clear();
Panel* menu = (Panel*) Vector_get(this->scr->panels, 0);
Header_draw(header);
RichString_setAttr(&(super->header), CRT_colors[PANEL_HEADER_FOCUS]);

View File

@ -22,14 +22,15 @@ in the source distribution for its full text.
typedef struct ColumnsPanel_ {
Panel super;
ScreenSettings* ss;
bool* changed;
Settings* settings;
bool moving;
} ColumnsPanel;
}*/
static const char* ColumnsFunctions[] = {" ", " ", " ", " ", " ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL};
static const char* const ColumnsFunctions[] = {" ", " ", " ", " ", " ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL};
static void ColumnsPanel_delete(Object* object) {
Panel* super = (Panel*) object;
@ -65,8 +66,8 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
if (!this->moving) {
break;
}
/* else fallthrough */
}
/* else fallthrough */
case KEY_F(7):
case '[':
case '-':
@ -81,8 +82,8 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
if (!this->moving) {
break;
}
/* else fallthrough */
}
/* else fallthrough */
case KEY_F(8):
case ']':
case '+':
@ -123,22 +124,31 @@ PanelClass ColumnsPanel_class = {
.eventHandler = ColumnsPanel_eventHandler
};
ColumnsPanel* ColumnsPanel_new(Settings* settings) {
ColumnsPanel* this = AllocThis(ColumnsPanel);
void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss) {
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings;
this->moving = false;
Panel_setHeader(super, "Active Columns");
ProcessField* fields = this->settings->fields;
Panel_prune(super);
ProcessField* fields = ss->fields;
for (; *fields; fields++) {
if (Process_fields[*fields].name) {
Panel_add(super, (Object*) ListItem_new(Process_fields[*fields].name, *fields));
}
}
this->ss = ss;
}
ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, bool* changed) {
ColumnsPanel* this = AllocThis(ColumnsPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->ss = ss;
this->changed = changed;
this->moving = false;
Panel_setHeader(super, "Active Columns");
ColumnsPanel_fill(this, ss);
return this;
}
@ -154,14 +164,14 @@ int ColumnsPanel_fieldNameToIndex(const char* name) {
void ColumnsPanel_update(Panel* super) {
ColumnsPanel* this = (ColumnsPanel*) super;
int size = Panel_size(super);
this->settings->changed = true;
this->settings->fields = xRealloc(this->settings->fields, sizeof(ProcessField) * (size+1));
this->settings->flags = 0;
*(this->changed) = true;
this->ss->fields = xRealloc(this->ss->fields, sizeof(ProcessField) * (size+1));
this->ss->flags = 0;
for (int i = 0; i < size; i++) {
int key = ((ListItem*) Panel_get(super, i))->key;
this->settings->fields[i] = key;
this->settings->flags |= Process_fields[key].flags;
this->ss->fields[i] = key;
this->ss->flags |= key < 1000 ? Process_fields[key].flags : 0;
}
this->settings->fields[size] = 0;
this->ss->fields[size] = 0;
}

View File

@ -14,15 +14,18 @@ in the source distribution for its full text.
typedef struct ColumnsPanel_ {
Panel super;
ScreenSettings* ss;
bool* changed;
Settings* settings;
bool moving;
} ColumnsPanel;
extern PanelClass ColumnsPanel_class;
ColumnsPanel* ColumnsPanel_new(Settings* settings);
void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss);
ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, bool* changed);
int ColumnsPanel_fieldNameToIndex(const char* name);

View File

@ -28,7 +28,7 @@ typedef struct DisplayOptionsPanel_ {
}*/
static const char* DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static void DisplayOptionsPanel_delete(Object* object) {
Panel* super = (Panel*) object;
@ -83,7 +83,6 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
this->scr = scr;
Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Tree view"), &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Shadow other users' processes"), &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide kernel threads"), &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide userland process threads"), &(settings->hideUserlandThreads)));
@ -97,5 +96,6 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Count CPUs from 0 instead of 1"), &(settings->countCPUsFromZero)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Update process names on every refresh"), &(settings->updateProcessNames)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Add guest time in CPU meter percentage"), &(settings->accountGuestInCPUMeter)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show tabs for screens"), &(settings->screenTabs)));
return this;
}

View File

@ -48,10 +48,9 @@ void EnvScreen_scan(InfoScreen* this) {
Panel_prune(panel);
uid_t euid = geteuid();
seteuid(getuid());
char *env = Platform_getProcessEnv(this->process->pid);
seteuid(euid);
CRT_dropPrivileges();
char* env = Platform_getProcessEnv(this->process->pid);
CRT_restorePrivileges();
if (env) {
for (char *p = env; *p; p = strrchr(p, 0)+1)
InfoScreen_addLine(this, p);

View File

@ -28,21 +28,21 @@ typedef struct FunctionBar_ {
}*/
static const char* FunctionBar_FKeys[] = {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", NULL};
static const char* const FunctionBar_FKeys[] = {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", NULL};
static const char* FunctionBar_FLabels[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", NULL};
static const char* const FunctionBar_FLabels[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", NULL};
static int FunctionBar_FEvents[] = {KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10)};
static const char* FunctionBar_EnterEscKeys[] = {"Enter", "Esc", NULL};
static int FunctionBar_EnterEscEvents[] = {13, 27};
static const char* const FunctionBar_EnterEscKeys[] = {"Enter", "Esc", NULL};
static const int FunctionBar_EnterEscEvents[] = {13, 27};
FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc) {
const char* functions[] = {enter, esc, NULL};
return FunctionBar_new(functions, FunctionBar_EnterEscKeys, FunctionBar_EnterEscEvents);
}
FunctionBar* FunctionBar_new(const char** functions, const char** keys, int* events) {
FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events) {
FunctionBar* this = xCalloc(1, sizeof(FunctionBar));
this->functions = xCalloc(16, sizeof(char*));
if (!functions) {
@ -96,11 +96,12 @@ void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) {
}
}
void FunctionBar_draw(const FunctionBar* this, char* buffer) {
FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]);
int FunctionBar_draw(const FunctionBar* this, char* buffer) {
return FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]);
}
void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
int FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
int cursorX = 0;
attrset(CRT_colors[FUNCTION_BAR]);
mvhline(LINES-1, 0, ' ', COLS);
int x = 0;
@ -115,12 +116,10 @@ void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
if (buffer) {
attrset(attr);
mvaddstr(LINES-1, x, buffer);
CRT_cursorX = x + strlen(buffer);
curs_set(1);
} else {
curs_set(0);
cursorX = x + strlen(buffer);
}
attrset(CRT_colors[RESET_COLOR]);
return cursorX;
}
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) {

View File

@ -24,15 +24,15 @@ typedef struct FunctionBar_ {
FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc);
FunctionBar* FunctionBar_new(const char** functions, const char** keys, int* events);
FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events);
void FunctionBar_delete(FunctionBar* this);
void FunctionBar_setLabel(FunctionBar* this, int event, const char* text);
void FunctionBar_draw(const FunctionBar* this, char* buffer);
int FunctionBar_draw(const FunctionBar* this, char* buffer);
void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr);
int FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr);
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos);

View File

@ -37,7 +37,7 @@ typedef struct Header_ {
#endif
#ifndef Header_forEachColumn
#define Header_forEachColumn(this_, i_) for (int i_=0; i_ < this->nrColumns; i_++)
#define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_))
#endif
Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns) {
@ -62,7 +62,7 @@ void Header_delete(Header* this) {
void Header_populateFromSettings(Header* this) {
Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->columns[col];
MeterColumnSettings* colSettings = &this->settings->meterColumns[col];
for (int i = 0; i < colSettings->len; i++) {
Header_addMeterByName(this, colSettings->names[i], col);
if (colSettings->modes[i] != 0) {
@ -75,7 +75,7 @@ void Header_populateFromSettings(Header* this) {
void Header_writeBackToSettings(const Header* this) {
Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->columns[col];
MeterColumnSettings* colSettings = &this->settings->meterColumns[col];
String_freeArray(colSettings->names);
free(colSettings->modes);
@ -91,9 +91,9 @@ void Header_writeBackToSettings(const Header* this) {
Meter* meter = (Meter*) Vector_get(vec, i);
char* name = xCalloc(64, sizeof(char));
if (meter->param) {
snprintf(name, 63, "%s(%d)", As_Meter(meter)->name, meter->param);
xSnprintf(name, 63, "%s(%d)", As_Meter(meter)->name, meter->param);
} else {
snprintf(name, 63, "%s", As_Meter(meter)->name);
xSnprintf(name, 63, "%s", As_Meter(meter)->name);
}
colSettings->names[i] = name;
colSettings->modes[i] = meter->mode;
@ -155,7 +155,7 @@ char* Header_readMeterName(Header* this, int i, int column) {
strncpy(name, Meter_name(meter), nameLen);
name[nameLen] = '\0';
if (meter->param)
snprintf(name + nameLen, len - nameLen, "(%d)", meter->param);
xSnprintf(name + nameLen, len - nameLen, "(%d)", meter->param);
return name;
}
@ -211,6 +211,9 @@ int Header_calculateHeight(Header* this) {
}
maxHeight = MAX(maxHeight, height);
}
if (this->settings->screenTabs) {
maxHeight++;
}
this->height = maxHeight;
this->pad = pad;
return maxHeight;

View File

@ -28,7 +28,7 @@ typedef struct Header_ {
#endif
#ifndef Header_forEachColumn
#define Header_forEachColumn(this_, i_) for (int i_=0; i_ < this->nrColumns; i_++)
#define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_))
#endif
Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns);

View File

@ -19,7 +19,7 @@ int HostnameMeter_attributes[] = {
HOSTNAME
};
static void HostnameMeter_setValues(Meter* this, char* buffer, int size) {
static void HostnameMeter_updateValues(Meter* this, char* buffer, int size) {
(void) this;
gethostname(buffer, size-1);
}
@ -29,8 +29,9 @@ MeterClass HostnameMeter_class = {
.extends = Class(Meter),
.delete = Meter_delete
},
.setValues = HostnameMeter_setValues,
.updateValues = HostnameMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 0,
.total = 100.0,
.attributes = HostnameMeter_attributes,
.name = "Hostname",

370
INSTALL
View File

@ -1,370 +0,0 @@
Installation Instructions
*************************
Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation,
Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. This file is offered as-is,
without warranty of any kind.
Basic Installation
==================
Briefly, the shell commands `./configure; make; make install' should
configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package. Some packages provide this
`INSTALL' file but do not implement all of the features documented
below. The lack of an optional feature in a given package is not
necessarily a bug. More recommendations for GNU packages can be found
in *note Makefile Conventions: (standards)Makefile Conventions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale
cache files.
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You need `configure.ac' if
you want to change it or regenerate `configure' using a newer version
of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system.
Running `configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package, generally using the just-built uninstalled binaries.
4. Type `make install' to install the programs and any data files and
documentation. When installing into a prefix owned by root, it is
recommended that the package be configured and built as a regular
user, and only the `make install' phase executed with root
privileges.
5. Optionally, type `make installcheck' to repeat any self-tests, but
this time using the binaries in their final installed location.
This target does not install anything. Running this target as a
regular user, particularly if the prior `make install' required
root privileges, verifies that the installation completed
correctly.
6. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
7. Often, you can also type `make uninstall' to remove the installed
files again. In practice, not all packages have tested that
uninstallation works correctly, even though it is required by the
GNU Coding Standards.
8. Some packages, particularly those that use Automake, provide `make
distcheck', which can by used by developers to test that all other
targets like `make install' and `make uninstall' work correctly.
This target is generally not run by end users.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the `configure' script does not know about. Run `./configure --help'
for details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'. This
is known as a "VPATH" build.
With a non-GNU `make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use `make distclean' before
reconfiguring for another architecture.
On MacOS X 10.5 and later systems, you can create libraries and
executables that work on multiple system types--known as "fat" or
"universal" binaries--by specifying multiple `-arch' options to the
compiler but only a single `-arch' option to the preprocessor. Like
this:
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CPP="gcc -E" CXXCPP="g++ -E"
This is not guaranteed to produce working output in all cases, you
may have to build one architecture at a time and combine the results
using the `lipo' tool if you have problems.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX', where PREFIX must be an
absolute file name.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them. In general, the
default for these options is expressed in terms of `${prefix}', so that
specifying just `--prefix' will affect all of the other directory
specifications that were not explicitly provided.
The most portable way to affect installation locations is to pass the
correct locations to `configure'; however, many packages provide one or
both of the following shortcuts of passing variable assignments to the
`make install' command line to change installation locations without
having to reconfigure or recompile.
The first method involves providing an override variable for each
affected directory. For example, `make install
prefix=/alternate/directory' will choose an alternate location for all
directory configuration variables that were expressed in terms of
`${prefix}'. Any directories that were specified during `configure',
but not in terms of `${prefix}', must each be overridden at install
time for the entire installation to be relocated. The approach of
makefile variable overrides for each directory variable is required by
the GNU Coding Standards, and ideally causes no recompilation.
However, some platforms have known limitations with the semantics of
shared libraries that end up requiring recompilation when using this
method, particularly noticeable in packages that use GNU Libtool.
The second method involves providing the `DESTDIR' variable. For
example, `make install DESTDIR=/alternate/directory' will prepend
`/alternate/directory' before all installation names. The approach of
`DESTDIR' overrides is not required by the GNU Coding Standards, and
does not work on platforms that have drive letters. On the other hand,
it does better at avoiding recompilation issues, and works well even
when some directory options were not specified in terms of `${prefix}'
at `configure' time.
Optional Features
=================
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Some packages offer the ability to configure how verbose the
execution of `make' will be. For these packages, running `./configure
--enable-silent-rules' sets the default to minimal output, which can be
overridden with `make V=1'; while running `./configure
--disable-silent-rules' sets the default to verbose, which can be
overridden with `make V=0'.
Particular systems
==================
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
CC is not installed, it is recommended to use the following options in
order to use an ANSI C compiler:
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
HP-UX `make' updates targets which have the same time stamps as
their prerequisites, which makes it generally unusable when shipped
generated files such as `configure' are involved. Use GNU `make'
instead.
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
a workaround. If GNU CC is not installed, it is therefore recommended
to try
./configure CC="cc"
and if that doesn't work, try
./configure CC="cc -nodtk"
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
directory contains several dysfunctional programs; working variants of
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
in your `PATH', put it _after_ `/usr/bin'.
On Haiku, software installed for all users goes in `/boot/common',
not `/usr/local'. It is recommended to use the following options:
./configure --prefix=/boot/common
Specifying the System Type
==========================
There may be some features `configure' cannot figure out
automatically, but needs to determine by the type of machine the package
will run on. Usually, assuming the package is built to be run on the
_same_ architectures, `configure' can figure that out, but if it prints
a message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS
KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf limitation. Until the limitation is lifted, you can use
this workaround:
CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
`configure' recognizes the following options to control how it
operates.
`--help'
`-h'
Print a summary of all of the options to `configure', and exit.
`--help=short'
`--help=recursive'
Print a summary of the options unique to this package's
`configure', and exit. The `short' variant lists options used
only in the top level, while the `recursive' variant lists options
also present in any nested packages.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`--prefix=DIR'
Use DIR as the installation prefix. *note Installation Names::
for more details, including other options available for fine-tuning
the installation locations.
`--no-create'
`-n'
Run the configure checks, but stop before creating any output
files.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

View File

@ -38,8 +38,10 @@ typedef struct IncMode_ {
typedef struct IncSet_ {
IncMode modes[2];
IncMode* active;
Panel* panel;
FunctionBar* defaultBar;
bool filtering;
bool found;
} IncSet;
typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
@ -51,8 +53,8 @@ static void IncMode_reset(IncMode* mode) {
mode->buffer[0] = 0;
}
static const char* searchFunctions[] = {"Next ", "Cancel ", " Search: ", NULL};
static const char* searchKeys[] = {"F3", "Esc", " "};
static const char* const searchFunctions[] = {"Next ", "Cancel ", " Search: ", NULL};
static const char* const searchKeys[] = {"F3", "Esc", " "};
static int searchEvents[] = {KEY_F(3), 27, ERR};
static inline void IncMode_initSearch(IncMode* search) {
@ -61,8 +63,8 @@ static inline void IncMode_initSearch(IncMode* search) {
search->isFilter = false;
}
static const char* filterFunctions[] = {"Done ", "Clear ", " Filter: ", NULL};
static const char* filterKeys[] = {"Enter", "Esc", " "};
static const char* const filterFunctions[] = {"Done ", "Clear ", " Filter: ", NULL};
static const char* const filterKeys[] = {"Enter", "Esc", " "};
static int filterEvents[] = {13, 27, ERR};
static inline void IncMode_initFilter(IncMode* filter) {
@ -114,20 +116,33 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
}
}
static void search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
static bool search(IncSet* this, Panel* panel, IncMode_GetPanelValue getPanelValue) {
int size = Panel_size(panel);
bool found = false;
for (int i = 0; i < size; i++) {
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
if (String_contains_i(getPanelValue(panel, i), this->active->buffer)) {
Panel_setSelected(panel, i);
found = true;
break;
}
}
if (found)
FunctionBar_draw(mode->bar, mode->buffer);
else
FunctionBar_drawAttr(mode->bar, mode->buffer, CRT_colors[FAILED_SEARCH]);
IncSet_drawBar(this, found ? CRT_colors[FUNCTION_BAR] : CRT_colors[FAILED_SEARCH]);
return found;
}
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
panel->currentBar = this->active->bar;
panel->cursorOn = true;
this->panel = panel;
IncSet_drawBar(this, CRT_colors[FUNCTION_BAR]);
}
static void IncSet_deactivate(IncSet* this, Panel* panel) {
this->active = NULL;
Panel_setDefaultBar(panel);
panel->cursorOn = false;
FunctionBar_draw(this->defaultBar, NULL);
}
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) {
@ -151,24 +166,30 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
}
}
doSearch = false;
} else if (ch < 255 && isprint((char)ch) && (mode->index < INCMODE_MAX)) {
mode->buffer[mode->index] = ch;
mode->index++;
mode->buffer[mode->index] = 0;
if (mode->isFilter) {
filterChanged = true;
if (mode->index == 1) this->filtering = true;
}
} else if ((ch == KEY_BACKSPACE || ch == 127) && (mode->index > 0)) {
mode->index--;
mode->buffer[mode->index] = 0;
if (mode->isFilter) {
filterChanged = true;
if (mode->index == 0) {
this->filtering = false;
IncMode_reset(mode);
} else if (ch < 255 && isprint((char)ch)) {
if (mode->index < INCMODE_MAX) {
mode->buffer[mode->index] = ch;
mode->index++;
mode->buffer[mode->index] = 0;
if (mode->isFilter) {
filterChanged = true;
if (mode->index == 1) this->filtering = true;
}
}
} else if ((ch == KEY_BACKSPACE || ch == 127)) {
if (mode->index > 0) {
mode->index--;
mode->buffer[mode->index] = 0;
if (mode->isFilter) {
filterChanged = true;
if (mode->index == 0) {
this->filtering = false;
IncMode_reset(mode);
}
}
} else {
doSearch = false;
}
} else if (ch == KEY_RESIZE) {
Panel_resize(panel, COLS, LINES-panel->y-1);
} else {
@ -181,13 +202,11 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
} else {
IncMode_reset(mode);
}
this->active = NULL;
Panel_setDefaultBar(panel);
FunctionBar_draw(this->defaultBar, NULL);
IncSet_deactivate(this, panel);
doSearch = false;
}
if (doSearch) {
search(mode, panel, getPanelValue);
this->found = search(this, panel, getPanelValue);
}
if (filterChanged && lines) {
updateWeakPanel(this, panel, lines);
@ -202,15 +221,11 @@ const char* IncSet_getListItemValue(Panel* panel, int i) {
return "";
}
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
FunctionBar_draw(this->active->bar, this->active->buffer);
panel->currentBar = this->active->bar;
}
void IncSet_drawBar(IncSet* this) {
void IncSet_drawBar(IncSet* this, int attr) {
if (this->active) {
FunctionBar_draw(this->active->bar, this->active->buffer);
int cursorX = FunctionBar_drawAttr(this->active->bar, this->active->buffer, attr);
this->panel->cursorY = LINES - 1;
this->panel->cursorX = cursorX;
} else {
FunctionBar_draw(this->defaultBar, NULL);
}

View File

@ -33,8 +33,10 @@ typedef struct IncMode_ {
typedef struct IncSet_ {
IncMode modes[2];
IncMode* active;
Panel* panel;
FunctionBar* defaultBar;
bool filtering;
bool found;
} IncSet;
typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
@ -44,13 +46,13 @@ IncSet* IncSet_new(FunctionBar* bar);
void IncSet_delete(IncSet* this);
void IncSet_activate(IncSet* this, IncType type, Panel* panel);
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines);
const char* IncSet_getListItemValue(Panel* panel, int i);
void IncSet_activate(IncSet* this, IncType type, Panel* panel);
void IncSet_drawBar(IncSet* this);
void IncSet_drawBar(IncSet* this, int attr);
int IncSet_synthesizeEvent(IncSet* this, int x);

View File

@ -50,9 +50,9 @@ struct InfoScreen_ {
};
}*/
static const char* InfoScreenFunctions[] = {"Search ", "Filter ", "Refresh", "Done ", NULL};
static const char* const InfoScreenFunctions[] = {"Search ", "Filter ", "Refresh", "Done ", NULL};
static const char* InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"};
static const char* const InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"};
static int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27};
@ -83,8 +83,9 @@ void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...) {
wmove(stdscr, 0, 0);
vw_printw(stdscr, fmt, ap);
attrset(CRT_colors[DEFAULT_COLOR]);
this->display->needsRedraw = true;
Panel_draw(this->display, true);
IncSet_drawBar(this->inc);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
va_end(ap);
}
@ -114,10 +115,7 @@ void InfoScreen_run(InfoScreen* this) {
Panel_draw(panel, true);
if (this->inc->active)
move(LINES-1, CRT_cursorX);
ESCDELAY = 25;
int ch = getch();
int ch = Panel_getCh(panel);
if (ch == ERR) {
if (As_InfoScreen(this)->onErr) {

View File

@ -27,19 +27,19 @@ typedef struct ListItem_ {
}*/
static void ListItem_delete(Object* cast) {
void ListItem_delete(Object* cast) {
ListItem* this = (ListItem*)cast;
free(this->value);
free(this);
}
static void ListItem_display(Object* cast, RichString* out) {
void ListItem_display(Object* cast, RichString* out) {
ListItem* const this = (ListItem*)cast;
assert (this != NULL);
/*
int len = strlen(this->value)+1;
char buffer[len+1];
snprintf(buffer, len, "%s", this->value);
xSnprintf(buffer, len, "%s", this->value);
*/
if (this->moving) {
RichString_write(out, CRT_colors[DEFAULT_COLOR],
@ -59,11 +59,15 @@ ObjectClass ListItem_class = {
.compare = ListItem_compare
};
ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem);
void ListItem_init(ListItem* this, const char* value, int key) {
this->value = xStrdup(value);
this->key = key;
this->moving = false;
}
ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem);
ListItem_init(this, value, key);
return this;
}

View File

@ -19,8 +19,14 @@ typedef struct ListItem_ {
} ListItem;
void ListItem_delete(Object* cast);
void ListItem_display(Object* cast, RichString* out);
extern ObjectClass ListItem_class;
void ListItem_init(ListItem* this, const char* value, int key);
ListItem* ListItem_new(const char* value, int key);
void ListItem_append(ListItem* this, const char* text);

View File

@ -20,35 +20,35 @@ int LoadAverageMeter_attributes[] = {
int LoadMeter_attributes[] = { LOAD };
static void LoadAverageMeter_setValues(Meter* this, char* buffer, int size) {
static void LoadAverageMeter_updateValues(Meter* this, char* buffer, int size) {
Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]);
snprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]);
xSnprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]);
}
static void LoadAverageMeter_display(Object* cast, RichString* out) {
Meter* this = (Meter*)cast;
char buffer[20];
sprintf(buffer, "%.2f ", this->values[0]);
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
RichString_write(out, CRT_colors[LOAD_AVERAGE_ONE], buffer);
sprintf(buffer, "%.2f ", this->values[1]);
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[1]);
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer);
sprintf(buffer, "%.2f ", this->values[2]);
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[2]);
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer);
}
static void LoadMeter_setValues(Meter* this, char* buffer, int size) {
static void LoadMeter_updateValues(Meter* this, char* buffer, int size) {
double five, fifteen;
Platform_getLoadAverage(&this->values[0], &five, &fifteen);
if (this->values[0] > this->total) {
this->total = this->values[0];
}
snprintf(buffer, size, "%.2f", this->values[0]);
xSnprintf(buffer, size, "%.2f", this->values[0]);
}
static void LoadMeter_display(Object* cast, RichString* out) {
Meter* this = (Meter*)cast;
char buffer[20];
sprintf(buffer, "%.2f ", ((Meter*)this)->values[0]);
xSnprintf(buffer, sizeof(buffer), "%.2f ", ((Meter*)this)->values[0]);
RichString_write(out, CRT_colors[LOAD], buffer);
}
@ -58,7 +58,7 @@ MeterClass LoadAverageMeter_class = {
.delete = Meter_delete,
.display = LoadAverageMeter_display,
},
.setValues = LoadAverageMeter_setValues,
.updateValues = LoadAverageMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 3,
.total = 100.0,
@ -75,8 +75,9 @@ MeterClass LoadMeter_class = {
.delete = Meter_delete,
.display = LoadMeter_display,
},
.setValues = LoadMeter_setValues,
.updateValues = LoadMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 100.0,
.attributes = LoadMeter_attributes,
.name = "Load",

View File

@ -25,13 +25,18 @@ typedef struct MainPanel_ {
pid_t pidSearch;
} MainPanel;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, size_t);
typedef union {
int i;
void* v;
} Arg;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
}*/
static const char* MainFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL};
static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL};
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) {
FunctionBar* bar = MainPanel_getFunctionBar(this);
@ -67,27 +72,36 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
Htop_Reaction reaction = HTOP_OK;
Settings* settings = this->state->settings;
ScreenSettings* ss = settings->ss;
if (EVENT_IS_HEADER_CLICK(ch)) {
int x = EVENT_HEADER_CLICK_GET_X(ch);
ProcessList* pl = this->state->pl;
Settings* settings = this->state->settings;
int hx = super->scrollH + x + 1;
ProcessField field = ProcessList_keyAt(pl, hx);
if (field == settings->sortKey) {
Settings_invertSortOrder(settings);
settings->treeView = false;
if (field == ss->sortKey) {
ScreenSettings_invertSortOrder(ss);
ss->treeView = false;
} else {
reaction |= Action_setSortKey(settings, field);
}
reaction |= HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_SAVE_SETTINGS;
result = HANDLED;
} else if (EVENT_IS_SCREEN_TAB_CLICK(ch)) {
int x = EVENT_SCREEN_TAB_GET_X(ch);
reaction |= Action_setScreenTab(settings, x);
result = HANDLED;
} else if (ch != ERR && this->inc->active) {
bool filterChanged = IncSet_handleKey(this->inc, ch, super, (IncMode_GetPanelValue) MainPanel_getValue, NULL);
if (filterChanged) {
this->state->pl->incFilter = IncSet_filter(this->inc);
reaction = HTOP_REFRESH | HTOP_REDRAW_BAR;
}
reaction |= HTOP_KEEP_FOLLOWING;
if (this->inc->found) {
reaction |= Action_follow(this->state);
reaction |= HTOP_KEEP_FOLLOWING;
}
result = HANDLED;
} else if (ch == 27) {
return HANDLED;
@ -102,25 +116,11 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
} else {
reaction |= HTOP_KEEP_FOLLOWING;
}
switch (ch) {
case KEY_LEFT:
case KEY_CTRLB:
if (super->scrollH > 0) {
super->scrollH -= CRT_scrollHAmount;
super->needsRedraw = true;
}
return HANDLED;
case KEY_RIGHT:
case KEY_CTRLF:
super->scrollH += CRT_scrollHAmount;
super->needsRedraw = true;
return HANDLED;
}
}
if (reaction & HTOP_REDRAW_BAR) {
MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
IncSet_drawBar(this->inc);
MainPanel_updateTreeFunctions(this, settings->ss->treeView);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
}
if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(this->state->pl, Panel_getHeader(super));
@ -159,7 +159,7 @@ const char* MainPanel_getValue(MainPanel* this, int i) {
return "";
}
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, int arg, bool* wasAnyTagged) {
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged) {
Panel* super = (Panel*) this;
bool ok = true;
bool anyTagged = false;

View File

@ -21,7 +21,12 @@ typedef struct MainPanel_ {
pid_t pidSearch;
} MainPanel;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, size_t);
typedef union {
int i;
void* v;
} Arg;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
@ -34,7 +39,7 @@ int MainPanel_selectedPid(MainPanel* this);
const char* MainPanel_getValue(MainPanel* this, int i);
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, int arg, bool* wasAnyTagged);
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged);
extern PanelClass MainPanel_class;

View File

@ -12,8 +12,8 @@ applications_DATA = htop.desktop
pixmapdir = $(datadir)/pixmaps
pixmap_DATA = htop.png
htop_CFLAGS = -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
htop_LDFLAGS =
AM_CFLAGS = -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
AM_LDFLAGS =
AM_CPPFLAGS = -DNDEBUG
myhtopsources = AvailableMetersPanel.c CategoriesPanel.c CheckItem.c \
@ -21,7 +21,7 @@ ClockMeter.c ColorsPanel.c ColumnsPanel.c CPUMeter.c CRT.c MainPanel.c \
DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \
LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \
BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \
SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c \
SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c ScreensPanel.c \
TraceScreen.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c \
HostnameMeter.c OpenFilesScreen.c Affinity.c IncSet.c Action.c EnvScreen.c \
InfoScreen.c XAlloc.c
@ -31,60 +31,169 @@ CategoriesPanel.h CheckItem.h ClockMeter.h ColorsPanel.h ColumnsPanel.h \
CPUMeter.h CRT.h MainPanel.h DisplayOptionsPanel.h FunctionBar.h \
Hashtable.h Header.h htop.h ListItem.h LoadAverageMeter.h MemoryMeter.h \
BatteryMeter.h Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \
ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h \
ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h ScreensPanel.h \
TasksMeter.h UptimeMeter.h TraceScreen.h UsersTable.h Vector.h Process.h \
AffinityPanel.h HostnameMeter.h OpenFilesScreen.h Affinity.h IncSet.h Action.h \
EnvScreen.h InfoScreen.h XAlloc.h
if HTOP_LINUX
htop_CFLAGS += -rdynamic
myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \
linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c
all_platform_headers =
myhtopplatheaders = linux/Platform.h linux/IOPriorityPanel.h linux/IOPriority.h \
linux/LinuxProcess.h linux/LinuxProcessList.h linux/LinuxCRT.h linux/Battery.h
# Linux
# -----
linux_platform_headers = \
linux/Platform.h \
linux/IOPriorityPanel.h \
linux/IOPriority.h \
linux/LinuxProcess.h \
linux/LinuxProcessList.h \
linux/LinuxCRT.h \
linux/Battery.h \
linux/PerfCounter.h
all_platform_headers += $(linux_platform_headers)
if HTOP_LINUX
AM_CFLAGS += -rdynamic
myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \
linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c \
linux/PerfCounter.c
myhtopplatheaders = $(linux_platform_headers)
endif
# FreeBSD
# -------
freebsd_platform_headers = \
freebsd/Platform.h \
freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h \
freebsd/FreeBSDCRT.h \
freebsd/Battery.h
all_platform_headers += $(freebsd_platform_headers)
if HTOP_FREEBSD
myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \
freebsd/FreeBSDProcess.c freebsd/FreeBSDCRT.c freebsd/Battery.c
myhtopplatheaders = freebsd/Platform.h freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h freebsd/FreeBSDCRT.h freebsd/Battery.h
myhtopplatheaders = $(freebsd_platform_headers)
endif
# DragonFlyBSD
# ------------
dragonflybsd_platform_headers = \
dragonflybsd/Platform.h \
dragonflybsd/DragonFlyBSDProcessList.h \
dragonflybsd/DragonFlyBSDProcess.h \
dragonflybsd/DragonFlyBSDCRT.h \
dragonflybsd/Battery.h
all_platform_headers += $(dragonflybsd_platform_headers)
if HTOP_DRAGONFLYBSD
AM_LDFLAGS += -lkvm -lkinfo -lexecinfo
myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c dragonflybsd/DragonFlyBSDCRT.c dragonflybsd/Battery.c
myhtopplatheaders = $(dragonflybsd_platform_headers)
endif
# OpenBSD
# -------
openbsd_platform_headers = \
openbsd/Platform.h \
openbsd/OpenBSDProcessList.h \
openbsd/OpenBSDProcess.h \
openbsd/OpenBSDCRT.h \
openbsd/Battery.h
all_platform_headers += $(openbsd_platform_headers)
if HTOP_OPENBSD
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \
openbsd/OpenBSDProcess.c openbsd/OpenBSDCRT.c openbsd/Battery.c
myhtopplatheaders = openbsd/Platform.h openbsd/OpenBSDProcessList.h \
openbsd/OpenBSDProcess.h openbsd/OpenBSDCRT.h openbsd/Battery.h
myhtopplatheaders = $(openbsd_platform_headers)
endif
# Darwin
# ------
darwin_platform_headers = \
darwin/Platform.h \
darwin/DarwinProcess.h \
darwin/DarwinProcessList.h \
darwin/DarwinCRT.h \
darwin/Battery.h
all_platform_headers += $(darwin_platform_headers)
if HTOP_DARWIN
htop_LDFLAGS += -framework IOKit -framework CoreFoundation
AM_LDFLAGS += -framework IOKit -framework CoreFoundation
myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \
darwin/DarwinProcessList.c darwin/DarwinCRT.c darwin/Battery.c
myhtopplatheaders = darwin/Platform.h darwin/DarwinProcess.h \
darwin/DarwinProcessList.h darwin/DarwinCRT.h darwin/Battery.h
myhtopplatheaders = $(darwin_platform_headers)
endif
# Solaris
# -------
solaris_platform_headers = \
solaris/Platform.h \
solaris/SolarisProcess.h \
solaris/SolarisProcessList.h \
solaris/SolarisCRT.h \
solaris/Battery.h
all_platform_headers += $(solaris_platform_headers)
if HTOP_SOLARIS
myhtopplatsources = solaris/Platform.c \
solaris/SolarisProcess.c solaris/SolarisProcessList.c \
solaris/SolarisCRT.c solaris/Battery.c
myhtopplatheaders = $(solaris_platform_headers)
endif
# Unsupported
# -----------
unsupported_platform_headers = \
unsupported/Platform.h \
unsupported/UnsupportedProcess.h \
unsupported/UnsupportedProcessList.h \
unsupported/UnsupportedCRT.h \
unsupported/Battery.h
all_platform_headers += $(unsupported_platform_headers)
if HTOP_UNSUPPORTED
myhtopplatsources = unsupported/Platform.c \
unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c \
unsupported/UnsupportedCRT.c unsupported/Battery.c
myhtopplatheaders = unsupported/Platform.h \
unsupported/UnsupportedProcess.h unsupported/UnsupportedProcessList.h \
unsupported/UnsupportedCRT.h unsupported/Battery.h
myhtopplatheaders = $(unsupported_platform_headers)
endif
# ----
SUFFIXES = .h
BUILT_SOURCES = $(myhtopheaders) $(myhtopplatheaders)
htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources) config.h
.PHONY: htop-headers clean-htop-headers
htop-headers: $(myhtopheaders) $(all_platform_headers)
clean-htop-headers:
-rm -f $(myhtopheaders) $(all_platform_headers)
target:
echo $(htop_SOURCES)
@ -94,6 +203,9 @@ profile:
debug:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG"
symbols:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DNDEBUG"
coverage:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov"
@ -103,6 +215,13 @@ coverage:
cppcheck:
cppcheck -q -v . --enable=all -DHAVE_CGROUP -DHAVE_OPENVZ -DHAVE_TASKSTATS
dist-hook: $(top_distdir)/configure
@if grep 'pkg_m4_absent' '$(top_distdir)/configure'; then \
echo 'configure is generated without pkg.m4. Please supply pkg.m4 and run ./autogen.sh to rebuild the configure script.'>&2; \
(exit 1); \
else :; \
fi
.PHONY: lcov
lcov:

View File

@ -24,7 +24,7 @@ int MemoryMeter_attributes[] = {
MEMORY_USED, MEMORY_BUFFERS, MEMORY_CACHE
};
static void MemoryMeter_setValues(Meter* this, char* buffer, int size) {
static void MemoryMeter_updateValues(Meter* this, char* buffer, int size) {
int written;
Platform_setMemoryValues(this);
@ -60,7 +60,7 @@ MeterClass MemoryMeter_class = {
.delete = Meter_delete,
.display = MemoryMeter_display,
},
.setValues = MemoryMeter_setValues,
.updateValues = MemoryMeter_updateValues,
.defaultMode = BAR_METERMODE,
.maxItems = 3,
.total = 100.0,

69
Meter.c
View File

@ -37,7 +37,7 @@ typedef struct Meter_ Meter;
typedef void(*Meter_Init)(Meter*);
typedef void(*Meter_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int);
typedef void(*Meter_SetValues)(Meter*, char*, int);
typedef void(*Meter_UpdateValues)(Meter*, char*, int);
typedef void(*Meter_Draw)(Meter*, int, int, int);
typedef struct MeterClass_ {
@ -46,7 +46,7 @@ typedef struct MeterClass_ {
const Meter_Done done;
const Meter_UpdateMode updateMode;
const Meter_Draw draw;
const Meter_SetValues setValues;
const Meter_UpdateValues updateValues;
const int defaultMode;
const double total;
const int* attributes;
@ -66,7 +66,8 @@ typedef struct MeterClass_ {
#define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_)
#define Meter_drawFn(this_) As_Meter(this_)->draw
#define Meter_doneFn(this_) As_Meter(this_)->done
#define Meter_setValues(this_, c_, i_) As_Meter(this_)->setValues((Meter*)(this_), c_, i_)
#define Meter_updateValues(this_, buf_, sz_) \
As_Meter(this_)->updateValues((Meter*)(this_), buf_, sz_)
#define Meter_defaultMode(this_) As_Meter(this_)->defaultMode
#define Meter_getItems(this_) As_Meter(this_)->curItems
#define Meter_setItems(this_, n_) As_Meter(this_)->curItems = (n_)
@ -132,12 +133,8 @@ Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type) {
this->h = 1;
this->param = param;
this->pl = pl;
char maxItems = type->maxItems;
if (maxItems == 0) {
maxItems = 1;
}
type->curItems = maxItems;
this->values = xCalloc(maxItems, sizeof(double));
type->curItems = type->maxItems;
this->values = xCalloc(type->maxItems, sizeof(double));
this->total = type->total;
this->caption = xStrdup(type->caption);
if (Meter_initFn(this))
@ -155,7 +152,7 @@ int Meter_humanUnit(char* buffer, unsigned long int value, int size) {
if (value / 1024 < powi)
break;
if (prefix[1] == 0)
if (prefix[1] == '\0')
break;
powi *= 1024;
@ -184,8 +181,7 @@ void Meter_delete(Object* cast) {
if (Meter_doneFn(this)) {
Meter_done(this);
}
if (this->drawData)
free(this->drawData);
free(this->drawData);
free(this->caption);
free(this->values);
free(this);
@ -216,8 +212,7 @@ void Meter_setMode(Meter* this, int modeIndex) {
Meter_updateMode(this, modeIndex);
} else {
assert(modeIndex >= 1);
if (this->drawData)
free(this->drawData);
free(this->drawData);
this->drawData = NULL;
MeterMode* mode = Meter_modes[modeIndex];
@ -230,16 +225,16 @@ void Meter_setMode(Meter* this, int modeIndex) {
ListItem* Meter_toListItem(Meter* this, bool moving) {
char mode[21];
if (this->mode)
snprintf(mode, 20, " [%s]", Meter_modes[this->mode]->uiName);
xSnprintf(mode, 20, " [%s]", Meter_modes[this->mode]->uiName);
else
mode[0] = '\0';
char number[11];
if (this->param > 0)
snprintf(number, 10, " %d", this->param);
xSnprintf(number, 10, " %d", this->param);
else
number[0] = '\0';
char buffer[51];
snprintf(buffer, 50, "%s%s%s", Meter_uiName(this), number, mode);
xSnprintf(buffer, 50, "%s%s%s", Meter_uiName(this), number, mode);
ListItem* li = ListItem_new(buffer, 0);
li->moving = moving;
return li;
@ -249,7 +244,7 @@ ListItem* Meter_toListItem(Meter* this, bool moving) {
static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
char buffer[METER_BUFFER_LEN];
Meter_setValues(this, buffer, METER_BUFFER_LEN - 1);
Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
(void) w;
attrset(CRT_colors[METER_TEXT]);
@ -265,11 +260,11 @@ static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
/* ---------- BarMeterMode ---------- */
static char BarMeterMode_characters[] = "|#*@$%&.";
static const char BarMeterMode_characters[] = "|#*@$%&.";
static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
char buffer[METER_BUFFER_LEN];
Meter_setValues(this, buffer, METER_BUFFER_LEN - 1);
Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
w -= 2;
attrset(CRT_colors[METER_TEXT]);
@ -291,11 +286,8 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
char bar[w + 1];
int blockSizes[10];
for (int i = 0; i < w; i++)
bar[i] = ' ';
const size_t barOffset = w - MIN((int)strlen(buffer), w);
snprintf(bar + barOffset, w - barOffset + 1, "%s", buffer);
xSnprintf(bar, w + 1, "%*.*s", w, w, buffer);
// First draw in the bar[] buffer...
int offset = 0;
@ -344,7 +336,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
#ifdef HAVE_LIBNCURSESW
#define PIXPERROW_UTF8 4
static const char* GraphMeterMode_dotsUtf8[] = {
static const char* const GraphMeterMode_dotsUtf8[] = {
/*00*/" ", /*01*/"", /*02*/"", /*03*/"", /*04*/ "",
/*10*/"", /*11*/"", /*12*/"", /*13*/"", /*14*/ "",
/*20*/"", /*21*/"", /*22*/"", /*23*/"", /*24*/ "",
@ -355,19 +347,19 @@ static const char* GraphMeterMode_dotsUtf8[] = {
#endif
#define PIXPERROW_ASCII 2
static const char* GraphMeterMode_dotsAscii[] = {
static const char* const GraphMeterMode_dotsAscii[] = {
/*00*/" ", /*01*/".", /*02*/":",
/*10*/".", /*11*/".", /*12*/":",
/*20*/":", /*21*/":", /*22*/":"
};
static const char** GraphMeterMode_dots;
static const char* const* GraphMeterMode_dots;
static int GraphMeterMode_pixPerRow;
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
if (!this->drawData) this->drawData = xCalloc(1, sizeof(GraphData));
GraphData* data = (GraphData*) this->drawData;
GraphData* data = (GraphData*) this->drawData;
const int nValues = METER_BUFFER_LEN;
#ifdef HAVE_LIBNCURSESW
@ -397,7 +389,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
data->values[i] = data->values[i+1];
char buffer[nValues];
Meter_setValues(this, buffer, nValues - 1);
Meter_updateValues(this, buffer, nValues - 1);
double value = 0.0;
int items = Meter_getItems(this);
@ -412,10 +404,10 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
k = -i/2;
i = 0;
}
for (; i < nValues; i+=2, k++) {
for (; i < nValues - 1; i+=2, k++) {
int pix = GraphMeterMode_pixPerRow * GRAPH_HEIGHT;
int v1 = CLAMP(data->values[i] * pix, 1, pix);
int v2 = CLAMP(data->values[i+1] * pix, 1, pix);
int v1 = CLAMP((int) lround(data->values[i] * pix), 1, pix);
int v2 = CLAMP((int) lround(data->values[i+1] * pix), 1, pix);
int colorIdx = GRAPH_1;
for (int line = 0; line < GRAPH_HEIGHT; line++) {
@ -432,7 +424,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
/* ---------- LEDMeterMode ---------- */
static const char* LEDMeterMode_digitsAscii[] = {
static const char* const LEDMeterMode_digitsAscii[] = {
" __ "," "," __ "," __ "," "," __ "," __ "," __ "," __ "," __ ",
"| |"," |"," __|"," __|","|__|","|__ ","|__ "," |","|__|","|__|",
"|__|"," |","|__ "," __|"," |"," __|","|__|"," |","|__|"," __|"
@ -440,7 +432,7 @@ static const char* LEDMeterMode_digitsAscii[] = {
#ifdef HAVE_LIBNCURSESW
static const char* LEDMeterMode_digitsUtf8[] = {
static const char* const LEDMeterMode_digitsUtf8[] = {
"┌──┐","","╶──┐","╶──┐","╷ ╷","┌──╴","┌──╴","╶──┐","┌──┐","┌──┐",
"│ │","","┌──┘"," ──┤","└──┤","└──┐","├──┐","","├──┤","└──┤",
"└──┘","","└──╴","╶──┘","","╶──┘","└──┘","","└──┘"," ──┘"
@ -448,7 +440,7 @@ static const char* LEDMeterMode_digitsUtf8[] = {
#endif
static const char** LEDMeterMode_digits;
static const char* const* LEDMeterMode_digits;
static void LEDMeterMode_drawDigit(int x, int y, int n) {
for (int i = 0; i < 3; i++)
@ -466,7 +458,7 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
LEDMeterMode_digits = LEDMeterMode_digitsAscii;
char buffer[METER_BUFFER_LEN];
Meter_setValues(this, buffer, METER_BUFFER_LEN - 1);
Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
RichString_begin(out);
Meter_displayBuffer(this, buffer, &out);
@ -529,7 +521,7 @@ MeterMode* Meter_modes[] = {
/* Blank meter */
static void BlankMeter_setValues(Meter* this, char* buffer, int size) {
static void BlankMeter_updateValues(Meter* this, char* buffer, int size) {
(void) this; (void) buffer; (void) size;
}
@ -548,8 +540,9 @@ MeterClass BlankMeter_class = {
.delete = Meter_delete,
.display = BlankMeter_display,
},
.setValues = BlankMeter_setValues,
.updateValues = BlankMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 0,
.total = 100.0,
.attributes = BlankMeter_attributes,
.name = "Blank",

View File

@ -24,7 +24,7 @@ typedef struct Meter_ Meter;
typedef void(*Meter_Init)(Meter*);
typedef void(*Meter_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int);
typedef void(*Meter_SetValues)(Meter*, char*, int);
typedef void(*Meter_UpdateValues)(Meter*, char*, int);
typedef void(*Meter_Draw)(Meter*, int, int, int);
typedef struct MeterClass_ {
@ -33,7 +33,7 @@ typedef struct MeterClass_ {
const Meter_Done done;
const Meter_UpdateMode updateMode;
const Meter_Draw draw;
const Meter_SetValues setValues;
const Meter_UpdateValues updateValues;
const int defaultMode;
const double total;
const int* attributes;
@ -53,7 +53,8 @@ typedef struct MeterClass_ {
#define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_)
#define Meter_drawFn(this_) As_Meter(this_)->draw
#define Meter_doneFn(this_) As_Meter(this_)->done
#define Meter_setValues(this_, c_, i_) As_Meter(this_)->setValues((Meter*)(this_), c_, i_)
#define Meter_updateValues(this_, buf_, sz_) \
As_Meter(this_)->updateValues((Meter*)(this_), buf_, sz_)
#define Meter_defaultMode(this_) As_Meter(this_)->defaultMode
#define Meter_getItems(this_) As_Meter(this_)->curItems
#define Meter_setItems(this_, n_) As_Meter(this_)->curItems = (n_)

View File

@ -31,13 +31,19 @@ struct MetersPanel_ {
}*/
static const char* MetersFunctions[] = {"Type ", "Move ", "Delete", "Done ", NULL};
static const char* MetersKeys[] = {"Space", "Enter", "Del", "Esc"};
static int MetersEvents[] = {' ', 13, KEY_DC, 27};
// Note: In code the meters are known to have bar/text/graph "Modes", but in UI
// we call them "Styles".
static const char* const MetersFunctions[] = {"Style ", "Move ", " ", "Delete", "Done ", NULL};
static const char* const MetersKeys[] = {"Space", "Enter", " ", "Del", "F10"};
static int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
static const char* MetersMovingFunctions[] = {"Up ", "Down ", "Left ", "Right ", "Confirm", "Delete", "Done ", NULL};
static const char* MetersMovingKeys[] = {"Up", "Dn", "Lt", "Rt", "Enter", "Del", "Esc"};
static int MetersMovingEvents[] = {KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, 13, KEY_DC, 27};
// We avoid UTF-8 arrows ← → here as they might display full-width on Chinese
// terminals, breaking our aligning.
// In <http://unicode.org/reports/tr11/>, arrows (U+2019..U+2199) are
// considered "Ambiguous characters".
static const char* const MetersMovingFunctions[] = {"Style ", "Lock ", "Up ", "Down ", "Left ", "Right ", " ", "Delete", "Done ", NULL};
static const char* const MetersMovingKeys[] = {"Space", "Enter", "Up", "Dn", "<-", "->", " ", "Del", "F10"};
static int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)};
static FunctionBar* Meters_movingBar = NULL;
static void MetersPanel_delete(Object* object) {
@ -61,6 +67,7 @@ void MetersPanel_setMoving(MetersPanel* this, bool moving) {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOLLOW]);
super->currentBar = Meters_movingBar;
}
FunctionBar_draw(this->super.currentBar, NULL);
}
static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) {
@ -99,7 +106,6 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
if (!Vector_size(this->meters))
break;
MetersPanel_setMoving(this, !(this->moving));
FunctionBar_draw(this->super.currentBar, NULL);
result = HANDLED;
break;
}
@ -122,8 +128,8 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
if (!this->moving) {
break;
}
/* else fallthrough */
}
/* else fallthrough */
case KEY_F(7):
case '[':
case '-':
@ -138,8 +144,8 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
if (!this->moving) {
break;
}
/* else fallthrough */
}
/* else fallthrough */
case KEY_F(8):
case ']':
case '+':

View File

@ -27,6 +27,12 @@ struct MetersPanel_ {
};
// Note: In code the meters are known to have bar/text/graph "Modes", but in UI
// we call them "Styles".
// We avoid UTF-8 arrows ← → here as they might display full-width on Chinese
// terminals, breaking our aligning.
// In <http://unicode.org/reports/tr11/>, arrows (U+2019..U+2199) are
// considered "Ambiguous characters".
void MetersPanel_setMoving(MetersPanel* this, bool moving);

View File

@ -76,27 +76,41 @@ void OpenFilesScreen_draw(InfoScreen* this) {
}
static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
char command[1025];
snprintf(command, 1024, "lsof -P -p %d -F 2> /dev/null", pid);
FILE* fd = popen(command, "r");
char buffer[1025];
xSnprintf(buffer, 1024, "%d", pid);
OpenFiles_ProcessData* pdata = xCalloc(1, sizeof(OpenFiles_ProcessData));
OpenFiles_FileData* fdata = NULL;
OpenFiles_Data* item = &(pdata->data);
if (!fd) {
pdata->error = 127;
int fdpair[2];
if (pipe(fdpair) == -1) {
pdata->error = 1;
return pdata;
}
while (!feof(fd)) {
int cmd = fgetc(fd);
if (cmd == EOF)
break;
char* entry = xMalloc(1024);
if (!fgets(entry, 1024, fd)) {
free(entry);
pid_t child = fork();
if (child == -1) {
pdata->error = 1;
return pdata;
}
if (child == 0) {
close(fdpair[0]);
dup2(fdpair[1], STDOUT_FILENO);
close(fdpair[1]);
int fdnull = open("/dev/null", O_WRONLY);
if (fdnull < 0)
exit(1);
dup2(fdnull, STDERR_FILENO);
close(fdnull);
execlp("lsof", "lsof", "-P", "-p", buffer, "-F", NULL);
exit(127);
}
close(fdpair[1]);
FILE* fd = fdopen(fdpair[0], "r");
for (;;) {
char* line = String_readLine(fd);
if (!line) {
break;
}
char* newline = strrchr(entry, '\n');
*newline = '\0';
unsigned char cmd = line[0];
if (cmd == 'f') {
OpenFiles_FileData* nextFile = xCalloc(1, sizeof(OpenFiles_FileData));
if (fdata == NULL) {
@ -107,10 +121,18 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
fdata = nextFile;
item = &(fdata->data);
}
assert(cmd >= 0 && cmd <= 0xff);
item->data[cmd] = entry;
item->data[cmd] = xStrdup(line + 1);
free(line);
}
pdata->error = pclose(fd);
int wstatus;
if (waitpid(child, &wstatus, 0) == -1) {
pdata->error = 1;
return pdata;
}
if (!WIFEXITED(wstatus))
pdata->error = 1;
else
pdata->error = WEXITSTATUS(wstatus);
return pdata;
}
@ -132,9 +154,11 @@ void OpenFilesScreen_scan(InfoScreen* this) {
} else {
OpenFiles_FileData* fdata = pdata->files;
while (fdata) {
char entry[1024];
char** data = fdata->data.data;
sprintf(entry, "%5s %4s %10s %10s %10s %s",
int lenN = data['n'] ? strlen(data['n']) : 0;
int sizeEntry = 5 + 7 + 10 + 10 + 10 + lenN + 5 /*spaces*/ + 1 /*null*/;
char entry[sizeEntry];
xSnprintf(entry, sizeEntry, "%5.5s %7.7s %10.10s %10.10s %10.10s %s",
data['f'] ? data['f'] : "",
data['t'] ? data['t'] : "",
data['D'] ? data['D'] : "",

63
Panel.c
View File

@ -40,8 +40,12 @@ typedef enum HandlerResult_ {
#define EVENT_SET_SELECTED -1
#define EVENT_HEADER_CLICK(x_) (-10000 + x_)
#define EVENT_IS_HEADER_CLICK(ev_) (ev_ >= -10000 && ev_ <= -9000)
#define EVENT_HEADER_CLICK_GET_X(ev_) (ev_ + 10000)
#define EVENT_IS_HEADER_CLICK(ev_) (ev_ >= -10000 && ev_ < -9000)
#define EVENT_SCREEN_TAB_CLICK(x_) (-20000 + x_)
#define EVENT_SCREEN_TAB_GET_X(ev_) (ev_ + 20000)
#define EVENT_IS_SCREEN_TAB_CLICK(ev_) (ev_ >= -20000 && ev_ < -10000)
typedef HandlerResult(*Panel_EventHandler)(Panel*, int);
@ -57,14 +61,17 @@ typedef struct PanelClass_ {
struct Panel_ {
Object super;
int x, y, w, h;
int cursorX, cursorY;
WINDOW* window;
Vector* items;
int selected;
int oldSelected;
int selectedLen;
void* eventHandlerState;
int scrollV;
short scrollH;
bool needsRedraw;
bool cursorOn;
FunctionBar* currentBar;
FunctionBar* defaultBar;
RichString header;
@ -82,10 +89,12 @@ struct Panel_ {
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
#define KEY_CTRLN 0016 /* control-n key */
#define KEY_CTRLP 0020 /* control-p key */
#define KEY_CTRLF 0006 /* control-f key */
#define KEY_CTRLB 0002 /* control-b key */
#define KEY_CTRL(l) ((l)-'A'+1)
void Panel_setCursorToSelection(Panel* this) {
this->cursorY = this->y + this->selected - this->scrollV + 1;
this->cursorX = this->x + this->selectedLen - this->scrollH;
}
PanelClass Panel_class = {
.super = {
@ -114,6 +123,8 @@ void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool
this->y = y;
this->w = w;
this->h = h;
this->cursorX = 0;
this->cursorY = 0;
this->eventHandlerState = NULL;
this->items = Vector_new(type, owner, DEFAULT_SIZE);
this->scrollV = 0;
@ -327,6 +338,7 @@ void Panel_draw(Panel* this, bool focus) {
if (selected) {
attrset(selectionColor);
RichString_setAttr(&item, selectionColor);
this->selectedLen = itemLen;
}
mvhline(y + line, x, ' ', this->w);
if (amt > 0)
@ -352,6 +364,7 @@ void Panel_draw(Panel* this, bool focus) {
RichString_begin(new);
Object_display(newObj, &new);
int newLen = RichString_sizeVal(new);
this->selectedLen = newLen;
mvhline(y+ this->oldSelected - first, x+0, ' ', this->w);
if (scrollH < oldLen)
RichString_printoffnVal(old, y+this->oldSelected - first, x,
@ -367,7 +380,6 @@ void Panel_draw(Panel* this, bool focus) {
RichString_end(old);
}
this->oldSelected = this->selected;
move(0, 0);
}
bool Panel_onKey(Panel* this, int key) {
@ -376,11 +388,11 @@ bool Panel_onKey(Panel* this, int key) {
int size = Vector_size(this->items);
switch (key) {
case KEY_DOWN:
case KEY_CTRLN:
case KEY_CTRL('N'):
this->selected++;
break;
case KEY_UP:
case KEY_CTRLP:
case KEY_CTRL('P'):
this->selected--;
break;
#ifdef KEY_C_DOWN
@ -394,25 +406,26 @@ bool Panel_onKey(Panel* this, int key) {
break;
#endif
case KEY_LEFT:
case KEY_CTRLB:
case KEY_CTRL('B'):
if (this->scrollH > 0) {
this->scrollH -= CRT_scrollHAmount;
this->scrollH -= MAX(CRT_scrollHAmount, 0);
this->needsRedraw = true;
}
break;
case KEY_RIGHT:
case KEY_CTRLF:
case KEY_CTRL('F'):
this->scrollH += CRT_scrollHAmount;
this->needsRedraw = true;
break;
case KEY_PPAGE:
this->selected -= (this->h - 1);
this->scrollV -= (this->h - 1);
this->scrollV = MAX(0, this->scrollV - this->h + 1);
this->needsRedraw = true;
break;
case KEY_NPAGE:
this->selected += (this->h - 1);
this->scrollV += (this->h - 1);
this->scrollV = MAX(0, MIN(Vector_size(this->items) - this->h,
this->scrollV + this->h - 1));
this->needsRedraw = true;
break;
case KEY_WHEELUP:
@ -436,6 +449,16 @@ bool Panel_onKey(Panel* this, int key) {
case KEY_END:
this->selected = size - 1;
break;
case KEY_CTRL('A'):
case '^':
this->scrollH = 0;
this->needsRedraw = true;
break;
case KEY_CTRL('E'):
case '$':
this->scrollH = MAX(this->selectedLen - this->w, 0);
this->needsRedraw = true;
break;
default:
return false;
}
@ -458,7 +481,7 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
this->eventHandlerState = xCalloc(100, sizeof(char));
char* buffer = this->eventHandlerState;
if (ch < 255 && isalnum(ch)) {
if (ch > 0 && ch < 255 && isalnum(ch)) {
int len = strlen(buffer);
if (len < 99) {
buffer[len] = ch;
@ -488,3 +511,15 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
}
return IGNORED;
}
int Panel_getCh(Panel* this) {
if (this->cursorOn) {
move(this->cursorY, this->cursorX);
curs_set(1);
} else {
curs_set(0);
}
set_escdelay(25);
return getch();
}

19
Panel.h
View File

@ -29,8 +29,12 @@ typedef enum HandlerResult_ {
#define EVENT_SET_SELECTED -1
#define EVENT_HEADER_CLICK(x_) (-10000 + x_)
#define EVENT_IS_HEADER_CLICK(ev_) (ev_ >= -10000 && ev_ <= -9000)
#define EVENT_HEADER_CLICK_GET_X(ev_) (ev_ + 10000)
#define EVENT_IS_HEADER_CLICK(ev_) (ev_ >= -10000 && ev_ < -9000)
#define EVENT_SCREEN_TAB_CLICK(x_) (-20000 + x_)
#define EVENT_SCREEN_TAB_GET_X(ev_) (ev_ + 20000)
#define EVENT_IS_SCREEN_TAB_CLICK(ev_) (ev_ >= -20000 && ev_ < -10000)
typedef HandlerResult(*Panel_EventHandler)(Panel*, int);
@ -46,14 +50,17 @@ typedef struct PanelClass_ {
struct Panel_ {
Object super;
int x, y, w, h;
int cursorX, cursorY;
WINDOW* window;
Vector* items;
int selected;
int oldSelected;
int selectedLen;
void* eventHandlerState;
int scrollV;
short scrollH;
bool needsRedraw;
bool cursorOn;
FunctionBar* currentBar;
FunctionBar* defaultBar;
RichString header;
@ -70,10 +77,9 @@ struct Panel_ {
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
#define KEY_CTRLN 0016 /* control-n key */
#define KEY_CTRLP 0020 /* control-p key */
#define KEY_CTRLF 0006 /* control-f key */
#define KEY_CTRLB 0002 /* control-b key */
#define KEY_CTRL(l) ((l)-'A'+1)
void Panel_setCursorToSelection(Panel* this);
extern PanelClass Panel_class;
@ -125,4 +131,7 @@ bool Panel_onKey(Panel* this, int key);
HandlerResult Panel_selectByTyping(Panel* this, int ch);
int Panel_getCh(Panel* this);
#endif

143
Process.c
View File

@ -18,6 +18,7 @@ in the source distribution for its full text.
#include <sys/resource.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
@ -27,6 +28,12 @@ in the source distribution for its full text.
#include <time.h>
#include <assert.h>
#include <math.h>
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS) || \
(defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H)
#include <sys/sysmacros.h>
#endif
#ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get
@ -44,10 +51,12 @@ in the source distribution for its full text.
#include "Object.h"
#include <sys/types.h>
#include <inttypes.h>
#define PROCESS_FLAG_IO 0x0001
typedef enum ProcessFields {
NULL_PROCESSFIELD = 0,
PID = 1,
COMM = 2,
STATE = 3,
@ -148,7 +157,7 @@ typedef struct ProcessFieldData_ {
const char* name;
const char* title;
const char* description;
int flags;
uint64_t flags;
} ProcessFieldData;
// Implemented in platform-specific code:
@ -170,6 +179,12 @@ typedef struct ProcessClass_ {
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass))
#define Process_getParentPid(process_) (process_->tgid == process_->pid ? process_->ppid : process_->tgid)
#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
}*/
static int Process_getuid = -1;
@ -193,10 +208,10 @@ void Process_setupColumnWidths() {
assert(digits < 20);
for (int i = 0; Process_pidColumns[i].label; i++) {
assert(i < 20);
sprintf(Process_titleBuffer[i], "%*s ", digits, Process_pidColumns[i].label);
xSnprintf(Process_titleBuffer[i], 20, "%*s ", digits, Process_pidColumns[i].label);
Process_fields[Process_pidColumns[i].id].title = Process_titleBuffer[i];
}
sprintf(Process_pidFormat, "%%%dd ", digits);
xSnprintf(Process_pidFormat, sizeof(Process_pidFormat), "%%%dd ", digits);
}
void Process_humanNumber(RichString* str, unsigned long number, bool coloring) {
@ -260,13 +275,16 @@ void Process_colorNumber(RichString* str, unsigned long long number, bool colori
processShadowColor = CRT_colors[PROCESS];
}
if (number > 10000000000) {
snprintf(buffer, 13, "%11lld ", number / 1000);
if ((long long) number == -1LL) {
int len = snprintf(buffer, 13, " no perm ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
} else if (number > 10000000000) {
xSnprintf(buffer, 13, "%11lld ", number / 1000);
RichString_appendn(str, largeNumberColor, buffer, 5);
RichString_appendn(str, processMegabytesColor, buffer+5, 3);
RichString_appendn(str, processColor, buffer+8, 4);
} else {
snprintf(buffer, 13, "%11llu ", number);
xSnprintf(buffer, 13, "%11llu ", number);
RichString_appendn(str, largeNumberColor, buffer, 2);
RichString_appendn(str, processMegabytesColor, buffer+2, 3);
RichString_appendn(str, processColor, buffer+5, 3);
@ -283,15 +301,15 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths) {
int hundredths = totalHundredths - (totalSeconds * 100);
char buffer[11];
if (hours >= 100) {
snprintf(buffer, 10, "%7lluh ", hours);
xSnprintf(buffer, 10, "%7lluh ", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer);
} else {
if (hours) {
snprintf(buffer, 10, "%2lluh", hours);
xSnprintf(buffer, 10, "%2lluh", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer);
snprintf(buffer, 10, "%02d:%02d ", minutes, seconds);
xSnprintf(buffer, 10, "%02d:%02d ", minutes, seconds);
} else {
snprintf(buffer, 10, "%2d:%02d.%02d ", minutes, seconds, hundredths);
xSnprintf(buffer, 10, "%2d:%02d.%02d ", minutes, seconds, hundredths);
}
RichString_append(str, CRT_colors[DEFAULT_COLOR], buffer);
}
@ -353,6 +371,21 @@ void Process_outputRate(RichString* str, char* buffer, int n, double rate, int c
}
}
void Process_printPercentage(float val, char* buffer, int n, int* attr) {
if (val >= 0) {
if (val < 100) {
xSnprintf(buffer, n, "%4.1f ", val);
} else if (val < 1000) {
xSnprintf(buffer, n, "%3d. ", (unsigned int)val);
} else {
xSnprintf(buffer, n, "%4d ", (unsigned int)val);
}
} else {
*attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, " N/A ");
}
}
void Process_writeField(Process* this, RichString* str, ProcessField field) {
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
@ -361,30 +394,15 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
bool coloring = this->settings->highlightMegabytes;
switch (field) {
case PERCENT_CPU: {
if (this->percent_cpu > 999.9) {
snprintf(buffer, n, "%4d ", (unsigned int)this->percent_cpu);
} else if (this->percent_cpu > 99.9) {
snprintf(buffer, n, "%3d. ", (unsigned int)this->percent_cpu);
} else {
snprintf(buffer, n, "%4.1f ", this->percent_cpu);
}
break;
}
case PERCENT_MEM: {
if (this->percent_mem > 99.9) {
snprintf(buffer, n, "100. ");
} else {
snprintf(buffer, n, "%4.1f ", this->percent_mem);
}
break;
}
case PERCENT_CPU: Process_printPercentage(this->percent_cpu, buffer, n, &attr); break;
case PERCENT_MEM: Process_printPercentage(this->percent_mem, buffer, n, &attr); break;
case COMM: {
if (this->settings->highlightThreads && Process_isThread(this)) {
attr = CRT_colors[PROCESS_THREAD];
baseattr = CRT_colors[PROCESS_THREAD_BASENAME];
}
if (!this->settings->treeView || this->indent == 0) {
ScreenSettings* ss = this->settings->ss;
if (!ss->treeView || this->indent == 0) {
Process_writeCommand(this, attr, baseattr, str);
return;
} else {
@ -394,7 +412,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
int indent = (this->indent < 0 ? -this->indent : this->indent);
for (int i = 0; i < 32; i++)
if (indent & (1 << i))
if (indent & (1U << i))
maxIndent = i+1;
for (int i = 0; i < maxIndent - 1; i++) {
int written;
@ -405,8 +423,8 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
buf += written;
n -= written;
}
const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
snprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
const char* draw = CRT_treeStr[lastItem ? (ss->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
RichString_append(str, CRT_colors[PROCESS_TREE], buffer);
Process_writeCommand(this, attr, baseattr, str);
return;
@ -417,28 +435,28 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
case M_RESIDENT: Process_humanNumber(str, this->m_resident * PAGE_SIZE_KB, coloring); return;
case M_SIZE: Process_humanNumber(str, this->m_size * PAGE_SIZE_KB, coloring); return;
case NICE: {
snprintf(buffer, n, "%3ld ", this->nice);
xSnprintf(buffer, n, "%3ld ", this->nice);
attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY]
: this->nice > 0 ? CRT_colors[PROCESS_LOW_PRIORITY]
: attr;
break;
}
case NLWP: snprintf(buffer, n, "%4ld ", this->nlwp); break;
case PGRP: snprintf(buffer, n, Process_pidFormat, this->pgrp); break;
case PID: snprintf(buffer, n, Process_pidFormat, this->pid); break;
case PPID: snprintf(buffer, n, Process_pidFormat, this->ppid); break;
case NLWP: xSnprintf(buffer, n, "%4ld ", this->nlwp); break;
case PGRP: xSnprintf(buffer, n, Process_pidFormat, this->pgrp); break;
case PID: xSnprintf(buffer, n, Process_pidFormat, this->pid); break;
case PPID: xSnprintf(buffer, n, Process_pidFormat, this->ppid); break;
case PRIORITY: {
if(this->priority == -100)
snprintf(buffer, n, " RT ");
if(this->priority <= -100)
xSnprintf(buffer, n, " RT ");
else
snprintf(buffer, n, "%3ld ", this->priority);
xSnprintf(buffer, n, "%3ld ", this->priority);
break;
}
case PROCESSOR: snprintf(buffer, n, "%3d ", Settings_cpuId(this->settings, this->processor)); break;
case SESSION: snprintf(buffer, n, Process_pidFormat, this->session); break;
case STARTTIME: snprintf(buffer, n, "%s", this->starttime_show); break;
case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(this->settings, this->processor)); break;
case SESSION: xSnprintf(buffer, n, Process_pidFormat, this->session); break;
case STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break;
case STATE: {
snprintf(buffer, n, "%c ", this->state);
xSnprintf(buffer, n, "%c ", this->state);
switch(this->state) {
case 'R':
attr = CRT_colors[PROCESS_R_STATE];
@ -449,18 +467,18 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
}
break;
}
case ST_UID: snprintf(buffer, n, "%4d ", this->st_uid); break;
case ST_UID: xSnprintf(buffer, n, "%4d ", this->st_uid); break;
case TIME: Process_printTime(str, this->time); return;
case TGID: snprintf(buffer, n, Process_pidFormat, this->tgid); break;
case TPGID: snprintf(buffer, n, Process_pidFormat, this->tpgid); break;
case TTY_NR: snprintf(buffer, n, "%5u ", this->tty_nr); break;
case TGID: xSnprintf(buffer, n, Process_pidFormat, this->tgid); break;
case TPGID: xSnprintf(buffer, n, Process_pidFormat, this->tpgid); break;
case TTY_NR: xSnprintf(buffer, n, "%3u:%3u ", major(this->tty_nr), minor(this->tty_nr)); break;
case USER: {
if (Process_getuid != (int) this->st_uid)
attr = CRT_colors[PROCESS_SHADOW];
if (this->user) {
snprintf(buffer, n, "%-9s ", this->user);
xSnprintf(buffer, n, "%-9s ", this->user);
} else {
snprintf(buffer, n, "%-9d ", this->st_uid);
xSnprintf(buffer, n, "%-9d ", this->st_uid);
}
if (buffer[9] != '\0') {
buffer[9] = ' ';
@ -469,14 +487,14 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
break;
}
default:
snprintf(buffer, n, "- ");
xSnprintf(buffer, n, "- ");
}
RichString_append(str, attr, buffer);
}
void Process_display(Object* cast, RichString* out) {
Process* this = (Process*) cast;
ProcessField* fields = this->settings->fields;
ProcessField* fields = this->settings->ss->fields;
RichString_prune(out);
for (int i = 0; fields[i]; i++)
As_Process(this)->writeField(this, out, fields[i]);
@ -517,26 +535,24 @@ void Process_toggleTag(Process* this) {
}
bool Process_setPriority(Process* this, int priority) {
uid_t euid = geteuid();
seteuid(getuid());
CRT_dropPrivileges();
int old_prio = getpriority(PRIO_PROCESS, this->pid);
int err = setpriority(PRIO_PROCESS, this->pid, priority);
seteuid(euid);
CRT_restorePrivileges();
if (err == 0 && old_prio != getpriority(PRIO_PROCESS, this->pid)) {
this->nice = priority;
}
return (err == 0);
}
bool Process_changePriorityBy(Process* this, size_t delta) {
bool Process_changePriorityBy(Process* this, int delta) {
return Process_setPriority(this, this->nice + delta);
}
void Process_sendSignal(Process* this, size_t sgn) {
uid_t euid = geteuid();
seteuid(getuid());
void Process_sendSignal(Process* this, int sgn) {
CRT_dropPrivileges();
kill(this->pid, (int) sgn);
seteuid(euid);
CRT_restorePrivileges();
}
long Process_pidCompare(const void* v1, const void* v2) {
@ -548,14 +564,15 @@ long Process_pidCompare(const void* v1, const void* v2) {
long Process_compare(const void* v1, const void* v2) {
Process *p1, *p2;
Settings *settings = ((Process*)v1)->settings;
if (settings->direction == 1) {
ScreenSettings* ss = settings->ss;
if (ss->direction == 1) {
p1 = (Process*)v1;
p2 = (Process*)v2;
} else {
p2 = (Process*)v1;
p1 = (Process*)v2;
}
switch (settings->sortKey) {
switch (ss->sortKey) {
case PERCENT_CPU:
return (p2->percent_cpu > p1->percent_cpu ? 1 : -1);
case PERCENT_MEM:
@ -593,7 +610,7 @@ long Process_compare(const void* v1, const void* v2) {
return (p1->starttime_ctime - p2->starttime_ctime);
}
case STATE:
return (p1->state - p2->state);
return (Process_sortState(p1->state) - Process_sortState(p2->state));
case ST_UID:
return (p1->st_uid - p2->st_uid);
case TIME:

View File

@ -9,6 +9,11 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#ifdef MAJOR_IN_MKDEV
#elif defined(MAJOR_IN_SYSMACROS) || \
(defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H)
#endif
#ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set
@ -24,10 +29,12 @@ in the source distribution for its full text.
#include "Object.h"
#include <sys/types.h>
#include <inttypes.h>
#define PROCESS_FLAG_IO 0x0001
typedef enum ProcessFields {
NULL_PROCESSFIELD = 0,
PID = 1,
COMM = 2,
STATE = 3,
@ -128,7 +135,7 @@ typedef struct ProcessFieldData_ {
const char* name;
const char* title;
const char* description;
int flags;
uint64_t flags;
} ProcessFieldData;
// Implemented in platform-specific code:
@ -150,6 +157,12 @@ typedef struct ProcessClass_ {
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass))
#define Process_getParentPid(process_) (process_->tgid == process_->pid ? process_->ppid : process_->tgid)
#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
#define ONE_K 1024L
#define ONE_M (ONE_K * ONE_K)
@ -171,6 +184,8 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths);
void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring);
void Process_printPercentage(float val, char* buffer, int n, int* attr);
void Process_writeField(Process* this, RichString* str, ProcessField field);
void Process_display(Object* cast, RichString* out);
@ -185,9 +200,9 @@ void Process_toggleTag(Process* this);
bool Process_setPriority(Process* this, int priority);
bool Process_changePriorityBy(Process* this, size_t delta);
bool Process_changePriorityBy(Process* this, int delta);
void Process_sendSignal(Process* this, size_t sgn);
void Process_sendSignal(Process* this, int sgn);
long Process_pidCompare(const void* v1, const void* v2);

View File

@ -124,14 +124,15 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
void ProcessList_printHeader(ProcessList* this, RichString* header) {
RichString_prune(header);
ProcessField* fields = this->settings->fields;
ProcessField* fields = this->settings->ss->fields;
for (int i = 0; fields[i]; i++) {
const char* field = Process_fields[fields[i]].title;
unsigned int key = fields[i];
const char* field = Process_fields[key].title;
if (!field) field = "- ";
if (!this->settings->treeView && this->settings->sortKey == fields[i])
RichString_append(header, CRT_colors[PANEL_SELECTION_FOCUS], field);
else
RichString_append(header, CRT_colors[PANEL_HEADER_FOCUS], field);
int color = (!this->settings->ss->treeView && this->settings->ss->sortKey == key)
? CRT_colors[PANEL_SELECTION_FOCUS]
: CRT_colors[PANEL_HEADER_FOCUS];
RichString_append(header, color, field);
}
}
@ -173,7 +174,7 @@ static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int i
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
Process* process = (Process*) (Vector_get(this->processes, i));
if (process->show && (process->tgid == pid || (process->tgid == process->pid && process->ppid == pid))) {
if (process->show && Process_isChildOf(process, pid)) {
process = (Process*) (Vector_take(this->processes, i));
Vector_add(children, process);
}
@ -200,36 +201,64 @@ static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int i
}
void ProcessList_sort(ProcessList* this) {
if (!this->settings->treeView) {
if (!this->settings->ss->treeView) {
Vector_insertionSort(this->processes);
} else {
// Save settings
int direction = this->settings->direction;
int sortKey = this->settings->sortKey;
int direction = this->settings->ss->direction;
int sortKey = this->settings->ss->sortKey;
// Sort by PID
this->settings->sortKey = PID;
this->settings->direction = 1;
this->settings->ss->sortKey = PID;
this->settings->ss->direction = 1;
Vector_quickSort(this->processes);
// Restore settings
this->settings->sortKey = sortKey;
this->settings->direction = direction;
// Take PID 1 as root and add to the new listing
this->settings->ss->sortKey = sortKey;
this->settings->ss->direction = direction;
int vsize = Vector_size(this->processes);
Process* init = (Process*) (Vector_take(this->processes, 0));
if (!init) return;
// This assertion crashes on hardened kernels.
// I wonder how well tree view works on those systems.
// assert(init->pid == 1);
init->indent = 0;
Vector_add(this->processes2, init);
// Recursively empty list
ProcessList_buildTree(this, init->pid, 0, 0, direction, true);
// Add leftovers
while (Vector_size(this->processes)) {
Process* p = (Process*) (Vector_take(this->processes, 0));
p->indent = 0;
Vector_add(this->processes2, p);
ProcessList_buildTree(this, p->pid, 0, 0, direction, p->showChildren);
// Find all processes whose parent is not visible
int size;
while ((size = Vector_size(this->processes))) {
int i;
for (i = 0; i < size; i++) {
Process* process = (Process*)(Vector_get(this->processes, i));
// Immediately consume not shown processes
if (!process->show) {
process = (Process*)(Vector_take(this->processes, i));
process->indent = 0;
Vector_add(this->processes2, process);
ProcessList_buildTree(this, process->pid, 0, 0, direction, false);
break;
}
pid_t ppid = Process_getParentPid(process);
// Bisect the process vector to find parent
int l = 0, r = size;
// If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
// on Mac OS X 10.11.6) cancel bisecting and regard this process as
// root.
if (process->pid == ppid)
r = 0;
while (l < r) {
int c = (l + r) / 2;
pid_t pid = ((Process*)(Vector_get(this->processes, c)))->pid;
if (ppid == pid) {
break;
} else if (ppid < pid) {
r = c;
} else {
l = c + 1;
}
}
// If parent not found, then construct the tree with this root
if (l >= r) {
process = (Process*)(Vector_take(this->processes, i));
process->indent = 0;
Vector_add(this->processes2, process);
ProcessList_buildTree(this, process->pid, 0, 0, direction, process->showChildren);
break;
}
}
// There should be no loop in the process tree
assert(i < size);
}
assert(Vector_size(this->processes2) == vsize); (void)vsize;
assert(Vector_size(this->processes) == 0);
@ -243,7 +272,7 @@ void ProcessList_sort(ProcessList* this) {
ProcessField ProcessList_keyAt(ProcessList* this, int at) {
int x = 0;
ProcessField* fields = this->settings->fields;
ProcessField* fields = this->settings->ss->fields;
ProcessField field;
for (int i = 0; (field = fields[i]); i++) {
const char* title = Process_fields[field].title;

2
README
View File

@ -40,7 +40,7 @@ Compilation instructions
This program is distributed as a standard autotools-based package.
See the [INSTALL](/INSTALL) file for detailed instructions.
When compiling from a release tarball, run:
When compiling from a [release tarball](https://hisham.hm/htop/releases/), run:
./configure && make

View File

@ -63,6 +63,10 @@ typedef struct RichString_ {
}*/
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
#define charBytes(n) (sizeof(CharType) * (n))
static void RichString_extendLen(RichString* this, int len) {
@ -103,6 +107,7 @@ static inline void RichString_writeFrom(RichString* this, int attrs, const char*
inline void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
cchar_t* ch = this->chptr + start;
finish = CLAMP(finish, 0, this->chlen - 1);
for (int i = start; i <= finish; i++) {
ch->attr = attrs;
ch++;
@ -132,6 +137,7 @@ static inline void RichString_writeFrom(RichString* this, int attrs, const char*
void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
chtype* ch = this->chptr + start;
finish = CLAMP(finish, 0, this->chlen - 1);
for (int i = start; i <= finish; i++) {
*ch = (*ch & 0xff) | attrs;
ch++;

View File

@ -59,6 +59,10 @@ typedef struct RichString_ {
} RichString;
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
#define charBytes(n) (sizeof(CharType) * (n))
#define RichString_setLen(this, len) do{ if(len < RICHSTRING_MAXLEN && this->chlen < RICHSTRING_MAXLEN) { RichString_setChar(this,len,0); this->chlen=len; } else RichString_extendLen(this,len); }while(0)

View File

@ -13,6 +13,7 @@ in the source distribution for its full text.
#include <assert.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
@ -35,6 +36,7 @@ typedef struct ScreenManager_ {
int y2;
Orientation orientation;
Vector* panels;
const char* name;
int panelCount;
const Header* header;
const Settings* settings;
@ -71,30 +73,46 @@ inline int ScreenManager_size(ScreenManager* this) {
}
void ScreenManager_add(ScreenManager* this, Panel* item, int size) {
ScreenManager_insert(this, item, size, Vector_size(this->panels));
}
void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx) {
if (this->orientation == HORIZONTAL) {
int lastX = 0;
if (this->panelCount > 0) {
Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1);
if (idx > 0) {
Panel* last = (Panel*) Vector_get(this->panels, idx - 1);
lastX = last->x + last->w + 1;
}
int height = LINES - this->y1 + this->y2;
if (size > 0) {
Panel_resize(item, size, height);
} else {
Panel_resize(item, COLS-this->x1+this->x2-lastX, height);
if (size <= 0) {
size = COLS-this->x1+this->x2-lastX;
}
Panel_resize(item, size, height);
Panel_move(item, lastX, this->y1);
if (idx < this->panelCount) {
for (int i = idx + 1; i <= this->panelCount; i++) {
Panel* p = (Panel*) Vector_get(this->panels, i);
Panel_move(p, p->x + size, p->y);
}
}
}
// TODO: VERTICAL
Vector_add(this->panels, item);
Vector_insert(this->panels, idx, item);
item->needsRedraw = true;
this->panelCount++;
}
Panel* ScreenManager_remove(ScreenManager* this, int idx) {
assert(this->panelCount > idx);
int w = ((Panel*) Vector_get(this->panels, idx))->w;
Panel* panel = (Panel*) Vector_remove(this->panels, idx);
this->panelCount--;
if (idx < this->panelCount) {
for (int i = idx; i < this->panelCount; i++) {
Panel* p = (Panel*) Vector_get(this->panels, i);
Panel_move(p, p->x - w, p->y);
}
}
return panel;
}
@ -131,7 +149,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
if (*rescan) {
*oldTime = newTime;
ProcessList_scan(pl);
if (*sortTimeout == 0 || this->settings->treeView) {
if (*sortTimeout == 0 || this->settings->ss->treeView) {
ProcessList_sort(pl);
*sortTimeout = 1;
}
@ -144,7 +162,50 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
*rescan = false;
}
static inline bool drawTab(int* y, int* x, int l, const char* name, bool cur) {
attrset(CRT_colors[cur ? SCREENS_CUR_BORDER : SCREENS_OTH_BORDER]);
mvaddch(*y, *x, '[');
(*x)++;
if (*x >= l) return false;
int nameLen = strlen(name);
int n = MIN(l - *x, nameLen);
attrset(CRT_colors[cur ? SCREENS_CUR_TEXT : SCREENS_OTH_TEXT]);
mvaddnstr(*y, *x, name, n);
*x += n;
if (*x >= l) return false;
attrset(CRT_colors[cur ? SCREENS_CUR_BORDER : SCREENS_OTH_BORDER]);
mvaddch(*y, *x, ']');
*x += 2;
if (*x >= l) return false;
return true;
}
static void ScreenManager_drawScreenTabs(ScreenManager* this) {
ScreenSettings** screens = this->settings->screens;
int cur = this->settings->ssIndex;
int l = COLS;
Panel* panel = (Panel*) Vector_get(this->panels, 0);
int y = panel->y - 1;
int x = 2;
if (this->name) {
drawTab(&y, &x, l, this->name, true);
return;
}
for (int s = 0; screens[s]; s++) {
bool ok = drawTab(&y, &x, l, screens[s]->name, s == cur);
if (!ok) {
break;
}
}
attrset(CRT_colors[RESET_COLOR]);
}
static void ScreenManager_drawPanels(ScreenManager* this, int focus) {
if (this->settings->screenTabs) {
ScreenManager_drawScreenTabs(this);
}
int nPanels = this->panelCount;
for (int i = 0; i < nPanels; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i);
@ -157,16 +218,17 @@ static void ScreenManager_drawPanels(ScreenManager* this, int focus) {
}
}
static Panel* setCurrentPanel(Panel* panel) {
static Panel* setCurrentPanel(ScreenManager* this, int focus) {
Panel* panel = (Panel*) Vector_get(this->panels, focus);
FunctionBar_draw(panel->currentBar, NULL);
return panel;
}
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey, char* name) {
bool quit = false;
int focus = 0;
Panel* panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
Panel* panelFocus = setCurrentPanel(this, focus);
double oldTime = 0.0;
@ -178,6 +240,8 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool rescan = false;
int sortTimeout = 0;
int resetSortTimeout = 5;
this->name = name;
while (!quit) {
if (this->header) {
@ -189,8 +253,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
}
int prevCh = ch;
ESCDELAY = 25;
ch = getch();
ch = Panel_getCh(panelFocus);
HandlerResult result = IGNORED;
if (ch == KEY_MOUSE) {
@ -208,11 +271,14 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (mevent.y == panel->y) {
ch = EVENT_HEADER_CLICK(mevent.x - panel->x);
break;
} else if (this->settings->screenTabs && mevent.y == panel->y - 1) {
ch = EVENT_SCREEN_TAB_CLICK(mevent.x);
break;
} else if (mevent.y > panel->y && mevent.y <= panel->y+panel->h) {
ch = KEY_MOUSE;
if (panel == panelFocus || this->allowFocusChange) {
focus = i;
panelFocus = setCurrentPanel(panel);
panelFocus = setCurrentPanel(this, i);
Object* oldSelection = Panel_getSelected(panel);
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
if (Panel_getSelected(panel) == oldSelection) {
@ -279,25 +345,31 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
continue;
}
case KEY_LEFT:
case KEY_CTRLB:
case KEY_CTRL('B'):
if (this->panelCount < 2) {
goto defaultHandler;
}
if (!this->allowFocusChange)
break;
tryLeft:
if (focus > 0)
focus--;
panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
panelFocus = setCurrentPanel(this, focus);
if (Panel_size(panelFocus) == 0 && focus > 0)
goto tryLeft;
break;
case KEY_RIGHT:
case KEY_CTRLF:
case KEY_CTRL('F'):
case 9:
if (this->panelCount < 2) {
goto defaultHandler;
}
if (!this->allowFocusChange)
break;
tryRight:
if (focus < this->panelCount - 1)
focus++;
panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
panelFocus = setCurrentPanel(this, focus);
if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1)
goto tryRight;
break;
@ -307,6 +379,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
quit = true;
continue;
default:
defaultHandler:
sortTimeout = resetSortTimeout;
Panel_onKey(panelFocus, ch);
break;

View File

@ -27,6 +27,7 @@ typedef struct ScreenManager_ {
int y2;
Orientation orientation;
Vector* panels;
const char* name;
int panelCount;
const Header* header;
const Settings* settings;
@ -43,10 +44,12 @@ extern int ScreenManager_size(ScreenManager* this);
void ScreenManager_add(ScreenManager* this, Panel* item, int size);
void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx);
Panel* ScreenManager_remove(ScreenManager* this, int idx);
void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2);
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey);
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey, char* name);
#endif

331
ScreensPanel.c Normal file
View File

@ -0,0 +1,331 @@
/*
htop - ScreensPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "ScreensPanel.h"
#include "Platform.h"
#include "StringUtils.h"
#include "CRT.h"
#include <assert.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
/*{
#include "Panel.h"
#include "ScreenManager.h"
#include "ColumnsPanel.h"
#include "Settings.h"
#include "ListItem.h"
#ifndef SCREEN_NAME_LEN
#define SCREEN_NAME_LEN 20
#endif
typedef struct ScreensPanel_ {
Panel super;
ScreenManager* scr;
Settings* settings;
ColumnsPanel* columns;
char buffer[SCREEN_NAME_LEN + 1];
char* saved;
int cursor;
bool moving;
bool renaming;
} ScreensPanel;
typedef struct ScreenListItem_ {
ListItem super;
ScreenSettings* ss;
} ScreenListItem;
}*/
ObjectClass ScreenListItem_class = {
.extends = Class(ListItem),
.display = ListItem_display,
.delete = ListItem_delete,
.compare = ListItem_compare
};
ScreenListItem* ScreenListItem_new(const char* value, ScreenSettings* ss) {
ScreenListItem* this = AllocThis(ScreenListItem);
ListItem_init((ListItem*)this, value, 0);
this->ss = ss;
return this;
}
static const char* const ScreensFunctions[] = {" ", "Rename", " ", " ", "New ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL};
static void ScreensPanel_delete(Object* object) {
Panel* super = (Panel*) object;
ScreensPanel* this = (ScreensPanel*) object;
Panel_done(super);
free(this);
}
static HandlerResult ScreensPanel_eventHandlerRenaming(Panel* super, int ch) {
ScreensPanel* const this = (ScreensPanel*) super;
//ListItem* item = (ListItem*)Panel_getSelected(super);
if (ch >= 32 && ch < 127 && ch != 61 && ch != 22) {
if (this->cursor < SCREEN_NAME_LEN - 1) {
this->buffer[this->cursor] = ch;
this->cursor++;
super->selectedLen = strlen(this->buffer);
Panel_setCursorToSelection(super);
}
} else {
switch(ch) {
case 127:
case KEY_BACKSPACE:
{
if (this->cursor > 0) {
this->cursor--;
this->buffer[this->cursor] = '\0';
super->selectedLen = strlen(this->buffer);
Panel_setCursorToSelection(super);
}
break;
}
case 0x0a:
case 0x0d:
case KEY_ENTER:
{
ListItem* item = (ListItem*) Panel_getSelected(super);
free(this->saved);
item->value = xStrdup(this->buffer);
this->renaming = false;
super->cursorOn = false;
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
ScreensPanel_update(super);
break;
}
case 27: // Esc
{
ListItem* item = (ListItem*) Panel_getSelected(super);
item->value = this->saved;
this->renaming = false;
super->cursorOn = false;
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
break;
}
}
}
return HANDLED;
}
static void startRenaming(Panel* super) {
ScreensPanel* const this = (ScreensPanel*) super;
ListItem* item = (ListItem*) Panel_getSelected(super);
this->renaming = true;
super->cursorOn = true;
char* name = item->value;
this->saved = name;
strncpy(this->buffer, name, SCREEN_NAME_LEN);
this->buffer[SCREEN_NAME_LEN] = '\0';
this->cursor = strlen(this->buffer);
item->value = this->buffer;
Panel_setSelectionColor(super, CRT_colors[PANEL_EDIT]);
super->selectedLen = strlen(this->buffer);
Panel_setCursorToSelection(super);
}
static void rebuildSettingsArray(Panel* super) {
ScreensPanel* const this = (ScreensPanel*) super;
int n = Panel_size(super);
free(this->settings->screens);
this->settings->screens = xMalloc(sizeof(ScreenSettings*) * (n + 1));
this->settings->screens[n] = NULL;
for (int i = 0; i < n; i++) {
ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
this->settings->screens[i] = item->ss;
}
this->settings->nScreens = n;
}
static void addNewScreen(Panel* super) {
ScreensPanel* const this = (ScreensPanel*) super;
char* name = "New";
ScreenSettings* ss = Settings_newScreen(this->settings, name, "PID Command");
ScreenListItem* item = ScreenListItem_new(name, ss);
int idx = Panel_getSelectedIndex(super);
Panel_insert(super, idx + 1, (Object*) item);
Panel_setSelected(super, idx + 1);
}
static HandlerResult ScreensPanel_eventHandlerNormal(Panel* super, int ch) {
ScreensPanel* const this = (ScreensPanel*) super;
int selected = Panel_getSelectedIndex(super);
ScreenListItem* oldFocus = (ScreenListItem*) Panel_getSelected(super);
bool shouldRebuildArray = false;
HandlerResult result = IGNORED;
switch(ch) {
case 0x0a:
case 0x0d:
case KEY_ENTER:
case KEY_MOUSE:
case KEY_RECLICK:
{
this->moving = !(this->moving);
Panel_setSelectionColor(super, this->moving ? CRT_colors[PANEL_SELECTION_FOLLOW] : CRT_colors[PANEL_SELECTION_FOCUS]);
((ListItem*)Panel_getSelected(super))->moving = this->moving;
result = HANDLED;
break;
}
case EVENT_SET_SELECTED:
result = HANDLED;
break;
case KEY_NPAGE:
case KEY_PPAGE:
case KEY_HOME:
case KEY_END: {
Panel_onKey(super, ch);
break;
}
case KEY_F(2):
case KEY_CTRL('R'):
{
startRenaming(super);
result = HANDLED;
break;
}
case KEY_F(5):
case KEY_CTRL('N'):
{
addNewScreen(super);
startRenaming(super);
shouldRebuildArray = true;
result = HANDLED;
break;
}
case KEY_UP:
{
if (!this->moving) {
Panel_onKey(super, ch);
break;
}
/* else fallthrough */
}
case KEY_F(7):
case '[':
case '-':
{
Panel_moveSelectedUp(super);
shouldRebuildArray = true;
result = HANDLED;
break;
}
case KEY_DOWN:
{
if (!this->moving) {
Panel_onKey(super, ch);
break;
}
/* else fallthrough */
}
case KEY_F(8):
case ']':
case '+':
{
Panel_moveSelectedDown(super);
shouldRebuildArray = true;
result = HANDLED;
break;
}
case KEY_F(9):
//case KEY_DC:
{
if (Panel_size(super) > 1) {
Panel_remove(super, selected);
}
shouldRebuildArray = true;
result = HANDLED;
break;
}
default:
{
if (ch < 255 && isalpha(ch))
result = Panel_selectByTyping(super, ch);
if (result == BREAK_LOOP)
result = IGNORED;
break;
}
}
ScreenListItem* newFocus = (ScreenListItem*) Panel_getSelected(super);
if (oldFocus != newFocus) {
ColumnsPanel_fill(this->columns, newFocus->ss);
result = HANDLED;
}
if (shouldRebuildArray) {
rebuildSettingsArray(super);
}
if (result == HANDLED)
ScreensPanel_update(super);
return result;
}
static HandlerResult ScreensPanel_eventHandler(Panel* super, int ch) {
ScreensPanel* const this = (ScreensPanel*) super;
if (this->renaming) {
return ScreensPanel_eventHandlerRenaming(super, ch);
} else {
return ScreensPanel_eventHandlerNormal(super, ch);
}
}
PanelClass ScreensPanel_class = {
.super = {
.extends = Class(Panel),
.delete = ScreensPanel_delete
},
.eventHandler = ScreensPanel_eventHandler
};
ScreensPanel* ScreensPanel_new(Settings* settings) {
ScreensPanel* this = AllocThis(ScreensPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ScreensFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings;
this->columns = ColumnsPanel_new(settings->screens[0], &(settings->changed));
this->moving = false;
this->renaming = false;
super->cursorOn = false;
this->cursor = 0;
Panel_setHeader(super, "Screens");
for (unsigned int i = 0; i < settings->nScreens; i++) {
ScreenSettings* ss = settings->screens[i];
char* name = ss->name;
Panel_add(super, (Object*) ScreenListItem_new(name, ss));
}
return this;
}
void ScreensPanel_update(Panel* super) {
ScreensPanel* this = (ScreensPanel*) super;
int size = Panel_size(super);
this->settings->changed = true;
this->settings->screens = xRealloc(this->settings->screens, sizeof(char*) * (size+1));
for (int i = 0; i < size; i++) {
ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
ScreenSettings* ss = item->ss;
free(ss->name);
this->settings->screens[i] = ss;
ss->name = xStrdup(((ListItem*) item)->value);
}
this->settings->screens[size] = NULL;
}

51
ScreensPanel.h Normal file
View File

@ -0,0 +1,51 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_ScreensPanel
#define HEADER_ScreensPanel
/*
htop - ScreensPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
#include "ScreenManager.h"
#include "ColumnsPanel.h"
#include "Settings.h"
#include "ListItem.h"
#ifndef SCREEN_NAME_LEN
#define SCREEN_NAME_LEN 20
#endif
typedef struct ScreensPanel_ {
Panel super;
ScreenManager* scr;
Settings* settings;
ColumnsPanel* columns;
char buffer[SCREEN_NAME_LEN + 1];
char* saved;
int cursor;
bool moving;
bool renaming;
} ScreensPanel;
typedef struct ScreenListItem_ {
ListItem super;
ScreenSettings* ss;
} ScreenListItem;
extern ObjectClass ScreenListItem_class;
ScreenListItem* ScreenListItem_new(const char* value, ScreenSettings* ss);
extern PanelClass ScreensPanel_class;
ScreensPanel* ScreensPanel_new(Settings* settings);
void ScreensPanel_update(Panel* super);
#endif

View File

@ -23,29 +23,44 @@ in the source distribution for its full text.
#include "Process.h"
#include <stdbool.h>
typedef struct {
const char* name;
const char* columns;
const char* sortKey;
} ScreenDefaults;
typedef struct {
int len;
char** names;
int* modes;
} MeterColumnSettings;
typedef struct {
char* name;
ProcessField* fields;
int flags;
int direction;
ProcessField sortKey;
bool treeView;
} ScreenSettings;
typedef struct Settings_ {
char* filename;
MeterColumnSettings columns[2];
MeterColumnSettings meterColumns[2];
ScreenSettings** screens;
unsigned int nScreens;
unsigned int ssIndex;
ScreenSettings* ss;
ProcessField* fields;
int flags;
int colorScheme;
int delay;
int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero;
bool detailedCPUTime;
bool treeView;
bool showProgramPath;
bool hideThreads;
bool shadowOtherUsers;
@ -58,6 +73,7 @@ typedef struct Settings_ {
bool updateProcessNames;
bool accountGuestInCPUMeter;
bool headerMargin;
bool screenTabs;
bool changed;
} Settings;
@ -68,25 +84,82 @@ typedef struct Settings_ {
}*/
static void writeList(FILE* fd, char** list, int len) {
const char* sep = "";
for (int i = 0; i < len; i++) {
fprintf(fd, "%s%s", sep, list[i]);
sep = " ";
}
fprintf(fd, "\n");
}
/*
static char** readQuotedList(char* line) {
int n = 0;
char** list = xCalloc(sizeof(char*), 1);
int start = 0;
for (;;) {
while (line[start] && line[start] == ' ') {
start++;
}
if (line[start] != '"') {
break;
}
start++;
int close = start;
while (line[close] && line[close] != '"') {
close++;
}
int len = close - start;
char* item = xMalloc(len + 1);
strncpy(item, line + start, len);
item[len] = '\0';
list[n] = item;
n++;
list = xRealloc(list, sizeof(char*) * (n + 1));
start = close + 1;
}
list[n] = NULL;
return list;
}
static void writeQuotedList(FILE* fd, char** list) {
const char* sep = "";
for (int i = 0; list[i]; i++) {
fprintf(fd, "%s\"%s\"", sep, list[i]);
sep = " ";
}
fprintf(fd, "\n");
}
*/
void Settings_delete(Settings* this) {
free(this->filename);
free(this->fields);
for (unsigned int i = 0; i < (sizeof(this->columns)/sizeof(MeterColumnSettings)); i++) {
String_freeArray(this->columns[i].names);
free(this->columns[i].modes);
for (unsigned int i = 0; i < (sizeof(this->meterColumns)/sizeof(MeterColumnSettings)); i++) {
String_freeArray(this->meterColumns[i].names);
free(this->meterColumns[i].modes);
}
if (this->screens) {
for (unsigned int i = 0; this->screens[i]; i++) {
free(this->screens[i]->name);
free(this->screens[i]->fields);
}
free(this->screens);
}
free(this);
}
static void Settings_readMeters(Settings* this, char* line, int column) {
static void Settings_readMeters(Settings* this, char* line, int side) {
char* trim = String_trim(line);
int nIds;
char** ids = String_split(trim, ' ', &nIds);
free(trim);
this->columns[column].names = ids;
this->meterColumns[side].names = ids;
}
static void Settings_readMeterModes(Settings* this, char* line, int column) {
static void Settings_readMeterModes(Settings* this, char* line, int side) {
char* trim = String_trim(line);
int nIds;
char** ids = String_split(trim, ' ', &nIds);
@ -95,13 +168,13 @@ static void Settings_readMeterModes(Settings* this, char* line, int column) {
for (int i = 0; ids[i]; i++) {
len++;
}
this->columns[column].len = len;
this->meterColumns[side].len = len;
int* modes = xCalloc(len, sizeof(int));
for (int i = 0; i < len; i++) {
modes[i] = atoi(ids[i]);
}
String_freeArray(ids);
this->columns[column].modes = modes;
this->meterColumns[side].modes = modes;
}
static void Settings_defaultMeters(Settings* this) {
@ -110,37 +183,62 @@ static void Settings_defaultMeters(Settings* this) {
sizes[1]++;
}
for (int i = 0; i < 2; i++) {
this->columns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
this->columns[i].modes = xCalloc(sizes[i], sizeof(int));
this->columns[i].len = sizes[i];
this->meterColumns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
this->meterColumns[i].modes = xCalloc(sizes[i], sizeof(int));
this->meterColumns[i].len = sizes[i];
}
int r = 0;
if (this->cpuCount > 8) {
this->columns[0].names[0] = xStrdup("LeftCPUs2");
this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs2");
this->columns[1].modes[r++] = BAR_METERMODE;
this->meterColumns[0].names[0] = xStrdup("LeftCPUs2");
this->meterColumns[0].modes[0] = BAR_METERMODE;
this->meterColumns[1].names[r] = xStrdup("RightCPUs2");
this->meterColumns[1].modes[r++] = BAR_METERMODE;
} else if (this->cpuCount > 4) {
this->columns[0].names[0] = xStrdup("LeftCPUs");
this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs");
this->columns[1].modes[r++] = BAR_METERMODE;
this->meterColumns[0].names[0] = xStrdup("LeftCPUs");
this->meterColumns[0].modes[0] = BAR_METERMODE;
this->meterColumns[1].names[r] = xStrdup("RightCPUs");
this->meterColumns[1].modes[r++] = BAR_METERMODE;
} else {
this->columns[0].names[0] = xStrdup("AllCPUs");
this->columns[0].modes[0] = BAR_METERMODE;
this->meterColumns[0].names[0] = xStrdup("AllCPUs");
this->meterColumns[0].modes[0] = BAR_METERMODE;
}
this->columns[0].names[1] = xStrdup("Memory");
this->columns[0].modes[1] = BAR_METERMODE;
this->columns[0].names[2] = xStrdup("Swap");
this->columns[0].modes[2] = BAR_METERMODE;
this->meterColumns[0].names[1] = xStrdup("Memory");
this->meterColumns[0].modes[1] = BAR_METERMODE;
this->meterColumns[0].names[2] = xStrdup("Swap");
this->meterColumns[0].modes[2] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("Tasks");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("LoadAverage");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("Uptime");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->meterColumns[1].names[r] = xStrdup("Tasks");
this->meterColumns[1].modes[r++] = TEXT_METERMODE;
this->meterColumns[1].names[r] = xStrdup("LoadAverage");
this->meterColumns[1].modes[r++] = TEXT_METERMODE;
this->meterColumns[1].names[r] = xStrdup("Uptime");
this->meterColumns[1].modes[r++] = TEXT_METERMODE;
}
static const char* toFieldName(int i) {
if (i < 0 || i > LAST_PROCESSFIELD) {
return "";
}
return Process_fields[i].name;
}
static int toFieldIndex(const char* str) {
if (isdigit(str[0])) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(str) + 1;
if (id < Platform_numberOfFields && toFieldName(id)) {
return id;
}
} else {
for (int p = 1; p < LAST_PROCESSFIELD; p++) {
const char* pName = toFieldName(p);
if (pName && strcmp(pName, str) == 0) {
return p;
}
}
}
return -1;
}
static void readFields(ProcessField* fields, int* flags, const char* line) {
@ -151,47 +249,70 @@ static void readFields(ProcessField* fields, int* flags, const char* line) {
int i, j;
*flags = 0;
for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(ids[i]) + 1;
if (id > 0 && Process_fields[id].name && id < Platform_numberOfFields) {
fields[j] = id;
*flags |= Process_fields[id].flags;
int idx = toFieldIndex(ids[i]);
if (idx != -1) {
fields[j] = idx;
*flags |= Process_fields[idx].flags;
j++;
}
}
fields[j] = (ProcessField) NULL;
fields[j] = NULL_PROCESSFIELD;
String_freeArray(ids);
}
ScreenSettings* Settings_newScreen(Settings* this, const char* name, const char* line) {
ScreenSettings* ss = xCalloc(sizeof(ScreenSettings), 1);
ss->name = xStrdup(name);
ss->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
ss->flags = 0;
ss->direction = 1;
ss->treeView = 0;
readFields(ss->fields, &(ss->flags), line);
ss->sortKey = ss->fields[0];
this->screens[this->nScreens] = ss;
this->nScreens++;
this->screens = xRealloc(this->screens, sizeof(ScreenSettings*) * (this->nScreens + 1));
this->screens[this->nScreens] = NULL;
return ss;
}
static void Settings_defaultScreens(Settings* this) {
for (unsigned int i = 0; i < Platform_numberOfDefaultScreens; i++) {
ScreenDefaults* defaults = &Platform_defaultScreens[i];
Settings_newScreen(this, defaults->name, defaults->columns);
this->screens[i]->sortKey = toFieldIndex(defaults->sortKey);
}
}
static bool Settings_read(Settings* this, const char* fileName) {
FILE* fd;
uid_t euid = geteuid();
seteuid(getuid());
CRT_dropPrivileges();
fd = fopen(fileName, "r");
seteuid(euid);
CRT_restorePrivileges();
if (!fd)
return false;
const int maxLine = 2048;
char buffer[maxLine];
bool readMeters = false;
while (fgets(buffer, maxLine, fd)) {
bool didReadMeters = false;
bool didReadFields = false;
ProcessField* legacyFields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
int legacyFlags;
bool legacyFieldsRead = false;
for (;;) {
char* line = String_readLine(fd);
if (!line) {
break;
}
int nOptions;
char** option = String_split(buffer, '=', &nOptions);
char** option = String_split(line, '=', &nOptions);
free (line);
if (nOptions < 2) {
String_freeArray(option);
continue;
}
if (String_eq(option[0], "fields")) {
readFields(this->fields, &(this->flags), option[1]);
} else if (String_eq(option[0], "sort_key")) {
// This "+1" is for compatibility with the older enum format.
this->sortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "sort_direction")) {
this->direction = atoi(option[1]);
} else if (String_eq(option[0], "tree_view")) {
this->treeView = atoi(option[1]);
readFields(legacyFields, &legacyFlags, option[1]);
legacyFieldsRead = true;
} else if (String_eq(option[0], "hide_threads")) {
this->hideThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_kernel_threads")) {
@ -212,6 +333,8 @@ static bool Settings_read(Settings* this, const char* fileName) {
this->highlightThreads = atoi(option[1]);
} else if (String_eq(option[0], "header_margin")) {
this->headerMargin = atoi(option[1]);
} else if (String_eq(option[0], "screen_tabs")) {
this->screenTabs = atoi(option[1]);
} else if (String_eq(option[0], "expand_system_time")) {
// Compatibility option.
this->detailedCPUTime = atoi(option[1]);
@ -230,65 +353,90 @@ static bool Settings_read(Settings* this, const char* fileName) {
if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) this->colorScheme = 0;
} else if (String_eq(option[0], "left_meters")) {
Settings_readMeters(this, option[1], 0);
readMeters = true;
didReadMeters = true;
} else if (String_eq(option[0], "right_meters")) {
Settings_readMeters(this, option[1], 1);
readMeters = true;
didReadMeters = true;
} else if (String_eq(option[0], "left_meter_modes")) {
Settings_readMeterModes(this, option[1], 0);
readMeters = true;
didReadMeters = true;
} else if (String_eq(option[0], "right_meter_modes")) {
Settings_readMeterModes(this, option[1], 1);
readMeters = true;
didReadMeters = true;
} else if (strncmp(option[0], "screen:", 7) == 0) {
Settings_newScreen(this, option[0] + 7, option[1]);
didReadFields = true;
} else if (String_eq(option[0], ".tree_view")) {
if (this->nScreens > 0) {
this->screens[this->nScreens - 1]->treeView = atoi(option[1]);
}
} else if (String_eq(option[0], ".sort_direction")) {
if (this->nScreens > 0) {
this->screens[this->nScreens - 1]->direction = atoi(option[1]);
}
} else if (String_eq(option[0], ".sort_key")) {
if (this->nScreens > 0) {
this->screens[this->nScreens - 1]->sortKey = toFieldIndex(option[1]);
}
}
String_freeArray(option);
}
fclose(fd);
if (!readMeters) {
if (this->nScreens == 0) {
Settings_defaultScreens(this);
if (legacyFieldsRead) {
didReadFields = true;
free(this->screens[0]->fields);
this->screens[0]->fields = legacyFields;
this->screens[0]->flags = legacyFlags;
}
}
if (!didReadMeters) {
Settings_defaultMeters(this);
}
return true;
return didReadFields;
}
static void writeFields(FILE* fd, ProcessField* fields, const char* name) {
fprintf(fd, "%s=", name);
static void writeFields(FILE* fd, ProcessField* fields, bool byName) {
const char* sep = "";
for (int i = 0; fields[i]; i++) {
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "%d ", (int) fields[i]-1);
if (byName) {
fprintf(fd, "%s%s", sep, toFieldName(fields[i]));
} else {
// This " - 1" is for compatibility with the older enum format.
fprintf(fd, "%s%d", sep, (int) fields[i] - 1);
}
sep = " ";
}
fprintf(fd, "\n");
}
static void writeMeters(Settings* this, FILE* fd, int column) {
for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%s ", this->columns[column].names[i]);
}
fprintf(fd, "\n");
static void writeMeters(Settings* this, FILE* fd, int side) {
writeList(fd, this->meterColumns[side].names, this->meterColumns[side].len);
}
static void writeMeterModes(Settings* this, FILE* fd, int column) {
for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%d ", this->columns[column].modes[i]);
static void writeMeterModes(Settings* this, FILE* fd, int side) {
const char* sep = "";
for (int i = 0; i < this->meterColumns[side].len; i++) {
fprintf(fd, "%s%d", sep, this->meterColumns[side].modes[i]);
sep = " ";
}
fprintf(fd, "\n");
}
bool Settings_write(Settings* this) {
FILE* fd;
uid_t euid = geteuid();
seteuid(getuid());
CRT_dropPrivileges();
fd = fopen(this->filename, "w");
seteuid(euid);
CRT_restorePrivileges();
if (fd == NULL) {
return false;
}
fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n");
fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
writeFields(fd, this->fields, "fields");
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->sortKey-1);
fprintf(fd, "sort_direction=%d\n", (int) this->direction);
fprintf(fd, "fields="); writeFields(fd, this->screens[0]->fields, false);
fprintf(fd, "hide_threads=%d\n", (int) this->hideThreads);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
@ -298,8 +446,8 @@ bool Settings_write(Settings* this) {
fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName);
fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes);
fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads);
fprintf(fd, "tree_view=%d\n", (int) this->treeView);
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
fprintf(fd, "screen_tabs=%d\n", (int) this->screenTabs);
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero);
fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames);
@ -310,6 +458,23 @@ bool Settings_write(Settings* this) {
fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0);
fprintf(fd, "right_meters="); writeMeters(this, fd, 1);
fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1);
// Legacy compatibility with older versions of htop
fprintf(fd, "tree_view=%d\n", (int) this->screens[0]->treeView);
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->screens[0]->sortKey-1);
fprintf(fd, "sort_direction=%d\n", (int) this->screens[0]->direction);
if (this->screens && this->screens[0]) {
for (unsigned int i = 0; i < this->nScreens; i++) {
ScreenSettings* ss = this->screens[i];
fprintf(fd, "screen:%s=", ss->name);
writeFields(fd, ss->fields, true);
fprintf(fd, ".tree_view=%d\n", (int) ss->treeView);
fprintf(fd, ".sort_key=%s\n", toFieldName(ss->sortKey));
fprintf(fd, ".sort_direction=%d\n", (int) ss->direction);
}
}
fclose(fd);
return true;
}
@ -318,14 +483,11 @@ Settings* Settings_new(int cpuCount) {
Settings* this = xCalloc(1, sizeof(Settings));
this->sortKey = PERCENT_CPU;
this->direction = 1;
this->hideThreads = false;
this->shadowOtherUsers = false;
this->showThreadNames = false;
this->hideKernelThreads = false;
this->hideUserlandThreads = false;
this->treeView = false;
this->highlightBaseName = false;
this->highlightMegabytes = false;
this->detailedCPUTime = false;
@ -335,15 +497,8 @@ Settings* Settings_new(int cpuCount) {
this->showProgramPath = true;
this->highlightThreads = true;
this->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
// TODO: turn 'fields' into a Vector,
// (and ProcessFields into proper objects).
this->flags = 0;
ProcessField* defaults = Platform_defaultFields;
for (int i = 0; defaults[i]; i++) {
this->fields[i] = defaults[i];
this->flags |= Process_fields[defaults[i]].flags;
}
this->screens = xCalloc(sizeof(ScreenSettings*), 1);
this->nScreens = 0;
char* legacyDotfile = NULL;
char* rcfile = getenv("HTOPRC");
@ -365,51 +520,60 @@ Settings* Settings_new(int cpuCount) {
htopDir = String_cat(home, "/.config/htop");
}
legacyDotfile = String_cat(home, "/.htoprc");
uid_t euid = geteuid();
seteuid(getuid());
CRT_dropPrivileges();
(void) mkdir(configDir, 0700);
(void) mkdir(htopDir, 0700);
free(htopDir);
free(configDir);
struct stat st;
if (lstat(legacyDotfile, &st) != 0) {
st.st_mode = 0;
}
if (access(legacyDotfile, R_OK) != 0 || S_ISLNK(st.st_mode)) {
int err = lstat(legacyDotfile, &st);
if (err || S_ISLNK(st.st_mode)) {
free(legacyDotfile);
legacyDotfile = NULL;
}
seteuid(euid);
CRT_restorePrivileges();
}
this->colorScheme = 0;
this->changed = false;
this->delay = DEFAULT_DELAY;
bool ok = Settings_read(this, legacyDotfile ? legacyDotfile : this->filename);
if (ok) {
if (legacyDotfile) {
bool ok = false;
if (legacyDotfile) {
ok = Settings_read(this, legacyDotfile);
if (ok) {
// Transition to new location and delete old configuration file
if (Settings_write(this))
unlink(legacyDotfile);
}
} else {
free(legacyDotfile);
}
if (!ok) {
ok = Settings_read(this, this->filename);
}
if (!ok) {
this->changed = true;
// TODO: how to get SYSCONFDIR correctly through Autoconf?
char* systemSettings = String_cat(SYSCONFDIR, "/htoprc");
ok = Settings_read(this, systemSettings);
free(systemSettings);
if (!ok) {
Settings_defaultMeters(this);
this->hideKernelThreads = true;
this->highlightMegabytes = true;
this->highlightThreads = true;
this->headerMargin = true;
}
}
free(legacyDotfile);
if (!ok) {
Settings_defaultMeters(this);
Settings_defaultScreens(this);
this->hideKernelThreads = true;
this->highlightMegabytes = true;
this->highlightThreads = true;
this->headerMargin = true;
this->screenTabs = true;
}
this->ssIndex = 0;
this->ss = this->screens[this->ssIndex];
return this;
}
void Settings_invertSortOrder(Settings* this) {
void ScreenSettings_invertSortOrder(ScreenSettings* this) {
if (this->direction == 1)
this->direction = -1;
else

View File

@ -14,29 +14,44 @@ in the source distribution for its full text.
#include "Process.h"
#include <stdbool.h>
typedef struct {
const char* name;
const char* columns;
const char* sortKey;
} ScreenDefaults;
typedef struct {
int len;
char** names;
int* modes;
} MeterColumnSettings;
typedef struct {
char* name;
ProcessField* fields;
int flags;
int direction;
ProcessField sortKey;
bool treeView;
} ScreenSettings;
typedef struct Settings_ {
char* filename;
MeterColumnSettings columns[2];
MeterColumnSettings meterColumns[2];
ScreenSettings** screens;
unsigned int nScreens;
unsigned int ssIndex;
ScreenSettings* ss;
ProcessField* fields;
int flags;
int colorScheme;
int delay;
int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero;
bool detailedCPUTime;
bool treeView;
bool showProgramPath;
bool hideThreads;
bool shadowOtherUsers;
@ -49,6 +64,7 @@ typedef struct Settings_ {
bool updateProcessNames;
bool accountGuestInCPUMeter;
bool headerMargin;
bool screenTabs;
bool changed;
} Settings;
@ -58,12 +74,18 @@ typedef struct Settings_ {
#endif
/*
*/
void Settings_delete(Settings* this);
ScreenSettings* Settings_newScreen(Settings* this, const char* name, const char* line);
bool Settings_write(Settings* this);
Settings* Settings_new(int cpuCount);
void Settings_invertSortOrder(Settings* this);
void ScreenSettings_invertSortOrder(ScreenSettings* this);
#endif

View File

@ -31,13 +31,27 @@ Panel* SignalsPanel_new() {
Panel* this = Panel_new(1, 1, 1, 1, true, Class(ListItem), FunctionBar_newEnterEsc("Send ", "Cancel "));
const int defaultSignal = SIGTERM;
int defaultPosition = 15;
for(unsigned int i = 0; i < Platform_numberOfSignals; i++) {
unsigned int i;
for (i = 0; i < Platform_numberOfSignals; i++) {
Panel_set(this, i, (Object*) ListItem_new(Platform_signals[i].name, Platform_signals[i].number));
// signal 15 is not always the 15th signal in the table
if (Platform_signals[i].number == defaultSignal) {
defaultPosition = i;
}
}
#if (defined(SIGRTMIN) && defined(SIGRTMAX))
if (SIGRTMAX - SIGRTMIN <= 100) {
static char buf[16];
for (int sig = SIGRTMIN; sig <= SIGRTMAX; i++, sig++) {
int n = sig - SIGRTMIN;
xSnprintf(buf, 16, "%2d SIGRTMIN%-+3d", sig, n);
if (n == 0) {
buf[11] = '\0';
}
Panel_set(this, i, (Object*) ListItem_new(buf, sig));
}
}
#endif
Panel_setHeader(this, "Send signal:");
Panel_setSelected(this, defaultPosition);
return this;

View File

@ -13,19 +13,25 @@ in the source distribution for its full text.
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>
/*{
#define String_startsWith(s, match) (strstr((s), (match)) == (s))
#include <stdio.h>
#define String_startsWith(s, match) (strncmp((s),(match),strlen(match)) == 0)
#define String_contains_i(s1, s2) (strcasestr(s1, s2) != NULL)
}*/
/*
* String_startsWith gives better performance if strlen(match) can be computed
* at compile time (e.g. when they are immutable string literals). :)
*/
char* String_cat(const char* s1, const char* s2) {
int l1 = strlen(s1);
int l2 = strlen(s2);
size_t l1 = strlen(s1);
size_t l2 = strlen(s2);
char* out = xMalloc(l1 + l2 + 1);
strncpy(out, s1, l1);
strncpy(out+l1, s2, l2+1);
strncpy(out + l1, s2, l2 + 1);
return out;
}
@ -33,7 +39,7 @@ char* String_trim(const char* in) {
while (in[0] == ' ' || in[0] == '\t' || in[0] == '\n') {
in++;
}
int len = strlen(in);
size_t len = strlen(in);
while (len > 0 && (in[len-1] == ' ' || in[len-1] == '\t' || in[len-1] == '\n')) {
len--;
}
@ -69,33 +75,27 @@ char** String_split(const char* s, char sep, int* n) {
ctr++;
if (ctr == blocks) {
blocks += rate;
char** newOut = (char**) xRealloc(out, sizeof(char*) * blocks);
if (newOut) {
out = newOut;
} else {
blocks -= rate;
break;
}
out = (char**) xRealloc(out, sizeof(char*) * blocks);
}
s += size + 1;
}
if (s[0] != '\0') {
int size = strlen(s);
size_t size = strlen(s);
char* token = xMalloc(size + 1);
strncpy(token, s, size + 1);
out[ctr] = token;
ctr++;
}
char** newOut = xRealloc(out, sizeof(char*) * (ctr + 1));
if (newOut) {
out = newOut;
}
out = xRealloc(out, sizeof(char*) * (ctr + 1));
out[ctr] = NULL;
*n = ctr;
return out;
}
void String_freeArray(char** s) {
if (!s) {
return;
}
for (int i = 0; s[i] != NULL; i++) {
free(s[i]);
}
@ -128,3 +128,29 @@ char* String_getToken(const char* line, const unsigned short int numMatch) {
match[foundCount] = '\0';
return((char*)xStrdup(match));
}
char* String_readLine(FILE* fd) {
const int step = 1024;
int bufSize = step;
char* buffer = xMalloc(step + 1);
char* at = buffer;
for (;;) {
char* ok = fgets(at, step + 1, fd);
if (!ok) {
free(buffer);
return NULL;
}
char* newLine = strrchr(at, '\n');
if (newLine) {
*newLine = '\0';
return buffer;
} else {
if (feof(fd)) {
return buffer;
}
}
bufSize += step;
buffer = xRealloc(buffer, bufSize + 1);
at = buffer + bufSize - step;
}
}

View File

@ -9,9 +9,16 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#define String_startsWith(s, match) (strstr((s), (match)) == (s))
#include <stdio.h>
#define String_startsWith(s, match) (strncmp((s),(match),strlen(match)) == 0)
#define String_contains_i(s1, s2) (strcasestr(s1, s2) != NULL)
/*
* String_startsWith gives better performance if strlen(match) can be computed
* at compile time (e.g. when they are immutable string literals). :)
*/
char* String_cat(const char* s1, const char* s2);
char* String_trim(const char* in);
@ -24,4 +31,6 @@ void String_freeArray(char** s);
char* String_getToken(const char* line, const unsigned short int numMatch);
char* String_readLine(FILE* fd);
#endif

View File

@ -24,7 +24,7 @@ int SwapMeter_attributes[] = {
SWAP
};
static void SwapMeter_setValues(Meter* this, char* buffer, int size) {
static void SwapMeter_updateValues(Meter* this, char* buffer, int size) {
int written;
Platform_setSwapValues(this);
@ -54,8 +54,9 @@ MeterClass SwapMeter_class = {
.delete = Meter_delete,
.display = SwapMeter_display,
},
.setValues = SwapMeter_setValues,
.updateValues = SwapMeter_updateValues,
.defaultMode = BAR_METERMODE,
.maxItems = 1,
.total = 100.0,
.attributes = SwapMeter_attributes,
.name = "Swap",

View File

@ -18,19 +18,19 @@ int TasksMeter_attributes[] = {
CPU_KERNEL, PROCESS_THREAD, PROCESS, TASKS_RUNNING
};
static void TasksMeter_setValues(Meter* this, char* buffer, int len) {
static void TasksMeter_updateValues(Meter* this, char* buffer, int len) {
ProcessList* pl = this->pl;
this->values[0] = pl->kernelThreads;
this->values[1] = pl->userlandThreads;
this->values[2] = pl->totalTasks - pl->kernelThreads - pl->userlandThreads;
this->values[3] = pl->runningTasks;
this->values[3] = MIN(pl->runningTasks, pl->cpuCount);
if (pl->totalTasks > this->total) {
this->total = pl->totalTasks;
}
if (this->pl->settings->hideKernelThreads) {
this->values[0] = 0;
}
snprintf(buffer, len, "%d/%d", (int) this->values[3], (int) this->total);
xSnprintf(buffer, len, "%d/%d", (int) this->values[3], (int) this->total);
}
static void TasksMeter_display(Object* cast, RichString* out) {
@ -40,7 +40,7 @@ static void TasksMeter_display(Object* cast, RichString* out) {
int processes = (int) this->values[2];
sprintf(buffer, "%d", processes);
xSnprintf(buffer, sizeof(buffer), "%d", processes);
RichString_write(out, CRT_colors[METER_VALUE], buffer);
int threadValueColor = CRT_colors[METER_VALUE];
int threadCaptionColor = CRT_colors[METER_TEXT];
@ -50,18 +50,18 @@ static void TasksMeter_display(Object* cast, RichString* out) {
}
if (!settings->hideUserlandThreads) {
RichString_append(out, CRT_colors[METER_TEXT], ", ");
sprintf(buffer, "%d", (int)this->values[1]);
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[1]);
RichString_append(out, threadValueColor, buffer);
RichString_append(out, threadCaptionColor, " thr");
}
if (!settings->hideKernelThreads) {
RichString_append(out, CRT_colors[METER_TEXT], ", ");
sprintf(buffer, "%d", (int)this->values[0]);
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[0]);
RichString_append(out, threadValueColor, buffer);
RichString_append(out, threadCaptionColor, " kthr");
}
RichString_append(out, CRT_colors[METER_TEXT], "; ");
sprintf(buffer, "%d", (int)this->values[3]);
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[3]);
RichString_append(out, CRT_colors[TASKS_RUNNING], buffer);
RichString_append(out, CRT_colors[METER_TEXT], " running");
}
@ -72,10 +72,10 @@ MeterClass TasksMeter_class = {
.delete = Meter_delete,
.display = TasksMeter_display,
},
.setValues = TasksMeter_setValues,
.updateValues = TasksMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.total = 100.0,
.maxItems = 4,
.total = 100.0,
.attributes = TasksMeter_attributes,
.name = "Tasks",
.uiName = "Task counter",

View File

@ -22,6 +22,7 @@ in the source distribution for its full text.
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
@ -42,9 +43,9 @@ typedef struct TraceScreen_ {
}*/
static const char* TraceScreenFunctions[] = {"Search ", "Filter ", "AutoScroll ", "Stop Tracing ", "Done ", NULL};
static const char* const TraceScreenFunctions[] = {"Search ", "Filter ", "AutoScroll ", "Stop Tracing ", "Done ", NULL};
static const char* TraceScreenKeys[] = {"F3", "F4", "F8", "F9", "Esc"};
static const char* const TraceScreenKeys[] = {"F3", "F4", "F8", "F9", "Esc"};
static int TraceScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(8), KEY_F(9), 27};
@ -85,29 +86,30 @@ void TraceScreen_draw(InfoScreen* this) {
mvhline(0, 0, ' ', COLS);
mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, this->process->comm);
attrset(CRT_colors[DEFAULT_COLOR]);
IncSet_drawBar(this->inc);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
}
bool TraceScreen_forkTracer(TraceScreen* this) {
char buffer[1001];
int err = pipe(this->fdpair);
if (err == -1) return false;
int error = pipe(this->fdpair);
if (error == -1) return false;
this->child = fork();
if (this->child == -1) return false;
if (this->child == 0) {
seteuid(getuid());
CRT_dropPrivileges();
dup2(this->fdpair[1], STDERR_FILENO);
int ok = fcntl(this->fdpair[1], F_SETFL, O_NONBLOCK);
if (ok != -1) {
sprintf(buffer, "%d", this->super.process->pid);
execlp("strace", "strace", "-p", buffer, NULL);
xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid);
execlp("strace", "strace", "-s", "512", "-p", buffer, NULL);
}
const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH.";
ssize_t written = write(this->fdpair[1], message, strlen(message));
(void) written;
exit(1);
}
fcntl(this->fdpair[0], F_SETFL, O_NONBLOCK);
int ok = fcntl(this->fdpair[0], F_SETFL, O_NONBLOCK);
if (ok == -1) return false;
this->strace = fdopen(this->fdpair[0], "r");
this->fd_strace = fileno(this->strace);
return true;

View File

@ -17,10 +17,10 @@ int UptimeMeter_attributes[] = {
UPTIME
};
static void UptimeMeter_setValues(Meter* this, char* buffer, int len) {
static void UptimeMeter_updateValues(Meter* this, char* buffer, int len) {
int totalseconds = Platform_getUptime();
if (totalseconds == -1) {
snprintf(buffer, len, "(unknown)");
xSnprintf(buffer, len, "(unknown)");
return;
}
int seconds = totalseconds % 60;
@ -31,17 +31,17 @@ static void UptimeMeter_setValues(Meter* this, char* buffer, int len) {
if (days > this->total) {
this->total = days;
}
char daysbuf[15];
char daysbuf[32];
if (days > 100) {
sprintf(daysbuf, "%d days(!), ", days);
xSnprintf(daysbuf, sizeof(daysbuf), "%d days(!), ", days);
} else if (days > 1) {
sprintf(daysbuf, "%d days, ", days);
xSnprintf(daysbuf, sizeof(daysbuf), "%d days, ", days);
} else if (days == 1) {
sprintf(daysbuf, "1 day, ");
xSnprintf(daysbuf, sizeof(daysbuf), "1 day, ");
} else {
daysbuf[0] = '\0';
}
snprintf(buffer, len, "%s%02d:%02d:%02d", daysbuf, hours, minutes, seconds);
xSnprintf(buffer, len, "%s%02d:%02d:%02d", daysbuf, hours, minutes, seconds);
}
MeterClass UptimeMeter_class = {
@ -49,8 +49,9 @@ MeterClass UptimeMeter_class = {
.extends = Class(Meter),
.delete = Meter_delete
},
.setValues = UptimeMeter_setValues,
.updateValues = UptimeMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 100.0,
.attributes = UptimeMeter_attributes,
.name = "Uptime",

View File

@ -284,11 +284,19 @@ inline Object* Vector_get(Vector* this, int idx) {
#endif
#ifdef DEBUG
inline int Vector_size(Vector* this) {
assert(Vector_isConsistent(this));
return this->items;
}
#else
#define Vector_size(v_) ((v_)->items)
#endif
/*
static void Vector_merge(Vector* this, Vector* v2) {

View File

@ -70,8 +70,16 @@ extern Object* Vector_get(Vector* this, int idx);
#endif
#ifdef DEBUG
extern int Vector_size(Vector* this);
#else
#define Vector_size(v_) ((v_)->items)
#endif
/*
*/

View File

@ -5,11 +5,12 @@
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <err.h>
#include <stdlib.h>
#include <string.h>
/*{
#include <err.h>
#include <assert.h>
#include <stdlib.h>
}*/
@ -29,7 +30,7 @@ void* xMalloc(size_t size) {
void* xCalloc(size_t nmemb, size_t size) {
void* data = calloc(nmemb, size);
if (!data) {
if (!data && nmemb > 0 && size > 0) {
fail();
}
return data;
@ -43,9 +44,27 @@ void* xRealloc(void* ptr, size_t size) {
return data;
}
char* xStrdup(const char* str) {
#define xSnprintf(fmt, len, ...) do { int _l=len; int _n=snprintf(fmt, _l, __VA_ARGS__); if (!(_n > -1 && _n < _l)) { curs_set(1); endwin(); err(1, NULL); } } while(0)
#undef xStrdup
#undef xStrdup_
#ifdef NDEBUG
# define xStrdup_ xStrdup
#else
# define xStrdup(str_) (assert(str_), xStrdup_(str_))
#endif
#ifndef __has_attribute // Clang's macro
# define __has_attribute(x) 0
#endif
#if (__has_attribute(nonnull) || \
((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)))
char* xStrdup_(const char* str) __attribute__((nonnull));
#endif // __has_attribute(nonnull) || GNU C 3.3 or later
char* xStrdup_(const char* str) {
char* data = strdup(str);
if (!data && str) {
if (!data) {
fail();
}
return data;

View File

@ -7,6 +7,8 @@
#define _GNU_SOURCE
#endif
#include <err.h>
#include <assert.h>
#include <stdlib.h>
void* xMalloc(size_t size);
@ -15,6 +17,24 @@ void* xCalloc(size_t nmemb, size_t size);
void* xRealloc(void* ptr, size_t size);
char* xStrdup(const char* str);
#define xSnprintf(fmt, len, ...) do { int _l=len; int _n=snprintf(fmt, _l, __VA_ARGS__); if (!(_n > -1 && _n < _l)) { curs_set(1); endwin(); err(1, NULL); } } while(0)
#undef xStrdup
#undef xStrdup_
#ifdef NDEBUG
# define xStrdup_ xStrdup
#else
# define xStrdup(str_) (assert(str_), xStrdup_(str_))
#endif
#ifndef __has_attribute // Clang's macro
# define __has_attribute(x) 0
#endif
#if (__has_attribute(nonnull) || \
((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)))
char* xStrdup_(const char* str) __attribute__((nonnull));
#endif // __has_attribute(nonnull) || GNU C 3.3 or later
char* xStrdup_(const char* str);
#endif

View File

@ -2,43 +2,50 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.65)
AC_INIT([htop],[2.0.1],[hisham@gobolinux.org])
AC_INIT([htop],[3.0.0beta5],[hisham@gobolinux.org])
year=$(date +%Y)
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(date +%s)}"
year=$(date -u -d "@$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u -r "$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u "+%Y")
# The following two lines are required by hwloc scripts
AC_USE_SYSTEM_EXTENSIONS
AC_CONFIG_SRCDIR([htop.c])
AC_CONFIG_AUX_DIR([.])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
# Required by hwloc scripts
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([1.11])
AC_CONFIG_SRCDIR([htop.c])
AC_CONFIG_HEADER([config.h])
AC_CONFIG_MACRO_DIR([m4])
# Checks for programs.
# ----------------------------------------------------------------------
AC_PROG_CC
AM_PROG_CC_C_O
AC_DISABLE_SHARED
AC_ENABLE_STATIC
AC_PROG_LIBTOOL
# Required by hwloc scripts
AC_USE_SYSTEM_EXTENSIONS
# Checks for platform.
# ----------------------------------------------------------------------
case "$target" in
*linux*)
case "$target_os" in
linux*|gnu*)
my_htop_platform=linux
;;
*freebsd*)
freebsd*|kfreebsd*)
my_htop_platform=freebsd
;;
*openbsd*)
openbsd*)
my_htop_platform=openbsd
;;
*darwin*)
dragonfly*)
my_htop_platform=dragonflybsd
;;
darwin*)
my_htop_platform=darwin
;;
solaris*)
my_htop_platform=solaris
;;
*)
my_htop_platform=unsupported
;;
@ -57,6 +64,16 @@ AC_CHECK_HEADERS([stdlib.h string.h strings.h sys/param.h sys/time.h unistd.h],[
])
AC_CHECK_HEADERS([execinfo.h],[:],[:])
AC_HEADER_MAJOR
dnl glibc 2.25 deprecates 'major' and 'minor' in <sys/types.h> and requires to
dnl include <sys/sysmacros.h>. However the logic in AC_HEADER_MAJOR has not yet
dnl been updated in Autoconf 2.69, so use a workaround:
m4_version_prereq([2.70], [],
[if test "x$ac_cv_header_sys_mkdev_h" = xno; then
AC_CHECK_HEADER(sys/sysmacros.h, [AC_DEFINE(MAJOR_IN_SYSMACROS, 1,
[Define to 1 if `major', `minor', and `makedev' are declared in <sys/sysmacros.h>.])])
fi])
# Checks for typedefs, structures, and compiler characteristics.
# ----------------------------------------------------------------------
AC_HEADER_STDBOOL
@ -74,15 +91,16 @@ AC_CHECK_FUNCS([memmove strncasecmp strstr strdup])
save_cflags="${CFLAGS}"
CFLAGS="${CFLAGS} -std=c99"
AC_MSG_CHECKING([whether gcc -std=c99 option works])
AC_TRY_COMPILE(AC_INCLUDES_DEFAULT, [char *a; a = strdup("foo"); int i = 0; i++; // C99],
AC_MSG_RESULT([yes]),
AC_MSG_ERROR([htop is written in C99. A newer version of gcc is required.]))
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
[AC_INCLUDES_DEFAULT], [[char *a; a = strdup("foo"); int i = 0; i++; // C99]])],
[AC_MSG_RESULT([yes])],
[AC_MSG_ERROR([htop is written in C99. A newer version of gcc is required.])])
CFLAGS="$save_cflags"
save_cflags="${CFLAGS}"
CFLAGS="$CFLAGS -Wextra"
AC_MSG_CHECKING([if compiler supports -Wextra])
AC_TRY_COMPILE([], [], [
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [])],[
wextra_flag=-Wextra
AC_MSG_RESULT([yes])
],[
@ -96,14 +114,14 @@ AC_SUBST(wextra_flag)
# ----------------------------------------------------------------------
PROCDIR=/proc
AC_ARG_ENABLE(proc, [AC_HELP_STRING([--enable-proc], [use Linux-compatible proc filesystem])], enable_proc="yes", enable_proc="no")
AC_ARG_ENABLE(proc, [AS_HELP_STRING([--enable-proc], [use Linux-compatible proc filesystem])], enable_proc="yes", enable_proc="no")
if test "x$enable_proc" = xyes; then
# An enabled proc assumes we're emulating Linux.
my_htop_platform=linux
AC_DEFINE(HAVE_PROC, 1, [Define if using a Linux-compatible proc filesystem.])
fi
AC_ARG_WITH(proc, [AC_HELP_STRING([--with-proc=DIR], [Location of a Linux-compatible proc filesystem (default=/proc).])],
AC_ARG_WITH(proc, [AS_HELP_STRING([--with-proc=DIR], [Location of a Linux-compatible proc filesystem (default=/proc).])],
if test -n "$withval"; then
AC_DEFINE_UNQUOTED(PROCDIR, "$withval", [Path of proc filesystem])
@ -118,28 +136,28 @@ if test "x$cross_compiling" = xno; then
fi
fi
AC_ARG_ENABLE(openvz, [AC_HELP_STRING([--enable-openvz], [enable OpenVZ support])], ,enable_openvz="no")
AC_ARG_ENABLE(openvz, [AS_HELP_STRING([--enable-openvz], [enable OpenVZ support])], ,enable_openvz="no")
if test "x$enable_openvz" = xyes; then
AC_DEFINE(HAVE_OPENVZ, 1, [Define if openvz support enabled.])
fi
AC_ARG_ENABLE(cgroup, [AC_HELP_STRING([--enable-cgroup], [enable cgroups support])], ,enable_cgroup="no")
AC_ARG_ENABLE(cgroup, [AS_HELP_STRING([--enable-cgroup], [enable cgroups support])], ,enable_cgroup="no")
if test "x$enable_cgroup" = xyes; then
AC_DEFINE(HAVE_CGROUP, 1, [Define if cgroup support enabled.])
fi
AC_ARG_ENABLE(vserver, [AC_HELP_STRING([--enable-vserver], [enable VServer support])], ,enable_vserver="no")
AC_ARG_ENABLE(vserver, [AS_HELP_STRING([--enable-vserver], [enable VServer support])], ,enable_vserver="no")
if test "x$enable_vserver" = xyes; then
AC_DEFINE(HAVE_VSERVER, 1, [Define if vserver support enabled.])
fi
AC_ARG_ENABLE(ancient_vserver, [AC_HELP_STRING([--enable-ancient-vserver], [enable ancient VServer support (implies --enable-vserver)])], ,enable_ancient_vserver="no")
AC_ARG_ENABLE(ancient_vserver, [AS_HELP_STRING([--enable-ancient-vserver], [enable ancient VServer support (implies --enable-vserver)])], ,enable_ancient_vserver="no")
if test "x$enable_ancient_vserver" = xyes; then
AC_DEFINE(HAVE_VSERVER, 1, [Define if vserver support enabled.])
AC_DEFINE(HAVE_ANCIENT_VSERVER, 1, [Define if ancient vserver support enabled.])
fi
AC_ARG_ENABLE(taskstats, [AC_HELP_STRING([--enable-taskstats], [enable per-task IO Stats (taskstats kernel sup required)])], ,enable_taskstats="yes")
AC_ARG_ENABLE(taskstats, [AS_HELP_STRING([--enable-taskstats], [enable per-task IO Stats (taskstats kernel sup required)])], ,enable_taskstats="yes")
if test "x$enable_taskstats" = xyes; then
AC_DEFINE(HAVE_TASKSTATS, 1, [Define if taskstats support enabled.])
fi
@ -147,17 +165,28 @@ fi
# HTOP_CHECK_SCRIPT(LIBNAME, FUNCTION, DEFINE, CONFIG_SCRIPT, ELSE_PART)
m4_define([HTOP_CHECK_SCRIPT],
[
htop_config_script=$([$4] --libs 2> /dev/null)
if test ! -z "m4_toupper($HTOP_[$1]_CONFIG_SCRIPT)"; then
# to be used to set the path to ncurses*-config when cross-compiling
htop_config_script_libs=$(m4_toupper($HTOP_[$1]_CONFIG_SCRIPT) --libs 2> /dev/null)
htop_config_script_cflags=$(m4_toupper($HTOP_[$1]_CONFIG_SCRIPT) --cflags 2> /dev/null)
else
htop_config_script_libs=$([$4] --libs 2> /dev/null)
htop_config_script_cflags=$([$4] --cflags 2> /dev/null)
fi
htop_script_success=no
htop_save_LDFLAGS="$LDFLAGS"
if test ! "x$htop_config_script" = x; then
LDFLAGS="$htop_config_script $LDFLAGS"
htop_save_CFLAGS="$CFLAGS"
if test ! "x$htop_config_script_libs" = x; then
LDFLAGS="$htop_config_script_libs $LDFLAGS"
CFLAGS="$htop_config_script_cflags $CFLAGS"
AC_CHECK_LIB([$1], [$2], [
AC_DEFINE([$3], 1, [The library is present.])
LIBS="$htop_config_script $LIBS "
LIBS="$htop_config_script_libs $LIBS "
htop_script_success=yes
], [])
LDFLAGS="$save_LDFLAGS"
], [
CFLAGS="$htop_save_CFLAGS"
])
LDFLAGS="$htop_save_LDFLAGS"
fi
if test "x$htop_script_success" = xno; then
[$5]
@ -173,17 +202,18 @@ m4_define([HTOP_CHECK_LIB],
], [$4])
])
AC_ARG_ENABLE(unicode, [AC_HELP_STRING([--enable-unicode], [enable Unicode support])], ,enable_unicode="yes")
AC_ARG_ENABLE(unicode, [AS_HELP_STRING([--enable-unicode], [enable Unicode support])], ,enable_unicode="yes")
if test "x$enable_unicode" = xyes; then
HTOP_CHECK_SCRIPT([ncursesw6], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw6-config",
HTOP_CHECK_SCRIPT([ncursesw], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw5-config",
HTOP_CHECK_SCRIPT([ncurses], [addnwstr], [HAVE_LIBNCURSESW], "ncurses5-config",
HTOP_CHECK_LIB([ncursesw6], [addnwstr], [HAVE_LIBNCURSESW],
HTOP_CHECK_LIB([ncursesw], [addnwstr], [HAVE_LIBNCURSESW],
HTOP_CHECK_LIB([ncurses], [addnwstr], [HAVE_LIBNCURSESW],
missing_libraries="$missing_libraries libncursesw"
AC_MSG_ERROR([You may want to use --disable-unicode or install libncursesw.])
))))))
HTOP_CHECK_SCRIPT([ncursesw], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw6-config",
HTOP_CHECK_SCRIPT([ncursesw], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw5-config",
HTOP_CHECK_SCRIPT([ncurses], [addnwstr], [HAVE_LIBNCURSESW], "ncurses5-config",
HTOP_CHECK_LIB([ncursesw6], [addnwstr], [HAVE_LIBNCURSESW],
HTOP_CHECK_LIB([ncursesw], [addnwstr], [HAVE_LIBNCURSESW],
HTOP_CHECK_LIB([ncurses], [addnwstr], [HAVE_LIBNCURSESW],
missing_libraries="$missing_libraries libncursesw"
AC_MSG_ERROR([You may want to use --disable-unicode or install libncursesw.])
)))))))
AC_CHECK_HEADERS([ncursesw/curses.h],[:],
[AC_CHECK_HEADERS([ncurses/ncurses.h],[:],
@ -211,7 +241,17 @@ if test "$my_htop_platform" = "openbsd"; then
AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"])
fi
AC_ARG_ENABLE(linux_affinity, [AC_HELP_STRING([--enable-linux-affinity], [enable Linux sched_setaffinity and sched_getaffinity for affinity support, disables hwloc])], ,enable_linux_affinity="yes")
if test "$my_htop_platform" = "solaris"; then
AC_CHECK_LIB([kstat], [kstat_open], [], [missing_libraries="$missing_libraries libkstat"])
AC_CHECK_LIB([proc], [Pgrab_error], [], [missing_libraries="$missing_libraries libproc"])
AC_CHECK_LIB([malloc], [free], [], [missing_libraries="$missing_libraries libmalloc"])
AC_CHECK_HEADERS([err.h],[:],[
missing_headers="$missing_headers err.h"
AC_MSG_ERROR([You appear to be on Solaris 10, or very early Solaris Express, which are currently unsupported.])
])
fi
AC_ARG_ENABLE(linux_affinity, [AS_HELP_STRING([--enable-linux-affinity], [enable Linux sched_setaffinity and sched_getaffinity for affinity support, disables hwloc])], ,enable_linux_affinity="yes")
if test "x$enable_linux_affinity" = xyes -a "x$cross_compiling" = xno; then
AC_MSG_CHECKING([for usable sched_setaffinity])
AC_RUN_IFELSE([
@ -232,13 +272,83 @@ if test "x$enable_linux_affinity" = xyes; then
AC_DEFINE(HAVE_LINUX_AFFINITY, 1, [Define if Linux sched_setaffinity and sched_getaffinity are to be used.])
fi
AC_ARG_ENABLE(hwloc, [AC_HELP_STRING([--enable-hwloc], [enable hwloc support for CPU affinity])],, enable_hwloc="no")
AC_ARG_ENABLE(hwloc, [AS_HELP_STRING([--enable-hwloc], [enable hwloc support for CPU affinity])],, enable_hwloc="no")
if test "x$enable_hwloc" = xyes
then
AC_CHECK_LIB([hwloc], [hwloc_get_proc_cpubind], [], [missing_libraries="$missing_libraries libhwloc"])
AC_CHECK_HEADERS([hwloc.h],[:], [missing_headers="$missing_headers $ac_header"])
fi
AC_ARG_ENABLE(setuid, [AS_HELP_STRING([--enable-setuid], [enable setuid support for platforms that need it])],, enable_setuid="no")
if test "x$enable_setuid" = xyes
then
AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.])
fi
AC_ARG_ENABLE(perfcounters, [AS_HELP_STRING([--enable-perfcounters], [enable hardware performance counters])],, enable_perfcounters="yes")
if test "x$enable_perfcounters" = "xyes" -a "$my_htop_platform" = "linux"
then
AC_DEFINE(HAVE_PERFCOUNTERS, 1, [Define if hardware performance counter support should be enabled.])
AC_CHECK_HEADERS([linux/perf_counter.h], [have_perf_counter=yes],
[have_perf_counter=no])
AC_CHECK_HEADERS([linux/perf_event.h], [have_perf_event=yes],
[have_perf_event=no])
if test "x${have_perf_counter}" = xno -a "x${have_perf_event}" = xno; then
os=`uname -s -r`
AC_MSG_FAILURE([
------------------------------------------------------------
Could not locate linux/perf_count.h or linux/perf_event.h.
Are performance counters supported on this machine?
Linux 2.6.31+ is required.
uname reports: ${os}
------------------------------------------------------------])
fi
# Check for hardware architecture
no_target=yes
AC_MSG_CHECKING([hardware])
hw=`uname -m`
case $hw in
x86_64 | i386 | i686 ) :
AC_MSG_RESULT([x86])
AC_DEFINE([TARGET_X86], [1], [Define to 1 if the target is x86.])
no_target=no
;;
unknown ) :
AC_MSG_RESULT([unknown])
AC_MSG_WARN([Could not detect architecture])
;;
* ) :
AC_MSG_RESULT([$hw])
;;
esac
if test x$no_target = xyes; then
AC_DEFINE([NOTARGET], [1], [Define to 1 when no specific target is supported.])
fi
fi
AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable linux delay accounting])],, enable_delayacct="no")
if test "x$enable_delayacct" = xyes
then
m4_ifdef([PKG_PROG_PKG_CONFIG], [
PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"])
PKG_CHECK_MODULES(LIBNL3GENL, libnl-genl-3.0, [], [missing_libraries="$missing_libraries libnl-genl-3"])
CFLAGS="$CFLAGS $LIBNL3_CFLAGS $LIBNL3GENL_CFLAGS"
LIBS="$LIBS $LIBNL3_LIBS $LIBNL3GENL_LIBS"
AC_DEFINE(HAVE_DELAYACCT, 1, [Define if delay accounting support should be enabled.])
], [
pkg_m4_absent=1
m4_warning([configure is generated without pkg.m4. 'make dist' target will be disabled.])
AC_MSG_ERROR([htop on Linux requires pkg-config for checking delayacct requirements. Please install pkg-config and run ./autogen.sh to rebuild the configure script.])
])
fi
# Bail out on errors.
# ----------------------------------------------------------------------
if test ! -z "$missing_libraries"; then
@ -254,9 +364,25 @@ AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-$year Hisham Muhammad", [Copyright messa
# ----------------------------------------------------------------------
AM_CONDITIONAL([HTOP_LINUX], [test "$my_htop_platform" = linux])
AM_CONDITIONAL([HTOP_FREEBSD], [test "$my_htop_platform" = freebsd])
AM_CONDITIONAL([HTOP_DRAGONFLYBSD], [test "$my_htop_platform" = dragonflybsd])
AM_CONDITIONAL([HTOP_OPENBSD], [test "$my_htop_platform" = openbsd])
AM_CONDITIONAL([HTOP_DARWIN], [test "$my_htop_platform" = darwin])
AM_CONDITIONAL([HTOP_SOLARIS], [test "$my_htop_platform" = solaris])
AM_CONDITIONAL([HTOP_UNSUPPORTED], [test "$my_htop_platform" = unsupported])
AC_SUBST(my_htop_platform)
AC_CONFIG_FILES([Makefile htop.1])
AC_OUTPUT
if test "$my_htop_platform" = "unsupported"
then
echo ""
echo "****************************************************************"
echo "WARNING! This platform is not currently supported by htop."
echo ""
echo "The code will build, but it will produce a dummy version of htop"
echo "which shows no processes, using the files from the unsupported/"
echo "directory. This is meant to be a skeleton, to be used as a"
echo "starting point if you are porting htop to a new platform."
echo "****************************************************************"
echo ""
fi

View File

@ -26,6 +26,7 @@ typedef struct DarwinProcess_ {
uint64_t utime;
uint64_t stime;
bool taskAccess;
} DarwinProcess;
}*/
@ -47,6 +48,7 @@ DarwinProcess* DarwinProcess_new(Settings* settings) {
this->utime = 0;
this->stime = 0;
this->taskAccess = true;
return this;
}
@ -301,6 +303,10 @@ void DarwinProcess_scanThreads(DarwinProcess *dp) {
Process* proc = (Process*) dp;
kern_return_t ret;
if (!dp->taskAccess) {
return;
}
if (proc->state == 'Z') {
return;
}
@ -308,6 +314,7 @@ void DarwinProcess_scanThreads(DarwinProcess *dp) {
task_t port;
ret = task_for_pid(mach_task_self(), proc->pid, &port);
if (ret != KERN_SUCCESS) {
dp->taskAccess = false;
return;
}
@ -315,6 +322,7 @@ void DarwinProcess_scanThreads(DarwinProcess *dp) {
mach_msg_type_number_t task_info_count = TASK_INFO_MAX;
ret = task_info(port, TASK_BASIC_INFO, (task_info_t) tinfo, &task_info_count);
if (ret != KERN_SUCCESS) {
dp->taskAccess = false;
return;
}
@ -322,6 +330,7 @@ void DarwinProcess_scanThreads(DarwinProcess *dp) {
mach_msg_type_number_t thread_count;
ret = task_threads(port, &thread_list, &thread_count);
if (ret != KERN_SUCCESS) {
dp->taskAccess = false;
mach_port_deallocate(mach_task_self(), port);
return;
}

View File

@ -19,6 +19,7 @@ typedef struct DarwinProcess_ {
uint64_t utime;
uint64_t stime;
bool taskAccess;
} DarwinProcess;

View File

@ -18,6 +18,8 @@ in the source distribution for its full text.
#include <sys/mman.h>
#include <utmpx.h>
#include <err.h>
#include <sys/sysctl.h>
#include <stdbool.h>
/*{
#include "ProcessList.h"
@ -38,19 +40,51 @@ typedef struct DarwinProcessList_ {
}*/
typedef struct kern {
short int version[3];
} kern_;
static void getKernelVersion(struct kern *k) {
static short int version_[3] = {0};
if (!version_[0]) {
// just in case it fails someday
version_[0] = version_[1] = version_[2] = -1;
char str[256] = {0};
size_t size = sizeof(str);
int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0);
if (ret == 0) sscanf(str, "%hd.%hd.%hd", &version_[0], &version_[1], &version_[2]);
}
memcpy(k->version, version_, sizeof(version_));
}
static int compareKernelVersion(short int major, short int minor, short int component) {
/*
compare the given os version with the one installed returns:
0 if equals the installed version
positive value if less than the installed version
negative value if more than the installed version
*/
struct kern k;
getKernelVersion(&k);
if ( k.version[0] != major) return k.version[0] - major;
if ( k.version[1] != minor) return k.version[1] - minor;
if ( k.version[2] != component) return k.version[2] - component;
return 0;
}
void ProcessList_getHostInfo(host_basic_info_data_t *p) {
mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT;
if(0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) {
CRT_fatalError("Unable to retrieve host info\n");
if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) {
CRT_fatalError("Unable to retrieve host info\n");
}
}
void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p) {
if(NULL != p && NULL != *p) {
if(0 != munmap(*p, vm_page_size)) {
CRT_fatalError("Unable to free old CPU load information\n");
}
if (NULL != p && NULL != *p) {
if (0 != munmap(*p, vm_page_size)) {
CRT_fatalError("Unable to free old CPU load information\n");
}
}
*p = NULL;
@ -61,7 +95,7 @@ unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p) {
unsigned cpu_count;
// TODO Improving the accuracy of the load counts woule help a lot.
if(0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t *)p, &info_size)) {
if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t *)p, &info_size)) {
CRT_fatalError("Unable to retrieve CPU info\n");
}
@ -69,10 +103,10 @@ unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p) {
}
void ProcessList_getVMStats(vm_statistics_t p) {
mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT;
mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT;
if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0)
CRT_fatalError("Unable to retrieve VM statistics\n");
if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0)
CRT_fatalError("Unable to retrieve VM statistics\n");
}
struct kinfo_proc *ProcessList_getKInfoProcs(size_t *count) {
@ -127,60 +161,65 @@ void ProcessList_delete(ProcessList* this) {
}
void ProcessList_goThroughEntries(ProcessList* super) {
DarwinProcessList *dpl = (DarwinProcessList *)super;
bool preExisting = true;
struct kinfo_proc *ps;
size_t count;
DarwinProcess *proc;
struct timeval tv;
DarwinProcessList *dpl = (DarwinProcessList *)super;
bool preExisting = true;
struct kinfo_proc *ps;
size_t count;
DarwinProcess *proc;
struct timeval tv;
gettimeofday(&tv, NULL); /* Start processing time */
gettimeofday(&tv, NULL); /* Start processing time */
/* Update the global data (CPU times and VM stats) */
ProcessList_freeCPULoadInfo(&dpl->prev_load);
dpl->prev_load = dpl->curr_load;
ProcessList_allocateCPULoadInfo(&dpl->curr_load);
ProcessList_getVMStats(&dpl->vm_stats);
/* Update the global data (CPU times and VM stats) */
ProcessList_freeCPULoadInfo(&dpl->prev_load);
dpl->prev_load = dpl->curr_load;
ProcessList_allocateCPULoadInfo(&dpl->curr_load);
ProcessList_getVMStats(&dpl->vm_stats);
/* Get the time difference */
dpl->global_diff = 0;
for(int i = 0; i < dpl->super.cpuCount; ++i) {
for(size_t j = 0; j < CPU_STATE_MAX; ++j) {
dpl->global_diff += dpl->curr_load[i].cpu_ticks[j] - dpl->prev_load[i].cpu_ticks[j];
}
}
/* Get the time difference */
dpl->global_diff = 0;
for(int i = 0; i < dpl->super.cpuCount; ++i) {
for(size_t j = 0; j < CPU_STATE_MAX; ++j) {
dpl->global_diff += dpl->curr_load[i].cpu_ticks[j] - dpl->prev_load[i].cpu_ticks[j];
}
}
/* Clear the thread counts */
super->kernelThreads = 0;
super->userlandThreads = 0;
super->totalTasks = 0;
super->runningTasks = 0;
/* Clear the thread counts */
super->kernelThreads = 0;
super->userlandThreads = 0;
super->totalTasks = 0;
super->runningTasks = 0;
/* We use kinfo_procs for initial data since :
*
* 1) They always succeed.
* 2) The contain the basic information.
*
* We attempt to fill-in additional information with libproc.
*/
ps = ProcessList_getKInfoProcs(&count);
/* We use kinfo_procs for initial data since :
*
* 1) They always succeed.
* 2) The contain the basic information.
*
* We attempt to fill-in additional information with libproc.
*/
ps = ProcessList_getKInfoProcs(&count);
for(size_t i = 0; i < count; ++i) {
proc = (DarwinProcess *)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, (Process_New)DarwinProcess_new);
for(size_t i = 0; i < count; ++i) {
proc = (DarwinProcess *)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, (Process_New)DarwinProcess_new);
DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], tv.tv_sec, preExisting);
DarwinProcess_setFromLibprocPidinfo(proc, dpl);
DarwinProcess_scanThreads(proc);
DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], tv.tv_sec, preExisting);
DarwinProcess_setFromLibprocPidinfo(proc, dpl);
super->totalTasks += 1;
// Disabled for High Sierra due to bug in macOS High Sierra
bool isScanThreadSupported = ! ( compareKernelVersion(17, 0, 0) >= 0 && compareKernelVersion(17, 5, 0) < 0);
if(!preExisting) {
proc->super.user = UsersTable_getRef(super->usersTable, proc->super.st_uid);
if (isScanThreadSupported){
DarwinProcess_scanThreads(proc);
}
ProcessList_add(super, &proc->super);
}
}
super->totalTasks += 1;
free(ps);
if (!preExisting) {
proc->super.user = UsersTable_getRef(super->usersTable, proc->super.st_uid);
ProcessList_add(super, &proc->super);
}
}
free(ps);
}

View File

@ -26,6 +26,7 @@ typedef struct DarwinProcessList_ {
} DarwinProcessList;
void ProcessList_getHostInfo(host_basic_info_data_t *p);
void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p);

View File

@ -25,15 +25,28 @@ in the source distribution for its full text.
#include "CPUMeter.h"
#include "BatteryMeter.h"
#include "DarwinProcess.h"
typedef enum DarwinProcessFields {
LAST_PROCESSFIELD = 100,
} DarwinProcessField;
}*/
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Main",
.columns = "PID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
SignalItem Platform_signals[] = {
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },
{ .name = " 1 SIGHUP", .number = 1 },
{ .name = " 2 SIGINT", .number = 2 },
@ -69,7 +82,7 @@ SignalItem Platform_signals[] = {
{ .name = "31 SIGUSR2", .number = 31 },
};
unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
@ -78,8 +91,8 @@ ProcessFieldData Process_fields[] = {
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SESN ", .description = "Process's session ID", .flags = 0, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
@ -97,7 +110,7 @@ ProcessFieldData Process_fields[] = {
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
[100] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
MeterClass* Platform_meterTypes[] = {
@ -166,7 +179,7 @@ ProcessPidColumn Process_pidColumns[] = {
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SESN" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
};
@ -280,11 +293,9 @@ char* Platform_getProcessEnv(pid_t pid) {
size_t size = endp - p;
env = xMalloc(size+2);
if (env) {
memcpy(env, p, size);
env[size] = 0;
env[size+1] = 0;
}
memcpy(env, p, size);
env[size] = 0;
env[size+1] = 0;
}
}
free(buf);

View File

@ -16,15 +16,22 @@ in the source distribution for its full text.
#include "BatteryMeter.h"
#include "DarwinProcess.h"
typedef enum DarwinProcessFields {
LAST_PROCESSFIELD = 100,
} DarwinProcessField;
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
extern ProcessField Platform_defaultFields[];
extern ScreenDefaults Platform_defaultScreens[];
extern SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern unsigned int Platform_numberOfSignals;
extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals;
extern ProcessFieldData Process_fields[];

26
dragonflybsd/Battery.c Normal file
View File

@ -0,0 +1,26 @@
/*
htop - dragonflybsd/Battery.c
(C) 2015 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "BatteryMeter.h"
#include <sys/sysctl.h>
void Battery_getData(double* level, ACPresence* isOnAC) {
int life;
size_t life_len = sizeof(life);
if (sysctlbyname("hw.acpi.battery.life", &life, &life_len, NULL, 0) == -1)
*level = -1;
else
*level = life;
int acline;
size_t acline_len = sizeof(acline);
if (sysctlbyname("hw.acpi.acline", &acline, &acline_len, NULL, 0) == -1)
*isOnAC = AC_ERROR;
else
*isOnAC = acline == 0 ? AC_ABSENT : AC_PRESENT;
}

15
dragonflybsd/Battery.h Normal file
View File

@ -0,0 +1,15 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Battery
#define HEADER_Battery
/*
htop - dragonflybsd/Battery.h
(C) 2015 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
void Battery_getData(double* level, ACPresence* isOnAC);
#endif

View File

@ -0,0 +1,35 @@
/*
htop - dragonflybsd/DragonFlyBSDCRT.c
(C) 2014 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h"
#include "CRT.h"
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#endif
void CRT_handleSIGSEGV(int sgn) {
(void) sgn;
CRT_done();
fprintf(stderr, "\n\nhtop " VERSION " aborting. Please report bug at http://hisham.hm/htop\n");
#ifdef HAVE_EXECINFO_H
size_t size = backtrace(backtraceArray, sizeof(backtraceArray) / sizeof(void *));
fprintf(stderr, "\n Please include in your report the following backtrace: \n");
backtrace_symbols_fd(backtraceArray, size, 2);
fprintf(stderr, "\nAdditionally, in order to make the above backtrace useful,");
fprintf(stderr, "\nplease also run the following command to generate a disassembly of your binary:");
fprintf(stderr, "\n\n objdump -d `which htop` > ~/htop.objdump");
fprintf(stderr, "\n\nand then attach the file ~/htop.objdump to your bug report.");
fprintf(stderr, "\n\nThank you for helping to improve htop!\n\n");
#else
fprintf(stderr, "\nPlease contact your DragonFlyBSD package maintainer!\n\n");
#endif
abort();
}

View File

@ -0,0 +1,19 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_DragonFlyBSDCRT
#define HEADER_DragonFlyBSDCRT
/*
htop - dragonflybsd/DragonFlyBSDCRT.h
(C) 2014 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#ifdef HAVE_EXECINFO_H
#endif
void CRT_handleSIGSEGV(int sgn);
#endif

View File

@ -0,0 +1,166 @@
/*
htop - dragonflybsd/DragonFlyBSDProcess.c
(C) 2015 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Process.h"
#include "ProcessList.h"
#include "DragonFlyBSDProcess.h"
#include "Platform.h"
#include "CRT.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
/*{
typedef enum DragonFlyBSDProcessFields {
// Add platform-specific fields here, with ids >= 100
JID = 100,
JAIL = 101,
LAST_PROCESSFIELD = 102,
} DragonFlyBSDProcessField;
typedef struct DragonFlyBSDProcess_ {
Process super;
int kernel;
int jid;
char* jname;
} DragonFlyBSDProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->kernel == 1)
#endif
#ifndef Process_isUserlandThread
//#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
#define Process_isUserlandThread(_process) (_process->nlwp > 1)
#endif
}*/
ProcessClass DragonFlyBSDProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = DragonFlyBSDProcess_compare
},
.writeField = (Process_WriteField) DragonFlyBSDProcess_writeField,
};
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping (<20s), I Idle, Q Queued for Run, R running, D disk, Z zombie, T traced, W paging, B Blocked, A AskedPage, C Core, J Jailed)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
[JID] = { .name = "JID", .title = " JID ", .description = "Jail prison ID", .flags = 0, },
[JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
ProcessPidColumn Process_pidColumns[] = {
{ .id = JID, .label = "JID" },
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
};
DragonFlyBSDProcess* DragonFlyBSDProcess_new(Settings* settings) {
DragonFlyBSDProcess* this = xCalloc(1, sizeof(DragonFlyBSDProcess));
Object_setClass(this, Class(DragonFlyBSDProcess));
Process_init(&this->super, settings);
return this;
}
void Process_delete(Object* cast) {
DragonFlyBSDProcess* this = (DragonFlyBSDProcess*) cast;
Process_done((Process*)cast);
free(this->jname);
free(this);
}
void DragonFlyBSDProcess_writeField(Process* this, RichString* str, ProcessField field) {
DragonFlyBSDProcess* fp = (DragonFlyBSDProcess*) this;
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1;
switch ((int) field) {
// add Platform-specific fields here
case PID: xSnprintf(buffer, n, Process_pidFormat, (fp->kernel ? -1 : this->pid)); break;
case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break;
case JAIL:{
xSnprintf(buffer, n, "%-11s ", fp->jname); break;
if (buffer[11] != '\0') {
buffer[11] = ' ';
buffer[12] = '\0';
}
break;
}
default:
Process_writeField(this, str, field);
return;
}
RichString_append(str, attr, buffer);
}
long DragonFlyBSDProcess_compare(const void* v1, const void* v2) {
DragonFlyBSDProcess *p1, *p2;
Settings *settings = ((Process*)v1)->settings;
if (settings->ss->direction == 1) {
p1 = (DragonFlyBSDProcess*)v1;
p2 = (DragonFlyBSDProcess*)v2;
} else {
p2 = (DragonFlyBSDProcess*)v1;
p1 = (DragonFlyBSDProcess*)v2;
}
switch ((int) settings->ss->sortKey) {
// add Platform-specific fields here
case JID:
return (p1->jid - p2->jid);
case JAIL:
return strcmp(p1->jname ? p1->jname : "", p2->jname ? p2->jname : "");
default:
return Process_compare(v1, v2);
}
}
bool Process_isThread(Process* this) {
DragonFlyBSDProcess* fp = (DragonFlyBSDProcess*) this;
if (fp->kernel == 1 )
return 1;
else
return (Process_isUserlandThread(this));
}

View File

@ -0,0 +1,56 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_DragonFlyBSDProcess
#define HEADER_DragonFlyBSDProcess
/*
htop - dragonflybsd/DragonFlyBSDProcess.h
(C) 2015 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
typedef enum DragonFlyBSDProcessFields {
// Add platform-specific fields here, with ids >= 100
JID = 100,
JAIL = 101,
LAST_PROCESSFIELD = 102,
} DragonFlyBSDProcessField;
typedef struct DragonFlyBSDProcess_ {
Process super;
int kernel;
int jid;
char* jname;
} DragonFlyBSDProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->kernel == 1)
#endif
#ifndef Process_isUserlandThread
//#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
#define Process_isUserlandThread(_process) (_process->nlwp > 1)
#endif
extern ProcessClass DragonFlyBSDProcess_class;
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
DragonFlyBSDProcess* DragonFlyBSDProcess_new(Settings* settings);
void Process_delete(Object* cast);
void DragonFlyBSDProcess_writeField(Process* this, RichString* str, ProcessField field);
long DragonFlyBSDProcess_compare(const void* v1, const void* v2);
bool Process_isThread(Process* this);
#endif

View File

@ -0,0 +1,578 @@
/*
htop - DragonFlyBSDProcessList.c
(C) 2014 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "ProcessList.h"
#include "DragonFlyBSDProcessList.h"
#include "DragonFlyBSDProcess.h"
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <err.h>
#include <fcntl.h>
#include <limits.h>
#include <string.h>
#include <sys/param.h>
/*{
#include <kvm.h>
#include <sys/param.h>
#include <osreldate.h>
#include <sys/kinfo.h>
#include <kinfo.h>
#include <sys/jail.h>
#include <sys/uio.h>
#include <sys/resource.h>
#include "Hashtable.h"
#include "DragonFlyBSDProcess.h"
#define JAIL_ERRMSGLEN 1024
char jail_errmsg[JAIL_ERRMSGLEN];
typedef struct CPUData_ {
double userPercent;
double nicePercent;
double systemPercent;
double irqPercent;
double idlePercent;
double systemAllPercent;
} CPUData;
typedef struct DragonFlyBSDProcessList_ {
ProcessList super;
kvm_t* kd;
unsigned long long int memWire;
unsigned long long int memActive;
unsigned long long int memInactive;
unsigned long long int memFree;
CPUData* cpus;
unsigned long *cp_time_o;
unsigned long *cp_time_n;
unsigned long *cp_times_o;
unsigned long *cp_times_n;
Hashtable *jails;
} DragonFlyBSDProcessList;
}*/
#define _UNUSED_ __attribute__((unused))
static int MIB_hw_physmem[2];
static int MIB_vm_stats_vm_v_page_count[4];
static int pageSize;
static int pageSizeKb;
static int MIB_vm_stats_vm_v_wire_count[4];
static int MIB_vm_stats_vm_v_active_count[4];
static int MIB_vm_stats_vm_v_cache_count[4];
static int MIB_vm_stats_vm_v_inactive_count[4];
static int MIB_vm_stats_vm_v_free_count[4];
static int MIB_vfs_bufspace[2];
static int MIB_kern_cp_time[2];
static int MIB_kern_cp_times[2];
static int kernelFScale;
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
size_t len;
char errbuf[_POSIX2_LINE_MAX];
DragonFlyBSDProcessList* dfpl = xCalloc(1, sizeof(DragonFlyBSDProcessList));
ProcessList* pl = (ProcessList*) dfpl;
ProcessList_init(pl, Class(DragonFlyBSDProcess), usersTable, pidWhiteList, userId);
// physical memory in system: hw.physmem
// physical page size: hw.pagesize
// usable pagesize : vm.stats.vm.v_page_size
len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len);
len = sizeof(pageSize);
if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1) {
pageSize = PAGE_SIZE;
pageSizeKb = PAGE_SIZE_KB;
} else {
pageSizeKb = pageSize / ONE_K;
}
// usable page count vm.stats.vm.v_page_count
// actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size
len = 4; sysctlnametomib("vm.stats.vm.v_page_count", MIB_vm_stats_vm_v_page_count, &len);
len = 4; sysctlnametomib("vm.stats.vm.v_wire_count", MIB_vm_stats_vm_v_wire_count, &len);
len = 4; sysctlnametomib("vm.stats.vm.v_active_count", MIB_vm_stats_vm_v_active_count, &len);
len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", MIB_vm_stats_vm_v_cache_count, &len);
len = 4; sysctlnametomib("vm.stats.vm.v_inactive_count", MIB_vm_stats_vm_v_inactive_count, &len);
len = 4; sysctlnametomib("vm.stats.vm.v_free_count", MIB_vm_stats_vm_v_free_count, &len);
len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len);
int cpus = 1;
len = sizeof(cpus);
if (sysctlbyname("hw.ncpu", &cpus, &len, NULL, 0) != 0) {
cpus = 1;
}
size_t sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES;
len = 2; sysctlnametomib("kern.cp_time", MIB_kern_cp_time, &len);
dfpl->cp_time_o = xCalloc(cpus, sizeof_cp_time_array);
dfpl->cp_time_n = xCalloc(cpus, sizeof_cp_time_array);
len = sizeof_cp_time_array;
// fetch initial single (or average) CPU clicks from kernel
sysctl(MIB_kern_cp_time, 2, dfpl->cp_time_o, &len, NULL, 0);
// on smp box, fetch rest of initial CPU's clicks
if (cpus > 1) {
len = 2; sysctlnametomib("kern.cp_times", MIB_kern_cp_times, &len);
dfpl->cp_times_o = xCalloc(cpus, sizeof_cp_time_array);
dfpl->cp_times_n = xCalloc(cpus, sizeof_cp_time_array);
len = cpus * sizeof_cp_time_array;
sysctl(MIB_kern_cp_times, 2, dfpl->cp_times_o, &len, NULL, 0);
}
pl->cpuCount = MAX(cpus, 1);
if (cpus == 1 ) {
dfpl->cpus = xRealloc(dfpl->cpus, sizeof(CPUData));
} else {
// on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well)
dfpl->cpus = xRealloc(dfpl->cpus, (pl->cpuCount + 1) * sizeof(CPUData));
}
len = sizeof(kernelFScale);
if (sysctlbyname("kern.fscale", &kernelFScale, &len, NULL, 0) == -1) {
//sane default for kernel provided CPU percentage scaling, at least on x86 machines, in case this sysctl call failed
kernelFScale = 2048;
}
dfpl->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf);
if (dfpl->kd == NULL) {
errx(1, "kvm_open: %s", errbuf);
}
return pl;
}
void ProcessList_delete(ProcessList* this) {
const DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) this;
if (dfpl->kd) kvm_close(dfpl->kd);
if (dfpl->jails) {
Hashtable_delete(dfpl->jails);
}
free(dfpl->cp_time_o);
free(dfpl->cp_time_n);
free(dfpl->cp_times_o);
free(dfpl->cp_times_n);
free(dfpl->cpus);
ProcessList_done(this);
free(this);
}
static inline void DragonFlyBSDProcessList_scanCPUTime(ProcessList* pl) {
const DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) pl;
int cpus = pl->cpuCount; // actual CPU count
int maxcpu = cpus; // max iteration (in case we have average + smp)
int cp_times_offset;
assert(cpus > 0);
size_t sizeof_cp_time_array;
unsigned long *cp_time_n; // old clicks state
unsigned long *cp_time_o; // current clicks state
unsigned long cp_time_d[CPUSTATES];
double cp_time_p[CPUSTATES];
// get averages or single CPU clicks
sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES;
sysctl(MIB_kern_cp_time, 2, dfpl->cp_time_n, &sizeof_cp_time_array, NULL, 0);
// get rest of CPUs
if (cpus > 1) {
// on smp systems DragonFlyBSD kernel concats all CPU states into one long array in
// kern.cp_times sysctl OID
// we store averages in dfpl->cpus[0], and actual cores after that
maxcpu = cpus + 1;
sizeof_cp_time_array = cpus * sizeof(unsigned long) * CPUSTATES;
sysctl(MIB_kern_cp_times, 2, dfpl->cp_times_n, &sizeof_cp_time_array, NULL, 0);
}
for (int i = 0; i < maxcpu; i++) {
if (cpus == 1) {
// single CPU box
cp_time_n = dfpl->cp_time_n;
cp_time_o = dfpl->cp_time_o;
} else {
if (i == 0 ) {
// average
cp_time_n = dfpl->cp_time_n;
cp_time_o = dfpl->cp_time_o;
} else {
// specific smp cores
cp_times_offset = i - 1;
cp_time_n = dfpl->cp_times_n + (cp_times_offset * CPUSTATES);
cp_time_o = dfpl->cp_times_o + (cp_times_offset * CPUSTATES);
}
}
// diff old vs new
unsigned long long total_o = 0;
unsigned long long total_n = 0;
unsigned long long total_d = 0;
for (int s = 0; s < CPUSTATES; s++) {
cp_time_d[s] = cp_time_n[s] - cp_time_o[s];
total_o += cp_time_o[s];
total_n += cp_time_n[s];
}
// totals
total_d = total_n - total_o;
if (total_d < 1 ) total_d = 1;
// save current state as old and calc percentages
for (int s = 0; s < CPUSTATES; ++s) {
cp_time_o[s] = cp_time_n[s];
cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100;
}
CPUData* cpuData = &(dfpl->cpus[i]);
cpuData->userPercent = cp_time_p[CP_USER];
cpuData->nicePercent = cp_time_p[CP_NICE];
cpuData->systemPercent = cp_time_p[CP_SYS];
cpuData->irqPercent = cp_time_p[CP_INTR];
cpuData->systemAllPercent = cp_time_p[CP_SYS] + cp_time_p[CP_INTR];
// this one is not really used, but we store it anyway
cpuData->idlePercent = cp_time_p[CP_IDLE];
}
}
static inline void DragonFlyBSDProcessList_scanMemoryInfo(ProcessList* pl) {
DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) pl;
// @etosan:
// memory counter relationships seem to be these:
// total = active + wired + inactive + cache + free
// htop_used (unavail to anybody) = active + wired
// htop_cache (for cache meter) = buffers + cache
// user_free (avail to procs) = buffers + inactive + cache + free
size_t len = sizeof(pl->totalMem);
//disabled for now, as it is always smaller than phycal amount of memory...
//...to avoid "where is my memory?" questions
//sysctl(MIB_vm_stats_vm_v_page_count, 4, &(pl->totalMem), &len, NULL, 0);
//pl->totalMem *= pageSizeKb;
sysctl(MIB_hw_physmem, 2, &(pl->totalMem), &len, NULL, 0);
pl->totalMem /= 1024;
sysctl(MIB_vm_stats_vm_v_active_count, 4, &(dfpl->memActive), &len, NULL, 0);
dfpl->memActive *= pageSizeKb;
sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(dfpl->memWire), &len, NULL, 0);
dfpl->memWire *= pageSizeKb;
sysctl(MIB_vfs_bufspace, 2, &(pl->buffersMem), &len, NULL, 0);
pl->buffersMem /= 1024;
sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(pl->cachedMem), &len, NULL, 0);
pl->cachedMem *= pageSizeKb;
pl->usedMem = dfpl->memActive + dfpl->memWire;
//currently unused, same as with arc, custom meter perhaps
//sysctl(MIB_vm_stats_vm_v_inactive_count, 4, &(dfpl->memInactive), &len, NULL, 0);
//sysctl(MIB_vm_stats_vm_v_free_count, 4, &(dfpl->memFree), &len, NULL, 0);
//pl->freeMem = dfpl->memInactive + dfpl->memFree;
//pl->freeMem *= pageSizeKb;
struct kvm_swap swap[16];
int nswap = kvm_getswapinfo(dfpl->kd, swap, sizeof(swap)/sizeof(swap[0]), 0);
pl->totalSwap = 0;
pl->usedSwap = 0;
for (int i = 0; i < nswap; i++) {
pl->totalSwap += swap[i].ksw_total;
pl->usedSwap += swap[i].ksw_used;
}
pl->totalSwap *= pageSizeKb;
pl->usedSwap *= pageSizeKb;
pl->sharedMem = 0; // currently unused
}
char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd) {
char** argv = kvm_getargv(kd, kproc, 0);
if (!argv) {
return xStrdup(kproc->kp_comm);
}
int len = 0;
for (int i = 0; argv[i]; i++) {
len += strlen(argv[i]) + 1;
}
char* comm = xMalloc(len);
char* at = comm;
*basenameEnd = 0;
for (int i = 0; argv[i]; i++) {
at = stpcpy(at, argv[i]);
if (!*basenameEnd) {
*basenameEnd = at - comm;
}
*at = ' ';
at++;
}
at--;
*at = '\0';
return comm;
}
static inline void DragonFlyBSDProcessList_scanJails(DragonFlyBSDProcessList* dfpl) {
size_t len;
char *jls; /* Jail list */
char *curpos;
char *nextpos;
if (sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1) {
fprintf(stderr, "initial sysctlbyname / jail.list failed\n");
exit(3);
}
retry:
if (len == 0)
return;
jls = xMalloc(len);
if (jls == NULL) {
fprintf(stderr, "xMalloc failed\n");
exit(4);
}
if (sysctlbyname("jail.list", jls, &len, NULL, 0) == -1) {
if (errno == ENOMEM) {
free(jls);
goto retry;
}
fprintf(stderr, "sysctlbyname / jail.list failed\n");
exit(5);
}
if (dfpl->jails) {
Hashtable_delete(dfpl->jails);
}
dfpl->jails = Hashtable_new(20, true);
curpos = jls;
while (curpos) {
int jailid;
char *str_hostname;
nextpos = strchr(curpos, '\n');
if (nextpos)
*nextpos++ = 0;
jailid = atoi(strtok(curpos, " "));
str_hostname = strtok(NULL, " ");
char *jname = (char *) (Hashtable_get(dfpl->jails, jailid));
if (jname == NULL) {
jname = xStrdup(str_hostname);
Hashtable_put(dfpl->jails, jailid, jname);
}
curpos = nextpos;
}
free(jls);
}
char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid) {
char* hostname;
char* jname;
if (jailid != 0 && dfpl->jails && (hostname = (char *)Hashtable_get(dfpl->jails, jailid))) {
jname = xStrdup(hostname);
} else {
jname = xStrdup("-");
}
return jname;
}
void ProcessList_goThroughEntries(ProcessList* this) {
DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) this;
Settings* settings = this->settings;
bool hideKernelThreads = settings->hideKernelThreads;
bool hideUserlandThreads = settings->hideUserlandThreads;
DragonFlyBSDProcessList_scanMemoryInfo(this);
DragonFlyBSDProcessList_scanCPUTime(this);
DragonFlyBSDProcessList_scanJails(dfpl);
int count = 0;
// TODO Kernel Threads seem to be skipped, need to figure out the correct flag
struct kinfo_proc* kprocs = kvm_getprocs(dfpl->kd, KERN_PROC_ALL | (!hideUserlandThreads ? KERN_PROC_FLAG_LWP : 0), 0, &count);
for (int i = 0; i < count; i++) {
struct kinfo_proc* kproc = &kprocs[i];
bool preExisting = false;
bool _UNUSED_ isIdleProcess = false;
// note: dragonflybsd kernel processes all have the same pid, so we misuse the kernel thread address to give them a unique identifier
Process* proc = ProcessList_getProcess(this, kproc->kp_ktaddr ? (pid_t)kproc->kp_ktaddr : kproc->kp_pid, &preExisting, (Process_New) DragonFlyBSDProcess_new);
DragonFlyBSDProcess* dfp = (DragonFlyBSDProcess*) proc;
proc->show = ! ((hideKernelThreads && Process_isKernelThread(dfp)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
if (!preExisting) {
dfp->jid = kproc->kp_jailid;
if (kproc->kp_ktaddr && kproc->kp_flags & P_SYSTEM) {
// dfb kernel threads all have the same pid, so we misuse the kernel thread address to give them a unique identifier
proc->pid = (pid_t)kproc->kp_ktaddr;
dfp->kernel = 1;
} else {
proc->pid = kproc->kp_pid; // process ID
dfp->kernel = 0;
}
proc->ppid = kproc->kp_ppid; // parent process id
proc->tpgid = kproc->kp_tpgid; // tty process group id
//proc->tgid = kproc->kp_lwp.kl_tid; // thread group id
proc->tgid = kproc->kp_pid; // thread group id
proc->pgrp = kproc->kp_pgid; // process group id
proc->session = kproc->kp_sid;
proc->tty_nr = kproc->kp_tdev; // control terminal device number
proc->st_uid = kproc->kp_uid; // user ID
proc->processor = kproc->kp_lwp.kl_origcpu;
proc->starttime_ctime = kproc->kp_start.tv_sec;
proc->user = UsersTable_getRef(this->usersTable, proc->st_uid);
ProcessList_add((ProcessList*)this, proc);
proc->comm = DragonFlyBSDProcessList_readProcessName(dfpl->kd, kproc, &proc->basenameOffset);
dfp->jname = DragonFlyBSDProcessList_readJailName(dfpl, kproc->kp_jailid);
} else {
proc->processor = kproc->kp_lwp.kl_cpuid;
if(dfp->jid != kproc->kp_jailid) { // process can enter jail anytime
dfp->jid = kproc->kp_jailid;
free(dfp->jname);
dfp->jname = DragonFlyBSDProcessList_readJailName(dfpl, kproc->kp_jailid);
}
if (proc->ppid != kproc->kp_ppid) { // if there are reapers in the system, process can get reparented anytime
proc->ppid = kproc->kp_ppid;
}
if(proc->st_uid != kproc->kp_uid) { // some processes change users (eg. to lower privs)
proc->st_uid = kproc->kp_uid;
proc->user = UsersTable_getRef(this->usersTable, proc->st_uid);
}
if (settings->updateProcessNames) {
free(proc->comm);
proc->comm = DragonFlyBSDProcessList_readProcessName(dfpl->kd, kproc, &proc->basenameOffset);
}
}
proc->m_size = kproc->kp_vm_map_size / 1024 / pageSizeKb;
proc->m_resident = kproc->kp_vm_rssize;
proc->percent_mem = (proc->m_resident * PAGE_SIZE_KB) / (double)(this->totalMem) * 100.0;
proc->nlwp = kproc->kp_nthreads; // number of lwp thread
proc->time = (kproc->kp_swtime + 5000) / 10000;
proc->percent_cpu = 100.0 * ((double)kproc->kp_lwp.kl_pctcpu / (double)kernelFScale);
proc->percent_mem = 100.0 * (proc->m_resident * PAGE_SIZE_KB) / (double)(this->totalMem);
if (proc->percent_cpu > 0.1) {
// system idle process should own all CPU time left regardless of CPU count
if ( strcmp("idle", kproc->kp_comm) == 0 ) {
isIdleProcess = true;
}
}
if (kproc->kp_lwp.kl_pid != -1)
proc->priority = kproc->kp_lwp.kl_prio;
else
proc->priority = -kproc->kp_lwp.kl_tdprio;
switch(kproc->kp_lwp.kl_rtprio.type) {
case RTP_PRIO_REALTIME:
proc->nice = PRIO_MIN - 1 - RTP_PRIO_MAX + kproc->kp_lwp.kl_rtprio.prio;
break;
case RTP_PRIO_IDLE:
proc->nice = PRIO_MAX + 1 + kproc->kp_lwp.kl_rtprio.prio;
break;
case RTP_PRIO_THREAD:
proc->nice = PRIO_MIN - 1 - RTP_PRIO_MAX - kproc->kp_lwp.kl_rtprio.prio;
break;
default:
proc->nice = kproc->kp_nice;
break;
}
// would be nice if we could store multiple states in proc->state (as enum) and have writeField render them
switch (kproc->kp_stat) {
case SIDL: proc->state = 'I'; isIdleProcess = true; break;
case SACTIVE:
switch (kproc->kp_lwp.kl_stat) {
case LSSLEEP:
if (kproc->kp_lwp.kl_flags & LWP_SINTR) // interruptable wait short/long
if (kproc->kp_lwp.kl_slptime >= MAXSLP) {
proc->state = 'I';
isIdleProcess = true;
} else {
proc->state = 'S';
}
else if (kproc->kp_lwp.kl_tdflags & TDF_SINTR) // interruptable lwkt wait
proc->state = 'S';
else if (kproc->kp_paddr) // uninterruptable wait
proc->state = 'D';
else // uninterruptable lwkt wait
proc->state = 'B';
break;
case LSRUN:
if (kproc->kp_lwp.kl_stat == LSRUN) {
if (!(kproc->kp_lwp.kl_tdflags & (TDF_RUNNING | TDF_RUNQ)))
proc->state = 'Q';
else
proc->state = 'R';
}
break;
case LSSTOP:
proc->state = 'T';
break;
default:
proc->state = 'A';
break;
}
break;
case SSTOP: proc->state = 'T'; break;
case SZOMB: proc->state = 'Z'; break;
case SCORE: proc->state = 'C'; break;
default: proc->state = '?';
}
if (kproc->kp_flags & P_SWAPPEDOUT) {
proc->state = 'W';
}
if (kproc->kp_flags & P_TRACED) {
proc->state = 'T';
}
if (kproc->kp_flags & P_JAILED) {
proc->state = 'J';
}
if (Process_isKernelThread(dfp)) {
this->kernelThreads++;
}
this->totalTasks++;
if (proc->state == 'R')
this->runningTasks++;
proc->updated = true;
}
}

View File

@ -0,0 +1,73 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_DragonFlyBSDProcessList
#define HEADER_DragonFlyBSDProcessList
/*
htop - DragonFlyBSDProcessList.h
(C) 2014 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include <kvm.h>
#include <sys/param.h>
#include <osreldate.h>
#include <sys/kinfo.h>
#include <kinfo.h>
#include <sys/jail.h>
#include <sys/uio.h>
#include <sys/resource.h>
#include "Hashtable.h"
#include "DragonFlyBSDProcess.h"
#define JAIL_ERRMSGLEN 1024
char jail_errmsg[JAIL_ERRMSGLEN];
typedef struct CPUData_ {
double userPercent;
double nicePercent;
double systemPercent;
double irqPercent;
double idlePercent;
double systemAllPercent;
} CPUData;
typedef struct DragonFlyBSDProcessList_ {
ProcessList super;
kvm_t* kd;
unsigned long long int memWire;
unsigned long long int memActive;
unsigned long long int memInactive;
unsigned long long int memFree;
CPUData* cpus;
unsigned long *cp_time_o;
unsigned long *cp_time_n;
unsigned long *cp_times_o;
unsigned long *cp_times_n;
Hashtable *jails;
} DragonFlyBSDProcessList;
#define _UNUSED_ __attribute__((unused))
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
void ProcessList_delete(ProcessList* this);
char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd);
char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid);
void ProcessList_goThroughEntries(ProcessList* this);
#endif

218
dragonflybsd/Platform.c Normal file
View File

@ -0,0 +1,218 @@
/*
htop - dragonflybsd/Platform.c
(C) 2014 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Platform.h"
#include "Meter.h"
#include "CPUMeter.h"
#include "MemoryMeter.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "UptimeMeter.h"
#include "ClockMeter.h"
#include "HostnameMeter.h"
#include "DragonFlyBSDProcess.h"
#include "DragonFlyBSDProcessList.h"
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <vm/vm_param.h>
#include <time.h>
#include <math.h>
/*{
#include "Action.h"
#include "BatteryMeter.h"
#include "SignalsPanel.h"
extern ProcessFieldData Process_fields[];
}*/
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Main",
.columns = "PID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
int Platform_numberOfFields = LAST_PROCESSFIELD;
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },
{ .name = " 1 SIGHUP", .number = 1 },
{ .name = " 2 SIGINT", .number = 2 },
{ .name = " 3 SIGQUIT", .number = 3 },
{ .name = " 4 SIGILL", .number = 4 },
{ .name = " 5 SIGTRAP", .number = 5 },
{ .name = " 6 SIGABRT", .number = 6 },
{ .name = " 7 SIGEMT", .number = 7 },
{ .name = " 8 SIGFPE", .number = 8 },
{ .name = " 9 SIGKILL", .number = 9 },
{ .name = "10 SIGBUS", .number = 10 },
{ .name = "11 SIGSEGV", .number = 11 },
{ .name = "12 SIGSYS", .number = 12 },
{ .name = "13 SIGPIPE", .number = 13 },
{ .name = "14 SIGALRM", .number = 14 },
{ .name = "15 SIGTERM", .number = 15 },
{ .name = "16 SIGURG", .number = 16 },
{ .name = "17 SIGSTOP", .number = 17 },
{ .name = "18 SIGTSTP", .number = 18 },
{ .name = "19 SIGCONT", .number = 19 },
{ .name = "20 SIGCHLD", .number = 20 },
{ .name = "21 SIGTTIN", .number = 21 },
{ .name = "22 SIGTTOU", .number = 22 },
{ .name = "23 SIGIO", .number = 23 },
{ .name = "24 SIGXCPU", .number = 24 },
{ .name = "25 SIGXFSZ", .number = 25 },
{ .name = "26 SIGVTALRM", .number = 26 },
{ .name = "27 SIGPROF", .number = 27 },
{ .name = "28 SIGWINCH", .number = 28 },
{ .name = "29 SIGINFO", .number = 29 },
{ .name = "30 SIGUSR1", .number = 30 },
{ .name = "31 SIGUSR2", .number = 31 },
{ .name = "32 SIGTHR", .number = 32 },
{ .name = "33 SIGLIBRT", .number = 33 },
};
const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
void Platform_setBindings(Htop_Action* keys) {
(void) keys;
}
MeterClass* Platform_meterTypes[] = {
&CPUMeter_class,
&ClockMeter_class,
&LoadAverageMeter_class,
&LoadMeter_class,
&MemoryMeter_class,
&SwapMeter_class,
&TasksMeter_class,
&UptimeMeter_class,
&BatteryMeter_class,
&HostnameMeter_class,
&AllCPUsMeter_class,
&AllCPUs2Meter_class,
&LeftCPUsMeter_class,
&RightCPUsMeter_class,
&LeftCPUs2Meter_class,
&RightCPUs2Meter_class,
&BlankMeter_class,
NULL
};
int Platform_getUptime() {
struct timeval bootTime, currTime;
int mib[2] = { CTL_KERN, KERN_BOOTTIME };
size_t size = sizeof(bootTime);
int err = sysctl(mib, 2, &bootTime, &size, NULL, 0);
if (err) {
return -1;
}
gettimeofday(&currTime, NULL);
return (int) difftime(currTime.tv_sec, bootTime.tv_sec);
}
void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
struct loadavg loadAverage;
int mib[2] = { CTL_VM, VM_LOADAVG };
size_t size = sizeof(loadAverage);
int err = sysctl(mib, 2, &loadAverage, &size, NULL, 0);
if (err) {
*one = 0;
*five = 0;
*fifteen = 0;
return;
}
*one = (double) loadAverage.ldavg[0] / loadAverage.fscale;
*five = (double) loadAverage.ldavg[1] / loadAverage.fscale;
*fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale;
}
int Platform_getMaxPid() {
int maxPid;
size_t size = sizeof(maxPid);
int err = sysctlbyname("kern.pid_max", &maxPid, &size, NULL, 0);
if (err) {
return 999999;
}
return maxPid;
}
double Platform_setCPUValues(Meter* this, int cpu) {
DragonFlyBSDProcessList* fpl = (DragonFlyBSDProcessList*) this->pl;
int cpus = this->pl->cpuCount;
CPUData* cpuData;
if (cpus == 1) {
// single CPU box has everything in fpl->cpus[0]
cpuData = &(fpl->cpus[0]);
} else {
cpuData = &(fpl->cpus[cpu]);
}
double percent;
double* v = this->values;
v[CPU_METER_NICE] = cpuData->nicePercent;
v[CPU_METER_NORMAL] = cpuData->userPercent;
if (this->pl->settings->detailedCPUTime) {
v[CPU_METER_KERNEL] = cpuData->systemPercent;
v[CPU_METER_IRQ] = cpuData->irqPercent;
Meter_setItems(this, 4);
percent = v[0]+v[1]+v[2]+v[3];
} else {
v[2] = cpuData->systemAllPercent;
Meter_setItems(this, 3);
percent = v[0]+v[1]+v[2];
}
percent = CLAMP(percent, 0.0, 100.0);
if (isnan(percent)) percent = 0.0;
return percent;
}
void Platform_setMemoryValues(Meter* this) {
// TODO
ProcessList* pl = (ProcessList*) this->pl;
this->total = pl->totalMem;
this->values[0] = pl->usedMem;
this->values[1] = pl->buffersMem;
this->values[2] = pl->cachedMem;
}
void Platform_setSwapValues(Meter* this) {
ProcessList* pl = (ProcessList*) this->pl;
this->total = pl->totalSwap;
this->values[0] = pl->usedSwap;
}
void Platform_setTasksValues(Meter* this) {
// TODO
(void)this; // prevent unused warning
}
char* Platform_getProcessEnv(pid_t pid) {
// TODO
(void)pid; // prevent unused warning
return NULL;
}

54
dragonflybsd/Platform.h Normal file
View File

@ -0,0 +1,54 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Platform
#define HEADER_Platform
/*
htop - dragonflybsd/Platform.h
(C) 2014 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Action.h"
#include "BatteryMeter.h"
#include "SignalsPanel.h"
extern ProcessFieldData Process_fields[];
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
extern ScreenDefaults Platform_defaultScreens[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern int Platform_numberOfFields;
extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals;
void Platform_setBindings(Htop_Action* keys);
extern MeterClass* Platform_meterTypes[];
int Platform_getUptime();
void Platform_getLoadAverage(double* one, double* five, double* fifteen);
int Platform_getMaxPid();
double Platform_setCPUValues(Meter* this, int cpu);
void Platform_setMemoryValues(Meter* this);
void Platform_setSwapValues(Meter* this);
void Platform_setTasksValues(Meter* this);
char* Platform_getProcessEnv(pid_t pid);
#endif

View File

@ -61,8 +61,8 @@ ProcessFieldData Process_fields[] = {
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SESN ", .description = "Process's session ID", .flags = 0, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
@ -92,7 +92,7 @@ ProcessPidColumn Process_pidColumns[] = {
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SESN" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
};
@ -117,9 +117,9 @@ void FreeBSDProcess_writeField(Process* this, RichString* str, ProcessField fiel
int n = sizeof(buffer) - 1;
switch ((int) field) {
// add FreeBSD-specific fields here
case JID: snprintf(buffer, n, Process_pidFormat, fp->jid); break;
case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break;
case JAIL:{
snprintf(buffer, n, "%-11s ", fp->jname); break;
xSnprintf(buffer, n, "%-11s ", fp->jname); break;
if (buffer[11] != '\0') {
buffer[11] = ' ';
buffer[12] = '\0';
@ -136,14 +136,14 @@ void FreeBSDProcess_writeField(Process* this, RichString* str, ProcessField fiel
long FreeBSDProcess_compare(const void* v1, const void* v2) {
FreeBSDProcess *p1, *p2;
Settings *settings = ((Process*)v1)->settings;
if (settings->direction == 1) {
if (settings->ss->direction == 1) {
p1 = (FreeBSDProcess*)v1;
p2 = (FreeBSDProcess*)v2;
} else {
p2 = (FreeBSDProcess*)v1;
p1 = (FreeBSDProcess*)v2;
}
switch ((int) settings->sortKey) {
switch ((int) settings->ss->sortKey) {
// add FreeBSD-specific fields here
case JID:
return (p1->jid - p2->jid);

View File

@ -151,10 +151,10 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui
fpl->cp_time_n = xCalloc(cpus, sizeof_cp_time_array);
len = sizeof_cp_time_array;
// fetch intial single (or average) CPU clicks from kernel
// fetch initial single (or average) CPU clicks from kernel
sysctl(MIB_kern_cp_time, 2, fpl->cp_time_o, &len, NULL, 0);
// on smp box, fetch rest of intial CPU's clicks
// on smp box, fetch rest of initial CPU's clicks
if (cpus > 1) {
len = 2; sysctlnametomib("kern.cp_times", MIB_kern_cp_times, &len);
fpl->cp_times_o = xCalloc(cpus, sizeof_cp_time_array);
@ -175,7 +175,7 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui
len = sizeof(kernelFScale);
if (sysctlbyname("kern.fscale", &kernelFScale, &len, NULL, 0) == -1) {
//sane default for kernel provded CPU precentage scaling, at least on x86 machines, in case this sysctl call failed
//sane default for kernel provided CPU percentage scaling, at least on x86 machines, in case this sysctl call failed
kernelFScale = 2048;
}
@ -398,7 +398,7 @@ char* FreeBSDProcessList_readJailName(struct kinfo_proc* kproc) {
jid = jail_get(jiov, 6, 0);
if (jid < 0) {
if (!jail_errmsg[0])
snprintf(jail_errmsg, JAIL_ERRMSGLEN, "jail_get: %s", strerror(errno));
xSnprintf(jail_errmsg, JAIL_ERRMSGLEN, "jail_get: %s", strerror(errno));
return NULL;
} else if (jid == kproc->ki_jid) {
jname = xStrdup(jnamebuf);
@ -427,7 +427,7 @@ void ProcessList_goThroughEntries(ProcessList* this) {
int cpus = this->cpuCount;
int count = 0;
struct kinfo_proc* kprocs = kvm_getprocs(fpl->kd, KERN_PROC_ALL, 0, &count);
struct kinfo_proc* kprocs = kvm_getprocs(fpl->kd, KERN_PROC_PROC, 0, &count);
for (int i = 0; i < count; i++) {
struct kinfo_proc* kproc = &kprocs[i];
@ -459,17 +459,17 @@ void ProcessList_goThroughEntries(ProcessList* this) {
fp->jname = FreeBSDProcessList_readJailName(kproc);
} else {
if(fp->jid != kproc->ki_jid) {
// proces can enter jail anytime
// process can enter jail anytime
fp->jid = kproc->ki_jid;
free(fp->jname);
fp->jname = FreeBSDProcessList_readJailName(kproc);
}
if (proc->ppid != kproc->ki_ppid) {
// if there are reapers in the system, proces can get reparented anytime
// if there are reapers in the system, process can get reparented anytime
proc->ppid = kproc->ki_ppid;
}
if(proc->st_uid != kproc->ki_uid) {
// some proceses change users (eg. to lower privs)
// some processes change users (eg. to lower privs)
proc->st_uid = kproc->ki_uid;
proc->user = UsersTable_getRef(this->usersTable, proc->st_uid);
}
@ -495,10 +495,6 @@ void ProcessList_goThroughEntries(ProcessList* this) {
isIdleProcess = true;
}
}
if (isIdleProcess == false && proc->percent_cpu >= 99.8) {
// don't break formatting
proc->percent_cpu = 99.8;
}
proc->priority = kproc->ki_pri.pri_level - PZERO;

View File

@ -39,11 +39,21 @@ extern ProcessFieldData Process_fields[];
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Main",
.columns = "PID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
SignalItem Platform_signals[] = {
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },
{ .name = " 1 SIGHUP", .number = 1 },
{ .name = " 2 SIGINT", .number = 2 },
@ -80,7 +90,7 @@ SignalItem Platform_signals[] = {
{ .name = "33 SIGLIBRT", .number = 33 },
};
unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
void Platform_setBindings(Htop_Action* keys) {
(void) keys;
@ -163,15 +173,15 @@ double Platform_setCPUValues(Meter* this, int cpu) {
double percent;
double* v = this->values;
v[CPU_METER_NICE] = cpuData->nicePercent * cpus;
v[CPU_METER_NORMAL] = cpuData->userPercent * cpus;
v[CPU_METER_NICE] = cpuData->nicePercent;
v[CPU_METER_NORMAL] = cpuData->userPercent;
if (this->pl->settings->detailedCPUTime) {
v[CPU_METER_KERNEL] = cpuData->systemPercent * cpus;
v[CPU_METER_IRQ] = cpuData->irqPercent * cpus;
v[CPU_METER_KERNEL] = cpuData->systemPercent;
v[CPU_METER_IRQ] = cpuData->irqPercent;
Meter_setItems(this, 4);
percent = v[0]+v[1]+v[2]+v[3];
} else {
v[2] = cpuData->systemAllPercent * cpus;
v[2] = cpuData->systemAllPercent;
Meter_setItems(this, 3);
percent = v[0]+v[1]+v[2];
}

View File

@ -20,13 +20,17 @@ extern ProcessFieldData Process_fields[];
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
extern ScreenDefaults Platform_defaultScreens[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields;
extern SignalItem Platform_signals[];
extern const SignalItem Platform_signals[];
extern unsigned int Platform_numberOfSignals;
extern const unsigned int Platform_numberOfSignals;
void Platform_setBindings(Htop_Action* keys);

View File

@ -3,14 +3,14 @@
htop \- interactive process viewer
.SH "SYNOPSIS"
.LP
.B htop [\fI\-dChusv\fR]
.B htop [\fI\-dChustv\fR]
.SH "DESCRIPTION"
.LP
Htop is a free (GPL) ncurses-based process viewer for Linux.
.LP
It is similar to top, but allows you to scroll vertically and horizontally,
so you can see all the processes running on the system, along with their full
command lines, as well as viewing them as a process tree, selecting mutiple
command lines, as well as viewing them as a process tree, selecting multiple
processes and acting on them all at once.
.LP
Tasks related to processes (killing, renicing) can be done without
@ -41,6 +41,9 @@ Show only the processes of a given user
.TP
\fB\-v \-\-version
Output version information and exit
.TP
\fB\-t \-\-tree
Show processes in tree view
.PP
.br
.SH "INTERACTIVE COMMANDS"
@ -48,16 +51,47 @@ Output version information and exit
The following commands are supported while in htop:
.LP
.TP 5
.B Arrows, PgUP, PgDn, Home, End
Scroll the process list.
.B Up, Alt-k
Select (highlight) the previous process in the process list. Scroll the list
if necessary.
.TP
.B Down, Alt-j
Select (highlight) the next process in the process list. Scroll the list if
necessary.
.TP
.B Left, Alt-h
Scroll the process list left.
.TP
.B Right, Alt-l
Scroll the process list right.
.TP
.B PgUp, PgDn
Scroll the process list up or down one window.
.TP
.B Home
Scroll to the top of the process list and select the first process.
.TP
.B End
Scroll to the bottom of the process list and select the last process.
.TP
.B Ctrl-A, ^
Scroll left to the beginning of the process entry (i.e. beginning of line).
.TP
.B Ctrl-E, $
Scroll right to the end of the process entry (i.e. end of line).
.TP
.B Space
Tag or untag a process. Commands that can operate on multiple processes,
like "kill", will then apply over the list of tagged processes, instead
of the currently highlighted one.
.TP
.B c
Tag the current process and its children. Commands that can operate on multiple
processes, like "kill", will then apply over the list of tagged processes,
instead of the currently highlighted one.
.TP
.B U
Untag all processes (remove all tags added with the Space key).
Untag all processes (remove all tags added with the Space or c keys).
.TP
.B s
Trace process system calls: if strace(1) is installed, pressing this key
@ -191,7 +225,7 @@ The parent process ID.
.B PGRP
The process's group ID.
.TP
.B SESSION (SESN)
.B SESSION (SID)
The process's session ID.
.TP
.B TTY_NR (TTY)
@ -344,6 +378,15 @@ The I/O scheduling class followed by the priority if the class supports it:
\fBB\fR for Best-effort
\fBid\fR for Idle
.TP
.B PERCENT_CPU_DELAY (CPUD%)
The percentage of time spent waiting for a CPU (while runnable). Requires CAP_NET_ADMIN.
.TP
.B PERCENT_IO_DELAY (IOD%)
The percentage of time spent waiting for the completion of synchronous block I/O. Requires CAP_NET_ADMIN.
.TP
.B PERCENT_SWAP_DELAY (SWAPD%)
The percentage of time spent swapping in pages. Requires CAP_NET_ADMIN.
.TP
.B All other flags
Currently unsupported (always displays '-').

21
htop.c
View File

@ -42,6 +42,7 @@ static void printHelpFlag() {
"-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
"-h --help Print this help screen\n"
"-s --sort-key=COLUMN Sort by COLUMN (try --sort-key=help for a list)\n"
"-t --tree Show the tree view by default\n"
"-u --user=USERNAME Show only processes of a given user\n"
"-p --pid=PID,[,PID,PID...] Show only the given PIDs\n"
"-v --version Print version info\n"
@ -61,6 +62,7 @@ typedef struct CommandLineSettings_ {
int sortKey;
int delay;
bool useColors;
bool treeView;
} CommandLineSettings;
static CommandLineSettings parseArguments(int argc, char** argv) {
@ -71,6 +73,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
.sortKey = 0,
.delay = -1,
.useColors = true,
.treeView = false,
};
static struct option long_opts[] =
@ -82,6 +85,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
{"user", required_argument, 0, 'u'},
{"no-color", no_argument, 0, 'C'},
{"no-colour",no_argument, 0, 'C'},
{"tree", no_argument, 0, 't'},
{"pid", required_argument, 0, 'p'},
{"io", no_argument, 0, 'i'},
{0,0,0,0}
@ -89,7 +93,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
int opt, opti=0;
/* Parse arguments */
while ((opt = getopt_long(argc, argv, "hvCs:d:u:p:i", long_opts, &opti))) {
while ((opt = getopt_long(argc, argv, "hvCs:td:u:p:i", long_opts, &opti))) {
if (opt == EOF) break;
switch (opt) {
case 'h':
@ -127,6 +131,9 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
case 'C':
flags.useColors = false;
break;
case 't':
flags.treeView = true;
break;
case 'p': {
char* argCopy = xStrdup(optarg);
char* saveptr;
@ -197,18 +204,20 @@ int main(int argc, char** argv) {
settings->delay = flags.delay;
if (!flags.useColors)
settings->colorScheme = COLORSCHEME_MONOCHROME;
if (flags.treeView)
settings->screens[0]->treeView = true;
CRT_init(settings->delay, settings->colorScheme);
MainPanel* panel = MainPanel_new();
ProcessList_setPanel(pl, (Panel*) panel);
MainPanel_updateTreeFunctions(panel, settings->treeView);
MainPanel_updateTreeFunctions(panel, settings->screens[0]->treeView);
if (flags.sortKey > 0) {
settings->sortKey = flags.sortKey;
settings->treeView = false;
settings->direction = 1;
settings->screens[0]->sortKey = flags.sortKey;
settings->screens[0]->treeView = false;
settings->screens[0]->direction = 1;
}
ProcessList_printHeader(pl, Panel_getHeader((Panel*)panel));
@ -228,7 +237,7 @@ int main(int argc, char** argv) {
millisleep(75);
ProcessList_scan(pl);
ScreenManager_run(scr, NULL, NULL);
ScreenManager_run(scr, NULL, NULL, NULL);
attron(CRT_colors[RESET_COLOR]);
mvhline(LINES-1, 0, ' ', COLS);

View File

@ -1,40 +1,9 @@
[Desktop Entry]
Type=Application
Version=1.0
Name=Htop
Type=Application
Comment=Show System Processes
Comment[ca]=Mostra els processos del sistema
Comment[de]=Systemprozesse anzeigen
Comment[en_GB]=Show System Processes
Comment[es]=Mostrar procesos del sistema
Comment[fi]=Katsele järjestelmän prosesseja
Comment[fr]=Affiche les processus système
Comment[gl]=Mostrar os procesos do sistema.
Comment[it]=Mostra processi di sistema
Comment[ko]=시스템 프로세스 보기
Comment[nb]=Vis systemprosesser
Comment[nl]=Systeemprocessen tonen
Comment[nn]=Vis systemprosessar
Comment[pl]=Pokaż procesy systemowe
Comment[pt]=Mostrar os Processos do Sistema
Comment[pt_BR]=Mostra os processos do sistema
Comment[ru]=Просмотр списка процессов в системе
Comment[sk]=Zobraziť systémové procesy
Comment[sl]=Prikaz sistemskih opravil
Comment[sr]=Приказ системских процеса
Comment[sr@ijekavian]=Приказ системских процеса
Comment[sr@ijekavianlatin]=Prikaz sistemskih procesa
Comment[sr@latin]=Prikaz sistemskih procesa
Comment[sv]=Visa systemprocesser
Comment[tr]=Sistem Süreçlerini Göster
Comment[uk]=Перегляд системних процесів
Comment[zh_TW]=顯示系統行程
Terminal=true
Exec=htop
Icon=htop
Categories=ConsoleOnly;System;
GenericName=Process Viewer
GenericName[ca]=Visor de processos
GenericName[ca]=Visualitzador de processos
GenericName[de]=Prozessanzeige
GenericName[en_GB]=Process Viewer
GenericName[es]=Visor de procesos
@ -52,11 +21,45 @@ GenericName[pt_BR]=Visualizador de processos
GenericName[ru]=Монитор процессов
GenericName[sk]=Prehliadač procesov
GenericName[sl]=Pregledovalnik opravil
GenericName[sr]=Приказивач процеса
GenericName[sr@ijekavian]=Приказивач процеса
GenericName[sr@ijekavianlatin]=Prikazivač procesa
GenericName[sr@latin]=Prikazivač procesa
GenericName[sr]=Приказивач процеса
GenericName[sv]=Processvisning
GenericName[tr]=Süreç Görüntüleyici
GenericName[uk]=Перегляд процесів
GenericName[zh_CN]=进程查看器
GenericName[zh_TW]=行程檢視器
Comment=Show System Processes
Comment[ca]=Visualitzeu els processos del sistema
Comment[de]=Systemprozesse anzeigen
Comment[en_GB]=Show System Processes
Comment[es]=Mostrar procesos del sistema
Comment[fi]=Katsele järjestelmän prosesseja
Comment[fr]=Affiche les processus système
Comment[gl]=Mostrar os procesos do sistema.
Comment[it]=Mostra processi di sistema
Comment[ko]=시스템 프로세스 보기
Comment[nb]=Vis systemprosesser
Comment[nl]=Systeemprocessen tonen
Comment[nn]=Vis systemprosessar
Comment[pl]=Pokaż procesy systemowe
Comment[pt]=Mostrar os Processos do Sistema
Comment[pt_BR]=Mostra os processos do sistema
Comment[ru]=Просмотр списка процессов в системе
Comment[sk]=Zobraziť systémové procesy
Comment[sl]=Prikaz sistemskih opravil
Comment[sr@ijekavian]=Приказ системских процеса
Comment[sr@ijekavianlatin]=Prikaz sistemskih procesa
Comment[sr@latin]=Prikaz sistemskih procesa
Comment[sr]=Приказ системских процеса
Comment[sv]=Visa systemprocesser
Comment[tr]=Sistem Süreçlerini Göster
Comment[uk]=Перегляд системних процесів
Comment[zh_CN]=显示系统进程
Comment[zh_TW]=顯示系統行程
Icon=htop
Exec=htop
Terminal=true
Categories=System;Monitor;ConsoleOnly;
Keywords=system;process;task

View File

@ -41,11 +41,9 @@ static unsigned long int parseBatInfo(const char *fileName, const unsigned short
unsigned int nBatteries = 0;
memset(batteries, 0, MAX_BATTERIES * sizeof(char*));
struct dirent result;
struct dirent* dirEntry;
while (nBatteries < MAX_BATTERIES) {
int err = readdir_r(batteryDir, &result, &dirEntry);
if (err || !dirEntry)
struct dirent* dirEntry = readdir(batteryDir);
if (!dirEntry)
break;
char* entryName = dirEntry->d_name;
if (strncmp(entryName, "BAT", 3))
@ -58,25 +56,29 @@ static unsigned long int parseBatInfo(const char *fileName, const unsigned short
unsigned long int total = 0;
for (unsigned int i = 0; i < nBatteries; i++) {
char infoPath[30];
snprintf(infoPath, sizeof infoPath, "%s%s/%s", batteryPath, batteries[i], fileName);
xSnprintf(infoPath, sizeof infoPath, "%s%s/%s", batteryPath, batteries[i], fileName);
FILE* file = fopen(infoPath, "r");
if (!file) {
break;
}
char line[50] = "";
char* line = NULL;
for (unsigned short int i = 0; i < lineNum; i++) {
char* ok = fgets(line, sizeof line, file);
if (!ok) break;
free(line);
line = String_readLine(file);
if (!line) break;
}
fclose(file);
if (!line) break;
char *foundNumStr = String_getToken(line, wordNum);
const unsigned long int foundNum = atoi(foundNumStr);
free(foundNumStr);
free(line);
total += foundNum;
}
@ -95,11 +97,9 @@ static ACPresence procAcpiCheck() {
return AC_ERROR;
}
struct dirent result;
struct dirent* dirEntry;
for (;;) {
int err = readdir_r((DIR *) dir, &result, &dirEntry);
if (err || !dirEntry)
struct dirent* dirEntry = readdir((DIR *) dir);
if (!dirEntry)
break;
char* entryName = (char *) dirEntry->d_name;
@ -108,22 +108,18 @@ static ACPresence procAcpiCheck() {
continue;
char statePath[50];
snprintf((char *) statePath, sizeof statePath, "%s/%s/state", power_supplyPath, entryName);
xSnprintf((char *) statePath, sizeof statePath, "%s/%s/state", power_supplyPath, entryName);
FILE* file = fopen(statePath, "r");
if (!file) {
isOn = AC_ERROR;
continue;
}
char line[100];
char* ok = fgets(line, sizeof line, file);
if (!ok) continue;
line[sizeof(line) - 1] = '\0';
char* line = String_readLine(file);
fclose(file);
if (!line) continue;
const char *isOnline = String_getToken(line, 2);
free(line);
if (strcmp(isOnline, "on-line") == 0) {
isOn = AC_PRESENT;
@ -178,25 +174,6 @@ static inline ssize_t xread(int fd, void *buf, size_t count) {
}
}
/**
* Returns a pointer to the suffix of `str` if its beginning matches `prefix`.
* Returns NULL if the prefix does not match.
* Examples:
* match("hello world", "hello "); -> "world"
* match("hello world", "goodbye "); -> NULL
*/
static inline const char* match(const char* str, const char* prefix) {
for (;;) {
if (*prefix == '\0') {
return str;
}
if (*prefix != *str) {
return NULL;
}
prefix++; str++;
}
}
static void Battery_getSysData(double* level, ACPresence* isOnAC) {
*level = 0;
@ -209,18 +186,16 @@ static void Battery_getSysData(double* level, ACPresence* isOnAC) {
unsigned long int totalFull = 0;
unsigned long int totalRemain = 0;
struct dirent result;
struct dirent* dirEntry;
for (;;) {
int err = readdir_r((DIR *) dir, &result, &dirEntry);
if (err || !dirEntry)
struct dirent* dirEntry = readdir((DIR *) dir);
if (!dirEntry)
break;
char* entryName = (char *) dirEntry->d_name;
const char filePath[50];
if (entryName[0] == 'B' && entryName[1] == 'A' && entryName[2] == 'T') {
snprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName);
xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName);
int fd = open(filePath, O_RDONLY);
if (fd == -1) {
closedir(dir);
@ -239,6 +214,8 @@ static void Battery_getSysData(double* level, ACPresence* isOnAC) {
bool full = false;
bool now = false;
while ((line = strsep(&buf, "\n")) != NULL) {
#define match(str,prefix) \
(String_startsWith(str,prefix) ? (str) + strlen(prefix) : NULL)
const char* ps = match(line, "POWER_SUPPLY_");
if (!ps) {
continue;
@ -265,12 +242,13 @@ static void Battery_getSysData(double* level, ACPresence* isOnAC) {
continue;
}
}
#undef match
} else if (entryName[0] == 'A') {
if (*isOnAC != AC_ERROR) {
continue;
}
snprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName);
xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName);
int fd = open(filePath, O_RDONLY);
if (fd == -1) {
closedir(dir);
@ -325,6 +303,9 @@ void Battery_getData(double* level, ACPresence* isOnAC) {
*level = -1;
*isOnAC = AC_ERROR;
}
if (*level > 100.0) {
*level = 100.0;
}
Battery_cacheLevel = *level;
Battery_cacheIsOnAC = *isOnAC;
Battery_cacheTime = now;

View File

@ -29,13 +29,6 @@ Linux battery readings written by Ian P. Hands (iphands@gmail.com, ihands@redhat
// READ FROM /sys
// ----------------------------------------
/**
* Returns a pointer to the suffix of `str` if its beginning matches `prefix`.
* Returns NULL if the prefix does not match.
* Examples:
* match("hello world", "hello "); -> "world"
* match("hello world", "goodbye "); -> NULL
*/
void Battery_getData(double* level, ACPresence* isOnAC);
#endif

View File

@ -35,7 +35,7 @@ typedef int IOPriority;
#define IOPriority_error 0xffffffff
#define IOPriority_None IOPriority_tuple(IOPRIO_CLASS_NONE, 0)
#define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 0)
#define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 7)
}*/

View File

@ -36,7 +36,7 @@ typedef int IOPriority;
#define IOPriority_error 0xffffffff
#define IOPriority_None IOPriority_tuple(IOPRIO_CLASS_NONE, 0)
#define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 0)
#define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 7)

View File

@ -19,7 +19,7 @@ Panel* IOPriorityPanel_new(IOPriority currPrio) {
Panel_setHeader(this, "IO Priority:");
Panel_add(this, (Object*) ListItem_new("None (based on nice)", IOPriority_None));
if (currPrio == IOPriority_None) Panel_setSelected(this, 0);
struct { int klass; const char* name; } classes[] = {
static const struct { int klass; const char* name; } classes[] = {
{ .klass = IOPRIO_CLASS_RT, .name = "Realtime" },
{ .klass = IOPRIO_CLASS_BE, .name = "Best-effort" },
{ .klass = 0, .name = NULL }
@ -27,7 +27,7 @@ Panel* IOPriorityPanel_new(IOPriority currPrio) {
for (int c = 0; classes[c].name; c++) {
for (int i = 0; i < 8; i++) {
char name[50];
snprintf(name, sizeof(name)-1, "%s %d %s", classes[c].name, i, i == 0 ? "(High)" : (i == 7 ? "(Low)" : ""));
xSnprintf(name, sizeof(name)-1, "%s %d %s", classes[c].name, i, i == 0 ? "(High)" : (i == 7 ? "(Low)" : ""));
IOPriority ioprio = IOPriority_tuple(classes[c].klass, i);
Panel_add(this, (Object*) ListItem_new(name, ioprio));
if (currPrio == ioprio) Panel_setSelected(this, Panel_size(this) - 1);

View File

@ -15,14 +15,28 @@ in the source distribution for its full text.
#include <unistd.h>
#include <string.h>
#include <sys/syscall.h>
#include <time.h>
/*{
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200
#define PROCESS_FLAG_LINUX_VSERVER 0x0400
#define PROCESS_FLAG_LINUX_CGROUP 0x0800
#define PROCESS_FLAG_LINUX_OOM 0x1000
#include "PerfCounter.h"
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100L
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200L
#define PROCESS_FLAG_LINUX_VSERVER 0x0400L
#define PROCESS_FLAG_LINUX_CGROUP 0x0800L
#define PROCESS_FLAG_LINUX_OOM 0x1000L
#define PROCESS_FLAG_LINUX_HPC 0xff0000L
#define PROCESS_FLAG_LINUX_HPC_CYCLE 0x10000L
#define PROCESS_FLAG_LINUX_HPC_INSN 0x20000L
#define PROCESS_FLAG_LINUX_HPC_MISS 0x40000L
#define PROCESS_FLAG_LINUX_HPC_BMISS 0x80000L
#define PROCESS_FLAG_LINUX_HPC_L1DR 0x100000L
#define PROCESS_FLAG_LINUX_HPC_L1DW 0x200000L
#define PROCESS_FLAG_LINUX_HPC_L1DRM 0x400000L
#define PROCESS_FLAG_LINUX_HPC_L1DWM 0x800000L
typedef enum UnsupportedProcessFields {
FLAGS = 9,
@ -81,13 +95,30 @@ typedef enum LinuxProcessFields {
#endif
OOM = 114,
IO_PRIORITY = 115,
LAST_PROCESSFIELD = 116,
#ifdef HAVE_DELAYACCT
PERCENT_CPU_DELAY = 116,
PERCENT_IO_DELAY = 117,
PERCENT_SWAP_DELAY = 118,
#endif
#ifdef HAVE_PERFCOUNTERS
IPC = 119,
MCYCLE = 120,
MINSTR = 121,
PERCENT_MISS = 122,
PERCENT_BMISS = 123,
L1DREADS = 124,
L1DRMISSES = 125,
L1DWRITES = 126,
L1DWMISSES = 127,
#endif
LAST_PROCESSFIELD = 128,
} LinuxProcessField;
#include "IOPriority.h"
typedef struct LinuxProcess_ {
Process super;
bool isKernelThread;
IOPriority ioPriority;
unsigned long int cminflt;
unsigned long int cmajflt;
@ -100,6 +131,7 @@ typedef struct LinuxProcess_ {
long m_drs;
long m_lrs;
long m_dt;
unsigned long long starttime;
#ifdef HAVE_TASKSTATS
unsigned long long io_rchar;
unsigned long long io_wchar;
@ -124,10 +156,39 @@ typedef struct LinuxProcess_ {
char* cgroup;
#endif
unsigned int oom;
char* ttyDevice;
#ifdef HAVE_DELAYACCT
unsigned long long int delay_read_time;
unsigned long long cpu_delay_total;
unsigned long long blkio_delay_total;
unsigned long long swapin_delay_total;
float cpu_delay_percent;
float blkio_delay_percent;
float swapin_delay_percent;
#endif
#ifdef HAVE_PERFCOUNTERS
PerfCounter* cycleCounter;
PerfCounter* insnCounter;
PerfCounter* missCounter;
PerfCounter* brCounter;
PerfCounter* l1drCounter;
PerfCounter* l1drmCounter;
PerfCounter* l1dwCounter;
PerfCounter* l1dwmCounter;
float ipc;
float mcycle;
float minstr;
float pMiss;
float pBMiss;
float l1dr;
float l1drm;
float l1dw;
float l1dwm;
#endif
} LinuxProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->pgrp == 0)
#define Process_isKernelThread(_process) (((LinuxProcess*)(_process))->isKernelThread)
#endif
#ifndef Process_isUserlandThread
@ -136,15 +197,17 @@ typedef struct LinuxProcess_ {
}*/
long long btime; /* semi-global */
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging, I idle)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SESN ", .description = "Process's session ID", .flags = 0, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
[TTY_NR] = { .name = "TTY_NR", .title = "TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
[FLAGS] = { .name = "FLAGS", .title = NULL, .description = NULL, .flags = 0, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
@ -214,6 +277,22 @@ ProcessFieldData Process_fields[] = {
#endif
[OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, },
[IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, },
#ifdef HAVE_DELAYACCT
[PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPD% ", .description = "CPU delay %", .flags = 0, },
[PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = 0, },
[PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWD% ", .description = "Swapin delay %", .flags = 0, },
#endif
#ifdef HAVE_PERFCOUNTERS
[IPC] = { .name = "IPC", .title = " IPC ", .description = "Executed instructions per cycle", .flags = PROCESS_FLAG_LINUX_HPC_CYCLE | PROCESS_FLAG_LINUX_HPC_INSN, },
[MCYCLE] = { .name = "MCYCLE", .title = " Mcycle ", .description = "Cycles (millions)", .flags = PROCESS_FLAG_LINUX_HPC_CYCLE, },
[MINSTR] = { .name = "MINSTR", .title = " Minstr ", .description = "Instructions (millions)", .flags = PROCESS_FLAG_LINUX_HPC_INSN, },
[PERCENT_MISS] = { .name = "PERCENT_MISS", .title = "MIS% ", .description = "Cache misses per 100 instructions", .flags = PROCESS_FLAG_LINUX_HPC_MISS | PROCESS_FLAG_LINUX_HPC_INSN, },
[PERCENT_BMISS] = { .name = "PERCENT_BMISS", .title = "BrM% ", .description = "Branch misprediction per 100 instructions", .flags = PROCESS_FLAG_LINUX_HPC_BMISS | PROCESS_FLAG_LINUX_HPC_INSN, },
[L1DREADS] = { .name = "L1DREADS", .title = " L1Dread ", .description = "L1 data cache: reads (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DR, },
[L1DRMISSES] = { .name = "L1DRMISSES", .title = " R miss ", .description = "L1 data cache: reads misses (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DRM, },
[L1DWRITES] = { .name = "L1DWRITES", .title = " L1Dwrite ", .description = "L1D data cache: writes (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DW, },
[L1DWMISSES] = { .name = "L1DWMISSES", .title = " W miss ", .description = "L1D data cache: write misses (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DWM, },
#endif
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
@ -226,7 +305,7 @@ ProcessPidColumn Process_pidColumns[] = {
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SESN" },
{ .id = SESSION, .label = "SID" },
{ .id = OOM, .label = "OOM" },
{ .id = 0, .label = NULL },
};
@ -254,6 +333,11 @@ void Process_delete(Object* cast) {
#ifdef HAVE_CGROUP
free(this->cgroup);
#endif
#ifdef HAVE_PERFCOUNTERS
PerfCounter_delete(this->cycleCounter);
PerfCounter_delete(this->insnCounter);
#endif
free(this->ttyDevice);
free(this);
}
@ -285,6 +369,45 @@ bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio) {
return (LinuxProcess_updateIOPriority(this) == ioprio);
}
#if HAVE_DELAYACCT || HAVE_PERFCOUNTERS
static char* perfFmt[] = {
"%6.2f ",
NULL,
NULL,
NULL,
NULL,
NULL,
"%6.1f ",
"%7.1f ",
"%8.2f ",
"%9.1f ",
};
static char* perfNA[] = {
" N/A ",
NULL,
NULL,
NULL,
NULL,
NULL,
" N/A ",
" N/A ",
" N/A ",
" N/A ",
};
static inline void LinuxProcess_printPerfCounter(float val, int len, char* buffer, int n, int* attr) {
if (val != -1) {
xSnprintf(buffer, n, perfFmt[len], val);
} else {
xSnprintf(buffer, n, perfNA[len]);
*attr = CRT_colors[PROCESS_SHADOW];
}
}
#endif
void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) {
LinuxProcess* lp = (LinuxProcess*) this;
bool coloring = this->settings->highlightMegabytes;
@ -292,6 +415,15 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1;
switch ((int)field) {
case TTY_NR: {
if (lp->ttyDevice) {
xSnprintf(buffer, n, "%-9s", lp->ttyDevice + 5 /* skip "/dev/" */);
} else {
attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, "? ");
}
break;
}
case CMINFLT: Process_colorNumber(str, lp->cminflt, coloring); return;
case CMAJFLT: Process_colorNumber(str, lp->cmajflt, coloring); return;
case M_DRS: Process_humanNumber(str, lp->m_drs * PAGE_SIZE_KB, coloring); return;
@ -303,6 +435,13 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
case STIME: Process_printTime(str, lp->stime); return;
case CUTIME: Process_printTime(str, lp->cutime); return;
case CSTIME: Process_printTime(str, lp->cstime); return;
case STARTTIME: {
struct tm date;
time_t starttimewall = btime + (lp->starttime / sysconf(_SC_CLK_TCK));
(void) localtime_r(&starttimewall, &date);
strftime(buffer, n, ((starttimewall > time(NULL) - 86400) ? "%R " : "%b%d "), &date);
break;
}
#ifdef HAVE_TASKSTATS
case RCHAR: Process_colorNumber(str, lp->io_rchar, coloring); return;
case WCHAR: Process_colorNumber(str, lp->io_wchar, coloring); return;
@ -321,34 +460,50 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
}
#endif
#ifdef HAVE_OPENVZ
case CTID: snprintf(buffer, n, "%7u ", lp->ctid); break;
case VPID: snprintf(buffer, n, Process_pidFormat, lp->vpid); break;
case CTID: xSnprintf(buffer, n, "%7u ", lp->ctid); break;
case VPID: xSnprintf(buffer, n, Process_pidFormat, lp->vpid); break;
#endif
#ifdef HAVE_VSERVER
case VXID: snprintf(buffer, n, "%5u ", lp->vxid); break;
case VXID: xSnprintf(buffer, n, "%5u ", lp->vxid); break;
#endif
#ifdef HAVE_CGROUP
case CGROUP: snprintf(buffer, n, "%-10s ", lp->cgroup); break;
case CGROUP: xSnprintf(buffer, n, "%-10s ", lp->cgroup); break;
#endif
case OOM: snprintf(buffer, n, Process_pidFormat, lp->oom); break;
case OOM: xSnprintf(buffer, n, Process_pidFormat, lp->oom); break;
case IO_PRIORITY: {
int klass = IOPriority_class(lp->ioPriority);
if (klass == IOPRIO_CLASS_NONE) {
// see note [1] above
snprintf(buffer, n, "B%1d ", (int) (this->nice + 20) / 5);
xSnprintf(buffer, n, "B%1d ", (int) (this->nice + 20) / 5);
} else if (klass == IOPRIO_CLASS_BE) {
snprintf(buffer, n, "B%1d ", IOPriority_data(lp->ioPriority));
xSnprintf(buffer, n, "B%1d ", IOPriority_data(lp->ioPriority));
} else if (klass == IOPRIO_CLASS_RT) {
attr = CRT_colors[PROCESS_HIGH_PRIORITY];
snprintf(buffer, n, "R%1d ", IOPriority_data(lp->ioPriority));
} else if (lp->ioPriority == IOPriority_Idle) {
xSnprintf(buffer, n, "R%1d ", IOPriority_data(lp->ioPriority));
} else if (klass == IOPRIO_CLASS_IDLE) {
attr = CRT_colors[PROCESS_LOW_PRIORITY];
snprintf(buffer, n, "id ");
xSnprintf(buffer, n, "id ");
} else {
snprintf(buffer, n, "?? ");
xSnprintf(buffer, n, "?? ");
}
break;
}
#ifdef HAVE_DELAYACCT
case PERCENT_CPU_DELAY: Process_printPercentage(lp->cpu_delay_percent, buffer, n, &attr); break;
case PERCENT_IO_DELAY: Process_printPercentage(lp->blkio_delay_percent, buffer, n, &attr); break;
case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_delay_percent, buffer, n, &attr); break;
#endif
#ifdef HAVE_PERFCOUNTERS
case PERCENT_MISS: Process_printPercentage(lp->pMiss, buffer, n, &attr); break;
case PERCENT_BMISS: Process_printPercentage(lp->pBMiss, buffer, n, &attr); break;
case IPC: LinuxProcess_printPerfCounter(lp->ipc, 0, buffer, n, &attr); break;
case MCYCLE: LinuxProcess_printPerfCounter(lp->mcycle, 8, buffer, n, &attr); break;
case MINSTR: LinuxProcess_printPerfCounter(lp->minstr, 8, buffer, n, &attr); break;
case L1DREADS: LinuxProcess_printPerfCounter(lp->l1dr, 9, buffer, n, &attr); break;
case L1DRMISSES: LinuxProcess_printPerfCounter(lp->l1drm, 9, buffer, n, &attr); break;
case L1DWRITES: LinuxProcess_printPerfCounter(lp->l1dw, 9, buffer, n, &attr); break;
case L1DWMISSES: LinuxProcess_printPerfCounter(lp->l1dwm, 9, buffer, n, &attr); break;
#endif
default:
Process_writeField((Process*)this, str, field);
return;
@ -356,10 +511,12 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
RichString_append(str, attr, buffer);
}
#define COMPARE_FIELD(_f) return (p2->_f > p1->_f ? 1 : -1)
long LinuxProcess_compare(const void* v1, const void* v2) {
LinuxProcess *p1, *p2;
Settings *settings = ((Process*)v1)->settings;
if (settings->direction == 1) {
if (settings->ss->direction == 1) {
p1 = (LinuxProcess*)v1;
p2 = (LinuxProcess*)v2;
} else {
@ -367,7 +524,11 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
p1 = (LinuxProcess*)v2;
}
long long diff;
switch ((int)settings->sortKey) {
switch ((int)settings->ss->sortKey) {
case CMAJFLT:
return (p2->cmajflt - p1->cmajflt);
case CMINFLT:
return (p2->cminflt - p1->cminflt);
case M_DRS:
return (p2->m_drs - p1->m_drs);
case M_DT:
@ -382,6 +543,12 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
case CUTIME: diff = p2->cutime - p1->cutime; goto test_diff;
case STIME: diff = p2->stime - p1->stime; goto test_diff;
case CSTIME: diff = p2->cstime - p1->cstime; goto test_diff;
case STARTTIME: {
if (p1->starttime == p2->starttime)
return (p1->super.pid - p2->super.pid);
else
return (p1->starttime - p2->starttime);
}
#ifdef HAVE_TASKSTATS
case RCHAR: diff = p2->io_rchar - p1->io_rchar; goto test_diff;
case WCHAR: diff = p2->io_wchar - p1->io_wchar; goto test_diff;
@ -410,6 +577,22 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
#endif
case OOM:
return (p2->oom - p1->oom);
#ifdef HAVE_DELAYACCT
case PERCENT_CPU_DELAY: COMPARE_FIELD(cpu_delay_percent);
case PERCENT_IO_DELAY: COMPARE_FIELD(blkio_delay_percent);
case PERCENT_SWAP_DELAY: COMPARE_FIELD(swapin_delay_percent);
#endif
#ifdef HAVE_PERFCOUNTERS
case PERCENT_MISS: COMPARE_FIELD(pMiss);
case PERCENT_BMISS: COMPARE_FIELD(pBMiss);
case IPC: COMPARE_FIELD(ipc);
case MCYCLE: COMPARE_FIELD(mcycle);
case MINSTR: COMPARE_FIELD(minstr);
case L1DREADS: COMPARE_FIELD(l1dr);
case L1DRMISSES: COMPARE_FIELD(l1drm);
case L1DWRITES: COMPARE_FIELD(l1dw);
case L1DWMISSES: COMPARE_FIELD(l1dwm);
#endif
case IO_PRIORITY:
return LinuxProcess_effectiveIOPriority(p1) - LinuxProcess_effectiveIOPriority(p2);
default:
@ -422,4 +605,3 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
bool Process_isThread(Process* this) {
return (Process_isUserlandThread(this) || Process_isKernelThread(this));
}

View File

@ -10,11 +10,24 @@ in the source distribution for its full text.
*/
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200
#define PROCESS_FLAG_LINUX_VSERVER 0x0400
#define PROCESS_FLAG_LINUX_CGROUP 0x0800
#define PROCESS_FLAG_LINUX_OOM 0x1000
#include "PerfCounter.h"
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100L
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200L
#define PROCESS_FLAG_LINUX_VSERVER 0x0400L
#define PROCESS_FLAG_LINUX_CGROUP 0x0800L
#define PROCESS_FLAG_LINUX_OOM 0x1000L
#define PROCESS_FLAG_LINUX_HPC 0xff0000L
#define PROCESS_FLAG_LINUX_HPC_CYCLE 0x10000L
#define PROCESS_FLAG_LINUX_HPC_INSN 0x20000L
#define PROCESS_FLAG_LINUX_HPC_MISS 0x40000L
#define PROCESS_FLAG_LINUX_HPC_BMISS 0x80000L
#define PROCESS_FLAG_LINUX_HPC_L1DR 0x100000L
#define PROCESS_FLAG_LINUX_HPC_L1DW 0x200000L
#define PROCESS_FLAG_LINUX_HPC_L1DRM 0x400000L
#define PROCESS_FLAG_LINUX_HPC_L1DWM 0x800000L
typedef enum UnsupportedProcessFields {
FLAGS = 9,
@ -73,13 +86,30 @@ typedef enum LinuxProcessFields {
#endif
OOM = 114,
IO_PRIORITY = 115,
LAST_PROCESSFIELD = 116,
#ifdef HAVE_DELAYACCT
PERCENT_CPU_DELAY = 116,
PERCENT_IO_DELAY = 117,
PERCENT_SWAP_DELAY = 118,
#endif
#ifdef HAVE_PERFCOUNTERS
IPC = 119,
MCYCLE = 120,
MINSTR = 121,
PERCENT_MISS = 122,
PERCENT_BMISS = 123,
L1DREADS = 124,
L1DRMISSES = 125,
L1DWRITES = 126,
L1DWMISSES = 127,
#endif
LAST_PROCESSFIELD = 128,
} LinuxProcessField;
#include "IOPriority.h"
typedef struct LinuxProcess_ {
Process super;
bool isKernelThread;
IOPriority ioPriority;
unsigned long int cminflt;
unsigned long int cmajflt;
@ -92,6 +122,7 @@ typedef struct LinuxProcess_ {
long m_drs;
long m_lrs;
long m_dt;
unsigned long long starttime;
#ifdef HAVE_TASKSTATS
unsigned long long io_rchar;
unsigned long long io_wchar;
@ -116,10 +147,39 @@ typedef struct LinuxProcess_ {
char* cgroup;
#endif
unsigned int oom;
char* ttyDevice;
#ifdef HAVE_DELAYACCT
unsigned long long int delay_read_time;
unsigned long long cpu_delay_total;
unsigned long long blkio_delay_total;
unsigned long long swapin_delay_total;
float cpu_delay_percent;
float blkio_delay_percent;
float swapin_delay_percent;
#endif
#ifdef HAVE_PERFCOUNTERS
PerfCounter* cycleCounter;
PerfCounter* insnCounter;
PerfCounter* missCounter;
PerfCounter* brCounter;
PerfCounter* l1drCounter;
PerfCounter* l1drmCounter;
PerfCounter* l1dwCounter;
PerfCounter* l1dwmCounter;
float ipc;
float mcycle;
float minstr;
float pMiss;
float pBMiss;
float l1dr;
float l1drm;
float l1dw;
float l1dwm;
#endif
} LinuxProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->pgrp == 0)
#define Process_isKernelThread(_process) (((LinuxProcess*)(_process))->isKernelThread)
#endif
#ifndef Process_isUserlandThread
@ -127,6 +187,8 @@ typedef struct LinuxProcess_ {
#endif
long long btime; /* semi-global */
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
@ -151,11 +213,16 @@ IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this);
bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio);
#if HAVE_DELAYACCT || HAVE_PERFCOUNTERS
#endif
void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field);
#define COMPARE_FIELD(_f) return (p2->_f > p1->_f ? 1 : -1)
long LinuxProcess_compare(const void* v1, const void* v2);
bool Process_isThread(Process* this);
#endif

View File

@ -26,11 +26,29 @@ in the source distribution for its full text.
#include <assert.h>
#include <sys/types.h>
#include <fcntl.h>
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS) || \
(defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H)
#include <sys/sysmacros.h>
#endif
#ifdef HAVE_DELAYACCT
#include <netlink/attr.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <netlink/socket.h>
#include <netlink/msg.h>
#include <linux/taskstats.h>
#endif
/*{
#include "ProcessList.h"
extern long long btime;
typedef struct CPUData_ {
unsigned long long int totalTime;
unsigned long long int userTime;
@ -59,11 +77,23 @@ typedef struct CPUData_ {
unsigned long long int guestPeriod;
} CPUData;
typedef struct TtyDriver_ {
char* path;
unsigned int major;
unsigned int minorFrom;
unsigned int minorTo;
} TtyDriver;
typedef struct LinuxProcessList_ {
ProcessList super;
CPUData* cpus;
TtyDriver* ttyDrivers;
#ifdef HAVE_DELAYACCT
struct nl_sock *netlink_socket;
int netlink_family;
#endif
} LinuxProcessList;
#ifndef PROCDIR
@ -78,8 +108,12 @@ typedef struct LinuxProcessList_ {
#define PROCMEMINFOFILE PROCDIR "/meminfo"
#endif
#ifndef PROCTTYDRIVERSFILE
#define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers"
#endif
#ifndef PROC_LINE_LENGTH
#define PROC_LINE_LENGTH 512
#define PROC_LINE_LENGTH 4096
#endif
}*/
@ -87,43 +121,6 @@ typedef struct LinuxProcessList_ {
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList));
ProcessList* pl = &(this->super);
ProcessList_init(pl, Class(LinuxProcess), usersTable, pidWhiteList, userId);
// Update CPU count:
FILE* file = fopen(PROCSTATFILE, "r");
if (file == NULL) {
CRT_fatalError("Cannot open " PROCSTATFILE);
}
char buffer[PROC_LINE_LENGTH + 1];
int cpus = -1;
do {
cpus++;
char * s = fgets(buffer, PROC_LINE_LENGTH, file);
(void) s;
} while (String_startsWith(buffer, "cpu"));
fclose(file);
pl->cpuCount = MAX(cpus - 1, 1);
this->cpus = xCalloc(cpus, sizeof(CPUData));
for (int i = 0; i < cpus; i++) {
this->cpus[i].totalTime = 1;
this->cpus[i].totalPeriod = 1;
}
return pl;
}
void ProcessList_delete(ProcessList* pl) {
LinuxProcessList* this = (LinuxProcessList*) pl;
ProcessList_done(pl);
free(this->cpus);
free(this);
}
static ssize_t xread(int fd, void *buf, size_t count) {
// Read some bytes. Retry on EINTR and when we don't get as many bytes as we requested.
@ -141,6 +138,157 @@ static ssize_t xread(int fd, void *buf, size_t count) {
}
}
static int sortTtyDrivers(const void* va, const void* vb) {
TtyDriver* a = (TtyDriver*) va;
TtyDriver* b = (TtyDriver*) vb;
return (a->major == b->major) ? (a->minorFrom - b->minorFrom) : (a->major - b->major);
}
static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) {
TtyDriver* ttyDrivers;
int fd = open(PROCTTYDRIVERSFILE, O_RDONLY);
if (fd == -1)
return;
char* buf = NULL;
int bufSize = MAX_READ;
int bufLen = 0;
for(;;) {
buf = realloc(buf, bufSize);
int size = xread(fd, buf + bufLen, MAX_READ);
if (size <= 0) {
buf[bufLen] = '\0';
close(fd);
break;
}
bufLen += size;
bufSize += MAX_READ;
}
if (bufLen == 0) {
free(buf);
return;
}
int numDrivers = 0;
int allocd = 10;
ttyDrivers = malloc(sizeof(TtyDriver) * allocd);
char* at = buf;
while (*at != '\0') {
at = strchr(at, ' '); // skip first token
while (*at == ' ') at++; // skip spaces
char* token = at; // mark beginning of path
at = strchr(at, ' '); // find end of path
*at = '\0'; at++; // clear and skip
ttyDrivers[numDrivers].path = strdup(token); // save
while (*at == ' ') at++; // skip spaces
token = at; // mark beginning of major
at = strchr(at, ' '); // find end of major
*at = '\0'; at++; // clear and skip
ttyDrivers[numDrivers].major = atoi(token); // save
while (*at == ' ') at++; // skip spaces
token = at; // mark beginning of minorFrom
while (*at >= '0' && *at <= '9') at++; //find end of minorFrom
if (*at == '-') { // if has range
*at = '\0'; at++; // clear and skip
ttyDrivers[numDrivers].minorFrom = atoi(token); // save
token = at; // mark beginning of minorTo
at = strchr(at, ' '); // find end of minorTo
*at = '\0'; at++; // clear and skip
ttyDrivers[numDrivers].minorTo = atoi(token); // save
} else { // no range
*at = '\0'; at++; // clear and skip
ttyDrivers[numDrivers].minorFrom = atoi(token); // save
ttyDrivers[numDrivers].minorTo = atoi(token); // save
}
at = strchr(at, '\n'); // go to end of line
at++; // skip
numDrivers++;
if (numDrivers == allocd) {
allocd += 10;
ttyDrivers = realloc(ttyDrivers, sizeof(TtyDriver) * allocd);
}
}
free(buf);
numDrivers++;
ttyDrivers = realloc(ttyDrivers, sizeof(TtyDriver) * numDrivers);
ttyDrivers[numDrivers - 1].path = NULL;
qsort(ttyDrivers, numDrivers - 1, sizeof(TtyDriver), sortTtyDrivers);
this->ttyDrivers = ttyDrivers;
}
#ifdef HAVE_DELAYACCT
static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) {
this->netlink_socket = nl_socket_alloc();
if (this->netlink_socket == NULL) {
return;
}
if (nl_connect(this->netlink_socket, NETLINK_GENERIC) < 0) {
return;
}
this->netlink_family = genl_ctrl_resolve(this->netlink_socket, TASKSTATS_GENL_NAME);
}
#endif
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList));
ProcessList* pl = &(this->super);
ProcessList_init(pl, Class(LinuxProcess), usersTable, pidWhiteList, userId);
LinuxProcessList_initTtyDrivers(this);
#ifdef HAVE_DELAYACCT
LinuxProcessList_initNetlinkSocket(this);
#endif
// Update CPU count:
FILE* file = fopen(PROCSTATFILE, "r");
if (file == NULL) {
CRT_fatalError("Cannot open " PROCSTATFILE);
}
int cpus = 0;
do {
char buffer[PROC_LINE_LENGTH + 1];
if (fgets(buffer, PROC_LINE_LENGTH + 1, file) == NULL) {
CRT_fatalError("No btime in " PROCSTATFILE);
} else if (String_startsWith(buffer, "cpu")) {
cpus++;
} else if (String_startsWith(buffer, "btime ")) {
sscanf(buffer, "btime %lld\n", &btime);
break;
}
} while(true);
fclose(file);
pl->cpuCount = MAX(cpus - 1, 1);
this->cpus = xCalloc(cpus, sizeof(CPUData));
for (int i = 0; i < cpus; i++) {
this->cpus[i].totalTime = 1;
this->cpus[i].totalPeriod = 1;
}
return pl;
}
void ProcessList_delete(ProcessList* pl) {
LinuxProcessList* this = (LinuxProcessList*) pl;
ProcessList_done(pl);
free(this->cpus);
if (this->ttyDrivers) {
for(int i = 0; this->ttyDrivers[i].path; i++) {
free(this->ttyDrivers[i].path);
}
free(this->ttyDrivers);
}
#ifdef HAVE_DELAYACCT
if (this->netlink_socket) {
nl_close(this->netlink_socket);
nl_socket_free(this->netlink_socket);
}
#endif
free(this);
}
static double jiffy = 0.0;
static inline unsigned long long LinuxProcess_adjustTime(unsigned long long t) {
@ -152,7 +300,7 @@ static inline unsigned long long LinuxProcess_adjustTime(unsigned long long t) {
static bool LinuxProcessList_readStatFile(Process *process, const char* dirname, const char* name, char* command, int* commLen) {
LinuxProcess* lp = (LinuxProcess*) process;
char filename[MAX_NAME+1];
snprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
int fd = open(filename, O_RDONLY);
if (fd == -1)
return false;
@ -214,35 +362,31 @@ static bool LinuxProcessList_readStatFile(Process *process, const char* dirname,
location += 1;
process->nlwp = strtol(location, &location, 10);
location += 1;
for (int i=0; i<17; i++) location = strchr(location, ' ')+1;
location = strchr(location, ' ')+1;
lp->starttime = strtoll(location, &location, 10);
location += 1;
for (int i=0; i<15; i++) location = strchr(location, ' ')+1;
process->exit_signal = strtol(location, &location, 10);
location += 1;
assert(location != NULL);
process->processor = strtol(location, &location, 10);
process->time = lp->utime + lp->stime;
return true;
}
static bool LinuxProcessList_statProcessDir(Process* process, const char* dirname, char* name, time_t curTime) {
static bool LinuxProcessList_statProcessDir(Process* process, const char* dirname, char* name) {
char filename[MAX_NAME+1];
filename[MAX_NAME] = '\0';
snprintf(filename, MAX_NAME, "%s/%s", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s", dirname, name);
struct stat sstat;
int statok = stat(filename, &sstat);
if (statok == -1)
return false;
process->st_uid = sstat.st_uid;
struct tm date;
time_t ctime = sstat.st_ctime;
process->starttime_ctime = ctime;
(void) localtime_r((time_t*) &ctime, &date);
strftime(process->starttime_show, 7, ((ctime > curTime - 86400) ? "%R " : "%b%d "), &date);
return true;
}
@ -252,11 +396,20 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna
char filename[MAX_NAME+1];
filename[MAX_NAME] = '\0';
snprintf(filename, MAX_NAME, "%s/%s/io", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s/io", dirname, name);
int fd = open(filename, O_RDONLY);
if (fd == -1) {
process->io_rate_read_bps = -1;
process->io_rate_write_bps = -1;
process->io_rchar = -1LL;
process->io_wchar = -1LL;
process->io_syscr = -1LL;
process->io_syscw = -1LL;
process->io_read_bytes = -1LL;
process->io_write_bytes = -1LL;
process->io_cancelled_write_bytes = -1LL;
process->io_rate_read_time = -1LL;
process->io_rate_write_time = -1LL;
return;
}
@ -292,7 +445,7 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna
}
break;
case 's':
if (line[5] == 'r' && strncmp(line+1, "yscr: ", 6) == 0) {
if (line[4] == 'r' && strncmp(line+1, "yscr: ", 6) == 0) {
process->io_syscr = strtoull(line+7, NULL, 10);
} else if (strncmp(line+1, "yscw: ", 6) == 0) {
process->io_syscw = strtoull(line+7, NULL, 10);
@ -312,7 +465,7 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna
static bool LinuxProcessList_readStatmFile(LinuxProcess* process, const char* dirname, const char* name) {
char filename[MAX_NAME+1];
snprintf(filename, MAX_NAME, "%s/%s/statm", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s/statm", dirname, name);
int fd = open(filename, O_RDONLY);
if (fd == -1)
return false;
@ -342,7 +495,7 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, const char* d
return;
}
char filename[MAX_NAME+1];
snprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
FILE* file = fopen(filename, "r");
if (!file)
return;
@ -365,7 +518,7 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, const char* d
static void LinuxProcessList_readCGroupFile(LinuxProcess* process, const char* dirname, const char* name) {
char filename[MAX_NAME+1];
snprintf(filename, MAX_NAME, "%s/%s/cgroup", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s/cgroup", dirname, name);
FILE* file = fopen(filename, "r");
if (!file) {
process->cgroup = xStrdup("");
@ -400,7 +553,7 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, const char* d
static void LinuxProcessList_readVServerData(LinuxProcess* process, const char* dirname, const char* name) {
char filename[MAX_NAME+1];
snprintf(filename, MAX_NAME, "%s/%s/status", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s/status", dirname, name);
FILE* file = fopen(filename, "r");
if (!file)
return;
@ -431,10 +584,11 @@ static void LinuxProcessList_readVServerData(LinuxProcess* process, const char*
static void LinuxProcessList_readOomData(LinuxProcess* process, const char* dirname, const char* name) {
char filename[MAX_NAME+1];
snprintf(filename, MAX_NAME, "%s/%s/oom_score", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s/oom_score", dirname, name);
FILE* file = fopen(filename, "r");
if (!file)
if (!file) {
return;
}
char buffer[PROC_LINE_LENGTH + 1];
if (fgets(buffer, PROC_LINE_LENGTH, file)) {
unsigned int oom;
@ -446,6 +600,146 @@ static void LinuxProcessList_readOomData(LinuxProcess* process, const char* dirn
fclose(file);
}
#ifdef HAVE_DELAYACCT
static int handleNetlinkMsg(struct nl_msg *nlmsg, void *linuxProcess) {
struct nlmsghdr *nlhdr;
struct nlattr *nlattrs[TASKSTATS_TYPE_MAX + 1];
struct nlattr *nlattr;
struct taskstats *stats;
int rem;
unsigned long long int timeDelta;
LinuxProcess* lp = (LinuxProcess*) linuxProcess;
nlhdr = nlmsg_hdr(nlmsg);
if (genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) {
return NL_SKIP;
}
if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) {
stats = nla_data(nla_next(nla_data(nlattr), &rem));
assert(lp->super.pid == stats->ac_pid);
timeDelta = (stats->ac_etime*1000 - lp->delay_read_time);
#define BOUNDS(x) isnan(x) ? 0.0 : (x > 100) ? 100.0 : x;
#define DELTAPERC(x,y) BOUNDS((float) (x - y) / timeDelta * 100);
lp->cpu_delay_percent = DELTAPERC(stats->cpu_delay_total, lp->cpu_delay_total);
lp->blkio_delay_percent = DELTAPERC(stats->blkio_delay_total, lp->blkio_delay_total);
lp->swapin_delay_percent = DELTAPERC(stats->swapin_delay_total, lp->swapin_delay_total);
#undef DELTAPERC
#undef BOUNDS
lp->swapin_delay_total = stats->swapin_delay_total;
lp->blkio_delay_total = stats->blkio_delay_total;
lp->cpu_delay_total = stats->cpu_delay_total;
lp->delay_read_time = stats->ac_etime*1000;
}
return NL_OK;
}
static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProcess* process) {
struct nl_msg *msg;
if (nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) {
return;
}
if (! (msg = nlmsg_alloc())) {
return;
}
if (! genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) {
nlmsg_free(msg);
}
if (nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, process->super.pid) < 0) {
nlmsg_free(msg);
}
if (nl_send_sync(this->netlink_socket, msg) < 0) {
process->swapin_delay_percent = -1;
process->blkio_delay_percent = -1;
process->cpu_delay_percent = -1;
return;
}
if (nl_recvmsgs_default(this->netlink_socket) < 0) {
return;
}
}
#endif
#ifdef HAVE_PERFCOUNTERS
#define READ_COUNTER(_b, _var, _flag, _type, _config) \
bool _b ## Ok = false; \
uint64_t _b ## Delta = 0; \
if (flags & _flag && lp->super.show) { \
if (!_var) { \
_var = PerfCounter_new(lp->super.pid, _type, _config); \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = 0; \
} else { \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = PerfCounter_delta(_var); \
} \
if (_b ## Ok) { \
} \
} else { \
if (_var) { \
PerfCounter_delete(_var); \
_var = NULL; \
} \
}
#define SET_IF(_ok, _var, _exp) \
if (_ok) { \
_var = _exp; \
} else { \
_var = -1; \
}
#define SET_IFNZ(_ok, _z, _var, _exp) \
if (_ok) { \
if (_z > 0) { \
_var = _exp; \
} else { \
_var = 0; \
} \
} else { \
_var = -1; \
}
#define L1DR (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DRM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
#define L1DW (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DWM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
static void LinuxProcessList_readPerfCounters(LinuxProcess* lp, uint64_t flags) {
READ_COUNTER(c, lp->cycleCounter, PROCESS_FLAG_LINUX_HPC_CYCLE, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES);
READ_COUNTER(i, lp->insnCounter, PROCESS_FLAG_LINUX_HPC_INSN, PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS);
READ_COUNTER(m, lp->missCounter, PROCESS_FLAG_LINUX_HPC_MISS, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES);
READ_COUNTER(b, lp->brCounter, PROCESS_FLAG_LINUX_HPC_BMISS, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES);
READ_COUNTER(r, lp->l1drCounter, PROCESS_FLAG_LINUX_HPC_L1DR, PERF_TYPE_HW_CACHE, L1DR);
READ_COUNTER(R, lp->l1drmCounter, PROCESS_FLAG_LINUX_HPC_L1DRM, PERF_TYPE_HW_CACHE, L1DRM);
READ_COUNTER(w, lp->l1dwCounter, PROCESS_FLAG_LINUX_HPC_L1DW, PERF_TYPE_HW_CACHE, L1DW);
READ_COUNTER(W, lp->l1dwmCounter, PROCESS_FLAG_LINUX_HPC_L1DWM, PERF_TYPE_HW_CACHE, L1DWM);
SET_IF(cOk, lp->mcycle, (double)cDelta / 1000000);
SET_IF(iOk, lp->minstr, (double)iDelta / 1000000);
SET_IFNZ(cOk && iOk, cDelta, lp->ipc, (double)iDelta / cDelta);
SET_IFNZ(mOk && iOk, iDelta, lp->pMiss, 100 * ((double)mDelta / iDelta));
SET_IFNZ(bOk && iOk, iDelta, lp->pBMiss, 100 * ((double)bDelta / iDelta));
SET_IF(rOk, lp->l1dr, (double)rDelta / 1000);
SET_IF(ROk, lp->l1drm, (double)RDelta / 1000);
SET_IF(wOk, lp->l1dw, (double)wDelta / 1000);
SET_IF(WOk, lp->l1dwm, (double)WDelta / 1000);
}
#endif
static void setCommand(Process* process, const char* command, int len) {
if (process->comm && process->commLen >= len) {
strncpy(process->comm, command, len + 1);
@ -457,11 +751,8 @@ static void setCommand(Process* process, const char* command, int len) {
}
static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) {
if (Process_isKernelThread(process))
return true;
char filename[MAX_NAME+1];
snprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
int fd = open(filename, O_RDONLY);
if (fd == -1)
return false;
@ -470,32 +761,82 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna
int amtRead = xread(fd, command, sizeof(command) - 1);
close(fd);
int tokenEnd = 0;
if (amtRead > 0) {
for (int i = 0; i < amtRead; i++)
if (command[i] == '\0' || command[i] == '\n') {
if (tokenEnd == 0) {
tokenEnd = i;
}
command[i] = ' ';
int lastChar = 0;
if (amtRead == 0) {
((LinuxProcess*)process)->isKernelThread = true;
return true;
} else if (amtRead < 0) {
return false;
}
for (int i = 0; i < amtRead; i++) {
if (command[i] == '\0' || command[i] == '\n') {
if (tokenEnd == 0) {
tokenEnd = i;
}
command[i] = ' ';
} else {
lastChar = i;
}
}
if (tokenEnd == 0) {
tokenEnd = amtRead;
}
command[amtRead] = '\0';
command[lastChar + 1] = '\0';
process->basenameOffset = tokenEnd;
setCommand(process, command, amtRead);
setCommand(process, command, lastChar + 1);
return true;
}
static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned int tty_nr) {
unsigned int maj = major(tty_nr);
unsigned int min = minor(tty_nr);
int i = -1;
for (;;) {
i++;
if ((!ttyDrivers[i].path) || maj < ttyDrivers[i].major) {
break;
}
if (maj > ttyDrivers[i].major) {
continue;
}
if (min < ttyDrivers[i].minorFrom) {
break;
}
if (min > ttyDrivers[i].minorTo) {
continue;
}
unsigned int idx = min - ttyDrivers[i].minorFrom;
struct stat sstat;
char* fullPath;
for(;;) {
asprintf(&fullPath, "%s/%d", ttyDrivers[i].path, idx);
int err = stat(fullPath, &sstat);
if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) return fullPath;
free(fullPath);
asprintf(&fullPath, "%s%d", ttyDrivers[i].path, idx);
err = stat(fullPath, &sstat);
if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) return fullPath;
free(fullPath);
if (idx == min) break;
idx = min;
}
int err = stat(ttyDrivers[i].path, &sstat);
if (err == 0 && tty_nr == sstat.st_rdev) return strdup(ttyDrivers[i].path);
}
char* out;
asprintf(&out, "/dev/%u:%u", maj, min);
return out;
}
static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* dirname, Process* parent, double period, struct timeval tv) {
ProcessList* pl = (ProcessList*) this;
DIR* dir;
struct dirent* entry;
Settings* settings = pl->settings;
ScreenSettings* ss = settings->ss;
time_t curTime = tv.tv_sec;
#ifdef HAVE_TASKSTATS
unsigned long long now = tv.tv_sec*1000LL+tv.tv_usec/1000LL;
#endif
@ -535,11 +876,11 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
LinuxProcess* lp = (LinuxProcess*) proc;
char subdirname[MAX_NAME+1];
snprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
xSnprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
LinuxProcessList_recurseProcTree(this, subdirname, proc, period, tv);
#ifdef HAVE_TASKSTATS
if (settings->flags & PROCESS_FLAG_IO)
if (ss->flags & PROCESS_FLAG_IO)
LinuxProcessList_readIoFile(lp, dirname, name, now);
#endif
@ -551,9 +892,14 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
char command[MAX_NAME+1];
unsigned long long int lasttimes = (lp->utime + lp->stime);
int commLen = 0;
unsigned int tty_nr = proc->tty_nr;
if (! LinuxProcessList_readStatFile(proc, dirname, name, command, &commLen))
goto errorReadingProcess;
if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO)
if (tty_nr != proc->tty_nr && this->ttyDrivers) {
free(lp->ttyDevice);
lp->ttyDevice = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr);
}
if (ss->flags & PROCESS_FLAG_LINUX_IOPRIO)
LinuxProcess_updateIOPriority(lp);
float percent_cpu = (lp->utime + lp->stime - lasttimes) / period * 100.0;
proc->percent_cpu = CLAMP(percent_cpu, 0.0, cpus * 100.0);
@ -562,19 +908,19 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
if(!preExisting) {
if (! LinuxProcessList_statProcessDir(proc, dirname, name, curTime))
if (! LinuxProcessList_statProcessDir(proc, dirname, name))
goto errorReadingProcess;
proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid);
#ifdef HAVE_OPENVZ
if (settings->flags & PROCESS_FLAG_LINUX_OPENVZ) {
if (ss->flags & PROCESS_FLAG_LINUX_OPENVZ) {
LinuxProcessList_readOpenVZData(lp, dirname, name);
}
#endif
#ifdef HAVE_VSERVER
if (settings->flags & PROCESS_FLAG_LINUX_VSERVER) {
if (ss->flags & PROCESS_FLAG_LINUX_VSERVER) {
LinuxProcessList_readVServerData(lp, dirname, name);
}
#endif
@ -592,14 +938,23 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
}
}
#ifdef HAVE_DELAYACCT
LinuxProcessList_readDelayAcctData(this, lp);
#endif
#ifdef HAVE_CGROUP
if (settings->flags & PROCESS_FLAG_LINUX_CGROUP)
if (ss->flags & PROCESS_FLAG_LINUX_CGROUP)
LinuxProcessList_readCGroupFile(lp, dirname, name);
#endif
if (settings->flags & PROCESS_FLAG_LINUX_OOM)
if (ss->flags & PROCESS_FLAG_LINUX_OOM)
LinuxProcessList_readOomData(lp, dirname, name);
#ifdef HAVE_PERFCOUNTERS
if (ss->flags & PROCESS_FLAG_LINUX_HPC)
LinuxProcessList_readPerfCounters(lp, ss->flags);
#endif
if (proc->state == 'Z' && (proc->basenameOffset == 0)) {
proc->basenameOffset = -1;
setCommand(proc, command, commLen);

View File

@ -9,9 +9,19 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#ifdef MAJOR_IN_MKDEV
#elif defined(MAJOR_IN_SYSMACROS) || \
(defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H)
#endif
#ifdef HAVE_DELAYACCT
#endif
#include "ProcessList.h"
extern long long btime;
typedef struct CPUData_ {
unsigned long long int totalTime;
unsigned long long int userTime;
@ -40,11 +50,23 @@ typedef struct CPUData_ {
unsigned long long int guestPeriod;
} CPUData;
typedef struct TtyDriver_ {
char* path;
unsigned int major;
unsigned int minorFrom;
unsigned int minorTo;
} TtyDriver;
typedef struct LinuxProcessList_ {
ProcessList super;
CPUData* cpus;
TtyDriver* ttyDrivers;
#ifdef HAVE_DELAYACCT
struct nl_sock *netlink_socket;
int netlink_family;
#endif
} LinuxProcessList;
#ifndef PROCDIR
@ -59,15 +81,23 @@ typedef struct LinuxProcessList_ {
#define PROCMEMINFOFILE PROCDIR "/meminfo"
#endif
#ifndef PROCTTYDRIVERSFILE
#define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers"
#endif
#ifndef PROC_LINE_LENGTH
#define PROC_LINE_LENGTH 512
#define PROC_LINE_LENGTH 4096
#endif
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
#ifdef HAVE_DELAYACCT
#endif
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
void ProcessList_delete(ProcessList* pl);
@ -89,6 +119,58 @@ void ProcessList_delete(ProcessList* pl);
#endif
#ifdef HAVE_DELAYACCT
#endif
#ifdef HAVE_PERFCOUNTERS
#define READ_COUNTER(_b, _var, _flag, _type, _config) \
bool _b ## Ok = false; \
uint64_t _b ## Delta = 0; \
if (flags & _flag && lp->super.show) { \
if (!_var) { \
_var = PerfCounter_new(lp->super.pid, _type, _config); \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = 0; \
} else { \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = PerfCounter_delta(_var); \
} \
if (_b ## Ok) { \
} \
} else { \
if (_var) { \
PerfCounter_delete(_var); \
_var = NULL; \
} \
}
#define SET_IF(_ok, _var, _exp) \
if (_ok) { \
_var = _exp; \
} else { \
_var = -1; \
}
#define SET_IFNZ(_ok, _z, _var, _exp) \
if (_ok) { \
if (_z > 0) { \
_var = _exp; \
} else { \
_var = 0; \
} \
} else { \
_var = -1; \
}
#define L1DR (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DRM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
#define L1DW (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DWM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
#endif
void ProcessList_goThroughEntries(ProcessList* super);
#endif

135
linux/PerfCounter.c Normal file
View File

@ -0,0 +1,135 @@
/*
* This file is based on tiptop.
* by Erven ROHOU
* Copyright (c) 2011, 2012, 2014 Inria
* License: GNU General Public License version 2.
*/
#include "PerfCounter.h"
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include "XAlloc.h"
/*{
#include <config.h>
#include <sys/types.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <inttypes.h>
#include <stdbool.h>
// The sys_perf_counter_open syscall and header files changed names
// between Linux 2.6.31 and 2.6.32. Do the mangling here.
#ifdef HAVE_LINUX_PERF_COUNTER_H
#include <linux/perf_counter.h>
#define STRUCT_NAME perf_counter_attr
#define SYSCALL_NUM __NR_perf_counter_open
#elif HAVE_LINUX_PERF_EVENT_H
#include <linux/perf_event.h>
#define STRUCT_NAME perf_event_attr
#define SYSCALL_NUM __NR_perf_event_open
#else
#error Sorry, performance counters not supported on this system.
#endif
typedef struct PerfCounter_ {
struct STRUCT_NAME events;
pid_t pid;
int fd;
uint64_t prevValue;
uint64_t value;
} PerfCounter;
#define PerfCounter_delta(pc_) ((pc_)->value - (pc_)->prevValue)
}*/
int PerfCounter_openFds = 0;
static int PerfCounter_fdLimit = -1;
static void PerfCounter_initFdLimit() {
char name[100] = { 0 }; /* needs to fit the name /proc/xxxx/limits */
snprintf(name, sizeof(name) - 1, "/proc/%d/limits", getpid());
FILE* f = fopen(name, "r");
if (f) {
char line[100];
while (fgets(line, 100, f)) {
int n = sscanf(line, "Max open files %d", &PerfCounter_fdLimit);
if (n) {
break;
}
}
fclose(f);
}
PerfCounter_fdLimit -= 20; /* keep some slack */
if (PerfCounter_fdLimit == 0) { /* something went wrong */
PerfCounter_fdLimit = 200; /* reasonable default? */
}
}
static long perf_event_open(struct STRUCT_NAME *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) {
int ret = syscall(SYSCALL_NUM, hw_event, pid, cpu, group_fd, flags);
#if defined(__x86_64__) || defined(__i386__)
if (ret < 0 && ret > -4096) {
errno = -ret;
ret = -1;
}
#endif
return ret;
}
PerfCounter* PerfCounter_new(pid_t pid, uint32_t type, uint64_t config) {
if (PerfCounter_fdLimit == -1) {
PerfCounter_initFdLimit();
}
PerfCounter* this = xCalloc(sizeof(PerfCounter), 1);
this->pid = pid;
this->events.disabled = 0;
this->events.pinned = 1;
this->events.exclude_hv = 1;
this->events.exclude_kernel = 1;
this->events.type = type;
this->events.config = config;
if (PerfCounter_openFds < PerfCounter_fdLimit) {
this->fd = perf_event_open(&this->events, pid, -1, -1, 0);
} else {
this->fd = -1;
}
if (this->fd != -1) {
PerfCounter_openFds++;
}
return this;
}
void PerfCounter_delete(PerfCounter* this) {
if (!this) {
return;
}
if (this->fd != -1) {
PerfCounter_openFds--;
}
close(this->fd);
free(this);
}
bool PerfCounter_read(PerfCounter* this) {
if (this->fd == -1) {
return false;
}
uint64_t value;
int r = read(this->fd, &value, sizeof(value));
if (r != sizeof(value)) {
close(this->fd);
this->fd = perf_event_open(&this->events, this->pid, -1, -1, 0);
return false;
}
this->prevValue = this->value;
this->value = value;
return true;
}

54
linux/PerfCounter.h Normal file
View File

@ -0,0 +1,54 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_PerfCounter
#define HEADER_PerfCounter
/*
* This file is based on tiptop.
* by Erven ROHOU
* Copyright (c) 2011, 2012, 2014 Inria
* License: GNU General Public License version 2.
*/
#include <config.h>
#include <sys/types.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <inttypes.h>
#include <stdbool.h>
// The sys_perf_counter_open syscall and header files changed names
// between Linux 2.6.31 and 2.6.32. Do the mangling here.
#ifdef HAVE_LINUX_PERF_COUNTER_H
#include <linux/perf_counter.h>
#define STRUCT_NAME perf_counter_attr
#define SYSCALL_NUM __NR_perf_counter_open
#elif HAVE_LINUX_PERF_EVENT_H
#include <linux/perf_event.h>
#define STRUCT_NAME perf_event_attr
#define SYSCALL_NUM __NR_perf_event_open
#else
#error Sorry, performance counters not supported on this system.
#endif
typedef struct PerfCounter_ {
struct STRUCT_NAME events;
pid_t pid;
int fd;
uint64_t prevValue;
uint64_t value;
} PerfCounter;
#define PerfCounter_delta(pc_) ((pc_)->value - (pc_)->prevValue)
extern int PerfCounter_openFds;
PerfCounter* PerfCounter_new(pid_t pid, uint32_t type, uint64_t config);
void PerfCounter_delete(PerfCounter* this);
bool PerfCounter_read(PerfCounter* this);
#endif

Some files were not shown because too many files have changed in this diff Show More