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.49.2.13.2.3.2.15.2.12 2005/09/20 21:31:52 jrjackson Exp $
30 * controlling process for the Amanda backup system
44 int main P((int argc, char **argv));
46 void force P((int argc, char **argv));
47 void force_one P((disk_t *dp));
48 void unforce P((int argc, char **argv));
49 void unforce_one P((disk_t *dp));
50 void force_bump P((int argc, char **argv));
51 void force_bump_one P((disk_t *dp));
52 void force_no_bump P((int argc, char **argv));
53 void force_no_bump_one P((disk_t *dp));
54 void unforce_bump P((int argc, char **argv));
55 void unforce_bump_one P((disk_t *dp));
56 void reuse P((int argc, char **argv));
57 void noreuse P((int argc, char **argv));
58 void info P((int argc, char **argv));
59 void info_one P((disk_t *dp));
60 void due P((int argc, char **argv));
61 void due_one P((disk_t *dp));
62 void find P((int argc, char **argv));
63 void delete P((int argc, char **argv));
64 void delete_one P((disk_t *dp));
65 void balance P((int argc, char **argv));
66 void tape P((int argc, char **argv));
67 void bumpsize P((void));
68 void diskloop P((int argc, char **argv, char *cmdname,
69 void (*func) P((disk_t *dp))));
70 char *seqdatestr P((int seq));
71 static int next_level0 P((disk_t *dp, info_t *info));
72 int bump_thresh P((int level));
73 void export_db P((int argc, char **argv));
74 void import_db P((int argc, char **argv));
75 void disklist P((int argc, char **argv));
76 void disklist_one P((disk_t *dp));
77 static void check_dumpuser P((void));
79 static char *conf_tapelist = NULL;
80 static char *displayunit;
81 static long int unitdivisor;
87 unsigned long malloc_hist_1, malloc_size_1;
88 unsigned long malloc_hist_2, malloc_size_2;
98 malloc_size_1 = malloc_inuse(&malloc_hist_1);
100 erroutput_type = ERR_INTERACTIVE;
102 if(argc < 3) usage();
104 if(strcmp(argv[2],"version") == 0) {
105 for(argc=0; version_info[argc]; printf("%s",version_info[argc++]));
108 config_name = argv[1];
109 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
110 conffile = stralloc2(config_dir, CONFFILE_NAME);
111 if(read_conffile(conffile)) {
112 error("errors processing config file \"%s\"", conffile);
118 conf_diskfile = getconf_str(CNF_DISKFILE);
119 if (*conf_diskfile == '/') {
120 conf_diskfile = stralloc(conf_diskfile);
122 conf_diskfile = stralloc2(config_dir, conf_diskfile);
124 if((diskqp = read_diskfile(conf_diskfile)) == NULL) {
125 error("could not load disklist \"%s\"", conf_diskfile);
127 amfree(conf_diskfile);
128 conf_tapelist = getconf_str(CNF_TAPELIST);
129 if (*conf_tapelist == '/') {
130 conf_tapelist = stralloc(conf_tapelist);
132 conf_tapelist = stralloc2(config_dir, conf_tapelist);
134 if(read_tapelist(conf_tapelist)) {
135 error("could not load tapelist \"%s\"", conf_tapelist);
137 conf_infofile = getconf_str(CNF_INFOFILE);
138 if (*conf_infofile == '/') {
139 conf_infofile = stralloc(conf_infofile);
141 conf_infofile = stralloc2(config_dir, conf_infofile);
143 if(open_infofile(conf_infofile)) {
144 error("could not open info db \"%s\"", conf_infofile);
146 amfree(conf_infofile);
148 displayunit = getconf_str(CNF_DISPLAYUNIT);
149 unitdivisor = getconf_unit_divisor();
151 if(strcmp(argv[2],"force-bump") == 0) force_bump(argc, argv);
152 else if(strcmp(argv[2],"force-no-bump") == 0) force_no_bump(argc, argv);
153 else if(strcmp(argv[2],"unforce-bump") == 0) unforce_bump(argc, argv);
154 else if(strcmp(argv[2],"force") == 0) force(argc, argv);
155 else if(strcmp(argv[2],"unforce") == 0) unforce(argc, argv);
156 else if(strcmp(argv[2],"reuse") == 0) reuse(argc, argv);
157 else if(strcmp(argv[2],"no-reuse") == 0) noreuse(argc, argv);
158 else if(strcmp(argv[2],"info") == 0) info(argc, argv);
159 else if(strcmp(argv[2],"due") == 0) due(argc, argv);
160 else if(strcmp(argv[2],"find") == 0) find(argc, argv);
161 else if(strcmp(argv[2],"delete") == 0) delete(argc, argv);
162 else if(strcmp(argv[2],"balance") == 0) balance(argc, argv);
163 else if(strcmp(argv[2],"tape") == 0) tape(argc, argv);
164 else if(strcmp(argv[2],"bumpsize") == 0) bumpsize();
165 else if(strcmp(argv[2],"import") == 0) import_db(argc, argv);
166 else if(strcmp(argv[2],"export") == 0) export_db(argc, argv);
167 else if(strcmp(argv[2],"disklist") == 0) disklist(argc, argv);
169 fprintf(stderr, "%s: unknown command \"%s\"\n", argv[0], argv[2]);
174 amfree(conf_tapelist);
177 malloc_size_2 = malloc_inuse(&malloc_hist_2);
179 if(malloc_size_1 != malloc_size_2) {
180 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
189 fprintf(stderr, "\nUsage: %s%s <conf> <command> {<args>} ...\n",
190 get_pname(), versionsuffix());
191 fprintf(stderr, " Valid <command>s are:\n");
192 fprintf(stderr,"\tversion\t\t\t\t# Show version info.\n");
194 "\tforce [<hostname> [<disks>]* ]+\t# Force level 0 at next run.\n");
196 "\tunforce [<hostname> [<disks>]* ]+\t# Clear force command.\n");
198 "\tforce-bump [<hostname> [<disks>]* ]+\t# Force bump at next run.\n");
200 "\tforce-no-bump [<hostname> [<disks>]* ]+\t# Force no-bump at next run.\n");
202 "\tunforce-bump [<hostname> [<disks>]* ]+\t# Clear bump command.\n");
204 "\treuse <tapelabel> ...\t\t# re-use this tape.\n");
206 "\tno-reuse <tapelabel> ...\t# never re-use this tape.\n");
208 "\tfind [<hostname> [<disks>]* ]*\t# Show which tapes these dumps are on.\n");
210 "\tdelete [<hostname> [<disks>]* ]*\t# Delete from database.\n");
212 "\tinfo [<hostname> [<disks>]* ]*\t# Show current info records.\n");
214 "\tdue [<hostname> [<disks>]* ]*\t# Show due date.\n");
216 "\tbalance [-days <num>]\t\t# Show nightly dump size balance.\n");
218 "\ttape [-days <num>]\t\t# Show which tape is due next.\n");
220 "\tbumpsize\t\t\t# Show current bump thresholds.\n");
222 "\texport [<hostname> [<disks>]* ]*\t# Export curinfo database to stdout.\n");
224 "\timport\t\t\t\t# Import curinfo database from stdin.\n");
226 "\tdisklist [<hostname> [<disks>]* ]*\t# Show disklist entries.\n");
232 /* ----------------------------------------------- */
234 #define SECS_PER_DAY (24*60*60)
237 char *seqdatestr(seq)
241 static char *dow[7] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
242 time_t t = today + seq*SECS_PER_DAY;
247 ap_snprintf(str, sizeof(str),
248 "%2d/%02d %3s", tm->tm_mon+1, tm->tm_mday, dow[tm->tm_wday]);
253 #define days_diff(a, b) (((b) - (a) + SECS_PER_DAY) / SECS_PER_DAY)
255 /* when is next level 0 due? 0 = tonight, 1 = tommorrow, etc*/
256 static int next_level0(dp, info)
260 if(dp->strategy == DS_NOFULL)
261 return 1; /* fake it */
262 if(info->inf[0].date < (time_t)0)
263 return 0; /* new disk */
265 return dp->dumpcycle - days_diff(info->inf[0].date, today);
268 static void check_dumpuser()
270 static int been_here = 0;
280 uid_dumpuser = uid_me;
281 dumpuser = getconf_str(CNF_DUMPUSER);
283 if ((pw = getpwnam(dumpuser)) == NULL) {
284 error("cannot look up dump user \"%s\"", dumpuser);
287 uid_dumpuser = pw->pw_uid;
288 if ((pw = getpwuid(uid_me)) == NULL) {
289 error("cannot look up my own uid %ld", (long)uid_me);
292 if (uid_me != uid_dumpuser) {
293 error("ERROR: running as user \"%s\" instead of \"%s\"",
294 pw->pw_name, dumpuser);
300 /* ----------------------------------------------- */
302 void diskloop(argc, argv, cmdname, func)
306 void (*func) P((disk_t *dp));
312 fprintf(stderr,"%s: expecting \"%s [<hostname> [<disks>]* ]+\"\n",
313 get_pname(), cmdname);
317 match_disklist(diskqp, argc-3, argv+3);
319 for(dp = diskqp->head; dp != NULL; dp = dp->next) {
326 fprintf(stderr,"%s: no disk matched\n",get_pname());
330 /* ----------------------------------------------- */
336 char *hostname = dp->host->hostname;
337 char *diskname = dp->name;
343 get_info(hostname, diskname, &info);
344 info.command |= FORCE_FULL;
345 if(info.command & FORCE_BUMP) {
346 info.command ^= FORCE_BUMP;
347 printf("%s: WARNING: %s:%s FORCE_BUMP command was cleared.\n",
348 get_pname(), hostname, diskname);
350 if(put_info(hostname, diskname, &info) == 0) {
351 printf("%s: %s:%s is set to a forced level 0 at next run.\n",
352 get_pname(), hostname, diskname);
354 fprintf(stderr, "%s: %s:%s could not be forced.\n",
355 get_pname(), hostname, diskname);
360 void force(argc, argv)
364 diskloop(argc, argv, "force", force_one);
368 /* ----------------------------------------------- */
374 char *hostname = dp->host->hostname;
375 char *diskname = dp->name;
378 get_info(hostname, diskname, &info);
379 if(info.command & FORCE_FULL) {
383 info.command ^= FORCE_FULL;
384 if(put_info(hostname, diskname, &info) == 0){
385 printf("%s: force command for %s:%s cleared.\n",
386 get_pname(), hostname, diskname);
389 "%s: force command for %s:%s could not be cleared.\n",
390 get_pname(), hostname, diskname);
394 printf("%s: no force command outstanding for %s:%s, unchanged.\n",
395 get_pname(), hostname, diskname);
399 void unforce(argc, argv)
403 diskloop(argc, argv, "unforce", unforce_one);
407 /* ----------------------------------------------- */
410 void force_bump_one(dp)
413 char *hostname = dp->host->hostname;
414 char *diskname = dp->name;
420 get_info(hostname, diskname, &info);
421 info.command |= FORCE_BUMP;
422 if(info.command & FORCE_NO_BUMP) {
423 info.command ^= FORCE_NO_BUMP;
424 printf("%s: WARNING: %s:%s FORCE_NO_BUMP command was cleared.\n",
425 get_pname(), hostname, diskname);
427 if (info.command & FORCE_FULL) {
428 info.command ^= FORCE_FULL;
429 printf("%s: WARNING: %s:%s FORCE_FULL command was cleared.\n",
430 get_pname(), hostname, diskname);
432 if(put_info(hostname, diskname, &info) == 0) {
433 printf("%s: %s:%s is set to bump at next run.\n",
434 get_pname(), hostname, diskname);
436 fprintf(stderr, "%s: %s:%s could not be forced to bump.\n",
437 get_pname(), hostname, diskname);
442 void force_bump(argc, argv)
446 diskloop(argc, argv, "force-bump", force_bump_one);
450 /* ----------------------------------------------- */
453 void force_no_bump_one(dp)
456 char *hostname = dp->host->hostname;
457 char *diskname = dp->name;
463 get_info(hostname, diskname, &info);
464 info.command |= FORCE_NO_BUMP;
465 if(info.command & FORCE_BUMP) {
466 info.command ^= FORCE_BUMP;
467 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 printf("%s: %s:%s is set to not bump at next run.\n",
472 get_pname(), hostname, diskname);
474 fprintf(stderr, "%s: %s:%s could not be force to not bump.\n",
475 get_pname(), hostname, diskname);
480 void force_no_bump(argc, argv)
484 diskloop(argc, argv, "force-no-bump", force_no_bump_one);
488 /* ----------------------------------------------- */
491 void unforce_bump_one(dp)
494 char *hostname = dp->host->hostname;
495 char *diskname = dp->name;
498 get_info(hostname, diskname, &info);
499 if(info.command & (FORCE_BUMP | FORCE_NO_BUMP)) {
503 if(info.command & FORCE_BUMP)
504 info.command ^= FORCE_BUMP;
505 if(info.command & FORCE_NO_BUMP)
506 info.command ^= FORCE_NO_BUMP;
507 if(put_info(hostname, diskname, &info) == 0) {
508 printf("%s: bump command for %s:%s cleared.\n",
509 get_pname(), hostname, diskname);
511 fprintf(stderr, "%s: %s:%s bump command could not be cleared.\n",
512 get_pname(), hostname, diskname);
516 printf("%s: no bump command outstanding for %s:%s, unchanged.\n",
517 get_pname(), hostname, diskname);
522 void unforce_bump(argc, argv)
526 diskloop(argc, argv, "unforce-bump", unforce_bump_one);
530 /* ----------------------------------------------- */
532 void reuse(argc, argv)
540 fprintf(stderr,"%s: expecting \"reuse <tapelabel> ...\"\n",
546 for(count=3; count< argc; count++) {
547 tp = lookup_tapelabel(argv[count]);
549 fprintf(stderr, "reuse: tape label %s not found in tapelist.\n",
553 if( tp->reuse == 0 ) {
555 printf("%s: marking tape %s as reusable.\n",
556 get_pname(), argv[count]);
558 fprintf(stderr, "%s: tape %s already reusable.\n",
559 get_pname(), argv[count]);
563 if(write_tapelist(conf_tapelist)) {
564 error("could not write tapelist \"%s\"", conf_tapelist);
568 void noreuse(argc, argv)
576 fprintf(stderr,"%s: expecting \"no-reuse <tapelabel> ...\"\n",
582 for(count=3; count< argc; count++) {
583 tp = lookup_tapelabel(argv[count]);
585 fprintf(stderr, "no-reuse: tape label %s not found in tapelist.\n",
589 if( tp->reuse == 1 ) {
591 printf("%s: marking tape %s as not reusable.\n",
592 get_pname(), argv[count]);
594 fprintf(stderr, "%s: tape %s already not reusable.\n",
595 get_pname(), argv[count]);
599 if(write_tapelist(conf_tapelist)) {
600 error("could not write tapelist \"%s\"", conf_tapelist);
605 /* ----------------------------------------------- */
612 char *hostname = dp->host->hostname;
613 char *diskname = dp->name;
616 if(get_info(hostname, diskname, &info)) {
617 printf("%s: %s:%s NOT currently in database.\n",
618 get_pname(), hostname, diskname);
623 if(del_info(hostname, diskname))
624 error("couldn't delete %s:%s from database: %s",
625 hostname, diskname, strerror(errno));
627 printf("%s: %s:%s deleted from curinfo database.\n",
628 get_pname(), hostname, diskname);
631 void delete(argc, argv)
636 diskloop(argc, argv, "delete", delete_one);
640 "%s: NOTE: you'll have to remove these from the disklist yourself.\n",
644 /* ----------------------------------------------- */
654 get_info(dp->host->hostname, dp->name, &info);
656 printf("\nCurrent info for %s %s:\n", dp->host->hostname, dp->name);
657 if(info.command & FORCE_FULL)
658 printf(" (Forcing to level 0 dump at next run)\n");
659 if(info.command & FORCE_BUMP)
660 printf(" (Forcing bump at next run)\n");
661 if(info.command & FORCE_NO_BUMP)
662 printf(" (Forcing no-bump at next run)\n");
663 printf(" Stats: dump rates (kps), Full: %5.1f, %5.1f, %5.1f\n",
664 info.full.rate[0], info.full.rate[1], info.full.rate[2]);
665 printf(" Incremental: %5.1f, %5.1f, %5.1f\n",
666 info.incr.rate[0], info.incr.rate[1], info.incr.rate[2]);
667 printf(" compressed size, Full: %5.1f%%,%5.1f%%,%5.1f%%\n",
668 info.full.comp[0]*100, info.full.comp[1]*100, info.full.comp[2]*100);
669 printf(" Incremental: %5.1f%%,%5.1f%%,%5.1f%%\n",
670 info.incr.comp[0]*100, info.incr.comp[1]*100, info.incr.comp[2]*100);
672 printf(" Dumps: lev datestmp tape file origK compK secs\n");
673 for(lev = 0, sp = &info.inf[0]; lev < 9; lev++, sp++) {
674 if(sp->date < (time_t)0 && sp->label[0] == '\0') continue;
675 tm = localtime(&sp->date);
676 printf(" %d %04d%02d%02d %-15s %4d %7ld %7ld %4ld\n",
677 lev, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
678 sp->label, sp->filenum, sp->size, sp->csize, sp->secs);
683 void info(argc, argv)
690 diskloop(argc, argv, "info", info_one);
692 for(dp = diskqp->head; dp != NULL; dp = dp->next)
696 /* ----------------------------------------------- */
706 if(get_info(hp->hostname, dp->name, &info)) {
707 printf("new disk %s:%s ignored.\n", hp->hostname, dp->name);
710 days = next_level0(dp, &info);
712 printf("Overdue %2d day%s %s:%s\n",
713 -days, (-days == 1) ? ": " : "s:",
714 hp->hostname, dp->name);
717 printf("Due today: %s:%s\n", hp->hostname, dp->name);
720 printf("Due in %2d day%s %s:%s\n", days,
721 (days == 1) ? ": " : "s:",
722 hp->hostname, dp->name);
735 diskloop(argc, argv, "due", due_one);
737 for(dp = diskqp->head; dp != NULL; dp = dp->next)
741 /* ----------------------------------------------- */
743 void tape(argc, argv)
751 if(argc > 4 && strcmp(argv[3],"--days") == 0) {
752 nb_days = atoi(argv[4]);
754 printf("days must be an integer bigger than 0\n");
758 runtapes = getconf_int(CNF_RUNTAPES);
759 tp = lookup_last_reusable_tape(0);
761 for ( j=0 ; j < nb_days ; j++ ) {
762 for ( i=0 ; i < runtapes ; i++ ) {
764 printf("The next Amanda run should go onto ");
768 printf("tape %s or ", tp->label);
769 printf("a new tape.\n");
771 tp = lookup_last_reusable_tape((j*runtapes) + i + 1);
774 lasttp = lookup_tapepos(lookup_nb_tape());
776 if(lasttp && i > 0 && lasttp->datestamp == 0) {
778 while(lasttp && i > 0 && lasttp->datestamp == 0) {
780 lasttp = lasttp->prev;
783 lasttp = lookup_tapepos(lookup_nb_tape());
786 printf("The next new tape already labelled is: %s.\n",
790 printf("The next %d new tapes already labelled are: %s", c,
792 lasttp = lasttp->prev;
794 while(lasttp && c > 0 && lasttp->datestamp == 0) {
795 printf(", %s", lasttp->label);
796 lasttp = lasttp->prev;
804 /* ----------------------------------------------- */
806 void balance(argc, argv)
811 struct balance_stats {
813 long origsize, outsize;
815 int conf_runspercycle, conf_dumpcycle;
816 int seq, runs_per_cycle, overdue, max_overdue;
817 int later, total, balance, distinct;
818 float fseq, disk_dumpcycle;
820 long int total_balanced, balanced;
824 conf_dumpcycle = getconf_int(CNF_DUMPCYCLE);
825 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
826 later = conf_dumpcycle;
827 if(later > 10000) later = 10000;
831 if(argc > 4 && strcmp(argv[3],"--days") == 0) {
832 later = atoi(argv[4]);
833 if(later < 0) later = conf_dumpcycle;
836 if(conf_runspercycle == 0) {
837 runs_per_cycle = conf_dumpcycle;
838 } else if(conf_runspercycle == -1 ) {
839 runs_per_cycle = guess_runs_from_tapelist();
841 runs_per_cycle = conf_runspercycle;
843 if (runs_per_cycle <= 0) {
849 distinct = later + 3;
851 sp = (struct balance_stats *)
852 alloc(sizeof(struct balance_stats) * (distinct+1));
854 for(seq=0; seq <= distinct; seq++)
855 sp[seq].disks = sp[seq].origsize = sp[seq].outsize = 0;
857 for(dp = diskqp->head; dp != NULL; dp = dp->next) {
858 if(get_info(dp->host->hostname, dp->name, &info)) {
859 printf("new disk %s:%s ignored.\n", dp->host->hostname, dp->name);
862 if (dp->strategy == DS_NOFULL) {
865 sp[distinct].disks++;
866 sp[distinct].origsize += info.inf[0].size/unitdivisor;
867 sp[distinct].outsize += info.inf[0].csize/unitdivisor;
870 if(dp->dumpcycle == 0) {
871 sp[balance].origsize += (info.inf[0].size/unitdivisor) * runs_per_cycle;
872 sp[balance].outsize += (info.inf[0].csize/unitdivisor) * runs_per_cycle;
875 sp[balance].origsize += (info.inf[0].size/unitdivisor) *
876 (conf_dumpcycle / dp->dumpcycle);
877 sp[balance].outsize += (info.inf[0].csize/unitdivisor) *
878 (conf_dumpcycle / dp->dumpcycle);
881 disk_dumpcycle = dp->dumpcycle;
882 if(dp->dumpcycle <= 0)
883 disk_dumpcycle = ((float)conf_dumpcycle) / ((float)runs_per_cycle);
885 seq = next_level0(dp, &info);
890 if (-seq > max_overdue)
900 sp[seq].origsize += info.inf[0].size/unitdivisor;
901 sp[seq].outsize += info.inf[0].csize/unitdivisor;
905 sp[total].origsize += info.inf[0].size/unitdivisor;
906 sp[total].outsize += info.inf[0].csize/unitdivisor;
909 /* See, if there's another run in this dumpcycle */
910 fseq += disk_dumpcycle;
912 } while (seq < later);
915 if(sp[total].outsize == 0 && sp[later].outsize == 0) {
916 printf("\nNo data to report on yet.\n");
921 balanced = sp[balance].outsize / runs_per_cycle;
922 if(conf_dumpcycle == later) {
923 total_balanced = sp[total].outsize / runs_per_cycle;
926 total_balanced = 1024*(((sp[total].outsize/1024) * conf_dumpcycle)
927 / (runs_per_cycle * later));
931 printf("\n due-date #fs orig %cB out %cB balance\n",
932 displayunit[0], displayunit[0]);
933 printf("----------------------------------------------\n");
934 for(seq = 0; seq < later; seq++) {
935 if(sp[seq].disks == 0 &&
936 ((seq > 0 && sp[seq-1].disks == 0) ||
937 ((seq < later-1) && sp[seq+1].disks == 0))) {
945 printf("%-9.9s %3d %10ld %10ld ",
946 seqdatestr(seq), sp[seq].disks,
947 sp[seq].origsize, sp[seq].outsize);
948 if(!sp[seq].outsize) printf(" --- \n");
949 else printf("%+8.1f%%\n",
950 (sp[seq].outsize-balanced)*100.0/(double)balanced);
954 if(sp[later].disks != 0) {
955 printf("later %3d %10ld %10ld ",
957 sp[later].origsize, sp[later].outsize);
958 if(!sp[later].outsize) printf(" --- \n");
959 else printf("%+8.1f%%\n",
960 (sp[later].outsize-balanced)*100.0/(double)balanced);
962 printf("----------------------------------------------\n");
963 printf("TOTAL %3d %10ld %10ld %9ld\n", sp[total].disks,
964 sp[total].origsize, sp[total].outsize, total_balanced);
965 if (sp[balance].origsize != sp[total].origsize ||
966 sp[balance].outsize != sp[total].outsize ||
967 balanced != total_balanced) {
968 printf("BALANCED %10ld %10ld %9ld\n",
969 sp[balance].origsize, sp[balance].outsize, balanced);
971 if (sp[distinct].disks != sp[total].disks) {
972 printf("DISTINCT %3d %10ld %10ld\n", sp[distinct].disks,
973 sp[distinct].origsize, sp[distinct].outsize);
975 printf(" (estimated %d run%s per dumpcycle)\n",
976 runs_per_cycle, (runs_per_cycle == 1) ? "" : "s");
978 printf(" (%d filesystem%s overdue, the most being overdue %d day%s)\n",
979 overdue, (overdue == 1) ? "" : "s",
980 max_overdue, (max_overdue == 1) ? "" : "s");
986 /* ----------------------------------------------- */
988 void find(argc, argv)
993 char *sort_order = NULL;
994 find_result_t *output_find;
998 "%s: expecting \"find [--sort <hkdlb>] [hostname [<disk>]]*\"\n",
1004 sort_order = newstralloc(sort_order, DEFAULT_SORT_ORDER);
1005 if(argc > 4 && strcmp(argv[3],"--sort") == 0) {
1006 int i, valid_sort=1;
1008 for(i=strlen(argv[4])-1;i>=0;i--) {
1009 switch (argv[4][i]) {
1021 default: valid_sort=0;
1025 sort_order = newstralloc(sort_order, argv[4]);
1027 printf("Invalid sort order: %s\n", argv[4]);
1028 printf("Use default sort order: %s\n", sort_order);
1034 match_disklist(diskqp, argc-(start_argc-1), argv+(start_argc-1));
1035 output_find = find_dump(1, diskqp);
1037 if(argc-(start_argc-1) > 0) {
1038 free_find_result(&output_find);
1039 match_disklist(diskqp, argc-(start_argc-1), argv+(start_argc-1));
1040 output_find = find_dump(0, NULL);
1043 sort_find_result(sort_order, &output_find);
1044 print_find_result(output_find);
1045 free_find_result(&output_find);
1051 /* ------------------------ */
1054 /* shared code with planner.c */
1056 int bump_thresh(level)
1059 int bump = getconf_int(CNF_BUMPSIZE);
1060 double mult = getconf_real(CNF_BUMPMULT);
1062 while(--level) bump = (int) bump * mult;
1069 int conf_bumppercent = getconf_int(CNF_BUMPPERCENT);
1070 double conf_bumpmult = getconf_real(CNF_BUMPMULT);
1072 printf("Current bump parameters:\n");
1074 if(conf_bumppercent == 0) {
1075 printf(" bumpsize %5d KB\t- minimum savings (threshold) to bump level 1 -> 2\n",
1076 getconf_int(CNF_BUMPSIZE));
1077 printf(" bumpdays %5d\t- minimum days at each level\n",
1078 getconf_int(CNF_BUMPDAYS));
1079 printf(" bumpmult %5.5g\t- threshold = bumpsize * bumpmult**(level-1)\n\n",
1082 printf(" Bump -> To Threshold\n");
1083 for(l = 1; l < 9; l++) {
1084 printf("\t%d -> %d %9d KB\n", l, l+1, bump_thresh(l));
1089 double bumppercent = conf_bumppercent;
1091 printf(" bumppercent %3d %%\t- minimum savings (threshold) to bump level 1 -> 2\n",
1093 printf(" bumpdays %5d\t- minimum days at each level\n",
1094 getconf_int(CNF_BUMPDAYS));
1095 printf(" bumpmult %5.5g\t- threshold = disk_size * bumppercent * bumpmult**(level-1)\n\n",
1098 printf(" Bump -> To Threshold\n");
1099 for(l = 1; l < 9; l++) {
1100 printf("\t%d -> %d %7.2f %%\n", l, l+1, bumppercent);
1101 bumppercent *= conf_bumpmult;
1102 if(bumppercent >= 100.000) { bumppercent = 100.0;}
1108 /* ----------------------------------------------- */
1110 void export_one P((disk_t *dp));
1112 void export_db(argc, argv)
1118 char hostname[MAX_HOSTNAME_LENGTH+1];
1121 printf("CURINFO Version %s CONF %s\n", version(), getconf_str(CNF_ORG));
1124 if(gethostname(hostname, sizeof(hostname)-1) == -1) {
1125 error("could not determine host name: %s", strerror(errno));
1127 hostname[sizeof(hostname)-1] = '\0';
1128 printf("# Generated by:\n# host: %s\n# date: %s",
1129 hostname, ctime(&curtime));
1131 printf("# command:");
1132 for(i = 0; i < argc; i++) {
1133 printf(" %s", argv[i]);
1136 printf("\n# This file can be merged back in with \"amadmin import\".\n");
1137 printf("# Edit only with care.\n");
1140 diskloop(argc, argv, "export", export_one);
1141 } else for(dp = diskqp->head; dp != NULL; dp = dp->next) {
1152 if(get_info(dp->host->hostname, dp->name, &info)) {
1153 fprintf(stderr, "Warning: no curinfo record for %s:%s\n",
1154 dp->host->hostname, dp->name);
1157 printf("host: %s\ndisk: %s\n", dp->host->hostname, dp->name);
1158 printf("command: %d\n", info.command);
1159 printf("last_level: %d\n",info.last_level);
1160 printf("consecutive_runs: %d\n",info.consecutive_runs);
1161 printf("full-rate:");
1162 for(i=0;i<AVG_COUNT;i++) printf(" %f", info.full.rate[i]);
1163 printf("\nfull-comp:");
1164 for(i=0;i<AVG_COUNT;i++) printf(" %f", info.full.comp[i]);
1166 printf("\nincr-rate:");
1167 for(i=0;i<AVG_COUNT;i++) printf(" %f", info.incr.rate[i]);
1168 printf("\nincr-comp:");
1169 for(i=0;i<AVG_COUNT;i++) printf(" %f", info.incr.comp[i]);
1171 for(l=0;l<DUMP_LEVELS;l++) {
1172 if(info.inf[l].date < (time_t)0 && info.inf[l].label[0] == '\0') continue;
1173 printf("stats: %d %ld %ld %ld %ld %d %s\n", l,
1174 info.inf[l].size, info.inf[l].csize, info.inf[l].secs,
1175 (long)info.inf[l].date, info.inf[l].filenum,
1178 for(l=0;info.history[l].level > -1;l++) {
1179 printf("history: %d %ld %ld %ld\n",info.history[l].level,
1180 info.history[l].size, info.history[l].csize,
1181 info.history[l].date);
1186 /* ----------------------------------------------- */
1188 int import_one P((void));
1189 char *impget_line P((void));
1191 void import_db(argc, argv)
1195 int vers_maj, vers_min, vers_patch, newer;
1202 /* process header line */
1204 if((line = agets(stdin)) == NULL) {
1205 fprintf(stderr, "%s: empty input.\n", get_pname());
1213 #define sc "CURINFO Version"
1214 if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1220 skip_whitespace(s, ch);
1222 || sscanf(s - 1, "%d.%d.%d", &vers_maj, &vers_min, &vers_patch) != 3) {
1226 skip_integer(s, ch); /* skip over major */
1231 skip_integer(s, ch); /* skip over minor */
1236 skip_integer(s, ch); /* skip over patch */
1242 skip_non_whitespace(s, ch);
1246 skip_whitespace(s, ch); /* find the org keyword */
1248 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1256 skip_whitespace(s, ch); /* find the org string */
1262 newer = (vers_maj != VERSION_MAJOR)? vers_maj > VERSION_MAJOR :
1263 (vers_min != VERSION_MINOR)? vers_min > VERSION_MINOR :
1264 vers_patch > VERSION_PATCH;
1267 "%s: WARNING: input is from newer Amanda version: %d.%d.%d.\n",
1268 get_pname(), vers_maj, vers_min, vers_patch);
1270 if(strcmp(org, getconf_str(CNF_ORG)) != 0) {
1271 fprintf(stderr, "%s: WARNING: input is from different org: %s\n",
1275 while(import_one());
1283 fprintf(stderr, "%s: bad CURINFO header line in input: %s.\n",
1285 fprintf(stderr, " Was the input in \"amadmin export\" format?\n");
1290 int import_one P((void))
1300 char *hostname = NULL;
1301 char *diskname = NULL;
1307 memset(&info, 0, sizeof(info_t));
1309 for(level = 0; level < DUMP_LEVELS; level++) {
1310 info.inf[level].date = (time_t)-1;
1313 /* get host: disk: command: lines */
1315 hostname = diskname = NULL;
1317 if((line = impget_line()) == NULL) return 0; /* nothing there */
1321 skip_whitespace(s, ch);
1323 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) goto parse_err;
1327 skip_whitespace(s, ch);
1328 if(ch == '\0') goto parse_err;
1330 skip_non_whitespace(s, ch);
1332 hostname = stralloc(fp);
1335 skip_whitespace(s, ch);
1338 if((line = impget_line()) == NULL) goto shortfile_err;
1341 skip_whitespace(s, ch);
1344 if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) goto parse_err;
1348 skip_whitespace(s, ch);
1349 if(ch == '\0') goto parse_err;
1351 skip_non_whitespace(s, ch);
1353 diskname = stralloc(fp);
1357 if((line = impget_line()) == NULL) goto shortfile_err;
1358 if(sscanf(line, "command: %d", &info.command) != 1) goto parse_err;
1360 /* get last_level and consecutive_runs */
1363 if((line = impget_line()) == NULL) goto shortfile_err;
1364 rc = sscanf(line, "last_level: %d", &info.last_level);
1367 if((line = impget_line()) == NULL) goto shortfile_err;
1368 if(sscanf(line, "consecutive_runs: %d", &info.consecutive_runs) != 1) goto parse_err;
1370 if((line = impget_line()) == NULL) goto shortfile_err;
1373 /* get rate: and comp: lines for full dumps */
1375 rc = sscanf(line, "full-rate: %f %f %f",
1376 &info.full.rate[0], &info.full.rate[1], &info.full.rate[2]);
1377 if(rc != 3) goto parse_err;
1380 if((line = impget_line()) == NULL) goto shortfile_err;
1381 rc = sscanf(line, "full-comp: %f %f %f",
1382 &info.full.comp[0], &info.full.comp[1], &info.full.comp[2]);
1383 if(rc != 3) goto parse_err;
1385 /* get rate: and comp: lines for incr dumps */
1388 if((line = impget_line()) == NULL) goto shortfile_err;
1389 rc = sscanf(line, "incr-rate: %f %f %f",
1390 &info.incr.rate[0], &info.incr.rate[1], &info.incr.rate[2]);
1391 if(rc != 3) goto parse_err;
1394 if((line = impget_line()) == NULL) goto shortfile_err;
1395 rc = sscanf(line, "incr-comp: %f %f %f",
1396 &info.incr.comp[0], &info.incr.comp[1], &info.incr.comp[2]);
1397 if(rc != 3) goto parse_err;
1399 /* get stats for dump levels */
1403 if((line = impget_line()) == NULL) goto shortfile_err;
1404 if(strncmp(line, "//", 2) == 0) {
1408 if(strncmp(line, "history:", 8) == 0) {
1412 memset(&onestat, 0, sizeof(onestat));
1417 skip_whitespace(s, ch);
1419 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1426 skip_whitespace(s, ch);
1427 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1430 skip_integer(s, ch);
1432 skip_whitespace(s, ch);
1433 if(ch == '\0' || sscanf(s - 1, "%ld", &onestat.size) != 1) {
1436 skip_integer(s, ch);
1438 skip_whitespace(s, ch);
1439 if(ch == '\0' || sscanf(s - 1, "%ld", &onestat.csize) != 1) {
1442 skip_integer(s, ch);
1444 skip_whitespace(s, ch);
1445 if(ch == '\0' || sscanf(s - 1, "%ld", &onestat.secs) != 1) {
1448 skip_integer(s, ch);
1450 skip_whitespace(s, ch);
1451 if(ch == '\0' || sscanf(s - 1, "%ld", &onedate) != 1) {
1454 skip_integer(s, ch);
1456 skip_whitespace(s, ch);
1458 if(sscanf(s - 1, "%d", &onestat.filenum) != 1) {
1461 skip_integer(s, ch);
1463 skip_whitespace(s, ch);
1465 if (onestat.filenum != 0)
1467 onestat.label[0] = '\0';
1469 strncpy(onestat.label, s - 1, sizeof(onestat.label)-1);
1470 onestat.label[sizeof(onestat.label)-1] = '\0';
1474 /* time_t not guarranteed to be long */
1475 onestat.date = onedate;
1476 if(level < 0 || level > 9) goto parse_err;
1478 info.inf[level] = onestat;
1481 for(i=0;i<=NB_HISTORY;i++) {
1482 info.history[i].level = -2;
1485 history_t onehistory;
1488 if(line[0] == '/' && line[1] == '/') {
1489 info.history[nb_history].level = -2;
1493 memset(&onehistory, 0, sizeof(onehistory));
1496 #define sc "history:"
1497 if(strncmp(line, sc, sizeof(sc)-1) != 0) {
1503 skip_whitespace(s, ch);
1504 if(ch == '\0' || sscanf((s - 1), "%d", &onehistory.level) != 1) {
1507 skip_integer(s, ch);
1509 skip_whitespace(s, ch);
1510 if(ch == '\0' || sscanf((s - 1), "%ld", &onehistory.size) != 1) {
1513 skip_integer(s, ch);
1515 skip_whitespace(s, ch);
1516 if(ch == '\0' || sscanf((s - 1), "%ld", &onehistory.csize) != 1) {
1519 skip_integer(s, ch);
1521 skip_whitespace(s, ch);
1522 if(ch == '\0' || sscanf((s - 1), "%ld", &date) != 1) {
1525 skip_integer(s, ch);
1526 onehistory.date = date; /* time_t not guarranteed to be long */
1528 info.history[nb_history++] = onehistory;
1530 if((line = impget_line()) == NULL) goto shortfile_err;
1534 /* got a full record, now write it out to the database */
1536 if(put_info(hostname, diskname, &info)) {
1537 fprintf(stderr, "%s: error writing record for %s:%s\n",
1538 get_pname(), hostname, diskname);
1548 fprintf(stderr, "%s: parse error reading import record.\n", get_pname());
1555 fprintf(stderr, "%s: short file reading import record.\n", get_pname());
1566 for(; (line = agets(stdin)) != NULL; free(line)) {
1570 skip_whitespace(s, ch);
1572 /* ignore comment lines */
1575 /* found non-blank, return line */
1578 /* otherwise, a blank line, so keep going */
1581 fprintf(stderr, "%s: reading stdin: %s\n",
1582 get_pname(), strerror(errno));
1587 /* ----------------------------------------------- */
1589 void disklist_one(dp)
1599 printf("line %d:\n", dp->line);
1601 printf(" host %s:\n", hp->hostname);
1602 printf(" interface %s\n",
1603 ip->name[0] ? ip->name : "default");
1605 printf(" disk %s:\n", dp->name);
1606 if(dp->device) printf(" device %s\n", dp->device);
1608 printf(" program \"%s\"\n", dp->program);
1609 if(dp->exclude_file != NULL && dp->exclude_file->nb_element > 0) {
1610 printf(" exclude file");
1611 for(excl = dp->exclude_file->first; excl != NULL; excl = excl->next) {
1612 printf(" \"%s\"", excl->name);
1616 if(dp->exclude_list != NULL && dp->exclude_list->nb_element > 0) {
1617 printf(" exclude list");
1618 if(dp->exclude_optional) printf(" optional");
1619 for(excl = dp->exclude_list->first; excl != NULL; excl = excl->next) {
1620 printf(" \"%s\"", excl->name);
1624 if(dp->include_file != NULL && dp->include_file->nb_element > 0) {
1625 printf(" include file");
1626 for(excl = dp->include_file->first; excl != NULL; excl = excl->next) {
1627 printf(" \"%s\"", excl->name);
1631 if(dp->include_list != NULL && dp->include_list->nb_element > 0) {
1632 printf(" include list");
1633 if(dp->include_optional) printf(" optional");
1634 for(excl = dp->include_list->first; excl != NULL; excl = excl->next) {
1635 printf(" \"%s\"", excl->name);
1639 printf(" priority %ld\n", dp->priority);
1640 printf(" dumpcycle %ld\n", dp->dumpcycle);
1641 printf(" maxdumps %d\n", dp->maxdumps);
1642 printf(" maxpromoteday %d\n", dp->maxpromoteday);
1643 if(dp->bumppercent > 0) {
1644 printf(" bumppercent %d\n", dp->bumppercent);
1647 printf(" bumpsize %d\n", dp->bumpsize);
1649 printf(" bumpdays %d\n", dp->bumpdays);
1650 printf(" bumpmult %f\n", dp->bumpmult);
1652 printf(" strategy ");
1653 switch(dp->strategy) {
1658 printf("STANDARD\n");
1670 printf("INCRONLY\n");
1674 printf(" estimate ");
1675 switch(dp->estimate) {
1683 printf("CALCSIZE\n");
1687 printf(" compress ");
1688 switch(dp->compress) {
1693 printf("CLIENT FAST\n");
1696 printf("CLIENT BEST\n");
1698 case COMP_SERV_FAST:
1699 printf("SERVER FAST\n");
1701 case COMP_SERV_BEST:
1702 printf("SERVER BEST\n");
1705 if(dp->compress != COMP_NONE) {
1706 printf(" comprate %.2f %.2f\n",
1707 dp->comprate[0], dp->comprate[1]);
1719 printf(" kencrypt %s\n", (dp->kencrypt? "YES" : "NO"));
1721 printf(" holdingdisk %s\n", (!dp->no_hold? "YES" : "NO"));
1722 printf(" record %s\n", (dp->record? "YES" : "NO"));
1723 printf(" index %s\n", (dp->index? "YES" : "NO"));
1724 printf(" skip-incr %s\n", (dp->skip_incr? "YES" : "NO"));
1725 printf(" skip-full %s\n", (dp->skip_full? "YES" : "NO"));
1730 void disklist(argc, argv)
1737 diskloop(argc, argv, "disklist", disklist_one);
1739 for(dp = diskqp->head; dp != NULL; dp = dp->next)