1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *
21  *  Routines for getting and displaying tape alerts
22  *
23  *   Written by Kern Sibbald, October MMXVI
24  *
25  */
26 
27 #include "bacula.h"                   /* pull in global headers */
28 #include "stored.h"                   /* pull in Storage Deamon headers */
29 
30 #include "tape_alert_msgs.h"
31 
32 static const int dbglvl = 120;
33 
34 #define MAX_MSG 54                    /* Maximum alert message number */
35 
alert_callback(void * ctx,const char * short_msg,const char * long_msg,char * Volume,int severity,int flags,int alertno,utime_t alert_time)36 void alert_callback(void *ctx, const char *short_msg, const char *long_msg,
37        char *Volume, int severity, int flags, int alertno, utime_t alert_time)
38 {
39    DCR *dcr = (DCR *)ctx;
40    JCR *jcr = dcr->jcr;
41    DEVICE *dev = dcr->dev;
42    int type = M_INFO;
43 
44    switch (severity) {
45    case 'C':
46       type = M_FATAL;
47       break;
48    case 'W':
49       type = M_WARNING;
50       break;
51    case 'I':
52       type = M_INFO;
53       break;
54    }
55    if (flags & TA_DISABLE_DRIVE) {
56       dev->enabled = false;
57       Jmsg(jcr, M_WARNING, 0, _("Disabled Device %s due to tape alert=%d.\n"),
58          dev->print_name(), alertno);
59       Tmsg2(dbglvl, _("Disabled Device %s due to tape alert=%d.\n"),
60          dev->print_name(), alertno);
61    }
62    if (flags & TA_DISABLE_VOLUME) {
63       dev->setVolCatStatus("Disabled");
64       dev->VolCatInfo.VolEnabled = false;
65       dir_update_volume_info(dcr, false, true);
66       Jmsg(jcr, M_WARNING, 0, _("Disabled Volume \"%s\" due to tape alert=%d.\n"),
67            Volume, alertno);
68       Tmsg2(dbglvl, _("Disabled Volume \"%s\" due to tape alert=%d.\n"),
69             Volume, alertno);
70    }
71    Jmsg(jcr, type, (utime_t)alert_time, _("Alert: Volume=\"%s\" alert=%d: ERR=%s\n"),
72       Volume, alertno, long_msg);
73 }
74 
get_tape_alerts(DCR * dcr)75 bool tape_dev::get_tape_alerts(DCR *dcr)
76 {
77    JCR *jcr = dcr->jcr;
78 
79    if (!job_canceled(jcr) && dcr->device->alert_command &&
80        dcr->device->control_name) {
81       POOLMEM *alertcmd;
82       int status = 1;
83       int nalerts = 0;
84       BPIPE *bpipe;
85       ALERT *alert, *rmalert;
86       char line[MAXSTRING];
87       const char *fmt = "TapeAlert[%d]";
88 
89       if (!alert_list) {
90          alert_list = New(alist(10));
91       }
92       alertcmd = get_pool_memory(PM_FNAME);
93       alertcmd = edit_device_codes(dcr, alertcmd, dcr->device->alert_command, "");
94       /* Wait maximum 5 minutes */
95       bpipe = open_bpipe(alertcmd, 60 * 5, "r");
96       if (bpipe) {
97          int alertno;
98          alert = (ALERT *)malloc(sizeof(ALERT));
99          memset(alert->alerts, 0, sizeof(alert->alerts));
100          alert->Volume = bstrdup(getVolCatName());
101          alert->alert_time = (utime_t)time(NULL);
102          while (fgets(line, (int)sizeof(line), bpipe->rfd)) {
103             alertno = 0;
104             if (bsscanf(line, fmt, &alertno) == 1) {
105                if (alertno > 0) {
106                   if (nalerts+1 > (int)sizeof(alert->alerts)) {
107                      break;
108                   } else {
109                      alert->alerts[nalerts++] = alertno;
110                   }
111                }
112             }
113          }
114          status = close_bpipe(bpipe);
115          if (nalerts > 0) {
116              /* Maintain First in, last out list */
117              if (alert_list->size() > 8) {
118                 rmalert = (ALERT *)alert_list->last();
119                 free(rmalert->Volume);
120                 alert_list->pop();
121                 free(rmalert);
122              }
123             alert_list->prepend(alert);
124          } else {
125             free(alert->Volume);
126             free(alert);
127          }
128          free_pool_memory(alertcmd);
129          return true;
130       } else {
131          status = errno;
132       }
133       if (status != 0) {
134          berrno be;
135          Jmsg(jcr, M_ALERT, 0, _("3997 Bad alert command: %s: ERR=%s.\n"),
136               alertcmd, be.bstrerror(status));
137          Tmsg2(10, _("3997 Bad alert command: %s: ERR=%s.\n"),
138               alertcmd, be.bstrerror(status));
139       }
140 
141       Dmsg1(400, "alert status=%d\n", status);
142       free_pool_memory(alertcmd);
143    } else {
144       if (!dcr->device->alert_command) {
145          Dmsg1(dbglvl, "Cannot do tape alerts: no Alert Command specified for device %s\n",
146             print_name());
147          Tmsg1(dbglvl, "Cannot do tape alerts: no Alert Command specified for device %s\n",
148             print_name());
149 
150       }
151       if (!dcr->device->control_name) {
152          Dmsg1(dbglvl, "Cannot do tape alerts: no Control Device specified for device %s\n",
153             print_name());
154          Tmsg1(dbglvl, "Cannot do tape alerts: no Control Device specified for device %s\n",
155             print_name());
156       }
157    }
158    return false;
159 }
160 
161 
162 /*
163  * Print desired tape alert messages
164  */
show_tape_alerts(DCR * dcr,alert_list_type list_type,alert_list_which which,alert_cb alert_callback)165 void tape_dev::show_tape_alerts(DCR *dcr, alert_list_type list_type,
166         alert_list_which which, alert_cb alert_callback)
167 {
168    int i;
169    ALERT *alert;
170    int code;
171 
172    if (!alert_list) {
173       return;
174    }
175    Dmsg1(dbglvl, "There are %d alerts.\n", alert_list->size());
176    switch (list_type) {
177    case list_codes:
178       foreach_alist(alert, alert_list) {
179          for (i=0; i<(int)sizeof(alert->alerts) && alert->alerts[i]; i++) {
180             code = alert->alerts[i];
181             Dmsg4(dbglvl, "Volume=%s alert=%d severity=%c flags=0x%x\n", alert->Volume, code,
182                ta_errors[code].severity, (int)ta_errors[code].flags);
183             alert_callback(dcr, ta_errors[code].short_msg, long_msg[code],
184                alert->Volume, ta_errors[code].severity,
185                ta_errors[code].flags,  code, (utime_t)alert->alert_time);
186          }
187          if (which == list_last) {
188             break;
189          }
190       }
191       break;
192    default:
193       foreach_alist(alert, alert_list) {
194          for (i=0; i<(int)sizeof(alert->alerts) && alert->alerts[i]; i++) {
195             code = alert->alerts[i];
196             Dmsg4(dbglvl, "Volume=%s severity=%c flags=0x%x alert=%s\n", alert->Volume,
197                ta_errors[code].severity, (int)ta_errors[code].flags,
198                ta_errors[code].short_msg);
199             alert_callback(dcr, ta_errors[code].short_msg, long_msg[code],
200                alert->Volume, ta_errors[code].severity,
201                ta_errors[code].flags, code, (utime_t)alert->alert_time);
202          }
203          if (which == list_last) {
204             break;
205          }
206       }
207       break;
208    }
209    return;
210 }
211 
212 /*
213  * Delete alert list returning number deleted
214  */
delete_alerts()215 int tape_dev::delete_alerts()
216 {
217    ALERT *alert;
218    int deleted = 0;
219 
220    if (alert_list) {
221       foreach_alist(alert, alert_list) {
222          free(alert->Volume);
223          deleted++;
224       }
225       alert_list->destroy();
226       free(alert_list);
227       alert_list = NULL;
228    }
229    return deleted;
230 }
231