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

Last change on this file since 900 was 900, checked in by Bruno Cornec, 15 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.