/* $Id: libmondo-tools.c 2383 2009-09-10 00:55:51Z bruno $ */ /** * @file * Miscellaneous tools that didn't really fit anywhere else. */ #include "my-stuff.h" #include "mr_mem.h" #include "mondostructures.h" #include "lib-common-externs.h" #include "libmondo-tools.h" #include "libmondo-gui-EXT.h" #include "libmondo-files-EXT.h" #include "libmondo-fork-EXT.h" #include "libmondo-raid-EXT.h" #include "libmondo-devices-EXT.h" #include #include #include #include #include #include /*@unused@*/ //static char cvsid[] = "$Id: libmondo-tools.c 2383 2009-09-10 00:55:51Z bruno $"; extern int g_tape_buffer_size_MB; extern bool g_text_mode; extern int g_currentY; extern int g_current_media_number; extern char *MONDO_LOGFILE; extern char *g_serial_string; /* Reference to global bkpinfo */ extern struct s_bkpinfo *bkpinfo; /** * @addtogroup globalGroup * @{ */ bool g_remount_cdrom_at_end, ///< TRUE if we unmounted the CD-ROM and should remount it when done with the backup. g_remount_floppy_at_end; ///< TRUE if we unmounted the floppy and should remount it when done with the backup. bool g_cd_recovery; ///< TRUE if we're making an "autonuke" backup. double g_kernel_version; /** * The place where /boot is mounted. */ char *g_boot_mountpt = NULL; /** * The location of Mondo's home directory. */ char *g_mondo_home = NULL; /** * The location where tmpfs is mounted, or "" if it's not mounted. char *g_tmpfs_mountpt = NULL; */ char *g_magicdev_command = NULL; /** * The default maximum level to log messages at or below. */ int g_loglevel = DEFAULT_DEBUG_LEVEL; /* @} - end of globalGroup */ extern pid_t g_buffer_pid; extern pid_t g_main_pid; extern t_bkptype g_backup_media_type; extern bool am_I_in_disaster_recovery_mode(void); /*-----------------------------------------------------------*/ /** * @addtogroup utilityGroup * @{ */ /** * Assertion handler. Prints a friendly message to the user, * offering to ignore all, dump core, break to debugger, * exit, or ignore. Intended to be used with an assert() macro. * * @param file The file in which the assertion triggered. * @param function The function (@c __FUNCTION__) in which the assertion triggered. * @param line The line number of the assert() statement. * @param exp The expression that failed (as a string). */ void _mondo_assert_fail(const char *file, const char *function, int line, const char *exp) { static int ignoring_assertions = 0; bool is_valid = TRUE; log_it("ASSERTION FAILED: `%s' at %s:%d in %s", exp, file, line, function); if (ignoring_assertions) { log_it("Well, the user doesn't care..."); return; } if (!g_text_mode) newtSuspend(); printf("ASSERTION FAILED: `%s'\n", exp); printf("\tat %s:%d in %s\n\n", file, line, function); printf("(I)gnore, ignore (A)ll, (D)ebug, a(B)ort, or (E)xit? "); do { is_valid = TRUE; switch (toupper(getchar())) { case 'A': // ignore (A)ll ignoring_assertions = 1; break; case 'B': // a(B)ort signal(SIGABRT, SIG_DFL); /* prevent SIGABRT handler from running */ raise(SIGABRT); break; /* "can't get here" */ case 'D': // (D)ebug, aka asm("int 3") #ifdef __IA32__ __asm__ __volatile__("int $3"); // break to debugger #endif break; case 'E': // (E)xit fatal_error("Failed assertion -- see above for details"); break; /* "can't get here" */ case 'I': // (I)gnore break; /* These next two work as follows: the `default' catches the user's invalid choice and says so; the '\n' catches the newline on the end and prints the prompt again. */ case '\n': printf ("(I)gnore, ignore (A)ll, (D)ebug, a(B)ort, or (E)xit? "); break; default: is_valid = FALSE; printf("Invalid choice.\n"); break; } } while (!is_valid); if (ignoring_assertions) { log_it("Ignoring ALL assertions from now on."); } else { log_it("Ignoring assertion: %s", exp); } getchar(); // skip \n if (!g_text_mode) newtResume(); } /** * Clean's up users' KDE desktops. * @bug Details about this function are unknown. */ void clean_up_KDE_desktop_if_necessary(void) { char *tmp = NULL; mr_asprintf(tmp, "for i in `find /root /home -type d -name Desktop -maxdepth 2`; do file=$i/.directory; if [ -f \"$file\" ] ; then mv -f $file $file.old ; awk '{if (index($0, \"rootimagesmindi\")) { while (length($0)>2) { getline;} ; } else { print $0;};}' $file.old > $file ; fi ; done"); run_program_and_log_output(tmp, 5); mr_free(tmp); } /** * Locate mondoarchive's home directory. Searches in /usr/local/mondo, /usr/share/mondo, * /usr/local/share/mondo, /opt, or if all else fails, search /usr. * * @param home_sz String to store the home directory ("" if it could not be found). * @return 0 for success, nonzero for failure. */ int find_and_store_mondoarchives_home(char *home_sz) { assert(home_sz != NULL); strcpy(home_sz, MONDO_SHARE); return (0); } char *get_architecture(void) { #ifdef __IA32__ # ifdef __X86_64__ return ("x86_64"); # else return ("i386"); # endif #endif #ifdef __IA64__ return ("ia64"); #endif return ("unknown"); } char *get_uname_m(void) { struct utsname utsn; char *tmp = NULL; uname(&utsn); mr_asprintf(tmp, "%s", utsn.machine); return (tmp); } double get_kernel_version(void) { char *p = NULL; char *tmp = NULL; double d; #ifdef __FreeBSD__ // JOSH - FIXME :) d = 5.2; // :-) #else tmp = call_program_and_get_last_line_of_output("uname -r"); p = strchr(tmp, '.'); if (p) { p = strchr(++p, '.'); if (p) { while (*p) { *p = *(p + 1); p++; } } } d = atof(tmp); mr_free(tmp); #endif log_msg(1, "g_kernel_version = %f", d); return (d); } /** * Get the current time. * @return number of seconds since the epoch. */ long get_time() { return (long) time((void *) 0); } /** * Initialize a RAID volume structure, setting fields to zero. The * actual hard drive is unaffected. * * @param raidrec The RAID volume structure to initialize. * @note This function is system dependent. */ #ifdef __FreeBSD__ void initialize_raidrec(struct vinum_volume *raidrec) { int i, j; raidrec->volname[0] = '\0'; raidrec->plexes = 0; for (i = 0; i < 9; ++i) { raidrec->plex[i].raidlevel = -1; raidrec->plex[i].stripesize = 0; raidrec->plex[i].subdisks = 0; for (j = 0; j < 9; ++j) { strcpy(raidrec->plex[i].sd[j].which_device, ""); } } } #else void initialize_raidrec(struct raid_device_record *raidrec) { assert(raidrec != NULL); raidrec->raid_device[0] = '\0'; raidrec->raid_level = -9; raidrec->persistent_superblock = 1; raidrec->chunk_size = 64; raidrec->parity = -1; raidrec->data_disks.entries = 0; raidrec->spare_disks.entries = 0; raidrec->parity_disks.entries = 0; raidrec->failed_disks.entries = 0; raidrec->additional_vars.entries = 0; } #endif /** * Insert modules that Mondo requires. * Currently inserts @c msdos, @c vfat, and @c loop for Linux; * @c msdosfs and @c ext2fs for FreeBSD. */ void insmod_crucial_modules(void) { #ifdef __FreeBSD__ system("kldstat | grep msdosfs || kldload msdosfs 2> /dev/null"); system("kldstat | grep ext2fs || kldload ext2fs 2> /dev/null"); #else system("modprobe -a msdos vfat loop &> /dev/null"); #endif } /** * Log a trace message to the trace file. * @bug This function seems orphaned. Please remove. */ void log_trace(char *o) { /*@ pointers **************************************************** */ FILE *fout; /*@ buffers ***************************************************** */ char output[MAX_STR_LEN]; /*@ int ****************************************************** */ int i; /*@ end vars *************************************************** */ if (o[0] == '\0') { return; } strcpy(output, o); i = (int) strlen(output); if (i <= 0) { return; } if (output[i - 1] < 32) { output[i - 1] = '\0'; } if (g_text_mode) { printf("%s\n", output); } fout = fopen(MONDO_TRACEFILE, "a"); if (fout) { fprintf(fout, "%s\n", output); paranoid_fclose(fout); } else { log_OS_error("Cannot write to tracefile"); } } /** * Finish configuring the backup information structure. Call this function * to set the parameters that depend on those that can be given on the command * line. * * @param bkpinfo The backup information structure. Fields modified/used: * - Used: @c bkpinfo->backup_data * - Used: @c bkpinfo->backup_media_type * - Used: @c bkpinfo->cdrw_speed * - Used: @c bkpinfo->compression_level * - Used: @c bkpinfo->include_paths * - Used: @c bkpinfo->prefix * - Used: @c bkpinfo->isodir * - Used: @c bkpinfo->manual_cd_tray * - Used: @c bkpinfo->make_cd_use_lilo * - Used: @c bkpinfo->media_device * - Used: @c bkpinfo->netfs_mount * - Used: @c bkpinfo->nonbootable_backup * - Used: @c bkpinfo->scratchdir * - Used: @c bkpinfo->tmpdir * - Used: @c bkpinfo->use_lzo * - Modified: @c bkpinfo->call_before_iso * - Modified: @c bkpinfo->call_make_iso * - Modified: @c bkpinfo->optimal_set_size * - Modified: @c bkpinfo->zip_exe * - Modified: @c bkpinfo->zip_suffix * * @return number of errors, or 0 for success. * @note Also creates directories that are specified in the @c bkpinfo structure but * do not exist. */ int post_param_configuration() { char *extra_cdrom_params = NULL; char *mondo_mkisofs_sz = NULL; char *command = NULL; char *mtpt; char *hostname = NULL; char *ip_address = NULL; int retval = 0; char *colon; char *cdr_exe = NULL; char *tmp = NULL; char *p = NULL; char *call_before_iso_user = NULL; char *iso_dev = NULL; char *iso_mnt = NULL; char *iso_tmp = NULL; char *iso_path = NULL; assert(bkpinfo != NULL); if (!bkpinfo->tmpdir) { fatal_error("Tmpdir set to NULL !"); } malloc_string(mtpt); bkpinfo->optimal_set_size = (IS_THIS_A_STREAMING_BACKUP(bkpinfo->backup_media_type) ? 16 : 16) * 1024; if (strlen(bkpinfo->tmpdir) < 2) { log_it("tmpdir is blank/missing"); retval++; } log_msg(1, "Foo"); if (bkpinfo->backup_media_type == tape) { log_msg(1, "Bar"); if (bkpinfo->media_device == NULL) { return(1); } mr_asprintf(tmp, "mt -f %s status", bkpinfo->media_device); log_msg(1, "tmp = '%s'", tmp); if (run_program_and_log_output(tmp, 3)) { mr_free(tmp); fatal_error ("Unable to open tape device. If you haven't specified it with -d, do so. If you already have, check your parameter. I think it's wrong."); } mr_free(tmp); } if (bkpinfo->scratchdir) { make_hole_for_dir(bkpinfo->scratchdir); chmod(bkpinfo->scratchdir, 0700); } else { fatal_error("Scratchdir set to NULL !"); } if (bkpinfo->backup_media_type == iso) make_hole_for_dir(bkpinfo->isodir); run_program_and_log_output("uname -a", 5); run_program_and_log_output("cat /etc/*-release", 5); run_program_and_log_output("cat /etc/*issue*", 5); #ifdef __FreeBSD__ #else run_program_and_log_output("cat /proc/cpuinfo", 5); run_program_and_log_output ("rpm -q newt newt-devel slang slang-devel ncurses ncurses-devel gcc", 5); #endif if (bkpinfo->use_lzo) { mr_asprintf(bkpinfo->zip_exe, "lzop"); mr_asprintf(bkpinfo->zip_suffix, "lzo"); } else if (bkpinfo->use_gzip) { mr_asprintf(bkpinfo->zip_exe, "gzip"); mr_asprintf(bkpinfo->zip_suffix, "gz"); } else if (bkpinfo->use_lzma) { mr_asprintf(bkpinfo->zip_exe, "lzma"); mr_asprintf(bkpinfo->zip_suffix, "lzma"); } else if (bkpinfo->compression_level != 0) { mr_asprintf(bkpinfo->zip_exe, "bzip2"); mr_asprintf(bkpinfo->zip_suffix, "bz2"); } else { mr_asprintf(bkpinfo->zip_exe, "none"); mr_asprintf(bkpinfo->zip_suffix, ""); } // DVD if (bkpinfo->backup_media_type == dvd) { tmp = find_home_of_exe("growisofs"); if (tmp) { mr_asprintf(cdr_exe, "growisofs"); } // unlikely to be used else { mr_free(tmp); fatal_error("Please install growisofs."); } mr_free(tmp); if (bkpinfo->nonbootable_backup) { mr_asprintf(mondo_mkisofs_sz, MONDO_GROWISOFS_NONBOOT); } else if #ifdef __FreeBSD__ (TRUE) #else (bkpinfo->make_cd_use_lilo) #endif #ifdef __IA64__ { mr_asprintf(mondo_mkisofs_sz, MONDO_GROWISOFS_REGULAR_ELILO); } #else { mr_asprintf(mondo_mkisofs_sz, MONDO_GROWISOFS_REGULAR_LILO); } #endif else { mr_asprintf(mondo_mkisofs_sz, MONDO_GROWISOFS_REGULAR_SYSLINUX); } if (bkpinfo->manual_cd_tray) { paranoid_free(mondo_mkisofs_sz); fatal_error("Manual CD tray + DVD not supported yet."); // -m isn't supported by growisofs, BTW... } else { if (bkpinfo->media_device == NULL) { return(1); } mr_free(bkpinfo->call_make_iso); mr_asprintf(bkpinfo->call_make_iso, "%s %s -Z %s . 2>> _ERR_", mondo_mkisofs_sz, "", bkpinfo->media_device); paranoid_free(mondo_mkisofs_sz); } if (getenv ("SUDO_COMMAND")) { mr_asprintf(command, "strings `which growisofs` | grep -c SUDO_COMMAND"); tmp = call_program_and_get_last_line_of_output(command); if (!strcmp(tmp, "1")) { mr_free(command); mr_free(tmp); popup_and_OK("Fatal Error: Can't write DVDs as sudo because growisofs doesn't support this - see the growisofs manpage for details."); fatal_error("Can't write DVDs as sudo because growisofs doesn't support this - see the growisofs manpage for details."); } mr_free(command); mr_free(tmp); } log_msg(2, "call_make_iso (DVD res) is ... %s", bkpinfo->call_make_iso); } // end of DVD code // CD-R or CD-RW if (bkpinfo->backup_media_type == cdrw || bkpinfo->backup_media_type == cdr) { if (!bkpinfo->manual_cd_tray) { mr_asprintf(extra_cdrom_params, "-waiti "); } if (bkpinfo->backup_media_type == cdrw) { mr_asprintf(extra_cdrom_params, "blank=fast "); } tmp = find_home_of_exe("cdrecord"); if (tmp) { mr_asprintf(cdr_exe, "cdrecord"); } else { mr_free(tmp); tmp = find_home_of_exe("dvdrecord"); if (tmp) { mr_asprintf(cdr_exe, "dvdrecord"); } else { mr_free(tmp); fatal_error("Please install either cdrecord or dvdrecord."); } } mr_free(tmp); if (bkpinfo->nonbootable_backup) { mr_asprintf(mondo_mkisofs_sz, MONDO_MKISOFS_NONBOOT); } else if #ifdef __FreeBSD__ (TRUE) #else (bkpinfo->make_cd_use_lilo) #endif #ifdef __IA64__ { mr_asprintf(mondo_mkisofs_sz, MONDO_MKISOFS_REGULAR_ELILO); } #else { mr_asprintf(mondo_mkisofs_sz, MONDO_MKISOFS_REGULAR_LILO); } #endif else { mr_asprintf(mondo_mkisofs_sz, MONDO_MKISOFS_REGULAR_SYSLINUX); } mr_free(bkpinfo->call_make_iso); if (bkpinfo->manual_cd_tray) { if (bkpinfo->call_before_iso == NULL) { mr_asprintf(bkpinfo->call_before_iso, "%s -o %s/"MONDO_TMPISOS" . 2>> _ERR_", mondo_mkisofs_sz, bkpinfo->tmpdir); } else { mr_asprintf(call_before_iso_user, "%s", bkpinfo->call_before_iso); mr_free(bkpinfo->call_before_iso); mr_asprintf(bkpinfo->call_before_iso, "( %s -o %s/"MONDO_TMPISOS" . 2>> _ERR_ ; %s )", mondo_mkisofs_sz, bkpinfo->tmpdir, call_before_iso_user); mr_free(call_before_iso_user); } log_it("bkpinfo->call_before_iso = %s", bkpinfo->call_before_iso); if (bkpinfo->media_device == NULL) { return(1); } mr_asprintf(bkpinfo->call_make_iso, "%s %s -v %s fs=4m dev=%s speed=%d %s/"MONDO_TMPISOS, cdr_exe, (bkpinfo->please_dont_eject) ? " " : "-eject", extra_cdrom_params, bkpinfo->media_device, bkpinfo->cdrw_speed, bkpinfo->tmpdir); } else { mr_asprintf(bkpinfo->call_make_iso, "%s . 2>> _ERR_ | %s %s %s fs=4m dev=%s speed=%d -", mondo_mkisofs_sz, cdr_exe, (bkpinfo->please_dont_eject) ? " " : "-eject", extra_cdrom_params, bkpinfo->media_device, bkpinfo->cdrw_speed); } mr_free(cdr_exe); paranoid_free(mondo_mkisofs_sz); paranoid_free(extra_cdrom_params); } // end of CD code if (bkpinfo->backup_media_type == iso) { /* Patch by Conor Daly * 23-june-2004 * Break up isodir into iso_mnt and iso_path * These will be used along with iso-dev at restore time * to locate the ISOs where ever they're mounted */ log_it("isodir = %s", bkpinfo->isodir); mr_asprintf(command, "df -P %s | tail -n1 | cut -d' ' -f1", bkpinfo->isodir); log_it("command = %s", command); iso_dev = call_program_and_get_last_line_of_output(command); log_it("res of it = %s", iso_dev); mr_asprintf(tmp, "%s/ISO-DEV", bkpinfo->tmpdir); write_one_liner_data_file(tmp, iso_dev); mr_free(tmp); mr_free(command); mr_asprintf(command, "mount | grep -w %s | tail -n1 | cut -d' ' -f3", iso_dev); mr_free(iso_dev); log_it("command = %s", command); iso_mnt = call_program_and_get_last_line_of_output(command); log_it("res of it = %s", iso_mnt); mr_asprintf(tmp, "%s/ISO-MNT", bkpinfo->tmpdir); write_one_liner_data_file(tmp, iso_mnt); mr_free(tmp); mr_free(command); log_it("isomnt: %s, %d", iso_mnt, strlen(iso_mnt)); mr_asprintf(iso_tmp, "%s", bkpinfo->isodir); if (strlen(iso_tmp) < strlen(iso_mnt)) { mr_asprintf(iso_path, "%s", ""); } else { mr_asprintf(iso_path, "%s", iso_tmp + strlen(iso_mnt)); } mr_free(iso_mnt); mr_free(iso_tmp); mr_asprintf(tmp, "%s/ISODIR", bkpinfo->tmpdir); write_one_liner_data_file(tmp, iso_path); mr_free(tmp); log_it("isodir: %s", iso_path); mr_free(iso_path); mr_asprintf(tmp, "%s/ISO-PREFIX", bkpinfo->tmpdir); write_one_liner_data_file(tmp, bkpinfo->prefix); mr_free(tmp); log_it("iso-prefix: %s", bkpinfo->prefix); /* End patch */ } // end of iso code if (bkpinfo->backup_media_type == netfs) { if (bkpinfo->netfs_mount) { mr_asprintf(hostname, "%s", bkpinfo->netfs_mount); } else { log_it("netfs_mount is NULL"); retval++; mr_asprintf(hostname, ""); } colon = strchr(hostname, ':'); if (!colon) { log_it("netfs mount doesn't have a colon in it"); retval++; } else { struct hostent *hent; *colon = '\0'; hent = gethostbyname(hostname); if (!hent) { log_it("Can't resolve Network mount (%s): %s", hostname, hstrerror(h_errno)); retval++; } else { mr_asprintf(ip_address, "%s", inet_ntoa((struct in_addr) *((struct in_addr *) hent->h_addr))); mr_strcat(ip_address, strchr(bkpinfo->netfs_mount, ':')); mr_free(bkpinfo->netfs_mount); bkpinfo->netfs_mount = ip_address; } } mr_free(hostname); store_netfs_config(); } log_it("Finished processing incoming params"); if (retval) { fprintf(stderr, "Type 'man mondoarchive' for help.\n"); } if (bkpinfo->include_paths == NULL) { mr_asprintf(p, "/"); bkpinfo->include_paths = p; } g_backup_media_type = bkpinfo->backup_media_type; paranoid_free(mtpt); return (retval); } /** * Do some miscellaneous setup tasks to be performed before filling @c bkpinfo. * Seeds the random-number generator, loads important modules, checks the sanity * of the user's Linux distribution, and deletes logfile. * @param bkpinfo The backup information structure. Will be initialized. * @return number of errors (0 for success) */ int pre_param_configuration() { int res = 0; char *tmp = NULL; make_hole_for_dir(MNT_CDROM); assert(bkpinfo != NULL); srandom((unsigned long) (time(NULL))); insmod_crucial_modules(); if (bkpinfo->disaster_recovery) { if (!does_nonMS_partition_exist()) { fatal_error("I am in disaster recovery mode\nPlease don't run mondoarchive."); } } unlink(MONDO_TRACEFILE); mr_asprintf(tmp,"rm -Rf %s/changed.files*",MONDO_CACHE); run_program_and_log_output(tmp, FALSE); paranoid_free(tmp); if (find_and_store_mondoarchives_home(g_mondo_home)) { fprintf(stderr, "Cannot find Mondo's homedir. I think you have >1 'mondo' directory on your hard disk. Please delete the superfluous 'mondo' directories and try again\n"); res++; return (res); } res += some_basic_system_sanity_checks(); if (res) { log_it("Your distribution did not pass Mondo's sanity test."); } g_current_media_number = 1; return (res); } void setup_tmpdir(char *path) { char *tmp = NULL; char *p = NULL; if ((bkpinfo->tmpdir != NULL) && (strstr(bkpinfo->tmpdir,"mondo.tmp.") != NULL)) { /* purging a potential old tmpdir */ log_it("Purging old tmpdir %s", bkpinfo->tmpdir); mr_asprintf(tmp,"rm -Rf %s",bkpinfo->tmpdir); mr_free(bkpinfo->tmpdir); (void)system(tmp); mr_free(tmp); } if (path != NULL) { mr_asprintf(tmp, "%s/mondo.tmp.XXXXXX", path); } else if (getenv("TMPDIR")) { mr_asprintf(tmp, "%s/mondo.tmp.XXXXXX", getenv("TMPDIR")); } else if (getenv("TMP")) { mr_asprintf(tmp, "%s/mondo.tmp.XXXXXX", getenv("TMP")); } else { mr_asprintf(tmp, "/tmp/mondo.tmp.XXXXXX"); } p = mkdtemp(tmp); if (p == NULL) { log_it("Failed to create global tmp directory %s for Mondo.",tmp); mr_free(tmp); finish(-1); } bkpinfo->tmpdir = p; log_it("bkpinfo->tmpdir is being set to %s", bkpinfo->tmpdir); } /* Free all memory allocated into bkpinfo */ void mr_free_bkpinfo() { log_msg(1, "Freeing bkpinfo"); mr_free(bkpinfo->media_device); mr_free(bkpinfo->boot_device); mr_free(bkpinfo->zip_exe); mr_free(bkpinfo->zip_suffix); mr_free(bkpinfo->image_devs); mr_free(bkpinfo->isodir); mr_free(bkpinfo->prefix); mr_free(bkpinfo->scratchdir); mr_free(bkpinfo->tmpdir); mr_free(bkpinfo->include_paths); mr_free(bkpinfo->exclude_paths); mr_free(bkpinfo->restore_path); mr_free(bkpinfo->call_before_iso); mr_free(bkpinfo->call_make_iso); mr_free(bkpinfo->call_burn_iso); mr_free(bkpinfo->call_after_iso); mr_free(bkpinfo->kernel_path); mr_free(bkpinfo->netfs_mount); mr_free(bkpinfo->netfs_remote_dir); mr_free(bkpinfo->netfs_user); mr_free(bkpinfo->netfs_proto); mr_free(bkpinfo->postnuke_tarball); /* Then free the structure */ mr_free(bkpinfo); } /** * Reset all fields of the backup information structure to a sensible default. * @param bkpinfo The @c bkpinfo to reset. */ void init_bkpinfo() { int i; log_msg(1, "Initializing bkpinfo"); bkpinfo = (struct s_bkpinfo *)mr_malloc(sizeof(struct s_bkpinfo)); /* Initialized in same order as in the structure declaration to detect errors more easily */ bkpinfo->media_device = NULL; for (i = 0; i <= MAX_NOOF_MEDIA; i++) { bkpinfo->media_size[i] = -1; } bkpinfo->boot_loader = '\0'; bkpinfo->boot_device = NULL; bkpinfo->zip_exe = NULL; bkpinfo->zip_suffix = NULL; bkpinfo->image_devs = NULL; bkpinfo->compression_level = 3; bkpinfo->use_lzo = FALSE; bkpinfo->use_gzip = FALSE; bkpinfo->use_lzma = FALSE; bkpinfo->verify_data = FALSE; bkpinfo->backup_data = FALSE; bkpinfo->restore_data = FALSE; bkpinfo->use_star = FALSE; bkpinfo->internal_tape_block_size = DEFAULT_INTERNAL_TAPE_BLOCK_SIZE; bkpinfo->disaster_recovery = (am_I_in_disaster_recovery_mode()? TRUE : FALSE); if (bkpinfo->disaster_recovery) { mr_asprintf(bkpinfo->isodir, "%s", "/"); } else { mr_asprintf(bkpinfo->isodir, "%s", MONDO_CACHE); } mr_asprintf(bkpinfo->prefix, "%s", STD_PREFIX); bkpinfo->scratchdir = NULL; // Really setup after bkpinfo->tmpdir = NULL; // Really setup after bkpinfo->optimal_set_size = 0; bkpinfo->backup_media_type = none; bkpinfo->make_filelist = TRUE; // unless -J supplied to mondoarchive bkpinfo->include_paths = NULL; bkpinfo->exclude_paths = NULL; bkpinfo->restore_path = NULL; bkpinfo->call_before_iso = NULL; bkpinfo->call_make_iso = NULL; bkpinfo->call_burn_iso = NULL; bkpinfo->call_after_iso = NULL; bkpinfo->kernel_path = NULL; bkpinfo->netfs_mount = NULL; bkpinfo->netfs_remote_dir = NULL; bkpinfo->netfs_user = NULL; bkpinfo->netfs_proto = NULL; bkpinfo->postnuke_tarball = NULL; bkpinfo->wipe_media_first = FALSE; bkpinfo->differential = 0; bkpinfo->please_dont_eject = FALSE; bkpinfo->cdrw_speed = 0; bkpinfo->manual_cd_tray = FALSE; bkpinfo->nonbootable_backup = FALSE; bkpinfo->make_cd_use_lilo = FALSE; bkpinfo->use_obdr = FALSE; bkpinfo->restore_mode = interactive; setup_tmpdir(NULL); sensibly_set_scratchdir(); } /** * Get the remaining free space (in MB) on @p partition. * @param partition The partition to check free space on (either a device or a mountpoint). * @return The free space on @p partition, in MB. */ long free_space_on_given_partition(char *partition) { char *out_sz = NULL; char *command = NULL; long res; assert_string_is_neither_NULL_nor_zerolength(partition); mr_asprintf(command, "df -m -P %s 1> /dev/null 2> /dev/null", partition); if (system(command)) { mr_free(command); return (-1); } // partition does not exist mr_free(command); mr_asprintf(command, "df -m -P %s | tail -n1 | tr -s ' ' '\t' | cut -f4", partition); out_sz = call_program_and_get_last_line_of_output(command); mr_free(command); if (strlen(out_sz) == 0) { mr_free(out_sz); return (-1); } // error within df, probably res = atol(out_sz); mr_free(out_sz); return (res); } /** * Check the user's system for sanity. Checks performed: * - make sure user has enough RAM (32mb required, 64mb recommended) * - make sure user has enough free space in @c / * - check kernel for ramdisk support * - make sure afio, cdrecord, mkisofs, bzip2, awk, md5sum, strings, mindi, and buffer exist * - make sure CD-ROM is unmounted * - make sure user's mountlist is OK by running mindi --makemountlist * * @return number of problems with the user's setup (0 for success) */ int some_basic_system_sanity_checks() { /*@ buffers ************ */ char *tmp = NULL; /*@ int's *************** */ int retval = 0; mvaddstr_and_log_it(g_currentY, 0, "Checking sanity of your Linux distribution"); #ifndef __FreeBSD__ if (system("which mkfs.vfat 2> /dev/null 1> /dev/null") && !system("which mkfs.msdos 2> /dev/null 1> /dev/null")) { log_it("OK, you've got mkfs.msdos but not mkfs.vfat; time for the fairy to wave her magic wand..."); run_program_and_log_output("ln -sf `which mkfs.msdos` /sbin/mkfs.vfat", FALSE); } tmp = call_program_and_get_last_line_of_output("free | grep Mem | head -n1 | tr -s ' ' '\t' | cut -f2"); if (atol(tmp) < 35000) { retval++; log_to_screen("You must have at least 32MB of RAM to use Mondo."); } if (atol(tmp) < 66000) { log_to_screen("WARNING! You have very little RAM. Please upgrade to 64MB or more."); } mr_free(tmp); #endif if (system("which " MKE2FS_OR_NEWFS " > /dev/null 2> /dev/null")) { retval++; log_to_screen ("Unable to find " MKE2FS_OR_NEWFS " in system path."); fatal_error ("Please use \"su -\", not \"su\" to become root. OK? ...and please don't e-mail the mailing list or me about this. Just read the message. :)"); } #ifndef __FreeBSD__ if (run_program_and_log_output ("grep ramdisk /proc/devices", FALSE)) { /* Some SuSE have ramdisk as modules, so insert it first, then test again */ run_program_and_log_output("modprobe brd 2> /dev/null > /dev/null",FALSE); if (run_program_and_log_output ("grep ramdisk /proc/devices", FALSE)) { if (!ask_me_yes_or_no ("Your kernel has no ramdisk support. That's mind-numbingly stupid but I'll allow it if you're planning to use a failsafe kernel. Are you?")) { // retval++; log_to_screen ("It looks as if your kernel lacks ramdisk and initrd support."); log_to_screen ("I'll allow you to proceed but FYI, if I'm right, your kernel is broken."); } } } #endif retval += whine_if_not_found(MKE2FS_OR_NEWFS); retval += whine_if_not_found("mkisofs"); if (system("which dvdrecord > /dev/null 2> /dev/null")) { retval += whine_if_not_found("cdrecord"); } retval += whine_if_not_found("bzip2"); retval += whine_if_not_found("gzip"); retval += whine_if_not_found("awk"); retval += whine_if_not_found("md5sum"); retval += whine_if_not_found("strings"); retval += whine_if_not_found("mindi"); retval += whine_if_not_found("buffer"); // abort if Windows partition but no ms-sys and parted if (!run_program_and_log_output("mount | grep -Ew 'vfat|fat|dos' | grep -vE \"/dev/fd|nexdisk\"", 0)) { log_to_screen("I think you have a Windows 9x partition."); retval += whine_if_not_found("parted"); } tmp = find_home_of_exe("cmp"); if (!tmp) { mr_free(tmp); tmp = find_home_of_exe("true"); if (!tmp) { retval += whine_if_not_found("cmp"); } else { log_to_screen("Your system lacks the 'cmp' binary. I'll create a dummy cmp for you."); if (run_program_and_log_output("cp -f `which true` /usr/bin/cmp", 0)) { mr_free(tmp); fatal_error("Failed to create dummy 'cmp' file."); } } } mr_free(tmp); run_program_and_log_output("umount `mount | grep cdr | cut -d' ' -f3 | tr '\n' ' '`", 5); tmp = call_program_and_get_last_line_of_output("mount | grep -E \"cdr(om|w)\""); if (strcmp("", tmp)) { if (strstr(tmp, "autofs")) { log_to_screen ("Your CD-ROM is mounted via autofs. I therefore cannot tell"); log_to_screen ("if a CD actually is inserted. If a CD is inserted, please"); log_to_screen("eject it. Thank you."); log_it ("Ignoring autofs CD-ROM 'mount' since we hope nothing's in it."); } else if (run_program_and_log_output("uname -a | grep Knoppix", 5)) { retval++; mr_free(tmp); fatal_error("Your CD-ROM drive is mounted. Please unmount it."); } } mr_free(tmp); run_program_and_log_output("cat /etc/fstab", 5); #ifdef __FreeBSD__ run_program_and_log_output("vinum printconfig", 5); #else run_program_and_log_output("cat /etc/raidtab", 5); #endif if (run_program_and_log_output("mindi -V", 1)) { log_to_screen("Could not ascertain mindi's version number."); log_to_screen ("You have not installed Mondo and/or Mindi properly."); log_to_screen("Please uninstall and reinstall them both."); fatal_error("Please reinstall Mondo and Mindi."); } mr_asprintf(tmp, "mindi --makemountlist %s/mountlist.txt.test", bkpinfo->tmpdir); if (run_program_and_log_output(tmp, 5)) { mr_free(tmp); mr_asprintf(tmp, "mindi --makemountlist %s/mountlist.txt.test failed for some reason.", bkpinfo->tmpdir); log_to_screen(tmp); log_to_screen ("Please run that command by hand and examine /var/log/mindi.log"); log_to_screen ("for more information. Perhaps your /etc/fstab file is insane."); log_to_screen ("Perhaps Mindi's MakeMountlist() subroutine has a bug. We'll see."); retval++; } mr_free(tmp); if (!run_program_and_log_output("parted2fdisk -l | grep -i raid", 1) && !does_file_exist("/etc/raidtab")) { log_to_screen ("You have RAID partitions but no /etc/raidtab - creating one from /proc/mdstat"); create_raidtab_from_mdstat("/etc/raidtab"); } if (retval) { mvaddstr_and_log_it(g_currentY++, 74, "Failed."); } else { mvaddstr_and_log_it(g_currentY++, 74, "Done."); } return (retval); } /** * Retrieve the line containing @p label from the config file. * @param config_file The file to read from, usually @c /tmp/mondo-restore.cfg. * @param label What to read from the file. * @param value Where to put it. * @return 0 for success, 1 for failure. */ char *read_cfg_var(char *config_file, char *label) { /*@ buffer ****************************************************** */ char *command = NULL; char *value = NULL; /*@ end vars *************************************************** */ assert_string_is_neither_NULL_nor_zerolength(config_file); assert_string_is_neither_NULL_nor_zerolength(label); if (!does_file_exist(config_file)) { log_to_screen("(read_cfg_var) Cannot find %s config file", config_file); return (NULL); /* BERLIOS: not sure the usage of this one ? } else if ((value != NULL) && (strstr(value, "/dev/") && strstr(value, "t0") && !strcmp(label, "media-dev"))) { log_msg(2, "FYI, I can't read new value for %s - already got %s", label, value); return (0); */ } else { mr_asprintf(command, "grep '%s .*' %s| cut -d' ' -f2,3,4,5", label, config_file); value = call_program_and_get_last_line_of_output(command); mr_free(command); if (strlen(value) == 0) { return (NULL); } else { return (value); } } } /** * Remount @c supermount if it was unmounted earlier. */ void remount_supermounts_if_necessary() { if (g_remount_cdrom_at_end) { run_program_and_log_output("mount " MNT_CDROM, FALSE); } if (g_remount_floppy_at_end) { run_program_and_log_output("mount " MNT_FLOPPY, FALSE); } } /** * Unmount @c supermount if it's mounted. */ void unmount_supermounts_if_necessary() { if (run_program_and_log_output ("mount | grep cdrom | grep super", FALSE) == 0) { g_remount_cdrom_at_end = TRUE; run_program_and_log_output("umount " MNT_CDROM, FALSE); } if (run_program_and_log_output ("mount | grep floppy | grep super", FALSE) == 0) { g_remount_floppy_at_end = TRUE; run_program_and_log_output("umount " MNT_FLOPPY, FALSE); } } /** * Whether we had to stop autofs (if so, restart it at end). */ bool g_autofs_stopped = FALSE; /** * Path to the autofs initscript ("" if none exists). */ char g_autofs_exe[MAX_STR_LEN]; /** * Autofs initscript in Xandros Linux distribution. */ #define XANDROS_AUTOFS_FNAME "/etc/init.d/xandros-autofs" /** * Autofs initscript in most Linux distributions. */ #define STOCK_AUTOFS_FNAME "/etc/rc.d/init.d/autofs" /** * If autofs is mounted, stop it (restart at end). */ void stop_autofs_if_necessary() { char *tmp = NULL; g_autofs_exe[0] = '\0'; if (does_file_exist(XANDROS_AUTOFS_FNAME)) { strcpy(g_autofs_exe, XANDROS_AUTOFS_FNAME); } else if (does_file_exist(STOCK_AUTOFS_FNAME)) { strcpy(g_autofs_exe, STOCK_AUTOFS_FNAME); } if (!g_autofs_exe[0]) { log_msg(3, "No autofs detected."); } else { log_msg(3, "%s --- autofs detected", g_autofs_exe); // FIXME -- only disable it if it's running --- sprintf(tmp, "%s status", autofs_exe); mr_asprintf(tmp, "%s stop", g_autofs_exe); if (run_program_and_log_output(tmp, 2)) { log_it("Failed to stop autofs - I assume it wasn't running"); } else { g_autofs_stopped = TRUE; log_it("Stopped autofs OK"); } mr_free(tmp); } } /** * If autofs was stopped earlier, restart it. */ void restart_autofs_if_necessary() { char *tmp = NULL; if (!g_autofs_stopped || !g_autofs_exe[0]) { log_msg(3, "No autofs detected."); return; } mr_asprintf(tmp, "%s start", g_autofs_exe); if (run_program_and_log_output(tmp, 2)) { log_it("Failed to start autofs"); } else { g_autofs_stopped = FALSE; log_it("Started autofs OK"); } mr_free(tmp); } /** * If this is a distribution like Gentoo that doesn't keep /boot mounted, mount it. */ void mount_boot_if_necessary() { char *tmp = NULL; char *command = NULL; log_msg(1, "Started sub"); log_msg(4, "About to set g_boot_mountpt[0] to '\\0'"); g_boot_mountpt[0] = '\0'; log_msg(4, "Done. Great. Seeting command to something"); mr_asprintf(command, "%s", "grep -v \":\" /etc/fstab | grep -vE '^#.*$' | grep -E \"[ ]/boot[ ]\" | tr -s ' ' '\t' | cut -f1 | head -n1"); log_msg(4, "Cool. Command = '%s'", command); tmp = call_program_and_get_last_line_of_output(command); mr_free(command); log_msg(4, "tmp = '%s'", tmp); log_it("/boot is at %s according to /etc/fstab", tmp); mr_asprintf(command, "mount | grep -Ew '/boot'"); mr_free(tmp); tmp = call_program_and_get_last_line_of_output(command); mr_free(command); if (!strcmp(tmp,"")) { if ((strstr(tmp, "LABEL=") || strstr(tmp,"UUID="))) { if (!run_program_and_log_output("mount /boot", 5)) { strcpy(g_boot_mountpt, "/boot"); log_msg(1, "Mounted /boot"); } else { log_it("...ignored cos it's a label or uuid :-)"); } } else { mr_asprintf(command, "mount | grep -E '^%s'", tmp); log_msg(3, "command = %s", command); if (run_program_and_log_output(command, 5)) { strcpy(g_boot_mountpt, tmp); mr_free(tmp); log_it("%s (your /boot partition) is not mounted. I'll mount it before backing up", g_boot_mountpt); mr_asprintf(tmp, "mount %s", g_boot_mountpt); if (run_program_and_log_output(tmp, 5)) { g_boot_mountpt[0] = '\0'; log_msg(1, "Plan B"); if (!run_program_and_log_output("mount /boot", 5)) { strcpy(g_boot_mountpt, "/boot"); log_msg(1, "Plan B worked"); } else { log_msg(1, "Plan B failed. Unable to mount /boot for backup purposes. This probably means /boot is mounted already, or doesn't have its own partition."); } } } mr_free(command); } } mr_free(tmp); log_msg(1, "Ended sub"); } /** * If we mounted /boot earlier, unmount it. */ void unmount_boot_if_necessary() { char *tmp = NULL; log_msg(3, "starting"); if (g_boot_mountpt[0]) { mr_asprintf(tmp, "umount %s", g_boot_mountpt); if (run_program_and_log_output(tmp, 5)) { log_it("WARNING - unable to unmount /boot"); } mr_free(tmp); } log_msg(3, "leaving"); } /** * Write a line to a configuration file. Writes a line of the form, * @c label @c value. * @param config_file The file to write to. Usually @c mondo-restore.cfg. * @param label What to call this bit of data you're writing. * @param value The bit of data you're writing. * @return 0 for success, 1 for failure. */ int write_cfg_var(char *config_file, char *label, char *value) { /*@ buffers ***************************************************** */ char *command = NULL; char *tempfile = NULL; /*@ end vars *************************************************** */ assert_string_is_neither_NULL_nor_zerolength(config_file); assert_string_is_neither_NULL_nor_zerolength(label); assert(value != NULL); if (!does_file_exist(config_file)) { log_to_screen("(write_cfg_file) Cannot find %s config file", config_file); return (1); } mr_asprintf(tempfile, "%s/mojo-jojo.blah", bkpinfo->tmpdir); if (does_file_exist(config_file)) { mr_asprintf(command, "grep -vE '^%s .*$' %s > %s", label, config_file, tempfile); paranoid_system(command); mr_free(command); } mr_asprintf(command, "echo \"%s %s\" >> %s", label, value, tempfile); paranoid_system(command); mr_free(command); mr_asprintf(command, "mv -f %s %s", tempfile, config_file); paranoid_system(command); mr_free(command); unlink(tempfile); mr_free(tempfile); return (0); } /** * The standard log_debug_msg() (log_msg() also due to a macro). Writes some describing * information to the logfile. */ void standard_log_debug_msg(int debug_level, const char *szFile, const char *szFunction, int nLine, const char *fmt, ...) { va_list args; static int depth = 0; FILE *fout; if (depth > 5) { depth--; return; } depth++; if (debug_level <= g_loglevel) { if (!(fout = fopen(MONDO_LOGFILE, "a"))) { return; } // add tabs to distinguish log levels if (debug_level > 0) { fprintf(fout, "DBG%d: ", debug_level); if (getpid() == g_main_pid) fprintf(fout, "[Main] %s->%s#%d: ", szFile, szFunction, nLine); else if (getpid() == g_buffer_pid && g_buffer_pid > 0) fprintf(fout, "[Buff] %s->%s#%d: ", szFile, szFunction, nLine); else fprintf(fout, "[TH=%d] %s->%s#%d: ", getpid(), szFile, szFunction, nLine); } else { fprintf(fout, "INFO: "); } va_start(args, fmt); vfprintf(fout, fmt, args); va_end(args); // do not slow down the progran if standard debug level // must be enabled: if no flush, the log won't be up-to-date if there // is a segfault //if (g_dwDebugLevel != 1) fprintf(fout, "\n"); paranoid_fclose(fout); } depth--; } /** * Function pointer to the @c log_debug_msg function to use. Points to standard_log_debug_msg() by default. */ void (*log_debug_msg) (int, const char *, const char *, int, const char *, ...) = standard_log_debug_msg; /** * Allocate or free important globals, depending on @p mal. * @param mal If TRUE, malloc; if FALSE, free. */ void do_libmondo_global_strings_thing(int mal) { if (mal) { malloc_string(g_boot_mountpt); malloc_string(g_mondo_home); malloc_string(g_magicdev_command); } else { paranoid_free(g_boot_mountpt); paranoid_free(g_mondo_home); paranoid_free(g_magicdev_command); mr_free(g_serial_string); } } /** * Allocate important globals. * @see do_libmondo_global_strings_thing */ void malloc_libmondo_global_strings(void) { do_libmondo_global_strings_thing(1); } /** * Free important globals. * @see do_libmondo_global_strings_thing */ void free_libmondo_global_strings(void) { do_libmondo_global_strings_thing(0); } /** * Stop @c magicdev if it's running. * The command used to start it is saved in @p g_magicdev_command. */ void stop_magicdev_if_necessary() { char *tmp = NULL; tmp = call_program_and_get_last_line_of_output("ps ax | grep -w magicdev | grep -v grep | tr -s '\t' ' '| cut -d' ' -f6-99"); strcpy(g_magicdev_command, tmp); mr_free(tmp); if (g_magicdev_command[0]) { log_msg(1, "g_magicdev_command = '%s'", g_magicdev_command); paranoid_system("killall magicdev"); } } /** * Restart magicdev if it was stopped. */ void restart_magicdev_if_necessary() { char *tmp = NULL; if (g_magicdev_command && g_magicdev_command[0]) { mr_asprintf(tmp, "%s &", g_magicdev_command); paranoid_system(tmp); mr_free(tmp); } } /* @} - end of utilityGroup */