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