backlight.c revision 42542f5f
142542f5fSchristos/***************************************************************************
242542f5fSchristos
342542f5fSchristos Copyright 2014 Intel Corporation.  All Rights Reserved.
442542f5fSchristos Copyright 2014 Red Hat, Inc.
542542f5fSchristos
642542f5fSchristos Permission is hereby granted, free of charge, to any person obtaining a
742542f5fSchristos copy of this software and associated documentation files (the
842542f5fSchristos "Software"), to deal in the Software without restriction, including
942542f5fSchristos without limitation the rights to use, copy, modify, merge, publish,
1042542f5fSchristos distribute, sub license, and/or sell copies of the Software, and to
1142542f5fSchristos permit persons to whom the Software is furnished to do so, subject to
1242542f5fSchristos the following conditions:
1342542f5fSchristos
1442542f5fSchristos The above copyright notice and this permission notice (including the
1542542f5fSchristos next paragraph) shall be included in all copies or substantial portions
1642542f5fSchristos of the Software.
1742542f5fSchristos
1842542f5fSchristos THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1942542f5fSchristos OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2042542f5fSchristos MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
2142542f5fSchristos IN NO EVENT SHALL INTEL, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
2242542f5fSchristos DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
2342542f5fSchristos OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
2442542f5fSchristos THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2542542f5fSchristos
2642542f5fSchristos **************************************************************************/
2742542f5fSchristos
2842542f5fSchristos#ifdef HAVE_CONFIG_H
2942542f5fSchristos#include "config.h"
3042542f5fSchristos#endif
3142542f5fSchristos
3242542f5fSchristos#include <sys/types.h>
3342542f5fSchristos#include <sys/wait.h>
3442542f5fSchristos#include <sys/stat.h>
3542542f5fSchristos#include <sys/ioctl.h>
3642542f5fSchristos
3742542f5fSchristos#include <stdio.h>
3842542f5fSchristos#include <stdlib.h>
3942542f5fSchristos#include <string.h>
4042542f5fSchristos#include <ctype.h>
4142542f5fSchristos#include <limits.h>
4242542f5fSchristos#include <fcntl.h>
4342542f5fSchristos#include <unistd.h>
4442542f5fSchristos#include <dirent.h>
4542542f5fSchristos
4642542f5fSchristos#include <xorg-server.h>
4742542f5fSchristos#include <xf86.h>
4842542f5fSchristos#include <pciaccess.h>
4942542f5fSchristos
5042542f5fSchristos#include "backlight.h"
5142542f5fSchristos#include "fd.h"
5242542f5fSchristos
5342542f5fSchristos#define BACKLIGHT_CLASS "/sys/class/backlight"
5442542f5fSchristos
5542542f5fSchristos/* Enough for 10 digits of backlight + '\n' + '\0' */
5642542f5fSchristos#define BACKLIGHT_VALUE_LEN 12
5742542f5fSchristos
5842542f5fSchristos#ifndef ARRAY_SIZE
5942542f5fSchristos#define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0]))
6042542f5fSchristos#endif
6142542f5fSchristos
6242542f5fSchristos/*
6342542f5fSchristos * Unfortunately this is not as simple as I would like it to be. If selinux is
6442542f5fSchristos * dropping dbus messages pkexec may block *forever*.
6542542f5fSchristos *
6642542f5fSchristos * Backgrounding pkexec by doing System("pkexec ...&") does not work because
6742542f5fSchristos * that detaches pkexec from its parent at which point its security checks
6842542f5fSchristos * fail and it refuses to execute the helper.
6942542f5fSchristos *
7042542f5fSchristos * So we're left with spawning a helper child which gets levels to set written
7142542f5fSchristos * to it through a pipe. This turns the blocking forever problem from a hung
7242542f5fSchristos * machine problem into a simple backlight control not working problem.
7342542f5fSchristos *
7442542f5fSchristos * If only things were as simple as on OpenBSD! :)
7542542f5fSchristos */
7642542f5fSchristos
7742542f5fSchristosvoid backlight_init(struct backlight *b)
7842542f5fSchristos{
7942542f5fSchristos	b->type = BL_NONE;
8042542f5fSchristos	b->iface = NULL;
8142542f5fSchristos	b->fd = -1;
8242542f5fSchristos	b->pid = -1;
8342542f5fSchristos	b->max = -1;
8442542f5fSchristos}
8542542f5fSchristos
8642542f5fSchristos#ifdef __OpenBSD__
8742542f5fSchristos
8842542f5fSchristos#include <dev/wscons/wsconsio.h>
8942542f5fSchristos#include <xf86Priv.h>
9042542f5fSchristos
9142542f5fSchristosint backlight_set(struct backlight *b, int level)
9242542f5fSchristos{
9342542f5fSchristos	struct wsdisplay_param param;
9442542f5fSchristos
9542542f5fSchristos	if (b->iface == NULL)
9642542f5fSchristos		return -1;
9742542f5fSchristos
9842542f5fSchristos	if ((unsigned)level > b->max)
9942542f5fSchristos		level = b->max;
10042542f5fSchristos
10142542f5fSchristos	memset(&param, 0, sizeof(param));
10242542f5fSchristos	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
10342542f5fSchristos	param.curval = level;
10442542f5fSchristos
10542542f5fSchristos	return ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, &param);
10642542f5fSchristos}
10742542f5fSchristos
10842542f5fSchristosint backlight_get(struct backlight *b)
10942542f5fSchristos{
11042542f5fSchristos	struct wsdisplay_param param;
11142542f5fSchristos
11242542f5fSchristos	if (b->iface == NULL)
11342542f5fSchristos		return -1;
11442542f5fSchristos
11542542f5fSchristos	memset(&param, 0, sizeof(param));
11642542f5fSchristos	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
11742542f5fSchristos
11842542f5fSchristos	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, &param))
11942542f5fSchristos		return -1;
12042542f5fSchristos
12142542f5fSchristos	return param.curval;
12242542f5fSchristos}
12342542f5fSchristos
12442542f5fSchristosint backlight_open(struct backlight *b, char *iface)
12542542f5fSchristos{
12642542f5fSchristos	struct wsdisplay_param param;
12742542f5fSchristos
12842542f5fSchristos	if (iface != NULL)
12942542f5fSchristos		return -1;
13042542f5fSchristos
13142542f5fSchristos	memset(&param, 0, sizeof(param));
13242542f5fSchristos	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
13342542f5fSchristos
13442542f5fSchristos	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, &param) == -1)
13542542f5fSchristos		return -1;
13642542f5fSchristos
13742542f5fSchristos	b->iface = strdup("wscons");
13842542f5fSchristos	if (b->iface == NULL)
13942542f5fSchristos		return -1;
14042542f5fSchristos
14142542f5fSchristos	b->max = param.max;
14242542f5fSchristos	b->fd = -1;
14342542f5fSchristos	b->type = BL_PLATFORM;
14442542f5fSchristos
14542542f5fSchristos	return param.curval;
14642542f5fSchristos}
14742542f5fSchristos
14842542f5fSchristosenum backlight_type backlight_exists(const char *iface)
14942542f5fSchristos{
15042542f5fSchristos	if (iface != NULL)
15142542f5fSchristos		return BL_NONE;
15242542f5fSchristos
15342542f5fSchristos	return BL_PLATFORM;
15442542f5fSchristos}
15542542f5fSchristos
15642542f5fSchristos#else
15742542f5fSchristos
15842542f5fSchristosstatic int
15942542f5fSchristosis_sysfs_fd(int fd)
16042542f5fSchristos{
16142542f5fSchristos	struct stat st;
16242542f5fSchristos	return fstat(fd, &st) == 0 && major(st.st_dev) == 0;
16342542f5fSchristos}
16442542f5fSchristos
16542542f5fSchristosstatic int
16642542f5fSchristos__backlight_open(const char *iface, const char *file, int mode)
16742542f5fSchristos{
16842542f5fSchristos	char buf[1024];
16942542f5fSchristos	int fd;
17042542f5fSchristos
17142542f5fSchristos	snprintf(buf, sizeof(buf), BACKLIGHT_CLASS "/%s/%s", iface, file);
17242542f5fSchristos	fd = open(buf, mode);
17342542f5fSchristos	if (fd == -1)
17442542f5fSchristos		return -1;
17542542f5fSchristos
17642542f5fSchristos	if (!is_sysfs_fd(fd)) {
17742542f5fSchristos		close(fd);
17842542f5fSchristos		return -1;
17942542f5fSchristos	}
18042542f5fSchristos
18142542f5fSchristos	return fd;
18242542f5fSchristos}
18342542f5fSchristos
18442542f5fSchristosstatic int
18542542f5fSchristos__backlight_read(const char *iface, const char *file)
18642542f5fSchristos{
18742542f5fSchristos	char buf[BACKLIGHT_VALUE_LEN];
18842542f5fSchristos	int fd, val;
18942542f5fSchristos
19042542f5fSchristos	fd = __backlight_open(iface, file, O_RDONLY);
19142542f5fSchristos	if (fd < 0)
19242542f5fSchristos		return -1;
19342542f5fSchristos
19442542f5fSchristos	val = read(fd, buf, BACKLIGHT_VALUE_LEN - 1);
19542542f5fSchristos	if (val > 0) {
19642542f5fSchristos		buf[val] = '\0';
19742542f5fSchristos		val = atoi(buf);
19842542f5fSchristos	} else
19942542f5fSchristos		val = -1;
20042542f5fSchristos	close(fd);
20142542f5fSchristos
20242542f5fSchristos	return val;
20342542f5fSchristos}
20442542f5fSchristos
20542542f5fSchristos/* List of available kernel interfaces in priority order */
20642542f5fSchristosstatic const char *known_interfaces[] = {
20742542f5fSchristos	"dell_backlight",
20842542f5fSchristos	"gmux_backlight",
20942542f5fSchristos	"asus-laptop",
21042542f5fSchristos	"asus-nb-wmi",
21142542f5fSchristos	"eeepc",
21242542f5fSchristos	"thinkpad_screen",
21342542f5fSchristos	"mbp_backlight",
21442542f5fSchristos	"fujitsu-laptop",
21542542f5fSchristos	"sony",
21642542f5fSchristos	"samsung",
21742542f5fSchristos	"acpi_video1",
21842542f5fSchristos	"acpi_video0",
21942542f5fSchristos	"intel_backlight",
22042542f5fSchristos};
22142542f5fSchristos
22242542f5fSchristosstatic enum backlight_type __backlight_type(const char *iface)
22342542f5fSchristos{
22442542f5fSchristos	char buf[1024];
22542542f5fSchristos	int fd, v;
22642542f5fSchristos
22742542f5fSchristos	v = -1;
22842542f5fSchristos	fd = __backlight_open(iface, "type", O_RDONLY);
22942542f5fSchristos	if (fd >= 0) {
23042542f5fSchristos		v = read(fd, buf, sizeof(buf)-1);
23142542f5fSchristos		close(fd);
23242542f5fSchristos	}
23342542f5fSchristos	if (v > 0) {
23442542f5fSchristos		while (v > 0 && isspace(buf[v-1]))
23542542f5fSchristos			v--;
23642542f5fSchristos		buf[v] = '\0';
23742542f5fSchristos
23842542f5fSchristos		if (strcmp(buf, "raw") == 0)
23942542f5fSchristos			v = BL_RAW;
24042542f5fSchristos		else if (strcmp(buf, "platform") == 0)
24142542f5fSchristos			v = BL_PLATFORM;
24242542f5fSchristos		else if (strcmp(buf, "firmware") == 0)
24342542f5fSchristos			v = BL_FIRMWARE;
24442542f5fSchristos		else
24542542f5fSchristos			v = BL_NAMED;
24642542f5fSchristos	} else
24742542f5fSchristos		v = BL_NAMED;
24842542f5fSchristos
24942542f5fSchristos	if (v == BL_NAMED) {
25042542f5fSchristos		int i;
25142542f5fSchristos		for (i = 0; i < ARRAY_SIZE(known_interfaces); i++) {
25242542f5fSchristos			if (strcmp(iface, known_interfaces[i]) == 0)
25342542f5fSchristos				break;
25442542f5fSchristos		}
25542542f5fSchristos		v += i;
25642542f5fSchristos	}
25742542f5fSchristos
25842542f5fSchristos	return v;
25942542f5fSchristos}
26042542f5fSchristos
26142542f5fSchristosenum backlight_type backlight_exists(const char *iface)
26242542f5fSchristos{
26342542f5fSchristos	if (__backlight_read(iface, "brightness") < 0)
26442542f5fSchristos		return BL_NONE;
26542542f5fSchristos
26642542f5fSchristos	if (__backlight_read(iface, "max_brightness") <= 0)
26742542f5fSchristos		return BL_NONE;
26842542f5fSchristos
26942542f5fSchristos	return __backlight_type(iface);
27042542f5fSchristos}
27142542f5fSchristos
27242542f5fSchristosstatic int __backlight_init(struct backlight *b, char *iface, int fd)
27342542f5fSchristos{
27442542f5fSchristos	b->fd = fd_move_cloexec(fd_set_nonblock(fd));
27542542f5fSchristos	b->iface = iface;
27642542f5fSchristos	return 1;
27742542f5fSchristos}
27842542f5fSchristos
27942542f5fSchristosstatic int __backlight_direct_init(struct backlight *b, char *iface)
28042542f5fSchristos{
28142542f5fSchristos	int fd;
28242542f5fSchristos
28342542f5fSchristos	fd = __backlight_open(iface, "brightness", O_RDWR);
28442542f5fSchristos	if (fd < 0)
28542542f5fSchristos		return 0;
28642542f5fSchristos
28742542f5fSchristos	return __backlight_init(b, iface, fd);
28842542f5fSchristos}
28942542f5fSchristos
29042542f5fSchristosstatic int __backlight_helper_init(struct backlight *b, char *iface)
29142542f5fSchristos{
29242542f5fSchristos#if USE_BACKLIGHT_HELPER
29342542f5fSchristos	struct stat st;
29442542f5fSchristos	char *env[] = { NULL };
29542542f5fSchristos	int use_pkexec = 0;
29642542f5fSchristos	int fds[2];
29742542f5fSchristos
29842542f5fSchristos	/*
29942542f5fSchristos	 * Some systems may prefer using PolicyKit's pkexec over
30042542f5fSchristos	 * making the helper suid root, since the suid option will allow
30142542f5fSchristos	 * anyone to control the backlight.  However, as pkexec
30242542f5fSchristos	 * is quite troublesome and not universally available, we
30342542f5fSchristos	 * still try the old fashioned and simple method first.
30442542f5fSchristos	 * Either way, we have to trust that it is our backlight-helper
30542542f5fSchristos	 * that is run and that we have scrutinised it carefully.
30642542f5fSchristos	 */
30742542f5fSchristos	if (stat(LIBEXEC_PATH "/xf86-video-intel-backlight-helper", &st))
30842542f5fSchristos		return 0;
30942542f5fSchristos
31042542f5fSchristos	if ((st.st_mode & (S_IFREG | S_ISUID | S_IXUSR)) != (S_IFREG | S_ISUID | S_IXUSR)) {
31142542f5fSchristos		if (System("pkexec --version"))
31242542f5fSchristos			return 0;
31342542f5fSchristos
31442542f5fSchristos		use_pkexec = 1;
31542542f5fSchristos	}
31642542f5fSchristos
31742542f5fSchristos	if (pipe(fds))
31842542f5fSchristos		return 0;
31942542f5fSchristos
32042542f5fSchristos	switch ((b->pid = fork())) {
32142542f5fSchristos	case 0:
32242542f5fSchristos		if (setgid(getgid()) || setuid(getuid()))
32342542f5fSchristos			_exit(127);
32442542f5fSchristos
32542542f5fSchristos		close(fds[1]);
32642542f5fSchristos		if (dup2(fds[0], 0))
32742542f5fSchristos			_exit(127);
32842542f5fSchristos		close(fds[0]);
32942542f5fSchristos
33042542f5fSchristos		if (use_pkexec) {
33142542f5fSchristos			execlp("pkexec", "pkexec",
33242542f5fSchristos			       LIBEXEC_PATH "/xf86-video-intel-backlight-helper",
33342542f5fSchristos			       iface, (char *)0);
33442542f5fSchristos		} else {
33542542f5fSchristos			execle(LIBEXEC_PATH "/xf86-video-intel-backlight-helper",
33642542f5fSchristos			       "xf86-video-intel-backlight-helper",
33742542f5fSchristos			       iface, (char *)0, env);
33842542f5fSchristos		}
33942542f5fSchristos		_exit(1);
34042542f5fSchristos		/* unreachable fallthrough */
34142542f5fSchristos	case -1:
34242542f5fSchristos		close(fds[1]);
34342542f5fSchristos		close(fds[0]);
34442542f5fSchristos		return 0;
34542542f5fSchristos
34642542f5fSchristos	default:
34742542f5fSchristos		close(fds[0]);
34842542f5fSchristos		return __backlight_init(b, iface, fds[1]);
34942542f5fSchristos	}
35042542f5fSchristos#else
35142542f5fSchristos	return 0;
35242542f5fSchristos#endif
35342542f5fSchristos}
35442542f5fSchristos
35542542f5fSchristosstatic char *
35642542f5fSchristos__backlight_find(void)
35742542f5fSchristos{
35842542f5fSchristos	char *best_iface = NULL;
35942542f5fSchristos	unsigned best_type = INT_MAX;
36042542f5fSchristos	DIR *dir;
36142542f5fSchristos	struct dirent *de;
36242542f5fSchristos
36342542f5fSchristos	dir = opendir(BACKLIGHT_CLASS);
36442542f5fSchristos	if (dir == NULL)
36542542f5fSchristos		return NULL;
36642542f5fSchristos
36742542f5fSchristos	while ((de = readdir(dir))) {
36842542f5fSchristos		int v;
36942542f5fSchristos
37042542f5fSchristos		if (*de->d_name == '.')
37142542f5fSchristos			continue;
37242542f5fSchristos
37342542f5fSchristos		/* Fallback to priority list of known iface for old kernels */
37442542f5fSchristos		v = backlight_exists(de->d_name);
37542542f5fSchristos		if (v < best_type) {
37642542f5fSchristos			char *copy = strdup(de->d_name);
37742542f5fSchristos			if (copy) {
37842542f5fSchristos				free(best_iface);
37942542f5fSchristos				best_iface = copy;
38042542f5fSchristos				best_type = v;
38142542f5fSchristos			}
38242542f5fSchristos		}
38342542f5fSchristos	}
38442542f5fSchristos	closedir(dir);
38542542f5fSchristos
38642542f5fSchristos	return best_iface;
38742542f5fSchristos}
38842542f5fSchristos
38942542f5fSchristosint backlight_open(struct backlight *b, char *iface)
39042542f5fSchristos{
39142542f5fSchristos	int level;
39242542f5fSchristos
39342542f5fSchristos	if (iface == NULL)
39442542f5fSchristos		iface = __backlight_find();
39542542f5fSchristos	if (iface == NULL)
39642542f5fSchristos		goto err;
39742542f5fSchristos
39842542f5fSchristos	b->type = __backlight_type(iface);
39942542f5fSchristos
40042542f5fSchristos	b->max = __backlight_read(iface, "max_brightness");
40142542f5fSchristos	if (b->max <= 0)
40242542f5fSchristos		goto err;
40342542f5fSchristos
40442542f5fSchristos	level = __backlight_read(iface, "brightness");
40542542f5fSchristos	if (level < 0)
40642542f5fSchristos		goto err;
40742542f5fSchristos
40842542f5fSchristos	if (!__backlight_direct_init(b, iface) &&
40942542f5fSchristos	    !__backlight_helper_init(b, iface))
41042542f5fSchristos		goto err;
41142542f5fSchristos
41242542f5fSchristos	return level;
41342542f5fSchristos
41442542f5fSchristoserr:
41542542f5fSchristos	backlight_init(b);
41642542f5fSchristos	return -1;
41742542f5fSchristos}
41842542f5fSchristos
41942542f5fSchristosint backlight_set(struct backlight *b, int level)
42042542f5fSchristos{
42142542f5fSchristos	char val[BACKLIGHT_VALUE_LEN];
42242542f5fSchristos	int len, ret = 0;
42342542f5fSchristos
42442542f5fSchristos	if (b->iface == NULL)
42542542f5fSchristos		return 0;
42642542f5fSchristos
42742542f5fSchristos	if ((unsigned)level > b->max)
42842542f5fSchristos		level = b->max;
42942542f5fSchristos
43042542f5fSchristos	len = snprintf(val, BACKLIGHT_VALUE_LEN, "%d\n", level);
43142542f5fSchristos	if (write(b->fd, val, len) != len)
43242542f5fSchristos		ret = -1;
43342542f5fSchristos
43442542f5fSchristos	return ret;
43542542f5fSchristos}
43642542f5fSchristos
43742542f5fSchristosint backlight_get(struct backlight *b)
43842542f5fSchristos{
43942542f5fSchristos	int level;
44042542f5fSchristos
44142542f5fSchristos	if (b->iface == NULL)
44242542f5fSchristos		return -1;
44342542f5fSchristos
44442542f5fSchristos	level = __backlight_read(b->iface, "brightness");
44542542f5fSchristos	if (level > b->max)
44642542f5fSchristos		level = b->max;
44742542f5fSchristos	else if (level < 0)
44842542f5fSchristos		level = -1;
44942542f5fSchristos	return level;
45042542f5fSchristos}
45142542f5fSchristos#endif
45242542f5fSchristos
45342542f5fSchristosvoid backlight_disable(struct backlight *b)
45442542f5fSchristos{
45542542f5fSchristos	if (b->iface == NULL)
45642542f5fSchristos		return;
45742542f5fSchristos
45842542f5fSchristos	if (b->fd != -1)
45942542f5fSchristos		close(b->fd);
46042542f5fSchristos
46142542f5fSchristos	free(b->iface);
46242542f5fSchristos	b->iface = NULL;
46342542f5fSchristos}
46442542f5fSchristos
46542542f5fSchristosvoid backlight_close(struct backlight *b)
46642542f5fSchristos{
46742542f5fSchristos	backlight_disable(b);
46842542f5fSchristos	if (b->pid)
46942542f5fSchristos		waitpid(b->pid, NULL, 0);
47042542f5fSchristos}
47142542f5fSchristos
47242542f5fSchristoschar *backlight_find_for_device(struct pci_device *pci)
47342542f5fSchristos{
47442542f5fSchristos	char path[200];
47542542f5fSchristos	unsigned best_type = INT_MAX;
47642542f5fSchristos	char *best_iface = NULL;
47742542f5fSchristos	DIR *dir;
47842542f5fSchristos	struct dirent *de;
47942542f5fSchristos
48042542f5fSchristos	snprintf(path, sizeof(path),
48142542f5fSchristos		 "/sys/bus/pci/devices/%04x:%02x:%02x.%d/backlight",
48242542f5fSchristos		 pci->domain, pci->bus, pci->dev, pci->func);
48342542f5fSchristos
48442542f5fSchristos	dir = opendir(path);
48542542f5fSchristos	if (dir == NULL)
48642542f5fSchristos		return NULL;
48742542f5fSchristos
48842542f5fSchristos	while ((de = readdir(dir))) {
48942542f5fSchristos		int v;
49042542f5fSchristos
49142542f5fSchristos		if (*de->d_name == '.')
49242542f5fSchristos			continue;
49342542f5fSchristos
49442542f5fSchristos		v = backlight_exists(de->d_name);
49542542f5fSchristos		if (v < best_type) {
49642542f5fSchristos			char *copy = strdup(de->d_name);
49742542f5fSchristos			if (copy) {
49842542f5fSchristos				free(best_iface);
49942542f5fSchristos				best_iface = copy;
50042542f5fSchristos				best_type = v;
50142542f5fSchristos			}
50242542f5fSchristos		}
50342542f5fSchristos	}
50442542f5fSchristos	closedir(dir);
50542542f5fSchristos
50642542f5fSchristos	return best_iface;
50742542f5fSchristos}
508