338 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
e2ccc7b240 Prepare for release 2.0.1. 2016-03-07 18:32:06 -03:00
ef1e62d1fa Merge branch 'juanfra684-openbsd-mem-used' 2016-03-07 15:03:50 -03:00
453105e77a Merge branch 'Sp1l-master' 2016-03-07 16:58:47 -03:00
db80f202f2 Avoid global, as done by @gaod in #387. 2016-03-07 16:58:02 -03:00
98e43816a5 Merge branch 'master' of https://github.com/Sp1l/htop into Sp1l-master 2016-03-07 16:54:38 -03:00
694addceb5 Merge pull request #436 from mmcco/tuneup
Misc. OpenBSD tuneup and improvement
2016-03-07 16:38:39 -03:00
4623394456 Merge pull request #435 from mmcco/freebsd
Improve error reporting on FreeBSD libkvm call
2016-03-07 16:35:59 -03:00
4ad7aa6432 Merge branch 'openbsd-mem-used' of https://github.com/juanfra684/htop into juanfra684-openbsd-mem-used 2016-03-07 15:03:18 -03:00
4b780a3499 A few more OpenBSD fixes
Namely:

 o use malloc where an xCalloc slipped in

 o safeguard against an empty arg list - I don't think it's possible,
   but it would be potentially exploitable

 o we need to initialize the arg string to an empty string because we no
 longer use strlcpy(3)

 o annotate a tricky use of strlcpy(3)'s truncation
2016-03-05 23:38:12 -05:00
b08cb7352e Misc. OpenBSD tuneup and improvement
Including:

 o set *basenameEnd even in error cases (FreeBSD probably needs this)

 o use kvm_openfiles(3) rather than kvm_open(3) so that we can report
   errors (as with FreeBSD)

 o sanify the process argument list creation by using strlcat(3)

 o drop the pageSizeKb variable and use the PAGE_SIZE_KB macro directly,
   as the page size can't change anyway

 o clean up a few macros, add MINIMUM() and MAXIMUM() (should be
   mirrored to FreeBSD)

 o fix some syntax

 o add some useful comments
2016-03-05 23:23:29 -05:00
b886ecc479 Improve error reporting on FreeBSD libkvm call
This involves switching from kvm_open(3) to kvm_openfiles(3). The only
difference is that the latter has saner error reporting (see the man
page for details). We can now fatally report the error rather than just
calling assert(3).
2016-03-05 22:59:39 -05:00
63c55854e5 Merge pull request #388 from mhinz/add-p-to-help
Add "p" to Help
2016-02-29 22:03:45 -03:00
4f1bd232d8 Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-29 21:57:47 -03:00
84a69b1ea4 Release resource when using hwloc 2016-02-29 21:57:27 -03:00
e77811e99b any of these values may wrap 2016-02-29 21:57:03 -03:00
0a4a447d0d Add "p" to Help 2016-03-01 00:57:09 +01:00
cc8375f9ea FreeBSD: use KERN_PROC_PROC with kvm_getprocs to avoid erroneously combining LWPs 2016-02-28 22:41:50 -05:00
03af73bbc7 Merge pull request #427 from tcreech/master
Fix low CPU usage reporting in FreeBSD
2016-02-27 03:04:52 -03:00
0cf3cfa3af Fix low CPU usage reporting in FreeBSD 2016-02-26 23:23:27 -05:00
797bcd0961 Catch invalid IO values due to no permissions.
Display them properly. Not fully convinced of the "no perm" message...
2016-02-20 02:23:26 -02:00
baec4bdcb0 Try to retain last full name of a zombie process.
Once a process goes zombie on Linux, /proc/PID/cmdline
gets empty. So, when we detect it is a zombie we stop
reading this file.
For processes that were zombies before htop started,
there's no way to get the full name.
Closes #49.
2016-02-19 20:51:57 -02:00
a8a5e62760 Force -lgcov harder when running make coverage.
Shouldn't be needed, but I had to make this tweak to make this work again.
2016-02-19 13:40:21 -02:00
f5ddb974a1 Tweaks on the test suite, still problematic. 2016-02-19 13:40:00 -02:00
565101234a Remove old commented code. 2016-02-19 13:39:38 -02:00
d4741d5410 Fix error test — looks like nobody bumped into this one! 2016-02-19 13:39:16 -02:00
9668cd11ed Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-19 13:38:54 -02:00
c23d4f12d1 Fix behavior of ESC key, getting rid of the annoying delay.
Thank you @Explorer09 for the push!
Closes #417.
2016-02-19 13:38:02 -02:00
34431dc7fd Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-18 23:46:36 -02:00
42c4459375 Run through all command line arguments on Darwin.
Also fixes the basename offset for highlighting the basename.
Closes #379.
2016-02-18 23:45:17 -02:00
48254f92e4 Fix behavior of Panel on empty lists.
Closes #370.
2016-02-18 17:45:04 -02:00
5ee6715843 Scan threads for process state information.
Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread
and       e86692e869/ProcessList.c
This should be a fix for #361.
2016-02-18 14:57:09 -02:00
c18309466d Pre-reset 'show' for process.
This simplifies the protocol between the platform-independent
and platform-specific parts. The platform-specific parts
were supposed to re-determine the value of process->show
on each iteration, and the Darwin subsystem wasn't doing that.
Instead of adding the code to the Darwin part, I lifted the
burden of the OS-specific of resetting process->show: now
they can choose to hide a process if they want to (e.g.
detecting kernel threads) but are not required to
(e.g. on Darwin where we're not listing threads separately (yet?)).
Fixes tree view collapsing/expanding on OSX. Closes #416.
2016-02-18 14:32:49 -02:00
fe0ad86e6d Fix tree organization on OSX.
Closes #393.
2016-02-18 14:14:45 -02:00
dc4f145629 Merge pull request #413 from koresar/master
(Regression) Typo in the word "prority" -> "priority"
2016-02-17 10:38:38 -02:00
d43e709364 Typo in the word "priority" 2016-02-17 13:44:07 +11:00
0627a5f0e9 Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-16 14:52:08 -02:00
d10f4f615a Merge pull request #391 from gaod/master
Add MEM% for processes in process list on FreeBSD.
2016-02-16 14:52:02 -02:00
e0c364b9cc Fix reading of io_syscr and io_syscw.
Issue noticed by GCC6 -Wmisleading-indentation.
Thanks @JIghtuse and @Explorer09!
Closes #409.
2016-02-16 14:34:25 -02:00
fd216309d9 Merge pull request #380 from fingolfin/patch-1
Simplify autogen.sh
2016-02-14 21:17:19 -02:00
474d26cea8 Portable affinity using hwloc 2016-02-14 19:57:29 -02:00
471cd60635 Simplify autogen.sh
Make use of the autoreconf tool
2016-02-14 22:36:40 +01:00
f554f08fa9 Fix FreeBSD CPU% calculation 2016-02-14 22:21:11 +01:00
d753996b96 Fix memory percentage display on FreeBSD 2016-02-14 21:48:36 +01:00
b10278318b Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-14 17:47:29 +01:00
35657208d7 Disable the syscall on systems that don't have it.
Got a report in #397 that htop runs in NetBSD
masquerading as Linux and using a compatibility /proc
(like we used to in FreeBSD) and that it builds fine
apart from this syscall.
2016-02-14 12:05:35 -02:00
ae823c375a Make unsupported platform build again.
(Thanks @coypoop at #397 for the heads up!)
2016-02-14 11:45:47 -02:00
1b5025e6f5 Add support for cachedMem and fix usedMem on OpenBSD. 2016-02-14 13:04:18 +01:00
c66ac09a66 Add MEM% for processes in process list on FreeBSD. 2016-02-13 23:24:57 +08:00
0b70439316 Fix buffer reuse. 2016-02-13 02:18:28 -02:00
e9b32eb62f Fix memory accounting in Darwin.
htop currently expects m_size and m_resident in pages (Process.c).
According to the proc_info.h header, the values returned by libproc
are in bytes:
http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/proc_info.h
Eventually we should change the htop crossplatform API to expect memory
in bytes, but this is the smaller change that should fix it.
Closes #385.
2016-02-13 02:13:57 -02:00
3b9a4b1024 Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-12 23:34:28 -02:00
0e3cf6975f Fix crash when emptying column. Closes #381. 2016-02-12 23:33:53 -02:00
a618f477b2 Merge pull request #387 from gaod/master
Suppress compiler warnings on FreeBSD
2016-02-12 22:47:29 -02:00
cc4267cc5c Suppress compiler warnings. 2016-02-13 02:09:50 +08:00
d0d14da8d9 Fix implicit define isnan() 2016-02-12 14:37:24 +01:00
1bedac1ca2 Merge pull request #363 from myfreeweb/freebsd-memory-fix
Fix process memory on FreeBSD
2016-02-11 17:30:41 -02:00
011bf30d22 Minor tweaks to the README 2016-02-11 17:20:50 -02:00
c56b1a8830 Merge pull request #365 from deric/master
Add travis badge, update README
2016-02-11 17:18:20 -02:00
01edfcf4d4 link to the official website, paypal donate button 2016-02-11 19:17:00 +01:00
d916f6e6f0 remove Debian instructions 2016-02-11 18:58:20 +01:00
e46488463d Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-11 15:24:49 -02:00
8fa9da47de don't mention installing to /usr 2016-02-11 17:32:06 +01:00
d408c74465 add travis badge, code formatting, updated compilation instructions 2016-02-11 14:59:45 +01:00
bb0333e45d fix process memory on FreeBSD 2016-02-11 15:52:39 +03:00
b6f927bffa Merge pull request #359 from myfreeweb/freebsd-battery
Add FreeBSD battery support
2016-02-11 08:46:42 -02:00
f1d1d6e0d6 add FreeBSD battery support 2016-02-11 13:42:37 +03:00
132 changed files with 6582 additions and 1840 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

125
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,18 +280,65 @@ 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;
#if (HAVE_LIBHWLOC || HAVE_NATIVE_AFFINITY)
#if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY)
Panel* panel = st->panel;
Process* p = (Process*) Panel_getSelected(panel);
@ -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,12 +448,13 @@ 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" },
{ .key = " F4 \\: ",.info = "incremental name filtering" },
{ .key = " F5 t: ", .info = "tree view" },
{ .key = " p: ", .info = "toggle program path" },
{ .key = " u: ", .info = "show processes of a single user" },
{ .key = " H: ", .info = "hide/show user process threads" },
{ .key = " K: ", .info = "hide/show kernel threads" },
@ -394,26 +462,26 @@ 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" },
{ .key = " F9 k: ", .info = "kill process/tagged processes" },
{ .key = " F7 ]: ", .info = "higher priority (root only)" },
{ .key = " F8 [: ", .info = "lower priority (+ nice)" },
#if (HAVE_LIBHWLOC || HAVE_NATIVE_AFFINITY)
#if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY)
{ .key = " a: ", .info = "set CPU affinity" },
#endif
{ .key = " e: ", .info = "show process environment" },
{ .key = " i: ", .info = "set IO prority" },
{ .key = " i: ", .info = "set IO priority" },
{ .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 }
@ -449,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], "]");
@ -479,8 +547,8 @@ static Htop_Reaction actionHelp(State* st) {
for (int i = 0; helpLeft[i].key; i++) { mvaddstr(9+i, 0, helpLeft[i].key); }
for (int i = 0; helpRight[i].key; i++) { mvaddstr(9+i, 40, helpRight[i].key); }
attrset(CRT_colors[PROCESS_THREAD]);
mvaddstr(15, 32, "threads");
mvaddstr(16, 26, "threads");
mvaddstr(16, 32, "threads");
mvaddstr(17, 26, "threads");
attrset(CRT_colors[DEFAULT_COLOR]);
attrset(CRT_colors[HELP_BOLD]);
@ -555,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;
@ -570,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

@ -10,8 +10,13 @@ in the source distribution for its full text.
#include <stdlib.h>
#ifdef HAVE_LIBHWLOC
#include <hwloc/linux.h>
#elif HAVE_NATIVE_AFFINITY
#include <hwloc.h>
#if __linux__
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_THREAD
#else
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS
#endif
#elif HAVE_LINUX_AFFINITY
#include <sched.h>
#endif
@ -55,7 +60,7 @@ void Affinity_add(Affinity* this, int id) {
Affinity* Affinity_get(Process* proc, ProcessList* pl) {
hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
bool ok = (hwloc_linux_get_tid_cpubind(pl->topology, proc->pid, cpuset) == 0);
bool ok = (hwloc_get_proc_cpubind(pl->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0);
Affinity* affinity = NULL;
if (ok) {
affinity = Affinity_new(pl);
@ -76,15 +81,15 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl) {
bool Affinity_set(Process* proc, Affinity* this) {
hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
for (int i = 0; i < affinity->used; i++) {
hwloc_bitmap_set(cpuset, affinity->cpus[i]);
for (int i = 0; i < this->used; i++) {
hwloc_bitmap_set(cpuset, this->cpus[i]);
}
bool ok = (hwloc_linux_set_tid_cpubind(this->pl->topology, proc->pid, cpuset) == 0);
bool ok = (hwloc_set_proc_cpubind(this->pl->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0);
hwloc_bitmap_free(cpuset);
return ok;
}
#elif HAVE_NATIVE_AFFINITY
#elif HAVE_LINUX_AFFINITY
Affinity* Affinity_get(Process* proc, ProcessList* pl) {
cpu_set_t cpuset;

View File

@ -10,7 +10,12 @@ in the source distribution for its full text.
*/
#ifdef HAVE_LIBHWLOC
#elif HAVE_NATIVE_AFFINITY
#if __linux__
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_THREAD
#else
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS
#endif
#elif HAVE_LINUX_AFFINITY
#endif
#include "Process.h"
@ -36,7 +41,7 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl);
bool Affinity_set(Process* proc, Affinity* this);
#elif HAVE_NATIVE_AFFINITY
#elif HAVE_LINUX_AFFINITY
Affinity* Affinity_get(Process* proc, ProcessList* pl);

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,

144
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,6 +141,8 @@ void CRT_fatalError(const char* note) __attribute__ ((noreturn));
void CRT_handleSIGSEGV(int sgn);
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
}*/
const char *CRT_treeStrAscii[TREE_STR_COUNT] = {
@ -181,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),
@ -193,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),
@ -220,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,
@ -279,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),
@ -338,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),
@ -358,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),
@ -370,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),
@ -397,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),
@ -456,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),
@ -476,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),
@ -515,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;
@ -539,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) {
@ -553,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);
@ -573,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);
@ -587,11 +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;
@ -618,6 +716,7 @@ void CRT_init(int delay, int colorScheme) {
#else
mousemask(BUTTON1_RELEASED, NULL);
#endif
}
void CRT_done() {
@ -653,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,6 +129,8 @@ void CRT_fatalError(const char* note) __attribute__ ((noreturn));
void CRT_handleSIGSEGV(int sgn);
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
extern const char *CRT_treeStrAscii[TREE_STR_COUNT];
@ -134,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;
@ -148,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,102 @@
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
* OpenBSD: Various fixes and improvements
(thanks to Michael McConville and Juan Francisco Cantero Hurtado)
* FreeBSD: fix CPU and memory readings
(thanks to Tim Creech, Hung-Yi Chen, Bernard Spil, Greg V)
* FreeBSD: add battery support
(thanks to Greg V)
* Linux: Retain last-obtained name of a zombie process
* Mac OS X: Improve portability for OS X versions
(thanks to Michael Klein)
* Mac OS X: Fix reading command-line arguments and basename
* Mac OS X: Fix process state information
* Mac OS X: Fix tree view collapsing/expanding
* Mac OS X: Fix tree organization
* Mac OS X: Fix memory accounting
* Fix crash when emptying a column of meters
* Make Esc key more responsive
What's new in version 2.0.0

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",

229
INSTALL
View File

@ -1,229 +0,0 @@
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
Foundation, Inc.
This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.
Basic Installation
==================
These are generic installation instructions.
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 only 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. If you're
using `csh' on an old version of System V, you might need to type
`sh ./configure' instead to prevent `csh' from trying to execute
`configure' itself.
Running `configure' takes awhile. 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.
4. Type `make install' to install the programs and any data files and
documentation.
5. 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.
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=c89 CFLAGS=-O2 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 must use a version of `make' that
supports the `VPATH' variable, such as 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 `..'.
If you have to use a `make' that does not support the `VPATH'
variable, you have 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.
Installation Names
==================
By default, `make install' will install the package's files in
`/usr/local/bin', `/usr/local/man', etc. You can specify an
installation prefix other than `/usr/local' by giving `configure' the
option `--prefix=PATH'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
give `configure' the option `--exec-prefix=PATH', the package will use
PATH as the prefix for installing programs and libraries.
Documentation and other data files will still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=PATH' 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.
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'.
Optional Features
=================
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.
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 `--target=TYPE' option 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
will cause the specified gcc to be used as the C compiler (unless it is
overridden in the site shell script).
`configure' Invocation
======================
`configure' recognizes the following options to control how it
operates.
`--help'
`-h'
Print a summary of the options to `configure', and exit.
`--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.
`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,9 +115,7 @@ void InfoScreen_run(InfoScreen* this) {
Panel_draw(panel, true);
if (this->inc->active)
move(LINES-1, CRT_cursorX);
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,8 +203,11 @@ 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" AM_LDFLAGS="-lgcov"
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov"
.c.h:
@srcdir@/scripts/MakeHeader.py $<
@ -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) {
@ -50,7 +56,10 @@ static void MetersPanel_delete(Object* object) {
void MetersPanel_setMoving(MetersPanel* this, bool moving) {
Panel* super = (Panel*) this;
this->moving = moving;
((ListItem*)Panel_getSelected(super))->moving = moving;
ListItem* selected = (ListItem*)Panel_getSelected(super);
if (selected) {
selected->moving = moving;
}
if (!moving) {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
Panel_setDefaultBar(super);
@ -58,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) {
@ -96,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;
}
@ -119,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 '-':
@ -135,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'] : "",

65
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,12 +449,22 @@ 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;
}
// ensure selection within bounds
if (this->selected < 0) {
if (this->selected < 0 || size == 0) {
this->selected = 0;
this->needsRedraw = true;
} else if (this->selected >= size) {
@ -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

148
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);
}
@ -335,7 +353,10 @@ void Process_outputRate(RichString* str, char* buffer, int n, double rate, int c
largeNumberColor = CRT_colors[PROCESS];
processMegabytesColor = CRT_colors[PROCESS];
}
if (rate < ONE_K) {
if (rate == -1) {
int len = snprintf(buffer, n, " no perm ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
} else if (rate < ONE_K) {
int len = snprintf(buffer, n, "%7.2f B/s ", rate);
RichString_appendn(str, processColor, buffer, len);
} else if (rate < ONE_K * ONE_K) {
@ -350,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];
@ -358,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 {
@ -391,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;
@ -402,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;
@ -414,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];
@ -446,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] = ' ';
@ -466,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]);
@ -514,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) {
@ -545,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:
@ -590,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

@ -22,6 +22,10 @@ in the source distribution for its full text.
#include "Process.h"
#include "Settings.h"
#ifdef HAVE_LIBHWLOC
#include <hwloc.h>
#endif
#ifndef MAX_NAME
#define MAX_NAME 128
#endif
@ -104,6 +108,11 @@ ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable*
}
void ProcessList_done(ProcessList* this) {
#ifdef HAVE_LIBHWLOC
if (this->topologyOk) {
hwloc_topology_destroy(this->topology);
}
#endif
Hashtable_delete(this->processTable);
Vector_delete(this->processes);
Vector_delete(this->processes2);
@ -115,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);
}
}
@ -164,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);
}
@ -191,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);
@ -234,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;
@ -307,6 +345,7 @@ void ProcessList_scan(ProcessList* this) {
for (int i = 0; i < Vector_size(this->processes); i++) {
Process* p = (Process*) Vector_get(this->processes, i);
p->updated = false;
p->show = true;
}
this->totalTasks = 0;

View File

@ -16,6 +16,10 @@ in the source distribution for its full text.
#include "Process.h"
#include "Settings.h"
#ifdef HAVE_LIBHWLOC
#include <hwloc.h>
#endif
#ifndef MAX_NAME
#define MAX_NAME 128
#endif

59
README
View File

@ -1,50 +1,61 @@
htop
[![Build Status](https://travis-ci.org/hishamhm/htop.svg?branch=master)](https://travis-ci.org/hishamhm/htop)
[![PayPal donate](https://img.shields.io/badge/paypal-donate-green.svg)](http://hisham.hm/htop/index.php?page=donate)
[htop](http://hisham.hm/htop/)
====
by Hisham Muhammad <hisham@gobolinux.org>
2004 - 2015
by Hisham Muhammad <hisham@gobolinux.org> (2004 - 2016)
Introduction
------------
This is htop, an interactive process viewer.
It requires ncurses. It is developed primarily on Linux,
This is `htop`, an interactive process viewer.
It requires `ncurses`. It is developed primarily on Linux,
but we also have code for running under FreeBSD and Mac OS X
(help and testing are wanted for these platforms!)
This software has evolved considerably over the years,
and is reasonably complete, but there is always room for improvement.
Comparison between 'htop' and classic 'top'
Comparison between `htop` and classic `top`
-------------------------------------------
* In 'htop' you can scroll the list vertically and horizontally
* In `htop` you can scroll the list vertically and horizontally
to see all processes and full command lines.
* In 'top' you are subject to a delay for each unassigned
* In `top` you are subject to a delay for each unassigned
key you press (especially annoying when multi-key escape
sequences are triggered by accident).
* 'htop' starts faster ('top' seems to collect data for a while
* `htop` starts faster (`top` seems to collect data for a while
before displaying anything).
* In 'htop' you don't need to type the process number to
kill a process, in 'top' you do.
* In 'htop' you don't need to type the process number or
the priority value to renice a process, in 'top' you do.
* In 'htop' you can kill multiple processes at once.
* 'top' is older, hence, more tested.
* In `htop` you don't need to type the process number to
kill a process, in `top` you do.
* In `htop` you don't need to type the process number or
the priority value to renice a process, in `top` you do.
* In `htop` you can kill multiple processes at once.
* `top` is older, hence, more tested.
Compilation instructions
------------------------
This program is distributed as a standard autotools-based package.
See the INSTALL file for detailed instructions, but you are
probably used to the common `./configure`/`make`/`make install` routine.
See the [INSTALL](/INSTALL) file for detailed instructions.
When fetching the code from the development repository, you need
to run the `./autogen.sh` script, which in turn requires autotools
to be installed.
When compiling from a [release tarball](https://hisham.hm/htop/releases/), run:
See the manual page (man htop) or the on-line help ('F1' or 'h'
inside htop) for a list of supported key commands.
./configure && make
if not all keys work check your curses configuration.
For compiling sources downloaded from the Git repository, run:
./autogen.sh && ./configure && make
By default `make install` will install into `/usr/local`, for changing
the path use `./configure --prefix=/some/path`.
See the manual page (`man htop`) or the on-line help ('F1' or 'h'
inside `htop`) for a list of supported key commands.
If not all keys work check your curses configuration.
## License
GNU General Public License, version 2 (GPL-2.0)

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,7 +253,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
}
int prevCh = ch;
ch = getch();
ch = Panel_getCh(panelFocus);
HandlerResult result = IGNORED;
if (ch == KEY_MOUSE) {
@ -207,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) {
@ -244,28 +311,11 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
redraw = false;
continue;
}
else if (ch == 27) {
int ch2 = getch();
if (ch2 != ERR) {
switch(ch2)
{
case 'h':
ch = KEY_LEFT;
break;
case 'j':
ch = KEY_DOWN;
break;
case 'k':
ch = KEY_UP;
break;
case 'l':
ch = KEY_RIGHT;
break;
default:
ungetch(ch2);
break;
}
}
switch (ch) {
case KEY_ALT('H'): ch = KEY_LEFT; break;
case KEY_ALT('J'): ch = KEY_DOWN; break;
case KEY_ALT('K'): ch = KEY_UP; break;
case KEY_ALT('L'): ch = KEY_RIGHT; break;
}
redraw = true;
if (Panel_eventHandlerFn(panelFocus)) {
@ -295,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;
@ -323,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

@ -1,15 +1,3 @@
#!/bin/sh
glibtoolize=$(which glibtoolize 2> /dev/null)
if [ ${#glibtoolize} -gt 0 ]
then libtoolize=glibtoolize
else libtoolize=libtoolize
fi
mkdir -p m4
aclocal -I m4
autoconf
autoheader
$libtoolize --copy --force
automake --add-missing --copy
autoreconf --install --force

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.0],[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,8 +241,18 @@ if test "$my_htop_platform" = "openbsd"; then
AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"])
fi
AC_ARG_ENABLE(native_affinity, [AC_HELP_STRING([--enable-native-affinity], [enable native sched_setaffinity and sched_getaffinity for affinity support, disables hwloc])], ,enable_native_affinity="yes")
if test "x$enable_native_affinity" = xyes -a "x$cross_compiling" = xno; then
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([
AC_LANG_PROGRAM([[
@ -225,20 +265,90 @@ if test "x$enable_native_affinity" = xyes -a "x$cross_compiling" = xno; then
if (errno == ENOSYS) return 1;
]])],
[AC_MSG_RESULT([yes])],
[enable_native_affinity=no
[enable_linux_affinity=no
AC_MSG_RESULT([no])])
fi
if test "x$enable_native_affinity" = xyes; then
AC_DEFINE(HAVE_NATIVE_AFFINITY, 1, [Define if native sched_setaffinity and sched_getaffinity are to be used.])
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_linux_get_tid_cpubind], [], [missing_libraries="$missing_libraries libhwloc"])
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

@ -13,6 +13,8 @@ in the source distribution for its full text.
#include <string.h>
#include <stdio.h>
#include <mach/mach.h>
/*{
#include "Settings.h"
#include "DarwinProcessList.h"
@ -24,6 +26,7 @@ typedef struct DarwinProcess_ {
uint64_t utime;
uint64_t stime;
bool taskAccess;
} DarwinProcess;
}*/
@ -45,6 +48,7 @@ DarwinProcess* DarwinProcess_new(Settings* settings) {
this->utime = 0;
this->stime = 0;
this->taskAccess = true;
return this;
}
@ -69,7 +73,7 @@ void DarwinProcess_setStartTime(Process *proc, struct extern_proc *ep, time_t no
strftime(proc->starttime_show, 7, ((proc->starttime_ctime > now - 86400) ? "%R " : "%b%d "), &date);
}
char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int show_args ) {
char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int* basenameOffset) {
/* This function is from the old Mac version of htop. Originally from ps? */
int mib[3], argmax, nargs, c = 0;
size_t size;
@ -167,13 +171,7 @@ char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int show_args ) {
/* Save where the argv[0] string starts. */
sp = cp;
/*
* Iterate through the '\0'-terminated strings and convert '\0' to ' '
* until a string is found that has a '=' character in it (or there are
* no more strings in procargs). There is no way to deterministically
* know where the command arguments end and the environment strings
* start, which is why the '=' character is searched for as a heuristic.
*/
*basenameOffset = 0;
for ( np = NULL; c < nargs && cp < &procargs[size]; cp++ ) {
if ( *cp == '\0' ) {
c++;
@ -183,49 +181,11 @@ char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int show_args ) {
}
/* Note location of current '\0'. */
np = cp;
if ( !show_args ) {
/*
* Don't convert '\0' characters to ' '.
* However, we needed to know that the
* command name was terminated, which we
* now know.
*/
break;
if (*basenameOffset == 0) {
*basenameOffset = cp - sp;
}
}
}
#if 0
/*
* If eflg is non-zero, continue converting '\0' characters to ' '
* characters until no more strings that look like environment settings
* follow.
*/
if ( ( eflg != 0 )
&& ( ( getuid( ) == 0 )
|| ( k->kp_eproc.e_pcred.p_ruid == getuid( ) ) ) ) {
for ( ; cp < &procargs[size]; cp++ ) {
if ( *cp == '\0' ) {
if ( np != NULL ) {
if ( &np[1] == cp ) {
/*
* Two '\0' characters in a row.
* This should normally only
* happen after all the strings
* have been seen, but in any
* case, stop parsing.
*/
break;
}
/* Convert previous '\0'. */
*np = ' ';
}
/* Note location of current '\0'. */
np = cp;
}
}
}
#endif
/*
* sp points to the beginning of the arguments/environment string, and
@ -235,6 +195,9 @@ char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int show_args ) {
/* Empty or unterminated string. */
goto ERROR_B;
}
if (*basenameOffset == 0) {
*basenameOffset = np - sp;
}
/* Make a copy of the string. */
retval = xStrdup(sp);
@ -248,7 +211,8 @@ ERROR_B:
free( procargs );
ERROR_A:
retval = xStrdup(k->kp_proc.p_comm);
*basenameOffset = strlen(retval);
return retval;
}
@ -275,35 +239,22 @@ void DarwinProcess_setFromKInfoProc(Process *proc, struct kinfo_proc *ps, time_t
proc->ppid = ps->kp_eproc.e_ppid;
proc->pgrp = ps->kp_eproc.e_pgid;
proc->session = 0; /* TODO Get the session id */
proc->tgid = ps->kp_eproc.e_tpgid;
proc->tpgid = ps->kp_eproc.e_tpgid;
proc->tgid = proc->pid;
proc->st_uid = ps->kp_eproc.e_ucred.cr_uid;
/* e_tdev = (major << 24) | (minor & 0xffffff) */
/* e_tdev == -1 for "no device" */
proc->tty_nr = ps->kp_eproc.e_tdev & 0xff; /* TODO tty_nr is unsigned */
DarwinProcess_setStartTime(proc, ep, now);
/* The command is from the old Mac htop */
char *slash;
proc->comm = DarwinProcess_getCmdLine(ps, false);
slash = strrchr(proc->comm, '/');
proc->basenameOffset = (NULL != slash) ? (slash - proc->comm) : 0;
proc->comm = DarwinProcess_getCmdLine(ps, &(proc->basenameOffset));
}
/* Mutable information */
proc->nice = ep->p_nice;
proc->priority = ep->p_priority;
/* Set the state */
switch(ep->p_stat) {
case SIDL: proc->state = 'I'; break;
case SRUN: proc->state = 'R'; break;
case SSLEEP: proc->state = 'S'; break;
case SSTOP: proc->state = 'T'; break;
case SZOMB: proc->state = 'Z'; break;
default: proc->state = '?'; break;
}
proc->state = (ep->p_stat == SZOMB) ? 'Z' : '?';
/* Make sure the updated flag is set */
proc->updated = true;
@ -327,8 +278,8 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess *proc, DarwinProcessList
proc->super.time = (pti.pti_total_system + pti.pti_total_user) / 10000000;
proc->super.nlwp = pti.pti_threadnum;
proc->super.m_size = pti.pti_virtual_size / 1024;
proc->super.m_resident = pti.pti_resident_size / 1024;
proc->super.m_size = pti.pti_virtual_size / 1024 / PAGE_SIZE_KB;
proc->super.m_resident = pti.pti_resident_size / 1024 / PAGE_SIZE_KB;
proc->super.majflt = pti.pti_faults;
proc->super.percent_mem = (double)pti.pti_resident_size * 100.0
/ (double)dpl->host_info.max_mem;
@ -342,3 +293,71 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess *proc, DarwinProcessList
dpl->super.runningTasks += pti.pti_numrunning;
}
}
/*
* Scan threads for process state information.
* Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread
* and https://github.com/max-horvath/htop-osx/blob/e86692e869e30b0bc7264b3675d2a4014866ef46/ProcessList.c
*/
void DarwinProcess_scanThreads(DarwinProcess *dp) {
Process* proc = (Process*) dp;
kern_return_t ret;
if (!dp->taskAccess) {
return;
}
if (proc->state == 'Z') {
return;
}
task_t port;
ret = task_for_pid(mach_task_self(), proc->pid, &port);
if (ret != KERN_SUCCESS) {
dp->taskAccess = false;
return;
}
task_info_data_t tinfo;
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;
}
thread_array_t thread_list;
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;
}
integer_t run_state = 999;
for (unsigned int i = 0; i < thread_count; i++) {
thread_info_data_t thinfo;
mach_msg_type_number_t thread_info_count = THREAD_BASIC_INFO_COUNT;
ret = thread_info(thread_list[i], THREAD_BASIC_INFO, (thread_info_t)thinfo, &thread_info_count);
if (ret == KERN_SUCCESS) {
thread_basic_info_t basic_info_th = (thread_basic_info_t) thinfo;
if (basic_info_th->run_state < run_state) {
run_state = basic_info_th->run_state;
}
mach_port_deallocate(mach_task_self(), thread_list[i]);
}
}
vm_deallocate(mach_task_self(), (vm_address_t) thread_list, sizeof(thread_port_array_t) * thread_count);
mach_port_deallocate(mach_task_self(), port);
char state = '?';
switch (run_state) {
case TH_STATE_RUNNING: state = 'R'; break;
case TH_STATE_STOPPED: state = 'S'; break;
case TH_STATE_WAITING: state = 'W'; break;
case TH_STATE_UNINTERRUPTIBLE: state = 'U'; break;
case TH_STATE_HALTED: state = 'H'; break;
}
proc->state = state;
}

View File

@ -19,6 +19,7 @@ typedef struct DarwinProcess_ {
uint64_t utime;
uint64_t stime;
bool taskAccess;
} DarwinProcess;
@ -32,10 +33,17 @@ bool Process_isThread(Process* this);
void DarwinProcess_setStartTime(Process *proc, struct extern_proc *ep, time_t now);
char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int show_args );
char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int* basenameOffset);
void DarwinProcess_setFromKInfoProc(Process *proc, struct kinfo_proc *ps, time_t now, bool exists);
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess *proc, DarwinProcessList *dpl);
/*
* Scan threads for process state information.
* Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread
* and https://github.com/max-horvath/htop-osx/blob/e86692e869e30b0bc7264b3675d2a4014866ef46/ProcessList.c
*/
void DarwinProcess_scanThreads(DarwinProcess *dp);
#endif

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,58 +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_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

@ -6,10 +6,20 @@ in the source distribution for its full text.
*/
#include "BatteryMeter.h"
#include <sys/sysctl.h>
void Battery_getData(double* level, ACPresence* isOnAC) {
// TODO
*level = -1;
*isOnAC = AC_ERROR;
}
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;
}

View File

@ -11,5 +11,4 @@ in the source distribution for its full text.
void Battery_getData(double* level, ACPresence* isOnAC);
#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 },
};
@ -115,11 +115,11 @@ void FreeBSDProcess_writeField(Process* this, RichString* str, ProcessField fiel
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1;
switch (field) {
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 (settings->sortKey) {
switch ((int) settings->ss->sortKey) {
// add FreeBSD-specific fields here
case JID:
return (p1->jid - p2->jid);

View File

@ -14,7 +14,9 @@ in the source distribution for its full text.
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <err.h>
#include <fcntl.h>
#include <limits.h>
#include <string.h>
/*{
@ -84,14 +86,13 @@ 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];
FreeBSDProcessList* fpl = xCalloc(1, sizeof(FreeBSDProcessList));
ProcessList* pl = (ProcessList*) fpl;
ProcessList_init(pl, Class(FreeBSDProcess), usersTable, pidWhiteList, userId);
size_t len;
// physical memory in system: hw.physmem
// physical page size: hw.pagesize
// usable pagesize : vm.stats.vm.v_page_size
@ -150,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);
@ -174,12 +175,14 @@ 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;
}
fpl->kd = kvm_open(NULL, "/dev/null", NULL, 0, NULL);
assert(fpl->kd);
fpl->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf);
if (fpl->kd == NULL) {
errx(1, "kvm_open: %s", errbuf);
}
return pl;
}
@ -212,9 +215,6 @@ static inline void FreeBSDProcessList_scanCPUTime(ProcessList* pl) {
unsigned long *cp_time_n; // old clicks state
unsigned long *cp_time_o; // current clicks state
unsigned long long total_o = 0;
unsigned long long total_n = 0;
unsigned long long total_d = 0;
unsigned long cp_time_d[CPUSTATES];
double cp_time_p[CPUSTATES];
@ -251,6 +251,9 @@ static inline void FreeBSDProcessList_scanCPUTime(ProcessList* pl) {
}
// 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];
@ -395,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);
@ -424,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];
@ -456,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);
}
@ -477,25 +480,21 @@ void ProcessList_goThroughEntries(ProcessList* this) {
}
// from FreeBSD source /src/usr.bin/top/machine.c
proc->m_size = kproc->ki_size / 1024;
proc->m_resident = kproc->ki_rssize * pageSizeKb;
proc->m_size = kproc->ki_size / 1024 / pageSizeKb;
proc->m_resident = kproc->ki_rssize;
proc->percent_mem = (proc->m_resident * PAGE_SIZE_KB) / (double)(this->totalMem) * 100.0;
proc->nlwp = kproc->ki_numthreads;
proc->time = (kproc->ki_runtime + 5000) / 10000;
proc->percent_cpu = 100.0 * ((double)kproc->ki_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->ki_comm) == 0 ) {
isIdleProcess = true;
} else {
if (cpus > 1)
proc->percent_cpu = proc->percent_cpu / (double) cpus;
}
}
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

@ -55,7 +55,6 @@ typedef struct FreeBSDProcessList_ {
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
void ProcessList_delete(ProcessList* this);

View File

@ -24,6 +24,7 @@ in the source distribution for its full text.
#include <sys/resource.h>
#include <vm/vm_param.h>
#include <time.h>
#include <math.h>
/*{
#include "Action.h"
@ -38,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 },
@ -79,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;

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 '-').

217
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;
@ -176,7 +183,8 @@ int main(int argc, char** argv) {
#ifdef HAVE_PROC
if (access(PROCDIR, R_OK) != 0) {
fprintf("Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
exit(1);
}
#endif
@ -196,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));
@ -227,195 +237,7 @@ int main(int argc, char** argv) {
millisleep(75);
ProcessList_scan(pl);
ScreenManager_run(scr, NULL, NULL);
/*
FunctionBar_draw(defaultBar, NULL);
int acc = 0;
bool follow = false;
struct timeval tv;
double oldTime = 0.0;
int ch = ERR;
int closeTimeout = 0;
bool drawPanel = true;
bool collapsed = false;
Htop_Action keys[KEY_MAX] = { NULL };
setBindings(keys);
Platform_setBindings(keys);
bool quit = false;
int sortTimeout = 0;
int resetSortTimeout = 5;
bool doRefresh = true;
bool forceRecalculate = false;
while (!quit) {
gettimeofday(&tv, NULL);
double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000);
bool timeToRecalculate = (newTime - oldTime > settings->delay);
if (newTime < oldTime) timeToRecalculate = true; // clock was adjusted?
int following = follow ? MainPanel_selectedPid((MainPanel*)panel) : -1;
if (timeToRecalculate) {
Header_draw(header);
oldTime = newTime;
}
if (doRefresh) {
if (timeToRecalculate || forceRecalculate) {
ProcessList_scan(pl);
forceRecalculate = false;
}
if (sortTimeout == 0 || settings->treeView) {
ProcessList_sort(pl);
sortTimeout = 1;
}
ProcessList_rebuildPanel(pl, true, following, IncSet_filter(inc));
drawPanel = true;
}
doRefresh = true;
if (settings->treeView) {
Process* p = (Process*) Panel_getSelected(panel);
if (p) {
if (!p->showChildren && !collapsed) {
FunctionBar_setLabel(defaultBar, KEY_F(6), "Expand");
FunctionBar_draw(defaultBar, NULL);
} else if (p->showChildren && collapsed) {
FunctionBar_setLabel(defaultBar, KEY_F(6), "Collap");
FunctionBar_draw(defaultBar, NULL);
}
collapsed = !p->showChildren;
}
} else {
collapsed = false;
}
if (drawPanel) {
Panel_draw(panel, true);
}
int prev = ch;
if (inc->active)
move(LINES-1, CRT_cursorX);
ch = getch();
if (ch == ERR) {
if (!inc->active)
sortTimeout--;
if (prev == ch && !timeToRecalculate) {
closeTimeout++;
if (closeTimeout == 100) {
break;
}
} else
closeTimeout = 0;
drawPanel = false;
continue;
}
drawPanel = true;
Htop_Reaction reaction = HTOP_OK;
if (ch == KEY_MOUSE) {
MEVENT mevent;
int ok = getmouse(&mevent);
if (ok == OK) {
if (mevent.bstate & BUTTON1_CLICKED) {
if (mevent.y == panel->y) {
int x = panel->scrollH + mevent.x + 1;
ProcessField field = ProcessList_keyAt(pl, x);
if (field == settings->sortKey) {
Settings_invertSortOrder(settings);
settings->treeView = false;
reaction |= HTOP_REDRAW_BAR;
} else {
reaction |= setSortKey(settings, field);
}
sortTimeout = 0;
ch = ERR;
} else if (mevent.y >= panel->y + 1 && mevent.y < LINES - 1) {
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
follow = true;
ch = ERR;
} if (mevent.y == LINES - 1) {
ch = FunctionBar_synthesizeEvent(inc->bar, mevent.x);
}
} else if (mevent.bstate & BUTTON4_CLICKED) {
ch = KEY_UP;
#if NCURSES_MOUSE_VERSION > 1
} else if (mevent.bstate & BUTTON5_CLICKED) {
ch = KEY_DOWN;
#endif
}
}
}
if (inc->active) {
doRefresh = IncSet_handleKey(inc, ch, panel, getMainPanelValue, NULL);
if (!inc->active) {
follow = true;
}
continue;
}
if (ch < 255 && isdigit((char)ch)) {
if (Panel_size(panel) == 0) continue;
pid_t pid = ch-48 + acc;
for (int i = 0; i < ProcessList_size(pl); i++) {
Panel_setSelected(panel, i);
Process* p = (Process*) Panel_getSelected(panel);
if (p && p->pid == pid) {
break;
}
}
acc = pid * 10;
if (acc > 10000000)
acc = 0;
continue;
} else {
acc = 0;
}
if(ch != ERR && keys[ch]) {
reaction |= (keys[ch])(&state);
} else {
doRefresh = false;
sortTimeout = resetSortTimeout;
Panel_onKey(panel, ch);
}
// Reaction handlers:
if (reaction & HTOP_REDRAW_BAR) {
updateTreeFunctions(defaultBar, settings->treeView);
IncSet_drawBar(inc);
}
if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(pl, Panel_getHeader(panel));
}
if (reaction & HTOP_REFRESH) {
doRefresh = true;
sortTimeout = 0;
}
if (reaction & HTOP_RECALCULATE) {
forceRecalculate = true;
sortTimeout = 0;
}
if (reaction & HTOP_SAVE_SETTINGS) {
settings->changed = true;
}
if (reaction & HTOP_QUIT) {
quit = true;
}
follow = (reaction & HTOP_KEEP_FOLLOWING);
}
*/
ScreenManager_run(scr, NULL, NULL, NULL);
attron(CRT_colors[RESET_COLOR]);
mvhline(LINES-1, 0, ' ', COLS);
@ -428,11 +250,6 @@ int main(int argc, char** argv) {
Header_delete(header);
ProcessList_delete(pl);
/*
FunctionBar_delete((Object*)defaultBar);
Panel_delete((Object*)panel);
*/
ScreenManager_delete(scr);
UsersTable_delete(ut);

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)

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