11d002212e7cb743443e5710e67d5c98d89a9fde
[debian/amanda] / server-src / amtape.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
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.
15  *
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.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: amtape.c,v 1.47 2006/07/25 18:27:57 martinea Exp $
28  *
29  * tape changer interface program
30  */
31 #include "amanda.h"
32 #include "conffile.h"
33 #include "tapefile.h"
34 #include "taperscan.h"
35 #include "clock.h"
36 #include "changer.h"
37 #include "version.h"
38 #include "device.h"
39 #include "timestamp.h"
40
41 /* local functions */
42 static void usage(void);
43 int main(int argc, char **argv);
44 static void reset_changer(int argc, char **argv);
45 static void eject_tape(int argc, char **argv);
46 static void clean_tape(int argc, char **argv);
47 static void load_slot(int argc, char **argv);
48 static void load_label(int argc, char **argv);
49 static void show_slots(int argc, char **argv);
50 static void show_current(int argc, char **argv);
51 static void amtape_taper_scan(int argc, char **argv);
52 static void show_device(int argc, char **argv);
53 static int loadlabel_slot(void *ud, int rc, char *slotstr, char *device);
54 int show_init(void *ud, int rc, int ns, int bk, int s);
55 static int show_slots_slot(void *ud, int rc, char *slotstr, char *device);
56
57 static const struct {
58     const char *name;
59     void (*fn)(int, char **);
60     const char *usage;
61 } cmdtab[] = {
62     { "reset", reset_changer,
63         T_("reset                Reset changer to known state") },
64     { "eject", eject_tape,
65         T_("eject                Eject current tape from drive") },
66     { "clean", clean_tape,
67         T_("clean                Clean the drive") },
68     { "show", show_slots,
69         T_("show                 Show contents of all slots") },
70     { "current", show_current,
71         T_("current              Show contents of current slot") },
72     { "slot" , load_slot,
73         T_("slot <slot #>        load tape from slot <slot #>") },
74     { "slot" , load_slot,
75         T_("slot current         load tape from current slot") },
76     { "slot" , load_slot,
77         T_("slot prev            load tape from previous slot") },
78     { "slot" , load_slot,
79         T_("slot next            load tape from next slot") },
80     { "slot" , load_slot,
81         T_("slot advance         advance to next slot but do not load") },
82     { "slot" , load_slot,
83         T_("slot first           load tape from first slot") },
84     { "slot" , load_slot,
85         T_("slot last            load tape from last slot") },
86     { "label", load_label,
87         T_("label <label>        find and load labeled tape") },
88     { "taper", amtape_taper_scan,
89         T_("taper                perform taper's scan alg.") },
90     { "device", show_device,
91         T_("device               show current tape device") },
92     { "update", show_slots,
93         T_("update               update the label matchingdatabase")},
94 };
95 #define NCMDS   (int)(sizeof(cmdtab) / sizeof(cmdtab[0]))
96
97 static void
98 usage(void)
99 {
100     int i;
101
102     g_fprintf(stderr, _("Usage: amtape%s <conf> <command> {<args>} [-o configoption]*\n"), versionsuffix());
103     g_fprintf(stderr, _("\tValid commands are:\n"));
104     for (i = 0; i < NCMDS; i++)
105         g_fprintf(stderr, "\t\t%s\n", _(cmdtab[i].usage));
106     exit(1);
107 }
108
109 int
110 main(
111     int         argc,
112     char **     argv)
113 {
114     char *conf_tapelist;
115     int i;
116     int have_changer;
117     config_overwrites_t *cfg_ovr = NULL;
118
119     /*
120      * Configure program for internationalization:
121      *   1) Only set the message locale for now.
122      *   2) Set textdomain for all amanda related programs to "amanda"
123      *      We don't want to be forced to support dozens of message catalogs.
124      */  
125     setlocale(LC_MESSAGES, "C");
126     textdomain("amanda"); 
127
128     safe_fd(-1, 0);
129     safe_cd();
130
131     set_pname("amtape");
132
133     /* Don't die when child closes pipe */
134     signal(SIGPIPE, SIG_IGN);
135
136     dbopen(DBG_SUBDIR_SERVER);
137
138     malloc_size_1 = malloc_inuse(&malloc_hist_1);
139
140     erroutput_type = ERR_INTERACTIVE;
141
142     cfg_ovr = extract_commandline_config_overwrites(&argc, &argv);
143     if(argc < 3) usage();
144
145     config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_FATAL,
146                 argv[1]);
147     apply_config_overwrites(cfg_ovr);
148
149     check_running_as(RUNNING_AS_DUMPUSER);
150
151     dbrename(config_name, DBG_SUBDIR_SERVER);
152
153     conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
154     if (read_tapelist(conf_tapelist)) {
155         error(_("could not load tapelist \"%s\""), conf_tapelist);
156         /*NOTREACHED*/
157     }
158     amfree(conf_tapelist);
159
160     if((have_changer = changer_init()) == 0) {
161         error(_("no tpchanger specified in \"%s\""), config_filename);
162         /*NOTREACHED*/
163     } else if (have_changer != 1) {
164         error(_("changer initialization failed: %s"), strerror(errno));
165         /*NOTREACHED*/
166     }
167
168     /* switch on command name */
169
170     argc -= 2; argv += 2;
171     for (i = 0; i < NCMDS; i++)
172         if (strcmp(argv[0], cmdtab[i].name) == 0) {
173             (*cmdtab[i].fn)(argc, argv);
174             break;
175         }
176     if (i == NCMDS) {
177         g_fprintf(stderr, _("%s: unknown command \"%s\"\n"), get_pname(), argv[0]);
178         usage();
179     }
180
181     amfree(changer_resultstr);
182
183     dbclose();
184     return 0;
185 }
186
187 /* ---------------------------- */
188
189 static void
190 reset_changer(G_GNUC_UNUSED int argc,
191               G_GNUC_UNUSED char ** argv) {
192     char *slotstr = NULL;
193
194     (void)argc; /* Quiet unused parameter warning */
195     (void)argv; /* Quiet unused parameter warning */
196
197     switch(changer_reset(&slotstr)) {
198     case 0:
199         g_fprintf(stderr, _("%s: changer is reset, slot %s is loaded.\n"),
200                 get_pname(), slotstr);
201         break;
202     case 1:
203         g_fprintf(stderr, _("%s: changer is reset, but slot %s not loaded: %s\n"),
204                 get_pname(), slotstr, changer_resultstr);
205         break;
206     default:
207         error(_("could not reset changer: %s"), changer_resultstr);
208         /*NOTREACHED*/
209     }
210     amfree(slotstr);
211 }
212
213
214 /* ---------------------------- */
215 static void
216 clean_tape(G_GNUC_UNUSED int    argc,
217            G_GNUC_UNUSED char ** argv) {
218     char *devstr = NULL;
219
220     (void)argc; /* Quiet unused parameter warning */
221     (void)argv; /* Quiet unused parameter warning */
222
223     if(changer_clean(&devstr) == 0) {
224         g_fprintf(stderr, _("%s: device %s is clean.\n"), get_pname(), devstr);
225     } else {
226         g_fprintf(stderr, _("%s: device %s not clean: %s\n"),
227                 get_pname(), devstr ? devstr : "??", changer_resultstr);
228     }
229     amfree(devstr);
230 }
231
232
233 /* ---------------------------- */
234 static void
235 eject_tape(G_GNUC_UNUSED int    argc,
236            G_GNUC_UNUSED char ** argv) {
237     char *slotstr = NULL;
238
239     (void)argc; /* Quiet unused parameter warning */
240     (void)argv; /* Quiet unused parameter warning */
241
242     if(changer_eject(&slotstr) == 0) {
243         g_fprintf(stderr, _("%s: slot %3s is ejected.\n"), get_pname(), slotstr);
244     } else {
245         g_fprintf(stderr, _("%s: slot %3s not ejected: %s\n"),
246                 get_pname(), slotstr ? slotstr : "??", changer_resultstr);
247     }
248     amfree(slotstr);
249 }
250
251
252 /* ---------------------------- */
253
254 static void
255 load_slot(
256     int         argc,
257     char **     argv)
258 {
259     char *slotstr = NULL, *devicename = NULL;
260     int is_advance;
261     Device * device;
262
263     if(argc != 2)
264         usage();
265
266     device_api_init();
267
268     is_advance = (strcmp(argv[1], "advance") == 0);
269     if(changer_loadslot(argv[1], &slotstr, &devicename)) {
270         error(_("could not load slot %s: %s"), slotstr, changer_resultstr);
271         /*NOTREACHED*/
272     }
273     
274     if (!is_advance) {
275         device = device_open(devicename);
276         if (device == NULL) {
277             g_fprintf(stderr,
278                     _("%s: could not open device %s"), get_pname(),
279                     devicename);
280         } else {
281             g_object_unref(device);
282         }
283     }
284
285     g_fprintf(stderr, _("%s: changed to slot %s"), get_pname(), slotstr);
286     if(! is_advance) {
287         g_fprintf(stderr, _(" on %s"), devicename);
288     }
289     fputc('\n', stderr);
290     amfree(slotstr);
291     amfree(devicename);
292 }
293
294
295 /* ---------------------------- */
296
297 /* This initalizes the ChangerStatus structure for all commands that
298    use changer_find; namely, show_slots, load_label, and
299    show_current. */
300 static int 
301 scan_init(G_GNUC_UNUSED void * data, int rc, G_GNUC_UNUSED int numslots,
302           G_GNUC_UNUSED int backwards, G_GNUC_UNUSED int searchable) {
303     if(rc != 0) {
304         error(_("could not get changer info: %s"), changer_resultstr);
305         /*NOTREACHED*/
306     }
307
308     nslots = ns;
309     backwards = bk;
310
311     return 0;
312 }
313
314 static int scan_init_print(void * data, int rc, int numslots,
315                          int backwards, int searchable) {
316     
317     g_fprintf(stderr, _("%s: scanning all %d slots in tape-changer rack:\n"),
318             get_pname(), numslots);
319
320     return scan_init(data, rc, numslots, backwards, searchable);
321 }
322
323 typedef struct {
324     gboolean found;
325     char *searchlabel;
326 } LabelChangerStatus;
327
328 /* This is the 'user_slot' callback for the 'load label' command. */
329 static int
330 loadlabel_slot(
331     void *      ud,
332     int         rc,
333     char *      slotstr,
334     char *      device_name)
335 {
336     LabelChangerStatus * status = ud;
337     Device * device;
338     ReadLabelStatusFlags label_status;
339
340     if (rc > 1) {
341         error(_("could not load slot %s: %s"), slotstr, changer_resultstr);
342         g_assert_not_reached();
343     } else if (rc == 1) {
344         g_fprintf(stderr, _("%s: slot %3s: %s\n"),
345                 get_pname(), slotstr, changer_resultstr);
346         return 0;
347     }
348
349     device = device_open(device_name);
350     if (device == NULL) {
351         g_fprintf(stderr, _("%s: slot %3s: Could not open device.\n"),
352                 get_pname(), slotstr);
353         return 0;
354     }
355     
356     device_set_startup_properties_from_config(device);
357
358     label_status = device_read_label(device);
359     if (label_status != READ_LABEL_STATUS_SUCCESS) {
360         char * errstr = 
361             g_english_strjoinv_and_free
362                 (g_flags_nick_to_strv(label_status,
363                                       READ_LABEL_STATUS_FLAGS_TYPE), "or");
364         g_fprintf(stderr, _("%s: slot %3s: %s\n"),
365                 get_pname(), slotstr, errstr);
366         g_object_unref(device);
367         return 0;
368     }
369     
370     g_fprintf(stderr, _("%s: slot %3s: time %-14s label %s"),
371             get_pname(), slotstr, device->volume_time, device->volume_label);
372
373     if(strcmp(device->volume_label, status->searchlabel) != 0) {
374         g_fprintf(stderr, _(" (wrong tape)\n"));
375         g_object_unref(device);
376         return 0;
377     } else {
378         g_fprintf(stderr, _(" (exact label match)\n"));
379         g_object_unref(device);
380         status->found = 1;
381         return 1;
382     }
383
384     g_assert_not_reached();
385 }
386
387 /* This does the 'load label' command. */
388 static void
389 load_label(
390     int         argc,
391     char **     argv)
392 {
393     LabelChangerStatus status;
394
395     if(argc != 2)
396         usage();
397     
398     device_api_init();
399
400     status.searchlabel = argv[1];
401
402     g_fprintf(stderr, _("%s: scanning for tape with label %s\n"),
403             get_pname(), status.searchlabel);
404
405     status.found = 0;
406
407     changer_find(&status, scan_init, loadlabel_slot, status.searchlabel);
408
409     if(status.found)
410         g_fprintf(stderr, _("%s: label %s is now loaded.\n"),
411                 get_pname(), status.searchlabel);
412     else
413         g_fprintf(stderr, _("%s: could not find label %s in tape rack.\n"),
414                 get_pname(), status.searchlabel);
415 }
416
417
418 /* ---------------------------- */
419
420 /* This is the user_slot function for "amtape show". */
421 static int
422 show_slots_slot(G_GNUC_UNUSED void * data, int rc, char * slotstr,
423                 char * device_name)
424 {
425     Device * device;
426
427     if(rc > 1) {
428         error(_("could not load slot %s: %s"), slotstr, changer_resultstr);
429         g_assert_not_reached();
430     }
431
432     if(rc == 1) {
433         g_fprintf(stderr, _("slot %s: %s\n"), slotstr, changer_resultstr);
434         return 0;
435     }
436
437     device = device_open(device_name);
438     if (device == NULL) {
439         g_fprintf(stderr, _("%s: slot %3s: Could not open device.\n"),
440                 get_pname(), slotstr);
441     } else {
442         ReadLabelStatusFlags label_status;
443         device_set_startup_properties_from_config(device);
444         label_status = device_read_label(device);
445
446         if (label_status != READ_LABEL_STATUS_SUCCESS) {
447             char * errstr = 
448                 g_english_strjoinv_and_free
449                 (g_flags_nick_to_strv(label_status,
450                                       READ_LABEL_STATUS_FLAGS_TYPE), "or");
451             g_fprintf(stderr, _("%s: slot %3s: %s\n"),
452                     get_pname(), slotstr, errstr);
453         } else {
454             g_fprintf(stderr, _("slot %3s: time %-14s label %s\n"),
455                     slotstr, device->volume_time, device->volume_label);
456
457             /* update the changer db */
458             changer_label(slotstr, device->volume_label);
459         }
460     }
461
462     if (device != NULL)
463         g_object_unref(device);
464
465     return 0;
466 }
467
468 static void
469 show_current(int argc, G_GNUC_UNUSED char ** argv) {
470     if(argc != 1)
471         usage();
472
473     device_api_init();
474
475     g_fprintf(stderr, _("%s: scanning current slot in tape-changer rack:\n"),
476             get_pname());
477     changer_current(NULL, scan_init, show_slots_slot);
478 }
479
480 static void
481 show_slots(int argc, G_GNUC_UNUSED char ** argv) {
482     if(argc != 1)
483         usage();
484
485     device_api_init();
486     changer_find(NULL, scan_init_print, show_slots_slot, NULL);
487 }
488
489
490 /* ---------------------------- */
491
492 static void
493 amtape_taper_scan(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char ** argv) {
494     char *label = NULL;
495     char * searchlabel;
496     tape_t * tp;
497     char *timestamp = NULL;
498     char *tapedev = NULL;
499     int result;
500
501     device_api_init();
502     
503     g_fprintf(stderr, _("%s: scanning for "), get_pname());
504
505     tp = lookup_last_reusable_tape(0);
506     if (tp == NULL) {
507         searchlabel = NULL;
508     } else {
509         searchlabel = stralloc(tp->label);
510         g_fprintf(stderr, _("tape label %s or "), searchlabel);
511     }
512     g_fprintf(stderr, _("a new tape.\n"));
513
514     result = taper_scan(searchlabel, &label, &timestamp, &tapedev, NULL,
515                         FILE_taperscan_output_callback, stderr, NULL, NULL);
516
517     if (result < 0) {
518         g_fprintf(stderr, _("Could not find a  non-active Amanda tape.\n"));
519         if (label) {
520             g_fprintf(stderr, _("Tape with label `%s' is now loaded.\n"), label);
521         }
522     } else if (result == 3) {
523         g_fprintf(stderr, _("New tape loaded, it will be labelled `%s'.\n"),
524                   label);
525     } else {
526         g_fprintf(stderr, _("Tape with label `%s' is now loaded.\n"), label);
527     }
528
529     amfree(searchlabel);
530     amfree(label);
531     amfree(timestamp);
532     amfree(tapedev);
533 }
534
535 /* ---------------------------- */
536
537 static void
538 show_device(G_GNUC_UNUSED int   argc,
539             G_GNUC_UNUSED char ** argv) {
540     char *slot = NULL, *device = NULL;
541     
542     if(changer_loadslot(_("current"), &slot, &device)) {
543         error(_("Could not load current slot.\n"));
544         /*NOTREACHED*/
545     }
546
547     g_printf("%s\n", device);
548     amfree(slot);
549     amfree(device);
550 }
551
552 /* ---------------------------- */
553
554 int
555 update_one_slot(
556     void *      ud,
557     int         rc,
558     char *      slotstr,
559     char *      device)
560 {
561     char *errstr = NULL;
562     char *datestamp = NULL;
563     char *label = NULL;
564
565     (void)ud;   /* Quiet unused parameter warning */
566
567     if(rc > 1)
568         error("could not load slot %s: %s", slotstr, changer_resultstr);
569     else if(rc == 1)
570         fprintf(stderr, "slot %s: %s\n", slotstr, changer_resultstr);
571     else if((errstr = tape_rdlabel(device, &datestamp, &label)) != NULL)
572         fprintf(stderr, "slot %s: %s\n", slotstr, errstr);
573     else {
574         fprintf(stderr, "slot %s: date %-8s label %s\n",
575                 slotstr, datestamp, label);
576         changer_label(slotstr, label);
577     }
578     amfree(errstr);
579     amfree(datestamp);
580     amfree(label);
581     return 0;
582 }
583
584 void
585 update_labeldb(
586     int         argc,
587     char **     argv)
588 {
589     (void)argv; /* Quiet unused parameter warning */
590
591     if(argc != 1)
592         usage();
593
594     changer_find(NULL, show_init_all, update_one_slot, NULL);
595 }