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"
44 #include "server_util.h"
48 int main(int argc, char **argv);
50 static void estimate(int argc, char **argv);
51 static void estimate_one(disk_t *dp);
52 void force(int argc, char **argv);
53 void force_one(disk_t *dp);
54 void unforce(int argc, char **argv);
55 void unforce_one(disk_t *dp);
56 void force_bump(int argc, char **argv);
57 void force_bump_one(disk_t *dp);
58 void force_no_bump(int argc, char **argv);
59 void force_no_bump_one(disk_t *dp);
60 void unforce_bump(int argc, char **argv);
61 void unforce_bump_one(disk_t *dp);
62 void reuse(int argc, char **argv);
63 void noreuse(int argc, char **argv);
64 void info(int argc, char **argv);
65 void info_one(disk_t *dp);
66 void due(int argc, char **argv);
67 void due_one(disk_t *dp);
68 void find(int argc, char **argv);
69 void holding(int argc, char **argv);
70 void delete(int argc, char **argv);
71 void delete_one(disk_t *dp);
72 void balance(int argc, char **argv);
73 void tape(int argc, char **argv);
74 void bumpsize(int argc, char **argv);
75 void diskloop(int argc, char **argv, char *cmdname, void (*func)(disk_t *dp));
76 char *seqdatestr(int seq);
77 static int next_level0(disk_t *dp, info_t *info);
78 int bump_thresh(int level);
79 void export_db(int argc, char **argv);
80 void import_db(int argc, char **argv);
81 void hosts(int argc, char **argv);
82 void dles(int argc, char **argv);
83 void disklist(int argc, char **argv);
84 void disklist_one(disk_t *dp);
85 void show_version(int argc, char **argv);
86 static void show_config(int argc, char **argv);
88 static char *conf_tapelist = NULL;
89 static char *displayunit;
90 static long int unitdivisor;
94 void (*fn)(int, char **);
97 { "version", show_version,
98 T_("\t\t\t\t\t# Show version info.") },
99 { "config", show_config,
100 T_("\t\t\t\t\t# Show configuration.") },
101 { "estimate", estimate,
102 T_(" [<hostname> [<disks>]* ]*\t# Print server estimate.") },
104 T_(" [<hostname> [<disks>]* ]+\t\t# Force level 0 at next run.") },
105 { "unforce", unforce,
106 T_(" [<hostname> [<disks>]* ]+\t# Clear force command.") },
107 { "force-bump", force_bump,
108 T_(" [<hostname> [<disks>]* ]+\t# Force bump at next run.") },
109 { "force-no-bump", force_no_bump,
110 T_(" [<hostname> [<disks>]* ]+\t# Force no-bump at next run.") },
111 { "unforce-bump", unforce_bump,
112 T_(" [<hostname> [<disks>]* ]+\t# Clear bump command.") },
113 { "disklist", disklist,
114 T_(" [<hostname> [<disks>]* ]*\t# Debug disklist entries.") },
116 T_("\t\t\t\t\t# Show all distinct hosts in disklist.") },
118 T_("\t\t\t\t\t# Show all dles in disklist, one per line.") },
120 T_(" <tapelabel> ...\t\t # re-use this tape.") },
121 { "no-reuse", noreuse,
122 T_(" <tapelabel> ...\t # never re-use this tape.") },
124 T_(" [<hostname> [<disks>]* ]*\t # Show which tapes these dumps are on.") },
125 { "holding", holding,
126 T_(" {list [ -l ] |delete} [ <hostname> [ <disk> [ <datestamp> [ .. ] ] ] ]+\t # Show or delete holding disk contents.") },
128 T_(" [<hostname> [<disks>]* ]+ # Delete from database.") },
130 T_(" [<hostname> [<disks>]* ]*\t # Show current info records.") },
132 T_(" [<hostname> [<disks>]* ]*\t # Show due date.") },
133 { "balance", balance,
134 T_(" [--days <num>]\t\t # Show nightly dump size balance.") },
136 T_(" [--days <num>]\t\t # Show which tape is due next.") },
137 { "bumpsize", bumpsize,
138 T_("\t\t\t # Show current bump thresholds.") },
139 { "export", export_db,
140 T_(" [<hostname> [<disks>]* ]* # Export curinfo database to stdout.") },
141 { "import", import_db,
142 T_("\t\t\t\t # Import curinfo database from stdin.") },
144 #define NCMDS (int)(sizeof(cmdtab) / sizeof(cmdtab[0]))
154 config_overrides_t *cfg_ovr = NULL;
157 * Configure program for internationalization:
158 * 1) Only set the message locale for now.
159 * 2) Set textdomain for all amanda related programs to "amanda"
160 * We don't want to be forced to support dozens of message catalogs.
162 setlocale(LC_MESSAGES, "C");
163 textdomain("amanda");
168 set_pname("amadmin");
170 /* Don't die when child closes pipe */
171 signal(SIGPIPE, SIG_IGN);
173 dbopen(DBG_SUBDIR_SERVER);
175 add_amanda_log_handler(amanda_log_stderr);
177 cfg_ovr = extract_commandline_config_overrides(&argc, &argv);
179 if(argc < 3) usage();
181 set_config_overrides(cfg_ovr);
182 config_init(CONFIG_INIT_EXPLICIT_NAME, argv[1]);
184 if(strcmp(argv[2],"version") == 0) {
185 show_version(argc, argv);
189 conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
190 read_diskfile(conf_diskfile, &diskq);
191 amfree(conf_diskfile);
193 if (config_errors(NULL) >= CFGERR_WARNINGS) {
194 config_print_errors();
195 if (config_errors(NULL) >= CFGERR_ERRORS) {
196 g_critical(_("errors processing config file"));
200 dbrename(get_config_name(), DBG_SUBDIR_SERVER);
202 check_running_as(RUNNING_AS_DUMPUSER);
204 conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
205 if(read_tapelist(conf_tapelist)) {
206 error(_("could not load tapelist \"%s\""), conf_tapelist);
209 /* conf_tapelist is not freed yet -- it may be used to write the
212 conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
213 if(open_infofile(conf_infofile)) {
214 error(_("could not open info db \"%s\""), conf_infofile);
217 amfree(conf_infofile);
219 displayunit = getconf_str(CNF_DISPLAYUNIT);
220 unitdivisor = getconf_unit_divisor();
222 for (i = 0; i < NCMDS; i++)
223 if (strcmp(argv[2], cmdtab[i].name) == 0) {
224 (*cmdtab[i].fn)(argc, argv);
228 g_fprintf(stderr, _("%s: unknown command \"%s\"\n"), argv[0], argv[2]);
234 amfree(conf_tapelist);
238 free_disklist(&diskq);
249 g_fprintf(stderr, _("\nUsage: %s [-o configoption]* <conf> <command> {<args>} ...\n"),
251 g_fprintf(stderr, _(" Valid <command>s are:\n"));
252 for (i = 0; i < NCMDS; i++)
253 g_fprintf(stderr, "\t%s%s\n", cmdtab[i].name, _(cmdtab[i].usage));
258 /* ----------------------------------------------- */
260 #define SECS_PER_DAY (24*60*60)
268 static char *dow[7] = {
277 time_t t = today + seq*SECS_PER_DAY;
283 g_snprintf(str, SIZEOF(str),
284 "%2d/%02d %3s", tm->tm_mon+1, tm->tm_mday, _(dow[tm->tm_wday]));
286 strcpy(str, _("BAD DATE"));
292 #define days_diff(a, b) (int)(((b) - (a) + SECS_PER_DAY) / SECS_PER_DAY)
294 /* when is next level 0 due? 0 = tonight, 1 = tommorrow, etc*/
300 if(dp->strategy == DS_NOFULL)
301 return 1; /* fake it */
302 if(info->inf[0].date < (time_t)0)
303 return 0; /* new disk */
305 return dp->dumpcycle - days_diff(info->inf[0].date, today);
308 /* ----------------------------------------------- */
315 void (*func)(disk_t *dp))
322 g_fprintf(stderr,_("%s: expecting \"%s [<hostname> [<disks>]* ]+\"\n"),
323 get_pname(), cmdname);
327 errstr = match_disklist(&diskq, argc-3, argv+3);
329 g_printf("%s", errstr);
333 for(dp = diskq.head; dp != NULL; dp = dp->next) {
340 g_fprintf(stderr,_("%s: no disk matched\n"),get_pname());
344 /* ----------------------------------------------- */
351 char *hostname = dp->host->hostname;
352 char *diskname = dp->name;
353 char *qhost = quote_string(hostname);
354 char *qdisk = quote_string(diskname);
359 get_info(hostname, diskname, &info);
361 size = internal_server_estimate(dp, &info, 0, &stats);
363 printf("%s %s %d %jd\n", qhost, qdisk, 0, (intmax_t)size);
366 if (info.last_level > 0) {
367 size = internal_server_estimate(dp, &info, info.last_level, &stats);
369 printf("%s %s %d %jd\n", qhost, qdisk, info.last_level,
374 if (info.last_level > -1) {
375 size = internal_server_estimate(dp, &info, info.last_level+1, &stats);
377 printf("%s %s %d %jd\n", qhost, qdisk, info.last_level+1,
395 diskloop(argc, argv, "estimate", estimate_one);
397 for(dp = diskq.head; dp != NULL; dp = dp->next)
402 /* ----------------------------------------------- */
409 char *hostname = dp->host->hostname;
410 char *diskname = dp->name;
413 get_info(hostname, diskname, &info);
414 SET(info.command, FORCE_FULL);
415 if (ISSET(info.command, FORCE_BUMP)) {
416 CLR(info.command, FORCE_BUMP);
417 g_printf(_("%s: WARNING: %s:%s FORCE_BUMP command was cleared.\n"),
418 get_pname(), hostname, diskname);
420 if(put_info(hostname, diskname, &info) == 0) {
421 if (dp->strategy == DS_INCRONLY) {
422 g_printf(_("%s: %s:%s, full dump done offline, next dump will be at level 1.\n"),
423 get_pname(), hostname, diskname);
425 g_printf(_("%s: %s:%s is set to a forced level 0 at next run.\n"),
426 get_pname(), hostname, diskname);
429 g_fprintf(stderr, _("%s: %s:%s could not be forced.\n"),
430 get_pname(), hostname, diskname);
440 diskloop(argc, argv, "force", force_one);
444 /* ----------------------------------------------- */
451 char *hostname = dp->host->hostname;
452 char *diskname = dp->name;
455 get_info(hostname, diskname, &info);
456 if (ISSET(info.command, FORCE_FULL)) {
457 CLR(info.command, FORCE_FULL);
458 if(put_info(hostname, diskname, &info) == 0){
459 g_printf(_("%s: force command for %s:%s cleared.\n"),
460 get_pname(), hostname, diskname);
463 _("%s: force command for %s:%s could not be cleared.\n"),
464 get_pname(), hostname, diskname);
468 g_printf(_("%s: no force command outstanding for %s:%s, unchanged.\n"),
469 get_pname(), hostname, diskname);
478 diskloop(argc, argv, "unforce", unforce_one);
482 /* ----------------------------------------------- */
489 char *hostname = dp->host->hostname;
490 char *diskname = dp->name;
493 get_info(hostname, diskname, &info);
494 SET(info.command, FORCE_BUMP);
495 if (ISSET(info.command, FORCE_NO_BUMP)) {
496 CLR(info.command, FORCE_NO_BUMP);
497 g_printf(_("%s: WARNING: %s:%s FORCE_NO_BUMP command was cleared.\n"),
498 get_pname(), hostname, diskname);
500 if (ISSET(info.command, FORCE_FULL)) {
501 CLR(info.command, FORCE_FULL);
502 g_printf(_("%s: WARNING: %s:%s FORCE_FULL command was cleared.\n"),
503 get_pname(), hostname, diskname);
505 if(put_info(hostname, diskname, &info) == 0) {
506 g_printf(_("%s: %s:%s is set to bump at next run.\n"),
507 get_pname(), hostname, diskname);
509 g_fprintf(stderr, _("%s: %s:%s could not be forced to bump.\n"),
510 get_pname(), hostname, diskname);
520 diskloop(argc, argv, "force-bump", force_bump_one);
524 /* ----------------------------------------------- */
531 char *hostname = dp->host->hostname;
532 char *diskname = dp->name;
535 get_info(hostname, diskname, &info);
536 SET(info.command, FORCE_NO_BUMP);
537 if (ISSET(info.command, FORCE_BUMP)) {
538 CLR(info.command, FORCE_BUMP);
539 g_printf(_("%s: WARNING: %s:%s FORCE_BUMP command was cleared.\n"),
540 get_pname(), hostname, diskname);
542 if(put_info(hostname, diskname, &info) == 0) {
543 g_printf(_("%s: %s:%s is set to not bump at next run.\n"),
544 get_pname(), hostname, diskname);
546 g_fprintf(stderr, _("%s: %s:%s could not be force to not bump.\n"),
547 get_pname(), hostname, diskname);
557 diskloop(argc, argv, "force-no-bump", force_no_bump_one);
561 /* ----------------------------------------------- */
568 char *hostname = dp->host->hostname;
569 char *diskname = dp->name;
572 get_info(hostname, diskname, &info);
573 if (ISSET(info.command, FORCE_BUMP|FORCE_NO_BUMP)) {
574 CLR(info.command, FORCE_BUMP|FORCE_NO_BUMP);
575 if(put_info(hostname, diskname, &info) == 0) {
576 g_printf(_("%s: bump command for %s:%s cleared.\n"),
577 get_pname(), hostname, diskname);
579 g_fprintf(stderr, _("%s: %s:%s bump command could not be cleared.\n"),
580 get_pname(), hostname, diskname);
584 g_printf(_("%s: no bump command outstanding for %s:%s, unchanged.\n"),
585 get_pname(), hostname, diskname);
595 diskloop(argc, argv, "unforce-bump", unforce_bump_one);
599 /* ----------------------------------------------- */
610 g_fprintf(stderr,_("%s: expecting \"reuse <tapelabel> ...\"\n"),
615 for(count=3; count< argc; count++) {
616 tp = lookup_tapelabel(argv[count]);
618 g_fprintf(stderr, _("reuse: tape label %s not found in tapelist.\n"),
622 if( tp->reuse == 0 ) {
624 g_printf(_("%s: marking tape %s as reusable.\n"),
625 get_pname(), argv[count]);
627 g_fprintf(stderr, _("%s: tape %s already reusable.\n"),
628 get_pname(), argv[count]);
632 if(write_tapelist(conf_tapelist)) {
633 error(_("could not write tapelist \"%s\""), conf_tapelist);
647 g_fprintf(stderr,_("%s: expecting \"no-reuse <tapelabel> ...\"\n"),
652 for(count=3; count< argc; count++) {
653 tp = lookup_tapelabel(argv[count]);
655 g_fprintf(stderr, _("no-reuse: tape label %s not found in tapelist.\n"),
659 if( tp->reuse == 1 ) {
661 g_printf(_("%s: marking tape %s as not reusable.\n"),
662 get_pname(), argv[count]);
664 g_fprintf(stderr, _("%s: tape %s already not reusable.\n"),
665 get_pname(), argv[count]);
669 if(write_tapelist(conf_tapelist)) {
670 error(_("could not write tapelist \"%s\""), conf_tapelist);
676 /* ----------------------------------------------- */
684 char *hostname = dp->host->hostname;
685 char *diskname = dp->name;
688 if(get_info(hostname, diskname, &info)) {
689 g_printf(_("%s: %s:%s NOT currently in database.\n"),
690 get_pname(), hostname, diskname);
695 if(del_info(hostname, diskname)) {
696 error(_("couldn't delete %s:%s from database: %s"),
697 hostname, diskname, strerror(errno));
700 g_printf(_("%s: %s:%s deleted from curinfo database.\n"),
701 get_pname(), hostname, diskname);
711 diskloop(argc, argv, "delete", delete_one);
715 _("%s: NOTE: you'll have to remove these from the disklist yourself.\n"),
719 /* ----------------------------------------------- */
730 get_info(dp->host->hostname, dp->name, &info);
732 g_printf(_("\nCurrent info for %s %s:\n"), dp->host->hostname, dp->name);
733 if (ISSET(info.command, FORCE_FULL))
734 g_printf(_(" (Forcing to level 0 dump at next run)\n"));
735 if (ISSET(info.command, FORCE_BUMP))
736 g_printf(_(" (Forcing bump at next run)\n"));
737 if (ISSET(info.command, FORCE_NO_BUMP))
738 g_printf(_(" (Forcing no-bump at next run)\n"));
739 g_printf(_(" Stats: dump rates (kps), Full: %5.1lf, %5.1lf, %5.1lf\n"),
740 info.full.rate[0], info.full.rate[1], info.full.rate[2]);
741 g_printf(_(" Incremental: %5.1lf, %5.1lf, %5.1lf\n"),
742 info.incr.rate[0], info.incr.rate[1], info.incr.rate[2]);
743 g_printf(_(" compressed size, Full: %5.1lf%%,%5.1lf%%,%5.1lf%%\n"),
744 info.full.comp[0]*100, info.full.comp[1]*100, info.full.comp[2]*100);
745 g_printf(_(" Incremental: %5.1lf%%,%5.1lf%%,%5.1lf%%\n"),
746 info.incr.comp[0]*100, info.incr.comp[1]*100, info.incr.comp[2]*100);
748 g_printf(_(" Dumps: lev datestmp tape file origK compK secs\n"));
749 for(lev = 0, sp = &info.inf[0]; lev < 9; lev++, sp++) {
750 if(sp->date < (time_t)0 && sp->label[0] == '\0') continue;
751 tm = localtime(&sp->date);
753 g_printf(_(" %d %04d%02d%02d %-15s %lld %lld %lld %jd\n"),
754 lev, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
756 (long long)sp->filenum,
758 (long long)sp->csize,
761 g_printf(_(" %d BAD-DATE %-15s %lld %lld %lld %jd\n"),
764 (long long)sp->filenum,
766 (long long)sp->csize,
781 diskloop(argc, argv, "info", info_one);
783 for(dp = diskq.head; dp != NULL; dp = dp->next)
787 /* ----------------------------------------------- */
798 if(get_info(hp->hostname, dp->name, &info)) {
799 g_printf(_("new disk %s:%s ignored.\n"), hp->hostname, dp->name);
802 days = next_level0(dp, &info);
804 g_printf(_("Overdue %2d day%s %s:%s\n"),
805 -days, (-days == 1) ? ": " : "s:",
806 hp->hostname, dp->name);
809 g_printf(_("Due today: %s:%s\n"), hp->hostname, dp->name);
812 g_printf(_("Due in %2d day%s %s:%s\n"), days,
813 (days == 1) ? ": " : "s:",
814 hp->hostname, dp->name);
828 diskloop(argc, argv, "due", due_one);
830 for(dp = diskq.head; dp != NULL; dp = dp->next)
834 /* ----------------------------------------------- */
848 if(argc > 4 && strcmp(argv[3],"--days") == 0) {
849 nb_days = atoi(argv[4]);
851 g_printf(_("days must be an integer bigger than 0\n"));
858 runtapes = getconf_int(CNF_RUNTAPES);
859 tp = lookup_last_reusable_tape(0);
862 for ( j=0 ; j < nb_days ; j++ ) {
864 for ( i=0 ; i < runtapes ; i++ ) {
866 g_fprintf(stdout, _("The next Amanda run should go onto "));
868 if (nb_new_tape > 0) {
869 if (nb_new_tape == 1)
870 g_fprintf(stdout, _("1 new tape.\n"));
872 g_fprintf(stdout, _("%d new tapes.\n"), nb_new_tape);
873 g_fprintf(stdout, " ");
876 g_fprintf(stdout, _("tape %s or a new tape.\n"), tp->label);
878 g_fprintf(stdout, " ");
884 tp = lookup_last_reusable_tape(skip);
886 if (nb_new_tape > 0) {
887 if (nb_new_tape == 1)
888 g_fprintf(stdout, _("1 new tape.\n"));
890 g_fprintf(stdout, _("%d new tapes.\n"), nb_new_tape);
894 print_new_tapes(stdout, nb_days * runtapes);
897 /* ----------------------------------------------- */
905 struct balance_stats {
907 off_t origsize, outsize;
909 int conf_runspercycle, conf_dumpcycle;
910 int seq, runs_per_cycle, overdue, max_overdue;
911 int later, total, balance, distinct;
912 double fseq, disk_dumpcycle;
914 off_t total_balanced, balanced;
918 conf_dumpcycle = getconf_int(CNF_DUMPCYCLE);
919 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
920 later = conf_dumpcycle;
924 if(argc > 4 && strcmp(argv[3],"--days") == 0) {
925 later = atoi(argv[4]);
926 if(later < 0) later = conf_dumpcycle;
928 if(later > 10000) later = 10000;
930 if(conf_runspercycle == 0) {
931 runs_per_cycle = conf_dumpcycle;
932 } else if(conf_runspercycle == -1 ) {
933 runs_per_cycle = guess_runs_from_tapelist();
935 runs_per_cycle = conf_runspercycle;
937 if (runs_per_cycle <= 0) {
943 distinct = later + 3;
945 sp = (struct balance_stats *)
946 alloc(SIZEOF(struct balance_stats) * (distinct+1));
948 for(seq=0; seq <= distinct; seq++) {
950 sp[seq].origsize = sp[seq].outsize = (off_t)0;
953 for(dp = diskq.head; dp != NULL; dp = dp->next) {
954 if(get_info(dp->host->hostname, dp->name, &info)) {
955 g_printf(_("new disk %s:%s ignored.\n"), dp->host->hostname, dp->name);
958 if (dp->strategy == DS_NOFULL) {
961 sp[distinct].disks++;
962 sp[distinct].origsize += info.inf[0].size/(off_t)unitdivisor;
963 sp[distinct].outsize += info.inf[0].csize/(off_t)unitdivisor;
966 if(dp->dumpcycle == 0) {
967 sp[balance].origsize += (info.inf[0].size/(off_t)unitdivisor) * (off_t)runs_per_cycle;
968 sp[balance].outsize += (info.inf[0].csize/(off_t)unitdivisor) * (off_t)runs_per_cycle;
971 sp[balance].origsize += (info.inf[0].size/(off_t)unitdivisor) *
972 (off_t)(conf_dumpcycle / dp->dumpcycle);
973 sp[balance].outsize += (info.inf[0].csize/(off_t)unitdivisor) *
974 (off_t)(conf_dumpcycle / dp->dumpcycle);
977 disk_dumpcycle = (double)dp->dumpcycle;
978 if(dp->dumpcycle <= 0)
979 disk_dumpcycle = ((double)conf_dumpcycle) / ((double)runs_per_cycle);
981 seq = next_level0(dp, &info);
986 if (-seq > max_overdue)
996 sp[seq].origsize += info.inf[0].size/(off_t)unitdivisor;
997 sp[seq].outsize += info.inf[0].csize/(off_t)unitdivisor;
1001 sp[total].origsize += info.inf[0].size/(off_t)unitdivisor;
1002 sp[total].outsize += info.inf[0].csize/(off_t)unitdivisor;
1005 /* See, if there's another run in this dumpcycle */
1006 fseq += disk_dumpcycle;
1008 } while (seq < later);
1011 if(sp[total].outsize == (off_t)0 && sp[later].outsize == (off_t)0) {
1012 g_printf(_("\nNo data to report on yet.\n"));
1017 balanced = sp[balance].outsize / (off_t)runs_per_cycle;
1018 if(conf_dumpcycle == later) {
1019 total_balanced = sp[total].outsize / (off_t)runs_per_cycle;
1022 total_balanced = (((sp[total].outsize/(off_t)1024) * (off_t)conf_dumpcycle)
1023 / (off_t)(runs_per_cycle * later)) * (off_t)1024;
1027 g_printf(_("\n due-date #fs orig %cB out %cB balance\n"),
1028 displayunit[0], displayunit[0]);
1029 g_printf("----------------------------------------------\n");
1030 for(seq = 0; seq < later; seq++) {
1031 if(sp[seq].disks == 0 &&
1032 ((seq > 0 && sp[seq-1].disks == 0) ||
1033 ((seq < later-1) && sp[seq+1].disks == 0))) {
1041 g_printf(_("%-9.9s %3d %10lld %10lld "),
1042 seqdatestr(seq), sp[seq].disks,
1043 (long long)sp[seq].origsize,
1044 (long long)sp[seq].outsize);
1045 if(!sp[seq].outsize) g_printf(" --- \n");
1046 else g_printf(_("%+8.1lf%%\n"),
1047 (((double)sp[seq].outsize - (double)balanced) * 100.0 /
1052 if(sp[later].disks != 0) {
1053 g_printf(_("later %3d %10lld %10lld "),
1055 (long long)sp[later].origsize,
1056 (long long)sp[later].outsize);
1057 if(!sp[later].outsize) g_printf(" --- \n");
1058 else g_printf(_("%+8.1lf%%\n"),
1059 (((double)sp[later].outsize - (double)balanced) * 100.0 /
1062 g_printf("----------------------------------------------\n");
1063 g_printf(_("TOTAL %3d %10lld %10lld %9lld\n"),
1065 (long long)sp[total].origsize,
1066 (long long)sp[total].outsize,
1067 (long long)total_balanced);
1068 if (sp[balance].origsize != sp[total].origsize ||
1069 sp[balance].outsize != sp[total].outsize ||
1070 balanced != total_balanced) {
1071 g_printf(_("BALANCED %10lld %10lld %9lld\n"),
1072 (long long)sp[balance].origsize,
1073 (long long)sp[balance].outsize,
1074 (long long)balanced);
1076 if (sp[distinct].disks != sp[total].disks) {
1077 g_printf(_("DISTINCT %3d %10lld %10lld\n"),
1079 (long long)sp[distinct].origsize,
1080 (long long)sp[distinct].outsize);
1082 g_printf(plural(_(" (estimated %d run per dumpcycle)\n"),
1083 _(" (estimated %d runs per dumpcycle)\n"),
1087 g_printf(plural(_(" (%d filesystem overdue."),
1088 _(" (%d filesystems overdue."), overdue),
1090 g_printf(plural(_(" The most being overdue %d day.)\n"),
1091 _(" The most being overdue %d days.)\n"), max_overdue),
1098 /* ----------------------------------------------- */
1106 char *sort_order = NULL;
1107 find_result_t *output_find;
1109 char **output_find_log;
1114 _("%s: expecting \"find [--sort <hkdlpbfw>] [hostname [<disk>]]*\"\n"),
1120 sort_order = newstralloc(sort_order, DEFAULT_SORT_ORDER);
1121 if(argc > 4 && strcmp(argv[3],"--sort") == 0) {
1122 size_t i, valid_sort=1;
1124 for(i = strlen(argv[4]); i > 0; i--) {
1125 switch (argv[4][i - 1]) {
1143 default: valid_sort=0;
1147 sort_order = newstralloc(sort_order, argv[4]);
1149 g_printf(_("Invalid sort order: %s\n"), argv[4]);
1150 g_printf(_("Use default sort order: %s\n"), sort_order);
1156 errstr = match_disklist(&diskq, argc-(start_argc-1), argv+(start_argc-1));
1158 /* check all log file exists */
1159 output_find_log = find_log();
1160 for (name = output_find_log; *name != NULL; name++) {
1163 amfree(output_find_log);
1165 output_find = find_dump(&diskq); /* Add deleted dump to diskq */
1166 if(argc-(start_argc-1) > 0) {
1167 find_result_t *afind = NULL;
1168 find_result_t *afind_next = NULL;
1169 find_result_t *new_output_find = NULL;
1173 errstr = match_disklist(&diskq, argc-(start_argc-1),
1174 argv+(start_argc-1));
1176 g_printf("%s", errstr);
1179 for (afind = output_find; afind; afind = afind_next) {
1180 afind_next = afind->next;
1181 dp = lookup_disk(afind->hostname, afind->diskname);
1183 afind->next = new_output_find;
1184 new_output_find = afind;
1186 amfree(afind->timestamp);
1187 amfree(afind->write_timestamp);
1188 amfree(afind->hostname);
1189 amfree(afind->diskname);
1190 amfree(afind->label);
1191 amfree(afind->status);
1192 amfree(afind->dump_status);
1193 amfree(afind->message);
1197 output_find = new_output_find;
1198 } else if (errstr) {
1199 g_printf("%s", errstr);
1203 sort_find_result(sort_order, &output_find);
1204 print_find_result(output_find);
1205 free_find_result(&output_find);
1211 /* ------------------------ */
1219 GSList * file_list = NULL;
1223 flags = CMDLINE_PARSE_DATESTAMP;
1224 if (allow_empty) flags |= CMDLINE_EMPTY_TO_WILDCARD;
1225 dumplist = cmdline_parse_dumpspecs(argc, argv, flags);
1227 file_list = cmdline_match_holding(dumplist);
1228 dumpspec_list_free(dumplist);
1233 /* Given a file header, find the history element in curinfo most likely
1234 * corresponding to that dump (this is not an exact science).
1236 * @param info: the info_t element for this DLE
1237 * @param file: the header of the file
1238 * @returns: index of the matching history element, or -1 if not found
1241 holding_file_find_history(
1245 int matching_hist_idx = -1;
1249 /* Begin by trying to find the history element matching this dump.
1250 * The datestamp on the dump is for the entire run of amdump, while the
1251 * 'date' in the history element of 'info' is the time the dump itself
1252 * began. A matching history element, then, is the earliest element
1253 * with a 'date' equal to or later than the date of the dumpfile.
1255 * We compare using formatted datestamps; even using seconds since epoch,
1256 * we would still face timezone issues, and have to do a reverse (timezone
1257 * to gmt) translation.
1260 /* get to the end of the history list and search backward */
1261 for (nhist = 0; info->history[nhist].level > -1; nhist++) /* empty loop */;
1262 for (i = nhist-1; i > -1; i--) {
1263 char *info_datestamp = get_timestamp_from_time(info->history[i].date);
1264 int order = strcmp(file->datestamp, info_datestamp);
1265 amfree(info_datestamp);
1268 /* only a match if the levels are equal */
1269 if (info->history[i].level == file->dumplevel) {
1270 matching_hist_idx = i;
1276 return matching_hist_idx;
1279 /* A holding file is 'outdated' if a subsequent dump of the same DLE was made
1280 * at the same level or a lower leve; for example, a level 2 dump is outdated if
1281 * there is a subsequent level 2, or a subsequent level 0.
1283 * @param file: the header of the file
1284 * @returns: true if the file is outdated
1287 holding_file_is_outdated(
1291 int matching_hist_idx;
1293 if (get_info(file->name, file->disk, &info) == -1) {
1294 return 0; /* assume it's not outdated */
1297 /* if the last level is less than the level of this dump, then
1299 if (info.last_level < file->dumplevel)
1302 /* otherwise, we need to see if this dump is the last at its level */
1303 matching_hist_idx = holding_file_find_history(&info, file);
1304 if (matching_hist_idx == -1) {
1305 return 0; /* assume it's not outdated */
1308 /* compare the date of the history element with the most recent date
1309 * for this level. If they match, then this is the last dump at this
1310 * level, and we checked above for more recent lower-level dumps, so
1311 * the dump is not outdated. */
1312 if (info.history[matching_hist_idx].date ==
1313 info.inf[info.history[matching_hist_idx].level].date) {
1321 remove_holding_file_from_catalog(
1324 static int warnings_printed; /* only print once per invocation */
1327 int matching_hist_idx = -1;
1328 history_t matching_hist; /* will be a copy */
1331 if (!holding_file_get_dumpfile(filename, &file)) {
1332 g_printf(_("Could not read holding file %s\n"), filename);
1336 if (get_info(file.name, file.disk, &info) == -1) {
1337 g_printf(_("WARNING: No curinfo record for %s:%s\n"), file.name, file.disk);
1338 dumpfile_free_data(&file);
1339 return 1; /* not an error */
1342 matching_hist_idx = holding_file_find_history(&info, &file);
1344 if (matching_hist_idx == -1) {
1345 g_printf(_("WARNING: No dump matching %s found in curinfo.\n"), filename);
1346 dumpfile_free_data(&file);
1347 return 1; /* not an error */
1351 matching_hist = info.history[matching_hist_idx];
1353 /* Remove the history element itself before doing the stats */
1354 for (i = matching_hist_idx; i < NB_HISTORY; i++) {
1355 info.history[i] = info.history[i+1];
1357 info.history[NB_HISTORY].level = -1;
1359 /* Remove stats for that history element, if necessary. Doing so
1360 * will result in an inconsistent set of backups, so we warn the
1361 * user and adjust last_level to make the next dump get us a
1362 * consistent picture. */
1363 if (matching_hist.date == info.inf[matching_hist.level].date) {
1364 /* search for an earlier dump at this level */
1365 for (i = matching_hist_idx; info.history[i].level > -1; i++) {
1366 if (info.history[i].level == matching_hist.level)
1370 if (info.history[i].level < 0) {
1371 /* not found => zero it out */
1372 info.inf[matching_hist.level].date = (time_t)-1; /* flag as not set */
1373 info.inf[matching_hist.level].label[0] = '\0';
1375 /* found => reconstruct stats as best we can */
1376 info.inf[matching_hist.level].size = info.history[i].size;
1377 info.inf[matching_hist.level].csize = info.history[i].csize;
1378 info.inf[matching_hist.level].secs = info.history[i].secs;
1379 info.inf[matching_hist.level].date = info.history[i].date;
1380 info.inf[matching_hist.level].filenum = 0; /* we don't know */
1381 info.inf[matching_hist.level].label[0] = '\0'; /* we don't know */
1384 /* set last_level to the level we just deleted, and set command
1385 * appropriately to make sure planner does a new dump at this level
1387 info.last_level = matching_hist.level;
1388 if (info.last_level == 0) {
1389 g_printf(_("WARNING: Deleting the most recent full dump; forcing a full dump at next run.\n"));
1390 SET(info.command, FORCE_FULL);
1392 g_printf(_("WARNING: Deleting the most recent level %d dump; forcing a level %d dump or \nWARNING: lower at next run.\n"),
1393 info.last_level, info.last_level);
1394 SET(info.command, FORCE_NO_BUMP);
1397 /* Search for and display any subsequent runs that depended on this one */
1398 warnings_printed = 0;
1399 for (i = matching_hist_idx-1; i >= 0; i--) {
1401 if (info.history[i].level <= matching_hist.level) break;
1403 datestamp = get_timestamp_from_time(info.history[i].date);
1404 g_printf(_("WARNING: Level %d dump made %s can no longer be accurately restored.\n"),
1405 info.history[i].level, datestamp);
1408 warnings_printed = 1;
1410 if (warnings_printed)
1411 g_printf(_("WARNING: (note, dates shown above are for dumps, and may be later than the\nWARNING: corresponding run date)\n"));
1414 /* recalculate consecutive_runs based on the history: find the first run
1415 * at this level, and then count the consecutive runs at that level. This
1416 * number may be zero (if we just deleted the last run at this level) */
1417 info.consecutive_runs = 0;
1418 for (i = 0; info.history[i].level >= 0; i++) {
1419 if (info.history[i].level == info.last_level) break;
1421 while (info.history[i+info.consecutive_runs].level == info.last_level)
1422 info.consecutive_runs++;
1424 /* this function doesn't touch the performance stats */
1426 /* write out the changes */
1427 if (put_info(file.name, file.disk, &info) == -1) {
1428 g_printf(_("Could not write curinfo record for %s:%s\n"), file.name, file.disk);
1429 dumpfile_free_data(&file);
1433 dumpfile_free_data(&file);
1444 enum { HOLDING_USAGE, HOLDING_LIST, HOLDING_DELETE } action = HOLDING_USAGE;
1446 int outdated_list = 0;
1450 action = HOLDING_USAGE;
1451 else if (strcmp(argv[3], "list") == 0 && argc >= 4)
1452 action = HOLDING_LIST;
1453 else if (strcmp(argv[3], "delete") == 0 && argc > 4)
1454 action = HOLDING_DELETE;
1459 _("%s: expecting \"holding list [-l] [-d]\" or \"holding delete <host> [ .. ]\"\n"),
1465 argc -= 4; argv += 4;
1466 while (argc && argv[0][0] == '-') {
1467 switch (argv[0][1]) {
1471 case 'd': /* have to use '-d', and not '-o', because of parse_config */
1475 g_fprintf(stderr, _("Unknown option -%c\n"), argv[0][1]);
1484 g_printf("%-10s %-2s %-4s %s\n",
1485 _("size (kB)"), _("lv"), _("outd"), _("dump specification"));
1488 file_list = get_file_list(argc, argv, 1);
1489 for (li = file_list; li != NULL; li = li->next) {
1493 if (!holding_file_get_dumpfile((char *)li->data, &file)) {
1494 g_fprintf(stderr, _("Error reading %s\n"), (char *)li->data);
1498 is_outdated = holding_file_is_outdated(&file);
1500 dumpstr = cmdline_format_dumpspec_components(file.name, file.disk, file.datestamp, NULL);
1501 /* only print this entry if we're printing everything, or if it's outdated and
1502 * we're only printing outdated files (-o) */
1503 if (!outdated_list || is_outdated) {
1505 g_printf("%-10lld %-2d %-4s %s\n",
1506 (long long)holding_file_size((char *)li->data, 0),
1508 is_outdated? " *":"",
1511 g_printf("%s\n", dumpstr);
1515 dumpfile_free_data(&file);
1517 slist_free_full(file_list, g_free);
1520 case HOLDING_DELETE:
1521 argc -= 4; argv += 4;
1523 file_list = get_file_list(argc, argv, 0);
1524 for (li = file_list; li != NULL; li = li->next) {
1525 g_fprintf(stderr, _("Deleting '%s'\n"), (char *)li->data);
1526 /* remove it from the catalog */
1527 if (!remove_holding_file_from_catalog((char *)li->data))
1531 if (!holding_file_unlink((char *)li->data)) {
1532 error(_("Could not delete '%s'"), (char *)li->data);
1535 slist_free_full(file_list, g_free);
1541 /* ------------------------ */
1544 /* shared code with planner.c */
1550 int bump = getconf_int(CNF_BUMPSIZE);
1551 double mult = getconf_real(CNF_BUMPMULT);
1554 bump = (int)((double)bump * mult);
1564 int conf_bumppercent = getconf_int(CNF_BUMPPERCENT);
1565 double conf_bumpmult = getconf_real(CNF_BUMPMULT);
1567 (void)argc; /* Quiet unused parameter warning */
1568 (void)argv; /* Quiet unused parameter warning */
1570 g_printf(_("Current bump parameters:\n"));
1571 if(conf_bumppercent == 0) {
1572 g_printf(_(" bumpsize %5d KB\t- minimum savings (threshold) to bump level 1 -> 2\n"),
1573 getconf_int(CNF_BUMPSIZE));
1574 g_printf(_(" bumpdays %5d\t- minimum days at each level\n"),
1575 getconf_int(CNF_BUMPDAYS));
1576 g_printf(_(" bumpmult %5.5lg\t- threshold = bumpsize * bumpmult**(level-1)\n\n"),
1579 g_printf(_(" Bump -> To Threshold\n"));
1580 for(l = 1; l < 9; l++)
1581 g_printf(_("\t%d -> %d %9d KB\n"), l, l+1, bump_thresh(l));
1585 double bumppercent = (double)conf_bumppercent;
1587 g_printf(_(" bumppercent %3d %%\t- minimum savings (threshold) to bump level 1 -> 2\n"),
1589 g_printf(_(" bumpdays %5d\t- minimum days at each level\n"),
1590 getconf_int(CNF_BUMPDAYS));
1591 g_printf(_(" bumpmult %5.5lg\t- threshold = disk_size * bumppercent * bumpmult**(level-1)\n\n"),
1593 g_printf(_(" Bump -> To Threshold\n"));
1594 for(l = 1; l < 9; l++) {
1595 g_printf(_("\t%d -> %d %7.2lf %%\n"), l, l+1, bumppercent);
1596 bumppercent *= conf_bumpmult;
1597 if(bumppercent >= 100.000) { bumppercent = 100.0;}
1603 /* ----------------------------------------------- */
1605 void export_one(disk_t *dp);
1614 char hostname[MAX_HOSTNAME_LENGTH+1];
1617 g_printf(_("CURINFO Version %s CONF %s\n"), VERSION, getconf_str(CNF_ORG));
1620 if(gethostname(hostname, SIZEOF(hostname)-1) == -1) {
1621 error(_("could not determine host name: %s\n"), strerror(errno));
1624 hostname[SIZEOF(hostname)-1] = '\0';
1625 g_printf(_("# Generated by:\n# host: %s\n# date: %s"),
1626 hostname, ctime(&curtime));
1628 g_printf(_("# command:"));
1629 for(i = 0; i < argc; i++)
1630 g_printf(_(" %s"), argv[i]);
1632 g_printf(_("\n# This file can be merged back in with \"amadmin import\".\n"));
1633 g_printf(_("# Edit only with care.\n"));
1636 diskloop(argc, argv, "export", export_one);
1637 else for(dp = diskq.head; dp != NULL; dp = dp->next)
1648 if(get_info(dp->host->hostname, dp->name, &info)) {
1649 g_fprintf(stderr, _("Warning: no curinfo record for %s:%s\n"),
1650 dp->host->hostname, dp->name);
1653 g_printf(_("host: %s\ndisk: %s\n"), dp->host->hostname, dp->name);
1654 g_printf(_("command: %u\n"), info.command);
1655 g_printf(_("last_level: %d\n"),info.last_level);
1656 g_printf(_("consecutive_runs: %d\n"),info.consecutive_runs);
1657 g_printf(_("full-rate:"));
1658 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.full.rate[i]);
1659 g_printf(_("\nfull-comp:"));
1660 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.full.comp[i]);
1662 g_printf(_("\nincr-rate:"));
1663 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.incr.rate[i]);
1664 g_printf(_("\nincr-comp:"));
1665 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.incr.comp[i]);
1667 for(l=0;l<DUMP_LEVELS;l++) {
1668 if(info.inf[l].date < (time_t)0 && info.inf[l].label[0] == '\0') continue;
1669 g_printf(_("stats: %d %lld %lld %jd %jd %lld %s\n"), l,
1670 (long long)info.inf[l].size,
1671 (long long)info.inf[l].csize,
1672 (intmax_t)info.inf[l].secs,
1673 (intmax_t)info.inf[l].date,
1674 (long long)info.inf[l].filenum,
1677 for(l=0;info.history[l].level > -1;l++) {
1678 g_printf(_("history: %d %lld %lld %jd\n"),
1679 info.history[l].level,
1680 (long long)info.history[l].size,
1681 (long long)info.history[l].csize,
1682 (intmax_t)info.history[l].date);
1687 /* ----------------------------------------------- */
1689 int import_one(void);
1690 char *impget_line(void);
1708 (void)argc; /* Quiet unused parameter warning */
1709 (void)argv; /* Quiet unused parameter warning */
1711 /* process header line */
1713 if((line = agets(stdin)) == NULL) {
1714 g_fprintf(stderr, _("%s: empty input.\n"), get_pname());
1722 if(strncmp_const_skip(s - 1, "CURINFO Version", s, ch) != 0) {
1726 skip_whitespace(s, ch);
1728 || sscanf(s - 1, "%d.%d.%d", &vers_maj, &vers_min, &vers_patch) != 3) {
1732 skip_integer(s, ch); /* skip over major */
1737 skip_integer(s, ch); /* skip over minor */
1742 skip_integer(s, ch); /* skip over patch */
1748 skip_non_whitespace(s, ch);
1752 skip_whitespace(s, ch); /* find the org keyword */
1753 if(ch == '\0' || strncmp_const_skip(s - 1, "CONF", s, ch) != 0) {
1759 skip_whitespace(s, ch); /* find the org string */
1766 newer = (vers_maj != VERSION_MAJOR)? vers_maj > VERSION_MAJOR :
1767 (vers_min != VERSION_MINOR)? vers_min > VERSION_MINOR :
1768 vers_patch > VERSION_PATCH;
1771 _("%s: WARNING: input is from newer Amanda version: %d.%d.%d.\n"),
1772 get_pname(), vers_maj, vers_min, vers_patch);
1775 if(strcmp(org, getconf_str(CNF_ORG)) != 0) {
1776 g_fprintf(stderr, _("%s: WARNING: input is from different org: %s\n"),
1789 /*@i@*/ amfree(line);
1790 g_fprintf(stderr, _("%s: bad CURINFO header line in input: %s.\n"),
1792 g_fprintf(stderr, _(" Was the input in \"amadmin export\" format?\n"));
1807 char *hostname = NULL;
1808 char *diskname = NULL;
1809 long long off_t_tmp;
1810 long long time_t_tmp;
1812 memset(&info, 0, SIZEOF(info_t));
1814 for(level = 0; level < DUMP_LEVELS; level++) {
1815 info.inf[level].date = (time_t)-1;
1818 /* get host: disk: command: lines */
1820 hostname = diskname = NULL;
1822 if((line = impget_line()) == NULL) return 0; /* nothing there */
1826 skip_whitespace(s, ch);
1827 if(ch == '\0' || strncmp_const_skip(s - 1, "host:", s, ch) != 0) goto parse_err;
1828 skip_whitespace(s, ch);
1829 if(ch == '\0') goto parse_err;
1831 skip_non_whitespace(s, ch);
1833 hostname = stralloc(fp);
1836 skip_whitespace(s, ch);
1839 if((line = impget_line()) == NULL) goto shortfile_err;
1842 skip_whitespace(s, ch);
1844 if(strncmp_const_skip(s - 1, "disk:", s, ch) != 0) goto parse_err;
1845 skip_whitespace(s, ch);
1846 if(ch == '\0') goto parse_err;
1848 skip_non_whitespace(s, ch);
1850 diskname = stralloc(fp);
1854 if((line = impget_line()) == NULL) goto shortfile_err;
1855 if(sscanf(line, "command: %u", &info.command) != 1) goto parse_err;
1857 /* get last_level and consecutive_runs */
1860 if((line = impget_line()) == NULL) goto shortfile_err;
1861 rc = sscanf(line, "last_level: %d", &info.last_level);
1864 if((line = impget_line()) == NULL) goto shortfile_err;
1865 if(sscanf(line, "consecutive_runs: %d", &info.consecutive_runs) != 1) goto parse_err;
1867 if((line = impget_line()) == NULL) goto shortfile_err;
1870 /* get rate: and comp: lines for full dumps */
1872 rc = sscanf(line, "full-rate: %lf %lf %lf",
1873 &info.full.rate[0], &info.full.rate[1], &info.full.rate[2]);
1874 if(rc != 3) goto parse_err;
1877 if((line = impget_line()) == NULL) goto shortfile_err;
1878 rc = sscanf(line, "full-comp: %lf %lf %lf",
1879 &info.full.comp[0], &info.full.comp[1], &info.full.comp[2]);
1880 if(rc != 3) goto parse_err;
1882 /* get rate: and comp: lines for incr dumps */
1885 if((line = impget_line()) == NULL) goto shortfile_err;
1886 rc = sscanf(line, "incr-rate: %lf %lf %lf",
1887 &info.incr.rate[0], &info.incr.rate[1], &info.incr.rate[2]);
1888 if(rc != 3) goto parse_err;
1891 if((line = impget_line()) == NULL) goto shortfile_err;
1892 rc = sscanf(line, "incr-comp: %lf %lf %lf",
1893 &info.incr.comp[0], &info.incr.comp[1], &info.incr.comp[2]);
1894 if(rc != 3) goto parse_err;
1896 /* get stats for dump levels */
1900 if((line = impget_line()) == NULL) goto shortfile_err;
1901 if(strncmp_const(line, "//") == 0) {
1905 if(strncmp_const(line, "history:") == 0) {
1909 memset(&onestat, 0, SIZEOF(onestat));
1914 skip_whitespace(s, ch);
1915 if(ch == '\0' || strncmp_const_skip(s - 1, "stats:", s, ch) != 0) {
1919 skip_whitespace(s, ch);
1920 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1923 skip_integer(s, ch);
1925 skip_whitespace(s, ch);
1926 if(ch == '\0' || sscanf(s - 1, "%lld", &off_t_tmp) != 1) {
1929 onestat.size = (off_t)off_t_tmp;
1930 skip_integer(s, ch);
1932 skip_whitespace(s, ch);
1933 if(ch == '\0' || sscanf(s - 1, "%lld", &off_t_tmp) != 1) {
1936 onestat.csize = (off_t)off_t_tmp;
1937 skip_integer(s, ch);
1939 skip_whitespace(s, ch);
1940 if(ch == '\0' || sscanf(s - 1, "%lld", &time_t_tmp) != 1) {
1943 onestat.secs = (time_t)time_t_tmp;
1944 skip_integer(s, ch);
1946 skip_whitespace(s, ch);
1947 if(ch == '\0' || sscanf(s - 1, "%lld", &time_t_tmp) != 1) {
1950 /* time_t not guarranteed to be long */
1951 /*@i1@*/ onestat.date = (time_t)time_t_tmp;
1952 skip_integer(s, ch);
1954 skip_whitespace(s, ch);
1956 if(sscanf(s - 1, "%lld", &off_t_tmp) != 1) {
1959 onestat.filenum = (off_t)off_t_tmp;
1960 skip_integer(s, ch);
1962 skip_whitespace(s, ch);
1964 if (onestat.filenum != 0)
1966 onestat.label[0] = '\0';
1968 strncpy(onestat.label, s - 1, SIZEOF(onestat.label)-1);
1969 onestat.label[SIZEOF(onestat.label)-1] = '\0';
1973 if(level < 0 || level > 9) goto parse_err;
1975 info.inf[level] = onestat;
1978 for(i=0;i<=NB_HISTORY;i++) {
1979 info.history[i].level = -2;
1982 history_t onehistory;
1984 if(line[0] == '/' && line[1] == '/') {
1985 info.history[nb_history].level = -2;
1989 memset(&onehistory, 0, SIZEOF(onehistory));
1992 if(strncmp_const_skip(line, "history:", s, ch) != 0) {
1996 skip_whitespace(s, ch);
1997 if(ch == '\0' || sscanf((s - 1), "%d", &onehistory.level) != 1) {
2000 skip_integer(s, ch);
2002 skip_whitespace(s, ch);
2003 if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
2006 onehistory.size = (off_t)off_t_tmp;
2007 skip_integer(s, ch);
2009 skip_whitespace(s, ch);
2010 if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
2013 onehistory.csize = (off_t)off_t_tmp;
2014 skip_integer(s, ch);
2016 skip_whitespace(s, ch);
2017 if((ch == '\0') || sscanf((s - 1), "%lld", &time_t_tmp) != 1) {
2020 /* time_t not guarranteed to be long */
2021 /*@i1@*/ onehistory.date = (time_t)time_t_tmp;
2022 skip_integer(s, ch);
2024 info.history[nb_history++] = onehistory;
2026 if((line = impget_line()) == NULL) goto shortfile_err;
2028 /*@i@*/ amfree(line);
2030 /* got a full record, now write it out to the database */
2032 if(put_info(hostname, diskname, &info)) {
2033 g_fprintf(stderr, _("%s: error writing record for %s:%s\n"),
2034 get_pname(), hostname, diskname);
2041 /*@i@*/ amfree(line);
2044 g_fprintf(stderr, _("%s: parse error reading import record.\n"), get_pname());
2048 /*@i@*/ amfree(line);
2051 g_fprintf(stderr, _("%s: short file reading import record.\n"), get_pname());
2062 for(; (line = agets(stdin)) != NULL; free(line)) {
2066 skip_whitespace(s, ch);
2068 /* ignore comment lines */
2071 /* found non-blank, return line */
2074 /* otherwise, a blank line, so keep going */
2077 g_fprintf(stderr, _("%s: reading stdin: %s\n"),
2078 get_pname(), strerror(errno));
2083 /* ----------------------------------------------- */
2092 identlist_t pp_scriptlist;
2093 estimatelist_t estimates;
2094 dumptype_t *dtype = lookup_dumptype(dp->dtype_name);
2099 g_printf("line %d:\n", dp->line);
2101 g_printf(" host %s:\n", hp->hostname);
2102 g_printf(" interface %s\n",
2103 interface_name(ip->config)[0] ? interface_name(ip->config) : "default");
2104 g_printf(" disk %s:\n", dp->name);
2105 if (dp->device) g_printf(" device %s\n", dp->device);
2107 g_printf(" program \"%s\"\n", dp->program);
2108 if (dp->application)
2109 g_printf(" application \"%s\"\n", dp->application);
2110 g_printf(" data-path %s\n", data_path_to_string(dp->data_path));
2111 if (dp->exclude_file != NULL && dp->exclude_file->nb_element > 0) {
2112 g_printf(" exclude file");
2113 for(excl = dp->exclude_file->first; excl != NULL; excl = excl->next) {
2114 g_printf(" \"%s\"", excl->name);
2118 if (dp->exclude_list != NULL && dp->exclude_list->nb_element > 0) {
2119 g_printf(" exclude list");
2120 if(dp->exclude_optional) g_printf(" optional");
2121 for(excl = dp->exclude_list->first; excl != NULL; excl = excl->next) {
2122 g_printf(" \"%s\"", excl->name);
2126 if (dp->include_file != NULL && dp->include_file->nb_element > 0) {
2127 g_printf(" include file");
2128 for(excl = dp->include_file->first; excl != NULL; excl = excl->next) {
2129 g_printf(" \"%s\"", excl->name);
2133 if (dp->include_list != NULL && dp->include_list->nb_element > 0) {
2134 g_printf(" include list");
2135 if (dp->include_optional) g_printf(" optional");
2136 for(excl = dp->include_list->first; excl != NULL; excl = excl->next) {
2137 g_printf(" \"%s\"", excl->name);
2141 g_printf(" priority %d\n", dp->priority);
2142 g_printf(" dumpcycle %d\n", dp->dumpcycle);
2143 g_printf(" maxdumps %d\n", dp->maxdumps);
2144 g_printf(" maxpromoteday %d\n", dp->maxpromoteday);
2145 if (dp->bumppercent > 0) {
2146 g_printf(" bumppercent %d\n", dp->bumppercent);
2149 g_printf(" bumpsize %lld\n",
2150 (long long)dp->bumpsize);
2152 g_printf(" bumpdays %d\n", dp->bumpdays);
2153 g_printf(" bumpmult %lf\n", dp->bumpmult);
2155 g_printf(" strategy ");
2156 switch(dp->strategy) {
2161 g_printf("STANDARD\n");
2164 g_printf("NOFULL\n");
2167 g_printf("NOINC\n");
2170 g_printf("HANOI\n");
2173 g_printf("INCRONLY\n");
2176 g_printf(" ignore %s\n", (dp->ignore? "YES" : "NO"));
2177 g_printf(" estimate ");
2178 estimates = dp->estimatelist;
2180 switch((estimate_t)GPOINTER_TO_INT(estimates->data)) {
2188 g_printf("CALCSIZE");
2193 estimates = estimates->next;
2199 g_printf(" compress ");
2200 switch(dp->compress) {
2205 g_printf("CLIENT FAST\n");
2208 g_printf("CLIENT BEST\n");
2211 g_printf("CLIENT CUSTOM\n");
2212 g_printf(" client-custom-compress \"%s\"\n",
2213 dp->clntcompprog? dp->clntcompprog : "");
2215 case COMP_SERVER_FAST:
2216 g_printf("SERVER FAST\n");
2218 case COMP_SERVER_BEST:
2219 g_printf("SERVER BEST\n");
2221 case COMP_SERVER_CUST:
2222 g_printf("SERVER CUSTOM\n");
2223 g_printf(" server-custom-compress \"%s\"\n",
2224 dp->srvcompprog? dp->srvcompprog : "");
2227 if(dp->compress != COMP_NONE) {
2228 g_printf(" comprate %.2lf %.2lf\n",
2229 dp->comprate[0], dp->comprate[1]);
2232 g_printf(" encrypt ");
2233 switch(dp->encrypt) {
2238 g_printf("CLIENT\n");
2239 g_printf(" client-encrypt \"%s\"\n",
2240 dp->clnt_encrypt? dp->clnt_encrypt : "");
2241 g_printf(" client-decrypt-option \"%s\"\n",
2242 dp->clnt_decrypt_opt? dp->clnt_decrypt_opt : "");
2244 case ENCRYPT_SERV_CUST:
2245 g_printf("SERVER\n");
2246 g_printf(" server-encrypt \"%s\"\n",
2247 dp->srv_encrypt? dp->srv_encrypt : "");
2248 g_printf(" server-decrypt-option \"%s\"\n",
2249 dp->srv_decrypt_opt? dp->srv_decrypt_opt : "");
2253 g_printf(" auth \"%s\"\n", dp->auth);
2254 g_printf(" kencrypt %s\n", (dp->kencrypt? "YES" : "NO"));
2255 g_printf(" amandad-path \"%s\"\n", dp->amandad_path);
2256 g_printf(" client-username \"%s\"\n", dp->client_username);
2257 g_printf(" client-port \"%s\"\n", dp->client_port);
2258 g_printf(" ssh-keys \"%s\"\n", dp->ssh_keys);
2260 g_printf(" holdingdisk ");
2261 switch(dp->to_holdingdisk) {
2263 g_printf("NEVER\n");
2269 g_printf("REQUIRED\n");
2273 g_printf(" record %s\n", (dp->record? "YES" : "NO"));
2274 g_printf(" index %s\n", (dp->index? "YES" : "NO"));
2275 g_printf(" starttime %04d\n", (int)dp->starttime);
2276 if(dp->tape_splitsize > (off_t)0) {
2277 g_printf(" tape-splitsize %lld\n",
2278 (long long)dp->tape_splitsize);
2280 if(dp->split_diskbuffer) {
2281 g_printf(" split-diskbuffer %s\n", dp->split_diskbuffer);
2283 if(dp->fallback_splitsize > (off_t)0) {
2284 g_printf(" fallback-splitsize %lldMb\n",
2285 (long long)(dp->fallback_splitsize / (off_t)1024));
2287 g_printf(" skip-incr %s\n", (dp->skip_incr? "YES" : "NO"));
2288 g_printf(" skip-full %s\n", (dp->skip_full? "YES" : "NO"));
2289 g_printf(" allow-split %s\n", (dp->allow_split ? "YES" : "NO"));
2290 if (dumptype_seen(dtype, DUMPTYPE_RECOVERY_LIMIT)) {
2292 rl = val_t_display_strs(dumptype_getconf((dtype),
2293 DUMPTYPE_RECOVERY_LIMIT), 1);
2294 for(r1 = rl; *r1 != NULL; r1++) {
2295 g_printf(" recovery-limit %s\n", *r1);
2299 g_printf(" spindle %d\n", dp->spindle);
2300 pp_scriptlist = dp->pp_scriptlist;
2301 while (pp_scriptlist != NULL) {
2302 g_printf(" script \"%s\"\n", (char *)pp_scriptlist->data);
2303 pp_scriptlist = pp_scriptlist->next;
2309 prop = val_t_display_strs(dumptype_getconf((dtype), DUMPTYPE_PROPERTY),
2311 for(p1 = prop; *p1 != NULL; p1++) {
2312 g_printf(" property %s\n", *p1);
2329 diskloop(argc, argv, "disklist", disklist_one);
2331 for(dp = diskq.head; dp != NULL; dp = dp->next)
2335 /* ----------------------------------------------- */
2339 int argc G_GNUC_UNUSED,
2340 char ** argv G_GNUC_UNUSED)
2344 GHashTable *seen = g_hash_table_new(g_str_hash, g_str_equal);
2346 /* enumerate all hosts, skipping those that have been seen (since
2347 * there may be more than one DLE on a host */
2348 for(dp = diskq.head; dp != NULL; dp = dp->next) {
2349 char *hostname = dp->host->hostname;
2350 if (g_hash_table_lookup(seen, hostname))
2352 g_printf("%s\n", hostname);
2353 g_hash_table_insert(seen, hostname, &sentinel);
2355 g_hash_table_destroy(seen);
2358 /* ----------------------------------------------- */
2362 int argc G_GNUC_UNUSED,
2363 char ** argv G_GNUC_UNUSED)
2367 for(dp = diskq.head; dp != NULL; dp = dp->next)
2368 g_printf("%s %s\n", dp->host->hostname, dp->name);
2371 /* ----------------------------------------------- */
2380 (void)argc; /* Quiet unused parameter warning */
2381 (void)argv; /* Quiet unused parameter warning */
2383 for(i = 0; version_info[i] != NULL; i++)
2384 g_printf("%s", version_info[i]);
2389 int argc G_GNUC_UNUSED,
2390 char **argv G_GNUC_UNUSED)
2392 dump_configuration();