source: MondoRescue/branches/2.2.10/mondo/src/common/libmondo-files.c@ 2296

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