1 /*
2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
4 * Copyright (c) 2007-2013 Zmanda, Inc. All Rights Reserved.
5 * All Rights Reserved.
6 *
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation, and that the name of U.M. not be used in advertising or
12 * publicity pertaining to distribution of the software without specific,
13 * written prior permission. U.M. makes no representations about the
14 * suitability of this software for any purpose. It is provided "as is"
15 * without express or implied warranty.
16 *
17 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 *
24 * Author: James da Silva, Systems Design and Analysis Group
25 * Computer Science Department
26 * University of Maryland at College Park
27 */
28 /*
29 * $Id: amadmin.c,v 1.124 2006/07/26 15:17:37 martinea Exp $
30 *
31 * controlling process for the Amanda backup system
32 */
33 #include "amanda.h"
34 #include "cmdline.h"
35 #include "conffile.h"
36 #include "diskfile.h"
37 #include "tapefile.h"
38 #include "infofile.h"
39 #include "logfile.h"
40 #include "version.h"
41 #include "holding.h"
42 #include "find.h"
43 #include "util.h"
44 #include "timestamp.h"
45 #include "server_util.h"
46 #include <getopt.h>
47
48 disklist_t diskq;
49
50 int main(int argc, char **argv);
51 void usage(void);
52 static void estimate(int argc, char **argv);
53 static void estimate_one(disk_t *dp);
54 void force(int argc, char **argv);
55 void force_one(disk_t *dp);
56 void force_level_1(int argc, char **argv);
57 void force_level_1_one(disk_t *dp);
58 void unforce(int argc, char **argv);
59 void unforce_one(disk_t *dp);
60 void force_bump(int argc, char **argv);
61 void force_bump_one(disk_t *dp);
62 void force_no_bump(int argc, char **argv);
63 void force_no_bump_one(disk_t *dp);
64 void unforce_bump(int argc, char **argv);
65 void unforce_bump_one(disk_t *dp);
66 void reuse(int argc, char **argv);
67 void noreuse(int argc, char **argv);
68 void info(int argc, char **argv);
69 void info_one(disk_t *dp);
70 void due(int argc, char **argv);
71 void due_one(disk_t *dp);
72 void find(int argc, char **argv);
73 void holding(int argc, char **argv);
74 void delete(int argc, char **argv);
75 void delete_one(disk_t *dp);
76 void balance(int argc, char **argv);
77 void tape(int argc, char **argv);
78 void bumpsize(int argc, char **argv);
79 void diskloop(int argc, char **argv, char *cmdname, void (*func)(disk_t *dp));
80 char *seqdatestr(int seq);
81 static int next_level0(disk_t *dp, info_t *info);
82 int bump_thresh(int level);
83 void export_db(int argc, char **argv);
84 void import_db(int argc, char **argv);
85 void hosts(int argc, char **argv);
86 void dles(int argc, char **argv);
87 void disklist(int argc, char **argv);
88 void disklist_one(disk_t *dp);
89 void show_version(int argc, char **argv);
90 static void show_config(int argc, char **argv);
91
92 static char *conf_tapelist = NULL;
93 static char *displayunit;
94 static long int unitdivisor;
95 static gboolean print_default = 1;
96 static gboolean print_source = 0;
97 static int opt_days = -1;
98 static char *opt_sort = NULL;
99 static gboolean exact_match = FALSE;
100 static gboolean opt_long = 0;
101 static gboolean opt_outdated = 0;
102
103 static const struct {
104 const char *name;
105 void (*fn)(int, char **);
106 const char *usage;
107 } cmdtab[] = {
108 { "version", show_version,
109 T_("\t\t\t\t\t# Show version info.") },
110 { "config", show_config,
111 T_("\t\t\t\t\t# Show configuration.") },
112 { "estimate", estimate,
113 T_(" [<hostname> [<disks>]* ]*\t# Print server estimate.") },
114 { "force", force,
115 T_(" [<hostname> [<disks>]* ]+\t\t# Force level 0 at next run.") },
116 { "force-level-1", force_level_1,
117 T_(" [<hostname> [<disks>]* ]+\t\t# Force level 1 at next run.") },
118 { "unforce", unforce,
119 T_(" [<hostname> [<disks>]* ]+\t# Clear force command.") },
120 { "force-bump", force_bump,
121 T_(" [<hostname> [<disks>]* ]+\t# Force bump at next run.") },
122 { "force-no-bump", force_no_bump,
123 T_(" [<hostname> [<disks>]* ]+\t# Force no-bump at next run.") },
124 { "unforce-bump", unforce_bump,
125 T_(" [<hostname> [<disks>]* ]+\t# Clear bump command.") },
126 { "disklist", disklist,
127 T_(" [<hostname> [<disks>]* ]*\t# Debug disklist entries.") },
128 { "hosts", hosts,
129 T_("\t\t\t\t\t# Show all distinct hosts in disklist.") },
130 { "dles", dles,
131 T_("\t\t\t\t\t# Show all dles in disklist, one per line.") },
132 { "reuse", reuse,
133 T_(" <tapelabel> ...\t\t # re-use this tape.") },
134 { "no-reuse", noreuse,
135 T_(" <tapelabel> ...\t # never re-use this tape.") },
136 { "find", find,
137 T_(" [<hostname> [<disks>]* ]*\t # Show which tapes these dumps are on.") },
138 { "holding", holding,
139 T_(" {list [ -l ] |delete} [ <hostname> [ <disk> [ <datestamp> [ .. ] ] ] ]+\t # Show or delete holding disk contents.") },
140 { "delete", delete,
141 T_(" [<hostname> [<disks>]* ]+ # Delete from database.") },
142 { "info", info,
143 T_(" [<hostname> [<disks>]* ]*\t # Show current info records.") },
144 { "due", due,
145 T_(" [<hostname> [<disks>]* ]*\t # Show due date.") },
146 { "balance", balance,
147 T_(" [--days <num>]\t\t # Show nightly dump size balance.") },
148 { "tape", tape,
149 T_(" [--days <num>]\t\t # Show which tape is due next.") },
150 { "bumpsize", bumpsize,
151 T_("\t\t\t # Show current bump thresholds.") },
152 { "export", export_db,
153 T_(" [<hostname> [<disks>]* ]* # Export curinfo database to stdout.") },
154 { "import", import_db,
155 T_("\t\t\t\t # Import curinfo database from stdin.") },
156 };
157 #define NCMDS (int)(sizeof(cmdtab) / sizeof(cmdtab[0]))
158
159 static struct option long_options[] = {
160 {"version" , 0, NULL, 1},
161 {"no-default" , 0, NULL, 2},
162 {"print-source" , 0, NULL, 3},
163 {"days" , 1, NULL, 4},
164 {"sort" , 1, NULL, 5},
165 {"exact-match" , 0, NULL, 6},
166 {NULL, 0, NULL, 0}
167 };
168
169 int
main(int argc,char ** argv)170 main(
171 int argc,
172 char ** argv)
173 {
174 int i;
175 char *conf_diskfile;
176 char *conf_infofile;
177 config_overrides_t *cfg_ovr = NULL;
178
179 /*
180 * Configure program for internationalization:
181 * 1) Only set the message locale for now.
182 * 2) Set textdomain for all amanda related programs to "amanda"
183 * We don't want to be forced to support dozens of message catalogs.
184 */
185 setlocale(LC_MESSAGES, "C");
186 textdomain("amanda");
187
188 safe_fd(-1, 0);
189 safe_cd();
190
191 set_pname("amadmin");
192
193 /* Don't die when child closes pipe */
194 signal(SIGPIPE, SIG_IGN);
195
196 dbopen(DBG_SUBDIR_SERVER);
197
198 add_amanda_log_handler(amanda_log_stderr);
199
200 cfg_ovr = extract_commandline_config_overrides(&argc, &argv);
201
202 while (1) {
203 int option_index = 0;
204 int c;
205 c = getopt_long(argc, argv, "ld", long_options, &option_index);
206
207 if (c == -1) {
208 break;
209 }
210
211 switch(c) {
212 case 1: printf("amadmin-%s\n", VERSION);
213 return 0;
214 case 2: print_default = 0;
215 break;
216 case 3: print_source = 1;
217 break;
218 case 4: opt_days = atoi(optarg);
219 break;
220 case 5: opt_sort = g_strdup(optarg);
221 break;
222 case 6: exact_match = TRUE;
223 break;
224 case 'l': opt_long = TRUE;
225 break;
226 case 'd': opt_outdated = TRUE;
227 break;
228 default: usage();
229 }
230 }
231 argc -= optind-1, argv += optind-1;
232
233 if(argc < 3) usage();
234
235 set_config_overrides(cfg_ovr);
236
237 if(strcmp(argv[2],"version") == 0) {
238 config_init(0, NULL);
239 show_version(argc, argv);
240 goto done;
241 }
242
243 config_init(CONFIG_INIT_EXPLICIT_NAME, argv[1]);
244
245 conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
246 read_diskfile(conf_diskfile, &diskq);
247 amfree(conf_diskfile);
248
249 if (config_errors(NULL) >= CFGERR_WARNINGS) {
250 config_print_errors();
251 if (config_errors(NULL) >= CFGERR_ERRORS) {
252 g_critical(_("errors processing config file"));
253 }
254 }
255
256 dbrename(get_config_name(), DBG_SUBDIR_SERVER);
257
258 check_running_as(RUNNING_AS_DUMPUSER);
259
260 conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
261 if(read_tapelist(conf_tapelist)) {
262 error(_("could not load tapelist \"%s\""), conf_tapelist);
263 /*NOTREACHED*/
264 }
265 /* conf_tapelist is not freed yet -- it may be used to write the
266 * tapelist later. */
267
268 conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
269 if(open_infofile(conf_infofile)) {
270 error(_("could not open info db \"%s\""), conf_infofile);
271 /*NOTREACHED*/
272 }
273 amfree(conf_infofile);
274
275 displayunit = getconf_str(CNF_DISPLAYUNIT);
276 unitdivisor = getconf_unit_divisor();
277
278 for (i = 0; i < NCMDS; i++)
279 if (strcmp(argv[2], cmdtab[i].name) == 0) {
280 (*cmdtab[i].fn)(argc, argv);
281 break;
282 }
283 if (i == NCMDS) {
284 g_fprintf(stderr, _("%s: unknown command \"%s\"\n"), argv[0], argv[2]);
285 usage();
286 }
287
288 close_infofile();
289 clear_tapelist();
290 amfree(conf_tapelist);
291
292 done:
293
294 unload_disklist();
295 diskq.head = NULL;
296 diskq.tail = NULL;
297 dbclose();
298 return 0;
299 }
300
301
302 void
usage(void)303 usage(void)
304 {
305 int i;
306
307 g_fprintf(stderr, _("\nUsage: %s [--version] [--exact-match] [--no-default] [--print-source] [-o configoption]*\n <conf> <command> {<args>} ...\n"),
308 get_pname());
309 g_fprintf(stderr, _(" Valid <command>s are:\n"));
310 for (i = 0; i < NCMDS; i++)
311 g_fprintf(stderr, "\t%s%s\n", cmdtab[i].name, _(cmdtab[i].usage));
312 exit(1);
313 }
314
315
316 /* ----------------------------------------------- */
317
318 #define SECS_PER_DAY (24*60*60)
319 time_t today;
320
321 char *
seqdatestr(int seq)322 seqdatestr(
323 int seq)
324 {
325 static char str[16];
326 static char *dow[7] = {
327 T_("Sun"),
328 T_("Mon"),
329 T_("Tue"),
330 T_("Wed"),
331 T_("Thu"),
332 T_("Fri"),
333 T_("Sat")
334 };
335 time_t t = today + seq*SECS_PER_DAY;
336 struct tm *tm;
337
338 tm = localtime(&t);
339
340 if (tm)
341 g_snprintf(str, SIZEOF(str),
342 "%2d/%02d %3s", tm->tm_mon+1, tm->tm_mday, _(dow[tm->tm_wday]));
343 else
344 strcpy(str, _("BAD DATE"));
345
346 return str;
347 }
348
349 #undef days_diff
350 #define days_diff(a, b) (int)(((b) - (a) + SECS_PER_DAY) / SECS_PER_DAY)
351
352 /* when is next level 0 due? 0 = tonight, 1 = tommorrow, etc*/
353 static int
next_level0(disk_t * dp,info_t * info)354 next_level0(
355 disk_t * dp,
356 info_t * info)
357 {
358 if(dp->strategy == DS_NOFULL)
359 return 1; /* fake it */
360 if(info->inf[0].date < (time_t)0)
361 return 0; /* new disk */
362 else
363 return dp->dumpcycle - days_diff(info->inf[0].date, today);
364 }
365
366 /* ----------------------------------------------- */
367
368 void
diskloop(int argc,char ** argv,char * cmdname,void (* func)(disk_t * dp))369 diskloop(
370 int argc,
371 char ** argv,
372 char * cmdname,
373 void (*func)(disk_t *dp))
374 {
375 disk_t *dp;
376 int count = 0;
377 char *errstr;
378
379 if(argc < 4) {
380 g_fprintf(stderr,_("%s: expecting \"%s [<hostname> [<disks>]* ]+\"\n"),
381 get_pname(), cmdname);
382 usage();
383 }
384
385 errstr = match_disklist(&diskq, exact_match, argc-3, argv+3);
386 if (errstr) {
387 g_printf("%s", errstr);
388 amfree(errstr);
389 }
390
391 for(dp = diskq.head; dp != NULL; dp = dp->next) {
392 if(dp->todo) {
393 count++;
394 func(dp);
395 }
396 }
397 if(count==0) {
398 g_fprintf(stderr,_("%s: no disk matched\n"),get_pname());
399 }
400 }
401
402 /* ----------------------------------------------- */
403
404
405 static void
estimate_one(disk_t * dp)406 estimate_one(
407 disk_t * dp)
408 {
409 char *hostname = dp->host->hostname;
410 char *diskname = dp->name;
411 char *qhost = quote_string(hostname);
412 char *qdisk = quote_string(diskname);
413 info_t info;
414 int stats;
415 gint64 size;
416
417 get_info(hostname, diskname, &info);
418
419 size = internal_server_estimate(dp, &info, 0, &stats);
420 if (stats) {
421 printf("%s %s %d %jd\n", qhost, qdisk, 0, (intmax_t)size);
422 }
423
424 if (info.last_level > 0) {
425 size = internal_server_estimate(dp, &info, info.last_level, &stats);
426 if (stats) {
427 printf("%s %s %d %jd\n", qhost, qdisk, info.last_level,
428 (intmax_t)size);
429 }
430 }
431
432 if (info.last_level > -1) {
433 size = internal_server_estimate(dp, &info, info.last_level+1, &stats);
434 if (stats) {
435 printf("%s %s %d %jd\n", qhost, qdisk, info.last_level+1,
436 (intmax_t)size);
437 }
438 }
439
440 amfree(qhost);
441 amfree(qdisk);
442 }
443
444
445 static void
estimate(int argc,char ** argv)446 estimate(
447 int argc,
448 char ** argv)
449 {
450 disk_t *dp;
451
452 if(argc >= 4)
453 diskloop(argc, argv, "estimate", estimate_one);
454 else
455 for(dp = diskq.head; dp != NULL; dp = dp->next)
456 estimate_one(dp);
457 }
458
459
460 /* ----------------------------------------------- */
461
462
463 void
force_one(disk_t * dp)464 force_one(
465 disk_t * dp)
466 {
467 char *hostname = dp->host->hostname;
468 char *diskname = dp->name;
469 info_t info;
470
471 get_info(hostname, diskname, &info);
472 SET(info.command, FORCE_FULL);
473 if (ISSET(info.command, FORCE_LEVEL_1)) {
474 CLR(info.command, FORCE_LEVEL_1);
475 g_printf(_("%s: WARNING: %s:%s FORCE-LEVEL-1 command was cleared.\n"),
476 get_pname(), hostname, diskname);
477 }
478 if (ISSET(info.command, FORCE_BUMP)) {
479 CLR(info.command, FORCE_BUMP);
480 g_printf(_("%s: WARNING: %s:%s FORCE-BUMP command was cleared.\n"),
481 get_pname(), hostname, diskname);
482 }
483 if(put_info(hostname, diskname, &info) == 0) {
484 if (dp->strategy == DS_INCRONLY) {
485 g_printf(_("%s: %s:%s, full dump done offline, next dump will be at level 1.\n"),
486 get_pname(), hostname, diskname);
487 } else {
488 g_printf(_("%s: %s:%s is set to a forced level 0 at next run.\n"),
489 get_pname(), hostname, diskname);
490 }
491 } else {
492 g_fprintf(stderr, _("%s: %s:%s could not be forced.\n"),
493 get_pname(), hostname, diskname);
494 }
495 }
496
497
498 void
force(int argc,char ** argv)499 force(
500 int argc,
501 char ** argv)
502 {
503 diskloop(argc, argv, "force", force_one);
504 }
505
506
507 /* ----------------------------------------------- */
508
509
510 void
force_level_1_one(disk_t * dp)511 force_level_1_one(
512 disk_t * dp)
513 {
514 char *hostname = dp->host->hostname;
515 char *diskname = dp->name;
516 info_t info;
517
518 get_info(hostname, diskname, &info);
519 SET(info.command, FORCE_LEVEL_1);
520 if (ISSET(info.command, FORCE_FULL)) {
521 CLR(info.command, FORCE_FULL);
522 g_printf(_("%s: WARNING: %s:%s FORCE command was cleared.\n"),
523 get_pname(), hostname, diskname);
524 }
525 if (ISSET(info.command, FORCE_BUMP)) {
526 CLR(info.command, FORCE_BUMP);
527 g_printf(_("%s: WARNING: %s:%s FORCE-BUMP command was cleared.\n"),
528 get_pname(), hostname, diskname);
529 }
530 if(put_info(hostname, diskname, &info) == 0) {
531 g_printf(_("%s: %s:%s is set to a forced level 1 at next run.\n"),
532 get_pname(), hostname, diskname);
533 } else {
534 g_fprintf(stderr, _("%s: %s:%s could not be forced.\n"),
535 get_pname(), hostname, diskname);
536 }
537 }
538
539
540 void
force_level_1(int argc,char ** argv)541 force_level_1(
542 int argc,
543 char ** argv)
544 {
545 diskloop(argc, argv, "force", force_level_1_one);
546 }
547
548
549 /* ----------------------------------------------- */
550
551
552 void
unforce_one(disk_t * dp)553 unforce_one(
554 disk_t * dp)
555 {
556 char *hostname = dp->host->hostname;
557 char *diskname = dp->name;
558 info_t info;
559
560 get_info(hostname, diskname, &info);
561 if (ISSET(info.command, FORCE_FULL)) {
562 CLR(info.command, FORCE_FULL);
563 if(put_info(hostname, diskname, &info) == 0){
564 g_printf(_("%s: force command for %s:%s cleared.\n"),
565 get_pname(), hostname, diskname);
566 } else {
567 g_fprintf(stderr,
568 _("%s: force command for %s:%s could not be cleared.\n"),
569 get_pname(), hostname, diskname);
570 }
571 } else if (ISSET(info.command, FORCE_LEVEL_1)) {
572 CLR(info.command, FORCE_LEVEL_1);
573 if(put_info(hostname, diskname, &info) == 0){
574 g_printf(_("%s: force-level-1 command for %s:%s cleared.\n"),
575 get_pname(), hostname, diskname);
576 } else {
577 g_fprintf(stderr,
578 _("%s: force-level-1 command for %s:%s could not be cleared.\n"),
579 get_pname(), hostname, diskname);
580 }
581 }
582 else {
583 g_printf(_("%s: no force command outstanding for %s:%s, unchanged.\n"),
584 get_pname(), hostname, diskname);
585 }
586 }
587
588 void
unforce(int argc,char ** argv)589 unforce(
590 int argc,
591 char ** argv)
592 {
593 diskloop(argc, argv, "unforce", unforce_one);
594 }
595
596
597 /* ----------------------------------------------- */
598
599
600 void
force_bump_one(disk_t * dp)601 force_bump_one(
602 disk_t * dp)
603 {
604 char *hostname = dp->host->hostname;
605 char *diskname = dp->name;
606 info_t info;
607
608 get_info(hostname, diskname, &info);
609 SET(info.command, FORCE_BUMP);
610 if (ISSET(info.command, FORCE_NO_BUMP)) {
611 CLR(info.command, FORCE_NO_BUMP);
612 g_printf(_("%s: WARNING: %s:%s FORCE-NO-BUMP command was cleared.\n"),
613 get_pname(), hostname, diskname);
614 }
615 if (ISSET(info.command, FORCE_FULL)) {
616 CLR(info.command, FORCE_FULL);
617 g_printf(_("%s: WARNING: %s:%s FORCE command was cleared.\n"),
618 get_pname(), hostname, diskname);
619 }
620 if (ISSET(info.command, FORCE_LEVEL_1)) {
621 CLR(info.command, FORCE_LEVEL_1);
622 g_printf(_("%s: WARNING: %s:%s FORCE-LEVEL-1 command was cleared.\n"),
623 get_pname(), hostname, diskname);
624 }
625 if(put_info(hostname, diskname, &info) == 0) {
626 g_printf(_("%s: %s:%s is set to bump at next run.\n"),
627 get_pname(), hostname, diskname);
628 } else {
629 g_fprintf(stderr, _("%s: %s:%s could not be forced to bump.\n"),
630 get_pname(), hostname, diskname);
631 }
632 }
633
634
635 void
force_bump(int argc,char ** argv)636 force_bump(
637 int argc,
638 char ** argv)
639 {
640 diskloop(argc, argv, "force-bump", force_bump_one);
641 }
642
643
644 /* ----------------------------------------------- */
645
646
647 void
force_no_bump_one(disk_t * dp)648 force_no_bump_one(
649 disk_t * dp)
650 {
651 char *hostname = dp->host->hostname;
652 char *diskname = dp->name;
653 info_t info;
654
655 get_info(hostname, diskname, &info);
656 SET(info.command, FORCE_NO_BUMP);
657 if (ISSET(info.command, FORCE_BUMP)) {
658 CLR(info.command, FORCE_BUMP);
659 g_printf(_("%s: WARNING: %s:%s FORCE-BUMP command was cleared.\n"),
660 get_pname(), hostname, diskname);
661 }
662 if(put_info(hostname, diskname, &info) == 0) {
663 g_printf(_("%s: %s:%s is set to not bump at next run.\n"),
664 get_pname(), hostname, diskname);
665 } else {
666 g_fprintf(stderr, _("%s: %s:%s could not be force to not bump.\n"),
667 get_pname(), hostname, diskname);
668 }
669 }
670
671
672 void
force_no_bump(int argc,char ** argv)673 force_no_bump(
674 int argc,
675 char ** argv)
676 {
677 diskloop(argc, argv, "force-no-bump", force_no_bump_one);
678 }
679
680
681 /* ----------------------------------------------- */
682
683
684 void
unforce_bump_one(disk_t * dp)685 unforce_bump_one(
686 disk_t * dp)
687 {
688 char *hostname = dp->host->hostname;
689 char *diskname = dp->name;
690 info_t info;
691
692 get_info(hostname, diskname, &info);
693 if (ISSET(info.command, FORCE_BUMP|FORCE_NO_BUMP)) {
694 CLR(info.command, FORCE_BUMP|FORCE_NO_BUMP);
695 if(put_info(hostname, diskname, &info) == 0) {
696 g_printf(_("%s: bump command for %s:%s cleared.\n"),
697 get_pname(), hostname, diskname);
698 } else {
699 g_fprintf(stderr, _("%s: %s:%s bump command could not be cleared.\n"),
700 get_pname(), hostname, diskname);
701 }
702 }
703 else {
704 g_printf(_("%s: no bump command outstanding for %s:%s, unchanged.\n"),
705 get_pname(), hostname, diskname);
706 }
707 }
708
709
710 void
unforce_bump(int argc,char ** argv)711 unforce_bump(
712 int argc,
713 char ** argv)
714 {
715 diskloop(argc, argv, "unforce-bump", unforce_bump_one);
716 }
717
718
719 /* ----------------------------------------------- */
720
721 void
reuse(int argc,char ** argv)722 reuse(
723 int argc,
724 char ** argv)
725 {
726 tape_t *tp;
727 int count;
728
729 if(argc < 4) {
730 g_fprintf(stderr,_("%s: expecting \"reuse <tapelabel> ...\"\n"),
731 get_pname());
732 usage();
733 }
734
735 for(count=3; count< argc; count++) {
736 tp = lookup_tapelabel(argv[count]);
737 if ( tp == NULL) {
738 g_fprintf(stderr, _("reuse: tape label %s not found in tapelist.\n"),
739 argv[count]);
740 continue;
741 }
742 if( tp->reuse == 0 ) {
743 tp->reuse = 1;
744 g_printf(_("%s: marking tape %s as reusable.\n"),
745 get_pname(), argv[count]);
746 } else {
747 g_fprintf(stderr, _("%s: tape %s already reusable.\n"),
748 get_pname(), argv[count]);
749 }
750 }
751
752 if(write_tapelist(conf_tapelist)) {
753 error(_("could not write tapelist \"%s\""), conf_tapelist);
754 /*NOTREACHED*/
755 }
756 }
757
758 void
noreuse(int argc,char ** argv)759 noreuse(
760 int argc,
761 char ** argv)
762 {
763 tape_t *tp;
764 int count;
765
766 if(argc < 4) {
767 g_fprintf(stderr,_("%s: expecting \"no-reuse <tapelabel> ...\"\n"),
768 get_pname());
769 usage();
770 }
771
772 for(count=3; count< argc; count++) {
773 tp = lookup_tapelabel(argv[count]);
774 if ( tp == NULL) {
775 g_fprintf(stderr, _("no-reuse: tape label %s not found in tapelist.\n"),
776 argv[count]);
777 continue;
778 }
779 if( tp->reuse == 1 ) {
780 tp->reuse = 0;
781 g_printf(_("%s: marking tape %s as not reusable.\n"),
782 get_pname(), argv[count]);
783 } else {
784 g_fprintf(stderr, _("%s: tape %s already not reusable.\n"),
785 get_pname(), argv[count]);
786 }
787 }
788
789 if(write_tapelist(conf_tapelist)) {
790 error(_("could not write tapelist \"%s\""), conf_tapelist);
791 /*NOTREACHED*/
792 }
793 }
794
795
796 /* ----------------------------------------------- */
797
798 static int deleted;
799
800 void
delete_one(disk_t * dp)801 delete_one(
802 disk_t * dp)
803 {
804 char *hostname = dp->host->hostname;
805 char *diskname = dp->name;
806 info_t info;
807
808 if(get_info(hostname, diskname, &info)) {
809 g_printf(_("%s: %s:%s NOT currently in database.\n"),
810 get_pname(), hostname, diskname);
811 return;
812 }
813
814 deleted++;
815 if(del_info(hostname, diskname)) {
816 error(_("couldn't delete %s:%s from database: %s"),
817 hostname, diskname, strerror(errno));
818 /*NOTREACHED*/
819 } else {
820 g_printf(_("%s: %s:%s deleted from curinfo database.\n"),
821 get_pname(), hostname, diskname);
822 }
823 }
824
825 void
delete(int argc,char ** argv)826 delete(
827 int argc,
828 char ** argv)
829 {
830 deleted = 0;
831 diskloop(argc, argv, "delete", delete_one);
832
833 if(deleted)
834 g_printf(
835 _("%s: NOTE: you'll have to remove these from the disklist yourself.\n"),
836 get_pname());
837 }
838
839 /* ----------------------------------------------- */
840
841 void
info_one(disk_t * dp)842 info_one(
843 disk_t * dp)
844 {
845 info_t info;
846 int lev;
847 struct tm *tm;
848 stats_t *sp;
849
850 get_info(dp->host->hostname, dp->name, &info);
851
852 g_printf(_("\nCurrent info for %s %s:\n"), dp->host->hostname, dp->name);
853 if (ISSET(info.command, FORCE_FULL))
854 g_printf(_(" (Forcing to level 0 dump at next run)\n"));
855 if (ISSET(info.command, FORCE_BUMP))
856 g_printf(_(" (Forcing bump at next run)\n"));
857 if (ISSET(info.command, FORCE_NO_BUMP))
858 g_printf(_(" (Forcing no-bump at next run)\n"));
859 g_printf(_(" Stats: dump rates (kps), Full: %5.1lf, %5.1lf, %5.1lf\n"),
860 info.full.rate[0], info.full.rate[1], info.full.rate[2]);
861 g_printf(_(" Incremental: %5.1lf, %5.1lf, %5.1lf\n"),
862 info.incr.rate[0], info.incr.rate[1], info.incr.rate[2]);
863 g_printf(_(" compressed size, Full: %5.1lf%%,%5.1lf%%,%5.1lf%%\n"),
864 info.full.comp[0]*100, info.full.comp[1]*100, info.full.comp[2]*100);
865 g_printf(_(" Incremental: %5.1lf%%,%5.1lf%%,%5.1lf%%\n"),
866 info.incr.comp[0]*100, info.incr.comp[1]*100, info.incr.comp[2]*100);
867
868 g_printf(_(" Dumps: lev datestmp tape file origK compK secs\n"));
869 for(lev = 0, sp = &info.inf[0]; lev < 9; lev++, sp++) {
870 if(sp->date < (time_t)0 && sp->label[0] == '\0') continue;
871 tm = localtime(&sp->date);
872 if (tm) {
873 g_printf(_(" %d %04d%02d%02d %-15s %lld %lld %lld %jd\n"),
874 lev, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
875 sp->label,
876 (long long)sp->filenum,
877 (long long)sp->size,
878 (long long)sp->csize,
879 (intmax_t)sp->secs);
880 } else {
881 g_printf(_(" %d BAD-DATE %-15s %lld %lld %lld %jd\n"),
882 lev,
883 sp->label,
884 (long long)sp->filenum,
885 (long long)sp->size,
886 (long long)sp->csize,
887 (intmax_t)sp->secs);
888 }
889 }
890 }
891
892
893 void
info(int argc,char ** argv)894 info(
895 int argc,
896 char ** argv)
897 {
898 disk_t *dp;
899
900 if(argc >= 4)
901 diskloop(argc, argv, "info", info_one);
902 else
903 for(dp = diskq.head; dp != NULL; dp = dp->next)
904 info_one(dp);
905 }
906
907 /* ----------------------------------------------- */
908
909 void
due_one(disk_t * dp)910 due_one(
911 disk_t * dp)
912 {
913 am_host_t *hp;
914 int days;
915 info_t info;
916
917 hp = dp->host;
918 if(get_info(hp->hostname, dp->name, &info)) {
919 g_printf(_("new disk %s:%s ignored.\n"), hp->hostname, dp->name);
920 }
921 else {
922 days = next_level0(dp, &info);
923 if(days < 0) {
924 g_printf(_("Overdue %2d day%s %s:%s\n"),
925 -days, (-days == 1) ? ": " : "s:",
926 hp->hostname, dp->name);
927 }
928 else if(days == 0) {
929 g_printf(_("Due today: %s:%s\n"), hp->hostname, dp->name);
930 }
931 else {
932 g_printf(_("Due in %2d day%s %s:%s\n"), days,
933 (days == 1) ? ": " : "s:",
934 hp->hostname, dp->name);
935 }
936 }
937 }
938
939 void
due(int argc,char ** argv)940 due(
941 int argc,
942 char ** argv)
943 {
944 disk_t *dp;
945
946 time(&today);
947 if(argc >= 4)
948 diskloop(argc, argv, "due", due_one);
949 else
950 for(dp = diskq.head; dp != NULL; dp = dp->next)
951 due_one(dp);
952 }
953
954 /* ----------------------------------------------- */
955
956 void
tape(int argc G_GNUC_UNUSED,char ** argv G_GNUC_UNUSED)957 tape(
958 int argc G_GNUC_UNUSED,
959 char ** argv G_GNUC_UNUSED)
960 {
961 int nb_days = 1;
962 int runtapes;
963 tape_t *tp;
964 int i, j;
965 int skip;
966 int nb_new_tape;
967
968 nb_days = opt_days;
969 if (opt_days == 0 || opt_days == -1) {
970 nb_days = 1;
971 }
972 if (nb_days < 1) {
973 g_printf(_("days must be an integer bigger than 0\n"));
974 return;
975 }
976 if (nb_days > 10000)
977 nb_days = 10000;
978
979 if(argc > 4 && strcmp(argv[3],"--days") == 0) {
980 nb_days = atoi(argv[4]);
981 if(nb_days < 1) {
982 g_printf(_("days must be an integer bigger than 0\n"));
983 return;
984 }
985 if (nb_days > 10000)
986 nb_days = 10000;
987 }
988
989 runtapes = getconf_int(CNF_RUNTAPES);
990 tp = lookup_last_reusable_tape(0);
991 skip = 0;
992
993 for ( j=0 ; j < nb_days ; j++ ) {
994 nb_new_tape=0;
995 for ( i=0 ; i < runtapes ; i++ ) {
996 if(i==0)
997 g_fprintf(stdout, _("The next Amanda run should go onto "));
998 if(tp != NULL) {
999 if (nb_new_tape > 0) {
1000 if (nb_new_tape == 1)
1001 g_fprintf(stdout, _("1 new tape.\n"));
1002 else
1003 g_fprintf(stdout, _("%d new tapes.\n"), nb_new_tape);
1004 g_fprintf(stdout, " ");
1005 nb_new_tape = 0;
1006 }
1007 g_fprintf(stdout, _("tape %s or a new tape.\n"), tp->label);
1008 if (i < runtapes-1)
1009 g_fprintf(stdout, " ");
1010 } else {
1011 nb_new_tape++;
1012 }
1013 skip++;
1014
1015 tp = lookup_last_reusable_tape(skip);
1016 }
1017 if (nb_new_tape > 0) {
1018 if (nb_new_tape == 1)
1019 g_fprintf(stdout, _("1 new tape.\n"));
1020 else
1021 g_fprintf(stdout, _("%d new tapes.\n"), nb_new_tape);
1022 }
1023 }
1024
1025 print_new_tapes(stdout, nb_days * runtapes);
1026 }
1027
1028 /* ----------------------------------------------- */
1029
1030 void
balance(int argc G_GNUC_UNUSED,char ** argv G_GNUC_UNUSED)1031 balance(
1032 int argc G_GNUC_UNUSED,
1033 char ** argv G_GNUC_UNUSED)
1034 {
1035 disk_t *dp;
1036 struct balance_stats {
1037 int disks;
1038 off_t origsize, outsize;
1039 } *sp;
1040 int conf_runspercycle, conf_dumpcycle;
1041 int seq, runs_per_cycle, overdue, max_overdue;
1042 int later, total, balance, distinct;
1043 double fseq, disk_dumpcycle;
1044 info_t info;
1045 off_t total_balanced, balanced;
1046 int empty_day;
1047
1048 time(&today);
1049 conf_dumpcycle = getconf_int(CNF_DUMPCYCLE);
1050 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
1051 later = conf_dumpcycle;
1052 overdue = 0;
1053 max_overdue = 0;
1054
1055 if (opt_days > 0) {
1056 later = opt_days;
1057 } else if (opt_days == 0) {
1058 later = conf_dumpcycle;
1059 }
1060 if(later > 10000) later = 10000;
1061
1062 if(conf_runspercycle == 0) {
1063 runs_per_cycle = conf_dumpcycle;
1064 } else if(conf_runspercycle == -1 ) {
1065 runs_per_cycle = guess_runs_from_tapelist();
1066 } else
1067 runs_per_cycle = conf_runspercycle;
1068
1069 if (runs_per_cycle <= 0) {
1070 runs_per_cycle = 1;
1071 }
1072
1073 total = later + 1;
1074 balance = later + 2;
1075 distinct = later + 3;
1076
1077 sp = (struct balance_stats *)
1078 alloc(SIZEOF(struct balance_stats) * (distinct+1));
1079
1080 for(seq=0; seq <= distinct; seq++) {
1081 sp[seq].disks = 0;
1082 sp[seq].origsize = sp[seq].outsize = (off_t)0;
1083 }
1084
1085 for(dp = diskq.head; dp != NULL; dp = dp->next) {
1086 if(get_info(dp->host->hostname, dp->name, &info)) {
1087 g_printf(_("new disk %s:%s ignored.\n"), dp->host->hostname, dp->name);
1088 continue;
1089 }
1090 if (dp->strategy == DS_NOFULL) {
1091 continue;
1092 }
1093 sp[distinct].disks++;
1094 sp[distinct].origsize += info.inf[0].size/(off_t)unitdivisor;
1095 sp[distinct].outsize += info.inf[0].csize/(off_t)unitdivisor;
1096
1097 sp[balance].disks++;
1098 if(dp->dumpcycle == 0) {
1099 sp[balance].origsize += (info.inf[0].size/(off_t)unitdivisor) * (off_t)runs_per_cycle;
1100 sp[balance].outsize += (info.inf[0].csize/(off_t)unitdivisor) * (off_t)runs_per_cycle;
1101 }
1102 else {
1103 sp[balance].origsize += (info.inf[0].size/(off_t)unitdivisor) *
1104 (off_t)(conf_dumpcycle / dp->dumpcycle);
1105 sp[balance].outsize += (info.inf[0].csize/(off_t)unitdivisor) *
1106 (off_t)(conf_dumpcycle / dp->dumpcycle);
1107 }
1108
1109 disk_dumpcycle = (double)dp->dumpcycle;
1110 if(dp->dumpcycle <= 0)
1111 disk_dumpcycle = ((double)conf_dumpcycle) / ((double)runs_per_cycle);
1112
1113 seq = next_level0(dp, &info);
1114 fseq = seq + 0.0001;
1115 do {
1116 if(seq < 0) {
1117 overdue++;
1118 if (-seq > max_overdue)
1119 max_overdue = -seq;
1120 seq = 0;
1121 fseq = seq + 0.0001;
1122 }
1123 if(seq > later) {
1124 seq = later;
1125 }
1126
1127 sp[seq].disks++;
1128 sp[seq].origsize += info.inf[0].size/(off_t)unitdivisor;
1129 sp[seq].outsize += info.inf[0].csize/(off_t)unitdivisor;
1130
1131 if(seq < later) {
1132 sp[total].disks++;
1133 sp[total].origsize += info.inf[0].size/(off_t)unitdivisor;
1134 sp[total].outsize += info.inf[0].csize/(off_t)unitdivisor;
1135 }
1136
1137 /* See, if there's another run in this dumpcycle */
1138 fseq += disk_dumpcycle;
1139 seq = (int)fseq;
1140 } while (seq < later);
1141 }
1142
1143 if(sp[total].outsize == (off_t)0 && sp[later].outsize == (off_t)0) {
1144 g_printf(_("\nNo data to report on yet.\n"));
1145 amfree(sp);
1146 return;
1147 }
1148
1149 balanced = sp[balance].outsize / (off_t)runs_per_cycle;
1150 if(conf_dumpcycle == later) {
1151 total_balanced = sp[total].outsize / (off_t)runs_per_cycle;
1152 }
1153 else {
1154 total_balanced = (((sp[total].outsize/(off_t)1024) * (off_t)conf_dumpcycle)
1155 / (off_t)(runs_per_cycle * later)) * (off_t)1024;
1156 }
1157
1158 empty_day = 0;
1159 g_printf(_("\n due-date #fs orig %cB out %cB balance\n"),
1160 displayunit[0], displayunit[0]);
1161 g_printf("----------------------------------------------\n");
1162 for(seq = 0; seq < later; seq++) {
1163 if(sp[seq].disks == 0 &&
1164 ((seq > 0 && sp[seq-1].disks == 0) ||
1165 ((seq < later-1) && sp[seq+1].disks == 0))) {
1166 empty_day++;
1167 }
1168 else {
1169 if(empty_day > 0) {
1170 g_printf("\n");
1171 empty_day = 0;
1172 }
1173 g_printf(_("%-9.9s %3d %10lld %10lld "),
1174 seqdatestr(seq), sp[seq].disks,
1175 (long long)sp[seq].origsize,
1176 (long long)sp[seq].outsize);
1177 if(!sp[seq].outsize) g_printf(" --- \n");
1178 else g_printf(_("%+8.1lf%%\n"),
1179 (((double)sp[seq].outsize - (double)balanced) * 100.0 /
1180 (double)balanced));
1181 }
1182 }
1183
1184 if(sp[later].disks != 0) {
1185 g_printf(_("later %3d %10lld %10lld "),
1186 sp[later].disks,
1187 (long long)sp[later].origsize,
1188 (long long)sp[later].outsize);
1189 if(!sp[later].outsize) g_printf(" --- \n");
1190 else g_printf(_("%+8.1lf%%\n"),
1191 (((double)sp[later].outsize - (double)balanced) * 100.0 /
1192 (double)balanced));
1193 }
1194 g_printf("----------------------------------------------\n");
1195 g_printf(_("TOTAL %3d %10lld %10lld %9lld\n"),
1196 sp[total].disks,
1197 (long long)sp[total].origsize,
1198 (long long)sp[total].outsize,
1199 (long long)total_balanced);
1200 if (sp[balance].origsize != sp[total].origsize ||
1201 sp[balance].outsize != sp[total].outsize ||
1202 balanced != total_balanced) {
1203 g_printf(_("BALANCED %10lld %10lld %9lld\n"),
1204 (long long)sp[balance].origsize,
1205 (long long)sp[balance].outsize,
1206 (long long)balanced);
1207 }
1208 if (sp[distinct].disks != sp[total].disks) {
1209 g_printf(_("DISTINCT %3d %10lld %10lld\n"),
1210 sp[distinct].disks,
1211 (long long)sp[distinct].origsize,
1212 (long long)sp[distinct].outsize);
1213 }
1214 g_printf(plural(_(" (estimated %d run per dumpcycle)\n"),
1215 _(" (estimated %d runs per dumpcycle)\n"),
1216 runs_per_cycle),
1217 runs_per_cycle);
1218 if (overdue) {
1219 g_printf(plural(_(" (%d filesystem overdue."),
1220 _(" (%d filesystems overdue."), overdue),
1221 overdue);
1222 g_printf(plural(_(" The most being overdue %d day.)\n"),
1223 _(" The most being overdue %d days.)\n"), max_overdue),
1224 max_overdue);
1225 }
1226 amfree(sp);
1227 }
1228
1229
1230 /* ----------------------------------------------- */
1231
1232 void
find(int argc,char ** argv)1233 find(
1234 int argc,
1235 char ** argv)
1236 {
1237 int start_argc;
1238 char *sort_order = NULL;
1239 find_result_t *output_find;
1240 char *errstr;
1241 char **output_find_log;
1242 char **name;
1243
1244 if(argc < 3) {
1245 g_fprintf(stderr,
1246 _("%s: expecting \"find [--sort <hkdlpbfw>] [hostname [<disk>]]*\"\n"),
1247 get_pname());
1248 usage();
1249 }
1250
1251
1252 sort_order = newstralloc(sort_order, DEFAULT_SORT_ORDER);
1253 if (opt_sort) {
1254 size_t i, valid_sort=1;
1255
1256 for(i = strlen(opt_sort); i > 0; i--) {
1257 switch (opt_sort[i - 1]) {
1258 case 'h':
1259 case 'H':
1260 case 'k':
1261 case 'K':
1262 case 'd':
1263 case 'D':
1264 case 'f':
1265 case 'F':
1266 case 'l':
1267 case 'L':
1268 case 'p':
1269 case 'P':
1270 case 'b':
1271 case 'B':
1272 case 'w':
1273 case 'W':
1274 break;
1275 default: valid_sort=0;
1276 }
1277 }
1278 if(valid_sort) {
1279 sort_order = newstralloc(sort_order, opt_sort);
1280 } else {
1281 g_printf(_("Invalid sort order: %s\n"), opt_sort);
1282 g_printf(_("Use default sort order: %s\n"), sort_order);
1283 }
1284 }
1285 start_argc=4;
1286 errstr = match_disklist(&diskq, exact_match, argc-(start_argc-1),
1287 argv+(start_argc-1));
1288
1289 /* check all log file exists */
1290 output_find_log = find_log();
1291 for (name = output_find_log; *name != NULL; name++) {
1292 amfree(*name);
1293 }
1294 amfree(output_find_log);
1295
1296 output_find = find_dump(&diskq); /* Add deleted dump to diskq */
1297 if(argc-(start_argc-1) > 0) {
1298 find_result_t *afind = NULL;
1299 find_result_t *afind_next = NULL;
1300 find_result_t *new_output_find = NULL;
1301 disk_t *dp;
1302
1303 amfree(errstr);
1304 errstr = match_disklist(&diskq, exact_match, argc-(start_argc-1),
1305 argv+(start_argc-1));
1306 if (errstr) {
1307 g_printf("%s", errstr);
1308 amfree(errstr);
1309 }
1310 for (afind = output_find; afind; afind = afind_next) {
1311 afind_next = afind->next;
1312 dp = lookup_disk(afind->hostname, afind->diskname);
1313 if (dp->todo) {
1314 afind->next = new_output_find;
1315 new_output_find = afind;
1316 } else {
1317 amfree(afind);
1318 }
1319 }
1320 output_find = new_output_find;
1321 } else if (errstr) {
1322 g_printf("%s", errstr);
1323 amfree(errstr);
1324 }
1325
1326 sort_find_result(sort_order, &output_find);
1327 print_find_result(output_find);
1328 free_find_result(&output_find);
1329
1330 amfree(sort_order);
1331 }
1332
1333
1334 /* ------------------------ */
1335
1336 static GSList *
get_file_list(int argc,char ** argv,int allow_empty,gboolean exact_match)1337 get_file_list(
1338 int argc,
1339 char **argv,
1340 int allow_empty,
1341 gboolean exact_match)
1342 {
1343 GSList * file_list = NULL;
1344 GSList * dumplist;
1345 int flags;
1346
1347 flags = CMDLINE_PARSE_DATESTAMP;
1348 if (allow_empty) flags |= CMDLINE_EMPTY_TO_WILDCARD;
1349 if (exact_match) flags |= CMDLINE_EXACT_MATCH;
1350 dumplist = cmdline_parse_dumpspecs(argc, argv, flags);
1351
1352 file_list = cmdline_match_holding(dumplist);
1353 dumpspec_list_free(dumplist);
1354
1355 return file_list;
1356 }
1357
1358 /* Given a file header, find the history element in curinfo most likely
1359 * corresponding to that dump (this is not an exact science).
1360 *
1361 * @param info: the info_t element for this DLE
1362 * @param file: the header of the file
1363 * @returns: index of the matching history element, or -1 if not found
1364 */
1365 static int
holding_file_find_history(info_t * info,dumpfile_t * file)1366 holding_file_find_history(
1367 info_t *info,
1368 dumpfile_t *file)
1369 {
1370 int matching_hist_idx = -1;
1371 int nhist;
1372 int i;
1373
1374 /* Begin by trying to find the history element matching this dump.
1375 * The datestamp on the dump is for the entire run of amdump, while the
1376 * 'date' in the history element of 'info' is the time the dump itself
1377 * began. A matching history element, then, is the earliest element
1378 * with a 'date' equal to or later than the date of the dumpfile.
1379 *
1380 * We compare using formatted datestamps; even using seconds since epoch,
1381 * we would still face timezone issues, and have to do a reverse (timezone
1382 * to gmt) translation.
1383 */
1384
1385 /* get to the end of the history list and search backward */
1386 for (nhist = 0; info->history[nhist].level > -1; nhist++) /* empty loop */;
1387 for (i = nhist-1; i > -1; i--) {
1388 char *info_datestamp = get_timestamp_from_time(info->history[i].date);
1389 int order = strcmp(file->datestamp, info_datestamp);
1390 amfree(info_datestamp);
1391
1392 if (order <= 0) {
1393 /* only a match if the levels are equal */
1394 if (info->history[i].level == file->dumplevel) {
1395 matching_hist_idx = i;
1396 }
1397 break;
1398 }
1399 }
1400
1401 return matching_hist_idx;
1402 }
1403
1404 /* A holding file is 'outdated' if a subsequent dump of the same DLE was made
1405 * at the same level or a lower leve; for example, a level 2 dump is outdated if
1406 * there is a subsequent level 2, or a subsequent level 0.
1407 *
1408 * @param file: the header of the file
1409 * @returns: true if the file is outdated
1410 */
1411 static int
holding_file_is_outdated(dumpfile_t * file)1412 holding_file_is_outdated(
1413 dumpfile_t *file)
1414 {
1415 info_t info;
1416 int matching_hist_idx;
1417
1418 if (get_info(file->name, file->disk, &info) == -1) {
1419 return 0; /* assume it's not outdated */
1420 }
1421
1422 /* if the last level is less than the level of this dump, then
1423 * it's outdated */
1424 if (info.last_level < file->dumplevel)
1425 return 1;
1426
1427 /* otherwise, we need to see if this dump is the last at its level */
1428 matching_hist_idx = holding_file_find_history(&info, file);
1429 if (matching_hist_idx == -1) {
1430 return 0; /* assume it's not outdated */
1431 }
1432
1433 /* compare the date of the history element with the most recent date
1434 * for this level. If they match, then this is the last dump at this
1435 * level, and we checked above for more recent lower-level dumps, so
1436 * the dump is not outdated. */
1437 if (info.history[matching_hist_idx].date ==
1438 info.inf[info.history[matching_hist_idx].level].date) {
1439 return 0;
1440 } else {
1441 return 1;
1442 }
1443 }
1444
1445 static int
remove_holding_file_from_catalog(char * filename)1446 remove_holding_file_from_catalog(
1447 char *filename)
1448 {
1449 static int warnings_printed; /* only print once per invocation */
1450 dumpfile_t file;
1451 info_t info;
1452 int matching_hist_idx = -1;
1453 history_t matching_hist; /* will be a copy */
1454 int i;
1455
1456 if (!holding_file_get_dumpfile(filename, &file)) {
1457 g_printf(_("Could not read holding file %s\n"), filename);
1458 return 0;
1459 }
1460
1461 if (get_info(file.name, file.disk, &info) == -1) {
1462 g_printf(_("WARNING: No curinfo record for %s:%s\n"), file.name, file.disk);
1463 dumpfile_free_data(&file);
1464 return 1; /* not an error */
1465 }
1466
1467 matching_hist_idx = holding_file_find_history(&info, &file);
1468
1469 if (matching_hist_idx == -1) {
1470 g_printf(_("WARNING: No dump matching %s found in curinfo.\n"), filename);
1471 dumpfile_free_data(&file);
1472 return 1; /* not an error */
1473 }
1474
1475 /* make a copy */
1476 matching_hist = info.history[matching_hist_idx];
1477
1478 /* Remove the history element itself before doing the stats */
1479 for (i = matching_hist_idx; i < NB_HISTORY; i++) {
1480 info.history[i] = info.history[i+1];
1481 }
1482 info.history[NB_HISTORY].level = -1;
1483
1484 /* Remove stats for that history element, if necessary. Doing so
1485 * will result in an inconsistent set of backups, so we warn the
1486 * user and adjust last_level to make the next dump get us a
1487 * consistent picture. */
1488 if (matching_hist.date == info.inf[matching_hist.level].date) {
1489 /* search for an earlier dump at this level */
1490 for (i = matching_hist_idx; info.history[i].level > -1; i++) {
1491 if (info.history[i].level == matching_hist.level)
1492 break;
1493 }
1494
1495 if (info.history[i].level < 0) {
1496 /* not found => zero it out */
1497 info.inf[matching_hist.level].date = (time_t)-1; /* flag as not set */
1498 info.inf[matching_hist.level].label[0] = '\0';
1499 } else {
1500 /* found => reconstruct stats as best we can */
1501 info.inf[matching_hist.level].size = info.history[i].size;
1502 info.inf[matching_hist.level].csize = info.history[i].csize;
1503 info.inf[matching_hist.level].secs = info.history[i].secs;
1504 info.inf[matching_hist.level].date = info.history[i].date;
1505 info.inf[matching_hist.level].filenum = 0; /* we don't know */
1506 info.inf[matching_hist.level].label[0] = '\0'; /* we don't know */
1507 }
1508
1509 /* set last_level to the level we just deleted, and set command
1510 * appropriately to make sure planner does a new dump at this level
1511 * or lower */
1512 info.last_level = matching_hist.level;
1513 if (info.last_level == 0) {
1514 g_printf(_("WARNING: Deleting the most recent full dump; forcing a full dump at next run.\n"));
1515 SET(info.command, FORCE_FULL);
1516 } else {
1517 g_printf(_("WARNING: Deleting the most recent level %d dump; forcing a level %d dump or \nWARNING: lower at next run.\n"),
1518 info.last_level, info.last_level);
1519 SET(info.command, FORCE_NO_BUMP);
1520 }
1521
1522 /* Search for and display any subsequent runs that depended on this one */
1523 warnings_printed = 0;
1524 for (i = matching_hist_idx-1; i >= 0; i--) {
1525 char *datestamp;
1526 if (info.history[i].level <= matching_hist.level) break;
1527
1528 datestamp = get_timestamp_from_time(info.history[i].date);
1529 g_printf(_("WARNING: Level %d dump made %s can no longer be accurately restored.\n"),
1530 info.history[i].level, datestamp);
1531 amfree(datestamp);
1532
1533 warnings_printed = 1;
1534 }
1535 if (warnings_printed)
1536 g_printf(_("WARNING: (note, dates shown above are for dumps, and may be later than the\nWARNING: corresponding run date)\n"));
1537 }
1538
1539 /* recalculate consecutive_runs based on the history: find the first run
1540 * at this level, and then count the consecutive runs at that level. This
1541 * number may be zero (if we just deleted the last run at this level) */
1542 info.consecutive_runs = 0;
1543 for (i = 0; info.history[i].level >= 0; i++) {
1544 if (info.history[i].level == info.last_level) break;
1545 }
1546 while (info.history[i+info.consecutive_runs].level == info.last_level)
1547 info.consecutive_runs++;
1548
1549 /* this function doesn't touch the performance stats */
1550
1551 /* write out the changes */
1552 if (put_info(file.name, file.disk, &info) == -1) {
1553 g_printf(_("Could not write curinfo record for %s:%s\n"), file.name, file.disk);
1554 dumpfile_free_data(&file);
1555 return 0;
1556 }
1557
1558 dumpfile_free_data(&file);
1559 return 1;
1560 }
1561
1562 void
holding(int argc,char ** argv)1563 holding(
1564 int argc,
1565 char ** argv)
1566 {
1567 GSList *file_list;
1568 GSList *li;
1569 enum { HOLDING_USAGE, HOLDING_LIST, HOLDING_DELETE } action = HOLDING_USAGE;
1570 int long_list = 0;
1571 int outdated_list = 0;
1572 dumpfile_t file;
1573
1574 if (argc < 4)
1575 action = HOLDING_USAGE;
1576 else if (strcmp(argv[3], "list") == 0 && argc >= 4)
1577 action = HOLDING_LIST;
1578 else if (strcmp(argv[3], "delete") == 0 && argc > 4)
1579 action = HOLDING_DELETE;
1580
1581 switch (action) {
1582 case HOLDING_USAGE:
1583 g_fprintf(stderr,
1584 _("%s: expecting \"holding list [-l] [-d]\" or \"holding delete <host> [ .. ]\"\n"),
1585 get_pname());
1586 usage();
1587 return;
1588
1589 case HOLDING_LIST:
1590 long_list = opt_long;
1591 outdated_list = opt_outdated;
1592 argc -= 4; argv += 4;
1593
1594 /* header */
1595 if (long_list) {
1596 g_printf("%-10s %-2s %-4s %s\n",
1597 _("size (kB)"), _("lv"), _("outd"), _("dump specification"));
1598 }
1599
1600 file_list = get_file_list(argc, argv, 1, exact_match);
1601 for (li = file_list; li != NULL; li = li->next) {
1602 char *dumpstr;
1603 int is_outdated;
1604
1605 if (!holding_file_get_dumpfile((char *)li->data, &file)) {
1606 g_fprintf(stderr, _("Error reading %s\n"), (char *)li->data);
1607 continue;
1608 }
1609
1610 is_outdated = holding_file_is_outdated(&file);
1611
1612 dumpstr = cmdline_format_dumpspec_components(file.name, file.disk, file.datestamp, NULL);
1613 /* only print this entry if we're printing everything, or if it's outdated and
1614 * we're only printing outdated files (-o) */
1615 if (!outdated_list || is_outdated) {
1616 if (long_list) {
1617 g_printf("%-10lld %-2d %-4s %s\n",
1618 (long long)holding_file_size((char *)li->data, 0),
1619 file.dumplevel,
1620 is_outdated? " *":"",
1621 dumpstr);
1622 } else {
1623 g_printf("%s\n", dumpstr);
1624 }
1625 }
1626 amfree(dumpstr);
1627 dumpfile_free_data(&file);
1628 }
1629 slist_free_full(file_list, g_free);
1630 break;
1631
1632 case HOLDING_DELETE:
1633 argc -= 4; argv += 4;
1634
1635 file_list = get_file_list(argc, argv, 0, exact_match);
1636 for (li = file_list; li != NULL; li = li->next) {
1637 g_fprintf(stderr, _("Deleting '%s'\n"), (char *)li->data);
1638 /* remove it from the catalog */
1639 if (!remove_holding_file_from_catalog((char *)li->data))
1640 exit(1);
1641
1642 /* unlink it */
1643 if (!holding_file_unlink((char *)li->data)) {
1644 error(_("Could not delete '%s'"), (char *)li->data);
1645 }
1646 }
1647 slist_free_full(file_list, g_free);
1648 break;
1649 }
1650 }
1651
1652
1653 /* ------------------------ */
1654
1655
1656 /* shared code with planner.c */
1657
1658 int
bump_thresh(int level)1659 bump_thresh(
1660 int level)
1661 {
1662 gint64 bump = getconf_int64(CNF_BUMPSIZE);
1663 double mult = getconf_real(CNF_BUMPMULT);
1664
1665 while(--level)
1666 bump = (int)((double)bump * mult);
1667 return bump;
1668 }
1669
1670 void
bumpsize(int argc,char ** argv)1671 bumpsize(
1672 int argc,
1673 char ** argv)
1674 {
1675 int l;
1676 int conf_bumppercent = getconf_int(CNF_BUMPPERCENT);
1677 double conf_bumpmult = getconf_real(CNF_BUMPMULT);
1678
1679 (void)argc; /* Quiet unused parameter warning */
1680 (void)argv; /* Quiet unused parameter warning */
1681
1682 g_printf(_("Current bump parameters:\n"));
1683 if(conf_bumppercent == 0) {
1684 g_printf(_(" bumpsize %5jd KB\t- minimum savings (threshold) to bump level 1 -> 2\n"),
1685 (intmax_t)getconf_int64(CNF_BUMPSIZE));
1686 g_printf(_(" bumpdays %5d\t- minimum days at each level\n"),
1687 getconf_int(CNF_BUMPDAYS));
1688 g_printf(_(" bumpmult %5.5lg\t- threshold = bumpsize * bumpmult**(level-1)\n\n"),
1689 conf_bumpmult);
1690
1691 g_printf(_(" Bump -> To Threshold\n"));
1692 for(l = 1; l < 9; l++)
1693 g_printf(_("\t%d -> %d %9d KB\n"), l, l+1, bump_thresh(l));
1694 putchar('\n');
1695 }
1696 else {
1697 double bumppercent = (double)conf_bumppercent;
1698
1699 g_printf(_(" bumppercent %3d %%\t- minimum savings (threshold) to bump level 1 -> 2\n"),
1700 conf_bumppercent);
1701 g_printf(_(" bumpdays %5d\t- minimum days at each level\n"),
1702 getconf_int(CNF_BUMPDAYS));
1703 g_printf(_(" bumpmult %5.5lg\t- threshold = disk_size * bumppercent * bumpmult**(level-1)\n\n"),
1704 conf_bumpmult);
1705 g_printf(_(" Bump -> To Threshold\n"));
1706 for(l = 1; l < 9; l++) {
1707 g_printf(_("\t%d -> %d %7.2lf %%\n"), l, l+1, bumppercent);
1708 bumppercent *= conf_bumpmult;
1709 if(bumppercent >= 100.000) { bumppercent = 100.0;}
1710 }
1711 putchar('\n');
1712 }
1713 }
1714
1715 /* ----------------------------------------------- */
1716
1717 void export_one(disk_t *dp);
1718
1719 void
export_db(int argc,char ** argv)1720 export_db(
1721 int argc,
1722 char ** argv)
1723 {
1724 disk_t *dp;
1725 time_t curtime;
1726 char hostname[MAX_HOSTNAME_LENGTH+1];
1727 int i;
1728
1729 g_printf(_("CURINFO Version %s CONF %s\n"), VERSION, getconf_str(CNF_ORG));
1730
1731 curtime = time(0);
1732 if(gethostname(hostname, SIZEOF(hostname)-1) == -1) {
1733 error(_("could not determine host name: %s\n"), strerror(errno));
1734 /*NOTREACHED*/
1735 }
1736 hostname[SIZEOF(hostname)-1] = '\0';
1737 g_printf(_("# Generated by:\n# host: %s\n# date: %s"),
1738 hostname, ctime(&curtime));
1739
1740 g_printf(_("# command:"));
1741 for(i = 0; i < argc; i++)
1742 g_printf(_(" %s"), argv[i]);
1743
1744 g_printf(_("\n# This file can be merged back in with \"amadmin import\".\n"));
1745 g_printf(_("# Edit only with care.\n"));
1746
1747 if(argc >= 4)
1748 diskloop(argc, argv, "export", export_one);
1749 else for(dp = diskq.head; dp != NULL; dp = dp->next)
1750 export_one(dp);
1751 }
1752
1753 void
export_one(disk_t * dp)1754 export_one(
1755 disk_t * dp)
1756 {
1757 info_t info;
1758 int i,l;
1759 char *qhost, *qdisk;
1760
1761 if(get_info(dp->host->hostname, dp->name, &info)) {
1762 g_fprintf(stderr, _("Warning: no curinfo record for %s:%s\n"),
1763 dp->host->hostname, dp->name);
1764 return;
1765 }
1766 qhost = quote_string(dp->host->hostname);
1767 qdisk = quote_string(dp->name);
1768 g_printf(_("host: %s\ndisk: %s\n"), qhost, qdisk);
1769 g_printf(_("command: %u\n"), info.command);
1770 g_printf(_("last_level: %d\n"),info.last_level);
1771 g_printf(_("consecutive_runs: %d\n"),info.consecutive_runs);
1772 g_printf(_("full-rate:"));
1773 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.full.rate[i]);
1774 g_printf(_("\nfull-comp:"));
1775 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.full.comp[i]);
1776
1777 g_printf(_("\nincr-rate:"));
1778 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.incr.rate[i]);
1779 g_printf(_("\nincr-comp:"));
1780 for(i=0;i<AVG_COUNT;i++) g_printf(_(" %lf"), info.incr.comp[i]);
1781 g_printf("\n");
1782 for(l=0;l<DUMP_LEVELS;l++) {
1783 if(info.inf[l].date < (time_t)0 && info.inf[l].label[0] == '\0') continue;
1784 g_printf(_("stats: %d %lld %lld %jd %jd %lld %s\n"), l,
1785 (long long)info.inf[l].size,
1786 (long long)info.inf[l].csize,
1787 (intmax_t)info.inf[l].secs,
1788 (intmax_t)info.inf[l].date,
1789 (long long)info.inf[l].filenum,
1790 info.inf[l].label);
1791 }
1792 for(l=0;info.history[l].level > -1;l++) {
1793 g_printf(_("history: %d %lld %lld %jd\n"),
1794 info.history[l].level,
1795 (long long)info.history[l].size,
1796 (long long)info.history[l].csize,
1797 (intmax_t)info.history[l].date);
1798 }
1799 g_printf("//\n");
1800 amfree(qhost);
1801 amfree(qdisk);
1802 }
1803
1804 /* ----------------------------------------------- */
1805
1806 int import_one(void);
1807 char *impget_line(void);
1808
1809 void
import_db(int argc,char ** argv)1810 import_db(
1811 int argc,
1812 char ** argv)
1813 {
1814 int vers_maj;
1815 int vers_min;
1816 int vers_patch;
1817 int newer;
1818 char *org;
1819 char *line = NULL;
1820 char *hdr;
1821 char *s;
1822 int rc;
1823 int ch;
1824
1825 (void)argc; /* Quiet unused parameter warning */
1826 (void)argv; /* Quiet unused parameter warning */
1827
1828 /* process header line */
1829
1830 if((line = agets(stdin)) == NULL) {
1831 g_fprintf(stderr, _("%s: empty input.\n"), get_pname());
1832 return;
1833 }
1834
1835 s = line;
1836 ch = *s++;
1837
1838 hdr = "version";
1839 if(strncmp_const_skip(s - 1, "CURINFO Version", s, ch) != 0) {
1840 goto bad_header;
1841 }
1842 ch = *s++;
1843 skip_whitespace(s, ch);
1844 if(ch == '\0'
1845 || sscanf(s - 1, "%d.%d.%d", &vers_maj, &vers_min, &vers_patch) != 3) {
1846 vers_patch = -1;
1847 if (sscanf(s - 1, "%d.%d", &vers_maj, &vers_min) != 2) {
1848 goto bad_header;
1849 }
1850 }
1851
1852 skip_integer(s, ch); /* skip over major */
1853 if(ch != '.') {
1854 goto bad_header;
1855 }
1856 ch = *s++;
1857 skip_integer(s, ch); /* skip over minor */
1858 if (vers_patch != -1) {
1859 if (ch != '.') {
1860 goto bad_header;
1861 }
1862 ch = *s++;
1863 skip_integer(s, ch); /* skip over patch */
1864 } else {
1865 vers_patch = 0;
1866 }
1867
1868 hdr = "comment";
1869 if(ch == '\0') {
1870 goto bad_header;
1871 }
1872 skip_non_whitespace(s, ch);
1873 s[-1] = '\0';
1874
1875 hdr = "CONF";
1876 skip_whitespace(s, ch); /* find the org keyword */
1877 if(ch == '\0' || strncmp_const_skip(s - 1, "CONF", s, ch) != 0) {
1878 goto bad_header;
1879 }
1880 ch = *s++;
1881
1882 hdr = "org";
1883 skip_whitespace(s, ch); /* find the org string */
1884 if(ch == '\0') {
1885 goto bad_header;
1886 }
1887 org = s - 1;
1888
1889 /*@ignore@*/
1890 newer = (vers_maj != VERSION_MAJOR)? vers_maj > VERSION_MAJOR :
1891 (vers_min != VERSION_MINOR)? vers_min > VERSION_MINOR :
1892 vers_patch > VERSION_PATCH;
1893 if(newer)
1894 g_fprintf(stderr,
1895 _("%s: WARNING: input is from newer Amanda version: %d.%d.%d.\n"),
1896 get_pname(), vers_maj, vers_min, vers_patch);
1897 /*@end@*/
1898
1899 if(strcmp(org, getconf_str(CNF_ORG)) != 0) {
1900 g_fprintf(stderr, _("%s: WARNING: input is from different org: %s\n"),
1901 get_pname(), org);
1902 }
1903
1904 do {
1905 rc = import_one();
1906 } while (rc);
1907
1908 amfree(line);
1909 return;
1910
1911 bad_header:
1912
1913 /*@i@*/ amfree(line);
1914 g_fprintf(stderr, _("%s: bad CURINFO header line in input: %s.\n"),
1915 get_pname(), hdr);
1916 g_fprintf(stderr, _(" Was the input in \"amadmin export\" format?\n"));
1917 return;
1918 }
1919
1920
1921 int
import_one(void)1922 import_one(void)
1923 {
1924 info_t info;
1925 stats_t onestat;
1926 int rc, level;
1927 char *line = NULL;
1928 char *s, *fp;
1929 int ch;
1930 int nb_history, i;
1931 char *hostname = NULL;
1932 char *diskname = NULL;
1933 long long off_t_tmp;
1934 long long time_t_tmp;
1935
1936 memset(&info, 0, SIZEOF(info_t));
1937
1938 for(level = 0; level < DUMP_LEVELS; level++) {
1939 info.inf[level].date = (time_t)-1;
1940 }
1941
1942 /* get host: disk: command: lines */
1943
1944 hostname = diskname = NULL;
1945
1946 if((line = impget_line()) == NULL) return 0; /* nothing there */
1947 s = line;
1948 ch = *s++;
1949
1950 skip_whitespace(s, ch);
1951 if(ch == '\0' || strncmp_const_skip(s - 1, "host:", s, ch) != 0) goto parse_err;
1952 skip_whitespace(s, ch);
1953 if(ch == '\0') goto parse_err;
1954 fp = s-1;
1955 skip_quoted_string(s, ch);
1956 s[-1] = '\0';
1957 hostname = unquote_string(fp);
1958 s[-1] = (char)ch;
1959
1960 skip_whitespace(s, ch);
1961 while (ch == 0) {
1962 amfree(line);
1963 if((line = impget_line()) == NULL) goto shortfile_err;
1964 s = line;
1965 ch = *s++;
1966 skip_whitespace(s, ch);
1967 }
1968 if(strncmp_const_skip(s - 1, "disk:", s, ch) != 0) goto parse_err;
1969 skip_whitespace(s, ch);
1970 if(ch == '\0') goto parse_err;
1971 fp = s-1;
1972 skip_quoted_string(s, ch);
1973 s[-1] = '\0';
1974 diskname = unquote_string(fp);
1975 s[-1] = (char)ch;
1976
1977 amfree(line);
1978 if((line = impget_line()) == NULL) goto shortfile_err;
1979 if(sscanf(line, "command: %u", &info.command) != 1) goto parse_err;
1980
1981 /* get last_level and consecutive_runs */
1982
1983 amfree(line);
1984 if((line = impget_line()) == NULL) goto shortfile_err;
1985 rc = sscanf(line, "last_level: %d", &info.last_level);
1986 if(rc == 1) {
1987 amfree(line);
1988 if((line = impget_line()) == NULL) goto shortfile_err;
1989 if(sscanf(line, "consecutive_runs: %d", &info.consecutive_runs) != 1) goto parse_err;
1990 amfree(line);
1991 if((line = impget_line()) == NULL) goto shortfile_err;
1992 }
1993
1994 /* get rate: and comp: lines for full dumps */
1995
1996 rc = sscanf(line, "full-rate: %lf %lf %lf",
1997 &info.full.rate[0], &info.full.rate[1], &info.full.rate[2]);
1998 if(rc != 3) goto parse_err;
1999
2000 amfree(line);
2001 if((line = impget_line()) == NULL) goto shortfile_err;
2002 rc = sscanf(line, "full-comp: %lf %lf %lf",
2003 &info.full.comp[0], &info.full.comp[1], &info.full.comp[2]);
2004 if(rc != 3) goto parse_err;
2005
2006 /* get rate: and comp: lines for incr dumps */
2007
2008 amfree(line);
2009 if((line = impget_line()) == NULL) goto shortfile_err;
2010 rc = sscanf(line, "incr-rate: %lf %lf %lf",
2011 &info.incr.rate[0], &info.incr.rate[1], &info.incr.rate[2]);
2012 if(rc != 3) goto parse_err;
2013
2014 amfree(line);
2015 if((line = impget_line()) == NULL) goto shortfile_err;
2016 rc = sscanf(line, "incr-comp: %lf %lf %lf",
2017 &info.incr.comp[0], &info.incr.comp[1], &info.incr.comp[2]);
2018 if(rc != 3) goto parse_err;
2019
2020 /* get stats for dump levels */
2021
2022 while(1) {
2023 amfree(line);
2024 if((line = impget_line()) == NULL) goto shortfile_err;
2025 if(strncmp_const(line, "//") == 0) {
2026 /* end of record */
2027 break;
2028 }
2029 if(strncmp_const(line, "history:") == 0) {
2030 /* end of record */
2031 break;
2032 }
2033 memset(&onestat, 0, SIZEOF(onestat));
2034
2035 s = line;
2036 ch = *s++;
2037
2038 skip_whitespace(s, ch);
2039 if(ch == '\0' || strncmp_const_skip(s - 1, "stats:", s, ch) != 0) {
2040 goto parse_err;
2041 }
2042
2043 skip_whitespace(s, ch);
2044 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
2045 goto parse_err;
2046 }
2047 skip_integer(s, ch);
2048
2049 skip_whitespace(s, ch);
2050 if(ch == '\0' || sscanf(s - 1, "%lld", &off_t_tmp) != 1) {
2051 goto parse_err;
2052 }
2053 onestat.size = (off_t)off_t_tmp;
2054 skip_integer(s, ch);
2055
2056 skip_whitespace(s, ch);
2057 if(ch == '\0' || sscanf(s - 1, "%lld", &off_t_tmp) != 1) {
2058 goto parse_err;
2059 }
2060 onestat.csize = (off_t)off_t_tmp;
2061 skip_integer(s, ch);
2062
2063 skip_whitespace(s, ch);
2064 if(ch == '\0' || sscanf(s - 1, "%lld", &time_t_tmp) != 1) {
2065 goto parse_err;
2066 }
2067 onestat.secs = (time_t)time_t_tmp;
2068 skip_integer(s, ch);
2069
2070 skip_whitespace(s, ch);
2071 if(ch == '\0' || sscanf(s - 1, "%lld", &time_t_tmp) != 1) {
2072 goto parse_err;
2073 }
2074 /* time_t not guarranteed to be long */
2075 /*@i1@*/ onestat.date = (time_t)time_t_tmp;
2076 skip_integer(s, ch);
2077
2078 skip_whitespace(s, ch);
2079 if(ch != '\0') {
2080 if(sscanf(s - 1, "%lld", &off_t_tmp) != 1) {
2081 goto parse_err;
2082 }
2083 onestat.filenum = (off_t)off_t_tmp;
2084 skip_integer(s, ch);
2085
2086 skip_whitespace(s, ch);
2087 if(ch == '\0') {
2088 if (onestat.filenum != 0)
2089 goto parse_err;
2090 onestat.label[0] = '\0';
2091 } else {
2092 strncpy(onestat.label, s - 1, SIZEOF(onestat.label)-1);
2093 onestat.label[SIZEOF(onestat.label)-1] = '\0';
2094 }
2095 }
2096
2097 if(level < 0 || level > 9) goto parse_err;
2098
2099 info.inf[level] = onestat;
2100 }
2101 nb_history = 0;
2102 for(i=0;i<=NB_HISTORY;i++) {
2103 info.history[i].level = -2;
2104 }
2105 while(1) {
2106 history_t onehistory;
2107
2108 if(line[0] == '/' && line[1] == '/') {
2109 info.history[nb_history].level = -2;
2110 rc = 0;
2111 break;
2112 }
2113 memset(&onehistory, 0, SIZEOF(onehistory));
2114 s = line;
2115 ch = *s++;
2116 if(strncmp_const_skip(line, "history:", s, ch) != 0) {
2117 break;
2118 }
2119
2120 skip_whitespace(s, ch);
2121 if(ch == '\0' || sscanf((s - 1), "%d", &onehistory.level) != 1) {
2122 break;
2123 }
2124 skip_integer(s, ch);
2125
2126 skip_whitespace(s, ch);
2127 if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
2128 break;
2129 }
2130 onehistory.size = (off_t)off_t_tmp;
2131 skip_integer(s, ch);
2132
2133 skip_whitespace(s, ch);
2134 if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
2135 break;
2136 }
2137 onehistory.csize = (off_t)off_t_tmp;
2138 skip_integer(s, ch);
2139
2140 skip_whitespace(s, ch);
2141 if((ch == '\0') || sscanf((s - 1), "%lld", &time_t_tmp) != 1) {
2142 break;
2143 }
2144 /* time_t not guarranteed to be long */
2145 /*@i1@*/ onehistory.date = (time_t)time_t_tmp;
2146 skip_integer(s, ch);
2147
2148 info.history[nb_history++] = onehistory;
2149 amfree(line);
2150 if((line = impget_line()) == NULL) goto shortfile_err;
2151 }
2152 /*@i@*/ amfree(line);
2153
2154 /* got a full record, now write it out to the database */
2155
2156 if(put_info(hostname, diskname, &info)) {
2157 g_fprintf(stderr, _("%s: error writing record for %s:%s\n"),
2158 get_pname(), hostname, diskname);
2159 }
2160 amfree(hostname);
2161 amfree(diskname);
2162 return 1;
2163
2164 parse_err:
2165 /*@i@*/ amfree(line);
2166 amfree(hostname);
2167 amfree(diskname);
2168 g_fprintf(stderr, _("%s: parse error reading import record.\n"), get_pname());
2169 return 0;
2170
2171 shortfile_err:
2172 /*@i@*/ amfree(line);
2173 amfree(hostname);
2174 amfree(diskname);
2175 g_fprintf(stderr, _("%s: short file reading import record.\n"), get_pname());
2176 return 0;
2177 }
2178
2179 char *
impget_line(void)2180 impget_line(void)
2181 {
2182 char *line;
2183 char *s;
2184 int ch;
2185
2186 for(; (line = agets(stdin)) != NULL; free(line)) {
2187 s = line;
2188 ch = *s++;
2189
2190 skip_whitespace(s, ch);
2191 if(ch == '#') {
2192 /* ignore comment lines */
2193 continue;
2194 } else if(ch) {
2195 /* found non-blank, return line */
2196 return line;
2197 }
2198 /* otherwise, a blank line, so keep going */
2199 }
2200 if(ferror(stdin)) {
2201 g_fprintf(stderr, _("%s: reading stdin: %s\n"),
2202 get_pname(), strerror(errno));
2203 }
2204 return NULL;
2205 }
2206
2207 /* ----------------------------------------------- */
2208
2209 void
disklist_one(disk_t * dp)2210 disklist_one(
2211 disk_t * dp)
2212 {
2213 am_host_t *hp;
2214 netif_t *ip;
2215 dumptype_t *dtype = lookup_dumptype(dp->dtype_name);
2216
2217 hp = dp->host;
2218 ip = hp->netif;
2219
2220 g_printf("line %d (%s):\n", dp->line, dp->filename);
2221
2222 g_printf(" host %s:\n", hp->hostname);
2223 g_printf(" interface %s\n",
2224 interface_name(ip->config)[0] ? interface_name(ip->config) : "default");
2225 g_printf(" disk %s:\n", dp->name);
2226 if (dp->device) g_printf(" device %s\n", dp->device);
2227
2228 g_printf(" program \"%s\"\n", dp->program);
2229 if (dp->application)
2230 g_printf(" application \"%s\"\n", dp->application);
2231
2232 dump_dumptype(dtype, " ", print_default, print_source);
2233
2234 g_printf(" spindle %d\n", dp->spindle);
2235
2236 g_printf("\n");
2237 }
2238
2239 void
disklist(int argc,char ** argv)2240 disklist(
2241 int argc,
2242 char ** argv)
2243 {
2244 disk_t *dp;
2245
2246 if(argc >= 4)
2247 diskloop(argc, argv, "disklist", disklist_one);
2248 else
2249 for(dp = diskq.head; dp != NULL; dp = dp->next)
2250 disklist_one(dp);
2251 }
2252
2253 /* ----------------------------------------------- */
2254
2255 void
hosts(int argc G_GNUC_UNUSED,char ** argv G_GNUC_UNUSED)2256 hosts(
2257 int argc G_GNUC_UNUSED,
2258 char ** argv G_GNUC_UNUSED)
2259 {
2260 disk_t *dp;
2261 gint sentinel = 1;
2262 GHashTable *seen = g_hash_table_new(g_str_hash, g_str_equal);
2263
2264 /* enumerate all hosts, skipping those that have been seen (since
2265 * there may be more than one DLE on a host */
2266 for(dp = diskq.head; dp != NULL; dp = dp->next) {
2267 char *hostname = dp->host->hostname;
2268 if (g_hash_table_lookup(seen, hostname))
2269 continue;
2270 g_printf("%s\n", hostname);
2271 g_hash_table_insert(seen, hostname, &sentinel);
2272 }
2273 g_hash_table_destroy(seen);
2274 }
2275
2276 /* ----------------------------------------------- */
2277
2278 void
dles(int argc G_GNUC_UNUSED,char ** argv G_GNUC_UNUSED)2279 dles(
2280 int argc G_GNUC_UNUSED,
2281 char ** argv G_GNUC_UNUSED)
2282 {
2283 disk_t *dp;
2284
2285 for(dp = diskq.head; dp != NULL; dp = dp->next)
2286 g_printf("%s %s\n", dp->host->hostname, dp->name);
2287 }
2288
2289 /* ----------------------------------------------- */
2290
2291 void
show_version(int argc,char ** argv)2292 show_version(
2293 int argc,
2294 char ** argv)
2295 {
2296 int i;
2297
2298 (void)argc; /* Quiet unused parameter warning */
2299 (void)argv; /* Quiet unused parameter warning */
2300
2301 for(i = 0; version_info[i] != NULL; i++)
2302 g_printf("%s", version_info[i]);
2303 }
2304
2305
show_config(int argc G_GNUC_UNUSED,char ** argv G_GNUC_UNUSED)2306 void show_config(
2307 int argc G_GNUC_UNUSED,
2308 char **argv G_GNUC_UNUSED)
2309 {
2310 dump_configuration(print_default, print_source);
2311 }
2312
2313