More generic handling of tap device under Windows.

This commit is contained in:
Guus Sliepen 2003-07-28 21:54:03 +00:00
parent 83263b7446
commit c15e8a96bf
2 changed files with 516 additions and 607 deletions

View file

@ -17,7 +17,7 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: device.c,v 1.1.2.10 2003/07/22 20:55:20 guus Exp $ $Id: device.c,v 1.1.2.11 2003/07/28 21:54:03 guus Exp $
*/ */
#include "system.h" #include "system.h"
@ -32,43 +32,23 @@
#include "utils.h" #include "utils.h"
#include "xalloc.h" #include "xalloc.h"
/* Definitions from CIPE */
#define NETCARD_REG_KEY_2000 "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}" #define NETCARD_REG_KEY_2000 "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
#define NETCARD_REG_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards" #define NETCARD_REG_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards"
#define REG_SERVICE_KEY "SYSTEM\\CurrentControlSet\\Services" #define REG_SERVICE_KEY "SYSTEM\\CurrentControlSet\\Services"
#define REG_CONTROL_NET "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
#define USERMODEDEVICEDIR "\\\\.\\" #define USERMODEDEVICEDIR "\\\\.\\"
#define SYSDEVICEDIR "\\Device\\" #define SYSDEVICEDIR "\\Device\\"
#define USERDEVICEDIR "\\??\\" #define USERDEVICEDIR "\\??\\"
#define TAPSUFFIX ".tap" #define TAPSUFFIX ".tap"
#define PRODUCT_STRING "DKW Heavy Industries VPN Adapter." #define TAP_CONTROL_CODE(request,method) CTL_CODE(FILE_DEVICE_PHYSICAL_NETCARD | 8000, request, method, FILE_ANY_ACCESS)
#define CIPE_SERVICE_NAME "CIPE_Daemon"
#define CIPE_DRIVER_NAME "CIPE"
#define CIPE_NDIS_MAJOR_VERSION 4 #define TAP_IOCTL_GET_LASTMAC TAP_CONTROL_CODE(0, METHOD_BUFFERED)
#define CIPE_NDIS_MINOR_VERSION 0 #define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE(1, METHOD_BUFFERED)
#define TAP_IOCTL_SET_STATISTICS TAP_CONTROL_CODE(2, METHOD_BUFFERED)
#ifndef CIPE_DRIVER_MAJOR_VERSION /* FIXME: This only works for Windows 2000 */
# define CIPE_DRIVER_MAJOR_VERSION 2
#endif
#ifndef CIPE_DRIVER_MINOR_VERSION
# define CIPE_DRIVER_MINOR_VERSION 1
#endif
#ifndef CIPE_MAC_ROOT_ADDRESS
# define CIPE_MAC_ROOT_ADDRESS "8:0:58:0:0:1"
#endif
#define CIPE_CONTROL_CODE(request,method) CTL_CODE (FILE_DEVICE_PHYSICAL_NETCARD | 8000, request, method, FILE_ANY_ACCESS)
#define CIPE_IOCTL_GET_LASTMAC CIPE_CONTROL_CODE (0, METHOD_BUFFERED)
#define CIPE_IOCTL_GET_MAC CIPE_CONTROL_CODE (1, METHOD_BUFFERED)
#define CIPE_IOCTL_SET_STATISTICS CIPE_CONTROL_CODE (2, METHOD_BUFFERED)
/* Windows 2000 */
#define OSTYPE 5 #define OSTYPE 5
int device_fd = -1; int device_fd = -1;
@ -86,79 +66,81 @@ int sp[2];
bool setup_device(void) bool setup_device(void)
{ {
HKEY key, key2, adapterkey; HKEY key, key2;
int i; int i;
char regpath[1024];
char adapterid[1024]; char adapterid[1024];
char manufacturer[1024];
char productname[1024];
char adaptername[1024]; char adaptername[1024];
char tapname[1024]; char tapname[1024];
char gelukt = 0; char gelukt = 0;
long len; long len;
FILETIME filetime;
bool found = false; bool found = false;
cp(); cp();
get_config_string(lookup_config(config_tree, "Device"), &device); get_config_string(lookup_config(config_tree, "Device"), &device);
get_config_string(lookup_config(config_tree, "Interface"), &iface);
/* Open registry and look for network adapters */ /* Open registry and look for network adapters */
if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, (OSTYPE > 4 ? NETCARD_REG_KEY_2000 : NETCARD_REG_KEY), 0, KEY_READ, &key)) { if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CONTROL_NET, 0, KEY_READ, &key)) {
logger(LOG_ERR, _("Unable to read registry")); logger(LOG_ERR, _("Unable to read registry"));
return false; return false;
} }
for (i = 0; ; i++) { for (i = 0; ; i++) {
len = sizeof(adapterid); len = sizeof(adapterid);
if(RegEnumKeyEx (key, i, adapterid, &len, 0, 0, 0, &filetime)) if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL))
break; break;
if(device) {
if(!strcmp(device, adapterid)) {
found = true;
break;
} else
continue;
}
/* Find out more about this adapter */ /* Find out more about this adapter */
if(RegOpenKeyEx (key, adapterid, 0, KEY_READ, &adapterkey)) { snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", REG_CONTROL_NET, adapterid);
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2)) {
logger(LOG_ERR, _("Unable to read registry")); logger(LOG_ERR, _("Unable to read registry"));
return false; return false;
} }
len = sizeof(productname); len = sizeof(adaptername);
if(RegQueryValueEx(adapterkey, "ProductName", 0, 0, productname, &len)) RegQueryValueEx(key2, "Name", 0, 0, adaptername, &len);
goto skip;
len = sizeof(manufacturer); if(iface) {
if(RegQueryValueEx(adapterkey, "Manufacturer", 0, 0, manufacturer, &len)) if(!strcmp(iface, adaptername)) {
goto skip; found = true;
break;
if(!strcmp(productname, "CIPE") && !strcmp(manufacturer, "DKWHeavyIndustries")) { } else
if(device && strcmp(adapterid, device))
continue; continue;
if(!device) }
device = xstrdup(adapterid);
snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
if(handle != INVALID_HANDLE_VALUE) {
CloseHandle(handle);
found = true; found = true;
break; break;
} }
skip:
RegCloseKey (adapterkey);
} }
if(!found) { if(!found) {
logger(LOG_ERR, _("No CIPE adapters found!")); logger(LOG_ERR, _("No Windows tap device found!"));
return false; return false;
} }
/* Get adapter name */ device = adapterid;
iface = adaptername;
len = sizeof(adaptername); snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
RegQueryValueEx(adapterkey, (OSTYPE > 4 ? "NetCfgInstanceId" : "ServiceName"), 0, 0, adaptername, &len);
/* FIXME? cipsrvr checks if the device is in use at this point */
/* Try to open the corresponding tap device */
snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adaptername);
/* Now we are going to open this device twice: once for reading and once for writing. /* Now we are going to open this device twice: once for reading and once for writing.
We do this because apparently it isn't possible to check for activity in the select() loop. We do this because apparently it isn't possible to check for activity in the select() loop.
@ -169,6 +151,30 @@ skip:
return false; return false;
} }
/* The parent opens the tap device for writing. */
handle = CreateFile(tapname, GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM , 0);
if(handle == INVALID_HANDLE_VALUE) {
logger(LOG_ERR, _("Could not open CIPE tap device for writing!"));
return false;
}
device_fd = sp[0];
/* Get MAC address from tap device */
if(DeviceIoControl(device_fd, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) {
logger(LOG_ERR, _("Could not get MAC address from Windows tap device!"));
return false;
}
if(routing_mode == RMODE_ROUTER) {
overwrite_mac = 1;
}
/* Now we start the child */
reader_pid = fork(); reader_pid = fork();
if(reader_pid == -1) { if(reader_pid == -1) {
@ -207,24 +213,6 @@ skip:
} }
} }
/* The parent opens the tap device for writing. */
handle = CreateFile(tapname, GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM , 0);
if(handle == INVALID_HANDLE_VALUE) {
logger(LOG_ERR, _("Could not open CIPE tap device for writing!"));
return false;
}
device_fd = sp[0];
/* Get MAC address from tap device */
if(routing_mode == RMODE_ROUTER) {
DeviceIoControl (handle, CIPE_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0);
overwrite_mac = 1;
}
read(device_fd, &gelukt, 1); read(device_fd, &gelukt, 1);
if(gelukt != 1) { if(gelukt != 1) {
logger(LOG_DEBUG, "Tap reader failed!"); logger(LOG_DEBUG, "Tap reader failed!");
@ -234,9 +222,9 @@ skip:
if(!get_config_string(lookup_config(config_tree, "Interface"), &iface)) if(!get_config_string(lookup_config(config_tree, "Interface"), &iface))
iface = device; iface = device;
device_info = _("Cygwin CIPE device"); device_info = _("Windows tap device");
logger(LOG_INFO, _("%s is a %s"), device, device_info); logger(LOG_INFO, _("%s (%s) is a %s"), device, iface, device_info);
return false; return false;
} }

View file

@ -17,13 +17,12 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: device.c,v 1.1.2.2 2003/07/22 20:55:21 guus Exp $ $Id: device.c,v 1.1.2.3 2003/07/28 21:54:03 guus Exp $
*/ */
#error "Device driver for MinGW environment not written yet!"
#include "system.h" #include "system.h"
#include <windows.h>
#include <winioctl.h> #include <winioctl.h>
#include "conf.h" #include "conf.h"
@ -33,46 +32,26 @@
#include "utils.h" #include "utils.h"
#include "xalloc.h" #include "xalloc.h"
/* Definitions from CIPE */
#define NETCARD_REG_KEY_2000 "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}" #define NETCARD_REG_KEY_2000 "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
#define NETCARD_REG_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards" #define NETCARD_REG_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards"
#define REG_SERVICE_KEY "SYSTEM\\CurrentControlSet\\Services" #define REG_SERVICE_KEY "SYSTEM\\CurrentControlSet\\Services"
#define REG_CONTROL_NET "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
#define USERMODEDEVICEDIR "\\\\.\\" #define USERMODEDEVICEDIR "\\\\.\\"
#define SYSDEVICEDIR "\\Device\\" #define SYSDEVICEDIR "\\Device\\"
#define USERDEVICEDIR "\\??\\" #define USERDEVICEDIR "\\??\\"
#define TAPSUFFIX ".tap" #define TAPSUFFIX ".tap"
#define PRODUCT_STRING "DKW Heavy Industries VPN Adapter." #define TAP_CONTROL_CODE(request,method) CTL_CODE(FILE_DEVICE_PHYSICAL_NETCARD | 8000, request, method, FILE_ANY_ACCESS)
#define CIPE_SERVICE_NAME "CIPE_Daemon"
#define CIPE_DRIVER_NAME "CIPE"
#define CIPE_NDIS_MAJOR_VERSION 4 #define TAP_IOCTL_GET_LASTMAC TAP_CONTROL_CODE(0, METHOD_BUFFERED)
#define CIPE_NDIS_MINOR_VERSION 0 #define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE(1, METHOD_BUFFERED)
#define TAP_IOCTL_SET_STATISTICS TAP_CONTROL_CODE(2, METHOD_BUFFERED)
#ifndef CIPE_DRIVER_MAJOR_VERSION /* FIXME: This only works for Windows 2000 */
# define CIPE_DRIVER_MAJOR_VERSION 2
#endif
#ifndef CIPE_DRIVER_MINOR_VERSION
# define CIPE_DRIVER_MINOR_VERSION 1
#endif
#ifndef CIPE_MAC_ROOT_ADDRESS
# define CIPE_MAC_ROOT_ADDRESS "8:0:58:0:0:1"
#endif
#define CIPE_CONTROL_CODE(request,method) CTL_CODE (FILE_DEVICE_PHYSICAL_NETCARD | 8000, request, method, FILE_ANY_ACCESS)
#define CIPE_IOCTL_GET_LASTMAC CIPE_CONTROL_CODE (0, METHOD_BUFFERED)
#define CIPE_IOCTL_GET_MAC CIPE_CONTROL_CODE (1, METHOD_BUFFERED)
#define CIPE_IOCTL_SET_STATISTICS CIPE_CONTROL_CODE (2, METHOD_BUFFERED)
/* Windows 2000 */
#define OSTYPE 5 #define OSTYPE 5
int device_fd = -1; HANDLE device_fd = INVALID_HANDLE_VALUE;
char *device = NULL; char *device = NULL;
char *iface = NULL; char *iface = NULL;
char *device_info = NULL; char *device_info = NULL;
@ -80,164 +59,110 @@ char *device_info = NULL;
int device_total_in = 0; int device_total_in = 0;
int device_total_out = 0; int device_total_out = 0;
HANDLE handle;
pid_t reader_pid;
int sp[2];
bool setup_device(void) bool setup_device(void)
{ {
HKEY key, key2, adapterkey; HKEY key, key2;
int i; int i;
char regpath[1024];
char adapterid[1024]; char adapterid[1024];
char manufacturer[1024];
char productname[1024];
char adaptername[1024]; char adaptername[1024];
char tapname[1024]; char tapname[1024];
char gelukt = 0; char gelukt = 0;
long len; long len;
FILETIME filetime;
bool found = false; bool found = false;
cp(); cp();
get_config_string(lookup_config(config_tree, "Device"), &device); get_config_string(lookup_config(config_tree, "Device"), &device);
get_config_string(lookup_config(config_tree, "Interface"), &iface);
/* Open registry and look for network adapters */ /* Open registry and look for network adapters */
if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, (OSTYPE > 4 ? NETCARD_REG_KEY_2000 : NETCARD_REG_KEY), 0, KEY_READ, &key)) { if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CONTROL_NET, 0, KEY_READ, &key)) {
logger(LOG_ERR, _("Unable to read registry")); logger(LOG_ERR, _("Unable to read registry"));
return false; return false;
} }
for (i = 0; ; i++) { for (i = 0; ; i++) {
len = sizeof(adapterid); len = sizeof(adapterid);
if(RegEnumKeyEx (key, i, adapterid, &len, 0, 0, 0, &filetime)) if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL))
break; break;
if(device) {
if(!strcmp(device, adapterid)) {
found = true;
break;
} else
continue;
}
/* Find out more about this adapter */ /* Find out more about this adapter */
if(RegOpenKeyEx (key, adapterid, 0, KEY_READ, &adapterkey)) { snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", REG_CONTROL_NET, adapterid);
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2)) {
logger(LOG_ERR, _("Unable to read registry")); logger(LOG_ERR, _("Unable to read registry"));
return false; return false;
} }
len = sizeof(productname); len = sizeof(adaptername);
if(RegQueryValueEx(adapterkey, "ProductName", 0, 0, productname, &len)) RegQueryValueEx(key2, "Name", 0, 0, adaptername, &len);
goto skip;
len = sizeof(manufacturer); if(iface) {
if(RegQueryValueEx(adapterkey, "Manufacturer", 0, 0, manufacturer, &len)) if(!strcmp(iface, adaptername)) {
goto skip; found = true;
break;
if(!strcmp(productname, "CIPE") && !strcmp(manufacturer, "DKWHeavyIndustries")) { } else
if(device && strcmp(adapterid, device))
continue; continue;
if(!device) }
device = xstrdup(adapterid);
snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
device_fd = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
if(device_fd != INVALID_HANDLE_VALUE) {
found = true; found = true;
break; break;
} }
skip:
RegCloseKey (adapterkey);
} }
if(!found) { if(!found) {
logger(LOG_ERR, _("No CIPE adapters found!")); logger(LOG_ERR, _("No Windows tap device found!"));
return false; return false;
} }
/* Get adapter name */ device = adapterid;
iface = adaptername;
len = sizeof(adaptername);
RegQueryValueEx(adapterkey, (OSTYPE > 4 ? "NetCfgInstanceId" : "ServiceName"), 0, 0, adaptername, &len);
/* FIXME? cipsrvr checks if the device is in use at this point */
/* Try to open the corresponding tap device */ /* Try to open the corresponding tap device */
snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adaptername); if(device_fd == INVALID_HANDLE_VALUE) {
snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
device_fd = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
}
/* Now we are going to open this device twice: once for reading and once for writing. if(device_fd == INVALID_HANDLE_VALUE) {
We do this because apparently it isn't possible to check for activity in the select() loop. logger(LOG_ERR, _("%s (%s) is no a usable Windows tap device!"), device, iface);
Furthermore I don't really know how to do it the "Windows" way. */
if(socketpair(AF_UNIX, SOCK_DGRAM, PF_UNIX, sp)) {
logger(LOG_DEBUG, _("System call `%s' failed: %s"), "socketpair", strerror(errno));
return false; return false;
} }
reader_pid = fork();
if(reader_pid == -1) {
logger(LOG_DEBUG, _("System call `%s' failed: %s"), "fork", strerror(errno));
return false;
}
if(!reader_pid) {
/* The child opens the tap device for reading, blocking.
It passes everything it reads to the socket. */
char buf[MTU];
int lenin;
handle = CreateFile(tapname, GENERIC_READ, FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM , 0);
if(handle == INVALID_HANDLE_VALUE) {
logger(LOG_ERR, _("Could not open CIPE tap device for reading!"));
buf[0] = 0;
write(sp[1], buf, 1);
exit(1);
}
logger(LOG_DEBUG, _("Tap reader forked and running."));
/* Notify success */
buf[0] = 1;
write(sp[1], buf, 1);
/* Pass packets */
for(;;) {
ReadFile (handle, buf, MTU, &lenin, NULL);
write(sp[1], buf, lenin);
}
}
/* The parent opens the tap device for writing. */
handle = CreateFile(tapname, GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM , 0);
if(handle == INVALID_HANDLE_VALUE) {
logger(LOG_ERR, _("Could not open CIPE tap device for writing!"));
return false;
}
device_fd = sp[0];
/* Get MAC address from tap device */ /* Get MAC address from tap device */
if(routing_mode == RMODE_ROUTER) { if(DeviceIoControl(device_fd, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) {
DeviceIoControl (handle, CIPE_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0); logger(LOG_ERR, _("Could not get MAC address from Windows tap device!"));
overwrite_mac = 1; return false;
} }
read(device_fd, &gelukt, 1); if(routing_mode == RMODE_ROUTER) {
if(gelukt != 1) { overwrite_mac = 1;
logger(LOG_DEBUG, "Tap reader failed!");
return false;
} }
if(!get_config_string(lookup_config(config_tree, "Interface"), &iface)) if(!get_config_string(lookup_config(config_tree, "Interface"), &iface))
iface = device; iface = device;
device_info = _("Cygwin CIPE device"); device_info = _("Windows tap device");
logger(LOG_INFO, _("%s is a %s"), device, device_info); logger(LOG_INFO, _("%s (%s) is a %s"), device, iface, device_info);
return true; return true;
} }
@ -246,11 +171,7 @@ void close_device(void)
{ {
cp(); cp();
close(sp[0]); CloseHandle(device_fd);
close(sp[1]);
CloseHandle(handle);
kill(reader_pid, SIGKILL);
} }
bool read_packet(vpn_packet_t *packet) bool read_packet(vpn_packet_t *packet)
@ -259,7 +180,7 @@ bool read_packet(vpn_packet_t *packet)
cp(); cp();
if((lenin = read(sp[0], packet->data, MTU)) <= 0) { if(!ReadFile(device_fd, packet->data, MTU, &lenin, NULL)) {
logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info,
device, strerror(errno)); device, strerror(errno));
return false; return false;
@ -284,7 +205,7 @@ bool write_packet(vpn_packet_t *packet)
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Writing packet of %d bytes to %s"), ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Writing packet of %d bytes to %s"),
packet->len, device_info); packet->len, device_info);
if(!WriteFile (handle, packet->data, packet->len, &lenout, NULL)) { if(!WriteFile(device_fd, packet->data, packet->len, &lenout, NULL)) {
logger(LOG_ERR, "Error while writing to %s %s", device_info, device); logger(LOG_ERR, "Error while writing to %s %s", device_info, device);
return false; return false;
} }