source: MondoRescue/branches/3.1/mondo/src/common/libmondo-files.c@ 3190

Last change on this file since 3190 was 3190, checked in by Bruno Cornec, 11 years ago
  • Modification to 3.1 branch to make it extremely similar to 3.0. What remains are function rewrite with allocation in the function and desallocation outside of the function. Will be next step
  • Property svn:keywords set to Id
File size: 35.7 KB
Line 
1/* file manipulation
2 $Id: libmondo-files.c 3190 2013-09-25 06:55:43Z bruno $
3*/
4
5/**
6 * @file
7 * Functions to manipulate files.
8 */
9
10
11#include "my-stuff.h"
12#include "mr_mem.h"
13#include "mondostructures.h"
14#include "libmondo-files.h"
15
16#include "lib-common-externs.h"
17
18#include "libmondo-tools-EXT.h"
19#include "libmondo-gui-EXT.h"
20#include "libmondo-devices-EXT.h"
21#include "libmondo-fork-EXT.h"
22#include "libmondo-string-EXT.h"
23
24/*@unused@*/
25//static char cvsid[] = "$Id: libmondo-files.c 3190 2013-09-25 06:55:43Z bruno $";
26
27extern char err_log_lines[NOOF_ERR_LINES][MAX_STR_LEN];
28
29extern int g_currentY;
30extern char *g_mondo_home;
31
32/* Reference to global bkpinfo */
33extern struct s_bkpinfo *bkpinfo;
34
35/**
36 * @addtogroup fileGroup
37 * @{
38 */
39/**
40 * Get an md5 checksum of the specified file.
41 * @param filename The file to checksum.
42 * @return The 32-character ASCII representation of the 128-bit checksum.
43 * @note The returned string points to static storage that will be overwritten with each call.
44 */
45char *calc_checksum_of_file(char *filename)
46{
47 /*@ buffers ***************************************************** */
48 char *output = NULL;
49 char *command = NULL;
50
51 /*@ pointers **************************************************** */
52 char *p = NULL;
53 FILE *fin;
54
55 /*@************************************************************** */
56
57 assert_string_is_neither_NULL_nor_zerolength(filename);
58 if (does_file_exist(filename)) {
59 mr_asprintf(command, "md5sum \"%s\"", filename);
60 fin = popen(command, "r");
61 if (fin) {
62 mr_getline(output, fin);
63 p = strchr(output, ' ');
64 if (p != NULL) {
65 *p = '\0';
66 } else {
67 mr_free(output);
68 mr_asprintf(output, "");
69 }
70 paranoid_pclose(fin);
71 }
72 mr_free(command);
73 } else {
74 log_it("File '%s' not found; cannot calc checksum", filename);
75 mr_asprintf(output, "");
76 }
77 return (output);
78}
79
80
81/**
82 * Get a not-quite-unique representation of some of the file's @c stat properties.
83 * The returned string has the form <tt>size-mtime-ctime</tt>.
84 * @param curr_fname The file to generate the "checksum" for.
85 * @return The "checksum".
86 * @note The returned string points to static storage that will be overwritten with each call.
87 */
88char *calc_file_ugly_minichecksum(char *curr_fname)
89{
90
91 /*@ buffers ***************************************************** */
92 static char curr_cksum[1000];
93
94 /*@ pointers **************************************************** */
95
96 /*@ structures ************************************************** */
97 struct stat buf;
98
99 /*@ initialize data *************************************************** */
100 curr_cksum[0] = '\0';
101
102 /*@************************************************************** */
103
104 assert_string_is_neither_NULL_nor_zerolength(curr_fname);
105 if (lstat(curr_fname, &buf)) {
106 return (curr_cksum); // empty
107 }
108
109 sprintf(curr_cksum, "%ld-%ld-%ld", (long) (buf.st_size),
110 (long) (buf.st_mtime), (long) (buf.st_ctime));
111 return (curr_cksum);
112}
113
114
115
116/**
117 * Get the number of lines in @p filename.
118 * @param filename The file to count lines in.
119 * @return The number of lines in @p filename.
120 * @bug This function uses the shell and "wc -l"; it should probably be rewritten in C.
121 */
122long count_lines_in_file(char *filename)
123{
124
125 /*@ buffers ***************************************************** */
126 char *command = NULL;
127 char *incoming = NULL;
128
129 /*@ long ******************************************************** */
130 long noof_lines = -1L;
131
132 /*@ pointers **************************************************** */
133 FILE *fin;
134
135 assert_string_is_neither_NULL_nor_zerolength(filename);
136 if (!does_file_exist(filename)) {
137 log_it("%s does not exist, so I cannot found the number of lines in it", filename);
138 return (0);
139 }
140 mr_asprintf(command, "cat %s | wc -l", filename);
141 if (!does_file_exist(filename)) {
142 mr_free(command);
143 return (-1);
144 }
145 fin = popen(command, "r");
146 mr_free(command);
147
148 if (fin) {
149 if (feof(fin)) {
150 noof_lines = 0;
151 } else {
152 mr_getline(incoming, fin);
153 while (strlen(incoming) > 0 && incoming[strlen(incoming) - 1] < 32) {
154 incoming[strlen(incoming) - 1] = '\0';
155 }
156 noof_lines = atol(incoming);
157 mr_free(incoming);
158 }
159 paranoid_pclose(fin);
160 }
161 return (noof_lines);
162}
163
164
165/**
166 * Check for existence of given @p filename.
167 * @param filename The file to check for.
168 * @return TRUE if it exists, FALSE otherwise.
169 */
170bool does_file_exist(char *filename)
171{
172
173 /*@ structures ************************************************** */
174 struct stat buf;
175
176 /*@************************************************************** */
177
178 assert(filename != NULL);
179 // assert_string_is_neither_NULL_nor_zerolength(filename);
180 if (lstat(filename, &buf)) {
181 log_msg(20, "%s does not exist", filename);
182 return (FALSE);
183 } else {
184 log_msg(20, "%s exists", filename);
185 return (TRUE);
186 }
187}
188
189
190
191
192
193
194/**
195 * Modify @p inout (a file containing a list of files) to only contain files
196 * that exist.
197 * @param inout The filelist to operate on.
198 * @note The original file is renamed beforehand, so it will not be accessible
199 * while the modification is in progress.
200 */
201void exclude_nonexistent_files(char *inout)
202{
203 char *infname = NULL;
204 char *outfname = NULL;
205 char *tmp = NULL;
206 char *incoming = NULL;
207
208 /*@ int ********************************************************* */
209 int i;
210
211 /*@ pointers **************************************************** */
212 FILE *fin, *fout;
213
214
215 /*@ end vars *********************************************************** */
216
217 assert_string_is_neither_NULL_nor_zerolength(inout);
218 mr_asprintf(infname, "%s.in", inout);
219 mr_asprintf(tmp, "cp -f %s %s", inout, infname);
220 run_program_and_log_output(tmp, FALSE);
221 mr_free(tmp);
222
223 if (!(fin = fopen(infname, "r"))) {
224 log_OS_error("Unable to openin infname");
225 mr_free(infname);
226 return;
227 }
228
229 mr_asprintf(outfname, "%s", inout);
230 if (!(fout = fopen(outfname, "w"))) {
231 log_OS_error("Unable to openout outfname");
232 mr_free(infname);
233 mr_free(outfname);
234 return;
235 }
236 mr_free(outfname);
237
238 for (mr_getline(incoming, fin); !feof(fin); mr_getline(incoming, fin)) {
239 i = strlen(incoming) - 1;
240 if (i >= 0 && incoming[i] < 32) {
241 incoming[i] = '\0';
242 }
243 if (does_file_exist(incoming)) {
244 fprintf(fout, "%s\n", incoming);
245 } else {
246 log_it("Excluding '%s'-nonexistent\n", incoming);
247 }
248 mr_free(incoming);
249 }
250 mr_free(incoming);
251
252 paranoid_fclose(fout);
253 paranoid_fclose(fin);
254 unlink(infname);
255 mr_free(infname);
256}
257
258
259/**
260 * Attempt to find the user's kernel by calling Mindi.
261 * If Mindi can't find the kernel, ask user. If @p kernel is not empty,
262 * don't do anything.
263 * @param kernel Where to put the found kernel.
264 * @return 0 for success, 1 for failure.
265 */
266char *figure_out_kernel_path_interactively_if_necessary(void) {
267
268 char *tmp = NULL;
269 char *command = NULL;
270 char *kernel = NULL;
271
272 kernel = call_program_and_get_last_line_of_output("mindi --findkernel 2> /dev/null",TRUE);
273
274 // If we didn't get anything back, check whether mindi raised a fatal error
275 if (!kernel[0]) {
276 mr_asprintf(command, "%s", "grep 'Fatal error' /var/log/mindi.log");
277 tmp = call_program_and_get_last_line_of_output(command,TRUE);
278 if (strlen(tmp) > 1) {
279 popup_and_OK(tmp);
280 mr_free(tmp);
281 mr_free(command);
282 mr_free(kernel);
283 fatal_error("Mindi gave a fatal error. Please check '/var/log/mindi.log'.");
284 }
285 mr_free(tmp);
286 mr_free(command);
287 }
288 log_it("Calling Mindi with kernel path of '%s'", kernel);
289 while (!kernel[0]) {
290 if (!ask_me_yes_or_no("Kernel not found or invalid. Choose another?")) {
291 return (NULL);
292 }
293 tmp = popup_and_get_string("Kernel path", "What is the full path and filename of your kernel, please?", kernel);
294 if (tmp == NULL) {
295 fatal_error("Kernel not found. Please specify with the '-k' flag.");
296 }
297 mr_free(kernel);
298 kernel = tmp;
299
300 log_it("User says kernel is at %s", kernel);
301 }
302 return (kernel);
303}
304
305
306/**
307 * Find location of specified executable in user's PATH.
308 * @param fname The basename of the executable to search for (e.g. @c afio).
309 * @return The full path to the executable, or "" if it does not exist, or NULL if @c file could not be found.
310 * @note The returned string points to static storage that will be overwritten with each call.
311 * @bug The checks with @c file and @c dirname seem pointless. If @c output is "", then you're calling
312 * <tt>dirname 2\>/dev/null</tt> or <tt>file 2\>/dev/null | cut -d':' -f1 2\>/dev/null</tt>, which basically amounts
313 * to nothing.
314 */
315char *find_home_of_exe(char *fname)
316{
317 /*@ buffers ********************* */
318 char *output = NULL;
319 char *command = NULL;
320
321 /*@******************************* */
322
323 assert_string_is_neither_NULL_nor_zerolength(fname);
324 mr_asprintf(command, "which %s 2> /dev/null", fname);
325 output = call_program_and_get_last_line_of_output(command,TRUE);
326 mr_free(command);
327 if (output[0] == '\0') {
328 if (system("which file > /dev/null 2> /dev/null")) {
329 mr_free(output);
330 return (NULL); // forget it :)
331 }
332 mr_asprintf(command, "file %s 2> /dev/null | cut -d':' -f1 2> /dev/null", output);
333 mr_free(output);
334 output = call_program_and_get_last_line_of_output(command,TRUE);
335 mr_free(command);
336 }
337 if (output[0] == '\0') {
338 mr_asprintf(command, "dirname %s 2> /dev/null", output);
339 mr_free(output);
340 output = call_program_and_get_last_line_of_output(command,TRUE);
341 mr_free(command);
342 }
343
344 if (output[0] != '\0' && does_file_exist(output)) {
345 log_msg(4, "find_home_of_exe () --- Found %s at %s", fname, output);
346 } else {
347 mr_free(output);
348 log_msg(4, "find_home_of_exe() --- Could not find %s", fname);
349 }
350 return (output);
351}
352
353
354/**
355 * Get the last sequence of digits surrounded by non-digits in the first 32k of
356 * a file.
357 * @param logfile The file to look in.
358 * @return The number found, or 0 if none.
359 */
360int get_trackno_from_logfile(char *logfile)
361{
362
363 /*@ pointers ********************************************************* */
364 FILE *fin;
365
366 /*@ int ************************************************************** */
367 int trackno = 0;
368 size_t len = 0;
369
370 /*@ buffer ************************************************************ */
371 char datablock[32701];
372
373 assert_string_is_neither_NULL_nor_zerolength(logfile);
374 if (!(fin = fopen(logfile, "r"))) {
375 log_OS_error("Unable to open logfile");
376 fatal_error("Unable to open logfile to read trackno");
377 }
378 len = fread(datablock, 1, 32700, fin);
379 paranoid_fclose(fin);
380 if (len <= 0) {
381 return (0);
382 }
383 for (; len > 0 && !isdigit(datablock[len - 1]); len--);
384 datablock[len--] = '\0';
385 for (; len > 0 && isdigit(datablock[len - 1]); len--);
386 trackno = atoi(datablock + len);
387 return (trackno);
388}
389
390
391
392
393
394
395
396/**
397 * Get a percentage from the last line of @p filename. We look for the string
398 * "% done" on the last line and, if we find it, grab the number before the last % sign.
399 * @param filename The file to get the percentage from.
400 * @return The percentage found, or 0 for error.
401 */
402int grab_percentage_from_last_line_of_file(char *filename)
403{
404
405 /*@ buffers ***************************************************** */
406 char *lastline = NULL;
407 char *command = NULL;
408 /*@ pointers **************************************************** */
409 char *p = NULL;
410
411 /*@ int's ******************************************************* */
412 int i = 0;
413
414 for (i = NOOF_ERR_LINES - 1; i >= 0 && !strstr(err_log_lines[i], "% Done") && !strstr(err_log_lines[i], "% done"); i--);
415 if (i < 0) {
416 mr_asprintf(command, "tail -n3 %s | grep -Fi %% | tail -n1 | awk '{print $0;}'", filename);
417 lastline = call_program_and_get_last_line_of_output(command,FALSE);
418 mr_free(command);
419 if (!lastline[0]) {
420 mr_free(lastline);
421 return (0);
422 }
423 } else {
424 mr_asprintf(lastline, "%s", err_log_lines[i]);
425 }
426
427 p = strrchr(lastline, '%');
428 if (p) {
429 *p = '\0';
430 } else {
431 mr_free(lastline);
432 return (0);
433 }
434
435 for (p--; isdigit(*p) && p != lastline; p--);
436 if (p != lastline) {
437 p++;
438 }
439 i = atoi(p);
440 mr_free(lastline);
441 return (i);
442}
443
444
445
446
447
448/**
449 * Return the last line of @p filename.
450 * @param filename The file to get the last line of.
451 * @return The last line of the file.
452 * @note The returned string points to static storage that will be overwritten with each call.
453 */
454char *last_line_of_file(char *filename)
455{
456 /*@ buffers ***************************************************** */
457 char *output = NULL;
458 char *command = NULL;
459
460 /*@ pointers **************************************************** */
461 FILE *fin;
462
463 /*@ end vars **************************************************** */
464
465 if (!does_file_exist(filename)) {
466 log_it("Tring to get last line of nonexistent file (%s)", filename);
467 mr_asprintf(output, "");
468 return (output);
469 }
470 mr_asprintf(command, "tail -n1 %s", filename);
471 fin = popen(command, "r");
472 mr_free(command);
473
474 mr_getline(output, fin);
475 paranoid_pclose(fin);
476 while (strlen(output) > 0 && output[strlen(output) - 1] < 32) {
477 output[strlen(output) - 1] = '\0';
478 }
479 return (output);
480}
481
482/**
483 * Get the length of @p filename in bytes.
484 * @param filename The file to get the length of.
485 * @return The length of the file, or -1 for error.
486 */
487off_t length_of_file(char *filename)
488{
489 /*@ pointers *************************************************** */
490 FILE *fin;
491
492 /*@ long long ************************************************* */
493 off_t length;
494
495 fin = fopen(filename, "r");
496 if (!fin) {
497 log_it("filename=%s", filename);
498 log_OS_error("Unable to openin filename");
499 return (-1);
500 }
501 fseeko(fin, 0, SEEK_END);
502 length = ftello(fin);
503 paranoid_fclose(fin);
504 return (length);
505}
506
507
508
509/**
510 * ?????
511 * @bug I don't know what this function does. However, it seems orphaned, so it should probably be removed.
512 */
513int make_checksum_list_file(char *filelist, char *cksumlist, char *comppath) {
514 /*@ pointers **************************************************** */
515 FILE *fin;
516 FILE *fout;
517
518 /*@ int ******************************************************* */
519 int percentage;
520 int i;
521 int counter = 0;
522
523 /*@ buffer ****************************************************** */
524 char *stub_fname = NULL;
525 char *curr_fname = NULL;
526 char *curr_cksum = NULL;
527 char *tmp = NULL;
528
529 /*@ long [long] ************************************************* */
530 off_t filelist_length;
531 off_t curr_pos;
532 long start_time;
533 long current_time;
534 long time_taken;
535 long time_remaining;
536
537 /*@ end vars *************************************************** */
538
539 start_time = get_time();
540 filelist_length = length_of_file(filelist);
541 log_it("filelist = %s; cksumlist = %s", filelist, cksumlist);
542
543 fin = fopen(filelist, "r");
544 if (fin == NULL) {
545 log_OS_error("Unable to fopen-in filelist");
546 log_to_screen("Can't open filelist");
547 return (1);
548 }
549 fout = fopen(cksumlist, "w");
550 if (fout == NULL) {
551 log_OS_error("Unable to openout cksumlist");
552 paranoid_fclose(fin);
553 log_to_screen("Can't open checksum list");
554 return (1);
555 }
556 for (mr_getline(stub_fname, fin); !feof(fin); mr_getline(stub_fname, fin)) {
557 if (stub_fname[(i = strlen(stub_fname) - 1)] < 32) {
558 stub_fname[i] = '\0';
559 }
560 mr_asprintf(tmp, "%s%s", comppath, stub_fname);
561 mr_free(stub_fname);
562
563 mr_asprintf(curr_fname, "%s", tmp + 1);
564 mr_free(tmp);
565
566 mr_asprintf(curr_cksum, "%s", calc_file_ugly_minichecksum(curr_fname));
567 fprintf(fout, "%s\t%s\n", curr_fname, curr_cksum);
568 mr_free(curr_cksum);
569
570 if (counter++ > 12) {
571 current_time = get_time();
572 counter = 0;
573 /* BERLIOS: 37 really ? */
574 curr_fname[37] = '\0';
575 curr_pos = ftello(fin) / 1024;
576 percentage = (int) (curr_pos * 100 / filelist_length);
577 time_taken = current_time - start_time;
578 if (percentage != 0) {
579 time_remaining = time_taken * 100 / (long) (percentage) - time_taken;
580 log_to_screen("%02d%% done %02d:%02d taken %02d:%02d remaining %-37s\r", percentage, (int) (time_taken / 60), (int) (time_taken % 60), (int) (time_remaining / 60), (int) (time_remaining % 60), curr_fname);
581 }
582 sync();
583 }
584 mr_free(curr_fname);
585 }
586 mr_free(stub_fname);
587
588 paranoid_fclose(fout);
589 paranoid_fclose(fin);
590 log_it("Done.");
591 return (0);
592}
593
594
595/**
596 * Create the directory @p outdir_fname and all parent directories. Equivalent to <tt>mkdir -p</tt>.
597 * @param outdir_fname The directory to create.
598 * @return The return value of @c mkdir.
599 */
600int make_hole_for_dir(const char *outdir_fname)
601{
602 char *tmp = NULL;
603 int res = 0;
604
605 assert_string_is_neither_NULL_nor_zerolength(outdir_fname);
606 mr_asprintf(tmp, "mkdir -p %s", outdir_fname);
607 res = system(tmp);
608 mr_free(tmp);
609 return (res);
610}
611
612
613/**
614 * Create the parent directories of @p outfile_fname.
615 * @param outfile_fname The file to make a "hole" for.
616 * @return 0, always.
617 * @bug Return value unnecessary.
618 */
619int make_hole_for_file(char *outfile_fname)
620{
621 /*@ buffer ****************************************************** */
622 char *command = NULL;
623
624 /*@ int ******************************************************** */
625 int res = 0;
626
627 /*@ end vars *************************************************** */
628
629 assert_string_is_neither_NULL_nor_zerolength(outfile_fname);
630 assert(!strstr(outfile_fname, MNT_CDROM));
631 assert(!strstr(outfile_fname, "/dev/cdrom"));
632 mr_asprintf(command, "mkdir -p \"%s\" 2> /dev/null", outfile_fname);
633 res += system(command);
634 mr_free(command);
635
636 mr_asprintf(command, "rmdir \"%s\" 2> /dev/null", outfile_fname);
637 res += system(command);
638 mr_free(command);
639
640 mr_asprintf(command, "rm -f \"%s\" 2> /dev/null", outfile_fname);
641 res += system(command);
642 mr_free(command);
643
644 unlink(outfile_fname);
645 return (0);
646}
647
648
649/**
650 * Get the number of lines in @p filelist_fname that contain the string @p wildcard.
651 * @param filelist_fname The file to search through.
652 * @param wildcard The string to search for. This is @e not a shell glob or a regular expression.
653 * @return The number of lines matched.
654 */
655long noof_lines_that_match_wildcard(char *filelist_fname, char *wildcard)
656{
657 /*@ long ******************************************************* */
658 long matches = 0;
659
660 /*@ pointers *************************************************** */
661 FILE *fin;
662
663 /*@ buffers **************************************************** */
664 char *incoming = NULL;
665
666 /*@ end vars *************************************************** */
667
668
669 fin = fopen(filelist_fname, "r");
670
671 if (!fin) {
672 log_OS_error("Unable to openin filelist_fname");
673 return (0);
674 }
675 mr_getline(incoming, fin);
676 while (!feof(fin)) {
677 if (strstr(incoming, wildcard)) {
678 matches++;
679 }
680 mr_free(incoming);
681 mr_getline(incoming, fin);
682 }
683 mr_free(incoming);
684 paranoid_fclose(fin);
685 return (matches);
686}
687
688
689
690/**
691 * Determine the size (in KB) of @p dev in the mountlist in mountlist.txt.
692 * @param tmpdir The tempdir where the mountlist is stored.
693 * @param dev The device to search for.
694 * @return The size of the partition in KB.
695 */
696long size_of_partition_in_mountlist_K(char *tmpdir, char *dev)
697{
698 char *command = NULL;
699 char *mountlist = NULL;
700 char *sz_res = NULL;
701 long file_len_K;
702
703 mr_asprintf(mountlist, "%s/mountlist.txt", MINDI_CACHE);
704 mr_asprintf(command, "grep \"%s \" %s/mountlist.txt | head -n1 | awk '{print $4}'", dev, MINDI_CACHE);
705 mr_free(mountlist);
706
707 log_it(command);
708 sz_res = call_program_and_get_last_line_of_output(command,TRUE);
709 file_len_K = atol(sz_res);
710 log_msg(4, "%s --> %s --> %ld", command, sz_res, file_len_K);
711 mr_free(sz_res);
712 mr_free(command);
713
714 return (file_len_K);
715}
716
717/**
718 * Calculate the total size (in KB) of all the biggiefiles in this backup.
719 * @param bkpinfo The backup information structure. Only the @c bkpinfo->tmpdir field is used.
720 * @return The total size of all biggiefiles in KB.
721 */
722long size_of_all_biggiefiles_K()
723{
724 /*@ buffers ***************************************************** */
725 char *fname = NULL;
726 char *biggielist = NULL;
727 char *tmp = NULL;
728 char *command = NULL;
729
730 /*@ long ******************************************************** */
731 long scratchL = 0;
732 long file_len_K;
733
734 /*@ pointers *************************************************** */
735 FILE *fin = NULL;
736
737 /*@ end vars *************************************************** */
738
739 log_it("Calculating size of all biggiefiles (in total)");
740 mr_asprintf(biggielist, "%s/biggielist.txt", bkpinfo->scratchdir);
741 log_it("biggielist = %s", biggielist);
742 fin = fopen(biggielist, "r");
743 mr_free(biggielist);
744
745 if (!(fin)) {
746 log_OS_error("Cannot open biggielist. OK, so estimate is based on filesets only.");
747 } else {
748 log_msg(4, "Reading it...");
749 for (mr_getline(fname, fin); !feof(fin); mr_getline(fname, fin)) {
750 if (fname[strlen(fname) - 1] <= 32) {
751 fname[strlen(fname) - 1] = '\0';
752 }
753 if (0 == strncmp(fname, "/dev/", 5)) {
754 if (is_dev_an_NTFS_dev(fname)) {
755 tmp = find_home_of_exe("ntfsresize");
756 if (!tmp) {
757 mr_free(tmp);
758 mr_free(fname);
759 fatal_error("ntfsresize not found");
760 }
761 mr_free(tmp);
762
763 mr_asprintf(command, "ntfsresize --force --info %s|grep '^You might resize at '|cut -d' ' -f5", fname);
764 log_it("command = %s", command);
765 tmp = call_program_and_get_last_line_of_output(command,TRUE);
766 mr_free(command);
767
768 log_it("res of it = %s", tmp);
769 file_len_K = atoll(tmp) / 1024L;
770 mr_free(tmp);
771 } else {
772 file_len_K = get_phys_size_of_drive(fname) * 1024L;
773 }
774 } else {
775 /* BERLIOS: more than long here ??? */
776 file_len_K = (long) (length_of_file(fname) / 1024);
777 }
778 if (file_len_K > 0) {
779 scratchL += file_len_K;
780 log_msg(4, "%s --> %ld K", fname, file_len_K);
781 }
782 log_msg(4, "After adding %s, scratchL+%ld now equals %ld", fname, file_len_K, scratchL);
783 if (feof(fin)) {
784 break;
785 }
786 mr_free(fname);
787 }
788 mr_free(fname);
789 }
790 log_it("Closing...");
791 paranoid_fclose(fin);
792 log_it("Finished calculating total size of all biggiefiles");
793 return (scratchL);
794}
795
796/**
797 * Determine the amount of space (in KB) occupied by a mounted CD.
798 * This can also be used to find the space used for other directories.
799 * @param mountpt The mountpoint/directory to check.
800 * @return The amount of space occupied in KB.
801 */
802long long space_occupied_by_cd(char *mountpt)
803{
804 /*@ buffer ****************************************************** */
805 char *tmp = NULL;
806 char *command = NULL;
807 long long llres;
808 /*@ pointers **************************************************** */
809 char *p;
810 FILE *fin;
811
812 /*@ end vars *************************************************** */
813
814 mr_asprintf(command, "du -sk %s", mountpt);
815 errno = 0;
816 fin = popen(command, "r");
817 if (errno) {
818 log_it("popen() FAILED: command=%s, mountpt=%s, fin=%d, errno=%d, strerror=%s", command, mountpt, fin, errno, strerror(errno));
819 llres = 0;
820 } else {
821 mr_getline(tmp, fin);
822 paranoid_pclose(fin);
823 p = strchr(tmp, '\t');
824 if (p) {
825 *p = '\0';
826 }
827 for (p = tmp, llres = 0; *p != '\0'; p++) {
828 llres *= 10;
829 llres += (int) (*p - '0');
830 }
831 mr_free(tmp);
832 }
833 mr_free(command);
834
835 return (llres);
836}
837
838
839/**
840 * Update a CRC checksum to include another character.
841 * @param crc The original CRC checksum.
842 * @param c The character to add.
843 * @return The new CRC checksum.
844 * @ingroup utilityGroup
845 */
846unsigned int updcrc(unsigned int crc, unsigned int c)
847{
848 unsigned int tmp;
849 tmp = (crc >> 8) ^ c;
850 crc = (crc << 8) ^ crctttab[tmp & 255];
851 return crc;
852}
853
854/**
855 * Update a reverse CRC checksum to include another character.
856 * @param crc The original CRC checksum.
857 * @param c The character to add.
858 * @return The new CRC checksum.
859 * @ingroup utilityGroup
860 */
861unsigned int updcrcr(unsigned int crc, unsigned int c)
862{
863 unsigned int tmp;
864 tmp = crc ^ c;
865 crc = (crc >> 8) ^ crc16tab[tmp & 0xff];
866 return crc;
867}
868
869
870
871
872/**
873 * Check for an executable on the user's system; write a message to the
874 * screen and the log if we can't find it.
875 * @param fname The executable basename to look for.
876 * @return 0 if it's found, nonzero if not.
877 */
878int whine_if_not_found(char *fname)
879{
880 /*@ buffers *** */
881 char *command = NULL;
882 int res = 0;
883
884 mr_asprintf(command, "which %s > /dev/null 2> /dev/null", fname);
885 res = system(command);
886 mr_free(command);
887
888 if (res) {
889 log_to_screen("Please install '%s'. I cannot find it on your system.", fname);
890 log_to_screen("There may be hyperlink at http://www.mondorescue.org which");
891 log_to_screen("will take you to the relevant (missing) package.");
892 return (1);
893 } else {
894 return (0);
895 }
896}
897
898
899
900
901
902
903/**
904 * Create a data file at @p fname containing @p contents.
905 * The data actually can be multiple lines, despite the name.
906 * @param fname The file to create.
907 * @param contents The data to put in it.
908 * @return 0 for success, 1 for failure.
909 */
910int write_one_liner_data_file(char *fname, char *contents)
911{
912 /*@ pointers *************************************************** */
913 FILE *fout;
914 int res = 0;
915
916 /*@ end vars *************************************************** */
917
918 assert_string_is_neither_NULL_nor_zerolength(fname);
919 if (!contents) {
920 log_it("%d: Warning - writing NULL to %s", __LINE__, fname);
921 }
922 if (!(fout = fopen(fname, "w"))) {
923 log_it("fname=%s");
924 log_OS_error("Unable to openout fname");
925 return (1);
926 }
927 fprintf(fout, "%s\n", contents);
928 paranoid_fclose(fout);
929 return (res);
930}
931
932
933
934/**
935 * Read @p fname into @p contents.
936 * @param fname The file to read.
937 * @param contents Where to put its contents.
938 * @return 0 for success, nonzero for failure.
939 */
940int read_one_liner_data_file(char *fname, char *contents)
941{
942 /*@ pointers *************************************************** */
943 FILE *fin;
944 int res = 0;
945 int i;
946
947 /*@ end vars *************************************************** */
948
949 assert_string_is_neither_NULL_nor_zerolength(fname);
950 if (!contents) {
951 log_it("%d: Warning - reading NULL from %s", __LINE__, fname);
952 }
953 if (!(fin = fopen(fname, "r"))) {
954 log_it("fname=%s", fname);
955 log_OS_error("Unable to openin fname");
956 return (1);
957 }
958 res = fscanf(fin, "%s\n", contents);
959 i = strlen(contents);
960 if (i > 0 && contents[i - 1] < 32) {
961 contents[i - 1] = '\0';
962 }
963 paranoid_fclose(fin);
964 res = 0;
965 return (res);
966}
967
968
969
970
971
972
973
974
975
976/**
977 * Copy the files that Mondo/Mindi need to run to the scratchdir or tempdir.
978 * Currently this includes: copy Mondo's home directory to scratchdir,
979 * copy post-nuke.tgz (if it exists) to tmpdir,
980 * and run "hostname > scratchdir/HOSTNAME".
981 * @param bkpinfo The backup information structure. Fields used:
982 * - @c bkpinfo->postnuke_tarball
983 * - @c bkpinfo->scratchdir
984 * - @c bkpinfo->tmpdir
985 */
986void copy_mondo_and_mindi_stuff_to_scratchdir()
987{
988 /*@ Char buffers ** */
989 char *command = NULL;
990 char *tmp = NULL;
991 int res = 0;
992
993 mvaddstr_and_log_it(g_currentY, 0, "Copying Mondo's core files to the scratch directory");
994
995 log_msg(4, "g_mondo_home='%s'", g_mondo_home);
996 if (strlen(g_mondo_home) < 2) {
997 find_and_store_mondoarchives_home(g_mondo_home);
998 }
999 mr_asprintf(command, CP_BIN " --parents -pRdf %s %s", g_mondo_home, bkpinfo->scratchdir);
1000
1001 log_msg(4, "command = %s", command);
1002 res = run_program_and_log_output(command, 1);
1003 mr_free(command);
1004
1005 if (res) {
1006 fatal_error("Failed to copy Mondo's stuff to scratchdir");
1007 }
1008
1009 mr_asprintf(command, "cp -f %s/LAST-FILELIST-NUMBER %s", bkpinfo->tmpdir, bkpinfo->scratchdir);
1010
1011 if (run_program_and_log_output(command, FALSE)) {
1012 fatal_error("Failed to copy LAST-FILELIST-NUMBER to scratchdir");
1013 }
1014 mr_free(command);
1015
1016 tmp = call_program_and_get_last_line_of_output("which mondorestore",TRUE);
1017 if (!tmp[0]) {
1018 mr_free(tmp);
1019 fatal_error("'which mondorestore' returned null. Where's your mondorestore? `which` can't find it. That's odd. Did you install mondorestore?");
1020 }
1021 mr_free(tmp);
1022
1023 mr_asprintf(command, "hostname > %s/HOSTNAME", bkpinfo->scratchdir);
1024 paranoid_system(command);
1025 mr_free(command);
1026
1027 if (bkpinfo->postnuke_tarball) {
1028 mr_asprintf(command, "cp -f %s %s/post-nuke.tgz", bkpinfo->postnuke_tarball, bkpinfo->tmpdir);
1029 res = run_program_and_log_output(command, FALSE);
1030 mr_free(command);
1031
1032 if (res) {
1033 fatal_error("Unable to copy post-nuke tarball to tmpdir");
1034 }
1035 }
1036
1037 mvaddstr_and_log_it(g_currentY++, 74, "Done.");
1038}
1039
1040
1041
1042
1043
1044/**
1045 * Store the client's NETFS configuration in files to be restored at restore-time.
1046 * Assumes that @c bkpinfo->media_type = netfs, but does not check for this.
1047 * @param bkpinfo The backup information structure. Fields used:
1048 * - @c netfs_mount
1049 * - @c netfs_remote_dir
1050 * - @c tmpdir
1051 */
1052void store_netfs_config()
1053{
1054
1055 /*@ buffers ******** */
1056 char *netfs_dev = NULL;
1057 char *netfs_client_hwaddr = NULL;
1058 char *netfs_mount = NULL;
1059 char *netfs_client_ipaddr = NULL;
1060 char *netfs_client_netmask = NULL;
1061 char *netfs_client_broadcast = NULL;
1062 char *netfs_client_defgw = NULL;
1063 char *netfs_server_ipaddr = NULL;
1064 char *tmp = NULL;
1065 char *command = NULL;
1066
1067 /*@ pointers ***** */
1068 char *p;
1069
1070 if (! bkpinfo->netfs_mount) {
1071 fatal_error("No netfs_mount found !");
1072 }
1073
1074 log_it("Storing Network configuration");
1075 mr_asprintf(tmp, "%s", bkpinfo->netfs_mount);
1076 p = strchr(tmp, ':');
1077 if (!p) {
1078 fatal_error("Network mount doesn't have a colon in it, e.g. 192.168.1.4:/home/nfs");
1079 }
1080 *(p++) = '\0';
1081 mr_asprintf(netfs_server_ipaddr, "%s", tmp);
1082 mr_asprintf(netfs_mount, "%s", p);
1083 mr_free(tmp);
1084
1085 /* BERLIOS : there is a bug #67 here as it only considers the first NIC */
1086 mr_asprintf(command, "%s", "ifconfig | tr '\n' '#' | sed s/##// | tr '#' ' ' | tr '' '\n' | head -n1 | cut -d' ' -f1");
1087 netfs_dev = call_program_and_get_last_line_of_output(command,TRUE);
1088 mr_free(command);
1089
1090 mr_asprintf(command, "%s", "ifconfig | tr '\n' '#' | sed s/##// | tr '#' ' ' | tr '' '\\n' | head -n1 | tr -s '\t' ' ' | cut -d' ' -f7 | cut -d':' -f2");
1091 netfs_client_ipaddr = call_program_and_get_last_line_of_output(command,TRUE);
1092 mr_free(command);
1093
1094 mr_asprintf(command, "%s", "ifconfig | tr '\n' '#' | sed s/##// | tr '#' ' ' | tr '' '\\n' | head -n1 | tr -s '\t' ' ' | cut -d' ' -f9 | cut -d':' -f2");
1095 netfs_client_netmask = call_program_and_get_last_line_of_output(command,TRUE);
1096 mr_free(command);
1097
1098 mr_asprintf(command, "%s", "ifconfig | tr '\n' '#' | sed s/##// | tr '#' ' ' | tr '' '\\n' | head -n1 | tr -s '\t' ' ' | cut -d' ' -f8 | cut -d':' -f2");
1099 netfs_client_broadcast = call_program_and_get_last_line_of_output(command,TRUE);
1100 mr_free(command);
1101
1102 mr_asprintf(command, "%s", "route -n | grep '^0.0.0.0' | awk '{print $2}'");
1103 netfs_client_defgw = call_program_and_get_last_line_of_output(command,TRUE);
1104 mr_free(command);
1105
1106 mr_asprintf(command, "ifconfig %s | head -1 | awk '{print $5}'", netfs_dev);
1107 netfs_client_hwaddr = call_program_and_get_last_line_of_output(command);
1108 mr_free(command);
1109
1110 log_it("netfs_client_hwaddr=%s; netfs_client_ipaddr=%s; netfs_server_ipaddr=%s; netfs_mount=%s", netfs_client_hwaddr, netfs_client_ipaddr, netfs_server_ipaddr, netfs_mount);
1111 if (strlen(netfs_dev) < 2) {
1112 fatal_error("Unable to find ethN (eth0, eth1, ...) adapter via Network mount you specified.");
1113 }
1114
1115 /********
1116 * If the Network device found above is a bonded device,
1117 * we need to replace it with an ethN device or the
1118 * networking will not start during an Network restore.
1119 *
1120 * If the Network device in netfs_dev begins with the word "bond", or alb or aft
1121 * look for the corresponding slave ethN device and copy it to netfs_dev.
1122 * Using the common MAC address
1123 ********/
1124 if (!strncmp(netfs_dev, "bond", 4) || !strncmp(netfs_dev, "alb", 3) || !strncmp(netfs_dev, "aft", 3)) {
1125 log_to_screen("Found bonding device %s; looking for corresponding ethN slave device\n", netfs_dev);
1126
1127 mr_asprintf(command, "ifconfig %s | awk '{print $5}' | head -n1", netfs_dev);
1128 netfs_client_hwaddr = call_program_and_get_last_line_of_output(command,TRUE);
1129 mr_free(command);
1130
1131 mr_asprintf(command, "ifconfig | grep -E '%s' | grep -v '%s' | head -n1 | cut -d' ' -f1", netfs_client_hwaddr, netfs_dev);
1132 mr_free(netfs_dev);
1133
1134 netfs_dev = call_program_and_get_last_line_of_output(command,TRUE);
1135 mr_free(command);
1136
1137 log_to_screen("Replacing it with %s\n", netfs_dev);
1138 }
1139
1140 mr_asprintf(tmp, "%s/NETFS-DEV", bkpinfo->tmpdir);
1141 write_one_liner_data_file(tmp, netfs_dev);
1142 mr_free(netfs_dev);
1143 mr_free(tmp);
1144
1145 mr_asprintf(tmp, "%s/NETFS-CLIENT-HWADDR", bkpinfo->tmpdir);
1146 write_one_liner_data_file(tmp, netfs_client_hwaddr);
1147 mr_free(netfs_client_hwaddr);
1148 mr_free(tmp);
1149
1150 mr_asprintf(tmp, "%s/NETFS-CLIENT-IPADDR", bkpinfo->tmpdir);
1151 write_one_liner_data_file(tmp, netfs_client_ipaddr);
1152 mr_free(netfs_client_ipaddr);
1153 mr_free(tmp);
1154
1155 mr_asprintf(tmp, "%s/NETFS-CLIENT-NETMASK", bkpinfo->tmpdir);
1156 write_one_liner_data_file(tmp, netfs_client_netmask);
1157 mr_free(netfs_client_netmask);
1158 mr_free(tmp);
1159
1160 mr_asprintf(tmp, "%s/NETFS-CLIENT-BROADCAST", bkpinfo->tmpdir);
1161 write_one_liner_data_file(tmp, netfs_client_broadcast);
1162 mr_free(netfs_client_broadcast);
1163 mr_free(tmp);
1164
1165 mr_asprintf(tmp, "%s/NETFS-CLIENT-DEFGW", bkpinfo->tmpdir);
1166 write_one_liner_data_file(tmp, netfs_client_defgw);
1167 mr_free(netfs_client_defgw);
1168 mr_free(tmp);
1169
1170 mr_asprintf(tmp, "%s/NETFS-SERVER-IPADDR", bkpinfo->tmpdir);
1171 write_one_liner_data_file(tmp, netfs_server_ipaddr);
1172 mr_free(netfs_server_ipaddr);
1173 mr_free(tmp);
1174
1175 mr_asprintf(tmp, "%s/NETFS-SERVER-MOUNT", bkpinfo->tmpdir);
1176 write_one_liner_data_file(tmp, bkpinfo->netfs_mount);
1177 mr_free(tmp);
1178
1179 if (bkpinfo->netfs_user) {
1180 mr_asprintf(tmp, "%s/NETFS-SERVER-USER", bkpinfo->tmpdir);
1181 write_one_liner_data_file(tmp, bkpinfo->netfs_user);
1182 mr_free(tmp);
1183 }
1184 mr_asprintf(tmp, "%s/NETFS-SERVER-PATH", bkpinfo->tmpdir);
1185 write_one_liner_data_file(tmp, bkpinfo->netfs_remote_dir);
1186 mr_free(tmp);
1187
1188 mr_asprintf(tmp, "%s/ISO-PREFIX", bkpinfo->tmpdir);
1189 write_one_liner_data_file(tmp, bkpinfo->prefix);
1190 mr_free(tmp);
1191
1192 sprintf(tmp, "%s/NETFS-PROTO", bkpinfo->tmpdir);
1193 write_one_liner_data_file(tmp, bkpinfo->netfs_proto);
1194 mr_free(tmp);
1195
1196
1197 log_it("Finished storing Network configuration");
1198}
1199
1200
1201/**
1202 * Determine the approximate number of media that the backup will take up,
1203 * and tell the user. The uncompressed size is estimated as size_of_all_biggiefiles_K()
1204 * plus (noof_sets x bkpinfo->optimal_set_size). The compression factor is estimated as
1205 * 2/3 for LZO and 1/2 for bzip2. The data is not saved anywhere. If there are any
1206 * "imagedevs", the estimate is not shown as it will be wildly inaccurate.
1207 * If there are more than 50 media estimated, the estimate will not be shown.
1208 * @param bkpinfo The backup information structure. Fields used:
1209 * - @c bkpinfo->backup_media_type
1210 * - @c bkpinfo->media_size
1211 * - @c bkpinfo->optimal_set_size
1212 * - @c bkpinfo->use_lzo
1213 * @param noof_sets The number of filesets created.
1214 * @ingroup archiveGroup
1215 */
1216void
1217estimate_noof_media_required(long noof_sets)
1218{
1219 /*@ buffers *************** */
1220 char *tmp = NULL;
1221 char *mds = NULL;
1222
1223 /*@ long long ************* */
1224 long long scratchLL;
1225
1226 if (bkpinfo->media_size <= 0) {
1227 log_to_screen("Number of media required: UNKNOWN");
1228 return;
1229 }
1230
1231 log_it("Estimating number of media required...");
1232 scratchLL = (long long) (noof_sets) * (long long) (bkpinfo->optimal_set_size) + (long long) (size_of_all_biggiefiles_K());
1233 scratchLL = (scratchLL / 1024) / bkpinfo->media_size;
1234 scratchLL++;
1235 if (bkpinfo->use_lzo) {
1236 scratchLL = (scratchLL * 2) / 3;
1237 } else if (bkpinfo->use_gzip) {
1238 scratchLL = (scratchLL * 2) / 3;
1239 } else if (bkpinfo->use_lzma) {
1240 scratchLL = (scratchLL * 2) / 3;
1241 } else {
1242 scratchLL = scratchLL / 2;
1243 }
1244 if (!scratchLL) {
1245 scratchLL++;
1246 }
1247 if (scratchLL <= 1) {
1248 mds = media_descriptor_string(bkpinfo->backup_media_type);
1249 mr_asprintf(tmp, "Your backup will probably occupy a single %s. Maybe two.", mds);
1250 mr_free(mds);
1251 } else if (scratchLL > 4) {
1252 mr_asprintf(tmp, "Your backup will occupy one meeeeellion media! (maybe %s)", number_to_text((int) (scratchLL + 1)));
1253 } else {
1254 mr_asprintf(tmp, "Your backup will occupy approximately %s media.", number_to_text((int) (scratchLL + 1)));
1255 }
1256 if (scratchLL < 50) {
1257 log_to_screen(tmp);
1258 }
1259 mr_free(tmp);
1260}
1261
1262
1263/**
1264 * Determine whether a file is compressed. This is done
1265 * by reading through the "do-not-compress-these" file distributed with Mondo.
1266 * @param filename The file to check.
1267 * @return TRUE if it's compressed, FALSE if not.
1268 */
1269bool is_this_file_compressed(char *filename)
1270{
1271 char *do_not_compress_these = NULL;
1272 char *tmp = NULL;
1273 char *p;
1274 char *q = NULL;
1275
1276 q = strrchr(filename, '.');
1277 if (q == NULL) {
1278 return (FALSE);
1279 }
1280
1281 mr_asprintf(tmp, "%s/do-not-compress-these", g_mondo_home);
1282 if (!does_file_exist(tmp)) {
1283 mr_free(tmp);
1284 return (FALSE);
1285 }
1286 /* BERLIOS: This is just plain WRONG !! */
1287 do_not_compress_these = last_line_of_file(tmp);
1288 mr_free(tmp);
1289
1290 for (p = do_not_compress_these; p != NULL; p++) {
1291 mr_asprintf(tmp, "%s", p);
1292 if (strchr(tmp, ' ')) {
1293 *(strchr(tmp, ' ')) = '\0';
1294 }
1295 if (!strcmp(q, tmp)) {
1296 mr_free(tmp);
1297 mr_free(do_not_compress_these);
1298 return (TRUE);
1299 }
1300 if (!(p = strchr(p, ' '))) {
1301 break;
1302 }
1303 mr_free(tmp);
1304 }
1305 mr_free(do_not_compress_these);
1306 return (FALSE);
1307}
1308
1309/* @} - end fileGroup */
Note: See TracBrowser for help on using the repository browser.