2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Author: James da Silva, Systems Design and Analysis Group
24 * Computer Science Department
25 * University of Maryland at College Park
28 * $Id: amadmin.c,v 1.124 2006/07/26 15:17:37 martinea Exp $
30 * controlling process for the Amanda backup system
43 #include "timestamp.h"
47 int main(int argc, char **argv);
49 void force(int argc, char **argv);
50 void force_one(disk_t *dp);
51 void unforce(int argc, char **argv);
52 void unforce_one(disk_t *dp);
53 void force_bump(int argc, char **argv);
54 void force_bump_one(disk_t *dp);
55 void force_no_bump(int argc, char **argv);
56 void force_no_bump_one(disk_t *dp);
57 void unforce_bump(int argc, char **argv);
58 void unforce_bump_one(disk_t *dp);
59 void reuse(int argc, char **argv);
60 void noreuse(int argc, char **argv);
61 void info(int argc, char **argv);
62 void info_one(disk_t *dp);
63 void due(int argc, char **argv);
64 void due_one(disk_t *dp);
65 void find(int argc, char **argv);
66 void holding(int argc, char **argv);
67 void delete(int argc, char **argv);
68 void delete_one(disk_t *dp);
69 void balance(int argc, char **argv);
70 void tape(int argc, char **argv);
71 void bumpsize(int argc, char **argv);
72 void diskloop(int argc, char **argv, char *cmdname, void (*func)(disk_t *dp));
73 char *seqdatestr(int seq);
74 static int next_level0(disk_t *dp, info_t *info);
75 int bump_thresh(int level);
76 void export_db(int argc, char **argv);
77 void import_db(int argc, char **argv);
78 void disklist(int argc, char **argv);
79 void disklist_one(disk_t *dp);
80 void show_version(int argc, char **argv);
81 static void show_config(int argc, char **argv);
83 static char *conf_tapelist = NULL;
84 static char *displayunit;
85 static long int unitdivisor;
89 void (*fn)(int, char **);
92 { "version", show_version,
93 T_("\t\t\t\t\t# Show version info.") },
94 { "config", show_config,
95 T_("\t\t\t\t\t# Show configuration.") },
97 T_(" [<hostname> [<disks>]* ]+\t\t# Force level 0 at next run.") },
99 T_(" [<hostname> [<disks>]* ]+\t# Clear force command.") },
100 { "force-bump", force_bump,
101 T_(" [<hostname> [<disks>]* ]+\t# Force bump at next run.") },
102 { "force-no-bump", force_no_bump,
103 T_(" [<hostname> [<disks>]* ]+\t# Force no-bump at next run.") },
104 { "unforce-bump", unforce_bump,
105 T_(" [<hostname> [<disks>]* ]+\t# Clear bump command.") },
106 { "disklist", disklist,
107 T_(" [<hostname> [<disks>]* ]*\t# Debug disklist entries.") },
109 T_(" <tapelabel> ...\t\t # re-use this tape.") },
110 { "no-reuse", noreuse,
111 T_(" <tapelabel> ...\t # never re-use this tape.") },
113 T_(" [<hostname> [<disks>]* ]*\t # Show which tapes these dumps are on.") },
114 { "holding", holding,
115 T_(" {list [ -l ] |delete} [ <hostname> [ <disk> [ <datestamp> [ .. ] ] ] ]+\t # Show or delete holding disk contents.") },
117 T_(" [<hostname> [<disks>]* ]+ # Delete from database.") },
119 T_(" [<hostname> [<disks>]* ]*\t # Show current info records.") },
121 T_(" [<hostname> [<disks>]* ]*\t # Show due date.") },
122 { "balance", balance,
123 T_(" [-days <num>]\t\t # Show nightly dump size balance.") },
125 T_(" [-days <num>]\t\t # Show which tape is due next.") },
126 { "bumpsize", bumpsize,
127 T_("\t\t\t # Show current bump thresholds.") },
128 { "export", export_db,
129 T_(" [<hostname> [<disks>]* ]* # Export curinfo database to stdout.") },
130 { "import", import_db,
131 T_("\t\t\t\t # Import curinfo database from stdin.") },
133 #define NCMDS (int)(sizeof(cmdtab) / sizeof(cmdtab[0]))
143 config_overwrites_t *cfg_ovr = NULL;
146 * Configure program for internationalization:
147 * 1) Only set the message locale for now.
148 * 2) Set textdomain for all amanda related programs to "amanda"
149 * We don't want to be forced to support dozens of message catalogs.
151 setlocale(LC_MESSAGES, "C");
152 textdomain("amanda");
157 set_pname("amadmin");
159 /* Don't die when child closes pipe */
160 signal(SIGPIPE, SIG_IGN);
162 dbopen(DBG_SUBDIR_SERVER);
164 erroutput_type = ERR_INTERACTIVE;
166 cfg_ovr = extract_commandline_config_overwrites(&argc, &argv);
168 if(argc < 3) usage();
170 if(strcmp(argv[2],"version") == 0) {
171 show_version(argc, argv);
175 config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_FATAL, argv[1]);
176 apply_config_overwrites(cfg_ovr);
178 dbrename(config_name, DBG_SUBDIR_SERVER);
180 check_running_as(RUNNING_AS_DUMPUSER);
182 conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
183 if (read_diskfile(conf_diskfile, &diskq) < 0) {
184 error(_("could not load disklist \"%s\""), conf_diskfile);
187 amfree(conf_diskfile);
189 conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
190 if(read_tapelist(conf_tapelist)) {
191 error(_("could not load tapelist \"%s\""), conf_tapelist);
194 /* conf_tapelist is not freed yet -- it may be used to write the
197 conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
198 if(open_infofile(conf_infofile)) {
199 error(_("could not open info db \"%s\""), conf_infofile);
202 amfree(conf_infofile);
204 displayunit = getconf_str(CNF_DISPLAYUNIT);
205 unitdivisor = getconf_unit_divisor();
207 for (i = 0; i < NCMDS; i++)
208 if (strcmp(argv[2], cmdtab[i].name) == 0) {
209 (*cmdtab[i].fn)(argc, argv);
213 g_fprintf(stderr, _("%s: unknown command \"%s\"\n"), argv[0], argv[2]);
219 amfree(conf_tapelist);
223 free_disklist(&diskq);
234 g_fprintf(stderr, _("\nUsage: %s%s <conf> <command> {<args>} [-o configoption]* ...\n"),
235 get_pname(), versionsuffix());
236 g_fprintf(stderr, _(" Valid <command>s are:\n"));
237 for (i = 0; i < NCMDS; i++)
238 g_fprintf(stderr, "\t%s%s\n", cmdtab[i].name, _(cmdtab[i].usage));
243 /* ----------------------------------------------- */
245 #define SECS_PER_DAY (24*60*60)
253 static char *dow[7] = {
262 time_t t = today + seq*SECS_PER_DAY;
268 g_snprintf(str, SIZEOF(str),
269 "%2d/%02d %3s", tm->tm_mon+1, tm->tm_mday, _(dow[tm->tm_wday]));
271 strcpy(str, _("BAD DATE"));
277 #define days_diff(a, b) (int)(((b) - (a) + SECS_PER_DAY) / SECS_PER_DAY)
279 /* when is next level 0 due? 0 = tonight, 1 = tommorrow, etc*/
285 if(dp->strategy == DS_NOFULL)
286 return 1; /* fake it */
287 if(info->inf[0].date < (time_t)0)
288 return 0; /* new disk */
290 return dp->dumpcycle - days_diff(info->inf[0].date, today);
293 /* ----------------------------------------------- */
300 void (*func)(disk_t *dp))
307 g_fprintf(stderr,_("%s: expecting \"%s [<hostname> [<disks>]* ]+\"\n"),
308 get_pname(), cmdname);
312 errstr = match_disklist(&diskq, argc-3, argv+3);
314 g_printf("%s", errstr);
318 for(dp = diskq.head; dp != NULL; dp = dp->next) {
325 g_fprintf(stderr,_("%s: no disk matched\n"),get_pname());
329 /* ----------------------------------------------- */
336 char *hostname = dp->host->hostname;
337 char *diskname = dp->name;
340 get_info(hostname, diskname, &info);
341 SET(info.command, FORCE_FULL);
342 if (ISSET(info.command, FORCE_BUMP)) {
343 CLR(info.command, FORCE_BUMP);
344 g_printf(_("%s: WARNING: %s:%s FORCE_BUMP command was cleared.\n"),
345 get_pname(), hostname, diskname);
347 if(put_info(hostname, diskname, &info) == 0) {
348 if (dp->strategy == DS_INCRONLY) {
349 g_printf(_("%s: %s:%s, full dump done offline, next dump will be at level 1.\n"),
350 get_pname(), hostname, diskname);
352 g_printf(_("%s: %s:%s is set to a forced level 0 at next run.\n"),
353 get_pname(), hostname, diskname);
356 g_fprintf(stderr, _("%s: %s:%s could not be forced.\n"),
357 get_pname(), hostname, diskname);
367 diskloop(argc, argv, "force", force_one);
371 /* ----------------------------------------------- */
378 char *hostname = dp->host->hostname;
379 char *diskname = dp->name;
382 get_info(hostname, diskname, &info);
383 if (ISSET(info.command, FORCE_FULL)) {
384 CLR(info.command, FORCE_FULL);
385 if(put_info(hostname, diskname, &info) == 0){
386 g_printf(_("%s: force command for %s:%s cleared.\n"),
387 get_pname(), hostname, diskname);
390 _("%s: force command for %s:%s could not be cleared.\n"),
391 get_pname(), hostname, diskname);
395 g_printf(_("%s: no force command outstanding for %s:%s, unchanged.\n"),
396 get_pname(), hostname, diskname);
405 diskloop(argc, argv, "unforce", unforce_one);
409 /* ----------------------------------------------- */
416 char *hostname = dp->host->hostname;
417 char *diskname = dp->name;
420 get_info(hostname, diskname, &info);
421 SET(info.command, FORCE_BUMP);
422 if (ISSET(info.command, FORCE_NO_BUMP)) {
423 CLR(info.command, FORCE_NO_BUMP);
424 g_printf(_("%s: WARNING: %s:%s FORCE_NO_BUMP command was cleared.\n"),
425 get_pname(), hostname, diskname);
427 if (ISSET(info.command, FORCE_FULL)) {
428 CLR(info.command, FORCE_FULL);
429 g_printf(_("%s: WARNING: %s:%s FORCE_FULL command was cleared.\n"),
430 get_pname(), hostname, diskname);
432 if(put_info(hostname, diskname, &info) == 0) {
433 g_printf(_("%s: %s:%s is set to bump at next run.\n"),
434 get_pname(), hostname, diskname);
436 g_fprintf(stderr, _("%s: %s:%s could not be forced to bump.\n"),
437 get_pname(), hostname, diskname);
447 diskloop(argc, argv, "force-bump", force_bump_one);
451 /* ----------------------------------------------- */
458 char *hostname = dp->host->hostname;
459 char *diskname = dp->name;
462 get_info(hostname, diskname, &info);
463 SET(info.command, FORCE_NO_BUMP);
464 if (ISSET(info.command, FORCE_BUMP)) {
465 CLR(info.command, FORCE_BUMP);
466 g_printf(_("%s: WARNING: %s:%s FORCE_BUMP command was cleared.\n"),
467 get_pname(), hostname, diskname);
469 if(put_info(hostname, diskname, &info) == 0) {
470 g_printf(_("%s: %s:%s is set to not bump at next run.\n"),
471 get_pname(), hostname, diskname);
473 g_fprintf(stderr, _("%s: %s:%s could not be force to not bump.\n"),
474 get_pname(), hostname, diskname);
484 diskloop(argc, argv, "force-no-bump", force_no_bump_one);
488 /* ----------------------------------------------- */
495 char *hostname = dp->host->hostname;
496 char *diskname = dp->name;
499 get_info(hostname, diskname, &info);
500 if (ISSET(info.command, FORCE_BUMP|FORCE_NO_BUMP)) {
501 CLR(info.command, FORCE_BUMP|FORCE_NO_BUMP);
502 if(put_info(hostname, diskname, &info) == 0) {
503 g_printf(_("%s: bump command for %s:%s cleared.\n"),
504 get_pname(), hostname, diskname);
506 g_fprintf(stderr, _("%s: %s:%s bump command could not be cleared.\n"),
507 get_pname(), hostname, diskname);
511 g_printf(_("%s: no bump command outstanding for %s:%s, unchanged.\n"),
512 get_pname(), hostname, diskname);
522 diskloop(argc, argv, "unforce-bump", unforce_bump_one);
526 /* ----------------------------------------------- */
537 g_fprintf(stderr,_("%s: expecting \"reuse <tapelabel> ...\"\n"),
542 for(count=3; count< argc; count++) {
543 tp = lookup_tapelabel(argv[count]);
545 g_fprintf(stderr, _("reuse: tape label %s not found in tapelist.\n"),
549 if( tp->reuse == 0 ) {
551 g_printf(_("%s: marking tape %s as reusable.\n"),
552 get_pname(), argv[count]);
554 g_fprintf(stderr, _("%s: tape %s already reusable.\n"),
555 get_pname(), argv[count]);
559 if(write_tapelist(conf_tapelist)) {
560 error(_("could not write tapelist \"%s\""), conf_tapelist);
574 g_fprintf(stderr,_("%s: expecting \"no-reuse <tapelabel> ...\"\n"),
579 for(count=3; count< argc; count++) {
580 tp = lookup_tapelabel(argv[count]);
582 g_fprintf(stderr, _("no-reuse: tape label %s not found in tapelist.\n"),
586 if( tp->reuse == 1 ) {
588 g_printf(_("%s: marking tape %s as not reusable.\n"),
589 get_pname(), argv[count]);
591 g_fprintf(stderr, _("%s: tape %s already not reusable.\n"),
592 get_pname(), argv[count]);
596 if(write_tapelist(conf_tapelist)) {
597 error(_("could not write tapelist \"%s\""), conf_tapelist);
603 /* ----------------------------------------------- */
611 char *hostname = dp->host->hostname;
612 char *diskname = dp->name;
615 if(get_info(hostname, diskname, &info)) {
616 g_printf(_("%s: %s:%s NOT currently in database.\n"),
617 get_pname(), hostname, diskname);
622 if(del_info(hostname, diskname)) {
623 error(_("couldn't delete %s:%s from database: %s"),
624 hostname, diskname, strerror(errno));
627 g_printf(_("%s: %s:%s deleted from curinfo database.\n"),
628 get_pname(), hostname, diskname);
638 diskloop(argc, argv, "delete", delete_one);
642 _("%s: NOTE: you'll have to remove these from the disklist yourself.\n"),
646 /* ----------------------------------------------- */
657 get_info(dp->host->hostname, dp->name, &info);
659 g_printf(_("\nCurrent info for %s %s:\n"), dp->host->hostname, dp->name);
660 if (ISSET(info.command, FORCE_FULL))
661 g_printf(_(" (Forcing to level 0 dump at next run)\n"));
662 if (ISSET(info.command, FORCE_BUMP))
663 g_printf(_(" (Forcing bump at next run)\n"));
664 if (ISSET(info.command, FORCE_NO_BUMP))
665 g_printf(_(" (Forcing no-bump at next run)\n"));
666 g_printf(_(" Stats: dump rates (kps), Full: %5.1lf, %5.1lf, %5.1lf\n"),
667 info.full.rate[0], info.full.rate[1], info.full.rate[2]);
668 g_printf(_(" Incremental: %5.1lf, %5.1lf, %5.1lf\n"),
669 info.incr.rate[0], info.incr.rate[1], info.incr.rate[2]);
670 g_printf(_(" compressed size, Full: %5.1lf%%,%5.1lf%%,%5.1lf%%\n"),
671 info.full.comp[0]*100, info.full.comp[1]*100, info.full.comp[2]*100);
672 g_printf(_(" Incremental: %5.1lf%%,%5.1lf%%,%5.1lf%%\n"),
673 info.incr.comp[0]*100, info.incr.comp[1]*100, info.incr.comp[2]*100);
675 g_printf(_(" Dumps: lev datestmp tape file origK compK secs\n"));
676 for(lev = 0, sp = &info.inf[0]; lev < 9; lev++, sp++) {
677 if(sp->date < (time_t)0 && sp->label[0] == '\0') continue;
678 tm = localtime(&sp->date);
680 g_printf(_(" %d %04d%02d%02d %-15s %lld %lld %lld %jd\n"),
681 lev, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
683 (long long)sp->filenum,
685 (long long)sp->csize,
688 g_printf(_(" %d BAD-DATE %-15s %lld %lld %lld %jd\n"),
691 (long long)sp->filenum,
693 (long long)sp->csize,
708 diskloop(argc, argv, "info", info_one);
710 for(dp = diskq.head; dp != NULL; dp = dp->next)
714 /* ----------------------------------------------- */
725 if(get_info(hp->hostname, dp->name, &info)) {
726 g_printf(_("new disk %s:%s ignored.\n"), hp->hostname, dp->name);
729 days = next_level0(dp, &info);
731 g_printf(_("Overdue %2d day%s %s:%s\n"),
732 -days, (-days == 1) ? ": " : "s:",
733 hp->hostname, dp->name);
736 g_printf(_("Due today: %s:%s\n"), hp->hostname, dp->name);
739 g_printf(_("Due in %2d day%s %s:%s\n"), days,
740 (days == 1) ? ": " : "s:",
741 hp->hostname, dp->name);
755 diskloop(argc, argv, "due", due_one);
757 for(dp = diskq.head; dp != NULL; dp = dp->next)
761 /* ----------------------------------------------- */
772 if(argc > 4 && strcmp(argv[3],"--days") == 0) {
773 nb_days = atoi(argv[4]);
775 g_printf(_("days must be an integer bigger than 0\n"));
782 runtapes = getconf_int(CNF_RUNTAPES);
783 tp = lookup_last_reusable_tape(0);
785 for ( j=0 ; j < nb_days ; j++ ) {
786 for ( i=0 ; i < runtapes ; i++ ) {
788 g_printf(_("The next Amanda run should go onto "));
792 g_printf(_("tape %s or a new tape.\n"), tp->label);
794 if (runtapes - i == 1)
795 g_printf(_("1 new tape.\n"));
797 g_printf(_("%d new tapes.\n"), runtapes - i);
801 tp = lookup_last_reusable_tape(i + 1);
804 lasttp = lookup_tapepos(lookup_nb_tape());
806 if(lasttp && i > 0 && strcmp(lasttp->datestamp,"0") == 0) {
808 while(lasttp && i > 0 && strcmp(lasttp->datestamp,"0") == 0) {
810 lasttp = lasttp->prev;
813 lasttp = lookup_tapepos(lookup_nb_tape());
816 g_printf(_("The next new tape already labelled is: %s.\n"),
820 g_printf(_("The next %d new tapes already labelled are: %s"), c,
822 lasttp = lasttp->prev;
824 while(lasttp && c > 0 && strcmp(lasttp->datestamp,"0") == 0) {
825 g_printf(", %s", lasttp->label);
826 lasttp = lasttp->prev;
834 /* ----------------------------------------------- */
842 struct balance_stats {
844 off_t origsize, outsize;
846 int conf_runspercycle, conf_dumpcycle;
847 int seq, runs_per_cycle, overdue, max_overdue;
848 int later, total, balance, distinct;
849 double fseq, disk_dumpcycle;
851 off_t total_balanced, balanced;
855 conf_dumpcycle = getconf_int(CNF_DUMPCYCLE);
856 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
857 later = conf_dumpcycle;
861 if(argc > 4 && strcmp(argv[3],"--days") == 0) {
862 later = atoi(argv[4]);
863 if(later < 0) later = conf_dumpcycle;
865 if(later > 10000) later = 10000;
867 if(conf_runspercycle == 0) {
868 runs_per_cycle = conf_dumpcycle;
869 } else if(conf_runspercycle == -1 ) {
870 runs_per_cycle = guess_runs_from_tapelist();
872 runs_per_cycle = conf_runspercycle;
874 if (runs_per_cycle <= 0) {
880 distinct = later + 3;
882 sp = (struct balance_stats *)
883 alloc(SIZEOF(struct balance_stats) * (distinct+1));
885 for(seq=0; seq <= distinct; seq++) {
887 sp[seq].origsize = sp[seq].outsize = (off_t)0;
890 for(dp = diskq.head; dp != NULL; dp = dp->next) {
891 if(get_info(dp->host->hostname, dp->name, &info)) {
892 g_printf(_("new disk %s:%s ignored.\n"), dp->host->hostname, dp->name);
895 if (dp->strategy == DS_NOFULL) {
898 sp[distinct].disks++;
899 sp[distinct].origsize += info.inf[0].size/(off_t)unitdivisor;
900 sp[distinct].outsize += info.inf[0].csize/(off_t)unitdivisor;
903 if(dp->dumpcycle == 0) {
904 sp[balance].origsize += (info.inf[0].size/(off_t)unitdivisor) * (off_t)runs_per_cycle;
905 sp[balance].outsize += (info.inf[0].csize/(off_t)unitdivisor) * (off_t)runs_per_cycle;
908 sp[balance].origsize += (info.inf[0].size/(off_t)unitdivisor) *
909 (off_t)(conf_dumpcycle / dp->dumpcycle);
910 sp[balance].outsize += (info.inf[0].csize/(off_t)unitdivisor) *
911 (off_t)(conf_dumpcycle / dp->dumpcycle);
914 disk_dumpcycle = (double)dp->dumpcycle;
915 if(dp->dumpcycle <= 0)
916 disk_dumpcycle = ((double)conf_dumpcycle) / ((double)runs_per_cycle);
918 seq = next_level0(dp, &info);
923 if (-seq > max_overdue)
933 sp[seq].origsize += info.inf[0].size/(off_t)unitdivisor;
934 sp[seq].outsize += info.inf[0].csize/(off_t)unitdivisor;
938 sp[total].origsize += info.inf[0].size/(off_t)unitdivisor;
939 sp[total].outsize += info.inf[0].csize/(off_t)unitdivisor;
942 /* See, if there's another run in this dumpcycle */
943 fseq += disk_dumpcycle;
945 } while (seq < later);
948 if(sp[total].outsize == (off_t)0 && sp[later].outsize == (off_t)0) {
949 g_printf(_("\nNo data to report on yet.\n"));
954 balanced = sp[balance].outsize / (off_t)runs_per_cycle;
955 if(conf_dumpcycle == later) {
956 total_balanced = sp[total].outsize / (off_t)runs_per_cycle;
959 total_balanced = (((sp[total].outsize/(off_t)1024) * (off_t)conf_dumpcycle)
960 / (off_t)(runs_per_cycle * later)) * (off_t)1024;
964 g_printf(_("\n due-date #fs orig %cB out %cB balance\n"),
965 displayunit[0], displayunit[0]);
966 g_printf("----------------------------------------------\n");
967 for(seq = 0; seq < later; seq++) {
968 if(sp[seq].disks == 0 &&
969 ((seq > 0 && sp[seq-1].disks == 0) ||
970 ((seq < later-1) && sp[seq+1].disks == 0))) {
978 g_printf(_("%-9.9s %3d %10lld %10lld "),
979 seqdatestr(seq), sp[seq].disks,
980 (long long)sp[seq].origsize,
981 (long long)sp[seq].outsize);
982 if(!sp[seq].outsize) g_printf(" --- \n");
983 else g_printf(_("%+8.1lf%%\n"),
984 (((double)sp[seq].outsize - (double)balanced) * 100.0 /
989 if(sp[later].disks != 0) {
990 g_printf(_("later %3d %10lld %10lld "),
992 (long long)sp[later].origsize,
993 (long long)sp[later].outsize);
994 if(!sp[later].outsize) g_printf(" --- \n");
995 else g_printf(_("%+8.1lf%%\n"),
996 (((double)sp[later].outsize - (double)balanced) * 100.0 /
999 g_printf("----------------------------------------------\n");
1000 g_printf(_("TOTAL %3d %10lld %10lld %9lld\n"),
1002 (long long)sp[total].origsize,
1003 (long long)sp[total].outsize,
1004 (long long)total_balanced);
1005 if (sp[balance].origsize != sp[total].origsize ||
1006 sp[balance].outsize != sp[total].outsize ||
1007 balanced != total_balanced) {
1008 g_printf(_("BALANCED %10lld %10lld %9lld\n"),
1009 (long long)sp[balance].origsize,
1010 (long long)sp[balance].outsize,
1011 (long long)balanced);
1013 if (sp[distinct].disks != sp[total].disks) {
1014 g_printf(_("DISTINCT %3d %10lld %10lld\n"),
1016 (long long)sp[distinct].origsize,
1017 (long long)sp[distinct].outsize);
1019 g_printf(plural(_(" (estimated %d run per dumpcycle)\n"),
1020 _(" (estimated %d runs per dumpcycle)\n"),
1024 g_printf(plural(_(" (%d filesystem overdue."),
1025 _(" (%d filesystems overdue."), overdue),
1027 g_printf(plural(_(" The most being overdue %d day.)\n"),
1028 _(" The most being overdue %d days.)\n"), max_overdue),
1035 /* ----------------------------------------------- */
1043 char *sort_order = NULL;
1044 find_result_t *output_find;
1049 _("%s: expecting \"find [--sort <hkdlpbf>] [hostname [<disk>]]*\"\n"),
1055 sort_order = newstralloc(sort_order, DEFAULT_SORT_ORDER);
1056 if(argc > 4 && strcmp(argv[3],"--sort") == 0) {
1057 size_t i, valid_sort=1;
1059 for(i = strlen(argv[4]); i > 0; i--) {
1060 switch (argv[4][i - 1]) {
1076 default: valid_sort=0;
1080 sort_order = newstralloc(sort_order, argv[4]);
1082 g_printf(_("Invalid sort order: %s\n"), argv[4]);
1083 g_printf(_("Use default sort order: %s\n"), sort_order);
1089 errstr = match_disklist(&diskq, argc-(start_argc-1), argv+(start_argc-1));
1091 g_printf("%s", errstr);
1095 output_find = find_dump(&diskq);
1096 if(argc-(start_argc-1) > 0) {
1097 free_find_result(&output_find);
1098 errstr = match_disklist(&diskq, argc-(start_argc-1),
1099 argv+(start_argc-1));
1101 g_printf("%s", errstr);
1104 output_find = find_dump(NULL);
1107 sort_find_result(sort_order, &output_find);
1108 print_find_result(output_find);
1109 free_find_result(&output_find);
1115 /* ------------------------ */
1123 GSList * file_list = NULL;
1127 flags = CMDLINE_PARSE_DATESTAMP;
1128 if (allow_empty) flags |= CMDLINE_EMPTY_TO_WILDCARD;
1129 dumplist = cmdline_parse_dumpspecs(argc, argv, flags);
1131 file_list = cmdline_match_holding(dumplist);
1132 dumpspec_list_free(dumplist);
1137 /* Given a file header, find the history element in curinfo most likely
1138 * corresponding to that dump (this is not an exact science).
1140 * @param info: the info_t element for this DLE
1141 * @param file: the header of the file
1142 * @returns: index of the matching history element, or -1 if not found
1145 holding_file_find_history(
1149 int matching_hist_idx = -1;
1153 /* Begin by trying to find the history element matching this dump.
1154 * The datestamp on the dump is for the entire run of amdump, while the
1155 * 'date' in the history element of 'info' is the time the dump itself
1156 * began. A matching history element, then, is the earliest element
1157 * with a 'date' equal to or later than the date of the dumpfile.
1159 * We compare using formatted datestamps; even using seconds since epoch,
1160 * we would still face timezone issues, and have to do a reverse (timezone
1161 * to gmt) translation.
1164 /* get to the end of the history list and search backward */
1165 for (nhist = 0; info->history[nhist].level > -1; nhist++) /* empty loop */;
1166 for (i = nhist-1; i > -1; i--) {
1167 char *info_datestamp = get_timestamp_from_time(info->history[i].date);
1168 int order = strcmp(file->datestamp, info_datestamp);
1169 amfree(info_datestamp);
1172 /* only a match if the levels are equal */
1173 if (info->history[i].level == file->dumplevel) {
1174 matching_hist_idx = i;
1180 return matching_hist_idx;
1183 /* A holding file is 'outdated' if a subsequent dump of the same DLE was made
1184 * at the same level or a lower leve; for example, a level 2 dump is outdated if
1185 * there is a subsequent level 2, or a subsequent level 0.
1187 * @param file: the header of the file
1188 * @returns: true if the file is outdated
1191 holding_file_is_outdated(
1195 int matching_hist_idx;
1197 if (get_info(file->name, file->disk, &info) == -1) {
1198 return 0; /* assume it's not outdated */
1201 /* if the last level is less than the level of this dump, then
1203 if (info.last_level < file->dumplevel)
1206 /* otherwise, we need to see if this dump is the last at its level */
1207 matching_hist_idx = holding_file_find_history(&info, file);
1208 if (matching_hist_idx == -1) {
1209 return 0; /* assume it's not outdated */
1212 /* compare the date of the history element with the most recent date
1213 * for this level. If they match, then this is the last dump at this
1214 * level, and we checked above for more recent lower-level dumps, so
1215 * the dump is not outdated. */
1216 if (info.history[matching_hist_idx].date ==
1217 info.inf[info.history[matching_hist_idx].level].date) {
1225 remove_holding_file_from_catalog(
1228 static int warnings_printed; /* only print once per invocation */
1231 int matching_hist_idx = -1;
1232 history_t matching_hist; /* will be a copy */
1235 if (!holding_file_get_dumpfile(filename, &file)) {
1236 g_printf(_("Could not read holding file %s\n"), filename);
1240 if (get_info(file.name, file.disk, &info) == -1) {
1241 g_printf(_("WARNING: No curinfo record for %s:%s\n"), file.name, file.disk);
1242 return 1; /* not an error */
1245 matching_hist_idx = holding_file_find_history(&info, &file);
1247 if (matching_hist_idx == -1) {
1248 g_printf(_("WARNING: No dump matching %s found in curinfo.\n"), filename);
1249 return 1; /* not an error */
1253 matching_hist = info.history[matching_hist_idx];
1255 /* Remove the history element itself before doing the stats */
1256 for (i = matching_hist_idx; i <= NB_HISTORY; i++) {
1257 info.history[i] = info.history[i+1];
1259 info.history[NB_HISTORY].level = -1;
1261 /* Remove stats for that history element, if necessary. Doing so
1262 * will result in an inconsistent set of backups, so we warn the
1263 * user and adjust last_level to make the next dump get us a
1264 * consistent picture. */
1265 if (matching_hist.date == info.inf[matching_hist.level].date) {
1266 /* search for an earlier dump at this level */
1267 for (i = matching_hist_idx; info.history[i].level > -1; i++) {
1268 if (info.history[i].level == matching_hist.level)
1272 if (info.history[i].level < 0) {
1273 /* not found => zero it out */
1274 info.inf[matching_hist.level].date = (time_t)-1; /* flag as not set */
1275 info.inf[matching_hist.level].label[0] = '\0';
1277 /* found => reconstruct stats as best we can */
1278 info.inf[matching_hist.level].size = info.history[i].size;
1279 info.inf[matching_hist.level].csize = info.history[i].csize;
1280 info.inf[matching_hist.level].secs = info.history[i].secs;
1281 info.inf[matching_hist.level].date = info.history[i].date;
1282 info.inf[matching_hist.level].filenum = 0; /* we don't know */
1283 info.inf[matching_hist.level].label[0] = '\0'; /* we don't know */
1286 /* set last_level to the level we just deleted, and set command
1287 * appropriately to make sure planner does a new dump at this level
1289 info.last_level = matching_hist.level;
1290 if (info.last_level == 0) {
1291 g_printf(_("WARNING: Deleting the most recent full dump; forcing a full dump at next run.\n"));
1292 SET(info.command, FORCE_FULL);
1294 g_printf(_("WARNING: Deleting the most recent level %d dump; forcing a level %d dump or \nWARNING: lower at next run.\n"),
1295 info.last_level, info.last_level);
1296 SET(info.command, FORCE_NO_BUMP);
1299 /* Search for and display any subsequent runs that depended on this one */
1300 warnings_printed = 0;
1301 for (i = matching_hist_idx-1; i >= 0; i--) {
1303 if (info.history[i].level <= matching_hist.level) break;
1305 datestamp = get_timestamp_from_time(info.history[i].date);
1306 g_printf(_("WARNING: Level %d dump made %s can no longer be accurately restored.\n"),
1307 info.history[i].level, datestamp);
1310 warnings_printed = 1;
1312 if (warnings_printed)
1313 g_printf(_("WARNING: (note, dates shown above are for dumps, and may be later than the\nWARNING: corresponding run date)\n"));
1316 /* recalculate consecutive_runs based on the history: find the first run
1317 * at this level, and then count the consecutive runs at that level. This
1318 * number may be zero (if we just deleted the last run at this level) */
1319 info.consecutive_runs = 0;
1320 for (i = 0; info.history[i].level >= 0; i++) {
1321 if (info.history[i].level == info.last_level) break;
1323 while (info.history[i+info.consecutive_runs].level == info.last_level)
1324 info.consecutive_runs++;
1326 /* this function doesn't touch the performance stats */
1328 /* write out the changes */
1329 if (put_info(file.name, file.disk, &info) == -1) {
1330 g_printf(_("Could not write curinfo record for %s:%s\n"), file.name, file.disk);
1344 enum { HOLDING_USAGE, HOLDING_LIST, HOLDING_DELETE } action = HOLDING_USAGE;
1346 int outdated_list = 0;
1350 action = HOLDING_USAGE;
1351 else if (strcmp(argv[3], "list") == 0 && argc >= 4)
1352 action = HOLDING_LIST;
1353 else if (strcmp(argv[3], "delete") == 0 && argc > 4)
1354 action = HOLDING_DELETE;
1359 _("%s: expecting \"holding list [-l] [-d]\" or \"holding delete <host> [ .. ]\"\n"),
1365 argc -= 4; argv += 4;
1366 while (argc && argv[0][0] == '-') {
1367 switch (argv[0][1]) {
1371 case 'd': /* have to use '-d', and not '-o', because of parse_config */
1375 g_fprintf(stderr, _("Unknown option -%c\n"), argv[0][1]);
1384 g_printf("%-10s %-2s %-4s %s\n",
1385 _("size (kB)"), _("lv"), _("outd"), _("dump specification"));
1388 file_list = get_file_list(argc, argv, 1);
1389 for (li = file_list; li != NULL; li = li->next) {
1393 if (!holding_file_get_dumpfile((char *)li->data, &file)) {
1394 g_fprintf(stderr, _("Error reading %s\n"), (char *)li->data);
1398 is_outdated = holding_file_is_outdated(&file);
1400 dumpstr = cmdline_format_dumpspec_components(file.name, file.disk, file.datestamp, NULL);
1401 /* only print this entry if we're printing everything, or if it's outdated and
1402 * we're only printing outdated files (-o) */
1403 if (!outdated_list || is_outdated) {
1405 g_printf("%-10lld %-2d %-4s %s\n",
1406 (long long)holding_file_size((char *)li->data, 0),
1408 is_outdated? " *":"",
1411 g_printf("%s\n", dumpstr);
1416 g_slist_free_full(file_list);
1419 case HOLDING_DELETE:
1420 argc -= 4; argv += 4;
1422 file_list = get_file_list(argc, argv, 0);
1423 for (li = file_list; li != NULL; li = li->next) {
1424 g_fprintf(stderr, _("Deleting '%s'\n"), (char *)li->data);
1425 /* remove it from the catalog */
1426 if (!remove_holding_file_from_catalog((char *)li->data))
1430 if (!holding_file_unlink((char *)li->data)) {
1431 error(_("Could not delete '%s'"), (char *)li->data);
1434 g_slist_free_full(file_list);
1440 /* ------------------------ */
1443 /* shared code with planner.c */
1449 int bump = getconf_int(CNF_BUMPSIZE);
1450 double mult = getconf_real(CNF_BUMPMULT);
1453 bump = (int)((double)bump * mult);
1463 int conf_bumppercent = getconf_int(CNF_BUMPPERCENT);
1464 double conf_bumpmult = getconf_real(CNF_BUMPMULT);
1466 (void)argc; /* Quiet unused parameter warning */
1467 (void)argv; /* Quiet unused parameter warning */
1469 g_printf(_("Current bump parameters:\n"));
1470 if(conf_bumppercent == 0) {
1471 g_printf(_(" bumpsize %5d KB\t- minimum savings (threshold) to bump level 1 -> 2\n"),
1472 getconf_int(CNF_BUMPSIZE));
1473 g_printf(_(" bumpdays %5d\t- minimum days at each level\n"),
1474 getconf_int(CNF_BUMPDAYS));
1475 g_printf(_(" bumpmult %5.5lg\t- threshold = bumpsize * bumpmult**(level-1)\n\n"),
1478 g_printf(_(" Bump -> To Threshold\n"));
1479 for(l = 1; l < 9; l++)
1480 g_printf(_("\t%d -> %d %9d KB\n"), l, l+1, bump_thresh(l));
1484 double bumppercent = (double)conf_bumppercent;
1486 g_printf(_(" bumppercent %3d %%\t- minimum savings (threshold) to bump level 1 -> 2\n"),
1488 g_printf(_(" bumpdays %5d\t- minimum days at each level\n"),
1489 getconf_int(CNF_BUMPDAYS));
1490 g_printf(_(" bumpmult %5.5lg\t- threshold = disk_size * bumppercent * bumpmult**(level-1)\n\n"),
1492 g_printf(_(" Bump -> To Threshold\n"));
1493 for(l = 1; l < 9; l++) {
1494 g_printf(_("\t%d -> %d %7.2lf %%\n"), l, l+1, bumppercent);
1495 bumppercent *= conf_bumpmult;
1496 if(bumppercent >= 100.000) { bumppercent = 100.0;}
1502 /* ----------------------------------------------- */
1504 void export_one(disk_t *dp);
1513 char hostname[MAX_HOSTNAME_LENGTH+1];
1516 g_printf(_("CURINFO Version %s CONF %s\n"), version(), getconf_str(CNF_ORG));
1519 if(gethostname(hostname, SIZEOF(hostname)-1) == -1) {
1520 error(_("could not determine host name: %s\n"), strerror(errno));
1523 hostname[SIZEOF(hostname)-1] = '\0';
1524 g_printf(_("# Generated by:\n# host: %s\n# date: %s"),
1525 hostname, ctime(&curtime));
1527 g_printf(_("# command:"));
1528 for(i = 0; i < argc; i++)
1529 g_printf(_(" %s"), argv[i]);
1531 g_printf(_("\n# This file can be merged back in with \"amadmin import\".\n"));
1532 g_printf(_("# Edit only with care.\n"));
1535 diskloop(argc, argv, "export", export_one);
1536 else for(dp = diskq.head; dp != NULL; dp = dp->next)
1547 if(get_info(dp->host->hostname, dp->name, &info)) {
1548 g_fprintf(stderr, _("Warning: no curinfo record for %s:%s\n"),
1549 dp->host->hostname, dp->name);
1552 g_printf(_("host: %s\ndisk: %s\n"), dp->host->hostname, dp->name);
1553 g_printf(_("command: %u\n"), info.command);
1554 g_printf(_("last_level: %d\n"),info.last_level);
1555 g_printf(_("consecutive_runs: %d\n"),info.consecutive_runs);
1556 g_printf(_("full-rate:"));
1557 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.full.rate[i]);
1558 g_printf(_("\nfull-comp:"));
1559 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.full.comp[i]);
1561 g_printf(_("\nincr-rate:"));
1562 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.incr.rate[i]);
1563 g_printf(_("\nincr-comp:"));
1564 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.incr.comp[i]);
1566 for(l=0;l<DUMP_LEVELS;l++) {
1567 if(info.inf[l].date < (time_t)0 && info.inf[l].label[0] == '\0') continue;
1568 g_printf(_("stats: %d %lld %lld %jd %jd %lld %s\n"), l,
1569 (long long)info.inf[l].size,
1570 (long long)info.inf[l].csize,
1571 (intmax_t)info.inf[l].secs,
1572 (intmax_t)info.inf[l].date,
1573 (long long)info.inf[l].filenum,
1576 for(l=0;info.history[l].level > -1;l++) {
1577 g_printf(_("history: %d %lld %lld %jd\n"),
1578 info.history[l].level,
1579 (long long)info.history[l].size,
1580 (long long)info.history[l].csize,
1581 (intmax_t)info.history[l].date);
1586 /* ----------------------------------------------- */
1588 int import_one(void);
1589 char *impget_line(void);
1607 (void)argc; /* Quiet unused parameter warning */
1608 (void)argv; /* Quiet unused parameter warning */
1610 /* process header line */
1612 if((line = agets(stdin)) == NULL) {
1613 g_fprintf(stderr, _("%s: empty input.\n"), get_pname());
1621 if(strncmp_const_skip(s - 1, "CURINFO Version", s, ch) != 0) {
1625 skip_whitespace(s, ch);
1627 || sscanf(s - 1, "%d.%d.%d", &vers_maj, &vers_min, &vers_patch) != 3) {
1631 skip_integer(s, ch); /* skip over major */
1636 skip_integer(s, ch); /* skip over minor */
1641 skip_integer(s, ch); /* skip over patch */
1647 skip_non_whitespace(s, ch);
1651 skip_whitespace(s, ch); /* find the org keyword */
1652 if(ch == '\0' || strncmp_const_skip(s - 1, "CONF", s, ch) != 0) {
1658 skip_whitespace(s, ch); /* find the org string */
1665 newer = (vers_maj != VERSION_MAJOR)? vers_maj > VERSION_MAJOR :
1666 (vers_min != VERSION_MINOR)? vers_min > VERSION_MINOR :
1667 vers_patch > VERSION_PATCH;
1670 _("%s: WARNING: input is from newer Amanda version: %d.%d.%d.\n"),
1671 get_pname(), vers_maj, vers_min, vers_patch);
1674 if(strcmp(org, getconf_str(CNF_ORG)) != 0) {
1675 g_fprintf(stderr, _("%s: WARNING: input is from different org: %s\n"),
1688 /*@i@*/ amfree(line);
1689 g_fprintf(stderr, _("%s: bad CURINFO header line in input: %s.\n"),
1691 g_fprintf(stderr, _(" Was the input in \"amadmin export\" format?\n"));
1706 char *hostname = NULL;
1707 char *diskname = NULL;
1708 long long off_t_tmp;
1709 long long time_t_tmp;
1711 memset(&info, 0, SIZEOF(info_t));
1713 for(level = 0; level < DUMP_LEVELS; level++) {
1714 info.inf[level].date = (time_t)-1;
1717 /* get host: disk: command: lines */
1719 hostname = diskname = NULL;
1721 if((line = impget_line()) == NULL) return 0; /* nothing there */
1725 skip_whitespace(s, ch);
1726 if(ch == '\0' || strncmp_const_skip(s - 1, "host:", s, ch) != 0) goto parse_err;
1727 skip_whitespace(s, ch);
1728 if(ch == '\0') goto parse_err;
1730 skip_non_whitespace(s, ch);
1732 hostname = stralloc(fp);
1735 skip_whitespace(s, ch);
1738 if((line = impget_line()) == NULL) goto shortfile_err;
1741 skip_whitespace(s, ch);
1743 if(strncmp_const_skip(s - 1, "disk:", s, ch) != 0) goto parse_err;
1744 skip_whitespace(s, ch);
1745 if(ch == '\0') goto parse_err;
1747 skip_non_whitespace(s, ch);
1749 diskname = stralloc(fp);
1753 if((line = impget_line()) == NULL) goto shortfile_err;
1754 if(sscanf(line, "command: %u", &info.command) != 1) goto parse_err;
1756 /* get last_level and consecutive_runs */
1759 if((line = impget_line()) == NULL) goto shortfile_err;
1760 rc = sscanf(line, "last_level: %d", &info.last_level);
1763 if((line = impget_line()) == NULL) goto shortfile_err;
1764 if(sscanf(line, "consecutive_runs: %d", &info.consecutive_runs) != 1) goto parse_err;
1766 if((line = impget_line()) == NULL) goto shortfile_err;
1769 /* get rate: and comp: lines for full dumps */
1771 rc = sscanf(line, "full-rate: %lf %lf %lf",
1772 &info.full.rate[0], &info.full.rate[1], &info.full.rate[2]);
1773 if(rc != 3) goto parse_err;
1776 if((line = impget_line()) == NULL) goto shortfile_err;
1777 rc = sscanf(line, "full-comp: %lf %lf %lf",
1778 &info.full.comp[0], &info.full.comp[1], &info.full.comp[2]);
1779 if(rc != 3) goto parse_err;
1781 /* get rate: and comp: lines for incr dumps */
1784 if((line = impget_line()) == NULL) goto shortfile_err;
1785 rc = sscanf(line, "incr-rate: %lf %lf %lf",
1786 &info.incr.rate[0], &info.incr.rate[1], &info.incr.rate[2]);
1787 if(rc != 3) goto parse_err;
1790 if((line = impget_line()) == NULL) goto shortfile_err;
1791 rc = sscanf(line, "incr-comp: %lf %lf %lf",
1792 &info.incr.comp[0], &info.incr.comp[1], &info.incr.comp[2]);
1793 if(rc != 3) goto parse_err;
1795 /* get stats for dump levels */
1799 if((line = impget_line()) == NULL) goto shortfile_err;
1800 if(strncmp_const(line, "//") == 0) {
1804 if(strncmp_const(line, "history:") == 0) {
1808 memset(&onestat, 0, SIZEOF(onestat));
1813 skip_whitespace(s, ch);
1814 if(ch == '\0' || strncmp_const_skip(s - 1, "stats:", s, ch) != 0) {
1818 skip_whitespace(s, ch);
1819 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1822 skip_integer(s, ch);
1824 skip_whitespace(s, ch);
1825 if(ch == '\0' || sscanf(s - 1, "%lld", &off_t_tmp) != 1) {
1828 onestat.size = (off_t)off_t_tmp;
1829 skip_integer(s, ch);
1831 skip_whitespace(s, ch);
1832 if(ch == '\0' || sscanf(s - 1, "%lld", &off_t_tmp) != 1) {
1835 onestat.csize = (off_t)off_t_tmp;
1836 skip_integer(s, ch);
1838 skip_whitespace(s, ch);
1839 if(ch == '\0' || sscanf(s - 1, "%lld", &time_t_tmp) != 1) {
1842 onestat.secs = (time_t)time_t_tmp;
1843 skip_integer(s, ch);
1845 skip_whitespace(s, ch);
1846 if(ch == '\0' || sscanf(s - 1, "%lld", &time_t_tmp) != 1) {
1849 /* time_t not guarranteed to be long */
1850 /*@i1@*/ onestat.date = (time_t)time_t_tmp;
1851 skip_integer(s, ch);
1853 skip_whitespace(s, ch);
1855 if(sscanf(s - 1, "%lld", &off_t_tmp) != 1) {
1858 onestat.filenum = (off_t)off_t_tmp;
1859 skip_integer(s, ch);
1861 skip_whitespace(s, ch);
1863 if (onestat.filenum != 0)
1865 onestat.label[0] = '\0';
1867 strncpy(onestat.label, s - 1, SIZEOF(onestat.label)-1);
1868 onestat.label[SIZEOF(onestat.label)-1] = '\0';
1872 if(level < 0 || level > 9) goto parse_err;
1874 info.inf[level] = onestat;
1877 for(i=0;i<=NB_HISTORY;i++) {
1878 info.history[i].level = -2;
1881 history_t onehistory;
1883 if(line[0] == '/' && line[1] == '/') {
1884 info.history[nb_history].level = -2;
1888 memset(&onehistory, 0, SIZEOF(onehistory));
1891 if(strncmp_const_skip(line, "history:", s, ch) != 0) {
1895 skip_whitespace(s, ch);
1896 if(ch == '\0' || sscanf((s - 1), "%d", &onehistory.level) != 1) {
1899 skip_integer(s, ch);
1901 skip_whitespace(s, ch);
1902 if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
1905 onehistory.size = (off_t)off_t_tmp;
1906 skip_integer(s, ch);
1908 skip_whitespace(s, ch);
1909 if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
1912 onehistory.csize = (off_t)off_t_tmp;
1913 skip_integer(s, ch);
1915 skip_whitespace(s, ch);
1916 if((ch == '\0') || sscanf((s - 1), "%lld", &time_t_tmp) != 1) {
1919 /* time_t not guarranteed to be long */
1920 /*@i1@*/ onehistory.date = (time_t)time_t_tmp;
1921 skip_integer(s, ch);
1923 info.history[nb_history++] = onehistory;
1925 if((line = impget_line()) == NULL) goto shortfile_err;
1927 /*@i@*/ amfree(line);
1929 /* got a full record, now write it out to the database */
1931 if(put_info(hostname, diskname, &info)) {
1932 g_fprintf(stderr, _("%s: error writing record for %s:%s\n"),
1933 get_pname(), hostname, diskname);
1940 /*@i@*/ amfree(line);
1943 g_fprintf(stderr, _("%s: parse error reading import record.\n"), get_pname());
1947 /*@i@*/ amfree(line);
1950 g_fprintf(stderr, _("%s: short file reading import record.\n"), get_pname());
1961 for(; (line = agets(stdin)) != NULL; free(line)) {
1965 skip_whitespace(s, ch);
1967 /* ignore comment lines */
1970 /* found non-blank, return line */
1973 /* otherwise, a blank line, so keep going */
1976 g_fprintf(stderr, _("%s: reading stdin: %s\n"),
1977 get_pname(), strerror(errno));
1982 /* ----------------------------------------------- */
1995 g_printf("line %d:\n", dp->line);
1997 g_printf(" host %s:\n", hp->hostname);
1998 g_printf(" interface %s\n",
1999 interface_name(ip->config)[0] ? interface_name(ip->config) : "default");
2000 g_printf(" disk %s:\n", dp->name);
2001 if(dp->device) g_printf(" device %s\n", dp->device);
2003 g_printf(" program \"%s\"\n", dp->program);
2004 if(dp->exclude_file != NULL && dp->exclude_file->nb_element > 0) {
2005 g_printf(" exclude file");
2006 for(excl = dp->exclude_file->first; excl != NULL; excl = excl->next) {
2007 g_printf(" \"%s\"", excl->name);
2011 if(dp->exclude_list != NULL && dp->exclude_list->nb_element > 0) {
2012 g_printf(" exclude list");
2013 if(dp->exclude_optional) g_printf(" optional");
2014 for(excl = dp->exclude_list->first; excl != NULL; excl = excl->next) {
2015 g_printf(" \"%s\"", excl->name);
2019 if(dp->include_file != NULL && dp->include_file->nb_element > 0) {
2020 g_printf(" include file");
2021 for(excl = dp->include_file->first; excl != NULL; excl = excl->next) {
2022 g_printf(" \"%s\"", excl->name);
2026 if(dp->include_list != NULL && dp->include_list->nb_element > 0) {
2027 g_printf(" include list");
2028 if(dp->include_optional) g_printf(" optional");
2029 for(excl = dp->include_list->first; excl != NULL; excl = excl->next) {
2030 g_printf(" \"%s\"", excl->name);
2034 g_printf(" priority %d\n", dp->priority);
2035 g_printf(" dumpcycle %d\n", dp->dumpcycle);
2036 g_printf(" maxdumps %d\n", dp->maxdumps);
2037 g_printf(" maxpromoteday %d\n", dp->maxpromoteday);
2038 if(dp->bumppercent > 0) {
2039 g_printf(" bumppercent %d\n", dp->bumppercent);
2042 g_printf(" bumpsize %lld\n",
2043 (long long)dp->bumpsize);
2045 g_printf(" bumpdays %d\n", dp->bumpdays);
2046 g_printf(" bumpmult %lf\n", dp->bumpmult);
2048 g_printf(" strategy ");
2049 switch(dp->strategy) {
2054 g_printf("STANDARD\n");
2057 g_printf("NOFULL\n");
2060 g_printf("NOINC\n");
2063 g_printf("HANOI\n");
2066 g_printf("INCRONLY\n");
2069 g_printf(" ignore %s\n", (dp->ignore? "YES" : "NO"));
2070 g_printf(" estimate ");
2071 switch(dp->estimate) {
2073 g_printf("CLIENT\n");
2076 g_printf("SERVER\n");
2079 g_printf("CALCSIZE\n");
2083 g_printf(" compress ");
2084 switch(dp->compress) {
2089 g_printf("CLIENT FAST\n");
2092 g_printf("CLIENT BEST\n");
2094 case COMP_SERVER_FAST:
2095 g_printf("SERVER FAST\n");
2097 case COMP_SERVER_BEST:
2098 g_printf("SERVER BEST\n");
2101 if(dp->compress != COMP_NONE) {
2102 g_printf(" comprate %.2lf %.2lf\n",
2103 dp->comprate[0], dp->comprate[1]);
2106 g_printf(" encrypt ");
2107 switch(dp->encrypt) {
2112 g_printf("CLIENT\n");
2114 case ENCRYPT_SERV_CUST:
2115 g_printf("SERVER\n");
2119 g_printf(" auth %s\n", dp->security_driver);
2120 g_printf(" kencrypt %s\n", (dp->kencrypt? "YES" : "NO"));
2121 g_printf(" amandad_path %s\n", dp->amandad_path);
2122 g_printf(" client_username %s\n", dp->client_username);
2123 g_printf(" ssh_keys %s\n", dp->ssh_keys);
2125 g_printf(" holdingdisk ");
2126 switch(dp->to_holdingdisk) {
2128 g_printf("NEVER\n");
2134 g_printf("REQUIRED\n");
2138 g_printf(" record %s\n", (dp->record? "YES" : "NO"));
2139 g_printf(" index %s\n", (dp->index? "YES" : "NO"));
2140 g_printf(" starttime %04d\n", (int)dp->starttime);
2141 if(dp->tape_splitsize > (off_t)0) {
2142 g_printf(" tape_splitsize %lld\n",
2143 (long long)dp->tape_splitsize);
2145 if(dp->split_diskbuffer) {
2146 g_printf(" split_diskbuffer %s\n", dp->split_diskbuffer);
2148 if(dp->fallback_splitsize > (off_t)0) {
2149 g_printf(" fallback_splitsize %lldMb\n",
2150 (long long)(dp->fallback_splitsize / (off_t)1024));
2152 g_printf(" skip-incr %s\n", (dp->skip_incr? "YES" : "NO"));
2153 g_printf(" skip-full %s\n", (dp->skip_full? "YES" : "NO"));
2154 g_printf(" spindle %d\n", dp->spindle);
2167 diskloop(argc, argv, "disklist", disklist_one);
2169 for(dp = diskq.head; dp != NULL; dp = dp->next)
2180 (void)argc; /* Quiet unused parameter warning */
2181 (void)argv; /* Quiet unused parameter warning */
2183 for(i = 0; version_info[i] != NULL; i++)
2184 g_printf("%s", version_info[i]);
2189 int argc G_GNUC_UNUSED,
2190 char **argv G_GNUC_UNUSED)
2192 dump_configuration();