1 | // This file is part of BOINC. |
---|
2 | // http://boinc.berkeley.edu |
---|
3 | // Copyright (C) 2008 University of California |
---|
4 | // |
---|
5 | // BOINC is free software; you can redistribute it and/or modify it |
---|
6 | // under the terms of the GNU Lesser General Public License |
---|
7 | // as published by the Free Software Foundation, |
---|
8 | // either version 3 of the License, or (at your option) any later version. |
---|
9 | // |
---|
10 | // BOINC is distributed in the hope that it will be useful, |
---|
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
---|
13 | // See the GNU Lesser General Public License for more details. |
---|
14 | // |
---|
15 | // You should have received a copy of the GNU Lesser General Public |
---|
16 | // License |
---|
17 | // along with BOINC. If not, see <http://www.gnu.org/licenses/>. |
---|
18 | |
---|
19 | // vmwrapper.C |
---|
20 | // VMWare wrapper program - lets you use BOINC to drive a VMWare Server |
---|
21 | // guest OS |
---|
22 | |
---|
23 | #include <stdio.h> |
---|
24 | #include <vector> |
---|
25 | #include <string> |
---|
26 | #ifdef _WIN32 |
---|
27 | #include "boinc_win.h" |
---|
28 | #include "win_util.h" |
---|
29 | #else |
---|
30 | #include <sys/wait.h> |
---|
31 | #include <sys/types.h> |
---|
32 | #include <sys/stat.h> |
---|
33 | #include <unistd.h> |
---|
34 | #include "procinfo.h" |
---|
35 | #endif |
---|
36 | |
---|
37 | #include "boinc_api.h" |
---|
38 | #include "diagnostics.h" |
---|
39 | #include "filesys.h" |
---|
40 | #include "parse.h" |
---|
41 | #include "str_util.h" |
---|
42 | #include "util.h" |
---|
43 | #include "error_numbers.h" |
---|
44 | |
---|
45 | #include "vmware-vix/vix.h" |
---|
46 | |
---|
47 | #define JOB_FILENAME "job.xml" |
---|
48 | #define CHECKPOINT_FILENAME "checkpoint.txt" |
---|
49 | |
---|
50 | #define POLL_PERIOD 1.0 |
---|
51 | |
---|
52 | using std::vector; |
---|
53 | using std::string; |
---|
54 | |
---|
55 | struct TASK |
---|
56 | { |
---|
57 | string application; |
---|
58 | string stdin_filename; |
---|
59 | string stdout_filename; |
---|
60 | string stderr_filename; |
---|
61 | |
---|
62 | string vm; |
---|
63 | string innerjob; |
---|
64 | string datadir; |
---|
65 | string partial_credit; |
---|
66 | string snapshots; |
---|
67 | string user; |
---|
68 | string password; |
---|
69 | |
---|
70 | vector < string > data; |
---|
71 | vector < string > output; |
---|
72 | |
---|
73 | string checkpoint_filename; |
---|
74 | // name of task's checkpoint file, if any |
---|
75 | double checkpoint_cpu_time; |
---|
76 | // CPU time at last checkpoint |
---|
77 | string command_line; |
---|
78 | double weight; |
---|
79 | // contribution of this task to overall fraction done |
---|
80 | double final_cpu_time; |
---|
81 | double starting_cpu; |
---|
82 | // how much CPU time was used by tasks before this in the job file |
---|
83 | bool suspended; |
---|
84 | double wall_cpu_time; |
---|
85 | // for estimating CPU time on Win98/ME and Mac |
---|
86 | |
---|
87 | |
---|
88 | #ifdef _WIN32 |
---|
89 | |
---|
90 | HANDLE pid_handle; |
---|
91 | DWORD pid; |
---|
92 | HANDLE thread_handle; |
---|
93 | struct _stat last_stat; // mod time of checkpoint file |
---|
94 | |
---|
95 | #else |
---|
96 | |
---|
97 | int pid; |
---|
98 | struct stat last_stat; |
---|
99 | |
---|
100 | #endif |
---|
101 | |
---|
102 | |
---|
103 | bool stat_first; |
---|
104 | int parse (XML_PARSER &); |
---|
105 | bool poll (int &status); |
---|
106 | int run (int argc, char **argv); |
---|
107 | void kill (); |
---|
108 | void stop (); |
---|
109 | void resume (); |
---|
110 | double cpu_time (); |
---|
111 | |
---|
112 | inline bool has_checkpointed () |
---|
113 | { |
---|
114 | bool changed = false; |
---|
115 | |
---|
116 | if (checkpoint_filename.size () == 0) |
---|
117 | return false; |
---|
118 | |
---|
119 | struct stat new_stat; |
---|
120 | |
---|
121 | int retval = stat (checkpoint_filename.c_str (), &new_stat); |
---|
122 | |
---|
123 | if (retval) |
---|
124 | return false; |
---|
125 | |
---|
126 | if (!stat_first && new_stat.st_mtime != last_stat.st_mtime) |
---|
127 | { |
---|
128 | changed = true; |
---|
129 | } |
---|
130 | |
---|
131 | stat_first = false; |
---|
132 | |
---|
133 | last_stat.st_mtime = new_stat.st_mtime; |
---|
134 | return changed; |
---|
135 | } |
---|
136 | }; |
---|
137 | |
---|
138 | vector < TASK > tasks; |
---|
139 | APP_INIT_DATA aid; |
---|
140 | bool graphics = false; |
---|
141 | |
---|
142 | int TASK::parse (XML_PARSER & xp) |
---|
143 | { |
---|
144 | |
---|
145 | string this_data, this_output; |
---|
146 | char tag[1024], buf[8192], buf2[8192]; |
---|
147 | |
---|
148 | bool is_tag; |
---|
149 | |
---|
150 | weight = 1; |
---|
151 | final_cpu_time = 0; |
---|
152 | stat_first = true; |
---|
153 | while (!xp.get (tag, sizeof (tag), is_tag)) |
---|
154 | { |
---|
155 | if (!is_tag) |
---|
156 | { |
---|
157 | fprintf (stderr, "SCHED_CONFIG::parse(): unexpected text %s\n", |
---|
158 | tag); |
---|
159 | continue; |
---|
160 | } |
---|
161 | if (!strcmp (tag, "/task")) |
---|
162 | { |
---|
163 | return 0; |
---|
164 | } |
---|
165 | else if (xp.parse_string (tag, "application", application)) |
---|
166 | continue; |
---|
167 | |
---|
168 | else if (xp.parse_string (tag, "innerjob", innerjob)) |
---|
169 | continue; |
---|
170 | else if (xp.parse_string (tag, "vm", vm)) |
---|
171 | continue; |
---|
172 | else if (xp.parse_string (tag, "datadir", datadir)) |
---|
173 | continue; |
---|
174 | else if (xp.parse_string (tag, "partial_credit", partial_credit)) |
---|
175 | continue; |
---|
176 | else if (xp.parse_string (tag, "snapshots", snapshots)) |
---|
177 | continue; |
---|
178 | |
---|
179 | |
---|
180 | else if (xp.parse_string (tag, "data", this_data)) { |
---|
181 | data.push_back(this_data); |
---|
182 | continue; |
---|
183 | } |
---|
184 | |
---|
185 | else if (xp.parse_string (tag, "output", this_output)) { |
---|
186 | output.push_back(this_output); |
---|
187 | continue; |
---|
188 | } |
---|
189 | |
---|
190 | else if (xp.parse_string (tag, "user", user)) |
---|
191 | continue; |
---|
192 | else if (xp.parse_string (tag, "password", password)) |
---|
193 | continue; |
---|
194 | |
---|
195 | else if (xp.parse_string (tag, "stdin_filename", stdin_filename)) |
---|
196 | continue; |
---|
197 | else if (xp.parse_string (tag, "stdout_filename", stdout_filename)) |
---|
198 | continue; |
---|
199 | else if (xp.parse_string (tag, "stderr_filename", stderr_filename)) |
---|
200 | continue; |
---|
201 | else if (xp.parse_str (tag, "command_line", buf, sizeof (buf))) |
---|
202 | { |
---|
203 | while (1) |
---|
204 | { |
---|
205 | char *p = strstr (buf, "$PROJECT_DIR"); |
---|
206 | |
---|
207 | if (!p) |
---|
208 | break; |
---|
209 | strcpy (buf2, p + strlen ("$PROJECT_DIR")); |
---|
210 | strcpy (p, aid.project_dir); |
---|
211 | strcat (p, buf2); |
---|
212 | } |
---|
213 | command_line = buf; |
---|
214 | continue; |
---|
215 | } |
---|
216 | else if (xp. |
---|
217 | parse_string (tag, "checkpoint_filename", |
---|
218 | checkpoint_filename)) |
---|
219 | continue; |
---|
220 | else if (xp.parse_double (tag, "weight", weight)) |
---|
221 | continue; |
---|
222 | } |
---|
223 | return ERR_XML_PARSE; |
---|
224 | } |
---|
225 | |
---|
226 | |
---|
227 | int parse_job_file () |
---|
228 | { |
---|
229 | MIOFILE mf; |
---|
230 | char tag[1024], buf[256]; |
---|
231 | |
---|
232 | bool is_tag; |
---|
233 | |
---|
234 | boinc_resolve_filename (JOB_FILENAME, buf, 1024); |
---|
235 | FILE *f = boinc_fopen (buf, "r"); |
---|
236 | |
---|
237 | if (!f) |
---|
238 | { |
---|
239 | fprintf (stderr, "can't open job file %s\n", buf); |
---|
240 | return ERR_FOPEN; |
---|
241 | } |
---|
242 | mf.init_file (f); |
---|
243 | XML_PARSER xp (&mf); |
---|
244 | |
---|
245 | if (!xp.parse_start ("job_desc")) |
---|
246 | return ERR_XML_PARSE; |
---|
247 | while (!xp.get (tag, sizeof (tag), is_tag)) |
---|
248 | { |
---|
249 | if (!is_tag) |
---|
250 | { |
---|
251 | fprintf (stderr, "SCHED_CONFIG::parse(): unexpected text %s\n", |
---|
252 | tag); |
---|
253 | continue; |
---|
254 | } |
---|
255 | if (!strcmp (tag, "/job_desc")) |
---|
256 | { |
---|
257 | fclose (f); |
---|
258 | return 0; |
---|
259 | } |
---|
260 | if (!strcmp (tag, "task")) |
---|
261 | { |
---|
262 | TASK task; |
---|
263 | int retval = task.parse (xp); |
---|
264 | |
---|
265 | if (!retval) |
---|
266 | { |
---|
267 | tasks.push_back (task); |
---|
268 | } |
---|
269 | } |
---|
270 | } |
---|
271 | fclose (f); |
---|
272 | return ERR_XML_PARSE; |
---|
273 | } |
---|
274 | |
---|
275 | |
---|
276 | #ifdef _WIN32 |
---|
277 | // CreateProcess() takes HANDLEs for the stdin/stdout. |
---|
278 | // We need to use CreateFile() to get them. Ugh. |
---|
279 | // |
---|
280 | HANDLE |
---|
281 | win_fopen (const char *path, const char *mode) |
---|
282 | { |
---|
283 | SECURITY_ATTRIBUTES sa; |
---|
284 | memset (&sa, 0, sizeof (sa)); |
---|
285 | sa.nLength = sizeof (sa); |
---|
286 | sa.bInheritHandle = TRUE; |
---|
287 | |
---|
288 | if (!strcmp (mode, "r")) |
---|
289 | { |
---|
290 | return CreateFile (path, |
---|
291 | GENERIC_READ, |
---|
292 | FILE_SHARE_READ, &sa, OPEN_EXISTING, 0, 0); |
---|
293 | } |
---|
294 | else if (!strcmp (mode, "w")) |
---|
295 | { |
---|
296 | return CreateFile (path, |
---|
297 | GENERIC_WRITE, |
---|
298 | FILE_SHARE_WRITE, &sa, OPEN_ALWAYS, 0, 0); |
---|
299 | } |
---|
300 | else if (!strcmp (mode, "a")) |
---|
301 | { |
---|
302 | HANDLE |
---|
303 | hAppend = CreateFile (path, |
---|
304 | GENERIC_WRITE, |
---|
305 | FILE_SHARE_WRITE, &sa, OPEN_ALWAYS, 0, 0); |
---|
306 | SetFilePointer (hAppend, 0, NULL, FILE_END); |
---|
307 | return hAppend; |
---|
308 | } |
---|
309 | else |
---|
310 | { |
---|
311 | return 0; |
---|
312 | } |
---|
313 | } |
---|
314 | #endif |
---|
315 | |
---|
316 | void slash_to_backslash (char *p) |
---|
317 | { |
---|
318 | while (1) |
---|
319 | { |
---|
320 | char *q = strchr (p, '/'); |
---|
321 | |
---|
322 | if (!q) |
---|
323 | break; |
---|
324 | *q = '\\'; |
---|
325 | } |
---|
326 | } |
---|
327 | |
---|
328 | |
---|
329 | int TASK::run (int argct, char **argvt) |
---|
330 | { |
---|
331 | string stdout_path, stdin_path, stderr_path; |
---|
332 | char app_path[1024], buf[256]; |
---|
333 | |
---|
334 | strcpy (buf, application.c_str ()); |
---|
335 | char *p = strstr (buf, "$PROJECT_DIR"); |
---|
336 | |
---|
337 | if (p) |
---|
338 | { |
---|
339 | p += strlen ("$PROJECT_DIR"); |
---|
340 | sprintf (app_path, "%s%s", aid.project_dir, p); |
---|
341 | } |
---|
342 | else |
---|
343 | { |
---|
344 | boinc_resolve_filename (buf, app_path, sizeof (app_path)); |
---|
345 | } |
---|
346 | |
---|
347 | // Append wrapper's command-line arguments to those in the job file. |
---|
348 | // |
---|
349 | for (int i = 1; i < argct; i++) |
---|
350 | { |
---|
351 | command_line += argvt[i]; |
---|
352 | if ((i + 1) < argct) |
---|
353 | { |
---|
354 | command_line += string (" "); |
---|
355 | } |
---|
356 | } |
---|
357 | |
---|
358 | fprintf (stderr, "wrapper: running %s (%s)\n", |
---|
359 | app_path, command_line.c_str ()); |
---|
360 | |
---|
361 | #ifdef _WIN32 |
---|
362 | PROCESS_INFORMATION process_info; |
---|
363 | STARTUPINFO startup_info; |
---|
364 | string command; |
---|
365 | |
---|
366 | slash_to_backslash (app_path); |
---|
367 | memset (&process_info, 0, sizeof (process_info)); |
---|
368 | memset (&startup_info, 0, sizeof (startup_info)); |
---|
369 | command = string ("\"") + app_path + string ("\" ") + command_line; |
---|
370 | |
---|
371 | // pass std handles to app |
---|
372 | // |
---|
373 | startup_info.dwFlags = STARTF_USESTDHANDLES; |
---|
374 | if (stdout_filename != "") |
---|
375 | { |
---|
376 | boinc_resolve_filename_s (stdout_filename.c_str (), stdout_path); |
---|
377 | startup_info.hStdOutput = win_fopen (stdout_path.c_str (), "a"); |
---|
378 | } |
---|
379 | if (stdin_filename != "") |
---|
380 | { |
---|
381 | boinc_resolve_filename_s (stdin_filename.c_str (), stdin_path); |
---|
382 | startup_info.hStdInput = win_fopen (stdin_path.c_str (), "r"); |
---|
383 | } |
---|
384 | if (stderr_filename != "") |
---|
385 | { |
---|
386 | boinc_resolve_filename_s (stderr_filename.c_str (), stderr_path); |
---|
387 | startup_info.hStdError = win_fopen (stderr_path.c_str (), "a"); |
---|
388 | } |
---|
389 | else |
---|
390 | { |
---|
391 | startup_info.hStdError = win_fopen (STDERR_FILE, "a"); |
---|
392 | } |
---|
393 | |
---|
394 | // bInheritHandles |
---|
395 | if (!CreateProcess (app_path, (LPSTR) command.c_str (), NULL, NULL, TRUE, |
---|
396 | CREATE_NO_WINDOW | IDLE_PRIORITY_CLASS, |
---|
397 | NULL, NULL, &startup_info, &process_info)) |
---|
398 | { |
---|
399 | return ERR_EXEC; |
---|
400 | } |
---|
401 | pid_handle = process_info.hProcess; |
---|
402 | pid = process_info.dwProcessId; |
---|
403 | thread_handle = process_info.hThread; |
---|
404 | SetThreadPriority (thread_handle, THREAD_PRIORITY_IDLE); |
---|
405 | #else |
---|
406 | int retval, argc; |
---|
407 | char progname[256]; |
---|
408 | char *argv[256]; |
---|
409 | char arglist[4096]; |
---|
410 | FILE *stdout_file; |
---|
411 | FILE *stdin_file; |
---|
412 | FILE *stderr_file; |
---|
413 | |
---|
414 | pid = fork (); |
---|
415 | if (pid == -1) |
---|
416 | { |
---|
417 | boinc_finish (ERR_FORK); |
---|
418 | } |
---|
419 | if (pid == 0) |
---|
420 | { |
---|
421 | // we're in the child process here |
---|
422 | // |
---|
423 | // open stdout, stdin if file names are given |
---|
424 | // NOTE: if the application is restartable, |
---|
425 | // we should deal with atomicity somehow |
---|
426 | // |
---|
427 | if (stdout_filename != "") |
---|
428 | { |
---|
429 | boinc_resolve_filename_s (stdout_filename.c_str (), stdout_path); |
---|
430 | stdout_file = freopen (stdout_path.c_str (), "a", stdout); |
---|
431 | if (!stdout_file) |
---|
432 | return ERR_FOPEN; |
---|
433 | } |
---|
434 | if (stdin_filename != "") |
---|
435 | { |
---|
436 | boinc_resolve_filename_s (stdin_filename.c_str (), stdin_path); |
---|
437 | stdin_file = freopen (stdin_path.c_str (), "r", stdin); |
---|
438 | if (!stdin_file) |
---|
439 | return ERR_FOPEN; |
---|
440 | } |
---|
441 | if (stderr_filename != "") |
---|
442 | { |
---|
443 | boinc_resolve_filename_s (stderr_filename.c_str (), stderr_path); |
---|
444 | stderr_file = freopen (stderr_path.c_str (), "a", stderr); |
---|
445 | if (!stderr_file) |
---|
446 | return ERR_FOPEN; |
---|
447 | } |
---|
448 | // construct argv |
---|
449 | // TODO: use malloc instead of stack var |
---|
450 | // |
---|
451 | argv[0] = app_path; |
---|
452 | strlcpy (arglist, command_line.c_str (), sizeof (arglist)); |
---|
453 | argc = parse_command_line (arglist, argv + 1); |
---|
454 | setpriority (PRIO_PROCESS, 0, PROCESS_IDLE_PRIORITY); |
---|
455 | retval = execv (app_path, argv); |
---|
456 | exit (ERR_EXEC); |
---|
457 | } |
---|
458 | #endif |
---|
459 | wall_cpu_time = 0; |
---|
460 | suspended = false; |
---|
461 | return 0; |
---|
462 | } |
---|
463 | |
---|
464 | |
---|
465 | bool TASK::poll (int &status) |
---|
466 | { |
---|
467 | if (!suspended) |
---|
468 | wall_cpu_time += POLL_PERIOD; |
---|
469 | #ifdef _WIN32 |
---|
470 | unsigned long |
---|
471 | exit_code; |
---|
472 | |
---|
473 | if (GetExitCodeProcess (pid_handle, &exit_code)) |
---|
474 | { |
---|
475 | if (exit_code != STILL_ACTIVE) |
---|
476 | { |
---|
477 | status = exit_code; |
---|
478 | final_cpu_time = cpu_time (); |
---|
479 | return true; |
---|
480 | } |
---|
481 | } |
---|
482 | #else |
---|
483 | int |
---|
484 | wpid, |
---|
485 | stat; |
---|
486 | struct rusage |
---|
487 | ru; |
---|
488 | |
---|
489 | wpid = wait4 (pid, &status, WNOHANG, &ru); |
---|
490 | if (wpid) |
---|
491 | { |
---|
492 | final_cpu_time = |
---|
493 | (float) ru.ru_utime.tv_sec + ((float) ru.ru_utime.tv_usec) / 1e+6; |
---|
494 | return true; |
---|
495 | } |
---|
496 | #endif |
---|
497 | return false; |
---|
498 | } |
---|
499 | |
---|
500 | |
---|
501 | void TASK::kill () |
---|
502 | { |
---|
503 | #ifdef _WIN32 |
---|
504 | TerminateProcess (pid_handle, -1); |
---|
505 | #else |
---|
506 | ::kill (pid, SIGKILL); |
---|
507 | #endif |
---|
508 | } |
---|
509 | |
---|
510 | |
---|
511 | void TASK::stop () |
---|
512 | { |
---|
513 | suspended = true; |
---|
514 | } |
---|
515 | |
---|
516 | |
---|
517 | void TASK::resume () |
---|
518 | { |
---|
519 | suspended = false; |
---|
520 | } |
---|
521 | |
---|
522 | |
---|
523 | |
---|
524 | |
---|
525 | double TASK::cpu_time () |
---|
526 | { |
---|
527 | #ifdef _WIN32 |
---|
528 | FILETIME creation_time, exit_time, kernel_time, user_time; |
---|
529 | ULARGE_INTEGER tKernel, tUser; |
---|
530 | LONGLONG totTime; |
---|
531 | |
---|
532 | int retval = GetProcessTimes (pid_handle, &creation_time, &exit_time, |
---|
533 | &kernel_time, |
---|
534 | &user_time); |
---|
535 | |
---|
536 | if (retval == 0) |
---|
537 | { |
---|
538 | return wall_cpu_time; |
---|
539 | } |
---|
540 | |
---|
541 | tKernel.LowPart = kernel_time.dwLowDateTime; |
---|
542 | tKernel.HighPart = kernel_time.dwHighDateTime; |
---|
543 | tUser.LowPart = user_time.dwLowDateTime; |
---|
544 | tUser.HighPart = user_time.dwHighDateTime; |
---|
545 | totTime = tKernel.QuadPart + tUser.QuadPart; |
---|
546 | |
---|
547 | return totTime / 1.e7; |
---|
548 | #elif defined(__APPLE__) |
---|
549 | // There's no easy way to get another process's CPU time in Mac OS X |
---|
550 | // |
---|
551 | return wall_cpu_time; |
---|
552 | #else |
---|
553 | return linux_cpu_time (pid); |
---|
554 | #endif |
---|
555 | } |
---|
556 | |
---|
557 | |
---|
558 | |
---|
559 | |
---|
560 | // Support for multiple tasks. |
---|
561 | // We keep a checkpoint file that says how many tasks we've completed |
---|
562 | // and how much CPU time has been used so far |
---|
563 | // |
---|
564 | void write_checkpoint (int ntasks, double cpu) |
---|
565 | { |
---|
566 | FILE *f = fopen (CHECKPOINT_FILENAME, "w"); |
---|
567 | |
---|
568 | if (!f) |
---|
569 | return; |
---|
570 | fprintf (f, "%d %f\n", ntasks, cpu); |
---|
571 | fclose (f); |
---|
572 | } |
---|
573 | |
---|
574 | |
---|
575 | void read_checkpoint (int &ntasks, double &cpu) |
---|
576 | { |
---|
577 | int nt; |
---|
578 | double c; |
---|
579 | |
---|
580 | ntasks = 0; |
---|
581 | cpu = 0; |
---|
582 | FILE *f = fopen (CHECKPOINT_FILENAME, "r"); |
---|
583 | |
---|
584 | if (!f) |
---|
585 | return; |
---|
586 | int n = fscanf (f, "%d %lf", &nt, &c); |
---|
587 | |
---|
588 | fclose (f); |
---|
589 | if (n != 2) |
---|
590 | return; |
---|
591 | ntasks = nt; |
---|
592 | cpu = c; |
---|
593 | } |
---|
594 | |
---|
595 | |
---|
596 | |
---|
597 | void check_vm_result(VixError err, VixHandle hostHandle, string errorString) |
---|
598 | { |
---|
599 | if (VIX_OK != err) |
---|
600 | { |
---|
601 | fprintf (stderr, "\n\n Error: %s\n",errorString.c_str()); |
---|
602 | fprintf (stderr, "Error message: \"%s\"\n", Vix_GetErrorText (err, NULL)); |
---|
603 | VixHost_Disconnect (hostHandle); |
---|
604 | boinc_finish(100); |
---|
605 | } |
---|
606 | } |
---|
607 | |
---|
608 | int main (int argc, char **argv) |
---|
609 | { |
---|
610 | |
---|
611 | BOINC_OPTIONS options; |
---|
612 | int retval, ntasks; |
---|
613 | unsigned int i; |
---|
614 | double cpu, total_weight = 0, w = 0; |
---|
615 | |
---|
616 | for (i = 1; i < (unsigned int) argc; i++) |
---|
617 | { |
---|
618 | if (!strcmp (argv[i], "--graphics")) |
---|
619 | { |
---|
620 | graphics = true; |
---|
621 | } |
---|
622 | } |
---|
623 | |
---|
624 | memset (&options, 0, sizeof (options)); |
---|
625 | options.main_program = true; |
---|
626 | options.check_heartbeat = true; |
---|
627 | options.handle_process_control = true; |
---|
628 | if (graphics) |
---|
629 | { |
---|
630 | options.backwards_compatible_graphics = true; |
---|
631 | } |
---|
632 | |
---|
633 | // boinc_init_options(&options); |
---|
634 | |
---|
635 | fprintf (stderr, "vmwrapper: starting\n"); |
---|
636 | |
---|
637 | boinc_get_init_data (aid); |
---|
638 | |
---|
639 | retval = parse_job_file (); |
---|
640 | if (retval) |
---|
641 | { |
---|
642 | fprintf (stderr, "can't parse job file: %d\n", retval); |
---|
643 | boinc_finish (retval); |
---|
644 | } |
---|
645 | |
---|
646 | read_checkpoint (ntasks, cpu); |
---|
647 | if (ntasks > (int) tasks.size ()) |
---|
648 | { |
---|
649 | fprintf (stderr, "Checkpoint file: ntasks %d too large\n", ntasks); |
---|
650 | boinc_finish (1); |
---|
651 | } |
---|
652 | |
---|
653 | for (i = 0; i < tasks.size (); i++) |
---|
654 | { |
---|
655 | total_weight += tasks[i].weight; |
---|
656 | } |
---|
657 | |
---|
658 | |
---|
659 | |
---|
660 | |
---|
661 | VixHandle hostHandle = VIX_INVALID_HANDLE; |
---|
662 | VixHandle jobHandle = VIX_INVALID_HANDLE; |
---|
663 | VixError err; |
---|
664 | string vmxFilename; |
---|
665 | |
---|
666 | jobHandle = VixHost_Connect (VIX_API_VERSION, VIX_SERVICEPROVIDER_VMWARE_SERVER, NULL, 0, NULL, NULL, 0, VIX_INVALID_HANDLE, NULL, NULL); |
---|
667 | |
---|
668 | err = VixJob_Wait (jobHandle, VIX_PROPERTY_JOB_RESULT_HANDLE, &hostHandle, VIX_PROPERTY_NONE); |
---|
669 | |
---|
670 | if (VIX_OK != err) |
---|
671 | { |
---|
672 | fprintf (stderr, "Unable to connect to server!\n"); |
---|
673 | fprintf (stderr, "Error message: \"%s\"\n", Vix_GetErrorText (err, NULL)); |
---|
674 | VixHost_Disconnect (hostHandle); |
---|
675 | boinc_finish(1); |
---|
676 | } |
---|
677 | |
---|
678 | fprintf (stderr, "Connected to server.\n"); |
---|
679 | |
---|
680 | |
---|
681 | Vix_ReleaseHandle (jobHandle); |
---|
682 | jobHandle = VIX_INVALID_HANDLE; |
---|
683 | |
---|
684 | |
---|
685 | for (i = 0; i < tasks.size (); i++) |
---|
686 | { |
---|
687 | |
---|
688 | fprintf(stderr,"Processing task %d.\n", i); |
---|
689 | |
---|
690 | TASK & task = tasks[i]; |
---|
691 | |
---|
692 | // w += task.weight; |
---|
693 | |
---|
694 | // this line is to do with cpu time storage |
---|
695 | // if ((int) i < ntasks) |
---|
696 | // continue; |
---|
697 | |
---|
698 | double frac_done = w / total_weight; |
---|
699 | |
---|
700 | |
---|
701 | fprintf(stderr, "Task %d.\n========\n\n", i+1); |
---|
702 | |
---|
703 | |
---|
704 | fprintf(stderr, "VM file root is %s\n", task.vm.c_str ()); |
---|
705 | |
---|
706 | string vm_file_in_1, vm_file_in_2; |
---|
707 | char vm_file_1[1024], vm_file_2[1024]; |
---|
708 | |
---|
709 | string pwd = get_current_dir_name (); |
---|
710 | |
---|
711 | |
---|
712 | // VMWare Server 1 requires full path |
---|
713 | vm_file_in_1 = pwd + "/" + task.vm + ".vmx"; |
---|
714 | vm_file_in_2 = pwd + "/" + task.vm + ".vmdk"; |
---|
715 | |
---|
716 | // fprintf (stderr, "Input filenames are %s, %s\n", |
---|
717 | // vm_file_in_1.c_str (), vm_file_in_2.c_str ()); |
---|
718 | |
---|
719 | boinc_resolve_filename (vm_file_in_1.c_str (), vm_file_1, 1024); |
---|
720 | boinc_resolve_filename (vm_file_in_2.c_str (), vm_file_2, 1024); |
---|
721 | |
---|
722 | // fprintf (stderr, "Resolved to %s, %s\n", vm_file_1, vm_file_2); |
---|
723 | |
---|
724 | if (access (vm_file_1, R_OK)) |
---|
725 | { |
---|
726 | fprintf (stderr, "Unable to access VM file %s!\n",vm_file_1); |
---|
727 | boinc_finish (1); |
---|
728 | } |
---|
729 | |
---|
730 | if (access (vm_file_2, R_OK)) |
---|
731 | { |
---|
732 | fprintf (stderr, "Unable to access VM file %s!\n",vm_file_2); |
---|
733 | boinc_finish (1); |
---|
734 | } |
---|
735 | |
---|
736 | |
---|
737 | // Register VM |
---|
738 | jobHandle = VixHost_RegisterVM (hostHandle, vm_file_1, NULL, NULL); |
---|
739 | err = VixJob_Wait (jobHandle, VIX_PROPERTY_NONE); |
---|
740 | check_vm_result(err, hostHandle, "Unable to register VM."); |
---|
741 | fprintf (stderr, "Registered virtual machine with the server.\n"); |
---|
742 | |
---|
743 | VixHandle vmHandle = VIX_INVALID_HANDLE; |
---|
744 | |
---|
745 | // Open VM |
---|
746 | jobHandle = VixVM_Open (hostHandle, vm_file_1, NULL, NULL); |
---|
747 | err = VixJob_Wait (jobHandle, VIX_PROPERTY_JOB_RESULT_HANDLE, &vmHandle, VIX_PROPERTY_NONE); |
---|
748 | check_vm_result(err, hostHandle, "Unable to open VM."); |
---|
749 | fprintf (stderr, "Got handle.\n"); |
---|
750 | |
---|
751 | // Power on VM |
---|
752 | jobHandle = VixVM_PowerOn (vmHandle, 0, VIX_INVALID_HANDLE, NULL, NULL); |
---|
753 | err = VixJob_Wait (jobHandle, VIX_PROPERTY_NONE); |
---|
754 | check_vm_result(err, hostHandle, "Unable to power on VM."); |
---|
755 | fprintf (stderr, "VM is now on (if it wasn't already).\n"); |
---|
756 | |
---|
757 | // Wait for guest OS to start |
---|
758 | jobHandle = VixVM_WaitForToolsInGuest (vmHandle, 0, NULL, NULL); |
---|
759 | err = VixJob_Wait (jobHandle, VIX_PROPERTY_NONE); |
---|
760 | check_vm_result(err, hostHandle, "Did not get confirmation of VMWare Tools."); |
---|
761 | |
---|
762 | // Log in |
---|
763 | jobHandle = VixVM_LoginInGuest (vmHandle, task.user.c_str(), task.password.c_str(), 0, NULL, NULL); |
---|
764 | err = VixJob_Wait (jobHandle, VIX_PROPERTY_NONE); |
---|
765 | check_vm_result(err, hostHandle, "Unable to log in."); |
---|
766 | fprintf (stderr, "Logged in. Preparing job...\n"); |
---|
767 | |
---|
768 | |
---|
769 | string guestfile = task.datadir + "/" + task.innerjob; |
---|
770 | string st_hostfile = pwd + "/" + task.innerjob; |
---|
771 | char hostfile[1024]; |
---|
772 | |
---|
773 | boinc_resolve_filename (st_hostfile.c_str (), hostfile, 1024); |
---|
774 | |
---|
775 | jobHandle = VixVM_CopyFileFromHostToGuest (vmHandle, hostfile, guestfile.c_str(), 0, VIX_INVALID_HANDLE, NULL, NULL); |
---|
776 | err = VixJob_Wait (jobHandle, VIX_PROPERTY_NONE); |
---|
777 | check_vm_result(err, hostHandle, "Unable to copy executable."); |
---|
778 | |
---|
779 | Vix_ReleaseHandle (jobHandle); |
---|
780 | |
---|
781 | // Changing permissions, do we really need to do this? |
---|
782 | jobHandle = VixVM_RunProgramInGuest (vmHandle, "/bin/chmod", ("+x " + guestfile).c_str (), 0, VIX_INVALID_HANDLE, NULL, NULL); |
---|
783 | err = VixJob_Wait (jobHandle, VIX_PROPERTY_NONE); |
---|
784 | check_vm_result(err, hostHandle, "Unable to change permissions."); |
---|
785 | |
---|
786 | Vix_ReleaseHandle (jobHandle); |
---|
787 | |
---|
788 | |
---|
789 | fprintf (stderr, "Copying in input...\n"); |
---|
790 | |
---|
791 | for(int files=0;files<task.data.size();files++) |
---|
792 | { |
---|
793 | fprintf(stderr,"Processing: %s\n",task.data[files].c_str()); |
---|
794 | |
---|
795 | // should use boinc_resolve_filename here |
---|
796 | jobHandle = VixVM_CopyFileFromHostToGuest (vmHandle, (pwd + "/" + task.data[files]).c_str(), (task.datadir + "/" + task.data[files]).c_str(), |
---|
797 | 0, VIX_INVALID_HANDLE, NULL, NULL); |
---|
798 | err = VixJob_Wait (jobHandle, VIX_PROPERTY_NONE); |
---|
799 | check_vm_result(err, hostHandle, "Error copying in file."); |
---|
800 | |
---|
801 | Vix_ReleaseHandle (jobHandle); |
---|
802 | |
---|
803 | } |
---|
804 | |
---|
805 | |
---|
806 | |
---|
807 | |
---|
808 | |
---|
809 | |
---|
810 | // Run the target program. |
---|
811 | fprintf (stderr, "Running main program: "); |
---|
812 | jobHandle = VixVM_RunProgramInGuest (vmHandle, guestfile.c_str (), "", 0, VIX_INVALID_HANDLE, NULL, NULL); |
---|
813 | |
---|
814 | Bool complete=false; |
---|
815 | |
---|
816 | task.starting_cpu = cpu; |
---|
817 | task.checkpoint_cpu_time = cpu; |
---|
818 | |
---|
819 | |
---|
820 | BOINC_STATUS status; |
---|
821 | VixHandle innerJobHandle; |
---|
822 | VixHandle snapshotHandle; |
---|
823 | |
---|
824 | while(!complete) |
---|
825 | { |
---|
826 | if(boinc_time_to_checkpoint()) |
---|
827 | fprintf(stderr,"Time to checkpoint run here...\n"); |
---|
828 | |
---|
829 | |
---|
830 | |
---|
831 | boinc_get_status (&status); |
---|
832 | |
---|
833 | if (status.no_heartbeat || status.quit_request || status.abort_request) |
---|
834 | { |
---|
835 | VixHost_Disconnect (hostHandle); |
---|
836 | boinc_finish(0); |
---|
837 | } |
---|
838 | |
---|
839 | |
---|
840 | // contents of this control structure needs fixing, since suspend doesn't work |
---|
841 | if (status.suspended) |
---|
842 | { |
---|
843 | if (!task.suspended) |
---|
844 | { |
---|
845 | // innerJobHandle = VixVM_Suspend(vmHandle,0,VIX_INVALID_HANDLE,NULL,NULL); |
---|
846 | // err = VixJob_Wait(innerJobHandle, VIX_PROPERTY_JOB_RESULT_HANDLE, &snapshotHandle, VIX_PROPERTY_NONE); |
---|
847 | // check_vm_result(err, hostHandle, "Error pausing VM."); |
---|
848 | task.stop(); |
---|
849 | } |
---|
850 | } |
---|
851 | else |
---|
852 | { |
---|
853 | if (task.suspended) |
---|
854 | { |
---|
855 | // innerJobHandle = VixVM_Unpause(vmHandle,0,VIX_INVALID_HANDLE,NULL,NULL); |
---|
856 | // err = VixJob_Wait(innerJobHandle, VIX_PROPERTY_JOB_RESULT_HANDLE, &snapshotHandle, VIX_PROPERTY_NONE); |
---|
857 | // check_vm_result(err, hostHandle, "Error unpausing VM."); |
---|
858 | task.resume (); |
---|
859 | } |
---|
860 | } |
---|
861 | |
---|
862 | |
---|
863 | // send_status_message (task, frac_done); |
---|
864 | // fprintf(stderr,"Partial credit check here...\n"); |
---|
865 | |
---|
866 | // double current_cpu_time = task.starting_cpu + task.cpu_time (); |
---|
867 | |
---|
868 | // if (task.has_checkpointed ()) |
---|
869 | // { |
---|
870 | // task.checkpoint_cpu_time = current_cpu_time; |
---|
871 | // } |
---|
872 | |
---|
873 | // not sure how to do this yet |
---|
874 | // boinc_report_app_status (current_cpu_time, task.checkpoint_cpu_time, frac_done); |
---|
875 | |
---|
876 | |
---|
877 | boinc_sleep(POLL_PERIOD); |
---|
878 | |
---|
879 | |
---|
880 | if(!task.suspended) { |
---|
881 | err = VixJob_CheckCompletion(jobHandle, &complete); |
---|
882 | check_vm_result(err, hostHandle, "Error polling for job completion."); |
---|
883 | } |
---|
884 | } |
---|
885 | |
---|
886 | |
---|
887 | cpu += task.final_cpu_time; |
---|
888 | // write_checkpoint here is necessary to keep track of CPU time |
---|
889 | // we either use the time measured, or the time from within |
---|
890 | write_checkpoint (i + 1, cpu); |
---|
891 | |
---|
892 | err = VixJob_Wait (jobHandle, VIX_PROPERTY_NONE); |
---|
893 | check_vm_result(err, hostHandle, "Error running target program."); |
---|
894 | fprintf (stderr, "done.\n"); |
---|
895 | |
---|
896 | |
---|
897 | |
---|
898 | |
---|
899 | Vix_ReleaseHandle (jobHandle); |
---|
900 | |
---|
901 | |
---|
902 | fprintf(stderr, "Copying out output...\n"); |
---|
903 | |
---|
904 | for(int files=0;files<task.output.size();files++) |
---|
905 | { |
---|
906 | fprintf(stderr,"Processing: %s\n",task.output[files].c_str()); |
---|
907 | |
---|
908 | |
---|
909 | // FIXME: need to tell BOINC about these files |
---|
910 | jobHandle = VixVM_CopyFileFromGuestToHost (vmHandle, (task.datadir + "/" + task.output[0]).c_str(), |
---|
911 | (pwd + "/" + task.output[files]).c_str (), 0, VIX_INVALID_HANDLE, NULL, NULL); |
---|
912 | err = VixJob_Wait (jobHandle, VIX_PROPERTY_NONE); |
---|
913 | check_vm_result(err, hostHandle, "Error copying out file."); |
---|
914 | |
---|
915 | Vix_ReleaseHandle (jobHandle); |
---|
916 | |
---|
917 | jobHandle = VixVM_RunProgramInGuest (vmHandle, "/bin/rm", (task.datadir + "/" + task.output[files]).c_str(), 0, VIX_INVALID_HANDLE, NULL, NULL); |
---|
918 | err = VixJob_Wait (jobHandle, VIX_PROPERTY_NONE); |
---|
919 | check_vm_result(err, hostHandle, "Error deleting file."); |
---|
920 | |
---|
921 | Vix_ReleaseHandle (jobHandle); |
---|
922 | } |
---|
923 | |
---|
924 | |
---|
925 | // now delete the executable, the output and the data |
---|
926 | fprintf(stderr, "Sanitising virtual machine...\n"); |
---|
927 | |
---|
928 | for(int files=0;files<task.data.size();files++) |
---|
929 | { |
---|
930 | fprintf(stderr,"Removing: %s\n",task.data[files].c_str()); |
---|
931 | |
---|
932 | jobHandle = VixVM_RunProgramInGuest (vmHandle, "/bin/rm", (task.datadir + "/" + task.data[files]).c_str(), 0, VIX_INVALID_HANDLE, NULL, NULL); |
---|
933 | err = VixJob_Wait (jobHandle, VIX_PROPERTY_NONE); |
---|
934 | check_vm_result(err, hostHandle, "Error deleting file."); |
---|
935 | |
---|
936 | Vix_ReleaseHandle (jobHandle); |
---|
937 | } |
---|
938 | |
---|
939 | |
---|
940 | fprintf(stderr,"Removing executable.\n"); |
---|
941 | jobHandle = VixVM_RunProgramInGuest (vmHandle, "/bin/rm", guestfile.c_str(), 0, VIX_INVALID_HANDLE, NULL, NULL); |
---|
942 | err = VixJob_Wait (jobHandle, VIX_PROPERTY_NONE); |
---|
943 | check_vm_result(err, hostHandle, "Error deleting file."); |
---|
944 | |
---|
945 | Vix_ReleaseHandle (jobHandle); |
---|
946 | |
---|
947 | |
---|
948 | |
---|
949 | } |
---|
950 | |
---|
951 | boinc_finish (0); |
---|
952 | } |
---|
953 | |
---|
954 | |
---|
955 | #ifdef _WIN32 |
---|
956 | |
---|
957 | int WINAPI |
---|
958 | WinMain (HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR Args, int WinMode) |
---|
959 | { |
---|
960 | LPSTR command_line; |
---|
961 | char *argv[100]; |
---|
962 | int argc; |
---|
963 | |
---|
964 | command_line = GetCommandLine (); |
---|
965 | argc = parse_command_line (command_line, argv); |
---|
966 | return main (argc, argv); |
---|
967 | } |
---|
968 | #endif |
---|