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

Last change on this file since 1769 was 1769, checked in by bruno, 12 years ago

Continue on configuration file items (compression)

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