source: MondoRescue/branches/3.0/mondo/src/common/libmondo-files.c@ 2972

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