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"
49 int main(int argc, char **argv);
51 static void estimate(int argc, char **argv);
52 static void estimate_one(disk_t *dp);
53 void force(int argc, char **argv);
54 void force_one(disk_t *dp);
55 void unforce(int argc, char **argv);
56 void unforce_one(disk_t *dp);
57 void force_bump(int argc, char **argv);
58 void force_bump_one(disk_t *dp);
59 void force_no_bump(int argc, char **argv);
60 void force_no_bump_one(disk_t *dp);
61 void unforce_bump(int argc, char **argv);
62 void unforce_bump_one(disk_t *dp);
63 void reuse(int argc, char **argv);
64 void noreuse(int argc, char **argv);
65 void info(int argc, char **argv);
66 void info_one(disk_t *dp);
67 void due(int argc, char **argv);
68 void due_one(disk_t *dp);
69 void find(int argc, char **argv);
70 void holding(int argc, char **argv);
71 void delete(int argc, char **argv);
72 void delete_one(disk_t *dp);
73 void balance(int argc, char **argv);
74 void tape(int argc, char **argv);
75 void bumpsize(int argc, char **argv);
76 void diskloop(int argc, char **argv, char *cmdname, void (*func)(disk_t *dp));
77 char *seqdatestr(int seq);
78 static int next_level0(disk_t *dp, info_t *info);
79 int bump_thresh(int level);
80 void export_db(int argc, char **argv);
81 void import_db(int argc, char **argv);
82 void hosts(int argc, char **argv);
83 void dles(int argc, char **argv);
84 void disklist(int argc, char **argv);
85 void disklist_one(disk_t *dp);
86 void show_version(int argc, char **argv);
87 static void show_config(int argc, char **argv);
89 static char *conf_tapelist = NULL;
90 static char *displayunit;
91 static long int unitdivisor;
92 static gboolean print_default = 1;
93 static gboolean print_source = 0;
94 static int opt_days = -1;
95 static char *opt_sort = NULL;
96 static gboolean opt_long = 0;
97 static gboolean opt_outdated = 0;
101 void (*fn)(int, char **);
104 { "version", show_version,
105 T_("\t\t\t\t\t# Show version info.") },
106 { "config", show_config,
107 T_("\t\t\t\t\t# Show configuration.") },
108 { "estimate", estimate,
109 T_(" [<hostname> [<disks>]* ]*\t# Print server estimate.") },
111 T_(" [<hostname> [<disks>]* ]+\t\t# Force level 0 at next run.") },
112 { "unforce", unforce,
113 T_(" [<hostname> [<disks>]* ]+\t# Clear force command.") },
114 { "force-bump", force_bump,
115 T_(" [<hostname> [<disks>]* ]+\t# Force bump at next run.") },
116 { "force-no-bump", force_no_bump,
117 T_(" [<hostname> [<disks>]* ]+\t# Force no-bump at next run.") },
118 { "unforce-bump", unforce_bump,
119 T_(" [<hostname> [<disks>]* ]+\t# Clear bump command.") },
120 { "disklist", disklist,
121 T_(" [<hostname> [<disks>]* ]*\t# Debug disklist entries.") },
123 T_("\t\t\t\t\t# Show all distinct hosts in disklist.") },
125 T_("\t\t\t\t\t# Show all dles in disklist, one per line.") },
127 T_(" <tapelabel> ...\t\t # re-use this tape.") },
128 { "no-reuse", noreuse,
129 T_(" <tapelabel> ...\t # never re-use this tape.") },
131 T_(" [<hostname> [<disks>]* ]*\t # Show which tapes these dumps are on.") },
132 { "holding", holding,
133 T_(" {list [ -l ] |delete} [ <hostname> [ <disk> [ <datestamp> [ .. ] ] ] ]+\t # Show or delete holding disk contents.") },
135 T_(" [<hostname> [<disks>]* ]+ # Delete from database.") },
137 T_(" [<hostname> [<disks>]* ]*\t # Show current info records.") },
139 T_(" [<hostname> [<disks>]* ]*\t # Show due date.") },
140 { "balance", balance,
141 T_(" [--days <num>]\t\t # Show nightly dump size balance.") },
143 T_(" [--days <num>]\t\t # Show which tape is due next.") },
144 { "bumpsize", bumpsize,
145 T_("\t\t\t # Show current bump thresholds.") },
146 { "export", export_db,
147 T_(" [<hostname> [<disks>]* ]* # Export curinfo database to stdout.") },
148 { "import", import_db,
149 T_("\t\t\t\t # Import curinfo database from stdin.") },
151 #define NCMDS (int)(sizeof(cmdtab) / sizeof(cmdtab[0]))
153 static struct option long_options[] = {
154 {"version" , 0, NULL, 1},
155 {"no-default" , 0, NULL, 2},
156 {"print-source" , 0, NULL, 3},
157 {"days" , 1, NULL, 4},
158 {"sort" , 1, NULL, 5},
170 config_overrides_t *cfg_ovr = NULL;
173 * Configure program for internationalization:
174 * 1) Only set the message locale for now.
175 * 2) Set textdomain for all amanda related programs to "amanda"
176 * We don't want to be forced to support dozens of message catalogs.
178 setlocale(LC_MESSAGES, "C");
179 textdomain("amanda");
184 set_pname("amadmin");
186 /* Don't die when child closes pipe */
187 signal(SIGPIPE, SIG_IGN);
189 dbopen(DBG_SUBDIR_SERVER);
191 add_amanda_log_handler(amanda_log_stderr);
193 cfg_ovr = extract_commandline_config_overrides(&argc, &argv);
196 int option_index = 0;
198 c = getopt_long(argc, argv, "ld", long_options, &option_index);
205 case 1: printf("amadmin-%s\n", VERSION);
207 case 2: print_default = 0;
209 case 3: print_source = 1;
211 case 4: opt_days = atoi(optarg);
213 case 5: opt_sort = g_strdup(optarg);
215 case 'l': opt_long = TRUE;
217 case 'd': opt_outdated = TRUE;
222 argc -= optind-1, argv += optind-1;
224 if(argc < 3) usage();
226 set_config_overrides(cfg_ovr);
227 config_init(CONFIG_INIT_EXPLICIT_NAME, argv[1]);
229 if(strcmp(argv[2],"version") == 0) {
230 show_version(argc, argv);
234 conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
235 read_diskfile(conf_diskfile, &diskq);
236 amfree(conf_diskfile);
238 if (config_errors(NULL) >= CFGERR_WARNINGS) {
239 config_print_errors();
240 if (config_errors(NULL) >= CFGERR_ERRORS) {
241 g_critical(_("errors processing config file"));
245 dbrename(get_config_name(), DBG_SUBDIR_SERVER);
247 check_running_as(RUNNING_AS_DUMPUSER);
249 conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
250 if(read_tapelist(conf_tapelist)) {
251 error(_("could not load tapelist \"%s\""), conf_tapelist);
254 /* conf_tapelist is not freed yet -- it may be used to write the
257 conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
258 if(open_infofile(conf_infofile)) {
259 error(_("could not open info db \"%s\""), conf_infofile);
262 amfree(conf_infofile);
264 displayunit = getconf_str(CNF_DISPLAYUNIT);
265 unitdivisor = getconf_unit_divisor();
267 for (i = 0; i < NCMDS; i++)
268 if (strcmp(argv[2], cmdtab[i].name) == 0) {
269 (*cmdtab[i].fn)(argc, argv);
273 g_fprintf(stderr, _("%s: unknown command \"%s\"\n"), argv[0], argv[2]);
279 amfree(conf_tapelist);
283 free_disklist(&diskq);
294 g_fprintf(stderr, _("\nUsage: %s [--version] [--no-default] [--print-source] [-o configoption]*\n <conf> <command> {<args>} ...\n"),
296 g_fprintf(stderr, _(" Valid <command>s are:\n"));
297 for (i = 0; i < NCMDS; i++)
298 g_fprintf(stderr, "\t%s%s\n", cmdtab[i].name, _(cmdtab[i].usage));
303 /* ----------------------------------------------- */
305 #define SECS_PER_DAY (24*60*60)
313 static char *dow[7] = {
322 time_t t = today + seq*SECS_PER_DAY;
328 g_snprintf(str, SIZEOF(str),
329 "%2d/%02d %3s", tm->tm_mon+1, tm->tm_mday, _(dow[tm->tm_wday]));
331 strcpy(str, _("BAD DATE"));
337 #define days_diff(a, b) (int)(((b) - (a) + SECS_PER_DAY) / SECS_PER_DAY)
339 /* when is next level 0 due? 0 = tonight, 1 = tommorrow, etc*/
345 if(dp->strategy == DS_NOFULL)
346 return 1; /* fake it */
347 if(info->inf[0].date < (time_t)0)
348 return 0; /* new disk */
350 return dp->dumpcycle - days_diff(info->inf[0].date, today);
353 /* ----------------------------------------------- */
360 void (*func)(disk_t *dp))
367 g_fprintf(stderr,_("%s: expecting \"%s [<hostname> [<disks>]* ]+\"\n"),
368 get_pname(), cmdname);
372 errstr = match_disklist(&diskq, argc-3, argv+3);
374 g_printf("%s", errstr);
378 for(dp = diskq.head; dp != NULL; dp = dp->next) {
385 g_fprintf(stderr,_("%s: no disk matched\n"),get_pname());
389 /* ----------------------------------------------- */
396 char *hostname = dp->host->hostname;
397 char *diskname = dp->name;
398 char *qhost = quote_string(hostname);
399 char *qdisk = quote_string(diskname);
404 get_info(hostname, diskname, &info);
406 size = internal_server_estimate(dp, &info, 0, &stats);
408 printf("%s %s %d %jd\n", qhost, qdisk, 0, (intmax_t)size);
411 if (info.last_level > 0) {
412 size = internal_server_estimate(dp, &info, info.last_level, &stats);
414 printf("%s %s %d %jd\n", qhost, qdisk, info.last_level,
419 if (info.last_level > -1) {
420 size = internal_server_estimate(dp, &info, info.last_level+1, &stats);
422 printf("%s %s %d %jd\n", qhost, qdisk, info.last_level+1,
440 diskloop(argc, argv, "estimate", estimate_one);
442 for(dp = diskq.head; dp != NULL; dp = dp->next)
447 /* ----------------------------------------------- */
454 char *hostname = dp->host->hostname;
455 char *diskname = dp->name;
458 get_info(hostname, diskname, &info);
459 SET(info.command, FORCE_FULL);
460 if (ISSET(info.command, FORCE_BUMP)) {
461 CLR(info.command, FORCE_BUMP);
462 g_printf(_("%s: WARNING: %s:%s FORCE_BUMP command was cleared.\n"),
463 get_pname(), hostname, diskname);
465 if(put_info(hostname, diskname, &info) == 0) {
466 if (dp->strategy == DS_INCRONLY) {
467 g_printf(_("%s: %s:%s, full dump done offline, next dump will be at level 1.\n"),
468 get_pname(), hostname, diskname);
470 g_printf(_("%s: %s:%s is set to a forced level 0 at next run.\n"),
471 get_pname(), hostname, diskname);
474 g_fprintf(stderr, _("%s: %s:%s could not be forced.\n"),
475 get_pname(), hostname, diskname);
485 diskloop(argc, argv, "force", force_one);
489 /* ----------------------------------------------- */
496 char *hostname = dp->host->hostname;
497 char *diskname = dp->name;
500 get_info(hostname, diskname, &info);
501 if (ISSET(info.command, FORCE_FULL)) {
502 CLR(info.command, FORCE_FULL);
503 if(put_info(hostname, diskname, &info) == 0){
504 g_printf(_("%s: force command for %s:%s cleared.\n"),
505 get_pname(), hostname, diskname);
508 _("%s: force command for %s:%s could not be cleared.\n"),
509 get_pname(), hostname, diskname);
513 g_printf(_("%s: no force command outstanding for %s:%s, unchanged.\n"),
514 get_pname(), hostname, diskname);
523 diskloop(argc, argv, "unforce", unforce_one);
527 /* ----------------------------------------------- */
534 char *hostname = dp->host->hostname;
535 char *diskname = dp->name;
538 get_info(hostname, diskname, &info);
539 SET(info.command, FORCE_BUMP);
540 if (ISSET(info.command, FORCE_NO_BUMP)) {
541 CLR(info.command, FORCE_NO_BUMP);
542 g_printf(_("%s: WARNING: %s:%s FORCE_NO_BUMP command was cleared.\n"),
543 get_pname(), hostname, diskname);
545 if (ISSET(info.command, FORCE_FULL)) {
546 CLR(info.command, FORCE_FULL);
547 g_printf(_("%s: WARNING: %s:%s FORCE_FULL command was cleared.\n"),
548 get_pname(), hostname, diskname);
550 if(put_info(hostname, diskname, &info) == 0) {
551 g_printf(_("%s: %s:%s is set to bump at next run.\n"),
552 get_pname(), hostname, diskname);
554 g_fprintf(stderr, _("%s: %s:%s could not be forced to bump.\n"),
555 get_pname(), hostname, diskname);
565 diskloop(argc, argv, "force-bump", force_bump_one);
569 /* ----------------------------------------------- */
576 char *hostname = dp->host->hostname;
577 char *diskname = dp->name;
580 get_info(hostname, diskname, &info);
581 SET(info.command, FORCE_NO_BUMP);
582 if (ISSET(info.command, FORCE_BUMP)) {
583 CLR(info.command, FORCE_BUMP);
584 g_printf(_("%s: WARNING: %s:%s FORCE_BUMP command was cleared.\n"),
585 get_pname(), hostname, diskname);
587 if(put_info(hostname, diskname, &info) == 0) {
588 g_printf(_("%s: %s:%s is set to not bump at next run.\n"),
589 get_pname(), hostname, diskname);
591 g_fprintf(stderr, _("%s: %s:%s could not be force to not bump.\n"),
592 get_pname(), hostname, diskname);
602 diskloop(argc, argv, "force-no-bump", force_no_bump_one);
606 /* ----------------------------------------------- */
613 char *hostname = dp->host->hostname;
614 char *diskname = dp->name;
617 get_info(hostname, diskname, &info);
618 if (ISSET(info.command, FORCE_BUMP|FORCE_NO_BUMP)) {
619 CLR(info.command, FORCE_BUMP|FORCE_NO_BUMP);
620 if(put_info(hostname, diskname, &info) == 0) {
621 g_printf(_("%s: bump command for %s:%s cleared.\n"),
622 get_pname(), hostname, diskname);
624 g_fprintf(stderr, _("%s: %s:%s bump command could not be cleared.\n"),
625 get_pname(), hostname, diskname);
629 g_printf(_("%s: no bump command outstanding for %s:%s, unchanged.\n"),
630 get_pname(), hostname, diskname);
640 diskloop(argc, argv, "unforce-bump", unforce_bump_one);
644 /* ----------------------------------------------- */
655 g_fprintf(stderr,_("%s: expecting \"reuse <tapelabel> ...\"\n"),
660 for(count=3; count< argc; count++) {
661 tp = lookup_tapelabel(argv[count]);
663 g_fprintf(stderr, _("reuse: tape label %s not found in tapelist.\n"),
667 if( tp->reuse == 0 ) {
669 g_printf(_("%s: marking tape %s as reusable.\n"),
670 get_pname(), argv[count]);
672 g_fprintf(stderr, _("%s: tape %s already reusable.\n"),
673 get_pname(), argv[count]);
677 if(write_tapelist(conf_tapelist)) {
678 error(_("could not write tapelist \"%s\""), conf_tapelist);
692 g_fprintf(stderr,_("%s: expecting \"no-reuse <tapelabel> ...\"\n"),
697 for(count=3; count< argc; count++) {
698 tp = lookup_tapelabel(argv[count]);
700 g_fprintf(stderr, _("no-reuse: tape label %s not found in tapelist.\n"),
704 if( tp->reuse == 1 ) {
706 g_printf(_("%s: marking tape %s as not reusable.\n"),
707 get_pname(), argv[count]);
709 g_fprintf(stderr, _("%s: tape %s already not reusable.\n"),
710 get_pname(), argv[count]);
714 if(write_tapelist(conf_tapelist)) {
715 error(_("could not write tapelist \"%s\""), conf_tapelist);
721 /* ----------------------------------------------- */
729 char *hostname = dp->host->hostname;
730 char *diskname = dp->name;
733 if(get_info(hostname, diskname, &info)) {
734 g_printf(_("%s: %s:%s NOT currently in database.\n"),
735 get_pname(), hostname, diskname);
740 if(del_info(hostname, diskname)) {
741 error(_("couldn't delete %s:%s from database: %s"),
742 hostname, diskname, strerror(errno));
745 g_printf(_("%s: %s:%s deleted from curinfo database.\n"),
746 get_pname(), hostname, diskname);
756 diskloop(argc, argv, "delete", delete_one);
760 _("%s: NOTE: you'll have to remove these from the disklist yourself.\n"),
764 /* ----------------------------------------------- */
775 get_info(dp->host->hostname, dp->name, &info);
777 g_printf(_("\nCurrent info for %s %s:\n"), dp->host->hostname, dp->name);
778 if (ISSET(info.command, FORCE_FULL))
779 g_printf(_(" (Forcing to level 0 dump at next run)\n"));
780 if (ISSET(info.command, FORCE_BUMP))
781 g_printf(_(" (Forcing bump at next run)\n"));
782 if (ISSET(info.command, FORCE_NO_BUMP))
783 g_printf(_(" (Forcing no-bump at next run)\n"));
784 g_printf(_(" Stats: dump rates (kps), Full: %5.1lf, %5.1lf, %5.1lf\n"),
785 info.full.rate[0], info.full.rate[1], info.full.rate[2]);
786 g_printf(_(" Incremental: %5.1lf, %5.1lf, %5.1lf\n"),
787 info.incr.rate[0], info.incr.rate[1], info.incr.rate[2]);
788 g_printf(_(" compressed size, Full: %5.1lf%%,%5.1lf%%,%5.1lf%%\n"),
789 info.full.comp[0]*100, info.full.comp[1]*100, info.full.comp[2]*100);
790 g_printf(_(" Incremental: %5.1lf%%,%5.1lf%%,%5.1lf%%\n"),
791 info.incr.comp[0]*100, info.incr.comp[1]*100, info.incr.comp[2]*100);
793 g_printf(_(" Dumps: lev datestmp tape file origK compK secs\n"));
794 for(lev = 0, sp = &info.inf[0]; lev < 9; lev++, sp++) {
795 if(sp->date < (time_t)0 && sp->label[0] == '\0') continue;
796 tm = localtime(&sp->date);
798 g_printf(_(" %d %04d%02d%02d %-15s %lld %lld %lld %jd\n"),
799 lev, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
801 (long long)sp->filenum,
803 (long long)sp->csize,
806 g_printf(_(" %d BAD-DATE %-15s %lld %lld %lld %jd\n"),
809 (long long)sp->filenum,
811 (long long)sp->csize,
826 diskloop(argc, argv, "info", info_one);
828 for(dp = diskq.head; dp != NULL; dp = dp->next)
832 /* ----------------------------------------------- */
843 if(get_info(hp->hostname, dp->name, &info)) {
844 g_printf(_("new disk %s:%s ignored.\n"), hp->hostname, dp->name);
847 days = next_level0(dp, &info);
849 g_printf(_("Overdue %2d day%s %s:%s\n"),
850 -days, (-days == 1) ? ": " : "s:",
851 hp->hostname, dp->name);
854 g_printf(_("Due today: %s:%s\n"), hp->hostname, dp->name);
857 g_printf(_("Due in %2d day%s %s:%s\n"), days,
858 (days == 1) ? ": " : "s:",
859 hp->hostname, dp->name);
873 diskloop(argc, argv, "due", due_one);
875 for(dp = diskq.head; dp != NULL; dp = dp->next)
879 /* ----------------------------------------------- */
893 if(argc > 4 && strcmp(argv[3],"--days") == 0) {
894 nb_days = atoi(argv[4]);
896 g_printf(_("days must be an integer bigger than 0\n"));
903 runtapes = getconf_int(CNF_RUNTAPES);
904 tp = lookup_last_reusable_tape(0);
907 for ( j=0 ; j < nb_days ; j++ ) {
909 for ( i=0 ; i < runtapes ; i++ ) {
911 g_fprintf(stdout, _("The next Amanda run should go onto "));
913 if (nb_new_tape > 0) {
914 if (nb_new_tape == 1)
915 g_fprintf(stdout, _("1 new tape.\n"));
917 g_fprintf(stdout, _("%d new tapes.\n"), nb_new_tape);
918 g_fprintf(stdout, " ");
921 g_fprintf(stdout, _("tape %s or a new tape.\n"), tp->label);
923 g_fprintf(stdout, " ");
929 tp = lookup_last_reusable_tape(skip);
931 if (nb_new_tape > 0) {
932 if (nb_new_tape == 1)
933 g_fprintf(stdout, _("1 new tape.\n"));
935 g_fprintf(stdout, _("%d new tapes.\n"), nb_new_tape);
939 print_new_tapes(stdout, nb_days * runtapes);
942 /* ----------------------------------------------- */
946 int argc G_GNUC_UNUSED,
947 char ** argv G_GNUC_UNUSED)
950 struct balance_stats {
952 off_t origsize, outsize;
954 int conf_runspercycle, conf_dumpcycle;
955 int seq, runs_per_cycle, overdue, max_overdue;
956 int later, total, balance, distinct;
957 double fseq, disk_dumpcycle;
959 off_t total_balanced, balanced;
963 conf_dumpcycle = getconf_int(CNF_DUMPCYCLE);
964 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
965 later = conf_dumpcycle;
971 } else if (opt_days == 0) {
972 later = conf_dumpcycle;
974 if(later > 10000) later = 10000;
976 if(conf_runspercycle == 0) {
977 runs_per_cycle = conf_dumpcycle;
978 } else if(conf_runspercycle == -1 ) {
979 runs_per_cycle = guess_runs_from_tapelist();
981 runs_per_cycle = conf_runspercycle;
983 if (runs_per_cycle <= 0) {
989 distinct = later + 3;
991 sp = (struct balance_stats *)
992 alloc(SIZEOF(struct balance_stats) * (distinct+1));
994 for(seq=0; seq <= distinct; seq++) {
996 sp[seq].origsize = sp[seq].outsize = (off_t)0;
999 for(dp = diskq.head; dp != NULL; dp = dp->next) {
1000 if(get_info(dp->host->hostname, dp->name, &info)) {
1001 g_printf(_("new disk %s:%s ignored.\n"), dp->host->hostname, dp->name);
1004 if (dp->strategy == DS_NOFULL) {
1007 sp[distinct].disks++;
1008 sp[distinct].origsize += info.inf[0].size/(off_t)unitdivisor;
1009 sp[distinct].outsize += info.inf[0].csize/(off_t)unitdivisor;
1011 sp[balance].disks++;
1012 if(dp->dumpcycle == 0) {
1013 sp[balance].origsize += (info.inf[0].size/(off_t)unitdivisor) * (off_t)runs_per_cycle;
1014 sp[balance].outsize += (info.inf[0].csize/(off_t)unitdivisor) * (off_t)runs_per_cycle;
1017 sp[balance].origsize += (info.inf[0].size/(off_t)unitdivisor) *
1018 (off_t)(conf_dumpcycle / dp->dumpcycle);
1019 sp[balance].outsize += (info.inf[0].csize/(off_t)unitdivisor) *
1020 (off_t)(conf_dumpcycle / dp->dumpcycle);
1023 disk_dumpcycle = (double)dp->dumpcycle;
1024 if(dp->dumpcycle <= 0)
1025 disk_dumpcycle = ((double)conf_dumpcycle) / ((double)runs_per_cycle);
1027 seq = next_level0(dp, &info);
1028 fseq = seq + 0.0001;
1032 if (-seq > max_overdue)
1035 fseq = seq + 0.0001;
1042 sp[seq].origsize += info.inf[0].size/(off_t)unitdivisor;
1043 sp[seq].outsize += info.inf[0].csize/(off_t)unitdivisor;
1047 sp[total].origsize += info.inf[0].size/(off_t)unitdivisor;
1048 sp[total].outsize += info.inf[0].csize/(off_t)unitdivisor;
1051 /* See, if there's another run in this dumpcycle */
1052 fseq += disk_dumpcycle;
1054 } while (seq < later);
1057 if(sp[total].outsize == (off_t)0 && sp[later].outsize == (off_t)0) {
1058 g_printf(_("\nNo data to report on yet.\n"));
1063 balanced = sp[balance].outsize / (off_t)runs_per_cycle;
1064 if(conf_dumpcycle == later) {
1065 total_balanced = sp[total].outsize / (off_t)runs_per_cycle;
1068 total_balanced = (((sp[total].outsize/(off_t)1024) * (off_t)conf_dumpcycle)
1069 / (off_t)(runs_per_cycle * later)) * (off_t)1024;
1073 g_printf(_("\n due-date #fs orig %cB out %cB balance\n"),
1074 displayunit[0], displayunit[0]);
1075 g_printf("----------------------------------------------\n");
1076 for(seq = 0; seq < later; seq++) {
1077 if(sp[seq].disks == 0 &&
1078 ((seq > 0 && sp[seq-1].disks == 0) ||
1079 ((seq < later-1) && sp[seq+1].disks == 0))) {
1087 g_printf(_("%-9.9s %3d %10lld %10lld "),
1088 seqdatestr(seq), sp[seq].disks,
1089 (long long)sp[seq].origsize,
1090 (long long)sp[seq].outsize);
1091 if(!sp[seq].outsize) g_printf(" --- \n");
1092 else g_printf(_("%+8.1lf%%\n"),
1093 (((double)sp[seq].outsize - (double)balanced) * 100.0 /
1098 if(sp[later].disks != 0) {
1099 g_printf(_("later %3d %10lld %10lld "),
1101 (long long)sp[later].origsize,
1102 (long long)sp[later].outsize);
1103 if(!sp[later].outsize) g_printf(" --- \n");
1104 else g_printf(_("%+8.1lf%%\n"),
1105 (((double)sp[later].outsize - (double)balanced) * 100.0 /
1108 g_printf("----------------------------------------------\n");
1109 g_printf(_("TOTAL %3d %10lld %10lld %9lld\n"),
1111 (long long)sp[total].origsize,
1112 (long long)sp[total].outsize,
1113 (long long)total_balanced);
1114 if (sp[balance].origsize != sp[total].origsize ||
1115 sp[balance].outsize != sp[total].outsize ||
1116 balanced != total_balanced) {
1117 g_printf(_("BALANCED %10lld %10lld %9lld\n"),
1118 (long long)sp[balance].origsize,
1119 (long long)sp[balance].outsize,
1120 (long long)balanced);
1122 if (sp[distinct].disks != sp[total].disks) {
1123 g_printf(_("DISTINCT %3d %10lld %10lld\n"),
1125 (long long)sp[distinct].origsize,
1126 (long long)sp[distinct].outsize);
1128 g_printf(plural(_(" (estimated %d run per dumpcycle)\n"),
1129 _(" (estimated %d runs per dumpcycle)\n"),
1133 g_printf(plural(_(" (%d filesystem overdue."),
1134 _(" (%d filesystems overdue."), overdue),
1136 g_printf(plural(_(" The most being overdue %d day.)\n"),
1137 _(" The most being overdue %d days.)\n"), max_overdue),
1144 /* ----------------------------------------------- */
1152 char *sort_order = NULL;
1153 find_result_t *output_find;
1155 char **output_find_log;
1160 _("%s: expecting \"find [--sort <hkdlpbfw>] [hostname [<disk>]]*\"\n"),
1166 sort_order = newstralloc(sort_order, DEFAULT_SORT_ORDER);
1168 size_t i, valid_sort=1;
1170 for(i = strlen(opt_sort); i > 0; i--) {
1171 switch (opt_sort[i - 1]) {
1189 default: valid_sort=0;
1193 sort_order = newstralloc(sort_order, opt_sort);
1195 g_printf(_("Invalid sort order: %s\n"), opt_sort);
1196 g_printf(_("Use default sort order: %s\n"), sort_order);
1200 errstr = match_disklist(&diskq, argc-(start_argc-1), argv+(start_argc-1));
1202 /* check all log file exists */
1203 output_find_log = find_log();
1204 for (name = output_find_log; *name != NULL; name++) {
1207 amfree(output_find_log);
1209 output_find = find_dump(&diskq); /* Add deleted dump to diskq */
1210 if(argc-(start_argc-1) > 0) {
1211 find_result_t *afind = NULL;
1212 find_result_t *afind_next = NULL;
1213 find_result_t *new_output_find = NULL;
1217 errstr = match_disklist(&diskq, argc-(start_argc-1),
1218 argv+(start_argc-1));
1220 g_printf("%s", errstr);
1223 for (afind = output_find; afind; afind = afind_next) {
1224 afind_next = afind->next;
1225 dp = lookup_disk(afind->hostname, afind->diskname);
1227 afind->next = new_output_find;
1228 new_output_find = afind;
1233 output_find = new_output_find;
1234 } else if (errstr) {
1235 g_printf("%s", errstr);
1239 sort_find_result(sort_order, &output_find);
1240 print_find_result(output_find);
1241 free_find_result(&output_find);
1247 /* ------------------------ */
1255 GSList * file_list = NULL;
1259 flags = CMDLINE_PARSE_DATESTAMP;
1260 if (allow_empty) flags |= CMDLINE_EMPTY_TO_WILDCARD;
1261 dumplist = cmdline_parse_dumpspecs(argc, argv, flags);
1263 file_list = cmdline_match_holding(dumplist);
1264 dumpspec_list_free(dumplist);
1269 /* Given a file header, find the history element in curinfo most likely
1270 * corresponding to that dump (this is not an exact science).
1272 * @param info: the info_t element for this DLE
1273 * @param file: the header of the file
1274 * @returns: index of the matching history element, or -1 if not found
1277 holding_file_find_history(
1281 int matching_hist_idx = -1;
1285 /* Begin by trying to find the history element matching this dump.
1286 * The datestamp on the dump is for the entire run of amdump, while the
1287 * 'date' in the history element of 'info' is the time the dump itself
1288 * began. A matching history element, then, is the earliest element
1289 * with a 'date' equal to or later than the date of the dumpfile.
1291 * We compare using formatted datestamps; even using seconds since epoch,
1292 * we would still face timezone issues, and have to do a reverse (timezone
1293 * to gmt) translation.
1296 /* get to the end of the history list and search backward */
1297 for (nhist = 0; info->history[nhist].level > -1; nhist++) /* empty loop */;
1298 for (i = nhist-1; i > -1; i--) {
1299 char *info_datestamp = get_timestamp_from_time(info->history[i].date);
1300 int order = strcmp(file->datestamp, info_datestamp);
1301 amfree(info_datestamp);
1304 /* only a match if the levels are equal */
1305 if (info->history[i].level == file->dumplevel) {
1306 matching_hist_idx = i;
1312 return matching_hist_idx;
1315 /* A holding file is 'outdated' if a subsequent dump of the same DLE was made
1316 * at the same level or a lower leve; for example, a level 2 dump is outdated if
1317 * there is a subsequent level 2, or a subsequent level 0.
1319 * @param file: the header of the file
1320 * @returns: true if the file is outdated
1323 holding_file_is_outdated(
1327 int matching_hist_idx;
1329 if (get_info(file->name, file->disk, &info) == -1) {
1330 return 0; /* assume it's not outdated */
1333 /* if the last level is less than the level of this dump, then
1335 if (info.last_level < file->dumplevel)
1338 /* otherwise, we need to see if this dump is the last at its level */
1339 matching_hist_idx = holding_file_find_history(&info, file);
1340 if (matching_hist_idx == -1) {
1341 return 0; /* assume it's not outdated */
1344 /* compare the date of the history element with the most recent date
1345 * for this level. If they match, then this is the last dump at this
1346 * level, and we checked above for more recent lower-level dumps, so
1347 * the dump is not outdated. */
1348 if (info.history[matching_hist_idx].date ==
1349 info.inf[info.history[matching_hist_idx].level].date) {
1357 remove_holding_file_from_catalog(
1360 static int warnings_printed; /* only print once per invocation */
1363 int matching_hist_idx = -1;
1364 history_t matching_hist; /* will be a copy */
1367 if (!holding_file_get_dumpfile(filename, &file)) {
1368 g_printf(_("Could not read holding file %s\n"), filename);
1372 if (get_info(file.name, file.disk, &info) == -1) {
1373 g_printf(_("WARNING: No curinfo record for %s:%s\n"), file.name, file.disk);
1374 dumpfile_free_data(&file);
1375 return 1; /* not an error */
1378 matching_hist_idx = holding_file_find_history(&info, &file);
1380 if (matching_hist_idx == -1) {
1381 g_printf(_("WARNING: No dump matching %s found in curinfo.\n"), filename);
1382 dumpfile_free_data(&file);
1383 return 1; /* not an error */
1387 matching_hist = info.history[matching_hist_idx];
1389 /* Remove the history element itself before doing the stats */
1390 for (i = matching_hist_idx; i < NB_HISTORY; i++) {
1391 info.history[i] = info.history[i+1];
1393 info.history[NB_HISTORY].level = -1;
1395 /* Remove stats for that history element, if necessary. Doing so
1396 * will result in an inconsistent set of backups, so we warn the
1397 * user and adjust last_level to make the next dump get us a
1398 * consistent picture. */
1399 if (matching_hist.date == info.inf[matching_hist.level].date) {
1400 /* search for an earlier dump at this level */
1401 for (i = matching_hist_idx; info.history[i].level > -1; i++) {
1402 if (info.history[i].level == matching_hist.level)
1406 if (info.history[i].level < 0) {
1407 /* not found => zero it out */
1408 info.inf[matching_hist.level].date = (time_t)-1; /* flag as not set */
1409 info.inf[matching_hist.level].label[0] = '\0';
1411 /* found => reconstruct stats as best we can */
1412 info.inf[matching_hist.level].size = info.history[i].size;
1413 info.inf[matching_hist.level].csize = info.history[i].csize;
1414 info.inf[matching_hist.level].secs = info.history[i].secs;
1415 info.inf[matching_hist.level].date = info.history[i].date;
1416 info.inf[matching_hist.level].filenum = 0; /* we don't know */
1417 info.inf[matching_hist.level].label[0] = '\0'; /* we don't know */
1420 /* set last_level to the level we just deleted, and set command
1421 * appropriately to make sure planner does a new dump at this level
1423 info.last_level = matching_hist.level;
1424 if (info.last_level == 0) {
1425 g_printf(_("WARNING: Deleting the most recent full dump; forcing a full dump at next run.\n"));
1426 SET(info.command, FORCE_FULL);
1428 g_printf(_("WARNING: Deleting the most recent level %d dump; forcing a level %d dump or \nWARNING: lower at next run.\n"),
1429 info.last_level, info.last_level);
1430 SET(info.command, FORCE_NO_BUMP);
1433 /* Search for and display any subsequent runs that depended on this one */
1434 warnings_printed = 0;
1435 for (i = matching_hist_idx-1; i >= 0; i--) {
1437 if (info.history[i].level <= matching_hist.level) break;
1439 datestamp = get_timestamp_from_time(info.history[i].date);
1440 g_printf(_("WARNING: Level %d dump made %s can no longer be accurately restored.\n"),
1441 info.history[i].level, datestamp);
1444 warnings_printed = 1;
1446 if (warnings_printed)
1447 g_printf(_("WARNING: (note, dates shown above are for dumps, and may be later than the\nWARNING: corresponding run date)\n"));
1450 /* recalculate consecutive_runs based on the history: find the first run
1451 * at this level, and then count the consecutive runs at that level. This
1452 * number may be zero (if we just deleted the last run at this level) */
1453 info.consecutive_runs = 0;
1454 for (i = 0; info.history[i].level >= 0; i++) {
1455 if (info.history[i].level == info.last_level) break;
1457 while (info.history[i+info.consecutive_runs].level == info.last_level)
1458 info.consecutive_runs++;
1460 /* this function doesn't touch the performance stats */
1462 /* write out the changes */
1463 if (put_info(file.name, file.disk, &info) == -1) {
1464 g_printf(_("Could not write curinfo record for %s:%s\n"), file.name, file.disk);
1465 dumpfile_free_data(&file);
1469 dumpfile_free_data(&file);
1480 enum { HOLDING_USAGE, HOLDING_LIST, HOLDING_DELETE } action = HOLDING_USAGE;
1482 int outdated_list = 0;
1486 action = HOLDING_USAGE;
1487 else if (strcmp(argv[3], "list") == 0 && argc >= 4)
1488 action = HOLDING_LIST;
1489 else if (strcmp(argv[3], "delete") == 0 && argc > 4)
1490 action = HOLDING_DELETE;
1495 _("%s: expecting \"holding list [-l] [-d]\" or \"holding delete <host> [ .. ]\"\n"),
1501 long_list = opt_long;
1502 outdated_list = opt_outdated;
1503 argc -= 4; argv += 4;
1507 g_printf("%-10s %-2s %-4s %s\n",
1508 _("size (kB)"), _("lv"), _("outd"), _("dump specification"));
1511 file_list = get_file_list(argc, argv, 1);
1512 for (li = file_list; li != NULL; li = li->next) {
1516 if (!holding_file_get_dumpfile((char *)li->data, &file)) {
1517 g_fprintf(stderr, _("Error reading %s\n"), (char *)li->data);
1521 is_outdated = holding_file_is_outdated(&file);
1523 dumpstr = cmdline_format_dumpspec_components(file.name, file.disk, file.datestamp, NULL);
1524 /* only print this entry if we're printing everything, or if it's outdated and
1525 * we're only printing outdated files (-o) */
1526 if (!outdated_list || is_outdated) {
1528 g_printf("%-10lld %-2d %-4s %s\n",
1529 (long long)holding_file_size((char *)li->data, 0),
1531 is_outdated? " *":"",
1534 g_printf("%s\n", dumpstr);
1538 dumpfile_free_data(&file);
1540 slist_free_full(file_list, g_free);
1543 case HOLDING_DELETE:
1544 argc -= 4; argv += 4;
1546 file_list = get_file_list(argc, argv, 0);
1547 for (li = file_list; li != NULL; li = li->next) {
1548 g_fprintf(stderr, _("Deleting '%s'\n"), (char *)li->data);
1549 /* remove it from the catalog */
1550 if (!remove_holding_file_from_catalog((char *)li->data))
1554 if (!holding_file_unlink((char *)li->data)) {
1555 error(_("Could not delete '%s'"), (char *)li->data);
1558 slist_free_full(file_list, g_free);
1564 /* ------------------------ */
1567 /* shared code with planner.c */
1573 gint64 bump = getconf_int64(CNF_BUMPSIZE);
1574 double mult = getconf_real(CNF_BUMPMULT);
1577 bump = (int)((double)bump * mult);
1587 int conf_bumppercent = getconf_int(CNF_BUMPPERCENT);
1588 double conf_bumpmult = getconf_real(CNF_BUMPMULT);
1590 (void)argc; /* Quiet unused parameter warning */
1591 (void)argv; /* Quiet unused parameter warning */
1593 g_printf(_("Current bump parameters:\n"));
1594 if(conf_bumppercent == 0) {
1595 g_printf(_(" bumpsize %5jd KB\t- minimum savings (threshold) to bump level 1 -> 2\n"),
1596 (intmax_t)getconf_int64(CNF_BUMPSIZE));
1597 g_printf(_(" bumpdays %5d\t- minimum days at each level\n"),
1598 getconf_int(CNF_BUMPDAYS));
1599 g_printf(_(" bumpmult %5.5lg\t- threshold = bumpsize * bumpmult**(level-1)\n\n"),
1602 g_printf(_(" Bump -> To Threshold\n"));
1603 for(l = 1; l < 9; l++)
1604 g_printf(_("\t%d -> %d %9d KB\n"), l, l+1, bump_thresh(l));
1608 double bumppercent = (double)conf_bumppercent;
1610 g_printf(_(" bumppercent %3d %%\t- minimum savings (threshold) to bump level 1 -> 2\n"),
1612 g_printf(_(" bumpdays %5d\t- minimum days at each level\n"),
1613 getconf_int(CNF_BUMPDAYS));
1614 g_printf(_(" bumpmult %5.5lg\t- threshold = disk_size * bumppercent * bumpmult**(level-1)\n\n"),
1616 g_printf(_(" Bump -> To Threshold\n"));
1617 for(l = 1; l < 9; l++) {
1618 g_printf(_("\t%d -> %d %7.2lf %%\n"), l, l+1, bumppercent);
1619 bumppercent *= conf_bumpmult;
1620 if(bumppercent >= 100.000) { bumppercent = 100.0;}
1626 /* ----------------------------------------------- */
1628 void export_one(disk_t *dp);
1637 char hostname[MAX_HOSTNAME_LENGTH+1];
1640 g_printf(_("CURINFO Version %s CONF %s\n"), VERSION, getconf_str(CNF_ORG));
1643 if(gethostname(hostname, SIZEOF(hostname)-1) == -1) {
1644 error(_("could not determine host name: %s\n"), strerror(errno));
1647 hostname[SIZEOF(hostname)-1] = '\0';
1648 g_printf(_("# Generated by:\n# host: %s\n# date: %s"),
1649 hostname, ctime(&curtime));
1651 g_printf(_("# command:"));
1652 for(i = 0; i < argc; i++)
1653 g_printf(_(" %s"), argv[i]);
1655 g_printf(_("\n# This file can be merged back in with \"amadmin import\".\n"));
1656 g_printf(_("# Edit only with care.\n"));
1659 diskloop(argc, argv, "export", export_one);
1660 else for(dp = diskq.head; dp != NULL; dp = dp->next)
1670 char *qhost, *qdisk;
1672 if(get_info(dp->host->hostname, dp->name, &info)) {
1673 g_fprintf(stderr, _("Warning: no curinfo record for %s:%s\n"),
1674 dp->host->hostname, dp->name);
1677 qhost = quote_string(dp->host->hostname);
1678 qdisk = quote_string(dp->name);
1679 g_printf(_("host: %s\ndisk: %s\n"), qhost, qdisk);
1680 g_printf(_("command: %u\n"), info.command);
1681 g_printf(_("last_level: %d\n"),info.last_level);
1682 g_printf(_("consecutive_runs: %d\n"),info.consecutive_runs);
1683 g_printf(_("full-rate:"));
1684 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.full.rate[i]);
1685 g_printf(_("\nfull-comp:"));
1686 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.full.comp[i]);
1688 g_printf(_("\nincr-rate:"));
1689 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.incr.rate[i]);
1690 g_printf(_("\nincr-comp:"));
1691 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.incr.comp[i]);
1693 for(l=0;l<DUMP_LEVELS;l++) {
1694 if(info.inf[l].date < (time_t)0 && info.inf[l].label[0] == '\0') continue;
1695 g_printf(_("stats: %d %lld %lld %jd %jd %lld %s\n"), l,
1696 (long long)info.inf[l].size,
1697 (long long)info.inf[l].csize,
1698 (intmax_t)info.inf[l].secs,
1699 (intmax_t)info.inf[l].date,
1700 (long long)info.inf[l].filenum,
1703 for(l=0;info.history[l].level > -1;l++) {
1704 g_printf(_("history: %d %lld %lld %jd\n"),
1705 info.history[l].level,
1706 (long long)info.history[l].size,
1707 (long long)info.history[l].csize,
1708 (intmax_t)info.history[l].date);
1715 /* ----------------------------------------------- */
1717 int import_one(void);
1718 char *impget_line(void);
1736 (void)argc; /* Quiet unused parameter warning */
1737 (void)argv; /* Quiet unused parameter warning */
1739 /* process header line */
1741 if((line = agets(stdin)) == NULL) {
1742 g_fprintf(stderr, _("%s: empty input.\n"), get_pname());
1750 if(strncmp_const_skip(s - 1, "CURINFO Version", s, ch) != 0) {
1754 skip_whitespace(s, ch);
1756 || sscanf(s - 1, "%d.%d.%d", &vers_maj, &vers_min, &vers_patch) != 3) {
1758 if (sscanf(s - 1, "%d.%d", &vers_maj, &vers_min) != 2) {
1763 skip_integer(s, ch); /* skip over major */
1768 skip_integer(s, ch); /* skip over minor */
1769 if (vers_patch != -1) {
1774 skip_integer(s, ch); /* skip over patch */
1783 skip_non_whitespace(s, ch);
1787 skip_whitespace(s, ch); /* find the org keyword */
1788 if(ch == '\0' || strncmp_const_skip(s - 1, "CONF", s, ch) != 0) {
1794 skip_whitespace(s, ch); /* find the org string */
1801 newer = (vers_maj != VERSION_MAJOR)? vers_maj > VERSION_MAJOR :
1802 (vers_min != VERSION_MINOR)? vers_min > VERSION_MINOR :
1803 vers_patch > VERSION_PATCH;
1806 _("%s: WARNING: input is from newer Amanda version: %d.%d.%d.\n"),
1807 get_pname(), vers_maj, vers_min, vers_patch);
1810 if(strcmp(org, getconf_str(CNF_ORG)) != 0) {
1811 g_fprintf(stderr, _("%s: WARNING: input is from different org: %s\n"),
1824 /*@i@*/ amfree(line);
1825 g_fprintf(stderr, _("%s: bad CURINFO header line in input: %s.\n"),
1827 g_fprintf(stderr, _(" Was the input in \"amadmin export\" format?\n"));
1842 char *hostname = NULL;
1843 char *diskname = NULL;
1844 long long off_t_tmp;
1845 long long time_t_tmp;
1847 memset(&info, 0, SIZEOF(info_t));
1849 for(level = 0; level < DUMP_LEVELS; level++) {
1850 info.inf[level].date = (time_t)-1;
1853 /* get host: disk: command: lines */
1855 hostname = diskname = NULL;
1857 if((line = impget_line()) == NULL) return 0; /* nothing there */
1861 skip_whitespace(s, ch);
1862 if(ch == '\0' || strncmp_const_skip(s - 1, "host:", s, ch) != 0) goto parse_err;
1863 skip_whitespace(s, ch);
1864 if(ch == '\0') goto parse_err;
1866 skip_quoted_string(s, ch);
1868 hostname = unquote_string(fp);
1871 skip_whitespace(s, ch);
1874 if((line = impget_line()) == NULL) goto shortfile_err;
1877 skip_whitespace(s, ch);
1879 if(strncmp_const_skip(s - 1, "disk:", s, ch) != 0) goto parse_err;
1880 skip_whitespace(s, ch);
1881 if(ch == '\0') goto parse_err;
1883 skip_quoted_string(s, ch);
1885 diskname = unquote_string(fp);
1889 if((line = impget_line()) == NULL) goto shortfile_err;
1890 if(sscanf(line, "command: %u", &info.command) != 1) goto parse_err;
1892 /* get last_level and consecutive_runs */
1895 if((line = impget_line()) == NULL) goto shortfile_err;
1896 rc = sscanf(line, "last_level: %d", &info.last_level);
1899 if((line = impget_line()) == NULL) goto shortfile_err;
1900 if(sscanf(line, "consecutive_runs: %d", &info.consecutive_runs) != 1) goto parse_err;
1902 if((line = impget_line()) == NULL) goto shortfile_err;
1905 /* get rate: and comp: lines for full dumps */
1907 rc = sscanf(line, "full-rate: %lf %lf %lf",
1908 &info.full.rate[0], &info.full.rate[1], &info.full.rate[2]);
1909 if(rc != 3) goto parse_err;
1912 if((line = impget_line()) == NULL) goto shortfile_err;
1913 rc = sscanf(line, "full-comp: %lf %lf %lf",
1914 &info.full.comp[0], &info.full.comp[1], &info.full.comp[2]);
1915 if(rc != 3) goto parse_err;
1917 /* get rate: and comp: lines for incr dumps */
1920 if((line = impget_line()) == NULL) goto shortfile_err;
1921 rc = sscanf(line, "incr-rate: %lf %lf %lf",
1922 &info.incr.rate[0], &info.incr.rate[1], &info.incr.rate[2]);
1923 if(rc != 3) goto parse_err;
1926 if((line = impget_line()) == NULL) goto shortfile_err;
1927 rc = sscanf(line, "incr-comp: %lf %lf %lf",
1928 &info.incr.comp[0], &info.incr.comp[1], &info.incr.comp[2]);
1929 if(rc != 3) goto parse_err;
1931 /* get stats for dump levels */
1935 if((line = impget_line()) == NULL) goto shortfile_err;
1936 if(strncmp_const(line, "//") == 0) {
1940 if(strncmp_const(line, "history:") == 0) {
1944 memset(&onestat, 0, SIZEOF(onestat));
1949 skip_whitespace(s, ch);
1950 if(ch == '\0' || strncmp_const_skip(s - 1, "stats:", s, ch) != 0) {
1954 skip_whitespace(s, ch);
1955 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1958 skip_integer(s, ch);
1960 skip_whitespace(s, ch);
1961 if(ch == '\0' || sscanf(s - 1, "%lld", &off_t_tmp) != 1) {
1964 onestat.size = (off_t)off_t_tmp;
1965 skip_integer(s, ch);
1967 skip_whitespace(s, ch);
1968 if(ch == '\0' || sscanf(s - 1, "%lld", &off_t_tmp) != 1) {
1971 onestat.csize = (off_t)off_t_tmp;
1972 skip_integer(s, ch);
1974 skip_whitespace(s, ch);
1975 if(ch == '\0' || sscanf(s - 1, "%lld", &time_t_tmp) != 1) {
1978 onestat.secs = (time_t)time_t_tmp;
1979 skip_integer(s, ch);
1981 skip_whitespace(s, ch);
1982 if(ch == '\0' || sscanf(s - 1, "%lld", &time_t_tmp) != 1) {
1985 /* time_t not guarranteed to be long */
1986 /*@i1@*/ onestat.date = (time_t)time_t_tmp;
1987 skip_integer(s, ch);
1989 skip_whitespace(s, ch);
1991 if(sscanf(s - 1, "%lld", &off_t_tmp) != 1) {
1994 onestat.filenum = (off_t)off_t_tmp;
1995 skip_integer(s, ch);
1997 skip_whitespace(s, ch);
1999 if (onestat.filenum != 0)
2001 onestat.label[0] = '\0';
2003 strncpy(onestat.label, s - 1, SIZEOF(onestat.label)-1);
2004 onestat.label[SIZEOF(onestat.label)-1] = '\0';
2008 if(level < 0 || level > 9) goto parse_err;
2010 info.inf[level] = onestat;
2013 for(i=0;i<=NB_HISTORY;i++) {
2014 info.history[i].level = -2;
2017 history_t onehistory;
2019 if(line[0] == '/' && line[1] == '/') {
2020 info.history[nb_history].level = -2;
2024 memset(&onehistory, 0, SIZEOF(onehistory));
2027 if(strncmp_const_skip(line, "history:", s, ch) != 0) {
2031 skip_whitespace(s, ch);
2032 if(ch == '\0' || sscanf((s - 1), "%d", &onehistory.level) != 1) {
2035 skip_integer(s, ch);
2037 skip_whitespace(s, ch);
2038 if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
2041 onehistory.size = (off_t)off_t_tmp;
2042 skip_integer(s, ch);
2044 skip_whitespace(s, ch);
2045 if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
2048 onehistory.csize = (off_t)off_t_tmp;
2049 skip_integer(s, ch);
2051 skip_whitespace(s, ch);
2052 if((ch == '\0') || sscanf((s - 1), "%lld", &time_t_tmp) != 1) {
2055 /* time_t not guarranteed to be long */
2056 /*@i1@*/ onehistory.date = (time_t)time_t_tmp;
2057 skip_integer(s, ch);
2059 info.history[nb_history++] = onehistory;
2061 if((line = impget_line()) == NULL) goto shortfile_err;
2063 /*@i@*/ amfree(line);
2065 /* got a full record, now write it out to the database */
2067 if(put_info(hostname, diskname, &info)) {
2068 g_fprintf(stderr, _("%s: error writing record for %s:%s\n"),
2069 get_pname(), hostname, diskname);
2076 /*@i@*/ amfree(line);
2079 g_fprintf(stderr, _("%s: parse error reading import record.\n"), get_pname());
2083 /*@i@*/ amfree(line);
2086 g_fprintf(stderr, _("%s: short file reading import record.\n"), get_pname());
2097 for(; (line = agets(stdin)) != NULL; free(line)) {
2101 skip_whitespace(s, ch);
2103 /* ignore comment lines */
2106 /* found non-blank, return line */
2109 /* otherwise, a blank line, so keep going */
2112 g_fprintf(stderr, _("%s: reading stdin: %s\n"),
2113 get_pname(), strerror(errno));
2118 /* ----------------------------------------------- */
2126 dumptype_t *dtype = lookup_dumptype(dp->dtype_name);
2131 g_printf("line %d (%s):\n", dp->line, dp->filename);
2133 g_printf(" host %s:\n", hp->hostname);
2134 g_printf(" interface %s\n",
2135 interface_name(ip->config)[0] ? interface_name(ip->config) : "default");
2136 g_printf(" disk %s:\n", dp->name);
2137 if (dp->device) g_printf(" device %s\n", dp->device);
2139 g_printf(" program \"%s\"\n", dp->program);
2140 if (dp->application)
2141 g_printf(" application \"%s\"\n", dp->application);
2143 dump_dumptype(dtype, " ", print_default, print_source);
2145 g_printf(" spindle %d\n", dp->spindle);
2158 diskloop(argc, argv, "disklist", disklist_one);
2160 for(dp = diskq.head; dp != NULL; dp = dp->next)
2164 /* ----------------------------------------------- */
2168 int argc G_GNUC_UNUSED,
2169 char ** argv G_GNUC_UNUSED)
2173 GHashTable *seen = g_hash_table_new(g_str_hash, g_str_equal);
2175 /* enumerate all hosts, skipping those that have been seen (since
2176 * there may be more than one DLE on a host */
2177 for(dp = diskq.head; dp != NULL; dp = dp->next) {
2178 char *hostname = dp->host->hostname;
2179 if (g_hash_table_lookup(seen, hostname))
2181 g_printf("%s\n", hostname);
2182 g_hash_table_insert(seen, hostname, &sentinel);
2184 g_hash_table_destroy(seen);
2187 /* ----------------------------------------------- */
2191 int argc G_GNUC_UNUSED,
2192 char ** argv G_GNUC_UNUSED)
2196 for(dp = diskq.head; dp != NULL; dp = dp->next)
2197 g_printf("%s %s\n", dp->host->hostname, dp->name);
2200 /* ----------------------------------------------- */
2209 (void)argc; /* Quiet unused parameter warning */
2210 (void)argv; /* Quiet unused parameter warning */
2212 for(i = 0; version_info[i] != NULL; i++)
2213 g_printf("%s", version_info[i]);
2218 int argc G_GNUC_UNUSED,
2219 char **argv G_GNUC_UNUSED)
2221 dump_configuration(print_default, print_source);