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.9 2005/03/29 16:35:11 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));
77 static void check_dumpuser P((void));
79 static char *conf_tapelist = NULL;
80 static char *displayunit;
81 static long int unitdivisor;
88 unsigned long malloc_hist_1, malloc_size_1;
89 unsigned long malloc_hist_2, malloc_size_2;
94 for(fd = 3; fd < FD_SETSIZE; fd++) {
96 * Make sure nobody spoofs us with a lot of extra open files
97 * that would cause an open we do to get a very high file
98 * descriptor, which in turn might be used as an index into
99 * an array (e.g. an fd_set).
106 set_pname("amadmin");
108 malloc_size_1 = malloc_inuse(&malloc_hist_1);
110 erroutput_type = ERR_INTERACTIVE;
112 if(argc < 3) usage();
114 if(strcmp(argv[2],"version") == 0) {
115 for(argc=0; version_info[argc]; printf("%s",version_info[argc++]));
118 config_name = argv[1];
119 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
120 conffile = stralloc2(config_dir, CONFFILE_NAME);
121 if(read_conffile(conffile)) {
122 error("errors processing config file \"%s\"", conffile);
128 conf_diskfile = getconf_str(CNF_DISKFILE);
129 if (*conf_diskfile == '/') {
130 conf_diskfile = stralloc(conf_diskfile);
132 conf_diskfile = stralloc2(config_dir, conf_diskfile);
134 if((diskqp = read_diskfile(conf_diskfile)) == NULL) {
135 error("could not load disklist \"%s\"", conf_diskfile);
137 amfree(conf_diskfile);
138 conf_tapelist = getconf_str(CNF_TAPELIST);
139 if (*conf_tapelist == '/') {
140 conf_tapelist = stralloc(conf_tapelist);
142 conf_tapelist = stralloc2(config_dir, conf_tapelist);
144 if(read_tapelist(conf_tapelist)) {
145 error("could not load tapelist \"%s\"", conf_tapelist);
147 conf_infofile = getconf_str(CNF_INFOFILE);
148 if (*conf_infofile == '/') {
149 conf_infofile = stralloc(conf_infofile);
151 conf_infofile = stralloc2(config_dir, conf_infofile);
153 if(open_infofile(conf_infofile)) {
154 error("could not open info db \"%s\"", conf_infofile);
156 amfree(conf_infofile);
158 displayunit = getconf_str(CNF_DISPLAYUNIT);
159 unitdivisor = getconf_unit_divisor();
161 if(strcmp(argv[2],"force-bump") == 0) force_bump(argc, argv);
162 else if(strcmp(argv[2],"force-no-bump") == 0) force_no_bump(argc, argv);
163 else if(strcmp(argv[2],"unforce-bump") == 0) unforce_bump(argc, argv);
164 else if(strcmp(argv[2],"force") == 0) force(argc, argv);
165 else if(strcmp(argv[2],"unforce") == 0) unforce(argc, argv);
166 else if(strcmp(argv[2],"reuse") == 0) reuse(argc, argv);
167 else if(strcmp(argv[2],"no-reuse") == 0) noreuse(argc, argv);
168 else if(strcmp(argv[2],"info") == 0) info(argc, argv);
169 else if(strcmp(argv[2],"due") == 0) due(argc, argv);
170 else if(strcmp(argv[2],"find") == 0) find(argc, argv);
171 else if(strcmp(argv[2],"delete") == 0) delete(argc, argv);
172 else if(strcmp(argv[2],"balance") == 0) balance(argc, argv);
173 else if(strcmp(argv[2],"tape") == 0) tape();
174 else if(strcmp(argv[2],"bumpsize") == 0) bumpsize();
175 else if(strcmp(argv[2],"import") == 0) import_db(argc, argv);
176 else if(strcmp(argv[2],"export") == 0) export_db(argc, argv);
177 else if(strcmp(argv[2],"disklist") == 0) disklist(argc, argv);
179 fprintf(stderr, "%s: unknown command \"%s\"\n", argv[0], argv[2]);
184 amfree(conf_tapelist);
187 malloc_size_2 = malloc_inuse(&malloc_hist_2);
189 if(malloc_size_1 != malloc_size_2) {
190 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
199 fprintf(stderr, "\nUsage: %s%s <conf> <command> {<args>} ...\n",
200 get_pname(), versionsuffix());
201 fprintf(stderr, " Valid <command>s are:\n");
202 fprintf(stderr,"\tversion\t\t\t\t# Show version info.\n");
204 "\tforce [<hostname> [<disks>]* ]+\t# Force level 0 at next run.\n");
206 "\tunforce [<hostname> [<disks>]* ]+\t# Clear force command.\n");
208 "\tforce-bump [<hostname> [<disks>]* ]+\t# Force bump at next run.\n");
210 "\tforce-no-bump [<hostname> [<disks>]* ]+\t# Force no-bump at next run.\n");
212 "\tunforce-bump [<hostname> [<disks>]* ]+\t# Clear bump command.\n");
214 "\treuse <tapelabel> ...\t\t# re-use this tape.\n");
216 "\tno-reuse <tapelabel> ...\t# never re-use this tape.\n");
218 "\tfind [<hostname> [<disks>]* ]*\t# Show which tapes these dumps are on.\n");
220 "\tdelete [<hostname> [<disks>]* ]*\t# Delete from database.\n");
222 "\tinfo [<hostname> [<disks>]* ]*\t# Show current info records.\n");
224 "\tdue [<hostname> [<disks>]* ]*\t# Show due date.\n");
226 "\tbalance [-days <num>]\t\t# Show nightly dump size balance.\n");
228 "\ttape\t\t\t\t# Show which tape is due next.\n");
230 "\tbumpsize\t\t\t# Show current bump thresholds.\n");
232 "\texport [<hostname> [<disks>]* ]*\t# Export curinfo database to stdout.\n");
234 "\timport\t\t\t\t# Import curinfo database from stdin.\n");
236 "\tdisklist [<hostname> [<disks>]* ]*\t# Show disklist entries.\n");
242 /* ----------------------------------------------- */
244 #define SECS_PER_DAY (24*60*60)
247 char *seqdatestr(seq)
251 static char *dow[7] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
252 time_t t = today + seq*SECS_PER_DAY;
257 ap_snprintf(str, sizeof(str),
258 "%2d/%02d %3s", tm->tm_mon+1, tm->tm_mday, dow[tm->tm_wday]);
263 #define days_diff(a, b) (((b) - (a) + SECS_PER_DAY) / SECS_PER_DAY)
265 /* when is next level 0 due? 0 = tonight, 1 = tommorrow, etc*/
266 static int next_level0(dp, info)
270 if(dp->strategy == DS_NOFULL)
271 return 1; /* fake it */
272 if(info->inf[0].date < (time_t)0)
273 return 0; /* new disk */
275 return dp->dumpcycle - days_diff(info->inf[0].date, today);
278 static void check_dumpuser()
280 static int been_here = 0;
290 uid_dumpuser = uid_me;
291 dumpuser = getconf_str(CNF_DUMPUSER);
293 if ((pw = getpwnam(dumpuser)) == NULL) {
294 error("cannot look up dump user \"%s\"", dumpuser);
297 uid_dumpuser = pw->pw_uid;
298 if ((pw = getpwuid(uid_me)) == NULL) {
299 error("cannot look up my own uid %ld", (long)uid_me);
302 if (uid_me != uid_dumpuser) {
303 error("ERROR: running as user \"%s\" instead of \"%s\"",
304 pw->pw_name, dumpuser);
310 /* ----------------------------------------------- */
312 void diskloop(argc, argv, cmdname, func)
316 void (*func) P((disk_t *dp));
322 fprintf(stderr,"%s: expecting \"%s [<hostname> [<disks>]* ]+\"\n",
323 get_pname(), cmdname);
327 match_disklist(diskqp, argc-3, argv+3);
329 for(dp = diskqp->head; dp != NULL; dp = dp->next) {
336 fprintf(stderr,"%s: no disk matched\n",get_pname());
340 /* ----------------------------------------------- */
346 char *hostname = dp->host->hostname;
347 char *diskname = dp->name;
353 get_info(hostname, diskname, &info);
354 info.command |= FORCE_FULL;
355 if(info.command & FORCE_BUMP) {
356 info.command ^= FORCE_BUMP;
357 printf("%s: WARNING: %s:%s FORCE_BUMP command was cleared.\n",
358 get_pname(), hostname, diskname);
360 if(put_info(hostname, diskname, &info) == 0) {
361 printf("%s: %s:%s is set to a forced level 0 at next run.\n",
362 get_pname(), hostname, diskname);
364 fprintf(stderr, "%s: %s:%s could not be forced.\n",
365 get_pname(), hostname, diskname);
370 void force(argc, argv)
374 diskloop(argc, argv, "force", force_one);
378 /* ----------------------------------------------- */
384 char *hostname = dp->host->hostname;
385 char *diskname = dp->name;
388 get_info(hostname, diskname, &info);
389 if(info.command & FORCE_FULL) {
393 info.command ^= FORCE_FULL;
394 if(put_info(hostname, diskname, &info) == 0){
395 printf("%s: force command for %s:%s cleared.\n",
396 get_pname(), hostname, diskname);
399 "%s: force command for %s:%s could not be cleared.\n",
400 get_pname(), hostname, diskname);
404 printf("%s: no force command outstanding for %s:%s, unchanged.\n",
405 get_pname(), hostname, diskname);
409 void unforce(argc, argv)
413 diskloop(argc, argv, "unforce", unforce_one);
417 /* ----------------------------------------------- */
420 void force_bump_one(dp)
423 char *hostname = dp->host->hostname;
424 char *diskname = dp->name;
430 get_info(hostname, diskname, &info);
431 info.command |= FORCE_BUMP;
432 if(info.command & FORCE_NO_BUMP) {
433 info.command ^= FORCE_NO_BUMP;
434 printf("%s: WARNING: %s:%s FORCE_NO_BUMP command was cleared.\n",
435 get_pname(), hostname, diskname);
437 if (info.command & FORCE_FULL) {
438 info.command ^= FORCE_FULL;
439 printf("%s: WARNING: %s:%s FORCE_FULL command was cleared.\n",
440 get_pname(), hostname, diskname);
442 if(put_info(hostname, diskname, &info) == 0) {
443 printf("%s: %s:%s is set to bump at next run.\n",
444 get_pname(), hostname, diskname);
446 fprintf(stderr, "%s: %s:%s could not be forced to bump.\n",
447 get_pname(), hostname, diskname);
452 void force_bump(argc, argv)
456 diskloop(argc, argv, "force-bump", force_bump_one);
460 /* ----------------------------------------------- */
463 void force_no_bump_one(dp)
466 char *hostname = dp->host->hostname;
467 char *diskname = dp->name;
473 get_info(hostname, diskname, &info);
474 info.command |= FORCE_NO_BUMP;
475 if(info.command & FORCE_BUMP) {
476 info.command ^= FORCE_BUMP;
477 printf("%s: WARNING: %s:%s FORCE_BUMP command was cleared.\n",
478 get_pname(), hostname, diskname);
480 if(put_info(hostname, diskname, &info) == 0) {
481 printf("%s: %s:%s is set to not bump at next run.\n",
482 get_pname(), hostname, diskname);
484 fprintf(stderr, "%s: %s:%s could not be force to not bump.\n",
485 get_pname(), hostname, diskname);
490 void force_no_bump(argc, argv)
494 diskloop(argc, argv, "force-no-bump", force_no_bump_one);
498 /* ----------------------------------------------- */
501 void unforce_bump_one(dp)
504 char *hostname = dp->host->hostname;
505 char *diskname = dp->name;
508 get_info(hostname, diskname, &info);
509 if(info.command & (FORCE_BUMP | FORCE_NO_BUMP)) {
513 if(info.command & FORCE_BUMP)
514 info.command ^= FORCE_BUMP;
515 if(info.command & FORCE_NO_BUMP)
516 info.command ^= FORCE_NO_BUMP;
517 if(put_info(hostname, diskname, &info) == 0) {
518 printf("%s: bump command for %s:%s cleared.\n",
519 get_pname(), hostname, diskname);
521 fprintf(stderr, "%s: %s:%s bump command could not be cleared.\n",
522 get_pname(), hostname, diskname);
526 printf("%s: no bump command outstanding for %s:%s, unchanged.\n",
527 get_pname(), hostname, diskname);
532 void unforce_bump(argc, argv)
536 diskloop(argc, argv, "unforce-bump", unforce_bump_one);
540 /* ----------------------------------------------- */
542 void reuse(argc, argv)
550 fprintf(stderr,"%s: expecting \"reuse <tapelabel> ...\"\n",
556 for(count=3; count< argc; count++) {
557 tp = lookup_tapelabel(argv[count]);
559 fprintf(stderr, "reuse: tape label %s not found in tapelist.\n",
563 if( tp->reuse == 0 ) {
565 printf("%s: marking tape %s as reusable.\n",
566 get_pname(), argv[count]);
568 fprintf(stderr, "%s: tape %s already reusable.\n",
569 get_pname(), argv[count]);
573 if(write_tapelist(conf_tapelist)) {
574 error("could not write tapelist \"%s\"", conf_tapelist);
578 void noreuse(argc, argv)
586 fprintf(stderr,"%s: expecting \"no-reuse <tapelabel> ...\"\n",
592 for(count=3; count< argc; count++) {
593 tp = lookup_tapelabel(argv[count]);
595 fprintf(stderr, "no-reuse: tape label %s not found in tapelist.\n",
599 if( tp->reuse == 1 ) {
601 printf("%s: marking tape %s as not reusable.\n",
602 get_pname(), argv[count]);
604 fprintf(stderr, "%s: tape %s already not reusable.\n",
605 get_pname(), argv[count]);
609 if(write_tapelist(conf_tapelist)) {
610 error("could not write tapelist \"%s\"", conf_tapelist);
615 /* ----------------------------------------------- */
622 char *hostname = dp->host->hostname;
623 char *diskname = dp->name;
626 if(get_info(hostname, diskname, &info)) {
627 printf("%s: %s:%s NOT currently in database.\n",
628 get_pname(), hostname, diskname);
633 if(del_info(hostname, diskname))
634 error("couldn't delete %s:%s from database: %s",
635 hostname, diskname, strerror(errno));
637 printf("%s: %s:%s deleted from curinfo database.\n",
638 get_pname(), hostname, diskname);
641 void delete(argc, argv)
646 diskloop(argc, argv, "delete", delete_one);
650 "%s: NOTE: you'll have to remove these from the disklist yourself.\n",
654 /* ----------------------------------------------- */
664 get_info(dp->host->hostname, dp->name, &info);
666 printf("\nCurrent info for %s %s:\n", dp->host->hostname, dp->name);
667 if(info.command & FORCE_FULL)
668 printf(" (Forcing to level 0 dump at next run)\n");
669 if(info.command & FORCE_BUMP)
670 printf(" (Forcing bump at next run)\n");
671 if(info.command & FORCE_NO_BUMP)
672 printf(" (Forcing no-bump at next run)\n");
673 printf(" Stats: dump rates (kps), Full: %5.1f, %5.1f, %5.1f\n",
674 info.full.rate[0], info.full.rate[1], info.full.rate[2]);
675 printf(" Incremental: %5.1f, %5.1f, %5.1f\n",
676 info.incr.rate[0], info.incr.rate[1], info.incr.rate[2]);
677 printf(" compressed size, Full: %5.1f%%,%5.1f%%,%5.1f%%\n",
678 info.full.comp[0]*100, info.full.comp[1]*100, info.full.comp[2]*100);
679 printf(" Incremental: %5.1f%%,%5.1f%%,%5.1f%%\n",
680 info.incr.comp[0]*100, info.incr.comp[1]*100, info.incr.comp[2]*100);
682 printf(" Dumps: lev datestmp tape file origK compK secs\n");
683 for(lev = 0, sp = &info.inf[0]; lev < 9; lev++, sp++) {
684 if(sp->date < (time_t)0 && sp->label[0] == '\0') continue;
685 tm = localtime(&sp->date);
686 printf(" %d %04d%02d%02d %-15s %4d %7ld %7ld %4ld\n",
687 lev, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
688 sp->label, sp->filenum, sp->size, sp->csize, sp->secs);
693 void info(argc, argv)
700 diskloop(argc, argv, "info", info_one);
702 for(dp = diskqp->head; dp != NULL; dp = dp->next)
706 /* ----------------------------------------------- */
716 if(get_info(hp->hostname, dp->name, &info)) {
717 printf("new disk %s:%s ignored.\n", hp->hostname, dp->name);
720 days = next_level0(dp, &info);
722 printf("Overdue %2d day%s %s:%s\n",
723 -days, (-days == 1) ? ": " : "s:",
724 hp->hostname, dp->name);
727 printf("Due today: %s:%s\n", hp->hostname, dp->name);
730 printf("Due in %2d day%s %s:%s\n", days,
731 (days == 1) ? ": " : "s:",
732 hp->hostname, dp->name);
745 diskloop(argc, argv, "due", due_one);
747 for(dp = diskqp->head; dp != NULL; dp = dp->next)
751 /* ----------------------------------------------- */
758 runtapes = getconf_int(CNF_RUNTAPES);
759 tp = lookup_last_reusable_tape(0);
761 for ( i=0 ; i < runtapes ; i++ ) {
762 printf("The next Amanda run should go onto ");
765 printf("tape %s or ", tp->label);
766 printf("a new tape.\n");
768 tp = lookup_last_reusable_tape(i + 1);
770 lasttp = lookup_tapepos(lookup_nb_tape());
772 if(lasttp && i > 0 && lasttp->datestamp == 0) {
774 while(lasttp && i > 0 && lasttp->datestamp == 0) {
776 lasttp = lasttp->prev;
779 lasttp = lookup_tapepos(lookup_nb_tape());
782 printf("The next new tape already labelled is: %s.\n",
786 printf("The next %d new tapes already labelled are: %s", c,
788 lasttp = lasttp->prev;
790 while(lasttp && c > 0 && lasttp->datestamp == 0) {
791 printf(", %s", lasttp->label);
792 lasttp = lasttp->prev;
800 /* ----------------------------------------------- */
802 void balance(argc, argv)
807 struct balance_stats {
809 long origsize, outsize;
811 int conf_runspercycle, conf_dumpcycle;
812 int seq, runs_per_cycle, overdue, max_overdue;
813 int later, total, balance, distinct;
814 float fseq, disk_dumpcycle;
816 long int total_balanced, balanced;
820 conf_dumpcycle = getconf_int(CNF_DUMPCYCLE);
821 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
822 later = conf_dumpcycle;
823 if(later > 10000) later = 10000;
827 if(argc > 4 && strcmp(argv[3],"--days") == 0) {
828 later = atoi(argv[4]);
829 if(later < 0) later = conf_dumpcycle;
832 if(conf_runspercycle == 0) {
833 runs_per_cycle = conf_dumpcycle;
834 } else if(conf_runspercycle == -1 ) {
835 runs_per_cycle = guess_runs_from_tapelist();
837 runs_per_cycle = conf_runspercycle;
839 if (runs_per_cycle <= 0) {
845 distinct = later + 3;
847 sp = (struct balance_stats *)
848 alloc(sizeof(struct balance_stats) * (distinct+1));
850 for(seq=0; seq <= distinct; seq++)
851 sp[seq].disks = sp[seq].origsize = sp[seq].outsize = 0;
853 for(dp = diskqp->head; dp != NULL; dp = dp->next) {
854 if(get_info(dp->host->hostname, dp->name, &info)) {
855 printf("new disk %s:%s ignored.\n", dp->host->hostname, dp->name);
858 if (dp->strategy == DS_NOFULL) {
861 sp[distinct].disks++;
862 sp[distinct].origsize += info.inf[0].size/unitdivisor;
863 sp[distinct].outsize += info.inf[0].csize/unitdivisor;
866 if(dp->dumpcycle == 0) {
867 sp[balance].origsize += (info.inf[0].size/unitdivisor) * runs_per_cycle;
868 sp[balance].outsize += (info.inf[0].csize/unitdivisor) * runs_per_cycle;
871 sp[balance].origsize += (info.inf[0].size/unitdivisor) *
872 (conf_dumpcycle / dp->dumpcycle);
873 sp[balance].outsize += (info.inf[0].csize/unitdivisor) *
874 (conf_dumpcycle / dp->dumpcycle);
877 disk_dumpcycle = dp->dumpcycle;
878 if(dp->dumpcycle <= 0)
879 disk_dumpcycle = ((float)conf_dumpcycle) / ((float)runs_per_cycle);
881 seq = next_level0(dp, &info);
886 if (-seq > max_overdue)
896 sp[seq].origsize += info.inf[0].size/unitdivisor;
897 sp[seq].outsize += info.inf[0].csize/unitdivisor;
901 sp[total].origsize += info.inf[0].size/unitdivisor;
902 sp[total].outsize += info.inf[0].csize/unitdivisor;
905 /* See, if there's another run in this dumpcycle */
906 fseq += disk_dumpcycle;
908 } while (seq < later);
911 if(sp[total].outsize == 0 && sp[later].outsize == 0) {
912 printf("\nNo data to report on yet.\n");
917 balanced = sp[balance].outsize / runs_per_cycle;
918 if(conf_dumpcycle == later) {
919 total_balanced = sp[total].outsize / runs_per_cycle;
922 total_balanced = 1024*(((sp[total].outsize/1024) * conf_dumpcycle)
923 / (runs_per_cycle * later));
927 printf("\n due-date #fs orig %cB out %cB balance\n",
928 displayunit[0], displayunit[0]);
929 printf("----------------------------------------------\n");
930 for(seq = 0; seq < later; seq++) {
931 if(sp[seq].disks == 0 &&
932 ((seq > 0 && sp[seq-1].disks == 0) ||
933 ((seq < later-1) && sp[seq+1].disks == 0))) {
941 printf("%-9.9s %3d %10ld %10ld ",
942 seqdatestr(seq), sp[seq].disks,
943 sp[seq].origsize, sp[seq].outsize);
944 if(!sp[seq].outsize) printf(" --- \n");
945 else printf("%+8.1f%%\n",
946 (sp[seq].outsize-balanced)*100.0/(double)balanced);
950 if(sp[later].disks != 0) {
951 printf("later %3d %10ld %10ld ",
953 sp[later].origsize, sp[later].outsize);
954 if(!sp[later].outsize) printf(" --- \n");
955 else printf("%+8.1f%%\n",
956 (sp[later].outsize-balanced)*100.0/(double)balanced);
958 printf("----------------------------------------------\n");
959 printf("TOTAL %3d %10ld %10ld %9ld\n", sp[total].disks,
960 sp[total].origsize, sp[total].outsize, total_balanced);
961 if (sp[balance].origsize != sp[total].origsize ||
962 sp[balance].outsize != sp[total].outsize ||
963 balanced != total_balanced) {
964 printf("BALANCED %10ld %10ld %9ld\n",
965 sp[balance].origsize, sp[balance].outsize, balanced);
967 if (sp[distinct].disks != sp[total].disks) {
968 printf("DISTINCT %3d %10ld %10ld\n", sp[distinct].disks,
969 sp[distinct].origsize, sp[distinct].outsize);
971 printf(" (estimated %d run%s per dumpcycle)\n",
972 runs_per_cycle, (runs_per_cycle == 1) ? "" : "s");
974 printf(" (%d filesystem%s overdue, the most being overdue %d day%s)\n",
975 overdue, (overdue == 1) ? "" : "s",
976 max_overdue, (max_overdue == 1) ? "" : "s");
982 /* ----------------------------------------------- */
984 void find(argc, argv)
989 char *sort_order = NULL;
990 find_result_t *output_find;
994 "%s: expecting \"find [--sort <hkdlb>] [hostname [<disk>]]*\"\n",
1000 sort_order = newstralloc(sort_order, DEFAULT_SORT_ORDER);
1001 if(argc > 4 && strcmp(argv[3],"--sort") == 0) {
1002 int i, valid_sort=1;
1004 for(i=strlen(argv[4])-1;i>=0;i--) {
1005 switch (argv[4][i]) {
1017 default: valid_sort=0;
1021 sort_order = newstralloc(sort_order, argv[4]);
1023 printf("Invalid sort order: %s\n", argv[4]);
1024 printf("Use default sort order: %s\n", sort_order);
1030 match_disklist(diskqp, argc-(start_argc-1), argv+(start_argc-1));
1031 output_find = find_dump(1, diskqp);
1033 if(argc-(start_argc-1) > 0) {
1034 free_find_result(&output_find);
1035 match_disklist(diskqp, argc-(start_argc-1), argv+(start_argc-1));
1036 output_find = find_dump(0, NULL);
1039 sort_find_result(sort_order, &output_find);
1040 print_find_result(output_find);
1041 free_find_result(&output_find);
1047 /* ------------------------ */
1050 /* shared code with planner.c */
1052 int bump_thresh(level)
1055 int bump = getconf_int(CNF_BUMPSIZE);
1056 double mult = getconf_real(CNF_BUMPMULT);
1058 while(--level) bump = (int) bump * mult;
1065 int conf_bumppercent = getconf_int(CNF_BUMPPERCENT);
1066 double conf_bumpmult = getconf_real(CNF_BUMPMULT);
1068 printf("Current bump parameters:\n");
1070 if(conf_bumppercent == 0) {
1071 printf(" bumpsize %5d KB\t- minimum savings (threshold) to bump level 1 -> 2\n",
1072 getconf_int(CNF_BUMPSIZE));
1073 printf(" bumpdays %5d\t- minimum days at each level\n",
1074 getconf_int(CNF_BUMPDAYS));
1075 printf(" bumpmult %5.5g\t- threshold = bumpsize * bumpmult**(level-1)\n\n",
1078 printf(" Bump -> To Threshold\n");
1079 for(l = 1; l < 9; l++) {
1080 printf("\t%d -> %d %9d KB\n", l, l+1, bump_thresh(l));
1085 double bumppercent = conf_bumppercent;
1087 printf(" bumppercent %3d %%\t- minimum savings (threshold) to bump level 1 -> 2\n",
1089 printf(" bumpdays %5d\t- minimum days at each level\n",
1090 getconf_int(CNF_BUMPDAYS));
1091 printf(" bumpmult %5.5g\t- threshold = disk_size * bumppercent * bumpmult**(level-1)\n\n",
1094 printf(" Bump -> To Threshold\n");
1095 for(l = 1; l < 9; l++) {
1096 printf("\t%d -> %d %7.2f %%\n", l, l+1, bumppercent);
1097 bumppercent *= conf_bumpmult;
1098 if(bumppercent >= 100.000) { bumppercent = 100.0;}
1104 /* ----------------------------------------------- */
1106 void export_one P((disk_t *dp));
1108 void export_db(argc, argv)
1114 char hostname[MAX_HOSTNAME_LENGTH+1];
1117 printf("CURINFO Version %s CONF %s\n", version(), getconf_str(CNF_ORG));
1120 if(gethostname(hostname, sizeof(hostname)-1) == -1) {
1121 error("could not determine host name: %s", strerror(errno));
1123 hostname[sizeof(hostname)-1] = '\0';
1124 printf("# Generated by:\n# host: %s\n# date: %s",
1125 hostname, ctime(&curtime));
1127 printf("# command:");
1128 for(i = 0; i < argc; i++) {
1129 printf(" %s", argv[i]);
1132 printf("\n# This file can be merged back in with \"amadmin import\".\n");
1133 printf("# Edit only with care.\n");
1136 diskloop(argc, argv, "export", export_one);
1137 } else for(dp = diskqp->head; dp != NULL; dp = dp->next) {
1148 if(get_info(dp->host->hostname, dp->name, &info)) {
1149 fprintf(stderr, "Warning: no curinfo record for %s:%s\n",
1150 dp->host->hostname, dp->name);
1153 printf("host: %s\ndisk: %s\n", dp->host->hostname, dp->name);
1154 printf("command: %d\n", info.command);
1155 printf("last_level: %d\n",info.last_level);
1156 printf("consecutive_runs: %d\n",info.consecutive_runs);
1157 printf("full-rate:");
1158 for(i=0;i<AVG_COUNT;i++) printf(" %f", info.full.rate[i]);
1159 printf("\nfull-comp:");
1160 for(i=0;i<AVG_COUNT;i++) printf(" %f", info.full.comp[i]);
1162 printf("\nincr-rate:");
1163 for(i=0;i<AVG_COUNT;i++) printf(" %f", info.incr.rate[i]);
1164 printf("\nincr-comp:");
1165 for(i=0;i<AVG_COUNT;i++) printf(" %f", info.incr.comp[i]);
1167 for(l=0;l<DUMP_LEVELS;l++) {
1168 if(info.inf[l].date < (time_t)0 && info.inf[l].label[0] == '\0') continue;
1169 printf("stats: %d %ld %ld %ld %ld %d %s\n", l,
1170 info.inf[l].size, info.inf[l].csize, info.inf[l].secs,
1171 (long)info.inf[l].date, info.inf[l].filenum,
1174 for(l=0;info.history[l].level > -1;l++) {
1175 printf("history: %d %ld %ld %ld\n",info.history[l].level,
1176 info.history[l].size, info.history[l].csize,
1177 info.history[l].date);
1182 /* ----------------------------------------------- */
1184 int import_one P((void));
1185 char *impget_line P((void));
1187 void import_db(argc, argv)
1191 int vers_maj, vers_min, vers_patch, newer;
1198 /* process header line */
1200 if((line = agets(stdin)) == NULL) {
1201 fprintf(stderr, "%s: empty input.\n", get_pname());
1209 #define sc "CURINFO Version"
1210 if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1216 skip_whitespace(s, ch);
1218 || sscanf(s - 1, "%d.%d.%d", &vers_maj, &vers_min, &vers_patch) != 3) {
1222 skip_integer(s, ch); /* skip over major */
1227 skip_integer(s, ch); /* skip over minor */
1232 skip_integer(s, ch); /* skip over patch */
1238 skip_non_whitespace(s, ch);
1242 skip_whitespace(s, ch); /* find the org keyword */
1244 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1252 skip_whitespace(s, ch); /* find the org string */
1258 newer = (vers_maj != VERSION_MAJOR)? vers_maj > VERSION_MAJOR :
1259 (vers_min != VERSION_MINOR)? vers_min > VERSION_MINOR :
1260 vers_patch > VERSION_PATCH;
1263 "%s: WARNING: input is from newer Amanda version: %d.%d.%d.\n",
1264 get_pname(), vers_maj, vers_min, vers_patch);
1266 if(strcmp(org, getconf_str(CNF_ORG)) != 0) {
1267 fprintf(stderr, "%s: WARNING: input is from different org: %s\n",
1271 while(import_one());
1279 fprintf(stderr, "%s: bad CURINFO header line in input: %s.\n",
1281 fprintf(stderr, " Was the input in \"amadmin export\" format?\n");
1286 int import_one P((void))
1296 char *hostname = NULL;
1297 char *diskname = NULL;
1303 memset(&info, 0, sizeof(info_t));
1305 for(level = 0; level < DUMP_LEVELS; level++) {
1306 info.inf[level].date = (time_t)-1;
1309 /* get host: disk: command: lines */
1311 hostname = diskname = NULL;
1313 if((line = impget_line()) == NULL) return 0; /* nothing there */
1317 skip_whitespace(s, ch);
1319 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) goto parse_err;
1323 skip_whitespace(s, ch);
1324 if(ch == '\0') goto parse_err;
1326 skip_non_whitespace(s, ch);
1328 hostname = stralloc(fp);
1331 skip_whitespace(s, ch);
1334 if((line = impget_line()) == NULL) goto shortfile_err;
1337 skip_whitespace(s, ch);
1340 if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) goto parse_err;
1344 skip_whitespace(s, ch);
1345 if(ch == '\0') goto parse_err;
1347 skip_non_whitespace(s, ch);
1349 diskname = stralloc(fp);
1353 if((line = impget_line()) == NULL) goto shortfile_err;
1354 if(sscanf(line, "command: %d", &info.command) != 1) goto parse_err;
1356 /* get last_level and consecutive_runs */
1359 if((line = impget_line()) == NULL) goto shortfile_err;
1360 rc = sscanf(line, "last_level: %d", &info.last_level);
1363 if((line = impget_line()) == NULL) goto shortfile_err;
1364 if(sscanf(line, "consecutive_runs: %d", &info.consecutive_runs) != 1) goto parse_err;
1366 if((line = impget_line()) == NULL) goto shortfile_err;
1369 /* get rate: and comp: lines for full dumps */
1371 rc = sscanf(line, "full-rate: %f %f %f",
1372 &info.full.rate[0], &info.full.rate[1], &info.full.rate[2]);
1373 if(rc != 3) goto parse_err;
1376 if((line = impget_line()) == NULL) goto shortfile_err;
1377 rc = sscanf(line, "full-comp: %f %f %f",
1378 &info.full.comp[0], &info.full.comp[1], &info.full.comp[2]);
1379 if(rc != 3) goto parse_err;
1381 /* get rate: and comp: lines for incr dumps */
1384 if((line = impget_line()) == NULL) goto shortfile_err;
1385 rc = sscanf(line, "incr-rate: %f %f %f",
1386 &info.incr.rate[0], &info.incr.rate[1], &info.incr.rate[2]);
1387 if(rc != 3) goto parse_err;
1390 if((line = impget_line()) == NULL) goto shortfile_err;
1391 rc = sscanf(line, "incr-comp: %f %f %f",
1392 &info.incr.comp[0], &info.incr.comp[1], &info.incr.comp[2]);
1393 if(rc != 3) goto parse_err;
1395 /* get stats for dump levels */
1399 if((line = impget_line()) == NULL) goto shortfile_err;
1400 if(strncmp(line, "//", 2) == 0) {
1404 if(strncmp(line, "history:", 8) == 0) {
1408 memset(&onestat, 0, sizeof(onestat));
1413 skip_whitespace(s, ch);
1415 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1422 skip_whitespace(s, ch);
1423 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1426 skip_integer(s, ch);
1428 skip_whitespace(s, ch);
1429 if(ch == '\0' || sscanf(s - 1, "%ld", &onestat.size) != 1) {
1432 skip_integer(s, ch);
1434 skip_whitespace(s, ch);
1435 if(ch == '\0' || sscanf(s - 1, "%ld", &onestat.csize) != 1) {
1438 skip_integer(s, ch);
1440 skip_whitespace(s, ch);
1441 if(ch == '\0' || sscanf(s - 1, "%ld", &onestat.secs) != 1) {
1444 skip_integer(s, ch);
1446 skip_whitespace(s, ch);
1447 if(ch == '\0' || sscanf(s - 1, "%ld", &onedate) != 1) {
1450 skip_integer(s, ch);
1452 skip_whitespace(s, ch);
1454 if(sscanf(s - 1, "%d", &onestat.filenum) != 1) {
1457 skip_integer(s, ch);
1459 skip_whitespace(s, ch);
1461 if (onestat.filenum != 0)
1463 onestat.label[0] = '\0';
1465 strncpy(onestat.label, s - 1, sizeof(onestat.label)-1);
1466 onestat.label[sizeof(onestat.label)-1] = '\0';
1470 /* time_t not guarranteed to be long */
1471 onestat.date = onedate;
1472 if(level < 0 || level > 9) goto parse_err;
1474 info.inf[level] = onestat;
1477 for(i=0;i<=NB_HISTORY+1;i++) {
1478 info.history[i].level = -2;
1481 history_t onehistory;
1484 if(line[0] == '/' && line[1] == '/') {
1485 info.history[nb_history].level = -2;
1489 memset(&onehistory, 0, sizeof(onehistory));
1492 #define sc "history:"
1493 if(strncmp(line, sc, sizeof(sc)-1) != 0) {
1499 skip_whitespace(s, ch);
1500 if(ch == '\0' || sscanf((s - 1), "%d", &onehistory.level) != 1) {
1503 skip_integer(s, ch);
1505 skip_whitespace(s, ch);
1506 if(ch == '\0' || sscanf((s - 1), "%ld", &onehistory.size) != 1) {
1509 skip_integer(s, ch);
1511 skip_whitespace(s, ch);
1512 if(ch == '\0' || sscanf((s - 1), "%ld", &onehistory.csize) != 1) {
1515 skip_integer(s, ch);
1517 skip_whitespace(s, ch);
1518 if(ch == '\0' || sscanf((s - 1), "%ld", &date) != 1) {
1521 skip_integer(s, ch);
1522 onehistory.date = date; /* time_t not guarranteed to be long */
1524 info.history[nb_history++] = onehistory;
1526 if((line = impget_line()) == NULL) goto shortfile_err;
1530 /* got a full record, now write it out to the database */
1532 if(put_info(hostname, diskname, &info)) {
1533 fprintf(stderr, "%s: error writing record for %s:%s\n",
1534 get_pname(), hostname, diskname);
1544 fprintf(stderr, "%s: parse error reading import record.\n", get_pname());
1551 fprintf(stderr, "%s: short file reading import record.\n", get_pname());
1562 for(; (line = agets(stdin)) != NULL; free(line)) {
1566 skip_whitespace(s, ch);
1568 /* ignore comment lines */
1571 /* found non-blank, return line */
1574 /* otherwise, a blank line, so keep going */
1577 fprintf(stderr, "%s: reading stdin: %s\n",
1578 get_pname(), strerror(errno));
1583 /* ----------------------------------------------- */
1585 void disklist_one(dp)
1595 printf("line %d:\n", dp->line);
1597 printf(" host %s:\n", hp->hostname);
1598 printf(" interface %s\n",
1599 ip->name[0] ? ip->name : "default");
1601 printf(" disk %s:\n", dp->name);
1602 if(dp->device) printf(" device %s\n", dp->device);
1604 printf(" program \"%s\"\n", dp->program);
1605 if(dp->exclude_file != NULL && dp->exclude_file->nb_element > 0) {
1606 printf(" exclude file");
1607 for(excl = dp->exclude_file->first; excl != NULL; excl = excl->next) {
1608 printf(" \"%s\"", excl->name);
1612 if(dp->exclude_list != NULL && dp->exclude_list->nb_element > 0) {
1613 printf(" exclude list");
1614 if(dp->exclude_optional) printf(" optional");
1615 for(excl = dp->exclude_list->first; excl != NULL; excl = excl->next) {
1616 printf(" \"%s\"", excl->name);
1620 if(dp->include_file != NULL && dp->include_file->nb_element > 0) {
1621 printf(" include file");
1622 for(excl = dp->include_file->first; excl != NULL; excl = excl->next) {
1623 printf(" \"%s\"", excl->name);
1627 if(dp->include_list != NULL && dp->include_list->nb_element > 0) {
1628 printf(" include list");
1629 if(dp->include_optional) printf(" optional");
1630 for(excl = dp->include_list->first; excl != NULL; excl = excl->next) {
1631 printf(" \"%s\"", excl->name);
1635 printf(" priority %ld\n", dp->priority);
1636 printf(" dumpcycle %ld\n", dp->dumpcycle);
1637 printf(" maxdumps %d\n", dp->maxdumps);
1638 printf(" maxpromoteday %d\n", dp->maxpromoteday);
1639 if(dp->bumppercent > 0) {
1640 printf(" bumppercent %d\n", dp->bumppercent);
1643 printf(" bumpsize %d\n", dp->bumpsize);
1645 printf(" bumpdays %d\n", dp->bumpdays);
1646 printf(" bumpmult %f\n", dp->bumpmult);
1648 printf(" strategy ");
1649 switch(dp->strategy) {
1654 printf("STANDARD\n");
1666 printf("INCRONLY\n");
1670 printf(" estimate ");
1671 switch(dp->estimate) {
1679 printf("CALCSIZE\n");
1683 printf(" compress ");
1684 switch(dp->compress) {
1689 printf("CLIENT FAST\n");
1692 printf("CLIENT BEST\n");
1694 case COMP_SERV_FAST:
1695 printf("SERVER FAST\n");
1697 case COMP_SERV_BEST:
1698 printf("SERVER BEST\n");
1701 if(dp->compress != COMP_NONE) {
1702 printf(" comprate %.2f %.2f\n",
1703 dp->comprate[0], dp->comprate[1]);
1715 printf(" kencrypt %s\n", (dp->kencrypt? "YES" : "NO"));
1717 printf(" holdingdisk %s\n", (!dp->no_hold? "YES" : "NO"));
1718 printf(" record %s\n", (dp->record? "YES" : "NO"));
1719 printf(" index %s\n", (dp->index? "YES" : "NO"));
1720 printf(" skip-incr %s\n", (dp->skip_incr? "YES" : "NO"));
1721 printf(" skip-full %s\n", (dp->skip_full? "YES" : "NO"));
1726 void disklist(argc, argv)
1733 diskloop(argc, argv, "disklist", disklist_one);
1735 for(dp = diskqp->head; dp != NULL; dp = dp->next)