source: MondoRescue/branches/stable/mondo/src/common/libmondo-files.c @ 1140

Last change on this file since 1140 was 1140, checked in by Bruno Cornec, 13 years ago

Try to fix some valgrind reports (note that this version still doesn't work)

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