1 /*
2    Copyright (c) 2012, 2021, Oracle and/or its affiliates.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 // C headers
26 #include <stdio.h>
27 #include <stdint.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <pwd.h>
32 #include <signal.h>
33 #include <spawn.h>
34 #include <pthread.h>
35 #include <sys/types.h>
36 #include <sys/wait.h>
37 
38 // MySQL headers
39 #include "my_global.h"
40 #include "my_default.h"
41 #include "my_getopt.h"
42 #include "welcome_copyright_notice.h"
43 #include "mysql_version.h"
44 #include "auth_utils.h"
45 #include "path.h"
46 #include "logger.h"
47 #include "infix_ostream_it.h"
48 #include "my_dir.h"
49 
50 // Additional C++ headers
51 #include <string>
52 #include <algorithm>
53 #include <locale>
54 #include <iostream>
55 #include <fstream>
56 #include <iterator>
57 #include <vector>
58 #include <sstream>
59 #include <map>
60 #include <iomanip>
61 
62 using namespace std;
63 
64 #include "../scripts/sql_commands_system_tables.h"
65 #include "../scripts/sql_commands_system_data.h"
66 #include "../scripts/sql_commands_help_data.h"
67 #include "../scripts/sql_commands_sys_schema.h"
68 
69 #define PROGRAM_NAME "mysql_install_db"
70 #define MYSQLD_EXECUTABLE "mysqld"
71 #define MAX_MYSQLD_ARGUMENTS 10
72 #define MAX_USER_NAME_LEN 32
73 
74 char *opt_euid= 0;
75 char *opt_basedir= 0;
76 char *opt_datadir= 0;
77 char *opt_adminlogin= 0;
78 char *opt_loginpath= 0;
79 char default_loginpath[]= "client";
80 char *opt_sqlfile= 0;
81 char default_adminuser[]= "root";
82 char *opt_adminuser= 0;
83 char default_adminhost[]= "localhost";
84 char *opt_adminhost= 0;
85 char default_authplugin[]= "mysql_native_password";
86 char *opt_authplugin= 0;
87 char *opt_mysqldfile= 0;
88 char *opt_randpwdfile= 0;
89 char default_randpwfile[]= ".mysql_secret";
90 char *opt_langpath= 0;
91 char *opt_lang= 0;
92 char default_lang[]= "en_US";
93 char *opt_defaults_file= 0;
94 char *opt_def_extra_file= 0;
95 char *opt_builddir= 0;
96 char *opt_srcdir= 0;
97 my_bool opt_no_defaults= FALSE;
98 my_bool opt_insecure= FALSE;
99 my_bool opt_verbose= FALSE;
100 my_bool opt_ssl= FALSE;
101 my_bool opt_skipsys= FALSE;
102 
103 /**
104   Connection options.
105   @note first element must be 'help' and last element must be
106   the end token: {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
107 */
108 static struct my_option my_connection_options[]=
109 {
110   {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG,
111     NO_ARG, 0, 0, 0, 0, 0, 0},
112   {"user", 'u', "The effective user id used when executing the bootstrap "
113     "sequence.", &opt_euid, &opt_euid, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0,
114     0, 0, 0},
115   {"builddir", 0, "For use with --srcdir and out-of-source builds. Set this to "
116     "the location of the directory where the built files reside.",
117     &opt_builddir, 0, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
118   {"srcdir", 0, "For internal use. This option specifies the directory under"
119     " which mysql_install_db looks for support files such as the error"
120     " message file and the file for populating the help tables.", &opt_srcdir,
121     0, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
122   {"basedir", 0, "The path to the MySQL installation directory.",
123     &opt_basedir, 0, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
124   {"datadir", 0, "The path to the MySQL data directory.", &opt_datadir,
125     0, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
126   {"login-path", 0, "Set the credential category to use with the MySQL password"
127   " store when setting default credentials. This option takes precedence over "
128   "admin-user, admin-host options.",
129     &opt_loginpath, 0, 0, GET_STR_ALLOC, REQUIRED_ARG,
130     (longlong)&default_loginpath, 0, 0, 0, 0, 0},
131    {"login-file", 0, "Use the MySQL password store at the specified location "
132   " to set the default password. This option takes precedence over admin-user, "
133   "admin-host options. Use the login-path option to change the default "
134   "credential category (default is 'client').",
135     &opt_adminlogin, 0, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
136   {"extra-sql-file", 'f', "Optional SQL file to execute during bootstrap.",
137     &opt_sqlfile, 0, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
138   {"admin-user", 0, "Username part of the default admin account.",
139     &opt_adminuser, 0, 0, GET_STR_ALLOC, REQUIRED_ARG,
140     (longlong)&default_adminuser, 0, 0, 0, 0, 0},
141   {"admin-host", 0, "Hostname part of the default admin account.",
142     &opt_adminhost, 0, 0, GET_STR_ALLOC,REQUIRED_ARG,
143     (longlong)&default_adminhost, 0, 0, 0, 0, 0},
144   {"admin-auth-plugin", 0, "Plugin to use for the default admin account.",
145     &opt_authplugin, 0, 0, GET_STR_ALLOC, REQUIRED_ARG,
146     (longlong)&default_authplugin, 0, 0, 0, 0, 0},
147   {"admin-require-ssl", 0, "Require SSL/TLS for the default admin account.",
148    &opt_ssl, 0, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
149   {"mysqld-file", 0, "Qualified path to the mysqld binary.", &opt_mysqldfile,
150    0, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
151   {"random-password-file", 0, "Specifies the qualified path to the "
152      ".mysql_secret temporary password file.", &opt_randpwdfile,
153    0, 0, GET_STR_ALLOC, REQUIRED_ARG, 0,
154    0, 0, 0, 0, 0},
155   {"insecure", 0, "Disables random passwords for the default admin account.",
156    &opt_insecure, 0, 0, GET_BOOL, NO_ARG, 0, 0, 0,
157    0, 0, 0},
158   {"verbose", 'v', "Be more verbose when running program.",
159    &opt_verbose, 0, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
160   {"version", 'V', "Print program version and exit.",
161    &opt_verbose, 0, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
162   {"lc-messages-dir", 'l', "Specifies the path to the language files.",
163    &opt_langpath, 0, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
164   {"lc-messages", 0, "Specifies the language to use.", &opt_lang,
165    0, 0, GET_STR_ALLOC, REQUIRED_ARG, (longlong)&default_lang, 0, 0, 0, 0, 0},
166   {"skip-sys-schema", 0, "Skip installation of the sys schema.",
167    &opt_skipsys, 0, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
168   /* End token */
169   {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
170 };
171 
172 Log info(cout,"NOTE");
173 Log error(cerr,"ERROR");
174 Log warning(cout, "WARNING");
175 
176 /**
177   Escapes quotes and backslash.
178   @param str The string which needs to be quoted
179   @note This is not a replacement for the mysql_real_escape_string() function
180   as it only does what is necessary for this application.
181   @return A quoted copy of the original string.
182 */
escape_string(string str)183 string escape_string(string str)
184 {
185   string esc("'\"\\");
186   for(string::iterator it= esc.begin(); it != esc.end(); ++it)
187   {
188     string::size_type idx= 0;
189     while ((idx= str.find(*it, idx)) != string::npos)
190     {
191       str.insert(idx, 1, '\\');
192       idx +=2;
193     }
194   }
195   return str;
196 }
197 
198 /**
199 
200 */
201 struct Proxy_user
202 {
Proxy_userProxy_user203   Proxy_user(string opt_host, string opt_user) : host(opt_host), user(opt_user)
204   {}
205 
206   string host;
207   string user;
to_strProxy_user208   void to_str(string *sql)
209   {
210     sql->clear();
211     sql->append("INSERT INTO proxies_priv VALUES ('");
212     sql->append(escape_string(host)).
213       append("','");
214     sql->append(escape_string(user)).
215       append("','','',TRUE,'',now());\n");
216   }
217 
218 };
219 
220 /**
221   A trivial container for attributes associated with the creation of a MySQL
222   user.
223 */
224 struct Sql_user
225 {
Sql_userSql_user226   Sql_user(string opt_host,
227            string opt_user,
228            string opt_password,
229            Access_privilege opt_priv,
230            string opt_ssl_type,
231            string opt_ssl_cipher,
232            string opt_x509_issuer,
233            string opt_x509_subject,
234            int opt_max_questions,
235            int opt_max_updates,
236            int opt_max_connections,
237            int opt_max_user_connections,
238            string opt_plugin,
239            string opt_authentication_string,
240            bool opt_password_expired,
241            int opt_password_lifetime) :
242               host(opt_host),
243               user(opt_user),
244               password(opt_password),
245               priv(opt_priv),
246               ssl_type(opt_ssl_type),
247               ssl_cipher(opt_ssl_cipher),
248               x509_issuer(opt_x509_issuer),
249               x509_subject(opt_x509_subject),
250               max_questions(opt_max_questions),
251               max_updates(opt_max_updates),
252               max_connections(opt_max_connections),
253               max_user_connections(opt_max_user_connections),
254               plugin(opt_plugin),
255               authentication_string(opt_authentication_string),
256               password_expired(opt_password_expired),
257               password_lifetime(opt_password_lifetime) {}
258 
259   string host;
260   string user;
261   string password;
262   Access_privilege priv;
263   string ssl_type;
264   string ssl_cipher;
265   string x509_issuer;
266   string x509_subject;
267   int max_questions;
268   int max_updates;
269   int max_connections;
270   int max_user_connections;
271   string plugin;
272   string authentication_string;
273   bool password_expired;
274   int password_lifetime;
275 
to_sqlSql_user276   void to_sql(string *cmdstr)
277   {
278     stringstream set_passcmd,ss, flush_priv;
279     ss << "INSERT INTO mysql.user VALUES ("
280        << "'" << escape_string(host) << "','" << escape_string(user) << "',";
281 
282     uint64_t acl= priv.to_int();
283     for(int i= 0; i< NUM_ACLS; ++i)
284     {
285       if( (acl & (1L << i)) != 0 )
286         ss << "'Y',";
287       else
288         ss << "'N',";
289     }
290     ss << "'" << escape_string(ssl_type) << "',"
291        << "'" << escape_string(ssl_cipher) << "',"
292        << "'" << escape_string(x509_issuer) << "',"
293        << "'" << escape_string(x509_subject) << "',"
294        << max_questions << ","
295        << max_updates << ","
296        << max_connections << ","
297        << max_user_connections << ","
298        << "'" << plugin << "',";
299     ss << "'',";
300     if (password_expired)
301       ss << "'Y',";
302     else
303       ss << "'N',";
304     ss << "now(), NULL, 'N');\n";
305 
306     flush_priv << "FLUSH PRIVILEGES;\n";
307 
308     if (password_expired)
309     {
310       set_passcmd << "ALTER USER '" << escape_string(user) << "'@'"
311                   << escape_string(host) << "' IDENTIFIED BY '"
312                   << escape_string(password) << "' PASSWORD EXPIRE;\n";
313     }
314     cmdstr->append(ss.str()).append(flush_priv.str()).append(set_passcmd.str());
315   }
316 
317 };
318 
319 static const char *load_default_groups[]= { PROGRAM_NAME, 0 };
320 
print_version(const string & p)321 void print_version(const string &p)
322 {
323   cout << p
324        << " Ver " << MYSQL_SERVER_VERSION << ", for "
325        << SYSTEM_TYPE << " on "
326        << MACHINE_TYPE << "\n";
327 }
328 
usage(const string & p)329 void usage(const string &p)
330 {
331   print_version(p);
332   cout << ORACLE_WELCOME_COPYRIGHT_NOTICE("2015") << endl
333        << "MySQL Database Deployment Utility." << endl
334        << "Usage: "
335        << p
336        << " [OPTIONS]" << endl;
337   my_print_help(my_connection_options);
338   cout << endl
339    << "The following options may be given as the first argument:"
340    << endl
341    << "--print-defaults        Print the program argument list and exit."
342    << endl
343    << "--no-defaults           Don't read default options from any option file,"
344    << endl
345    << "                        except for login file."
346    << endl
347    << "--defaults-file=#       Only read default options from the given file #."
348    << endl
349    << "--defaults-extra-file=# Read this file after the global files are read."
350    << endl;
351   my_print_variables(my_connection_options);
352 }
353 
354 
355 extern "C" my_bool
my_arguments_get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument)356 my_arguments_get_one_option(int optid,
357                             const struct my_option *opt MY_ATTRIBUTE((unused)),
358                             char *argument)
359 {
360   switch(optid)
361   {
362     case '?':
363       usage(PROGRAM_NAME);
364       exit(0);
365     case 'V':
366       print_version(PROGRAM_NAME);
367       exit(0);
368   }
369   return 0;
370 }
371 
372 
373 /**
374   The string class will break if constructed with a NULL pointer. This wrapper
375  provides a systematic protection when importing char pointers.
376 */
create_string(char * ptr)377 string create_string(char *ptr)
378 {
379   if (ptr)
380     return string(ptr);
381   else
382     return string("");
383 }
384 
385 template <class InputIterator, class UnaryPredicate >
my_all_of(InputIterator first,InputIterator last,UnaryPredicate pred)386 bool my_all_of(InputIterator first, InputIterator last, UnaryPredicate pred)
387 {
388   while (first != last)
389   {
390     if (!pred(*first)) return false;
391     ++first;
392   }
393   return true;
394 }
395 
my_legal_username_chars(const char & c)396 bool my_legal_username_chars(const char &c)
397 {
398   return isalnum(c) || c == '_';
399 }
400 
my_legal_hostname_chars(const char & c)401 bool my_legal_hostname_chars(const char &c)
402 {
403   return isalnum(c) || c == '_' || c == '.';
404 }
405 
my_legal_plugin_chars(const char & c)406 bool my_legal_plugin_chars(const char &c)
407 {
408   return isalnum(c) || c == '_';
409 }
410 
411 // defined in auth_utils.cc
412 extern const string g_allowed_pwd_chars;
413 
my_legal_password(const char & c)414 bool my_legal_password(const char &c)
415 {
416   return (get_allowed_pwd_chars().find(c) != string::npos);
417 }
418 
419 /**
420   Verify that the default admin account follows the recommendations and
421   restrictions.
422 */
assert_valid_root_account(const string & username,const string & host,const string & plugin,bool ssl)423 bool assert_valid_root_account(const string &username, const string &host,
424                                   const string &plugin, bool ssl)
425 {
426   if( username.length() > MAX_USER_NAME_LEN || username.length() < 1)
427   {
428     error << "Username must be between 1 and "
429           << MAX_USER_NAME_LEN
430           << " characters in length."
431           << endl;
432     return false;
433   }
434   if (!my_all_of(username.begin(), username.end(), my_legal_username_chars) ||
435       !my_all_of(host.begin(), host.end(), my_legal_hostname_chars))
436   {
437     error << "Recommended practice is to use only alpha-numericals in "
438              "the user / host name."
439           << endl;
440     return false;
441   }
442   if (!my_all_of(plugin.begin(), plugin.end(), my_legal_plugin_chars))
443   {
444     error << "Only use alpha-numericals in the the plugin name."
445           << endl;
446     return false;
447   }
448   if (plugin != "mysql_native_password" && plugin != "sha256_password")
449   {
450     error << "Unsupported authentication plugin specified."
451           << endl;
452     return false;
453   }
454   return true;
455 }
456 
assert_valid_datadir(const string & datadir,Path * target)457 bool assert_valid_datadir(const string &datadir, Path *target)
458 {
459   if (datadir.length() == 0)
460   {
461     error << "The data directory needs to be specified."
462           << endl;
463     return false;
464   }
465 
466   target->append(datadir);
467 
468   if (target->exists())
469   {
470     if (!target->empty())
471     {
472       error << "The data directory '"
473             << datadir.c_str()
474             << "' already exist and is not empty." << endl;
475       return false;
476     }
477   }
478   return true;
479 }
480 
481 class File_exists
482 {
483 public:
File_exists(const string * file,Path * qp)484   File_exists(const string *file, Path *qp) : m_file(file), m_qpath(qp)
485   {}
486 
operator ()(const Path & path)487   bool operator()(const Path &path)
488   {
489     Path tmp_path(path);
490     tmp_path.filename(*m_file);
491     if (tmp_path.exists())
492     {
493       m_qpath->path(path);
494       m_qpath->filename(*m_file);
495       return true;
496     }
497     return false;
498   }
499 
500 private:
501   const string *m_file;
502   Path *m_qpath;
503 };
504 
505 
506 /**
507  Given a list of search paths; find the file.
508  If search_paths=0 then the filename is considered to be a qualified path.
509  If filename is empty then the qpath will be the first directory which
510  is found.
511  @param filename The file to look for
512  @search_paths paths to search
513  @qpath[out] The qualified path to the first found file
514 
515  @return true if a file is found, false if not.
516 */
517 
locate_file(const string & filename,vector<Path> * search_paths,Path * qpath)518 bool locate_file(const string &filename, vector<Path > *search_paths,
519                   Path *qpath)
520 {
521   if (search_paths == 0)
522   {
523     MY_STAT s;
524     if (my_stat(filename.c_str(), &s, MYF(0)) == NULL)
525       return false;
526     qpath->qpath(filename);
527   }
528   else
529   {
530     vector<Path>::iterator qpath_it=
531       find_if(search_paths->begin(), search_paths->end(),
532               File_exists(&filename, qpath));
533     if (qpath_it == search_paths->end())
534       return false;
535   }
536   return true;
537 }
538 
add_standard_search_paths(vector<Path> * spaths)539 void add_standard_search_paths(vector<Path > *spaths)
540 {
541   Path p;
542   if (!p.path_getcwd())
543     warning << "Can't determine current working directory." << endl;
544 
545   spaths->push_back(p);
546   spaths->push_back(Path(p).append("/bin"));
547   spaths->push_back(Path("/usr/bin"));
548   spaths->push_back(Path("/usr/local/bin"));
549   spaths->push_back(Path("/opt/mysql/bin"));
550 #ifdef INSTALL_SBINDIR
551   spaths->push_back(Path(INSTALL_SBINDIR));
552 #endif
553 #ifdef INSTALL_BINDIR
554   spaths->push_back(Path(INSTALL_BINDIR));
555 #endif
556 
557 }
558 
559 /**
560  Attempts to locate the mysqld file.
561  If opt_mysqldfile is specified then the this assumed to be a correct qualified
562  path to the mysqld executable.
563  If opt_basedir is specified then opt_basedir+"/bin" is assumed to be a
564  candidate path for the mysqld executable.
565  If opt_srcdir is set then opt_srcdir+"/bin" is assumed to be a
566  candidate path for the mysqld executable.
567  If opt_builddir is set then opt_builddir+"/sql" is assumed to be a
568  candidate path for the mysqld executable.
569 
570  If the executable isn't found in any of these locations,
571  attempt to search the local directory and "bin" and "sbin" subdirectories.
572  Finally check "/usr/bin","/usr/sbin", "/usr/local/bin","/usr/local/sbin",
573  "/opt/mysql/bin","/opt/mysql/sbin"
574 
575 */
assert_mysqld_exists(const string & opt_mysqldfile,const string & opt_basedir,const string & opt_builddir,const string & opt_srcdir,Path * qpath)576 bool assert_mysqld_exists(const string &opt_mysqldfile,
577                             const string &opt_basedir,
578                             const string &opt_builddir,
579                             const string &opt_srcdir,
580                             Path *qpath)
581 {
582   vector<Path > spaths;
583   if (opt_mysqldfile.length() > 0)
584   {
585     /* Use explicit option to file mysqld */
586     if (!locate_file(opt_mysqldfile, 0, qpath))
587     {
588       error << "No such file: " << opt_mysqldfile << endl;
589       return false;
590     }
591   }
592   else
593   {
594     if (opt_basedir.length() > 0)
595     {
596       spaths.push_back(Path(opt_basedir).
597         append("bin"));
598       /* cater for RPM installs : mysqld in sbin */
599       spaths.push_back(Path(opt_basedir).
600         append("sbin"));
601     }
602     if (opt_builddir.length() > 0)
603     {
604       spaths.push_back(Path(opt_builddir).
605         append("sql"));
606     }
607 
608     add_standard_search_paths(&spaths);
609 
610     if (!locate_file(MYSQLD_EXECUTABLE, &spaths, qpath))
611     {
612       error << "Can't locate the server executable (mysqld)." << endl;
613       info << "The following paths were searched: ";
614       copy(spaths.begin(), spaths.end(),
615            infix_ostream_iterator<Path >(info, ", "));
616       info << endl;
617       return false;
618     }
619   }
620   return true;
621 }
622 
623 
assert_valid_language_directory(const string & opt_langpath,const string & opt_basedir,const string & opt_builddir,const string & opt_srcdir,Path * language_directory)624 bool assert_valid_language_directory(const string &opt_langpath,
625                                      const string &opt_basedir,
626                                      const string &opt_builddir,
627                                      const string &opt_srcdir,
628                                      Path *language_directory)
629 {
630   vector<Path > search_paths;
631   bool found_subdir= false;
632   if (opt_langpath.length() > 0)
633   {
634     search_paths.push_back(opt_langpath);
635   }
636   else
637   {
638     if(opt_basedir.length() > 0)
639     {
640       Path ld(opt_basedir);
641       ld.append("/share/english");
642       search_paths.push_back(ld);
643 
644       /* cater for RPMs */
645       Path ld2(opt_basedir);
646       ld2.append("/share/mysql/english");
647       search_paths.push_back(ld2);
648     }
649     if (opt_builddir.length() > 0)
650     {
651       Path ld(opt_builddir);
652       ld.append("/sql/share/english");
653       search_paths.push_back(ld);
654     }
655     if (opt_srcdir.length() > 0)
656     {
657       Path ld(opt_srcdir);
658       ld.append("/sql/share/english");
659       search_paths.push_back(ld);
660     }
661     search_paths.push_back(Path("/usr/share/mysql/english"));
662     search_paths.push_back(Path("/opt/mysql/share/english"));
663 #ifdef INSTALL_MYSQLSHAREDIR
664     search_paths.push_back(Path(INSTALL_MYSQLSHAREDIR).append("/english"));
665 #endif
666     found_subdir= true;
667   }
668 
669   if (!locate_file("", &search_paths, language_directory))
670   {
671     error << "Can't locate the language directory." << endl;
672     info << "Attempted the following paths: ";
673     copy(search_paths.begin(), search_paths.end(),
674          infix_ostream_iterator<Path>(info, ", "));
675     info << endl;
676     return false;
677   }
678   if (found_subdir)
679     language_directory->up();
680   return true;
681 }
682 
683 /**
684   Parse the login.cnf file and extract the missing admin credentials.
685   If any of adminuser or adminhost contains information, it won't be overwritten
686   by new data. Password is always updated.
687 
688   @return Error
689     @retval ALL_OK Reporting success
690     @retval ERR_FILE File not found
691     @retval ERR_ENCRYPTION Error while decrypting
692     @retval ERR_SYNTAX Error while parsing
693 */
get_admin_credentials(const string & opt_adminlogin,const string & login_path,string * adminuser,string * adminhost,string * password)694 int get_admin_credentials(const string &opt_adminlogin,
695                           const string &login_path,
696                           string *adminuser,
697                           string *adminhost,
698                           string *password)
699 {
700   Path path;
701   int ret= ERR_OTHER;
702   if (!path.qpath(opt_adminlogin) || !path.exists())
703     return ERR_FILE;
704 
705   ifstream fin(opt_adminlogin.c_str(), ifstream::binary);
706   stringstream sout;
707   if (decrypt_login_cnf_file(fin, sout) != ALL_OK)
708     return ERR_ENCRYPTION;
709 
710   map<string, string > options;
711 
712   if ((ret= parse_cnf_file(sout, &options, login_path)) != ALL_OK)
713     return ret;
714 
715   for( map<string, string >::iterator it= options.begin();
716        it != options.end(); ++it)
717   {
718     if (it->first == "user")
719       *adminuser= it->second;
720     if (it->first == "host")
721       *adminhost= it->second;
722     if (it->first == "password")
723       *password= it->second;
724   }
725   return ALL_OK;
726 }
727 
create_ssl_policy(string * ssl_type,string * ssl_cipher,string * x509_issuer,string * x509_subject)728 void create_ssl_policy(string *ssl_type, string *ssl_cipher,
729                          string *x509_issuer, string *x509_subject)
730 {
731   /* TODO set up a specific SSL restriction on the default account */
732   *ssl_type= "ANY";
733   *ssl_cipher= "";
734   *x509_issuer= "";
735   *x509_subject= "";
736 }
737 
738 #define  READ_BUFFER_SIZE 2048
739 #define TIMEOUT_IN_SEC 30
740 
741 class Process_reader
742 {
743 public:
Process_reader(string * buffer)744   Process_reader(string *buffer) : m_buffer(buffer)
745   {}
operator ()(int fh)746   bool operator()(int fh)
747   {
748     errno= 0;
749     char ch[READ_BUFFER_SIZE];
750     ssize_t n= 1;
751     int select_ret= 0;
752     fd_set rd, ex;
753     struct timeval tm;
754     tm.tv_sec = TIMEOUT_IN_SEC;
755     tm.tv_usec = 0;
756 
757     int flags = fcntl(fh, F_GETFL, 0);
758     fcntl(fh, F_SETFL, flags | O_NONBLOCK);
759     FD_ZERO(&rd);
760     FD_ZERO(&ex);
761     FD_SET(fh, &rd);
762     FD_SET(fh, &ex);
763     errno= 0;
764     /* Wait for something to read */
765     if ((select_ret= select(fh + 1, &rd, NULL, &ex, &tm)) == 0)
766     {
767         /* if 30 s passed we attempt to read anyway */
768         warning << "select() timed out." << endl;
769     }
770     /* Read any error reports from the child process */
771     while((n= read(fh, ch, READ_BUFFER_SIZE)) > 0 && ch[0] != 0 && errno == 0)
772     {
773       m_buffer->append(ch, n);
774     }
775 
776     return true;
777   }
778 
779 private:
780   string *m_buffer;
781 
782 };
783 
784 struct Delimiter_parser
785 {
Delimiter_parserDelimiter_parser786   Delimiter_parser() : m_delimiter(";") {}
~Delimiter_parserDelimiter_parser787   ~Delimiter_parser() {}
788 
789  bool operator()(std::string &line);
790 private:
791   string m_agg;
792   string m_delimiter;
793 };
794 
operator ()(std::string & line)795 bool Delimiter_parser::operator()(std::string &line)
796 {
797   if (line.empty() || line.size() == m_delimiter.size())
798     return false;
799   const string delimiter_tok("delimiter ");
800 
801   string::size_type pos= line.find(delimiter_tok);
802   string::size_type curr_del_pos= line.find(m_delimiter);
803 
804   if (pos != string::npos && curr_del_pos != string::npos)
805   {
806     /* replace old delimiter with new */
807     m_delimiter= line.substr(pos+delimiter_tok.size(), curr_del_pos - (pos+delimiter_tok.size()));
808     line.clear();
809     return false;
810   }
811 
812   if (curr_del_pos != string::npos)
813   {
814     line.erase(curr_del_pos, m_delimiter.length());
815     line.append(";\n");
816     line= m_agg.append(line);
817     m_agg.clear();
818   }
819   else
820   {
821     m_agg.append(line.append(" "));
822     line.clear();
823     return false;
824   }
825   return true; /* line has delimiter */
826 }
827 
828 struct Comment_extractor
829 {
Comment_extractorComment_extractor830   Comment_extractor() : m_in_comment(false) {}
~Comment_extractorComment_extractor831   ~Comment_extractor() {}
832 
833  bool operator()(string &line);
834 private:
835   bool m_in_comment;
836 };
837 
operator ()(string & line)838 bool Comment_extractor::operator ()(string& line)
839 {
840 
841   /* Are we in a multi comment? */
842   if (m_in_comment)
843   {
844     string::size_type i= line.find("*/");
845     if (i != string::npos)
846     {
847       m_in_comment= false;
848       string::iterator b= line.begin();
849       advance(b, i+2);
850       line.erase(line.begin(), b);
851       if (line.empty())
852         return true;
853     }
854     else
855     {
856       /* We're still in a multi comment clear the line */
857       line.clear();
858     }
859   }
860   else
861   {
862     string::size_type i= line.find("--");
863     if (i != string::npos)
864     {
865       string::iterator it= line.begin();
866       advance(it, i);
867       line.erase(it,line.end());
868       return true;
869     }
870     i= line.find("/*");
871     if (i != string::npos)
872     {
873       m_in_comment= true;
874       string::iterator a= line.begin();
875       advance(a, i);
876       string::iterator b;
877       string::size_type j= line.find("*/");
878       if (j != string::npos)
879       {
880         b= line.begin();
881         advance(b, j+2);
882         m_in_comment= false;
883       }
884       else
885         b= line.end();
886       line.erase(a, b);
887       if (line.empty())
888         return true;
889     }
890   }
891 
892   return m_in_comment;
893 }
894 
895 class Process_writer
896 {
897 public:
Process_writer(Sql_user * user,const string & opt_sqlfile)898   Process_writer(Sql_user *user, const string &opt_sqlfile) : m_user(user),
899     m_opt_sqlfile(opt_sqlfile) {}
operator ()(int fh)900   bool operator()(int fh)
901   {
902     errno= 0;
903     info << "Creating system tables...";
904 
905     string create_db("CREATE DATABASE mysql;\n");
906     string use_db("USE mysql;\n");
907     // ssize_t write() may be declared with attribute warn_unused_result
908     size_t w1= write(fh, create_db.c_str(), create_db.length());
909     size_t w2= write(fh, use_db.c_str(), use_db.length());
910     if (w1 != create_db.length() || w2 != use_db.length())
911     {
912       info << "failed." << endl;
913       return false;
914     }
915 
916     unsigned s= 0;
917     s= sizeof(mysql_system_tables)/sizeof(*mysql_system_tables);
918     for(unsigned i=0, n= 1; i< s && errno != EPIPE && n != 0 &&
919         mysql_system_tables[i] != NULL; ++i)
920     {
921       n= write(fh, mysql_system_tables[i],
922                strlen(mysql_system_tables[i]));
923     }
924     if (errno != 0)
925     {
926       info << "failed." << endl;
927       return false;
928     }
929     else
930       info << "done." << endl;
931 
932     info << "Filling system tables with data...";
933     s= sizeof(mysql_system_data)/sizeof(*mysql_system_data);
934     for(unsigned i=0, n= 1; i< s && errno != EPIPE && n != 0 &&
935         mysql_system_data[i] != NULL; ++i)
936     {
937       n= write(fh, mysql_system_data[i],
938                strlen(mysql_system_data[i]));
939     }
940     if (errno != 0)
941     {
942       info << "failed." << endl;
943       return false;
944     }
945     else
946       info << "done." << endl;
947 
948     info << "Filling help table with data...";
949     s= sizeof(fill_help_tables)/sizeof(*fill_help_tables);
950     for(unsigned i=0, n= 1; i< s && errno != EPIPE && n != 0 &&
951         fill_help_tables[i] != NULL; ++i)
952     {
953       n= write(fh, fill_help_tables[i],
954                strlen(fill_help_tables[i]));
955     }
956     if (errno != 0)
957     {
958       info << "failed." << endl;
959       return false;
960     }
961     else
962       info << "done." << endl;
963 
964     info << "Creating user for internal session service...";
965 
966     string create_session_serv_user(
967       "INSERT IGNORE INTO mysql.user VALUES ('localhost','mysql.session',"
968         "'N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','Y','N',"
969         "'N','N','N','N','N','N','N','N','N','N','N','N','','','','',0,0,0,0,"
970         "'mysql_native_password','*THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE',"
971         "'N',CURRENT_TIMESTAMP,NULL,'Y');\n");
972     string select_table_priv(
973       "INSERT IGNORE INTO mysql.tables_priv VALUES ('localhost', 'mysql',"
974         " 'mysql.session', 'user', 'root@localhost', CURRENT_TIMESTAMP,"
975         " 'Select', '');\n"
976       );
977     string select_db_priv(
978       "INSERT IGNORE INTO mysql.db VALUES ('localhost', 'performance_schema',"
979         " 'mysql.session','Y','N','N','N','N','N','N','N','N','N','N','N',"
980         "'N','N','N','N','N','N','N');\n"
981     );
982 
983     w1= write(fh, create_session_serv_user.c_str(),
984               create_session_serv_user.length());
985     w2= write(fh, select_table_priv.c_str(), select_table_priv.length());
986     size_t w3= write(fh, select_db_priv.c_str(), select_db_priv.length());
987 
988     if (w1 != create_session_serv_user.length() ||
989         w2 != select_table_priv.length() ||
990         w3 != select_db_priv.length())
991     {
992       info << "failed." << endl;
993       return false;
994     }
995     else
996       info << "done." << endl;
997 
998     info << "Creating default user " << m_user->user << "@"
999          << m_user->host
1000          << endl;
1001     string create_user_cmd;
1002     m_user->to_sql(&create_user_cmd);
1003     w1= write(fh, create_user_cmd.c_str(), create_user_cmd.length());
1004     if (w1 !=create_user_cmd.length() || errno != 0)
1005       return false;
1006     info << "Creating default proxy " << m_user->user << "@"
1007          << m_user->host
1008          << endl;
1009     Proxy_user proxy_user(m_user->host, m_user->user);
1010     string create_proxy_cmd;
1011     proxy_user.to_str(&create_proxy_cmd);
1012     w1= write(fh, create_proxy_cmd.c_str(), create_proxy_cmd.length());
1013     if (w1 != create_proxy_cmd.length() || errno != 0)
1014       return false;
1015 
1016     if (!opt_skipsys)
1017     {
1018       info << "Creating sys schema" << endl;
1019       s= sizeof(mysql_sys_schema)/sizeof(*mysql_sys_schema);
1020       for(unsigned i=0, n= 1; i< s && errno != EPIPE && n != 0 &&
1021           mysql_sys_schema[i] != NULL; ++i)
1022       {
1023          n= write(fh, mysql_sys_schema[i],
1024                   strlen(mysql_sys_schema[i]));
1025       }
1026       if (errno != 0)
1027       {
1028         info << "failed." << endl;
1029         return false;
1030       }
1031       else
1032         info << "done." << endl;
1033     }
1034 
1035     /* Execute optional SQL from a file */
1036     if (m_opt_sqlfile.length() > 0)
1037     {
1038       Path extra_sql;
1039       extra_sql.qpath(m_opt_sqlfile);
1040       if (!extra_sql.exists())
1041       {
1042         warning << "No such file '" << extra_sql.to_str() << "' "
1043                 << "(skipping)"
1044                 << endl;
1045       } else
1046       {
1047         info << "Executing extra SQL commands from " << extra_sql.to_str()
1048              << endl;
1049         ifstream fin(extra_sql.to_str().c_str());
1050         string sql_command;
1051         string default_se_command("SET default_storage_engine=INNODB;\n");
1052         int n= write(fh, default_se_command.c_str(), default_se_command.length());
1053         Comment_extractor strip_comments;
1054         Delimiter_parser check_delimiters;
1055         while (!getline(fin, sql_command).eof() && errno != EPIPE &&
1056              n != 0)
1057         {
1058           bool is_comment= strip_comments(sql_command);
1059           if (!is_comment)
1060           {
1061             bool has_delimiter= check_delimiters(sql_command);
1062             if (!has_delimiter)
1063               continue;
1064             n= write(fh, sql_command.c_str(), sql_command.length());
1065           }
1066         }
1067         fin.close();
1068       }
1069     }
1070     return true;
1071   }
1072 private:
1073   Sql_user *m_user;
1074   string m_opt_sqlfile;
1075 };
1076 
1077 struct Reader_thd_command_st
1078 {
1079   Process_reader *reader_functor;
1080   int read_hndl;
1081 };
1082 
reader_func_adaptor(void * f)1083 static void *reader_func_adaptor(void *f)
1084 {
1085   Reader_thd_command_st *cmd= static_cast<Reader_thd_command_st*>(f);
1086   (*cmd->reader_functor)(cmd->read_hndl);
1087   return NULL;
1088 }
1089 
1090 
1091 template <typename Reader_func_t, typename Writer_func_t,
1092           typename Fwd_iterator >
process_execute(const string & exec,Fwd_iterator begin,Fwd_iterator end,Reader_func_t reader,Writer_func_t writer)1093 bool process_execute(const string &exec, Fwd_iterator begin,
1094                        Fwd_iterator end, Reader_func_t reader,
1095                        Writer_func_t writer)
1096 {
1097   pid_t child;
1098   bool retval= true;
1099   int read_pipe[2];
1100   int write_pipe[2];
1101   posix_spawn_file_actions_t spawn_action;
1102   char *execve_args[MAX_MYSQLD_ARGUMENTS];
1103 
1104   /*
1105     Disable any signal handler for broken pipes and check for EPIPE during
1106     IO instead.
1107   */
1108   signal(SIGPIPE, SIG_IGN);
1109   if (pipe(read_pipe) < 0)
1110   {
1111     return false;
1112   }
1113   if (pipe(write_pipe) < 0)
1114   {
1115     ::close(read_pipe[0]);
1116     ::close(read_pipe[1]);
1117     return false;
1118   }
1119 
1120   posix_spawn_file_actions_init(&spawn_action);
1121   /* target process std input (0) reads from our write_pipe */
1122   posix_spawn_file_actions_adddup2(&spawn_action, write_pipe[0], 0);
1123   /* target process shouldn't attempt to write to this pipe */
1124   posix_spawn_file_actions_addclose(&spawn_action, write_pipe[1]);
1125 
1126   /* target process output (1) is mapped to our read_pipe */
1127   posix_spawn_file_actions_adddup2(&spawn_action, read_pipe[1], 2);
1128   /* target process shouldn't attempt to read from this pipe */
1129   posix_spawn_file_actions_addclose(&spawn_action, read_pipe[0]);
1130 
1131   /*
1132     We need to copy the strings or spawn will fail
1133   */
1134   char *local_filename= strdup(exec.c_str());
1135   execve_args[0]= local_filename;
1136   int i= 1;
1137   for(Fwd_iterator it= begin;
1138       it!= end && i < MAX_MYSQLD_ARGUMENTS-1;)
1139   {
1140     execve_args[i]= strdup(const_cast<char *>((*it).c_str()));
1141     ++it;
1142     ++i;
1143   }
1144   execve_args[i]= 0;
1145 
1146   int ret= posix_spawnp(&child, (const char *)execve_args[0], &spawn_action,
1147                         NULL, execve_args, NULL);
1148 
1149   /* This end is for the target process to read from */
1150   ::close(write_pipe[0]);
1151   /* This end is for the target process to write to */
1152   ::close(read_pipe[1]);
1153 
1154   if (ret != 0)
1155   {
1156     /* always failure if we get here! */
1157     error << "Child process: " << exec <<
1158              " exited with return value " << ret << endl;
1159     exit(1);
1160   }
1161   else
1162   {
1163     pthread_t thd_id;
1164     Reader_thd_command_st cmd;
1165     cmd.read_hndl= read_pipe[0];
1166     cmd.reader_functor= &reader;
1167     sigset_t set;
1168     sigemptyset(&set);
1169     sigaddset(&set, SIGPIPE);
1170     pthread_sigmask(SIG_BLOCK, &set, NULL);
1171     pthread_create(&thd_id, NULL, reader_func_adaptor, &cmd);
1172     if (!writer(write_pipe[1]) || errno != 0)
1173     {
1174       error << "Child process: " << exec <<
1175                "terminated prematurely with errno= "
1176             << errno
1177             << endl;
1178       retval= false;
1179     }
1180     // join with read thread
1181     void *ret= NULL;
1182     pthread_cancel(thd_id); // break select()
1183     pthread_join(thd_id, &ret);
1184   }
1185 
1186   while(i > 0)
1187   {
1188     free(execve_args[i]);
1189     --i;
1190   }
1191   ::close(write_pipe[1]);
1192   ::close(read_pipe[0]);
1193 
1194   /* Wait for the child to die */
1195   int signal= 0;
1196   waitpid(child, &signal, 0);
1197 
1198   posix_spawn_file_actions_destroy(&spawn_action);
1199   free(local_filename);
1200   return retval;
1201 }
1202 
generate_password_file(Path & pwdfile,const string & adminuser,const string & adminhost,const string & password)1203 int generate_password_file(Path &pwdfile, const string &adminuser,
1204                            const string &adminhost,
1205                            const string &password)
1206 {
1207 
1208   /*
1209     The format of the password file is
1210     ['#'][bytes]['\n']['password bytes']['\n']|[EOF])
1211   */
1212   ofstream fout;
1213   mode_t old_mask= umask(~(S_IRWXU));
1214   fout.open(pwdfile.to_str().c_str());
1215   if (!fout.is_open())
1216   {
1217     umask(old_mask);
1218     return ERR_FILE;
1219   }
1220 
1221   fout << "# Password set for user '"
1222        << adminuser << "@" << adminhost << "' at "
1223        << Datetime() << "\n"
1224        << password << "\n";
1225   fout.close();
1226   info << "done." << endl;
1227   umask(old_mask);
1228   return ALL_OK;
1229 }
1230 
connection_options_sorter(const void * a,const void * b)1231 int connection_options_sorter(const void *a, const void *b)
1232 {
1233   return strcmp(static_cast<const my_option*>(a)->name,
1234                 static_cast<const my_option *>(b)->name);
1235 }
1236 
is_prefix(const char * s,const char * t)1237 static int is_prefix(const char *s, const char *t)
1238 {
1239   while (*t)
1240     if (*s++ != *t++) return 0;
1241   return 1;
1242 }
1243 
real_get_defaults_options(int argc,char ** argv,my_bool * no_defaults,char ** defaults,char ** extra_defaults,char ** group_suffix,char ** login_path)1244 static int real_get_defaults_options(int argc, char **argv,
1245                                      my_bool *no_defaults,
1246                                      char **defaults,
1247                                      char **extra_defaults,
1248                                      char **group_suffix,
1249                                      char **login_path)
1250 {
1251   char **argv_it= argv;
1252   int org_argc= argc, prev_argc= 0, default_option_count= 0;
1253 
1254   while (argc >= 2 && argc != prev_argc)
1255   {
1256     /* Skip program name or previously handled argument */
1257     argv_it++;
1258     prev_argc= argc;                            /* To check if we found */
1259     /* --no-defaults is always the first option. */
1260     if (is_prefix(*argv_it,"--no-defaults") && ! default_option_count)
1261     {
1262        argc--;
1263        default_option_count ++;
1264        *no_defaults= TRUE;
1265        continue;
1266     }
1267     if (!*defaults && is_prefix(*argv_it, "--defaults-file="))
1268     {
1269       *defaults= *argv_it + sizeof("--defaults-file=")-1;
1270        argc--;
1271        default_option_count ++;
1272        continue;
1273     }
1274     if (!*extra_defaults && is_prefix(*argv_it, "--defaults-extra-file="))
1275     {
1276       *extra_defaults= *argv_it + sizeof("--defaults-extra-file=")-1;
1277       argc--;
1278       default_option_count ++;
1279       continue;
1280     }
1281     if (!*group_suffix && is_prefix(*argv_it, "--defaults-group-suffix="))
1282     {
1283       *group_suffix= *argv_it + sizeof("--defaults-group-suffix=")-1;
1284       argc--;
1285       default_option_count ++;
1286       continue;
1287     }
1288     if (!*login_path && is_prefix(*argv_it, "--login-path="))
1289     {
1290       *login_path= *argv_it + sizeof("--login-path=")-1;
1291       argc--;
1292       default_option_count ++;
1293       continue;
1294     }
1295   }
1296   return org_argc - argc;
1297 }
1298 
1299 
1300 class Resource_releaser
1301 {
1302   char **m_argv;
1303 
1304 public:
Resource_releaser(char ** argv)1305   explicit Resource_releaser(char **argv)
1306     : m_argv(argv) { }
1307 
~Resource_releaser()1308   ~Resource_releaser()
1309   {
1310     free_defaults(m_argv);
1311     my_cleanup_options(my_connection_options);
1312   }
1313 };
1314 
1315 
main(int argc,char * argv[])1316 int main(int argc,char *argv[])
1317 {
1318   /*
1319     In order to use the mysys library and the program option library
1320     we need to call the MY_INIT() macro.
1321   */
1322   MY_INIT(argv[0]);
1323 
1324   char *dummy= 0; // ignore group suffix when transferring to mysqld
1325   /* Remember the defaults argument so we later can pass these to mysqld */
1326   int default_opt_used= real_get_defaults_options(argc, argv,
1327                                            &opt_no_defaults,
1328                                            &opt_defaults_file,
1329                                            &opt_def_extra_file,
1330                                            &dummy,
1331                                            &opt_loginpath);
1332 #ifdef __WIN__
1333   /* Convert command line parameters from UTF16LE to UTF8MB4. */
1334   my_win_translate_command_line_args(&my_charset_utf8mb4_bin, &argc, &argv);
1335 #endif
1336 
1337   my_getopt_use_args_separator= TRUE;
1338   if (load_defaults("my", load_default_groups, &argc, &argv))
1339     return 1;
1340 
1341   // Remember to call free_defaults() and my_cleanup_options()
1342   Resource_releaser resource_releaser(argv);
1343 
1344   // Assert that the help messages are in sorted order
1345   // except that --help must be the first element and 0 must indicate the end.
1346   my_qsort(my_connection_options + 1,
1347            sizeof(my_connection_options)/sizeof(my_option) - 2,
1348            sizeof(my_option),
1349            connection_options_sorter);
1350 
1351   int rc= 0;
1352   if ((rc= handle_options(&argc, &argv, my_connection_options,
1353                              my_arguments_get_one_option)))
1354   {
1355     error << "Unrecognized options" << endl;
1356     return 1;
1357   }
1358 
1359   warning << "mysql_install_db is deprecated. ";
1360   warning << "Please consider switching to mysqld --initialize" << endl;
1361 
1362   bool expire_password= !opt_insecure;
1363   string adminuser(create_string(opt_adminuser));
1364   string adminhost(create_string(opt_adminhost));
1365   string authplugin(create_string(opt_authplugin));
1366   string password;
1367   string basedir(create_string(opt_basedir));
1368   string srcdir(create_string(opt_srcdir));
1369   string builddir(create_string(opt_builddir));
1370 
1371   if (opt_verbose != 1)
1372   {
1373     info.enabled(false);
1374   }
1375 
1376   if (default_opt_used > 0)
1377   {
1378     if (opt_defaults_file != 0)
1379     {
1380       info << "Using default values from " << opt_defaults_file << endl;
1381     }
1382     else
1383     if (!opt_no_defaults)
1384     {
1385       info << "Using default values from my.cnf" << endl;
1386     }
1387     if (opt_def_extra_file != 0)
1388     {
1389       info << "Using additional default values from " << endl;
1390     }
1391   }
1392 
1393   /*
1394    1. Verify all option parameters
1395    2. Create missing directories
1396    3. Compose mysqld start string
1397    4. Execute mysqld
1398    5. Exit
1399   */
1400 
1401   if (opt_adminlogin)
1402   {
1403     info << "Reading the login config file "
1404          << opt_adminlogin
1405          << " for default account credentials using login-path = "
1406          << opt_loginpath
1407          << endl;
1408     int ret= get_admin_credentials(create_string(opt_adminlogin),
1409                                    create_string(opt_loginpath),
1410                                    &adminuser,
1411                                    &adminhost,
1412                                    &password);
1413     switch(ret)
1414     {
1415       case ALL_OK: expire_password= false;
1416         if (password.length() == 0 && !opt_insecure)
1417         {
1418           error << "Password is specified as empty! You need to use the "
1419                    "--insecure option" << endl;
1420           return 1;
1421         }
1422       break;
1423       case ERR_FILE:
1424         error << "Can't read the login config file: "
1425               << opt_adminlogin << endl;
1426         return 1;
1427       case ERR_ENCRYPTION:
1428         error << "Failed to decrypt the login config file: "
1429               << opt_adminlogin << endl;
1430         return 1;
1431       case ERR_NO_SUCH_CATEGORY:
1432         error << "Failed to locate login-path '"
1433               << opt_loginpath << "' "
1434               << "in the login config file '"
1435               << opt_adminlogin << "' " << endl;
1436         return 1;
1437       case ERR_SYNTAX:
1438       default:
1439         error << "Failed to parse the login config file: "
1440               << opt_adminlogin << endl;
1441         return 1;
1442 
1443     }
1444   }
1445 
1446   if (!assert_valid_root_account(adminuser,
1447                                  adminhost,
1448                                  create_string(opt_authplugin),
1449                                  opt_ssl))
1450   {
1451     /* Subroutine reported error */
1452     return 1;
1453   }
1454 
1455 
1456   Path data_directory;
1457   if (!assert_valid_datadir(create_string(opt_datadir), &data_directory))
1458   {
1459     /* Subroutine reported error */
1460     return 1;
1461   }
1462 
1463   Path language_directory;
1464   if (!assert_valid_language_directory(create_string(opt_langpath),
1465                                        basedir,
1466                                        builddir,
1467                                        srcdir,
1468                                        &language_directory))
1469   {
1470     /* Subroutine reported error */
1471     return 1;
1472   }
1473 
1474   Path mysqld_exec;
1475   if( !assert_mysqld_exists(create_string(opt_mysqldfile),
1476                             basedir,
1477                             builddir,
1478                             srcdir,
1479                             &mysqld_exec))
1480   {
1481     /* Subroutine reported error */
1482     return 1;
1483   }
1484 
1485   if (opt_def_extra_file)
1486   {
1487     Path def_extra_file;
1488     def_extra_file.qpath(opt_def_extra_file);
1489     if (!def_extra_file.exists())
1490     {
1491       warning << "Can't open extra defaults file '"
1492               << opt_def_extra_file
1493               << "' (skipping)" << endl;
1494       opt_def_extra_file= NULL;
1495     }
1496   }
1497 
1498   if (opt_defaults_file)
1499   {
1500     opt_no_defaults= FALSE;
1501     Path defaults_file;
1502     defaults_file.qpath(opt_defaults_file);
1503     if (!defaults_file.exists())
1504     {
1505       error << "Can't open defaults file '"
1506             << defaults_file
1507             << "'" << endl;
1508       opt_defaults_file= NULL;
1509       return 1;
1510     }
1511   }
1512 
1513   if (data_directory.exists())
1514   {
1515     info << "Using existing directory "
1516          << data_directory
1517          << endl;
1518   }
1519   else
1520   {
1521     info << "Creating data directory "
1522          << data_directory << endl;
1523     mode_t old_mask= umask(0);
1524     if (my_mkdir(data_directory.to_str().c_str(),
1525                  S_IRWXU | S_IRGRP | S_IXGRP, MYF(MY_WME)))
1526     {
1527       error << "Failed to create the data directory '"
1528             << data_directory << "'" << endl;
1529       umask(old_mask);
1530       return 1;
1531     }
1532     umask(old_mask);
1533   }
1534 
1535   /* Generate a random password is no password was found previously */
1536   if (password.length() == 0 && !opt_insecure)
1537   {
1538     Path randpwdfile;
1539     if (opt_randpwdfile != 0)
1540     {
1541       randpwdfile.qpath(opt_randpwdfile);
1542     }
1543     else
1544     {
1545       randpwdfile.get_homedir();
1546       randpwdfile.filename(default_randpwfile);
1547     }
1548     info << "Generating random password to "
1549          << randpwdfile << "...";
1550     generate_password(&password,12);
1551     if (generate_password_file(randpwdfile, adminuser, adminhost,
1552                                password) != ALL_OK)
1553     {
1554       error << "Can't create password file "
1555             << randpwdfile
1556             << endl;
1557       return 1;
1558     }
1559   }
1560 
1561   if (opt_euid && geteuid() == 0)
1562   {
1563     struct passwd *pwd;
1564     info << "Setting file ownership to " << opt_euid
1565          << endl;
1566     pwd= getpwnam(opt_euid);   /* Try getting UID for username */
1567     if (pwd == NULL)
1568     {
1569       error << "Failed to verify user id '" << opt_euid
1570             << "'. Does it exist?" << endl;
1571       return 1;
1572     }
1573     if (chown(data_directory.to_str().c_str(), pwd->pw_uid, pwd->pw_gid) != 0)
1574     {
1575       error << "Failed to set file ownership for "
1576             << data_directory.to_str()
1577             << " to (" << pwd->pw_uid << ", " << pwd->pw_gid << ")"
1578             << endl;
1579     }
1580     if (setegid(pwd->pw_gid) != 0)
1581     {
1582       warning << "Failed to set effective group id to " << pwd->pw_gid
1583               << endl;
1584     }
1585     if (seteuid(pwd->pw_uid) != 0)
1586     {
1587       warning << "Failed to set effective user id to " << pwd->pw_uid
1588               << endl;
1589     }
1590   }
1591   else
1592     opt_euid= 0;
1593   vector<string> command_line;
1594   if (opt_no_defaults == TRUE && opt_defaults_file == NULL &&
1595       opt_def_extra_file == NULL)
1596     command_line.push_back(string("--no-defaults"));
1597   if (opt_defaults_file != NULL)
1598     command_line.push_back(string("--defaults-file=")
1599       .append(opt_defaults_file));
1600   if (opt_def_extra_file != NULL)
1601     command_line.push_back(string("--defaults-extra-file=")
1602       .append(opt_def_extra_file));
1603   command_line.push_back(string("--bootstrap"));
1604   command_line.push_back(string("--datadir=")
1605     .append(data_directory.to_str()));
1606   command_line.push_back(string("--lc-messages-dir=")
1607     .append(language_directory.to_str()));
1608   command_line.push_back(string("--lc-messages=")
1609     .append(create_string(opt_lang)));
1610   if (basedir.length() > 0)
1611   command_line.push_back(string("--basedir=")
1612     .append(basedir));
1613 
1614   string ssl_type;
1615   string ssl_cipher;
1616   string x509_issuer;
1617   string x509_subject;
1618   if (opt_ssl == true)
1619     create_ssl_policy(&ssl_type, &ssl_cipher, &x509_issuer, &x509_subject);
1620   info << "Executing " << mysqld_exec.to_str() << " ";
1621   copy(command_line.begin(), command_line.end(),
1622        infix_ostream_iterator<Path>(info, " "));
1623   info << endl;
1624   Sql_user user(adminhost,
1625                 adminuser,
1626                 password,
1627                 Access_privilege(Access_privilege::acl_all()),
1628                 ssl_type, // ssl_type
1629                 ssl_cipher, // ssl_cipher
1630                 x509_issuer, // x509_issuer
1631                 x509_subject, // x509_subject
1632                 0, // max_questions
1633                 0, // max updates
1634                 0, // max connections
1635                 0, // max user connections
1636                 create_string(opt_authplugin),
1637                 string(""),
1638                 expire_password,
1639                 0);
1640   string output;
1641   bool success= process_execute(mysqld_exec.to_str(),
1642                   command_line.begin(),
1643                   command_line.end(),
1644                   Process_reader(&output),
1645                   Process_writer(&user,create_string(opt_sqlfile)));
1646   if (!success)
1647   {
1648     error << "Failed to execute " << mysqld_exec.to_str() << " ";
1649     copy(command_line.begin(), command_line.end(),
1650          infix_ostream_iterator<Path>(error, " "));
1651     error << endl;
1652     cerr << "-- server log begin --" << endl;
1653     cerr << output << endl;
1654     cerr << "-- server log end --" << endl;
1655     return 1;
1656   }
1657   else if (output.find("ERROR") != string::npos)
1658   {
1659     error << "The bootstrap log isn't empty:"
1660           << endl
1661           << output
1662           << endl;
1663   }
1664   else if (output.size() > 0 &&
1665            output.find_first_not_of(" \t\n\r") != string::npos)
1666   {
1667     warning << "The bootstrap log isn't empty:"
1668             << endl
1669             << output
1670             << endl;
1671   }
1672   else
1673   {
1674     info << "Success!"
1675          << endl;
1676   }
1677 
1678   return 0;
1679 }
1680