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.124 2006/07/26 15:17:37 martinea Exp $
30 * controlling process for the Amanda backup system
44 int main(int argc, char **argv);
46 void force(int argc, char **argv);
47 void force_one(disk_t *dp);
48 void unforce(int argc, char **argv);
49 void unforce_one(disk_t *dp);
50 void force_bump(int argc, char **argv);
51 void force_bump_one(disk_t *dp);
52 void force_no_bump(int argc, char **argv);
53 void force_no_bump_one(disk_t *dp);
54 void unforce_bump(int argc, char **argv);
55 void unforce_bump_one(disk_t *dp);
56 void reuse(int argc, char **argv);
57 void noreuse(int argc, char **argv);
58 void info(int argc, char **argv);
59 void info_one(disk_t *dp);
60 void due(int argc, char **argv);
61 void due_one(disk_t *dp);
62 void find(int argc, char **argv);
63 void delete(int argc, char **argv);
64 void delete_one(disk_t *dp);
65 void balance(int argc, char **argv);
66 void tape(int argc, char **argv);
67 void bumpsize(int argc, char **argv);
68 void diskloop(int argc, char **argv, char *cmdname, void (*func)(disk_t *dp));
69 char *seqdatestr(int seq);
70 static int next_level0(disk_t *dp, info_t *info);
71 int bump_thresh(int level);
72 void export_db(int argc, char **argv);
73 void import_db(int argc, char **argv);
74 void disklist(int argc, char **argv);
75 void disklist_one(disk_t *dp);
76 void show_version(int argc, char **argv);
77 static void show_config(int argc, char **argv);
78 static void check_dumpuser(void);
80 static char *conf_tapelist = NULL;
81 static char *displayunit;
82 static long int unitdivisor;
86 void (*fn)(int, char **);
89 { "version", show_version,
90 "\t\t\t\t\t# Show version info." },
91 { "config", show_config,
92 "\t\t\t\t\t# Show configuration." },
94 " [<hostname> [<disks>]* ]+\t\t# Force level 0 at next run." },
96 " [<hostname> [<disks>]* ]+\t# Clear force command." },
97 { "force-bump", force_bump,
98 " [<hostname> [<disks>]* ]+\t# Force bump at next run." },
99 { "force-no-bump", force_no_bump,
100 " [<hostname> [<disks>]* ]+\t# Force no-bump at next run." },
101 { "unforce-bump", unforce_bump,
102 " [<hostname> [<disks>]* ]+\t# Clear bump command." },
103 { "disklist", disklist,
104 " [<hostname> [<disks>]* ]*\t# Debug disklist entries." },
106 " <tapelabel> ...\t\t # re-use this tape." },
107 { "no-reuse", noreuse,
108 " <tapelabel> ...\t # never re-use this tape." },
110 " [<hostname> [<disks>]* ]*\t # Show which tapes these dumps are on." },
112 " [<hostname> [<disks>]* ]+ # Delete from database." },
114 " [<hostname> [<disks>]* ]*\t # Show current info records." },
116 " [<hostname> [<disks>]* ]*\t # Show due date." },
117 { "balance", balance,
118 " [-days <num>]\t\t # Show nightly dump size balance." },
120 " [-days <num>]\t\t # Show which tape is due next." },
121 { "bumpsize", bumpsize,
122 "\t\t\t # Show current bump thresholds." },
123 { "export", export_db,
124 " [<hostname> [<disks>]* ]* # Export curinfo database to stdout." },
125 { "import", import_db,
126 "\t\t\t\t # Import curinfo database from stdin." },
128 #define NCMDS (int)(sizeof(cmdtab) / sizeof(cmdtab[0]))
130 static char *conffile;
140 unsigned long malloc_hist_1, malloc_size_1;
141 unsigned long malloc_hist_2, malloc_size_2;
148 set_pname("amadmin");
150 dbopen(DBG_SUBDIR_SERVER);
152 /* Don't die when child closes pipe */
153 signal(SIGPIPE, SIG_IGN);
155 malloc_size_1 = malloc_inuse(&malloc_hist_1);
157 erroutput_type = ERR_INTERACTIVE;
159 parse_server_conf(argc, argv, &new_argc, &new_argv);
161 if(new_argc < 3) usage();
163 if(strcmp(new_argv[2],"version") == 0) {
164 show_version(new_argc, new_argv);
168 config_name = new_argv[1];
170 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
171 conffile = stralloc2(config_dir, CONFFILE_NAME);
173 if(read_conffile(conffile)) {
174 error("errors processing config file \"%s\"", conffile);
178 dbrename(config_name, DBG_SUBDIR_SERVER);
180 report_bad_conf_arg();
184 conf_diskfile = getconf_str(CNF_DISKFILE);
185 if (*conf_diskfile == '/') {
186 conf_diskfile = stralloc(conf_diskfile);
188 conf_diskfile = stralloc2(config_dir, conf_diskfile);
190 if (read_diskfile(conf_diskfile, &diskq) < 0) {
191 error("could not load disklist \"%s\"", conf_diskfile);
194 amfree(conf_diskfile);
196 conf_tapelist = getconf_str(CNF_TAPELIST);
197 if (*conf_tapelist == '/') {
198 conf_tapelist = stralloc(conf_tapelist);
200 conf_tapelist = stralloc2(config_dir, conf_tapelist);
202 if(read_tapelist(conf_tapelist)) {
203 error("could not load tapelist \"%s\"", conf_tapelist);
206 conf_infofile = getconf_str(CNF_INFOFILE);
207 if (*conf_infofile == '/') {
208 conf_infofile = stralloc(conf_infofile);
210 conf_infofile = stralloc2(config_dir, conf_infofile);
212 if(open_infofile(conf_infofile)) {
213 error("could not open info db \"%s\"", conf_infofile);
216 amfree(conf_infofile);
218 displayunit = getconf_str(CNF_DISPLAYUNIT);
219 unitdivisor = getconf_unit_divisor();
221 for (i = 0; i < NCMDS; i++)
222 if (strcmp(new_argv[2], cmdtab[i].name) == 0) {
223 (*cmdtab[i].fn)(new_argc, new_argv);
227 fprintf(stderr, "%s: unknown command \"%s\"\n", new_argv[0], new_argv[2]);
231 free_new_argv(new_argc, new_argv);
235 amfree(conf_tapelist);
240 malloc_size_2 = malloc_inuse(&malloc_hist_2);
242 if(malloc_size_1 != malloc_size_2) {
243 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
247 free_disklist(&diskq);
248 free_server_config();
259 fprintf(stderr, "\nUsage: %s%s <conf> <command> {<args>} [-o configoption]* ...\n",
260 get_pname(), versionsuffix());
261 fprintf(stderr, " Valid <command>s are:\n");
262 for (i = 0; i < NCMDS; i++)
263 fprintf(stderr, "\t%s%s\n", cmdtab[i].name, cmdtab[i].usage);
268 /* ----------------------------------------------- */
270 #define SECS_PER_DAY (24*60*60)
278 static char *dow[7] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
279 time_t t = today + seq*SECS_PER_DAY;
285 snprintf(str, SIZEOF(str),
286 "%2d/%02d %3s", tm->tm_mon+1, tm->tm_mday, dow[tm->tm_wday]);
288 strcpy(str, "BAD DATE");
294 #define days_diff(a, b) (int)(((b) - (a) + SECS_PER_DAY) / SECS_PER_DAY)
296 /* when is next level 0 due? 0 = tonight, 1 = tommorrow, etc*/
302 if(dp->strategy == DS_NOFULL)
303 return 1; /* fake it */
304 if(info->inf[0].date < (time_t)0)
305 return 0; /* new disk */
307 return dp->dumpcycle - days_diff(info->inf[0].date, today);
313 static int been_here = 0;
323 uid_dumpuser = uid_me;
324 dumpuser = getconf_str(CNF_DUMPUSER);
326 if ((pw = getpwnam(dumpuser)) == NULL) {
327 error("cannot look up dump user \"%s\"", dumpuser);
330 uid_dumpuser = pw->pw_uid;
331 if ((pw = getpwuid(uid_me)) == NULL) {
332 error("cannot look up my own uid %ld", (long)uid_me);
335 if (uid_me != uid_dumpuser) {
336 error("ERROR: running as user \"%s\" instead of \"%s\"",
337 pw->pw_name, dumpuser);
344 /* ----------------------------------------------- */
351 void (*func)(disk_t *dp))
358 fprintf(stderr,"%s: expecting \"%s [<hostname> [<disks>]* ]+\"\n",
359 get_pname(), cmdname);
363 errstr = match_disklist(&diskq, argc-3, argv+3);
365 printf("%s", errstr);
369 for(dp = diskq.head; dp != NULL; dp = dp->next) {
376 fprintf(stderr,"%s: no disk matched\n",get_pname());
380 /* ----------------------------------------------- */
387 char *hostname = dp->host->hostname;
388 char *diskname = dp->name;
394 get_info(hostname, diskname, &info);
395 SET(info.command, FORCE_FULL);
396 if (ISSET(info.command, FORCE_BUMP)) {
397 CLR(info.command, FORCE_BUMP);
398 printf("%s: WARNING: %s:%s FORCE_BUMP command was cleared.\n",
399 get_pname(), hostname, diskname);
401 if(put_info(hostname, diskname, &info) == 0) {
402 printf("%s: %s:%s is set to a forced level 0 at next run.\n",
403 get_pname(), hostname, diskname);
405 fprintf(stderr, "%s: %s:%s could not be forced.\n",
406 get_pname(), hostname, diskname);
416 diskloop(argc, argv, "force", force_one);
420 /* ----------------------------------------------- */
427 char *hostname = dp->host->hostname;
428 char *diskname = dp->name;
431 get_info(hostname, diskname, &info);
432 if (ISSET(info.command, FORCE_FULL)) {
436 CLR(info.command, FORCE_FULL);
437 if(put_info(hostname, diskname, &info) == 0){
438 printf("%s: force command for %s:%s cleared.\n",
439 get_pname(), hostname, diskname);
442 "%s: force command for %s:%s could not be cleared.\n",
443 get_pname(), hostname, diskname);
447 printf("%s: no force command outstanding for %s:%s, unchanged.\n",
448 get_pname(), hostname, diskname);
457 diskloop(argc, argv, "unforce", unforce_one);
461 /* ----------------------------------------------- */
468 char *hostname = dp->host->hostname;
469 char *diskname = dp->name;
475 get_info(hostname, diskname, &info);
476 SET(info.command, FORCE_BUMP);
477 if (ISSET(info.command, FORCE_NO_BUMP)) {
478 CLR(info.command, FORCE_NO_BUMP);
479 printf("%s: WARNING: %s:%s FORCE_NO_BUMP command was cleared.\n",
480 get_pname(), hostname, diskname);
482 if (ISSET(info.command, FORCE_FULL)) {
483 CLR(info.command, FORCE_FULL);
484 printf("%s: WARNING: %s:%s FORCE_FULL command was cleared.\n",
485 get_pname(), hostname, diskname);
487 if(put_info(hostname, diskname, &info) == 0) {
488 printf("%s: %s:%s is set to bump at next run.\n",
489 get_pname(), hostname, diskname);
491 fprintf(stderr, "%s: %s:%s could not be forced to bump.\n",
492 get_pname(), hostname, diskname);
502 diskloop(argc, argv, "force-bump", force_bump_one);
506 /* ----------------------------------------------- */
513 char *hostname = dp->host->hostname;
514 char *diskname = dp->name;
520 get_info(hostname, diskname, &info);
521 SET(info.command, FORCE_NO_BUMP);
522 if (ISSET(info.command, FORCE_BUMP)) {
523 CLR(info.command, FORCE_BUMP);
524 printf("%s: WARNING: %s:%s FORCE_BUMP command was cleared.\n",
525 get_pname(), hostname, diskname);
527 if(put_info(hostname, diskname, &info) == 0) {
528 printf("%s: %s:%s is set to not bump at next run.\n",
529 get_pname(), hostname, diskname);
531 fprintf(stderr, "%s: %s:%s could not be force to not bump.\n",
532 get_pname(), hostname, diskname);
542 diskloop(argc, argv, "force-no-bump", force_no_bump_one);
546 /* ----------------------------------------------- */
553 char *hostname = dp->host->hostname;
554 char *diskname = dp->name;
557 get_info(hostname, diskname, &info);
558 if (ISSET(info.command, FORCE_BUMP|FORCE_NO_BUMP)) {
562 CLR(info.command, FORCE_BUMP|FORCE_NO_BUMP);
563 if(put_info(hostname, diskname, &info) == 0) {
564 printf("%s: bump command for %s:%s cleared.\n",
565 get_pname(), hostname, diskname);
567 fprintf(stderr, "%s: %s:%s bump command could not be cleared.\n",
568 get_pname(), hostname, diskname);
572 printf("%s: no bump command outstanding for %s:%s, unchanged.\n",
573 get_pname(), hostname, diskname);
583 diskloop(argc, argv, "unforce-bump", unforce_bump_one);
587 /* ----------------------------------------------- */
598 fprintf(stderr,"%s: expecting \"reuse <tapelabel> ...\"\n",
604 for(count=3; count< argc; count++) {
605 tp = lookup_tapelabel(argv[count]);
607 fprintf(stderr, "reuse: tape label %s not found in tapelist.\n",
611 if( tp->reuse == 0 ) {
613 printf("%s: marking tape %s as reusable.\n",
614 get_pname(), argv[count]);
616 fprintf(stderr, "%s: tape %s already reusable.\n",
617 get_pname(), argv[count]);
621 if(write_tapelist(conf_tapelist)) {
622 error("could not write tapelist \"%s\"", conf_tapelist);
636 fprintf(stderr,"%s: expecting \"no-reuse <tapelabel> ...\"\n",
642 for(count=3; count< argc; count++) {
643 tp = lookup_tapelabel(argv[count]);
645 fprintf(stderr, "no-reuse: tape label %s not found in tapelist.\n",
649 if( tp->reuse == 1 ) {
651 printf("%s: marking tape %s as not reusable.\n",
652 get_pname(), argv[count]);
654 fprintf(stderr, "%s: tape %s already not reusable.\n",
655 get_pname(), argv[count]);
659 if(write_tapelist(conf_tapelist)) {
660 error("could not write tapelist \"%s\"", conf_tapelist);
666 /* ----------------------------------------------- */
674 char *hostname = dp->host->hostname;
675 char *diskname = dp->name;
678 if(get_info(hostname, diskname, &info)) {
679 printf("%s: %s:%s NOT currently in database.\n",
680 get_pname(), hostname, diskname);
685 if(del_info(hostname, diskname)) {
686 error("couldn't delete %s:%s from database: %s",
687 hostname, diskname, strerror(errno));
690 printf("%s: %s:%s deleted from curinfo database.\n",
691 get_pname(), hostname, diskname);
701 diskloop(argc, argv, "delete", delete_one);
705 "%s: NOTE: you'll have to remove these from the disklist yourself.\n",
709 /* ----------------------------------------------- */
720 get_info(dp->host->hostname, dp->name, &info);
722 printf("\nCurrent info for %s %s:\n", dp->host->hostname, dp->name);
723 if (ISSET(info.command, FORCE_FULL))
724 printf(" (Forcing to level 0 dump at next run)\n");
725 if (ISSET(info.command, FORCE_BUMP))
726 printf(" (Forcing bump at next run)\n");
727 if (ISSET(info.command, FORCE_NO_BUMP))
728 printf(" (Forcing no-bump at next run)\n");
729 printf(" Stats: dump rates (kps), Full: %5.1lf, %5.1lf, %5.1lf\n",
730 info.full.rate[0], info.full.rate[1], info.full.rate[2]);
731 printf(" Incremental: %5.1lf, %5.1lf, %5.1lf\n",
732 info.incr.rate[0], info.incr.rate[1], info.incr.rate[2]);
733 printf(" compressed size, Full: %5.1lf%%,%5.1lf%%,%5.1lf%%\n",
734 info.full.comp[0]*100, info.full.comp[1]*100, info.full.comp[2]*100);
735 printf(" Incremental: %5.1lf%%,%5.1lf%%,%5.1lf%%\n",
736 info.incr.comp[0]*100, info.incr.comp[1]*100, info.incr.comp[2]*100);
738 printf(" Dumps: lev datestmp tape file origK compK secs\n");
739 for(lev = 0, sp = &info.inf[0]; lev < 9; lev++, sp++) {
740 if(sp->date < (time_t)0 && sp->label[0] == '\0') continue;
741 tm = localtime(&sp->date);
743 printf(" %d %04d%02d%02d %-15s "
744 OFF_T_FMT " " OFF_T_FMT " " OFF_T_FMT " " TIME_T_FMT "\n",
745 lev, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
747 (OFF_T_FMT_TYPE)sp->filenum,
748 (OFF_T_FMT_TYPE)sp->size,
749 (OFF_T_FMT_TYPE)sp->csize,
750 (TIME_T_FMT_TYPE)sp->secs);
752 printf(" %d BAD-DATE %-15s "
753 OFF_T_FMT " " OFF_T_FMT " " OFF_T_FMT " " TIME_T_FMT "\n",
756 (OFF_T_FMT_TYPE)sp->filenum,
757 (OFF_T_FMT_TYPE)sp->size,
758 (OFF_T_FMT_TYPE)sp->csize,
759 (TIME_T_FMT_TYPE)sp->secs);
773 diskloop(argc, argv, "info", info_one);
775 for(dp = diskq.head; dp != NULL; dp = dp->next)
779 /* ----------------------------------------------- */
790 if(get_info(hp->hostname, dp->name, &info)) {
791 printf("new disk %s:%s ignored.\n", hp->hostname, dp->name);
794 days = next_level0(dp, &info);
796 printf("Overdue %2d day%s %s:%s\n",
797 -days, (-days == 1) ? ": " : "s:",
798 hp->hostname, dp->name);
801 printf("Due today: %s:%s\n", hp->hostname, dp->name);
804 printf("Due in %2d day%s %s:%s\n", days,
805 (days == 1) ? ": " : "s:",
806 hp->hostname, dp->name);
820 diskloop(argc, argv, "due", due_one);
822 for(dp = diskq.head; dp != NULL; dp = dp->next)
826 /* ----------------------------------------------- */
837 if(argc > 4 && strcmp(argv[3],"--days") == 0) {
838 nb_days = atoi(argv[4]);
840 printf("days must be an integer bigger than 0\n");
847 runtapes = getconf_int(CNF_RUNTAPES);
848 tp = lookup_last_reusable_tape(0);
850 for ( j=0 ; j < nb_days ; j++ ) {
851 for ( i=0 ; i < runtapes ; i++ ) {
853 printf("The next Amanda run should go onto ");
857 printf("tape %s or a new tape.\n", tp->label);
859 if (runtapes - i == 1)
860 printf("1 new tape.\n");
862 printf("%d new tapes.\n", runtapes - i);
866 tp = lookup_last_reusable_tape(i + 1);
869 lasttp = lookup_tapepos(lookup_nb_tape());
871 if(lasttp && i > 0 && strcmp(lasttp->datestamp,"0") == 0) {
873 while(lasttp && i > 0 && strcmp(lasttp->datestamp,"0") == 0) {
875 lasttp = lasttp->prev;
878 lasttp = lookup_tapepos(lookup_nb_tape());
881 printf("The next new tape already labelled is: %s.\n",
885 printf("The next %d new tapes already labelled are: %s", c,
887 lasttp = lasttp->prev;
889 while(lasttp && c > 0 && strcmp(lasttp->datestamp,"0") == 0) {
890 printf(", %s", lasttp->label);
891 lasttp = lasttp->prev;
899 /* ----------------------------------------------- */
907 struct balance_stats {
909 off_t origsize, outsize;
911 int conf_runspercycle, conf_dumpcycle;
912 int seq, runs_per_cycle, overdue, max_overdue;
913 int later, total, balance, distinct;
914 double fseq, disk_dumpcycle;
916 off_t total_balanced, balanced;
920 conf_dumpcycle = getconf_int(CNF_DUMPCYCLE);
921 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
922 later = conf_dumpcycle;
926 if(argc > 4 && strcmp(argv[3],"--days") == 0) {
927 later = atoi(argv[4]);
928 if(later < 0) later = conf_dumpcycle;
930 if(later > 10000) later = 10000;
932 if(conf_runspercycle == 0) {
933 runs_per_cycle = conf_dumpcycle;
934 } else if(conf_runspercycle == -1 ) {
935 runs_per_cycle = guess_runs_from_tapelist();
937 runs_per_cycle = conf_runspercycle;
939 if (runs_per_cycle <= 0) {
945 distinct = later + 3;
947 sp = (struct balance_stats *)
948 alloc(SIZEOF(struct balance_stats) * (distinct+1));
950 for(seq=0; seq <= distinct; seq++) {
952 sp[seq].origsize = sp[seq].outsize = (off_t)0;
955 for(dp = diskq.head; dp != NULL; dp = dp->next) {
956 if(get_info(dp->host->hostname, dp->name, &info)) {
957 printf("new disk %s:%s ignored.\n", dp->host->hostname, dp->name);
960 if (dp->strategy == DS_NOFULL) {
963 sp[distinct].disks++;
964 sp[distinct].origsize += info.inf[0].size/(off_t)unitdivisor;
965 sp[distinct].outsize += info.inf[0].csize/(off_t)unitdivisor;
968 if(dp->dumpcycle == 0) {
969 sp[balance].origsize += (info.inf[0].size/(off_t)unitdivisor) * (off_t)runs_per_cycle;
970 sp[balance].outsize += (info.inf[0].csize/(off_t)unitdivisor) * (off_t)runs_per_cycle;
973 sp[balance].origsize += (info.inf[0].size/(off_t)unitdivisor) *
974 (off_t)(conf_dumpcycle / dp->dumpcycle);
975 sp[balance].outsize += (info.inf[0].csize/(off_t)unitdivisor) *
976 (off_t)(conf_dumpcycle / dp->dumpcycle);
979 disk_dumpcycle = (double)dp->dumpcycle;
980 if(dp->dumpcycle <= 0)
981 disk_dumpcycle = ((double)conf_dumpcycle) / ((double)runs_per_cycle);
983 seq = next_level0(dp, &info);
988 if (-seq > max_overdue)
998 sp[seq].origsize += info.inf[0].size/(off_t)unitdivisor;
999 sp[seq].outsize += info.inf[0].csize/(off_t)unitdivisor;
1003 sp[total].origsize += info.inf[0].size/(off_t)unitdivisor;
1004 sp[total].outsize += info.inf[0].csize/(off_t)unitdivisor;
1007 /* See, if there's another run in this dumpcycle */
1008 fseq += disk_dumpcycle;
1010 } while (seq < later);
1013 if(sp[total].outsize == (off_t)0 && sp[later].outsize == (off_t)0) {
1014 printf("\nNo data to report on yet.\n");
1019 balanced = sp[balance].outsize / (off_t)runs_per_cycle;
1020 if(conf_dumpcycle == later) {
1021 total_balanced = sp[total].outsize / (off_t)runs_per_cycle;
1024 total_balanced = (((sp[total].outsize/(off_t)1024) * (off_t)conf_dumpcycle)
1025 / (off_t)(runs_per_cycle * later)) * (off_t)1024;
1029 printf("\n due-date #fs orig %cB out %cB balance\n",
1030 displayunit[0], displayunit[0]);
1031 printf("----------------------------------------------\n");
1032 for(seq = 0; seq < later; seq++) {
1033 if(sp[seq].disks == 0 &&
1034 ((seq > 0 && sp[seq-1].disks == 0) ||
1035 ((seq < later-1) && sp[seq+1].disks == 0))) {
1043 printf("%-9.9s %3d " OFF_T_FMT " " OFF_T_FMT " ",
1044 seqdatestr(seq), sp[seq].disks,
1045 (OFF_T_FMT_TYPE)sp[seq].origsize,
1046 (OFF_T_FMT_TYPE)sp[seq].outsize);
1047 if(!sp[seq].outsize) printf(" --- \n");
1048 else printf("%+8.1lf%%\n",
1049 (((double)sp[seq].outsize - (double)balanced) * 100.0 /
1054 if(sp[later].disks != 0) {
1055 printf("later %3d " OFF_T_FMT " " OFF_T_FMT " ",
1057 (OFF_T_FMT_TYPE)sp[later].origsize,
1058 (OFF_T_FMT_TYPE)sp[later].outsize);
1059 if(!sp[later].outsize) printf(" --- \n");
1060 else printf("%+8.1lf%%\n",
1061 (((double)sp[later].outsize - (double)balanced) * 100.0 /
1064 printf("----------------------------------------------\n");
1065 printf("TOTAL %3d " OFF_T_FMT " " OFF_T_FMT " " OFF_T_FMT "\n",
1067 (OFF_T_FMT_TYPE)sp[total].origsize,
1068 (OFF_T_FMT_TYPE)sp[total].outsize,
1069 (OFF_T_FMT_TYPE)total_balanced);
1070 if (sp[balance].origsize != sp[total].origsize ||
1071 sp[balance].outsize != sp[total].outsize ||
1072 balanced != total_balanced) {
1073 printf("BALANCED " OFF_T_FMT " " OFF_T_FMT " " OFF_T_FMT "\n",
1074 (OFF_T_FMT_TYPE)sp[balance].origsize,
1075 (OFF_T_FMT_TYPE)sp[balance].outsize,
1076 (OFF_T_FMT_TYPE)balanced);
1078 if (sp[distinct].disks != sp[total].disks) {
1079 printf("DISTINCT %3d " OFF_T_FMT " " OFF_T_FMT "\n",
1081 (OFF_T_FMT_TYPE)sp[distinct].origsize,
1082 (OFF_T_FMT_TYPE)sp[distinct].outsize);
1084 printf(" (estimated %d run%s per dumpcycle)\n",
1085 runs_per_cycle, (runs_per_cycle == 1) ? "" : "s");
1087 printf(" (%d filesystem%s overdue, the most being overdue %d day%s)\n",
1088 overdue, (overdue == 1) ? "" : "s",
1089 max_overdue, (max_overdue == 1) ? "" : "s");
1095 /* ----------------------------------------------- */
1103 char *sort_order = NULL;
1104 find_result_t *output_find;
1109 "%s: expecting \"find [--sort <hkdlpb>] [hostname [<disk>]]*\"\n",
1115 sort_order = newstralloc(sort_order, DEFAULT_SORT_ORDER);
1116 if(argc > 4 && strcmp(argv[3],"--sort") == 0) {
1117 size_t i, valid_sort=1;
1119 for(i = strlen(argv[4]); i > 0; i--) {
1120 switch (argv[4][i - 1]) {
1134 default: valid_sort=0;
1138 sort_order = newstralloc(sort_order, argv[4]);
1140 printf("Invalid sort order: %s\n", argv[4]);
1141 printf("Use default sort order: %s\n", sort_order);
1147 errstr = match_disklist(&diskq, argc-(start_argc-1), argv+(start_argc-1));
1149 printf("%s", errstr);
1153 output_find = find_dump(1, &diskq);
1154 if(argc-(start_argc-1) > 0) {
1155 free_find_result(&output_find);
1156 errstr = match_disklist(&diskq, argc-(start_argc-1),
1157 argv+(start_argc-1));
1159 printf("%s", errstr);
1162 output_find = find_dump(0, NULL);
1165 sort_find_result(sort_order, &output_find);
1166 print_find_result(output_find);
1167 free_find_result(&output_find);
1173 /* ------------------------ */
1176 /* shared code with planner.c */
1182 int bump = getconf_int(CNF_BUMPSIZE);
1183 double mult = getconf_real(CNF_BUMPMULT);
1186 bump = (int)((double)bump * mult);
1196 int conf_bumppercent = getconf_int(CNF_BUMPPERCENT);
1197 double conf_bumpmult = getconf_real(CNF_BUMPMULT);
1199 (void)argc; /* Quiet unused parameter warning */
1200 (void)argv; /* Quiet unused parameter warning */
1202 printf("Current bump parameters:\n");
1203 if(conf_bumppercent == 0) {
1204 printf(" bumpsize %5d KB\t- minimum savings (threshold) to bump level 1 -> 2\n",
1205 getconf_int(CNF_BUMPSIZE));
1206 printf(" bumpdays %5d\t- minimum days at each level\n",
1207 getconf_int(CNF_BUMPDAYS));
1208 printf(" bumpmult %5.5lg\t- threshold = bumpsize * bumpmult**(level-1)\n\n",
1211 printf(" Bump -> To Threshold\n");
1212 for(l = 1; l < 9; l++)
1213 printf("\t%d -> %d %9d KB\n", l, l+1, bump_thresh(l));
1217 double bumppercent = (double)conf_bumppercent;
1219 printf(" bumppercent %3d %%\t- minimum savings (threshold) to bump level 1 -> 2\n",
1221 printf(" bumpdays %5d\t- minimum days at each level\n",
1222 getconf_int(CNF_BUMPDAYS));
1223 printf(" bumpmult %5.5lg\t- threshold = disk_size * bumppercent * bumpmult**(level-1)\n\n",
1225 printf(" Bump -> To Threshold\n");
1226 for(l = 1; l < 9; l++) {
1227 printf("\t%d -> %d %7.2lf %%\n", l, l+1, bumppercent);
1228 bumppercent *= conf_bumpmult;
1229 if(bumppercent >= 100.000) { bumppercent = 100.0;}
1235 /* ----------------------------------------------- */
1237 void export_one(disk_t *dp);
1246 char hostname[MAX_HOSTNAME_LENGTH+1];
1249 printf("CURINFO Version %s CONF %s\n", version(), getconf_str(CNF_ORG));
1252 if(gethostname(hostname, SIZEOF(hostname)-1) == -1) {
1253 error("could not determine host name: %s\n", strerror(errno));
1256 hostname[SIZEOF(hostname)-1] = '\0';
1257 printf("# Generated by:\n# host: %s\n# date: %s",
1258 hostname, ctime(&curtime));
1260 printf("# command:");
1261 for(i = 0; i < argc; i++)
1262 printf(" %s", argv[i]);
1264 printf("\n# This file can be merged back in with \"amadmin import\".\n");
1265 printf("# Edit only with care.\n");
1268 diskloop(argc, argv, "export", export_one);
1269 else for(dp = diskq.head; dp != NULL; dp = dp->next)
1280 if(get_info(dp->host->hostname, dp->name, &info)) {
1281 fprintf(stderr, "Warning: no curinfo record for %s:%s\n",
1282 dp->host->hostname, dp->name);
1285 printf("host: %s\ndisk: %s\n", dp->host->hostname, dp->name);
1286 printf("command: %u\n", info.command);
1287 printf("last_level: %d\n",info.last_level);
1288 printf("consecutive_runs: %d\n",info.consecutive_runs);
1289 printf("full-rate:");
1290 for(i=0;i<AVG_COUNT;i++) printf(" %lf", info.full.rate[i]);
1291 printf("\nfull-comp:");
1292 for(i=0;i<AVG_COUNT;i++) printf(" %lf", info.full.comp[i]);
1294 printf("\nincr-rate:");
1295 for(i=0;i<AVG_COUNT;i++) printf(" %lf", info.incr.rate[i]);
1296 printf("\nincr-comp:");
1297 for(i=0;i<AVG_COUNT;i++) printf(" %lf", info.incr.comp[i]);
1299 for(l=0;l<DUMP_LEVELS;l++) {
1300 if(info.inf[l].date < (time_t)0 && info.inf[l].label[0] == '\0') continue;
1301 printf("stats: %d " OFF_T_FMT " " OFF_T_FMT " " TIME_T_FMT " " TIME_T_FMT " " OFF_T_FMT " %s\n", l,
1302 (OFF_T_FMT_TYPE)info.inf[l].size,
1303 (OFF_T_FMT_TYPE)info.inf[l].csize,
1304 (TIME_T_FMT_TYPE)info.inf[l].secs,
1305 (TIME_T_FMT_TYPE)info.inf[l].date,
1306 (OFF_T_FMT_TYPE)info.inf[l].filenum,
1309 for(l=0;info.history[l].level > -1;l++) {
1310 printf("history: %d " OFF_T_FMT " " OFF_T_FMT " " TIME_T_FMT "\n",
1311 info.history[l].level,
1312 (OFF_T_FMT_TYPE)info.history[l].size,
1313 (OFF_T_FMT_TYPE)info.history[l].csize,
1314 (TIME_T_FMT_TYPE)info.history[l].date);
1319 /* ----------------------------------------------- */
1321 int import_one(void);
1322 char *impget_line(void);
1340 (void)argc; /* Quiet unused parameter warning */
1341 (void)argv; /* Quiet unused parameter warning */
1343 /* process header line */
1345 if((line = agets(stdin)) == NULL) {
1346 fprintf(stderr, "%s: empty input.\n", get_pname());
1354 #define sc "CURINFO Version"
1355 if(strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) {
1361 skip_whitespace(s, ch);
1363 || sscanf(s - 1, "%d.%d.%d", &vers_maj, &vers_min, &vers_patch) != 3) {
1367 skip_integer(s, ch); /* skip over major */
1372 skip_integer(s, ch); /* skip over minor */
1377 skip_integer(s, ch); /* skip over patch */
1383 skip_non_whitespace(s, ch);
1387 skip_whitespace(s, ch); /* find the org keyword */
1389 if(ch == '\0' || strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) {
1397 skip_whitespace(s, ch); /* find the org string */
1404 newer = (vers_maj != VERSION_MAJOR)? vers_maj > VERSION_MAJOR :
1405 (vers_min != VERSION_MINOR)? vers_min > VERSION_MINOR :
1406 vers_patch > VERSION_PATCH;
1409 "%s: WARNING: input is from newer Amanda version: %d.%d.%d.\n",
1410 get_pname(), vers_maj, vers_min, vers_patch);
1413 if(strcmp(org, getconf_str(CNF_ORG)) != 0) {
1414 fprintf(stderr, "%s: WARNING: input is from different org: %s\n",
1427 /*@i@*/ amfree(line);
1428 fprintf(stderr, "%s: bad CURINFO header line in input: %s.\n",
1430 fprintf(stderr, " Was the input in \"amadmin export\" format?\n");
1442 time_t *onedate_p = &onedate;
1447 char *hostname = NULL;
1448 char *diskname = NULL;
1455 memset(&info, 0, SIZEOF(info_t));
1457 for(level = 0; level < DUMP_LEVELS; level++) {
1458 info.inf[level].date = (time_t)-1;
1461 /* get host: disk: command: lines */
1463 hostname = diskname = NULL;
1465 if((line = impget_line()) == NULL) return 0; /* nothing there */
1469 skip_whitespace(s, ch);
1471 if(ch == '\0' || strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) goto parse_err;
1475 skip_whitespace(s, ch);
1476 if(ch == '\0') goto parse_err;
1478 skip_non_whitespace(s, ch);
1480 hostname = stralloc(fp);
1483 skip_whitespace(s, ch);
1486 if((line = impget_line()) == NULL) goto shortfile_err;
1489 skip_whitespace(s, ch);
1492 if(strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) goto parse_err;
1496 skip_whitespace(s, ch);
1497 if(ch == '\0') goto parse_err;
1499 skip_non_whitespace(s, ch);
1501 diskname = stralloc(fp);
1505 if((line = impget_line()) == NULL) goto shortfile_err;
1506 if(sscanf(line, "command: %u", &info.command) != 1) goto parse_err;
1508 /* get last_level and consecutive_runs */
1511 if((line = impget_line()) == NULL) goto shortfile_err;
1512 rc = sscanf(line, "last_level: %d", &info.last_level);
1515 if((line = impget_line()) == NULL) goto shortfile_err;
1516 if(sscanf(line, "consecutive_runs: %d", &info.consecutive_runs) != 1) goto parse_err;
1518 if((line = impget_line()) == NULL) goto shortfile_err;
1521 /* get rate: and comp: lines for full dumps */
1523 rc = sscanf(line, "full-rate: %lf %lf %lf",
1524 &info.full.rate[0], &info.full.rate[1], &info.full.rate[2]);
1525 if(rc != 3) goto parse_err;
1528 if((line = impget_line()) == NULL) goto shortfile_err;
1529 rc = sscanf(line, "full-comp: %lf %lf %lf",
1530 &info.full.comp[0], &info.full.comp[1], &info.full.comp[2]);
1531 if(rc != 3) goto parse_err;
1533 /* get rate: and comp: lines for incr dumps */
1536 if((line = impget_line()) == NULL) goto shortfile_err;
1537 rc = sscanf(line, "incr-rate: %lf %lf %lf",
1538 &info.incr.rate[0], &info.incr.rate[1], &info.incr.rate[2]);
1539 if(rc != 3) goto parse_err;
1542 if((line = impget_line()) == NULL) goto shortfile_err;
1543 rc = sscanf(line, "incr-comp: %lf %lf %lf",
1544 &info.incr.comp[0], &info.incr.comp[1], &info.incr.comp[2]);
1545 if(rc != 3) goto parse_err;
1547 /* get stats for dump levels */
1551 if((line = impget_line()) == NULL) goto shortfile_err;
1552 if(strncmp(line, "//", 2) == 0) {
1556 if(strncmp(line, "history:", 8) == 0) {
1560 memset(&onestat, 0, SIZEOF(onestat));
1565 skip_whitespace(s, ch);
1567 if(ch == '\0' || strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) {
1574 skip_whitespace(s, ch);
1575 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1578 skip_integer(s, ch);
1580 skip_whitespace(s, ch);
1581 if(ch == '\0' || sscanf(s - 1, OFF_T_FMT,
1582 (OFF_T_FMT_TYPE *)&onestat.size) != 1) {
1585 skip_integer(s, ch);
1587 skip_whitespace(s, ch);
1588 if(ch == '\0' || sscanf(s - 1, OFF_T_FMT,
1589 (OFF_T_FMT_TYPE *)&onestat.csize) != 1) {
1592 skip_integer(s, ch);
1594 skip_whitespace(s, ch);
1595 secs_p = &onestat.secs;
1596 if(ch == '\0' || sscanf(s - 1, TIME_T_FMT,
1597 (TIME_T_FMT_TYPE *)secs_p) != 1) {
1600 skip_integer(s, ch);
1602 skip_whitespace(s, ch);
1603 if(ch == '\0' || sscanf(s - 1, TIME_T_FMT,
1604 (TIME_T_FMT_TYPE *)onedate_p) != 1) {
1607 skip_integer(s, ch);
1609 skip_whitespace(s, ch);
1611 if(sscanf(s - 1, OFF_T_FMT, (OFF_T_FMT_TYPE *)&onestat.filenum) != 1) {
1614 skip_integer(s, ch);
1616 skip_whitespace(s, ch);
1618 if (onestat.filenum != 0)
1620 onestat.label[0] = '\0';
1622 strncpy(onestat.label, s - 1, SIZEOF(onestat.label)-1);
1623 onestat.label[SIZEOF(onestat.label)-1] = '\0';
1627 /* time_t not guarranteed to be long */
1628 /*@i1@*/ onestat.date = onedate;
1629 if(level < 0 || level > 9) goto parse_err;
1631 info.inf[level] = onestat;
1634 for(i=0;i<=NB_HISTORY;i++) {
1635 info.history[i].level = -2;
1638 history_t onehistory;
1640 time_t *date_p = &date;
1642 if(line[0] == '/' && line[1] == '/') {
1643 info.history[nb_history].level = -2;
1647 memset(&onehistory, 0, SIZEOF(onehistory));
1650 #define sc "history:"
1651 if(strncmp(line, sc, SIZEOF(sc)-1) != 0) {
1657 skip_whitespace(s, ch);
1658 if(ch == '\0' || sscanf((s - 1), "%d", &onehistory.level) != 1) {
1661 skip_integer(s, ch);
1663 skip_whitespace(s, ch);
1664 if(ch == '\0' || sscanf((s - 1), OFF_T_FMT,
1665 (OFF_T_FMT_TYPE *)&onehistory.size) != 1) {
1668 skip_integer(s, ch);
1670 skip_whitespace(s, ch);
1671 if(ch == '\0' || sscanf((s - 1), OFF_T_FMT,
1672 (OFF_T_FMT_TYPE *)&onehistory.csize) != 1) {
1675 skip_integer(s, ch);
1677 skip_whitespace(s, ch);
1678 if((ch == '\0') || (sscanf((s - 1), TIME_T_FMT,
1679 (TIME_T_FMT_TYPE *)date_p) != 1)) {
1682 skip_integer(s, ch);
1683 /*@i1@*/onehistory.date = date; /* time_t not guarranteed to be long */
1685 info.history[nb_history++] = onehistory;
1687 if((line = impget_line()) == NULL) goto shortfile_err;
1689 /*@i@*/ amfree(line);
1691 /* got a full record, now write it out to the database */
1693 if(put_info(hostname, diskname, &info)) {
1694 fprintf(stderr, "%s: error writing record for %s:%s\n",
1695 get_pname(), hostname, diskname);
1702 /*@i@*/ amfree(line);
1705 fprintf(stderr, "%s: parse error reading import record.\n", get_pname());
1709 /*@i@*/ amfree(line);
1712 fprintf(stderr, "%s: short file reading import record.\n", get_pname());
1723 for(; (line = agets(stdin)) != NULL; free(line)) {
1727 skip_whitespace(s, ch);
1729 /* ignore comment lines */
1732 /* found non-blank, return line */
1735 /* otherwise, a blank line, so keep going */
1738 fprintf(stderr, "%s: reading stdin: %s\n",
1739 get_pname(), strerror(errno));
1744 /* ----------------------------------------------- */
1759 printf("line %d:\n", dp->line);
1761 printf(" host %s:\n", hp->hostname);
1762 printf(" interface %s\n",
1763 ip->name[0] ? ip->name : "default");
1764 printf(" disk %s:\n", dp->name);
1765 if(dp->device) printf(" device %s\n", dp->device);
1767 printf(" program \"%s\"\n", dp->program);
1768 if(dp->exclude_file != NULL && dp->exclude_file->nb_element > 0) {
1769 printf(" exclude file");
1770 for(excl = dp->exclude_file->first; excl != NULL; excl = excl->next) {
1771 printf(" \"%s\"", excl->name);
1775 if(dp->exclude_list != NULL && dp->exclude_list->nb_element > 0) {
1776 printf(" exclude list");
1777 if(dp->exclude_optional) printf(" optional");
1778 for(excl = dp->exclude_list->first; excl != NULL; excl = excl->next) {
1779 printf(" \"%s\"", excl->name);
1783 if(dp->include_file != NULL && dp->include_file->nb_element > 0) {
1784 printf(" include file");
1785 for(excl = dp->include_file->first; excl != NULL; excl = excl->next) {
1786 printf(" \"%s\"", excl->name);
1790 if(dp->include_list != NULL && dp->include_list->nb_element > 0) {
1791 printf(" include list");
1792 if(dp->include_optional) printf(" optional");
1793 for(excl = dp->include_list->first; excl != NULL; excl = excl->next) {
1794 printf(" \"%s\"", excl->name);
1798 printf(" priority %d\n", dp->priority);
1799 printf(" dumpcycle %d\n", dp->dumpcycle);
1800 printf(" maxdumps %d\n", dp->maxdumps);
1801 printf(" maxpromoteday %d\n", dp->maxpromoteday);
1802 if(dp->bumppercent > 0) {
1803 printf(" bumppercent %d\n", dp->bumppercent);
1806 printf(" bumpsize " OFF_T_FMT "\n",
1807 (OFF_T_FMT_TYPE)dp->bumpsize);
1809 printf(" bumpdays %d\n", dp->bumpdays);
1810 printf(" bumpmult %lf\n", dp->bumpmult);
1812 printf(" strategy ");
1813 switch(dp->strategy) {
1818 printf("STANDARD\n");
1830 printf("INCRONLY\n");
1834 printf(" estimate ");
1835 switch(dp->estimate) {
1843 printf("CALCSIZE\n");
1847 printf(" compress ");
1848 switch(dp->compress) {
1853 printf("CLIENT FAST\n");
1856 printf("CLIENT BEST\n");
1858 case COMP_SERV_FAST:
1859 printf("SERVER FAST\n");
1861 case COMP_SERV_BEST:
1862 printf("SERVER BEST\n");
1865 if(dp->compress != COMP_NONE) {
1866 printf(" comprate %.2lf %.2lf\n",
1867 dp->comprate[0], dp->comprate[1]);
1870 printf(" encrypt ");
1871 switch(dp->encrypt) {
1878 case ENCRYPT_SERV_CUST:
1883 printf(" auth %s\n", dp->security_driver);
1884 printf(" kencrypt %s\n", (dp->kencrypt? "YES" : "NO"));
1885 printf(" amandad_path %s\n", dp->amandad_path);
1886 printf(" client_username %s\n", dp->client_username);
1887 printf(" ssh_keys %s\n", dp->ssh_keys);
1889 printf(" holdingdisk ");
1890 switch(dp->to_holdingdisk) {
1898 printf("REQUIRED\n");
1902 printf(" record %s\n", (dp->record? "YES" : "NO"));
1903 printf(" index %s\n", (dp->index? "YES" : "NO"));
1906 stm = localtime(&st);
1908 printf(" starttime %d:%02d:%02d\n",
1909 stm->tm_hour, stm->tm_min, stm->tm_sec);
1911 printf(" starttime BAD DATE\n");
1914 if(dp->tape_splitsize > (off_t)0) {
1915 printf(" tape_splitsize " OFF_T_FMT "\n",
1916 (OFF_T_FMT_TYPE)dp->tape_splitsize);
1918 if(dp->split_diskbuffer) {
1919 printf(" split_diskbuffer %s\n", dp->split_diskbuffer);
1921 if(dp->fallback_splitsize > (off_t)0) {
1922 printf(" fallback_splitsize " OFF_T_FMT "Mb\n",
1923 (OFF_T_FMT_TYPE)(dp->fallback_splitsize / (off_t)1024));
1925 printf(" skip-incr %s\n", (dp->skip_incr? "YES" : "NO"));
1926 printf(" skip-full %s\n", (dp->skip_full? "YES" : "NO"));
1927 printf(" spindle %d\n", dp->spindle);
1940 diskloop(argc, argv, "disklist", disklist_one);
1942 for(dp = diskq.head; dp != NULL; dp = dp->next)
1953 (void)argc; /* Quiet unused parameter warning */
1954 (void)argv; /* Quiet unused parameter warning */
1956 for(i = 0; version_info[i] != NULL; i++)
1957 printf("%s", version_info[i]);
1967 dump_configuration(conffile);