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 /* Written by Eric Bollengier Apr 2020 */
20 
21 #include "bacula.h"
22 
23 /*****************************************************************/
24 /* Events management in Bacula */
25 
26 /* Generate an Event/Audit message */
events_send_msg(JCR * jcr,const char * code,const char * type,const char * source,intptr_t ref,const char * fmt,...)27 void events_send_msg(JCR *jcr, const char *code, const char *type, const char *source,
28                      intptr_t ref, const char *fmt, ...)
29 {
30    POOL_MEM tmp(PM_MESSAGE), tmp2(PM_MESSAGE), rbuf(PM_MESSAGE);
31    va_list arg_ptr;
32 
33    pm_strcpy(tmp, source);
34    bash_spaces(tmp);
35 
36    pm_strcpy(tmp2, my_name);
37    bash_spaces(tmp2);
38 
39    /* TODO: Handle references with Date+Id rather than pointers */
40    Mmsg(rbuf, "Events: code=%s daemon=%s ref=0x%p type=%s source=%s text=",
41         code, tmp2.c_str(), ref, type, tmp.c_str());
42 
43    va_start(arg_ptr, fmt);
44    bvsnprintf(tmp.c_str(), tmp.size(), fmt, arg_ptr);
45    va_end(arg_ptr);
46 
47    pm_strcat(rbuf, tmp.c_str());
48    Dmsg1(5, "%s\n", rbuf.c_str());
49 
50    MSGS *msgs = get_current_MSGS(jcr);
51    int mtype = msgs->get_custom_type((char *)type);
52    if (mtype < 0) {
53       mtype = M_EVENTS;
54    }
55    Jmsg(jcr, mtype, 0, "%s\n", rbuf.c_str());
56 }
57 
58 /* Generate an Event/Audit message */
events_send_msg(JCR * jcr,EVENTS_DBR * ev)59 void events_send_msg(JCR *jcr, EVENTS_DBR *ev)
60 {
61    POOL_MEM rbuf(PM_MESSAGE);
62    bash_spaces(ev->EventsSource);
63    bash_spaces(ev->EventsDaemon);
64 
65    Mmsg(rbuf, "Events: code=%s daemon=%s ref=0x%p type=%s source=%s text=%s",
66         ev->EventsCode, ev->EventsDaemon, ev->EventsRef, ev->EventsType,
67         ev->EventsSource, ev->EventsText);
68 
69    MSGS *msgs = get_current_MSGS(jcr);
70    int mtype = msgs->get_custom_type((char *)ev->EventsType);
71    if (mtype < 0) {
72       mtype = M_EVENTS;
73    }
74 
75    Jmsg(jcr, mtype, 0, "%s\n", rbuf.c_str());
76 
77    unbash_spaces(ev->EventsSource);
78    unbash_spaces(ev->EventsDaemon);
79 }
80 
scan_line(const char * line)81 bool EVENTS_DBR::scan_line(const char *line)
82 {
83    if (scan_string(line, "Events: code=%127s daemon=%127s ref=%llx type=%127s source=%127s text=",
84                    EventsCode, EventsDaemon, &EventsRef, EventsType, EventsSource) != 5) {
85       Dmsg1(0, "Malformed Audit message [%s]\n", line);
86       return false;             /* invalid format */
87    }
88    unbash_spaces(EventsSource);
89    unbash_spaces(EventsDaemon);
90    EventsText = bstrdup(strstr(line, "text=") + 5);
91    strip_trailing_junk(EventsText);
92    return true;
93 }
94 
95 /**********************************************************************
96  * Custom Events code - Used to let the user control Events in Messages
97  **********************************************************************
98  *
99  * A custom event can be generated with the .events bconsole command. It is
100  * also possible to generate a new event from the code with the
101  * events_send_msg() function. The argument "Type" is not checked and is free.
102  *
103  * .events type="bweb" source="myscript" ref=1 text="This is an event"
104  *
105  * Users can create events as many as they want, but we have some limits for
106  * the Messages filtering system. We can drive 32 message type, and 17 are used
107  * for regular job messages (warning, skipped, saved, error, info, warning...).
108  *
109  * It is possible to increase the number of events by changing the code (int)
110  * to a int64 for example.
111  *
112  */
113 
114 /* Stored in the MSGS resource in custom_type rblist */
115 struct CUSTOM_TYPE {
116    rblink link;
117    int    code;
118    char   kw[1];
119 };
120 
121 /* RBLIST function to look a custom event name */
custom_type_lookup(void * a,void * b)122 static int custom_type_lookup(void *a, void *b)
123 {
124    const char  *s = (const char *)a;
125    CUSTOM_TYPE *t = (CUSTOM_TYPE*)b;
126    return strcasecmp(s, t->kw);
127 }
128 
129 /* RBLIST function to insert a custom event name */
custom_type_insert(void * a,void * b)130 static int custom_type_insert(void *a, void *b)
131 {
132    CUSTOM_TYPE *e1 = (CUSTOM_TYPE *)a;
133    CUSTOM_TYPE *e2 = (CUSTOM_TYPE *)b;
134    return strcasecmp(e1->kw, e2->kw);
135 }
136 
137 /* MESSAGES resource is copied for each job. We need
138  * to make sure the memory is not shared between instances
139  */
custom_type_copy(MSGS * dest,MSGS * src)140 void custom_type_copy(MSGS *dest, MSGS *src)
141 {
142    CUSTOM_TYPE *elt=NULL;
143    dest->custom_type_current_index = src->custom_type_current_index;
144    if (src->custom_type) {
145       dest->custom_type = New(rblist(elt, &elt->link));
146       foreach_rblist(elt, src->custom_type) {
147          CUSTOM_TYPE *elt2 = (CUSTOM_TYPE *)malloc(sizeof(CUSTOM_TYPE)+strlen(elt->kw)+1);
148          elt2->code = elt->code;
149          strcpy(elt2->kw, elt->kw);
150          dest->custom_type->insert(elt2, custom_type_insert);
151       }
152    } else {
153       dest->custom_type = NULL;
154    }
155 }
156 
157 /* Add a custom event type
158  * > M_MAX => new custom type
159  * M_DEBUG => ignored
160  * -2      => incorrect format
161  * -1      => too much element
162  */
add_custom_type(bool is_not,char * type)163 int MSGS::add_custom_type(bool is_not, char *type)
164 {
165    CUSTOM_TYPE *t = NULL;
166    if (!type || *type == 0) {
167       return -2;                /* Incorrect format */
168    }
169 
170    if (custom_type == NULL) {
171       custom_type = New(rblist(t, &t->link));
172    }
173 
174    if (custom_type_current_index >= (int)M_EVENTS_LIMIT) {
175       return -1;                /* Too much elements */
176    }
177 
178    int len = strlen(type);
179    t = (CUSTOM_TYPE*) malloc(sizeof(CUSTOM_TYPE)+len+1);
180    bstrncpy(t->kw, type, len+1);
181    CUSTOM_TYPE *t2 = (CUSTOM_TYPE*) custom_type->insert(t, custom_type_insert);
182    if (t2 == t) {
183       custom_type_current_index = MAX(M_ALL, custom_type_current_index);
184       t2->code = ++custom_type_current_index;
185       Dmsg2(50, "Add custom type [Events.%s] = %d\n", t2->kw, t2->code);
186    } else {
187       free(t);                 /* Already in */
188    }
189    return t2->code;
190 }
191 
192 /* Get an existing custom event name */
get_custom_type(char * type)193 int MSGS::get_custom_type(char *type)
194 {
195    CUSTOM_TYPE *t = NULL;
196    if (custom_type == NULL) {
197       return -1;
198    }
199    t = (CUSTOM_TYPE *)custom_type->search(type, custom_type_lookup);
200    if (t) {
201       return t->code;
202    }
203    return -1;
204 }
205 
edit_custom_type(POOLMEM ** edbuf,MSGS * msgs,char * msg_types)206 void edit_custom_type(POOLMEM **edbuf, MSGS *msgs, char *msg_types)
207 {
208    CUSTOM_TYPE *elt;
209    bool first_time = (*edbuf)[0] == '\0' || ((*edbuf)[0] == '[' && (*edbuf)[1] == '\0');
210 
211    if (msgs->custom_type == NULL) {
212       return;
213    }
214    foreach_rblist(elt, msgs->custom_type) {
215       if (bit_is_set(M_EVENTS, msg_types) == 0 &&
216           bit_is_set(elt->code, msg_types))
217       {
218          if (!first_time) {
219             pm_strcat(edbuf, ",");
220 
221          } else {
222             first_time = false;
223          }
224          pm_strcat(edbuf, "\"Events.");
225          pm_strcat(edbuf, elt->kw);
226          pm_strcat(edbuf, "\"");
227 
228       } else if (bit_is_set(M_EVENTS, msg_types) &&
229                  bit_is_set(elt->code, msg_types) == 0)
230       {
231          if (!first_time) {
232             pm_strcat(edbuf, ",");
233          } else {
234             first_time = false;
235          }
236          pm_strcat(edbuf, "\"!Events.");
237          pm_strcat(edbuf, elt->kw);
238          pm_strcat(edbuf, "\"");
239       }
240    }
241 }
242