aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/drivers/win32/registry_drv.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/drivers/win32/registry_drv.c')
-rw-r--r--erts/emulator/drivers/win32/registry_drv.c535
1 files changed, 535 insertions, 0 deletions
diff --git a/erts/emulator/drivers/win32/registry_drv.c b/erts/emulator/drivers/win32/registry_drv.c
new file mode 100644
index 0000000000..05fd2ea55f
--- /dev/null
+++ b/erts/emulator/drivers/win32/registry_drv.c
@@ -0,0 +1,535 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Purpose: Interface to the registry API.
+ */
+
+#include <windows.h>
+#include "erl_driver.h"
+#include "sys.h"
+
+/*
+ * Commands recognised by this driver.
+ */
+
+#define CMD_GET_CURRENT 0
+#define CMD_OPEN_KEY 1
+#define CMD_CREATE_KEY 2
+#define CMD_GET_ALL_SUBKEYS 3
+#define CMD_GET_VALUE 4
+#define CMD_GET_ALL_VALUES 5
+#define CMD_SET_VALUE 6
+#define CMD_DELETE_KEY 7
+#define CMD_DELETE_VALUE 8
+
+/*
+ * Microsoft-specific function to map a WIN32 error code to a Posix errno.
+ */
+
+extern void _dosmaperr(DWORD);
+
+/*
+ * Information kept for a registry port (since there is no controlling
+ * Erlang process, all state must be kept here).
+ */
+
+typedef struct {
+ ErlDrvPort port; /* Port handle. */
+ REGSAM sam; /* Access for handles. */
+ HKEY hkey; /* Handle to open key. */
+ HKEY hkey_root; /* Root handle for current key. */
+ char* key; /* Name of key. */
+ DWORD key_size; /* Size of key. */
+ LPSTR name_buf; /* Buffer for names. */
+ DWORD name_buf_size; /* Size of name buffer. */
+ LPSTR value_buf; /* Buffer for values. */
+ DWORD value_buf_size; /* Size of value buffer. */
+} RegPort;
+
+
+/*
+ * Local functions.
+ */
+
+static void reply(RegPort* rp, LONG result);
+static BOOL fix_value_result(RegPort* rp, LONG result, DWORD type,
+ LPSTR name, DWORD nameSize, LPSTR value,
+ DWORD valueSize);
+static int key_reply(RegPort* rp, LPSTR name, DWORD nameSize);
+static int value_reply(RegPort* rp, DWORD type, LPSTR name, DWORD nameSize,
+ LPSTR value, DWORD valueSize);
+static int state_reply(RegPort* rp, HKEY root, LPSTR name, DWORD nameSize);
+static int maperror(DWORD error);
+/*
+ * Local variables.
+ */
+
+static int reg_init(void);
+static ErlDrvData reg_start(ErlDrvPort, char*);
+static void reg_stop(ErlDrvData);
+static void reg_from_erlang(ErlDrvData, char*, int);
+
+struct erl_drv_entry registry_driver_entry = {
+ reg_init,
+ reg_start,
+ reg_stop,
+ reg_from_erlang,
+ NULL,
+ NULL,
+ "registry__drv__",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static int
+reg_init(void)
+{
+ DEBUGF(("reg_init()\n"));
+ return 0;
+}
+
+static ErlDrvData
+reg_start(ErlDrvPort port, char* buf)
+{
+ RegPort* rp;
+ char* s;
+ REGSAM sam = KEY_READ;
+
+ if ((s = strchr(buf, ' ')) != NULL) {
+ while (isspace(*s))
+ s++;
+ while (*s != '\0') {
+ if (*s == 'r') {
+ sam |= KEY_READ;
+ } else if (*s == 'w') {
+ sam |= KEY_WRITE;
+ }
+ s++;
+ }
+ }
+
+ rp = driver_alloc(sizeof(RegPort));
+ if (rp == NULL) {
+ return ERL_DRV_ERROR_GENERAL;
+ }
+ rp->port = port;
+ rp->hkey = rp->hkey_root = HKEY_CLASSES_ROOT;
+ rp->sam = sam;
+ rp->key = driver_alloc(1);
+ rp->key_size = 0;
+ rp->name_buf_size = 64;
+ rp->name_buf = driver_alloc(rp->name_buf_size);
+ rp->value_buf_size = 64;
+ rp->value_buf = driver_alloc(rp->value_buf_size);
+ return (ErlDrvData) rp;
+}
+
+static void
+reg_stop(ErlDrvData clientData)
+{
+ RegPort* rp = (RegPort *) clientData;
+
+ (void) RegCloseKey(rp->hkey);
+ driver_free(rp->name_buf);
+ driver_free(rp->value_buf);
+ driver_free(rp->key);
+ driver_free(rp);
+ /* return 1; */
+}
+
+static void
+reg_from_erlang(ErlDrvData clientData, char* buf, int count)
+{
+ RegPort* rp = (RegPort *) clientData;
+ int cmd;
+ HKEY hkey;
+ LONG result;
+ DWORD nameSize;
+ DWORD type; /* Type of data in buffer. */
+ DWORD valueSize; /* Size of value buffer. */
+
+ cmd = buf[0];
+ buf++, count--;
+ switch (cmd) {
+ case CMD_GET_CURRENT:
+ state_reply(rp, rp->hkey_root, rp->key, rp->key_size);
+ break;
+ case CMD_OPEN_KEY:
+ {
+ char* key;
+ HKEY newKey;
+
+ /*
+ * [HKEY(DWORD), KeyString(string)]
+ */
+
+ hkey = (HKEY) get_int32(buf+0);
+ rp->hkey_root = hkey;
+ key = buf+4;
+ result = RegOpenKeyEx(hkey, key, 0, rp->sam, &newKey);
+ if (result == ERROR_SUCCESS) {
+ RegCloseKey(rp->hkey);
+ rp->hkey = newKey;
+ driver_free(rp->key);
+ rp->key_size = strlen(key);
+ rp->key = driver_alloc(rp->key_size+1);
+ strcpy(rp->key, key);
+ }
+ reply(rp, result);
+ return;
+ }
+ break;
+ case CMD_CREATE_KEY:
+ {
+ char* key;
+ HKEY newKey;
+ DWORD disposition;
+
+ hkey = (HKEY) get_int32(buf+0);
+ rp->hkey_root = hkey;
+ key = buf+4;
+ result = RegCreateKeyEx(hkey, key, 0, "", 0, rp->sam, NULL,
+ &newKey, &disposition);
+ if (result == ERROR_SUCCESS) {
+ RegCloseKey(rp->hkey);
+ rp->hkey = newKey;
+ driver_free(rp->key);
+ rp->key_size = strlen(key);
+ rp->key = driver_alloc(rp->key_size+1);
+ strcpy(rp->key, key);
+ }
+ reply(rp, result);
+ return;
+ }
+ break;
+ case CMD_GET_ALL_SUBKEYS:
+ {
+ int i;
+
+ i = 0;
+ for (;;) {
+ nameSize = rp->name_buf_size;
+ result = RegEnumKeyEx(rp->hkey, i, rp->name_buf, &nameSize,
+ NULL, NULL, NULL, NULL);
+ if (result == ERROR_MORE_DATA) {
+ rp->name_buf_size *= 2;
+ rp->name_buf = driver_realloc(rp->name_buf,
+ rp->name_buf_size);
+ continue;
+ } else if (result == ERROR_NO_MORE_ITEMS) {
+ reply(rp, ERROR_SUCCESS);
+ return;
+ } else if (result != ERROR_SUCCESS) {
+ reply(rp, result);
+ return;
+ }
+ key_reply(rp, rp->name_buf, nameSize);
+ i++;
+ }
+ }
+ break;
+ case CMD_GET_VALUE:
+ do {
+ valueSize = rp->value_buf_size;
+ result = RegQueryValueEx(rp->hkey, buf, NULL, &type,
+ rp->value_buf, &valueSize);
+ } while (!fix_value_result(rp, result, type, buf, strlen(buf),
+ rp->value_buf, valueSize));
+ break;
+ case CMD_GET_ALL_VALUES:
+ {
+ int i;
+
+ i = 0;
+ for (;;) {
+ nameSize = rp->name_buf_size;
+ valueSize = rp->value_buf_size;
+ result = RegEnumValue(rp->hkey, i, rp->name_buf, &nameSize,
+ NULL, &type, rp->value_buf, &valueSize);
+ if (result == ERROR_NO_MORE_ITEMS) {
+ reply(rp, ERROR_SUCCESS);
+ return;
+ }
+ if (fix_value_result(rp, result, type, rp->name_buf, nameSize,
+ rp->value_buf, valueSize)) {
+ i++;
+ }
+ }
+ }
+ break;
+ case CMD_SET_VALUE:
+ {
+ LPSTR name;
+ DWORD dword;
+
+ /*
+ * [Type(DWORD), Name(string), Value(bytes)]
+ */
+
+ type = get_int32(buf);
+ buf += 4;
+ count -= 4;
+ name = buf;
+ nameSize = strlen(buf) + 1;
+ buf += nameSize;
+ count -= nameSize;
+ if (type == REG_DWORD) {
+ /*
+ * Must pass a pointer to a DWORD in host byte order.
+ */
+ dword = get_int32(buf);
+ buf = (char *) &dword;
+ ASSERT(count == 4);
+ }
+ result = RegSetValueEx(rp->hkey, name, 0, type, buf, count);
+ reply(rp, result);
+ }
+ break;
+ case CMD_DELETE_KEY:
+ result = RegDeleteKey(rp->hkey, NULL);
+ reply(rp, result);
+ break;
+ case CMD_DELETE_VALUE:
+ result = RegDeleteValue(rp->hkey, buf);
+ reply(rp, result);
+ break;
+ }
+ /* return 1; */
+}
+
+static BOOL
+fix_value_result(RegPort* rp, LONG result, DWORD type,
+ LPSTR name, DWORD nameSize, LPSTR value, DWORD valueSize)
+{
+ if (result == ERROR_MORE_DATA) {
+ DWORD max_name1;
+ DWORD max_name2;
+ DWORD max_value;
+ int ok;
+
+ ok = RegQueryInfoKey(rp->hkey, NULL, NULL, NULL,
+ NULL, &max_name1, NULL, NULL, &max_name2,
+ &max_value, NULL, NULL);
+#ifdef DEBUG
+ if (ok != ERROR_SUCCESS) {
+ char buff[256];
+ sprintf(buff,"Failure in registry_drv line %d, error = %d",
+ __LINE__, GetLastError());
+ MessageBox(NULL, buff, "Internal error", MB_OK);
+ ASSERT(ok == ERROR_SUCCESS);
+ }
+#endif
+ rp->name_buf_size = (max_name1 > max_name2 ? max_name1 : max_name2)
+ + 1;
+ rp->value_buf_size = max_value + 1;
+ rp->name_buf = driver_realloc(rp->name_buf, rp->name_buf_size);
+ rp->value_buf = driver_realloc(rp->value_buf, rp->value_buf_size);
+ return FALSE;
+ } else if (result != ERROR_SUCCESS) {
+ reply(rp, result);
+ return TRUE;
+ }
+
+ /*
+ * Do some data conversion which is easier to do here
+ * than in Erlang.
+ */
+
+ switch (type) {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ valueSize--; /* No reason to send the '\0' to Erlang. */
+ break;
+ case REG_DWORD_LITTLE_ENDIAN:
+ case REG_DWORD_BIG_ENDIAN:
+ /*
+ * The value is a DWORD stored in host byte order.
+ * We must retrieve it and store it in network byte order.
+ */
+ {
+ DWORD dword = * ((DWORD *) value);
+ put_int32(dword, value);
+ type = REG_DWORD; /* Simplify life for Erlang code. */
+ break;
+ }
+ }
+
+ return value_reply(rp, type, name, nameSize, value, valueSize);
+}
+
+/*
+ * Sends one of the following replies back to Erlang,
+ * depending on result:
+ *
+ * [$e|Posix error(string)] Error
+ * [$o] Ok
+ */
+
+static void
+reply(RegPort* rp, LONG result)
+{
+ char sbuf[256];
+
+ if (result == ERROR_SUCCESS) {
+ sbuf[0] = 'o';
+ driver_output(rp->port, sbuf, 1);
+ } else {
+ char* s;
+ char* t;
+ int err;
+
+ sbuf[0] = 'e';
+ err = maperror(result);
+ for (s = erl_errno_id(err), t = sbuf+1; *s; s++, t++) {
+ *t = tolower(*s);
+ }
+ driver_output(rp->port, sbuf, t-sbuf);
+ }
+ /* return 1; */
+}
+
+/*
+ * Sends a key to Erlang:
+ *
+ * [$k, Keyname(string)]
+ */
+
+static int
+key_reply(RegPort* rp, /* Pointer to port structure. */
+ LPSTR name, /* Pointer to name. */
+ DWORD nameSize) /* Length of name. */
+{
+ char sbuf[512];
+ char* s = sbuf;
+ int needed = 1+nameSize;
+
+ if (sizeof sbuf < needed) {
+ s = driver_alloc(needed);
+ }
+
+ s[0] = 'k';
+ memcpy(s+1, name, nameSize);
+ driver_output(rp->port, s, needed);
+
+ if (s != sbuf) {
+ driver_free(s);
+ }
+ return 1;
+}
+
+/*
+ * Sends a value to Erlang:
+ *
+ * [$v, Type(DWORD), Valuename(string), 0, Value(bytes)]
+ */
+
+static int
+value_reply(RegPort* rp, /* Pointer to port structure. */
+ DWORD type, /* Type of value */
+ LPSTR name, /* Pointer to name. */
+ DWORD nameSize, /* Length of name. */
+ LPSTR value, /* Pointer to value. */
+ DWORD valueSize) /* Size of value. */
+{
+ char sbuf[512];
+ char* s = sbuf;
+ int needed = 1+4+nameSize+1+valueSize;
+ int i;
+
+ if (sizeof sbuf < needed) {
+ s = driver_alloc(needed);
+ }
+
+ s[0] = 'v';
+ i = 1;
+ put_int32(type, s+i);
+ i += 4;
+ memcpy(s+i, name, nameSize);
+ i += nameSize;
+ s[i++] = '\0';
+ memcpy(s+i, value, valueSize);
+ ASSERT(i+valueSize == needed);
+ driver_output(rp->port, s, needed);
+
+ if (s != sbuf) {
+ driver_free(s);
+ }
+ return 1;
+}
+
+/*
+ * Sends a key to Erlang:
+ *
+ * [$s, HKEY(DWORD), Keyname(string)] State
+ */
+
+static int
+state_reply(RegPort* rp, /* Pointer to port structure. */
+ HKEY root, /* Handle to root key for this key. */
+ LPSTR name, /* Pointer to name. */
+ DWORD nameSize) /* Length of name. */
+{
+ char sbuf[512];
+ char* s = sbuf;
+ int needed = 1+4+nameSize;
+ int i;
+
+ if (sizeof sbuf < needed) {
+ s = driver_alloc(needed);
+ }
+
+ s[0] = 's';
+ i = 1;
+ put_int32((DWORD) root, s+i);
+ i += 4;
+ memcpy(s+i, name, nameSize);
+ ASSERT(i+nameSize == needed);
+ driver_output(rp->port, s, needed);
+
+ if (s != sbuf) {
+ driver_free(s);
+ }
+ return 1;
+}
+
+static int
+maperror(DWORD error)
+{
+ DEBUGF(("Mapping %d\n", error));
+ switch (error) {
+ case ERROR_BADDB:
+ case ERROR_BADKEY:
+ case ERROR_CANTOPEN:
+ case ERROR_CANTWRITE:
+ case ERROR_REGISTRY_RECOVERED:
+ case ERROR_REGISTRY_CORRUPT:
+ case ERROR_REGISTRY_IO_FAILED:
+ case ERROR_NOT_REGISTRY_FILE:
+ return EIO;
+ case ERROR_KEY_DELETED:
+ return EINVAL;
+ default:
+ _dosmaperr(error);
+ return errno;
+ }
+}