]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_label.c
Fix automatic tape labeling
[bacula/bacula] / bacula / src / dird / ua_label.c
1 /*
2  *
3  *   Bacula Director -- Tape labeling commands
4  *
5  *     Kern Sibbald, April MMIII
6  *
7  *   Version $Id$
8  */
9
10 /*
11    Copyright (C) 2000-2003 Kern Sibbald and John Walker
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License as
15    published by the Free Software Foundation; either version 2 of
16    the License, or (at your option) any later version.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21    General Public License for more details.
22
23    You should have received a copy of the GNU General Public
24    License along with this program; if not, write to the Free
25    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
26    MA 02111-1307, USA.
27
28  */
29
30 #include "bacula.h"
31 #include "dird.h"
32
33 /* Slot list definition */
34 typedef struct s_vol_list {
35    struct s_vol_list *next;
36    char *VolName;
37    int Slot;
38 } vol_list_t;
39
40
41 /* Forward referenced functions */
42 static int do_label(UAContext *ua, char *cmd, int relabel);
43 static void label_from_barcodes(UAContext *ua);
44 static int send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr, 
45                POOL_DBR *pr, int relabel);
46 static vol_list_t *get_slot_list_from_SD(UAContext *ua);
47 static int is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr);
48
49
50 /*
51  * Label a tape 
52  *  
53  *   label storage=xxx volume=vvv
54  */
55 int label_cmd(UAContext *ua, char *cmd)
56 {
57    return do_label(ua, cmd, 0);       /* standard label */
58 }
59
60 int relabel_cmd(UAContext *ua, char *cmd)
61 {
62    return do_label(ua, cmd, 1);      /* relabel tape */
63 }
64
65
66 /*
67  * Update Slots corresponding to Volumes in autochanger 
68  */
69 int update_slots(UAContext *ua)
70 {
71    STORE *store;
72    vol_list_t *vl, *vol_list = NULL;
73    MEDIA_DBR mr;
74
75    if (!open_db(ua)) {
76       return 1;
77    }
78    store = get_storage_resource(ua, 1);
79    if (!store) {
80       return 1;
81    }
82    ua->jcr->store = store;
83
84    vol_list = get_slot_list_from_SD(ua);
85
86
87    if (!vol_list) {
88       bsendmsg(ua, _("No Volumes found to label, or no barcodes.\n"));
89       goto bail_out;
90    }
91
92    /* Walk through the list updating the media records */
93    for (vl=vol_list; vl; vl=vl->next) {
94
95       memset(&mr, 0, sizeof(mr));
96       bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
97       db_lock(ua->db);
98       if (db_get_media_record(ua->jcr, ua->db, &mr)) {
99           if (mr.Slot != vl->Slot) {
100              mr.Slot = vl->Slot;
101              if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
102                 bsendmsg(ua, _("%s\n"), db_strerror(ua->db));
103              } else {
104                 bsendmsg(ua, _(
105                   "Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
106                   mr.VolumeName, mr.Slot);
107              }
108           } else {
109              bsendmsg(ua, _("Catalog record for Volume \"%s\" is up to date.\n"),
110                 mr.VolumeName);
111           }   
112           db_unlock(ua->db);
113           continue;
114       } else {
115           bsendmsg(ua, _("Record for Volume \"%s\" not found in catalog.\n"), 
116              mr.VolumeName);
117       }
118       db_unlock(ua->db);
119    }
120
121
122 bail_out:
123    /* Free list */
124    for (vl=vol_list; vl; ) {
125       vol_list_t *ovl;
126       free(vl->VolName);
127       ovl = vl;
128       vl = vl->next;
129       free(ovl);
130    }
131
132    if (ua->jcr->store_bsock) {
133       bnet_sig(ua->jcr->store_bsock, BNET_TERMINATE);
134       bnet_close(ua->jcr->store_bsock);
135       ua->jcr->store_bsock = NULL;
136    }
137    return 1;
138 }
139
140 /*
141  * Common routine for both label and relabel
142  */
143 static int do_label(UAContext *ua, char *cmd, int relabel)
144 {
145    STORE *store;
146    BSOCK *sd;
147    sd = ua->jcr->store_bsock;
148    char dev_name[MAX_NAME_LENGTH];
149    MEDIA_DBR mr, omr;
150    POOL_DBR pr;
151    int ok = FALSE;
152    int mounted = FALSE;
153    int i;
154    static char *barcode_keyword[] = {
155       "barcode",
156       "barcodes",
157       NULL};
158
159
160    memset(&pr, 0, sizeof(pr));
161    if (!open_db(ua)) {
162       return 1;
163    }
164    store = get_storage_resource(ua, 1);
165    if (!store) {
166       return 1;
167    }
168    ua->jcr->store = store;
169
170    if (!relabel && find_arg_keyword(ua, barcode_keyword) >= 0) {
171       label_from_barcodes(ua);
172       return 1;
173    }
174
175    /* If relabel get name of Volume to relabel */
176    if (relabel) {
177       /* Check for oldvolume=name */
178       i = find_arg_with_value(ua, "oldvolume"); 
179       if (i >= 0) {
180          memset(&omr, 0, sizeof(omr));
181          bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
182          if (db_get_media_record(ua->jcr, ua->db, &omr)) {
183             goto checkVol;
184          } 
185          bsendmsg(ua, "%s", db_strerror(ua->db));
186       }
187       /* No keyword or Vol not found, ask user to select */
188       if (!select_media_dbr(ua, &omr)) {
189          return 1;
190       }
191
192       /* Require Volume to be Purged */
193 checkVol:
194       if (strcmp(omr.VolStatus, "Purged") != 0) {
195          bsendmsg(ua, _("Volume \"%s\" has VolStatus %s. It must be purged before relabeling.\n"),
196             omr.VolumeName, omr.VolStatus);
197          return 1;
198       }
199    }
200
201    /* Check for volume=NewVolume */
202    i = find_arg_with_value(ua, "volume");
203    if (i >= 0) {
204       pm_strcpy(&ua->cmd, ua->argv[i]);
205       goto checkName;
206    }
207
208    /* Get a new Volume name */
209    for ( ;; ) {
210       if (!get_cmd(ua, _("Enter new Volume name: "))) {
211          return 1;
212       }
213 checkName:
214       if (!is_volume_name_legal(ua, ua->cmd)) {
215          continue;
216       }
217
218       memset(&mr, 0, sizeof(mr));
219       bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));
220       if (db_get_media_record(ua->jcr, ua->db, &mr)) {
221           bsendmsg(ua, _("Media record for new Volume \"%s\" already exists.\n"), 
222              mr.VolumeName);
223           continue;
224       }
225       break;                          /* Got it */
226    }
227
228    /* If autochanger, request slot */
229    if (store->autochanger) {
230       i = find_arg_with_value(ua, "slot"); 
231       if (i >= 0) {
232          mr.Slot = atoi(ua->argv[i]);
233       } else if (!get_pint(ua, _("Enter slot (0 for none): "))) {
234          return 1;
235       } else {
236          mr.Slot = ua->pint32_val;
237       }
238    }
239
240    bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
241
242    /* Must select Pool if not already done */
243    if (pr.PoolId == 0) {
244       memset(&pr, 0, sizeof(pr));
245       if (!select_pool_dbr(ua, &pr)) {
246          return 1;
247       }
248    }
249
250    bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d ...\n"), 
251       store->hdr.name, store->address, store->SDport);
252    if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
253       bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
254       return 1;   
255    }
256    sd = ua->jcr->store_bsock;
257
258    ok = send_label_request(ua, &mr, &omr, &pr, relabel);
259
260    if (ok) {
261       if (relabel) {
262          if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
263             bsendmsg(ua, _("Delete of Volume \"%s\" failed. ERR=%s"),
264                omr.VolumeName, db_strerror(ua->db));
265          } else {
266             bsendmsg(ua, _("Old volume \"%s\" deleted from catalog.\n"), 
267                omr.VolumeName);
268          }
269       }
270       if (ua->automount) {
271          bstrncpy(dev_name, store->dev_name, sizeof(dev_name));
272          bsendmsg(ua, _("Requesting mount %s ...\n"), dev_name);
273          bash_spaces(dev_name);
274          bnet_fsend(sd, "mount %s", dev_name);
275          unbash_spaces(dev_name);
276          while (bnet_recv(sd) >= 0) {
277             bsendmsg(ua, "%s", sd->msg);
278             /* Here we can get
279              *  3001 OK mount. Device=xxx      or
280              *  3001 Mounted Volume vvvv
281              */
282             mounted = strncmp(sd->msg, "3001 ", 5) == 0;
283          }
284       }
285    }
286    if (!mounted) {
287       bsendmsg(ua, _("Do not forget to mount the drive!!!\n"));
288    }
289    bnet_sig(sd, BNET_TERMINATE);
290    bnet_close(sd);
291    ua->jcr->store_bsock = NULL;
292
293    return 1;
294 }
295
296 /*
297  * Request SD to send us the slot:barcodes, then wiffle
298  *  through them all labeling them.
299  */
300 static void label_from_barcodes(UAContext *ua)
301 {
302    STORE *store = ua->jcr->store;
303    POOL_DBR pr;
304    MEDIA_DBR mr, omr;
305    vol_list_t *vl, *vol_list = NULL;
306
307    vol_list = get_slot_list_from_SD(ua);
308
309    if (!vol_list) {
310       bsendmsg(ua, _("No Volumes found to label, or no barcodes.\n"));
311       goto bail_out;
312    }
313
314    /* Display list of Volumes and ask if he really wants to proceed */
315    bsendmsg(ua, _("The following Volumes will be labeled:\n"
316                   "Slot  Volume\n"
317                   "==============\n"));
318    for (vl=vol_list; vl; vl=vl->next) {
319       bsendmsg(ua, "%4d  %s\n", vl->Slot, vl->VolName);
320    }
321    if (!get_cmd(ua, _("Do you want to continue? (y/n): ")) ||
322        (ua->cmd[0] != 'y' && ua->cmd[0] != 'Y')) {
323       goto bail_out;
324    }
325    /* Select a pool */
326    memset(&pr, 0, sizeof(pr));
327    if (!select_pool_dbr(ua, &pr)) {
328       goto bail_out;
329    }
330    memset(&omr, 0, sizeof(omr));
331
332    /* Fire off the label requests */
333    for (vl=vol_list; vl; vl=vl->next) {
334
335       memset(&mr, 0, sizeof(mr));
336       bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
337       if (db_get_media_record(ua->jcr, ua->db, &mr)) {
338           bsendmsg(ua, _("Media record for Slot %d Volume \"%s\" already exists.\n"), 
339              vl->Slot, mr.VolumeName);
340           continue;
341       }
342       /*
343        * Deal with creating cleaning tape here. Normal tapes created in
344        *  send_label_request() below
345        */
346       if (is_cleaning_tape(ua, &mr, &pr)) {
347          set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
348          if (db_create_media_record(ua->jcr, ua->db, &mr)) {
349             bsendmsg(ua, _("Catalog record for cleaning tape \"%s\" successfully created.\n"),
350                mr.VolumeName);
351          } else {
352             bsendmsg(ua, "Catalog error on cleaning tape: %s", db_strerror(ua->db));
353          }
354          continue;
355       }
356       bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
357       if (ua->jcr->store_bsock) {
358          bnet_sig(ua->jcr->store_bsock, BNET_TERMINATE);
359          bnet_close(ua->jcr->store_bsock);
360          ua->jcr->store_bsock = NULL;
361       }
362       bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d ...\n"), 
363          store->hdr.name, store->address, store->SDport);
364       if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
365          bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
366          goto bail_out;
367       }
368
369       mr.Slot = vl->Slot;
370       send_label_request(ua, &mr, &omr, &pr, 0);
371    }
372
373
374 bail_out:
375    /* Free list */
376    for (vl=vol_list; vl; ) {
377       vol_list_t *ovl;
378       free(vl->VolName);
379       ovl = vl;
380       vl = vl->next;
381       free(ovl);
382    }
383
384    if (ua->jcr->store_bsock) {
385       bnet_sig(ua->jcr->store_bsock, BNET_TERMINATE);
386       bnet_close(ua->jcr->store_bsock);
387       ua->jcr->store_bsock = NULL;
388    }
389
390    return;
391 }
392
393 /* 
394  * Check if the Volume name has legal characters
395  * If ua is non-NULL send the message
396  */
397 int is_volume_name_legal(UAContext *ua, char *name)
398 {
399    int len;
400    char *p;
401    char *accept = ":.-_";
402
403    /* Restrict the characters permitted in the Volume name */
404    for (p=name; *p; p++) {
405       if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
406          continue;
407       }
408       if (ua) {
409          bsendmsg(ua, _("Illegal character \"%c\" in a volume name.\n"), *p);
410       }
411       return 0;
412    }
413    len = strlen(name);
414    if (len >= MAX_NAME_LENGTH) {
415       if (ua) {
416          bsendmsg(ua, _("Volume name too long.\n"));
417       }
418       return 0;
419    }
420    if (len == 0) {
421       if (ua) {
422          bsendmsg(ua, _("Volume name must be at least one character long.\n"));
423       }
424       return 0;
425    }
426    return 1;
427 }
428
429 static int send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr, 
430                               POOL_DBR *pr, int relabel)
431 {
432    BSOCK *sd;
433    char dev_name[MAX_NAME_LENGTH];
434    int ok = FALSE;
435
436    sd = ua->jcr->store_bsock;
437    bstrncpy(dev_name, ua->jcr->store->dev_name, sizeof(dev_name));
438    bash_spaces(dev_name);
439    bash_spaces(mr->VolumeName);
440    bash_spaces(mr->MediaType);
441    bash_spaces(pr->Name);
442    if (relabel) {
443       bash_spaces(omr->VolumeName);
444       bnet_fsend(sd, "relabel %s OldName=%s NewName=%s PoolName=%s MediaType=%s Slot=%d", 
445          dev_name, omr->VolumeName, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot);
446       bsendmsg(ua, _("Sending relabel command from \"%s\" to \"%s\" ...\n"),
447          omr->VolumeName, mr->VolumeName);
448    } else {
449       bnet_fsend(sd, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d", 
450          dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot);
451       bsendmsg(ua, _("Sending label command for Volume \"%s\" Slot %d ...\n"), 
452          mr->VolumeName, mr->Slot);
453       Dmsg5(200, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d\n", 
454          dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot);
455    }
456
457    while (bnet_recv(sd) >= 0) {
458       bsendmsg(ua, "%s", sd->msg);
459       if (strncmp(sd->msg, "3000 OK label.", 14) == 0) {
460          ok = TRUE;
461       } 
462    }
463    unbash_spaces(mr->VolumeName);
464    unbash_spaces(mr->MediaType);
465    unbash_spaces(pr->Name);
466    mr->LabelDate = time(NULL);
467    if (ok) {
468       set_pool_dbr_defaults_in_media_dbr(mr, pr);
469       mr->VolBytes = 1;               /* flag indicating Volume labeled */
470       if (db_create_media_record(ua->jcr, ua->db, mr)) {
471          bsendmsg(ua, _("Catalog record for Volume \"%s\", Slot %d  successfully created.\n"),
472             mr->VolumeName, mr->Slot);
473       } else {
474          bsendmsg(ua, "%s", db_strerror(ua->db));
475          ok = FALSE;
476       }
477    } else {
478       bsendmsg(ua, _("Label command failed.\n"));
479    }
480    return ok;
481 }
482
483 static vol_list_t *get_slot_list_from_SD(UAContext *ua)
484 {
485    STORE *store = ua->jcr->store;
486    char dev_name[MAX_NAME_LENGTH];
487    BSOCK *sd;
488    vol_list_t *vl;
489    vol_list_t *vol_list = NULL;
490
491
492    bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d ...\n"), 
493       store->hdr.name, store->address, store->SDport);
494    if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
495       bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
496       return NULL;
497    }
498    sd  = ua->jcr->store_bsock;
499
500    bstrncpy(dev_name, store->dev_name, sizeof(dev_name));
501    bash_spaces(dev_name);
502    /* Ask for autochanger list of volumes */
503    bnet_fsend(sd, _("autochanger list %s \n"), dev_name);
504
505    /* Read and organize list of Volumes */
506    while (bnet_recv(sd) >= 0) {
507       char *p;
508       int Slot;
509       strip_trailing_junk(sd->msg);
510
511       /* Check for returned SD messages */
512       if (sd->msg[0] == '3'     && B_ISDIGIT(sd->msg[1]) &&
513           B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
514           sd->msg[4] == ' ') {
515          bsendmsg(ua, "%s\n", sd->msg);   /* pass them on to user */
516          continue;
517       }
518
519       /* Validate Slot:Barcode */
520       p = strchr(sd->msg, ':');
521       if (p && strlen(p) > 1) {
522          *p++ = 0;
523          if (!is_an_integer(sd->msg)) {
524             continue;
525          }
526       } else {
527          continue;
528       }
529       Slot = atoi(sd->msg);
530       if (Slot <= 0 || !is_volume_name_legal(ua, p)) {
531          continue;
532       }
533
534       /* Add Slot and VolumeName to list */
535       vl = (vol_list_t *)malloc(sizeof(vol_list_t));
536       vl->Slot = Slot;
537       vl->VolName = bstrdup(p);
538       if (!vol_list) {
539          vl->next = vol_list;
540          vol_list = vl;
541       } else {
542          /* Add new entry to end of list */
543          for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) {
544             if (!tvl->next) {
545                tvl->next = vl;
546                vl->next = NULL;
547                break;
548             }
549          }
550       }
551    }
552    return vol_list;
553 }
554
555 /*
556  * Check if this is a cleaning tape by comparing the Volume name
557  *  with the Cleaning Prefix. If they match, this is a cleaning 
558  *  tape.
559  */
560 static int is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr)
561 {
562    if (!ua->jcr->pool) {
563       /* Find Pool resource */
564       ua->jcr->pool = (POOL *)GetResWithName(R_POOL, pr->Name);
565       if (!ua->jcr->pool) {
566          bsendmsg(ua, _("Pool %s resource not found!\n"), pr->Name);
567          return 1;
568       }
569    }
570    if (ua->jcr->pool->cleaning_prefix == NULL) {
571       return 0;
572    }
573    Dmsg4(200, "CLNprefix=%s: Vol=%s: len=%d strncmp=%d\n",
574       ua->jcr->pool->cleaning_prefix, mr->VolumeName,
575       strlen(ua->jcr->pool->cleaning_prefix), 
576       strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
577                   strlen(ua->jcr->pool->cleaning_prefix)));
578    return strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
579                   strlen(ua->jcr->pool->cleaning_prefix)) == 0;
580 }