2 Bacula® - The Network Backup Solution
4 Copyright (C) 2004-2010 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 // compat.cpp -- compatibilty layer to make bacula-fd run
30 // natively under windows
32 // Copyright transferred from Raider Solutions, Inc to
33 // Kern Sibbald and John Walker by express permission.
35 // Author : Christopher S. Hull
36 // Created On : Sat Jan 31 15:55:00 2004
42 #include "findlib/find.h"
44 static const int dbglvl = 500;
46 #define b_errno_win32 (1<<29)
48 #define MAX_PATHLENGTH 1024
51 UTF-8 to UCS2 path conversion is expensive,
52 so we cache the conversion. During backup the
53 conversion is called 3 times (lstat, attribs, open),
54 by using the cache this is reduced to 1 time
56 static POOLMEM *g_pWin32ConvUTF8Cache = NULL;
57 static POOLMEM *g_pWin32ConvUCS2Cache = NULL;
58 static DWORD g_dwWin32ConvUTF8strlen = 0;
59 static pthread_mutex_t Win32Convmutex = PTHREAD_MUTEX_INITIALIZER;
61 static t_pVSSPathConvert g_pVSSPathConvert;
62 static t_pVSSPathConvertW g_pVSSPathConvertW;
64 /* Forward referenced functions */
65 static const char *errorString(void);
68 void SetVSSPathConvert(t_pVSSPathConvert pPathConvert, t_pVSSPathConvertW pPathConvertW)
70 g_pVSSPathConvert = pPathConvert;
71 g_pVSSPathConvertW = pPathConvertW;
74 static void Win32ConvInitCache()
76 if (g_pWin32ConvUTF8Cache) {
79 g_pWin32ConvUTF8Cache = get_pool_memory(PM_FNAME);
80 g_pWin32ConvUCS2Cache = get_pool_memory(PM_FNAME);
83 void Win32ConvCleanupCache()
86 if (g_pWin32ConvUTF8Cache) {
87 free_pool_memory(g_pWin32ConvUTF8Cache);
88 g_pWin32ConvUTF8Cache = NULL;
91 if (g_pWin32ConvUCS2Cache) {
92 free_pool_memory(g_pWin32ConvUCS2Cache);
93 g_pWin32ConvUCS2Cache = NULL;
96 g_dwWin32ConvUTF8strlen = 0;
101 /* to allow the usage of the original version in this file here */
105 //#define USE_WIN32_COMPAT_IO 1
106 #define USE_WIN32_32KPATHCONVERSION 1
108 extern DWORD g_platform_id;
109 extern DWORD g_MinorVersion;
111 /* From Microsoft SDK (KES) is the diff between Jan 1 1601 and Jan 1 1970 */
113 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000ULL
115 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000I64
118 #define WIN32_FILETIME_SCALE 10000000 // 100ns/second
121 * Convert from UTF-8 to VSS Windows path/file
122 * Used by compatibility layer for Unix system calls
124 static void conv_unix_to_vss_win32_path(const char *name, char *win32_name, DWORD dwSize)
126 const char *fname = name;
127 char *tname = win32_name;
129 Dmsg0(dbglvl, "Enter convert_unix_to_win32_path\n");
131 if (IsPathSeparator(name[0]) &&
132 IsPathSeparator(name[1]) &&
134 IsPathSeparator(name[3])) {
136 *win32_name++ = '\\';
137 *win32_name++ = '\\';
139 *win32_name++ = '\\';
142 } else if (g_platform_id != VER_PLATFORM_WIN32_WINDOWS &&
143 g_pVSSPathConvert == NULL) {
144 /* allow path to be 32767 bytes */
145 *win32_name++ = '\\';
146 *win32_name++ = '\\';
148 *win32_name++ = '\\';
152 /** Check for Unix separator and convert to Win32 */
153 if (name[0] == '/' && name[1] == '/') { /* double slash? */
154 name++; /* yes, skip first one */
157 *win32_name++ = '\\'; /* convert char */
158 /* If Win32 separator that is "quoted", remove quote */
159 } else if (*name == '\\' && name[1] == '\\') {
160 *win32_name++ = '\\';
161 name++; /* skip first \ */
163 *win32_name++ = *name; /* copy character */
167 /** Strip any trailing slash, if we stored something
168 * but leave "c:\" with backslash (root directory case
170 if (*fname != 0 && win32_name[-1] == '\\' && strlen (fname) != 3) {
176 /** here we convert to VSS specific file name which
177 can get longer because VSS will make something like
178 \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
179 from c:\bacula\uninstall.exe
181 Dmsg1(dbglvl, "path=%s\n", tname);
182 if (g_pVSSPathConvert != NULL) {
183 POOLMEM *pszBuf = get_pool_memory (PM_FNAME);
184 pszBuf = check_pool_memory_size(pszBuf, dwSize);
185 bstrncpy(pszBuf, tname, strlen(tname)+1);
186 g_pVSSPathConvert(pszBuf, tname, dwSize);
187 free_pool_memory(pszBuf);
190 Dmsg1(dbglvl, "Leave cvt_u_to_win32_path path=%s\n", tname);
193 /** Conversion of a Unix filename to a Win32 filename */
194 void unix_name_to_win32(POOLMEM **win32_name, char *name)
196 /* One extra byte should suffice, but we double it */
197 /* add MAX_PATH bytes for VSS shadow copy name */
198 DWORD dwSize = 2*strlen(name)+MAX_PATH;
199 *win32_name = check_pool_memory_size(*win32_name, dwSize);
200 conv_unix_to_vss_win32_path(name, *win32_name, dwSize);
205 * This function expects an UCS-encoded standard wchar_t in pszUCSPath and
206 * will complete the input path to an absolue path of the form \\?\c:\path\file
208 * With this trick, it is possible to have 32K characters long paths.
210 * Optionally one can use pBIsRawPath to determine id pszUCSPath contains a path
211 * to a raw windows partition.
213 * created 02/27/2006 Thorsten Engel
216 make_wchar_win32_path(POOLMEM *pszUCSPath, BOOL *pBIsRawPath /*= NULL*/)
219 Dmsg0(dbglvl, "Enter wchar_win32_path\n");
221 *pBIsRawPath = FALSE; /* Initialize, set later */
224 if (!p_GetCurrentDirectoryW) {
225 Dmsg0(dbglvl, "Leave wchar_win32_path no change \n");
229 wchar_t *name = (wchar_t *)pszUCSPath;
231 /* if it has already the desired form, exit without changes */
232 if (wcslen(name) > 3 && wcsncmp(name, L"\\\\?\\", 4) == 0) {
233 Dmsg0(dbglvl, "Leave wchar_win32_path no change \n");
237 wchar_t *pwszBuf = (wchar_t *)get_pool_memory(PM_FNAME);
238 wchar_t *pwszCurDirBuf = (wchar_t *)get_pool_memory(PM_FNAME);
239 DWORD dwCurDirPathSize = 0;
241 /* get buffer with enough size (name+max 6. wchars+1 null terminator */
242 DWORD dwBufCharsNeeded = (wcslen(name)+7);
243 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
245 /* add \\?\ to support 32K long filepaths
246 it is important to make absolute paths, so we add drive and
247 current path if necessary */
249 BOOL bAddDrive = TRUE;
250 BOOL bAddCurrentPath = TRUE;
251 BOOL bAddPrefix = TRUE;
253 /* does path begin with drive? if yes, it is absolute */
254 if (iswalpha(name[0]) && name[1] == ':' && IsPathSeparator(name[2])) {
256 bAddCurrentPath = FALSE;
259 /* is path absolute? */
260 if (IsPathSeparator(name[0]))
261 bAddCurrentPath = FALSE;
263 /* is path relative to itself?, if yes, skip ./ */
264 if (name[0] == '.' && IsPathSeparator(name[1])) {
268 /* is path of form '//./'? */
269 if (IsPathSeparator(name[0]) &&
270 IsPathSeparator(name[1]) &&
272 IsPathSeparator(name[3])) {
274 bAddCurrentPath = FALSE;
281 int nParseOffset = 0;
283 /* add 4 bytes header */
286 wcscpy(pwszBuf, L"\\\\?\\");
289 /* get current path if needed */
290 if (bAddDrive || bAddCurrentPath) {
291 dwCurDirPathSize = p_GetCurrentDirectoryW(0, NULL);
292 if (dwCurDirPathSize > 0) {
293 /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
294 pwszCurDirBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszCurDirBuf, (dwCurDirPathSize+1)*sizeof(wchar_t));
295 p_GetCurrentDirectoryW(dwCurDirPathSize, pwszCurDirBuf);
297 /* we have no info for doing so */
299 bAddCurrentPath = FALSE;
304 /* add drive if needed */
305 if (bAddDrive && !bAddCurrentPath) {
308 if (IsPathSeparator(pwszCurDirBuf[0]) &&
309 IsPathSeparator(pwszCurDirBuf[1]) &&
310 pwszCurDirBuf[2] == '?' &&
311 IsPathSeparator(pwszCurDirBuf[3])) {
312 /* copy drive character */
313 szDrive[0] = pwszCurDirBuf[4];
315 /* copy drive character */
316 szDrive[0] = pwszCurDirBuf[0];
322 wcscat(pwszBuf, szDrive);
326 /* add path if needed */
327 if (bAddCurrentPath) {
328 /* the 1 add. character is for the eventually added backslash */
329 dwBufCharsNeeded += dwCurDirPathSize+1;
330 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
331 /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
333 if (IsPathSeparator(pwszCurDirBuf[0]) &&
334 IsPathSeparator(pwszCurDirBuf[1]) &&
335 pwszCurDirBuf[2] == '?' &&
336 IsPathSeparator(pwszCurDirBuf[3])) {
337 /* copy complete string */
338 wcscpy(pwszBuf, pwszCurDirBuf);
341 wcscat(pwszBuf, pwszCurDirBuf);
344 nParseOffset = wcslen((LPCWSTR) pwszBuf);
346 /* check if path ends with backslash, if not, add one */
347 if (!IsPathSeparator(pwszBuf[nParseOffset-1])) {
348 wcscat(pwszBuf, L"\\");
353 wchar_t *win32_name = &pwszBuf[nParseOffset];
354 wchar_t *name_start = name;
357 /* Check for Unix separator and convert to Win32, eliminating
358 * duplicate separators.
360 if (IsPathSeparator(*name)) {
361 *win32_name++ = '\\'; /* convert char */
363 /* Eliminate consecutive slashes, but not at the start so that
366 if (name_start != name && IsPathSeparator(name[1])) {
370 *win32_name++ = *name; /* copy character */
375 /* null terminate string */
378 /* here we convert to VSS specific file name which
379 * can get longer because VSS will make something like
380 * \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
381 * from c:\bacula\uninstall.exe
383 if (g_pVSSPathConvertW != NULL) {
384 /* is output buffer large enough? */
385 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf,
386 (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
387 /* create temp. buffer */
388 wchar_t *pszBuf = (wchar_t *)get_pool_memory(PM_FNAME);
389 pszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pszBuf,
390 (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
395 wcsncpy(pszBuf, &pwszBuf[nParseOffset], wcslen(pwszBuf)+1-nParseOffset);
396 g_pVSSPathConvertW(pszBuf, pwszBuf, dwBufCharsNeeded+MAX_PATH);
397 free_pool_memory((POOLMEM *)pszBuf);
400 free_pool_memory(pszUCSPath);
401 free_pool_memory((POOLMEM *)pwszCurDirBuf);
403 Dmsg1(dbglvl, "Leave wchar_win32_path=%s\n", pwszBuf);
404 return (POOLMEM *)pwszBuf;
408 wchar_2_UTF8(char *pszUTF, const wchar_t *pszUCS, int cchChar)
411 * The return value is the number of bytes written to the buffer.
412 * The number includes the byte for the null terminator.
415 if (p_WideCharToMultiByte) {
416 int nRet = p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,pszUTF,cchChar,NULL,NULL);
425 UTF8_2_wchar(POOLMEM **ppszUCS, const char *pszUTF)
427 /* the return value is the number of wide characters written to the buffer. */
428 /* convert null terminated string from utf-8 to ucs2, enlarge buffer if necessary */
430 if (p_MultiByteToWideChar) {
431 /* strlen of UTF8 +1 is enough */
432 DWORD cchSize = (strlen(pszUTF)+1);
433 *ppszUCS = check_pool_memory_size(*ppszUCS, cchSize*sizeof (wchar_t));
435 int nRet = p_MultiByteToWideChar(CP_UTF8, 0, pszUTF, -1, (LPWSTR) *ppszUCS,cchSize);
445 wchar_win32_path(const char *name, wchar_t *win32_name)
447 const char *fname = name;
449 /* Check for Unix separator and convert to Win32 */
451 *win32_name++ = '\\'; /* convert char */
452 /* If Win32 separated that is "quoted", remove quote */
453 } else if (*name == '\\' && name[1] == '\\') {
454 *win32_name++ = '\\';
455 name++; /* skip first \ */
457 *win32_name++ = *name; /* copy character */
461 /* Strip any trailing slash, if we stored something */
462 if (*fname != 0 && win32_name[-1] == '\\') {
470 make_win32_path_UTF8_2_wchar(POOLMEM **pszUCS, const char *pszUTF, BOOL* pBIsRawPath /*= NULL*/)
473 /* if we find the utf8 string in cache, we use the cached ucs2 version.
474 we compare the stringlength first (quick check) and then compare the content.
476 if (!g_pWin32ConvUTF8Cache) {
477 Win32ConvInitCache();
478 } else if (g_dwWin32ConvUTF8strlen == strlen(pszUTF)) {
479 if (bstrcmp(pszUTF, g_pWin32ConvUTF8Cache)) {
480 /* Return cached value */
481 int32_t nBufSize = sizeof_pool_memory(g_pWin32ConvUCS2Cache);
482 *pszUCS = check_pool_memory_size(*pszUCS, nBufSize);
483 wcscpy((LPWSTR) *pszUCS, (LPWSTR)g_pWin32ConvUCS2Cache);
485 return nBufSize / sizeof (WCHAR);
489 /* helper to convert from utf-8 to UCS-2 and to complete a path for 32K path syntax */
490 int nRet = UTF8_2_wchar(pszUCS, pszUTF);
492 #ifdef USE_WIN32_32KPATHCONVERSION
493 /* add \\?\ to support 32K long filepaths */
494 *pszUCS = make_wchar_win32_path(*pszUCS, pBIsRawPath);
497 *pBIsRawPath = FALSE;
501 g_pWin32ConvUCS2Cache = check_pool_memory_size(g_pWin32ConvUCS2Cache, sizeof_pool_memory(*pszUCS));
502 wcscpy((LPWSTR) g_pWin32ConvUCS2Cache, (LPWSTR) *pszUCS);
504 g_dwWin32ConvUTF8strlen = strlen(pszUTF);
505 g_pWin32ConvUTF8Cache = check_pool_memory_size(g_pWin32ConvUTF8Cache, g_dwWin32ConvUTF8strlen+2);
506 bstrncpy(g_pWin32ConvUTF8Cache, pszUTF, g_dwWin32ConvUTF8strlen+1);
512 #if !defined(_MSC_VER) || (_MSC_VER < 1400) // VC8+
519 #ifndef LOAD_WITH_ALTERED_SEARCH_PATH
520 #define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008
523 void *dlopen(const char *file, int mode)
527 handle = LoadLibraryEx(file, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
531 void *dlsym(void *handle, const char *name)
534 symaddr = (void *)GetProcAddress((HMODULE)handle, name);
538 int dlclose(void *handle)
540 if (handle && !FreeLibrary((HMODULE)handle)) {
541 errno = b_errno_win32;
542 return 1; /* failed */
549 static char buf[200];
550 const char *err = errorString();
551 bstrncpy(buf, (char *)err, sizeof(buf));
552 LocalFree((void *)err);
556 int fcntl(int fd, int cmd)
561 int chown(const char *k, uid_t, gid_t)
566 int lchown(const char *k, uid_t, gid_t)
578 srandom(unsigned int seed)
582 // /////////////////////////////////////////////////////////////////
583 // convert from Windows concept of time to Unix concept of time
584 // /////////////////////////////////////////////////////////////////
586 cvt_utime_to_ftime(const time_t &time, FILETIME &wintime)
588 uint64_t mstime = time;
589 mstime *= WIN32_FILETIME_SCALE;
590 mstime += WIN32_FILETIME_ADJUST;
592 #if defined(_MSC_VER)
593 wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffI64);
595 wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffUL);
597 wintime.dwHighDateTime = (DWORD) ((mstime>>32)& 0xffffffffUL);
601 cvt_ftime_to_utime(const FILETIME &time)
603 uint64_t mstime = time.dwHighDateTime;
605 mstime |= time.dwLowDateTime;
607 mstime -= WIN32_FILETIME_ADJUST;
608 mstime /= WIN32_FILETIME_SCALE; // convert to seconds.
610 return (time_t) (mstime & 0xffffffff);
613 static const char *errorString(void)
617 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
618 FORMAT_MESSAGE_FROM_SYSTEM |
619 FORMAT_MESSAGE_IGNORE_INSERTS,
622 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default lang
627 /* Strip any \r or \n */
628 char *rval = (char *) lpMsgBuf;
629 char *cp = strchr(rval, '\r');
633 cp = strchr(rval, '\n');
642 statDir(const char *file, struct stat *sb)
644 WIN32_FIND_DATAW info_w; // window's file info
645 WIN32_FIND_DATAA info_a; // window's file info
647 // cache some common vars to make code more transparent
648 DWORD *pdwFileAttributes;
649 DWORD *pnFileSizeHigh;
650 DWORD *pnFileSizeLow;
652 FILETIME *pftLastAccessTime;
653 FILETIME *pftLastWriteTime;
654 FILETIME *pftCreationTime;
657 * Oh, cool, another exception: Microsoft doesn't let us do
658 * FindFile operations on a Drive, so simply fake root attibutes.
660 if (file[1] == ':' && file[2] == 0) {
661 time_t now = time(NULL);
662 Dmsg1(dbglvl, "faking ROOT attrs(%s).\n", file);
663 sb->st_mode = S_IFDIR;
664 sb->st_mode |= S_IREAD|S_IEXEC|S_IWRITE;
672 HANDLE h = INVALID_HANDLE_VALUE;
675 if (p_FindFirstFileW) {
676 POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
677 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
679 Dmsg1(dbglvl, "FindFirstFileW=%s\n", file);
680 h = p_FindFirstFileW((LPCWSTR)pwszBuf, &info_w);
681 free_pool_memory(pwszBuf);
683 pdwFileAttributes = &info_w.dwFileAttributes;
684 pdwReserved0 = &info_w.dwReserved0;
685 pnFileSizeHigh = &info_w.nFileSizeHigh;
686 pnFileSizeLow = &info_w.nFileSizeLow;
687 pftLastAccessTime = &info_w.ftLastAccessTime;
688 pftLastWriteTime = &info_w.ftLastWriteTime;
689 pftCreationTime = &info_w.ftCreationTime;
692 } else if (p_FindFirstFileA) {
693 Dmsg1(dbglvl, "FindFirstFileA=%s\n", file);
694 h = p_FindFirstFileA(file, &info_a);
696 pdwFileAttributes = &info_a.dwFileAttributes;
697 pdwReserved0 = &info_a.dwReserved0;
698 pnFileSizeHigh = &info_a.nFileSizeHigh;
699 pnFileSizeLow = &info_a.nFileSizeLow;
700 pftLastAccessTime = &info_a.ftLastAccessTime;
701 pftLastWriteTime = &info_a.ftLastWriteTime;
702 pftCreationTime = &info_a.ftCreationTime;
704 Dmsg0(dbglvl, "No findFirstFile A or W found\n");
707 if (h == INVALID_HANDLE_VALUE) {
708 const char *err = errorString();
710 * Note, in creating leading paths, it is normal that
711 * the file does not exist.
713 Dmsg2(2099, "FindFirstFile(%s):%s\n", file, err);
714 LocalFree((void *)err);
715 errno = b_errno_win32;
721 sb->st_mode = 0777; /* start with everything */
722 if (*pdwFileAttributes & FILE_ATTRIBUTE_READONLY)
723 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
724 if (*pdwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
725 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
726 if (*pdwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
727 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
728 sb->st_mode |= S_IFDIR;
731 * Store reparse/mount point info in st_rdev. Note a
732 * Win32 reparse point (junction point) is like a link
733 * though it can have many properties (directory link,
734 * soft link, hard link, HSM, ...
735 * A mount point is a reparse point where another volume
736 * is mounted, so it is like a Unix mount point (change of
739 if (*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
740 if (*pdwReserved0 & IO_REPARSE_TAG_MOUNT_POINT) {
741 sb->st_rdev = WIN32_MOUNT_POINT; /* mount point */
743 sb->st_rdev = WIN32_REPARSE_POINT; /* reparse point */
746 Dmsg2(dbglvl, "st_rdev=%d file=%s\n", sb->st_rdev, file);
747 sb->st_size = *pnFileSizeHigh;
749 sb->st_size |= *pnFileSizeLow;
750 sb->st_blksize = 4096;
751 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
753 sb->st_atime = cvt_ftime_to_utime(*pftLastAccessTime);
754 sb->st_mtime = cvt_ftime_to_utime(*pftLastWriteTime);
755 sb->st_ctime = cvt_ftime_to_utime(*pftCreationTime);
761 fstat(intptr_t fd, struct stat *sb)
763 BY_HANDLE_FILE_INFORMATION info;
765 if (!GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
766 const char *err = errorString();
767 Dmsg1(2099, "GetfileInformationByHandle: %s\n", err);
768 LocalFree((void *)err);
769 errno = b_errno_win32;
773 sb->st_dev = info.dwVolumeSerialNumber;
774 sb->st_ino = info.nFileIndexHigh;
776 sb->st_ino |= info.nFileIndexLow;
777 sb->st_nlink = (short)info.nNumberOfLinks;
778 if (sb->st_nlink > 1) {
779 Dmsg1(dbglvl, "st_nlink=%d\n", sb->st_nlink);
782 sb->st_mode = 0777; /* start with everything */
783 if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
784 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
785 if (info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
786 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
787 if (info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
788 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
789 sb->st_mode |= S_IFREG;
791 /* Use st_rdev to store reparse attribute */
792 if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
793 sb->st_rdev = WIN32_REPARSE_POINT;
795 Dmsg3(dbglvl, "st_rdev=%d sizino=%d ino=%lld\n", sb->st_rdev, sizeof(sb->st_ino),
796 (long long)sb->st_ino);
798 sb->st_size = info.nFileSizeHigh;
800 sb->st_size |= info.nFileSizeLow;
801 sb->st_blksize = 4096;
802 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
803 sb->st_atime = cvt_ftime_to_utime(info.ftLastAccessTime);
804 sb->st_mtime = cvt_ftime_to_utime(info.ftLastWriteTime);
805 sb->st_ctime = cvt_ftime_to_utime(info.ftCreationTime);
811 stat2(const char *file, struct stat *sb)
813 HANDLE h = INVALID_HANDLE_VALUE;
816 conv_unix_to_vss_win32_path(file, tmpbuf, 5000);
818 DWORD attr = (DWORD)-1;
820 if (p_GetFileAttributesW) {
821 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
822 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
824 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
826 h = CreateFileW((LPCWSTR)pwszBuf, GENERIC_READ,
827 FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
829 free_pool_memory(pwszBuf);
830 } else if (p_GetFileAttributesA) {
831 attr = p_GetFileAttributesA(tmpbuf);
832 h = CreateFileA(tmpbuf, GENERIC_READ,
833 FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
836 if (attr == (DWORD)-1) {
837 const char *err = errorString();
838 Dmsg2(2099, "GetFileAttributes(%s): %s\n", tmpbuf, err);
839 LocalFree((void *)err);
840 if (h != INVALID_HANDLE_VALUE) {
843 errno = b_errno_win32;
847 if (h == INVALID_HANDLE_VALUE) {
848 const char *err = errorString();
849 Dmsg2(2099, "Cannot open file for stat (%s):%s\n", tmpbuf, err);
850 LocalFree((void *)err);
851 errno = b_errno_win32;
855 rval = fstat((intptr_t)h, sb);
858 if (attr & FILE_ATTRIBUTE_DIRECTORY &&
859 file[1] == ':' && file[2] != 0) {
860 rval = statDir(file, sb);
866 stat(const char *file, struct stat *sb)
868 WIN32_FILE_ATTRIBUTE_DATA data;
871 memset(sb, 0, sizeof(*sb));
873 if (p_GetFileAttributesExW) {
874 /* dynamically allocate enough space for UCS2 filename */
875 POOLMEM *pwszBuf = get_pool_memory(PM_FNAME);
876 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
878 BOOL b = p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard, &data);
879 free_pool_memory(pwszBuf);
882 return stat2(file, sb);
885 } else if (p_GetFileAttributesExA) {
886 if (!p_GetFileAttributesExA(file, GetFileExInfoStandard, &data)) {
887 return stat2(file, sb);
890 return stat2(file, sb);
893 sb->st_mode = 0777; /* start with everything */
894 if (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
895 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
897 if (data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
898 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
900 if (data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
901 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
903 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
904 sb->st_mode |= S_IFDIR;
906 sb->st_mode |= S_IFREG;
909 /* Use st_rdev to store reparse attribute */
910 sb->st_rdev = (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? 1 : 0;
913 sb->st_size = data.nFileSizeHigh;
915 sb->st_size |= data.nFileSizeLow;
916 sb->st_blksize = 4096;
917 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
918 sb->st_atime = cvt_ftime_to_utime(data.ftLastAccessTime);
919 sb->st_mtime = cvt_ftime_to_utime(data.ftLastWriteTime);
920 sb->st_ctime = cvt_ftime_to_utime(data.ftCreationTime);
923 * If we are not at the root, then to distinguish a reparse
924 * point from a mount point, we must call FindFirstFile() to
925 * get the WIN32_FIND_DATA, which has the bit that indicates
926 * that this directory is a mount point -- aren't Win32 APIs
927 * wonderful? (sarcasm). The code exists in the statDir
930 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
931 file[1] == ':' && file[2] != 0) {
934 Dmsg3(dbglvl, "sizino=%d ino=%lld file=%s\n", sizeof(sb->st_ino),
935 (long long)sb->st_ino, file);
940 * We write our own ftruncate because the one in the
941 * Microsoft library mrcrt.dll does not truncate
942 * files greater than 2GB.
945 int win32_ftruncate(int fd, int64_t length)
947 /* Set point we want to truncate file */
948 __int64 pos = _lseeki64(fd, (__int64)length, SEEK_SET);
950 if (pos != (__int64)length) {
951 errno = EACCES; /* truncation failed, get out */
956 if (SetEndOfFile((HANDLE)_get_osfhandle(fd)) == 0) {
957 errno = b_errno_win32;
964 int fcntl(int fd, int cmd, long arg)
987 lstat(const char *file, struct stat *sb)
989 return stat(file, sb);
1005 execvp(const char *, char *[]) {
1026 waitpid(int, int*, int)
1033 readlink(const char *, char *, int)
1042 strcasecmp(const char *s1, const char *s2)
1044 register int ch1, ch2;
1047 return 0; /* strings are equal if same object. */
1057 } while (ch1 != 0 && tolower(ch1) == tolower(ch2));
1064 strncasecmp(const char *s1, const char *s2, int len)
1066 register int ch1 = 0, ch2 = 0;
1069 return 0; /* strings are equal if same object. */
1080 if (ch1 == 0 || tolower(ch1) != tolower(ch2)) break;
1087 gettimeofday(struct timeval *tv, struct timezone *)
1092 GetSystemTime(&now);
1098 if (!SystemTimeToFileTime(&now, &tmp)) {
1099 errno = b_errno_win32;
1103 int64_t _100nsec = tmp.dwHighDateTime;
1105 _100nsec |= tmp.dwLowDateTime;
1106 _100nsec -= WIN32_FILETIME_ADJUST;
1108 tv->tv_sec = (long)(_100nsec / 10000000);
1109 tv->tv_usec = (long)((_100nsec % 10000000)/10);
1115 * Write in Windows System log
1117 extern "C" void syslog(int type, const char *fmt, ...)
1123 msg = get_pool_memory(PM_EMSG);
1126 maxlen = sizeof_pool_memory(msg) - 1;
1127 va_start(arg_ptr, fmt);
1128 len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
1130 if (len < 0 || len >= (maxlen-5)) {
1131 msg = realloc_pool_memory(msg, maxlen + maxlen/2);
1136 LogErrorMsg((const char *)msg);
1157 // implement opendir/readdir/closedir on top of window's API
1161 WIN32_FIND_DATAA data_a; // window's file info (ansii version)
1162 WIN32_FIND_DATAW data_w; // window's file info (wchar version)
1163 const char *spec; // the directory we're traversing
1164 HANDLE dirh; // the search handle
1165 BOOL valid_a; // the info in data_a field is valid
1166 BOOL valid_w; // the info in data_w field is valid
1167 UINT32 offset; // pseudo offset for d_off
1171 opendir(const char *path)
1173 /* enough space for VSS !*/
1174 int max_len = strlen(path) + MAX_PATH;
1182 Dmsg1(dbglvl, "Opendir path=%s\n", path);
1183 rval = (_dir *)malloc(sizeof(_dir));
1187 memset (rval, 0, sizeof (_dir));
1189 tspec = (char *)malloc(max_len);
1194 conv_unix_to_vss_win32_path(path, tspec, max_len);
1195 Dmsg1(dbglvl, "win32 path=%s\n", tspec);
1197 // add backslash only if there is none yet (think of c:\)
1198 if (tspec[strlen(tspec)-1] != '\\')
1199 bstrncat(tspec, "\\*", max_len);
1201 bstrncat(tspec, "*", max_len);
1205 // convert to wchar_t
1206 if (p_FindFirstFileW) {
1207 POOLMEM* pwcBuf = get_pool_memory(PM_FNAME);;
1208 make_win32_path_UTF8_2_wchar(&pwcBuf, rval->spec);
1210 rval->dirh = p_FindFirstFileW((LPCWSTR)pwcBuf, &rval->data_w);
1212 free_pool_memory(pwcBuf);
1214 if (rval->dirh != INVALID_HANDLE_VALUE)
1216 } else if (p_FindFirstFileA) {
1217 rval->dirh = p_FindFirstFileA(rval->spec, &rval->data_a);
1219 if (rval->dirh != INVALID_HANDLE_VALUE)
1224 Dmsg3(dbglvl, "opendir(%s)\n\tspec=%s,\n\tFindFirstFile returns %d\n",
1225 path, rval->spec, rval->dirh);
1228 if (rval->dirh == INVALID_HANDLE_VALUE)
1231 if (rval->valid_w) {
1232 Dmsg1(dbglvl, "\tFirstFile=%s\n", rval->data_w.cFileName);
1235 if (rval->valid_a) {
1236 Dmsg1(dbglvl, "\tFirstFile=%s\n", rval->data_a.cFileName);
1248 errno = b_errno_win32;
1255 _dir *dp = (_dir *)dirp;
1256 FindClose(dp->dirh);
1257 free((void *)dp->spec);
1263 typedef struct _WIN32_FIND_DATA {
1264 DWORD dwFileAttributes;
1265 FILETIME ftCreationTime;
1266 FILETIME ftLastAccessTime;
1267 FILETIME ftLastWriteTime;
1268 DWORD nFileSizeHigh;
1272 TCHAR cFileName[MAX_PATH];
1273 TCHAR cAlternateFileName[14];
1274 } WIN32_FIND_DATA, *PWIN32_FIND_DATA;
1278 copyin(struct dirent &dp, const char *fname)
1282 char *cp = dp.d_name;
1292 readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
1294 _dir *dp = (_dir *)dirp;
1295 if (dp->valid_w || dp->valid_a) {
1296 entry->d_off = dp->offset;
1300 char szBuf[MAX_PATH_UTF8+1];
1301 wchar_2_UTF8(szBuf,dp->data_w.cFileName);
1302 dp->offset += copyin(*entry, szBuf);
1303 } else if (dp->valid_a) { // copy ansi (only 1 will be valid)
1304 dp->offset += copyin(*entry, dp->data_a.cFileName);
1307 *result = entry; /* return entry address */
1308 Dmsg4(dbglvl, "readdir_r(%p, { d_name=\"%s\", d_reclen=%d, d_off=%d\n",
1309 dirp, entry->d_name, entry->d_reclen, entry->d_off);
1311 // Dmsg0(dbglvl, "readdir_r !valid\n");
1312 errno = b_errno_win32;
1316 // get next file, try unicode first
1317 if (p_FindNextFileW)
1318 dp->valid_w = p_FindNextFileW(dp->dirh, &dp->data_w);
1319 else if (p_FindNextFileA)
1320 dp->valid_a = p_FindNextFileA(dp->dirh, &dp->data_a);
1322 dp->valid_a = FALSE;
1323 dp->valid_w = FALSE;
1330 * Dotted IP address to network address
1336 inet_aton(const char *a, struct in_addr *inp)
1339 uint32_t acc = 0, tmp = 0;
1342 if (!isdigit(*cp)) { /* first char must be digit */
1343 return 0; /* error */
1347 tmp = (tmp * 10) + (*cp -'0');
1348 } else if (*cp == '.' || *cp == 0) {
1350 return 0; /* error */
1352 acc = (acc << 8) + tmp;
1356 return 0; /* error */
1358 } while (*cp++ != 0);
1359 if (dotc != 4) { /* want 3 .'s plus EOS */
1360 return 0; /* error */
1362 inp->s_addr = htonl(acc); /* store addr in network format */
1367 nanosleep(const struct timespec *req, struct timespec *rem)
1370 rem->tv_sec = rem->tv_nsec = 0;
1371 Sleep((req->tv_sec * 1000) + (req->tv_nsec/100000));
1376 init_signals(void terminate(int sig))
1382 init_stack_dump(void)
1389 pathconf(const char *path, int name)
1393 if (strncmp(path, "\\\\?\\", 4) == 0)
1405 WORD wVersionRequested = MAKEWORD( 1, 1);
1408 int err = WSAStartup(wVersionRequested, &wsaData);
1412 printf("Can not start Windows Sockets\n");
1420 static DWORD fill_attribute(DWORD attr, mode_t mode)
1422 Dmsg1(dbglvl, " before attr=%lld\n", (uint64_t) attr);
1423 /* Use Bacula mappings define in stat() above */
1424 if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) { // If file is readable
1425 attr &= ~FILE_ATTRIBUTE_READONLY; // then this is not READONLY
1427 attr |= FILE_ATTRIBUTE_READONLY;
1429 if (mode & S_ISVTX) { // The sticky bit <=> HIDDEN
1430 attr |= FILE_ATTRIBUTE_HIDDEN;
1432 attr &= ~FILE_ATTRIBUTE_HIDDEN;
1434 if (mode & S_IRWXO) { // Other can read/write/execute ?
1435 attr &= ~FILE_ATTRIBUTE_SYSTEM; // => Not system
1437 attr |= FILE_ATTRIBUTE_SYSTEM;
1439 Dmsg1(dbglvl, " after attr=%lld\n", (uint64_t)attr);
1443 int win32_chmod(const char *path, mode_t mode)
1448 Dmsg2(dbglvl, "win32_chmod(path=%s mode=%lld)\n", path, (uint64_t)mode);
1449 if (p_GetFileAttributesW) {
1450 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1451 make_win32_path_UTF8_2_wchar(&pwszBuf, path);
1453 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
1454 if (attr != INVALID_FILE_ATTRIBUTES) {
1455 /* Use Bacula mappings define in stat() above */
1456 attr = fill_attribute(attr, mode);
1457 ret = p_SetFileAttributesW((LPCWSTR)pwszBuf, attr);
1459 free_pool_memory(pwszBuf);
1460 Dmsg0(dbglvl, "Leave win32_chmod. AttributesW\n");
1461 } else if (p_GetFileAttributesA) {
1462 attr = p_GetFileAttributesA(path);
1463 if (attr != INVALID_FILE_ATTRIBUTES) {
1464 attr = fill_attribute(attr, mode);
1465 ret = p_SetFileAttributesA(path, attr);
1467 Dmsg0(dbglvl, "Leave win32_chmod did AttributesA\n");
1469 Dmsg0(dbglvl, "Leave win32_chmod did nothing\n");
1473 const char *err = errorString();
1474 Dmsg2(dbglvl, "Get/SetFileAttributes(%s): %s\n", path, err);
1475 LocalFree((void *)err);
1476 errno = b_errno_win32;
1484 win32_chdir(const char *dir)
1486 if (p_SetCurrentDirectoryW) {
1487 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1488 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1490 BOOL b=p_SetCurrentDirectoryW((LPCWSTR)pwszBuf);
1492 free_pool_memory(pwszBuf);
1495 errno = b_errno_win32;
1498 } else if (p_SetCurrentDirectoryA) {
1499 if (0 == p_SetCurrentDirectoryA(dir)) {
1500 errno = b_errno_win32;
1511 win32_mkdir(const char *dir)
1513 Dmsg1(dbglvl, "enter win32_mkdir. dir=%s\n", dir);
1515 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1516 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1518 int n = p_wmkdir((LPCWSTR)pwszBuf);
1519 free_pool_memory(pwszBuf);
1520 Dmsg0(dbglvl, "Leave win32_mkdir did wmkdir\n");
1524 Dmsg0(dbglvl, "Leave win32_mkdir did _mkdir\n");
1530 win32_getcwd(char *buf, int maxlen)
1534 if (p_GetCurrentDirectoryW) {
1535 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1536 pwszBuf = check_pool_memory_size (pwszBuf, maxlen*sizeof(wchar_t));
1538 n = p_GetCurrentDirectoryW(maxlen, (LPWSTR) pwszBuf);
1540 n = wchar_2_UTF8 (buf, (wchar_t *)pwszBuf, maxlen)-1;
1541 free_pool_memory(pwszBuf);
1543 } else if (p_GetCurrentDirectoryA)
1544 n = p_GetCurrentDirectoryA(maxlen, buf);
1546 if (n == 0 || n > maxlen) return NULL;
1548 if (n+1 > maxlen) return NULL;
1557 win32_fputs(const char *string, FILE *stream)
1559 /* we use WriteConsoleA / WriteConsoleA
1560 so we can be sure that unicode support works on win32.
1561 with fallback if something fails
1564 HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
1565 if (hOut && (hOut != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte &&
1566 p_MultiByteToWideChar && (stream == stdout)) {
1568 POOLMEM* pwszBuf = get_pool_memory(PM_MESSAGE);
1570 DWORD dwCharsWritten;
1573 dwChars = UTF8_2_wchar(&pwszBuf, string);
1575 /* try WriteConsoleW */
1576 if (WriteConsoleW (hOut, pwszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1577 free_pool_memory(pwszBuf);
1578 return dwCharsWritten;
1581 /* convert to local codepage and try WriteConsoleA */
1582 POOLMEM* pszBuf = get_pool_memory(PM_MESSAGE);
1583 pszBuf = check_pool_memory_size(pszBuf, dwChars+1);
1585 dwChars = p_WideCharToMultiByte(GetConsoleOutputCP(),0,(LPCWSTR)pwszBuf,-1,pszBuf,dwChars,NULL,NULL);
1586 free_pool_memory(pwszBuf);
1588 if (WriteConsoleA (hOut, pszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1589 free_pool_memory(pszBuf);
1590 return dwCharsWritten;
1592 free_pool_memory(pszBuf);
1595 return fputs(string, stream);
1599 win32_cgets (char* buffer, int len)
1601 /* we use console ReadConsoleA / ReadConsoleW to be able to read unicode
1602 from the win32 console and fallback if seomething fails */
1604 HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
1605 if (hIn && (hIn != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte && p_MultiByteToWideChar) {
1607 wchar_t wszBuf[1024];
1610 /* nt and unicode conversion */
1611 if (ReadConsoleW (hIn, wszBuf, 1024, &dwRead, NULL)) {
1613 /* null terminate at end */
1614 if (wszBuf[dwRead-1] == L'\n') {
1615 wszBuf[dwRead-1] = L'\0';
1619 if (wszBuf[dwRead-1] == L'\r') {
1620 wszBuf[dwRead-1] = L'\0';
1624 wchar_2_UTF8(buffer, wszBuf, len);
1628 /* win 9x and unicode conversion */
1629 if (ReadConsoleA (hIn, szBuf, 1024, &dwRead, NULL)) {
1631 /* null terminate at end */
1632 if (szBuf[dwRead-1] == L'\n') {
1633 szBuf[dwRead-1] = L'\0';
1637 if (szBuf[dwRead-1] == L'\r') {
1638 szBuf[dwRead-1] = L'\0';
1642 /* convert from ansii to wchar_t */
1643 p_MultiByteToWideChar(GetConsoleCP(), 0, szBuf, -1, wszBuf,1024);
1644 /* convert from wchar_t to UTF-8 */
1645 if (wchar_2_UTF8(buffer, wszBuf, len))
1651 if (fgets(buffer, len, stdin))
1658 win32_unlink(const char *filename)
1662 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1663 make_win32_path_UTF8_2_wchar(&pwszBuf, filename);
1665 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1668 * special case if file is readonly,
1669 * we retry but unset attribute before
1671 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesW && p_GetFileAttributesW) {
1672 DWORD dwAttr = p_GetFileAttributesW((LPCWSTR)pwszBuf);
1673 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1674 if (p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1675 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1676 /* reset to original if it didn't help */
1678 p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr);
1682 free_pool_memory(pwszBuf);
1684 nRetCode = _unlink(filename);
1686 /* special case if file is readonly,
1687 we retry but unset attribute before */
1688 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesA && p_GetFileAttributesA) {
1689 DWORD dwAttr = p_GetFileAttributesA(filename);
1690 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1691 if (p_SetFileAttributesA(filename, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1692 nRetCode = _unlink(filename);
1693 /* reset to original if it didn't help */
1695 p_SetFileAttributesA(filename, dwAttr);
1704 #include "mswinver.h"
1706 char WIN_VERSION_LONG[64];
1707 char WIN_VERSION[32];
1708 char WIN_RAWVERSION[32];
1715 static winver INIT; // cause constructor to be called before main()
1718 winver::winver(void)
1720 const char *version = "";
1721 const char *platform = "";
1722 OSVERSIONINFO osvinfo;
1723 osvinfo.dwOSVersionInfoSize = sizeof(osvinfo);
1725 // Get the current OS version
1726 if (!GetVersionEx(&osvinfo)) {
1727 version = "Unknown";
1728 platform = "Unknown";
1730 const int ver = _mkversion(osvinfo.dwPlatformId,
1731 osvinfo.dwMajorVersion,
1732 osvinfo.dwMinorVersion);
1733 snprintf(WIN_RAWVERSION, sizeof(WIN_RAWVERSION), "Windows %#08x", ver);
1736 case MS_WINDOWS_95: (version = "Windows 95"); break;
1737 case MS_WINDOWS_98: (version = "Windows 98"); break;
1738 case MS_WINDOWS_ME: (version = "Windows ME"); break;
1739 case MS_WINDOWS_NT4:(version = "Windows NT 4.0"); platform = "NT"; break;
1740 case MS_WINDOWS_2K: (version = "Windows 2000");platform = "NT"; break;
1741 case MS_WINDOWS_XP: (version = "Windows XP");platform = "NT"; break;
1742 case MS_WINDOWS_S2003: (version = "Windows Server 2003");platform = "NT"; break;
1743 default: version = WIN_RAWVERSION; break;
1746 bstrncpy(WIN_VERSION_LONG, version, sizeof(WIN_VERSION_LONG));
1747 snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %lu.%lu.%lu",
1748 platform, osvinfo.dwMajorVersion, osvinfo.dwMinorVersion, osvinfo.dwBuildNumber);
1751 HANDLE h = CreateFile("G:\\foobar", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
1755 BPIPE *b = open_bpipe("ls -l", 10, "r");
1757 while (!feof(b->rfd)) {
1758 fgets(buf, sizeof(buf), b->rfd);
1764 BOOL CreateChildProcess(VOID);
1765 VOID WriteToPipe(VOID);
1766 VOID ReadFromPipe(VOID);
1767 VOID ErrorExit(LPCSTR);
1768 VOID ErrMsg(LPTSTR, BOOL);
1771 * Check for a quoted path, if an absolute path name is given and it contains
1772 * spaces it will need to be quoted. i.e. "c:/Program Files/foo/bar.exe"
1773 * CreateProcess() says the best way to ensure proper results with executables
1774 * with spaces in path or filename is to quote the string.
1777 getArgv0(const char *cmdline)
1782 for (cp = cmdline; *cp; cp++)
1787 if (!inquote && isspace(*cp))
1792 int len = cp - cmdline;
1793 char *rval = (char *)malloc(len+1);
1806 * Extracts the executable or script name from the first string in
1809 * If the name contains blanks then it must be quoted with double quotes,
1810 * otherwise quotes are optional. If the name contains blanks then it
1811 * will be converted to a short name.
1813 * The optional quotes will be removed. The result is copied to a malloc'ed
1814 * buffer and returned through the pexe argument. The pargs parameter is set
1815 * to the address of the character in cmdline located after the name.
1817 * The malloc'ed buffer returned in *pexe must be freed by the caller.
1820 GetApplicationName(const char *cmdline, char **pexe, const char **pargs)
1822 const char *pExeStart = NULL; /* Start of executable name in cmdline */
1823 const char *pExeEnd = NULL; /* Character after executable name (separator) */
1825 const char *pBasename = NULL; /* Character after last path separator */
1826 const char *pExtension = NULL; /* Period at start of extension */
1828 const char *current = cmdline;
1830 bool bQuoted = false;
1832 /* Skip initial whitespace */
1834 while (*current == ' ' || *current == '\t')
1839 /* Calculate start of name and determine if quoted */
1841 if (*current == '"') {
1842 pExeStart = ++current;
1845 pExeStart = current;
1853 * Scan command line looking for path separators (/ and \\) and the
1854 * terminator, either a quote or a blank. The location of the
1855 * extension is also noted.
1858 for ( ; *current != '\0'; current++)
1860 if (*current == '.') {
1861 pExtension = current;
1862 } else if (IsPathSeparator(*current) && current[1] != '\0') {
1863 pBasename = ¤t[1];
1867 /* Check for terminator, either quote or blank */
1869 if (*current != '"') {
1873 if (*current != ' ') {
1879 * Hit terminator, remember end of name (address of terminator) and
1880 * start of arguments
1884 if (bQuoted && *current == '"') {
1885 *pargs = ¤t[1];
1893 if (pBasename == NULL) {
1894 pBasename = pExeStart;
1897 if (pExeEnd == NULL) {
1906 bool bHasPathSeparators = pExeStart != pBasename;
1908 /* We have pointers to all the useful parts of the name */
1910 /* Default extensions in the order cmd.exe uses to search */
1912 static const char ExtensionList[][5] = { ".com", ".exe", ".bat", ".cmd" };
1913 DWORD dwBasePathLength = pExeEnd - pExeStart;
1915 DWORD dwAltNameLength = 0;
1916 char *pPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1917 char *pAltPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1919 pPathname[MAX_PATHLENGTH] = '\0';
1920 pAltPathname[MAX_PATHLENGTH] = '\0';
1922 memcpy(pPathname, pExeStart, dwBasePathLength);
1923 pPathname[dwBasePathLength] = '\0';
1925 if (pExtension == NULL) {
1926 /* Try appending extensions */
1927 for (int index = 0; index < (int)(sizeof(ExtensionList) / sizeof(ExtensionList[0])); index++) {
1929 if (!bHasPathSeparators) {
1930 /* There are no path separators, search in the standard locations */
1931 dwAltNameLength = SearchPath(NULL, pPathname, ExtensionList[index], MAX_PATHLENGTH, pAltPathname, NULL);
1932 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1933 memcpy(pPathname, pAltPathname, dwAltNameLength);
1934 pPathname[dwAltNameLength] = '\0';
1938 bstrncpy(&pPathname[dwBasePathLength], ExtensionList[index], MAX_PATHLENGTH - dwBasePathLength);
1939 if (GetFileAttributes(pPathname) != INVALID_FILE_ATTRIBUTES) {
1942 pPathname[dwBasePathLength] = '\0';
1945 } else if (!bHasPathSeparators) {
1946 /* There are no path separators, search in the standard locations */
1947 dwAltNameLength = SearchPath(NULL, pPathname, NULL, MAX_PATHLENGTH, pAltPathname, NULL);
1948 if (dwAltNameLength > 0 && dwAltNameLength < MAX_PATHLENGTH) {
1949 memcpy(pPathname, pAltPathname, dwAltNameLength);
1950 pPathname[dwAltNameLength] = '\0';
1954 if (strchr(pPathname, ' ') != NULL) {
1955 dwAltNameLength = GetShortPathName(pPathname, pAltPathname, MAX_PATHLENGTH);
1957 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1958 *pexe = (char *)malloc(dwAltNameLength + 1);
1959 if (*pexe == NULL) {
1962 memcpy(*pexe, pAltPathname, dwAltNameLength + 1);
1966 if (*pexe == NULL) {
1967 DWORD dwPathnameLength = strlen(pPathname);
1968 *pexe = (char *)malloc(dwPathnameLength + 1);
1969 if (*pexe == NULL) {
1972 memcpy(*pexe, pPathname, dwPathnameLength + 1);
1979 * Create the process with WCHAR API
1982 CreateChildProcessW(const char *comspec, const char *cmdLine,
1983 PROCESS_INFORMATION *hProcInfo,
1984 HANDLE in, HANDLE out, HANDLE err)
1986 STARTUPINFOW siStartInfo;
1987 BOOL bFuncRetn = FALSE;
1989 // Set up members of the STARTUPINFO structure.
1990 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
1991 siStartInfo.cb = sizeof(siStartInfo);
1992 // setup new process to use supplied handles for stdin,stdout,stderr
1994 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1995 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
1997 siStartInfo.hStdInput = in;
1998 siStartInfo.hStdOutput = out;
1999 siStartInfo.hStdError = err;
2001 // Convert argument to WCHAR
2002 POOLMEM *cmdLine_wchar = get_pool_memory(PM_FNAME);
2003 POOLMEM *comspec_wchar = get_pool_memory(PM_FNAME);
2005 UTF8_2_wchar(&cmdLine_wchar, cmdLine);
2006 UTF8_2_wchar(&comspec_wchar, comspec);
2008 // Create the child process.
2009 Dmsg2(dbglvl, "Calling CreateProcess(%s, %s, ...)\n", comspec_wchar, cmdLine_wchar);
2011 // try to execute program
2012 bFuncRetn = p_CreateProcessW((WCHAR*)comspec_wchar,
2013 (WCHAR*)cmdLine_wchar,// command line
2014 NULL, // process security attributes
2015 NULL, // primary thread security attributes
2016 TRUE, // handles are inherited
2017 0, // creation flags
2018 NULL, // use parent's environment
2019 NULL, // use parent's current directory
2020 &siStartInfo, // STARTUPINFO pointer
2021 hProcInfo); // receives PROCESS_INFORMATION
2022 free_pool_memory(cmdLine_wchar);
2023 free_pool_memory(comspec_wchar);
2030 * Create the process with ANSI API
2033 CreateChildProcessA(const char *comspec, char *cmdLine,
2034 PROCESS_INFORMATION *hProcInfo,
2035 HANDLE in, HANDLE out, HANDLE err)
2037 STARTUPINFOA siStartInfo;
2038 BOOL bFuncRetn = FALSE;
2040 // Set up members of the STARTUPINFO structure.
2041 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
2042 siStartInfo.cb = sizeof(siStartInfo);
2043 // setup new process to use supplied handles for stdin,stdout,stderr
2044 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
2045 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
2047 siStartInfo.hStdInput = in;
2048 siStartInfo.hStdOutput = out;
2049 siStartInfo.hStdError = err;
2051 // Create the child process.
2052 Dmsg2(dbglvl, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
2054 // try to execute program
2055 bFuncRetn = p_CreateProcessA(comspec,
2056 cmdLine, // command line
2057 NULL, // process security attributes
2058 NULL, // primary thread security attributes
2059 TRUE, // handles are inherited
2060 0, // creation flags
2061 NULL, // use parent's environment
2062 NULL, // use parent's current directory
2063 &siStartInfo,// STARTUPINFO pointer
2064 hProcInfo);// receives PROCESS_INFORMATION
2069 * OK, so it would seem CreateProcess only handles true executables:
2070 * .com or .exe files. So grab $COMSPEC value and pass command line to it.
2073 CreateChildProcess(const char *cmdline, HANDLE in, HANDLE out, HANDLE err)
2075 static const char *comspec = NULL;
2076 PROCESS_INFORMATION piProcInfo;
2077 BOOL bFuncRetn = FALSE;
2079 if (!p_CreateProcessA || !p_CreateProcessW)
2080 return INVALID_HANDLE_VALUE;
2082 if (comspec == NULL)
2083 comspec = getenv("COMSPEC");
2084 if (comspec == NULL) // should never happen
2085 return INVALID_HANDLE_VALUE;
2087 // Set up members of the PROCESS_INFORMATION structure.
2088 ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
2090 // if supplied handles are not used the send a copy of our STD_HANDLE
2092 if (in == INVALID_HANDLE_VALUE)
2093 in = GetStdHandle(STD_INPUT_HANDLE);
2095 if (out == INVALID_HANDLE_VALUE)
2096 out = GetStdHandle(STD_OUTPUT_HANDLE);
2098 if (err == INVALID_HANDLE_VALUE)
2099 err = GetStdHandle(STD_ERROR_HANDLE);
2102 const char *argStart;
2104 if (!GetApplicationName(cmdline, &exeFile, &argStart)) {
2105 return INVALID_HANDLE_VALUE;
2108 POOL_MEM cmdLine(PM_FNAME);
2109 Mmsg(cmdLine, "%s /c %s%s", comspec, exeFile, argStart);
2113 // New function disabled
2114 if (p_CreateProcessW && p_MultiByteToWideChar) {
2115 bFuncRetn = CreateChildProcessW(comspec, cmdLine.c_str(), &piProcInfo,
2118 bFuncRetn = CreateChildProcessA(comspec, cmdLine.c_str(), &piProcInfo,
2122 if (bFuncRetn == 0) {
2123 ErrorExit("CreateProcess failed\n");
2124 const char *err = errorString();
2125 Dmsg3(dbglvl, "CreateProcess(%s, %s, ...)=%s\n",comspec,cmdLine.c_str(),err);
2126 LocalFree((void *)err);
2127 return INVALID_HANDLE_VALUE;
2129 // we don't need a handle on the process primary thread so we close
2131 CloseHandle(piProcInfo.hThread);
2132 return piProcInfo.hProcess;
2136 ErrorExit (LPCSTR lpszMessage)
2138 Dmsg1(0, "%s", lpszMessage);
2143 typedef struct s_bpipe {
2145 time_t worker_stime;
2154 CloseIfValid(HANDLE handle)
2156 if (handle != INVALID_HANDLE_VALUE)
2157 CloseHandle(handle);
2161 open_bpipe(char *prog, int wait, const char *mode)
2163 HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
2164 hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
2167 SECURITY_ATTRIBUTES saAttr;
2171 hChildStdinRd = hChildStdinWr = hChildStdinWrDup =
2172 hChildStdoutRd = hChildStdoutWr = hChildStdoutRdDup =
2173 hInputFile = INVALID_HANDLE_VALUE;
2175 BPIPE *bpipe = (BPIPE *)malloc(sizeof(BPIPE));
2176 memset((void *)bpipe, 0, sizeof(BPIPE));
2178 int mode_read = (mode[0] == 'r');
2179 int mode_write = (mode[0] == 'w' || mode[1] == 'w');
2182 // Set the bInheritHandle flag so pipe handles are inherited.
2184 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
2185 saAttr.bInheritHandle = TRUE;
2186 saAttr.lpSecurityDescriptor = NULL;
2190 // Create a pipe for the child process's STDOUT.
2191 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
2192 ErrorExit("Stdout pipe creation failed\n");
2195 // Create noninheritable read handle and close the inheritable read
2198 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
2199 GetCurrentProcess(), &hChildStdoutRdDup , 0,
2201 DUPLICATE_SAME_ACCESS);
2203 ErrorExit("DuplicateHandle failed");
2207 CloseHandle(hChildStdoutRd);
2208 hChildStdoutRd = INVALID_HANDLE_VALUE;
2213 // Create a pipe for the child process's STDIN.
2215 if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
2216 ErrorExit("Stdin pipe creation failed\n");
2220 // Duplicate the write handle to the pipe so it is not inherited.
2221 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
2222 GetCurrentProcess(), &hChildStdinWrDup,
2224 FALSE, // not inherited
2225 DUPLICATE_SAME_ACCESS);
2227 ErrorExit("DuplicateHandle failed");
2231 CloseHandle(hChildStdinWr);
2232 hChildStdinWr = INVALID_HANDLE_VALUE;
2234 // spawn program with redirected handles as appropriate
2235 bpipe->worker_pid = (pid_t)
2236 CreateChildProcess(prog, // commandline
2237 hChildStdinRd, // stdin HANDLE
2238 hChildStdoutWr, // stdout HANDLE
2239 hChildStdoutWr); // stderr HANDLE
2241 if ((HANDLE) bpipe->worker_pid == INVALID_HANDLE_VALUE)
2245 bpipe->worker_stime = time(NULL);
2248 CloseHandle(hChildStdoutWr); // close our write side so when
2249 // process terminates we can
2251 // ugly but convert WIN32 HANDLE to FILE*
2252 int rfd = _open_osfhandle((intptr_t)hChildStdoutRdDup, O_RDONLY | O_BINARY);
2254 bpipe->rfd = _fdopen(rfd, "rb");
2258 CloseHandle(hChildStdinRd); // close our read side so as not
2259 // to interfre with child's copy
2260 // ugly but convert WIN32 HANDLE to FILE*
2261 int wfd = _open_osfhandle((intptr_t)hChildStdinWrDup, O_WRONLY | O_BINARY);
2263 bpipe->wfd = _fdopen(wfd, "wb");
2268 bpipe->timer_id = start_child_timer(NULL, bpipe->worker_pid, wait);
2275 CloseIfValid(hChildStdoutRd);
2276 CloseIfValid(hChildStdoutRdDup);
2277 CloseIfValid(hChildStdinWr);
2278 CloseIfValid(hChildStdinWrDup);
2280 free((void *) bpipe);
2281 errno = b_errno_win32; /* do GetLastError() for error code */
2287 kill(int pid, int signal)
2290 if (!TerminateProcess((HANDLE)pid, (UINT) signal)) {
2292 errno = b_errno_win32;
2294 CloseHandle((HANDLE)pid);
2300 close_bpipe(BPIPE *bpipe)
2303 int32_t remaining_wait = bpipe->wait;
2315 if (remaining_wait == 0) { /* wait indefinitely */
2316 remaining_wait = INT32_MAX;
2320 if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) {
2321 const char *err = errorString();
2322 rval = b_errno_win32;
2323 Dmsg1(dbglvl, "GetExitCode error %s\n", err);
2324 LocalFree((void *)err);
2327 if (exitCode == STILL_ACTIVE) {
2328 if (remaining_wait <= 0) {
2329 rval = ETIME; /* timed out */
2332 bmicrosleep(1, 0); /* wait one second */
2334 } else if (exitCode != 0) {
2335 /* Truncate exit code as it doesn't seem to be correct */
2336 rval = (exitCode & 0xFF) | b_errno_exit;
2339 break; /* Shouldn't get here */
2343 if (bpipe->timer_id) {
2344 stop_child_timer(bpipe->timer_id);
2346 if (bpipe->rfd) fclose(bpipe->rfd);
2347 if (bpipe->wfd) fclose(bpipe->wfd);
2348 free((void *)bpipe);
2353 close_wpipe(BPIPE *bpipe)
2359 if (fclose(bpipe->wfd) != 0) {
2369 utime(const char *fname, struct utimbuf *times)
2374 conv_unix_to_vss_win32_path(fname, tmpbuf, 5000);
2376 cvt_utime_to_ftime(times->actime, acc);
2377 cvt_utime_to_ftime(times->modtime, mod);
2379 HANDLE h = INVALID_HANDLE_VALUE;
2381 if (p_CreateFileW) {
2382 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2383 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
2385 h = p_CreateFileW((LPCWSTR)pwszBuf,
2386 FILE_WRITE_ATTRIBUTES,
2387 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2390 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2393 free_pool_memory(pwszBuf);
2394 } else if (p_CreateFileA) {
2395 h = p_CreateFileA(tmpbuf,
2396 FILE_WRITE_ATTRIBUTES,
2397 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2400 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2404 if (h == INVALID_HANDLE_VALUE) {
2405 const char *err = errorString();
2406 Dmsg2(dbglvl, "Cannot open file \"%s\" for utime(): ERR=%s", tmpbuf, err);
2407 LocalFree((void *)err);
2408 errno = b_errno_win32;
2412 int rval = SetFileTime(h, NULL, &acc, &mod) ? 0 : -1;
2415 errno = b_errno_win32;
2423 file_open(const char *file, int flags, int mode)
2426 DWORD shareMode = 0;
2429 HANDLE foo = INVALID_HANDLE_VALUE;
2430 const char *remap = file;
2432 if (flags & O_WRONLY) access = GENERIC_WRITE;
2433 else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE;
2434 else access = GENERIC_READ;
2436 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2437 create = CREATE_NEW;
2438 else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
2439 create = CREATE_ALWAYS;
2440 else if (flags & O_CREAT)
2441 create = OPEN_ALWAYS;
2442 else if (flags & O_TRUNC)
2443 create = TRUNCATE_EXISTING;
2445 create = OPEN_EXISTING;
2449 if (flags & O_APPEND) {
2450 printf("open...APPEND not implemented yet.");
2454 if (p_CreateFileW) {
2455 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2456 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
2458 foo = p_CreateFileW((LPCWSTR) pwszBuf, access, shareMode, NULL, create, msflags, NULL);
2459 free_pool_memory(pwszBuf);
2460 } else if (p_CreateFileA)
2461 foo = CreateFile(file, access, shareMode, NULL, create, msflags, NULL);
2463 if (INVALID_HANDLE_VALUE == foo) {
2464 errno = b_errno_win32;
2475 if (!CloseHandle((HANDLE)fd)) {
2476 errno = b_errno_win32;
2484 file_write(int fd, const void *data, ssize_t len)
2488 status = WriteFile((HANDLE)fd, data, len, &bwrite, NULL);
2489 if (status) return bwrite;
2490 errno = b_errno_win32;
2496 file_read(int fd, void *data, ssize_t len)
2501 status = ReadFile((HANDLE)fd, data, len, &bread, NULL);
2502 if (status) return bread;
2503 errno = b_errno_win32;
2508 file_seek(int fd, boffset_t offset, int whence)
2512 LONG offset_low = (LONG)offset;
2513 LONG offset_high = (LONG)(offset >> 32);
2517 method = FILE_BEGIN;
2520 method = FILE_CURRENT;
2531 if ((val=SetFilePointer((HANDLE)fd, offset_low, &offset_high, method)) == INVALID_SET_FILE_POINTER) {
2532 errno = b_errno_win32;
2535 /* ***FIXME*** I doubt this works right */
2549 * Emulation of mmap and unmmap for tokyo dbm
2551 void *mmap(void *start, size_t length, int prot, int flags,
2552 int fd, off_t offset)
2554 DWORD fm_access = 0;
2555 DWORD mv_access = 0;
2566 if (flags & PROT_WRITE) {
2567 fm_access |= PAGE_READWRITE;
2568 } else if (flags & PROT_READ) {
2569 fm_access |= PAGE_READONLY;
2572 if (flags & PROT_READ) {
2573 mv_access |= FILE_MAP_READ;
2575 if (flags & PROT_WRITE) {
2576 mv_access |= FILE_MAP_WRITE;
2579 h = CreateFileMapping((HANDLE)_get_osfhandle (fd),
2580 NULL /* security */,
2582 0 /* MaximumSizeHigh */,
2583 0 /* MaximumSizeLow */,
2584 NULL /* name of the file mapping object */);
2586 if (!h || h == INVALID_HANDLE_VALUE) {
2590 mv = MapViewOfFile(h, mv_access,
2596 if (!mv || mv == INVALID_HANDLE_VALUE) {
2603 int munmap(void *start, size_t length)
2608 UnmapViewOfFile(start);
2614 /* syslog function, added by Nicolas Boichat */
2615 void openlog(const char *ident, int option, int facility) {}
2618 /* Log an error message */
2619 void LogErrorMsg(const char *message)
2621 HANDLE eventHandler;
2622 const char *strings[2];
2624 /* Use the OS event logging to log the error */
2625 eventHandler = RegisterEventSource(NULL, "Bacula");
2627 strings[0] = _("\n\nBacula ERROR: ");
2628 strings[1] = message;
2631 ReportEvent(eventHandler, EVENTLOG_ERROR_TYPE,
2635 2, /* Number of strings */
2636 0, /* raw data size */
2637 (const char **)strings, /* error strings */
2638 NULL); /* raw data */
2639 DeregisterEventSource(eventHandler);