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
					
				
					 1 changed files with 95 additions and 31 deletions
				
			
		| 
						 | 
					@ -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