Skip to content

Commit

Permalink
sysutils/wmmemfree: modernize, unbreak, undeprecate the port (+)
Browse files Browse the repository at this point in the history
When I ported this dockapp 20 years ago, it was not uncommon to
peek inside ``struct vmmeter'' and other kernel structures with
kvm_read(3) to obtain various data (e.g. page counters).

These interfaces had always been fragile and potentially unsafe
(due to sgid-to-kmem requirement), so let's switch to sysctl(3)
instead as it provides the same data via documented, stable API.

Address another problem while here: the dockapp tried to update
the window from the signal handler, and no Xlib function can be
safely called within a signal handler*.  Do as advised and only
raise a "need update" flag upon receiving a signal, then gather
new data and redraw the window in the main loop.  This approach,
however, cannot work without modifying process_events() because
of the blocking nature of XNextEvent(3) -- put another select(2)
call in front so it can relinquish control to the outer loop.

[*] http://www-h.eng.cam.ac.uk/help/tpl/graphics/X/signals.html
  • Loading branch information
Alexey Dokuchaev authored and Alexey Dokuchaev committed Jul 26, 2023
1 parent 69d3a97 commit c8bcf96
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 154 deletions.
12 changes: 3 additions & 9 deletions sysutils/wmmemfree/Makefile
Expand Up @@ -4,21 +4,15 @@ PORTREVISION= 4
CATEGORIES= sysutils windowmaker
MASTER_SITES= SUNSITE/X11/xutils

MAINTAINER= ports@FreeBSD.org
MAINTAINER= danfe@FreeBSD.org
COMMENT= Memory and swap monitoring dockapp

DEPRECATED= Broken on all supported FreeBSD releases for more than an year
EXPIRATION_DATE= 2023-08-16
BROKEN_FreeBSD_12= fails to compile: read_mem.c: invalid use of undefined type 'struct vmmeter'
BROKEN_FreeBSD_13= fails to compile: read_mem.c: invalid use of undefined type 'struct vmmeter'
BROKEN_FreeBSD_14= fails to compile: read_mem.c: invalid use of undefined type 'struct vmmeter'
BROKEN_mips= fails to compile: mem_freebsd.c: storage size of 'vm' isn't known
BROKEN_mips64= fails to compile: mem_freebsd.c: storage size of 'vm' isn't known
LICENSE= GPLv2+

USES= localbase tar:bzip2 xorg
USE_XORG= x11 xext xpm

PLIST_FILES= "@(,kmem,2555) bin/wmmemfree" man/man1/${PORTNAME}.1.gz
PLIST_FILES= bin/${PORTNAME} man/man1/${PORTNAME}.1.gz

post-patch:
@${CP} ${FILESDIR}/mem_freebsd.* ${WRKSRC}
Expand Down
150 changes: 54 additions & 96 deletions sysutils/wmmemfree/files/mem_freebsd.c
@@ -1,118 +1,76 @@
/*
* mem_freebsd.c - get memory status
*
* Copyright (C) 2003 Alexey Dokuchaev <danfe@regency.nsu.ru>
* Parts are Copyright (C) 1993-2003 FreeBSD Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
*/

#include <sys/types.h>
#include <sys/vmmeter.h>
#include <fcntl.h>
#include <kvm.h>
#include <nlist.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/sysctl.h>
#include <vm/vm_param.h>
#include <unistd.h>

#include "mem_freebsd.h"
long long int mem_total, mem_used, mem_buffers, mem_cached;
long long int swp_total, swp_used;

long long int mem_total, mem_used, mem_free, mem_buffers, mem_cached;
long long int swp_total, swp_used, swp_free;

static kvm_t *kd;
struct nlist nlst[] = { {"_cnt"}, {"_bufspace"}, {0} };
enum { total, active, laundry, wired, buffers, swap };
int mib[swap - total + 1][4];
/*
* Values below are in kilobytes: we don't need higher granulation,
* and it allows to keep numbers relatively small.
*/
unsigned int mem[swap - total];
int pagesize;

void
mem_init(void)
mem_init()
{
int pagesize;

if (!(kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")))
{
perror("kvm_open");
exit(1);
}
size_t len = 4;

kvm_nlist(kd, nlst);
/* Precache MIB name vectors for faster lookups later */
sysctlnametomib("vm.stats.vm.v_page_count", mib[total], &len);
sysctlnametomib("vm.stats.vm.v_active_count", mib[active], &len);
sysctlnametomib("vm.stats.vm.v_laundry_count", mib[laundry], &len);
sysctlnametomib("vm.stats.vm.v_wire_count", mib[wired], &len);

if (!nlst[0].n_type)
{
perror("kvm_nlist");
exit(1);
}
len = 2;
sysctlnametomib("vfs.bufspace", mib[buffers], &len);
sysctlnametomib("vm.swap_info", mib[swap], &len);

seteuid(getuid());
setegid(getgid());
pagesize = getpagesize() / 1024;
}

if (geteuid() != getuid() || getegid() != getgid())
{
perror("sete?id");
exit(1);
static void
get_swap_info()
{
struct xswdev xsw;
size_t len = sizeof(xsw);
int n;

for (swp_total = swp_used = n = 0; ; ++n) {
mib[swap][2] = n;
if (sysctl(mib[swap], 3, &xsw, &len, NULL, 0) == -1)
break;
swp_total += pagesize * xsw.xsw_nblks;
swp_used += pagesize * xsw.xsw_used;
}
}

void
mem_getfree()
{
struct vmmeter vm;
struct kvm_swap sw;
int bufspace = 0;
unsigned long cnt_offset;
unsigned long bufspace_offset;

static int firsttime = 1;
static time_t lasttime = 0;
time_t curtime;

if ((kvm_read(kd, nlst[0].n_value, &vm, sizeof(vm))
!= sizeof(vm)) ||
(kvm_read(kd, nlst[1].n_value, &bufspace, sizeof(bufspace))
!= sizeof(bufspace)))
{
perror("kvm_read");
exit(1);
}
size_t len = 4;
int n;

mem_total = vm.v_page_count;
mem_free = vm.v_free_count;
mem_used = mem_total - mem_free;
mem_cached = vm.v_cache_count;
mem_buffers = bufspace / vm.v_page_size;
for (n = 0; n < buffers - total; ++n)
sysctl(mib[n], 4, &mem[n], &len, NULL, 0);
sysctl(mib[buffers], 2, &mem[buffers], &len, NULL, 0);

/*
* Only calculate when first time or when changes took place.
* Do not call it more than 1 time per 2 seconds; otherwise
* it can eat up to 50% of CPU time on heavy swap activity.
* See the following links for explanation which pages we consider
* free and used (cf. Linux vs. FreeBSD):
* https://unix.stackexchange.com/questions/14102/real-memory-usage
* https://unix.stackexchange.com/questions/134862/what-do-the-different-memory-counters-in-freebsd-mean
*/

curtime = time(NULL);

if (firsttime || curtime > lasttime + 5)
{
if (kvm_getswapinfo(kd, &sw, 1, 0) >= 0 &&
sw.ksw_total)
{
swp_total = sw.ksw_total;
swp_used = sw.ksw_used;
swp_free = swp_total - swp_used;
}
firsttime = 0;
lasttime = curtime;
}
mem_total = pagesize * mem[total];
/* On FreeBSD, "Laundry" had replaced "Cache" in November 2016 */
mem_cached = pagesize * mem[laundry];
mem_buffers = mem[buffers] / 1024;
mem_used = pagesize * (mem[active] + mem[wired]);
mem_used += mem_cached + mem_buffers;

get_swap_info();
}
32 changes: 0 additions & 32 deletions sysutils/wmmemfree/files/mem_freebsd.h

This file was deleted.

2 changes: 1 addition & 1 deletion sysutils/wmmemfree/files/patch-Makefile
Expand Up @@ -18,7 +18,7 @@
INST=install
MANUAL=$(PROG).1
-LIBS=-L/usr/X11R6/lib -lX11 -lXext -lXpm
+LIBS+=-lX11 -lXext -lXpm -lkvm
+LIBS+=-lX11 -lXext -lXpm

all: $(PROG)

39 changes: 39 additions & 0 deletions sysutils/wmmemfree/files/patch-dockapp.c
@@ -0,0 +1,39 @@
--- dockapp.c.orig 2003-03-22 18:30:01 UTC
+++ dockapp.c
@@ -41,7 +41,7 @@
#define WINDOW_HEIGHT 64

Display *display;
-int screen;
+int screen, x11fd;
Window iconwindow, window, mapwindow;
Colormap colormap;
GC gc;
@@ -114,6 +114,7 @@ void make_window()
fprintf(stderr, "Could not open display %s\n", opt_display);
exit(1);
}
+ x11fd = ConnectionNumber(display);
screen = DefaultScreen(display);
screenwidth = DisplayWidth(display, screen);
screenheight = DisplayHeight(display, screen);
@@ -254,6 +255,19 @@ void process_events()
XEvent event;
int winx, winy;

+ XSync(display, False);
+ if(!XPending(display))
+ {
+ struct timeval timeout;
+ fd_set readset;
+ const int milliseconds = 200;
+ timeout.tv_sec = milliseconds / 1000;
+ timeout.tv_usec = (milliseconds % 1000) * 1000;
+ FD_ZERO(&readset);
+ FD_SET(x11fd, &readset);
+ if(select(x11fd + 1, &readset, NULL, NULL, &timeout) <= 0)
+ return;
+ }
XNextEvent(display, &event);
switch(event.type)
{
11 changes: 0 additions & 11 deletions sysutils/wmmemfree/files/patch-draw.c

This file was deleted.

43 changes: 38 additions & 5 deletions sysutils/wmmemfree/files/patch-wmmemfree.c
@@ -1,10 +1,27 @@
--- wmmemfree.c.orig Fri Apr 11 12:48:52 2003
+++ wmmemfree.c Fri Apr 11 12:50:05 2003
@@ -43,10 +43,11 @@
--- wmmemfree.c.orig 2003-03-21 20:47:14 UTC
+++ wmmemfree.c
@@ -27,15 +27,18 @@
#include "draw.h"
#include "options.h"

+void mem_init();
+
int argc;
char **argv;
int exitloop = 0;
+volatile sig_atomic_t need_update = 0;

void handle_timer(int sig)
{
struct itimerval tv;
if(sig == SIGALRM)
{
- draw_window();
+ need_update = 1;
}
}

@@ -45,8 +48,8 @@ void start_timer()

+ mem_init();
tv.it_value.tv_sec = 2; /* give 2 seconds for safety */
tv.it_value.tv_usec = 0;
- tv.it_interval.tv_sec = 0;
Expand All @@ -14,3 +31,19 @@
signal(SIGALRM, handle_timer);
setitimer(ITIMER_REAL, &tv, NULL);
}
@@ -69,9 +72,15 @@ int main(int carg, char **varg)
argv = varg;
parse_args();
make_window();
+ mem_init();
start_timer();
while(!exitloop)
{
+ if(need_update)
+ {
+ draw_window();
+ need_update = 0;
+ }
process_events();
}
stop_timer();

0 comments on commit c8bcf96

Please sign in to comment.