Niels, thanks for the fix --- unfortunately a compile from scratch fails as follows: ======== make[5]: Entering directory /net/corona.ins.cwi.nl/export/scratch0/manegold/Monet/Testing/Stable/build.--enable-strict_--disable-optimize_--enable-debug_--enable-assert/MonetDB5/src/tools' /bin/sh ../../libtool --tag=CC --mode=link gcc -Wall -Wextra -std=c99 -g -Werror-implicit-function-declaration -Werror -Wpointer-arith -Wdeclaration-after-statement -Wundef -Wp,-D_FORTIFY_SOURCE=2 -D_REENTRANT -o mserver5 -export-dynamic mserver5.o ../mal/libmal.la -L/ufs/manegold/_/scratch0/Monet/Testing/Stable/prefix.--enable-strict_--disable-optimize_--enable-debug_--enable-assert/lib64 -lbat -lmutils -lstream -lz -lbz2 -lpthread -ldl -lpcre -lssl -lcrypto -lcrypt gcc -Wall -Wextra -std=c99 -g -Werror-implicit-function-declaration -Werror -Wpointer-arith -Wdeclaration-after-statement -Wundef -Wp,-D_FORTIFY_SOURCE=2 -D_REENTRANT -o .libs/mserver5 mserver5.o -Wl,--export-dynamic ../mal/.libs/libmal.so -L/ufs/manegold/_/scratch0/Monet/Testing/Stable/prefix.--enable-strict_--disable-optimize_--enable-debug_--enable-assert/lib64 -lreadline -ltermcap /ufs/manegold/_/scratch0/Monet/Testing/Stable/prefix.--enable-strict_--disable-optimize_--enable-debug_--enable-assert/lib64/libbat.so /ufs/manegold/_/scratch0/Monet/Testing/Stable/prefix.--enable-strict_--disable-optimize_--enable-debug_--enable-assert/lib64/libstream.so /ufs/manegold/_/scratch0/Monet/Testing/Stable/prefix.--enable-strict_--disable-optimize_--enable-debug_--enable-assert/lib64/libmutils.so -lz -lbz2 -lpthread -ldl -lpcre -lssl -lcrypto -lcrypt -Wl,--rpath -Wl,/ufs/manegold/_/scratch0/Monet/Testing/Stable/prefix.--enable-strict_--disable-optimize_--enable-debug_--enable-assert/lib64 mserver5.o: In function `main': /ufs/manegold/_/scratch0/Monet/Testing/Stable/source/MonetDB5/src/tools/mserver5.mx:452: undefined reference to `monet_version' collect2: ld returned 1 exit status make[5]: *** [mserver5] Error 1 ======== It seems that autogen does not handle .c.in files properly, at least not in "SOURCE =" lists ... Stefan On Sun, Oct 14, 2007 at 08:20:25AM +0000, Niels Nes wrote:
Update of /cvsroot/monetdb/MonetDB5/src/tools In directory sc8-pr-cvs16.sourceforge.net:/tmp/cvs-serv25912/src/tools
Modified Files: Tag: MonetDB_5-2 Makefile.ag Added Files: Tag: MonetDB_5-2 monet_version.c.in monet_version.h mserver5.mx Removed Files: Tag: MonetDB_5-2 mserver5.mx.in Log Message: tablet fixed null pointer dereference split out the monet_version code as it needs configure to set variables. (mx.in's are now not allowed anymore)
Index: Makefile.ag =================================================================== RCS file: /cvsroot/monetdb/MonetDB5/src/tools/Makefile.ag,v retrieving revision 1.54.2.1 retrieving revision 1.54.2.2 diff -u -d -r1.54.2.1 -r1.54.2.2 --- Makefile.ag 3 Oct 2007 09:45:59 -0000 1.54.2.1 +++ Makefile.ag 14 Oct 2007 08:20:22 -0000 1.54.2.2 @@ -19,7 +19,7 @@ INCLUDES = -I../mal $(CLIENTS_INCS) $(MONETDB_INCS) $(OPENSSL_INCS) $(PCRE_CFLAGS)
bin_mserver5 = { - SOURCES = mserver5.mx.in + SOURCES = mserver5.mx monet_version.h monet_version.c.in LIBS = ../mal/libmal @SHARED_LIBS@ $(MONETDB_LIBS) \ -lbat -lmutils -lstream \ $(SOCKET_LIBS) $(Z_LIBS) $(BZ_LIBS) \
--- NEW FILE: monet_version.h --- #ifndef MONETDB_VERSION_H #define MONETDB_VERSION_H
extern void monet_version(void);
#endif /* MONETDB_VERSION_H */
--- NEW FILE: mserver5.mx --- @' The contents of this file are subject to the MonetDB Public License @' Version 1.1 (the "License"); you may not use this file except in @' compliance with the License. You may obtain a copy of the License at @' http://monetdb.cwi.nl/Legal/MonetDBLicense-1.1.html @' @' 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. @' @' The Original Code is the MonetDB Database System. @' @' The Initial Developer of the Original Code is CWI. @' Portions created by CWI are Copyright (C) 1997-2007 CWI. @' All Rights Reserved.
@f mserver5 @a M.L. Kersten, P. Boncz, Niels Nes, Stefan Manegold, Sjoerd Mullender @v 5.0 @* The Monet Server The program @code{ mserver5} is the MonetDB server. It is started by the database administrator. The console is primarilly meant to further initialize the server, such as starting internet listeners.
@+ Manual Page The server is a multithreaded program. There is one system thread, and for each service, e.g. a client session or transaction service, there is one worker thread. As a default, the server also starts the internet listener thread, on the port number specified in the parameter file.
@- Usage @verbatim usage: mserver5 [options] [scripts] --dbname=
--dbfarm=<directory> --dbinit=<stmt> Server prepare statement --config= --debug=<number> trace server actions[0] --daemon=yes|no run in background [no] --set <option>=<value> set environment value --help this list of options --version version and compile time info @end verbatim The server options have the following meaning: @table @code @item --dbname <db-name> open the database <db-name>. @item --config <config-file> where to find the environment settings @item --dbinit <stmt> execute the statement first. @end itemize
@{ @+ Implementation @h #ifndef _MONET_GLOBAL_H_ #define _MONET_GLOBAL_H_ #include "mal_config.h" #include "monet_options.h" #include "mal.h" #include "mal_session.h" #include "mal_import.h" #include "mal_client.h" #include "mal_function.h"
/* #define MONET_GLOBAL_DEBUG */ #endif /* _MONET_GLOBAL_H_ */ @c #include "mal_config.h" #include "mserver5.h" #include "mal_authorize.h" #include "mal_sabaoth.h" #include
#include #include /* strerror */ #include #include "monet_version.h" #ifdef _CRTDBG_MAP_ALLOC /* Windows only: our definition of new and delete clashes with the one if _CRTDBG_MAP_ALLOC is defined. */ #undef _CRTDBG_MAP_ALLOC #endif
#if defined(_MSC_VER) && _MSC_VER >= 1400 #define getcwd _getcwd #endif @- The architecture is setup to handle multiple streams of requests. The first thread started represents the server. It reads from standard input and writes to standard input. This is also a way to recognize the server actions. To start the server in the background one should use the argument -background. This closes standard input. Direct execution in the background may cause the server to hang in stdio for input from the terminal. @ The server thread started remains in existence until all other threads die. The server is stopped by cntrl-D or receiving the quit command. @
@c static int malloc_init = 1; /* NEEDED? */ #if defined(_MSC_VER) && defined(__cplusplus) #include
void mserver_abort() { fprintf(stderr, "\n! mserver_abort() was called by terminate(). !\n"); fflush(stderr); MT_global_exit(0); } #endif void usage(char *prog) { fprintf(stderr, "Usage: %s [options] [scripts]\n", prog); fprintf(stderr, " --dbname=
\n"); fprintf(stderr, " --dbfarm= \n"); fprintf(stderr," --version \n"); fprintf(stderr," --help for more details\n"); exit(0); } void usage2(char *prog) { fprintf(stderr, "Usage: %s [options] [scripts]\n", prog); fprintf(stderr, " --dbname= \n"); fprintf(stderr, " --dbfarm=<directory> \n"); fprintf(stderr, " --dbinit=<stmt> Server prepare statement\n"); fprintf(stderr, " --config= \n"); fprintf(stderr, " --daemon=yes|no run in background [no]\n"); fprintf(stderr, " --set <option>=<value> set environment value\n"); fprintf(stderr, " --help this list of options \n"); fprintf(stderr, " --version version and compile time info\n"); fprintf(stderr,"The debug options:\n"); fprintf(stderr," --threads\n"); fprintf(stderr," --memory\n"); fprintf(stderr," --properties\n"); fprintf(stderr," --io\n"); fprintf(stderr," --transactions\n"); fprintf(stderr," --modules\n"); fprintf(stderr," --algorithms\n"); fprintf(stderr," --xproperties\n"); fprintf(stderr," --performance\n"); fprintf(stderr," --debug=<bitmask>\n");
exit(0); }
@- A welcoming message is displayed to inform the user about recent changes. @c void monet_hello(opt *set, int setlen) { #ifdef STATIC char *linkinfo = "statically"; #else char *linkinfo = "dynamically"; #endif char *msg = mo_find_option(set, setlen, "monet_welcome");
if (msg && strcmp(msg, "yes") == 0) { printf("# MonetDB server v" VERSION ", based on kernel v%s\n", GDKversion()); printf("# Serving database '%s'\n", GDKgetenv("gdk_dbname")); printf("# Compiled for %s/" SZFMT "bit with " SZFMT "bit OIDs %s linked\n", HOST, (size_t) (sizeof(ptr) * 8), (size_t) (sizeof(oid) * 8), linkinfo); #ifdef MONET_GLOBAL_DEBUG printf("# Configuration:%s\n", GDKgetenv("config")); printf("# Database farm:%s\n", GDKgetenv("gdk_dbfarm")); #endif printf("# Copyright (c) 1993-2007 CWI, all rights reserved\n"); printf("# Visit http://monetdb.cwi.nl/ for further information\n"); } }
@- Version information, and compile time options. @c #ifdef HAVE_LIBPCRE #include
#endif #ifdef HAVE_OPENSSL #include #endif str absolute_path(str s) { if (!MT_path_absolute(s)) { str ret = (str) GDKmalloc(strlen(s) + strlen(monet_cwd) + 2);
sprintf(ret, "%s%c%s", monet_cwd, DIR_SEP, s); return ret; } return GDKstrdup(s); }
@- The options obtained during initialization should be maintained as a global structure for other components to extract information. @c #define BSIZE 8192
int monet_init(opt *set, int setlen) { char *p; opt *n = (opt *) malloc(setlen * sizeof(opt)); int i, j, nlen = 0;
char *dbname = mo_find_option(set, setlen, "gdk_dbname"); char *dbfarm = mo_find_option(set, setlen, "gdk_dbfarm"); char *alloc_map = mo_find_option(set, setlen, "gdk_alloc_map");
if (n == NULL || dbname == NULL || dbfarm == NULL || alloc_map == NULL) { fprintf(stderr, "Error, no database name or directory\n"); if (n) free(n); return 0; }
dbfarm = mo_substitute(set, setlen, dbfarm);
if ((p = mo_find_option(set, setlen, "gdk_debug"))) GDKdebug = strtol(p, NULL, 10);
if ((p = mo_find_option(set, setlen, "gdk_mem_pagebits"))) GDK_mem_pagebits = strtol(p, NULL, 10);
if ((p = mo_find_option(set, setlen, "gdk_vmtrim"))) GDK_vm_trim = strcasecmp(p, "yes") == 0;
/* determine Monet's kernel settings */ if (!GDKinit(dbname, dbfarm, strcasecmp(alloc_map, "yes") == 0) ) { free(dbfarm); free(n); return 0; } free(dbfarm);
@- Find duplicate entries in the property list and move them to the front. Actually, this should be done in monet_options.mx.in @c for (i = 0; i < setlen; i++) { int done = 0;
for (j = 0; j < nlen; j++) { if (strcmp(n[j].name, set[i].name) == 0) { if (n[j].kind < set[i].kind) { n[j] = set[i]; } done = 1; break; } } if (!done) { n[nlen] = set[i]; nlen++; } } for (i = 0; i < nlen; i++) { char *value;
value = mo_substitute(n, nlen, n[i].value); GDKsetenv(n[i].name, value); free(value); } free(n);
if ((p = GDKgetenv("gdk_mem_bigsize"))) { /* when allocating >6% of all RAM; do so using vmalloc() iso malloc() */ lng max_mem_bigsize = GDK_mem_maxsize/16;
/* sanity check to avoid memory fragmentation */ GDK_mem_bigsize = (size_t) MIN(max_mem_bigsize, strtol(p, NULL, 10)); } if (GDKgetenv_isyes("gdk_embedded") || GDKgetenv_isyes("embedded")) { GDKembedded = 1; }
if (GDKgetenv_isyes("monet_daemon") || GDKgetenv_isyes("daemon")) { monet_daemon = 1; #ifdef HAVE_SETSID setsid(); #endif }
monet_hello(set, setlen); /* you don't need the commandline arguments anymore */ mo_free_options(set, setlen); return 1; }
void emergencyBreakpoint(){ /* just a handle to break after system initialization for GDB */ }
static void handler(int sig) { (void)sig; mal_exit(); }
int main(int argc, char **av) { char *prog = *av; opt *set = NULL; int idx = 0, debug = 0, setlen = 0, listing = 0, i = 0; str dbinit = NULL; str err = MAL_SUCCEED;
static struct option long_options[] = { {"config", 1, 0, 'c'}, {"dbname", 1, 0, 0}, {"dbfarm", 1, 0, 0}, {"dbinit", 1, 0, 0}, {"daemon", 1, 0, 0}, {"debug", 2, 0, 'd'}, {"help", 0, 0, 'h'}, {"version", 0, 0, 0}, {"set", 1, 0, 's'}, {"trace", 0, 0, 't'}, {"threads",0,0,0}, {"memory",0,0,0}, {"properties",0,0,0}, {"io",0,0,0}, {"transaction",0,0,0}, {"modules",0,0,0}, {"algorithms",0,0,0}, {"performance",0,0,0}, {"xproperties",0,0,0}, {0, 0, 0, 0} };
@- We give malloc advice here. Main goal: prevent fragmentation. We do this by declaring everything below 2K as 'small'. These values will be drawn from a fixed pools of 400K. A grain size of 128 bytes is used to keep overhead low.
We do this by declaring everything below 2K as 'small'. These values will be drawn from a fixed pools of 400K. A grain size of 128 bytes is used to keep overhead low. Trivial remark: for dynamically linked executables the mallopt capabilities depend on the malloc implementation used at run time.
Unlike V4 we ignore the alloc_map advice, which leads to a much faster system start. @= mallopt if (malloc_init) { /* for (Red Hat) Linux (6.2) unused and ignored at least as of glibc-2.1.3-15 */ /* for (Red Hat) Linux (8) used at least as of glibc-2.2.93-5 */ if (mallopt(M_MXFAST, 192)) { fprintf(stderr, "!monet: mallopt(M_MXFAST,192) fails.\n"); } #ifdef M_BLKSZ if (mallopt(M_BLKSZ, 8*1024)) { fprintf(stderr, "!monet: mallopt(M_BLKSZ,8*1024) fails.\n"); } #endif } malloc_init=0; @c
#if defined(_MSC_VER) && defined(__cplusplus) set_terminate(mserver_abort); #endif if (setlocale(LC_CTYPE, "") == NULL) { GDKfatal("cannot set locale\n"); }
#ifdef HAVE_MALLOPT @:mallopt@ #else (void) malloc_init; /* still unused */ #endif
if (getcwd(monet_cwd, PATHLENGTH - 1) == NULL) { perror("pwd"); GDKfatal("monet_init: could not determine current directory\n"); }
if (!(setlen = mo_builtin_settings(&set))) usage(prog);
setlen = mo_add_option(&set, setlen, opt_config, "prefix", MONETDBPREFIX); setlen = mo_add_option(&set, setlen, opt_config, "config", MONETDBCONFIG);
for (;;) { int option_index = 0;
int c = getopt_long(argc, av, "c:d::t:r:h?s:m:i:a:e:x", long_options, &option_index);
if (c == -1) break;
switch (c) { case 0: if (strcmp(long_options[option_index].name, "dbname") == 0) { setlen = mo_add_option(&set, setlen, opt_cmdline, "gdk_dbname", optarg); break; } if (strcmp(long_options[option_index].name, "dbfarm") == 0) { setlen = mo_add_option(&set, setlen, opt_cmdline, "gdk_dbfarm", optarg); break; } if (strcmp(long_options[option_index].name, "dbinit") == 0) { if (dbinit) fprintf(stderr, "#warning: ignoring multiple --dbinit argument\n"); else dbinit = optarg; break; } if (strcmp(long_options[option_index].name, "daemon") == 0) { setlen = mo_add_option(&set, setlen, opt_cmdline, "gdk_daemon", optarg); break; } if (strcmp(long_options[option_index].name, "version") == 0) { monet_version(); break; } /* debugging options */ if (strcmp(long_options[option_index].name, "properties") == 0) { debug |=GRPproperties; break; } if (strcmp(long_options[option_index].name, "algorithms") == 0) { debug|= GRPalgorithms; break; } if (strcmp(long_options[option_index].name, "xproperties") == 0) { debug|= GRPxproperties; break; } if (strcmp(long_options[option_index].name, "performance") == 0) { debug|= GRPperformance; break; } if (strcmp(long_options[option_index].name, "io") == 0) { debug|= GRPio; break; } if (strcmp(long_options[option_index].name, "memory") == 0) { debug|= GRPmemory; break; } if (strcmp(long_options[option_index].name, "modules") == 0) { debug|= GRPmodules; break; } if (strcmp(long_options[option_index].name, "transactions") == 0) { debug|= GRPtransactions; break; } if (strcmp(long_options[option_index].name, "threads") == 0) { debug|= GRPthreads; break; } usage(prog); break; case 'c': setlen = mo_add_option(&set, setlen, opt_cmdline, "config", optarg); break; case 'd': if (optarg) { setlen = mo_add_option(&set, setlen, opt_cmdline, "gdk_debug", optarg); } else { debug = 1; } break; case 's':{ /* should add option to a list */ char *tmp = strchr(optarg, '=');
if (tmp) { *tmp = '\0'; setlen = mo_add_option(&set, setlen, opt_cmdline, optarg, tmp + 1); } else { fprintf(stderr, "ERROR: wrong format %s\n", optarg); } } break; case 't': /* trace option, ignored to reduce testweb complaints fprintf(stderr, "#warning: trace option not yet supported\n"); */ break; case 'h': if(strcmp(long_options[option_index].name, "help") == 0) { usage2(prog); break; } case '?': usage(prog); default: fprintf(stderr, "ERROR: getopt returned character " "code '%c' 0%o\n",c, c); usage(prog); } }
if (!(setlen = mo_system_config(&set, setlen))) usage(prog);
monet_script = (str *) GDKmalloc(sizeof(str) * (argc + 1)); monet_script[idx] = NULL; while (optind < argc) { monet_script[idx] = absolute_path(av[optind]); monet_script[idx + 1] = NULL; optind++; idx++; }
if (debug) mo_print_options(set, setlen); if (monet_init(set, setlen) == 0 ) return 0; /* propagate the debug flag to the environment table */ if( debug){ char buf[BUFSIZ]; snprintf(buf,BUFSIZ,"%d",debug); GDKsetenv("gdk_debug", buf); GDKdebug=debug; /* overruled by command line */ }
GDKsetenv("monet_version", VERSION);
/* configure sabaoth to use the right dbfarm and active database */ SABAOTHinit(GDKgetenv("gdk_dbfarm"), GDKgetenv("gdk_dbname")); /* wipe out all cruft, if left over */ if ((err = SABAOTHwildRetreat(&i)) != MAL_SUCCEED) { /* just swallow the error */ GDKfree(err); } /* From this point, the server should exit cleanly. Discussion: * even earlier? Sabaoth here registers the server has started up. */ if ((err = SABAOTHregisterStart(&i)) != MAL_SUCCEED) { /* throw the error at the user, but don't die */ fprintf(stderr, "!%s\n", err); GDKfree(err); }
#ifdef HAVE_SIGACTION { struct sigaction sa;
sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = handler; if ( sigaction(SIGINT, &sa, NULL) == -1 || sigaction(SIGQUIT, &sa, NULL) == -1 || sigaction(SIGTERM, &sa, NULL) == -1) { fprintf(stderr, "!unable to create signal handlers\n"); } } #else signal(SIGINT, handler); #ifdef SIGQUIT signal(SIGQUIT, handler); #endif signal(SIGTERM, handler); #endif
{ str lang = "mal"; /* we inited mal before, so publish its existence */ if ((err = SABAOTHmarchScenario(&i, &lang)) != MAL_SUCCEED) { /* throw the error at the user, but don't die */ fprintf(stderr, "!%s\n", err); GDKfree(err); } }
{ /* unlock the vault, first see if we can find the file which * holds the secret */ char* secret = alloca(sizeof(char) * 1024); FILE* secretf; size_t len;
if (GDKgetenv("monet_vault_key") == NULL) { /* use a default (hard coded, non safe) key */ secret = "Xas632jsi2whjds8"; fprintf(stderr, "#warning: please don't forget to set your " "vault key!\n#(see %s)\n", GDKgetenv("config")); } else { if ((secretf = fopen(GDKgetenv("monet_vault_key"), "r")) == NULL) { snprintf(secret, 1023, "unable to open vault_key_file %s: %s", GDKgetenv("monet_vault_key"), strerror(errno)); secret[1023] = '\0'; GDKfatal(secret); } len = fread(secret, 1, 1023, secretf); secret[len] = '\0'; len = strlen(secret); /* secret can contain null-bytes */ if (len < 5) { fprintf(stderr, "#warning: your vault key is too short " "(" SZFMT "), enlarge your vault key!\n", len); /* penis */ } fclose(secretf); } if ((err = AUTHunlockVault(&secret)) != MAL_SUCCEED) GDKfatal(err); } /* make sure the authorisation BATs are loaded */ if ((err = AUTHinitTables()) != MAL_SUCCEED) GDKfatal(err); if (mal_init()) return 0;
@- Time to execute the script files. Start with the init command[todo], which may be defined in the configuration file. @c MSinitClientPrg(mal_clients, "user","main"); if (dbinit == NULL) dbinit = GDKgetenv("dbinit"); if (dbinit) { str input = GDKstrdup(dbinit);
callString(mal_clients, input, listing); GDKfree(input); }
if (GDKgetenv("mal_listing")) sscanf(GDKgetenv("mal_listing"), "%d", &listing);
emergencyBreakpoint(); for (i = 0; monet_script[i]; i++) { str msg=evalFile(mal_clients, monet_script[i], listing); /* check for internal exception message to terminate */ if(msg && strcmp(msg,"MALException:client.quit:Server stopped.")==0) mal_exit(); if( msg) GDKfree(msg); GDKfree(monet_script[i]); monet_script[i] = 0; } GDKfree(monet_script); if (monet_daemon) { while(1) MT_sleep_ms(5000); } else MSserveClient(mal_clients);
/* mal_exit calls MT_global_exit, so statements after this call will * never get reached */ mal_exit();
return 0; } @}
--- NEW FILE: monet_version.c.in ---
#include "monet_version.h"
void monet_version() { printf("MonetDB server v" VERSION " (" SZFMT "-bit), " "based on kernel v%s (" SZFMT "-bit oids)\n", (size_t) (sizeof(ptr) * 8), GDKversion(), (size_t) (sizeof(oid) * 8)); printf("Copyright (c) 1993-2007 CWI, all rights reserved\n"); printf("Visit http://monetdb.cwi.nl/ for further information\n"); printf("Configured for prefix: " MONETDBPREFIX "\n"); printf("Libraries:\n"); #ifdef HAVE_LIBPCRE printf(" libpcre: %s (%s)\n", pcre_version(), "@pcreversion@"); #endif #ifdef HAVE_OPENSSL printf(" openssl: %s (%s)\n", OPENSSL_VERSION_TEXT, "@opensslversion@"); #endif printf("Compiled by: %s\n", "@builtby@"); printf("Compilation: %s\n", "@compilercall@"); #ifdef STATIC printf("Linking : %s (static)\n", "@linkercall@"); #else printf("Linking : %s\n", "@linkercall@"); #endif exit(0); }
--- mserver5.mx.in DELETED ---
-- | Dr. Stefan Manegold | mailto:Stefan.Manegold@cwi.nl | | CWI, P.O.Box 94079 | http://www.cwi.nl/~manegold/ | | 1090 GB Amsterdam | Tel.: +31 (20) 592-4212 | | The Netherlands | Fax : +31 (20) 592-4312 |