2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
4 * Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation, and that the name of U.M. not be used in advertising or
12 * publicity pertaining to distribution of the software without specific,
13 * written prior permission. U.M. makes no representations about the
14 * suitability of this software for any purpose. It is provided "as is"
15 * without express or implied warranty.
17 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 * Author: James da Silva, Systems Design and Analysis Group
25 * Computer Science Department
26 * University of Maryland at College Park
29 * $Id: amadmin.c,v 1.124 2006/07/26 15:17:37 martinea Exp $
31 * controlling process for the Amanda backup system
44 #include "timestamp.h"
45 #include "server_util.h"
50 int main(int argc, char **argv);
52 static void estimate(int argc, char **argv);
53 static void estimate_one(disk_t *dp);
54 void force(int argc, char **argv);
55 void force_one(disk_t *dp);
56 void unforce(int argc, char **argv);
57 void unforce_one(disk_t *dp);
58 void force_bump(int argc, char **argv);
59 void force_bump_one(disk_t *dp);
60 void force_no_bump(int argc, char **argv);
61 void force_no_bump_one(disk_t *dp);
62 void unforce_bump(int argc, char **argv);
63 void unforce_bump_one(disk_t *dp);
64 void reuse(int argc, char **argv);
65 void noreuse(int argc, char **argv);
66 void info(int argc, char **argv);
67 void info_one(disk_t *dp);
68 void due(int argc, char **argv);
69 void due_one(disk_t *dp);
70 void find(int argc, char **argv);
71 void holding(int argc, char **argv);
72 void delete(int argc, char **argv);
73 void delete_one(disk_t *dp);
74 void balance(int argc, char **argv);
75 void tape(int argc, char **argv);
76 void bumpsize(int argc, char **argv);
77 void diskloop(int argc, char **argv, char *cmdname, void (*func)(disk_t *dp));
78 char *seqdatestr(int seq);
79 static int next_level0(disk_t *dp, info_t *info);
80 int bump_thresh(int level);
81 void export_db(int argc, char **argv);
82 void import_db(int argc, char **argv);
83 void hosts(int argc, char **argv);
84 void dles(int argc, char **argv);
85 void disklist(int argc, char **argv);
86 void disklist_one(disk_t *dp);
87 void show_version(int argc, char **argv);
88 static void show_config(int argc, char **argv);
90 static char *conf_tapelist = NULL;
91 static char *displayunit;
92 static long int unitdivisor;
93 static gboolean print_default = 1;
94 static gboolean print_source = 0;
95 static int opt_days = -1;
96 static char *opt_sort = NULL;
97 static gboolean exact_match = FALSE;
98 static gboolean opt_long = 0;
99 static gboolean opt_outdated = 0;
101 static const struct {
103 void (*fn)(int, char **);
106 { "version", show_version,
107 T_("\t\t\t\t\t# Show version info.") },
108 { "config", show_config,
109 T_("\t\t\t\t\t# Show configuration.") },
110 { "estimate", estimate,
111 T_(" [<hostname> [<disks>]* ]*\t# Print server estimate.") },
113 T_(" [<hostname> [<disks>]* ]+\t\t# Force level 0 at next run.") },
114 { "unforce", unforce,
115 T_(" [<hostname> [<disks>]* ]+\t# Clear force command.") },
116 { "force-bump", force_bump,
117 T_(" [<hostname> [<disks>]* ]+\t# Force bump at next run.") },
118 { "force-no-bump", force_no_bump,
119 T_(" [<hostname> [<disks>]* ]+\t# Force no-bump at next run.") },
120 { "unforce-bump", unforce_bump,
121 T_(" [<hostname> [<disks>]* ]+\t# Clear bump command.") },
122 { "disklist", disklist,
123 T_(" [<hostname> [<disks>]* ]*\t# Debug disklist entries.") },
125 T_("\t\t\t\t\t# Show all distinct hosts in disklist.") },
127 T_("\t\t\t\t\t# Show all dles in disklist, one per line.") },
129 T_(" <tapelabel> ...\t\t # re-use this tape.") },
130 { "no-reuse", noreuse,
131 T_(" <tapelabel> ...\t # never re-use this tape.") },
133 T_(" [<hostname> [<disks>]* ]*\t # Show which tapes these dumps are on.") },
134 { "holding", holding,
135 T_(" {list [ -l ] |delete} [ <hostname> [ <disk> [ <datestamp> [ .. ] ] ] ]+\t # Show or delete holding disk contents.") },
137 T_(" [<hostname> [<disks>]* ]+ # Delete from database.") },
139 T_(" [<hostname> [<disks>]* ]*\t # Show current info records.") },
141 T_(" [<hostname> [<disks>]* ]*\t # Show due date.") },
142 { "balance", balance,
143 T_(" [--days <num>]\t\t # Show nightly dump size balance.") },
145 T_(" [--days <num>]\t\t # Show which tape is due next.") },
146 { "bumpsize", bumpsize,
147 T_("\t\t\t # Show current bump thresholds.") },
148 { "export", export_db,
149 T_(" [<hostname> [<disks>]* ]* # Export curinfo database to stdout.") },
150 { "import", import_db,
151 T_("\t\t\t\t # Import curinfo database from stdin.") },
153 #define NCMDS (int)(sizeof(cmdtab) / sizeof(cmdtab[0]))
155 static struct option long_options[] = {
156 {"version" , 0, NULL, 1},
157 {"no-default" , 0, NULL, 2},
158 {"print-source" , 0, NULL, 3},
159 {"days" , 1, NULL, 4},
160 {"sort" , 1, NULL, 5},
161 {"exact-match" , 0, NULL, 6},
173 config_overrides_t *cfg_ovr = NULL;
176 * Configure program for internationalization:
177 * 1) Only set the message locale for now.
178 * 2) Set textdomain for all amanda related programs to "amanda"
179 * We don't want to be forced to support dozens of message catalogs.
181 setlocale(LC_MESSAGES, "C");
182 textdomain("amanda");
187 set_pname("amadmin");
189 /* Don't die when child closes pipe */
190 signal(SIGPIPE, SIG_IGN);
192 dbopen(DBG_SUBDIR_SERVER);
194 add_amanda_log_handler(amanda_log_stderr);
196 cfg_ovr = extract_commandline_config_overrides(&argc, &argv);
199 int option_index = 0;
201 c = getopt_long(argc, argv, "ld", long_options, &option_index);
208 case 1: printf("amadmin-%s\n", VERSION);
210 case 2: print_default = 0;
212 case 3: print_source = 1;
214 case 4: opt_days = atoi(optarg);
216 case 5: opt_sort = g_strdup(optarg);
218 case 6: exact_match = TRUE;
220 case 'l': opt_long = TRUE;
222 case 'd': opt_outdated = TRUE;
227 argc -= optind-1, argv += optind-1;
229 if(argc < 3) usage();
231 set_config_overrides(cfg_ovr);
232 config_init(CONFIG_INIT_EXPLICIT_NAME, argv[1]);
234 if(strcmp(argv[2],"version") == 0) {
235 show_version(argc, argv);
239 conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
240 read_diskfile(conf_diskfile, &diskq);
241 amfree(conf_diskfile);
243 if (config_errors(NULL) >= CFGERR_WARNINGS) {
244 config_print_errors();
245 if (config_errors(NULL) >= CFGERR_ERRORS) {
246 g_critical(_("errors processing config file"));
250 dbrename(get_config_name(), DBG_SUBDIR_SERVER);
252 check_running_as(RUNNING_AS_DUMPUSER);
254 conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
255 if(read_tapelist(conf_tapelist)) {
256 error(_("could not load tapelist \"%s\""), conf_tapelist);
259 /* conf_tapelist is not freed yet -- it may be used to write the
262 conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
263 if(open_infofile(conf_infofile)) {
264 error(_("could not open info db \"%s\""), conf_infofile);
267 amfree(conf_infofile);
269 displayunit = getconf_str(CNF_DISPLAYUNIT);
270 unitdivisor = getconf_unit_divisor();
272 for (i = 0; i < NCMDS; i++)
273 if (strcmp(argv[2], cmdtab[i].name) == 0) {
274 (*cmdtab[i].fn)(argc, argv);
278 g_fprintf(stderr, _("%s: unknown command \"%s\"\n"), argv[0], argv[2]);
284 amfree(conf_tapelist);
288 free_disklist(&diskq);
299 g_fprintf(stderr, _("\nUsage: %s [--version] [--exact-match] [--no-default] [--print-source] [-o configoption]*\n <conf> <command> {<args>} ...\n"),
301 g_fprintf(stderr, _(" Valid <command>s are:\n"));
302 for (i = 0; i < NCMDS; i++)
303 g_fprintf(stderr, "\t%s%s\n", cmdtab[i].name, _(cmdtab[i].usage));
308 /* ----------------------------------------------- */
310 #define SECS_PER_DAY (24*60*60)
318 static char *dow[7] = {
327 time_t t = today + seq*SECS_PER_DAY;
333 g_snprintf(str, SIZEOF(str),
334 "%2d/%02d %3s", tm->tm_mon+1, tm->tm_mday, _(dow[tm->tm_wday]));
336 strcpy(str, _("BAD DATE"));
342 #define days_diff(a, b) (int)(((b) - (a) + SECS_PER_DAY) / SECS_PER_DAY)
344 /* when is next level 0 due? 0 = tonight, 1 = tommorrow, etc*/
350 if(dp->strategy == DS_NOFULL)
351 return 1; /* fake it */
352 if(info->inf[0].date < (time_t)0)
353 return 0; /* new disk */
355 return dp->dumpcycle - days_diff(info->inf[0].date, today);
358 /* ----------------------------------------------- */
365 void (*func)(disk_t *dp))
372 g_fprintf(stderr,_("%s: expecting \"%s [<hostname> [<disks>]* ]+\"\n"),
373 get_pname(), cmdname);
377 errstr = match_disklist(&diskq, exact_match, argc-3, argv+3);
379 g_printf("%s", errstr);
383 for(dp = diskq.head; dp != NULL; dp = dp->next) {
390 g_fprintf(stderr,_("%s: no disk matched\n"),get_pname());
394 /* ----------------------------------------------- */
401 char *hostname = dp->host->hostname;
402 char *diskname = dp->name;
403 char *qhost = quote_string(hostname);
404 char *qdisk = quote_string(diskname);
409 get_info(hostname, diskname, &info);
411 size = internal_server_estimate(dp, &info, 0, &stats);
413 printf("%s %s %d %jd\n", qhost, qdisk, 0, (intmax_t)size);
416 if (info.last_level > 0) {
417 size = internal_server_estimate(dp, &info, info.last_level, &stats);
419 printf("%s %s %d %jd\n", qhost, qdisk, info.last_level,
424 if (info.last_level > -1) {
425 size = internal_server_estimate(dp, &info, info.last_level+1, &stats);
427 printf("%s %s %d %jd\n", qhost, qdisk, info.last_level+1,
445 diskloop(argc, argv, "estimate", estimate_one);
447 for(dp = diskq.head; dp != NULL; dp = dp->next)
452 /* ----------------------------------------------- */
459 char *hostname = dp->host->hostname;
460 char *diskname = dp->name;
463 get_info(hostname, diskname, &info);
464 SET(info.command, FORCE_FULL);
465 if (ISSET(info.command, FORCE_BUMP)) {
466 CLR(info.command, FORCE_BUMP);
467 g_printf(_("%s: WARNING: %s:%s FORCE_BUMP command was cleared.\n"),
468 get_pname(), hostname, diskname);
470 if(put_info(hostname, diskname, &info) == 0) {
471 if (dp->strategy == DS_INCRONLY) {
472 g_printf(_("%s: %s:%s, full dump done offline, next dump will be at level 1.\n"),
473 get_pname(), hostname, diskname);
475 g_printf(_("%s: %s:%s is set to a forced level 0 at next run.\n"),
476 get_pname(), hostname, diskname);
479 g_fprintf(stderr, _("%s: %s:%s could not be forced.\n"),
480 get_pname(), hostname, diskname);
490 diskloop(argc, argv, "force", force_one);
494 /* ----------------------------------------------- */
501 char *hostname = dp->host->hostname;
502 char *diskname = dp->name;
505 get_info(hostname, diskname, &info);
506 if (ISSET(info.command, FORCE_FULL)) {
507 CLR(info.command, FORCE_FULL);
508 if(put_info(hostname, diskname, &info) == 0){
509 g_printf(_("%s: force command for %s:%s cleared.\n"),
510 get_pname(), hostname, diskname);
513 _("%s: force command for %s:%s could not be cleared.\n"),
514 get_pname(), hostname, diskname);
518 g_printf(_("%s: no force command outstanding for %s:%s, unchanged.\n"),
519 get_pname(), hostname, diskname);
528 diskloop(argc, argv, "unforce", unforce_one);
532 /* ----------------------------------------------- */
539 char *hostname = dp->host->hostname;
540 char *diskname = dp->name;
543 get_info(hostname, diskname, &info);
544 SET(info.command, FORCE_BUMP);
545 if (ISSET(info.command, FORCE_NO_BUMP)) {
546 CLR(info.command, FORCE_NO_BUMP);
547 g_printf(_("%s: WARNING: %s:%s FORCE_NO_BUMP command was cleared.\n"),
548 get_pname(), hostname, diskname);
550 if (ISSET(info.command, FORCE_FULL)) {
551 CLR(info.command, FORCE_FULL);
552 g_printf(_("%s: WARNING: %s:%s FORCE_FULL command was cleared.\n"),
553 get_pname(), hostname, diskname);
555 if(put_info(hostname, diskname, &info) == 0) {
556 g_printf(_("%s: %s:%s is set to bump at next run.\n"),
557 get_pname(), hostname, diskname);
559 g_fprintf(stderr, _("%s: %s:%s could not be forced to bump.\n"),
560 get_pname(), hostname, diskname);
570 diskloop(argc, argv, "force-bump", force_bump_one);
574 /* ----------------------------------------------- */
581 char *hostname = dp->host->hostname;
582 char *diskname = dp->name;
585 get_info(hostname, diskname, &info);
586 SET(info.command, FORCE_NO_BUMP);
587 if (ISSET(info.command, FORCE_BUMP)) {
588 CLR(info.command, FORCE_BUMP);
589 g_printf(_("%s: WARNING: %s:%s FORCE_BUMP command was cleared.\n"),
590 get_pname(), hostname, diskname);
592 if(put_info(hostname, diskname, &info) == 0) {
593 g_printf(_("%s: %s:%s is set to not bump at next run.\n"),
594 get_pname(), hostname, diskname);
596 g_fprintf(stderr, _("%s: %s:%s could not be force to not bump.\n"),
597 get_pname(), hostname, diskname);
607 diskloop(argc, argv, "force-no-bump", force_no_bump_one);
611 /* ----------------------------------------------- */
618 char *hostname = dp->host->hostname;
619 char *diskname = dp->name;
622 get_info(hostname, diskname, &info);
623 if (ISSET(info.command, FORCE_BUMP|FORCE_NO_BUMP)) {
624 CLR(info.command, FORCE_BUMP|FORCE_NO_BUMP);
625 if(put_info(hostname, diskname, &info) == 0) {
626 g_printf(_("%s: bump command for %s:%s cleared.\n"),
627 get_pname(), hostname, diskname);
629 g_fprintf(stderr, _("%s: %s:%s bump command could not be cleared.\n"),
630 get_pname(), hostname, diskname);
634 g_printf(_("%s: no bump command outstanding for %s:%s, unchanged.\n"),
635 get_pname(), hostname, diskname);
645 diskloop(argc, argv, "unforce-bump", unforce_bump_one);
649 /* ----------------------------------------------- */
660 g_fprintf(stderr,_("%s: expecting \"reuse <tapelabel> ...\"\n"),
665 for(count=3; count< argc; count++) {
666 tp = lookup_tapelabel(argv[count]);
668 g_fprintf(stderr, _("reuse: tape label %s not found in tapelist.\n"),
672 if( tp->reuse == 0 ) {
674 g_printf(_("%s: marking tape %s as reusable.\n"),
675 get_pname(), argv[count]);
677 g_fprintf(stderr, _("%s: tape %s already reusable.\n"),
678 get_pname(), argv[count]);
682 if(write_tapelist(conf_tapelist)) {
683 error(_("could not write tapelist \"%s\""), conf_tapelist);
697 g_fprintf(stderr,_("%s: expecting \"no-reuse <tapelabel> ...\"\n"),
702 for(count=3; count< argc; count++) {
703 tp = lookup_tapelabel(argv[count]);
705 g_fprintf(stderr, _("no-reuse: tape label %s not found in tapelist.\n"),
709 if( tp->reuse == 1 ) {
711 g_printf(_("%s: marking tape %s as not reusable.\n"),
712 get_pname(), argv[count]);
714 g_fprintf(stderr, _("%s: tape %s already not reusable.\n"),
715 get_pname(), argv[count]);
719 if(write_tapelist(conf_tapelist)) {
720 error(_("could not write tapelist \"%s\""), conf_tapelist);
726 /* ----------------------------------------------- */
734 char *hostname = dp->host->hostname;
735 char *diskname = dp->name;
738 if(get_info(hostname, diskname, &info)) {
739 g_printf(_("%s: %s:%s NOT currently in database.\n"),
740 get_pname(), hostname, diskname);
745 if(del_info(hostname, diskname)) {
746 error(_("couldn't delete %s:%s from database: %s"),
747 hostname, diskname, strerror(errno));
750 g_printf(_("%s: %s:%s deleted from curinfo database.\n"),
751 get_pname(), hostname, diskname);
761 diskloop(argc, argv, "delete", delete_one);
765 _("%s: NOTE: you'll have to remove these from the disklist yourself.\n"),
769 /* ----------------------------------------------- */
780 get_info(dp->host->hostname, dp->name, &info);
782 g_printf(_("\nCurrent info for %s %s:\n"), dp->host->hostname, dp->name);
783 if (ISSET(info.command, FORCE_FULL))
784 g_printf(_(" (Forcing to level 0 dump at next run)\n"));
785 if (ISSET(info.command, FORCE_BUMP))
786 g_printf(_(" (Forcing bump at next run)\n"));
787 if (ISSET(info.command, FORCE_NO_BUMP))
788 g_printf(_(" (Forcing no-bump at next run)\n"));
789 g_printf(_(" Stats: dump rates (kps), Full: %5.1lf, %5.1lf, %5.1lf\n"),
790 info.full.rate[0], info.full.rate[1], info.full.rate[2]);
791 g_printf(_(" Incremental: %5.1lf, %5.1lf, %5.1lf\n"),
792 info.incr.rate[0], info.incr.rate[1], info.incr.rate[2]);
793 g_printf(_(" compressed size, Full: %5.1lf%%,%5.1lf%%,%5.1lf%%\n"),
794 info.full.comp[0]*100, info.full.comp[1]*100, info.full.comp[2]*100);
795 g_printf(_(" Incremental: %5.1lf%%,%5.1lf%%,%5.1lf%%\n"),
796 info.incr.comp[0]*100, info.incr.comp[1]*100, info.incr.comp[2]*100);
798 g_printf(_(" Dumps: lev datestmp tape file origK compK secs\n"));
799 for(lev = 0, sp = &info.inf[0]; lev < 9; lev++, sp++) {
800 if(sp->date < (time_t)0 && sp->label[0] == '\0') continue;
801 tm = localtime(&sp->date);
803 g_printf(_(" %d %04d%02d%02d %-15s %lld %lld %lld %jd\n"),
804 lev, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
806 (long long)sp->filenum,
808 (long long)sp->csize,
811 g_printf(_(" %d BAD-DATE %-15s %lld %lld %lld %jd\n"),
814 (long long)sp->filenum,
816 (long long)sp->csize,
831 diskloop(argc, argv, "info", info_one);
833 for(dp = diskq.head; dp != NULL; dp = dp->next)
837 /* ----------------------------------------------- */
848 if(get_info(hp->hostname, dp->name, &info)) {
849 g_printf(_("new disk %s:%s ignored.\n"), hp->hostname, dp->name);
852 days = next_level0(dp, &info);
854 g_printf(_("Overdue %2d day%s %s:%s\n"),
855 -days, (-days == 1) ? ": " : "s:",
856 hp->hostname, dp->name);
859 g_printf(_("Due today: %s:%s\n"), hp->hostname, dp->name);
862 g_printf(_("Due in %2d day%s %s:%s\n"), days,
863 (days == 1) ? ": " : "s:",
864 hp->hostname, dp->name);
878 diskloop(argc, argv, "due", due_one);
880 for(dp = diskq.head; dp != NULL; dp = dp->next)
884 /* ----------------------------------------------- */
898 if(argc > 4 && strcmp(argv[3],"--days") == 0) {
899 nb_days = atoi(argv[4]);
901 g_printf(_("days must be an integer bigger than 0\n"));
908 runtapes = getconf_int(CNF_RUNTAPES);
909 tp = lookup_last_reusable_tape(0);
912 for ( j=0 ; j < nb_days ; j++ ) {
914 for ( i=0 ; i < runtapes ; i++ ) {
916 g_fprintf(stdout, _("The next Amanda run should go onto "));
918 if (nb_new_tape > 0) {
919 if (nb_new_tape == 1)
920 g_fprintf(stdout, _("1 new tape.\n"));
922 g_fprintf(stdout, _("%d new tapes.\n"), nb_new_tape);
923 g_fprintf(stdout, " ");
926 g_fprintf(stdout, _("tape %s or a new tape.\n"), tp->label);
928 g_fprintf(stdout, " ");
934 tp = lookup_last_reusable_tape(skip);
936 if (nb_new_tape > 0) {
937 if (nb_new_tape == 1)
938 g_fprintf(stdout, _("1 new tape.\n"));
940 g_fprintf(stdout, _("%d new tapes.\n"), nb_new_tape);
944 print_new_tapes(stdout, nb_days * runtapes);
947 /* ----------------------------------------------- */
951 int argc G_GNUC_UNUSED,
952 char ** argv G_GNUC_UNUSED)
955 struct balance_stats {
957 off_t origsize, outsize;
959 int conf_runspercycle, conf_dumpcycle;
960 int seq, runs_per_cycle, overdue, max_overdue;
961 int later, total, balance, distinct;
962 double fseq, disk_dumpcycle;
964 off_t total_balanced, balanced;
968 conf_dumpcycle = getconf_int(CNF_DUMPCYCLE);
969 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
970 later = conf_dumpcycle;
976 } else if (opt_days == 0) {
977 later = conf_dumpcycle;
979 if(later > 10000) later = 10000;
981 if(conf_runspercycle == 0) {
982 runs_per_cycle = conf_dumpcycle;
983 } else if(conf_runspercycle == -1 ) {
984 runs_per_cycle = guess_runs_from_tapelist();
986 runs_per_cycle = conf_runspercycle;
988 if (runs_per_cycle <= 0) {
994 distinct = later + 3;
996 sp = (struct balance_stats *)
997 alloc(SIZEOF(struct balance_stats) * (distinct+1));
999 for(seq=0; seq <= distinct; seq++) {
1001 sp[seq].origsize = sp[seq].outsize = (off_t)0;
1004 for(dp = diskq.head; dp != NULL; dp = dp->next) {
1005 if(get_info(dp->host->hostname, dp->name, &info)) {
1006 g_printf(_("new disk %s:%s ignored.\n"), dp->host->hostname, dp->name);
1009 if (dp->strategy == DS_NOFULL) {
1012 sp[distinct].disks++;
1013 sp[distinct].origsize += info.inf[0].size/(off_t)unitdivisor;
1014 sp[distinct].outsize += info.inf[0].csize/(off_t)unitdivisor;
1016 sp[balance].disks++;
1017 if(dp->dumpcycle == 0) {
1018 sp[balance].origsize += (info.inf[0].size/(off_t)unitdivisor) * (off_t)runs_per_cycle;
1019 sp[balance].outsize += (info.inf[0].csize/(off_t)unitdivisor) * (off_t)runs_per_cycle;
1022 sp[balance].origsize += (info.inf[0].size/(off_t)unitdivisor) *
1023 (off_t)(conf_dumpcycle / dp->dumpcycle);
1024 sp[balance].outsize += (info.inf[0].csize/(off_t)unitdivisor) *
1025 (off_t)(conf_dumpcycle / dp->dumpcycle);
1028 disk_dumpcycle = (double)dp->dumpcycle;
1029 if(dp->dumpcycle <= 0)
1030 disk_dumpcycle = ((double)conf_dumpcycle) / ((double)runs_per_cycle);
1032 seq = next_level0(dp, &info);
1033 fseq = seq + 0.0001;
1037 if (-seq > max_overdue)
1040 fseq = seq + 0.0001;
1047 sp[seq].origsize += info.inf[0].size/(off_t)unitdivisor;
1048 sp[seq].outsize += info.inf[0].csize/(off_t)unitdivisor;
1052 sp[total].origsize += info.inf[0].size/(off_t)unitdivisor;
1053 sp[total].outsize += info.inf[0].csize/(off_t)unitdivisor;
1056 /* See, if there's another run in this dumpcycle */
1057 fseq += disk_dumpcycle;
1059 } while (seq < later);
1062 if(sp[total].outsize == (off_t)0 && sp[later].outsize == (off_t)0) {
1063 g_printf(_("\nNo data to report on yet.\n"));
1068 balanced = sp[balance].outsize / (off_t)runs_per_cycle;
1069 if(conf_dumpcycle == later) {
1070 total_balanced = sp[total].outsize / (off_t)runs_per_cycle;
1073 total_balanced = (((sp[total].outsize/(off_t)1024) * (off_t)conf_dumpcycle)
1074 / (off_t)(runs_per_cycle * later)) * (off_t)1024;
1078 g_printf(_("\n due-date #fs orig %cB out %cB balance\n"),
1079 displayunit[0], displayunit[0]);
1080 g_printf("----------------------------------------------\n");
1081 for(seq = 0; seq < later; seq++) {
1082 if(sp[seq].disks == 0 &&
1083 ((seq > 0 && sp[seq-1].disks == 0) ||
1084 ((seq < later-1) && sp[seq+1].disks == 0))) {
1092 g_printf(_("%-9.9s %3d %10lld %10lld "),
1093 seqdatestr(seq), sp[seq].disks,
1094 (long long)sp[seq].origsize,
1095 (long long)sp[seq].outsize);
1096 if(!sp[seq].outsize) g_printf(" --- \n");
1097 else g_printf(_("%+8.1lf%%\n"),
1098 (((double)sp[seq].outsize - (double)balanced) * 100.0 /
1103 if(sp[later].disks != 0) {
1104 g_printf(_("later %3d %10lld %10lld "),
1106 (long long)sp[later].origsize,
1107 (long long)sp[later].outsize);
1108 if(!sp[later].outsize) g_printf(" --- \n");
1109 else g_printf(_("%+8.1lf%%\n"),
1110 (((double)sp[later].outsize - (double)balanced) * 100.0 /
1113 g_printf("----------------------------------------------\n");
1114 g_printf(_("TOTAL %3d %10lld %10lld %9lld\n"),
1116 (long long)sp[total].origsize,
1117 (long long)sp[total].outsize,
1118 (long long)total_balanced);
1119 if (sp[balance].origsize != sp[total].origsize ||
1120 sp[balance].outsize != sp[total].outsize ||
1121 balanced != total_balanced) {
1122 g_printf(_("BALANCED %10lld %10lld %9lld\n"),
1123 (long long)sp[balance].origsize,
1124 (long long)sp[balance].outsize,
1125 (long long)balanced);
1127 if (sp[distinct].disks != sp[total].disks) {
1128 g_printf(_("DISTINCT %3d %10lld %10lld\n"),
1130 (long long)sp[distinct].origsize,
1131 (long long)sp[distinct].outsize);
1133 g_printf(plural(_(" (estimated %d run per dumpcycle)\n"),
1134 _(" (estimated %d runs per dumpcycle)\n"),
1138 g_printf(plural(_(" (%d filesystem overdue."),
1139 _(" (%d filesystems overdue."), overdue),
1141 g_printf(plural(_(" The most being overdue %d day.)\n"),
1142 _(" The most being overdue %d days.)\n"), max_overdue),
1149 /* ----------------------------------------------- */
1157 char *sort_order = NULL;
1158 find_result_t *output_find;
1160 char **output_find_log;
1165 _("%s: expecting \"find [--sort <hkdlpbfw>] [hostname [<disk>]]*\"\n"),
1171 sort_order = newstralloc(sort_order, DEFAULT_SORT_ORDER);
1173 size_t i, valid_sort=1;
1175 for(i = strlen(opt_sort); i > 0; i--) {
1176 switch (opt_sort[i - 1]) {
1194 default: valid_sort=0;
1198 sort_order = newstralloc(sort_order, opt_sort);
1200 g_printf(_("Invalid sort order: %s\n"), opt_sort);
1201 g_printf(_("Use default sort order: %s\n"), sort_order);
1205 errstr = match_disklist(&diskq, exact_match, argc-(start_argc-1),
1206 argv+(start_argc-1));
1208 /* check all log file exists */
1209 output_find_log = find_log();
1210 for (name = output_find_log; *name != NULL; name++) {
1213 amfree(output_find_log);
1215 output_find = find_dump(&diskq); /* Add deleted dump to diskq */
1216 if(argc-(start_argc-1) > 0) {
1217 find_result_t *afind = NULL;
1218 find_result_t *afind_next = NULL;
1219 find_result_t *new_output_find = NULL;
1223 errstr = match_disklist(&diskq, exact_match, argc-(start_argc-1),
1224 argv+(start_argc-1));
1226 g_printf("%s", errstr);
1229 for (afind = output_find; afind; afind = afind_next) {
1230 afind_next = afind->next;
1231 dp = lookup_disk(afind->hostname, afind->diskname);
1233 afind->next = new_output_find;
1234 new_output_find = afind;
1239 output_find = new_output_find;
1240 } else if (errstr) {
1241 g_printf("%s", errstr);
1245 sort_find_result(sort_order, &output_find);
1246 print_find_result(output_find);
1247 free_find_result(&output_find);
1253 /* ------------------------ */
1260 gboolean exact_match)
1262 GSList * file_list = NULL;
1266 flags = CMDLINE_PARSE_DATESTAMP;
1267 if (allow_empty) flags |= CMDLINE_EMPTY_TO_WILDCARD;
1268 if (exact_match) flags |= CMDLINE_EXACT_MATCH;
1269 dumplist = cmdline_parse_dumpspecs(argc, argv, flags);
1271 file_list = cmdline_match_holding(dumplist);
1272 dumpspec_list_free(dumplist);
1277 /* Given a file header, find the history element in curinfo most likely
1278 * corresponding to that dump (this is not an exact science).
1280 * @param info: the info_t element for this DLE
1281 * @param file: the header of the file
1282 * @returns: index of the matching history element, or -1 if not found
1285 holding_file_find_history(
1289 int matching_hist_idx = -1;
1293 /* Begin by trying to find the history element matching this dump.
1294 * The datestamp on the dump is for the entire run of amdump, while the
1295 * 'date' in the history element of 'info' is the time the dump itself
1296 * began. A matching history element, then, is the earliest element
1297 * with a 'date' equal to or later than the date of the dumpfile.
1299 * We compare using formatted datestamps; even using seconds since epoch,
1300 * we would still face timezone issues, and have to do a reverse (timezone
1301 * to gmt) translation.
1304 /* get to the end of the history list and search backward */
1305 for (nhist = 0; info->history[nhist].level > -1; nhist++) /* empty loop */;
1306 for (i = nhist-1; i > -1; i--) {
1307 char *info_datestamp = get_timestamp_from_time(info->history[i].date);
1308 int order = strcmp(file->datestamp, info_datestamp);
1309 amfree(info_datestamp);
1312 /* only a match if the levels are equal */
1313 if (info->history[i].level == file->dumplevel) {
1314 matching_hist_idx = i;
1320 return matching_hist_idx;
1323 /* A holding file is 'outdated' if a subsequent dump of the same DLE was made
1324 * at the same level or a lower leve; for example, a level 2 dump is outdated if
1325 * there is a subsequent level 2, or a subsequent level 0.
1327 * @param file: the header of the file
1328 * @returns: true if the file is outdated
1331 holding_file_is_outdated(
1335 int matching_hist_idx;
1337 if (get_info(file->name, file->disk, &info) == -1) {
1338 return 0; /* assume it's not outdated */
1341 /* if the last level is less than the level of this dump, then
1343 if (info.last_level < file->dumplevel)
1346 /* otherwise, we need to see if this dump is the last at its level */
1347 matching_hist_idx = holding_file_find_history(&info, file);
1348 if (matching_hist_idx == -1) {
1349 return 0; /* assume it's not outdated */
1352 /* compare the date of the history element with the most recent date
1353 * for this level. If they match, then this is the last dump at this
1354 * level, and we checked above for more recent lower-level dumps, so
1355 * the dump is not outdated. */
1356 if (info.history[matching_hist_idx].date ==
1357 info.inf[info.history[matching_hist_idx].level].date) {
1365 remove_holding_file_from_catalog(
1368 static int warnings_printed; /* only print once per invocation */
1371 int matching_hist_idx = -1;
1372 history_t matching_hist; /* will be a copy */
1375 if (!holding_file_get_dumpfile(filename, &file)) {
1376 g_printf(_("Could not read holding file %s\n"), filename);
1380 if (get_info(file.name, file.disk, &info) == -1) {
1381 g_printf(_("WARNING: No curinfo record for %s:%s\n"), file.name, file.disk);
1382 dumpfile_free_data(&file);
1383 return 1; /* not an error */
1386 matching_hist_idx = holding_file_find_history(&info, &file);
1388 if (matching_hist_idx == -1) {
1389 g_printf(_("WARNING: No dump matching %s found in curinfo.\n"), filename);
1390 dumpfile_free_data(&file);
1391 return 1; /* not an error */
1395 matching_hist = info.history[matching_hist_idx];
1397 /* Remove the history element itself before doing the stats */
1398 for (i = matching_hist_idx; i < NB_HISTORY; i++) {
1399 info.history[i] = info.history[i+1];
1401 info.history[NB_HISTORY].level = -1;
1403 /* Remove stats for that history element, if necessary. Doing so
1404 * will result in an inconsistent set of backups, so we warn the
1405 * user and adjust last_level to make the next dump get us a
1406 * consistent picture. */
1407 if (matching_hist.date == info.inf[matching_hist.level].date) {
1408 /* search for an earlier dump at this level */
1409 for (i = matching_hist_idx; info.history[i].level > -1; i++) {
1410 if (info.history[i].level == matching_hist.level)
1414 if (info.history[i].level < 0) {
1415 /* not found => zero it out */
1416 info.inf[matching_hist.level].date = (time_t)-1; /* flag as not set */
1417 info.inf[matching_hist.level].label[0] = '\0';
1419 /* found => reconstruct stats as best we can */
1420 info.inf[matching_hist.level].size = info.history[i].size;
1421 info.inf[matching_hist.level].csize = info.history[i].csize;
1422 info.inf[matching_hist.level].secs = info.history[i].secs;
1423 info.inf[matching_hist.level].date = info.history[i].date;
1424 info.inf[matching_hist.level].filenum = 0; /* we don't know */
1425 info.inf[matching_hist.level].label[0] = '\0'; /* we don't know */
1428 /* set last_level to the level we just deleted, and set command
1429 * appropriately to make sure planner does a new dump at this level
1431 info.last_level = matching_hist.level;
1432 if (info.last_level == 0) {
1433 g_printf(_("WARNING: Deleting the most recent full dump; forcing a full dump at next run.\n"));
1434 SET(info.command, FORCE_FULL);
1436 g_printf(_("WARNING: Deleting the most recent level %d dump; forcing a level %d dump or \nWARNING: lower at next run.\n"),
1437 info.last_level, info.last_level);
1438 SET(info.command, FORCE_NO_BUMP);
1441 /* Search for and display any subsequent runs that depended on this one */
1442 warnings_printed = 0;
1443 for (i = matching_hist_idx-1; i >= 0; i--) {
1445 if (info.history[i].level <= matching_hist.level) break;
1447 datestamp = get_timestamp_from_time(info.history[i].date);
1448 g_printf(_("WARNING: Level %d dump made %s can no longer be accurately restored.\n"),
1449 info.history[i].level, datestamp);
1452 warnings_printed = 1;
1454 if (warnings_printed)
1455 g_printf(_("WARNING: (note, dates shown above are for dumps, and may be later than the\nWARNING: corresponding run date)\n"));
1458 /* recalculate consecutive_runs based on the history: find the first run
1459 * at this level, and then count the consecutive runs at that level. This
1460 * number may be zero (if we just deleted the last run at this level) */
1461 info.consecutive_runs = 0;
1462 for (i = 0; info.history[i].level >= 0; i++) {
1463 if (info.history[i].level == info.last_level) break;
1465 while (info.history[i+info.consecutive_runs].level == info.last_level)
1466 info.consecutive_runs++;
1468 /* this function doesn't touch the performance stats */
1470 /* write out the changes */
1471 if (put_info(file.name, file.disk, &info) == -1) {
1472 g_printf(_("Could not write curinfo record for %s:%s\n"), file.name, file.disk);
1473 dumpfile_free_data(&file);
1477 dumpfile_free_data(&file);
1488 enum { HOLDING_USAGE, HOLDING_LIST, HOLDING_DELETE } action = HOLDING_USAGE;
1490 int outdated_list = 0;
1494 action = HOLDING_USAGE;
1495 else if (strcmp(argv[3], "list") == 0 && argc >= 4)
1496 action = HOLDING_LIST;
1497 else if (strcmp(argv[3], "delete") == 0 && argc > 4)
1498 action = HOLDING_DELETE;
1503 _("%s: expecting \"holding list [-l] [-d]\" or \"holding delete <host> [ .. ]\"\n"),
1509 long_list = opt_long;
1510 outdated_list = opt_outdated;
1511 argc -= 4; argv += 4;
1515 g_printf("%-10s %-2s %-4s %s\n",
1516 _("size (kB)"), _("lv"), _("outd"), _("dump specification"));
1519 file_list = get_file_list(argc, argv, 1, exact_match);
1520 for (li = file_list; li != NULL; li = li->next) {
1524 if (!holding_file_get_dumpfile((char *)li->data, &file)) {
1525 g_fprintf(stderr, _("Error reading %s\n"), (char *)li->data);
1529 is_outdated = holding_file_is_outdated(&file);
1531 dumpstr = cmdline_format_dumpspec_components(file.name, file.disk, file.datestamp, NULL);
1532 /* only print this entry if we're printing everything, or if it's outdated and
1533 * we're only printing outdated files (-o) */
1534 if (!outdated_list || is_outdated) {
1536 g_printf("%-10lld %-2d %-4s %s\n",
1537 (long long)holding_file_size((char *)li->data, 0),
1539 is_outdated? " *":"",
1542 g_printf("%s\n", dumpstr);
1546 dumpfile_free_data(&file);
1548 slist_free_full(file_list, g_free);
1551 case HOLDING_DELETE:
1552 argc -= 4; argv += 4;
1554 file_list = get_file_list(argc, argv, 0, exact_match);
1555 for (li = file_list; li != NULL; li = li->next) {
1556 g_fprintf(stderr, _("Deleting '%s'\n"), (char *)li->data);
1557 /* remove it from the catalog */
1558 if (!remove_holding_file_from_catalog((char *)li->data))
1562 if (!holding_file_unlink((char *)li->data)) {
1563 error(_("Could not delete '%s'"), (char *)li->data);
1566 slist_free_full(file_list, g_free);
1572 /* ------------------------ */
1575 /* shared code with planner.c */
1581 gint64 bump = getconf_int64(CNF_BUMPSIZE);
1582 double mult = getconf_real(CNF_BUMPMULT);
1585 bump = (int)((double)bump * mult);
1595 int conf_bumppercent = getconf_int(CNF_BUMPPERCENT);
1596 double conf_bumpmult = getconf_real(CNF_BUMPMULT);
1598 (void)argc; /* Quiet unused parameter warning */
1599 (void)argv; /* Quiet unused parameter warning */
1601 g_printf(_("Current bump parameters:\n"));
1602 if(conf_bumppercent == 0) {
1603 g_printf(_(" bumpsize %5jd KB\t- minimum savings (threshold) to bump level 1 -> 2\n"),
1604 (intmax_t)getconf_int64(CNF_BUMPSIZE));
1605 g_printf(_(" bumpdays %5d\t- minimum days at each level\n"),
1606 getconf_int(CNF_BUMPDAYS));
1607 g_printf(_(" bumpmult %5.5lg\t- threshold = bumpsize * bumpmult**(level-1)\n\n"),
1610 g_printf(_(" Bump -> To Threshold\n"));
1611 for(l = 1; l < 9; l++)
1612 g_printf(_("\t%d -> %d %9d KB\n"), l, l+1, bump_thresh(l));
1616 double bumppercent = (double)conf_bumppercent;
1618 g_printf(_(" bumppercent %3d %%\t- minimum savings (threshold) to bump level 1 -> 2\n"),
1620 g_printf(_(" bumpdays %5d\t- minimum days at each level\n"),
1621 getconf_int(CNF_BUMPDAYS));
1622 g_printf(_(" bumpmult %5.5lg\t- threshold = disk_size * bumppercent * bumpmult**(level-1)\n\n"),
1624 g_printf(_(" Bump -> To Threshold\n"));
1625 for(l = 1; l < 9; l++) {
1626 g_printf(_("\t%d -> %d %7.2lf %%\n"), l, l+1, bumppercent);
1627 bumppercent *= conf_bumpmult;
1628 if(bumppercent >= 100.000) { bumppercent = 100.0;}
1634 /* ----------------------------------------------- */
1636 void export_one(disk_t *dp);
1645 char hostname[MAX_HOSTNAME_LENGTH+1];
1648 g_printf(_("CURINFO Version %s CONF %s\n"), VERSION, getconf_str(CNF_ORG));
1651 if(gethostname(hostname, SIZEOF(hostname)-1) == -1) {
1652 error(_("could not determine host name: %s\n"), strerror(errno));
1655 hostname[SIZEOF(hostname)-1] = '\0';
1656 g_printf(_("# Generated by:\n# host: %s\n# date: %s"),
1657 hostname, ctime(&curtime));
1659 g_printf(_("# command:"));
1660 for(i = 0; i < argc; i++)
1661 g_printf(_(" %s"), argv[i]);
1663 g_printf(_("\n# This file can be merged back in with \"amadmin import\".\n"));
1664 g_printf(_("# Edit only with care.\n"));
1667 diskloop(argc, argv, "export", export_one);
1668 else for(dp = diskq.head; dp != NULL; dp = dp->next)
1678 char *qhost, *qdisk;
1680 if(get_info(dp->host->hostname, dp->name, &info)) {
1681 g_fprintf(stderr, _("Warning: no curinfo record for %s:%s\n"),
1682 dp->host->hostname, dp->name);
1685 qhost = quote_string(dp->host->hostname);
1686 qdisk = quote_string(dp->name);
1687 g_printf(_("host: %s\ndisk: %s\n"), qhost, qdisk);
1688 g_printf(_("command: %u\n"), info.command);
1689 g_printf(_("last_level: %d\n"),info.last_level);
1690 g_printf(_("consecutive_runs: %d\n"),info.consecutive_runs);
1691 g_printf(_("full-rate:"));
1692 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.full.rate[i]);
1693 g_printf(_("\nfull-comp:"));
1694 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.full.comp[i]);
1696 g_printf(_("\nincr-rate:"));
1697 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.incr.rate[i]);
1698 g_printf(_("\nincr-comp:"));
1699 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.incr.comp[i]);
1701 for(l=0;l<DUMP_LEVELS;l++) {
1702 if(info.inf[l].date < (time_t)0 && info.inf[l].label[0] == '\0') continue;
1703 g_printf(_("stats: %d %lld %lld %jd %jd %lld %s\n"), l,
1704 (long long)info.inf[l].size,
1705 (long long)info.inf[l].csize,
1706 (intmax_t)info.inf[l].secs,
1707 (intmax_t)info.inf[l].date,
1708 (long long)info.inf[l].filenum,
1711 for(l=0;info.history[l].level > -1;l++) {
1712 g_printf(_("history: %d %lld %lld %jd\n"),
1713 info.history[l].level,
1714 (long long)info.history[l].size,
1715 (long long)info.history[l].csize,
1716 (intmax_t)info.history[l].date);
1723 /* ----------------------------------------------- */
1725 int import_one(void);
1726 char *impget_line(void);
1744 (void)argc; /* Quiet unused parameter warning */
1745 (void)argv; /* Quiet unused parameter warning */
1747 /* process header line */
1749 if((line = agets(stdin)) == NULL) {
1750 g_fprintf(stderr, _("%s: empty input.\n"), get_pname());
1758 if(strncmp_const_skip(s - 1, "CURINFO Version", s, ch) != 0) {
1762 skip_whitespace(s, ch);
1764 || sscanf(s - 1, "%d.%d.%d", &vers_maj, &vers_min, &vers_patch) != 3) {
1766 if (sscanf(s - 1, "%d.%d", &vers_maj, &vers_min) != 2) {
1771 skip_integer(s, ch); /* skip over major */
1776 skip_integer(s, ch); /* skip over minor */
1777 if (vers_patch != -1) {
1782 skip_integer(s, ch); /* skip over patch */
1791 skip_non_whitespace(s, ch);
1795 skip_whitespace(s, ch); /* find the org keyword */
1796 if(ch == '\0' || strncmp_const_skip(s - 1, "CONF", s, ch) != 0) {
1802 skip_whitespace(s, ch); /* find the org string */
1809 newer = (vers_maj != VERSION_MAJOR)? vers_maj > VERSION_MAJOR :
1810 (vers_min != VERSION_MINOR)? vers_min > VERSION_MINOR :
1811 vers_patch > VERSION_PATCH;
1814 _("%s: WARNING: input is from newer Amanda version: %d.%d.%d.\n"),
1815 get_pname(), vers_maj, vers_min, vers_patch);
1818 if(strcmp(org, getconf_str(CNF_ORG)) != 0) {
1819 g_fprintf(stderr, _("%s: WARNING: input is from different org: %s\n"),
1832 /*@i@*/ amfree(line);
1833 g_fprintf(stderr, _("%s: bad CURINFO header line in input: %s.\n"),
1835 g_fprintf(stderr, _(" Was the input in \"amadmin export\" format?\n"));
1850 char *hostname = NULL;
1851 char *diskname = NULL;
1852 long long off_t_tmp;
1853 long long time_t_tmp;
1855 memset(&info, 0, SIZEOF(info_t));
1857 for(level = 0; level < DUMP_LEVELS; level++) {
1858 info.inf[level].date = (time_t)-1;
1861 /* get host: disk: command: lines */
1863 hostname = diskname = NULL;
1865 if((line = impget_line()) == NULL) return 0; /* nothing there */
1869 skip_whitespace(s, ch);
1870 if(ch == '\0' || strncmp_const_skip(s - 1, "host:", s, ch) != 0) goto parse_err;
1871 skip_whitespace(s, ch);
1872 if(ch == '\0') goto parse_err;
1874 skip_quoted_string(s, ch);
1876 hostname = unquote_string(fp);
1879 skip_whitespace(s, ch);
1882 if((line = impget_line()) == NULL) goto shortfile_err;
1885 skip_whitespace(s, ch);
1887 if(strncmp_const_skip(s - 1, "disk:", s, ch) != 0) goto parse_err;
1888 skip_whitespace(s, ch);
1889 if(ch == '\0') goto parse_err;
1891 skip_quoted_string(s, ch);
1893 diskname = unquote_string(fp);
1897 if((line = impget_line()) == NULL) goto shortfile_err;
1898 if(sscanf(line, "command: %u", &info.command) != 1) goto parse_err;
1900 /* get last_level and consecutive_runs */
1903 if((line = impget_line()) == NULL) goto shortfile_err;
1904 rc = sscanf(line, "last_level: %d", &info.last_level);
1907 if((line = impget_line()) == NULL) goto shortfile_err;
1908 if(sscanf(line, "consecutive_runs: %d", &info.consecutive_runs) != 1) goto parse_err;
1910 if((line = impget_line()) == NULL) goto shortfile_err;
1913 /* get rate: and comp: lines for full dumps */
1915 rc = sscanf(line, "full-rate: %lf %lf %lf",
1916 &info.full.rate[0], &info.full.rate[1], &info.full.rate[2]);
1917 if(rc != 3) goto parse_err;
1920 if((line = impget_line()) == NULL) goto shortfile_err;
1921 rc = sscanf(line, "full-comp: %lf %lf %lf",
1922 &info.full.comp[0], &info.full.comp[1], &info.full.comp[2]);
1923 if(rc != 3) goto parse_err;
1925 /* get rate: and comp: lines for incr dumps */
1928 if((line = impget_line()) == NULL) goto shortfile_err;
1929 rc = sscanf(line, "incr-rate: %lf %lf %lf",
1930 &info.incr.rate[0], &info.incr.rate[1], &info.incr.rate[2]);
1931 if(rc != 3) goto parse_err;
1934 if((line = impget_line()) == NULL) goto shortfile_err;
1935 rc = sscanf(line, "incr-comp: %lf %lf %lf",
1936 &info.incr.comp[0], &info.incr.comp[1], &info.incr.comp[2]);
1937 if(rc != 3) goto parse_err;
1939 /* get stats for dump levels */
1943 if((line = impget_line()) == NULL) goto shortfile_err;
1944 if(strncmp_const(line, "//") == 0) {
1948 if(strncmp_const(line, "history:") == 0) {
1952 memset(&onestat, 0, SIZEOF(onestat));
1957 skip_whitespace(s, ch);
1958 if(ch == '\0' || strncmp_const_skip(s - 1, "stats:", s, ch) != 0) {
1962 skip_whitespace(s, ch);
1963 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1966 skip_integer(s, ch);
1968 skip_whitespace(s, ch);
1969 if(ch == '\0' || sscanf(s - 1, "%lld", &off_t_tmp) != 1) {
1972 onestat.size = (off_t)off_t_tmp;
1973 skip_integer(s, ch);
1975 skip_whitespace(s, ch);
1976 if(ch == '\0' || sscanf(s - 1, "%lld", &off_t_tmp) != 1) {
1979 onestat.csize = (off_t)off_t_tmp;
1980 skip_integer(s, ch);
1982 skip_whitespace(s, ch);
1983 if(ch == '\0' || sscanf(s - 1, "%lld", &time_t_tmp) != 1) {
1986 onestat.secs = (time_t)time_t_tmp;
1987 skip_integer(s, ch);
1989 skip_whitespace(s, ch);
1990 if(ch == '\0' || sscanf(s - 1, "%lld", &time_t_tmp) != 1) {
1993 /* time_t not guarranteed to be long */
1994 /*@i1@*/ onestat.date = (time_t)time_t_tmp;
1995 skip_integer(s, ch);
1997 skip_whitespace(s, ch);
1999 if(sscanf(s - 1, "%lld", &off_t_tmp) != 1) {
2002 onestat.filenum = (off_t)off_t_tmp;
2003 skip_integer(s, ch);
2005 skip_whitespace(s, ch);
2007 if (onestat.filenum != 0)
2009 onestat.label[0] = '\0';
2011 strncpy(onestat.label, s - 1, SIZEOF(onestat.label)-1);
2012 onestat.label[SIZEOF(onestat.label)-1] = '\0';
2016 if(level < 0 || level > 9) goto parse_err;
2018 info.inf[level] = onestat;
2021 for(i=0;i<=NB_HISTORY;i++) {
2022 info.history[i].level = -2;
2025 history_t onehistory;
2027 if(line[0] == '/' && line[1] == '/') {
2028 info.history[nb_history].level = -2;
2032 memset(&onehistory, 0, SIZEOF(onehistory));
2035 if(strncmp_const_skip(line, "history:", s, ch) != 0) {
2039 skip_whitespace(s, ch);
2040 if(ch == '\0' || sscanf((s - 1), "%d", &onehistory.level) != 1) {
2043 skip_integer(s, ch);
2045 skip_whitespace(s, ch);
2046 if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
2049 onehistory.size = (off_t)off_t_tmp;
2050 skip_integer(s, ch);
2052 skip_whitespace(s, ch);
2053 if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
2056 onehistory.csize = (off_t)off_t_tmp;
2057 skip_integer(s, ch);
2059 skip_whitespace(s, ch);
2060 if((ch == '\0') || sscanf((s - 1), "%lld", &time_t_tmp) != 1) {
2063 /* time_t not guarranteed to be long */
2064 /*@i1@*/ onehistory.date = (time_t)time_t_tmp;
2065 skip_integer(s, ch);
2067 info.history[nb_history++] = onehistory;
2069 if((line = impget_line()) == NULL) goto shortfile_err;
2071 /*@i@*/ amfree(line);
2073 /* got a full record, now write it out to the database */
2075 if(put_info(hostname, diskname, &info)) {
2076 g_fprintf(stderr, _("%s: error writing record for %s:%s\n"),
2077 get_pname(), hostname, diskname);
2084 /*@i@*/ amfree(line);
2087 g_fprintf(stderr, _("%s: parse error reading import record.\n"), get_pname());
2091 /*@i@*/ amfree(line);
2094 g_fprintf(stderr, _("%s: short file reading import record.\n"), get_pname());
2105 for(; (line = agets(stdin)) != NULL; free(line)) {
2109 skip_whitespace(s, ch);
2111 /* ignore comment lines */
2114 /* found non-blank, return line */
2117 /* otherwise, a blank line, so keep going */
2120 g_fprintf(stderr, _("%s: reading stdin: %s\n"),
2121 get_pname(), strerror(errno));
2126 /* ----------------------------------------------- */
2134 dumptype_t *dtype = lookup_dumptype(dp->dtype_name);
2139 g_printf("line %d (%s):\n", dp->line, dp->filename);
2141 g_printf(" host %s:\n", hp->hostname);
2142 g_printf(" interface %s\n",
2143 interface_name(ip->config)[0] ? interface_name(ip->config) : "default");
2144 g_printf(" disk %s:\n", dp->name);
2145 if (dp->device) g_printf(" device %s\n", dp->device);
2147 g_printf(" program \"%s\"\n", dp->program);
2148 if (dp->application)
2149 g_printf(" application \"%s\"\n", dp->application);
2151 dump_dumptype(dtype, " ", print_default, print_source);
2153 g_printf(" spindle %d\n", dp->spindle);
2166 diskloop(argc, argv, "disklist", disklist_one);
2168 for(dp = diskq.head; dp != NULL; dp = dp->next)
2172 /* ----------------------------------------------- */
2176 int argc G_GNUC_UNUSED,
2177 char ** argv G_GNUC_UNUSED)
2181 GHashTable *seen = g_hash_table_new(g_str_hash, g_str_equal);
2183 /* enumerate all hosts, skipping those that have been seen (since
2184 * there may be more than one DLE on a host */
2185 for(dp = diskq.head; dp != NULL; dp = dp->next) {
2186 char *hostname = dp->host->hostname;
2187 if (g_hash_table_lookup(seen, hostname))
2189 g_printf("%s\n", hostname);
2190 g_hash_table_insert(seen, hostname, &sentinel);
2192 g_hash_table_destroy(seen);
2195 /* ----------------------------------------------- */
2199 int argc G_GNUC_UNUSED,
2200 char ** argv G_GNUC_UNUSED)
2204 for(dp = diskq.head; dp != NULL; dp = dp->next)
2205 g_printf("%s %s\n", dp->host->hostname, dp->name);
2208 /* ----------------------------------------------- */
2217 (void)argc; /* Quiet unused parameter warning */
2218 (void)argv; /* Quiet unused parameter warning */
2220 for(i = 0; version_info[i] != NULL; i++)
2221 g_printf("%s", version_info[i]);
2226 int argc G_GNUC_UNUSED,
2227 char **argv G_GNUC_UNUSED)
2229 dump_configuration(print_default, print_source);