source: MondoRescue/branches/stable/mondo/src/common/libmondo-devices.c@ 1903

Last change on this file since 1903 was 1903, checked in by Bruno Cornec, 16 years ago

merge -r 1842:1889 2.2.5

  • Property svn:keywords set to Id
File size: 70.7 KB
Line 
1/* libmondo-devices.c Subroutines for handling devices
2 $Id: libmondo-devices.c 1903 2008-04-09 23:13:58Z bruno $
3*/
4
5/**
6 * @file
7 * Functions to handle interactions with backup devices.
8 */
9
10#include "my-stuff.h"
11#include "mondostructures.h"
12#include "libmondo-files-EXT.h"
13#include "libmondo-devices.h"
14#include "libmondo-string-EXT.h"
15#include "libmondo-tools-EXT.h"
16#include "newt-specific-EXT.h"
17#include "libmondo-fork-EXT.h"
18#include "libmondo-stream-EXT.h"
19
20#include "mr_mem.h"
21#include "mr_msg.h"
22#include "mr_str.h"
23#include "mr_gettext.h"
24#include "mr_conf.h"
25
26#include <sys/ioctl.h>
27#include <sys/types.h>
28#include <unistd.h>
29#ifdef __FreeBSD__
30#define DKTYPENAMES
31#define FSTYPENAMES
32#include <sys/disklabel.h>
33#include <sys/disk.h>
34#elif linux
35#define u64 unsigned long long
36#include <linux/fs.h> /* for BLKGETSIZE64 */
37#include <linux/hdreg.h>
38#endif
39
40/*@unused@*/
41//static char cvsid[] = "$Id: libmondo-devices.c 1903 2008-04-09 23:13:58Z bruno $";
42
43extern int g_current_media_number;
44extern double g_kernel_version;
45
46extern bool g_ISO_restore_mode;
47extern char *g_selfmounted_isodir;
48extern char *MONDO_LOGFILE;
49extern struct mr_ar_conf *mr_conf;
50
51extern void setup_tmpdir(char *path);
52
53static char g_cdrw_drive_is_here[MAX_STR_LEN / 4] = "";
54static char g_cdrom_drive_is_here[MAX_STR_LEN / 4] = "";
55static char g_dvd_drive_is_here[MAX_STR_LEN / 4] = "";
56
57
58/**
59 * ????? @bug ?????
60 * @ingroup globalGroup
61 */
62bool g_restoring_live_from_cd = FALSE;
63
64extern t_bkptype g_backup_media_type; // set by main()
65
66/* Reference to global bkpinfo */
67extern struct s_bkpinfo *bkpinfo;
68
69
70
71
72void set_g_cdrom_and_g_dvd_to_bkpinfo_value()
73{
74 strcpy(g_cdrom_drive_is_here, bkpinfo->media_device); // just in case
75 strcpy(g_dvd_drive_is_here, bkpinfo->media_device); // just in case
76}
77
78
79
80/**
81 * Retract all CD trays and wait for autorun to complete.
82 * @ingroup deviceGroup
83 */
84void retract_CD_tray_and_defeat_autorun(void)
85{
86// log_it("rctada: Retracting all CD trays", __LINE__);
87 if (strlen(g_cdrom_drive_is_here) > 0) {
88 inject_device(g_cdrom_drive_is_here);
89 }
90 if (strlen(g_dvd_drive_is_here) > 0) {
91 inject_device(g_dvd_drive_is_here);
92 }
93 if (strlen(g_cdrw_drive_is_here) > 0) {
94 inject_device(g_cdrw_drive_is_here);
95 }
96// log_it("rctada: killing autorun");
97// run_program_and_log_output("killall autorun", TRUE);
98 if (!run_program_and_log_output("ps | grep autorun | grep -v grep", 5)) {
99 log_it("autorun detected; sleeping for 2 seconds");
100 sleep(2);
101 }
102 log_it("rctada: Unmounting all CD drives", __LINE__);
103 run_program_and_log_output("umount /dev/cdr* /dev/dvd*", 5);
104}
105
106
107/**
108 * Mount the CD-ROM at @p mountpoint.
109 * @param device The device (or file if g_ISO_restore_mode) to mount.
110 * @param mountpoint The place to mount it.
111 * @return 0 for success, nonzero for failure.
112 */
113int mount_CDROM_here(char *device, char *mountpoint)
114{
115 /*@ buffer ****************************************************** */
116 char *command = NULL;
117 char *dev;
118 int retval = 0;
119
120 malloc_string(dev);
121 assert_string_is_neither_NULL_nor_zerolength(device);
122 assert_string_is_neither_NULL_nor_zerolength(mountpoint);
123
124 make_hole_for_dir(mountpoint);
125 if (isdigit(device[0])) {
126 find_cdrom_device(device, FALSE);
127 } else {
128 strcpy(dev, device);
129 }
130 if (g_ISO_restore_mode) {
131
132#ifdef __FreeBSD__
133 strcpy(dev, make_vn(device));
134 if (!dev) {
135 sprintf(command, "Unable to mount ISO (make_vn(%s) failed)",
136 device);
137 fatal_error(command);
138 }
139 strcpy(device, dev);
140#endif
141 }
142
143 mr_msg(4, "(mount_CDROM_here --- device=%s, mountpoint=%s", device,
144 mountpoint);
145 /*@ end vars *************************************************** */
146
147#ifdef __FreeBSD__
148 mr_asprintf(&command, "mount_cd9660 -r %s %s 2>> %s",
149 device, mountpoint, MONDO_LOGFILE);
150#else
151 mr_asprintf(&command, "mount %s -o ro,loop -t iso9660 %s 2>> %s",
152 device, mountpoint, MONDO_LOGFILE);
153#endif
154
155 mr_msg(4, command);
156 if (strncmp(device, "/dev/", 5) == 0) {
157 retract_CD_tray_and_defeat_autorun();
158 }
159 retval = system(command);
160 mr_msg(1, "system(%s) returned %d", command, retval);
161 mr_free(command);
162
163 mr_free(dev);
164 return (retval);
165}
166
167
168
169/**
170 * Determine whether we're booted off a ramdisk.
171 * @return @c TRUE (we are) or @c FALSE (we aren't).
172 * @ingroup utilityGroup
173 */
174bool am_I_in_disaster_recovery_mode(void)
175{
176 char *tmp = NULL;
177 char *comment = NULL;
178 bool is_this_a_ramdisk = FALSE;
179
180 mr_asprintf(&tmp, where_is_root_mounted());
181 mr_asprintf(&comment, "root is mounted at %s\n", tmp);
182 mr_msg(0, comment);
183 mr_free(comment);
184
185 mr_msg(0,
186 "No, Schlomo, that doesn't mean %s is the root partition. It's just a debugging message. Relax. It's part of am_I_in_disaster_recovery_mode().",
187 tmp);
188
189#ifdef __FreeBSD__
190 if (strstr(tmp, "/dev/md")) {
191 is_this_a_ramdisk = TRUE;
192 }
193#else
194 if (!strncmp(tmp, "/dev/ram", 8)
195 || (!strncmp(tmp, "/dev/rd", 7) && !strcmp(tmp, "/dev/rd/")
196 && strncmp(tmp, "/dev/rd/cd", 10)) || strstr(tmp, "rootfs")
197 || !strcmp(tmp, "/dev/root")) {
198 is_this_a_ramdisk = TRUE;
199 } else {
200 is_this_a_ramdisk = FALSE;
201 }
202#endif
203 mr_free(tmp);
204
205 if (is_this_a_ramdisk) {
206 if (!does_file_exist("/THIS-IS-A-RAMDISK")
207 && !does_file_exist("/tmp/mountlist.txt.sample")) {
208 log_to_screen
209 (_("Using /dev/root is stupid of you but I'll forgive you."));
210 is_this_a_ramdisk = FALSE;
211 }
212 }
213 if (does_file_exist("/THIS-IS-A-RAMDISK")) {
214 is_this_a_ramdisk = TRUE;
215 }
216 mr_msg(1, "Is this a ramdisk? result = %d", is_this_a_ramdisk);
217 return (is_this_a_ramdisk);
218}
219
220
221/**
222 * Turn @c bkpinfo->backup_media_type into a human-readable string.
223 * @return The human readable string (e.g. @c cdr becomes <tt>"cdr"</tt>).
224 * @note The returned string points to static storage that will be overwritten with each call.
225 * @ingroup stringGroup
226 */
227char *bkptype_to_string(t_bkptype bt)
228{
229 static char output[MAX_STR_LEN / 4];
230 switch (bt) {
231 case none:
232 strcpy(output, "none");
233 break;
234 case iso:
235 strcpy(output, "iso");
236 break;
237 case cdr:
238 strcpy(output, "cdr");
239 break;
240 case cdrw:
241 strcpy(output, "cdrw");
242 break;
243 case cdstream:
244 strcpy(output, "cdstream");
245 break;
246 case nfs:
247 strcpy(output, "nfs");
248 break;
249 case tape:
250 strcpy(output, "tape");
251 break;
252 case udev:
253 strcpy(output, "udev");
254 break;
255 case usb:
256 strcpy(output, "usb");
257 break;
258 default:
259 strcpy(output, "default");
260 }
261 return (output);
262}
263
264
265/**
266 * @addtogroup deviceGroup
267 * @{
268 */
269/**
270 * Eject the tray of the specified CD device.
271 * @param dev The device to eject.
272 * @return the return value of the @c eject command. (0=success, nonzero=failure)
273 */
274int eject_device(char *dev)
275{
276 char *command = NULL;
277 int res1 = 0, res2 = 0;
278
279 if (IS_THIS_A_STREAMING_BACKUP(g_backup_media_type)
280 && g_backup_media_type != udev) {
281 mr_asprintf(&command, "mt -f %s offline", dev);
282 res1 = run_program_and_log_output(command, 1);
283 mr_free(command);
284 } else {
285 res1 = 0;
286 }
287
288#ifdef __FreeBSD__
289 if (strstr(dev, "acd")) {
290 mr_asprintf(&command, "cdcontrol -f %s eject", dev);
291 } else {
292 mr_asprintf(&command, "camcontrol eject `echo %s | sed 's|/dev/||'`",
293 dev);
294 }
295#else
296 mr_asprintf(&command, "eject %s", dev);
297#endif
298
299 mr_msg(3, "Ejecting %s", dev);
300 res2 = run_program_and_log_output(command, 1);
301 mr_free(command);
302 if (res1 && res2) {
303 return (1);
304 } else {
305 return (0);
306 }
307}
308
309
310/**
311 * Load (inject) the tray of the specified CD device.
312 * @param dev The device to load/inject.
313 * @return 0 for success, nonzero for failure.
314 */
315int inject_device(char *dev)
316{
317 char *command = NULL;
318 int i;
319
320#ifdef __FreeBSD__
321 if (strstr(dev, "acd")) {
322 mr_asprintf(&command, "cdcontrol -f %s close", dev);
323 } else {
324 mr_asprintf(&command, "camcontrol load `echo %s | sed 's|/dev/||'`",
325 dev);
326 }
327#else
328 mr_asprintf(&command, "eject -t %s", dev);
329#endif
330 i = run_program_and_log_output(command, FALSE);
331 mr_free(command);
332 return (i);
333}
334
335
336/**
337 * Determine whether the specified @p device (really, you can use any file)
338 * exists.
339 * @return TRUE if it exists, FALSE if it doesn't.
340 */
341bool does_device_exist(char *device)
342{
343
344 /*@ buffers *********************************************************** */
345 char *tmp = NULL;
346 bool ret = FALSE;
347
348 assert_string_is_neither_NULL_nor_zerolength(device);
349
350 mr_asprintf(&tmp, "ls %s > /dev/null 2> /dev/null", device);
351
352 if (system(tmp)) {
353 ret = FALSE;
354 } else {
355 ret = TRUE;
356 }
357 mr_free(tmp);
358 return(ret);
359}
360
361
362/**
363 * Determine whether a non-Microsoft partition exists on any connected hard drive.
364 * @return TRUE (there's a Linux/FreeBSD partition) or FALSE (Microsoft has taken over yet another innocent PC).
365 */
366bool does_nonMS_partition_exist(void)
367{
368#if __FreeBSD__
369 return
370 !system
371 ("for drive in /dev/ad? /dev/da?; do fdisk $drive | grep -q FreeBSD && exit 0; done; false");
372#else
373 return
374 !system
375 ("parted2fdisk -l 2>/dev/null | grep '^/dev/' | grep -Eqv '(MS|DOS|FAT|NTFS)'");
376#endif
377}
378
379/**
380 * Determine whether the specified @p partno exists on the specified @p drive.
381 * @param drive The drive to search for the partition in.
382 * @param partno The partition number to look for.
383 * @return 0 if it exists, nonzero otherwise.
384 */
385int does_partition_exist(const char *drive, int partno)
386{
387 /*@ buffers **************************************************** */
388 char *program = NULL;
389 char *incoming = NULL;
390 char *searchstr = NULL;
391
392 /*@ ints ******************************************************* */
393 int res = 0;
394
395 /*@ pointers *************************************************** */
396 FILE *fin;
397
398
399 /*@ end vars *************************************************** */
400 assert_string_is_neither_NULL_nor_zerolength(drive);
401 assert(partno >= 0 && partno < 999);
402
403 malloc_string(incoming);
404 malloc_string(searchstr);
405
406#ifdef __FreeBSD__
407 // We assume here that this is running from mondorestore. (It is.)
408 mr_asprintf(&program, "ls %s %s >/dev/null 2>&1", drive,
409 build_partition_name(tmp, drive, partno));
410 res = system(program);
411 mr_free(program);
412 return(res);
413#endif
414
415 mr_asprintf(&program, "parted2fdisk -l %s 2> /dev/null", drive);
416 fin = popen(program, "r");
417 if (!fin) {
418 log_it("program=%s", program);
419 log_OS_error("Cannot popen-in program");
420 mr_free(program);
421 return (0);
422 }
423 mr_free(program);
424
425 (void) build_partition_name(searchstr, drive, partno);
426 strcat(searchstr, " ");
427 for (res = 0; !res && fgets(incoming, MAX_STR_LEN - 1, fin);) {
428 if (strstr(incoming, searchstr)) {
429 res = 1;
430 }
431 }
432 mr_free(incoming);
433
434 if (pclose(fin)) {
435 log_OS_error("Cannot pclose fin");
436 }
437 mr_free(searchstr);
438 return (res);
439}
440
441
442/**
443 * Determine whether given NULL-terminated @p str exists in the MBR of @p dev.
444 * @param dev The device to look in.
445 * @param str The string to look for.
446 * @return TRUE if it exists, FALSE if it doesn't.
447 */
448bool does_string_exist_in_boot_block(char *dev, char *str)
449{
450 /*@ buffers **************************************************** */
451 char *command = NULL;
452
453 /*@ end vars *************************************************** */
454 int ret;
455
456 assert_string_is_neither_NULL_nor_zerolength(dev);
457 assert_string_is_neither_NULL_nor_zerolength(str);
458
459 mr_asprintf(&command,
460 "dd if=%s bs=446 count=1 2> /dev/null | strings | grep \"%s\" > /dev/null 2> /dev/null",
461 dev, str);
462 ret = system(command);
463 mr_free(command);
464 if (ret) {
465 return (FALSE);
466 } else {
467 return (TRUE);
468 }
469}
470
471
472/**
473 * Determine whether specified @p str exists in the first @p n sectors of
474 * @p dev.
475 * @param dev The device to look in.
476 * @param str The string to look for.
477 * @param n The number of 512-byte sectors to search.
478 */
479bool does_string_exist_in_first_N_blocks(char *dev, char *str, int n)
480{
481 /*@ buffers **************************************************** */
482 char *command = NULL;
483 /*@ end vars *************************************************** */
484 int ret;
485
486 mr_asprintf(&command,
487 "dd if=%s bs=512 count=%i 2> /dev/null | strings | grep \"%s\" > /dev/null 2> /dev/null",
488 dev, n, str);
489 ret = system(command);
490 mr_free(command);
491
492 if (ret) {
493 return (FALSE);
494 } else {
495 return (TRUE);
496 }
497}
498
499
500/**
501 * Try to mount CD-ROM at @p mountpoint. If the CD-ROM is not found or has
502 * not been specified, call find_cdrom_device() to find it.
503 * @param bkpinfo The backup information structure. The only field used is @c bkpinfo->media_device.
504 * @param mountpoint Where to mount the CD-ROM.
505 * @return 0 for success, nonzero for failure.
506 * @see mount_CDROM_here
507 */
508int find_and_mount_actual_cd(char *mountpoint)
509{
510 /*@ buffers ***************************************************** */
511
512 /*@ int's ****************************************************** */
513 int res;
514 char *dev = NULL;
515
516 /*@ end vars **************************************************** */
517
518 malloc_string(dev);
519 assert(bkpinfo != NULL);
520 assert_string_is_neither_NULL_nor_zerolength(mountpoint);
521
522 if (g_backup_media_type == dvd) {
523 strcpy(dev, g_dvd_drive_is_here);
524 if (!dev[0]) {
525 find_dvd_device(dev, FALSE);
526 }
527 } else {
528 strcpy(dev, g_cdrom_drive_is_here);
529 if (!dev[0]) {
530 find_cdrom_device(dev, FALSE);
531 }
532 }
533
534 if (bkpinfo->backup_media_type != iso) {
535 retract_CD_tray_and_defeat_autorun();
536 }
537
538 if (!dev[0] || (res = mount_CDROM_here(dev, mountpoint))) {
539 if (!popup_and_get_string
540 ("CD-ROM device", "Please enter your CD-ROM's /dev device",
541 dev, MAX_STR_LEN / 4)) {
542 res = 1;
543 } else {
544 res = mount_CDROM_here(dev, mountpoint);
545 }
546 }
547 if (res) {
548 mr_msg(1, _("mount failed"));
549 } else {
550 mr_msg(1, _("mount succeeded with %s"), dev);
551 }
552 mr_free(dev);
553 return(res);
554}
555
556
557
558/**
559 * Locate a CD-R/W writer's SCSI node.
560 * @param cdrw_device SCSI node will be placed here.
561 * @return 0 for success, nonzero for failure.
562 */
563int find_cdrw_device(char *cdrw_device)
564{
565 /*@ buffers ************************ */
566 char *comment;
567 char *tmp;
568 char *cdr_exe;
569 char *command;
570
571 malloc_string(comment);
572 malloc_string(tmp);
573 malloc_string(cdr_exe);
574 malloc_string(command);
575 if (g_cdrw_drive_is_here[0]) {
576 strcpy(cdrw_device, g_cdrw_drive_is_here);
577 mr_msg(3, "Been there, done that. Returning %s", cdrw_device);
578 mr_free(comment);
579 mr_free(tmp);
580 mr_free(cdr_exe);
581 mr_free(command);
582 return (0);
583 }
584 if (g_backup_media_type == dvd) {
585 mr_msg(1,
586 "This is dumb. You're calling find_cdrw_device() but you're backing up to DVD. WTF?");
587 mr_free(comment);
588 mr_free(tmp);
589 mr_free(cdr_exe);
590 mr_free(command);
591 return (1);
592 }
593 run_program_and_log_output("insmod ide-scsi", -1);
594 if (find_home_of_exe("cdrecord")) {
595 strcpy(cdr_exe, "cdrecord");
596 } else {
597 strcpy(cdr_exe, "dvdrecord");
598 }
599 tmp[0] = '\0';
600 if (find_home_of_exe(cdr_exe)) {
601 sprintf(command,
602 "%s -scanbus 2> /dev/null | tr -s '\t' ' ' | grep \"[0-9]*,[0-9]*,[0-9]*\" | grep -v \"[0-9]*) \\*\" | grep CD | cut -d' ' -f2 | head -n1",
603 cdr_exe);
604 strcpy(tmp, call_program_and_get_last_line_of_output(command));
605 }
606 if (strlen(tmp) < 2) {
607 mr_free(comment);
608 mr_free(tmp);
609 mr_free(cdr_exe);
610 mr_free(command);
611 return 1;
612 } else {
613 strcpy(cdrw_device, tmp);
614 sprintf(comment, "Found CDRW device - %s", cdrw_device);
615 log_it(comment);
616 strcpy(g_cdrw_drive_is_here, cdrw_device);
617 mr_free(comment);
618 mr_free(tmp);
619 mr_free(cdr_exe);
620 mr_free(command);
621 return (0);
622 }
623}
624
625
626
627
628/**
629 * Attempt to locate a CD-ROM device's /dev entry.
630 * Several different methods may be used to find the device, including
631 * calling @c cdrecord, searching @c dmesg, and trial-and-error.
632 * @param output Where to put the located /dev entry.
633 * @param try_to_mount Whether to mount the CD as part of the test; if mount
634 * fails then return failure.
635 * @return 0 for success, nonzero for failure.
636 */
637int find_cdrom_device(char *output, bool try_to_mount)
638{
639 /*@ pointers **************************************************** */
640 FILE *fin;
641 char *p;
642 char *q;
643 char *r;
644 int retval = 0;
645
646 /*@ bool's ****************************************************** */
647 bool found_it = FALSE;
648
649 /*@ buffers ***************************************************** */
650 char *tmp;
651 char *cdr_exe;
652 char *phrase_one;
653 char *phrase_two;
654 char *command;
655 char *dvd_last_resort;
656 char *mountpoint;
657 static char the_last_place_i_found_it[MAX_STR_LEN] = "";
658
659 /*@ intialize *************************************************** */
660 malloc_string(tmp);
661 malloc_string(cdr_exe);
662 malloc_string(phrase_one);
663 malloc_string(phrase_two);
664 malloc_string(command);
665 malloc_string(dvd_last_resort);
666 malloc_string(mountpoint);
667
668 output[0] = '\0';
669 phrase_one[0] = '\0';
670 phrase_two[0] = '\0';
671 dvd_last_resort[0] = '\0';
672
673 /*@ end vars **************************************************** */
674
675 if (g_cdrom_drive_is_here[0] && !isdigit(g_cdrom_drive_is_here[0])) {
676 strcpy(output, g_cdrom_drive_is_here);
677 mr_msg(3, "Been there, done that. Returning %s", output);
678 retval = 0;
679 goto end_of_find_cdrom_device;
680 }
681 if (the_last_place_i_found_it[0] != '\0' && !try_to_mount) {
682 strcpy(output, the_last_place_i_found_it);
683 mr_msg(3,
684 "find_cdrom_device() --- returning last found location - '%s'",
685 output);
686 retval = 0;
687 goto end_of_find_cdrom_device;
688 }
689
690 sprintf(mountpoint, "%s/cd.mnt", bkpinfo->tmpdir);
691 make_hole_for_dir(mountpoint);
692
693 if (find_home_of_exe("cdrecord")) {
694 strcpy(cdr_exe, "cdrecord");
695 } else {
696 strcpy(cdr_exe, "dvdrecord");
697 }
698 tmp[0] = '\0';
699 if (!find_home_of_exe(cdr_exe)) {
700 strcpy(output, "/dev/cdrom");
701 mr_msg(4, "Can't find cdrecord; assuming %s", output);
702 if (!does_device_exist(output)) {
703 mr_msg(4, "That didn't work. Sorry.");
704 retval = 1;
705 goto end_of_find_cdrom_device;
706 } else {
707 retval = 0;
708 goto end_of_find_cdrom_device;
709 }
710 }
711
712 sprintf(command, "%s -scanbus 2> /dev/null", cdr_exe);
713 fin = popen(command, "r");
714 if (!fin) {
715 mr_msg(4, "command=%s", command);
716 log_OS_error("Cannot popen command");
717 return (1);
718 }
719 for (fgets(tmp, MAX_STR_LEN, fin); !feof(fin);
720 fgets(tmp, MAX_STR_LEN, fin)) {
721 p = strchr(tmp, '\'');
722 if (p) {
723 q = strchr(++p, '\'');
724 if (q) {
725 for (r = q; *(r - 1) == ' '; r--);
726 *r = '\0';
727 strcpy(phrase_one, p);
728 p = strchr(++q, '\'');
729 if (p) {
730 q = strchr(++p, '\'');
731 if (q) {
732 while (*(q - 1) == ' ') {
733 q--;
734 }
735 *q = '\0';
736 strcpy(phrase_two, p);
737 }
738 }
739 }
740 }
741 }
742 paranoid_pclose(fin);
743
744#ifndef __FreeBSD__
745 if (strlen(phrase_two) == 0) {
746 mr_msg(4, "Not running phase two. String is empty.");
747 } else {
748 sprintf(command, "dmesg | grep \"%s\" 2> /dev/null", phrase_two);
749 fin = popen(command, "r");
750 if (!fin) {
751 mr_msg(4, "Cannot run 2nd command - non-fatal, fortunately");
752 } else {
753 for (fgets(tmp, MAX_STR_LEN, fin); !feof(fin);
754 fgets(tmp, MAX_STR_LEN, fin)) {
755 mr_msg(5, "--> '%s'", tmp);
756 if (tmp[0] != ' ' && tmp[1] != ' ') {
757 p = strchr(tmp, ':');
758 if (p) {
759 *p = '\0';
760 if (strstr(tmp, "DVD")) {
761 sprintf(dvd_last_resort, "/dev/%s", tmp);
762 mr_msg(4,
763 "Ignoring '%s' because it's a DVD drive",
764 tmp);
765 } else {
766 sprintf(output, "/dev/%s", tmp);
767 found_it = TRUE;
768 }
769 }
770 }
771 }
772 paranoid_pclose(fin);
773 }
774 }
775
776#endif
777#ifdef __FreeBSD__
778 if (!found_it) {
779 mr_msg(4, "OK, approach 2");
780 if (!(found_it = set_dev_to_this_if_rx_OK(output, "/dev/cdrom"))) {
781 if (!
782 (found_it =
783 set_dev_to_this_if_rx_OK(output, "/dev/cdrom1"))) {
784 if (!
785 (found_it =
786 set_dev_to_this_if_rx_OK(output, "/dev/dvd"))) {
787 if (!
788 (found_it =
789 set_dev_to_this_if_rx_OK(output, "/dev/acd0"))) {
790 if (!
791 (found_it =
792 set_dev_to_this_if_rx_OK(output,
793 "/dev/cd01"))) {
794 if (!
795 (found_it =
796 set_dev_to_this_if_rx_OK(output,
797 "/dev/acd1"))) {
798 if (!
799 (found_it =
800 set_dev_to_this_if_rx_OK(output,
801 "/dev/cd1")))
802 {
803 retval = 1;
804 goto end_of_find_cdrom_device;
805 }
806 }
807 }
808 }
809 }
810 }
811 }
812 }
813#else
814 if (!found_it && strlen(dvd_last_resort) > 0) {
815 mr_msg(4, "Well, I'll use the DVD - %s - as a last resort",
816 dvd_last_resort);
817 strcpy(output, dvd_last_resort);
818 found_it = TRUE;
819 }
820 if (found_it) {
821 sprintf(tmp, "grep \"%s=ide-scsi\" /proc/cmdline &> /dev/null",
822 strrchr(output, '/') + 1);
823 if (system(tmp) == 0) {
824 mr_msg(4,
825 "%s is not right. It's being SCSI-emulated. Continuing.",
826 output);
827 found_it = FALSE;
828 output[0] = '\0';
829 }
830 }
831
832 if (found_it) {
833 mr_msg(4, "(find_cdrom_device) --> '%s'", output);
834 if (!does_device_exist(output)) {
835 found_it = FALSE;
836 mr_msg(4, "OK, I was wrong, I haven't found it... yet.");
837 }
838 }
839
840 if (!found_it) {
841 mr_msg(4, "OK, approach 2");
842 if (!(found_it = set_dev_to_this_if_rx_OK(output, "/dev/scd0"))) {
843 if (!(found_it = set_dev_to_this_if_rx_OK(output, "/dev/sr0"))) {
844 if (!
845 (found_it =
846 set_dev_to_this_if_rx_OK(output, "/dev/cdrom"))) {
847 if (!
848 (found_it =
849 set_dev_to_this_if_rx_OK(output,
850 "/dev/cdrom0"))) {
851 if (!
852 (found_it =
853 set_dev_to_this_if_rx_OK(output,
854 "/dev/cdrom1"))) {
855 if (!
856 (found_it =
857 set_dev_to_this_if_rx_OK(output,
858 "/dev/sr1"))) {
859 if (!
860 (found_it =
861 set_dev_to_this_if_rx_OK(output,
862 "/dev/dvd")))
863 {
864 if (!
865 (found_it =
866 set_dev_to_this_if_rx_OK(output,
867 g_cdrw_drive_is_here)))
868 {
869 retval = 1;
870 goto end_of_find_cdrom_device;
871 }
872 }
873 }
874 }
875 }
876 }
877 }
878 }
879 }
880#endif
881
882 if (found_it && try_to_mount) {
883 if (mount_CDROM_here(output, mountpoint)) {
884 mr_msg(4, "[Cardigans] I've changed my mind");
885 found_it = FALSE;
886 } else {
887 sprintf(tmp, "%s/archives", mountpoint);
888 if (!does_file_exist(tmp)) {
889 mr_msg(4, "[Cardigans] I'll take it back");
890 found_it = FALSE;
891 } else {
892 sprintf(command, "umount %s", output);
893 paranoid_system(command);
894 mr_msg(4, "I'm confident the Mondo CD is in %s", output);
895 }
896 }
897 }
898 unlink(mountpoint);
899
900 if (found_it) {
901 if (!does_file_exist(output)) {
902 mr_msg(3, "I still haven't found it.");
903 return (1);
904 }
905 mr_msg(3, "(find_cdrom_device) --> '%s'", output);
906 strcpy(the_last_place_i_found_it, output);
907 strcpy(g_cdrom_drive_is_here, output);
908 retval = 0;
909 goto end_of_find_cdrom_device;
910 }
911
912 sprintf(command,
913 "%s -scanbus | grep \"[0-9],[0-9],[0-9]\" | grep \"[D|C][V|D]\" | grep -n \"\" | grep \"%s\" | cut -d':' -f2",
914 cdr_exe, g_cdrw_drive_is_here);
915 mr_msg(1, "command=%s", command);
916 strcpy(tmp, call_program_and_get_last_line_of_output(command));
917 if (tmp[0]) {
918 strcpy(output, tmp);
919 mr_msg(4, "Finally found it at %s", output);
920 retval = 0;
921 goto end_of_find_cdrom_device;
922 } else {
923 mr_msg(4, "Still couldn't find it.");
924 retval = 1;
925 goto end_of_find_cdrom_device;
926 }
927 end_of_find_cdrom_device:
928 mr_free(tmp);
929 mr_free(cdr_exe);
930 mr_free(phrase_one);
931 mr_free(phrase_two);
932 mr_free(command);
933 mr_free(dvd_last_resort);
934 mr_free(mountpoint);
935 return (retval);
936}
937
938
939
940
941
942int find_dvd_device(char *output, bool try_to_mount)
943{
944 char *command;
945 char *tmp;
946 int retval = 0, devno = -1;
947
948 malloc_string(command);
949 malloc_string(tmp);
950
951 if (g_dvd_drive_is_here[0]) {
952 strcpy(output, g_dvd_drive_is_here);
953 mr_msg(3, "Been there, done that. Returning %s", output);
954 return (0);
955 }
956
957 sprintf(tmp, call_program_and_get_last_line_of_output
958 ("dvdrecord -scanbus 2> /dev/null | grep \") '\" | grep -n \"\" | grep DVD | cut -d':' -f1")
959 );
960 mr_msg(5, "tmp = '%s'", tmp);
961 if (!tmp[0])
962 sprintf(tmp, call_program_and_get_last_line_of_output
963 ("cdrecord -scanbus 2> /dev/null | grep \") '\" | grep -n \"\" | grep DVD | cut -d':' -f1")
964 );
965 if (tmp[0]) {
966 devno = atoi(tmp) - 1;
967 }
968 if (devno >= 0) {
969 retval = 0;
970 sprintf(output, "/dev/scd%d", devno);
971 strcpy(g_dvd_drive_is_here, output);
972 mr_msg(2, "I think DVD is at %s", output);
973 } else {
974 mr_msg(2, "I cannot find DVD");
975 retval = 1;
976 }
977
978 if (try_to_mount) {
979 mr_msg(1, "Ignoring the fact that try_to_mount==TRUE");
980 }
981 return (retval);
982}
983
984
985
986
987
988#include <sys/ioctl.h>
989
990/**
991 * Find the size of the specified @p drive, in megabytes. Uses @c ioctl calls
992 * and @c dmesg.
993 * @param drive The device to find the size of.
994 * @return size in megabytes.
995 */
996long get_phys_size_of_drive(char *drive)
997{
998 int fd;
999#if linux
1000 unsigned long long s = 0;
1001 int fileid, cylinders = 0, cylindersleft = 0;
1002 int cylindersize = 0;
1003 int gotgeo = 0;
1004
1005
1006 struct hd_geometry hdgeo;
1007#elif __FreeBSD__
1008 off_t s;
1009#endif
1010
1011 long outvalA = -1;
1012 long outvalB = -1;
1013 long outvalC = -1;
1014
1015 if ((fd = open(drive, O_RDONLY)) != -1) {
1016 if (ioctl(fd,
1017#if linux
1018#ifdef BLKGETSIZE64
1019 BLKGETSIZE64,
1020#else
1021 BLKGETSIZE,
1022#endif
1023#elif __FreeBSD__
1024 DIOCGMEDIASIZE,
1025#endif
1026 &s) != -1) {
1027 close(fd);
1028 // s>>11 works for older disks but not for newer ones
1029 outvalB =
1030#if linux
1031#ifdef BLKGETSIZE64
1032 s >> 20
1033#else
1034 s >> 11
1035#endif
1036#else
1037 s >> 20
1038#endif
1039 ;
1040 }
1041 }
1042
1043 if (outvalB <= 0) {
1044 mr_msg(1, "Error getting size of %s: %s", drive, strerror(errno));
1045#if linux
1046 fileid = open(drive, O_RDONLY);
1047 if (fileid) {
1048 if (ioctl(fileid, HDIO_GETGEO, &hdgeo) != -1) {
1049 if (hdgeo.cylinders && hdgeo.heads && hdgeo.sectors) {
1050 cylindersleft = cylinders = hdgeo.cylinders;
1051 cylindersize = hdgeo.heads * hdgeo.sectors / 2;
1052 outvalA = cylindersize * cylinders / 1024;
1053 mr_msg(2, "Got Harddisk geometry, C:%d, H:%d, S:%d",
1054 hdgeo.cylinders, hdgeo.heads, hdgeo.sectors);
1055 gotgeo = 1;
1056 } else {
1057 mr_msg(1, "Harddisk geometry wrong");
1058 }
1059 } else {
1060 mr_msg(1,
1061 "Error in ioctl() getting new hard disk geometry (%s), resizing in unsafe mode", strerror(errno));
1062 }
1063 close(fileid);
1064 } else {
1065 mr_msg(1, "Failed to open %s for reading: %s", drive,
1066 strerror(errno));
1067 }
1068 if (!gotgeo) {
1069 mr_msg(1, "Failed to get harddisk geometry, using old mode");
1070 }
1071/*
1072 if ((fd = open (drive, O_RDONLY)) != -1) {
1073 if (ioctl (fd, HDIO_GETGEO, &hdgeo) != -1) {
1074 close (fd);
1075 mr_msg (2, "Geometry of drive %s is C:%d, H:%d, S%d, its size is %d MB", drive, hdgeo.cylinders, hdgeo.heads, hdgeo.sectors, (hdgeo.cylinders * hdgeo.heads * hdgeo.sectors / 2 / 1024));
1076 if ( hdgeo.cylinders && hdgeo.heads && hdgeo.sectors ) {
1077 outvalB = ((long) (hdgeo.cylinders * hdgeo.heads * hdgeo.sectors / 2 / 1024));
1078 }
1079 }
1080 close (fd);
1081 */
1082#endif
1083 }
1084// OLDER DISKS will give ridiculously low value for outvalB (so outvalA is returned) :)
1085// NEWER DISKS will give sane value for outvalB (close to outvalA, in other words) :)
1086
1087 outvalC = (outvalA > outvalB) ? outvalA : outvalB;
1088
1089// mr_msg (5, "drive = %s, error = %s", drive, strerror (errno));
1090// fatal_error ("GPSOD: Unable to get size of drive");
1091 mr_msg(1, "%s --> %ld or %ld --> %ld", drive, outvalA, outvalB,
1092 outvalC);
1093
1094 return (outvalC);
1095}
1096
1097
1098/**
1099 * Determine whether @p format is supported by the kernel. Uses /proc/filesystems
1100 * under Linux and @c lsvfs under FreeBSD.
1101 * @param format The format to test.
1102 * @return TRUE if the format is supported, FALSE if not.
1103 */
1104bool is_this_a_valid_disk_format(char *format)
1105{
1106 char *good_formats = NULL;
1107 char *command = NULL;
1108 char *tmp = NULL;
1109 char *format_sz = NULL;
1110
1111 FILE *pin = NULL;
1112 bool retval = FALSE;
1113 size_t n = 0;
1114 assert_string_is_neither_NULL_nor_zerolength(format);
1115
1116 mr_asprintf(&format_sz, "%s ", format);
1117
1118#ifdef __FreeBSD__
1119 mr_asprintf(&command,
1120 "lsvfs | tr -s '\t' ' ' | grep -v Filesys | grep -v -- -- | cut -d' ' -f1 | tr -s '\n' ' '");
1121#else
1122 mr_asprintf(&command,
1123 "grep -v nodev /proc/filesystems | tr -s '\t' ' ' | cut -d' ' -f2 | tr -s '\n' ' '");
1124#endif
1125
1126 pin = popen(command, "r");
1127 mr_free(command);
1128
1129 if (!pin) {
1130 log_OS_error("Unable to read good formats");
1131 } else {
1132 mr_getline(&good_formats, &n , pin);
1133 if (pclose(pin)) {
1134 log_OS_error("Cannot pclose good formats");
1135 }
1136 mr_strip_spaces(good_formats);
1137 // " ntfs 7 " -- um, cheating much? :)
1138 mr_asprintf(&tmp, " %s swap lvm raid ntfs 7 ",good_formats);
1139 mr_free(good_formats);
1140 good_formats = tmp;
1141
1142 if (strstr(good_formats, format_sz)) {
1143 retval = TRUE;
1144 }
1145 mr_free(good_formats);
1146 }
1147 mr_free(format_sz);
1148 return (retval);
1149}
1150
1151
1152/** @def SWAPLIST_COMMAND The command to list the swap files/partitions in use. */
1153
1154/**
1155 * Determine whether @p device_raw is currently mounted.
1156 * @param device_raw The device to check.
1157 * @return TRUE if it's mounted, FALSE if not.
1158 */
1159bool is_this_device_mounted(char *device_raw)
1160{
1161
1162 /*@ pointers **************************************************** */
1163 FILE *fin;
1164
1165 /*@ buffers ***************************************************** */
1166 char *incoming = NULL;
1167 char *device_with_tab = NULL;
1168 char *device_with_space = NULL;
1169 char *tmp = NULL;
1170 size_t n = 0;
1171
1172#ifdef __FreeBSD__
1173#define SWAPLIST_COMMAND "swapinfo"
1174#else
1175#define SWAPLIST_COMMAND "cat /proc/swaps"
1176#endif
1177
1178 /*@ end vars **************************************************** */
1179
1180 assert(device_raw != NULL);
1181// assert_string_is_neither_NULL_nor_zerolength(device_raw);
1182 if (device_raw[0] != '/' && !strstr(device_raw, ":/")) {
1183 mr_msg(1, "%s needs to have a '/' prefixed - I'll do it",
1184 device_raw);
1185 mr_asprintf(&tmp, "/%s", device_raw);
1186 } else {
1187 mr_asprintf(&tmp, device_raw);
1188 }
1189 mr_msg(1, "Is %s mounted?", tmp);
1190 if (!strcmp(tmp, "/proc") || !strcmp(tmp, "proc")) {
1191 mr_msg(1,
1192 "I don't know how the heck /proc made it into the mountlist. I'll ignore it.");
1193 return (FALSE);
1194 }
1195 mr_asprintf(&device_with_tab, "%s\t", tmp);
1196 mr_asprintf(&device_with_space, "%s ", tmp);
1197 mr_free(tmp);
1198
1199 if (!(fin = popen("mount", "r"))) {
1200 log_OS_error("Cannot popen 'mount'");
1201 return (FALSE);
1202 }
1203 for (mr_getline(&incoming, &n, fin); !feof(fin);
1204 mr_getline(&incoming, &n, fin)) {
1205 if (strstr(incoming, device_with_space) //> incoming
1206 || strstr(incoming, device_with_tab)) // > incoming)
1207 {
1208 paranoid_pclose(fin);
1209 mr_free(incoming);
1210 return(TRUE);
1211 }
1212 }
1213 mr_free(incoming);
1214 mr_free(device_with_tab);
1215 paranoid_pclose(fin);
1216
1217 mr_asprintf(&tmp, "%s | grep -E \"^%s\" > /dev/null 2> /dev/null",
1218 SWAPLIST_COMMAND, device_with_space);
1219 mr_free(device_with_space);
1220
1221 mr_msg(4, "tmp (command) = '%s'", tmp);
1222 if (!system(tmp)) {
1223 mr_free(tmp);
1224 return(TRUE);
1225 }
1226 mr_free(tmp);
1227 return (FALSE);
1228}
1229
1230
1231#ifdef __FreeBSD__
1232// CODE IS FREEBSD-SPECIFIC
1233/**
1234 * Create a loopback device for specified @p fname.
1235 * @param fname The file to associate with a device.
1236 * @return /dev entry for the device, or NULL if it couldn't be allocated.
1237 */
1238char *make_vn(char *fname)
1239{
1240 char *device = NULL;
1241 char *mddevice = NULL;
1242 char *command = NULL;
1243 int vndev = 2;
1244
1245 if (atoi
1246 (call_program_and_get_last_line_of_output
1247 ("/sbin/sysctl -n kern.osreldate")) < 500000) {
1248 do {
1249 mr_free(mddevice);
1250 mr_asprintf(&mddevice, "vn%ic", vndev++);
1251 mr_free(command);
1252 mr_asprintf(&command, "vnconfig %s %s", mddevice, fname);
1253
1254 if (vndev > 10) {
1255 mr_free(command);
1256 mr_free(mddevice);
1257 return NULL;
1258 }
1259 }
1260 while (system(command));
1261 mr_free(command);
1262 } else {
1263 mr_asprintf(&command, "mdconfig -a -t vnode -f %s", fname);
1264 mr_asprintf(&mddevice, call_program_and_get_last_line_of_output(command));
1265 mr_free(command);
1266
1267 if (!strstr(mddevice, "md")) {
1268 mr_free(mddevice);
1269 return NULL;
1270 }
1271 }
1272 mr_asprintf(&device, "/dev/%s", mddevice);
1273 mr_free(mddevice);
1274 return(device);
1275}
1276
1277
1278// CODE IS FREEBSD-SPECIFIC
1279/**
1280 * Deallocate specified @p dname.
1281 * This should be called when you are done with the device created by make_vn(),
1282 * so the system does not run out of @c vn devices.
1283 * @param dname The device to deallocate.
1284 * @return 0 for success, nonzero for failure.
1285 */
1286int kick_vn(char *dname)
1287{
1288 char *command;
1289 int ret = 0;
1290
1291 if (strncmp(dname, "/dev/", 5) == 0) {
1292 dname += 5;
1293 }
1294
1295 if (atoi
1296 (call_program_and_get_last_line_of_output
1297 ("/sbin/sysctl -n kern.osreldate")) < 500000) {
1298 mr_asprintf(&command, "vnconfig -d %s", dname);
1299 } else {
1300 mr_asprintf(&command, "mdconfig -d -u %s", dname);
1301 }
1302 ret = system(command);
1303 mr_free(command);
1304 return(ret);
1305}
1306#endif
1307
1308
1309/**
1310 * Mount the CD-ROM at @p mountpoint.
1311 * @param device The device (or file if g_ISO_restore_mode) to mount.
1312 * @param mountpoint The place to mount it.
1313 * @return 0 for success, nonzero for failure.
1314 */
1315int mount_USB_here(char *device, char *mountpoint)
1316{
1317 /*@ buffer ****************************************************** */
1318 char *command;
1319 char *dev;
1320 int retval;
1321
1322 malloc_string(command);
1323 malloc_string(dev);
1324 assert_string_is_neither_NULL_nor_zerolength(device);
1325 assert_string_is_neither_NULL_nor_zerolength(mountpoint);
1326
1327 make_hole_for_dir(mountpoint);
1328 if (isdigit(device[0])) {
1329 return(1);
1330 } else {
1331 strcpy(dev, device);
1332 }
1333 log_msg(4, "(mount_USB_here --- device=%s, mountpoint=%s", device,
1334 mountpoint);
1335
1336#ifdef __FreeBSD__
1337 sprintf(command, "mount_vfat %s %s 2>> %s",
1338 device, mountpoint, MONDO_LOGFILE);
1339
1340#else
1341 sprintf(command, "mount %s -t vfat %s 2>> %s",
1342 device, mountpoint, MONDO_LOGFILE);
1343#endif
1344
1345 log_msg(4, command);
1346 retval = system(command);
1347 log_msg(1, "system(%s) returned %d", command, retval);
1348
1349 paranoid_free(command);
1350 paranoid_free(dev);
1351 return (retval);
1352}
1353
1354/**
1355 * Mount the CD-ROM at @p mountpoint.
1356 * @param device The device (or file if g_ISO_restore_mode) to mount.
1357 * @param mountpoint The place to mount it.
1358 * @return 0 for success, nonzero for failure.
1359 */
1360int mount_CDROM_here(char *device, char *mountpoint)
1361{
1362 /*@ buffer ****************************************************** */
1363 char *command;
1364 char *dev;
1365 char *options;
1366 int retval;
1367
1368 malloc_string(command);
1369 malloc_string(dev);
1370 malloc_string(options);
1371 assert_string_is_neither_NULL_nor_zerolength(device);
1372 assert_string_is_neither_NULL_nor_zerolength(mountpoint);
1373
1374 make_hole_for_dir(mountpoint);
1375 strcpy(options, "ro");
1376 if (isdigit(device[0])) {
1377 find_cdrom_device(device, FALSE);
1378 } else {
1379 strcpy(dev, device);
1380 }
1381 if (g_ISO_restore_mode) {
1382
1383#ifdef __FreeBSD__
1384 strcpy(dev, make_vn(device));
1385 if (!dev) {
1386 sprintf(command, "Unable to mount ISO (make_vn(%s) failed)",
1387 device);
1388 fatal_error(command);
1389 }
1390 strcpy(device, dev);
1391#else
1392 strcat(options, ",loop");
1393#endif
1394
1395 }
1396 log_msg(4, "(mount_CDROM_here --- device=%s, mountpoint=%s", device,
1397 mountpoint);
1398 /*@ end vars *************************************************** */
1399
1400#ifdef __FreeBSD__
1401 sprintf(command, "mount_cd9660 -r %s %s 2>> %s",
1402 device, mountpoint, MONDO_LOGFILE);
1403
1404#else
1405 sprintf(command, "mount %s -o %s -t iso9660 %s 2>> %s",
1406 device, options, mountpoint, MONDO_LOGFILE);
1407#endif
1408
1409 log_msg(4, command);
1410 if (strncmp(device, "/dev/", 5) == 0) {
1411 retract_CD_tray_and_defeat_autorun();
1412 }
1413 retval = system(command);
1414 log_msg(1, "system(%s) returned %d", command, retval);
1415
1416 paranoid_free(command);
1417 paranoid_free(dev);
1418 paranoid_free(options);
1419 return (retval);
1420}
1421
1422
1423
1424
1425
1426
1427/**
1428 * Ask the user for CD number @p cd_number_i_want.
1429 * Sets g_current_media_number once the correct CD is inserted.
1430 * @param bkpinfo The backup information structure. Fields used:
1431 * - @c bkpinfo->backup_media_type
1432 * - @c bkpinfo->prefix
1433 * - @c bkpinfo->isodir
1434 * - @c bkpinfo->media_device
1435 * - @c bkpinfo->please_dont_eject_when_restoring
1436 * @param cd_number_i_want The CD number to ask for.
1437 */
1438void
1439insist_on_this_cd_number(int cd_number_i_want)
1440{
1441
1442 /*@ int ************************************************************* */
1443 int res = 0;
1444
1445
1446 /*@ buffers ********************************************************* */
1447 char *tmp = NULL;
1448 char *request = NULL;
1449
1450 assert(bkpinfo != NULL);
1451 assert(cd_number_i_want > 0);
1452
1453// mr_msg(3, "Insisting on CD number %d", cd_number_i_want);
1454
1455 if (IS_THIS_A_STREAMING_BACKUP(bkpinfo->backup_media_type)) {
1456 mr_msg(3,
1457 "No need to insist_on_this_cd_number when the backup type isn't CD-R(W) or NFS or ISO");
1458 return;
1459 }
1460 mr_asprintf(&tmp, "mkdir -p " MNT_CDROM);
1461 run_program_and_log_output(tmp, 5);
1462 mr_free(tmp);
1463
1464 if (g_ISO_restore_mode || bkpinfo->backup_media_type == iso
1465 || bkpinfo->backup_media_type == nfs) {
1466 mr_msg(3, "Remounting CD");
1467 g_ISO_restore_mode = TRUE;
1468// FIXME --- I'm tempted to do something about this...
1469// Why unmount and remount again and again?
1470 if (is_this_device_mounted(MNT_CDROM)) {
1471 run_program_and_log_output("umount " MNT_CDROM, 5);
1472 }
1473 sprintf(tmp, "mkdir -p %s/isodir &> /dev/null", bkpinfo->tmpdir);
1474 system(tmp);
1475 mr_asprintf(&tmp, "%s/%s/%s-%d.iso", bkpinfo->isodir,
1476 bkpinfo->nfs_remote_dir, bkpinfo->prefix,
1477 cd_number_i_want);
1478 if (!does_file_exist(tmp)) {
1479 mr_free(tmp);
1480 mr_asprintf(&tmp, "%s/isodir/%s/%s-%d.iso", bkpinfo->tmpdir,
1481 bkpinfo->nfs_remote_dir, bkpinfo->prefix,
1482 cd_number_i_want);
1483 if (does_file_exist(tmp)) {
1484 mr_msg(1,
1485 "FIXME - hacking bkpinfo->isodir from '%s' to %s/isodir",
1486 bkpinfo->isodir, bkpinfo->tmpdir);
1487 sprintf(bkpinfo->isodir, "%s/isodir", bkpinfo->tmpdir);
1488 }
1489 }
1490 mr_msg(3, "Mounting %s at %s", tmp, MNT_CDROM);
1491 if (mount_CDROM_here(tmp, MNT_CDROM)) {
1492 fatal_error("Mommy!");
1493 }
1494 mr_free(tmp);
1495 }
1496 if ((res = what_number_cd_is_this()) != cd_number_i_want) {
1497 mr_msg(3, "Currently, we hold %d but we want %d", res,
1498 cd_number_i_want);
1499 mr_asprintf(&tmp, "Insisting on %s #%d",
1500 bkpinfo->backup_media_string,
1501 cd_number_i_want);
1502 mr_asprintf(&request, "Please insert %s #%d and press Enter.",
1503 bkpinfo->backup_media_string,
1504 cd_number_i_want);
1505 mr_msg(3, tmp);
1506 mr_free(tmp);
1507
1508 while (what_number_cd_is_this() != cd_number_i_want) {
1509 sync();
1510 if (is_this_device_mounted(MNT_CDROM)) {
1511 res =
1512 run_program_and_log_output("umount " MNT_CDROM, FALSE);
1513 } else {
1514 res = 0;
1515 }
1516 if (res) {
1517 log_to_screen(_("WARNING - failed to unmount CD-ROM drive"));
1518 }
1519 if (!bkpinfo->please_dont_eject) {
1520 res = eject_device(bkpinfo->media_device);
1521 } else {
1522 res = 0;
1523 }
1524 if (res) {
1525 log_to_screen(_("WARNING - failed to eject CD-ROM disk"));
1526 }
1527 popup_and_OK(request);
1528 if (!bkpinfo->please_dont_eject) {
1529 inject_device(bkpinfo->media_device);
1530 }
1531 sync();
1532 }
1533 mr_free(request);
1534
1535 mr_msg(1, "Thankyou. Proceeding...");
1536 g_current_media_number = cd_number_i_want;
1537 }
1538}
1539/* @} - end of deviceGroup */
1540
1541
1542/**
1543 * Ask user for details of backup/restore information.
1544 * Called when @c mondoarchive doesn't get any parameters.
1545 * @param bkpinfo The backup information structure to fill out with the user's data.
1546 * @param archiving_to_media TRUE if archiving, FALSE if restoring.
1547 * @return 0, always.
1548 * @bug No point of `int' return value.
1549 * @ingroup archiveGroup
1550 */
1551int interactively_obtain_media_parameters_from_user(bool archiving_to_media)
1552// archiving_to_media is TRUE if I'm being called by mondoarchive
1553// archiving_to_media is FALSE if I'm being called by mondorestore
1554{
1555 char *tmp = NULL;
1556 char *tmp1 = NULL;
1557 char *tmp2 = NULL;
1558 char *sz_size = NULL;
1559 char *command = NULL;
1560 char *comment = NULL;
1561 char *prompt = NULL;
1562 int i = 0;
1563 FILE *fin = NULL;
1564
1565 assert(bkpinfo != NULL);
1566 bkpinfo->nonbootable_backup = FALSE;
1567
1568// Tape, CD, NFS, ...?
1569 srandom(getpid());
1570 bkpinfo->backup_media_type =
1571 (g_restoring_live_from_cd) ? cdr :
1572 which_backup_media_type(bkpinfo->restore_data);
1573 if (bkpinfo->backup_media_type == none) {
1574 log_to_screen(_("User has chosen not to backup the PC"));
1575 finish(1);
1576 }
1577 if (bkpinfo->backup_media_type == tape && bkpinfo->restore_data) {
1578 popup_and_OK(_("Please remove media from drive"));
1579 }
1580 mr_msg(3, "media type = %s",
1581 bkptype_to_string(bkpinfo->backup_media_type));
1582 if (archiving_to_media) {
1583 sensibly_set_tmpdir_and_scratchdir();
1584 }
1585 bkpinfo->compression_level =
1586 (bkpinfo->backup_media_type == cdstream) ? 1 : 5;
1587 mvaddstr_and_log_it(2, 0, " ");
1588
1589// Find device's /dev (or SCSI) entry
1590 switch (bkpinfo->backup_media_type) {
1591 case cdr:
1592 case cdrw:
1593 case dvd:
1594 case usb:
1595 /* Never try to eject a USB device */
1596 if (bkpinfo->backup_media_type == usb) {
1597 bkpinfo->please_dont_eject = TRUE;
1598 }
1599 if (archiving_to_media) {
1600 if ((bkpinfo->backup_media_type != dvd) && (bkpinfo->backup_media_type != usb)) {
1601 if (ask_me_yes_or_no
1602 (_("Does your computer have a manual tray?")))
1603 {
1604 bkpinfo->manual_tray = TRUE;
1605 }
1606 }
1607 if ((bkpinfo->compression_level =
1608 which_compression_level()) == -1) {
1609 log_to_screen(_("User has chosen not to backup the PC"));
1610 finish(1);
1611 }
1612 mr_asprintf(&comment, _("What speed is your %s (re)writer?"),
1613 bkpinfo->backup_media_string);
1614 mr_asprintf(&tmp, "%d", mr_conf->iso_burning_speed);
1615 if (bkpinfo->backup_media_type != usb) {
1616 /* BERLIOS: NOW that tmp isn't static anymore it does NOT work */
1617 if (!popup_and_get_string(_("Speed"), comment, tmp, 4)) {
1618 log_to_screen(_("User has chosen not to backup the PC"));
1619 finish(1);
1620 }
1621 }
1622 mr_free(comment);
1623
1624 bkpinfo->writer_speed = atoi(tmp);
1625 mr_free(tmp);
1626
1627 mr_asprintf(&comment, _("What is your media device ?"));
1628 mr_asprintf(&tmp, mr_conf->media_device);
1629
1630 if (!popup_and_get_string("Device", comment, tmp, 5)) {
1631 log_to_screen(_("User has chosen not to backup the PC"));
1632 finish(1);
1633 }
1634 mr_free(comment);
1635
1636 mr_free(bkpinfo->media_device);
1637 bkpinfo->media_device = tmp;
1638
1639 /* Also adapt the burning device if needed */
1640 if ((strcmp(bkpinfo->media_device, mr_conf->media_device) != 0) &&
1641 (strcmp(mr_conf->media_device,mr_conf->iso_burning_dev) != 0)) {
1642 mr_asprintf(&comment, _("What is your burning media device then ?"));
1643 mr_asprintf(&tmp, mr_conf->iso_burning_dev);
1644
1645 if (!popup_and_get_string("Device", comment, tmp, 5)) {
1646 log_to_screen(_("User has chosen not to backup the PC"));
1647 finish(1);
1648 }
1649 mr_free(comment);
1650
1651 /* BCO: We change the mr_conf struct. Check that it doesn't create pb */
1652 mr_free(bkpinfo->iso_burning_dev);
1653 bkpinfo->iso_burning_dev = tmp;
1654 }
1655
1656 mr_asprintf(&comment,
1657 _("How much data (in Megabytes) will each %s store?"),
1658 bkpinfo->backup_media_string);
1659 mr_asprintf(&sz_size, "%d", mr_conf->media_size);
1660
1661 if (!popup_and_get_string("Size", comment, sz_size, 5)) {
1662 log_to_screen(_("User has chosen not to backup the PC"));
1663 finish(1);
1664 }
1665 mr_free(comment);
1666
1667 bkpinfo->media_size = atol(sz_size);
1668 mr_free(sz_size);
1669
1670 if (bkpinfo->media_size <= 0L) {
1671 log_to_screen(_("User has chosen not to backup the PC"));
1672 finish(1);
1673 }
1674 }
1675 /* No break because we continue even for usb */
1676 case cdstream:
1677 if ((bkpinfo->disaster_recovery) && (bkpinfo->backup_media_type != usb)) {
1678 mr_allocstr(bkpinfo->media_device, "/dev/cdrom");
1679 mr_msg(2, "CD-ROM device assumed to be at %s", bkpinfo->media_device);
1680 } else if ((bkpinfo->restore_data && (bkpinfo->backup_media_type != usb))
1681 || bkpinfo->backup_media_type == dvd) {
1682 if (!bkpinfo->media_device) {
1683 mr_allocstr(bkpinfo->media_device, "/dev/cdrom");
1684 } // just for the heck of it :)
1685 mr_msg(1, "bkpinfo->media_device = %s", bkpinfo->media_device);
1686 mr_asprintf(&comment,
1687 _("Please specify your %s drive's /dev entry"), media_descriptor_string(bkpinfo->backup_media));
1688 mr_asprintf(&tmp,"/dev/cdrom");
1689 if (!popup_and_get_string
1690 (_("Device?"), comment, tmp, MAX_STR_LEN / 4)) {
1691 log_to_screen(_("User has chosen not to backup the PC"));
1692 finish(1);
1693 }
1694 mr_free(comment);
1695 mr_free(bkpinfo->media_device);
1696 bkpinfo->media_device=tmp;
1697
1698 mr_msg(2, "%s device found at %s",
1699 bkpinfo->backup_media_string,
1700 bkpinfo->media_device);
1701 } else {
1702 if (bkpinfo->backup_media_type == usb) {
1703 mr_asprintf(&comment, _("What is the /dev entry of your USB Disk/Key, please ?"));
1704 } else {
1705 mr_asprintf(&comment, _("What is your media device ?"));
1706 }
1707 mr_asprintf(&tmp, mr_conf->media_device);
1708
1709 if (!popup_and_get_string("Device", comment, tmp, 5)) {
1710 log_to_screen(_("User has chosen not to backup the PC"));
1711 finish(1);
1712 }
1713 mr_free(comment);
1714
1715 mr_free(bkpinfo->media_device);
1716 bkpinfo->media_device = tmp;
1717
1718 /* Also adapt the burning device if needed */
1719 if ((strcmp(bkpinfo->media_device, mr_conf->media_device) != 0) &&
1720 (strcmp(mr_conf->media_device,mr_conf->iso_burning_dev) != 0)) {
1721 mr_asprintf(&comment, _("What is your burning media device then ?"));
1722 mr_asprintf(&tmp, mr_conf->iso_burning_dev);
1723
1724 if (!popup_and_get_string("Device", comment, tmp, 5)) {
1725 log_to_screen(_("User has chosen not to backup the PC"));
1726 finish(1);
1727 }
1728 mr_free(comment);
1729
1730 /* BCO: We change the mr_conf struct. Check that it doesn't create pb */
1731 mr_free(bkpinfo->iso_burning_dev);
1732 bkpinfo->iso_burning_dev = tmp;
1733 }
1734
1735 }
1736 /* BERLIOS: Is it useful ?? */
1737 bkpinfo->media_size = (long)650;
1738 break;
1739
1740 case udev:
1741 if (!ask_me_yes_or_no
1742 (_("This option is for advanced users only. Are you sure?"))) {
1743 log_to_screen(_("User has chosen not to backup the PC"));
1744 finish(1);
1745 }
1746
1747 case tape:
1748 if (bkpinfo->media_device) {
1749 if ((fin = fopen(bkpinfo->media_device, "r"))) {
1750 paranoid_fclose(fin);
1751 } else {
1752 if (does_file_exist("/tmp/mondo-restore.cfg")) {
1753 /* BERLIOS: NOW that bkpinfo->media_device isn't static anymore it does NOT work */
1754 read_cfg_var("/tmp/mondo-restore.cfg", "media-dev",
1755 bkpinfo->media_device);
1756 }
1757 }
1758 mr_asprintf(&comment,
1759 _("I think I've found your tape streamer at %s; am I right on the money?"),
1760 bkpinfo->media_device);
1761 if (!ask_me_yes_or_no(comment)) {
1762 mr_asprintf(&tmp,bkpinfo->media_device);
1763 if (!popup_and_get_string
1764 (_("Device name?"),
1765 _("What is the /dev entry of your tape streamer?"),
1766 tmp, MAX_STR_LEN / 4)) {
1767 log_to_screen("User has chosen not to backup the PC");
1768 finish(1);
1769 }
1770 mr_free(bkpinfo->media_device);
1771 bkpinfo->media_device = tmp;
1772 }
1773 mr_free(comment);
1774 } else {
1775 mr_asprintf(&tmp,bkpinfo->media_device);
1776 if (!popup_and_get_string
1777 (_("Device name?"),
1778 _("What is the /dev entry of your tape streamer?"),
1779 tmp, MAX_STR_LEN / 4)) {
1780 log_to_screen("User has chosen not to backup the PC");
1781 finish(1);
1782 }
1783 mr_free(bkpinfo->media_device);
1784 bkpinfo->media_device = tmp;
1785 }
1786 mr_asprintf(&tmp, "ls -l %s", bkpinfo->media_device);
1787 if (run_program_and_log_output(tmp, FALSE)) {
1788 log_to_screen(_("User has not specified a valid /dev entry"));
1789 finish(1);
1790 }
1791 mr_free(tmp);
1792 bkpinfo->media_size = 0L;
1793 mr_msg(4, "media_size = %ld", bkpinfo->media_size);
1794 if (archiving_to_media) {
1795 if ((bkpinfo->compression_level =
1796 which_compression_level()) == -1) {
1797 log_to_screen(_("User has chosen not to backup the PC"));
1798 finish(1);
1799 }
1800 }
1801 break;
1802
1803
1804
1805 case nfs:
1806 /* Never try to eject a NFS device */
1807 bkpinfo->please_dont_eject = TRUE;
1808
1809 /* Initiate bkpinfo nfs_mount path from running environment if not already done */
1810 if (!bkpinfo->nfs_mount[0]) {
1811 strcpy(bkpinfo->nfs_mount,
1812 call_program_and_get_last_line_of_output
1813 ("mount | grep \":\" | cut -d' ' -f1 | head -n1"));
1814 }
1815#ifdef __FreeBSD__
1816 if (TRUE)
1817#else
1818 if (!bkpinfo->disaster_recovery)
1819#endif
1820 {
1821 if (!popup_and_get_string
1822 ("NFS dir.",
1823 "Please enter path and directory where archives are stored remotely. (Mondo has taken a guess at the correct value. If it is incorrect, delete it and type the correct one.)",
1824 bkpinfo->nfs_mount, MAX_STR_LEN / 4)) {
1825 log_to_screen("User has chosen not to backup the PC");
1826 finish(1);
1827 }
1828 if (!bkpinfo->restore_data) {
1829 if ((bkpinfo->compression_level =
1830 which_compression_level()) == -1) {
1831 log_to_screen(_("User has chosen not to backup the PC"));
1832 finish(1);
1833 }
1834 }
1835 // check whether already mounted - we better remove
1836 // surrounding spaces and trailing '/' for this
1837 mr_strip_spaces(bkpinfo->nfs_mount);
1838 if (bkpinfo->nfs_mount[strlen(bkpinfo->nfs_mount) - 1] == '/')
1839 bkpinfo->nfs_mount[strlen(bkpinfo->nfs_mount) - 1] = '\0';
1840 mr_asprintf(&command, "mount | grep \"%s \" | cut -d' ' -f3",
1841 bkpinfo->nfs_mount);
1842 strcpy(bkpinfo->isodir,
1843 call_program_and_get_last_line_of_output(command));
1844 mr_free(command);
1845
1846 if (!bkpinfo->restore_data) {
1847 mr_asprintf(&comment,
1848 _("How much data (in Megabytes) will each media store?"));
1849 if (!popup_and_get_string(_("Size"), comment, sz_size, 5)) {
1850 log_to_screen(_("User has chosen not to backup the PC"));
1851 finish(1);
1852 }
1853 } else {
1854 sz_size = 0;
1855 }
1856 mr_free(comment);
1857 bkpinfo->media_size = atol(sz_size);
1858 if (bkpinfo->media_size <= 0L) {
1859 log_to_screen(_("User has chosen not to backup the PC"));
1860 finish(1);
1861 }
1862 }
1863 if (bkpinfo->disaster_recovery) {
1864 sprintf(command ,"umount %s/isodir 2> /dev/null", bkpinfo->tmpdir);
1865 system(command);
1866 if (!popup_and_get_string
1867 ("NFS share", "Which remote NFS share should I mount?",
1868 bkpinfo->nfs_mount, MAX_STR_LEN)) {
1869 log_to_screen("User has chosen not to backup the PC");
1870 finish(1);
1871 }
1872 }
1873 /* Initiate bkpinfo isodir path from running environment if mount already done */
1874 if (is_this_device_mounted(bkpinfo->nfs_mount)) {
1875 strcpy(bkpinfo->isodir,
1876 call_program_and_get_last_line_of_output
1877 ("mount | grep \":\" | cut -d' ' -f3 | head -n1"));
1878 } else {
1879 sprintf(bkpinfo->isodir, "%s/nfsdir", bkpinfo->tmpdir);
1880 mr_asprintf(&command, "mkdir -p %s", bkpinfo->isodir);
1881 run_program_and_log_output(command, 5);
1882 mr_free(command);
1883
1884 mr_asprintf(&tmp, "mount -t nfs -o nolock %s %s", bkpinfo->nfs_mount,
1885 bkpinfo->isodir);
1886 run_program_and_log_output(tmp, 3);
1887 mr_free(tmp);
1888 malloc_string(g_selfmounted_isodir);
1889 strcpy(g_selfmounted_isodir, bkpinfo->isodir);
1890 }
1891 if (!is_this_device_mounted(bkpinfo->nfs_mount)) {
1892 popup_and_OK
1893 (_("Please mount that partition before you try to backup to or restore from it."));
1894 finish(1);
1895 }
1896 mr_asprintf(&tmp, bkpinfo->nfs_remote_dir);
1897 if (!popup_and_get_string
1898 ("Directory", "Which directory within that mountpoint?", tmp,
1899 MAX_STR_LEN)) {
1900 log_to_screen("User has chosen not to backup the PC");
1901 finish(1);
1902 }
1903 strcpy(bkpinfo->nfs_remote_dir, tmp);
1904 mr_free(tmp);
1905 // check whether writable - we better remove surrounding spaces for this
1906 mr_strip_spaces(bkpinfo->nfs_remote_dir);
1907 mr_asprintf(&tmp1,"%s/%s/.dummy.txt", bkpinfo->isodir,bkpinfo->nfs_remote_dir);
1908 mr_asprintf(&command, "echo hi > '%s'", tmp1);
1909 while (run_program_and_log_output(command, FALSE)) {
1910 mr_asprintf(&tmp, bkpinfo->nfs_remote_dir);
1911 mr_asprintf(&prompt,
1912 _("Directory '%s' under mountpoint '%s' does not exist or is not writable. You can fix this or change the directory and retry or cancel the backup."),
1913 bkpinfo->nfs_remote_dir, bkpinfo->isodir);
1914 if (!popup_and_get_string
1915 ("Directory", prompt, tmp, MAX_STR_LEN)) {
1916 log_to_screen("User has chosen not to backup the PC");
1917 finish(1);
1918 }
1919 mr_free(prompt);
1920
1921 strcpy(bkpinfo->nfs_remote_dir, tmp);
1922 mr_free(tmp);
1923 // check whether writable - we better remove surrounding space s for this
1924 mr_strip_spaces(bkpinfo->nfs_remote_dir);
1925 paranoid_free(tmp1);
1926 mr_asprintf(&tmp1,"%s/%s/.dummy.txt", bkpinfo->isodir,bkpinfo->nfs_remote_dir);
1927
1928 mr_free(command);
1929 mr_asprintf(&command, "echo hi > '%s'", tmp1);
1930 }
1931 mr_free(command);
1932 unlink(tmp1);
1933 paranoid_free(tmp1);
1934
1935 mr_asprintf(&tmp, mr_conf->prefix);
1936 if (!popup_and_get_string
1937 ("Prefix.",
1938 "Please enter the prefix that will be prepended to your ISO filename. Example: machine1 to obtain machine1-[1-9]*.iso files",
1939 tmp, MAX_STR_LEN / 4)) {
1940 log_to_screen("User has chosen not to backup the PC");
1941 finish(1);
1942 }
1943 mr_free(bkpinfo->prefix);
1944 bkpinfo->prefix = tmp;
1945 mr_msg(3, "prefix set to %s", bkpinfo->prefix);
1946
1947 if (archiving_to_media) {
1948 if (!popup_and_get_string
1949 ("Size.",
1950 "Please enter how big you want each NFS image to be (in megabytes).",
1951 sz_size, 16)) {
1952 log_to_screen("User has chosen not to backup the PC");
1953 finish(1);
1954 }
1955 bkpinfo->media_size = atol(sz_size);
1956 if (bkpinfo->media_size <= 0L) {
1957 log_to_screen(_("User has chosen not to backup the PC"));
1958 finish(1);
1959 }
1960 }
1961 mr_msg(3, "Just set nfs_remote_dir to %s",
1962 bkpinfo->nfs_remote_dir);
1963 mr_msg(3, "isodir is still %s", bkpinfo->isodir);
1964 break;
1965
1966 case iso:
1967 if (!bkpinfo->disaster_recovery) {
1968 if (!popup_and_get_string
1969 ("Storage dir.",
1970 "Please enter the full path name to the directory for your ISO images. Example: /mnt/raid0_0",
1971 bkpinfo->isodir, MAX_STR_LEN / 4)) {
1972 log_to_screen("User has chosen not to backup the PC");
1973 finish(1);
1974 }
1975 if (archiving_to_media) {
1976 if ((bkpinfo->compression_level =
1977 which_compression_level()) == -1) {
1978 log_to_screen(_("User has chosen not to backup the PC"));
1979 finish(1);
1980 }
1981 if (!popup_and_get_string
1982 ("ISO size.",
1983 "Please enter how big you want each ISO image to be (in megabytes). This should be less than or equal to the size of the CD-R[W]'s or DVD's you plan to backup to.",
1984 sz_size, 16)) {
1985 log_to_screen("User has chosen not to backup the PC");
1986 finish(1);
1987 }
1988 bkpinfo->media_size = atol(sz_size);
1989 /* BERLIOS: this should be useless
1990 */
1991 } else {
1992 bkpinfo->media_size = (long)650;
1993 }
1994 }
1995 mr_asprintf(&tmp, mr_conf->prefix);
1996 if (!popup_and_get_string
1997 ("Prefix.",
1998 "Please enter the prefix that will be prepended to your ISO filename. Example: machine1 to obtain machine1-[1-9]*.iso files",
1999 tmp, MAX_STR_LEN / 4)) {
2000 log_to_screen("User has chosen not to backup the PC");
2001 finish(1);
2002 }
2003 mr_free(bkpinfo->prefix);
2004 bkpinfo->prefix = tmp;
2005 mr_msg(3, "prefix set to %s", bkpinfo->prefix);
2006 break;
2007 default:
2008 fatal_error
2009 ("I, Mojo Jojo, shall defeat those pesky Powerpuff Girls!");
2010 }
2011 if (archiving_to_media) {
2012
2013#ifdef __FreeBSD__
2014 strcpy(bkpinfo->boot_device,
2015 call_program_and_get_last_line_of_output
2016 ("mount | grep ' / ' | head -1 | cut -d' ' -f1 | sed 's/\\([0-9]\\).*/\\1/'"));
2017#else
2018 strcpy(bkpinfo->boot_device,
2019 call_program_and_get_last_line_of_output
2020 ("mount | grep ' / ' | head -1 | cut -d' ' -f1 | sed 's/[0-9].*//'"));
2021#endif
2022 i = which_boot_loader(bkpinfo->boot_device);
2023 if (i == 'U') // unknown
2024 {
2025
2026#ifdef __FreeBSD__
2027 if (!popup_and_get_string
2028 ("Boot device",
2029 "What is your boot device? (e.g. /dev/ad0)",
2030 bkpinfo->boot_device, MAX_STR_LEN / 4)) {
2031 log_to_screen("User has chosen not to backup the PC");
2032 finish(1);
2033 }
2034 i = which_boot_loader(bkpinfo->boot_device);
2035#else
2036 if (!popup_and_get_string
2037 ("Boot device",
2038 "What is your boot device? (e.g. /dev/hda)",
2039 bkpinfo->boot_device, MAX_STR_LEN / 4)) {
2040 log_to_screen("User has chosen not to backup the PC");
2041 finish(1);
2042 }
2043 if (does_string_exist_in_boot_block
2044 (bkpinfo->boot_device, "LILO")) {
2045 i = 'L';
2046 } else
2047 if (does_string_exist_in_boot_block
2048 (bkpinfo->boot_device, "ELILO")) {
2049 i = 'E';
2050 } else
2051 if (does_string_exist_in_boot_block
2052 (bkpinfo->boot_device, "GRUB")) {
2053 i = 'G';
2054 } else {
2055 i = 'U';
2056 }
2057#endif
2058 if (i == 'U') {
2059 if (ask_me_yes_or_no
2060 (_("Unidentified boot loader. Shall I restore it byte-for-byte at restore time and hope for the best?")))
2061 {
2062 i = 'R'; // raw
2063 } else {
2064 log_to_screen
2065 (_("I cannot find your boot loader. Please run mondoarchive with parameters."));
2066 finish(1);
2067 }
2068 }
2069 }
2070 bkpinfo->boot_loader = i;
2071 strcpy(bkpinfo->include_paths, "/");
2072 if (!popup_and_get_string
2073 ("Backup paths",
2074 "Please enter paths which you want me to backup. The default is '/' (i.e. everything).",
2075 bkpinfo->include_paths, MAX_STR_LEN)) {
2076 log_to_screen("User has chosen not to backup the PC");
2077 finish(1);
2078 }
2079 mr_asprintf(&tmp, list_of_NFS_mounts_only());
2080 if (strlen(tmp) > 2) {
2081 if (bkpinfo->exclude_paths != NULL) {
2082 mr_strcat(bkpinfo->exclude_paths, " ");
2083 }
2084 mr_strcat(bkpinfo->exclude_paths,tmp);
2085 }
2086 mr_free(tmp);
2087// NTFS
2088 mr_asprintf(&tmp,
2089 call_program_and_get_last_line_of_output
2090 ("parted2fdisk -l | grep -i ntfs | awk '{ print $1};' | tr -s '\\n' ' ' | awk '{ print $0};'"));
2091 if (strlen(tmp) > 2) {
2092 if (!popup_and_get_string
2093 ("NTFS partitions",
2094 "Please enter/confirm the NTFS partitions you wish to backup as well.",
2095 tmp, MAX_STR_LEN / 4)) {
2096 log_to_screen("User has chosen not to backup the PC");
2097 finish(1);
2098 }
2099 strncpy(bkpinfo->image_devs, tmp, MAX_STR_LEN / 4);
2100 }
2101 mr_free(tmp);
2102
2103 if (!popup_and_get_string
2104 ("Exclude paths",
2105 "Please enter paths which you do NOT want to backup. Separate them with spaces. NB: /tmp and /proc are always excluded. :-) Just hit 'Enter' if you want to do a full system backup.",
2106 bkpinfo->exclude_paths, (4*MAX_STR_LEN)-1)) {
2107 log_to_screen("User has chosen not to backup the PC");
2108 finish(1);
2109 }
2110// Interactive mode:
2111#ifdef __IA64__
2112 bkpinfo->make_cd_use_lilo = TRUE;
2113#else
2114 bkpinfo->make_cd_use_lilo = FALSE;
2115#endif
2116 bkpinfo->backup_data = TRUE;
2117 bkpinfo->verify_data =
2118 ask_me_yes_or_no
2119 (_("Will you want to verify your backups after Mondo has created them?"));
2120
2121#ifndef __FreeBSD__
2122 if (!ask_me_yes_or_no
2123 ("Are you confident that your kernel is a sane, sensible, standard Linux kernel? Say 'no' if you are using a Gentoo <1.4 or Debian <3.0, please."))
2124#endif
2125 {
2126 mr_allocstr(bkpinfo->kernel_path, "FAILSAFE");
2127 }
2128
2129 if (!ask_me_yes_or_no
2130 (_("Are you sure you want to proceed? Hit 'no' to abort."))) {
2131 log_to_screen(_("User has chosen not to backup the PC"));
2132 finish(1);
2133 }
2134 } else {
2135 bkpinfo->restore_data = TRUE; // probably...
2136 }
2137
2138 if (bkpinfo->backup_media_type == iso
2139 || bkpinfo->backup_media_type == nfs) {
2140 g_ISO_restore_mode = TRUE;
2141 }
2142#ifdef __FreeBSD__
2143// skip
2144#else
2145 if (bkpinfo->backup_media_type == nfs) {
2146 mr_msg(3, "I think the NFS mount is mounted at %s",
2147 bkpinfo->isodir);
2148 }
2149 log_it("isodir = %s", bkpinfo->isodir);
2150 log_it("nfs_mount = '%s'", bkpinfo->nfs_mount);
2151#endif
2152
2153 log_it("media device = %s", bkpinfo->media_device);
2154 log_it("media size = %ld", bkpinfo->media_size);
2155 log_it("media type = %s", bkpinfo->backup_media_string);
2156 log_it("prefix = %s", bkpinfo->prefix);
2157 log_it("compression tool = %ld", bkpinfo->compression_tool);
2158 log_it("compression suffix = %ld", bkpinfo->compression_suffix);
2159 log_it("compression level = %ld", bkpinfo->compression_level);
2160 log_it("include_paths = '%s'", bkpinfo->include_paths);
2161 log_it("exclude_paths = '%s'", bkpinfo->exclude_paths);
2162 log_it("scratchdir = '%s'", bkpinfo->scratchdir);
2163 log_it("tmpdir = '%s'", bkpinfo->tmpdir);
2164 log_it("image_devs = '%s'", bkpinfo->image_devs);
2165 log_it("boot_device = '%s' (loader=%c)", bkpinfo->boot_device,
2166 bkpinfo->boot_loader);
2167 if (bkpinfo->media_size < 0L) {
2168 if (archiving_to_media) {
2169 fatal_error("Media size is less than zero.");
2170 } else {
2171 mr_msg(2, "Warning - media size is less than zero.");
2172 bkpinfo->media_size = 0L;
2173 }
2174 }
2175 return (0);
2176}
2177
2178
2179/**
2180 * Get a space-separated list of NFS mounts.
2181 * @return The list created.
2182 * @note The return value points to static data that will be overwritten with each call.
2183 * @bug Even though we only want the mounts, the devices are still checked.
2184 */
2185char *list_of_NFS_mounts_only(void)
2186{
2187 char *exclude_these_devices;
2188 char *exclude_these_directories;
2189 static char result_sz[512];
2190
2191 malloc_string(exclude_these_devices);
2192 malloc_string(exclude_these_directories);
2193 strcpy(exclude_these_directories,
2194 call_program_and_get_last_line_of_output
2195 ("mount -t coda,ncpfs,nfs,smbfs,cifs,afs,ocfs,ocfs2,mvfs | tr -s '\t' ' ' | cut -d' ' -f3 | tr -s '\n' ' ' | awk '{print $0;}'"));
2196 strcpy(exclude_these_devices,
2197 call_program_and_get_last_line_of_output
2198 ("tr -s '\t' ' ' < /etc/fstab | grep -E '( (coda|ncpfs|nfs|smbfs|cifs|afs|ocfs|ocfs2|mvfs) )' | cut -d' ' -f1 | tr -s '\n' ' ' | awk '{print $0;}'"));
2199 sprintf(result_sz, "%s", exclude_these_directories);
2200 mr_free(exclude_these_devices);
2201 mr_free(exclude_these_directories);
2202 return (result_sz);
2203}
2204/* @} - end of utilityGroup */
2205
2206
2207/**
2208 * Set the tmpdir and scratchdir to reside on the partition with the most free space.
2209 * Automatically excludes DOS, NTFS, SMB, and NFS filesystems.
2210 * @param bkpinfo The backup information structure. @c bkpinfo->tmpdir and @c bkpinfo->scratchdir will be set.
2211 * @ingroup utilityGroup
2212 */
2213void sensibly_set_tmpdir_and_scratchdir()
2214{
2215 char *tmp = NULL;
2216 char *command = NULL;
2217 char *sz = NULL;
2218 int i = 0;
2219
2220 malloc_string(command);
2221 assert(bkpinfo != NULL);
2222
2223#ifdef __FreeBSD__
2224 mr_asprintf(&tmp,
2225 call_program_and_get_last_line_of_output
2226 ("LANGUAGE=C df -m -P -t nonfs,msdosfs,ntfs,smbfs,smb,cifs,afs,ocfs,ocfs2,mvfs | tr -s '\t' ' ' | grep -vE \"none|Filesystem\" | awk '{printf \"%s %s\\n\", $4, $6;}' | sort -n | tail -n1 | awk '{print $NF;}'"));
2227#else
2228 mr_asprintf(&tmp,
2229 call_program_and_get_last_line_of_output
2230 ("LANGUAGE=C df -m -P -x nfs -x vfat -x ntfs -x smbfs -x smb -x cifs -x afs -x ocfs -x ocfs2 -x mvfs | sed 's/ /devdev/' | tr -s '\t' ' ' | grep -vE \"none|Filesystem|/dev/shm\" | awk '{printf \"%s %s\\n\", $4, $6;}' | sort -n | tail -n1 | awk '{print $NF;}'"));
2231#endif
2232
2233 if (tmp[0] != '/') {
2234 mr_asprintf(&sz, "/%s", tmp);
2235 mr_free(tmp);
2236 tmp = sz;
2237 }
2238 if (!tmp[0]) {
2239 fatal_error("I couldn't figure out the tempdir!");
2240 }
2241 setup_tmpdir(tmp);
2242 log_it("bkpinfo->tmpdir is being set to %s", bkpinfo->tmpdir);
2243
2244 sprintf(bkpinfo->scratchdir, "%s/mondo.scratch.%d", tmp, i);
2245 log_it("bkpinfo->scratchdir is being set to %s", bkpinfo->scratchdir);
2246
2247 mr_asprintf(&command, "rm -Rf %s/tmp.mondo.* %s/mondo.scratch.*", tmp, tmp);
2248 mr_free(tmp);
2249
2250 paranoid_system(command);
2251 mr_free(command);
2252}
2253
2254
2255/**
2256 * @addtogroup deviceGroup
2257 * @{
2258 */
2259/**
2260 * If we can read @p dev, set @p output to it.
2261 * If @p dev cannot be read, set @p output to "".
2262 * @param dev The device to check for.
2263 * @param output Set to @p dev if @p dev exists, "" otherwise.
2264 * @return TRUE if @p dev exists, FALSE if it doesn't.
2265 */
2266bool set_dev_to_this_if_rx_OK(char *output, char *dev)
2267{
2268 char *command = NULL;
2269
2270 if (!dev || dev[0] == '\0') {
2271 output[0] = '\0';
2272 return (FALSE);
2273 }
2274 mr_msg(10, "Injecting %s", dev);
2275 inject_device(dev);
2276 if (!does_file_exist(dev)) {
2277 mr_msg(10, "%s doesn't exist. Returning FALSE.", dev);
2278 return (FALSE);
2279 }
2280 mr_asprintf(&command, "dd bs=%ld count=1 if=%s of=/dev/null &> /dev/null",
2281 512L, dev);
2282 if (!run_program_and_log_output(command, FALSE)
2283 && !run_program_and_log_output(command, FALSE)) {
2284 strcpy(output, dev);
2285 mr_msg(4, "Found it - %s", dev);
2286 mr_free(command);
2287 return (TRUE);
2288 } else {
2289 output[0] = '\0';
2290 mr_msg(4, "It's not %s", dev);
2291 mr_free(command);
2292 return (FALSE);
2293 }
2294}
2295
2296
2297/**
2298 * Find out what number CD is in the drive.
2299 * @param bkpinfo The backup information structure. The @c bkpinfo->media_device field is the only one used.
2300 * @return The current CD number, or -1 if it could not be found.
2301 * @note If the CD is not mounted, it will be mounted
2302 * (and remain mounted after this function returns).
2303 */
2304int what_number_cd_is_this()
2305{
2306 int cd_number = -1;
2307 char *mountdev = NULL;
2308 char *tmp = NULL;
2309
2310 assert(bkpinfo != NULL);
2311 if (g_ISO_restore_mode) {
2312 mr_asprintf(&tmp, "mount | grep iso9660 | awk '{print $3;}'");
2313
2314 mr_asprintf(&mountdev, "%s/archives/THIS-CD-NUMBER", call_program_and_get_last_line_of_output(tmp));
2315 cd_number = atoi(last_line_of_file(mountdev));
2316 mr_free(mountdev);
2317 mr_free(tmp);
2318 return (cd_number);
2319 }
2320
2321 mr_asprintf(&mountdev, bkpinfo->media_device);
2322 if (!is_this_device_mounted(MNT_CDROM)) {
2323 if (bkpinfo->backup_media_type == usb) {
2324 mount_USB_here(mountdev, MNT_CDROM);
2325 } else {
2326 mount_CDROM_here(mountdev, MNT_CDROM);
2327 }
2328 }
2329 cd_number =
2330 atoi(last_line_of_file(MNT_CDROM "/archives/THIS-CD-NUMBER"));
2331 mr_free(mountdev);
2332 return (cd_number);
2333}
2334
2335
2336/**
2337 * Find out what device is mounted as root (/).
2338 * @return Root device.
2339 * @note The returned string points to static storage and will be overwritten with every call.
2340 * @bug A bit of a misnomer; it's actually finding out the root device.
2341 * The mountpoint (where it's mounted) will obviously be '/'.
2342 */
2343char *where_is_root_mounted()
2344{
2345 /*@ buffers **************** */
2346 static char tmp[MAX_STR_LEN];
2347
2348
2349#ifdef __FreeBSD__
2350 strcpy(tmp, call_program_and_get_last_line_of_output
2351 ("mount | grep \" on / \" | cut -d' ' -f1"));
2352#else
2353 strcpy(tmp, call_program_and_get_last_line_of_output
2354 ("mount | grep \" on / \" | cut -d' ' -f1 | sed s/[0-9]// | sed s/[0-9]//"));
2355 if (strstr(tmp, "/dev/cciss/")) {
2356 strcpy(tmp, call_program_and_get_last_line_of_output
2357 ("mount | grep \" on / \" | cut -d' ' -f1 | cut -dp -f1"));
2358 }
2359 if (strstr(tmp, "/dev/md")) {
2360 strcpy(tmp,
2361 call_program_and_get_last_line_of_output
2362 ("mount | grep \" on / \" | cut -d' ' -f1"));
2363 }
2364#endif
2365
2366 return (tmp);
2367}
2368
2369
2370/**
2371 * Find out which boot loader is in use.
2372 * @param which_device Device to look for the boot loader on.
2373 * @return 'L' for LILO, 'E'for ELILO, 'G' for GRUB, 'B' or 'D' for FreeBSD boot loaders, or 'U' for Unknown.
2374 * @note Under Linux, all drives are examined, not just @p which_device.
2375 */
2376char which_boot_loader(char *which_device)
2377{
2378#ifdef __FreeBSD__
2379 int count_lilos = 0;
2380 int count_grubs = 0;
2381 int count_boot0s = 0;
2382 int count_dangerouslydedicated = 0;
2383
2384 log_it("looking at drive %s's MBR", which_device);
2385 if (does_string_exist_in_boot_block(which_device, "GRUB")) {
2386 count_grubs++;
2387 }
2388 if (does_string_exist_in_boot_block(which_device, "LILO")) {
2389 count_lilos++;
2390 }
2391 if (does_string_exist_in_boot_block(which_device, "Drive")) {
2392 count_boot0s++;
2393 }
2394 if (does_string_exist_in_first_N_blocks
2395 (which_device, "FreeBSD/i386", 17)) {
2396 count_dangerouslydedicated++;
2397 }
2398 log_it("%d grubs and %d lilos and %d elilos and %d boot0s and %d DD\n",
2399 count_grubs, count_lilos, count_elilos, count_boot0s,
2400 count_dangerouslydedicated);
2401
2402 if (count_grubs && !count_lilos) {
2403 return ('G');
2404 } else if (count_lilos && !count_grubs) {
2405 return ('L');
2406 } else if (count_grubs == 1 && count_lilos == 1) {
2407 log_it("I'll bet you used to use LILO but switched to GRUB...");
2408 return ('G');
2409 } else if (count_boot0s == 1) {
2410 return ('B');
2411 } else if (count_dangerouslydedicated) {
2412 return ('D');
2413 } else {
2414 log_it("Unknown boot loader");
2415 return ('U');
2416 }
2417#else
2418 /*@ buffer ***************************************************** */
2419 char *list_drives_cmd;
2420 char *current_drive = NULL;
2421
2422 /*@ pointers *************************************************** */
2423 FILE *pdrives;
2424
2425 /*@ int ******************************************************** */
2426 int count_lilos = 0;
2427 int count_grubs = 0;
2428 size_t n = 0;
2429
2430 /*@ end vars *************************************************** */
2431
2432#ifdef __IA64__
2433 /* No choice for it */
2434 return ('E');
2435#endif
2436 assert(which_device != NULL);
2437 mr_asprintf(&list_drives_cmd,
2438 "parted2fdisk -l 2>/dev/null | grep \"/dev/.*:\" | tr -s ':' ' ' | tr -s ' ' '\n' | grep /dev/; echo %s",
2439 where_is_root_mounted());
2440 log_it("list_drives_cmd = %s", list_drives_cmd);
2441
2442 if (!(pdrives = popen(list_drives_cmd, "r"))) {
2443 log_OS_error("Unable to open list of drives");
2444 mr_free(list_drives_cmd);
2445 return ('\0');
2446 }
2447 mr_free(list_drives_cmd);
2448
2449 for (mr_getline(&current_drive, &n, pdrives); !feof(pdrives);
2450 mr_getline(&current_drive, &n, pdrives)) {
2451 mr_strip_spaces(current_drive);
2452 log_it("looking at drive %s's MBR", current_drive);
2453 if (does_string_exist_in_boot_block(current_drive, "GRUB")) {
2454 count_grubs++;
2455 strcpy(which_device, current_drive);
2456 break;
2457 }
2458 if (does_string_exist_in_boot_block(current_drive, "LILO")) {
2459 count_lilos++;
2460 strcpy(which_device, current_drive);
2461 break;
2462 }
2463 }
2464 mr_free(current_drive);
2465
2466 if (pclose(pdrives)) {
2467 log_OS_error("Cannot pclose pdrives");
2468 }
2469 log_it("%d grubs and %d lilos\n", count_grubs, count_lilos);
2470 if (count_grubs && !count_lilos) {
2471 return ('G');
2472 } else if (count_lilos && !count_grubs) {
2473 return ('L');
2474 } else if (count_grubs == 1 && count_lilos == 1) {
2475 log_it("I'll bet you used to use LILO but switched to GRUB...");
2476 return ('G');
2477 } else {
2478 // We need to look on each partition then
2479 mr_asprintf(&list_drives_cmd,
2480 "parted2fdisk -l 2>/dev/null | grep -E \"^/dev/\" | tr -s ':' ' ' | tr -s ' ' '\n' | grep /dev/");
2481 log_it("list_drives_cmd = %s", list_drives_cmd);
2482
2483 if (!(pdrives = popen(list_drives_cmd, "r"))) {
2484 log_OS_error("Unable to open list of drives");
2485 mr_free(list_drives_cmd);
2486 return ('\0');
2487 }
2488 mr_free(list_drives_cmd);
2489
2490 for (mr_getline(&current_drive, &n, pdrives); !feof(pdrives);
2491 mr_getline(&current_drive, &n, pdrives)) {
2492 mr_strip_spaces(current_drive);
2493 log_it("looking at partition %s's BR", current_drive);
2494 if (does_string_exist_in_boot_block(current_drive, "GRUB")) {
2495 count_grubs++;
2496 strcpy(which_device, current_drive);
2497 break;
2498 }
2499 if (does_string_exist_in_boot_block(current_drive, "LILO")) {
2500 count_lilos++;
2501 strcpy(which_device, current_drive);
2502 break;
2503 }
2504 }
2505 mr_free(current_drive);
2506
2507 if (pclose(pdrives)) {
2508 log_OS_error("Cannot pclose pdrives");
2509 }
2510 log_it("%d grubs and %d lilos\n", count_grubs, count_lilos);
2511 if (count_grubs && !count_lilos) {
2512 return ('G');
2513 } else if (count_lilos && !count_grubs) {
2514 return ('L');
2515 } else if (count_grubs == 1 && count_lilos == 1) {
2516 log_it("I'll bet you used to use LILO but switched to GRUB...");
2517 return ('G');
2518 } else {
2519 log_it("Unknown boot loader");
2520 return ('U');
2521 }
2522 }
2523#endif
2524}
2525
2526
2527/**
2528 * Write zeroes over the first 16K of @p device.
2529 * @param device The device to zero.
2530 * @return 0 for success, 1 for failure.
2531 */
2532int zero_out_a_device(char *device)
2533{
2534 FILE *fout;
2535 int i;
2536
2537 assert_string_is_neither_NULL_nor_zerolength(device);
2538
2539 log_it("Zeroing drive %s", device);
2540 if (!(fout = fopen(device, "w"))) {
2541 log_OS_error("Unable to open/write to device");
2542 return (1);
2543 }
2544 for (i = 0; i < 16384; i++) {
2545 fputc('\0', fout);
2546 }
2547 paranoid_fclose(fout);
2548 log_it("Device successfully zeroed.");
2549 return (0);
2550}
2551
2552
2553/**
2554 * Return the device pointed to by @p incoming.
2555 * @param incoming The device to resolve symlinks for.
2556 * @return The path to the real device file.
2557 * @note The returned string points to static storage that will be overwritten with each call.
2558 * @bug Won't work with file v4.0; needs to be written in C.
2559 */
2560char *resolve_softlinks_to_get_to_actual_device_file(char *incoming)
2561{
2562 static char output[MAX_STR_LEN];
2563 char *command = NULL;
2564 char *curr_fname = NULL;
2565 char *scratch = NULL;
2566 char *tmp = NULL;
2567 char *p = NULL;
2568
2569 struct stat statbuf;
2570 malloc_string(tmp);
2571 malloc_string(scratch);
2572 malloc_string(curr_fname);
2573 if (!does_file_exist(incoming)) {
2574 log_it
2575 ("resolve_softlinks_to_get_to_actual_device_file --- device not found");
2576 strcpy(output, incoming);
2577 } else {
2578 strcpy(curr_fname, incoming);
2579 lstat(curr_fname, &statbuf);
2580 while (S_ISLNK(statbuf.st_mode)) {
2581 mr_msg(1, "curr_fname = %s", curr_fname);
2582 mr_asprintf(&command, "file %s", curr_fname);
2583 strcpy(tmp, call_program_and_get_last_line_of_output(command));
2584 mr_free(command);
2585
2586 for (p = tmp + strlen(tmp); p != tmp && *p != '`' && *p != ' ';
2587 p--);
2588 p++;
2589 strcpy(scratch, p);
2590 for (p = scratch; *p != '\0' && *p != '\''; p++);
2591 *p = '\0';
2592 mr_msg(0, "curr_fname %s --> '%s' --> %s", curr_fname, tmp,
2593 scratch);
2594 if (scratch[0] == '/') {
2595 strcpy(curr_fname, scratch); // copy whole thing because it's an absolute softlink
2596 } else { // copy over the basename cos it's a relative softlink
2597 p = curr_fname + strlen(curr_fname);
2598 while (p != curr_fname && *p != '/') {
2599 p--;
2600 }
2601 if (*p == '/') {
2602 p++;
2603 }
2604 strcpy(p, scratch);
2605 }
2606 lstat(curr_fname, &statbuf);
2607 }
2608 strcpy(output, curr_fname);
2609 log_it("resolved %s to %s", incoming, output);
2610 }
2611 mr_free(curr_fname);
2612 mr_free(tmp);
2613 return (output);
2614}
2615
2616/* @} - end of deviceGroup */
2617
2618
2619/**
2620 * Return the type of partition format (GPT or MBR)
2621 */
2622char *which_partition_format(const char *drive)
2623{
2624 static char output[4];
2625 char *tmp = NULL;
2626 char *command = NULL;
2627 char *fdisk = NULL;
2628
2629 mr_msg(0, "Looking for partition table format type");
2630 mr_asprintf(&fdisk, "/sbin/parted2fdisk");
2631 mr_msg(1, "Using %s", fdisk);
2632 mr_asprintf(&command, "%s -l %s | grep 'EFI GPT'", fdisk, drive);
2633 mr_free(fdisk);
2634
2635 mr_asprintf(&tmp, call_program_and_get_last_line_of_output(command));
2636 mr_free(command);
2637
2638 if (strstr(tmp, "GPT") == NULL) {
2639 strcpy(output, "MBR");
2640 } else {
2641 strcpy(output, "GPT");
2642 }
2643 mr_free(tmp);
2644 mr_msg(0, "Found %s partition table format type", output);
2645 return(output);
2646}
2647/* @} - end of deviceGroup */
Note: See TracBrowser for help on using the repository browser.