1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2002-2011 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2016 Planets Communications B.V.
6    Copyright (C) 2013-2019 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22 */
23 /*
24  * Kern Sibbald, April MMII
25  */
26 /**
27  * @file
28  * User Agent Commands
29  *
30  * These are "dot" commands, i.e. commands preceded
31  * by a period. These commands are meant to be used
32  * by a program, so there is no prompting, and the
33  * returned results are (supposed to be) predictable.
34  */
35 
36 #include "include/bareos.h"
37 #include "dird.h"
38 #include "dird/jcr_private.h"
39 #include "dird/job.h"
40 #include "dird/dird_globals.h"
41 #include "dird/sd_cmds.h"
42 #include "dird/fd_cmds.h"
43 #include "cats/bvfs.h"
44 #include "findlib/find.h"
45 #include "dird/ua_db.h"
46 #include "dird/ua_select.h"
47 #include "dird/storage.h"
48 #include "include/auth_protocol_types.h"
49 #include "lib/edit.h"
50 #include "lib/parse_conf.h"
51 #include "lib/util.h"
52 
53 namespace directordaemon {
54 
55 /* Imported variables */
56 extern struct s_jl joblevels[];
57 extern struct s_jt jobtypes[];
58 extern struct s_kw ActionOnPurgeOptions[];
59 extern struct s_kw VolumeStatus[];
60 
61 /* Imported functions */
62 
63 #ifdef DEVELOPER
64 /* ua_cmds.c */
65 extern bool quit_cmd(UaContext* ua, const char* cmd);
66 #endif
67 
68 /* ua_output.c */
69 extern void DoMessages(UaContext* ua, const char* cmd);
70 
71 struct authorization_mapping {
72   const char* type;
73   int acl_type;
74 };
75 
76 static authorization_mapping authorization_mappings[] = {
77     {"job", Job_ACL},
78     {"client", Client_ACL},
79     {"storage", Storage_ACL},
80     {"schedule", Schedule_ACL},
81     {"pool", Pool_ACL},
82     {"cmd", Command_ACL},
83     {"fileset", FileSet_ACL},
84     {"catalog", Catalog_ACL},
85     {NULL, 0},
86 };
87 
DotAuthorizedCmd(UaContext * ua,const char * cmd)88 bool DotAuthorizedCmd(UaContext* ua, const char* cmd)
89 {
90   bool retval = false;
91 
92   for (int i = 1; i < ua->argc; i++) {
93     for (int j = 0; authorization_mappings[j].type; j++) {
94       if (Bstrcasecmp(ua->argk[i], authorization_mappings[j].type)) {
95         if (ua->argv[i] && ua->AclAccessOk(authorization_mappings[j].acl_type,
96                                            ua->argv[i], false)) {
97           retval = true;
98         } else {
99           retval = false;
100         }
101       }
102     }
103   }
104 
105   ua->send->ObjectStart(".authorized");
106   if (retval) {
107     ua->send->ObjectKeyValueBool("authorized", retval, "authorized\n");
108   } else {
109     ua->send->ObjectKeyValueBool("authorized", retval, "not authorized\n");
110   }
111   ua->send->ObjectEnd(".authorized");
112 
113   return retval;
114 }
115 
DotBvfsUpdateCmd(UaContext * ua,const char * cmd)116 bool DotBvfsUpdateCmd(UaContext* ua, const char* cmd)
117 {
118   int pos;
119 
120   if (!OpenClientDb(ua, true)) { return 1; }
121   pos = FindArgWithValue(ua, "jobid");
122   if (pos != -1 && Is_a_number_list(ua->argv[pos])) {
123     if (!ua->db->BvfsUpdatePathHierarchyCache(ua->jcr, ua->argv[pos])) {
124       ua->ErrorMsg("ERROR: BVFS reported a problem for %s\n", ua->argv[pos]);
125     }
126   } else {
127     /* update cache for all jobids */
128     ua->db->BvfsUpdateCache(ua->jcr);
129   }
130 
131   return true;
132 }
133 
DotBvfsClearCacheCmd(UaContext * ua,const char * cmd)134 bool DotBvfsClearCacheCmd(UaContext* ua, const char* cmd)
135 {
136   if (!OpenClientDb(ua, true)) { return 1; }
137 
138   int pos = FindArg(ua, "yes");
139   if (pos != -1) {
140     Bvfs fs(ua->jcr, ua->db);
141     fs.clear_cache();
142     ua->InfoMsg("OK\n");
143   } else {
144     ua->ErrorMsg("Can't find 'yes' argument\n");
145   }
146 
147   return true;
148 }
149 
BvfsStat(UaContext * ua,char * lstat,int32_t * LinkFI)150 static int BvfsStat(UaContext* ua, char* lstat, int32_t* LinkFI)
151 {
152   struct stat statp;
153   char en1[30], en2[30];
154 
155   memset(&statp, 0, sizeof(struct stat));
156   DecodeStat(lstat, &statp, sizeof(statp), LinkFI);
157 
158   ua->send->ObjectStart("stat");
159   ua->send->ObjectKeyValue("dev", statp.st_dev);
160   ua->send->ObjectKeyValue("ino", statp.st_ino);
161   ua->send->ObjectKeyValue("mode", statp.st_mode);
162   ua->send->ObjectKeyValue("nlink", statp.st_nlink);
163   ua->send->ObjectKeyValue(
164       "user", ua->guid->uid_to_name(statp.st_uid, en1, sizeof(en1)));
165   ua->send->ObjectKeyValue(
166       "group", ua->guid->gid_to_name(statp.st_gid, en2, sizeof(en2)));
167   ua->send->ObjectKeyValue("rdev", statp.st_rdev);
168   ua->send->ObjectKeyValue("size", statp.st_size);
169   ua->send->ObjectKeyValue("atime", statp.st_atime);
170   ua->send->ObjectKeyValue("mtime", statp.st_mtime);
171   ua->send->ObjectKeyValue("ctime", statp.st_ctime);
172   ua->send->ObjectEnd("stat");
173 
174   return 0;
175 }
176 
BvfsResultHandler(void * ctx,int fields,char ** row)177 static int BvfsResultHandler(void* ctx, int fields, char** row)
178 {
179   UaContext* ua = (UaContext*)ctx;
180   char* fileid = row[BVFS_FileId];
181   char* lstat = row[BVFS_LStat];
182   char* jobid = row[BVFS_JobId];
183 
184   char empty[] = "A A A A A A A A A A A A A A";
185   char zero[] = "0";
186   int32_t LinkFI = 0;
187 
188   /*
189    * We need to deal with non existant path
190    */
191   if (!fileid || !Is_a_number(fileid)) {
192     lstat = empty;
193     jobid = zero;
194     fileid = zero;
195   }
196 
197   Dmsg1(100, "type=%s\n", row[0]);
198   if (BvfsIsDir(row)) {
199     char* path = bvfs_basename_dir(row[BVFS_Name]);
200 
201     ua->send->ObjectStart();
202     ua->send->ObjectKeyValue("Type", row[BVFS_Type]);
203     ua->send->ObjectKeyValue("PathId", str_to_uint64(row[BVFS_PathId]),
204                              "%lld\t");
205     ua->send->ObjectKeyValue("FileId", str_to_uint64(fileid), "%lld\t");
206     ua->send->ObjectKeyValue("JobId", str_to_uint64(jobid), "%lld\t");
207     ua->send->ObjectKeyValue("lstat", lstat, "%s\t");
208     ua->send->ObjectKeyValue("Name", path, "%s\n");
209     ua->send->ObjectKeyValue("Fullpath", row[BVFS_Name]);
210     BvfsStat(ua, lstat, &LinkFI);
211     ua->send->ObjectKeyValue("LinkFileIndex", LinkFI);
212     ua->send->ObjectEnd();
213   } else if (BvfsIsVersion(row)) {
214     ua->send->ObjectStart();
215     ua->send->ObjectKeyValue("Type", row[BVFS_Type]);
216     ua->send->ObjectKeyValue("PathId", str_to_uint64(row[BVFS_PathId]),
217                              "%lld\t");
218     ua->send->ObjectKeyValue("FileId", str_to_uint64(fileid), "%lld\t");
219     ua->send->ObjectKeyValue("JobId", str_to_uint64(jobid), "%lld\t");
220     ua->send->ObjectKeyValue("lstat", lstat, "%s\t");
221     ua->send->ObjectKeyValue("MD5", row[BVFS_Md5], "%s\t");
222     ua->send->ObjectKeyValue("VolumeName", row[BVFS_VolName], "%s\t");
223     ua->send->ObjectKeyValue("VolumeInChanger",
224                              str_to_uint64(row[BVFS_VolInchanger]), "%lld\n");
225     BvfsStat(ua, lstat, &LinkFI);
226     ua->send->ObjectEnd();
227   } else if (BvfsIsFile(row)) {
228     ua->send->ObjectStart();
229     ua->send->ObjectKeyValue("Type", row[BVFS_Type]);
230     ua->send->ObjectKeyValue("PathId", str_to_uint64(row[BVFS_PathId]),
231                              "%lld\t");
232     ua->send->ObjectKeyValue("FileId", str_to_uint64(fileid), "%lld\t");
233     ua->send->ObjectKeyValue("JobId", str_to_uint64(jobid), "%lld\t");
234     ua->send->ObjectKeyValue("lstat", lstat, "%s\t");
235     ua->send->ObjectKeyValue("Name", row[BVFS_Name], "%s\n");
236     BvfsStat(ua, lstat, &LinkFI);
237     ua->send->ObjectKeyValue("LinkFileIndex", LinkFI);
238     ua->send->ObjectEnd();
239   }
240 
241   return 0;
242 }
243 
BvfsParseArgVersion(UaContext * ua,char ** client,char ** fname,bool * versions,bool * copies)244 static inline bool BvfsParseArgVersion(UaContext* ua,
245                                        char** client,
246                                        char** fname,
247                                        bool* versions,
248                                        bool* copies)
249 {
250   *fname = NULL;
251   *client = NULL;
252   *versions = false;
253   *copies = false;
254 
255   for (int i = 1; i < ua->argc; i++) {
256     if (Bstrcasecmp(ua->argk[i], NT_("name")) ||
257         Bstrcasecmp(ua->argk[i], NT_("fname")) ||
258         Bstrcasecmp(ua->argk[i], NT_("filename"))) {
259       *fname = ua->argv[i];
260     }
261 
262     if (Bstrcasecmp(ua->argk[i], NT_("client"))) { *client = ua->argv[i]; }
263 
264     if (copies && Bstrcasecmp(ua->argk[i], NT_("copies"))) { *copies = true; }
265 
266     if (versions && Bstrcasecmp(ua->argk[i], NT_("versions"))) {
267       *versions = true;
268     }
269   }
270 
271   return (*client && *fname);
272 }
273 
BvfsParseArg(UaContext * ua,DBId_t * pathid,char ** path,char ** jobid,int * limit,int * offset)274 static bool BvfsParseArg(UaContext* ua,
275                          DBId_t* pathid,
276                          char** path,
277                          char** jobid,
278                          int* limit,
279                          int* offset)
280 {
281   *pathid = 0;
282   *limit = 2000;
283   *offset = 0;
284   *path = NULL;
285   *jobid = NULL;
286 
287   for (int i = 1; i < ua->argc; i++) {
288     if (Bstrcasecmp(ua->argk[i], NT_("pathid"))) {
289       if (ua->argv[i] && Is_a_number(ua->argv[i])) {
290         *pathid = str_to_int64(ua->argv[i]);
291       }
292     }
293 
294     if (Bstrcasecmp(ua->argk[i], NT_("path"))) { *path = ua->argv[i]; }
295 
296     if (Bstrcasecmp(ua->argk[i], NT_("jobid"))) {
297       if (ua->argv[i] && Is_a_number_list(ua->argv[i])) {
298         *jobid = ua->argv[i];
299       }
300     }
301 
302     if (Bstrcasecmp(ua->argk[i], NT_("limit"))) {
303       if (ua->argv[i] && Is_a_number(ua->argv[i])) {
304         *limit = str_to_int64(ua->argv[i]);
305       }
306     }
307 
308     if (Bstrcasecmp(ua->argk[i], NT_("offset"))) {
309       if (ua->argv[i] && Is_a_number(ua->argv[i])) {
310         *offset = str_to_int64(ua->argv[i]);
311       }
312     }
313   }
314 
315   if (!((*pathid || *path) && *jobid)) { return false; }
316 
317   if (!OpenClientDb(ua, true)) { return false; }
318 
319   return true;
320 }
321 
322 /**
323  * This checks to see if the JobId given is allowed under the current
324  * ACLs e.g. comparing the JobName against the Job_ACL and the client
325  * against the Client_ACL.
326  */
BvfsValidateJobid(UaContext * ua,const char * jobid,bool audit_event)327 static inline bool BvfsValidateJobid(UaContext* ua,
328                                      const char* jobid,
329                                      bool audit_event)
330 {
331   JobDbRecord jr;
332   ClientDbRecord cr;
333   bool retval = false;
334 
335   jr.JobId = str_to_int64(jobid);
336 
337   if (ua->db->GetJobRecord(ua->jcr, &jr)) {
338     if (!ua->AclAccessOk(Job_ACL, jr.Name, audit_event)) { goto bail_out; }
339 
340     if (jr.ClientId) {
341       cr.ClientId = jr.ClientId;
342       if (ua->db->GetClientRecord(ua->jcr, &cr)) {
343         if (!ua->AclAccessOk(Client_ACL, cr.Name, audit_event)) {
344           goto bail_out;
345         }
346       }
347     }
348 
349     retval = true;
350   }
351 
352 bail_out:
353   return retval;
354 }
355 
356 /**
357  * This returns in filtered_jobids the list of allowed jobids in the
358  * jobids variable under the current ACLs e.g. using BvfsValidateJobid().
359  */
BvfsValidateJobids(UaContext * ua,const char * jobids,PoolMem & filtered_jobids,bool audit_event)360 static bool BvfsValidateJobids(UaContext* ua,
361                                const char* jobids,
362                                PoolMem& filtered_jobids,
363                                bool audit_event)
364 {
365   int cnt = 0;
366   char *cur_id, *bp;
367   PoolMem temp(PM_FNAME);
368 
369   PmStrcpy(temp, jobids);
370   PmStrcpy(filtered_jobids, "");
371 
372   cur_id = temp.c_str();
373   while (cur_id && strlen(cur_id)) {
374     bp = strchr(cur_id, ',');
375     if (bp) { *bp++ = '\0'; }
376 
377     /*
378      * See if this JobId is allowed under the current ACLs.
379      */
380     if (BvfsValidateJobid(ua, cur_id, audit_event)) {
381       if (!cnt) {
382         PmStrcpy(filtered_jobids, cur_id);
383       } else {
384         PmStrcat(filtered_jobids, ",");
385         PmStrcat(filtered_jobids, cur_id);
386       }
387       cnt++;
388     } else {
389       Dmsg1(200, "Removing jobid from list, %s\n", cur_id);
390     }
391 
392     cur_id = bp;
393   }
394 
395   return (cnt > 0) ? true : false;
396 }
397 
398 /**
399  * .bvfs_cleanup path=b2XXXXX
400  */
DotBvfsCleanupCmd(UaContext * ua,const char * cmd)401 bool DotBvfsCleanupCmd(UaContext* ua, const char* cmd)
402 {
403   int i;
404 
405   if ((i = FindArgWithValue(ua, "path")) < 0) {
406     ua->ErrorMsg("Can't find path argument\n");
407     return false; /* not enough param */
408   }
409 
410   if (!OpenClientDb(ua, true)) { return false; }
411 
412   Bvfs fs(ua->jcr, ua->db);
413   fs.DropRestoreList(ua->argv[i]);
414 
415   return true;
416 }
417 
418 /**
419  * .bvfs_restore path=b2XXXXX jobid=1,2 fileid=1,2 dirid=1,2 hardlink=1,2,3,4
420  */
DotBvfsRestoreCmd(UaContext * ua,const char * cmd)421 bool DotBvfsRestoreCmd(UaContext* ua, const char* cmd)
422 {
423   DBId_t pathid = 0;
424   char* empty = (char*)"";
425   int limit = 2000, offset = 0;
426   int i = 0;
427   char *path = NULL, *jobid = NULL;
428   char *fileid, *dirid, *hardlink;
429   PoolMem filtered_jobids(PM_FNAME);
430 
431   fileid = dirid = hardlink = empty;
432   if (!BvfsParseArg(ua, &pathid, &path, &jobid, &limit, &offset)) {
433     ua->ErrorMsg("Can't find jobid, pathid or path argument\n");
434     return false; /* not enough param */
435   }
436 
437   if (!BvfsValidateJobids(ua, jobid, filtered_jobids, true)) {
438     ua->ErrorMsg(_("Unauthorized command from this console.\n"));
439     return false;
440   }
441 
442   Bvfs fs(ua->jcr, ua->db);
443   fs.SetJobids(filtered_jobids.c_str());
444 
445   if ((i = FindArgWithValue(ua, "fileid")) >= 0) { fileid = ua->argv[i]; }
446   if ((i = FindArgWithValue(ua, "dirid")) >= 0) { dirid = ua->argv[i]; }
447   if ((i = FindArgWithValue(ua, "hardlink")) >= 0) { hardlink = ua->argv[i]; }
448 
449   if (fs.compute_restore_list(fileid, dirid, hardlink, path)) {
450     ua->SendMsg("OK\n");
451   } else {
452     ua->ErrorMsg("Can't create restore list\n");
453   }
454 
455   return true;
456 }
457 
458 /**
459  * .bvfs_lsfiles jobid=1,2,3,4 path=/
460  * .bvfs_lsfiles jobid=1,2,3,4 pathid=10
461  */
DotBvfsLsfilesCmd(UaContext * ua,const char * cmd)462 bool DotBvfsLsfilesCmd(UaContext* ua, const char* cmd)
463 {
464   int i;
465   DBId_t pathid = 0;
466   char* pattern = NULL;
467   int limit = 2000, offset = 0;
468   char *path = NULL, *jobid = NULL;
469   PoolMem filtered_jobids(PM_FNAME);
470 
471   if (!BvfsParseArg(ua, &pathid, &path, &jobid, &limit, &offset)) {
472     ua->ErrorMsg("Can't find jobid, pathid or path argument\n");
473     return false; /* not enough param */
474   }
475 
476   if (!BvfsValidateJobids(ua, jobid, filtered_jobids, true)) {
477     ua->ErrorMsg(_("Unauthorized command from this console.\n"));
478     return false;
479   }
480 
481   if ((i = FindArgWithValue(ua, "pattern")) >= 0) { pattern = ua->argv[i]; }
482 
483   if (!ua->guid) { ua->guid = new_guid_list(); }
484 
485   Bvfs fs(ua->jcr, ua->db);
486   fs.SetJobids(filtered_jobids.c_str());
487   fs.SetHandler(BvfsResultHandler, ua);
488   fs.SetLimit(limit);
489   if (pattern) { fs.SetPattern(pattern); }
490   if (pathid) {
491     fs.ChDir(pathid);
492   } else {
493     fs.ChDir(path);
494   }
495 
496   fs.SetOffset(offset);
497 
498   ua->send->ArrayStart("files");
499   fs.ls_files();
500   ua->send->ArrayEnd("files");
501 
502   return true;
503 }
504 
505 /**
506  * .bvfs_lsdirs jobid=1,2,3,4 path=
507  * .bvfs_lsdirs jobid=1,2,3,4 path=/
508  * .bvfs_lsdirs jobid=1,2,3,4 pathid=10
509  */
DotBvfsLsdirsCmd(UaContext * ua,const char * cmd)510 bool DotBvfsLsdirsCmd(UaContext* ua, const char* cmd)
511 {
512   DBId_t pathid = 0;
513   int limit = 2000, offset = 0;
514   char *path = NULL, *jobid = NULL;
515   PoolMem filtered_jobids(PM_FNAME);
516 
517   if (!BvfsParseArg(ua, &pathid, &path, &jobid, &limit, &offset)) {
518     ua->ErrorMsg("Can't find jobid, pathid or path argument\n");
519     return true; /* not enough param */
520   }
521 
522   if (!BvfsValidateJobids(ua, jobid, filtered_jobids, true)) {
523     ua->ErrorMsg(_("Unauthorized command from this console.\n"));
524     return false;
525   }
526 
527   if (!ua->guid) { ua->guid = new_guid_list(); }
528 
529   Bvfs fs(ua->jcr, ua->db);
530   fs.SetJobids(filtered_jobids.c_str());
531 
532   if (pathid) {
533     fs.ChDir(pathid);
534   } else {
535     if (!fs.ChDir(path)) {
536       /* path could not be found. Giving up. */
537       return false;
538     }
539   }
540 
541   fs.SetHandler(BvfsResultHandler, ua);
542   fs.SetOffset(offset);
543   fs.SetLimit(limit);
544 
545   ua->send->ArrayStart("directories");
546   fs.ls_dirs();
547   ua->send->ArrayEnd("directories");
548 
549   return true;
550 }
551 
552 /**
553  * .bvfs_versions jobid=0 client=<client-name> filename=<file-name>
554  * pathid=<number> [copies] [versions]
555  *
556  * jobid isn't used.
557  * versions is set, but not used.
558  */
DotBvfsVersionsCmd(UaContext * ua,const char * cmd)559 bool DotBvfsVersionsCmd(UaContext* ua, const char* cmd)
560 {
561   DBId_t pathid = 0;
562   int limit = 2000, offset = 0;
563   char *path = NULL, *jobid = NULL, *client = NULL, *fname = NULL;
564   bool copies = false, versions = false;
565 
566   if (!BvfsParseArg(ua, &pathid, &path, &jobid, &limit, &offset)) {
567     ua->ErrorMsg("Can't find jobid, pathid or path argument\n");
568     return false; /* not enough param */
569   }
570 
571   if (!BvfsParseArgVersion(ua, &client, &fname, &versions, &copies)) {
572     ua->ErrorMsg("Can't find client or fname argument\n");
573     return false; /* not enough param */
574   }
575 
576   if (!ua->AclAccessOk(Client_ACL, client)) {
577     ua->ErrorMsg(_("Unauthorized command from this console.\n"));
578     return false;
579   }
580 
581   if (!ua->guid) { ua->guid = new_guid_list(); }
582 
583   Bvfs fs(ua->jcr, ua->db);
584   fs.SetSeeAllVersions(versions);
585   fs.SetSeeCopies(copies);
586   fs.SetHandler(BvfsResultHandler, ua);
587   fs.SetLimit(limit);
588   fs.SetOffset(offset);
589   ua->send->ArrayStart("versions");
590   if (pathid) {
591     fs.GetAllFileVersions(pathid, fname, client);
592   } else {
593     fs.GetAllFileVersions(path, fname, client);
594   }
595   ua->send->ArrayEnd("versions");
596 
597   return true;
598 }
599 
600 /**
601  * .bvfs_get_jobids jobid=1
602  *  -> returns needed jobids to restore
603  * .bvfs_get_jobids jobid=1 all
604  *  -> returns needed jobids to restore with all filesets a JobId=1 time
605  * .bvfs_get_jobids ujobid=JobName
606  *  -> returns needed jobids to restore
607  */
DotBvfsGetJobidsCmd(UaContext * ua,const char * cmd)608 bool DotBvfsGetJobidsCmd(UaContext* ua, const char* cmd)
609 {
610   int pos;
611   JobDbRecord jr;
612   char ed1[50];
613   dbid_list ids; /* Store all FileSetIds for this client */
614   PoolMem query;
615   db_list_ctx jobids, tempids;
616   PoolMem filtered_jobids(PM_FNAME);
617 
618   if (!OpenClientDb(ua, true)) { return true; }
619 
620   if ((pos = FindArgWithValue(ua, "ujobid")) >= 0) {
621     bstrncpy(jr.Job, ua->argv[pos], sizeof(jr.Job));
622   } else if ((pos = FindArgWithValue(ua, "jobid")) >= 0) {
623     jr.JobId = str_to_int64(ua->argv[pos]);
624   } else {
625     ua->ErrorMsg(_("Can't find ujobid or jobid argument\n"));
626     return false;
627   }
628 
629   if (!ua->db->GetJobRecord(ua->jcr, &jr)) {
630     ua->ErrorMsg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
631                  ua->argv[pos], ua->db->strerror());
632     return false;
633   }
634 
635   /*
636    * When in level base, we don't rely on any Full/Incr/Diff
637    */
638   if (jr.JobLevel == L_BASE) {
639     jobids.add(edit_int64(jr.JobId, ed1));
640   } else {
641     /*
642      * If we have the "all" option, we do a search on all defined fileset for
643      * this client
644      */
645     if (FindArg(ua, "all") > 0) {
646       ua->db->FillQuery(query, BareosDb::SQL_QUERY::uar_sel_filesetid,
647                         edit_int64(jr.ClientId, ed1));
648       ua->db->GetQueryDbids(ua->jcr, query, ids);
649     } else {
650       ids.num_ids = 1;
651       ids.DBId[0] = jr.FileSetId;
652     }
653 
654     jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */
655 
656     /*
657      * Foreach different FileSet, we build a restore jobid list
658      */
659     for (int i = 0; i < ids.num_ids; i++) {
660       FileSetDbRecord fs;
661 
662       /*
663        * Lookup the FileSet.
664        */
665       fs.FileSetId = ids.DBId[i];
666       if (!ua->db->GetFilesetRecord(ua->jcr, &fs)) { continue; }
667 
668       /*
669        * Make sure the FileSet is allowed under the current ACLs.
670        */
671       if (!ua->AclAccessOk(FileSet_ACL, fs.FileSet, false)) { continue; }
672 
673       /*
674        * Lookup the actual JobIds for the given fileset.
675        */
676       jr.FileSetId = fs.FileSetId;
677       if (!ua->db->AccurateGetJobids(ua->jcr, &jr, &tempids)) { return true; }
678       jobids.add(tempids);
679     }
680   }
681 
682   BvfsValidateJobids(ua, jobids.list, filtered_jobids, false);
683   switch (ua->api) {
684     case API_MODE_JSON: {
685       char *cur_id, *bp;
686 
687       ua->send->ArrayStart("jobids");
688       cur_id = filtered_jobids.c_str();
689       while (cur_id && strlen(cur_id)) {
690         bp = strchr(cur_id, ',');
691         if (bp) { *bp++ = '\0'; }
692 
693         ua->send->ObjectStart();
694         ua->send->ObjectKeyValue("id", cur_id, "%s\n");
695         ua->send->ObjectEnd();
696 
697         cur_id = bp;
698       }
699       ua->send->ArrayEnd("jobids");
700       break;
701     }
702     default:
703       ua->SendMsg("%s\n", filtered_jobids.c_str());
704       break;
705   }
706 
707   return true;
708 }
709 
DotGetmsgsCmd(UaContext * ua,const char * cmd)710 bool DotGetmsgsCmd(UaContext* ua, const char* cmd)
711 {
712   if (console_msg_pending) { DoMessages(ua, cmd); }
713   return 1;
714 }
715 
716 #ifdef DEVELOPER
DoStorageCmd(UaContext * ua,StorageResource * store,const char * cmd)717 static void DoStorageCmd(UaContext* ua, StorageResource* store, const char* cmd)
718 {
719   BareosSocket* sd;
720   JobControlRecord* jcr = ua->jcr;
721   UnifiedStorageResource lstore;
722 
723   lstore.store = store;
724   PmStrcpy(lstore.store_source, _("unknown source"));
725   SetWstorage(jcr, &lstore);
726 
727   if (!(sd = open_sd_bsock(ua))) {
728     ua->ErrorMsg(_("Could not open SD socket.\n"));
729     return;
730   }
731 
732   Dmsg0(120, _("Connected to storage daemon\n"));
733   sd = jcr->store_bsock;
734   sd->fsend("%s", cmd);
735   if (sd->recv() >= 0) { ua->SendMsg("%s", sd->msg); }
736 
737   CloseSdBsock(ua);
738   return;
739 }
740 
DoClientCmd(UaContext * ua,ClientResource * client,const char * cmd)741 static void DoClientCmd(UaContext* ua, ClientResource* client, const char* cmd)
742 {
743   BareosSocket* fd;
744 
745   /* Connect to File daemon */
746 
747   ua->jcr->impl->res.client = client;
748   /* Try to connect for 15 seconds */
749   ua->SendMsg(_("Connecting to Client %s at %s:%d\n"), client->resource_name_,
750               client->address, client->FDport);
751   if (!ConnectToFileDaemon(ua->jcr, 1, 15, false, ua)) {
752     ua->ErrorMsg(_("Failed to connect to Client.\n"));
753     return;
754   }
755   Dmsg0(120, "Connected to file daemon\n");
756   fd = ua->jcr->file_bsock;
757   fd->fsend("%s", cmd);
758   if (fd->recv() >= 0) { ua->SendMsg("%s", fd->msg); }
759   fd->signal(BNET_TERMINATE);
760   fd->close();
761   ua->jcr->file_bsock = NULL;
762   return;
763 }
764 
765 /**
766  * .die (seg fault)
767  * .exit (no arg => .quit)
768  */
DotAdminCmds(UaContext * ua,const char * cmd)769 bool DotAdminCmds(UaContext* ua, const char* cmd)
770 {
771   int i, a;
772   JobControlRecord* jcr = NULL;
773   bool dir = false;
774   bool result = true;
775   const char* remote_cmd;
776   StorageResource* store = NULL;
777   ClientResource* client = NULL;
778   bool do_deadlock = false;
779   pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
780 
781   if (strncmp(ua->argk[0], ".die", 4) == 0) {
782     if (FindArg(ua, "deadlock") > 0) {
783       do_deadlock = true;
784       remote_cmd = ".die deadlock";
785     } else {
786       remote_cmd = ".die";
787     }
788   } else if (strncmp(ua->argk[0], ".exit", 5) == 0) {
789     remote_cmd = "exit";
790   } else {
791     ua->ErrorMsg(_("Unknown command: %s\n"), ua->argk[0]);
792     return true;
793   }
794 
795   /*
796    * General debug?
797    */
798   for (i = 1; i < ua->argc; i++) {
799     if (Bstrcasecmp(ua->argk[i], "dir") ||
800         Bstrcasecmp(ua->argk[i], "director")) {
801       dir = true;
802     }
803     if (Bstrcasecmp(ua->argk[i], "client") || Bstrcasecmp(ua->argk[i], "fd")) {
804       client = NULL;
805       if (ua->argv[i]) { client = ua->GetClientResWithName(ua->argv[i]); }
806       if (!client) { client = select_client_resource(ua); }
807     }
808 
809     if (Bstrcasecmp(ua->argk[i], NT_("store")) ||
810         Bstrcasecmp(ua->argk[i], NT_("storage")) ||
811         Bstrcasecmp(ua->argk[i], NT_("sd"))) {
812       store = NULL;
813       if (ua->argv[i]) { store = ua->GetStoreResWithName(ua->argv[i]); }
814       if (!store) { store = get_storage_resource(ua); }
815     }
816   }
817 
818   if (!dir && !store && !client) {
819     /*
820      * We didn't find an appropriate keyword above, so
821      * prompt the user.
822      */
823     StartPrompt(ua, _("Available daemons are: \n"));
824     AddPrompt(ua, _("Director"));
825     AddPrompt(ua, _("Storage"));
826     AddPrompt(ua, _("Client"));
827     switch (DoPrompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
828       case 0: /* Director */
829         dir = true;
830         break;
831       case 1:
832         store = get_storage_resource(ua);
833         break;
834       case 2:
835         client = select_client_resource(ua);
836         break;
837       default:
838         break;
839     }
840   }
841 
842   if (store) {
843     switch (store->Protocol) {
844       case APT_NDMPV2:
845       case APT_NDMPV3:
846       case APT_NDMPV4:
847         ua->WarningMsg(_("Storage has non-native protocol.\n"));
848         break;
849       default:
850         DoStorageCmd(ua, store, remote_cmd);
851         break;
852     }
853   }
854 
855   if (client) { DoClientCmd(ua, client, remote_cmd); }
856 
857   if (dir) {
858     if (strncmp(remote_cmd, ".die", 4) == 0) {
859       if (do_deadlock) {
860         ua->SendMsg(_("The Director will generate a deadlock.\n"));
861         P(mutex);
862         P(mutex);
863       }
864       ua->SendMsg(_("The Director will segment fault.\n"));
865       a = jcr->JobId;    /* ref NULL pointer */
866       jcr->JobId = 1000; /* another ref NULL pointer */
867       jcr->JobId = a;
868 
869     } else if (strncmp(remote_cmd, ".exit", 5) == 0) {
870       quit_cmd(ua, cmd);
871     }
872   }
873 
874   return result;
875 }
876 #else
877 /**
878  * Dummy routine for non-development version
879  */
DotAdminCmds(UaContext * ua,const char * cmd)880 bool DotAdminCmds(UaContext* ua, const char* cmd)
881 {
882   ua->ErrorMsg(_("Unknown command: %s\n"), ua->argk[0]);
883   return true;
884 }
885 #endif
886 
DotJobdefsCmd(UaContext * ua,const char * cmd)887 bool DotJobdefsCmd(UaContext* ua, const char* cmd)
888 {
889   JobResource* jobdefs;
890 
891   LockRes(my_config);
892   ua->send->ArrayStart("jobdefs");
893   foreach_res (jobdefs, R_JOBDEFS) {
894     if (ua->AclAccessOk(Job_ACL, jobdefs->resource_name_)) {
895       ua->send->ObjectStart();
896       ua->send->ObjectKeyValue("name", jobdefs->resource_name_, "%s\n");
897       ua->send->ObjectEnd();
898     }
899   }
900   ua->send->ArrayEnd("jobdefs");
901   UnlockRes(my_config);
902 
903   return true;
904 }
905 
906 /**
907  * Can use an argument to filter on JobType
908  * .jobs [type=B]
909  */
DotJobsCmd(UaContext * ua,const char * cmd)910 bool DotJobsCmd(UaContext* ua, const char* cmd)
911 {
912   int pos;
913   JobResource* job;
914   bool enabled;
915   bool disabled;
916   uint32_t type = 0;
917 
918   if ((pos = FindArgWithValue(ua, "type")) >= 0) { type = ua->argv[pos][0]; }
919 
920   enabled = FindArg(ua, NT_("enabled")) >= 0;
921   disabled = FindArg(ua, NT_("disabled")) >= 0;
922 
923   LockRes(my_config);
924   ua->send->ArrayStart("jobs");
925   foreach_res (job, R_JOB) {
926     if (!type || type == job->JobType) {
927       if (ua->AclAccessOk(Job_ACL, job->resource_name_)) {
928         if (enabled && !job->enabled) { continue; }
929         if (disabled && job->enabled) { continue; }
930 
931         ua->send->ObjectStart();
932         ua->send->ObjectKeyValue("name", job->resource_name_, "%s\n");
933         ua->send->ObjectKeyValueBool("enabled", job->enabled);
934         ua->send->ObjectEnd();
935       }
936     }
937   }
938   ua->send->ArrayEnd("jobs");
939   UnlockRes(my_config);
940 
941   return true;
942 }
943 
DotJobstatusCmd(UaContext * ua,const char * cmd)944 bool DotJobstatusCmd(UaContext* ua, const char* cmd)
945 {
946   bool retval = false;
947   PoolMem select;
948   PoolMem where;
949 
950   if (ua->argv[0]) {
951     if (strlen(ua->argv[0]) == 1) {
952       Mmsg(where, "WHERE JobStatus = '%c' ", ua->argv[0][0]);
953     } else {
954       ua->ErrorMsg(
955           _("Unknown JobStatus '%s'. JobStatus must be a single character.\n"),
956           ua->argv[0]);
957       return false;
958     }
959   }
960 
961   ua->db->FillQuery(select, BareosDb::SQL_QUERY::get_jobstatus_details,
962                     where.c_str());
963 
964   if (!OpenClientDb(ua)) { return false; }
965 
966   ua->send->ArrayStart("jobstatus");
967   retval =
968       ua->db->ListSqlQuery(ua->jcr, select.c_str(), ua->send, HORZ_LIST, false);
969   ua->send->ArrayEnd("jobstatus");
970 
971   return retval;
972 }
973 
DotFilesetsCmd(UaContext * ua,const char * cmd)974 bool DotFilesetsCmd(UaContext* ua, const char* cmd)
975 {
976   FilesetResource* fs;
977 
978   LockRes(my_config);
979   ua->send->ArrayStart("filesets");
980   foreach_res (fs, R_FILESET) {
981     if (ua->AclAccessOk(FileSet_ACL, fs->resource_name_)) {
982       ua->send->ObjectStart();
983       ua->send->ObjectKeyValue("name", fs->resource_name_, "%s\n");
984       ua->send->ObjectEnd();
985     }
986   }
987   ua->send->ArrayEnd("filesets");
988   UnlockRes(my_config);
989 
990   return true;
991 }
992 
DotCatalogsCmd(UaContext * ua,const char * cmd)993 bool DotCatalogsCmd(UaContext* ua, const char* cmd)
994 {
995   CatalogResource* cat;
996 
997   LockRes(my_config);
998   ua->send->ArrayStart("catalogs");
999   foreach_res (cat, R_CATALOG) {
1000     if (ua->AclAccessOk(Catalog_ACL, cat->resource_name_)) {
1001       ua->send->ObjectStart();
1002       ua->send->ObjectKeyValue("name", cat->resource_name_, "%s\n");
1003       ua->send->ObjectEnd();
1004     }
1005   }
1006   ua->send->ArrayEnd("catalogs");
1007   UnlockRes(my_config);
1008 
1009   return true;
1010 }
1011 
DotClientsCmd(UaContext * ua,const char * cmd)1012 bool DotClientsCmd(UaContext* ua, const char* cmd)
1013 {
1014   bool enabled;
1015   bool disabled;
1016   ClientResource* client;
1017 
1018   enabled = FindArg(ua, NT_("enabled")) >= 0;
1019   disabled = FindArg(ua, NT_("disabled")) >= 0;
1020 
1021   LockRes(my_config);
1022   ua->send->ArrayStart("clients");
1023   foreach_res (client, R_CLIENT) {
1024     if (ua->AclAccessOk(Client_ACL, client->resource_name_)) {
1025       if (enabled && !client->enabled) { continue; }
1026       if (disabled && client->enabled) { continue; }
1027 
1028       ua->send->ObjectStart();
1029       ua->send->ObjectKeyValue("name", client->resource_name_, "%s\n");
1030       ua->send->ObjectKeyValueBool("enabled", client->enabled);
1031       ua->send->ObjectEnd();
1032     }
1033   }
1034   ua->send->ArrayEnd("clients");
1035   UnlockRes(my_config);
1036 
1037   return true;
1038 }
1039 
DotConsolesCmd(UaContext * ua,const char * cmd)1040 bool DotConsolesCmd(UaContext* ua, const char* cmd)
1041 {
1042   ConsoleResource* console;
1043 
1044   LockRes(my_config);
1045   ua->send->ArrayStart("consoles");
1046   foreach_res (console, R_CONSOLE) {
1047     ua->send->ObjectStart();
1048     ua->send->ObjectKeyValue("name", console->resource_name_, "%s\n");
1049     ua->send->ObjectEnd();
1050   }
1051   ua->send->ArrayEnd("consoles");
1052   UnlockRes(my_config);
1053 
1054   return true;
1055 }
1056 
DotUsersCmd(UaContext * ua,const char * cmd)1057 bool DotUsersCmd(UaContext* ua, const char* cmd)
1058 {
1059   UserResource* user;
1060 
1061   LockRes(my_config);
1062   ua->send->ArrayStart("users");
1063   foreach_res (user, R_USER) {
1064     ua->send->ObjectStart();
1065     ua->send->ObjectKeyValue("name", user->resource_name_, "%s\n");
1066     ua->send->ObjectEnd();
1067   }
1068   ua->send->ArrayEnd("users");
1069   UnlockRes(my_config);
1070 
1071   return true;
1072 }
1073 
DotMsgsCmd(UaContext * ua,const char * cmd)1074 bool DotMsgsCmd(UaContext* ua, const char* cmd)
1075 {
1076   MessagesResource* msgs = NULL;
1077 
1078   LockRes(my_config);
1079   ua->send->ArrayStart("messages");
1080   foreach_res (msgs, R_MSGS) {
1081     ua->send->ObjectStart();
1082     ua->send->ObjectKeyValue("text", msgs->resource_name_, "%s\n");
1083     ua->send->ObjectEnd();
1084   }
1085   ua->send->ArrayEnd("messages");
1086   UnlockRes(my_config);
1087 
1088   return true;
1089 }
1090 
DotPoolsCmd(UaContext * ua,const char * cmd)1091 bool DotPoolsCmd(UaContext* ua, const char* cmd)
1092 {
1093   int pos, length;
1094   PoolResource* pool;
1095 
1096   pos = FindArgWithValue(ua, "type");
1097   if (pos >= 0) {
1098     length = strlen(ua->argv[pos]);
1099   } else {
1100     length = 0;
1101   }
1102 
1103   LockRes(my_config);
1104   ua->send->ArrayStart("pools");
1105   foreach_res (pool, R_POOL) {
1106     if (ua->AclAccessOk(Pool_ACL, pool->resource_name_)) {
1107       if (pos == -1 || bstrncasecmp(pool->pool_type, ua->argv[pos], length)) {
1108         ua->send->ObjectStart();
1109         ua->send->ObjectKeyValue("name", pool->resource_name_, "%s\n");
1110         ua->send->ObjectEnd();
1111       }
1112     }
1113   }
1114   ua->send->ArrayEnd("pools");
1115   UnlockRes(my_config);
1116 
1117   return true;
1118 }
1119 
DotStorageCmd(UaContext * ua,const char * cmd)1120 bool DotStorageCmd(UaContext* ua, const char* cmd)
1121 {
1122   bool enabled;
1123   bool disabled;
1124   StorageResource* store;
1125 
1126   enabled = FindArg(ua, NT_("enabled")) >= 0;
1127   disabled = FindArg(ua, NT_("disabled")) >= 0;
1128 
1129   LockRes(my_config);
1130   ua->send->ArrayStart("storages");
1131   foreach_res (store, R_STORAGE) {
1132     if (ua->AclAccessOk(Storage_ACL, store->resource_name_)) {
1133       if (enabled && !store->enabled) { continue; }
1134       if (disabled && store->enabled) { continue; }
1135 
1136       ua->send->ObjectStart();
1137       ua->send->ObjectKeyValue("name", store->resource_name_, "%s\n");
1138       ua->send->ObjectKeyValueBool("enabled", store->enabled);
1139       ua->send->ObjectEnd();
1140     }
1141   }
1142   ua->send->ArrayEnd("storages");
1143   UnlockRes(my_config);
1144 
1145   return true;
1146 }
1147 
DotProfilesCmd(UaContext * ua,const char * cmd)1148 bool DotProfilesCmd(UaContext* ua, const char* cmd)
1149 {
1150   ProfileResource* profile;
1151 
1152   LockRes(my_config);
1153   ua->send->ArrayStart("profiles");
1154   foreach_res (profile, R_PROFILE) {
1155     ua->send->ObjectStart();
1156     ua->send->ObjectKeyValue("name", profile->resource_name_, "%s\n");
1157     ua->send->ObjectEnd();
1158   }
1159   ua->send->ArrayEnd("profiles");
1160   UnlockRes(my_config);
1161 
1162   return true;
1163 }
1164 
DotAopCmd(UaContext * ua,const char * cmd)1165 bool DotAopCmd(UaContext* ua, const char* cmd)
1166 {
1167   ua->send->ArrayStart("actiononpurge");
1168   for (int i = 0; ActionOnPurgeOptions[i].name; i++) {
1169     ua->send->ObjectStart();
1170     ua->send->ObjectKeyValue("name", ActionOnPurgeOptions[i].name, "%s\n");
1171     ua->send->ObjectEnd();
1172   }
1173   ua->send->ArrayEnd("actiononpurge");
1174 
1175   return true;
1176 }
1177 
DotTypesCmd(UaContext * ua,const char * cmd)1178 bool DotTypesCmd(UaContext* ua, const char* cmd)
1179 {
1180   ua->send->ArrayStart("jobtypes");
1181   for (int i = 0; jobtypes[i].type_name; i++) {
1182     ua->send->ObjectStart();
1183     ua->send->ObjectKeyValue("name", jobtypes[i].type_name, "%s\n");
1184     ua->send->ObjectEnd();
1185   }
1186   ua->send->ArrayEnd("jobtypes");
1187 
1188   return true;
1189 }
1190 
1191 /**
1192  * If this command is called, it tells the director that we
1193  * are a program that wants a sort of API, and hence,
1194  * we will probably suppress certain output, include more
1195  * error codes, and most of all send back a good number
1196  * of new signals that indicate whether or not the command
1197  * succeeded.
1198  */
DotApiCmd(UaContext * ua,const char * cmd)1199 bool DotApiCmd(UaContext* ua, const char* cmd)
1200 {
1201   if (ua->argc == 1) {
1202     ua->api = 1;
1203   } else if ((ua->argc >= 2) && (ua->argc <= 3)) {
1204     if (Bstrcasecmp(ua->argk[1], "off") || Bstrcasecmp(ua->argk[1], "0")) {
1205       ua->api = API_MODE_OFF;
1206       ua->batch = false;
1207     } else if (Bstrcasecmp(ua->argk[1], "on") ||
1208                Bstrcasecmp(ua->argk[1], "1")) {
1209       ua->api = API_MODE_ON;
1210       ua->batch = false;
1211     } else if (Bstrcasecmp(ua->argk[1], "json") ||
1212                Bstrcasecmp(ua->argk[1], "2")) {
1213       ua->api = API_MODE_JSON;
1214       ua->batch = true;
1215       if ((ua->argc == 3) && (FindArgWithValue(ua, "compact") == 2)) {
1216         if (Bstrcasecmp(ua->argv[2], "yes")) {
1217           ua->send->SetCompact(true);
1218         } else {
1219           ua->send->SetCompact(false);
1220         }
1221       }
1222     } else {
1223       return false;
1224     }
1225   } else {
1226     return false;
1227   }
1228 
1229   ua->send->SetMode(ua->api);
1230   ua->send->ObjectKeyValue("api", "%s: ", ua->api, "%d\n");
1231 
1232   return true;
1233 }
1234 
SqlHandler(void * ctx,int num_field,char ** row)1235 static int SqlHandler(void* ctx, int num_field, char** row)
1236 {
1237   UaContext* ua = (UaContext*)ctx;
1238   PoolMem rows(PM_MESSAGE);
1239 
1240   /* Check for nonsense */
1241   if (num_field == 0 || row == NULL || row[0] == NULL) {
1242     return 0; /* nothing returned */
1243   }
1244   for (int i = 0; num_field--; i++) {
1245     if (i == 0) {
1246       PmStrcpy(rows, NPRT(row[0]));
1247     } else {
1248       PmStrcat(rows, NPRT(row[i]));
1249     }
1250     PmStrcat(rows, "\t");
1251   }
1252   if (!rows.c_str() || !*rows.c_str()) {
1253     ua->SendMsg("\t");
1254   } else {
1255     ua->SendMsg("%s", rows.c_str());
1256   }
1257   return 0;
1258 }
1259 
DotSqlCmd(UaContext * ua,const char * cmd)1260 bool DotSqlCmd(UaContext* ua, const char* cmd)
1261 {
1262   int pos;
1263   bool retval = false;
1264 
1265   if (!OpenClientDb(ua, true)) { return false; }
1266 
1267   pos = FindArgWithValue(ua, "query");
1268   if (pos < 0) {
1269     ua->ErrorMsg(_("query keyword not found.\n"));
1270     return false;
1271   }
1272 
1273   switch (ua->api) {
1274     case API_MODE_ON:
1275       /*
1276        * BAT uses the ".sql" command and expects this format
1277        */
1278       retval = ua->db->SqlQuery(ua->argv[pos], SqlHandler, (void*)ua);
1279       break;
1280     default:
1281       /*
1282        * General format
1283        */
1284       retval = ua->db->ListSqlQuery(ua->jcr, ua->argv[pos], ua->send, HORZ_LIST,
1285                                     false);
1286       break;
1287   }
1288 
1289   if (!retval) {
1290     Dmsg1(100, "Query failed: ERR=%s", ua->db->strerror());
1291     ua->ErrorMsg(_("Query failed: %s. ERR=%s"), ua->cmd, ua->db->strerror());
1292   }
1293 
1294   return retval;
1295 }
1296 
OneHandler(void * ctx,int num_field,char ** row)1297 static int OneHandler(void* ctx, int num_field, char** row)
1298 {
1299   UaContext* ua = (UaContext*)ctx;
1300 
1301   ua->send->ObjectStart();
1302   ua->send->ObjectKeyValue("name", row[0], "%s\n");
1303   ua->send->ObjectEnd();
1304 
1305   return 0;
1306 }
1307 
DotMediatypesCmd(UaContext * ua,const char * cmd)1308 bool DotMediatypesCmd(UaContext* ua, const char* cmd)
1309 {
1310   if (!OpenClientDb(ua)) { return true; }
1311 
1312   ua->send->ArrayStart("mediatypes");
1313   if (!ua->db->SqlQuery(
1314           "SELECT DISTINCT MediaType FROM MediaType ORDER BY MediaType",
1315           OneHandler, (void*)ua)) {
1316     ua->ErrorMsg(_("List MediaType failed: ERR=%s\n"), ua->db->strerror());
1317   }
1318   ua->send->ArrayEnd("mediatypes");
1319 
1320   return true;
1321 }
1322 
DotMediaCmd(UaContext * ua,const char * cmd)1323 bool DotMediaCmd(UaContext* ua, const char* cmd)
1324 {
1325   if (!OpenClientDb(ua)) { return true; }
1326 
1327   ua->send->ArrayStart("media");
1328   if (!ua->db->SqlQuery(
1329           "SELECT DISTINCT Media.VolumeName FROM Media ORDER BY VolumeName",
1330           OneHandler, (void*)ua)) {
1331     ua->ErrorMsg(_("List Media failed: ERR=%s\n"), ua->db->strerror());
1332   }
1333   ua->send->ArrayEnd("media");
1334 
1335   return true;
1336 }
1337 
DotScheduleCmd(UaContext * ua,const char * cmd)1338 bool DotScheduleCmd(UaContext* ua, const char* cmd)
1339 {
1340   bool enabled;
1341   bool disabled;
1342   ScheduleResource* sched;
1343 
1344   enabled = FindArg(ua, NT_("enabled")) >= 0;
1345   disabled = FindArg(ua, NT_("disabled")) >= 0;
1346 
1347   LockRes(my_config);
1348   ua->send->ArrayStart("schedules");
1349   foreach_res (sched, R_SCHEDULE) {
1350     if (ua->AclAccessOk(Schedule_ACL, sched->resource_name_)) {
1351       if (enabled && !sched->enabled) { continue; }
1352       if (disabled && sched->enabled) { continue; }
1353 
1354       ua->send->ObjectStart();
1355       ua->send->ObjectKeyValue("name", sched->resource_name_, "%s\n");
1356       ua->send->ObjectKeyValueBool("enabled", sched->enabled);
1357       ua->send->ObjectEnd();
1358     }
1359   }
1360   ua->send->ArrayEnd("schedules");
1361   UnlockRes(my_config);
1362 
1363   return true;
1364 }
1365 
DotLocationsCmd(UaContext * ua,const char * cmd)1366 bool DotLocationsCmd(UaContext* ua, const char* cmd)
1367 {
1368   if (!OpenClientDb(ua)) { return true; }
1369 
1370   ua->send->ArrayStart("locations");
1371   if (!ua->db->SqlQuery(
1372           "SELECT DISTINCT Location FROM Location ORDER BY Location",
1373           OneHandler, (void*)ua)) {
1374     ua->ErrorMsg(_("List Location failed: ERR=%s\n"), ua->db->strerror());
1375   }
1376   ua->send->ArrayEnd("locations");
1377 
1378   return true;
1379 }
1380 
DotLevelsCmd(UaContext * ua,const char * cmd)1381 bool DotLevelsCmd(UaContext* ua, const char* cmd)
1382 {
1383   /*
1384    * Note some levels are blank, which means none is needed
1385    */
1386   ua->send->ArrayStart("levels");
1387   if (ua->argc == 1) {
1388     for (int i = 0; joblevels[i].level_name; i++) {
1389       if (joblevels[i].level_name[0] != ' ') {
1390         ua->send->ObjectStart();
1391         ua->send->ObjectKeyValue("name", joblevels[i].level_name, "%s\n");
1392         ua->send->ObjectKeyValue("level", joblevels[i].level);
1393         ua->send->ObjectKeyValue("jobtype", joblevels[i].job_type);
1394         ua->send->ObjectEnd();
1395       }
1396     }
1397   } else if (ua->argc == 2) {
1398     int jobtype = 0;
1399 
1400     /*
1401      * Assume that first argument is the Job Type
1402      */
1403     for (int i = 0; jobtypes[i].type_name; i++) {
1404       if (Bstrcasecmp(ua->argk[1], jobtypes[i].type_name)) {
1405         jobtype = jobtypes[i].job_type;
1406         break;
1407       }
1408     }
1409 
1410     for (int i = 0; joblevels[i].level_name; i++) {
1411       if ((joblevels[i].job_type == jobtype) &&
1412           (joblevels[i].level_name[0] != ' ')) {
1413         ua->send->ObjectStart();
1414         ua->send->ObjectKeyValue("name", joblevels[i].level_name, "%s\n");
1415         ua->send->ObjectKeyValue("level", joblevels[i].level);
1416         ua->send->ObjectKeyValue("jobtype", joblevels[i].job_type);
1417         ua->send->ObjectEnd();
1418       }
1419     }
1420   }
1421   ua->send->ArrayEnd("levels");
1422 
1423   return true;
1424 }
1425 
DotVolstatusCmd(UaContext * ua,const char * cmd)1426 bool DotVolstatusCmd(UaContext* ua, const char* cmd)
1427 {
1428   ua->send->ArrayStart("volstatus");
1429   for (int i = 0; VolumeStatus[i].name; i++) {
1430     ua->send->ObjectStart();
1431     ua->send->ObjectKeyValue("name", VolumeStatus[i].name, "%s\n");
1432     ua->send->ObjectEnd();
1433   }
1434   ua->send->ArrayEnd("volstatus");
1435 
1436   return true;
1437 }
1438 
1439 /**
1440  * Return default values for a job
1441  */
DotDefaultsCmd(UaContext * ua,const char * cmd)1442 bool DotDefaultsCmd(UaContext* ua, const char* cmd)
1443 {
1444   char ed1[50];
1445   int pos = 0;
1446 
1447   ua->send->ObjectStart("defaults");
1448   if ((pos = FindArgWithValue(ua, "job")) >= 0) {
1449     JobResource* job;
1450 
1451     /*
1452      * Job defaults
1453      */
1454     job = ua->GetJobResWithName(ua->argv[pos]);
1455     if (job) {
1456       UnifiedStorageResource store;
1457 
1458       /*
1459        * BAT parses the result of this command message by message,
1460        * instead of looking for a separator.
1461        * Therefore the SendBuffer() function is called after each line.
1462        */
1463       ua->send->ObjectKeyValue("job", "%s=", job->resource_name_, "%s\n");
1464       ua->send->SendBuffer();
1465       ua->send->ObjectKeyValue("pool", "%s=", job->pool->resource_name_,
1466                                "%s\n");
1467       ua->send->SendBuffer();
1468       ua->send->ObjectKeyValue("messages", "%s=", job->messages->resource_name_,
1469                                "%s\n");
1470       ua->send->SendBuffer();
1471       ua->send->ObjectKeyValue(
1472           "client",
1473           "%s=", ((job->client) ? job->client->resource_name_ : _("*None*")),
1474           "%s\n");
1475       ua->send->SendBuffer();
1476       GetJobStorage(&store, job, NULL);
1477       ua->send->ObjectKeyValue(
1478           "storage",
1479           "%s=", store.store ? store.store->resource_name_ : "*None*", "%s\n");
1480       ua->send->SendBuffer();
1481       ua->send->ObjectKeyValue(
1482           "where", "%s=", (job->RestoreWhere ? job->RestoreWhere : ""), "%s\n");
1483       ua->send->SendBuffer();
1484       ua->send->ObjectKeyValue("level", "%s=", JobLevelToString(job->JobLevel),
1485                                "%s\n");
1486       ua->send->SendBuffer();
1487       ua->send->ObjectKeyValue("type", "%s=", job_type_to_str(job->JobType),
1488                                "%s\n");
1489       ua->send->SendBuffer();
1490       ua->send->ObjectKeyValue(
1491           "fileset",
1492           "%s=", ((job->fileset) ? job->fileset->resource_name_ : _("*None*")),
1493           "%s\n");
1494       ua->send->SendBuffer();
1495       ua->send->ObjectKeyValue("enabled", "%s=", job->enabled, "%d\n");
1496       ua->send->SendBuffer();
1497       ua->send->ObjectKeyValue(
1498           "catalog", "%s=",
1499           ((job->client) ? job->client->catalog->resource_name_ : _("*None*")),
1500           "%s\n");
1501       ua->send->SendBuffer();
1502     }
1503   } else if ((pos = FindArgWithValue(ua, "client")) >= 0) {
1504     ClientResource* client;
1505 
1506     /*
1507      * Client defaults
1508      */
1509     client = ua->GetClientResWithName(ua->argv[pos]);
1510     if (client) {
1511       ua->send->ObjectKeyValue("client", "%s=", client->resource_name_, "%s\n");
1512       ua->send->ObjectKeyValue("address", "%s=", client->address, "%s\n");
1513       ua->send->ObjectKeyValue("port", "%s=", client->FDport, "%d\n");
1514       ua->send->ObjectKeyValue("file_retention",
1515                                "%s=", edit_uint64(client->FileRetention, ed1),
1516                                "%s\n");
1517       ua->send->ObjectKeyValue("job_retention",
1518                                "%s=", edit_uint64(client->JobRetention, ed1),
1519                                "%s\n");
1520       ua->send->ObjectKeyValue("autoprune", "%s=", client->AutoPrune, "%d\n");
1521       ua->send->ObjectKeyValue("enabled", "%s=", client->enabled, "%d\n");
1522       ua->send->ObjectKeyValue("catalog",
1523                                "%s=", client->catalog->resource_name_, "%s\n");
1524     }
1525   } else if ((pos = FindArgWithValue(ua, "storage")) >= 0) {
1526     StorageResource* storage;
1527 
1528     /*
1529      * Storage defaults
1530      */
1531     storage = ua->GetStoreResWithName(ua->argv[pos]);
1532     if (storage) {
1533       DeviceResource* device;
1534       PoolMem devices;
1535 
1536       ua->send->ObjectKeyValue("storage", "%s=", storage->resource_name_,
1537                                "%s\n");
1538       ua->send->ObjectKeyValue("address", "%s=", storage->address, "%s\n");
1539       ua->send->ObjectKeyValue("port", "%s=", storage->SDport, "%d\n");
1540       ua->send->ObjectKeyValue("enabled", "%s=", storage->enabled, "%d\n");
1541       ua->send->ObjectKeyValue("media_type", "%s=", storage->media_type,
1542                                "%s\n");
1543 
1544       device = (DeviceResource*)storage->device->first();
1545       if (device) {
1546         devices.strcpy(device->resource_name_);
1547         if (storage->device->size() > 1) {
1548           while ((device = (DeviceResource*)storage->device->next())) {
1549             devices.strcat(",");
1550             devices.strcat(device->resource_name_);
1551           }
1552         }
1553         ua->send->ObjectKeyValue("device", "%s=", devices.c_str(), "%s\n");
1554       }
1555     }
1556   } else if ((pos = FindArgWithValue(ua, "pool")) >= 0) {
1557     PoolResource* pool;
1558 
1559     /*
1560      * Pool defaults
1561      */
1562     pool = ua->GetPoolResWithName(ua->argv[pos]);
1563     if (pool) {
1564       ua->send->ObjectKeyValue("pool", "%s=", pool->resource_name_, "%s\n");
1565       ua->send->ObjectKeyValue("pool_type", "%s=", pool->pool_type, "%s\n");
1566       ua->send->ObjectKeyValue(
1567           "label_format", "%s=", (pool->label_format ? pool->label_format : ""),
1568           "%s\n");
1569       ua->send->ObjectKeyValue("use_volume_once", "%s=", pool->use_volume_once,
1570                                "%d\n");
1571       ua->send->ObjectKeyValue(
1572           "purge_oldest_volume=", "%s=", pool->purge_oldest_volume, "%d\n");
1573       ua->send->ObjectKeyValue("recycle_oldest_volume",
1574                                "%s=", pool->recycle_oldest_volume, "%d\n");
1575       ua->send->ObjectKeyValue("max_volumes", "%s=", pool->max_volumes, "%d\n");
1576       ua->send->ObjectKeyValue(
1577           "vol_retention", "%s=", edit_uint64(pool->VolRetention, ed1), "%s\n");
1578       ua->send->ObjectKeyValue("vol_use_duration",
1579                                "%s=", edit_uint64(pool->VolUseDuration, ed1),
1580                                "%s\n");
1581       ua->send->ObjectKeyValue("max_vol_jobs", "%s=", pool->MaxVolJobs, "%d\n");
1582       ua->send->ObjectKeyValue("max_vol_files", "%s=", pool->MaxVolFiles,
1583                                "%d\n");
1584       ua->send->ObjectKeyValue(
1585           "max_vol_bytes", "%s=", edit_uint64(pool->MaxVolBytes, ed1), "%s\n");
1586       ua->send->ObjectKeyValue("auto_prune", "%s=", pool->AutoPrune, "%d\n");
1587       ua->send->ObjectKeyValue("recycle", "%s=", pool->Recycle, "%d\n");
1588       ua->send->ObjectKeyValue("file_retention",
1589                                "%s=", edit_uint64(pool->FileRetention, ed1),
1590                                "%s\n");
1591       ua->send->ObjectKeyValue(
1592           "job_retention", "%s=", edit_uint64(pool->JobRetention, ed1), "%s\n");
1593     }
1594   } else {
1595     ua->SendMsg(".defaults command requires a parameter.\n");
1596     return false;
1597   }
1598   ua->send->ObjectEnd("defaults");
1599 
1600   return true;
1601 }
1602 } /* namespace directordaemon */
1603