/* file manipulation $Id: libmondo-files.c 2937 2012-01-28 00:51:28Z bruno $ */ /** * @file * Functions to manipulate files. */ #include "my-stuff.h" #include "mr_mem.h" #include "mondostructures.h" #include "libmondo-files.h" #include "lib-common-externs.h" #include "libmondo-tools-EXT.h" #include "libmondo-gui-EXT.h" #include "libmondo-devices-EXT.h" #include "libmondo-fork-EXT.h" #include "libmondo-string-EXT.h" /*@unused@*/ //static char cvsid[] = "$Id: libmondo-files.c 2937 2012-01-28 00:51:28Z bruno $"; extern char err_log_lines[NOOF_ERR_LINES][MAX_STR_LEN]; extern int g_currentY; extern char *g_mondo_home; /* Reference to global bkpinfo */ extern struct s_bkpinfo *bkpinfo; /** * @addtogroup fileGroup * @{ */ /** * Get an md5 checksum of the specified file. * @param filename The file to checksum. * @return The 32-character ASCII representation of the 128-bit checksum. * @note The returned string points to static storage that will be overwritten with each call. */ char *calc_checksum_of_file(char *filename) { /*@ buffers ***************************************************** */ char *output = NULL; char *command = NULL; /*@ pointers **************************************************** */ char *p = NULL; FILE *fin; /*@************************************************************** */ assert_string_is_neither_NULL_nor_zerolength(filename); if (does_file_exist(filename)) { mr_asprintf(command, "md5sum \"%s\"", filename); fin = popen(command, "r"); if (fin) { mr_getline(output, fin); p = strchr(output, ' '); if (p != NULL) { *p = '\0'; } else { mr_free(output); mr_asprintf(output, ""); } paranoid_pclose(fin); } mr_free(command); } else { log_it("File '%s' not found; cannot calc checksum", filename); mr_asprintf(output, ""); } return (output); } /** * Get a not-quite-unique representation of some of the file's @c stat properties. * The returned string has the form size-mtime-ctime. * @param curr_fname The file to generate the "checksum" for. * @return The "checksum". * @note The returned string points to static storage that will be overwritten with each call. */ char *calc_file_ugly_minichecksum(char *curr_fname) { /*@ buffers ***************************************************** */ static char curr_cksum[1000]; /*@ pointers **************************************************** */ /*@ structures ************************************************** */ struct stat buf; /*@ initialize data *************************************************** */ curr_cksum[0] = '\0'; /*@************************************************************** */ assert_string_is_neither_NULL_nor_zerolength(curr_fname); if (lstat(curr_fname, &buf)) { return (curr_cksum); // empty } sprintf(curr_cksum, "%ld-%ld-%ld", (long) (buf.st_size), (long) (buf.st_mtime), (long) (buf.st_ctime)); return (curr_cksum); } /** * Get the number of lines in @p filename. * @param filename The file to count lines in. * @return The number of lines in @p filename. * @bug This function uses the shell and "wc -l"; it should probably be rewritten in C. */ long count_lines_in_file(char *filename) { /*@ buffers ***************************************************** */ char *command = NULL; char *incoming = NULL; /*@ long ******************************************************** */ long noof_lines = -1L; /*@ pointers **************************************************** */ FILE *fin; assert_string_is_neither_NULL_nor_zerolength(filename); if (!does_file_exist(filename)) { log_it("%s does not exist, so I cannot found the number of lines in it", filename); return (0); } mr_asprintf(command, "cat %s | wc -l", filename); if (!does_file_exist(filename)) { mr_free(command); return (-1); } fin = popen(command, "r"); mr_free(command); if (fin) { if (feof(fin)) { noof_lines = 0; } else { mr_getline(incoming, fin); while (strlen(incoming) > 0 && incoming[strlen(incoming) - 1] < 32) { incoming[strlen(incoming) - 1] = '\0'; } noof_lines = atol(incoming); mr_free(incoming); } paranoid_pclose(fin); } return (noof_lines); } /** * Check for existence of given @p filename. * @param filename The file to check for. * @return TRUE if it exists, FALSE otherwise. */ bool does_file_exist(char *filename) { /*@ structures ************************************************** */ struct stat buf; /*@************************************************************** */ assert(filename != NULL); // assert_string_is_neither_NULL_nor_zerolength(filename); if (lstat(filename, &buf)) { log_msg(20, "%s does not exist", filename); return (FALSE); } else { log_msg(20, "%s exists", filename); return (TRUE); } } /** * Modify @p inout (a file containing a list of files) to only contain files * that exist. * @param inout The filelist to operate on. * @note The original file is renamed beforehand, so it will not be accessible * while the modification is in progress. */ void exclude_nonexistent_files(char *inout) { char *infname = NULL; char *outfname = NULL; char *tmp = NULL; char *incoming = NULL; /*@ int ********************************************************* */ int i; /*@ pointers **************************************************** */ FILE *fin, *fout; /*@ end vars *********************************************************** */ assert_string_is_neither_NULL_nor_zerolength(inout); mr_asprintf(infname, "%s.in", inout); mr_asprintf(outfname, "%s", inout); mr_asprintf(tmp, "cp -f %s %s", inout, infname); run_program_and_log_output(tmp, FALSE); mr_free(tmp); if (!(fin = fopen(infname, "r"))) { log_OS_error("Unable to openin infname"); mr_free(infname); return; } if (!(fout = fopen(outfname, "w"))) { log_OS_error("Unable to openout outfname"); mr_free(outfname); return; } mr_free(outfname); for (mr_getline(incoming, fin); !feof(fin); mr_getline(incoming, fin)) { i = strlen(incoming) - 1; if (i >= 0 && incoming[i] < 32) { incoming[i] = '\0'; } if (does_file_exist(incoming)) { fprintf(fout, "%s\n", incoming); } else { log_it("Excluding '%s'-nonexistent\n", incoming); } mr_free(incoming); } mr_free(incoming); paranoid_fclose(fout); paranoid_fclose(fin); unlink(infname); mr_free(infname); } /** * Attempt to find the user's kernel by calling Mindi. * If Mindi can't find the kernel, ask user. If @p kernel is not empty, * don't do anything. * @param kernel Where to put the found kernel. * @return 0 for success, 1 for failure. */ char *figure_out_kernel_path_interactively_if_necessary(void) { char *tmp = NULL; char *command = NULL;; char *kernel = NULL;; kernel = call_program_and_get_last_line_of_output("mindi --findkernel 2> /dev/null",TRUE); // If we didn't get anything back, check whether mindi raised a fatal error if (!kernel[0]) { mr_asprintf(command, "%s", "grep 'Fatal error' /var/log/mindi.log"); tmp = call_program_and_get_last_line_of_output(command,TRUE); if (strlen(tmp) > 1) { popup_and_OK(tmp); mr_free(tmp); mr_free(command); mr_free(kernel); fatal_error("Mindi gave a fatal error. Please check '/var/log/mindi.log'."); } mr_free(tmp); mr_free(command); } log_it("Calling Mindi with kernel path of '%s'", kernel); while (!kernel[0]) { if (!ask_me_yes_or_no("Kernel not found or invalid. Choose another?")) { return (NULL); } tmp = popup_and_get_string("Kernel path", "What is the full path and filename of your kernel, please?", kernel); if (tmp == NULL) { fatal_error("Kernel not found. Please specify with the '-k' flag."); } mr_free(kernel); kernel = tmp; log_it("User says kernel is at %s", kernel); } return (kernel); } /** * Find location of specified executable in user's PATH. * @param fname The basename of the executable to search for (e.g. @c afio). * @return The full path to the executable, or "" if it does not exist, or NULL if @c file could not be found. * @note The returned string points to static storage that will be overwritten with each call. * @bug The checks with @c file and @c dirname seem pointless. If @c output is "", then you're calling * dirname 2\>/dev/null or file 2\>/dev/null | cut -d':' -f1 2\>/dev/null, which basically amounts * to nothing. */ char *find_home_of_exe(char *fname) { /*@ buffers ********************* */ char *output = NULL; char *command = NULL; /*@******************************* */ assert_string_is_neither_NULL_nor_zerolength(fname); mr_asprintf(command, "which %s 2> /dev/null", fname); output = call_program_and_get_last_line_of_output(command,TRUE); mr_free(command); if (output[0] == '\0') { if (system("which file > /dev/null 2> /dev/null")) { mr_free(output); return (NULL); // forget it :) } mr_asprintf(command, "file %s 2> /dev/null | cut -d':' -f1 2> /dev/null", output); mr_free(output); output = call_program_and_get_last_line_of_output(command,TRUE); mr_free(command); } if (output[0] == '\0') { mr_asprintf(command, "dirname %s 2> /dev/null", output); mr_free(output); output = call_program_and_get_last_line_of_output(command,TRUE); mr_free(command); } if (output[0] != '\0' && does_file_exist(output)) { log_msg(4, "find_home_of_exe () --- Found %s at %s", fname, output); } else { mr_free(output); log_msg(4, "find_home_of_exe() --- Could not find %s", fname); } return (output); } /** * Get the last sequence of digits surrounded by non-digits in the first 32k of * a file. * @param logfile The file to look in. * @return The number found, or 0 if none. */ int get_trackno_from_logfile(char *logfile) { /*@ pointers ********************************************************* */ FILE *fin; /*@ int ************************************************************** */ int trackno = 0; size_t len = 0; /*@ buffer ************************************************************ */ char datablock[32701]; assert_string_is_neither_NULL_nor_zerolength(logfile); if (!(fin = fopen(logfile, "r"))) { log_OS_error("Unable to open logfile"); fatal_error("Unable to open logfile to read trackno"); } len = fread(datablock, 1, 32700, fin); paranoid_fclose(fin); if (len <= 0) { return (0); } for (; len > 0 && !isdigit(datablock[len - 1]); len--); datablock[len--] = '\0'; for (; len > 0 && isdigit(datablock[len - 1]); len--); trackno = atoi(datablock + len); return (trackno); } /** * Get a percentage from the last line of @p filename. We look for the string * "% done" on the last line and, if we find it, grab the number before the last % sign. * @param filename The file to get the percentage from. * @return The percentage found, or 0 for error. */ int grab_percentage_from_last_line_of_file(char *filename) { /*@ buffers ***************************************************** */ char *lastline = NULL; char *command = NULL; /*@ pointers **************************************************** */ char *p = NULL; /*@ int's ******************************************************* */ int i = 0; for (i = NOOF_ERR_LINES - 1; i >= 0 && !strstr(err_log_lines[i], "% Done") && !strstr(err_log_lines[i], "% done"); i--); if (i < 0) { mr_asprintf(command, "tail -n3 %s | grep -Fi %% | tail -n1 | awk '{print $0;}'", filename); lastline = call_program_and_get_last_line_of_output(command,FALSE); mr_free(command); if (!lastline[0]) { mr_free(lastline); return (0); } } else { mr_asprintf(lastline, "%s", err_log_lines[i]); } p = strrchr(lastline, '%'); if (p) { *p = '\0'; } else { mr_free(lastline); return (0); } for (p--; isdigit(*p) && p != lastline; p--); if (p != lastline) { p++; } i = atoi(p); mr_free(lastline); return (i); } /** * Return the last line of @p filename. * @param filename The file to get the last line of. * @return The last line of the file. * @note The returned string points to static storage that will be overwritten with each call. */ char *last_line_of_file(char *filename) { /*@ buffers ***************************************************** */ char *output = NULL; char *command = NULL; /*@ pointers **************************************************** */ FILE *fin; /*@ end vars **************************************************** */ if (!does_file_exist(filename)) { log_it("Tring to get last line of nonexistent file (%s)", filename); mr_asprintf(output, ""); return (output); } mr_asprintf(command, "tail -n1 %s", filename); fin = popen(command, "r"); mr_free(command); mr_getline(output, fin); paranoid_pclose(fin); while (strlen(output) > 0 && output[strlen(output) - 1] < 32) { output[strlen(output) - 1] = '\0'; } return (output); } /** * Get the length of @p filename in bytes. * @param filename The file to get the length of. * @return The length of the file, or -1 for error. */ off_t length_of_file(char *filename) { /*@ pointers *************************************************** */ FILE *fin; /*@ long long ************************************************* */ off_t length; fin = fopen(filename, "r"); if (!fin) { log_it("filename=%s", filename); log_OS_error("Unable to openin filename"); return (-1); } fseeko(fin, 0, SEEK_END); length = ftello(fin); paranoid_fclose(fin); return (length); } /** * ????? * @bug I don't know what this function does. However, it seems orphaned, so it should probably be removed. */ int make_checksum_list_file(char *filelist, char *cksumlist, char *comppath) { /*@ pointers **************************************************** */ FILE *fin; FILE *fout; /*@ int ******************************************************* */ int percentage; int i; int counter = 0; /*@ buffer ****************************************************** */ char *stub_fname = NULL; char *curr_fname = NULL; char *curr_cksum = NULL; char *tmp = NULL; /*@ long [long] ************************************************* */ off_t filelist_length; off_t curr_pos; long start_time; long current_time; long time_taken; long time_remaining; /*@ end vars *************************************************** */ start_time = get_time(); filelist_length = length_of_file(filelist); log_it("filelist = %s; cksumlist = %s", filelist, cksumlist); fin = fopen(filelist, "r"); if (fin == NULL) { log_OS_error("Unable to fopen-in filelist"); log_to_screen("Can't open filelist"); return (1); } fout = fopen(cksumlist, "w"); if (fout == NULL) { log_OS_error("Unable to openout cksumlist"); paranoid_fclose(fin); log_to_screen("Can't open checksum list"); return (1); } for (mr_getline(stub_fname, fin); !feof(fin); mr_getline(stub_fname, fin)) { if (stub_fname[(i = strlen(stub_fname) - 1)] < 32) { stub_fname[i] = '\0'; } mr_asprintf(tmp, "%s%s", comppath, stub_fname); mr_free(stub_fname); mr_asprintf(curr_fname, "%s", tmp + 1); mr_free(tmp); mr_asprintf(curr_cksum, "%s", calc_file_ugly_minichecksum(curr_fname)); fprintf(fout, "%s\t%s\n", curr_fname, curr_cksum); mr_free(curr_cksum); if (counter++ > 12) { current_time = get_time(); counter = 0; /* BERLIOS: 37 really ? */ curr_fname[37] = '\0'; curr_pos = ftello(fin) / 1024; percentage = (int) (curr_pos * 100 / filelist_length); time_taken = current_time - start_time; if (percentage != 0) { time_remaining = time_taken * 100 / (long) (percentage) - time_taken; log_to_screen("%02d%% done %02d:%02d taken %02d:%02d remaining %-37s\r", percentage, (int) (time_taken / 60), (int) (time_taken % 60), (int) (time_remaining / 60), (int) (time_remaining % 60), curr_fname); } sync(); } mr_free(curr_fname); } mr_free(stub_fname); paranoid_fclose(fout); paranoid_fclose(fin); log_it("Done."); return (0); } /** * Create the directory @p outdir_fname and all parent directories. Equivalent to mkdir -p. * @param outdir_fname The directory to create. * @return The return value of @c mkdir. */ int make_hole_for_dir(const char *outdir_fname) { char *tmp = NULL; int res = 0; assert_string_is_neither_NULL_nor_zerolength(outdir_fname); mr_asprintf(tmp, "mkdir -p %s", outdir_fname); res = system(tmp); mr_free(tmp); return (res); } /** * Create the parent directories of @p outfile_fname. * @param outfile_fname The file to make a "hole" for. * @return 0, always. * @bug Return value unnecessary. */ int make_hole_for_file(char *outfile_fname) { /*@ buffer ****************************************************** */ char *command = NULL; /*@ int ******************************************************** */ int res = 0; /*@ end vars *************************************************** */ assert_string_is_neither_NULL_nor_zerolength(outfile_fname); assert(!strstr(outfile_fname, MNT_CDROM)); assert(!strstr(outfile_fname, "/dev/cdrom")); mr_asprintf(command, "mkdir -p \"%s\" 2> /dev/null", outfile_fname); res += system(command); mr_free(command); mr_asprintf(command, "rmdir \"%s\" 2> /dev/null", outfile_fname); res += system(command); mr_free(command); mr_asprintf(command, "rm -f \"%s\" 2> /dev/null", outfile_fname); res += system(command); mr_free(command); unlink(outfile_fname); return (0); } /** * Get the number of lines in @p filelist_fname that contain the string @p wildcard. * @param filelist_fname The file to search through. * @param wildcard The string to search for. This is @e not a shell glob or a regular expression. * @return The number of lines matched. */ long noof_lines_that_match_wildcard(char *filelist_fname, char *wildcard) { /*@ long ******************************************************* */ long matches = 0; /*@ pointers *************************************************** */ FILE *fin; /*@ buffers **************************************************** */ char *incoming = NULL; /*@ end vars *************************************************** */ fin = fopen(filelist_fname, "r"); if (!fin) { log_OS_error("Unable to openin filelist_fname"); return (0); } mr_getline(incoming, fin); while (!feof(fin)) { if (strstr(incoming, wildcard)) { matches++; } mr_free(incoming); mr_getline(incoming, fin); } mr_free(incoming); paranoid_fclose(fin); return (matches); } /** * Determine the size (in KB) of @p dev in the mountlist in mountlist.txt. * @param tmpdir The tempdir where the mountlist is stored. * @param dev The device to search for. * @return The size of the partition in KB. */ long size_of_partition_in_mountlist_K(char *tmpdir, char *dev) { char *command = NULL; char *mountlist = NULL; char *sz_res = NULL; long file_len_K; mr_asprintf(mountlist, "%s/mountlist.txt", MINDI_CACHE); mr_asprintf(command, "grep \"%s \" %s/mountlist.txt | head -n1 | awk '{print $4}'", dev, MINDI_CACHE); mr_free(mountlist); log_it(command); sz_res = call_program_and_get_last_line_of_output(command,TRUE); file_len_K = atol(sz_res); log_msg(4, "%s --> %s --> %ld", command, sz_res, file_len_K); mr_free(sz_res); mr_free(command); return (file_len_K); } /** * Calculate the total size (in KB) of all the biggiefiles in this backup. * @param bkpinfo The backup information structure. Only the @c bkpinfo->tmpdir field is used. * @return The total size of all biggiefiles in KB. */ long size_of_all_biggiefiles_K() { /*@ buffers ***************************************************** */ char *fname = NULL; char *biggielist = NULL; char *tmp = NULL; char *command = NULL; /*@ long ******************************************************** */ long scratchL = 0; long file_len_K; /*@ pointers *************************************************** */ FILE *fin = NULL; /*@ end vars *************************************************** */ log_it("Calculating size of all biggiefiles (in total)"); mr_asprintf(biggielist, "%s/biggielist.txt", bkpinfo->scratchdir); log_it("biggielist = %s", biggielist); fin = fopen(biggielist, "r"); mr_free(biggielist); if (!(fin)) { log_OS_error("Cannot open biggielist. OK, so estimate is based on filesets only."); } else { log_msg(4, "Reading it..."); for (mr_getline(fname, fin); !feof(fin); mr_getline(fname, fin)) { if (fname[strlen(fname) - 1] <= 32) { fname[strlen(fname) - 1] = '\0'; } if (0 == strncmp(fname, "/dev/", 5)) { if (is_dev_an_NTFS_dev(fname)) { tmp = find_home_of_exe("ntfsresize"); if (!tmp) { mr_free(tmp); mr_free(fname); fatal_error("ntfsresize not found"); } mr_free(tmp); mr_asprintf(command, "ntfsresize --force --info %s|grep '^You might resize at '|cut -d' ' -f5", fname); log_it("command = %s", command); tmp = call_program_and_get_last_line_of_output(command,TRUE); mr_free(command); log_it("res of it = %s", tmp); file_len_K = atoll(tmp) / 1024L; mr_free(tmp); } else { file_len_K = get_phys_size_of_drive(fname) * 1024L; } } else { /* BERLIOS: more than long here ??? */ file_len_K = (long) (length_of_file(fname) / 1024); } if (file_len_K > 0) { scratchL += file_len_K; log_msg(4, "%s --> %ld K", fname, file_len_K); } log_msg(4, "After adding %s, scratchL+%ld now equals %ld", fname, file_len_K, scratchL); if (feof(fin)) { break; } mr_free(fname); } mr_free(fname); } log_it("Closing..."); paranoid_fclose(fin); log_it("Finished calculating total size of all biggiefiles"); return (scratchL); } /** * Determine the amount of space (in KB) occupied by a mounted CD. * This can also be used to find the space used for other directories. * @param mountpt The mountpoint/directory to check. * @return The amount of space occupied in KB. */ long long space_occupied_by_cd(char *mountpt) { /*@ buffer ****************************************************** */ char *tmp = NULL; char *command = NULL; long long llres; /*@ pointers **************************************************** */ char *p; FILE *fin; /*@ end vars *************************************************** */ mr_asprintf(command, "du -sk %s", mountpt); errno = 0; fin = popen(command, "r"); if (errno) { log_it("popen() FAILED: command=%s, mountpt=%s, fin=%d, errno=%d, strerror=%s", command, mountpt, fin, errno, strerror(errno)); llres = 0; } else { mr_getline(tmp, fin); paranoid_pclose(fin); p = strchr(tmp, '\t'); if (p) { *p = '\0'; } for (p = tmp, llres = 0; *p != '\0'; p++) { llres *= 10; llres += (int) (*p - '0'); } mr_free(tmp); } mr_free(command); return (llres); } /** * Update a CRC checksum to include another character. * @param crc The original CRC checksum. * @param c The character to add. * @return The new CRC checksum. * @ingroup utilityGroup */ unsigned int updcrc(unsigned int crc, unsigned int c) { unsigned int tmp; tmp = (crc >> 8) ^ c; crc = (crc << 8) ^ crctttab[tmp & 255]; return crc; } /** * Update a reverse CRC checksum to include another character. * @param crc The original CRC checksum. * @param c The character to add. * @return The new CRC checksum. * @ingroup utilityGroup */ unsigned int updcrcr(unsigned int crc, unsigned int c) { unsigned int tmp; tmp = crc ^ c; crc = (crc >> 8) ^ crc16tab[tmp & 0xff]; return crc; } /** * Check for an executable on the user's system; write a message to the * screen and the log if we can't find it. * @param fname The executable basename to look for. * @return 0 if it's found, nonzero if not. */ int whine_if_not_found(char *fname) { /*@ buffers *** */ char *command = NULL; int res = 0; mr_asprintf(command, "which %s > /dev/null 2> /dev/null", fname); res = system(command); mr_free(command); if (res) { log_to_screen("Please install '%s'. I cannot find it on your system.", fname); log_to_screen("There may be hyperlink at http://www.mondorescue.org which"); log_to_screen("will take you to the relevant (missing) package."); return (1); } else { return (0); } } /** * Create a data file at @p fname containing @p contents. * The data actually can be multiple lines, despite the name. * @param fname The file to create. * @param contents The data to put in it. * @return 0 for success, 1 for failure. */ int write_one_liner_data_file(char *fname, char *contents) { /*@ pointers *************************************************** */ FILE *fout; int res = 0; /*@ end vars *************************************************** */ assert_string_is_neither_NULL_nor_zerolength(fname); if (!contents) { log_it("%d: Warning - writing NULL to %s", __LINE__, fname); } if (!(fout = fopen(fname, "w"))) { log_it("fname=%s"); log_OS_error("Unable to openout fname"); return (1); } fprintf(fout, "%s\n", contents); paranoid_fclose(fout); return (res); } /** * Read @p fname into @p contents. * @param fname The file to read. * @param contents Where to put its contents. * @return 0 for success, nonzero for failure. */ int read_one_liner_data_file(char *fname, char *contents) { /*@ pointers *************************************************** */ FILE *fin; int res = 0; int i; /*@ end vars *************************************************** */ assert_string_is_neither_NULL_nor_zerolength(fname); if (!contents) { log_it("%d: Warning - reading NULL from %s", __LINE__, fname); } if (!(fin = fopen(fname, "r"))) { log_it("fname=%s", fname); log_OS_error("Unable to openin fname"); return (1); } fscanf(fin, "%s\n", contents); i = strlen(contents); if (i > 0 && contents[i - 1] < 32) { contents[i - 1] = '\0'; } paranoid_fclose(fin); return (res); } /** * Copy the files that Mondo/Mindi need to run to the scratchdir or tempdir. * Currently this includes: copy Mondo's home directory to scratchdir, * copy post-nuke.tgz (if it exists) to tmpdir, * and run "hostname > scratchdir/HOSTNAME". * @param bkpinfo The backup information structure. Fields used: * - @c bkpinfo->postnuke_tarball * - @c bkpinfo->scratchdir * - @c bkpinfo->tmpdir */ void copy_mondo_and_mindi_stuff_to_scratchdir() { /*@ Char buffers ** */ char *command = NULL; char *tmp = NULL; int res = 0; mvaddstr_and_log_it(g_currentY, 0, "Copying Mondo's core files to the scratch directory"); log_msg(4, "g_mondo_home='%s'", g_mondo_home); if (strlen(g_mondo_home) < 2) { find_and_store_mondoarchives_home(g_mondo_home); } mr_asprintf(command, CP_BIN " --parents -pRdf %s %s", g_mondo_home, bkpinfo->scratchdir); log_msg(4, "command = %s", command); res = run_program_and_log_output(command, 1); mr_free(command); if (res) { fatal_error("Failed to copy Mondo's stuff to scratchdir"); } tmp = call_program_and_get_last_line_of_output("which mondorestore",TRUE); if (!tmp[0]) { mr_free(tmp); fatal_error("'which mondorestore' returned null. Where's your mondorestore? `which` can't find it. That's odd. Did you install mondorestore?"); } mr_free(tmp); mr_asprintf(command, "hostname > %s/HOSTNAME", bkpinfo->scratchdir); paranoid_system(command); mr_free(command); if (bkpinfo->postnuke_tarball) { mr_asprintf(command, "cp -f %s %s/post-nuke.tgz", bkpinfo->postnuke_tarball, bkpinfo->tmpdir); res = run_program_and_log_output(command, FALSE); mr_free(command); if (res) { fatal_error("Unable to copy post-nuke tarball to tmpdir"); } } mvaddstr_and_log_it(g_currentY++, 74, "Done."); } /** * Store the client's NETFS configuration in files to be restored at restore-time. * Assumes that @c bkpinfo->media_type = netfs, but does not check for this. * @param bkpinfo The backup information structure. Fields used: * - @c netfs_mount * - @c netfs_remote_dir * - @c tmpdir */ void store_netfs_config() { /*@ buffers ******** */ char *netfs_dev = NULL; char *netfs_client_hwaddr = NULL; char *netfs_mount = NULL; char *netfs_client_ipaddr = NULL; char *netfs_client_netmask = NULL; char *netfs_client_broadcast = NULL; char *netfs_client_defgw = NULL; char *netfs_server_ipaddr = NULL; char *tmp = NULL; char *command = NULL; /*@ pointers ***** */ char *p; if (! bkpinfo->netfs_mount) { fatal_error("No netfs_mount found !"); } log_it("Storing Network configuration"); mr_asprintf(tmp, "%s", bkpinfo->netfs_mount); p = strchr(tmp, ':'); if (!p) { fatal_error("Network mount doesn't have a colon in it, e.g. 192.168.1.4:/home/nfs"); } *(p++) = '\0'; mr_asprintf(netfs_server_ipaddr, "%s", tmp); mr_asprintf(netfs_mount, "%s", p); mr_free(tmp); /* BERLIOS : there is a bug #67 here as it only considers the first NIC */ mr_asprintf(command, "ifconfig | tr '\n' '#' | sed s/##// | tr '#' ' ' | tr '' '\n' | head -n1 | cut -d' ' -f1"); netfs_dev = call_program_and_get_last_line_of_output(command,TRUE); mr_free(command); mr_asprintf(command, "%s", "ifconfig | tr '\n' '#' | sed s/##// | tr '#' ' ' | tr '' '\\n' | head -n1 | tr -s '\t' ' ' | cut -d' ' -f7 | cut -d':' -f2"); netfs_client_ipaddr = call_program_and_get_last_line_of_output(command,TRUE); mr_free(command); mr_asprintf(command, "%s", "ifconfig | tr '\n' '#' | sed s/##// | tr '#' ' ' | tr '' '\\n' | head -n1 | tr -s '\t' ' ' | cut -d' ' -f9 | cut -d':' -f2"); netfs_client_netmask = call_program_and_get_last_line_of_output(command,TRUE); mr_free(command); mr_asprintf(command, "%s", "ifconfig | tr '\n' '#' | sed s/##// | tr '#' ' ' | tr '' '\\n' | head -n1 | tr -s '\t' ' ' | cut -d' ' -f8 | cut -d':' -f2"); netfs_client_broadcast = call_program_and_get_last_line_of_output(command,TRUE); mr_free(command); mr_asprintf(command, "%s", "route -n | grep '^0.0.0.0' | awk '{print $2}'"); netfs_client_defgw = call_program_and_get_last_line_of_output(command,TRUE); mr_free(command); if (strlen(netfs_dev) < 2) { fatal_error("Unable to find ethN (eth0, eth1, ...) adapter via Network mount you specified."); } /******** * If the Network device that found above is a bonded device, * we need to replace it with an ethN device or the * networking will not start during an Network restore. * * If the Network device in netfs_dev begins with the word "bond", or alb or aft * look for the corresponding slave ethN device and copy it to netfs_dev. * Using the common MAC address ********/ if (!strncmp(netfs_dev, "bond", 4) || !strncmp(netfs_dev, "alb", 3) || !strncmp(netfs_dev, "aft", 3)) { log_to_screen("Found bonding device %s; looking for corresponding ethN slave device\n", netfs_dev); mr_asprintf(command, "ifconfig %s | awk '{print $5}' | head -n1", netfs_dev); netfs_client_hwaddr = call_program_and_get_last_line_of_output(command,TRUE); mr_free(command); mr_asprintf(command, "ifconfig | grep -E '%s' | grep -v '%s' | head -n1 | cut -d' ' -f1", netfs_client_hwaddr, netfs_dev); mr_free(netfs_dev); netfs_dev = call_program_and_get_last_line_of_output(command,TRUE); mr_free(command); log_to_screen("Replacing it with %s\n", netfs_dev); } mr_asprintf(tmp, "%s/NETFS-DEV", bkpinfo->tmpdir); write_one_liner_data_file(tmp, netfs_dev); mr_free(netfs_dev); mr_free(tmp); mr_asprintf(tmp, "%s/NETFS-SERVER-MOUNT", bkpinfo->tmpdir); write_one_liner_data_file(tmp, bkpinfo->netfs_mount); mr_free(tmp); mr_free(netfs_mount); mr_asprintf(tmp, "%s/NETFS-CLIENT-IPADDR", bkpinfo->tmpdir); write_one_liner_data_file(tmp, netfs_client_ipaddr); mr_free(tmp); mr_asprintf(tmp, "%s/NETFS-CLIENT-HWADDR", bkpinfo->tmpdir); write_one_liner_data_file(tmp, netfs_client_hwaddr); mr_free(tmp); mr_asprintf(tmp, "%s/NETFS-CLIENT-NETMASK", bkpinfo->tmpdir); write_one_liner_data_file(tmp, netfs_client_netmask); mr_free(tmp); mr_asprintf(tmp, "%s/NETFS-CLIENT-BROADCAST", bkpinfo->tmpdir); write_one_liner_data_file(tmp, netfs_client_broadcast); mr_free(tmp); mr_asprintf(tmp, "%s/NETFS-CLIENT-DEFGW", bkpinfo->tmpdir); write_one_liner_data_file(tmp, netfs_client_defgw); mr_free(tmp); mr_asprintf(tmp, "%s/NETFS-SERVER-IPADDR", bkpinfo->tmpdir); write_one_liner_data_file(tmp, netfs_server_ipaddr); mr_free(tmp); mr_asprintf(tmp, "%s/NETFS-SERVER-MOUNT", bkpinfo->tmpdir); write_one_liner_data_file(tmp, bkpinfo->netfs_mount); mr_free(tmp); if (bkpinfo->netfs_user) { mr_asprintf(tmp, "%s/NETFS-SERVER-USER", bkpinfo->tmpdir); write_one_liner_data_file(tmp, bkpinfo->netfs_user); mr_free(tmp); } mr_asprintf(tmp, "%s/NETFS-SERVER-PATH", bkpinfo->tmpdir); write_one_liner_data_file(tmp, bkpinfo->netfs_remote_dir); mr_free(tmp); mr_asprintf(tmp, "%s/ISO-PREFIX", bkpinfo->tmpdir); write_one_liner_data_file(tmp, bkpinfo->prefix); mr_free(tmp); log_it("Finished storing Network configuration"); } /** * Determine the approximate number of media that the backup will take up, * and tell the user. The uncompressed size is estimated as size_of_all_biggiefiles_K() * plus (noof_sets x bkpinfo->optimal_set_size). The compression factor is estimated as * 2/3 for LZO and 1/2 for bzip2. The data is not saved anywhere. If there are any * "imagedevs", the estimate is not shown as it will be wildly inaccurate. * If there are more than 50 media estimated, the estimate will not be shown. * @param bkpinfo The backup information structure. Fields used: * - @c bkpinfo->backup_media_type * - @c bkpinfo->media_size * - @c bkpinfo->optimal_set_size * - @c bkpinfo->use_lzo * @param noof_sets The number of filesets created. * @ingroup archiveGroup */ void estimate_noof_media_required(long noof_sets) { /*@ buffers *************** */ char *tmp = NULL; char *mds = NULL; /*@ long long ************* */ long long scratchLL; if (bkpinfo->media_size[1] <= 0) { log_to_screen("Number of media required: UNKNOWN"); return; } log_it("Estimating number of media required..."); scratchLL = (long long) (noof_sets) * (long long) (bkpinfo->optimal_set_size) + (long long) (size_of_all_biggiefiles_K()); scratchLL = (scratchLL / 1024) / bkpinfo->media_size[1]; scratchLL++; if (bkpinfo->use_lzo) { scratchLL = (scratchLL * 2) / 3; } else if (bkpinfo->use_gzip) { scratchLL = (scratchLL * 2) / 3; } else if (bkpinfo->use_lzma) { scratchLL = (scratchLL * 2) / 3; } else { scratchLL = scratchLL / 2; } if (!scratchLL) { scratchLL++; } if (scratchLL <= 1) { mds = media_descriptor_string(bkpinfo->backup_media_type); mr_asprintf(tmp, "Your backup will probably occupy a single %s. Maybe two.", mds); mr_free(mds); } else if (scratchLL > 4) { mr_asprintf(tmp, "Your backup will occupy one meeeeellion media! (maybe %s)", number_to_text((int) (scratchLL + 1))); } else { mr_asprintf(tmp, "Your backup will occupy approximately %s media.", number_to_text((int) (scratchLL + 1))); } if (scratchLL < 50) { log_to_screen(tmp); } mr_free(tmp); } /** * Determine whether a file is compressed. This is done * by reading through the "do-not-compress-these" file distributed with Mondo. * @param filename The file to check. * @return TRUE if it's compressed, FALSE if not. */ bool is_this_file_compressed(char *filename) { char *do_not_compress_these = NULL; char *tmp = NULL; char *p; char *q = NULL; q = strrchr(filename, '.'); if (q == NULL) { return (FALSE); } mr_asprintf(tmp, "%s/do-not-compress-these", g_mondo_home); if (!does_file_exist(tmp)) { mr_free(tmp); return (FALSE); } /* BERLIOS: This is just plain WRONG !! */ do_not_compress_these = last_line_of_file(tmp); mr_free(tmp); for (p = do_not_compress_these; p != NULL; p++) { mr_asprintf(tmp, "%s", p); if (strchr(tmp, ' ')) { *(strchr(tmp, ' ')) = '\0'; } if (!strcmp(q, tmp)) { mr_free(tmp); mr_free(do_not_compress_these); return (TRUE); } if (!(p = strchr(p, ' '))) { break; } mr_free(tmp); } mr_free(do_not_compress_these); return (FALSE); } /* @} - end fileGroup */