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  *  Status packet definition that is used in both the SD and FD. It
21  *    permits Win32 to call output_status() and get the output back
22  *    at the callback address line by line, and for Linux code,
23  *    the output can be sent directly to a BSOCK.
24  *
25  *     Kern Sibbald, March MMVII
26  *
27  */
28 
29 #ifndef __STATUS_H_
30 #define __STATUS_H_
31 
32 #ifdef HAVE_GETRLIMIT
33    #include <sys/time.h>
34    #include <sys/resource.h>
35 #endif
36 
37 /*
38  * Packet to send to output_status()
39  */
40 class STATUS_PKT {
41 public:
42   BSOCK *bs;                       /* used on Unix machines */
43   void *context;                   /* Win32 */
44   void (*callback)(const char *msg, int len, void *context);  /* Win32 */
45   char api_opts[MAX_NAME_LENGTH];
46   int  api;                        /* set if we want API output, with api level */
47 
48   /* Methods */
STATUS_PKT()49   STATUS_PKT() { memset(this, 0, sizeof(STATUS_PKT)); };
~STATUS_PKT()50   ~STATUS_PKT() { };
51 };
52 
53 extern void output_status(STATUS_PKT *sp);
54 
55 /*
56  * Send to bsock (Director or Console)
57  */
sendit(const char * msg,int len,STATUS_PKT * sp)58 static void sendit(const char *msg, int len, STATUS_PKT *sp)
59 {
60    if (sp->bs) {
61       BSOCK *user = sp->bs;
62       user->msg = check_pool_memory_size(user->msg, len+1);
63       memcpy(user->msg, msg, len+1);
64       user->msglen = len+1;
65       user->send();
66    } else {
67       sp->callback(msg, len, sp->context);
68    }
69 }
70 
71 #ifndef STATUS_FUNCTIONS
72 #define STATUS_FUNCTIONS
73 
74 /* common to SD/FD */
list_terminated_jobs(STATUS_PKT * sp)75 static void list_terminated_jobs(STATUS_PKT *sp)
76 {
77    OutputWriter ow(sp->api_opts);
78    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
79    char level[10];
80    struct s_last_job *je;
81    const char *msg;
82    char *p;
83 
84    msg =  _("\nTerminated Jobs:\n");
85    if (!sp->api) sendit(msg, strlen(msg), sp);
86    if (last_jobs->size() == 0) {
87       if (!sp->api) sendit("====\n", 5, sp);
88       return;
89    }
90    lock_last_jobs_list();
91    msg =  _(" JobId  Level    Files      Bytes   Status   Finished        Name \n");
92    if (!sp->api) sendit(msg, strlen(msg), sp);
93    msg =  _("===================================================================\n");
94    if (!sp->api) sendit(msg, strlen(msg), sp);
95    if (sp->api > 1) {
96       p = ow.start_group("terminated");
97       sendit(p, strlen(p), sp);
98    }
99    foreach_dlist(je, last_jobs) {
100       char JobName[MAX_NAME_LENGTH];
101       const char *termstat;
102       char buf[1000];
103 
104       bstrftime_nc(dt, sizeof(dt), je->end_time);
105       switch (je->JobType) {
106       case JT_ADMIN:
107          bstrncpy(level, "Admn", sizeof(level));
108          break;
109       case JT_RESTORE:
110          bstrncpy(level, "Rest", sizeof(level));
111          break;
112       default:
113          bstrncpy(level, job_level_to_str(je->JobLevel), sizeof(level));
114          level[4] = 0;
115          break;
116       }
117       switch (je->JobStatus) {
118       case JS_Created:
119          termstat = _("Created");
120          break;
121       case JS_FatalError:
122       case JS_ErrorTerminated:
123          termstat = _("Error");
124          break;
125       case JS_Differences:
126          termstat = _("Diffs");
127          break;
128       case JS_Canceled:
129          termstat = _("Cancel");
130          break;
131       case JS_Terminated:
132          termstat = _("OK");
133          break;
134       case JS_Warnings:
135          termstat = _("OK -- with warnings");
136          break;
137       case JS_Incomplete:
138          termstat = _("Incomplete");
139          break;
140       default:
141          termstat = _("Other");
142          break;
143       }
144       bstrncpy(JobName, je->Job, sizeof(JobName));
145       /* There are three periods after the Job name */
146       char *p;
147       for (int i=0; i<3; i++) {
148          if ((p=strrchr(JobName, '.')) != NULL) {
149             *p = 0;
150          }
151       }
152       if (sp->api == 1) {
153          bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
154             je->JobId,
155             level,
156             edit_uint64_with_commas(je->JobFiles, b1),
157             edit_uint64_with_suffix(je->JobBytes, b2),
158             termstat,
159             dt, JobName);
160 
161       } else if (sp->api > 1) {
162          p = ow.get_output(OT_CLEAR,
163                            OT_START_OBJ,
164                            OT_INT,     "jobid",     je->JobId,
165                            OT_JOBLEVEL,"level",     je->JobLevel,
166                            OT_JOBTYPE, "type",      je->JobType,
167                            OT_JOBSTATUS,"status",    je->JobStatus,
168                            OT_STRING,  "status_desc",termstat,
169                            OT_SIZE,    "jobbytes",  je->JobBytes,
170                            OT_INT32,   "jobfiles",  je->JobFiles,
171                            OT_STRING,  "job",       je->Job,
172                            OT_STRING,  "name",      JobName,
173                            OT_UTIME,   "starttime", je->start_time,
174                            OT_UTIME,   "endtime",   je->end_time,
175                            OT_INT,     "errors",    je->Errors,
176                            OT_END_OBJ,
177                            OT_END);
178          sendit(p, strlen(p), sp);
179 
180 
181       } else {
182          bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %10s  %-7s  %-8s %s\n"),
183             je->JobId,
184             level,
185             edit_uint64_with_commas(je->JobFiles, b1),
186             edit_uint64_with_suffix(je->JobBytes, b2),
187             termstat,
188             dt, JobName);
189       }
190       sendit(buf, strlen(buf), sp);
191    }
192    unlock_last_jobs_list();
193    if (!sp->api) {
194       sendit("====\n", 5, sp);
195    } else if (sp->api > 1) {
196       p = ow.end_group(false);
197       sendit(p, strlen(p), sp);
198    }
199 }
200 
201 /* common to SD/FD/DIR */
list_resource_limits(STATUS_PKT * sp,int64_t l_nofile,int64_t l_memlock)202 static void list_resource_limits(STATUS_PKT *sp, int64_t l_nofile, int64_t l_memlock)
203 {
204 #ifdef HAVE_GETRLIMIT
205    OutputWriter ow(sp->api_opts);
206    POOL_MEM msg(PM_MESSAGE), msg_status(PM_MESSAGE);
207    struct rlimit rlim;
208    int64_t nofile=-1, memlock=-1;
209    char nofile_s[128], memlock_s[128];
210    *nofile_s = *memlock_s = '\0';
211 
212    msg_status.strcpy("");
213 #ifdef RLIMIT_NOFILE
214    if (getrlimit(RLIMIT_NOFILE, &rlim)==0) {
215       if (rlim.rlim_cur == RLIM_INFINITY) {
216          nofile=-1;
217          bstrncpy(nofile_s, "unlimited", sizeof(nofile_s));
218       } else {
219          nofile=rlim.rlim_cur;
220          edit_int64(nofile, nofile_s);
221          if (l_nofile > 0 && nofile<l_nofile) {
222             msg_status.strcat("nofile ");
223          }
224       }
225    }
226 #endif
227 #ifdef RLIMIT_MEMLOCK
228    if (getrlimit(RLIMIT_MEMLOCK, &rlim)==0) {
229       if (rlim.rlim_cur == RLIM_INFINITY) {
230          memlock=-1;
231          bstrncpy(memlock_s, "unlimited", sizeof(memlock_s));
232       } else {
233          memlock=rlim.rlim_cur;
234          edit_int64(memlock, memlock_s);
235          if (l_memlock > 0 && memlock<l_memlock) {
236             msg_status.strcat("memlock ");
237          }
238       }
239    }
240 #endif
241 
242    if (strlen(msg_status.c_str())>0) {
243       strip_trailing_junk(msg_status.c_str());
244    } else {
245       msg_status.strcpy("ok");
246    }
247 
248    if (sp->api > 1) {
249       OutputWriter ow(sp->api_opts);
250       char *p;
251       ow.start_group("ulimit");
252       ow.get_output(    OT_START_OBJ,
253                         OT_INT64,   "nofile",   nofile,
254                         OT_INT64,   "memlock",  memlock,
255                         OT_STRING,  "status",   msg_status.c_str(),
256                         OT_END_OBJ,
257                         OT_END);
258       p = ow.end_group(); // dedupengine
259       sendit(p, strlen(p), sp);
260    } else {
261       int len = Mmsg(msg, _(" Ulimits: nofile=%s memlock=%s status=%s\n"),
262             nofile_s, memlock_s, msg_status.c_str());
263       sendit(msg.c_str(), len, sp);
264    }
265 #endif
266 }
267 
268 #if defined(HAVE_WIN32)
269 int bacstat = 0;
270 
271 #ifdef FILE_DAEMON
272 # define BAC_COMPONENT "Client"
273 #else
274 # define BAC_COMPONENT "Storage"
275 #endif
276 
277 /* Return a one line status for the tray monitor */
bac_status(char * buf,int buf_len)278 char *bac_status(char *buf, int buf_len)
279 {
280    JCR *njcr;
281    const char *termstat = _("Bacula " BAC_COMPONENT ": Idle");
282    struct s_last_job *job;
283    int stat = 0;                      /* Idle */
284 
285    if (!last_jobs) {
286       goto done;
287    }
288    Dmsg0(1000, "Begin bac_status jcr loop.\n");
289    foreach_jcr(njcr) {
290       if (njcr->JobId != 0) {
291          stat = JS_Running;
292          termstat = _("Bacula " BAC_COMPONENT ": Running");
293          break;
294       }
295    }
296    endeach_jcr(njcr);
297 
298    if (stat != 0) {
299       goto done;
300    }
301    if (last_jobs->size() > 0) {
302       job = (struct s_last_job *)last_jobs->last();
303       stat = job->JobStatus;
304       switch (job->JobStatus) {
305       case JS_Canceled:
306          termstat = _("Bacula " BAC_COMPONENT ": Last Job Canceled");
307          break;
308       case JS_ErrorTerminated:
309       case JS_FatalError:
310          termstat = _("Bacula " BAC_COMPONENT ": Last Job Failed");
311          break;
312       default:
313          if (job->Errors) {
314             termstat = _("Bacula " BAC_COMPONENT ": Last Job had Warnings");
315          }
316          break;
317       }
318    }
319    Dmsg0(1000, "End bac_status jcr loop.\n");
320 done:
321    bacstat = stat;
322    if (buf) {
323       bstrncpy(buf, termstat, buf_len);
324    }
325    return buf;
326 }
327 
328 #endif /* HAVE_WIN32 */
329 
330 #endif  /* ! STATUS_FUNCTIONS */
331 
332 #endif
333