diff options
author | Vladislav Vaintroub <wlad@montyprogram.com> | 2011-03-09 20:21:03 +0100 |
---|---|---|
committer | Vladislav Vaintroub <wlad@montyprogram.com> | 2011-03-09 20:21:03 +0100 |
commit | d4a3a7b90ef06cb3eaf68317c42020e2980d3777 (patch) | |
tree | 94351c78c87bd6588e648dee70a7da84eb2b5d9c /sql/mysql_install_db.cc | |
parent | 7ac23980b8009c7d446b008208f1bd6ba6ae9839 (diff) | |
download | mariadb-git-d4a3a7b90ef06cb3eaf68317c42020e2980d3777.tar.gz |
mwl#59 - windows installer.
Address Monty's review comments
Diffstat (limited to 'sql/mysql_install_db.cc')
-rw-r--r-- | sql/mysql_install_db.cc | 264 |
1 files changed, 148 insertions, 116 deletions
diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc index abc8ccb0290..adaf9401567 100644 --- a/sql/mysql_install_db.cc +++ b/sql/mysql_install_db.cc @@ -1,12 +1,28 @@ +/* Copyright (C) 2010-2011 Monty Program Ab & Vladislav Vaintroub + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + mysql_install_db creates a new database instance (optionally as service) + on Windows. +*/ #define DONT_DEFINE_VOID #include <my_global.h> #include <my_getopt.h> #include <my_sys.h> #include <m_string.h> -#include <stdlib.h> -#include <stdio.h> #include <windows.h> #include <assert.h> #include <shellapi.h> @@ -14,7 +30,8 @@ #include <aclapi.h> #define USAGETEXT \ -"mysql_install_db.exe Ver 1.42 for Windows\n" \ +"mysql_install_db.exe Ver 1.00 for Windows\n" \ +"Copyright (C) 2010-2011 Monty Program Ab & Vladislav Vaintroub\n" \ "This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n" \ "and you are welcome to modify and redistribute it under the GPL v2 license\n" \ "Usage: mysql_install_db.exe [OPTIONS]\n" \ @@ -22,9 +39,9 @@ extern "C" const char mysql_bootstrap_sql[]; -char default_os_user[] = "NT AUTHORITY\\NetworkService"; +char default_os_user[]= "NT AUTHORITY\\NetworkService"; static int create_db_instance(); -static uint opt_verbose, opt_silent; +static uint opt_silent; static char datadir_buffer[FN_REFLEN]; static char mysqld_path[FN_REFLEN]; static char *opt_datadir; @@ -37,6 +54,7 @@ static char *opt_os_password; static my_bool opt_default_user; static my_bool opt_allow_remote_root_access; static my_bool opt_skip_networking; +static my_bool verbose_errors; static struct my_option my_long_options[]= @@ -45,13 +63,13 @@ static struct my_option my_long_options[]= NO_ARG, 0, 0, 0, 0, 0, 0}, {"datadir", 'd', "Data directory of the new database", &opt_datadir, &opt_datadir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"service", 's', "Name of the Windows service", + {"service", 'S', "Name of the Windows service", &opt_service, &opt_service, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"password", 'p', "Root password", &opt_password, &opt_password, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"port", 'P', "mysql port", &opt_port, &opt_port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"socket", 'S', + {"socket", 'W', "named pipe name (if missing, it will be set the same as service)", &opt_socket, &opt_socket, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"default-user", 'D', "Create default user", @@ -63,6 +81,8 @@ static struct my_option my_long_options[]= {"skip-networking", 'N', "Do not use TCP connections, use pipe instead", &opt_skip_networking, &opt_skip_networking, 0 , GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"silent", 's', "Print less information", &opt_silent, + &opt_silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; @@ -91,13 +111,18 @@ static void die(const char *fmt, ...) /* Print the error message */ va_start(args, fmt); - if (fmt) - { - fprintf(stderr, "FATAL ERROR: "); - vfprintf(stderr, fmt, args); - fprintf(stderr, "\n"); - fflush(stderr); + fprintf(stderr, "FATAL ERROR: "); + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + if (verbose_errors)
+ {
+ fprintf(stderr,
+ "http://kb.askmonty.org/v/installation-issues-on-windows contains some help\n"
+ "for solving the most common problems. If this doesn't help you, please\n"
+ "leave a comment in the knowledge base or file a bug report at\n"
+ "https://bugs.launchpad.net/maria");
} + fflush(stderr); va_end(args); my_end(0); exit(1); @@ -113,12 +138,9 @@ static void verbose(const char *fmt, ...) /* Print the verbose message */ va_start(args, fmt); - if (fmt) - { - vfprintf(stdout, fmt, args); - fprintf(stdout, "\n"); - fflush(stdout); - } + vfprintf(stdout, fmt, args); + fputc('\n', stdout); + fflush(stdout); va_end(args); } @@ -126,41 +148,45 @@ static void verbose(const char *fmt, ...) int main(int argc, char **argv) { int error; - MY_INIT(argv[0]); char self_name[FN_REFLEN]; char *p; + MY_INIT(argv[0]); GetModuleFileName(NULL, self_name, FN_REFLEN); strcpy(mysqld_path,self_name); - p = strrchr(mysqld_path, FN_LIBCHAR); - if(p) + p= strrchr(mysqld_path, FN_LIBCHAR); + if (p) { strcpy(p, "\\mysqld.exe"); } if ((error= handle_options(&argc, &argv, my_long_options, get_one_option))) exit(error); - if(!opt_datadir) + if (!opt_datadir) { my_print_help(my_long_options); - die("parameter datadir is mandatory"); + die("parameter --datadir=# is mandatory"); } - if(!opt_os_user) + + /* Print some help on errors */ + verbose_errors= TRUE; + + if (!opt_os_user) { opt_os_user= default_os_user; - opt_os_password = NULL; + opt_os_password= NULL; } /* Workaround WiX bug (strip possible quote character at the end of path) */ size_t len= strlen(opt_datadir); - if(len > 0) + if (len > 0) { - if(opt_datadir[len-1] == '"') + if (opt_datadir[len-1] == '"') { opt_datadir[len-1]= 0; } } GetFullPathName(opt_datadir, FN_REFLEN, datadir_buffer, NULL); - opt_datadir = datadir_buffer; + opt_datadir= datadir_buffer; if (create_db_instance()) { @@ -174,41 +200,44 @@ int main(int argc, char **argv) /** - Convert slashes in paths into MySQL-compatible form + Convert slashes in paths into MySQL-compatible form */ + static void convert_slashes(char *s) { - for(size_t i=0; i< strlen(s); i++) - if(s[i] == '\\') - s[i] = '/'; + for (; *s ; s++)
+ if (*s == '\\')
+ *s= '/'; } - /** - Calculate basedir from mysqld.exe path + Calculate basedir from mysqld.exe path. + Basedir assumed to be is one level up from the mysqld.exe directory location.
+ E.g basedir for C:\my\bin\mysqld.exe would be C:\my */ + static void get_basedir(char *basedir, int size, const char *mysqld_path) { strcpy_s(basedir, size, mysqld_path); convert_slashes(basedir); - char *p = strrchr(basedir,'/'); - if(p) + char *p= strrchr(basedir,'/'); + if (p) { *p = 0; - p=strrchr(basedir, '/'); - if(p) - *p=0; + p= strrchr(basedir, '/'); + if (p) + *p= 0; } } - /** Allocate and initialize command line for mysqld --bootstrap. The resulting string is passed to popen, so it has a lot of quoting quoting around the full string plus quoting around parameters with spaces. */ + static char *init_bootstrap_command_line(char *cmdline, size_t size) { char basedir[MAX_PATH]; @@ -226,8 +255,9 @@ static char *init_bootstrap_command_line(char *cmdline, size_t size) /** Create my.ini in current directory (this is assumed to be - data directory as well. + data directory as well). */ + static int create_myini() { my_bool enable_named_pipe= FALSE; @@ -237,33 +267,31 @@ static int create_myini() GetCurrentDirectory(MAX_PATH, path_buf); /* Create ini file. */ - FILE *myini = fopen("my.ini","wt"); - if(!myini) + FILE *myini= fopen("my.ini","wt"); + if (!myini) { die("Cannot create my.ini in data directory"); } - /* - Write out server settings. datadir and basedir are calculated, - using path to mysqld.exe. - */ + + /* Write out server settings. */ fprintf(myini, "[mysqld]\n"); convert_slashes(path_buf); fprintf(myini, "datadir=%s\n", path_buf); if (opt_skip_networking) { fprintf(myini,"skip-networking\n"); - if(!opt_socket) + if (!opt_socket) opt_socket= opt_service; } enable_named_pipe= (my_bool) ((opt_socket && opt_socket[0]) || opt_skip_networking); - if(enable_named_pipe) + if (enable_named_pipe) { fprintf(myini,"enable-named-pipe\n"); } - if(opt_socket && opt_socket[0]) + if (opt_socket && opt_socket[0]) { fprintf(myini, "socket=%s\n", opt_socket); } @@ -274,11 +302,13 @@ static int create_myini() /* Write out client settings. */ fprintf(myini, "[client]\n"); - if(opt_socket && opt_socket[0]) + + /* Used for named pipes */ + if (opt_socket && opt_socket[0]) fprintf(myini,"socket=%s\n",opt_socket); - if(opt_skip_networking) + if (opt_skip_networking) fprintf(myini,"protocol=pipe\n"); - if(opt_port) + else if (opt_port) fprintf(myini,"port=%d\n",opt_port); fclose(myini); return 0; @@ -292,7 +322,7 @@ static const char update_root_passwd_part2[]= static const char remove_default_user_cmd[]= "DELETE FROM mysql.user where User='';\n"; static const char allow_remote_root_access_cmd[]= - "CREATE TEMPORARY TABLE tmp_user LIKE user;\n" + "CREATE TEMPORARY TABLE tmp_user LIKE user engine=memory;\n" "INSERT INTO tmp_user SELECT * from user where user='root' " " AND host='localhost';\n" "UPDATE tmp_user SET host='%';\n" @@ -301,45 +331,42 @@ static const char allow_remote_root_access_cmd[]= static const char end_of_script[]="-- end."; /* Register service. Assume my.ini is in datadir */ + static int register_service() { char buf[3*MAX_PATH +32]; /* path to mysqld.exe, to my.ini, service name */ SC_HANDLE sc_manager, sc_service; size_t datadir_len= strlen(opt_datadir); - const char *backslash_after_datadir="\\"; + const char *backslash_after_datadir= "\\"; - if (datadir_len && opt_datadir[datadir_len-1] =='\\') - backslash_after_datadir=""; + if (datadir_len && opt_datadir[datadir_len-1] == '\\') + backslash_after_datadir= ""; - verbose("Registering service"); + verbose("Registering service '%s'", opt_service); my_snprintf(buf, sizeof(buf)-1, "\"%s\" \"--defaults-file=%s%smy.ini\" \"%s\"" , mysqld_path, opt_datadir, backslash_after_datadir, opt_service); /* Get a handle to the SCM database. */ - sc_manager= OpenSCManager( - NULL, - NULL, - SC_MANAGER_ALL_ACCESS); - + sc_manager= OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS); if (!sc_manager) { - die("OpenSCManager failed (%d)\n", GetLastError()); + die("OpenSCManager failed (%u)\n", GetLastError()); } /* Create the service. */ - sc_service = CreateServiceA(sc_manager, opt_service, opt_service, + sc_service= CreateService(sc_manager, opt_service, opt_service, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, buf, NULL, NULL, NULL, opt_os_user, opt_os_password); if (!sc_service) { CloseServiceHandle(sc_manager); - die("CreateService failed (%d)", GetLastError()); + die("CreateService failed (%u)", GetLastError()); } - SERVICE_DESCRIPTION sd = { "MariaDB database server" }; + SERVICE_DESCRIPTION sd= { "MariaDB database server" }; ChangeServiceConfig2(sc_service, SERVICE_CONFIG_DESCRIPTION, &sd); CloseServiceHandle(sc_service); CloseServiceHandle(sc_manager); @@ -350,11 +377,8 @@ static int register_service() static void clean_directory(const char *dir) { char dir2[MAX_PATH+2]; - size_t len = strlen(dir); + *(strmake(dir2, dir, MAX_PATH+1)+1)= 0;
- strcpy_s(dir2, MAX_PATH+2, dir); - dir2[len+1] = 0; - SHFILEOPSTRUCT fileop; fileop.hwnd= NULL; /* no status display */ fileop.wFunc= FO_DELETE; /* delete operation */ @@ -370,10 +394,12 @@ static void clean_directory(const char *dir) SHFileOperation(&fileop); } + /* Define directory permission to have inheritable all access for a user (defined as username or group string or as SID) */ + static int set_directory_permissions(const char *dir, const char *os_user) { @@ -382,103 +408,105 @@ static int set_directory_permissions(const char *dir, const char *os_user) BYTE buffer[SECURITY_MAX_SID_SIZE]; } tokenInfoBuffer; - HANDLE hDir = CreateFile(dir,READ_CONTROL|WRITE_DAC,0,NULL,OPEN_EXISTING, + HANDLE hDir= CreateFile(dir,READ_CONTROL|WRITE_DAC,0,NULL,OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,NULL); - if(hDir == INVALID_HANDLE_VALUE) + if (hDir == INVALID_HANDLE_VALUE) return -1; ACL* pOldDACL; - SECURITY_DESCRIPTOR* pSD = NULL; + SECURITY_DESCRIPTOR* pSD= NULL; EXPLICIT_ACCESS ea={0}; BOOL isWellKnownSID= FALSE; WELL_KNOWN_SID_TYPE wellKnownSidType = WinNullSid; - PSID pSid = NULL; + PSID pSid= NULL; GetSecurityInfo(hDir, SE_FILE_OBJECT , DACL_SECURITY_INFORMATION,NULL, NULL, &pOldDACL, NULL, (void**)&pSD); - if(os_user) + if (os_user) { /* Check for 3 predefined service users They might have localized names in non-English Windows, thus they need to be handled using well-known SIDs. */ - if(stricmp(os_user, "NT AUTHORITY\\NetworkService") == 0) + if (stricmp(os_user, "NT AUTHORITY\\NetworkService") == 0) { wellKnownSidType= WinNetworkServiceSid; } - else if(stricmp(os_user, "NT AUTHORITY\\LocalService") == 0) + else if (stricmp(os_user, "NT AUTHORITY\\LocalService") == 0) { wellKnownSidType= WinLocalServiceSid; } - else if(stricmp(os_user, "NT AUTHORITY\\LocalSystem") == 0) + else if (stricmp(os_user, "NT AUTHORITY\\LocalSystem") == 0) { wellKnownSidType= WinLocalSystemSid; } - if(wellKnownSidType != WinNullSid) + if (wellKnownSidType != WinNullSid) { - DWORD size = SECURITY_MAX_SID_SIZE; - pSid= (PSID)tokenInfoBuffer.buffer; + DWORD size= SECURITY_MAX_SID_SIZE; + pSid= (PSID)tokenInfoBuffer.buffer; if (!CreateWellKnownSid(wellKnownSidType, NULL, pSid, &size)) { return 1; } - ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea.Trustee.ptstrName = (LPTSTR)pSid; + ea.Trustee.TrusteeForm= TRUSTEE_IS_SID; + ea.Trustee.ptstrName= (LPTSTR)pSid; } else { - ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME; - ea.Trustee.ptstrName = (LPSTR)os_user; + ea.Trustee.TrusteeForm= TRUSTEE_IS_NAME; + ea.Trustee.ptstrName= (LPSTR)os_user; } } else { HANDLE token; - if(OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY, &token)) + if (OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY, &token)) { - DWORD length=(DWORD) sizeof(tokenInfoBuffer); + DWORD length= (DWORD) sizeof(tokenInfoBuffer); if (GetTokenInformation(token, TokenUser, &tokenInfoBuffer, length, &length)) { pSid= tokenInfoBuffer.tokenUser.User.Sid; } } - if(!pSid) + if (!pSid) return 0; - ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea.Trustee.ptstrName = (LPTSTR)pSid; + ea.Trustee.TrusteeForm= TRUSTEE_IS_SID; + ea.Trustee.ptstrName= (LPTSTR)pSid; } - ea.grfAccessMode = GRANT_ACCESS; - ea.grfAccessPermissions = GENERIC_ALL; - ea.grfInheritance = CONTAINER_INHERIT_ACE|OBJECT_INHERIT_ACE; - ea.Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN; - ACL* pNewDACL = 0; - DWORD err = SetEntriesInAcl(1,&ea,pOldDACL,&pNewDACL); - if(pNewDACL) + ea.grfAccessMode= GRANT_ACCESS; + ea.grfAccessPermissions= GENERIC_ALL; + ea.grfInheritance= CONTAINER_INHERIT_ACE|OBJECT_INHERIT_ACE; + ea.Trustee.TrusteeType= TRUSTEE_IS_UNKNOWN; + ACL* pNewDACL= 0; + DWORD err= SetEntriesInAcl(1,&ea,pOldDACL,&pNewDACL); + if (pNewDACL) { SetSecurityInfo(hDir,SE_FILE_OBJECT,DACL_SECURITY_INFORMATION,NULL, NULL, pNewDACL, NULL); } - if(pSD != NULL) + if (pSD != NULL) LocalFree((HLOCAL) pSD); - if(pNewDACL != NULL) + if (pNewDACL != NULL) LocalFree((HLOCAL) pNewDACL); CloseHandle(hDir); return 0; } + /* Give directory permissions for special service user NT SERVICE\servicename this user is available only on Win7 and later. */ + void grant_directory_permissions_to_service() { char service_user[MAX_PATH+ 12]; OSVERSIONINFO info; - info.dwOSVersionInfoSize = sizeof(info); + info.dwOSVersionInfoSize= sizeof(info); GetVersionEx(&info); if (info.dwMajorVersion >6 || (info.dwMajorVersion== 6 && info.dwMinorVersion > 0) @@ -490,10 +518,12 @@ void grant_directory_permissions_to_service() } } + /* Create database instance (including registering as service etc) .*/ + static int create_db_instance() { - int ret=0; + int ret= 0; char cwd[MAX_PATH]; DWORD cwd_len= MAX_PATH; char cmdline[3*MAX_PATH]; @@ -504,30 +534,34 @@ static int create_db_instance() GetCurrentDirectory(cwd_len, cwd); CreateDirectory(opt_datadir, NULL); /*ignore error, it might already exist */ - if(!SetCurrentDirectory(opt_datadir)) + if (!SetCurrentDirectory(opt_datadir)) { - die("Cannot set current directory to %s\n",opt_datadir); + die("Cannot set current directory to '%s'\n",opt_datadir); return -1; } CreateDirectory("mysql",NULL); CreateDirectory("test", NULL); + /* + Set data directory permissions for both current user and + default_os_user (the one who runs services). + */ set_directory_permissions(opt_datadir, NULL); set_directory_permissions(opt_datadir, default_os_user); - /* Create mysqld --bootstrap process */ + /* Do mysqld --bootstrap. */ init_bootstrap_command_line(cmdline, sizeof(cmdline)); /* verbose("Executing %s", cmdline); */ in= popen(cmdline, "wt"); - if(!in) + if (!in) goto end; if (fwrite("use mysql;\n",11,1, in) != 1) { verbose("ERROR: Cannot write to mysqld's stdin"); - ret = 1; + ret= 1; goto end; } @@ -539,16 +573,15 @@ static int create_db_instance() goto end; } - /* Remove default user, if requested. */ - if(!opt_default_user) + if (!opt_default_user) { verbose("Removing default user",remove_default_user_cmd); fputs(remove_default_user_cmd, in); fflush(in); } - if(opt_allow_remote_root_access) + if (opt_allow_remote_root_access) { verbose("Allowing remote access for user root",remove_default_user_cmd); fputs(allow_remote_root_access_cmd,in); @@ -565,7 +598,6 @@ static int create_db_instance() fflush(in); } - /* On some reason, bootstrap chokes if last command sent via stdin ends with newline, so we supply a dummy comment, that does not end with newline. @@ -577,21 +609,21 @@ static int create_db_instance() ret= pclose(in); if (ret) { - verbose("mysqld returned an error in pclose"); + verbose("mysqld returned error %d in pclose",ret); goto end; } /* Create my.ini file in data directory.*/ ret= create_myini(); - if(ret) + if (ret) goto end; /* Register service if requested. */ - if(opt_service && opt_service[0]) + if (opt_service && opt_service[0]) { ret= register_service(); grant_directory_permissions_to_service(); - if(ret) + if (ret) goto end; } |