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 2003/11/18 16:44:34 martinea 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));
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));
78 static char *conf_tapelist = NULL;
85 unsigned long malloc_hist_1, malloc_size_1;
86 unsigned long malloc_hist_2, malloc_size_2;
91 for(fd = 3; fd < FD_SETSIZE; fd++) {
93 * Make sure nobody spoofs us with a lot of extra open files
94 * that would cause an open we do to get a very high file
95 * descriptor, which in turn might be used as an index into
96 * an array (e.g. an fd_set).
103 set_pname("amadmin");
105 malloc_size_1 = malloc_inuse(&malloc_hist_1);
107 erroutput_type = ERR_INTERACTIVE;
109 if(argc < 3) usage();
111 if(strcmp(argv[2],"version") == 0) {
112 for(argc=0; version_info[argc]; printf("%s",version_info[argc++]));
115 config_name = argv[1];
116 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
117 conffile = stralloc2(config_dir, CONFFILE_NAME);
118 if(read_conffile(conffile)) {
119 error("errors processing config file \"%s\"", conffile);
122 conf_diskfile = getconf_str(CNF_DISKFILE);
123 if (*conf_diskfile == '/') {
124 conf_diskfile = stralloc(conf_diskfile);
126 conf_diskfile = stralloc2(config_dir, conf_diskfile);
128 if((diskqp = read_diskfile(conf_diskfile)) == NULL) {
129 error("could not load disklist \"%s\"", conf_diskfile);
131 amfree(conf_diskfile);
132 conf_tapelist = getconf_str(CNF_TAPELIST);
133 if (*conf_tapelist == '/') {
134 conf_tapelist = stralloc(conf_tapelist);
136 conf_tapelist = stralloc2(config_dir, conf_tapelist);
138 if(read_tapelist(conf_tapelist)) {
139 error("could not load tapelist \"%s\"", conf_tapelist);
141 conf_infofile = getconf_str(CNF_INFOFILE);
142 if (*conf_infofile == '/') {
143 conf_infofile = stralloc(conf_infofile);
145 conf_infofile = stralloc2(config_dir, conf_infofile);
147 if(open_infofile(conf_infofile)) {
148 error("could not open info db \"%s\"", conf_infofile);
150 amfree(conf_infofile);
152 if(strcmp(argv[2],"force-bump") == 0) force_bump(argc, argv);
153 else if(strcmp(argv[2],"force-no-bump") == 0) force_no_bump(argc, argv);
154 else if(strcmp(argv[2],"unforce-bump") == 0) unforce_bump(argc, argv);
155 else if(strcmp(argv[2],"force") == 0) force(argc, argv);
156 else if(strcmp(argv[2],"unforce") == 0) unforce(argc, argv);
157 else if(strcmp(argv[2],"reuse") == 0) reuse(argc, argv);
158 else if(strcmp(argv[2],"no-reuse") == 0) noreuse(argc, argv);
159 else if(strcmp(argv[2],"info") == 0) info(argc, argv);
160 else if(strcmp(argv[2],"due") == 0) due(argc, argv);
161 else if(strcmp(argv[2],"find") == 0) find(argc, argv);
162 else if(strcmp(argv[2],"delete") == 0) delete(argc, argv);
163 else if(strcmp(argv[2],"balance") == 0) balance(argc, argv);
164 else if(strcmp(argv[2],"tape") == 0) tape();
165 else if(strcmp(argv[2],"bumpsize") == 0) bumpsize();
166 else if(strcmp(argv[2],"import") == 0) import_db(argc, argv);
167 else if(strcmp(argv[2],"export") == 0) export_db(argc, argv);
168 else if(strcmp(argv[2],"disklist") == 0) disklist(argc, argv);
170 fprintf(stderr, "%s: unknown command \"%s\"\n", argv[0], argv[2]);
175 amfree(conf_tapelist);
178 malloc_size_2 = malloc_inuse(&malloc_hist_2);
180 if(malloc_size_1 != malloc_size_2) {
181 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
190 fprintf(stderr, "\nUsage: %s%s <conf> <command> {<args>} ...\n",
191 get_pname(), versionsuffix());
192 fprintf(stderr, " Valid <command>s are:\n");
193 fprintf(stderr,"\tversion\t\t\t\t# Show version info.\n");
195 "\tforce [<hostname> [<disks>]* ]+\t# Force level 0 at next run.\n");
197 "\tunforce [<hostname> [<disks>]* ]+\t# Clear force command.\n");
199 "\tforce-bump [<hostname> [<disks>]* ]+\t# Force bump at next run.\n");
201 "\tforce-no-bump [<hostname> [<disks>]* ]+\t# Force no-bump at next run.\n");
203 "\tunforce-bump [<hostname> [<disks>]* ]+\t# Clear bump command.\n");
205 "\treuse <tapelabel> ...\t\t# re-use this tape.\n");
207 "\tno-reuse <tapelabel> ...\t# never re-use this tape.\n");
209 "\tfind [<hostname> [<disks>]* ]*\t# Show which tapes these dumps are on.\n");
211 "\tdelete [<hostname> [<disks>]* ]*\t# Delete from database.\n");
213 "\tinfo [<hostname> [<disks>]* ]*\t# Show current info records.\n");
215 "\tdue [<hostname> [<disks>]* ]*\t# Show due date.\n");
217 "\tbalance [-days <num>]\t\t# Show nightly dump size balance.\n");
219 "\ttape\t\t\t\t# Show which tape is due next.\n");
221 "\tbumpsize\t\t\t# Show current bump thresholds.\n");
223 "\texport [<hostname> [<disks>]* ]*\t# Export curinfo database to stdout.\n");
225 "\timport\t\t\t\t# Import curinfo database from stdin.\n");
227 "\tdisklist [<hostname> [<disks>]* ]*\t# Show disklist entries.\n");
233 /* ----------------------------------------------- */
235 #define SECS_PER_DAY (24*60*60)
238 char *seqdatestr(seq)
242 static char *dow[7] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
243 time_t t = today + seq*SECS_PER_DAY;
248 ap_snprintf(str, sizeof(str),
249 "%2d/%02d %3s", tm->tm_mon+1, tm->tm_mday, dow[tm->tm_wday]);
254 #define days_diff(a, b) (((b) - (a) + SECS_PER_DAY) / SECS_PER_DAY)
256 /* when is next level 0 due? 0 = tonight, 1 = tommorrow, etc*/
257 static int next_level0(dp, info)
261 if(dp->strategy == DS_NOFULL)
262 return 1; /* fake it */
263 if(info->inf[0].date < (time_t)0)
264 return 0; /* new disk */
266 return dp->dumpcycle - days_diff(info->inf[0].date, today);
269 static void check_dumpuser()
271 static int been_here = 0;
281 uid_dumpuser = uid_me;
282 dumpuser = getconf_str(CNF_DUMPUSER);
284 if ((pw = getpwnam(dumpuser)) == NULL) {
285 error("cannot look up dump user \"%s\"", dumpuser);
288 uid_dumpuser = pw->pw_uid;
289 if ((pw = getpwuid(uid_me)) == NULL) {
290 error("cannot look up my own uid %ld", (long)uid_me);
293 if (uid_me != uid_dumpuser) {
294 error("ERROR: running as user \"%s\" instead of \"%s\"",
295 pw->pw_name, dumpuser);
301 /* ----------------------------------------------- */
303 void diskloop(argc, argv, cmdname, func)
307 void (*func) P((disk_t *dp));
313 fprintf(stderr,"%s: expecting \"%s [<hostname> [<disks>]* ]+\"\n",
314 get_pname(), cmdname);
318 match_disklist(diskqp, argc-3, argv+3);
320 for(dp = diskqp->head; dp != NULL; dp = dp->next) {
327 fprintf(stderr,"%s: no disk matched\n",get_pname());
331 /* ----------------------------------------------- */
337 char *hostname = dp->host->hostname;
338 char *diskname = dp->name;
344 get_info(hostname, diskname, &info);
345 info.command |= FORCE_FULL;
346 if(info.command & FORCE_BUMP) {
347 info.command ^= FORCE_BUMP;
348 printf("%s: WARNING: %s:%s FORCE_BUMP command was cleared.\n",
349 get_pname(), hostname, diskname);
351 if(put_info(hostname, diskname, &info) == 0) {
352 printf("%s: %s:%s is set to a forced level 0 at next run.\n",
353 get_pname(), hostname, diskname);
355 fprintf(stderr, "%s: %s:%s could not be forced.\n",
356 get_pname(), hostname, diskname);
361 void force(argc, argv)
365 diskloop(argc, argv, "force", force_one);
369 /* ----------------------------------------------- */
375 char *hostname = dp->host->hostname;
376 char *diskname = dp->name;
379 get_info(hostname, diskname, &info);
380 if(info.command & FORCE_FULL) {
384 info.command ^= FORCE_FULL;
385 if(put_info(hostname, diskname, &info) == 0){
386 printf("%s: force command for %s:%s cleared.\n",
387 get_pname(), hostname, diskname);
390 "%s: force command for %s:%s could not be cleared.\n",
391 get_pname(), hostname, diskname);
395 printf("%s: no force command outstanding for %s:%s, unchanged.\n",
396 get_pname(), hostname, diskname);
400 void unforce(argc, argv)
404 diskloop(argc, argv, "unforce", unforce_one);
408 /* ----------------------------------------------- */
411 void force_bump_one(dp)
414 char *hostname = dp->host->hostname;
415 char *diskname = dp->name;
421 get_info(hostname, diskname, &info);
422 info.command |= FORCE_BUMP;
423 if(info.command & FORCE_NO_BUMP) {
424 info.command ^= FORCE_NO_BUMP;
425 printf("%s: WARNING: %s:%s FORCE_NO_BUMP command was cleared.\n",
426 get_pname(), hostname, diskname);
428 if (info.command & FORCE_FULL) {
429 info.command ^= FORCE_FULL;
430 printf("%s: WARNING: %s:%s FORCE_FULL command was cleared.\n",
431 get_pname(), hostname, diskname);
433 if(put_info(hostname, diskname, &info) == 0) {
434 printf("%s: %s:%s is set to bump at next run.\n",
435 get_pname(), hostname, diskname);
437 fprintf(stderr, "%s: %s:%s could not be forced to bump.\n",
438 get_pname(), hostname, diskname);
443 void force_bump(argc, argv)
447 diskloop(argc, argv, "force-bump", force_bump_one);
451 /* ----------------------------------------------- */
454 void force_no_bump_one(dp)
457 char *hostname = dp->host->hostname;
458 char *diskname = dp->name;
464 get_info(hostname, diskname, &info);
465 info.command |= FORCE_NO_BUMP;
466 if(info.command & FORCE_BUMP) {
467 info.command ^= FORCE_BUMP;
468 printf("%s: WARNING: %s:%s FORCE_BUMP command was cleared.\n",
469 get_pname(), hostname, diskname);
471 if(put_info(hostname, diskname, &info) == 0) {
472 printf("%s: %s:%s is set to not bump at next run.\n",
473 get_pname(), hostname, diskname);
475 fprintf(stderr, "%s: %s:%s could not be force to not bump.\n",
476 get_pname(), hostname, diskname);
481 void force_no_bump(argc, argv)
485 diskloop(argc, argv, "force-no-bump", force_no_bump_one);
489 /* ----------------------------------------------- */
492 void unforce_bump_one(dp)
495 char *hostname = dp->host->hostname;
496 char *diskname = dp->name;
499 get_info(hostname, diskname, &info);
500 if(info.command & (FORCE_BUMP | FORCE_NO_BUMP)) {
504 if(info.command & FORCE_BUMP)
505 info.command ^= FORCE_BUMP;
506 if(info.command & FORCE_NO_BUMP)
507 info.command ^= FORCE_NO_BUMP;
508 if(put_info(hostname, diskname, &info) == 0) {
509 printf("%s: bump command for %s:%s cleared.\n",
510 get_pname(), hostname, diskname);
512 fprintf(stderr, "%s: %s:%s bump command could not be cleared.\n",
513 get_pname(), hostname, diskname);
517 printf("%s: no bump command outstanding for %s:%s, unchanged.\n",
518 get_pname(), hostname, diskname);
523 void unforce_bump(argc, argv)
527 diskloop(argc, argv, "unforce-bump", unforce_bump_one);
531 /* ----------------------------------------------- */
533 void reuse(argc, argv)
541 fprintf(stderr,"%s: expecting \"reuse <tapelabel> ...\"\n",
547 for(count=3; count< argc; count++) {
548 tp = lookup_tapelabel(argv[count]);
550 fprintf(stderr, "reuse: tape label %s not found in tapelist.\n",
554 if( tp->reuse == 0 ) {
556 printf("%s: marking tape %s as reusable.\n",
557 get_pname(), argv[count]);
559 fprintf(stderr, "%s: tape %s already reusable.\n",
560 get_pname(), argv[count]);
564 if(write_tapelist(conf_tapelist)) {
565 error("could not write tapelist \"%s\"", conf_tapelist);
569 void noreuse(argc, argv)
577 fprintf(stderr,"%s: expecting \"no-reuse <tapelabel> ...\"\n",
583 for(count=3; count< argc; count++) {
584 tp = lookup_tapelabel(argv[count]);
586 fprintf(stderr, "no-reuse: tape label %s not found in tapelist.\n",
590 if( tp->reuse == 1 ) {
592 printf("%s: marking tape %s as not reusable.\n",
593 get_pname(), argv[count]);
595 fprintf(stderr, "%s: tape %s already not reusable.\n",
596 get_pname(), argv[count]);
600 if(write_tapelist(conf_tapelist)) {
601 error("could not write tapelist \"%s\"", conf_tapelist);
606 /* ----------------------------------------------- */
613 char *hostname = dp->host->hostname;
614 char *diskname = dp->name;
617 if(get_info(hostname, diskname, &info)) {
618 printf("%s: %s:%s NOT currently in database.\n",
619 get_pname(), hostname, diskname);
624 if(del_info(hostname, diskname))
625 error("couldn't delete %s:%s from database: %s",
626 hostname, diskname, strerror(errno));
628 printf("%s: %s:%s deleted from curinfo database.\n",
629 get_pname(), hostname, diskname);
632 void delete(argc, argv)
637 diskloop(argc, argv, "delete", delete_one);
641 "%s: NOTE: you'll have to remove these from the disklist yourself.\n",
645 /* ----------------------------------------------- */
655 get_info(dp->host->hostname, dp->name, &info);
657 printf("\nCurrent info for %s %s:\n", dp->host->hostname, dp->name);
658 if(info.command & FORCE_FULL)
659 printf(" (Forcing to level 0 dump at next run)\n");
660 if(info.command & FORCE_BUMP)
661 printf(" (Forcing bump at next run)\n");
662 if(info.command & FORCE_NO_BUMP)
663 printf(" (Forcing no-bump at next run)\n");
664 printf(" Stats: dump rates (kps), Full: %5.1f, %5.1f, %5.1f\n",
665 info.full.rate[0], info.full.rate[1], info.full.rate[2]);
666 printf(" Incremental: %5.1f, %5.1f, %5.1f\n",
667 info.incr.rate[0], info.incr.rate[1], info.incr.rate[2]);
668 printf(" compressed size, Full: %5.1f%%,%5.1f%%,%5.1f%%\n",
669 info.full.comp[0]*100, info.full.comp[1]*100, info.full.comp[2]*100);
670 printf(" Incremental: %5.1f%%,%5.1f%%,%5.1f%%\n",
671 info.incr.comp[0]*100, info.incr.comp[1]*100, info.incr.comp[2]*100);
673 printf(" Dumps: lev datestmp tape file origK compK secs\n");
674 for(lev = 0, sp = &info.inf[0]; lev < 9; lev++, sp++) {
675 if(sp->date < (time_t)0 && sp->label[0] == '\0') continue;
676 tm = localtime(&sp->date);
677 printf(" %d %04d%02d%02d %-15s %4d %7ld %7ld %4ld\n",
678 lev, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
679 sp->label, sp->filenum, sp->size, sp->csize, sp->secs);
684 void info(argc, argv)
691 diskloop(argc, argv, "info", info_one);
693 for(dp = diskqp->head; dp != NULL; dp = dp->next)
697 /* ----------------------------------------------- */
707 if(get_info(hp->hostname, dp->name, &info)) {
708 printf("new disk %s:%s ignored.\n", hp->hostname, dp->name);
711 days = next_level0(dp, &info);
713 printf("Overdue %2d day%s %s:%s\n",
714 -days, (-days == 1) ? ": " : "s:",
715 hp->hostname, dp->name);
718 printf("Due today: %s:%s\n", hp->hostname, dp->name);
721 printf("Due in %2d day%s %s:%s\n", days,
722 (days == 1) ? ": " : "s:",
723 hp->hostname, dp->name);
736 diskloop(argc, argv, "due", due_one);
738 for(dp = diskqp->head; dp != NULL; dp = dp->next)
742 /* ----------------------------------------------- */
749 runtapes = getconf_int(CNF_RUNTAPES);
750 tp = lookup_last_reusable_tape(0);
752 for ( i=0 ; i < runtapes ; i++ ) {
753 printf("The next Amanda run should go onto ");
756 printf("tape %s or ", tp->label);
757 printf("a new tape.\n");
759 tp = lookup_last_reusable_tape(i + 1);
761 lasttp = lookup_tapepos(lookup_nb_tape());
763 if(lasttp && i > 0 && lasttp->datestamp == 0) {
765 while(lasttp && i > 0 && lasttp->datestamp == 0) {
767 lasttp = lasttp->prev;
770 lasttp = lookup_tapepos(lookup_nb_tape());
773 printf("The next new tape already labelled is: %s.\n",
777 printf("The next %d new tapes already labelled are: %s", c,
779 lasttp = lasttp->prev;
781 while(lasttp && c > 0 && lasttp->datestamp == 0) {
782 printf(", %s", lasttp->label);
783 lasttp = lasttp->prev;
791 /* ----------------------------------------------- */
793 void balance(argc, argv)
798 struct balance_stats {
800 long origsize, outsize;
802 int conf_runspercycle, conf_dumpcycle;
803 int seq, runs_per_cycle, overdue, max_overdue;
804 int later, total, balance, distinct;
805 float fseq, disk_dumpcycle;
807 long int total_balanced, balanced;
810 conf_dumpcycle = getconf_int(CNF_DUMPCYCLE);
811 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
812 later = conf_dumpcycle;
816 if(argc > 4 && strcmp(argv[3],"--days") == 0) {
817 later = atoi(argv[4]);
818 if(later < 0) later = conf_dumpcycle;
821 if(conf_runspercycle == 0) {
822 runs_per_cycle = conf_dumpcycle;
823 } else if(conf_runspercycle == -1 ) {
824 runs_per_cycle = guess_runs_from_tapelist();
826 runs_per_cycle = conf_runspercycle;
828 if (runs_per_cycle <= 0) {
834 distinct = later + 3;
836 sp = (struct balance_stats *)
837 alloc(sizeof(struct balance_stats) * (distinct+1));
839 for(seq=0; seq <= distinct; seq++)
840 sp[seq].disks = sp[seq].origsize = sp[seq].outsize = 0;
842 for(dp = diskqp->head; dp != NULL; dp = dp->next) {
843 if(get_info(dp->host->hostname, dp->name, &info)) {
844 printf("new disk %s:%s ignored.\n", dp->host->hostname, dp->name);
847 if (dp->strategy == DS_NOFULL) {
850 sp[distinct].disks++;
851 sp[distinct].origsize += info.inf[0].size;
852 sp[distinct].outsize += info.inf[0].csize;
855 if(dp->dumpcycle == 0) {
856 sp[balance].origsize += info.inf[0].size * runs_per_cycle;
857 sp[balance].outsize += info.inf[0].csize * runs_per_cycle;
860 sp[balance].origsize += info.inf[0].size *
861 (conf_dumpcycle / dp->dumpcycle);
862 sp[balance].outsize += info.inf[0].csize *
863 (conf_dumpcycle / dp->dumpcycle);
866 disk_dumpcycle = dp->dumpcycle;
867 if(dp->dumpcycle <= 0)
868 disk_dumpcycle = ((float)conf_dumpcycle) / ((float)runs_per_cycle);
870 seq = next_level0(dp, &info);
875 if (-seq > max_overdue)
885 sp[seq].origsize += info.inf[0].size;
886 sp[seq].outsize += info.inf[0].csize;
890 sp[total].origsize += info.inf[0].size;
891 sp[total].outsize += info.inf[0].csize;
894 /* See, if there's another run in this dumpcycle */
895 fseq += disk_dumpcycle;
897 } while (seq < later);
900 if(sp[total].outsize == 0) {
901 printf("\nNo data to report on yet.\n");
906 balanced = sp[balance].outsize / runs_per_cycle;
907 if(conf_dumpcycle == later) {
908 total_balanced = sp[total].outsize / runs_per_cycle;
911 total_balanced = 1024*(((sp[total].outsize/1024) * conf_dumpcycle)
912 / (runs_per_cycle * later));
915 printf("\n due-date #fs orig KB out KB balance\n");
916 printf("----------------------------------------------\n");
917 for(seq = 0; seq < later; seq++) {
918 printf("%-9.9s %3d %10ld %10ld ",
919 seqdatestr(seq), sp[seq].disks,
920 sp[seq].origsize, sp[seq].outsize);
921 if(!sp[seq].outsize) printf(" --- \n");
922 else printf("%+8.1f%%\n",
923 (sp[seq].outsize-balanced)*100.0/(double)balanced);
925 if(sp[later].disks != 0) {
926 printf("later %3d %10ld %10ld ",
928 sp[later].origsize, sp[later].outsize);
929 if(!sp[later].outsize) printf(" --- \n");
930 else printf("%+8.1f%%\n",
931 (sp[later].outsize-balanced)*100.0/(double)balanced);
933 printf("----------------------------------------------\n");
934 printf("TOTAL %3d %10ld %10ld %9ld\n", sp[total].disks,
935 sp[total].origsize, sp[total].outsize, total_balanced);
936 if (sp[balance].origsize != sp[total].origsize ||
937 sp[balance].outsize != sp[total].outsize ||
938 balanced != total_balanced) {
939 printf("BALANCED %10ld %10ld %9ld\n",
940 sp[balance].origsize, sp[balance].outsize, balanced);
942 if (sp[distinct].disks != sp[total].disks) {
943 printf("DISTINCT %3d %10ld %10ld\n", sp[distinct].disks,
944 sp[distinct].origsize, sp[distinct].outsize);
946 printf(" (estimated %d run%s per dumpcycle)\n",
947 runs_per_cycle, (runs_per_cycle == 1) ? "" : "s");
949 printf(" (%d filesystem%s overdue, the most being overdue %d day%s)\n",
950 overdue, (overdue == 1) ? "" : "s",
951 max_overdue, (max_overdue == 1) ? "" : "s");
957 /* ----------------------------------------------- */
959 void find(argc, argv)
964 char *sort_order = NULL;
965 find_result_t *output_find;
969 "%s: expecting \"find [--sort <hkdlb>] [hostname [<disk>]]*\"\n",
975 sort_order = newstralloc(sort_order, DEFAULT_SORT_ORDER);
976 if(argc > 4 && strcmp(argv[3],"--sort") == 0) {
979 for(i=strlen(argv[4])-1;i>=0;i--) {
980 switch (argv[4][i]) {
992 default: valid_sort=0;
996 sort_order = newstralloc(sort_order, argv[4]);
998 printf("Invalid sort order: %s\n", argv[4]);
999 printf("Use default sort order: %s\n", sort_order);
1005 match_disklist(diskqp, argc-(start_argc-1), argv+(start_argc-1));
1006 output_find = find_dump(1, diskqp);
1008 if(argc-(start_argc-1) > 0) {
1009 free_find_result(&output_find);
1010 match_disklist(diskqp, argc-(start_argc-1), argv+(start_argc-1));
1011 output_find = find_dump(0, NULL);
1014 sort_find_result(sort_order, &output_find);
1015 print_find_result(output_find);
1016 free_find_result(&output_find);
1022 /* ------------------------ */
1025 /* shared code with planner.c */
1027 int bump_thresh(level)
1030 int bump = getconf_int(CNF_BUMPSIZE);
1031 double mult = getconf_real(CNF_BUMPMULT);
1033 while(--level) bump = (int) bump * mult;
1041 printf("Current bump parameters:\n");
1042 printf(" bumpsize %5d KB\t- minimum savings (threshold) to bump level 1 -> 2\n",
1043 getconf_int(CNF_BUMPSIZE));
1044 printf(" bumpdays %5d\t- minimum days at each level\n",
1045 getconf_int(CNF_BUMPDAYS));
1046 printf(" bumpmult %5.5g\t- threshold = bumpsize * bumpmult**(level-1)\n\n",
1047 getconf_real(CNF_BUMPMULT));
1049 printf(" Bump -> To Threshold\n");
1050 for(l = 1; l < 9; l++) {
1051 printf("\t%d -> %d %9d KB\n", l, l+1, bump_thresh(l));
1056 /* ----------------------------------------------- */
1058 void export_one P((disk_t *dp));
1060 void export_db(argc, argv)
1066 char hostname[MAX_HOSTNAME_LENGTH+1];
1069 printf("CURINFO Version %s CONF %s\n", version(), getconf_str(CNF_ORG));
1072 if(gethostname(hostname, sizeof(hostname)-1) == -1) {
1073 error("could not determine host name: %s", strerror(errno));
1075 hostname[sizeof(hostname)-1] = '\0';
1076 printf("# Generated by:\n# host: %s\n# date: %s",
1077 hostname, ctime(&curtime));
1079 printf("# command:");
1080 for(i = 0; i < argc; i++) {
1081 printf(" %s", argv[i]);
1084 printf("\n# This file can be merged back in with \"amadmin import\".\n");
1085 printf("# Edit only with care.\n");
1088 diskloop(argc, argv, "export", export_one);
1089 } else for(dp = diskqp->head; dp != NULL; dp = dp->next) {
1100 if(get_info(dp->host->hostname, dp->name, &info)) {
1101 fprintf(stderr, "Warning: no curinfo record for %s:%s\n",
1102 dp->host->hostname, dp->name);
1105 printf("host: %s\ndisk: %s\n", dp->host->hostname, dp->name);
1106 printf("command: %d\n", info.command);
1107 printf("last_level: %d\n",info.last_level);
1108 printf("consecutive_runs: %d\n",info.consecutive_runs);
1109 printf("full-rate:");
1110 for(i=0;i<AVG_COUNT;i++) printf(" %f", info.full.rate[i]);
1111 printf("\nfull-comp:");
1112 for(i=0;i<AVG_COUNT;i++) printf(" %f", info.full.comp[i]);
1114 printf("\nincr-rate:");
1115 for(i=0;i<AVG_COUNT;i++) printf(" %f", info.incr.rate[i]);
1116 printf("\nincr-comp:");
1117 for(i=0;i<AVG_COUNT;i++) printf(" %f", info.incr.comp[i]);
1119 for(l=0;l<DUMP_LEVELS;l++) {
1120 if(info.inf[l].date < (time_t)0 && info.inf[l].label[0] == '\0') continue;
1121 printf("stats: %d %ld %ld %ld %ld %d %s\n", l,
1122 info.inf[l].size, info.inf[l].csize, info.inf[l].secs,
1123 (long)info.inf[l].date, info.inf[l].filenum,
1129 /* ----------------------------------------------- */
1131 int import_one P((void));
1132 char *impget_line P((void));
1134 void import_db(argc, argv)
1138 int vers_maj, vers_min, vers_patch, newer;
1145 /* process header line */
1147 if((line = agets(stdin)) == NULL) {
1148 fprintf(stderr, "%s: empty input.\n", get_pname());
1156 #define sc "CURINFO Version"
1157 if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1163 skip_whitespace(s, ch);
1165 || sscanf(s - 1, "%d.%d.%d", &vers_maj, &vers_min, &vers_patch) != 3) {
1169 skip_integer(s, ch); /* skip over major */
1174 skip_integer(s, ch); /* skip over minor */
1179 skip_integer(s, ch); /* skip over patch */
1185 skip_non_whitespace(s, ch);
1189 skip_whitespace(s, ch); /* find the org keyword */
1191 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1199 skip_whitespace(s, ch); /* find the org string */
1205 newer = (vers_maj != VERSION_MAJOR)? vers_maj > VERSION_MAJOR :
1206 (vers_min != VERSION_MINOR)? vers_min > VERSION_MINOR :
1207 vers_patch > VERSION_PATCH;
1210 "%s: WARNING: input is from newer Amanda version: %d.%d.%d.\n",
1211 get_pname(), vers_maj, vers_min, vers_patch);
1213 if(strcmp(org, getconf_str(CNF_ORG)) != 0) {
1214 fprintf(stderr, "%s: WARNING: input is from different org: %s\n",
1218 while(import_one());
1226 fprintf(stderr, "%s: bad CURINFO header line in input: %s.\n",
1228 fprintf(stderr, " Was the input in \"amadmin export\" format?\n");
1233 int import_one P((void))
1242 char *hostname = NULL;
1243 char *diskname = NULL;
1249 memset(&info, 0, sizeof(info_t));
1251 for(level = 0; level < DUMP_LEVELS; level++) {
1252 info.inf[level].date = (time_t)-1;
1255 /* get host: disk: command: lines */
1257 hostname = diskname = NULL;
1259 if((line = impget_line()) == NULL) return 0; /* nothing there */
1263 skip_whitespace(s, ch);
1265 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) goto parse_err;
1269 skip_whitespace(s, ch);
1270 if(ch == '\0') goto parse_err;
1272 skip_non_whitespace(s, ch);
1274 hostname = stralloc(fp);
1277 skip_whitespace(s, ch);
1280 if((line = impget_line()) == NULL) goto shortfile_err;
1283 skip_whitespace(s, ch);
1286 if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) goto parse_err;
1290 skip_whitespace(s, ch);
1291 if(ch == '\0') goto parse_err;
1293 skip_non_whitespace(s, ch);
1295 diskname = stralloc(fp);
1299 if((line = impget_line()) == NULL) goto shortfile_err;
1300 if(sscanf(line, "command: %d", &info.command) != 1) goto parse_err;
1302 /* get last_level and consecutive_runs */
1305 if((line = impget_line()) == NULL) goto shortfile_err;
1306 rc = sscanf(line, "last_level: %d", &info.last_level);
1309 if((line = impget_line()) == NULL) goto shortfile_err;
1310 if(sscanf(line, "consecutive_runs: %d", &info.consecutive_runs) != 1) goto parse_err;
1312 if((line = impget_line()) == NULL) goto shortfile_err;
1315 /* get rate: and comp: lines for full dumps */
1317 rc = sscanf(line, "full-rate: %f %f %f",
1318 &info.full.rate[0], &info.full.rate[1], &info.full.rate[2]);
1319 if(rc != 3) goto parse_err;
1322 if((line = impget_line()) == NULL) goto shortfile_err;
1323 rc = sscanf(line, "full-comp: %f %f %f",
1324 &info.full.comp[0], &info.full.comp[1], &info.full.comp[2]);
1325 if(rc != 3) goto parse_err;
1327 /* get rate: and comp: lines for incr dumps */
1330 if((line = impget_line()) == NULL) goto shortfile_err;
1331 rc = sscanf(line, "incr-rate: %f %f %f",
1332 &info.incr.rate[0], &info.incr.rate[1], &info.incr.rate[2]);
1333 if(rc != 3) goto parse_err;
1336 if((line = impget_line()) == NULL) goto shortfile_err;
1337 rc = sscanf(line, "incr-comp: %f %f %f",
1338 &info.incr.comp[0], &info.incr.comp[1], &info.incr.comp[2]);
1339 if(rc != 3) goto parse_err;
1341 /* get stats for dump levels */
1345 if((line = impget_line()) == NULL) goto shortfile_err;
1346 if(strncmp(line, "//", 2) == 0) {
1350 memset(&onestat, 0, sizeof(onestat));
1355 skip_whitespace(s, ch);
1357 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1364 skip_whitespace(s, ch);
1365 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1368 skip_integer(s, ch);
1370 skip_whitespace(s, ch);
1371 if(ch == '\0' || sscanf(s - 1, "%ld", &onestat.size) != 1) {
1374 skip_integer(s, ch);
1376 skip_whitespace(s, ch);
1377 if(ch == '\0' || sscanf(s - 1, "%ld", &onestat.csize) != 1) {
1380 skip_integer(s, ch);
1382 skip_whitespace(s, ch);
1383 if(ch == '\0' || sscanf(s - 1, "%ld", &onestat.secs) != 1) {
1386 skip_integer(s, ch);
1388 skip_whitespace(s, ch);
1389 if(ch == '\0' || sscanf(s - 1, "%ld", &onedate) != 1) {
1392 skip_integer(s, ch);
1394 skip_whitespace(s, ch);
1396 if(sscanf(s - 1, "%d", &onestat.filenum) != 1) {
1399 skip_integer(s, ch);
1401 skip_whitespace(s, ch);
1403 if (onestat.filenum != 0)
1405 onestat.label[0] = '\0';
1407 strncpy(onestat.label, s - 1, sizeof(onestat.label)-1);
1408 onestat.label[sizeof(onestat.label)-1] = '\0';
1412 /* time_t not guarranteed to be long */
1413 onestat.date = onedate;
1414 if(level < 0 || level > 9) goto parse_err;
1416 info.inf[level] = onestat;
1420 /* got a full record, now write it out to the database */
1422 if(put_info(hostname, diskname, &info)) {
1423 fprintf(stderr, "%s: error writing record for %s:%s\n",
1424 get_pname(), hostname, diskname);
1434 fprintf(stderr, "%s: parse error reading import record.\n", get_pname());
1441 fprintf(stderr, "%s: short file reading import record.\n", get_pname());
1452 for(; (line = agets(stdin)) != NULL; free(line)) {
1456 skip_whitespace(s, ch);
1458 /* ignore comment lines */
1461 /* found non-blank, return line */
1464 /* otherwise, a blank line, so keep going */
1467 fprintf(stderr, "%s: reading stdin: %s\n",
1468 get_pname(), strerror(errno));
1473 /* ----------------------------------------------- */
1475 void disklist_one(dp)
1485 printf("line %d:\n", dp->line);
1487 printf(" host %s:\n", hp->hostname);
1488 printf(" interface %s\n",
1489 ip->name[0] ? ip->name : "default");
1491 printf(" disk %s:\n", dp->name);
1492 if(dp->device) printf(" device %s\n", dp->device);
1494 printf(" program \"%s\"\n", dp->program);
1495 if(dp->exclude_file != NULL && dp->exclude_file->nb_element > 0) {
1496 printf(" exclude file");
1497 for(excl = dp->exclude_file->first; excl != NULL; excl = excl->next) {
1498 printf(" \"%s\"", excl->name);
1502 if(dp->exclude_list != NULL && dp->exclude_list->nb_element > 0) {
1503 printf(" exclude list");
1504 if(dp->exclude_optional) printf(" optional");
1505 for(excl = dp->exclude_list->first; excl != NULL; excl = excl->next) {
1506 printf(" \"%s\"", excl->name);
1510 if(dp->include_file != NULL && dp->include_file->nb_element > 0) {
1511 printf(" include file");
1512 for(excl = dp->include_file->first; excl != NULL; excl = excl->next) {
1513 printf(" \"%s\"", excl->name);
1517 if(dp->include_list != NULL && dp->include_list->nb_element > 0) {
1518 printf(" include list");
1519 if(dp->include_optional) printf(" optional");
1520 for(excl = dp->include_list->first; excl != NULL; excl = excl->next) {
1521 printf(" \"%s\"", excl->name);
1525 printf(" priority %ld\n", dp->priority);
1526 printf(" dumpcycle %ld\n", dp->dumpcycle);
1527 printf(" maxdumps %d\n", dp->maxdumps);
1528 printf(" maxpromoteday %d\n", dp->maxpromoteday);
1530 printf(" strategy ");
1531 switch(dp->strategy) {
1536 printf("STANDARD\n");
1548 printf("INCRONLY\n");
1552 printf(" compress ");
1553 switch(dp->compress) {
1558 printf("CLIENT FAST\n");
1561 printf("CLIENT BEST\n");
1563 case COMP_SERV_FAST:
1564 printf("SERVER FAST\n");
1566 case COMP_SERV_BEST:
1567 printf("SERVER BEST\n");
1570 if(dp->compress != COMP_NONE) {
1571 printf(" comprate %.2f %.2f\n",
1572 dp->comprate[0], dp->comprate[1]);
1584 printf(" kencrypt %s\n", (dp->kencrypt? "YES" : "NO"));
1586 printf(" holdingdisk %s\n", (!dp->no_hold? "YES" : "NO"));
1587 printf(" record %s\n", (dp->record? "YES" : "NO"));
1588 printf(" index %s\n", (dp->index? "YES" : "NO"));
1589 printf(" skip-incr %s\n", (dp->skip_incr? "YES" : "NO"));
1590 printf(" skip-full %s\n", (dp->skip_full? "YES" : "NO"));
1595 void disklist(argc, argv)
1602 diskloop(argc, argv, "disklist", disklist_one);
1604 for(dp = diskqp->head; dp != NULL; dp = dp->next)