1 // Copyright (C) 2006 Timothy Brownawell <tbrownaw@gmail.com>
2 //               2007 Zack Weinberg <zackw@panix.com>
3 //
4 // This program is made available under the GNU GPL version 2.0 or
5 // greater. See the accompanying file COPYING for details.
6 //
7 // This program is distributed WITHOUT ANY WARRANTY; without even the
8 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9 // PURPOSE.
10 
11 // Tester-specific platform interface glue, Windows version.
12 
13 #define WIN32_LEAN_AND_MEAN // we don't need the GUI interfaces
14 
15 #include "../../../src/base.hh"
16 #include "../tester-plaf.hh"
17 #include "../../../src/sanity.hh"
18 #include "../../../src/platform.hh"
19 
20 #include <windows.h>
21 
get_last_write_time(char const * name)22 time_t get_last_write_time(char const * name)
23 {
24   HANDLE h = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL,
25                         OPEN_EXISTING, 0, NULL);
26   E(h != INVALID_HANDLE_VALUE, origin::system,
27     F("CreateFile(%s) failed: %s") % name % os_strerror(GetLastError()));
28 
29   FILETIME ft;
30   E(GetFileTime(h, NULL, NULL, &ft), origin::system,
31     F("GetFileTime(%s) failed: %s") % name % os_strerror(GetLastError()));
32 
33   CloseHandle(h);
34 
35   // A FILETIME is a 64-bit quantity (represented as a pair of DWORDs)
36   // representing the number of 100-nanosecond intervals elapsed since
37   // 12:00 AM, January 1, 1601 UTC.  A time_t is the same as it is for
38   // Unix: seconds since 12:00 AM, January 1, 1970 UTC.  The offset is
39   // taken verbatim from MSDN.
40   LONGLONG ft64 = (((LONGLONG)ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
41   return (time_t)((ft64/10000000) - 11644473600LL);
42 }
43 
do_copy_file(std::string const & from,std::string const & to)44 void do_copy_file(std::string const & from, std::string const & to)
45 {
46   // For once something is easier with Windows.
47   E(CopyFile(from.c_str(), to.c_str(), true), origin::system,
48     F("copy %s to %s: %s") % from % to % os_strerror(GetLastError()));
49 }
50 
51 
set_env(char const * var,char const * val)52 void set_env(char const * var, char const * val)
53 {
54   SetEnvironmentVariable(var, val);
55 }
56 
unset_env(char const * var)57 void unset_env(char const * var)
58 {
59   SetEnvironmentVariable(var, 0);
60 }
61 
do_umask(int)62 int do_umask(int /* mask */)
63 {
64   return -1; // not a meaningful operation on Windows
65 }
66 
make_temp_dir()67 char * make_temp_dir()
68 {
69   // PATH_MAX isn't available everywhere, while FILENAME_MAX is supposed
70   // to be, in C89 and on.  However, the GNU C Library manual says this:
71   //
72   // Macro: int FILENAME_MAX
73   //    The value of this macro is an integer constant expression that
74   //    represents the maximum length of a file name string. It is defined
75   //    in `stdio.h'.
76   //
77   //    Unlike PATH_MAX, this macro is defined even if there is no actual
78   //    limit imposed. In such a case, its value is typically a very large
79   //    number. This is always the case on the GNU system.
80   //
81   //    Usage Note: Don't use FILENAME_MAX as the size of an array in which
82   //    to store a file name! You can't possibly make an array that big!
83   //    Use dynamic allocation (see section 3.2 Allocating Storage For
84   //    Program Data) instead.
85   //
86   // So, to make sure we don't exceed resources, make sure to use a value
87   // no larger than 16384 if FILENAME_MAX is larger.
88 #if defined(PATH_MAX)
89 # define DIR_MAX_SIZE PATH_MAX
90 #elif defined(FILENAME_MAX)
91 # if FILENAME_MAX > 16384
92 #  define DIR_MAX_SIZE 16384
93 # else
94 #  define DIR_MAX_SIZE FILENAME_MAX
95 # endif
96 #endif
97   char dir[DIR_MAX_SIZE];
98 
99   // GetTempFileName wants 14 characters at the end of the path.
100   {
101     DWORD ret = GetTempPath(DIR_MAX_SIZE - 14, dir);
102     E(ret > 0 && ret <= DIR_MAX_SIZE - 14, origin::system,
103       F("GetTempPath failed: %s") % os_strerror(GetLastError()));
104   }
105 
106   // If the third argument to GetTempFileName is zero, it will create a
107   // file, which is not what we want.
108   UINT base = GetTickCount();
109   char * name = new char[DIR_MAX_SIZE];
110   for (UINT i = 0; i < 65535; i++)
111     {
112       if (base + i == 0)
113         continue;
114 
115       E(GetTempFileName(dir, "MTN", base + i, name) != 0, origin::system,
116         F("GetTempFileName failed: %s") % os_strerror(GetLastError()));
117 
118       if (CreateDirectory(name, NULL))
119         return name;
120 
121       E(GetLastError() == ERROR_ALREADY_EXISTS, origin::system,
122         F("CreateDirectory(%s) failed: %s") % name % GetLastError());
123     }
124   E(false, origin::system, F("All temporary directory names are already in use."));
125 }
126 
running_as_root()127 bool running_as_root()
128 {
129   // ??? check for privileges (what the test suite cares about is being able
130   // to create files it cannot write to - may not be impossible for any
131   // privileged account in Windows)
132   return false;
133 }
134 
135 // FIXME: I don't know any intrinsic reason why parallel test cases and the
136 // jobserver protocol could not be supported on Windows (see the lengthy
137 // explanation of the protocol in unix/tester-plaf.cc) but someone with a
138 // deep understanding of Win32 would have to implement it to ensure its
139 // race-free-ness.  (Before bothering, confirm that GNU Make supports the
140 // jobserver protocol on Windows.)
141 //
142 // NOTE TO POTENTIAL FIXERS: If you code this with the fake POSIX layer in
143 // the C runtime instead of with WaitForMultipleObjects and other kernel
144 // primitives, you will suffer the curse of the vengeful ghost of Dave Cutler.
145 
prepare_for_parallel_testcases(int jobs,int,int)146 void prepare_for_parallel_testcases(int jobs, int, int)
147 {
148   if (jobs != 1)
149     W(F("parallel execution of test cases is not supported on Windows."));
150 }
151 
152 // General note: the magic numbers in this function are meaningful to
153 // testlib.lua.  They indicate a number of failure scenarios in which
154 // more detailed diagnostics are not possible.
155 // The bulk of the work is done in main(), -r case, q.v.
156 
run_tests_in_children(test_enumerator const & next_test,test_invoker const &,test_cleaner const & cleanup,std::string const & run_dir,std::string const & runner,std::string const & testfile,std::string const & firstdir)157 void run_tests_in_children(test_enumerator const & next_test,
158                            test_invoker const & /*invoke*/,
159                            test_cleaner const & cleanup,
160                            std::string const & run_dir,
161                            std::string const & runner,
162                            std::string const & testfile,
163                            std::string const & firstdir)
164 {
165   char const * argv[6];
166   argv[0] = runner.c_str();
167   argv[1] = "-r";
168   argv[2] = testfile.c_str();
169   argv[3] = firstdir.c_str();
170   argv[4] = 0;
171   argv[5] = 0;
172 
173   test_to_run t;
174   std::string testdir;
175   while (next_test(t))
176     {
177       // This must be done before we try to redirect stdout/err to a
178       // file within testdir.
179       try
180         {
181           testdir = run_dir + "/" + t.name;
182           do_remove_recursive(testdir);
183           do_mkdir(testdir);
184         }
185       catch (...)
186         {
187           cleanup(t, 121, 0, 0);
188           continue;
189         }
190 
191       change_current_working_dir(testdir);
192       argv[4] = t.name.c_str();
193       pid_t child = process_spawn(argv);
194       DWORD start_millis = GetTickCount();
195       change_current_working_dir(run_dir);
196 
197       int status;
198       if (child == -1)
199         status = 122;
200       else
201         process_wait(child, &status);
202 
203       DWORD end_millis = GetTickCount();
204       if (cleanup(t, status, (end_millis - start_millis) / 1000, -1))
205         try
206           {
207             do_remove_recursive(testdir);
208           }
209         catch (...)
210           {
211             // process_wait sometimes returns before releasing the lock on
212             // the directory that we tried to remove. So wait a little
213             // longer and try again.
214             Sleep (1000);// milliseconds
215             do_remove_recursive(testdir);
216           }
217     }
218 }
219 
220 // Local Variables:
221 // mode: C++
222 // fill-column: 76
223 // c-file-style: "gnu"
224 // indent-tabs-mode: nil
225 // End:
226 // vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
227