~ubuntu-branches/ubuntu/trusty/mariadb-5.5/trusty-proposed

« back to all changes in this revision

Viewing changes to win/packaging/ca/CustomAction.cpp

  • Committer: Package Import Robot
  • Author(s): Otto Kekäläinen
  • Date: 2013-12-22 10:27:05 UTC
  • Revision ID: package-import@ubuntu.com-20131222102705-mndw7s12mz0szrcn
Tags: upstream-5.5.32
Import upstream version 5.5.32

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright 2010, Oracle and/or its affiliates. All rights reserved.
 
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
15
 
 
16
#ifndef UNICODE
 
17
#define UNICODE
 
18
#endif
 
19
 
 
20
#include <winsock2.h>
 
21
#include <windows.h>
 
22
#include <winreg.h>
 
23
#include <msi.h>
 
24
#include <msiquery.h>
 
25
#include <wcautil.h>
 
26
#include <strutil.h>
 
27
#include <string.h>
 
28
#include <strsafe.h>
 
29
#include <assert.h>
 
30
#include <shellapi.h>
 
31
#include <stdlib.h>
 
32
#include <winservice.h>
 
33
 
 
34
#define ONE_MB 1048576
 
35
UINT ExecRemoveDataDirectory(wchar_t *dir)
 
36
{
 
37
   /* Strip stray backslash */
 
38
  DWORD len = (DWORD)wcslen(dir);
 
39
  if(len > 0 && dir[len-1]==L'\\')
 
40
    dir[len-1] = 0;
 
41
 
 
42
  SHFILEOPSTRUCTW fileop;
 
43
  fileop.hwnd= NULL;    /* no status display */
 
44
  fileop.wFunc= FO_DELETE;  /* delete operation */
 
45
  fileop.pFrom= dir;  /* source file name as double null terminated string */
 
46
  fileop.pTo= NULL;    /* no destination needed */
 
47
  fileop.fFlags= FOF_NOCONFIRMATION|FOF_SILENT;  /* do not prompt the user */
 
48
 
 
49
  fileop.fAnyOperationsAborted= FALSE;
 
50
  fileop.lpszProgressTitle= NULL;
 
51
  fileop.hNameMappings= NULL;
 
52
 
 
53
  return SHFileOperationW(&fileop);
 
54
}
 
55
 
 
56
 
 
57
extern "C" UINT __stdcall RemoveDataDirectory(MSIHANDLE hInstall) 
 
58
{
 
59
  HRESULT hr = S_OK;
 
60
  UINT er = ERROR_SUCCESS;
 
61
 
 
62
  hr = WcaInitialize(hInstall, __FUNCTION__);
 
63
  ExitOnFailure(hr, "Failed to initialize");
 
64
  WcaLog(LOGMSG_STANDARD, "Initialized.");
 
65
 
 
66
  wchar_t dir[MAX_PATH];
 
67
  DWORD len = MAX_PATH;
 
68
  MsiGetPropertyW(hInstall, L"CustomActionData", dir, &len);
 
69
 
 
70
  er= ExecRemoveDataDirectory(dir);
 
71
  WcaLog(LOGMSG_STANDARD, "SHFileOperation returned %d", er);
 
72
LExit:
 
73
  return WcaFinalize(er); 
 
74
}
 
75
 
 
76
/*
 
77
  Escape command line parameter fpr pass to CreateProcess().
 
78
 
 
79
  We assume out has enough space to include encoded string 
 
80
  2*wcslen(in) is enough.
 
81
 
 
82
  It is assumed that called will add double quotation marks before and after
 
83
  the string.
 
84
*/
 
85
static void EscapeCommandLine(const wchar_t *in, wchar_t *out, size_t buflen)
 
86
{
 
87
  const wchar_t special_chars[]=L" \t\n\v\"";
 
88
  bool needs_escaping= false;
 
89
  size_t pos;
 
90
 
 
91
  for(int i=0; i< sizeof(special_chars) -1; i++)
 
92
  {
 
93
    if (wcschr(in, special_chars[i]))
 
94
    {
 
95
      needs_escaping = true;
 
96
      break;
 
97
    }
 
98
  }
 
99
 
 
100
  if(!needs_escaping)
 
101
  {
 
102
    wcscpy_s(out, buflen, in);
 
103
    return;
 
104
  }
 
105
 
 
106
  pos= 0;
 
107
  for(int i = 0 ; ; i++) 
 
108
  {
 
109
    size_t n_backslashes = 0;
 
110
    wchar_t c;
 
111
    while (in[i] == L'\\') 
 
112
    {
 
113
      i++;
 
114
      n_backslashes++;
 
115
    }
 
116
 
 
117
    c= in[i];
 
118
    if (c == 0) 
 
119
    {
 
120
      /*
 
121
        Escape all backslashes, but let the terminating double quotation mark 
 
122
        that caller adds be interpreted as a metacharacter.
 
123
      */
 
124
      for(size_t j= 0; j < 2*n_backslashes;j++)
 
125
      {
 
126
        out[pos++]=L'\\';
 
127
      }
 
128
      break;
 
129
    }
 
130
    else if (c == L'"') 
 
131
    {
 
132
      /*
 
133
        Escape all backslashes and the following double quotation mark.
 
134
      */
 
135
      for(size_t j= 0; j < 2*n_backslashes + 1; j++)
 
136
      {
 
137
        out[pos++]=L'\\';
 
138
      }
 
139
      out[pos++]= L'"';
 
140
    }
 
141
    else 
 
142
    {
 
143
      /* Backslashes aren't special here. */
 
144
      for (size_t j=0; j < n_backslashes; j++)
 
145
        out[pos++] = L'\\';
 
146
 
 
147
      out[pos++]= c;
 
148
    }
 
149
  }
 
150
  out[pos++]= 0;
 
151
}
 
152
/* 
 
153
  Check for if directory is empty during install, 
 
154
  sets "<PROPERTY>_NOT_EMPTY" otherise
 
155
*/
 
156
extern "C" UINT __stdcall CheckDirectoryEmpty(MSIHANDLE hInstall, 
 
157
  const wchar_t *PropertyName)
 
158
{
 
159
  HRESULT hr = S_OK;
 
160
  UINT er = ERROR_SUCCESS;
 
161
  hr = WcaInitialize(hInstall, __FUNCTION__);
 
162
  ExitOnFailure(hr, "Failed to initialize");
 
163
  WcaLog(LOGMSG_STANDARD, "Initialized.");
 
164
 
 
165
  
 
166
  wchar_t buf[MAX_PATH];
 
167
  DWORD len = MAX_PATH;
 
168
  MsiGetPropertyW(hInstall, PropertyName, buf, &len);
 
169
  wcscat_s(buf, MAX_PATH, L"*.*");
 
170
  
 
171
  WcaLog(LOGMSG_STANDARD, "Checking files in %S", buf);
 
172
  WIN32_FIND_DATAW data;
 
173
  HANDLE h;
 
174
  bool empty;
 
175
  h= FindFirstFile(buf, &data);
 
176
  if (h !=  INVALID_HANDLE_VALUE)
 
177
  {
 
178
     empty= true;
 
179
     for(;;)
 
180
     {
 
181
       if (wcscmp(data.cFileName, L".") || wcscmp(data.cFileName, L".."))
 
182
       {
 
183
         empty= false;
 
184
         break;
 
185
       }
 
186
       if (!FindNextFile(h, &data))
 
187
         break;
 
188
     }
 
189
     FindClose(h);
 
190
  }
 
191
  else
 
192
  {
 
193
    /* Non-existent directory, we handle it as empty */
 
194
    empty = true;
 
195
  }
 
196
 
 
197
  if(empty)
 
198
    WcaLog(LOGMSG_STANDARD, "Directory %S is empty or non-existent", 
 
199
    PropertyName);
 
200
  else
 
201
    WcaLog(LOGMSG_STANDARD, "Directory %S is NOT empty", PropertyName);
 
202
 
 
203
  wcscpy_s(buf, MAX_PATH, PropertyName);
 
204
  wcscat_s(buf, L"NOTEMPTY");
 
205
  WcaSetProperty(buf, empty? L"":L"1");
 
206
 
 
207
LExit:
 
208
  return WcaFinalize(er); 
 
209
}
 
210
 
 
211
extern "C" UINT __stdcall CheckDataDirectoryEmpty(MSIHANDLE hInstall)
 
212
{
 
213
  return CheckDirectoryEmpty(hInstall, L"DATADIR");
 
214
}
 
215
 
 
216
bool CheckServiceExists(const wchar_t *name)
 
217
{
 
218
   SC_HANDLE manager =0, service=0;
 
219
   manager = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT);
 
220
   if (!manager) 
 
221
   {
 
222
     return false;
 
223
   }
 
224
 
 
225
   service = OpenService(manager, name, SC_MANAGER_CONNECT); 
 
226
   if(service)
 
227
     CloseServiceHandle(service);
 
228
   CloseServiceHandle(manager);
 
229
 
 
230
   return service?true:false;
 
231
}
 
232
 
 
233
/* User in rollback of create database custom action */
 
234
bool ExecRemoveService(const wchar_t *name)
 
235
{
 
236
   SC_HANDLE manager =0, service=0;
 
237
   manager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS);
 
238
   bool ret;
 
239
   if (!manager) 
 
240
   {
 
241
     return false;
 
242
   }
 
243
   service = OpenService(manager, name, DELETE); 
 
244
   if(service)
 
245
   {
 
246
     ret= DeleteService(service);
 
247
   }
 
248
   else
 
249
   {
 
250
     ret= false;
 
251
   }
 
252
   CloseServiceHandle(manager);
 
253
   return ret;
 
254
}
 
255
 
 
256
/*
 
257
  Check if port is free by trying to bind to the port
 
258
*/
 
259
bool IsPortFree(short port)
 
260
{
 
261
   WORD wVersionRequested;
 
262
   WSADATA wsaData;
 
263
 
 
264
   wVersionRequested = MAKEWORD(2, 2);
 
265
 
 
266
   WSAStartup(wVersionRequested, &wsaData);
 
267
 
 
268
  struct sockaddr_in sin;
 
269
  SOCKET sock;
 
270
  sock = socket(AF_INET, SOCK_STREAM, 0);
 
271
  if(sock == -1)
 
272
  {
 
273
    return false;
 
274
  }
 
275
  sin.sin_port = htons(port);
 
276
  sin.sin_addr.s_addr = 0;
 
277
  sin.sin_addr.s_addr = INADDR_ANY;
 
278
  sin.sin_family = AF_INET;
 
279
  if(bind(sock, (struct sockaddr *)&sin,sizeof(struct sockaddr_in) ) == -1)
 
280
  {
 
281
    return false;
 
282
  }
 
283
  closesocket(sock);
 
284
  WSACleanup();
 
285
  return true;
 
286
}
 
287
 
 
288
 
 
289
/* 
 
290
   Helper function used in filename normalization.
 
291
   Removes leading quote and terminates string at the position of the next one
 
292
   (if applicable, does not change string otherwise). Returns modified string
 
293
*/
 
294
wchar_t *strip_quotes(wchar_t *s)
 
295
{
 
296
   if (s && (*s == L'"'))
 
297
   {
 
298
     s++;
 
299
     wchar_t *p = wcschr(s, L'"');
 
300
     if(p)
 
301
       *p = 0;
 
302
   }
 
303
   return s;
 
304
}
 
305
 
 
306
 
 
307
/*
 
308
  Checks  for consistency of service configuration.
 
309
 
 
310
  It can happen that SERVICENAME or DATADIR 
 
311
  MSI properties are in inconsistent state after somebody upgraded database 
 
312
  We catch this case during uninstall. In particular, either service is not 
 
313
  removed even if SERVICENAME was set (but this name is reused by someone else)
 
314
  or data directory is not removed (if it is used by someone else). To find out
 
315
  whether service name and datadirectory are in use For every service, 
 
316
  configuration is read and checked as follows:
 
317
 
 
318
  - look if a service has to do something with mysql
 
319
  - If so, check its name against SERVICENAME. if match, check binary path 
 
320
    against INSTALLDIR\bin.  If binary path does not match, then service runs
 
321
    under different  installation and won't be removed.
 
322
  - Check options file for datadir and look if this is inside this
 
323
    installation's datadir don't remove datadir if this is the case.
 
324
 
 
325
  "Don't remove" in this context means that custom action is removing 
 
326
  SERVICENAME property or CLEANUPDATA property, which later on in course of 
 
327
  installation mean, that either datadir or service is kept.
 
328
*/
 
329
 
 
330
void CheckServiceConfig(
 
331
  wchar_t *my_servicename,    /* SERVICENAME property in this installation*/
 
332
  wchar_t *datadir,           /* DATADIR property in this installation*/
 
333
  wchar_t *bindir,            /* INSTALLDIR\bin */
 
334
  wchar_t *other_servicename, /* Service to check against */
 
335
  QUERY_SERVICE_CONFIGW * config /* Other service's config */
 
336
  )
 
337
{
 
338
 
 
339
  bool same_bindir = false;
 
340
  wchar_t * commandline= config->lpBinaryPathName;
 
341
  int numargs;
 
342
  wchar_t **argv= CommandLineToArgvW(commandline, &numargs);
 
343
  WcaLog(LOGMSG_VERBOSE, "CommandLine= %S", commandline);
 
344
  if(!argv  ||  !argv[0]  || ! wcsstr(argv[0], L"mysqld"))
 
345
  {
 
346
    goto end;
 
347
  }
 
348
 
 
349
  WcaLog(LOGMSG_STANDARD, "MySQL service %S found: CommandLine= %S", 
 
350
    other_servicename, commandline);
 
351
  if (wcsstr(argv[0], bindir))
 
352
  {
 
353
    WcaLog(LOGMSG_STANDARD, "executable under bin directory");
 
354
    same_bindir = true;
 
355
  }
 
356
 
 
357
  bool is_my_service = (_wcsicmp(my_servicename, other_servicename) == 0);
 
358
  if(!is_my_service)
 
359
  {
 
360
    WcaLog(LOGMSG_STANDARD, "service does not match current service");
 
361
    /* 
 
362
      TODO probably the best thing possible would be to add temporary
 
363
      row to MSI ServiceConfig table with remove on uninstall
 
364
    */
 
365
  }
 
366
  else if (!same_bindir)
 
367
  {
 
368
    WcaLog(LOGMSG_STANDARD, 
 
369
      "Service name matches, but not the executable path directory, mine is %S", 
 
370
      bindir);
 
371
    WcaSetProperty(L"SERVICENAME", L"");
 
372
  }
 
373
 
 
374
  /* Check if data directory is used */
 
375
  if(!datadir || numargs <= 1 ||  wcsncmp(argv[1],L"--defaults-file=",16) != 0)
 
376
  {
 
377
    goto end;
 
378
  }
 
379
 
 
380
  wchar_t current_datadir_buf[MAX_PATH]={0};
 
381
  wchar_t normalized_current_datadir[MAX_PATH+1];
 
382
  wchar_t *current_datadir= current_datadir_buf;
 
383
  wchar_t *defaults_file= argv[1]+16;
 
384
  defaults_file= strip_quotes(defaults_file);
 
385
 
 
386
  WcaLog(LOGMSG_STANDARD, "parsed defaults file is %S", defaults_file);
 
387
 
 
388
  if (GetPrivateProfileStringW(L"mysqld", L"datadir", NULL, current_datadir, 
 
389
    MAX_PATH, defaults_file) == 0)
 
390
  {
 
391
    WcaLog(LOGMSG_STANDARD, 
 
392
      "Cannot find datadir in ini file '%S'", defaults_file);
 
393
    goto end;
 
394
  }
 
395
 
 
396
  WcaLog(LOGMSG_STANDARD, "datadir from defaults-file is %S", current_datadir);
 
397
  strip_quotes(current_datadir);
 
398
 
 
399
  /* Convert to Windows path */
 
400
  if (GetFullPathNameW(current_datadir, MAX_PATH, normalized_current_datadir, 
 
401
    NULL))
 
402
  {
 
403
    /* Add backslash to be compatible with directory formats in MSI */
 
404
    wcsncat(normalized_current_datadir, L"\\", MAX_PATH+1);
 
405
    WcaLog(LOGMSG_STANDARD, "normalized current datadir is '%S'", 
 
406
      normalized_current_datadir);
 
407
  }
 
408
 
 
409
  if (_wcsicmp(datadir, normalized_current_datadir) == 0 && !same_bindir)
 
410
  {
 
411
    WcaLog(LOGMSG_STANDARD, 
 
412
      "database directory from current installation, but different mysqld.exe");
 
413
    WcaSetProperty(L"CLEANUPDATA", L"");
 
414
  }
 
415
 
 
416
end:
 
417
  LocalFree((HLOCAL)argv);
 
418
}
 
419
 
 
420
/*
 
421
  Checks if database directory or service are modified by user
 
422
  For example, service may point to different mysqld.exe that it was originally
 
423
  installed, or some different service might use this database directory. This 
 
424
  would normally mean user has done an upgrade of the database and in this case 
 
425
  uninstall should neither delete service nor database directory.
 
426
 
 
427
  If this function find that service is modified by user (mysqld.exe used by 
 
428
  service does not point to the installation bin directory), MSI public variable
 
429
  SERVICENAME is removed, if DATADIR is used by some other service, variables 
 
430
  DATADIR and CLEANUPDATA are removed.
 
431
 
 
432
  The effect of variable removal is that service does not get uninstalled and
 
433
  datadir is not touched by uninstallation.
 
434
 
 
435
  Note that this function is running without elevation and does not use anything
 
436
  that would require special privileges.
 
437
 
 
438
*/
 
439
extern "C" UINT CheckDBInUse(MSIHANDLE hInstall)
 
440
{
 
441
  static BYTE buf[256*1024]; /* largest possible buffer for EnumServices */
 
442
  static char config_buffer[8*1024]; /*largest buffer for QueryServiceConfig */
 
443
  HRESULT hr = S_OK;
 
444
  UINT er = ERROR_SUCCESS;
 
445
  wchar_t *servicename= NULL;
 
446
  wchar_t *datadir= NULL;
 
447
  wchar_t *bindir=NULL;
 
448
 
 
449
  SC_HANDLE scm    = NULL; 
 
450
  ULONG  bufsize   = sizeof(buf); 
 
451
  ULONG  bufneed   = 0x00; 
 
452
  ULONG  num_services = 0x00; 
 
453
  LPENUM_SERVICE_STATUS_PROCESS info = NULL;  
 
454
 
 
455
  hr = WcaInitialize(hInstall, __FUNCTION__);
 
456
  ExitOnFailure(hr, "Failed to initialize");
 
457
  WcaLog(LOGMSG_STANDARD, "Initialized.");
 
458
 
 
459
  WcaGetProperty(L"SERVICENAME", &servicename);
 
460
  WcaGetProperty(L"DATADIR", &datadir);
 
461
  WcaGetFormattedString(L"[INSTALLDIR]bin\\", &bindir);
 
462
  WcaLog(LOGMSG_STANDARD,"SERVICENAME=%S, DATADIR=%S, bindir=%S",
 
463
    servicename, datadir, bindir);
 
464
 
 
465
  scm = OpenSCManager(NULL, NULL, 
 
466
    SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT);  
 
467
  if (scm == NULL) 
 
468
  { 
 
469
    ExitOnFailure(E_FAIL, "OpenSCManager failed");
 
470
  }
 
471
 
 
472
  BOOL ok= EnumServicesStatusExW(  scm,
 
473
    SC_ENUM_PROCESS_INFO, 
 
474
    SERVICE_WIN32,
 
475
    SERVICE_STATE_ALL,  
 
476
    buf, 
 
477
    bufsize,  
 
478
    &bufneed,  
 
479
    &num_services,  
 
480
    NULL, 
 
481
    NULL);
 
482
  if(!ok) 
 
483
  {
 
484
    WcaLog(LOGMSG_STANDARD, "last error %d", GetLastError());
 
485
    ExitOnFailure(E_FAIL, "EnumServicesStatusExW failed");
 
486
  }
 
487
  info = (LPENUM_SERVICE_STATUS_PROCESS)buf; 
 
488
  for (ULONG i=0; i < num_services; i++)
 
489
  {
 
490
    SC_HANDLE service= OpenServiceW(scm, info[i].lpServiceName, 
 
491
      SERVICE_QUERY_CONFIG);
 
492
    if (!service)
 
493
      continue;
 
494
    WcaLog(LOGMSG_VERBOSE, "Checking Service %S", info[i].lpServiceName);
 
495
    QUERY_SERVICE_CONFIGW *config= 
 
496
      (QUERY_SERVICE_CONFIGW *)(void *)config_buffer;
 
497
    DWORD needed;
 
498
    BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer), 
 
499
      &needed);
 
500
    CloseServiceHandle(service);
 
501
    if (ok)
 
502
    {
 
503
       CheckServiceConfig(servicename, datadir, bindir, info[i].lpServiceName, 
 
504
         config);
 
505
    }
 
506
  }
 
507
 
 
508
LExit:
 
509
  if(scm)
 
510
    CloseServiceHandle(scm);
 
511
 
 
512
  ReleaseStr(servicename);
 
513
  ReleaseStr(datadir);
 
514
  ReleaseStr(bindir);
 
515
  return WcaFinalize(er); 
 
516
}
 
517
 
 
518
/* 
 
519
  Get maximum size of the buffer process can allocate.
 
520
  this is calculated as min(RAM,virtualmemorylimit)
 
521
  For 32bit processes, virtual address memory is 2GB (x86 OS)
 
522
  or 4GB(x64 OS).
 
523
  
 
524
  Fragmentation due to loaded modules, heap  and stack 
 
525
  limit maximum size of continous memory block further,
 
526
  so that limit for 32 bit process is about 1200 on 32 bit OS 
 
527
  or 2000 MB on 64 bit OS(found experimentally).
 
528
*/
 
529
unsigned long long GetMaxBufferSize(unsigned long long totalPhys)
 
530
{
 
531
#ifdef _M_IX86
 
532
  BOOL wow64;
 
533
  if (IsWow64Process(GetCurrentProcess(), &wow64))
 
534
    return min(totalPhys, 2000ULL*ONE_MB);
 
535
  else
 
536
    return min(totalPhys, 1200ULL*ONE_MB);
 
537
#else
 
538
  return totalPhys;
 
539
#endif
 
540
}
 
541
 
 
542
 
 
543
/*
 
544
  Checks SERVICENAME, PORT and BUFFERSIZE parameters 
 
545
*/
 
546
extern "C" UINT  __stdcall CheckDatabaseProperties (MSIHANDLE hInstall) 
 
547
{
 
548
  wchar_t ServiceName[MAX_PATH]={0};
 
549
  wchar_t SkipNetworking[MAX_PATH]={0};
 
550
  wchar_t QuickConfig[MAX_PATH]={0};
 
551
  wchar_t Password[MAX_PATH]={0};
 
552
  wchar_t EscapedPassword[2*MAX_PATH+2];
 
553
  wchar_t Port[6];
 
554
  wchar_t BufferPoolSize[16];
 
555
  DWORD PortLen=6;
 
556
  bool haveInvalidPort=false;
 
557
  const wchar_t *ErrorMsg=0;
 
558
  HRESULT hr= S_OK;
 
559
  UINT er= ERROR_SUCCESS;
 
560
 
 
561
 
 
562
  hr = WcaInitialize(hInstall, __FUNCTION__);
 
563
  ExitOnFailure(hr, "Failed to initialize");
 
564
  WcaLog(LOGMSG_STANDARD, "Initialized.");
 
565
 
 
566
  DWORD ServiceNameLen = MAX_PATH;
 
567
  MsiGetPropertyW (hInstall, L"SERVICENAME", ServiceName, &ServiceNameLen);
 
568
  if(ServiceName[0])
 
569
  {
 
570
    if(ServiceNameLen > 256)
 
571
    {
 
572
      ErrorMsg= L"Invalid service name. The maximum length is 256 characters.";
 
573
      goto LExit;
 
574
    }
 
575
    for(DWORD i=0; i< ServiceNameLen;i++)
 
576
    {
 
577
      if(ServiceName[i] == L'\\' || ServiceName[i] == L'/' 
 
578
        || ServiceName[i]=='\'' || ServiceName[i] ==L'"')
 
579
      {
 
580
        ErrorMsg = 
 
581
          L"Invalid service name. Forward slash and back slash are forbidden."
 
582
          L"Single and double quotes are also not permitted.";
 
583
        goto LExit;
 
584
      }
 
585
    }
 
586
    if(CheckServiceExists(ServiceName))
 
587
    {
 
588
      ErrorMsg=
 
589
        L"A service with the same name already exists. "
 
590
        L"Please use a different name.";
 
591
      goto LExit;
 
592
    }
 
593
  }
 
594
 
 
595
  DWORD PasswordLen= MAX_PATH;
 
596
  MsiGetPropertyW (hInstall, L"PASSWORD", Password, &PasswordLen);
 
597
  EscapeCommandLine(Password, EscapedPassword,
 
598
    sizeof(EscapedPassword)/sizeof(EscapedPassword[0]));
 
599
  MsiSetPropertyW(hInstall,L"ESCAPEDPASSWORD",EscapedPassword);
 
600
 
 
601
  DWORD SkipNetworkingLen= MAX_PATH;
 
602
  MsiGetPropertyW(hInstall, L"SKIPNETWORKING", SkipNetworking, 
 
603
    &SkipNetworkingLen);
 
604
  MsiGetPropertyW(hInstall, L"PORT", Port, &PortLen);
 
605
  
 
606
  if(SkipNetworking[0]==0 && Port[0] != 0)
 
607
  {
 
608
    /* Strip spaces */
 
609
    for(DWORD i=PortLen-1; i > 0; i--)
 
610
    {
 
611
      if(Port[i]== ' ')
 
612
        Port[i] = 0;
 
613
    }
 
614
 
 
615
    if(PortLen > 5 || PortLen <= 3)
 
616
      haveInvalidPort = true;
 
617
    else
 
618
    {
 
619
      for (DWORD i=0; i< PortLen && Port[i] != 0;i++)
 
620
      {
 
621
        if(Port[i] < '0' || Port[i] >'9')
 
622
        {
 
623
          haveInvalidPort=true;
 
624
          break;
 
625
        }
 
626
      }
 
627
    }
 
628
    if (haveInvalidPort)
 
629
    {
 
630
      ErrorMsg =
 
631
        L"Invalid port number. Please use a number between 1025 and 65535.";
 
632
      goto LExit;
 
633
    }
 
634
 
 
635
    short port = (short)_wtoi(Port);
 
636
    if (!IsPortFree(port))
 
637
    {
 
638
      ErrorMsg = 
 
639
        L"The TCP Port you selected is already in use. "
 
640
        L"Please choose a different port.";
 
641
      goto LExit;
 
642
    }
 
643
  }
 
644
  
 
645
  
 
646
  DWORD QuickConfigLen = MAX_PATH;
 
647
  MsiGetPropertyW (hInstall, L"STDCONFIG", QuickConfig, &QuickConfigLen);
 
648
  if(QuickConfig[0] !=0)
 
649
  {
 
650
     MEMORYSTATUSEX memstatus;
 
651
     memstatus.dwLength =sizeof(memstatus);
 
652
     wchar_t invalidValueMsg[256];
 
653
 
 
654
     if (!GlobalMemoryStatusEx(&memstatus))
 
655
     {
 
656
        WcaLog(LOGMSG_STANDARD, "Error %u from GlobalMemoryStatusEx", 
 
657
          GetLastError());
 
658
        er= ERROR_INSTALL_FAILURE;
 
659
        goto LExit;
 
660
     }
 
661
     DWORD BufferPoolSizeLen= 16;
 
662
     MsiGetPropertyW(hInstall, L"BUFFERPOOLSIZE", BufferPoolSize, &BufferPoolSizeLen);
 
663
     /* Strip spaces */
 
664
     for(DWORD i=BufferPoolSizeLen-1; i > 0; i--)
 
665
     {
 
666
      if(BufferPoolSize[i]== ' ')
 
667
        BufferPoolSize[i] = 0;
 
668
     }
 
669
     unsigned long long availableMemory=
 
670
       GetMaxBufferSize(memstatus.ullTotalPhys)/ONE_MB;
 
671
     swprintf_s(invalidValueMsg,
 
672
        L"Invalid buffer pool size. Please use a number between 1 and %llu",
 
673
         availableMemory);
 
674
     if(BufferPoolSizeLen == 0 || BufferPoolSizeLen > 15)
 
675
     {
 
676
       ErrorMsg= invalidValueMsg;
 
677
       goto LExit;
 
678
     }
 
679
     for (DWORD i=0; i < BufferPoolSizeLen && BufferPoolSize[BufferPoolSizeLen];
 
680
       i++)
 
681
     {
 
682
       if(BufferPoolSize[i]< '0' || BufferPoolSize[i] > '9')
 
683
       {
 
684
         ErrorMsg= invalidValueMsg;
 
685
         goto LExit;
 
686
       }
 
687
     }
 
688
     BufferPoolSize[BufferPoolSizeLen]=0;
 
689
     MsiSetPropertyW(hInstall, L"BUFFERPOOLSIZE", BufferPoolSize);
 
690
     long long sz = _wtoi64(BufferPoolSize);
 
691
     if(sz <= 0 || sz > (long long)availableMemory)
 
692
     {
 
693
       if(sz > 0)
 
694
       {
 
695
         swprintf_s(invalidValueMsg,
 
696
           L"Value for buffer pool size is too large."
 
697
           L"Only approximately %llu MB is available for allocation."
 
698
           L"Please use a number between 1 and %llu.",
 
699
          availableMemory, availableMemory);
 
700
       }
 
701
       ErrorMsg= invalidValueMsg;
 
702
       goto LExit;
 
703
     }
 
704
  }
 
705
LExit:
 
706
  MsiSetPropertyW (hInstall, L"WarningText", ErrorMsg);
 
707
  return WcaFinalize(er);
 
708
}
 
709
 
 
710
/* 
 
711
  Sets Innodb buffer pool size (1/8 of RAM by default), if not already specified
 
712
  via command line.
 
713
  Calculates innodb log file size as min(50, innodb buffer pool size/8)
 
714
*/
 
715
extern "C" UINT __stdcall PresetDatabaseProperties(MSIHANDLE hInstall)
 
716
{
 
717
  unsigned long long InnodbBufferPoolSize= 256;
 
718
  unsigned long long InnodbLogFileSize= 50;
 
719
  wchar_t buff[MAX_PATH];
 
720
  UINT er = ERROR_SUCCESS;
 
721
  HRESULT hr= S_OK;
 
722
  MEMORYSTATUSEX memstatus;
 
723
  hr = WcaInitialize(hInstall, __FUNCTION__);
 
724
  ExitOnFailure(hr, "Failed to initialize");
 
725
  WcaLog(LOGMSG_STANDARD, "Initialized.");
 
726
 
 
727
  /* Check if bufferpoolsize parameter was given on the command line*/
 
728
  DWORD BufferPoolsizeParamLen = MAX_PATH;
 
729
  MsiGetPropertyW(hInstall, L"BUFFERPOOLSIZE", buff, &BufferPoolsizeParamLen);
 
730
 
 
731
  if (BufferPoolsizeParamLen && buff[0])
 
732
  {
 
733
    WcaLog(LOGMSG_STANDARD, "BUFFERPOOLSIZE=%s, len=%u",buff, BufferPoolsizeParamLen);
 
734
    InnodbBufferPoolSize= _wtoi64(buff);
 
735
  }
 
736
  else
 
737
  {
 
738
    memstatus.dwLength = sizeof(memstatus);
 
739
    if (!GlobalMemoryStatusEx(&memstatus))
 
740
    {
 
741
       WcaLog(LOGMSG_STANDARD, "Error %u from GlobalMemoryStatusEx", 
 
742
         GetLastError());
 
743
       er= ERROR_INSTALL_FAILURE;
 
744
       goto LExit;
 
745
    }
 
746
    unsigned long long totalPhys= memstatus.ullTotalPhys;
 
747
    /* Give innodb 12.5% of available physical memory. */
 
748
    InnodbBufferPoolSize= totalPhys/ONE_MB/8;
 
749
 #ifdef _M_IX86
 
750
    /* 
 
751
      For 32 bit processes, take virtual address space limitation into account.
 
752
      Do not try to use more than 3/4 of virtual address space, even if there
 
753
      is plenty of physical memory.
 
754
    */
 
755
    InnodbBufferPoolSize= min(GetMaxBufferSize(totalPhys)/ONE_MB*3/4,
 
756
      InnodbBufferPoolSize);
 
757
 #endif 
 
758
    swprintf_s(buff, L"%llu",InnodbBufferPoolSize);
 
759
    MsiSetPropertyW(hInstall, L"BUFFERPOOLSIZE", buff);
 
760
  }
 
761
  InnodbLogFileSize = min(50, InnodbBufferPoolSize);
 
762
  swprintf_s(buff, L"%llu",InnodbLogFileSize);
 
763
  MsiSetPropertyW(hInstall, L"LOGFILESIZE", buff);
 
764
 
 
765
LExit:
 
766
  return WcaFinalize(er);
 
767
}
 
768
/* Remove service and data directory created by CreateDatabase operation */
 
769
extern "C" UINT __stdcall CreateDatabaseRollback(MSIHANDLE hInstall) 
 
770
{
 
771
  HRESULT hr = S_OK;
 
772
  UINT er = ERROR_SUCCESS;
 
773
  wchar_t* service= 0;
 
774
  wchar_t* dir= 0;
 
775
 
 
776
  hr = WcaInitialize(hInstall, __FUNCTION__);
 
777
  ExitOnFailure(hr, "Failed to initialize");
 
778
  WcaLog(LOGMSG_STANDARD, "Initialized.");
 
779
  wchar_t data[2*MAX_PATH];
 
780
  DWORD len= MAX_PATH;
 
781
  MsiGetPropertyW(hInstall, L"CustomActionData", data, &len);
 
782
 
 
783
  /* Property is encoded as [SERVICENAME]\[DBLOCATION] */
 
784
  if(data[0] == L'\\')
 
785
  {
 
786
    dir= data+1;
 
787
  }
 
788
  else
 
789
  {
 
790
    service= data;
 
791
    dir= wcschr(data, '\\');
 
792
    if (dir)
 
793
    {
 
794
     *dir=0;
 
795
     dir++;
 
796
    }
 
797
  }
 
798
 
 
799
  if(service)
 
800
  {
 
801
    ExecRemoveService(service);
 
802
  }
 
803
  if(dir)
 
804
  {
 
805
    ExecRemoveDataDirectory(dir);
 
806
  }
 
807
LExit:
 
808
  return WcaFinalize(er); 
 
809
}
 
810
 
 
811
 
 
812
/*
 
813
  Enables/disables optional "Launch upgrade wizard" checkbox at the end of 
 
814
  installation
 
815
*/
 
816
#define MAX_VERSION_PROPERTY_SIZE 64
 
817
 
 
818
extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall) 
 
819
{
 
820
  HRESULT hr = S_OK;
 
821
  UINT er = ERROR_SUCCESS;
 
822
  wchar_t* service= 0;
 
823
  wchar_t* dir= 0;
 
824
  wchar_t installerVersion[MAX_VERSION_PROPERTY_SIZE];
 
825
  char installDir[MAX_PATH];
 
826
  DWORD size =MAX_VERSION_PROPERTY_SIZE;
 
827
  int installerMajorVersion, installerMinorVersion, installerPatchVersion;
 
828
  bool upgradableServiceFound=false;
 
829
 
 
830
  hr = WcaInitialize(hInstall, __FUNCTION__);
 
831
   WcaLog(LOGMSG_STANDARD, "Initialized.");
 
832
  if (MsiGetPropertyW(hInstall, L"ProductVersion", installerVersion, &size) 
 
833
    != ERROR_SUCCESS)
 
834
  {
 
835
    hr = HRESULT_FROM_WIN32(GetLastError());
 
836
    ExitOnFailure(hr, "MsiGetPropertyW failed");
 
837
  }
 
838
   if (swscanf(installerVersion,L"%d.%d.%d",
 
839
    &installerMajorVersion, &installerMinorVersion, &installerPatchVersion) !=3)
 
840
  {
 
841
    assert(FALSE);
 
842
  }
 
843
 
 
844
  size= MAX_PATH;
 
845
  if (MsiGetPropertyA(hInstall,"INSTALLDIR", installDir, &size)
 
846
    != ERROR_SUCCESS)
 
847
  {
 
848
    hr = HRESULT_FROM_WIN32(GetLastError());
 
849
    ExitOnFailure(hr, "MsiGetPropertyW failed");
 
850
  }
 
851
 
 
852
 
 
853
  SC_HANDLE scm = OpenSCManager(NULL, NULL, 
 
854
    SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT);  
 
855
  if (scm == NULL) 
 
856
  { 
 
857
    hr = HRESULT_FROM_WIN32(GetLastError());
 
858
    ExitOnFailure(hr,"OpenSCManager failed");
 
859
  }
 
860
 
 
861
  static BYTE buf[64*1024];
 
862
  static BYTE config_buffer[8*1024];
 
863
 
 
864
  DWORD bufsize= sizeof(buf);
 
865
  DWORD bufneed;
 
866
  DWORD num_services;
 
867
  BOOL ok= EnumServicesStatusExW(scm, SC_ENUM_PROCESS_INFO,  SERVICE_WIN32,
 
868
    SERVICE_STATE_ALL,  buf, bufsize,  &bufneed, &num_services, NULL, NULL);
 
869
  if(!ok) 
 
870
  {
 
871
    hr = HRESULT_FROM_WIN32(GetLastError());
 
872
    ExitOnFailure(hr,"EnumServicesStatusEx failed");
 
873
  }
 
874
  LPENUM_SERVICE_STATUS_PROCESSW info =
 
875
    (LPENUM_SERVICE_STATUS_PROCESSW)buf;
 
876
  int index=-1;
 
877
  for (ULONG i=0; i < num_services; i++)
 
878
  {
 
879
    SC_HANDLE service= OpenServiceW(scm, info[i].lpServiceName, 
 
880
      SERVICE_QUERY_CONFIG);
 
881
    if (!service)
 
882
      continue;
 
883
    QUERY_SERVICE_CONFIGW *config= 
 
884
      (QUERY_SERVICE_CONFIGW*)(void *)config_buffer;
 
885
    DWORD needed;
 
886
    BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer),
 
887
      &needed);
 
888
    CloseServiceHandle(service);
 
889
    if (ok)
 
890
    {
 
891
        mysqld_service_properties props;
 
892
       if (get_mysql_service_properties(config->lpBinaryPathName, &props))
 
893
                  continue;
 
894
        /* 
 
895
          Only look for services that have mysqld.exe outside of the current
 
896
          installation directory.
 
897
        */
 
898
       if(installDir[0] == 0 || strstr(props.mysqld_exe,installDir) == 0)
 
899
        {
 
900
           WcaLog(LOGMSG_STANDARD, "found service %S, major=%d, minor=%d",
 
901
            info[i].lpServiceName, props.version_major, props.version_minor);
 
902
          if(props.version_major < installerMajorVersion
 
903
            || (props.version_major == installerMajorVersion && 
 
904
                props.version_minor <= installerMinorVersion))
 
905
          {
 
906
            upgradableServiceFound= true;
 
907
            break;
 
908
          }
 
909
       }
 
910
    }
 
911
  }
 
912
 
 
913
  if(!upgradableServiceFound)
 
914
  {
 
915
    /* Disable optional checkbox at the end of installation */
 
916
    MsiSetPropertyW(hInstall, L"WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT", L"");
 
917
    MsiSetPropertyW(hInstall, L"WIXUI_EXITDIALOGOPTIONALCHECKBOX",L"");
 
918
  }
 
919
  else
 
920
  {
 
921
    MsiSetPropertyW(hInstall, L"UpgradableServiceFound", L"1");
 
922
    MsiSetPropertyW(hInstall, L"WIXUI_EXITDIALOGOPTIONALCHECKBOX",L"1");
 
923
  }
 
924
LExit:
 
925
  if(scm)
 
926
    CloseServiceHandle(scm);
 
927
  return WcaFinalize(er); 
 
928
}
 
929
 
 
930
 
 
931
/* DllMain - Initialize and cleanup WiX custom action utils */
 
932
extern "C" BOOL WINAPI DllMain(
 
933
  __in HINSTANCE hInst,
 
934
  __in ULONG ulReason,
 
935
  __in LPVOID
 
936
  )
 
937
{
 
938
  switch(ulReason)
 
939
  {
 
940
  case DLL_PROCESS_ATTACH:
 
941
    WcaGlobalInitialize(hInst);
 
942
    break;
 
943
 
 
944
  case DLL_PROCESS_DETACH:
 
945
    WcaGlobalFinalize();
 
946
    break;
 
947
  }
 
948
 
 
949
  return TRUE;
 
950
}