1 /* Copyright (C) 2010-2011 Monty Program Ab & Vladislav Vaintroub
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; version 2 of the License.
6 
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11 
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
15 
16 /*
17   mysql_install_db creates a new database instance (optionally as service)
18   on Windows.
19 */
20 #define DONT_DEFINE_VOID
21 #include "mariadb.h"
22 #include <my_getopt.h>
23 #include <m_string.h>
24 
25 #include <windows.h>
26 #include <shellapi.h>
27 #include <accctrl.h>
28 #include <aclapi.h>
29 struct IUnknown;
30 #include <shlwapi.h>
31 
32 #define USAGETEXT \
33 "mysql_install_db.exe  Ver 1.00 for Windows\n" \
34 "Copyright (C) 2010-2011 Monty Program Ab & Vladislav Vaintroub\n" \
35 "This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n" \
36 "and you are welcome to modify and redistribute it under the GPL v2 license\n" \
37 "Usage: mysql_install_db.exe [OPTIONS]\n" \
38 "OPTIONS:"
39 
40 extern "C" const char* mysql_bootstrap_sql[];
41 
42 static char default_os_user[]= "NT AUTHORITY\\NetworkService";
43 static char default_datadir[MAX_PATH];
44 static int create_db_instance();
45 static uint opt_silent;
46 static char datadir_buffer[FN_REFLEN];
47 static char mysqld_path[FN_REFLEN];
48 static char *opt_datadir;
49 static char *opt_service;
50 static char *opt_password;
51 static int  opt_port;
52 static int  opt_innodb_page_size;
53 static char *opt_socket;
54 static char *opt_os_user;
55 static char *opt_os_password;
56 static my_bool opt_default_user;
57 static my_bool opt_allow_remote_root_access;
58 static my_bool opt_skip_networking;
59 static my_bool opt_verbose_bootstrap;
60 static my_bool verbose_errors;
61 
62 #define DEFAULT_INNODB_PAGE_SIZE 16*1024
63 
64 static struct my_option my_long_options[]=
65 {
66   {"help", '?', "Display this help message and exit.", 0, 0, 0, GET_NO_ARG,
67    NO_ARG, 0, 0, 0, 0, 0, 0},
68   {"datadir", 'd', "Data directory of the new database",
69   &opt_datadir, &opt_datadir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
70   {"service", 'S', "Name of the Windows service",
71   &opt_service, &opt_service, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
72   {"password", 'p', "Root password",
73   &opt_password, &opt_password, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
74   {"port", 'P', "mysql port",
75   &opt_port, &opt_port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
76   {"socket", 'W',
77   "named pipe name (if missing, it will be set the same as service)",
78   &opt_socket, &opt_socket, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
79   {"default-user", 'D', "Create default user",
80   &opt_default_user, &opt_default_user, 0 , GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
81   {"allow-remote-root-access", 'R',
82   "Allows remote access from network for user root",
83   &opt_allow_remote_root_access, &opt_allow_remote_root_access, 0 , GET_BOOL,
84   OPT_ARG, 0, 0, 0, 0, 0, 0},
85   {"skip-networking", 'N', "Do not use TCP connections, use pipe instead",
86   &opt_skip_networking, &opt_skip_networking, 0 , GET_BOOL, OPT_ARG, 0, 0, 0, 0,
87   0, 0},
88   { "innodb-page-size", 'i', "Page size for innodb",
89   &opt_innodb_page_size, &opt_innodb_page_size, 0, GET_INT, REQUIRED_ARG, DEFAULT_INNODB_PAGE_SIZE, 1*1024, 64*1024, 0, 0, 0 },
90   {"silent", 's', "Print less information", &opt_silent,
91    &opt_silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
92   {"verbose-bootstrap", 'o', "Include mysqld bootstrap output",&opt_verbose_bootstrap,
93    &opt_verbose_bootstrap, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
94   {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
95 };
96 
97 
98 static my_bool
get_one_option(const struct my_option * opt,const char *,const char *)99 get_one_option(const struct my_option *opt, const char *, const char *)
100 {
101   DBUG_ENTER("get_one_option");
102   switch (opt->id) {
103   case '?':
104     printf("%s\n", USAGETEXT);
105     my_print_help(my_long_options);
106     exit(0);
107     break;
108   }
109   DBUG_RETURN(0);
110 }
111 
112 
die(const char * fmt,...)113 ATTRIBUTE_NORETURN  static void die(const char *fmt, ...)
114 {
115   va_list args;
116   DBUG_ENTER("die");
117 
118   /* Print the error message */
119   va_start(args, fmt);
120   fprintf(stderr, "FATAL ERROR: ");
121   vfprintf(stderr, fmt, args);
122   fputc('\n', stderr);
123   if (verbose_errors)
124   {
125    fprintf(stderr,
126    "https://mariadb.com/kb/en/installation-issues-on-windows contains some help\n"
127    "for solving the most common problems.  If this doesn't help you, please\n"
128    "leave a comment in the Knowledge Base or file a bug report at\n"
129    "https://jira.mariadb.org");
130   }
131   fflush(stderr);
132   va_end(args);
133   my_end(0);
134   exit(1);
135 }
136 
137 
verbose(const char * fmt,...)138 static void verbose(const char *fmt, ...)
139 {
140   va_list args;
141 
142   if (opt_silent)
143     return;
144 
145   /* Print the verbose message */
146   va_start(args, fmt);
147   vfprintf(stdout, fmt, args);
148   fputc('\n', stdout);
149   fflush(stdout);
150   va_end(args);
151 }
152 
153 
main(int argc,char ** argv)154 int main(int argc, char **argv)
155 {
156   int error;
157   char self_name[FN_REFLEN];
158   char *p;
159 
160   MY_INIT(argv[0]);
161   GetModuleFileName(NULL, self_name, FN_REFLEN);
162   strcpy(mysqld_path,self_name);
163   p= strrchr(mysqld_path, FN_LIBCHAR);
164   if (p)
165   {
166     strcpy(p, "\\mysqld.exe");
167   }
168 
169   if ((error= handle_options(&argc, &argv, my_long_options, get_one_option)))
170     exit(error);
171   if (!opt_datadir)
172   {
173     /*
174       Figure out default data directory. It "data" directory, next to "bin" directory, where
175       mysql_install_db.exe resides.
176     */
177     strcpy(default_datadir, self_name);
178     p = strrchr(default_datadir, FN_LIBCHAR);
179     if (p)
180     {
181       *p= 0;
182       p= strrchr(default_datadir, FN_LIBCHAR);
183       if (p)
184         *p= 0;
185     }
186     if (!p)
187     {
188       die("--datadir option not provided, and default datadir not found");
189       my_print_help(my_long_options);
190     }
191     strcat_s(default_datadir, "\\data");
192     opt_datadir= default_datadir;
193     printf("Default data directory is %s\n",opt_datadir);
194   }
195 
196   /* Print some help on errors */
197   verbose_errors= TRUE;
198 
199   if (!opt_os_user)
200   {
201     opt_os_user= default_os_user;
202     opt_os_password= NULL;
203   }
204   /* Workaround WiX bug (strip possible quote character at the end of path) */
205   size_t len= strlen(opt_datadir);
206   if (len > 0)
207   {
208     if (opt_datadir[len-1] == '"')
209     {
210       opt_datadir[len-1]= 0;
211     }
212   }
213   GetFullPathName(opt_datadir, FN_REFLEN, datadir_buffer, NULL);
214   opt_datadir= datadir_buffer;
215 
216   if (create_db_instance())
217   {
218     die("database creation failed");
219   }
220 
221   printf("Creation of the database was successful\n");
222   return 0;
223 }
224 
225 
226 
227 /**
228   Convert slashes in paths into MySQL-compatible form
229 */
230 
convert_slashes(char * s)231 static void convert_slashes(char *s)
232 {
233   for (; *s ; s++)
234    if (*s == '\\')
235      *s= '/';
236 }
237 
238 
239 /**
240   Calculate basedir from mysqld.exe path.
241   Basedir assumed to be is one level up from the mysqld.exe directory location.
242   E.g basedir for C:\my\bin\mysqld.exe would be C:\my
243 */
244 
get_basedir(char * basedir,int size,const char * mysqld_path)245 static void get_basedir(char *basedir, int size, const char *mysqld_path)
246 {
247   strcpy_s(basedir, size,  mysqld_path);
248   convert_slashes(basedir);
249   char *p= strrchr(basedir,'/');
250   if (p)
251   {
252     *p = 0;
253     p= strrchr(basedir, '/');
254     if (p)
255       *p= 0;
256   }
257 }
258 
259 #define STR(s) _STR(s)
260 #define _STR(s) #s
261 
get_plugindir()262 static char *get_plugindir()
263 {
264   static char plugin_dir[2*MAX_PATH];
265   get_basedir(plugin_dir, sizeof(plugin_dir), mysqld_path);
266   strcat(plugin_dir, "/" STR(INSTALL_PLUGINDIR));
267 
268   if (access(plugin_dir, 0) == 0)
269     return plugin_dir;
270 
271   return NULL;
272 }
273 
274 /**
275   Allocate and initialize command line for mysqld --bootstrap.
276  The resulting string is passed to popen, so it has a lot of quoting
277  quoting around the full string plus quoting around parameters with spaces.
278 */
279 
init_bootstrap_command_line(char * cmdline,size_t size)280 static char *init_bootstrap_command_line(char *cmdline, size_t size)
281 {
282   char basedir[MAX_PATH];
283   get_basedir(basedir, sizeof(basedir), mysqld_path);
284 
285   my_snprintf(cmdline, size - 1,
286     "\"\"%s\" --no-defaults %s --innodb-page-size=%d --bootstrap"
287     " \"--lc-messages-dir=%s/share\""
288     " --basedir=. --datadir=. --default-storage-engine=myisam"
289     " --max_allowed_packet=9M "
290     " --net-buffer-length=16k\"", mysqld_path,
291     opt_verbose_bootstrap ? "--console" : "", opt_innodb_page_size, basedir);
292   return cmdline;
293 }
294 
295 
296 /**
297   Create my.ini in  current directory (this is assumed to be
298   data directory as well).
299 */
300 
create_myini()301 static int create_myini()
302 {
303   my_bool enable_named_pipe= FALSE;
304   printf("Creating my.ini file\n");
305 
306   char path_buf[MAX_PATH];
307   GetCurrentDirectory(MAX_PATH, path_buf);
308 
309   /* Create ini file. */
310   FILE *myini= fopen("my.ini","wt");
311   if (!myini)
312   {
313     die("Can't create my.ini in data directory");
314   }
315 
316   /* Write out server settings. */
317   fprintf(myini, "[mysqld]\n");
318   convert_slashes(path_buf);
319   fprintf(myini, "datadir=%s\n", path_buf);
320   if (opt_skip_networking)
321   {
322     fprintf(myini,"skip-networking\n");
323     if (!opt_socket)
324       opt_socket= opt_service;
325   }
326   enable_named_pipe= (my_bool)
327     ((opt_socket && opt_socket[0]) || opt_skip_networking);
328 
329   if (enable_named_pipe)
330   {
331     fprintf(myini,"named-pipe=ON\n");
332   }
333 
334   if (opt_socket && opt_socket[0])
335   {
336     fprintf(myini, "socket=%s\n", opt_socket);
337   }
338   if (opt_port)
339   {
340     fprintf(myini,"port=%d\n", opt_port);
341   }
342   if (opt_innodb_page_size != DEFAULT_INNODB_PAGE_SIZE)
343   {
344     fprintf(myini, "innodb-page-size=%d\n", opt_innodb_page_size);
345   }
346   /* Write out client settings. */
347   fprintf(myini, "[client]\n");
348 
349   /* Used for named pipes */
350   if (opt_socket && opt_socket[0])
351     fprintf(myini,"socket=%s\n",opt_socket);
352   if (opt_skip_networking)
353     fprintf(myini,"protocol=pipe\n");
354   else if (opt_port)
355     fprintf(myini,"port=%d\n",opt_port);
356 
357   char *plugin_dir = get_plugindir();
358   if (plugin_dir)
359     fprintf(myini, "plugin-dir=%s\n", plugin_dir);
360   fclose(myini);
361   return 0;
362 }
363 
364 
365 static const char update_root_passwd_part1[]=
366   "UPDATE mysql.global_priv SET priv=json_set(priv,"
367   "'$.password_last_changed', UNIX_TIMESTAMP(),"
368   "'$.plugin','mysql_native_password',"
369   "'$.authentication_string',PASSWORD(";
370 static const char update_root_passwd_part2[]=
371   ")) where User='root';\n";
372 static const char remove_default_user_cmd[]=
373   "DELETE FROM mysql.user where User='';\n";
374 static const char allow_remote_root_access_cmd[]=
375   "CREATE TEMPORARY TABLE tmp_user LIKE global_priv;\n"
376   "INSERT INTO tmp_user SELECT * from global_priv where user='root' "
377     " AND host='localhost';\n"
378   "UPDATE tmp_user SET host='%';\n"
379   "INSERT INTO global_priv SELECT * FROM tmp_user;\n"
380   "DROP TABLE tmp_user;\n";
381 static const char end_of_script[]="-- end.";
382 
383 /* Register service. Assume my.ini is in datadir */
384 
register_service()385 static int register_service()
386 {
387   char buf[3*MAX_PATH +32]; /* path to mysqld.exe, to my.ini, service name */
388   SC_HANDLE sc_manager, sc_service;
389 
390   size_t datadir_len= strlen(opt_datadir);
391   const char *backslash_after_datadir= "\\";
392 
393   if (datadir_len && opt_datadir[datadir_len-1] == '\\')
394     backslash_after_datadir= "";
395 
396   verbose("Registering service '%s'", opt_service);
397   my_snprintf(buf, sizeof(buf)-1,
398     "\"%s\" \"--defaults-file=%s%smy.ini\" \"%s\"" ,  mysqld_path, opt_datadir,
399     backslash_after_datadir, opt_service);
400 
401   /* Get a handle to the SCM database. */
402   sc_manager= OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS);
403   if (!sc_manager)
404   {
405     die("OpenSCManager failed (%u)\n", GetLastError());
406   }
407 
408   /* Create the service. */
409   sc_service= CreateService(sc_manager, opt_service,  opt_service,
410     SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START,
411     SERVICE_ERROR_NORMAL, buf, NULL, NULL, NULL, opt_os_user, opt_os_password);
412 
413   if (!sc_service)
414   {
415     CloseServiceHandle(sc_manager);
416     die("CreateService failed (%u)", GetLastError());
417   }
418   char description[] = "MariaDB database server";
419   SERVICE_DESCRIPTION sd= { description };
420   ChangeServiceConfig2(sc_service, SERVICE_CONFIG_DESCRIPTION, &sd);
421   CloseServiceHandle(sc_service);
422   CloseServiceHandle(sc_manager);
423   return 0;
424 }
425 
426 
clean_directory(const char * dir)427 static void clean_directory(const char *dir)
428 {
429   char dir2[MAX_PATH + 4]= {};
430   snprintf(dir2, MAX_PATH+2, "%s\\*", dir);
431 
432   SHFILEOPSTRUCT fileop;
433   fileop.hwnd= NULL;    /* no status display */
434   fileop.wFunc= FO_DELETE;  /* delete operation */
435   fileop.pFrom= dir2;  /* source file name as double null terminated string */
436   fileop.pTo= NULL;    /* no destination needed */
437   fileop.fFlags= FOF_NOCONFIRMATION|FOF_SILENT;  /* do not prompt the user */
438 
439 
440   fileop.fAnyOperationsAborted= FALSE;
441   fileop.lpszProgressTitle= NULL;
442   fileop.hNameMappings= NULL;
443 
444   SHFileOperation(&fileop);
445 }
446 
447 
448 /*
449   Define directory permission to have inheritable all access for a user
450   (defined as username or group string or as SID)
451 */
452 
set_directory_permissions(const char * dir,const char * os_user)453 static int set_directory_permissions(const char *dir, const char *os_user)
454 {
455 
456    struct{
457         TOKEN_USER tokenUser;
458         BYTE buffer[SECURITY_MAX_SID_SIZE];
459    } tokenInfoBuffer;
460 
461   HANDLE hDir= CreateFile(dir,READ_CONTROL|WRITE_DAC,0,NULL,OPEN_EXISTING,
462     FILE_FLAG_BACKUP_SEMANTICS,NULL);
463   if (hDir == INVALID_HANDLE_VALUE)
464     return -1;
465   ACL* pOldDACL;
466   SECURITY_DESCRIPTOR* pSD= NULL;
467   EXPLICIT_ACCESS ea={0};
468   WELL_KNOWN_SID_TYPE wellKnownSidType = WinNullSid;
469   PSID pSid= NULL;
470 
471   GetSecurityInfo(hDir, SE_FILE_OBJECT , DACL_SECURITY_INFORMATION,NULL, NULL,
472     &pOldDACL, NULL, (void**)&pSD);
473 
474   if (os_user)
475   {
476     /* Check for 3 predefined service users
477        They might have localized names in non-English Windows, thus they need
478        to be handled using well-known SIDs.
479     */
480     if (stricmp(os_user, "NT AUTHORITY\\NetworkService") == 0)
481     {
482       wellKnownSidType= WinNetworkServiceSid;
483     }
484     else if (stricmp(os_user, "NT AUTHORITY\\LocalService") == 0)
485     {
486       wellKnownSidType= WinLocalServiceSid;
487     }
488     else if (stricmp(os_user, "NT AUTHORITY\\LocalSystem") == 0)
489     {
490       wellKnownSidType= WinLocalSystemSid;
491     }
492 
493     if (wellKnownSidType != WinNullSid)
494     {
495       DWORD size= SECURITY_MAX_SID_SIZE;
496       pSid= (PSID)tokenInfoBuffer.buffer;
497       if (!CreateWellKnownSid(wellKnownSidType, NULL, pSid,
498         &size))
499       {
500         return 1;
501       }
502       ea.Trustee.TrusteeForm= TRUSTEE_IS_SID;
503       ea.Trustee.ptstrName= (LPTSTR)pSid;
504     }
505     else
506     {
507       ea.Trustee.TrusteeForm= TRUSTEE_IS_NAME;
508       ea.Trustee.ptstrName= (LPSTR)os_user;
509     }
510   }
511   else
512   {
513     HANDLE token;
514     if (OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY, &token))
515     {
516 
517       DWORD length= (DWORD) sizeof(tokenInfoBuffer);
518       if (GetTokenInformation(token, TokenUser, &tokenInfoBuffer,
519         length, &length))
520       {
521         pSid= tokenInfoBuffer.tokenUser.User.Sid;
522       }
523     }
524     if (!pSid)
525       return 0;
526     ea.Trustee.TrusteeForm= TRUSTEE_IS_SID;
527     ea.Trustee.ptstrName= (LPTSTR)pSid;
528   }
529   ea.grfAccessMode= GRANT_ACCESS;
530   ea.grfAccessPermissions= GENERIC_ALL;
531   ea.grfInheritance= CONTAINER_INHERIT_ACE|OBJECT_INHERIT_ACE;
532   ea.Trustee.TrusteeType= TRUSTEE_IS_UNKNOWN;
533   ACL* pNewDACL= 0;
534   SetEntriesInAcl(1,&ea,pOldDACL,&pNewDACL);
535   if (pNewDACL)
536   {
537     SetSecurityInfo(hDir,SE_FILE_OBJECT,DACL_SECURITY_INFORMATION,NULL, NULL,
538       pNewDACL, NULL);
539   }
540   if (pSD != NULL)
541     LocalFree((HLOCAL) pSD);
542   if (pNewDACL != NULL)
543     LocalFree((HLOCAL) pNewDACL);
544   CloseHandle(hDir);
545   return 0;
546 }
547 
548 
549 
550 /* Create database instance (including registering as service etc) .*/
551 
create_db_instance()552 static int create_db_instance()
553 {
554   int ret= 0;
555   char cwd[MAX_PATH];
556   DWORD cwd_len= MAX_PATH;
557   char cmdline[3*MAX_PATH];
558   FILE *in;
559   bool created_datadir= false;
560   DWORD last_error;
561 
562   verbose("Running bootstrap");
563 
564   GetCurrentDirectory(cwd_len, cwd);
565 
566   /* Create datadir and datadir/mysql, if they do not already exist. */
567 
568   if (CreateDirectory(opt_datadir, NULL))
569   {
570     created_datadir= true;
571   }
572   else if (GetLastError() != ERROR_ALREADY_EXISTS)
573   {
574     last_error = GetLastError();
575     switch(last_error)
576     {
577       case ERROR_ACCESS_DENIED:
578         die("Can't create data directory '%s' (access denied)\n",
579             opt_datadir);
580         break;
581       case ERROR_PATH_NOT_FOUND:
582         die("Can't create data directory '%s' "
583             "(one or more intermediate directories do not exist)\n",
584             opt_datadir);
585         break;
586       default:
587         die("Can't create data directory '%s', last error %u\n",
588          opt_datadir, last_error);
589         break;
590     }
591   }
592 
593   if (!SetCurrentDirectory(opt_datadir))
594   {
595     last_error = GetLastError();
596     switch (last_error)
597     {
598       case ERROR_DIRECTORY:
599         die("Can't set current directory to '%s', the path is not a valid directory \n",
600             opt_datadir);
601         break;
602       default:
603         die("Can' set current directory to '%s', last error %u\n",
604             opt_datadir, last_error);
605         break;
606     }
607   }
608 
609   if (!PathIsDirectoryEmpty(opt_datadir))
610   {
611     fprintf(stderr,"ERROR : Data directory %s is not empty."
612         " Only new or empty existing directories are accepted for --datadir\n",opt_datadir);
613     exit(1);
614   }
615 
616   if (!CreateDirectory("mysql",NULL))
617   {
618     last_error = GetLastError();
619     DWORD attributes;
620     switch(last_error)
621     {
622       case ERROR_ACCESS_DENIED:
623         die("Can't create subdirectory 'mysql' in '%s' (access denied)\n",opt_datadir);
624         break;
625       case ERROR_ALREADY_EXISTS:
626        attributes = GetFileAttributes("mysql");
627 
628        if (attributes == INVALID_FILE_ATTRIBUTES)
629          die("GetFileAttributes() failed for existing file '%s\\mysql', last error %u",
630             opt_datadir, GetLastError());
631        else if (!(attributes & FILE_ATTRIBUTE_DIRECTORY))
632          die("File '%s\\mysql' exists, but it is not a directory", opt_datadir);
633 
634        break;
635     }
636   }
637 
638   /*
639     Set data directory permissions for both current user and
640     default_os_user (the one who runs services).
641   */
642   set_directory_permissions(opt_datadir, NULL);
643   set_directory_permissions(opt_datadir, default_os_user);
644 
645   /* Do mysqld --bootstrap. */
646   init_bootstrap_command_line(cmdline, sizeof(cmdline));
647 
648   if(opt_verbose_bootstrap)
649     printf("Executing %s\n", cmdline);
650 
651   in= popen(cmdline, "wt");
652   if (!in)
653     goto end;
654 
655   if (setvbuf(in, NULL, _IONBF, 0))
656   {
657     verbose("WARNING: Can't disable buffering on mysqld's stdin");
658   }
659   if (fwrite("use mysql;\n",11,1, in) != 1)
660   {
661     verbose("ERROR: Can't write to mysqld's stdin");
662     ret= 1;
663     goto end;
664   }
665 
666   int i;
667   for (i=0; mysql_bootstrap_sql[i]; i++)
668   {
669     /* Write the bootstrap script to stdin. */
670     if (fwrite(mysql_bootstrap_sql[i], strlen(mysql_bootstrap_sql[i]), 1, in) != 1)
671     {
672       verbose("ERROR: Can't write to mysqld's stdin");
673       ret= 1;
674       goto end;
675     }
676   }
677 
678   /* Remove default user, if requested. */
679   if (!opt_default_user)
680   {
681     verbose("Removing default user",remove_default_user_cmd);
682     fputs(remove_default_user_cmd, in);
683     fflush(in);
684   }
685 
686   if (opt_allow_remote_root_access)
687   {
688      verbose("Allowing remote access for user root",remove_default_user_cmd);
689      fputs(allow_remote_root_access_cmd,in);
690      fflush(in);
691   }
692 
693   /* Change root password if requested. */
694   if (opt_password && opt_password[0])
695   {
696     verbose("Setting root password",remove_default_user_cmd);
697     fputs(update_root_passwd_part1, in);
698 
699     /* Use hex encoding for password, to avoid escaping problems.*/
700     fputc('0', in);
701     fputc('x', in);
702     for(int i= 0; opt_password[i]; i++)
703     {
704       fprintf(in,"%02x",opt_password[i]);
705     }
706 
707     fputs(update_root_passwd_part2, in);
708     fflush(in);
709   }
710 
711   /*
712     On some reason, bootstrap chokes if last command sent via stdin ends with
713     newline, so we supply a dummy comment, that does not end with newline.
714   */
715   fputs(end_of_script, in);
716   fflush(in);
717 
718   /* Check if bootstrap has completed successfully. */
719   ret= pclose(in);
720   if (ret)
721   {
722     verbose("mysqld returned error %d in pclose",ret);
723     goto end;
724   }
725 
726 
727   /* Create my.ini file in data directory.*/
728   ret= create_myini();
729   if (ret)
730     goto end;
731 
732   /* Register service if requested. */
733   if (opt_service && opt_service[0])
734   {
735     ret= register_service();
736     if (ret)
737       goto end;
738   }
739 
740 end:
741   if (ret)
742   {
743     SetCurrentDirectory(cwd);
744     clean_directory(opt_datadir);
745     if (created_datadir)
746       RemoveDirectory(opt_datadir);
747   }
748   return ret;
749 }
750