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    if (job_canceled(jcr)) {
79       return false;
80    }
81    if (dcr->device->alert_command && dcr->device->control_name) {
82       POOLMEM *alertcmd;
83       int status = 1;
84       int nalerts = 0;
85       BPIPE *bpipe;
86       ALERT *alert, *rmalert;
87       char line[MAXSTRING];
88       const char *fmt = "TapeAlert[%d]";
89 
90       struct stat statp;
91       if (stat(dcr->device->control_name, &statp) < 0) {
92           berrno be;
93           Jmsg2(jcr, M_ERROR, 0, _("Unable to stat ControlDevice %s: ERR=%s\n"),
94                 dcr->device->control_name, be.bstrerror());
95           return false;
96       }
97 
98       if (!alert_list) {
99          alert_list = New(alist(10));
100       }
101       alertcmd = get_pool_memory(PM_FNAME);
102       alertcmd = edit_device_codes(dcr, alertcmd, dcr->device->alert_command, "");
103       /* Wait maximum 5 minutes */
104       bpipe = open_bpipe(alertcmd, 60 * 5, "r");
105       if (bpipe) {
106          int alertno;
107          alert = (ALERT *)malloc(sizeof(ALERT));
108          memset(alert->alerts, 0, sizeof(alert->alerts));
109          alert->Volume = bstrdup(getVolCatName());
110          alert->alert_time = (utime_t)time(NULL);
111          while (fgets(line, (int)sizeof(line), bpipe->rfd)) {
112             alertno = 0;
113             if (bsscanf(line, fmt, &alertno) == 1) {
114                if (alertno > 0) {
115                   if (nalerts+1 > (int)sizeof(alert->alerts)) {
116                      break;
117                   } else {
118                      alert->alerts[nalerts++] = alertno;
119                   }
120                }
121             }
122          }
123          status = close_bpipe(bpipe);
124          if (nalerts > 0) {
125              /* Maintain First in, last out list */
126              if (alert_list->size() > 8) {
127                 rmalert = (ALERT *)alert_list->last();
128                 free(rmalert->Volume);
129                 alert_list->pop();
130                 free(rmalert);
131              }
132             alert_list->prepend(alert);
133          } else {
134             free(alert->Volume);
135             free(alert);
136          }
137          free_pool_memory(alertcmd);
138          return true;
139       } else {
140          status = errno;
141       }
142       if (status != 0) {
143          berrno be;
144          Jmsg(jcr, M_ALERT, 0, _("3997 Bad alert command: %s: ERR=%s.\n"),
145               alertcmd, be.bstrerror(status));
146          Tmsg2(10, _("3997 Bad alert command: %s: ERR=%s.\n"),
147               alertcmd, be.bstrerror(status));
148       }
149 
150       Dmsg1(400, "alert status=%d\n", status);
151       free_pool_memory(alertcmd);
152    } else {
153       if (!dcr->device->alert_command) {
154          Dmsg1(dbglvl, "Cannot do tape alerts: no Alert Command specified for device %s\n",
155             print_name());
156          Tmsg1(dbglvl, "Cannot do tape alerts: no Alert Command specified for device %s\n",
157             print_name());
158 
159       }
160       if (!dcr->device->control_name) {
161          Dmsg1(dbglvl, "Cannot do tape alerts: no Control Device specified for device %s\n",
162             print_name());
163          Tmsg1(dbglvl, "Cannot do tape alerts: no Control Device specified for device %s\n",
164             print_name());
165       }
166    }
167    return false;
168 }
169 
170 
171 /*
172  * Print desired tape alert messages
173  */
show_tape_alerts(DCR * dcr,alert_list_type list_type,alert_list_which which,alert_cb alert_callback)174 void tape_dev::show_tape_alerts(DCR *dcr, alert_list_type list_type,
175         alert_list_which which, alert_cb alert_callback)
176 {
177    int i;
178    ALERT *alert;
179    int code;
180 
181    if (!alert_list) {
182       return;
183    }
184    Dmsg1(dbglvl, "There are %d alerts.\n", alert_list->size());
185    switch (list_type) {
186    case list_codes:
187       foreach_alist(alert, alert_list) {
188          for (i=0; i<(int)sizeof(alert->alerts) && alert->alerts[i]; i++) {
189             code = alert->alerts[i];
190             Dmsg4(dbglvl, "Volume=%s alert=%d severity=%c flags=0x%x\n", alert->Volume, code,
191                ta_errors[code].severity, (int)ta_errors[code].flags);
192             alert_callback(dcr, ta_errors[code].short_msg, long_msg[code],
193                alert->Volume, ta_errors[code].severity,
194                ta_errors[code].flags,  code, (utime_t)alert->alert_time);
195          }
196          if (which == list_last) {
197             break;
198          }
199       }
200       break;
201    default:
202       foreach_alist(alert, alert_list) {
203          for (i=0; i<(int)sizeof(alert->alerts) && alert->alerts[i]; i++) {
204             code = alert->alerts[i];
205             Dmsg4(dbglvl, "Volume=%s severity=%c flags=0x%x alert=%s\n", alert->Volume,
206                ta_errors[code].severity, (int)ta_errors[code].flags,
207                ta_errors[code].short_msg);
208             alert_callback(dcr, ta_errors[code].short_msg, long_msg[code],
209                alert->Volume, ta_errors[code].severity,
210                ta_errors[code].flags, code, (utime_t)alert->alert_time);
211          }
212          if (which == list_last) {
213             break;
214          }
215       }
216       break;
217    }
218    return;
219 }
220 
221 /*
222  * Delete alert list returning number deleted
223  */
delete_alerts()224 int tape_dev::delete_alerts()
225 {
226    ALERT *alert;
227    int deleted = 0;
228 
229    if (alert_list) {
230       foreach_alist(alert, alert_list) {
231          free(alert->Volume);
232          deleted++;
233       }
234       alert_list->destroy();
235       free(alert_list);
236       alert_list = NULL;
237    }
238    return deleted;
239 }
240