wmbattery: Fix memory leak.
Patch by David Johnson <davijoh3@cisco.com> to fix Debian bug #816872 [1]: > wmbattery appears to have a memory leak. When invoked with "wmbattery -w 2" > it is leaking approx 350KB/minute on my system. Eventually it runs out of > memory and is killed by kernel. ... > I've concluded libupower-glib is just super leaky. The below patch > moves the upower API calls into a child process that doesn't exist for > long to keep the leaks out of the main process. > Tested against stretch versions, looks good. [1] https://bugs.debian.org/816872
This commit is contained in:
parent
6434c272b3
commit
13d3321f14
|
@ -6,13 +6,15 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <upower.h>
|
#include <upower.h>
|
||||||
#include "apm.h"
|
#include "apm.h"
|
||||||
|
|
||||||
#define MAX_RETRIES 3
|
#define MAX_RETRIES 3
|
||||||
|
|
||||||
static UpClient * up;
|
|
||||||
|
|
||||||
struct context {
|
struct context {
|
||||||
int current;
|
int current;
|
||||||
int needed;
|
int needed;
|
||||||
|
@ -54,29 +56,11 @@ static void get_devinfo(gpointer device, gpointer result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int upower_supported(void)
|
|
||||||
{
|
|
||||||
up = up_client_new();
|
|
||||||
|
|
||||||
if (!up) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
GPtrArray *devices = up_client_get_devices(up);
|
|
||||||
|
|
||||||
if (!devices) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
g_ptr_array_unref(devices);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fill the passed apm_info struct. */
|
/* Fill the passed apm_info struct. */
|
||||||
int upower_read(int battery, apm_info *info)
|
static int upower_read_child(int battery, apm_info *info)
|
||||||
{
|
{
|
||||||
|
UpClient * up;
|
||||||
GPtrArray *devices = NULL;
|
GPtrArray *devices = NULL;
|
||||||
static int retries = 0;
|
|
||||||
|
|
||||||
up = up_client_new();
|
up = up_client_new();
|
||||||
|
|
||||||
|
@ -90,15 +74,9 @@ int upower_read(int battery, apm_info *info)
|
||||||
|
|
||||||
devices = up_client_get_devices(up);
|
devices = up_client_get_devices(up);
|
||||||
|
|
||||||
if (!devices) {
|
if (!devices)
|
||||||
retries++;
|
return -1;
|
||||||
if (retries < MAX_RETRIES)
|
|
||||||
return 0; /* fine immediately after hibernation */
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
retries = 0;
|
|
||||||
info->battery_flags = 0;
|
info->battery_flags = 0;
|
||||||
info->using_minutes = 0;
|
info->using_minutes = 0;
|
||||||
|
|
||||||
|
@ -143,6 +121,92 @@ int upower_read(int battery, apm_info *info)
|
||||||
info->battery_status = BATTERY_STATUS_ABSENT;
|
info->battery_status = BATTERY_STATUS_ABSENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_ptr_array_free(devices, TRUE);
|
g_ptr_array_unref(devices);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int upower_read_work(int battery, apm_info *info)
|
||||||
|
{
|
||||||
|
int sp[2];
|
||||||
|
int child;
|
||||||
|
int status;
|
||||||
|
ssize_t cv;
|
||||||
|
|
||||||
|
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sp) < 0) {
|
||||||
|
fprintf(stderr, "socketpair: %s", strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
child = fork();
|
||||||
|
if (child < 0) {
|
||||||
|
fprintf(stderr, "fork: %s", strerror(errno));
|
||||||
|
goto fail_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child == 0) {
|
||||||
|
/* child process does work, writes failure or result back to parent */
|
||||||
|
close(sp[0]);
|
||||||
|
status = upower_read_child(battery, info);
|
||||||
|
if (status < 0) {
|
||||||
|
cv = send(sp[1], &status, sizeof(status), 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cv = send(sp[1], info, sizeof(*info), 0);
|
||||||
|
}
|
||||||
|
exit(cv < 0);
|
||||||
|
}
|
||||||
|
close(sp[1]);
|
||||||
|
|
||||||
|
child = waitpid(child, &status, 0);
|
||||||
|
if (child < 0) {
|
||||||
|
fprintf(stderr, "waitpid: %s status=%d", strerror(errno), status);
|
||||||
|
goto fail_close0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv = recv(sp[0], info, sizeof(*info), 0);
|
||||||
|
if (cv < 0) {
|
||||||
|
fprintf(stderr, "recv: %s", strerror(errno));
|
||||||
|
goto fail_close0;
|
||||||
|
}
|
||||||
|
else if (cv == sizeof(status)) {
|
||||||
|
memcpy(&status, info, sizeof(status));
|
||||||
|
goto fail_close0;
|
||||||
|
}
|
||||||
|
else if (cv != sizeof(*info)) {
|
||||||
|
fprintf(stderr, "recv: unexpected size %d", cv);
|
||||||
|
goto fail_close0;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sp[0]);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail_close:
|
||||||
|
close(sp[1]);
|
||||||
|
fail_close0:
|
||||||
|
close(sp[0]);
|
||||||
|
fail:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int upower_supported(void)
|
||||||
|
{
|
||||||
|
apm_info info;
|
||||||
|
return !(upower_read_work(1, &info) < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int upower_read(int battery, apm_info *info)
|
||||||
|
{
|
||||||
|
static int retries = 0;
|
||||||
|
|
||||||
|
if (upower_read_work(battery, info) < 0) {
|
||||||
|
retries++;
|
||||||
|
if (retries < MAX_RETRIES)
|
||||||
|
return 0; /* fine immediately after hibernation */
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
retries = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue