fed07efd571fb00e5307920f16b84c8143671845
[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     erroutput_type = ERR_INTERACTIVE;
139
140     cfg_ovr = extract_commandline_config_overwrites(&argc, &argv);
141     if(argc < 3) usage();
142
143     config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_FATAL,
144                 argv[1]);
145     apply_config_overwrites(cfg_ovr);
146
147     check_running_as(RUNNING_AS_DUMPUSER);
148
149     dbrename(config_name, DBG_SUBDIR_SERVER);
150
151     conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
152     if (read_tapelist(conf_tapelist)) {
153         error(_("could not load tapelist \"%s\""), conf_tapelist);
154         /*NOTREACHED*/
155     }
156     amfree(conf_tapelist);
157
158     if((have_changer = changer_init()) == 0) {
159         error(_("no tpchanger specified in \"%s\""), config_filename);
160         /*NOTREACHED*/
161     } else if (have_changer != 1) {
162         error(_("changer initialization failed: %s"), strerror(errno));
163         /*NOTREACHED*/
164     }
165
166     /* switch on command name */
167
168     argc -= 2; argv += 2;
169     for (i = 0; i < NCMDS; i++)
170         if (strcmp(argv[0], cmdtab[i].name) == 0) {
171             (*cmdtab[i].fn)(argc, argv);
172             break;
173         }
174     if (i == NCMDS) {
175         g_fprintf(stderr, _("%s: unknown command \"%s\"\n"), get_pname(), argv[0]);
176         usage();
177     }
178
179     amfree(changer_resultstr);
180
181     dbclose();
182     return 0;
183 }
184
185 /* ---------------------------- */
186
187 static void
188 reset_changer(G_GNUC_UNUSED int argc,
189               G_GNUC_UNUSED char ** argv) {
190     char *slotstr = NULL;
191
192     switch(changer_reset(&slotstr)) {
193     case 0:
194         g_fprintf(stderr, _("%s: changer is reset, slot %s is loaded.\n"),
195                 get_pname(), slotstr);
196         break;
197     case 1:
198         g_fprintf(stderr, _("%s: changer is reset, but slot %s not loaded: %s\n"),
199                 get_pname(), slotstr, changer_resultstr);
200         break;
201     default:
202         error(_("could not reset changer: %s"), changer_resultstr);
203         /*NOTREACHED*/
204     }
205     amfree(slotstr);
206 }
207
208
209 /* ---------------------------- */
210 static void
211 clean_tape(G_GNUC_UNUSED int    argc,
212            G_GNUC_UNUSED char ** argv) {
213     char *devstr = NULL;
214
215     if(changer_clean(&devstr) == 0) {
216         g_fprintf(stderr, _("%s: device %s is clean.\n"), get_pname(), devstr);
217     } else {
218         g_fprintf(stderr, _("%s: device %s not clean: %s\n"),
219                 get_pname(), devstr ? devstr : "??", changer_resultstr);
220     }
221     amfree(devstr);
222 }
223
224
225 /* ---------------------------- */
226 static void
227 eject_tape(G_GNUC_UNUSED int    argc,
228            G_GNUC_UNUSED char ** argv) {
229     char *slotstr = NULL;
230
231     if(changer_eject(&slotstr) == 0) {
232         g_fprintf(stderr, _("%s: slot %3s is ejected.\n"), get_pname(), slotstr);
233     } else {
234         g_fprintf(stderr, _("%s: slot %3s not ejected: %s\n"),
235                 get_pname(), slotstr ? slotstr : "??", changer_resultstr);
236     }
237     amfree(slotstr);
238 }
239
240
241 /* ---------------------------- */
242
243 static void
244 load_slot(
245     int         argc,
246     char **     argv)
247 {
248     char *slotstr = NULL, *devicename = NULL;
249     int is_advance;
250     Device * device;
251
252     if(argc != 2)
253         usage();
254
255     device_api_init();
256
257     is_advance = (strcmp(argv[1], "advance") == 0);
258     if(changer_loadslot(argv[1], &slotstr, &devicename)) {
259         error(_("could not load slot %s: %s"), slotstr, changer_resultstr);
260         /*NOTREACHED*/
261     }
262     
263     if (!is_advance) {
264         device = device_open(devicename);
265         if (device == NULL) {
266             g_fprintf(stderr,
267                     _("%s: could not open device %s"), get_pname(),
268                     devicename);
269         } else {
270             g_object_unref(device);
271         }
272     }
273
274     g_fprintf(stderr, _("%s: changed to slot %s"), get_pname(), slotstr);
275     if(! is_advance) {
276         g_fprintf(stderr, _(" on %s"), devicename);
277     }
278     fputc('\n', stderr);
279     amfree(slotstr);
280     amfree(devicename);
281 }
282
283
284 /* ---------------------------- */
285
286 /* This initalizes the ChangerStatus structure for all commands that
287    use changer_find; namely, show_slots, load_label, and
288    show_current. */
289 static int 
290 scan_init(G_GNUC_UNUSED void * data, int rc, G_GNUC_UNUSED int numslots,
291           G_GNUC_UNUSED int backwards, G_GNUC_UNUSED int searchable) {
292     if(rc != 0) {
293         error(_("could not get changer info: %s"), changer_resultstr);
294         /*NOTREACHED*/
295     }
296
297     return 0;
298 }
299
300 static int scan_init_print(void * data, int rc, int numslots,
301                          int backwards, int searchable) {
302     
303     g_fprintf(stderr, _("%s: scanning all %d slots in tape-changer rack:\n"),
304             get_pname(), numslots);
305
306     return scan_init(data, rc, numslots, backwards, searchable);
307 }
308
309 typedef struct {
310     gboolean found;
311     char *searchlabel;
312 } LabelChangerStatus;
313
314 /* This is the 'user_slot' callback for the 'load label' command. */
315 static int
316 loadlabel_slot(
317     void *      ud,
318     int         rc,
319     char *      slotstr,
320     char *      device_name)
321 {
322     LabelChangerStatus * status = ud;
323     Device * device;
324     ReadLabelStatusFlags label_status;
325
326     if (rc > 1) {
327         error(_("could not load slot %s: %s"), slotstr, changer_resultstr);
328         g_assert_not_reached();
329     } else if (rc == 1) {
330         g_fprintf(stderr, _("%s: slot %3s: %s\n"),
331                 get_pname(), slotstr, changer_resultstr);
332         return 0;
333     }
334
335     device = device_open(device_name);
336     if (device == NULL) {
337         g_fprintf(stderr, _("%s: slot %3s: Could not open device.\n"),
338                 get_pname(), slotstr);
339         return 0;
340     }
341     
342     device_set_startup_properties_from_config(device);
343
344     label_status = device_read_label(device);
345     if (label_status != READ_LABEL_STATUS_SUCCESS) {
346         char * errstr = 
347             g_english_strjoinv_and_free
348                 (g_flags_nick_to_strv(label_status,
349                                       READ_LABEL_STATUS_FLAGS_TYPE), "or");
350         g_fprintf(stderr, _("%s: slot %3s: %s\n"),
351                 get_pname(), slotstr, errstr);
352         g_object_unref(device);
353         return 0;
354     }
355     
356     g_fprintf(stderr, _("%s: slot %3s: time %-14s label %s"),
357             get_pname(), slotstr, device->volume_time, device->volume_label);
358
359     if(strcmp(device->volume_label, status->searchlabel) != 0) {
360         g_fprintf(stderr, _(" (wrong tape)\n"));
361         g_object_unref(device);
362         return 0;
363     } else {
364         g_fprintf(stderr, _(" (exact label match)\n"));
365         g_object_unref(device);
366         status->found = 1;
367         return 1;
368     }
369
370     g_assert_not_reached();
371 }
372
373 /* This does the 'load label' command. */
374 static void
375 load_label(
376     int         argc,
377     char **     argv)
378 {
379     LabelChangerStatus status;
380
381     if(argc != 2)
382         usage();
383     
384     device_api_init();
385
386     status.searchlabel = argv[1];
387
388     g_fprintf(stderr, _("%s: scanning for tape with label %s\n"),
389             get_pname(), status.searchlabel);
390
391     status.found = 0;
392
393     changer_find(&status, scan_init, loadlabel_slot, status.searchlabel);
394
395     if(status.found)
396         g_fprintf(stderr, _("%s: label %s is now loaded.\n"),
397                 get_pname(), status.searchlabel);
398     else
399         g_fprintf(stderr, _("%s: could not find label %s in tape rack.\n"),
400                 get_pname(), status.searchlabel);
401 }
402
403
404 /* ---------------------------- */
405
406 /* This is the user_slot function for "amtape show". */
407 static int
408 show_slots_slot(G_GNUC_UNUSED void * data, int rc, char * slotstr,
409                 char * device_name)
410 {
411     Device * device;
412
413     if(rc > 1) {
414         error(_("could not load slot %s: %s"), slotstr, changer_resultstr);
415         g_assert_not_reached();
416     }
417
418     if(rc == 1) {
419         g_fprintf(stderr, _("slot %s: %s\n"), slotstr, changer_resultstr);
420         return 0;
421     }
422
423     device = device_open(device_name);
424     if (device == NULL) {
425         g_fprintf(stderr, _("%s: slot %3s: Could not open device.\n"),
426                 get_pname(), slotstr);
427     } else {
428         ReadLabelStatusFlags label_status;
429         device_set_startup_properties_from_config(device);
430         label_status = device_read_label(device);
431
432         if (label_status != READ_LABEL_STATUS_SUCCESS) {
433             char * errstr = 
434                 g_english_strjoinv_and_free
435                 (g_flags_nick_to_strv(label_status,
436                                       READ_LABEL_STATUS_FLAGS_TYPE), "or");
437             g_fprintf(stderr, _("%s: slot %3s: %s\n"),
438                     get_pname(), slotstr, errstr);
439         } else {
440             g_fprintf(stderr, _("slot %3s: time %-14s label %s\n"),
441                     slotstr, device->volume_time, device->volume_label);
442
443             /* update the changer db */
444             changer_label(slotstr, device->volume_label);
445         }
446     }
447
448     if (device != NULL)
449         g_object_unref(device);
450
451     return 0;
452 }
453
454 static void
455 show_current(int argc, G_GNUC_UNUSED char ** argv) {
456     if(argc != 1)
457         usage();
458
459     device_api_init();
460
461     g_fprintf(stderr, _("%s: scanning current slot in tape-changer rack:\n"),
462             get_pname());
463     changer_current(NULL, scan_init, show_slots_slot);
464 }
465
466 static void
467 show_slots(int argc, G_GNUC_UNUSED char ** argv) {
468     if(argc != 1)
469         usage();
470
471     device_api_init();
472     changer_find(NULL, scan_init_print, show_slots_slot, NULL);
473 }
474
475
476 /* ---------------------------- */
477
478 static void
479 amtape_taper_scan(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char ** argv) {
480     char *label = NULL;
481     char * searchlabel;
482     tape_t * tp;
483     char *timestamp = NULL;
484     char *tapedev = NULL;
485     int result;
486
487     device_api_init();
488     
489     g_fprintf(stderr, _("%s: scanning for "), get_pname());
490
491     tp = lookup_last_reusable_tape(0);
492     if (tp == NULL) {
493         searchlabel = NULL;
494     } else {
495         searchlabel = stralloc(tp->label);
496         g_fprintf(stderr, _("tape label %s or "), searchlabel);
497     }
498     g_fprintf(stderr, _("a new tape.\n"));
499
500     result = taper_scan(searchlabel, &label, &timestamp, &tapedev, NULL,
501                         FILE_taperscan_output_callback, stderr, NULL, NULL);
502
503     if (result < 0) {
504         g_fprintf(stderr, _("Could not find a  non-active Amanda tape.\n"));
505         if (label) {
506             g_fprintf(stderr, _("Tape with label `%s' is now loaded.\n"), label);
507         }
508     } else if (result == 3) {
509         g_fprintf(stderr, _("New tape loaded, it will be labelled `%s'.\n"),
510                   label);
511     } else {
512         g_fprintf(stderr, _("Tape with label `%s' is now loaded.\n"), label);
513     }
514
515     amfree(searchlabel);
516     amfree(label);
517     amfree(timestamp);
518     amfree(tapedev);
519 }
520
521 /* ---------------------------- */
522
523 static void
524 show_device(G_GNUC_UNUSED int   argc,
525             G_GNUC_UNUSED char ** argv) {
526     char *slot = NULL, *device = NULL;
527     
528     if(changer_loadslot(_("current"), &slot, &device)) {
529         error(_("Could not load current slot.\n"));
530         /*NOTREACHED*/
531     }
532
533     g_printf("%s\n", device);
534     amfree(slot);
535     amfree(device);
536 }