~ubuntu-branches/ubuntu/precise/boinc/precise

« back to all changes in this revision

Viewing changes to sched/file_deleter.cpp

Tags: 6.12.8+dfsg-1
* New upstream release.
* Simplified debian/rules

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
//
30
30
#define ANTIQUE_DELAY       3600
31
31
#define ANTIQUE_INTERVAL    86400
 
32
#define ANTIQUE_LIMIT       50000
32
33
 
33
34
// how often to retry errors
34
35
//
54
55
#include "error_numbers.h"
55
56
#include "util.h"
56
57
#include "str_util.h"
 
58
#include "str_replace.h"
57
59
#include "filesys.h"
58
60
#include "strings.h"
 
61
#include "svn_version.h"
59
62
 
60
63
#include "sched_config.h"
61
64
#include "sched_util.h"
62
65
#include "sched_msgs.h"
63
66
 
64
 
using namespace std;
65
 
 
66
67
#define LOCKFILE "file_deleter.out"
67
68
#define PIDFILE  "file_deleter.pid"
68
69
 
69
 
#define SLEEP_INTERVAL 5
 
70
#define DEFAULT_SLEEP_INTERVAL 5
70
71
#define RESULTS_PER_WU 4        // an estimate of redundancy 
71
72
 
72
 
int id_modulus=0, id_remainder=0;
 
73
int id_modulus=0, id_remainder=0, appid=0;
73
74
bool dont_retry_errors = false;
74
75
bool dont_delete_antiques = false;
75
76
bool dont_delete_batches = false;
76
77
int antique_delay = ANTIQUE_DELAY;
 
78
int antique_interval = ANTIQUE_INTERVAL;
 
79
int antique_limit = ANTIQUE_LIMIT;
77
80
bool do_input_files = true;
78
81
bool do_output_files = true;
 
82
int sleep_interval = DEFAULT_SLEEP_INTERVAL;
79
83
 
80
 
void usage() {
81
 
    fprintf(stderr,
82
 
"file_deleter: deletes files that are no longer needed.\n"
83
 
"\n"
84
 
"default operation:\n"
85
 
"1) enumerate N WUs and M results (N,M compile params)\n"
86
 
"   that are ready to file-delete, and try to delete their files\n"
87
 
"2) if the enums didn't yield anything, sleep for K seconds\n"
88
 
"3) repeat from 1)\n"
89
 
"4) every 1 hour, enumerate everything in state FILE_DELETE_ERROR\n"
90
 
"   and try to delete it.\n"
91
 
"5) after 1 hour, and every 24 hours thereafter,\n"
92
 
"   scan for and delete all files in the upload directories\n"
93
 
"   that are older than any WU in the database,\n"
94
 
"   and were created at least one month ago.\n"
95
 
"   This deletes files uploaded by hosts after the WU was deleted.\n"
96
 
"\n"
97
 
"options:\n"
98
 
"\n"
99
 
"-d N\n"
100
 
"     set debug output level (1/2/3)\n"
101
 
"-mod M R\n"
102
 
"     handle only WUs with ID mod M == R\n"
103
 
"-one_pass\n"
104
 
"     instead of sleeping in 2), exit\n"
105
 
"-dont_retry_error\n"
106
 
"     don't do 4)\n"
107
 
"-dont_delete_antiques\n"
108
 
"     don't do 5)\n"
109
 
"-preserve_result_files\n"
110
 
"     update the DB, but don't delete output files.\n"
111
 
"     For debugging.\n"
112
 
"-preserve_wu_files\n"
113
 
"     update the DB, but don't delete input files.\n"
114
 
"     For debugging.\n"
115
 
"-dont_delete_batches\n"
116
 
"     don't delete anything with positive batch number\n"
117
 
"-input_files_only\n"
118
 
"     delete only input (download) files\n"
119
 
"-output_files_only\n"
120
 
"     delete only output (upload) files\n"
 
84
void usage(char *name) {
 
85
    fprintf(stderr, "Deletes files that are no longer needed.\n\n"
 
86
        "Default operation:\n"
 
87
        "1) enumerate N WUs and M results (N,M compile params)\n"
 
88
        "   that are ready to file-delete, and try to delete their files\n"
 
89
        "2) if the enums didn't yield anything, sleep for K seconds\n"
 
90
        "3) repeat from 1)\n"
 
91
        "4) every 1 hour, enumerate everything in state FILE_DELETE_ERROR\n"
 
92
        "   and try to delete it.\n"
 
93
        "5) after 1 hour, and every 24 hours thereafter,\n"
 
94
        "   scan for and delete all files in the upload directories\n"
 
95
        "   that are older than any WU in the database,\n"
 
96
        "   and were created at least one month ago.\n"
 
97
        "   This deletes files uploaded by hosts after the WU was deleted.\n\n"
 
98
        "Usage: %s [OPTION]...\n\n"
 
99
        "Options:\n"
 
100
        "  -d N | --debug_level N          set debug output level (1 to 4)\n"
 
101
        "  --mod M R                       handle only WUs with ID mod M == R\n"
 
102
        "  --appid ID                      handle only WUs of a particular app\n"
 
103
        "  --one_pass                      instead of sleeping in 2), exit\n"
 
104
        "  --delete_antiques_now           do 5) immediately\n"
 
105
        "  --dont_retry_error              don't do 4)\n"
 
106
        "  --dont_delete_antiques          don't do 5)\n"
 
107
        "  --delete_antiques_interval      change the interval between delete antique passes (in seconds, defaults to 24h)\n"
 
108
        "  --delete_antiques_limit         change the maximum number of files deleted in one delete antique pass (defaults to 50000)\n"
 
109
        "  --preserve_result_files         update the DB, but don't delete output files.\n"
 
110
        "                                  For debugging.\n"
 
111
        "  --preserve_wu_files             update the DB, but don't delete input files.\n"
 
112
        "                                 For debugging.\n"
 
113
        "  --dont_delete_batches           don't delete anything with positive batch number\n"
 
114
        "  --input_files_only              delete only input (download) files\n"
 
115
        "  --output_files_only             delete only output (upload) files\n"
 
116
        "  [ -h | --help ]                 shows this help text\n"
 
117
        "  [ -v | --version ]              shows version information\n",
 
118
        name
121
119
    );
122
 
    exit(1);
123
120
}
124
121
 
125
 
// Given a filename, find its full path in the upload directory hierarchy\n"
126
 
// Return an error if file isn't there.\n"
 
122
// Given a filename, find its full path in the upload directory hierarchy
 
123
// Return ERR_OPENDIR if dir isn't there (possibly recoverable error),
 
124
// ERR_NOT_FOUND if dir is there but not file
127
125
//
128
126
int get_file_path(
129
127
    const char *filename, char* upload_dir, int fanout, char* path
130
128
) {
131
 
    dir_hier_path(filename, upload_dir, fanout, path);
 
129
    dir_hier_path(filename, upload_dir, fanout, path, true);
132
130
    if (boinc_file_exists(path)) {
133
131
        return 0;
134
132
    }
135
 
    return ERR_NOT_FOUND;
 
133
    char* p = strrchr(path, '/');
 
134
    *p = 0;
 
135
    if (boinc_file_exists(path)) {
 
136
        return ERR_NOT_FOUND;
 
137
    }
 
138
    return ERR_OPENDIR;
136
139
}
137
140
 
138
141
 
157
160
            no_delete = true;
158
161
        } else if (match_tag(p, "</file_info>")) {
159
162
            if (!no_delete) {
160
 
                retval = get_file_path(filename, config.download_dir, config.uldl_dir_fanout,
 
163
                retval = get_file_path(
 
164
                    filename, config.download_dir, config.uldl_dir_fanout,
161
165
                    pathname
162
166
                );
163
 
                if (retval) {
 
167
                if (retval == ERR_OPENDIR) {
 
168
                    log_messages.printf(MSG_CRITICAL,
 
169
                        "[WU#%d] missing dir for %s\n",
 
170
                        wu.id, filename
 
171
                    );
 
172
                    mthd_retval = ERR_UNLINK;
 
173
                } else if (retval) {
164
174
                    log_messages.printf(MSG_CRITICAL,
165
175
                        "[WU#%d] get_file_path: %s: %d\n",
166
176
                        wu.id, filename, retval
179
189
                    } else {
180
190
                        count_deleted++;
181
191
                    }
 
192
                    // delete the cached MD5 file if needed
 
193
                    //
 
194
                    if (config.cache_md5_info) {
 
195
                        strcat(pathname,".md5");
 
196
                        log_messages.printf(MSG_NORMAL,
 
197
                            "[WU#%d] deleting %s\n", wu.id, filename
 
198
                        );
 
199
                        retval = unlink(pathname);
 
200
                        if (retval) {
 
201
                            log_messages.printf(MSG_CRITICAL,
 
202
                                "[WU#%d] unlink %s failed: %d\n",
 
203
                                wu.id, filename, retval
 
204
                            );
 
205
                        }
 
206
                    }
182
207
                }
183
208
            }
184
209
        }
211
236
                    filename, config.upload_dir, config.uldl_dir_fanout,
212
237
                    pathname
213
238
                );
214
 
                if (retval) {
 
239
                if (retval == ERR_OPENDIR) {
 
240
                    mthd_retval = ERR_OPENDIR;
 
241
                    log_messages.printf(MSG_CRITICAL,
 
242
                        "[RESULT#%d] missing dir for %s\n",
 
243
                        result.id, pathname
 
244
                    );
 
245
                } else if (retval) {
215
246
                    // the fact that no result files were found is a critical
216
247
                    // error if this was a successful result, but is to be
217
248
                    // expected if the result outcome was failure, since in
258
289
static bool preserve_wu_files=false;
259
290
static bool preserve_result_files=false;
260
291
 
261
 
// return nonzero if did anything
 
292
// return true if we changed the file_delete_state of a WU or a result
262
293
//
263
294
bool do_pass(bool retry_error) {
264
295
    DB_WORKUNIT wu;
266
297
    bool did_something = false;
267
298
    char buf[256];
268
299
    char clause[256];
269
 
    int retval;
 
300
    int retval, new_state;
270
301
 
271
302
    check_stop_daemons();
272
303
 
277
308
    if (dont_delete_batches) {
278
309
        strcat(clause, " and batch <= 0 ");
279
310
    }
280
 
 
 
311
    if (appid) {
 
312
        sprintf(buf, " and appid = %d ", appid);
 
313
        strcat(clause, buf);
 
314
    }
281
315
    sprintf(buf,
282
316
        "where file_delete_state=%d %s limit %d",
283
317
        retry_error?FILE_DELETE_ERROR:FILE_DELETE_READY,
293
327
            }
294
328
            break;
295
329
        }
296
 
        did_something = true;
297
330
 
298
 
        retval = 0;
299
 
        if (!preserve_wu_files) {
 
331
        if (preserve_wu_files) {
 
332
            retval = 0;
 
333
        } else {
300
334
            retval = wu_delete_files(wu);
301
335
        }
302
336
        if (retval) {
303
 
            wu.file_delete_state = FILE_DELETE_ERROR;
 
337
            new_state = FILE_DELETE_ERROR;
304
338
            log_messages.printf(MSG_CRITICAL,
305
 
                "[WU#%d] update failed: %d\n", wu.id, retval
 
339
                "[WU#%d] file deletion failed: %d\n", wu.id, retval
306
340
            );
307
341
        } else {
308
 
            wu.file_delete_state = FILE_DELETE_DONE;
 
342
            new_state = FILE_DELETE_DONE;
309
343
        }
310
 
        sprintf(buf, "file_delete_state=%d", wu.file_delete_state);
311
 
        retval= wu.update_field(buf);
 
344
        if (new_state != wu.file_delete_state) {
 
345
            sprintf(buf, "file_delete_state=%d", new_state);
 
346
            retval = wu.update_field(buf);
 
347
            if (retval) {
 
348
                log_messages.printf(MSG_CRITICAL,
 
349
                    "[WU#%d] update failed: %d\n", wu.id, retval
 
350
                );
 
351
            } else {
 
352
                log_messages.printf(MSG_DEBUG,
 
353
                    "[WU#%d] file_delete_state updated\n", wu.id
 
354
                );
 
355
                did_something = true;
 
356
            }
 
357
        } 
312
358
    }
313
359
 
314
360
    sprintf(buf,
327
373
            break;
328
374
        }
329
375
 
330
 
        did_something = true;
331
 
        retval = 0;
332
 
        if (!preserve_result_files) {
 
376
        if (preserve_result_files) {
 
377
            retval = 0;
 
378
        } else {
333
379
            retval = result_delete_files(result);
334
380
        }
335
381
        if (retval) {
336
 
            result.file_delete_state = FILE_DELETE_ERROR;
 
382
            new_state = FILE_DELETE_ERROR;
337
383
            log_messages.printf(MSG_CRITICAL,
338
 
                "[RESULT#%d] update failed: %d\n", result.id, retval
 
384
                "[RESULT#%d] file deletion failed: %d\n", result.id, retval
339
385
            );
340
386
        } else {
341
 
            result.file_delete_state = FILE_DELETE_DONE;
 
387
            new_state = FILE_DELETE_DONE;
342
388
        }
343
 
        sprintf(buf, "file_delete_state=%d", result.file_delete_state); 
344
 
        retval= result.update_field(buf);
 
389
        if (new_state != result.file_delete_state) {
 
390
            sprintf(buf, "file_delete_state=%d", new_state); 
 
391
            retval = result.update_field(buf);
 
392
            if (retval) {
 
393
                log_messages.printf(MSG_CRITICAL,
 
394
                    "[RESULT#%d] update failed: %d\n", result.id, retval
 
395
                );
 
396
            } else {
 
397
                log_messages.printf(MSG_DEBUG,
 
398
                    "[RESULT#%d] file_delete_state updated\n", result.id
 
399
                );
 
400
                did_something = true;
 
401
            }
 
402
        } 
345
403
    } 
346
404
 
347
405
    return did_something;
348
406
}
349
407
 
350
408
struct FILE_RECORD {
351
 
     string name;
 
409
     std::string name;
352
410
     int date_modified;
353
411
};
354
412
 
371
429
// delete files in antique files list, and empty the list.
372
430
// Returns number of files deleted, or negative for error.
373
431
//
 
432
// TODO: the list contains filenames, and we convert these to paths.
 
433
// This is wacked.  The list should contain paths.
 
434
//
374
435
int delete_antique_files() {
375
436
    int nfiles=0;
376
437
 
377
438
    log_messages.printf(MSG_DEBUG,
378
 
        "delete_antique_files(): start (%d files)\n", files_to_delete.size()
 
439
        "delete_antique_files(): start (%d files)\n",
 
440
        (int)files_to_delete.size()
379
441
    );
380
442
    while (!files_to_delete.empty()) {
381
443
        char timestamp[128];
391
453
        );
392
454
        if (retval) {
393
455
            log_messages.printf(MSG_CRITICAL,
394
 
                "get_file_path(%s) failed: %d\n",
395
 
                fr.name.c_str(), retval
 
456
                "get_file_path(%s) failed: %d\n", fr.name.c_str(), retval
396
457
            );
397
458
            return retval;
398
459
        }
435
496
 
436
497
    if (!apache_info) {
437
498
        log_messages.printf(MSG_CRITICAL,
438
 
            "httpd_user'%s' found - add <httpd_user> entry in config.xml\n",
 
499
            "default httpd_user '%s' found - add <httpd_user> entry in config.xml\n",
439
500
            config.httpd_user
440
501
        );
441
502
        return -1;
444
505
        "Searching for antique files older than %d days\n", days
445
506
    );
446
507
 
447
 
    sprintf(command,  "find %s -type f -mtime +%d -follow", config.upload_dir, days);
 
508
    sprintf(command, "find %s -type f -mtime +%d -follow | head -%d", config.upload_dir, days, antique_limit);
448
509
    
449
510
    // Now execute the command, read output on a stream.  We could use
450
511
    // find to also exec a 'delete' command.  But we want to log all
581
642
    int retval;
582
643
    bool one_pass = false;
583
644
    int i;
 
645
    DB_APP app;
 
646
    
 
647
    check_stop_daemons();
584
648
 
585
 
    check_stop_daemons();
 
649
    *app.name='\0';
586
650
    for (i=1; i<argc; i++) {
587
 
        if (!strcmp(argv[i], "-one_pass")) {
 
651
        if (is_arg(argv[i], "one_pass")) {
588
652
            one_pass = true;
589
 
        } else if (!strcmp(argv[i], "-dont_retry_errors")) {
 
653
        } else if (is_arg(argv[i], "dont_retry_errors")) {
590
654
            dont_retry_errors = true;
591
 
        } else if (!strcmp(argv[i], "-preserve_wu_files")) {
 
655
        } else if (is_arg(argv[i], "preserve_wu_files")) {
592
656
            preserve_wu_files = true;
593
 
        } else if (!strcmp(argv[i], "-preserve_result_files")) {
 
657
        } else if (is_arg(argv[i], "preserve_result_files")) {
594
658
            preserve_result_files = true;
595
 
        } else if (!strcmp(argv[i], "-d")) {
596
 
            log_messages.set_debug_level(atoi(argv[++i]));
597
 
        } else if (!strcmp(argv[i], "-mod")) {
 
659
        } else if (is_arg(argv[i], "app")) {
 
660
            strcpy(app.name, argv[++i]);
 
661
        } else if (is_arg(argv[i], "appid")) {
 
662
            if (!argv[++i]) {
 
663
                log_messages.printf(MSG_CRITICAL, "%s requires an argument\n\n", argv[--i]);
 
664
                usage(argv[0]);
 
665
                exit(1);
 
666
            }
 
667
            appid = atoi(argv[i]);
 
668
        } else if (is_arg(argv[i], "d") || is_arg(argv[i], "debug_level")) {
 
669
            if (!argv[++i]) {
 
670
                log_messages.printf(MSG_CRITICAL, "%s requires an argument\n\n", argv[--i]);
 
671
                usage(argv[0]);
 
672
                exit(1);
 
673
            }
 
674
            int dl = atoi(argv[i]);
 
675
            log_messages.set_debug_level(dl);
 
676
            if (dl == 4) g_print_queries = true;
 
677
        } else if (is_arg(argv[i], "mod")) {
 
678
            if (!argv[i+1] || !argv[i+2]) {
 
679
                log_messages.printf(MSG_CRITICAL, "%s requires two arguments\n\n", argv[i]);
 
680
                usage(argv[0]);
 
681
                exit(1);
 
682
            }
598
683
            id_modulus   = atoi(argv[++i]);
599
684
            id_remainder = atoi(argv[++i]);
600
 
        } else if (!strcmp(argv[i], "-dont_delete_antiques")) {
 
685
        } else if (is_arg(argv[i], "dont_delete_antiques")) {
601
686
            dont_delete_antiques = true;
602
 
        } else if (!strcmp(argv[i], "-dont_delete_batches")) {
 
687
        } else if (is_arg(argv[i], "delete_antiques_interval")) {
 
688
            antique_interval = atoi(argv[++i]);
 
689
        } else if (is_arg(argv[i], "delete_antiques_limit")) {
 
690
            antique_limit = atoi(argv[++i]);
 
691
        } else if (is_arg(argv[i], "dont_delete_batches")) {
603
692
            dont_delete_batches = true;
604
 
        } else if (!strcmp(argv[i], "-delete_antiques_now")) {
 
693
        } else if (is_arg(argv[i], "delete_antiques_now")) {
605
694
            antique_delay = 0;
606
 
        } else if (!strcmp(argv[i], "-input_files_only")) {
 
695
        } else if (is_arg(argv[i], "input_files_only")) {
607
696
            do_output_files = false;
608
697
            dont_delete_antiques = true;
609
 
        } else if (!strcmp(argv[i], "-output_files_only")) {
 
698
        } else if (is_arg(argv[i], "output_files_only")) {
610
699
            do_input_files = false;
611
 
        } else if (!strcmp(argv[i], "-help")) {
612
 
            usage();
 
700
        } else if (is_arg(argv[i], "sleep_interval")) {
 
701
            if (!argv[++i]) {
 
702
                log_messages.printf(MSG_CRITICAL, "%s requires an argument\n\n", argv[--i]);
 
703
                usage(argv[0]);
 
704
                exit(1);
 
705
            }
 
706
            sleep_interval = atoi(argv[i]);
 
707
        } else if (is_arg(argv[i], "h") || is_arg(argv[i], "help")) {
 
708
            usage(argv[0]);
 
709
            exit(0);
 
710
        } else if (is_arg(argv[i], "v") || is_arg(argv[i], "version")) {
 
711
            printf("%s\n", SVN_VERSION);
 
712
            exit(0);
613
713
        } else {
614
 
            log_messages.printf(MSG_CRITICAL,
615
 
                "Unrecognized arg: %s\n", argv[i]
616
 
            );
617
 
            usage();
 
714
            log_messages.printf(MSG_CRITICAL, "unknown command line argument: %s\n\n", argv[i]);
 
715
            usage(argv[0]);
 
716
            exit(1);
618
717
        }
619
718
    }
620
719
 
625
724
        );
626
725
    }
627
726
 
628
 
    retval = config.parse_file("..");
 
727
    retval = config.parse_file();
629
728
    if (retval) {
630
729
        log_messages.printf(MSG_CRITICAL,
631
 
            "Can't parse ../config.xml: %s\n", boincerror(retval)
 
730
            "Can't parse config.xml: %s\n", boincerror(retval)
632
731
        );
633
732
        exit(1);
634
733
    }
646
745
            "boinc_db.set_isolation_level: %d; %s\n", retval, boinc_db.error_string()
647
746
        );
648
747
    }
 
748
 
 
749
    if (*app.name && !appid) {
 
750
      char buf[256];      
 
751
      sprintf(buf, "where name='%s'", app.name);
 
752
      retval = app.lookup(buf);
 
753
      if (retval) {
 
754
        log_messages.printf(MSG_CRITICAL, "Can't find app\n");
 
755
        exit(1);
 
756
      }
 
757
      appid=app.id;
 
758
      log_messages.printf(MSG_DEBUG, "Deleting files of appid %d\n",appid);
 
759
    }
 
760
 
649
761
    install_stop_signal_handler();
650
762
 
651
763
    bool retry_errors_now = !dont_retry_errors;
667
779
        }
668
780
        if (!got_any) {
669
781
            if (one_pass) break;
670
 
            sleep(SLEEP_INTERVAL);
 
782
            sleep(sleep_interval);
671
783
        }
672
784
        if (!dont_delete_antiques && (dtime() > next_antique_time)) {
673
785
            log_messages.printf(MSG_DEBUG,
674
786
                "Doing antique deletion pass\n"
675
787
            );
676
788
            do_antique_pass();
677
 
            next_antique_time = dtime() + ANTIQUE_INTERVAL;
 
789
            next_antique_time = dtime() + antique_interval;
678
790
        }
679
791
        if (!dont_retry_errors && !retry_errors_now && (dtime() > next_error_time)) {
680
792
            retry_errors_now = true;
685
797
    }
686
798
}
687
799
 
688
 
const char *BOINC_RCSID_bd0d4938a6 = "$Id: file_deleter.cpp 16397 2008-11-03 22:56:16Z davea $";
 
800
const char *BOINC_RCSID_bd0d4938a6 = "$Id: file_deleter.cpp 22083 2010-07-31 04:08:14Z davea $";