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.105 2006/02/03 17:29:28 vectro 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((int argc, char **argv));
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 void show_version P((int argc, char **argv));
78 static void check_dumpuser P((void));
80 static char *conf_tapelist = NULL;
81 static char *displayunit;
82 static long int unitdivisor;
86 void (*fn) P((int, char **));
89 { "version", show_version,
90 "\t\t\t\t# Show version info." },
92 " [<hostname> [<disks>]* ]+\t# Force level 0 at next run." },
94 " [<hostname> [<disks>]* ]+\t# Clear force command." },
95 { "force-bump", force_bump,
96 " [<hostname> [<disks>]* ]+\t# Force bump at next run." },
97 { "force-no-bump", force_no_bump,
98 " [<hostname> [<disks>]* ]+\t# Force no-bump at next run." },
99 { "unforce-bump", unforce_bump,
100 " [<hostname> [<disks>]* ]+\t# Clear bump command." },
102 " <tapelabel> ...\t\t# re-use this tape." },
103 { "no-reuse", noreuse,
104 " <tapelabel> ...\t# never re-use this tape." },
106 " [<hostname> [<disks>]* ]*\t# Show which tapes these dumps are on." },
108 " [<hostname> [<disks>]* ]+\t# Delete from database." },
110 " [<hostname> [<disks>]* ]*\t# Show current info records." },
112 " [<hostname> [<disks>]* ]*\t# Show due date." },
113 { "balance", balance,
114 " [-days <num>]\t\t# Show nightly dump size balance." },
116 " [-days <num>]\t\t\t# Show which tape is due next." },
117 { "bumpsize", bumpsize,
118 "\t\t\t# Show current bump thresholds." },
119 { "export", export_db,
120 " [<hostname> [<disks>]* ]*\t# Export curinfo database to stdout." },
121 { "import", import_db,
122 "\t\t\t\t# Import curinfo database from stdin." },
123 { "disklist", disklist,
124 " [<hostname> [<disks>]* ]*\t# Debug disklist entries." },
126 #define NCMDS (sizeof(cmdtab) / sizeof(cmdtab[0]))
136 unsigned long malloc_hist_1, malloc_size_1;
137 unsigned long malloc_hist_2, malloc_size_2;
142 set_pname("amadmin");
144 /* Don't die when child closes pipe */
145 signal(SIGPIPE, SIG_IGN);
147 malloc_size_1 = malloc_inuse(&malloc_hist_1);
149 erroutput_type = ERR_INTERACTIVE;
151 if(argc < 3) usage();
153 if(strcmp(argv[2],"version") == 0) {
154 show_version(argc, argv);
158 config_name = argv[1];
159 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
160 conffile = stralloc2(config_dir, CONFFILE_NAME);
162 if(read_conffile(conffile))
163 error("errors processing config file \"%s\"", conffile);
168 conf_diskfile = getconf_str(CNF_DISKFILE);
169 if (*conf_diskfile == '/') {
170 conf_diskfile = stralloc(conf_diskfile);
172 conf_diskfile = stralloc2(config_dir, conf_diskfile);
174 if (read_diskfile(conf_diskfile, &diskq) < 0)
175 error("could not load disklist \"%s\"", conf_diskfile);
176 amfree(conf_diskfile);
178 conf_tapelist = getconf_str(CNF_TAPELIST);
179 if (*conf_tapelist == '/') {
180 conf_tapelist = stralloc(conf_tapelist);
182 conf_tapelist = stralloc2(config_dir, conf_tapelist);
184 if(read_tapelist(conf_tapelist))
185 error("could not load tapelist \"%s\"", conf_tapelist);
187 conf_infofile = getconf_str(CNF_INFOFILE);
188 if (*conf_infofile == '/') {
189 conf_infofile = stralloc(conf_infofile);
191 conf_infofile = stralloc2(config_dir, conf_infofile);
193 if(open_infofile(conf_infofile))
194 error("could not open info db \"%s\"", conf_infofile);
195 amfree(conf_infofile);
197 displayunit = getconf_str(CNF_DISPLAYUNIT);
198 unitdivisor = getconf_unit_divisor();
200 for (i = 0; i < NCMDS; i++)
201 if (strcmp(argv[2], cmdtab[i].name) == 0) {
202 (*cmdtab[i].fn)(argc, argv);
206 fprintf(stderr, "%s: unknown command \"%s\"\n", argv[0], argv[2]);
212 amfree(conf_tapelist);
217 malloc_size_2 = malloc_inuse(&malloc_hist_2);
219 if(malloc_size_1 != malloc_size_2) {
220 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
231 fprintf(stderr, "\nUsage: %s%s <conf> <command> {<args>} ...\n",
232 get_pname(), versionsuffix());
233 fprintf(stderr, " Valid <command>s are:\n");
234 for (i = 0; i < NCMDS; i++)
235 fprintf(stderr, "\t%s%s\n", cmdtab[i].name, cmdtab[i].usage);
240 /* ----------------------------------------------- */
242 #define SECS_PER_DAY (24*60*60)
245 char *seqdatestr(seq)
249 static char *dow[7] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
250 time_t t = today + seq*SECS_PER_DAY;
255 snprintf(str, sizeof(str),
256 "%2d/%02d %3s", tm->tm_mon+1, tm->tm_mday, dow[tm->tm_wday]);
261 #define days_diff(a, b) (((b) - (a) + SECS_PER_DAY) / SECS_PER_DAY)
263 /* when is next level 0 due? 0 = tonight, 1 = tommorrow, etc*/
264 static int next_level0(dp, info)
268 if(dp->strategy == DS_NOFULL)
269 return 1; /* fake it */
270 if(info->inf[0].date < (time_t)0)
271 return 0; /* new disk */
273 return dp->dumpcycle - days_diff(info->inf[0].date, today);
276 static void check_dumpuser()
278 static int been_here = 0;
288 uid_dumpuser = uid_me;
289 dumpuser = getconf_str(CNF_DUMPUSER);
291 if ((pw = getpwnam(dumpuser)) == NULL) {
292 error("cannot look up dump user \"%s\"", dumpuser);
295 uid_dumpuser = pw->pw_uid;
296 if ((pw = getpwuid(uid_me)) == NULL) {
297 error("cannot look up my own uid %ld", (long)uid_me);
300 if (uid_me != uid_dumpuser) {
301 error("ERROR: running as user \"%s\" instead of \"%s\"",
302 pw->pw_name, dumpuser);
308 /* ----------------------------------------------- */
310 void diskloop(argc, argv, cmdname, func)
314 void (*func) P((disk_t *dp));
320 fprintf(stderr,"%s: expecting \"%s [<hostname> [<disks>]* ]+\"\n",
321 get_pname(), cmdname);
325 match_disklist(&diskq, argc-3, argv+3);
327 for(dp = diskq.head; dp != NULL; dp = dp->next) {
334 fprintf(stderr,"%s: no disk matched\n",get_pname());
338 /* ----------------------------------------------- */
344 char *hostname = dp->host->hostname;
345 char *diskname = dp->name;
351 get_info(hostname, diskname, &info);
352 SET(info.command, FORCE_FULL);
353 if (ISSET(info.command, FORCE_BUMP)) {
354 CLR(info.command, FORCE_BUMP);
355 printf("%s: WARNING: %s:%s FORCE_BUMP command was cleared.\n",
356 get_pname(), hostname, diskname);
358 if(put_info(hostname, diskname, &info) == 0) {
359 printf("%s: %s:%s is set to a forced level 0 at next run.\n",
360 get_pname(), hostname, diskname);
362 fprintf(stderr, "%s: %s:%s could not be forced.\n",
363 get_pname(), hostname, diskname);
368 void force(argc, argv)
372 diskloop(argc, argv, "force", force_one);
376 /* ----------------------------------------------- */
382 char *hostname = dp->host->hostname;
383 char *diskname = dp->name;
386 get_info(hostname, diskname, &info);
387 if (ISSET(info.command, FORCE_FULL)) {
391 CLR(info.command, FORCE_FULL);
392 if(put_info(hostname, diskname, &info) == 0){
393 printf("%s: force command for %s:%s cleared.\n",
394 get_pname(), hostname, diskname);
397 "%s: force command for %s:%s could not be cleared.\n",
398 get_pname(), hostname, diskname);
402 printf("%s: no force command outstanding for %s:%s, unchanged.\n",
403 get_pname(), hostname, diskname);
407 void unforce(argc, argv)
411 diskloop(argc, argv, "unforce", unforce_one);
415 /* ----------------------------------------------- */
418 void force_bump_one(dp)
421 char *hostname = dp->host->hostname;
422 char *diskname = dp->name;
428 get_info(hostname, diskname, &info);
429 SET(info.command, FORCE_BUMP);
430 if (ISSET(info.command, FORCE_NO_BUMP)) {
431 CLR(info.command, FORCE_NO_BUMP);
432 printf("%s: WARNING: %s:%s FORCE_NO_BUMP command was cleared.\n",
433 get_pname(), hostname, diskname);
435 if (ISSET(info.command, FORCE_FULL)) {
436 CLR(info.command, FORCE_FULL);
437 printf("%s: WARNING: %s:%s FORCE_FULL command was cleared.\n",
438 get_pname(), hostname, diskname);
440 if(put_info(hostname, diskname, &info) == 0) {
441 printf("%s: %s:%s is set to bump at next run.\n",
442 get_pname(), hostname, diskname);
444 fprintf(stderr, "%s: %s:%s could not be forced to bump.\n",
445 get_pname(), hostname, diskname);
450 void force_bump(argc, argv)
454 diskloop(argc, argv, "force-bump", force_bump_one);
458 /* ----------------------------------------------- */
461 void force_no_bump_one(dp)
464 char *hostname = dp->host->hostname;
465 char *diskname = dp->name;
471 get_info(hostname, diskname, &info);
472 SET(info.command, FORCE_NO_BUMP);
473 if (ISSET(info.command, FORCE_BUMP)) {
474 CLR(info.command, FORCE_BUMP);
475 printf("%s: WARNING: %s:%s FORCE_BUMP command was cleared.\n",
476 get_pname(), hostname, diskname);
478 if(put_info(hostname, diskname, &info) == 0) {
479 printf("%s: %s:%s is set to not bump at next run.\n",
480 get_pname(), hostname, diskname);
482 fprintf(stderr, "%s: %s:%s could not be force to not bump.\n",
483 get_pname(), hostname, diskname);
488 void force_no_bump(argc, argv)
492 diskloop(argc, argv, "force-no-bump", force_no_bump_one);
496 /* ----------------------------------------------- */
499 void unforce_bump_one(dp)
502 char *hostname = dp->host->hostname;
503 char *diskname = dp->name;
506 get_info(hostname, diskname, &info);
507 if (ISSET(info.command, FORCE_BUMP|FORCE_NO_BUMP)) {
511 CLR(info.command, FORCE_BUMP|FORCE_NO_BUMP);
512 if(put_info(hostname, diskname, &info) == 0) {
513 printf("%s: bump command for %s:%s cleared.\n",
514 get_pname(), hostname, diskname);
516 fprintf(stderr, "%s: %s:%s bump command could not be cleared.\n",
517 get_pname(), hostname, diskname);
521 printf("%s: no bump command outstanding for %s:%s, unchanged.\n",
522 get_pname(), hostname, diskname);
527 void unforce_bump(argc, argv)
531 diskloop(argc, argv, "unforce-bump", unforce_bump_one);
535 /* ----------------------------------------------- */
537 void reuse(argc, argv)
545 fprintf(stderr,"%s: expecting \"reuse <tapelabel> ...\"\n",
551 for(count=3; count< argc; count++) {
552 tp = lookup_tapelabel(argv[count]);
554 fprintf(stderr, "reuse: tape label %s not found in tapelist.\n",
558 if( tp->reuse == 0 ) {
560 printf("%s: marking tape %s as reusable.\n",
561 get_pname(), argv[count]);
563 fprintf(stderr, "%s: tape %s already reusable.\n",
564 get_pname(), argv[count]);
568 if(write_tapelist(conf_tapelist)) {
569 error("could not write tapelist \"%s\"", conf_tapelist);
573 void noreuse(argc, argv)
581 fprintf(stderr,"%s: expecting \"no-reuse <tapelabel> ...\"\n",
587 for(count=3; count< argc; count++) {
588 tp = lookup_tapelabel(argv[count]);
590 fprintf(stderr, "no-reuse: tape label %s not found in tapelist.\n",
594 if( tp->reuse == 1 ) {
596 printf("%s: marking tape %s as not reusable.\n",
597 get_pname(), argv[count]);
599 fprintf(stderr, "%s: tape %s already not reusable.\n",
600 get_pname(), argv[count]);
604 if(write_tapelist(conf_tapelist)) {
605 error("could not write tapelist \"%s\"", conf_tapelist);
610 /* ----------------------------------------------- */
617 char *hostname = dp->host->hostname;
618 char *diskname = dp->name;
621 if(get_info(hostname, diskname, &info)) {
622 printf("%s: %s:%s NOT currently in database.\n",
623 get_pname(), hostname, diskname);
628 if(del_info(hostname, diskname))
629 error("couldn't delete %s:%s from database: %s",
630 hostname, diskname, strerror(errno));
632 printf("%s: %s:%s deleted from curinfo database.\n",
633 get_pname(), hostname, diskname);
636 void delete(argc, argv)
641 diskloop(argc, argv, "delete", delete_one);
645 "%s: NOTE: you'll have to remove these from the disklist yourself.\n",
649 /* ----------------------------------------------- */
659 get_info(dp->host->hostname, dp->name, &info);
661 printf("\nCurrent info for %s %s:\n", dp->host->hostname, dp->name);
662 if (ISSET(info.command, FORCE_FULL))
663 printf(" (Forcing to level 0 dump at next run)\n");
664 if (ISSET(info.command, FORCE_BUMP))
665 printf(" (Forcing bump at next run)\n");
666 if (ISSET(info.command, FORCE_NO_BUMP))
667 printf(" (Forcing no-bump at next run)\n");
668 printf(" Stats: dump rates (kps), Full: %5.1f, %5.1f, %5.1f\n",
669 info.full.rate[0], info.full.rate[1], info.full.rate[2]);
670 printf(" Incremental: %5.1f, %5.1f, %5.1f\n",
671 info.incr.rate[0], info.incr.rate[1], info.incr.rate[2]);
672 printf(" compressed size, Full: %5.1f%%,%5.1f%%,%5.1f%%\n",
673 info.full.comp[0]*100, info.full.comp[1]*100, info.full.comp[2]*100);
674 printf(" Incremental: %5.1f%%,%5.1f%%,%5.1f%%\n",
675 info.incr.comp[0]*100, info.incr.comp[1]*100, info.incr.comp[2]*100);
677 printf(" Dumps: lev datestmp tape file origK compK secs\n");
678 for(lev = 0, sp = &info.inf[0]; lev < 9; lev++, sp++) {
679 if(sp->date < (time_t)0 && sp->label[0] == '\0') continue;
680 tm = localtime(&sp->date);
681 printf(" %d %04d%02d%02d %-15s %4d %7ld %7ld %4ld\n",
682 lev, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
683 sp->label, sp->filenum, sp->size, sp->csize, sp->secs);
688 void info(argc, argv)
695 diskloop(argc, argv, "info", info_one);
697 for(dp = diskq.head; dp != NULL; dp = dp->next)
701 /* ----------------------------------------------- */
711 if(get_info(hp->hostname, dp->name, &info)) {
712 printf("new disk %s:%s ignored.\n", hp->hostname, dp->name);
715 days = next_level0(dp, &info);
717 printf("Overdue %2d day%s %s:%s\n",
718 -days, (-days == 1) ? ": " : "s:",
719 hp->hostname, dp->name);
722 printf("Due today: %s:%s\n", hp->hostname, dp->name);
725 printf("Due in %2d day%s %s:%s\n", days,
726 (days == 1) ? ": " : "s:",
727 hp->hostname, dp->name);
740 diskloop(argc, argv, "due", due_one);
742 for(dp = diskq.head; dp != NULL; dp = dp->next)
746 /* ----------------------------------------------- */
748 void tape(argc, argv)
756 if(argc > 4 && strcmp(argv[3],"--days") == 0) {
757 nb_days = atoi(argv[4]);
759 printf("days must be an integer bigger than 0\n");
764 runtapes = getconf_int(CNF_RUNTAPES);
765 tp = lookup_last_reusable_tape(0);
767 for ( j=0 ; j < nb_days ; j++ ) {
768 for ( i=0 ; i < runtapes ; i++ ) {
770 printf("The next Amanda run should go onto ");
774 printf("tape %s or ", tp->label);
775 printf("a new tape.\n");
777 tp = lookup_last_reusable_tape(i + 1);
780 lasttp = lookup_tapepos(lookup_nb_tape());
782 if(lasttp && i > 0 && lasttp->datestamp == 0) {
784 while(lasttp && i > 0 && lasttp->datestamp == 0) {
786 lasttp = lasttp->prev;
789 lasttp = lookup_tapepos(lookup_nb_tape());
792 printf("The next new tape already labelled is: %s.\n",
796 printf("The next %d new tapes already labelled are: %s", c,
798 lasttp = lasttp->prev;
800 while(lasttp && c > 0 && lasttp->datestamp == 0) {
801 printf(", %s", lasttp->label);
802 lasttp = lasttp->prev;
810 /* ----------------------------------------------- */
812 void balance(argc, argv)
817 struct balance_stats {
819 long origsize, outsize;
821 int conf_runspercycle, conf_dumpcycle;
822 int seq, runs_per_cycle, overdue, max_overdue;
823 int later, total, balance, distinct;
824 float fseq, disk_dumpcycle;
826 long int total_balanced, balanced;
830 conf_dumpcycle = getconf_int(CNF_DUMPCYCLE);
831 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
832 later = conf_dumpcycle;
833 if(later > 10000) later = 10000;
837 if(argc > 4 && strcmp(argv[3],"--days") == 0) {
838 later = atoi(argv[4]);
839 if(later < 0) later = conf_dumpcycle;
842 if(conf_runspercycle == 0) {
843 runs_per_cycle = conf_dumpcycle;
844 } else if(conf_runspercycle == -1 ) {
845 runs_per_cycle = guess_runs_from_tapelist();
847 runs_per_cycle = conf_runspercycle;
849 if (runs_per_cycle <= 0) {
855 distinct = later + 3;
857 sp = (struct balance_stats *)
858 alloc(sizeof(struct balance_stats) * (distinct+1));
860 for(seq=0; seq <= distinct; seq++)
861 sp[seq].disks = sp[seq].origsize = sp[seq].outsize = 0;
863 for(dp = diskq.head; dp != NULL; dp = dp->next) {
864 if(get_info(dp->host->hostname, dp->name, &info)) {
865 printf("new disk %s:%s ignored.\n", dp->host->hostname, dp->name);
868 if (dp->strategy == DS_NOFULL) {
871 sp[distinct].disks++;
872 sp[distinct].origsize += info.inf[0].size/unitdivisor;
873 sp[distinct].outsize += info.inf[0].csize/unitdivisor;
876 if(dp->dumpcycle == 0) {
877 sp[balance].origsize += (info.inf[0].size/unitdivisor) * runs_per_cycle;
878 sp[balance].outsize += (info.inf[0].csize/unitdivisor) * runs_per_cycle;
881 sp[balance].origsize += (info.inf[0].size/unitdivisor) *
882 (conf_dumpcycle / dp->dumpcycle);
883 sp[balance].outsize += (info.inf[0].csize/unitdivisor) *
884 (conf_dumpcycle / dp->dumpcycle);
887 disk_dumpcycle = dp->dumpcycle;
888 if(dp->dumpcycle <= 0)
889 disk_dumpcycle = ((float)conf_dumpcycle) / ((float)runs_per_cycle);
891 seq = next_level0(dp, &info);
896 if (-seq > max_overdue)
906 sp[seq].origsize += info.inf[0].size/unitdivisor;
907 sp[seq].outsize += info.inf[0].csize/unitdivisor;
911 sp[total].origsize += info.inf[0].size/unitdivisor;
912 sp[total].outsize += info.inf[0].csize/unitdivisor;
915 /* See, if there's another run in this dumpcycle */
916 fseq += disk_dumpcycle;
918 } while (seq < later);
921 if(sp[total].outsize == 0 && sp[later].outsize == 0) {
922 printf("\nNo data to report on yet.\n");
927 balanced = sp[balance].outsize / runs_per_cycle;
928 if(conf_dumpcycle == later) {
929 total_balanced = sp[total].outsize / runs_per_cycle;
932 total_balanced = 1024*(((sp[total].outsize/1024) * conf_dumpcycle)
933 / (runs_per_cycle * later));
937 printf("\n due-date #fs orig %cB out %cB balance\n",
938 displayunit[0], displayunit[0]);
939 printf("----------------------------------------------\n");
940 for(seq = 0; seq < later; seq++) {
941 if(sp[seq].disks == 0 &&
942 ((seq > 0 && sp[seq-1].disks == 0) ||
943 ((seq < later-1) && sp[seq+1].disks == 0))) {
951 printf("%-9.9s %3d %10ld %10ld ",
952 seqdatestr(seq), sp[seq].disks,
953 sp[seq].origsize, sp[seq].outsize);
954 if(!sp[seq].outsize) printf(" --- \n");
955 else printf("%+8.1f%%\n",
956 (sp[seq].outsize-balanced)*100.0/(double)balanced);
960 if(sp[later].disks != 0) {
961 printf("later %3d %10ld %10ld ",
963 sp[later].origsize, sp[later].outsize);
964 if(!sp[later].outsize) printf(" --- \n");
965 else printf("%+8.1f%%\n",
966 (sp[later].outsize-balanced)*100.0/(double)balanced);
968 printf("----------------------------------------------\n");
969 printf("TOTAL %3d %10ld %10ld %9ld\n", sp[total].disks,
970 sp[total].origsize, sp[total].outsize, total_balanced);
971 if (sp[balance].origsize != sp[total].origsize ||
972 sp[balance].outsize != sp[total].outsize ||
973 balanced != total_balanced) {
974 printf("BALANCED %10ld %10ld %9ld\n",
975 sp[balance].origsize, sp[balance].outsize, balanced);
977 if (sp[distinct].disks != sp[total].disks) {
978 printf("DISTINCT %3d %10ld %10ld\n", sp[distinct].disks,
979 sp[distinct].origsize, sp[distinct].outsize);
981 printf(" (estimated %d run%s per dumpcycle)\n",
982 runs_per_cycle, (runs_per_cycle == 1) ? "" : "s");
984 printf(" (%d filesystem%s overdue, the most being overdue %d day%s)\n",
985 overdue, (overdue == 1) ? "" : "s",
986 max_overdue, (max_overdue == 1) ? "" : "s");
992 /* ----------------------------------------------- */
994 void find(argc, argv)
999 char *sort_order = NULL;
1000 find_result_t *output_find;
1004 "%s: expecting \"find [--sort <hkdlpb>] [hostname [<disk>]]*\"\n",
1010 sort_order = newstralloc(sort_order, DEFAULT_SORT_ORDER);
1011 if(argc > 4 && strcmp(argv[3],"--sort") == 0) {
1012 int i, valid_sort=1;
1014 for(i=strlen(argv[4])-1;i>=0;i--) {
1015 switch (argv[4][i]) {
1027 default: valid_sort=0;
1031 sort_order = newstralloc(sort_order, argv[4]);
1033 printf("Invalid sort order: %s\n", argv[4]);
1034 printf("Use default sort order: %s\n", sort_order);
1040 match_disklist(&diskq, argc-(start_argc-1), argv+(start_argc-1));
1041 output_find = find_dump(1, &diskq);
1042 if(argc-(start_argc-1) > 0) {
1043 free_find_result(&output_find);
1044 match_disklist(&diskq, argc-(start_argc-1), argv+(start_argc-1));
1045 output_find = find_dump(0, NULL);
1048 sort_find_result(sort_order, &output_find);
1049 print_find_result(output_find);
1050 free_find_result(&output_find);
1056 /* ------------------------ */
1059 /* shared code with planner.c */
1061 int bump_thresh(level)
1064 int bump = getconf_int(CNF_BUMPSIZE);
1065 double mult = getconf_real(CNF_BUMPMULT);
1067 while(--level) bump = (int) bump * mult;
1071 void bumpsize(argc, argv)
1076 int conf_bumppercent = getconf_int(CNF_BUMPPERCENT);
1077 double conf_bumpmult = getconf_real(CNF_BUMPMULT);
1079 printf("Current bump parameters:\n");
1080 if(conf_bumppercent == 0) {
1081 printf(" bumpsize %5d KB\t- minimum savings (threshold) to bump level 1 -> 2\n",
1082 getconf_int(CNF_BUMPSIZE));
1083 printf(" bumpdays %5d\t- minimum days at each level\n",
1084 getconf_int(CNF_BUMPDAYS));
1085 printf(" bumpmult %5.5g\t- threshold = bumpsize * bumpmult**(level-1)\n\n",
1088 printf(" Bump -> To Threshold\n");
1089 for(l = 1; l < 9; l++)
1090 printf("\t%d -> %d %9d KB\n", l, l+1, bump_thresh(l));
1094 double bumppercent = conf_bumppercent;
1096 printf(" bumppercent %3d %%\t- minimum savings (threshold) to bump level 1 -> 2\n",
1098 printf(" bumpdays %5d\t- minimum days at each level\n",
1099 getconf_int(CNF_BUMPDAYS));
1100 printf(" bumpmult %5.5g\t- threshold = disk_size * bumppercent * bumpmult**(level-1)\n\n",
1102 printf(" Bump -> To Threshold\n");
1103 for(l = 1; l < 9; l++) {
1104 printf("\t%d -> %d %7.2f %%\n", l, l+1, bumppercent);
1105 bumppercent *= conf_bumpmult;
1106 if(bumppercent >= 100.000) { bumppercent = 100.0;}
1112 /* ----------------------------------------------- */
1114 void export_one P((disk_t *dp));
1116 void export_db(argc, argv)
1122 char hostname[MAX_HOSTNAME_LENGTH+1];
1125 printf("CURINFO Version %s CONF %s\n", version(), getconf_str(CNF_ORG));
1128 if(gethostname(hostname, sizeof(hostname)-1) == -1)
1129 error("could not determine host name: %s\n", strerror(errno));
1130 hostname[sizeof(hostname)-1] = '\0';
1131 printf("# Generated by:\n# host: %s\n# date: %s",
1132 hostname, ctime(&curtime));
1134 printf("# command:");
1135 for(i = 0; i < argc; i++)
1136 printf(" %s", argv[i]);
1138 printf("\n# This file can be merged back in with \"amadmin import\".\n");
1139 printf("# Edit only with care.\n");
1142 diskloop(argc, argv, "export", export_one);
1143 else for(dp = diskq.head; dp != NULL; dp = dp->next)
1153 if(get_info(dp->host->hostname, dp->name, &info)) {
1154 fprintf(stderr, "Warning: no curinfo record for %s:%s\n",
1155 dp->host->hostname, dp->name);
1158 printf("host: %s\ndisk: %s\n", dp->host->hostname, dp->name);
1159 printf("command: %d\n", info.command);
1160 printf("last_level: %d\n",info.last_level);
1161 printf("consecutive_runs: %d\n",info.consecutive_runs);
1162 printf("full-rate:");
1163 for(i=0;i<AVG_COUNT;i++) printf(" %f", info.full.rate[i]);
1164 printf("\nfull-comp:");
1165 for(i=0;i<AVG_COUNT;i++) printf(" %f", info.full.comp[i]);
1167 printf("\nincr-rate:");
1168 for(i=0;i<AVG_COUNT;i++) printf(" %f", info.incr.rate[i]);
1169 printf("\nincr-comp:");
1170 for(i=0;i<AVG_COUNT;i++) printf(" %f", info.incr.comp[i]);
1172 for(l=0;l<DUMP_LEVELS;l++) {
1173 if(info.inf[l].date < (time_t)0 && info.inf[l].label[0] == '\0') continue;
1174 printf("stats: %d %ld %ld %ld %ld %d %s\n", l,
1175 info.inf[l].size, info.inf[l].csize, info.inf[l].secs,
1176 (long)info.inf[l].date, info.inf[l].filenum,
1179 for(l=0;info.history[l].level > -1;l++) {
1180 printf("history: %d %ld %ld %ld\n",info.history[l].level,
1181 info.history[l].size, info.history[l].csize,
1182 info.history[l].date);
1187 /* ----------------------------------------------- */
1189 int import_one P((void));
1190 char *impget_line P((void));
1192 void import_db(argc, argv)
1196 int vers_maj, vers_min, vers_patch, newer;
1203 /* process header line */
1205 if((line = agets(stdin)) == NULL) {
1206 fprintf(stderr, "%s: empty input.\n", get_pname());
1214 #define sc "CURINFO Version"
1215 if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1221 skip_whitespace(s, ch);
1223 || sscanf(s - 1, "%d.%d.%d", &vers_maj, &vers_min, &vers_patch) != 3) {
1227 skip_integer(s, ch); /* skip over major */
1232 skip_integer(s, ch); /* skip over minor */
1237 skip_integer(s, ch); /* skip over patch */
1243 skip_non_whitespace(s, ch);
1247 skip_whitespace(s, ch); /* find the org keyword */
1249 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1257 skip_whitespace(s, ch); /* find the org string */
1263 newer = (vers_maj != VERSION_MAJOR)? vers_maj > VERSION_MAJOR :
1264 (vers_min != VERSION_MINOR)? vers_min > VERSION_MINOR :
1265 vers_patch > VERSION_PATCH;
1268 "%s: WARNING: input is from newer Amanda version: %d.%d.%d.\n",
1269 get_pname(), vers_maj, vers_min, vers_patch);
1271 if(strcmp(org, getconf_str(CNF_ORG)) != 0) {
1272 fprintf(stderr, "%s: WARNING: input is from different org: %s\n",
1276 while(import_one());
1284 fprintf(stderr, "%s: bad CURINFO header line in input: %s.\n",
1286 fprintf(stderr, " Was the input in \"amadmin export\" format?\n");
1291 int import_one P((void))
1301 char *hostname = NULL;
1302 char *diskname = NULL;
1308 memset(&info, 0, sizeof(info_t));
1310 for(level = 0; level < DUMP_LEVELS; level++) {
1311 info.inf[level].date = (time_t)-1;
1314 /* get host: disk: command: lines */
1316 hostname = diskname = NULL;
1318 if((line = impget_line()) == NULL) return 0; /* nothing there */
1322 skip_whitespace(s, ch);
1324 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) goto parse_err;
1328 skip_whitespace(s, ch);
1329 if(ch == '\0') goto parse_err;
1331 skip_non_whitespace(s, ch);
1333 hostname = stralloc(fp);
1336 skip_whitespace(s, ch);
1339 if((line = impget_line()) == NULL) goto shortfile_err;
1342 skip_whitespace(s, ch);
1345 if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) goto parse_err;
1349 skip_whitespace(s, ch);
1350 if(ch == '\0') goto parse_err;
1352 skip_non_whitespace(s, ch);
1354 diskname = stralloc(fp);
1358 if((line = impget_line()) == NULL) goto shortfile_err;
1359 if(sscanf(line, "command: %d", &info.command) != 1) goto parse_err;
1361 /* get last_level and consecutive_runs */
1364 if((line = impget_line()) == NULL) goto shortfile_err;
1365 rc = sscanf(line, "last_level: %d", &info.last_level);
1368 if((line = impget_line()) == NULL) goto shortfile_err;
1369 if(sscanf(line, "consecutive_runs: %d", &info.consecutive_runs) != 1) goto parse_err;
1371 if((line = impget_line()) == NULL) goto shortfile_err;
1374 /* get rate: and comp: lines for full dumps */
1376 rc = sscanf(line, "full-rate: %f %f %f",
1377 &info.full.rate[0], &info.full.rate[1], &info.full.rate[2]);
1378 if(rc != 3) goto parse_err;
1381 if((line = impget_line()) == NULL) goto shortfile_err;
1382 rc = sscanf(line, "full-comp: %f %f %f",
1383 &info.full.comp[0], &info.full.comp[1], &info.full.comp[2]);
1384 if(rc != 3) goto parse_err;
1386 /* get rate: and comp: lines for incr dumps */
1389 if((line = impget_line()) == NULL) goto shortfile_err;
1390 rc = sscanf(line, "incr-rate: %f %f %f",
1391 &info.incr.rate[0], &info.incr.rate[1], &info.incr.rate[2]);
1392 if(rc != 3) goto parse_err;
1395 if((line = impget_line()) == NULL) goto shortfile_err;
1396 rc = sscanf(line, "incr-comp: %f %f %f",
1397 &info.incr.comp[0], &info.incr.comp[1], &info.incr.comp[2]);
1398 if(rc != 3) goto parse_err;
1400 /* get stats for dump levels */
1404 if((line = impget_line()) == NULL) goto shortfile_err;
1405 if(strncmp(line, "//", 2) == 0) {
1409 if(strncmp(line, "history:", 8) == 0) {
1413 memset(&onestat, 0, sizeof(onestat));
1418 skip_whitespace(s, ch);
1420 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1427 skip_whitespace(s, ch);
1428 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1431 skip_integer(s, ch);
1433 skip_whitespace(s, ch);
1434 if(ch == '\0' || sscanf(s - 1, "%ld", &onestat.size) != 1) {
1437 skip_integer(s, ch);
1439 skip_whitespace(s, ch);
1440 if(ch == '\0' || sscanf(s - 1, "%ld", &onestat.csize) != 1) {
1443 skip_integer(s, ch);
1445 skip_whitespace(s, ch);
1446 if(ch == '\0' || sscanf(s - 1, "%ld", &onestat.secs) != 1) {
1449 skip_integer(s, ch);
1451 skip_whitespace(s, ch);
1452 if(ch == '\0' || sscanf(s - 1, "%ld", &onedate) != 1) {
1455 skip_integer(s, ch);
1457 skip_whitespace(s, ch);
1459 if(sscanf(s - 1, "%d", &onestat.filenum) != 1) {
1462 skip_integer(s, ch);
1464 skip_whitespace(s, ch);
1466 if (onestat.filenum != 0)
1468 onestat.label[0] = '\0';
1470 strncpy(onestat.label, s - 1, sizeof(onestat.label)-1);
1471 onestat.label[sizeof(onestat.label)-1] = '\0';
1475 /* time_t not guarranteed to be long */
1476 onestat.date = onedate;
1477 if(level < 0 || level > 9) goto parse_err;
1479 info.inf[level] = onestat;
1482 for(i=0;i<=NB_HISTORY;i++) {
1483 info.history[i].level = -2;
1486 history_t onehistory;
1489 if(line[0] == '/' && line[1] == '/') {
1490 info.history[nb_history].level = -2;
1494 memset(&onehistory, 0, sizeof(onehistory));
1497 #define sc "history:"
1498 if(strncmp(line, sc, sizeof(sc)-1) != 0) {
1504 skip_whitespace(s, ch);
1505 if(ch == '\0' || sscanf((s - 1), "%d", &onehistory.level) != 1) {
1508 skip_integer(s, ch);
1510 skip_whitespace(s, ch);
1511 if(ch == '\0' || sscanf((s - 1), "%ld", &onehistory.size) != 1) {
1514 skip_integer(s, ch);
1516 skip_whitespace(s, ch);
1517 if(ch == '\0' || sscanf((s - 1), "%ld", &onehistory.csize) != 1) {
1520 skip_integer(s, ch);
1522 skip_whitespace(s, ch);
1523 if(ch == '\0' || sscanf((s - 1), "%ld", &date) != 1) {
1526 skip_integer(s, ch);
1527 onehistory.date = date; /* time_t not guarranteed to be long */
1529 info.history[nb_history++] = onehistory;
1531 if((line = impget_line()) == NULL) goto shortfile_err;
1535 /* got a full record, now write it out to the database */
1537 if(put_info(hostname, diskname, &info)) {
1538 fprintf(stderr, "%s: error writing record for %s:%s\n",
1539 get_pname(), hostname, diskname);
1549 fprintf(stderr, "%s: parse error reading import record.\n", get_pname());
1556 fprintf(stderr, "%s: short file reading import record.\n", get_pname());
1567 for(; (line = agets(stdin)) != NULL; free(line)) {
1571 skip_whitespace(s, ch);
1573 /* ignore comment lines */
1576 /* found non-blank, return line */
1579 /* otherwise, a blank line, so keep going */
1582 fprintf(stderr, "%s: reading stdin: %s\n",
1583 get_pname(), strerror(errno));
1588 /* ----------------------------------------------- */
1590 void disklist_one(dp)
1602 printf("line %d:\n", dp->line);
1604 printf(" host %s:\n", hp->hostname);
1605 printf(" interface %s\n",
1606 ip->name[0] ? ip->name : "default");
1608 printf(" disk %s:\n", dp->name);
1609 if(dp->device) printf(" device %s\n", dp->device);
1611 printf(" program \"%s\"\n", dp->program);
1612 if(dp->exclude_file != NULL && dp->exclude_file->nb_element > 0) {
1613 printf(" exclude file");
1614 for(excl = dp->exclude_file->first; excl != NULL; excl = excl->next) {
1615 printf(" \"%s\"", excl->name);
1619 if(dp->exclude_list != NULL && dp->exclude_list->nb_element > 0) {
1620 printf(" exclude list");
1621 if(dp->exclude_optional) printf(" optional");
1622 for(excl = dp->exclude_list->first; excl != NULL; excl = excl->next) {
1623 printf(" \"%s\"", excl->name);
1627 if(dp->include_file != NULL && dp->include_file->nb_element > 0) {
1628 printf(" include file");
1629 for(excl = dp->include_file->first; excl != NULL; excl = excl->next) {
1630 printf(" \"%s\"", excl->name);
1634 if(dp->include_list != NULL && dp->include_list->nb_element > 0) {
1635 printf(" include list");
1636 if(dp->include_optional) printf(" optional");
1637 for(excl = dp->include_list->first; excl != NULL; excl = excl->next) {
1638 printf(" \"%s\"", excl->name);
1642 printf(" priority %ld\n", dp->priority);
1643 printf(" dumpcycle %ld\n", dp->dumpcycle);
1644 printf(" maxdumps %d\n", dp->maxdumps);
1645 printf(" maxpromoteday %d\n", dp->maxpromoteday);
1646 if(dp->bumppercent > 0) {
1647 printf(" bumppercent %d\n", dp->bumppercent);
1650 printf(" bumpsize %d\n", dp->bumpsize);
1652 printf(" bumpdays %d\n", dp->bumpdays);
1653 printf(" bumpmult %f\n", dp->bumpmult);
1655 printf(" strategy ");
1656 switch(dp->strategy) {
1661 printf("STANDARD\n");
1673 printf("INCRONLY\n");
1677 printf(" estimate ");
1678 switch(dp->estimate) {
1686 printf("CALCSIZE\n");
1690 printf(" compress ");
1691 switch(dp->compress) {
1696 printf("CLIENT FAST\n");
1699 printf("CLIENT BEST\n");
1701 case COMP_SERV_FAST:
1702 printf("SERVER FAST\n");
1704 case COMP_SERV_BEST:
1705 printf("SERVER BEST\n");
1708 if(dp->compress != COMP_NONE) {
1709 printf(" comprate %.2f %.2f\n",
1710 dp->comprate[0], dp->comprate[1]);
1713 printf(" encrypt ");
1714 switch(dp->encrypt) {
1721 case ENCRYPT_SERV_CUST:
1726 printf(" auth %s\n", dp->security_driver);
1727 printf(" kencrypt %s\n", (dp->kencrypt? "YES" : "NO"));
1728 printf(" holdingdisk %s\n", (!dp->no_hold? "YES" : "NO"));
1729 printf(" record %s\n", (dp->record? "YES" : "NO"));
1730 printf(" index %s\n", (dp->index? "YES" : "NO"));
1733 stm = localtime(&st);
1734 printf(" starttime %d:%02d:%02d\n",
1735 stm->tm_hour, stm->tm_min, stm->tm_sec);
1738 if(dp->tape_splitsize > 0) {
1739 printf(" tape_splitsize %ld\n", dp->tape_splitsize);
1741 if(dp->split_diskbuffer) {
1742 printf(" split_diskbuffer %s\n", dp->split_diskbuffer);
1744 if(dp->fallback_splitsize > 0) {
1745 printf(" fallback_splitsize %ldMb\n", (dp->fallback_splitsize / 1024));
1747 printf(" skip-incr %s\n", (dp->skip_incr? "YES" : "NO"));
1748 printf(" skip-full %s\n", (dp->skip_full? "YES" : "NO"));
1753 void disklist(argc, argv)
1760 diskloop(argc, argv, "disklist", disklist_one);
1762 for(dp = diskq.head; dp != NULL; dp = dp->next)
1766 void show_version(argc, argv)
1772 for(i = 0; version_info[i] != NULL; i++)
1773 printf("%s", version_info[i]);