vboxwrapper.cpp 54.1 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
// --nthreads N     create a VM with N threads.
27
// --vmimage N      Use "vm_image_N" as the VM image.
28
29
30
31
32
//                  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
//
// Contributors:
42
43
// Rom Walton
// David Anderson
Rom Walton's avatar
Rom Walton committed
44
45
46
47
48
49
50
51
52
53
54
55
56
// 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>

#ifdef _WIN32
#include "boinc_win.h"
#include "win_util.h"
#else
#include <vector>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
57
#include <cmath>
Rom Walton's avatar
Rom Walton committed
58
59
60
61
#include <string>
#include <unistd.h>
#endif

62
#include "version.h"
Rom Walton's avatar
Rom Walton committed
63
64
65
#include "boinc_api.h"
#include "diagnostics.h"
#include "filesys.h"
66
#include "md5_file.h"
Rom Walton's avatar
Rom Walton committed
67
68
69
70
71
72
#include "parse.h"
#include "str_util.h"
#include "str_replace.h"
#include "util.h"
#include "error_numbers.h"
#include "procinfo.h"
73
#include "floppyio.h"
Rom Walton's avatar
Rom Walton committed
74
#include "vboxwrapper.h"
75
#include "vbox_common.h"
76
#ifdef _WIN32
77
78
#include "vbox_mscom42.h"
#include "vbox_mscom43.h"
79
#endif
80
81
#include "vbox_vboxmanage.h"

82

Rom Walton's avatar
Rom Walton committed
83
84
85
using std::vector;
using std::string;

86

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

91
92
93
94
95
96
97
98
99
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
100
101
102
103
104
105
106
107
108
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__
109
    if ((tmp = localtime(&x)) == NULL)
Rom Walton's avatar
Rom Walton committed
110
#else
111
    if (localtime_s(&tm, &x) == EINVAL)
Rom Walton's avatar
Rom Walton committed
112
113
#endif
#else
114
    if (localtime_r(&x, &tm) == NULL)
Rom Walton's avatar
Rom Walton committed
115
#endif
116
    {
Rom Walton's avatar
Rom Walton committed
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
        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;
}

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

183
int parse_job_file(VBOX_VM& vm) {
184
    INTERMEDIATE_UPLOAD iu;
Rom Walton's avatar
Rom Walton committed
185
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
    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;
216
        else if (xp.parse_double("memory_size_mb", vm.memory_size_mb)) continue;
Rom Walton's avatar
Rom Walton committed
217
        else if (xp.parse_double("job_duration", vm.job_duration)) continue;
218
        else if (xp.parse_double("minimum_checkpoint_interval", vm.minimum_checkpoint_interval)) continue;
Rom Walton's avatar
Rom Walton committed
219
220
221
        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;
222
        else if (xp.parse_bool("network_bridged_mode", vm.network_bridged_mode)) continue;
Rom Walton's avatar
Rom Walton committed
223
224
        else if (xp.parse_bool("enable_shared_directory", vm.enable_shared_directory)) continue;
        else if (xp.parse_bool("enable_floppyio", vm.enable_floppyio)) continue;
225
        else if (xp.parse_bool("enable_cache_disk", vm.enable_cache_disk)) continue;
226
        else if (xp.parse_bool("enable_isocontextualization", vm.enable_isocontextualization)) continue;
Rom Walton's avatar
Rom Walton committed
227
        else if (xp.parse_bool("enable_remotedesktop", vm.enable_remotedesktop)) continue;
228
        else if (xp.parse_bool("enable_gbac", vm.enable_gbac)) 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
322
323
324
325
326
327
bool completion_file_exists(VBOX_VM& vm) {
    char path[MAXPATHLEN];
    sprintf(path, "shared/%s", vm.completion_trigger_file.c_str());
    if (boinc_file_exists(path)) return true;
    return false;
}

328
void read_completion_file_info(unsigned long& exit_code, bool& is_notice, string& message, VBOX_VM& vm) {
329
330
331
332
333
334
335
336
337
338
    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) {
Rom Walton's avatar
Rom Walton committed
339
            exit_code = atoi(buf) != 0;
340
        }
341
        if (fgets(buf, 1024, f) != NULL) {
Rom Walton's avatar
Rom Walton committed
342
            is_notice = atoi(buf) != 0;
343
        }
344
345
346
347
348
349
350
        while (fgets(buf, 1024, f) != NULL) {
            message += buf;
        }
        fclose(f);
    }
}

351
352
353
354
355
356
357
bool temporary_exit_file_exists(VBOX_VM& vm) {
    char path[MAXPATHLEN];
    sprintf(path, "shared/%s", vm.temporary_exit_trigger_file.c_str());
    if (boinc_file_exists(path)) return true;
    return false;
}

358
359
360
361
362
363
364
365
366
367
368
369
370
371
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) {
Rom Walton's avatar
Rom Walton committed
372
            is_notice = atoi(buf) != 0;
373
374
375
376
377
378
379
380
        }
        while (fgets(buf, 1024, f) != NULL) {
            message += buf;
        }
        fclose(f);
    }
}

381
382
383
384
385
386
void delete_temporary_exit_trigger_file(VBOX_VM& vm) {
    char path[MAXPATHLEN];
    sprintf(path, "shared/%s", vm.temporary_exit_trigger_file.c_str());
    boinc_delete_file(path);
}

Rom Walton's avatar
Rom Walton committed
387
388
389
// set CPU and network throttling if needed
//
void set_throttles(APP_INIT_DATA& aid, VBOX_VM& vm) {
390
    double x = 0, y = 0;
391

392
    // VirtualBox freaks out if the CPU Usage value is too low to actually
393
394
395
    // do any processing.  It probably wouldn't be so bad if the RDP interface
    // didn't also get hosed by it.
    //
396
397
398
399
    x = aid.global_prefs.cpu_usage_limit;
    // 0 means "no limit"
    //
    if (x == 0.0) x = 100;
400
    // For now set the minimum CPU Usage value to 1.
401
    //
402
403
    if (x < 1) x = 1;
    vm.set_cpu_usage((int)x);
Rom Walton's avatar
Rom Walton committed
404
405
406
407

    // vbox doesn't distinguish up and down bandwidth; use the min of the prefs
    //
    x = aid.global_prefs.max_bytes_sec_up;
408
    y = aid.global_prefs.max_bytes_sec_down;
Rom Walton's avatar
Rom Walton committed
409
    if (y) {
410
        if (!x || y < x) {
Rom Walton's avatar
Rom Walton committed
411
412
413
414
            x = y;
        }
    }
    if (x) {
415
        vm.set_network_usage((int)(x/1024));
Rom Walton's avatar
Rom Walton committed
416
    }
417

Rom Walton's avatar
Rom Walton committed
418
419
420
}

// If the Floppy device has been specified, initialize its state so that
421
422
423
424
// 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
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
//
// 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);
    }
}

469
// if there's a port for web graphics, tell the client about it
Rom Walton's avatar
Rom Walton committed
470
//
471
void set_web_graphics_url(VBOX_VM& vm) {
472
    char buf[256], buf2[256];
473
474
    for (unsigned int i=0; i<vm.port_forwards.size(); i++) {
        PORT_FORWARD& pf = vm.port_forwards[i];
475
        if (pf.guest_port == vm.pf_guest_port) {
476
            sprintf(buf2, "http://localhost:%d", pf.host_port);
477
            fprintf(stderr, "%s Detected: Web Application Enabled (%s)\n",
478
479
                vboxwrapper_msg_prefix(buf, sizeof(buf)),
                buf2
480
            );
481
            boinc_web_graphics_url(buf2);
482
483
            break;
        }
Rom Walton's avatar
Rom Walton committed
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
    }
}

// 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);
    }
}

513
514
// check for trickle trigger files, and send trickles if find them.
//
515
void check_trickle_triggers(VBOX_VM& vm) {
516
    char filename[256], path[MAXPATHLEN], buf[256];
517
518
    for (unsigned int i=0; i<vm.trickle_trigger_files.size(); i++) {
        strcpy(filename, vm.trickle_trigger_files[i].c_str());
519
520
521
522
523
524
525
526
527
        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
            );
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
        } 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
                );
            }
543
544
545
546
547
        }
        boinc_delete_file(path);
    }
}

548
549
// check for intermediate upload files, and send them if found.
//
550
void check_intermediate_uploads(VBOX_VM& vm) {
551
552
    int retval;
    char filename[256], path[MAXPATHLEN], buf[256];
553
554
    for (unsigned int i=0; i<vm.intermediate_upload_files.size(); i++) {
        strcpy(filename, vm.intermediate_upload_files[i].file.c_str());
555
556
        sprintf(path, "shared/%s", filename);
        if (!boinc_file_exists(path)) continue;
557
        if (!vm.intermediate_upload_files[i].reported && !vm.intermediate_upload_files[i].ignore) {
558
559
560
            fprintf(stderr,
                "%s Reporting an intermediate file. (%s)\n",
                vboxwrapper_msg_prefix(buf, sizeof(buf)),
561
                vm.intermediate_upload_files[i].file.c_str()
562
            );
563
            retval = boinc_upload_file(vm.intermediate_upload_files[i].file);
564
565
566
567
568
            if (retval) {
                fprintf(stderr,
                    "%s boinc_upload_file() failed: %s\n",
                    vboxwrapper_msg_prefix(buf, sizeof(buf)), boincerror(retval)
                );
569
                vm.intermediate_upload_files[i].ignore = true;
570
            } else {
571
                vm.intermediate_upload_files[i].reported = true;
572
            }
573
574
        } else if (vm.intermediate_upload_files[i].reported && !vm.intermediate_upload_files[i].ignore) {
            retval = boinc_upload_status(vm.intermediate_upload_files[i].file);
575
576
577
578
            if (!retval) {
                fprintf(stderr,
                    "%s Intermediate file uploaded. (%s)\n",
                    vboxwrapper_msg_prefix(buf, sizeof(buf)),
579
                    vm.intermediate_upload_files[i].file.c_str()
580
                );
581
                vm.intermediate_upload_files[i].ignore = true;
582
583
584
585
586
            }
        }
    }
}

587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
// 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
618
int main(int argc, char** argv) {
619
    int retval = 0;
620
    int loop_iteration = 0;
Rom Walton's avatar
Rom Walton committed
621
622
    BOINC_OPTIONS boinc_options;
    APP_INIT_DATA aid;
623
    VBOX_VM* pVM = NULL;
624
    double random_checkpoint_factor = 0;
Rom Walton's avatar
Rom Walton committed
625
    double fraction_done = 0;
626
    double current_cpu_time = 0;
627
    double starting_cpu_time = 0;
628
629
    double last_checkpoint_cpu_time = 0;
    double last_checkpoint_elapsed_time = 0;
Rom Walton's avatar
Rom Walton committed
630
    double last_status_report_time = 0;
631
    double stopwatch_starttime = 0;
Rom Walton's avatar
Rom Walton committed
632
    double stopwatch_endtime = 0;
633
    double stopwatch_elapsedtime = 0;
Rom Walton's avatar
Rom Walton committed
634
635
636
637
    double sleep_time = 0;
    double bytes_sent = 0;
    double bytes_received = 0;
    double ncpus = 0;
638
    double memory_size_mb = 0;
639
    double timeout = 0.0;
Rom Walton's avatar
Rom Walton committed
640
    bool report_net_usage = false;
641
    double net_usage_timer = 600;
642
	int vm_image = 0;
Rom Walton's avatar
Rom Walton committed
643
    unsigned long vm_exit_code = 0;
644
    bool is_notice = false;
645
    int temp_delay = 86400;
646
    string message;
Rom Walton's avatar
Rom Walton committed
647
648
649
    char buf[256];


650
651
652
653
654
655
    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);

656

657
658
659
660
661
662
    // Prepare environment for detecting system conditions
    //
    boinc_get_init_data_p(&aid);

    // Log banner
    //
Rom Walton's avatar
Rom Walton committed
663
664
    fprintf(
        stderr,
665
        "%s vboxwrapper (%d.%d.%d): starting\n",
666
        vboxwrapper_msg_prefix(buf, sizeof(buf)),
667
668
669
        BOINC_MAJOR_VERSION,
        BOINC_MINOR_VERSION,
        VBOXWRAPPER_RELEASE
Rom Walton's avatar
Rom Walton committed
670
671
    );

672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
    // Initialize system services
    // 
#ifdef _WIN32
    // Initialize the COM subsystem.
    CoInitialize(NULL);

#ifdef 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
#endif


#ifdef _WIN32
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
    // Determine what version of VirtualBox we are using via the registry. Use a
    // namespace specific version of the function because VirtualBox has been known
    // to change the registry location from time to time.
    //
    // NOTE: We cannot use COM to automatically detect which interfaces are installed
    //       on the machine because it will attempt to launch the 'vboxsvc' process
    //       without out environment variable changes and muck everything up.
    //
    string vbox_version;
    int vbox_major = 0, vbox_minor = 0;

    if (BOINC_SUCCESS != vbox42::VBOX_VM::get_version_information(vbox_version)) {
        vbox43::VBOX_VM::get_version_information(vbox_version);
    }
    if (!vbox_version.empty()) {
        sscanf(vbox_version.c_str(), "%d.%d", &vbox_major, &vbox_minor);
        if ((4 == vbox_major) && (2 == vbox_minor)) {
            pVM = (VBOX_VM*) new vbox42::VBOX_VM();
        }
        if ((4 == vbox_major) && (3 == vbox_minor)) {
            pVM = (VBOX_VM*) new vbox43::VBOX_VM();
        }
    }
    if (!pVM) {
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
        pVM = (VBOX_VM*) new vboxmanage::VBOX_VM();
    }
#else
    pVM = (VBOX_VM*) new vboxmanage::VBOX_VM();
#endif


    for (int i=1; i<argc; i++) {
        if (!strcmp(argv[i], "--trickle")) {
            trickle_period = atof(argv[++i]);
        }
        if (!strcmp(argv[i], "--ncpus")) {
            ncpus = atof(argv[++i]);
        }
        if (!strcmp(argv[i], "--memory_size_mb")) {
            memory_size_mb = atof(argv[++i]);
        }
        if (!strcmp(argv[i], "--vmimage")) {
            vm_image = atoi(argv[++i]);
        }
        if (!strcmp(argv[i], "--register_only")) {
            pVM->register_only = true;
        }
    }
743
744
745

    // Choose a random interleave value for checkpoint intervals to stagger disk I/O.
    // 
746
747
748
749
750
751
    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)));
    }
752
753
754
    random_checkpoint_factor = (double)(((int)(drand() * 100000.0)) % 600);
    fprintf(
        stderr,
755
        "%s Feature: Checkpoint interval offset (%d seconds)\n",
756
        vboxwrapper_msg_prefix(buf, sizeof(buf)),
757
        (int)random_checkpoint_factor
758
759
760
761
762
763
764
765
766
767
768
769
    );

    // 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
        );
    }

770
771
772
773
774
775
    // Check for architecture incompatibilities
    // 
#if defined(_WIN32) && defined(_M_IX86)
    if (strstr(aid.host_info.os_version, "x64")) {
        fprintf(
            stderr,
776
            "%s 64-bit version of BOINC is required, please upgrade. Rescheduling execution for a later date.\n",
777
778
779
780
781
782
            vboxwrapper_msg_prefix(buf, sizeof(buf))
        );
        boinc_temporary_exit(86400, "Architecture incompatibility detected.");
    }
#endif

783
784
    // Initialize VM Hypervisor
    //
785
    retval = pVM->initialize();
786
787
788
    if (retval) {
        fprintf(
            stderr,
789
            "%s Could not detect VM Hypervisor. Rescheduling execution for a later date.\n",
790
            vboxwrapper_msg_prefix(buf, sizeof(buf))
791
        );
792
        boinc_temporary_exit(86400, "Detection of VM Hypervisor failed.");
793
794
    }

795
796
    // Record what version of VirtualBox was used.
    // 
797
    if (!pVM->virtualbox_version.empty()) {
Rom Walton's avatar
Rom Walton committed
798
799
        fprintf(
            stderr,
800
            "%s Detected: %s\n",
Rom Walton's avatar
Rom Walton committed
801
            vboxwrapper_msg_prefix(buf, sizeof(buf)),
802
            pVM->virtualbox_version.c_str()
803
804
805
        );
    }

806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
    // 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
826
    // Record which mode VirtualBox should be started in.
827
    //
828
    if (aid.vbox_window || boinc_is_standalone()) {
829
830
831
832
833
        fprintf(
            stderr,
            "%s Detected: Headless Mode Disabled\n",
            vboxwrapper_msg_prefix(buf, sizeof(buf))
        );
834
        pVM->headless = false;
835
836
837
838
839
    }

    // Check for invalid confgiurations.
    //
    if (aid.using_sandbox && aid.vbox_window) {
Rom Walton's avatar
Rom Walton committed
840
        vboxwrapper_msg_prefix(buf, sizeof(buf));
841
842
843
        fprintf(
            stderr,
            "%s Invalid configuration detected.\n"
Rom Walton's avatar
Rom Walton committed
844
845
846
            "%s NOTE: BOINC cannot be installed as a service and run VirtualBox in headfull mode at the same time.\n",
            buf,
            buf
847
        );
Rom Walton's avatar
Rom Walton committed
848
        boinc_temporary_exit(86400, "Incompatible configuration detected.");
849
850
    }

851
    // Check against known incompatible versions of VirtualBox.  
852
853
    // VirtualBox 4.2.6 crashes during snapshot operations
    // and 4.2.18 fails to restore from snapshots properly.
854
    //
855
856
857
    if ((pVM->virtualbox_version.find("4.2.6") != std::string::npos) || 
        (pVM->virtualbox_version.find("4.2.18") != std::string::npos) || 
        (pVM->virtualbox_version.find("4.3.0") != std::string::npos) ) {
858
859
860
861
862
        fprintf(
            stderr,
            "%s Incompatible version of VirtualBox detected. Please upgrade to a later version.\n",
            vboxwrapper_msg_prefix(buf, sizeof(buf))
        );
863
864
865
866
        boinc_temporary_exit(86400,
            "Incompatible version of VirtualBox detected; please upgrade.",
            true
        );
867
868
    }

869
870
871
872
    // 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.
    //
873
    if (!pVM->is_system_ready(message)) {
874
875
        fprintf(
            stderr,
876
            "%s Could not communicate with VM Hypervisor. Rescheduling execution for a later date.\n",
877
878
            vboxwrapper_msg_prefix(buf, sizeof(buf))
        );
879
        boinc_temporary_exit(86400, message.c_str());
880
881
    }

882
883
    // Parse Job File
    //
884
    retval = parse_job_file(*pVM);
Rom Walton's avatar
Rom Walton committed
885
886
887
888
889
890
891
892
893
894
    if (retval) {
        fprintf(
            stderr,
            "%s can't parse job file: %d\n",
            vboxwrapper_msg_prefix(buf, sizeof(buf)),
            retval
        );
        boinc_finish(retval);
    }

895
896
    // Record which mode VirtualBox should be started in.
    //
897
898
899
900
    fprintf(
        stderr,
        "%s Detected: Minimum checkpoint interval (%f seconds)\n",
        vboxwrapper_msg_prefix(buf, sizeof(buf)),
901
        pVM->minimum_checkpoint_interval
902
    );
903

Rom Walton's avatar
Rom Walton committed
904
905
    // Validate whatever configuration options we can
    //
906
    if (pVM->enable_shared_directory) {
Rom Walton's avatar
Rom Walton committed
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
        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
    //
929
930
    if (pVM->enable_shared_directory && pVM->copy_to_shared.size()) {
        for (vector<string>::iterator iter = pVM->copy_to_shared.begin(); iter != pVM->copy_to_shared.end(); iter++) {
Rom Walton's avatar
Rom Walton committed
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
            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()
                    );
                }
            }
        }
    }

951
952
    // Configure Instance specific VM Parameters
    //
953
954
    pVM->vm_master_name = "boinc_";
    pVM->image_filename = IMAGE_FILENAME_COMPLETE;
Rom Walton's avatar
Rom Walton committed
955
    if (boinc_is_standalone()) {
956
957
958
        pVM->vm_master_name += "standalone";
        pVM->vm_master_description = "standalone";
        if (pVM->enable_floppyio) {
959
960
961
            sprintf(buf, "%s.%s",
                FLOPPY_IMAGE_FILENAME, FLOPPY_IMAGE_FILENAME_EXTENSION
            );
962
            pVM->floppy_image_filename = buf;
Rom Walton's avatar
Rom Walton committed
963
964
        }
    } else {
965
966
        pVM->vm_master_name += md5_string(std::string(aid.result_name)).substr(0, 16);
        pVM->vm_master_description = aid.result_name;
967
		if (vm_image) {
968
969
970
            sprintf(buf, "%s_%d.%s",
                IMAGE_FILENAME, vm_image, IMAGE_FILENAME_EXTENSION
            );
971
            pVM->image_filename = buf;
972
		}
973
        if (pVM->enable_floppyio) {
974
975
976
977
            sprintf(buf, "%s_%d.%s",
                FLOPPY_IMAGE_FILENAME, aid.slot,
                FLOPPY_IMAGE_FILENAME_EXTENSION
            );
978
            pVM->floppy_image_filename = buf;
Rom Walton's avatar
Rom Walton committed
979
        }
980
    }
981
982
    if (pVM->enable_cache_disk) {
        pVM->cache_disk_filename = CACHE_DISK_FILENAME;
983
    }
984
985
    if (pVM->enable_isocontextualization) {
        pVM->iso_image_filename = ISO_IMAGE_FILENAME;
Rom Walton's avatar
Rom Walton committed
986
987
988
989
990
991
992
    }
    if (aid.ncpus > 1.0 || ncpus > 1.0) {
        if (ncpus) {
            sprintf(buf, "%d", (int)ceil(ncpus));
        } else {
            sprintf(buf, "%d", (int)ceil(aid.ncpus));
        }
993
        pVM->vm_cpu_count = buf;
Rom Walton's avatar
Rom Walton committed
994
    } else {
995
        pVM->vm_cpu_count = "1";
Rom Walton's avatar
Rom Walton committed
996
    }
997
    if (pVM->memory_size_mb > 1.0 || memory_size_mb > 1.0) {
998
999
1000
        if (memory_size_mb) {
            sprintf(buf, "%d", (int)ceil(memory_size_mb));
        } else {
For faster browsing, not all history is shown. View entire blame