vboxwrapper.cpp 51.7 KB
Newer Older
Rom Walton's avatar
Rom Walton committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2010-2012 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.

18
// BOINC VirtualBox wrapper; lets you run apps in VMs
Rom Walton's avatar
Rom Walton committed
19
// see: http://boinc.berkeley.edu/trac/wiki/VboxApps
20
21
22
//
// usage: vboxwrapper [options]
//
23
// --trickle X      send a trickle-up message reporting elapsed time every X sec
Rom Walton's avatar
Rom Walton committed
24
25
//                  (use this for credit granting if your app does its
//                  own job management, like CernVM).
26
27
28
29
30
31
32
// --nthreads N     create a VM with N threads.
// --vmimage file   Use "file" as the VM image.
//                  This lets you create an app version with several images,
//                  and the app_plan function can decide which one to use
//                  for the particular host.
// --register_only  Register the VM but don't run it.
//                  Useful for debugging; see the wiki page
Rom Walton's avatar
Rom Walton committed
33
34
35
36
//
// Handles:
// - suspend/resume/quit/abort
// - reporting CPU time
37
38
39
// - loss of heartbeat from client
// - checkpoint (using snapshots)
// - a bunch of other stuff; see the wiki page
Rom Walton's avatar
Rom Walton committed
40
41
42
43
44
45
46
47
48
49
50
//
// Contributors:
// Andrew J. Younge (ajy4490 AT umiacs DOT umd DOT edu)
// Jie Wu <jiewu AT cern DOT ch>
// Daniel Lombraña González <teleyinex AT gmail DOT com>
// Rom Walton
// David Anderson

#ifdef _WIN32
#include "boinc_win.h"
#include "win_util.h"
51
52
53
#include "atlcomcli.h"
#include "atlstr.h"
#include "mscom/VirtualBox.h"
Rom Walton's avatar
Rom Walton committed
54
55
56
57
58
59
#else
#include <vector>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
60
#include <cmath>
Rom Walton's avatar
Rom Walton committed
61
62
63
64
#include <string>
#include <unistd.h>
#endif

65
#include "version.h"
Rom Walton's avatar
Rom Walton committed
66
67
68
#include "boinc_api.h"
#include "diagnostics.h"
#include "filesys.h"
69
#include "md5_file.h"
Rom Walton's avatar
Rom Walton committed
70
71
72
73
74
75
76
77
78
#include "parse.h"
#include "str_util.h"
#include "str_replace.h"
#include "util.h"
#include "error_numbers.h"
#include "procinfo.h"
#include "vboxwrapper.h"
#include "vbox.h"

79
80
81
82
83
84
#ifdef _WIN32
#include "vbox_win.h"
#else
#include "vbox_unix.h"
#endif

Rom Walton's avatar
Rom Walton committed
85
86
87
using std::vector;
using std::string;

88
89
90
double elapsed_time = 0;
    // job's total elapsed time (over all sessions)
double trickle_period = 0;
Rom Walton's avatar
Rom Walton committed
91

92
93
94
95
96
97
98
99
100
bool is_boinc_client_version_newer(APP_INIT_DATA& aid, int maj, int min, int rel) {
    if (maj < aid.major_version) return true;
    if (maj > aid.major_version) return false;
    if (min < aid.minor_version) return true;
    if (min > aid.minor_version) return false;
    if (rel < aid.release) return true;
    return false;
}

Rom Walton's avatar
Rom Walton committed
101
102
103
104
105
106
107
108
109
char* vboxwrapper_msg_prefix(char* sbuf, int len) {
    char buf[256];
    struct tm tm;
    struct tm *tmp = &tm;
    int n;

    time_t x = time(0);
#ifdef _WIN32
#ifdef __MINGW32__
110
    if ((tmp = localtime(&x)) == NULL)
Rom Walton's avatar
Rom Walton committed
111
#else
112
    if (localtime_s(&tm, &x) == EINVAL)
Rom Walton's avatar
Rom Walton committed
113
114
#endif
#else
115
    if (localtime_r(&x, &tm) == NULL)
Rom Walton's avatar
Rom Walton committed
116
#endif
117
    {
Rom Walton's avatar
Rom Walton committed
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
        strcpy(sbuf, "localtime() failed");
        return sbuf;
    }
    if (strftime(buf, sizeof(buf)-1, "%Y-%m-%d %H:%M:%S", tmp) == 0) {
        strcpy(sbuf, "strftime() failed");
        return sbuf;
    }
#ifdef _WIN32
    n = _snprintf(sbuf, len, "%s (%d):", buf, GetCurrentProcessId());
#else
    n = snprintf(sbuf, len, "%s (%d):", buf, getpid());
#endif
    if (n < 0) {
        strcpy(sbuf, "sprintf() failed");
        return sbuf;
    }
    sbuf[len-1] = 0;    // just in case
    return sbuf;
}

138
139
int parse_port_forward(VBOX_VM& vm, XML_PARSER& xp) {
    char buf2[256];
140
    int host_port=0, guest_port=0, nports=1;
141
    bool is_remote;
142
143
    while (!xp.get_tag()) {
        if (xp.match_tag("/port_forward")) {
144
145
146
147
148
149
150
            if (!host_port) {
                fprintf(stderr,
                    "%s parse_port_forward(): unspecified host port\n",
                    vboxwrapper_msg_prefix(buf2, sizeof(buf2))
                );
                return ERR_XML_PARSE;
            }
151
            if (!guest_port) {
152
153
154
                fprintf(stderr,
                    "%s parse_port_forward(): unspecified guest port\n",
                    vboxwrapper_msg_prefix(buf2, sizeof(buf2))
155
                );
156
                return ERR_XML_PARSE;
157
158
159
160
161
162
            }
            PORT_FORWARD pf;
            pf.host_port = host_port;
            pf.guest_port = guest_port;
            pf.is_remote = is_remote;
            for (int i=0; i<nports; i++) {
163
                vm.port_forwards.push_back(pf);
164
165
166
167
168
169
170
171
172
173
                pf.host_port++;
                pf.guest_port++;
            }
            return 0;
        }
        else if (xp.parse_bool("is_remote", is_remote)) continue;
        else if (xp.parse_int("host_port", host_port)) continue;
        else if (xp.parse_int("guest_port", guest_port)) continue;
        else if (xp.parse_int("nports", nports)) continue;
        else {
174
175
176
177
            fprintf(stderr,
                "%s parse_port_forward(): unparsed %s\n",
                vboxwrapper_msg_prefix(buf2, sizeof(buf2)),
                xp.parsed_tag
178
            );
179
180
181
182
        }
    }
    return ERR_XML_PARSE;
}
Rom Walton's avatar
Rom Walton committed
183

184
int parse_job_file(VBOX_VM& vm) {
185
    INTERMEDIATE_UPLOAD iu;
Rom Walton's avatar
Rom Walton committed
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
    MIOFILE mf;
    string str;
    char buf[1024], buf2[256];

    boinc_resolve_filename(JOB_FILENAME, buf, sizeof(buf));
    FILE* f = boinc_fopen(buf, "r");
    if (!f) {
        fprintf(stderr,
            "%s can't open job file %s\n",
            vboxwrapper_msg_prefix(buf2, sizeof(buf2)), buf
        );
        return ERR_FOPEN;
    }
    mf.init_file(f);
    XML_PARSER xp(&mf);

    if (!xp.parse_start("vbox_job")) return ERR_XML_PARSE;
    while (!xp.get_tag()) {
        if (!xp.is_tag) {
            fprintf(stderr, "%s parse_job_file(): unexpected text %s\n",
                vboxwrapper_msg_prefix(buf, sizeof(buf)), xp.parsed_tag
            );
            continue;
        }
        if (xp.match_tag("/vbox_job")) {
            fclose(f);
            return 0;
        }
        else if (xp.parse_string("vm_disk_controller_type", vm.vm_disk_controller_type)) continue;
        else if (xp.parse_string("vm_disk_controller_model", vm.vm_disk_controller_model)) continue;
        else if (xp.parse_string("os_name", vm.os_name)) continue;
217
        else if (xp.parse_double("memory_size_mb", vm.memory_size_mb)) continue;
Rom Walton's avatar
Rom Walton committed
218
        else if (xp.parse_double("job_duration", vm.job_duration)) continue;
219
        else if (xp.parse_double("minimum_checkpoint_interval", vm.minimum_checkpoint_interval)) continue;
Rom Walton's avatar
Rom Walton committed
220
221
222
        else if (xp.parse_string("fraction_done_filename", vm.fraction_done_filename)) continue;
        else if (xp.parse_bool("enable_cern_dataformat", vm.enable_cern_dataformat)) continue;
        else if (xp.parse_bool("enable_network", vm.enable_network)) continue;
223
        else if (xp.parse_bool("network_bridged_mode", vm.network_bridged_mode)) continue;
Rom Walton's avatar
Rom Walton committed
224
225
        else if (xp.parse_bool("enable_shared_directory", vm.enable_shared_directory)) continue;
        else if (xp.parse_bool("enable_floppyio", vm.enable_floppyio)) continue;
226
        else if (xp.parse_bool("enable_cache_disk", vm.enable_cache_disk)) continue;
227
        else if (xp.parse_bool("enable_isocontextualization", vm.enable_isocontextualization)) continue;
Rom Walton's avatar
Rom Walton committed
228
        else if (xp.parse_bool("enable_remotedesktop", vm.enable_remotedesktop)) continue;
229
230
        else if (xp.parse_int("pf_guest_port", vm.pf_guest_port)) continue;
        else if (xp.parse_int("pf_host_port", vm.pf_host_port)) continue;
Rom Walton's avatar
Rom Walton committed
231
        else if (xp.parse_string("copy_to_shared", str)) {
232
233
234
235
236
            vm.copy_to_shared.push_back(str);
            continue;
        }
        else if (xp.parse_string("trickle_trigger_file", str)) {
            vm.trickle_trigger_files.push_back(str);
Rom Walton's avatar
Rom Walton committed
237
238
            continue;
        }
239
240
241
242
243
244
        else if (xp.parse_string("intermediate_upload_file", str)) {
            iu.clear();
            iu.file = str;
            vm.intermediate_upload_files.push_back(iu);
            continue;
        }
245
        else if (xp.parse_string("completion_trigger_file", str)) {
246
            vm.completion_trigger_file = str;
247
248
            continue;
        }
249
250
251
252
        else if (xp.parse_string("temporary_exit_trigger_file", str)) {
            vm.temporary_exit_trigger_file = str;
            continue;
        }
253
        else if (xp.match_tag("port_forward")) {
254
            parse_port_forward(vm, xp);
255
        }
256
257
258
        fprintf(stderr, "%s parse_job_file(): unexpected tag %s\n",
            vboxwrapper_msg_prefix(buf, sizeof(buf)), xp.parsed_tag
        );
Rom Walton's avatar
Rom Walton committed
259
260
261
262
263
    }
    fclose(f);
    return ERR_XML_PARSE;
}

264
void write_checkpoint(double elapsed, double cpu, VBOX_VM& vm) {
Rom Walton's avatar
Rom Walton committed
265
266
    FILE* f = fopen(CHECKPOINT_FILENAME, "w");
    if (!f) return;
267
    fprintf(f, "%f %f %d %d\n", elapsed, cpu, vm.pf_host_port, vm.rd_host_port);
Rom Walton's avatar
Rom Walton committed
268
269
270
    fclose(f);
}

271
void read_checkpoint(double& elapsed, double& cpu, VBOX_VM& vm) {
Rom Walton's avatar
Rom Walton committed
272
    double c;
273
    double e;
Rom Walton's avatar
Rom Walton committed
274
275
    int pf_host;
    int rd_host;
276
    elapsed = 0.0;
Rom Walton's avatar
Rom Walton committed
277
278
279
280
281
    cpu = 0.0;
    vm.pf_host_port = 0;
    vm.rd_host_port = 0;
    FILE* f = fopen(CHECKPOINT_FILENAME, "r");
    if (!f) return;
282
    int n = fscanf(f, "%lf %lf %d %d", &e, &c, &pf_host, &rd_host);
Rom Walton's avatar
Rom Walton committed
283
    fclose(f);
284
285
    if (n != 4) return;
    elapsed = e;
Rom Walton's avatar
Rom Walton committed
286
287
288
289
290
291
    cpu = c;
    vm.pf_host_port = pf_host;
    vm.rd_host_port = rd_host;
}

void read_fraction_done(double& frac_done, VBOX_VM& vm) {
292
    char path[MAXPATHLEN];
Rom Walton's avatar
Rom Walton committed
293
294
295
    char buf[256];
    double temp, frac = 0;

296
297
    sprintf(path, "shared/%s", vm.fraction_done_filename.c_str());
    FILE* f = fopen(path, "r");
Rom Walton's avatar
Rom Walton committed
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
    if (!f) return;

    // read the last line of the file
    //
    fseek(f, -32, SEEK_END);
    while (!feof(f)) {
        char* p = fgets(buf, 256, f);
        if (p == NULL) break;
        int n = sscanf(buf, "%lf", &temp);
        if (n == 1) frac = temp;
    }
    fclose(f);

    if (frac < 0) {
        frac = 0;
    }
    if (frac > 1) {
        frac = 1;
    }

    frac_done = frac;
}

321
void read_completion_file_info(unsigned long& exit_code, bool& is_notice, string& message, VBOX_VM& vm) {
322
323
324
325
326
327
328
329
330
331
332
333
    char path[MAXPATHLEN];
    char buf[1024];

    exit_code = 0;
    message = "";

    sprintf(path, "shared/%s", vm.completion_trigger_file.c_str());
    FILE* f = fopen(path, "r");
    if (f) {
        if (fgets(buf, 1024, f) != NULL) {
            exit_code = atoi(buf);
        }
334
335
336
        if (fgets(buf, 1024, f) != NULL) {
            is_notice = atoi(buf);
        }
337
338
339
340
341
342
343
        while (fgets(buf, 1024, f) != NULL) {
            message += buf;
        }
        fclose(f);
    }
}

344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
void read_temporary_exit_file_info(int& temp_delay, bool& is_notice, string& message, VBOX_VM& vm) {
    char path[MAXPATHLEN];
    char buf[1024];

    temp_delay = 0;
    message = "";

    sprintf(path, "shared/%s", vm.temporary_exit_trigger_file.c_str());
    FILE* f = fopen(path, "r");
    if (f) {
        if (fgets(buf, 1024, f) != NULL) {
            temp_delay = atoi(buf);
        }
        if (fgets(buf, 1024, f) != NULL) {
            is_notice = atoi(buf);
        }
        while (fgets(buf, 1024, f) != NULL) {
            message += buf;
        }
        fclose(f);
    }
}

Rom Walton's avatar
Rom Walton committed
367
368
369
// set CPU and network throttling if needed
//
void set_throttles(APP_INIT_DATA& aid, VBOX_VM& vm) {
370
    double x = 0, y = 0;
371

372
    // VirtualBox freaks out if the CPU Usage value is too low to actually
373
374
375
    // do any processing.  It probably wouldn't be so bad if the RDP interface
    // didn't also get hosed by it.
    //
376
377
378
379
    x = aid.global_prefs.cpu_usage_limit;
    // 0 means "no limit"
    //
    if (x == 0.0) x = 100;
380
    // For now set the minimum CPU Usage value to 1.
381
    //
382
383
    if (x < 1) x = 1;
    vm.set_cpu_usage((int)x);
Rom Walton's avatar
Rom Walton committed
384
385
386
387

    // vbox doesn't distinguish up and down bandwidth; use the min of the prefs
    //
    x = aid.global_prefs.max_bytes_sec_up;
388
    y = aid.global_prefs.max_bytes_sec_down;
Rom Walton's avatar
Rom Walton committed
389
    if (y) {
390
        if (!x || y < x) {
Rom Walton's avatar
Rom Walton committed
391
392
393
394
            x = y;
        }
    }
    if (x) {
395
        vm.set_network_usage((int)(x/1024));
Rom Walton's avatar
Rom Walton committed
396
    }
397

Rom Walton's avatar
Rom Walton committed
398
399
400
}

// If the Floppy device has been specified, initialize its state so that
401
402
403
404
// it contains the contents of the init_data.xml file.
// In theory this would allow network enabled VMs to know about
// proxy server configurations either specified by the volunteer
// or automatically detected by the client.
Rom Walton's avatar
Rom Walton committed
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
//
// CERN decided they only needed a small subset of the data and changed the
// data format to 'name=value\n' pairs.  So if we are running under their
// environment set things up accordingly.
//
void set_floppy_image(APP_INIT_DATA& aid, VBOX_VM& vm) {
    int retval;
    char buf[256];
    std::string scratch;

    if (vm.enable_floppyio) {
        scratch = "";
        if (!vm.enable_cern_dataformat) {
            retval = read_file_string(INIT_DATA_FILE, scratch);
            if (retval) {
                fprintf(stderr,
                    "%s can't write init_data.xml to floppy abstration device\n",
                    vboxwrapper_msg_prefix(buf, sizeof(buf))
                );
            }
        } else {
            // Per: https://github.com/stig/json-framework/issues/48
            //
            // Use %.17g to represent doubles
            //
            scratch  = "BOINC_USERNAME=" + string(aid.user_name) + "\n";
            scratch += "BOINC_AUTHENTICATOR=" + string(aid.authenticator) + "\n";

            sprintf(buf, "%d", aid.userid);
            scratch += "BOINC_USERID=" + string(buf) + "\n";

            sprintf(buf, "%d", aid.hostid);
            scratch += "BOINC_HOSTID=" + string(buf) + "\n";

            sprintf(buf, "%.17g", aid.user_total_credit);
            scratch += "BOINC_USER_TOTAL_CREDIT=" + string(buf) + "\n";

            sprintf(buf, "%.17g", aid.host_total_credit);
            scratch += "BOINC_HOST_TOTAL_CREDIT=" + string(buf) + "\n";
        }
        vm.write_floppy(scratch);
    }
}

449
// if there's a port for web graphics, tell the client about it
Rom Walton's avatar
Rom Walton committed
450
//
451
void set_web_graphics_url(VBOX_VM& vm) {
Rom Walton's avatar
Rom Walton committed
452
    char buf[256];
453
454
    for (unsigned int i=0; i<vm.port_forwards.size(); i++) {
        PORT_FORWARD& pf = vm.port_forwards[i];
455
        if (pf.guest_port == vm.pf_guest_port) {
456
457
458
459
            sprintf(buf, "http://localhost:%d", pf.host_port);
            boinc_web_graphics_url(buf);
            break;
        }
Rom Walton's avatar
Rom Walton committed
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
    }
}

// set remote desktop information if needed
//
void set_remote_desktop_info(APP_INIT_DATA& /* aid */, VBOX_VM& vm) {
    char buf[256];

    if (vm.rd_host_port) {
        // Write info to disk
        //
        MIOFILE mf;
        FILE* f = boinc_fopen(REMOTEDESKTOP_FILENAME, "w");
        mf.init_file(f);

        mf.printf(
            "<remote_desktop>\n"
            "  <host_port>%d</host_port>\n"
            "</remote_desktop>\n",
            vm.rd_host_port
        );

        fclose(f);

        sprintf(buf, "localhost:%d", vm.rd_host_port);
        boinc_remote_desktop_addr(buf);
    }
}

489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
// check for trickle trigger files, and send trickles if find them.
//
void VBOX_VM::check_trickle_triggers() {
    char filename[256], path[MAXPATHLEN], buf[256];
    for (unsigned int i=0; i<trickle_trigger_files.size(); i++) {
        strcpy(filename, trickle_trigger_files[i].c_str());
        sprintf(path, "shared/%s", filename);
        if (!boinc_file_exists(path)) continue;
        string text;
        int retval = read_file_string(path, text);
        if (retval) {
            fprintf(stderr,
                "%s can't read trickle trigger file %s\n",
                vboxwrapper_msg_prefix(buf, sizeof(buf)), filename
            );
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
        } else {
            retval = boinc_send_trickle_up(
                filename, const_cast<char*>(text.c_str())
            );
            if (retval) {
                fprintf(stderr,
                    "%s boinc_send_trickle_up() failed: %s\n",
                    vboxwrapper_msg_prefix(buf, sizeof(buf)), boincerror(retval)
                );
            } else {
                fprintf(stderr,
                    "%s sent trickle-up of variety %s\n",
                    vboxwrapper_msg_prefix(buf, sizeof(buf)), filename
                );
            }
519
520
521
522
523
        }
        boinc_delete_file(path);
    }
}

524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
// check for intermediate upload files, and send them if found.
//
void VBOX_VM::check_intermediate_uploads() {
    int retval;
    char filename[256], path[MAXPATHLEN], buf[256];
    for (unsigned int i=0; i<intermediate_upload_files.size(); i++) {
        strcpy(filename, intermediate_upload_files[i].file.c_str());
        sprintf(path, "shared/%s", filename);
        if (!boinc_file_exists(path)) continue;
        if (!intermediate_upload_files[i].reported && !intermediate_upload_files[i].ignore) {
            fprintf(stderr,
                "%s Reporting an intermediate file. (%s)\n",
                vboxwrapper_msg_prefix(buf, sizeof(buf)),
                intermediate_upload_files[i].file.c_str()
            );
            retval = boinc_upload_file(intermediate_upload_files[i].file);
            if (retval) {
                fprintf(stderr,
                    "%s boinc_upload_file() failed: %s\n",
                    vboxwrapper_msg_prefix(buf, sizeof(buf)), boincerror(retval)
                );
                intermediate_upload_files[i].ignore = true;
            } else {
                intermediate_upload_files[i].reported = true;
            }
        } else if (intermediate_upload_files[i].reported && !intermediate_upload_files[i].ignore) {
            retval = boinc_upload_status(intermediate_upload_files[i].file);
            if (!retval) {
                fprintf(stderr,
                    "%s Intermediate file uploaded. (%s)\n",
                    vboxwrapper_msg_prefix(buf, sizeof(buf)),
                    intermediate_upload_files[i].file.c_str()
                );
                intermediate_upload_files[i].ignore = true;
            }
        }
    }
}

563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
// see if it's time to send trickle-up reporting elapsed time
//
void check_trickle_period() {
    char buf[256];
    static double last_trickle_report_time = 0;

    if ((elapsed_time - last_trickle_report_time) < trickle_period) {
        return;
    }
    last_trickle_report_time = elapsed_time;
    fprintf(
        stderr,
        "%s Status Report: Trickle-Up Event.\n",
        vboxwrapper_msg_prefix(buf, sizeof(buf))
    );
    sprintf(buf,
        "<cpu_time>%f</cpu_time>", last_trickle_report_time
    );
    int retval = boinc_send_trickle_up(
        const_cast<char*>("cpu_time"), buf
    );
    if (retval) {
        fprintf(
            stderr,
            "%s Sending Trickle-Up Event failed (%d).\n",
            vboxwrapper_msg_prefix(buf, sizeof(buf)),
            retval
        );
    }
}

Rom Walton's avatar
Rom Walton committed
594
595
int main(int argc, char** argv) {
    int retval;
596
    int loop_iteration = 0;
Rom Walton's avatar
Rom Walton committed
597
598
599
    BOINC_OPTIONS boinc_options;
    VBOX_VM vm;
    APP_INIT_DATA aid;
600
    double random_checkpoint_factor = 0;
Rom Walton's avatar
Rom Walton committed
601
    double fraction_done = 0;
602
    double current_cpu_time = 0;
603
604
    double starting_cpu_time = 0;
    double last_checkpoint_time = 0;
Rom Walton's avatar
Rom Walton committed
605
    double last_status_report_time = 0;
606
    double stopwatch_starttime = 0;
Rom Walton's avatar
Rom Walton committed
607
    double stopwatch_endtime = 0;
608
    double stopwatch_elapsedtime = 0;
Rom Walton's avatar
Rom Walton committed
609
610
611
612
    double sleep_time = 0;
    double bytes_sent = 0;
    double bytes_received = 0;
    double ncpus = 0;
613
    double memory_size_mb = 0;
614
    double timeout = 0.0;
Rom Walton's avatar
Rom Walton committed
615
    bool report_net_usage = false;
616
    double net_usage_timer = 600;
617
	int vm_image = 0;
Rom Walton's avatar
Rom Walton committed
618
    unsigned long vm_exit_code = 0;
619
    bool is_notice = false;
620
    int temp_delay = 86400;
621
    string message;
Rom Walton's avatar
Rom Walton committed
622
623
624
625
626
627
628
    char buf[256];


    for (int i=1; i<argc; i++) {
        if (!strcmp(argv[i], "--trickle")) {
            trickle_period = atof(argv[++i]);
        }
629
        if (!strcmp(argv[i], "--ncpus")) {
Rom Walton's avatar
Rom Walton committed
630
631
            ncpus = atof(argv[++i]);
        }
632
633
634
        if (!strcmp(argv[i], "--memory_size_mb")) {
            memory_size_mb = atof(argv[++i]);
        }
635
636
637
        if (!strcmp(argv[i], "--vmimage")) {
            vm_image = atoi(argv[++i]);
        }
Rom Walton's avatar
Rom Walton committed
638
639
640
641
642
        if (!strcmp(argv[i], "--register_only")) {
            vm.register_only = true;
        }
    }

643
644
645
646
647
648
649
650
651
652
653
654
    memset(&boinc_options, 0, sizeof(boinc_options));
    boinc_options.main_program = true;
    boinc_options.check_heartbeat = true;
    boinc_options.handle_process_control = true;
    boinc_init_options(&boinc_options);

    // Prepare environment for detecting system conditions
    //
    boinc_get_init_data_p(&aid);

    // Log banner
    //
Rom Walton's avatar
Rom Walton committed
655
656
    fprintf(
        stderr,
657
        "%s vboxwrapper (%d.%d.%d): starting\n",
658
        vboxwrapper_msg_prefix(buf, sizeof(buf)),
659
660
661
        BOINC_MAJOR_VERSION,
        BOINC_MINOR_VERSION,
        VBOXWRAPPER_RELEASE
Rom Walton's avatar
Rom Walton committed
662
663
    );

664
665
    // Log important information
    //
666
667
668

    // Choose a random interleave value for checkpoint intervals to stagger disk I/O.
    // 
669
670
671
672
673
674
    struct stat vm_image_stat;
    if (-1 == stat(IMAGE_FILENAME_COMPLETE, &vm_image_stat)) {
        srand((int)time(NULL));
    } else {
        srand((int)(vm_image_stat.st_mtime * time(NULL)));
    }
675
676
677
    random_checkpoint_factor = (double)(((int)(drand() * 100000.0)) % 600);
    fprintf(
        stderr,
678
        "%s Feature: Checkpoint interval offset (%d seconds)\n",
679
        vboxwrapper_msg_prefix(buf, sizeof(buf)),
680
        (int)random_checkpoint_factor
681
682
683
684
685
686
687
688
689
690
691
692
693
694
    );

    // Display trickle value if specified
    //
    if (trickle_period > 0.0) {
        fprintf(
            stderr,
            "%s Feature: Enabling trickle-ups (Interval: %f)\n",
            vboxwrapper_msg_prefix(buf, sizeof(buf)), trickle_period
        );
    }

    // Initialize system services
    // 
695
696
697
698
699
700
701
702
703
704
705
706
707
708
#if defined(_WIN32) && defined(USE_WINSOCK)
    WSADATA wsdata;
    retval = WSAStartup( MAKEWORD( 1, 1 ), &wsdata);
    if (retval) {
        fprintf(
            stderr,
            "%s can't initialize winsock: %d\n",
            vboxwrapper_msg_prefix(buf, sizeof(buf)),
            retval
        );
        boinc_finish(retval);
    }
#endif

709
710
711
712
713
714
    // Check for architecture incompatibilities
    // 
#if defined(_WIN32) && defined(_M_IX86)
    if (strstr(aid.host_info.os_version, "x64")) {
        fprintf(
            stderr,
715
            "%s 64-bit version of BOINC is required, please upgrade. Rescheduling execution for a later date.\n",
716
717
718
719
720
721
            vboxwrapper_msg_prefix(buf, sizeof(buf))
        );
        boinc_temporary_exit(86400, "Architecture incompatibility detected.");
    }
#endif

722
723
    // Initialize VM Hypervisor
    //
724
725
726
727
    retval = vm.initialize();
    if (retval) {
        fprintf(
            stderr,
728
            "%s Could not detect VM Hypervisor. Rescheduling execution for a later date.\n",
729
            vboxwrapper_msg_prefix(buf, sizeof(buf))
730
        );
731
        boinc_temporary_exit(86400, "Detection of VM Hypervisor failed.");
732
733
    }

734
735
736
    // Record what version of VirtualBox was used.
    // 
    if (!vm.virtualbox_version.empty()) {
Rom Walton's avatar
Rom Walton committed
737
738
        fprintf(
            stderr,
739
            "%s Detected: VirtualBox %s\n",
Rom Walton's avatar
Rom Walton committed
740
            vboxwrapper_msg_prefix(buf, sizeof(buf)),
741
742
743
744
            vm.virtualbox_version.c_str()
        );
    }

745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
    // Record if anonymous platform was used.
    // 
    if (boinc_file_exists((std::string(aid.project_dir) + std::string("/app_info.xml")).c_str())) {
        fprintf(
            stderr,
            "%s Detected: Anonymous Platform Enabled\n",
            vboxwrapper_msg_prefix(buf, sizeof(buf))
        );
    }

    // Record if the sandboxed configuration is going to be used.
    //
    if (aid.using_sandbox) {
        fprintf(
            stderr,
            "%s Detected: Sandbox Configuration Enabled\n",
            vboxwrapper_msg_prefix(buf, sizeof(buf))
        );
    }

Rom Walton's avatar
Rom Walton committed
765
    // Record which mode VirtualBox should be started in.
766
    //
767
    if (aid.vbox_window || boinc_is_standalone()) {
768
769
770
771
772
773
774
775
776
777
778
        fprintf(
            stderr,
            "%s Detected: Headless Mode Disabled\n",
            vboxwrapper_msg_prefix(buf, sizeof(buf))
        );
        vm.headless = false;
    }

    // Check for invalid confgiurations.
    //
    if (aid.using_sandbox && aid.vbox_window) {
Rom Walton's avatar
Rom Walton committed
779
        vboxwrapper_msg_prefix(buf, sizeof(buf));
780
781
782
        fprintf(
            stderr,
            "%s Invalid configuration detected.\n"
Rom Walton's avatar
Rom Walton committed
783
784
785
            "%s NOTE: BOINC cannot be installed as a service and run VirtualBox in headfull mode at the same time.\n",
            buf,
            buf
786
        );
Rom Walton's avatar
Rom Walton committed
787
        boinc_temporary_exit(86400, "Incompatible configuration detected.");
788
789
    }

790
    // Check against known incompatible versions of VirtualBox.  
791
792
    // VirtualBox 4.2.6 crashes during snapshot operations
    // and 4.2.18 fails to restore from snapshots properly.
793
    //
794
    if ((vm.virtualbox_version.find("4.2.6") != std::string::npos) || 
795
796
        (vm.virtualbox_version.find("4.2.18") != std::string::npos) || 
        (vm.virtualbox_version.find("4.3.0") != std::string::npos) ) {
797
798
799
800
801
        fprintf(
            stderr,
            "%s Incompatible version of VirtualBox detected. Please upgrade to a later version.\n",
            vboxwrapper_msg_prefix(buf, sizeof(buf))
        );
802
803
804
805
        boinc_temporary_exit(86400,
            "Incompatible version of VirtualBox detected; please upgrade.",
            true
        );
806
807
    }

808
809
810
811
    // Check to see if the system is in a state in which we expect to be able to run
    // VirtualBox successfully.  Sometimes the system is in a wierd state after a
    // reboot and the system needs a little bit of time.
    //
812
    if (!vm.is_system_ready(message)) {
813
814
        fprintf(
            stderr,
815
            "%s Could not communicate with VM Hypervisor. Rescheduling execution for a later date.\n",
816
817
            vboxwrapper_msg_prefix(buf, sizeof(buf))
        );
818
        boinc_temporary_exit(86400, message.c_str());
819
820
    }

821
822
    // Parse Job File
    //
823
    retval = parse_job_file(vm);
Rom Walton's avatar
Rom Walton committed
824
825
826
827
828
829
830
831
832
833
    if (retval) {
        fprintf(
            stderr,
            "%s can't parse job file: %d\n",
            vboxwrapper_msg_prefix(buf, sizeof(buf)),
            retval
        );
        boinc_finish(retval);
    }

834
835
    // Record which mode VirtualBox should be started in.
    //
836
837
838
839
840
841
    fprintf(
        stderr,
        "%s Detected: Minimum checkpoint interval (%f seconds)\n",
        vboxwrapper_msg_prefix(buf, sizeof(buf)),
        vm.minimum_checkpoint_interval
    );
842

Rom Walton's avatar
Rom Walton committed
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
    // Validate whatever configuration options we can
    //
    if (vm.enable_shared_directory) {
        if (boinc_file_exists("shared")) {
            if (!is_dir("shared")) {
                fprintf(
                    stderr,
                    "%s 'shared' exists but is not a directory.\n",
                    vboxwrapper_msg_prefix(buf, sizeof(buf))
                );
            }
        } else {
            retval = boinc_mkdir("shared");
            if (retval) {
                fprintf(stderr,
                    "%s couldn't created shared directory: %s.\n",
                    vboxwrapper_msg_prefix(buf, sizeof(buf)),
                    boincerror(retval)
                );
            }
        }
    }

    // Copy files to the shared directory
    //
868
869
    if (vm.enable_shared_directory && vm.copy_to_shared.size()) {
        for (vector<string>::iterator iter = vm.copy_to_shared.begin(); iter != vm.copy_to_shared.end(); iter++) {
Rom Walton's avatar
Rom Walton committed
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
            string source = *iter;
            string destination = string("shared/") + *iter;
            if (!boinc_file_exists(destination.c_str())) {
                if (!boinc_copy(source.c_str(), destination.c_str())) {
                    fprintf(stderr,
                        "%s successfully copied '%s' to the shared directory.\n",
                        vboxwrapper_msg_prefix(buf, sizeof(buf)),
                        source.c_str()
                    );
                } else {
                    fprintf(stderr,
                        "%s failed to copy '%s' to the shared directory.\n",
                        vboxwrapper_msg_prefix(buf, sizeof(buf)),
                        source.c_str()
                    );
                }
            }
        }
    }

890
891
    // Configure Instance specific VM Parameters
    //
Rom Walton's avatar
Rom Walton committed
892
893
894
895
    vm.vm_master_name = "boinc_";
    vm.image_filename = IMAGE_FILENAME_COMPLETE;
    if (boinc_is_standalone()) {
        vm.vm_master_name += "standalone";
896
        vm.vm_master_description = "standalone";
Rom Walton's avatar
Rom Walton committed
897
        if (vm.enable_floppyio) {
898
899
900
            sprintf(buf, "%s.%s",
                FLOPPY_IMAGE_FILENAME, FLOPPY_IMAGE_FILENAME_EXTENSION
            );
Rom Walton's avatar
Rom Walton committed
901
902
903
            vm.floppy_image_filename = buf;
        }
    } else {
904
        vm.vm_master_name += md5_string(std::string(aid.result_name)).substr(0, 16);
905
        vm.vm_master_description = aid.result_name;
906
		if (vm_image) {
907
908
909
            sprintf(buf, "%s_%d.%s",
                IMAGE_FILENAME, vm_image, IMAGE_FILENAME_EXTENSION
            );
910
911
            vm.image_filename = buf;
		}
Rom Walton's avatar
Rom Walton committed
912
        if (vm.enable_floppyio) {
913
914
915
916
            sprintf(buf, "%s_%d.%s",
                FLOPPY_IMAGE_FILENAME, aid.slot,
                FLOPPY_IMAGE_FILENAME_EXTENSION
            );
Rom Walton's avatar
Rom Walton committed
917
918
            vm.floppy_image_filename = buf;
        }
919
920
    }
    if (vm.enable_cache_disk) {
921
        vm.cache_disk_filename = CACHE_DISK_FILENAME;
922
923
    }
    if (vm.enable_isocontextualization) {
924
        vm.iso_image_filename = ISO_IMAGE_FILENAME;
Rom Walton's avatar
Rom Walton committed
925
926
927
928
929
930
931
932
933
934
935
    }
    if (aid.ncpus > 1.0 || ncpus > 1.0) {
        if (ncpus) {
            sprintf(buf, "%d", (int)ceil(ncpus));
        } else {
            sprintf(buf, "%d", (int)ceil(aid.ncpus));
        }
        vm.vm_cpu_count = buf;
    } else {
        vm.vm_cpu_count = "1";
    }
936
937
938
939
940
941
942
943
    if (vm.memory_size_mb > 1.0 || memory_size_mb > 1.0) {
        if (memory_size_mb) {
            sprintf(buf, "%d", (int)ceil(memory_size_mb));
        } else {
            sprintf(buf, "%d", (int)ceil(vm.memory_size_mb));
        }
        vm.vm_memory_size_mb = buf;
    }
Rom Walton's avatar
Rom Walton committed
944
945
946
947
948
    if (aid.vbox_window && !aid.using_sandbox) {
        vm.headless = false;
    }

    // Restore from checkpoint
949
    //
950
    read_checkpoint(elapsed_time, current_cpu_time, vm);
951
    starting_cpu_time = current_cpu_time;
952
    last_checkpoint_time = current_cpu_time;
Rom Walton's avatar
Rom Walton committed
953
954

    // Should we even try to start things up?
955
    //
Rom Walton's avatar
Rom Walton committed
956
957
958
959
    if (vm.job_duration && (elapsed_time > vm.job_duration)) {
        return EXIT_TIME_LIMIT_EXCEEDED;
    }

960
    retval = vm.run((current_cpu_time > 0));
Rom Walton's avatar
Rom Walton committed
961
    if (retval) {
962
        // All 'failure to start' errors are unrecoverable by default
963
        bool   unrecoverable_error = true;
964
        bool   skip_cleanup = false;
965
        bool   do_dump_hypervisor_logs = false;
966
        string error_reason;
967
        const char*  temp_reason = "";
Rom Walton's avatar
Rom Walton committed
968

969
970
971
972
973
        if (VBOXWRAPPER_ERR_RECOVERABLE == retval) {
            error_reason =
                "    BOINC will be notified that it needs to clean up the environment.\n"
                "    This is a temporary problem and so this job will be rescheduled for another time.\n";
            unrecoverable_error = false;
974
            temp_reason = "VM environment needed to be cleaned up.";
975
        } else if (ERR_NOT_EXITED == retval) {
976
977
978
979
980
            error_reason =
                "   NOTE: VM was already running.\n"
                "    BOINC will be notified that it needs to clean up the environment.\n"
                "    This might be a temporary problem and so this job will be rescheduled for another time.\n";
            unrecoverable_error = false;
981
            temp_reason = "VM environment needed to be cleaned up.";
982
983
        } else if (ERR_INVALID_PARAM == retval) {
            unrecoverable_error = false;
984
            temp_reason = "Please upgrade BOINC to the latest version.";
985
            temp_delay = 86400;
986
        } else if (retval == (int)RPC_S_SERVER_UNAVAILABLE) {
987
988
989
990
991
            error_reason =
                "    VboxSvc crashed while attempting to restore the current snapshot.  This is a critical\n"
                "    operation and this job cannot be recovered.\n";
            skip_cleanup = true;
            retval = ERR_EXEC;
992
        } else if (vm.is_logged_failure_vm_extensions_disabled()) {
993
994
            error_reason =
                "   NOTE: BOINC has detected that your computer's processor supports hardware acceleration for\n"
Rom Walton's avatar
Rom Walton committed
995
                "    virtual machines but the hypervisor failed to successfully launch with this feature enabled.\n"
Rom Walton's avatar
Rom Walton committed
996
                "    This means that the hardware acceleration feature has been disabled in the computer's BIOS.\n"
Rom Walton's avatar
Rom Walton committed
997
                "    Please enable this feature in your computer's BIOS.\n"
Rom Walton's avatar
Rom Walton committed
998
999
                "    Intel calls it 'VT-x'\n"
                "    AMD calls it 'AMD-V'\n"
Rom Walton's avatar
Rom Walton committed
1000
                "    More information can be found here: http://en.wikipedia.org/wiki/X86_virtualization\n"
For faster browsing, not all history is shown. View entire blame