source: MondoRescue/trunk/mondo/src/common/libmondo-files.c@ 900

Last change on this file since 900 was 900, checked in by Bruno Cornec, 18 years ago

Huge patch to introduce low level functions that will bw used everywhere (mr_free, mr_asprintf, ...)
Nearly linking now due to that.

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